|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "ai_hint.h"
#include "env_headcrabcanister_shared.h"
#include "explode.h"
#include "beam_shared.h"
#include "SpriteTrail.h"
#include "ar2_explosion.h"
#include "SkyCamera.h"
#include "smoke_trail.h"
#include "ai_basenpc.h"
#include "npc_headcrab.h"
#include "ai_motor.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Models!
//-----------------------------------------------------------------------------
#define ENV_HEADCRABCANISTER_MODEL "models/props_combine/headcrabcannister01a.mdl"
#define ENV_HEADCRABCANISTER_BROKEN_MODEL "models/props_combine/headcrabcannister01b.mdl"
#define ENV_HEADCRABCANISTER_SKYBOX_MODEL "models/props_combine/headcrabcannister01a_skybox.mdl"
#define ENV_HEADCRABCANISTER_INCOMING_SOUND_TIME 1.0f
ConVar sk_env_headcrabcanister_shake_amplitude( "sk_env_headcrabcanister_shake_amplitude", "50" ); ConVar sk_env_headcrabcanister_shake_radius( "sk_env_headcrabcanister_shake_radius", "1024" ); ConVar sk_env_headcrabcanister_shake_radius_vehicle( "sk_env_headcrabcanister_shake_radius_vehicle", "2500" );
#define ENV_HEADCRABCANISTER_TRAIL_TIME 3.0f
//-----------------------------------------------------------------------------
// Spawn flags
//-----------------------------------------------------------------------------
enum { SF_NO_IMPACT_SOUND = 0x1, SF_NO_LAUNCH_SOUND = 0x2, SF_START_IMPACTED = 0x1000, SF_LAND_AT_INITIAL_POSITION = 0x2000, SF_WAIT_FOR_INPUT_TO_OPEN = 0x4000, SF_WAIT_FOR_INPUT_TO_SPAWN_HEADCRABS = 0x8000, SF_NO_SMOKE = 0x10000, SF_NO_SHAKE = 0x20000, SF_REMOVE_ON_IMPACT = 0x40000, SF_NO_IMPACT_EFFECTS = 0x80000, };
//-----------------------------------------------------------------------------
// Headcrab types
//-----------------------------------------------------------------------------
static const char *s_pHeadcrabClass[] = { "npc_headcrab", "npc_headcrab_fast", "npc_headcrab_poison", };
//-----------------------------------------------------------------------------
// Context think
//-----------------------------------------------------------------------------
static const char *s_pOpenThinkContext = "OpenThink"; static const char *s_pHeadcrabThinkContext = "HeadcrabThink";
//-----------------------------------------------------------------------------
// HeadcrabCanister Class
//-----------------------------------------------------------------------------
class CEnvHeadcrabCanister : public CBaseAnimating { DECLARE_CLASS( CEnvHeadcrabCanister, CBaseAnimating ); DECLARE_DATADESC(); DECLARE_SERVERCLASS();
public:
// Initialization
CEnvHeadcrabCanister();
virtual void Precache( void ); virtual void Spawn( void ); virtual void UpdateOnRemove();
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
private: void InputFireCanister( inputdata_t &inputdata ); void InputOpenCanister( inputdata_t &inputdata ); void InputSpawnHeadcrabs( inputdata_t &inputdata ); void InputStopSmoke( inputdata_t &inputdata );
// Think(s)
void HeadcrabCanisterSkyboxThink( void ); void HeadcrabCanisterWorldThink( void ); void HeadcrabCanisterSpawnHeadcrabThink(); void HeadcrabCanisterSkyboxOnlyThink( void ); void HeadcrabCanisterSkyboxRestartThink( void ); void WaitForOpenSequenceThink();
// Place the canister in the world
CSkyCamera* PlaceCanisterInWorld();
// Check for impacts
void TestForCollisionsAgainstEntities( const Vector &vecEndPosition ); void TestForCollisionsAgainstWorld( const Vector &vecEndPosition );
// Figure out where we enter the world
void ComputeWorldEntryPoint( Vector *pStartPosition, QAngle *pStartAngles, Vector *pStartDirection );
// Blows up!
void Detonate( void );
// Landed!
void SetLanded( void ); void Landed( void );
// Open!
void OpenCanister( void ); void CanisterFinishedOpening();
// Set up the world model
void SetupWorldModel();
// Start spawning headcrabs
void StartSpawningHeadcrabs( float flDelay );
private: CNetworkVar( bool, m_bLanded );
CNetworkVarEmbedded( CEnvHeadcrabCanisterShared, m_Shared ); CHandle<CSpriteTrail> m_hTrail; CHandle<SmokeTrail> m_hSmokeTrail; int m_nHeadcrabType; int m_nHeadcrabCount; Vector m_vecImpactPosition; float m_flDamageRadius; float m_flDamage; bool m_bIncomingSoundStarted; bool m_bHasDetonated; bool m_bLaunched; bool m_bOpened; float m_flSmokeLifetime; string_t m_iszLaunchPositionName;
COutputEHANDLE m_OnLaunched; COutputEvent m_OnImpacted; COutputEvent m_OnOpened;
// Only for skybox only cannisters.
float m_flMinRefireTime; float m_flMaxRefireTime; int m_nSkyboxCannisterCount; };
//=============================================================================
//
// HeadcrabCanister Functions
//
LINK_ENTITY_TO_CLASS( env_headcrabcanister, CEnvHeadcrabCanister );
BEGIN_DATADESC( CEnvHeadcrabCanister )
DEFINE_FIELD( m_bLanded, FIELD_BOOLEAN ), DEFINE_EMBEDDED( m_Shared ), DEFINE_FIELD( m_hTrail, FIELD_EHANDLE ), DEFINE_FIELD( m_hSmokeTrail, FIELD_EHANDLE ), DEFINE_KEYFIELD( m_nHeadcrabType, FIELD_INTEGER, "HeadcrabType" ), DEFINE_KEYFIELD( m_nHeadcrabCount, FIELD_INTEGER, "HeadcrabCount" ), DEFINE_KEYFIELD( m_flSmokeLifetime, FIELD_FLOAT, "SmokeLifetime" ), DEFINE_KEYFIELD( m_iszLaunchPositionName, FIELD_STRING, "LaunchPositionName" ), DEFINE_FIELD( m_vecImpactPosition, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_bIncomingSoundStarted, FIELD_BOOLEAN ), DEFINE_FIELD( m_bHasDetonated, FIELD_BOOLEAN ), DEFINE_FIELD( m_bLaunched, FIELD_BOOLEAN ), DEFINE_FIELD( m_bOpened, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_flMinRefireTime, FIELD_FLOAT, "MinSkyboxRefireTime" ), DEFINE_KEYFIELD( m_flMaxRefireTime, FIELD_FLOAT, "MaxSkyboxRefireTime" ), DEFINE_KEYFIELD( m_nSkyboxCannisterCount, FIELD_INTEGER, "SkyboxCannisterCount" ), DEFINE_KEYFIELD( m_flDamageRadius, FIELD_FLOAT, "DamageRadius" ), DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ),
// Function Pointers.
DEFINE_FUNCTION( HeadcrabCanisterSkyboxThink ), DEFINE_FUNCTION( HeadcrabCanisterWorldThink ), DEFINE_FUNCTION( HeadcrabCanisterSpawnHeadcrabThink ), DEFINE_FUNCTION( WaitForOpenSequenceThink ), DEFINE_FUNCTION( HeadcrabCanisterSkyboxOnlyThink ), DEFINE_FUNCTION( HeadcrabCanisterSkyboxRestartThink ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "FireCanister", InputFireCanister ), DEFINE_INPUTFUNC( FIELD_VOID, "OpenCanister", InputOpenCanister ), DEFINE_INPUTFUNC( FIELD_VOID, "SpawnHeadcrabs", InputSpawnHeadcrabs ), DEFINE_INPUTFUNC( FIELD_VOID, "StopSmoke", InputStopSmoke ),
// Outputs
DEFINE_OUTPUT( m_OnLaunched, "OnLaunched" ), DEFINE_OUTPUT( m_OnImpacted, "OnImpacted" ), DEFINE_OUTPUT( m_OnOpened, "OnOpened" ),
END_DATADESC()
EXTERN_SEND_TABLE(DT_EnvHeadcrabCanisterShared);
IMPLEMENT_SERVERCLASS_ST( CEnvHeadcrabCanister, DT_EnvHeadcrabCanister ) SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE(DT_EnvHeadcrabCanisterShared) ), SendPropBool( SENDINFO( m_bLanded ) ), END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CEnvHeadcrabCanister::CEnvHeadcrabCanister() { m_flMinRefireTime = -1.0f; m_flMaxRefireTime = -1.0f; }
//-----------------------------------------------------------------------------
// Precache!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::Precache( void ) { BaseClass::Precache(); PrecacheModel( ENV_HEADCRABCANISTER_MODEL ); PrecacheModel( ENV_HEADCRABCANISTER_BROKEN_MODEL ); PrecacheModel( ENV_HEADCRABCANISTER_SKYBOX_MODEL ); PrecacheModel("sprites/smoke.vmt");
PrecacheScriptSound( "HeadcrabCanister.LaunchSound" ); PrecacheScriptSound( "HeadcrabCanister.AfterLanding" ); PrecacheScriptSound( "HeadcrabCanister.Explosion" ); PrecacheScriptSound( "HeadcrabCanister.IncomingSound" ); PrecacheScriptSound( "HeadcrabCanister.SkyboxExplosion" ); PrecacheScriptSound( "HeadcrabCanister.Open" );
UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); }
//-----------------------------------------------------------------------------
// Spawn!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::Spawn( void ) { Precache(); BaseClass::Spawn();
// Do we have a position to launch from?
if ( m_iszLaunchPositionName != NULL_STRING ) { // It doesn't have any real presence at first.
SetSolid( SOLID_NONE );
m_vecImpactPosition = GetAbsOrigin(); m_bIncomingSoundStarted = false; m_bLanded = false; m_bHasDetonated = false; m_bOpened = false; } else if ( !HasSpawnFlags( SF_START_IMPACTED ) ) { // It doesn't have any real presence at first.
SetSolid( SOLID_NONE );
if ( !HasSpawnFlags( SF_LAND_AT_INITIAL_POSITION ) ) { Vector vecForward; GetVectors( &vecForward, NULL, NULL ); vecForward *= -1.0f;
trace_t trace; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecForward * 10000, MASK_NPCWORLDSTATIC, this, COLLISION_GROUP_NONE, &trace );
m_vecImpactPosition = trace.endpos; } else { m_vecImpactPosition = GetAbsOrigin(); }
m_bIncomingSoundStarted = false; m_bLanded = false; m_bHasDetonated = false; m_bOpened = false; } else { m_bHasDetonated = true; m_bIncomingSoundStarted = true; m_bOpened = false; m_vecImpactPosition = GetAbsOrigin(); Landed(); } }
//-----------------------------------------------------------------------------
// On remove!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::UpdateOnRemove() { BaseClass::UpdateOnRemove(); StopSound( "HeadcrabCanister.AfterLanding" ); if ( m_hTrail ) { UTIL_Remove( m_hTrail ); m_hTrail = NULL; } if ( m_hSmokeTrail ) { UTIL_Remove( m_hSmokeTrail ); m_hSmokeTrail = NULL; } }
//-----------------------------------------------------------------------------
// Set up the world model
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::SetupWorldModel() { SetModel( ENV_HEADCRABCANISTER_MODEL ); SetSolid( SOLID_BBOX );
float flRadius = CollisionProp()->BoundingRadius(); Vector vecMins( -flRadius, -flRadius, -flRadius ); Vector vecMaxs( flRadius, flRadius, flRadius ); SetSize( vecMins, vecMaxs );
}
//-----------------------------------------------------------------------------
// Figure out where we enter the world
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::ComputeWorldEntryPoint( Vector *pStartPosition, QAngle *pStartAngles, Vector *pStartDirection ) { SetupWorldModel();
Vector vecForward; GetVectors( &vecForward, NULL, NULL );
// Raycast up to the place where we should start from (start raycast slightly off the ground,
// since it'll be buried in the ground oftentimes)
trace_t tr; CTraceFilterWorldOnly filter; UTIL_TraceLine( GetAbsOrigin() + vecForward * 100, GetAbsOrigin() + vecForward * 10000, CONTENTS_SOLID, &filter, &tr );
*pStartPosition = tr.endpos; *pStartAngles = GetAbsAngles(); VectorMultiply( vecForward, -1.0f, *pStartDirection ); }
//-----------------------------------------------------------------------------
// Place the canister in the world
//-----------------------------------------------------------------------------
CSkyCamera *CEnvHeadcrabCanister::PlaceCanisterInWorld() { CSkyCamera *pCamera = NULL;
// Are we launching from a point? If so, use that point.
if ( m_iszLaunchPositionName != NULL_STRING ) { // Get the launch position entity
CBaseEntity *pLaunchPos = gEntList.FindEntityByName( NULL, m_iszLaunchPositionName ); if ( !pLaunchPos ) { Warning("%s (%s) could not find an entity matching LaunchPositionName of '%s'\n", GetEntityName().ToCStr(), GetDebugName(), STRING(m_iszLaunchPositionName) ); SUB_Remove(); } else { SetupWorldModel();
Vector vecForward, vecImpactDirection; GetVectors( &vecForward, NULL, NULL ); VectorMultiply( vecForward, -1.0f, vecImpactDirection );
m_Shared.InitInWorld( gpGlobals->curtime, pLaunchPos->GetAbsOrigin(), GetAbsAngles(), vecImpactDirection, m_vecImpactPosition, true ); SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterWorldThink ); SetNextThink( gpGlobals->curtime ); } } else if ( DetectInSkybox() ) { pCamera = GetEntitySkybox();
SetModel( ENV_HEADCRABCANISTER_SKYBOX_MODEL ); SetSolid( SOLID_NONE );
Vector vecForward; GetVectors( &vecForward, NULL, NULL ); vecForward *= -1.0f;
m_Shared.InitInSkybox( gpGlobals->curtime, m_vecImpactPosition, GetAbsAngles(), vecForward, m_vecImpactPosition, pCamera->m_skyboxData.origin, pCamera->m_skyboxData.scale ); AddEFlags( EFL_IN_SKYBOX ); SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterSkyboxOnlyThink ); SetNextThink( gpGlobals->curtime + m_Shared.GetEnterWorldTime() + TICK_INTERVAL ); } else { Vector vecStartPosition, vecDirection; QAngle vecStartAngles; ComputeWorldEntryPoint( &vecStartPosition, &vecStartAngles, &vecDirection );
// Figure out which skybox to place the entity in.
pCamera = GetCurrentSkyCamera(); if ( pCamera ) { m_Shared.InitInSkybox( gpGlobals->curtime, vecStartPosition, vecStartAngles, vecDirection, m_vecImpactPosition, pCamera->m_skyboxData.origin, pCamera->m_skyboxData.scale );
if ( m_Shared.IsInSkybox() ) { SetModel( ENV_HEADCRABCANISTER_SKYBOX_MODEL ); SetSolid( SOLID_NONE ); AddEFlags( EFL_IN_SKYBOX ); SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterSkyboxThink ); SetNextThink( gpGlobals->curtime + m_Shared.GetEnterWorldTime() ); } else { SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterWorldThink ); SetNextThink( gpGlobals->curtime ); } } else { m_Shared.InitInWorld( gpGlobals->curtime, vecStartPosition, vecStartAngles, vecDirection, m_vecImpactPosition ); SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterWorldThink ); SetNextThink( gpGlobals->curtime ); } }
Vector vecEndPosition; QAngle vecEndAngles; m_Shared.GetPositionAtTime( gpGlobals->curtime, vecEndPosition, vecEndAngles ); SetAbsOrigin( vecEndPosition ); SetAbsAngles( vecEndAngles );
return pCamera; }
//-----------------------------------------------------------------------------
// Fires the canister!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::InputFireCanister( inputdata_t &inputdata ) { if (m_bLaunched) return;
m_bLaunched = true;
if ( HasSpawnFlags( SF_START_IMPACTED ) ) { StartSpawningHeadcrabs( 0.01f ); return; }
// Play a firing sound
CPASAttenuationFilter filter( this, ATTN_NONE );
if ( !HasSpawnFlags( SF_NO_LAUNCH_SOUND ) ) { EmitSound( filter, entindex(), "HeadcrabCanister.LaunchSound" ); }
// Place the canister
CSkyCamera *pCamera = PlaceCanisterInWorld();
// Hook up a smoke trail
m_hTrail = CSpriteTrail::SpriteTrailCreate( "sprites/smoke.vmt", GetAbsOrigin(), true ); m_hTrail->SetTransparency( kRenderTransAdd, 224, 224, 255, 255, kRenderFxNone ); m_hTrail->SetAttachment( this, 0 ); m_hTrail->SetStartWidth( 32.0 ); m_hTrail->SetEndWidth( 200.0 ); m_hTrail->SetStartWidthVariance( 15.0f ); m_hTrail->SetTextureResolution( 0.002 ); m_hTrail->SetLifeTime( ENV_HEADCRABCANISTER_TRAIL_TIME ); m_hTrail->SetMinFadeLength( 1000.0f );
if ( pCamera && m_Shared.IsInSkybox() ) { m_hTrail->SetSkybox( pCamera->m_skyboxData.origin, pCamera->m_skyboxData.scale ); }
// Fire that output!
m_OnLaunched.Set( this, this, this ); }
//-----------------------------------------------------------------------------
// Opens the canister!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::InputOpenCanister( inputdata_t &inputdata ) { if ( m_bLanded && !m_bOpened && HasSpawnFlags( SF_WAIT_FOR_INPUT_TO_OPEN ) ) { OpenCanister(); } }
//-----------------------------------------------------------------------------
// Spawns headcrabs
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::InputSpawnHeadcrabs( inputdata_t &inputdata ) { if ( m_bLanded && m_bOpened && HasSpawnFlags( SF_WAIT_FOR_INPUT_TO_SPAWN_HEADCRABS ) ) { StartSpawningHeadcrabs( 0.01f ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::InputStopSmoke( inputdata_t &inputdata ) { if ( m_hSmokeTrail != NULL ) { UTIL_Remove( m_hSmokeTrail ); m_hSmokeTrail = NULL; } }
//=============================================================================
//
// Enumerator for swept bbox collision.
//
class CCollideList : public IEntityEnumerator { public: CCollideList( Ray_t *pRay, CBaseEntity* pIgnoreEntity, int nContentsMask ) : m_Entities( 0, 32 ), m_pIgnoreEntity( pIgnoreEntity ), m_nContentsMask( nContentsMask ), m_pRay(pRay) {}
virtual bool EnumEntity( IHandleEntity *pHandleEntity ) { // Don't bother with the ignore entity.
if ( pHandleEntity == m_pIgnoreEntity ) return true;
Assert( pHandleEntity );
trace_t tr; enginetrace->ClipRayToEntity( *m_pRay, m_nContentsMask, pHandleEntity, &tr ); if (( tr.fraction < 1.0f ) || (tr.startsolid) || (tr.allsolid)) { CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); m_Entities.AddToTail( pEntity ); }
return true; }
CUtlVector<CBaseEntity*> m_Entities;
private: CBaseEntity *m_pIgnoreEntity; int m_nContentsMask; Ray_t *m_pRay; };
//-----------------------------------------------------------------------------
// Test for impact!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::TestForCollisionsAgainstEntities( const Vector &vecEndPosition ) { // Debugging!!
// NDebugOverlay::Box( GetAbsOrigin(), m_vecMin * 0.5f, m_vecMax * 0.5f, 255, 255, 0, 0, 5 );
// NDebugOverlay::Box( vecEndPosition, m_vecMin, m_vecMax, 255, 0, 0, 0, 5 );
float flRadius = CollisionProp()->BoundingRadius(); Vector vecMins( -flRadius, -flRadius, -flRadius ); Vector vecMaxs( flRadius, flRadius, flRadius );
Ray_t ray; ray.Init( GetAbsOrigin(), vecEndPosition, vecMins, vecMaxs );
CCollideList collideList( &ray, this, MASK_SOLID ); enginetrace->EnumerateEntities( ray, false, &collideList );
float flDamage = m_flDamage;
// Now get each entity and react accordinly!
for( int iEntity = collideList.m_Entities.Count(); --iEntity >= 0; ) { CBaseEntity *pEntity = collideList.m_Entities[iEntity]; Vector vecForceDir = m_Shared.m_vecDirection;
// Check for a physics object and apply force!
IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject ) { float flMass = PhysGetEntityMass( pEntity ); vecForceDir *= flMass * 750; pPhysObject->ApplyForceCenter( vecForceDir ); }
if ( pEntity->m_takedamage && ( m_flDamage != 0.0f ) ) { CTakeDamageInfo info( this, this, flDamage, DMG_BLAST ); CalculateExplosiveDamageForce( &info, vecForceDir, pEntity->GetAbsOrigin() ); pEntity->TakeDamage( info ); } } }
//-----------------------------------------------------------------------------
// Test for impact!
//-----------------------------------------------------------------------------
#define INNER_RADIUS_FRACTION 0.25f
void CEnvHeadcrabCanister::TestForCollisionsAgainstWorld( const Vector &vecEndPosition ) { // Splash damage!
// Iterate on all entities in the vicinity.
float flDamageRadius = m_flDamageRadius; float flDamage = m_flDamage;
CBaseEntity *pEntity; for ( CEntitySphereQuery sphere( vecEndPosition, flDamageRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { if ( pEntity == this ) continue;
if ( !pEntity->IsSolid() ) continue;
// Get distance to object and use it as a scale value.
Vector vecSegment; VectorSubtract( pEntity->GetAbsOrigin(), vecEndPosition, vecSegment ); float flDistance = VectorNormalize( vecSegment );
float flFactor = 1.0f / ( flDamageRadius * (INNER_RADIUS_FRACTION - 1) ); flFactor *= flFactor; float flScale = flDistance - flDamageRadius; flScale *= flScale * flFactor; if ( flScale > 1.0f ) { flScale = 1.0f; } // Check for a physics object and apply force!
Vector vecForceDir = vecSegment; IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if ( pPhysObject ) { // Send it flying!!!
float flMass = PhysGetEntityMass( pEntity ); vecForceDir *= flMass * 750 * flScale; pPhysObject->ApplyForceCenter( vecForceDir ); }
if ( pEntity->m_takedamage && ( m_flDamage != 0.0f ) ) { CTakeDamageInfo info( this, this, flDamage * flScale, DMG_BLAST ); CalculateExplosiveDamageForce( &info, vecSegment, pEntity->GetAbsOrigin() ); pEntity->TakeDamage( info ); }
if ( pEntity->IsPlayer() && !(static_cast<CBasePlayer*>(pEntity)->IsInAVehicle()) ) { if (vecSegment.z < 0.1f) { vecSegment.z = 0.1f; VectorNormalize( vecSegment ); } float flAmount = SimpleSplineRemapVal( flScale, 0.0f, 1.0f, 250.0f, 1000.0f ); pEntity->ApplyAbsVelocityImpulse( vecSegment * flAmount ); } } }
//-----------------------------------------------------------------------------
// Headcrab creation
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink() { Vector vecSpawnPosition; QAngle vecSpawnAngles;
--m_nHeadcrabCount;
int nHeadCrabAttachment = LookupAttachment( "headcrab" ); if ( GetAttachment( nHeadCrabAttachment, vecSpawnPosition, vecSpawnAngles ) ) { CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[m_nHeadcrabType] ); CBaseHeadcrab *pHeadCrab = assert_cast<CBaseHeadcrab*>(pEnt);
// Necessary to get it to eject properly (don't allow the NPC
// to override the spawn position specified).
pHeadCrab->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
// So we don't collide with the canister
// NOTE: Hierarchical attachment is necessary here to get the animations to work
pHeadCrab->SetOwnerEntity( this ); DispatchSpawn( pHeadCrab ); pHeadCrab->SetParent( this, nHeadCrabAttachment ); pHeadCrab->SetLocalOrigin( vec3_origin ); pHeadCrab->SetLocalAngles( vec3_angle ); pHeadCrab->CrawlFromCanister(); }
if ( m_nHeadcrabCount != 0 ) { float flWaitTime = random->RandomFloat( 1.0f, 2.0f ); SetContextThink( &CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink, gpGlobals->curtime + flWaitTime, s_pHeadcrabThinkContext ); } else { SetContextThink( NULL, gpGlobals->curtime, s_pHeadcrabThinkContext ); } }
//-----------------------------------------------------------------------------
// Start spawning headcrabs
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::StartSpawningHeadcrabs( float flDelay ) { if ( !m_bLanded || !m_bOpened || m_nHeadcrabCount == 0 ) return;
if ( m_nHeadcrabCount != 0 ) { SetContextThink( &CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink, gpGlobals->curtime + flDelay, s_pHeadcrabThinkContext ); } }
//-----------------------------------------------------------------------------
// Canister finished opening
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::CanisterFinishedOpening( void ) { ResetSequence( LookupSequence( "idle_open" ) ); m_OnOpened.FireOutput( this, this, 0 ); m_bOpened = true; SetContextThink( NULL, gpGlobals->curtime, s_pOpenThinkContext );
if ( !HasSpawnFlags( SF_START_IMPACTED ) ) { if ( !HasSpawnFlags( SF_WAIT_FOR_INPUT_TO_SPAWN_HEADCRABS ) ) { StartSpawningHeadcrabs( 3.0f ); } } }
//-----------------------------------------------------------------------------
// Finish the opening sequence
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::WaitForOpenSequenceThink() { StudioFrameAdvance(); if ( ( GetSequence() == LookupSequence( "open" ) ) && IsSequenceFinished() ) { CanisterFinishedOpening(); } else { SetContextThink( &CEnvHeadcrabCanister::WaitForOpenSequenceThink, gpGlobals->curtime + 0.01f, s_pOpenThinkContext ); } }
//-----------------------------------------------------------------------------
// Open the canister!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::OpenCanister( void ) { if ( m_bOpened ) return;
int nOpenSequence = LookupSequence( "open" ); if ( nOpenSequence != ACT_INVALID ) { EmitSound( "HeadcrabCanister.Open" );
ResetSequence( nOpenSequence ); SetContextThink( &CEnvHeadcrabCanister::WaitForOpenSequenceThink, gpGlobals->curtime + 0.01f, s_pOpenThinkContext ); } else { CanisterFinishedOpening(); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::SetLanded( void ) { SetAbsOrigin( m_vecImpactPosition ); SetModel( ENV_HEADCRABCANISTER_BROKEN_MODEL ); SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_VPHYSICS ); VPhysicsInitStatic(); IncrementInterpolationFrame(); m_bLanded = true; }
//-----------------------------------------------------------------------------
// Landed!
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::Landed( void ) { EmitSound( "HeadcrabCanister.AfterLanding" );
// Lock us now that we've stopped
SetLanded();
// Hook the follow trail to the lead of the canister (which should be buried)
// to hide problems with the edge of the follow trail
if (m_hTrail) { m_hTrail->SetAttachment( this, LookupAttachment("trail") ); }
// Start smoke, unless we don't want it
if ( !HasSpawnFlags( SF_NO_SMOKE ) ) { // Create the smoke trail to obscure the headcrabs
m_hSmokeTrail = SmokeTrail::CreateSmokeTrail(); m_hSmokeTrail->FollowEntity( this, "smoke" );
m_hSmokeTrail->m_SpawnRate = 8; m_hSmokeTrail->m_ParticleLifetime = 2.0f;
m_hSmokeTrail->m_StartColor.Init( 0.7f, 0.7f, 0.7f ); m_hSmokeTrail->m_EndColor.Init( 0.6, 0.6, 0.6 );
m_hSmokeTrail->m_StartSize = 32; m_hSmokeTrail->m_EndSize = 64; m_hSmokeTrail->m_SpawnRadius= 8; m_hSmokeTrail->m_MinSpeed = 0; m_hSmokeTrail->m_MaxSpeed = 8; m_hSmokeTrail->m_MinDirectedSpeed = 32; m_hSmokeTrail->m_MaxDirectedSpeed = 64; m_hSmokeTrail->m_Opacity = 0.35f;
m_hSmokeTrail->SetLifetime( m_flSmokeLifetime ); }
SetThink( NULL );
if ( !HasSpawnFlags( SF_WAIT_FOR_INPUT_TO_OPEN ) ) { if ( HasSpawnFlags( SF_START_IMPACTED ) ) { CanisterFinishedOpening( ); } else { OpenCanister(); } } }
//-----------------------------------------------------------------------------
// Creates the explosion effect
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::Detonate( ) { // Send the impact output
m_OnImpacted.FireOutput( this, this, 0 );
if ( !HasSpawnFlags( SF_NO_IMPACT_SOUND ) ) { StopSound( "HeadcrabCanister.IncomingSound" ); EmitSound( "HeadcrabCanister.Explosion" ); }
// If we're supposed to be removed, do that now
if ( HasSpawnFlags( SF_REMOVE_ON_IMPACT ) ) { SetAbsOrigin( m_vecImpactPosition ); SetModel( ENV_HEADCRABCANISTER_BROKEN_MODEL ); SetMoveType( MOVETYPE_NONE ); IncrementInterpolationFrame(); m_bLanded = true; // Become invisible so our trail can finish up
AddEffects( EF_NODRAW ); SetSolidFlags( FSOLID_NOT_SOLID );
SetThink( &CEnvHeadcrabCanister::SUB_Remove ); SetNextThink( gpGlobals->curtime + ENV_HEADCRABCANISTER_TRAIL_TIME );
return; }
// Test for damaging things
TestForCollisionsAgainstWorld( m_vecImpactPosition );
// Shake the screen unless flagged otherwise
if ( !HasSpawnFlags( SF_NO_SHAKE ) ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
// If the player is on foot, then do a more limited shake
float shakeRadius = ( pPlayer && pPlayer->IsInAVehicle() ) ? sk_env_headcrabcanister_shake_radius_vehicle.GetFloat() : sk_env_headcrabcanister_shake_radius.GetFloat();
UTIL_ScreenShake( m_vecImpactPosition, sk_env_headcrabcanister_shake_amplitude.GetFloat(), 150.0, 1.0, shakeRadius, SHAKE_START ); }
// Do explosion effects
if ( !HasSpawnFlags( SF_NO_IMPACT_EFFECTS ) ) { // Normal explosion
ExplosionCreate( m_vecImpactPosition, GetAbsAngles(), this, 50.0f, 500.0f, SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSOUND, 1300.0f ); // Dust explosion
AR2Explosion *pExplosion = AR2Explosion::CreateAR2Explosion( m_vecImpactPosition ); if( pExplosion ) { pExplosion->SetLifetime(10); } } }
//-----------------------------------------------------------------------------
// Purpose: This think function simulates (moves/collides) the HeadcrabCanister while in
// the world.
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::HeadcrabCanisterWorldThink( void ) { // Get the current time.
float flTime = gpGlobals->curtime;
Vector vecStartPosition = GetAbsOrigin();
// Update HeadcrabCanister position for swept collision test.
Vector vecEndPosition; QAngle vecEndAngles; m_Shared.GetPositionAtTime( flTime, vecEndPosition, vecEndAngles );
if ( !m_bIncomingSoundStarted && !HasSpawnFlags( SF_NO_IMPACT_SOUND ) ) { float flDistSq = ENV_HEADCRABCANISTER_INCOMING_SOUND_TIME * m_Shared.m_flFlightSpeed; flDistSq *= flDistSq; if ( vecEndPosition.DistToSqr(m_vecImpactPosition) <= flDistSq ) { // Figure out if we're close enough to play the incoming sound
EmitSound( "HeadcrabCanister.IncomingSound" ); m_bIncomingSoundStarted = true; } }
TestForCollisionsAgainstEntities( vecEndPosition ); if ( m_Shared.DidImpact( flTime ) ) { if ( !m_bHasDetonated ) { Detonate(); m_bHasDetonated = true; } if ( !HasSpawnFlags( SF_REMOVE_ON_IMPACT ) ) { Landed(); }
return; } // Always move full movement.
SetAbsOrigin( vecEndPosition );
// Touch triggers along the way
PhysicsTouchTriggers( &vecStartPosition );
SetNextThink( gpGlobals->curtime + 0.2f ); SetAbsAngles( vecEndAngles );
if ( !m_bHasDetonated ) { if ( vecEndPosition.DistToSqr( m_vecImpactPosition ) < BoundingRadius() * BoundingRadius() ) { Detonate(); m_bHasDetonated = true; } } }
//-----------------------------------------------------------------------------
// Purpose: This think function should be called at the time when the HeadcrabCanister
// will be leaving the skybox and entering the world.
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::HeadcrabCanisterSkyboxThink( void ) { // Use different position computation
m_Shared.ConvertFromSkyboxToWorld();
Vector vecEndPosition; QAngle vecEndAngles; m_Shared.GetPositionAtTime( gpGlobals->curtime, vecEndPosition, vecEndAngles ); UTIL_SetOrigin( this, vecEndPosition ); SetAbsAngles( vecEndAngles ); RemoveEFlags( EFL_IN_SKYBOX );
// Switch to the actual-scale model
SetupWorldModel();
// Futz with the smoke trail to get it working across the boundary
m_hTrail->SetSkybox( vec3_origin, 1.0f );
// Now we start looking for collisions
SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterWorldThink ); SetNextThink( gpGlobals->curtime + 0.01f ); }
//-----------------------------------------------------------------------------
// Purpose: This stops its motion in the skybox
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::HeadcrabCanisterSkyboxOnlyThink( void ) { Vector vecEndPosition; QAngle vecEndAngles; m_Shared.GetPositionAtTime( gpGlobals->curtime, vecEndPosition, vecEndAngles ); UTIL_SetOrigin( this, vecEndPosition ); SetAbsAngles( vecEndAngles );
if ( !HasSpawnFlags( SF_NO_IMPACT_SOUND ) ) { CPASAttenuationFilter filter( this, ATTN_NONE ); EmitSound( filter, entindex(), "HeadcrabCanister.SkyboxExplosion" ); }
if ( m_nSkyboxCannisterCount != 0 ) { if ( --m_nSkyboxCannisterCount <= 0 ) { SetThink( NULL ); return; } }
float flRefireTime = random->RandomFloat( m_flMinRefireTime, m_flMaxRefireTime ) + ENV_HEADCRABCANISTER_TRAIL_TIME; SetThink( &CEnvHeadcrabCanister::HeadcrabCanisterSkyboxRestartThink ); SetNextThink( gpGlobals->curtime + flRefireTime ); }
//-----------------------------------------------------------------------------
// This will re-fire the headcrab cannister
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::HeadcrabCanisterSkyboxRestartThink( void ) { if ( m_hTrail ) { UTIL_Remove( m_hTrail ); m_hTrail = NULL; }
m_bLaunched = false;
inputdata_t data; InputFireCanister( data ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pInfo -
// bAlways -
//-----------------------------------------------------------------------------
void CEnvHeadcrabCanister::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) { // Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) return;
BaseClass::SetTransmit( pInfo, bAlways ); // Make our smoke trail always come with us
if ( m_hSmokeTrail ) { m_hSmokeTrail->SetTransmit( pInfo, bAlways ); } }
|