From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/server/env_entity_maker.cpp | 366 ++++++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 mp/src/game/server/env_entity_maker.cpp (limited to 'mp/src/game/server/env_entity_maker.cpp') diff --git a/mp/src/game/server/env_entity_maker.cpp b/mp/src/game/server/env_entity_maker.cpp new file mode 100644 index 00000000..ff0fbcfd --- /dev/null +++ b/mp/src/game/server/env_entity_maker.cpp @@ -0,0 +1,366 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "cbase.h" +#include "entityoutput.h" +#include "TemplateEntities.h" +#include "point_template.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define SF_ENTMAKER_AUTOSPAWN 0x0001 +#define SF_ENTMAKER_WAITFORDESTRUCTION 0x0002 +#define SF_ENTMAKER_IGNOREFACING 0x0004 +#define SF_ENTMAKER_CHECK_FOR_SPACE 0x0008 +#define SF_ENTMAKER_CHECK_PLAYER_LOOKING 0x0010 + + +//----------------------------------------------------------------------------- +// Purpose: An entity that mapmakers can use to ensure there's a required entity never runs out. +// i.e. physics cannisters that need to be used. +//----------------------------------------------------------------------------- +class CEnvEntityMaker : public CPointEntity +{ + DECLARE_CLASS( CEnvEntityMaker, CPointEntity ); +public: + DECLARE_DATADESC(); + + virtual void Spawn( void ); + virtual void Activate( void ); + + void SpawnEntity( Vector vecAlternateOrigin = vec3_invalid, QAngle vecAlternateAngles = vec3_angle ); + void CheckSpawnThink( void ); + void InputForceSpawn( inputdata_t &inputdata ); + void InputForceSpawnAtEntityOrigin( inputdata_t &inputdata ); + +private: + + CPointTemplate *FindTemplate(); + + bool HasRoomToSpawn(); + bool IsPlayerLooking(); + + Vector m_vecEntityMins; + Vector m_vecEntityMaxs; + EHANDLE m_hCurrentInstance; + EHANDLE m_hCurrentBlocker; // Last entity that blocked us spawning something + Vector m_vecBlockerOrigin; + + // Movement after spawn + QAngle m_angPostSpawnDirection; + float m_flPostSpawnDirectionVariance; + float m_flPostSpawnSpeed; + bool m_bPostSpawnUseAngles; + + string_t m_iszTemplate; + + COutputEvent m_pOutputOnSpawned; + COutputEvent m_pOutputOnFailedSpawn; +}; + +BEGIN_DATADESC( CEnvEntityMaker ) + // DEFINE_FIELD( m_vecEntityMins, FIELD_VECTOR ), + // DEFINE_FIELD( m_vecEntityMaxs, FIELD_VECTOR ), + DEFINE_FIELD( m_hCurrentInstance, FIELD_EHANDLE ), + DEFINE_FIELD( m_hCurrentBlocker, FIELD_EHANDLE ), + DEFINE_FIELD( m_vecBlockerOrigin, FIELD_VECTOR ), + DEFINE_KEYFIELD( m_iszTemplate, FIELD_STRING, "EntityTemplate" ), + DEFINE_KEYFIELD( m_angPostSpawnDirection, FIELD_VECTOR, "PostSpawnDirection" ), + DEFINE_KEYFIELD( m_flPostSpawnDirectionVariance, FIELD_FLOAT, "PostSpawnDirectionVariance" ), + DEFINE_KEYFIELD( m_flPostSpawnSpeed, FIELD_FLOAT, "PostSpawnSpeed" ), + DEFINE_KEYFIELD( m_bPostSpawnUseAngles, FIELD_BOOLEAN, "PostSpawnInheritAngles" ), + + // Outputs + DEFINE_OUTPUT( m_pOutputOnSpawned, "OnEntitySpawned" ), + DEFINE_OUTPUT( m_pOutputOnFailedSpawn, "OnEntityFailedSpawn" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawn", InputForceSpawn ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForceSpawnAtEntityOrigin", InputForceSpawnAtEntityOrigin ), + + // Functions + DEFINE_THINKFUNC( CheckSpawnThink ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( env_entity_maker, CEnvEntityMaker ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvEntityMaker::Spawn( void ) +{ + m_vecEntityMins = vec3_origin; + m_vecEntityMaxs = vec3_origin; + m_hCurrentInstance = NULL; + m_hCurrentBlocker = NULL; + m_vecBlockerOrigin = vec3_origin; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvEntityMaker::Activate( void ) +{ + BaseClass::Activate(); + + // check for valid template + if ( m_iszTemplate == NULL_STRING ) + { + Warning( "env_entity_maker %s has no template entity!\n", GetEntityName().ToCStr() ); + UTIL_Remove( this ); + return; + } + + // Spawn an instance + if ( m_spawnflags & SF_ENTMAKER_AUTOSPAWN ) + { + SpawnEntity(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPointTemplate *CEnvEntityMaker::FindTemplate() +{ + // Find our point_template + CPointTemplate *pTemplate = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszTemplate) )); + if ( !pTemplate ) + { + Warning( "env_entity_maker %s failed to find template %s.\n", GetEntityName().ToCStr(), STRING(m_iszTemplate) ); + } + + return pTemplate; +} + + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternateAngles ) +{ + CPointTemplate *pTemplate = FindTemplate(); + if (!pTemplate) + return; + + // Spawn our template + Vector vecSpawnOrigin = GetAbsOrigin(); + QAngle vecSpawnAngles = GetAbsAngles(); + + if( vecAlternateOrigin != vec3_invalid ) + { + // We have a valid alternate origin and angles. Use those instead + // of spawning the items at my own origin and angles. + vecSpawnOrigin = vecAlternateOrigin; + vecSpawnAngles = vecAlternateAngles; + } + + CUtlVector hNewEntities; + if ( !pTemplate->CreateInstance( vecSpawnOrigin, vecSpawnAngles, &hNewEntities ) ) + return; + + //Adrian: oops we couldn't spawn the entity (or entities) for some reason! + if ( hNewEntities.Count() == 0 ) + return; + + m_hCurrentInstance = hNewEntities[0]; + + // Assume it'll block us + m_hCurrentBlocker = m_hCurrentInstance; + m_vecBlockerOrigin = m_hCurrentBlocker->GetAbsOrigin(); + + // Store off the mins & maxs the first time we spawn + if ( m_vecEntityMins == vec3_origin ) + { + m_hCurrentInstance->CollisionProp()->WorldSpaceAABB( &m_vecEntityMins, &m_vecEntityMaxs ); + m_vecEntityMins -= m_hCurrentInstance->GetAbsOrigin(); + m_vecEntityMaxs -= m_hCurrentInstance->GetAbsOrigin(); + } + + // Fire our output + m_pOutputOnSpawned.FireOutput( this, this ); + + // Start thinking + if ( m_spawnflags & SF_ENTMAKER_AUTOSPAWN ) + { + SetThink( &CEnvEntityMaker::CheckSpawnThink ); + SetNextThink( gpGlobals->curtime + 0.5f ); + } + + // If we have a specified post spawn speed, apply it to all spawned entities + if ( m_flPostSpawnSpeed ) + { + for ( int i = 0; i < hNewEntities.Count(); i++ ) + { + CBaseEntity *pEntity = hNewEntities[i]; + if ( pEntity->GetMoveType() == MOVETYPE_NONE ) + continue; + + // Calculate a velocity for this entity + Vector vForward,vRight,vUp; + QAngle angSpawnDir( m_angPostSpawnDirection ); + if ( m_bPostSpawnUseAngles ) + { + if ( GetParent() ) + { + angSpawnDir += GetParent()->GetAbsAngles(); + } + else + { + angSpawnDir += GetAbsAngles(); + } + } + AngleVectors( angSpawnDir, &vForward, &vRight, &vUp ); + Vector vecShootDir = vForward; + vecShootDir += vRight * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance; + vecShootDir += vForward * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance; + vecShootDir += vUp * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance; + VectorNormalize( vecShootDir ); + vecShootDir *= m_flPostSpawnSpeed; + + // Apply it to the entity + IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); + if ( pPhysicsObject ) + { + pPhysicsObject->AddVelocity(&vecShootDir, NULL); + } + else + { + pEntity->SetAbsVelocity( vecShootDir ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not the template entities can fit if spawned. +// Input : pBlocker - Returns blocker unless NULL. +//----------------------------------------------------------------------------- +bool CEnvEntityMaker::HasRoomToSpawn() +{ + // Do we have a blocker from last time? + if ( m_hCurrentBlocker ) + { + // If it hasn't moved, abort immediately + if ( m_vecBlockerOrigin == m_hCurrentBlocker->GetAbsOrigin() ) + { + return false; + } + } + + // Check to see if there's enough room to spawn + trace_t tr; + UTIL_TraceHull( GetAbsOrigin(), GetAbsOrigin(), m_vecEntityMins, m_vecEntityMaxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + if ( tr.m_pEnt || tr.startsolid ) + { + // Store off our blocker to check later + m_hCurrentBlocker = tr.m_pEnt; + if ( m_hCurrentBlocker ) + { + m_vecBlockerOrigin = m_hCurrentBlocker->GetAbsOrigin(); + } + + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the player is looking towards us. +//----------------------------------------------------------------------------- +bool CEnvEntityMaker::IsPlayerLooking() +{ + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + { + // Only spawn if the player's looking away from me + Vector vLookDir = pPlayer->EyeDirection3D(); + Vector vTargetDir = GetAbsOrigin() - pPlayer->EyePosition(); + VectorNormalize( vTargetDir ); + + float fDotPr = DotProduct( vLookDir,vTargetDir ); + if ( fDotPr > 0 ) + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Check to see if we should spawn another instance +//----------------------------------------------------------------------------- +void CEnvEntityMaker::CheckSpawnThink( void ) +{ + SetNextThink( gpGlobals->curtime + 0.5f ); + + // Do we have an instance? + if ( m_hCurrentInstance ) + { + // If Wait-For-Destruction is set, abort immediately + if ( m_spawnflags & SF_ENTMAKER_WAITFORDESTRUCTION ) + return; + } + + // Check to see if there's enough room to spawn + if ( !HasRoomToSpawn() ) + return; + + // We're clear, now check to see if the player's looking + if ( !( HasSpawnFlags( SF_ENTMAKER_IGNOREFACING ) ) && IsPlayerLooking() ) + return; + + // Clear, no player watching, so spawn! + SpawnEntity(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Spawns the entities, checking for space if flagged to do so. +//----------------------------------------------------------------------------- +void CEnvEntityMaker::InputForceSpawn( inputdata_t &inputdata ) +{ + CPointTemplate *pTemplate = FindTemplate(); + if (!pTemplate) + return; + + if ( HasSpawnFlags( SF_ENTMAKER_CHECK_FOR_SPACE ) && !HasRoomToSpawn() ) + { + m_pOutputOnFailedSpawn.FireOutput( this, this ); + return; + } + + if ( HasSpawnFlags( SF_ENTMAKER_CHECK_PLAYER_LOOKING ) && IsPlayerLooking() ) + { + m_pOutputOnFailedSpawn.FireOutput( this, this ); + return; + } + + SpawnEntity(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CEnvEntityMaker::InputForceSpawnAtEntityOrigin( inputdata_t &inputdata ) +{ + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); + + if( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} -- cgit v1.2.3