home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / ai / AI.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  143KB  |  5,132 lines

  1. /*
  2. ================
  3.  
  4. AI.cpp
  5.  
  6. ================
  7. */
  8.  
  9. #include "../../idlib/precompiled.h"
  10. #pragma hdrstop
  11.  
  12. #include "../Game_local.h"
  13.  
  14. #include "AI.h"
  15. #include "AI_Manager.h"
  16. #include "AI_Util.h"
  17. #include "../Projectile.h"
  18. #include "../spawner.h"
  19. #include "AI_Tactical.h"
  20.  
  21. const char* aiTalkMessageString [ ] = {
  22.     "None",
  23.     "primary",
  24.     "secondary",
  25.     "loop"
  26. };
  27.  
  28. static const float AI_SIGHTDELAYSCALE    = 5000.0f;            // Full sight delay at 5 seconds or more of not seeing enemy
  29.  
  30.  
  31. /*
  32. ===============================================================================
  33.  
  34.     idAI
  35.  
  36. ===============================================================================
  37. */
  38.  
  39. /*
  40. =====================
  41. idAI::idAI
  42. =====================
  43. */
  44. idAI::idAI ( void ) {
  45.     projectile_height_to_distance_ratio = 1.0f;
  46.  
  47.     aas                        = NULL;
  48.     aasSensor                = NULL;
  49.     aasFind                    = NULL;
  50.  
  51.     lastHitCheckResult        = false;
  52.     lastHitCheckTime        = 0;
  53.     lastAttackTime            = 0;
  54.     projectile                = NULL;
  55.     projectileClipModel        = NULL;
  56.     chatterTime                = 0;
  57.     talkState                = TALK_NEVER;
  58.     talkTarget                = NULL;
  59.     talkMessage                = TALKMSG_NONE;
  60.     talkBusyCount            = 0;
  61.  
  62.     enemy.ent                    = NULL;
  63.     enemy.lastVisibleChangeTime    = 0;
  64.     enemy.lastVisibleTime        = 0;
  65.  
  66.     fl.neverDormant            = false;        // AI's can go dormant
  67.  
  68.     allowEyeFocus            = true;
  69.     disablePain                = false;
  70.     allowJointMod            = true;
  71.     focusEntity                = NULL;
  72.     focusTime                = 0;
  73.     alignHeadTime            = 0;
  74.     forceAlignHeadTime        = 0;
  75.  
  76.     orientationJoint        = INVALID_JOINT;
  77.  
  78.     eyeVerticalOffset        = 0.0f;
  79.     eyeHorizontalOffset     = 0.0f;
  80.     headFocusRate            = 0.0f;
  81.     eyeFocusRate            = 0.0f;
  82.     focusAlignTime            = 0;
  83.     focusRange                = 0.0f;
  84.     focusType                = AIFOCUS_NONE;
  85.  
  86.     memset ( &combat.fl, 0, sizeof(combat.fl) );
  87.     combat.max_chasing_turn            = 0;
  88.     combat.shotAtTime                = 0;
  89.     combat.shotAtAngle                = 0.0f;
  90.     combat.meleeRange                = 0.0f;
  91.      combat.tacticalPainTaken        = 0;
  92.      combat.tacticalFlinches            = 0;
  93.      combat.investigateTime            = 0;
  94.      combat.aggressiveScale            = 1.0f;
  95.  
  96.     passive.animFidgetPrefix.Clear ( );
  97.     passive.animIdlePrefix.Clear ( );
  98.     passive.animTalkPrefix.Clear ( );
  99.     passive.idleAnim.Clear();
  100.     passive.prefix.Clear();
  101.     passive.idleAnimChangeTime        = 0;
  102.     passive.fidgetTime                = 0;
  103.     passive.talkTime                = 0;
  104.     memset ( &passive.fl, 0, sizeof(passive.fl) );
  105.             
  106.     pain.lastTakenTime                = 0;
  107.     pain.takenThisFrame                = 0;
  108.     pain.loopEndTime                = 0;
  109.         
  110.     enemy.range = 0;
  111.     enemy.range2d = 0;
  112.     enemy.smoothedLinearVelocity.Zero ( );
  113.     enemy.smoothedPushedVelocity.Zero ( );
  114.     enemy.lastKnownPosition.Zero ( );
  115.     enemy.lastVisibleEyePosition.Zero ( );
  116.     enemy.lastVisibleChestPosition.Zero ( );
  117.     enemy.lastVisibleFromEyePosition.Zero ( );
  118.     enemy.checkTime = 0;
  119.     enemy.changeTime = 0;
  120.     enemy.lastVisibleChangeTime = 0;
  121.     enemy.lastVisibleTime = 0;
  122.     memset ( &enemy.fl, 0, sizeof(enemy.fl) );
  123.  
  124.     currentFocusPos.Zero();
  125.     eyeAng.Zero();
  126.     lookAng.Zero();
  127.     destLookAng.Zero();
  128.     lookMin.Zero();
  129.     lookMax.Zero();
  130.  
  131.     eyeMin.Zero();
  132.     eyeMax.Zero();
  133.     
  134.     helperCurrent    = NULL;
  135.     helperIdeal        = NULL;
  136.  
  137.     speakTime        = 0;
  138.  
  139.     actionAnimNum    = 0;
  140.     actionSkipTime    = 0;
  141.     actionTime        = 0;
  142. }
  143.  
  144. /*
  145. =====================
  146. idAI::~idAI
  147. =====================
  148. */
  149. idAI::~idAI() {
  150.     // Make sure we arent stuck in the simple think list
  151.     simpleThinkNode.Remove ( );
  152.  
  153.     delete aasFind;
  154.     delete aasSensor;
  155.     delete projectileClipModel;
  156.     DeconstructScriptObject();
  157.     scriptObject.Free();
  158.     aiManager.RemoveTeammate ( this );
  159.     SetPhysics( NULL );
  160. }
  161.  
  162. /*
  163. =====================
  164. idAI::Save
  165. =====================
  166. */
  167. void idAI::Save( idSaveGame *savefile ) const {
  168.     int i;
  169.  
  170. // cnicholson: These 3 vars are intentionally not saved, as noted in the restore
  171.     // NOSAVE: idLinkList<idAI>        simpleThinkNode;
  172.     // NOSAVE: idAAS*                aas;
  173.     // NOSAVE: idAASCallback*        aasFind;
  174.     // NOTE That some AAS stuff is done at end of ::Restore
  175.  
  176.     // Movement
  177.     move.Save( savefile );
  178.     savedMove.Save( savefile );
  179.     
  180.     savefile->WriteStaticObject( physicsObj );
  181.     savefile->WriteBool( GetPhysics() == static_cast<const idPhysics *>(&physicsObj) );
  182.  
  183.     savefile->WriteBool( lastHitCheckResult );
  184.     savefile->WriteInt( lastHitCheckTime );
  185.     savefile->WriteInt( lastAttackTime );
  186.     savefile->WriteFloat( projectile_height_to_distance_ratio );
  187.  
  188.     savefile->WriteInt( attackAnimInfo.Num() );
  189.     for( i = 0; i < attackAnimInfo.Num(); i++ ) {
  190.         savefile->WriteVec3( attackAnimInfo[ i ].attackOffset );
  191.         savefile->WriteVec3( attackAnimInfo[ i ].eyeOffset );
  192.     }
  193.     
  194.     // TOSAVE: mutable idClipModel*    projectileClipModel;
  195.     projectile.Save ( savefile );
  196.  
  197.     // Talking
  198.     savefile->WriteInt( chatterTime );
  199. //    savefile->WriteInt( chatterRateCombat );    // NOSAVE:
  200. //    savefile->WriteInt( chatterRateIdle );        // NOSAVE:
  201.     savefile->WriteInt( talkState );
  202.     talkTarget.Save( savefile );
  203.     savefile->WriteInt( talkMessage );
  204.     savefile->WriteInt( talkBusyCount );
  205.     savefile->WriteInt ( speakTime );
  206.  
  207.     // Focus
  208.     lookTarget.Save ( savefile );
  209.     savefile->WriteInt( focusType );
  210.     focusEntity.Save ( savefile );
  211.     savefile->WriteFloat ( focusRange );
  212.     savefile->WriteInt ( focusAlignTime );
  213.     savefile->WriteInt ( focusTime );
  214.     savefile->WriteVec3( currentFocusPos );
  215.  
  216.     // Looking
  217.     savefile->WriteBool( allowJointMod );
  218.     savefile->WriteInt( alignHeadTime );
  219.     savefile->WriteInt( forceAlignHeadTime );
  220.     savefile->WriteAngles( eyeAng );
  221.     savefile->WriteAngles( lookAng );
  222.     savefile->WriteAngles( destLookAng );
  223.     savefile->WriteAngles( lookMin );
  224.     savefile->WriteAngles( lookMax );
  225.  
  226.     savefile->WriteInt( lookJoints.Num() );
  227.     for( i = 0; i < lookJoints.Num(); i++ ) {
  228.         savefile->WriteJoint( lookJoints[ i ] );
  229.         savefile->WriteAngles( lookJointAngles[ i ] );
  230.     }
  231.  
  232.     savefile->WriteFloat( eyeVerticalOffset );
  233.     savefile->WriteFloat( eyeHorizontalOffset );
  234.     savefile->WriteFloat( headFocusRate );
  235.     savefile->WriteFloat( eyeFocusRate );
  236.  
  237.     // Joint Controllers
  238.     savefile->WriteAngles( eyeMin );
  239.     savefile->WriteAngles( eyeMax );
  240.     savefile->WriteJoint( orientationJoint );
  241.  
  242.     pusher.Save( savefile );            // cnicholson: Added unsaved var
  243.     scriptedActionEnt.Save( savefile ); // cnicholson: Added unsaved var
  244.     
  245.     savefile->Write( &aifl, sizeof( aifl ) );
  246.  
  247.     // Misc
  248.     savefile->WriteInt ( actionAnimNum );
  249.     savefile->WriteInt ( actionTime );
  250.     savefile->WriteInt ( actionSkipTime );
  251.     savefile->WriteInt ( flagOverrides );
  252.  
  253.     // Combat variables
  254.     savefile->Write( &combat.fl, sizeof( combat.fl ) );
  255.     savefile->WriteFloat ( combat.max_chasing_turn );
  256.     savefile->WriteFloat ( combat.shotAtTime );
  257.     savefile->WriteFloat ( combat.shotAtAngle );
  258.     savefile->WriteVec2 ( combat.hideRange );
  259.     savefile->WriteVec2 ( combat.attackRange );
  260.     savefile->WriteInt ( combat.attackSightDelay );
  261.     savefile->WriteFloat ( combat.meleeRange );
  262.     savefile->WriteFloat ( combat.aggressiveRange );
  263.     savefile->WriteFloat ( combat.aggressiveScale );
  264.     savefile->WriteInt ( combat.investigateTime );    
  265.     savefile->WriteFloat ( combat.visStandHeight );
  266.     savefile->WriteFloat ( combat.visCrouchHeight );
  267.     savefile->WriteFloat ( combat.visRange );
  268.     savefile->WriteFloat ( combat.earRange );
  269.     savefile->WriteFloat ( combat.awareRange );
  270.     savefile->WriteInt ( combat.tacticalMaskAvailable );
  271.     savefile->WriteInt ( (int&)combat.tacticalCurrent );    
  272.     savefile->WriteInt ( combat.tacticalUpdateTime );    
  273.     savefile->WriteInt ( combat.tacticalPainTaken );    
  274.     savefile->WriteInt ( combat.tacticalPainThreshold );    
  275.     savefile->WriteInt ( combat.tacticalFlinches );    
  276.     savefile->WriteInt ( combat.maxLostVisTime );    
  277.     savefile->WriteFloat ( combat.threatBase );
  278.     savefile->WriteFloat ( combat.threatCurrent );
  279.     savefile->WriteInt     ( combat.coverValidTime );
  280.     savefile->WriteInt   ( combat.maxInvalidCoverTime );
  281.  
  282.     // Passive state variables
  283.     savefile->WriteString ( passive.prefix );
  284.     savefile->WriteString ( passive.animFidgetPrefix );
  285.     savefile->WriteString ( passive.animIdlePrefix);
  286.     savefile->WriteString ( passive.animTalkPrefix );
  287.     savefile->WriteString ( passive.idleAnim );
  288.     savefile->WriteInt      ( passive.idleAnimChangeTime );
  289.     savefile->WriteInt      ( passive.fidgetTime );
  290.     savefile->WriteInt      ( passive.talkTime );
  291.     savefile->Write ( &passive.fl, sizeof(passive.fl) );
  292.  
  293.     // Enemy 
  294.     enemy.ent.Save ( savefile );
  295.     savefile->Write ( &enemy.fl, sizeof(enemy.fl) );
  296.     savefile->WriteInt ( enemy.lastVisibleChangeTime );
  297.     savefile->WriteVec3 ( enemy.lastKnownPosition );
  298.     savefile->WriteVec3 ( enemy.smoothedLinearVelocity );
  299.     savefile->WriteVec3 ( enemy.smoothedPushedVelocity );
  300.     savefile->WriteVec3 ( enemy.lastVisibleEyePosition );
  301.     savefile->WriteVec3 ( enemy.lastVisibleChestPosition );
  302.     savefile->WriteVec3 ( enemy.lastVisibleFromEyePosition );
  303.     savefile->WriteInt ( enemy.lastVisibleTime );
  304.     savefile->WriteFloat ( enemy.range );
  305.     savefile->WriteFloat ( enemy.range2d );
  306.     savefile->WriteInt ( enemy.changeTime );
  307.     savefile->WriteInt ( enemy.checkTime );
  308.  
  309.     // Pain variables
  310.     savefile->WriteFloat ( pain.threshold );
  311.     savefile->WriteFloat ( pain.takenThisFrame );
  312.     savefile->WriteInt ( pain.lastTakenTime );
  313.     savefile->WriteInt ( pain.loopEndTime );
  314.     savefile->WriteString ( pain.loopType );
  315.  
  316.     // Functions
  317.     funcs.first_sight.Save ( savefile );
  318.     funcs.sight.Save ( savefile );
  319.     funcs.pain.Save ( savefile );
  320.     funcs.damage.Save ( savefile );
  321.     funcs.death.Save ( savefile );
  322.     funcs.attack.Save ( savefile );
  323.     funcs.init.Save ( savefile );
  324.     funcs.onclick.Save ( savefile );
  325.     funcs.launch_projectile.Save ( savefile );
  326.     funcs.footstep.Save ( savefile );
  327.  
  328.     mPlayback.Save( savefile );        // cnicholson: Added save functionality
  329.     mLookPlayback.Save( savefile );    // cnicholson: Added save functionality
  330.  
  331.     // Tactical Sensor
  332.     aasSensor->Save( savefile );
  333.  
  334.     // Helpers
  335.     tether.Save ( savefile );
  336.     helperCurrent.Save ( savefile );
  337.     helperIdeal.Save ( savefile );
  338.     leader.Save ( savefile );
  339.     spawner.Save ( savefile );
  340.  
  341.     // Action timers
  342.     actionTimerRangedAttack.Save ( savefile );
  343.     actionTimerEvade.Save ( savefile );
  344.     actionTimerSpecialAttack.Save ( savefile );
  345.     actionTimerPain.Save ( savefile );
  346.     
  347.     // Actions
  348.     actionEvadeLeft.Save ( savefile );
  349.     actionEvadeRight.Save ( savefile );
  350.     actionRangedAttack.Save ( savefile );
  351.     actionMeleeAttack.Save ( savefile );
  352.     actionLeapAttack.Save ( savefile );
  353.     actionJumpBack.Save ( savefile );
  354. }
  355.  
  356. /*
  357. =====================
  358. idAI::Restore
  359. =====================
  360. */
  361. void idAI::Restore( idRestoreGame *savefile ) {
  362.     bool        restorePhysics;
  363.     int            i;
  364.     int            num;
  365.     idBounds    bounds;
  366.  
  367.     InitNonPersistentSpawnArgs ( );
  368.  
  369.     // INTENTIONALLY NOT SAVED: idLinkList<idAI>    simpleThinkNode;
  370.     // INTENTIONALLY NOT SAVED: idAAS*                aas;
  371.     // INTENTIONALLY NOT SAVED: idAASCallback*        aasFind;
  372.     // NOTE That some AAS stuff is done at end of ::Restore
  373.  
  374.     move.Restore( savefile );
  375.     savedMove.Restore( savefile );
  376.  
  377.     savefile->ReadStaticObject( physicsObj );
  378.     savefile->ReadBool( restorePhysics );
  379.     if ( restorePhysics ) {
  380.         RestorePhysics( &physicsObj );
  381.     }
  382.  
  383.     savefile->ReadBool( lastHitCheckResult );
  384.     savefile->ReadInt( lastHitCheckTime );
  385.     savefile->ReadInt( lastAttackTime );
  386.     savefile->ReadFloat( projectile_height_to_distance_ratio );
  387.  
  388.     savefile->ReadInt( num );
  389.     attackAnimInfo.SetGranularity( 1 );
  390.     attackAnimInfo.SetNum( num );
  391.     for( i = 0; i < num; i++ ) {
  392.         savefile->ReadVec3( attackAnimInfo[ i ].attackOffset );
  393.         savefile->ReadVec3( attackAnimInfo[ i ].eyeOffset );
  394.     }
  395.  
  396.     // TORESTORE: mutable idClipModel*    projectileClipModel;
  397.     projectile.Restore( savefile );
  398.  
  399.     // Talking
  400.     savefile->ReadInt( chatterTime );
  401. //    savefile->ReadInt( chatterRateCombat );        // Don't save
  402. //    savefile->ReadInt( chatterRateIdle );        // Don't save
  403.     savefile->ReadInt( i );
  404.     talkState = static_cast<talkState_t>( i );
  405.     talkTarget.Restore( savefile );
  406.     savefile->ReadInt( i );
  407.     talkMessage = static_cast<talkMessage_t>( i );
  408.     savefile->ReadInt( talkBusyCount );
  409.     savefile->ReadInt ( speakTime );
  410.  
  411.     // Focus
  412.     lookTarget.Restore ( savefile );
  413.     savefile->ReadInt( i );
  414.     focusType = static_cast<aiFocus_t>( i );
  415.     focusEntity.Restore ( savefile );
  416.     savefile->ReadFloat ( focusRange );
  417.     savefile->ReadInt ( focusAlignTime );
  418.     savefile->ReadInt ( focusTime );
  419.     savefile->ReadVec3( currentFocusPos );
  420.     
  421.     // Looking
  422.     savefile->ReadBool( allowJointMod );
  423.     savefile->ReadInt( alignHeadTime );
  424.     savefile->ReadInt( forceAlignHeadTime );
  425.     savefile->ReadAngles( eyeAng );
  426.     savefile->ReadAngles( lookAng );
  427.     savefile->ReadAngles( destLookAng );
  428.     savefile->ReadAngles( lookMin );
  429.     savefile->ReadAngles( lookMax );
  430.  
  431.     savefile->ReadInt( num );
  432.     lookJoints.SetGranularity( 1 );
  433.     lookJoints.SetNum( num );
  434.     lookJointAngles.SetGranularity( 1 );
  435.     lookJointAngles.SetNum( num );
  436.     for( i = 0; i < num; i++ ) {
  437.         savefile->ReadJoint( lookJoints[ i ] );
  438.         savefile->ReadAngles( lookJointAngles[ i ] );
  439.     }
  440.  
  441.     savefile->ReadFloat( eyeVerticalOffset );
  442.     savefile->ReadFloat( eyeHorizontalOffset );
  443.     savefile->ReadFloat( headFocusRate );
  444.     savefile->ReadFloat( eyeFocusRate );
  445.  
  446.     // Joint Controllers
  447.     savefile->ReadAngles( eyeMin );
  448.     savefile->ReadAngles( eyeMax );
  449.     savefile->ReadJoint( orientationJoint );
  450.  
  451.     pusher.Restore( savefile );                // cnicholson: Added unrestored var
  452.     scriptedActionEnt.Restore ( savefile ); // cnicholson: Added unrestored var
  453.  
  454.     savefile->Read( &aifl, sizeof( aifl ) );
  455.  
  456.     // Misc
  457.     savefile->ReadInt ( actionAnimNum );
  458.     savefile->ReadInt ( actionTime );
  459.     savefile->ReadInt ( actionSkipTime );
  460.     savefile->ReadInt ( flagOverrides );
  461.  
  462.     // Combat variables
  463.     savefile->Read ( &combat.fl, sizeof( combat.fl ) );
  464.     savefile->ReadFloat ( combat.max_chasing_turn );
  465.     savefile->ReadFloat ( combat.shotAtTime );
  466.     savefile->ReadFloat ( combat.shotAtAngle );
  467.     savefile->ReadVec2 ( combat.hideRange );
  468.     savefile->ReadVec2 ( combat.attackRange );
  469.     savefile->ReadInt ( combat.attackSightDelay );
  470.     savefile->ReadFloat ( combat.meleeRange );
  471.     savefile->ReadFloat ( combat.aggressiveRange );
  472.     savefile->ReadFloat ( combat.aggressiveScale );
  473.     savefile->ReadInt ( combat.investigateTime );    
  474.     savefile->ReadFloat ( combat.visStandHeight );
  475.     savefile->ReadFloat ( combat.visCrouchHeight );
  476.     savefile->ReadFloat ( combat.visRange );
  477.     savefile->ReadFloat ( combat.earRange );
  478.     savefile->ReadFloat ( combat.awareRange );
  479.     savefile->ReadInt ( combat.tacticalMaskAvailable );    
  480.     savefile->ReadInt ( (int&)combat.tacticalCurrent );    
  481.     savefile->ReadInt ( combat.tacticalUpdateTime );    
  482.     savefile->ReadInt ( combat.tacticalPainTaken );    
  483.     savefile->ReadInt ( combat.tacticalPainThreshold );    
  484.     savefile->ReadInt ( combat.tacticalFlinches );    
  485.     savefile->ReadInt ( combat.maxLostVisTime );    
  486.     savefile->ReadFloat ( combat.threatBase );
  487.     savefile->ReadFloat ( combat.threatCurrent );
  488.     savefile->ReadInt     ( combat.coverValidTime );
  489.     savefile->ReadInt   ( combat.maxInvalidCoverTime );
  490.  
  491.     // Passive state variables
  492.     savefile->ReadString ( passive.prefix );
  493.     savefile->ReadString ( passive.animFidgetPrefix );
  494.     savefile->ReadString ( passive.animIdlePrefix);
  495.     savefile->ReadString ( passive.animTalkPrefix );
  496.     savefile->ReadString ( passive.idleAnim );
  497.     savefile->ReadInt     ( passive.idleAnimChangeTime );
  498.     savefile->ReadInt     ( passive.fidgetTime );
  499.     savefile->ReadInt     ( passive.talkTime );
  500.     savefile->Read ( &passive.fl, sizeof(passive.fl) );
  501.  
  502.     // Enemy 
  503.     enemy.ent.Restore ( savefile );
  504.     savefile->Read ( &enemy.fl, sizeof(enemy.fl) );
  505.     savefile->ReadInt ( enemy.lastVisibleChangeTime );
  506.     savefile->ReadVec3 ( enemy.lastKnownPosition );
  507.     savefile->ReadVec3 ( enemy.smoothedLinearVelocity );
  508.     savefile->ReadVec3 ( enemy.smoothedPushedVelocity );
  509.     savefile->ReadVec3 ( enemy.lastVisibleEyePosition );
  510.     savefile->ReadVec3 ( enemy.lastVisibleChestPosition );
  511.     savefile->ReadVec3 ( enemy.lastVisibleFromEyePosition );
  512.     savefile->ReadInt ( enemy.lastVisibleTime );
  513.     savefile->ReadFloat ( enemy.range );
  514.     savefile->ReadFloat ( enemy.range2d );
  515.     savefile->ReadInt ( enemy.changeTime );
  516.     savefile->ReadInt ( enemy.checkTime );
  517.  
  518.     // Pain variables
  519.     savefile->ReadFloat ( pain.threshold );
  520.     savefile->ReadFloat ( pain.takenThisFrame );
  521.     savefile->ReadInt ( pain.lastTakenTime );
  522.     savefile->ReadInt ( pain.loopEndTime );
  523.     savefile->ReadString ( pain.loopType );
  524.  
  525.     // Functions
  526.     funcs.first_sight.Restore ( savefile );
  527.     funcs.sight.Restore ( savefile );
  528.     funcs.pain.Restore ( savefile );
  529.     funcs.damage.Restore ( savefile );
  530.     funcs.death.Restore ( savefile );
  531.     funcs.attack.Restore ( savefile );
  532.     funcs.init.Restore ( savefile );
  533.     funcs.onclick.Restore ( savefile );
  534.     funcs.launch_projectile.Restore ( savefile );
  535.     funcs.footstep.Restore ( savefile );
  536.  
  537.     mPlayback.Restore( savefile );        // cnicholson: Added restore functionality
  538.     mLookPlayback.Restore( savefile );    // cnicholson: Added restore functionality
  539.  
  540.     // Tactical Sensor
  541.     aasSensor->Restore( savefile );
  542.  
  543.     // Helpers
  544.     tether.Restore ( savefile );
  545.     helperCurrent.Restore( savefile );
  546.     helperIdeal.Restore ( savefile );
  547.     leader.Restore ( savefile );
  548.     spawner.Restore ( savefile );
  549.  
  550.     // Action timers
  551.     actionTimerRangedAttack.Restore ( savefile );
  552.     actionTimerEvade.Restore ( savefile );
  553.     actionTimerSpecialAttack.Restore ( savefile );
  554.     actionTimerPain.Restore ( savefile );
  555.     
  556.     // Actions
  557.     actionEvadeLeft.Restore ( savefile );
  558.     actionEvadeRight.Restore ( savefile );
  559.     actionRangedAttack.Restore ( savefile );
  560.     actionMeleeAttack.Restore ( savefile );
  561.     actionLeapAttack.Restore ( savefile );
  562.     actionJumpBack.Restore ( savefile );
  563.  
  564.     // Set the AAS if the character has the correct gravity vector
  565.     idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
  566.     gravity *= g_gravity.GetFloat();
  567.     if ( gravity == gameLocal.GetGravity() ) {
  568.         SetAAS();
  569.     }
  570.     
  571.     // create combat collision hull for exact collision detection, do initial set un-hidden
  572.     SetCombatModel();
  573.     LinkCombat();
  574. }
  575.  
  576. /*
  577. =====================
  578. idAI::InitNonPersistentSpawnArgs
  579. =====================
  580. */
  581. void idAI::InitNonPersistentSpawnArgs ( void ) {
  582.     aasSensor = rvAASTacticalSensor::CREATE_SENSOR(this);
  583.     aasFind   = NULL;
  584.  
  585.     simpleThinkNode.Remove ( );
  586.     simpleThinkNode.SetOwner ( this );
  587.     
  588.     chatterRateIdle      = SEC2MS ( spawnArgs.GetFloat ( "chatter_rate_idle", "0" ) );
  589.     chatterRateCombat = SEC2MS ( spawnArgs.GetFloat ( "chatter_rate_combat", "0" ) );
  590.     chatterTime          = 0;    
  591.     
  592.     combat.tacticalMaskUpdate = 0;
  593.         
  594.     enemy.smoothVelocityRate = spawnArgs.GetFloat ( "smoothVelocityRate", "0.1" );
  595. }
  596.  
  597. /*
  598. =====================
  599. idAI::Spawn
  600. =====================
  601. */
  602. void idAI::Spawn( void ) {
  603.     const char*            jointname;
  604.     const idKeyValue*    kv;
  605.     idStr                jointName;
  606.     idAngles            jointScale;
  607.     jointHandle_t        joint;
  608.     idVec3                local_dir;
  609.  
  610.     // Are all monsters disabled?
  611.     if ( !g_monsters.GetBool() ) {
  612.         PostEventMS( &EV_Remove, 0 );
  613.         return;
  614.     }
  615.  
  616.     // Initialize the non saved spawn args
  617.     InitNonPersistentSpawnArgs ( );    
  618.  
  619.     spawnArgs.GetInt(    "team",                    "1",        team );
  620.     spawnArgs.GetInt(    "rank",                    "0",        rank );
  621.  
  622.     animPrefix = spawnArgs.GetString ( "animPrefix", "" );
  623.  
  624.     // Standard flags
  625.     fl.notarget = spawnArgs.GetBool ( "notarget", "0" );
  626.  
  627.     // AI flags
  628.     flagOverrides = 0;
  629.     memset ( &aifl, 0, sizeof(aifl) );
  630.     aifl.ignoreFlashlight         = spawnArgs.GetBool ( "ignore_flashlight", "1" );
  631.     aifl.lookAtPlayer              = spawnArgs.GetBool ( "lookAtPlayer", "0" );
  632.     aifl.disableLook              = spawnArgs.GetBool ( "noLook", "0" );
  633.     aifl.undying                  = spawnArgs.GetBool ( "undying", "0" );
  634.     aifl.killerGuard            = spawnArgs.GetBool ( "killer_guard", "0" );
  635.     aifl.scriptedEndWithIdle    = true;
  636.  
  637.     // Setup Move Data
  638.     move.Spawn( spawnArgs );
  639.  
  640.     pain.threshold        = spawnArgs.GetInt ( "painThreshold", "0" );
  641.     pain.takenThisFrame    = 0;        
  642.     
  643.     // Initialize combat variables
  644.      combat.fl.aware                    = spawnArgs.GetBool ( "ambush", "0" );
  645.      combat.fl.tetherNoBreak            = spawnArgs.GetBool ( "tetherNoBreak", "0" );
  646.      combat.fl.noChatter                = spawnArgs.GetBool ( "noCombatChatter" );
  647.     combat.hideRange                = spawnArgs.GetVec2 ( "hideRange", "150 750" );
  648.     combat.attackRange                = spawnArgs.GetVec2 ( "attackRange", "0 1000" );
  649.     combat.attackSightDelay            = SEC2MS ( spawnArgs.GetFloat ( "attackSightDelay", "1" ) );
  650.      combat.visRange                    = spawnArgs.GetFloat( "visRange", "2048" );
  651.      combat.visStandHeight            = spawnArgs.GetFloat( "visStandHeight", "68" );
  652.      combat.visCrouchHeight            = spawnArgs.GetFloat( "visCrouchHeight", "48" );
  653.      combat.earRange                    = spawnArgs.GetFloat( "earRange", "2048" );
  654.      combat.awareRange                = spawnArgs.GetFloat( "awareRange", "150" );
  655.      combat.aggressiveRange            = spawnArgs.GetFloat( "aggressiveRange", "0" );
  656.      combat.maxLostVisTime            = SEC2MS ( spawnArgs.GetFloat ( "maxLostVisTime", "10" ) );
  657.      combat.tacticalPainThreshold    = spawnArgs.GetInt ( "tactical_painThreshold", va("%d", health / 4) );
  658.     combat.coverValidTime            = 0;
  659.     combat.maxInvalidCoverTime        = SEC2MS ( spawnArgs.GetFloat ( "maxInvalidCoverTime", "1" ) );
  660.     combat.threatBase                = spawnArgs.GetFloat ( "threatBase", "1" );
  661.     combat.threatCurrent            = combat.threatBase;
  662.  
  663.     SetPassivePrefix ( spawnArgs.GetString ( "passivePrefix" ) );
  664.  
  665.     disablePain = spawnArgs.GetBool ( "nopain", "0" );
  666.  
  667.     spawnArgs.GetFloat( "melee_range",            "64",        combat.meleeRange );
  668.     spawnArgs.GetFloat( "projectile_height_to_distance_ratio",    "1", projectile_height_to_distance_ratio );
  669.     
  670.     //melee superhero -- take far reduced damage from melee.
  671.     if ( spawnArgs.GetString( "objectivetitle_failed", NULL ) && spawnArgs.GetBool( "meleeSuperhero", "1")   )  {
  672.         aifl.meleeSuperhero = true;
  673.     } else    {
  674.         aifl.meleeSuperhero = false;
  675.     }
  676.  
  677.     //announce rate
  678.     announceRate = spawnArgs.GetFloat( "announceRate" );
  679.     if( 0.0f == announceRate )    {
  680.         announceRate = AISPEAK_CHANCE;
  681.     }
  682.  
  683.     fl.takedamage = !spawnArgs.GetBool( "noDamage" );
  684.  
  685.     animator.RemoveOriginOffset( true );
  686.  
  687.     // create combat collision hull for exact collision detection
  688.     SetCombatModel();
  689.  
  690.     lookMin    = spawnArgs.GetAngles( "look_min", "-80 -75 0" );
  691.     lookMax    = spawnArgs.GetAngles( "look_max", "80 75 0" );
  692.  
  693.     lookJoints.SetGranularity( 1 );
  694.     lookJointAngles.SetGranularity( 1 );
  695.     kv = spawnArgs.MatchPrefix( "look_joint", NULL );
  696.     while( kv ) {
  697.         jointName = kv->GetKey();
  698.         jointName.StripLeadingOnce( "look_joint " );
  699.         joint = animator.GetJointHandle( jointName );
  700.         if ( joint == INVALID_JOINT ) {
  701.             gameLocal.Warning( "Unknown look_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
  702.         } else {
  703.             jointScale = spawnArgs.GetAngles( kv->GetKey(), "0 0 0" );
  704.             jointScale.roll = 0.0f;
  705.  
  706.             // if no scale on any component, then don't bother adding it.  this may be done to
  707.             // zero out rotation from an inherited entitydef.
  708.             if ( jointScale != ang_zero ) {
  709.                 lookJoints.Append( joint );
  710.                 lookJointAngles.Append( jointScale );
  711.             }
  712.         }
  713.         kv = spawnArgs.MatchPrefix( "look_joint", kv );
  714.     }
  715.  
  716.     // calculate joint positions on attack frames so we can do proper "can hit" tests
  717.     CalculateAttackOffsets();
  718.  
  719.     eyeMin                = spawnArgs.GetAngles( "eye_turn_min", "-10 -30 0" );
  720.     eyeMax                = spawnArgs.GetAngles( "eye_turn_max", "10 30 0" );
  721.     eyeVerticalOffset    = spawnArgs.GetFloat( "eye_verticle_offset", "5" );
  722.     eyeHorizontalOffset = spawnArgs.GetFloat( "eye_horizontal_offset", "-8" );
  723.     headFocusRate        = spawnArgs.GetFloat( "head_focus_rate", "0.06" );
  724.     eyeFocusRate        = spawnArgs.GetFloat( "eye_focus_rate", "0.5" );
  725.     focusRange            = spawnArgs.GetFloat( "focus_range", "0" );
  726.     focusAlignTime        = SEC2MS( spawnArgs.GetFloat( "focus_align_time", "1" ) );
  727.  
  728.     jointname = spawnArgs.GetString( "joint_orientation" );
  729.     if ( *jointname ) {
  730.         orientationJoint = animator.GetJointHandle( jointname );
  731.         if ( orientationJoint == INVALID_JOINT ) {
  732.             gameLocal.Warning( "Joint '%s' not found on '%s'", jointname, name.c_str() );
  733.         }
  734.     }
  735.  
  736.     jointname = spawnArgs.GetString( "joint_flytilt" );
  737.     if ( *jointname ) {
  738.         move.flyTiltJoint = animator.GetJointHandle( jointname );
  739.         if ( move.flyTiltJoint == INVALID_JOINT ) {
  740.             gameLocal.Warning( "Joint '%s' not found on '%s'", jointname, name.c_str() );
  741.         }
  742.     }
  743.  
  744.     physicsObj.SetSelf( this );
  745.     physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  746.     physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
  747.  
  748.     if ( spawnArgs.GetBool( "big_monster" ) ) {
  749.         physicsObj.SetContents( 0 );
  750.         physicsObj.SetClipMask( MASK_MONSTERSOLID & ~CONTENTS_BODY );
  751.     } else {
  752.         if ( use_combat_bbox ) {
  753.             physicsObj.SetContents( CONTENTS_BODY|CONTENTS_SOLID );
  754.         } else {
  755.             physicsObj.SetContents( CONTENTS_BODY );
  756.         }
  757.         physicsObj.SetClipMask( MASK_MONSTERSOLID );
  758.     }
  759.  
  760.     // move up to make sure the monster is at least an epsilon above the floor
  761.     physicsObj.SetOrigin( GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  762.     
  763.     idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
  764.     gravity *= g_gravity.GetFloat();
  765.     physicsObj.SetGravity( gravity );
  766.  
  767.     SetPhysics( &physicsObj );
  768.  
  769.     physicsObj.GetGravityAxis().ProjectVector( viewAxis[ 0 ], local_dir );
  770.     move.current_yaw        = local_dir.ToYaw();
  771.     move.ideal_yaw            = idMath::AngleNormalize180( move.current_yaw );
  772.     
  773.     lookAng.Zero ( );
  774.     lookAng.yaw        = move.current_yaw;
  775.  
  776.  
  777.     SetAAS();
  778.  
  779.     projectile        = NULL;
  780.     projectileClipModel    = NULL;
  781.  
  782.     if ( spawnArgs.GetBool( "hide" ) || spawnArgs.GetBool( "trigger_anim" ) ) {
  783.         fl.takedamage = false;
  784.         physicsObj.SetContents( 0 );
  785.         physicsObj.GetClipModel()->Unlink();
  786.         Hide();
  787.     } else {
  788.         // play a looping ambient sound if we have one
  789.         StartSound( "snd_ambient", SND_CHANNEL_AMBIENT, 0, false, NULL );
  790.     }
  791.  
  792.     if ( health <= 0 ) {
  793.         gameLocal.Warning( "entity '%s' doesn't have health set", name.c_str() );
  794.         health = 1;
  795.     }
  796.  
  797.     BecomeActive( TH_THINK );
  798.  
  799.     if ( move.fl.allowPushMovables ) {
  800.         af.SetupPose( this, gameLocal.time );
  801.         af.GetPhysics()->EnableClip();
  802.     }
  803.  
  804.     // init the move variables
  805.     StopMove( MOVE_STATUS_DONE );
  806.     
  807.     // Initialize any scripts
  808.     idStr prefix = "script_";
  809.     for ( kv = spawnArgs.MatchPrefix ( prefix.c_str(), NULL );
  810.           kv;
  811.           kv = spawnArgs.MatchPrefix ( prefix.c_str(), kv ) ) {
  812.               SetScript ( kv->GetKey().c_str() + prefix.Length(), kv->GetValue() );
  813.     }
  814.     
  815.     // Initialize actions    
  816.     actionEvadeLeft.Init        ( spawnArgs, "action_evadeLeft",        NULL,                    0 );
  817.     actionEvadeRight.Init        ( spawnArgs, "action_evadeRight",        NULL,                     0 );
  818.     actionRangedAttack.Init        ( spawnArgs, "action_rangedAttack",        NULL,                     AIACTIONF_ATTACK );
  819.     actionMeleeAttack.Init        ( spawnArgs, "action_meleeAttack",        NULL,                     AIACTIONF_ATTACK|AIACTIONF_MELEE );
  820.     actionLeapAttack.Init        ( spawnArgs, "action_leapAttack",        NULL,                     AIACTIONF_ATTACK );
  821.     actionJumpBack.Init            ( spawnArgs, "action_jumpBack",            NULL,                     0 );
  822.             
  823.     // Global action timers
  824.     actionTimerRangedAttack.Init    ( spawnArgs, "actionTimer_RangedAttack" );
  825.     actionTimerEvade.Init            ( spawnArgs, "actionTimer_Evade" );
  826.     actionTimerSpecialAttack.Init    ( spawnArgs, "actionTimer_SpecialAttack" );
  827.     actionTimerPain.Init            ( spawnArgs, "actionTimer_pain" );
  828.  
  829.     // Available tactical options    
  830.     combat.tacticalUpdateTime = 0;
  831.     combat.tacticalCurrent    = AITACTICAL_NONE;
  832.     combat.tacticalMaskUpdate = 0;
  833.     combat.tacticalMaskAvailable  = AITACTICAL_TURRET_BIT|AITACTICAL_MOVE_FOLLOW_BIT|AITACTICAL_MOVE_TETHER_BIT;
  834.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "tactical_cover",   "0" )        ? AITACTICAL_COVER_BITS                : 0);
  835.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "tactical_ranged",  "0" )         ? AITACTICAL_RANGED_BITS            : 0);
  836.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "tactical_hide",    "0" )         ? AITACTICAL_HIDE_BIT                : 0);    
  837.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "tactical_rush",    "0" )         ? AITACTICAL_MELEE_BIT                : 0);
  838.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "tactical_passive", "0" )         ? AITACTICAL_PASSIVE_BIT            : 0);
  839.     combat.tacticalMaskAvailable |= (spawnArgs.GetBool ( "allowPlayerPush",  "0" )        ? AITACTICAL_MOVE_PLAYERPUSH_BIT    : 0);
  840.     
  841.     // Talking?
  842.     const char* npc;
  843.     if ( spawnArgs.GetString( "npc_name", NULL, &npc ) != NULL && *npc ) {
  844.         if ( spawnArgs.GetBool ( "follows", "0" ) ) {
  845.             SetTalkState ( TALK_FOLLOW );
  846.         } else {
  847.             switch ( spawnArgs.GetInt( "talks" ) )
  848.             {
  849.             case 2:
  850.                 SetTalkState ( TALK_WAIT );
  851.                 break;
  852.             case 1:
  853.                 SetTalkState ( TALK_OK );
  854.                 break;
  855.             case 0:
  856.             default:
  857.                 SetTalkState ( TALK_BUSY );
  858.                 break;
  859.             }
  860.         }
  861.     } else {
  862.         SetTalkState ( TALK_NEVER );
  863.     }
  864.  
  865.     // Passive or aggressive ai?
  866.     if ( spawnArgs.GetBool ( "passive" ) ) {
  867.         Event_BecomePassive ( true );
  868.         
  869.         if ( spawnArgs.GetInt ( "passive" ) > 1 ) {
  870.             aifl.disableLook = true;
  871.         }
  872.     }
  873.     
  874.     // Print out a warning about any AI that is spawned unhidden since they will be all thinking
  875.     if( gameLocal.GameState ( ) == GAMESTATE_STARTUP && !spawnArgs.GetInt( "hide" ) && !spawnArgs.GetInt( "trigger_anim" ) && !spawnArgs.GetInt( "trigger_cover" ) && !spawnArgs.GetInt( "trigger_move" ) ){
  876.         gameLocal.Warning( "Unhidden AI placed in map (will be constantly active): %s (%s)", name.c_str(), GetPhysics()->GetOrigin().ToString() );
  877.     }
  878.  
  879.     Begin ( );
  880.  
  881.     // RAVEN BEGIN
  882.     // twhitaker: needed this for difficulty settings
  883.     PostEventMS( &EV_PostSpawn, 0 );
  884.     // RAVEN END
  885. }
  886.  
  887. /*
  888. ===================
  889. idAI::Begin
  890. ===================
  891. */
  892. void idAI::Begin ( void ) {
  893.     const char* temp;
  894.     bool        animWalk;
  895.     bool        animRun;
  896.  
  897.     // Look for the lack of a run or walk animation and force the opposite
  898.     animWalk = HasAnim ( ANIMCHANNEL_LEGS, "walk" );
  899.     animRun  = HasAnim ( ANIMCHANNEL_LEGS, "run" );
  900.     if ( !animWalk && animRun ) {
  901.         move.fl.noWalk = true;
  902.     } else if ( !animRun && animWalk ) {
  903.         move.fl.noRun = true;
  904.     }
  905.  
  906.     // If a trigger anim or hide is specified then wait until activated before waking up
  907.     if ( spawnArgs.GetString ( "trigger_anim", "", &temp ) || spawnArgs.GetBool ( "hide" ) ) {    
  908.         Hide();
  909.         PostState ( "Wait_Activated" );
  910.         PostState ( "State_WakeUp", SEC2MS(spawnArgs.GetFloat ( "wait" ) ) );
  911.     // Wake up
  912.     } else {
  913.         PostState ( "State_WakeUp", SEC2MS(spawnArgs.GetFloat ( "wait" ) ) );
  914.     }
  915. }
  916.  
  917. /*
  918. ===================
  919. idAI::WakeUp
  920. ===================
  921. */
  922. void idAI::WakeUp ( void ) {
  923.     const char* temp;
  924.  
  925.     // Already awake?
  926.     if ( aifl.awake ) {
  927.         return;
  928.     }    
  929.  
  930.     // Find the closest helper
  931.     UpdateHelper ( );
  932.  
  933.     aifl.awake = true;
  934.  
  935.     // If the monster is flying then start them with the flying movement type    
  936.     if ( spawnArgs.GetBool ( "static" ) ) {
  937.         SetMoveType ( MOVETYPE_STATIC );
  938.     } else if ( spawnArgs.GetBool ( "flying" ) ) {
  939.         SetMoveType ( MOVETYPE_FLY );
  940.         move.moveDest = physicsObj.GetOrigin();
  941.         move.moveDest.z += move.fly_offset;
  942.     } else {
  943.         SetMoveType ( MOVETYPE_ANIM );
  944.     }    
  945.     
  946.     // Wake up any linked entities
  947.     WakeUpTargets ( );
  948.  
  949.     // Default enemy?
  950.     if ( !combat.fl.ignoreEnemies ) {
  951.         if ( spawnArgs.GetString ( "enemy", "", &temp ) && *temp ) {    
  952.             SetEnemy ( gameLocal.FindEntity ( temp ) );
  953.         } else if ( spawnArgs.GetBool ( "forceEnemy", "0" ) ) {
  954.             SetEnemy ( FindEnemy ( false, true ) );
  955.         }
  956.     }
  957.  
  958.     // Default leader?
  959.      if ( spawnArgs.GetString ( "leader", "", &temp ) && *temp ) {
  960.         SetLeader ( gameLocal.FindEntity ( temp ) );
  961.      }
  962.      
  963.      // If hidden and face enemy is specified we should orient ourselves toward our enemy immediately
  964.      if ( enemy.ent && IsHidden ( ) && spawnArgs.GetBool ( "faceEnemy" ) ) {
  965.         TurnToward ( enemy.ent->GetPhysics()->GetOrigin() );
  966.         move.current_yaw = move.ideal_yaw;
  967.         viewAxis = idAngles( 0, move.current_yaw, 0 ).ToMat3();
  968.     } 
  969.  
  970.     Show ( );
  971.  
  972.     aiManager.AddTeammate ( this );
  973.  
  974.     // External script functions
  975.     ExecScriptFunction( funcs.init, this );
  976.     
  977.     OnWakeUp ( );
  978. }
  979.  
  980. /*
  981. ===================
  982. idAI::List_f
  983. ===================
  984. */
  985. void idAI::List_f( const idCmdArgs &args ) {
  986.     int            e;
  987.     int            i;
  988.     idEntity*    check;
  989.     int            count;
  990.     idDict        countsFixed;
  991.     idDict        countsSpawned;
  992.  
  993.     count = 0;
  994.  
  995.     gameLocal.Printf( "%-4s  %-20s %s\n", " Num", "EntityDef", "Name" );
  996.     gameLocal.Printf( "------------------------------------------------\n" );
  997.     for( e = 0; e < MAX_GENTITIES; e++ ) {        
  998.         check = gameLocal.entities[ e ];
  999.         if ( !check ) {
  1000.             continue;
  1001.         }
  1002.         
  1003.         if ( check->IsType ( idAI::Type ) ) {
  1004.             idAI* checkAI = static_cast<idAI*>(check);
  1005.  
  1006.             // Skip spawned AI
  1007.             if ( checkAI->spawner ) {
  1008.                 continue;
  1009.             }
  1010.             countsFixed.SetInt ( check->GetEntityDefName(), countsFixed.GetInt ( check->GetEntityDefName(), "0" ) + 1 );
  1011.  
  1012.             gameLocal.Printf( "%4i: %-20s %-20s move: %d\n", e, check->GetEntityDefName(), check->name.c_str(), checkAI->move.fl.allowAnimMove );
  1013.             count++;
  1014.         } else if ( check->IsType ( rvSpawner::GetClassType() ) ) {            
  1015.             const idKeyValue*    kv;    
  1016.             rvSpawner*            checkSpawner = static_cast<rvSpawner*>(check);
  1017.             for ( kv = check->spawnArgs.MatchPrefix( "def_spawn", NULL ); kv; kv = check->spawnArgs.MatchPrefix( "def_spawn", kv ) ) {
  1018.                 countsSpawned.SetInt ( kv->GetValue(), countsSpawned.GetInt ( kv->GetValue(), "0" ) + 1 );
  1019.             }
  1020.             for ( i = 0; i < checkSpawner->GetNumSpawnPoints(); i ++ ) {
  1021.                 check = checkSpawner->GetSpawnPoint ( i );
  1022.                 if ( !check ) {
  1023.                     continue;
  1024.                 }
  1025.                 for ( kv = check->spawnArgs.MatchPrefix( "def_spawn", NULL ); kv; kv = check->spawnArgs.MatchPrefix( "def_spawn", kv ) ) {
  1026.                     countsSpawned.SetInt ( kv->GetValue(), countsSpawned.GetInt ( kv->GetValue(), "0" ) + 1 );
  1027.                 }
  1028.             }
  1029.         }
  1030.     }
  1031.  
  1032.     // Combine the two lists
  1033.     for ( e = 0; e < countsSpawned.GetNumKeyVals(); e ++ ) {
  1034.         const char* keyName = countsSpawned.GetKeyVal ( e )->GetKey();
  1035.         countsFixed.Set ( keyName, va("(%s) %3d", 
  1036.                           countsSpawned.GetKeyVal ( e )->GetValue().c_str(),
  1037.                           countsFixed.GetInt ( keyName, "0" ) + atoi(countsSpawned.GetKeyVal ( e )->GetValue()) ) );
  1038.     }    
  1039.  
  1040.     // Print out total counts
  1041.     if ( countsFixed.GetNumKeyVals() ) {
  1042.         gameLocal.Printf( "------------------------------------------------\n" );
  1043.         for ( e = 0; e < countsFixed.GetNumKeyVals(); e ++ ) {
  1044.             const idKeyValue* kv = countsFixed.GetKeyVal ( e );
  1045.             gameLocal.Printf( "%10s: %-20s\n", kv->GetValue().c_str(), kv->GetKey().c_str() );
  1046.         }
  1047.     }
  1048.     
  1049.     gameLocal.Printf( "------------------------------------------------\n" );
  1050.     gameLocal.Printf( "...%d monsters (%d unique types)\n", count, countsFixed.GetNumKeyVals() );
  1051. }
  1052.  
  1053. /*
  1054. ================
  1055. idAI::DormantBegin
  1056.  
  1057. called when entity becomes dormant
  1058. ================
  1059. */
  1060. void idAI::DormantBegin( void ) {
  1061.     // since dormant happens on a timer, we wont get to update particles to
  1062.     // hidden through the think loop, but we need to hide them though.
  1063.     if ( enemyNode.InList() ) {
  1064.         // remove ourselves from the enemy's enemylist
  1065.         enemyNode.Remove();
  1066.     }
  1067.     idActor::DormantBegin();
  1068. }
  1069.  
  1070. /*
  1071. ================
  1072. idAI::DormantEnd
  1073.  
  1074. called when entity wakes from being dormant
  1075. ================
  1076. */
  1077. void idAI::DormantEnd( void ) {
  1078.     if ( enemy.ent && !enemyNode.InList() ) {
  1079.         // let our enemy know we're back on the trail
  1080.         idActor *enemyEnt = dynamic_cast< idActor *>( enemy.ent.GetEntity() );
  1081.         if( enemyEnt ){
  1082.             enemyNode.AddToEnd( enemyEnt->enemyList );
  1083.         }
  1084.     }
  1085.     idActor::DormantEnd();
  1086. }
  1087.  
  1088. /*
  1089. =====================
  1090. idAI::ValidateCover
  1091.  
  1092. Validate the reserved cover feature with current conditions.  Also quickly tests if
  1093. cover does not face enemy at all, which triggers an instant update (ignoring maxCoverInvalidTime)
  1094. =====================
  1095. */
  1096. bool idAI::ValidateCover( ) {
  1097.     if ( !InCoverMode( ) ) {
  1098.         return false;
  1099.     }
  1100.     
  1101.     if ( aasSensor->TestValidWithCurrentState() && (!enemy.ent || enemy.range > combat.awareRange ) ) {
  1102.         combat.coverValidTime = gameLocal.time;
  1103.     } else if ( combat.coverValidTime && !IsCoverValid ( ) ) {
  1104.         combat.coverValidTime = 0;
  1105.         OnCoverInvalidated();
  1106.     }
  1107.  
  1108.     return IsCoverValid();
  1109. }
  1110.  
  1111. /*
  1112. =====================
  1113. idAI::DoDormantTests
  1114. =====================
  1115. */
  1116. bool idAI::DoDormantTests ( void ) {
  1117.     // If in a scripted move that we should never go dormant in
  1118.     if ( aifl.scripted && aifl.scriptedNeverDormant ) {
  1119.         return false;
  1120.     }
  1121.     // If following a player never go dormant
  1122.     if ( leader && leader->IsType ( idPlayer::GetClassType ( ) ) ) {
  1123.         return false;
  1124.     }
  1125.     // AI should no longer go dormant when outside of tether
  1126.     if ( IsTethered () && !IsWithinTether ( ) ) {
  1127.         return false;
  1128.     }
  1129.     return idActor::DoDormantTests ( );
  1130. }
  1131.  
  1132. /*
  1133. =====================
  1134. idAI::Think
  1135. =====================
  1136. */
  1137. void idAI::Think( void ) {
  1138.     idTimer timerThink;
  1139.  
  1140.     // if we are completely closed off from the player, don't do anything at all
  1141.     if ( CheckDormant() ) {
  1142.         return;
  1143.     }
  1144.  
  1145.     // Simple think this frame?
  1146.     aifl.simpleThink = aiManager.IsSimpleThink ( this );
  1147.  
  1148.     aiManager.thinkCount++;
  1149.  
  1150.     // AI Speeds
  1151.     if ( ai_speeds.GetBool ( ) ) {
  1152.         aiManager.timerThink.Start ( );
  1153.     }
  1154.  
  1155.     if ( thinkFlags & TH_THINK ) {    
  1156.         // clear out the enemy when he dies or is hidden
  1157.         idEntity* enemyEnt = enemy.ent;
  1158.         idActor*  enemyAct = dynamic_cast<idActor*>( enemyEnt );
  1159.         
  1160.         // Clear our enemy if necessary
  1161.         if ( enemyEnt ) {
  1162.             if (enemyAct && enemyAct->IsInVehicle()) {
  1163.                 SetEnemy(enemyAct->GetVehicleController().GetVehicle());    // always get angry at the enemy's vehicle first, not the enemy himself
  1164.             } else {
  1165.                 bool enemyDead = (enemyEnt->fl.takedamage && enemyEnt->health <= 0);
  1166.                 if ( enemyDead || enemyEnt->fl.notarget || enemyEnt->IsHidden() || (enemyAct && enemyAct->team == team)) {
  1167.                     ClearEnemy ( enemyDead );
  1168.                 }
  1169.             }
  1170.         }
  1171.  
  1172.         // Action time is stopped when the torso is not idle
  1173.         if ( !aifl.action ) {
  1174.             actionTime += gameLocal.msec;
  1175.         }
  1176.  
  1177.         ValidateCover();
  1178.  
  1179.         move.current_yaw += deltaViewAngles.yaw;
  1180.         move.ideal_yaw = idMath::AngleNormalize180( move.ideal_yaw + deltaViewAngles.yaw );
  1181.         deltaViewAngles.Zero();
  1182.  
  1183.         if( move.moveType != MOVETYPE_PLAYBACK ){
  1184.             viewAxis = idAngles( 0, move.current_yaw, 0 ).ToMat3();
  1185.         }
  1186.  
  1187.         if ( !move.fl.allowHiddenMove && IsHidden() ) {
  1188.             // hidden monsters
  1189.             UpdateStates ();
  1190.         } else if( !ai_freeze.GetBool() ) {
  1191.             Prethink(); 
  1192.  
  1193.             // clear the ik before we do anything else so the skeleton doesn't get updated twice
  1194.             walkIK.ClearJointMods();
  1195.  
  1196.             // update enemy position if not dead
  1197.             if ( !aifl.dead ) {
  1198.                 UpdateEnemy ( );
  1199.             }
  1200.  
  1201.             // update state machine
  1202.             UpdateStates();
  1203.  
  1204.             // run all movement commands
  1205.             Move();
  1206.  
  1207.             // if not dead, chatter and blink
  1208.             if( move.moveType != MOVETYPE_DEAD ){
  1209.                 UpdateChatter();
  1210.                 CheckBlink();
  1211.             }
  1212.  
  1213.             Postthink();
  1214.         } else {
  1215.             DrawTactical ( );
  1216.         }
  1217.  
  1218.         // clear pain flag so that we recieve any damage between now and the next time we run the script
  1219.         aifl.pain = false;
  1220.         aifl.damage = false;
  1221.         aifl.pushed = false;
  1222.         pusher = NULL;
  1223.     } else if ( thinkFlags & TH_PHYSICS ) {
  1224.         RunPhysics();
  1225.     }
  1226.  
  1227.     if ( move.fl.allowPushMovables ) {
  1228.         PushWithAF();
  1229.     }
  1230.  
  1231.     if ( fl.hidden && move.fl.allowHiddenMove ) {
  1232.         // UpdateAnimation won't call frame commands when hidden, so call them here when we allow hidden movement
  1233.         animator.ServiceAnims( gameLocal.previousTime, gameLocal.time );
  1234.     }
  1235.  
  1236.     aasSensor->Update();
  1237.  
  1238.     UpdateAnimation();
  1239.     Present();
  1240.     LinkCombat();
  1241.  
  1242.     // AI Speeds
  1243.     if ( ai_speeds.GetBool ( ) ) {
  1244.         aiManager.timerThink.Stop ( );
  1245.     }
  1246. }
  1247.  
  1248. /*
  1249. ============
  1250. idAI::UpdateFocus
  1251. ============
  1252. */
  1253. void idAI::UpdateFocus ( const idMat3& orientationAxis ) {    
  1254.     // Alwasy look at enemy    
  1255.     if ( !allowJointMod || !allowEyeFocus ) {
  1256.         SetFocus ( AIFOCUS_NONE, 0 );
  1257.     } else if ( IsBehindCover() && !aifl.action && !IsSpeaking() ) {
  1258.         SetFocus ( AIFOCUS_NONE, 0 );
  1259.     } else if ( !aifl.scripted && (IsEnemyRecentlyVisible(2.0f) || gameLocal.GetTime()-lastAttackTime<1000) ) {
  1260.         //not scripted and: have an enemy OR we shot at an enemy recently (for when we kill an enemy in the middle of a volley)
  1261.         SetFocus ( AIFOCUS_ENEMY, 1000 );
  1262.     } else if ( move.fl.moving && InCoverMode ( ) && DistanceTo ( aasSensor->ReservedOrigin() ) < move.walkRange * 2.0f ) {
  1263.         SetFocus ( AIFOCUS_COVER, 1000 );
  1264.     } else if ( InLookAtCoverMode() ) {
  1265.         SetFocus ( AIFOCUS_COVERLOOK, 1000 );
  1266.     } else if ( talkTarget && gameLocal.time < passive.talkTime ) {
  1267.         SetFocus ( AIFOCUS_TALK, 100 );
  1268.     } else if ( lookTarget ) {
  1269.         SetFocus ( AIFOCUS_TARGET, 100 );
  1270.     } else if ( !IsBehindCover() && tether && DistanceTo ( move.moveDest ) < move.walkRange * 2.0f ) {
  1271.         SetFocus ( AIFOCUS_TETHER, 100 );
  1272.     } else if ( IsTethered() && (move.fl.moving || IsBehindCover()) ) {
  1273.         //tethered and at cover or moving - don't look at leader or player
  1274.         SetFocus ( AIFOCUS_NONE, 0 );
  1275.     } else if ( helperCurrent && helperCurrent->IsCombat ( ) ) {
  1276.         SetFocus ( AIFOCUS_HELPER, 100 );        
  1277.     } else if ( !aifl.scripted && !move.fl.moving ) {
  1278.         idEntity*    lookat   = NULL;
  1279.         aiFocus_t    newfocus = AIFOCUS_NONE;
  1280.         
  1281.         if ( leader ) {
  1282.             idVec3 dir2Leader = leader->GetPhysics()->GetOrigin()-GetPhysics()->GetOrigin();
  1283.             if ( (viewAxis[0] * dir2Leader) > 0.0f ) {
  1284.                 //leader is in front of me
  1285.                 newfocus  = AIFOCUS_LEADER;
  1286.                 lookat      = leader;
  1287.             } else if ( focusType == AIFOCUS_LEADER ) {
  1288.                 //stop looking at him!
  1289.                 SetFocus ( AIFOCUS_NONE, 0 );
  1290.             }
  1291.         } else if ( aifl.lookAtPlayer && !move.fl.moving ) {
  1292.             newfocus  = AIFOCUS_PLAYER;
  1293.             lookat      = gameLocal.GetLocalPlayer();
  1294.         }
  1295.         
  1296.         if ( newfocus != AIFOCUS_NONE && lookat ) {
  1297.             if ( DistanceTo ( lookat ) < focusRange * (move.fl.moving?2.0f:1.0f) && CheckFOV ( lookat->GetPhysics()->GetOrigin() ) ) {
  1298.                 SetFocus ( newfocus, 1000 );
  1299.             }        
  1300.         }
  1301.     }    
  1302.     
  1303.     // Make sure cover look wasnt invalidated    
  1304.     if ( focusType == AIFOCUS_COVERLOOK && !aasSensor->Look() ) {
  1305.         focusType = AIFOCUS_NONE;
  1306.     } else if ( focusType == AIFOCUS_COVER && !aasSensor->Reserved ( ) ) {
  1307.         focusType = AIFOCUS_NONE;
  1308.     } else if ( focusType == AIFOCUS_HELPER && !helperCurrent ) {
  1309.         focusType = AIFOCUS_NONE;
  1310.     } else if ( focusType == AIFOCUS_TETHER && !tether ) {
  1311.         focusType = AIFOCUS_NONE;
  1312.     }
  1313.     
  1314.     // Update focus type to none when the focus time runs out
  1315.     if ( focusType != AIFOCUS_NONE ) {
  1316.         if ( gameLocal.time > focusTime ) {
  1317.             focusType = AIFOCUS_NONE;
  1318.         }    
  1319.     }
  1320.  
  1321.     // Calculate the focus position
  1322.     if ( focusType == AIFOCUS_NONE ) {
  1323.         currentFocusPos = GetEyePosition() + orientationAxis[ 0 ] * 64.0f;
  1324.     } else if ( focusType == AIFOCUS_COVER ) {
  1325.         currentFocusPos = GetEyePosition() + aasSensor->Reserved()->Normal() * 64.0f;
  1326.     } else if ( focusType == AIFOCUS_COVERLOOK ) {
  1327.         currentFocusPos = GetEyePosition() + aasSensor->Look()->Normal() * 64.0f;
  1328.     } else if ( focusType == AIFOCUS_ENEMY ) {
  1329.         currentFocusPos = enemy.lastVisibleEyePosition;
  1330.     } else if ( focusType == AIFOCUS_HELPER ) {
  1331.         currentFocusPos = helperCurrent->GetPhysics()->GetOrigin() + helperCurrent->GetDirection ( this );
  1332.     } else if ( focusType == AIFOCUS_TETHER ) {
  1333.         currentFocusPos = GetEyePosition() + tether->GetPhysics()->GetAxis()[0] * 64.0f;
  1334.     } else { 
  1335.         idEntity* focusEnt = NULL;
  1336.         switch ( focusType ) {
  1337.             case AIFOCUS_LEADER:        focusEnt = leader;                        break;
  1338.             case AIFOCUS_PLAYER:        focusEnt = gameLocal.GetLocalPlayer();    break;
  1339.             case AIFOCUS_TALK:            focusEnt = talkTarget;                    break;
  1340.             case AIFOCUS_TARGET:        focusEnt = lookTarget;                    break;
  1341.         }
  1342.         if ( focusEnt ) {
  1343.             if ( focusEnt->IsType ( idActor::GetClassType ( ) ) ) {
  1344.                 currentFocusPos = static_cast<idActor*>(focusEnt)->GetEyePosition() - eyeVerticalOffset * focusEnt->GetPhysics()->GetGravityNormal();
  1345.             } else {
  1346.                 currentFocusPos = focusEnt->GetPhysics()->GetOrigin();
  1347.             }
  1348.         } else { 
  1349.             currentFocusPos = GetEyePosition() + orientationAxis[ 0 ] * 64.0f;
  1350.         }
  1351.     }
  1352.  
  1353.     //draw it so we can see what they think they're looking at!
  1354.     if ( DebugFilter(ai_debugMove) ) { // Cyan & Blue = currentFocusPos
  1355.         if ( focusType != AIFOCUS_NONE ) {
  1356.             gameRenderWorld->DebugArrow( colorCyan, GetEyePosition(), currentFocusPos, 0 );
  1357.         } else {
  1358.             gameRenderWorld->DebugArrow( colorBlue, GetEyePosition(), currentFocusPos, 0 );
  1359.         }
  1360.     }
  1361. }
  1362.  
  1363. /*
  1364. =====================
  1365. idAI::UpdateStates
  1366. =====================
  1367. */
  1368. void idAI::UpdateStates ( void ) {
  1369.     MEM_SCOPED_TAG(tag,MA_DEFAULT);
  1370.  
  1371.     // Continue updating tactical state if for some reason we dont have one 
  1372.     if ( !aifl.dead && !aifl.scripted && !aifl.action && stateThread.IsIdle ( ) && aifl.scriptedEndWithIdle ) {
  1373.         UpdateTactical ( 0 );
  1374.     } else {
  1375.         UpdateState();
  1376.     }
  1377.  
  1378.     // clear the hit enemy flag so we catch the next time we hit someone
  1379.     aifl.hitEnemy = false;
  1380.  
  1381.     if ( move.fl.allowHiddenMove || !IsHidden() ) {
  1382.         // update the animstate if we're not hidden
  1383.         UpdateAnimState();
  1384.     }
  1385. }
  1386.  
  1387. /*
  1388. =====================
  1389. idAI::OnStateChange
  1390. =====================
  1391. */
  1392. void idAI::OnStateChange ( int channel ) {
  1393. }
  1394.  
  1395. /*
  1396. =====================
  1397. idAI::OverrideFlag
  1398. =====================
  1399. */
  1400. void idAI::OverrideFlag ( aiFlagOverride_t flag, bool value ) {
  1401.     bool oldValue;
  1402.     
  1403.     switch ( flag ) {
  1404.         case AIFLAGOVERRIDE_DISABLEPAIN:    oldValue = disablePain;                break;
  1405.         case AIFLAGOVERRIDE_DAMAGE:            oldValue = fl.takedamage;            break;
  1406.         case AIFLAGOVERRIDE_NOTURN:            oldValue = move.fl.noTurn;        break;
  1407.         case AIFLAGOVERRIDE_NOGRAVITY:        oldValue = move.fl.noGravity;    break;
  1408.         default:
  1409.             return;
  1410.     }
  1411.  
  1412.     if ( oldValue == value ) {
  1413.         return;
  1414.     }
  1415.     
  1416.     int flagOverride        = 1 << (flag * 2);
  1417.     int flagOverrideValue    = 1 << ((flag * 2) + 1);
  1418.     
  1419.     if ( (flagOverrides & flagOverride) && !!(flagOverrides & flagOverrideValue) == value ) {
  1420.         flagOverrides &= ~flagOverride;
  1421.     } else {
  1422.         if ( oldValue ) {
  1423.             flagOverrides |= flagOverrideValue;
  1424.         } else {
  1425.             flagOverrides &= ~flagOverrideValue;
  1426.         }
  1427.         flagOverrides |= flagOverride;
  1428.     }
  1429.     
  1430.     switch ( flag ) {
  1431.         case AIFLAGOVERRIDE_DISABLEPAIN:    disablePain = value;            break;
  1432.         case AIFLAGOVERRIDE_DAMAGE:            fl.takedamage = value;            break;
  1433.         case AIFLAGOVERRIDE_NOTURN:            move.fl.noTurn = value;        break;
  1434.         case AIFLAGOVERRIDE_NOGRAVITY:        move.fl.noGravity = value;    break;
  1435.     }
  1436. }
  1437.  
  1438. /*
  1439. =====================
  1440. idAI::RestoreFlag
  1441. =====================
  1442. */
  1443. void idAI::RestoreFlag ( aiFlagOverride_t flag ) {
  1444.     int flagOverride        = 1 << (flag * 2);
  1445.     int flagOverrideValue    = 1 << ((flag * 2) + 1);
  1446.     bool value;
  1447.     
  1448.     if ( !(flagOverrides&flagOverride) ) {
  1449.         return;
  1450.     }
  1451.     
  1452.     value = !!(flagOverrides & flagOverrideValue);
  1453.  
  1454.     switch ( flag ) {
  1455.         case AIFLAGOVERRIDE_DISABLEPAIN:    disablePain = value;            break;
  1456.         case AIFLAGOVERRIDE_DAMAGE:            fl.takedamage = value;            break;
  1457.         case AIFLAGOVERRIDE_NOTURN:            move.fl.noTurn = value;        break;
  1458.         case AIFLAGOVERRIDE_NOGRAVITY:        move.fl.noGravity = value;    break;
  1459.     }
  1460. }    
  1461.  
  1462. /***********************************************************************
  1463.  
  1464.     Damage
  1465.  
  1466. ***********************************************************************/
  1467.  
  1468. /*
  1469. =====================
  1470. idAI::ReactionTo
  1471. =====================
  1472. */
  1473. int idAI::ReactionTo( const idEntity *ent ) {
  1474.  
  1475.     if ( ent->fl.hidden ) {
  1476.         // ignore hidden entities
  1477.         return ATTACK_IGNORE;
  1478.     }
  1479.  
  1480.     if ( !ent->IsType( idActor::GetClassType() ) ) {
  1481.         return ATTACK_IGNORE;
  1482.     }
  1483.  
  1484.     if( combat.fl.ignoreEnemies ){
  1485.         return ATTACK_IGNORE;
  1486.     }
  1487.  
  1488.     const idActor *actor = static_cast<const idActor *>( ent );
  1489.     if ( actor->IsType( idPlayer::GetClassType() ) && static_cast<const idPlayer *>(actor)->noclip ) {
  1490.         // ignore players in noclip mode
  1491.         return ATTACK_IGNORE;
  1492.     }
  1493.  
  1494.     // actors on different teams will always fight each other
  1495.     if ( actor->team != team ) {
  1496.         if ( actor->fl.notarget ) {
  1497.             // don't attack on sight when attacker is notargeted
  1498.             return ATTACK_ON_DAMAGE; /* | ATTACK_ON_ACTIVATE ; */
  1499.         }
  1500.         return ATTACK_ON_SIGHT | ATTACK_ON_DAMAGE; /* | ATTACK_ON_ACTIVATE; */
  1501.     }
  1502.  
  1503.     //FIXME: temporarily disabled monsters of same rank fighting because it's happening too much.
  1504.     // monsters will fight when attacked by lower or equal ranked monsters.  rank 0 never fights back.
  1505.     //if ( rank && ( actor->rank <= rank ) ) {
  1506.     if ( rank && ( actor->rank < rank ) ) {
  1507.         return ATTACK_ON_DAMAGE;
  1508.     }
  1509.  
  1510.     // don't fight back
  1511.     return ATTACK_IGNORE;
  1512. }
  1513.  
  1514. /*
  1515. =====================
  1516. idAI::AdjustHealthByDamage
  1517. =====================
  1518. */
  1519. void idAI::AdjustHealthByDamage    ( int damage ) {
  1520.     if ( aifl.undying ) {
  1521.         return;
  1522.     }    
  1523.     idActor::AdjustHealthByDamage ( damage );
  1524.  
  1525.     if ( g_perfTest_aiUndying.GetBool() && health <= 0 ) {
  1526.         //so we still take pain!
  1527.         health = 1;
  1528.     }
  1529. }
  1530.  
  1531. /*
  1532. =====================
  1533. idAI::Pain
  1534. =====================
  1535. */
  1536. bool idAI::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  1537.     aifl.pain   = idActor::Pain( inflictor, attacker, damage, dir, location );
  1538.     aifl.damage = true;
  1539.  
  1540.     // force a blink
  1541.     blink_time = 0;
  1542.  
  1543.     // No special handling if friendly fire
  1544.     // jshepard: friendly fire will cause pain. Players will only be able to pain buddy marines
  1545.     // via splash damage.
  1546.  
  1547. /*
  1548.     if ( attacker && attacker->IsType ( idActor::GetClassType ( ) ) ) {
  1549.         if ( static_cast<idActor*>( attacker )->team == team ) {
  1550.             return aifl.pain;
  1551.         }
  1552.     }
  1553. */
  1554.  
  1555.     // ignore damage from self
  1556.     if ( attacker != this ) {
  1557.         // React to taking pain
  1558.         ReactToPain ( attacker, damage );
  1559.  
  1560.         pain.takenThisFrame += damage;
  1561.         pain.lastTakenTime = gameLocal.time;
  1562.         combat.tacticalPainTaken += damage;
  1563.  
  1564.         // If taken too much pain where we then skip the current destination
  1565.         if ( combat.tacticalPainThreshold && combat.tacticalPainTaken > combat.tacticalPainThreshold ) {
  1566.             if (team==AITEAM_STROGG && 
  1567.                 (combat.tacticalMaskAvailable&AITACTICAL_COVER_BITS) &&
  1568.                 IsType(rvAITactical::GetClassType()) && 
  1569.                 ai_allowTacticalRush.GetBool() && 
  1570.                 spawnArgs.GetBool("rushOnPain", "1")) {
  1571.  
  1572.                 // clear any tether
  1573.                 tether = NULL;
  1574.  
  1575.                 // change ranged distances
  1576.                 combat.attackRange[0] = 0.0f;
  1577.                 combat.attackRange[1] = 100.0f;    // make them get really close
  1578.  
  1579.                 // remove cover behavior
  1580.                 combat.tacticalMaskAvailable &= ~AITACTICAL_COVER_BITS;
  1581.  
  1582.                 // add ranged behavior
  1583.                 combat.tacticalMaskAvailable |=  AITACTICAL_RANGED_BITS;
  1584.             }
  1585.             ForceTacticalUpdate ( );
  1586.             return true;
  1587.         }
  1588.  
  1589.         // If looping pain and a new paintype comes in we can stop the loop
  1590.         if ( pain.loopEndTime && pain.loopType.Icmp ( painType ) ) {
  1591.             pain.loopEndTime = 0;
  1592.             pain.loopType = painType;
  1593.         }
  1594.  
  1595.         ExecScriptFunction( funcs.damage );
  1596.     }
  1597.  
  1598.     // If we were hit by our enemy and our enemy isnt visible then 
  1599.     // force the enemy visiblity to be updated as if we saw him momentarily
  1600.     if ( enemy.ent == attacker && !enemy.fl.visible ) {
  1601.         UpdateEnemyPosition ( true );
  1602.     }    
  1603.  
  1604.     return aifl.pain;
  1605. }
  1606.  
  1607. /*
  1608. =====================
  1609. idAI::Killed
  1610. =====================
  1611. */
  1612. void idAI::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  1613.     idAngles            ang;
  1614.     const char*            modelDeath;
  1615.     const idKeyValue*    kv;
  1616.     
  1617.     if ( g_debugDamage.GetBool() ) {
  1618.         gameLocal.Printf( "Damage: joint: '%s', zone '%s'\n", animator.GetJointName( ( jointHandle_t )location ), 
  1619.             GetDamageGroup( location ) );
  1620.     }
  1621.  
  1622.     if ( aifl.dead ) {
  1623.         aifl.pain = true;
  1624.         aifl.damage = true;
  1625.         return;
  1626.     }
  1627.  
  1628.     aifl.dead = true;
  1629.  
  1630.     // turn off my flashlight, if I had one
  1631.     ProcessEvent( &AI_Flashlight, false );
  1632.  
  1633.     // Detach from any spawners
  1634.     if( GetSpawner() ) {
  1635.         GetSpawner()->Detach( this );
  1636.         SetSpawner( NULL );
  1637.     }
  1638.  
  1639.     // Hide surfaces on death
  1640.     for ( kv = spawnArgs.MatchPrefix ( "deathhidesurface", NULL );
  1641.           kv;
  1642.           kv = spawnArgs.MatchPrefix ( "deathhidesurface", kv ) ) {
  1643.         HideSurface ( kv->GetValue() );
  1644.     }
  1645.  
  1646.     // stop all voice sounds 
  1647.     StopSpeaking( true );
  1648.  
  1649.     SetMoveType ( MOVETYPE_DEAD );
  1650.  
  1651.     move.fl.noGravity = false;
  1652.     move.fl.allowPushMovables = false;
  1653.     aifl.scripted = false;
  1654.  
  1655.     physicsObj.UseFlyMove( false );
  1656.     physicsObj.ForceDeltaMove( false );
  1657.  
  1658.     // end our looping ambient sound
  1659.     StopSound( SND_CHANNEL_AMBIENT, false );
  1660.  
  1661.     if ( attacker && attacker->IsType( idActor::GetClassType() ) ) {
  1662.         gameLocal.AlertAI( ( idActor * )attacker );
  1663.  
  1664.         aiManager.AnnounceKill ( this, attacker, inflictor );
  1665.         aiManager.AnnounceDeath ( this, attacker );
  1666.        }
  1667.  
  1668.     if ( attacker && attacker->IsType( idActor::GetClassType() ) ) {
  1669.         gameLocal.AlertAI( ( idActor * )attacker );
  1670.     }
  1671.  
  1672.     // activate targets
  1673.     ActivateTargets( this );
  1674.  
  1675.     RemoveAttachments();
  1676.     RemoveProjectile();
  1677.     StopMove( MOVE_STATUS_DONE );
  1678.  
  1679.     OnDeath();
  1680.     CheckDeathObjectives();
  1681.  
  1682.     ClearEnemy();
  1683.  
  1684.     // make monster nonsolid
  1685.     physicsObj.SetContents( 0 );
  1686.     physicsObj.GetClipModel()->Unlink();
  1687.  
  1688.     Unbind();
  1689.     if ( g_perfTest_aiNoRagdoll.GetBool() ) {
  1690.         if ( spawnArgs.MatchPrefix( "lipsync_death" ) ) {
  1691.             Speak( "lipsync_death", true );
  1692.         } else {
  1693.             StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  1694.         }
  1695.         physicsObj.SetLinearVelocity( vec3_zero );
  1696.         physicsObj.PutToRest();
  1697.         physicsObj.DisableImpact();
  1698.     } else {
  1699.         if ( StartRagdoll() ) {
  1700.             if ( spawnArgs.MatchPrefix( "lipsync_death" ) ) {
  1701.                 Speak( "lipsync_death", true );
  1702.             } else {
  1703.                 StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  1704.             }
  1705.         }
  1706.  
  1707.         if ( spawnArgs.GetString( "model_death", "", &modelDeath ) ) {
  1708.             // lost soul is only case that does not use a ragdoll and has a model_death so get the death sound in here
  1709.             if ( spawnArgs.MatchPrefix( "lipsync_death" ) ) {
  1710.                 Speak( "lipsync_death", true );
  1711.             } else {
  1712.                 StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  1713.             }
  1714.             renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1715.             SetModel( modelDeath );
  1716.             physicsObj.SetLinearVelocity( vec3_zero );
  1717.             physicsObj.PutToRest();
  1718.             physicsObj.DisableImpact();
  1719.         }
  1720.     }
  1721.  
  1722.     SetState ( "State_Killed" );
  1723.  
  1724.     kv = spawnArgs.MatchPrefix( "def_drops", NULL );
  1725.     while( kv ) {
  1726.         idDict args;
  1727.         idEntity *tEnt;
  1728.         if( kv->GetValue() != "" ){
  1729.             args.Set( "classname", kv->GetValue() );
  1730.             args.Set( "origin", physicsObj.GetAbsBounds().GetCenter().ToString() );
  1731.             // Let items know that they are of the dropped variety
  1732.             args.Set( "dropped", "1" );
  1733.             if (gameLocal.SpawnEntityDef( args, &tEnt )) {
  1734.                 if ( tEnt && tEnt->GetPhysics()) { //tEnt *should* be valid, but hey...
  1735.                     // magic/arbitrary number to give it some spin.  Some constants used to ensure guns rarely fall standing up
  1736.                     tEnt->GetPhysics()->SetAngularVelocity( idVec3( (gameLocal.random.RandomFloat() * 10.0f) + 20.0f,
  1737.                                                                     (gameLocal.random.RandomFloat() * 10.0f) + 20.0f,
  1738.                                                                     (gameLocal.random.RandomFloat() * 10.0f) + 20.0f));
  1739.                 }
  1740.             }
  1741.         }
  1742.         kv = spawnArgs.MatchPrefix( "def_drops", kv );
  1743.     }
  1744. }
  1745.  
  1746. /***********************************************************************
  1747.  
  1748.     Targeting/Combat
  1749.  
  1750. ***********************************************************************/
  1751.  
  1752. /*
  1753. =====================
  1754. idAI::Activate
  1755.  
  1756. Notifies the script that a monster has been activated by a trigger or flashlight
  1757. =====================
  1758. */
  1759. void idAI::Activate( idEntity *activator ) {
  1760.     idPlayer *player;
  1761.  
  1762.     // Set our tether?
  1763.     if ( activator && activator->IsType ( rvAITether::GetClassType ( ) ) ) {
  1764.         SetTether ( static_cast<rvAITether*>(activator) );
  1765.     }
  1766.  
  1767.     if ( aifl.dead ) {
  1768.         // ignore it when they're dead
  1769.         return;
  1770.     }
  1771.  
  1772.     // make sure he's not dormant
  1773.     dormantStart = 0;
  1774.  
  1775.     aifl.activated = true;
  1776.     if ( !activator || !activator->IsType( idPlayer::GetClassType() ) ) {
  1777.         player = gameLocal.GetLocalPlayer();
  1778.     } else {
  1779.         player = static_cast<idPlayer *>( activator );
  1780.     }
  1781.  
  1782.     if ( ReactionTo( player ) & ATTACK_ON_ACTIVATE ) {
  1783.         SetEnemy( player );
  1784.     }
  1785.     
  1786.     // If being activated by a spawner we need to attach to it
  1787.     if ( activator && activator->IsType ( rvSpawner::GetClassType() ) ) {
  1788.         rvSpawner* spawn = static_cast<rvSpawner*>( activator );
  1789.         spawn->Attach ( this );
  1790.         SetSpawner ( spawn );
  1791.     }
  1792. }
  1793.  
  1794. /*
  1795. =====================
  1796. idAI::TalkTo
  1797. =====================
  1798. */
  1799. void idAI::TalkTo( idActor *actor ) {
  1800.  
  1801.     // jshepard: the dead do not speak.
  1802.     if ( aifl.dead )
  1803.         return;
  1804.  
  1805.     ExecScriptFunction( funcs.onclick );
  1806.  
  1807.     // Cant talk when already talking
  1808.     if ( aifl.action || IsSpeaking ( ) || !aiManager.CheckTeamTimer ( team, AITEAMTIMER_ACTION_TALK ) ) {
  1809.         return;
  1810.     }
  1811.  
  1812.  
  1813.     switch ( GetTalkState ( ) ) {
  1814.         case TALK_OK:
  1815.             if ( talkMessage >= TALKMSG_LOOP ) {
  1816.                 //MCG: requested change: stop responding after one loop
  1817.                 return;
  1818.             }
  1819.             // at least one second between talking to people 
  1820.             aiManager.SetTeamTimer ( team, AITEAMTIMER_ACTION_TALK, 2000 );
  1821.         
  1822.             talkTarget = actor;
  1823.  
  1824.             // Move to the next talk message
  1825.             talkMessage = (talkMessage_t)((int)talkMessage + 1);
  1826.             
  1827.             // Loop until we find a valid talk message
  1828.             while ( 1 ) {
  1829.                 idStr postfix;
  1830.                 if ( talkMessage >= TALKMSG_LOOP ) {
  1831.                     postfix = aiTalkMessageString[TALKMSG_LOOP];
  1832.                     postfix += va("%d", (int)(talkMessage - TALKMSG_LOOP+1) );
  1833.                 } else {
  1834.                     postfix = aiTalkMessageString[talkMessage];
  1835.                 }
  1836.                 // Try the lipsync for the specific passive prefix first
  1837.                 if ( Speak ( va("lipsync_%stalk_%s", passive.prefix.c_str(), postfix.c_str() ) ) ) {
  1838.                     break;
  1839.                 }
  1840.                 // Try the generic lipsync for the current talk message
  1841.                 if ( Speak ( va("lipsync_talk_%s", postfix.c_str() ) ) ) {
  1842.                     break;
  1843.                 }
  1844.                 // If the first loop failed then there is no other options
  1845.                 if ( talkMessage == TALKMSG_LOOP ) {
  1846.                     return;
  1847.                 } else if ( talkMessage >= TALKMSG_LOOP ) {
  1848.                     talkMessage = TALKMSG_LOOP;
  1849.                 } else {
  1850.                     talkMessage = (talkMessage_t)((int)talkMessage + 1);
  1851.                 }
  1852.             }
  1853.             
  1854.             // Start talking anim if we have one
  1855.             if ( IsSpeaking ( ) ) {
  1856.                 passive.talkTime = speakTime;
  1857.             }
  1858.             
  1859.             break;
  1860.         
  1861.         case TALK_FOLLOW:
  1862.             if ( actor == leader ) {
  1863.                 leader = NULL;
  1864.                 Speak ( "lipsync_stopfollow", true );
  1865.             } else {
  1866.                 leader = actor;
  1867.                 Speak ( "lipsync_follow", true );
  1868.             }
  1869.             break;
  1870.             
  1871.         case TALK_BUSY:
  1872.             if ( talkBusyCount > 3 )
  1873.             {//only say a max of 4 lines
  1874.                 return;
  1875.             }
  1876.             talkBusyCount++;
  1877.             //try to say this variant - if we can't, stop forever
  1878.             if ( !Speak ( va("lipsync_busy_%d",talkBusyCount) ) )
  1879.             {//nevermore
  1880.                 talkBusyCount = 999;
  1881.             }
  1882.             break;
  1883.  
  1884.         case TALK_WAIT:
  1885.             //do nothing
  1886.             break;
  1887.     }
  1888. }
  1889.  
  1890. /*
  1891. =====================
  1892. idAI::GetTalkState
  1893. =====================
  1894. */
  1895. talkState_t idAI::GetTalkState( void ) const {
  1896.     if ( ( talkState != TALK_NEVER ) && aifl.dead ) {
  1897.         return TALK_DEAD;
  1898.     }
  1899.     if ( IsHidden() ) {
  1900.         return TALK_NEVER;
  1901.     }
  1902.     if ( (talkState == TALK_OK || talkState == TALK_FOLLOW) && aifl.scripted ) {
  1903.         return TALK_BUSY;
  1904.     }
  1905.     return talkState;
  1906. }
  1907.  
  1908. /*
  1909. =====================
  1910. idAI::TouchedByFlashlight
  1911. =====================
  1912. */
  1913. void idAI::TouchedByFlashlight( idActor *flashlight_owner ) {
  1914.     if ( RespondToFlashlight() ) {
  1915.         Activate( flashlight_owner );
  1916.     }
  1917. }
  1918.  
  1919. /*
  1920. =====================
  1921. idAI::ClearEnemy
  1922. =====================
  1923. */
  1924. void idAI::ClearEnemy( bool dead ) {
  1925.     // Dont bother if we dont have an enemy
  1926.     if ( !enemy.ent ) {
  1927.         return;
  1928.     }
  1929.  
  1930.     if ( move.moveCommand == MOVE_TO_ENEMY ) {
  1931.         StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  1932.     } else if ( !aifl.scripted
  1933.         && move.moveCommand == MOVE_TO_COVER
  1934.         && !move.fl.done
  1935.         && aasSensor->Reserved()
  1936.         && !IsBehindCover() ) {
  1937.         //clear cover and stop move
  1938.         aasSensor->Reserve( NULL );
  1939.         combat.coverValidTime = 0;
  1940.         OnCoverInvalidated();
  1941.         ForceTacticalUpdate();
  1942.         StopMove( MOVE_STATUS_DONE );
  1943.     }
  1944.  
  1945.     enemyNode.Remove();
  1946.     enemy.ent            = NULL;
  1947.     enemy.fl.dead        = dead;    
  1948.     combat.fl.seenEnemyDirectly = false;
  1949.     
  1950.     UpdateHelper ( );
  1951. }
  1952.  
  1953. /*
  1954. =====================
  1955. idAI::UpdateEnemyPosition
  1956. =====================
  1957. */
  1958. void idAI::UpdateEnemyPosition ( bool forceUpdate ) {
  1959.     if( !enemy.ent || (!enemy.fl.visible && !forceUpdate) ) {
  1960.         return;
  1961.     }
  1962.  
  1963.     idActor*  enemyActor = dynamic_cast<idActor*>(enemy.ent.GetEntity());
  1964.     idEntity* enemyEnt   = static_cast<idEntity*>(enemy.ent.GetEntity());
  1965.  
  1966.     enemy.lastVisibleFromEyePosition = GetEyePosition ( );
  1967.  
  1968.     // Update the enemy origin if it isnt locked
  1969.     if ( !enemy.fl.lockOrigin ) {
  1970.         enemy.lastKnownPosition    = enemyEnt->GetPhysics()->GetOrigin();
  1971.  
  1972.         if ( enemyActor ) {                
  1973.             enemy.lastVisibleEyePosition    = enemyActor->GetEyePosition();
  1974.             enemy.lastVisibleChestPosition    = enemyActor->GetChestPosition();
  1975.         } else {
  1976.             enemy.lastVisibleEyePosition    = enemy.ent->GetPhysics()->GetOrigin ( );
  1977.             enemy.lastVisibleChestPosition    = enemy.ent->GetPhysics()->GetOrigin ( );
  1978.         }        
  1979.     }    
  1980. }
  1981.  
  1982. /*
  1983. =====================
  1984. idAI::UpdateEnemy
  1985. =====================
  1986. */
  1987. void idAI::UpdateEnemy ( void ) {
  1988.     predictedPath_t predictedPath;
  1989.     
  1990.     // If we lost our enemy then clear it out to be sure
  1991.     if( !enemy.ent ) {
  1992.         return;
  1993.     }
  1994.  
  1995.     // Rest to not being in fov
  1996.     enemy.fl.inFov = false;
  1997.  
  1998.     // Bail out if we arent queued to do a complex think
  1999.     if ( aifl.simpleThink ) {
  2000.         // Keep the fov flag up to date
  2001.         if ( IsEnemyVisible ( ) ) { 
  2002.             enemy.fl.inFov = CheckFOV( enemy.lastKnownPosition );
  2003.         }
  2004.     } else {         
  2005.         bool oldVisible;
  2006.     
  2007.         // Cache current enemy visiblity so we can see if it changes
  2008.         oldVisible = IsEnemyVisible ( );
  2009.     
  2010.         // See if enemy still visible
  2011.         UpdateEnemyVisibility  ( );
  2012.  
  2013.         // If our enemy isnt visible but is within aware range then we know where he is
  2014.         if ( !IsEnemyVisible ( ) && DistanceTo ( enemy.ent ) < combat.awareRange ) {
  2015.             UpdateEnemyPosition( true );
  2016.         } else {
  2017. /*        
  2018.             // check if we heard any sounds in the last frame
  2019.             if ( enemyEnt == gameLocal.GetAlertActor() ) {
  2020.                 float dist = ( enemyEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin() ).LengthSqr();
  2021.                 if ( dist < Square( combat.earRange ) ) {
  2022.                     SetEnemyPosition();
  2023.                 }
  2024.             }
  2025. */
  2026.         }
  2027.  
  2028.         // Update fov
  2029.         enemy.fl.inFov = CheckFOV( enemy.lastKnownPosition );
  2030.  
  2031.         // Set enemy.visibleTime to the time when the visibility flag changed
  2032.         if ( oldVisible != IsEnemyVisible ( ) ) {
  2033.             // Just caught sight of the enemy after loosing him, delay the attack actions a bit
  2034.             if ( !oldVisible && combat.attackSightDelay ) {
  2035.                 float delay;
  2036.                 if ( enemy.lastVisibleChangeTime ) {
  2037.                     // The longer the enemy has not been visible, the more we should delay
  2038.                     delay = idMath::ClampFloat ( 0.0f, 1.0f, (gameLocal.time - enemy.lastVisibleChangeTime) / AI_SIGHTDELAYSCALE );
  2039.                 } else {
  2040.                     // The enemy has never been visible so delay the full amount
  2041.                     delay = 1.0f;
  2042.                 }
  2043.                 // Add to the ranged attack timer providing its not already running a timer
  2044.                 if ( actionTimerRangedAttack.IsDone ( actionTime) ) {
  2045.                     actionTimerRangedAttack.Clear ( actionTime );
  2046.                     actionTimerRangedAttack.Add ( combat.attackSightDelay * delay, 0.5f );
  2047.                 }
  2048.                 // Add to the special attack timer providing its not already running a timer
  2049.                 if ( actionTimerSpecialAttack.IsDone ( actionTime ) ) {
  2050.                     actionTimerSpecialAttack.Clear ( actionTime );        
  2051.                     actionTimerSpecialAttack.Add ( combat.attackSightDelay * delay, 0.5f );
  2052.                 }
  2053.             }
  2054.  
  2055.             enemy.lastVisibleChangeTime = gameLocal.time;
  2056.         }
  2057.         
  2058.         // Handler for visibility change
  2059.         if ( oldVisible != IsEnemyVisible ( ) ) {
  2060.             OnEnemyVisiblityChange ( oldVisible );
  2061.         }
  2062.     }
  2063.  
  2064.     // Adjust smoothed linear and pushed velocities for enemies
  2065.     if ( enemy.fl.visible ) {
  2066.         enemy.smoothedLinearVelocity += ((enemy.ent->GetPhysics()->GetLinearVelocity ( ) - enemy.ent->GetPhysics()->GetPushedLinearVelocity ( ) - enemy.smoothedLinearVelocity) * enemy.smoothVelocityRate );
  2067.          enemy.smoothedPushedVelocity += ((enemy.ent->GetPhysics()->GetPushedLinearVelocity ( ) - enemy.smoothedPushedVelocity) * enemy.smoothVelocityRate );
  2068.      } else {
  2069.         enemy.smoothedLinearVelocity -= (enemy.smoothedLinearVelocity * enemy.smoothVelocityRate );
  2070.          enemy.smoothedPushedVelocity -= (enemy.smoothedPushedVelocity * enemy.smoothVelocityRate );
  2071.     }     
  2072.     
  2073.     // Update enemy range
  2074.     enemy.range      = DistanceTo ( enemy.lastKnownPosition );
  2075.     enemy.range2d = DistanceTo2d ( enemy.lastKnownPosition );
  2076.  
  2077.     // Calulcate the aggression scale
  2078.     // Skill level 0: (1.0)
  2079.     // Skill level 1: (1.0 - 1.25)
  2080.     // Skill level 2: (1.0 - 1.5)
  2081.     // Skill level 3: (1.0 - 1.75)
  2082.     if ( combat.aggressiveRange > 0.0f ) {
  2083.         combat.aggressiveScale = (g_skill.GetFloat() / MAX_SKILL_LEVELS);
  2084.         combat.aggressiveScale *= 1.0f - idMath::ClampFloat ( 0.0f, 1.0f, enemy.range / combat.aggressiveRange );
  2085.         combat.aggressiveScale += 1.0f;
  2086.     }
  2087. }
  2088.  
  2089. /*
  2090. =====================
  2091. idAI::LastKnownPosition
  2092. =====================
  2093. */
  2094. const idVec3& idAI::LastKnownPosition ( const idEntity *ent ) {
  2095.     return    (ent==enemy.ent)?(enemy.lastKnownPosition):(ent->GetPhysics()->GetOrigin());
  2096. }
  2097.  
  2098. /*
  2099. =====================
  2100. idAI::UpdateEnemyVisibility
  2101.  
  2102. Update whether or not the AI can see its enemy or not and determine how.
  2103. =====================
  2104. */
  2105. void idAI::UpdateEnemyVisibility ( void ) {
  2106.     enemy.fl.visible = false;
  2107.  
  2108.     // No enemy so nothing to see
  2109.     if ( !enemy.ent ) {
  2110.         return;
  2111.     }
  2112.  
  2113.     // Update enemy visibility flag
  2114.     enemy.fl.visible = CanSeeFrom ( GetEyePosition ( ), enemy.ent, false );
  2115.  
  2116.     // IF the enemy isnt visible and not forcing an update we can just early out
  2117.     if ( !enemy.fl.visible ) {
  2118.         return;
  2119.     }
  2120.     
  2121.     // If we are seeing our enemy for the first time after changing enemies, call him out
  2122.     if ( enemy.lastVisibleTime < enemy.changeTime ) {
  2123.         AnnounceNewEnemy( );
  2124.     }
  2125.     
  2126.     combat.fl.seenEnemyDirectly = true;
  2127.     enemy.lastVisibleTime        = gameLocal.time;
  2128.     
  2129.     // Update our known locations of the enemy since we just saw him
  2130.     UpdateEnemyPosition ( );
  2131. }
  2132.  
  2133. /*
  2134. =====================
  2135. idAI::SetSpawner
  2136. =====================
  2137. */
  2138. void idAI::SetSpawner ( rvSpawner* _spawner ) {
  2139.     spawner = _spawner;
  2140. }
  2141.  
  2142. /*
  2143. =====================
  2144. idAI::SetSpawner
  2145. =====================
  2146. */
  2147. rvSpawner* idAI::GetSpawner ( void ) {
  2148.     return spawner;
  2149. }
  2150.  
  2151. /*
  2152. =====================
  2153. idAI::SetEnemy
  2154. =====================
  2155. */
  2156. bool idAI::SetEnemy( idEntity *newEnemy ) {
  2157.     idEntity*    oldEnemy;
  2158.  
  2159.     // Look for obvious early out
  2160.     if ( enemy.ent == newEnemy ) {
  2161.         return true; 
  2162.     }
  2163.  
  2164.     // Cant set enemy if dead
  2165.     if ( aifl.dead ) {
  2166.         ClearEnemy ( false );
  2167.         return false;
  2168.     }
  2169.  
  2170.     // Cant set enemy if the enemy is dead or has no target set
  2171.     if ( newEnemy ) {
  2172.         if ( newEnemy->health <= 0 || newEnemy->fl.notarget ) {
  2173.             return false;
  2174.         }
  2175.     }
  2176.  
  2177.     oldEnemy              = enemy.ent;
  2178.     enemy.fl.dead        = false;
  2179.     enemy.fl.lockOrigin    = false;
  2180.  
  2181.     if ( !newEnemy ) {
  2182.         ClearEnemy ( false );
  2183.     } else {
  2184.         // Set our current enemy
  2185.         enemy.ent = newEnemy;
  2186.  
  2187.         // If the enemy is an actor then add to the actors enemy list
  2188.         if( newEnemy->IsType( idActor::GetClassType() ) ){
  2189.             idActor* enemyActor = static_cast<idActor*>( newEnemy );
  2190.             enemyNode.AddToEnd( enemyActor->enemyList );
  2191.         }
  2192.     }
  2193.     
  2194.     // Allow custom handling of enemy change
  2195.     if ( enemy.ent != oldEnemy ) {
  2196.         OnEnemyChange ( oldEnemy );
  2197.     }
  2198.     
  2199.     return true;
  2200. }
  2201.  
  2202.  
  2203. /*
  2204. ===================
  2205. idAI::CalculateAttackOffsets
  2206.  
  2207. calculate joint positions on attack frames so we can do proper "can hit" tests
  2208. ===================
  2209. */
  2210. void idAI::CalculateAttackOffsets ( void ) {
  2211.     const idDeclModelDef*    modelDef;
  2212.     int                        num;
  2213.     int                        i;
  2214.     int                        frame;
  2215.     const frameCommand_t*    command;
  2216.     idMat3                    axis;
  2217.     const idAnim*            anim;
  2218.     jointHandle_t            joint;
  2219.  
  2220.     modelDef = animator.ModelDef();
  2221.     if ( !modelDef ) {
  2222.         return;
  2223.     }
  2224.     num = modelDef->NumAnims();
  2225.     
  2226.     // needs to be off while getting the offsets so that we account for the distance the monster moves in the attack anim
  2227.     animator.RemoveOriginOffset( false );
  2228.  
  2229.     // anim number 0 is reserved for non-existant anims.  to avoid off by one issues, just allocate an extra spot for
  2230.     // launch offsets so that anim number can be used without subtracting 1.
  2231.     attackAnimInfo.SetGranularity( 1 );
  2232.     attackAnimInfo.SetNum( num + 1 );
  2233.     attackAnimInfo[ 0 ].attackOffset.Zero();
  2234.     attackAnimInfo[ 0 ].eyeOffset.Zero();
  2235.  
  2236.     for( i = 1; i <= num; i++ ) {
  2237.         attackAnimInfo[ i ].attackOffset.Zero();
  2238.         attackAnimInfo[ i ].eyeOffset.Zero();
  2239.         anim = modelDef->GetAnim( i );
  2240.         if ( !anim ) {
  2241.             continue;
  2242.         }
  2243.         
  2244.         frame = anim->FindFrameForFrameCommand( FC_AI_ATTACK, &command );
  2245.         if ( frame >= 0 ) {
  2246.             joint = animator.GetJointHandle( command->joint->c_str() );
  2247.             if ( joint == INVALID_JOINT ) {
  2248.                 gameLocal.Error( "Invalid joint '%s' on 'ai_attack' frame command on frame %d of model '%s'", command->joint->c_str(), frame, modelDef->GetName() );
  2249.             }
  2250.             GetJointTransformForAnim( joint, i, FRAME2MS( frame ), attackAnimInfo[ i ].attackOffset, axis );
  2251.             
  2252.             if ( chestOffsetJoint!= INVALID_JOINT ) {
  2253.                 GetJointTransformForAnim( chestOffsetJoint, i, FRAME2MS( frame ), attackAnimInfo[ i ].eyeOffset, axis );
  2254.             }
  2255.         }
  2256.     }
  2257.  
  2258.     animator.RemoveOriginOffset( true );
  2259. }
  2260.  
  2261. /*
  2262. =====================
  2263. idAI::CreateProjectileClipModel
  2264. =====================
  2265. */
  2266. void idAI::CreateProjectileClipModel( void ) const {
  2267.     if ( projectileClipModel == NULL ) {
  2268.         idBounds projectileBounds( vec3_origin );
  2269.         projectileBounds.ExpandSelf( 2.0f ); // projectileRadius );
  2270.         projectileClipModel    = new idClipModel( idTraceModel( projectileBounds ) );
  2271.     }
  2272. }
  2273.  
  2274. /*
  2275. =====================
  2276. idAI::GetPredictedAimDirOffset
  2277. =====================
  2278. */
  2279. void idAI::GetPredictedAimDirOffset ( const idVec3& source, const idVec3& target, float projectileSpeed, const idVec3& targetVelocity, idVec3& offset ) const {
  2280.     float  a;
  2281.     float  b;
  2282.     float  c;
  2283.     float  d;
  2284.     float  t;
  2285.     idVec3 r;
  2286.     
  2287.     // Make sure there is something to predict
  2288.     if ( targetVelocity.LengthSqr ( ) == 0.0f ) {
  2289.         offset.Zero ( );
  2290.         return;
  2291.     }
  2292.     
  2293.     // Solve for (t):  magnitude(targetVelocity * t + target - source) = projectileSpeed * t    
  2294.     
  2295.     r = (target-source);
  2296.     a = (targetVelocity * targetVelocity) - Square(projectileSpeed);
  2297.     b = (targetVelocity * 2.0f) * r;
  2298.     c = r*r;
  2299.     d = b*b - 4*a*c; 
  2300.     t = 0.0f;
  2301.     
  2302.     if ( d >= 0.0f ) {
  2303.         float  t1;
  2304.         float  t2;
  2305.         float  denom;
  2306.         d     = idMath::Sqrt(d);
  2307.         denom = 1.0f / (2.0f * a);
  2308.         t1    = (-b + d) * denom;
  2309.         t2    = (-b - d) * denom;
  2310.         if ( t1 < 0.0f && t2 < 0.0f ) {
  2311.             t = 0.0f;
  2312.         } else if ( t1 < 0.0f ) {
  2313.             t = t2;
  2314.         } else if ( t2 < 0.0f ) {
  2315.             t = t1;
  2316.         } else {
  2317.             t = Min(t1,t2);
  2318.         }
  2319.     }
  2320.  
  2321.     offset = targetVelocity * t;
  2322. }
  2323.  
  2324. /*
  2325. =====================
  2326. idAI::GetAimDir
  2327. =====================
  2328. */
  2329. bool idAI::GetAimDir( 
  2330.     const idVec3&    firePos, 
  2331.     const idEntity*    target, 
  2332.     const idDict*    projectileDef, 
  2333.     idEntity*        ignore, 
  2334.     idVec3&            aimDir, 
  2335.     float            aimOffset,
  2336.     float            predict
  2337.     ) const 
  2338. {
  2339.     idVec3        targetPos1;
  2340.     idVec3        targetPos2;
  2341.     idVec3        targetLinearVel;
  2342.     idVec3        targetPushedVel;
  2343.     idVec3        delta;
  2344.     float        max_height;
  2345.     bool        result;
  2346.     idEntity*    targetEnt = const_cast<idEntity*>(target);
  2347.  
  2348.     // if no aimAtEnt or projectile set
  2349.     if ( !targetEnt ) {
  2350.         aimDir = viewAxis[ 0 ] * physicsObj.GetGravityAxis();
  2351.         return false;
  2352.     }
  2353.  
  2354.     float    projectileSpeed   = idProjectile::GetVelocity ( projectileDef ).LengthFast ( );
  2355.     idVec3    projectileGravity = idProjectile::GetGravity ( projectileDef );
  2356.  
  2357.     if ( projectileClipModel == NULL ) {
  2358.         CreateProjectileClipModel();
  2359.     }
  2360.  
  2361.     if ( targetEnt == enemy.ent ) {
  2362.         targetPos2 = enemy.lastVisibleEyePosition;
  2363.         targetPos1 = enemy.lastVisibleChestPosition;
  2364.         targetPushedVel = enemy.smoothedPushedVelocity;
  2365.         targetLinearVel = enemy.smoothedLinearVelocity;
  2366.     } else if ( targetEnt->IsType( idActor::GetClassType() ) ) {
  2367.         targetPos2  = static_cast<idActor*>(targetEnt)->GetEyePosition ( );
  2368.         targetPos1  = static_cast<idActor*>(targetEnt)->GetChestPosition ( );
  2369.         targetPushedVel = target->GetPhysics()->GetPushedLinearVelocity ( );
  2370.         targetLinearVel = (target->GetPhysics()->GetLinearVelocity ( ) - targetPushedVel);
  2371.     } else {
  2372.         targetPos1 = targetEnt->GetPhysics()->GetAbsBounds().GetCenter();
  2373.         targetPos2 = targetPos1;
  2374.         targetPushedVel.Zero ( );
  2375.         targetLinearVel.Zero ( );
  2376.     }
  2377.     
  2378.     // Target prediction
  2379.     if ( projectileSpeed == 0.0f ) {
  2380.         // Hitscan prediction actually causes the hitscan to miss unless it is predicted.
  2381.         delta = (targetLinearVel * (-1.0f + predict));
  2382.     } else {
  2383.         // Projectile prediction must figure out how far to lead the shot to hit the target
  2384.         GetPredictedAimDirOffset ( firePos, targetPos1, projectileSpeed, (targetLinearVel*predict)+targetPushedVel, delta );
  2385.     }
  2386.     targetPos1 += delta;
  2387.     targetPos2 += delta;
  2388.  
  2389.     result = false;
  2390.     
  2391.     // try aiming for chest
  2392.     delta = targetPos1 - firePos;
  2393.     max_height = (delta.NormalizeFast ( ) + aimOffset) * projectile_height_to_distance_ratio;    
  2394.     if ( max_height > 0.0f ) {
  2395.         result = PredictTrajectory( firePos, targetPos1 + delta * aimOffset, projectileSpeed, projectileGravity, projectileClipModel, MASK_SHOT_RENDERMODEL, max_height, ignore, targetEnt, ai_debugTrajectory.GetBool() ? 1000 : 0, aimDir );
  2396.         if ( result || !targetEnt->IsType( idActor::GetClassType() ) ) {
  2397.             return result;
  2398.         }
  2399.     }
  2400.  
  2401.     // try aiming for head
  2402.     delta = targetPos2 - firePos;
  2403.     max_height = (delta.NormalizeFast ( ) + aimOffset) * projectile_height_to_distance_ratio;
  2404.     if ( max_height > 0.0f ) {
  2405.         result = PredictTrajectory( firePos, targetPos2 + delta * aimOffset, projectileSpeed, projectileGravity, projectileClipModel, MASK_SHOT_RENDERMODEL, max_height, ignore, targetEnt, ai_debugTrajectory.GetBool() ? 1000 : 0, aimDir );
  2406.     }
  2407.  
  2408.     return result;
  2409. }
  2410.  
  2411. /*
  2412. =====================
  2413. idAI::CreateProjectile
  2414. =====================
  2415. */
  2416. idProjectile *idAI::CreateProjectile ( const idDict* projectileDict, const idVec3 &pos, const idVec3 &dir ) {
  2417.     idEntity*    ent;
  2418.     const char*    clsname;
  2419.  
  2420.     if ( !projectile.GetEntity() ) {
  2421.         gameLocal.SpawnEntityDef( *projectileDict, &ent, false );
  2422.         if ( !ent ) {
  2423.             clsname = projectileDict->GetString( "classname" );
  2424.             gameLocal.Error( "Could not spawn entityDef '%s'", clsname );
  2425.         }
  2426.         
  2427.         if ( !ent->IsType( idProjectile::GetClassType() ) ) {
  2428.             clsname = ent->GetClassname();
  2429.             gameLocal.Error( "'%s' is not an idProjectile", clsname );
  2430.         }
  2431.         projectile = (idProjectile*)ent;
  2432.     }
  2433.  
  2434.     projectile.GetEntity()->Create( this, pos, dir );
  2435.  
  2436.     return projectile.GetEntity();
  2437. }
  2438.  
  2439. /*
  2440. =====================
  2441. idAI::RemoveProjectile
  2442. =====================
  2443. */
  2444. void idAI::RemoveProjectile( void ) {
  2445.     if ( projectile.GetEntity() ) {
  2446.         projectile.GetEntity()->PostEventMS( &EV_Remove, 0 );
  2447.         projectile = NULL;
  2448.     }
  2449. }
  2450.  
  2451. /*
  2452. =====================
  2453. idAI::Attack
  2454. =====================
  2455. */
  2456. bool idAI::Attack ( const char* attackName, jointHandle_t joint, idEntity* target, const idVec3& pushVelocity ) {
  2457.     // Get the attack dictionary
  2458.     const idDict* attackDict;
  2459.     attackDict = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( va("def_attack_%s", attackName ) ), false );
  2460.     if ( !attackDict ) {
  2461.         gameLocal.Error ( "could not find attack entityDef 'def_attack_%s (%s)' on AI entity %s", attackName, spawnArgs.GetString ( va("def_attack_%s", attackName ) ), GetName ( ) );
  2462.     }
  2463.  
  2464.     // Melee Attack?
  2465.     if ( spawnArgs.GetBool ( va("attack_%s_melee", attackName ), "0" ) ) {
  2466.         return AttackMelee ( attackName, attackDict );
  2467.     }
  2468.  
  2469.     // Ranged attack (hitscan or projectile)?
  2470.     return ( AttackRanged ( attackName, attackDict, joint, target, pushVelocity ) != NULL );
  2471. }
  2472.  
  2473. /*
  2474. =====================
  2475. idAI::AttackRanged
  2476. =====================
  2477. */
  2478. idProjectile* idAI::AttackRanged ( 
  2479.     const char*        attackName, 
  2480.     const idDict*    attackDict, 
  2481.     jointHandle_t    joint, 
  2482.     idEntity*        target, 
  2483.     const idVec3&    pushVelocity 
  2484.     )     
  2485. {
  2486.     float                attack_accuracy;
  2487.     float                attack_cone;
  2488.     float                attack_spread;
  2489.     int                    attack_count;
  2490.     bool                attack_hitscan;
  2491.     float                attack_predict;
  2492.     float                attack_pullback;
  2493.     int                    i;
  2494.     idVec3                muzzleOrigin;
  2495.     idMat3                muzzleAxis;
  2496.     idVec3                dir;
  2497.     idAngles            ang;
  2498.     idMat3                axis;
  2499.     idProjectile*        lastProjectile;
  2500.     
  2501.     lastProjectile        = NULL;
  2502.     
  2503.     // Generic attack properties
  2504.     attack_accuracy        = spawnArgs.GetFloat ( va("attack_%s_accuracy", attackName ), "7" );
  2505.     attack_cone            = spawnArgs.GetFloat ( va("attack_%s_cone", attackName ), "75" );
  2506.     attack_spread        = DEG2RAD ( spawnArgs.GetFloat ( va("attack_%s_spread", attackName ), "0" ) );
  2507.     attack_count        = spawnArgs.GetInt ( va("attack_%s_count", attackName ), "1" );
  2508.     attack_hitscan        = spawnArgs.GetBool ( va("attack_%s_hitscan", attackName ), "0" );
  2509.     attack_predict        = spawnArgs.GetFloat ( va("attack_%s_predict", attackName ), "0" );
  2510.     attack_pullback        = spawnArgs.GetFloat ( va("attack_%s_pullback", attackName ), "0" );
  2511.  
  2512.     // Get the muzzle origin and axis from the given launch joint
  2513.     GetMuzzle( joint, muzzleOrigin, muzzleAxis );
  2514.     if ( attack_pullback )
  2515.     {
  2516.         muzzleOrigin -= muzzleAxis[0]*attack_pullback;
  2517.     }
  2518.  
  2519.     // set aiming direction
  2520.     bool calcAim = true;
  2521.     if ( GetEnemy() && GetEnemy() == target && GetEnemy() == gameLocal.GetLocalPlayer() && spawnArgs.GetBool( va("attack_%s_missFirstShot",attackName) ) ) {
  2522.         //purposely miss
  2523.         if ( gameLocal.random.RandomFloat() < 0.5f ) {
  2524.             //actually, hit anyway...
  2525.         } else {
  2526.             idVec3 targetPos = enemy.lastVisibleEyePosition;
  2527.             idVec3 eFacing;
  2528.             idVec3 left, up;
  2529.             
  2530.             up.Set( 0, 0, 1 );
  2531.             left = viewAxis[0].Cross(up);
  2532.  
  2533.             if ( GetEnemy()->IsType( idActor::GetClassType() ) )
  2534.             {
  2535.                 eFacing = ((idActor*)GetEnemy())->viewAxis[0];
  2536.             }
  2537.             else
  2538.             {
  2539.                 eFacing = GetEnemy()->GetPhysics()->GetAxis()[0];
  2540.             }
  2541.             if ( left*eFacing > 0 )
  2542.             {
  2543.                 targetPos += left * ((gameLocal.random.RandomFloat()*8.0f) + 8.0f);
  2544.             }
  2545.             else
  2546.             {
  2547.                 targetPos -= left * ((gameLocal.random.RandomFloat()*8.0f) + 8.0f);
  2548.             }
  2549.             dir = targetPos-muzzleOrigin;
  2550.             dir.Normalize();
  2551.             attack_accuracy = 0;
  2552.  
  2553.             calcAim = false;
  2554.             //don't miss next time
  2555.             spawnArgs.SetBool( va("attack_%s_missFirstShot",attackName), false );
  2556.         }
  2557.     }
  2558.     if ( calcAim ) {
  2559.         if ( target && !spawnArgs.GetBool ( va("attack_%s_lockToJoint",attackName), "0" ) ) {
  2560.             GetAimDir( muzzleOrigin, target, attackDict, this, dir, 
  2561.                     spawnArgs.GetFloat ( va("attack_%s_aimoffset", attackName )),
  2562.                     attack_predict );
  2563.         } else {
  2564.             dir = muzzleAxis[0];
  2565.             if ( spawnArgs.GetBool ( va("attack_%s_lockToJoint",attackName), "0" ) )
  2566.             {
  2567.                 if ( spawnArgs.GetBool( va("attack_%s_traceToNextJoint", attackName ), "0" ) )
  2568.                 {
  2569.                     jointHandle_t endJoint = animator.GetFirstChild( joint );
  2570.                     if ( endJoint != INVALID_JOINT && endJoint != joint )
  2571.                     {
  2572.                         idVec3 endJointPos;
  2573.                         idMat3 blah;
  2574.                         GetJointWorldTransform( endJoint, gameLocal.GetTime(), endJointPos, blah );
  2575.                         dir = endJointPos-muzzleOrigin;
  2576.                         //ARGHH: need to be able to set range!!!
  2577.                         //const_cast<idDict*>(attackDict)->SetFloat( "range", dir.Normalize() );//NOTE: yes, I intentionally normalize here as well as use the result for length...
  2578.                         dir.Normalize();
  2579.                     }
  2580.                 }
  2581.             }
  2582.         }
  2583.     }
  2584.     
  2585.     // random accuracy
  2586.     ang = dir.ToAngles();
  2587.     ang.pitch += gameLocal.random.CRandomFloat ( ) * attack_accuracy;
  2588.     ang.yaw   += gameLocal.random.CRandomFloat ( ) * attack_accuracy;
  2589.  
  2590.     // Lock attacks to a cone?
  2591.     if ( attack_cone ) {
  2592.         float diff;
  2593.         // clamp the attack direction to be within monster's attack cone so he doesn't do
  2594.         // things like throw the missile backwards if you're behind him
  2595.         diff = idMath::AngleDelta( ang.yaw, move.current_yaw );
  2596.         if ( diff > attack_cone ) {
  2597.             ang.yaw = move.current_yaw + attack_cone;
  2598.         } else if ( diff < -attack_cone ) {
  2599.             ang.yaw = move.current_yaw - attack_cone;
  2600.         }
  2601.     }
  2602.  
  2603.     ang.Normalize360 ( );
  2604.     axis = ang.ToMat3();
  2605.  
  2606.     for( i = 0; i < attack_count; i++ ) {
  2607.         float angle;
  2608.         float spin;
  2609.         
  2610.         // spread the projectiles out
  2611.         angle = idMath::Sin( attack_spread * gameLocal.random.RandomFloat() );
  2612.         spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  2613.         dir = axis[ 0 ] + axis[ 2 ] * ( angle * idMath::Sin( spin ) ) - axis[ 1 ] * ( angle * idMath::Cos( spin ) );
  2614.         dir.Normalize();
  2615.  
  2616.         if ( attack_hitscan ) {
  2617.             gameLocal.HitScan( *attackDict, muzzleOrigin, dir, muzzleOrigin, this, false, combat.aggressiveScale );        
  2618.         } else {
  2619.             // launch the projectile
  2620.             if ( !projectile.GetEntity() ) {
  2621.                 CreateProjectile( attackDict, muzzleOrigin, dir );
  2622.             }
  2623.             lastProjectile = projectile.GetEntity();
  2624.             lastProjectile->Launch( muzzleOrigin, dir, pushVelocity, 0.0f, combat.aggressiveScale );
  2625.         
  2626.             // Let the script manage projectiles if need be
  2627.             ExecScriptFunction ( funcs.launch_projectile, lastProjectile );
  2628.         
  2629.             projectile = NULL;
  2630.         }
  2631.     }
  2632.  
  2633.     lastAttackTime = gameLocal.time;
  2634.  
  2635.     // If shooting at another ai entity then kick off an attack reaction
  2636.     if ( enemy.ent && enemy.ent->IsType ( idAI::GetClassType() ) ) {
  2637.         static_cast<idAI*>(enemy.ent.GetEntity())->ReactToShotAt ( this, muzzleOrigin, axis[0] );
  2638.     }
  2639.  
  2640.     return lastProjectile;
  2641. }
  2642.  
  2643. /*s
  2644. ================
  2645. idAI::DamageFeedback
  2646.  
  2647. callback function for when another entity recieved damage from this entity
  2648. ================
  2649. */
  2650. void idAI::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
  2651.     if ( ( victim == this ) && inflictor->IsType( idProjectile::GetClassType() ) ) {
  2652.         // monsters only get half damage from their own projectiles
  2653.         damage = ( damage + 1 ) / 2;  // round up so we don't do 0 damage
  2654.     } else if ( victim == enemy.ent ) {
  2655.         aifl.hitEnemy = true;
  2656.     }
  2657. }
  2658.  
  2659. /*
  2660. =====================
  2661. idAI::DirectDamage
  2662.  
  2663. Causes direct damage to an entity
  2664.  
  2665. kickDir is specified in the monster's coordinate system, and gives the direction
  2666. that the view kick and knockback should go
  2667. =====================
  2668. */
  2669. void idAI::DirectDamage( const char *meleeDefName, idEntity *ent ) {
  2670.     const idDict *meleeDef;
  2671.     const char *p;
  2672.     const idSoundShader *shader;
  2673.  
  2674.     meleeDef = gameLocal.FindEntityDefDict( meleeDefName, false );
  2675.     if ( !meleeDef ) {
  2676.         gameLocal.Error( "Unknown damage def '%s' on '%s'", meleeDefName, name.c_str() );
  2677.     }
  2678.  
  2679.     if ( !ent->fl.takedamage ) {
  2680.         const idSoundShader *shader = declManager->FindSound(meleeDef->GetString( "snd_miss" ));
  2681.         StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
  2682.         return;
  2683.     }
  2684.  
  2685.     //
  2686.     // do the damage
  2687.     //
  2688.     p = meleeDef->GetString( "snd_hit" );
  2689.     if ( p && *p ) {
  2690.         shader = declManager->FindSound( p );
  2691.         StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
  2692.     }
  2693.  
  2694.     idVec3    kickDir;
  2695.     meleeDef->GetVector( "kickDir", "0 0 0", kickDir );
  2696.  
  2697.     idVec3    globalKickDir;
  2698.     globalKickDir = ( viewAxis * physicsObj.GetGravityAxis() ) * kickDir;
  2699.  
  2700. //    ent->Damage( this, this, globalKickDir, meleeDefName, 1.0f, INVALID_JOINT );
  2701.     float    damageScale = spawnArgs.GetFloat( "damageScale", "1" );
  2702.     ent->Damage( this, this, globalKickDir, meleeDefName, damageScale, NULL );
  2703. }
  2704.  
  2705. /*
  2706. =====================
  2707. idAI::TestMelee
  2708. =====================
  2709. */
  2710. bool idAI::TestMelee( void ) const {
  2711.     trace_t        trace;
  2712.     idEntity*    enemyEnt = enemy.ent;
  2713.  
  2714.     if ( !enemyEnt || !combat.meleeRange ) {
  2715.         return false;
  2716.     }
  2717.  
  2718.     //FIXME: make work with gravity vector
  2719.     idVec3 org = physicsObj.GetOrigin();
  2720.     const idBounds &myBounds = physicsObj.GetBounds();
  2721.     idBounds bounds;
  2722.  
  2723.     // expand the bounds out by our melee range
  2724.     bounds[0][0] = -combat.meleeRange;
  2725.     bounds[0][1] = -combat.meleeRange;
  2726.     bounds[0][2] = myBounds[0][2] - 4.0f;
  2727.     bounds[1][0] = combat.meleeRange;
  2728.     bounds[1][1] = combat.meleeRange;
  2729.     bounds[1][2] = myBounds[1][2] + 4.0f;
  2730.     bounds.TranslateSelf( org );
  2731.  
  2732.     idVec3 enemyOrg = enemyEnt->GetPhysics()->GetOrigin();
  2733.     idBounds enemyBounds = enemyEnt->GetPhysics()->GetBounds();
  2734.     enemyBounds.TranslateSelf( enemyOrg );
  2735.  
  2736.     if ( DebugFilter(ai_debugMove) ) {    //YELLOW = Test Melee Bounds
  2737.         gameRenderWorld->DebugBounds( colorYellow, bounds, vec3_zero, gameLocal.msec );
  2738.     }
  2739.  
  2740.     if ( !bounds.IntersectsBounds( enemyBounds ) ) {
  2741.         return false;
  2742.     }
  2743.  
  2744.     idVec3 start = GetEyePosition();
  2745.     idVec3 end = enemyEnt->GetEyePosition();
  2746.  
  2747.     gameLocal.TracePoint( this, trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
  2748.     if ( ( trace.fraction == 1.0f ) || ( gameLocal.GetTraceEntity( trace ) == enemyEnt ) ) {
  2749.         return true;
  2750.     }
  2751.  
  2752.     return false;
  2753. }
  2754.  
  2755. /*
  2756. =====================
  2757. idAI::AttackMelee
  2758.  
  2759. jointname allows the endpoint to be exactly specified in the model,
  2760. as for the commando tentacle.  If not specified, it will be set to
  2761. the facing direction + combat.meleeRange.
  2762.  
  2763. kickDir is specified in the monster's coordinate system, and gives the direction
  2764. that the view kick and knockback should go
  2765. =====================
  2766. */
  2767. bool idAI::AttackMelee ( const char *attackName, const idDict* meleeDict ) {
  2768.     idEntity*                enemyEnt = enemy.ent;
  2769.     const char*                p;
  2770.     const idSoundShader*    shader;
  2771.  
  2772.     if ( !enemyEnt ) {
  2773.         p = meleeDict->GetString( "snd_miss" );
  2774.         if ( p && *p ) {
  2775.             shader = declManager->FindSound( p );
  2776.             StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
  2777.         }
  2778.         return false;
  2779.     }
  2780.  
  2781.     // check for the "saving throw" automatic melee miss on lethal blow
  2782.     // stupid place for this.
  2783.     bool forceMiss = false;
  2784.     if ( enemyEnt->IsType( idPlayer::GetClassType() ) && g_skill.GetInteger() < 2 ) {
  2785.         int    damage, armor;
  2786.         idPlayer *player = static_cast<idPlayer*>( enemyEnt );
  2787.         player->CalcDamagePoints( this, this, meleeDict, 1.0f, INVALID_JOINT, &damage, &armor );
  2788.  
  2789.         if ( enemyEnt->health <= damage ) {
  2790.             int    t = gameLocal.time - player->lastSavingThrowTime;
  2791.             if ( t > SAVING_THROW_TIME ) {
  2792.                 player->lastSavingThrowTime = gameLocal.time;
  2793.                 t = 0;
  2794.             }
  2795.             if ( t < 1000 ) {
  2796.                 forceMiss = true;
  2797.             }
  2798.         }
  2799.     }
  2800.  
  2801.     // make sure the trace can actually hit the enemy
  2802.     if ( forceMiss || !TestMelee( ) ) {
  2803.         // missed
  2804.         p = meleeDict->GetString( "snd_miss" );
  2805.         if ( p && *p ) {
  2806.             shader = declManager->FindSound( p );
  2807.             StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
  2808.         }
  2809.         return false;
  2810.     }
  2811.  
  2812.     //
  2813.     // do the damage
  2814.     //
  2815.     p = meleeDict->GetString( "snd_hit" );
  2816.     if ( p && *p ) {
  2817.         shader = declManager->FindSound( p );
  2818.         StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
  2819.     }
  2820.  
  2821.     idVec3    kickDir;
  2822.     meleeDict->GetVector( "kickDir", "0 0 0", kickDir );
  2823.  
  2824.     idVec3    globalKickDir;
  2825.     globalKickDir = ( viewAxis * physicsObj.GetGravityAxis() ) * kickDir;
  2826.  
  2827.     // This allows some AI to be soft against melee-- most marines are selfMeleeDamageScale of 3, which means they take 3X from melee attacks!
  2828.     float damageScale = spawnArgs.GetFloat( "damageScale", "1" ) * enemyEnt->spawnArgs.GetFloat ( "selfMeleeDamageScale", "1" );
  2829.  
  2830.     //if attacker is a melee superhero, damageScale is way increased
  2831.     idAI* enemyAI = static_cast<idAI*>(enemyEnt);
  2832.     if( aifl.meleeSuperhero)    {
  2833.         if( damageScale >=1 )    {
  2834.             damageScale *= 6;
  2835.         } else    {
  2836.             damageScale = 6;
  2837.         }
  2838.     }
  2839.     
  2840.     //if the defender is a melee superhero, damageScale is way decreased
  2841.     if( enemyAI->aifl.meleeSuperhero)    {
  2842.         damageScale = 0.5f;
  2843.     }
  2844.  
  2845.     int   location    = INVALID_JOINT;
  2846.     if ( enemyEnt->IsType ( idAI::Type ) ) {
  2847.         location = static_cast<idAI*>(enemyEnt)->chestOffsetJoint;
  2848.     }
  2849.     enemyEnt->Damage( this, this, globalKickDir, meleeDict->GetString ( "classname" ), damageScale, location );
  2850.  
  2851.     if ( meleeDict->GetString( "fx_impact", NULL ) ) {
  2852.         if ( enemyEnt == gameLocal.GetLocalPlayer() ) {
  2853.             idPlayer *ePlayer = static_cast<idPlayer*>(enemyEnt);
  2854.             if ( ePlayer ) {
  2855.                 idVec3 dir = ePlayer->firstPersonViewOrigin-GetEyePosition();
  2856.                 dir.Normalize();
  2857.                 idVec3 org = ePlayer->firstPersonViewOrigin + (dir * -((gameLocal.random.RandomFloat()*8.0f)+8.0f) );
  2858.                 idAngles ang = ePlayer->firstPersonViewAxis.ToAngles() * -1;
  2859.                 idMat3 axis = ang.ToMat3();
  2860.                 gameLocal.PlayEffect( *meleeDict, "fx_impact", org, viewAxis );
  2861.             }
  2862.         }
  2863.     }
  2864.  
  2865.     lastAttackTime = gameLocal.time;
  2866.  
  2867.     return true;
  2868. }
  2869.  
  2870. /*
  2871. ================
  2872. idAI::PushWithAF
  2873. ================
  2874. */
  2875. void idAI::PushWithAF( void ) {
  2876.     int i, j;
  2877.     afTouch_t touchList[ MAX_GENTITIES ];
  2878.     idEntity *pushed_ents[ MAX_GENTITIES ];
  2879.     idEntity *ent;
  2880.     idVec3 vel;
  2881.     int num_pushed;
  2882.  
  2883.     num_pushed = 0;
  2884.     af.ChangePose( this, gameLocal.time );
  2885.     int num = af.EntitiesTouchingAF( touchList );
  2886.     for( i = 0; i < num; i++ ) {
  2887.         if ( touchList[ i ].touchedEnt->IsType( idProjectile::GetClassType() ) ) {
  2888.             // skip projectiles
  2889.             continue;
  2890.         }
  2891.  
  2892.         // make sure we havent pushed this entity already.  this avoids causing double damage
  2893.         for( j = 0; j < num_pushed; j++ ) {
  2894.             if ( pushed_ents[ j ] == touchList[ i ].touchedEnt ) {
  2895.                 break;
  2896.             }
  2897.         }
  2898.         if ( j >= num_pushed ) {
  2899.             ent = touchList[ i ].touchedEnt;
  2900.             pushed_ents[num_pushed++] = ent;
  2901.             vel = ent->GetPhysics()->GetAbsBounds().GetCenter() - touchList[ i ].touchedByBody->GetWorldOrigin();
  2902.             vel.Normalize();
  2903.             ent->GetPhysics()->SetLinearVelocity( 100.0f * vel, touchList[ i ].touchedClipModel->GetId() );
  2904.         }
  2905.     }
  2906. }
  2907.  
  2908. /***********************************************************************
  2909.  
  2910.     Misc
  2911.  
  2912. ***********************************************************************/
  2913.  
  2914. /*
  2915. ================
  2916. idAI::GetMuzzle
  2917. ================
  2918. */
  2919. void idAI::GetMuzzle( jointHandle_t joint, idVec3 &muzzle, idMat3 &axis ) {
  2920.     if ( joint == INVALID_JOINT ) {
  2921.         muzzle = physicsObj.GetOrigin() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 14;
  2922.         muzzle -= physicsObj.GetGravityNormal() * physicsObj.GetBounds()[ 1 ].z * 0.5f;
  2923.     } else {
  2924.         GetJointWorldTransform( joint, gameLocal.time, muzzle, axis );
  2925.         //MCG
  2926.         //pull the muzzle back inside the bounds if possible
  2927.         //FIXME: this is nasty, we should just be able to check for starting in solid and register that as a hit!  But...
  2928.         /*
  2929.         float scale = 0.0f;
  2930.         if ( physicsObj.GetBounds().RayIntersection( muzzle, -axis[0], scale ) )
  2931.         {
  2932.             if ( scale != 0.0f )
  2933.             {//not already inside
  2934.                 muzzle += scale * -axis[0];
  2935.             }
  2936.         }
  2937.         else
  2938.         {//just pull it back a little anyway?
  2939.             idVec3 xyOfs = muzzle-physicsObj.GetOrigin();
  2940.             xyOfs.z = 0;
  2941.             muzzle += xyOfs.Length() * -axis[0];
  2942.         }
  2943.         */
  2944.     }
  2945. }
  2946.  
  2947. /*
  2948. ================
  2949. idAI::Hide
  2950. ================
  2951. */
  2952. void idAI::Hide( void ) {
  2953.     idActor::Hide();
  2954.     fl.takedamage = false;
  2955.     physicsObj.SetContents( 0 );
  2956.     physicsObj.GetClipModel()->Unlink();
  2957.     StopSound( SND_CHANNEL_AMBIENT, false );
  2958.  
  2959.     enemy.fl.inFov        = false;
  2960.     enemy.fl.visible    = false;
  2961.  
  2962.     StopMove( MOVE_STATUS_DONE );
  2963. }
  2964.  
  2965. /*
  2966. ================
  2967. idAI::Show
  2968. ================
  2969. */
  2970. void idAI::Show( void ) {
  2971.     idActor::Show();
  2972.     if ( spawnArgs.GetBool( "big_monster" ) ) {
  2973.         physicsObj.SetContents( 0 );
  2974.     } else if ( use_combat_bbox ) {
  2975.         physicsObj.SetContents( CONTENTS_BODY|CONTENTS_SOLID );
  2976.     } else {
  2977.         physicsObj.SetContents( CONTENTS_BODY );
  2978.     }
  2979.  
  2980.     physicsObj.GetClipModel()->Link();
  2981.  
  2982.     fl.takedamage = !spawnArgs.GetBool( "noDamage" );
  2983.     StartSound( "snd_ambient", SND_CHANNEL_AMBIENT, 0, false, NULL );
  2984. }
  2985.  
  2986. /*
  2987. ================
  2988. idAI::CanPlayChatterSounds
  2989.  
  2990. Used for playing chatter sounds on monsters.
  2991. ================
  2992. */
  2993. bool idAI::CanPlayChatterSounds( void ) const {
  2994.     if ( aifl.dead ) {
  2995.         return false;
  2996.     }
  2997.  
  2998.     if ( IsHidden() ) {
  2999.         return false;
  3000.     }
  3001.  
  3002.     if ( enemy.ent ) {
  3003.         return true;
  3004.     }
  3005.  
  3006.     if ( spawnArgs.GetBool( "no_idle_chatter" ) ) {
  3007.         return false;
  3008.     }
  3009.  
  3010.     return true;
  3011. }
  3012.  
  3013. /*
  3014. =====================
  3015. idAI::UpdateChatter
  3016. =====================
  3017. */
  3018. void idAI::UpdateChatter ( void ) {    
  3019.     int            chatterRate;
  3020.     const char* chatter;
  3021.  
  3022.     // No chatter?
  3023.     if ( !chatterRateIdle && !chatterRateCombat ) {
  3024.         return;
  3025.     }
  3026.  
  3027.     // check if it's time to play a chat sound
  3028.     if ( IsHidden() || !aifl.awake || aifl.dead || ( chatterTime > gameLocal.time ) ) {
  3029.         return;
  3030.     }
  3031.     
  3032.     // Skip first chatter
  3033.     if ( enemy.ent ) {
  3034.         chatter        = "lipsync_chatter_combat";
  3035.         chatterRate = chatterRateCombat;
  3036.     } else {
  3037.         chatter        = "lipsync_chatter_idle";
  3038.         chatterRate = chatterRateIdle;
  3039.     }
  3040.  
  3041.     // Can chatter ?
  3042.     if ( !chatterRate ) {
  3043.         return;
  3044.     }
  3045.  
  3046.     // Start chattering, but not if he's already speaking.  And he might already be speaking because he was scripted to do so.
  3047.     if ( chatterTime > 0 && !IsSpeaking() ) {
  3048.         Speak ( chatter, true );
  3049.     }
  3050.  
  3051.     // set the next chat time
  3052.     chatterTime = gameLocal.time + chatterRate + (gameLocal.random.RandomFloat() * chatterRate * 0.5f) - (chatterRate * 0.25f);
  3053. }
  3054.  
  3055. /*
  3056. =====================
  3057. idAI::HeardSound
  3058. =====================
  3059. */
  3060. idEntity *idAI::HeardSound( int ignore_team ){
  3061.     // check if we heard any sounds in the last frame
  3062.     idActor    *actor = gameLocal.GetAlertActor();
  3063.     if ( actor && ( !ignore_team || ( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) && gameLocal.InPlayerPVS( this ) )     {
  3064.         idVec3 pos = actor->GetPhysics()->GetOrigin();
  3065.         idVec3 org = physicsObj.GetOrigin();
  3066.         float dist = ( pos - org ).LengthSqr();
  3067.         
  3068.         if ( dist < Square( combat.earRange ) ) {
  3069.             //really close?
  3070.             if ( dist < Square( combat.earRange/4.0f ) ) {                
  3071.                 return actor;
  3072.             //possible LOS
  3073.             } else if ( dist < Square( combat.visRange * 2.0f ) && CanSee( actor, false ) ) {
  3074.                 return actor;
  3075.             } else if ( combat.fl.aware ) {
  3076.                 //FIXME: or, maybe find cover/hide/ambush spot and wait to ambush them?
  3077.             //don't have an enemy and not tethered to a position
  3078.             } else if ( !GetEnemy() && !tether ) {
  3079.                 //go into search mode
  3080.                 WanderAround();
  3081.                 move.fl.noRun = false;
  3082.                 move.fl.idealRunning = true;
  3083.                 //undid this: was causing them to not be able to do fine nav.
  3084.                 //move.fl.noWalk = true;
  3085.             }
  3086.         }
  3087.     }
  3088.  
  3089.     return NULL;
  3090. }
  3091.  
  3092. /*
  3093. ============
  3094. idAI::SetLeader
  3095. ============
  3096. */
  3097. void idAI::SetLeader ( idEntity *newLeader ) {
  3098.     idEntity* oldLeader = leader;
  3099.     
  3100.     if( !newLeader ){
  3101.         leader = NULL;
  3102.     } else if ( !newLeader->IsType( idActor::GetClassType() ) ) {
  3103.         gameLocal.Error( "'%s' is not an idActor (player or ai controlled character)", newLeader->name.c_str() );
  3104.     } else {
  3105.         leader = static_cast<idActor *>( newLeader );    
  3106.     }
  3107.     
  3108.     if ( oldLeader != leader ) {
  3109.         OnLeaderChange ( oldLeader );
  3110.     }
  3111. }
  3112.  
  3113.  
  3114.  
  3115. /***********************************************************************
  3116.  
  3117.     Head & torso aiming
  3118.  
  3119. ***********************************************************************/
  3120.  
  3121. /*
  3122. ================
  3123. idAI::UpdateAnimationControllers
  3124. ================
  3125. */
  3126. bool idAI::UpdateAnimationControllers( void ) {
  3127.     idVec3        left;
  3128.     idVec3         dir;
  3129.     idVec3         orientationJointPos;
  3130.     idVec3         localDir;
  3131.     idAngles     newLookAng;
  3132.     idMat3        mat;
  3133.     idMat3        axis;
  3134.     idMat3        orientationJointAxis;
  3135.     idVec3        pos;
  3136.     int            i;
  3137.     idAngles    jointAng;
  3138.     float        orientationJointYaw;
  3139.     float        currentHeadFocusRate = ( combat.fl.aware ? headFocusRate : headFocusRate * 0.5f );
  3140.  
  3141.     MEM_SCOPED_TAG(tag,MA_ANIM);
  3142.  
  3143.     if ( aifl.dead ) {
  3144.         return idActor::UpdateAnimationControllers();
  3145.     }
  3146.  
  3147.     if ( orientationJoint == INVALID_JOINT ) {
  3148.         orientationJointAxis = viewAxis;
  3149.         orientationJointPos = physicsObj.GetOrigin();
  3150.         orientationJointYaw = move.current_yaw;
  3151.     } else {
  3152.         GetJointWorldTransform( orientationJoint, gameLocal.time, orientationJointPos, orientationJointAxis );
  3153.         orientationJointYaw = orientationJointAxis[ 2 ].ToYaw();
  3154.         orientationJointAxis = idAngles( 0.0f, orientationJointYaw, 0.0f ).ToMat3();
  3155.     }
  3156.     
  3157.     // Update the IK after we've gotten all the joint positions we need, but before we set any joint positions.
  3158.     // Getting the joint positions causes the joints to be updated.  The IK gets joint positions itself (which
  3159.     // are already up to date because of getting the joints in this function) and then sets their positions, which
  3160.     // forces the heirarchy to be updated again next time we get a joint or present the model.  If IK is enabled,
  3161.     // or if we have a seperate head, we end up transforming the joints twice per frame.  Characters with no
  3162.     // head entity and no ik will only transform their joints once.  Set g_debuganim to the current entity number
  3163.     // in order to see how many times an entity transforms the joints per frame.
  3164.     idActor::UpdateAnimationControllers();
  3165.  
  3166.     // Update the focus position
  3167.     UpdateFocus( orientationJointAxis );
  3168.  
  3169.     //MCG NOTE: don't know why Dube added this extra check for the torsoAnim (5/09/05), but it was causing popping, so I took it out... :/
  3170.     //bool canLook = ( !torsoAnim.AnimDone(0) || !torsoAnim.GetAnimator()->CurrentAnim(ANIMCHANNEL_TORSO)->IsDone(gameLocal.GetTime()) || torsoAnim.Disabled() ) && !torsoAnim.GetAnimFlags().ai_no_look && !aifl.disableLook;
  3171.     //bool canLook = (!torsoAnim.GetAnimFlags().ai_no_look && !aifl.disableLook);
  3172.     
  3173.     bool canLook = (!animator.GetAnimFlags(animator.CurrentAnim(ANIMCHANNEL_TORSO)->AnimNum()).ai_no_look && !aifl.disableLook);
  3174.     if ( !canLook ) {
  3175.         //actually, do the looking, but bring it back forward...
  3176.         currentFocusPos = GetEyePosition() + orientationJointAxis[ 0 ] * 64.0f;
  3177.     }
  3178.  
  3179.     // Determine the new look yaw
  3180.     dir = currentFocusPos - orientationJointPos;
  3181.     newLookAng.yaw = dir.ToYaw( );
  3182.  
  3183.     // Determine the new look pitch
  3184.     dir = currentFocusPos - GetEyePosition();
  3185.     dir.NormalizeFast();
  3186.     orientationJointAxis.ProjectVector( dir, localDir );
  3187.     newLookAng.pitch = -idMath::AngleNormalize180( localDir.ToPitch() );
  3188.     newLookAng.roll    = 0.0f;
  3189.     
  3190.     if ( !canLook ) {
  3191.         //actually, do the looking, but bring it back forward...
  3192.         newLookAng.yaw = orientationJointAxis[0].ToYaw();
  3193.         newLookAng.pitch = 0;
  3194.     }
  3195.     // Add the angle change using the head focus rate and convert the look angles to
  3196.     // local angles so they can be properly clamped.
  3197.     float f = lookAng.yaw;
  3198.     if ( lookMin.yaw <= -360.0f && lookMax.yaw >= 360.0f )
  3199.     {
  3200.         lookAng.yaw    = f - orientationJointYaw;
  3201.         float diff;
  3202.         diff = ( idMath::AngleNormalize360(newLookAng.yaw) - idMath::AngleNormalize360(f) );
  3203.         diff = idMath::AngleNormalize180( diff );
  3204.         lookAng.yaw    += diff * currentHeadFocusRate;
  3205.         //lookAng.yaw   += ( newLookAng.yaw - f ) * currentHeadFocusRate;
  3206.     }
  3207.     else
  3208.     {
  3209.         lookAng.yaw    = idMath::AngleNormalize180 ( f - orientationJointYaw );
  3210.         lookAng.yaw   += (idMath::AngleNormalize180 ( newLookAng.yaw - f ) * currentHeadFocusRate);
  3211.     }
  3212.     lookAng.pitch += ( idMath::AngleNormalize180( newLookAng.pitch - lookAng.pitch ) * currentHeadFocusRate );
  3213.  
  3214.     // Clamp the look angles
  3215.     lookAng.Clamp( lookMin, lookMax );
  3216.  
  3217.     // Calcuate the eye angles
  3218.     f = eyeAng.yaw;
  3219.     eyeAng.yaw    = idMath::AngleNormalize180( f - orientationJointYaw );
  3220.     eyeAng.yaw   += (idMath::AngleNormalize180( newLookAng.yaw - f ) * eyeFocusRate);    
  3221.     eyeAng.pitch += (idMath::AngleNormalize180( newLookAng.pitch - eyeAng.pitch ) * eyeFocusRate);
  3222.  
  3223.     // Clamp eye angles relative to the look angles
  3224.     jointAng = eyeAng - lookAng;
  3225.     jointAng.Normalize180( );    
  3226.     jointAng.Clamp( eyeMin, eyeMax );
  3227.     eyeAng = lookAng + jointAng;
  3228.     eyeAng.Normalize180( );    
  3229.  
  3230.     if ( canLook ) {
  3231.         // Apply the look angles to the look joints
  3232.         if ( animator.GetAnimFlags( animator.CurrentAnim( ANIMCHANNEL_TORSO )->AnimNum()).ai_look_head_only ) {
  3233.             if ( neckJoint != INVALID_JOINT || headJoint != INVALID_JOINT ) {
  3234.                 float jScale = 1.0f;
  3235.                 if ( neckJoint != INVALID_JOINT && headJoint != INVALID_JOINT ) {
  3236.                     jScale = 0.5f;
  3237.                 }
  3238.                 jointAng.pitch    = lookAng.pitch * jScale;
  3239.                 jointAng.yaw    = lookAng.yaw * jScale;
  3240.                 if ( neckJoint != INVALID_JOINT ) {
  3241.                     animator.SetJointAxis( neckJoint, JOINTMOD_WORLD, jointAng.ToMat3() );
  3242.                 }
  3243.                 if ( headJoint != INVALID_JOINT ) {
  3244.                     animator.SetJointAxis( headJoint, JOINTMOD_WORLD, jointAng.ToMat3() );
  3245.                 }
  3246.             }
  3247.             //what if we have a previous joint mod on the rest of the lookJoints 
  3248.             //from an anim that *wasn't* ai_look_head_only...?
  3249.             //just clear them?  Or move them towards 0?
  3250.             //FIXME: move them towards zero...
  3251.             if ( newLookAng.Compare( lookAng ) ) {
  3252.                 //snap back now!
  3253.                 for( i = 0; i < lookJoints.Num(); i++ ) {
  3254.                     if ( lookJoints[i] != neckJoint
  3255.                         && lookJoints[i] != headJoint ) {
  3256.                         //snap back now!
  3257.                         animator.ClearJoint ( lookJoints[i] );
  3258.                     }
  3259.                 }
  3260.             } else {
  3261.                 //blend back
  3262.                 //yes, this is framerate dependant and inefficient and wrong, but... eliminates pops
  3263.                 jointAng.roll = 0.0f;
  3264.                 jointMod_t *curJointMod;
  3265.                 idAngles curJointAngleMod;
  3266.                 for( i = 0; i < lookJoints.Num(); i++ ) {
  3267.                     if ( lookJoints[i] != neckJoint
  3268.                         && lookJoints[i] != headJoint ) {
  3269.                         //blend back
  3270.                         curJointMod = animator.FindExistingJointMod( lookJoints[ i ], NULL );
  3271.                         if ( curJointMod )
  3272.                         {
  3273.                             curJointAngleMod = curJointMod->mat.ToAngles();
  3274.                             curJointAngleMod *= 0.75f;
  3275.                             if ( fabs(curJointAngleMod.pitch) < 1.0f 
  3276.                                 && fabs(curJointAngleMod.yaw) < 1.0f
  3277.                                 && fabs(curJointAngleMod.roll) < 1.0f )
  3278.                             {
  3279.                                 //snap back now!
  3280.                                 animator.ClearJoint ( lookJoints[i] );
  3281.                             }
  3282.                             else
  3283.                             {
  3284.                                 animator.SetJointAxis( lookJoints[ i ], JOINTMOD_WORLD, curJointAngleMod.ToMat3() );
  3285.                             }
  3286.                         }
  3287.                     }
  3288.                 }
  3289.             }
  3290.         } else {
  3291.             jointAng.roll = 0.0f;
  3292.             for( i = 0; i < lookJoints.Num(); i++ ) {
  3293.                 jointAng.pitch    = lookAng.pitch * lookJointAngles[ i ].pitch;
  3294.                 jointAng.yaw    = lookAng.yaw * lookJointAngles[ i ].yaw;
  3295.                 animator.SetJointAxis( lookJoints[ i ], JOINTMOD_WORLD, jointAng.ToMat3() );
  3296.             }
  3297.         }
  3298.     } else {
  3299.         //if ( animator.GetAnimFlags(animator.CurrentAnim(ANIMCHANNEL_TORSO)->AnimNum()).ai_no_look || aifl.disableLook ) {
  3300.         if ( newLookAng.Compare( lookAng ) ) {
  3301.             //snap back now!
  3302.             for( i = 0; i < lookJoints.Num(); i++ ) {
  3303.                 animator.ClearJoint ( lookJoints[i] );
  3304.             }
  3305.         } else {
  3306.             //PCJ back to neutral
  3307.             jointAng.roll = 0.0f;
  3308.             for( i = 0; i < lookJoints.Num(); i++ ) {
  3309.                 jointAng.pitch    = lookAng.pitch * lookJointAngles[ i ].pitch;
  3310.                 jointAng.yaw    = lookAng.yaw * lookJointAngles[ i ].yaw;
  3311.                 animator.SetJointAxis( lookJoints[ i ], JOINTMOD_WORLD, jointAng.ToMat3() );
  3312.             }
  3313.         }
  3314.     }    
  3315.  
  3316.     // Convert the look angles back to world angles.  This is done to prevent dramatic orietation changes
  3317.     // from changing the look direction abruptly (unless of course the minimums are not met)
  3318.     lookAng.yaw = idMath::AngleNormalize180( lookAng.yaw + orientationJointYaw );
  3319.     eyeAng.yaw = idMath::AngleNormalize180( eyeAng.yaw + orientationJointYaw );
  3320.     
  3321.     if ( move.moveType == MOVETYPE_FLY || move.fl.flyTurning ) {
  3322.         // lean into turns
  3323.         AdjustFlyingAngles();
  3324.     }
  3325.  
  3326.     // Orient the eyes towards their target
  3327.     if ( leftEyeJoint != INVALID_JOINT && rightEyeJoint != INVALID_JOINT ) {
  3328.         if ( head ) {
  3329.             idAnimator *headAnimator = head->GetAnimator();
  3330.  
  3331.             if ( focusType != AIFOCUS_NONE && allowEyeFocus && canLook ) {
  3332.                 //tweak these since it's looking at the wrong spot and not calculating from each eye and I don't have time to bother rewriting all of this properly
  3333.                 eyeAng.yaw -= 0.5f;
  3334.                 eyeAng.pitch += 3.25f;
  3335.                 idMat3 eyeAxis = ( eyeAng ).ToMat3() * head->GetPhysics()->GetAxis().Transpose ( );  
  3336.  
  3337.                 headAnimator->SetJointAxis ( leftEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyeAxis );    
  3338.  
  3339.                 eyeAng.yaw += 3.0f;
  3340.                 eyeAxis = ( eyeAng ).ToMat3() * head->GetPhysics()->GetAxis().Transpose ( );  
  3341.  
  3342.                 headAnimator->SetJointAxis ( rightEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyeAxis );
  3343.  
  3344.                 if ( ai_debugEyeFocus.GetBool() ) {
  3345.                     idVec3 eyeOrigin;
  3346.                     idMat3 axis;
  3347.                     
  3348.                     head->GetJointWorldTransform ( rightEyeJoint, gameLocal.time, eyeOrigin, axis );
  3349.                     gameRenderWorld->DebugArrow ( colorGreen, eyeOrigin, ( eyeOrigin + (32.0f*axis[0])), 1, 0 );
  3350.  
  3351.                     head->GetJointWorldTransform ( leftEyeJoint,  gameLocal.time, eyeOrigin,  axis );
  3352.                     gameRenderWorld->DebugArrow ( colorGreen, eyeOrigin, ( eyeOrigin + (32.0f*axis[0])), 1, 0 );
  3353.                 }
  3354.             } else {
  3355.                 headAnimator->ClearJoint( leftEyeJoint );
  3356.                 headAnimator->ClearJoint( rightEyeJoint );
  3357.             }
  3358.         } else {
  3359.             if ( allowEyeFocus && focusType != AIFOCUS_NONE && !torsoAnim.GetAnimFlags().ai_no_look && !aifl.disableLook ) {
  3360.                 idMat3 eyeAxis = ( eyeAng ).ToMat3() * GetPhysics()->GetAxis().Transpose ( );  
  3361.                 animator.SetJointAxis ( rightEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyeAxis );
  3362.                 animator.SetJointAxis ( leftEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyeAxis );            
  3363.             } else {
  3364.                 animator.ClearJoint( leftEyeJoint );
  3365.                 animator.ClearJoint( rightEyeJoint );
  3366.             }
  3367.         }
  3368.     }
  3369.     
  3370.     return true;
  3371. }
  3372.  
  3373. void idAI::OnTouch( idEntity *other, trace_t *trace ) {
  3374.     // if we dont have an enemy or had one for at least a second that is a potential enemy the set the enemy    
  3375.     if ( other->IsType( idActor::GetClassType() )
  3376.         && !other->fl.notarget 
  3377.         && ( ReactionTo( other )&ATTACK_ON_SIGHT)
  3378.         && (!enemy.ent || gameLocal.time - enemy.changeTime > 1000 ) ) {
  3379.         SetEnemy( other );
  3380.     }
  3381.  
  3382.     if ( !enemy.ent && !other->fl.notarget && ( ReactionTo( other ) & ATTACK_ON_ACTIVATE ) ) {
  3383.         Activate( other );
  3384.     }
  3385.     pusher = other;
  3386.     aifl.pushed = true;
  3387.     
  3388.     // If pushed by the player update tactical
  3389.     if ( pusher && pusher->IsType ( idPlayer::GetClassType() ) && (combat.tacticalMaskAvailable & AITACTICAL_MOVE_PLAYERPUSH_BIT) ) {
  3390.         ForceTacticalUpdate ( );
  3391.     }        
  3392. }
  3393.  
  3394. idProjectile* idAI::AttackProjectile ( const idDict* projectileDict, const idVec3 &org, const idAngles &ang ) {
  3395.     idVec3                start;
  3396.     trace_t                tr;
  3397.     idBounds            projBounds;
  3398.     const idClipModel*    projClip;
  3399.     idMat3                axis;
  3400.     float                distance;
  3401.     idProjectile*        result;
  3402.  
  3403.     if ( !projectileDict ) {
  3404.         gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
  3405.         return NULL;
  3406.     }
  3407.  
  3408.     axis = ang.ToMat3();
  3409.     if ( !projectile.GetEntity() ) {
  3410.         CreateProjectile( projectileDict, org, axis[ 0 ] );
  3411.     }
  3412.  
  3413.     // make sure the projectile starts inside the monster bounding box
  3414.     const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  3415.     projClip = projectile.GetEntity()->GetPhysics()->GetClipModel();
  3416.     projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
  3417.  
  3418.     // check if the owner bounds is bigger than the projectile bounds
  3419.     if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
  3420.         ( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
  3421.         ( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
  3422.         if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
  3423.             start = org + distance * viewAxis[ 0 ];
  3424.         } else {
  3425.             start = ownerBounds.GetCenter();
  3426.         }
  3427.     } else {
  3428.         // projectile bounds bigger than the owner bounds, so just start it from the center
  3429.         start = ownerBounds.GetCenter();
  3430.     }
  3431.  
  3432. // RAVEN BEGIN
  3433. // ddynerman: multiple clip worlds
  3434.     gameLocal.Translation( this, tr, start, org, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
  3435. // RAVEN END
  3436.  
  3437.     // launch the projectile
  3438.      projectile.GetEntity()->Launch( tr.endpos, axis[ 0 ], vec3_origin );
  3439.      result = projectile;
  3440.     projectile = NULL;
  3441.  
  3442.     lastAttackTime = gameLocal.time;
  3443.     
  3444.     return result;
  3445. }
  3446.  
  3447. void idAI::RadiusDamageFromJoint( const char *jointname, const char *damageDefName ) {
  3448.     jointHandle_t joint;
  3449.     idVec3 org;
  3450.     idMat3 axis;
  3451.  
  3452.     if ( !jointname || !jointname[ 0 ] ) {
  3453.         org = physicsObj.GetOrigin();
  3454.     } else {
  3455.         joint = animator.GetJointHandle( jointname );
  3456.         if ( joint == INVALID_JOINT ) {
  3457.             gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
  3458.         }
  3459.         GetJointWorldTransform( joint, gameLocal.time, org, axis );
  3460.     }
  3461.  
  3462.     gameLocal.RadiusDamage( org, this, this, this, this, damageDefName );
  3463. }
  3464.  
  3465. /*
  3466. =====================
  3467. idAI::CanBecomeSolid
  3468.  
  3469. returns true if the AI entity could become solid at its current position
  3470. =====================
  3471. */
  3472. bool idAI::CanBecomeSolid ( void ) {
  3473.     int                i;
  3474.     int                num;
  3475.     idEntity *        hit;
  3476.     idClipModel*    cm;
  3477.     idClipModel*    clipModels[ MAX_GENTITIES ];
  3478.  
  3479.     // Determine what we are currently touching
  3480. // RAVEN BEGIN
  3481. // ddynerman: multiple clip worlds
  3482.     num = gameLocal.ClipModelsTouchingBounds( this, physicsObj.GetAbsBounds(), MASK_MONSTERSOLID, clipModels, MAX_GENTITIES );
  3483. // RAVEN END
  3484.     for ( i = 0; i < num; i++ ) {
  3485.         cm = clipModels[ i ];
  3486.  
  3487.         // don't check render entities
  3488.         if ( cm->IsRenderModel() ) {
  3489.             continue;
  3490.         }
  3491.  
  3492.         hit = cm->GetEntity();
  3493.         if ( ( hit == this ) || !hit->fl.takedamage ) {
  3494.             continue;
  3495.         }
  3496.  
  3497.         if ( physicsObj.ClipContents( cm ) ) {
  3498.             return false;
  3499.         }
  3500.     }
  3501.  
  3502.     return true;
  3503. }
  3504.  
  3505. /*
  3506. =====================
  3507. idAI::BecomeSolid
  3508. =====================
  3509. */
  3510. void idAI::BecomeSolid( void ) {
  3511.     physicsObj.EnableClip();
  3512.     if ( spawnArgs.GetBool( "big_monster" ) ) {
  3513.         physicsObj.SetContents( 0 );
  3514.     } else if ( use_combat_bbox ) {
  3515.         physicsObj.SetContents( CONTENTS_BODY|CONTENTS_SOLID );
  3516.     } else {
  3517.         physicsObj.SetContents( CONTENTS_BODY );
  3518.     }
  3519. // RAVEN BEGIN
  3520. // ddynerman: multiple clip worlds
  3521.     physicsObj.GetClipModel()->Link();
  3522. // RAVEN END
  3523.     fl.takedamage = !spawnArgs.GetBool( "noDamage" );
  3524. }
  3525.  
  3526. /*
  3527. =====================
  3528. idAI::BecomeNonSolid
  3529. =====================
  3530. */
  3531. void idAI::BecomeNonSolid( void ) {
  3532.     fl.takedamage = false;
  3533.     physicsObj.SetContents( 0 );
  3534.     physicsObj.GetClipModel()->Unlink();
  3535. }
  3536.  
  3537. const char *idAI::ChooseAnim( int channel, const char *animname ) {
  3538.     int anim;
  3539.  
  3540.     anim = GetAnim( channel, animname );
  3541.     if ( anim ) {
  3542.         if ( channel == ANIMCHANNEL_HEAD ) {
  3543.             if ( head.GetEntity() ) {
  3544.                 return head.GetEntity()->GetAnimator()->AnimFullName( anim );
  3545.             }
  3546.         } else {
  3547.             return animator.AnimFullName( anim );
  3548.         }
  3549.     }
  3550.  
  3551.     return "";
  3552. }
  3553.  
  3554. /*
  3555. ============
  3556. idAI::ExecScriptFunction
  3557. ============
  3558. */
  3559. void idAI::ExecScriptFunction ( rvScriptFuncUtility& func, idEntity* parm ) {
  3560.     if( parm ) {
  3561.         func.InsertEntity( parm, 0 );
  3562.     } else {
  3563.         func.InsertEntity( this, 0 );
  3564.     }
  3565.  
  3566.     func.CallFunc( &spawnArgs );
  3567.  
  3568.     if( parm ) {
  3569.         func.RemoveIndex( 0 );
  3570.     }
  3571. }
  3572.  
  3573. /*
  3574. ============
  3575. idAI::Prethink
  3576. ============
  3577. */
  3578. void idAI::Prethink ( void ) {
  3579.     // Update our helper if we are moving
  3580.     if ( move.fl.moving ) {
  3581.         UpdateHelper ( );
  3582.     }
  3583.     
  3584.     if ( leader ) {
  3585.         idEntity* groundEnt    = leader->GetGroundEntity ( );
  3586.         if ( !(tether && !aifl.tetherMover) && groundEnt ) {
  3587.             if ( groundEnt->IsType ( idMover::GetClassType ( ) ) ) {            
  3588.                 idEntity* ent;
  3589.                 idEntity* next;
  3590.                 for( ent = groundEnt->GetNextTeamEntity(); ent != NULL; ent = next ) {
  3591.                     next = ent->GetNextTeamEntity();
  3592.                     if ( ent->GetBindMaster() == groundEnt && ent->IsType ( rvAITether::GetClassType ( ) ) ) {
  3593.                         SetTether ( static_cast<rvAITether*>(ent) );
  3594.                         aifl.tetherMover = true;
  3595.                         break;
  3596.                     }
  3597.                 }
  3598.             } else {
  3599.                 SetTether ( NULL );
  3600.             }
  3601.         }                    
  3602.     } else if ( tether && aifl.tetherMover ) {
  3603.         SetTether ( NULL );
  3604.     }
  3605. }
  3606.  
  3607. /*
  3608. ============
  3609. idAI::Postthink
  3610. ============
  3611. */
  3612. void idAI::Postthink( void ){
  3613.     if ( !aifl.simpleThink ) {
  3614.         pain.takenThisFrame = 0;
  3615.     }
  3616.  
  3617.     // Draw debug tactical information 
  3618.     DrawTactical ( );
  3619.                 
  3620.     if( vehicleController.IsDriving() ){    // Generate some sort of command?
  3621.         usercmd_t                usercmd;
  3622.  
  3623.         // Note!  usercmd angles stuff is in deltas, not in absolute values.
  3624.  
  3625.         memset( &usercmd, 0, sizeof( usercmd ) );
  3626.  
  3627.         idVec3 toEnemy;
  3628.  
  3629.         if( enemy.ent ){
  3630.             toEnemy = enemy.ent->GetPhysics()->GetOrigin();
  3631.             toEnemy -= GetPhysics()->GetOrigin();
  3632.             toEnemy.Normalize();
  3633.             
  3634.             idAngles enemyAng;
  3635.  
  3636.             enemyAng = toEnemy.ToAngles();
  3637.  
  3638.             usercmd.angles[PITCH] = ANGLE2SHORT( enemyAng.pitch );
  3639.             usercmd.angles[YAW] = ANGLE2SHORT( enemyAng.yaw );
  3640.  
  3641.             usercmd.buttons = BUTTON_ATTACK;
  3642.  
  3643.             vehicleController.SetInput ( usercmd, enemyAng );
  3644.         }
  3645.     }
  3646.     
  3647.     // Keep our threat value up to date
  3648.     UpdateThreat ( );
  3649. }
  3650.  
  3651. /*
  3652. ============
  3653. idAI::
  3654. ============
  3655. */
  3656.  
  3657. void idAI::OnDeath( void ){
  3658.     if( vehicleController.IsDriving() ){
  3659.         usercmd_t                usercmd;
  3660.  
  3661.         memset( &usercmd, 0, sizeof( usercmd ) );
  3662.         usercmd.buttons = BUTTON_ATTACK;
  3663.         usercmd.upmove = 300.0f; // This will cause the character to eject.
  3664.  
  3665.         vehicleController.SetInput( usercmd, idAngles( 0, 0, 0 ) );
  3666.  
  3667.         // Fixme!  Is this safe to do immediately?
  3668.         vehicleController.Eject();
  3669.     }
  3670.  
  3671.     aiManager.RemoveTeammate ( this );
  3672.  
  3673.     ExecScriptFunction( funcs.death );
  3674.  
  3675. /* DONT DROP ANYTHING FOR NOW
  3676.     float rVal = gameLocal.random.RandomInt( 100 );
  3677.  
  3678.     if( spawnArgs.GetFloat( "no_drops" ) >= 1.0 ){
  3679.         spawnArgs.Set( "def_dropsItem1", "" );
  3680.     }else{
  3681.         // Fixme!  Better guys should drop better stuffs!  Make drops related to guy type?  Do something cooler here?
  3682.         if( rVal < 25 ){    // Half of guys drop nothing?
  3683.             spawnArgs.Set( "def_dropsItem1", "" );
  3684.         }else if( rVal < 50 ){
  3685.             spawnArgs.Set( "def_dropsItem1", "item_health_small" );
  3686.         }
  3687.     }
  3688. */
  3689. }
  3690.  
  3691. /*
  3692. ============
  3693. idAI::OnWakeUp
  3694. ============
  3695. */
  3696. void idAI::OnWakeUp ( void ) {
  3697. }
  3698.  
  3699. /*
  3700. ============
  3701. idAI::OnUpdatePlayback
  3702. ============
  3703. */
  3704. void idAI::OnUpdatePlayback ( const rvDeclPlaybackData& pbd ) {
  3705.     return;
  3706. }
  3707.  
  3708. /*
  3709. ============
  3710. idAI::OnLeaderChange
  3711. ============
  3712. */
  3713. void idAI::OnLeaderChange ( idEntity* oldLeader ) {
  3714.     ForceTacticalUpdate ( );
  3715. }
  3716.  
  3717. /*
  3718. ============
  3719. idAI::OnEnemyChange
  3720. ============
  3721. */
  3722. void idAI::OnEnemyChange ( idEntity* oldEnemy ) {
  3723.     // Make sure we update our tactical state immediately
  3724.     ForceTacticalUpdate ( );
  3725.  
  3726.     // see if we should announce the enemy
  3727.     if ( enemy.ent ) {
  3728.         combat.fl.aware = true;
  3729.         combat.investigateTime = 0;
  3730.  
  3731.         enemy.changeTime = gameLocal.time;
  3732.  
  3733.         enemy.lastKnownPosition = enemy.ent->GetPhysics()->GetOrigin ( );
  3734.         
  3735.         UpdateEnemyVisibility ( );
  3736.         UpdateEnemyPosition ( true );
  3737.         UpdateEnemy ( );        
  3738.     } else {
  3739.         enemy.range         = 0;
  3740.         enemy.range2d      = 0;
  3741.         enemy.changeTime = 0;
  3742.         enemy.smoothedLinearVelocity.Zero ( );
  3743.         enemy.smoothedPushedVelocity.Zero ( );
  3744.     }    
  3745.     
  3746.     enemy.fl.visible = false;
  3747. }
  3748.  
  3749. /*
  3750. ============
  3751. idAI::OnTacticalChange
  3752. ============
  3753. */
  3754. void idAI::OnTacticalChange ( aiTactical_t oldTactical ) {
  3755.     // if acutally moving to a new tactial location announce it
  3756.     if ( move.fl.moving ) {
  3757.         AnnounceTactical( combat.tacticalCurrent );
  3758.     }
  3759. }
  3760.  
  3761. /*
  3762. ============
  3763. idAI::OnFriendlyFire
  3764. ============
  3765. */
  3766. void idAI::OnFriendlyFire ( idActor* attacker ) {
  3767.     AnnounceFriendlyFire( static_cast<idActor*>(attacker) );
  3768. }    
  3769.  
  3770. /*
  3771. ============
  3772. idAI::OnStartMoving
  3773. ============
  3774. */
  3775. void idAI::OnStartMoving ( void ) {
  3776.     aifl.simpleThink = false;
  3777.     combat.fl.crouchViewClear = false;
  3778. }
  3779.  
  3780. /*
  3781. ============
  3782. idAI::OnStopMoving
  3783. ============
  3784. */
  3785. void idAI::OnStopMoving ( aiMoveCommand_t oldMoveCommand ) {
  3786. }
  3787.  
  3788. /*
  3789. ============
  3790. idAI::OnStartAction
  3791. ============
  3792. */
  3793. void idAI::OnStartAction ( void ) {
  3794. }
  3795.  
  3796. /*
  3797. ============
  3798. idAI::OnStopAction
  3799. ============
  3800. */
  3801. void idAI::OnStopAction    ( void ) {
  3802. }
  3803.  
  3804. /*
  3805. ============
  3806. idAI::OnEnemyVisiblityChange
  3807. ============
  3808. */
  3809. void idAI::OnEnemyVisiblityChange ( bool oldVisible ) {
  3810. }
  3811.  
  3812. /*
  3813. ============
  3814. idAI::OnSetKey
  3815. ============
  3816. */
  3817. void idAI::OnSetKey    ( const char* key, const char* value ) {
  3818.     if ( !idStr::Icmp ( key, "noCombatChatter" ) ) {
  3819.         combat.fl.noChatter = spawnArgs.GetBool ( key );
  3820.     } else if ( !idStr::Icmp ( key, "allowPlayerPush" ) ) {
  3821.         combat.tacticalMaskAvailable &= ~(AITACTICAL_MOVE_PLAYERPUSH_BIT);        
  3822.         if ( spawnArgs.GetBool ( key ) ) {
  3823.             combat.tacticalMaskAvailable |= AITACTICAL_MOVE_PLAYERPUSH_BIT;
  3824.         }
  3825.     } else if ( !idStr::Icmp ( key, "noLook" ) ) {
  3826.         aifl.disableLook = spawnArgs.GetBool ( key );
  3827.     } else if ( !idStr::Icmp ( key, "killer_guard" ) ) {
  3828.         aifl.killerGuard = spawnArgs.GetBool ( key );
  3829.     }
  3830. }
  3831.  
  3832. /*
  3833. ============
  3834. idAI::OnCoverInvalidated
  3835. ============
  3836. */
  3837. void idAI::OnCoverInvalidated ( void ) {
  3838.     // Force a tactical update now
  3839.     ForceTacticalUpdate ( );
  3840. }
  3841.  
  3842. /*
  3843. ============
  3844. idAI::OnCoverNotFacingEnemy
  3845. ============
  3846. */
  3847. void idAI::OnCoverNotFacingEnemy ( void ) {
  3848.     // Clear attack timers so we can shoot right now
  3849.     actionTimerRangedAttack.Clear ( actionTime );
  3850.     actionRangedAttack.timer.Clear( actionTime );
  3851. }
  3852.  
  3853. /*
  3854. ============
  3855. idAI::SkipCurrentDestination
  3856.  
  3857. Is the AI's current destination ok enough to stay at?
  3858. ============
  3859. */
  3860. bool idAI::SkipCurrentDestination ( void ) const {
  3861.     // can only skip current destination when we are stopped
  3862.     if ( move.fl.moving ) {
  3863.         return false;
  3864.     }
  3865. /*
  3866.     // If we are currently behind cover and that cover is no longer valid we should skip it
  3867.     if ( IsBehindCover ( ) && !IsCoverValid ( ) ) {
  3868.         return true;
  3869.     }
  3870. */
  3871.     return false;
  3872. }
  3873.  
  3874. /*
  3875. ============
  3876. idAI::SkipImpulse
  3877. ============
  3878. */
  3879. bool idAI::SkipImpulse( idEntity *ent, int id ){
  3880.     bool skip = idActor::SkipImpulse( ent, id );
  3881.  
  3882.     if( af.IsActive ( ) ) {
  3883.         return false;
  3884.     }
  3885.     if( !fl.takedamage ){
  3886.         return true;
  3887.     }
  3888.     if( move.moveCommand == MOVE_RV_PLAYBACK ){
  3889.         return true;
  3890.     }    
  3891.     
  3892.     return skip;
  3893. }
  3894.  
  3895. /*
  3896. ============
  3897. idAI::CanHitEnemy
  3898. ============
  3899. */
  3900. bool idAI::CanHitEnemy ( void ) {
  3901.     trace_t    tr;
  3902.     idEntity *hit;
  3903.  
  3904.     idEntity *enemyEnt = enemy.ent;
  3905.     if ( !IsEnemyVisible ( ) || !enemyEnt ) {
  3906.         return false;
  3907.     }
  3908.  
  3909.     // don't check twice per frame
  3910.     if ( gameLocal.time == lastHitCheckTime ) {
  3911.         return lastHitCheckResult;
  3912.     }
  3913.  
  3914.     lastHitCheckTime = gameLocal.time;
  3915.  
  3916.     idVec3 toPos = enemyEnt->GetEyePosition();
  3917.     idVec3 eye = GetEyePosition();
  3918.     idVec3 dir;
  3919.  
  3920.     // expand the ray out as far as possible so we can detect anything behind the enemy
  3921.     dir = toPos - eye;
  3922.     dir.Normalize();
  3923.     toPos = eye + dir * MAX_WORLD_SIZE;
  3924. // RAVEN BEGIN
  3925. // ddynerman: multiple clip worlds
  3926.     if ( g_perfTest_aiNoVisTrace.GetBool() ) {
  3927.         lastHitCheckResult = true;
  3928.         return lastHitCheckResult;
  3929.     }
  3930.  
  3931.     gameLocal.TracePoint( this, tr, eye, toPos, MASK_SHOT_BOUNDINGBOX, this );
  3932.     hit = gameLocal.GetTraceEntity( tr );
  3933. // RAVEN END
  3934.     if ( tr.fraction >= 1.0f || ( hit == enemyEnt ) ) {
  3935.         lastHitCheckResult = true;
  3936.     } else if ( ( tr.fraction < 1.0f ) && ( hit->IsType( idAI::Type ) ) && 
  3937.         ( static_cast<idAI *>( hit )->team != team ) ) {
  3938.         lastHitCheckResult = true;
  3939.     } else {
  3940.         lastHitCheckResult = false;
  3941.     }
  3942.  
  3943.     return lastHitCheckResult;
  3944. }
  3945.  
  3946. /*
  3947. ============
  3948. idAI::CanHitEnemyFromJoint
  3949. ============
  3950. */
  3951.  
  3952. bool idAI::CanHitEnemyFromJoint( const char *jointname ){
  3953.     trace_t    tr;
  3954.     idVec3    muzzle;
  3955.     idMat3    axis;
  3956.  
  3957.     idEntity *enemyEnt = enemy.ent;
  3958.     if ( !IsEnemyVisible ( ) || !enemyEnt ) {
  3959.         return false;
  3960.     }
  3961.  
  3962.     // don't check twice per frame
  3963.     if ( gameLocal.time == lastHitCheckTime ) {
  3964.         return lastHitCheckResult;
  3965.     }
  3966.  
  3967.     if ( g_perfTest_aiNoVisTrace.GetBool() ) {
  3968.         lastHitCheckResult = true;
  3969.         return true;
  3970.     }
  3971.  
  3972.     lastHitCheckTime = gameLocal.time;
  3973.  
  3974.     idVec3 toPos = enemyEnt->GetEyePosition();
  3975.     jointHandle_t joint = animator.GetJointHandle( jointname );
  3976.     if ( joint == INVALID_JOINT ) {
  3977.         gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
  3978.     }
  3979.     animator.GetJointTransform( joint, gameLocal.time, muzzle, axis );
  3980.     muzzle = physicsObj.GetOrigin() + ( muzzle + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
  3981. // RAVEN BEGIN
  3982. // ddynerman: multiple clip worlds
  3983.     gameLocal.TracePoint( this, tr, muzzle, toPos, MASK_SHOT_BOUNDINGBOX, this );
  3984. // RAVEN END
  3985.     if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == enemyEnt ) ) {
  3986.         lastHitCheckResult = true;
  3987.     } else {
  3988.         lastHitCheckResult = false;
  3989.     }
  3990.  
  3991.     return lastHitCheckResult;
  3992. }
  3993.  
  3994. /*
  3995. ============
  3996. idAI::
  3997. ============
  3998.  
  3999. float HeightForTrajectory( const idVec3 &start, float zVel, float gravity );
  4000.  
  4001. int idAI::TestTrajectory( const idVec3 &firePos, const idVec3 &target, const char *projectileName ){
  4002.     idVec3    projVelocity;
  4003.     idVec3    projGravity;
  4004.     float    testTime;
  4005.     float    zVel, height, pitch, s, c;
  4006.     idVec3    dir;
  4007.     float    newVel;
  4008.     float    delta_x;
  4009.     float    delta_z;
  4010.  
  4011.     projectileDef    = gameLocal.FindEntityDefDict ( projectileName );    
  4012.     projVelocity    = idProjectile::GetVelocity( projectileDef );
  4013.     projGravity        = idProjectile::GetGravity( projectileDef ).z * GetPhysics()->GetGravity();
  4014.     pitch            = DEG2RAD( gameLocal.random.RandomFloat() * 50 + 20 );    // Random pitch range between 20 and 70.  Should this be customizeable?
  4015.  
  4016.     idMath::SinCos( pitch, s, c );
  4017.  
  4018.     delta_x            = idMath::Sqrt( ( target.x - firePos.x ) * ( target.x - firePos.x ) + ( target.y - firePos.y ) * ( target.y - firePos.y ) );
  4019.     delta_z            = target.z - firePos.z;
  4020.     newVel            = ( delta_x / idMath::Cos( pitch ) ) * idMath::Sqrt( projGravity.z / ( 2.0f * ( delta_x * idMath::Tan( pitch ) - delta_z ) ) );
  4021.     testTime        = delta_x / ( newVel * c );
  4022.     zVel            = newVel * s;
  4023.  
  4024.     float a = idMath::ASin ( delta_x * GetPhysics()->GetGravity().Length() / (projVelocity.x * projVelocity.x) );
  4025.     a = a / 2;
  4026.  
  4027.     float r = (projVelocity.x * projVelocity.x) * idMath::Sin ( 2 * a ) / GetPhysics()->GetGravity().Length();
  4028.     if ( r < delta_x - (delta_x * 0.1) ) {
  4029.         mVar.valid_lobbed_shot = 0;
  4030.         return 0;
  4031.     } else {
  4032.         mVar.lobDir = target - firePos;
  4033.         mVar.lobDir.z = 0;
  4034.         mVar.lobDir.z = idMath::Tan ( a ) * mVar.lobDir.LengthFast();
  4035.         mVar.lobDir.Normalize ( );
  4036.         mVar.valid_lobbed_shot = gameLocal.time;
  4037.         mVar.lob_vel_scale = 1.0f; // newVel / projVelocity.Length();
  4038.         return 1;
  4039.     }
  4040.  
  4041.     projGravity[2] *= -1.0f;
  4042.  
  4043.     dir                = target - firePos;
  4044.     dir.z            = 0;
  4045.     dir.Normalize();
  4046.     delta_x            = idMath::Sqrt( 1 - ( c * c ) );
  4047.     dir.x            *= delta_x;
  4048.     dir.y            *= delta_x;
  4049.     dir.z            = c;
  4050.  
  4051.     height = HeightForTrajectory( firePos, zVel, projGravity[2] ) - firePos.z;
  4052.     if ( height > MAX_WORLD_SIZE ) {
  4053.         // goes higher than we want to allow
  4054.         mVar.valid_lobbed_shot = 0;
  4055.         return 0;
  4056.     }
  4057.  
  4058.     if ( idAI::TestTrajectory ( firePos, target, zVel, projGravity[2], testTime, height * 2, NULL, 
  4059.                                 MASK_SHOT_RENDERMODEL, this, enemy.GetEntity(), ai_debugTrajectory.GetBool() ? 4000 : 0 ) ) {
  4060.  
  4061.         if ( ai_debugTrajectory.GetBool() ) {
  4062.             float t = testTime / 100.0f;
  4063.             idVec3 velocity = dir * newVel;
  4064.             idVec3 lastPos, pos;
  4065.             lastPos = firePos;
  4066.             pos = firePos;
  4067.             for ( int j = 1; j < 100; j++ ) {
  4068.                 pos += velocity * t;
  4069.                 velocity += projGravity * t;
  4070.                 gameRenderWorld->DebugLine( colorCyan, lastPos, pos, ai_debugTrajectory.GetBool() ? 4000 : 0 );
  4071.                 lastPos = pos;
  4072.             }
  4073.         }
  4074.  
  4075.         mVar.lobDir = dir;
  4076.         mVar.valid_lobbed_shot = gameLocal.time;
  4077.         mVar.lob_vel_scale = newVel / projVelocity.Length();
  4078.     }else{
  4079.         mVar.valid_lobbed_shot = 0;
  4080.     }
  4081.  
  4082.     return ( (int)mVar.valid_lobbed_shot != 0 );
  4083.  
  4084. }
  4085. */
  4086.  
  4087. /*
  4088. ============
  4089. idAI::
  4090. ============
  4091. */
  4092.  
  4093. float idAI::GetTurnDelta( void ){
  4094.     float amount;
  4095.  
  4096.     if ( move.turnRate ) {
  4097.         amount = idMath::AngleNormalize180( move.ideal_yaw - move.current_yaw );
  4098.         return amount;
  4099.     } else {
  4100.         return 0.0f;
  4101.     }
  4102. }
  4103.  
  4104. /*
  4105. ============
  4106. idAI::GetIdleAnimName
  4107. ============
  4108. */
  4109. const char* idAI::GetIdleAnimName ( void ) {
  4110.     const char* animName = NULL;
  4111.     
  4112.     // Start idle animation
  4113.     if ( enemy.ent ) {
  4114.         animName = "idle_alert";
  4115.     }
  4116.     
  4117.     if ( animName && HasAnim ( ANIMCHANNEL_ALL, animName ) ) {
  4118.         return animName;
  4119.     }
  4120.     
  4121.     return "idle";
  4122. }
  4123.  
  4124. /*
  4125. ===============================================================================
  4126.  
  4127.     idAI - Enemy Finding
  4128.  
  4129. ===============================================================================
  4130. */
  4131.  
  4132. /*
  4133. ============
  4134. idAI::FindEnemy
  4135. ============
  4136. */
  4137. idEntity *idAI::FindEnemy ( bool inFov, bool forceNearest, float maxDistSqr ){
  4138.     idActor*    actor;
  4139.     idActor*    bestEnemy;
  4140.     idActor*    bestEnemyBackup;
  4141.     float        bestThreat;
  4142.     float        bestThreatBackup;
  4143.     float        distSqr;
  4144.     float        enemyRangeSqr;
  4145.     float        awareRangeSqr;
  4146.     idVec3        origin;
  4147.     idVec3        delta;
  4148.     pvsHandle_t pvs;
  4149.     
  4150.     // Setup our local variables used in the search
  4151.     pvs                 = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
  4152.     bestThreat         = 0.0f;
  4153.     bestThreatBackup = 0.0f;
  4154.     bestEnemy         = NULL;
  4155.     bestEnemyBackup     = NULL;
  4156.     awareRangeSqr     = Square ( combat.awareRange );
  4157.     enemyRangeSqr     = enemy.ent ? Square ( enemy.range ) : Square ( combat.attackRange[1] );
  4158.     origin             = GetEyePosition ( );
  4159.  
  4160.     // Iterate through the enemy team
  4161.     for( actor = aiManager.GetEnemyTeam ( (aiTeam_t)team ); actor; actor = actor->teamNode.Next() ) {
  4162.         // Skip hidden enemies and enemies that cant be targeted
  4163.         if( actor->fl.notarget || actor->fl.isDormant || ( actor->IsHidden ( ) && !actor->IsInVehicle() ) ) {
  4164.             continue;
  4165.         }
  4166.  
  4167.         // Calculate the distance between ourselves and our potential enemy
  4168.         delta   = physicsObj.GetOrigin() - actor->GetPhysics()->GetOrigin();
  4169.         distSqr = delta.LengthSqr();
  4170.  
  4171.         // Calculate the adjusted threat for this actor        
  4172.         float threat = CalculateEnemyThreat ( actor );
  4173.  
  4174.         // Save the highest threat enemy as a backup in case we cannot find one we can see
  4175.         if ( threat > bestThreatBackup ) {
  4176.             bestThreatBackup = threat;
  4177.             bestEnemyBackup  = actor;
  4178.         }
  4179.  
  4180.         // If we have already found a more threatening enemy then attack that
  4181.         if ( threat < bestThreat ) {
  4182.             continue;
  4183.         }
  4184.         
  4185.         // If this enemy isnt in the same pvps then use them as a backup
  4186.         if ( !gameLocal.pvs.InCurrentPVS( pvs, actor->GetPVSAreas(), actor->GetNumPVSAreas() ) ) {
  4187.             continue;
  4188.         }
  4189.  
  4190.         // fov doesn't matter if they're within awareRange, we "sense" them if we're alert... (or should LOS not even matter at this point?)
  4191.         if ( distSqr < awareRangeSqr || CanSeeFrom ( origin, actor, (inFov && !(combat.fl.aware&&distSqr<awareRangeSqr)) ) ) {
  4192.             bestThreat = threat;
  4193.             bestEnemy  = actor;
  4194.         }
  4195.     }
  4196.  
  4197.     // If force nearest is set we will give them an enemy reguardless of distance or sight
  4198.     if( forceNearest ){
  4199.         if( bestEnemy == NULL ){
  4200.             bestEnemy = bestEnemyBackup;
  4201.         }
  4202.     }
  4203.  
  4204.     gameLocal.pvs.FreeCurrentPVS( pvs );
  4205.         
  4206.     return bestEnemy;
  4207. }
  4208.  
  4209. /*
  4210. ============
  4211. idAI::CheckForEnemy
  4212.  
  4213. Look for a suitable enemy
  4214. ============
  4215. */
  4216. bool idAI::CheckForEnemy ( bool useFov, bool force ) {
  4217.     idEntity *newEnemy;
  4218.  
  4219.     if ( combat.fl.ignoreEnemies ) {
  4220.         return false;
  4221.     }
  4222.  
  4223.     // If we already have an enemy and arent being forced to find a new on then just return now
  4224.     if ( enemy.ent && !force ) {
  4225.         return true;
  4226.     }
  4227.  
  4228.     // Save last time we checked for a new enemy
  4229.     enemy.checkTime = gameLocal.time;
  4230.  
  4231.     // Dont use fov check when behind cover because you are up against a wall
  4232.     newEnemy = FindEnemy ( !IsBehindCover ( ), 0, 0.0f );
  4233.      
  4234.      // Havent found an enemy yet, see if we heard something that can be our enemy
  4235.     if ( !newEnemy ) {
  4236.         newEnemy = HeardSound( true );
  4237.     }
  4238.  
  4239.     // If we still havent found an enemy, see if a teammate can give us one    
  4240.     if ( !newEnemy ) {
  4241.         return CheckForTeammateEnemy ( );
  4242.     }
  4243.  
  4244.     SetEnemy( newEnemy );
  4245.     return true;
  4246. }
  4247.  
  4248. /*
  4249. ============
  4250. idAI::CheckForTeammateEnemy
  4251. ============
  4252. */
  4253. bool idAI::CheckForTeammateEnemy( void ) {
  4254.     idActor*    teammate;
  4255.     idEntity*    teammateEnemy;
  4256.  
  4257.     // Not looking for a new enemy.
  4258.     if ( combat.fl.ignoreEnemies ) {
  4259.         return false;
  4260.     }
  4261.     
  4262.     // Find an enemy from a nearby ally
  4263.     teammateEnemy = aiManager.NearestTeammateEnemy ( this, 1000.0f, false, false, &teammate );
  4264.     if ( !teammateEnemy || teammateEnemy == enemy.ent ) {
  4265.         return false;
  4266.     }
  4267.     
  4268.     assert ( teammate );
  4269.     
  4270.     // Attempt to set the new enemy
  4271.     if ( !SetEnemy ( teammateEnemy ) ) {
  4272.         return false;
  4273.     }
  4274.     
  4275.     // If the ally is another AI entity we can use their enemy visibility information
  4276.     if ( teammate->IsType ( idAI::Type ) ) {
  4277.         idAI* teammateAI = static_cast<idAI*>(teammate);
  4278.         enemy.smoothedLinearVelocity        = teammateAI->enemy.smoothedLinearVelocity;
  4279.         enemy.smoothedPushedVelocity        = teammateAI->enemy.smoothedPushedVelocity;
  4280.         enemy.lastKnownPosition                = teammateAI->enemy.lastKnownPosition;
  4281.         enemy.lastVisibleEyePosition        = teammateAI->enemy.lastVisibleEyePosition;
  4282.         enemy.lastVisibleFromEyePosition    = teammateAI->enemy.lastVisibleEyePosition;
  4283.         enemy.lastVisibleChestPosition        = teammateAI->enemy.lastVisibleChestPosition;
  4284.         enemy.lastVisibleTime                = 0;
  4285.     }
  4286.     
  4287.     return true;
  4288. }
  4289.  
  4290. /*
  4291. ============
  4292. idAI::CheckForCloserEnemy
  4293. ============
  4294. */
  4295. bool idAI::CheckForCloserEnemy ( void ) {
  4296.     idEntity*    newEnemy = NULL;
  4297.     float        maxDistSqr;
  4298.  
  4299.     // Not looking for a new enemy.
  4300.     if ( combat.fl.ignoreEnemies ) {
  4301.         return false;
  4302.     }
  4303.  
  4304.     // See if we happen to have heard someone this frame that we can use 
  4305.     newEnemy = HeardSound( true );
  4306.     if ( newEnemy && newEnemy != enemy.ent && newEnemy->IsType( idActor::GetClassType() ) ) {
  4307.         //heard someone else!
  4308.         float newDist = DistanceTo ( newEnemy->GetPhysics()->GetOrigin() );
  4309.         
  4310.         // Are they closer than the enemy we are fighting?
  4311.         if ( newDist < enemy.range ) {
  4312.             //new enemy is closer than current one, take them!
  4313.             SetEnemy( newEnemy );
  4314.             return true;
  4315.         }
  4316.     }
  4317.  
  4318.     if ( GetEnemy() && enemy.range ) {
  4319.         maxDistSqr = Min( Square ( enemy.range ), Square ( combat.awareRange ) );
  4320.     } else {
  4321.         maxDistSqr = Square ( combat.awareRange );
  4322.     }
  4323.  
  4324.     newEnemy = FindEnemy( false, 0, maxDistSqr );
  4325.  
  4326.     if ( !newEnemy ) {
  4327.         return false;
  4328.     }
  4329.  
  4330.     SetEnemy( newEnemy );
  4331.     return true;
  4332. }
  4333.  
  4334. /*
  4335. ============
  4336. idAI::CheckForReplaceEnemy
  4337.  
  4338. TODO: Call CalculateThreat ( ent ) and compare to current entity
  4339. ============
  4340. */
  4341. bool idAI::CheckForReplaceEnemy ( idEntity* replacement ) {
  4342.     bool replace;
  4343.  
  4344.     // If our replacement is a driver a vehicle and they are hidden we will 
  4345.     // want to shoot back at their vehicle not them.
  4346.     idActor* actor;
  4347.     actor = dynamic_cast<idActor*>(replacement);
  4348.     if ( actor && actor->IsInVehicle ( ) && actor->IsHidden ( ) ) {
  4349.         replacement = actor->GetVehicleController ( ).GetVehicle ( );
  4350.     }
  4351.  
  4352.     // Invalid replacement?
  4353.     if ( !replacement) {
  4354.         return false;
  4355.     }
  4356.  
  4357.     // Not looking for a new enemy.
  4358.     if ( combat.fl.ignoreEnemies ) {
  4359.         return false;
  4360.     }
  4361.  
  4362.     if ( replacement == enemy.ent ) {
  4363.         return false;
  4364.     }
  4365.      
  4366.     // Dont want to set our enemy to a friendly target
  4367.     if ( replacement->IsType( idActor::GetClassType() ) && (static_cast<idActor*>(replacement))->team == team ) {
  4368.         return false;
  4369.     }
  4370.     
  4371.     // Not having an enemy will set it immediately
  4372.     if ( !enemy.ent ) {
  4373.         SetEnemy ( replacement );
  4374.         return true;
  4375.     }
  4376.  
  4377.     // Dont change enemies too often when being hit
  4378.     if ( gameLocal.time - enemy.changeTime < 1000 ) {
  4379.         return false;
  4380.     }
  4381.  
  4382.     replace = false;
  4383.  
  4384.     // If new enemy is more threatening then replace it reguardless if we can see it
  4385.     if ( CalculateEnemyThreat ( replacement ) > CalculateEnemyThreat ( enemy.ent ) ) {
  4386.         replace = true;
  4387.     // Replace our enemy if we havent seen ours in a bit
  4388.     } else if ( !IsEnemyRecentlyVisible ( 0.25f ) ) {
  4389.         replace = true;
  4390.     }
  4391.     
  4392.     // Replace enemy?
  4393.     if ( replace ) {
  4394.         SetEnemy ( replacement );
  4395.     }
  4396.     
  4397.     return replace;
  4398. }
  4399.  
  4400. /*
  4401. ============
  4402. idAI::UpdateThreat
  4403. ============
  4404. */
  4405. void idAI::UpdateThreat ( void ) {
  4406.     // Start threat at base threat level
  4407.     combat.threatCurrent = combat.threatBase;
  4408.     
  4409.     // Adjust threat using current tactical state
  4410.     switch ( combat.tacticalCurrent ) {
  4411.         case AITACTICAL_HIDE:    combat.threatCurrent *= 0.5f;    break;
  4412.         case AITACTICAL_MELEE:    combat.threatCurrent *= 2.0f;    break;
  4413.     }        
  4414.     
  4415.     // Signifigantly reduced threat when in undying mode
  4416.     if ( aifl.undying ) {
  4417.         combat.threatCurrent *= 0.25f;
  4418.     }
  4419. }
  4420.  
  4421. /*
  4422. ============
  4423. idAI::CalculateEnemyThreat
  4424. ============
  4425. */
  4426. float idAI::CalculateEnemyThreat ( idEntity* enemyEnt ) {
  4427.     // Calculate the adjusted threat for this actor        
  4428.     float threat = 1.0f;
  4429.     if ( enemyEnt->IsType ( idAI::GetClassType ( ) ) ) {
  4430.         idAI* enemyAI = static_cast<idAI*>(enemyEnt);
  4431.         threat = enemyAI->combat.threatCurrent;
  4432.         
  4433.         // Increase threat for enemies that are targetting us
  4434.         if ( enemyAI->enemy.ent == this && enemyAI->combat.tacticalCurrent == AITACTICAL_MELEE ) {
  4435.             threat *= 2.0f;
  4436.         }
  4437.     } else if ( enemyEnt->IsType ( idPlayer::GetClassType ( ) ) ) {
  4438.         threat = 2.0f; 
  4439.     } else {
  4440.         threat = 1.0f;
  4441.     }                
  4442.  
  4443.     float enemyRangeSqr;
  4444.     float distSqr;    
  4445.     
  4446.     enemyRangeSqr = (enemy.ent) ? Square ( enemy.range ) : Square ( combat.attackRange[1] );
  4447.     distSqr          = (physicsObj.GetOrigin ( ) - enemyEnt->GetPhysics()->GetOrigin ( )).LengthSqr ( );
  4448.     
  4449.     if ( distSqr > 0 ) {
  4450.         return threat * (enemyRangeSqr / distSqr);
  4451.     }
  4452.     
  4453.     return threat;
  4454. }
  4455.  
  4456. /*
  4457. ============
  4458. idAI::CheckBlink
  4459. ============
  4460. */
  4461. void idAI::CheckBlink ( void ) {
  4462. //    if ( IsSpeaking ( ) ) {
  4463. //        return;
  4464. //    }
  4465.     idActor::CheckBlink ( );
  4466. }
  4467.  
  4468. /*
  4469. ============
  4470. idAI::Speak
  4471. ============
  4472. */
  4473. bool idAI::Speak( const char *lipsync, bool random ){
  4474.     assert( idStr::Icmpn( lipsync, "lipsync_", 7 ) == 0 );
  4475.     
  4476.     if ( random ) {
  4477.         // If there is no lipsync then skip it
  4478.         if ( spawnArgs.MatchPrefix ( lipsync ) ) {
  4479.             lipsync = spawnArgs.RandomPrefix ( lipsync, gameLocal.random );
  4480.         } else { 
  4481.             lipsync = NULL;
  4482.         }
  4483.     } else {
  4484.         lipsync = spawnArgs.GetString ( lipsync );
  4485.     }
  4486.     
  4487.     if ( !lipsync || !*lipsync ) {
  4488.         return false;
  4489.     }
  4490.     
  4491.     if ( head ) {
  4492.         speakTime = head->StartLipSyncing( lipsync );
  4493.     } else {
  4494.         speakTime = 0;        
  4495.         StartSoundShader (declManager->FindSound ( lipsync ), SND_CHANNEL_VOICE, SSF_IS_VO, false, &speakTime );
  4496.     }
  4497.  
  4498.     speakTime += gameLocal.time;
  4499.     return true;    
  4500. }
  4501.  
  4502. /*
  4503. ============
  4504. idAI::StopSpeaking
  4505. ============
  4506. */
  4507. void idAI::StopSpeaking( bool stopAnims ){
  4508.     speakTime = 0;
  4509.     StopSound( SND_CHANNEL_VOICE, false );
  4510.     if ( head.GetEntity() ) {
  4511.         head.GetEntity()->StopSound( SND_CHANNEL_VOICE, false );
  4512.         if ( stopAnims ) {
  4513.             head.GetEntity()->GetAnimator()->ClearAllAnims( gameLocal.time, 100 );
  4514.         }
  4515.     }
  4516. }
  4517.  
  4518. /*
  4519. ============
  4520. idAI::CanHitEnemyFromAnim
  4521. ============
  4522. */
  4523. bool idAI::CanHitEnemyFromAnim( int animNum, idVec3 offset ) {
  4524.     idVec3        dir;
  4525.     idVec3        local_dir;
  4526.     idVec3        fromPos;
  4527.     idMat3        axis;
  4528.     idVec3        start;
  4529.     trace_t        tr;
  4530.     idEntity*    enemyEnt;
  4531.  
  4532.     // Need an enemy.
  4533.     if ( !enemy.ent ) {
  4534.         return false;
  4535.     }
  4536.  
  4537.     // Enemy actor pointer
  4538.     enemyEnt = static_cast<idEntity*>(enemy.ent.GetEntity());
  4539.  
  4540.     // just do a ray test if close enough
  4541.     if ( enemyEnt->GetPhysics()->GetAbsBounds().IntersectsBounds( physicsObj.GetAbsBounds().Expand( 16.0f ) ) ) {
  4542.         return CanHitEnemy();
  4543.     }
  4544.  
  4545.     // calculate the world transform of the launch position
  4546.       idVec3 org = physicsObj.GetOrigin()+offset;
  4547.     idVec3 from;
  4548.       dir = enemy.lastVisibleChestPosition - org;
  4549.       physicsObj.GetGravityAxis().ProjectVector( dir, local_dir );
  4550.       local_dir.z = 0.0f;
  4551.       local_dir.ToVec2().Normalize();
  4552.       axis = local_dir.ToMat3();
  4553.       from = org + attackAnimInfo[ animNum ].attackOffset * axis;
  4554.  
  4555. /*
  4556.     if( DebugFilter(ai_debugTactical) ) {
  4557.         gameRenderWorld->DebugLine ( colorYellow, org + attackAnimInfo[ animNum ].eyeOffset * viewAxis, from, 5000 );
  4558.         gameRenderWorld->DebugLine ( colorOrange, from, enemy.lastVisibleEyePosition, 5000 );
  4559.     }
  4560. */
  4561.  
  4562.     // If the point we are shooting from is within our bounds then we are good to go, otherwise make sure its not in a wall
  4563.     const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  4564.     if ( !ownerBounds.ContainsPoint ( from ) ) {
  4565.         trace_t tr;
  4566.         if ( !g_perfTest_aiNoVisTrace.GetBool() ) {
  4567.             gameLocal.TracePoint( this, tr, org + attackAnimInfo[ animNum ].eyeOffset * axis, from, MASK_SHOT_BOUNDINGBOX, this );
  4568.             if ( tr.fraction < 1.0f ) {
  4569.                 return false;
  4570.             }
  4571.         }
  4572.     }        
  4573.  
  4574.     return CanSeeFrom ( from, enemy.lastVisibleEyePosition, true );
  4575. }
  4576.  
  4577. /*
  4578. ================
  4579. idAI::ScriptedBegin
  4580. ================
  4581. */
  4582. bool idAI::ScriptedBegin ( bool endWithIdle, bool allowDormant ) {
  4583.     if ( aifl.dead ) {
  4584.         return false;
  4585.     }
  4586.     
  4587.     // Wakeup if not awake already
  4588.     WakeUp ( );
  4589.         
  4590.     aifl.scriptedEndWithIdle = endWithIdle;
  4591.     aifl.scripted             = true;
  4592. //    combat.fl.aware             = false;
  4593.     combat.tacticalCurrent     = AITACTICAL_NONE;
  4594.  
  4595.     // Make sure the entity never goes dormant during a scripted event or
  4596.     // the event may never end.
  4597.     aifl.scriptedNeverDormant = !allowDormant;
  4598.     dormantStart = 0;
  4599.  
  4600. /*        
  4601.     // actors will ignore enemies during scripted events
  4602.     ClearEnemy ( );
  4603. */
  4604.     
  4605.     // Cancel any current movement
  4606.     StopMove ( MOVE_STATUS_DONE );
  4607.     
  4608.     move.fl.allowAnimMove = true;
  4609.     
  4610.     return true;
  4611. }
  4612.  
  4613. /*
  4614. ================
  4615. idAI::ScriptedEnd
  4616. ================
  4617. */
  4618. void idAI::ScriptedEnd ( void ) {
  4619.     dormantStart = 0;
  4620.     aifl.scripted = false;
  4621. }
  4622.  
  4623. /*
  4624. ================
  4625. idAI::ScriptedStop
  4626. ================
  4627. */
  4628. void idAI::ScriptedStop ( void ) {
  4629.     if ( !aifl.scripted ) {
  4630.         return;
  4631.     }
  4632.     aifl.scriptedEndWithIdle = true;
  4633.     aifl.scripted             = false;
  4634.     StopMove( MOVE_STATUS_DONE );
  4635. }
  4636.  
  4637. /*
  4638. ================
  4639. idAI::ScriptedMove
  4640. ================
  4641. */
  4642. void idAI::ScriptedMove ( idEntity* destEnt, float minDist, bool endWithIdle ) {
  4643.     if ( !ScriptedBegin ( endWithIdle ) ) {
  4644.         return;
  4645.     }
  4646.  
  4647.     //disable all temporary blocked reachabilities due to teammate obstacle avoidance
  4648.     aiManager.UnMarkAllReachBlocked();
  4649.     //attempt the move - NOTE: this *can* fail if there's no route or AAS obstacles are in the way!
  4650.     MoveToEntity ( destEnt, minDist );
  4651.     //re-enable all temporary blocked reachabilities due to teammate obstacle avoidance
  4652.     aiManager.ReMarkAllReachBlocked();
  4653.  
  4654.     // Move the torso to the idle state if its not already there
  4655.     SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); 
  4656.  
  4657.     // Move the legs into the idle state so he will start moving
  4658.     SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); 
  4659.  
  4660.     // Set up state loop for moving
  4661.     SetState ( "State_ScriptedMove" );
  4662.     PostState ( "State_ScriptedStop" );
  4663. }
  4664.  
  4665. /*
  4666. ================
  4667. idAI::ScriptedFace
  4668. ================
  4669. */
  4670. void idAI::ScriptedFace ( idEntity* faceEnt, bool endWithIdle ) {
  4671.     if ( !ScriptedBegin ( endWithIdle ) ) {
  4672.         return;
  4673.     }
  4674.  
  4675.     // Force idle while facing
  4676.     SetAnimState ( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); 
  4677.     SetAnimState ( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); 
  4678.  
  4679.     // Start facing the entity
  4680.     FaceEntity ( faceEnt );
  4681.  
  4682.     SetState ( "State_ScriptedFace" );
  4683.     PostState ( "State_ScriptedStop" );
  4684. }
  4685.  
  4686. /*
  4687. ================
  4688. idAI::ScriptedAnim
  4689.  
  4690. Plays an the given animation in an un-interruptable state.  If looping will continue indefinately until
  4691. another operation which will stop a scripted sequence is called. When done can optionally return the character
  4692. back to their normal processing if endWithIdle is set to true.
  4693. ================
  4694. */
  4695. void idAI::ScriptedAnim ( const char* animname, int blendFrames, bool loop, bool endWithIdle ) {
  4696.     // Start the scripted sequence
  4697.     if ( !ScriptedBegin ( endWithIdle, true ) ) {
  4698.         return;
  4699.     }
  4700.     
  4701.     TurnToward ( move.current_yaw );
  4702.  
  4703.     if ( loop ) {
  4704.         // Loop the given animation
  4705.         PlayCycle ( ANIMCHANNEL_TORSO, animname, blendFrames );        
  4706.     } else {
  4707.         // Play the given animation
  4708.         PlayAnim ( ANIMCHANNEL_TORSO, animname, blendFrames );
  4709.     }
  4710.     
  4711.     SetAnimState ( ANIMCHANNEL_LEGS, "Wait_Frame" );
  4712.     SetAnimState ( ANIMCHANNEL_TORSO, "Torso_ScriptedAnim" );
  4713.  
  4714.     DisableAnimState ( ANIMCHANNEL_LEGS );
  4715.  
  4716.     SetState ( "Wait_ScriptedDone" );
  4717.     PostState ( "State_ScriptedStop" );
  4718. }
  4719.  
  4720.  
  4721. /*
  4722. ============
  4723. idAI::ScriptedPlaybackAim
  4724. ============
  4725. */
  4726. void idAI::ScriptedPlaybackAim ( const char* playback, int flags, int numFrames ) {
  4727.     // Start the scripted sequence
  4728.     if ( !ScriptedBegin ( false ) ) {
  4729.         return;
  4730.     }
  4731.  
  4732.     mLookPlayback.Start( spawnArgs.GetString ( playback ), this, flags, numFrames );
  4733.  
  4734.     // Wait till its done and mark it finished
  4735.     SetState ( "State_ScriptedPlaybackAim" );
  4736.     PostState ( "State_ScriptedStop" );
  4737. }        
  4738.  
  4739. /*
  4740. ============
  4741. idAI::ScriptedAction
  4742. ============
  4743. */
  4744. void idAI::ScriptedAction ( idEntity* actionEnt, bool endWithIdle ) {
  4745.     const char* actionName;
  4746.     
  4747.     if ( !actionEnt ) {
  4748.         return;
  4749.     }
  4750.     
  4751.     // Get the action name
  4752.     actionName = actionEnt->spawnArgs.GetString ( "action" );
  4753.     if ( !*actionName ) {
  4754.         gameLocal.Error ( "missing action keyword on scripted action entity '%s' for ai '%s'",
  4755.                           actionEnt->GetName(),
  4756.                           GetName() );
  4757.         return;
  4758.     }
  4759.  
  4760.     // Start the scripted sequence
  4761.     if ( !ScriptedBegin ( endWithIdle ) ) {
  4762.         return;
  4763.     }
  4764.  
  4765.     scriptedActionEnt = actionEnt;
  4766.     
  4767.     SetState ( "State_ScriptedStop" );
  4768.     PerformAction ( va("TorsoAction_%s", actionName ), 4, true );
  4769. }
  4770.  
  4771. /*
  4772. ============
  4773. idAI::FootStep
  4774. ============
  4775. */
  4776. void idAI::FootStep ( void ) {
  4777.     idActor::FootStep ( );
  4778.     
  4779.     ExecScriptFunction( funcs.footstep );    
  4780. }
  4781.  
  4782. /*
  4783. ============
  4784. idAI::SetScript
  4785. ============
  4786. */
  4787. void idAI::SetScript( const char* scriptName, const char* funcName ) {
  4788.     if ( !funcName || !funcName[0] ) {
  4789.         return;
  4790.     } 
  4791.  
  4792.     // Set the associated script
  4793.     if ( !idStr::Icmp ( scriptName, "first_sight" ) ) {
  4794.         funcs.first_sight.Init( funcName );
  4795.     } else if ( !idStr::Icmp ( scriptName, "sight" ) ) {
  4796.         funcs.sight.Init( funcName );
  4797.     } else if ( !idStr::Icmp ( scriptName, "pain" ) ) {
  4798.         funcs.pain.Init( funcName );
  4799.     } else if ( !idStr::Icmp ( scriptName, "damage" ) ) {
  4800.         funcs.damage.Init( funcName );
  4801.     } else if ( !idStr::Icmp ( scriptName, "death" ) ) {
  4802.         funcs.death.Init( funcName );
  4803.     } else if ( !idStr::Icmp ( scriptName, "attack" ) ) {
  4804.         funcs.attack.Init( funcName );
  4805.     } else if ( !idStr::Icmp ( scriptName, "init" ) ) {
  4806.         funcs.init.Init( funcName );
  4807.     } else if ( !idStr::Icmp ( scriptName, "onclick" ) ) {
  4808.         funcs.onclick.Init( funcName );
  4809.     } else if ( !idStr::Icmp ( scriptName, "launch_projectile" ) ) {
  4810.         funcs.launch_projectile.Init( funcName );
  4811.     } else if ( !idStr::Icmp ( scriptName, "footstep" ) ) {
  4812.         funcs.footstep.Init( funcName );
  4813.     } else if ( !idStr::Icmp ( scriptName, "postHeal" ) ) {
  4814.         // hax: this is a medic only script and I don't want to store it on every AI type..
  4815.         //    I also don't want it generating the warning below in this case.
  4816.     } else if ( !idStr::Icmp ( scriptName, "postWeaponDestroyed" ) ) {
  4817.         // hax: this is a Gladiator/Light Tank only script and I don't want to store it on every AI type..
  4818.         //    I also don't want it generating the warning below in this case.
  4819.     } else {
  4820.         gameLocal.Warning ( "unknown script '%s' specified on entity '%s'", scriptName, name.c_str() );
  4821.     }
  4822. }
  4823.  
  4824. /*
  4825. ===============================================================================
  4826.  
  4827.     idAI - Reactions
  4828.  
  4829. ===============================================================================
  4830. */
  4831.  
  4832. /*
  4833. ============
  4834. idAI::ReactToShotAt
  4835. ============
  4836. */
  4837. void idAI::ReactToShotAt ( idEntity* attacker, const idVec3 &origOrigin, const idVec3 &origDir ) {
  4838.     if ( g_perfTest_aiNoDodge.GetBool() ) {
  4839.         return;
  4840.     }
  4841.  
  4842.     idVec3 foo;
  4843.     idVec3 diff;
  4844.     diff = GetPhysics()->GetOrigin() - origOrigin;
  4845.     diff = origOrigin + diff.ProjectOntoVector ( origDir ) * origDir;
  4846.     diff = diff - GetPhysics()->GetOrigin();
  4847.     diff.NormalizeFast ( );
  4848.     diff.z = 0;
  4849.             
  4850.     idAngles angles = diff.ToAngles ( );
  4851.     float angleDelta = idMath::AngleDelta ( angles[YAW], move.current_yaw );
  4852.  
  4853.     combat.shotAtTime    = gameLocal.time;
  4854.     combat.shotAtAngle  = angleDelta;
  4855.             
  4856.     // Someone is attacking us so give them a chance to be our new enemy
  4857.     CheckForReplaceEnemy ( attacker );
  4858. }
  4859.  
  4860. /*
  4861. ============
  4862. idAI::ReactToPain
  4863. ============
  4864. */
  4865. void idAI::ReactToPain ( idEntity* attacker, int damage ) {
  4866.     CheckForReplaceEnemy ( attacker );
  4867. }
  4868.     
  4869. /*
  4870. ===============================================================================
  4871.  
  4872.     idAI - Helpers
  4873.  
  4874. ===============================================================================
  4875. */
  4876.  
  4877. /*
  4878. ============
  4879. idAI::UpdateHelper
  4880. ============
  4881. */
  4882. void idAI::UpdateHelper ( void ) {
  4883.     rvAIHelper* oldhelper;
  4884.             
  4885.     // Link ourselves to the closest helper
  4886.     oldhelper     = helperCurrent;
  4887.     helperCurrent = aiManager.FindClosestHelper ( physicsObj.GetOrigin() );
  4888.     
  4889.     // Ideal stays the same as current as long as it was the same when we started
  4890.     if ( oldhelper == helperIdeal ) {
  4891.         helperIdeal = helperCurrent;
  4892.     }
  4893. }
  4894.  
  4895. /*
  4896. ============
  4897. idAI::GetActiveHelper
  4898.  
  4899. When we have an enemy our current helper becomes the active helper, when we dont have an 
  4900. enemy we instead use our ideal.
  4901. ============
  4902. */
  4903. rvAIHelper* idAI::GetActiveHelper ( void ) {
  4904.     return GetEnemy ( ) ? helperCurrent : helperIdeal;
  4905. }
  4906.  
  4907. /*
  4908. ===============================================================================
  4909.  
  4910.     idAI - Tethers
  4911.  
  4912. ===============================================================================
  4913. */
  4914.  
  4915. /*
  4916. ============
  4917. idAI::SetTether
  4918. ============
  4919. */
  4920. void idAI::SetTether ( rvAITether* newTether ) {
  4921.     aifl.tetherMover = false;
  4922.     
  4923.     // Clear our current tether?
  4924.     if ( !newTether ) {
  4925.         if ( tether ) {
  4926.             tether = NULL;
  4927.             ForceTacticalUpdate ( );
  4928.         }
  4929.     } else if ( newTether->IsType ( rvAITetherClear::GetClassType ( ) ) ) {
  4930.         SetTether ( NULL );
  4931.     } else {
  4932.         if ( newTether && !newTether->ValidateAAS ( this ) ) {
  4933.             // If you have aas error out to make them fix it
  4934.             if ( aas ) {
  4935.                 gameLocal.Error ( "tether entity '%s' does no link into the aas for ai '%s'. (try moving it closer to the floor where the aas is)",
  4936.                                    newTether->GetName(), GetName ()  );
  4937.             // If we dont have aas, just warn                                   
  4938.             } else {
  4939.                 gameLocal.Warning ( "tether entity '%s' does no link into the aas for ai '%s'. (there is no aas available)",
  4940.                                    newTether->GetName(), GetName ()  );
  4941.             }                
  4942.             SetTether ( NULL );
  4943.         } else if ( newTether != tether ) {
  4944.             tether = newTether;
  4945.             ForceTacticalUpdate ( );
  4946.         }
  4947.     }
  4948. }
  4949.  
  4950. /*
  4951. ============
  4952. idAI::GetTether
  4953. ============
  4954. */
  4955. rvAITether* idAI::GetTether ( void ) {
  4956.     return tether;
  4957. }
  4958.  
  4959. /*
  4960. ============
  4961. idAI::IsTethered
  4962. ============
  4963. */
  4964. bool idAI::IsTethered ( void ) const {
  4965.     // Need a tether entity to be tethered
  4966.     if ( !tether ) {
  4967.         return false;
  4968.     }
  4969.     // If we have an enemy and that enemy is within our tether then break it if we can
  4970.     if ( enemy.ent && enemy.ent->IsType ( idAI::GetClassType() ) && tether->CanBreak ( ) ) {
  4971.         if ( tether->ValidateDestination ( static_cast<idAI*>(enemy.ent.GetEntity()), enemy.lastKnownPosition ) ) {
  4972.             return false;
  4973.         }
  4974.     }
  4975.     return true;
  4976. }
  4977.  
  4978. /*
  4979. ============
  4980. idAI::IsWithinTether
  4981. ============
  4982. */
  4983. bool idAI::IsWithinTether ( void ) const {
  4984.     if ( !IsTethered ( ) ) {
  4985.         return false;
  4986.     }
  4987.     if ( !tether->ValidateDestination ( (idAI*)this, physicsObj.GetOrigin ( ) ) ) {
  4988.         return false;
  4989.     }
  4990.     return true;
  4991. }
  4992.  
  4993. /*
  4994. ===============================================================================
  4995.  
  4996.     idAI - NonCombat
  4997.  
  4998. ===============================================================================
  4999. */
  5000.  
  5001. /*
  5002. =====================
  5003. idAI::SetTalkState
  5004. =====================
  5005. */
  5006. void idAI::SetTalkState ( talkState_t state ) {
  5007.     // Make sure state is valid
  5008.     if ( ( state < 0 ) || ( state >= NUM_TALK_STATES ) ) {
  5009.         gameLocal.Error( "Invalid talk state (%d)", (int)state ); 
  5010.     }
  5011.     
  5012.     // Same state we are already in?
  5013.     if ( talkState == state ) {
  5014.         return;
  5015.     }
  5016.     
  5017.     // Set new talk state
  5018.     talkState = state;
  5019.  
  5020. }
  5021.  
  5022. /*
  5023. ============
  5024. idAI::SetPassivePrefix
  5025. ============
  5026. */
  5027. void idAI::SetPassivePrefix ( const char* prefix ) {
  5028.     passive.prefix = prefix;
  5029.     if ( passive.prefix.Length() ) {
  5030.         passive.prefix += "_";
  5031.     }
  5032.  
  5033.     // Force an idle change
  5034.     passive.idleAnimChangeTime = 0;    
  5035.     passive.fidgetTime           = 0;
  5036.     passive.talkTime           = 0;
  5037.     
  5038.     // Get animation prefixs
  5039.     passive.fl.multipleIdles = GetPassiveAnimPrefix ( "idle",        passive.animIdlePrefix );    
  5040.                                GetPassiveAnimPrefix ( "fidget",        passive.animFidgetPrefix );
  5041.                                GetPassiveAnimPrefix ( "talk",        passive.animTalkPrefix );
  5042. }
  5043.  
  5044. /*
  5045. ============
  5046. idAI::GetPassiveAnimPrefix
  5047. ============
  5048. */
  5049. bool idAI::GetPassiveAnimPrefix ( const char* animName, idStr& animPrefix ) {
  5050.     const idKeyValue* key;
  5051.     
  5052.     // First see if we have custom idle animations for the passive prefix
  5053.     key = NULL;
  5054.     if ( passive.prefix.Length ( ) ) {
  5055.         animPrefix = va("anim_%s%s", passive.prefix.c_str(), animName );
  5056.         key           = spawnArgs.MatchPrefix ( animPrefix );
  5057.     }
  5058.     
  5059.     // If there are no custom idle animations for the prefix then see if there are any custom anims at all
  5060.     if ( !key ) {
  5061.         animPrefix = va("anim_%s", animName );
  5062.         key           = spawnArgs.MatchPrefix ( animPrefix );
  5063.     }
  5064.     
  5065.     if ( !key ) {
  5066.         animPrefix = "";
  5067.         return false;
  5068.     }
  5069.     
  5070.     return spawnArgs.MatchPrefix ( animPrefix, key ) ? true : false;
  5071. }
  5072.  
  5073. /*
  5074. ===================
  5075. idAI::IsMeleeNeeded
  5076. ===================
  5077. */
  5078. bool idAI::IsMeleeNeeded( void )    {
  5079.  
  5080.     if( enemy.ent && enemy.ent->IsType ( idAI::Type ))    {
  5081.         
  5082.         idAI* enemyAI = static_cast<idAI*>(enemy.ent.GetEntity());
  5083.  
  5084.         //if our enemy is closing in on us and demands melee, we'll meet him.
  5085.         if ( enemyAI->combat.tacticalCurrent == AITACTICAL_MELEE && enemy.range < combat.meleeRange ) {
  5086.             return true;
  5087.         }
  5088.     
  5089.         //other checks...
  5090.     }
  5091.     
  5092.     return false;
  5093. }
  5094.  
  5095. /*
  5096. ===================
  5097. idAI::IsCrouching
  5098. ===================
  5099. */
  5100. bool idAI::IsCrouching( void ) const {
  5101.     return move.fl.crouching;
  5102. }
  5103.  
  5104. bool idAI::CheckDeathCausesMissionFailure( void )
  5105. {
  5106.     if ( spawnArgs.GetString( "objectivetitle_failed", NULL ) )
  5107.     {
  5108.         return true;
  5109.     }
  5110.     if ( targets.Num() )
  5111.     {
  5112.         //go through my targets and see if any are of class rvObjectiveFailed
  5113.         idEntity* targEnt;
  5114.         for( int i = 0; i < targets.Num(); i++ ) {
  5115.             targEnt = targets[ i ].GetEntity();
  5116.             if ( !targEnt )
  5117.             {
  5118.                 continue;
  5119.             }
  5120.             if ( !targEnt->IsType( rvObjectiveFailed::GetClassType() ) ) {
  5121.                 continue;
  5122.             }
  5123.             if ( !spawnArgs.GetString( "inv_objective", NULL ) ) {
  5124.                 continue;
  5125.             }
  5126.             //yep!
  5127.             return true;
  5128.         }
  5129.     }
  5130.     return false;
  5131. }
  5132.