DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Actor.cpp (87998B)


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