Guest User

JEDI ACADEMY SP OPENJK SUPER POWERFUL AI

a guest
Dec 17th, 2019
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 36.33 KB | None | 0 0
  1. //////////////////////////////////////////////////////////////////////////////////
  2. //////////////////// SPECIAL AI COMBAT FOR SHOOTERS NPC ////////////////////////////////////
  3. // WORKING ALPHA VERSION
  4.  
  5. //////////////////////////////////////////////////////////////////////////////////
  6. //////////////////// SPECIAL AI COMBAT FOR SHOOTERS NPC ////////////////////////////////////
  7. void        NPC_Precache( void );
  8. void        NPC_DustFallNear(const vec3_t origin, int dustcount);
  9. void        NPC_ChangeWeapon2(int wp);
  10. qboolean    NPC_StopKnockdown(gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown = qfalse);
  11. // Flight Related Functions (used Only by flying classes)
  12. //--------------------------------------------------------
  13. extern qboolean NPC_Flying( gentity_t *self );
  14. extern void     NPC_FlyStart( gentity_t *self );
  15. extern void     NPC_FlyStop( gentity_t *self );
  16. // Called From NPC_Pain()
  17. //-----------------------------
  18. void        NPC_Pain2( gentity_t *self, gentity_t *inflictor, int damage, int mod);
  19.  
  20.  
  21. // Local: Other Tactics
  22. //----------------------
  23. void        NPC_DoAmbushWait( gentity_t *self);
  24. void        NPC_DoSniper( gentity_t *self);
  25.  
  26. // Local: Respawning
  27. //-------------------
  28. bool        NPC_Respawn();
  29.  
  30. // Called From Within AI_Jedi && AI_Seeker
  31. //-----------------------------------------
  32. void        NPC_Fire();
  33. void        NPC_FireDecide();
  34.  
  35. // Local: Called From Tactics()
  36. //----------------------------
  37. void        NPC_TacticsSelect();
  38. bool        NPC_CanSeeEnemy( gentity_t *self );
  39.  
  40.  
  41. // Called From NPC_RunBehavior()
  42. //-------------------------------
  43. void        NPC_Update();       // Always Called First, Before Any Other Thinking
  44. bool        NPC_Tactics();      // If returns true, Jedi and Seeker AI not used
  45. bool        NPC_Flee();     // If returns true, Jedi and Seeker AI not used
  46. ////////////////////////////////////////////////////////////////////////////////////////
  47. // External Functions
  48. ////////////////////////////////////////////////////////////////////////////////////////
  49. extern void     G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast );
  50. extern void     G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
  51. extern void     ChangeWeapon( gentity_t *ent, int newWeapon );
  52. extern void     WP_ResistForcePush( gentity_t *self, gentity_t *pusher, qboolean noPenalty );
  53. extern void     ForceJump( gentity_t *self, usercmd_t *ucmd );
  54. extern void     G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
  55. extern void WP_SaberAddG2SaberModels( gentity_t *ent, int specificSaberNum );
  56. // DUSTY ADD
  57. extern qboolean heavyWeap(int);
  58. extern qboolean blasterWeap(int);
  59. extern int ChooseBestWeapon(gentity_t* ent);
  60. extern int ChooseWeaponRandom(gentity_t *ent, int wpnGroup);
  61. extern qboolean lightBlasterWeap(int);
  62. extern qboolean heavyBlasterWeap(int);
  63. extern qboolean meleeWeap(int);
  64.  
  65. ////////////////////////////////////////////////////////////////////////////////////////
  66. // Defines
  67. ////////////////////////////////////////////////////////////////////////////////////////
  68. #define     NPC_SHOOTRANGEMIN           512
  69. #define     NPC_SHOOTRANGEMED           1024 // DISTANCE FOR SWITCHING BY SABER TO SHOOTING
  70. #define     NPC_SHOOTRANGEMAX           65536
  71.  
  72. ////////////////////////////////////////////////////////////////////////////////////////
  73. // Global Data
  74. ////////////////////////////////////////////////////////////////////////////////////////
  75. bool    NPCHadDeathScript = false;
  76. bool    NPCActive = false;
  77. vec3_t  NPCFootStepLoc;
  78. int     NPCFootStepCount = 0;
  79. vec3_t  AverageEnemyDirection2;
  80. int     AverageEnemyDirectionSamples2;
  81.  
  82.  
  83.  
  84. ////////////////////////////////////////////////////////////////////////////////////////
  85. // NPC NEW AGILITY SYSTEM FOR AVOID ATTACKS!
  86. ////////////////////////////////////////////////////////////////////////////////////////
  87.  
  88. enum    NpcTacticsState
  89. {
  90.     NPC_NONE,
  91.  
  92.     // Attack
  93.     //--------
  94.     NPC_GLYPHIC,    // Replace Rifle, alternate medium distance atk
  95.     NPC_MAGIC,      // Replace Rifle, medium distance atk  
  96.     NPC_ARROWS,     // Replace sniper, large distance atk archers  
  97.     NPC_NEC_HOLY,   // replace missile, large distance atk
  98.     NPC_WARRIOR,    // atk for short distance > Glyphic magic
  99.     NPC_FENCER,      // saberist NPC!
  100.     NPC_BURNER, // cannonier NPC
  101.     /// Waiting
  102.     //---------
  103.     NPC_AMBUSHWAIT,     // Goto CP & Wait
  104.     NPC_MAX
  105. };
  106.  
  107. void NPC_Precache( void )
  108. {
  109.     G_SoundIndex( "sound/chars/boba/bf_blast-off.wav" );
  110.     G_SoundIndex( "sound/chars/boba/bf_jetpack_lp.wav" );
  111.     G_SoundIndex( "sound/chars/boba/bf_land.wav" );
  112.     G_SoundIndex( "sound/weapons/boba/bf_flame.mp3" );
  113.     G_SoundIndex( "sound/player/footsteps/boot1" );
  114.     G_SoundIndex( "sound/player/footsteps/boot2" );
  115.     G_SoundIndex( "sound/player/footsteps/boot3" );
  116.     G_SoundIndex( "sound/player/footsteps/boot4" );
  117.     G_EffectIndex( "boba/jetSP" );
  118.     G_EffectIndex( "boba/fthrw" );
  119.     G_EffectIndex( "volumetric/black_smoke" );
  120.     G_EffectIndex( "chunks/dustFall" );
  121.  
  122.     AverageEnemyDirectionSamples2 = 0;
  123.     VectorClear(AverageEnemyDirection2);
  124.     NPCHadDeathScript           = false;
  125.     NPCActive                   = true;
  126.     NPCFootStepCount            = 0;
  127. }
  128.  
  129. ////////////////////////////////////////////////////////////////////////////////////////
  130. //
  131. ////////////////////////////////////////////////////////////////////////////////////////
  132. void    NPC_DustFallNear(const vec3_t origin, int dustcount)
  133. {
  134.     if (!NPCActive)
  135.     {
  136.         return;
  137.     }
  138.  
  139.     trace_t     testTrace;
  140.     vec3_t      testDirection;
  141.     vec3_t      testStartPos;
  142.     vec3_t      testEndPos;
  143.  
  144.     VectorCopy(origin, testStartPos);
  145.     for (int i=0; i<dustcount; i++)
  146.     {
  147.         testDirection[0] = (random() * 2.0f) - 1.0f;
  148.         testDirection[1] = (random() * 2.0f) - 1.0f;
  149.         testDirection[2] = 1.0f;
  150.  
  151.         VectorMA(origin, 1000.0f, testDirection, testEndPos);
  152.         gi.trace (&testTrace, origin, NULL, NULL, testEndPos, (player && player->inuse)?(0):(ENTITYNUM_NONE), MASK_SHOT, (EG2_Collision)0, 0 );
  153.  
  154.         if (!testTrace.startsolid &&
  155.             !testTrace.allsolid &&
  156.             testTrace.fraction>0.1f &&
  157.             testTrace.fraction<0.9f)
  158.         {
  159.             G_PlayEffect( "chunks/dustFall", testTrace.endpos, testTrace.plane.normal );
  160.         }
  161.     }
  162. }
  163.  
  164. void NPC_ChangeWeapon2( int wp )
  165. {
  166.     if ( NPC->s.weapon == wp )
  167.     {
  168.         return;
  169.     }
  170.     NPC_ChangeWeapon2( wp );
  171.     G_AddEvent( NPC, EV_GENERAL_SOUND, G_SoundIndex( "sound/weapons/change.wav" ));
  172. }
  173.  
  174. qboolean NPC_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown )
  175. {
  176.     if ( self->client->NPC_class == CLASS_BOBAFETT )// for others, not for boba.
  177.     {
  178.         return qfalse;
  179.     }
  180.  
  181.     if ( self->client->moveType == MT_FLYSWIM )
  182.     {//can't knock me down when I'm flying
  183.         return qtrue;
  184.     }
  185.  
  186.     vec3_t  pDir, fwd, right, ang = {0, self->currentAngles[YAW], 0};
  187.     float   fDot, rDot;
  188.     int     strafeTime = Q_irand( 1000, 2000 );
  189.  
  190.     AngleVectors( ang, fwd, right, NULL );
  191.     VectorNormalize2( pushDir, pDir );
  192.     fDot = DotProduct( pDir, fwd );
  193.     rDot = DotProduct( pDir, right );
  194.  
  195.     if ( Q_irand( 0, 2 ) )
  196.     {//flip or roll with it
  197.         usercmd_t   tempCmd;
  198.         if ( fDot >= 0.4f )
  199.         {
  200.             tempCmd.forwardmove = 127;
  201.             TIMER_Set( self, "moveforward", strafeTime );
  202.         }
  203.         else if ( fDot <= -0.4f )
  204.         {
  205.             tempCmd.forwardmove = -127;
  206.             TIMER_Set( self, "moveback", strafeTime );
  207.         }
  208.         else if ( rDot > 0 )
  209.         {
  210.             tempCmd.rightmove = 127;
  211.             TIMER_Set( self, "strafeRight", strafeTime );
  212.             TIMER_Set( self, "strafeLeft", -1 );
  213.         }
  214.         else
  215.         {
  216.             tempCmd.rightmove = -127;
  217.             TIMER_Set( self, "strafeLeft", strafeTime );
  218.             TIMER_Set( self, "strafeRight", -1 );
  219.         }
  220.         G_AddEvent( self, EV_JUMP, 0 );
  221.         if ( !Q_irand( 0, 1 ) )
  222.         {//flip
  223.             self->client->ps.forceJumpCharge = 280;//FIXME: calc this intelligently?
  224.             //ForceJump( self, &tempCmd );
  225.         }
  226.         else
  227.         {//roll
  228.             TIMER_Set( self, "duck", strafeTime );
  229.         }
  230.         self->painDebounceTime = 0;//so we do something
  231.     }
  232.     else
  233.     {//fall down
  234.         return qfalse;
  235.     }
  236.  
  237.     return qtrue;
  238. }
  239.  
  240. qboolean NPC_Flying( gentity_t *self )
  241. {
  242.     assert(self && self->client && self->client->NPC_class==CLASS_BOBAFETT);//self->NPC &&
  243.    
  244.     // ONLY FOR FLYING CLASSES
  245.  
  246.     return ((qboolean)(self->client->moveType==MT_FLYSWIM));
  247. }
  248.  
  249. ////////////////////////////////////////////////////////////////////////////////////////
  250. //
  251. ////////////////////////////////////////////////////////////////////////////////////////
  252. bool    NPC_CanSeeEnemy( gentity_t *self )
  253. {
  254.     assert(self && self->NPC && self->client && self->client->NPC_class==CLASS_BOBAFETT ||
  255.         self && self->NPC && self->client && self->client->NPC_class==CLASS_SITH ||
  256.         self && self->NPC && self->client && self->client->NPC_class==CLASS_SITHTROOPER );
  257.     return ((level.time - self->NPC->enemyLastSeenTime)<1000);
  258. }
  259.  
  260. ////////////////////////////////////////////////////////////////////////////////////////
  261. //
  262. ////////////////////////////////////////////////////////////////////////////////////////
  263. void    NPC_Pain( gentity_t *self, gentity_t *inflictor, int damage, int mod)
  264. {
  265.     if ( mod==MOD_SABER || mod !=MOD_SABER /*!(NPCInfo->aiFlags&NPCAI_FLAMETHROW)*/)
  266.     {
  267.         TIMER_Set( self, "NPC_TacticsSelect", 0);   // Hurt By The Saber, Time To Try Something New
  268.     }
  269.     /*if (self->NPC->aiFlags&NPCAI_FLAMETHROW)
  270.     {
  271.         NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  272.         self->client->ps.torsoAnimTimer  =  level.time - TIMER_Get(self, "falmeTime");
  273.     }*/
  274. }
  275.  
  276. ////////////////////////////////////////////////////////////////////////////////////////
  277. //
  278. ////////////////////////////////////////////////////////////////////////////////////////
  279. void NPC_FlyStart( gentity_t *self )
  280. {//switch to seeker AI for a while
  281.     if ( TIMER_Done( self, "flyRecharge" )
  282.         && !NPC_Flying( self ) )
  283.     {
  284.         self->client->ps.gravity = 0;
  285.         self->svFlags |= SVF_CUSTOM_GRAVITY;
  286.         self->client->moveType = MT_FLYSWIM;
  287.         //start jet effect
  288.         self->client->jetPackTime = level.time + Q_irand( 3000, 10000 );
  289.         //take-off sound
  290.         // CASE SWITCH FOR CLASS (different sound for different fly ways)
  291.         G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/boba/bf_blast-off.wav" );
  292.         //jet loop sound
  293.         self->s.loopSound = G_SoundIndex( "sound/chars/boba/bf_jetpack_lp.wav" );
  294.        
  295.         if ( self->NPC )
  296.         {
  297.             self->count = Q3_INFINITE; // SEEKER shot ammo count
  298.         }
  299.     }
  300. }
  301.  
  302. ////////////////////////////////////////////////////////////////////////////////////////
  303. //
  304. ////////////////////////////////////////////////////////////////////////////////////////
  305. void NPC_FlyStop( gentity_t *self )
  306. {
  307.     self->client->ps.gravity = g_gravity->value;
  308.     self->svFlags &= ~SVF_CUSTOM_GRAVITY;
  309.     self->client->moveType = MT_RUNJUMP;
  310.     //Stop effect
  311.     self->client->jetPackTime = 0;
  312.    
  313.     //stop jet loop sound
  314.     G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/boba/bf_land.wav" );
  315.  
  316.     self->s.loopSound = 0;
  317.     if ( self->NPC )
  318.     {
  319.         self->count = 0; // SEEKER shot ammo count
  320.         TIMER_Set( self, "flyRecharge", Q_irand( 1000, 5000 ) );
  321.         //TIMER_Set( self, "flyChaseDebounce", Q_irand( 500, 2000 ) );// seems not used
  322.     }
  323. }
  324.  
  325. ////////////////////////////////////////////////////////////////////////////////////////
  326. //
  327. ////////////////////////////////////////////////////////////////////////////////////////
  328. void        NPC_DoAmbushWait( gentity_t *self)
  329. {
  330.   NPC_TacticsSelect();
  331. }
  332.  
  333. ////////////////////////////////////////////////////////////////////////////////////////
  334. //
  335. ////////////////////////////////////////////////////////////////////////////////////////
  336. void        NPC_DoSniper( gentity_t *self)
  337. {
  338.     if (TIMER_Done(NPC, "PickNewSniperPoint"))
  339.     {
  340.         TIMER_Set(NPC, "PickNewSniperPoint", Q_irand(15000, 25000));
  341.         int     SniperPoint = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_SNIPE|CP_CLEAR|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
  342.         if (SniperPoint!=-1)
  343.         {
  344.             NPC_SetCombatPoint(SniperPoint);
  345.             NPC_SetMoveGoal( NPC, level.combatPoints[SniperPoint].origin, 20, qtrue, SniperPoint );
  346.         }
  347.     }
  348.  
  349.     if (Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<50.0f)
  350.     {
  351.         NPC_FireDecide();
  352.     }
  353.     else
  354.     {
  355.         NPC_FireDecide();
  356.     }
  357.  
  358.     bool    IsOnAPath = !!NPC_MoveToGoal(qtrue);
  359.  
  360.     // Resolve Blocked Problems
  361.     //--------------------------
  362.     if (NPCInfo->aiFlags&NPCAI_BLOCKED &&
  363.         NPC->client->moveType!=MT_FLYSWIM &&
  364.         ((level.time - NPCInfo->blockedDebounceTime)>3000)
  365.         )
  366.     {
  367.         ////NPC_Printf("BLOCKED: Attempting Jump");
  368.         if (IsOnAPath)
  369.         {
  370.             if (!NPC_TryJump(NPCInfo->blockedTargetPosition))
  371.             {
  372. //              //NPC_Printf("  Failed");
  373.             }
  374.         }
  375.     }
  376.  
  377.     NPC_FaceEnemy(qtrue);
  378.     NPC_UpdateAngles( qtrue, qtrue );
  379. }
  380.  
  381.  
  382. ////////////////////////////////////////////////////////////////////////////////////////
  383. // Call This function to make Boba actually shoot his current weapon
  384. ////////////////////////////////////////////////////////////////////////////////////////
  385. void    NPC_Fire()
  386. {
  387.     WeaponThink(qtrue);
  388.  
  389.     // If Actually Fired, Decide To Apply Alt Fire And Calc Next Attack Delay
  390.     //------------------------------------------------------------------------
  391.     if (ucmd.buttons&BUTTON_ATTACK)
  392.     {
  393.         // DUSTY ADD - THIS WORK WITHOUT TROUBLE!
  394.  
  395.         if (heavyWeap(NPC->s.weapon))
  396.         {
  397.         if (NPC->s.weapon == WP_ROCKET_LAUNCHER)
  398.                 TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 2000));
  399.  
  400.             // Occasionally Shoot A Homing Missile
  401.             //-------------------------------------
  402.             if (!Q_irand(0, 3))
  403.             {
  404.                 ucmd.buttons &= ~BUTTON_ATTACK;
  405.                 ucmd.buttons |= BUTTON_ALT_ATTACK;
  406.                 if (NPC->s.weapon == WP_ROCKET_LAUNCHER)
  407.                     NPC->client->fireDelay = Q_irand(1000, 3000);
  408.             }
  409.                        
  410.         }
  411.         else if ( NPC->s.weapon == WP_TUSKEN_RIFLE || NPC->s.weapon == WP_STUN_BATON ||
  412.         NPC->s.weapon == WP_NOGHRI_STICK )
  413.         {
  414.             NPC->client->fireDelay = Q_irand(1000, 3000); // NO ALT FIRE FOR THESE WEAPONS
  415.         }
  416.         else if (NPC->s.weapon == WP_DISRUPTOR)
  417.         {
  418.             TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 4000));
  419.  
  420.             // Occasionally Alt-Fire
  421.             //-----------------------
  422.  
  423.             if (!Q_irand(0, 3))
  424.             {
  425.                 ucmd.buttons &= ~BUTTON_ATTACK;
  426.                 ucmd.buttons |= BUTTON_ALT_ATTACK;
  427.                 NPC->client->fireDelay = Q_irand(1000, 3000);;
  428.             }
  429.         }
  430.         else
  431.         {//blaster-type weapon
  432.  
  433.             if (TIMER_Done(NPC, "nextBlasterAltFireDecide"))
  434.             {
  435.                 if (Q_irand(0, (NPC->count * 2) + 3)>2)
  436.                 {
  437.                     TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(3000, 8000));
  438.                     if (!(NPCInfo->scriptFlags&SCF_ALT_FIRE))
  439.                     {
  440. //                      //NPC_Printf("ALT FIRE On");
  441.                         NPCInfo->scriptFlags |= SCF_ALT_FIRE;
  442.                         NPC_ChangeWeapon(NPC->s.weapon);                    // Update Delay Timers
  443.                     }
  444.                 }
  445.                 else
  446.                 {
  447.                     TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(2000, 5000));
  448.                     if ( (NPCInfo->scriptFlags&SCF_ALT_FIRE))
  449.                     {
  450. //                      //NPC_Printf("ALT FIRE Off");
  451.                         NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
  452.                         NPC_ChangeWeapon(NPC->s.weapon);            // Update Delay Timers
  453.                     }
  454.                 }
  455.             }
  456.  
  457.             // Occasionally Alt Fire
  458.             //-----------------------
  459.             if ((NPCInfo->scriptFlags&SCF_ALT_FIRE))
  460.             {
  461.                 ucmd.buttons &= ~BUTTON_ATTACK;
  462.                 ucmd.buttons |= BUTTON_ALT_ATTACK;
  463.             }
  464.         }
  465.  
  466.  
  467.  
  468.     }
  469. }
  470.  
  471.  
  472. ////////////////////////////////////////////////////////////////////////////////////////
  473. // Call this function to see if Fett should fire his current weapon
  474. ////////////////////////////////////////////////////////////////////////////////////////
  475. //helper function
  476. qboolean isShooterClass(int className)
  477. {
  478.     if (NPC->client->NPC_class != CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_SITH || NPC->client->NPC_class == CLASS_COMMANDO ||
  479.         NPC->client->NPC_class == CLASS_SITHTROOPER )// Add other special classes )
  480.         return qtrue;
  481.  
  482.     return qfalse;
  483. }
  484.  
  485. void NPC_FireDecide( void )
  486. {
  487.     // Any Reason Not To Shoot?
  488.     //--------------------------
  489.     if (!NPC ||                                         // Only NPCs
  490.         !NPC->client ||                                 // Only Clients
  491.          //NPC->client->NPC_class!=CLASS_BOBAFETT ||        // Only Boba
  492.         !isShooterClass(NPC->client->NPC_class) ||          // Only Boba
  493.         !NPC->enemy ||                                  // Only If There Is An Enemy
  494.         NPC->s.weapon == WP_NONE ||             // Only If Using A Valid Weapon
  495.         !TIMER_Done(NPC, "nextAttackDelay") ||          // Only If Ready To Shoot Again
  496.         !NPC_CanSeeEnemy(NPC)       // Only If Enemy Recently Seen
  497.         )
  498.     {
  499.         return;
  500.     }
  501.  
  502.     // Now Check Weapon Specific Parameters To See If We Should Shoot Or Not
  503.     //-----------------------------------------------------------------------
  504.     // GOOD CODE
  505.     // DUSTY VARIANTE
  506.     if (heavyWeap(NPC->s.weapon) || blasterWeap(NPC->s.weapon) ||  
  507.         lightBlasterWeap(NPC->s.weapon) || heavyBlasterWeap(NPC->s.weapon)||
  508.         meleeWeap(NPC->s.weapon))
  509.     {
  510.    
  511.     // HERE PUT THE SWITCH FOR CLASS RAZIEL SWORD  
  512.     if (Distance(NPC->currentOrigin, NPC->enemy->currentOrigin) > 400.0f)
  513.         {
  514.             NPC_Fire();
  515.         }
  516.         else
  517.         {
  518.             NPC_Fire();
  519.         }
  520.     }
  521.     else
  522.     {
  523.         NPC_Fire();
  524.     }
  525. }
  526.  
  527. ////////////////////////////////////////////////////////////////////////////////////////
  528. // Tactics avaliable to Boba Fett:
  529. // --------------------------------
  530. //  NPC_MAGIC,          // Uses arcane magic, medium range
  531. //  NPC_GLYPHIC,        // Uses Glyphic weapon, medium range
  532. //  NPC_ARROWS,         // Uses Arrows, large range of atk
  533. //  NPC_NEC_HOLY,       // Uses powerful magic, far range
  534. //  NPC_WARRIOR,        // Use melee atk.
  535. //  NPC_AMBUSHWAIT,     // Goto CP & Wait
  536. //
  537. extern void WP_DeactivateSaber( gentity_t *self, qboolean clearLength );
  538. ////////////////////////////////////////////////////////////////////////////////////////
  539. void    NPC_TacticsSelect()
  540. {
  541.     // Don't Change Tactics For A Little While
  542.     //------------------------------------------
  543.     TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(8000, 15000));
  544.     int     nextState = NPCInfo->localState;
  545.  
  546.  
  547.     // Get Some Data That Will Help With The Selection Of The Next Tactic
  548.     //--------------------------------------------------------------------
  549.     bool    enemyAlive2         = (NPC->enemy->health>0);
  550.     float   enemyDistance2      = Distance(NPC->currentOrigin, NPC->enemy->currentOrigin);
  551.     bool    enemyCloseRange2    = (enemyDistance2<NPC_SHOOTRANGEMIN);
  552.     bool    enemyInShootRange2  = (enemyDistance2>NPC_SHOOTRANGEMIN && enemyDistance2<NPC_SHOOTRANGEMAX);
  553.     bool    enemyRecentlySeen2  = NPC_CanSeeEnemy(NPC);
  554.    
  555.     if (!enemyAlive2)
  556.     {
  557.         nextState = NPC_MAGIC;
  558.     }
  559.     else if (enemyCloseRange2)// Enemy is pretty near, so use Saber!  
  560.     {
  561.         if ( HaveWeapon(NPC, WP_SABER ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
  562.         {
  563.             nextState = NPC_FENCER;
  564.         }
  565.         else if ( HaveWeapon(NPC, WEAPS_MELEE ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
  566.         {
  567.             nextState = NPC_WARRIOR;
  568.         }
  569.                
  570.         // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy
  571.         //-----------------------------------------------------------------------------
  572.         //if (TIMER_Done(NPC, "nextFlameDelay"))
  573.         // DUSTY ADD
  574.         else if (TIMER_Done(NPC, "nextFlameDelay") && (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_SITH || NPC->client->NPC_class == CLASS_SITHTROOPER ))
  575.         {
  576.             nextState = NPC_GLYPHIC;
  577.         }
  578.  
  579.         // Otherwise, He's Probably Too Close, So Try To Get Clear Of Him
  580.         //----------------------------------------------------------------
  581.         else
  582.         {
  583.             nextState = NPC_MAGIC;
  584.         }
  585.     }
  586.     else if (enemyInShootRange2)// Enemy is distance so use shoot atk
  587.     {
  588.        
  589.     if ( HaveWeapon(NPC, WP_SABER ) || HaveWeapon(NPC, WP_MELEE ) ||
  590.         HaveWeapon(NPC, WP_TUSKEN_STAFF ) || HaveWeapon(NPC, WP_STUN_BATON ))
  591.         // You are a warrior and have a sword, but enemy is far and at shoot distance
  592.         // Put away the sword and use shoot weapons!  
  593.     {
  594.         if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
  595.         {
  596.             nextState = NPC_MAGIC;
  597.         }
  598.         else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
  599.         {
  600.             nextState = NPC_GLYPHIC;
  601.         }
  602.         else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!  
  603.         {
  604.             nextState = NPC_ARROWS;
  605.         }
  606.         else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!  
  607.         {
  608.             nextState = NPC_NEC_HOLY;
  609.         }  
  610.     }  
  611.     else
  612.     { // If you have shooting weapons, use that!
  613.         if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
  614.         {
  615.             nextState = NPC_MAGIC;
  616.         }
  617.         else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
  618.         {
  619.             nextState = NPC_GLYPHIC;
  620.         }
  621.         else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!  
  622.         {
  623.             nextState = NPC_ARROWS;
  624.         }
  625.         else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!  
  626.         {
  627.             nextState = NPC_NEC_HOLY;
  628.         }
  629.         else if ( HaveWeapon(NPC, WEAPS_MELEE ))// this is really stupid but okay...  
  630.         {
  631.             nextState = NPC_WARRIOR;
  632.         }
  633.     }
  634.        
  635.     }
  636.     else if (!enemyInShootRange2 && !enemyCloseRange2) // Enemy is pretty far, try distance atk
  637.     {
  638.         // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy
  639.         //-----------------------------------------------------------------------------
  640.         //if (TIMER_Done(NPC, "nextFlameDelay"))
  641.         // DUSTY ADD
  642.         if ( HaveWeapon(NPC, WP_SABER ) || HaveWeapon(NPC, WP_MELEE ) ||
  643.         HaveWeapon(NPC, WP_TUSKEN_STAFF ) || HaveWeapon(NPC, WP_STUN_BATON ))
  644.         // You are a warrior and have a sword, but enemy is far and at shoot distance
  645.         // Put away the sword and use shoot weapons!  
  646.     {
  647.         if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
  648.         {
  649.             nextState = NPC_MAGIC;
  650.         }
  651.         else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
  652.         {
  653.             nextState = NPC_GLYPHIC;
  654.         }
  655.         else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!  
  656.         {
  657.             nextState = NPC_ARROWS;
  658.         }
  659.         else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!  
  660.         {
  661.             nextState = NPC_NEC_HOLY;
  662.         }  
  663.     }  
  664.     else
  665.     { // If you have shooting weapons, use that!
  666.         if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
  667.         {
  668.             nextState = NPC_MAGIC;
  669.         }
  670.         else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
  671.         {
  672.             nextState = NPC_GLYPHIC;
  673.         }
  674.         else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!  
  675.         {
  676.             nextState = NPC_ARROWS;
  677.         }
  678.         else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!  
  679.         {
  680.             nextState = NPC_NEC_HOLY;
  681.         }
  682.         else if ( HaveWeapon(NPC, WEAPS_MELEE ))// this is really stupid but okay...  
  683.         {
  684.             nextState = NPC_WARRIOR;
  685.         }
  686.     }
  687.            
  688.    
  689.     }
  690.  
  691.  
  692.     // Recently Saw The Enemy, Time For Some Good Ole Fighten!
  693.     //---------------------------------------------------------
  694.     else if (enemyRecentlySeen2)
  695.     {
  696.         // At First, Boba will prefer to use his blaster against the player, but
  697.         //  the more times he is driven away (NPC->count), he will be less likely to
  698.         //  choose the blaster, and more likely to go for the missile launcher
  699.         if (!enemyCloseRange2 && NPC->client->ps.weapon == WP_SABER )
  700.         {
  701.           nextState = NPC_MAGIC;
  702.         }
  703.         nextState = (!enemyCloseRange2 || Q_irand(0, NPC->count)<1)?(NPC_FENCER):(NPC_MAGIC);  
  704.         //nextState = (!enemyInShootRange || Q_irand(0, NPC->count)<1)?(NPC_MAGIC):(NPC_FENCER);
  705.  
  706.  
  707.     }
  708.  
  709.     // Hmmm...  Havn't Seen The Player In A While, We Might Want To Try Something Sneaky
  710.     //-----------------------------------------------------------------------------------
  711.     else
  712.     {
  713.         bool    SnipePointsNear = false;         // TODO
  714.         bool    AmbushPointNear = false;         // TODO
  715.  
  716.         if (Q_irand(0, NPC->count)>0)
  717.         {
  718.             int     SniperPoint = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_SNIPE|CP_CLEAR|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
  719.             if (SniperPoint!=-1)
  720.             {
  721.                 NPC_SetCombatPoint(SniperPoint);
  722.                 NPC_SetMoveGoal( NPC, level.combatPoints[SniperPoint].origin, 20, qtrue, SniperPoint );
  723.                 TIMER_Set(NPC, "PickNewSniperPoint", Q_irand(15000, 25000));
  724.                 SnipePointsNear = true;
  725.             }
  726.         }
  727.  
  728.         // DUSTY ADD
  729.         //if (SnipePointsNear && TIMER_Done(NPC, "NPC_NoSniperTime"))
  730.         if (SnipePointsNear && TIMER_Done(NPC, "NPC_NoSniperTime") && HaveWeapon(NPC, WP_DISRUPTOR))
  731.         {
  732.             TIMER_Set(NPC, "NPC_NoSniperTime", 120000);             // Don't snipe again for a while
  733.             TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(35000, 45000));// More patience here
  734.             nextState = NPC_ARROWS;
  735.         }
  736.         else if (AmbushPointNear)
  737.         {
  738.             TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(15000, 25000));// More patience here
  739.             nextState = NPC_AMBUSHWAIT;
  740.         }
  741.         else
  742.         {
  743.             nextState = (!enemyInShootRange2 || Q_irand(0, NPC->count)<1)?(NPC_GLYPHIC):(NPC_ARROWS);
  744.         }
  745.     }
  746.  
  747.  
  748.  
  749.     // The Next State Has Been Selected, Now Change Weapon If Necessary
  750.     //------------------------------------------------------------------
  751.     // DUSTY ADD VERSION
  752.     // The Next State Has Been Selected, Now Change Weapon If Necessary
  753.     //------------------------------------------------------------------
  754.     NPCInfo->localState = nextState;
  755.     int weapon = 0;
  756.  
  757.     switch (NPCInfo->localState)
  758.     {
  759.     case NPC_FENCER:
  760.         if (HaveWeapon(NPC, WP_SABER))
  761.         {
  762.             //NPC_Printf("NEW TACTIC: Sniper");
  763.             NPC_ChangeWeapon(WP_SABER);
  764.             break;
  765.         }
  766.        
  767.     case NPC_MAGIC:
  768.         weapon = ChooseWeaponRandom(NPC, WEAPS_BLASTER);
  769.         if (weapon)
  770.         {
  771.             //NPC_Printf("NEW TACTIC: Rifle");
  772.             NPC_ChangeWeapon(weapon);
  773.             break;
  774.         }
  775.     case NPC_NEC_HOLY:
  776.         weapon = ChooseWeaponRandom(NPC, WEAPS_HEAVYBLASTER);
  777.         if (weapon)
  778.         {
  779.             //NPC_Printf("NEW TACTIC: Rocket Launcher");
  780.             NPC_ChangeWeapon(weapon);
  781.             break;
  782.         }
  783.        
  784.     case NPC_GLYPHIC: //kinda stuck at this point if doesn't have flamethrower
  785.         //NPC_Printf("NEW TACTIC: Flame Thrower");
  786.         weapon = ChooseWeaponRandom(NPC, WEAPS_HEAVY);
  787.         if (weapon)
  788.         {
  789.             //NPC_Printf("NEW TACTIC: Rocket Launcher");
  790.             NPC_ChangeWeapon(weapon);
  791.             break;
  792.         }
  793.     case NPC_WARRIOR: //kinda stuck at this point if doesn't have flamethrower
  794.         //NPC_Printf("NEW TACTIC: Flame Thrower");
  795.         weapon = ChooseWeaponRandom(NPC, WEAPS_MELEE);
  796.         if (weapon)
  797.         {
  798.             //NPC_Printf("NEW TACTIC: Rocket Launcher");
  799.             NPC_ChangeWeapon(weapon);
  800.             break;
  801.         }
  802.     case NPC_ARROWS: //kinda stuck at this point if doesn't have flamethrower
  803.         //NPC_Printf("NEW TACTIC: Flame Thrower");
  804.         weapon = ChooseWeaponRandom(NPC, WEAPS_LIGHTBLASTER);
  805.         if (weapon)
  806.         {
  807.             //NPC_Printf("NEW TACTIC: Rocket Launcher");
  808.             NPC_ChangeWeapon(weapon);
  809.             break;
  810.         }
  811.        
  812.     case NPC_AMBUSHWAIT:
  813.         //NPC_Printf("NEW TACTIC: Ambush");
  814.         weapon = ChooseWeaponRandom(NPC, WEAPS_ALL);
  815.         if (weapon)
  816.         {
  817.             //NPC_Printf("NEW TACTIC: Rocket Launcher");
  818.             NPC_ChangeWeapon(weapon);
  819.             break;
  820.         }
  821.     }  
  822.  }
  823.  
  824. ////////////////////////////////////////////////////////////////////////////////////////
  825. // Tactics
  826. //
  827. // This function is called right after Update()
  828. // If returns true, Jedi and Seeker AI not used for movement
  829. //
  830. ////////////////////////////////////////////////////////////////////////////////////////
  831. bool    NPC_Tactics()
  832. {
  833.     if (!NPC->enemy)
  834.     {
  835.         return false;
  836.     }
  837.  
  838.     // Think About Changing Tactics
  839.     //------------------------------
  840.     if (TIMER_Done(NPC, "NPC_TacticsSelect"))
  841.     {
  842.         NPC_TacticsSelect();
  843.     }
  844.  
  845.     // These Tactics Require Seeker & Jedi Movement
  846.     //----------------------------------------------
  847.     if (!NPCInfo->localState ||
  848.          NPCInfo->localState==NPC_MAGIC ||
  849.          NPCInfo->localState==NPC_NEC_HOLY ||
  850.          NPCInfo->localState==NPC_FENCER||
  851.          NPCInfo->localState==NPC_ARROWS ||
  852.          NPCInfo->localState==NPC_WARRIOR ||
  853.          NPCInfo->localState==NPC_GLYPHIC )
  854.     {
  855.         return false;
  856.     }
  857.  
  858.     // Flame Thrower - Locked In Place
  859.     //---------------------------------
  860.     if (NPCInfo->localState==NPC_GLYPHIC )
  861.     {
  862.         NPC_TacticsSelect();
  863.     }
  864.  
  865.     // Sniper - Move Around, And Take Shots
  866.     //--------------------------------------
  867.     else if (NPCInfo->localState==NPC_ARROWS)
  868.     {
  869.         NPC_DoSniper( NPC );
  870.     }
  871.  
  872.     // Ambush Wait
  873.     //------------
  874.     else if (NPCInfo->localState==NPC_AMBUSHWAIT)
  875.     {
  876.         NPC_DoAmbushWait( NPC );
  877.     }
  878.  
  879.  
  880.     NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
  881.     NPC_UpdateAngles(qtrue, qtrue);
  882.  
  883.     return true;            // Do Not Use Normal Jedi Or Seeker Movement
  884. }
  885.  
  886.  
  887. ////////////////////////////////////////////////////////////////////////////////////////
  888. //
  889. ////////////////////////////////////////////////////////////////////////////////////////
  890. bool    NPC_Respawn()
  891. {
  892.     int cp = -1;
  893.  
  894.     // Try To Predict Where The Enemy Is Going
  895.     //-----------------------------------------
  896.     if (AverageEnemyDirectionSamples2 && NPC->behaviorSet[BSET_DEATH]==0)
  897.     {
  898.         vec3_t  endPos;
  899.         VectorMA(NPC->enemy->currentOrigin, 1000.0f / (float)AverageEnemyDirectionSamples2, AverageEnemyDirection2, endPos);
  900.         cp = NPC_FindCombatPoint(endPos, 0, endPos, CP_FLEE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
  901.         //NPC_Printf("Attempting Predictive Spawn Point");
  902.     }
  903.  
  904.     // If That Failed, Try To Go Directly To The Enemy
  905.     //-------------------------------------------------
  906.     if (cp==-1)
  907.     {
  908.         cp = NPC_FindCombatPoint(NPC->enemy->currentOrigin, 0, NPC->enemy->currentOrigin, CP_FLEE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
  909.         //NPC_Printf("Attempting Closest Current Spawn Point");
  910.     }
  911.  
  912.     // If We've Found One, Go There
  913.     //------------------------------
  914.     if (cp!=-1)
  915.     {
  916.         NPC_SetCombatPoint( cp );
  917.         NPCInfo->surrenderTime = 0;
  918.         NPC->health = NPC->max_health;
  919.         NPC->svFlags &=~SVF_NOCLIENT;
  920.         NPC->count ++;                                      // This is the number of times spawned
  921.         G_SetOrigin(NPC, level.combatPoints[cp].origin);
  922.  
  923.         AverageEnemyDirectionSamples2 = 0;
  924.         VectorClear(AverageEnemyDirection2);
  925.  
  926.         //NPC_Printf("Found Spawn Point (%d)", cp);
  927.         return true;
  928.     }
  929.  
  930.     assert(0);  // Yea, that's bad...
  931.     //NPC_Printf("FAILED TO FIND SPAWN POINT");
  932.     return false;
  933. }
  934.  
  935.  
  936. ////////////////////////////////////////////////////////////////////////////////////////
  937. //
  938. ////////////////////////////////////////////////////////////////////////////////////////
  939. void    NPC_Update()
  940. {
  941.    
  942.     // Hey, That Tests The Trace All The Time
  943.     //----------------------------------------------------
  944.     if (NPC->enemy)
  945.     {
  946.         // Never Forget The Player... Never. NEVER!!!
  947.         NPC->svFlags                |= SVF_LOCKEDENEMY; // Don't forget about the enemy once you've found him
  948.         if (!(NPC->svFlags&SVF_NOCLIENT))
  949.         {
  950.             trace_t     testTrace;
  951.             vec3_t      eyes;
  952.             CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
  953.             gi.trace (&testTrace, eyes, NULL, NULL, NPC->enemy->currentOrigin, NPC->s.number, MASK_SHOT, (EG2_Collision)0, 0);
  954.  
  955.             bool    wasSeen = NPC_CanSeeEnemy(NPC);
  956.  
  957.             if (!testTrace.startsolid &&
  958.                 !testTrace.allsolid &&
  959.                 testTrace.entityNum == NPC->enemy->s.number)
  960.             {
  961.                 NPCInfo->enemyLastSeenTime  = level.time;
  962.                 NPCInfo->enemyLastHeardTime = level.time;
  963.                 VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation);
  964.                 VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
  965.             }
  966.             else if (gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin))
  967.             {
  968.                 NPCInfo->enemyLastHeardTime = level.time;
  969.                 VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
  970.             }
  971.         }
  972.  
  973.         if (!NPCInfo->surrenderTime)
  974.         {
  975.             if ((level.time - NPCInfo->enemyLastSeenTime)>20000 && TIMER_Done(NPC, "TooLongGoneRespawn"))
  976.             {
  977.                 TIMER_Set(NPC, "TooLongGoneRespawn", 30000);    // Give him some time to get to you before trying again
  978.                 //NPC_Printf("Gone Too Long, Attempting Respawn Even Though Not Hiding");
  979.                 NPC_Respawn();
  980.             }
  981.         }
  982.     }
  983.  
  984.  
  985.     // Make Sure He Always Appears In The Last Area With Full Health When His Death Script Is Turned On
  986.     //--------------------------------------------------------------------------------------------------
  987.     if (!NPCHadDeathScript && NPC->behaviorSet[BSET_DEATH]!=0)
  988.     {
  989.         if (!gi.inPVS(NPC->enemy->currentOrigin, NPC->currentOrigin))
  990.         {
  991.             //NPC_Printf("Attempting Final Battle Spawn...");
  992.             if (NPC_Respawn())
  993.             {
  994.                 NPCHadDeathScript = true;
  995.             }
  996.             else
  997.             {
  998.                 //NPC_Printf("Failed");
  999.             }
  1000.         }
  1001.     }
  1002.    
  1003.     // Occasionally A Jump Turns Into A Rocket Fly
  1004.     //---------------------------------------------
  1005.     if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE
  1006.         && NPC->client->ps.forceJumpZStart && NPC->client->NPC_class == CLASS_ROCKETTROOPER
  1007.         && !Q_irand( 0, 10 ) )
  1008.     {//take off
  1009.         NPC_FlyStart( NPC );
  1010.     }
  1011.  
  1012.  
  1013.     // If Hurting, Try To Run Away
  1014.     //-----------------------------
  1015.     if (!NPCInfo->surrenderTime && (NPC->health<NPC->max_health/10))
  1016.     {
  1017.         //NPC_Printf("Time To Surrender, Searching For Flee Point");
  1018.  
  1019.  
  1020.         // Find The Closest Flee Point That I Can Get To
  1021.         //-----------------------------------------------
  1022.         int cp = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_FLEE|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
  1023.         if (cp!=-1)
  1024.         {
  1025.             NPC_SetCombatPoint( cp );
  1026.             NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp );
  1027.             if (NPC->count<6)
  1028.             {
  1029.                 NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000) + 1000*(6-NPC->count);
  1030.             }
  1031.             else
  1032.             {
  1033.                 NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000);
  1034.             }
  1035.         }
  1036.         else
  1037.         {
  1038.             //NPC_Printf("  Failure");
  1039.         }
  1040.     }
  1041. }
  1042.  
  1043.  
  1044.  
  1045.  
  1046. ////////////////////////////////////////////////////////////////////////////////////////
  1047. //
  1048. ////////////////////////////////////////////////////////////////////////////////////////
  1049. bool    NPC_Flee()
  1050. {
  1051.     bool    EnemyRecentlySeen   = ((level.time - NPCInfo->enemyLastSeenTime)<10000);
  1052.     bool    ReachedEscapePoint  = (Distance(level.combatPoints[NPCInfo->combatPoint].origin, NPC->currentOrigin)<50.0f);
  1053.     bool    HasBeenGoneEnough   = (level.time>NPCInfo->surrenderTime || (level.time - NPCInfo->enemyLastSeenTime)>400000);
  1054.  
  1055.  
  1056.     // Is It Time To Come Back For Some More?
  1057.     //----------------------------------------
  1058.     if (!EnemyRecentlySeen || ReachedEscapePoint)
  1059.     {
  1060.         NPC->svFlags |= SVF_NOCLIENT;
  1061.         if (HasBeenGoneEnough)
  1062.         {
  1063.             if ((level.time - NPCInfo->enemyLastSeenTime)>400000)
  1064.             {
  1065.                 //NPC_Printf("  Gone Too Long, Attempting Respawn");
  1066.             }
  1067.  
  1068.             if (NPC_Respawn())
  1069.             {
  1070.                 return true;
  1071.             }
  1072.         }
  1073.         else if (ReachedEscapePoint && (NPCInfo->surrenderTime - level.time)>3000)
  1074.         {
  1075.             if (TIMER_Done(NPC, "SpookPlayerTimer"))
  1076.             {
  1077.                 vec3_t      testDirection;
  1078.                 TIMER_Set(NPC, "SpookPlayerTimer", Q_irand(2000, 10000));
  1079.                 switch(Q_irand(0, 1))
  1080.                 {
  1081.                 case 0:
  1082.                     //NPC_Printf("SPOOK: Dust");
  1083.                     NPC_DustFallNear(NPC->enemy->currentOrigin, Q_irand(1,2));
  1084.                     break;
  1085.  
  1086.                 case 1:
  1087.                     //NPC_Printf("SPOOK: Footsteps");
  1088.                     testDirection[0] =  (random() * 0.5f) - 1.0f;
  1089.                     testDirection[0] += (testDirection[0]>0.0f)?(0.5f):(-0.5f);
  1090.                     testDirection[1] = (random() * 0.5f) - 1.0f;
  1091.                     testDirection[1] += (testDirection[1]>0.0f)?(0.5f):(-0.5f);
  1092.                     testDirection[2] = 1.0f;
  1093.                     VectorMA(NPC->enemy->currentOrigin, 400.0f, testDirection, NPCFootStepLoc);
  1094.  
  1095.                     NPCFootStepCount = Q_irand(3,8);
  1096.                     break;
  1097.                 }
  1098.             }
  1099.  
  1100.             if (NPCFootStepCount && TIMER_Done(NPC, "NPCFootStepFakeTimer"))
  1101.             {
  1102.                 TIMER_Set(NPC, "NPCFootStepFakeTimer", Q_irand(300, 800));
  1103.                 NPCFootStepCount --;
  1104.                 G_SoundAtSpot(NPCFootStepLoc, G_SoundIndex(va("sound/player/footsteps/boot%d", Q_irand(1,4))), qtrue);
  1105.             }
  1106.  
  1107.             if (TIMER_Done(NPC, "ResampleEnemyDirection") && NPC->enemy->resultspeed>10.0f)
  1108.             {
  1109.                 TIMER_Set(NPC, "ResampleEnemyDirection", Q_irand(500, 1000));
  1110.                 AverageEnemyDirectionSamples2 ++;
  1111.  
  1112.                 vec3_t  moveDir;
  1113.                 VectorCopy(NPC->enemy->client->ps.velocity, moveDir);
  1114.                 VectorNormalize(moveDir);
  1115.  
  1116.                 VectorAdd(AverageEnemyDirection2, moveDir, AverageEnemyDirection2);
  1117.             }
  1118.         }
  1119.     }
  1120.     else
  1121.     {
  1122.         NPCInfo->surrenderTime += 100;
  1123.     }
  1124.  
  1125.     // Finish The Flame Thrower First...
  1126.     //-----------------------------------
  1127.     if (NPCInfo->aiFlags&NPCAI_FLAMETHROW)
  1128.     {
  1129.         NPC_TacticsSelect();
  1130.         NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
  1131.         NPC_UpdateAngles(qtrue, qtrue);
  1132.         return true;
  1133.     }
  1134.  
  1135.     bool    IsOnAPath = !!NPC_MoveToGoal(qtrue);
  1136.     if (!ReachedEscapePoint &&
  1137.         NPCInfo->aiFlags&NPCAI_BLOCKED &&
  1138.         NPC->client->moveType!=MT_FLYSWIM &&
  1139.         ((level.time - NPCInfo->blockedDebounceTime)>1000)
  1140.         )
  1141.     {
  1142.         if (!NPC_CanSeeEnemy(NPC) && Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<200)
  1143.         {
  1144.             //NPC_Printf("BLOCKED: Just Teleporting There");
  1145.             G_SetOrigin(NPC, level.combatPoints[NPCInfo->combatPoint].origin);
  1146.         }
  1147.         else
  1148.         {
  1149.             //NPC_Printf("BLOCKED: Attempting Jump");
  1150.  
  1151.             if (IsOnAPath)
  1152.             {
  1153.                 if (NPC_TryJump(NPCInfo->blockedTargetPosition))
  1154.                 {
  1155.                 }
  1156.                 else
  1157.                 {
  1158.                     //NPC_Printf("  Failed");
  1159.                 }
  1160.             }
  1161.             else if (EnemyRecentlySeen)
  1162.             {
  1163.                 if (NPC_TryJump(NPCInfo->enemyLastSeenLocation))
  1164.                 {
  1165.                 }
  1166.                 else
  1167.                 {
  1168.                     //NPC_Printf("  Failed");
  1169.                 }
  1170.             }
  1171.         }
  1172.     }
  1173.  
  1174.  
  1175.     NPC_UpdateAngles( qtrue, qtrue );
  1176.     return true;
  1177. }
Advertisement
Add Comment
Please, Sign In to add comment