AI_Remote.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  2. #include "g_headers.h"
  3. #include "b_local.h"
  4. #include "g_nav.h"
  5. gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t *owner, qboolean altFire = qfalse );
  6. void Remote_Strafe( void );
  7. #define VELOCITY_DECAY 0.85f
  8. //Local state enums
  9. enum
  10. {
  11. LSTATE_NONE = 0,
  12. };
  13. void Remote_Idle( void );
  14. void NPC_Remote_Precache(void)
  15. {
  16. G_SoundIndex("sound/chars/remote/misc/fire.wav");
  17. G_SoundIndex( "sound/chars/remote/misc/hiss.wav");
  18. G_EffectIndex( "env/small_explode");
  19. }
  20. /*
  21. -------------------------
  22. NPC_Remote_Pain
  23. -------------------------
  24. */
  25. void NPC_Remote_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
  26. {
  27. SaveNPCGlobals();
  28. SetNPCGlobals( self );
  29. Remote_Strafe();
  30. RestoreNPCGlobals();
  31. NPC_Pain( self, inflictor, other, point, damage, mod );
  32. }
  33. /*
  34. -------------------------
  35. Remote_MaintainHeight
  36. -------------------------
  37. */
  38. void Remote_MaintainHeight( void )
  39. {
  40. float dif;
  41. // Update our angles regardless
  42. NPC_UpdateAngles( qtrue, qtrue );
  43. if ( NPC->client->ps.velocity[2] )
  44. {
  45. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  46. if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
  47. {
  48. NPC->client->ps.velocity[2] = 0;
  49. }
  50. }
  51. // If we have an enemy, we should try to hover at or a little below enemy eye level
  52. if ( NPC->enemy )
  53. {
  54. if (TIMER_Done( NPC, "heightChange"))
  55. {
  56. TIMER_Set( NPC,"heightChange",Q_irand( 1000, 3000 ));
  57. // Find the height difference
  58. dif = (NPC->enemy->currentOrigin[2] + Q_irand( 0, NPC->enemy->maxs[2]+8 )) - NPC->currentOrigin[2];
  59. // cap to prevent dramatic height shifts
  60. if ( fabs( dif ) > 2 )
  61. {
  62. if ( fabs( dif ) > 24 )
  63. {
  64. dif = ( dif < 0 ? -24 : 24 );
  65. }
  66. dif *= 10;
  67. NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
  68. NPC->fx_time = level.time;
  69. G_Sound( NPC, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
  70. }
  71. }
  72. }
  73. else
  74. {
  75. gentity_t *goal = NULL;
  76. if ( NPCInfo->goalEntity ) // Is there a goal?
  77. {
  78. goal = NPCInfo->goalEntity;
  79. }
  80. else
  81. {
  82. goal = NPCInfo->lastGoalEntity;
  83. }
  84. if ( goal )
  85. {
  86. dif = goal->currentOrigin[2] - NPC->currentOrigin[2];
  87. if ( fabs( dif ) > 24 )
  88. {
  89. dif = ( dif < 0 ? -24 : 24 );
  90. NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
  91. }
  92. }
  93. }
  94. // Apply friction
  95. if ( NPC->client->ps.velocity[0] )
  96. {
  97. NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
  98. if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
  99. {
  100. NPC->client->ps.velocity[0] = 0;
  101. }
  102. }
  103. if ( NPC->client->ps.velocity[1] )
  104. {
  105. NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
  106. if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
  107. {
  108. NPC->client->ps.velocity[1] = 0;
  109. }
  110. }
  111. }
  112. #define REMOTE_STRAFE_VEL 256
  113. #define REMOTE_STRAFE_DIS 200
  114. #define REMOTE_UPWARD_PUSH 32
  115. /*
  116. -------------------------
  117. Remote_Strafe
  118. -------------------------
  119. */
  120. void Remote_Strafe( void )
  121. {
  122. int dir;
  123. vec3_t end, right;
  124. trace_t tr;
  125. AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
  126. // Pick a random strafe direction, then check to see if doing a strafe would be
  127. // reasonable valid
  128. dir = ( rand() & 1 ) ? -1 : 1;
  129. VectorMA( NPC->currentOrigin, REMOTE_STRAFE_DIS * dir, right, end );
  130. gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
  131. // Close enough
  132. if ( tr.fraction > 0.9f )
  133. {
  134. VectorMA( NPC->client->ps.velocity, REMOTE_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
  135. G_Sound( NPC, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
  136. // Add a slight upward push
  137. NPC->client->ps.velocity[2] += REMOTE_UPWARD_PUSH;
  138. // Set the strafe start time so we can do a controlled roll
  139. NPC->fx_time = level.time;
  140. NPCInfo->standTime = level.time + 3000 + random() * 500;
  141. }
  142. }
  143. #define REMOTE_FORWARD_BASE_SPEED 10
  144. #define REMOTE_FORWARD_MULTIPLIER 5
  145. /*
  146. -------------------------
  147. Remote_Hunt
  148. -------------------------
  149. */
  150. void Remote_Hunt( qboolean visible, qboolean advance, qboolean retreat )
  151. {
  152. float distance, speed;
  153. vec3_t forward;
  154. //If we're not supposed to stand still, pursue the player
  155. if ( NPCInfo->standTime < level.time )
  156. {
  157. // Only strafe when we can see the player
  158. if ( visible )
  159. {
  160. Remote_Strafe();
  161. return;
  162. }
  163. }
  164. //If we don't want to advance, stop here
  165. if ( advance == qfalse && visible == qtrue )
  166. return;
  167. //Only try and navigate if the player is visible
  168. if ( visible == qfalse )
  169. {
  170. // Move towards our goal
  171. NPCInfo->goalEntity = NPC->enemy;
  172. NPCInfo->goalRadius = 12;
  173. NPC_MoveToGoal(qtrue);
  174. return;
  175. }
  176. else
  177. {
  178. VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
  179. distance = VectorNormalize( forward );
  180. }
  181. speed = REMOTE_FORWARD_BASE_SPEED + REMOTE_FORWARD_MULTIPLIER * g_spskill->integer;
  182. if ( retreat == qtrue )
  183. {
  184. speed *= -1;
  185. }
  186. VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
  187. }
  188. /*
  189. -------------------------
  190. Remote_Fire
  191. -------------------------
  192. */
  193. void Remote_Fire (void)
  194. {
  195. vec3_t delta1, enemy_org1, muzzle1;
  196. vec3_t angleToEnemy1;
  197. static vec3_t forward, vright, up;
  198. static vec3_t muzzle;
  199. gentity_t *missile;
  200. CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 );
  201. VectorCopy( NPC->currentOrigin, muzzle1 );
  202. VectorSubtract (enemy_org1, muzzle1, delta1);
  203. vectoangles ( delta1, angleToEnemy1 );
  204. AngleVectors (angleToEnemy1, forward, vright, up);
  205. missile = CreateMissile( NPC->currentOrigin, forward, 1000, 10000, NPC );
  206. G_PlayEffect( "bryar/muzzle_flash", NPC->currentOrigin, forward );
  207. missile->classname = "briar";
  208. missile->s.weapon = WP_BRYAR_PISTOL;
  209. missile->damage = 10;
  210. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  211. missile->methodOfDeath = MOD_ENERGY;
  212. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  213. }
  214. /*
  215. -------------------------
  216. Remote_Ranged
  217. -------------------------
  218. */
  219. void Remote_Ranged( qboolean visible, qboolean advance, qboolean retreat )
  220. {
  221. if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
  222. {
  223. TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
  224. Remote_Fire();
  225. }
  226. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  227. {
  228. Remote_Hunt( visible, advance, retreat );
  229. }
  230. }
  231. #define MIN_MELEE_RANGE 320
  232. #define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
  233. #define MIN_DISTANCE 80
  234. #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
  235. /*
  236. -------------------------
  237. Remote_Attack
  238. -------------------------
  239. */
  240. void Remote_Attack( void )
  241. {
  242. if ( TIMER_Done(NPC,"spin") )
  243. {
  244. TIMER_Set( NPC, "spin", Q_irand( 250, 1500 ) );
  245. NPCInfo->desiredYaw += Q_irand( -200, 200 );
  246. }
  247. // Always keep a good height off the ground
  248. Remote_MaintainHeight();
  249. // If we don't have an enemy, just idle
  250. if ( NPC_CheckEnemyExt() == qfalse )
  251. {
  252. Remote_Idle();
  253. return;
  254. }
  255. // Rate our distance to the target, and our visibilty
  256. float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
  257. // distance_e distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
  258. qboolean visible = NPC_ClearLOS( NPC->enemy );
  259. float idealDist = MIN_DISTANCE_SQR+(MIN_DISTANCE_SQR*Q_flrand( 0, 1 ));
  260. qboolean advance = (qboolean)(distance > idealDist*1.25);
  261. qboolean retreat = (qboolean)(distance < idealDist*0.75);
  262. // If we cannot see our target, move to see it
  263. if ( visible == qfalse )
  264. {
  265. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  266. {
  267. Remote_Hunt( visible, advance, retreat );
  268. return;
  269. }
  270. }
  271. Remote_Ranged( visible, advance, retreat );
  272. }
  273. /*
  274. -------------------------
  275. Remote_Idle
  276. -------------------------
  277. */
  278. void Remote_Idle( void )
  279. {
  280. Remote_MaintainHeight();
  281. NPC_BSIdle();
  282. }
  283. /*
  284. -------------------------
  285. Remote_Patrol
  286. -------------------------
  287. */
  288. void Remote_Patrol( void )
  289. {
  290. Remote_MaintainHeight();
  291. //If we have somewhere to go, then do that
  292. if (!NPC->enemy)
  293. {
  294. if ( UpdateGoal() )
  295. {
  296. //start loop sound once we move
  297. ucmd.buttons |= BUTTON_WALKING;
  298. NPC_MoveToGoal( qtrue );
  299. }
  300. }
  301. NPC_UpdateAngles( qtrue, qtrue );
  302. }
  303. /*
  304. -------------------------
  305. NPC_BSRemote_Default
  306. -------------------------
  307. */
  308. void NPC_BSRemote_Default( void )
  309. {
  310. if ( NPC->enemy )
  311. {
  312. Remote_Attack();
  313. }
  314. else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
  315. {
  316. Remote_Patrol();
  317. }
  318. else
  319. {
  320. Remote_Idle();
  321. }
  322. }