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