2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
35 /***********************************************************************
39 ***********************************************************************/
43 idAnimState::idAnimState
46 idAnimState::idAnimState() {
52 channel = ANIMCHANNEL_ALL;
54 lastAnimBlendFrames = 0;
59 idAnimState::~idAnimState
62 idAnimState::~idAnimState() {
71 void idAnimState::Save( idSaveGame *savefile ) const {
73 savefile->WriteObject( self );
75 // Save the entity owner of the animator
76 savefile->WriteObject( animator->GetEntity() );
78 savefile->WriteObject( thread );
80 savefile->WriteString( state );
82 savefile->WriteInt( animBlendFrames );
83 savefile->WriteInt( lastAnimBlendFrames );
84 savefile->WriteInt( channel );
85 savefile->WriteBool( idleAnim );
86 savefile->WriteBool( disabled );
94 void idAnimState::Restore( idRestoreGame *savefile ) {
95 savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
98 savefile->ReadObject( reinterpret_cast<idClass *&>( animowner ) );
100 animator = animowner->GetAnimator();
103 savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
105 savefile->ReadString( state );
107 savefile->ReadInt( animBlendFrames );
108 savefile->ReadInt( lastAnimBlendFrames );
109 savefile->ReadInt( channel );
110 savefile->ReadBool( idleAnim );
111 savefile->ReadBool( disabled );
115 =====================
117 =====================
119 void idAnimState::Init( idActor *owner, idAnimator *_animator, int animchannel ) {
123 animator = _animator;
124 channel = animchannel;
127 thread = new idThread();
128 thread->ManualDelete();
131 thread->ManualControl();
135 =====================
136 idAnimState::Shutdown
137 =====================
139 void idAnimState::Shutdown( void ) {
145 =====================
146 idAnimState::SetState
147 =====================
149 void idAnimState::SetState( const char *statename, int blendFrames ) {
150 const function_t *func;
152 func = self->scriptObject.GetFunction( statename );
155 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, self->scriptObject.GetTypeName() );
160 animBlendFrames = blendFrames;
161 lastAnimBlendFrames = blendFrames;
162 thread->CallFunction( self, func, true );
164 animBlendFrames = blendFrames;
165 lastAnimBlendFrames = blendFrames;
169 if ( ai_debugScript.GetInteger() == self->entityNumber ) {
170 gameLocal.Printf( "%d: %s: Animstate: %s\n", gameLocal.time, self->name.c_str(), state.c_str() );
175 =====================
176 idAnimState::StopAnim
177 =====================
179 void idAnimState::StopAnim( int frames ) {
181 animator->Clear( channel, gameLocal.time, FRAME2MS( frames ) );
185 =====================
186 idAnimState::PlayAnim
187 =====================
189 void idAnimState::PlayAnim( int anim ) {
191 animator->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
197 =====================
198 idAnimState::CycleAnim
199 =====================
201 void idAnimState::CycleAnim( int anim ) {
203 animator->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
209 =====================
210 idAnimState::BecomeIdle
211 =====================
213 void idAnimState::BecomeIdle( void ) {
218 =====================
219 idAnimState::Disabled
220 =====================
222 bool idAnimState::Disabled( void ) const {
227 =====================
228 idAnimState::AnimDone
229 =====================
231 bool idAnimState::AnimDone( int blendFrames ) const {
234 animDoneTime = animator->CurrentAnim( channel )->GetEndTime();
235 if ( animDoneTime < 0 ) {
238 } else if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
246 =====================
248 =====================
250 bool idAnimState::IsIdle( void ) const {
251 return disabled || idleAnim;
255 =====================
256 idAnimState::GetAnimFlags
257 =====================
259 animFlags_t idAnimState::GetAnimFlags( void ) const {
262 memset( &flags, 0, sizeof( flags ) );
263 if ( !disabled && !AnimDone( 0 ) ) {
264 flags = animator->GetAnimFlags( animator->CurrentAnim( channel )->AnimNum() );
271 =====================
273 =====================
275 void idAnimState::Enable( int blendFrames ) {
278 animBlendFrames = blendFrames;
279 lastAnimBlendFrames = blendFrames;
280 if ( state.Length() ) {
281 SetState( state.c_str(), blendFrames );
287 =====================
289 =====================
291 void idAnimState::Disable( void ) {
297 =====================
298 idAnimState::UpdateState
299 =====================
301 bool idAnimState::UpdateState( void ) {
306 if ( ai_debugScript.GetInteger() == self->entityNumber ) {
307 thread->EnableDebugInfo();
309 thread->DisableDebugInfo();
317 /***********************************************************************
321 ***********************************************************************/
323 const idEventDef AI_EnableEyeFocus( "enableEyeFocus" );
324 const idEventDef AI_DisableEyeFocus( "disableEyeFocus" );
325 const idEventDef EV_Footstep( "footstep" );
326 const idEventDef EV_FootstepLeft( "leftFoot" );
327 const idEventDef EV_FootstepRight( "rightFoot" );
328 const idEventDef EV_EnableWalkIK( "EnableWalkIK" );
329 const idEventDef EV_DisableWalkIK( "DisableWalkIK" );
330 const idEventDef EV_EnableLegIK( "EnableLegIK", "d" );
331 const idEventDef EV_DisableLegIK( "DisableLegIK", "d" );
332 const idEventDef AI_StopAnim( "stopAnim", "dd" );
333 const idEventDef AI_PlayAnim( "playAnim", "ds", 'd' );
334 const idEventDef AI_PlayCycle( "playCycle", "ds", 'd' );
335 const idEventDef AI_IdleAnim( "idleAnim", "ds", 'd' );
336 const idEventDef AI_SetSyncedAnimWeight( "setSyncedAnimWeight", "ddf" );
337 const idEventDef AI_SetBlendFrames( "setBlendFrames", "dd" );
338 const idEventDef AI_GetBlendFrames( "getBlendFrames", "d", 'd' );
339 const idEventDef AI_AnimState( "animState", "dsd" );
340 const idEventDef AI_GetAnimState( "getAnimState", "d", 's' );
341 const idEventDef AI_InAnimState( "inAnimState", "ds", 'd' );
342 const idEventDef AI_FinishAction( "finishAction", "s" );
343 const idEventDef AI_AnimDone( "animDone", "dd", 'd' );
344 const idEventDef AI_OverrideAnim( "overrideAnim", "d" );
345 const idEventDef AI_EnableAnim( "enableAnim", "dd" );
346 const idEventDef AI_PreventPain( "preventPain", "f" );
347 const idEventDef AI_DisablePain( "disablePain" );
348 const idEventDef AI_EnablePain( "enablePain" );
349 const idEventDef AI_GetPainAnim( "getPainAnim", NULL, 's' );
350 const idEventDef AI_SetAnimPrefix( "setAnimPrefix", "s" );
351 const idEventDef AI_HasAnim( "hasAnim", "ds", 'f' );
352 const idEventDef AI_CheckAnim( "checkAnim", "ds" );
353 const idEventDef AI_ChooseAnim( "chooseAnim", "ds", 's' );
354 const idEventDef AI_AnimLength( "animLength", "ds", 'f' );
355 const idEventDef AI_AnimDistance( "animDistance", "ds", 'f' );
356 const idEventDef AI_HasEnemies( "hasEnemies", NULL, 'd' );
357 const idEventDef AI_NextEnemy( "nextEnemy", "E", 'e' );
358 const idEventDef AI_ClosestEnemyToPoint( "closestEnemyToPoint", "v", 'e' );
359 const idEventDef AI_SetNextState( "setNextState", "s" );
360 const idEventDef AI_SetState( "setState", "s" );
361 const idEventDef AI_GetState( "getState", NULL, 's' );
362 const idEventDef AI_GetHead( "getHead", NULL, 'e' );
364 const idEventDef EV_SetDamageGroupScale( "setDamageGroupScale", "sf" );
365 const idEventDef EV_SetDamageGroupScaleAll( "setDamageGroupScaleAll", "f" );
366 const idEventDef EV_GetDamageGroupScale( "getDamageGroupScale", "s", 'f' );
367 const idEventDef EV_SetDamageCap( "setDamageCap", "f" );
368 const idEventDef EV_SetWaitState( "setWaitState" , "s" );
369 const idEventDef EV_GetWaitState( "getWaitState", NULL, 's' );
372 CLASS_DECLARATION( idAFEntity_Gibbable, idActor )
373 EVENT( AI_EnableEyeFocus, idActor::Event_EnableEyeFocus )
374 EVENT( AI_DisableEyeFocus, idActor::Event_DisableEyeFocus )
375 EVENT( EV_Footstep, idActor::Event_Footstep )
376 EVENT( EV_FootstepLeft, idActor::Event_Footstep )
377 EVENT( EV_FootstepRight, idActor::Event_Footstep )
378 EVENT( EV_EnableWalkIK, idActor::Event_EnableWalkIK )
379 EVENT( EV_DisableWalkIK, idActor::Event_DisableWalkIK )
380 EVENT( EV_EnableLegIK, idActor::Event_EnableLegIK )
381 EVENT( EV_DisableLegIK, idActor::Event_DisableLegIK )
382 EVENT( AI_PreventPain, idActor::Event_PreventPain )
383 EVENT( AI_DisablePain, idActor::Event_DisablePain )
384 EVENT( AI_EnablePain, idActor::Event_EnablePain )
385 EVENT( AI_GetPainAnim, idActor::Event_GetPainAnim )
386 EVENT( AI_SetAnimPrefix, idActor::Event_SetAnimPrefix )
387 EVENT( AI_StopAnim, idActor::Event_StopAnim )
388 EVENT( AI_PlayAnim, idActor::Event_PlayAnim )
389 EVENT( AI_PlayCycle, idActor::Event_PlayCycle )
390 EVENT( AI_IdleAnim, idActor::Event_IdleAnim )
391 EVENT( AI_SetSyncedAnimWeight, idActor::Event_SetSyncedAnimWeight )
392 EVENT( AI_SetBlendFrames, idActor::Event_SetBlendFrames )
393 EVENT( AI_GetBlendFrames, idActor::Event_GetBlendFrames )
394 EVENT( AI_AnimState, idActor::Event_AnimState )
395 EVENT( AI_GetAnimState, idActor::Event_GetAnimState )
396 EVENT( AI_InAnimState, idActor::Event_InAnimState )
397 EVENT( AI_FinishAction, idActor::Event_FinishAction )
398 EVENT( AI_AnimDone, idActor::Event_AnimDone )
399 EVENT( AI_OverrideAnim, idActor::Event_OverrideAnim )
400 EVENT( AI_EnableAnim, idActor::Event_EnableAnim )
401 EVENT( AI_HasAnim, idActor::Event_HasAnim )
402 EVENT( AI_CheckAnim, idActor::Event_CheckAnim )
403 EVENT( AI_ChooseAnim, idActor::Event_ChooseAnim )
404 EVENT( AI_AnimLength, idActor::Event_AnimLength )
405 EVENT( AI_AnimDistance, idActor::Event_AnimDistance )
406 EVENT( AI_HasEnemies, idActor::Event_HasEnemies )
407 EVENT( AI_NextEnemy, idActor::Event_NextEnemy )
408 EVENT( AI_ClosestEnemyToPoint, idActor::Event_ClosestEnemyToPoint )
409 EVENT( EV_StopSound, idActor::Event_StopSound )
410 EVENT( AI_SetNextState, idActor::Event_SetNextState )
411 EVENT( AI_SetState, idActor::Event_SetState )
412 EVENT( AI_GetState, idActor::Event_GetState )
413 EVENT( AI_GetHead, idActor::Event_GetHead )
415 EVENT( EV_SetDamageGroupScale, idActor::Event_SetDamageGroupScale )
416 EVENT( EV_SetDamageGroupScaleAll, idActor::Event_SetDamageGroupScaleAll )
417 EVENT( EV_GetDamageGroupScale, idActor::Event_GetDamageGroupScale )
418 EVENT( EV_SetDamageCap, idActor::Event_SetDamageCap )
419 EVENT( EV_SetWaitState, idActor::Event_SetWaitState )
420 EVENT( EV_GetWaitState, idActor::Event_GetWaitState )
425 =====================
427 =====================
429 idActor::idActor( void ) {
432 scriptThread = NULL; // initialized by ConstructScriptObject, which is called by idEntity::Spawn
434 use_combat_bbox = false;
441 pain_debounce_time = 0;
448 leftEyeJoint = INVALID_JOINT;
449 rightEyeJoint = INVALID_JOINT;
450 soundJoint = INVALID_JOINT;
453 deltaViewAngles.Zero();
457 allowEyeFocus = false;
468 attachments.SetGranularity( 1 );
470 enemyNode.SetOwner( this );
471 enemyList.SetOwner( this );
479 =====================
481 =====================
483 idActor::~idActor( void ) {
487 DeconstructScriptObject();
490 StopSound( SND_CHANNEL_ANY, false );
495 if ( head.GetEntity() ) {
496 head.GetEntity()->ClearBody();
497 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
500 // remove any attached entities
501 for( i = 0; i < attachments.Num(); i++ ) {
502 ent = attachments[ i ].ent.GetEntity();
504 ent->PostEventMS( &EV_Remove, 0 );
512 =====================
514 =====================
516 void idActor::Spawn( void ) {
520 copyJoints_t copyJoint;
526 spawnArgs.GetInt( "rank", "0", rank );
527 spawnArgs.GetInt( "team", "0", team );
528 spawnArgs.GetVector( "offsetModel", "0 0 0", modelOffset );
530 spawnArgs.GetBool( "use_combat_bbox", "0", use_combat_bbox );
532 viewAxis = GetPhysics()->GetAxis();
534 spawnArgs.GetFloat( "fov", "90", fovDegrees );
535 SetFOV( fovDegrees );
537 pain_debounce_time = 0;
539 pain_delay = SEC2MS( spawnArgs.GetFloat( "pain_delay" ) );
540 pain_threshold = spawnArgs.GetInt( "pain_threshold" );
544 walkIK.Init( this, IK_ANIM, modelOffset );
546 // the animation used to be set to the IK_ANIM at this point, but that was fixed, resulting in
547 // attachments not binding correctly, so we're stuck setting the IK_ANIM before attaching things.
548 animator.ClearAllAnims( gameLocal.time, 0 );
549 animator.SetFrame( ANIMCHANNEL_ALL, animator.GetAnim( IK_ANIM ), 0, 0, 0 );
551 // spawn any attachments we might have
552 const idKeyValue *kv = spawnArgs.MatchPrefix( "def_attach", NULL );
556 args.Set( "classname", kv->GetValue().c_str() );
558 // make items non-touchable so the player can't take them out of the character's hands
559 args.Set( "no_touch", "1" );
561 // don't let them drop to the floor
562 args.Set( "dropToFloor", "0" );
564 gameLocal.SpawnEntityDef( args, &ent );
566 gameLocal.Error( "Couldn't spawn '%s' to attach to entity '%s'", kv->GetValue().c_str(), name.c_str() );
570 kv = spawnArgs.MatchPrefix( "def_attach", kv );
576 // clear the bind anim
577 animator.ClearAllAnims( gameLocal.time, 0 );
579 idEntity *headEnt = head.GetEntity();
580 idAnimator *headAnimator;
582 headAnimator = headEnt->GetAnimator();
584 headAnimator = &animator;
588 // set up the list of joints to copy to the head
589 for( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) ) {
590 if ( kv->GetValue() == "" ) {
591 // probably clearing out inherited key, so skip it
595 jointName = kv->GetKey();
596 if ( jointName.StripLeadingOnce( "copy_joint_world " ) ) {
597 copyJoint.mod = JOINTMOD_WORLD_OVERRIDE;
599 jointName.StripLeadingOnce( "copy_joint " );
600 copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE;
603 copyJoint.from = animator.GetJointHandle( jointName );
604 if ( copyJoint.from == INVALID_JOINT ) {
605 gameLocal.Warning( "Unknown copy_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
609 jointName = kv->GetValue();
610 copyJoint.to = headAnimator->GetJointHandle( jointName );
611 if ( copyJoint.to == INVALID_JOINT ) {
612 gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %s", jointName.c_str(), name.c_str() );
616 copyJoints.Append( copyJoint );
621 blink_anim = headAnimator->GetAnim( "blink" );
622 blink_time = 0; // it's ok to blink right away
623 blink_min = SEC2MS( spawnArgs.GetFloat( "blink_min", "0.5" ) );
624 blink_max = SEC2MS( spawnArgs.GetFloat( "blink_max", "8" ) );
626 // set up the head anim if necessary
627 int headAnim = headAnimator->GetAnim( "def_head" );
630 headAnimator->CycleAnim( ANIMCHANNEL_ALL, headAnim, gameLocal.time, 0 );
632 headAnimator->CycleAnim( ANIMCHANNEL_HEAD, headAnim, gameLocal.time, 0 );
636 if ( spawnArgs.GetString( "sound_bone", "", jointName ) ) {
637 soundJoint = animator.GetJointHandle( jointName );
638 if ( soundJoint == INVALID_JOINT ) {
639 gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), jointName.c_str() );
643 finalBoss = spawnArgs.GetBool( "finalBoss" );
653 void idActor::FinishSetup( void ) {
654 const char *scriptObjectName;
656 // setup script object
657 if ( spawnArgs.GetString( "scriptobject", NULL, &scriptObjectName ) ) {
658 if ( !scriptObject.SetType( scriptObjectName ) ) {
659 gameLocal.Error( "Script object '%s' not found on entity '%s'.", scriptObjectName, name.c_str() );
662 ConstructScriptObject();
673 void idActor::SetupHead( void ) {
674 idAFAttachment *headEnt;
676 const char *headModel;
678 jointHandle_t damageJoint;
680 const idKeyValue *sndKV;
682 if ( gameLocal.isClient ) {
686 headModel = spawnArgs.GetString( "def_head", "" );
687 if ( headModel[ 0 ] ) {
688 jointName = spawnArgs.GetString( "head_joint" );
689 joint = animator.GetJointHandle( jointName );
690 if ( joint == INVALID_JOINT ) {
691 gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
694 // set the damage joint to be part of the head damage group
696 for( i = 0; i < damageGroups.Num(); i++ ) {
697 if ( damageGroups[ i ] == "head" ) {
698 damageJoint = static_cast<jointHandle_t>( i );
703 // copy any sounds in case we have frame commands on the head
705 sndKV = spawnArgs.MatchPrefix( "snd_", NULL );
707 args.Set( sndKV->GetKey(), sndKV->GetValue() );
708 sndKV = spawnArgs.MatchPrefix( "snd_", sndKV );
712 // copy slowmo param to the head
713 args.SetBool( "slowmo", spawnArgs.GetBool("slowmo", "1") );
717 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, &args ) );
718 headEnt->SetName( va( "%s_head", name.c_str() ) );
719 headEnt->SetBody( this, headModel, damageJoint );
724 if ( spawnArgs.GetString( "skin_head_xray", "", xSkin ) ) {
725 headEnt->xraySkin = declManager->FindSkin( xSkin.c_str() );
726 headEnt->UpdateModel();
732 idAttachInfo &attach = attachments.Alloc();
733 attach.channel = animator.GetChannelForJoint( joint );
734 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
735 origin = renderEntity.origin + ( origin + modelOffset ) * renderEntity.axis;
736 attach.ent = headEnt;
737 headEnt->SetOrigin( origin );
738 headEnt->SetAxis( renderEntity.axis );
739 headEnt->BindToJoint( this, joint, true );
745 idActor::CopyJointsFromBodyToHead
748 void idActor::CopyJointsFromBodyToHead( void ) {
749 idEntity *headEnt = head.GetEntity();
750 idAnimator *headAnimator;
760 headAnimator = headEnt->GetAnimator();
762 // copy the animation from the body to the head
763 for( i = 0; i < copyJoints.Num(); i++ ) {
764 if ( copyJoints[ i ].mod == JOINTMOD_WORLD_OVERRIDE ) {
765 mat = headEnt->GetPhysics()->GetAxis().Transpose();
766 GetJointWorldTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
767 pos -= headEnt->GetPhysics()->GetOrigin();
768 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos * mat );
769 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis * mat );
771 animator.GetJointLocalTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
772 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos );
773 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis );
783 void idActor::Restart( void ) {
784 assert( !head.GetEntity() );
793 archive object for savegame file
796 void idActor::Save( idSaveGame *savefile ) const {
800 savefile->WriteInt( team );
801 savefile->WriteInt( rank );
802 savefile->WriteMat3( viewAxis );
804 savefile->WriteInt( enemyList.Num() );
805 for ( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
806 savefile->WriteObject( ent );
809 savefile->WriteFloat( fovDot );
810 savefile->WriteVec3( eyeOffset );
811 savefile->WriteVec3( modelOffset );
812 savefile->WriteAngles( deltaViewAngles );
814 savefile->WriteInt( pain_debounce_time );
815 savefile->WriteInt( pain_delay );
816 savefile->WriteInt( pain_threshold );
818 savefile->WriteInt( damageGroups.Num() );
819 for( i = 0; i < damageGroups.Num(); i++ ) {
820 savefile->WriteString( damageGroups[ i ] );
823 savefile->WriteInt( damageScale.Num() );
824 for( i = 0; i < damageScale.Num(); i++ ) {
825 savefile->WriteFloat( damageScale[ i ] );
828 savefile->WriteBool( use_combat_bbox );
829 head.Save( savefile );
831 savefile->WriteInt( copyJoints.Num() );
832 for( i = 0; i < copyJoints.Num(); i++ ) {
833 savefile->WriteInt( copyJoints[i].mod );
834 savefile->WriteJoint( copyJoints[i].from );
835 savefile->WriteJoint( copyJoints[i].to );
838 savefile->WriteJoint( leftEyeJoint );
839 savefile->WriteJoint( rightEyeJoint );
840 savefile->WriteJoint( soundJoint );
842 walkIK.Save( savefile );
844 savefile->WriteString( animPrefix );
845 savefile->WriteString( painAnim );
847 savefile->WriteInt( blink_anim );
848 savefile->WriteInt( blink_time );
849 savefile->WriteInt( blink_min );
850 savefile->WriteInt( blink_max );
853 savefile->WriteObject( scriptThread );
855 savefile->WriteString( waitState );
857 headAnim.Save( savefile );
858 torsoAnim.Save( savefile );
859 legsAnim.Save( savefile );
861 savefile->WriteBool( allowPain );
862 savefile->WriteBool( allowEyeFocus );
864 savefile->WriteInt( painTime );
866 savefile->WriteInt( attachments.Num() );
867 for ( i = 0; i < attachments.Num(); i++ ) {
868 attachments[i].ent.Save( savefile );
869 savefile->WriteInt( attachments[i].channel );
872 savefile->WriteBool( finalBoss );
876 //FIXME: this is unneccesary
878 idLexer src( state->Name(), idStr::Length( state->Name() ), "idAI::Save" );
880 src.ReadTokenOnLine( &token );
881 src.ExpectTokenString( "::" );
882 src.ReadTokenOnLine( &token );
884 savefile->WriteString( token );
886 savefile->WriteString( "" );
890 idLexer src( idealState->Name(), idStr::Length( idealState->Name() ), "idAI::Save" );
892 src.ReadTokenOnLine( &token );
893 src.ExpectTokenString( "::" );
894 src.ReadTokenOnLine( &token );
896 savefile->WriteString( token );
898 savefile->WriteString( "" );
902 savefile->WriteInt(damageCap);
911 unarchives object from save game file
914 void idActor::Restore( idRestoreGame *savefile ) {
918 savefile->ReadInt( team );
919 savefile->ReadInt( rank );
920 savefile->ReadMat3( viewAxis );
922 savefile->ReadInt( num );
923 for ( i = 0; i < num; i++ ) {
924 savefile->ReadObject( reinterpret_cast<idClass *&>( ent ) );
927 ent->enemyNode.AddToEnd( enemyList );
931 savefile->ReadFloat( fovDot );
932 savefile->ReadVec3( eyeOffset );
933 savefile->ReadVec3( modelOffset );
934 savefile->ReadAngles( deltaViewAngles );
936 savefile->ReadInt( pain_debounce_time );
937 savefile->ReadInt( pain_delay );
938 savefile->ReadInt( pain_threshold );
940 savefile->ReadInt( num );
941 damageGroups.SetGranularity( 1 );
942 damageGroups.SetNum( num );
943 for( i = 0; i < num; i++ ) {
944 savefile->ReadString( damageGroups[ i ] );
947 savefile->ReadInt( num );
948 damageScale.SetNum( num );
949 for( i = 0; i < num; i++ ) {
950 savefile->ReadFloat( damageScale[ i ] );
953 savefile->ReadBool( use_combat_bbox );
954 head.Restore( savefile );
956 savefile->ReadInt( num );
957 copyJoints.SetNum( num );
958 for( i = 0; i < num; i++ ) {
960 savefile->ReadInt( val );
961 copyJoints[i].mod = static_cast<jointModTransform_t>( val );
962 savefile->ReadJoint( copyJoints[i].from );
963 savefile->ReadJoint( copyJoints[i].to );
966 savefile->ReadJoint( leftEyeJoint );
967 savefile->ReadJoint( rightEyeJoint );
968 savefile->ReadJoint( soundJoint );
970 walkIK.Restore( savefile );
972 savefile->ReadString( animPrefix );
973 savefile->ReadString( painAnim );
975 savefile->ReadInt( blink_anim );
976 savefile->ReadInt( blink_time );
977 savefile->ReadInt( blink_min );
978 savefile->ReadInt( blink_max );
980 savefile->ReadObject( reinterpret_cast<idClass *&>( scriptThread ) );
982 savefile->ReadString( waitState );
984 headAnim.Restore( savefile );
985 torsoAnim.Restore( savefile );
986 legsAnim.Restore( savefile );
988 savefile->ReadBool( allowPain );
989 savefile->ReadBool( allowEyeFocus );
991 savefile->ReadInt( painTime );
993 savefile->ReadInt( num );
994 for ( i = 0; i < num; i++ ) {
995 idAttachInfo &attach = attachments.Alloc();
996 attach.ent.Restore( savefile );
997 savefile->ReadInt( attach.channel );
1000 savefile->ReadBool( finalBoss );
1004 savefile->ReadString( statename );
1005 if ( statename.Length() > 0 ) {
1006 state = GetScriptFunction( statename );
1009 savefile->ReadString( statename );
1010 if ( statename.Length() > 0 ) {
1011 idealState = GetScriptFunction( statename );
1015 savefile->ReadInt(damageCap);
1024 void idActor::Hide( void ) {
1028 idAFEntity_Base::Hide();
1029 if ( head.GetEntity() ) {
1030 head.GetEntity()->Hide();
1033 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1034 next = ent->GetNextTeamEntity();
1035 if ( ent->GetBindMaster() == this ) {
1037 if ( ent->IsType( idLight::Type ) ) {
1038 static_cast<idLight *>( ent )->Off();
1050 void idActor::Show( void ) {
1054 idAFEntity_Base::Show();
1055 if ( head.GetEntity() ) {
1056 head.GetEntity()->Show();
1058 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1059 next = ent->GetNextTeamEntity();
1060 if ( ent->GetBindMaster() == this ) {
1062 if ( ent->IsType( idLight::Type ) ) {
1064 if(!spawnArgs.GetBool("lights_off", "0")) {
1065 static_cast<idLight *>( ent )->On();
1078 idActor::GetDefaultSurfaceType
1081 int idActor::GetDefaultSurfaceType( void ) const {
1082 return SURFTYPE_FLESH;
1087 idActor::ProjectOverlay
1090 void idActor::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1094 idEntity::ProjectOverlay( origin, dir, size, material );
1096 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1097 next = ent->GetNextTeamEntity();
1098 if ( ent->GetBindMaster() == this ) {
1099 if ( ent->fl.takedamage && ent->spawnArgs.GetBool( "bleed" ) ) {
1100 ent->ProjectOverlay( origin, dir, size, material );
1111 bool idActor::LoadAF( void ) {
1114 if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) || !fileName.Length() ) {
1117 af.SetAnimator( GetAnimator() );
1118 return af.Load( this, fileName );
1122 =====================
1124 =====================
1126 void idActor::SetupBody( void ) {
1127 const char *jointname;
1129 animator.ClearAllAnims( gameLocal.time, 0 );
1130 animator.ClearAllJoints();
1132 idEntity *headEnt = head.GetEntity();
1134 jointname = spawnArgs.GetString( "bone_leftEye" );
1135 leftEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1137 jointname = spawnArgs.GetString( "bone_rightEye" );
1138 rightEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1140 // set up the eye height. check if it's specified in the def.
1141 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1142 // if not in the def, then try to base it off the idle animation
1143 int anim = headEnt->GetAnimator()->GetAnim( "idle" );
1144 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1147 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1148 headEnt->GetAnimator()->GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1149 headEnt->GetAnimator()->ClearAllAnims( gameLocal.time, 0 );
1150 headEnt->GetAnimator()->ForceUpdate();
1151 pos += headEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
1152 eyeOffset = pos + modelOffset;
1154 // just base it off the bounding box size
1155 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1158 headAnim.Init( this, headEnt->GetAnimator(), ANIMCHANNEL_ALL );
1160 jointname = spawnArgs.GetString( "bone_leftEye" );
1161 leftEyeJoint = animator.GetJointHandle( jointname );
1163 jointname = spawnArgs.GetString( "bone_rightEye" );
1164 rightEyeJoint = animator.GetJointHandle( jointname );
1166 // set up the eye height. check if it's specified in the def.
1167 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1168 // if not in the def, then try to base it off the idle animation
1169 int anim = animator.GetAnim( "idle" );
1170 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1173 animator.PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1174 animator.GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1175 animator.ClearAllAnims( gameLocal.time, 0 );
1176 animator.ForceUpdate();
1177 eyeOffset = pos + modelOffset;
1179 // just base it off the bounding box size
1180 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1183 headAnim.Init( this, &animator, ANIMCHANNEL_HEAD );
1188 torsoAnim.Init( this, &animator, ANIMCHANNEL_TORSO );
1189 legsAnim.Init( this, &animator, ANIMCHANNEL_LEGS );
1193 =====================
1195 =====================
1197 void idActor::CheckBlink( void ) {
1198 // check if it's time to blink
1199 if ( !blink_anim || ( health <= 0 ) || !allowEyeFocus || ( blink_time > gameLocal.time ) ) {
1203 idEntity *headEnt = head.GetEntity();
1205 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1207 animator.PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1210 // set the next blink time
1211 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
1216 idActor::GetPhysicsToVisualTransform
1219 bool idActor::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
1220 if ( af.IsActive() ) {
1221 af.GetPhysicsToVisualTransform( origin, axis );
1224 origin = modelOffset;
1231 idActor::GetPhysicsToSoundTransform
1234 bool idActor::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
1235 if ( soundJoint != INVALID_JOINT ) {
1236 animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
1237 origin += modelOffset;
1240 origin = GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1246 /***********************************************************************
1248 script state management
1250 ***********************************************************************/
1254 idActor::ShutdownThreads
1257 void idActor::ShutdownThreads( void ) {
1258 headAnim.Shutdown();
1259 torsoAnim.Shutdown();
1260 legsAnim.Shutdown();
1262 if ( scriptThread ) {
1263 scriptThread->EndThread();
1264 scriptThread->PostEventMS( &EV_Remove, 0 );
1265 delete scriptThread;
1266 scriptThread = NULL;
1272 idActor::ShouldConstructScriptObjectAtSpawn
1274 Called during idEntity::Spawn to see if it should construct the script object or not.
1275 Overridden by subclasses that need to spawn the script object themselves.
1278 bool idActor::ShouldConstructScriptObjectAtSpawn( void ) const {
1284 idActor::ConstructScriptObject
1286 Called during idEntity::Spawn. Calls the constructor on the script object.
1287 Can be overridden by subclasses when a thread doesn't need to be allocated.
1290 idThread *idActor::ConstructScriptObject( void ) {
1291 const function_t *constructor;
1293 // make sure we have a scriptObject
1294 if ( !scriptObject.HasObject() ) {
1295 gameLocal.Error( "No scriptobject set on '%s'. Check the '%s' entityDef.", name.c_str(), GetEntityDefName() );
1298 if ( !scriptThread ) {
1299 // create script thread
1300 scriptThread = new idThread();
1301 scriptThread->ManualDelete();
1302 scriptThread->ManualControl();
1303 scriptThread->SetThreadName( name.c_str() );
1305 scriptThread->EndThread();
1308 // call script object's constructor
1309 constructor = scriptObject.GetConstructor();
1310 if ( !constructor ) {
1311 gameLocal.Error( "Missing constructor on '%s' for entity '%s'", scriptObject.GetTypeName(), name.c_str() );
1314 // init the script object's data
1315 scriptObject.ClearObject();
1317 // just set the current function on the script. we'll execute in the subclasses.
1318 scriptThread->CallFunction( this, constructor, true );
1320 return scriptThread;
1324 =====================
1325 idActor::GetScriptFunction
1326 =====================
1328 const function_t *idActor::GetScriptFunction( const char *funcname ) {
1329 const function_t *func;
1331 func = scriptObject.GetFunction( funcname );
1333 scriptThread->Error( "Unknown function '%s' in '%s'", funcname, scriptObject.GetTypeName() );
1340 =====================
1342 =====================
1344 void idActor::SetState( const function_t *newState ) {
1346 gameLocal.Error( "idActor::SetState: Null state" );
1349 if ( ai_debugScript.GetInteger() == entityNumber ) {
1350 gameLocal.Printf( "%d: %s: State: %s\n", gameLocal.time, name.c_str(), newState->Name() );
1355 scriptThread->CallFunction( this, state, true );
1359 =====================
1361 =====================
1363 void idActor::SetState( const char *statename ) {
1364 const function_t *newState;
1366 newState = GetScriptFunction( statename );
1367 SetState( newState );
1371 =====================
1372 idActor::UpdateScript
1373 =====================
1375 void idActor::UpdateScript( void ) {
1378 if ( ai_debugScript.GetInteger() == entityNumber ) {
1379 scriptThread->EnableDebugInfo();
1381 scriptThread->DisableDebugInfo();
1384 // a series of state changes can happen in a single frame.
1385 // this loop limits them in case we've entered an infinite loop.
1386 for( i = 0; i < 20; i++ ) {
1387 if ( idealState != state ) {
1388 SetState( idealState );
1391 // don't call script until it's done waiting
1392 if ( scriptThread->IsWaiting() ) {
1396 scriptThread->Execute();
1397 if ( idealState == state ) {
1403 scriptThread->Warning( "idActor::UpdateScript: exited loop to prevent lockup" );
1407 /***********************************************************************
1411 ***********************************************************************/
1414 =====================
1416 =====================
1418 void idActor::SetFOV( float fov ) {
1419 fovDot = (float)cos( DEG2RAD( fov * 0.5f ) );
1423 =====================
1424 idActor::SetEyeHeight
1425 =====================
1427 void idActor::SetEyeHeight( float height ) {
1428 eyeOffset.z = height;
1432 =====================
1434 =====================
1436 float idActor::EyeHeight( void ) const {
1441 =====================
1443 =====================
1445 idVec3 idActor::EyeOffset( void ) const {
1446 return GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1450 =====================
1451 idActor::GetEyePosition
1452 =====================
1454 idVec3 idActor::GetEyePosition( void ) const {
1455 return GetPhysics()->GetOrigin() + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
1459 =====================
1461 =====================
1463 void idActor::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
1464 origin = GetEyePosition();
1469 =====================
1471 =====================
1473 bool idActor::CheckFOV( const idVec3 &pos ) const {
1474 if ( fovDot == 1.0f ) {
1481 delta = pos - GetEyePosition();
1483 // get our gravity normal
1484 const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
1486 // infinite vertical vision, so project it onto our orientation plane
1487 delta -= gravityDir * ( gravityDir * delta );
1490 dot = viewAxis[ 0 ] * delta;
1492 return ( dot >= fovDot );
1496 =====================
1498 =====================
1500 bool idActor::CanSee( idEntity *ent, bool useFov ) const {
1505 if ( ent->IsHidden() ) {
1509 if ( ent->IsType( idActor::Type ) ) {
1510 toPos = ( ( idActor * )ent )->GetEyePosition();
1512 toPos = ent->GetPhysics()->GetOrigin();
1515 if ( useFov && !CheckFOV( toPos ) ) {
1519 eye = GetEyePosition();
1521 gameLocal.clip.TracePoint( tr, eye, toPos, MASK_OPAQUE, this );
1522 if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == ent ) ) {
1530 =====================
1531 idActor::PointVisible
1532 =====================
1534 bool idActor::PointVisible( const idVec3 &point ) const {
1538 start = GetEyePosition();
1542 gameLocal.clip.TracePoint( results, start, end, MASK_OPAQUE, this );
1543 return ( results.fraction >= 1.0f );
1547 =====================
1548 idActor::GetAIAimTargets
1550 Returns positions for the AI to aim at.
1551 =====================
1553 void idActor::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
1554 headPos = lastSightPos + EyeOffset();
1555 chestPos = ( headPos + lastSightPos + GetPhysics()->GetBounds().GetCenter() ) * 0.5f;
1559 =====================
1560 idActor::GetRenderView
1561 =====================
1563 renderView_t *idActor::GetRenderView() {
1564 renderView_t *rv = idEntity::GetRenderView();
1565 rv->viewaxis = viewAxis;
1566 rv->vieworg = GetEyePosition();
1570 /***********************************************************************
1574 ***********************************************************************/
1578 idActor::SetCombatModel
1581 void idActor::SetCombatModel( void ) {
1582 idAFAttachment *headEnt;
1584 if ( !use_combat_bbox ) {
1585 if ( combatModel ) {
1586 combatModel->Unlink();
1587 combatModel->LoadModel( modelDefHandle );
1589 combatModel = new idClipModel( modelDefHandle );
1592 headEnt = head.GetEntity();
1594 headEnt->SetCombatModel();
1601 idActor::GetCombatModel
1604 idClipModel *idActor::GetCombatModel( void ) const {
1613 void idActor::LinkCombat( void ) {
1614 idAFAttachment *headEnt;
1616 if ( fl.hidden || use_combat_bbox ) {
1620 if ( combatModel ) {
1621 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1623 headEnt = head.GetEntity();
1625 headEnt->LinkCombat();
1631 idActor::UnlinkCombat
1634 void idActor::UnlinkCombat( void ) {
1635 idAFAttachment *headEnt;
1637 if ( combatModel ) {
1638 combatModel->Unlink();
1640 headEnt = head.GetEntity();
1642 headEnt->UnlinkCombat();
1648 idActor::StartRagdoll
1651 bool idActor::StartRagdoll( void ) {
1652 float slomoStart, slomoEnd;
1653 float jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd;
1654 float contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd;
1657 if ( !af.IsLoaded() ) {
1661 // if the AF is already active
1662 if ( af.IsActive() ) {
1666 // disable the monster bounding box
1667 GetPhysics()->DisableClip();
1669 // start using the AF
1670 af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
1672 slomoStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoStart", "-1.6" );
1673 slomoEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoEnd", "0.8" );
1675 // do the first part of the death in slow motion
1676 af.GetPhysics()->SetTimeScaleRamp( slomoStart, slomoEnd );
1678 jointFrictionDent = spawnArgs.GetFloat( "ragdoll_jointFrictionDent", "0.1" );
1679 jointFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionStart", "0.2" );
1680 jointFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionEnd", "1.2" );
1682 // set joint friction dent
1683 af.GetPhysics()->SetJointFrictionDent( jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd );
1685 contactFrictionDent = spawnArgs.GetFloat( "ragdoll_contactFrictionDent", "0.1" );
1686 contactFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionStart", "1.0" );
1687 contactFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionEnd", "2.0" );
1689 // set contact friction dent
1690 af.GetPhysics()->SetContactFrictionDent( contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd );
1692 // drop any items the actor is holding
1693 idMoveableItem::DropItems( this, "death", NULL );
1695 // drop any articulated figures the actor is holding
1696 idAFEntity_Base::DropAFs( this, "death", NULL );
1698 RemoveAttachments();
1705 idActor::StopRagdoll
1708 void idActor::StopRagdoll( void ) {
1709 if ( af.IsActive() ) {
1716 idActor::UpdateAnimationControllers
1719 bool idActor::UpdateAnimationControllers( void ) {
1721 if ( af.IsActive() ) {
1722 return idAFEntity_Base::UpdateAnimationControllers();
1724 animator.ClearAFPose();
1727 if ( walkIK.IsInitialized() ) {
1737 idActor::RemoveAttachments
1740 void idActor::RemoveAttachments( void ) {
1744 // remove any attached entities
1745 for( i = 0; i < attachments.Num(); i++ ) {
1746 ent = attachments[ i ].ent.GetEntity();
1747 if ( ent && ent->spawnArgs.GetBool( "remove" ) ) {
1748 ent->PostEventMS( &EV_Remove, 0 );
1758 void idActor::Attach( idEntity *ent ) {
1761 jointHandle_t joint;
1763 idAttachInfo &attach = attachments.Alloc();
1764 idAngles angleOffset;
1765 idVec3 originOffset;
1767 jointName = ent->spawnArgs.GetString( "joint" );
1768 joint = animator.GetJointHandle( jointName );
1769 if ( joint == INVALID_JOINT ) {
1770 gameLocal.Error( "Joint '%s' not found for attaching '%s' on '%s'", jointName.c_str(), ent->GetClassname(), name.c_str() );
1773 angleOffset = ent->spawnArgs.GetAngles( "angles" );
1774 originOffset = ent->spawnArgs.GetVector( "origin" );
1776 attach.channel = animator.GetChannelForJoint( joint );
1777 GetJointWorldTransform( joint, gameLocal.time, origin, axis );
1780 ent->SetOrigin( origin + originOffset * renderEntity.axis );
1781 idMat3 rotate = angleOffset.ToMat3();
1782 idMat3 newAxis = rotate * axis;
1783 ent->SetAxis( newAxis );
1784 ent->BindToJoint( this, joint, true );
1785 ent->cinematic = cinematic;
1793 void idActor::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
1794 GetPhysics()->SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
1795 GetPhysics()->SetLinearVelocity( vec3_origin );
1797 viewAxis = angles.ToMat3();
1801 if ( !IsHidden() ) {
1802 // kill anything at the new position
1803 gameLocal.KillBox( this );
1809 idActor::GetDeltaViewAngles
1812 const idAngles &idActor::GetDeltaViewAngles( void ) const {
1813 return deltaViewAngles;
1818 idActor::SetDeltaViewAngles
1821 void idActor::SetDeltaViewAngles( const idAngles &delta ) {
1822 deltaViewAngles = delta;
1830 bool idActor::HasEnemies( void ) const {
1833 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1834 if ( !ent->fl.hidden ) {
1844 idActor::ClosestEnemyToPoint
1847 idActor *idActor::ClosestEnemyToPoint( const idVec3 &pos ) {
1850 float bestDistSquared;
1854 bestDistSquared = idMath::INFINITY;
1856 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1857 if ( ent->fl.hidden ) {
1860 delta = ent->GetPhysics()->GetOrigin() - pos;
1861 distSquared = delta.LengthSqr();
1862 if ( distSquared < bestDistSquared ) {
1864 bestDistSquared = distSquared;
1873 idActor::EnemyWithMostHealth
1876 idActor *idActor::EnemyWithMostHealth() {
1882 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1883 if ( !ent->fl.hidden && ( ent->health > most ) ) {
1896 bool idActor::OnLadder( void ) const {
1902 idActor::GetAASLocation
1905 void idActor::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
1909 GetFloorPos( 64.0f, pos );
1915 size = aas->GetSettings()->boundingBoxes[0][1];
1920 areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK );
1922 aas->PushPointIntoAreaNum( areaNum, pos );
1926 /***********************************************************************
1930 ***********************************************************************/
1933 =====================
1934 idActor::SetAnimState
1935 =====================
1937 void idActor::SetAnimState( int channel, const char *statename, int blendFrames ) {
1938 const function_t *func;
1940 func = scriptObject.GetFunction( statename );
1943 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
1947 case ANIMCHANNEL_HEAD :
1948 headAnim.SetState( statename, blendFrames );
1949 allowEyeFocus = true;
1952 case ANIMCHANNEL_TORSO :
1953 torsoAnim.SetState( statename, blendFrames );
1954 legsAnim.Enable( blendFrames );
1956 allowEyeFocus = true;
1959 case ANIMCHANNEL_LEGS :
1960 legsAnim.SetState( statename, blendFrames );
1961 torsoAnim.Enable( blendFrames );
1963 allowEyeFocus = true;
1967 gameLocal.Error( "idActor::SetAnimState: Unknown anim group" );
1973 =====================
1974 idActor::GetAnimState
1975 =====================
1977 const char *idActor::GetAnimState( int channel ) const {
1979 case ANIMCHANNEL_HEAD :
1980 return headAnim.state;
1983 case ANIMCHANNEL_TORSO :
1984 return torsoAnim.state;
1987 case ANIMCHANNEL_LEGS :
1988 return legsAnim.state;
1992 gameLocal.Error( "idActor::GetAnimState: Unknown anim group" );
1999 =====================
2000 idActor::InAnimState
2001 =====================
2003 bool idActor::InAnimState( int channel, const char *statename ) const {
2005 case ANIMCHANNEL_HEAD :
2006 if ( headAnim.state == statename ) {
2011 case ANIMCHANNEL_TORSO :
2012 if ( torsoAnim.state == statename ) {
2017 case ANIMCHANNEL_LEGS :
2018 if ( legsAnim.state == statename ) {
2024 gameLocal.Error( "idActor::InAnimState: Unknown anim group" );
2032 =====================
2034 =====================
2036 const char *idActor::WaitState( void ) const {
2037 if ( waitState.Length() ) {
2045 =====================
2046 idActor::SetWaitState
2047 =====================
2049 void idActor::SetWaitState( const char *_waitstate ) {
2050 waitState = _waitstate;
2054 =====================
2055 idActor::UpdateAnimState
2056 =====================
2058 void idActor::UpdateAnimState( void ) {
2059 headAnim.UpdateState();
2060 torsoAnim.UpdateState();
2061 legsAnim.UpdateState();
2065 =====================
2067 =====================
2069 int idActor::GetAnim( int channel, const char *animname ) {
2072 idAnimator *animatorPtr;
2074 if ( channel == ANIMCHANNEL_HEAD ) {
2075 if ( !head.GetEntity() ) {
2078 animatorPtr = head.GetEntity()->GetAnimator();
2080 animatorPtr = &animator;
2083 if ( animPrefix.Length() ) {
2084 temp = va( "%s_%s", animPrefix.c_str(), animname );
2085 anim = animatorPtr->GetAnim( temp );
2091 anim = animatorPtr->GetAnim( animname );
2098 idActor::SyncAnimChannels
2101 void idActor::SyncAnimChannels( int channel, int syncToChannel, int blendFrames ) {
2102 idAnimator *headAnimator;
2103 idAFAttachment *headEnt;
2105 idAnimBlend *syncAnim;
2110 blendTime = FRAME2MS( blendFrames );
2111 if ( channel == ANIMCHANNEL_HEAD ) {
2112 headEnt = head.GetEntity();
2114 headAnimator = headEnt->GetAnimator();
2115 syncAnim = animator.CurrentAnim( syncToChannel );
2117 anim = headAnimator->GetAnim( syncAnim->AnimFullName() );
2119 anim = headAnimator->GetAnim( syncAnim->AnimName() );
2122 cycle = animator.CurrentAnim( syncToChannel )->GetCycleCount();
2123 starttime = animator.CurrentAnim( syncToChannel )->GetStartTime();
2124 headAnimator->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, blendTime );
2125 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
2126 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetStartTime( starttime );
2128 headEnt->PlayIdleAnim( blendTime );
2132 } else if ( syncToChannel == ANIMCHANNEL_HEAD ) {
2133 headEnt = head.GetEntity();
2135 headAnimator = headEnt->GetAnimator();
2136 syncAnim = headAnimator->CurrentAnim( ANIMCHANNEL_ALL );
2138 anim = GetAnim( channel, syncAnim->AnimFullName() );
2140 anim = GetAnim( channel, syncAnim->AnimName() );
2143 cycle = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetCycleCount();
2144 starttime = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetStartTime();
2145 animator.PlayAnim( channel, anim, gameLocal.time, blendTime );
2146 animator.CurrentAnim( channel )->SetCycleCount( cycle );
2147 animator.CurrentAnim( channel )->SetStartTime( starttime );
2152 animator.SyncAnimChannels( channel, syncToChannel, gameLocal.time, blendTime );
2156 /***********************************************************************
2160 ***********************************************************************/
2167 void idActor::Gib( const idVec3 &dir, const char *damageDefName ) {
2168 // no gibbing in multiplayer - by self damage or by moving objects
2169 if ( gameLocal.isMultiplayer ) {
2176 idAFEntity_Gibbable::Gib( dir, damageDefName );
2177 if ( head.GetEntity() ) {
2178 head.GetEntity()->Hide();
2180 StopSound( SND_CHANNEL_VOICE, false );
2188 this entity that is being damaged
2189 inflictor entity that is causing the damage
2190 attacker entity that caused the inflictor to damage targ
2191 example: this=monster, inflictor=rocket, attacker=player
2193 dir direction of the attack for knockback in global space
2194 point point at which the damage is being inflicted, used for headshots
2195 damage amount of damage being inflicted
2197 inflictor, attacker, dir, and point can be NULL for environmental effects
2199 Bleeding wounds and surface overlays are applied in the collision code that
2203 void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
2204 const char *damageDefName, const float damageScale, const int location ) {
2205 if ( !fl.takedamage ) {
2210 inflictor = gameLocal.world;
2213 attacker = gameLocal.world;
2217 SetTimeState ts( timeGroup );
2219 // Helltime boss is immune to all projectiles except the helltime killer
2220 if ( finalBoss && idStr::Icmp(inflictor->GetEntityDefName(), "projectile_helltime_killer") ) {
2224 // Maledict is immume to the falling asteroids
2225 if ( !idStr::Icmp( GetEntityDefName(), "monster_boss_d3xp_maledict" ) &&
2226 (!idStr::Icmp( damageDefName, "damage_maledict_asteroid" ) || !idStr::Icmp( damageDefName, "damage_maledict_asteroid_splash" ) ) ) {
2230 if ( finalBoss && !inflictor->IsType( idSoulCubeMissile::Type ) ) {
2235 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
2237 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
2240 int damage = damageDef->GetInt( "damage" ) * damageScale;
2241 damage = GetDamageForLocation( damage, location );
2243 // inform the attacker that they hit someone
2244 attacker->DamageFeedback( this, inflictor, damage );
2249 //Check the health against any damage cap that is currently set
2250 if(damageCap >= 0 && health < damageCap) {
2255 if ( health <= 0 ) {
2256 if ( health < -999 ) {
2259 Killed( inflictor, attacker, damage, dir, location );
2260 if ( ( health < -20 ) && spawnArgs.GetBool( "gib" ) && damageDef->GetBool( "gib" ) ) {
2261 Gib( dir, damageDefName );
2264 Pain( inflictor, attacker, damage, dir, location );
2267 // don't accumulate knockback
2268 if ( af.IsLoaded() ) {
2272 // physics is turned off by calling af.Rest()
2273 BecomeActive( TH_PHYSICS );
2279 =====================
2281 =====================
2283 void idActor::ClearPain( void ) {
2284 pain_debounce_time = 0;
2288 =====================
2290 =====================
2292 bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
2293 if ( af.IsLoaded() ) {
2297 // physics is turned off by calling af.Rest()
2298 BecomeActive( TH_PHYSICS );
2301 if ( gameLocal.time < pain_debounce_time ) {
2305 // don't play pain sounds more than necessary
2306 pain_debounce_time = gameLocal.time + pain_delay;
2308 if ( health > 75 ) {
2309 StartSound( "snd_pain_small", SND_CHANNEL_VOICE, 0, false, NULL );
2310 } else if ( health > 50 ) {
2311 StartSound( "snd_pain_medium", SND_CHANNEL_VOICE, 0, false, NULL );
2312 } else if ( health > 25 ) {
2313 StartSound( "snd_pain_large", SND_CHANNEL_VOICE, 0, false, NULL );
2315 StartSound( "snd_pain_huge", SND_CHANNEL_VOICE, 0, false, NULL );
2318 if ( !allowPain || ( gameLocal.time < painTime ) ) {
2319 // don't play a pain anim
2323 if ( pain_threshold && ( damage < pain_threshold ) ) {
2327 // set the pain anim
2328 idStr damageGroup = GetDamageGroup( location );
2331 if ( animPrefix.Length() ) {
2332 if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2333 sprintf( painAnim, "%s_pain_%s", animPrefix.c_str(), damageGroup.c_str() );
2334 if ( !animator.HasAnim( painAnim ) ) {
2335 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2336 if ( !animator.HasAnim( painAnim ) ) {
2342 if ( !painAnim.Length() ) {
2343 sprintf( painAnim, "%s_pain", animPrefix.c_str() );
2344 if ( !animator.HasAnim( painAnim ) ) {
2348 } else if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2349 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2350 if ( !animator.HasAnim( painAnim ) ) {
2351 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2352 if ( !animator.HasAnim( painAnim ) ) {
2358 if ( !painAnim.Length() ) {
2362 if ( g_debugDamage.GetBool() ) {
2363 gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ),
2364 damageGroup.c_str(), painAnim.c_str() );
2371 =====================
2373 =====================
2375 void idActor::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
2376 idAFEntity_Gibbable::SpawnGibs( dir, damageDefName );
2377 RemoveAttachments();
2381 =====================
2382 idActor::SetupDamageGroups
2384 FIXME: only store group names once and store an index for each joint
2385 =====================
2387 void idActor::SetupDamageGroups( void ) {
2389 const idKeyValue *arg;
2391 idList<jointHandle_t> jointList;
2395 // create damage zones
2396 damageGroups.SetNum( animator.NumJoints() );
2397 arg = spawnArgs.MatchPrefix( "damage_zone ", NULL );
2399 groupname = arg->GetKey();
2400 groupname.Strip( "damage_zone " );
2401 animator.GetJointList( arg->GetValue(), jointList );
2402 for( i = 0; i < jointList.Num(); i++ ) {
2403 jointnum = jointList[ i ];
2404 damageGroups[ jointnum ] = groupname;
2407 arg = spawnArgs.MatchPrefix( "damage_zone ", arg );
2410 // initilize the damage zones to normal damage
2411 damageScale.SetNum( animator.NumJoints() );
2412 for( i = 0; i < damageScale.Num(); i++ ) {
2413 damageScale[ i ] = 1.0f;
2416 // set the percentage on damage zones
2417 arg = spawnArgs.MatchPrefix( "damage_scale ", NULL );
2419 scale = atof( arg->GetValue() );
2420 groupname = arg->GetKey();
2421 groupname.Strip( "damage_scale " );
2422 for( i = 0; i < damageScale.Num(); i++ ) {
2423 if ( damageGroups[ i ] == groupname ) {
2424 damageScale[ i ] = scale;
2427 arg = spawnArgs.MatchPrefix( "damage_scale ", arg );
2432 =====================
2433 idActor::GetDamageForLocation
2434 =====================
2436 int idActor::GetDamageForLocation( int damage, int location ) {
2437 if ( ( location < 0 ) || ( location >= damageScale.Num() ) ) {
2441 return (int)ceil( damage * damageScale[ location ] );
2445 =====================
2446 idActor::GetDamageGroup
2447 =====================
2449 const char *idActor::GetDamageGroup( int location ) {
2450 if ( ( location < 0 ) || ( location >= damageGroups.Num() ) ) {
2454 return damageGroups[ location ];
2458 /***********************************************************************
2462 ***********************************************************************/
2465 =====================
2466 idActor::Event_EnableEyeFocus
2467 =====================
2469 void idActor::PlayFootStepSound( void ) {
2470 const char *sound = NULL;
2471 const idMaterial *material;
2473 if ( !GetPhysics()->HasGroundContacts() ) {
2477 // start footstep sound based on material type
2478 material = GetPhysics()->GetContact( 0 ).material;
2479 if ( material != NULL ) {
2480 sound = spawnArgs.GetString( va( "snd_footstep_%s", gameLocal.sufaceTypeNames[ material->GetSurfaceType() ] ) );
2482 if ( *sound == '\0' ) {
2483 sound = spawnArgs.GetString( "snd_footstep" );
2485 if ( *sound != '\0' ) {
2486 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
2491 =====================
2492 idActor::Event_EnableEyeFocus
2493 =====================
2495 void idActor::Event_EnableEyeFocus( void ) {
2496 allowEyeFocus = true;
2497 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
2501 =====================
2502 idActor::Event_DisableEyeFocus
2503 =====================
2505 void idActor::Event_DisableEyeFocus( void ) {
2506 allowEyeFocus = false;
2508 idEntity *headEnt = head.GetEntity();
2510 headEnt->GetAnimator()->Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2512 animator.Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2518 idActor::Event_Footstep
2521 void idActor::Event_Footstep( void ) {
2522 PlayFootStepSound();
2526 =====================
2527 idActor::Event_EnableWalkIK
2528 =====================
2530 void idActor::Event_EnableWalkIK( void ) {
2535 =====================
2536 idActor::Event_DisableWalkIK
2537 =====================
2539 void idActor::Event_DisableWalkIK( void ) {
2540 walkIK.DisableAll();
2544 =====================
2545 idActor::Event_EnableLegIK
2546 =====================
2548 void idActor::Event_EnableLegIK( int num ) {
2549 walkIK.EnableLeg( num );
2553 =====================
2554 idActor::Event_DisableLegIK
2555 =====================
2557 void idActor::Event_DisableLegIK( int num ) {
2558 walkIK.DisableLeg( num );
2562 =====================
2563 idActor::Event_PreventPain
2564 =====================
2566 void idActor::Event_PreventPain( float duration ) {
2567 painTime = gameLocal.time + SEC2MS( duration );
2572 idActor::Event_DisablePain
2575 void idActor::Event_DisablePain( void ) {
2581 idActor::Event_EnablePain
2584 void idActor::Event_EnablePain( void ) {
2589 =====================
2590 idActor::Event_GetPainAnim
2591 =====================
2593 void idActor::Event_GetPainAnim( void ) {
2594 if ( !painAnim.Length() ) {
2595 idThread::ReturnString( "pain" );
2597 idThread::ReturnString( painAnim );
2602 =====================
2603 idActor::Event_SetAnimPrefix
2604 =====================
2606 void idActor::Event_SetAnimPrefix( const char *prefix ) {
2607 animPrefix = prefix;
2612 idActor::Event_StopAnim
2615 void idActor::Event_StopAnim( int channel, int frames ) {
2617 case ANIMCHANNEL_HEAD :
2618 headAnim.StopAnim( frames );
2621 case ANIMCHANNEL_TORSO :
2622 torsoAnim.StopAnim( frames );
2625 case ANIMCHANNEL_LEGS :
2626 legsAnim.StopAnim( frames );
2630 gameLocal.Error( "Unknown anim group" );
2637 idActor::Event_PlayAnim
2640 void idActor::Event_PlayAnim( int channel, const char *animname ) {
2645 anim = GetAnim( channel, animname );
2647 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2648 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2650 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2652 idThread::ReturnInt( 0 );
2657 case ANIMCHANNEL_HEAD :
2658 headEnt = head.GetEntity();
2660 headAnim.idleAnim = false;
2661 headAnim.PlayAnim( anim );
2662 flags = headAnim.GetAnimFlags();
2663 if ( !flags.prevent_idle_override ) {
2664 if ( torsoAnim.IsIdle() ) {
2665 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2666 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2667 if ( legsAnim.IsIdle() ) {
2668 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2669 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2676 case ANIMCHANNEL_TORSO :
2677 torsoAnim.idleAnim = false;
2678 torsoAnim.PlayAnim( anim );
2679 flags = torsoAnim.GetAnimFlags();
2680 if ( !flags.prevent_idle_override ) {
2681 if ( headAnim.IsIdle() ) {
2682 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2683 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2685 if ( legsAnim.IsIdle() ) {
2686 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2687 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2692 case ANIMCHANNEL_LEGS :
2693 legsAnim.idleAnim = false;
2694 legsAnim.PlayAnim( anim );
2695 flags = legsAnim.GetAnimFlags();
2696 if ( !flags.prevent_idle_override ) {
2697 if ( torsoAnim.IsIdle() ) {
2698 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2699 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2700 if ( headAnim.IsIdle() ) {
2701 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2702 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2709 gameLocal.Error( "Unknown anim group" );
2712 idThread::ReturnInt( 1 );
2717 idActor::Event_PlayCycle
2720 void idActor::Event_PlayCycle( int channel, const char *animname ) {
2724 anim = GetAnim( channel, animname );
2726 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2727 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2729 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2731 idThread::ReturnInt( false );
2736 case ANIMCHANNEL_HEAD :
2737 headAnim.idleAnim = false;
2738 headAnim.CycleAnim( anim );
2739 flags = headAnim.GetAnimFlags();
2740 if ( !flags.prevent_idle_override ) {
2741 if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2742 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2743 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2744 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2745 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2750 case ANIMCHANNEL_TORSO :
2751 torsoAnim.idleAnim = false;
2752 torsoAnim.CycleAnim( anim );
2753 flags = torsoAnim.GetAnimFlags();
2754 if ( !flags.prevent_idle_override ) {
2755 if ( headAnim.IsIdle() ) {
2756 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2757 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2759 if ( legsAnim.IsIdle() ) {
2760 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2761 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2766 case ANIMCHANNEL_LEGS :
2767 legsAnim.idleAnim = false;
2768 legsAnim.CycleAnim( anim );
2769 flags = legsAnim.GetAnimFlags();
2770 if ( !flags.prevent_idle_override ) {
2771 if ( torsoAnim.IsIdle() ) {
2772 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2773 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2774 if ( headAnim.IsIdle() ) {
2775 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2776 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2783 gameLocal.Error( "Unknown anim group" );
2786 idThread::ReturnInt( true );
2791 idActor::Event_IdleAnim
2794 void idActor::Event_IdleAnim( int channel, const char *animname ) {
2797 anim = GetAnim( channel, animname );
2799 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2800 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2802 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2806 case ANIMCHANNEL_HEAD :
2807 headAnim.BecomeIdle();
2810 case ANIMCHANNEL_TORSO :
2811 torsoAnim.BecomeIdle();
2814 case ANIMCHANNEL_LEGS :
2815 legsAnim.BecomeIdle();
2819 gameLocal.Error( "Unknown anim group" );
2822 idThread::ReturnInt( false );
2827 case ANIMCHANNEL_HEAD :
2828 headAnim.BecomeIdle();
2829 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2830 // don't sync to torso body if it doesn't override idle anims
2831 headAnim.CycleAnim( anim );
2832 } else if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2833 // everything is idle, so play the anim on the head and copy it to the torso and legs
2834 headAnim.CycleAnim( anim );
2835 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2836 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2837 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2838 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2839 } else if ( torsoAnim.IsIdle() ) {
2840 // sync the head and torso to the legs
2841 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, headAnim.animBlendFrames );
2842 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2843 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2845 // sync the head to the torso
2846 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, headAnim.animBlendFrames );
2850 case ANIMCHANNEL_TORSO :
2851 torsoAnim.BecomeIdle();
2852 if ( legsAnim.GetAnimFlags().prevent_idle_override ) {
2853 // don't sync to legs if legs anim doesn't override idle anims
2854 torsoAnim.CycleAnim( anim );
2855 } else if ( legsAnim.IsIdle() ) {
2856 // play the anim in both legs and torso
2857 torsoAnim.CycleAnim( anim );
2858 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2859 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2861 // sync the anim to the legs
2862 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2865 if ( headAnim.IsIdle() ) {
2866 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2870 case ANIMCHANNEL_LEGS :
2871 legsAnim.BecomeIdle();
2872 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2873 // don't sync to torso if torso anim doesn't override idle anims
2874 legsAnim.CycleAnim( anim );
2875 } else if ( torsoAnim.IsIdle() ) {
2876 // play the anim in both legs and torso
2877 legsAnim.CycleAnim( anim );
2878 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2879 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2880 if ( headAnim.IsIdle() ) {
2881 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2884 // sync the anim to the torso
2885 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, legsAnim.animBlendFrames );
2890 gameLocal.Error( "Unknown anim group" );
2893 idThread::ReturnInt( true );
2898 idActor::Event_SetSyncedAnimWeight
2901 void idActor::Event_SetSyncedAnimWeight( int channel, int anim, float weight ) {
2904 headEnt = head.GetEntity();
2906 case ANIMCHANNEL_HEAD :
2908 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2910 animator.CurrentAnim( ANIMCHANNEL_HEAD )->SetSyncedAnimWeight( anim, weight );
2912 if ( torsoAnim.IsIdle() ) {
2913 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2914 if ( legsAnim.IsIdle() ) {
2915 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2920 case ANIMCHANNEL_TORSO :
2921 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2922 if ( legsAnim.IsIdle() ) {
2923 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2925 if ( headEnt && headAnim.IsIdle() ) {
2926 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2930 case ANIMCHANNEL_LEGS :
2931 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2932 if ( torsoAnim.IsIdle() ) {
2933 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2934 if ( headEnt && headAnim.IsIdle() ) {
2935 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2941 gameLocal.Error( "Unknown anim group" );
2947 idActor::Event_OverrideAnim
2950 void idActor::Event_OverrideAnim( int channel ) {
2952 case ANIMCHANNEL_HEAD :
2954 if ( !torsoAnim.IsIdle() ) {
2955 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2957 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2961 case ANIMCHANNEL_TORSO :
2962 torsoAnim.Disable();
2963 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2964 if ( headAnim.IsIdle() ) {
2965 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2969 case ANIMCHANNEL_LEGS :
2971 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2975 gameLocal.Error( "Unknown anim group" );
2982 idActor::Event_EnableAnim
2985 void idActor::Event_EnableAnim( int channel, int blendFrames ) {
2987 case ANIMCHANNEL_HEAD :
2988 headAnim.Enable( blendFrames );
2991 case ANIMCHANNEL_TORSO :
2992 torsoAnim.Enable( blendFrames );
2995 case ANIMCHANNEL_LEGS :
2996 legsAnim.Enable( blendFrames );
3000 gameLocal.Error( "Unknown anim group" );
3007 idActor::Event_SetBlendFrames
3010 void idActor::Event_SetBlendFrames( int channel, int blendFrames ) {
3012 case ANIMCHANNEL_HEAD :
3013 headAnim.animBlendFrames = blendFrames;
3014 headAnim.lastAnimBlendFrames = blendFrames;
3017 case ANIMCHANNEL_TORSO :
3018 torsoAnim.animBlendFrames = blendFrames;
3019 torsoAnim.lastAnimBlendFrames = blendFrames;
3022 case ANIMCHANNEL_LEGS :
3023 legsAnim.animBlendFrames = blendFrames;
3024 legsAnim.lastAnimBlendFrames = blendFrames;
3028 gameLocal.Error( "Unknown anim group" );
3035 idActor::Event_GetBlendFrames
3038 void idActor::Event_GetBlendFrames( int channel ) {
3040 case ANIMCHANNEL_HEAD :
3041 idThread::ReturnInt( headAnim.animBlendFrames );
3044 case ANIMCHANNEL_TORSO :
3045 idThread::ReturnInt( torsoAnim.animBlendFrames );
3048 case ANIMCHANNEL_LEGS :
3049 idThread::ReturnInt( legsAnim.animBlendFrames );
3053 gameLocal.Error( "Unknown anim group" );
3060 idActor::Event_AnimState
3063 void idActor::Event_AnimState( int channel, const char *statename, int blendFrames ) {
3064 SetAnimState( channel, statename, blendFrames );
3069 idActor::Event_GetAnimState
3072 void idActor::Event_GetAnimState( int channel ) {
3075 state = GetAnimState( channel );
3076 idThread::ReturnString( state );
3081 idActor::Event_InAnimState
3084 void idActor::Event_InAnimState( int channel, const char *statename ) {
3087 instate = InAnimState( channel, statename );
3088 idThread::ReturnInt( instate );
3093 idActor::Event_FinishAction
3096 void idActor::Event_FinishAction( const char *actionname ) {
3097 if ( waitState == actionname ) {
3104 idActor::Event_AnimDone
3107 void idActor::Event_AnimDone( int channel, int blendFrames ) {
3111 case ANIMCHANNEL_HEAD :
3112 result = headAnim.AnimDone( blendFrames );
3113 idThread::ReturnInt( result );
3116 case ANIMCHANNEL_TORSO :
3117 result = torsoAnim.AnimDone( blendFrames );
3118 idThread::ReturnInt( result );
3121 case ANIMCHANNEL_LEGS :
3122 result = legsAnim.AnimDone( blendFrames );
3123 idThread::ReturnInt( result );
3127 gameLocal.Error( "Unknown anim group" );
3133 idActor::Event_HasAnim
3136 void idActor::Event_HasAnim( int channel, const char *animname ) {
3137 if ( GetAnim( channel, animname ) != NULL ) {
3138 idThread::ReturnFloat( 1.0f );
3140 idThread::ReturnFloat( 0.0f );
3146 idActor::Event_CheckAnim
3149 void idActor::Event_CheckAnim( int channel, const char *animname ) {
3150 if ( !GetAnim( channel, animname ) ) {
3151 if ( animPrefix.Length() ) {
3152 gameLocal.Error( "Can't find anim '%s_%s' for '%s'", animPrefix.c_str(), animname, name.c_str() );
3154 gameLocal.Error( "Can't find anim '%s' for '%s'", animname, name.c_str() );
3161 idActor::Event_ChooseAnim
3164 void idActor::Event_ChooseAnim( int channel, const char *animname ) {
3167 anim = GetAnim( channel, animname );
3169 if ( channel == ANIMCHANNEL_HEAD ) {
3170 if ( head.GetEntity() ) {
3171 idThread::ReturnString( head.GetEntity()->GetAnimator()->AnimFullName( anim ) );
3175 idThread::ReturnString( animator.AnimFullName( anim ) );
3180 idThread::ReturnString( "" );
3185 idActor::Event_AnimLength
3188 void idActor::Event_AnimLength( int channel, const char *animname ) {
3191 anim = GetAnim( channel, animname );
3193 if ( channel == ANIMCHANNEL_HEAD ) {
3194 if ( head.GetEntity() ) {
3195 idThread::ReturnFloat( MS2SEC( head.GetEntity()->GetAnimator()->AnimLength( anim ) ) );
3199 idThread::ReturnFloat( MS2SEC( animator.AnimLength( anim ) ) );
3204 idThread::ReturnFloat( 0.0f );
3209 idActor::Event_AnimDistance
3212 void idActor::Event_AnimDistance( int channel, const char *animname ) {
3215 anim = GetAnim( channel, animname );
3217 if ( channel == ANIMCHANNEL_HEAD ) {
3218 if ( head.GetEntity() ) {
3219 idThread::ReturnFloat( head.GetEntity()->GetAnimator()->TotalMovementDelta( anim ).Length() );
3223 idThread::ReturnFloat( animator.TotalMovementDelta( anim ).Length() );
3228 idThread::ReturnFloat( 0.0f );
3233 idActor::Event_HasEnemies
3236 void idActor::Event_HasEnemies( void ) {
3239 hasEnemy = HasEnemies();
3240 idThread::ReturnInt( hasEnemy );
3245 idActor::Event_NextEnemy
3248 void idActor::Event_NextEnemy( idEntity *ent ) {
3251 if ( !ent || ( ent == this ) ) {
3252 actor = enemyList.Next();
3254 if ( !ent->IsType( idActor::Type ) ) {
3255 gameLocal.Error( "'%s' cannot be an enemy", ent->name.c_str() );
3258 actor = static_cast<idActor *>( ent );
3259 if ( actor->enemyNode.ListHead() != &enemyList ) {
3260 gameLocal.Error( "'%s' is not in '%s' enemy list", actor->name.c_str(), name.c_str() );
3264 for( ; actor != NULL; actor = actor->enemyNode.Next() ) {
3265 if ( !actor->fl.hidden ) {
3266 idThread::ReturnEntity( actor );
3271 idThread::ReturnEntity( NULL );
3276 idActor::Event_ClosestEnemyToPoint
3279 void idActor::Event_ClosestEnemyToPoint( const idVec3 &pos ) {
3280 idActor *bestEnt = ClosestEnemyToPoint( pos );
3281 idThread::ReturnEntity( bestEnt );
3286 idActor::Event_StopSound
3289 void idActor::Event_StopSound( int channel, int netSync ) {
3290 if ( channel == SND_CHANNEL_VOICE ) {
3291 idEntity *headEnt = head.GetEntity();
3293 headEnt->StopSound( channel, ( netSync != 0 ) );
3296 StopSound( channel, ( netSync != 0 ) );
3300 =====================
3301 idActor::Event_SetNextState
3302 =====================
3304 void idActor::Event_SetNextState( const char *name ) {
3305 idealState = GetScriptFunction( name );
3306 if ( idealState == state ) {
3312 =====================
3313 idActor::Event_SetState
3314 =====================
3316 void idActor::Event_SetState( const char *name ) {
3317 idealState = GetScriptFunction( name );
3318 if ( idealState == state ) {
3321 scriptThread->DoneProcessing();
3325 =====================
3326 idActor::Event_GetState
3327 =====================
3329 void idActor::Event_GetState( void ) {
3331 idThread::ReturnString( state->Name() );
3333 idThread::ReturnString( "" );
3338 =====================
3339 idActor::Event_GetHead
3340 =====================
3342 void idActor::Event_GetHead( void ) {
3343 idThread::ReturnEntity( head.GetEntity() );
3349 idActor::Event_SetDamageGroupScale
3352 void idActor::Event_SetDamageGroupScale( const char* groupName, float scale) {
3354 for( int i = 0; i < damageScale.Num(); i++ ) {
3355 if ( damageGroups[ i ] == groupName ) {
3356 damageScale[ i ] = scale;
3363 idActor::Event_SetDamageGroupScaleAll
3366 void idActor::Event_SetDamageGroupScaleAll( float scale ) {
3368 for( int i = 0; i < damageScale.Num(); i++ ) {
3369 damageScale[ i ] = scale;
3373 void idActor::Event_GetDamageGroupScale( const char* groupName ) {
3375 for( int i = 0; i < damageScale.Num(); i++ ) {
3376 if ( damageGroups[ i ] == groupName ) {
3377 idThread::ReturnFloat(damageScale[i]);
3382 idThread::ReturnFloat(0);
3385 void idActor::Event_SetDamageCap( float _damageCap ) {
3386 damageCap = _damageCap;
3389 void idActor::Event_SetWaitState( const char* waitState) {
3390 SetWaitState(waitState);
3393 void idActor::Event_GetWaitState() {
3395 idThread::ReturnString(WaitState());
3397 idThread::ReturnString("");