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 CLASS_DECLARATION( idAFEntity_Gibbable, idActor )
365 EVENT( AI_EnableEyeFocus, idActor::Event_EnableEyeFocus )
366 EVENT( AI_DisableEyeFocus, idActor::Event_DisableEyeFocus )
367 EVENT( EV_Footstep, idActor::Event_Footstep )
368 EVENT( EV_FootstepLeft, idActor::Event_Footstep )
369 EVENT( EV_FootstepRight, idActor::Event_Footstep )
370 EVENT( EV_EnableWalkIK, idActor::Event_EnableWalkIK )
371 EVENT( EV_DisableWalkIK, idActor::Event_DisableWalkIK )
372 EVENT( EV_EnableLegIK, idActor::Event_EnableLegIK )
373 EVENT( EV_DisableLegIK, idActor::Event_DisableLegIK )
374 EVENT( AI_PreventPain, idActor::Event_PreventPain )
375 EVENT( AI_DisablePain, idActor::Event_DisablePain )
376 EVENT( AI_EnablePain, idActor::Event_EnablePain )
377 EVENT( AI_GetPainAnim, idActor::Event_GetPainAnim )
378 EVENT( AI_SetAnimPrefix, idActor::Event_SetAnimPrefix )
379 EVENT( AI_StopAnim, idActor::Event_StopAnim )
380 EVENT( AI_PlayAnim, idActor::Event_PlayAnim )
381 EVENT( AI_PlayCycle, idActor::Event_PlayCycle )
382 EVENT( AI_IdleAnim, idActor::Event_IdleAnim )
383 EVENT( AI_SetSyncedAnimWeight, idActor::Event_SetSyncedAnimWeight )
384 EVENT( AI_SetBlendFrames, idActor::Event_SetBlendFrames )
385 EVENT( AI_GetBlendFrames, idActor::Event_GetBlendFrames )
386 EVENT( AI_AnimState, idActor::Event_AnimState )
387 EVENT( AI_GetAnimState, idActor::Event_GetAnimState )
388 EVENT( AI_InAnimState, idActor::Event_InAnimState )
389 EVENT( AI_FinishAction, idActor::Event_FinishAction )
390 EVENT( AI_AnimDone, idActor::Event_AnimDone )
391 EVENT( AI_OverrideAnim, idActor::Event_OverrideAnim )
392 EVENT( AI_EnableAnim, idActor::Event_EnableAnim )
393 EVENT( AI_HasAnim, idActor::Event_HasAnim )
394 EVENT( AI_CheckAnim, idActor::Event_CheckAnim )
395 EVENT( AI_ChooseAnim, idActor::Event_ChooseAnim )
396 EVENT( AI_AnimLength, idActor::Event_AnimLength )
397 EVENT( AI_AnimDistance, idActor::Event_AnimDistance )
398 EVENT( AI_HasEnemies, idActor::Event_HasEnemies )
399 EVENT( AI_NextEnemy, idActor::Event_NextEnemy )
400 EVENT( AI_ClosestEnemyToPoint, idActor::Event_ClosestEnemyToPoint )
401 EVENT( EV_StopSound, idActor::Event_StopSound )
402 EVENT( AI_SetNextState, idActor::Event_SetNextState )
403 EVENT( AI_SetState, idActor::Event_SetState )
404 EVENT( AI_GetState, idActor::Event_GetState )
405 EVENT( AI_GetHead, idActor::Event_GetHead )
409 =====================
411 =====================
413 idActor::idActor( void ) {
416 scriptThread = NULL; // initialized by ConstructScriptObject, which is called by idEntity::Spawn
418 use_combat_bbox = false;
425 pain_debounce_time = 0;
432 leftEyeJoint = INVALID_JOINT;
433 rightEyeJoint = INVALID_JOINT;
434 soundJoint = INVALID_JOINT;
437 deltaViewAngles.Zero();
441 allowEyeFocus = false;
452 attachments.SetGranularity( 1 );
454 enemyNode.SetOwner( this );
455 enemyList.SetOwner( this );
459 =====================
461 =====================
463 idActor::~idActor( void ) {
467 DeconstructScriptObject();
470 StopSound( SND_CHANNEL_ANY, false );
475 if ( head.GetEntity() ) {
476 head.GetEntity()->ClearBody();
477 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
480 // remove any attached entities
481 for( i = 0; i < attachments.Num(); i++ ) {
482 ent = attachments[ i ].ent.GetEntity();
484 ent->PostEventMS( &EV_Remove, 0 );
492 =====================
494 =====================
496 void idActor::Spawn( void ) {
500 copyJoints_t copyJoint;
506 spawnArgs.GetInt( "rank", "0", rank );
507 spawnArgs.GetInt( "team", "0", team );
508 spawnArgs.GetVector( "offsetModel", "0 0 0", modelOffset );
510 spawnArgs.GetBool( "use_combat_bbox", "0", use_combat_bbox );
512 viewAxis = GetPhysics()->GetAxis();
514 spawnArgs.GetFloat( "fov", "90", fovDegrees );
515 SetFOV( fovDegrees );
517 pain_debounce_time = 0;
519 pain_delay = SEC2MS( spawnArgs.GetFloat( "pain_delay" ) );
520 pain_threshold = spawnArgs.GetInt( "pain_threshold" );
524 walkIK.Init( this, IK_ANIM, modelOffset );
526 // the animation used to be set to the IK_ANIM at this point, but that was fixed, resulting in
527 // attachments not binding correctly, so we're stuck setting the IK_ANIM before attaching things.
528 animator.ClearAllAnims( gameLocal.time, 0 );
529 animator.SetFrame( ANIMCHANNEL_ALL, animator.GetAnim( IK_ANIM ), 0, 0, 0 );
531 // spawn any attachments we might have
532 const idKeyValue *kv = spawnArgs.MatchPrefix( "def_attach", NULL );
536 args.Set( "classname", kv->GetValue().c_str() );
538 // make items non-touchable so the player can't take them out of the character's hands
539 args.Set( "no_touch", "1" );
541 // don't let them drop to the floor
542 args.Set( "dropToFloor", "0" );
544 gameLocal.SpawnEntityDef( args, &ent );
546 gameLocal.Error( "Couldn't spawn '%s' to attach to entity '%s'", kv->GetValue().c_str(), name.c_str() );
550 kv = spawnArgs.MatchPrefix( "def_attach", kv );
556 // clear the bind anim
557 animator.ClearAllAnims( gameLocal.time, 0 );
559 idEntity *headEnt = head.GetEntity();
560 idAnimator *headAnimator;
562 headAnimator = headEnt->GetAnimator();
564 headAnimator = &animator;
568 // set up the list of joints to copy to the head
569 for( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) ) {
570 if ( kv->GetValue() == "" ) {
571 // probably clearing out inherited key, so skip it
575 jointName = kv->GetKey();
576 if ( jointName.StripLeadingOnce( "copy_joint_world " ) ) {
577 copyJoint.mod = JOINTMOD_WORLD_OVERRIDE;
579 jointName.StripLeadingOnce( "copy_joint " );
580 copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE;
583 copyJoint.from = animator.GetJointHandle( jointName );
584 if ( copyJoint.from == INVALID_JOINT ) {
585 gameLocal.Warning( "Unknown copy_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
589 jointName = kv->GetValue();
590 copyJoint.to = headAnimator->GetJointHandle( jointName );
591 if ( copyJoint.to == INVALID_JOINT ) {
592 gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %s", jointName.c_str(), name.c_str() );
596 copyJoints.Append( copyJoint );
601 blink_anim = headAnimator->GetAnim( "blink" );
602 blink_time = 0; // it's ok to blink right away
603 blink_min = SEC2MS( spawnArgs.GetFloat( "blink_min", "0.5" ) );
604 blink_max = SEC2MS( spawnArgs.GetFloat( "blink_max", "8" ) );
606 // set up the head anim if necessary
607 int headAnim = headAnimator->GetAnim( "def_head" );
610 headAnimator->CycleAnim( ANIMCHANNEL_ALL, headAnim, gameLocal.time, 0 );
612 headAnimator->CycleAnim( ANIMCHANNEL_HEAD, headAnim, gameLocal.time, 0 );
616 if ( spawnArgs.GetString( "sound_bone", "", jointName ) ) {
617 soundJoint = animator.GetJointHandle( jointName );
618 if ( soundJoint == INVALID_JOINT ) {
619 gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), jointName.c_str() );
623 finalBoss = spawnArgs.GetBool( "finalBoss" );
633 void idActor::FinishSetup( void ) {
634 const char *scriptObjectName;
636 // setup script object
637 if ( spawnArgs.GetString( "scriptobject", NULL, &scriptObjectName ) ) {
638 if ( !scriptObject.SetType( scriptObjectName ) ) {
639 gameLocal.Error( "Script object '%s' not found on entity '%s'.", scriptObjectName, name.c_str() );
642 ConstructScriptObject();
653 void idActor::SetupHead( void ) {
654 idAFAttachment *headEnt;
656 const char *headModel;
658 jointHandle_t damageJoint;
660 const idKeyValue *sndKV;
662 if ( gameLocal.isClient ) {
666 headModel = spawnArgs.GetString( "def_head", "" );
667 if ( headModel[ 0 ] ) {
668 jointName = spawnArgs.GetString( "head_joint" );
669 joint = animator.GetJointHandle( jointName );
670 if ( joint == INVALID_JOINT ) {
671 gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
674 // set the damage joint to be part of the head damage group
676 for( i = 0; i < damageGroups.Num(); i++ ) {
677 if ( damageGroups[ i ] == "head" ) {
678 damageJoint = static_cast<jointHandle_t>( i );
683 // copy any sounds in case we have frame commands on the head
685 sndKV = spawnArgs.MatchPrefix( "snd_", NULL );
687 args.Set( sndKV->GetKey(), sndKV->GetValue() );
688 sndKV = spawnArgs.MatchPrefix( "snd_", sndKV );
691 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, &args ) );
692 headEnt->SetName( va( "%s_head", name.c_str() ) );
693 headEnt->SetBody( this, headModel, damageJoint );
698 idAttachInfo &attach = attachments.Alloc();
699 attach.channel = animator.GetChannelForJoint( joint );
700 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
701 origin = renderEntity.origin + ( origin + modelOffset ) * renderEntity.axis;
702 attach.ent = headEnt;
703 headEnt->SetOrigin( origin );
704 headEnt->SetAxis( renderEntity.axis );
705 headEnt->BindToJoint( this, joint, true );
711 idActor::CopyJointsFromBodyToHead
714 void idActor::CopyJointsFromBodyToHead( void ) {
715 idEntity *headEnt = head.GetEntity();
716 idAnimator *headAnimator;
726 headAnimator = headEnt->GetAnimator();
728 // copy the animation from the body to the head
729 for( i = 0; i < copyJoints.Num(); i++ ) {
730 if ( copyJoints[ i ].mod == JOINTMOD_WORLD_OVERRIDE ) {
731 mat = headEnt->GetPhysics()->GetAxis().Transpose();
732 GetJointWorldTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
733 pos -= headEnt->GetPhysics()->GetOrigin();
734 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos * mat );
735 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis * mat );
737 animator.GetJointLocalTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
738 headAnimator->SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos );
739 headAnimator->SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis );
749 void idActor::Restart( void ) {
750 assert( !head.GetEntity() );
759 archive object for savegame file
762 void idActor::Save( idSaveGame *savefile ) const {
766 savefile->WriteInt( team );
767 savefile->WriteInt( rank );
768 savefile->WriteMat3( viewAxis );
770 savefile->WriteInt( enemyList.Num() );
771 for ( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
772 savefile->WriteObject( ent );
775 savefile->WriteFloat( fovDot );
776 savefile->WriteVec3( eyeOffset );
777 savefile->WriteVec3( modelOffset );
778 savefile->WriteAngles( deltaViewAngles );
780 savefile->WriteInt( pain_debounce_time );
781 savefile->WriteInt( pain_delay );
782 savefile->WriteInt( pain_threshold );
784 savefile->WriteInt( damageGroups.Num() );
785 for( i = 0; i < damageGroups.Num(); i++ ) {
786 savefile->WriteString( damageGroups[ i ] );
789 savefile->WriteInt( damageScale.Num() );
790 for( i = 0; i < damageScale.Num(); i++ ) {
791 savefile->WriteFloat( damageScale[ i ] );
794 savefile->WriteBool( use_combat_bbox );
795 head.Save( savefile );
797 savefile->WriteInt( copyJoints.Num() );
798 for( i = 0; i < copyJoints.Num(); i++ ) {
799 savefile->WriteInt( copyJoints[i].mod );
800 savefile->WriteJoint( copyJoints[i].from );
801 savefile->WriteJoint( copyJoints[i].to );
804 savefile->WriteJoint( leftEyeJoint );
805 savefile->WriteJoint( rightEyeJoint );
806 savefile->WriteJoint( soundJoint );
808 walkIK.Save( savefile );
810 savefile->WriteString( animPrefix );
811 savefile->WriteString( painAnim );
813 savefile->WriteInt( blink_anim );
814 savefile->WriteInt( blink_time );
815 savefile->WriteInt( blink_min );
816 savefile->WriteInt( blink_max );
819 savefile->WriteObject( scriptThread );
821 savefile->WriteString( waitState );
823 headAnim.Save( savefile );
824 torsoAnim.Save( savefile );
825 legsAnim.Save( savefile );
827 savefile->WriteBool( allowPain );
828 savefile->WriteBool( allowEyeFocus );
830 savefile->WriteInt( painTime );
832 savefile->WriteInt( attachments.Num() );
833 for ( i = 0; i < attachments.Num(); i++ ) {
834 attachments[i].ent.Save( savefile );
835 savefile->WriteInt( attachments[i].channel );
838 savefile->WriteBool( finalBoss );
842 //FIXME: this is unneccesary
844 idLexer src( state->Name(), idStr::Length( state->Name() ), "idAI::Save" );
846 src.ReadTokenOnLine( &token );
847 src.ExpectTokenString( "::" );
848 src.ReadTokenOnLine( &token );
850 savefile->WriteString( token );
852 savefile->WriteString( "" );
856 idLexer src( idealState->Name(), idStr::Length( idealState->Name() ), "idAI::Save" );
858 src.ReadTokenOnLine( &token );
859 src.ExpectTokenString( "::" );
860 src.ReadTokenOnLine( &token );
862 savefile->WriteString( token );
864 savefile->WriteString( "" );
873 unarchives object from save game file
876 void idActor::Restore( idRestoreGame *savefile ) {
880 savefile->ReadInt( team );
881 savefile->ReadInt( rank );
882 savefile->ReadMat3( viewAxis );
884 savefile->ReadInt( num );
885 for ( i = 0; i < num; i++ ) {
886 savefile->ReadObject( reinterpret_cast<idClass *&>( ent ) );
889 ent->enemyNode.AddToEnd( enemyList );
893 savefile->ReadFloat( fovDot );
894 savefile->ReadVec3( eyeOffset );
895 savefile->ReadVec3( modelOffset );
896 savefile->ReadAngles( deltaViewAngles );
898 savefile->ReadInt( pain_debounce_time );
899 savefile->ReadInt( pain_delay );
900 savefile->ReadInt( pain_threshold );
902 savefile->ReadInt( num );
903 damageGroups.SetGranularity( 1 );
904 damageGroups.SetNum( num );
905 for( i = 0; i < num; i++ ) {
906 savefile->ReadString( damageGroups[ i ] );
909 savefile->ReadInt( num );
910 damageScale.SetNum( num );
911 for( i = 0; i < num; i++ ) {
912 savefile->ReadFloat( damageScale[ i ] );
915 savefile->ReadBool( use_combat_bbox );
916 head.Restore( savefile );
918 savefile->ReadInt( num );
919 copyJoints.SetNum( num );
920 for( i = 0; i < num; i++ ) {
922 savefile->ReadInt( val );
923 copyJoints[i].mod = static_cast<jointModTransform_t>( val );
924 savefile->ReadJoint( copyJoints[i].from );
925 savefile->ReadJoint( copyJoints[i].to );
928 savefile->ReadJoint( leftEyeJoint );
929 savefile->ReadJoint( rightEyeJoint );
930 savefile->ReadJoint( soundJoint );
932 walkIK.Restore( savefile );
934 savefile->ReadString( animPrefix );
935 savefile->ReadString( painAnim );
937 savefile->ReadInt( blink_anim );
938 savefile->ReadInt( blink_time );
939 savefile->ReadInt( blink_min );
940 savefile->ReadInt( blink_max );
942 savefile->ReadObject( reinterpret_cast<idClass *&>( scriptThread ) );
944 savefile->ReadString( waitState );
946 headAnim.Restore( savefile );
947 torsoAnim.Restore( savefile );
948 legsAnim.Restore( savefile );
950 savefile->ReadBool( allowPain );
951 savefile->ReadBool( allowEyeFocus );
953 savefile->ReadInt( painTime );
955 savefile->ReadInt( num );
956 for ( i = 0; i < num; i++ ) {
957 idAttachInfo &attach = attachments.Alloc();
958 attach.ent.Restore( savefile );
959 savefile->ReadInt( attach.channel );
962 savefile->ReadBool( finalBoss );
966 savefile->ReadString( statename );
967 if ( statename.Length() > 0 ) {
968 state = GetScriptFunction( statename );
971 savefile->ReadString( statename );
972 if ( statename.Length() > 0 ) {
973 idealState = GetScriptFunction( statename );
982 void idActor::Hide( void ) {
986 idAFEntity_Base::Hide();
987 if ( head.GetEntity() ) {
988 head.GetEntity()->Hide();
991 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
992 next = ent->GetNextTeamEntity();
993 if ( ent->GetBindMaster() == this ) {
995 if ( ent->IsType( idLight::Type ) ) {
996 static_cast<idLight *>( ent )->Off();
1008 void idActor::Show( void ) {
1012 idAFEntity_Base::Show();
1013 if ( head.GetEntity() ) {
1014 head.GetEntity()->Show();
1016 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1017 next = ent->GetNextTeamEntity();
1018 if ( ent->GetBindMaster() == this ) {
1020 if ( ent->IsType( idLight::Type ) ) {
1021 static_cast<idLight *>( ent )->On();
1030 idActor::GetDefaultSurfaceType
1033 int idActor::GetDefaultSurfaceType( void ) const {
1034 return SURFTYPE_FLESH;
1039 idActor::ProjectOverlay
1042 void idActor::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1046 idEntity::ProjectOverlay( origin, dir, size, material );
1048 for( ent = GetNextTeamEntity(); ent != NULL; ent = next ) {
1049 next = ent->GetNextTeamEntity();
1050 if ( ent->GetBindMaster() == this ) {
1051 if ( ent->fl.takedamage && ent->spawnArgs.GetBool( "bleed" ) ) {
1052 ent->ProjectOverlay( origin, dir, size, material );
1063 bool idActor::LoadAF( void ) {
1066 if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) || !fileName.Length() ) {
1069 af.SetAnimator( GetAnimator() );
1070 return af.Load( this, fileName );
1074 =====================
1076 =====================
1078 void idActor::SetupBody( void ) {
1079 const char *jointname;
1081 animator.ClearAllAnims( gameLocal.time, 0 );
1082 animator.ClearAllJoints();
1084 idEntity *headEnt = head.GetEntity();
1086 jointname = spawnArgs.GetString( "bone_leftEye" );
1087 leftEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1089 jointname = spawnArgs.GetString( "bone_rightEye" );
1090 rightEyeJoint = headEnt->GetAnimator()->GetJointHandle( jointname );
1092 // set up the eye height. check if it's specified in the def.
1093 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1094 // if not in the def, then try to base it off the idle animation
1095 int anim = headEnt->GetAnimator()->GetAnim( "idle" );
1096 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1099 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1100 headEnt->GetAnimator()->GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1101 headEnt->GetAnimator()->ClearAllAnims( gameLocal.time, 0 );
1102 headEnt->GetAnimator()->ForceUpdate();
1103 pos += headEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
1104 eyeOffset = pos + modelOffset;
1106 // just base it off the bounding box size
1107 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1110 headAnim.Init( this, headEnt->GetAnimator(), ANIMCHANNEL_ALL );
1112 jointname = spawnArgs.GetString( "bone_leftEye" );
1113 leftEyeJoint = animator.GetJointHandle( jointname );
1115 jointname = spawnArgs.GetString( "bone_rightEye" );
1116 rightEyeJoint = animator.GetJointHandle( jointname );
1118 // set up the eye height. check if it's specified in the def.
1119 if ( !spawnArgs.GetFloat( "eye_height", "0", eyeOffset.z ) ) {
1120 // if not in the def, then try to base it off the idle animation
1121 int anim = animator.GetAnim( "idle" );
1122 if ( anim && ( leftEyeJoint != INVALID_JOINT ) ) {
1125 animator.PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 0 );
1126 animator.GetJointTransform( leftEyeJoint, gameLocal.time, pos, axis );
1127 animator.ClearAllAnims( gameLocal.time, 0 );
1128 animator.ForceUpdate();
1129 eyeOffset = pos + modelOffset;
1131 // just base it off the bounding box size
1132 eyeOffset.z = GetPhysics()->GetBounds()[ 1 ].z - 6;
1135 headAnim.Init( this, &animator, ANIMCHANNEL_HEAD );
1140 torsoAnim.Init( this, &animator, ANIMCHANNEL_TORSO );
1141 legsAnim.Init( this, &animator, ANIMCHANNEL_LEGS );
1145 =====================
1147 =====================
1149 void idActor::CheckBlink( void ) {
1150 // check if it's time to blink
1151 if ( !blink_anim || ( health <= 0 ) || !allowEyeFocus || ( blink_time > gameLocal.time ) ) {
1155 idEntity *headEnt = head.GetEntity();
1157 headEnt->GetAnimator()->PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1159 animator.PlayAnim( ANIMCHANNEL_EYELIDS, blink_anim, gameLocal.time, 1 );
1162 // set the next blink time
1163 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
1168 idActor::GetPhysicsToVisualTransform
1171 bool idActor::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
1172 if ( af.IsActive() ) {
1173 af.GetPhysicsToVisualTransform( origin, axis );
1176 origin = modelOffset;
1183 idActor::GetPhysicsToSoundTransform
1186 bool idActor::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
1187 if ( soundJoint != INVALID_JOINT ) {
1188 animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
1189 origin += modelOffset;
1192 origin = GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1198 /***********************************************************************
1200 script state management
1202 ***********************************************************************/
1206 idActor::ShutdownThreads
1209 void idActor::ShutdownThreads( void ) {
1210 headAnim.Shutdown();
1211 torsoAnim.Shutdown();
1212 legsAnim.Shutdown();
1214 if ( scriptThread ) {
1215 scriptThread->EndThread();
1216 scriptThread->PostEventMS( &EV_Remove, 0 );
1217 delete scriptThread;
1218 scriptThread = NULL;
1224 idActor::ShouldConstructScriptObjectAtSpawn
1226 Called during idEntity::Spawn to see if it should construct the script object or not.
1227 Overridden by subclasses that need to spawn the script object themselves.
1230 bool idActor::ShouldConstructScriptObjectAtSpawn( void ) const {
1236 idActor::ConstructScriptObject
1238 Called during idEntity::Spawn. Calls the constructor on the script object.
1239 Can be overridden by subclasses when a thread doesn't need to be allocated.
1242 idThread *idActor::ConstructScriptObject( void ) {
1243 const function_t *constructor;
1245 // make sure we have a scriptObject
1246 if ( !scriptObject.HasObject() ) {
1247 gameLocal.Error( "No scriptobject set on '%s'. Check the '%s' entityDef.", name.c_str(), GetEntityDefName() );
1250 if ( !scriptThread ) {
1251 // create script thread
1252 scriptThread = new idThread();
1253 scriptThread->ManualDelete();
1254 scriptThread->ManualControl();
1255 scriptThread->SetThreadName( name.c_str() );
1257 scriptThread->EndThread();
1260 // call script object's constructor
1261 constructor = scriptObject.GetConstructor();
1262 if ( !constructor ) {
1263 gameLocal.Error( "Missing constructor on '%s' for entity '%s'", scriptObject.GetTypeName(), name.c_str() );
1266 // init the script object's data
1267 scriptObject.ClearObject();
1269 // just set the current function on the script. we'll execute in the subclasses.
1270 scriptThread->CallFunction( this, constructor, true );
1272 return scriptThread;
1276 =====================
1277 idActor::GetScriptFunction
1278 =====================
1280 const function_t *idActor::GetScriptFunction( const char *funcname ) {
1281 const function_t *func;
1283 func = scriptObject.GetFunction( funcname );
1285 scriptThread->Error( "Unknown function '%s' in '%s'", funcname, scriptObject.GetTypeName() );
1292 =====================
1294 =====================
1296 void idActor::SetState( const function_t *newState ) {
1298 gameLocal.Error( "idActor::SetState: Null state" );
1301 if ( ai_debugScript.GetInteger() == entityNumber ) {
1302 gameLocal.Printf( "%d: %s: State: %s\n", gameLocal.time, name.c_str(), newState->Name() );
1307 scriptThread->CallFunction( this, state, true );
1311 =====================
1313 =====================
1315 void idActor::SetState( const char *statename ) {
1316 const function_t *newState;
1318 newState = GetScriptFunction( statename );
1319 SetState( newState );
1323 =====================
1324 idActor::UpdateScript
1325 =====================
1327 void idActor::UpdateScript( void ) {
1330 if ( ai_debugScript.GetInteger() == entityNumber ) {
1331 scriptThread->EnableDebugInfo();
1333 scriptThread->DisableDebugInfo();
1336 // a series of state changes can happen in a single frame.
1337 // this loop limits them in case we've entered an infinite loop.
1338 for( i = 0; i < 20; i++ ) {
1339 if ( idealState != state ) {
1340 SetState( idealState );
1343 // don't call script until it's done waiting
1344 if ( scriptThread->IsWaiting() ) {
1348 scriptThread->Execute();
1349 if ( idealState == state ) {
1355 scriptThread->Warning( "idActor::UpdateScript: exited loop to prevent lockup" );
1359 /***********************************************************************
1363 ***********************************************************************/
1366 =====================
1368 =====================
1370 void idActor::SetFOV( float fov ) {
1371 fovDot = (float)cos( DEG2RAD( fov * 0.5f ) );
1375 =====================
1376 idActor::SetEyeHeight
1377 =====================
1379 void idActor::SetEyeHeight( float height ) {
1380 eyeOffset.z = height;
1384 =====================
1386 =====================
1388 float idActor::EyeHeight( void ) const {
1393 =====================
1395 =====================
1397 idVec3 idActor::EyeOffset( void ) const {
1398 return GetPhysics()->GetGravityNormal() * -eyeOffset.z;
1402 =====================
1403 idActor::GetEyePosition
1404 =====================
1406 idVec3 idActor::GetEyePosition( void ) const {
1407 return GetPhysics()->GetOrigin() + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
1411 =====================
1413 =====================
1415 void idActor::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
1416 origin = GetEyePosition();
1421 =====================
1423 =====================
1425 bool idActor::CheckFOV( const idVec3 &pos ) const {
1426 if ( fovDot == 1.0f ) {
1433 delta = pos - GetEyePosition();
1435 // get our gravity normal
1436 const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
1438 // infinite vertical vision, so project it onto our orientation plane
1439 delta -= gravityDir * ( gravityDir * delta );
1442 dot = viewAxis[ 0 ] * delta;
1444 return ( dot >= fovDot );
1448 =====================
1450 =====================
1452 bool idActor::CanSee( idEntity *ent, bool useFov ) const {
1457 if ( ent->IsHidden() ) {
1461 if ( ent->IsType( idActor::Type ) ) {
1462 toPos = ( ( idActor * )ent )->GetEyePosition();
1464 toPos = ent->GetPhysics()->GetOrigin();
1467 if ( useFov && !CheckFOV( toPos ) ) {
1471 eye = GetEyePosition();
1473 gameLocal.clip.TracePoint( tr, eye, toPos, MASK_OPAQUE, this );
1474 if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == ent ) ) {
1482 =====================
1483 idActor::PointVisible
1484 =====================
1486 bool idActor::PointVisible( const idVec3 &point ) const {
1490 start = GetEyePosition();
1494 gameLocal.clip.TracePoint( results, start, end, MASK_OPAQUE, this );
1495 return ( results.fraction >= 1.0f );
1499 =====================
1500 idActor::GetAIAimTargets
1502 Returns positions for the AI to aim at.
1503 =====================
1505 void idActor::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
1506 headPos = lastSightPos + EyeOffset();
1507 chestPos = ( headPos + lastSightPos + GetPhysics()->GetBounds().GetCenter() ) * 0.5f;
1511 =====================
1512 idActor::GetRenderView
1513 =====================
1515 renderView_t *idActor::GetRenderView() {
1516 renderView_t *rv = idEntity::GetRenderView();
1517 rv->viewaxis = viewAxis;
1518 rv->vieworg = GetEyePosition();
1522 /***********************************************************************
1526 ***********************************************************************/
1530 idActor::SetCombatModel
1533 void idActor::SetCombatModel( void ) {
1534 idAFAttachment *headEnt;
1536 if ( !use_combat_bbox ) {
1537 if ( combatModel ) {
1538 combatModel->Unlink();
1539 combatModel->LoadModel( modelDefHandle );
1541 combatModel = new idClipModel( modelDefHandle );
1544 headEnt = head.GetEntity();
1546 headEnt->SetCombatModel();
1553 idActor::GetCombatModel
1556 idClipModel *idActor::GetCombatModel( void ) const {
1565 void idActor::LinkCombat( void ) {
1566 idAFAttachment *headEnt;
1568 if ( fl.hidden || use_combat_bbox ) {
1572 if ( combatModel ) {
1573 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1575 headEnt = head.GetEntity();
1577 headEnt->LinkCombat();
1583 idActor::UnlinkCombat
1586 void idActor::UnlinkCombat( void ) {
1587 idAFAttachment *headEnt;
1589 if ( combatModel ) {
1590 combatModel->Unlink();
1592 headEnt = head.GetEntity();
1594 headEnt->UnlinkCombat();
1600 idActor::StartRagdoll
1603 bool idActor::StartRagdoll( void ) {
1604 float slomoStart, slomoEnd;
1605 float jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd;
1606 float contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd;
1609 if ( !af.IsLoaded() ) {
1613 // if the AF is already active
1614 if ( af.IsActive() ) {
1618 // disable the monster bounding box
1619 GetPhysics()->DisableClip();
1621 // start using the AF
1622 af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
1624 slomoStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoStart", "-1.6" );
1625 slomoEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_slomoEnd", "0.8" );
1627 // do the first part of the death in slow motion
1628 af.GetPhysics()->SetTimeScaleRamp( slomoStart, slomoEnd );
1630 jointFrictionDent = spawnArgs.GetFloat( "ragdoll_jointFrictionDent", "0.1" );
1631 jointFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionStart", "0.2" );
1632 jointFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_jointFrictionEnd", "1.2" );
1634 // set joint friction dent
1635 af.GetPhysics()->SetJointFrictionDent( jointFrictionDent, jointFrictionDentStart, jointFrictionDentEnd );
1637 contactFrictionDent = spawnArgs.GetFloat( "ragdoll_contactFrictionDent", "0.1" );
1638 contactFrictionDentStart = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionStart", "1.0" );
1639 contactFrictionDentEnd = MS2SEC( gameLocal.time ) + spawnArgs.GetFloat( "ragdoll_contactFrictionEnd", "2.0" );
1641 // set contact friction dent
1642 af.GetPhysics()->SetContactFrictionDent( contactFrictionDent, contactFrictionDentStart, contactFrictionDentEnd );
1644 // drop any items the actor is holding
1645 idMoveableItem::DropItems( this, "death", NULL );
1647 // drop any articulated figures the actor is holding
1648 idAFEntity_Base::DropAFs( this, "death", NULL );
1650 RemoveAttachments();
1657 idActor::StopRagdoll
1660 void idActor::StopRagdoll( void ) {
1661 if ( af.IsActive() ) {
1668 idActor::UpdateAnimationControllers
1671 bool idActor::UpdateAnimationControllers( void ) {
1673 if ( af.IsActive() ) {
1674 return idAFEntity_Base::UpdateAnimationControllers();
1676 animator.ClearAFPose();
1679 if ( walkIK.IsInitialized() ) {
1689 idActor::RemoveAttachments
1692 void idActor::RemoveAttachments( void ) {
1696 // remove any attached entities
1697 for( i = 0; i < attachments.Num(); i++ ) {
1698 ent = attachments[ i ].ent.GetEntity();
1699 if ( ent && ent->spawnArgs.GetBool( "remove" ) ) {
1700 ent->PostEventMS( &EV_Remove, 0 );
1710 void idActor::Attach( idEntity *ent ) {
1713 jointHandle_t joint;
1715 idAttachInfo &attach = attachments.Alloc();
1716 idAngles angleOffset;
1717 idVec3 originOffset;
1719 jointName = ent->spawnArgs.GetString( "joint" );
1720 joint = animator.GetJointHandle( jointName );
1721 if ( joint == INVALID_JOINT ) {
1722 gameLocal.Error( "Joint '%s' not found for attaching '%s' on '%s'", jointName.c_str(), ent->GetClassname(), name.c_str() );
1725 angleOffset = ent->spawnArgs.GetAngles( "angles" );
1726 originOffset = ent->spawnArgs.GetVector( "origin" );
1728 attach.channel = animator.GetChannelForJoint( joint );
1729 GetJointWorldTransform( joint, gameLocal.time, origin, axis );
1732 ent->SetOrigin( origin + originOffset * renderEntity.axis );
1733 idMat3 rotate = angleOffset.ToMat3();
1734 idMat3 newAxis = rotate * axis;
1735 ent->SetAxis( newAxis );
1736 ent->BindToJoint( this, joint, true );
1737 ent->cinematic = cinematic;
1745 void idActor::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
1746 GetPhysics()->SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
1747 GetPhysics()->SetLinearVelocity( vec3_origin );
1749 viewAxis = angles.ToMat3();
1753 if ( !IsHidden() ) {
1754 // kill anything at the new position
1755 gameLocal.KillBox( this );
1761 idActor::GetDeltaViewAngles
1764 const idAngles &idActor::GetDeltaViewAngles( void ) const {
1765 return deltaViewAngles;
1770 idActor::SetDeltaViewAngles
1773 void idActor::SetDeltaViewAngles( const idAngles &delta ) {
1774 deltaViewAngles = delta;
1782 bool idActor::HasEnemies( void ) const {
1785 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1786 if ( !ent->fl.hidden ) {
1796 idActor::ClosestEnemyToPoint
1799 idActor *idActor::ClosestEnemyToPoint( const idVec3 &pos ) {
1802 float bestDistSquared;
1806 bestDistSquared = idMath::INFINITY;
1808 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1809 if ( ent->fl.hidden ) {
1812 delta = ent->GetPhysics()->GetOrigin() - pos;
1813 distSquared = delta.LengthSqr();
1814 if ( distSquared < bestDistSquared ) {
1816 bestDistSquared = distSquared;
1825 idActor::EnemyWithMostHealth
1828 idActor *idActor::EnemyWithMostHealth() {
1834 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
1835 if ( !ent->fl.hidden && ( ent->health > most ) ) {
1848 bool idActor::OnLadder( void ) const {
1854 idActor::GetAASLocation
1857 void idActor::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
1861 GetFloorPos( 64.0f, pos );
1867 size = aas->GetSettings()->boundingBoxes[0][1];
1872 areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK );
1874 aas->PushPointIntoAreaNum( areaNum, pos );
1878 /***********************************************************************
1882 ***********************************************************************/
1885 =====================
1886 idActor::SetAnimState
1887 =====================
1889 void idActor::SetAnimState( int channel, const char *statename, int blendFrames ) {
1890 const function_t *func;
1892 func = scriptObject.GetFunction( statename );
1895 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
1899 case ANIMCHANNEL_HEAD :
1900 headAnim.SetState( statename, blendFrames );
1901 allowEyeFocus = true;
1904 case ANIMCHANNEL_TORSO :
1905 torsoAnim.SetState( statename, blendFrames );
1906 legsAnim.Enable( blendFrames );
1908 allowEyeFocus = true;
1911 case ANIMCHANNEL_LEGS :
1912 legsAnim.SetState( statename, blendFrames );
1913 torsoAnim.Enable( blendFrames );
1915 allowEyeFocus = true;
1919 gameLocal.Error( "idActor::SetAnimState: Unknown anim group" );
1925 =====================
1926 idActor::GetAnimState
1927 =====================
1929 const char *idActor::GetAnimState( int channel ) const {
1931 case ANIMCHANNEL_HEAD :
1932 return headAnim.state;
1935 case ANIMCHANNEL_TORSO :
1936 return torsoAnim.state;
1939 case ANIMCHANNEL_LEGS :
1940 return legsAnim.state;
1944 gameLocal.Error( "idActor::GetAnimState: Unknown anim group" );
1951 =====================
1952 idActor::InAnimState
1953 =====================
1955 bool idActor::InAnimState( int channel, const char *statename ) const {
1957 case ANIMCHANNEL_HEAD :
1958 if ( headAnim.state == statename ) {
1963 case ANIMCHANNEL_TORSO :
1964 if ( torsoAnim.state == statename ) {
1969 case ANIMCHANNEL_LEGS :
1970 if ( legsAnim.state == statename ) {
1976 gameLocal.Error( "idActor::InAnimState: Unknown anim group" );
1984 =====================
1986 =====================
1988 const char *idActor::WaitState( void ) const {
1989 if ( waitState.Length() ) {
1997 =====================
1998 idActor::SetWaitState
1999 =====================
2001 void idActor::SetWaitState( const char *_waitstate ) {
2002 waitState = _waitstate;
2006 =====================
2007 idActor::UpdateAnimState
2008 =====================
2010 void idActor::UpdateAnimState( void ) {
2011 headAnim.UpdateState();
2012 torsoAnim.UpdateState();
2013 legsAnim.UpdateState();
2017 =====================
2019 =====================
2021 int idActor::GetAnim( int channel, const char *animname ) {
2024 idAnimator *animatorPtr;
2026 if ( channel == ANIMCHANNEL_HEAD ) {
2027 if ( !head.GetEntity() ) {
2030 animatorPtr = head.GetEntity()->GetAnimator();
2032 animatorPtr = &animator;
2035 if ( animPrefix.Length() ) {
2036 temp = va( "%s_%s", animPrefix.c_str(), animname );
2037 anim = animatorPtr->GetAnim( temp );
2043 anim = animatorPtr->GetAnim( animname );
2050 idActor::SyncAnimChannels
2053 void idActor::SyncAnimChannels( int channel, int syncToChannel, int blendFrames ) {
2054 idAnimator *headAnimator;
2055 idAFAttachment *headEnt;
2057 idAnimBlend *syncAnim;
2062 blendTime = FRAME2MS( blendFrames );
2063 if ( channel == ANIMCHANNEL_HEAD ) {
2064 headEnt = head.GetEntity();
2066 headAnimator = headEnt->GetAnimator();
2067 syncAnim = animator.CurrentAnim( syncToChannel );
2069 anim = headAnimator->GetAnim( syncAnim->AnimFullName() );
2071 anim = headAnimator->GetAnim( syncAnim->AnimName() );
2074 cycle = animator.CurrentAnim( syncToChannel )->GetCycleCount();
2075 starttime = animator.CurrentAnim( syncToChannel )->GetStartTime();
2076 headAnimator->PlayAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, blendTime );
2077 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
2078 headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->SetStartTime( starttime );
2080 headEnt->PlayIdleAnim( blendTime );
2084 } else if ( syncToChannel == ANIMCHANNEL_HEAD ) {
2085 headEnt = head.GetEntity();
2087 headAnimator = headEnt->GetAnimator();
2088 syncAnim = headAnimator->CurrentAnim( ANIMCHANNEL_ALL );
2090 anim = GetAnim( channel, syncAnim->AnimFullName() );
2092 anim = GetAnim( channel, syncAnim->AnimName() );
2095 cycle = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetCycleCount();
2096 starttime = headAnimator->CurrentAnim( ANIMCHANNEL_ALL )->GetStartTime();
2097 animator.PlayAnim( channel, anim, gameLocal.time, blendTime );
2098 animator.CurrentAnim( channel )->SetCycleCount( cycle );
2099 animator.CurrentAnim( channel )->SetStartTime( starttime );
2104 animator.SyncAnimChannels( channel, syncToChannel, gameLocal.time, blendTime );
2108 /***********************************************************************
2112 ***********************************************************************/
2119 void idActor::Gib( const idVec3 &dir, const char *damageDefName ) {
2120 // no gibbing in multiplayer - by self damage or by moving objects
2121 if ( gameLocal.isMultiplayer ) {
2128 idAFEntity_Gibbable::Gib( dir, damageDefName );
2129 if ( head.GetEntity() ) {
2130 head.GetEntity()->Hide();
2132 StopSound( SND_CHANNEL_VOICE, false );
2140 this entity that is being damaged
2141 inflictor entity that is causing the damage
2142 attacker entity that caused the inflictor to damage targ
2143 example: this=monster, inflictor=rocket, attacker=player
2145 dir direction of the attack for knockback in global space
2146 point point at which the damage is being inflicted, used for headshots
2147 damage amount of damage being inflicted
2149 inflictor, attacker, dir, and point can be NULL for environmental effects
2151 Bleeding wounds and surface overlays are applied in the collision code that
2155 void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
2156 const char *damageDefName, const float damageScale, const int location ) {
2157 if ( !fl.takedamage ) {
2162 inflictor = gameLocal.world;
2165 attacker = gameLocal.world;
2168 if ( finalBoss && !inflictor->IsType( idSoulCubeMissile::Type ) ) {
2172 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
2174 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
2177 int damage = damageDef->GetInt( "damage" ) * damageScale;
2178 damage = GetDamageForLocation( damage, location );
2180 // inform the attacker that they hit someone
2181 attacker->DamageFeedback( this, inflictor, damage );
2184 if ( health <= 0 ) {
2185 if ( health < -999 ) {
2188 Killed( inflictor, attacker, damage, dir, location );
2189 if ( ( health < -20 ) && spawnArgs.GetBool( "gib" ) && damageDef->GetBool( "gib" ) ) {
2190 Gib( dir, damageDefName );
2193 Pain( inflictor, attacker, damage, dir, location );
2196 // don't accumulate knockback
2197 if ( af.IsLoaded() ) {
2201 // physics is turned off by calling af.Rest()
2202 BecomeActive( TH_PHYSICS );
2208 =====================
2210 =====================
2212 void idActor::ClearPain( void ) {
2213 pain_debounce_time = 0;
2217 =====================
2219 =====================
2221 bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
2222 if ( af.IsLoaded() ) {
2226 // physics is turned off by calling af.Rest()
2227 BecomeActive( TH_PHYSICS );
2230 if ( gameLocal.time < pain_debounce_time ) {
2234 // don't play pain sounds more than necessary
2235 pain_debounce_time = gameLocal.time + pain_delay;
2237 if ( health > 75 ) {
2238 StartSound( "snd_pain_small", SND_CHANNEL_VOICE, 0, false, NULL );
2239 } else if ( health > 50 ) {
2240 StartSound( "snd_pain_medium", SND_CHANNEL_VOICE, 0, false, NULL );
2241 } else if ( health > 25 ) {
2242 StartSound( "snd_pain_large", SND_CHANNEL_VOICE, 0, false, NULL );
2244 StartSound( "snd_pain_huge", SND_CHANNEL_VOICE, 0, false, NULL );
2247 if ( !allowPain || ( gameLocal.time < painTime ) ) {
2248 // don't play a pain anim
2252 if ( pain_threshold && ( damage < pain_threshold ) ) {
2256 // set the pain anim
2257 idStr damageGroup = GetDamageGroup( location );
2260 if ( animPrefix.Length() ) {
2261 if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2262 sprintf( painAnim, "%s_pain_%s", animPrefix.c_str(), damageGroup.c_str() );
2263 if ( !animator.HasAnim( painAnim ) ) {
2264 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2265 if ( !animator.HasAnim( painAnim ) ) {
2271 if ( !painAnim.Length() ) {
2272 sprintf( painAnim, "%s_pain", animPrefix.c_str() );
2273 if ( !animator.HasAnim( painAnim ) ) {
2277 } else if ( damageGroup.Length() && ( damageGroup != "legs" ) ) {
2278 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2279 if ( !animator.HasAnim( painAnim ) ) {
2280 sprintf( painAnim, "pain_%s", damageGroup.c_str() );
2281 if ( !animator.HasAnim( painAnim ) ) {
2287 if ( !painAnim.Length() ) {
2291 if ( g_debugDamage.GetBool() ) {
2292 gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ),
2293 damageGroup.c_str(), painAnim.c_str() );
2300 =====================
2302 =====================
2304 void idActor::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
2305 idAFEntity_Gibbable::SpawnGibs( dir, damageDefName );
2306 RemoveAttachments();
2310 =====================
2311 idActor::SetupDamageGroups
2313 FIXME: only store group names once and store an index for each joint
2314 =====================
2316 void idActor::SetupDamageGroups( void ) {
2318 const idKeyValue *arg;
2320 idList<jointHandle_t> jointList;
2324 // create damage zones
2325 damageGroups.SetNum( animator.NumJoints() );
2326 arg = spawnArgs.MatchPrefix( "damage_zone ", NULL );
2328 groupname = arg->GetKey();
2329 groupname.Strip( "damage_zone " );
2330 animator.GetJointList( arg->GetValue(), jointList );
2331 for( i = 0; i < jointList.Num(); i++ ) {
2332 jointnum = jointList[ i ];
2333 damageGroups[ jointnum ] = groupname;
2336 arg = spawnArgs.MatchPrefix( "damage_zone ", arg );
2339 // initilize the damage zones to normal damage
2340 damageScale.SetNum( animator.NumJoints() );
2341 for( i = 0; i < damageScale.Num(); i++ ) {
2342 damageScale[ i ] = 1.0f;
2345 // set the percentage on damage zones
2346 arg = spawnArgs.MatchPrefix( "damage_scale ", NULL );
2348 scale = atof( arg->GetValue() );
2349 groupname = arg->GetKey();
2350 groupname.Strip( "damage_scale " );
2351 for( i = 0; i < damageScale.Num(); i++ ) {
2352 if ( damageGroups[ i ] == groupname ) {
2353 damageScale[ i ] = scale;
2356 arg = spawnArgs.MatchPrefix( "damage_scale ", arg );
2361 =====================
2362 idActor::GetDamageForLocation
2363 =====================
2365 int idActor::GetDamageForLocation( int damage, int location ) {
2366 if ( ( location < 0 ) || ( location >= damageScale.Num() ) ) {
2370 return (int)ceil( damage * damageScale[ location ] );
2374 =====================
2375 idActor::GetDamageGroup
2376 =====================
2378 const char *idActor::GetDamageGroup( int location ) {
2379 if ( ( location < 0 ) || ( location >= damageGroups.Num() ) ) {
2383 return damageGroups[ location ];
2387 /***********************************************************************
2391 ***********************************************************************/
2394 =====================
2395 idActor::Event_EnableEyeFocus
2396 =====================
2398 void idActor::PlayFootStepSound( void ) {
2399 const char *sound = NULL;
2400 const idMaterial *material;
2402 if ( !GetPhysics()->HasGroundContacts() ) {
2406 // start footstep sound based on material type
2407 material = GetPhysics()->GetContact( 0 ).material;
2408 if ( material != NULL ) {
2409 sound = spawnArgs.GetString( va( "snd_footstep_%s", gameLocal.sufaceTypeNames[ material->GetSurfaceType() ] ) );
2411 if ( *sound == '\0' ) {
2412 sound = spawnArgs.GetString( "snd_footstep" );
2414 if ( *sound != '\0' ) {
2415 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
2420 =====================
2421 idActor::Event_EnableEyeFocus
2422 =====================
2424 void idActor::Event_EnableEyeFocus( void ) {
2425 allowEyeFocus = true;
2426 blink_time = gameLocal.time + blink_min + gameLocal.random.RandomFloat() * ( blink_max - blink_min );
2430 =====================
2431 idActor::Event_DisableEyeFocus
2432 =====================
2434 void idActor::Event_DisableEyeFocus( void ) {
2435 allowEyeFocus = false;
2437 idEntity *headEnt = head.GetEntity();
2439 headEnt->GetAnimator()->Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2441 animator.Clear( ANIMCHANNEL_EYELIDS, gameLocal.time, FRAME2MS( 2 ) );
2447 idActor::Event_Footstep
2450 void idActor::Event_Footstep( void ) {
2451 PlayFootStepSound();
2455 =====================
2456 idActor::Event_EnableWalkIK
2457 =====================
2459 void idActor::Event_EnableWalkIK( void ) {
2464 =====================
2465 idActor::Event_DisableWalkIK
2466 =====================
2468 void idActor::Event_DisableWalkIK( void ) {
2469 walkIK.DisableAll();
2473 =====================
2474 idActor::Event_EnableLegIK
2475 =====================
2477 void idActor::Event_EnableLegIK( int num ) {
2478 walkIK.EnableLeg( num );
2482 =====================
2483 idActor::Event_DisableLegIK
2484 =====================
2486 void idActor::Event_DisableLegIK( int num ) {
2487 walkIK.DisableLeg( num );
2491 =====================
2492 idActor::Event_PreventPain
2493 =====================
2495 void idActor::Event_PreventPain( float duration ) {
2496 painTime = gameLocal.time + SEC2MS( duration );
2501 idActor::Event_DisablePain
2504 void idActor::Event_DisablePain( void ) {
2510 idActor::Event_EnablePain
2513 void idActor::Event_EnablePain( void ) {
2518 =====================
2519 idActor::Event_GetPainAnim
2520 =====================
2522 void idActor::Event_GetPainAnim( void ) {
2523 if ( !painAnim.Length() ) {
2524 idThread::ReturnString( "pain" );
2526 idThread::ReturnString( painAnim );
2531 =====================
2532 idActor::Event_SetAnimPrefix
2533 =====================
2535 void idActor::Event_SetAnimPrefix( const char *prefix ) {
2536 animPrefix = prefix;
2541 idActor::Event_StopAnim
2544 void idActor::Event_StopAnim( int channel, int frames ) {
2546 case ANIMCHANNEL_HEAD :
2547 headAnim.StopAnim( frames );
2550 case ANIMCHANNEL_TORSO :
2551 torsoAnim.StopAnim( frames );
2554 case ANIMCHANNEL_LEGS :
2555 legsAnim.StopAnim( frames );
2559 gameLocal.Error( "Unknown anim group" );
2566 idActor::Event_PlayAnim
2569 void idActor::Event_PlayAnim( int channel, const char *animname ) {
2574 anim = GetAnim( channel, animname );
2576 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2577 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2579 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2581 idThread::ReturnInt( 0 );
2586 case ANIMCHANNEL_HEAD :
2587 headEnt = head.GetEntity();
2589 headAnim.idleAnim = false;
2590 headAnim.PlayAnim( anim );
2591 flags = headAnim.GetAnimFlags();
2592 if ( !flags.prevent_idle_override ) {
2593 if ( torsoAnim.IsIdle() ) {
2594 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2595 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2596 if ( legsAnim.IsIdle() ) {
2597 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2598 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2605 case ANIMCHANNEL_TORSO :
2606 torsoAnim.idleAnim = false;
2607 torsoAnim.PlayAnim( anim );
2608 flags = torsoAnim.GetAnimFlags();
2609 if ( !flags.prevent_idle_override ) {
2610 if ( headAnim.IsIdle() ) {
2611 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2612 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2614 if ( legsAnim.IsIdle() ) {
2615 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2616 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2621 case ANIMCHANNEL_LEGS :
2622 legsAnim.idleAnim = false;
2623 legsAnim.PlayAnim( anim );
2624 flags = legsAnim.GetAnimFlags();
2625 if ( !flags.prevent_idle_override ) {
2626 if ( torsoAnim.IsIdle() ) {
2627 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2628 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2629 if ( headAnim.IsIdle() ) {
2630 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2631 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2638 gameLocal.Error( "Unknown anim group" );
2641 idThread::ReturnInt( 1 );
2646 idActor::Event_PlayCycle
2649 void idActor::Event_PlayCycle( int channel, const char *animname ) {
2653 anim = GetAnim( channel, animname );
2655 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2656 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2658 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2660 idThread::ReturnInt( false );
2665 case ANIMCHANNEL_HEAD :
2666 headAnim.idleAnim = false;
2667 headAnim.CycleAnim( anim );
2668 flags = headAnim.GetAnimFlags();
2669 if ( !flags.prevent_idle_override ) {
2670 if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2671 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2672 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2673 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2674 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2679 case ANIMCHANNEL_TORSO :
2680 torsoAnim.idleAnim = false;
2681 torsoAnim.CycleAnim( anim );
2682 flags = torsoAnim.GetAnimFlags();
2683 if ( !flags.prevent_idle_override ) {
2684 if ( headAnim.IsIdle() ) {
2685 headAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2686 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2688 if ( legsAnim.IsIdle() ) {
2689 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2690 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2695 case ANIMCHANNEL_LEGS :
2696 legsAnim.idleAnim = false;
2697 legsAnim.CycleAnim( anim );
2698 flags = legsAnim.GetAnimFlags();
2699 if ( !flags.prevent_idle_override ) {
2700 if ( torsoAnim.IsIdle() ) {
2701 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2702 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2703 if ( headAnim.IsIdle() ) {
2704 headAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2705 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2712 gameLocal.Error( "Unknown anim group" );
2715 idThread::ReturnInt( true );
2720 idActor::Event_IdleAnim
2723 void idActor::Event_IdleAnim( int channel, const char *animname ) {
2726 anim = GetAnim( channel, animname );
2728 if ( ( channel == ANIMCHANNEL_HEAD ) && head.GetEntity() ) {
2729 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), spawnArgs.GetString( "def_head", "" ) );
2731 gameLocal.DPrintf( "missing '%s' animation on '%s' (%s)\n", animname, name.c_str(), GetEntityDefName() );
2735 case ANIMCHANNEL_HEAD :
2736 headAnim.BecomeIdle();
2739 case ANIMCHANNEL_TORSO :
2740 torsoAnim.BecomeIdle();
2743 case ANIMCHANNEL_LEGS :
2744 legsAnim.BecomeIdle();
2748 gameLocal.Error( "Unknown anim group" );
2751 idThread::ReturnInt( false );
2756 case ANIMCHANNEL_HEAD :
2757 headAnim.BecomeIdle();
2758 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2759 // don't sync to torso body if it doesn't override idle anims
2760 headAnim.CycleAnim( anim );
2761 } else if ( torsoAnim.IsIdle() && legsAnim.IsIdle() ) {
2762 // everything is idle, so play the anim on the head and copy it to the torso and legs
2763 headAnim.CycleAnim( anim );
2764 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2765 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2766 legsAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2767 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_HEAD, headAnim.lastAnimBlendFrames );
2768 } else if ( torsoAnim.IsIdle() ) {
2769 // sync the head and torso to the legs
2770 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, headAnim.animBlendFrames );
2771 torsoAnim.animBlendFrames = headAnim.lastAnimBlendFrames;
2772 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2774 // sync the head to the torso
2775 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, headAnim.animBlendFrames );
2779 case ANIMCHANNEL_TORSO :
2780 torsoAnim.BecomeIdle();
2781 if ( legsAnim.GetAnimFlags().prevent_idle_override ) {
2782 // don't sync to legs if legs anim doesn't override idle anims
2783 torsoAnim.CycleAnim( anim );
2784 } else if ( legsAnim.IsIdle() ) {
2785 // play the anim in both legs and torso
2786 torsoAnim.CycleAnim( anim );
2787 legsAnim.animBlendFrames = torsoAnim.lastAnimBlendFrames;
2788 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2790 // sync the anim to the legs
2791 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, torsoAnim.animBlendFrames );
2794 if ( headAnim.IsIdle() ) {
2795 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2799 case ANIMCHANNEL_LEGS :
2800 legsAnim.BecomeIdle();
2801 if ( torsoAnim.GetAnimFlags().prevent_idle_override ) {
2802 // don't sync to torso if torso anim doesn't override idle anims
2803 legsAnim.CycleAnim( anim );
2804 } else if ( torsoAnim.IsIdle() ) {
2805 // play the anim in both legs and torso
2806 legsAnim.CycleAnim( anim );
2807 torsoAnim.animBlendFrames = legsAnim.lastAnimBlendFrames;
2808 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2809 if ( headAnim.IsIdle() ) {
2810 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2813 // sync the anim to the torso
2814 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, legsAnim.animBlendFrames );
2819 gameLocal.Error( "Unknown anim group" );
2822 idThread::ReturnInt( true );
2827 idActor::Event_SetSyncedAnimWeight
2830 void idActor::Event_SetSyncedAnimWeight( int channel, int anim, float weight ) {
2833 headEnt = head.GetEntity();
2835 case ANIMCHANNEL_HEAD :
2837 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2839 animator.CurrentAnim( ANIMCHANNEL_HEAD )->SetSyncedAnimWeight( anim, weight );
2841 if ( torsoAnim.IsIdle() ) {
2842 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2843 if ( legsAnim.IsIdle() ) {
2844 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2849 case ANIMCHANNEL_TORSO :
2850 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2851 if ( legsAnim.IsIdle() ) {
2852 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2854 if ( headEnt && headAnim.IsIdle() ) {
2855 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2859 case ANIMCHANNEL_LEGS :
2860 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( anim, weight );
2861 if ( torsoAnim.IsIdle() ) {
2862 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( anim, weight );
2863 if ( headEnt && headAnim.IsIdle() ) {
2864 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( anim, weight );
2870 gameLocal.Error( "Unknown anim group" );
2876 idActor::Event_OverrideAnim
2879 void idActor::Event_OverrideAnim( int channel ) {
2881 case ANIMCHANNEL_HEAD :
2883 if ( !torsoAnim.IsIdle() ) {
2884 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2886 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2890 case ANIMCHANNEL_TORSO :
2891 torsoAnim.Disable();
2892 SyncAnimChannels( ANIMCHANNEL_TORSO, ANIMCHANNEL_LEGS, legsAnim.lastAnimBlendFrames );
2893 if ( headAnim.IsIdle() ) {
2894 SyncAnimChannels( ANIMCHANNEL_HEAD, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2898 case ANIMCHANNEL_LEGS :
2900 SyncAnimChannels( ANIMCHANNEL_LEGS, ANIMCHANNEL_TORSO, torsoAnim.lastAnimBlendFrames );
2904 gameLocal.Error( "Unknown anim group" );
2911 idActor::Event_EnableAnim
2914 void idActor::Event_EnableAnim( int channel, int blendFrames ) {
2916 case ANIMCHANNEL_HEAD :
2917 headAnim.Enable( blendFrames );
2920 case ANIMCHANNEL_TORSO :
2921 torsoAnim.Enable( blendFrames );
2924 case ANIMCHANNEL_LEGS :
2925 legsAnim.Enable( blendFrames );
2929 gameLocal.Error( "Unknown anim group" );
2936 idActor::Event_SetBlendFrames
2939 void idActor::Event_SetBlendFrames( int channel, int blendFrames ) {
2941 case ANIMCHANNEL_HEAD :
2942 headAnim.animBlendFrames = blendFrames;
2943 headAnim.lastAnimBlendFrames = blendFrames;
2946 case ANIMCHANNEL_TORSO :
2947 torsoAnim.animBlendFrames = blendFrames;
2948 torsoAnim.lastAnimBlendFrames = blendFrames;
2951 case ANIMCHANNEL_LEGS :
2952 legsAnim.animBlendFrames = blendFrames;
2953 legsAnim.lastAnimBlendFrames = blendFrames;
2957 gameLocal.Error( "Unknown anim group" );
2964 idActor::Event_GetBlendFrames
2967 void idActor::Event_GetBlendFrames( int channel ) {
2969 case ANIMCHANNEL_HEAD :
2970 idThread::ReturnInt( headAnim.animBlendFrames );
2973 case ANIMCHANNEL_TORSO :
2974 idThread::ReturnInt( torsoAnim.animBlendFrames );
2977 case ANIMCHANNEL_LEGS :
2978 idThread::ReturnInt( legsAnim.animBlendFrames );
2982 gameLocal.Error( "Unknown anim group" );
2989 idActor::Event_AnimState
2992 void idActor::Event_AnimState( int channel, const char *statename, int blendFrames ) {
2993 SetAnimState( channel, statename, blendFrames );
2998 idActor::Event_GetAnimState
3001 void idActor::Event_GetAnimState( int channel ) {
3004 state = GetAnimState( channel );
3005 idThread::ReturnString( state );
3010 idActor::Event_InAnimState
3013 void idActor::Event_InAnimState( int channel, const char *statename ) {
3016 instate = InAnimState( channel, statename );
3017 idThread::ReturnInt( instate );
3022 idActor::Event_FinishAction
3025 void idActor::Event_FinishAction( const char *actionname ) {
3026 if ( waitState == actionname ) {
3033 idActor::Event_AnimDone
3036 void idActor::Event_AnimDone( int channel, int blendFrames ) {
3040 case ANIMCHANNEL_HEAD :
3041 result = headAnim.AnimDone( blendFrames );
3042 idThread::ReturnInt( result );
3045 case ANIMCHANNEL_TORSO :
3046 result = torsoAnim.AnimDone( blendFrames );
3047 idThread::ReturnInt( result );
3050 case ANIMCHANNEL_LEGS :
3051 result = legsAnim.AnimDone( blendFrames );
3052 idThread::ReturnInt( result );
3056 gameLocal.Error( "Unknown anim group" );
3062 idActor::Event_HasAnim
3065 void idActor::Event_HasAnim( int channel, const char *animname ) {
3066 if ( GetAnim( channel, animname ) != NULL ) {
3067 idThread::ReturnFloat( 1.0f );
3069 idThread::ReturnFloat( 0.0f );
3075 idActor::Event_CheckAnim
3078 void idActor::Event_CheckAnim( int channel, const char *animname ) {
3079 if ( !GetAnim( channel, animname ) ) {
3080 if ( animPrefix.Length() ) {
3081 gameLocal.Error( "Can't find anim '%s_%s' for '%s'", animPrefix.c_str(), animname, name.c_str() );
3083 gameLocal.Error( "Can't find anim '%s' for '%s'", animname, name.c_str() );
3090 idActor::Event_ChooseAnim
3093 void idActor::Event_ChooseAnim( int channel, const char *animname ) {
3096 anim = GetAnim( channel, animname );
3098 if ( channel == ANIMCHANNEL_HEAD ) {
3099 if ( head.GetEntity() ) {
3100 idThread::ReturnString( head.GetEntity()->GetAnimator()->AnimFullName( anim ) );
3104 idThread::ReturnString( animator.AnimFullName( anim ) );
3109 idThread::ReturnString( "" );
3114 idActor::Event_AnimLength
3117 void idActor::Event_AnimLength( int channel, const char *animname ) {
3120 anim = GetAnim( channel, animname );
3122 if ( channel == ANIMCHANNEL_HEAD ) {
3123 if ( head.GetEntity() ) {
3124 idThread::ReturnFloat( MS2SEC( head.GetEntity()->GetAnimator()->AnimLength( anim ) ) );
3128 idThread::ReturnFloat( MS2SEC( animator.AnimLength( anim ) ) );
3133 idThread::ReturnFloat( 0.0f );
3138 idActor::Event_AnimDistance
3141 void idActor::Event_AnimDistance( int channel, const char *animname ) {
3144 anim = GetAnim( channel, animname );
3146 if ( channel == ANIMCHANNEL_HEAD ) {
3147 if ( head.GetEntity() ) {
3148 idThread::ReturnFloat( head.GetEntity()->GetAnimator()->TotalMovementDelta( anim ).Length() );
3152 idThread::ReturnFloat( animator.TotalMovementDelta( anim ).Length() );
3157 idThread::ReturnFloat( 0.0f );
3162 idActor::Event_HasEnemies
3165 void idActor::Event_HasEnemies( void ) {
3168 hasEnemy = HasEnemies();
3169 idThread::ReturnInt( hasEnemy );
3174 idActor::Event_NextEnemy
3177 void idActor::Event_NextEnemy( idEntity *ent ) {
3180 if ( !ent || ( ent == this ) ) {
3181 actor = enemyList.Next();
3183 if ( !ent->IsType( idActor::Type ) ) {
3184 gameLocal.Error( "'%s' cannot be an enemy", ent->name.c_str() );
3187 actor = static_cast<idActor *>( ent );
3188 if ( actor->enemyNode.ListHead() != &enemyList ) {
3189 gameLocal.Error( "'%s' is not in '%s' enemy list", actor->name.c_str(), name.c_str() );
3193 for( ; actor != NULL; actor = actor->enemyNode.Next() ) {
3194 if ( !actor->fl.hidden ) {
3195 idThread::ReturnEntity( actor );
3200 idThread::ReturnEntity( NULL );
3205 idActor::Event_ClosestEnemyToPoint
3208 void idActor::Event_ClosestEnemyToPoint( const idVec3 &pos ) {
3209 idActor *bestEnt = ClosestEnemyToPoint( pos );
3210 idThread::ReturnEntity( bestEnt );
3215 idActor::Event_StopSound
3218 void idActor::Event_StopSound( int channel, int netSync ) {
3219 if ( channel == SND_CHANNEL_VOICE ) {
3220 idEntity *headEnt = head.GetEntity();
3222 headEnt->StopSound( channel, ( netSync != 0 ) );
3225 StopSound( channel, ( netSync != 0 ) );
3229 =====================
3230 idActor::Event_SetNextState
3231 =====================
3233 void idActor::Event_SetNextState( const char *name ) {
3234 idealState = GetScriptFunction( name );
3235 if ( idealState == state ) {
3241 =====================
3242 idActor::Event_SetState
3243 =====================
3245 void idActor::Event_SetState( const char *name ) {
3246 idealState = GetScriptFunction( name );
3247 if ( idealState == state ) {
3250 scriptThread->DoneProcessing();
3254 =====================
3255 idActor::Event_GetState
3256 =====================
3258 void idActor::Event_GetState( void ) {
3260 idThread::ReturnString( state->Name() );
3262 idThread::ReturnString( "" );
3267 =====================
3268 idActor::Event_GetHead
3269 =====================
3271 void idActor::Event_GetHead( void ) {
3272 idThread::ReturnEntity( head.GetEntity() );