diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/server/hl2/env_headcrabcanister.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/server/hl2/env_headcrabcanister.cpp')
| -rw-r--r-- | sp/src/game/server/hl2/env_headcrabcanister.cpp | 1114 |
1 files changed, 1114 insertions, 0 deletions
diff --git a/sp/src/game/server/hl2/env_headcrabcanister.cpp b/sp/src/game/server/hl2/env_headcrabcanister.cpp new file mode 100644 index 00000000..66afa98c --- /dev/null +++ b/sp/src/game/server/hl2/env_headcrabcanister.cpp @@ -0,0 +1,1114 @@ +//========= 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 );
+ }
+}
|