|
|
1.1 root 1: /*
2: ==============================================================================
3:
4: GUNNER
5:
6: ==============================================================================
7: */
8:
9: #include "g_local.h"
10: #include "m_gunner.h"
11:
12:
13: static int sound_pain;
14: static int sound_pain2;
15: static int sound_death;
16: static int sound_idle;
17: static int sound_open;
18: static int sound_search;
19: static int sound_sight;
20:
21: void gunner_idlesound (edict_t *self)
22: {
23: gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
24: }
25:
26: void gunner_sight (edict_t *self, edict_t *other)
27: {
28: gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
29: }
30:
31: void gunner_search (edict_t *self)
32: {
33: gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
34: }
35:
36:
37: qboolean visible (edict_t *self, edict_t *other);
38: void GunnerGrenade (edict_t *self);
39: void GunnerFire (edict_t *self);
40: void gunner_fire_chain(edict_t *self);
41: void gunner_refire_chain(edict_t *self);
42:
43:
44: void gunner_stand (edict_t *self);
45:
46: mframe_t gunner_frames_fidget [] =
47: {
48: ai_stand, 0, NULL,
49: ai_stand, 0, NULL,
50: ai_stand, 0, NULL,
51: ai_stand, 0, NULL,
52: ai_stand, 0, NULL,
53: ai_stand, 0, NULL,
54: ai_stand, 0, NULL,
55: ai_stand, 0, gunner_idlesound,
56: ai_stand, 0, NULL,
57:
58: ai_stand, 0, NULL,
59: ai_stand, 0, NULL,
60: ai_stand, 0, NULL,
61: ai_stand, 0, NULL,
62: ai_stand, 0, NULL,
63: ai_stand, 0, NULL,
64: ai_stand, 0, NULL,
65: ai_stand, 0, NULL,
66: ai_stand, 0, NULL,
67: ai_stand, 0, NULL,
68:
69: ai_stand, 0, NULL,
70: ai_stand, 0, NULL,
71: ai_stand, 0, NULL,
72: ai_stand, 0, NULL,
73: ai_stand, 0, NULL,
74: ai_stand, 0, NULL,
75: ai_stand, 0, NULL,
76: ai_stand, 0, NULL,
77: ai_stand, 0, NULL,
78: ai_stand, 0, NULL,
79:
80: ai_stand, 0, NULL,
81: ai_stand, 0, NULL,
82: ai_stand, 0, NULL,
83: ai_stand, 0, NULL,
84: ai_stand, 0, NULL,
85: ai_stand, 0, NULL,
86: ai_stand, 0, NULL,
87: ai_stand, 0, NULL,
88: ai_stand, 0, NULL,
89: ai_stand, 0, NULL,
90:
91: ai_stand, 0, NULL,
92: ai_stand, 0, NULL,
93: ai_stand, 0, NULL,
94: ai_stand, 0, NULL,
95: ai_stand, 0, NULL,
96: ai_stand, 0, NULL,
97: ai_stand, 0, NULL,
98: ai_stand, 0, NULL,
99: ai_stand, 0, NULL,
100: ai_stand, 0, NULL
101: };
102: mmove_t gunner_move_fidget = {FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand};
103:
104: void gunner_fidget (edict_t *self)
105: {
106: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
107: return;
108: if (random() <= 0.05)
109: self->monsterinfo.currentmove = &gunner_move_fidget;
110: }
111:
112: mframe_t gunner_frames_stand [] =
113: {
114: ai_stand, 0, NULL,
115: ai_stand, 0, NULL,
116: ai_stand, 0, NULL,
117: ai_stand, 0, NULL,
118: ai_stand, 0, NULL,
119: ai_stand, 0, NULL,
120: ai_stand, 0, NULL,
121: ai_stand, 0, NULL,
122: ai_stand, 0, NULL,
123: ai_stand, 0, gunner_fidget,
124:
125: ai_stand, 0, NULL,
126: ai_stand, 0, NULL,
127: ai_stand, 0, NULL,
128: ai_stand, 0, NULL,
129: ai_stand, 0, NULL,
130: ai_stand, 0, NULL,
131: ai_stand, 0, NULL,
132: ai_stand, 0, NULL,
133: ai_stand, 0, NULL,
134: ai_stand, 0, gunner_fidget,
135:
136: ai_stand, 0, NULL,
137: ai_stand, 0, NULL,
138: ai_stand, 0, NULL,
139: ai_stand, 0, NULL,
140: ai_stand, 0, NULL,
141: ai_stand, 0, NULL,
142: ai_stand, 0, NULL,
143: ai_stand, 0, NULL,
144: ai_stand, 0, NULL,
145: ai_stand, 0, gunner_fidget
146: };
147: mmove_t gunner_move_stand = {FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL};
148:
149: void gunner_stand (edict_t *self)
150: {
151: self->monsterinfo.currentmove = &gunner_move_stand;
152: }
153:
154:
155: mframe_t gunner_frames_walk [] =
156: {
157: ai_walk, 0, NULL,
158: ai_walk, 3, NULL,
159: ai_walk, 4, NULL,
160: ai_walk, 5, NULL,
161: ai_walk, 7, NULL,
162: ai_walk, 2, NULL,
163: ai_walk, 6, NULL,
164: ai_walk, 4, NULL,
165: ai_walk, 2, NULL,
166: ai_walk, 7, NULL,
167: ai_walk, 5, NULL,
168: ai_walk, 7, NULL,
169: ai_walk, 4, NULL
170: };
171: mmove_t gunner_move_walk = {FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL};
172:
173: void gunner_walk (edict_t *self)
174: {
175: self->monsterinfo.currentmove = &gunner_move_walk;
176: }
177:
178: mframe_t gunner_frames_run [] =
179: {
180: ai_run, 26, NULL,
181: ai_run, 9, NULL,
182: ai_run, 9, NULL,
183: ai_run, 9, monster_done_dodge,
184: ai_run, 15, NULL,
185: ai_run, 10, NULL,
186: ai_run, 13, NULL,
187: ai_run, 6, NULL
188: };
189:
190: mmove_t gunner_move_run = {FRAME_run01, FRAME_run08, gunner_frames_run, NULL};
191:
192: void gunner_run (edict_t *self)
193: {
194: monster_done_dodge(self);
195: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
196: self->monsterinfo.currentmove = &gunner_move_stand;
197: else
198: self->monsterinfo.currentmove = &gunner_move_run;
199: }
200:
201: mframe_t gunner_frames_runandshoot [] =
202: {
203: ai_run, 32, NULL,
204: ai_run, 15, NULL,
205: ai_run, 10, NULL,
206: ai_run, 18, NULL,
207: ai_run, 8, NULL,
208: ai_run, 20, NULL
209: };
210:
211: mmove_t gunner_move_runandshoot = {FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL};
212:
213: void gunner_runandshoot (edict_t *self)
214: {
215: self->monsterinfo.currentmove = &gunner_move_runandshoot;
216: }
217:
218: mframe_t gunner_frames_pain3 [] =
219: {
220: ai_move, -3, NULL,
221: ai_move, 1, NULL,
222: ai_move, 1, NULL,
223: ai_move, 0, NULL,
224: ai_move, 1, NULL
225: };
226: mmove_t gunner_move_pain3 = {FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run};
227:
228: mframe_t gunner_frames_pain2 [] =
229: {
230: ai_move, -2, NULL,
231: ai_move, 11, NULL,
232: ai_move, 6, NULL,
233: ai_move, 2, NULL,
234: ai_move, -1, NULL,
235: ai_move, -7, NULL,
236: ai_move, -2, NULL,
237: ai_move, -7, NULL
238: };
239: mmove_t gunner_move_pain2 = {FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run};
240:
241: mframe_t gunner_frames_pain1 [] =
242: {
243: ai_move, 2, NULL,
244: ai_move, 0, NULL,
245: ai_move, -5, NULL,
246: ai_move, 3, NULL,
247: ai_move, -1, NULL,
248: ai_move, 0, NULL,
249: ai_move, 0, NULL,
250: ai_move, 0, NULL,
251: ai_move, 0, NULL,
252: ai_move, 1, NULL,
253: ai_move, 1, NULL,
254: ai_move, 2, NULL,
255: ai_move, 1, NULL,
256: ai_move, 0, NULL,
257: ai_move, -2, NULL,
258: ai_move, -2, NULL,
259: ai_move, 0, NULL,
260: ai_move, 0, NULL
261: };
262: mmove_t gunner_move_pain1 = {FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run};
263:
264: void gunner_pain (edict_t *self, edict_t *other, float kick, int damage)
265: {
266: if (self->health < (self->max_health / 2))
267: self->s.skinnum = 1;
268:
269: monster_done_dodge (self);
270:
271: if (!self->groundentity)
272: {
273: if ((g_showlogic) && (g_showlogic->value))
274: gi.dprintf ("gunner: pain avoided due to no ground\n");
275: return;
276: }
277:
278: if (level.time < self->pain_debounce_time)
279: return;
280:
281: self->pain_debounce_time = level.time + 3;
282:
283: if (rand()&1)
284: gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
285: else
286: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
287:
288: if (skill->value == 3)
289: return; // no pain anims in nightmare
290:
291: if (damage <= 10)
292: self->monsterinfo.currentmove = &gunner_move_pain3;
293: else if (damage <= 25)
294: self->monsterinfo.currentmove = &gunner_move_pain2;
295: else
296: self->monsterinfo.currentmove = &gunner_move_pain1;
297:
298: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
299:
300: // PMM - clear duck flag
301: if (self->monsterinfo.aiflags & AI_DUCKED)
302: monster_duck_up(self);
303: }
304:
305: void gunner_dead (edict_t *self)
306: {
307: VectorSet (self->mins, -16, -16, -24);
308: VectorSet (self->maxs, 16, 16, -8);
309: self->movetype = MOVETYPE_TOSS;
310: self->svflags |= SVF_DEADMONSTER;
311: self->nextthink = 0;
312: gi.linkentity (self);
313: }
314:
315: mframe_t gunner_frames_death [] =
316: {
317: ai_move, 0, NULL,
318: ai_move, 0, NULL,
319: ai_move, 0, NULL,
320: ai_move, -7, NULL,
321: ai_move, -3, NULL,
322: ai_move, -5, NULL,
323: ai_move, 8, NULL,
324: ai_move, 6, NULL,
325: ai_move, 0, NULL,
326: ai_move, 0, NULL,
327: ai_move, 0, NULL
328: };
329: mmove_t gunner_move_death = {FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead};
330:
331: void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
332: {
333: int n;
334:
335: // check for gib
336: if (self->health <= self->gib_health)
337: {
338: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
339: for (n= 0; n < 2; n++)
340: ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
341: for (n= 0; n < 4; n++)
342: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
343: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
344: self->deadflag = DEAD_DEAD;
345: return;
346: }
347:
348: if (self->deadflag == DEAD_DEAD)
349: return;
350:
351: // regular death
352: gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
353: self->deadflag = DEAD_DEAD;
354: self->takedamage = DAMAGE_YES;
355: self->monsterinfo.currentmove = &gunner_move_death;
356: }
357:
358: // PMM - changed to duck code for new dodge
359:
360: //
361: // this is specific to the gunner, leave it be
362: //
363: void gunner_duck_down (edict_t *self)
364: {
365: // if (self->monsterinfo.aiflags & AI_DUCKED)
366: // return;
367: self->monsterinfo.aiflags |= AI_DUCKED;
368: if (skill->value >= 2)
369: {
370: if (random() > 0.5)
371: GunnerGrenade (self);
372: }
373:
374: // self->maxs[2] -= 32;
375: self->maxs[2] = self->monsterinfo.base_height - 32;
376: self->takedamage = DAMAGE_YES;
377: if (self->monsterinfo.duck_wait_time < level.time)
378: self->monsterinfo.duck_wait_time = level.time + 1;
379: gi.linkentity (self);
380: }
381:
382: mframe_t gunner_frames_duck [] =
383: {
384: ai_move, 1, gunner_duck_down,
385: ai_move, 1, NULL,
386: ai_move, 1, monster_duck_hold,
387: ai_move, 0, NULL,
388: ai_move, -1, NULL,
389: ai_move, -1, NULL,
390: ai_move, 0, monster_duck_up,
391: ai_move, -1, NULL
392: };
393: mmove_t gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run};
394:
395: // PMM - gunner dodge moved below so I know about attack sequences
396:
397: void gunner_opengun (edict_t *self)
398: {
399: gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
400: }
401:
402: void GunnerFire (edict_t *self)
403: {
404: vec3_t start;
405: vec3_t forward, right;
406: vec3_t target;
407: vec3_t aim;
408: int flash_number;
409:
410: if(!self->enemy || !self->enemy->inuse) //PGM
411: return; //PGM
412:
413: flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
414:
415: AngleVectors (self->s.angles, forward, right, NULL);
416: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
417:
418: // project enemy back a bit and target there
419: VectorCopy (self->enemy->s.origin, target);
420: VectorMA (target, -0.2, self->enemy->velocity, target);
421: target[2] += self->enemy->viewheight;
422:
423: VectorSubtract (target, start, aim);
424: VectorNormalize (aim);
425: monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
426: }
427:
428: qboolean gunner_grenade_check(edict_t *self)
429: {
430: vec3_t start;
431: vec3_t forward, right;
432: trace_t tr;
433: vec3_t target, dir;
434:
435: if(!self->enemy)
436: return false;
437:
438: // if the player is above my head, use machinegun.
439:
440: // check for flag telling us that we're blindfiring
441: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
442: {
443: if (self->s.origin[2]+self->viewheight < self->monsterinfo.blind_fire_target[2])
444: {
445: if(g_showlogic && g_showlogic->value)
446: gi.dprintf("blind_fire_target is above my head, using machinegun\n");
447: return false;
448: }
449: }
450: else if(self->absmax[2] <= self->enemy->absmin[2])
451: {
452: if(g_showlogic && g_showlogic->value)
453: gi.dprintf("player is above my head, using machinegun\n");
454: return false;
455: }
456:
457: // check to see that we can trace to the player before we start
458: // tossing grenades around.
459: AngleVectors (self->s.angles, forward, right, NULL);
460: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_GUNNER_GRENADE_1], forward, right, start);
461:
462: // pmm - check for blindfire flag
463: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
464: VectorCopy (self->monsterinfo.blind_fire_target, target);
465: else
466: VectorCopy (self->enemy->s.origin, target);
467:
468: // see if we're too close
469: VectorSubtract (self->s.origin, target, dir);
470:
471: if (VectorLength(dir) < 100)
472: return false;
473:
474: tr = gi.trace(start, vec3_origin, vec3_origin, target, self, MASK_SHOT);
475: if(tr.ent == self->enemy || tr.fraction == 1)
476: return true;
477:
478: if(g_showlogic && g_showlogic->value)
479: gi.dprintf("can't trace to target, using machinegun\n");
480: return false;
481: }
482:
483: void GunnerGrenade (edict_t *self)
484: {
485: vec3_t start;
486: vec3_t forward, right, up;
487: vec3_t aim;
488: int flash_number;
489: float spread;
490: float pitch;
491: // PMM
492: vec3_t target;
493: qboolean blindfire;
494:
495: if(!self->enemy || !self->enemy->inuse) //PGM
496: return; //PGM
497:
498: // pmm
499: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
500: blindfire = true;
501:
502: if (self->s.frame == FRAME_attak105)
503: {
504: spread = .02;
505: flash_number = MZ2_GUNNER_GRENADE_1;
506: }
507: else if (self->s.frame == FRAME_attak108)
508: {
509: spread = .05;
510: flash_number = MZ2_GUNNER_GRENADE_2;
511: }
512: else if (self->s.frame == FRAME_attak111)
513: {
514: spread = .08;
515: flash_number = MZ2_GUNNER_GRENADE_3;
516: }
517: else // (self->s.frame == FRAME_attak114)
518: {
519: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
520: spread = .11;
521: flash_number = MZ2_GUNNER_GRENADE_4;
522: }
523:
524: // pmm
525: // if we're shooting blind and we still can't see our enemy
526: if ((blindfire) && (!visible(self, self->enemy)))
527: {
528: // and we have a valid blind_fire_target
529: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
530: return;
531:
532: // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target));
533: // gi.dprintf ("GunnerGrenade: ideal yaw is %f\n", self->ideal_yaw);
534: VectorCopy (self->monsterinfo.blind_fire_target, target);
535: }
536: else
537: VectorCopy (self->s.origin, target);
538: // pmm
539:
540: AngleVectors (self->s.angles, forward, right, up); //PGM
541: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
542:
543: //PGM
544: if(self->enemy)
545: {
546: float dist;
547:
548: // VectorSubtract(self->enemy->s.origin, self->s.origin, aim);
549: VectorSubtract(target, self->s.origin, aim);
550: dist = VectorLength(aim);
551:
552: // aim up if they're on the same level as me and far away.
553: if((dist > 512) && (aim[2] < 64) && (aim[2] > -64))
554: {
555: aim[2] += (dist - 512);
556: }
557:
558: VectorNormalize (aim);
559: pitch = aim[2];
560: if(pitch > 0.4)
561: {
562: pitch = 0.4;
563: }
564: else if(pitch < -0.5)
565: pitch = -0.5;
566: }
567: //PGM
568:
569: //FIXME : do a spread -225 -75 75 225 degrees around forward
570: // VectorCopy (forward, aim);
571: VectorMA (forward, spread, right, aim);
572: VectorMA (aim, pitch, up, aim);
573:
574: monster_fire_grenade (self, start, aim, 50, 600, flash_number);
575: }
576:
577: mframe_t gunner_frames_attack_chain [] =
578: {
579: /*
580: ai_charge, 0, NULL,
581: ai_charge, 0, NULL,
582: ai_charge, 0, NULL,
583: ai_charge, 0, NULL,
584: ai_charge, 0, NULL,
585: ai_charge, 0, NULL,
586: ai_charge, 0, NULL,
587: ai_charge, 0, NULL,
588: */
589: ai_charge, 0, gunner_opengun,
590: ai_charge, 0, NULL,
591: ai_charge, 0, NULL,
592: ai_charge, 0, NULL,
593: ai_charge, 0, NULL,
594: ai_charge, 0, NULL,
595: ai_charge, 0, NULL
596: };
597: mmove_t gunner_move_attack_chain = {FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain};
598:
599: mframe_t gunner_frames_fire_chain [] =
600: {
601: ai_charge, 0, GunnerFire,
602: ai_charge, 0, GunnerFire,
603: ai_charge, 0, GunnerFire,
604: ai_charge, 0, GunnerFire,
605: ai_charge, 0, GunnerFire,
606: ai_charge, 0, GunnerFire,
607: ai_charge, 0, GunnerFire,
608: ai_charge, 0, GunnerFire
609: };
610: mmove_t gunner_move_fire_chain = {FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain};
611:
612: mframe_t gunner_frames_endfire_chain [] =
613: {
614: ai_charge, 0, NULL,
615: ai_charge, 0, NULL,
616: ai_charge, 0, NULL,
617: ai_charge, 0, NULL,
618: ai_charge, 0, NULL,
619: ai_charge, 0, NULL,
620: ai_charge, 0, NULL
621: };
622: mmove_t gunner_move_endfire_chain = {FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run};
623:
624: void gunner_blind_check (edict_t *self)
625: {
626: vec3_t aim;
627:
628: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
629: {
630: VectorSubtract(self->monsterinfo.blind_fire_target, self->s.origin, aim);
631: self->ideal_yaw = vectoyaw(aim);
632:
633: // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target));
634: // gi.dprintf ("gunner_attack: ideal yaw is %f\n", self->ideal_yaw);
635: }
636: }
637:
638: mframe_t gunner_frames_attack_grenade [] =
639: {
640: ai_charge, 0, gunner_blind_check,
641: ai_charge, 0, NULL,
642: ai_charge, 0, NULL,
643: ai_charge, 0, NULL,
644: ai_charge, 0, GunnerGrenade,
645: ai_charge, 0, NULL,
646: ai_charge, 0, NULL,
647: ai_charge, 0, GunnerGrenade,
648: ai_charge, 0, NULL,
649: ai_charge, 0, NULL,
650: ai_charge, 0, GunnerGrenade,
651: ai_charge, 0, NULL,
652: ai_charge, 0, NULL,
653: ai_charge, 0, GunnerGrenade,
654: ai_charge, 0, NULL,
655: ai_charge, 0, NULL,
656: ai_charge, 0, NULL,
657: ai_charge, 0, NULL,
658: ai_charge, 0, NULL,
659: ai_charge, 0, NULL,
660: ai_charge, 0, NULL
661: };
662: mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run};
663:
664: void gunner_attack(edict_t *self)
665: {
666: float chance, r;
667:
668: monster_done_dodge(self);
669:
670: // PMM
671: if (self->monsterinfo.attack_state == AS_BLIND)
672: {
673: // setup shot probabilities
674: if (self->monsterinfo.blind_fire_delay < 1.0)
675: chance = 1.0;
676: else if (self->monsterinfo.blind_fire_delay < 7.5)
677: chance = 0.4;
678: else
679: chance = 0.1;
680:
681: r = random();
682:
683: // gi.dprintf ("chance = %2.2f, roll = %2.2f\n", chance, r);
684:
685: // minimum of 2 seconds, plus 0-3, after the shots are done
686: self->monsterinfo.blind_fire_delay += 2.1 + 2.0 + random()*3.0;
687:
688: // don't shoot at the origin
689: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
690: return;
691:
692: // don't shoot if the dice say not to
693: if (r > chance)
694: {
695: // if ((g_showlogic) && (g_showlogic->value))
696: // gi.dprintf ("blindfire - NO SHOT\n");
697: return;
698: }
699:
700: // turn on manual steering to signal both manual steering and blindfire
701: self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
702: if (gunner_grenade_check(self))
703: {
704: self->monsterinfo.currentmove = &gunner_move_attack_grenade;
705: self->monsterinfo.attack_finished = level.time + 2*random();
706: return;
707: }
708: // pmm - should this be active?
709: // else
710: // self->monsterinfo.currentmove = &gunner_move_attack_chain;
711: // if ((g_showlogic) && (g_showlogic->value))
712: // gi.dprintf ("blind grenade check failed, doing nothing\n");
713:
714: // turn off blindfire flag
715: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
716: return;
717: }
718: // pmm
719:
720: // PGM - gunner needs to use his chaingun if he's being attacked by a tesla.
721: if ((range (self, self->enemy) == RANGE_MELEE) || self->bad_area)
722: {
723: self->monsterinfo.currentmove = &gunner_move_attack_chain;
724: }
725: else
726: {
727: if (random() <= 0.5 && gunner_grenade_check(self))
728: self->monsterinfo.currentmove = &gunner_move_attack_grenade;
729: else
730: self->monsterinfo.currentmove = &gunner_move_attack_chain;
731: }
732: }
733:
734: void gunner_fire_chain(edict_t *self)
735: {
736: self->monsterinfo.currentmove = &gunner_move_fire_chain;
737: }
738:
739: void gunner_refire_chain(edict_t *self)
740: {
741: if (self->enemy->health > 0)
742: if ( visible (self, self->enemy) )
743: if (random() <= 0.5)
744: {
745: self->monsterinfo.currentmove = &gunner_move_fire_chain;
746: return;
747: }
748: self->monsterinfo.currentmove = &gunner_move_endfire_chain;
749: }
750: /*
751: void gunner_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
752: {
753: // original quake2 dodge code
754:
755: if (random() > 0.25)
756: return;
757:
758: if (!self->enemy)
759: self->enemy = attacker;
760:
761: self->monsterinfo.currentmove = &gunner_move_duck;
762:
763: //===========
764: //PMM - rogue rewrite of gunner dodge code.
765: float r;
766: float height;
767: int shooting = 0;
768:
769: if (!self->enemy)
770: {
771: self->enemy = attacker;
772: FoundTarget (self);
773: }
774:
775: // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
776: // seeing numbers like 13 and 14)
777: if ((eta < 0.1) || (eta > 5))
778: return;
779:
780: r = random();
781: if (r > (0.25*((skill->value)+1)))
782: return;
783:
784: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
785: (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
786: (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
787: )
788: {
789: shooting = 1;
790: }
791: if (self->monsterinfo.aiflags & AI_DODGING)
792: {
793: height = self->absmax[2];
794: }
795: else
796: {
797: height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1
798: }
799:
800: // check to see if it makes sense to duck
801: if (tr->endpos[2] <= height)
802: {
803: vec3_t right, diff;
804: if (shooting)
805: {
806: self->monsterinfo.attack_state = AS_SLIDING;
807: return;
808: }
809: AngleVectors (self->s.angles, NULL, right, NULL);
810: VectorSubtract (tr->endpos, self->s.origin, diff);
811: if (DotProduct (right, diff) < 0)
812: {
813: self->monsterinfo.lefty = 1;
814: }
815: // if it doesn't sense to duck, try to strafe away
816: monster_done_dodge (self);
817: self->monsterinfo.currentmove = &gunner_move_run;
818: self->monsterinfo.attack_state = AS_SLIDING;
819: return;
820: }
821:
822: if (skill->value == 0)
823: {
824: self->monsterinfo.currentmove = &gunner_move_duck;
825: // PMM - stupid dodge
826: self->monsterinfo.duck_wait_time = level.time + eta + 1;
827: self->monsterinfo.aiflags |= AI_DODGING;
828: return;
829: }
830:
831: if (!shooting)
832: {
833: self->monsterinfo.currentmove = &gunner_move_duck;
834: self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
835: self->monsterinfo.aiflags |= AI_DODGING;
836: }
837: return;
838: //PMM
839: //===========
840: }
841: */
842: //===========
843: //PGM
844: void gunner_jump_now (edict_t *self)
845: {
846: vec3_t forward,up;
847:
848: AngleVectors (self->s.angles, forward, NULL, up);
849: VectorMA(self->velocity, 100, forward, self->velocity);
850: VectorMA(self->velocity, 300, up, self->velocity);
851: }
852:
853: void gunner_jump2_now (edict_t *self)
854: {
855: vec3_t forward,up;
856:
857: AngleVectors (self->s.angles, forward, NULL, up);
858: VectorMA(self->velocity, 150, forward, self->velocity);
859: VectorMA(self->velocity, 400, up, self->velocity);
860: }
861:
862: void gunner_jump_wait_land (edict_t *self)
863: {
864: if(self->groundentity == NULL)
865: self->monsterinfo.nextframe = self->s.frame;
866: else
867: self->monsterinfo.nextframe = self->s.frame + 1;
868: }
869:
870: mframe_t gunner_frames_jump [] =
871: {
872: ai_move, 0, NULL,
873: ai_move, 0, NULL,
874: ai_move, 0, NULL,
875: ai_move, 0, gunner_jump_now,
876: ai_move, 0, NULL,
877: ai_move, 0, NULL,
878: ai_move, 0, gunner_jump_wait_land,
879: ai_move, 0, NULL,
880: ai_move, 0, NULL,
881: ai_move, 0, NULL
882: };
883: mmove_t gunner_move_jump = { FRAME_jump01, FRAME_jump10, gunner_frames_jump, gunner_run };
884:
885: mframe_t gunner_frames_jump2 [] =
886: {
887: ai_move, -8, NULL,
888: ai_move, -4, NULL,
889: ai_move, -4, NULL,
890: ai_move, 0, gunner_jump_now,
891: ai_move, 0, NULL,
892: ai_move, 0, NULL,
893: ai_move, 0, gunner_jump_wait_land,
894: ai_move, 0, NULL,
895: ai_move, 0, NULL,
896: ai_move, 0, NULL
897: };
898: mmove_t gunner_move_jump2 = { FRAME_jump01, FRAME_jump10, gunner_frames_jump2, gunner_run };
899:
900: void gunner_jump (edict_t *self)
901: {
902: if(!self->enemy)
903: return;
904:
905: monster_done_dodge (self);
906:
907: if(self->enemy->s.origin[2] > self->s.origin[2])
908: self->monsterinfo.currentmove = &gunner_move_jump2;
909: else
910: self->monsterinfo.currentmove = &gunner_move_jump;
911: }
912:
913: //===========
914: //PGM
915: qboolean gunner_blocked (edict_t *self, float dist)
916: {
917: if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
918: return true;
919:
920: if(blocked_checkplat (self, dist))
921: return true;
922:
923: if(blocked_checkjump (self, dist, 192, 40))
924: {
925: gunner_jump(self);
926: return true;
927: }
928:
929: return false;
930: }
931: //PGM
932: //===========
933:
934: // PMM - new duck code
935: void gunner_duck (edict_t *self, float eta)
936: {
937: if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
938: (self->monsterinfo.currentmove == &gunner_move_jump))
939: {
940: return;
941: }
942:
943: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
944: (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
945: (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
946: )
947: {
948: // if we're shooting, and not on easy, don't dodge
949: if (skill->value)
950: {
951: self->monsterinfo.aiflags &= ~AI_DUCKED;
952: return;
953: }
954: }
955:
956: if (skill->value == 0)
957: // PMM - stupid dodge
958: self->monsterinfo.duck_wait_time = level.time + eta + 1;
959: else
960: self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
961:
962: // has to be done immediately otherwise he can get stuck
963: gunner_duck_down(self);
964:
965: self->monsterinfo.nextframe = FRAME_duck01;
966: self->monsterinfo.currentmove = &gunner_move_duck;
967: return;
968: }
969:
970: void gunner_sidestep (edict_t *self)
971: {
972: if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
973: (self->monsterinfo.currentmove == &gunner_move_jump))
974: {
975: return;
976: }
977:
978: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
979: (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
980: (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
981: )
982: {
983: // if we're shooting, and not on easy, don't dodge
984: if (skill->value)
985: {
986: self->monsterinfo.aiflags &= ~AI_DODGING;
987: return;
988: }
989: }
990:
991: if (self->monsterinfo.currentmove != &gunner_move_run)
992: self->monsterinfo.currentmove = &gunner_move_run;
993: }
994:
995:
996: /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
997: */
998: void SP_monster_gunner (edict_t *self)
999: {
1000: if (deathmatch->value)
1001: {
1002: G_FreeEdict (self);
1003: return;
1004: }
1005:
1006: sound_death = gi.soundindex ("gunner/death1.wav");
1007: sound_pain = gi.soundindex ("gunner/gunpain2.wav");
1008: sound_pain2 = gi.soundindex ("gunner/gunpain1.wav");
1009: sound_idle = gi.soundindex ("gunner/gunidle1.wav");
1010: sound_open = gi.soundindex ("gunner/gunatck1.wav");
1011: sound_search = gi.soundindex ("gunner/gunsrch1.wav");
1012: sound_sight = gi.soundindex ("gunner/sight1.wav");
1013:
1014: gi.soundindex ("gunner/gunatck2.wav");
1015: gi.soundindex ("gunner/gunatck3.wav");
1016:
1017: self->movetype = MOVETYPE_STEP;
1018: self->solid = SOLID_BBOX;
1019: self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2");
1020: VectorSet (self->mins, -16, -16, -24);
1021: VectorSet (self->maxs, 16, 16, 32);
1022:
1023: self->health = 175;
1024: self->gib_health = -70;
1025: self->mass = 200;
1026:
1027: self->pain = gunner_pain;
1028: self->die = gunner_die;
1029:
1030: self->monsterinfo.stand = gunner_stand;
1031: self->monsterinfo.walk = gunner_walk;
1032: self->monsterinfo.run = gunner_run;
1033: // pmm
1034: self->monsterinfo.dodge = M_MonsterDodge;
1035: self->monsterinfo.duck = gunner_duck;
1036: self->monsterinfo.unduck = monster_duck_up;
1037: self->monsterinfo.sidestep = gunner_sidestep;
1038: // self->monsterinfo.dodge = gunner_dodge;
1039: // pmm
1040: self->monsterinfo.attack = gunner_attack;
1041: self->monsterinfo.melee = NULL;
1042: self->monsterinfo.sight = gunner_sight;
1043: self->monsterinfo.search = gunner_search;
1044: self->monsterinfo.blocked = gunner_blocked; //PGM
1045:
1046: gi.linkentity (self);
1047:
1048: self->monsterinfo.currentmove = &gunner_move_stand;
1049: self->monsterinfo.scale = MODEL_SCALE;
1050:
1051: // PMM
1052: self->monsterinfo.blindfire = true;
1053:
1054: walkmonster_start (self);
1055: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.