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