AI_Wampa.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  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 48
  6. #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
  7. #define MAX_DISTANCE 1024
  8. #define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
  9. #define LSTATE_CLEAR 0
  10. #define LSTATE_WAITING 1
  11. float enemyDist = 0;
  12. extern qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t &trace, int clipmask );
  13. extern int PM_AnimLength( int index, animNumber_t anim );
  14. extern cvar_t *g_dismemberment;
  15. /*
  16. -------------------------
  17. NPC_Wampa_Precache
  18. -------------------------
  19. */
  20. void NPC_Wampa_Precache( void )
  21. {
  22. /*
  23. int i;
  24. for ( i = 1; i < 4; i ++ )
  25. {
  26. G_SoundIndex( va("sound/chars/wampa/growl%d.wav", i) );
  27. }
  28. for ( i = 1; i < 3; i ++ )
  29. {
  30. G_SoundIndex( va("sound/chars/wampa/snort%d.wav", i) );
  31. }
  32. */
  33. G_SoundIndex( "sound/chars/rancor/swipehit.wav" );
  34. //G_SoundIndex( "sound/chars/wampa/chomp.wav" );
  35. }
  36. /*
  37. -------------------------
  38. Wampa_Idle
  39. -------------------------
  40. */
  41. void Wampa_Idle( void )
  42. {
  43. NPCInfo->localState = LSTATE_CLEAR;
  44. //If we have somewhere to go, then do that
  45. if ( UpdateGoal() )
  46. {
  47. ucmd.buttons &= ~BUTTON_WALKING;
  48. NPC_MoveToGoal( qtrue );
  49. }
  50. }
  51. qboolean Wampa_CheckRoar( gentity_t *self )
  52. {
  53. if ( self->wait < level.time )
  54. {
  55. self->wait = level.time + Q_irand( 5000, 20000 );
  56. NPC_SetAnim( self, SETANIM_BOTH, Q_irand(BOTH_GESTURE1,BOTH_GESTURE2), (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD) );
  57. TIMER_Set( self, "rageTime", self->client->ps.legsAnimTimer );
  58. return qtrue;
  59. }
  60. return qfalse;
  61. }
  62. /*
  63. -------------------------
  64. Wampa_Patrol
  65. -------------------------
  66. */
  67. void Wampa_Patrol( void )
  68. {
  69. NPCInfo->localState = LSTATE_CLEAR;
  70. //If we have somewhere to go, then do that
  71. if ( UpdateGoal() )
  72. {
  73. ucmd.buttons |= BUTTON_WALKING;
  74. NPC_MoveToGoal( qtrue );
  75. }
  76. if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
  77. {
  78. Wampa_Idle();
  79. return;
  80. }
  81. Wampa_CheckRoar( NPC );
  82. TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
  83. }
  84. /*
  85. -------------------------
  86. Wampa_Move
  87. -------------------------
  88. */
  89. void Wampa_Move( qboolean visible )
  90. {
  91. if ( NPCInfo->localState != LSTATE_WAITING )
  92. {
  93. NPCInfo->goalEntity = NPC->enemy;
  94. trace_t trace;
  95. if ( !NAV_CheckAhead( NPC, NPCInfo->goalEntity->currentOrigin, trace, (NPC->clipmask|CONTENTS_BOTCLIP) ) )
  96. {
  97. if ( !NPC_MoveToGoal( qfalse ) )
  98. {
  99. STEER::Activate(NPC);
  100. STEER::Seek(NPC, NPCInfo->goalEntity->currentOrigin);
  101. STEER::AvoidCollisions(NPC);
  102. STEER::DeActivate(NPC, &ucmd);
  103. }
  104. }
  105. NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE; // just get us within combat range
  106. if ( NPC->enemy )
  107. {//pick correct movement speed and anim
  108. //run by default
  109. ucmd.buttons &= ~BUTTON_WALKING;
  110. if ( !TIMER_Done( NPC, "runfar" )
  111. || !TIMER_Done( NPC, "runclose" ) )
  112. {//keep running with this anim & speed for a bit
  113. }
  114. else if ( !TIMER_Done( NPC, "walk" ) )
  115. {//keep walking for a bit
  116. ucmd.buttons |= BUTTON_WALKING;
  117. }
  118. else if ( visible && enemyDist > 350 && NPCInfo->stats.runSpeed == 200 )//180 )
  119. {//fast run, all fours
  120. //BOTH_RUN1
  121. NPCInfo->stats.runSpeed = 300;
  122. TIMER_Set( NPC, "runfar", Q_irand( 4000, 8000 ) );
  123. if ( NPC->client->ps.legsAnim == BOTH_RUN2 )
  124. {
  125. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN2TORUN1, SETANIM_FLAG_HOLD );
  126. }
  127. }
  128. else if ( enemyDist > 200 && NPCInfo->stats.runSpeed == 300 )
  129. {//slow run, upright
  130. //BOTH_RUN2
  131. NPCInfo->stats.runSpeed = 200;//180;
  132. TIMER_Set( NPC, "runclose", Q_irand( 5000, 10000 ) );
  133. if ( NPC->client->ps.legsAnim == BOTH_RUN1 )
  134. {
  135. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1TORUN2, SETANIM_FLAG_HOLD );
  136. }
  137. }
  138. else if ( enemyDist < 100 )
  139. {//walk
  140. NPCInfo->stats.runSpeed = 200;//180;
  141. ucmd.buttons |= BUTTON_WALKING;
  142. TIMER_Set( NPC, "walk", Q_irand( 6000, 12000 ) );
  143. }
  144. }
  145. }
  146. }
  147. //---------------------------------------------------------
  148. extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
  149. extern qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse );
  150. extern int NPC_GetEntsNearBolt( gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
  151. void Wampa_Slash( int boltIndex, qboolean backhand )
  152. {
  153. gentity_t *radiusEnts[ 128 ];
  154. int numEnts;
  155. const float radius = 88;
  156. const float radiusSquared = (radius*radius);
  157. int i;
  158. vec3_t boltOrg;
  159. int damage = (backhand)?Q_irand(10,15):Q_irand(20,30);
  160. numEnts = NPC_GetEntsNearBolt( radiusEnts, radius, boltIndex, boltOrg );
  161. for ( i = 0; i < numEnts; i++ )
  162. {
  163. if ( !radiusEnts[i]->inuse )
  164. {
  165. continue;
  166. }
  167. if ( radiusEnts[i] == NPC )
  168. {//Skip the wampa ent
  169. continue;
  170. }
  171. if ( radiusEnts[i]->client == NULL )
  172. {//must be a client
  173. continue;
  174. }
  175. if ( DistanceSquared( radiusEnts[i]->currentOrigin, boltOrg ) <= radiusSquared )
  176. {
  177. //smack
  178. G_Damage( radiusEnts[i], NPC, NPC, vec3_origin, radiusEnts[i]->currentOrigin, damage, ((backhand)?0:DAMAGE_NO_KNOCKBACK), MOD_MELEE );
  179. if ( backhand )
  180. {
  181. //actually push the enemy
  182. vec3_t pushDir;
  183. vec3_t angs;
  184. VectorCopy( NPC->client->ps.viewangles, angs );
  185. angs[YAW] += Q_flrand( 25, 50 );
  186. angs[PITCH] = Q_flrand( -25, -15 );
  187. AngleVectors( angs, pushDir, NULL, NULL );
  188. if ( radiusEnts[i]->client->NPC_class != CLASS_WAMPA
  189. && radiusEnts[i]->client->NPC_class != CLASS_RANCOR
  190. && radiusEnts[i]->client->NPC_class != CLASS_ATST
  191. && !(radiusEnts[i]->flags&FL_NO_KNOCKBACK) )
  192. {
  193. G_Throw( radiusEnts[i], pushDir, 65 );
  194. if ( radiusEnts[i]->health > 0 && Q_irand( 0, 1 ) )
  195. {//do pain on enemy
  196. G_Knockdown( radiusEnts[i], NPC, pushDir, 300, qtrue );
  197. }
  198. }
  199. }
  200. else if ( radiusEnts[i]->health <= 0 && radiusEnts[i]->client )
  201. {//killed them, chance of dismembering
  202. if ( !Q_irand( 0, 1 ) )
  203. {//bite something off
  204. int hitLoc = HL_HAND_LT;
  205. /*
  206. if ( g_dismemberment->integer < 11381138 )
  207. {
  208. hitLoc = Q_irand( HL_WAIST, HL_HAND_LT );
  209. }
  210. else
  211. {
  212. hitLoc = Q_irand( HL_WAIST, HL_HEAD );
  213. }
  214. if ( hitLoc == HL_HEAD )
  215. {
  216. NPC_SetAnim( radiusEnts[i], SETANIM_BOTH, BOTH_DEATH17, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  217. }
  218. else if ( hitLoc == HL_WAIST )
  219. {
  220. NPC_SetAnim( radiusEnts[i], SETANIM_BOTH, BOTH_DEATHBACKWARD2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  221. }
  222. */
  223. radiusEnts[i]->client->dismembered = false;
  224. //FIXME: the limb should just disappear, cuz I ate it
  225. G_DoDismemberment( radiusEnts[i], radiusEnts[i]->currentOrigin, MOD_SABER, 1000, hitLoc, qtrue );
  226. }
  227. }
  228. else if ( !Q_irand( 0, 3 ) && radiusEnts[i]->health > 0 )
  229. {//one out of every 4 normal hits does a knockdown, too
  230. vec3_t pushDir;
  231. vec3_t angs;
  232. VectorCopy( NPC->client->ps.viewangles, angs );
  233. angs[YAW] += Q_flrand( 25, 50 );
  234. angs[PITCH] = Q_flrand( -25, -15 );
  235. AngleVectors( angs, pushDir, NULL, NULL );
  236. G_Knockdown( radiusEnts[i], NPC, pushDir, 35, qtrue );
  237. }
  238. G_Sound( radiusEnts[i], G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
  239. }
  240. }
  241. }
  242. //------------------------------
  243. void Wampa_Attack( float distance, qboolean doCharge )
  244. {
  245. if ( !TIMER_Exists( NPC, "attacking" ) )
  246. {
  247. if ( !Q_irand(0, 3) && !doCharge )
  248. {//double slash
  249. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  250. TIMER_Set( NPC, "attack_dmg", 750 );
  251. }
  252. else if ( doCharge || (distance > 270 && distance < 430 && !Q_irand(0, 1)) )
  253. {//leap
  254. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  255. TIMER_Set( NPC, "attack_dmg", 500 );
  256. vec3_t fwd, yawAng ={0, NPC->client->ps.viewangles[YAW], 0};
  257. AngleVectors( yawAng, fwd, NULL, NULL );
  258. VectorScale( fwd, distance*1.5f, NPC->client->ps.velocity );
  259. NPC->client->ps.velocity[2] = 150;
  260. NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
  261. }
  262. else if ( distance < 100 )//&& !Q_irand( 0, 4 ) )
  263. {//grab
  264. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_START, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  265. NPC->client->ps.legsAnimTimer += 200;
  266. TIMER_Set( NPC, "attack_dmg", 250 );
  267. }
  268. else
  269. {//backhand
  270. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  271. TIMER_Set( NPC, "attack_dmg", 250 );
  272. }
  273. TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + random() * 200 );
  274. //allow us to re-evaluate our running speed/anim
  275. TIMER_Set( NPC, "runfar", -1 );
  276. TIMER_Set( NPC, "runclose", -1 );
  277. TIMER_Set( NPC, "walk", -1 );
  278. }
  279. // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
  280. if ( TIMER_Done2( NPC, "attack_dmg", qtrue ) )
  281. {
  282. switch ( NPC->client->ps.legsAnim )
  283. {
  284. case BOTH_ATTACK1:
  285. Wampa_Slash( NPC->handRBolt, qfalse );
  286. //do second hit
  287. TIMER_Set( NPC, "attack_dmg2", 100 );
  288. break;
  289. case BOTH_ATTACK2:
  290. Wampa_Slash( NPC->handRBolt, qfalse );
  291. TIMER_Set( NPC, "attack_dmg2", 100 );
  292. break;
  293. case BOTH_ATTACK3:
  294. Wampa_Slash( NPC->handLBolt, qtrue );
  295. break;
  296. }
  297. }
  298. else if ( TIMER_Done2( NPC, "attack_dmg2", qtrue ) )
  299. {
  300. switch ( NPC->client->ps.legsAnim )
  301. {
  302. case BOTH_ATTACK1:
  303. Wampa_Slash( NPC->handLBolt, qfalse );
  304. break;
  305. case BOTH_ATTACK2:
  306. Wampa_Slash( NPC->handLBolt, qfalse );
  307. break;
  308. }
  309. }
  310. // Just using this to remove the attacking flag at the right time
  311. TIMER_Done2( NPC, "attacking", qtrue );
  312. if ( NPC->client->ps.legsAnim == BOTH_ATTACK1 && distance > (NPC->maxs[0]+MIN_DISTANCE) )
  313. {//okay to keep moving
  314. ucmd.buttons |= BUTTON_WALKING;
  315. Wampa_Move( 1 );
  316. }
  317. }
  318. //----------------------------------
  319. void Wampa_Combat( void )
  320. {
  321. // If we cannot see our target or we have somewhere to go, then do that
  322. if ( !NPC_ClearLOS( NPC->enemy ) )
  323. {
  324. if ( !Q_irand( 0, 10 ) )
  325. {
  326. if ( Wampa_CheckRoar( NPC ) )
  327. {
  328. return;
  329. }
  330. }
  331. NPCInfo->combatMove = qtrue;
  332. NPCInfo->goalEntity = NPC->enemy;
  333. NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE; // just get us within combat range
  334. Wampa_Move( 0 );
  335. return;
  336. }
  337. /*
  338. else if ( UpdateGoal() )
  339. {
  340. NPCInfo->combatMove = qtrue;
  341. NPCInfo->goalEntity = NPC->enemy;
  342. NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE; // just get us within combat range
  343. Wampa_Move( 1 );
  344. return;
  345. }*/
  346. // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
  347. //FIXME: always seems to face off to the left or right?!!!!
  348. NPC_FaceEnemy( qtrue );
  349. float distance = enemyDist = Distance( NPC->currentOrigin, NPC->enemy->currentOrigin );
  350. qboolean advance = (qboolean)( distance > (NPC->maxs[0]+MIN_DISTANCE) ? qtrue : qfalse );
  351. qboolean doCharge = qfalse;
  352. if ( advance )
  353. {//have to get closer
  354. vec3_t yawOnlyAngles = {0, NPC->currentAngles[YAW], 0};
  355. if ( NPC->enemy->health > 0//enemy still alive
  356. && fabs(distance-350) <= 80 //enemy anywhere from 270 to 430 away
  357. && InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, yawOnlyAngles, 20, 20 ) )//enemy generally in front
  358. {//10% chance of doing charge anim
  359. if ( !Q_irand( 0, 6 ) )
  360. {//go for the charge
  361. doCharge = qtrue;
  362. advance = qfalse;
  363. }
  364. }
  365. }
  366. if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
  367. {
  368. if ( TIMER_Done2( NPC, "takingPain", qtrue ))
  369. {
  370. NPCInfo->localState = LSTATE_CLEAR;
  371. }
  372. else
  373. {
  374. Wampa_Move( 1 );
  375. }
  376. }
  377. else
  378. {
  379. if ( !Q_irand( 0, 15 ) )
  380. {//FIXME: only do this if we just damaged them or vice-versa?
  381. if ( Wampa_CheckRoar( NPC ) )
  382. {
  383. return;
  384. }
  385. }
  386. Wampa_Attack( distance, doCharge );
  387. }
  388. }
  389. /*
  390. -------------------------
  391. NPC_Wampa_Pain
  392. -------------------------
  393. */
  394. void NPC_Wampa_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
  395. {
  396. qboolean hitByWampa = qfalse;
  397. if ( self->count )
  398. {//FIXME: need pain anim
  399. NPC_SetAnim( self, SETANIM_BOTH, BOTH_STAND2TO1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  400. TIMER_Set( self, "takingPain", self->client->ps.legsAnimTimer );
  401. TIMER_Set(self,"attacking",-level.time);
  402. return;
  403. }
  404. if ( other&&other->client&&other->client->NPC_class==CLASS_WAMPA )
  405. {
  406. hitByWampa = qtrue;
  407. }
  408. if ( other
  409. && other->inuse
  410. && other != self->enemy
  411. && !(other->flags&FL_NOTARGET) )
  412. {
  413. if ( (!other->s.number&&!Q_irand(0,3))
  414. || !self->enemy
  415. || self->enemy->health == 0
  416. || (self->enemy->client&&self->enemy->client->NPC_class == CLASS_WAMPA)
  417. || (!Q_irand(0, 4 ) && DistanceSquared( other->currentOrigin, self->currentOrigin ) < DistanceSquared( self->enemy->currentOrigin, self->currentOrigin )) )
  418. {//if my enemy is dead (or attacked by player) and I'm not still holding/eating someone, turn on the attacker
  419. //FIXME: if can't nav to my enemy, take this guy if I can nav to him
  420. self->lastEnemy = other;
  421. G_SetEnemy( self, other );
  422. if ( self->enemy != self->lastEnemy )
  423. {//clear this so that we only sniff the player the first time we pick them up
  424. self->useDebounceTime = 0;
  425. }
  426. TIMER_Set( self, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
  427. if ( hitByWampa )
  428. {//stay mad at this Wampa for 2-5 secs before looking for other enemies
  429. TIMER_Set( self, "wampaInfight", Q_irand( 2000, 5000 ) );
  430. }
  431. }
  432. }
  433. if ( (hitByWampa|| Q_irand( 0, 100 ) < damage )//hit by wampa, hit while holding live victim, or took a lot of damage
  434. && self->client->ps.legsAnim != BOTH_GESTURE1
  435. && self->client->ps.legsAnim != BOTH_GESTURE2
  436. && TIMER_Done( self, "takingPain" ) )
  437. {
  438. if ( !Wampa_CheckRoar( self ) )
  439. {
  440. if ( self->client->ps.legsAnim != BOTH_ATTACK1
  441. && self->client->ps.legsAnim != BOTH_ATTACK2
  442. && self->client->ps.legsAnim != BOTH_ATTACK3 )
  443. {//cant interrupt one of the big attack anims
  444. if ( self->health > 100 || hitByWampa )
  445. {
  446. TIMER_Remove( self, "attacking" );
  447. VectorCopy( self->NPC->lastPathAngles, self->s.angles );
  448. if ( !Q_irand( 0, 1 ) )
  449. {
  450. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  451. }
  452. else
  453. {
  454. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  455. }
  456. TIMER_Set( self, "takingPain", self->client->ps.legsAnimTimer+Q_irand(0, 500*(2-g_spskill->integer)) );
  457. TIMER_Set(self,"attacking",-level.time);
  458. //allow us to re-evaluate our running speed/anim
  459. TIMER_Set( self, "runfar", -1 );
  460. TIMER_Set( self, "runclose", -1 );
  461. TIMER_Set( self, "walk", -1 );
  462. if ( self->NPC )
  463. {
  464. self->NPC->localState = LSTATE_WAITING;
  465. }
  466. }
  467. }
  468. }
  469. }
  470. }
  471. void Wampa_DropVictim( gentity_t *self )
  472. {
  473. //FIXME: if Wampa dies, it should drop its victim.
  474. //FIXME: if Wampa is removed, it must remove its victim.
  475. //FIXME: if in BOTH_HOLD_DROP, throw them a little, too?
  476. if ( self->health > 0 )
  477. {
  478. NPC_SetAnim( self, SETANIM_BOTH, BOTH_STAND2TO1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  479. }
  480. TIMER_Set(self,"attacking",-level.time);
  481. if ( self->activator )
  482. {
  483. if ( self->activator->client )
  484. {
  485. self->activator->client->ps.eFlags &= ~EF_HELD_BY_WAMPA;
  486. }
  487. self->activator->activator = NULL;
  488. NPC_SetAnim( self->activator, SETANIM_BOTH, BOTH_RELEASED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  489. self->activator->client->ps.legsAnimTimer += 500;
  490. self->activator->client->ps.weaponTime = self->activator->client->ps.torsoAnimTimer = self->activator->client->ps.legsAnimTimer;
  491. if ( self->activator->health > 0 )
  492. {
  493. if ( self->activator->NPC )
  494. {//start thinking again
  495. self->activator->NPC->nextBStateThink = level.time;
  496. }
  497. if ( self->activator->client && self->activator->s.number < MAX_CLIENTS )
  498. {
  499. vec3_t vicAngles = {30,AngleNormalize180(self->client->ps.viewangles[YAW]+180),0};
  500. SetClientViewAngle( self->activator, vicAngles );
  501. }
  502. }
  503. else
  504. {
  505. if ( self->enemy == self->activator )
  506. {
  507. self->enemy = NULL;
  508. }
  509. self->activator->clipmask &= ~CONTENTS_BODY;
  510. }
  511. self->activator = NULL;
  512. }
  513. self->count = 0;//drop him
  514. }
  515. qboolean Wampa_CheckDropVictim( gentity_t *self, qboolean excludeMe )
  516. {
  517. if ( !self
  518. || !self->activator )
  519. {
  520. return qtrue;
  521. }
  522. vec3_t mins={self->activator->mins[0]-1,self->activator->mins[1]-1,0};
  523. vec3_t maxs={self->activator->maxs[0]+1,self->activator->maxs[1]+1,1};
  524. vec3_t start={self->activator->currentOrigin[0],self->activator->currentOrigin[1],self->activator->absmin[2]};
  525. vec3_t end={self->activator->currentOrigin[0],self->activator->currentOrigin[1],self->activator->absmax[2]-1};
  526. trace_t trace;
  527. if ( excludeMe )
  528. {
  529. gi.unlinkentity( self );
  530. }
  531. gi.trace( &trace, start, mins, maxs, end, self->activator->s.number, self->activator->clipmask );
  532. if ( excludeMe )
  533. {
  534. gi.linkentity( self );
  535. }
  536. if ( !trace.allsolid && !trace.startsolid && trace.fraction >= 1.0f )
  537. {
  538. Wampa_DropVictim( self );
  539. return qtrue;
  540. }
  541. if ( excludeMe )
  542. {//victim stuck in wall
  543. if ( self->NPC )
  544. {//turn
  545. self->NPC->desiredYaw += Q_irand( -30, 30 );
  546. self->NPC->lockedDesiredYaw = self->NPC->desiredYaw;
  547. }
  548. }
  549. return qfalse;
  550. }
  551. extern float NPC_EnemyRangeFromBolt( int boltIndex );
  552. qboolean Wampa_TryGrab( void )
  553. {
  554. const float radius = 64.0f;
  555. if ( !NPC->enemy
  556. || !NPC->enemy->client
  557. || NPC->enemy->health <= 0 )
  558. {
  559. return qfalse;
  560. }
  561. float enemyDist = NPC_EnemyRangeFromBolt( NPC->handRBolt );
  562. if ( enemyDist <= radius
  563. && !NPC->count //don't have one in hand already
  564. && NPC->enemy->client->NPC_class != CLASS_RANCOR
  565. && NPC->enemy->client->NPC_class != CLASS_GALAKMECH
  566. && NPC->enemy->client->NPC_class != CLASS_ATST
  567. && NPC->enemy->client->NPC_class != CLASS_GONK
  568. && NPC->enemy->client->NPC_class != CLASS_R2D2
  569. && NPC->enemy->client->NPC_class != CLASS_R5D2
  570. && NPC->enemy->client->NPC_class != CLASS_MARK1
  571. && NPC->enemy->client->NPC_class != CLASS_MARK2
  572. && NPC->enemy->client->NPC_class != CLASS_MOUSE
  573. && NPC->enemy->client->NPC_class != CLASS_PROBE
  574. && NPC->enemy->client->NPC_class != CLASS_SEEKER
  575. && NPC->enemy->client->NPC_class != CLASS_REMOTE
  576. && NPC->enemy->client->NPC_class != CLASS_SENTRY
  577. && NPC->enemy->client->NPC_class != CLASS_INTERROGATOR
  578. && NPC->enemy->client->NPC_class != CLASS_VEHICLE )
  579. {//grab
  580. NPC->enemy = NPC->enemy;//make him my new best friend
  581. NPC->enemy->client->ps.eFlags |= EF_HELD_BY_WAMPA;
  582. //FIXME: this makes it so that the victim can't hit us with shots! Just use activator or something
  583. NPC->enemy->activator = NPC; // kind of dumb, but when we are locked to the Rancor, we are owned by it.
  584. NPC->activator = NPC->enemy;//remember him
  585. NPC->count = 1;//in my hand
  586. //wait to attack
  587. TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + Q_irand(500, 2500) );
  588. NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_GRABBED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  589. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  590. TIMER_Set( NPC, "takingPain", -level.time );
  591. return qtrue;
  592. }
  593. else if ( enemyDist < radius*2.0f )
  594. {//smack
  595. G_Sound( NPC->enemy, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
  596. //actually push the enemy
  597. vec3_t pushDir;
  598. vec3_t angs;
  599. VectorCopy( NPC->client->ps.viewangles, angs );
  600. angs[YAW] += Q_flrand( 25, 50 );
  601. angs[PITCH] = Q_flrand( -25, -15 );
  602. AngleVectors( angs, pushDir, NULL, NULL );
  603. if ( NPC->enemy->client->NPC_class != CLASS_RANCOR
  604. && NPC->enemy->client->NPC_class != CLASS_ATST
  605. && !(NPC->enemy->flags&FL_NO_KNOCKBACK) )
  606. {
  607. G_Throw( NPC->enemy, pushDir, Q_irand( 30, 70 ) );
  608. if ( NPC->enemy->health > 0 )
  609. {//do pain on enemy
  610. G_Knockdown( NPC->enemy, NPC, pushDir, 300, qtrue );
  611. }
  612. }
  613. }
  614. return qfalse;
  615. }
  616. /*
  617. -------------------------
  618. NPC_BSWampa_Default
  619. -------------------------
  620. */
  621. void NPC_BSWampa_Default( void )
  622. {
  623. //NORMAL ANIMS
  624. // stand1 = normal stand
  625. // walk1 = normal, non-angry walk
  626. // walk2 = injured
  627. // run1 = far away run
  628. // run2 = close run
  629. //VICTIM ANIMS
  630. // grabswipe = melee1 - sweep out and grab
  631. // stand2 attack = attack4 - while holding victim, swipe at him
  632. // walk3_drag = walk5 - walk with drag
  633. // stand2 = hold victim
  634. // stand2to1 = drop victim
  635. if ( NPC->client->ps.legsAnim == BOTH_HOLD_START )
  636. {
  637. NPC_FaceEnemy( qtrue );
  638. if ( NPC->client->ps.legsAnimTimer < 200 )
  639. {//see if he's there to grab
  640. if ( !Wampa_TryGrab() )
  641. {
  642. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  643. }
  644. }
  645. return;
  646. }
  647. if ( NPC->count )
  648. {
  649. if ( !NPC->activator
  650. || !NPC->activator->client )
  651. {//wtf?
  652. NPC->count = 0;
  653. NPC->activator = NULL;
  654. }
  655. else
  656. {
  657. if ( NPC->client->ps.legsAnim == BOTH_HOLD_DROP )
  658. {
  659. if ( NPC->client->ps.legsAnimTimer < PM_AnimLength(NPC->client->clientInfo.animFileIndex, (animNumber_t)NPC->client->ps.legsAnim)-500 )
  660. {//at least half a second into the anim
  661. if ( Wampa_CheckDropVictim( NPC, qfalse ) )
  662. {
  663. TIMER_Set( NPC, "attacking", 1000+(Q_irand(500,1000)*(3-g_spskill->integer)) );
  664. }
  665. }
  666. }
  667. else if ( !TIMER_Done( NPC, "takingPain" ) )
  668. {
  669. Wampa_CheckDropVictim( NPC, qfalse );
  670. }
  671. else if ( NPC->activator->health <= 0 )
  672. {
  673. if ( TIMER_Done(NPC,"sniffCorpse") )
  674. {
  675. Wampa_CheckDropVictim( NPC, qfalse );
  676. }
  677. }
  678. else if ( NPC->useDebounceTime >= level.time
  679. && NPC->activator )
  680. {//just sniffing the guy
  681. if ( NPC->useDebounceTime <= level.time + 100
  682. && NPC->client->ps.legsAnim != BOTH_HOLD_DROP)
  683. {//just about done, drop him
  684. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_DROP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  685. TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer+500 );
  686. }
  687. }
  688. else
  689. {
  690. if ( !NPC->useDebounceTime
  691. && NPC->activator
  692. && NPC->activator->s.number < MAX_CLIENTS )
  693. {//first time I pick the player, just sniff them
  694. if ( TIMER_Done(NPC,"attacking") )
  695. {//ready to attack
  696. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_SNIFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  697. NPC->useDebounceTime = level.time + NPC->client->ps.legsAnimTimer + Q_irand( 500, 2000 );
  698. }
  699. }
  700. else
  701. {
  702. if ( TIMER_Done(NPC,"attacking") )
  703. {//ready to attack
  704. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_ATTACK/*BOTH_ATTACK4*/, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  705. TIMER_Set(NPC,"grabAttackDamage",1400);
  706. TIMER_Set(NPC,"attacking",NPC->client->ps.legsAnimTimer+Q_irand(3000,10000));
  707. }
  708. if ( NPC->client->ps.legsAnim == BOTH_HOLD_ATTACK )
  709. {
  710. if ( NPC->client->ps.legsAnimTimer )
  711. {
  712. if ( TIMER_Done2(NPC,"grabAttackDamage",qtrue) )
  713. {
  714. G_Sound( NPC->activator, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
  715. G_Damage( NPC->activator, NPC, NPC, vec3_origin, NPC->activator->currentOrigin, Q_irand( 25, 40 ), (DAMAGE_NO_KNOCKBACK|DAMAGE_NO_ARMOR), MOD_MELEE );
  716. if ( NPC->activator->health <= 0 )
  717. {//killed them, chance of dismembering
  718. int hitLoc = HL_HAND_LT;
  719. // if ( g_dismemberment->integer < 11381138 )
  720. // {
  721. // hitLoc = Q_irand( HL_WAIST, HL_HAND_LT );
  722. // }
  723. // else
  724. // {
  725. // hitLoc = Q_irand( HL_WAIST, HL_HEAD );
  726. // }
  727. NPC->activator->client->dismembered = false;
  728. //FIXME: the limb should just disappear, cuz I ate it
  729. G_DoDismemberment( NPC->activator, NPC->activator->currentOrigin, MOD_SABER, 1000, hitLoc, qtrue );
  730. TIMER_Set( NPC, "sniffCorpse", Q_irand( 2000, 5000 ) );
  731. }
  732. NPC_SetAnim( NPC->activator, SETANIM_BOTH, BOTH_HANG_PAIN, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  733. }
  734. }
  735. else
  736. {
  737. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_IDLE/*BOTH_ATTACK4*/, SETANIM_FLAG_NORMAL );
  738. }
  739. }
  740. else if ( NPC->client->ps.legsAnim == BOTH_STAND2TO1
  741. && !NPC->client->ps.legsAnimTimer )
  742. {
  743. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_HOLD_IDLE, SETANIM_FLAG_NORMAL );
  744. }
  745. }
  746. }
  747. }
  748. NPC_UpdateAngles( qtrue, qtrue );
  749. return;
  750. }
  751. if ( NPCInfo->localState == LSTATE_WAITING
  752. && TIMER_Done2( NPC, "takingPain", qtrue ) )
  753. {//was not doing anything because we were taking pain, but pain is done now, so clear it...
  754. NPCInfo->localState = LSTATE_CLEAR;
  755. }
  756. if ( !TIMER_Done( NPC, "rageTime" ) )
  757. {//do nothing but roar first time we see an enemy
  758. NPC_FaceEnemy( qtrue );
  759. return;
  760. }
  761. if ( NPC->enemy )
  762. {
  763. if ( NPC->enemy->client //enemy is a client
  764. && (NPC->enemy->client->NPC_class == CLASS_UGNAUGHT || NPC->enemy->client->NPC_class == CLASS_JAWA )//enemy is a lowly jawa or ugnaught
  765. && NPC->enemy->enemy != NPC//enemy's enemy is not me
  766. && (!NPC->enemy->enemy || !NPC->enemy->enemy->client || NPC->enemy->enemy->client->NPC_class!=CLASS_RANCOR) )//enemy's enemy is not a client or is not a rancor (which is scarier than me)
  767. {//they should be scared of ME and no-one else
  768. G_SetEnemy( NPC->enemy, NPC );
  769. }
  770. if ( !TIMER_Done(NPC,"attacking") )
  771. {//in middle of attack
  772. //face enemy
  773. NPC_FaceEnemy( qtrue );
  774. //continue attack logic
  775. enemyDist = Distance( NPC->currentOrigin, NPC->enemy->currentOrigin );
  776. Wampa_Attack( enemyDist, qfalse );
  777. return;
  778. }
  779. else
  780. {
  781. if ( TIMER_Done(NPC,"angrynoise") )
  782. {
  783. G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/wampa/misc/anger%d.wav", Q_irand(1, 2)) );
  784. TIMER_Set( NPC, "angrynoise", Q_irand( 5000, 10000 ) );
  785. }
  786. //else, if he's in our hand, we eat, else if he's on the ground, we keep attacking his dead body for a while
  787. if( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_WAMPA )
  788. {//got mad at another Wampa, look for a valid enemy
  789. if ( TIMER_Done( NPC, "wampaInfight" ) )
  790. {
  791. NPC_CheckEnemyExt( qtrue );
  792. }
  793. }
  794. else
  795. {
  796. if ( NPC_ValidEnemy( NPC->enemy ) == qfalse )
  797. {
  798. TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now
  799. if ( !NPC->enemy->inuse || level.time - NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
  800. {//it's been a while since the enemy died, or enemy is completely gone, get bored with him
  801. NPC->enemy = NULL;
  802. Wampa_Patrol();
  803. NPC_UpdateAngles( qtrue, qtrue );
  804. return;
  805. }
  806. }
  807. if ( TIMER_Done( NPC, "lookForNewEnemy" ) )
  808. {
  809. gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy?
  810. NPC->enemy = NULL;
  811. gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse );
  812. NPC->enemy = sav_enemy;
  813. if ( newEnemy && newEnemy != sav_enemy )
  814. {//picked up a new enemy!
  815. NPC->lastEnemy = NPC->enemy;
  816. G_SetEnemy( NPC, newEnemy );
  817. if ( NPC->enemy != NPC->lastEnemy )
  818. {//clear this so that we only sniff the player the first time we pick them up
  819. NPC->useDebounceTime = 0;
  820. }
  821. //hold this one for at least 5-15 seconds
  822. TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
  823. }
  824. else
  825. {//look again in 2-5 secs
  826. TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
  827. }
  828. }
  829. }
  830. Wampa_Combat();
  831. return;
  832. }
  833. }
  834. else
  835. {
  836. if ( TIMER_Done(NPC,"idlenoise") )
  837. {
  838. G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/wampa/misc/anger3.wav" );
  839. TIMER_Set( NPC, "idlenoise", Q_irand( 2000, 4000 ) );
  840. }
  841. if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
  842. {
  843. Wampa_Patrol();
  844. }
  845. else
  846. {
  847. Wampa_Idle();
  848. }
  849. }
  850. NPC_UpdateAngles( qtrue, qtrue );
  851. }