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 "gamesys/SysCvar.h"
31 #include "script/Script_Thread.h"
32 #include "Item.h"
33 #include "Light.h"
34 #include "Projectile.h"
35 #include "WorldSpawn.h"
36
37 #include "Actor.h"
38
39
40 /***********************************************************************
41
42 idAnimState
43
44 ***********************************************************************/
45
46 /*
47 =====================
48 idAnimState::idAnimState
49 =====================
50 */
idAnimState()51 idAnimState::idAnimState() {
52 self = NULL;
53 animator = NULL;
54 thread = NULL;
55 idleAnim = true;
56 disabled = true;
57 channel = ANIMCHANNEL_ALL;
58 animBlendFrames = 0;
59 lastAnimBlendFrames = 0;
60 }
61
62 /*
63 =====================
64 idAnimState::~idAnimState
65 =====================
66 */
~idAnimState()67 idAnimState::~idAnimState() {
68 delete thread;
69 }
70
71 /*
72 =====================
73 idAnimState::Save
74 =====================
75 */
Save(idSaveGame * savefile) const76 void idAnimState::Save( idSaveGame *savefile ) const {
77
78 savefile->WriteObject( self );
79
80 // Save the entity owner of the animator
81 savefile->WriteObject( animator->GetEntity() );
82
83 savefile->WriteObject( thread );
84
85 savefile->WriteString( state );
86
87 savefile->WriteInt( animBlendFrames );
88 savefile->WriteInt( lastAnimBlendFrames );
89 savefile->WriteInt( channel );
90 savefile->WriteBool( idleAnim );
91 savefile->WriteBool( disabled );
92 }
93
94 /*
95 =====================
96 idAnimState::Restore
97 =====================
98 */
Restore(idRestoreGame * savefile)99 void idAnimState::Restore( idRestoreGame *savefile ) {
100 savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
101
102 idEntity *animowner;
103 savefile->ReadObject( reinterpret_cast<idClass *&>( animowner ) );
104 if ( animowner ) {
105 animator = animowner->GetAnimator();
106 }
107
108 savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
109
110 savefile->ReadString( state );
111
112 savefile->ReadInt( animBlendFrames );
113 savefile->ReadInt( lastAnimBlendFrames );
114 savefile->ReadInt( channel );
115 savefile->ReadBool( idleAnim );
116 savefile->ReadBool( disabled );
117 }
118
119 /*
120 =====================
121 idAnimState::Init
122 =====================
123 */
Init(idActor * owner,idAnimator * _animator,int animchannel)124 void idAnimState::Init( idActor *owner, idAnimator *_animator, int animchannel ) {
125 assert( owner );
126 assert( _animator );
127 self = owner;
128 animator = _animator;
129 channel = animchannel;
130
131 if ( !thread ) {
132 thread = new idThread();
133 thread->ManualDelete();
134 }
135 thread->EndThread();
136 thread->ManualControl();
137 }
138
139 /*
140 =====================
141 idAnimState::Shutdown
142 =====================
143 */
Shutdown(void)144 void idAnimState::Shutdown( void ) {
145 delete thread;
146 thread = NULL;
147 }
148
149 /*
150 =====================
151 idAnimState::SetState
152 =====================
153 */
SetState(const char * statename,int blendFrames)154 void idAnimState::SetState( const char *statename, int blendFrames ) {
155 const function_t *func;
156
157 func = self->scriptObject.GetFunction( statename );
158 if ( !func ) {
159 assert( 0 );
160 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, self->scriptObject.GetTypeName() );
161 }
162
163 state = statename;
164 disabled = false;
165 animBlendFrames = blendFrames;
166 lastAnimBlendFrames = blendFrames;
167 thread->CallFunction( self, func, true );
168
169 animBlendFrames = blendFrames;
170 lastAnimBlendFrames = blendFrames;
171 disabled = false;
172 idleAnim = false;
173
174 if ( ai_debugScript.GetInteger() == self->entityNumber ) {
175 gameLocal.Printf( "%d: %s: Animstate: %s\n", gameLocal.time, self->name.c_str(), state.c_str() );
176 }
177 }
178
179 /*
180 =====================
181 idAnimState::StopAnim
182 =====================
183 */
StopAnim(int frames)184 void idAnimState::StopAnim( int frames ) {
185 animBlendFrames = 0;
186 animator->Clear( channel, gameLocal.time, FRAME2MS( frames ) );
187 }
188
189 /*
190 =====================
191 idAnimState::PlayAnim
192 =====================
193 */
PlayAnim(int anim)194 void idAnimState::PlayAnim( int anim ) {
195 if ( anim ) {
196 animator->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
197 }
198 animBlendFrames = 0;
199 }
200
201 /*
202 =====================
203 idAnimState::CycleAnim
204 =====================
205 */
CycleAnim(int anim)206 void idAnimState::CycleAnim( int anim ) {
207 if ( anim ) {
208 animator->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
209 }
210 animBlendFrames = 0;
211 }
212
213 /*
214 =====================
215 idAnimState::BecomeIdle
216 =====================
217 */
BecomeIdle(void)218 void idAnimState::BecomeIdle( void ) {
219 idleAnim = true;
220 }
221
222 /*
223 =====================
224 idAnimState::Disabled
225 =====================
226 */
Disabled(void) const227 bool idAnimState::Disabled( void ) const {
228 return disabled;
229 }
230
231 /*
232 =====================
233 idAnimState::AnimDone
234 =====================
235 */
AnimDone(int blendFrames) const236 bool idAnimState::AnimDone( int blendFrames ) const {
237 int animDoneTime;
238
239 animDoneTime = animator->CurrentAnim( channel )->GetEndTime();
240 if ( animDoneTime < 0 ) {
241 // playing a cycle
242 return false;
243 } else if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
244 return true;
245 } else {
246 return false;
247 }
248 }
249
250 /*
251 =====================
252 idAnimState::IsIdle
253 =====================
254 */
IsIdle(void) const255 bool idAnimState::IsIdle( void ) const {
256 return disabled || idleAnim;
257 }
258
259 /*
260 =====================
261 idAnimState::GetAnimFlags
262 =====================
263 */
GetAnimFlags(void) const264 animFlags_t idAnimState::GetAnimFlags( void ) const {
265 animFlags_t flags;
266
267 memset( &flags, 0, sizeof( flags ) );
268 if ( !disabled && !AnimDone( 0 ) ) {
269 flags = animator->GetAnimFlags( animator->CurrentAnim( channel )->AnimNum() );
270 }
271
272 return flags;
273 }
274
275 /*
276 =====================
277 idAnimState::Enable
278 =====================
279 */
Enable(int blendFrames)280 void idAnimState::Enable( int blendFrames ) {
281 if ( disabled ) {
282 disabled = false;
283 animBlendFrames = blendFrames;
284 lastAnimBlendFrames = blendFrames;
285 if ( state.Length() ) {
286 SetState( state.c_str(), blendFrames );
287 }
288 }
289 }
290
291 /*
292 =====================
293 idAnimState::Disable
294 =====================
295 */
Disable(void)296 void idAnimState::Disable( void ) {
297 disabled = true;
298 idleAnim = false;
299 }
300
301 /*
302 =====================
303 idAnimState::UpdateState
304 =====================
305 */
UpdateState(void)306 bool idAnimState::UpdateState( void ) {
307 if ( disabled ) {
308 return false;
309 }
310
311 if ( ai_debugScript.GetInteger() == self->entityNumber ) {
312 thread->EnableDebugInfo();
313 } else {
314 thread->DisableDebugInfo();
315 }
316
317 thread->Execute();
318
319 return true;
320 }
321
322 /***********************************************************************
323
324 idActor
325
326 ***********************************************************************/
327
328 const idEventDef AI_EnableEyeFocus( "enableEyeFocus" );
329 const idEventDef AI_DisableEyeFocus( "disableEyeFocus" );
330 const idEventDef EV_Footstep( "footstep" );
331 const idEventDef EV_FootstepLeft( "leftFoot" );
332 const idEventDef EV_FootstepRight( "rightFoot" );
333 const idEventDef EV_EnableWalkIK( "EnableWalkIK" );
334 const idEventDef EV_DisableWalkIK( "DisableWalkIK" );
335 const idEventDef EV_EnableLegIK( "EnableLegIK", "d" );
336 const idEventDef EV_DisableLegIK( "DisableLegIK", "d" );
337 const idEventDef AI_StopAnim( "stopAnim", "dd" );
338 const idEventDef AI_PlayAnim( "playAnim", "ds", 'd' );
339 const idEventDef AI_PlayCycle( "playCycle", "ds", 'd' );
340 const idEventDef AI_IdleAnim( "idleAnim", "ds", 'd' );
341 const idEventDef AI_SetSyncedAnimWeight( "setSyncedAnimWeight", "ddf" );
342 const idEventDef AI_SetBlendFrames( "setBlendFrames", "dd" );
343 const idEventDef AI_GetBlendFrames( "getBlendFrames", "d", 'd' );
344 const idEventDef AI_AnimState( "animState", "dsd" );
345 const idEventDef AI_GetAnimState( "getAnimState", "d", 's' );
346 const idEventDef AI_InAnimState( "inAnimState", "ds", 'd' );
347 const idEventDef AI_FinishAction( "finishAction", "s" );
348 const idEventDef AI_AnimDone( "animDone", "dd", 'd' );
349 const idEventDef AI_OverrideAnim( "overrideAnim", "d" );
350 const idEventDef AI_EnableAnim( "enableAnim", "dd" );
351 const idEventDef AI_PreventPain( "preventPain", "f" );
352 const idEventDef AI_DisablePain( "disablePain" );
353 const idEventDef AI_EnablePain( "enablePain" );
354 const idEventDef AI_GetPainAnim( "getPainAnim", NULL, 's' );
355 const idEventDef AI_SetAnimPrefix( "setAnimPrefix", "s" );
356 const idEventDef AI_HasAnim( "hasAnim", "ds", 'f' );
357 const idEventDef AI_CheckAnim( "checkAnim", "ds" );
358 const idEventDef AI_ChooseAnim( "chooseAnim", "ds", 's' );
359 const idEventDef AI_AnimLength( "animLength", "ds", 'f' );
360 const idEventDef AI_AnimDistance( "animDistance", "ds", 'f' );
361 const idEventDef AI_HasEnemies( "hasEnemies", NULL, 'd' );
362 const idEventDef AI_NextEnemy( "nextEnemy", "E", 'e' );
363 const idEventDef AI_ClosestEnemyToPoint( "closestEnemyToPoint", "v", 'e' );
364 const idEventDef AI_SetNextState( "setNextState", "s" );
365 const idEventDef AI_SetState( "setState", "s" );
366 const idEventDef AI_GetState( "getState", NULL, 's' );
367 const idEventDef AI_GetHead( "getHead", NULL, 'e' );
368 #ifdef _D3XP
369 const idEventDef EV_SetDamageGroupScale( "setDamageGroupScale", "sf" );
370 const idEventDef EV_SetDamageGroupScaleAll( "setDamageGroupScaleAll", "f" );
371 const idEventDef EV_GetDamageGroupScale( "getDamageGroupScale", "s", 'f' );
372 const idEventDef EV_SetDamageCap( "setDamageCap", "f" );
373 const idEventDef EV_SetWaitState( "setWaitState" , "s" );
374 const idEventDef EV_GetWaitState( "getWaitState", NULL, 's' );
375 #endif
376
CLASS_DECLARATION(idAFEntity_Gibbable,idActor)377 CLASS_DECLARATION( idAFEntity_Gibbable, idActor )
378 EVENT( AI_EnableEyeFocus, idActor::Event_EnableEyeFocus )
379 EVENT( AI_DisableEyeFocus, idActor::Event_DisableEyeFocus )
380 EVENT( EV_Footstep, idActor::Event_Footstep )
381 EVENT( EV_FootstepLeft, idActor::Event_Footstep )
382 EVENT( EV_FootstepRight, idActor::Event_Footstep )
383 EVENT( EV_EnableWalkIK, idActor::Event_EnableWalkIK )
384 EVENT( EV_DisableWalkIK, idActor::Event_DisableWalkIK )
385 EVENT( EV_EnableLegIK, idActor::Event_EnableLegIK )
386 EVENT( EV_DisableLegIK, idActor::Event_DisableLegIK )
387 EVENT( AI_PreventPain, idActor::Event_PreventPain )
388 EVENT( AI_DisablePain, idActor::Event_DisablePain )
389 EVENT( AI_EnablePain, idActor::Event_EnablePain )
390 EVENT( AI_GetPainAnim, idActor::Event_GetPainAnim )
391 EVENT( AI_SetAnimPrefix, idActor::Event_SetAnimPrefix )
392 EVENT( AI_StopAnim, idActor::Event_StopAnim )
393 EVENT( AI_PlayAnim, idActor::Event_PlayAnim )
394 EVENT( AI_PlayCycle, idActor::Event_PlayCycle )
395 EVENT( AI_IdleAnim, idActor::Event_IdleAnim )
396 EVENT( AI_SetSyncedAnimWeight, idActor::Event_SetSyncedAnimWeight )
397 EVENT( AI_SetBlendFrames, idActor::Event_SetBlendFrames )
398 EVENT( AI_GetBlendFrames, idActor::Event_GetBlendFrames )
399 EVENT( AI_AnimState, idActor::Event_AnimState )
400 EVENT( AI_GetAnimState, idActor::Event_GetAnimState )
401 EVENT( AI_InAnimState, idActor::Event_InAnimState )
402 EVENT( AI_FinishAction, idActor::Event_FinishAction )
403 EVENT( AI_AnimDone, idActor::Event_AnimDone )
404 EVENT( AI_OverrideAnim, idActor::Event_OverrideAnim )
405 EVENT( AI_EnableAnim, idActor::Event_EnableAnim )
406 EVENT( AI_HasAnim, idActor::Event_HasAnim )
407 EVENT( AI_CheckAnim, idActor::Event_CheckAnim )
408 EVENT( AI_ChooseAnim, idActor::Event_ChooseAnim )
409 EVENT( AI_AnimLength, idActor::Event_AnimLength )
410 EVENT( AI_AnimDistance, idActor::Event_AnimDistance )
411 EVENT( AI_HasEnemies, idActor::Event_HasEnemies )
412 EVENT( AI_NextEnemy, idActor::Event_NextEnemy )
413 EVENT( AI_ClosestEnemyToPoint, idActor::Event_ClosestEnemyToPoint )
414 EVENT( EV_StopSound, idActor::Event_StopSound )
415 EVENT( AI_SetNextState, idActor::Event_SetNextState )
416 EVENT( AI_SetState, idActor::Event_SetState )
417 EVENT( AI_GetState, idActor::Event_GetState )
418 EVENT( AI_GetHead, idActor::Event_GetHead )
419 #ifdef _D3XP
420 EVENT( EV_SetDamageGroupScale, idActor::Event_SetDamageGroupScale )
421 EVENT( EV_SetDamageGroupScaleAll, idActor::Event_SetDamageGroupScaleAll )
422 EVENT( EV_GetDamageGroupScale, idActor::Event_GetDamageGroupScale )
423 EVENT( EV_SetDamageCap, idActor::Event_SetDamageCap )
424 EVENT( EV_SetWaitState, idActor::Event_SetWaitState )
425 EVENT( EV_GetWaitState, idActor::Event_GetWaitState )
426 #endif
427 END_CLASS
428
429 /*
430 =====================
431 idActor::idActor
432 =====================
433 */
434 idActor::idActor( void ) {
435 viewAxis.Identity();
436
437 scriptThread = NULL; // initialized by ConstructScriptObject, which is called by idEntity::Spawn
438
439 use_combat_bbox = false;
440 head = NULL;
441
442 team = 0;
443 rank = 0;
444 fovDot = 0.0f;
445 eyeOffset.Zero();
446 pain_debounce_time = 0;
447 pain_delay = 0;
448 pain_threshold = 0;
449
450 state = NULL;
451 idealState = NULL;
452
453 leftEyeJoint = INVALID_JOINT;
454 rightEyeJoint = INVALID_JOINT;
455 soundJoint = INVALID_JOINT;
456
457 modelOffset.Zero();
458 deltaViewAngles.Zero();
459
460 painTime = 0;
461 allowPain = false;
462 allowEyeFocus = false;
463
464 waitState = "";
465
466 blink_anim = 0;
467 blink_time = 0;
468 blink_min = 0;
469 blink_max = 0;
470
471 finalBoss = false;
472
473 attachments.SetGranularity( 1 );
474
475 enemyNode.SetOwner( this );
476 enemyList.SetOwner( this );
477
478 #ifdef _D3XP
479 damageCap = -1;
480 #endif
481 }
482
483 /*
484 =====================
485 idActor::~idActor
486 =====================
487 */
~idActor(void)488 idActor::~idActor( void ) {
489 int i;
490 idEntity *ent;
491
492 DeconstructScriptObject();
493 scriptObject.Free();
494
495 StopSound( SND_CHANNEL_ANY, false );
496
497 delete combatModel;
498 combatModel = NULL;
499
500 if ( head.GetEntity() ) {
501 head.GetEntity()->ClearBody();
502 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
503 }
504
505 // remove any attached entities
506 for( i = 0; i < attachments.Num(); i++ ) {
507 ent = attachments[ i ].ent.GetEntity();
508 if ( ent ) {
509 ent->PostEventMS( &EV_Remove, 0 );
510 }
511 }
512
513 ShutdownThreads();
514 }
515
516 /*
517 =====================
518 idActor::Spawn
519 =====================
520 */
Spawn(void)521 void idActor::Spawn( void ) {
522 idEntity *ent;
523 idStr jointName;
524 float fovDegrees;
525 copyJoints_t copyJoint;
526
527 animPrefix = "";
528 state = NULL;
529 idealState = NULL;
530
531 spawnArgs.GetInt( "rank", "0", rank );
532 spawnArgs.GetInt( "team", "0", team );
533 spawnArgs.GetVector( "offsetModel", "0 0 0", modelOffset );
534
535 spawnArgs.GetBool( "use_combat_bbox", "0", use_combat_bbox );
536
537 viewAxis = GetPhysics()->GetAxis();
538
539 spawnArgs.GetFloat( "fov", "90", fovDegrees );
540 SetFOV( fovDegrees );
541
542 pain_debounce_time = 0;
543
544 pain_delay = SEC2MS( spawnArgs.GetFloat( "pain_delay" ) );
545 pain_threshold = spawnArgs.GetInt( "pain_threshold" );
546
547 LoadAF();
548
549 walkIK.Init( this, IK_ANIM, modelOffset );
550
551 // the animation used to be set to the IK_ANIM at this point, but that was fixed, resulting in
552 // attachments not binding correctly, so we're stuck setting the IK_ANIM before attaching things.
553 animator.ClearAllAnims( gameLocal.time, 0 );
554 animator.SetFrame( ANIMCHANNEL_ALL, animator.GetAnim( IK_ANIM ), 0, 0, 0 );
555
556 // spawn any attachments we might have
557 const idKeyValue *kv = spawnArgs.MatchPrefix( "def_attach", NULL );
558 while ( kv ) {
559 idDict args;
560
561 args.Set( "classname", kv->GetValue().c_str() );
562
563 // make items non-touchable so the player can't take them out of the character's hands
564 args.Set( "no_touch", "1" );
565
566 // don't let them drop to the floor
567 args.Set( "dropToFloor", "0" );
568
569 gameLocal.SpawnEntityDef( args, &ent );
570 if ( !ent ) {
571 gameLocal.Error( "Couldn't spawn '%s' to attach to entity '%s'", kv->GetValue().c_str(), name.c_str() );
572 } else {
573 Attach( ent );
574 }
575 kv = spawnArgs.MatchPrefix( "def_attach", kv );
576 }
577
578 SetupDamageGroups();
579 SetupHead();
580
581 // clear the bind anim
582 animator.ClearAllAnims( gameLocal.time, 0 );
583
584 idEntity *headEnt = head.GetEntity();
585 idAnimator *headAnimator;
586 if ( headEnt ) {
587 headAnimator = headEnt->GetAnimator();
588 } else {
589 headAnimator = &animator;
590 }
591
592 if ( headEnt ) {
593 // set up the list of joints to copy to the head
594 for( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) ) {
595 if ( kv->GetValue() == "" ) {
596 // probably clearing out inherited key, so skip it
597 continue;
598 }
599
600 jointName = kv->GetKey();
601 if ( jointName.StripLeadingOnce( "copy_joint_world " ) ) {
602 copyJoint.mod = JOINTMOD_WORLD_OVERRIDE;
603 } else {
604 jointName.StripLeadingOnce( "copy_joint " );
605 copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE;
606 }
607
608 copyJoint.from = animator.GetJointHandle( jointName );
609 if ( copyJoint.from == INVALID_JOINT ) {
610 gameLocal.Warning( "Unknown copy_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
611 continue;
612 }
613
614 jointName = kv->GetValue();
615 copyJoint.to = headAnimator->GetJointHandle( jointName );
616 if ( copyJoint.to == INVALID_JOINT ) {
617 gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %s", jointName.c_str(), name.c_str() );
618 continue;
619 }
620
621 copyJoints.Append( copyJoint );
622 }
623 }
624
625 // set up blinking
626 blink_anim = headAnimator->GetAnim( "blink" );
627 blink_time = 0; // it's ok to blink right away
628 blink_min = SEC2MS( spawnArgs.GetFloat( "blink_min", "0.5" ) );
629 blink_max = SEC2MS( spawnArgs.GetFloat( "blink_max", "8" ) );
630
631 // set up the head anim if necessary
632 int headAnim = headAnimator->GetAnim( "def_head" );
633 if ( headAnim ) {
634 if ( headEnt ) {
635 headAnimator->CycleAnim( ANIMCHANNEL_ALL, headAnim, gameLocal.time, 0 );
636 } else {
637 headAnimator->CycleAnim( ANIMCHANNEL_HEAD, headAnim, gameLocal.time, 0 );
638 }
639 }
640
641 if ( spawnArgs.GetString( "sound_bone", "", jointName ) ) {
642 soundJoint = animator.GetJointHandle( jointName );
643 if ( soundJoint == INVALID_JOINT ) {
644 gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), jointName.c_str() );
645 }
646 }
647
648 finalBoss = spawnArgs.GetBool( "finalBoss" );
649
650 FinishSetup();
651 }
652
653 /*
654 ================
655 idActor::FinishSetup
656 ================
657 */
FinishSetup(void)658 void idActor::FinishSetup( void ) {
659 const char *scriptObjectName;
660
661 // setup script object
662 if ( spawnArgs.GetString( "scriptobject", NULL, &scriptObjectName ) ) {
663 if ( !scriptObject.SetType( scriptObjectName ) ) {
664 gameLocal.Error( "Script object '%s' not found on entity '%s'.", scriptObjectName, name.c_str() );
665 }
666
667 ConstructScriptObject();
668 }
669
670 SetupBody();
671 }
672
673 /*
674 ================
675 idActor::SetupHead
676 ================
677 */
SetupHead(void)678 void idActor::SetupHead( void ) {
679 idAFAttachment *headEnt;
680 idStr jointName;
681 const char *headModel;
682 jointHandle_t joint;
683 jointHandle_t damageJoint;
684 int i;
685 const idKeyValue *sndKV;
686
687 if ( gameLocal.isClient ) {
688 return;
689 }
690
691 headModel = spawnArgs.GetString( "def_head", "" );
692 if ( headModel[ 0 ] ) {
693 jointName = spawnArgs.GetString( "head_joint" );
694 joint = animator.GetJointHandle( jointName );
695 if ( joint == INVALID_JOINT ) {
696 gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
697 }
698
699 // set the damage joint to be part of the head damage group
700 damageJoint = joint;
701 for( i = 0; i < damageGroups.Num(); i++ ) {
702 if ( damageGroups[ i ] == "head" ) {
703 damageJoint = static_cast<jointHandle_t>( i );
704 break;
705 }
706 }
707
708 // copy any sounds in case we have frame commands on the head
709 idDict args;
710 sndKV = spawnArgs.MatchPrefix( "snd_", NULL );
711 while( sndKV ) {
712 args.Set( sndKV->GetKey(), sndKV->GetValue() );
713 sndKV = spawnArgs.MatchPrefix( "snd_", sndKV );
714 }
715
716 #ifdef _D3XP
717 // copy slowmo param to the head
718 args.SetBool( "slowmo", spawnArgs.GetBool("slowmo", "1") );
719 #endif
720
721
722 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, &args ) );
723 headEnt->SetName( va( "%s_head", name.c_str() ) );
724 headEnt->SetBody( this, headModel, damageJoint );
725 head = headEnt;
726
727 #ifdef _D3XP
728 idStr xSkin;
729 if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
730 headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
731 headEnt->UpdateModel();
732 }
733 #endif
734
735 idVec3 origin;
736 idMat3 axis;
737 idAttachInfo &attach = attachments.Alloc();
738 attach.channel = animator.GetChannelForJoint( joint );
739 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
740 origin = renderEntity.origin + ( origin + modelOffset ) * renderEntity.axis;
741 attach.ent = headEnt;
742 headEnt->SetOrigin( origin );
743 headEnt->SetAxis( renderEntity.axis );
744 headEnt->BindToJoint( this, joint, true );
745 }
746 }
747
748 /*
749 ================
750 idActor::CopyJointsFromBodyToHead
751 ================
752 */
CopyJointsFromBodyToHead(void)753 void idActor::CopyJointsFromBodyToHead( void ) {
754 idEntity *headEnt = head.GetEntity();
755 idAnimator *headAnimator;
756 int i;
757 idMat3 mat;
758 idMat3 axis;
759 idVec3 pos;
760
761 if ( !headEnt ) {
762 return;
763 }
764
765 headAnimator = headEnt->GetAnimator();
766
767 // copy the animation from the body to the head
768 for( i = 0; i < copyJoints.Num(); i++ ) {
769 if ( copyJoints[ i ].mod == JOINTMOD_WORLD_OVERRIDE ) {
770 mat = headEnt->GetPhysics()->GetAxis().Transpose();
771 GetJointWorldTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
772 pos -= headEnt->GetPhysics()->GetOrigin();
773 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos * mat );
774 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis * mat );
775 } else {
776 animator.GetJointLocalTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
777 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos );
778 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis );
779 }
780 }
781 }
782
783 /*
784 ================
785 idActor::Restart
786 ================
787 */
Restart(void)788 void idActor::Restart( void ) {
789 assert( !head.GetEntity() );
790 SetupHead();
791 FinishSetup();
792 }
793
794 /*
795 ================
796 idActor::Save
797
798 archive object for savegame file
799 ================
800 */
Save(idSaveGame * savefile) const801 void idActor::Save( idSaveGame *savefile ) const {
802 idActor *ent;
803 int i;
804
805 savefile->WriteInt( team );
806 savefile->WriteInt( rank );
807 savefile->WriteMat3( viewAxis );
808
809 savefile->WriteInt( enemyList.Num() );
810 for ( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
811 savefile->WriteObject( ent );
812 }
813
814 savefile->WriteFloat( fovDot );
815 savefile->WriteVec3( eyeOffset );
816 savefile->WriteVec3( modelOffset );
817 savefile->WriteAngles( deltaViewAngles );
818
819 savefile->WriteInt( pain_debounce_time );
820 savefile->WriteInt( pain_delay );
821 savefile->WriteInt( pain_threshold );
822
823 savefile->WriteInt( damageGroups.Num() );
824 for( i = 0; i < damageGroups.Num(); i++ ) {
825 savefile->WriteString( damageGroups[ i ] );
826 }
827
828 savefile->WriteInt( damageScale.Num() );
829 for( i = 0; i < damageScale.Num(); i++ ) {
830 savefile->WriteFloat( damageScale[ i ] );
831 }
832
833 savefile->WriteBool( use_combat_bbox );
834 head.Save( savefile );
835
836 savefile->WriteInt( copyJoints.Num() );
837 for( i = 0; i < copyJoints.Num(); i++ ) {
838 savefile->WriteInt( copyJoints[i].mod );
839 savefile->WriteJoint( copyJoints[i].from );
840 savefile->WriteJoint( copyJoints[i].to );
841 }
842
843 savefile->WriteJoint( leftEyeJoint );
844 savefile->WriteJoint( rightEyeJoint );
845 savefile->WriteJoint( soundJoint );
846
847 walkIK.Save( savefile );
848
849 savefile->WriteString( animPrefix );
850 savefile->WriteString( painAnim );
851
852 savefile->WriteInt( blink_anim );
853 savefile->WriteInt( blink_time );
854 savefile->WriteInt( blink_min );
855 savefile->WriteInt( blink_max );
856
857 // script variables
858 savefile->WriteObject( scriptThread );
859
860 savefile->WriteString( waitState );
861
862 headAnim.Save( savefile );
863 torsoAnim.Save( savefile );
864 legsAnim.Save( savefile );
865
866 savefile->WriteBool( allowPain );
867 savefile->WriteBool( allowEyeFocus );
868
869 savefile->WriteInt( painTime );
870
871 savefile->WriteInt( attachments.Num() );
872 for ( i = 0; i < attachments.Num(); i++ ) {
873 attachments[i].ent.Save( savefile );
874 savefile->WriteInt( attachments[i].channel );
875 }
876
877 savefile->WriteBool( finalBoss );
878
879 idToken token;
880
881 //FIXME: this is unneccesary
882 if ( state ) {
883 idLexer src( state->Name(), idStr::Length( state->Name() ), "idAI::Save" );
884
885 src.ReadTokenOnLine( &token );
886 src.ExpectTokenString( "::" );
887 src.ReadTokenOnLine( &token );
888
889 savefile->WriteString( token );
890 } else {
891 savefile->WriteString( "" );
892 }
893
894 if ( idealState ) {
895 idLexer src( idealState->Name(), idStr::Length( idealState->Name() ), "idAI::Save" );
896
897 src.ReadTokenOnLine( &token );
898 src.ExpectTokenString( "::" );
899 src.ReadTokenOnLine( &token );
900
901 savefile->WriteString( token );
902 } else {
903 savefile->WriteString( "" );
904 }
905
906 #ifdef _D3XP
907 savefile->WriteInt(damageCap);
908 #endif
909
910 }
911
912 /*
913 ================
914 idActor::Restore
915
916 unarchives object from save game file
917 ================
918 */
Restore(idRestoreGame * savefile)919 void idActor::Restore( idRestoreGame *savefile ) {
920 int i, num;
921 idActor *ent;
922
923 savefile->ReadInt( team );
924 savefile->ReadInt( rank );
925 savefile->ReadMat3( viewAxis );
926
927 savefile->ReadInt( num );
928 for ( i = 0; i < num; i++ ) {
929 savefile->ReadObject( reinterpret_cast<idClass *&>( ent ) );
930 assert( ent );
931 if ( ent ) {
932 ent->enemyNode.AddToEnd( enemyList );
933 }
934 }
935
936 savefile->ReadFloat( fovDot );
937 savefile->ReadVec3( eyeOffset );
938 savefile->ReadVec3( modelOffset );
939 savefile->ReadAngles( deltaViewAngles );
940
941 savefile->ReadInt( pain_debounce_time );
942 savefile->ReadInt( pain_delay );
943 savefile->ReadInt( pain_threshold );
944
945 savefile->ReadInt( num );
946 damageGroups.SetGranularity( 1 );
947 damageGroups.SetNum( num );
948 for( i = 0; i < num; i++ ) {
949 savefile->ReadString( damageGroups[ i ] );
950 }
951
952 savefile->ReadInt( num );
953 damageScale.SetNum( num );
954 for( i = 0; i < num; i++ ) {
955 savefile->ReadFloat( damageScale[ i ] );
956 }
957
958 savefile->ReadBool( use_combat_bbox );
959 head.Restore( savefile );
960
961 savefile->ReadInt( num );
962 copyJoints.SetNum( num );
963 for( i = 0; i < num; i++ ) {
964 int val;
965 savefile->ReadInt( val );
966 copyJoints[i].mod = static_cast<jointModTransform_t>( val );
967 savefile->ReadJoint( copyJoints[i].from );
968 savefile->ReadJoint( copyJoints[i].to );
969 }
970
971 savefile->ReadJoint( leftEyeJoint );
972 savefile->ReadJoint( rightEyeJoint );
973 savefile->ReadJoint( soundJoint );
974
975 walkIK.Restore( savefile );
976
977 savefile->ReadString( animPrefix );
978 savefile->ReadString( painAnim );
979
980 savefile->ReadInt( blink_anim );
981 savefile->ReadInt( blink_time );
982 savefile->ReadInt( blink_min );
983 savefile->ReadInt( blink_max );
984
985 savefile->ReadObject( reinterpret_cast<idClass *&>( scriptThread ) );
986
987 savefile->ReadString( waitState );
988
989 headAnim.Restore( savefile );
990 torsoAnim.Restore( savefile );
991 legsAnim.Restore( savefile );
992
993 savefile->ReadBool( allowPain );
994 savefile->ReadBool( allowEyeFocus );
995
996 savefile->ReadInt( painTime );
997
998 savefile->ReadInt( num );
999 for ( i = 0; i < num; i++ ) {
1000 idAttachInfo &attach = attachments.Alloc();
1001 attach.ent.Restore( savefile );
1002 savefile->ReadInt( attach.channel );
1003 }
1004
1005 savefile->ReadBool( finalBoss );
1006
1007 idStr statename;
1008
1009 savefile->ReadString( statename );
1010 if ( statename.Length() > 0 ) {
1011 state = GetScriptFunction( statename );
1012 }
1013
1014 savefile->ReadString( statename );
1015 if ( statename.Length() > 0 ) {
1016 idealState = GetScriptFunction( statename );
1017 }
1018
1019 #ifdef _D3XP
1020 savefile->ReadInt(damageCap);
1021 #endif
1022 }
1023
1024 /*
1025 ================
1026 idActor::Hide
1027 ================
1028 */
Hide(void)1029 void idActor::Hide( void ) {
1030 idEntity *ent;
1031 idEntity *next;
1032
1033 idAFEntity_Base::Hide();
1034 if ( head.GetEntity() ) {
1035 head.GetEntity()->Hide();
1036 }
1037
1038 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1039 next = ent->GetNextTeamEntity();
1040 if ( ent->GetBindMaster() == this ) {
1041 ent->Hide();
1042 if ( ent->IsType( idLight::Type ) ) {
1043 static_cast<idLight *>( ent )->Off();
1044 }
1045 }
1046 }
1047 UnlinkCombat();
1048 }
1049
1050 /*
1051 ================
1052 idActor::Show
1053 ================
1054 */
Show(void)1055 void idActor::Show( void ) {
1056 idEntity *ent;
1057 idEntity *next;
1058
1059 idAFEntity_Base::Show();
1060 if ( head.GetEntity() ) {
1061 head.GetEntity()->Show();
1062 }
1063 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1064 next = ent->GetNextTeamEntity();
1065 if ( ent->GetBindMaster() == this ) {
1066 ent->Show();
1067 if ( ent->IsType( idLight::Type ) ) {
1068 #ifdef _D3XP
1069 if(!spawnArgs.GetBool("lights_off", "0")) {
1070 static_cast<idLight *>( ent )->On();
1071 }
1072 #endif
1073
1074
1075 }
1076 }
1077 }
1078 LinkCombat();
1079 }
1080
1081 /*
1082 ==============
1083 idActor::GetDefaultSurfaceType
1084 ==============
1085 */
GetDefaultSurfaceType(void) const1086 int idActor::GetDefaultSurfaceType( void ) const {
1087 return SURFTYPE_FLESH;
1088 }
1089
1090 /*
1091 ================
1092 idActor::ProjectOverlay
1093 ================
1094 */
ProjectOverlay(const idVec3 & origin,const idVec3 & dir,float size,const char * material)1095 void idActor::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1096 idEntity *ent;
1097 idEntity *next;
1098
1099 idEntity::ProjectOverlay( origin, dir, size, material );
1100
1101 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1102 next = ent->GetNextTeamEntity();
1103 if ( ent->GetBindMaster() == this ) {
1104 if ( ent->fl.takedamage && ent->spawnArgs.GetBool( "bleed" ) ) {
1105 ent->ProjectOverlay( origin, dir, size, material );
1106 }
1107 }
1108 }
1109 }
1110
1111 /*
1112 ================
1113 idActor::LoadAF
1114 ================
1115 */
LoadAF(void)1116 bool idActor::LoadAF( void ) {
1117 idStr fileName;
1118
1119 if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) || !fileName.Length() ) {
1120 return false;
1121 }
1122 af.SetAnimator( GetAnimator() );
1123 return af.Load( this, fileName );
1124 }
1125
1126 /*
1127 =====================
1128 idActor::SetupBody
1129 =====================
1130 */
SetupBody(void)1131 void idActor::SetupBody( void ) {
1132 const char *jointname;
1133
1134 animator.ClearAllAnims( gameLocal.time, 0 );
1135 animator.ClearAllJoints();
1136
1137 idEntity *headEnt = head.GetEntity();
1138 if ( headEnt ) {
1139 jointname = spawnArgs.GetString( "bone_leftEye" );
1140 leftEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1141
1142 jointname = spawnArgs.GetString( "bone_rightEye" );
1143 rightEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1144
1145 // set up the eye height. check if it's specified in the def.
1146 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1147 // if not in the def, then try to base it off the idle animation
1148 int anim = headEnt->GetAnimator()->GetAnim( "idle" );
1149 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1150 idVec3 pos;
1151 idMat3 axis;
1152 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1153 headEnt->GetAnimator()->GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1154 headEnt->GetAnimator()->ClearAllAnims( gameLocal.time, 0 );
1155 headEnt->GetAnimator()->ForceUpdate();
1156 pos += headEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
1157 eyeOffset = pos + modelOffset;
1158 } else {
1159 // just base it off the bounding box size
1160 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1161 }
1162 }
1163 headAnim.Init( this, headEnt->GetAnimator(), ANIMCHANNEL_ALL );
1164 } else {
1165 jointname = spawnArgs.GetString( "bone_leftEye" );
1166 leftEyeJoint = animator.GetJointHandle( jointname );
1167
1168 jointname = spawnArgs.GetString( "bone_rightEye" );
1169 rightEyeJoint = animator.GetJointHandle( jointname );
1170
1171 // set up the eye height. check if it's specified in the def.
1172 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1173 // if not in the def, then try to base it off the idle animation
1174 int anim = animator.GetAnim( "idle" );
1175 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1176 idVec3 pos;
1177 idMat3 axis;
1178 animator.PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1179 animator.GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1180 animator.ClearAllAnims( gameLocal.time, 0 );
1181 animator.ForceUpdate();
1182 eyeOffset = pos + modelOffset;
1183 } else {
1184 // just base it off the bounding box size
1185 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1186 }
1187 }
1188 headAnim.Init( this, &animator, ANIMCHANNEL_HEAD );
1189 }
1190
1191 waitState = "";
1192
1193 torsoAnim.Init( this, &animator, ANIMCHANNEL_TORSO );
1194 legsAnim.Init( this, &animator, ANIMCHANNEL_LEGS );
1195 }
1196
1197 /*
1198 =====================
1199 idActor::CheckBlink
1200 =====================
1201 */
CheckBlink(void)1202 void idActor::CheckBlink( void ) {
1203 // check if it's time to blink
1204 if ( !blink_anim || ( health <= 0 ) || !allowEyeFocus || ( blink_time > gameLocal.time ) ) {
1205 return;
1206 }
1207
1208 idEntity *headEnt = head.GetEntity();
1209 if ( headEnt ) {
1210 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1211 } else {
1212 animator.PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1213 }
1214
1215 // set the next blink time
1216 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
1217 }
1218
1219 /*
1220 ================
1221 idActor::GetPhysicsToVisualTransform
1222 ================
1223 */
GetPhysicsToVisualTransform(idVec3 & origin,idMat3 & axis)1224 bool idActor::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
1225 if ( af.IsActive() ) {
1226 af.GetPhysicsToVisualTransform( origin, axis );
1227 return true;
1228 }
1229 origin = modelOffset;
1230 axis = viewAxis;
1231 return true;
1232 }
1233
1234 /*
1235 ================
1236 idActor::GetPhysicsToSoundTransform
1237 ================
1238 */
GetPhysicsToSoundTransform(idVec3 & origin,idMat3 & axis)1239 bool idActor::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
1240 if ( soundJoint != INVALID_JOINT ) {
1241 animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
1242 origin += modelOffset;
1243 axis = viewAxis;
1244 } else {
1245 origin = GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1246 axis.Identity();
1247 }
1248 return true;
1249 }
1250
1251 /***********************************************************************
1252
1253 script state management
1254
1255 ***********************************************************************/
1256
1257 /*
1258 ================
1259 idActor::ShutdownThreads
1260 ================
1261 */
ShutdownThreads(void)1262 void idActor::ShutdownThreads( void ) {
1263 headAnim.Shutdown();
1264 torsoAnim.Shutdown();
1265 legsAnim.Shutdown();
1266
1267 if ( scriptThread ) {
1268 scriptThread->EndThread();
1269 scriptThread->PostEventMS( &EV_Remove, 0 );
1270 delete scriptThread;
1271 scriptThread = NULL;
1272 }
1273 }
1274
1275 /*
1276 ================
1277 idActor::ShouldConstructScriptObjectAtSpawn
1278
1279 Called during idEntity::Spawn to see if it should construct the script object or not.
1280 Overridden by subclasses that need to spawn the script object themselves.
1281 ================
1282 */
ShouldConstructScriptObjectAtSpawn(void) const1283 bool idActor::ShouldConstructScriptObjectAtSpawn( void ) const {
1284 return false;
1285 }
1286
1287 /*
1288 ================
1289 idActor::ConstructScriptObject
1290
1291 Called during idEntity::Spawn. Calls the constructor on the script object.
1292 Can be overridden by subclasses when a thread doesn't need to be allocated.
1293 ================
1294 */
ConstructScriptObject(void)1295 idThread *idActor::ConstructScriptObject( void ) {
1296 const function_t *constructor;
1297
1298 // make sure we have a scriptObject
1299 if ( !scriptObject.HasObject() ) {
1300 gameLocal.Error( "No scriptobject set on '%s'. Check the '%s' entityDef.", name.c_str(), GetEntityDefName() );
1301 }
1302
1303 if ( !scriptThread ) {
1304 // create script thread
1305 scriptThread = new idThread();
1306 scriptThread->ManualDelete();
1307 scriptThread->ManualControl();
1308 scriptThread->SetThreadName( name.c_str() );
1309 } else {
1310 scriptThread->EndThread();
1311 }
1312
1313 // call script object's constructor
1314 constructor = scriptObject.GetConstructor();
1315 if ( !constructor ) {
1316 gameLocal.Error( "Missing constructor on '%s' for entity '%s'", scriptObject.GetTypeName(), name.c_str() );
1317 }
1318
1319 // init the script object's data
1320 scriptObject.ClearObject();
1321
1322 // just set the current function on the script. we'll execute in the subclasses.
1323 scriptThread->CallFunction( this, constructor, true );
1324
1325 return scriptThread;
1326 }
1327
1328 /*
1329 =====================
1330 idActor::GetScriptFunction
1331 =====================
1332 */
GetScriptFunction(const char * funcname)1333 const function_t *idActor::GetScriptFunction( const char *funcname ) {
1334 const function_t *func;
1335
1336 func = scriptObject.GetFunction( funcname );
1337 if ( !func ) {
1338 scriptThread->Error( "Unknown function '%s' in '%s'", funcname, scriptObject.GetTypeName() );
1339 }
1340
1341 return func;
1342 }
1343
1344 /*
1345 =====================
1346 idActor::SetState
1347 =====================
1348 */
SetState(const function_t * newState)1349 void idActor::SetState( const function_t *newState ) {
1350 if ( !newState ) {
1351 gameLocal.Error( "idActor::SetState: Null state" );
1352 }
1353
1354 if ( ai_debugScript.GetInteger() == entityNumber ) {
1355 gameLocal.Printf( "%d: %s: State: %s\n", gameLocal.time, name.c_str(), newState->Name() );
1356 }
1357
1358 state = newState;
1359 idealState = state;
1360 scriptThread->CallFunction( this, state, true );
1361 }
1362
1363 /*
1364 =====================
1365 idActor::SetState
1366 =====================
1367 */
SetState(const char * statename)1368 void idActor::SetState( const char *statename ) {
1369 const function_t *newState;
1370
1371 newState = GetScriptFunction( statename );
1372 SetState( newState );
1373 }
1374
1375 /*
1376 =====================
1377 idActor::UpdateScript
1378 =====================
1379 */
UpdateScript(void)1380 void idActor::UpdateScript( void ) {
1381 int i;
1382
1383 if ( ai_debugScript.GetInteger() == entityNumber ) {
1384 scriptThread->EnableDebugInfo();
1385 } else {
1386 scriptThread->DisableDebugInfo();
1387 }
1388
1389 // a series of state changes can happen in a single frame.
1390 // this loop limits them in case we've entered an infinite loop.
1391 for( i = 0; i < 20; i++ ) {
1392 if ( idealState != state ) {
1393 SetState( idealState );
1394 }
1395
1396 // don't call script until it's done waiting
1397 if ( scriptThread->IsWaiting() ) {
1398 break;
1399 }
1400
1401 scriptThread->Execute();
1402 if ( idealState == state ) {
1403 break;
1404 }
1405 }
1406
1407 if ( i == 20 ) {
1408 scriptThread->Warning( "idActor::UpdateScript: exited loop to prevent lockup" );
1409 }
1410 }
1411
1412 /***********************************************************************
1413
1414 vision
1415
1416 ***********************************************************************/
1417
1418 /*
1419 =====================
1420 idActor::setFov
1421 =====================
1422 */
SetFOV(float fov)1423 void idActor::SetFOV( float fov ) {
1424 fovDot = (float)cos( DEG2RAD( fov * 0.5f ) );
1425 }
1426
1427 /*
1428 =====================
1429 idActor::SetEyeHeight
1430 =====================
1431 */
SetEyeHeight(float height)1432 void idActor::SetEyeHeight( float height ) {
1433 eyeOffset.z = height;
1434 }
1435
1436 /*
1437 =====================
1438 idActor::EyeHeight
1439 =====================
1440 */
EyeHeight(void) const1441 float idActor::EyeHeight( void ) const {
1442 return eyeOffset.z;
1443 }
1444
1445 /*
1446 =====================
1447 idActor::EyeOffset
1448 =====================
1449 */
EyeOffset(void) const1450 idVec3 idActor::EyeOffset( void ) const {
1451 return GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1452 }
1453
1454 /*
1455 =====================
1456 idActor::GetEyePosition
1457 =====================
1458 */
GetEyePosition(void) const1459 idVec3 idActor::GetEyePosition( void ) const {
1460 return GetPhysics()->GetOrigin() + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
1461 }
1462
1463 /*
1464 =====================
1465 idActor::GetViewPos
1466 =====================
1467 */
GetViewPos(idVec3 & origin,idMat3 & axis) const1468 void idActor::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
1469 origin = GetEyePosition();
1470 axis = viewAxis;
1471 }
1472
1473 /*
1474 =====================
1475 idActor::CheckFOV
1476 =====================
1477 */
CheckFOV(const idVec3 & pos) const1478 bool idActor::CheckFOV( const idVec3 &pos ) const {
1479 if ( fovDot == 1.0f ) {
1480 return true;
1481 }
1482
1483 float dot;
1484 idVec3 delta;
1485
1486 delta = pos - GetEyePosition();
1487
1488 // get our gravity normal
1489 const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
1490
1491 // infinite vertical vision, so project it onto our orientation plane
1492 delta -= gravityDir * ( gravityDir * delta );
1493
1494 delta.Normalize();
1495 dot = viewAxis[ 0 ] * delta;
1496
1497 return ( dot >= fovDot );
1498 }
1499
1500 /*
1501 =====================
1502 idActor::CanSee
1503 =====================
1504 */
CanSee(idEntity * ent,bool useFov) const1505 bool idActor::CanSee( idEntity *ent, bool useFov ) const {
1506 trace_t tr;
1507 idVec3 eye;
1508 idVec3 toPos;
1509
1510 if ( ent->IsHidden() ) {
1511 return false;
1512 }
1513
1514 if ( ent->IsType( idActor::Type ) ) {
1515 toPos = ( ( idActor * )ent )->GetEyePosition();
1516 } else {
1517 toPos = ent->GetPhysics()->GetOrigin();
1518 }
1519
1520 if ( useFov && !CheckFOV( toPos ) ) {
1521 return false;
1522 }
1523
1524 eye = GetEyePosition();
1525
1526 gameLocal.clip.TracePoint( tr, eye, toPos, MASK_OPAQUE, this );
1527 if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == ent ) ) {
1528 return true;
1529 }
1530
1531 return false;
1532 }
1533
1534 /*
1535 =====================
1536 idActor::PointVisible
1537 =====================
1538 */
PointVisible(const idVec3 & point) const1539 bool idActor::PointVisible( const idVec3 &point ) const {
1540 trace_t results;
1541 idVec3 start, end;
1542
1543 start = GetEyePosition();
1544 end = point;
1545 end[2] += 1.0f;
1546
1547 gameLocal.clip.TracePoint( results, start, end, MASK_OPAQUE, this );
1548 return ( results.fraction >= 1.0f );
1549 }
1550
1551 /*
1552 =====================
1553 idActor::GetAIAimTargets
1554
1555 Returns positions for the AI to aim at.
1556 =====================
1557 */
GetAIAimTargets(const idVec3 & lastSightPos,idVec3 & headPos,idVec3 & chestPos)1558 void idActor::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
1559 headPos = lastSightPos + EyeOffset();
1560 chestPos = ( headPos + lastSightPos + GetPhysics()->GetBounds().GetCenter() ) * 0.5f;
1561 }
1562
1563 /*
1564 =====================
1565 idActor::GetRenderView
1566 =====================
1567 */
GetRenderView()1568 renderView_t *idActor::GetRenderView() {
1569 renderView_t *rv = idEntity::GetRenderView();
1570 rv->viewaxis = viewAxis;
1571 rv->vieworg = GetEyePosition();
1572 return rv;
1573 }
1574
1575 /***********************************************************************
1576
1577 Model/Ragdoll
1578
1579 ***********************************************************************/
1580
1581 /*
1582 ================
1583 idActor::SetCombatModel
1584 ================
1585 */
SetCombatModel(void)1586 void idActor::SetCombatModel( void ) {
1587 idAFAttachment *headEnt;
1588
1589 if ( !use_combat_bbox ) {
1590 if ( combatModel ) {
1591 combatModel->Unlink();
1592 combatModel->LoadModel( modelDefHandle );
1593 } else {
1594 combatModel = new idClipModel( modelDefHandle );
1595 }
1596
1597 headEnt = head.GetEntity();
1598 if ( headEnt ) {
1599 headEnt->SetCombatModel();
1600 }
1601 }
1602 }
1603
1604 /*
1605 ================
1606 idActor::GetCombatModel
1607 ================
1608 */
GetCombatModel(void) const1609 idClipModel *idActor::GetCombatModel( void ) const {
1610 return combatModel;
1611 }
1612
1613 /*
1614 ================
1615 idActor::LinkCombat
1616 ================
1617 */
LinkCombat(void)1618 void idActor::LinkCombat( void ) {
1619 idAFAttachment *headEnt;
1620
1621 if ( fl.hidden || use_combat_bbox ) {
1622 return;
1623 }
1624
1625 if ( combatModel ) {
1626 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1627 }
1628 headEnt = head.GetEntity();
1629 if ( headEnt ) {
1630 headEnt->LinkCombat();
1631 }
1632 }
1633
1634 /*
1635 ================
1636 idActor::UnlinkCombat
1637 ================
1638 */
UnlinkCombat(void)1639 void idActor::UnlinkCombat( void ) {
1640 idAFAttachment *headEnt;
1641
1642 if ( combatModel ) {
1643 combatModel->Unlink();
1644 }
1645 headEnt = head.GetEntity();
1646 if ( headEnt ) {
1647 headEnt->UnlinkCombat();
1648 }
1649 }
1650
1651 /*
1652 ================
1653 idActor::StartRagdoll
1654 ================
1655 */
StartRagdoll(void)1656 bool idActor::StartRagdoll( void ) {
1657 float slomoStart, slomoEnd;
1658 float jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd;
1659 float contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd;
1660
1661 // if no AF loaded
1662 if ( !af.IsLoaded() ) {
1663 return false;
1664 }
1665
1666 // if the AF is already active
1667 if ( af.IsActive() ) {
1668 return true;
1669 }
1670
1671 // disable the monster bounding box
1672 GetPhysics()->DisableClip();
1673
1674 // start using the AF
1675 af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
1676
1677 slomoStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoStart", "-1.6" );
1678 slomoEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoEnd", "0.8" );
1679
1680 // do the first part of the death in slow motion
1681 af.GetPhysics()->SetTimeScaleRamp( slomoStart, slomoEnd );
1682
1683 jointFrictionDent = spawnArgs.GetFloat( "ragdoll_jointFrictionDent", "0.1" );
1684 jointFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionStart", "0.2" );
1685 jointFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionEnd", "1.2" );
1686
1687 // set joint friction dent
1688 af.GetPhysics()->SetJointFrictionDent( jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd );
1689
1690 contactFrictionDent = spawnArgs.GetFloat( "ragdoll_contactFrictionDent", "0.1" );
1691 contactFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionStart", "1.0" );
1692 contactFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionEnd", "2.0" );
1693
1694 // set contact friction dent
1695 af.GetPhysics()->SetContactFrictionDent( contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd );
1696
1697 // drop any items the actor is holding
1698 idMoveableItem::DropItems( this, "death", NULL );
1699
1700 // drop any articulated figures the actor is holding
1701 idAFEntity_Base::DropAFs( this, "death", NULL );
1702
1703 RemoveAttachments();
1704
1705 return true;
1706 }
1707
1708 /*
1709 ================
1710 idActor::StopRagdoll
1711 ================
1712 */
StopRagdoll(void)1713 void idActor::StopRagdoll( void ) {
1714 if ( af.IsActive() ) {
1715 af.Stop();
1716 }
1717 }
1718
1719 /*
1720 ================
1721 idActor::UpdateAnimationControllers
1722 ================
1723 */
UpdateAnimationControllers(void)1724 bool idActor::UpdateAnimationControllers( void ) {
1725
1726 if ( af.IsActive() ) {
1727 return idAFEntity_Base::UpdateAnimationControllers();
1728 } else {
1729 animator.ClearAFPose();
1730 }
1731
1732 if ( walkIK.IsInitialized() ) {
1733 walkIK.Evaluate();
1734 return true;
1735 }
1736
1737 return false;
1738 }
1739
1740 /*
1741 ================
1742 idActor::RemoveAttachments
1743 ================
1744 */
RemoveAttachments(void)1745 void idActor::RemoveAttachments( void ) {
1746 int i;
1747 idEntity *ent;
1748
1749 // remove any attached entities
1750 for( i = 0; i < attachments.Num(); i++ ) {
1751 ent = attachments[ i ].ent.GetEntity();
1752 if ( ent && ent->spawnArgs.GetBool( "remove" ) ) {
1753 ent->PostEventMS( &EV_Remove, 0 );
1754 }
1755 }
1756 }
1757
1758 /*
1759 ================
1760 idActor::Attach
1761 ================
1762 */
Attach(idEntity * ent)1763 void idActor::Attach( idEntity *ent ) {
1764 idVec3 origin;
1765 idMat3 axis;
1766 jointHandle_t joint;
1767 idStr jointName;
1768 idAttachInfo &attach = attachments.Alloc();
1769 idAngles angleOffset;
1770 idVec3 originOffset;
1771
1772 jointName = ent->spawnArgs.GetString( "joint" );
1773 joint = animator.GetJointHandle( jointName );
1774 if ( joint == INVALID_JOINT ) {
1775 gameLocal.Error( "Joint '%s' not found for attaching '%s' on '%s'", jointName.c_str(), ent->GetClassname(), name.c_str() );
1776 }
1777
1778 angleOffset = ent->spawnArgs.GetAngles( "angles" );
1779 originOffset = ent->spawnArgs.GetVector( "origin" );
1780
1781 attach.channel = animator.GetChannelForJoint( joint );
1782 GetJointWorldTransform( joint, gameLocal.time, origin, axis );
1783 attach.ent = ent;
1784
1785 ent->SetOrigin( origin + originOffset * renderEntity.axis );
1786 idMat3 rotate = angleOffset.ToMat3();
1787 idMat3 newAxis = rotate * axis;
1788 ent->SetAxis( newAxis );
1789 ent->BindToJoint( this, joint, true );
1790 ent->cinematic = cinematic;
1791 }
1792
1793 /*
1794 ================
1795 idActor::Teleport
1796 ================
1797 */
Teleport(const idVec3 & origin,const idAngles & angles,idEntity * destination)1798 void idActor::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
1799 GetPhysics()->SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
1800 GetPhysics()->SetLinearVelocity( vec3_origin );
1801
1802 viewAxis = angles.ToMat3();
1803
1804 UpdateVisuals();
1805
1806 if ( !IsHidden() ) {
1807 // kill anything at the new position
1808 gameLocal.KillBox( this );
1809 }
1810 }
1811
1812 /*
1813 ================
1814 idActor::GetDeltaViewAngles
1815 ================
1816 */
GetDeltaViewAngles(void) const1817 const idAngles &idActor::GetDeltaViewAngles( void ) const {
1818 return deltaViewAngles;
1819 }
1820
1821 /*
1822 ================
1823 idActor::SetDeltaViewAngles
1824 ================
1825 */
SetDeltaViewAngles(const idAngles & delta)1826 void idActor::SetDeltaViewAngles( const idAngles &delta ) {
1827 deltaViewAngles = delta;
1828 }
1829
1830 /*
1831 ================
1832 idActor::HasEnemies
1833 ================
1834 */
HasEnemies(void) const1835 bool idActor::HasEnemies( void ) const {
1836 idActor *ent;
1837
1838 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1839 if ( !ent->fl.hidden ) {
1840 return true;
1841 }
1842 }
1843
1844 return false;
1845 }
1846
1847 /*
1848 ================
1849 idActor::ClosestEnemyToPoint
1850 ================
1851 */
ClosestEnemyToPoint(const idVec3 & pos)1852 idActor *idActor::ClosestEnemyToPoint( const idVec3 &pos ) {
1853 idActor *ent;
1854 idActor *bestEnt;
1855 float bestDistSquared;
1856 float distSquared;
1857 idVec3 delta;
1858
1859 bestDistSquared = idMath::INFINITY;
1860 bestEnt = NULL;
1861 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1862 if ( ent->fl.hidden ) {
1863 continue;
1864 }
1865 delta = ent->GetPhysics()->GetOrigin() - pos;
1866 distSquared = delta.LengthSqr();
1867 if ( distSquared < bestDistSquared ) {
1868 bestEnt = ent;
1869 bestDistSquared = distSquared;
1870 }
1871 }
1872
1873 return bestEnt;
1874 }
1875
1876 /*
1877 ================
1878 idActor::EnemyWithMostHealth
1879 ================
1880 */
EnemyWithMostHealth()1881 idActor *idActor::EnemyWithMostHealth() {
1882 idActor *ent;
1883 idActor *bestEnt;
1884
1885 int most = -9999;
1886 bestEnt = NULL;
1887 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1888 if ( !ent->fl.hidden && ( ent->health > most ) ) {
1889 bestEnt = ent;
1890 most = ent->health;
1891 }
1892 }
1893 return bestEnt;
1894 }
1895
1896 /*
1897 ================
1898 idActor::OnLadder
1899 ================
1900 */
OnLadder(void) const1901 bool idActor::OnLadder( void ) const {
1902 return false;
1903 }
1904
1905 /*
1906 ==============
1907 idActor::GetAASLocation
1908 ==============
1909 */
GetAASLocation(idAAS * aas,idVec3 & pos,int & areaNum) const1910 void idActor::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
1911 idVec3 size;
1912 idBounds bounds;
1913
1914 GetFloorPos( 64.0f, pos );
1915 if ( !aas ) {
1916 areaNum = 0;
1917 return;
1918 }
1919
1920 size = aas->GetSettings()->boundingBoxes[0][1];
1921 bounds[0] = -size;
1922 size.z = 32.0f;
1923 bounds[1] = size;
1924
1925 areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK );
1926 if ( areaNum ) {
1927 aas->PushPointIntoAreaNum( areaNum, pos );
1928 }
1929 }
1930
1931 /***********************************************************************
1932
1933 animation state
1934
1935 ***********************************************************************/
1936
1937 /*
1938 =====================
1939 idActor::SetAnimState
1940 =====================
1941 */
SetAnimState(int channel,const char * statename,int blendFrames)1942 void idActor::SetAnimState( int channel, const char *statename, int blendFrames ) {
1943 const function_t *func;
1944
1945 func = scriptObject.GetFunction( statename );
1946 if ( !func ) {
1947 assert( 0 );
1948 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
1949 }
1950
1951 switch( channel ) {
1952 case ANIMCHANNEL_HEAD :
1953 headAnim.SetState( statename, blendFrames );
1954 allowEyeFocus = true;
1955 break;
1956
1957 case ANIMCHANNEL_TORSO :
1958 torsoAnim.SetState( statename, blendFrames );
1959 legsAnim.Enable( blendFrames );
1960 allowPain = true;
1961 allowEyeFocus = true;
1962 break;
1963
1964 case ANIMCHANNEL_LEGS :
1965 legsAnim.SetState( statename, blendFrames );
1966 torsoAnim.Enable( blendFrames );
1967 allowPain = true;
1968 allowEyeFocus = true;
1969 break;
1970
1971 default:
1972 gameLocal.Error( "idActor::SetAnimState: Unknown anim group" );
1973 break;
1974 }
1975 }
1976
1977 /*
1978 =====================
1979 idActor::GetAnimState
1980 =====================
1981 */
GetAnimState(int channel) const1982 const char *idActor::GetAnimState( int channel ) const {
1983 switch( channel ) {
1984 case ANIMCHANNEL_HEAD :
1985 return headAnim.state;
1986 break;
1987
1988 case ANIMCHANNEL_TORSO :
1989 return torsoAnim.state;
1990 break;
1991
1992 case ANIMCHANNEL_LEGS :
1993 return legsAnim.state;
1994 break;
1995
1996 default:
1997 gameLocal.Error( "idActor::GetAnimState: Unknown anim group" );
1998 return NULL;
1999 break;
2000 }
2001 }
2002
2003 /*
2004 =====================
2005 idActor::InAnimState
2006 =====================
2007 */
InAnimState(int channel,const char * statename) const2008 bool idActor::InAnimState( int channel, const char *statename ) const {
2009 switch( channel ) {
2010 case ANIMCHANNEL_HEAD :
2011 if ( headAnim.state == statename ) {
2012 return true;
2013 }
2014 break;
2015
2016 case ANIMCHANNEL_TORSO :
2017 if ( torsoAnim.state == statename ) {
2018 return true;
2019 }
2020 break;
2021
2022 case ANIMCHANNEL_LEGS :
2023 if ( legsAnim.state == statename ) {
2024 return true;
2025 }
2026 break;
2027
2028 default:
2029 gameLocal.Error( "idActor::InAnimState: Unknown anim group" );
2030 break;
2031 }
2032
2033 return false;
2034 }
2035
2036 /*
2037 =====================
2038 idActor::WaitState
2039 =====================
2040 */
WaitState(void) const2041 const char *idActor::WaitState( void ) const {
2042 if ( waitState.Length() ) {
2043 return waitState;
2044 } else {
2045 return NULL;
2046 }
2047 }
2048
2049 /*
2050 =====================
2051 idActor::SetWaitState
2052 =====================
2053 */
SetWaitState(const char * _waitstate)2054 void idActor::SetWaitState( const char *_waitstate ) {
2055 waitState = _waitstate;
2056 }
2057
2058 /*
2059 =====================
2060 idActor::UpdateAnimState
2061 =====================
2062 */
UpdateAnimState(void)2063 void idActor::UpdateAnimState( void ) {
2064 headAnim.UpdateState();
2065 torsoAnim.UpdateState();
2066 legsAnim.UpdateState();
2067 }
2068
2069 /*
2070 =====================
2071 idActor::GetAnim
2072 =====================
2073 */
GetAnim(int channel,const char * animname)2074 int idActor::GetAnim( int channel, const char *animname ) {
2075 int anim;
2076 const char *temp;
2077 idAnimator *animatorPtr;
2078
2079 if ( channel == ANIMCHANNEL_HEAD ) {
2080 if ( !head.GetEntity() ) {
2081 return 0;
2082 }
2083 animatorPtr = head.GetEntity()->GetAnimator();
2084 } else {
2085 animatorPtr = &animator;
2086 }
2087
2088 if ( animPrefix.Length() ) {
2089 temp = va( "%s_%s", animPrefix.c_str(), animname );
2090 anim = animatorPtr->GetAnim( temp );
2091 if ( anim ) {
2092 return anim;
2093 }
2094 }
2095
2096 anim = animatorPtr->GetAnim( animname );
2097
2098 return anim;
2099 }
2100
2101 /*
2102 ===============
2103 idActor::SyncAnimChannels
2104 ===============
2105 */
SyncAnimChannels(int channel,int syncToChannel,int blendFrames)2106 void idActor::SyncAnimChannels( int channel, int syncToChannel, int blendFrames ) {
2107 idAnimator *headAnimator;
2108 idAFAttachment *headEnt;
2109 int anim;
2110 idAnimBlend *syncAnim;
2111 int starttime;
2112 int blendTime;
2113 int cycle;
2114
2115 blendTime = FRAME2MS( blendFrames );
2116 if ( channel == ANIMCHANNEL_HEAD ) {
2117 headEnt = head.GetEntity();
2118 if ( headEnt ) {
2119 headAnimator = headEnt->GetAnimator();
2120 syncAnim = animator.CurrentAnim( syncToChannel );
2121 if ( syncAnim ) {
2122 anim = headAnimator->GetAnim( syncAnim->AnimFullName() );
2123 if ( !anim ) {
2124 anim = headAnimator->GetAnim( syncAnim->AnimName() );
2125 }
2126 if ( anim ) {
2127 cycle = animator.CurrentAnim( syncToChannel )->GetCycleCount();
2128 starttime = animator.CurrentAnim( syncToChannel )->GetStartTime();
2129 headAnimator->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, blendTime );
2130 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
2131 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetStartTime( starttime );
2132 } else {
2133 headEnt->PlayIdleAnim( blendTime );
2134 }
2135 }
2136 }
2137 } else if ( syncToChannel == ANIMCHANNEL_HEAD ) {
2138 headEnt = head.GetEntity();
2139 if ( headEnt ) {
2140 headAnimator = headEnt->GetAnimator();
2141 syncAnim = headAnimator->CurrentAnim( ANIMCHANNEL_ALL );
2142 if ( syncAnim ) {
2143 anim = GetAnim( channel, syncAnim->AnimFullName() );
2144 if ( !anim ) {
2145 anim = GetAnim( channel, syncAnim->AnimName() );
2146 }
2147 if ( anim ) {
2148 cycle = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetCycleCount();
2149 starttime = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetStartTime();
2150 animator.PlayAnim( channel, anim, gameLocal.time, blendTime );
2151 animator.CurrentAnim( channel )->SetCycleCount( cycle );
2152 animator.CurrentAnim( channel )->SetStartTime( starttime );
2153 }
2154 }
2155 }
2156 } else {
2157 animator.SyncAnimChannels( channel, syncToChannel, gameLocal.time, blendTime );
2158 }
2159 }
2160
2161 /***********************************************************************
2162
2163 Damage
2164
2165 ***********************************************************************/
2166
2167 /*
2168 ============
2169 idActor::Gib
2170 ============
2171 */
Gib(const idVec3 & dir,const char * damageDefName)2172 void idActor::Gib( const idVec3 &dir, const char *damageDefName ) {
2173 // no gibbing in multiplayer - by self damage or by moving objects
2174 if ( gameLocal.isMultiplayer ) {
2175 return;
2176 }
2177 // only gib once
2178 if ( gibbed ) {
2179 return;
2180 }
2181 idAFEntity_Gibbable::Gib( dir, damageDefName );
2182 if ( head.GetEntity() ) {
2183 head.GetEntity()->Hide();
2184 }
2185 StopSound( SND_CHANNEL_VOICE, false );
2186 }
2187
2188
2189 /*
2190 ============
2191 idActor::Damage
2192
2193 this entity that is being damaged
2194 inflictor entity that is causing the damage
2195 attacker entity that caused the inflictor to damage targ
2196 example: this=monster, inflictor=rocket, attacker=player
2197
2198 dir direction of the attack for knockback in global space
2199 point point at which the damage is being inflicted, used for headshots
2200 damage amount of damage being inflicted
2201
2202 inflictor, attacker, dir, and point can be NULL for environmental effects
2203
2204 Bleeding wounds and surface overlays are applied in the collision code that
2205 calls Damage()
2206 ============
2207 */
Damage(idEntity * inflictor,idEntity * attacker,const idVec3 & dir,const char * damageDefName,const float damageScale,const int location)2208 void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
2209 const char *damageDefName, const float damageScale, const int location ) {
2210 if ( !fl.takedamage ) {
2211 return;
2212 }
2213
2214 if ( !inflictor ) {
2215 inflictor = gameLocal.world;
2216 }
2217 if ( !attacker ) {
2218 attacker = gameLocal.world;
2219 }
2220
2221 #ifdef _D3XP
2222 SetTimeState ts( timeGroup );
2223
2224 // Helltime boss is immune to all projectiles except the helltime killer
2225 if ( finalBoss && idStr::Icmp(inflictor->GetEntityDefName(), "projectile_helltime_killer") ) {
2226 return;
2227 }
2228
2229 // Maledict is immume to the falling asteroids
2230 if ( !idStr::Icmp( GetEntityDefName(), "monster_boss_d3xp_maledict" ) &&
2231 (!idStr::Icmp( damageDefName, "damage_maledict_asteroid" ) || !idStr::Icmp( damageDefName, "damage_maledict_asteroid_splash" ) ) ) {
2232 return;
2233 }
2234 #else
2235 if ( finalBoss && !inflictor->IsType( idSoulCubeMissile::Type ) ) {
2236 return;
2237 }
2238 #endif
2239
2240 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
2241 if ( !damageDef ) {
2242 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
2243 }
2244
2245 int damage = damageDef->GetInt( "damage" ) * damageScale;
2246 damage = GetDamageForLocation( damage, location );
2247
2248 // inform the attacker that they hit someone
2249 attacker->DamageFeedback( this, inflictor, damage );
2250 if ( damage > 0 ) {
2251 health -= damage;
2252
2253 #ifdef _D3XP
2254 //Check the health against any damage cap that is currently set
2255 if(damageCap >= 0 && health < damageCap) {
2256 health = damageCap;
2257 }
2258 #endif
2259
2260 if ( health <= 0 ) {
2261 if ( health < -999 ) {
2262 health = -999;
2263 }
2264 Killed( inflictor, attacker, damage, dir, location );
2265 if ( ( health < -20 ) && spawnArgs.GetBool( "gib" ) && damageDef->GetBool( "gib" ) ) {
2266 Gib( dir, damageDefName );
2267 }
2268 } else {
2269 Pain( inflictor, attacker, damage, dir, location );
2270 }
2271 } else {
2272 // don't accumulate knockback
2273 if ( af.IsLoaded() ) {
2274 // clear impacts
2275 af.Rest();
2276
2277 // physics is turned off by calling af.Rest()
2278 BecomeActive( TH_PHYSICS );
2279 }
2280 }
2281 }
2282
2283 /*
2284 =====================
2285 idActor::ClearPain
2286 =====================
2287 */
ClearPain(void)2288 void idActor::ClearPain( void ) {
2289 pain_debounce_time = 0;
2290 }
2291
2292 /*
2293 =====================
2294 idActor::Pain
2295 =====================
2296 */
Pain(idEntity * inflictor,idEntity * attacker,int damage,const idVec3 & dir,int location)2297 bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
2298 if ( af.IsLoaded() ) {
2299 // clear impacts
2300 af.Rest();
2301
2302 // physics is turned off by calling af.Rest()
2303 BecomeActive( TH_PHYSICS );
2304 }
2305
2306 if ( gameLocal.time < pain_debounce_time ) {
2307 return false;
2308 }
2309
2310 // don't play pain sounds more than necessary
2311 pain_debounce_time = gameLocal.time + pain_delay;
2312
2313 if ( health > 75 ) {
2314 StartSound( "snd_pain_small", SND_CHANNEL_VOICE, 0, false, NULL );
2315 } else if ( health > 50 ) {
2316 StartSound( "snd_pain_medium", SND_CHANNEL_VOICE, 0, false, NULL );
2317 } else if ( health > 25 ) {
2318 StartSound( "snd_pain_large", SND_CHANNEL_VOICE, 0, false, NULL );
2319 } else {
2320 StartSound( "snd_pain_huge", SND_CHANNEL_VOICE, 0, false, NULL );
2321 }
2322
2323 if ( !allowPain || ( gameLocal.time < painTime ) ) {
2324 // don't play a pain anim
2325 return false;
2326 }
2327
2328 if ( pain_threshold && ( damage < pain_threshold ) ) {
2329 return false;
2330 }
2331
2332 // set the pain anim
2333 idStr damageGroup = GetDamageGroup( location );
2334
2335 painAnim = "";
2336 if ( animPrefix.Length() ) {
2337 if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2338 sprintf( painAnim, "%s_pain_%s", animPrefix.c_str(), damageGroup.c_str() );
2339 if ( !animator.HasAnim( painAnim ) ) {
2340 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2341 if ( !animator.HasAnim( painAnim ) ) {
2342 painAnim = "";
2343 }
2344 }
2345 }
2346
2347 if ( !painAnim.Length() ) {
2348 sprintf( painAnim, "%s_pain", animPrefix.c_str() );
2349 if ( !animator.HasAnim( painAnim ) ) {
2350 painAnim = "";
2351 }
2352 }
2353 } else if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2354 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2355 if ( !animator.HasAnim( painAnim ) ) {
2356 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2357 if ( !animator.HasAnim( painAnim ) ) {
2358 painAnim = "";
2359 }
2360 }
2361 }
2362
2363 if ( !painAnim.Length() ) {
2364 painAnim = "pain";
2365 }
2366
2367 if ( g_debugDamage.GetBool() ) {
2368 gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ),
2369 damageGroup.c_str(), painAnim.c_str() );
2370 }
2371
2372 return true;
2373 }
2374
2375 /*
2376 =====================
2377 idActor::SpawnGibs
2378 =====================
2379 */
SpawnGibs(const idVec3 & dir,const char * damageDefName)2380 void idActor::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
2381 idAFEntity_Gibbable::SpawnGibs( dir, damageDefName );
2382 RemoveAttachments();
2383 }
2384
2385 /*
2386 =====================
2387 idActor::SetupDamageGroups
2388
2389 FIXME: only store group names once and store an index for each joint
2390 =====================
2391 */
SetupDamageGroups(void)2392 void idActor::SetupDamageGroups( void ) {
2393 int i;
2394 const idKeyValue *arg;
2395 idStr groupname;
2396 idList<jointHandle_t> jointList;
2397 int jointnum;
2398 float scale;
2399
2400 // create damage zones
2401 damageGroups.SetNum( animator.NumJoints() );
2402 arg = spawnArgs.MatchPrefix( "damage_zone ", NULL );
2403 while ( arg ) {
2404 groupname = arg->GetKey();
2405 groupname.Strip( "damage_zone " );
2406 animator.GetJointList( arg->GetValue(), jointList );
2407 for( i = 0; i < jointList.Num(); i++ ) {
2408 jointnum = jointList[ i ];
2409 damageGroups[ jointnum ] = groupname;
2410 }
2411 jointList.Clear();
2412 arg = spawnArgs.MatchPrefix( "damage_zone ", arg );
2413 }
2414
2415 // initilize the damage zones to normal damage
2416 damageScale.SetNum( animator.NumJoints() );
2417 for( i = 0; i < damageScale.Num(); i++ ) {
2418 damageScale[ i ] = 1.0f;
2419 }
2420
2421 // set the percentage on damage zones
2422 arg = spawnArgs.MatchPrefix( "damage_scale ", NULL );
2423 while ( arg ) {
2424 scale = atof( arg->GetValue() );
2425 groupname = arg->GetKey();
2426 groupname.Strip( "damage_scale " );
2427 for( i = 0; i < damageScale.Num(); i++ ) {
2428 if ( damageGroups[ i ] == groupname ) {
2429 damageScale[ i ] = scale;
2430 }
2431 }
2432 arg = spawnArgs.MatchPrefix( "damage_scale ", arg );
2433 }
2434 }
2435
2436 /*
2437 =====================
2438 idActor::GetDamageForLocation
2439 =====================
2440 */
GetDamageForLocation(int damage,int location)2441 int idActor::GetDamageForLocation( int damage, int location ) {
2442 if ( ( location < 0 ) || ( location >= damageScale.Num() ) ) {
2443 return damage;
2444 }
2445
2446 return (int)ceil( damage * damageScale[ location ] );
2447 }
2448
2449 /*
2450 =====================
2451 idActor::GetDamageGroup
2452 =====================
2453 */
GetDamageGroup(int location)2454 const char *idActor::GetDamageGroup( int location ) {
2455 if ( ( location < 0 ) || ( location >= damageGroups.Num() ) ) {
2456 return "";
2457 }
2458
2459 return damageGroups[ location ];
2460 }
2461
2462
2463 /***********************************************************************
2464
2465 Events
2466
2467 ***********************************************************************/
2468
2469 /*
2470 =====================
2471 idActor::Event_EnableEyeFocus
2472 =====================
2473 */
PlayFootStepSound(void)2474 void idActor::PlayFootStepSound( void ) {
2475 const char *sound = NULL;
2476 const idMaterial *material;
2477
2478 if ( !GetPhysics()->HasGroundContacts() ) {
2479 return;
2480 }
2481
2482 // start footstep sound based on material type
2483 material = GetPhysics()->GetContact( 0 ).material;
2484 if ( material != NULL ) {
2485 sound = spawnArgs.GetString( va( "snd_footstep_%s", gameLocal.sufaceTypeNames[ material->GetSurfaceType() ] ) );
2486 }
2487 if ( *sound == '\0' ) {
2488 sound = spawnArgs.GetString( "snd_footstep" );
2489 }
2490 if ( *sound != '\0' ) {
2491 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
2492 }
2493 }
2494
2495 /*
2496 =====================
2497 idActor::Event_EnableEyeFocus
2498 =====================
2499 */
Event_EnableEyeFocus(void)2500 void idActor::Event_EnableEyeFocus( void ) {
2501 allowEyeFocus = true;
2502 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
2503 }
2504
2505 /*
2506 =====================
2507 idActor::Event_DisableEyeFocus
2508 =====================
2509 */
Event_DisableEyeFocus(void)2510 void idActor::Event_DisableEyeFocus( void ) {
2511 allowEyeFocus = false;
2512
2513 idEntity *headEnt = head.GetEntity();
2514 if ( headEnt ) {
2515 headEnt->GetAnimator()->Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2516 } else {
2517 animator.Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2518 }
2519 }
2520
2521 /*
2522 ===============
2523 idActor::Event_Footstep
2524 ===============
2525 */
Event_Footstep(void)2526 void idActor::Event_Footstep( void ) {
2527 PlayFootStepSound();
2528 }
2529
2530 /*
2531 =====================
2532 idActor::Event_EnableWalkIK
2533 =====================
2534 */
Event_EnableWalkIK(void)2535 void idActor::Event_EnableWalkIK( void ) {
2536 walkIK.EnableAll();
2537 }
2538
2539 /*
2540 =====================
2541 idActor::Event_DisableWalkIK
2542 =====================
2543 */
Event_DisableWalkIK(void)2544 void idActor::Event_DisableWalkIK( void ) {
2545 walkIK.DisableAll();
2546 }
2547
2548 /*
2549 =====================
2550 idActor::Event_EnableLegIK
2551 =====================
2552 */
Event_EnableLegIK(int num)2553 void idActor::Event_EnableLegIK( int num ) {
2554 walkIK.EnableLeg( num );
2555 }
2556
2557 /*
2558 =====================
2559 idActor::Event_DisableLegIK
2560 =====================
2561 */
Event_DisableLegIK(int num)2562 void idActor::Event_DisableLegIK( int num ) {
2563 walkIK.DisableLeg( num );
2564 }
2565
2566 /*
2567 =====================
2568 idActor::Event_PreventPain
2569 =====================
2570 */
Event_PreventPain(float duration)2571 void idActor::Event_PreventPain( float duration ) {
2572 painTime = gameLocal.time + SEC2MS( duration );
2573 }
2574
2575 /*
2576 ===============
2577 idActor::Event_DisablePain
2578 ===============
2579 */
Event_DisablePain(void)2580 void idActor::Event_DisablePain( void ) {
2581 allowPain = false;
2582 }
2583
2584 /*
2585 ===============
2586 idActor::Event_EnablePain
2587 ===============
2588 */
Event_EnablePain(void)2589 void idActor::Event_EnablePain( void ) {
2590 allowPain = true;
2591 }
2592
2593 /*
2594 =====================
2595 idActor::Event_GetPainAnim
2596 =====================
2597 */
Event_GetPainAnim(void)2598 void idActor::Event_GetPainAnim( void ) {
2599 if ( !painAnim.Length() ) {
2600 idThread::ReturnString( "pain" );
2601 } else {
2602 idThread::ReturnString( painAnim );
2603 }
2604 }
2605
2606 /*
2607 =====================
2608 idActor::Event_SetAnimPrefix
2609 =====================
2610 */
Event_SetAnimPrefix(const char * prefix)2611 void idActor::Event_SetAnimPrefix( const char *prefix ) {
2612 animPrefix = prefix;
2613 }
2614
2615 /*
2616 ===============
2617 idActor::Event_StopAnim
2618 ===============
2619 */
Event_StopAnim(int channel,int frames)2620 void idActor::Event_StopAnim( int channel, int frames ) {
2621 switch( channel ) {
2622 case ANIMCHANNEL_HEAD :
2623 headAnim.StopAnim( frames );
2624 break;
2625
2626 case ANIMCHANNEL_TORSO :
2627 torsoAnim.StopAnim( frames );
2628 break;
2629
2630 case ANIMCHANNEL_LEGS :
2631 legsAnim.StopAnim( frames );
2632 break;
2633
2634 default:
2635 gameLocal.Error( "Unknown anim group" );
2636 break;
2637 }
2638 }
2639
2640 /*
2641 ===============
2642 idActor::Event_PlayAnim
2643 ===============
2644 */
Event_PlayAnim(int channel,const char * animname)2645 void idActor::Event_PlayAnim( int channel, const char *animname ) {
2646 animFlags_t flags;
2647 idEntity *headEnt;
2648 int anim;
2649
2650 anim = GetAnim( channel, animname );
2651 if ( !anim ) {
2652 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2653 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2654 } else {
2655 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2656 }
2657 idThread::ReturnInt( 0 );
2658 return;
2659 }
2660
2661 switch( channel ) {
2662 case ANIMCHANNEL_HEAD :
2663 headEnt = head.GetEntity();
2664 if ( headEnt ) {
2665 headAnim.idleAnim = false;
2666 headAnim.PlayAnim( anim );
2667 flags = headAnim.GetAnimFlags();
2668 if ( !flags.prevent_idle_override ) {
2669 if ( torsoAnim.IsIdle() ) {
2670 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2671 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2672 if ( legsAnim.IsIdle() ) {
2673 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2674 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2675 }
2676 }
2677 }
2678 }
2679 break;
2680
2681 case ANIMCHANNEL_TORSO :
2682 torsoAnim.idleAnim = false;
2683 torsoAnim.PlayAnim( anim );
2684 flags = torsoAnim.GetAnimFlags();
2685 if ( !flags.prevent_idle_override ) {
2686 if ( headAnim.IsIdle() ) {
2687 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2688 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2689 }
2690 if ( legsAnim.IsIdle() ) {
2691 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2692 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2693 }
2694 }
2695 break;
2696
2697 case ANIMCHANNEL_LEGS :
2698 legsAnim.idleAnim = false;
2699 legsAnim.PlayAnim( anim );
2700 flags = legsAnim.GetAnimFlags();
2701 if ( !flags.prevent_idle_override ) {
2702 if ( torsoAnim.IsIdle() ) {
2703 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2704 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2705 if ( headAnim.IsIdle() ) {
2706 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2707 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2708 }
2709 }
2710 }
2711 break;
2712
2713 default :
2714 gameLocal.Error( "Unknown anim group" );
2715 break;
2716 }
2717 idThread::ReturnInt( 1 );
2718 }
2719
2720 /*
2721 ===============
2722 idActor::Event_PlayCycle
2723 ===============
2724 */
Event_PlayCycle(int channel,const char * animname)2725 void idActor::Event_PlayCycle( int channel, const char *animname ) {
2726 animFlags_t flags;
2727 int anim;
2728
2729 anim = GetAnim( channel, animname );
2730 if ( !anim ) {
2731 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2732 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2733 } else {
2734 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2735 }
2736 idThread::ReturnInt( false );
2737 return;
2738 }
2739
2740 switch( channel ) {
2741 case ANIMCHANNEL_HEAD :
2742 headAnim.idleAnim = false;
2743 headAnim.CycleAnim( anim );
2744 flags = headAnim.GetAnimFlags();
2745 if ( !flags.prevent_idle_override ) {
2746 if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2747 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2748 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2749 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2750 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2751 }
2752 }
2753 break;
2754
2755 case ANIMCHANNEL_TORSO :
2756 torsoAnim.idleAnim = false;
2757 torsoAnim.CycleAnim( anim );
2758 flags = torsoAnim.GetAnimFlags();
2759 if ( !flags.prevent_idle_override ) {
2760 if ( headAnim.IsIdle() ) {
2761 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2762 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2763 }
2764 if ( legsAnim.IsIdle() ) {
2765 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2766 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2767 }
2768 }
2769 break;
2770
2771 case ANIMCHANNEL_LEGS :
2772 legsAnim.idleAnim = false;
2773 legsAnim.CycleAnim( anim );
2774 flags = legsAnim.GetAnimFlags();
2775 if ( !flags.prevent_idle_override ) {
2776 if ( torsoAnim.IsIdle() ) {
2777 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2778 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2779 if ( headAnim.IsIdle() ) {
2780 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2781 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2782 }
2783 }
2784 }
2785 break;
2786
2787 default:
2788 gameLocal.Error( "Unknown anim group" );
2789 }
2790
2791 idThread::ReturnInt( true );
2792 }
2793
2794 /*
2795 ===============
2796 idActor::Event_IdleAnim
2797 ===============
2798 */
Event_IdleAnim(int channel,const char * animname)2799 void idActor::Event_IdleAnim( int channel, const char *animname ) {
2800 int anim;
2801
2802 anim = GetAnim( channel, animname );
2803 if ( !anim ) {
2804 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2805 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2806 } else {
2807 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2808 }
2809
2810 switch( channel ) {
2811 case ANIMCHANNEL_HEAD :
2812 headAnim.BecomeIdle();
2813 break;
2814
2815 case ANIMCHANNEL_TORSO :
2816 torsoAnim.BecomeIdle();
2817 break;
2818
2819 case ANIMCHANNEL_LEGS :
2820 legsAnim.BecomeIdle();
2821 break;
2822
2823 default:
2824 gameLocal.Error( "Unknown anim group" );
2825 }
2826
2827 idThread::ReturnInt( false );
2828 return;
2829 }
2830
2831 switch( channel ) {
2832 case ANIMCHANNEL_HEAD :
2833 headAnim.BecomeIdle();
2834 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2835 // don't sync to torso body if it doesn't override idle anims
2836 headAnim.CycleAnim( anim );
2837 } else if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2838 // everything is idle, so play the anim on the head and copy it to the torso and legs
2839 headAnim.CycleAnim( anim );
2840 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2841 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2842 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2843 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2844 } else if ( torsoAnim.IsIdle() ) {
2845 // sync the head and torso to the legs
2846 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, headAnim.animBlendFrames );
2847 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2848 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2849 } else {
2850 // sync the head to the torso
2851 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, headAnim.animBlendFrames );
2852 }
2853 break;
2854
2855 case ANIMCHANNEL_TORSO :
2856 torsoAnim.BecomeIdle();
2857 if ( legsAnim.GetAnimFlags().prevent_idle_override ) {
2858 // don't sync to legs if legs anim doesn't override idle anims
2859 torsoAnim.CycleAnim( anim );
2860 } else if ( legsAnim.IsIdle() ) {
2861 // play the anim in both legs and torso
2862 torsoAnim.CycleAnim( anim );
2863 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2864 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2865 } else {
2866 // sync the anim to the legs
2867 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2868 }
2869
2870 if ( headAnim.IsIdle() ) {
2871 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2872 }
2873 break;
2874
2875 case ANIMCHANNEL_LEGS :
2876 legsAnim.BecomeIdle();
2877 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2878 // don't sync to torso if torso anim doesn't override idle anims
2879 legsAnim.CycleAnim( anim );
2880 } else if ( torsoAnim.IsIdle() ) {
2881 // play the anim in both legs and torso
2882 legsAnim.CycleAnim( anim );
2883 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2884 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2885 if ( headAnim.IsIdle() ) {
2886 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2887 }
2888 } else {
2889 // sync the anim to the torso
2890 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, legsAnim.animBlendFrames );
2891 }
2892 break;
2893
2894 default:
2895 gameLocal.Error( "Unknown anim group" );
2896 }
2897
2898 idThread::ReturnInt( true );
2899 }
2900
2901 /*
2902 ================
2903 idActor::Event_SetSyncedAnimWeight
2904 ================
2905 */
Event_SetSyncedAnimWeight(int channel,int anim,float weight)2906 void idActor::Event_SetSyncedAnimWeight( int channel, int anim, float weight ) {
2907 idEntity *headEnt;
2908
2909 headEnt = head.GetEntity();
2910 switch( channel ) {
2911 case ANIMCHANNEL_HEAD :
2912 if ( headEnt ) {
2913 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2914 } else {
2915 animator.CurrentAnim( ANIMCHANNEL_HEAD )->SetSyncedAnimWeight( anim, weight );
2916 }
2917 if ( torsoAnim.IsIdle() ) {
2918 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2919 if ( legsAnim.IsIdle() ) {
2920 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2921 }
2922 }
2923 break;
2924
2925 case ANIMCHANNEL_TORSO :
2926 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2927 if ( legsAnim.IsIdle() ) {
2928 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2929 }
2930 if ( headEnt && headAnim.IsIdle() ) {
2931 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2932 }
2933 break;
2934
2935 case ANIMCHANNEL_LEGS :
2936 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2937 if ( torsoAnim.IsIdle() ) {
2938 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2939 if ( headEnt && headAnim.IsIdle() ) {
2940 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2941 }
2942 }
2943 break;
2944
2945 default:
2946 gameLocal.Error( "Unknown anim group" );
2947 }
2948 }
2949
2950 /*
2951 ===============
2952 idActor::Event_OverrideAnim
2953 ===============
2954 */
Event_OverrideAnim(int channel)2955 void idActor::Event_OverrideAnim( int channel ) {
2956 switch( channel ) {
2957 case ANIMCHANNEL_HEAD :
2958 headAnim.Disable();
2959 if ( !torsoAnim.IsIdle() ) {
2960 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2961 } else {
2962 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2963 }
2964 break;
2965
2966 case ANIMCHANNEL_TORSO :
2967 torsoAnim.Disable();
2968 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2969 if ( headAnim.IsIdle() ) {
2970 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2971 }
2972 break;
2973
2974 case ANIMCHANNEL_LEGS :
2975 legsAnim.Disable();
2976 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2977 break;
2978
2979 default:
2980 gameLocal.Error( "Unknown anim group" );
2981 break;
2982 }
2983 }
2984
2985 /*
2986 ===============
2987 idActor::Event_EnableAnim
2988 ===============
2989 */
Event_EnableAnim(int channel,int blendFrames)2990 void idActor::Event_EnableAnim( int channel, int blendFrames ) {
2991 switch( channel ) {
2992 case ANIMCHANNEL_HEAD :
2993 headAnim.Enable( blendFrames );
2994 break;
2995
2996 case ANIMCHANNEL_TORSO :
2997 torsoAnim.Enable( blendFrames );
2998 break;
2999
3000 case ANIMCHANNEL_LEGS :
3001 legsAnim.Enable( blendFrames );
3002 break;
3003
3004 default:
3005 gameLocal.Error( "Unknown anim group" );
3006 break;
3007 }
3008 }
3009
3010 /*
3011 ===============
3012 idActor::Event_SetBlendFrames
3013 ===============
3014 */
Event_SetBlendFrames(int channel,int blendFrames)3015 void idActor::Event_SetBlendFrames( int channel, int blendFrames ) {
3016 switch( channel ) {
3017 case ANIMCHANNEL_HEAD :
3018 headAnim.animBlendFrames = blendFrames;
3019 headAnim.lastAnimBlendFrames = blendFrames;
3020 break;
3021
3022 case ANIMCHANNEL_TORSO :
3023 torsoAnim.animBlendFrames = blendFrames;
3024 torsoAnim.lastAnimBlendFrames = blendFrames;
3025 break;
3026
3027 case ANIMCHANNEL_LEGS :
3028 legsAnim.animBlendFrames = blendFrames;
3029 legsAnim.lastAnimBlendFrames = blendFrames;
3030 break;
3031
3032 default:
3033 gameLocal.Error( "Unknown anim group" );
3034 break;
3035 }
3036 }
3037
3038 /*
3039 ===============
3040 idActor::Event_GetBlendFrames
3041 ===============
3042 */
Event_GetBlendFrames(int channel)3043 void idActor::Event_GetBlendFrames( int channel ) {
3044 switch( channel ) {
3045 case ANIMCHANNEL_HEAD :
3046 idThread::ReturnInt( headAnim.animBlendFrames );
3047 break;
3048
3049 case ANIMCHANNEL_TORSO :
3050 idThread::ReturnInt( torsoAnim.animBlendFrames );
3051 break;
3052
3053 case ANIMCHANNEL_LEGS :
3054 idThread::ReturnInt( legsAnim.animBlendFrames );
3055 break;
3056
3057 default:
3058 gameLocal.Error( "Unknown anim group" );
3059 break;
3060 }
3061 }
3062
3063 /*
3064 ===============
3065 idActor::Event_AnimState
3066 ===============
3067 */
Event_AnimState(int channel,const char * statename,int blendFrames)3068 void idActor::Event_AnimState( int channel, const char *statename, int blendFrames ) {
3069 SetAnimState( channel, statename, blendFrames );
3070 }
3071
3072 /*
3073 ===============
3074 idActor::Event_GetAnimState
3075 ===============
3076 */
Event_GetAnimState(int channel)3077 void idActor::Event_GetAnimState( int channel ) {
3078 const char *state;
3079
3080 state = GetAnimState( channel );
3081 idThread::ReturnString( state );
3082 }
3083
3084 /*
3085 ===============
3086 idActor::Event_InAnimState
3087 ===============
3088 */
Event_InAnimState(int channel,const char * statename)3089 void idActor::Event_InAnimState( int channel, const char *statename ) {
3090 bool instate;
3091
3092 instate = InAnimState( channel, statename );
3093 idThread::ReturnInt( instate );
3094 }
3095
3096 /*
3097 ===============
3098 idActor::Event_FinishAction
3099 ===============
3100 */
Event_FinishAction(const char * actionname)3101 void idActor::Event_FinishAction( const char *actionname ) {
3102 if ( waitState == actionname ) {
3103 SetWaitState( "" );
3104 }
3105 }
3106
3107 /*
3108 ===============
3109 idActor::Event_AnimDone
3110 ===============
3111 */
Event_AnimDone(int channel,int blendFrames)3112 void idActor::Event_AnimDone( int channel, int blendFrames ) {
3113 bool result;
3114
3115 switch( channel ) {
3116 case ANIMCHANNEL_HEAD :
3117 result = headAnim.AnimDone( blendFrames );
3118 idThread::ReturnInt( result );
3119 break;
3120
3121 case ANIMCHANNEL_TORSO :
3122 result = torsoAnim.AnimDone( blendFrames );
3123 idThread::ReturnInt( result );
3124 break;
3125
3126 case ANIMCHANNEL_LEGS :
3127 result = legsAnim.AnimDone( blendFrames );
3128 idThread::ReturnInt( result );
3129 break;
3130
3131 default:
3132 gameLocal.Error( "Unknown anim group" );
3133 }
3134 }
3135
3136 /*
3137 ================
3138 idActor::Event_HasAnim
3139 ================
3140 */
Event_HasAnim(int channel,const char * animname)3141 void idActor::Event_HasAnim( int channel, const char *animname ) {
3142 if ( GetAnim( channel, animname ) != 0 ) {
3143 idThread::ReturnFloat( 1.0f );
3144 } else {
3145 idThread::ReturnFloat( 0.0f );
3146 }
3147 }
3148
3149 /*
3150 ================
3151 idActor::Event_CheckAnim
3152 ================
3153 */
Event_CheckAnim(int channel,const char * animname)3154 void idActor::Event_CheckAnim( int channel, const char *animname ) {
3155 if ( !GetAnim( channel, animname ) ) {
3156 if ( animPrefix.Length() ) {
3157 gameLocal.Error( "Can't find anim '%s_%s' for '%s'", animPrefix.c_str(), animname, name.c_str() );
3158 } else {
3159 gameLocal.Error( "Can't find anim '%s' for '%s'", animname, name.c_str() );
3160 }
3161 }
3162 }
3163
3164 /*
3165 ================
3166 idActor::Event_ChooseAnim
3167 ================
3168 */
Event_ChooseAnim(int channel,const char * animname)3169 void idActor::Event_ChooseAnim( int channel, const char *animname ) {
3170 int anim;
3171
3172 anim = GetAnim( channel, animname );
3173 if ( anim ) {
3174 if ( channel == ANIMCHANNEL_HEAD ) {
3175 if ( head.GetEntity() ) {
3176 idThread::ReturnString( head.GetEntity()->GetAnimator()->AnimFullName( anim ) );
3177 return;
3178 }
3179 } else {
3180 idThread::ReturnString( animator.AnimFullName( anim ) );
3181 return;
3182 }
3183 }
3184
3185 idThread::ReturnString( "" );
3186 }
3187
3188 /*
3189 ================
3190 idActor::Event_AnimLength
3191 ================
3192 */
Event_AnimLength(int channel,const char * animname)3193 void idActor::Event_AnimLength( int channel, const char *animname ) {
3194 int anim;
3195
3196 anim = GetAnim( channel, animname );
3197 if ( anim ) {
3198 if ( channel == ANIMCHANNEL_HEAD ) {
3199 if ( head.GetEntity() ) {
3200 idThread::ReturnFloat( MS2SEC( head.GetEntity()->GetAnimator()->AnimLength( anim ) ) );
3201 return;
3202 }
3203 } else {
3204 idThread::ReturnFloat( MS2SEC( animator.AnimLength( anim ) ) );
3205 return;
3206 }
3207 }
3208
3209 idThread::ReturnFloat( 0.0f );
3210 }
3211
3212 /*
3213 ================
3214 idActor::Event_AnimDistance
3215 ================
3216 */
Event_AnimDistance(int channel,const char * animname)3217 void idActor::Event_AnimDistance( int channel, const char *animname ) {
3218 int anim;
3219
3220 anim = GetAnim( channel, animname );
3221 if ( anim ) {
3222 if ( channel == ANIMCHANNEL_HEAD ) {
3223 if ( head.GetEntity() ) {
3224 idThread::ReturnFloat( head.GetEntity()->GetAnimator()->TotalMovementDelta( anim ).Length() );
3225 return;
3226 }
3227 } else {
3228 idThread::ReturnFloat( animator.TotalMovementDelta( anim ).Length() );
3229 return;
3230 }
3231 }
3232
3233 idThread::ReturnFloat( 0.0f );
3234 }
3235
3236 /*
3237 ================
3238 idActor::Event_HasEnemies
3239 ================
3240 */
Event_HasEnemies(void)3241 void idActor::Event_HasEnemies( void ) {
3242 bool hasEnemy;
3243
3244 hasEnemy = HasEnemies();
3245 idThread::ReturnInt( hasEnemy );
3246 }
3247
3248 /*
3249 ================
3250 idActor::Event_NextEnemy
3251 ================
3252 */
Event_NextEnemy(idEntity * ent)3253 void idActor::Event_NextEnemy( idEntity *ent ) {
3254 idActor *actor;
3255
3256 if ( !ent || ( ent == this ) ) {
3257 actor = enemyList.Next();
3258 } else {
3259 if ( !ent->IsType( idActor::Type ) ) {
3260 gameLocal.Error( "'%s' cannot be an enemy", ent->name.c_str() );
3261 }
3262
3263 actor = static_cast<idActor *>( ent );
3264 if ( actor->enemyNode.ListHead() != &enemyList ) {
3265 gameLocal.Error( "'%s' is not in '%s' enemy list", actor->name.c_str(), name.c_str() );
3266 }
3267 }
3268
3269 for( ; actor != NULL; actor = actor->enemyNode.Next() ) {
3270 if ( !actor->fl.hidden ) {
3271 idThread::ReturnEntity( actor );
3272 return;
3273 }
3274 }
3275
3276 idThread::ReturnEntity( NULL );
3277 }
3278
3279 /*
3280 ================
3281 idActor::Event_ClosestEnemyToPoint
3282 ================
3283 */
Event_ClosestEnemyToPoint(const idVec3 & pos)3284 void idActor::Event_ClosestEnemyToPoint( const idVec3 &pos ) {
3285 idActor *bestEnt = ClosestEnemyToPoint( pos );
3286 idThread::ReturnEntity( bestEnt );
3287 }
3288
3289 /*
3290 ================
3291 idActor::Event_StopSound
3292 ================
3293 */
Event_StopSound(int channel,int netSync)3294 void idActor::Event_StopSound( int channel, int netSync ) {
3295 if ( channel == SND_CHANNEL_VOICE ) {
3296 idEntity *headEnt = head.GetEntity();
3297 if ( headEnt ) {
3298 headEnt->StopSound( channel, ( netSync != 0 ) );
3299 }
3300 }
3301 StopSound( channel, ( netSync != 0 ) );
3302 }
3303
3304 /*
3305 =====================
3306 idActor::Event_SetNextState
3307 =====================
3308 */
Event_SetNextState(const char * name)3309 void idActor::Event_SetNextState( const char *name ) {
3310 idealState = GetScriptFunction( name );
3311 if ( idealState == state ) {
3312 state = NULL;
3313 }
3314 }
3315
3316 /*
3317 =====================
3318 idActor::Event_SetState
3319 =====================
3320 */
Event_SetState(const char * name)3321 void idActor::Event_SetState( const char *name ) {
3322 idealState = GetScriptFunction( name );
3323 if ( idealState == state ) {
3324 state = NULL;
3325 }
3326 scriptThread->DoneProcessing();
3327 }
3328
3329 /*
3330 =====================
3331 idActor::Event_GetState
3332 =====================
3333 */
Event_GetState(void)3334 void idActor::Event_GetState( void ) {
3335 if ( state ) {
3336 idThread::ReturnString( state->Name() );
3337 } else {
3338 idThread::ReturnString( "" );
3339 }
3340 }
3341
3342 /*
3343 =====================
3344 idActor::Event_GetHead
3345 =====================
3346 */
Event_GetHead(void)3347 void idActor::Event_GetHead( void ) {
3348 idThread::ReturnEntity( head.GetEntity() );
3349 }
3350
3351 #ifdef _D3XP
3352 /*
3353 ================
3354 idActor::Event_SetDamageGroupScale
3355 ================
3356 */
Event_SetDamageGroupScale(const char * groupName,float scale)3357 void idActor::Event_SetDamageGroupScale( const char* groupName, float scale) {
3358
3359 for( int i = 0; i < damageScale.Num(); i++ ) {
3360 if ( damageGroups[ i ] == groupName ) {
3361 damageScale[ i ] = scale;
3362 }
3363 }
3364 }
3365
3366 /*
3367 ================
3368 idActor::Event_SetDamageGroupScaleAll
3369 ================
3370 */
Event_SetDamageGroupScaleAll(float scale)3371 void idActor::Event_SetDamageGroupScaleAll( float scale ) {
3372
3373 for( int i = 0; i < damageScale.Num(); i++ ) {
3374 damageScale[ i ] = scale;
3375 }
3376 }
3377
Event_GetDamageGroupScale(const char * groupName)3378 void idActor::Event_GetDamageGroupScale( const char* groupName ) {
3379
3380 for( int i = 0; i < damageScale.Num(); i++ ) {
3381 if ( damageGroups[ i ] == groupName ) {
3382 idThread::ReturnFloat(damageScale[i]);
3383 return;
3384 }
3385 }
3386
3387 idThread::ReturnFloat(0);
3388 }
3389
Event_SetDamageCap(float _damageCap)3390 void idActor::Event_SetDamageCap( float _damageCap ) {
3391 damageCap = _damageCap;
3392 }
3393
Event_SetWaitState(const char * waitState)3394 void idActor::Event_SetWaitState( const char* waitState) {
3395 SetWaitState(waitState);
3396 }
3397
Event_GetWaitState()3398 void idActor::Event_GetWaitState() {
3399 if(WaitState()) {
3400 idThread::ReturnString(WaitState());
3401 } else {
3402 idThread::ReturnString("");
3403 }
3404 }
3405 #endif
3406