AI_Interrogator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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. void Interrogator_Idle( void );
  6. void DeathFX( gentity_t *ent );
  7. enum
  8. {
  9. LSTATE_BLADESTOP=0,
  10. LSTATE_BLADEUP,
  11. LSTATE_BLADEDOWN,
  12. };
  13. /*
  14. -------------------------
  15. NPC_Interrogator_Precache
  16. -------------------------
  17. */
  18. void NPC_Interrogator_Precache(gentity_t *self)
  19. {
  20. G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_lp" );
  21. G_SoundIndex("sound/chars/mark1/misc/anger.wav");
  22. G_SoundIndex( "sound/chars/probe/misc/talk");
  23. G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject" );
  24. G_SoundIndex( "sound/chars/interrogator/misc/int_droid_explo" );
  25. G_EffectIndex( "explosions/droidexplosion1" );
  26. }
  27. /*
  28. -------------------------
  29. Interrogator_die
  30. -------------------------
  31. */
  32. void Interrogator_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
  33. {
  34. self->client->ps.velocity[2] = -100;
  35. /*
  36. self->locationDamage[HL_NONE] += damage;
  37. if (self->locationDamage[HL_NONE] > 40)
  38. {
  39. DeathFX(self);
  40. self->client->ps.eFlags |= EF_NODRAW;
  41. self->contents = CONTENTS_CORPSE;
  42. }
  43. else
  44. */
  45. {
  46. self->client->moveType = MT_WALK;
  47. self->client->ps.velocity[0] = Q_irand( -10, -20 );
  48. self->client->ps.velocity[1] = Q_irand( -10, -20 );
  49. self->client->ps.velocity[2] = -100;
  50. }
  51. //self->takedamage = qfalse;
  52. //self->client->ps.eFlags |= EF_NODRAW;
  53. //self->contents = 0;
  54. return;
  55. }
  56. /*
  57. -------------------------
  58. Interrogator_PartsMove
  59. -------------------------
  60. */
  61. void Interrogator_PartsMove(void)
  62. {
  63. // Syringe
  64. if ( TIMER_Done(NPC,"syringeDelay") )
  65. {
  66. NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);
  67. if ((NPC->pos1[1] < 60) || (NPC->pos1[1] > 300))
  68. {
  69. NPC->pos1[1]+=Q_irand( -20, 20 ); // Pitch
  70. }
  71. else if (NPC->pos1[1] > 180)
  72. {
  73. NPC->pos1[1]=Q_irand( 300, 360 ); // Pitch
  74. }
  75. else
  76. {
  77. NPC->pos1[1]=Q_irand( 0, 60 ); // Pitch
  78. }
  79. gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone1, NPC->pos1, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  80. TIMER_Set( NPC, "syringeDelay", Q_irand( 100, 1000 ) );
  81. }
  82. // Scalpel
  83. if ( TIMER_Done(NPC,"scalpelDelay") )
  84. {
  85. // Change pitch
  86. if ( NPCInfo->localState == LSTATE_BLADEDOWN ) // Blade is moving down
  87. {
  88. NPC->pos2[0]-= 30;
  89. if (NPC->pos2[0] < 180)
  90. {
  91. NPC->pos2[0] = 180;
  92. NPCInfo->localState = LSTATE_BLADEUP; // Make it move up
  93. }
  94. }
  95. else // Blade is coming back up
  96. {
  97. NPC->pos2[0]+= 30;
  98. if (NPC->pos2[0] >= 360)
  99. {
  100. NPC->pos2[0] = 360;
  101. NPCInfo->localState = LSTATE_BLADEDOWN; // Make it move down
  102. TIMER_Set( NPC, "scalpelDelay", Q_irand( 100, 1000 ) );
  103. }
  104. }
  105. NPC->pos2[0] = AngleNormalize360( NPC->pos2[0]);
  106. gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone2, NPC->pos2, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  107. }
  108. // Claw
  109. NPC->pos3[1] += Q_irand( 10, 30 );
  110. NPC->pos3[1] = AngleNormalize360( NPC->pos3[1]);
  111. gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone3, NPC->pos3, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  112. }
  113. #define VELOCITY_DECAY 0.85f
  114. #define HUNTER_UPWARD_PUSH 2
  115. /*
  116. -------------------------
  117. Interrogator_MaintainHeight
  118. -------------------------
  119. */
  120. void Interrogator_MaintainHeight( void )
  121. {
  122. float dif;
  123. // vec3_t endPos;
  124. // trace_t trace;
  125. NPC->s.loopSound = G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_lp" );
  126. // Update our angles regardless
  127. NPC_UpdateAngles( qtrue, qtrue );
  128. // If we have an enemy, we should try to hover at about enemy eye level
  129. if ( NPC->enemy )
  130. {
  131. // Find the height difference
  132. dif = (NPC->enemy->currentOrigin[2] + NPC->enemy->maxs[2]) - NPC->currentOrigin[2];
  133. // cap to prevent dramatic height shifts
  134. if ( fabs( dif ) > 2 )
  135. {
  136. if ( fabs( dif ) > 16 )
  137. {
  138. dif = ( dif < 0 ? -16 : 16 );
  139. }
  140. NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
  141. }
  142. }
  143. else
  144. {
  145. gentity_t *goal = NULL;
  146. if ( NPCInfo->goalEntity ) // Is there a goal?
  147. {
  148. goal = NPCInfo->goalEntity;
  149. }
  150. else
  151. {
  152. goal = NPCInfo->lastGoalEntity;
  153. }
  154. if ( goal )
  155. {
  156. dif = goal->currentOrigin[2] - NPC->currentOrigin[2];
  157. if ( fabs( dif ) > 24 )
  158. {
  159. ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
  160. }
  161. else
  162. {
  163. if ( NPC->client->ps.velocity[2] )
  164. {
  165. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  166. if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
  167. {
  168. NPC->client->ps.velocity[2] = 0;
  169. }
  170. }
  171. }
  172. }
  173. // Apply friction
  174. else if ( NPC->client->ps.velocity[2] )
  175. {
  176. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  177. if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
  178. {
  179. NPC->client->ps.velocity[2] = 0;
  180. }
  181. }
  182. }
  183. // Apply friction
  184. if ( NPC->client->ps.velocity[0] )
  185. {
  186. NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
  187. if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
  188. {
  189. NPC->client->ps.velocity[0] = 0;
  190. }
  191. }
  192. if ( NPC->client->ps.velocity[1] )
  193. {
  194. NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
  195. if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
  196. {
  197. NPC->client->ps.velocity[1] = 0;
  198. }
  199. }
  200. }
  201. #define HUNTER_STRAFE_VEL 32
  202. #define HUNTER_STRAFE_DIS 200
  203. /*
  204. -------------------------
  205. Interrogator_Strafe
  206. -------------------------
  207. */
  208. void Interrogator_Strafe( void )
  209. {
  210. int dir;
  211. vec3_t end, right;
  212. trace_t tr;
  213. float dif;
  214. AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
  215. // Pick a random strafe direction, then check to see if doing a strafe would be
  216. // reasonable valid
  217. dir = ( rand() & 1 ) ? -1 : 1;
  218. VectorMA( NPC->currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
  219. gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
  220. // Close enough
  221. if ( tr.fraction > 0.9f )
  222. {
  223. VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
  224. // Add a slight upward push
  225. if ( NPC->enemy )
  226. {
  227. // Find the height difference
  228. dif = (NPC->enemy->currentOrigin[2] + 32) - NPC->currentOrigin[2];
  229. // cap to prevent dramatic height shifts
  230. if ( fabs( dif ) > 8 )
  231. {
  232. dif = ( dif < 0 ? -HUNTER_UPWARD_PUSH : HUNTER_UPWARD_PUSH );
  233. }
  234. NPC->client->ps.velocity[2] += dif;
  235. }
  236. // Set the strafe start time
  237. NPC->fx_time = level.time;
  238. NPCInfo->standTime = level.time + 3000 + random() * 500;
  239. }
  240. }
  241. /*
  242. -------------------------
  243. Interrogator_Hunt
  244. -------------------------`
  245. */
  246. #define HUNTER_FORWARD_BASE_SPEED 10
  247. #define HUNTER_FORWARD_MULTIPLIER 2
  248. void Interrogator_Hunt( qboolean visible, qboolean advance )
  249. {
  250. float distance, speed;
  251. vec3_t forward;
  252. Interrogator_PartsMove();
  253. NPC_FaceEnemy(qfalse);
  254. //If we're not supposed to stand still, pursue the player
  255. if ( NPCInfo->standTime < level.time )
  256. {
  257. // Only strafe when we can see the player
  258. if ( visible )
  259. {
  260. Interrogator_Strafe();
  261. if ( NPCInfo->standTime > level.time )
  262. {//successfully strafed
  263. return;
  264. }
  265. }
  266. }
  267. //If we don't want to advance, stop here
  268. if ( advance == qfalse )
  269. return;
  270. //Only try and navigate if the player is visible
  271. if ( visible == qfalse )
  272. {
  273. // Move towards our goal
  274. NPCInfo->goalEntity = NPC->enemy;
  275. NPCInfo->goalRadius = 12;
  276. NPC_MoveToGoal(qtrue);
  277. return;
  278. }
  279. else
  280. {
  281. VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
  282. distance = VectorNormalize( forward );
  283. }
  284. speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill->integer;
  285. VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
  286. }
  287. #define MIN_DISTANCE 64
  288. /*
  289. -------------------------
  290. Interrogator_Melee
  291. -------------------------
  292. */
  293. void Interrogator_Melee( qboolean visible, qboolean advance )
  294. {
  295. if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
  296. {
  297. // Make sure that we are within the height range before we allow any damage to happen
  298. if ( NPC->currentOrigin[2] >= NPC->enemy->currentOrigin[2]+NPC->enemy->mins[2] && NPC->currentOrigin[2]+NPC->mins[2]+8 < NPC->enemy->currentOrigin[2]+NPC->enemy->maxs[2] )
  299. {
  300. TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
  301. G_Damage( NPC->enemy, NPC, NPC, 0, 0, 2, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
  302. NPC->enemy->client->poisonDamage = 18;
  303. NPC->enemy->client->poisonTime = level.time + 1000;
  304. // Drug our enemy up and do the wonky vision thing
  305. gentity_t *tent = G_TempEntity( NPC->enemy->currentOrigin, EV_DRUGGED );
  306. tent->owner = NPC->enemy;
  307. G_Sound( NPC, G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject.mp3" ));
  308. }
  309. }
  310. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  311. {
  312. Interrogator_Hunt( visible, advance );
  313. }
  314. }
  315. /*
  316. -------------------------
  317. Interrogator_Attack
  318. -------------------------
  319. */
  320. void Interrogator_Attack( void )
  321. {
  322. // Always keep a good height off the ground
  323. Interrogator_MaintainHeight();
  324. //randomly talk
  325. if ( TIMER_Done(NPC,"patrolNoise") )
  326. {
  327. if (TIMER_Done(NPC,"angerNoise"))
  328. {
  329. G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/talk.wav", Q_irand(1, 3)) );
  330. TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
  331. }
  332. }
  333. // If we don't have an enemy, just idle
  334. if ( NPC_CheckEnemyExt() == qfalse )
  335. {
  336. Interrogator_Idle();
  337. return;
  338. }
  339. // Rate our distance to the target, and our visibilty
  340. float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
  341. qboolean visible = NPC_ClearLOS( NPC->enemy );
  342. qboolean advance = (qboolean)(distance > MIN_DISTANCE*MIN_DISTANCE );
  343. if ( !visible )
  344. {
  345. advance = qtrue;
  346. }
  347. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  348. {
  349. Interrogator_Hunt( visible, advance );
  350. }
  351. NPC_FaceEnemy( qtrue );
  352. if (!advance)
  353. {
  354. Interrogator_Melee( visible, advance );
  355. }
  356. }
  357. /*
  358. -------------------------
  359. Interrogator_Idle
  360. -------------------------
  361. */
  362. void Interrogator_Idle( void )
  363. {
  364. if ( NPC_CheckPlayerTeamStealth() )
  365. {
  366. G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/mark1/misc/anger.wav" );
  367. NPC_UpdateAngles( qtrue, qtrue );
  368. return;
  369. }
  370. Interrogator_MaintainHeight();
  371. NPC_BSIdle();
  372. }
  373. /*
  374. -------------------------
  375. NPC_BSInterrogator_Default
  376. -------------------------
  377. */
  378. void NPC_BSInterrogator_Default( void )
  379. {
  380. //NPC->e_DieFunc = dieF_Interrogator_die;
  381. if ( NPC->enemy )
  382. {
  383. Interrogator_Attack();
  384. }
  385. else
  386. {
  387. Interrogator_Idle();
  388. }
  389. }