AI_ImperialProbe.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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. extern gitem_t *FindItemForAmmo( ammo_t ammo );
  7. //Local state enums
  8. enum
  9. {
  10. LSTATE_NONE = 0,
  11. LSTATE_BACKINGUP,
  12. LSTATE_SPINNING,
  13. LSTATE_PAIN,
  14. LSTATE_DROP
  15. };
  16. void ImperialProbe_Idle( void );
  17. void NPC_Probe_Precache(void)
  18. {
  19. for ( int i = 1; i < 4; i++)
  20. {
  21. G_SoundIndex( va( "sound/chars/probe/misc/probetalk%d", i ) );
  22. }
  23. G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
  24. G_SoundIndex("sound/chars/probe/misc/anger1");
  25. G_SoundIndex("sound/chars/probe/misc/fire");
  26. G_EffectIndex( "chunks/probehead" );
  27. G_EffectIndex( "env/med_explode2" );
  28. G_EffectIndex( "explosions/probeexplosion1");
  29. G_EffectIndex( "bryar/muzzle_flash" );
  30. RegisterItem( FindItemForAmmo( AMMO_BLASTER ));
  31. RegisterItem( FindItemForWeapon( WP_BRYAR_PISTOL ) );
  32. }
  33. /*
  34. -------------------------
  35. Hunter_MaintainHeight
  36. -------------------------
  37. */
  38. #define VELOCITY_DECAY 0.85f
  39. void ImperialProbe_MaintainHeight( void )
  40. {
  41. float dif;
  42. // vec3_t endPos;
  43. // trace_t trace;
  44. // Update our angles regardless
  45. NPC_UpdateAngles( qtrue, qtrue );
  46. // If we have an enemy, we should try to hover at about enemy eye level
  47. if ( NPC->enemy )
  48. {
  49. // Find the height difference
  50. dif = NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2];
  51. // cap to prevent dramatic height shifts
  52. if ( fabs( dif ) > 8 )
  53. {
  54. if ( fabs( dif ) > 16 )
  55. {
  56. dif = ( dif < 0 ? -16 : 16 );
  57. }
  58. NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
  59. }
  60. }
  61. else
  62. {
  63. gentity_t *goal = NULL;
  64. if ( NPCInfo->goalEntity ) // Is there a goal?
  65. {
  66. goal = NPCInfo->goalEntity;
  67. }
  68. else
  69. {
  70. goal = NPCInfo->lastGoalEntity;
  71. }
  72. if ( goal )
  73. {
  74. dif = goal->currentOrigin[2] - NPC->currentOrigin[2];
  75. if ( fabs( dif ) > 24 )
  76. {
  77. ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
  78. }
  79. else
  80. {
  81. if ( NPC->client->ps.velocity[2] )
  82. {
  83. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  84. if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
  85. {
  86. NPC->client->ps.velocity[2] = 0;
  87. }
  88. }
  89. }
  90. }
  91. // Apply friction
  92. else if ( NPC->client->ps.velocity[2] )
  93. {
  94. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  95. if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
  96. {
  97. NPC->client->ps.velocity[2] = 0;
  98. }
  99. }
  100. // Stay at a given height until we take on an enemy
  101. /* VectorSet( endPos, NPC->currentOrigin[0], NPC->currentOrigin[1], NPC->currentOrigin[2] - 512 );
  102. gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
  103. if ( trace.fraction != 1.0f )
  104. {
  105. float length = ( trace.fraction * 512 );
  106. if ( length < 80 )
  107. {
  108. ucmd.upmove = 32;
  109. }
  110. else if ( length > 120 )
  111. {
  112. ucmd.upmove = -32;
  113. }
  114. else
  115. {
  116. if ( NPC->client->ps.velocity[2] )
  117. {
  118. NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
  119. if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
  120. {
  121. NPC->client->ps.velocity[2] = 0;
  122. }
  123. }
  124. }
  125. } */
  126. }
  127. // Apply friction
  128. if ( NPC->client->ps.velocity[0] )
  129. {
  130. NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
  131. if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
  132. {
  133. NPC->client->ps.velocity[0] = 0;
  134. }
  135. }
  136. if ( NPC->client->ps.velocity[1] )
  137. {
  138. NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
  139. if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
  140. {
  141. NPC->client->ps.velocity[1] = 0;
  142. }
  143. }
  144. }
  145. /*
  146. -------------------------
  147. ImperialProbe_Strafe
  148. -------------------------
  149. */
  150. #define HUNTER_STRAFE_VEL 256
  151. #define HUNTER_STRAFE_DIS 200
  152. #define HUNTER_UPWARD_PUSH 32
  153. void ImperialProbe_Strafe( void )
  154. {
  155. int dir;
  156. vec3_t end, right;
  157. trace_t tr;
  158. AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
  159. // Pick a random strafe direction, then check to see if doing a strafe would be
  160. // reasonable valid
  161. dir = ( rand() & 1 ) ? -1 : 1;
  162. VectorMA( NPC->currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
  163. gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
  164. // Close enough
  165. if ( tr.fraction > 0.9f )
  166. {
  167. VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
  168. // Add a slight upward push
  169. NPC->client->ps.velocity[2] += HUNTER_UPWARD_PUSH;
  170. // Set the strafe start time so we can do a controlled roll
  171. NPC->fx_time = level.time;
  172. NPCInfo->standTime = level.time + 3000 + random() * 500;
  173. }
  174. }
  175. /*
  176. -------------------------
  177. ImperialProbe_Hunt
  178. -------------------------`
  179. */
  180. #define HUNTER_FORWARD_BASE_SPEED 10
  181. #define HUNTER_FORWARD_MULTIPLIER 5
  182. void ImperialProbe_Hunt( qboolean visible, qboolean advance )
  183. {
  184. float distance, speed;
  185. vec3_t forward;
  186. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  187. //If we're not supposed to stand still, pursue the player
  188. if ( NPCInfo->standTime < level.time )
  189. {
  190. // Only strafe when we can see the player
  191. if ( visible )
  192. {
  193. ImperialProbe_Strafe();
  194. return;
  195. }
  196. }
  197. //If we don't want to advance, stop here
  198. if ( advance == qfalse )
  199. return;
  200. //Only try and navigate if the player is visible
  201. if ( visible == qfalse )
  202. {
  203. // Move towards our goal
  204. NPCInfo->goalEntity = NPC->enemy;
  205. NPCInfo->goalRadius = 12;
  206. NPC_MoveToGoal(qtrue);
  207. return;
  208. }
  209. else
  210. {
  211. VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
  212. distance = VectorNormalize( forward );
  213. }
  214. speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill->integer;
  215. VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
  216. }
  217. /*
  218. -------------------------
  219. ImperialProbe_FireBlaster
  220. -------------------------
  221. */
  222. void ImperialProbe_FireBlaster(void)
  223. {
  224. vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1;
  225. static vec3_t forward, vright, up;
  226. static vec3_t muzzle;
  227. gentity_t *missile;
  228. mdxaBone_t boltMatrix;
  229. //FIXME: use {0, NPC->client->ps.legsYaw, 0}
  230. gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel,
  231. NPC->genericBolt1,
  232. &boltMatrix, NPC->currentAngles, NPC->currentOrigin, (cg.time?cg.time:level.time),
  233. NULL, NPC->s.modelScale );
  234. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzle1 );
  235. G_PlayEffect( "bryar/muzzle_flash", muzzle1 );
  236. G_Sound( NPC, G_SoundIndex( "sound/chars/probe/misc/fire" ));
  237. if (NPC->health)
  238. {
  239. CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 );
  240. enemy_org1[0]+= Q_irand(0,10);
  241. enemy_org1[1]+= Q_irand(0,10);
  242. VectorSubtract (enemy_org1, muzzle1, delta1);
  243. vectoangles ( delta1, angleToEnemy1 );
  244. AngleVectors (angleToEnemy1, forward, vright, up);
  245. }
  246. else
  247. {
  248. AngleVectors (NPC->currentAngles, forward, vright, up);
  249. }
  250. missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC );
  251. missile->classname = "bryar_proj";
  252. missile->s.weapon = WP_BRYAR_PISTOL;
  253. if ( g_spskill->integer <= 1 )
  254. {
  255. missile->damage = 5;
  256. }
  257. else
  258. {
  259. missile->damage = 10;
  260. }
  261. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  262. missile->methodOfDeath = MOD_ENERGY;
  263. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  264. }
  265. /*
  266. -------------------------
  267. ImperialProbe_Ranged
  268. -------------------------
  269. */
  270. void ImperialProbe_Ranged( qboolean visible, qboolean advance )
  271. {
  272. int delay_min,delay_max;
  273. if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
  274. {
  275. if ( g_spskill->integer == 0 )
  276. {
  277. delay_min = 500;
  278. delay_max = 3000;
  279. }
  280. else if ( g_spskill->integer > 1 )
  281. {
  282. delay_min = 500;
  283. delay_max = 2000;
  284. }
  285. else
  286. {
  287. delay_min = 300;
  288. delay_max = 1500;
  289. }
  290. TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
  291. ImperialProbe_FireBlaster();
  292. // ucmd.buttons |= BUTTON_ATTACK;
  293. }
  294. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  295. {
  296. ImperialProbe_Hunt( visible, advance );
  297. }
  298. }
  299. /*
  300. -------------------------
  301. ImperialProbe_AttackDecision
  302. -------------------------
  303. */
  304. #define MIN_MELEE_RANGE 320
  305. #define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
  306. #define MIN_DISTANCE 128
  307. #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
  308. void ImperialProbe_AttackDecision( void )
  309. {
  310. // Always keep a good height off the ground
  311. ImperialProbe_MaintainHeight();
  312. //randomly talk
  313. if ( TIMER_Done(NPC,"patrolNoise") )
  314. {
  315. if (TIMER_Done(NPC,"angerNoise"))
  316. {
  317. G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
  318. TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
  319. }
  320. }
  321. // If we don't have an enemy, just idle
  322. if ( NPC_CheckEnemyExt() == qfalse )
  323. {
  324. ImperialProbe_Idle();
  325. return;
  326. }
  327. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL);
  328. // Rate our distance to the target, and our visibilty
  329. float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
  330. // distance_e distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
  331. qboolean visible = NPC_ClearLOS( NPC->enemy );
  332. qboolean advance = (qboolean)(distance > MIN_DISTANCE_SQR);
  333. // If we cannot see our target, move to see it
  334. if ( visible == qfalse )
  335. {
  336. if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
  337. {
  338. ImperialProbe_Hunt( visible, advance );
  339. return;
  340. }
  341. }
  342. // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
  343. NPC_FaceEnemy( qtrue );
  344. // Decide what type of attack to do
  345. ImperialProbe_Ranged( visible, advance );
  346. }
  347. /*
  348. -------------------------
  349. NPC_BSDroid_Pain
  350. -------------------------
  351. */
  352. void NPC_Probe_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
  353. {
  354. float pain_chance;
  355. VectorCopy( self->NPC->lastPathAngles, self->s.angles );
  356. if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) // demp2 always messes them up real good
  357. {
  358. vec3_t endPos;
  359. trace_t trace;
  360. VectorSet( endPos, self->currentOrigin[0], self->currentOrigin[1], self->currentOrigin[2] - 128 );
  361. gi.trace( &trace, self->currentOrigin, NULL, NULL, endPos, self->s.number, MASK_SOLID );
  362. if ( trace.fraction == 1.0f || mod == MOD_DEMP2 ) // demp2 always does this
  363. {
  364. if (self->client->clientInfo.headModel != 0)
  365. {
  366. vec3_t origin;
  367. VectorCopy(self->currentOrigin,origin);
  368. origin[2] +=50;
  369. // G_PlayEffect( "small_chunks", origin );
  370. G_PlayEffect( "chunks/probehead", origin );
  371. G_PlayEffect( "env/med_explode2", origin );
  372. self->client->clientInfo.headModel = 0;
  373. self->client->moveType = MT_RUNJUMP;
  374. self->client->ps.gravity = g_gravity->value*.1;
  375. }
  376. if ( (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) && other )
  377. {
  378. vec3_t dir;
  379. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
  380. VectorSubtract( self->currentOrigin, other->currentOrigin, dir );
  381. VectorNormalize( dir );
  382. VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
  383. self->client->ps.velocity[2] -= 127;
  384. }
  385. self->s.powerups |= ( 1 << PW_SHOCKED );
  386. self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
  387. self->NPC->localState = LSTATE_DROP;
  388. }
  389. }
  390. else
  391. {
  392. pain_chance = NPC_GetPainChance( self, damage );
  393. if ( random() < pain_chance ) // Spin around in pain?
  394. {
  395. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE);
  396. }
  397. }
  398. NPC_Pain( self, inflictor, other, point, damage, mod);
  399. }
  400. /*
  401. -------------------------
  402. ImperialProbe_Idle
  403. -------------------------
  404. */
  405. void ImperialProbe_Idle( void )
  406. {
  407. ImperialProbe_MaintainHeight();
  408. NPC_BSIdle();
  409. }
  410. /*
  411. -------------------------
  412. NPC_BSImperialProbe_Patrol
  413. -------------------------
  414. */
  415. void ImperialProbe_Patrol( void )
  416. {
  417. ImperialProbe_MaintainHeight();
  418. if ( NPC_CheckPlayerTeamStealth() )
  419. {
  420. NPC_UpdateAngles( qtrue, qtrue );
  421. return;
  422. }
  423. //If we have somewhere to go, then do that
  424. if (!NPC->enemy)
  425. {
  426. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL );
  427. if ( UpdateGoal() )
  428. {
  429. //start loop sound once we move
  430. NPC->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
  431. ucmd.buttons |= BUTTON_WALKING;
  432. NPC_MoveToGoal( qtrue );
  433. }
  434. //randomly talk
  435. if (TIMER_Done(NPC,"patrolNoise"))
  436. {
  437. G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
  438. TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
  439. }
  440. }
  441. else // He's got an enemy. Make him angry.
  442. {
  443. G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/probe/misc/anger1" );
  444. TIMER_Set( NPC, "angerNoise", Q_irand( 2000, 4000 ) );
  445. //NPCInfo->behaviorState = BS_HUNT_AND_KILL;
  446. }
  447. NPC_UpdateAngles( qtrue, qtrue );
  448. }
  449. /*
  450. -------------------------
  451. ImperialProbe_Wait
  452. -------------------------
  453. */
  454. void ImperialProbe_Wait(void)
  455. {
  456. if ( NPCInfo->localState == LSTATE_DROP )
  457. {
  458. vec3_t endPos;
  459. trace_t trace;
  460. NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->desiredYaw + 25 );
  461. VectorSet( endPos, NPC->currentOrigin[0], NPC->currentOrigin[1], NPC->currentOrigin[2] - 32 );
  462. gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
  463. if ( trace.fraction != 1.0f )
  464. {
  465. G_Damage(NPC, NPC->enemy, NPC->enemy, NULL, NULL, 2000, 0,MOD_UNKNOWN);
  466. }
  467. }
  468. NPC_UpdateAngles( qtrue, qtrue );
  469. }
  470. /*
  471. -------------------------
  472. NPC_BSImperialProbe_Default
  473. -------------------------
  474. */
  475. void NPC_BSImperialProbe_Default( void )
  476. {
  477. if ( NPC->enemy )
  478. {
  479. NPCInfo->goalEntity = NPC->enemy;
  480. ImperialProbe_AttackDecision();
  481. }
  482. else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
  483. {
  484. ImperialProbe_Patrol();
  485. }
  486. else if ( NPCInfo->localState == LSTATE_DROP )
  487. {
  488. ImperialProbe_Wait();
  489. }
  490. else
  491. {
  492. ImperialProbe_Idle();
  493. }
  494. }