Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

451 lines
13 KiB

//========= 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 );
}