Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7496 lines
222 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The base class from which all game entities are derived.
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "globalstate.h"
  8. #include "isaverestore.h"
  9. #include "client.h"
  10. #include "decals.h"
  11. #include "gamerules.h"
  12. #include "entityapi.h"
  13. #include "entitylist.h"
  14. #include "eventqueue.h"
  15. #include "hierarchy.h"
  16. #include "basecombatweapon.h"
  17. #include "const.h"
  18. #include "player.h" // For debug draw sending
  19. #include "ndebugoverlay.h"
  20. #include "physics.h"
  21. #include "model_types.h"
  22. #include "team.h"
  23. #include "sendproxy.h"
  24. #include "IEffects.h"
  25. #include "vstdlib/random.h"
  26. #include "baseentity.h"
  27. #include "collisionutils.h"
  28. #include "coordsize.h"
  29. #include "animation.h"
  30. #include "tier1/strtools.h"
  31. #include "engine/IEngineSound.h"
  32. #include "physics_saverestore.h"
  33. #include "saverestore_utlvector.h"
  34. #include "bone_setup.h"
  35. #include "vcollide_parse.h"
  36. #include "filters.h"
  37. #include "te_effect_dispatch.h"
  38. #include "AI_Criteria.h"
  39. #include "AI_ResponseSystem.h"
  40. #include "world.h"
  41. #include "globals.h"
  42. #include "saverestoretypes.h"
  43. #include "SkyCamera.h"
  44. #include "sceneentity.h"
  45. #include "game.h"
  46. #include "tier0/vprof.h"
  47. #include "ai_basenpc.h"
  48. #include "iservervehicle.h"
  49. #include "eventlist.h"
  50. #include "scriptevent.h"
  51. #include "SoundEmitterSystem/isoundemittersystembase.h"
  52. #include "UtlCachedFileData.h"
  53. #include "utlbuffer.h"
  54. #include "positionwatcher.h"
  55. #include "movetype_push.h"
  56. #include "tier0/icommandline.h"
  57. #include "vphysics/friction.h"
  58. #include <ctype.h>
  59. #include "datacache/imdlcache.h"
  60. #include "ModelSoundsCache.h"
  61. #include "env_debughistory.h"
  62. #include "tier1/utlstring.h"
  63. #include "utlhashtable.h"
  64. #if defined( TF_DLL )
  65. #include "tf_gamerules.h"
  66. #endif
  67. // memdbgon must be the last include file in a .cpp file!!!
  68. #include "tier0/memdbgon.h"
  69. extern bool g_bTestMoveTypeStepSimulation;
  70. extern ConVar sv_vehicle_autoaim_scale;
  71. // Init static class variables
  72. bool CBaseEntity::m_bInDebugSelect = false; // Used for selection in debug overlays
  73. int CBaseEntity::m_nDebugPlayer = -1; // Player doing the selection
  74. // This can be set before creating an entity to force it to use a particular edict.
  75. edict_t *g_pForceAttachEdict = NULL;
  76. bool CBaseEntity::m_bDebugPause = false; // Whether entity i/o is paused.
  77. int CBaseEntity::m_nDebugSteps = 1; // Number of entity outputs to fire before pausing again.
  78. bool CBaseEntity::sm_bDisableTouchFuncs = false; // Disables PhysicsTouch and PhysicsStartTouch function calls
  79. bool CBaseEntity::sm_bAccurateTriggerBboxChecks = true; // set to false for legacy behavior in ep1
  80. int CBaseEntity::m_nPredictionRandomSeed = -1;
  81. int CBaseEntity::m_nPredictionRandomSeedServer = -1;
  82. CBasePlayer *CBaseEntity::m_pPredictionPlayer = NULL;
  83. // Used to make sure nobody calls UpdateTransmitState directly.
  84. int g_nInsideDispatchUpdateTransmitState = 0;
  85. // When this is false, throw an assert in debug when GetAbsAnything is called. Used when hierachy is incomplete/invalid.
  86. bool CBaseEntity::s_bAbsQueriesValid = true;
  87. ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Test networking visibility distance" );
  88. // This table encodes edict data.
  89. void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
  90. {
  91. CBaseEntity *pEntity = (CBaseEntity *)pStruct;
  92. #if defined( _DEBUG )
  93. CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
  94. Assert( pAnimating );
  95. if ( pAnimating )
  96. {
  97. Assert( !pAnimating->IsUsingClientSideAnimation() );
  98. }
  99. #endif
  100. int ticknumber = TIME_TO_TICKS( pEntity->m_flAnimTime );
  101. // Tickbase is current tick rounded down to closes 100 ticks
  102. int tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
  103. int addt = 0;
  104. // If it's within the last tick interval through the current one, then we can encode it
  105. if ( ticknumber >= ( tickbase - 100 ) )
  106. {
  107. addt = ( ticknumber - tickbase ) & 0xFF;
  108. }
  109. pOut->m_Int = addt;
  110. }
  111. // This table encodes edict data.
  112. void SendProxy_SimulationTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
  113. {
  114. CBaseEntity *pEntity = (CBaseEntity *)pStruct;
  115. int ticknumber = TIME_TO_TICKS( pEntity->m_flSimulationTime );
  116. // tickbase is current tick rounded down to closest 100 ticks
  117. int tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
  118. int addt = 0;
  119. if ( ticknumber >= tickbase )
  120. {
  121. addt = ( ticknumber - tickbase ) & 0xff;
  122. }
  123. pOut->m_Int = addt;
  124. }
  125. void* SendProxy_ClientSideAnimation( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  126. {
  127. CBaseEntity *pEntity = (CBaseEntity *)pStruct;
  128. CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
  129. if ( pAnimating && !pAnimating->IsUsingClientSideAnimation() )
  130. return (void*)pVarData;
  131. else
  132. return NULL; // Don't send animtime unless the client needs it.
  133. }
  134. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_ClientSideAnimation );
  135. BEGIN_SEND_TABLE_NOBASE( CBaseEntity, DT_AnimTimeMustBeFirst )
  136. // NOTE: Animtime must be sent before origin and angles ( from pev ) because it has a
  137. // proxy on the client that stores off the old values before writing in the new values and
  138. // if it is sent after the new values, then it will only have the new origin and studio model, etc.
  139. // interpolation will be busted
  140. SendPropInt (SENDINFO(m_flAnimTime), 8, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN|SPROP_ENCODED_AGAINST_TICKCOUNT, SendProxy_AnimTime),
  141. END_SEND_TABLE()
  142. #if !defined( NO_ENTITY_PREDICTION )
  143. BEGIN_SEND_TABLE_NOBASE( CBaseEntity, DT_PredictableId )
  144. SendPropPredictableId( SENDINFO( m_PredictableID ) ),
  145. SendPropInt( SENDINFO( m_bIsPlayerSimulated ), 1, SPROP_UNSIGNED ),
  146. END_SEND_TABLE()
  147. static void* SendProxy_SendPredictableId( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  148. {
  149. CBaseEntity *pEntity = (CBaseEntity *)pStruct;
  150. if ( !pEntity || !pEntity->m_PredictableID->IsActive() )
  151. return NULL;
  152. int id_player_index = pEntity->m_PredictableID->GetPlayer();
  153. pRecipients->SetOnly( id_player_index );
  154. return ( void * )pVarData;
  155. }
  156. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendPredictableId );
  157. #endif
  158. void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  159. {
  160. CBaseEntity *entity = (CBaseEntity*)pStruct;
  161. Assert( entity );
  162. const Vector *v;
  163. if ( !entity->UseStepSimulationNetworkOrigin( &v ) )
  164. {
  165. v = &entity->GetLocalOrigin();
  166. }
  167. pOut->m_Vector[ 0 ] = v->x;
  168. pOut->m_Vector[ 1 ] = v->y;
  169. pOut->m_Vector[ 2 ] = v->z;
  170. }
  171. //--------------------------------------------------------------------------------------------------------
  172. // Used when breaking up origin, note we still have to deal with StepSimulation
  173. //--------------------------------------------------------------------------------------------------------
  174. void SendProxy_OriginXY( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  175. {
  176. CBaseEntity *entity = (CBaseEntity*)pStruct;
  177. Assert( entity );
  178. const Vector *v;
  179. if ( !entity->UseStepSimulationNetworkOrigin( &v ) )
  180. {
  181. v = &entity->GetLocalOrigin();
  182. }
  183. pOut->m_Vector[ 0 ] = v->x;
  184. pOut->m_Vector[ 1 ] = v->y;
  185. }
  186. //--------------------------------------------------------------------------------------------------------
  187. // Used when breaking up origin, note we still have to deal with StepSimulation
  188. //--------------------------------------------------------------------------------------------------------
  189. void SendProxy_OriginZ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  190. {
  191. CBaseEntity *entity = (CBaseEntity*)pStruct;
  192. Assert( entity );
  193. const Vector *v;
  194. if ( !entity->UseStepSimulationNetworkOrigin( &v ) )
  195. {
  196. v = &entity->GetLocalOrigin();
  197. }
  198. pOut->m_Float = v->z;
  199. }
  200. void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  201. {
  202. CBaseEntity *entity = (CBaseEntity*)pStruct;
  203. Assert( entity );
  204. const QAngle *a;
  205. if ( !entity->UseStepSimulationNetworkAngles( &a ) )
  206. {
  207. a = &entity->GetLocalAngles();
  208. }
  209. pOut->m_Vector[ 0 ] = anglemod( a->x );
  210. pOut->m_Vector[ 1 ] = anglemod( a->y );
  211. pOut->m_Vector[ 2 ] = anglemod( a->z );
  212. }
  213. // This table encodes the CBaseEntity data.
  214. IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity )
  215. SendPropDataTable( "AnimTimeMustBeFirst", 0, &REFERENCE_SEND_TABLE(DT_AnimTimeMustBeFirst), SendProxy_ClientSideAnimation ),
  216. SendPropInt (SENDINFO(m_flSimulationTime), SIMULATION_TIME_WINDOW_BITS, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN|SPROP_ENCODED_AGAINST_TICKCOUNT, SendProxy_SimulationTime),
  217. #if PREDICTION_ERROR_CHECK_LEVEL > 1
  218. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  219. #else
  220. SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
  221. #endif
  222. SendPropInt (SENDINFO( m_ubInterpolationFrame ), NOINTERP_PARITY_MAX_BITS, SPROP_UNSIGNED ),
  223. SendPropModelIndex(SENDINFO(m_nModelIndex)),
  224. SendPropDataTable( SENDINFO_DT( m_Collision ), &REFERENCE_SEND_TABLE(DT_CollisionProperty) ),
  225. SendPropInt (SENDINFO(m_nRenderFX), 8, SPROP_UNSIGNED ),
  226. SendPropInt (SENDINFO(m_nRenderMode), 8, SPROP_UNSIGNED ),
  227. SendPropInt (SENDINFO(m_fEffects), EF_MAX_BITS, SPROP_UNSIGNED),
  228. SendPropInt (SENDINFO(m_clrRender), 32, SPROP_UNSIGNED),
  229. SendPropInt (SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0),
  230. SendPropInt (SENDINFO(m_CollisionGroup), 5, SPROP_UNSIGNED),
  231. SendPropFloat (SENDINFO(m_flElasticity), 0, SPROP_COORD),
  232. SendPropFloat (SENDINFO(m_flShadowCastDistance), 12, SPROP_UNSIGNED ),
  233. SendPropEHandle (SENDINFO(m_hOwnerEntity)),
  234. SendPropEHandle (SENDINFO(m_hEffectEntity)),
  235. SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)),
  236. SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED),
  237. SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ),
  238. SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ),
  239. #if PREDICTION_ERROR_CHECK_LEVEL > 1
  240. SendPropVector (SENDINFO(m_angRotation), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0, HIGH_DEFAULT, SendProxy_Angles ),
  241. #else
  242. SendPropQAngles (SENDINFO(m_angRotation), 13, SPROP_CHANGES_OFTEN, SendProxy_Angles ),
  243. #endif
  244. SendPropInt ( SENDINFO( m_iTextureFrameIndex ), 8, SPROP_UNSIGNED ),
  245. #if !defined( NO_ENTITY_PREDICTION )
  246. SendPropDataTable( "predictable_id", 0, &REFERENCE_SEND_TABLE( DT_PredictableId ), SendProxy_SendPredictableId ),
  247. #endif
  248. // FIXME: Collapse into another flag field?
  249. SendPropInt (SENDINFO(m_bSimulatedEveryTick), 1, SPROP_UNSIGNED ),
  250. SendPropInt (SENDINFO(m_bAnimatedEveryTick), 1, SPROP_UNSIGNED ),
  251. SendPropBool( SENDINFO( m_bAlternateSorting )),
  252. #ifdef TF_DLL
  253. SendPropArray3( SENDINFO_ARRAY3(m_nModelIndexOverrides), SendPropInt( SENDINFO_ARRAY(m_nModelIndexOverrides), SP_MODEL_INDEX_BITS, 0 ) ),
  254. #endif
  255. END_SEND_TABLE()
  256. // dynamic models
  257. class CBaseEntityModelLoadProxy
  258. {
  259. protected:
  260. class Handler : public IModelLoadCallback
  261. {
  262. public:
  263. explicit Handler( CBaseEntity *pEntity ) : m_pEntity(pEntity) { }
  264. virtual void OnModelLoadComplete( const model_t *pModel );
  265. CBaseEntity* m_pEntity;
  266. };
  267. Handler* m_pHandler;
  268. public:
  269. explicit CBaseEntityModelLoadProxy( CBaseEntity *pEntity ) : m_pHandler( new Handler( pEntity ) ) { }
  270. ~CBaseEntityModelLoadProxy() { delete m_pHandler; }
  271. void Register( int nModelIndex ) const { modelinfo->RegisterModelLoadCallback( nModelIndex, m_pHandler ); }
  272. operator CBaseEntity * () const { return m_pHandler->m_pEntity; }
  273. private:
  274. CBaseEntityModelLoadProxy( const CBaseEntityModelLoadProxy& );
  275. CBaseEntityModelLoadProxy& operator=( const CBaseEntityModelLoadProxy& );
  276. };
  277. static CUtlHashtable< CBaseEntityModelLoadProxy, empty_t, PointerHashFunctor, PointerEqualFunctor, CBaseEntity * > sg_DynamicLoadHandlers;
  278. void CBaseEntityModelLoadProxy::Handler::OnModelLoadComplete( const model_t *pModel )
  279. {
  280. m_pEntity->OnModelLoadComplete( pModel );
  281. sg_DynamicLoadHandlers.Remove( m_pEntity ); // NOTE: destroys *this!
  282. }
  283. CBaseEntity::CBaseEntity( bool bServerOnly )
  284. {
  285. m_pAttributes = NULL;
  286. COMPILE_TIME_ASSERT( MOVETYPE_LAST < (1 << MOVETYPE_MAX_BITS) );
  287. COMPILE_TIME_ASSERT( MOVECOLLIDE_COUNT < (1 << MOVECOLLIDE_MAX_BITS) );
  288. #ifdef _DEBUG
  289. // necessary since in debug, we initialize vectors to NAN for debugging
  290. m_vecAngVelocity.Init();
  291. // m_vecAbsAngVelocity.Init();
  292. m_vecViewOffset.Init();
  293. m_vecBaseVelocity.GetForModify().Init();
  294. m_vecVelocity.Init();
  295. m_vecAbsVelocity.Init();
  296. #endif
  297. m_bAlternateSorting = false;
  298. m_CollisionGroup = COLLISION_GROUP_NONE;
  299. m_iParentAttachment = 0;
  300. CollisionProp()->Init( this );
  301. NetworkProp()->Init( this );
  302. // NOTE: THIS MUST APPEAR BEFORE ANY SetMoveType() or SetNextThink() calls
  303. AddEFlags( EFL_NO_THINK_FUNCTION | EFL_NO_GAME_PHYSICS_SIMULATION | EFL_USE_PARTITION_WHEN_NOT_SOLID );
  304. // clear debug overlays
  305. m_debugOverlays = 0;
  306. m_pTimedOverlay = NULL;
  307. m_pPhysicsObject = NULL;
  308. m_flElasticity = 1.0f;
  309. m_flShadowCastDistance = m_flDesiredShadowCastDistance = 0;
  310. SetRenderColor( 255, 255, 255, 255 );
  311. m_iTeamNum = m_iInitialTeamNum = TEAM_UNASSIGNED;
  312. m_nLastThinkTick = gpGlobals->tickcount;
  313. m_nSimulationTick = -1;
  314. SetIdentityMatrix( m_rgflCoordinateFrame );
  315. m_pBlocker = NULL;
  316. #if _DEBUG
  317. m_iCurrentThinkContext = NO_THINK_CONTEXT;
  318. #endif
  319. m_nWaterTouch = m_nSlimeTouch = 0;
  320. SetSolid( SOLID_NONE );
  321. ClearSolidFlags();
  322. m_nModelIndex = 0;
  323. m_bDynamicModelAllowed = false;
  324. m_bDynamicModelPending = false;
  325. m_bDynamicModelSetBounds = false;
  326. SetMoveType( MOVETYPE_NONE );
  327. SetOwnerEntity( NULL );
  328. SetCheckUntouch( false );
  329. SetModelIndex( 0 );
  330. SetModelName( NULL_STRING );
  331. m_nTransmitStateOwnedCounter = 0;
  332. SetCollisionBounds( vec3_origin, vec3_origin );
  333. ClearFlags();
  334. SetFriction( 1.0f );
  335. if ( bServerOnly )
  336. {
  337. AddEFlags( EFL_SERVER_ONLY );
  338. }
  339. NetworkProp()->MarkPVSInformationDirty();
  340. #ifndef _XBOX
  341. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  342. #endif
  343. m_bTruceValidForEnt = false;
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Purpose: Scale up our physics hull and test against the new one
  347. // Input : *pNewCollide - New collision hull
  348. //-----------------------------------------------------------------------------
  349. void CBaseEntity::SetScaledPhysics( IPhysicsObject *pNewObject )
  350. {
  351. if ( pNewObject )
  352. {
  353. AddSolidFlags( FSOLID_CUSTOMBOXTEST | FSOLID_CUSTOMRAYTEST );
  354. }
  355. else
  356. {
  357. RemoveSolidFlags( FSOLID_CUSTOMBOXTEST | FSOLID_CUSTOMRAYTEST );
  358. }
  359. }
  360. extern bool g_bDisableEhandleAccess;
  361. //-----------------------------------------------------------------------------
  362. // Purpose: See note below
  363. //-----------------------------------------------------------------------------
  364. CBaseEntity::~CBaseEntity( )
  365. {
  366. // FIXME: This can't be called from UpdateOnRemove! There's at least one
  367. // case where friction sounds are added between the call to UpdateOnRemove + ~CBaseEntity
  368. PhysCleanupFrictionSounds( this );
  369. Assert( !IsDynamicModelIndex( m_nModelIndex ) );
  370. Verify( !sg_DynamicLoadHandlers.Remove( this ) );
  371. // In debug make sure that we don't call delete on an entity without setting
  372. // the disable flag first!
  373. // EHANDLE accessors will check, in debug, for access to entities during destruction of
  374. // another entity.
  375. // That kind of operation should only occur in UpdateOnRemove calls
  376. // Deletion should only occur via UTIL_Remove(Immediate) calls, not via naked delete calls
  377. Assert( g_bDisableEhandleAccess );
  378. VPhysicsDestroyObject();
  379. // Need to remove references to this entity before EHANDLES go null
  380. {
  381. g_bDisableEhandleAccess = false;
  382. CBaseEntity::PhysicsRemoveTouchedList( this );
  383. CBaseEntity::PhysicsRemoveGroundList( this );
  384. SetGroundEntity( NULL ); // remove us from the ground entity if we are on it
  385. DestroyAllDataObjects();
  386. g_bDisableEhandleAccess = true;
  387. // Remove this entity from the ent list (NOTE: This Makes EHANDLES go NULL)
  388. gEntList.RemoveEntity( GetRefEHandle() );
  389. }
  390. }
  391. void CBaseEntity::PostConstructor( const char *szClassname )
  392. {
  393. if ( szClassname )
  394. {
  395. SetClassname(szClassname);
  396. }
  397. Assert( m_iClassname != NULL_STRING && STRING(m_iClassname) != NULL );
  398. // Possibly get an edict, and add self to global list of entites.
  399. if ( IsEFlagSet( EFL_SERVER_ONLY ) )
  400. {
  401. gEntList.AddNonNetworkableEntity( this );
  402. }
  403. else
  404. {
  405. // Certain entities set up their edicts in the constructor
  406. if ( !IsEFlagSet( EFL_NO_AUTO_EDICT_ATTACH ) )
  407. {
  408. NetworkProp()->AttachEdict( g_pForceAttachEdict );
  409. g_pForceAttachEdict = NULL;
  410. }
  411. // Some ents like the player override the AttachEdict function and do it at a different time.
  412. // While precaching, they don't ever have an edict, so we don't need to add them to
  413. // the entity list in that case.
  414. if ( edict() )
  415. {
  416. gEntList.AddNetworkableEntity( this, entindex() );
  417. // Cache our IServerNetworkable pointer for the engine for fast access.
  418. if ( edict() )
  419. edict()->m_pNetworkable = NetworkProp();
  420. }
  421. }
  422. CheckHasThinkFunction( false );
  423. CheckHasGamePhysicsSimulation();
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose: Called after player becomes active in the game
  427. //-----------------------------------------------------------------------------
  428. void CBaseEntity::PostClientActive( void )
  429. {
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose: Verifies that this entity's data description is valid in debug builds.
  433. //-----------------------------------------------------------------------------
  434. #ifdef _DEBUG
  435. typedef CUtlVector< const char * > KeyValueNameList_t;
  436. static void AddDataMapFieldNamesToList( KeyValueNameList_t &list, datamap_t *pDataMap )
  437. {
  438. while (pDataMap != NULL)
  439. {
  440. for (int i = 0; i < pDataMap->dataNumFields; i++)
  441. {
  442. typedescription_t *pField = &pDataMap->dataDesc[i];
  443. if (pField->fieldType == FIELD_EMBEDDED)
  444. {
  445. AddDataMapFieldNamesToList( list, pField->td );
  446. continue;
  447. }
  448. if (pField->flags & FTYPEDESC_KEY)
  449. {
  450. list.AddToTail( pField->externalName );
  451. }
  452. }
  453. pDataMap = pDataMap->baseMap;
  454. }
  455. }
  456. void CBaseEntity::ValidateDataDescription(void)
  457. {
  458. // Multiple key fields that have the same name are not allowed - it creates an
  459. // ambiguity when trying to parse keyvalues and outputs.
  460. datamap_t *pDataMap = GetDataDescMap();
  461. if ((pDataMap == NULL) || pDataMap->bValidityChecked)
  462. return;
  463. pDataMap->bValidityChecked = true;
  464. // Let's generate a list of all keyvalue strings in the entire hierarchy...
  465. KeyValueNameList_t names(128);
  466. AddDataMapFieldNamesToList( names, pDataMap );
  467. for (int i = names.Count(); --i > 0; )
  468. {
  469. for (int j = i - 1; --j >= 0; )
  470. {
  471. if (!Q_stricmp(names[i], names[j]))
  472. {
  473. DevMsg( "%s has multiple data description entries for \"%s\"\n", STRING(m_iClassname), names[i]);
  474. break;
  475. }
  476. }
  477. }
  478. }
  479. #endif // _DEBUG
  480. //-----------------------------------------------------------------------------
  481. // Sets the collision bounds + the size
  482. //-----------------------------------------------------------------------------
  483. void CBaseEntity::SetCollisionBounds( const Vector& mins, const Vector &maxs )
  484. {
  485. m_Collision.SetCollisionBounds( mins, maxs );
  486. }
  487. void CBaseEntity::StopFollowingEntity( )
  488. {
  489. if( !IsFollowingEntity() )
  490. {
  491. // Assert( IsEffectActive( EF_BONEMERGE ) == 0 );
  492. return;
  493. }
  494. SetParent( NULL );
  495. RemoveEffects( EF_BONEMERGE );
  496. RemoveSolidFlags( FSOLID_NOT_SOLID );
  497. SetMoveType( MOVETYPE_NONE );
  498. CollisionRulesChanged();
  499. }
  500. bool CBaseEntity::IsFollowingEntity()
  501. {
  502. return IsEffectActive( EF_BONEMERGE ) && (GetMoveType() == MOVETYPE_NONE) && GetMoveParent();
  503. }
  504. CBaseEntity *CBaseEntity::GetFollowedEntity()
  505. {
  506. if (!IsFollowingEntity())
  507. return NULL;
  508. return GetMoveParent();
  509. }
  510. void CBaseEntity::SetClassname( const char *className )
  511. {
  512. m_iClassname = AllocPooledString( className );
  513. }
  514. void CBaseEntity::SetModelIndex( int index )
  515. {
  516. if ( IsDynamicModelIndex( index ) && !(GetBaseAnimating() && m_bDynamicModelAllowed) )
  517. {
  518. AssertMsg( false, "dynamic model support not enabled on server entity" );
  519. index = -1;
  520. }
  521. if ( index != m_nModelIndex )
  522. {
  523. if ( m_bDynamicModelPending )
  524. {
  525. sg_DynamicLoadHandlers.Remove( this );
  526. }
  527. modelinfo->ReleaseDynamicModel( m_nModelIndex );
  528. modelinfo->AddRefDynamicModel( index );
  529. m_nModelIndex = index;
  530. m_bDynamicModelSetBounds = false;
  531. if ( IsDynamicModelIndex( index ) )
  532. {
  533. m_bDynamicModelPending = true;
  534. sg_DynamicLoadHandlers[ sg_DynamicLoadHandlers.Insert( this ) ].Register( index );
  535. }
  536. else
  537. {
  538. m_bDynamicModelPending = false;
  539. OnNewModel();
  540. }
  541. }
  542. DispatchUpdateTransmitState();
  543. }
  544. void CBaseEntity::ClearModelIndexOverrides( void )
  545. {
  546. #ifdef TF_DLL
  547. for ( int index = 0 ; index < MAX_VISION_MODES ; index++ )
  548. {
  549. m_nModelIndexOverrides.Set( index, 0 );
  550. }
  551. #endif
  552. }
  553. void CBaseEntity::SetModelIndexOverride( int index, int nValue )
  554. {
  555. #ifdef TF_DLL
  556. if ( ( index >= VISION_MODE_NONE ) && ( index < MAX_VISION_MODES ) )
  557. {
  558. if ( nValue != m_nModelIndexOverrides[index] )
  559. {
  560. m_nModelIndexOverrides.Set( index, nValue );
  561. }
  562. }
  563. #endif
  564. }
  565. // position to shoot at
  566. Vector CBaseEntity::BodyTarget( const Vector &posSrc, bool bNoisy)
  567. {
  568. return WorldSpaceCenter( );
  569. }
  570. // return the position of my head. someone's trying to attack it.
  571. Vector CBaseEntity::HeadTarget( const Vector &posSrc )
  572. {
  573. return EyePosition();
  574. }
  575. struct TimedOverlay_t
  576. {
  577. char *msg;
  578. int msgEndTime;
  579. int msgStartTime;
  580. TimedOverlay_t *pNextTimedOverlay;
  581. };
  582. //-----------------------------------------------------------------------------
  583. // Purpose: Display an error message on the entity
  584. // Input :
  585. // Output :
  586. //-----------------------------------------------------------------------------
  587. void CBaseEntity::AddTimedOverlay( const char *msg, int endTime )
  588. {
  589. TimedOverlay_t *pNewTO = new TimedOverlay_t;
  590. int len = strlen(msg);
  591. pNewTO->msg = new char[len + 1];
  592. Q_strncpy(pNewTO->msg,msg, len+1);
  593. pNewTO->msgEndTime = gpGlobals->curtime + endTime;
  594. pNewTO->msgStartTime = gpGlobals->curtime;
  595. pNewTO->pNextTimedOverlay = m_pTimedOverlay;
  596. m_pTimedOverlay = pNewTO;
  597. }
  598. //-----------------------------------------------------------------------------
  599. // Purpose: Send debug overlay box to the client
  600. // Input :
  601. // Output :
  602. //-----------------------------------------------------------------------------
  603. void CBaseEntity::DrawBBoxOverlay( float flDuration )
  604. {
  605. if (edict())
  606. {
  607. NDebugOverlay::EntityBounds(this, 255, 100, 0, 0, flDuration );
  608. if ( CollisionProp()->IsSolidFlagSet( FSOLID_USE_TRIGGER_BOUNDS ) )
  609. {
  610. Vector vecTriggerMins, vecTriggerMaxs;
  611. CollisionProp()->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  612. Vector center = 0.5f * (vecTriggerMins + vecTriggerMaxs);
  613. Vector extents = vecTriggerMaxs - center;
  614. NDebugOverlay::Box(center, -extents, extents, 0, 255, 255, 0, flDuration );
  615. }
  616. }
  617. }
  618. void CBaseEntity::DrawAbsBoxOverlay()
  619. {
  620. int red = 0;
  621. int green = 200;
  622. if ( VPhysicsGetObject() && VPhysicsGetObject()->IsAsleep() )
  623. {
  624. red = 90;
  625. green = 120;
  626. }
  627. if (edict())
  628. {
  629. // Surrounding boxes are axially aligned, so ignore angles
  630. Vector vecSurroundMins, vecSurroundMaxs;
  631. CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  632. Vector center = 0.5f * (vecSurroundMins + vecSurroundMaxs);
  633. Vector extents = vecSurroundMaxs - center;
  634. NDebugOverlay::Box(center, -extents, extents, red, green, 0, 0 ,0);
  635. }
  636. }
  637. void CBaseEntity::DrawRBoxOverlay()
  638. {
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose: Draws an axis overlay at the origin and angles of the entity
  642. //-----------------------------------------------------------------------------
  643. void CBaseEntity::SendDebugPivotOverlay( void )
  644. {
  645. if ( edict() )
  646. {
  647. NDebugOverlay::Axis( GetAbsOrigin(), GetAbsAngles(), 20, true, 0 );
  648. }
  649. }
  650. //------------------------------------------------------------------------------
  651. // Purpose : Add new entity positioned overlay text
  652. // Input : How many lines to offset text from origin
  653. // The text to print
  654. // How long to display text
  655. // The color of the text
  656. // Output :
  657. //------------------------------------------------------------------------------
  658. void CBaseEntity::EntityText( int text_offset, const char *text, float duration, int r, int g, int b, int a )
  659. {
  660. Vector origin;
  661. Vector vecLocalCenter;
  662. VectorAdd( m_Collision.OBBMins(), m_Collision.OBBMaxs(), vecLocalCenter );
  663. vecLocalCenter *= 0.5f;
  664. if ( ( m_Collision.GetCollisionAngles() == vec3_angle ) || ( vecLocalCenter == vec3_origin ) )
  665. {
  666. VectorAdd( vecLocalCenter, m_Collision.GetCollisionOrigin(), origin );
  667. }
  668. else
  669. {
  670. VectorTransform( vecLocalCenter, m_Collision.CollisionToWorldTransform(), origin );
  671. }
  672. NDebugOverlay::EntityTextAtPosition( origin, text_offset, text, duration, r, g, b, a );
  673. }
  674. //------------------------------------------------------------------------------
  675. // Purpose :
  676. // Input :
  677. // Output :
  678. //------------------------------------------------------------------------------
  679. void CBaseEntity::DrawTimedOverlays(void)
  680. {
  681. // Draw name first if I have an overlay or am in message mode
  682. if ((m_debugOverlays & OVERLAY_MESSAGE_BIT))
  683. {
  684. char tempstr[512];
  685. Q_snprintf( tempstr, sizeof( tempstr ), "[%s]", GetDebugName() );
  686. EntityText(0,tempstr, 0);
  687. }
  688. // Now draw overlays
  689. TimedOverlay_t* pTO = m_pTimedOverlay;
  690. TimedOverlay_t* pNextTO = NULL;
  691. TimedOverlay_t* pLastTO = NULL;
  692. int nCount = 1; // Offset by one
  693. while (pTO)
  694. {
  695. pNextTO = pTO->pNextTimedOverlay;
  696. // Remove old messages unless messages are paused
  697. if ((!CBaseEntity::Debug_IsPaused() && gpGlobals->curtime > pTO->msgEndTime) ||
  698. (nCount > 10))
  699. {
  700. if (pLastTO)
  701. {
  702. pLastTO->pNextTimedOverlay = pNextTO;
  703. }
  704. else
  705. {
  706. m_pTimedOverlay = pNextTO;
  707. }
  708. delete pTO->msg;
  709. delete pTO;
  710. }
  711. else
  712. {
  713. int nAlpha = 0;
  714. // If messages aren't paused fade out
  715. if (!CBaseEntity::Debug_IsPaused())
  716. {
  717. nAlpha = 255*((gpGlobals->curtime - pTO->msgStartTime)/(pTO->msgEndTime - pTO->msgStartTime));
  718. }
  719. int r = 185;
  720. int g = 145;
  721. int b = 145;
  722. // Brighter when new message
  723. if (nAlpha < 50)
  724. {
  725. r = 255;
  726. g = 205;
  727. b = 205;
  728. }
  729. if (nAlpha < 0) nAlpha = 0;
  730. EntityText(nCount,pTO->msg, 0.0, r, g, b, 255-nAlpha);
  731. nCount++;
  732. pLastTO = pTO;
  733. }
  734. pTO = pNextTO;
  735. }
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Purpose: Draw all overlays (should be implemented by subclass to add
  739. // any additional non-text overlays)
  740. // Input :
  741. // Output : Current text offset from the top
  742. //-----------------------------------------------------------------------------
  743. void CBaseEntity::DrawDebugGeometryOverlays(void)
  744. {
  745. DrawTimedOverlays();
  746. DrawDebugTextOverlays();
  747. if (m_debugOverlays & OVERLAY_NAME_BIT)
  748. {
  749. EntityText(0,GetDebugName(), 0);
  750. }
  751. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  752. {
  753. DrawBBoxOverlay();
  754. }
  755. if (m_debugOverlays & OVERLAY_ABSBOX_BIT )
  756. {
  757. DrawAbsBoxOverlay();
  758. }
  759. if (m_debugOverlays & OVERLAY_PIVOT_BIT)
  760. {
  761. SendDebugPivotOverlay();
  762. }
  763. if( m_debugOverlays & OVERLAY_RBOX_BIT )
  764. {
  765. DrawRBoxOverlay();
  766. }
  767. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT) )
  768. {
  769. // draw mass center
  770. if ( VPhysicsGetObject() )
  771. {
  772. Vector massCenter = VPhysicsGetObject()->GetMassCenterLocalSpace();
  773. Vector worldPos;
  774. VPhysicsGetObject()->LocalToWorld( &worldPos, massCenter );
  775. NDebugOverlay::Cross3D( worldPos, 12, 255, 0, 0, false, 0 );
  776. DebugDrawContactPoints(VPhysicsGetObject());
  777. if ( GetMoveType() != MOVETYPE_VPHYSICS )
  778. {
  779. Vector pos;
  780. QAngle angles;
  781. VPhysicsGetObject()->GetPosition( &pos, &angles );
  782. float dist = (pos - GetAbsOrigin()).Length();
  783. Vector axis;
  784. float deltaAngle;
  785. RotationDeltaAxisAngle( angles, GetAbsAngles(), axis, deltaAngle );
  786. if ( dist > 2 || fabsf(deltaAngle) > 2 )
  787. {
  788. Vector mins, maxs;
  789. physcollision->CollideGetAABB( &mins, &maxs, VPhysicsGetObject()->GetCollide(), vec3_origin, vec3_angle );
  790. NDebugOverlay::BoxAngles( pos, mins, maxs, angles, 255, 255, 0, 16, 0 );
  791. }
  792. }
  793. }
  794. }
  795. if ( m_debugOverlays & OVERLAY_SHOW_BLOCKSLOS )
  796. {
  797. if ( BlocksLOS() )
  798. {
  799. NDebugOverlay::EntityBounds(this, 255, 255, 255, 0, 0 );
  800. }
  801. }
  802. if ( m_debugOverlays & OVERLAY_AUTOAIM_BIT && (GetFlags()&FL_AIMTARGET) && AI_GetSinglePlayer() != NULL )
  803. {
  804. // Crude, but it gets the point across.
  805. Vector vecCenter = GetAutoAimCenter();
  806. Vector vecRight, vecUp, vecDiag;
  807. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  808. float radius = GetAutoAimRadius();
  809. QAngle angles = pPlayer->EyeAngles();
  810. AngleVectors( angles, NULL, &vecRight, &vecUp );
  811. int r,g,b;
  812. if( ((int)gpGlobals->curtime) % 2 == 1 )
  813. {
  814. r = 255;
  815. g = 255;
  816. b = 255;
  817. if( pPlayer->GetActiveWeapon() != NULL )
  818. radius *= pPlayer->GetActiveWeapon()->WeaponAutoAimScale();
  819. }
  820. else
  821. {
  822. r = 255;g=0;b=0;
  823. if( !ShouldAttractAutoAim(pPlayer) )
  824. {
  825. g = 255;
  826. }
  827. }
  828. if( pPlayer->IsInAVehicle() )
  829. {
  830. radius *= sv_vehicle_autoaim_scale.GetFloat();
  831. }
  832. NDebugOverlay::Line( vecCenter, vecCenter + vecRight * radius, r, g, b, true, 0.1 );
  833. NDebugOverlay::Line( vecCenter, vecCenter - vecRight * radius, r, g, b, true, 0.1 );
  834. NDebugOverlay::Line( vecCenter, vecCenter + vecUp * radius, r, g, b, true, 0.1 );
  835. NDebugOverlay::Line( vecCenter, vecCenter - vecUp * radius, r, g, b, true, 0.1 );
  836. vecDiag = vecRight + vecUp;
  837. VectorNormalize( vecDiag );
  838. NDebugOverlay::Line( vecCenter - vecDiag * radius, vecCenter + vecDiag * radius, r, g, b, true, 0.1 );
  839. vecDiag = vecRight - vecUp;
  840. VectorNormalize( vecDiag );
  841. NDebugOverlay::Line( vecCenter - vecDiag * radius, vecCenter + vecDiag * radius, r, g, b, true, 0.1 );
  842. }
  843. }
  844. //-----------------------------------------------------------------------------
  845. // Purpose: Draw any text overlays (override in subclass to add additional text)
  846. // Output : Current text offset from the top
  847. //-----------------------------------------------------------------------------
  848. int CBaseEntity::DrawDebugTextOverlays(void)
  849. {
  850. int offset = 1;
  851. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  852. {
  853. char tempstr[512];
  854. Q_snprintf( tempstr, sizeof(tempstr), "(%d) Name: %s (%s)", entindex(), GetDebugName(), GetClassname() );
  855. EntityText(offset,tempstr, 0);
  856. offset++;
  857. if( m_iGlobalname != NULL_STRING )
  858. {
  859. Q_snprintf( tempstr, sizeof(tempstr), "GLOBALNAME: %s", STRING(m_iGlobalname) );
  860. EntityText(offset,tempstr, 0);
  861. offset++;
  862. }
  863. Vector vecOrigin = GetAbsOrigin();
  864. Q_snprintf( tempstr, sizeof(tempstr), "Position: %0.1f, %0.1f, %0.1f\n", vecOrigin.x, vecOrigin.y, vecOrigin.z );
  865. EntityText( offset, tempstr, 0 );
  866. offset++;
  867. if( GetModelName() != NULL_STRING || GetBaseAnimating() )
  868. {
  869. Q_snprintf(tempstr, sizeof(tempstr), "Model:%s", STRING(GetModelName()) );
  870. EntityText(offset,tempstr,0);
  871. offset++;
  872. }
  873. if( m_hDamageFilter.Get() != NULL )
  874. {
  875. Q_snprintf( tempstr, sizeof(tempstr), "DAMAGE FILTER:%s", m_hDamageFilter->GetDebugName() );
  876. EntityText( offset,tempstr,0 );
  877. offset++;
  878. }
  879. }
  880. if (m_debugOverlays & OVERLAY_VIEWOFFSET)
  881. {
  882. NDebugOverlay::Cross3D( EyePosition(), 16, 255, 0, 0, true, 0.05f );
  883. }
  884. return offset;
  885. }
  886. void CBaseEntity::SetParent( string_t newParent, CBaseEntity *pActivator, int iAttachment )
  887. {
  888. // find and notify the new parent
  889. CBaseEntity *pParent = gEntList.FindEntityByName( NULL, newParent, NULL, pActivator );
  890. // debug check
  891. if ( newParent != NULL_STRING && pParent == NULL )
  892. {
  893. Msg( "Entity %s(%s) has bad parent %s\n", STRING(m_iClassname), GetDebugName(), STRING(newParent) );
  894. }
  895. else
  896. {
  897. // make sure there isn't any ambiguity
  898. if ( gEntList.FindEntityByName( pParent, newParent, NULL, pActivator ) )
  899. {
  900. Msg( "Entity %s(%s) has ambigious parent %s\n", STRING(m_iClassname), GetDebugName(), STRING(newParent) );
  901. }
  902. SetParent( pParent, iAttachment );
  903. }
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Purpose: Move our points from parent to worldspace
  907. // Input : *pParent - Parent to use as reference
  908. //-----------------------------------------------------------------------------
  909. void CBaseEntity::TransformStepData_ParentToWorld( CBaseEntity *pParent )
  910. {
  911. // Fix up our step simulation points to be in the proper local space
  912. StepSimulationData *step = (StepSimulationData *) GetDataObject( STEPSIMULATION );
  913. if ( step != NULL )
  914. {
  915. // Convert our positions
  916. UTIL_ParentToWorldSpace( pParent, step->m_Previous2.vecOrigin, step->m_Previous2.qRotation );
  917. UTIL_ParentToWorldSpace( pParent, step->m_Previous.vecOrigin, step->m_Previous.qRotation );
  918. }
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose: Move step data between two parent-spaces
  922. // Input : *pOldParent - parent we were attached to
  923. // *pNewParent - parent we're now attached to
  924. //-----------------------------------------------------------------------------
  925. void CBaseEntity::TransformStepData_ParentToParent( CBaseEntity *pOldParent, CBaseEntity *pNewParent )
  926. {
  927. // Fix up our step simulation points to be in the proper local space
  928. StepSimulationData *step = (StepSimulationData *) GetDataObject( STEPSIMULATION );
  929. if ( step != NULL )
  930. {
  931. // Convert our positions
  932. UTIL_ParentToWorldSpace( pOldParent, step->m_Previous2.vecOrigin, step->m_Previous2.qRotation );
  933. UTIL_WorldToParentSpace( pNewParent, step->m_Previous2.vecOrigin, step->m_Previous2.qRotation );
  934. UTIL_ParentToWorldSpace( pOldParent, step->m_Previous.vecOrigin, step->m_Previous.qRotation );
  935. UTIL_WorldToParentSpace( pNewParent, step->m_Previous.vecOrigin, step->m_Previous.qRotation );
  936. }
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose: After parenting to an object, we need to also correctly translate our
  940. // step stimulation positions and angles into that parent space. Otherwise
  941. // we end up splining between two different world spaces.
  942. //-----------------------------------------------------------------------------
  943. void CBaseEntity::TransformStepData_WorldToParent( CBaseEntity *pParent )
  944. {
  945. // Fix up our step simulation points to be in the proper local space
  946. StepSimulationData *step = (StepSimulationData *) GetDataObject( STEPSIMULATION );
  947. if ( step != NULL )
  948. {
  949. // Convert our positions
  950. UTIL_WorldToParentSpace( pParent, step->m_Previous2.vecOrigin, step->m_Previous2.qRotation );
  951. UTIL_WorldToParentSpace( pParent, step->m_Previous.vecOrigin, step->m_Previous.qRotation );
  952. }
  953. }
  954. //-----------------------------------------------------------------------------
  955. // Purpose: Sets the movement parent of this entity. This entity will be moved
  956. // to a local coordinate calculated from its current absolute offset
  957. // from the parent entity and will then follow the parent entity.
  958. // Input : pParentEntity - This entity's new parent in the movement hierarchy.
  959. //-----------------------------------------------------------------------------
  960. void CBaseEntity::SetParent( CBaseEntity *pParentEntity, int iAttachment )
  961. {
  962. // If they didn't specify an attachment, use our current
  963. if ( iAttachment == -1 )
  964. {
  965. iAttachment = m_iParentAttachment;
  966. }
  967. bool bWasNotParented = ( GetParent() == NULL );
  968. CBaseEntity *pOldParent = m_pParent;
  969. // notify the old parent of the loss
  970. UnlinkFromParent( this );
  971. // set the new name
  972. m_pParent = pParentEntity;
  973. if ( m_pParent == this )
  974. {
  975. // should never set parent to 'this' - makes no sense
  976. Assert(0);
  977. m_pParent = NULL;
  978. }
  979. if ( m_pParent == NULL )
  980. {
  981. m_iParent = NULL_STRING;
  982. // Transform step data from parent to worldspace
  983. TransformStepData_ParentToWorld( pOldParent );
  984. return;
  985. }
  986. m_iParent = m_pParent->m_iName;
  987. RemoveSolidFlags( FSOLID_ROOT_PARENT_ALIGNED );
  988. if ( pParentEntity )
  989. {
  990. if ( const_cast<CBaseEntity *>(pParentEntity)->GetRootMoveParent()->GetSolid() == SOLID_BSP )
  991. {
  992. AddSolidFlags( FSOLID_ROOT_PARENT_ALIGNED );
  993. }
  994. else
  995. {
  996. if ( GetSolid() == SOLID_BSP )
  997. {
  998. // Must be SOLID_VPHYSICS because parent might rotate
  999. SetSolid( SOLID_VPHYSICS );
  1000. }
  1001. }
  1002. }
  1003. // set the move parent if we have one
  1004. if ( edict() )
  1005. {
  1006. // add ourselves to the list
  1007. LinkChild( m_pParent, this );
  1008. m_iParentAttachment = (char)iAttachment;
  1009. EntityMatrix matrix, childMatrix;
  1010. matrix.InitFromEntity( const_cast<CBaseEntity *>(pParentEntity), m_iParentAttachment ); // parent->world
  1011. childMatrix.InitFromEntityLocal( this ); // child->world
  1012. Vector localOrigin = matrix.WorldToLocal( GetLocalOrigin() );
  1013. // I have the axes of local space in world space. (childMatrix)
  1014. // I want to compute those world space axes in the parent's local space
  1015. // and set that transform (as angles) on the child's object so the net
  1016. // result is that the child is now in parent space, but still oriented the same way
  1017. VMatrix tmp = matrix.Transpose(); // world->parent
  1018. tmp.MatrixMul( childMatrix, matrix ); // child->parent
  1019. QAngle angles;
  1020. MatrixToAngles( matrix, angles );
  1021. SetLocalAngles( angles );
  1022. UTIL_SetOrigin( this, localOrigin );
  1023. // Move our step data into the correct space
  1024. if ( bWasNotParented )
  1025. {
  1026. // Transform step data from world to parent-space
  1027. TransformStepData_WorldToParent( this );
  1028. }
  1029. else
  1030. {
  1031. // Transform step data between parent-spaces
  1032. TransformStepData_ParentToParent( pOldParent, this );
  1033. }
  1034. }
  1035. if ( VPhysicsGetObject() )
  1036. {
  1037. if ( VPhysicsGetObject()->IsStatic())
  1038. {
  1039. if ( VPhysicsGetObject()->IsAttachedToConstraint(false) )
  1040. {
  1041. Warning("SetParent on static object, all constraints attached to %s (%s)will now be broken!\n", GetDebugName(), GetClassname() );
  1042. }
  1043. VPhysicsDestroyObject();
  1044. VPhysicsInitShadow(false, false);
  1045. }
  1046. }
  1047. CollisionRulesChanged();
  1048. }
  1049. //-----------------------------------------------------------------------------
  1050. // Purpose:
  1051. //-----------------------------------------------------------------------------
  1052. void CBaseEntity::ValidateEntityConnections()
  1053. {
  1054. if ( m_target == NULL_STRING )
  1055. return;
  1056. if ( ClassMatches( "scripted_*" ) ||
  1057. ClassMatches( "trigger_relay" ) ||
  1058. ClassMatches( "trigger_auto" ) ||
  1059. ClassMatches( "path_*" ) ||
  1060. ClassMatches( "monster_*" ) ||
  1061. ClassMatches( "trigger_teleport" ) ||
  1062. ClassMatches( "func_train" ) ||
  1063. ClassMatches( "func_tracktrain" ) ||
  1064. ClassMatches( "func_plat*" ) ||
  1065. ClassMatches( "npc_*" ) ||
  1066. ClassMatches( "info_big*" ) ||
  1067. ClassMatches( "env_texturetoggle" ) ||
  1068. ClassMatches( "env_render" ) ||
  1069. ClassMatches( "func_areaportalwindow") ||
  1070. ClassMatches( "point_view*") ||
  1071. ClassMatches( "func_traincontrols" ) ||
  1072. ClassMatches( "multisource" ) ||
  1073. ClassMatches( "xen_plant*" ) )
  1074. return;
  1075. datamap_t *dmap = GetDataDescMap();
  1076. while ( dmap )
  1077. {
  1078. int fields = dmap->dataNumFields;
  1079. for ( int i = 0; i < fields; i++ )
  1080. {
  1081. typedescription_t *dataDesc = &dmap->dataDesc[i];
  1082. if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) )
  1083. {
  1084. CBaseEntityOutput *pOutput = (CBaseEntityOutput *)((int)this + (int)dataDesc->fieldOffset[0]);
  1085. if ( pOutput->NumberOfElements() )
  1086. return;
  1087. }
  1088. }
  1089. dmap = dmap->baseMap;
  1090. }
  1091. Vector vecLoc = WorldSpaceCenter();
  1092. Warning("---------------------------------\n");
  1093. Warning( "Entity %s - (%s) has a target and NO OUTPUTS\n", GetDebugName(), GetClassname() );
  1094. Warning( "Location %f %f %f\n", vecLoc.x, vecLoc.y, vecLoc.z );
  1095. Warning("---------------------------------\n");
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose:
  1099. //-----------------------------------------------------------------------------
  1100. void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay )
  1101. {
  1102. if ( pszOutput == NULL )
  1103. return;
  1104. datamap_t *dmap = GetDataDescMap();
  1105. while ( dmap )
  1106. {
  1107. int fields = dmap->dataNumFields;
  1108. for ( int i = 0; i < fields; i++ )
  1109. {
  1110. typedescription_t *dataDesc = &dmap->dataDesc[i];
  1111. if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) )
  1112. {
  1113. CBaseEntityOutput *pOutput = ( CBaseEntityOutput * )( ( int )this + ( int )dataDesc->fieldOffset[0] );
  1114. if ( !Q_stricmp( dataDesc->externalName, pszOutput ) )
  1115. {
  1116. pOutput->FireOutput( variant, pActivator, pCaller, flDelay );
  1117. return;
  1118. }
  1119. }
  1120. }
  1121. dmap = dmap->baseMap;
  1122. }
  1123. }
  1124. void CBaseEntity::Activate( void )
  1125. {
  1126. #ifdef DEBUG
  1127. extern bool g_bCheckForChainedActivate;
  1128. extern bool g_bReceivedChainedActivate;
  1129. if ( g_bCheckForChainedActivate && g_bReceivedChainedActivate )
  1130. {
  1131. Assert( !"Multiple calls to base class Activate()\n" );
  1132. }
  1133. g_bReceivedChainedActivate = true;
  1134. #endif
  1135. // NOTE: This forces a team change so that stuff in the level
  1136. // that starts out on a team correctly changes team
  1137. if (m_iInitialTeamNum)
  1138. {
  1139. ChangeTeam( m_iInitialTeamNum );
  1140. }
  1141. // Get a handle to my damage filter entity if there is one.
  1142. if ( m_iszDamageFilterName != NULL_STRING )
  1143. {
  1144. m_hDamageFilter = gEntList.FindEntityByName( NULL, m_iszDamageFilterName );
  1145. }
  1146. // Add any non-null context strings to our context vector
  1147. if ( m_iszResponseContext != NULL_STRING )
  1148. {
  1149. AddContext( m_iszResponseContext.ToCStr() );
  1150. }
  1151. #ifdef HL1_DLL
  1152. ValidateEntityConnections();
  1153. #endif //HL1_DLL
  1154. }
  1155. //////////////////////////// old CBaseEntity stuff ///////////////////////////////////
  1156. // give health.
  1157. // Returns the amount of health actually taken.
  1158. int CBaseEntity::TakeHealth( float flHealth, int bitsDamageType )
  1159. {
  1160. if ( !edict() || m_takedamage < DAMAGE_YES )
  1161. return 0;
  1162. int iMax = GetMaxHealth();
  1163. // heal
  1164. if ( m_iHealth >= iMax )
  1165. return 0;
  1166. const int oldHealth = m_iHealth;
  1167. m_iHealth += flHealth;
  1168. if (m_iHealth > iMax)
  1169. m_iHealth = iMax;
  1170. return m_iHealth - oldHealth;
  1171. }
  1172. // inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH
  1173. int CBaseEntity::OnTakeDamage( const CTakeDamageInfo &info )
  1174. {
  1175. Vector vecTemp;
  1176. if ( !edict() || !m_takedamage )
  1177. return 0;
  1178. if ( info.GetInflictor() )
  1179. {
  1180. vecTemp = info.GetInflictor()->WorldSpaceCenter() - ( WorldSpaceCenter() );
  1181. }
  1182. else
  1183. {
  1184. vecTemp.Init( 1, 0, 0 );
  1185. }
  1186. // this global is still used for glass and other non-NPC killables, along with decals.
  1187. g_vecAttackDir = vecTemp;
  1188. VectorNormalize(g_vecAttackDir);
  1189. // save damage based on the target's armor level
  1190. // figure momentum add (don't let hurt brushes or other triggers move player)
  1191. // physics objects have their own calcs for this: (don't let fire move things around!)
  1192. if ( !IsEFlagSet( EFL_NO_DAMAGE_FORCES ) )
  1193. {
  1194. if ( ( GetMoveType() == MOVETYPE_VPHYSICS ) )
  1195. {
  1196. VPhysicsTakeDamage( info );
  1197. }
  1198. else
  1199. {
  1200. if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK || GetMoveType() == MOVETYPE_STEP) &&
  1201. !info.GetAttacker()->IsSolidFlagSet(FSOLID_TRIGGER) )
  1202. {
  1203. Vector vecDir, vecInflictorCentroid;
  1204. vecDir = WorldSpaceCenter( );
  1205. vecInflictorCentroid = info.GetInflictor()->WorldSpaceCenter( );
  1206. vecDir -= vecInflictorCentroid;
  1207. VectorNormalize( vecDir );
  1208. float flForce = info.GetDamage() * ((32 * 32 * 72.0) / (WorldAlignSize().x * WorldAlignSize().y * WorldAlignSize().z)) * 5;
  1209. if (flForce > 1000.0)
  1210. flForce = 1000.0;
  1211. ApplyAbsVelocityImpulse( vecDir * flForce );
  1212. }
  1213. }
  1214. }
  1215. if ( m_takedamage != DAMAGE_EVENTS_ONLY )
  1216. {
  1217. // do the damage
  1218. m_iHealth -= info.GetDamage();
  1219. if (m_iHealth <= 0)
  1220. {
  1221. Event_Killed( info );
  1222. return 0;
  1223. }
  1224. }
  1225. return 1;
  1226. }
  1227. //-----------------------------------------------------------------------------
  1228. // Purpose: Scale damage done and call OnTakeDamage
  1229. //-----------------------------------------------------------------------------
  1230. int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo )
  1231. {
  1232. if ( !g_pGameRules )
  1233. return 0;
  1234. bool bHasPhysicsForceDamage = !g_pGameRules->Damage_NoPhysicsForce( inputInfo.GetDamageType() );
  1235. if ( bHasPhysicsForceDamage && inputInfo.GetDamageType() != DMG_GENERIC )
  1236. {
  1237. // If you hit this assert, you've called TakeDamage with a damage type that requires a physics damage
  1238. // force & position without specifying one or both of them. Decide whether your damage that's causing
  1239. // this is something you believe should impart physics force on the receiver. If it is, you need to
  1240. // setup the damage force & position inside the CTakeDamageInfo (Utility functions for this are in
  1241. // takedamageinfo.cpp. If you think the damage shouldn't cause force (unlikely!) then you can set the
  1242. // damage type to DMG_GENERIC, or | DMG_CRUSH if you need to preserve the damage type for purposes of HUD display.
  1243. if ( inputInfo.GetDamageForce() == vec3_origin || inputInfo.GetDamagePosition() == vec3_origin )
  1244. {
  1245. static int warningCount = 0;
  1246. if ( ++warningCount < 10 )
  1247. {
  1248. if ( inputInfo.GetDamageForce() == vec3_origin )
  1249. {
  1250. DevWarning( "CBaseEntity::TakeDamage: with inputInfo.GetDamageForce() == vec3_origin\n" );
  1251. }
  1252. if ( inputInfo.GetDamagePosition() == vec3_origin )
  1253. {
  1254. DevWarning( "CBaseEntity::TakeDamage: with inputInfo.GetDamagePosition() == vec3_origin\n" );
  1255. }
  1256. }
  1257. }
  1258. }
  1259. // Make sure our damage filter allows the damage.
  1260. if ( !PassesDamageFilter( inputInfo ))
  1261. {
  1262. return 0;
  1263. }
  1264. if( !g_pGameRules->AllowDamage(this, inputInfo) )
  1265. {
  1266. return 0;
  1267. }
  1268. if ( PhysIsInCallback() )
  1269. {
  1270. PhysCallbackDamage( this, inputInfo );
  1271. }
  1272. else
  1273. {
  1274. CTakeDamageInfo info = inputInfo;
  1275. // Scale the damage by the attacker's modifier.
  1276. if ( info.GetAttacker() )
  1277. {
  1278. info.ScaleDamage( info.GetAttacker()->GetAttackDamageScale( this ) );
  1279. }
  1280. // Scale the damage by my own modifiers
  1281. info.ScaleDamage( GetReceivedDamageScale( info.GetAttacker() ) );
  1282. //Msg("%s took %.2f Damage, at %.2f\n", GetClassname(), info.GetDamage(), gpGlobals->curtime );
  1283. return OnTakeDamage( info );
  1284. }
  1285. return 0;
  1286. }
  1287. //-----------------------------------------------------------------------------
  1288. // Purpose: Returns a value that scales all damage done by this entity.
  1289. //-----------------------------------------------------------------------------
  1290. float CBaseEntity::GetAttackDamageScale( CBaseEntity *pVictim )
  1291. {
  1292. float flScale = 1;
  1293. FOR_EACH_LL( m_DamageModifiers, i )
  1294. {
  1295. if ( !m_DamageModifiers[i]->IsDamageDoneToMe() )
  1296. {
  1297. flScale *= m_DamageModifiers[i]->GetModifier();
  1298. }
  1299. }
  1300. return flScale;
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose: Returns a value that scales all damage done to this entity
  1304. //-----------------------------------------------------------------------------
  1305. float CBaseEntity::GetReceivedDamageScale( CBaseEntity *pAttacker )
  1306. {
  1307. float flScale = 1;
  1308. FOR_EACH_LL( m_DamageModifiers, i )
  1309. {
  1310. if ( m_DamageModifiers[i]->IsDamageDoneToMe() )
  1311. {
  1312. flScale *= m_DamageModifiers[i]->GetModifier();
  1313. }
  1314. }
  1315. return flScale;
  1316. }
  1317. //-----------------------------------------------------------------------------
  1318. // Purpose: Applies forces to our physics object in response to damage.
  1319. //-----------------------------------------------------------------------------
  1320. int CBaseEntity::VPhysicsTakeDamage( const CTakeDamageInfo &info )
  1321. {
  1322. // don't let physics impacts or fire cause objects to move (again)
  1323. bool bNoPhysicsForceDamage = g_pGameRules->Damage_NoPhysicsForce( info.GetDamageType() );
  1324. if ( bNoPhysicsForceDamage || info.GetDamageType() == DMG_GENERIC )
  1325. return 1;
  1326. Assert(VPhysicsGetObject() != NULL);
  1327. if ( VPhysicsGetObject() )
  1328. {
  1329. Vector force = info.GetDamageForce();
  1330. Vector offset = info.GetDamagePosition();
  1331. // If you hit this assert, you've called TakeDamage with a damage type that requires a physics damage
  1332. // force & position without specifying one or both of them. Decide whether your damage that's causing
  1333. // this is something you believe should impart physics force on the receiver. If it is, you need to
  1334. // setup the damage force & position inside the CTakeDamageInfo (Utility functions for this are in
  1335. // takedamageinfo.cpp. If you think the damage shouldn't cause force (unlikely!) then you can set the
  1336. // damage type to DMG_GENERIC, or | DMG_CRUSH if you need to preserve the damage type for purposes of HUD display.
  1337. #if !defined( TF_DLL )
  1338. Assert( force != vec3_origin && offset != vec3_origin );
  1339. #else
  1340. // this was spamming the console for Payload maps in TF (trigger_hurt entity on the front of the cart)
  1341. if ( !TFGameRules() || TFGameRules()->GetGameType() != TF_GAMETYPE_ESCORT )
  1342. {
  1343. Assert( force != vec3_origin && offset != vec3_origin );
  1344. }
  1345. #endif
  1346. unsigned short gameFlags = VPhysicsGetObject()->GetGameFlags();
  1347. if ( gameFlags & FVPHYSICS_PLAYER_HELD )
  1348. {
  1349. // if the player is holding the object, use it's real mass (player holding reduced the mass)
  1350. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  1351. if ( pPlayer )
  1352. {
  1353. float mass = pPlayer->GetHeldObjectMass( VPhysicsGetObject() );
  1354. if ( mass != 0.0f )
  1355. {
  1356. float ratio = VPhysicsGetObject()->GetMass() / mass;
  1357. force *= ratio;
  1358. }
  1359. }
  1360. }
  1361. else if ( (gameFlags & FVPHYSICS_PART_OF_RAGDOLL) && (gameFlags & FVPHYSICS_CONSTRAINT_STATIC) )
  1362. {
  1363. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1364. int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1365. for ( int i = 0; i < count; i++ )
  1366. {
  1367. if ( !(pList[i]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) )
  1368. {
  1369. pList[i]->ApplyForceOffset( force, offset );
  1370. return 1;
  1371. }
  1372. }
  1373. }
  1374. VPhysicsGetObject()->ApplyForceOffset( force, offset );
  1375. }
  1376. return 1;
  1377. }
  1378. // Character killed (only fired once)
  1379. void CBaseEntity::Event_Killed( const CTakeDamageInfo &info )
  1380. {
  1381. if( info.GetAttacker() )
  1382. {
  1383. info.GetAttacker()->Event_KilledOther(this, info);
  1384. }
  1385. m_takedamage = DAMAGE_NO;
  1386. m_lifeState = LIFE_DEAD;
  1387. UTIL_Remove( this );
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // Purpose: helper method to send a game event when this entity is killed. Note:
  1391. // gets called specifically for particular entities (mostly NPC), this
  1392. // does not get called for every entity
  1393. //-----------------------------------------------------------------------------
  1394. void CBaseEntity::SendOnKilledGameEvent( const CTakeDamageInfo &info )
  1395. {
  1396. IGameEvent *event = gameeventmanager->CreateEvent( "entity_killed" );
  1397. if ( event )
  1398. {
  1399. event->SetInt( "entindex_killed", entindex() );
  1400. if ( info.GetAttacker())
  1401. {
  1402. event->SetInt( "entindex_attacker", info.GetAttacker()->entindex() );
  1403. }
  1404. if ( info.GetInflictor())
  1405. {
  1406. event->SetInt( "entindex_inflictor", info.GetInflictor()->entindex() );
  1407. }
  1408. event->SetInt( "damagebits", info.GetDamageType() );
  1409. gameeventmanager->FireEvent( event );
  1410. }
  1411. }
  1412. bool CBaseEntity::HasTarget( string_t targetname )
  1413. {
  1414. if( targetname != NULL_STRING && m_target != NULL_STRING )
  1415. return FStrEq(STRING(targetname), STRING(m_target) );
  1416. else
  1417. return false;
  1418. }
  1419. CBaseEntity *CBaseEntity::GetNextTarget( void )
  1420. {
  1421. if ( !m_target )
  1422. return NULL;
  1423. return gEntList.FindEntityByName( NULL, m_target );
  1424. }
  1425. class CThinkContextsSaveDataOps : public CDefSaveRestoreOps
  1426. {
  1427. virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
  1428. {
  1429. AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CThinkContextsSaveDataOps does not support arrays");
  1430. // Write out the vector
  1431. CUtlVector< thinkfunc_t > *pUtlVector = (CUtlVector< thinkfunc_t > *)fieldInfo.pField;
  1432. SaveUtlVector( pSave, pUtlVector, FIELD_EMBEDDED );
  1433. // Get our owner
  1434. CBaseEntity *pOwner = (CBaseEntity*)fieldInfo.pOwner;
  1435. pSave->StartBlock();
  1436. // Now write out all the functions
  1437. for ( int i = 0; i < pUtlVector->Size(); i++ )
  1438. {
  1439. #ifdef WIN32
  1440. void **ppV = (void**)&((*pUtlVector)[i].m_pfnThink);
  1441. #else
  1442. BASEPTR *ppV = &((*pUtlVector)[i].m_pfnThink);
  1443. #endif
  1444. bool bHasFunc = (*ppV != NULL);
  1445. pSave->WriteBool( &bHasFunc, 1 );
  1446. if ( bHasFunc )
  1447. {
  1448. pSave->WriteFunction( pOwner->GetDataDescMap(), "m_pfnThink", (inputfunc_t **)ppV, 1 );
  1449. }
  1450. }
  1451. pSave->EndBlock();
  1452. }
  1453. virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
  1454. {
  1455. AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CThinkContextsSaveDataOps does not support arrays");
  1456. // Read in the vector
  1457. CUtlVector< thinkfunc_t > *pUtlVector = (CUtlVector< thinkfunc_t > *)fieldInfo.pField;
  1458. RestoreUtlVector( pRestore, pUtlVector, FIELD_EMBEDDED );
  1459. // Get our owner
  1460. CBaseEntity *pOwner = (CBaseEntity*)fieldInfo.pOwner;
  1461. pRestore->StartBlock();
  1462. // Now read in all the functions
  1463. for ( int i = 0; i < pUtlVector->Size(); i++ )
  1464. {
  1465. bool bHasFunc;
  1466. pRestore->ReadBool( &bHasFunc, 1 );
  1467. #ifdef WIN32
  1468. void **ppV = (void**)&((*pUtlVector)[i].m_pfnThink);
  1469. #else
  1470. BASEPTR *ppV = &((*pUtlVector)[i].m_pfnThink);
  1471. Q_memset( (void *)ppV, 0x0, sizeof(inputfunc_t) );
  1472. #endif
  1473. if ( bHasFunc )
  1474. {
  1475. SaveRestoreRecordHeader_t header;
  1476. pRestore->ReadHeader( &header );
  1477. pRestore->ReadFunction( pOwner->GetDataDescMap(), (inputfunc_t **)ppV, 1, header.size );
  1478. }
  1479. else
  1480. {
  1481. *ppV = NULL;
  1482. }
  1483. }
  1484. pRestore->EndBlock();
  1485. }
  1486. virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
  1487. {
  1488. CUtlVector< thinkfunc_t > *pUtlVector = (CUtlVector< thinkfunc_t > *)fieldInfo.pField;
  1489. return ( pUtlVector->Count() == 0 );
  1490. }
  1491. virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
  1492. {
  1493. BASEPTR pFunc = *((BASEPTR*)fieldInfo.pField);
  1494. pFunc = NULL;
  1495. }
  1496. };
  1497. CThinkContextsSaveDataOps g_ThinkContextsSaveDataOps;
  1498. ISaveRestoreOps *thinkcontextFuncs = &g_ThinkContextsSaveDataOps;
  1499. BEGIN_SIMPLE_DATADESC( thinkfunc_t )
  1500. DEFINE_FIELD( m_iszContext, FIELD_STRING ),
  1501. // DEFINE_FIELD( m_pfnThink, FIELD_FUNCTION ), // Manually written
  1502. DEFINE_FIELD( m_nNextThinkTick, FIELD_TICK ),
  1503. DEFINE_FIELD( m_nLastThinkTick, FIELD_TICK ),
  1504. END_DATADESC()
  1505. BEGIN_SIMPLE_DATADESC( ResponseContext_t )
  1506. DEFINE_FIELD( m_iszName, FIELD_STRING ),
  1507. DEFINE_FIELD( m_iszValue, FIELD_STRING ),
  1508. DEFINE_FIELD( m_fExpirationTime, FIELD_TIME ),
  1509. END_DATADESC()
  1510. BEGIN_DATADESC_NO_BASE( CBaseEntity )
  1511. DEFINE_KEYFIELD( m_iClassname, FIELD_STRING, "classname" ),
  1512. DEFINE_GLOBAL_KEYFIELD( m_iGlobalname, FIELD_STRING, "globalname" ),
  1513. DEFINE_KEYFIELD( m_iParent, FIELD_STRING, "parentname" ),
  1514. DEFINE_KEYFIELD( m_iHammerID, FIELD_INTEGER, "hammerid" ), // save ID numbers so that entities can be tracked between save/restore and vmf
  1515. DEFINE_KEYFIELD( m_flSpeed, FIELD_FLOAT, "speed" ),
  1516. DEFINE_KEYFIELD( m_nRenderFX, FIELD_CHARACTER, "renderfx" ),
  1517. DEFINE_KEYFIELD( m_nRenderMode, FIELD_CHARACTER, "rendermode" ),
  1518. // Consider moving to CBaseAnimating?
  1519. DEFINE_FIELD( m_flPrevAnimTime, FIELD_TIME ),
  1520. DEFINE_FIELD( m_flAnimTime, FIELD_TIME ),
  1521. DEFINE_FIELD( m_flSimulationTime, FIELD_TIME ),
  1522. DEFINE_FIELD( m_nLastThinkTick, FIELD_TICK ),
  1523. DEFINE_KEYFIELD( m_nNextThinkTick, FIELD_TICK, "nextthink" ),
  1524. DEFINE_KEYFIELD( m_fEffects, FIELD_INTEGER, "effects" ),
  1525. DEFINE_KEYFIELD( m_clrRender, FIELD_COLOR32, "rendercolor" ),
  1526. DEFINE_GLOBAL_KEYFIELD( m_nModelIndex, FIELD_SHORT, "modelindex" ),
  1527. #if !defined( NO_ENTITY_PREDICTION )
  1528. // DEFINE_FIELD( m_PredictableID, CPredictableId ),
  1529. #endif
  1530. DEFINE_FIELD( touchStamp, FIELD_INTEGER ),
  1531. DEFINE_CUSTOM_FIELD( m_aThinkFunctions, thinkcontextFuncs ),
  1532. // m_iCurrentThinkContext (not saved, debug field only, and think transient to boot)
  1533. DEFINE_UTLVECTOR(m_ResponseContexts, FIELD_EMBEDDED),
  1534. DEFINE_KEYFIELD( m_iszResponseContext, FIELD_STRING, "ResponseContext" ),
  1535. DEFINE_FIELD( m_pfnThink, FIELD_FUNCTION ),
  1536. DEFINE_FIELD( m_pfnTouch, FIELD_FUNCTION ),
  1537. DEFINE_FIELD( m_pfnUse, FIELD_FUNCTION ),
  1538. DEFINE_FIELD( m_pfnBlocked, FIELD_FUNCTION ),
  1539. DEFINE_FIELD( m_pfnMoveDone, FIELD_FUNCTION ),
  1540. DEFINE_FIELD( m_lifeState, FIELD_CHARACTER ),
  1541. DEFINE_FIELD( m_takedamage, FIELD_CHARACTER ),
  1542. DEFINE_KEYFIELD( m_iMaxHealth, FIELD_INTEGER, "max_health" ),
  1543. DEFINE_KEYFIELD( m_iHealth, FIELD_INTEGER, "health" ),
  1544. // DEFINE_FIELD( m_pLink, FIELD_CLASSPTR ),
  1545. DEFINE_KEYFIELD( m_target, FIELD_STRING, "target" ),
  1546. DEFINE_KEYFIELD( m_iszDamageFilterName, FIELD_STRING, "damagefilter" ),
  1547. DEFINE_FIELD( m_hDamageFilter, FIELD_EHANDLE ),
  1548. DEFINE_FIELD( m_debugOverlays, FIELD_INTEGER ),
  1549. DEFINE_GLOBAL_FIELD( m_pParent, FIELD_EHANDLE ),
  1550. DEFINE_FIELD( m_iParentAttachment, FIELD_CHARACTER ),
  1551. DEFINE_GLOBAL_FIELD( m_hMoveParent, FIELD_EHANDLE ),
  1552. DEFINE_GLOBAL_FIELD( m_hMoveChild, FIELD_EHANDLE ),
  1553. DEFINE_GLOBAL_FIELD( m_hMovePeer, FIELD_EHANDLE ),
  1554. DEFINE_FIELD( m_iEFlags, FIELD_INTEGER ),
  1555. DEFINE_FIELD( m_iName, FIELD_STRING ),
  1556. DEFINE_EMBEDDED( m_Collision ),
  1557. DEFINE_EMBEDDED( m_Network ),
  1558. DEFINE_FIELD( m_MoveType, FIELD_CHARACTER ),
  1559. DEFINE_FIELD( m_MoveCollide, FIELD_CHARACTER ),
  1560. DEFINE_FIELD( m_hOwnerEntity, FIELD_EHANDLE ),
  1561. DEFINE_FIELD( m_CollisionGroup, FIELD_INTEGER ),
  1562. DEFINE_PHYSPTR( m_pPhysicsObject),
  1563. DEFINE_FIELD( m_flElasticity, FIELD_FLOAT ),
  1564. DEFINE_KEYFIELD( m_flShadowCastDistance, FIELD_FLOAT, "shadowcastdist" ),
  1565. DEFINE_FIELD( m_flDesiredShadowCastDistance, FIELD_FLOAT ),
  1566. DEFINE_INPUT( m_iInitialTeamNum, FIELD_INTEGER, "TeamNum" ),
  1567. DEFINE_FIELD( m_iTeamNum, FIELD_INTEGER ),
  1568. // DEFINE_FIELD( m_bSentLastFrame, FIELD_INTEGER ),
  1569. DEFINE_FIELD( m_hGroundEntity, FIELD_EHANDLE ),
  1570. DEFINE_FIELD( m_flGroundChangeTime, FIELD_TIME ),
  1571. DEFINE_GLOBAL_KEYFIELD( m_ModelName, FIELD_MODELNAME, "model" ),
  1572. DEFINE_KEYFIELD( m_vecBaseVelocity, FIELD_VECTOR, "basevelocity" ),
  1573. DEFINE_FIELD( m_vecAbsVelocity, FIELD_VECTOR ),
  1574. DEFINE_KEYFIELD( m_vecAngVelocity, FIELD_VECTOR, "avelocity" ),
  1575. // DEFINE_FIELD( m_vecAbsAngVelocity, FIELD_VECTOR ),
  1576. DEFINE_ARRAY( m_rgflCoordinateFrame, FIELD_FLOAT, 12 ), // NOTE: MUST BE IN LOCAL SPACE, NOT POSITION_VECTOR!!! (see CBaseEntity::Restore)
  1577. DEFINE_KEYFIELD( m_nWaterLevel, FIELD_CHARACTER, "waterlevel" ),
  1578. DEFINE_FIELD( m_nWaterType, FIELD_CHARACTER ),
  1579. DEFINE_FIELD( m_pBlocker, FIELD_EHANDLE ),
  1580. DEFINE_KEYFIELD( m_flGravity, FIELD_FLOAT, "gravity" ),
  1581. DEFINE_KEYFIELD( m_flFriction, FIELD_FLOAT, "friction" ),
  1582. // Local time is local to each object. It doesn't need to be re-based if the clock
  1583. // changes. Therefore it is saved as a FIELD_FLOAT, not a FIELD_TIME
  1584. DEFINE_KEYFIELD( m_flLocalTime, FIELD_FLOAT, "ltime" ),
  1585. DEFINE_FIELD( m_flVPhysicsUpdateLocalTime, FIELD_FLOAT ),
  1586. DEFINE_FIELD( m_flMoveDoneTime, FIELD_FLOAT ),
  1587. // DEFINE_FIELD( m_nPushEnumCount, FIELD_INTEGER ),
  1588. DEFINE_FIELD( m_vecAbsOrigin, FIELD_POSITION_VECTOR ),
  1589. DEFINE_KEYFIELD( m_vecVelocity, FIELD_VECTOR, "velocity" ),
  1590. DEFINE_KEYFIELD( m_iTextureFrameIndex, FIELD_CHARACTER, "texframeindex" ),
  1591. DEFINE_FIELD( m_bSimulatedEveryTick, FIELD_BOOLEAN ),
  1592. DEFINE_FIELD( m_bAnimatedEveryTick, FIELD_BOOLEAN ),
  1593. DEFINE_FIELD( m_bAlternateSorting, FIELD_BOOLEAN ),
  1594. DEFINE_KEYFIELD( m_spawnflags, FIELD_INTEGER, "spawnflags" ),
  1595. DEFINE_FIELD( m_nTransmitStateOwnedCounter, FIELD_CHARACTER ),
  1596. DEFINE_FIELD( m_angAbsRotation, FIELD_VECTOR ),
  1597. DEFINE_FIELD( m_vecOrigin, FIELD_VECTOR ), // NOTE: MUST BE IN LOCAL SPACE, NOT POSITION_VECTOR!!! (see CBaseEntity::Restore)
  1598. DEFINE_FIELD( m_angRotation, FIELD_VECTOR ),
  1599. DEFINE_KEYFIELD( m_vecViewOffset, FIELD_VECTOR, "view_ofs" ),
  1600. DEFINE_FIELD( m_fFlags, FIELD_INTEGER ),
  1601. #if !defined( NO_ENTITY_PREDICTION )
  1602. // DEFINE_FIELD( m_bIsPlayerSimulated, FIELD_INTEGER ),
  1603. // DEFINE_FIELD( m_hPlayerSimulationOwner, FIELD_EHANDLE ),
  1604. #endif
  1605. // DEFINE_FIELD( m_pTimedOverlay, TimedOverlay_t* ),
  1606. DEFINE_FIELD( m_nSimulationTick, FIELD_TICK ),
  1607. // DEFINE_FIELD( m_RefEHandle, CBaseHandle ),
  1608. // DEFINE_FIELD( m_nWaterTouch, FIELD_INTEGER ),
  1609. // DEFINE_FIELD( m_nSlimeTouch, FIELD_INTEGER ),
  1610. DEFINE_FIELD( m_flNavIgnoreUntilTime, FIELD_TIME ),
  1611. // DEFINE_FIELD( m_bToolRecording, FIELD_BOOLEAN ),
  1612. // DEFINE_FIELD( m_ToolHandle, FIELD_INTEGER ),
  1613. // NOTE: This is tricky. TeamNum must be saved, but we can't directly
  1614. // read it in, because we can only set it after the team entity has been read in,
  1615. // which may or may not actually occur before the entity is parsed.
  1616. // Therefore, we set the TeamNum from the InitialTeamNum in Activate
  1617. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetTeam", InputSetTeam ),
  1618. DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
  1619. DEFINE_INPUTFUNC( FIELD_VOID, "KillHierarchy", InputKillHierarchy ),
  1620. DEFINE_INPUTFUNC( FIELD_VOID, "Use", InputUse ),
  1621. DEFINE_INPUTFUNC( FIELD_INTEGER, "Alpha", InputAlpha ),
  1622. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "AlternativeSorting", InputAlternativeSorting ),
  1623. DEFINE_INPUTFUNC( FIELD_COLOR32, "Color", InputColor ),
  1624. DEFINE_INPUTFUNC( FIELD_STRING, "SetParent", InputSetParent ),
  1625. DEFINE_INPUTFUNC( FIELD_STRING, "SetParentAttachment", InputSetParentAttachment ),
  1626. DEFINE_INPUTFUNC( FIELD_STRING, "SetParentAttachmentMaintainOffset", InputSetParentAttachmentMaintainOffset ),
  1627. DEFINE_INPUTFUNC( FIELD_VOID, "ClearParent", InputClearParent ),
  1628. DEFINE_INPUTFUNC( FIELD_STRING, "SetDamageFilter", InputSetDamageFilter ),
  1629. DEFINE_INPUTFUNC( FIELD_VOID, "EnableDamageForces", InputEnableDamageForces ),
  1630. DEFINE_INPUTFUNC( FIELD_VOID, "DisableDamageForces", InputDisableDamageForces ),
  1631. DEFINE_INPUTFUNC( FIELD_STRING, "DispatchEffect", InputDispatchEffect ),
  1632. DEFINE_INPUTFUNC( FIELD_STRING, "DispatchResponse", InputDispatchResponse ),
  1633. // Entity I/O methods to alter context
  1634. DEFINE_INPUTFUNC( FIELD_STRING, "AddContext", InputAddContext ),
  1635. DEFINE_INPUTFUNC( FIELD_STRING, "RemoveContext", InputRemoveContext ),
  1636. DEFINE_INPUTFUNC( FIELD_STRING, "ClearContext", InputClearContext ),
  1637. DEFINE_INPUTFUNC( FIELD_VOID, "DisableShadow", InputDisableShadow ),
  1638. DEFINE_INPUTFUNC( FIELD_VOID, "EnableShadow", InputEnableShadow ),
  1639. DEFINE_INPUTFUNC( FIELD_STRING, "AddOutput", InputAddOutput ),
  1640. DEFINE_INPUTFUNC( FIELD_STRING, "FireUser1", InputFireUser1 ),
  1641. DEFINE_INPUTFUNC( FIELD_STRING, "FireUser2", InputFireUser2 ),
  1642. DEFINE_INPUTFUNC( FIELD_STRING, "FireUser3", InputFireUser3 ),
  1643. DEFINE_INPUTFUNC( FIELD_STRING, "FireUser4", InputFireUser4 ),
  1644. DEFINE_OUTPUT( m_OnUser1, "OnUser1" ),
  1645. DEFINE_OUTPUT( m_OnUser2, "OnUser2" ),
  1646. DEFINE_OUTPUT( m_OnUser3, "OnUser3" ),
  1647. DEFINE_OUTPUT( m_OnUser4, "OnUser4" ),
  1648. // Function Pointers
  1649. DEFINE_FUNCTION( SUB_Remove ),
  1650. DEFINE_FUNCTION( SUB_DoNothing ),
  1651. DEFINE_FUNCTION( SUB_StartFadeOut ),
  1652. DEFINE_FUNCTION( SUB_StartFadeOutInstant ),
  1653. DEFINE_FUNCTION( SUB_FadeOut ),
  1654. DEFINE_FUNCTION( SUB_Vanish ),
  1655. DEFINE_FUNCTION( SUB_CallUseToggle ),
  1656. DEFINE_THINKFUNC( ShadowCastDistThink ),
  1657. DEFINE_FIELD( m_hEffectEntity, FIELD_EHANDLE ),
  1658. //DEFINE_FIELD( m_DamageModifiers, FIELD_?? ), // can't save?
  1659. // DEFINE_FIELD( m_fDataObjectTypes, FIELD_INTEGER ),
  1660. #ifdef TF_DLL
  1661. DEFINE_ARRAY( m_nModelIndexOverrides, FIELD_INTEGER, MAX_VISION_MODES ),
  1662. #endif
  1663. END_DATADESC()
  1664. // For code error checking
  1665. extern bool g_bReceivedChainedUpdateOnRemove;
  1666. //-----------------------------------------------------------------------------
  1667. // Purpose: Called just prior to object destruction
  1668. // Entities that need to unlink themselves from other entities should do the unlinking
  1669. // here rather than in their destructor. The reason why is that when the global entity list
  1670. // is told to Clear(), it first takes a pass through all active entities and calls UTIL_Remove
  1671. // on each such entity. Then it calls the delete function on each deleted entity in the list.
  1672. // In the old code, the objects were simply destroyed in order and there was no guarantee that the
  1673. // destructor of one object would not try to access another object that might already have been
  1674. // destructed (especially since the entity list order is more or less random!).
  1675. // NOTE: You should never call delete directly on an entity (there's an assert now), see note
  1676. // at CBaseEntity::~CBaseEntity for more information.
  1677. //
  1678. // NOTE: You should chain to BaseClass::UpdateOnRemove after doing your own cleanup code, e.g.:
  1679. //
  1680. // void CDerived::UpdateOnRemove( void )
  1681. // {
  1682. // ... cleanup code
  1683. // ...
  1684. //
  1685. // BaseClass::UpdateOnRemove();
  1686. // }
  1687. //
  1688. // In general, this function updates global tables that need to know about entities being removed
  1689. //-----------------------------------------------------------------------------
  1690. void CBaseEntity::UpdateOnRemove( void )
  1691. {
  1692. g_bReceivedChainedUpdateOnRemove = true;
  1693. // Virtual call to shut down any looping sounds.
  1694. StopLoopingSounds();
  1695. // Notifies entity listeners, etc
  1696. gEntList.NotifyRemoveEntity( GetRefEHandle() );
  1697. if ( edict() )
  1698. {
  1699. AddFlag( FL_KILLME );
  1700. if ( GetFlags() & FL_GRAPHED )
  1701. {
  1702. /* <<TODO>>
  1703. // this entity was a LinkEnt in the world node graph, so we must remove it from
  1704. // the graph since we are removing it from the world.
  1705. for ( int i = 0 ; i < WorldGraph.m_cLinks ; i++ )
  1706. {
  1707. if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev )
  1708. {
  1709. // if this link has a link ent which is the same ent that is removing itself, remove it!
  1710. WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL;
  1711. }
  1712. }
  1713. */
  1714. }
  1715. }
  1716. if ( m_iGlobalname != NULL_STRING )
  1717. {
  1718. // NOTE: During level shutdown the global list will suppress this
  1719. // it assumes your changing levels or the game will end
  1720. // causing the whole list to be flushed
  1721. GlobalEntity_SetState( m_iGlobalname, GLOBAL_DEAD );
  1722. }
  1723. VPhysicsDestroyObject();
  1724. // This is only here to allow the MOVETYPE_NONE to be set without the
  1725. // assertion triggering. Why do we bother setting the MOVETYPE to none here?
  1726. RemoveEffects( EF_BONEMERGE );
  1727. SetMoveType(MOVETYPE_NONE);
  1728. // If we have a parent, unlink from it.
  1729. UnlinkFromParent( this );
  1730. // Any children still connected are orphans, mark all for delete
  1731. CUtlVector<CBaseEntity *> childrenList;
  1732. GetAllChildren( this, childrenList );
  1733. if ( childrenList.Count() )
  1734. {
  1735. DevMsg( 2, "Warning: Deleting orphaned children of %s\n", GetClassname() );
  1736. for ( int i = childrenList.Count()-1; i >= 0; --i )
  1737. {
  1738. UTIL_Remove( childrenList[i] );
  1739. }
  1740. }
  1741. SetGroundEntity( NULL );
  1742. if ( m_bDynamicModelPending )
  1743. {
  1744. sg_DynamicLoadHandlers.Remove( this );
  1745. }
  1746. if ( IsDynamicModelIndex( m_nModelIndex ) )
  1747. {
  1748. modelinfo->ReleaseDynamicModel( m_nModelIndex ); // no-op if not dynamic
  1749. m_nModelIndex = -1;
  1750. }
  1751. }
  1752. //-----------------------------------------------------------------------------
  1753. // capabilities
  1754. //-----------------------------------------------------------------------------
  1755. int CBaseEntity::ObjectCaps( void )
  1756. {
  1757. #if 1
  1758. model_t *pModel = GetModel();
  1759. bool bIsBrush = ( pModel && modelinfo->GetModelType( pModel ) == mod_brush );
  1760. // We inherit our parent's use capabilities so that we can forward use commands
  1761. // to our parent.
  1762. CBaseEntity *pParent = GetParent();
  1763. if ( pParent )
  1764. {
  1765. int caps = pParent->ObjectCaps();
  1766. if ( !bIsBrush )
  1767. caps &= ( FCAP_ACROSS_TRANSITION | FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE | FCAP_DIRECTIONAL_USE );
  1768. else
  1769. caps &= ( FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE | FCAP_DIRECTIONAL_USE );
  1770. if ( pParent->IsPlayer() )
  1771. caps |= FCAP_ACROSS_TRANSITION;
  1772. return caps;
  1773. }
  1774. else if ( !bIsBrush )
  1775. {
  1776. return FCAP_ACROSS_TRANSITION;
  1777. }
  1778. return 0;
  1779. #else
  1780. // We inherit our parent's use capabilities so that we can forward use commands
  1781. // to our parent.
  1782. int parentCaps = 0;
  1783. if (GetParent())
  1784. {
  1785. parentCaps = GetParent()->ObjectCaps();
  1786. parentCaps &= ( FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE | FCAP_DIRECTIONAL_USE );
  1787. }
  1788. model_t *pModel = GetModel();
  1789. if ( pModel && modelinfo->GetModelType( pModel ) == mod_brush )
  1790. return parentCaps;
  1791. return FCAP_ACROSS_TRANSITION | parentCaps;
  1792. #endif
  1793. }
  1794. void CBaseEntity::StartTouch( CBaseEntity *pOther )
  1795. {
  1796. // notify parent
  1797. if ( m_pParent != NULL )
  1798. m_pParent->StartTouch( pOther );
  1799. }
  1800. void CBaseEntity::Touch( CBaseEntity *pOther )
  1801. {
  1802. if ( m_pfnTouch )
  1803. (this->*m_pfnTouch)( pOther );
  1804. // notify parent of touch
  1805. if ( m_pParent != NULL )
  1806. m_pParent->Touch( pOther );
  1807. }
  1808. void CBaseEntity::EndTouch( CBaseEntity *pOther )
  1809. {
  1810. // notify parent
  1811. if ( m_pParent != NULL )
  1812. {
  1813. m_pParent->EndTouch( pOther );
  1814. }
  1815. }
  1816. //-----------------------------------------------------------------------------
  1817. // Purpose: Dispatches blocked events to this entity's blocked handler, set via SetBlocked.
  1818. // Input : pOther - The entity that is blocking us.
  1819. //-----------------------------------------------------------------------------
  1820. void CBaseEntity::Blocked( CBaseEntity *pOther )
  1821. {
  1822. if ( m_pfnBlocked )
  1823. {
  1824. (this->*m_pfnBlocked)( pOther );
  1825. }
  1826. //
  1827. // Forward the blocked event to our parent, if any.
  1828. //
  1829. if ( m_pParent != NULL )
  1830. {
  1831. m_pParent->Blocked( pOther );
  1832. }
  1833. }
  1834. //-----------------------------------------------------------------------------
  1835. // Purpose: Dispatches use events to this entity's use handler, set via SetUse.
  1836. // Input : pActivator -
  1837. // pCaller -
  1838. // useType -
  1839. // value -
  1840. //-----------------------------------------------------------------------------
  1841. void CBaseEntity::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1842. {
  1843. if ( m_pfnUse != NULL )
  1844. {
  1845. (this->*m_pfnUse)( pActivator, pCaller, useType, value );
  1846. }
  1847. else
  1848. {
  1849. //
  1850. // We don't handle use events. Forward to our parent, if any.
  1851. //
  1852. if ( m_pParent != NULL )
  1853. {
  1854. m_pParent->Use( pActivator, pCaller, useType, value );
  1855. }
  1856. }
  1857. }
  1858. static CBaseEntity *FindPhysicsBlocker( IPhysicsObject *pPhysics, physicspushlist_t &list, const Vector &pushVel )
  1859. {
  1860. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  1861. CBaseEntity *pBlocker = NULL;
  1862. float maxForce = 0;
  1863. while ( pSnapshot->IsValid() )
  1864. {
  1865. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  1866. CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
  1867. bool inList = false;
  1868. for ( int i = 0; i < list.pushedCount; i++ )
  1869. {
  1870. if ( pOtherEntity == list.pushedEnts[i] )
  1871. {
  1872. inList = true;
  1873. break;
  1874. }
  1875. }
  1876. Vector normal;
  1877. pSnapshot->GetSurfaceNormal(normal);
  1878. float dot = DotProduct( pushVel, pSnapshot->GetNormalForce() * normal );
  1879. if ( !pBlocker || (!inList && dot > maxForce) )
  1880. {
  1881. pBlocker = pOtherEntity;
  1882. if ( !inList )
  1883. {
  1884. maxForce = dot;
  1885. }
  1886. }
  1887. pSnapshot->NextFrictionData();
  1888. }
  1889. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  1890. return pBlocker;
  1891. }
  1892. struct pushblock_t
  1893. {
  1894. physicspushlist_t *pList;
  1895. CBaseEntity *pRootParent;
  1896. CBaseEntity *pBlockedEntity;
  1897. float moveBackFraction;
  1898. float movetime;
  1899. };
  1900. static void ComputePushStartMatrix( matrix3x4_t &start, CBaseEntity *pEntity, const pushblock_t &params )
  1901. {
  1902. Vector localOrigin;
  1903. QAngle localAngles;
  1904. if ( params.pList )
  1905. {
  1906. localOrigin = params.pList->localOrigin;
  1907. localAngles = params.pList->localAngles;
  1908. }
  1909. else
  1910. {
  1911. localOrigin = params.pRootParent->GetAbsOrigin() - params.pRootParent->GetAbsVelocity() * params.movetime;
  1912. localAngles = params.pRootParent->GetAbsAngles() - params.pRootParent->GetLocalAngularVelocity() * params.movetime;
  1913. }
  1914. matrix3x4_t xform, delta;
  1915. AngleMatrix( localAngles, localOrigin, xform );
  1916. matrix3x4_t srcInv;
  1917. // xform = src(-1) * dest
  1918. MatrixInvert( params.pRootParent->EntityToWorldTransform(), srcInv );
  1919. ConcatTransforms( xform, srcInv, delta );
  1920. ConcatTransforms( delta, pEntity->EntityToWorldTransform(), start );
  1921. }
  1922. #define DEBUG_PUSH_MESSAGES 0
  1923. static void CheckPushedEntity( CBaseEntity *pEntity, pushblock_t &params )
  1924. {
  1925. IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
  1926. if ( !pPhysics )
  1927. return;
  1928. // somehow we've got a static or motion disabled physics object in hierarchy!
  1929. // This is not allowed! Don't test blocking in that case.
  1930. Assert(pPhysics->IsMoveable());
  1931. if ( !pPhysics->IsMoveable() || !pPhysics->GetShadowController() )
  1932. {
  1933. #if DEBUG_PUSH_MESSAGES
  1934. Msg("Blocking %s, not moveable!\n", pEntity->GetClassname());
  1935. #endif
  1936. return;
  1937. }
  1938. bool checkrot = true;
  1939. bool checkmove = true;
  1940. Vector origin;
  1941. QAngle angles;
  1942. pPhysics->GetShadowPosition( &origin, &angles );
  1943. float fraction = -1.0f;
  1944. matrix3x4_t parentDelta;
  1945. if ( pEntity == params.pRootParent )
  1946. {
  1947. if ( pEntity->GetLocalAngularVelocity() == vec3_angle )
  1948. checkrot = false;
  1949. if ( pEntity->GetLocalVelocity() == vec3_origin)
  1950. checkmove = false;
  1951. }
  1952. else
  1953. {
  1954. #if DEBUG_PUSH_MESSAGES
  1955. if ( pPhysics->IsAttachedToConstraint(false))
  1956. {
  1957. Msg("Warning, hierarchical entity is attached to a constraint %s\n", pEntity->GetClassname());
  1958. }
  1959. #endif
  1960. }
  1961. if ( checkmove )
  1962. {
  1963. // project error onto the axis of movement
  1964. Vector dir = pEntity->GetAbsVelocity();
  1965. float speed = VectorNormalize(dir);
  1966. Vector targetPos;
  1967. pPhysics->GetShadowController()->GetTargetPosition( &targetPos, NULL );
  1968. float targetAmount = DotProduct(targetPos, dir);
  1969. float currentAmount = DotProduct(origin, dir);
  1970. float entityAmount = DotProduct(pEntity->GetAbsOrigin(), dir);
  1971. // if target and entity origin are not in sync, then the position of the entity was updated
  1972. // by something outside of push physics
  1973. if ( (targetAmount - entityAmount) > 1 )
  1974. {
  1975. pEntity->UpdatePhysicsShadowToCurrentPosition(0);
  1976. #if DEBUG_PUSH_MESSAGES
  1977. Warning("Someone slammed the position of a %s\n", pEntity->GetClassname() );
  1978. #endif
  1979. }
  1980. else
  1981. {
  1982. float dist = targetAmount - currentAmount;
  1983. if ( dist > 1 )
  1984. {
  1985. #if DEBUG_PUSH_MESSAGES
  1986. const char *pName = pEntity->GetClassname();
  1987. Msg( "%s blocked by %.2f units\n", pName, dist );
  1988. #endif
  1989. float movementAmount = targetAmount - (speed * params.movetime);
  1990. if ( pEntity == params.pRootParent )
  1991. {
  1992. if ( params.pList )
  1993. {
  1994. Vector localVel = pEntity->GetLocalVelocity();
  1995. VectorNormalize(localVel);
  1996. float localTargetAmt = DotProduct(pEntity->GetLocalOrigin(), localVel);
  1997. movementAmount = targetAmount + DotProduct(params.pList->localOrigin, localVel) - localTargetAmt;
  1998. }
  1999. }
  2000. else
  2001. {
  2002. matrix3x4_t start;
  2003. ComputePushStartMatrix( start, pEntity, params );
  2004. Vector startPos;
  2005. MatrixPosition( start, startPos );
  2006. movementAmount = DotProduct(startPos, dir);
  2007. }
  2008. float expectedDist = targetAmount - movementAmount;
  2009. // compute the fraction to move back the AI to match the physics
  2010. if ( expectedDist <= 0 )
  2011. {
  2012. fraction = 1;
  2013. }
  2014. else
  2015. {
  2016. fraction = dist / expectedDist;
  2017. fraction = clamp(fraction, 0.f, 1.f);
  2018. }
  2019. }
  2020. }
  2021. }
  2022. if ( checkrot )
  2023. {
  2024. Vector axis;
  2025. float deltaAngle;
  2026. RotationDeltaAxisAngle( angles, pEntity->GetAbsAngles(), axis, deltaAngle );
  2027. if ( fabsf(deltaAngle) > 0.5f )
  2028. {
  2029. Vector targetAxis;
  2030. QAngle targetRot;
  2031. float deltaTargetAngle;
  2032. pPhysics->GetShadowController()->GetTargetPosition( NULL, &targetRot );
  2033. RotationDeltaAxisAngle( angles, targetRot, targetAxis, deltaTargetAngle );
  2034. if ( fabsf(deltaTargetAngle) > 0.01f )
  2035. {
  2036. float expectedDist = deltaAngle;
  2037. #if DEBUG_PUSH_MESSAGES
  2038. const char *pName = pEntity->GetClassname();
  2039. Msg( "%s blocked by %.2f degrees\n", pName, deltaAngle );
  2040. if ( pPhysics->IsAsleep() )
  2041. {
  2042. Msg("Asleep while blocked?\n");
  2043. }
  2044. if ( pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING )
  2045. {
  2046. Msg("Blocking for penetration!\n");
  2047. }
  2048. #endif
  2049. if ( pEntity == params.pRootParent )
  2050. {
  2051. expectedDist = pEntity->GetLocalAngularVelocity().Length() * params.movetime;
  2052. }
  2053. else
  2054. {
  2055. matrix3x4_t start;
  2056. ComputePushStartMatrix( start, pEntity, params );
  2057. Vector startAxis;
  2058. float startAngle;
  2059. Vector startPos;
  2060. QAngle startAngles;
  2061. MatrixAngles( start, startAngles, startPos );
  2062. RotationDeltaAxisAngle( startAngles, pEntity->GetAbsAngles(), startAxis, startAngle );
  2063. expectedDist = startAngle * DotProduct( startAxis, axis );
  2064. }
  2065. float t = expectedDist != 0.0f ? fabsf(deltaAngle / expectedDist) : 1.0f;
  2066. t = clamp(t,0.f,1.f);
  2067. fraction = MAX(fraction, t);
  2068. }
  2069. else
  2070. {
  2071. pEntity->UpdatePhysicsShadowToCurrentPosition(0);
  2072. #if DEBUG_PUSH_MESSAGES
  2073. Warning("Someone slammed the position of a %s\n", pEntity->GetClassname() );
  2074. #endif
  2075. }
  2076. }
  2077. }
  2078. if ( fraction >= params.moveBackFraction )
  2079. {
  2080. params.moveBackFraction = fraction;
  2081. params.pBlockedEntity = pEntity;
  2082. }
  2083. }
  2084. void CBaseEntity::VPhysicsUpdatePusher( IPhysicsObject *pPhysics )
  2085. {
  2086. float movetime = m_flLocalTime - m_flVPhysicsUpdateLocalTime;
  2087. if (movetime <= 0)
  2088. return;
  2089. // only reconcile pushers on the final vphysics tick
  2090. if ( !PhysIsFinalTick() )
  2091. return;
  2092. Vector origin;
  2093. QAngle angles;
  2094. // physics updated the shadow, so check to see if I got blocked
  2095. // NOTE: SOLID_BSP cannont compute consistent collisions wrt vphysics, so
  2096. // don't allow vphysics to block. Assume game physics has handled it.
  2097. if ( GetSolid() != SOLID_BSP && pPhysics->GetShadowPosition( &origin, &angles ) )
  2098. {
  2099. CUtlVector<CBaseEntity *> list;
  2100. GetAllInHierarchy( this, list );
  2101. //NDebugOverlay::BoxAngles( origin, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), angles, 255,0,0,0, gpGlobals->frametime);
  2102. physicspushlist_t *pList = NULL;
  2103. if ( HasDataObjectType(PHYSICSPUSHLIST) )
  2104. {
  2105. pList = (physicspushlist_t *)GetDataObject( PHYSICSPUSHLIST );
  2106. Assert(pList);
  2107. }
  2108. bool checkrot = (GetLocalAngularVelocity() != vec3_angle) ? true : false;
  2109. bool checkmove = (GetLocalVelocity() != vec3_origin) ? true : false;
  2110. pushblock_t params;
  2111. params.pRootParent = this;
  2112. params.pList = pList;
  2113. params.pBlockedEntity = NULL;
  2114. params.moveBackFraction = 0.0f;
  2115. params.movetime = movetime;
  2116. for ( int i = 0; i < list.Count(); i++ )
  2117. {
  2118. if ( list[i]->IsSolid() )
  2119. {
  2120. CheckPushedEntity( list[i], params );
  2121. }
  2122. }
  2123. float physLocalTime = m_flLocalTime;
  2124. if ( params.pBlockedEntity )
  2125. {
  2126. float moveback = movetime * params.moveBackFraction;
  2127. if ( moveback > 0 )
  2128. {
  2129. physLocalTime = m_flLocalTime - moveback;
  2130. // add 1% noise for bouncing in collision.
  2131. if ( physLocalTime <= (m_flVPhysicsUpdateLocalTime + movetime * 0.99f) )
  2132. {
  2133. CBaseEntity *pBlocked = NULL;
  2134. IPhysicsObject *pOther;
  2135. if ( params.pBlockedEntity->VPhysicsGetObject()->GetContactPoint( NULL, &pOther ) )
  2136. {
  2137. pBlocked = static_cast<CBaseEntity *>(pOther->GetGameData());
  2138. }
  2139. // UNDONE: Need to traverse hierarchy here? Shouldn't.
  2140. if ( pList )
  2141. {
  2142. SetLocalOrigin( pList->localOrigin );
  2143. SetLocalAngles( pList->localAngles );
  2144. physLocalTime = pList->localMoveTime;
  2145. for ( int i = 0; i < pList->pushedCount; i++ )
  2146. {
  2147. CBaseEntity *pEntity = pList->pushedEnts[i];
  2148. if ( !pEntity )
  2149. continue;
  2150. pEntity->SetAbsOrigin( pEntity->GetAbsOrigin() - pList->pushVec[i] );
  2151. }
  2152. CBaseEntity *pPhysicsBlocker = FindPhysicsBlocker( VPhysicsGetObject(), *pList, pList->pushVec[0] );
  2153. if ( pPhysicsBlocker )
  2154. {
  2155. pBlocked = pPhysicsBlocker;
  2156. }
  2157. }
  2158. else
  2159. {
  2160. Vector origin = GetLocalOrigin();
  2161. QAngle angles = GetLocalAngles();
  2162. if ( checkmove )
  2163. {
  2164. origin -= GetLocalVelocity() * moveback;
  2165. }
  2166. if ( checkrot )
  2167. {
  2168. // BUGBUG: This is pretty hack-tastic!
  2169. angles -= GetLocalAngularVelocity() * moveback;
  2170. }
  2171. SetLocalOrigin( origin );
  2172. SetLocalAngles( angles );
  2173. }
  2174. if ( pBlocked )
  2175. {
  2176. Blocked( pBlocked );
  2177. }
  2178. m_flLocalTime = physLocalTime;
  2179. }
  2180. }
  2181. }
  2182. }
  2183. // this data is no longer useful, free the memory
  2184. if ( HasDataObjectType(PHYSICSPUSHLIST) )
  2185. {
  2186. DestroyDataObject( PHYSICSPUSHLIST );
  2187. }
  2188. m_flVPhysicsUpdateLocalTime = m_flLocalTime;
  2189. if ( m_flMoveDoneTime <= m_flLocalTime && m_flMoveDoneTime > 0 )
  2190. {
  2191. SetMoveDoneTime( -1 );
  2192. MoveDone();
  2193. }
  2194. }
  2195. void CBaseEntity::SetMoveDoneTime( float flDelay )
  2196. {
  2197. if (flDelay >= 0)
  2198. {
  2199. m_flMoveDoneTime = GetLocalTime() + flDelay;
  2200. }
  2201. else
  2202. {
  2203. m_flMoveDoneTime = -1;
  2204. }
  2205. CheckHasGamePhysicsSimulation();
  2206. }
  2207. //-----------------------------------------------------------------------------
  2208. // Purpose: Relinks all of a parents children into the collision tree
  2209. //-----------------------------------------------------------------------------
  2210. void CBaseEntity::PhysicsRelinkChildren( float dt )
  2211. {
  2212. CBaseEntity *child;
  2213. // iterate through all children
  2214. for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
  2215. {
  2216. if ( child->IsSolid() || child->IsSolidFlagSet(FSOLID_TRIGGER) )
  2217. {
  2218. child->PhysicsTouchTriggers();
  2219. }
  2220. //
  2221. // Update their physics shadows. We should never have any children of
  2222. // movetype VPHYSICS.
  2223. //
  2224. if ( child->GetMoveType() != MOVETYPE_VPHYSICS )
  2225. {
  2226. child->UpdatePhysicsShadowToCurrentPosition( dt );
  2227. }
  2228. else if ( child->GetOwnerEntity() != this )
  2229. {
  2230. // the only case where this is valid is if this entity is an attached ragdoll.
  2231. // So assert here to catch the non-ragdoll case.
  2232. Assert( 0 );
  2233. }
  2234. if ( child->FirstMoveChild() )
  2235. {
  2236. child->PhysicsRelinkChildren(dt);
  2237. }
  2238. }
  2239. }
  2240. void CBaseEntity::PhysicsTouchTriggers( const Vector *pPrevAbsOrigin )
  2241. {
  2242. edict_t *pEdict = edict();
  2243. if ( pEdict && !IsWorld() )
  2244. {
  2245. Assert(CollisionProp());
  2246. bool isTriggerCheckSolids = IsSolidFlagSet( FSOLID_TRIGGER );
  2247. bool isSolidCheckTriggers = IsSolid() && !isTriggerCheckSolids; // NOTE: Moving triggers (items, ammo etc) are not
  2248. // checked against other triggers to reduce the number of touchlinks created
  2249. if ( !(isSolidCheckTriggers || isTriggerCheckSolids) )
  2250. return;
  2251. if ( GetSolid() == SOLID_BSP )
  2252. {
  2253. if ( !GetModel() && Q_strlen( STRING( GetModelName() ) ) == 0 )
  2254. {
  2255. Warning( "Inserted %s with no model\n", GetClassname() );
  2256. return;
  2257. }
  2258. }
  2259. SetCheckUntouch( true );
  2260. if ( isSolidCheckTriggers )
  2261. {
  2262. engine->SolidMoved( pEdict, CollisionProp(), pPrevAbsOrigin, sm_bAccurateTriggerBboxChecks );
  2263. }
  2264. if ( isTriggerCheckSolids )
  2265. {
  2266. engine->TriggerMoved( pEdict, sm_bAccurateTriggerBboxChecks );
  2267. }
  2268. }
  2269. }
  2270. void CBaseEntity::VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent )
  2271. {
  2272. }
  2273. void CBaseEntity::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  2274. {
  2275. // filter out ragdoll props hitting other parts of itself too often
  2276. // UNDONE: Store a sound time for this entity (not just this pair of objects)
  2277. // and filter repeats on that?
  2278. int otherIndex = !index;
  2279. CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
  2280. // Don't make sounds / effects if neither entity is MOVETYPE_VPHYSICS. The game
  2281. // physics should have done so.
  2282. if ( GetMoveType() != MOVETYPE_VPHYSICS && pHitEntity->GetMoveType() != MOVETYPE_VPHYSICS )
  2283. return;
  2284. if ( pEvent->deltaCollisionTime < 0.5 && (pHitEntity == this) )
  2285. return;
  2286. // don't make noise for hidden/invisible/sky materials
  2287. surfacedata_t *phit = physprops->GetSurfaceData( pEvent->surfaceProps[otherIndex] );
  2288. const surfacedata_t *pprops = physprops->GetSurfaceData( pEvent->surfaceProps[index] );
  2289. if ( phit->game.material == 'X' || pprops->game.material == 'X' )
  2290. return;
  2291. if ( pHitEntity == this )
  2292. {
  2293. PhysCollisionSound( this, pEvent->pObjects[index], CHAN_BODY, pEvent->surfaceProps[index], pEvent->surfaceProps[otherIndex], pEvent->deltaCollisionTime, pEvent->collisionSpeed );
  2294. }
  2295. else
  2296. {
  2297. PhysCollisionSound( this, pEvent->pObjects[index], CHAN_STATIC, pEvent->surfaceProps[index], pEvent->surfaceProps[otherIndex], pEvent->deltaCollisionTime, pEvent->collisionSpeed );
  2298. }
  2299. PhysCollisionScreenShake( pEvent, index );
  2300. #if HL2_EPISODIC
  2301. // episodic does something different for when advisor shields are struck
  2302. if ( phit->game.material == 'Z' || pprops->game.material == 'Z')
  2303. {
  2304. PhysCollisionWarpEffect( pEvent, phit );
  2305. }
  2306. else
  2307. {
  2308. PhysCollisionDust( pEvent, phit );
  2309. }
  2310. #else
  2311. PhysCollisionDust( pEvent, phit );
  2312. #endif
  2313. }
  2314. void CBaseEntity::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
  2315. {
  2316. PhysFrictionSound( this, pObject, energy, surfaceProps, surfacePropsHit );
  2317. }
  2318. void CBaseEntity::VPhysicsSwapObject( IPhysicsObject *pSwap )
  2319. {
  2320. if ( !pSwap )
  2321. {
  2322. PhysRemoveShadow(this);
  2323. }
  2324. if ( !m_pPhysicsObject )
  2325. {
  2326. Warning( "Bad vphysics swap for %s\n", STRING(m_iClassname) );
  2327. }
  2328. m_pPhysicsObject = pSwap;
  2329. }
  2330. // Tells the physics shadow to update it's target to the current position
  2331. void CBaseEntity::UpdatePhysicsShadowToCurrentPosition( float deltaTime )
  2332. {
  2333. if ( GetMoveType() != MOVETYPE_VPHYSICS )
  2334. {
  2335. IPhysicsObject *pPhys = VPhysicsGetObject();
  2336. if ( pPhys )
  2337. {
  2338. pPhys->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, deltaTime );
  2339. }
  2340. }
  2341. }
  2342. int CBaseEntity::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
  2343. {
  2344. IPhysicsObject *pPhys = VPhysicsGetObject();
  2345. if ( pPhys )
  2346. {
  2347. // multi-object entities must implement this function
  2348. Assert( !(pPhys->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY) );
  2349. if ( listMax > 0 )
  2350. {
  2351. pList[0] = pPhys;
  2352. return 1;
  2353. }
  2354. }
  2355. return 0;
  2356. }
  2357. //-----------------------------------------------------------------------------
  2358. //-----------------------------------------------------------------------------
  2359. bool CBaseEntity::VPhysicsIsFlesh( void )
  2360. {
  2361. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  2362. int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  2363. for ( int i = 0; i < count; i++ )
  2364. {
  2365. int material = pList[i]->GetMaterialIndex();
  2366. const surfacedata_t *pSurfaceData = physprops->GetSurfaceData( material );
  2367. // Is flesh ?, don't allow pickup
  2368. if ( pSurfaceData->game.material == CHAR_TEX_ANTLION || pSurfaceData->game.material == CHAR_TEX_FLESH || pSurfaceData->game.material == CHAR_TEX_BLOODYFLESH || pSurfaceData->game.material == CHAR_TEX_ALIENFLESH )
  2369. return true;
  2370. }
  2371. return false;
  2372. }
  2373. bool CBaseEntity::Intersects( CBaseEntity *pOther )
  2374. {
  2375. if ( !edict() || !pOther->edict() )
  2376. return false;
  2377. CCollisionProperty *pMyProp = CollisionProp();
  2378. CCollisionProperty *pOtherProp = pOther->CollisionProp();
  2379. return IsOBBIntersectingOBB(
  2380. pMyProp->GetCollisionOrigin(), pMyProp->GetCollisionAngles(), pMyProp->OBBMins(), pMyProp->OBBMaxs(),
  2381. pOtherProp->GetCollisionOrigin(), pOtherProp->GetCollisionAngles(), pOtherProp->OBBMins(), pOtherProp->OBBMaxs() );
  2382. }
  2383. extern ConVar ai_LOS_mode;
  2384. //=========================================================
  2385. // FVisible - returns true if a line can be traced from
  2386. // the caller's eyes to the target
  2387. //=========================================================
  2388. bool CBaseEntity::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  2389. {
  2390. VPROF( "CBaseEntity::FVisible" );
  2391. if ( pEntity->GetFlags() & FL_NOTARGET )
  2392. return false;
  2393. #if HL1_DLL
  2394. // FIXME: only block LOS through opaque water
  2395. // don't look through water
  2396. if ((m_nWaterLevel != 3 && pEntity->m_nWaterLevel == 3)
  2397. || (m_nWaterLevel == 3 && pEntity->m_nWaterLevel == 0))
  2398. return false;
  2399. #endif
  2400. Vector vecLookerOrigin = EyePosition();//look through the caller's 'eyes'
  2401. Vector vecTargetOrigin = pEntity->EyePosition();
  2402. trace_t tr;
  2403. if ( !IsXbox() && ai_LOS_mode.GetBool() )
  2404. {
  2405. UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, traceMask, this, COLLISION_GROUP_NONE, &tr);
  2406. }
  2407. else
  2408. {
  2409. // If we're doing an LOS search, include NPCs.
  2410. if ( traceMask == MASK_BLOCKLOS )
  2411. {
  2412. traceMask = MASK_BLOCKLOS_AND_NPCS;
  2413. }
  2414. // Player sees through nodraw
  2415. if ( IsPlayer() )
  2416. {
  2417. traceMask &= ~CONTENTS_BLOCKLOS;
  2418. }
  2419. // Use the custom LOS trace filter
  2420. CTraceFilterLOS traceFilter( this, COLLISION_GROUP_NONE, pEntity );
  2421. UTIL_TraceLine( vecLookerOrigin, vecTargetOrigin, traceMask, &traceFilter, &tr );
  2422. }
  2423. if (tr.fraction != 1.0 || tr.startsolid )
  2424. {
  2425. // If we hit the entity we're looking for, it's visible
  2426. if ( tr.m_pEnt == pEntity )
  2427. return true;
  2428. // Got line of sight on the vehicle the player is driving!
  2429. if ( pEntity && pEntity->IsPlayer() )
  2430. {
  2431. CBasePlayer *pPlayer = assert_cast<CBasePlayer*>( pEntity );
  2432. if ( tr.m_pEnt == pPlayer->GetVehicleEntity() )
  2433. return true;
  2434. }
  2435. if (ppBlocker)
  2436. {
  2437. *ppBlocker = tr.m_pEnt;
  2438. }
  2439. return false;// Line of sight is not established
  2440. }
  2441. return true;// line of sight is valid.
  2442. }
  2443. //=========================================================
  2444. // FVisible - returns true if a line can be traced from
  2445. // the caller's eyes to the wished position.
  2446. //=========================================================
  2447. bool CBaseEntity::FVisible( const Vector &vecTarget, int traceMask, CBaseEntity **ppBlocker )
  2448. {
  2449. #if HL1_DLL
  2450. // don't look through water
  2451. // FIXME: only block LOS through opaque water
  2452. bool inWater = ( UTIL_PointContents( vecTarget ) & (CONTENTS_SLIME|CONTENTS_WATER) ) ? true : false;
  2453. // Don't allow it if we're straddling two areas
  2454. if ( ( m_nWaterLevel == 3 && !inWater ) || ( m_nWaterLevel != 3 && inWater ) )
  2455. return false;
  2456. #endif
  2457. trace_t tr;
  2458. Vector vecLookerOrigin = EyePosition();// look through the caller's 'eyes'
  2459. if ( ai_LOS_mode.GetBool() )
  2460. {
  2461. UTIL_TraceLine( vecLookerOrigin, vecTarget, traceMask, this, COLLISION_GROUP_NONE, &tr);
  2462. }
  2463. else
  2464. {
  2465. // If we're doing an LOS search, include NPCs.
  2466. if ( traceMask == MASK_BLOCKLOS )
  2467. {
  2468. traceMask = MASK_BLOCKLOS_AND_NPCS;
  2469. }
  2470. // Player sees through nodraw and blocklos
  2471. if ( IsPlayer() )
  2472. {
  2473. traceMask |= CONTENTS_IGNORE_NODRAW_OPAQUE;
  2474. traceMask &= ~CONTENTS_BLOCKLOS;
  2475. }
  2476. // Use the custom LOS trace filter
  2477. CTraceFilterLOS traceFilter( this, COLLISION_GROUP_NONE );
  2478. UTIL_TraceLine( vecLookerOrigin, vecTarget, traceMask, &traceFilter, &tr );
  2479. }
  2480. if (tr.fraction != 1.0)
  2481. {
  2482. if (ppBlocker)
  2483. {
  2484. *ppBlocker = tr.m_pEnt;
  2485. }
  2486. return false;// Line of sight is not established
  2487. }
  2488. return true;// line of sight is valid.
  2489. }
  2490. extern ConVar ai_debug_los;
  2491. //-----------------------------------------------------------------------------
  2492. // Purpose: Turn on prop LOS debugging mode
  2493. //-----------------------------------------------------------------------------
  2494. void CC_AI_LOS_Debug( IConVar *var, const char *pOldString, float flOldValue )
  2495. {
  2496. int iLOSMode = ai_debug_los.GetInt();
  2497. for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  2498. {
  2499. if ( iLOSMode == 1 && pEntity->IsSolid() )
  2500. {
  2501. pEntity->m_debugOverlays |= OVERLAY_SHOW_BLOCKSLOS;
  2502. }
  2503. else if ( iLOSMode == 2 )
  2504. {
  2505. pEntity->m_debugOverlays |= OVERLAY_SHOW_BLOCKSLOS;
  2506. }
  2507. else
  2508. {
  2509. pEntity->m_debugOverlays &= ~OVERLAY_SHOW_BLOCKSLOS;
  2510. }
  2511. }
  2512. }
  2513. ConVar ai_debug_los("ai_debug_los", "0", FCVAR_CHEAT, "NPC Line-Of-Sight debug mode. If 1, solid entities that block NPC LOC will be highlighted with white bounding boxes. If 2, it'll show non-solid entities that would do it if they were solid.", CC_AI_LOS_Debug );
  2514. Class_T CBaseEntity::Classify ( void )
  2515. {
  2516. return CLASS_NONE;
  2517. }
  2518. float CBaseEntity::GetAutoAimRadius()
  2519. {
  2520. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  2521. return 48.0f;
  2522. else
  2523. return 24.0f;
  2524. }
  2525. //-----------------------------------------------------------------------------
  2526. // Changes the shadow cast distance over time
  2527. //-----------------------------------------------------------------------------
  2528. void CBaseEntity::ShadowCastDistThink( )
  2529. {
  2530. SetShadowCastDistance( m_flDesiredShadowCastDistance );
  2531. SetContextThink( NULL, gpGlobals->curtime, "ShadowCastDistThink" );
  2532. }
  2533. void CBaseEntity::SetShadowCastDistance( float flDesiredDistance, float flDelay )
  2534. {
  2535. m_flDesiredShadowCastDistance = flDesiredDistance;
  2536. if ( m_flDesiredShadowCastDistance != m_flShadowCastDistance )
  2537. {
  2538. SetContextThink( &CBaseEntity::ShadowCastDistThink, gpGlobals->curtime + flDelay, "ShadowCastDistThink" );
  2539. }
  2540. }
  2541. /*
  2542. ================
  2543. TraceAttack
  2544. ================
  2545. */
  2546. //-----------------------------------------------------------------------------
  2547. // Purpose: Returns whether a damage info can damage this entity.
  2548. //-----------------------------------------------------------------------------
  2549. bool CBaseEntity::PassesDamageFilter( const CTakeDamageInfo &info )
  2550. {
  2551. if (m_hDamageFilter)
  2552. {
  2553. CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get());
  2554. return pFilter->PassesDamageFilter(info);
  2555. }
  2556. return true;
  2557. }
  2558. FORCEINLINE bool NamesMatch( const char *pszQuery, string_t nameToMatch )
  2559. {
  2560. if ( nameToMatch == NULL_STRING )
  2561. return (!pszQuery || *pszQuery == 0 || *pszQuery == '*');
  2562. const char *pszNameToMatch = STRING(nameToMatch);
  2563. // If the pointers are identical, we're identical
  2564. if ( pszNameToMatch == pszQuery )
  2565. return true;
  2566. while ( *pszNameToMatch && *pszQuery )
  2567. {
  2568. unsigned char cName = *pszNameToMatch;
  2569. unsigned char cQuery = *pszQuery;
  2570. // simple ascii case conversion
  2571. if ( cName == cQuery )
  2572. ;
  2573. else if ( cName - 'A' <= (unsigned char)'Z' - 'A' && cName - 'A' + 'a' == cQuery )
  2574. ;
  2575. else if ( cName - 'a' <= (unsigned char)'z' - 'a' && cName - 'a' + 'A' == cQuery )
  2576. ;
  2577. else
  2578. break;
  2579. ++pszNameToMatch;
  2580. ++pszQuery;
  2581. }
  2582. if ( *pszQuery == 0 && *pszNameToMatch == 0 )
  2583. return true;
  2584. // @TODO (toml 03-18-03): Perhaps support real wildcards. Right now, only thing supported is trailing *
  2585. if ( *pszQuery == '*' )
  2586. return true;
  2587. return false;
  2588. }
  2589. bool CBaseEntity::NameMatchesComplex( const char *pszNameOrWildcard )
  2590. {
  2591. if ( !Q_stricmp( "!player", pszNameOrWildcard) )
  2592. return IsPlayer();
  2593. return NamesMatch( pszNameOrWildcard, m_iName );
  2594. }
  2595. bool CBaseEntity::ClassMatchesComplex( const char *pszClassOrWildcard )
  2596. {
  2597. return NamesMatch( pszClassOrWildcard, m_iClassname );
  2598. }
  2599. void CBaseEntity::MakeDormant( void )
  2600. {
  2601. AddEFlags( EFL_DORMANT );
  2602. // disable thinking for dormant entities
  2603. SetThink( NULL );
  2604. if ( !edict() )
  2605. return;
  2606. SETBITS( m_iEFlags, EFL_DORMANT );
  2607. // Don't touch
  2608. AddSolidFlags( FSOLID_NOT_SOLID );
  2609. // Don't move
  2610. SetMoveType( MOVETYPE_NONE );
  2611. // Don't draw
  2612. AddEffects( EF_NODRAW );
  2613. // Don't think
  2614. SetNextThink( TICK_NEVER_THINK );
  2615. }
  2616. int CBaseEntity::IsDormant( void )
  2617. {
  2618. return IsEFlagSet( EFL_DORMANT );
  2619. }
  2620. bool CBaseEntity::IsInWorld( void ) const
  2621. {
  2622. if ( !edict() )
  2623. return true;
  2624. // position
  2625. if (GetAbsOrigin().x >= MAX_COORD_INTEGER) return false;
  2626. if (GetAbsOrigin().y >= MAX_COORD_INTEGER) return false;
  2627. if (GetAbsOrigin().z >= MAX_COORD_INTEGER) return false;
  2628. if (GetAbsOrigin().x <= MIN_COORD_INTEGER) return false;
  2629. if (GetAbsOrigin().y <= MIN_COORD_INTEGER) return false;
  2630. if (GetAbsOrigin().z <= MIN_COORD_INTEGER) return false;
  2631. // speed
  2632. if (GetAbsVelocity().x >= 2000) return false;
  2633. if (GetAbsVelocity().y >= 2000) return false;
  2634. if (GetAbsVelocity().z >= 2000) return false;
  2635. if (GetAbsVelocity().x <= -2000) return false;
  2636. if (GetAbsVelocity().y <= -2000) return false;
  2637. if (GetAbsVelocity().z <= -2000) return false;
  2638. return true;
  2639. }
  2640. bool CBaseEntity::IsViewable( void )
  2641. {
  2642. if ( IsEffectActive( EF_NODRAW ) )
  2643. {
  2644. return false;
  2645. }
  2646. if (IsBSPModel())
  2647. {
  2648. if (GetMoveType() != MOVETYPE_NONE)
  2649. {
  2650. return true;
  2651. }
  2652. }
  2653. else if (GetModelIndex() != 0)
  2654. {
  2655. // check for total transparency???
  2656. return true;
  2657. }
  2658. return false;
  2659. }
  2660. int CBaseEntity::ShouldToggle( USE_TYPE useType, int currentState )
  2661. {
  2662. if ( useType != USE_TOGGLE && useType != USE_SET )
  2663. {
  2664. if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) )
  2665. return 0;
  2666. }
  2667. return 1;
  2668. }
  2669. // NOTE: szName must be a pointer to constant memory, e.g. "NPC_class" because the entity
  2670. // will keep a pointer to it after this call.
  2671. CBaseEntity *CBaseEntity::Create( const char *szName, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
  2672. {
  2673. CBaseEntity *pEntity = CreateNoSpawn( szName, vecOrigin, vecAngles, pOwner );
  2674. DispatchSpawn( pEntity );
  2675. return pEntity;
  2676. }
  2677. // NOTE: szName must be a pointer to constant memory, e.g. "NPC_class" because the entity
  2678. // will keep a pointer to it after this call.
  2679. CBaseEntity * CBaseEntity::CreateNoSpawn( const char *szName, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
  2680. {
  2681. CBaseEntity *pEntity = CreateEntityByName( szName );
  2682. if ( !pEntity )
  2683. {
  2684. Assert( !"CreateNoSpawn: only works for CBaseEntities" );
  2685. return NULL;
  2686. }
  2687. pEntity->SetLocalOrigin( vecOrigin );
  2688. pEntity->SetLocalAngles( vecAngles );
  2689. pEntity->SetOwnerEntity( pOwner );
  2690. gEntList.NotifyCreateEntity( pEntity );
  2691. return pEntity;
  2692. }
  2693. Vector CBaseEntity::GetSoundEmissionOrigin() const
  2694. {
  2695. return WorldSpaceCenter();
  2696. }
  2697. //-----------------------------------------------------------------------------
  2698. // Purpose: Saves the current object out to disk, by iterating through the objects
  2699. // data description hierarchy
  2700. // Input : &save - save buffer which the class data is written to
  2701. // Output : int - 0 if the save failed, 1 on success
  2702. //-----------------------------------------------------------------------------
  2703. int CBaseEntity::Save( ISave &save )
  2704. {
  2705. // loop through the data description list, saving each data desc block
  2706. int status = SaveDataDescBlock( save, GetDataDescMap() );
  2707. return status;
  2708. }
  2709. //-----------------------------------------------------------------------------
  2710. // Purpose: Recursively saves all the classes in an object, in reverse order (top down)
  2711. // Output : int 0 on failure, 1 on success
  2712. //-----------------------------------------------------------------------------
  2713. int CBaseEntity::SaveDataDescBlock( ISave &save, datamap_t *dmap )
  2714. {
  2715. return save.WriteAll( this, dmap );
  2716. }
  2717. //-----------------------------------------------------------------------------
  2718. // Purpose: Restores the current object from disk, by iterating through the objects
  2719. // data description hierarchy
  2720. // Input : &restore - restore buffer which the class data is read from
  2721. // Output : int - 0 if the restore failed, 1 on success
  2722. //-----------------------------------------------------------------------------
  2723. int CBaseEntity::Restore( IRestore &restore )
  2724. {
  2725. // This is essential to getting the spatial partition info correct
  2726. CollisionProp()->DestroyPartitionHandle();
  2727. // loops through the data description list, restoring each data desc block in order
  2728. int status = RestoreDataDescBlock( restore, GetDataDescMap() );
  2729. // ---------------------------------------------------------------
  2730. // HACKHACK: We don't know the space of these vectors until now
  2731. // if they are worldspace, fix them up.
  2732. // ---------------------------------------------------------------
  2733. {
  2734. CGameSaveRestoreInfo *pGameInfo = restore.GetGameSaveRestoreInfo();
  2735. Vector parentSpaceOffset = pGameInfo->modelSpaceOffset;
  2736. if ( !GetParent() )
  2737. {
  2738. // parent is the world, so parent space is worldspace
  2739. // so update with the worldspace leveltransition transform
  2740. parentSpaceOffset += pGameInfo->GetLandmark();
  2741. }
  2742. // NOTE: Do *not* use GetAbsOrigin() here because it will
  2743. // try to recompute m_rgflCoordinateFrame!
  2744. MatrixSetColumn( m_vecAbsOrigin, 3, m_rgflCoordinateFrame );
  2745. m_vecOrigin += parentSpaceOffset;
  2746. }
  2747. // Gotta do this after the coordframe is set up as it depends on it.
  2748. // By definition, the surrounding bounds are dirty
  2749. // Also, twiddling with the flags here ensures it gets added to the KD tree dirty list
  2750. // (We don't want to use the saved version of this flag)
  2751. RemoveEFlags( EFL_DIRTY_SPATIAL_PARTITION );
  2752. CollisionProp()->MarkSurroundingBoundsDirty();
  2753. if ( edict() && GetModelIndex() != 0 && GetModelName() != NULL_STRING && restore.GetPrecacheMode() )
  2754. {
  2755. PrecacheModel( STRING( GetModelName() ) );
  2756. //Adrian: We should only need to do this after we precache. No point in setting the model again.
  2757. SetModelIndex( modelinfo->GetModelIndex( STRING(GetModelName() ) ) );
  2758. }
  2759. // Restablish ground entity
  2760. if ( m_hGroundEntity != NULL )
  2761. {
  2762. m_hGroundEntity->AddEntityToGroundList( this );
  2763. }
  2764. return status;
  2765. }
  2766. //-----------------------------------------------------------------------------
  2767. // handler to do stuff before you are saved
  2768. //-----------------------------------------------------------------------------
  2769. void CBaseEntity::OnSave( IEntitySaveUtils *pUtils )
  2770. {
  2771. // Here, we must force recomputation of all abs data so it gets saved correctly
  2772. // We can't leave the dirty bits set because the loader can't cope with it.
  2773. CalcAbsolutePosition();
  2774. CalcAbsoluteVelocity();
  2775. }
  2776. //-----------------------------------------------------------------------------
  2777. // handler to do stuff after you are restored
  2778. //-----------------------------------------------------------------------------
  2779. void CBaseEntity::OnRestore()
  2780. {
  2781. #if defined( PORTAL ) || defined( HL2_EPISODIC ) || defined ( HL2_DLL ) || defined( HL2_LOSTCOAST )
  2782. // We had a short period during the 2013 beta where the FL_* flags had a bogus value near the top, so detect
  2783. // these bad saves and just give up. Only saves from the short beta period should have been effected.
  2784. if ( GetFlags() & FL_FAKECLIENT )
  2785. {
  2786. char szMsg[256];
  2787. V_snprintf( szMsg, sizeof(szMsg), "\nInvalid save, unable to load. Please run \"map %s\" to restart this level manually\n\n", gpGlobals->mapname.ToCStr() );
  2788. Msg( "%s", szMsg );
  2789. engine->ServerCommand("wait;wait;disconnect;showconsole\n");
  2790. }
  2791. #endif
  2792. SimThink_EntityChanged( this );
  2793. // touchlinks get recomputed
  2794. if ( IsEFlagSet( EFL_CHECK_UNTOUCH ) )
  2795. {
  2796. RemoveEFlags( EFL_CHECK_UNTOUCH );
  2797. SetCheckUntouch( true );
  2798. }
  2799. // disable touch functions while we recreate the touch links between entities
  2800. // NOTE: We don't do this on transitions, because we'd miss the OnStartTouch call!
  2801. #if !defined(HL2_DLL) || ( defined(HL2_DLL) && defined(HL2_EPISODIC) )
  2802. CBaseEntity::sm_bDisableTouchFuncs = ( gpGlobals->eLoadType != MapLoad_Transition );
  2803. PhysicsTouchTriggers();
  2804. CBaseEntity::sm_bDisableTouchFuncs = false;
  2805. #endif // HL2_EPISODIC
  2806. //Adrian: If I'm restoring with these fields it means I've become a client side ragdoll.
  2807. //Don't create another one, just wait until is my time of being removed.
  2808. if ( GetFlags() & FL_TRANSRAGDOLL )
  2809. {
  2810. m_nRenderFX = kRenderFxNone;
  2811. AddEffects( EF_NODRAW );
  2812. RemoveFlag( FL_DISSOLVING | FL_ONFIRE );
  2813. }
  2814. if ( m_pParent )
  2815. {
  2816. CBaseEntity *pChild = m_pParent->FirstMoveChild();
  2817. while ( pChild )
  2818. {
  2819. if ( pChild == this )
  2820. break;
  2821. pChild = pChild->NextMovePeer();
  2822. }
  2823. if ( pChild != this )
  2824. {
  2825. #if _DEBUG
  2826. // generally this means you've got something marked FCAP_DONT_SAVE
  2827. // in a hierarchy. That's probably ok given this fixup, but the hierarhcy
  2828. // linked list is just saved/loaded in-place
  2829. Warning("Fixing up parent on %s\n", GetClassname() );
  2830. #endif
  2831. // We only need to be back in the parent's list because we're already in the right place and with the right data
  2832. LinkChild( m_pParent, this );
  2833. }
  2834. }
  2835. // We're not save/loading the PVS dirty state. Assume everything is dirty after a restore
  2836. NetworkProp()->MarkPVSInformationDirty();
  2837. }
  2838. //-----------------------------------------------------------------------------
  2839. // Purpose: Recursively restores all the classes in an object, in reverse order (top down)
  2840. // Output : int 0 on failure, 1 on success
  2841. //-----------------------------------------------------------------------------
  2842. int CBaseEntity::RestoreDataDescBlock( IRestore &restore, datamap_t *dmap )
  2843. {
  2844. return restore.ReadAll( this, dmap );
  2845. }
  2846. //-----------------------------------------------------------------------------
  2847. bool CBaseEntity::ShouldSavePhysics()
  2848. {
  2849. return true;
  2850. }
  2851. //-----------------------------------------------------------------------------
  2852. #include "tier0/memdbgoff.h"
  2853. //-----------------------------------------------------------------------------
  2854. // CBaseEntity new/delete
  2855. // allocates and frees memory for itself from the engine->
  2856. // All fields in the object are all initialized to 0.
  2857. //-----------------------------------------------------------------------------
  2858. void *CBaseEntity::operator new( size_t stAllocateBlock )
  2859. {
  2860. // call into engine to get memory
  2861. Assert( stAllocateBlock != 0 );
  2862. return engine->PvAllocEntPrivateData(stAllocateBlock);
  2863. };
  2864. void *CBaseEntity::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
  2865. {
  2866. // call into engine to get memory
  2867. Assert( stAllocateBlock != 0 );
  2868. return engine->PvAllocEntPrivateData(stAllocateBlock);
  2869. }
  2870. void CBaseEntity::operator delete( void *pMem )
  2871. {
  2872. // get the engine to free the memory
  2873. engine->FreeEntPrivateData( pMem );
  2874. }
  2875. #include "tier0/memdbgon.h"
  2876. #ifdef _DEBUG
  2877. void CBaseEntity::FunctionCheck( void *pFunction, const char *name )
  2878. {
  2879. #ifdef USES_SAVERESTORE
  2880. // Note, if you crash here and your class is using multiple inheritance, it is
  2881. // probably the case that CBaseEntity (or a descendant) is not the first
  2882. // class in your list of ancestors, which it must be.
  2883. if (pFunction && !UTIL_FunctionToName( GetDataDescMap(), (inputfunc_t *)pFunction ) )
  2884. {
  2885. Warning( "FUNCTION NOT IN TABLE!: %s:%s (%08lx)\n", STRING(m_iClassname), name, (unsigned long)pFunction );
  2886. Assert(0);
  2887. }
  2888. #endif
  2889. }
  2890. #endif
  2891. bool CBaseEntity::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
  2892. {
  2893. return false;
  2894. }
  2895. //-----------------------------------------------------------------------------
  2896. // Perform hitbox test, returns true *if hitboxes were tested at all*!!
  2897. //-----------------------------------------------------------------------------
  2898. bool CBaseEntity::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  2899. {
  2900. return false;
  2901. }
  2902. void CBaseEntity::SetOwnerEntity( CBaseEntity* pOwner )
  2903. {
  2904. if ( m_hOwnerEntity.Get() != pOwner )
  2905. {
  2906. m_hOwnerEntity = pOwner;
  2907. CollisionRulesChanged();
  2908. }
  2909. }
  2910. void CBaseEntity::SetMoveType( MoveType_t val, MoveCollide_t moveCollide )
  2911. {
  2912. #ifdef _DEBUG
  2913. // Make sure the move type + move collide are compatible...
  2914. if ((val != MOVETYPE_FLY) && (val != MOVETYPE_FLYGRAVITY))
  2915. {
  2916. Assert( moveCollide == MOVECOLLIDE_DEFAULT );
  2917. }
  2918. if ( m_MoveType == MOVETYPE_VPHYSICS && val != m_MoveType )
  2919. {
  2920. if ( VPhysicsGetObject() && val != MOVETYPE_NONE )
  2921. {
  2922. // What am I supposed to do with the physics object if
  2923. // you're changing away from MOVETYPE_VPHYSICS without making the object
  2924. // shadow? This isn't likely to work, assert.
  2925. // You probably meant to call VPhysicsInitShadow() instead of VPhysicsInitNormal()!
  2926. Assert( VPhysicsGetObject()->GetShadowController() );
  2927. }
  2928. }
  2929. #endif
  2930. if ( m_MoveType == val )
  2931. {
  2932. m_MoveCollide = moveCollide;
  2933. return;
  2934. }
  2935. // This is needed to the removal of MOVETYPE_FOLLOW:
  2936. // We can't transition from follow to a different movetype directly
  2937. // or the leaf code will break.
  2938. Assert( !IsEffectActive( EF_BONEMERGE ) );
  2939. m_MoveType = val;
  2940. m_MoveCollide = moveCollide;
  2941. CollisionRulesChanged();
  2942. switch( m_MoveType )
  2943. {
  2944. case MOVETYPE_WALK:
  2945. {
  2946. SetSimulatedEveryTick( true );
  2947. SetAnimatedEveryTick( true );
  2948. }
  2949. break;
  2950. case MOVETYPE_STEP:
  2951. {
  2952. // This will probably go away once I remove the cvar that controls the test code
  2953. SetSimulatedEveryTick( g_bTestMoveTypeStepSimulation ? true : false );
  2954. SetAnimatedEveryTick( false );
  2955. }
  2956. break;
  2957. case MOVETYPE_FLY:
  2958. case MOVETYPE_FLYGRAVITY:
  2959. {
  2960. // Initialize our water state, because these movetypes care about transitions in/out of water
  2961. UpdateWaterState();
  2962. }
  2963. break;
  2964. default:
  2965. {
  2966. SetSimulatedEveryTick( true );
  2967. SetAnimatedEveryTick( false );
  2968. }
  2969. }
  2970. // This will probably go away or be handled in a better way once I remove the cvar that controls the test code
  2971. CheckStepSimulationChanged();
  2972. CheckHasGamePhysicsSimulation();
  2973. }
  2974. void CBaseEntity::Spawn( void )
  2975. {
  2976. }
  2977. CBaseEntity* CBaseEntity::Instance( const CBaseHandle &hEnt )
  2978. {
  2979. return gEntList.GetBaseEntity( hEnt );
  2980. }
  2981. int CBaseEntity::GetTransmitState( void )
  2982. {
  2983. edict_t *ed = edict();
  2984. if ( !ed )
  2985. return 0;
  2986. return ed->m_fStateFlags;
  2987. }
  2988. int CBaseEntity::SetTransmitState( int nFlag)
  2989. {
  2990. edict_t *ed = edict();
  2991. if ( !ed )
  2992. return 0;
  2993. // clear current flags = check ShouldTransmit()
  2994. ed->ClearTransmitState();
  2995. int oldFlags = ed->m_fStateFlags;
  2996. ed->m_fStateFlags |= nFlag;
  2997. // Tell the engine (used for a network backdoor optimization).
  2998. if ( (oldFlags & FL_EDICT_DONTSEND) != (ed->m_fStateFlags & FL_EDICT_DONTSEND) )
  2999. engine->NotifyEdictFlagsChange( entindex() );
  3000. return ed->m_fStateFlags;
  3001. }
  3002. int CBaseEntity::UpdateTransmitState()
  3003. {
  3004. // If you get this assert, you should be calling DispatchUpdateTransmitState
  3005. // instead of UpdateTransmitState.
  3006. Assert( g_nInsideDispatchUpdateTransmitState > 0 );
  3007. // If an object is the moveparent of something else, don't skip it just because it's marked EF_NODRAW or else
  3008. // the client won't have a proper origin for the child since the hierarchy won't be correctly transmitted down
  3009. if ( IsEffectActive( EF_NODRAW ) &&
  3010. !m_hMoveChild.Get() )
  3011. {
  3012. return SetTransmitState( FL_EDICT_DONTSEND );
  3013. }
  3014. if ( !IsEFlagSet( EFL_FORCE_CHECK_TRANSMIT ) )
  3015. {
  3016. if ( !GetModelIndex() || !GetModelName() )
  3017. {
  3018. return SetTransmitState( FL_EDICT_DONTSEND );
  3019. }
  3020. }
  3021. // Always send the world
  3022. if ( GetModelIndex() == 1 )
  3023. {
  3024. return SetTransmitState( FL_EDICT_ALWAYS );
  3025. }
  3026. if ( IsEFlagSet( EFL_IN_SKYBOX ) )
  3027. {
  3028. return SetTransmitState( FL_EDICT_ALWAYS );
  3029. }
  3030. // by default cull against PVS
  3031. return SetTransmitState( FL_EDICT_PVSCHECK );
  3032. }
  3033. int CBaseEntity::DispatchUpdateTransmitState()
  3034. {
  3035. edict_t *ed = edict();
  3036. if ( m_nTransmitStateOwnedCounter != 0 )
  3037. return ed ? ed->m_fStateFlags : 0;
  3038. g_nInsideDispatchUpdateTransmitState++;
  3039. int ret = UpdateTransmitState();
  3040. g_nInsideDispatchUpdateTransmitState--;
  3041. return ret;
  3042. }
  3043. //-----------------------------------------------------------------------------
  3044. // Purpose: Note, an entity can override the send table ( e.g., to send less data or to send minimal data for
  3045. // objects ( prob. players ) that are not in the pvs.
  3046. // Input : **ppSendTable -
  3047. // *recipient -
  3048. // *pvs -
  3049. // Output : Returns true on success, false on failure.
  3050. //-----------------------------------------------------------------------------
  3051. int CBaseEntity::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  3052. {
  3053. int fFlags = DispatchUpdateTransmitState();
  3054. if ( fFlags & FL_EDICT_PVSCHECK )
  3055. {
  3056. return FL_EDICT_PVSCHECK;
  3057. }
  3058. else if ( fFlags & FL_EDICT_ALWAYS )
  3059. {
  3060. return FL_EDICT_ALWAYS;
  3061. }
  3062. else if ( fFlags & FL_EDICT_DONTSEND )
  3063. {
  3064. return FL_EDICT_DONTSEND;
  3065. }
  3066. // if ( IsToolRecording() )
  3067. // {
  3068. // return FL_EDICT_ALWAYS;
  3069. // }
  3070. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  3071. Assert( pRecipientEntity->IsPlayer() );
  3072. CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
  3073. // FIXME: Refactor once notion of "team" is moved into HL2 code
  3074. // Team rules may tell us that we should
  3075. if ( pRecipientPlayer->GetTeam() )
  3076. {
  3077. if ( pRecipientPlayer->GetTeam()->ShouldTransmitToPlayer( pRecipientPlayer, this ))
  3078. return FL_EDICT_ALWAYS;
  3079. }
  3080. /*#ifdef INVASION_DLL
  3081. // Check test network vis distance stuff. Eventually network LOD will do this.
  3082. float flTestDistSqr = pRecipientEntity->GetAbsOrigin().DistToSqr( WorldSpaceCenter() );
  3083. if ( flTestDistSqr > sv_netvisdist.GetFloat() * sv_netvisdist.GetFloat() )
  3084. return TRANSMIT_NO; // TODO doesn't work with HLTV
  3085. #endif*/
  3086. // by default do a PVS check
  3087. return FL_EDICT_PVSCHECK;
  3088. }
  3089. //-----------------------------------------------------------------------------
  3090. // Rules about which entities need to transmit along with me
  3091. //-----------------------------------------------------------------------------
  3092. void CBaseEntity::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  3093. {
  3094. int index = entindex();
  3095. // Are we already marked for transmission?
  3096. if ( pInfo->m_pTransmitEdict->Get( index ) )
  3097. return;
  3098. CServerNetworkProperty *pNetworkParent = NetworkProp()->GetNetworkParent();
  3099. pInfo->m_pTransmitEdict->Set( index );
  3100. // HLTV/Replay need to know if this entity is culled by PVS limits
  3101. if ( pInfo->m_pTransmitAlways )
  3102. {
  3103. // in HLTV/Replay mode always transmit entitys with move-parents
  3104. // HLTV/Replay can't resolve the mode-parents relationships
  3105. if ( bAlways || pNetworkParent )
  3106. {
  3107. // tell HLTV/Replay that this entity is always transmitted
  3108. pInfo->m_pTransmitAlways->Set( index );
  3109. }
  3110. else
  3111. {
  3112. // HLTV/Replay will PVS cull this entity, so update the
  3113. // node/cluster infos if necessary
  3114. m_Network.RecomputePVSInformation();
  3115. }
  3116. }
  3117. // Force our aiment and move parent to be sent.
  3118. if ( pNetworkParent )
  3119. {
  3120. CBaseEntity *pMoveParent = pNetworkParent->GetBaseEntity();
  3121. pMoveParent->SetTransmit( pInfo, bAlways );
  3122. }
  3123. }
  3124. //-----------------------------------------------------------------------------
  3125. // Returns which skybox the entity is in
  3126. //-----------------------------------------------------------------------------
  3127. CSkyCamera *CBaseEntity::GetEntitySkybox()
  3128. {
  3129. int area = engine->GetArea( WorldSpaceCenter() );
  3130. CSkyCamera *pCur = GetSkyCameraList();
  3131. while ( pCur )
  3132. {
  3133. if ( engine->CheckAreasConnected( area, pCur->m_skyboxData.area ) )
  3134. return pCur;
  3135. pCur = pCur->m_pNext;
  3136. }
  3137. return NULL;
  3138. }
  3139. bool CBaseEntity::DetectInSkybox()
  3140. {
  3141. if ( GetEntitySkybox() != NULL )
  3142. {
  3143. AddEFlags( EFL_IN_SKYBOX );
  3144. return true;
  3145. }
  3146. RemoveEFlags( EFL_IN_SKYBOX );
  3147. return false;
  3148. }
  3149. //------------------------------------------------------------------------------
  3150. // Computes a world-aligned bounding box that surrounds everything in the entity
  3151. //------------------------------------------------------------------------------
  3152. void CBaseEntity::ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs )
  3153. {
  3154. // Should never get here.. only use USE_GAME_CODE with bounding boxes
  3155. // if you have an implementation for this method
  3156. Assert( 0 );
  3157. }
  3158. //------------------------------------------------------------------------------
  3159. // Purpose : If name exists returns name, otherwise returns classname
  3160. // Input :
  3161. // Output :
  3162. //------------------------------------------------------------------------------
  3163. const char *CBaseEntity::GetDebugName(void)
  3164. {
  3165. if ( this == NULL )
  3166. return "<<null>>";
  3167. if ( m_iName != NULL_STRING )
  3168. {
  3169. return STRING(m_iName);
  3170. }
  3171. else
  3172. {
  3173. return STRING(m_iClassname);
  3174. }
  3175. }
  3176. //------------------------------------------------------------------------------
  3177. // Purpose :
  3178. // Input :
  3179. // Output :
  3180. //------------------------------------------------------------------------------
  3181. void CBaseEntity::DrawInputOverlay(const char *szInputName, CBaseEntity *pCaller, variant_t Value)
  3182. {
  3183. char bigstring[1024];
  3184. if ( Value.FieldType() == FIELD_INTEGER )
  3185. {
  3186. Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s,%d) <-- (%s)\n", gpGlobals->curtime, szInputName, Value.Int(), pCaller ? pCaller->GetDebugName() : NULL);
  3187. }
  3188. else if ( Value.FieldType() == FIELD_STRING )
  3189. {
  3190. Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s,%s) <-- (%s)\n", gpGlobals->curtime, szInputName, Value.String(), pCaller ? pCaller->GetDebugName() : NULL);
  3191. }
  3192. else
  3193. {
  3194. Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s) <-- (%s)\n", gpGlobals->curtime, szInputName, pCaller ? pCaller->GetDebugName() : NULL);
  3195. }
  3196. AddTimedOverlay(bigstring, 10.0);
  3197. if ( Value.FieldType() == FIELD_INTEGER )
  3198. {
  3199. DevMsg( 2, "input: (%s,%d) -> (%s,%s), from (%s)\n", szInputName, Value.Int(), STRING(m_iClassname), GetDebugName(), pCaller ? pCaller->GetDebugName() : NULL);
  3200. }
  3201. else if ( Value.FieldType() == FIELD_STRING )
  3202. {
  3203. DevMsg( 2, "input: (%s,%s) -> (%s,%s), from (%s)\n", szInputName, Value.String(), STRING(m_iClassname), GetDebugName(), pCaller ? pCaller->GetDebugName() : NULL);
  3204. }
  3205. else
  3206. DevMsg( 2, "input: (%s) -> (%s,%s), from (%s)\n", szInputName, STRING(m_iClassname), GetDebugName(), pCaller ? pCaller->GetDebugName() : NULL);
  3207. }
  3208. //------------------------------------------------------------------------------
  3209. // Purpose :
  3210. // Input :
  3211. // Output :
  3212. //------------------------------------------------------------------------------
  3213. void CBaseEntity::DrawOutputOverlay(CEventAction *ev)
  3214. {
  3215. // Print to entity
  3216. char bigstring[1024];
  3217. if ( ev->m_flDelay )
  3218. {
  3219. Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s) --> (%s),%.1f) \n", gpGlobals->curtime, STRING(ev->m_iTargetInput), STRING(ev->m_iTarget), ev->m_flDelay);
  3220. }
  3221. else
  3222. {
  3223. Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s) --> (%s)\n", gpGlobals->curtime, STRING(ev->m_iTargetInput), STRING(ev->m_iTarget));
  3224. }
  3225. AddTimedOverlay(bigstring, 10.0);
  3226. // Now print to the console
  3227. if ( ev->m_flDelay )
  3228. {
  3229. DevMsg( 2, "output: (%s,%s) -> (%s,%s,%.1f)\n", STRING(m_iClassname), GetDebugName(), STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), ev->m_flDelay );
  3230. }
  3231. else
  3232. {
  3233. DevMsg( 2, "output: (%s,%s) -> (%s,%s)\n", STRING(m_iClassname), GetDebugName(), STRING(ev->m_iTarget), STRING(ev->m_iTargetInput) );
  3234. }
  3235. }
  3236. //-----------------------------------------------------------------------------
  3237. // Entity events... these are events targetted to a particular entity
  3238. // Each event defines its own well-defined event data structure
  3239. //-----------------------------------------------------------------------------
  3240. void CBaseEntity::OnEntityEvent( EntityEvent_t event, void *pEventData )
  3241. {
  3242. switch( event )
  3243. {
  3244. case ENTITY_EVENT_WATER_TOUCH:
  3245. {
  3246. int nContents = (int)pEventData;
  3247. if ( !nContents || (nContents & CONTENTS_WATER) )
  3248. {
  3249. ++m_nWaterTouch;
  3250. }
  3251. if ( nContents & CONTENTS_SLIME )
  3252. {
  3253. ++m_nSlimeTouch;
  3254. }
  3255. }
  3256. break;
  3257. case ENTITY_EVENT_WATER_UNTOUCH:
  3258. {
  3259. int nContents = (int)pEventData;
  3260. if ( !nContents || (nContents & CONTENTS_WATER) )
  3261. {
  3262. --m_nWaterTouch;
  3263. }
  3264. if ( nContents & CONTENTS_SLIME )
  3265. {
  3266. --m_nSlimeTouch;
  3267. }
  3268. }
  3269. break;
  3270. default:
  3271. return;
  3272. }
  3273. // Only do this for vphysics objects
  3274. if ( GetMoveType() != MOVETYPE_VPHYSICS )
  3275. return;
  3276. int nNewContents = 0;
  3277. if ( m_nWaterTouch > 0 )
  3278. {
  3279. nNewContents |= CONTENTS_WATER;
  3280. }
  3281. if ( m_nSlimeTouch > 0 )
  3282. {
  3283. nNewContents |= CONTENTS_SLIME;
  3284. }
  3285. if (( nNewContents & MASK_WATER ) == 0)
  3286. {
  3287. SetWaterLevel( 0 );
  3288. SetWaterType( CONTENTS_EMPTY );
  3289. return;
  3290. }
  3291. SetWaterLevel( 1 );
  3292. SetWaterType( nNewContents );
  3293. }
  3294. ConVar ent_messages_draw( "ent_messages_draw", "0", FCVAR_CHEAT, "Visualizes all entity input/output activity." );
  3295. //-----------------------------------------------------------------------------
  3296. // Purpose: calls the appropriate message mapped function in the entity according
  3297. // to the fired action.
  3298. // Input : char *szInputName - input destination
  3299. // *pActivator - entity which initiated this sequence of actions
  3300. // *pCaller - entity from which this event is sent
  3301. // Output : Returns true on success, false on failure.
  3302. //-----------------------------------------------------------------------------
  3303. bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID )
  3304. {
  3305. if ( ent_messages_draw.GetBool() )
  3306. {
  3307. if ( pCaller != NULL )
  3308. {
  3309. NDebugOverlay::Line( pCaller->GetAbsOrigin(), GetAbsOrigin(), 255, 255, 255, false, 3 );
  3310. NDebugOverlay::Box( pCaller->GetAbsOrigin(), Vector(-4, -4, -4), Vector(4, 4, 4), 255, 0, 0, 0, 3 );
  3311. }
  3312. NDebugOverlay::Text( GetAbsOrigin(), szInputName, false, 3 );
  3313. NDebugOverlay::Box( GetAbsOrigin(), Vector(-4, -4, -4), Vector(4, 4, 4), 0, 255, 0, 0, 3 );
  3314. }
  3315. // loop through the data description list, restoring each data desc block
  3316. for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  3317. {
  3318. // search through all the actions in the data description, looking for a match
  3319. for ( int i = 0; i < dmap->dataNumFields; i++ )
  3320. {
  3321. if ( dmap->dataDesc[i].flags & FTYPEDESC_INPUT )
  3322. {
  3323. if ( !Q_stricmp(dmap->dataDesc[i].externalName, szInputName) )
  3324. {
  3325. // found a match
  3326. char szBuffer[256];
  3327. // mapper debug message
  3328. if (pCaller != NULL)
  3329. {
  3330. Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName), GetDebugName(), szInputName, Value.String() );
  3331. }
  3332. else
  3333. {
  3334. Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input <NULL>: %s.%s(%s)\n", gpGlobals->curtime, GetDebugName(), szInputName, Value.String() );
  3335. }
  3336. DevMsg( 2, "%s", szBuffer );
  3337. ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
  3338. if (m_debugOverlays & OVERLAY_MESSAGE_BIT)
  3339. {
  3340. DrawInputOverlay(szInputName,pCaller,Value);
  3341. }
  3342. // convert the value if necessary
  3343. if ( Value.FieldType() != dmap->dataDesc[i].fieldType )
  3344. {
  3345. if ( !(Value.FieldType() == FIELD_VOID && dmap->dataDesc[i].fieldType == FIELD_STRING) ) // allow empty strings
  3346. {
  3347. if ( !Value.Convert( (fieldtype_t)dmap->dataDesc[i].fieldType ) )
  3348. {
  3349. // bad conversion
  3350. Warning( "!! ERROR: bad input/output link:\n!! %s(%s,%s) doesn't match type from %s(%s)\n",
  3351. STRING(m_iClassname), GetDebugName(), szInputName,
  3352. ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "<null>",
  3353. ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "<null>" );
  3354. return false;
  3355. }
  3356. }
  3357. }
  3358. // call the input handler, or if there is none just set the value
  3359. inputfunc_t pfnInput = dmap->dataDesc[i].inputFunc;
  3360. if ( pfnInput )
  3361. {
  3362. // Package the data into a struct for passing to the input handler.
  3363. inputdata_t data;
  3364. data.pActivator = pActivator;
  3365. data.pCaller = pCaller;
  3366. data.value = Value;
  3367. data.nOutputID = outputID;
  3368. (this->*pfnInput)( data );
  3369. }
  3370. else if ( dmap->dataDesc[i].flags & FTYPEDESC_KEY )
  3371. {
  3372. // set the value directly
  3373. Value.SetOther( ((char*)this) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ]);
  3374. // TODO: if this becomes evil and causes too many full entity updates, then we should make
  3375. // a macro like this:
  3376. //
  3377. // define MAKE_INPUTVAR(x) void Note##x##Modified() { x.GetForModify(); }
  3378. //
  3379. // Then the datadesc points at that function and we call it here. The only pain is to add
  3380. // that function for all the DEFINE_INPUT calls.
  3381. NetworkStateChanged();
  3382. }
  3383. return true;
  3384. }
  3385. }
  3386. }
  3387. }
  3388. DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName)*/ );
  3389. return false;
  3390. }
  3391. //-----------------------------------------------------------------------------
  3392. // Purpose: Input handler for the entity alpha.
  3393. // Input : nAlpha - Alpha value (0 - 255).
  3394. //-----------------------------------------------------------------------------
  3395. void CBaseEntity::InputAlpha( inputdata_t &inputdata )
  3396. {
  3397. SetRenderColorA( clamp( inputdata.value.Int(), 0, 255 ) );
  3398. }
  3399. //-----------------------------------------------------------------------------
  3400. // Activate alternative sorting
  3401. //-----------------------------------------------------------------------------
  3402. void CBaseEntity::InputAlternativeSorting( inputdata_t &inputdata )
  3403. {
  3404. m_bAlternateSorting = inputdata.value.Bool();
  3405. }
  3406. //-----------------------------------------------------------------------------
  3407. // Purpose: Input handler for the entity color. Ignores alpha since that is handled
  3408. // by a separate input handler.
  3409. // Input : Color32 new value for color (alpha is ignored).
  3410. //-----------------------------------------------------------------------------
  3411. void CBaseEntity::InputColor( inputdata_t &inputdata )
  3412. {
  3413. color32 clr = inputdata.value.Color32();
  3414. SetRenderColor( clr.r, clr.g, clr.b );
  3415. }
  3416. //-----------------------------------------------------------------------------
  3417. // Purpose: Called whenever the entity is 'Used'. This can be when a player hits
  3418. // use, or when an entity targets it without an output name (legacy entities)
  3419. //-----------------------------------------------------------------------------
  3420. void CBaseEntity::InputUse( inputdata_t &inputdata )
  3421. {
  3422. Use( inputdata.pActivator, inputdata.pCaller, (USE_TYPE)inputdata.nOutputID, 0 );
  3423. }
  3424. //-----------------------------------------------------------------------------
  3425. // Purpose: Reads an output variable, by string name, from an entity
  3426. // Input : char *varName - the string name of the variable
  3427. // variant_t *var - the value is stored here
  3428. // Output : Returns true on success, false on failure.
  3429. //-----------------------------------------------------------------------------
  3430. bool CBaseEntity::ReadKeyField( const char *varName, variant_t *var )
  3431. {
  3432. if ( !varName )
  3433. return false;
  3434. // loop through the data description list, restoring each data desc block
  3435. for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  3436. {
  3437. // search through all the readable fields in the data description, looking for a match
  3438. for ( int i = 0; i < dmap->dataNumFields; i++ )
  3439. {
  3440. if ( dmap->dataDesc[i].flags & (FTYPEDESC_OUTPUT | FTYPEDESC_KEY) )
  3441. {
  3442. if ( !Q_stricmp(dmap->dataDesc[i].externalName, varName) )
  3443. {
  3444. var->Set( dmap->dataDesc[i].fieldType, ((char*)this) + dmap->dataDesc[i].fieldOffset[ TD_OFFSET_NORMAL ] );
  3445. return true;
  3446. }
  3447. }
  3448. }
  3449. }
  3450. return false;
  3451. }
  3452. //-----------------------------------------------------------------------------
  3453. // Purpose: Sets the damage filter on the object
  3454. //-----------------------------------------------------------------------------
  3455. void CBaseEntity::InputEnableDamageForces( inputdata_t &inputdata )
  3456. {
  3457. RemoveEFlags( EFL_NO_DAMAGE_FORCES );
  3458. }
  3459. void CBaseEntity::InputDisableDamageForces( inputdata_t &inputdata )
  3460. {
  3461. AddEFlags( EFL_NO_DAMAGE_FORCES );
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. // Purpose: Sets the damage filter on the object
  3465. //-----------------------------------------------------------------------------
  3466. void CBaseEntity::InputSetDamageFilter( inputdata_t &inputdata )
  3467. {
  3468. // Get a handle to my damage filter entity if there is one.
  3469. m_iszDamageFilterName = inputdata.value.StringID();
  3470. if ( m_iszDamageFilterName != NULL_STRING )
  3471. {
  3472. m_hDamageFilter = gEntList.FindEntityByName( NULL, m_iszDamageFilterName );
  3473. }
  3474. else
  3475. {
  3476. m_hDamageFilter = NULL;
  3477. }
  3478. }
  3479. //-----------------------------------------------------------------------------
  3480. // Purpose: Dispatch effects on this entity
  3481. //-----------------------------------------------------------------------------
  3482. void CBaseEntity::InputDispatchEffect( inputdata_t &inputdata )
  3483. {
  3484. const char *sEffect = inputdata.value.String();
  3485. if ( sEffect && sEffect[0] )
  3486. {
  3487. CEffectData data;
  3488. GetInputDispatchEffectPosition( sEffect, data.m_vOrigin, data.m_vAngles );
  3489. AngleVectors( data.m_vAngles, &data.m_vNormal );
  3490. data.m_vStart = data.m_vOrigin;
  3491. data.m_nEntIndex = entindex();
  3492. // Clip off leading attachment point numbers
  3493. while ( sEffect[0] >= '0' && sEffect[0] <= '9' )
  3494. {
  3495. sEffect++;
  3496. }
  3497. DispatchEffect( sEffect, data );
  3498. }
  3499. }
  3500. //-----------------------------------------------------------------------------
  3501. // Purpose: Returns the origin at which to play an inputted dispatcheffect
  3502. //-----------------------------------------------------------------------------
  3503. void CBaseEntity::GetInputDispatchEffectPosition( const char *sInputString, Vector &pOrigin, QAngle &pAngles )
  3504. {
  3505. pOrigin = GetAbsOrigin();
  3506. pAngles = GetAbsAngles();
  3507. }
  3508. //-----------------------------------------------------------------------------
  3509. // Purpose: Marks the entity for deletion
  3510. //-----------------------------------------------------------------------------
  3511. void CBaseEntity::InputKill( inputdata_t &inputdata )
  3512. {
  3513. // tell owner ( if any ) that we're dead.This is mostly for NPCMaker functionality.
  3514. CBaseEntity *pOwner = GetOwnerEntity();
  3515. if ( pOwner )
  3516. {
  3517. pOwner->DeathNotice( this );
  3518. SetOwnerEntity( NULL );
  3519. }
  3520. UTIL_Remove( this );
  3521. }
  3522. void CBaseEntity::InputKillHierarchy( inputdata_t &inputdata )
  3523. {
  3524. CBaseEntity *pChild, *pNext;
  3525. for ( pChild = FirstMoveChild(); pChild; pChild = pNext )
  3526. {
  3527. pNext = pChild->NextMovePeer();
  3528. pChild->InputKillHierarchy( inputdata );
  3529. }
  3530. // tell owner ( if any ) that we're dead. This is mostly for NPCMaker functionality.
  3531. CBaseEntity *pOwner = GetOwnerEntity();
  3532. if ( pOwner )
  3533. {
  3534. pOwner->DeathNotice( this );
  3535. SetOwnerEntity( NULL );
  3536. }
  3537. UTIL_Remove( this );
  3538. }
  3539. //------------------------------------------------------------------------------
  3540. // Purpose: Input handler for changing this entity's movement parent.
  3541. //------------------------------------------------------------------------------
  3542. void CBaseEntity::InputSetParent( inputdata_t &inputdata )
  3543. {
  3544. // If we had a parent attachment, clear it, because it's no longer valid.
  3545. if ( m_iParentAttachment )
  3546. {
  3547. m_iParentAttachment = 0;
  3548. }
  3549. SetParent( inputdata.value.StringID(), inputdata.pActivator );
  3550. }
  3551. //------------------------------------------------------------------------------
  3552. // Purpose:
  3553. //------------------------------------------------------------------------------
  3554. void CBaseEntity::SetParentAttachment( const char *szInputName, const char *szAttachment, bool bMaintainOffset )
  3555. {
  3556. // Must have a parent
  3557. if ( !m_pParent )
  3558. {
  3559. Warning("ERROR: Tried to %s for entity %s (%s), but it has no parent.\n", szInputName, GetClassname(), GetDebugName() );
  3560. return;
  3561. }
  3562. // Valid only on CBaseAnimating
  3563. CBaseAnimating *pAnimating = m_pParent->GetBaseAnimating();
  3564. if ( !pAnimating )
  3565. {
  3566. Warning("ERROR: Tried to %s for entity %s (%s), but its parent has no model.\n", szInputName, GetClassname(), GetDebugName() );
  3567. return;
  3568. }
  3569. // Lookup the attachment
  3570. int iAttachment = pAnimating->LookupAttachment( szAttachment );
  3571. if ( iAttachment <= 0 )
  3572. {
  3573. Warning("ERROR: Tried to %s for entity %s (%s), but it has no attachment named %s.\n", szInputName, GetClassname(), GetDebugName(), szAttachment );
  3574. return;
  3575. }
  3576. m_iParentAttachment = iAttachment;
  3577. SetParent( m_pParent, m_iParentAttachment );
  3578. // Now move myself directly onto the attachment point
  3579. SetMoveType( MOVETYPE_NONE );
  3580. if ( !bMaintainOffset )
  3581. {
  3582. SetLocalOrigin( vec3_origin );
  3583. SetLocalAngles( vec3_angle );
  3584. }
  3585. }
  3586. //-----------------------------------------------------------------------------
  3587. // Purpose: Input handler for changing this entity's movement parent's attachment point
  3588. //-----------------------------------------------------------------------------
  3589. void CBaseEntity::InputSetParentAttachment( inputdata_t &inputdata )
  3590. {
  3591. SetParentAttachment( "SetParentAttachment", inputdata.value.String(), false );
  3592. }
  3593. //-----------------------------------------------------------------------------
  3594. // Purpose: Input handler for changing this entity's movement parent's attachment point
  3595. //-----------------------------------------------------------------------------
  3596. void CBaseEntity::InputSetParentAttachmentMaintainOffset( inputdata_t &inputdata )
  3597. {
  3598. SetParentAttachment( "SetParentAttachmentMaintainOffset", inputdata.value.String(), true );
  3599. }
  3600. //------------------------------------------------------------------------------
  3601. // Purpose: Input handler for clearing this entity's movement parent.
  3602. //------------------------------------------------------------------------------
  3603. void CBaseEntity::InputClearParent( inputdata_t &inputdata )
  3604. {
  3605. SetParent( NULL );
  3606. }
  3607. //------------------------------------------------------------------------------
  3608. // Purpose : Returns velcocity of base entity. If physically simulated gets
  3609. // velocity from physics object
  3610. // Input :
  3611. // Output :
  3612. //------------------------------------------------------------------------------
  3613. void CBaseEntity::GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity)
  3614. {
  3615. if (GetMoveType()==MOVETYPE_VPHYSICS && m_pPhysicsObject)
  3616. {
  3617. m_pPhysicsObject->GetVelocity(vVelocity,vAngVelocity);
  3618. }
  3619. else
  3620. {
  3621. if (vVelocity != NULL)
  3622. {
  3623. *vVelocity = GetAbsVelocity();
  3624. }
  3625. if (vAngVelocity != NULL)
  3626. {
  3627. QAngle tmp = GetLocalAngularVelocity();
  3628. QAngleToAngularImpulse( tmp, *vAngVelocity );
  3629. }
  3630. }
  3631. }
  3632. bool CBaseEntity::IsMoving()
  3633. {
  3634. Vector velocity;
  3635. GetVelocity( &velocity, NULL );
  3636. return velocity != vec3_origin;
  3637. }
  3638. //-----------------------------------------------------------------------------
  3639. // Purpose: Retrieves the coordinate frame for this entity.
  3640. // Input : forward - Receives the entity's forward vector.
  3641. // right - Receives the entity's right vector.
  3642. // up - Receives the entity's up vector.
  3643. //-----------------------------------------------------------------------------
  3644. void CBaseEntity::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const
  3645. {
  3646. // This call is necessary to cause m_rgflCoordinateFrame to be recomputed
  3647. const matrix3x4_t &entityToWorld = EntityToWorldTransform();
  3648. if (pForward != NULL)
  3649. {
  3650. MatrixGetColumn( entityToWorld, 0, *pForward );
  3651. }
  3652. if (pRight != NULL)
  3653. {
  3654. MatrixGetColumn( entityToWorld, 1, *pRight );
  3655. *pRight *= -1.0f;
  3656. }
  3657. if (pUp != NULL)
  3658. {
  3659. MatrixGetColumn( entityToWorld, 2, *pUp );
  3660. }
  3661. }
  3662. //-----------------------------------------------------------------------------
  3663. // Purpose: Sets the model, validates that it's of the appropriate type
  3664. // Input : *szModelName -
  3665. //-----------------------------------------------------------------------------
  3666. void CBaseEntity::SetModel( const char *szModelName )
  3667. {
  3668. int modelIndex = modelinfo->GetModelIndex( szModelName );
  3669. const model_t *model = modelinfo->GetModel( modelIndex );
  3670. if ( model && modelinfo->GetModelType( model ) != mod_brush )
  3671. {
  3672. Msg( "Setting CBaseEntity to non-brush model %s\n", szModelName );
  3673. }
  3674. UTIL_SetModel( this, szModelName );
  3675. }
  3676. //------------------------------------------------------------------------------
  3677. CStudioHdr *CBaseEntity::OnNewModel()
  3678. {
  3679. // Do nothing.
  3680. return NULL;
  3681. }
  3682. //================================================================================
  3683. // TEAM HANDLING
  3684. //================================================================================
  3685. void CBaseEntity::InputSetTeam( inputdata_t &inputdata )
  3686. {
  3687. ChangeTeam( inputdata.value.Int() );
  3688. }
  3689. //-----------------------------------------------------------------------------
  3690. // Purpose: Put the entity in the specified team
  3691. //-----------------------------------------------------------------------------
  3692. void CBaseEntity::ChangeTeam( int iTeamNum )
  3693. {
  3694. m_iTeamNum = iTeamNum;
  3695. }
  3696. //-----------------------------------------------------------------------------
  3697. // Get the Team this entity is on
  3698. //-----------------------------------------------------------------------------
  3699. CTeam *CBaseEntity::GetTeam( void ) const
  3700. {
  3701. return GetGlobalTeam( m_iTeamNum );
  3702. }
  3703. //-----------------------------------------------------------------------------
  3704. // Purpose: Returns true if these players are both in at least one team together
  3705. //-----------------------------------------------------------------------------
  3706. bool CBaseEntity::InSameTeam( const CBaseEntity *pEntity ) const
  3707. {
  3708. if ( !pEntity )
  3709. return false;
  3710. return ( pEntity->GetTeam() == GetTeam() );
  3711. }
  3712. //-----------------------------------------------------------------------------
  3713. // Purpose: Returns the string name of the players team
  3714. //-----------------------------------------------------------------------------
  3715. const char *CBaseEntity::TeamID( void ) const
  3716. {
  3717. if ( GetTeam() == NULL )
  3718. return "";
  3719. return GetTeam()->GetName();
  3720. }
  3721. //-----------------------------------------------------------------------------
  3722. // Purpose: Returns true if the player is on the same team
  3723. //-----------------------------------------------------------------------------
  3724. bool CBaseEntity::IsInTeam( CTeam *pTeam ) const
  3725. {
  3726. return ( GetTeam() == pTeam );
  3727. }
  3728. //-----------------------------------------------------------------------------
  3729. // Purpose:
  3730. //-----------------------------------------------------------------------------
  3731. int CBaseEntity::GetTeamNumber( void ) const
  3732. {
  3733. return m_iTeamNum;
  3734. }
  3735. //-----------------------------------------------------------------------------
  3736. // Purpose:
  3737. //-----------------------------------------------------------------------------
  3738. bool CBaseEntity::IsInAnyTeam( void ) const
  3739. {
  3740. return ( GetTeam() != NULL );
  3741. }
  3742. //-----------------------------------------------------------------------------
  3743. // Purpose: Returns the type of damage that this entity inflicts.
  3744. //-----------------------------------------------------------------------------
  3745. int CBaseEntity::GetDamageType() const
  3746. {
  3747. return DMG_GENERIC;
  3748. }
  3749. //-----------------------------------------------------------------------------
  3750. // process notification
  3751. //-----------------------------------------------------------------------------
  3752. void CBaseEntity::NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  3753. {
  3754. }
  3755. //-----------------------------------------------------------------------------
  3756. // Purpose: Holds an entity's previous abs origin and angles at the time of
  3757. // teleportation. Used for child & constrained entity fixup to prevent
  3758. // lazy updates of abs origins and angles from messing things up.
  3759. //-----------------------------------------------------------------------------
  3760. struct TeleportListEntry_t
  3761. {
  3762. CBaseEntity *pEntity;
  3763. Vector prevAbsOrigin;
  3764. QAngle prevAbsAngles;
  3765. };
  3766. static void TeleportEntity( CBaseEntity *pSourceEntity, TeleportListEntry_t &entry, const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
  3767. {
  3768. CBaseEntity *pTeleport = entry.pEntity;
  3769. Vector prevOrigin = entry.prevAbsOrigin;
  3770. QAngle prevAngles = entry.prevAbsAngles;
  3771. int nSolidFlags = pTeleport->GetSolidFlags();
  3772. pTeleport->AddSolidFlags( FSOLID_NOT_SOLID );
  3773. // I'm teleporting myself
  3774. if ( pSourceEntity == pTeleport )
  3775. {
  3776. if ( newAngles )
  3777. {
  3778. pTeleport->SetLocalAngles( *newAngles );
  3779. if ( pTeleport->IsPlayer() )
  3780. {
  3781. CBasePlayer *pPlayer = (CBasePlayer *)pTeleport;
  3782. pPlayer->SnapEyeAngles( *newAngles );
  3783. }
  3784. }
  3785. if ( newVelocity )
  3786. {
  3787. pTeleport->SetAbsVelocity( *newVelocity );
  3788. pTeleport->SetBaseVelocity( vec3_origin );
  3789. }
  3790. if ( newPosition )
  3791. {
  3792. pTeleport->IncrementInterpolationFrame();
  3793. UTIL_SetOrigin( pTeleport, *newPosition );
  3794. }
  3795. }
  3796. else
  3797. {
  3798. // My parent is teleporting, just update my position & physics
  3799. pTeleport->CalcAbsolutePosition();
  3800. }
  3801. IPhysicsObject *pPhys = pTeleport->VPhysicsGetObject();
  3802. bool rotatePhysics = false;
  3803. // handle physics objects / shadows
  3804. if ( pPhys )
  3805. {
  3806. if ( newVelocity )
  3807. {
  3808. pPhys->SetVelocity( newVelocity, NULL );
  3809. }
  3810. const QAngle *rotAngles = &pTeleport->GetAbsAngles();
  3811. // don't rotate physics on players or bbox entities
  3812. if (pTeleport->IsPlayer() || pTeleport->GetSolid() == SOLID_BBOX )
  3813. {
  3814. rotAngles = &vec3_angle;
  3815. }
  3816. else
  3817. {
  3818. rotatePhysics = true;
  3819. }
  3820. pPhys->SetPosition( pTeleport->GetAbsOrigin(), *rotAngles, true );
  3821. }
  3822. g_pNotify->ReportTeleportEvent( pTeleport, prevOrigin, prevAngles, rotatePhysics );
  3823. pTeleport->SetSolidFlags( nSolidFlags );
  3824. }
  3825. //-----------------------------------------------------------------------------
  3826. // Purpose: Recurses an entity hierarchy and fills out a list of all entities
  3827. // in the hierarchy with their current origins and angles.
  3828. //
  3829. // This list is necessary to keep lazy updates of abs origins and angles
  3830. // from messing up our child/constrained entity fixup.
  3831. //-----------------------------------------------------------------------------
  3832. static void BuildTeleportList_r( CBaseEntity *pTeleport, CUtlVector<TeleportListEntry_t> &teleportList )
  3833. {
  3834. TeleportListEntry_t entry;
  3835. entry.pEntity = pTeleport;
  3836. entry.prevAbsOrigin = pTeleport->GetAbsOrigin();
  3837. entry.prevAbsAngles = pTeleport->GetAbsAngles();
  3838. teleportList.AddToTail( entry );
  3839. CBaseEntity *pList = pTeleport->FirstMoveChild();
  3840. while ( pList )
  3841. {
  3842. BuildTeleportList_r( pList, teleportList );
  3843. pList = pList->NextMovePeer();
  3844. }
  3845. }
  3846. static CUtlVector<CBaseEntity *> g_TeleportStack;
  3847. void CBaseEntity::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
  3848. {
  3849. if ( g_TeleportStack.Find( this ) >= 0 )
  3850. return;
  3851. int index = g_TeleportStack.AddToTail( this );
  3852. CUtlVector<TeleportListEntry_t> teleportList;
  3853. BuildTeleportList_r( this, teleportList );
  3854. int i;
  3855. for ( i = 0; i < teleportList.Count(); i++)
  3856. {
  3857. TeleportEntity( this, teleportList[i], newPosition, newAngles, newVelocity );
  3858. }
  3859. for (i = 0; i < teleportList.Count(); i++)
  3860. {
  3861. teleportList[i].pEntity->CollisionRulesChanged();
  3862. }
  3863. if ( IsPlayer() )
  3864. {
  3865. // Tell the client being teleported
  3866. IGameEvent *event = gameeventmanager->CreateEvent( "base_player_teleported" );
  3867. if ( event )
  3868. {
  3869. event->SetInt( "entindex", entindex() );
  3870. gameeventmanager->FireEventClientSide( event );
  3871. }
  3872. }
  3873. Assert( g_TeleportStack[index] == this );
  3874. g_TeleportStack.FastRemove( index );
  3875. // FIXME: add an initializer function to StepSimulationData
  3876. StepSimulationData *step = ( StepSimulationData * )GetDataObject( STEPSIMULATION );
  3877. if (step)
  3878. {
  3879. Q_memset( step, 0, sizeof( *step ) );
  3880. }
  3881. }
  3882. // Stuff implemented for weapon prediction code
  3883. void CBaseEntity::SetSize( const Vector &vecMin, const Vector &vecMax )
  3884. {
  3885. UTIL_SetSize( this, vecMin, vecMax );
  3886. }
  3887. CStudioHdr *ModelSoundsCache_LoadModel( const char *filename )
  3888. {
  3889. // Load the file
  3890. int idx = engine->PrecacheModel( filename, true );
  3891. if ( idx != -1 )
  3892. {
  3893. model_t *mdl = (model_t *)modelinfo->GetModel( idx );
  3894. if ( mdl )
  3895. {
  3896. CStudioHdr *studioHdr = new CStudioHdr( modelinfo->GetStudiomodel( mdl ), mdlcache );
  3897. if ( studioHdr->IsValid() )
  3898. {
  3899. return studioHdr;
  3900. }
  3901. }
  3902. }
  3903. return NULL;
  3904. }
  3905. void ModelSoundsCache_FinishModel( CStudioHdr *hdr )
  3906. {
  3907. Assert( hdr );
  3908. delete hdr;
  3909. }
  3910. void ModelSoundsCache_PrecacheScriptSound( const char *soundname )
  3911. {
  3912. CBaseEntity::PrecacheScriptSound( soundname );
  3913. }
  3914. static CUtlCachedFileData< CModelSoundsCache > g_ModelSoundsCache( "modelsounds.cache", MODELSOUNDSCACHE_VERSION, 0, UTL_CACHED_FILE_USE_FILESIZE, false );
  3915. void ClearModelSoundsCache()
  3916. {
  3917. if ( IsX360() )
  3918. {
  3919. return;
  3920. }
  3921. g_ModelSoundsCache.Reload();
  3922. }
  3923. //-----------------------------------------------------------------------------
  3924. // Purpose:
  3925. // Output : Returns true on success, false on failure.
  3926. //-----------------------------------------------------------------------------
  3927. bool ModelSoundsCacheInit()
  3928. {
  3929. if ( IsX360() )
  3930. {
  3931. return true;
  3932. }
  3933. return g_ModelSoundsCache.Init();
  3934. }
  3935. //-----------------------------------------------------------------------------
  3936. // Purpose:
  3937. //-----------------------------------------------------------------------------
  3938. void ModelSoundsCacheShutdown()
  3939. {
  3940. if ( IsX360() )
  3941. {
  3942. return;
  3943. }
  3944. g_ModelSoundsCache.Shutdown();
  3945. }
  3946. static CUtlSymbolTable g_ModelSoundsSymbolHelper( 0, 32, true );
  3947. class CModelSoundsCacheSaver: public CAutoGameSystem
  3948. {
  3949. public:
  3950. CModelSoundsCacheSaver( const char *name ) : CAutoGameSystem( name )
  3951. {
  3952. }
  3953. virtual void LevelInitPostEntity()
  3954. {
  3955. if ( IsX360() )
  3956. {
  3957. return;
  3958. }
  3959. if ( g_ModelSoundsCache.IsDirty() )
  3960. {
  3961. g_ModelSoundsCache.Save();
  3962. }
  3963. }
  3964. virtual void LevelShutdownPostEntity()
  3965. {
  3966. if ( IsX360() )
  3967. {
  3968. // Unforunate that this table must persist through duration of level.
  3969. // It is the common case that PrecacheModel() still gets called (and needs this table),
  3970. // after LevelInitPostEntity, as PrecacheModel() redundantly precaches.
  3971. g_ModelSoundsSymbolHelper.RemoveAll();
  3972. return;
  3973. }
  3974. if ( g_ModelSoundsCache.IsDirty() )
  3975. {
  3976. g_ModelSoundsCache.Save();
  3977. }
  3978. }
  3979. };
  3980. static CModelSoundsCacheSaver g_ModelSoundsCacheSaver( "CModelSoundsCacheSaver" );
  3981. //#define WATCHACCESS
  3982. #if defined( WATCHACCESS )
  3983. static bool g_bWatching = true;
  3984. void ModelLogFunc( const char *fileName, const char *accessType )
  3985. {
  3986. if ( g_bWatching && !CBaseEntity::IsPrecacheAllowed() )
  3987. {
  3988. if ( Q_stristr( fileName, ".vcd" ) )
  3989. {
  3990. Msg( "%s\n", fileName );
  3991. }
  3992. }
  3993. }
  3994. class CWatchForModelAccess: public CAutoGameSystem
  3995. {
  3996. public:
  3997. virtual bool Init()
  3998. {
  3999. filesystem->AddLoggingFunc(&ModelLogFunc);
  4000. return true;
  4001. }
  4002. virtual void Shutdown()
  4003. {
  4004. filesystem->RemoveLoggingFunc(&ModelLogFunc);
  4005. }
  4006. };
  4007. static CWatchForModelAccess g_WatchForModels;
  4008. #endif
  4009. // HACK: This must match the #define in cl_animevent.h in the client .dll code!!!
  4010. #define CL_EVENT_SOUND 5004
  4011. #define CL_EVENT_FOOTSTEP_LEFT 6004
  4012. #define CL_EVENT_FOOTSTEP_RIGHT 6005
  4013. #define CL_EVENT_MFOOTSTEP_LEFT 6006
  4014. #define CL_EVENT_MFOOTSTEP_RIGHT 6007
  4015. //-----------------------------------------------------------------------------
  4016. // Precache model sound. Requires a local symbol table to prevent
  4017. // a very expensive call to PrecacheScriptSound().
  4018. //-----------------------------------------------------------------------------
  4019. void CBaseEntity::PrecacheSoundHelper( const char *pName )
  4020. {
  4021. if ( !IsX360() )
  4022. {
  4023. // 360 only
  4024. Assert( 0 );
  4025. return;
  4026. }
  4027. if ( !pName || !pName[0] )
  4028. {
  4029. return;
  4030. }
  4031. if ( UTL_INVAL_SYMBOL == g_ModelSoundsSymbolHelper.Find( pName ) )
  4032. {
  4033. g_ModelSoundsSymbolHelper.AddString( pName );
  4034. // very expensive, only call when required
  4035. PrecacheScriptSound( pName );
  4036. }
  4037. }
  4038. //-----------------------------------------------------------------------------
  4039. // Precache model components
  4040. //-----------------------------------------------------------------------------
  4041. void CBaseEntity::PrecacheModelComponents( int nModelIndex )
  4042. {
  4043. model_t *pModel = (model_t *)modelinfo->GetModel( nModelIndex );
  4044. if ( !pModel || modelinfo->GetModelType( pModel ) != mod_studio )
  4045. {
  4046. return;
  4047. }
  4048. // sounds
  4049. if ( IsPC() )
  4050. {
  4051. const char *name = modelinfo->GetModelName( pModel );
  4052. if ( !g_ModelSoundsCache.EntryExists( name ) )
  4053. {
  4054. char extension[ 8 ];
  4055. Q_ExtractFileExtension( name, extension, sizeof( extension ) );
  4056. if ( Q_stristr( extension, "mdl" ) )
  4057. {
  4058. DevMsg( 2, "Late precache of %s, need to rebuild modelsounds.cache\n", name );
  4059. }
  4060. else
  4061. {
  4062. if ( !extension[ 0 ] )
  4063. {
  4064. Warning( "Precache of %s ambigious (no extension specified)\n", name );
  4065. }
  4066. else
  4067. {
  4068. Warning( "Late precache of %s (file missing?)\n", name );
  4069. }
  4070. return;
  4071. }
  4072. }
  4073. CModelSoundsCache *entry = g_ModelSoundsCache.Get( name );
  4074. Assert( entry );
  4075. if ( entry )
  4076. {
  4077. entry->PrecacheSoundList();
  4078. }
  4079. }
  4080. // particles
  4081. {
  4082. // Check keyvalues for auto-emitting particles
  4083. KeyValues *pModelKeyValues = new KeyValues("");
  4084. KeyValues::AutoDelete autodelete_pModelKeyValues( pModelKeyValues );
  4085. if ( pModelKeyValues->LoadFromBuffer( modelinfo->GetModelName( pModel ), modelinfo->GetModelKeyValueText( pModel ) ) )
  4086. {
  4087. KeyValues *pParticleEffects = pModelKeyValues->FindKey("Particles");
  4088. if ( pParticleEffects )
  4089. {
  4090. // Start grabbing the sounds and slotting them in
  4091. for ( KeyValues *pSingleEffect = pParticleEffects->GetFirstSubKey(); pSingleEffect; pSingleEffect = pSingleEffect->GetNextKey() )
  4092. {
  4093. const char *pParticleEffectName = pSingleEffect->GetString( "name", "" );
  4094. PrecacheParticleSystem( pParticleEffectName );
  4095. }
  4096. }
  4097. }
  4098. }
  4099. // model anim event owned components
  4100. {
  4101. // Check animevents for particle events
  4102. CStudioHdr studioHdr( modelinfo->GetStudiomodel( pModel ), mdlcache );
  4103. if ( studioHdr.IsValid() )
  4104. {
  4105. // force animation event resolution!!!
  4106. VerifySequenceIndex( &studioHdr );
  4107. int nSeqCount = studioHdr.GetNumSeq();
  4108. for ( int i = 0; i < nSeqCount; ++i )
  4109. {
  4110. mstudioseqdesc_t &seq = studioHdr.pSeqdesc( i );
  4111. int nEventCount = seq.numevents;
  4112. for ( int j = 0; j < nEventCount; ++j )
  4113. {
  4114. mstudioevent_t *pEvent = seq.pEvent( j );
  4115. if ( !( pEvent->type & AE_TYPE_NEWEVENTSYSTEM ) || ( pEvent->type & AE_TYPE_CLIENT ) )
  4116. {
  4117. if ( pEvent->event == AE_CL_CREATE_PARTICLE_EFFECT )
  4118. {
  4119. char token[256];
  4120. const char *pOptions = pEvent->pszOptions();
  4121. nexttoken( token, pOptions, ' ' );
  4122. if ( token[0] )
  4123. {
  4124. PrecacheParticleSystem( token );
  4125. }
  4126. continue;
  4127. }
  4128. }
  4129. // 360 precaches the model sounds now at init time, the cost is now ~250 msecs worst case.
  4130. // The disk based solution was not needed. Now at runtime partly due to already crawling the sequences
  4131. // for the particles and the expensive part was redundant PrecacheScriptSound(), which is now prevented
  4132. // by a local symbol table.
  4133. if ( IsX360() )
  4134. {
  4135. switch ( pEvent->event )
  4136. {
  4137. default:
  4138. {
  4139. if ( ( pEvent->type & AE_TYPE_NEWEVENTSYSTEM ) && ( pEvent->event == AE_SV_PLAYSOUND ) )
  4140. {
  4141. PrecacheSoundHelper( pEvent->pszOptions() );
  4142. }
  4143. }
  4144. break;
  4145. case CL_EVENT_FOOTSTEP_LEFT:
  4146. case CL_EVENT_FOOTSTEP_RIGHT:
  4147. {
  4148. char soundname[256];
  4149. char const *options = pEvent->pszOptions();
  4150. if ( !options || !options[0] )
  4151. {
  4152. options = "NPC_CombineS";
  4153. }
  4154. Q_snprintf( soundname, sizeof( soundname ), "%s.RunFootstepLeft", options );
  4155. PrecacheSoundHelper( soundname );
  4156. Q_snprintf( soundname, sizeof( soundname ), "%s.RunFootstepRight", options );
  4157. PrecacheSoundHelper( soundname );
  4158. Q_snprintf( soundname, sizeof( soundname ), "%s.FootstepLeft", options );
  4159. PrecacheSoundHelper( soundname );
  4160. Q_snprintf( soundname, sizeof( soundname ), "%s.FootstepRight", options );
  4161. PrecacheSoundHelper( soundname );
  4162. }
  4163. break;
  4164. case AE_CL_PLAYSOUND:
  4165. {
  4166. if ( !( pEvent->type & AE_TYPE_CLIENT ) )
  4167. break;
  4168. if ( pEvent->pszOptions()[0] )
  4169. {
  4170. PrecacheSoundHelper( pEvent->pszOptions() );
  4171. }
  4172. else
  4173. {
  4174. Warning( "-- Error --: empty soundname, .qc error on AE_CL_PLAYSOUND in model %s, sequence %s, animevent # %i\n",
  4175. studioHdr.GetRenderHdr()->pszName(), seq.pszLabel(), j+1 );
  4176. }
  4177. }
  4178. break;
  4179. case CL_EVENT_SOUND:
  4180. case SCRIPT_EVENT_SOUND:
  4181. case SCRIPT_EVENT_SOUND_VOICE:
  4182. {
  4183. PrecacheSoundHelper( pEvent->pszOptions() );
  4184. }
  4185. break;
  4186. }
  4187. }
  4188. }
  4189. }
  4190. }
  4191. }
  4192. }
  4193. //-----------------------------------------------------------------------------
  4194. // Purpose: Add model to level precache list
  4195. // Input : *name - model name
  4196. // Output : int -- model index for model
  4197. //-----------------------------------------------------------------------------
  4198. int CBaseEntity::PrecacheModel( const char *name, bool bPreload )
  4199. {
  4200. if ( !name || !*name )
  4201. {
  4202. #ifdef STAGING_ONLY
  4203. Msg( "Attempting to precache model, but model name is NULL\n");
  4204. #endif
  4205. return -1;
  4206. }
  4207. // Warn on out of order precache
  4208. if ( !CBaseEntity::IsPrecacheAllowed() )
  4209. {
  4210. if ( !engine->IsModelPrecached( name ) )
  4211. {
  4212. DevMsg( "Late precache of %s -- not necessarily a bug now that we allow ~everything to be dynamically loaded.\n", name );
  4213. }
  4214. }
  4215. #if defined( WATCHACCESS )
  4216. else
  4217. {
  4218. g_bWatching = false;
  4219. }
  4220. #endif
  4221. int idx = engine->PrecacheModel( name, bPreload );
  4222. if ( idx != -1 )
  4223. {
  4224. PrecacheModelComponents( idx );
  4225. }
  4226. #if defined( WATCHACCESS )
  4227. g_bWatching = true;
  4228. #endif
  4229. return idx;
  4230. }
  4231. //-----------------------------------------------------------------------------
  4232. // Purpose:
  4233. //-----------------------------------------------------------------------------
  4234. void CBaseEntity::Remove( )
  4235. {
  4236. UTIL_Remove( this );
  4237. }
  4238. // Entity degugging console commands
  4239. extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
  4240. extern void SetDebugBits( CBasePlayer* pPlayer, const char *name, int bit );
  4241. extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent );
  4242. //------------------------------------------------------------------------------
  4243. // Purpose :
  4244. // Input :
  4245. // Output :
  4246. //------------------------------------------------------------------------------
  4247. void ConsoleFireTargets( CBasePlayer *pPlayer, const char *name)
  4248. {
  4249. // If no name was given use the picker
  4250. if (FStrEq(name,""))
  4251. {
  4252. CBaseEntity *pEntity = FindPickerEntity( pPlayer );
  4253. if ( pEntity && !pEntity->IsMarkedForDeletion())
  4254. {
  4255. Msg( "[%03d] Found: %s, firing\n", gpGlobals->tickcount%1000, pEntity->GetDebugName());
  4256. pEntity->Use( pPlayer, pPlayer, USE_TOGGLE, 0 );
  4257. return;
  4258. }
  4259. }
  4260. // Otherwise use name or classname
  4261. FireTargets( name, pPlayer, pPlayer, USE_TOGGLE, 0 );
  4262. }
  4263. //------------------------------------------------------------------------------
  4264. // Purpose :
  4265. // Input :
  4266. // Output :
  4267. //------------------------------------------------------------------------------
  4268. void CC_Ent_Name( const CCommand& args )
  4269. {
  4270. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_NAME_BIT);
  4271. }
  4272. static ConCommand ent_name("ent_name", CC_Ent_Name, 0, FCVAR_CHEAT);
  4273. //------------------------------------------------------------------------------
  4274. void CC_Ent_Text( const CCommand& args )
  4275. {
  4276. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_TEXT_BIT);
  4277. }
  4278. static ConCommand ent_text("ent_text", CC_Ent_Text, "Displays text debugging information about the given entity(ies) on top of the entity (See Overlay Text)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4279. //------------------------------------------------------------------------------
  4280. void CC_Ent_BBox( const CCommand& args )
  4281. {
  4282. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_BBOX_BIT);
  4283. }
  4284. static ConCommand ent_bbox("ent_bbox", CC_Ent_BBox, "Displays the movement bounding box for the given entity(ies) in orange. Some entites will also display entity specific overlays.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4285. //------------------------------------------------------------------------------
  4286. void CC_Ent_AbsBox( const CCommand& args )
  4287. {
  4288. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_ABSBOX_BIT);
  4289. }
  4290. static ConCommand ent_absbox("ent_absbox", CC_Ent_AbsBox, "Displays the total bounding box for the given entity(s) in green. Some entites will also display entity specific overlays.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4291. //------------------------------------------------------------------------------
  4292. void CC_Ent_RBox( const CCommand& args )
  4293. {
  4294. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_RBOX_BIT);
  4295. }
  4296. static ConCommand ent_rbox("ent_rbox", CC_Ent_RBox, "Displays the total bounding box for the given entity(s) in green. Some entites will also display entity specific overlays.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4297. //------------------------------------------------------------------------------
  4298. void CC_Ent_AttachmentPoints( const CCommand& args )
  4299. {
  4300. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_ATTACHMENTS_BIT);
  4301. }
  4302. static ConCommand ent_attachments("ent_attachments", CC_Ent_AttachmentPoints, "Displays the attachment points on an entity.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4303. //------------------------------------------------------------------------------
  4304. void CC_Ent_ViewOffset( const CCommand& args )
  4305. {
  4306. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_VIEWOFFSET);
  4307. }
  4308. static ConCommand ent_viewoffset("ent_viewoffset", CC_Ent_ViewOffset, "Displays the eye position for the given entity(ies) in red.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4309. //------------------------------------------------------------------------------
  4310. void CC_Ent_Remove( const CCommand& args )
  4311. {
  4312. CBaseEntity *pEntity = NULL;
  4313. // If no name was given set bits based on the picked
  4314. if ( FStrEq( args[1],"") )
  4315. {
  4316. pEntity = FindPickerEntity( UTIL_GetCommandClient() );
  4317. }
  4318. else
  4319. {
  4320. int index = atoi( args[1] );
  4321. if ( index )
  4322. {
  4323. pEntity = CBaseEntity::Instance( index );
  4324. }
  4325. else
  4326. {
  4327. // Otherwise set bits based on name or classname
  4328. CBaseEntity *ent = NULL;
  4329. while ( (ent = gEntList.NextEnt(ent)) != NULL )
  4330. {
  4331. if ( (ent->GetEntityName() != NULL_STRING && FStrEq(args[1], STRING(ent->GetEntityName()))) ||
  4332. (ent->m_iClassname != NULL_STRING && FStrEq(args[1], STRING(ent->m_iClassname))) ||
  4333. (ent->GetClassname()!=NULL && FStrEq(args[1], ent->GetClassname())))
  4334. {
  4335. pEntity = ent;
  4336. break;
  4337. }
  4338. }
  4339. }
  4340. }
  4341. // Found one?
  4342. if ( pEntity )
  4343. {
  4344. Msg( "Removed %s(%s)\n", STRING(pEntity->m_iClassname), pEntity->GetDebugName() );
  4345. UTIL_Remove( pEntity );
  4346. }
  4347. }
  4348. static ConCommand ent_remove("ent_remove", CC_Ent_Remove, "Removes the given entity(s)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4349. //------------------------------------------------------------------------------
  4350. void CC_Ent_RemoveAll( const CCommand& args )
  4351. {
  4352. // If no name was given remove based on the picked
  4353. if ( args.ArgC() < 2 )
  4354. {
  4355. Msg( "Removes all entities of the specified type\n\tArguments: {entity_name} / {class_name}\n" );
  4356. }
  4357. else
  4358. {
  4359. // Otherwise remove based on name or classname
  4360. int iCount = 0;
  4361. CBaseEntity *ent = NULL;
  4362. while ( (ent = gEntList.NextEnt(ent)) != NULL )
  4363. {
  4364. if ( (ent->GetEntityName() != NULL_STRING && FStrEq(args[1], STRING(ent->GetEntityName()))) ||
  4365. (ent->m_iClassname != NULL_STRING && FStrEq(args[1], STRING(ent->m_iClassname))) ||
  4366. (ent->GetClassname()!=NULL && FStrEq(args[1], ent->GetClassname())))
  4367. {
  4368. UTIL_Remove( ent );
  4369. iCount++;
  4370. }
  4371. }
  4372. if ( iCount )
  4373. {
  4374. Msg( "Removed %d %s's\n", iCount, args[1] );
  4375. }
  4376. else
  4377. {
  4378. Msg( "No %s found.\n", args[1] );
  4379. }
  4380. }
  4381. }
  4382. static ConCommand ent_remove_all("ent_remove_all", CC_Ent_RemoveAll, "Removes all entities of the specified type\n\tArguments: {entity_name} / {class_name} ", FCVAR_CHEAT);
  4383. //------------------------------------------------------------------------------
  4384. void CC_Ent_SetName( const CCommand& args )
  4385. {
  4386. CBaseEntity *pEntity = NULL;
  4387. if ( args.ArgC() < 1 )
  4388. {
  4389. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  4390. if (!pPlayer)
  4391. return;
  4392. ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage:\n ent_setname <new name> <entity name>\n" );
  4393. }
  4394. else
  4395. {
  4396. // If no name was given set bits based on the picked
  4397. if ( FStrEq( args[2],"") )
  4398. {
  4399. pEntity = FindPickerEntity( UTIL_GetCommandClient() );
  4400. }
  4401. else
  4402. {
  4403. // Otherwise set bits based on name or classname
  4404. CBaseEntity *ent = NULL;
  4405. while ( (ent = gEntList.NextEnt(ent)) != NULL )
  4406. {
  4407. if ( (ent->GetEntityName() != NULL_STRING && FStrEq(args[1], STRING(ent->GetEntityName()))) ||
  4408. (ent->m_iClassname != NULL_STRING && FStrEq(args[1], STRING(ent->m_iClassname))) ||
  4409. (ent->GetClassname()!=NULL && FStrEq(args[1], ent->GetClassname())))
  4410. {
  4411. pEntity = ent;
  4412. break;
  4413. }
  4414. }
  4415. }
  4416. // Found one?
  4417. if ( pEntity )
  4418. {
  4419. Msg( "Set the name of %s to %s\n", STRING(pEntity->m_iClassname), args[1] );
  4420. pEntity->SetName( AllocPooledString( args[1] ) );
  4421. }
  4422. }
  4423. }
  4424. static ConCommand ent_setname("ent_setname", CC_Ent_SetName, "Sets the targetname of the given entity(s)\n\tArguments: {new entity name} {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4425. //------------------------------------------------------------------------------
  4426. void CC_Find_Ent( const CCommand& args )
  4427. {
  4428. if ( args.ArgC() < 2 )
  4429. {
  4430. Msg( "Total entities: %d (%d edicts)\n", gEntList.NumberOfEntities(), gEntList.NumberOfEdicts() );
  4431. Msg( "Format: find_ent <substring>\n" );
  4432. return;
  4433. }
  4434. int iCount = 0;
  4435. const char *pszSubString = args[1];
  4436. Msg("Searching for entities with class/target name containing substring: '%s'\n", pszSubString );
  4437. CBaseEntity *ent = NULL;
  4438. while ( (ent = gEntList.NextEnt(ent)) != NULL )
  4439. {
  4440. const char *pszClassname = ent->GetClassname();
  4441. const char *pszTargetname = STRING(ent->GetEntityName());
  4442. bool bMatches = false;
  4443. if ( pszClassname && pszClassname[0] )
  4444. {
  4445. if ( Q_stristr( pszClassname, pszSubString ) )
  4446. {
  4447. bMatches = true;
  4448. }
  4449. }
  4450. if ( !bMatches && pszTargetname && pszTargetname[0] )
  4451. {
  4452. if ( Q_stristr( pszTargetname, pszSubString ) )
  4453. {
  4454. bMatches = true;
  4455. }
  4456. }
  4457. if ( bMatches )
  4458. {
  4459. iCount++;
  4460. Msg(" '%s' : '%s' (entindex %d) \n", ent->GetClassname(), ent->GetEntityName().ToCStr(), ent->entindex() );
  4461. }
  4462. }
  4463. Msg("Found %d matches.\n", iCount);
  4464. }
  4465. static ConCommand find_ent("find_ent", CC_Find_Ent, "Find and list all entities with classnames or targetnames that contain the specified substring.\nFormat: find_ent <substring>\n", FCVAR_CHEAT);
  4466. //------------------------------------------------------------------------------
  4467. void CC_Find_Ent_Index( const CCommand& args )
  4468. {
  4469. if ( args.ArgC() < 2 )
  4470. {
  4471. Msg( "Format: find_ent_index <index>\n" );
  4472. return;
  4473. }
  4474. int iIndex = atoi(args[1]);
  4475. CBaseEntity *pEnt = UTIL_EntityByIndex( iIndex );
  4476. if ( pEnt )
  4477. {
  4478. Msg(" '%s' : '%s' (entindex %d) \n", pEnt->GetClassname(), pEnt->GetEntityName().ToCStr(), iIndex );
  4479. }
  4480. else
  4481. {
  4482. Msg("Found no entity at %d.\n", iIndex);
  4483. }
  4484. }
  4485. static ConCommand find_ent_index("find_ent_index", CC_Find_Ent_Index, "Display data for entity matching specified index.\nFormat: find_ent_index <index>\n", FCVAR_CHEAT);
  4486. // Purpose :
  4487. //------------------------------------------------------------------------------
  4488. void CC_Ent_Dump( const CCommand& args )
  4489. {
  4490. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  4491. if (!pPlayer)
  4492. {
  4493. return;
  4494. }
  4495. if ( args.ArgC() < 2 )
  4496. {
  4497. ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage:\n ent_dump <entity name>\n" );
  4498. }
  4499. else
  4500. {
  4501. // iterate through all the ents of this name, printing out their details
  4502. CBaseEntity *ent = NULL;
  4503. bool bFound = false;
  4504. while ( ( ent = gEntList.FindEntityByName(ent, args[1] ) ) != NULL )
  4505. {
  4506. bFound = true;
  4507. for ( datamap_t *dmap = ent->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  4508. {
  4509. // search through all the actions in the data description, printing out details
  4510. for ( int i = 0; i < dmap->dataNumFields; i++ )
  4511. {
  4512. variant_t var;
  4513. if ( ent->ReadKeyField( dmap->dataDesc[i].externalName, &var) )
  4514. {
  4515. char buf[256];
  4516. buf[0] = 0;
  4517. switch( var.FieldType() )
  4518. {
  4519. case FIELD_STRING:
  4520. Q_strncpy( buf, var.String() ,sizeof(buf));
  4521. break;
  4522. case FIELD_INTEGER:
  4523. if ( var.Int() )
  4524. Q_snprintf( buf,sizeof(buf), "%d", var.Int() );
  4525. break;
  4526. case FIELD_FLOAT:
  4527. if ( var.Float() )
  4528. Q_snprintf( buf,sizeof(buf), "%.2f", var.Float() );
  4529. break;
  4530. case FIELD_EHANDLE:
  4531. {
  4532. // get the entities name
  4533. if ( var.Entity() )
  4534. {
  4535. Q_snprintf( buf,sizeof(buf), "%s", STRING(var.Entity()->GetEntityName()) );
  4536. }
  4537. }
  4538. break;
  4539. }
  4540. // don't print out the duplicate keys
  4541. if ( !Q_stricmp("parentname",dmap->dataDesc[i].externalName) || !Q_stricmp("targetname",dmap->dataDesc[i].externalName) )
  4542. continue;
  4543. // don't print out empty keys
  4544. if ( buf[0] )
  4545. {
  4546. ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs(" %s: %s\n", dmap->dataDesc[i].externalName, buf) );
  4547. }
  4548. }
  4549. }
  4550. }
  4551. }
  4552. if ( !bFound )
  4553. {
  4554. ClientPrint( pPlayer, HUD_PRINTCONSOLE, "ent_dump: no such entity" );
  4555. }
  4556. }
  4557. }
  4558. static ConCommand ent_dump("ent_dump", CC_Ent_Dump, "Usage:\n ent_dump <entity name>\n", FCVAR_CHEAT);
  4559. //------------------------------------------------------------------------------
  4560. // Purpose :
  4561. // Input :
  4562. // Output :
  4563. //------------------------------------------------------------------------------
  4564. void CC_Ent_FireTarget( const CCommand& args )
  4565. {
  4566. ConsoleFireTargets(UTIL_GetCommandClient(),args[1]);
  4567. }
  4568. static ConCommand firetarget("firetarget", CC_Ent_FireTarget, 0, FCVAR_CHEAT);
  4569. class CEntFireAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback
  4570. {
  4571. public:
  4572. virtual void CommandCallback( const CCommand &command )
  4573. {
  4574. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  4575. if (!pPlayer)
  4576. {
  4577. return;
  4578. }
  4579. // fires a command from the console
  4580. if ( command.ArgC() < 2 )
  4581. {
  4582. ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage:\n ent_fire <target> [action] [value] [delay]\n" );
  4583. }
  4584. else
  4585. {
  4586. const char *target = "", *action = "Use";
  4587. variant_t value;
  4588. int delay = 0;
  4589. target = STRING( AllocPooledString(command.Arg( 1 ) ) );
  4590. // Don't allow them to run anything on a point_servercommand unless they're the host player. Otherwise they can ent_fire
  4591. // and run any command on the server. Admittedly, they can only do the ent_fire if sv_cheats is on, but
  4592. // people complained about users resetting the rcon password if the server briefly turned on cheats like this:
  4593. // give point_servercommand
  4594. // ent_fire point_servercommand command "rcon_password mynewpassword"
  4595. //
  4596. // Robin: Unfortunately, they get around point_servercommand checks with this:
  4597. // ent_create point_servercommand; ent_setname mine; ent_fire mine command "rcon_password mynewpassword"
  4598. // So, I'm removing the ability for anyone to execute ent_fires on dedicated servers (we can't check to see if
  4599. // this command is going to connect with a point_servercommand entity here, because they could delay the event and create it later).
  4600. if ( engine->IsDedicatedServer() )
  4601. {
  4602. // We allow people with disabled autokick to do it, because they already have rcon.
  4603. if ( pPlayer->IsAutoKickDisabled() == false )
  4604. return;
  4605. }
  4606. else if ( gpGlobals->maxClients > 1 )
  4607. {
  4608. // On listen servers with more than 1 player, only allow the host to issue ent_fires.
  4609. CBasePlayer *pHostPlayer = UTIL_GetListenServerHost();
  4610. if ( pPlayer != pHostPlayer )
  4611. return;
  4612. }
  4613. if ( command.ArgC() >= 3 )
  4614. {
  4615. action = STRING( AllocPooledString(command.Arg( 2 )) );
  4616. }
  4617. if ( command.ArgC() >= 4 )
  4618. {
  4619. value.SetString( AllocPooledString(command.Arg( 3 )) );
  4620. }
  4621. if ( command.ArgC() >= 5 )
  4622. {
  4623. delay = atoi( command.Arg( 4 ) );
  4624. }
  4625. g_EventQueue.AddEvent( target, action, value, delay, pPlayer, pPlayer );
  4626. }
  4627. }
  4628. virtual int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands )
  4629. {
  4630. if ( !g_pGameRules )
  4631. {
  4632. return 0;
  4633. }
  4634. const char *cmdname = "ent_fire";
  4635. char *substring = (char *)partial;
  4636. if ( Q_strstr( partial, cmdname ) )
  4637. {
  4638. substring = (char *)partial + strlen( cmdname ) + 1;
  4639. }
  4640. int checklen = 0;
  4641. char *space = Q_strstr( substring, " " );
  4642. if ( space )
  4643. {
  4644. return EntFire_AutoCompleteInput( partial, commands );;
  4645. }
  4646. else
  4647. {
  4648. checklen = Q_strlen( substring );
  4649. }
  4650. CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc );
  4651. CBaseEntity *pos = NULL;
  4652. while ( ( pos = gEntList.NextEnt( pos ) ) != NULL )
  4653. {
  4654. // Check target name against partial string
  4655. if ( pos->GetEntityName() == NULL_STRING )
  4656. continue;
  4657. if ( Q_strnicmp( STRING( pos->GetEntityName() ), substring, checklen ) )
  4658. continue;
  4659. CUtlString sym = STRING( pos->GetEntityName() );
  4660. int idx = symbols.Find( sym );
  4661. if ( idx == symbols.InvalidIndex() )
  4662. {
  4663. symbols.Insert( sym );
  4664. }
  4665. // Too many
  4666. if ( symbols.Count() >= COMMAND_COMPLETION_MAXITEMS )
  4667. break;
  4668. }
  4669. // Now fill in the results
  4670. for ( int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder( i ) )
  4671. {
  4672. const char *name = symbols[ i ].String();
  4673. char buf[ 512 ];
  4674. Q_strncpy( buf, name, sizeof( buf ) );
  4675. Q_strlower( buf );
  4676. CUtlString command;
  4677. command = CFmtStr( "%s %s", cmdname, buf );
  4678. commands.AddToTail( command );
  4679. }
  4680. return symbols.Count();
  4681. }
  4682. private:
  4683. int EntFire_AutoCompleteInput( const char *partial, CUtlVector< CUtlString > &commands )
  4684. {
  4685. const char *cmdname = "ent_fire";
  4686. char *substring = (char *)partial;
  4687. if ( Q_strstr( partial, cmdname ) )
  4688. {
  4689. substring = (char *)partial + strlen( cmdname ) + 1;
  4690. }
  4691. int checklen = 0;
  4692. char *space = Q_strstr( substring, " " );
  4693. if ( !space )
  4694. {
  4695. Assert( !"CC_EntFireAutoCompleteInputFunc is broken\n" );
  4696. return 0;
  4697. }
  4698. checklen = Q_strlen( substring );
  4699. char targetEntity[ 256 ];
  4700. targetEntity[0] = 0;
  4701. int nEntityNameLength = (space-substring);
  4702. Q_strncat( targetEntity, substring, sizeof( targetEntity ), nEntityNameLength );
  4703. // Find the target entity by name
  4704. CBaseEntity *target = gEntList.FindEntityByName( NULL, targetEntity );
  4705. if ( target == NULL )
  4706. return 0;
  4707. CUtlRBTree< CUtlString > symbols( 0, 0, UtlStringLessFunc );
  4708. // Find the next portion of the text chain, if any (removing space)
  4709. int nInputNameLength = (checklen-nEntityNameLength-1);
  4710. // Starting past the last space, this is the remainder of the string
  4711. char *inputPartial = ( checklen > nEntityNameLength ) ? (space+1) : NULL;
  4712. for ( datamap_t *dmap = target->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  4713. {
  4714. // Make sure we don't keep adding things in if the satisfied the limit
  4715. if ( symbols.Count() >= COMMAND_COMPLETION_MAXITEMS )
  4716. break;
  4717. int c = dmap->dataNumFields;
  4718. for ( int i = 0; i < c; i++ )
  4719. {
  4720. typedescription_t *field = &dmap->dataDesc[ i ];
  4721. // Only want inputs
  4722. if ( !( field->flags & FTYPEDESC_INPUT ) )
  4723. continue;
  4724. // Only want input functions
  4725. if ( field->flags & FTYPEDESC_SAVE )
  4726. continue;
  4727. // See if we've got a partial string for the input name already
  4728. if ( inputPartial != NULL )
  4729. {
  4730. if ( Q_strnicmp( inputPartial, field->externalName, nInputNameLength ) )
  4731. continue;
  4732. }
  4733. CUtlString sym = field->externalName;
  4734. int idx = symbols.Find( sym );
  4735. if ( idx == symbols.InvalidIndex() )
  4736. {
  4737. symbols.Insert( sym );
  4738. }
  4739. // Too many items have been added
  4740. if ( symbols.Count() >= COMMAND_COMPLETION_MAXITEMS )
  4741. break;
  4742. }
  4743. }
  4744. // Now fill in the results
  4745. for ( int i = symbols.FirstInorder(); i != symbols.InvalidIndex(); i = symbols.NextInorder( i ) )
  4746. {
  4747. const char *name = symbols[ i ].String();
  4748. char buf[ 512 ];
  4749. Q_strncpy( buf, name, sizeof( buf ) );
  4750. Q_strlower( buf );
  4751. CUtlString command;
  4752. command = CFmtStr( "%s %s %s", cmdname, targetEntity, buf );
  4753. commands.AddToTail( command );
  4754. }
  4755. return symbols.Count();
  4756. }
  4757. };
  4758. static CEntFireAutoCompletionFunctor g_EntFireAutoComplete;
  4759. static ConCommand ent_fire("ent_fire", &g_EntFireAutoComplete, "Usage:\n ent_fire <target> [action] [value] [delay]\n", FCVAR_CHEAT, &g_EntFireAutoComplete );
  4760. void CC_Ent_CancelPendingEntFires( const CCommand& args )
  4761. {
  4762. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  4763. return;
  4764. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  4765. if (!pPlayer)
  4766. return;
  4767. g_EventQueue.CancelEvents( pPlayer );
  4768. }
  4769. static ConCommand ent_cancelpendingentfires("ent_cancelpendingentfires", CC_Ent_CancelPendingEntFires, "Cancels all ent_fire created outputs that are currently waiting for their delay to expire." );
  4770. //------------------------------------------------------------------------------
  4771. // Purpose :
  4772. // Input :
  4773. // Output :
  4774. //------------------------------------------------------------------------------
  4775. void CC_Ent_Info( const CCommand& args )
  4776. {
  4777. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  4778. if (!pPlayer)
  4779. {
  4780. return;
  4781. }
  4782. if ( args.ArgC() < 2 )
  4783. {
  4784. ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage:\n ent_info <class name>\n" );
  4785. }
  4786. else
  4787. {
  4788. // iterate through all the ents printing out their details
  4789. CBaseEntity *ent = CreateEntityByName( args[1] );
  4790. if ( ent )
  4791. {
  4792. datamap_t *dmap;
  4793. for ( dmap = ent->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  4794. {
  4795. // search through all the actions in the data description, printing out details
  4796. for ( int i = 0; i < dmap->dataNumFields; i++ )
  4797. {
  4798. if ( dmap->dataDesc[i].flags & FTYPEDESC_OUTPUT )
  4799. {
  4800. ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs(" output: %s\n", dmap->dataDesc[i].externalName) );
  4801. }
  4802. }
  4803. }
  4804. for ( dmap = ent->GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  4805. {
  4806. // search through all the actions in the data description, printing out details
  4807. for ( int i = 0; i < dmap->dataNumFields; i++ )
  4808. {
  4809. if ( dmap->dataDesc[i].flags & FTYPEDESC_INPUT )
  4810. {
  4811. ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs(" input: %s\n", dmap->dataDesc[i].externalName) );
  4812. }
  4813. }
  4814. }
  4815. delete ent;
  4816. }
  4817. else
  4818. {
  4819. ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs("no such entity %s\n", args[1]) );
  4820. }
  4821. }
  4822. }
  4823. static ConCommand ent_info("ent_info", CC_Ent_Info, "Usage:\n ent_info <class name>\n", FCVAR_CHEAT);
  4824. //------------------------------------------------------------------------------
  4825. // Purpose :
  4826. // Input :
  4827. // Output :
  4828. //------------------------------------------------------------------------------
  4829. void CC_Ent_Messages( const CCommand& args )
  4830. {
  4831. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_MESSAGE_BIT);
  4832. }
  4833. static ConCommand ent_messages("ent_messages", CC_Ent_Messages ,"Toggles input/output message display for the selected entity(ies). The name of the entity will be displayed as well as any messages that it sends or receives.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
  4834. //------------------------------------------------------------------------------
  4835. // Purpose :
  4836. // Input :
  4837. // Output :
  4838. //------------------------------------------------------------------------------
  4839. void CC_Ent_Pause( void )
  4840. {
  4841. if (CBaseEntity::Debug_IsPaused())
  4842. {
  4843. Msg( "Resuming entity I/O events\n" );
  4844. CBaseEntity::Debug_Pause(false);
  4845. }
  4846. else
  4847. {
  4848. Msg( "Pausing entity I/O events\n" );
  4849. CBaseEntity::Debug_Pause(true);
  4850. }
  4851. }
  4852. static ConCommand ent_pause("ent_pause", CC_Ent_Pause, "Toggles pausing of input/output message processing for entities. When turned on processing of all message will stop. Any messages displayed with 'ent_messages' will stop fading and be displayed indefinitely. To step through the messages one by one use 'ent_step'.", FCVAR_CHEAT);
  4853. //------------------------------------------------------------------------------
  4854. // Purpose : Enables the entity picker, revelaing debug information about the
  4855. // entity under the crosshair.
  4856. // Input : an optional command line argument "full" enables all debug info.
  4857. // Output :
  4858. //------------------------------------------------------------------------------
  4859. void CC_Ent_Picker( void )
  4860. {
  4861. CBaseEntity::m_bInDebugSelect = CBaseEntity::m_bInDebugSelect ? false : true;
  4862. // Remember the player that's making this request
  4863. CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
  4864. }
  4865. static ConCommand picker("picker", CC_Ent_Picker, "Toggles 'picker' mode. When picker is on, the bounding box, pivot and debugging text is displayed for whatever entity the player is looking at.\n\tArguments: full - enables all debug information", FCVAR_CHEAT);
  4866. //------------------------------------------------------------------------------
  4867. // Purpose :
  4868. // Input :
  4869. // Output :
  4870. //------------------------------------------------------------------------------
  4871. void CC_Ent_Pivot( const CCommand& args )
  4872. {
  4873. SetDebugBits(UTIL_GetCommandClient(),args[1],OVERLAY_PIVOT_BIT);
  4874. }
  4875. static ConCommand ent_pivot("ent_pivot", CC_Ent_Pivot, "Displays the pivot for the given entity(ies).\n\t(y=up=green, z=forward=blue, x=left=red). \n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  4876. //------------------------------------------------------------------------------
  4877. // Purpose :
  4878. // Input :
  4879. // Output :
  4880. //------------------------------------------------------------------------------
  4881. void CC_Ent_Step( const CCommand& args )
  4882. {
  4883. int nSteps = atoi(args[1]);
  4884. if (nSteps <= 0)
  4885. {
  4886. nSteps = 1;
  4887. }
  4888. CBaseEntity::Debug_SetSteps(nSteps);
  4889. }
  4890. static ConCommand ent_step("ent_step", CC_Ent_Step, "When 'ent_pause' is set this will step through one waiting input / output message at a time.", FCVAR_CHEAT);
  4891. void CBaseEntity::SetCheckUntouch( bool check )
  4892. {
  4893. // Invalidate touchstamp
  4894. if ( check )
  4895. {
  4896. touchStamp++;
  4897. if ( !IsEFlagSet( EFL_CHECK_UNTOUCH ) )
  4898. {
  4899. AddEFlags( EFL_CHECK_UNTOUCH );
  4900. EntityTouch_Add( this );
  4901. }
  4902. }
  4903. else
  4904. {
  4905. RemoveEFlags( EFL_CHECK_UNTOUCH );
  4906. }
  4907. }
  4908. model_t *CBaseEntity::GetModel( void )
  4909. {
  4910. return (model_t *)modelinfo->GetModel( GetModelIndex() );
  4911. }
  4912. //-----------------------------------------------------------------------------
  4913. // Purpose: Calculates the absolute position of an edict in the world
  4914. // assumes the parent's absolute origin has already been calculated
  4915. //-----------------------------------------------------------------------------
  4916. void CBaseEntity::CalcAbsolutePosition( void )
  4917. {
  4918. if (!IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ))
  4919. return;
  4920. {
  4921. AUTO_LOCK( m_CalcAbsolutePositionMutex );
  4922. // Test again under the lock, in case another thread did the work in the interim
  4923. if ( !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) )
  4924. {
  4925. return;
  4926. }
  4927. // Plop the entity->parent matrix into m_rgflCoordinateFrame
  4928. AngleMatrix( m_angRotation, m_vecOrigin, m_rgflCoordinateFrame );
  4929. CBaseEntity *pMoveParent = GetMoveParent();
  4930. if ( !pMoveParent )
  4931. {
  4932. // no move parent, so just copy existing values
  4933. m_vecAbsOrigin = m_vecOrigin;
  4934. m_angAbsRotation = m_angRotation;
  4935. }
  4936. else
  4937. {
  4938. // concatenate with our parent's transform
  4939. matrix3x4_t tmpMatrix, scratchSpace;
  4940. ConcatTransforms( GetParentToWorldTransform( scratchSpace ), m_rgflCoordinateFrame, tmpMatrix );
  4941. MatrixCopy( tmpMatrix, m_rgflCoordinateFrame );
  4942. // pull our absolute position out of the matrix
  4943. MatrixGetColumn( m_rgflCoordinateFrame, 3, m_vecAbsOrigin );
  4944. // if we have any angles, we have to extract our absolute angles from our matrix
  4945. if ( ( m_angRotation == vec3_angle ) && ( m_iParentAttachment == 0 ) )
  4946. {
  4947. // just copy our parent's absolute angles
  4948. VectorCopy( pMoveParent->GetAbsAngles(), m_angAbsRotation );
  4949. }
  4950. else
  4951. {
  4952. MatrixAngles( m_rgflCoordinateFrame, m_angAbsRotation );
  4953. }
  4954. }
  4955. ThreadMemoryBarrier();
  4956. RemoveEFlags( EFL_DIRTY_ABSTRANSFORM );
  4957. }
  4958. // Do this callback *after* we have updated the position, and (importantly) after we clear the dirty flag, because this callback can potentially
  4959. // end up recursively calling back in here, so the dirty flag must be cleared to break the recursion in that case.
  4960. if ( HasDataObjectType( POSITIONWATCHER ) )
  4961. {
  4962. ReportPositionChanged( this );
  4963. }
  4964. }
  4965. void CBaseEntity::CalcAbsoluteVelocity()
  4966. {
  4967. if (!IsEFlagSet( EFL_DIRTY_ABSVELOCITY ))
  4968. return;
  4969. RemoveEFlags( EFL_DIRTY_ABSVELOCITY );
  4970. CBaseEntity *pMoveParent = GetMoveParent();
  4971. if ( !pMoveParent )
  4972. {
  4973. m_vecAbsVelocity = m_vecVelocity;
  4974. return;
  4975. }
  4976. // This transforms the local velocity into world space
  4977. VectorRotate( m_vecVelocity, pMoveParent->EntityToWorldTransform(), m_vecAbsVelocity );
  4978. // Now add in the parent abs velocity
  4979. m_vecAbsVelocity += pMoveParent->GetAbsVelocity();
  4980. }
  4981. // FIXME: While we're using (dPitch, dYaw, dRoll) as our local angular velocity
  4982. // representation, we can't actually solve this problem
  4983. /*
  4984. void CBaseEntity::CalcAbsoluteAngularVelocity()
  4985. {
  4986. if (!IsEFlagSet( EFL_DIRTY_ABSANGVELOCITY ))
  4987. return;
  4988. RemoveEFlags( EFL_DIRTY_ABSANGVELOCITY );
  4989. CBaseEntity *pMoveParent = GetMoveParent();
  4990. if ( !pMoveParent )
  4991. {
  4992. m_vecAbsAngVelocity = m_vecAngVelocity;
  4993. return;
  4994. }
  4995. // This transforms the local ang velocity into world space
  4996. matrix3x4_t angVelToParent, angVelToWorld;
  4997. AngleMatrix( m_vecAngVelocity, angVelToParent );
  4998. ConcatTransforms( pMoveParent->EntityToWorldTransform(), angVelToParent, angVelToWorld );
  4999. MatrixAngles( angVelToWorld, m_vecAbsAngVelocity );
  5000. }
  5001. */
  5002. //-----------------------------------------------------------------------------
  5003. // Computes the abs position of a point specified in local space
  5004. //-----------------------------------------------------------------------------
  5005. void CBaseEntity::ComputeAbsPosition( const Vector &vecLocalPosition, Vector *pAbsPosition )
  5006. {
  5007. CBaseEntity *pMoveParent = GetMoveParent();
  5008. if ( !pMoveParent )
  5009. {
  5010. *pAbsPosition = vecLocalPosition;
  5011. }
  5012. else
  5013. {
  5014. VectorTransform( vecLocalPosition, pMoveParent->EntityToWorldTransform(), *pAbsPosition );
  5015. }
  5016. }
  5017. //-----------------------------------------------------------------------------
  5018. // Computes the abs position of a point specified in local space
  5019. //-----------------------------------------------------------------------------
  5020. void CBaseEntity::ComputeAbsDirection( const Vector &vecLocalDirection, Vector *pAbsDirection )
  5021. {
  5022. CBaseEntity *pMoveParent = GetMoveParent();
  5023. if ( !pMoveParent )
  5024. {
  5025. *pAbsDirection = vecLocalDirection;
  5026. }
  5027. else
  5028. {
  5029. VectorRotate( vecLocalDirection, pMoveParent->EntityToWorldTransform(), *pAbsDirection );
  5030. }
  5031. }
  5032. matrix3x4_t& CBaseEntity::GetParentToWorldTransform( matrix3x4_t &tempMatrix )
  5033. {
  5034. CBaseEntity *pMoveParent = GetMoveParent();
  5035. if ( !pMoveParent )
  5036. {
  5037. Assert( false );
  5038. SetIdentityMatrix( tempMatrix );
  5039. return tempMatrix;
  5040. }
  5041. if ( m_iParentAttachment != 0 )
  5042. {
  5043. MDLCACHE_CRITICAL_SECTION();
  5044. CBaseAnimating *pAnimating = pMoveParent->GetBaseAnimating();
  5045. if ( pAnimating && pAnimating->GetAttachment( m_iParentAttachment, tempMatrix ) )
  5046. {
  5047. return tempMatrix;
  5048. }
  5049. }
  5050. // If we fall through to here, then just use the move parent's abs origin and angles.
  5051. return pMoveParent->EntityToWorldTransform();
  5052. }
  5053. //-----------------------------------------------------------------------------
  5054. // These methods recompute local versions as well as set abs versions
  5055. //-----------------------------------------------------------------------------
  5056. void CBaseEntity::SetAbsOrigin( const Vector& absOrigin )
  5057. {
  5058. AssertMsg( absOrigin.IsValid(), "Invalid origin set" );
  5059. // This is necessary to get the other fields of m_rgflCoordinateFrame ok
  5060. CalcAbsolutePosition();
  5061. if ( m_vecAbsOrigin == absOrigin )
  5062. return;
  5063. // All children are invalid, but we are not
  5064. InvalidatePhysicsRecursive( POSITION_CHANGED );
  5065. RemoveEFlags( EFL_DIRTY_ABSTRANSFORM );
  5066. m_vecAbsOrigin = absOrigin;
  5067. MatrixSetColumn( absOrigin, 3, m_rgflCoordinateFrame );
  5068. Vector vecNewOrigin;
  5069. CBaseEntity *pMoveParent = GetMoveParent();
  5070. if (!pMoveParent)
  5071. {
  5072. vecNewOrigin = absOrigin;
  5073. }
  5074. else
  5075. {
  5076. matrix3x4_t tempMat;
  5077. matrix3x4_t &parentTransform = GetParentToWorldTransform( tempMat );
  5078. // Moveparent case: transform the abs position into local space
  5079. VectorITransform( absOrigin, parentTransform, vecNewOrigin );
  5080. }
  5081. if (m_vecOrigin != vecNewOrigin)
  5082. {
  5083. m_vecOrigin = vecNewOrigin;
  5084. SetSimulationTime( gpGlobals->curtime );
  5085. }
  5086. }
  5087. void CBaseEntity::SetAbsAngles( const QAngle& absAngles )
  5088. {
  5089. // This is necessary to get the other fields of m_rgflCoordinateFrame ok
  5090. CalcAbsolutePosition();
  5091. // FIXME: The normalize caused problems in server code like momentary_rot_button that isn't
  5092. // handling things like +/-180 degrees properly. This should be revisited.
  5093. //QAngle angleNormalize( AngleNormalize( absAngles.x ), AngleNormalize( absAngles.y ), AngleNormalize( absAngles.z ) );
  5094. if ( m_angAbsRotation == absAngles )
  5095. return;
  5096. // All children are invalid, but we are not
  5097. InvalidatePhysicsRecursive( ANGLES_CHANGED );
  5098. RemoveEFlags( EFL_DIRTY_ABSTRANSFORM );
  5099. m_angAbsRotation = absAngles;
  5100. AngleMatrix( absAngles, m_rgflCoordinateFrame );
  5101. MatrixSetColumn( m_vecAbsOrigin, 3, m_rgflCoordinateFrame );
  5102. QAngle angNewRotation;
  5103. CBaseEntity *pMoveParent = GetMoveParent();
  5104. if (!pMoveParent)
  5105. {
  5106. angNewRotation = absAngles;
  5107. }
  5108. else
  5109. {
  5110. if ( m_angAbsRotation == pMoveParent->GetAbsAngles() )
  5111. {
  5112. angNewRotation.Init( );
  5113. }
  5114. else
  5115. {
  5116. // Moveparent case: transform the abs transform into local space
  5117. matrix3x4_t worldToParent, localMatrix;
  5118. MatrixInvert( pMoveParent->EntityToWorldTransform(), worldToParent );
  5119. ConcatTransforms( worldToParent, m_rgflCoordinateFrame, localMatrix );
  5120. MatrixAngles( localMatrix, angNewRotation );
  5121. }
  5122. }
  5123. if (m_angRotation != angNewRotation)
  5124. {
  5125. m_angRotation = angNewRotation;
  5126. SetSimulationTime( gpGlobals->curtime );
  5127. }
  5128. }
  5129. void CBaseEntity::SetAbsVelocity( const Vector &vecAbsVelocity )
  5130. {
  5131. if ( m_vecAbsVelocity == vecAbsVelocity )
  5132. return;
  5133. // The abs velocity won't be dirty since we're setting it here
  5134. // All children are invalid, but we are not
  5135. InvalidatePhysicsRecursive( VELOCITY_CHANGED );
  5136. RemoveEFlags( EFL_DIRTY_ABSVELOCITY );
  5137. m_vecAbsVelocity = vecAbsVelocity;
  5138. // NOTE: Do *not* do a network state change in this case.
  5139. // m_vecVelocity is only networked for the player, which is not manual mode
  5140. CBaseEntity *pMoveParent = GetMoveParent();
  5141. if (!pMoveParent)
  5142. {
  5143. m_vecVelocity = vecAbsVelocity;
  5144. return;
  5145. }
  5146. // First subtract out the parent's abs velocity to get a relative
  5147. // velocity measured in world space
  5148. Vector relVelocity;
  5149. VectorSubtract( vecAbsVelocity, pMoveParent->GetAbsVelocity(), relVelocity );
  5150. // Transform relative velocity into parent space
  5151. Vector vNew;
  5152. VectorIRotate( relVelocity, pMoveParent->EntityToWorldTransform(), vNew );
  5153. m_vecVelocity = vNew;
  5154. }
  5155. // FIXME: While we're using (dPitch, dYaw, dRoll) as our local angular velocity
  5156. // representation, we can't actually solve this problem
  5157. /*
  5158. void CBaseEntity::SetAbsAngularVelocity( const QAngle &vecAbsAngVelocity )
  5159. {
  5160. // The abs velocity won't be dirty since we're setting it here
  5161. // All children are invalid, but we are not
  5162. InvalidatePhysicsRecursive( EFL_DIRTY_ABSANGVELOCITY );
  5163. RemoveEFlags( EFL_DIRTY_ABSANGVELOCITY );
  5164. m_vecAbsAngVelocity = vecAbsAngVelocity;
  5165. CBaseEntity *pMoveParent = GetMoveParent();
  5166. if (!pMoveParent)
  5167. {
  5168. m_vecAngVelocity = vecAbsAngVelocity;
  5169. return;
  5170. }
  5171. // NOTE: We *can't* subtract out parent ang velocity, it's nonsensical
  5172. matrix3x4_t entityToWorld;
  5173. AngleMatrix( vecAbsAngVelocity, entityToWorld );
  5174. // Moveparent case: transform the abs relative angular vel into local space
  5175. matrix3x4_t worldToParent, localMatrix;
  5176. MatrixInvert( pMoveParent->EntityToWorldTransform(), worldToParent );
  5177. ConcatTransforms( worldToParent, entityToWorld, localMatrix );
  5178. MatrixAngles( localMatrix, m_vecAngVelocity );
  5179. }
  5180. */
  5181. //-----------------------------------------------------------------------------
  5182. // Methods that modify local physics state, and let us know to compute abs state later
  5183. //-----------------------------------------------------------------------------
  5184. void CBaseEntity::SetLocalOrigin( const Vector& origin )
  5185. {
  5186. // Safety check against NaN's or really huge numbers
  5187. if ( !IsEntityPositionReasonable( origin ) )
  5188. {
  5189. if ( CheckEmitReasonablePhysicsSpew() )
  5190. {
  5191. Warning( "Bad SetLocalOrigin(%f,%f,%f) on %s\n", origin.x, origin.y, origin.z, GetDebugName() );
  5192. }
  5193. Assert( false );
  5194. return;
  5195. }
  5196. // if ( !origin.IsValid() )
  5197. // {
  5198. // AssertMsg( 0, "Bad origin set" );
  5199. // return;
  5200. // }
  5201. if (m_vecOrigin != origin)
  5202. {
  5203. // Sanity check to make sure the origin is valid.
  5204. #ifdef _DEBUG
  5205. float largeVal = 1024 * 128;
  5206. Assert( origin.x >= -largeVal && origin.x <= largeVal );
  5207. Assert( origin.y >= -largeVal && origin.y <= largeVal );
  5208. Assert( origin.z >= -largeVal && origin.z <= largeVal );
  5209. #endif
  5210. InvalidatePhysicsRecursive( POSITION_CHANGED );
  5211. m_vecOrigin = origin;
  5212. SetSimulationTime( gpGlobals->curtime );
  5213. }
  5214. }
  5215. void CBaseEntity::SetLocalAngles( const QAngle& angles )
  5216. {
  5217. // NOTE: The angle normalize is a little expensive, but we can save
  5218. // a bunch of time in interpolation if we don't have to invalidate everything
  5219. // and sometimes it's off by a normalization amount
  5220. // FIXME: The normalize caused problems in server code like momentary_rot_button that isn't
  5221. // handling things like +/-180 degrees properly. This should be revisited.
  5222. //QAngle angleNormalize( AngleNormalize( angles.x ), AngleNormalize( angles.y ), AngleNormalize( angles.z ) );
  5223. // Safety check against NaN's or really huge numbers
  5224. if ( !IsEntityQAngleReasonable( angles ) )
  5225. {
  5226. if ( CheckEmitReasonablePhysicsSpew() )
  5227. {
  5228. Warning( "Bad SetLocalAngles(%f,%f,%f) on %s\n", angles.x, angles.y, angles.z, GetDebugName() );
  5229. }
  5230. AssertMsg( false, "Bad SetLocalAngles(%f,%f,%f) on %s\n", angles.x, angles.y, angles.z, GetDebugName() );
  5231. return;
  5232. }
  5233. if (m_angRotation != angles)
  5234. {
  5235. InvalidatePhysicsRecursive( ANGLES_CHANGED );
  5236. m_angRotation = angles;
  5237. SetSimulationTime( gpGlobals->curtime );
  5238. }
  5239. }
  5240. void CBaseEntity::SetLocalVelocity( const Vector &inVecVelocity )
  5241. {
  5242. Vector vecVelocity = inVecVelocity;
  5243. // Safety check against receive a huge impulse, which can explode physics
  5244. switch ( CheckEntityVelocity( vecVelocity ) )
  5245. {
  5246. case -1:
  5247. Warning( "Discarding SetLocalVelocity(%f,%f,%f) on %s\n", vecVelocity.x, vecVelocity.y, vecVelocity.z, GetDebugName() );
  5248. Assert( false );
  5249. return;
  5250. case 0:
  5251. if ( CheckEmitReasonablePhysicsSpew() )
  5252. {
  5253. Warning( "Clamping SetLocalVelocity(%f,%f,%f) on %s\n", inVecVelocity.x, inVecVelocity.y, inVecVelocity.z, GetDebugName() );
  5254. }
  5255. break;
  5256. }
  5257. if (m_vecVelocity != vecVelocity)
  5258. {
  5259. InvalidatePhysicsRecursive( VELOCITY_CHANGED );
  5260. m_vecVelocity = vecVelocity;
  5261. }
  5262. }
  5263. void CBaseEntity::SetLocalAngularVelocity( const QAngle &vecAngVelocity )
  5264. {
  5265. // Safety check against NaN's or really huge numbers
  5266. if ( !IsEntityQAngleVelReasonable( vecAngVelocity ) )
  5267. {
  5268. if ( CheckEmitReasonablePhysicsSpew() )
  5269. {
  5270. Warning( "Bad SetLocalAngularVelocity(%f,%f,%f) on %s\n", vecAngVelocity.x, vecAngVelocity.y, vecAngVelocity.z, GetDebugName() );
  5271. }
  5272. Assert( false );
  5273. return;
  5274. }
  5275. if (m_vecAngVelocity != vecAngVelocity)
  5276. {
  5277. // InvalidatePhysicsRecursive( EFL_DIRTY_ABSANGVELOCITY );
  5278. m_vecAngVelocity = vecAngVelocity;
  5279. }
  5280. }
  5281. //-----------------------------------------------------------------------------
  5282. // Sets the local position from a transform
  5283. //-----------------------------------------------------------------------------
  5284. void CBaseEntity::SetLocalTransform( const matrix3x4_t &localTransform )
  5285. {
  5286. // FIXME: Should angles go away? Should we just use transforms?
  5287. Vector vecLocalOrigin;
  5288. QAngle vecLocalAngles;
  5289. MatrixGetColumn( localTransform, 3, vecLocalOrigin );
  5290. MatrixAngles( localTransform, vecLocalAngles );
  5291. SetLocalOrigin( vecLocalOrigin );
  5292. SetLocalAngles( vecLocalAngles );
  5293. }
  5294. //-----------------------------------------------------------------------------
  5295. // Is the entity floating?
  5296. //-----------------------------------------------------------------------------
  5297. bool CBaseEntity::IsFloating()
  5298. {
  5299. if ( !IsEFlagSet(EFL_TOUCHING_FLUID) )
  5300. return false;
  5301. IPhysicsObject *pObject = VPhysicsGetObject();
  5302. if ( !pObject )
  5303. return false;
  5304. int nMaterialIndex = pObject->GetMaterialIndex();
  5305. float flDensity;
  5306. float flThickness;
  5307. float flFriction;
  5308. float flElasticity;
  5309. physprops->GetPhysicsProperties( nMaterialIndex, &flDensity,
  5310. &flThickness, &flFriction, &flElasticity );
  5311. // FIXME: This really only works for water at the moment..
  5312. // Owing the check for density == 1000
  5313. return (flDensity < 1000.0f);
  5314. }
  5315. //-----------------------------------------------------------------------------
  5316. // Purpose: Created predictable and sets up Id. Note that persist is ignored on the server.
  5317. // Input : *classname -
  5318. // *module -
  5319. // line -
  5320. // persist -
  5321. // Output : CBaseEntity
  5322. //-----------------------------------------------------------------------------
  5323. CBaseEntity *CBaseEntity::CreatePredictedEntityByName( const char *classname, const char *module, int line, bool persist /* = false */ )
  5324. {
  5325. #if !defined( NO_ENTITY_PREDICTION )
  5326. CBasePlayer *player = CBaseEntity::GetPredictionPlayer();
  5327. Assert( player );
  5328. CBaseEntity *ent = NULL;
  5329. int command_number = player->CurrentCommandNumber();
  5330. int player_index = player->entindex() - 1;
  5331. CPredictableId testId;
  5332. testId.Init( player_index, command_number, classname, module, line );
  5333. ent = CreateEntityByName( classname );
  5334. // No factory???
  5335. if ( !ent )
  5336. return NULL;
  5337. ent->SetPredictionEligible( true );
  5338. // Set up "shared" id number
  5339. ent->m_PredictableID.GetForModify().SetRaw( testId.GetRaw() );
  5340. return ent;
  5341. #else
  5342. return NULL;
  5343. #endif
  5344. }
  5345. void CBaseEntity::SetPredictionEligible( bool canpredict )
  5346. {
  5347. // Nothing in game code m_bPredictionEligible = canpredict;
  5348. }
  5349. //-----------------------------------------------------------------------------
  5350. // These could be virtual, but only the player is overriding them
  5351. // NOTE: If you make any of these virtual, remove this implementation!!!
  5352. //-----------------------------------------------------------------------------
  5353. void CBaseEntity::AddPoints( int score, bool bAllowNegativeScore )
  5354. {
  5355. CBasePlayer *pPlayer = ToBasePlayer(this);
  5356. if ( pPlayer )
  5357. {
  5358. pPlayer->CBasePlayer::AddPoints( score, bAllowNegativeScore );
  5359. }
  5360. }
  5361. void CBaseEntity::AddPointsToTeam( int score, bool bAllowNegativeScore )
  5362. {
  5363. CBasePlayer *pPlayer = ToBasePlayer(this);
  5364. if ( pPlayer )
  5365. {
  5366. pPlayer->CBasePlayer::AddPointsToTeam( score, bAllowNegativeScore );
  5367. }
  5368. }
  5369. void CBaseEntity::ViewPunch( const QAngle &angleOffset )
  5370. {
  5371. CBasePlayer *pPlayer = ToBasePlayer(this);
  5372. if ( pPlayer )
  5373. {
  5374. pPlayer->CBasePlayer::ViewPunch( angleOffset );
  5375. }
  5376. }
  5377. void CBaseEntity::VelocityPunch( const Vector &vecForce )
  5378. {
  5379. CBasePlayer *pPlayer = ToBasePlayer(this);
  5380. if ( pPlayer )
  5381. {
  5382. pPlayer->CBasePlayer::VelocityPunch( vecForce );
  5383. }
  5384. }
  5385. //-----------------------------------------------------------------------------
  5386. //-----------------------------------------------------------------------------
  5387. // Purpose: Tell clients to remove all decals from this entity
  5388. //-----------------------------------------------------------------------------
  5389. void CBaseEntity::RemoveAllDecals( void )
  5390. {
  5391. EntityMessageBegin( this );
  5392. WRITE_BYTE( BASEENTITY_MSG_REMOVE_DECALS );
  5393. MessageEnd();
  5394. }
  5395. //-----------------------------------------------------------------------------
  5396. // Purpose:
  5397. // Input : set -
  5398. //-----------------------------------------------------------------------------
  5399. void CBaseEntity::ModifyOrAppendCriteria( AI_CriteriaSet& set )
  5400. {
  5401. // TODO
  5402. // Append chapter/day?
  5403. set.AppendCriteria( "randomnum", UTIL_VarArgs("%d", RandomInt(0,100)) );
  5404. // Append map name
  5405. set.AppendCriteria( "map", gpGlobals->mapname.ToCStr() );
  5406. // Append our classname and game name
  5407. set.AppendCriteria( "classname", GetClassname() );
  5408. set.AppendCriteria( "name", GetEntityName().ToCStr() );
  5409. // Append our health
  5410. set.AppendCriteria( "health", UTIL_VarArgs( "%i", GetHealth() ) );
  5411. float healthfrac = 0.0f;
  5412. if ( GetMaxHealth() > 0 )
  5413. {
  5414. healthfrac = (float)GetHealth() / (float)GetMaxHealth();
  5415. }
  5416. set.AppendCriteria( "healthfrac", UTIL_VarArgs( "%.3f", healthfrac ) );
  5417. // Go through all the global states and append them
  5418. for ( int i = 0; i < GlobalEntity_GetNumGlobals(); i++ )
  5419. {
  5420. const char *szGlobalName = GlobalEntity_GetName(i);
  5421. int iGlobalState = (int)GlobalEntity_GetStateByIndex(i);
  5422. set.AppendCriteria( szGlobalName, UTIL_VarArgs( "%i", iGlobalState ) );
  5423. }
  5424. // Append anything from I/O or keyvalues pairs
  5425. AppendContextToCriteria( set );
  5426. if( hl2_episodic.GetBool() )
  5427. {
  5428. set.AppendCriteria( "episodic", "1" );
  5429. }
  5430. // Append anything from world I/O/keyvalues with "world" as prefix
  5431. CWorld *world = dynamic_cast< CWorld * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) ) );
  5432. if ( world )
  5433. {
  5434. world->AppendContextToCriteria( set, "world" );
  5435. }
  5436. }
  5437. //-----------------------------------------------------------------------------
  5438. // Purpose:
  5439. // Input : set -
  5440. // "" -
  5441. //-----------------------------------------------------------------------------
  5442. void CBaseEntity::AppendContextToCriteria( AI_CriteriaSet& set, const char *prefix /*= ""*/ )
  5443. {
  5444. RemoveExpiredConcepts();
  5445. int c = GetContextCount();
  5446. int i;
  5447. char sz[ 128 ];
  5448. for ( i = 0; i < c; i++ )
  5449. {
  5450. const char *name = GetContextName( i );
  5451. const char *value = GetContextValue( i );
  5452. Q_snprintf( sz, sizeof( sz ), "%s%s", prefix, name );
  5453. set.AppendCriteria( sz, value );
  5454. }
  5455. }
  5456. //-----------------------------------------------------------------------------
  5457. // Purpose: Removes expired concepts from list
  5458. // Output :
  5459. //-----------------------------------------------------------------------------
  5460. void CBaseEntity::RemoveExpiredConcepts( void )
  5461. {
  5462. int c = GetContextCount();
  5463. int i;
  5464. for ( i = 0; i < c; i++ )
  5465. {
  5466. if ( ContextExpired( i ) )
  5467. {
  5468. m_ResponseContexts.Remove( i );
  5469. c--;
  5470. i--;
  5471. continue;
  5472. }
  5473. }
  5474. }
  5475. //-----------------------------------------------------------------------------
  5476. // Purpose: Get current context count
  5477. // Output : int
  5478. //-----------------------------------------------------------------------------
  5479. int CBaseEntity::GetContextCount() const
  5480. {
  5481. return m_ResponseContexts.Count();
  5482. }
  5483. //-----------------------------------------------------------------------------
  5484. // Purpose:
  5485. // Input : index -
  5486. // Output : const char
  5487. //-----------------------------------------------------------------------------
  5488. const char *CBaseEntity::GetContextName( int index ) const
  5489. {
  5490. if ( index < 0 || index >= m_ResponseContexts.Count() )
  5491. {
  5492. Assert( 0 );
  5493. return "";
  5494. }
  5495. return m_ResponseContexts[ index ].m_iszName.ToCStr();
  5496. }
  5497. //-----------------------------------------------------------------------------
  5498. // Purpose:
  5499. // Input : index -
  5500. // Output : const char
  5501. //-----------------------------------------------------------------------------
  5502. const char *CBaseEntity::GetContextValue( int index ) const
  5503. {
  5504. if ( index < 0 || index >= m_ResponseContexts.Count() )
  5505. {
  5506. Assert( 0 );
  5507. return "";
  5508. }
  5509. return m_ResponseContexts[ index ].m_iszValue.ToCStr();
  5510. }
  5511. //-----------------------------------------------------------------------------
  5512. // Purpose: Check if context has expired
  5513. // Input : index -
  5514. // Output : bool
  5515. //-----------------------------------------------------------------------------
  5516. bool CBaseEntity::ContextExpired( int index ) const
  5517. {
  5518. if ( index < 0 || index >= m_ResponseContexts.Count() )
  5519. {
  5520. Assert( 0 );
  5521. return true;
  5522. }
  5523. if ( !m_ResponseContexts[ index ].m_fExpirationTime )
  5524. {
  5525. return false;
  5526. }
  5527. return ( m_ResponseContexts[ index ].m_fExpirationTime <= gpGlobals->curtime );
  5528. }
  5529. //-----------------------------------------------------------------------------
  5530. // Purpose: Search for index of named context string
  5531. // Input : *name -
  5532. // Output : int
  5533. //-----------------------------------------------------------------------------
  5534. int CBaseEntity::FindContextByName( const char *name ) const
  5535. {
  5536. int c = m_ResponseContexts.Count();
  5537. for ( int i = 0; i < c; i++ )
  5538. {
  5539. if ( FStrEq( name, GetContextName( i ) ) )
  5540. return i;
  5541. }
  5542. return -1;
  5543. }
  5544. //-----------------------------------------------------------------------------
  5545. // Purpose:
  5546. // Input : inputdata -
  5547. //-----------------------------------------------------------------------------
  5548. void CBaseEntity::InputAddContext( inputdata_t& inputdata )
  5549. {
  5550. const char *contextName = inputdata.value.String();
  5551. AddContext( contextName );
  5552. }
  5553. //-----------------------------------------------------------------------------
  5554. // Purpose: User inputs. These fire the corresponding user outputs, and are
  5555. // a means of forwarding messages through !activator to a target known
  5556. // known by !activator but not by the targetting entity.
  5557. //
  5558. // For example, say you have three identical trains, following the same
  5559. // path. Each train has a sprite in hierarchy with it that needs to
  5560. // toggle on/off as it passes each path_track. You would hook each train's
  5561. // OnUser1 output to it's sprite's Toggle input, then connect each path_track's
  5562. // OnPass output to !activator's FireUser1 input.
  5563. //-----------------------------------------------------------------------------
  5564. void CBaseEntity::InputFireUser1( inputdata_t& inputdata )
  5565. {
  5566. m_OnUser1.FireOutput( inputdata.pActivator, this );
  5567. }
  5568. void CBaseEntity::InputFireUser2( inputdata_t& inputdata )
  5569. {
  5570. m_OnUser2.FireOutput( inputdata.pActivator, this );
  5571. }
  5572. void CBaseEntity::InputFireUser3( inputdata_t& inputdata )
  5573. {
  5574. m_OnUser3.FireOutput( inputdata.pActivator, this );
  5575. }
  5576. void CBaseEntity::InputFireUser4( inputdata_t& inputdata )
  5577. {
  5578. m_OnUser4.FireOutput( inputdata.pActivator, this );
  5579. }
  5580. //-----------------------------------------------------------------------------
  5581. // Purpose:
  5582. // Input : *contextName -
  5583. //-----------------------------------------------------------------------------
  5584. void CBaseEntity::AddContext( const char *contextName )
  5585. {
  5586. char key[ 128 ];
  5587. char value[ 128 ];
  5588. float duration;
  5589. const char *p = contextName;
  5590. while ( p )
  5591. {
  5592. duration = 0.0f;
  5593. p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration );
  5594. if ( duration )
  5595. {
  5596. duration += gpGlobals->curtime;
  5597. }
  5598. int iIndex = FindContextByName( key );
  5599. if ( iIndex != -1 )
  5600. {
  5601. // Set the existing context to the new value
  5602. m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value );
  5603. m_ResponseContexts[iIndex].m_fExpirationTime = duration;
  5604. continue;
  5605. }
  5606. ResponseContext_t newContext;
  5607. newContext.m_iszName = AllocPooledString( key );
  5608. newContext.m_iszValue = AllocPooledString( value );
  5609. newContext.m_fExpirationTime = duration;
  5610. m_ResponseContexts.AddToTail( newContext );
  5611. }
  5612. }
  5613. //-----------------------------------------------------------------------------
  5614. // Purpose:
  5615. // Input : inputdata -
  5616. //-----------------------------------------------------------------------------
  5617. void CBaseEntity::InputRemoveContext( inputdata_t& inputdata )
  5618. {
  5619. const char *contextName = inputdata.value.String();
  5620. int idx = FindContextByName( contextName );
  5621. if ( idx == -1 )
  5622. return;
  5623. m_ResponseContexts.Remove( idx );
  5624. }
  5625. //-----------------------------------------------------------------------------
  5626. // Purpose:
  5627. // Input : inputdata -
  5628. //-----------------------------------------------------------------------------
  5629. void CBaseEntity::InputClearContext( inputdata_t& inputdata )
  5630. {
  5631. m_ResponseContexts.RemoveAll();
  5632. }
  5633. //-----------------------------------------------------------------------------
  5634. // Purpose:
  5635. // Output : IResponseSystem
  5636. //-----------------------------------------------------------------------------
  5637. IResponseSystem *CBaseEntity::GetResponseSystem()
  5638. {
  5639. return NULL;
  5640. }
  5641. //-----------------------------------------------------------------------------
  5642. // Purpose:
  5643. // Input : inputdata -
  5644. //-----------------------------------------------------------------------------
  5645. void CBaseEntity::InputDispatchResponse( inputdata_t& inputdata )
  5646. {
  5647. DispatchResponse( inputdata.value.String() );
  5648. }
  5649. //-----------------------------------------------------------------------------
  5650. //-----------------------------------------------------------------------------
  5651. void CBaseEntity::InputDisableShadow( inputdata_t &inputdata )
  5652. {
  5653. AddEffects( EF_NOSHADOW );
  5654. }
  5655. //-----------------------------------------------------------------------------
  5656. //-----------------------------------------------------------------------------
  5657. void CBaseEntity::InputEnableShadow( inputdata_t &inputdata )
  5658. {
  5659. RemoveEffects( EF_NOSHADOW );
  5660. }
  5661. //-----------------------------------------------------------------------------
  5662. // Purpose: An input to add a new connection from this entity
  5663. // Input : &inputdata -
  5664. //-----------------------------------------------------------------------------
  5665. void CBaseEntity::InputAddOutput( inputdata_t &inputdata )
  5666. {
  5667. char sOutputName[MAX_PATH];
  5668. Q_strncpy( sOutputName, inputdata.value.String(), sizeof(sOutputName) );
  5669. char *sChar = strchr( sOutputName, ' ' );
  5670. if ( sChar )
  5671. {
  5672. *sChar = '\0';
  5673. // Now replace all the :'s in the string with ,'s.
  5674. // Has to be done this way because Hammer doesn't allow ,'s inside parameters.
  5675. char *sColon = strchr( sChar+1, ':' );
  5676. while ( sColon )
  5677. {
  5678. *sColon = ',';
  5679. sColon = strchr( sChar+1, ':' );
  5680. }
  5681. KeyValue( sOutputName, sChar+1 );
  5682. }
  5683. else
  5684. {
  5685. Warning("AddOutput input fired with bad string. Format: <output name> <targetname>,<inputname>,<parameter>,<delay>,<max times to fire (-1 == infinite)>\n");
  5686. }
  5687. }
  5688. //-----------------------------------------------------------------------------
  5689. // Purpose:
  5690. // Input : *conceptName -
  5691. //-----------------------------------------------------------------------------
  5692. void CBaseEntity::DispatchResponse( const char *conceptName )
  5693. {
  5694. IResponseSystem *rs = GetResponseSystem();
  5695. if ( !rs )
  5696. return;
  5697. AI_CriteriaSet set;
  5698. // Always include the concept name
  5699. set.AppendCriteria( "concept", conceptName, CONCEPT_WEIGHT );
  5700. // Let NPC fill in most match criteria
  5701. ModifyOrAppendCriteria( set );
  5702. // Append local player criteria to set,too
  5703. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  5704. if( pPlayer )
  5705. pPlayer->ModifyOrAppendPlayerCriteria( set );
  5706. // Now that we have a criteria set, ask for a suitable response
  5707. AI_Response result;
  5708. bool found = rs->FindBestResponse( set, result );
  5709. if ( !found )
  5710. return;
  5711. // Handle the response here...
  5712. const char *szResponse = result.GetResponsePtr();
  5713. switch ( result.GetType() )
  5714. {
  5715. case RESPONSE_SPEAK:
  5716. EmitSound( szResponse );
  5717. break;
  5718. case RESPONSE_SENTENCE:
  5719. {
  5720. int sentenceIndex = SENTENCEG_Lookup( szResponse );
  5721. if( sentenceIndex == -1 )
  5722. {
  5723. // sentence not found
  5724. break;
  5725. }
  5726. // FIXME: Get pitch from npc?
  5727. CPASAttenuationFilter filter( this );
  5728. CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, result.GetSoundLevel(), 0, PITCH_NORM );
  5729. }
  5730. break;
  5731. case RESPONSE_SCENE:
  5732. // Try to fire scene w/o an actor
  5733. InstancedScriptedScene( NULL, szResponse );
  5734. break;
  5735. case RESPONSE_PRINT:
  5736. break;
  5737. default:
  5738. // Don't know how to handle .vcds!!!
  5739. break;
  5740. }
  5741. }
  5742. //-----------------------------------------------------------------------------
  5743. // Purpose:
  5744. //-----------------------------------------------------------------------------
  5745. void CBaseEntity::DumpResponseCriteria( void )
  5746. {
  5747. Msg("----------------------------------------------\n");
  5748. Msg("RESPONSE CRITERIA FOR: %s (%s)\n", GetClassname(), GetDebugName() );
  5749. AI_CriteriaSet set;
  5750. // Let NPC fill in most match criteria
  5751. ModifyOrAppendCriteria( set );
  5752. // Append local player criteria to set,too
  5753. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  5754. if ( pPlayer )
  5755. {
  5756. pPlayer->ModifyOrAppendPlayerCriteria( set );
  5757. }
  5758. // Now dump it all to console
  5759. set.Describe();
  5760. }
  5761. //------------------------------------------------------------------------------
  5762. void CC_Ent_Show_Response_Criteria( const CCommand& args )
  5763. {
  5764. CBaseEntity *pEntity = NULL;
  5765. while ( (pEntity = GetNextCommandEntity( UTIL_GetCommandClient(), args[1], pEntity )) != NULL )
  5766. {
  5767. pEntity->DumpResponseCriteria();
  5768. }
  5769. }
  5770. static ConCommand ent_show_response_criteria("ent_show_response_criteria", CC_Ent_Show_Response_Criteria, "Print, to the console, an entity's current criteria set used to select responses.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
  5771. //------------------------------------------------------------------------------
  5772. // Purpose: Show an entity's autoaim radius
  5773. //------------------------------------------------------------------------------
  5774. void CC_Ent_Autoaim( const CCommand& args )
  5775. {
  5776. SetDebugBits( UTIL_GetCommandClient(),args[1], OVERLAY_AUTOAIM_BIT );
  5777. }
  5778. static ConCommand ent_autoaim("ent_autoaim", CC_Ent_Autoaim, "Displays the entity's autoaim radius.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT );
  5779. //-----------------------------------------------------------------------------
  5780. // Purpose:
  5781. //-----------------------------------------------------------------------------
  5782. CAI_BaseNPC *CBaseEntity::MyNPCPointer( void )
  5783. {
  5784. if ( IsNPC() )
  5785. return assert_cast<CAI_BaseNPC *>(this);
  5786. return NULL;
  5787. }
  5788. ConVar step_spline( "step_spline", "0" );
  5789. //-----------------------------------------------------------------------------
  5790. // Purpose: Run one tick's worth of faked simulation
  5791. // Input : *step -
  5792. //-----------------------------------------------------------------------------
  5793. void CBaseEntity::ComputeStepSimulationNetwork( StepSimulationData *step )
  5794. {
  5795. if ( !step )
  5796. {
  5797. Assert( !"ComputeStepSimulationNetworkOriginAndAngles with NULL step\n" );
  5798. return;
  5799. }
  5800. // Don't run again if we've already calculated this tick
  5801. if ( step->m_nLastProcessTickCount == gpGlobals->tickcount )
  5802. {
  5803. return;
  5804. }
  5805. step->m_nLastProcessTickCount = gpGlobals->tickcount;
  5806. // Origin
  5807. // It's inactive
  5808. if ( step->m_bOriginActive )
  5809. {
  5810. // First see if any external code moved the entity
  5811. if ( GetStepOrigin() != step->m_Next.vecOrigin )
  5812. {
  5813. step->m_bOriginActive = false;
  5814. }
  5815. else
  5816. {
  5817. // Compute interpolated info based on tick interval
  5818. float frac = 1.0f;
  5819. int tickdelta = step->m_Next.nTickCount - step->m_Previous.nTickCount;
  5820. if ( tickdelta > 0 )
  5821. {
  5822. frac = (float)( gpGlobals->tickcount - step->m_Previous.nTickCount ) / (float) tickdelta;
  5823. frac = clamp( frac, 0.0f, 1.0f );
  5824. }
  5825. if (step->m_Previous2.nTickCount == 0 || step->m_Previous2.nTickCount >= step->m_Previous.nTickCount)
  5826. {
  5827. Vector delta = step->m_Next.vecOrigin - step->m_Previous.vecOrigin;
  5828. VectorMA( step->m_Previous.vecOrigin, frac, delta, step->m_vecNetworkOrigin );
  5829. }
  5830. else if (!step_spline.GetBool())
  5831. {
  5832. StepSimulationStep *pOlder = &step->m_Previous;
  5833. StepSimulationStep *pNewer = &step->m_Next;
  5834. if (step->m_Discontinuity.nTickCount > step->m_Previous.nTickCount)
  5835. {
  5836. if (gpGlobals->tickcount > step->m_Discontinuity.nTickCount)
  5837. {
  5838. pOlder = &step->m_Discontinuity;
  5839. }
  5840. else
  5841. {
  5842. pNewer = &step->m_Discontinuity;
  5843. }
  5844. tickdelta = pNewer->nTickCount - pOlder->nTickCount;
  5845. if ( tickdelta > 0 )
  5846. {
  5847. frac = (float)( gpGlobals->tickcount - pOlder->nTickCount ) / (float) tickdelta;
  5848. frac = clamp( frac, 0.0f, 1.0f );
  5849. }
  5850. }
  5851. Vector delta = pNewer->vecOrigin - pOlder->vecOrigin;
  5852. VectorMA( pOlder->vecOrigin, frac, delta, step->m_vecNetworkOrigin );
  5853. }
  5854. else
  5855. {
  5856. Hermite_Spline( step->m_Previous2.vecOrigin, step->m_Previous.vecOrigin, step->m_Next.vecOrigin, frac, step->m_vecNetworkOrigin );
  5857. }
  5858. }
  5859. }
  5860. // Angles
  5861. if ( step->m_bAnglesActive )
  5862. {
  5863. // See if external code changed the orientation of the entity
  5864. if ( GetStepAngles() != step->m_angNextRotation )
  5865. {
  5866. step->m_bAnglesActive = false;
  5867. }
  5868. else
  5869. {
  5870. // Compute interpolated info based on tick interval
  5871. float frac = 1.0f;
  5872. int tickdelta = step->m_Next.nTickCount - step->m_Previous.nTickCount;
  5873. if ( tickdelta > 0 )
  5874. {
  5875. frac = (float)( gpGlobals->tickcount - step->m_Previous.nTickCount ) / (float) tickdelta;
  5876. frac = clamp( frac, 0.0f, 1.0f );
  5877. }
  5878. if (step->m_Previous2.nTickCount == 0 || step->m_Previous2.nTickCount >= step->m_Previous.nTickCount)
  5879. {
  5880. // Pure blend between start/end orientations
  5881. Quaternion outangles;
  5882. QuaternionBlend( step->m_Previous.qRotation, step->m_Next.qRotation, frac, outangles );
  5883. QuaternionAngles( outangles, step->m_angNetworkAngles );
  5884. }
  5885. else if (!step_spline.GetBool())
  5886. {
  5887. StepSimulationStep *pOlder = &step->m_Previous;
  5888. StepSimulationStep *pNewer = &step->m_Next;
  5889. if (step->m_Discontinuity.nTickCount > step->m_Previous.nTickCount)
  5890. {
  5891. if (gpGlobals->tickcount > step->m_Discontinuity.nTickCount)
  5892. {
  5893. pOlder = &step->m_Discontinuity;
  5894. }
  5895. else
  5896. {
  5897. pNewer = &step->m_Discontinuity;
  5898. }
  5899. tickdelta = pNewer->nTickCount - pOlder->nTickCount;
  5900. if ( tickdelta > 0 )
  5901. {
  5902. frac = (float)( gpGlobals->tickcount - pOlder->nTickCount ) / (float) tickdelta;
  5903. frac = clamp( frac, 0.0f, 1.0f );
  5904. }
  5905. }
  5906. // Pure blend between start/end orientations
  5907. Quaternion outangles;
  5908. QuaternionBlend( pOlder->qRotation, pNewer->qRotation, frac, outangles );
  5909. QuaternionAngles( outangles, step->m_angNetworkAngles );
  5910. }
  5911. else
  5912. {
  5913. // FIXME: enable spline interpolation when turning is debounced.
  5914. Quaternion outangles;
  5915. Hermite_Spline( step->m_Previous2.qRotation, step->m_Previous.qRotation, step->m_Next.qRotation, frac, outangles );
  5916. QuaternionAngles( outangles, step->m_angNetworkAngles );
  5917. }
  5918. }
  5919. }
  5920. }
  5921. //-----------------------------------------------------------------------------
  5922. bool CBaseEntity::UseStepSimulationNetworkOrigin( const Vector **out_v )
  5923. {
  5924. Assert( out_v );
  5925. if ( g_bTestMoveTypeStepSimulation &&
  5926. GetMoveType() == MOVETYPE_STEP &&
  5927. HasDataObjectType( STEPSIMULATION ) )
  5928. {
  5929. StepSimulationData *step = ( StepSimulationData * )GetDataObject( STEPSIMULATION );
  5930. ComputeStepSimulationNetwork( step );
  5931. *out_v = &step->m_vecNetworkOrigin;
  5932. return step->m_bOriginActive;
  5933. }
  5934. return false;
  5935. }
  5936. //-----------------------------------------------------------------------------
  5937. bool CBaseEntity::UseStepSimulationNetworkAngles( const QAngle **out_a )
  5938. {
  5939. Assert( out_a );
  5940. if ( g_bTestMoveTypeStepSimulation &&
  5941. GetMoveType() == MOVETYPE_STEP &&
  5942. HasDataObjectType( STEPSIMULATION ) )
  5943. {
  5944. StepSimulationData *step = ( StepSimulationData * )GetDataObject( STEPSIMULATION );
  5945. ComputeStepSimulationNetwork( step );
  5946. *out_a = &step->m_angNetworkAngles;
  5947. return step->m_bAnglesActive;
  5948. }
  5949. return false;
  5950. }
  5951. //-----------------------------------------------------------------------------
  5952. // Purpose:
  5953. //-----------------------------------------------------------------------------
  5954. bool CBaseEntity::AddStepDiscontinuity( float flTime, const Vector &vecOrigin, const QAngle &vecAngles )
  5955. {
  5956. if ((GetMoveType() != MOVETYPE_STEP ) || !HasDataObjectType( STEPSIMULATION ) )
  5957. {
  5958. return false;
  5959. }
  5960. StepSimulationData *step = ( StepSimulationData * )GetDataObject( STEPSIMULATION );
  5961. if (!step)
  5962. {
  5963. Assert( 0 );
  5964. return false;
  5965. }
  5966. step->m_Discontinuity.nTickCount = TIME_TO_TICKS( flTime );
  5967. step->m_Discontinuity.vecOrigin = vecOrigin;
  5968. AngleQuaternion( vecAngles, step->m_Discontinuity.qRotation );
  5969. return true;
  5970. }
  5971. Vector CBaseEntity::GetStepOrigin( void ) const
  5972. {
  5973. return GetLocalOrigin();
  5974. }
  5975. QAngle CBaseEntity::GetStepAngles( void ) const
  5976. {
  5977. return GetLocalAngles();
  5978. }
  5979. //-----------------------------------------------------------------------------
  5980. // Purpose: For each client who appears to be a valid recipient, checks the client has disabled CC and if so, removes them from
  5981. // the recipient list.
  5982. // Input : filter -
  5983. //-----------------------------------------------------------------------------
  5984. void CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( CRecipientFilter& filter )
  5985. {
  5986. int c = filter.GetRecipientCount();
  5987. for ( int i = c - 1; i >= 0; --i )
  5988. {
  5989. int playerIndex = filter.GetRecipientIndex( i );
  5990. CBasePlayer *player = static_cast< CBasePlayer * >( CBaseEntity::Instance( playerIndex ) );
  5991. if ( !player )
  5992. continue;
  5993. #if !defined( _XBOX )
  5994. const char *cvarvalue = engine->GetClientConVarValue( playerIndex, "closecaption" );
  5995. Assert( cvarvalue );
  5996. if ( !cvarvalue[ 0 ] )
  5997. continue;
  5998. int value = atoi( cvarvalue );
  5999. #else
  6000. static ConVar *s_pCloseCaption = NULL;
  6001. if ( !s_pCloseCaption )
  6002. {
  6003. s_pCloseCaption = cvar->FindVar( "closecaption" );
  6004. if ( !s_pCloseCaption )
  6005. {
  6006. Error( "XBOX couldn't find closecaption convar!!!" );
  6007. }
  6008. }
  6009. int value = s_pCloseCaption->GetInt();
  6010. #endif
  6011. // No close captions?
  6012. if ( value == 0 )
  6013. {
  6014. filter.RemoveRecipient( player );
  6015. }
  6016. }
  6017. }
  6018. //-----------------------------------------------------------------------------
  6019. // Purpose: Wrapper to emit a sentence and also a close caption token for the sentence as appropriate.
  6020. // Input : filter -
  6021. // iEntIndex -
  6022. // iChannel -
  6023. // iSentenceIndex -
  6024. // flVolume -
  6025. // iSoundlevel -
  6026. // iFlags -
  6027. // iPitch -
  6028. // bUpdatePositions -
  6029. // soundtime -
  6030. //-----------------------------------------------------------------------------
  6031. void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex,
  6032. float flVolume, soundlevel_t iSoundlevel, int iFlags /*= 0*/, int iPitch /*=PITCH_NORM*/,
  6033. const Vector *pOrigin /*=NULL*/, const Vector *pDirection /*=NULL*/,
  6034. bool bUpdatePositions /*=true*/, float soundtime /*=0.0f*/ )
  6035. {
  6036. CUtlVector< Vector > dummy;
  6037. enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex,
  6038. flVolume, iSoundlevel, iFlags, iPitch, 0, pOrigin, pDirection, &dummy, bUpdatePositions, soundtime );
  6039. }
  6040. void CBaseEntity::SetRefEHandle( const CBaseHandle &handle )
  6041. {
  6042. m_RefEHandle = handle;
  6043. if ( edict() )
  6044. {
  6045. COMPILE_TIME_ASSERT( NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS <= 8*sizeof( edict()->m_NetworkSerialNumber ) );
  6046. edict()->m_NetworkSerialNumber = m_RefEHandle.GetSerialNumber() & ( (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1 );
  6047. }
  6048. }
  6049. bool CPointEntity::KeyValue( const char *szKeyName, const char *szValue )
  6050. {
  6051. if ( FStrEq( szKeyName, "mins" ) || FStrEq( szKeyName, "maxs" ) )
  6052. {
  6053. Warning("Warning! Can't specify mins/maxs for point entities! (%s)\n", GetClassname() );
  6054. return true;
  6055. }
  6056. return BaseClass::KeyValue( szKeyName, szValue );
  6057. }
  6058. bool CServerOnlyPointEntity::KeyValue( const char *szKeyName, const char *szValue )
  6059. {
  6060. if ( FStrEq( szKeyName, "mins" ) || FStrEq( szKeyName, "maxs" ) )
  6061. {
  6062. Warning("Warning! Can't specify mins/maxs for point entities! (%s)\n", GetClassname() );
  6063. return true;
  6064. }
  6065. return BaseClass::KeyValue( szKeyName, szValue );
  6066. }
  6067. bool CLogicalEntity::KeyValue( const char *szKeyName, const char *szValue )
  6068. {
  6069. if ( FStrEq( szKeyName, "mins" ) || FStrEq( szKeyName, "maxs" ) )
  6070. {
  6071. Warning("Warning! Can't specify mins/maxs for point entities! (%s)\n", GetClassname() );
  6072. return true;
  6073. }
  6074. return BaseClass::KeyValue( szKeyName, szValue );
  6075. }
  6076. //-----------------------------------------------------------------------------
  6077. // Purpose: Sets the entity invisible, and makes it remove itself on the next frame
  6078. //-----------------------------------------------------------------------------
  6079. void CBaseEntity::RemoveDeferred( void )
  6080. {
  6081. // Set our next think to remove us
  6082. SetThink( &CBaseEntity::SUB_Remove );
  6083. SetNextThink( gpGlobals->curtime + 0.1f );
  6084. // Hide us completely
  6085. AddEffects( EF_NODRAW );
  6086. AddSolidFlags( FSOLID_NOT_SOLID );
  6087. SetMoveType( MOVETYPE_NONE );
  6088. }
  6089. #define MIN_CORPSE_FADE_TIME 10.0
  6090. #define MIN_CORPSE_FADE_DIST 256.0
  6091. #define MAX_CORPSE_FADE_DIST 1500.0
  6092. //
  6093. // fade out - slowly fades a entity out, then removes it.
  6094. //
  6095. // DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER!
  6096. // SET A FUTURE THINK AND A RENDERMODE!!
  6097. void CBaseEntity::SUB_StartFadeOut( float delay, bool notSolid )
  6098. {
  6099. SetThink( &CBaseEntity::SUB_FadeOut );
  6100. SetNextThink( gpGlobals->curtime + delay );
  6101. SetRenderColorA( 255 );
  6102. m_nRenderMode = kRenderNormal;
  6103. if ( notSolid )
  6104. {
  6105. AddSolidFlags( FSOLID_NOT_SOLID );
  6106. SetLocalAngularVelocity( vec3_angle );
  6107. }
  6108. }
  6109. void CBaseEntity::SUB_StartFadeOutInstant()
  6110. {
  6111. SUB_StartFadeOut( 0, true );
  6112. }
  6113. //-----------------------------------------------------------------------------
  6114. // Purpose: Vanish when players aren't looking
  6115. //-----------------------------------------------------------------------------
  6116. void CBaseEntity::SUB_Vanish( void )
  6117. {
  6118. //Always think again next frame
  6119. SetNextThink( gpGlobals->curtime + 0.1f );
  6120. CBasePlayer *pPlayer;
  6121. //Get all players
  6122. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  6123. {
  6124. //Get the next client
  6125. if ( ( pPlayer = UTIL_PlayerByIndex( i ) ) != NULL )
  6126. {
  6127. Vector corpseDir = (GetAbsOrigin() - pPlayer->WorldSpaceCenter() );
  6128. float flDistSqr = corpseDir.LengthSqr();
  6129. //If the player is close enough, don't fade out
  6130. if ( flDistSqr < (MIN_CORPSE_FADE_DIST*MIN_CORPSE_FADE_DIST) )
  6131. return;
  6132. // If the player's far enough away, we don't care about looking at it
  6133. if ( flDistSqr < (MAX_CORPSE_FADE_DIST*MAX_CORPSE_FADE_DIST) )
  6134. {
  6135. VectorNormalize( corpseDir );
  6136. Vector plForward;
  6137. pPlayer->EyeVectors( &plForward );
  6138. float dot = plForward.Dot( corpseDir );
  6139. if ( dot > 0.0f )
  6140. return;
  6141. }
  6142. }
  6143. }
  6144. //If we're here, then we can vanish safely
  6145. m_iHealth = 0;
  6146. SetThink( &CBaseEntity::SUB_Remove );
  6147. }
  6148. void CBaseEntity::SUB_PerformFadeOut( void )
  6149. {
  6150. float dt = gpGlobals->frametime;
  6151. if ( dt > 0.1f )
  6152. {
  6153. dt = 0.1f;
  6154. }
  6155. m_nRenderMode = kRenderTransTexture;
  6156. int speed = MAX(1,256*dt); // fade out over 1 second
  6157. SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
  6158. }
  6159. bool CBaseEntity::SUB_AllowedToFade( void )
  6160. {
  6161. if( VPhysicsGetObject() )
  6162. {
  6163. if( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD || GetEFlags() & EFL_IS_BEING_LIFTED_BY_BARNACLE )
  6164. return false;
  6165. }
  6166. // on Xbox, allow these to fade out
  6167. #ifndef _XBOX
  6168. CBasePlayer *pPlayer = ( AI_IsSinglePlayer() ) ? UTIL_GetLocalPlayer() : NULL;
  6169. if ( pPlayer && pPlayer->FInViewCone( this ) )
  6170. return false;
  6171. #endif
  6172. return true;
  6173. }
  6174. //-----------------------------------------------------------------------------
  6175. // Purpose: Fade out slowly
  6176. //-----------------------------------------------------------------------------
  6177. void CBaseEntity::SUB_FadeOut( void )
  6178. {
  6179. if ( SUB_AllowedToFade() == false )
  6180. {
  6181. SetNextThink( gpGlobals->curtime + 1 );
  6182. SetRenderColorA( 255 );
  6183. return;
  6184. }
  6185. SUB_PerformFadeOut();
  6186. if ( m_clrRender->a == 0 )
  6187. {
  6188. UTIL_Remove(this);
  6189. }
  6190. else
  6191. {
  6192. SetNextThink( gpGlobals->curtime );
  6193. }
  6194. }
  6195. inline bool AnyPlayersInHierarchy_R( CBaseEntity *pEnt )
  6196. {
  6197. if ( pEnt->IsPlayer() )
  6198. return true;
  6199. for ( CBaseEntity *pCur = pEnt->FirstMoveChild(); pCur; pCur=pCur->NextMovePeer() )
  6200. {
  6201. if ( AnyPlayersInHierarchy_R( pCur ) )
  6202. return true;
  6203. }
  6204. return false;
  6205. }
  6206. void CBaseEntity::RecalcHasPlayerChildBit()
  6207. {
  6208. if ( AnyPlayersInHierarchy_R( this ) )
  6209. AddEFlags( EFL_HAS_PLAYER_CHILD );
  6210. else
  6211. RemoveEFlags( EFL_HAS_PLAYER_CHILD );
  6212. }
  6213. bool CBaseEntity::DoesHavePlayerChild()
  6214. {
  6215. return IsEFlagSet( EFL_HAS_PLAYER_CHILD );
  6216. }
  6217. //------------------------------------------------------------------------------
  6218. void CBaseEntity::IncrementInterpolationFrame()
  6219. {
  6220. m_ubInterpolationFrame = (m_ubInterpolationFrame + 1) % NOINTERP_PARITY_MAX;
  6221. }
  6222. //------------------------------------------------------------------------------
  6223. void CBaseEntity::OnModelLoadComplete( const model_t* model )
  6224. {
  6225. Assert( m_bDynamicModelPending && IsDynamicModelIndex( m_nModelIndex ) );
  6226. Assert( model == modelinfo->GetModel( m_nModelIndex ) );
  6227. m_bDynamicModelPending = false;
  6228. if ( m_bDynamicModelSetBounds )
  6229. {
  6230. m_bDynamicModelSetBounds = false;
  6231. SetCollisionBoundsFromModel();
  6232. }
  6233. OnNewModel();
  6234. }
  6235. //------------------------------------------------------------------------------
  6236. void CBaseEntity::SetCollisionBoundsFromModel()
  6237. {
  6238. if ( IsDynamicModelLoading() )
  6239. {
  6240. m_bDynamicModelSetBounds = true;
  6241. return;
  6242. }
  6243. if ( const model_t *pModel = GetModel() )
  6244. {
  6245. Vector mns, mxs;
  6246. modelinfo->GetModelBounds( pModel, mns, mxs );
  6247. UTIL_SetSize( this, mns, mxs );
  6248. }
  6249. }
  6250. //------------------------------------------------------------------------------
  6251. // Purpose: Create an NPC of the given type
  6252. //------------------------------------------------------------------------------
  6253. void CC_Ent_Create( const CCommand& args )
  6254. {
  6255. MDLCACHE_CRITICAL_SECTION();
  6256. CBasePlayer *pPlayer = UTIL_GetCommandClient();
  6257. if (!pPlayer)
  6258. {
  6259. return;
  6260. }
  6261. // Don't allow regular users to create point_servercommand entities for the same reason as blocking ent_fire
  6262. if ( !Q_stricmp( args[1], "point_servercommand" ) )
  6263. {
  6264. if ( engine->IsDedicatedServer() )
  6265. {
  6266. // We allow people with disabled autokick to do it, because they already have rcon.
  6267. if ( pPlayer->IsAutoKickDisabled() == false )
  6268. return;
  6269. }
  6270. else if ( gpGlobals->maxClients > 1 )
  6271. {
  6272. // On listen servers with more than 1 player, only allow the host to create point_servercommand.
  6273. CBasePlayer *pHostPlayer = UTIL_GetListenServerHost();
  6274. if ( pPlayer != pHostPlayer )
  6275. return;
  6276. }
  6277. }
  6278. bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
  6279. CBaseEntity::SetAllowPrecache( true );
  6280. // Try to create entity
  6281. CBaseEntity *entity = dynamic_cast< CBaseEntity * >( CreateEntityByName(args[1]) );
  6282. if (entity)
  6283. {
  6284. entity->Precache();
  6285. // Pass in any additional parameters.
  6286. for ( int i = 2; i + 1 < args.ArgC(); i += 2 )
  6287. {
  6288. const char *pKeyName = args[i];
  6289. const char *pValue = args[i+1];
  6290. entity->KeyValue( pKeyName, pValue );
  6291. }
  6292. DispatchSpawn(entity);
  6293. // Now attempt to drop into the world
  6294. trace_t tr;
  6295. Vector forward;
  6296. pPlayer->EyeVectors( &forward );
  6297. UTIL_TraceLine(pPlayer->EyePosition(),
  6298. pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID,
  6299. pPlayer, COLLISION_GROUP_NONE, &tr );
  6300. if ( tr.fraction != 1.0 )
  6301. {
  6302. // Raise the end position a little up off the floor, place the npc and drop him down
  6303. tr.endpos.z += 12;
  6304. entity->Teleport( &tr.endpos, NULL, NULL );
  6305. UTIL_DropToFloor( entity, MASK_SOLID );
  6306. }
  6307. entity->Activate();
  6308. }
  6309. CBaseEntity::SetAllowPrecache( allowPrecache );
  6310. }
  6311. static ConCommand ent_create("ent_create", CC_Ent_Create, "Creates an entity of the given type where the player is looking. Additional parameters can be passed in in the form: ent_create <entity name> <param 1 name> <param 1> <param 2 name> <param 2>...<param N name> <param N>", FCVAR_GAMEDLL | FCVAR_CHEAT);
  6312. //------------------------------------------------------------------------------
  6313. // Purpose: Teleport a specified entity to where the player is looking
  6314. //------------------------------------------------------------------------------
  6315. bool CC_GetCommandEnt( const CCommand& args, CBaseEntity **ent, Vector *vecTargetPoint, QAngle *vecPlayerAngle )
  6316. {
  6317. // Find the entity
  6318. *ent = NULL;
  6319. // First try using it as an entindex
  6320. int iEntIndex = atoi( args[1] );
  6321. if ( iEntIndex )
  6322. {
  6323. *ent = CBaseEntity::Instance( iEntIndex );
  6324. }
  6325. else
  6326. {
  6327. // Try finding it by name
  6328. *ent = gEntList.FindEntityByName( NULL, args[1] );
  6329. if ( !*ent )
  6330. {
  6331. // Finally, try finding it by classname
  6332. *ent = gEntList.FindEntityByClassname( NULL, args[1] );
  6333. }
  6334. }
  6335. if ( !*ent )
  6336. {
  6337. Msg( "Couldn't find any entity named '%s'\n", args[1] );
  6338. return false;
  6339. }
  6340. CBasePlayer *pPlayer = UTIL_GetCommandClient();
  6341. if ( !pPlayer )
  6342. {
  6343. Msg( "Command must originate from a player\n" );
  6344. return false;
  6345. }
  6346. if ( vecTargetPoint )
  6347. {
  6348. trace_t tr;
  6349. Vector forward;
  6350. pPlayer->EyeVectors( &forward );
  6351. UTIL_TraceLine(pPlayer->EyePosition(),
  6352. pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
  6353. pPlayer, COLLISION_GROUP_NONE, &tr );
  6354. if ( tr.fraction != 1.0 )
  6355. {
  6356. *vecTargetPoint = tr.endpos;
  6357. }
  6358. }
  6359. if ( vecPlayerAngle )
  6360. {
  6361. *vecPlayerAngle = pPlayer->EyeAngles();
  6362. }
  6363. return true;
  6364. }
  6365. //------------------------------------------------------------------------------
  6366. // Purpose: Teleport a specified entity to where the player is looking
  6367. //------------------------------------------------------------------------------
  6368. void CC_Ent_Teleport( const CCommand& args )
  6369. {
  6370. if ( args.ArgC() < 2 )
  6371. {
  6372. Msg( "Format: ent_teleport <entity name>\n" );
  6373. return;
  6374. }
  6375. CBaseEntity *pEnt;
  6376. Vector vecTargetPoint;
  6377. if ( CC_GetCommandEnt( args, &pEnt, &vecTargetPoint, NULL ) )
  6378. {
  6379. pEnt->Teleport( &vecTargetPoint, NULL, NULL );
  6380. }
  6381. }
  6382. static ConCommand ent_teleport("ent_teleport", CC_Ent_Teleport, "Teleport the specified entity to where the player is looking.\n\tFormat: ent_teleport <entity name>", FCVAR_CHEAT);
  6383. //------------------------------------------------------------------------------
  6384. // Purpose: Orient a specified entity to match the player's angles
  6385. //------------------------------------------------------------------------------
  6386. void CC_Ent_Orient( const CCommand& args )
  6387. {
  6388. if ( args.ArgC() < 2 )
  6389. {
  6390. Msg( "Format: ent_orient <entity name> <optional: allangles>\n" );
  6391. return;
  6392. }
  6393. CBaseEntity *pEnt;
  6394. QAngle vecPlayerAngles;
  6395. if ( CC_GetCommandEnt( args, &pEnt, NULL, &vecPlayerAngles ) )
  6396. {
  6397. QAngle vecEntAngles = pEnt->GetAbsAngles();
  6398. if ( args.ArgC() == 3 && !Q_strncmp( args[2], "allangles", 9 ) )
  6399. {
  6400. vecEntAngles = vecPlayerAngles;
  6401. }
  6402. else
  6403. {
  6404. vecEntAngles[YAW] = vecPlayerAngles[YAW];
  6405. }
  6406. pEnt->SetAbsAngles( vecEntAngles );
  6407. }
  6408. }
  6409. static ConCommand ent_orient("ent_orient", CC_Ent_Orient, "Orient the specified entity to match the player's angles. By default, only orients target entity's YAW. Use the 'allangles' option to orient on all axis.\n\tFormat: ent_orient <entity name> <optional: allangles>", FCVAR_CHEAT);