|
|
//========= Copyright � 1996-2005, 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 "cs_gamerules.h"
#include "animation.h"
#define GRENADE_MODEL "models/Weapons/w_eq_flashbang_dropped.mdl"
LINK_ENTITY_TO_CLASS( flashbang_projectile, CFlashbangProjectile ); PRECACHE_REGISTER( flashbang_projectile );
#if !defined( CLIENT_DLL )
BEGIN_DATADESC( CFlashbangProjectile )
// Fields
//DEFINE_KEYFIELD( m_flTimeToDetonate, FIELD_FLOAT, "TimeToDetonate" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTimer", InputSetTimer ),
END_DATADESC() #endif
// hack to allow de_nuke vents to occlude flashbangs when closed
class CTraceFilterNoPlayersAndFlashbangPassableAnims : public CTraceFilterNoPlayers { public: CTraceFilterNoPlayersAndFlashbangPassableAnims( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE ) : CTraceFilterNoPlayers( passentity, collisionGroup ) { }
virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) {
CBaseEntity *pEnt = EntityFromEntityHandle(pHandleEntity); if ( pEnt ) { CBaseAnimating* pAnimating = dynamic_cast< CBaseAnimating* >( pEnt ); if ( pAnimating ) { // look for the flashbang passable animtag
float flFlashbangPassable = pAnimating->GetAnySequenceAnimTag( pAnimating->GetSequence(), ANIMTAG_FLASHBANG_PASSABLE, -1 );
if ( flFlashbangPassable != -1 ) return false; // model animation is tagged to allow flashbangs through
}
// Weapons don't block flashbangs
CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pEnt ); CBaseGrenade* pGrenade = dynamic_cast< CBaseGrenade* > ( pEnt ); if ( pWeapon || pGrenade ) return false; }
return CTraceFilterNoPlayers::ShouldHitEntity( pHandleEntity, contentsMask ); } };
float PercentageOfFlashForPlayer(CBaseEntity *player, Vector flashPos, CBaseEntity *pevInflictor) { if (!(player->IsPlayer())) { // if this entity isn't a player, it's a hostage or some other entity, then don't bother with the expensive checks
// that come below.
return 0.0f; }
const float FLASH_FRACTION = 0.167f; const float SIDE_OFFSET = 75.0f;
Vector pos = player->EyePosition(); Vector vecRight, vecUp;
QAngle tempAngle; VectorAngles(player->EyePosition() - flashPos, tempAngle); AngleVectors(tempAngle, NULL, &vecRight, &vecUp);
vecRight.NormalizeInPlace(); vecUp.NormalizeInPlace();
// Set up all the ray stuff.
// We don't want to let other players block the flash bang so we use this custom filter.
Ray_t ray; trace_t tr; CTraceFilterNoPlayersAndFlashbangPassableAnims traceFilter( pevInflictor, COLLISION_GROUP_NONE ); unsigned int FLASH_MASK = MASK_OPAQUE_AND_NPCS | CONTENTS_DEBRIS;
// According to comment in IsNoDrawBrush in cmodel.cpp, CONTENTS_OPAQUE is ONLY used for block light surfaces,
// and we want flashbang traces to pass through those, since the block light surface is only used for blocking
// lightmap light rays during map compilation.
FLASH_MASK &= ~CONTENTS_OPAQUE;
ray.Init( flashPos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
if ((tr.fraction == 1.0f) || (tr.m_pEnt == player)) { return 1.0f; }
float retval = 0.0f;
// check the point straight up.
pos = flashPos + vecUp*50.0f; ray.Init( flashPos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr ); // Now shoot it to the player's eye.
pos = player->EyePosition(); ray.Init( tr.endpos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
if ((tr.fraction == 1.0f) || (tr.m_pEnt == player)) { retval += FLASH_FRACTION; }
// check the point up and right.
pos = flashPos + vecRight*SIDE_OFFSET + vecUp*10.0f; ray.Init( flashPos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr ); // Now shoot it to the player's eye.
pos = player->EyePosition(); ray.Init( tr.endpos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
if ((tr.fraction == 1.0f) || (tr.m_pEnt == player)) { retval += FLASH_FRACTION; }
// Check the point up and left.
pos = flashPos - vecRight*SIDE_OFFSET + vecUp*10.0f; ray.Init( flashPos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr ); // Now shoot it to the player's eye.
pos = player->EyePosition(); ray.Init( tr.endpos, pos ); enginetrace->TraceRay( ray, FLASH_MASK, &traceFilter, &tr );
if ((tr.fraction == 1.0f) || (tr.m_pEnt == player)) { retval += FLASH_FRACTION; }
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, uint8 *pOutNumOpponentsEffected = NULL, uint8 *pOutNumTeammatesEffected = NULL ) { vecSrc.z += 1;// in case grenade is lying on the ground
if ( !pevAttacker ) pevAttacker = pevInflictor;
if ( pOutNumOpponentsEffected ) *pOutNumOpponentsEffected = 0;
if ( pOutNumTeammatesEffected ) *pOutNumTeammatesEffected = 0; 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 = 3000; float falloff = flDamage / flRadius;
//bool bInWater = (UTIL_PointContents( vecSrc, MASK_WATER ) == CONTENTS_WATER);
// iterate on all entities in the vicinity.
while ((pEntity = gEntList.FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) { bool bPlayer = pEntity->IsPlayer(); if( !bPlayer ) continue;
vecEyePos = pEntity->EyePosition();
//// blasts used to not travel into or out of water, users assumed it was a bug. Fix is not to run this check -wills
//if ( bInWater && pEntity->GetWaterLevel() == WL_NotInWater)
// continue;
//if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes)
// continue;
float percentageOfFlash = PercentageOfFlashForPlayer(pEntity, vecSrc, pevInflictor);
if ( percentageOfFlash > 0.0 ) { if ( pOutNumOpponentsEffected && pEntity->GetTeamNumber() != pevAttacker->GetTeamNumber() ) (*pOutNumOpponentsEffected)++; if ( pOutNumTeammatesEffected && pEntity->GetTeamNumber() == pevAttacker->GetTeamNumber() ) (*pOutNumTeammatesEffected)++;
// 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();
//DebugDrawLine( vecEyePos, vecEyePos + (100.0 * vecLOS), 0, 255, 0, true, 10.0 );
//DebugDrawLine( vecEyePos, vecEyePos + (100.0 * vForward), 0, 0, 255, true, 10.0 );
// 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.6 ) { // looking at the flashbang
fadeTime = flAdjustedDamage * 2.5f; fadeHold = flAdjustedDamage * 1.25f; } else if( flDot >= 0.3 ) { // looking to the side
fadeTime = flAdjustedDamage * 1.75f; fadeHold = flAdjustedDamage * 0.8f; } else if( flDot >= -0.2 ) { // looking to the side
fadeTime = flAdjustedDamage * 1.00f; fadeHold = flAdjustedDamage * 0.5f; } else { // facing away
fadeTime = flAdjustedDamage * 0.5f; fadeHold = flAdjustedDamage * 0.25f; // startingAlpha = 200;
}
fadeTime *= percentageOfFlash; fadeHold *= percentageOfFlash;
if ( bPlayer ) { // blind players and bots
CCSPlayer *player = static_cast< CCSPlayer * >( pEntity );
// [tj] Store who was responsible for the most recent flashbang blinding.
CCSPlayer *attacker = ToCSPlayer (pevAttacker); if ( attacker && player && player->IsAlive() ) { player->SetLastFlashbangAttacker(attacker);
// score points/penalties for blinding players
if ( flDot >= 0.0f ) { if ( attacker->GetTeamNumber() == player->GetTeamNumber() ) CSGameRules()->ScoreBlindFriendly( attacker ); else CSGameRules()->ScoreBlindEnemy( attacker ); } }
player->Blind( fadeHold, fadeTime, startingAlpha ); // fire an event when a player has been sufficiently blinded as to not
// be able to perform the training map flashbang range test
if ( CSGameRules()->IsPlayingTraining() && fadeHold > 1.9f ) { IGameEvent * event = gameeventmanager->CreateEvent( "tr_player_flashbanged" ); if ( event ) { event->SetInt( "userid", player->GetUserID() ); gameeventmanager->FireEvent( event ); } } IGameEvent * event = gameeventmanager->CreateEvent( "player_blind" ); if ( event ) { event->SetInt( "userid", player->GetUserID() ); event->SetInt( "attacker", attacker ? attacker->GetUserID() : 0 ); event->SetInt( "entityid", pevInflictor ? pevInflictor->entindex() : 0 ); event->SetFloat( "blind_duration", player->m_flFlashDuration ); gameeventmanager->FireEvent( event ); }
// deafen players and bots
player->Deafen( flDistance ); } } } }
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, const CCSWeaponInfo& weaponInfo ) { 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_pWeaponInfo = &weaponInfo;
pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity ); pGrenade->SetCollisionGroup( COLLISION_GROUP_PROJECTILE ); return pGrenade; }
CFlashbangProjectile::CFlashbangProjectile() { m_flDamage = 100; // default timer value for when a player throws it
// can be overridden when spawned from an entity maker
m_flTimeToDetonate = 1.5; m_numOpponentsHit = m_numTeammatesHit = 0; }
void CFlashbangProjectile::Spawn() { SetModel( GRENADE_MODEL );
SetDetonateTimerLength( m_flTimeToDetonate );
SetTouch( &CBaseGrenade::BounceTouch );
SetThink( &CBaseCSGrenadeProjectile::DangerSoundThink ); SetNextThink( gpGlobals->curtime );
SetGravity( BaseClass::GetGrenadeGravity() ); SetFriction( BaseClass::GetGrenadeFriction() ); SetElasticity( BaseClass::GetGrenadeElasticity() );
m_pWeaponInfo = GetWeaponInfo( WEAPON_FLASHBANG );
BaseClass::Spawn();
SetBodygroupPreset( "thrown" ); }
void CFlashbangProjectile::Precache() { PrecacheModel( GRENADE_MODEL );
PrecacheScriptSound( "Flashbang.Explode" ); PrecacheScriptSound( "Flashbang.Bounce" );
BaseClass::Precache(); }
ConVar sv_flashbang_strength( "sv_flashbang_strength", "3.55", FCVAR_REPLICATED, "Flashbang strength", true, 2.0, true, 8.0 );
void CFlashbangProjectile::Detonate() { // tell the bots a flashbang grenade has exploded (and record log events)
CCSPlayer *player = ToCSPlayer( GetThrower() ); if ( player ) { IGameEvent * event = gameeventmanager->CreateEvent( "flashbang_detonate" ); if ( event ) { event->SetInt( "userid", player->GetUserID() ); event->SetInt( "entityid", this->entindex() ); event->SetFloat( "x", GetAbsOrigin().x ); event->SetFloat( "y", GetAbsOrigin().y ); event->SetFloat( "z", GetAbsOrigin().z ); gameeventmanager->FireEvent( event ); } }
RadiusFlash ( GetAbsOrigin(), this, GetThrower(), sv_flashbang_strength.GetInt(), CLASS_NONE, DMG_BLAST, &m_numOpponentsHit, &m_numTeammatesHit ); EmitSound( "Flashbang.Explode" );
trace_t tr; Vector vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 2 ); UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -64 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr); UTIL_DecalTrace( &tr, "Scorch" );
// Because we don't chain to base, tell ogs to record this detonation here
RecordDetonation();
UTIL_Remove( this ); }
//TODO: Let physics handle the sound!
void CFlashbangProjectile::BounceSound( void ) { EmitSound( "Flashbang.Bounce" ); }
void CFlashbangProjectile::InputSetTimer( inputdata_t &inputdata ) { m_flTimeToDetonate = inputdata.value.Float(); SetDetonateTimerLength( m_flTimeToDetonate ); }
|