m_stalker.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. stalker
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_stalker.h"
  10. #include <float.h>
  11. static int sound_pain;
  12. static int sound_die;
  13. static int sound_sight;
  14. static int sound_punch_hit1;
  15. static int sound_punch_hit2;
  16. static int sound_idle;
  17. int stalker_do_pounce(edict_t *self, vec3_t dest);
  18. void stalker_stand (edict_t *self);
  19. void stalker_run (edict_t *self);
  20. void stalker_walk (edict_t *self);
  21. void stalker_jump (edict_t *self);
  22. void stalker_dodge_jump (edict_t *self);
  23. void stalker_swing_check_l (edict_t *self);
  24. void stalker_swing_check_r (edict_t *self);
  25. void stalker_swing_attack (edict_t *self);
  26. void stalker_jump_straightup (edict_t *self);
  27. void stalker_jump_wait_land (edict_t *self);
  28. void stalker_false_death (edict_t *self);
  29. void stalker_false_death_start (edict_t *self);
  30. qboolean stalker_ok_to_transition (edict_t *self);
  31. #define STALKER_ON_CEILING(ent) ( ent->gravityVector[2] > 0 ? 1 : 0 )
  32. //extern qboolean SV_StepDirection (edict_t *ent, float yaw, float dist);
  33. extern qboolean SV_PointCloseEnough (edict_t *ent, vec3_t goal, float dist);
  34. extern void drawbbox(edict_t *self);
  35. //=========================
  36. //=========================
  37. qboolean stalker_ok_to_transition (edict_t *self)
  38. {
  39. trace_t trace;
  40. vec3_t pt, start;
  41. float max_dist;
  42. float margin;
  43. float end_height;
  44. if(STALKER_ON_CEILING(self))
  45. {
  46. max_dist = -384;
  47. margin = self->mins[2] - 8;
  48. }
  49. else
  50. {
  51. // her stalkers are just better
  52. if (self->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
  53. max_dist = 256;
  54. else
  55. max_dist = 180;
  56. margin = self->maxs[2] + 8;
  57. }
  58. VectorCopy(self->s.origin, pt);
  59. pt[2] += max_dist;
  60. trace = gi.trace (self->s.origin, self->mins, self->maxs, pt, self, MASK_MONSTERSOLID);
  61. if(trace.fraction == 1.0 ||
  62. !(trace.contents & CONTENTS_SOLID) ||
  63. (trace.ent != world))
  64. {
  65. if(STALKER_ON_CEILING(self))
  66. {
  67. if(trace.plane.normal[2] < 0.9)
  68. return false;
  69. }
  70. else
  71. {
  72. if(trace.plane.normal[2] > -0.9)
  73. return false;
  74. }
  75. }
  76. // gi.dprintf("stalker_check_pt: main check ok\n");
  77. end_height = trace.endpos[2];
  78. // check the four corners, tracing only to the endpoint of the center trace (vertically).
  79. pt[0] = self->absmin[0];
  80. pt[1] = self->absmin[1];
  81. pt[2] = trace.endpos[2] + margin; // give a little margin of error to allow slight inclines
  82. VectorCopy(pt, start);
  83. start[2] = self->s.origin[2];
  84. trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
  85. if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
  86. {
  87. // gi.dprintf("stalker_check_pt: absmin/absmin failed\n");
  88. return false;
  89. }
  90. if(abs(end_height + margin - trace.endpos[2]) > 8)
  91. return false;
  92. pt[0] = self->absmax[0];
  93. pt[1] = self->absmin[1];
  94. VectorCopy(pt, start);
  95. start[2] = self->s.origin[2];
  96. trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
  97. if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
  98. {
  99. // gi.dprintf("stalker_check_pt: absmax/absmin failed\n");
  100. return false;
  101. }
  102. if(abs(end_height + margin - trace.endpos[2]) > 8)
  103. return false;
  104. pt[0] = self->absmax[0];
  105. pt[1] = self->absmax[1];
  106. VectorCopy(pt, start);
  107. start[2] = self->s.origin[2];
  108. trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
  109. if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
  110. {
  111. // gi.dprintf("stalker_check_pt: absmax/absmax failed\n");
  112. return false;
  113. }
  114. if(abs(end_height + margin - trace.endpos[2]) > 8)
  115. return false;
  116. pt[0] = self->absmin[0];
  117. pt[1] = self->absmax[1];
  118. VectorCopy(pt, start);
  119. start[2] = self->s.origin[2];
  120. trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
  121. if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
  122. {
  123. // gi.dprintf("stalker_check_pt: absmin/absmax failed\n");
  124. return false;
  125. }
  126. if(abs(end_height + margin - trace.endpos[2]) > 8)
  127. return false;
  128. return true;
  129. }
  130. //=========================
  131. //=========================
  132. void stalker_sight (edict_t *self, edict_t *other)
  133. {
  134. gi.sound (self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
  135. }
  136. // ******************
  137. // IDLE
  138. // ******************
  139. void stalker_idle_noise (edict_t *self)
  140. {
  141. gi.sound (self, CHAN_WEAPON, sound_idle, 0.5, ATTN_IDLE, 0);
  142. }
  143. mframe_t stalker_frames_idle [] =
  144. {
  145. ai_stand, 0, NULL,
  146. ai_stand, 0, NULL,
  147. ai_stand, 0, NULL,
  148. ai_stand, 0, NULL,
  149. ai_stand, 0, NULL,
  150. ai_stand, 0, NULL,
  151. ai_stand, 0, stalker_idle_noise,
  152. ai_stand, 0, NULL,
  153. ai_stand, 0, NULL,
  154. ai_stand, 0, NULL,
  155. ai_stand, 0, NULL,
  156. ai_stand, 0, NULL,
  157. ai_stand, 0, NULL,
  158. ai_stand, 0, NULL,
  159. ai_stand, 0, NULL,
  160. ai_stand, 0, NULL,
  161. ai_stand, 0, NULL,
  162. ai_stand, 0, NULL,
  163. ai_stand, 0, NULL,
  164. ai_stand, 0, NULL,
  165. ai_stand, 0, NULL
  166. };
  167. mmove_t stalker_move_idle = {FRAME_idle01, FRAME_idle21, stalker_frames_idle, stalker_stand};
  168. mframe_t stalker_frames_idle2 [] =
  169. {
  170. ai_stand, 0, NULL,
  171. ai_stand, 0, NULL,
  172. ai_stand, 0, NULL,
  173. ai_stand, 0, NULL,
  174. ai_stand, 0, NULL,
  175. ai_stand, 0, NULL,
  176. ai_stand, 0, NULL,
  177. ai_stand, 0, NULL,
  178. ai_stand, 0, NULL,
  179. ai_stand, 0, NULL,
  180. ai_stand, 0, NULL,
  181. ai_stand, 0, NULL,
  182. ai_stand, 0, NULL
  183. };
  184. mmove_t stalker_move_idle2 = {FRAME_idle201, FRAME_idle213, stalker_frames_idle2, stalker_stand};
  185. void stalker_idle (edict_t *self)
  186. {
  187. if (random() < 0.35)
  188. self->monsterinfo.currentmove = &stalker_move_idle;
  189. else
  190. self->monsterinfo.currentmove = &stalker_move_idle2;
  191. }
  192. // ******************
  193. // STAND
  194. // ******************
  195. mframe_t stalker_frames_stand [] =
  196. {
  197. ai_stand, 0, NULL,
  198. ai_stand, 0, NULL,
  199. ai_stand, 0, NULL,
  200. ai_stand, 0, NULL,
  201. ai_stand, 0, NULL,
  202. ai_stand, 0, NULL,
  203. ai_stand, 0, stalker_idle_noise,
  204. ai_stand, 0, NULL,
  205. ai_stand, 0, NULL,
  206. ai_stand, 0, NULL,
  207. ai_stand, 0, NULL,
  208. ai_stand, 0, NULL,
  209. ai_stand, 0, NULL,
  210. ai_stand, 0, NULL,
  211. ai_stand, 0, NULL,
  212. ai_stand, 0, NULL,
  213. ai_stand, 0, NULL,
  214. ai_stand, 0, NULL,
  215. ai_stand, 0, NULL,
  216. ai_stand, 0, NULL,
  217. ai_stand, 0, NULL
  218. };
  219. mmove_t stalker_move_stand = {FRAME_idle01, FRAME_idle21, stalker_frames_stand, stalker_stand};
  220. void stalker_stand (edict_t *self)
  221. {
  222. if (random() < 0.25)
  223. self->monsterinfo.currentmove = &stalker_move_stand;
  224. else
  225. self->monsterinfo.currentmove = &stalker_move_idle2;
  226. }
  227. // ******************
  228. // RUN
  229. // ******************
  230. mframe_t stalker_frames_run [] =
  231. {
  232. ai_run, 13, NULL,
  233. ai_run, 17, NULL,
  234. ai_run, 21, NULL,
  235. ai_run, 18, NULL
  236. /* ai_run, 15, NULL,
  237. ai_run, 20, NULL,
  238. ai_run, 18, NULL,
  239. ai_run, 14, NULL*/
  240. };
  241. mmove_t stalker_move_run = {FRAME_run01, FRAME_run04, stalker_frames_run, NULL};
  242. void stalker_run (edict_t *self)
  243. {
  244. // gi.dprintf("stalker_run %5.1f\n", level.time);
  245. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  246. self->monsterinfo.currentmove = &stalker_move_stand;
  247. else
  248. self->monsterinfo.currentmove = &stalker_move_run;
  249. }
  250. // ******************
  251. // WALK
  252. // ******************
  253. mframe_t stalker_frames_walk [] =
  254. {
  255. ai_walk, 4, NULL,
  256. ai_walk, 6, NULL,
  257. ai_walk, 8, NULL,
  258. ai_walk, 5, NULL,
  259. ai_walk, 4, NULL,
  260. ai_walk, 6, NULL,
  261. ai_walk, 8, NULL,
  262. ai_walk, 4, NULL
  263. };
  264. mmove_t stalker_move_walk = {FRAME_walk01, FRAME_walk08, stalker_frames_walk, stalker_walk};
  265. void stalker_walk (edict_t *self)
  266. {
  267. // gi.dprintf("stalker_walk\n");
  268. self->monsterinfo.currentmove = &stalker_move_walk;
  269. }
  270. // ******************
  271. // false death
  272. // ******************
  273. mframe_t stalker_frames_reactivate [] =
  274. {
  275. ai_move, 0, NULL,
  276. ai_move, 0, NULL,
  277. ai_move, 0, NULL,
  278. ai_move, 0, NULL
  279. };
  280. mmove_t stalker_move_false_death_end = { FRAME_reactive01, FRAME_reactive04, stalker_frames_reactivate, stalker_run };
  281. void stalker_reactivate (edict_t *self)
  282. {
  283. self->monsterinfo.aiflags &= ~AI_STAND_GROUND;
  284. self->monsterinfo.currentmove = &stalker_move_false_death_end;
  285. }
  286. void stalker_heal (edict_t *self)
  287. {
  288. if(skill->value == 2)
  289. self->health+=2;
  290. else if(skill->value == 3)
  291. self->health+=3;
  292. else
  293. self->health++;
  294. // gi.dprintf("stalker_heal: %d\n", self->health);
  295. if(self->health > (self->max_health/2))
  296. self->s.skinnum = 0;
  297. if(self->health >= self->max_health)
  298. {
  299. self->health = self->max_health;
  300. stalker_reactivate(self);
  301. }
  302. }
  303. mframe_t stalker_frames_false_death [] =
  304. {
  305. ai_move, 0, stalker_heal,
  306. ai_move, 0, stalker_heal,
  307. ai_move, 0, stalker_heal,
  308. ai_move, 0, stalker_heal,
  309. ai_move, 0, stalker_heal,
  310. ai_move, 0, stalker_heal,
  311. ai_move, 0, stalker_heal,
  312. ai_move, 0, stalker_heal,
  313. ai_move, 0, stalker_heal,
  314. ai_move, 0, stalker_heal
  315. };
  316. mmove_t stalker_move_false_death = {FRAME_twitch01, FRAME_twitch10, stalker_frames_false_death, stalker_false_death};
  317. void stalker_false_death (edict_t *self)
  318. {
  319. self->monsterinfo.currentmove = &stalker_move_false_death;
  320. }
  321. mframe_t stalker_frames_false_death_start [] =
  322. {
  323. ai_move, 0, NULL,
  324. ai_move, 0, NULL,
  325. ai_move, 0, NULL,
  326. ai_move, 0, NULL,
  327. ai_move, 0, NULL,
  328. ai_move, 0, NULL,
  329. ai_move, 0, NULL,
  330. ai_move, 0, NULL,
  331. ai_move, 0, NULL,
  332. };
  333. mmove_t stalker_move_false_death_start = {FRAME_death01, FRAME_death09, stalker_frames_false_death_start, stalker_false_death};
  334. void stalker_false_death_start (edict_t *self)
  335. {
  336. self->s.angles[2] = 0;
  337. VectorSet(self->gravityVector, 0, 0, -1);
  338. self->monsterinfo.aiflags |= AI_STAND_GROUND;
  339. self->monsterinfo.currentmove = &stalker_move_false_death_start;
  340. }
  341. // ******************
  342. // PAIN
  343. // ******************
  344. mframe_t stalker_frames_pain [] =
  345. {
  346. ai_move, 0, NULL,
  347. ai_move, 0, NULL,
  348. ai_move, 0, NULL,
  349. ai_move, 0, NULL
  350. };
  351. mmove_t stalker_move_pain = {FRAME_pain01, FRAME_pain04, stalker_frames_pain, stalker_run};
  352. void stalker_pain (edict_t *self, edict_t *other, float kick, int damage)
  353. {
  354. if (self->deadflag == DEAD_DEAD)
  355. return;
  356. if (self->health < (self->max_health / 2))
  357. {
  358. self->s.skinnum = 1;
  359. }
  360. if (skill->value == 3)
  361. return; // no pain anims in nightmare
  362. // if (self->monsterinfo.aiflags & AI_DODGING)
  363. // monster_done_dodge (self);
  364. if (self->groundentity == NULL)
  365. return;
  366. // if we're reactivating or false dying, ignore the pain.
  367. if (self->monsterinfo.currentmove == &stalker_move_false_death_end ||
  368. self->monsterinfo.currentmove == &stalker_move_false_death_start )
  369. return;
  370. if (self->monsterinfo.currentmove == &stalker_move_false_death)
  371. {
  372. stalker_reactivate(self);
  373. return;
  374. }
  375. if ((self->health > 0) && (self->health < (self->max_health / 4)))
  376. {
  377. if(random() < (0.2 * skill->value))
  378. {
  379. if( !STALKER_ON_CEILING(self) || stalker_ok_to_transition(self) )
  380. {
  381. // gi.dprintf("starting false death sequence\n");
  382. stalker_false_death_start(self);
  383. return;
  384. }
  385. }
  386. }
  387. if (level.time < self->pain_debounce_time)
  388. return;
  389. self->pain_debounce_time = level.time + 3;
  390. // gi.dprintf("stalker_pain\n");
  391. if (damage > 10) // don't react unless the damage was significant
  392. {
  393. // stalker should dodge jump periodically to help avoid damage.
  394. if(self->groundentity && (random() < 0.5))
  395. stalker_dodge_jump(self);
  396. else
  397. self->monsterinfo.currentmove = &stalker_move_pain;
  398. gi.sound (self, CHAN_WEAPON, sound_pain, 1, ATTN_NORM, 0);
  399. }
  400. }
  401. // ******************
  402. // STALKER ATTACK
  403. // ******************
  404. //extern qboolean infront (edict_t *self, edict_t *other);
  405. void stalker_shoot_attack (edict_t *self)
  406. {
  407. vec3_t offset, start, f, r, dir;
  408. vec3_t end;
  409. float time, dist;
  410. trace_t trace;
  411. if(!has_valid_enemy(self))
  412. return;
  413. if(self->groundentity && random() < 0.33)
  414. {
  415. VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
  416. dist = VectorLength (dir);
  417. if((dist > 256) || (random() < 0.5))
  418. stalker_do_pounce(self, self->enemy->s.origin);
  419. else
  420. stalker_jump_straightup (self);
  421. }
  422. // FIXME -- keep this but use a custom one
  423. // if (!infront(self, self->enemy))
  424. // return;
  425. AngleVectors (self->s.angles, f, r, NULL);
  426. VectorSet (offset, 24, 0, 6);
  427. G_ProjectSource (self->s.origin, offset, f, r, start);
  428. VectorSubtract(self->enemy->s.origin, start, dir);
  429. if(random() < (0.20 + 0.1 * skill->value))
  430. {
  431. dist = VectorLength(dir);
  432. time = dist / 1000;
  433. VectorMA(self->enemy->s.origin, time, self->enemy->velocity, end);
  434. VectorSubtract(end, start, dir);
  435. }
  436. else
  437. VectorCopy(self->enemy->s.origin, end);
  438. trace = gi.trace(start, vec3_origin, vec3_origin, end, self, MASK_SHOT);
  439. if(trace.ent == self->enemy || trace.ent == world)
  440. monster_fire_blaster2(self, start, dir, 15, 800, MZ2_STALKER_BLASTER, EF_BLASTER);
  441. // else
  442. // gi.dprintf("blocked by entity %s\n", trace.ent->classname);
  443. }
  444. void stalker_shoot_attack2 (edict_t *self)
  445. {
  446. // if (random() < (0.4+(float)skill->value))
  447. // stalker_shoot_attack (self);
  448. if (random() < (0.4 + (0.1 * (float)skill->value)))
  449. stalker_shoot_attack (self);
  450. }
  451. mframe_t stalker_frames_shoot [] =
  452. {
  453. ai_charge, 13, NULL,
  454. ai_charge, 17, stalker_shoot_attack,
  455. ai_charge, 21, NULL,
  456. ai_charge, 18, stalker_shoot_attack2
  457. };
  458. mmove_t stalker_move_shoot = {FRAME_run01, FRAME_run04, stalker_frames_shoot, stalker_run};
  459. void stalker_attack_ranged (edict_t *self)
  460. {
  461. if(!has_valid_enemy(self))
  462. return;
  463. // PMM - circle strafe stuff
  464. if (random() > (1.0 - (0.5/(float)(skill->value))))
  465. {
  466. self->monsterinfo.attack_state = AS_STRAIGHT;
  467. }
  468. else
  469. {
  470. if (random () <= 0.5) // switch directions
  471. self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
  472. self->monsterinfo.attack_state = AS_SLIDING;
  473. }
  474. self->monsterinfo.currentmove = &stalker_move_shoot;
  475. }
  476. // ******************
  477. // close combat
  478. // ******************
  479. void stalker_swing_attack (edict_t *self)
  480. {
  481. vec3_t aim;
  482. VectorSet (aim, MELEE_DISTANCE, 0, 0);
  483. if (fire_hit (self, aim, (5 + (rand() % 5)), 50))
  484. if (self->s.frame < FRAME_attack08)
  485. gi.sound (self, CHAN_WEAPON, sound_punch_hit2, 1, ATTN_NORM, 0);
  486. else
  487. gi.sound (self, CHAN_WEAPON, sound_punch_hit1, 1, ATTN_NORM, 0);
  488. }
  489. mframe_t stalker_frames_swing_l [] =
  490. {
  491. ai_charge, 2, NULL,
  492. ai_charge, 4, NULL,
  493. ai_charge, 6, NULL,
  494. ai_charge, 10, NULL,
  495. ai_charge, 5, stalker_swing_attack,
  496. ai_charge, 5, NULL,
  497. ai_charge, 5, NULL,
  498. ai_charge, 5, NULL // stalker_swing_check_l
  499. };
  500. mmove_t stalker_move_swing_l = {FRAME_attack01, FRAME_attack08, stalker_frames_swing_l, stalker_run};
  501. mframe_t stalker_frames_swing_r [] =
  502. {
  503. ai_charge, 4, NULL,
  504. ai_charge, 6, NULL,
  505. ai_charge, 6, stalker_swing_attack,
  506. ai_charge, 10, NULL,
  507. ai_charge, 5, NULL // stalker_swing_check_r
  508. };
  509. mmove_t stalker_move_swing_r = {FRAME_attack11, FRAME_attack15, stalker_frames_swing_r, stalker_run};
  510. void stalker_attack_melee (edict_t *self)
  511. {
  512. if(!has_valid_enemy(self))
  513. return;
  514. if(random() < 0.5)
  515. {
  516. self->monsterinfo.currentmove = &stalker_move_swing_l;
  517. }
  518. else
  519. {
  520. self->monsterinfo.currentmove = &stalker_move_swing_r;
  521. }
  522. }
  523. // ******************
  524. // POUNCE
  525. // ******************
  526. #define PI 3.14159
  527. #define RAD2DEG(x) (x * (float)180.0 / (float)PI)
  528. #define DEG2RAD(x) (x * (float)PI / (float)180.0)
  529. #define FAUX_GRAVITY 800.0
  530. // ====================
  531. // ====================
  532. void calcJumpAngle(vec3_t start, vec3_t end, float velocity, vec3_t angles)
  533. {
  534. float distV, distH;
  535. float one, cosU;
  536. float l, U;
  537. vec3_t dist;
  538. VectorSubtract(end, start, dist);
  539. distH = (float)sqrt(dist[0]*dist[0] + dist[1]*dist[1]);
  540. distV = dist[2];
  541. if(distV < 0)
  542. distV = 0 - distV;
  543. if(distV)
  544. {
  545. l = (float) sqrt(distH*distH + distV*distV);
  546. U = (float) atan(distV / distH);
  547. if(dist[2] > 0)
  548. U = (float)0.0 - U;
  549. angles[2] = 0.0;
  550. cosU = (float)cos(U);
  551. one = l * FAUX_GRAVITY * (cosU * cosU);
  552. one = one / (velocity * velocity);
  553. one = one - (float)sin(U);
  554. // one = ((l * FAUX_GRAVITY * (cosU * cosU)) / (velocity * velocity)) - (float)sin(U);
  555. angles[0] = (float)asin(one);
  556. if(_isnan(angles[0]))
  557. angles[2] = 1.0;
  558. angles[1] = (float)PI - angles[0];
  559. if(_isnan(angles[1]))
  560. angles[2] = 1.0;
  561. angles[0] = RAD2DEG ( (angles[0] - U) / 2.0 );
  562. angles[1] = RAD2DEG ( (angles[1] - U) / 2.0 );
  563. }
  564. else
  565. {
  566. l = (float) sqrt(distH*distH + distV*distV);
  567. angles[2] = 0.0;
  568. one = l * FAUX_GRAVITY;
  569. one = one / (velocity * velocity);
  570. angles[0] = (float)asin(one);
  571. if(_isnan(angles[0]))
  572. angles[2] = 1.0;
  573. angles[1] = (float)PI - angles[0];
  574. if(_isnan(angles[1]))
  575. angles[2] = 1.0;
  576. angles[0] = RAD2DEG ( (angles[0]) / 2.0 );
  577. angles[1] = RAD2DEG ( (angles[1]) / 2.0 );
  578. }
  579. }
  580. // ====================
  581. // ====================
  582. int stalker_check_lz (edict_t *self, edict_t *target, vec3_t dest)
  583. {
  584. vec3_t jumpLZ;
  585. if( (gi.pointcontents (dest) & MASK_WATER) || (target->waterlevel))
  586. {
  587. // gi.dprintf ("you won't make me jump in water!\n");
  588. return false;
  589. }
  590. if( !target->groundentity )
  591. {
  592. // gi.dprintf( "I'll wait until you land..\n");
  593. return false;
  594. }
  595. // check under the player's four corners
  596. // if they're not solid, bail.
  597. jumpLZ[0] = self->enemy->mins[0];
  598. jumpLZ[1] = self->enemy->mins[1];
  599. jumpLZ[2] = self->enemy->mins[2] - 0.25;
  600. if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
  601. return false;
  602. jumpLZ[0] = self->enemy->maxs[0];
  603. jumpLZ[1] = self->enemy->mins[1];
  604. if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
  605. return false;
  606. jumpLZ[0] = self->enemy->maxs[0];
  607. jumpLZ[1] = self->enemy->maxs[1];
  608. if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
  609. return false;
  610. jumpLZ[0] = self->enemy->mins[0];
  611. jumpLZ[1] = self->enemy->maxs[1];
  612. if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
  613. return false;
  614. return true;
  615. }
  616. // ====================
  617. // ====================
  618. int stalker_do_pounce(edict_t *self, vec3_t dest)
  619. {
  620. vec3_t forward, right;
  621. vec3_t dist;
  622. vec_t length;
  623. vec3_t jumpAngles;
  624. vec3_t jumpLZ;
  625. float velocity = 400.1;
  626. trace_t trace;
  627. int preferHighJump;
  628. // don't pounce when we're on the ceiling
  629. if(STALKER_ON_CEILING(self))
  630. return false;
  631. if(!stalker_check_lz (self, self->enemy, dest))
  632. return false;
  633. VectorSubtract(dest, self->s.origin, dist);
  634. // make sure we're pointing in that direction 15deg margin of error.
  635. vectoangles2 (dist, jumpAngles);
  636. if(abs(jumpAngles[YAW] - self->s.angles[YAW]) > 45)
  637. return false; // not facing the player...
  638. self->ideal_yaw = jumpAngles[YAW];
  639. M_ChangeYaw(self);
  640. length = VectorLength(dist);
  641. if(length > 450)
  642. return false; // can't jump that far...
  643. VectorCopy(dest, jumpLZ);
  644. preferHighJump = 0;
  645. // if we're having to jump up a distance, jump a little too high to compensate.
  646. if(dist[2] >= 32.0)
  647. {
  648. preferHighJump = 1;
  649. jumpLZ[2] += 32;
  650. }
  651. trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, dest, self, MASK_MONSTERSOLID);
  652. if((trace.fraction < 1) && (trace.ent != self->enemy))
  653. {
  654. // gi.dprintf("prefer high jump angle\n");
  655. preferHighJump = 1;
  656. }
  657. // find a valid angle/velocity combination
  658. while(velocity <= 800)
  659. {
  660. calcJumpAngle(self->s.origin, jumpLZ, velocity, jumpAngles);
  661. if((!_isnan(jumpAngles[0])) || (!_isnan(jumpAngles[1])))
  662. break;
  663. velocity+=200;
  664. };
  665. if(!preferHighJump && (!_isnan(jumpAngles[0])) )
  666. {
  667. AngleVectors (self->s.angles, forward, right, NULL);
  668. VectorNormalize ( forward ) ;
  669. VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[0])), self->velocity);
  670. self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[0])) + (0.5 * sv_gravity->value * FRAMETIME);
  671. // gi.dprintf(" pouncing! %0.1f,%0.1f (%0.1f) --> %0.1f, %0.1f, %0.1f\n",
  672. // jumpAngles[0], jumpAngles[1], jumpAngles[0],
  673. // self->velocity[0], self->velocity[1], self->velocity[2]);
  674. return 1;
  675. }
  676. if(!_isnan(jumpAngles[1]))
  677. {
  678. AngleVectors (self->s.angles, forward, right, NULL);
  679. VectorNormalize ( forward ) ;
  680. VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[1])), self->velocity);
  681. self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[1])) + (0.5 * sv_gravity->value * FRAMETIME);
  682. // gi.dprintf(" pouncing! %0.1f,%0.1f (%0.1f) --> %0.1f, %0.1f, %0.1f\n",
  683. // jumpAngles[0], jumpAngles[1], jumpAngles[1],
  684. // self->velocity[0], self->velocity[1], self->velocity[2]);
  685. return 1;
  686. }
  687. // gi.dprintf(" nan\n");
  688. return 0;
  689. }
  690. // ******************
  691. // DODGE
  692. // ******************
  693. //===================
  694. // stalker_jump_straightup
  695. //===================
  696. void stalker_jump_straightup (edict_t *self)
  697. {
  698. if (self->deadflag == DEAD_DEAD)
  699. return;
  700. if(STALKER_ON_CEILING(self))
  701. {
  702. if(stalker_ok_to_transition(self))
  703. {
  704. // gi.dprintf("falling off ceiling %d\n", self->health);
  705. self->gravityVector[2] = -1;
  706. self->s.angles[2] += 180.0;
  707. if(self->s.angles[2] > 360.0)
  708. self->s.angles[2] -= 360.0;
  709. self->groundentity = NULL;
  710. }
  711. }
  712. else if(self->groundentity) // make sure we're standing on SOMETHING...
  713. {
  714. self->velocity[0] += ((random() * 10) - 5);
  715. self->velocity[1] += ((random() * 10) - 5);
  716. self->velocity[2] += -400 * self->gravityVector[2];
  717. if(stalker_ok_to_transition(self))
  718. {
  719. // gi.dprintf("falling TO ceiling %d\n", self->health);
  720. self->gravityVector[2] = 1;
  721. self->s.angles[2] = 180.0;
  722. self->groundentity = NULL;
  723. }
  724. }
  725. }
  726. mframe_t stalker_frames_jump_straightup [] =
  727. {
  728. ai_move, 1, stalker_jump_straightup,
  729. ai_move, 1, stalker_jump_wait_land,
  730. ai_move, -1, NULL,
  731. ai_move, -1, NULL
  732. };
  733. mmove_t stalker_move_jump_straightup = {FRAME_jump04, FRAME_jump07, stalker_frames_jump_straightup, stalker_run};
  734. //===================
  735. // stalker_dodge_jump - abstraction so pain function can trigger a dodge jump too without
  736. // faking the inputs to stalker_dodge
  737. //===================
  738. void stalker_dodge_jump (edict_t *self)
  739. {
  740. self->monsterinfo.currentmove = &stalker_move_jump_straightup;
  741. }
  742. mframe_t stalker_frames_dodge_run [] =
  743. {
  744. ai_run, 13, NULL,
  745. ai_run, 17, NULL,
  746. ai_run, 21, NULL,
  747. ai_run, 18, monster_done_dodge
  748. };
  749. mmove_t stalker_move_dodge_run = {FRAME_run01, FRAME_run04, stalker_frames_dodge_run, NULL};
  750. void stalker_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
  751. {
  752. if (!self->groundentity || self->health <= 0)
  753. return;
  754. if (!self->enemy)
  755. {
  756. self->enemy = attacker;
  757. FoundTarget(self);
  758. return;
  759. }
  760. // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
  761. // seeing numbers like 13 and 14)
  762. if ((eta < 0.1) || (eta > 5))
  763. return;
  764. // this will override the foundtarget call of stalker_run
  765. stalker_dodge_jump(self);
  766. }
  767. // ******************
  768. // Jump onto / off of things
  769. // ******************
  770. //===================
  771. //===================
  772. void stalker_jump_down (edict_t *self)
  773. {
  774. vec3_t forward,up;
  775. monster_jump_start (self);
  776. AngleVectors (self->s.angles, forward, NULL, up);
  777. VectorMA(self->velocity, 100, forward, self->velocity);
  778. VectorMA(self->velocity, 300, up, self->velocity);
  779. }
  780. //===================
  781. //===================
  782. void stalker_jump_up (edict_t *self)
  783. {
  784. vec3_t forward,up;
  785. monster_jump_start (self);
  786. AngleVectors (self->s.angles, forward, NULL, up);
  787. VectorMA(self->velocity, 200, forward, self->velocity);
  788. VectorMA(self->velocity, 450, up, self->velocity);
  789. }
  790. //===================
  791. //===================
  792. void stalker_jump_wait_land (edict_t *self)
  793. {
  794. if ((random() < (0.3 + (0.1*(float)(skill->value)))) && (level.time >= self->monsterinfo.attack_finished))
  795. {
  796. self->monsterinfo.attack_finished = level.time + 0.3;
  797. stalker_shoot_attack(self);
  798. }
  799. if(self->groundentity == NULL)
  800. {
  801. self->gravity = 1.3;
  802. self->monsterinfo.nextframe = self->s.frame;
  803. if(monster_jump_finished (self))
  804. {
  805. self->gravity = 1;
  806. self->monsterinfo.nextframe = self->s.frame + 1;
  807. }
  808. }
  809. else
  810. {
  811. self->gravity = 1;
  812. self->monsterinfo.nextframe = self->s.frame + 1;
  813. }
  814. }
  815. mframe_t stalker_frames_jump_up [] =
  816. {
  817. ai_move, -8, NULL,
  818. ai_move, -8, NULL,
  819. ai_move, -8, NULL,
  820. ai_move, -8, NULL,
  821. ai_move, 0, stalker_jump_up,
  822. ai_move, 0, stalker_jump_wait_land,
  823. ai_move, 0, NULL
  824. };
  825. mmove_t stalker_move_jump_up = { FRAME_jump01, FRAME_jump07, stalker_frames_jump_up, stalker_run };
  826. mframe_t stalker_frames_jump_down [] =
  827. {
  828. ai_move, 0, NULL,
  829. ai_move, 0, NULL,
  830. ai_move, 0, NULL,
  831. ai_move, 0, NULL,
  832. ai_move, 0, stalker_jump_down,
  833. ai_move, 0, stalker_jump_wait_land,
  834. ai_move, 0, NULL
  835. };
  836. mmove_t stalker_move_jump_down = { FRAME_jump01, FRAME_jump07, stalker_frames_jump_down, stalker_run };
  837. //============
  838. // stalker_jump - this is only used for jumping onto or off of things. for dodge jumping,
  839. // use stalker_dodge_jump
  840. //============
  841. void stalker_jump (edict_t *self)
  842. {
  843. if(!self->enemy)
  844. return;
  845. if(self->enemy->s.origin[2] >= self->s.origin[2])
  846. {
  847. // gi.dprintf("stalker_jump_up\n");
  848. self->monsterinfo.currentmove = &stalker_move_jump_up;
  849. }
  850. else
  851. {
  852. // gi.dprintf("stalker_jump_down\n");
  853. self->monsterinfo.currentmove = &stalker_move_jump_down;
  854. }
  855. }
  856. // ******************
  857. // Blocked
  858. // ******************
  859. qboolean stalker_blocked (edict_t *self, float dist)
  860. {
  861. qboolean onCeiling;
  862. // gi.dprintf("stalker_blocked\n");
  863. if(!has_valid_enemy(self))
  864. return false;
  865. onCeiling = false;
  866. if(self->gravityVector[2] > 0)
  867. onCeiling = true;
  868. if(!onCeiling)
  869. {
  870. if(blocked_checkshot(self, 0.25 + (0.05 * skill->value) ))
  871. {
  872. // gi.dprintf("blocked: shooting\n");
  873. return true;
  874. }
  875. if(visible (self, self->enemy))
  876. {
  877. // gi.dprintf("blocked: jumping at player!\n");
  878. stalker_do_pounce(self, self->enemy->s.origin);
  879. return true;
  880. }
  881. if(blocked_checkjump (self, dist, 256, 68))
  882. {
  883. // gi.dprintf("blocked: jumping up/down\n");
  884. stalker_jump (self);
  885. return true;
  886. }
  887. if(blocked_checkplat (self, dist))
  888. return true;
  889. }
  890. else
  891. {
  892. if(blocked_checkshot(self, 0.25 + (0.05 * skill->value) ))
  893. {
  894. // gi.dprintf("blocked: shooting\n");
  895. return true;
  896. }
  897. else if(stalker_ok_to_transition(self))
  898. {
  899. self->gravityVector[2] = -1;
  900. self->s.angles[2] += 180.0;
  901. if(self->s.angles[2] > 360.0)
  902. self->s.angles[2] -= 360.0;
  903. self->groundentity = NULL;
  904. // gi.dprintf("falling off ceiling\n");
  905. return true;
  906. }
  907. // else
  908. // gi.dprintf("Not OK to fall!\n");
  909. }
  910. return false;
  911. }
  912. // ******************
  913. // Death
  914. // ******************
  915. void stalker_dead (edict_t *self)
  916. {
  917. VectorSet (self->mins, -28, -28, -18);
  918. VectorSet (self->maxs, 28, 28, -4);
  919. self->movetype = MOVETYPE_TOSS;
  920. self->svflags |= SVF_DEADMONSTER;
  921. self->nextthink = 0;
  922. gi.linkentity (self);
  923. // drawbbox(self);
  924. }
  925. mframe_t stalker_frames_death [] =
  926. {
  927. ai_move, 0, NULL,
  928. ai_move, -5, NULL,
  929. ai_move, -10, NULL,
  930. ai_move, -20, NULL,
  931. ai_move, -10, NULL,
  932. ai_move, -10, NULL,
  933. ai_move, -5, NULL,
  934. ai_move, -5, NULL,
  935. ai_move, 0, NULL
  936. };
  937. mmove_t stalker_move_death = {FRAME_death01, FRAME_death09, stalker_frames_death, stalker_dead};
  938. void stalker_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  939. {
  940. int n;
  941. // gi.dprintf("stalker_die: %d\n", self->health);
  942. // dude bit it, make him fall!
  943. self->movetype = MOVETYPE_TOSS;
  944. self->s.angles[2] = 0;
  945. VectorSet(self->gravityVector, 0, 0, -1);
  946. // check for gib
  947. if (self->health <= self->gib_health)
  948. {
  949. gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  950. for (n= 0; n < 2; n++)
  951. ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
  952. for (n= 0; n < 4; n++)
  953. ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  954. ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
  955. self->deadflag = DEAD_DEAD;
  956. return;
  957. }
  958. if (self->deadflag == DEAD_DEAD)
  959. return;
  960. // regular death
  961. gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
  962. self->deadflag = DEAD_DEAD;
  963. self->takedamage = DAMAGE_YES;
  964. self->monsterinfo.currentmove = &stalker_move_death;
  965. }
  966. // ******************
  967. // SPAWN
  968. // ******************
  969. /*QUAKED monster_stalker (1 .5 0) (-28 -28 -18) (28 28 18) Ambush Trigger_Spawn Sight OnRoof
  970. Spider Monster
  971. ONROOF - Monster starts sticking to the roof.
  972. */
  973. void SP_monster_stalker (edict_t *self)
  974. {
  975. if (deathmatch->value)
  976. {
  977. G_FreeEdict (self);
  978. return;
  979. }
  980. sound_pain = gi.soundindex ("stalker/pain.wav");
  981. sound_die = gi.soundindex ("stalker/death.wav");
  982. sound_sight = gi.soundindex("stalker/sight.wav");
  983. sound_punch_hit1 = gi.soundindex ("stalker/melee1.wav");
  984. sound_punch_hit2 = gi.soundindex ("stalker/melee2.wav");
  985. sound_idle = gi.soundindex ("stalker/idle.wav");
  986. // PMM - precache bolt2
  987. gi.modelindex ("models/proj/laser2/tris.md2");
  988. self->s.modelindex = gi.modelindex ("models/monsters/stalker/tris.md2");
  989. VectorSet (self->mins, -28, -28, -18);
  990. VectorSet (self->maxs, 28, 28, 18);
  991. self->movetype = MOVETYPE_STEP;
  992. self->solid = SOLID_BBOX;
  993. self->health = 250;
  994. self->gib_health = -50; // FIXME
  995. self->mass = 250;
  996. self->pain = stalker_pain;
  997. self->die = stalker_die;
  998. self->monsterinfo.stand = stalker_stand;
  999. self->monsterinfo.walk = stalker_walk;
  1000. self->monsterinfo.run = stalker_run;
  1001. self->monsterinfo.attack = stalker_attack_ranged;
  1002. self->monsterinfo.sight = stalker_sight;
  1003. self->monsterinfo.idle = stalker_idle;
  1004. self->monsterinfo.dodge = stalker_dodge;
  1005. self->monsterinfo.blocked = stalker_blocked;
  1006. self->monsterinfo.melee = stalker_attack_melee;
  1007. gi.linkentity (self);
  1008. self->monsterinfo.currentmove = &stalker_move_stand;
  1009. self->monsterinfo.scale = MODEL_SCALE;
  1010. self->monsterinfo.aiflags |= AI_WALK_WALLS;
  1011. if(self->spawnflags & 8)
  1012. {
  1013. self->s.angles[2] = 180;
  1014. self->gravityVector[2] = 1;
  1015. }
  1016. walkmonster_start (self);
  1017. }