1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "idlib/geometry/JointTransform.h"
31 #include "idlib/LangDict.h"
32 #include "framework/async/NetworkSystem.h"
33 #include "framework/DeclEntityDef.h"
34 #include "renderer/ModelManager.h"
35 
36 #include "gamesys/SysCvar.h"
37 #include "physics/Physics_Parametric.h"
38 #include "physics/Physics_Actor.h"
39 #include "script/Script_Thread.h"
40 #include "Fx.h"
41 #include "AFEntity.h"
42 #include "Player.h"
43 #include "Mover.h"
44 #include "WorldSpawn.h"
45 #include "SmokeParticles.h"
46 
47 #include "Entity.h"
48 
49 /*
50 ===============================================================================
51 
52 	idEntity
53 
54 ===============================================================================
55 */
56 
57 // overridable events
58 const idEventDef EV_PostSpawn( "<postspawn>", NULL );
59 const idEventDef EV_FindTargets( "<findTargets>", NULL );
60 const idEventDef EV_Touch( "<touch>", "et" );
61 const idEventDef EV_GetName( "getName", NULL, 's' );
62 const idEventDef EV_SetName( "setName", "s" );
63 const idEventDef EV_Activate( "activate", "e" );
64 const idEventDef EV_ActivateTargets( "activateTargets", "e" );
65 const idEventDef EV_NumTargets( "numTargets", NULL, 'f' );
66 const idEventDef EV_GetTarget( "getTarget", "f", 'e' );
67 const idEventDef EV_RandomTarget( "randomTarget", "s", 'e' );
68 const idEventDef EV_Bind( "bind", "e" );
69 const idEventDef EV_BindPosition( "bindPosition", "e" );
70 const idEventDef EV_BindToJoint( "bindToJoint", "esf" );
71 const idEventDef EV_Unbind( "unbind", NULL );
72 const idEventDef EV_RemoveBinds( "removeBinds" );
73 const idEventDef EV_SpawnBind( "<spawnbind>", NULL );
74 const idEventDef EV_SetOwner( "setOwner", "e" );
75 const idEventDef EV_SetModel( "setModel", "s" );
76 const idEventDef EV_SetSkin( "setSkin", "s" );
77 const idEventDef EV_GetWorldOrigin( "getWorldOrigin", NULL, 'v' );
78 const idEventDef EV_SetWorldOrigin( "setWorldOrigin", "v" );
79 const idEventDef EV_GetOrigin( "getOrigin", NULL, 'v' );
80 const idEventDef EV_SetOrigin( "setOrigin", "v" );
81 const idEventDef EV_GetAngles( "getAngles", NULL, 'v' );
82 const idEventDef EV_SetAngles( "setAngles", "v" );
83 const idEventDef EV_GetLinearVelocity( "getLinearVelocity", NULL, 'v' );
84 const idEventDef EV_SetLinearVelocity( "setLinearVelocity", "v" );
85 const idEventDef EV_GetAngularVelocity( "getAngularVelocity", NULL, 'v' );
86 const idEventDef EV_SetAngularVelocity( "setAngularVelocity", "v" );
87 const idEventDef EV_GetSize( "getSize", NULL, 'v' );
88 const idEventDef EV_SetSize( "setSize", "vv" );
89 const idEventDef EV_GetMins( "getMins", NULL, 'v' );
90 const idEventDef EV_GetMaxs( "getMaxs", NULL, 'v' );
91 const idEventDef EV_IsHidden( "isHidden", NULL, 'd' );
92 const idEventDef EV_Hide( "hide", NULL );
93 const idEventDef EV_Show( "show", NULL );
94 const idEventDef EV_Touches( "touches", "E", 'd' );
95 const idEventDef EV_ClearSignal( "clearSignal", "d" );
96 const idEventDef EV_GetShaderParm( "getShaderParm", "d", 'f' );
97 const idEventDef EV_SetShaderParm( "setShaderParm", "df" );
98 const idEventDef EV_SetShaderParms( "setShaderParms", "ffff" );
99 const idEventDef EV_SetColor( "setColor", "fff" );
100 const idEventDef EV_GetColor( "getColor", NULL, 'v' );
101 const idEventDef EV_CacheSoundShader( "cacheSoundShader", "s" );
102 const idEventDef EV_StartSoundShader( "startSoundShader", "sd", 'f' );
103 const idEventDef EV_StartSound( "startSound", "sdd", 'f' );
104 const idEventDef EV_StopSound( "stopSound", "dd" );
105 const idEventDef EV_FadeSound( "fadeSound", "dff" );
106 const idEventDef EV_SetGuiParm( "setGuiParm", "ss" );
107 const idEventDef EV_SetGuiFloat( "setGuiFloat", "sf" );
108 const idEventDef EV_GetNextKey( "getNextKey", "ss", 's' );
109 const idEventDef EV_SetKey( "setKey", "ss" );
110 const idEventDef EV_GetKey( "getKey", "s", 's' );
111 const idEventDef EV_GetIntKey( "getIntKey", "s", 'f' );
112 const idEventDef EV_GetFloatKey( "getFloatKey", "s", 'f' );
113 const idEventDef EV_GetVectorKey( "getVectorKey", "s", 'v' );
114 const idEventDef EV_GetEntityKey( "getEntityKey", "s", 'e' );
115 const idEventDef EV_RestorePosition( "restorePosition" );
116 const idEventDef EV_UpdateCameraTarget( "<updateCameraTarget>", NULL );
117 const idEventDef EV_DistanceTo( "distanceTo", "E", 'f' );
118 const idEventDef EV_DistanceToPoint( "distanceToPoint", "v", 'f' );
119 const idEventDef EV_StartFx( "startFx", "s" );
120 const idEventDef EV_HasFunction( "hasFunction", "s", 'd' );
121 const idEventDef EV_CallFunction( "callFunction", "s" );
122 const idEventDef EV_SetNeverDormant( "setNeverDormant", "d" );
123 #ifdef _D3XP
124 const idEventDef EV_SetGui ( "setGui", "ds" );
125 const idEventDef EV_PrecacheGui ( "precacheGui", "s" );
126 const idEventDef EV_GetGuiParm ( "getGuiParm", "ds", 's' );
127 const idEventDef EV_GetGuiParmFloat ( "getGuiParmFloat", "ds", 'f' );
128 const idEventDef EV_MotionBlurOn( "motionBlurOn" );
129 const idEventDef EV_MotionBlurOff( "motionBlurOff" );
130 const idEventDef EV_GuiNamedEvent ( "guiNamedEvent", "ds" );
131 #endif
132 
ABSTRACT_DECLARATION(idClass,idEntity)133 ABSTRACT_DECLARATION( idClass, idEntity )
134 	EVENT( EV_GetName,				idEntity::Event_GetName )
135 	EVENT( EV_SetName,				idEntity::Event_SetName )
136 	EVENT( EV_FindTargets,			idEntity::Event_FindTargets )
137 	EVENT( EV_ActivateTargets,		idEntity::Event_ActivateTargets )
138 	EVENT( EV_NumTargets,			idEntity::Event_NumTargets )
139 	EVENT( EV_GetTarget,			idEntity::Event_GetTarget )
140 	EVENT( EV_RandomTarget,			idEntity::Event_RandomTarget )
141 	EVENT( EV_BindToJoint,			idEntity::Event_BindToJoint )
142 	EVENT( EV_RemoveBinds,			idEntity::Event_RemoveBinds )
143 	EVENT( EV_Bind,					idEntity::Event_Bind )
144 	EVENT( EV_BindPosition,			idEntity::Event_BindPosition )
145 	EVENT( EV_Unbind,				idEntity::Event_Unbind )
146 	EVENT( EV_SpawnBind,			idEntity::Event_SpawnBind )
147 	EVENT( EV_SetOwner,				idEntity::Event_SetOwner )
148 	EVENT( EV_SetModel,				idEntity::Event_SetModel )
149 	EVENT( EV_SetSkin,				idEntity::Event_SetSkin )
150 	EVENT( EV_GetShaderParm,		idEntity::Event_GetShaderParm )
151 	EVENT( EV_SetShaderParm,		idEntity::Event_SetShaderParm )
152 	EVENT( EV_SetShaderParms,		idEntity::Event_SetShaderParms )
153 	EVENT( EV_SetColor,				idEntity::Event_SetColor )
154 	EVENT( EV_GetColor,				idEntity::Event_GetColor )
155 	EVENT( EV_IsHidden,				idEntity::Event_IsHidden )
156 	EVENT( EV_Hide,					idEntity::Event_Hide )
157 	EVENT( EV_Show,					idEntity::Event_Show )
158 	EVENT( EV_CacheSoundShader,		idEntity::Event_CacheSoundShader )
159 	EVENT( EV_StartSoundShader,		idEntity::Event_StartSoundShader )
160 	EVENT( EV_StartSound,			idEntity::Event_StartSound )
161 	EVENT( EV_StopSound,			idEntity::Event_StopSound )
162 	EVENT( EV_FadeSound,			idEntity::Event_FadeSound )
163 	EVENT( EV_GetWorldOrigin,		idEntity::Event_GetWorldOrigin )
164 	EVENT( EV_SetWorldOrigin,		idEntity::Event_SetWorldOrigin )
165 	EVENT( EV_GetOrigin,			idEntity::Event_GetOrigin )
166 	EVENT( EV_SetOrigin,			idEntity::Event_SetOrigin )
167 	EVENT( EV_GetAngles,			idEntity::Event_GetAngles )
168 	EVENT( EV_SetAngles,			idEntity::Event_SetAngles )
169 	EVENT( EV_GetLinearVelocity,	idEntity::Event_GetLinearVelocity )
170 	EVENT( EV_SetLinearVelocity,	idEntity::Event_SetLinearVelocity )
171 	EVENT( EV_GetAngularVelocity,	idEntity::Event_GetAngularVelocity )
172 	EVENT( EV_SetAngularVelocity,	idEntity::Event_SetAngularVelocity )
173 	EVENT( EV_GetSize,				idEntity::Event_GetSize )
174 	EVENT( EV_SetSize,				idEntity::Event_SetSize )
175 	EVENT( EV_GetMins,				idEntity::Event_GetMins)
176 	EVENT( EV_GetMaxs,				idEntity::Event_GetMaxs )
177 	EVENT( EV_Touches,				idEntity::Event_Touches )
178 	EVENT( EV_SetGuiParm,			idEntity::Event_SetGuiParm )
179 	EVENT( EV_SetGuiFloat,			idEntity::Event_SetGuiFloat )
180 	EVENT( EV_GetNextKey,			idEntity::Event_GetNextKey )
181 	EVENT( EV_SetKey,				idEntity::Event_SetKey )
182 	EVENT( EV_GetKey,				idEntity::Event_GetKey )
183 	EVENT( EV_GetIntKey,			idEntity::Event_GetIntKey )
184 	EVENT( EV_GetFloatKey,			idEntity::Event_GetFloatKey )
185 	EVENT( EV_GetVectorKey,			idEntity::Event_GetVectorKey )
186 	EVENT( EV_GetEntityKey,			idEntity::Event_GetEntityKey )
187 	EVENT( EV_RestorePosition,		idEntity::Event_RestorePosition )
188 	EVENT( EV_UpdateCameraTarget,	idEntity::Event_UpdateCameraTarget )
189 	EVENT( EV_DistanceTo,			idEntity::Event_DistanceTo )
190 	EVENT( EV_DistanceToPoint,		idEntity::Event_DistanceToPoint )
191 	EVENT( EV_StartFx,				idEntity::Event_StartFx )
192 	EVENT( EV_Thread_WaitFrame,		idEntity::Event_WaitFrame )
193 	EVENT( EV_Thread_Wait,			idEntity::Event_Wait )
194 	EVENT( EV_HasFunction,			idEntity::Event_HasFunction )
195 	EVENT( EV_CallFunction,			idEntity::Event_CallFunction )
196 	EVENT( EV_SetNeverDormant,		idEntity::Event_SetNeverDormant )
197 #ifdef _D3XP
198 	EVENT( EV_SetGui,				idEntity::Event_SetGui )
199 	EVENT( EV_PrecacheGui,			idEntity::Event_PrecacheGui )
200 	EVENT( EV_GetGuiParm,			idEntity::Event_GetGuiParm )
201 	EVENT( EV_GetGuiParmFloat,		idEntity::Event_GetGuiParmFloat )
202 	EVENT( EV_GuiNamedEvent,		idEntity::Event_GuiNamedEvent )
203 #endif
204 END_CLASS
205 
206 /*
207 ================
208 UpdateGuiParms
209 ================
210 */
211 void UpdateGuiParms( idUserInterface *gui, const idDict *args ) {
212 	if ( gui == NULL || args == NULL ) {
213 		return;
214 	}
215 	const idKeyValue *kv = args->MatchPrefix( "gui_parm", NULL );
216 	while( kv ) {
217 		gui->SetStateString( kv->GetKey(), kv->GetValue() );
218 		kv = args->MatchPrefix( "gui_parm", kv );
219 	}
220 	gui->SetStateBool( "noninteractive",  args->GetBool( "gui_noninteractive" ) ) ;
221 	gui->StateChanged( gameLocal.time );
222 }
223 
224 /*
225 ================
226 AddRenderGui
227 ================
228 */
AddRenderGui(const char * name,idUserInterface ** gui,const idDict * args)229 void AddRenderGui( const char *name, idUserInterface **gui, const idDict *args ) {
230 	const idKeyValue *kv = args->MatchPrefix( "gui_parm", NULL );
231 	*gui = uiManager->FindGui( name, true, ( kv != NULL ) );
232 	UpdateGuiParms( *gui, args );
233 }
234 
235 /*
236 ================
237 idGameEdit::ParseSpawnArgsToRenderEntity
238 
239 parse the static model parameters
240 this is the canonical renderEntity parm parsing,
241 which should be used by dmap and the editor
242 ================
243 */
ParseSpawnArgsToRenderEntity(const idDict * args,renderEntity_t * renderEntity)244 void idGameEdit::ParseSpawnArgsToRenderEntity( const idDict *args, renderEntity_t *renderEntity ) {
245 	int			i;
246 	const char	*temp;
247 	idVec3		color;
248 	float		angle;
249 	const idDeclModelDef *modelDef;
250 
251 	memset( renderEntity, 0, sizeof( *renderEntity ) );
252 
253 	temp = args->GetString( "model" );
254 
255 	modelDef = NULL;
256 	if ( temp[0] != '\0' ) {
257 		modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
258 		if ( modelDef ) {
259 			renderEntity->hModel = modelDef->ModelHandle();
260 		}
261 		if ( !renderEntity->hModel ) {
262 			renderEntity->hModel = renderModelManager->FindModel( temp );
263 		}
264 	}
265 	if ( renderEntity->hModel ) {
266 		renderEntity->bounds = renderEntity->hModel->Bounds( renderEntity );
267 	} else {
268 		renderEntity->bounds.Zero();
269 	}
270 
271 	temp = args->GetString( "skin" );
272 	if ( temp[0] != '\0' ) {
273 		renderEntity->customSkin = declManager->FindSkin( temp );
274 	} else if ( modelDef ) {
275 		renderEntity->customSkin = modelDef->GetDefaultSkin();
276 	}
277 
278 	temp = args->GetString( "shader" );
279 	if ( temp[0] != '\0' ) {
280 		renderEntity->customShader = declManager->FindMaterial( temp );
281 	}
282 
283 	args->GetVector( "origin", "0 0 0", renderEntity->origin );
284 
285 	// get the rotation matrix in either full form, or single angle form
286 	if ( !args->GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", renderEntity->axis ) ) {
287 		angle = args->GetFloat( "angle" );
288 		if ( angle != 0.0f ) {
289 			renderEntity->axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
290 		} else {
291 			renderEntity->axis.Identity();
292 		}
293 	}
294 
295 	renderEntity->referenceSound = NULL;
296 
297 	// get shader parms
298 	args->GetVector( "_color", "1 1 1", color );
299 	renderEntity->shaderParms[ SHADERPARM_RED ]		= color[0];
300 	renderEntity->shaderParms[ SHADERPARM_GREEN ]	= color[1];
301 	renderEntity->shaderParms[ SHADERPARM_BLUE ]	= color[2];
302 	renderEntity->shaderParms[ 3 ]					= args->GetFloat( "shaderParm3", "1" );
303 	renderEntity->shaderParms[ 4 ]					= args->GetFloat( "shaderParm4", "0" );
304 	renderEntity->shaderParms[ 5 ]					= args->GetFloat( "shaderParm5", "0" );
305 	renderEntity->shaderParms[ 6 ]					= args->GetFloat( "shaderParm6", "0" );
306 	renderEntity->shaderParms[ 7 ]					= args->GetFloat( "shaderParm7", "0" );
307 	renderEntity->shaderParms[ 8 ]					= args->GetFloat( "shaderParm8", "0" );
308 	renderEntity->shaderParms[ 9 ]					= args->GetFloat( "shaderParm9", "0" );
309 	renderEntity->shaderParms[ 10 ]					= args->GetFloat( "shaderParm10", "0" );
310 	renderEntity->shaderParms[ 11 ]					= args->GetFloat( "shaderParm11", "0" );
311 
312 	// check noDynamicInteractions flag
313 	renderEntity->noDynamicInteractions = args->GetBool( "noDynamicInteractions" );
314 
315 	// check noshadows flag
316 	renderEntity->noShadow = args->GetBool( "noshadows" );
317 
318 	// check noselfshadows flag
319 	renderEntity->noSelfShadow = args->GetBool( "noselfshadows" );
320 
321 	// init any guis, including entity-specific states
322 	for( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
323 		temp = args->GetString( i == 0 ? "gui" : va( "gui%d", i + 1 ) );
324 		if ( temp[ 0 ] != '\0' ) {
325 			AddRenderGui( temp, &renderEntity->gui[ i ], args );
326 		}
327 	}
328 }
329 
330 /*
331 ================
332 idGameEdit::ParseSpawnArgsToRefSound
333 
334 parse the sound parameters
335 this is the canonical refSound parm parsing,
336 which should be used by dmap and the editor
337 ================
338 */
ParseSpawnArgsToRefSound(const idDict * args,refSound_t * refSound)339 void idGameEdit::ParseSpawnArgsToRefSound( const idDict *args, refSound_t *refSound ) {
340 	const char	*temp;
341 
342 	memset( refSound, 0, sizeof( *refSound ) );
343 
344 	refSound->parms.minDistance = args->GetFloat( "s_mindistance" );
345 	refSound->parms.maxDistance = args->GetFloat( "s_maxdistance" );
346 	refSound->parms.volume = args->GetFloat( "s_volume" );
347 	refSound->parms.shakes = args->GetFloat( "s_shakes" );
348 
349 	args->GetVector( "origin", "0 0 0", refSound->origin );
350 
351 	refSound->referenceSound  = NULL;
352 
353 	// if a diversity is not specified, every sound start will make
354 	// a random one.  Specifying diversity is usefull to make multiple
355 	// lights all share the same buzz sound offset, for instance.
356 	refSound->diversity = args->GetFloat( "s_diversity", "-1" );
357 	refSound->waitfortrigger = args->GetBool( "s_waitfortrigger" );
358 
359 	if ( args->GetBool( "s_omni" ) ) {
360 		refSound->parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
361 	}
362 	if ( args->GetBool( "s_looping" ) ) {
363 		refSound->parms.soundShaderFlags |= SSF_LOOPING;
364 	}
365 	if ( args->GetBool( "s_occlusion" ) ) {
366 		refSound->parms.soundShaderFlags |= SSF_NO_OCCLUSION;
367 	}
368 	if ( args->GetBool( "s_global" ) ) {
369 		refSound->parms.soundShaderFlags |= SSF_GLOBAL;
370 	}
371 	if ( args->GetBool( "s_unclamped" ) ) {
372 		refSound->parms.soundShaderFlags |= SSF_UNCLAMPED;
373 	}
374 	refSound->parms.soundClass = args->GetInt( "s_soundClass" );
375 
376 	temp = args->GetString( "s_shader" );
377 	if ( temp[0] != '\0' ) {
378 		refSound->shader = declManager->FindSound( temp );
379 	}
380 }
381 
382 /*
383 ===============
384 idEntity::UpdateChangeableSpawnArgs
385 
386 Any key val pair that might change during the course of the game ( via a gui or whatever )
387 should be initialize here so a gui or other trigger can change something and have it updated
388 properly. An optional source may be provided if the values reside in an outside dictionary and
389 first need copied over to spawnArgs
390 ===============
391 */
UpdateChangeableSpawnArgs(const idDict * source)392 void idEntity::UpdateChangeableSpawnArgs( const idDict *source ) {
393 	int i;
394 	const char *target;
395 
396 	if ( !source ) {
397 		source = &spawnArgs;
398 	}
399 	cameraTarget = NULL;
400 	target = source->GetString( "cameraTarget" );
401 	if ( target && target[0] ) {
402 		// update the camera taget
403 		PostEventMS( &EV_UpdateCameraTarget, 0 );
404 	}
405 
406 	for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
407 		UpdateGuiParms( renderEntity.gui[ i ], source );
408 	}
409 }
410 
411 /*
412 ================
413 idEntity::idEntity
414 ================
415 */
idEntity()416 idEntity::idEntity() {
417 
418 	entityNumber	= ENTITYNUM_NONE;
419 	entityDefNumber = -1;
420 
421 	spawnNode.SetOwner( this );
422 	activeNode.SetOwner( this );
423 
424 	snapshotNode.SetOwner( this );
425 	snapshotSequence = -1;
426 	snapshotBits = 0;
427 
428 	thinkFlags		= 0;
429 	dormantStart	= 0;
430 	cinematic		= false;
431 	renderView		= NULL;
432 	cameraTarget	= NULL;
433 	health			= 0;
434 
435 	physics			= NULL;
436 	bindMaster		= NULL;
437 	bindJoint		= INVALID_JOINT;
438 	bindBody		= -1;
439 	teamMaster		= NULL;
440 	teamChain		= NULL;
441 	signals			= NULL;
442 
443 	memset( PVSAreas, 0, sizeof( PVSAreas ) );
444 	numPVSAreas		= -1;
445 
446 	memset( &fl, 0, sizeof( fl ) );
447 	fl.neverDormant	= true;			// most entities never go dormant
448 
449 	memset( &renderEntity, 0, sizeof( renderEntity ) );
450 	modelDefHandle	= -1;
451 	memset( &refSound, 0, sizeof( refSound ) );
452 
453 	mpGUIState = -1;
454 
455 #ifdef _D3XP
456 	memset( &xrayEntity, 0, sizeof( xrayEntity ) );
457 
458 	timeGroup = TIME_GROUP1;
459 	xrayEntityHandle = -1;
460 	xraySkin = NULL;
461 
462 	noGrab = false;
463 #endif
464 }
465 
466 /*
467 ================
468 idEntity::FixupLocalizedStrings
469 ================
470 */
FixupLocalizedStrings()471 void idEntity::FixupLocalizedStrings() {
472 	for ( int i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) {
473 		const idKeyValue *kv = spawnArgs.GetKeyVal( i );
474 		if ( idStr::Cmpn( kv->GetValue(), STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ){
475 			spawnArgs.Set( kv->GetKey(), common->GetLanguageDict()->GetString( kv->GetValue() ) );
476 		}
477 	}
478 }
479 
480 /*
481 ================
482 idEntity::Spawn
483 ================
484 */
Spawn(void)485 void idEntity::Spawn( void ) {
486 	int					i;
487 	const char			*temp;
488 	idVec3				origin;
489 	idMat3				axis;
490 	const idKeyValue	*networkSync;
491 	const char			*classname;
492 	const char			*scriptObjectName;
493 
494 	gameLocal.RegisterEntity( this );
495 
496 	spawnArgs.GetString( "classname", NULL, &classname );
497 	const idDeclEntityDef *def = gameLocal.FindEntityDef( classname, false );
498 	if ( def ) {
499 		entityDefNumber = def->Index();
500 	}
501 
502 	FixupLocalizedStrings();
503 
504 	// parse static models the same way the editor display does
505 	gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
506 
507 	renderEntity.entityNum = entityNumber;
508 
509 #ifdef _D3XP
510 	noGrab = spawnArgs.GetBool( "noGrab", "0" );
511 
512 	xraySkin = NULL;
513 	renderEntity.xrayIndex = 1;
514 
515 	idStr str;
516 	if ( spawnArgs.GetString( "skin_xray", "", str ) ) {
517 		xraySkin = declManager->FindSkin( str.c_str() );
518 	}
519 #endif
520 
521 	// go dormant within 5 frames so that when the map starts most monsters are dormant
522 	dormantStart = gameLocal.time - DELAY_DORMANT_TIME + gameLocal.msec * 5;
523 
524 	origin = renderEntity.origin;
525 	axis = renderEntity.axis;
526 
527 	// do the audio parsing the same way dmap and the editor do
528 	gameEdit->ParseSpawnArgsToRefSound( &spawnArgs, &refSound );
529 
530 	// only play SCHANNEL_PRIVATE when sndworld->PlaceListener() is called with this listenerId
531 	// don't spatialize sounds from the same entity
532 	refSound.listenerId = entityNumber + 1;
533 
534 	cameraTarget = NULL;
535 	temp = spawnArgs.GetString( "cameraTarget" );
536 	if ( temp && temp[0] ) {
537 		// update the camera taget
538 		PostEventMS( &EV_UpdateCameraTarget, 0 );
539 	}
540 
541 	for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
542 		UpdateGuiParms( renderEntity.gui[ i ], &spawnArgs );
543 	}
544 
545 	fl.solidForTeam = spawnArgs.GetBool( "solidForTeam", "0" );
546 	fl.neverDormant = spawnArgs.GetBool( "neverDormant", "0" );
547 	fl.hidden = spawnArgs.GetBool( "hide", "0" );
548 	if ( fl.hidden ) {
549 		// make sure we're hidden, since a spawn function might not set it up right
550 		PostEventMS( &EV_Hide, 0 );
551 	}
552 	cinematic = spawnArgs.GetBool( "cinematic", "0" );
553 
554 	networkSync = spawnArgs.FindKey( "networkSync" );
555 	if ( networkSync ) {
556 		fl.networkSync = ( atoi( networkSync->GetValue() ) != 0 );
557 	}
558 
559 #if 0
560 	if ( !gameLocal.isClient ) {
561 		// common->DPrintf( "NET: DBG %s - %s is synced: %s\n", spawnArgs.GetString( "classname", "" ), GetType()->classname, fl.networkSync ? "true" : "false" );
562 		if ( spawnArgs.GetString( "classname", "" )[ 0 ] == '\0' && !fl.networkSync ) {
563 			common->DPrintf( "NET: WRN %s entity, no classname, and no networkSync?\n", GetType()->classname );
564 		}
565 	}
566 #endif
567 
568 	// every object will have a unique name
569 	temp = spawnArgs.GetString( "name", va( "%s_%s_%d", GetClassname(), spawnArgs.GetString( "classname" ), entityNumber ) );
570 	SetName( temp );
571 
572 	// if we have targets, wait until all entities are spawned to get them
573 	if ( spawnArgs.MatchPrefix( "target" ) || spawnArgs.MatchPrefix( "guiTarget" ) ) {
574 		if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
575 			PostEventMS( &EV_FindTargets, 0 );
576 		} else {
577 			// not during spawn, so it's ok to get the targets
578 			FindTargets();
579 		}
580 	}
581 
582 	health = spawnArgs.GetInt( "health" );
583 
584 	InitDefaultPhysics( origin, axis );
585 
586 	SetOrigin( origin );
587 	SetAxis( axis );
588 
589 	temp = spawnArgs.GetString( "model" );
590 	if ( temp && *temp ) {
591 		SetModel( temp );
592 	}
593 
594 	if ( spawnArgs.GetString( "bind", "", &temp ) ) {
595 		PostEventMS( &EV_SpawnBind, 0 );
596 	}
597 
598 	// auto-start a sound on the entity
599 	if ( refSound.shader && !refSound.waitfortrigger ) {
600 		StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
601 	}
602 
603 	// setup script object
604 	if ( ShouldConstructScriptObjectAtSpawn() && spawnArgs.GetString( "scriptobject", NULL, &scriptObjectName ) ) {
605 		if ( !scriptObject.SetType( scriptObjectName ) ) {
606 			gameLocal.Error( "Script object '%s' not found on entity '%s'.", scriptObjectName, name.c_str() );
607 		}
608 
609 		ConstructScriptObject();
610 	}
611 
612 #ifdef _D3XP
613 	// determine time group
614 	DetermineTimeGroup( spawnArgs.GetBool( "slowmo", "1" ) );
615 #endif
616 }
617 
618 /*
619 ================
620 idEntity::~idEntity
621 ================
622 */
~idEntity(void)623 idEntity::~idEntity( void ) {
624 
625 	if ( gameLocal.GameState() != GAMESTATE_SHUTDOWN && !gameLocal.isClient && fl.networkSync && entityNumber >= MAX_CLIENTS ) {
626 		idBitMsg	msg;
627 		byte		msgBuf[ MAX_GAME_MESSAGE_SIZE ];
628 
629 		msg.Init( msgBuf, sizeof( msgBuf ) );
630 		msg.WriteByte( GAME_RELIABLE_MESSAGE_DELETE_ENT );
631 		msg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
632 		networkSystem->ServerSendReliableMessage( -1, msg );
633 	}
634 
635 	DeconstructScriptObject();
636 	scriptObject.Free();
637 
638 	if ( thinkFlags ) {
639 		BecomeInactive( thinkFlags );
640 	}
641 	activeNode.Remove();
642 
643 	Signal( SIG_REMOVED );
644 
645 	// we have to set back the default physics object before unbinding because the entity
646 	// specific physics object might be an entity variable and as such could already be destroyed.
647 	SetPhysics( NULL );
648 
649 	// remove any entities that are bound to me
650 	RemoveBinds();
651 
652 	// unbind from master
653 	Unbind();
654 	QuitTeam();
655 
656 	gameLocal.RemoveEntityFromHash( name.c_str(), this );
657 
658 	delete renderView;
659 	renderView = NULL;
660 
661 	delete signals;
662 	signals = NULL;
663 
664 	FreeModelDef();
665 	FreeSoundEmitter( false );
666 
667 #ifdef _D3XP
668 	if ( xrayEntityHandle != -1) {
669 		gameRenderWorld->FreeEntityDef( xrayEntityHandle );
670 		xrayEntityHandle = -1;
671 	}
672 #endif
673 
674 	gameLocal.UnregisterEntity( this );
675 }
676 
677 /*
678 ================
679 idEntity::Save
680 ================
681 */
Save(idSaveGame * savefile) const682 void idEntity::Save( idSaveGame *savefile ) const {
683 	int i, j;
684 
685 	savefile->WriteInt( entityNumber );
686 	savefile->WriteInt( entityDefNumber );
687 
688 	// spawnNode and activeNode are restored by gameLocal
689 
690 	savefile->WriteInt( snapshotSequence );
691 	savefile->WriteInt( snapshotBits );
692 
693 	savefile->WriteDict( &spawnArgs );
694 	savefile->WriteString( name );
695 	scriptObject.Save( savefile );
696 
697 	savefile->WriteInt( thinkFlags );
698 	savefile->WriteInt( dormantStart );
699 	savefile->WriteBool( cinematic );
700 
701 	savefile->WriteObject( cameraTarget );
702 
703 	savefile->WriteInt( health );
704 
705 	savefile->WriteInt( targets.Num() );
706 	for( i = 0; i < targets.Num(); i++ ) {
707 		targets[ i ].Save( savefile );
708 	}
709 
710 	entityFlags_s flags = fl;
711 	LittleBitField( &flags, sizeof( flags ) );
712 	savefile->Write( &flags, sizeof( flags ) );
713 
714 #ifdef _D3XP
715 	savefile->WriteInt( timeGroup );
716 	savefile->WriteBool( noGrab );
717 	savefile->WriteRenderEntity( xrayEntity );
718 	savefile->WriteInt( xrayEntityHandle );
719 	savefile->WriteSkin( xraySkin );
720 #endif
721 
722 	savefile->WriteRenderEntity( renderEntity );
723 	savefile->WriteInt( modelDefHandle );
724 	savefile->WriteRefSound( refSound );
725 
726 	savefile->WriteObject( bindMaster );
727 	savefile->WriteJoint( bindJoint );
728 	savefile->WriteInt( bindBody );
729 	savefile->WriteObject( teamMaster );
730 	savefile->WriteObject( teamChain );
731 
732 	savefile->WriteStaticObject( defaultPhysicsObj );
733 
734 	savefile->WriteInt( numPVSAreas );
735 	for( i = 0; i < MAX_PVS_AREAS; i++ ) {
736 		savefile->WriteInt( PVSAreas[ i ] );
737 	}
738 
739 	if ( !signals ) {
740 		savefile->WriteBool( false );
741 	} else {
742 		savefile->WriteBool( true );
743 		for( i = 0; i < NUM_SIGNALS; i++ ) {
744 			savefile->WriteInt( signals->signal[ i ].Num() );
745 			for( j = 0; j < signals->signal[ i ].Num(); j++ ) {
746 				savefile->WriteInt( signals->signal[ i ][ j ].threadnum );
747 				savefile->WriteString( signals->signal[ i ][ j ].function->Name() );
748 			}
749 		}
750 	}
751 
752 	savefile->WriteInt( mpGUIState );
753 }
754 
755 /*
756 ================
757 idEntity::Restore
758 ================
759 */
Restore(idRestoreGame * savefile)760 void idEntity::Restore( idRestoreGame *savefile ) {
761 	int			i, j;
762 	int			num;
763 	idStr		funcname;
764 
765 	savefile->ReadInt( entityNumber );
766 	savefile->ReadInt( entityDefNumber );
767 
768 	// spawnNode and activeNode are restored by gameLocal
769 
770 	savefile->ReadInt( snapshotSequence );
771 	savefile->ReadInt( snapshotBits );
772 
773 	savefile->ReadDict( &spawnArgs );
774 	savefile->ReadString( name );
775 	SetName( name );
776 
777 	scriptObject.Restore( savefile );
778 
779 	savefile->ReadInt( thinkFlags );
780 	savefile->ReadInt( dormantStart );
781 	savefile->ReadBool( cinematic );
782 
783 	savefile->ReadObject( reinterpret_cast<idClass *&>( cameraTarget ) );
784 
785 	savefile->ReadInt( health );
786 
787 	targets.Clear();
788 	savefile->ReadInt( num );
789 	targets.SetNum( num );
790 	for( i = 0; i < num; i++ ) {
791 		targets[ i ].Restore( savefile );
792 	}
793 
794 	savefile->Read( &fl, sizeof( fl ) );
795 	LittleBitField( &fl, sizeof( fl ) );
796 
797 #ifdef _D3XP
798 	savefile->ReadInt( timeGroup );
799 	savefile->ReadBool( noGrab );
800 	savefile->ReadRenderEntity( xrayEntity );
801 	savefile->ReadInt( xrayEntityHandle );
802 	if ( xrayEntityHandle != -1 ) {
803 		xrayEntityHandle =  gameRenderWorld->AddEntityDef( &xrayEntity );
804 	}
805 	savefile->ReadSkin( xraySkin );
806 #endif
807 
808 	savefile->ReadRenderEntity( renderEntity );
809 	savefile->ReadInt( modelDefHandle );
810 	savefile->ReadRefSound( refSound );
811 
812 	savefile->ReadObject( reinterpret_cast<idClass *&>( bindMaster ) );
813 	savefile->ReadJoint( bindJoint );
814 	savefile->ReadInt( bindBody );
815 	savefile->ReadObject( reinterpret_cast<idClass *&>( teamMaster ) );
816 	savefile->ReadObject( reinterpret_cast<idClass *&>( teamChain ) );
817 
818 	savefile->ReadStaticObject( defaultPhysicsObj );
819 	RestorePhysics( &defaultPhysicsObj );
820 
821 	savefile->ReadInt( numPVSAreas );
822 	for( i = 0; i < MAX_PVS_AREAS; i++ ) {
823 		savefile->ReadInt( PVSAreas[ i ] );
824 	}
825 
826 	bool readsignals;
827 	savefile->ReadBool( readsignals );
828 	if ( readsignals ) {
829 		signals = new signalList_t;
830 		for( i = 0; i < NUM_SIGNALS; i++ ) {
831 			savefile->ReadInt( num );
832 			signals->signal[ i ].SetNum( num );
833 			for( j = 0; j < num; j++ ) {
834 				savefile->ReadInt( signals->signal[ i ][ j ].threadnum );
835 				savefile->ReadString( funcname );
836 				signals->signal[ i ][ j ].function = gameLocal.program.FindFunction( funcname );
837 				if ( !signals->signal[ i ][ j ].function ) {
838 					savefile->Error( "Function '%s' not found", funcname.c_str() );
839 				}
840 			}
841 		}
842 	}
843 
844 	savefile->ReadInt( mpGUIState );
845 
846 	// restore must retrieve modelDefHandle from the renderer
847 	if ( modelDefHandle != -1 ) {
848 		modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
849 	}
850 }
851 
852 /*
853 ================
854 idEntity::GetEntityDefName
855 ================
856 */
GetEntityDefName(void) const857 const char * idEntity::GetEntityDefName( void ) const {
858 	if ( entityDefNumber < 0 ) {
859 		return "*unknown*";
860 	}
861 	return declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
862 }
863 
864 /*
865 ================
866 idEntity::SetName
867 ================
868 */
SetName(const char * newname)869 void idEntity::SetName( const char *newname ) {
870 	if ( name.Length() ) {
871 		gameLocal.RemoveEntityFromHash( name.c_str(), this );
872 		gameLocal.program.SetEntity( name, NULL );
873 	}
874 
875 	name = newname;
876 	if ( name.Length() ) {
877 		if ( ( name == "NULL" ) || ( name == "null_entity" ) ) {
878 			gameLocal.Error( "Cannot name entity '%s'.  '%s' is reserved for script.", name.c_str(), name.c_str() );
879 		}
880 		gameLocal.AddEntityToHash( name.c_str(), this );
881 		gameLocal.program.SetEntity( name, this );
882 	}
883 }
884 
885 /*
886 ================
887 idEntity::GetName
888 ================
889 */
GetName(void) const890 const char * idEntity::GetName( void ) const {
891 	return name.c_str();
892 }
893 
894 
895 /***********************************************************************
896 
897 	Thinking
898 
899 ***********************************************************************/
900 
901 /*
902 ================
903 idEntity::Think
904 ================
905 */
Think(void)906 void idEntity::Think( void ) {
907 	RunPhysics();
908 	Present();
909 }
910 
911 /*
912 ================
913 idEntity::DoDormantTests
914 
915 Monsters and other expensive entities that are completely closed
916 off from the player can skip all of their work
917 ================
918 */
DoDormantTests(void)919 bool idEntity::DoDormantTests( void ) {
920 
921 	if ( fl.neverDormant ) {
922 		return false;
923 	}
924 
925 	// if the monster area is not topologically connected to a player
926 	if ( !gameLocal.InPlayerConnectedArea( this ) ) {
927 		if ( dormantStart == 0 ) {
928 			dormantStart = gameLocal.time;
929 		}
930 		if ( gameLocal.time - dormantStart < DELAY_DORMANT_TIME ) {
931 			// just got closed off, don't go dormant yet
932 			return false;
933 		}
934 		return true;
935 	}
936 
937 	// the monster area is topologically connected to a player, but if
938 	// the monster hasn't been woken up before, do the more precise PVS check
939 	if ( !fl.hasAwakened ) {
940 		if ( !gameLocal.InPlayerPVS( this ) ) {
941 			return true;		// stay dormant
942 		}
943 	}
944 
945 	// wake up
946 	dormantStart = 0;
947 	fl.hasAwakened = true;		// only go dormant when area closed off now, not just out of PVS
948 
949 	return false;
950 }
951 
952 /*
953 ================
954 idEntity::CheckDormant
955 
956 Monsters and other expensive entities that are completely closed
957 off from the player can skip all of their work
958 ================
959 */
CheckDormant(void)960 bool idEntity::CheckDormant( void ) {
961 	bool dormant;
962 
963 	dormant = DoDormantTests();
964 	if ( dormant && !fl.isDormant ) {
965 		fl.isDormant = true;
966 		DormantBegin();
967 	} else if ( !dormant && fl.isDormant ) {
968 		fl.isDormant = false;
969 		DormantEnd();
970 	}
971 
972 	return dormant;
973 }
974 
975 /*
976 ================
977 idEntity::DormantBegin
978 
979 called when entity becomes dormant
980 ================
981 */
DormantBegin(void)982 void idEntity::DormantBegin( void ) {
983 }
984 
985 /*
986 ================
987 idEntity::DormantEnd
988 
989 called when entity wakes from being dormant
990 ================
991 */
DormantEnd(void)992 void idEntity::DormantEnd( void ) {
993 }
994 
995 /*
996 ================
997 idEntity::IsActive
998 ================
999 */
IsActive(void) const1000 bool idEntity::IsActive( void ) const {
1001 	return activeNode.InList();
1002 }
1003 
1004 /*
1005 ================
1006 idEntity::BecomeActive
1007 ================
1008 */
BecomeActive(int flags)1009 void idEntity::BecomeActive( int flags ) {
1010 	if ( ( flags & TH_PHYSICS ) ) {
1011 		// enable the team master if this entity is part of a physics team
1012 		if ( teamMaster && teamMaster != this ) {
1013 			teamMaster->BecomeActive( TH_PHYSICS );
1014 		} else if ( !( thinkFlags & TH_PHYSICS ) ) {
1015 			// if this is a pusher
1016 			if ( physics->IsType( idPhysics_Parametric::Type ) || physics->IsType( idPhysics_Actor::Type ) ) {
1017 				gameLocal.sortPushers = true;
1018 			}
1019 		}
1020 	}
1021 
1022 	int oldFlags = thinkFlags;
1023 	thinkFlags |= flags;
1024 	if ( thinkFlags ) {
1025 		if ( !IsActive() ) {
1026 			activeNode.AddToEnd( gameLocal.activeEntities );
1027 		} else if ( !oldFlags ) {
1028 			// we became inactive this frame, so we have to decrease the count of entities to deactivate
1029 			gameLocal.numEntitiesToDeactivate--;
1030 		}
1031 	}
1032 }
1033 
1034 /*
1035 ================
1036 idEntity::BecomeInactive
1037 ================
1038 */
BecomeInactive(int flags)1039 void idEntity::BecomeInactive( int flags ) {
1040 	if ( ( flags & TH_PHYSICS ) ) {
1041 		// may only disable physics on a team master if no team members are running physics or bound to a joints
1042 		if ( teamMaster == this ) {
1043 			for ( idEntity *ent = teamMaster->teamChain; ent; ent = ent->teamChain ) {
1044 				if ( ( ent->thinkFlags & TH_PHYSICS ) || ( ( ent->bindMaster == this ) && ( ent->bindJoint != INVALID_JOINT ) ) ) {
1045 					flags &= ~TH_PHYSICS;
1046 					break;
1047 				}
1048 			}
1049 		}
1050 	}
1051 
1052 	if ( thinkFlags ) {
1053 		thinkFlags &= ~flags;
1054 		if ( !thinkFlags && IsActive() ) {
1055 			gameLocal.numEntitiesToDeactivate++;
1056 		}
1057 	}
1058 
1059 	if ( ( flags & TH_PHYSICS ) ) {
1060 		// if this entity has a team master
1061 		if ( teamMaster && teamMaster != this ) {
1062 			// if the team master is at rest
1063 			if ( teamMaster->IsAtRest() ) {
1064 				teamMaster->BecomeInactive( TH_PHYSICS );
1065 			}
1066 		}
1067 	}
1068 }
1069 
1070 /***********************************************************************
1071 
1072 	Visuals
1073 
1074 ***********************************************************************/
1075 
1076 /*
1077 ================
1078 idEntity::SetShaderParm
1079 ================
1080 */
SetShaderParm(int parmnum,float value)1081 void idEntity::SetShaderParm( int parmnum, float value ) {
1082 	if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
1083 		gameLocal.Warning( "shader parm index (%d) out of range", parmnum );
1084 		return;
1085 	}
1086 
1087 	renderEntity.shaderParms[ parmnum ] = value;
1088 	UpdateVisuals();
1089 }
1090 
1091 /*
1092 ================
1093 idEntity::SetColor
1094 ================
1095 */
SetColor(float red,float green,float blue)1096 void idEntity::SetColor( float red, float green, float blue ) {
1097 	renderEntity.shaderParms[ SHADERPARM_RED ]		= red;
1098 	renderEntity.shaderParms[ SHADERPARM_GREEN ]	= green;
1099 	renderEntity.shaderParms[ SHADERPARM_BLUE ]		= blue;
1100 	UpdateVisuals();
1101 }
1102 
1103 /*
1104 ================
1105 idEntity::SetColor
1106 ================
1107 */
SetColor(const idVec3 & color)1108 void idEntity::SetColor( const idVec3 &color ) {
1109 	SetColor( color[ 0 ], color[ 1 ], color[ 2 ] );
1110 	UpdateVisuals();
1111 }
1112 
1113 /*
1114 ================
1115 idEntity::GetColor
1116 ================
1117 */
GetColor(idVec3 & out) const1118 void idEntity::GetColor( idVec3 &out ) const {
1119 	out[ 0 ] = renderEntity.shaderParms[ SHADERPARM_RED ];
1120 	out[ 1 ] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
1121 	out[ 2 ] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
1122 }
1123 
1124 /*
1125 ================
1126 idEntity::SetColor
1127 ================
1128 */
SetColor(const idVec4 & color)1129 void idEntity::SetColor( const idVec4 &color ) {
1130 	renderEntity.shaderParms[ SHADERPARM_RED ]		= color[ 0 ];
1131 	renderEntity.shaderParms[ SHADERPARM_GREEN ]	= color[ 1 ];
1132 	renderEntity.shaderParms[ SHADERPARM_BLUE ]		= color[ 2 ];
1133 	renderEntity.shaderParms[ SHADERPARM_ALPHA ]	= color[ 3 ];
1134 	UpdateVisuals();
1135 }
1136 
1137 /*
1138 ================
1139 idEntity::GetColor
1140 ================
1141 */
GetColor(idVec4 & out) const1142 void idEntity::GetColor( idVec4 &out ) const {
1143 	out[ 0 ] = renderEntity.shaderParms[ SHADERPARM_RED ];
1144 	out[ 1 ] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
1145 	out[ 2 ] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
1146 	out[ 3 ] = renderEntity.shaderParms[ SHADERPARM_ALPHA ];
1147 }
1148 
1149 /*
1150 ================
1151 idEntity::UpdateAnimationControllers
1152 ================
1153 */
UpdateAnimationControllers(void)1154 bool idEntity::UpdateAnimationControllers( void ) {
1155 	// any ragdoll and IK animation controllers should be updated here
1156 	return false;
1157 }
1158 
1159 /*
1160 ================
1161 idEntity::SetModel
1162 ================
1163 */
SetModel(const char * modelname)1164 void idEntity::SetModel( const char *modelname ) {
1165 	assert( modelname );
1166 
1167 	FreeModelDef();
1168 
1169 	renderEntity.hModel = renderModelManager->FindModel( modelname );
1170 
1171 	if ( renderEntity.hModel ) {
1172 		renderEntity.hModel->Reset();
1173 	}
1174 
1175 	renderEntity.callback = NULL;
1176 	renderEntity.numJoints = 0;
1177 	renderEntity.joints = NULL;
1178 	if ( renderEntity.hModel ) {
1179 		renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity );
1180 	} else {
1181 		renderEntity.bounds.Zero();
1182 	}
1183 
1184 	UpdateVisuals();
1185 }
1186 
1187 /*
1188 ================
1189 idEntity::SetSkin
1190 ================
1191 */
SetSkin(const idDeclSkin * skin)1192 void idEntity::SetSkin( const idDeclSkin *skin ) {
1193 	renderEntity.customSkin = skin;
1194 	UpdateVisuals();
1195 }
1196 
1197 /*
1198 ================
1199 idEntity::GetSkin
1200 ================
1201 */
GetSkin(void) const1202 const idDeclSkin *idEntity::GetSkin( void ) const {
1203 	return renderEntity.customSkin;
1204 }
1205 
1206 /*
1207 ================
1208 idEntity::FreeModelDef
1209 ================
1210 */
FreeModelDef(void)1211 void idEntity::FreeModelDef( void ) {
1212 	if ( modelDefHandle != -1 ) {
1213 		gameRenderWorld->FreeEntityDef( modelDefHandle );
1214 		modelDefHandle = -1;
1215 	}
1216 }
1217 
1218 /*
1219 ================
1220 idEntity::FreeLightDef
1221 ================
1222 */
FreeLightDef(void)1223 void idEntity::FreeLightDef( void ) {
1224 }
1225 
1226 /*
1227 ================
1228 idEntity::IsHidden
1229 ================
1230 */
IsHidden(void) const1231 bool idEntity::IsHidden( void ) const {
1232 	return fl.hidden;
1233 }
1234 
1235 /*
1236 ================
1237 idEntity::Hide
1238 ================
1239 */
Hide(void)1240 void idEntity::Hide( void ) {
1241 	if ( !IsHidden() ) {
1242 		fl.hidden = true;
1243 		FreeModelDef();
1244 		UpdateVisuals();
1245 	}
1246 }
1247 
1248 /*
1249 ================
1250 idEntity::Show
1251 ================
1252 */
Show(void)1253 void idEntity::Show( void ) {
1254 	if ( IsHidden() ) {
1255 		fl.hidden = false;
1256 		UpdateVisuals();
1257 	}
1258 }
1259 
1260 /*
1261 ================
1262 idEntity::UpdateModelTransform
1263 ================
1264 */
UpdateModelTransform(void)1265 void idEntity::UpdateModelTransform( void ) {
1266 	idVec3 origin;
1267 	idMat3 axis;
1268 
1269 	if ( GetPhysicsToVisualTransform( origin, axis ) ) {
1270 		renderEntity.axis = axis * GetPhysics()->GetAxis();
1271 		renderEntity.origin = GetPhysics()->GetOrigin() + origin * renderEntity.axis;
1272 	} else {
1273 		renderEntity.axis = GetPhysics()->GetAxis();
1274 		renderEntity.origin = GetPhysics()->GetOrigin();
1275 	}
1276 }
1277 
1278 /*
1279 ================
1280 idEntity::UpdateModel
1281 ================
1282 */
UpdateModel(void)1283 void idEntity::UpdateModel( void ) {
1284 #ifdef _D3XP
1285 	renderEntity.timeGroup = timeGroup;
1286 #endif
1287 
1288 	UpdateModelTransform();
1289 
1290 	// check if the entity has an MD5 model
1291 	idAnimator *animator = GetAnimator();
1292 	if ( animator && animator->ModelHandle() ) {
1293 		// set the callback to update the joints
1294 		renderEntity.callback = idEntity::ModelCallback;
1295 	}
1296 
1297 	// set to invalid number to force an update the next time the PVS areas are retrieved
1298 	ClearPVSAreas();
1299 
1300 	// ensure that we call Present this frame
1301 	BecomeActive( TH_UPDATEVISUALS );
1302 
1303 #ifdef _D3XP
1304 	// If the entity has an xray skin, go ahead and add it
1305 	if ( xraySkin != NULL ) {
1306 		xrayEntity = renderEntity;
1307 		xrayEntity.xrayIndex = 2;
1308 		xrayEntity.customSkin = xraySkin;
1309 
1310 		if ( xrayEntityHandle == -1 ) {
1311 			xrayEntityHandle = gameRenderWorld->AddEntityDef( &xrayEntity );
1312 		} else {
1313 			gameRenderWorld->UpdateEntityDef( xrayEntityHandle, &xrayEntity );
1314 		}
1315 	}
1316 #endif
1317 }
1318 
1319 /*
1320 ================
1321 idEntity::UpdateVisuals
1322 ================
1323 */
UpdateVisuals(void)1324 void idEntity::UpdateVisuals( void ) {
1325 	UpdateModel();
1326 	UpdateSound();
1327 }
1328 
1329 /*
1330 ================
1331 idEntity::UpdatePVSAreas
1332 ================
1333 */
UpdatePVSAreas(void)1334 void idEntity::UpdatePVSAreas( void ) {
1335 	int localNumPVSAreas, localPVSAreas[32];
1336 	idBounds modelAbsBounds;
1337 	int i;
1338 
1339 	modelAbsBounds.FromTransformedBounds( renderEntity.bounds, renderEntity.origin, renderEntity.axis );
1340 	localNumPVSAreas = gameLocal.pvs.GetPVSAreas( modelAbsBounds, localPVSAreas, sizeof( localPVSAreas ) / sizeof( localPVSAreas[0] ) );
1341 
1342 	// FIXME: some particle systems may have huge bounds and end up in many PVS areas
1343 	// the first MAX_PVS_AREAS may not be visible to a network client and as a result the particle system may not show up when it should
1344 	if ( localNumPVSAreas > MAX_PVS_AREAS ) {
1345 		localNumPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( renderEntity.origin ).Expand( 64.0f ), localPVSAreas, sizeof( localPVSAreas ) / sizeof( localPVSAreas[0] ) );
1346 	}
1347 
1348 	for ( numPVSAreas = 0; numPVSAreas < MAX_PVS_AREAS && numPVSAreas < localNumPVSAreas; numPVSAreas++ ) {
1349 		PVSAreas[numPVSAreas] = localPVSAreas[numPVSAreas];
1350 	}
1351 
1352 	for( i = numPVSAreas; i < MAX_PVS_AREAS; i++ ) {
1353 		PVSAreas[ i ] = 0;
1354 	}
1355 }
1356 
1357 /*
1358 ================
1359 idEntity::UpdatePVSAreas
1360 ================
1361 */
UpdatePVSAreas(const idVec3 & pos)1362 void idEntity::UpdatePVSAreas( const idVec3 &pos ) {
1363 	int i;
1364 
1365 	numPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( pos ), PVSAreas, MAX_PVS_AREAS );
1366 	i = numPVSAreas;
1367 	while ( i < MAX_PVS_AREAS ) {
1368 		PVSAreas[ i++ ] = 0;
1369 	}
1370 }
1371 
1372 /*
1373 ================
1374 idEntity::GetNumPVSAreas
1375 ================
1376 */
GetNumPVSAreas(void)1377 int idEntity::GetNumPVSAreas( void ) {
1378 	if ( numPVSAreas < 0 ) {
1379 		UpdatePVSAreas();
1380 	}
1381 	return numPVSAreas;
1382 }
1383 
1384 /*
1385 ================
1386 idEntity::GetPVSAreas
1387 ================
1388 */
GetPVSAreas(void)1389 const int *idEntity::GetPVSAreas( void ) {
1390 	if ( numPVSAreas < 0 ) {
1391 		UpdatePVSAreas();
1392 	}
1393 	return PVSAreas;
1394 }
1395 
1396 /*
1397 ================
1398 idEntity::ClearPVSAreas
1399 ================
1400 */
ClearPVSAreas(void)1401 void idEntity::ClearPVSAreas( void ) {
1402 	numPVSAreas = -1;
1403 }
1404 
1405 /*
1406 ================
1407 idEntity::PhysicsTeamInPVS
1408 
1409   FIXME: for networking also return true if any of the entity shadows is in the PVS
1410 ================
1411 */
PhysicsTeamInPVS(pvsHandle_t pvsHandle)1412 bool idEntity::PhysicsTeamInPVS( pvsHandle_t pvsHandle ) {
1413 	idEntity *part;
1414 
1415 	if ( teamMaster ) {
1416 		for ( part = teamMaster; part; part = part->teamChain ) {
1417 			if ( gameLocal.pvs.InCurrentPVS( pvsHandle, part->GetPVSAreas(), part->GetNumPVSAreas() ) ) {
1418 				return true;
1419 			}
1420 		}
1421 	} else {
1422 		return gameLocal.pvs.InCurrentPVS( pvsHandle, GetPVSAreas(), GetNumPVSAreas() );
1423 	}
1424 	return false;
1425 }
1426 
1427 /*
1428 ==============
1429 idEntity::ProjectOverlay
1430 ==============
1431 */
ProjectOverlay(const idVec3 & origin,const idVec3 & dir,float size,const char * material)1432 void idEntity::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1433 	float s, c;
1434 	idMat3 axis, axistemp;
1435 	idVec3 localOrigin, localAxis[2];
1436 	idPlane localPlane[2];
1437 
1438 	// make sure the entity has a valid model handle
1439 	if ( modelDefHandle < 0 ) {
1440 		return;
1441 	}
1442 
1443 	// only do this on dynamic md5 models
1444 	if ( renderEntity.hModel->IsDynamicModel() != DM_CACHED ) {
1445 		return;
1446 	}
1447 
1448 	idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
1449 
1450 	axis[2] = -dir;
1451 	axis[2].NormalVectors( axistemp[0], axistemp[1] );
1452 	axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
1453 	axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
1454 
1455 	renderEntity.axis.ProjectVector( origin - renderEntity.origin, localOrigin );
1456 	renderEntity.axis.ProjectVector( axis[0], localAxis[0] );
1457 	renderEntity.axis.ProjectVector( axis[1], localAxis[1] );
1458 
1459 	size = 1.0f / size;
1460 	localAxis[0] *= size;
1461 	localAxis[1] *= size;
1462 
1463 	localPlane[0] = localAxis[0];
1464 	localPlane[0][3] = -( localOrigin * localAxis[0] ) + 0.5f;
1465 
1466 	localPlane[1] = localAxis[1];
1467 	localPlane[1][3] = -( localOrigin * localAxis[1] ) + 0.5f;
1468 
1469 	const idMaterial *mtr = declManager->FindMaterial( material );
1470 
1471 	// project an overlay onto the model
1472 	gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
1473 
1474 	// make sure non-animating models update their overlay
1475 	UpdateVisuals();
1476 }
1477 
1478 /*
1479 ================
1480 idEntity::Present
1481 
1482 Present is called to allow entities to generate refEntities, lights, etc for the renderer.
1483 ================
1484 */
Present(void)1485 void idEntity::Present( void ) {
1486 
1487 	if ( !gameLocal.isNewFrame ) {
1488 		return;
1489 	}
1490 
1491 	// don't present to the renderer if the entity hasn't changed
1492 	if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1493 		return;
1494 	}
1495 	BecomeInactive( TH_UPDATEVISUALS );
1496 
1497 	// camera target for remote render views
1498 	if ( cameraTarget && gameLocal.InPlayerPVS( this ) ) {
1499 		renderEntity.remoteRenderView = cameraTarget->GetRenderView();
1500 	}
1501 
1502 	// if set to invisible, skip
1503 	if ( !renderEntity.hModel || IsHidden() ) {
1504 		return;
1505 	}
1506 
1507 	// add to refresh list
1508 	if ( modelDefHandle == -1 ) {
1509 		modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
1510 	} else {
1511 		gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
1512 	}
1513 }
1514 
1515 /*
1516 ================
1517 idEntity::GetRenderEntity
1518 ================
1519 */
GetRenderEntity(void)1520 renderEntity_t *idEntity::GetRenderEntity( void ) {
1521 	return &renderEntity;
1522 }
1523 
1524 /*
1525 ================
1526 idEntity::GetModelDefHandle
1527 ================
1528 */
GetModelDefHandle(void)1529 int idEntity::GetModelDefHandle( void ) {
1530 	return modelDefHandle;
1531 }
1532 
1533 /*
1534 ================
1535 idEntity::UpdateRenderEntity
1536 ================
1537 */
UpdateRenderEntity(renderEntity_s * renderEntity,const renderView_t * renderView)1538 bool idEntity::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) {
1539 	if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
1540 		return false;
1541 	}
1542 
1543 	idAnimator *animator = GetAnimator();
1544 	if ( animator ) {
1545 #ifdef _D3XP
1546 		SetTimeState ts( timeGroup );
1547 #endif
1548 
1549 		return animator->CreateFrame( gameLocal.time, false );
1550 	}
1551 
1552 	return false;
1553 }
1554 
1555 /*
1556 ================
1557 idEntity::ModelCallback
1558 
1559 	NOTE: may not change the game state whatsoever!
1560 ================
1561 */
ModelCallback(renderEntity_s * renderEntity,const renderView_t * renderView)1562 bool idEntity::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
1563 	idEntity *ent;
1564 
1565 	ent = gameLocal.entities[ renderEntity->entityNum ];
1566 	if ( !ent ) {
1567 		gameLocal.Error( "idEntity::ModelCallback: callback with NULL game entity" );
1568 	}
1569 
1570 	return ent->UpdateRenderEntity( renderEntity, renderView );
1571 }
1572 
1573 /*
1574 ================
1575 idEntity::GetAnimator
1576 
1577 Subclasses will be responsible for allocating animator.
1578 ================
1579 */
GetAnimator(void)1580 idAnimator *idEntity::GetAnimator( void ) {
1581 	return NULL;
1582 }
1583 
1584 /*
1585 =============
1586 idEntity::GetRenderView
1587 
1588 This is used by remote camera views to look from an entity
1589 =============
1590 */
GetRenderView(void)1591 renderView_t *idEntity::GetRenderView( void ) {
1592 	if ( !renderView ) {
1593 		renderView = new renderView_t;
1594 	}
1595 	memset( renderView, 0, sizeof( *renderView ) );
1596 
1597 	renderView->vieworg = GetPhysics()->GetOrigin();
1598 	renderView->fov_x = 120;
1599 	renderView->fov_y = 120;
1600 	renderView->viewaxis = GetPhysics()->GetAxis();
1601 
1602 	// copy global shader parms
1603 	for( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
1604 		renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
1605 	}
1606 
1607 	renderView->globalMaterial = gameLocal.GetGlobalMaterial();
1608 
1609 	renderView->time = gameLocal.time;
1610 
1611 	return renderView;
1612 }
1613 
1614 /***********************************************************************
1615 
1616   Sound
1617 
1618 ***********************************************************************/
1619 
1620 /*
1621 ================
1622 idEntity::CanPlayChatterSounds
1623 
1624 Used for playing chatter sounds on monsters.
1625 ================
1626 */
CanPlayChatterSounds(void) const1627 bool idEntity::CanPlayChatterSounds( void ) const {
1628 	return true;
1629 }
1630 
1631 /*
1632 ================
1633 idEntity::StartSound
1634 ================
1635 */
StartSound(const char * soundName,const s_channelType channel,int soundShaderFlags,bool broadcast,int * length)1636 bool idEntity::StartSound( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
1637 	const idSoundShader *shader;
1638 	const char *sound;
1639 
1640 	if ( length ) {
1641 		*length = 0;
1642 	}
1643 
1644 	// we should ALWAYS be playing sounds from the def.
1645 	// hardcoded sounds MUST be avoided at all times because they won't get precached.
1646 	assert( idStr::Icmpn( soundName, "snd_", 4 ) == 0 );
1647 
1648 	if ( !spawnArgs.GetString( soundName, "", &sound ) ) {
1649 		return false;
1650 	}
1651 
1652 	if ( sound[0] == '\0' ) {
1653 		return false;
1654 	}
1655 
1656 	if ( !gameLocal.isNewFrame ) {
1657 		// don't play the sound, but don't report an error
1658 		return true;
1659 	}
1660 
1661 	shader = declManager->FindSound( sound );
1662 	return StartSoundShader( shader, channel, soundShaderFlags, broadcast, length );
1663 }
1664 
1665 /*
1666 ================
1667 idEntity::StartSoundShader
1668 ================
1669 */
StartSoundShader(const idSoundShader * shader,const s_channelType channel,int soundShaderFlags,bool broadcast,int * length)1670 bool idEntity::StartSoundShader( const idSoundShader *shader, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
1671 	float diversity;
1672 	int len;
1673 
1674 	if ( length ) {
1675 		*length = 0;
1676 	}
1677 
1678 	if ( !shader ) {
1679 		return false;
1680 	}
1681 
1682 	if ( !gameLocal.isNewFrame ) {
1683 		return true;
1684 	}
1685 
1686 	if ( gameLocal.isServer && broadcast ) {
1687 		idBitMsg	msg;
1688 		byte		msgBuf[MAX_EVENT_PARAM_SIZE];
1689 
1690 		msg.Init( msgBuf, sizeof( msgBuf ) );
1691 		msg.BeginWriting();
1692 		msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_SOUND, shader->Index() ) );
1693 		msg.WriteByte( channel );
1694 		ServerSendEvent( EVENT_STARTSOUNDSHADER, &msg, false, -1 );
1695 	}
1696 
1697 	// set a random value for diversity unless one was parsed from the entity
1698 	if ( refSound.diversity < 0.0f ) {
1699 		diversity = gameLocal.random.RandomFloat();
1700 	} else {
1701 		diversity = refSound.diversity;
1702 	}
1703 
1704 	// if we don't have a soundEmitter allocated yet, get one now
1705 	if ( !refSound.referenceSound ) {
1706 		refSound.referenceSound = gameSoundWorld->AllocSoundEmitter();
1707 	}
1708 
1709 	UpdateSound();
1710 
1711 	len = refSound.referenceSound->StartSound( shader, channel, diversity, soundShaderFlags, !timeGroup /*_D3XP*/ );
1712 	if ( length ) {
1713 		*length = len;
1714 	}
1715 
1716 	// set reference to the sound for shader synced effects
1717 	renderEntity.referenceSound = refSound.referenceSound;
1718 
1719 	return true;
1720 }
1721 
1722 /*
1723 ================
1724 idEntity::StopSound
1725 ================
1726 */
StopSound(const s_channelType channel,bool broadcast)1727 void idEntity::StopSound( const s_channelType channel, bool broadcast ) {
1728 	if ( !gameLocal.isNewFrame ) {
1729 		return;
1730 	}
1731 
1732 	if ( gameLocal.isServer && broadcast ) {
1733 		idBitMsg	msg;
1734 		byte		msgBuf[MAX_EVENT_PARAM_SIZE];
1735 
1736 		msg.Init( msgBuf, sizeof( msgBuf ) );
1737 		msg.BeginWriting();
1738 		msg.WriteByte( channel );
1739 		ServerSendEvent( EVENT_STOPSOUNDSHADER, &msg, false, -1 );
1740 	}
1741 
1742 	if ( refSound.referenceSound ) {
1743 		refSound.referenceSound->StopSound( channel );
1744 	}
1745 }
1746 
1747 /*
1748 ================
1749 idEntity::SetSoundVolume
1750 
1751   Must be called before starting a new sound.
1752 ================
1753 */
SetSoundVolume(float volume)1754 void idEntity::SetSoundVolume( float volume ) {
1755 	refSound.parms.volume = volume;
1756 }
1757 
1758 /*
1759 ================
1760 idEntity::UpdateSound
1761 ================
1762 */
UpdateSound(void)1763 void idEntity::UpdateSound( void ) {
1764 	if ( refSound.referenceSound ) {
1765 		idVec3 origin;
1766 		idMat3 axis;
1767 
1768 		if ( GetPhysicsToSoundTransform( origin, axis ) ) {
1769 			refSound.origin = GetPhysics()->GetOrigin() + origin * axis;
1770 		} else {
1771 			refSound.origin = GetPhysics()->GetOrigin();
1772 		}
1773 
1774 		refSound.referenceSound->UpdateEmitter( refSound.origin, refSound.listenerId, &refSound.parms );
1775 	}
1776 }
1777 
1778 /*
1779 ================
1780 idEntity::GetListenerId
1781 ================
1782 */
GetListenerId(void) const1783 int idEntity::GetListenerId( void ) const {
1784 	return refSound.listenerId;
1785 }
1786 
1787 /*
1788 ================
1789 idEntity::GetSoundEmitter
1790 ================
1791 */
GetSoundEmitter(void) const1792 idSoundEmitter *idEntity::GetSoundEmitter( void ) const {
1793 	return refSound.referenceSound;
1794 }
1795 
1796 /*
1797 ================
1798 idEntity::FreeSoundEmitter
1799 ================
1800 */
FreeSoundEmitter(bool immediate)1801 void idEntity::FreeSoundEmitter( bool immediate ) {
1802 	if ( refSound.referenceSound ) {
1803 		refSound.referenceSound->Free( immediate );
1804 		refSound.referenceSound = NULL;
1805 	}
1806 }
1807 
1808 /***********************************************************************
1809 
1810   entity binding
1811 
1812 ***********************************************************************/
1813 
1814 /*
1815 ================
1816 idEntity::PreBind
1817 ================
1818 */
PreBind(void)1819 void idEntity::PreBind( void ) {
1820 }
1821 
1822 /*
1823 ================
1824 idEntity::PostBind
1825 ================
1826 */
PostBind(void)1827 void idEntity::PostBind( void ) {
1828 }
1829 
1830 /*
1831 ================
1832 idEntity::PreUnbind
1833 ================
1834 */
PreUnbind(void)1835 void idEntity::PreUnbind( void ) {
1836 }
1837 
1838 /*
1839 ================
1840 idEntity::PostUnbind
1841 ================
1842 */
PostUnbind(void)1843 void idEntity::PostUnbind( void ) {
1844 }
1845 
1846 /*
1847 ================
1848 idEntity::InitBind
1849 ================
1850 */
InitBind(idEntity * master)1851 bool idEntity::InitBind( idEntity *master ) {
1852 
1853 	if ( master == this ) {
1854 		gameLocal.Error( "Tried to bind an object to itself." );
1855 		return false;
1856 	}
1857 
1858 	if ( this == gameLocal.world ) {
1859 		gameLocal.Error( "Tried to bind world to another entity" );
1860 		return false;
1861 	}
1862 
1863 	// unbind myself from my master
1864 	Unbind();
1865 
1866 	// add any bind constraints to an articulated figure
1867 	if ( master && IsType( idAFEntity_Base::Type ) ) {
1868 		static_cast<idAFEntity_Base *>(this)->AddBindConstraints();
1869 	}
1870 
1871 	if ( !master || master == gameLocal.world ) {
1872 		// this can happen in scripts, so safely exit out.
1873 		return false;
1874 	}
1875 
1876 	return true;
1877 }
1878 
1879 /*
1880 ================
1881 idEntity::FinishBind
1882 ================
1883 */
FinishBind(void)1884 void idEntity::FinishBind( void ) {
1885 
1886 	// set the master on the physics object
1887 	physics->SetMaster( bindMaster, fl.bindOrientated );
1888 
1889 	// We are now separated from our previous team and are either
1890 	// an individual, or have a team of our own.  Now we can join
1891 	// the new bindMaster's team.  Bindmaster must be set before
1892 	// joining the team, or we will be placed in the wrong position
1893 	// on the team.
1894 	JoinTeam( bindMaster );
1895 
1896 	// if our bindMaster is enabled during a cinematic, we must be, too
1897 	cinematic = bindMaster->cinematic;
1898 
1899 	// make sure the team master is active so that physics get run
1900 	teamMaster->BecomeActive( TH_PHYSICS );
1901 }
1902 
1903 /*
1904 ================
1905 idEntity::Bind
1906 
1907   bind relative to the visual position of the master
1908 ================
1909 */
Bind(idEntity * master,bool orientated)1910 void idEntity::Bind( idEntity *master, bool orientated ) {
1911 
1912 	if ( !InitBind( master ) ) {
1913 		return;
1914 	}
1915 
1916 	PreBind();
1917 
1918 	bindJoint = INVALID_JOINT;
1919 	bindBody = -1;
1920 	bindMaster = master;
1921 	fl.bindOrientated = orientated;
1922 
1923 	FinishBind();
1924 
1925 	PostBind( );
1926 }
1927 
1928 /*
1929 ================
1930 idEntity::BindToJoint
1931 
1932   bind relative to a joint of the md5 model used by the master
1933 ================
1934 */
BindToJoint(idEntity * master,const char * jointname,bool orientated)1935 void idEntity::BindToJoint( idEntity *master, const char *jointname, bool orientated ) {
1936 	jointHandle_t	jointnum;
1937 	idAnimator		*masterAnimator;
1938 
1939 	if ( !InitBind( master ) ) {
1940 		return;
1941 	}
1942 
1943 	masterAnimator = master->GetAnimator();
1944 	if ( !masterAnimator ) {
1945 		gameLocal.Warning( "idEntity::BindToJoint: entity '%s' cannot support skeletal models.", master->GetName() );
1946 		return;
1947 	}
1948 
1949 	jointnum = masterAnimator->GetJointHandle( jointname );
1950 	if ( jointnum == INVALID_JOINT ) {
1951 		gameLocal.Warning( "idEntity::BindToJoint: joint '%s' not found on entity '%s'.", jointname, master->GetName() );
1952 	}
1953 
1954 	PreBind();
1955 
1956 	bindJoint = jointnum;
1957 	bindBody = -1;
1958 	bindMaster = master;
1959 	fl.bindOrientated = orientated;
1960 
1961 	FinishBind();
1962 
1963 	PostBind();
1964 }
1965 
1966 /*
1967 ================
1968 idEntity::BindToJoint
1969 
1970   bind relative to a joint of the md5 model used by the master
1971 ================
1972 */
BindToJoint(idEntity * master,jointHandle_t jointnum,bool orientated)1973 void idEntity::BindToJoint( idEntity *master, jointHandle_t jointnum, bool orientated ) {
1974 
1975 	if ( !InitBind( master ) ) {
1976 		return;
1977 	}
1978 
1979 	PreBind();
1980 
1981 	bindJoint = jointnum;
1982 	bindBody = -1;
1983 	bindMaster = master;
1984 	fl.bindOrientated = orientated;
1985 
1986 	FinishBind();
1987 
1988 	PostBind();
1989 }
1990 
1991 /*
1992 ================
1993 idEntity::BindToBody
1994 
1995   bind relative to a collision model used by the physics of the master
1996 ================
1997 */
BindToBody(idEntity * master,int bodyId,bool orientated)1998 void idEntity::BindToBody( idEntity *master, int bodyId, bool orientated ) {
1999 
2000 	if ( !InitBind( master ) ) {
2001 		return;
2002 	}
2003 
2004 	if ( bodyId < 0 ) {
2005 		gameLocal.Warning( "idEntity::BindToBody: body '%d' not found.", bodyId );
2006 	}
2007 
2008 	PreBind();
2009 
2010 	bindJoint = INVALID_JOINT;
2011 	bindBody = bodyId;
2012 	bindMaster = master;
2013 	fl.bindOrientated = orientated;
2014 
2015 	FinishBind();
2016 
2017 	PostBind();
2018 }
2019 
2020 /*
2021 ================
2022 idEntity::Unbind
2023 ================
2024 */
Unbind(void)2025 void idEntity::Unbind( void ) {
2026 	idEntity *	prev;
2027 	idEntity *	next;
2028 	idEntity *	last;
2029 	idEntity *	ent;
2030 
2031 	// remove any bind constraints from an articulated figure
2032 	if ( IsType( idAFEntity_Base::Type ) ) {
2033 		static_cast<idAFEntity_Base *>(this)->RemoveBindConstraints();
2034 	}
2035 
2036 	if ( !bindMaster ) {
2037 		return;
2038 	}
2039 
2040 	if ( !teamMaster ) {
2041 		// Teammaster already has been freed
2042 		bindMaster = NULL;
2043 		return;
2044 	}
2045 
2046 	PreUnbind();
2047 
2048 	if ( physics ) {
2049 		physics->SetMaster( NULL, fl.bindOrientated );
2050 	}
2051 
2052 	// We're still part of a team, so that means I have to extricate myself
2053 	// and any entities that are bound to me from the old team.
2054 	// Find the node previous to me in the team
2055 	prev = teamMaster;
2056 	for( ent = teamMaster->teamChain; ent && ( ent != this ); ent = ent->teamChain ) {
2057 		prev = ent;
2058 	}
2059 
2060 	assert( ent == this ); // If ent is not pointing to this, then something is very wrong.
2061 
2062 	// Find the last node in my team that is bound to me.
2063 	// Also find the first node not bound to me, if one exists.
2064 	last = this;
2065 	for( next = teamChain; next != NULL; next = next->teamChain ) {
2066 		if ( !next->IsBoundTo( this ) ) {
2067 			break;
2068 		}
2069 
2070 		// Tell them I'm now the teamMaster
2071 		next->teamMaster = this;
2072 		last = next;
2073 	}
2074 
2075 	// disconnect the last member of our team from the old team
2076 	last->teamChain = NULL;
2077 
2078 	// connect up the previous member of the old team to the node that
2079 	// follow the last node bound to me (if one exists).
2080 	if ( teamMaster != this ) {
2081 		prev->teamChain = next;
2082 		if ( !next && ( teamMaster == prev ) ) {
2083 			prev->teamMaster = NULL;
2084 		}
2085 	} else if ( next ) {
2086 		// If we were the teamMaster, then the nodes that were not bound to me are now
2087 		// a disconnected chain.  Make them into their own team.
2088 		for( ent = next; ent->teamChain != NULL; ent = ent->teamChain ) {
2089 			ent->teamMaster = next;
2090 		}
2091 		next->teamMaster = next;
2092 	}
2093 
2094 	// If we don't have anyone on our team, then clear the team variables.
2095 	if ( teamChain ) {
2096 		// make myself my own team
2097 		teamMaster = this;
2098 	} else {
2099 		// no longer a team
2100 		teamMaster = NULL;
2101 	}
2102 
2103 	bindJoint = INVALID_JOINT;
2104 	bindBody = -1;
2105 	bindMaster = NULL;
2106 
2107 	PostUnbind();
2108 }
2109 
2110 /*
2111 ================
2112 idEntity::RemoveBinds
2113 ================
2114 */
RemoveBinds(void)2115 void idEntity::RemoveBinds( void ) {
2116 	idEntity *ent;
2117 	idEntity *next;
2118 
2119 	for( ent = teamChain; ent != NULL; ent = next ) {
2120 		next = ent->teamChain;
2121 		if ( ent->bindMaster == this ) {
2122 			ent->Unbind();
2123 			ent->PostEventMS( &EV_Remove, 0 );
2124 			next = teamChain;
2125 		}
2126 	}
2127 }
2128 
2129 /*
2130 ================
2131 idEntity::IsBound
2132 ================
2133 */
IsBound(void) const2134 bool idEntity::IsBound( void ) const {
2135 	if ( bindMaster ) {
2136 		return true;
2137 	}
2138 	return false;
2139 }
2140 
2141 /*
2142 ================
2143 idEntity::IsBoundTo
2144 ================
2145 */
IsBoundTo(idEntity * master) const2146 bool idEntity::IsBoundTo( idEntity *master ) const {
2147 	idEntity *ent;
2148 
2149 	if ( !bindMaster ) {
2150 		return false;
2151 	}
2152 
2153 	for ( ent = bindMaster; ent != NULL; ent = ent->bindMaster ) {
2154 		if ( ent == master ) {
2155 			return true;
2156 		}
2157 	}
2158 
2159 	return false;
2160 }
2161 
2162 /*
2163 ================
2164 idEntity::GetBindMaster
2165 ================
2166 */
GetBindMaster(void) const2167 idEntity *idEntity::GetBindMaster( void ) const {
2168 	return bindMaster;
2169 }
2170 
2171 /*
2172 ================
2173 idEntity::GetBindJoint
2174 ================
2175 */
GetBindJoint(void) const2176 jointHandle_t idEntity::GetBindJoint( void ) const {
2177 	return bindJoint;
2178 }
2179 
2180 /*
2181 ================
2182 idEntity::GetBindBody
2183 ================
2184 */
GetBindBody(void) const2185 int idEntity::GetBindBody( void ) const {
2186 	return bindBody;
2187 }
2188 
2189 /*
2190 ================
2191 idEntity::GetTeamMaster
2192 ================
2193 */
GetTeamMaster(void) const2194 idEntity *idEntity::GetTeamMaster( void ) const {
2195 	return teamMaster;
2196 }
2197 
2198 /*
2199 ================
2200 idEntity::GetNextTeamEntity
2201 ================
2202 */
GetNextTeamEntity(void) const2203 idEntity *idEntity::GetNextTeamEntity( void ) const {
2204 	return teamChain;
2205 }
2206 
2207 /*
2208 =====================
2209 idEntity::ConvertLocalToWorldTransform
2210 =====================
2211 */
ConvertLocalToWorldTransform(idVec3 & offset,idMat3 & axis)2212 void idEntity::ConvertLocalToWorldTransform( idVec3 &offset, idMat3 &axis ) {
2213 	UpdateModelTransform();
2214 
2215 	offset = renderEntity.origin + offset * renderEntity.axis;
2216 	axis *= renderEntity.axis;
2217 }
2218 
2219 /*
2220 ================
2221 idEntity::GetLocalVector
2222 
2223 Takes a vector in worldspace and transforms it into the parent
2224 object's localspace.
2225 
2226 Note: Does not take origin into acount.  Use getLocalCoordinate to
2227 convert coordinates.
2228 ================
2229 */
GetLocalVector(const idVec3 & vec) const2230 idVec3 idEntity::GetLocalVector( const idVec3 &vec ) const {
2231 	idVec3	pos;
2232 
2233 	if ( !bindMaster ) {
2234 		return vec;
2235 	}
2236 
2237 	idVec3	masterOrigin;
2238 	idMat3	masterAxis;
2239 
2240 	GetMasterPosition( masterOrigin, masterAxis );
2241 	masterAxis.ProjectVector( vec, pos );
2242 
2243 	return pos;
2244 }
2245 
2246 /*
2247 ================
2248 idEntity::GetLocalCoordinates
2249 
2250 Takes a vector in world coordinates and transforms it into the parent
2251 object's local coordinates.
2252 ================
2253 */
GetLocalCoordinates(const idVec3 & vec) const2254 idVec3 idEntity::GetLocalCoordinates( const idVec3 &vec ) const {
2255 	idVec3	pos;
2256 
2257 	if ( !bindMaster ) {
2258 		return vec;
2259 	}
2260 
2261 	idVec3	masterOrigin;
2262 	idMat3	masterAxis;
2263 
2264 	GetMasterPosition( masterOrigin, masterAxis );
2265 	masterAxis.ProjectVector( vec - masterOrigin, pos );
2266 
2267 	return pos;
2268 }
2269 
2270 /*
2271 ================
2272 idEntity::GetWorldVector
2273 
2274 Takes a vector in the parent object's local coordinates and transforms
2275 it into world coordinates.
2276 
2277 Note: Does not take origin into acount.  Use getWorldCoordinate to
2278 convert coordinates.
2279 ================
2280 */
GetWorldVector(const idVec3 & vec) const2281 idVec3 idEntity::GetWorldVector( const idVec3 &vec ) const {
2282 	idVec3	pos;
2283 
2284 	if ( !bindMaster ) {
2285 		return vec;
2286 	}
2287 
2288 	idVec3	masterOrigin;
2289 	idMat3	masterAxis;
2290 
2291 	GetMasterPosition( masterOrigin, masterAxis );
2292 	masterAxis.UnprojectVector( vec, pos );
2293 
2294 	return pos;
2295 }
2296 
2297 /*
2298 ================
2299 idEntity::GetWorldCoordinates
2300 
2301 Takes a vector in the parent object's local coordinates and transforms
2302 it into world coordinates.
2303 ================
2304 */
GetWorldCoordinates(const idVec3 & vec) const2305 idVec3 idEntity::GetWorldCoordinates( const idVec3 &vec ) const {
2306 	idVec3	pos;
2307 
2308 	if ( !bindMaster ) {
2309 		return vec;
2310 	}
2311 
2312 	idVec3	masterOrigin;
2313 	idMat3	masterAxis;
2314 
2315 	GetMasterPosition( masterOrigin, masterAxis );
2316 	masterAxis.UnprojectVector( vec, pos );
2317 	pos += masterOrigin;
2318 
2319 	return pos;
2320 }
2321 
2322 /*
2323 ================
2324 idEntity::GetMasterPosition
2325 ================
2326 */
GetMasterPosition(idVec3 & masterOrigin,idMat3 & masterAxis) const2327 bool idEntity::GetMasterPosition( idVec3 &masterOrigin, idMat3 &masterAxis ) const {
2328 	idVec3		localOrigin;
2329 	idMat3		localAxis;
2330 	idAnimator	*masterAnimator;
2331 
2332 	if ( bindMaster ) {
2333 		// if bound to a joint of an animated model
2334 		if ( bindJoint != INVALID_JOINT ) {
2335 			masterAnimator = bindMaster->GetAnimator();
2336 			if ( !masterAnimator ) {
2337 				masterOrigin = vec3_origin;
2338 				masterAxis = mat3_identity;
2339 				return false;
2340 			} else {
2341 				masterAnimator->GetJointTransform( bindJoint, gameLocal.time, masterOrigin, masterAxis );
2342 				masterAxis *= bindMaster->renderEntity.axis;
2343 				masterOrigin = bindMaster->renderEntity.origin + masterOrigin * bindMaster->renderEntity.axis;
2344 			}
2345 		} else if ( bindBody >= 0 && bindMaster->GetPhysics() ) {
2346 			masterOrigin = bindMaster->GetPhysics()->GetOrigin( bindBody );
2347 			masterAxis = bindMaster->GetPhysics()->GetAxis( bindBody );
2348 		} else {
2349 			masterOrigin = bindMaster->renderEntity.origin;
2350 			masterAxis = bindMaster->renderEntity.axis;
2351 		}
2352 		return true;
2353 	} else {
2354 		masterOrigin = vec3_origin;
2355 		masterAxis = mat3_identity;
2356 		return false;
2357 	}
2358 }
2359 
2360 /*
2361 ================
2362 idEntity::GetWorldVelocities
2363 ================
2364 */
GetWorldVelocities(idVec3 & linearVelocity,idVec3 & angularVelocity) const2365 void idEntity::GetWorldVelocities( idVec3 &linearVelocity, idVec3 &angularVelocity ) const {
2366 
2367 	linearVelocity = physics->GetLinearVelocity();
2368 	angularVelocity = physics->GetAngularVelocity();
2369 
2370 	if ( bindMaster ) {
2371 		idVec3 masterOrigin, masterLinearVelocity, masterAngularVelocity;
2372 		idMat3 masterAxis;
2373 
2374 		// get position of master
2375 		GetMasterPosition( masterOrigin, masterAxis );
2376 
2377 		// get master velocities
2378 		bindMaster->GetWorldVelocities( masterLinearVelocity, masterAngularVelocity );
2379 
2380 		// linear velocity relative to master plus master linear and angular velocity
2381 		linearVelocity = linearVelocity * masterAxis + masterLinearVelocity +
2382 								masterAngularVelocity.Cross( GetPhysics()->GetOrigin() - masterOrigin );
2383 	}
2384 }
2385 
2386 /*
2387 ================
2388 idEntity::JoinTeam
2389 ================
2390 */
JoinTeam(idEntity * teammember)2391 void idEntity::JoinTeam( idEntity *teammember ) {
2392 	idEntity *ent;
2393 	idEntity *master;
2394 	idEntity *prev;
2395 	idEntity *next;
2396 
2397 	// if we're already on a team, quit it so we can join this one
2398 	if ( teamMaster && ( teamMaster != this ) ) {
2399 		QuitTeam();
2400 	}
2401 
2402 	assert( teammember );
2403 
2404 	if ( teammember == this ) {
2405 		teamMaster = this;
2406 		return;
2407 	}
2408 
2409 	// check if our new team mate is already on a team
2410 	master = teammember->teamMaster;
2411 	if ( !master ) {
2412 		// he's not on a team, so he's the new teamMaster
2413 		master = teammember;
2414 		teammember->teamMaster = teammember;
2415 		teammember->teamChain = this;
2416 
2417 		// make anyone who's bound to me part of the new team
2418 		for( ent = teamChain; ent != NULL; ent = ent->teamChain ) {
2419 			ent->teamMaster = master;
2420 		}
2421 	} else {
2422 		// skip past the chain members bound to the entity we're teaming up with
2423 		prev = teammember;
2424 		next = teammember->teamChain;
2425 		if ( bindMaster ) {
2426 			// if we have a bindMaster, join after any entities bound to the entity
2427 			// we're joining
2428 			while( next && next->IsBoundTo( teammember ) ) {
2429 				prev = next;
2430 				next = next->teamChain;
2431 			}
2432 		} else {
2433 			// if we're not bound to someone, then put us at the end of the team
2434 			while( next ) {
2435 				prev = next;
2436 				next = next->teamChain;
2437 			}
2438 		}
2439 
2440 		// make anyone who's bound to me part of the new team and
2441 		// also find the last member of my team
2442 		for( ent = this; ent->teamChain != NULL; ent = ent->teamChain ) {
2443 			ent->teamChain->teamMaster = master;
2444 		}
2445 
2446 		prev->teamChain = this;
2447 		ent->teamChain = next;
2448 	}
2449 
2450 	teamMaster = master;
2451 
2452 	// reorder the active entity list
2453 	gameLocal.sortTeamMasters = true;
2454 }
2455 
2456 /*
2457 ================
2458 idEntity::QuitTeam
2459 ================
2460 */
QuitTeam(void)2461 void idEntity::QuitTeam( void ) {
2462 	idEntity *ent;
2463 
2464 	if ( !teamMaster ) {
2465 		return;
2466 	}
2467 
2468 	// check if I'm the teamMaster
2469 	if ( teamMaster == this ) {
2470 		// do we have more than one teammate?
2471 		if ( !teamChain->teamChain ) {
2472 			// no, break up the team
2473 			teamChain->teamMaster = NULL;
2474 		} else {
2475 			// yes, so make the first teammate the teamMaster
2476 			for( ent = teamChain; ent; ent = ent->teamChain ) {
2477 				ent->teamMaster = teamChain;
2478 			}
2479 		}
2480 	} else {
2481 		assert( teamMaster );
2482 		assert( teamMaster->teamChain );
2483 
2484 		// find the previous member of the teamChain
2485 		ent = teamMaster;
2486 		while( ent->teamChain != this ) {
2487 			assert( ent->teamChain ); // this should never happen
2488 			ent = ent->teamChain;
2489 		}
2490 
2491 		// remove this from the teamChain
2492 		ent->teamChain = teamChain;
2493 
2494 		// if no one is left on the team, break it up
2495 		if ( !teamMaster->teamChain ) {
2496 			teamMaster->teamMaster = NULL;
2497 		}
2498 	}
2499 
2500 	teamMaster = NULL;
2501 	teamChain = NULL;
2502 }
2503 
2504 /***********************************************************************
2505 
2506   Physics.
2507 
2508 ***********************************************************************/
2509 
2510 /*
2511 ================
2512 idEntity::InitDefaultPhysics
2513 ================
2514 */
InitDefaultPhysics(const idVec3 & origin,const idMat3 & axis)2515 void idEntity::InitDefaultPhysics( const idVec3 &origin, const idMat3 &axis ) {
2516 	const char *temp;
2517 	idClipModel *clipModel = NULL;
2518 
2519 	// check if a clipmodel key/value pair is set
2520 	if ( spawnArgs.GetString( "clipmodel", "", &temp ) ) {
2521 		if ( idClipModel::CheckModel( temp ) ) {
2522 			clipModel = new idClipModel( temp );
2523 		}
2524 	}
2525 
2526 	if ( !spawnArgs.GetBool( "noclipmodel", "0" ) ) {
2527 
2528 		// check if mins/maxs or size key/value pairs are set
2529 		if ( !clipModel ) {
2530 			idVec3 size;
2531 			idBounds bounds;
2532 			bool setClipModel = false;
2533 
2534 			if ( spawnArgs.GetVector( "mins", NULL, bounds[0] ) &&
2535 				spawnArgs.GetVector( "maxs", NULL, bounds[1] ) ) {
2536 				setClipModel = true;
2537 				if ( bounds[0][0] > bounds[1][0] || bounds[0][1] > bounds[1][1] || bounds[0][2] > bounds[1][2] ) {
2538 					gameLocal.Error( "Invalid bounds '%s'-'%s' on entity '%s'", bounds[0].ToString(), bounds[1].ToString(), name.c_str() );
2539 				}
2540 			} else if ( spawnArgs.GetVector( "size", NULL, size ) ) {
2541 				if ( ( size.x < 0.0f ) || ( size.y < 0.0f ) || ( size.z < 0.0f ) ) {
2542 					gameLocal.Error( "Invalid size '%s' on entity '%s'", size.ToString(), name.c_str() );
2543 				}
2544 				bounds[0].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
2545 				bounds[1].Set( size.x * 0.5f, size.y * 0.5f, size.z );
2546 				setClipModel = true;
2547 			}
2548 
2549 			if ( setClipModel ) {
2550 				int numSides;
2551 				idTraceModel trm;
2552 
2553 				if ( spawnArgs.GetInt( "cylinder", "0", numSides ) && numSides > 0 ) {
2554 					trm.SetupCylinder( bounds, numSides < 3 ? 3 : numSides );
2555 				} else if ( spawnArgs.GetInt( "cone", "0", numSides ) && numSides > 0 ) {
2556 					trm.SetupCone( bounds, numSides < 3 ? 3 : numSides );
2557 				} else {
2558 					trm.SetupBox( bounds );
2559 				}
2560 				clipModel = new idClipModel( trm );
2561 			}
2562 		}
2563 
2564 		// check if the visual model can be used as collision model
2565 		if ( !clipModel ) {
2566 			temp = spawnArgs.GetString( "model" );
2567 			if ( ( temp != NULL ) && ( *temp != 0 ) ) {
2568 				if ( idClipModel::CheckModel( temp ) ) {
2569 					clipModel = new idClipModel( temp );
2570 				}
2571 			}
2572 		}
2573 	}
2574 
2575 	defaultPhysicsObj.SetSelf( this );
2576 	defaultPhysicsObj.SetClipModel( clipModel, 1.0f );
2577 	defaultPhysicsObj.SetOrigin( origin );
2578 	defaultPhysicsObj.SetAxis( axis );
2579 
2580 	physics = &defaultPhysicsObj;
2581 }
2582 
2583 /*
2584 ================
2585 idEntity::SetPhysics
2586 ================
2587 */
SetPhysics(idPhysics * phys)2588 void idEntity::SetPhysics( idPhysics *phys ) {
2589 	// clear any contacts the current physics object has
2590 	if ( physics ) {
2591 		physics->ClearContacts();
2592 	}
2593 	// set new physics object or set the default physics if NULL
2594 	if ( phys != NULL ) {
2595 		defaultPhysicsObj.SetClipModel( NULL, 1.0f );
2596 		physics = phys;
2597 		physics->Activate();
2598 	} else {
2599 		physics = &defaultPhysicsObj;
2600 	}
2601 	physics->UpdateTime( gameLocal.time );
2602 	physics->SetMaster( bindMaster, fl.bindOrientated );
2603 }
2604 
2605 /*
2606 ================
2607 idEntity::RestorePhysics
2608 ================
2609 */
RestorePhysics(idPhysics * phys)2610 void idEntity::RestorePhysics( idPhysics *phys ) {
2611 	assert( phys != NULL );
2612 	// restore physics pointer
2613 	physics = phys;
2614 }
2615 
2616 /*
2617 ================
2618 idEntity::GetPhysics
2619 ================
2620 */
GetPhysics(void) const2621 idPhysics *idEntity::GetPhysics( void ) const {
2622 	return physics;
2623 }
2624 
2625 /*
2626 ================
2627 idEntity::RunPhysics
2628 ================
2629 */
RunPhysics(void)2630 bool idEntity::RunPhysics( void ) {
2631 	int			i, reachedTime, startTime, endTime;
2632 	idEntity *	part, *blockedPart, *blockingEntity;
2633 	bool		moved;
2634 
2635 	// don't run physics if not enabled
2636 	if ( !( thinkFlags & TH_PHYSICS ) ) {
2637 		// however do update any animation controllers
2638 		if ( UpdateAnimationControllers() ) {
2639 			BecomeActive( TH_ANIMATE );
2640 		}
2641 		return false;
2642 	}
2643 
2644 	// if this entity is a team slave don't do anything because the team master will handle everything
2645 	if ( teamMaster && teamMaster != this ) {
2646 		return false;
2647 	}
2648 
2649 	startTime = gameLocal.previousTime;
2650 	endTime = gameLocal.time;
2651 
2652 	gameLocal.push.InitSavingPushedEntityPositions();
2653 	blockedPart = NULL;
2654 
2655 	// save the physics state of the whole team and disable the team for collision detection
2656 	for ( part = this; part != NULL; part = part->teamChain ) {
2657 		if ( part->physics ) {
2658 			if ( !part->fl.solidForTeam ) {
2659 				part->physics->DisableClip();
2660 			}
2661 			part->physics->SaveState();
2662 		}
2663 	}
2664 
2665 	// move the whole team
2666 	for ( part = this; part != NULL; part = part->teamChain ) {
2667 
2668 		if ( part->physics ) {
2669 
2670 			// run physics
2671 			moved = part->physics->Evaluate( endTime - startTime, endTime );
2672 
2673 			// check if the object is blocked
2674 			blockingEntity = part->physics->GetBlockingEntity();
2675 			if ( blockingEntity ) {
2676 				blockedPart = part;
2677 				break;
2678 			}
2679 
2680 			// if moved or forced to update the visual position and orientation from the physics
2681 			if ( moved || part->fl.forcePhysicsUpdate ) {
2682 				part->UpdateFromPhysics( false );
2683 			}
2684 
2685 			// update any animation controllers here so an entity bound
2686 			// to a joint of this entity gets the correct position
2687 			if ( part->UpdateAnimationControllers() ) {
2688 				part->BecomeActive( TH_ANIMATE );
2689 			}
2690 		}
2691 	}
2692 
2693 	// enable the whole team for collision detection
2694 	for ( part = this; part != NULL; part = part->teamChain ) {
2695 		if ( part->physics ) {
2696 			if ( !part->fl.solidForTeam ) {
2697 				part->physics->EnableClip();
2698 			}
2699 		}
2700 	}
2701 
2702 	// if one of the team entities is a pusher and blocked
2703 	if ( blockedPart ) {
2704 		// move the parts back to the previous position
2705 		for ( part = this; part != blockedPart; part = part->teamChain ) {
2706 
2707 			if ( part->physics ) {
2708 
2709 				// restore the physics state
2710 				part->physics->RestoreState();
2711 
2712 				// move back the visual position and orientation
2713 				part->UpdateFromPhysics( true );
2714 			}
2715 		}
2716 		for ( part = this; part != NULL; part = part->teamChain ) {
2717 			if ( part->physics ) {
2718 				// update the physics time without moving
2719 				part->physics->UpdateTime( endTime );
2720 			}
2721 		}
2722 
2723 		// restore the positions of any pushed entities
2724 		gameLocal.push.RestorePushedEntityPositions();
2725 
2726 		if ( gameLocal.isClient ) {
2727 			return false;
2728 		}
2729 
2730 		// if the master pusher has a "blocked" function, call it
2731 		Signal( SIG_BLOCKED );
2732 		ProcessEvent( &EV_TeamBlocked, blockedPart, blockingEntity );
2733 		// call the blocked function on the blocked part
2734 		blockedPart->ProcessEvent( &EV_PartBlocked, blockingEntity );
2735 		return false;
2736 	}
2737 
2738 	// set pushed
2739 	for ( i = 0; i < gameLocal.push.GetNumPushedEntities(); i++ ) {
2740 		idEntity *ent = gameLocal.push.GetPushedEntity( i );
2741 		ent->physics->SetPushed( endTime - startTime );
2742 	}
2743 
2744 	if ( gameLocal.isClient ) {
2745 		return true;
2746 	}
2747 
2748 	// post reached event if the current time is at or past the end point of the motion
2749 	for ( part = this; part != NULL; part = part->teamChain ) {
2750 
2751 		if ( part->physics ) {
2752 
2753 			reachedTime = part->physics->GetLinearEndTime();
2754 			if ( startTime < reachedTime && endTime >= reachedTime ) {
2755 				part->ProcessEvent( &EV_ReachedPos );
2756 			}
2757 			reachedTime = part->physics->GetAngularEndTime();
2758 			if ( startTime < reachedTime && endTime >= reachedTime ) {
2759 				part->ProcessEvent( &EV_ReachedAng );
2760 			}
2761 		}
2762 	}
2763 
2764 	return true;
2765 }
2766 
2767 /*
2768 ================
2769 idEntity::UpdateFromPhysics
2770 ================
2771 */
UpdateFromPhysics(bool moveBack)2772 void idEntity::UpdateFromPhysics( bool moveBack ) {
2773 
2774 	if ( IsType( idActor::Type ) ) {
2775 		idActor *actor = static_cast<idActor *>( this );
2776 
2777 		// set master delta angles for actors
2778 		if ( GetBindMaster() ) {
2779 			idAngles delta = actor->GetDeltaViewAngles();
2780 			if ( moveBack ) {
2781 				delta.yaw -= static_cast<idPhysics_Actor *>(physics)->GetMasterDeltaYaw();
2782 			} else {
2783 				delta.yaw += static_cast<idPhysics_Actor *>(physics)->GetMasterDeltaYaw();
2784 			}
2785 			actor->SetDeltaViewAngles( delta );
2786 		}
2787 	}
2788 
2789 	UpdateVisuals();
2790 }
2791 
2792 /*
2793 ================
2794 idEntity::SetOrigin
2795 ================
2796 */
SetOrigin(const idVec3 & org)2797 void idEntity::SetOrigin( const idVec3 &org ) {
2798 
2799 	GetPhysics()->SetOrigin( org );
2800 
2801 	UpdateVisuals();
2802 }
2803 
2804 /*
2805 ================
2806 idEntity::SetAxis
2807 ================
2808 */
SetAxis(const idMat3 & axis)2809 void idEntity::SetAxis( const idMat3 &axis ) {
2810 
2811 	if ( GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
2812 		static_cast<idActor *>(this)->viewAxis = axis;
2813 	} else {
2814 		GetPhysics()->SetAxis( axis );
2815 	}
2816 
2817 	UpdateVisuals();
2818 }
2819 
2820 /*
2821 ================
2822 idEntity::SetAngles
2823 ================
2824 */
SetAngles(const idAngles & ang)2825 void idEntity::SetAngles( const idAngles &ang ) {
2826 	SetAxis( ang.ToMat3() );
2827 }
2828 
2829 /*
2830 ================
2831 idEntity::GetFloorPos
2832 ================
2833 */
GetFloorPos(float max_dist,idVec3 & floorpos) const2834 bool idEntity::GetFloorPos( float max_dist, idVec3 &floorpos ) const {
2835 	trace_t result;
2836 
2837 	if ( !GetPhysics()->HasGroundContacts() ) {
2838 		GetPhysics()->ClipTranslation( result, GetPhysics()->GetGravityNormal() * max_dist, NULL );
2839 		if ( result.fraction < 1.0f ) {
2840 			floorpos = result.endpos;
2841 			return true;
2842 		} else {
2843 			floorpos = GetPhysics()->GetOrigin();
2844 			return false;
2845 		}
2846 	} else {
2847 		floorpos = GetPhysics()->GetOrigin();
2848 		return true;
2849 	}
2850 }
2851 
2852 /*
2853 ================
2854 idEntity::GetPhysicsToVisualTransform
2855 ================
2856 */
GetPhysicsToVisualTransform(idVec3 & origin,idMat3 & axis)2857 bool idEntity::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
2858 	return false;
2859 }
2860 
2861 /*
2862 ================
2863 idEntity::GetPhysicsToSoundTransform
2864 ================
2865 */
GetPhysicsToSoundTransform(idVec3 & origin,idMat3 & axis)2866 bool idEntity::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
2867 	// by default play the sound at the center of the bounding box of the first clip model
2868 	if ( GetPhysics()->GetNumClipModels() > 0 ) {
2869 		origin = GetPhysics()->GetBounds().GetCenter();
2870 		axis.Identity();
2871 		return true;
2872 	}
2873 	return false;
2874 }
2875 
2876 /*
2877 ================
2878 idEntity::Collide
2879 ================
2880 */
Collide(const trace_t & collision,const idVec3 & velocity)2881 bool idEntity::Collide( const trace_t &collision, const idVec3 &velocity ) {
2882 	// this entity collides with collision.c.entityNum
2883 	return false;
2884 }
2885 
2886 /*
2887 ================
2888 idEntity::GetImpactInfo
2889 ================
2890 */
GetImpactInfo(idEntity * ent,int id,const idVec3 & point,impactInfo_t * info)2891 void idEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
2892 	GetPhysics()->GetImpactInfo( id, point, info );
2893 }
2894 
2895 /*
2896 ================
2897 idEntity::ApplyImpulse
2898 ================
2899 */
ApplyImpulse(idEntity * ent,int id,const idVec3 & point,const idVec3 & impulse)2900 void idEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
2901 	GetPhysics()->ApplyImpulse( id, point, impulse );
2902 }
2903 
2904 /*
2905 ================
2906 idEntity::AddForce
2907 ================
2908 */
AddForce(idEntity * ent,int id,const idVec3 & point,const idVec3 & force)2909 void idEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
2910 	GetPhysics()->AddForce( id, point, force );
2911 }
2912 
2913 /*
2914 ================
2915 idEntity::ActivatePhysics
2916 ================
2917 */
ActivatePhysics(idEntity * ent)2918 void idEntity::ActivatePhysics( idEntity *ent ) {
2919 	GetPhysics()->Activate();
2920 }
2921 
2922 /*
2923 ================
2924 idEntity::IsAtRest
2925 ================
2926 */
IsAtRest(void) const2927 bool idEntity::IsAtRest( void ) const {
2928 	return GetPhysics()->IsAtRest();
2929 }
2930 
2931 /*
2932 ================
2933 idEntity::GetRestStartTime
2934 ================
2935 */
GetRestStartTime(void) const2936 int idEntity::GetRestStartTime( void ) const {
2937 	return GetPhysics()->GetRestStartTime();
2938 }
2939 
2940 /*
2941 ================
2942 idEntity::AddContactEntity
2943 ================
2944 */
AddContactEntity(idEntity * ent)2945 void idEntity::AddContactEntity( idEntity *ent ) {
2946 	GetPhysics()->AddContactEntity( ent );
2947 }
2948 
2949 /*
2950 ================
2951 idEntity::RemoveContactEntity
2952 ================
2953 */
RemoveContactEntity(idEntity * ent)2954 void idEntity::RemoveContactEntity( idEntity *ent ) {
2955 	GetPhysics()->RemoveContactEntity( ent );
2956 }
2957 
2958 
2959 
2960 /***********************************************************************
2961 
2962 	Damage
2963 
2964 ***********************************************************************/
2965 
2966 /*
2967 ============
2968 idEntity::CanDamage
2969 
2970 Returns true if the inflictor can directly damage the target.  Used for
2971 explosions and melee attacks.
2972 ============
2973 */
CanDamage(const idVec3 & origin,idVec3 & damagePoint) const2974 bool idEntity::CanDamage( const idVec3 &origin, idVec3 &damagePoint ) const {
2975 	idVec3	dest;
2976 	trace_t	tr;
2977 	idVec3	midpoint;
2978 
2979 	// use the midpoint of the bounds instead of the origin, because
2980 	// bmodels may have their origin at 0,0,0
2981 	midpoint = ( GetPhysics()->GetAbsBounds()[0] + GetPhysics()->GetAbsBounds()[1] ) * 0.5;
2982 
2983 	dest = midpoint;
2984 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
2985 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
2986 		damagePoint = tr.endpos;
2987 		return true;
2988 	}
2989 
2990 	// this should probably check in the plane of projection, rather than in world coordinate
2991 	dest = midpoint;
2992 	dest[0] += 15.0;
2993 	dest[1] += 15.0;
2994 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
2995 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
2996 		damagePoint = tr.endpos;
2997 		return true;
2998 	}
2999 
3000 	dest = midpoint;
3001 	dest[0] += 15.0;
3002 	dest[1] -= 15.0;
3003 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
3004 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
3005 		damagePoint = tr.endpos;
3006 		return true;
3007 	}
3008 
3009 	dest = midpoint;
3010 	dest[0] -= 15.0;
3011 	dest[1] += 15.0;
3012 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
3013 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
3014 		damagePoint = tr.endpos;
3015 		return true;
3016 	}
3017 
3018 	dest = midpoint;
3019 	dest[0] -= 15.0;
3020 	dest[1] -= 15.0;
3021 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
3022 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
3023 		damagePoint = tr.endpos;
3024 		return true;
3025 	}
3026 
3027 	dest = midpoint;
3028 	dest[2] += 15.0;
3029 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
3030 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
3031 		damagePoint = tr.endpos;
3032 		return true;
3033 	}
3034 
3035 	dest = midpoint;
3036 	dest[2] -= 15.0;
3037 	gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
3038 	if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
3039 		damagePoint = tr.endpos;
3040 		return true;
3041 	}
3042 
3043 	return false;
3044 }
3045 
3046 /*
3047 ================
3048 idEntity::DamageFeedback
3049 
3050 callback function for when another entity received damage from this entity.  damage can be adjusted and returned to the caller.
3051 ================
3052 */
DamageFeedback(idEntity * victim,idEntity * inflictor,int & damage)3053 void idEntity::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
3054 	// implemented in subclasses
3055 }
3056 
3057 /*
3058 ============
3059 Damage
3060 
3061 this		entity that is being damaged
3062 inflictor	entity that is causing the damage
3063 attacker	entity that caused the inflictor to damage targ
3064 	example: this=monster, inflictor=rocket, attacker=player
3065 
3066 dir			direction of the attack for knockback in global space
3067 point		point at which the damage is being inflicted, used for headshots
3068 damage		amount of damage being inflicted
3069 
3070 inflictor, attacker, dir, and point can be NULL for environmental effects
3071 
3072 ============
3073 */
Damage(idEntity * inflictor,idEntity * attacker,const idVec3 & dir,const char * damageDefName,const float damageScale,const int location)3074 void idEntity::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
3075 					  const char *damageDefName, const float damageScale, const int location ) {
3076 	if ( !fl.takedamage ) {
3077 		return;
3078 	}
3079 
3080 #ifdef _D3XP
3081 	SetTimeState ts( timeGroup );
3082 #endif
3083 
3084 	if ( !inflictor ) {
3085 		inflictor = gameLocal.world;
3086 	}
3087 
3088 	if ( !attacker ) {
3089 		attacker = gameLocal.world;
3090 	}
3091 
3092 	const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
3093 	if ( !damageDef ) {
3094 		gameLocal.Error( "Unknown damageDef '%s'\n", damageDefName );
3095 	}
3096 
3097 	int	damage = damageDef->GetInt( "damage" );
3098 
3099 	// inform the attacker that they hit someone
3100 	attacker->DamageFeedback( this, inflictor, damage );
3101 	if ( damage ) {
3102 		// do the damage
3103 		health -= damage;
3104 		if ( health <= 0 ) {
3105 			if ( health < -999 ) {
3106 				health = -999;
3107 			}
3108 
3109 			Killed( inflictor, attacker, damage, dir, location );
3110 		} else {
3111 			Pain( inflictor, attacker, damage, dir, location );
3112 		}
3113 	}
3114 }
3115 
3116 /*
3117 ================
3118 idEntity::AddDamageEffect
3119 ================
3120 */
AddDamageEffect(const trace_t & collision,const idVec3 & velocity,const char * damageDefName)3121 void idEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
3122 	const char *sound, *decal, *key;
3123 
3124 	const idDeclEntityDef *def = gameLocal.FindEntityDef( damageDefName, false );
3125 	if ( def == NULL ) {
3126 		return;
3127 	}
3128 
3129 	const char *materialType = gameLocal.sufaceTypeNames[ collision.c.material->GetSurfaceType() ];
3130 
3131 	// start impact sound based on material type
3132 	key = va( "snd_%s", materialType );
3133 	sound = spawnArgs.GetString( key );
3134 	if ( *sound == '\0' ) {
3135 		sound = def->dict.GetString( key );
3136 	}
3137 	if ( *sound != '\0' ) {
3138 		StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
3139 	}
3140 
3141 	if ( g_decals.GetBool() ) {
3142 		// place a wound overlay on the model
3143 		key = va( "mtr_wound_%s", materialType );
3144 		decal = spawnArgs.RandomPrefix( key, gameLocal.random );
3145 		if ( *decal == '\0' ) {
3146 			decal = def->dict.RandomPrefix( key, gameLocal.random );
3147 		}
3148 		if ( *decal != '\0' ) {
3149 			idVec3 dir = velocity;
3150 			dir.Normalize();
3151 			ProjectOverlay( collision.c.point, dir, 20.0f, decal );
3152 		}
3153 	}
3154 }
3155 
3156 /*
3157 ============
3158 idEntity::Pain
3159 
3160 Called whenever an entity recieves damage.  Returns whether the entity responds to the pain.
3161 This is a virtual function that subclasses are expected to implement.
3162 ============
3163 */
Pain(idEntity * inflictor,idEntity * attacker,int damage,const idVec3 & dir,int location)3164 bool idEntity::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
3165 	return false;
3166 }
3167 
3168 /*
3169 ============
3170 idEntity::Killed
3171 
3172 Called whenever an entity's health is reduced to 0 or less.
3173 This is a virtual function that subclasses are expected to implement.
3174 ============
3175 */
Killed(idEntity * inflictor,idEntity * attacker,int damage,const idVec3 & dir,int location)3176 void idEntity::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
3177 }
3178 
3179 
3180 /***********************************************************************
3181 
3182   Script functions
3183 
3184 ***********************************************************************/
3185 
3186 /*
3187 ================
3188 idEntity::ShouldConstructScriptObjectAtSpawn
3189 
3190 Called during idEntity::Spawn to see if it should construct the script object or not.
3191 Overridden by subclasses that need to spawn the script object themselves.
3192 ================
3193 */
ShouldConstructScriptObjectAtSpawn(void) const3194 bool idEntity::ShouldConstructScriptObjectAtSpawn( void ) const {
3195 	return true;
3196 }
3197 
3198 /*
3199 ================
3200 idEntity::ConstructScriptObject
3201 
3202 Called during idEntity::Spawn.  Calls the constructor on the script object.
3203 Can be overridden by subclasses when a thread doesn't need to be allocated.
3204 ================
3205 */
ConstructScriptObject(void)3206 idThread *idEntity::ConstructScriptObject( void ) {
3207 	idThread		*thread;
3208 	const function_t *constructor;
3209 
3210 	// init the script object's data
3211 	scriptObject.ClearObject();
3212 
3213 	// call script object's constructor
3214 	constructor = scriptObject.GetConstructor();
3215 	if ( constructor ) {
3216 		// start a thread that will initialize after Spawn is done being called
3217 		thread = new idThread();
3218 		thread->SetThreadName( name.c_str() );
3219 		thread->CallFunction( this, constructor, true );
3220 		thread->DelayedStart( 0 );
3221 	} else {
3222 		thread = NULL;
3223 	}
3224 
3225 	// clear out the object's memory
3226 	scriptObject.ClearObject();
3227 
3228 	return thread;
3229 }
3230 
3231 /*
3232 ================
3233 idEntity::DeconstructScriptObject
3234 
3235 Called during idEntity::~idEntity.  Calls the destructor on the script object.
3236 Can be overridden by subclasses when a thread doesn't need to be allocated.
3237 Not called during idGameLocal::MapShutdown.
3238 ================
3239 */
DeconstructScriptObject(void)3240 void idEntity::DeconstructScriptObject( void ) {
3241 	idThread		*thread;
3242 	const function_t *destructor;
3243 
3244 	// don't bother calling the script object's destructor on map shutdown
3245 	if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
3246 		return;
3247 	}
3248 
3249 	// call script object's destructor
3250 	destructor = scriptObject.GetDestructor();
3251 	if ( destructor ) {
3252 		// start a thread that will run immediately and be destroyed
3253 		thread = new idThread();
3254 		thread->SetThreadName( name.c_str() );
3255 		thread->CallFunction( this, destructor, true );
3256 		thread->Execute();
3257 		delete thread;
3258 	}
3259 }
3260 
3261 /*
3262 ================
3263 idEntity::HasSignal
3264 ================
3265 */
HasSignal(signalNum_t signalnum) const3266 bool idEntity::HasSignal( signalNum_t signalnum ) const {
3267 	if ( !signals ) {
3268 		return false;
3269 	}
3270 	assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
3271 	return ( signals->signal[ signalnum ].Num() > 0 );
3272 }
3273 
3274 /*
3275 ================
3276 idEntity::SetSignal
3277 ================
3278 */
SetSignal(signalNum_t signalnum,idThread * thread,const function_t * function)3279 void idEntity::SetSignal( signalNum_t signalnum, idThread *thread, const function_t *function ) {
3280 	int			i;
3281 	int			num;
3282 	signal_t	sig;
3283 	int			threadnum;
3284 
3285 	assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
3286 
3287 	if ( !signals ) {
3288 		signals = new signalList_t;
3289 	}
3290 
3291 	assert( thread );
3292 	threadnum = thread->GetThreadNum();
3293 
3294 	num = signals->signal[ signalnum ].Num();
3295 	for( i = 0; i < num; i++ ) {
3296 		if ( signals->signal[ signalnum ][ i ].threadnum == threadnum ) {
3297 			signals->signal[ signalnum ][ i ].function = function;
3298 			return;
3299 		}
3300 	}
3301 
3302 	if ( num >= MAX_SIGNAL_THREADS ) {
3303 		thread->Error( "Exceeded maximum number of signals per object" );
3304 	}
3305 
3306 	sig.threadnum = threadnum;
3307 	sig.function = function;
3308 	signals->signal[ signalnum ].Append( sig );
3309 }
3310 
3311 /*
3312 ================
3313 idEntity::ClearSignal
3314 ================
3315 */
ClearSignal(idThread * thread,signalNum_t signalnum)3316 void idEntity::ClearSignal( idThread *thread, signalNum_t signalnum ) {
3317 	assert( thread );
3318 	if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
3319 		gameLocal.Error( "Signal out of range" );
3320 	}
3321 
3322 	if ( !signals ) {
3323 		return;
3324 	}
3325 
3326 	signals->signal[ signalnum ].Clear();
3327 }
3328 
3329 /*
3330 ================
3331 idEntity::ClearSignalThread
3332 ================
3333 */
ClearSignalThread(signalNum_t signalnum,idThread * thread)3334 void idEntity::ClearSignalThread( signalNum_t signalnum, idThread *thread ) {
3335 	int	i;
3336 	int	num;
3337 	int	threadnum;
3338 
3339 	assert( thread );
3340 
3341 	if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
3342 		gameLocal.Error( "Signal out of range" );
3343 	}
3344 
3345 	if ( !signals ) {
3346 		return;
3347 	}
3348 
3349 	threadnum = thread->GetThreadNum();
3350 
3351 	num = signals->signal[ signalnum ].Num();
3352 	for( i = 0; i < num; i++ ) {
3353 		if ( signals->signal[ signalnum ][ i ].threadnum == threadnum ) {
3354 			signals->signal[ signalnum ].RemoveIndex( i );
3355 			return;
3356 		}
3357 	}
3358 }
3359 
3360 /*
3361 ================
3362 idEntity::Signal
3363 ================
3364 */
Signal(signalNum_t signalnum)3365 void idEntity::Signal( signalNum_t signalnum ) {
3366 	int			i;
3367 	int			num;
3368 	signal_t	sigs[ MAX_SIGNAL_THREADS ];
3369 	idThread	*thread;
3370 
3371 	assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
3372 
3373 	if ( !signals ) {
3374 		return;
3375 	}
3376 
3377 	// we copy the signal list since each thread has the potential
3378 	// to end any of the threads in the list.  By copying the list
3379 	// we don't have to worry about the list changing as we're
3380 	// processing it.
3381 	num = signals->signal[ signalnum ].Num();
3382 	for( i = 0; i < num; i++ ) {
3383 		sigs[ i ] = signals->signal[ signalnum ][ i ];
3384 	}
3385 
3386 	// clear out the signal list so that we don't get into an infinite loop
3387 	signals->signal[ signalnum ].Clear();
3388 
3389 	for( i = 0; i < num; i++ ) {
3390 		thread = idThread::GetThread( sigs[ i ].threadnum );
3391 		if ( thread ) {
3392 			thread->CallFunction( this, sigs[ i ].function, true );
3393 			thread->Execute();
3394 		}
3395 	}
3396 }
3397 
3398 /*
3399 ================
3400 idEntity::SignalEvent
3401 ================
3402 */
SignalEvent(idThread * thread,signalNum_t signalnum)3403 void idEntity::SignalEvent( idThread *thread, signalNum_t signalnum ) {
3404 	if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
3405 		gameLocal.Error( "Signal out of range" );
3406 	}
3407 
3408 	if ( !signals ) {
3409 		return;
3410 	}
3411 
3412 	Signal( signalnum );
3413 }
3414 
3415 /***********************************************************************
3416 
3417   Guis.
3418 
3419 ***********************************************************************/
3420 
3421 
3422 /*
3423 ================
3424 idEntity::TriggerGuis
3425 ================
3426 */
TriggerGuis(void)3427 void idEntity::TriggerGuis( void ) {
3428 	int i;
3429 	for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
3430 		if ( renderEntity.gui[ i ] ) {
3431 			renderEntity.gui[ i ]->Trigger( gameLocal.time );
3432 		}
3433 	}
3434 }
3435 
3436 /*
3437 ================
3438 idEntity::HandleGuiCommands
3439 ================
3440 */
HandleGuiCommands(idEntity * entityGui,const char * cmds)3441 bool idEntity::HandleGuiCommands( idEntity *entityGui, const char *cmds ) {
3442 	idEntity *targetEnt;
3443 	bool ret = false;
3444 	if ( entityGui && cmds && *cmds ) {
3445 		idLexer src;
3446 		idToken token, token2, token3, token4;
3447 		src.LoadMemory( cmds, strlen( cmds ), "guiCommands" );
3448 		while( 1 ) {
3449 
3450 			if ( !src.ReadToken( &token ) ) {
3451 				return ret;
3452 			}
3453 
3454 			if ( token == ";" ) {
3455 				continue;
3456 			}
3457 
3458 			if ( token.Icmp( "activate" ) == 0 ) {
3459 				bool targets = true;
3460 				if ( src.ReadToken( &token2 ) ) {
3461 					if ( token2 == ";" ) {
3462 						src.UnreadToken( &token2 );
3463 					} else {
3464 						targets = false;
3465 					}
3466 				}
3467 
3468 				if ( targets ) {
3469 					entityGui->ActivateTargets( this );
3470 				} else {
3471 					idEntity *ent = gameLocal.FindEntity( token2 );
3472 					if ( ent ) {
3473 						ent->Signal( SIG_TRIGGER );
3474 						ent->PostEventMS( &EV_Activate, 0, this );
3475 					}
3476 				}
3477 
3478 				entityGui->renderEntity.shaderParms[ SHADERPARM_MODE ] = 1.0f;
3479 				continue;
3480 			}
3481 
3482 
3483 			if ( token.Icmp( "runScript" ) == 0 ) {
3484 				if ( src.ReadToken( &token2 ) ) {
3485 					while( src.CheckTokenString( "::" ) ) {
3486 						idToken token3;
3487 						if ( !src.ReadToken( &token3 ) ) {
3488 							gameLocal.Error( "Expecting function name following '::' in gui for entity '%s'", entityGui->name.c_str() );
3489 						}
3490 						token2 += "::" + token3;
3491 					}
3492 					const function_t *func = gameLocal.program.FindFunction( token2 );
3493 					if ( !func ) {
3494 						gameLocal.Error( "Can't find function '%s' for gui in entity '%s'", token2.c_str(), entityGui->name.c_str() );
3495 					} else {
3496 						idThread *thread = new idThread( func );
3497 						thread->DelayedStart( 0 );
3498 					}
3499 				}
3500 				continue;
3501 			}
3502 
3503 			if ( token.Icmp("play") == 0 ) {
3504 				if ( src.ReadToken( &token2 ) ) {
3505 					const idSoundShader *shader = declManager->FindSound(token2);
3506 					entityGui->StartSoundShader( shader, SND_CHANNEL_ANY, 0, false, NULL );
3507 				}
3508 				continue;
3509 			}
3510 
3511 			if ( token.Icmp( "setkeyval" ) == 0 ) {
3512 				if ( src.ReadToken( &token2 ) && src.ReadToken(&token3) && src.ReadToken( &token4 ) ) {
3513 					idEntity *ent = gameLocal.FindEntity( token2 );
3514 					if ( ent ) {
3515 						ent->spawnArgs.Set( token3, token4 );
3516 						ent->UpdateChangeableSpawnArgs( NULL );
3517 						ent->UpdateVisuals();
3518 					}
3519 				}
3520 				continue;
3521 			}
3522 
3523 			if ( token.Icmp( "setshaderparm" ) == 0 ) {
3524 				if ( src.ReadToken( &token2 ) && src.ReadToken(&token3) ) {
3525 					entityGui->SetShaderParm( atoi( token2 ), atof( token3 ) );
3526 					entityGui->UpdateVisuals();
3527 				}
3528 				continue;
3529 			}
3530 
3531 			if ( token.Icmp("close") == 0 ) {
3532 				ret = true;
3533 				continue;
3534 			}
3535 
3536 			if ( !token.Icmp( "turkeyscore" ) ) {
3537 				if ( src.ReadToken( &token2 ) && entityGui->renderEntity.gui[0] ) {
3538 					int score = entityGui->renderEntity.gui[0]->State().GetInt( "score" );
3539 					score += atoi( token2 );
3540 					entityGui->renderEntity.gui[0]->SetStateInt( "score", score );
3541 					if ( gameLocal.GetLocalPlayer() && score >= 25000 && !gameLocal.GetLocalPlayer()->inventory.turkeyScore ) {
3542 						gameLocal.GetLocalPlayer()->GiveEmail( "highScore" );
3543 						gameLocal.GetLocalPlayer()->inventory.turkeyScore = true;
3544 					}
3545 				}
3546 				continue;
3547 			}
3548 
3549 #ifdef _D3XP
3550 
3551 			if ( !token.Icmp( "martianbuddycomplete" ) ) {
3552 				gameLocal.GetLocalPlayer()->GiveEmail( "MartianBuddyGameComplete" );
3553 				continue;
3554 			}
3555 
3556 #endif
3557 
3558 
3559 			// handy for debugging GUI stuff
3560 			if ( !token.Icmp( "print" ) ) {
3561 				idStr msg;
3562 				while ( src.ReadToken( &token2 ) ) {
3563 					if ( token2 == ";" ) {
3564 						src.UnreadToken( &token2 );
3565 						break;
3566 					}
3567 					msg += token2.c_str();
3568 				}
3569 				common->Printf( "ent gui 0x%x '%s': %s\n", entityNumber, name.c_str(), msg.c_str() );
3570 				continue;
3571 			}
3572 
3573 			// if we get to this point we don't know how to handle it
3574 			src.UnreadToken(&token);
3575 			if ( !HandleSingleGuiCommand( entityGui, &src ) ) {
3576 				// not handled there see if entity or any of its targets can handle it
3577 				// this will only work for one target atm
3578 				if ( entityGui->HandleSingleGuiCommand( entityGui, &src ) ) {
3579 					continue;
3580 				}
3581 
3582 				int c = entityGui->targets.Num();
3583 				int i;
3584 				for ( i = 0; i < c; i++) {
3585 					targetEnt = entityGui->targets[ i ].GetEntity();
3586 					if ( targetEnt && targetEnt->HandleSingleGuiCommand( entityGui, &src ) ) {
3587 						break;
3588 					}
3589 				}
3590 
3591 				if ( i == c ) {
3592 					// not handled
3593 					common->DPrintf( "idEntity::HandleGuiCommands: '%s' not handled\n", token.c_str() );
3594 					src.ReadToken( &token );
3595 				}
3596 			}
3597 
3598 		}
3599 	}
3600 	return ret;
3601 }
3602 
3603 /*
3604 ================
3605 idEntity::HandleSingleGuiCommand
3606 ================
3607 */
HandleSingleGuiCommand(idEntity * entityGui,idLexer * src)3608 bool idEntity::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
3609 	return false;
3610 }
3611 
3612 /***********************************************************************
3613 
3614   Targets
3615 
3616 ***********************************************************************/
3617 
3618 /*
3619 ===============
3620 idEntity::FindTargets
3621 
3622 We have to wait until all entities are spawned
3623 Used to build lists of targets after the entity is spawned.  Since not all entities
3624 have been spawned when the entity is created at map load time, we have to wait
3625 ===============
3626 */
FindTargets(void)3627 void idEntity::FindTargets( void ) {
3628 	int			i;
3629 
3630 	// targets can be a list of multiple names
3631 	gameLocal.GetTargets( spawnArgs, targets, "target" );
3632 
3633 	// ensure that we don't target ourselves since that could cause an infinite loop when activating entities
3634 	for( i = 0; i < targets.Num(); i++ ) {
3635 		if ( targets[ i ].GetEntity() == this ) {
3636 			gameLocal.Error( "Entity '%s' is targeting itself", name.c_str() );
3637 		}
3638 	}
3639 }
3640 
3641 /*
3642 ================
3643 idEntity::RemoveNullTargets
3644 ================
3645 */
RemoveNullTargets(void)3646 void idEntity::RemoveNullTargets( void ) {
3647 	int i;
3648 
3649 	for( i = targets.Num() - 1; i >= 0; i-- ) {
3650 		if ( !targets[ i ].GetEntity() ) {
3651 			targets.RemoveIndex( i );
3652 		}
3653 	}
3654 }
3655 
3656 /*
3657 ==============================
3658 idEntity::ActivateTargets
3659 
3660 "activator" should be set to the entity that initiated the firing.
3661 ==============================
3662 */
ActivateTargets(idEntity * activator) const3663 void idEntity::ActivateTargets( idEntity *activator ) const {
3664 	idEntity	*ent;
3665 	int			i, j;
3666 
3667 	for( i = 0; i < targets.Num(); i++ ) {
3668 		ent = targets[ i ].GetEntity();
3669 		if ( !ent ) {
3670 			continue;
3671 		}
3672 		if ( ent->RespondsTo( EV_Activate ) || ent->HasSignal( SIG_TRIGGER ) ) {
3673 			ent->Signal( SIG_TRIGGER );
3674 			ent->ProcessEvent( &EV_Activate, activator );
3675 		}
3676 		for ( j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
3677 			if ( ent->renderEntity.gui[ j ] ) {
3678 				ent->renderEntity.gui[ j ]->Trigger( gameLocal.time );
3679 			}
3680 		}
3681 	}
3682 }
3683 
3684 /***********************************************************************
3685 
3686   Misc.
3687 
3688 ***********************************************************************/
3689 
3690 /*
3691 ================
3692 idEntity::Teleport
3693 ================
3694 */
Teleport(const idVec3 & origin,const idAngles & angles,idEntity * destination)3695 void idEntity::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
3696 	GetPhysics()->SetOrigin( origin );
3697 	GetPhysics()->SetAxis( angles.ToMat3() );
3698 
3699 	UpdateVisuals();
3700 }
3701 
3702 /*
3703 ============
3704 idEntity::TouchTriggers
3705 
3706   Activate all trigger entities touched at the current position.
3707 ============
3708 */
TouchTriggers(void) const3709 bool idEntity::TouchTriggers( void ) const {
3710 	int				i, numClipModels, numEntities;
3711 	idClipModel *	cm;
3712 	idClipModel *	clipModels[ MAX_GENTITIES ];
3713 	idEntity *		ent;
3714 	trace_t			trace;
3715 
3716 	memset( &trace, 0, sizeof( trace ) );
3717 	trace.endpos = GetPhysics()->GetOrigin();
3718 	trace.endAxis = GetPhysics()->GetAxis();
3719 
3720 	numClipModels = gameLocal.clip.ClipModelsTouchingBounds( GetPhysics()->GetAbsBounds(), CONTENTS_TRIGGER, clipModels, MAX_GENTITIES );
3721 	numEntities = 0;
3722 
3723 	for ( i = 0; i < numClipModels; i++ ) {
3724 		cm = clipModels[ i ];
3725 
3726 		// don't touch it if we're the owner
3727 		if ( cm->GetOwner() == this ) {
3728 			continue;
3729 		}
3730 
3731 		ent = cm->GetEntity();
3732 
3733 		if ( !ent->RespondsTo( EV_Touch ) && !ent->HasSignal( SIG_TOUCH ) ) {
3734 			continue;
3735 		}
3736 
3737 		if ( !GetPhysics()->ClipContents( cm ) ) {
3738 			continue;
3739 		}
3740 
3741 #ifdef _D3XP
3742 		SetTimeState ts( ent->timeGroup );
3743 #endif
3744 
3745 		numEntities++;
3746 
3747 		trace.c.contents = cm->GetContents();
3748 		trace.c.entityNum = cm->GetEntity()->entityNumber;
3749 		trace.c.id = cm->GetId();
3750 
3751 		ent->Signal( SIG_TOUCH );
3752 		ent->ProcessEvent( &EV_Touch, this, &trace );
3753 
3754 		if ( !gameLocal.entities[ entityNumber ] ) {
3755 			gameLocal.Printf( "entity was removed while touching triggers\n" );
3756 			return true;
3757 		}
3758 	}
3759 
3760 	return ( numEntities != 0 );
3761 }
3762 
3763 /*
3764 ================
3765 idEntity::GetSpline
3766 ================
3767 */
GetSpline(void) const3768 idCurve_Spline<idVec3> *idEntity::GetSpline( void ) const {
3769 	int i, numPoints, t;
3770 	const idKeyValue *kv;
3771 	idLexer lex;
3772 	idVec3 v;
3773 	idCurve_Spline<idVec3> *spline;
3774 	const char *curveTag = "curve_";
3775 
3776 	kv = spawnArgs.MatchPrefix( curveTag );
3777 	if ( !kv ) {
3778 		return NULL;
3779 	}
3780 
3781 	idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( curveTag ) );
3782 	if ( str.Icmp( "CatmullRomSpline" ) == 0 ) {
3783 		spline = new idCurve_CatmullRomSpline<idVec3>();
3784 	} else if ( str.Icmp( "nubs" ) == 0 ) {
3785 		spline = new idCurve_NonUniformBSpline<idVec3>();
3786 	} else if ( str.Icmp( "nurbs" ) == 0 ) {
3787 		spline = new idCurve_NURBS<idVec3>();
3788 	} else {
3789 		spline = new idCurve_BSpline<idVec3>();
3790 	}
3791 
3792 	spline->SetBoundaryType( idCurve_Spline<idVec3>::BT_CLAMPED );
3793 
3794 	lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), curveTag );
3795 	numPoints = lex.ParseInt();
3796 	lex.ExpectTokenString( "(" );
3797 	for ( t = i = 0; i < numPoints; i++, t += 100 ) {
3798 		v.x = lex.ParseFloat();
3799 		v.y = lex.ParseFloat();
3800 		v.z = lex.ParseFloat();
3801 		spline->AddValue( t, v );
3802 	}
3803 	lex.ExpectTokenString( ")" );
3804 
3805 	return spline;
3806 }
3807 
3808 /*
3809 ===============
3810 idEntity::ShowEditingDialog
3811 ===============
3812 */
ShowEditingDialog(void)3813 void idEntity::ShowEditingDialog( void ) {
3814 }
3815 
3816 /***********************************************************************
3817 
3818    Events
3819 
3820 ***********************************************************************/
3821 
3822 /*
3823 ================
3824 idEntity::Event_GetName
3825 ================
3826 */
Event_GetName(void)3827 void idEntity::Event_GetName( void ) {
3828 	idThread::ReturnString( name.c_str() );
3829 }
3830 
3831 /*
3832 ================
3833 idEntity::Event_SetName
3834 ================
3835 */
Event_SetName(const char * newname)3836 void idEntity::Event_SetName( const char *newname ) {
3837 	SetName( newname );
3838 }
3839 
3840 /*
3841 ===============
3842 idEntity::Event_FindTargets
3843 ===============
3844 */
Event_FindTargets(void)3845 void idEntity::Event_FindTargets( void ) {
3846 	FindTargets();
3847 }
3848 
3849 /*
3850 ============
3851 idEntity::Event_ActivateTargets
3852 
3853 Activates any entities targeted by this entity.  Mainly used as an
3854 event to delay activating targets.
3855 ============
3856 */
Event_ActivateTargets(idEntity * activator)3857 void idEntity::Event_ActivateTargets( idEntity *activator ) {
3858 	ActivateTargets( activator );
3859 }
3860 
3861 /*
3862 ================
3863 idEntity::Event_NumTargets
3864 ================
3865 */
Event_NumTargets(void)3866 void idEntity::Event_NumTargets( void ) {
3867 	idThread::ReturnFloat( targets.Num() );
3868 }
3869 
3870 /*
3871 ================
3872 idEntity::Event_GetTarget
3873 ================
3874 */
Event_GetTarget(float index)3875 void idEntity::Event_GetTarget( float index ) {
3876 	int i;
3877 
3878 	i = ( int )index;
3879 	if ( ( i < 0 ) || i >= targets.Num() ) {
3880 		idThread::ReturnEntity( NULL );
3881 	} else {
3882 		idThread::ReturnEntity( targets[ i ].GetEntity() );
3883 	}
3884 }
3885 
3886 /*
3887 ================
3888 idEntity::Event_RandomTarget
3889 ================
3890 */
Event_RandomTarget(const char * ignore)3891 void idEntity::Event_RandomTarget( const char *ignore ) {
3892 	int			num;
3893 	idEntity	*ent;
3894 	int			i;
3895 	int			ignoreNum;
3896 
3897 	RemoveNullTargets();
3898 	if ( !targets.Num() ) {
3899 		idThread::ReturnEntity( NULL );
3900 		return;
3901 	}
3902 
3903 	ignoreNum = -1;
3904 	if ( ignore && ( ignore[ 0 ] != 0 ) && ( targets.Num() > 1 ) ) {
3905 		for( i = 0; i < targets.Num(); i++ ) {
3906 			ent = targets[ i ].GetEntity();
3907 			if ( ent && ( ent->name == ignore ) ) {
3908 				ignoreNum = i;
3909 				break;
3910 			}
3911 		}
3912 	}
3913 
3914 	if ( ignoreNum >= 0 ) {
3915 		num = gameLocal.random.RandomInt( targets.Num() - 1 );
3916 		if ( num >= ignoreNum ) {
3917 			num++;
3918 		}
3919 	} else {
3920 		num = gameLocal.random.RandomInt( targets.Num() );
3921 	}
3922 
3923 	ent = targets[ num ].GetEntity();
3924 	idThread::ReturnEntity( ent );
3925 }
3926 
3927 /*
3928 ================
3929 idEntity::Event_BindToJoint
3930 ================
3931 */
Event_BindToJoint(idEntity * master,const char * jointname,float orientated)3932 void idEntity::Event_BindToJoint( idEntity *master, const char *jointname, float orientated ) {
3933 	BindToJoint( master, jointname, ( orientated != 0.0f ) );
3934 }
3935 
3936 /*
3937 ================
3938 idEntity::Event_RemoveBinds
3939 ================
3940 */
Event_RemoveBinds(void)3941 void idEntity::Event_RemoveBinds( void ) {
3942 	RemoveBinds();
3943 }
3944 
3945 /*
3946 ================
3947 idEntity::Event_Bind
3948 ================
3949 */
Event_Bind(idEntity * master)3950 void idEntity::Event_Bind( idEntity *master ) {
3951 	Bind( master, true );
3952 }
3953 
3954 /*
3955 ================
3956 idEntity::Event_BindPosition
3957 ================
3958 */
Event_BindPosition(idEntity * master)3959 void idEntity::Event_BindPosition( idEntity *master ) {
3960 	Bind( master, false );
3961 }
3962 
3963 /*
3964 ================
3965 idEntity::Event_Unbind
3966 ================
3967 */
Event_Unbind(void)3968 void idEntity::Event_Unbind( void ) {
3969 	Unbind();
3970 }
3971 
3972 /*
3973 ================
3974 idEntity::Event_SpawnBind
3975 ================
3976 */
Event_SpawnBind(void)3977 void idEntity::Event_SpawnBind( void ) {
3978 	idEntity		*parent;
3979 	const char		*bind, *joint, *bindanim;
3980 	jointHandle_t	bindJoint;
3981 	bool			bindOrientated;
3982 	int				id;
3983 	const idAnim	*anim;
3984 	int				animNum;
3985 	idAnimator		*parentAnimator;
3986 
3987 	if ( spawnArgs.GetString( "bind", "", &bind ) ) {
3988 		if ( idStr::Icmp( bind, "worldspawn" ) == 0 ) {
3989 			//FIXME: Completely unneccessary since the worldspawn is called "world"
3990 			parent = gameLocal.world;
3991 		} else {
3992 			parent = gameLocal.FindEntity( bind );
3993 		}
3994 		bindOrientated = spawnArgs.GetBool( "bindOrientated", "1" );
3995 		if ( parent ) {
3996 			// bind to a joint of the skeletal model of the parent
3997 			if ( spawnArgs.GetString( "bindToJoint", "", &joint ) && *joint ) {
3998 				parentAnimator = parent->GetAnimator();
3999 				if ( !parentAnimator ) {
4000 					gameLocal.Error( "Cannot bind to joint '%s' on '%s'.  Entity does not support skeletal models.", joint, name.c_str() );
4001 				}
4002 				bindJoint = parentAnimator->GetJointHandle( joint );
4003 				if ( bindJoint == INVALID_JOINT ) {
4004 					gameLocal.Error( "Joint '%s' not found for bind on '%s'", joint, name.c_str() );
4005 				}
4006 
4007 				// bind it relative to a specific anim
4008 				if ( ( parent->spawnArgs.GetString( "bindanim", "", &bindanim ) || parent->spawnArgs.GetString( "anim", "", &bindanim ) ) && *bindanim ) {
4009 					animNum = parentAnimator->GetAnim( bindanim );
4010 					if ( !animNum ) {
4011 						gameLocal.Error( "Anim '%s' not found for bind on '%s'", bindanim, name.c_str() );
4012 					}
4013 					anim = parentAnimator->GetAnim( animNum );
4014 					if ( !anim ) {
4015 						gameLocal.Error( "Anim '%s' not found for bind on '%s'", bindanim, name.c_str() );
4016 					}
4017 
4018 					// make sure parent's render origin has been set
4019 					parent->UpdateModelTransform();
4020 
4021 					//FIXME: need a BindToJoint that accepts a joint position
4022 					parentAnimator->CreateFrame( gameLocal.time, true );
4023 					idJointMat *frame = parent->renderEntity.joints;
4024 					gameEdit->ANIM_CreateAnimFrame( parentAnimator->ModelHandle(), anim->MD5Anim( 0 ), parent->renderEntity.numJoints, frame, 0, parentAnimator->ModelDef()->GetVisualOffset(), parentAnimator->RemoveOrigin() );
4025 					BindToJoint( parent, joint, bindOrientated );
4026 					parentAnimator->ForceUpdate();
4027 				} else {
4028 					BindToJoint( parent, joint, bindOrientated );
4029 				}
4030 			}
4031 			// bind to a body of the physics object of the parent
4032 			else if ( spawnArgs.GetInt( "bindToBody", "0", id ) ) {
4033 				BindToBody( parent, id, bindOrientated );
4034 			}
4035 			// bind to the parent
4036 			else {
4037 				Bind( parent, bindOrientated );
4038 			}
4039 		}
4040 	}
4041 }
4042 
4043 /*
4044 ================
4045 idEntity::Event_SetOwner
4046 ================
4047 */
Event_SetOwner(idEntity * owner)4048 void idEntity::Event_SetOwner( idEntity *owner ) {
4049 	int i;
4050 
4051 	for ( i = 0; i < GetPhysics()->GetNumClipModels(); i++ ) {
4052 		GetPhysics()->GetClipModel( i )->SetOwner( owner );
4053 	}
4054 }
4055 
4056 /*
4057 ================
4058 idEntity::Event_SetModel
4059 ================
4060 */
Event_SetModel(const char * modelname)4061 void idEntity::Event_SetModel( const char *modelname ) {
4062 	SetModel( modelname );
4063 }
4064 
4065 /*
4066 ================
4067 idEntity::Event_SetSkin
4068 ================
4069 */
Event_SetSkin(const char * skinname)4070 void idEntity::Event_SetSkin( const char *skinname ) {
4071 	renderEntity.customSkin = declManager->FindSkin( skinname );
4072 	UpdateVisuals();
4073 }
4074 
4075 /*
4076 ================
4077 idEntity::Event_GetShaderParm
4078 ================
4079 */
Event_GetShaderParm(int parmnum)4080 void idEntity::Event_GetShaderParm( int parmnum ) {
4081 	if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
4082 		gameLocal.Error( "shader parm index (%d) out of range", parmnum );
4083 	}
4084 
4085 	idThread::ReturnFloat( renderEntity.shaderParms[ parmnum ] );
4086 }
4087 
4088 /*
4089 ================
4090 idEntity::Event_SetShaderParm
4091 ================
4092 */
Event_SetShaderParm(int parmnum,float value)4093 void idEntity::Event_SetShaderParm( int parmnum, float value ) {
4094 	SetShaderParm( parmnum, value );
4095 }
4096 
4097 /*
4098 ================
4099 idEntity::Event_SetShaderParms
4100 ================
4101 */
Event_SetShaderParms(float parm0,float parm1,float parm2,float parm3)4102 void idEntity::Event_SetShaderParms( float parm0, float parm1, float parm2, float parm3 ) {
4103 	renderEntity.shaderParms[ SHADERPARM_RED ]		= parm0;
4104 	renderEntity.shaderParms[ SHADERPARM_GREEN ]	= parm1;
4105 	renderEntity.shaderParms[ SHADERPARM_BLUE ]		= parm2;
4106 	renderEntity.shaderParms[ SHADERPARM_ALPHA ]	= parm3;
4107 	UpdateVisuals();
4108 }
4109 
4110 
4111 /*
4112 ================
4113 idEntity::Event_SetColor
4114 ================
4115 */
Event_SetColor(float red,float green,float blue)4116 void idEntity::Event_SetColor( float red, float green, float blue ) {
4117 	SetColor( red, green, blue );
4118 }
4119 
4120 /*
4121 ================
4122 idEntity::Event_GetColor
4123 ================
4124 */
Event_GetColor(void)4125 void idEntity::Event_GetColor( void ) {
4126 	idVec3 out;
4127 
4128 	GetColor( out );
4129 	idThread::ReturnVector( out );
4130 }
4131 
4132 /*
4133 ================
4134 idEntity::Event_IsHidden
4135 ================
4136 */
Event_IsHidden(void)4137 void idEntity::Event_IsHidden( void ) {
4138 	idThread::ReturnInt( fl.hidden );
4139 }
4140 
4141 /*
4142 ================
4143 idEntity::Event_Hide
4144 ================
4145 */
Event_Hide(void)4146 void idEntity::Event_Hide( void ) {
4147 	Hide();
4148 }
4149 
4150 /*
4151 ================
4152 idEntity::Event_Show
4153 ================
4154 */
Event_Show(void)4155 void idEntity::Event_Show( void ) {
4156 	Show();
4157 }
4158 
4159 /*
4160 ================
4161 idEntity::Event_CacheSoundShader
4162 ================
4163 */
Event_CacheSoundShader(const char * soundName)4164 void idEntity::Event_CacheSoundShader( const char *soundName ) {
4165 	declManager->FindSound( soundName );
4166 }
4167 
4168 /*
4169 ================
4170 idEntity::Event_StartSoundShader
4171 ================
4172 */
Event_StartSoundShader(const char * soundName,int channel)4173 void idEntity::Event_StartSoundShader( const char *soundName, int channel ) {
4174 	int length;
4175 
4176 	StartSoundShader( declManager->FindSound( soundName ), (s_channelType)channel, 0, false, &length );
4177 	idThread::ReturnFloat( MS2SEC( length ) );
4178 }
4179 
4180 /*
4181 ================
4182 idEntity::Event_StopSound
4183 ================
4184 */
Event_StopSound(int channel,int netSync)4185 void idEntity::Event_StopSound( int channel, int netSync ) {
4186 	StopSound( channel, ( netSync != 0 ) );
4187 }
4188 
4189 /*
4190 ================
4191 idEntity::Event_StartSound
4192 ================
4193 */
Event_StartSound(const char * soundName,int channel,int netSync)4194 void idEntity::Event_StartSound( const char *soundName, int channel, int netSync ) {
4195 	int time;
4196 
4197 	StartSound( soundName, ( s_channelType )channel, 0, ( netSync != 0 ), &time );
4198 	idThread::ReturnFloat( MS2SEC( time ) );
4199 }
4200 
4201 /*
4202 ================
4203 idEntity::Event_FadeSound
4204 ================
4205 */
Event_FadeSound(int channel,float to,float over)4206 void idEntity::Event_FadeSound( int channel, float to, float over ) {
4207 	if ( refSound.referenceSound ) {
4208 		refSound.referenceSound->FadeSound( channel, to, over );
4209 	}
4210 }
4211 
4212 /*
4213 ================
4214 idEntity::Event_GetWorldOrigin
4215 ================
4216 */
Event_GetWorldOrigin(void)4217 void idEntity::Event_GetWorldOrigin( void ) {
4218 	idThread::ReturnVector( GetPhysics()->GetOrigin() );
4219 }
4220 
4221 /*
4222 ================
4223 idEntity::Event_SetWorldOrigin
4224 ================
4225 */
Event_SetWorldOrigin(idVec3 const & org)4226 void idEntity::Event_SetWorldOrigin( idVec3 const &org ) {
4227 	idVec3 neworg = GetLocalCoordinates( org );
4228 	SetOrigin( neworg );
4229 }
4230 
4231 /*
4232 ================
4233 idEntity::Event_SetOrigin
4234 ================
4235 */
Event_SetOrigin(idVec3 const & org)4236 void idEntity::Event_SetOrigin( idVec3 const &org ) {
4237 	SetOrigin( org );
4238 }
4239 
4240 /*
4241 ================
4242 idEntity::Event_GetOrigin
4243 ================
4244 */
Event_GetOrigin(void)4245 void idEntity::Event_GetOrigin( void ) {
4246 	idThread::ReturnVector( GetLocalCoordinates( GetPhysics()->GetOrigin() ) );
4247 }
4248 
4249 /*
4250 ================
4251 idEntity::Event_SetAngles
4252 ================
4253 */
Event_SetAngles(idAngles const & ang)4254 void idEntity::Event_SetAngles( idAngles const &ang ) {
4255 	SetAngles( ang );
4256 }
4257 
4258 /*
4259 ================
4260 idEntity::Event_GetAngles
4261 ================
4262 */
Event_GetAngles(void)4263 void idEntity::Event_GetAngles( void ) {
4264 	idAngles ang = GetPhysics()->GetAxis().ToAngles();
4265 	idThread::ReturnVector( idVec3( ang[0], ang[1], ang[2] ) );
4266 }
4267 
4268 /*
4269 ================
4270 idEntity::Event_SetLinearVelocity
4271 ================
4272 */
Event_SetLinearVelocity(const idVec3 & velocity)4273 void idEntity::Event_SetLinearVelocity( const idVec3 &velocity ) {
4274 	GetPhysics()->SetLinearVelocity( velocity );
4275 }
4276 
4277 /*
4278 ================
4279 idEntity::Event_GetLinearVelocity
4280 ================
4281 */
Event_GetLinearVelocity(void)4282 void idEntity::Event_GetLinearVelocity( void ) {
4283 	idThread::ReturnVector( GetPhysics()->GetLinearVelocity() );
4284 }
4285 
4286 /*
4287 ================
4288 idEntity::Event_SetAngularVelocity
4289 ================
4290 */
Event_SetAngularVelocity(const idVec3 & velocity)4291 void idEntity::Event_SetAngularVelocity( const idVec3 &velocity ) {
4292 	GetPhysics()->SetAngularVelocity( velocity );
4293 }
4294 
4295 /*
4296 ================
4297 idEntity::Event_GetAngularVelocity
4298 ================
4299 */
Event_GetAngularVelocity(void)4300 void idEntity::Event_GetAngularVelocity( void ) {
4301 	idThread::ReturnVector( GetPhysics()->GetAngularVelocity() );
4302 }
4303 
4304 /*
4305 ================
4306 idEntity::Event_SetSize
4307 ================
4308 */
Event_SetSize(idVec3 const & mins,idVec3 const & maxs)4309 void idEntity::Event_SetSize( idVec3 const &mins, idVec3 const &maxs ) {
4310 	GetPhysics()->SetClipBox( idBounds( mins, maxs ), 1.0f );
4311 }
4312 
4313 /*
4314 ================
4315 idEntity::Event_GetSize
4316 ================
4317 */
Event_GetSize(void)4318 void idEntity::Event_GetSize( void ) {
4319 	idBounds bounds;
4320 
4321 	bounds = GetPhysics()->GetBounds();
4322 	idThread::ReturnVector( bounds[1] - bounds[0] );
4323 }
4324 
4325 /*
4326 ================
4327 idEntity::Event_GetMins
4328 ================
4329 */
Event_GetMins(void)4330 void idEntity::Event_GetMins( void ) {
4331 	idThread::ReturnVector( GetPhysics()->GetBounds()[0] );
4332 }
4333 
4334 /*
4335 ================
4336 idEntity::Event_GetMaxs
4337 ================
4338 */
Event_GetMaxs(void)4339 void idEntity::Event_GetMaxs( void ) {
4340 	idThread::ReturnVector( GetPhysics()->GetBounds()[1] );
4341 }
4342 
4343 /*
4344 ================
4345 idEntity::Event_Touches
4346 ================
4347 */
Event_Touches(idEntity * ent)4348 void idEntity::Event_Touches( idEntity *ent ) {
4349 	if ( !ent ) {
4350 		idThread::ReturnInt( false );
4351 		return;
4352 	}
4353 
4354 	const idBounds &myBounds = GetPhysics()->GetAbsBounds();
4355 	const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
4356 
4357 	idThread::ReturnInt( myBounds.IntersectsBounds( entBounds ) );
4358 }
4359 
4360 /*
4361 ================
4362 idEntity::Event_SetGuiParm
4363 ================
4364 */
Event_SetGuiParm(const char * key,const char * val)4365 void idEntity::Event_SetGuiParm( const char *key, const char *val ) {
4366 	for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
4367 		if ( renderEntity.gui[ i ] ) {
4368 			if ( idStr::Icmpn( key, "gui_", 4 ) == 0 ) {
4369 				spawnArgs.Set( key, val );
4370 			}
4371 			renderEntity.gui[ i ]->SetStateString( key, val );
4372 			renderEntity.gui[ i ]->StateChanged( gameLocal.time );
4373 		}
4374 	}
4375 }
4376 
4377 /*
4378 ================
4379 idEntity::Event_SetGuiParm
4380 ================
4381 */
Event_SetGuiFloat(const char * key,float f)4382 void idEntity::Event_SetGuiFloat( const char *key, float f ) {
4383 	for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
4384 		if ( renderEntity.gui[ i ] ) {
4385 			renderEntity.gui[ i ]->SetStateString( key, va( "%f", f ) );
4386 			renderEntity.gui[ i ]->StateChanged( gameLocal.time );
4387 		}
4388 	}
4389 }
4390 
4391 /*
4392 ================
4393 idEntity::Event_GetNextKey
4394 ================
4395 */
Event_GetNextKey(const char * prefix,const char * lastMatch)4396 void idEntity::Event_GetNextKey( const char *prefix, const char *lastMatch ) {
4397 	const idKeyValue *kv;
4398 	const idKeyValue *previous;
4399 
4400 	if ( *lastMatch ) {
4401 		previous = spawnArgs.FindKey( lastMatch );
4402 	} else {
4403 		previous = NULL;
4404 	}
4405 
4406 	kv = spawnArgs.MatchPrefix( prefix, previous );
4407 	if ( !kv ) {
4408 		idThread::ReturnString( "" );
4409 	} else {
4410 		idThread::ReturnString( kv->GetKey() );
4411 	}
4412 }
4413 
4414 /*
4415 ================
4416 idEntity::Event_SetKey
4417 ================
4418 */
Event_SetKey(const char * key,const char * value)4419 void idEntity::Event_SetKey( const char *key, const char *value ) {
4420 	spawnArgs.Set( key, value );
4421 #ifdef _D3XP
4422 	UpdateChangeableSpawnArgs( NULL );
4423 #endif
4424 }
4425 
4426 /*
4427 ================
4428 idEntity::Event_GetKey
4429 ================
4430 */
Event_GetKey(const char * key)4431 void idEntity::Event_GetKey( const char *key ) {
4432 	const char *value;
4433 
4434 	spawnArgs.GetString( key, "", &value );
4435 	idThread::ReturnString( value );
4436 }
4437 
4438 /*
4439 ================
4440 idEntity::Event_GetIntKey
4441 ================
4442 */
Event_GetIntKey(const char * key)4443 void idEntity::Event_GetIntKey( const char *key ) {
4444 	int value;
4445 
4446 	spawnArgs.GetInt( key, "0", value );
4447 
4448 	// scripts only support floats
4449 	idThread::ReturnFloat( value );
4450 }
4451 
4452 /*
4453 ================
4454 idEntity::Event_GetFloatKey
4455 ================
4456 */
Event_GetFloatKey(const char * key)4457 void idEntity::Event_GetFloatKey( const char *key ) {
4458 	float value;
4459 
4460 	spawnArgs.GetFloat( key, "0", value );
4461 	idThread::ReturnFloat( value );
4462 }
4463 
4464 /*
4465 ================
4466 idEntity::Event_GetVectorKey
4467 ================
4468 */
Event_GetVectorKey(const char * key)4469 void idEntity::Event_GetVectorKey( const char *key ) {
4470 	idVec3 value;
4471 
4472 	spawnArgs.GetVector( key, "0 0 0", value );
4473 	idThread::ReturnVector( value );
4474 }
4475 
4476 /*
4477 ================
4478 idEntity::Event_GetEntityKey
4479 ================
4480 */
Event_GetEntityKey(const char * key)4481 void idEntity::Event_GetEntityKey( const char *key ) {
4482 	idEntity *ent;
4483 	const char *entname;
4484 
4485 	if ( !spawnArgs.GetString( key, NULL, &entname ) ) {
4486 		idThread::ReturnEntity( NULL );
4487 		return;
4488 	}
4489 
4490 	ent = gameLocal.FindEntity( entname );
4491 	if ( !ent ) {
4492 		gameLocal.Warning( "Couldn't find entity '%s' specified in '%s' key in entity '%s'", entname, key, name.c_str() );
4493 	}
4494 
4495 	idThread::ReturnEntity( ent );
4496 }
4497 
4498 /*
4499 ================
4500 idEntity::Event_RestorePosition
4501 ================
4502 */
Event_RestorePosition(void)4503 void idEntity::Event_RestorePosition( void ) {
4504 	idVec3		org;
4505 	idAngles	angles;
4506 	idMat3		axis;
4507 	idEntity *	part;
4508 
4509 	spawnArgs.GetVector( "origin", "0 0 0", org );
4510 
4511 	// get the rotation matrix in either full form, or single angle form
4512 	if ( spawnArgs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
4513 		angles = axis.ToAngles();
4514 	} else {
4515 		angles[ 0 ] = 0;
4516 		angles[ 1 ] = spawnArgs.GetFloat( "angle" );
4517 		angles[ 2 ] = 0;
4518 	}
4519 
4520 	Teleport( org, angles, NULL );
4521 
4522 	for ( part = teamChain; part != NULL; part = part->teamChain ) {
4523 		if ( part->bindMaster != this ) {
4524 			continue;
4525 		}
4526 		if ( part->GetPhysics()->IsType( idPhysics_Parametric::Type ) ) {
4527 			if ( static_cast<idPhysics_Parametric *>(part->GetPhysics())->IsPusher() ) {
4528 				gameLocal.Warning( "teleported '%s' which has the pushing mover '%s' bound to it\n", GetName(), part->GetName() );
4529 			}
4530 		} else if ( part->GetPhysics()->IsType( idPhysics_AF::Type ) ) {
4531 			gameLocal.Warning( "teleported '%s' which has the articulated figure '%s' bound to it\n", GetName(), part->GetName() );
4532 		}
4533 	}
4534 }
4535 
4536 /*
4537 ================
4538 idEntity::Event_UpdateCameraTarget
4539 ================
4540 */
Event_UpdateCameraTarget(void)4541 void idEntity::Event_UpdateCameraTarget( void ) {
4542 	const char *target;
4543 	const idKeyValue *kv;
4544 	idVec3 dir;
4545 
4546 	target = spawnArgs.GetString( "cameraTarget" );
4547 
4548 	cameraTarget = gameLocal.FindEntity( target );
4549 
4550 	if ( cameraTarget ) {
4551 		kv = cameraTarget->spawnArgs.MatchPrefix( "target", NULL );
4552 		while( kv ) {
4553 			idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
4554 			if ( ent && idStr::Icmp( ent->GetEntityDefName(), "target_null" ) == 0) {
4555 				dir = ent->GetPhysics()->GetOrigin() - cameraTarget->GetPhysics()->GetOrigin();
4556 				dir.Normalize();
4557 				cameraTarget->SetAxis( dir.ToMat3() );
4558 				SetAxis(dir.ToMat3());
4559 				break;
4560 			}
4561 			kv = cameraTarget->spawnArgs.MatchPrefix( "target", kv );
4562 		}
4563 	}
4564 	UpdateVisuals();
4565 }
4566 
4567 /*
4568 ================
4569 idEntity::Event_DistanceTo
4570 ================
4571 */
Event_DistanceTo(idEntity * ent)4572 void idEntity::Event_DistanceTo( idEntity *ent ) {
4573 	if ( !ent ) {
4574 		// just say it's really far away
4575 		idThread::ReturnFloat( MAX_WORLD_SIZE );
4576 	} else {
4577 		float dist = ( GetPhysics()->GetOrigin() - ent->GetPhysics()->GetOrigin() ).LengthFast();
4578 		idThread::ReturnFloat( dist );
4579 	}
4580 }
4581 
4582 /*
4583 ================
4584 idEntity::Event_DistanceToPoint
4585 ================
4586 */
Event_DistanceToPoint(const idVec3 & point)4587 void idEntity::Event_DistanceToPoint( const idVec3 &point ) {
4588 	float dist = ( GetPhysics()->GetOrigin() - point ).LengthFast();
4589 	idThread::ReturnFloat( dist );
4590 }
4591 
4592 /*
4593 ================
4594 idEntity::Event_StartFx
4595 ================
4596 */
Event_StartFx(const char * fx)4597 void idEntity::Event_StartFx( const char *fx ) {
4598 	idEntityFx::StartFx( fx, NULL, NULL, this, true );
4599 }
4600 
4601 /*
4602 ================
4603 idEntity::Event_WaitFrame
4604 ================
4605 */
Event_WaitFrame(void)4606 void idEntity::Event_WaitFrame( void ) {
4607 	idThread *thread;
4608 
4609 	thread = idThread::CurrentThread();
4610 	if ( thread ) {
4611 		thread->WaitFrame();
4612 	}
4613 }
4614 
4615 /*
4616 =====================
4617 idEntity::Event_Wait
4618 =====================
4619 */
Event_Wait(float time)4620 void idEntity::Event_Wait( float time ) {
4621 	idThread *thread = idThread::CurrentThread();
4622 
4623 	if ( !thread ) {
4624 		gameLocal.Error( "Event 'wait' called from outside thread" );
4625 	}
4626 
4627 	thread->WaitSec( time );
4628 }
4629 
4630 /*
4631 =====================
4632 idEntity::Event_HasFunction
4633 =====================
4634 */
Event_HasFunction(const char * name)4635 void idEntity::Event_HasFunction( const char *name ) {
4636 	const function_t *func;
4637 
4638 	func = scriptObject.GetFunction( name );
4639 	if ( func ) {
4640 		idThread::ReturnInt( true );
4641 	} else {
4642 		idThread::ReturnInt( false );
4643 	}
4644 }
4645 
4646 /*
4647 =====================
4648 idEntity::Event_CallFunction
4649 =====================
4650 */
Event_CallFunction(const char * funcname)4651 void idEntity::Event_CallFunction( const char *funcname ) {
4652 	const function_t *func;
4653 	idThread *thread;
4654 
4655 	thread = idThread::CurrentThread();
4656 	if ( !thread ) {
4657 		gameLocal.Error( "Event 'callFunction' called from outside thread" );
4658 	}
4659 
4660 	func = scriptObject.GetFunction( funcname );
4661 	if ( !func ) {
4662 		gameLocal.Error( "Unknown function '%s' in '%s'", funcname, scriptObject.GetTypeName() );
4663 	}
4664 
4665 	if ( func->type->NumParameters() != 1 ) {
4666 		gameLocal.Error( "Function '%s' has the wrong number of parameters for 'callFunction'", funcname );
4667 	}
4668 	if ( !scriptObject.GetTypeDef()->Inherits( func->type->GetParmType( 0 ) ) ) {
4669 		gameLocal.Error( "Function '%s' is the wrong type for 'callFunction'", funcname );
4670 	}
4671 
4672 	// function args will be invalid after this call
4673 	thread->CallFunction( this, func, false );
4674 }
4675 
4676 /*
4677 ================
4678 idEntity::Event_SetNeverDormant
4679 ================
4680 */
Event_SetNeverDormant(int enable)4681 void idEntity::Event_SetNeverDormant( int enable ) {
4682 	fl.neverDormant	= ( enable != 0 );
4683 	dormantStart = 0;
4684 }
4685 
4686 #ifdef _D3XP
4687 /*
4688 ================
4689 idEntity::Event_SetGui
4690 ================
4691 * BSM Nerve: Allows guis to be changed at runtime. Guis that are
4692 * loaded after the level loads should be precahced using PrecacheGui.
4693 */
Event_SetGui(int guiNum,const char * guiName)4694 void idEntity::Event_SetGui( int guiNum, const char *guiName) {
4695 	idUserInterface** gui = NULL;
4696 
4697 	if ( guiNum >= 1 && guiNum <= MAX_RENDERENTITY_GUI ) {
4698 		gui = &renderEntity.gui[ guiNum-1 ];
4699 	}
4700 
4701 	if( gui ) {
4702 		*gui = uiManager->FindGui( guiName, true, false );
4703 		UpdateGuiParms( *gui, &spawnArgs );
4704 		UpdateChangeableSpawnArgs( NULL );
4705 		gameRenderWorld->UpdateEntityDef(modelDefHandle, &renderEntity);
4706 
4707 	} else {
4708 		gameLocal.Error( "Entity '%s' doesn't have a GUI %d", name.c_str(), guiNum );
4709 	}
4710 
4711 }
4712 
4713 /*
4714 ================
4715 idEntity::Event_PrecacheGui
4716 ================
4717 * BSM Nerve: Forces the engine to initialize a gui even if it is not specified as used in a level.
4718 * This is useful for preventing load hitches when switching guis during the game using "setGui"
4719 */
Event_PrecacheGui(const char * guiName)4720 void idEntity::Event_PrecacheGui( const char *guiName ) {
4721 	uiManager->FindGui( guiName, true, true );
4722 }
4723 
Event_GetGuiParm(int guiNum,const char * key)4724 void idEntity::Event_GetGuiParm(int guiNum, const char *key) {
4725 	if(renderEntity.gui[guiNum-1]) {
4726 		idThread::ReturnString(renderEntity.gui[guiNum-1]->GetStateString(key));
4727 		return;
4728 	}
4729 	idThread::ReturnString("");
4730 }
4731 
Event_GetGuiParmFloat(int guiNum,const char * key)4732 void idEntity::Event_GetGuiParmFloat(int guiNum, const char *key) {
4733 	if(renderEntity.gui[guiNum-1]) {
4734 		idThread::ReturnFloat(renderEntity.gui[guiNum-1]->GetStateFloat(key));
4735 		return;
4736 	}
4737 	idThread::ReturnFloat(0.0f);
4738 }
4739 
Event_GuiNamedEvent(int guiNum,const char * event)4740 void idEntity::Event_GuiNamedEvent(int guiNum, const char *event) {
4741 	if(renderEntity.gui[guiNum-1]) {
4742 		renderEntity.gui[guiNum-1]->HandleNamedEvent(event);
4743 	}
4744 }
4745 
4746 #endif
4747 
4748 /***********************************************************************
4749 
4750    Network
4751 
4752 ***********************************************************************/
4753 
4754 /*
4755 ================
4756 idEntity::ClientPredictionThink
4757 ================
4758 */
ClientPredictionThink(void)4759 void idEntity::ClientPredictionThink( void ) {
4760 	RunPhysics();
4761 	Present();
4762 }
4763 
4764 /*
4765 ================
4766 idEntity::WriteBindToSnapshot
4767 ================
4768 */
WriteBindToSnapshot(idBitMsgDelta & msg) const4769 void idEntity::WriteBindToSnapshot( idBitMsgDelta &msg ) const {
4770 	int bindInfo;
4771 
4772 	if ( bindMaster ) {
4773 		bindInfo = bindMaster->entityNumber;
4774 		bindInfo |= ( fl.bindOrientated & 1 ) << GENTITYNUM_BITS;
4775 		if ( bindJoint != INVALID_JOINT ) {
4776 			bindInfo |= 1 << ( GENTITYNUM_BITS + 1 );
4777 			bindInfo |= bindJoint << ( 3 + GENTITYNUM_BITS );
4778 		} else if ( bindBody != -1 ) {
4779 			bindInfo |= 2 << ( GENTITYNUM_BITS + 1 );
4780 			bindInfo |= bindBody << ( 3 + GENTITYNUM_BITS );
4781 		}
4782 	} else {
4783 		bindInfo = ENTITYNUM_NONE;
4784 	}
4785 	msg.WriteBits( bindInfo, GENTITYNUM_BITS + 3 + 9 );
4786 }
4787 
4788 /*
4789 ================
4790 idEntity::ReadBindFromSnapshot
4791 ================
4792 */
ReadBindFromSnapshot(const idBitMsgDelta & msg)4793 void idEntity::ReadBindFromSnapshot( const idBitMsgDelta &msg ) {
4794 	int bindInfo, bindEntityNum, bindPos;
4795 	bool bindOrientated;
4796 	idEntity *master;
4797 
4798 	bindInfo = msg.ReadBits( GENTITYNUM_BITS + 3 + 9 );
4799 	bindEntityNum = bindInfo & ( ( 1 << GENTITYNUM_BITS ) - 1 );
4800 
4801 	if ( bindEntityNum != ENTITYNUM_NONE ) {
4802 		master = gameLocal.entities[ bindEntityNum ];
4803 
4804 		bindOrientated = ( bindInfo >> GENTITYNUM_BITS ) & 1;
4805 		bindPos = ( bindInfo >> ( GENTITYNUM_BITS + 3 ) );
4806 		switch( ( bindInfo >> ( GENTITYNUM_BITS + 1 ) ) & 3 ) {
4807 			case 1: {
4808 				BindToJoint( master, (jointHandle_t) bindPos, bindOrientated );
4809 				break;
4810 			}
4811 			case 2: {
4812 				BindToBody( master, bindPos, bindOrientated );
4813 				break;
4814 			}
4815 			default: {
4816 				Bind( master, bindOrientated );
4817 				break;
4818 			}
4819 		}
4820 	} else if ( bindMaster ) {
4821 		Unbind();
4822 	}
4823 }
4824 
4825 /*
4826 ================
4827 idEntity::WriteColorToSnapshot
4828 ================
4829 */
WriteColorToSnapshot(idBitMsgDelta & msg) const4830 void idEntity::WriteColorToSnapshot( idBitMsgDelta &msg ) const {
4831 	idVec4 color;
4832 
4833 	color[0] = renderEntity.shaderParms[ SHADERPARM_RED ];
4834 	color[1] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
4835 	color[2] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
4836 	color[3] = renderEntity.shaderParms[ SHADERPARM_ALPHA ];
4837 	msg.WriteInt( PackColor( color ) );
4838 }
4839 
4840 /*
4841 ================
4842 idEntity::ReadColorFromSnapshot
4843 ================
4844 */
ReadColorFromSnapshot(const idBitMsgDelta & msg)4845 void idEntity::ReadColorFromSnapshot( const idBitMsgDelta &msg ) {
4846 	idVec4 color;
4847 
4848 	UnpackColor( msg.ReadInt(), color );
4849 	renderEntity.shaderParms[ SHADERPARM_RED ] = color[0];
4850 	renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[1];
4851 	renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[2];
4852 	renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[3];
4853 }
4854 
4855 /*
4856 ================
4857 idEntity::WriteGUIToSnapshot
4858 ================
4859 */
WriteGUIToSnapshot(idBitMsgDelta & msg) const4860 void idEntity::WriteGUIToSnapshot( idBitMsgDelta &msg ) const {
4861 	// no need to loop over MAX_RENDERENTITY_GUI at this time
4862 	if ( renderEntity.gui[ 0 ] ) {
4863 		msg.WriteByte( renderEntity.gui[ 0 ]->State().GetInt( "networkState" ) );
4864 	} else {
4865 		msg.WriteByte( 0 );
4866 	}
4867 }
4868 
4869 /*
4870 ================
4871 idEntity::ReadGUIFromSnapshot
4872 ================
4873 */
ReadGUIFromSnapshot(const idBitMsgDelta & msg)4874 void idEntity::ReadGUIFromSnapshot( const idBitMsgDelta &msg ) {
4875 	int state;
4876 	idUserInterface *gui;
4877 	state = msg.ReadByte( );
4878 	gui = renderEntity.gui[ 0 ];
4879 	if ( gui && state != mpGUIState ) {
4880 		mpGUIState = state;
4881 		gui->SetStateInt( "networkState", state );
4882 		gui->HandleNamedEvent( "networkState" );
4883 	}
4884 }
4885 
4886 /*
4887 ================
4888 idEntity::WriteToSnapshot
4889 ================
4890 */
WriteToSnapshot(idBitMsgDelta & msg) const4891 void idEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
4892 }
4893 
4894 /*
4895 ================
4896 idEntity::ReadFromSnapshot
4897 ================
4898 */
ReadFromSnapshot(const idBitMsgDelta & msg)4899 void idEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
4900 }
4901 
4902 /*
4903 ================
4904 idEntity::ServerSendEvent
4905 
4906    Saved events are also sent to any client that connects late so all clients
4907    always receive the events nomatter what time they join the game.
4908 ================
4909 */
ServerSendEvent(int eventId,const idBitMsg * msg,bool saveEvent,int excludeClient) const4910 void idEntity::ServerSendEvent( int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient ) const {
4911 	idBitMsg	outMsg;
4912 	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
4913 
4914 	if ( !gameLocal.isServer ) {
4915 		return;
4916 	}
4917 
4918 	// prevent dupe events caused by frame re-runs
4919 	if ( !gameLocal.isNewFrame ) {
4920 		return;
4921 	}
4922 
4923 	outMsg.Init( msgBuf, sizeof( msgBuf ) );
4924 	outMsg.BeginWriting();
4925 	outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT );
4926 	outMsg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
4927 	outMsg.WriteByte( eventId );
4928 	outMsg.WriteInt( gameLocal.time );
4929 	if ( msg ) {
4930 		outMsg.WriteBits( msg->GetSize(), idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
4931 		outMsg.WriteData( msg->GetData(), msg->GetSize() );
4932 	} else {
4933 		outMsg.WriteBits( 0, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
4934 	}
4935 
4936 	if ( excludeClient != -1 ) {
4937 		networkSystem->ServerSendReliableMessageExcluding( excludeClient, outMsg );
4938 	} else {
4939 		networkSystem->ServerSendReliableMessage( -1, outMsg );
4940 	}
4941 
4942 	if ( saveEvent ) {
4943 		gameLocal.SaveEntityNetworkEvent( this, eventId, msg );
4944 	}
4945 }
4946 
4947 /*
4948 ================
4949 idEntity::ClientSendEvent
4950 ================
4951 */
ClientSendEvent(int eventId,const idBitMsg * msg) const4952 void idEntity::ClientSendEvent( int eventId, const idBitMsg *msg ) const {
4953 	idBitMsg	outMsg;
4954 	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
4955 
4956 	if ( !gameLocal.isClient ) {
4957 		return;
4958 	}
4959 
4960 	// prevent dupe events caused by frame re-runs
4961 	if ( !gameLocal.isNewFrame ) {
4962 		return;
4963 	}
4964 
4965 	outMsg.Init( msgBuf, sizeof( msgBuf ) );
4966 	outMsg.BeginWriting();
4967 	outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT );
4968 	outMsg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
4969 	outMsg.WriteByte( eventId );
4970 	outMsg.WriteInt( gameLocal.time );
4971 	if ( msg ) {
4972 		outMsg.WriteBits( msg->GetSize(), idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
4973 		outMsg.WriteData( msg->GetData(), msg->GetSize() );
4974 	} else {
4975 		outMsg.WriteBits( 0, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
4976 	}
4977 
4978 	networkSystem->ClientSendReliableMessage( outMsg );
4979 }
4980 
4981 /*
4982 ================
4983 idEntity::ServerReceiveEvent
4984 ================
4985 */
ServerReceiveEvent(int event,int time,const idBitMsg & msg)4986 bool idEntity::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
4987 	switch( event ) {
4988 		case 0: {
4989 		}
4990 		default: {
4991 			return false;
4992 		}
4993 	}
4994 }
4995 
4996 /*
4997 ================
4998 idEntity::ClientReceiveEvent
4999 ================
5000 */
ClientReceiveEvent(int event,int time,const idBitMsg & msg)5001 bool idEntity::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
5002 	int					index;
5003 	const idSoundShader	*shader;
5004 	s_channelType		channel;
5005 
5006 	switch( event ) {
5007 		case EVENT_STARTSOUNDSHADER: {
5008 			// the sound stuff would early out
5009 			assert( gameLocal.isNewFrame );
5010 			if ( time < gameLocal.realClientTime - 1000 ) {
5011 				// too old, skip it ( reliable messages don't need to be parsed in full )
5012 				common->DPrintf( "ent 0x%x: start sound shader too old (%d ms)\n", entityNumber, gameLocal.realClientTime - time );
5013 				return true;
5014 			}
5015 			index = gameLocal.ClientRemapDecl( DECL_SOUND, msg.ReadInt() );
5016 			if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
5017 				shader = declManager->SoundByIndex( index, false );
5018 				channel = (s_channelType)msg.ReadByte();
5019 				StartSoundShader( shader, channel, 0, false, NULL );
5020 			}
5021 			return true;
5022 		}
5023 		case EVENT_STOPSOUNDSHADER: {
5024 			// the sound stuff would early out
5025 			assert( gameLocal.isNewFrame );
5026 			channel = (s_channelType)msg.ReadByte();
5027 			StopSound( channel, false );
5028 			return true;
5029 		}
5030 		default:
5031 			break;
5032 	}
5033 
5034 	return false;
5035 }
5036 
5037 #ifdef _D3XP
5038 /*
5039 ================
5040 idEntity::DetermineTimeGroup
5041 ================
5042 */
DetermineTimeGroup(bool slowmo)5043 void idEntity::DetermineTimeGroup( bool slowmo ) {
5044 	if ( slowmo || gameLocal.isMultiplayer ) {
5045 		timeGroup = TIME_GROUP1;
5046 	}
5047 	else {
5048 		timeGroup = TIME_GROUP2;
5049 	}
5050 }
5051 
5052 /*
5053 ================
5054 idEntity::SetGrabbedState
5055 ================
5056 */
SetGrabbedState(bool grabbed)5057 void idEntity::SetGrabbedState( bool grabbed ) {
5058 	fl.grabbed = grabbed;
5059 }
5060 
5061 /*
5062 ================
5063 idEntity::IsGrabbed
5064 ================
5065 */
IsGrabbed()5066 bool idEntity::IsGrabbed() {
5067 	return fl.grabbed;
5068 }
5069 #endif
5070 
5071 /*
5072 ===============================================================================
5073 
5074   idAnimatedEntity
5075 
5076 ===============================================================================
5077 */
5078 
5079 const idEventDef EV_GetJointHandle( "getJointHandle", "s", 'd' );
5080 const idEventDef EV_ClearAllJoints( "clearAllJoints" );
5081 const idEventDef EV_ClearJoint( "clearJoint", "d" );
5082 const idEventDef EV_SetJointPos( "setJointPos", "ddv" );
5083 const idEventDef EV_SetJointAngle( "setJointAngle", "ddv" );
5084 const idEventDef EV_GetJointPos( "getJointPos", "d", 'v' );
5085 const idEventDef EV_GetJointAngle( "getJointAngle", "d", 'v' );
5086 
CLASS_DECLARATION(idEntity,idAnimatedEntity)5087 CLASS_DECLARATION( idEntity, idAnimatedEntity )
5088 	EVENT( EV_GetJointHandle,		idAnimatedEntity::Event_GetJointHandle )
5089 	EVENT( EV_ClearAllJoints,		idAnimatedEntity::Event_ClearAllJoints )
5090 	EVENT( EV_ClearJoint,			idAnimatedEntity::Event_ClearJoint )
5091 	EVENT( EV_SetJointPos,			idAnimatedEntity::Event_SetJointPos )
5092 	EVENT( EV_SetJointAngle,		idAnimatedEntity::Event_SetJointAngle )
5093 	EVENT( EV_GetJointPos,			idAnimatedEntity::Event_GetJointPos )
5094 	EVENT( EV_GetJointAngle,		idAnimatedEntity::Event_GetJointAngle )
5095 END_CLASS
5096 
5097 /*
5098 ================
5099 idAnimatedEntity::idAnimatedEntity
5100 ================
5101 */
5102 idAnimatedEntity::idAnimatedEntity() {
5103 	animator.SetEntity( this );
5104 	damageEffects = NULL;
5105 }
5106 
5107 /*
5108 ================
5109 idAnimatedEntity::~idAnimatedEntity
5110 ================
5111 */
~idAnimatedEntity()5112 idAnimatedEntity::~idAnimatedEntity() {
5113 	damageEffect_t	*de;
5114 
5115 	for ( de = damageEffects; de; de = damageEffects ) {
5116 		damageEffects = de->next;
5117 		delete de;
5118 	}
5119 }
5120 
5121 /*
5122 ================
5123 idAnimatedEntity::Save
5124 
5125 archives object for save game file
5126 ================
5127 */
Save(idSaveGame * savefile) const5128 void idAnimatedEntity::Save( idSaveGame *savefile ) const {
5129 	animator.Save( savefile );
5130 
5131 	// Wounds are very temporary, ignored at this time
5132 	//damageEffect_t			*damageEffects;
5133 }
5134 
5135 /*
5136 ================
5137 idAnimatedEntity::Restore
5138 
5139 unarchives object from save game file
5140 ================
5141 */
Restore(idRestoreGame * savefile)5142 void idAnimatedEntity::Restore( idRestoreGame *savefile ) {
5143 	animator.Restore( savefile );
5144 
5145 	// check if the entity has an MD5 model
5146 	if ( animator.ModelHandle() ) {
5147 		// set the callback to update the joints
5148 		renderEntity.callback = idEntity::ModelCallback;
5149 		animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
5150 		animator.GetBounds( gameLocal.time, renderEntity.bounds );
5151 		if ( modelDefHandle != -1 ) {
5152 			gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
5153 		}
5154 	}
5155 }
5156 
5157 /*
5158 ================
5159 idAnimatedEntity::ClientPredictionThink
5160 ================
5161 */
ClientPredictionThink(void)5162 void idAnimatedEntity::ClientPredictionThink( void ) {
5163 	RunPhysics();
5164 	UpdateAnimation();
5165 	Present();
5166 }
5167 
5168 /*
5169 ================
5170 idAnimatedEntity::Think
5171 ================
5172 */
Think(void)5173 void idAnimatedEntity::Think( void ) {
5174 	RunPhysics();
5175 	UpdateAnimation();
5176 	Present();
5177 	UpdateDamageEffects();
5178 }
5179 
5180 /*
5181 ================
5182 idAnimatedEntity::UpdateAnimation
5183 ================
5184 */
UpdateAnimation(void)5185 void idAnimatedEntity::UpdateAnimation( void ) {
5186 	// don't do animations if they're not enabled
5187 	if ( !( thinkFlags & TH_ANIMATE ) ) {
5188 		return;
5189 	}
5190 
5191 	// is the model an MD5?
5192 	if ( !animator.ModelHandle() ) {
5193 		// no, so nothing to do
5194 		return;
5195 	}
5196 
5197 	// call any frame commands that have happened in the past frame
5198 	if ( !fl.hidden ) {
5199 		animator.ServiceAnims( gameLocal.previousTime, gameLocal.time );
5200 	}
5201 
5202 	// if the model is animating then we have to update it
5203 	if ( !animator.FrameHasChanged( gameLocal.time ) ) {
5204 		// still fine the way it was
5205 		return;
5206 	}
5207 
5208 	// get the latest frame bounds
5209 	animator.GetBounds( gameLocal.time, renderEntity.bounds );
5210 	if ( renderEntity.bounds.IsCleared() && !fl.hidden ) {
5211 		gameLocal.DPrintf( "%d: inside out bounds\n", gameLocal.time );
5212 	}
5213 
5214 	// update the renderEntity
5215 	UpdateVisuals();
5216 
5217 	// the animation is updated
5218 	animator.ClearForceUpdate();
5219 }
5220 
5221 /*
5222 ================
5223 idAnimatedEntity::GetAnimator
5224 ================
5225 */
GetAnimator(void)5226 idAnimator *idAnimatedEntity::GetAnimator( void ) {
5227 	return &animator;
5228 }
5229 
5230 /*
5231 ================
5232 idAnimatedEntity::SetModel
5233 ================
5234 */
SetModel(const char * modelname)5235 void idAnimatedEntity::SetModel( const char *modelname ) {
5236 	FreeModelDef();
5237 
5238 	renderEntity.hModel = animator.SetModel( modelname );
5239 	if ( !renderEntity.hModel ) {
5240 		idEntity::SetModel( modelname );
5241 		return;
5242 	}
5243 
5244 	if ( !renderEntity.customSkin ) {
5245 		renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
5246 	}
5247 
5248 	// set the callback to update the joints
5249 	renderEntity.callback = idEntity::ModelCallback;
5250 	animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
5251 	animator.GetBounds( gameLocal.time, renderEntity.bounds );
5252 
5253 	UpdateVisuals();
5254 }
5255 
5256 /*
5257 =====================
5258 idAnimatedEntity::GetJointWorldTransform
5259 =====================
5260 */
GetJointWorldTransform(jointHandle_t jointHandle,int currentTime,idVec3 & offset,idMat3 & axis)5261 bool idAnimatedEntity::GetJointWorldTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) {
5262 	if ( !animator.GetJointTransform( jointHandle, currentTime, offset, axis ) ) {
5263 		return false;
5264 	}
5265 
5266 	ConvertLocalToWorldTransform( offset, axis );
5267 	return true;
5268 }
5269 
5270 /*
5271 ==============
5272 idAnimatedEntity::GetJointTransformForAnim
5273 ==============
5274 */
GetJointTransformForAnim(jointHandle_t jointHandle,int animNum,int frameTime,idVec3 & offset,idMat3 & axis) const5275 bool idAnimatedEntity::GetJointTransformForAnim( jointHandle_t jointHandle, int animNum, int frameTime, idVec3 &offset, idMat3 &axis ) const {
5276 	const idAnim	*anim;
5277 	int				numJoints;
5278 	idJointMat		*frame;
5279 
5280 	anim = animator.GetAnim( animNum );
5281 	if ( !anim ) {
5282 		assert( 0 );
5283 		return false;
5284 	}
5285 
5286 	numJoints = animator.NumJoints();
5287 	if ( ( jointHandle < 0 ) || ( jointHandle >= numJoints ) ) {
5288 		assert( 0 );
5289 		return false;
5290 	}
5291 
5292 	frame = ( idJointMat * )_alloca16( numJoints * sizeof( idJointMat ) );
5293 	gameEdit->ANIM_CreateAnimFrame( animator.ModelHandle(), anim->MD5Anim( 0 ), renderEntity.numJoints, frame, frameTime, animator.ModelDef()->GetVisualOffset(), animator.RemoveOrigin() );
5294 
5295 	offset = frame[ jointHandle ].ToVec3();
5296 	axis = frame[ jointHandle ].ToMat3();
5297 
5298 	return true;
5299 }
5300 
5301 /*
5302 ==============
5303 idAnimatedEntity::AddDamageEffect
5304 
5305   Dammage effects track the animating impact position, spitting out particles.
5306 ==============
5307 */
AddDamageEffect(const trace_t & collision,const idVec3 & velocity,const char * damageDefName)5308 void idAnimatedEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
5309 	jointHandle_t jointNum;
5310 	idVec3 origin, dir, localDir, localOrigin, localNormal;
5311 	idMat3 axis;
5312 
5313 	if ( !g_bloodEffects.GetBool() || renderEntity.joints == NULL ) {
5314 		return;
5315 	}
5316 
5317 	const idDeclEntityDef *def = gameLocal.FindEntityDef( damageDefName, false );
5318 	if ( def == NULL ) {
5319 		return;
5320 	}
5321 
5322 	jointNum = CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id );
5323 	if ( jointNum == INVALID_JOINT ) {
5324 		return;
5325 	}
5326 
5327 	dir = velocity;
5328 	dir.Normalize();
5329 
5330 	axis = renderEntity.joints[jointNum].ToMat3() * renderEntity.axis;
5331 	origin = renderEntity.origin + renderEntity.joints[jointNum].ToVec3() * renderEntity.axis;
5332 
5333 	localOrigin = ( collision.c.point - origin ) * axis.Transpose();
5334 	localNormal = collision.c.normal * axis.Transpose();
5335 	localDir = dir * axis.Transpose();
5336 
5337 	AddLocalDamageEffect( jointNum, localOrigin, localNormal, localDir, def, collision.c.material );
5338 
5339 	if ( gameLocal.isServer ) {
5340 		idBitMsg	msg;
5341 		byte		msgBuf[MAX_EVENT_PARAM_SIZE];
5342 
5343 		msg.Init( msgBuf, sizeof( msgBuf ) );
5344 		msg.BeginWriting();
5345 		msg.WriteShort( (int)jointNum );
5346 		msg.WriteFloat( localOrigin[0] );
5347 		msg.WriteFloat( localOrigin[1] );
5348 		msg.WriteFloat( localOrigin[2] );
5349 		msg.WriteDir( localNormal, 24 );
5350 		msg.WriteDir( localDir, 24 );
5351 		msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, def->Index() ) );
5352 		msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_MATERIAL, collision.c.material->Index() ) );
5353 		ServerSendEvent( EVENT_ADD_DAMAGE_EFFECT, &msg, false, -1 );
5354 	}
5355 }
5356 
5357 /*
5358 ==============
5359 idAnimatedEntity::GetDefaultSurfaceType
5360 ==============
5361 */
GetDefaultSurfaceType(void) const5362 int	idAnimatedEntity::GetDefaultSurfaceType( void ) const {
5363 	return SURFTYPE_METAL;
5364 }
5365 
5366 /*
5367 ==============
5368 idAnimatedEntity::AddLocalDamageEffect
5369 ==============
5370 */
AddLocalDamageEffect(jointHandle_t jointNum,const idVec3 & localOrigin,const idVec3 & localNormal,const idVec3 & localDir,const idDeclEntityDef * def,const idMaterial * collisionMaterial)5371 void idAnimatedEntity::AddLocalDamageEffect( jointHandle_t jointNum, const idVec3 &localOrigin, const idVec3 &localNormal, const idVec3 &localDir, const idDeclEntityDef *def, const idMaterial *collisionMaterial ) {
5372 	const char *sound, *splat, *decal, *bleed, *key;
5373 	damageEffect_t	*de;
5374 	idVec3 origin, dir;
5375 	idMat3 axis;
5376 
5377 #ifdef _D3XP
5378 	SetTimeState ts( timeGroup );
5379 #endif
5380 
5381 	axis = renderEntity.joints[jointNum].ToMat3() * renderEntity.axis;
5382 	origin = renderEntity.origin + renderEntity.joints[jointNum].ToVec3() * renderEntity.axis;
5383 
5384 	origin = origin + localOrigin * axis;
5385 	dir = localDir * axis;
5386 
5387 	int type = collisionMaterial->GetSurfaceType();
5388 	if ( type == SURFTYPE_NONE ) {
5389 		type = GetDefaultSurfaceType();
5390 	}
5391 
5392 	const char *materialType = gameLocal.sufaceTypeNames[ type ];
5393 
5394 	// start impact sound based on material type
5395 	key = va( "snd_%s", materialType );
5396 	sound = spawnArgs.GetString( key );
5397 	if ( *sound == '\0' ) {
5398 		sound = def->dict.GetString( key );
5399 	}
5400 	if ( *sound != '\0' ) {
5401 		StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
5402 	}
5403 
5404 	// blood splats are thrown onto nearby surfaces
5405 	key = va( "mtr_splat_%s", materialType );
5406 	splat = spawnArgs.RandomPrefix( key, gameLocal.random );
5407 	if ( *splat == '\0' ) {
5408 		splat = def->dict.RandomPrefix( key, gameLocal.random );
5409 	}
5410 	if ( *splat != '\0' ) {
5411 		gameLocal.BloodSplat( origin, dir, 64.0f, splat );
5412 	}
5413 
5414 	// can't see wounds on the player model in single player mode
5415 	if ( !( IsType( idPlayer::Type ) && !gameLocal.isMultiplayer ) ) {
5416 		// place a wound overlay on the model
5417 		key = va( "mtr_wound_%s", materialType );
5418 		decal = spawnArgs.RandomPrefix( key, gameLocal.random );
5419 		if ( *decal == '\0' ) {
5420 			decal = def->dict.RandomPrefix( key, gameLocal.random );
5421 		}
5422 		if ( *decal != '\0' ) {
5423 			ProjectOverlay( origin, dir, 20.0f, decal );
5424 		}
5425 	}
5426 
5427 	// a blood spurting wound is added
5428 	key = va( "smoke_wound_%s", materialType );
5429 	bleed = spawnArgs.GetString( key );
5430 	if ( *bleed == '\0' ) {
5431 		bleed = def->dict.GetString( key );
5432 	}
5433 	if ( *bleed != '\0' ) {
5434 		de = new damageEffect_t;
5435 		de->next = this->damageEffects;
5436 		this->damageEffects = de;
5437 
5438 		de->jointNum = jointNum;
5439 		de->localOrigin = localOrigin;
5440 		de->localNormal = localNormal;
5441 		de->type = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, bleed ) );
5442 		de->time = gameLocal.time;
5443 	}
5444 }
5445 
5446 /*
5447 ==============
5448 idAnimatedEntity::UpdateDamageEffects
5449 ==============
5450 */
UpdateDamageEffects(void)5451 void idAnimatedEntity::UpdateDamageEffects( void ) {
5452 	damageEffect_t	*de, **prev;
5453 
5454 	// free any that have timed out
5455 	prev = &this->damageEffects;
5456 	while ( *prev ) {
5457 		de = *prev;
5458 		if ( de->time == 0 ) {	// FIXME:SMOKE
5459 			*prev = de->next;
5460 			delete de;
5461 		} else {
5462 			prev = &de->next;
5463 		}
5464 	}
5465 
5466 	if ( !g_bloodEffects.GetBool() ) {
5467 		return;
5468 	}
5469 
5470 	// emit a particle for each bleeding wound
5471 	for ( de = this->damageEffects; de; de = de->next ) {
5472 		idVec3 origin, start;
5473 		idMat3 axis;
5474 
5475 		animator.GetJointTransform( de->jointNum, gameLocal.time, origin, axis );
5476 		axis *= renderEntity.axis;
5477 		origin = renderEntity.origin + origin * renderEntity.axis;
5478 		start = origin + de->localOrigin * axis;
5479 		if ( !gameLocal.smokeParticles->EmitSmoke( de->type, de->time, gameLocal.random.CRandomFloat(), start, axis, timeGroup /*_D3XP*/ ) ) {
5480 			de->time = 0;
5481 		}
5482 	}
5483 }
5484 
5485 /*
5486 ================
5487 idAnimatedEntity::ClientReceiveEvent
5488 ================
5489 */
ClientReceiveEvent(int event,int time,const idBitMsg & msg)5490 bool idAnimatedEntity::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
5491 	int damageDefIndex;
5492 	int materialIndex;
5493 	jointHandle_t jointNum;
5494 	idVec3 localOrigin, localNormal, localDir;
5495 
5496 	switch( event ) {
5497 		case EVENT_ADD_DAMAGE_EFFECT: {
5498 			jointNum = (jointHandle_t) msg.ReadShort();
5499 			localOrigin[0] = msg.ReadFloat();
5500 			localOrigin[1] = msg.ReadFloat();
5501 			localOrigin[2] = msg.ReadFloat();
5502 			localNormal = msg.ReadDir( 24 );
5503 			localDir = msg.ReadDir( 24 );
5504 			damageDefIndex = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadInt() );
5505 			materialIndex = gameLocal.ClientRemapDecl( DECL_MATERIAL, msg.ReadInt() );
5506 			const idDeclEntityDef *damageDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, damageDefIndex ) );
5507 			const idMaterial *collisionMaterial = static_cast<const idMaterial *>( declManager->DeclByIndex( DECL_MATERIAL, materialIndex ) );
5508 			AddLocalDamageEffect( jointNum, localOrigin, localNormal, localDir, damageDef, collisionMaterial );
5509 			return true;
5510 		}
5511 		default:
5512 			break;
5513 	}
5514 
5515 	return idEntity::ClientReceiveEvent( event, time, msg );
5516 }
5517 
5518 /*
5519 ================
5520 idAnimatedEntity::Event_GetJointHandle
5521 
5522 looks up the number of the specified joint.  returns INVALID_JOINT if the joint is not found.
5523 ================
5524 */
Event_GetJointHandle(const char * jointname)5525 void idAnimatedEntity::Event_GetJointHandle( const char *jointname ) {
5526 	jointHandle_t joint;
5527 
5528 	joint = animator.GetJointHandle( jointname );
5529 	idThread::ReturnInt( joint );
5530 }
5531 
5532 /*
5533 ================
5534 idAnimatedEntity::Event_ClearAllJoints
5535 
5536 removes any custom transforms on all joints
5537 ================
5538 */
Event_ClearAllJoints(void)5539 void idAnimatedEntity::Event_ClearAllJoints( void ) {
5540 	animator.ClearAllJoints();
5541 }
5542 
5543 /*
5544 ================
5545 idAnimatedEntity::Event_ClearJoint
5546 
5547 removes any custom transforms on the specified joint
5548 ================
5549 */
Event_ClearJoint(jointHandle_t jointnum)5550 void idAnimatedEntity::Event_ClearJoint( jointHandle_t jointnum ) {
5551 	animator.ClearJoint( jointnum );
5552 }
5553 
5554 /*
5555 ================
5556 idAnimatedEntity::Event_SetJointPos
5557 
5558 modifies the position of the joint based on the transform type
5559 ================
5560 */
Event_SetJointPos(jointHandle_t jointnum,jointModTransform_t transform_type,const idVec3 & pos)5561 void idAnimatedEntity::Event_SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos ) {
5562 	animator.SetJointPos( jointnum, transform_type, pos );
5563 }
5564 
5565 /*
5566 ================
5567 idAnimatedEntity::Event_SetJointAngle
5568 
5569 modifies the orientation of the joint based on the transform type
5570 ================
5571 */
Event_SetJointAngle(jointHandle_t jointnum,jointModTransform_t transform_type,const idAngles & angles)5572 void idAnimatedEntity::Event_SetJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &angles ) {
5573 	idMat3 mat;
5574 
5575 	mat = angles.ToMat3();
5576 	animator.SetJointAxis( jointnum, transform_type, mat );
5577 }
5578 
5579 /*
5580 ================
5581 idAnimatedEntity::Event_GetJointPos
5582 
5583 returns the position of the joint in worldspace
5584 ================
5585 */
Event_GetJointPos(jointHandle_t jointnum)5586 void idAnimatedEntity::Event_GetJointPos( jointHandle_t jointnum ) {
5587 	idVec3 offset;
5588 	idMat3 axis;
5589 
5590 	if ( !GetJointWorldTransform( jointnum, gameLocal.time, offset, axis ) ) {
5591 		gameLocal.Warning( "Joint # %d out of range on entity '%s'",  jointnum, name.c_str() );
5592 	}
5593 
5594 	idThread::ReturnVector( offset );
5595 }
5596 
5597 /*
5598 ================
5599 idAnimatedEntity::Event_GetJointAngle
5600 
5601 returns the orientation of the joint in worldspace
5602 ================
5603 */
Event_GetJointAngle(jointHandle_t jointnum)5604 void idAnimatedEntity::Event_GetJointAngle( jointHandle_t jointnum ) {
5605 	idVec3 offset;
5606 	idMat3 axis;
5607 
5608 	if ( !GetJointWorldTransform( jointnum, gameLocal.time, offset, axis ) ) {
5609 		gameLocal.Warning( "Joint # %d out of range on entity '%s'",  jointnum, name.c_str() );
5610 	}
5611 
5612 	idAngles ang = axis.ToAngles();
5613 	idVec3 vec( ang[ 0 ], ang[ 1 ], ang[ 2 ] );
5614 	idThread::ReturnVector( vec );
5615 }
5616