AI_Howler.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  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. // These define the working combat range for these suckers
  5. #define MIN_DISTANCE 54
  6. #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
  7. #define MAX_DISTANCE 128
  8. #define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
  9. #define LSTATE_CLEAR 0
  10. #define LSTATE_WAITING 1
  11. #define LSTATE_FLEE 2
  12. #define LSTATE_BERZERK 3
  13. #define HOWLER_RETREAT_DIST 300.0f
  14. #define HOWLER_PANIC_HEALTH 10
  15. extern void G_UcmdMoveForDir( gentity_t *self, usercmd_t *cmd, vec3_t dir );
  16. extern void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex = 0 );
  17. extern int PM_AnimLength( int index, animNumber_t anim );
  18. extern qboolean NAV_DirSafe( gentity_t *self, vec3_t dir, float dist );
  19. extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
  20. extern float NPC_EntRangeFromBolt( gentity_t *targEnt, int boltIndex );
  21. extern int NPC_GetEntsNearBolt( gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
  22. extern qboolean PM_InKnockDown( playerState_t *ps );
  23. extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
  24. static void Howler_Attack( float enemyDist, qboolean howl = qfalse );
  25. /*
  26. -------------------------
  27. NPC_Howler_Precache
  28. -------------------------
  29. */
  30. void NPC_Howler_Precache( void )
  31. {
  32. int i;
  33. //G_SoundIndex( "sound/chars/howler/howl.mp3" );
  34. G_EffectIndex( "howler/sonic" );
  35. G_SoundIndex( "sound/chars/howler/howl.mp3" );
  36. for ( i = 1; i < 3; i++ )
  37. {
  38. G_SoundIndex( va( "sound/chars/howler/idle_hiss%d.mp3", i ) );
  39. }
  40. for ( i = 1; i < 6; i++ )
  41. {
  42. G_SoundIndex( va( "sound/chars/howler/howl_talk%d.mp3", i ) );
  43. G_SoundIndex( va( "sound/chars/howler/howl_yell%d.mp3", i ) );
  44. }
  45. }
  46. void Howler_ClearTimers( gentity_t *self )
  47. {
  48. //clear all my timers
  49. TIMER_Set( self, "flee", -level.time );
  50. TIMER_Set( self, "retreating", -level.time );
  51. TIMER_Set( self, "standing", -level.time );
  52. TIMER_Set( self, "walking", -level.time );
  53. TIMER_Set( self, "running", -level.time );
  54. TIMER_Set( self, "aggressionDecay", -level.time );
  55. TIMER_Set( self, "speaking", -level.time );
  56. }
  57. static qboolean NPC_Howler_Move( int randomJumpChance = 0 )
  58. {
  59. if ( !TIMER_Done( NPC, "standing" ) )
  60. {//standing around
  61. return qfalse;
  62. }
  63. if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE )
  64. {//in air, don't do anything
  65. return qfalse;
  66. }
  67. if ( (!NPC->enemy&&TIMER_Done( NPC, "running" )) || !TIMER_Done( NPC, "walking" ) )
  68. {
  69. ucmd.buttons |= BUTTON_WALKING;
  70. }
  71. if ( (!randomJumpChance||Q_irand( 0, randomJumpChance ))
  72. && NPC_MoveToGoal( qtrue ) )
  73. {
  74. if ( VectorCompare( NPC->client->ps.moveDir, vec3_origin )
  75. || !NPC->client->ps.speed )
  76. {//uh.... wtf? Got there?
  77. if ( NPCInfo->goalEntity )
  78. {
  79. NPC_FaceEntity( NPCInfo->goalEntity, qfalse );
  80. }
  81. else
  82. {
  83. NPC_UpdateAngles( qfalse, qtrue );
  84. }
  85. return qtrue;
  86. }
  87. //TEMP: don't want to strafe
  88. VectorClear( NPC->client->ps.moveDir );
  89. ucmd.rightmove = 0.0f;
  90. // Com_Printf( "Howler moving %d\n",ucmd.forwardmove );
  91. //if backing up, go slow...
  92. if ( ucmd.forwardmove < 0.0f )
  93. {
  94. ucmd.buttons |= BUTTON_WALKING;
  95. //if ( NPC->client->ps.speed > NPCInfo->stats.walkSpeed )
  96. {//don't walk faster than I'm allowed to
  97. NPC->client->ps.speed = NPCInfo->stats.walkSpeed;
  98. }
  99. }
  100. else
  101. {
  102. if ( (ucmd.buttons&BUTTON_WALKING) )
  103. {
  104. NPC->client->ps.speed = NPCInfo->stats.walkSpeed;
  105. }
  106. else
  107. {
  108. NPC->client->ps.speed = NPCInfo->stats.runSpeed;
  109. }
  110. }
  111. NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
  112. NPC_UpdateAngles( qfalse, qtrue );
  113. }
  114. else if ( NPCInfo->goalEntity )
  115. {//couldn't get where we wanted to go, try to jump there
  116. NPC_FaceEntity( NPCInfo->goalEntity, qfalse );
  117. NPC_TryJump( NPCInfo->goalEntity, 400.0f, -256.0f );
  118. }
  119. return qtrue;
  120. }
  121. /*
  122. -------------------------
  123. Howler_Idle
  124. -------------------------
  125. */
  126. static void Howler_Idle( void )
  127. {
  128. }
  129. /*
  130. -------------------------
  131. Howler_Patrol
  132. -------------------------
  133. */
  134. static void Howler_Patrol( void )
  135. {
  136. NPCInfo->localState = LSTATE_CLEAR;
  137. //If we have somewhere to go, then do that
  138. if ( UpdateGoal() )
  139. {
  140. NPC_Howler_Move( 100 );
  141. }
  142. vec3_t dif;
  143. VectorSubtract( g_entities[0].currentOrigin, NPC->currentOrigin, dif );
  144. if ( VectorLengthSquared( dif ) < 256 * 256 )
  145. {
  146. G_SetEnemy( NPC, &g_entities[0] );
  147. }
  148. if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
  149. {
  150. Howler_Idle();
  151. return;
  152. }
  153. Howler_Attack( 0.0f, qtrue );
  154. }
  155. /*
  156. -------------------------
  157. Howler_Move
  158. -------------------------
  159. */
  160. static qboolean Howler_Move( qboolean visible )
  161. {
  162. if ( NPCInfo->localState != LSTATE_WAITING )
  163. {
  164. NPCInfo->goalEntity = NPC->enemy;
  165. NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
  166. return NPC_Howler_Move( 30 );
  167. }
  168. return qfalse;
  169. }
  170. //---------------------------------------------------------
  171. static void Howler_TryDamage( int damage, qboolean tongue, qboolean knockdown )
  172. {
  173. vec3_t start, end, dir;
  174. trace_t tr;
  175. if ( tongue )
  176. {
  177. G_GetBoltPosition( NPC, NPC->genericBolt1, start );
  178. G_GetBoltPosition( NPC, NPC->genericBolt2, end );
  179. VectorSubtract( end, start, dir );
  180. float dist = VectorNormalize( dir );
  181. VectorMA( start, dist+16, dir, end );
  182. }
  183. else
  184. {
  185. VectorCopy( NPC->currentOrigin, start );
  186. AngleVectors( NPC->currentAngles, dir, NULL, NULL );
  187. VectorMA( start, MIN_DISTANCE*2, dir, end );
  188. }
  189. #ifndef FINAL_BUILD
  190. if ( d_saberCombat->integer > 1 )
  191. {
  192. G_DebugLine(start, end, 1000, 0x000000ff, qtrue);
  193. }
  194. #endif
  195. // Should probably trace from the mouth, but, ah well.
  196. gi.trace( &tr, start, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
  197. if ( tr.entityNum < ENTITYNUM_WORLD )
  198. {//hit *something*
  199. gentity_t *victim = &g_entities[tr.entityNum];
  200. if ( !victim->client
  201. || victim->client->NPC_class != CLASS_HOWLER )
  202. {//not another howler
  203. if ( knockdown && victim->client )
  204. {//only do damage if victim isn't knocked down. If he isn't, knock him down
  205. if ( PM_InKnockDown( &victim->client->ps ) )
  206. {
  207. return;
  208. }
  209. }
  210. //FIXME: some sort of damage effect (claws and tongue are cutting you... blood?)
  211. G_Damage( victim, NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
  212. if ( knockdown && victim->health > 0 )
  213. {//victim still alive
  214. G_Knockdown( victim, NPC, NPC->client->ps.velocity, 500, qfalse );
  215. }
  216. }
  217. }
  218. }
  219. static void Howler_Howl( void )
  220. {
  221. gentity_t *radiusEnts[ 128 ];
  222. int numEnts;
  223. const float radius = (NPC->spawnflags&1)?256:128;
  224. const float halfRadSquared = ((radius/2)*(radius/2));
  225. const float radiusSquared = (radius*radius);
  226. float distSq;
  227. int i;
  228. vec3_t boltOrg;
  229. AddSoundEvent( NPC, NPC->currentOrigin, 512, AEL_DANGER, qfalse, qtrue );
  230. numEnts = NPC_GetEntsNearBolt( radiusEnts, radius, NPC->handLBolt, boltOrg );
  231. for ( i = 0; i < numEnts; i++ )
  232. {
  233. if ( !radiusEnts[i]->inuse )
  234. {
  235. continue;
  236. }
  237. if ( radiusEnts[i] == NPC )
  238. {//Skip the rancor ent
  239. continue;
  240. }
  241. if ( radiusEnts[i]->client == NULL )
  242. {//must be a client
  243. continue;
  244. }
  245. if ( radiusEnts[i]->client->NPC_class == CLASS_HOWLER )
  246. {//other howlers immune
  247. continue;
  248. }
  249. distSq = DistanceSquared( radiusEnts[i]->currentOrigin, boltOrg );
  250. if ( distSq <= radiusSquared )
  251. {
  252. if ( distSq < halfRadSquared )
  253. {//close enough to do damage, too
  254. if ( Q_irand( 0, g_spskill->integer ) )
  255. {//does no damage on easy, does 1 point every other frame on medium, more often on hard
  256. G_Damage( radiusEnts[i], NPC, NPC, vec3_origin, NPC->currentOrigin, 1, DAMAGE_NO_KNOCKBACK, MOD_IMPACT );
  257. }
  258. }
  259. if ( radiusEnts[i]->health > 0
  260. && radiusEnts[i]->client
  261. && radiusEnts[i]->client->NPC_class != CLASS_RANCOR
  262. && radiusEnts[i]->client->NPC_class != CLASS_ATST
  263. && !PM_InKnockDown( &radiusEnts[i]->client->ps ) )
  264. {
  265. if ( PM_HasAnimation( radiusEnts[i], BOTH_SONICPAIN_START ) )
  266. {
  267. if ( radiusEnts[i]->client->ps.torsoAnim != BOTH_SONICPAIN_START
  268. && radiusEnts[i]->client->ps.torsoAnim != BOTH_SONICPAIN_HOLD )
  269. {
  270. NPC_SetAnim( radiusEnts[i], SETANIM_LEGS, BOTH_SONICPAIN_START, SETANIM_FLAG_NORMAL );
  271. NPC_SetAnim( radiusEnts[i], SETANIM_TORSO, BOTH_SONICPAIN_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  272. radiusEnts[i]->client->ps.torsoAnimTimer += 100;
  273. radiusEnts[i]->client->ps.weaponTime = radiusEnts[i]->client->ps.torsoAnimTimer;
  274. }
  275. else if ( radiusEnts[i]->client->ps.torsoAnimTimer <= 100 )
  276. {//at the end of the sonic pain start or hold anim
  277. NPC_SetAnim( radiusEnts[i], SETANIM_LEGS, BOTH_SONICPAIN_HOLD, SETANIM_FLAG_NORMAL );
  278. NPC_SetAnim( radiusEnts[i], SETANIM_TORSO, BOTH_SONICPAIN_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  279. radiusEnts[i]->client->ps.torsoAnimTimer += 100;
  280. radiusEnts[i]->client->ps.weaponTime = radiusEnts[i]->client->ps.torsoAnimTimer;
  281. }
  282. }
  283. /*
  284. else if ( distSq < halfRadSquared
  285. && radiusEnts[i]->client->ps.groundEntityNum != ENTITYNUM_NONE
  286. && !Q_irand( 0, 10 ) )//FIXME: base on skill
  287. {//within range
  288. G_Knockdown( radiusEnts[i], NPC, vec3_origin, 500, qfalse );
  289. }
  290. */
  291. }
  292. }
  293. }
  294. float playerDist = NPC_EntRangeFromBolt( player, NPC->genericBolt1 );
  295. if ( playerDist < 256.0f )
  296. {
  297. CGCam_Shake( 1.0f*playerDist/128.0f, 200 );
  298. }
  299. }
  300. //------------------------------
  301. static void Howler_Attack( float enemyDist, qboolean howl )
  302. {
  303. int dmg = (NPCInfo->localState==LSTATE_BERZERK)?5:2;
  304. if ( !TIMER_Exists( NPC, "attacking" ))
  305. {
  306. int attackAnim = BOTH_GESTURE1;
  307. // Going to do an attack
  308. if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps )
  309. && enemyDist <= MIN_DISTANCE )
  310. {
  311. attackAnim = BOTH_ATTACK2;
  312. }
  313. else if ( !Q_irand( 0, 4 ) || howl )
  314. {//howl attack
  315. //G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" );
  316. }
  317. else if ( enemyDist > MIN_DISTANCE && Q_irand( 0, 1 ) )
  318. {//lunge attack
  319. //jump foward
  320. vec3_t fwd, yawAng = {0, NPC->client->ps.viewangles[YAW], 0};
  321. AngleVectors( yawAng, fwd, NULL, NULL );
  322. VectorScale( fwd, (enemyDist*3.0f), NPC->client->ps.velocity );
  323. NPC->client->ps.velocity[2] = 200;
  324. NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
  325. attackAnim = BOTH_ATTACK1;
  326. }
  327. else
  328. {//tongue attack
  329. attackAnim = BOTH_ATTACK2;
  330. }
  331. NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART );
  332. if ( NPCInfo->localState == LSTATE_BERZERK )
  333. {//attack again right away
  334. TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer );
  335. }
  336. else
  337. {
  338. TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + Q_irand( 0, 1500 ) );//FIXME: base on skill
  339. TIMER_Set( NPC, "standing", -level.time );
  340. TIMER_Set( NPC, "walking", -level.time );
  341. TIMER_Set( NPC, "running", NPC->client->ps.legsAnimTimer + 5000 );
  342. }
  343. TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage
  344. }
  345. // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
  346. switch ( NPC->client->ps.legsAnim )
  347. {
  348. case BOTH_ATTACK1:
  349. case BOTH_MELEE1:
  350. if ( NPC->client->ps.legsAnimTimer > 650//more than 13 frames left
  351. && PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsAnimTimer >= 800 )//at least 16 frames into anim
  352. {
  353. Howler_TryDamage( dmg, qfalse, qfalse );
  354. }
  355. break;
  356. case BOTH_ATTACK2:
  357. case BOTH_MELEE2:
  358. if ( NPC->client->ps.legsAnimTimer > 350//more than 7 frames left
  359. && PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsAnimTimer >= 550 )//at least 11 frames into anim
  360. {
  361. Howler_TryDamage( dmg, qtrue, qfalse );
  362. }
  363. break;
  364. case BOTH_GESTURE1:
  365. {
  366. if ( NPC->client->ps.legsAnimTimer > 1800//more than 36 frames left
  367. && PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsAnimTimer >= 950 )//at least 19 frames into anim
  368. {
  369. Howler_Howl();
  370. if ( !NPC->count )
  371. {
  372. G_PlayEffect( G_EffectIndex( "howler/sonic" ), NPC->playerModel, NPC->genericBolt1, NPC->s.number, NPC->currentOrigin, 4750, qtrue );
  373. G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" );
  374. NPC->count = 1;
  375. }
  376. }
  377. }
  378. break;
  379. default:
  380. //anims seem to get reset after a load, so just stop attacking and it will restart as needed.
  381. TIMER_Remove( NPC, "attacking" );
  382. break;
  383. }
  384. // Just using this to remove the attacking flag at the right time
  385. TIMER_Done2( NPC, "attacking", qtrue );
  386. }
  387. //----------------------------------
  388. static void Howler_Combat( void )
  389. {
  390. qboolean faced = qfalse;
  391. float distance;
  392. qboolean advance = qfalse;
  393. if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE )
  394. {//not on the ground
  395. if ( NPC->client->ps.legsAnim == BOTH_JUMP1
  396. || NPC->client->ps.legsAnim == BOTH_INAIR1 )
  397. {//flying through the air with the greatest of ease, etc
  398. Howler_TryDamage( 10, qfalse, qfalse );
  399. }
  400. }
  401. else
  402. {//not in air, see if we should attack or advance
  403. // If we cannot see our target or we have somewhere to go, then do that
  404. if ( !NPC_ClearLOS( NPC->enemy ) )//|| UpdateGoal( ))
  405. {
  406. NPCInfo->goalEntity = NPC->enemy;
  407. NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
  408. if ( NPCInfo->localState == LSTATE_BERZERK )
  409. {
  410. NPC_Howler_Move( 3 );
  411. }
  412. else
  413. {
  414. NPC_Howler_Move( 10 );
  415. }
  416. NPC_UpdateAngles( qfalse, qtrue );
  417. return;
  418. }
  419. distance = DistanceHorizontal( NPC->currentOrigin, NPC->enemy->currentOrigin );
  420. if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps ) )
  421. {//get really close to knocked down enemies
  422. advance = (qboolean)( distance > MIN_DISTANCE ? qtrue : qfalse );
  423. }
  424. else
  425. {
  426. advance = (qboolean)( distance > MAX_DISTANCE ? qtrue : qfalse );//MIN_DISTANCE
  427. }
  428. if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
  429. {
  430. if ( TIMER_Done2( NPC, "takingPain", qtrue ))
  431. {
  432. NPCInfo->localState = LSTATE_CLEAR;
  433. }
  434. else if ( TIMER_Done( NPC, "standing" ) )
  435. {
  436. faced = Howler_Move( 1 );
  437. }
  438. }
  439. else
  440. {
  441. Howler_Attack( distance );
  442. }
  443. }
  444. if ( !faced )
  445. {
  446. if ( //TIMER_Done( NPC, "standing" ) //not just standing there
  447. //!advance //not moving
  448. TIMER_Done( NPC, "attacking" ) )// not attacking
  449. {//not standing around
  450. // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
  451. NPC_FaceEnemy( qtrue );
  452. }
  453. else
  454. {
  455. NPC_UpdateAngles( qfalse, qtrue );
  456. }
  457. }
  458. }
  459. /*
  460. -------------------------
  461. NPC_Howler_Pain
  462. -------------------------
  463. */
  464. void NPC_Howler_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
  465. {
  466. if ( !self || !self->NPC )
  467. {
  468. return;
  469. }
  470. if ( self->NPC->localState != LSTATE_BERZERK )//damage >= 10 )
  471. {
  472. self->NPC->stats.aggression += damage;
  473. self->NPC->localState = LSTATE_WAITING;
  474. TIMER_Remove( self, "attacking" );
  475. VectorCopy( self->NPC->lastPathAngles, self->s.angles );
  476. //if ( self->client->ps.legsAnim == BOTH_GESTURE1 )
  477. {
  478. G_StopEffect( G_EffectIndex( "howler/sonic" ), self->playerModel, self->genericBolt1, self->s.number );
  479. }
  480. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  481. TIMER_Set( self, "takingPain", self->client->ps.legsAnimTimer );//2900 );
  482. if ( self->health > HOWLER_PANIC_HEALTH )
  483. {//still have some health left
  484. if ( Q_irand( 0, self->max_health ) > self->health )//FIXME: or check damage?
  485. {//back off!
  486. TIMER_Set( self, "standing", -level.time );
  487. TIMER_Set( self, "running", -level.time );
  488. TIMER_Set( self, "walking", -level.time );
  489. TIMER_Set( self, "retreating", Q_irand( 1000, 5000 ) );
  490. }
  491. else
  492. {//go after him!
  493. TIMER_Set( self, "standing", -level.time );
  494. TIMER_Set( self, "running", self->client->ps.legsAnimTimer+Q_irand(3000,6000) );
  495. TIMER_Set( self, "walking", -level.time );
  496. TIMER_Set( self, "retreating", -level.time );
  497. }
  498. }
  499. else if ( self->NPC )
  500. {//panic!
  501. if ( Q_irand( 0, 1 ) )
  502. {//berzerk
  503. self->NPC->localState = LSTATE_BERZERK;
  504. }
  505. else
  506. {//flee
  507. self->NPC->localState = LSTATE_FLEE;
  508. TIMER_Set( self, "flee", Q_irand( 10000, 30000 ) );
  509. }
  510. }
  511. }
  512. }
  513. /*
  514. -------------------------
  515. NPC_BSHowler_Default
  516. -------------------------
  517. */
  518. void NPC_BSHowler_Default( void )
  519. {
  520. if ( NPC->client->ps.legsAnim != BOTH_GESTURE1 )
  521. {
  522. NPC->count = 0;
  523. }
  524. //FIXME: if in jump, do damage in front and maybe knock them down?
  525. if ( !TIMER_Done( NPC, "attacking" ) )
  526. {
  527. if ( NPC->enemy )
  528. {
  529. //NPC_FaceEnemy( qfalse );
  530. Howler_Attack( Distance( NPC->enemy->currentOrigin, NPC->currentOrigin ) );
  531. }
  532. else
  533. {
  534. //NPC_UpdateAngles( qfalse, qtrue );
  535. Howler_Attack( 0.0f );
  536. }
  537. NPC_UpdateAngles( qfalse, qtrue );
  538. return;
  539. }
  540. if ( NPC->enemy )
  541. {
  542. if ( NPCInfo->stats.aggression > 0 )
  543. {
  544. if ( TIMER_Done( NPC, "aggressionDecay" ) )
  545. {
  546. NPCInfo->stats.aggression--;
  547. TIMER_Set( NPC, "aggressionDecay", 500 );
  548. }
  549. }
  550. if ( !TIMER_Done( NPC, "flee" )
  551. && NPC_BSFlee() ) //this can clear ENEMY
  552. {//successfully trying to run away
  553. return;
  554. }
  555. if ( NPC->enemy == NULL)
  556. {
  557. NPC_UpdateAngles( qfalse, qtrue );
  558. return;
  559. }
  560. if ( NPCInfo->localState == LSTATE_FLEE )
  561. {//we were fleeing, now done (either timer ran out or we cannot flee anymore
  562. if ( NPC_ClearLOS( NPC->enemy ) )
  563. {//if enemy is still around, go berzerk
  564. NPCInfo->localState = LSTATE_BERZERK;
  565. }
  566. else
  567. {//otherwise, lick our wounds?
  568. NPCInfo->localState = LSTATE_CLEAR;
  569. TIMER_Set( NPC, "standing", Q_irand( 3000, 10000 ) );
  570. }
  571. }
  572. else if ( NPCInfo->localState == LSTATE_BERZERK )
  573. {//go nuts!
  574. }
  575. else if ( NPCInfo->stats.aggression >= Q_irand( 75, 125 ) )
  576. {//that's it, go nuts!
  577. NPCInfo->localState = LSTATE_BERZERK;
  578. }
  579. else if ( !TIMER_Done( NPC, "retreating" ) )
  580. {//trying to back off
  581. NPC_FaceEnemy( qtrue );
  582. if ( NPC->client->ps.speed > NPCInfo->stats.walkSpeed )
  583. {
  584. NPC->client->ps.speed = NPCInfo->stats.walkSpeed;
  585. }
  586. ucmd.buttons |= BUTTON_WALKING;
  587. if ( Distance( NPC->enemy->currentOrigin, NPC->currentOrigin ) < HOWLER_RETREAT_DIST )
  588. {//enemy is close
  589. vec3_t moveDir;
  590. AngleVectors( NPC->currentAngles, moveDir, NULL, NULL );
  591. VectorScale( moveDir, -1, moveDir );
  592. if ( !NAV_DirSafe( NPC, moveDir, 8 ) )
  593. {//enemy is backing me up against a wall or ledge! Start to get really mad!
  594. NPCInfo->stats.aggression += 2;
  595. }
  596. else
  597. {//back off
  598. ucmd.forwardmove = -127;
  599. }
  600. //enemy won't leave me alone, get mad...
  601. NPCInfo->stats.aggression++;
  602. }
  603. return;
  604. }
  605. else if ( TIMER_Done( NPC, "standing" ) )
  606. {//not standing around
  607. if ( !(NPCInfo->last_ucmd.forwardmove)
  608. && !(NPCInfo->last_ucmd.rightmove) )
  609. {//stood last frame
  610. if ( TIMER_Done( NPC, "walking" )
  611. && TIMER_Done( NPC, "running" ) )
  612. {//not walking or running
  613. if ( Q_irand( 0, 2 ) )
  614. {//run for a while
  615. TIMER_Set( NPC, "walking", Q_irand( 4000, 8000 ) );
  616. }
  617. else
  618. {//walk for a bit
  619. TIMER_Set( NPC, "running", Q_irand( 2500, 5000 ) );
  620. }
  621. }
  622. }
  623. else if ( (NPCInfo->last_ucmd.buttons&BUTTON_WALKING) )
  624. {//walked last frame
  625. if ( TIMER_Done( NPC, "walking" ) )
  626. {//just finished walking
  627. if ( Q_irand( 0, 5 ) || DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ) < MAX_DISTANCE_SQR )
  628. {//run for a while
  629. TIMER_Set( NPC, "running", Q_irand( 4000, 20000 ) );
  630. }
  631. else
  632. {//stand for a bit
  633. TIMER_Set( NPC, "standing", Q_irand( 2000, 6000 ) );
  634. }
  635. }
  636. }
  637. else
  638. {//ran last frame
  639. if ( TIMER_Done( NPC, "running" ) )
  640. {//just finished running
  641. if ( Q_irand( 0, 8 ) || DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ) < MAX_DISTANCE_SQR )
  642. {//walk for a while
  643. TIMER_Set( NPC, "walking", Q_irand( 3000, 10000 ) );
  644. }
  645. else
  646. {//stand for a bit
  647. TIMER_Set( NPC, "standing", Q_irand( 2000, 6000 ) );
  648. }
  649. }
  650. }
  651. }
  652. if ( NPC_ValidEnemy( NPC->enemy ) == qfalse )
  653. {
  654. TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now
  655. if ( !NPC->enemy->inuse || level.time - NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
  656. {//it's been a while since the enemy died, or enemy is completely gone, get bored with him
  657. NPC->enemy = NULL;
  658. Howler_Patrol();
  659. NPC_UpdateAngles( qtrue, qtrue );
  660. return;
  661. }
  662. }
  663. if ( TIMER_Done( NPC, "lookForNewEnemy" ) )
  664. {
  665. gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy?
  666. NPC->enemy = NULL;
  667. gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse );
  668. NPC->enemy = sav_enemy;
  669. if ( newEnemy && newEnemy != sav_enemy )
  670. {//picked up a new enemy!
  671. NPC->lastEnemy = NPC->enemy;
  672. G_SetEnemy( NPC, newEnemy );
  673. if ( NPC->enemy != NPC->lastEnemy )
  674. {//clear this so that we only sniff the player the first time we pick them up
  675. NPC->useDebounceTime = 0;
  676. }
  677. //hold this one for at least 5-15 seconds
  678. TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
  679. }
  680. else
  681. {//look again in 2-5 secs
  682. TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
  683. }
  684. }
  685. Howler_Combat();
  686. if ( TIMER_Done( NPC, "speaking" ) )
  687. {
  688. if ( !TIMER_Done( NPC, "standing" )
  689. || !TIMER_Done( NPC, "retreating" ))
  690. {
  691. G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/idle_hiss%d.mp3", Q_irand( 1, 2 ) ) );
  692. }
  693. else if ( !TIMER_Done( NPC, "walking" )
  694. || NPCInfo->localState == LSTATE_FLEE )
  695. {
  696. G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_talk%d.mp3", Q_irand( 1, 5 ) ) );
  697. }
  698. else
  699. {
  700. G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_yell%d.mp3", Q_irand( 1, 5 ) ) );
  701. }
  702. if ( NPCInfo->localState == LSTATE_BERZERK
  703. || NPCInfo->localState == LSTATE_FLEE )
  704. {
  705. TIMER_Set( NPC, "speaking", Q_irand( 1000, 4000 ) );
  706. }
  707. else
  708. {
  709. TIMER_Set( NPC, "speaking", Q_irand( 3000, 8000 ) );
  710. }
  711. }
  712. return;
  713. }
  714. else
  715. {
  716. if ( TIMER_Done( NPC, "speaking" ) )
  717. {
  718. if ( !Q_irand( 0, 3 ) )
  719. {
  720. G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/idle_hiss%d.mp3", Q_irand( 1, 2 ) ) );
  721. }
  722. else
  723. {
  724. G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_talk%d.mp3", Q_irand( 1, 5 ) ) );
  725. }
  726. TIMER_Set( NPC, "speaking", Q_irand( 4000, 12000 ) );
  727. }
  728. if ( NPCInfo->stats.aggression > 0 )
  729. {
  730. if ( TIMER_Done( NPC, "aggressionDecay" ) )
  731. {
  732. NPCInfo->stats.aggression--;
  733. TIMER_Set( NPC, "aggressionDecay", 200 );
  734. }
  735. }
  736. if ( TIMER_Done( NPC, "standing" ) )
  737. {//not standing around
  738. if ( !(NPCInfo->last_ucmd.forwardmove)
  739. && !(NPCInfo->last_ucmd.rightmove) )
  740. {//stood last frame
  741. if ( TIMER_Done( NPC, "walking" )
  742. && TIMER_Done( NPC, "running" ) )
  743. {//not walking or running
  744. if ( NPCInfo->goalEntity )
  745. {//have somewhere to go
  746. if ( Q_irand( 0, 2 ) )
  747. {//walk for a while
  748. TIMER_Set( NPC, "walking", Q_irand( 3000, 10000 ) );
  749. }
  750. else
  751. {//run for a bit
  752. TIMER_Set( NPC, "running", Q_irand( 2500, 5000 ) );
  753. }
  754. }
  755. }
  756. }
  757. else if ( (NPCInfo->last_ucmd.buttons&BUTTON_WALKING) )
  758. {//walked last frame
  759. if ( TIMER_Done( NPC, "walking" ) )
  760. {//just finished walking
  761. if ( Q_irand( 0, 3 ) )
  762. {//run for a while
  763. TIMER_Set( NPC, "running", Q_irand( 3000, 6000 ) );
  764. }
  765. else
  766. {//stand for a bit
  767. TIMER_Set( NPC, "standing", Q_irand( 2500, 5000 ) );
  768. }
  769. }
  770. }
  771. else
  772. {//ran last frame
  773. if ( TIMER_Done( NPC, "running" ) )
  774. {//just finished running
  775. if ( Q_irand( 0, 2 ) )
  776. {//walk for a while
  777. TIMER_Set( NPC, "walking", Q_irand( 6000, 15000 ) );
  778. }
  779. else
  780. {//stand for a bit
  781. TIMER_Set( NPC, "standing", Q_irand( 4000, 6000 ) );
  782. }
  783. }
  784. }
  785. }
  786. if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
  787. {
  788. Howler_Patrol();
  789. }
  790. else
  791. {
  792. Howler_Idle();
  793. }
  794. }
  795. NPC_UpdateAngles( qfalse, qtrue );
  796. }