home *** CD-ROM | disk | FTP | other *** search
/ GameStar Special 2004 September / GSSH0904CD2.iso / Doom3 / Mods / UAC_Sentry / UAC_Sentry.exe / script / ai_character_sentry.script
Text File  |  2004-08-04  |  31KB  |  1,416 lines

  1. /***********************************************************************
  2.  
  3. ai_character_sentry.script
  4.  
  5. ***********************************************************************/
  6.  
  7. #define SENTRY_WAIT_GETCLOSER        3
  8. #define    SENTRY_LEAD_TIMEOUT        1
  9. #define SENTRY_FIRERATE            0.1
  10. #define SENTRY_MIN_SHOTS        8
  11. #define SENTRY_MIN_LOOK_DELAY        4
  12. #define    SENTRY_MAX_LOOK_DELAY        8
  13. #define    SENTRY_LOOK_TIME        1.5
  14. #define SENTRY_LEAD_DIST        140
  15. #define SENTRY_LOST_DIST        280
  16. #define SENTRY_WALKTURN            65
  17. #define SENTRY_WALKTURN2        30
  18. #define SENTRY_ATTACK_MAX_LENGTH    4
  19. #define SENTRY_ATTACK_MIN_LENGTH    1.2
  20. #define SENTRY_WAIT_MAX_LENGTH        1
  21. #define SENTRY_WAIT_MIN_LENGTH        0.3
  22. #define SENTRY_MIN_TURN            10
  23.  
  24. //rektek
  25. #define SENTRY_MAXDIST    190
  26. #define SENTRY_MINDIST    150
  27. #define SENTRY_RUNDIST    200
  28.  
  29. #define RETREATING            -1
  30. #define    NOT_MOVING            0
  31. #define    APPROACHING            1
  32.  
  33. #define RETREATING_DEST            -1
  34. #define    APPROACHING_DEST        1
  35.  
  36. #define PLAYER_LOST            0
  37. #define PLAYER_BEHIND_ME        1
  38. #define PLAYER_OK            2
  39. #define PLAYER_AHEAD_OF_ME        3
  40.  
  41. object char_sentry : character {
  42.     boolean        lead_player;
  43.     float        hanging;
  44.     boolean        awake;
  45.     boolean        fire;
  46.     float        playerMoveState;
  47.     entity        light;
  48.     boolean        light_on;
  49.     float        nextPositionSearch;
  50.     entity        ignore_enemy;
  51.     float        ignore_enemy_time;
  52.     vector        lastValidPlayerPosition;
  53.     float        playerPositionThread;
  54.     
  55.     //rektek
  56.     boolean        run;
  57.     
  58.     //
  59.     // States
  60.     //
  61.     void        state_WaitForTrigger();
  62.     void        state_Idle();
  63.     void        state_Killed();
  64.     void        state_FollowPath();
  65.     void        state_Wait();
  66.     void        state_GetCloser();
  67.     void        state_CantReachPlayer();
  68.     void        state_Lead();
  69.     void        state_FindPlayer();
  70.     void        state_Combat();
  71.     void        combat_chase();
  72.     boolean        find_attack_position();
  73.  
  74.     // attack checks
  75.     float        check_attacks();
  76.     void        do_attack( float attack_flags );
  77.     void        combat_range();
  78.  
  79.     void        state_Begin();
  80.     void        init();
  81.     void        destroy();
  82.  
  83.     void        spawn_flashlight();
  84.     void        flashlight_off();
  85.     void        flashlight_on();
  86.     void        ignore( entity ignore_ent );
  87.     boolean        checkForEnemy( float use_fov );
  88.     
  89.     float        checkDestinationDistance();
  90.     void        updatePlayerPositionThread();
  91.  
  92.     // path commands
  93.     void        path_sentry_light_on();
  94.     void        path_sentry_light_off();
  95.     void        path_sentry_shutdown();
  96.     void        path_sentry_unlock_door();
  97.     void        path_corner();
  98.     void        path_sentry_lead_player();
  99.     void        path_sentry_ignore_player();
  100.         
  101.     // torso anim states
  102.     void        Torso_Death();
  103.     void        Torso_Idle();
  104.     void        Torso_Pain();
  105.     void        Torso_RangeAttack();
  106.     
  107.     // legs anim states
  108.     void        Legs_Fold();
  109.     void        Legs_Folded();
  110.     void        Legs_Unfold();
  111.     void        Legs_Hanging();
  112.     void        Legs_Death();
  113.     void        Legs_Idle();
  114.     void        Legs_Walk();
  115.     void        Legs_TurnLeft();
  116.     void        Legs_TurnRight();
  117. };
  118.  
  119. /***********************************************************************
  120.  
  121.     Torso animation control
  122.  
  123. ***********************************************************************/
  124.  
  125. void char_sentry::Torso_Death() {
  126.     finishAction( "dead" );
  127.  
  128.     // never exit
  129.     waitUntil( 0 );
  130. }
  131.  
  132. void char_sentry::Torso_Idle() {
  133.     idleAnim( ANIMCHANNEL_TORSO, "stand" );
  134.     
  135.     eachFrame {
  136.         if ( AI_PAIN ) {
  137.             Torso_Pain();
  138.             idleAnim( ANIMCHANNEL_TORSO, "stand" );
  139.         }
  140.         if ( fire ) {
  141.             animState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
  142.         }
  143.     }
  144. }
  145.  
  146. void char_sentry::Torso_Pain() {
  147.     string animname;
  148.  
  149.     animname = getPainAnim();
  150.     setBlendFrames( ANIMCHANNEL_TORSO, 2 );
  151.     playAnim( ANIMCHANNEL_TORSO, animname );
  152.  
  153.     while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
  154.         waitFrame();
  155.     }
  156.     
  157.     // no pain for 2 seconds
  158.     preventPain( 2 );
  159.     
  160.     finishAction( "pain" );
  161.     setBlendFrames( ANIMCHANNEL_TORSO, 4 );
  162. }
  163.  
  164. void char_sentry::Torso_RangeAttack() {
  165.     float endtime;
  166.     float firetime;
  167.     float numshots;
  168.     
  169.     setAnimPrefix( "" );
  170.         
  171.     playAnim( ANIMCHANNEL_TORSO, "range_attack" );
  172.     while( !animDone( ANIMCHANNEL_TORSO, 0 ) ) {
  173.         if ( AI_PAIN ) {
  174.             Torso_Pain();
  175.         }
  176.         waitFrame();
  177.     }
  178.     
  179.     numshots = 0;
  180.     while( fire || ( numshots < SENTRY_MIN_SHOTS ) ) {
  181.         endtime = RandomDelay( SENTRY_ATTACK_MIN_LENGTH, SENTRY_ATTACK_MAX_LENGTH );
  182.         setBlendFrames( ANIMCHANNEL_TORSO, 2 );
  183.         playCycle( ANIMCHANNEL_TORSO, "range_attack_loop" );
  184.         firetime = sys.getTime();
  185.         while( ( fire || ( numshots < SENTRY_MIN_SHOTS ) ) && ( sys.getTime() < endtime ) ) {
  186.             if ( sys.getTime() >= firetime ) {
  187.                 startSound( "snd_fire", SND_CHANNEL_WEAPON, false );
  188.                 attackMissile( "Barrel" );
  189.                 numshots++;
  190.                 firetime = sys.getTime() + SENTRY_FIRERATE;
  191.             }
  192.             if ( AI_PAIN ) {
  193.                 Torso_Pain();
  194.                 playCycle( ANIMCHANNEL_TORSO, "range_attack_loop" );
  195.             }
  196.             waitFrame();
  197.         }
  198.  
  199.         if ( !fire && ( numshots >= SENTRY_MIN_SHOTS ) ) {
  200.             break;
  201.         }
  202.  
  203.         setBlendFrames( ANIMCHANNEL_TORSO, 2 );
  204.         playCycle( ANIMCHANNEL_TORSO, "range_attack_aim" );
  205.         endtime = RandomDelay( SENTRY_WAIT_MIN_LENGTH, SENTRY_WAIT_MAX_LENGTH );
  206.         while( ( fire || ( numshots < SENTRY_MIN_SHOTS ) ) && ( sys.getTime() < endtime ) ) {
  207.             if ( AI_PAIN ) {
  208.                 Torso_Pain();
  209.             }
  210.             waitFrame();
  211.         }
  212.     }
  213.     
  214.     playAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
  215.     while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
  216.         if ( AI_PAIN ) {
  217.             Torso_Pain();
  218.         }
  219.         waitFrame();
  220.     }
  221.     
  222.     finishAction( "range_attack" );
  223.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  224. }
  225.  
  226. /***********************************************************************
  227.  
  228.     Legs animation control
  229.  
  230. ***********************************************************************/
  231.  
  232. void char_sentry::Legs_Death() {
  233.     while( AI_DEAD ) {
  234.         waitFrame();
  235.     }
  236.     
  237.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
  238. }
  239.  
  240. void char_sentry::Legs_Idle() {
  241.     float delta;
  242.     
  243.     if ( !AI_FORWARD && !facingIdeal() ) {
  244.         if ( getTurnDelta() > SENTRY_MIN_TURN ) {
  245.             animState( ANIMCHANNEL_LEGS, "Legs_TurnLeft", 4 );
  246.         }
  247.  
  248.         if ( getTurnDelta() < -SENTRY_MIN_TURN ) {
  249.             animState( ANIMCHANNEL_LEGS, "Legs_TurnRight", 4 );
  250.         }
  251.     }
  252.     
  253.     idleAnim( ANIMCHANNEL_LEGS, "stand" );
  254.  
  255.     eachFrame {
  256. //rektek
  257. //        if ( !awake ) {
  258. //            animState( ANIMCHANNEL_LEGS, "Legs_Fold", 4 );
  259. //        }
  260.         if ( AI_FORWARD ) {
  261.             delta = getTurnDelta();
  262.             if ( ( delta <= SENTRY_WALKTURN ) && ( delta >= -SENTRY_WALKTURN ) ) {
  263.                 animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
  264.             }
  265.         }
  266.         if ( !facingIdeal() ) {
  267.             if ( getTurnDelta() > SENTRY_MIN_TURN ) {
  268.                 animState( ANIMCHANNEL_LEGS, "Legs_TurnLeft", 4 );
  269.             }
  270.             if ( getTurnDelta() < -SENTRY_MIN_TURN ) {
  271.                 animState( ANIMCHANNEL_LEGS, "Legs_TurnRight", 4 );
  272.             }
  273.         }
  274.     }
  275. }
  276.  
  277. void char_sentry::Legs_Walk() {
  278.     float delta;
  279.     
  280.     playCycle( ANIMCHANNEL_LEGS, "walk" );
  281.     
  282.     while( AI_FORWARD )    {
  283.         delta = getTurnDelta();
  284.         if ( ( delta > SENTRY_WALKTURN ) || ( delta < -SENTRY_WALKTURN ) ) {
  285.             break;
  286.         }
  287.  
  288.         waitFrame();
  289.     }
  290.     
  291.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 12 );
  292. }
  293.  
  294. void char_sentry::Legs_Fold() {
  295.     startSound( "snd_shutdown", SND_CHANNEL_VOICE, false );
  296.     playAnim( ANIMCHANNEL_LEGS, "fold" );    
  297.     while( !animDone( ANIMCHANNEL_LEGS, 0 ) ) {
  298.         waitFrame();
  299.     }
  300.     flashlight_off();
  301.     finishAction( "fold" );
  302.     animState( ANIMCHANNEL_LEGS, "Legs_Folded", 0 );
  303. }
  304.  
  305. void char_sentry::Legs_Folded() {
  306.     playCycle( ANIMCHANNEL_LEGS, "folded" );    
  307.     while( !awake ) {
  308.         waitFrame();
  309.     }
  310.     
  311.     animState( ANIMCHANNEL_LEGS, "Legs_Unfold", 4 );
  312. }
  313.  
  314. void char_sentry::Legs_Unfold() {
  315.     playAnim( ANIMCHANNEL_LEGS, "unfold" );    
  316.     while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
  317.         waitFrame();
  318.     }
  319.     
  320.     finishAction( "unfold" );
  321.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
  322. }
  323.  
  324. void char_sentry::Legs_Hanging() {
  325.     playCycle( ANIMCHANNEL_LEGS, "releaseidle" );    
  326.     while( hanging ) {
  327.         waitFrame();
  328.     }
  329.     
  330.     playAnim( ANIMCHANNEL_LEGS, "load" );    
  331.     while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
  332.         waitFrame();
  333.     }
  334.     
  335.     finishAction( "unload" );
  336.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
  337. }
  338.  
  339. void char_sentry::Legs_TurnLeft() {
  340.     float turnAmount;
  341.     float delta;
  342.     
  343.     turnAmount = getTurnDelta();
  344.     if ( turnAmount > 110 ) {
  345.         // do it in two turns
  346.         turnAmount *= 0.5;
  347.     }
  348.  
  349.     playAnim( ANIMCHANNEL_LEGS, "turn_left" );
  350.     while( !animDone( ANIMCHANNEL_LEGS, 0 ) ) {
  351.         if ( AI_FORWARD ) {
  352.             delta = getTurnDelta();
  353.             if ( ( delta <= SENTRY_WALKTURN2 ) && ( delta >= -SENTRY_WALKTURN2 ) ) {
  354.                 animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
  355.             }
  356.         }
  357.         waitFrame();
  358.     }
  359.     
  360.     playAnim( ANIMCHANNEL_LEGS, "turn_right" );
  361.     while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
  362.         if ( AI_FORWARD ) {
  363.             delta = getTurnDelta();
  364.             if ( ( delta <= SENTRY_WALKTURN2 ) && ( delta >= -SENTRY_WALKTURN2 ) ) {
  365.                 animState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
  366.             }
  367.         }
  368.         waitFrame();
  369.     }
  370.     
  371.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
  372. }
  373.  
  374. void char_sentry::Legs_TurnRight() {
  375.     float turnAmount;
  376.     float delta;
  377.     
  378.     turnAmount = getTurnDelta();
  379.     if ( turnAmount < -110 ) {
  380.         // do it in two turns
  381.         turnAmount *= 0.5;
  382.     }
  383.  
  384.     playAnim( ANIMCHANNEL_LEGS, "turn_right" );
  385.     while( !animDone( ANIMCHANNEL_LEGS, 0 ) ) {
  386.         if ( AI_FORWARD ) {
  387.             delta = getTurnDelta();
  388.             if ( ( delta <= SENTRY_WALKTURN2 ) && ( delta >= -SENTRY_WALKTURN2 ) ) {
  389.                 animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
  390.             }
  391.         }
  392.         waitFrame();
  393.     }
  394.     
  395.     playAnim( ANIMCHANNEL_LEGS, "turn_left" );
  396.     while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
  397.         if ( AI_FORWARD ) {
  398.             delta = getTurnDelta();
  399.             if ( ( delta <= SENTRY_WALKTURN2 ) && ( delta >= -SENTRY_WALKTURN2 ) ) {
  400.                 animState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
  401.             }
  402.         }
  403.         waitFrame();
  404.     }
  405.  
  406.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
  407. }
  408.  
  409. /***********************************************************************
  410.  
  411.     AI
  412.  
  413. ***********************************************************************/
  414.  
  415. /*
  416. =====================
  417. char_sentry::state_Begin
  418. =====================
  419. */
  420. void char_sentry::state_Begin() {
  421.     fire = false;
  422.     setBoneMod( true );
  423.  
  424.     light_on = false;
  425.     if ( getIntKey( "flashlight" ) ) {
  426.         spawn_flashlight();
  427.     }
  428.  
  429.     hanging = getIntKey( "hanging" );
  430.     awake = getIntKey( "unfolded" );
  431.  
  432.     waitFrame();
  433.     if ( hanging ) {
  434.         animState( ANIMCHANNEL_LEGS, "Legs_Hanging", 0 );
  435.     } else if ( !awake ) {
  436.         animState( ANIMCHANNEL_LEGS, "Legs_Folded", 0 );
  437.     } else {
  438.         animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
  439.     }
  440.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
  441.     setState( "state_WaitForTrigger" );
  442. }
  443.  
  444. /*
  445. =====================
  446. char_sentry::init
  447. =====================
  448. */
  449. void char_sentry::init() {
  450.  
  451.     lead_player = getIntKey( "lead_player" );
  452.     can_talk = false;
  453.     no_cower_saved = true;
  454.     no_cower = true;
  455. }
  456.  
  457. /*
  458. =====================
  459. char_sentry::destroy
  460. =====================
  461. */
  462. void char_sentry::destroy() {
  463.     light.remove();
  464.     if ( playerPositionThread ) {
  465.         sys.terminate( playerPositionThread );
  466.         playerPositionThread = 0;
  467.     }
  468. }
  469.  
  470. /*
  471. =====================
  472. char_sentry::spawn_flashlight
  473. =====================
  474. */
  475. void char_sentry::spawn_flashlight() {
  476.     string texture;
  477.     float distance;
  478.  
  479.     distance = getFloatKey( "flashlight_distance" );
  480.     if ( !distance ) {
  481.         distance = 640;
  482.     }
  483.     
  484.     // use ik_pose to bind light
  485.     playAnim( ANIMCHANNEL_TORSO, "ik_pose" );
  486.     if ( getIntKey( "flashlight" ) == 2 ) {
  487.         // spot light
  488.         sys.setSpawnArg( "light_target", "1 0 0" );
  489.         sys.setSpawnArg( "light_up", "0 0 .5" );
  490.         sys.setSpawnArg( "light_right", "0 -.5 0" );    
  491.         sys.setSpawnArg( "light_end", distance + " 0 0" );
  492.  
  493.         texture = getKey( "mtr_flashlight" );
  494.         sys.setSpawnArg( "texture", texture );
  495.  
  496.         sys.setSpawnArg( "name", getName() + "_light" );    
  497.         light = sys.spawn( "light" );
  498.         light.setAngles( getAngles() );
  499.         light.bindToJoint( self, "light", true );
  500.         light.setOrigin( '0 0 0' );
  501.     } else {
  502.         // radial light
  503.         sys.setSpawnArg( "name", getName() + "_light" );    
  504.         light = sys.spawn( "light" );
  505.         light.setRadius( 256 );    
  506.         light.setAngles( getAngles() + '0 90 0' );
  507.         light.bindToJoint( self, "light", true );
  508.         texture = getKey( "mtr_flashlight" );
  509.         light.setShader( texture );
  510.         light.setOrigin( '0 0 0' );
  511.     }
  512.     stopAnim( ANIMCHANNEL_TORSO, 0 );
  513.  
  514.     flashlight_off();
  515. }
  516.  
  517. /*
  518. =====================
  519. char_sentry::flashlight_off
  520. =====================
  521. */
  522. void char_sentry::flashlight_off() {
  523.     string skin;
  524.  
  525.     if ( light ) {
  526.         light.Off();
  527.         skin = getKey( "skin_flashlight_off" );
  528.         setSkin( skin );
  529.         light_on = false;
  530.     }
  531. }
  532.     
  533. /*
  534. =====================
  535. char_sentry::flashlight_on
  536. =====================
  537. */
  538. void char_sentry::flashlight_on() {
  539.     string skin;
  540.  
  541.     if ( light ) {
  542.         light.On();
  543.         skin = getKey( "skin_flashlight_on" );
  544.         setSkin( skin );
  545.         light_on = true;
  546.     }
  547. }
  548.     
  549. /*
  550. =====================
  551. char_sentry::ignore
  552. =====================
  553. */
  554. void char_sentry::ignore( entity ignore_ent ) {
  555.     ignore_enemy = ignore_ent;
  556.     ignore_enemy_time = sys.getTime() + 2;
  557. }
  558.  
  559. /*
  560. =====================
  561. char_sentry::checkForEnemy
  562. =====================
  563. */
  564. boolean    char_sentry::checkForEnemy( float use_fov ) {
  565.     entity enemy;
  566.     entity ent;
  567.  
  568.     enemy = $null_entity;
  569.     if ( lead_player && $player1.hasEnemies() ) {
  570.         enemy = closestReachableEnemyOfEntity( $player1 );
  571.         if ( ( ignore_enemy_time > sys.getTime() ) && ( ignore_enemy == enemy ) ) {
  572.             enemy = $null_entity;
  573.         }
  574.         if ( !enemy ) {
  575.             enemy = $player1.closestEnemyToPoint( getOrigin() );
  576.             if ( ( ignore_enemy_time > sys.getTime() ) && ( ignore_enemy == enemy ) ) {
  577.                 enemy = $null_entity;
  578.             }
  579.         }
  580.     }
  581.     
  582.     if ( !enemy ) {
  583.         enemy = findEnemyAI( false );
  584.         if ( ( ignore_enemy_time > sys.getTime() ) && ( ignore_enemy == enemy ) ) {
  585.             enemy = $null_entity;
  586.         }
  587.     }
  588.  
  589.     if ( enemy ) {
  590.         startSound( "snd_sight_enemy", SND_CHANNEL_VOICE, false );
  591.         setEnemy( enemy );
  592.         return true;
  593.     } else {
  594.         clearEnemy();
  595.     }
  596.  
  597.     return false;
  598. }
  599.  
  600. /*
  601. =====================
  602. char_sentry::checkDestinationDistance()
  603. =====================
  604. */
  605. float char_sentry::checkDestinationDistance() {
  606.     float    playerDist;
  607.     float    currentDist;
  608.     float    leadDistance;
  609.     float    relativeMoveDir;
  610.     vector    delta;
  611.  
  612.     // determine if the player is moving toward us
  613.     delta = getOrigin() - lastValidPlayerPosition;
  614.     relativeMoveDir = delta * $player1.getLinearVelocity();
  615.     if ( relativeMoveDir > 1 ) {
  616.         // Player is approaching
  617.         playerMoveState = APPROACHING;
  618.     } else if ( relativeMoveDir < -1 ) {
  619.         // Player is moving away from us
  620.         playerMoveState = RETREATING;
  621.     } else {
  622.         // Player is standing still
  623.         playerMoveState = NOT_MOVING;
  624.     }
  625.  
  626.     if ( !current_path ) {
  627.         return PLAYER_OK;
  628.     }
  629.  
  630.     playerDist = distanceToPoint( lastValidPlayerPosition );
  631.     if ( playerDist < SENTRY_LEAD_DIST ) {
  632.         return PLAYER_OK;
  633.     }
  634.  
  635.     if ( !canSee( $player1 ) ) {
  636.         currentDist = travelDistanceToEntity( current_path );
  637.         leadDistance = travelDistanceBetweenPoints( lastValidPlayerPosition, current_path.getOrigin() );
  638.         if ( currentDist > leadDistance ) {
  639.             return PLAYER_AHEAD_OF_ME;
  640.         } else {
  641.             return PLAYER_LOST;
  642.         }
  643.     }
  644.  
  645.     if ( playerDist > SENTRY_LOST_DIST ) {
  646.         currentDist = travelDistanceToEntity( current_path );
  647.         leadDistance = travelDistanceBetweenPoints( lastValidPlayerPosition, current_path.getOrigin() );
  648.         if ( currentDist > leadDistance ) {
  649.             return PLAYER_AHEAD_OF_ME;
  650.         } else {
  651.             return PLAYER_BEHIND_ME;
  652.         }
  653.     }
  654.  
  655.     return PLAYER_OK;
  656. }
  657.  
  658. /*
  659. =====================
  660. char_sentry::updatePlayerPositionThread()
  661. =====================
  662. */
  663. void char_sentry::updatePlayerPositionThread() {
  664.     vector pos;
  665.  
  666.     while( 1 ) {
  667.         if ( canReachEntity( $player1 ) ) {
  668.             lastValidPlayerPosition = getReachableEntityPosition( $player1 );
  669.         }
  670.         //sys.debugBounds( '1 0 0', lastValidPlayerPosition + $player1.getMins(), lastValidPlayerPosition + $player1.getMaxs(), 0 );
  671.         waitFrame();
  672.     }
  673. }
  674.  
  675. /***********************************************************************
  676.  
  677.     Path commands
  678.  
  679. ***********************************************************************/
  680.  
  681. /*
  682. =====================
  683. char_sentry::path_sentry_light_on
  684. =====================
  685. */
  686. void char_sentry::path_sentry_light_on() {
  687.     flashlight_on();
  688. }
  689.  
  690. /*
  691. =====================
  692. char_sentry::path_sentry_light_off
  693. =====================
  694. */
  695. void char_sentry::path_sentry_light_off() {
  696.     flashlight_off();
  697. }
  698.  
  699. /*
  700. =====================
  701. char_sentry::path_sentry_shutdown
  702. =====================
  703. */
  704. void char_sentry::path_sentry_shutdown() {
  705.     vector ang;
  706.  
  707.     stopMove();
  708.  
  709.     ang = current_path.getAngles();
  710.     turnTo( ang_y );
  711.     waitUntil( facingIdeal() );
  712.  
  713.     awake = false;
  714.     AI_ACTIVATED = false;
  715.  
  716.     if ( playerPositionThread ) {
  717.         sys.terminate( playerPositionThread );
  718.         playerPositionThread = 0;
  719.     }
  720.  
  721.     waitAction( "fold" );
  722.     while( !AI_ACTIVATED ) {
  723.         waitFrame();
  724.     }
  725.  
  726.     if ( getIntKey( "flashlight_on" ) ) {
  727.         flashlight_on();
  728.     }
  729.     awake = true;
  730.     waitAction( "unfold" );
  731. }
  732.  
  733. /*
  734. =====================
  735. char_sentry::path_sentry_unlock_door
  736. =====================
  737. */
  738. void char_sentry::path_sentry_unlock_door() {
  739.     vector ang;
  740.     string triggername;
  741.     entity triggerent;
  742.  
  743.     if ( current_path.getKey( "trigger" ) == "" ) {
  744.         // already unlocked the door
  745.         return;
  746.     }
  747.  
  748.     ang = current_path.getAngles();
  749.     turnTo( ang_y );
  750.     waitUntil( facingIdeal() );
  751.  
  752.     startSound( "snd_open_door", SND_CHANNEL_VOICE, false );
  753.     playCustomAnim( "unlock_door", 4, 4 );
  754.     waitAction( "customAnim" );
  755.  
  756.     // trigger any entities the path had targeted
  757.     triggername = current_path.getKey( "trigger" );
  758.     if ( triggername != "" ) {
  759.         triggerent = sys.getEntity( triggername );
  760.         if ( triggerent ) {
  761.             triggerent.activate( self );
  762.         }
  763.     }
  764.  
  765.     current_path.setKey( "trigger", "" );
  766. }
  767.  
  768. /*
  769. =====================
  770. char_sentry::path_corner
  771. =====================
  772. */
  773. void char_sentry::path_corner() {
  774.     if ( !lead_player ) {
  775.         setState( "state_FollowPath" );
  776.     }
  777.  
  778.     setNeverDormant( true );
  779.     if ( !canSee( $player1 ) ) {
  780.         setState( "state_FindPlayer" );
  781.     }
  782.  
  783.     if ( checkDestinationDistance() == PLAYER_BEHIND_ME ) {
  784.         setState( "state_Wait" );
  785.     } else {
  786.         setState( "state_Lead" );
  787.     }
  788. }
  789.  
  790. /*
  791. =====================
  792. char_sentry::path_sentry_lead_player
  793. =====================
  794. */
  795. void char_sentry::path_sentry_lead_player() {
  796.     lead_player = true;
  797.     if ( !playerPositionThread ) {
  798.         playerPositionThread = thread updatePlayerPositionThread();
  799.     }
  800. }
  801.  
  802. /*
  803. =====================
  804. char_sentry::path_sentry_ignore_player
  805. =====================
  806. */
  807. void char_sentry::path_sentry_ignore_player() {
  808.     lead_player = false;
  809.     if ( playerPositionThread ) {
  810.         sys.terminate( playerPositionThread );
  811.         playerPositionThread = 0;
  812.     }
  813. }
  814.  
  815. /***********************************************************************
  816.  
  817.     States
  818.  
  819. ***********************************************************************/
  820.  
  821. /*
  822. =====================
  823. char_sentry::state_Killed
  824. =====================
  825. */
  826. void char_sentry::state_Killed() {
  827.     stopMove();
  828.  
  829.     if ( playerPositionThread ) {
  830.         sys.terminate( playerPositionThread );
  831.         playerPositionThread = 0;
  832.     }
  833.  
  834.     flashlight_off();
  835.  
  836.     animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
  837.     animState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
  838.  
  839.     waitAction( "dead" );
  840.  
  841.     float burnDelay = getFloatKey( "burnaway" );
  842.     if ( burnDelay != 0 ) {
  843.         preBurn();
  844.         sys.wait( burnDelay );
  845.         burn();
  846.         startSound( "snd_burn", SND_CHANNEL_BODY, false );
  847.         sys.wait( 3 );
  848.         remove();
  849.     }
  850.  
  851.     stopThinking();
  852. }
  853.  
  854. /*
  855. =====================
  856. char_sentry::state_WaitForTrigger
  857. =====================
  858. */
  859. void char_sentry::state_WaitForTrigger() {
  860.  
  861.     //rektek -- Preprogram Bot AI "FOLLOW PLAYER"
  862.  
  863.     // UNLOAD SENTRY
  864.     Legs_Hanging();
  865.     
  866.     while (1)
  867.     {
  868.         // Always Flashlight?
  869.         flashlight_on();
  870.         lookAt( $player1, 0.1 );
  871.         allowDamage();
  872.         
  873.         // FIND PLAYER
  874.         if ( !canSee( $player1 ) )
  875.         {
  876.             setState( "state_FindPlayer" );
  877.         }
  878.  
  879.         // DEFEND PLAYER
  880.         if ( checkForEnemy( true ) )
  881.         {
  882.             setState( "state_Combat" );
  883.         }
  884.         
  885.         // DON'T BLOCK PLAYER PATH
  886.         if ( AI_PUSHED )
  887.         {
  888.             getOutOfWay();
  889.         }
  890.         
  891.         // RUN TO PLAYER
  892.         run = false;
  893.         moveToEntity( $player1 );
  894.         
  895.         while( !AI_DEST_UNREACHABLE && !AI_MOVE_DONE && ( distanceTo( $player1 ) > SENTRY_MINDIST ) )
  896.         {
  897.             lookAt( $player1, 0.1 );
  898.             if ( distanceTo( $player1 ) > SENTRY_RUNDIST )
  899.             {
  900.                 run = true;
  901.                 startSound( "snd_waiting_for_player", SND_CHANNEL_VOICE, false );
  902.             }
  903.             if ( distanceTo( $player1 ) < SENTRY_MAXDIST )
  904.             {
  905.                 run = false;
  906.             }
  907.             
  908.             waitFrame();
  909.         }
  910.         stopMove();
  911.         
  912.         waitFrame();
  913.     }
  914.     
  915. // OLD CODE "LEAD PLAYER"
  916. /*
  917.     if ( hanging != 2 ) {
  918.         AI_ACTIVATED = false;
  919.         while( !AI_ACTIVATED ) {
  920.             waitFrame();
  921.         }
  922.     }
  923.  
  924.     if ( getIntKey( "flashlight_on" ) ) {
  925.         flashlight_on();
  926.     }
  927.     
  928.     if ( hanging ) {
  929.         hanging = false;
  930.         awake = true;
  931.         waitAction( "unload" );
  932.     } else if ( !awake ) {
  933.         awake = true;
  934.         waitAction( "unfold" );
  935.     }
  936.  
  937.     // hanging guys turn off damage untill they're out.  make sure we get damage turned back on.
  938.     allowDamage();    
  939.     lookAt( $player1, 3 );
  940.  
  941.     current_path = randomPath();
  942.     //next_path = current_path.randomPath();
  943.     lookAt( $player1, 3 );
  944.  
  945.     setState( "state_Idle" );
  946. */
  947. // END OF OLD CODE
  948. }
  949.  
  950. /*
  951. =====================
  952. char_sentry::state_Idle
  953. =====================
  954. */
  955. void char_sentry::state_Idle()
  956. {
  957.  
  958.     //rektek -- Preprogram Bot AI "FOLLOW PLAYER"
  959.     
  960.     while (1)
  961.     {
  962.         // Always Flashlight?
  963.         flashlight_on();
  964.         lookAt( $player1, 0.1 );
  965.         
  966.         // FIND PLAYER
  967.         if ( !canSee( $player1 ) )
  968.         {
  969.             setState( "state_FindPlayer" );
  970.         }
  971.  
  972.         // DEFEND PLAYER
  973.         if ( checkForEnemy( true ) )
  974.         {
  975.             setState( "state_Combat" );
  976.         }
  977.         
  978.         // DON'T BLOCK PLAYER PATH
  979.         if ( AI_PUSHED )
  980.         {
  981.             getOutOfWay();
  982.         }
  983.         
  984.         // RUN TO PLAYER
  985.         run = false;
  986.         moveToEntity( $player1 );
  987.         
  988.         while( !AI_DEST_UNREACHABLE && !AI_MOVE_DONE && ( distanceTo( $player1 ) > SENTRY_MINDIST ) )
  989.         {
  990.             lookAt( $player1, 0.1 );
  991.             if ( distanceTo( $player1 ) > SENTRY_RUNDIST )
  992.             {
  993.                 run = true;
  994.                 startSound( "snd_waiting_for_player", SND_CHANNEL_VOICE, false );
  995.             }
  996.             if ( distanceTo( $player1 ) < SENTRY_MAXDIST )
  997.             {
  998.                 run = false;
  999.             }
  1000.             
  1001.             waitFrame();
  1002.         }
  1003.         stopMove();
  1004.         
  1005.         waitFrame();
  1006.         
  1007.     }
  1008.  
  1009. // OLD CODE "LEAD PLAYER"
  1010. /*
  1011.     stopMove();
  1012.  
  1013.     if ( lead_player && !playerPositionThread ) {
  1014.         playerPositionThread = thread updatePlayerPositionThread();
  1015.     }
  1016.  
  1017.     if ( current_path ) {
  1018.         executePathCommand( current_path );
  1019.     }
  1020.  
  1021.     while( 1 ) {
  1022.         if ( lead_player ) {
  1023.             if ( !canSee( $player1 ) ) {
  1024.                 setState( "state_FindPlayer" );
  1025.             }
  1026.         }
  1027.  
  1028.         if ( checkForEnemy( true ) ) {
  1029.             setState( "state_Combat" );
  1030.         }
  1031.         if ( AI_PUSHED ) {
  1032.             getOutOfWay();
  1033.         }
  1034.         if ( current_path ) {
  1035.             executePathCommand( current_path );
  1036.         }
  1037.         waitFrame();
  1038.     }
  1039. */
  1040. // END OF OLD CODE
  1041. }
  1042.  
  1043. /*
  1044. =====================
  1045. char_sentry::state_FollowPath
  1046. =====================
  1047. */
  1048. void char_sentry::state_FollowPath() {
  1049.     if ( !current_path ) {
  1050.         setState( "state_Idle" );
  1051.     }
  1052.  
  1053.     moveToEntity( current_path );
  1054.  
  1055.     while( 1 ) {
  1056.         if ( checkForEnemy( true ) ) {
  1057.             setState( "state_Combat" );
  1058.         }
  1059.  
  1060.         checkBlocked();
  1061.  
  1062.         if ( AI_MOVE_DONE ) {
  1063.             if ( AI_DEST_UNREACHABLE ) {
  1064.                 waitFrame();
  1065.                 setState( "state_FindPlayer" );
  1066.             }
  1067.             setNeverDormant( getFloatKey( "neverdormant" ) );
  1068.             finishPathCommand();
  1069.             setState( "state_Idle" );
  1070.         }
  1071.         
  1072.         waitFrame();
  1073.     }
  1074. }
  1075.  
  1076. /*
  1077. =====================
  1078. char_sentry::state_Wait
  1079. =====================
  1080. */
  1081. void char_sentry::state_Wait() {
  1082.     float getCloserTime;
  1083.  
  1084.     float result = checkDestinationDistance();
  1085.     if ( result == PLAYER_LOST ) {
  1086.         setState( "state_FindPlayer" );
  1087.     } else if ( result == PLAYER_AHEAD_OF_ME ) {
  1088.         setState( "state_Lead" );
  1089.     } else if ( ( result == PLAYER_OK ) && ( playerMoveState == APPROACHING ) ) {
  1090.         setState( "state_Lead" );
  1091.     }
  1092.  
  1093.     stopMove();
  1094.     startSound( "snd_waiting_for_player", SND_CHANNEL_VOICE, false );
  1095.  
  1096.     getCloserTime = sys.getTime() + SENTRY_WAIT_GETCLOSER;
  1097.     while( 1 ) {
  1098.         if ( checkForEnemy( true ) ) {
  1099.             setState( "state_Combat" );
  1100.         }
  1101.  
  1102.         lookAt( $player1, 0.1 );
  1103.  
  1104.         if ( AI_PUSHED ) {
  1105.             getOutOfWay();
  1106.         }
  1107.  
  1108.         result = checkDestinationDistance();
  1109.         if ( result == PLAYER_LOST ) {
  1110.             setState( "state_FindPlayer" );
  1111.         } else if ( result == PLAYER_AHEAD_OF_ME ) {
  1112.             setState( "state_Lead" );
  1113.         } else if ( ( result == PLAYER_OK ) && ( playerMoveState == APPROACHING ) ) {
  1114.             setState( "state_Lead" );
  1115.         } else  if ( ( sys.getTime() > getCloserTime ) && ( result == PLAYER_BEHIND_ME ) ) {
  1116.             setState( "state_GetCloser" );
  1117.         }
  1118.         waitFrame();
  1119.     }
  1120. }
  1121.  
  1122. /*
  1123. =====================
  1124. char_sentry::state_GetCloser
  1125. =====================
  1126. */
  1127. void char_sentry::state_GetCloser() {
  1128.     while( 1 ) {
  1129.         moveToPosition( lastValidPlayerPosition );
  1130.         if ( AI_MOVE_DONE ) {
  1131.             if ( !canReachEntity( $player1 ) ) {
  1132.                 setState( "state_CantReachPlayer" );
  1133.             }
  1134.         }
  1135.  
  1136.         if ( checkForEnemy( true ) ) {
  1137.             setState( "state_Combat" );
  1138.         }
  1139.  
  1140.         checkBlocked();
  1141.  
  1142.         float result = checkDestinationDistance();
  1143.         
  1144.         if ( result == PLAYER_LOST ) {
  1145.             setState( "state_FindPlayer" );
  1146.         }
  1147.         if ( result == PLAYER_OK ) {
  1148.             setState( "state_Wait" );
  1149.         }
  1150.         waitFrame();
  1151.     }
  1152. }
  1153.  
  1154. /*
  1155. =====================
  1156. char_sentry::state_CantReachPlayer
  1157. =====================
  1158. */
  1159. void char_sentry::state_CantReachPlayer() {
  1160.     startSound( "snd_cant_reach_player", SND_CHANNEL_VOICE, false );
  1161.     stopMove();
  1162.     while( 1 ) {
  1163.         if ( canReachEntity( $player1 ) ) {
  1164.             setState( "state_Wait" );
  1165.         }
  1166.         if ( checkForEnemy( true ) ) {
  1167.             setState( "state_Combat" );
  1168.         }
  1169.         waitFrame();
  1170.     }
  1171. }
  1172.  
  1173. /*
  1174. =====================
  1175. char_sentry::state_Lead
  1176. =====================
  1177. */
  1178. void char_sentry::state_Lead() {
  1179.     float    nextLookTime;
  1180.     float    leadTimeOut;
  1181.  
  1182.     if ( !current_path ) {
  1183.         setState( "state_Idle" );
  1184.     }
  1185.  
  1186.     nextLookTime = RandomDelay( SENTRY_MIN_LOOK_DELAY, SENTRY_MAX_LOOK_DELAY );
  1187.     moveToEntity( current_path );
  1188.  
  1189.     leadTimeOut = sys.getTime() + SENTRY_LEAD_TIMEOUT;
  1190.  
  1191.     while( 1 ) {
  1192.         if ( checkForEnemy( true ) ) {
  1193.             setState( "state_Combat" );
  1194.         }
  1195.  
  1196.         if ( !light_on && ( sys.getTime() > nextLookTime ) ) {
  1197.             lookAt( $player1, SENTRY_LOOK_TIME );
  1198.             nextLookTime = RandomDelay( SENTRY_MIN_LOOK_DELAY, SENTRY_MAX_LOOK_DELAY );
  1199.         }
  1200.  
  1201.         checkBlocked();
  1202.         
  1203.         float result = checkDestinationDistance();
  1204.         if ( ( result != PLAYER_BEHIND_ME ) && ( result != PLAYER_LOST ) ) {
  1205.             leadTimeOut = sys.getTime() + SENTRY_LEAD_TIMEOUT;
  1206.         } else if ( sys.getTime() > leadTimeOut ) {
  1207.             setState( "state_Wait" );
  1208.         } else if ( !light_on ) {
  1209.             // look back at player to see what he's up to
  1210.             lookAt( $player1, 0.1 );
  1211.             nextLookTime += 0.1;
  1212.         }
  1213.  
  1214.         if ( AI_MOVE_DONE ) {
  1215.             if ( AI_DEST_UNREACHABLE ) {
  1216.                 waitFrame();
  1217.                 setState( "state_FindPlayer" );
  1218.             }
  1219.             setNeverDormant( getFloatKey( "neverdormant" ) );
  1220.             finishPathCommand();
  1221.             setState( "state_Idle" );
  1222.         }
  1223.         
  1224.         waitFrame();
  1225.     }
  1226. }
  1227.  
  1228. /*
  1229. =====================
  1230. char_sentry::state_FindPlayer
  1231. =====================
  1232. */
  1233. void char_sentry::state_FindPlayer() {
  1234.     while( 1 ) {
  1235.         moveToPosition( lastValidPlayerPosition );
  1236.         if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) {
  1237.             if ( !canReachEntity( $player1 ) ) {
  1238.                 setState( "state_CantReachPlayer" );
  1239.             }
  1240.         }
  1241.  
  1242.         if ( checkForEnemy( true ) ) {
  1243.             waitFrame();
  1244.             setState( "state_Combat" );
  1245.         }
  1246.  
  1247.         checkBlocked();
  1248.         
  1249.         float result = checkDestinationDistance();
  1250.         if ( canSee( $player1 ) && ( ( result == PLAYER_BEHIND_ME ) || ( result == PLAYER_OK ) ) ) {
  1251.             setState( "state_Wait" );
  1252.         } else if ( result == PLAYER_AHEAD_OF_ME ) {
  1253.             setState( "state_Lead" );
  1254.         }
  1255.         waitFrame();
  1256.     }
  1257. }
  1258.  
  1259. /*
  1260. =====================
  1261. char_sentry::state_Combat
  1262. =====================
  1263. */
  1264. void char_sentry::state_Combat() {
  1265.     float attack_flags;
  1266.  
  1267.     eachFrame {
  1268.         faceEnemy();
  1269.         lookAtEnemy( 1 );
  1270.         
  1271.         if ( AI_ENEMY_DEAD || !getEnemy() ) {
  1272.             startSound( "snd_target_lost", SND_CHANNEL_VOICE, false );
  1273.             AI_ENEMY_DEAD = false;
  1274.             clearEnemy();
  1275.             setState( "state_Idle" );
  1276.             
  1277.         }
  1278.         
  1279.         attack_flags = check_attacks();
  1280.         if ( attack_flags ) {
  1281.             do_attack( attack_flags );
  1282.             continue;
  1283.         }
  1284.         
  1285.         if ( canReachEnemy() ) {
  1286.             combat_chase();
  1287.         } else if ( !find_attack_position() ) {
  1288.             ignore( getEnemy() );
  1289.             checkForEnemy( false );
  1290.         }
  1291.         
  1292.         waitFrame();
  1293.     }
  1294. }
  1295.  
  1296. /*
  1297. =====================
  1298. char_sentry::combat_chase
  1299. =====================
  1300. */
  1301. void char_sentry::combat_chase() {
  1302.     float attack_flags;
  1303.     
  1304.     moveToEnemy();
  1305.     while( !AI_DEST_UNREACHABLE ) {
  1306.         if ( AI_ENEMY_DEAD ) {
  1307.             startSound( "snd_target_lost", SND_CHANNEL_VOICE, false );
  1308.             AI_ENEMY_DEAD = false;
  1309.             clearEnemy();
  1310.             setState( "state_Idle" );
  1311.         }
  1312.  
  1313.         if ( AI_MOVE_DONE ) {
  1314.             if ( !enemyPositionValid() ) {
  1315.                 clearEnemy();
  1316.                 setState( "state_Idle" );
  1317.             }
  1318.             moveToEnemy();
  1319.         }
  1320.         
  1321.         lookAtEnemy( 1 );
  1322.  
  1323.         attack_flags = check_attacks();
  1324.         if ( attack_flags ) {
  1325.             do_attack( attack_flags );
  1326.             return;
  1327.         }
  1328.         
  1329.         waitFrame();
  1330.     }
  1331.     stopMove();
  1332. }
  1333.  
  1334. /*
  1335. =====================
  1336. char_sentry::find_attack_position
  1337. =====================
  1338. */
  1339. boolean char_sentry::find_attack_position() {
  1340.     float attack_flags;
  1341.  
  1342.     if ( sys.getTime() < nextPositionSearch ) {
  1343.         return false;
  1344.     }
  1345.     nextPositionSearch = sys.getTime() + 1;
  1346.     locateEnemy();
  1347.     moveToAttackPosition( getEnemy(), "range_attack_loop" );
  1348.     if ( AI_DEST_UNREACHABLE ) {
  1349.         return false;
  1350.     }
  1351.  
  1352.     while( !AI_MOVE_DONE ) {
  1353.         if ( AI_ENEMY_DEAD ) {
  1354.             startSound( "snd_target_lost", SND_CHANNEL_VOICE, false );
  1355.             AI_ENEMY_DEAD = false;
  1356.             clearEnemy();
  1357.             setState( "state_Idle" );
  1358.         }
  1359.  
  1360.         lookAtEnemy( 1 );
  1361.  
  1362.         attack_flags = check_attacks();
  1363.         if ( attack_flags ) {
  1364.             do_attack( attack_flags );
  1365.             return true;
  1366.         }
  1367.         
  1368.         waitFrame();
  1369.     }
  1370.     stopMove();
  1371.  
  1372.     return true;
  1373. }
  1374.  
  1375.  
  1376. /*
  1377. =====================
  1378. char_sentry::do_attack
  1379. =====================
  1380. */
  1381. void char_sentry::do_attack( float attack_flags ) {
  1382.     if ( attack_flags & ATTACK_MISSILE ) {
  1383.         combat_range();
  1384.     }
  1385. }
  1386.  
  1387. /*
  1388. =====================
  1389. char_sentry::check_attacks
  1390. =====================
  1391. */
  1392. float char_sentry::check_attacks() {
  1393.     float attack_flags = 0;
  1394.     
  1395.     if ( canHitEnemyFromAnim( "range_attack_loop" ) ) {
  1396.         attack_flags |= ATTACK_MISSILE;
  1397.     }
  1398.     
  1399.     return attack_flags;
  1400. }
  1401.  
  1402. /*
  1403. =====================
  1404. char_sentry::combat_range
  1405. =====================
  1406. */
  1407. void char_sentry::combat_range() {
  1408.     faceEnemy();
  1409.     fire = true;
  1410.     while( canHitEnemyFromAnim( "range_attack_loop" ) ) {
  1411.         waitFrame();
  1412.     }
  1413.     fire = false;
  1414.     waitUntil( !inAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack" ) );
  1415. }
  1416.