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