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
gunner_idlesound(edict_t * self)21 void gunner_idlesound (edict_t *self)
22 {
23 gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
24 }
25
gunner_sight(edict_t * self,edict_t * other)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
gunner_search(edict_t * self)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
gunner_fidget(edict_t * self)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
gunner_stand(edict_t * self)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
gunner_walk(edict_t * self)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
gunner_run(edict_t * self)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
gunner_runandshoot(edict_t * self)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
gunner_pain(edict_t * self,edict_t * other,float kick,int damage)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
gunner_dead(edict_t * self)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
gunner_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)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 //
gunner_duck_down(edict_t * self)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
gunner_opengun(edict_t * self)397 void gunner_opengun (edict_t *self)
398 {
399 gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
400 }
401
GunnerFire(edict_t * self)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
gunner_grenade_check(edict_t * self)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
GunnerGrenade(edict_t * self)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
gunner_blind_check(edict_t * self)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
gunner_attack(edict_t * self)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 // minimum of 2 seconds, plus 0-3, after the shots are done
684 self->monsterinfo.blind_fire_delay += 2.1 + 2.0 + random()*3.0;
685
686 // don't shoot at the origin
687 if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
688 return;
689
690 // don't shoot if the dice say not to
691 if (r > chance)
692 {
693 // if ((g_showlogic) && (g_showlogic->value))
694 // gi.dprintf ("blindfire - NO SHOT\n");
695 return;
696 }
697
698 // turn on manual steering to signal both manual steering and blindfire
699 self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
700 if (gunner_grenade_check(self))
701 {
702 // if the check passes, go for the attack
703 self->monsterinfo.currentmove = &gunner_move_attack_grenade;
704 self->monsterinfo.attack_finished = level.time + 2*random();
705 }
706 // pmm - should this be active?
707 // else
708 // self->monsterinfo.currentmove = &gunner_move_attack_chain;
709 // if ((g_showlogic) && (g_showlogic->value))
710 // gi.dprintf ("blind grenade check failed, doing nothing\n");
711
712 // turn off blindfire flag
713 self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
714 return;
715 }
716 // pmm
717
718 // PGM - gunner needs to use his chaingun if he's being attacked by a tesla.
719 if ((range (self, self->enemy) == RANGE_MELEE) || self->bad_area)
720 {
721 self->monsterinfo.currentmove = &gunner_move_attack_chain;
722 }
723 else
724 {
725 if (random() <= 0.5 && gunner_grenade_check(self))
726 self->monsterinfo.currentmove = &gunner_move_attack_grenade;
727 else
728 self->monsterinfo.currentmove = &gunner_move_attack_chain;
729 }
730 }
731
gunner_fire_chain(edict_t * self)732 void gunner_fire_chain(edict_t *self)
733 {
734 self->monsterinfo.currentmove = &gunner_move_fire_chain;
735 }
736
gunner_refire_chain(edict_t * self)737 void gunner_refire_chain(edict_t *self)
738 {
739 if (self->enemy->health > 0)
740 if ( visible (self, self->enemy) )
741 if (random() <= 0.5)
742 {
743 self->monsterinfo.currentmove = &gunner_move_fire_chain;
744 return;
745 }
746 self->monsterinfo.currentmove = &gunner_move_endfire_chain;
747 }
748 /*
749 void gunner_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
750 {
751 // original quake2 dodge code
752
753 if (random() > 0.25)
754 return;
755
756 if (!self->enemy)
757 self->enemy = attacker;
758
759 self->monsterinfo.currentmove = &gunner_move_duck;
760
761 //===========
762 //PMM - rogue rewrite of gunner dodge code.
763 float r;
764 float height;
765 int shooting = 0;
766
767 if (!self->enemy)
768 {
769 self->enemy = attacker;
770 FoundTarget (self);
771 }
772
773 // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
774 // seeing numbers like 13 and 14)
775 if ((eta < 0.1) || (eta > 5))
776 return;
777
778 r = random();
779 if (r > (0.25*((skill->value)+1)))
780 return;
781
782 if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
783 (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
784 (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
785 )
786 {
787 shooting = 1;
788 }
789 if (self->monsterinfo.aiflags & AI_DODGING)
790 {
791 height = self->absmax[2];
792 }
793 else
794 {
795 height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1
796 }
797
798 // check to see if it makes sense to duck
799 if (tr->endpos[2] <= height)
800 {
801 vec3_t right, diff;
802 if (shooting)
803 {
804 self->monsterinfo.attack_state = AS_SLIDING;
805 return;
806 }
807 AngleVectors (self->s.angles, NULL, right, NULL);
808 VectorSubtract (tr->endpos, self->s.origin, diff);
809 if (DotProduct (right, diff) < 0)
810 {
811 self->monsterinfo.lefty = 1;
812 }
813 // if it doesn't sense to duck, try to strafe away
814 monster_done_dodge (self);
815 self->monsterinfo.currentmove = &gunner_move_run;
816 self->monsterinfo.attack_state = AS_SLIDING;
817 return;
818 }
819
820 if (skill->value == 0)
821 {
822 self->monsterinfo.currentmove = &gunner_move_duck;
823 // PMM - stupid dodge
824 self->monsterinfo.duck_wait_time = level.time + eta + 1;
825 self->monsterinfo.aiflags |= AI_DODGING;
826 return;
827 }
828
829 if (!shooting)
830 {
831 self->monsterinfo.currentmove = &gunner_move_duck;
832 self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
833 self->monsterinfo.aiflags |= AI_DODGING;
834 }
835 return;
836 //PMM
837 //===========
838 }
839 */
840 //===========
841 //PGM
gunner_jump_now(edict_t * self)842 void gunner_jump_now (edict_t *self)
843 {
844 vec3_t forward,up;
845
846 monster_jump_start (self);
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
gunner_jump2_now(edict_t * self)853 void gunner_jump2_now (edict_t *self)
854 {
855 vec3_t forward,up;
856
857 monster_jump_start (self);
858
859 AngleVectors (self->s.angles, forward, NULL, up);
860 VectorMA(self->velocity, 150, forward, self->velocity);
861 VectorMA(self->velocity, 400, up, self->velocity);
862 }
863
gunner_jump_wait_land(edict_t * self)864 void gunner_jump_wait_land (edict_t *self)
865 {
866 if(self->groundentity == NULL)
867 {
868 self->monsterinfo.nextframe = self->s.frame;
869
870 if(monster_jump_finished (self))
871 self->monsterinfo.nextframe = self->s.frame + 1;
872 }
873 else
874 self->monsterinfo.nextframe = self->s.frame + 1;
875 }
876
877 mframe_t gunner_frames_jump [] =
878 {
879 ai_move, 0, NULL,
880 ai_move, 0, NULL,
881 ai_move, 0, NULL,
882 ai_move, 0, gunner_jump_now,
883 ai_move, 0, NULL,
884 ai_move, 0, NULL,
885 ai_move, 0, gunner_jump_wait_land,
886 ai_move, 0, NULL,
887 ai_move, 0, NULL,
888 ai_move, 0, NULL
889 };
890 mmove_t gunner_move_jump = { FRAME_jump01, FRAME_jump10, gunner_frames_jump, gunner_run };
891
892 mframe_t gunner_frames_jump2 [] =
893 {
894 ai_move, -8, NULL,
895 ai_move, -4, NULL,
896 ai_move, -4, NULL,
897 ai_move, 0, gunner_jump_now,
898 ai_move, 0, NULL,
899 ai_move, 0, NULL,
900 ai_move, 0, gunner_jump_wait_land,
901 ai_move, 0, NULL,
902 ai_move, 0, NULL,
903 ai_move, 0, NULL
904 };
905 mmove_t gunner_move_jump2 = { FRAME_jump01, FRAME_jump10, gunner_frames_jump2, gunner_run };
906
gunner_jump(edict_t * self)907 void gunner_jump (edict_t *self)
908 {
909 if(!self->enemy)
910 return;
911
912 monster_done_dodge (self);
913
914 if(self->enemy->s.origin[2] > self->s.origin[2])
915 self->monsterinfo.currentmove = &gunner_move_jump2;
916 else
917 self->monsterinfo.currentmove = &gunner_move_jump;
918 }
919
920 //===========
921 //PGM
gunner_blocked(edict_t * self,float dist)922 qboolean gunner_blocked (edict_t *self, float dist)
923 {
924 if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
925 return true;
926
927 if(blocked_checkplat (self, dist))
928 return true;
929
930 if(blocked_checkjump (self, dist, 192, 40))
931 {
932 gunner_jump(self);
933 return true;
934 }
935
936 return false;
937 }
938 //PGM
939 //===========
940
941 // PMM - new duck code
gunner_duck(edict_t * self,float eta)942 void gunner_duck (edict_t *self, float eta)
943 {
944 if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
945 (self->monsterinfo.currentmove == &gunner_move_jump))
946 {
947 return;
948 }
949
950 if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
951 (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
952 (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
953 )
954 {
955 // if we're shooting, and not on easy, don't dodge
956 if (skill->value)
957 {
958 self->monsterinfo.aiflags &= ~AI_DUCKED;
959 return;
960 }
961 }
962
963 if (skill->value == 0)
964 // PMM - stupid dodge
965 self->monsterinfo.duck_wait_time = level.time + eta + 1;
966 else
967 self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
968
969 // has to be done immediately otherwise he can get stuck
970 gunner_duck_down(self);
971
972 self->monsterinfo.nextframe = FRAME_duck01;
973 self->monsterinfo.currentmove = &gunner_move_duck;
974 return;
975 }
976
gunner_sidestep(edict_t * self)977 void gunner_sidestep (edict_t *self)
978 {
979 if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
980 (self->monsterinfo.currentmove == &gunner_move_jump))
981 {
982 return;
983 }
984
985 if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
986 (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
987 (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
988 )
989 {
990 // if we're shooting, and not on easy, don't dodge
991 if (skill->value)
992 {
993 self->monsterinfo.aiflags &= ~AI_DODGING;
994 return;
995 }
996 }
997
998 if (self->monsterinfo.currentmove != &gunner_move_run)
999 self->monsterinfo.currentmove = &gunner_move_run;
1000 }
1001
1002
1003 /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
1004 */
SP_monster_gunner(edict_t * self)1005 void SP_monster_gunner (edict_t *self)
1006 {
1007 if (deathmatch->value)
1008 {
1009 G_FreeEdict (self);
1010 return;
1011 }
1012
1013 sound_death = gi.soundindex ("gunner/death1.wav");
1014 sound_pain = gi.soundindex ("gunner/gunpain2.wav");
1015 sound_pain2 = gi.soundindex ("gunner/gunpain1.wav");
1016 sound_idle = gi.soundindex ("gunner/gunidle1.wav");
1017 sound_open = gi.soundindex ("gunner/gunatck1.wav");
1018 sound_search = gi.soundindex ("gunner/gunsrch1.wav");
1019 sound_sight = gi.soundindex ("gunner/sight1.wav");
1020
1021 gi.soundindex ("gunner/gunatck2.wav");
1022 gi.soundindex ("gunner/gunatck3.wav");
1023
1024 self->movetype = MOVETYPE_STEP;
1025 self->solid = SOLID_BBOX;
1026 self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2");
1027 VectorSet (self->mins, -16, -16, -24);
1028 VectorSet (self->maxs, 16, 16, 32);
1029
1030 self->health = 175;
1031 self->gib_health = -70;
1032 self->mass = 200;
1033
1034 self->pain = gunner_pain;
1035 self->die = gunner_die;
1036
1037 self->monsterinfo.stand = gunner_stand;
1038 self->monsterinfo.walk = gunner_walk;
1039 self->monsterinfo.run = gunner_run;
1040 // pmm
1041 self->monsterinfo.dodge = M_MonsterDodge;
1042 self->monsterinfo.duck = gunner_duck;
1043 self->monsterinfo.unduck = monster_duck_up;
1044 self->monsterinfo.sidestep = gunner_sidestep;
1045 // self->monsterinfo.dodge = gunner_dodge;
1046 // pmm
1047 self->monsterinfo.attack = gunner_attack;
1048 self->monsterinfo.melee = NULL;
1049 self->monsterinfo.sight = gunner_sight;
1050 self->monsterinfo.search = gunner_search;
1051 self->monsterinfo.blocked = gunner_blocked; //PGM
1052
1053 gi.linkentity (self);
1054
1055 self->monsterinfo.currentmove = &gunner_move_stand;
1056 self->monsterinfo.scale = MODEL_SCALE;
1057
1058 // PMM
1059 self->monsterinfo.blindfire = true;
1060
1061 walkmonster_start (self);
1062 }
1063