initial
This commit is contained in:
451
game/shared/cstrike15/flashbang_projectile.cpp
Normal file
451
game/shared/cstrike15/flashbang_projectile.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
//========= Copyright <20> 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 );
|
||||
}
|
||||
Reference in New Issue
Block a user