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