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