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