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