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