//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "flashbang_projectile.h" #include "shake.h" #include "engine/IEngineSound.h" #include "cs_player.h" #include "dlight.h" #include "KeyValues.h" #include "weapon_csbase.h" #include "collisionutils.h" #include "particle_smokegrenade.h" #include "smoke_fog_overlay_shared.h" #define GRENADE_MODEL "models/Weapons/w_eq_flashbang_thrown.mdl" LINK_ENTITY_TO_CLASS( flashbang_projectile, CFlashbangProjectile ); PRECACHE_WEAPON_REGISTER( flashbang_projectile ); float PercentageOfFlashForPlayer(CBaseEntity *player, Vector flashPos, CBaseEntity *pevInflictor) { float retval = 0.0f; trace_t tr; Vector pos = player->EyePosition(); Vector vecRight, vecUp, vecForward; AngleVectors( player->EyeAngles(), &vecForward ); QAngle tempAngle; VectorAngles(player->EyePosition() - flashPos, tempAngle); AngleVectors(tempAngle, NULL, &vecRight, &vecUp); vecRight.NormalizeInPlace(); vecUp.NormalizeInPlace(); UTIL_TraceLine( flashPos, pos, (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_DEBRIS|CONTENTS_MONSTER), pevInflictor, COLLISION_GROUP_NONE, &tr ); if ((tr.fraction == 1.0) || (tr.m_pEnt == player)) { retval = 1.0; } else { return 0.0; } CBaseEntity *pSGren; for( pSGren = gEntList.FindEntityByClassname( NULL, "env_particlesmokegrenade" ); pSGren; pSGren = gEntList.FindEntityByClassname( pSGren, "env_particlesmokegrenade" ) ) { ParticleSmokeGrenade *pPSG =( ParticleSmokeGrenade* ) pSGren; if ( gpGlobals->curtime > pPSG->m_flSpawnTime + pPSG->m_FadeStartTime ) // ignore the smoke grenade if it's fading. continue; float flHit1, flHit2; float flInnerRadius = SMOKEGRENADE_PARTICLERADIUS; // float flOutterRadius = flInnerRadius + ( 0.5 * SMOKEPARTICLE_SIZE ); Vector vPos = pSGren->GetAbsOrigin(); /*debugoverlay->AddBoxOverlay( pSGren->GetAbsOrigin(), Vector( flInnerRadius, flInnerRadius, flInnerRadius ), Vector( -flInnerRadius, -flInnerRadius, -flInnerRadius ), QAngle( 0, 0, 0 ), 0, 255, 0, 30, 10 ); debugoverlay->AddBoxOverlay( pSGren->GetAbsOrigin(), Vector( flOutterRadius, flOutterRadius, flOutterRadius ), Vector( -flOutterRadius, -flOutterRadius, -flOutterRadius ), QAngle( 0, 0, 0 ), 255, 0, 0, 30, 10 ); */ if ( IntersectInfiniteRayWithSphere( pos, vecForward, vPos, flInnerRadius, &flHit1, &flHit2 ) ) { retval *= 0.8; } /* else if ( IntersectInfiniteRayWithSphere( pos, vecForward, vPos, flOutterRadius, &flHit1, &flHit2 ) ) { retval *= 0.9; } */ } return retval; } // --------------------------------------------------------------------------------------------------- // // // RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range. // // only damage ents that can clearly be seen by the explosion! // --------------------------------------------------------------------------------------------------- // void RadiusFlash( Vector vecSrc, CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) { vecSrc.z += 1;// in case grenade is lying on the ground if ( !pevAttacker ) pevAttacker = pevInflictor; trace_t tr; float flAdjustedDamage; variant_t var; Vector vecEyePos; float fadeTime, fadeHold; Vector vForward; Vector vecLOS; float flDot; CBaseEntity *pEntity = NULL; static float flRadius = 1500; float falloff = flDamage / flRadius; bool bInWater = (UTIL_PointContents( vecSrc ) == CONTENTS_WATER); // iterate on all entities in the vicinity. while ((pEntity = gEntList.FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) { bool bPlayer = pEntity->IsPlayer(); bool bHostage = ( Q_stricmp( pEntity->GetClassname(), "hostage_entity" ) == 0 ); if( !bPlayer && !bHostage ) continue; vecEyePos = pEntity->EyePosition(); // blasts don't travel into or out of water if ( bInWater && pEntity->GetWaterLevel() == 0) continue; if (!bInWater && pEntity->GetWaterLevel() == 3) continue; float percentageOfFlash = PercentageOfFlashForPlayer(pEntity, vecSrc, pevInflictor); if ( percentageOfFlash > 0.0 ) { // decrease damage for an ent that's farther from the grenade flAdjustedDamage = flDamage - ( vecSrc - pEntity->EyePosition() ).Length() * falloff; if ( flAdjustedDamage > 0 ) { // See if we were facing the flash AngleVectors( pEntity->EyeAngles(), &vForward ); vecLOS = ( vecSrc - vecEyePos ); float flDistance = vecLOS.Length(); // Normalize both vectors so the dotproduct is in the range -1.0 <= x <= 1.0 vecLOS.NormalizeInPlace(); flDot = DotProduct (vecLOS, vForward); float startingAlpha = 255; // if target is facing the bomb, the effect lasts longer if( flDot >= 0.5 ) { // looking at the flashbang fadeTime = flAdjustedDamage * 2.5f; fadeHold = flAdjustedDamage * 1.25f; } else if( flDot >= -0.5 ) { // looking to the side fadeTime = flAdjustedDamage * 1.75f; fadeHold = flAdjustedDamage * 0.8f; } else { // facing away fadeTime = flAdjustedDamage * 1.0f; fadeHold = flAdjustedDamage * 0.75f; startingAlpha = 200; } fadeTime *= percentageOfFlash; fadeHold *= percentageOfFlash; if ( bPlayer ) { // blind players and bots CCSPlayer *player = static_cast< CCSPlayer * >( pEntity ); //============================================================================= // HPE_BEGIN: // [tj] Store who was responsible for the most recent flashbang blinding. //============================================================================= CCSPlayer *attacker = ToCSPlayer (pevAttacker); if (attacker && player) { player->SetLastFlashbangAttacker(attacker); } //============================================================================= // HPE_END //============================================================================= player->Blind( fadeHold, fadeTime, startingAlpha ); // deafen players and bots player->Deafen( flDistance ); } else if ( bHostage ) { variant_t val; val.SetFloat( fadeTime ); pEntity->AcceptInput( "flashbang", pevInflictor, pevAttacker, val, 0 ); } } } } CPVSFilter filter(vecSrc); te->DynamicLight( filter, 0.0, &vecSrc, 255, 255, 255, 2, 400, 0.1, 768 ); } // --------------------------------------------------------------------------------------------------- // // CFlashbangProjectile implementation. // --------------------------------------------------------------------------------------------------- // CFlashbangProjectile* CFlashbangProjectile::Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner ) { CFlashbangProjectile *pGrenade = (CFlashbangProjectile*)CBaseEntity::Create( "flashbang_projectile", position, angles, pOwner ); // Set the timer for 1 second less than requested. We're going to issue a SOUND_DANGER // one second before detonation. pGrenade->SetAbsVelocity( velocity ); pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity ); pGrenade->SetThrower( pOwner ); pGrenade->m_flDamage = 100; pGrenade->ChangeTeam( pOwner->GetTeamNumber() ); pGrenade->SetTouch( &CBaseGrenade::BounceTouch ); pGrenade->SetThink( &CBaseCSGrenadeProjectile::DangerSoundThink ); pGrenade->SetNextThink( gpGlobals->curtime ); pGrenade->SetDetonateTimerLength( 1.5 ); pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); pGrenade->SetGravity( BaseClass::GetGrenadeGravity() ); pGrenade->SetFriction( BaseClass::GetGrenadeFriction() ); pGrenade->SetElasticity( BaseClass::GetGrenadeElasticity() ); pGrenade->m_pWeaponInfo = GetWeaponInfo( WEAPON_FLASHBANG ); return pGrenade; } void CFlashbangProjectile::Spawn() { SetModel( GRENADE_MODEL ); BaseClass::Spawn(); } void CFlashbangProjectile::Precache() { PrecacheModel( GRENADE_MODEL ); PrecacheScriptSound( "Flashbang.Explode" ); PrecacheScriptSound( "Flashbang.Bounce" ); BaseClass::Precache(); } void CFlashbangProjectile::Detonate() { RadiusFlash ( GetAbsOrigin(), this, GetThrower(), 4, CLASS_NONE, DMG_BLAST ); EmitSound( "Flashbang.Explode" ); // tell the bots a flashbang grenade has exploded CCSPlayer *player = ToCSPlayer(GetThrower()); if ( player ) { IGameEvent * event = gameeventmanager->CreateEvent( "flashbang_detonate" ); if ( event ) { event->SetInt( "userid", player->GetUserID() ); event->SetFloat( "x", GetAbsOrigin().x ); event->SetFloat( "y", GetAbsOrigin().y ); event->SetFloat( "z", GetAbsOrigin().z ); gameeventmanager->FireEvent( event ); } } UTIL_Remove( this ); } //TODO: Let physics handle the sound! void CFlashbangProjectile::BounceSound( void ) { EmitSound( "Flashbang.Bounce" ); }