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