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