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 
gunner_idlesound(edict_t * self)22 void gunner_idlesound (edict_t *self)
23 {
24 	gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
25 }
26 
gunner_sight(edict_t * self,edict_t * other)27 void gunner_sight (edict_t *self, edict_t *other)
28 {
29 	gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
30 }
31 
gunner_search(edict_t * self)32 void gunner_search (edict_t *self)
33 {
34 	gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
35 }
36 
37 
38 qboolean visible (edict_t *self, edict_t *other);
39 void GunnerGrenade (edict_t *self);
40 void GunnerFire (edict_t *self);
41 void gunner_fire_chain(edict_t *self);
42 void gunner_refire_chain(edict_t *self);
43 
44 
45 void gunner_stand (edict_t *self);
46 
47 mframe_t gunner_frames_fidget [] =
48 {
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, NULL,
56 	ai_stand, 0, gunner_idlesound,
57 	ai_stand, 0, NULL,
58 
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 	ai_stand, 0, NULL,
69 
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 	ai_stand, 0, NULL,
80 
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 	ai_stand, 0, NULL,
91 
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 	ai_stand, 0, NULL
102 };
103 mmove_t	gunner_move_fidget = {FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand};
104 
gunner_fidget(edict_t * self)105 void gunner_fidget (edict_t *self)
106 {
107 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
108 		return;
109 	if (random() <= 0.05)
110 		self->monsterinfo.currentmove = &gunner_move_fidget;
111 }
112 
113 mframe_t gunner_frames_stand [] =
114 {
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, NULL,
124 	ai_stand, 0, gunner_fidget,
125 
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, NULL,
135 	ai_stand, 0, gunner_fidget,
136 
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, NULL,
146 	ai_stand, 0, gunner_fidget
147 };
148 mmove_t	gunner_move_stand = {FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL};
149 
gunner_stand(edict_t * self)150 void gunner_stand (edict_t *self)
151 {
152 		self->monsterinfo.currentmove = &gunner_move_stand;
153 }
154 
155 
156 mframe_t gunner_frames_walk [] =
157 {
158 	ai_walk, 0, NULL,
159 	ai_walk, 3, NULL,
160 	ai_walk, 4, NULL,
161 	ai_walk, 5, NULL,
162 	ai_walk, 7, NULL,
163 	ai_walk, 2, NULL,
164 	ai_walk, 6, NULL,
165 	ai_walk, 4, NULL,
166 	ai_walk, 2, NULL,
167 	ai_walk, 7, NULL,
168 	ai_walk, 5, NULL,
169 	ai_walk, 7, NULL,
170 	ai_walk, 4, NULL
171 };
172 mmove_t gunner_move_walk = {FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL};
173 
gunner_walk(edict_t * self)174 void gunner_walk (edict_t *self)
175 {
176 	self->monsterinfo.currentmove = &gunner_move_walk;
177 }
178 
179 mframe_t gunner_frames_run [] =
180 {
181 	ai_run, 26, NULL,
182 	ai_run, 9,  NULL,
183 	ai_run, 9,  NULL,
184 	ai_run, 9,  NULL,
185 	ai_run, 15, NULL,
186 	ai_run, 10, NULL,
187 	ai_run, 13, NULL,
188 	ai_run, 6,  NULL
189 };
190 
191 mmove_t gunner_move_run = {FRAME_run01, FRAME_run08, gunner_frames_run, NULL};
192 
gunner_run(edict_t * self)193 void gunner_run (edict_t *self)
194 {
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 	if (level.time < self->pain_debounce_time)
270 		return;
271 
272 	self->pain_debounce_time = level.time + 3;
273 
274 	if (rand()&1)
275 		gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
276 	else
277 		gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
278 
279 	if (skill->value == 3)
280 		return;		// no pain anims in nightmare
281 
282 	if (damage <= 10)
283 		self->monsterinfo.currentmove = &gunner_move_pain3;
284 	else if (damage <= 25)
285 		self->monsterinfo.currentmove = &gunner_move_pain2;
286 	else
287 		self->monsterinfo.currentmove = &gunner_move_pain1;
288 }
289 
gunner_dead(edict_t * self)290 void gunner_dead (edict_t *self)
291 {
292 	VectorSet (self->mins, -16, -16, -24);
293 	VectorSet (self->maxs, 16, 16, -8);
294 	self->movetype = MOVETYPE_TOSS;
295 	self->svflags |= SVF_DEADMONSTER;
296 	self->nextthink = 0;
297 	gi.linkentity (self);
298 }
299 
300 mframe_t gunner_frames_death [] =
301 {
302 	ai_move, 0,	 NULL,
303 	ai_move, 0,	 NULL,
304 	ai_move, 0,	 NULL,
305 	ai_move, -7, NULL,
306 	ai_move, -3, NULL,
307 	ai_move, -5, NULL,
308 	ai_move, 8,	 NULL,
309 	ai_move, 6,	 NULL,
310 	ai_move, 0,	 NULL,
311 	ai_move, 0,	 NULL,
312 	ai_move, 0,	 NULL
313 };
314 mmove_t gunner_move_death = {FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead};
315 
gunner_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)316 void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
317 {
318 	int		n;
319 
320 // check for gib
321 	if (self->health <= self->gib_health)
322 	{
323 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
324 		for (n= 0; n < 2; n++)
325 			ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
326 		for (n= 0; n < 4; n++)
327 			ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
328 		ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
329 		self->deadflag = DEAD_DEAD;
330 		return;
331 	}
332 
333 	if (self->deadflag == DEAD_DEAD)
334 		return;
335 
336 // regular death
337 	gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
338 	self->deadflag = DEAD_DEAD;
339 	self->takedamage = DAMAGE_YES;
340 	self->monsterinfo.currentmove = &gunner_move_death;
341 }
342 
343 
gunner_duck_down(edict_t * self)344 void gunner_duck_down (edict_t *self)
345 {
346 	if (self->monsterinfo.aiflags & AI_DUCKED)
347 		return;
348 	self->monsterinfo.aiflags |= AI_DUCKED;
349 	if (skill->value >= 2)
350 	{
351 		if (random() > 0.5)
352 			GunnerGrenade (self);
353 	}
354 
355 	self->maxs[2] -= 32;
356 	self->takedamage = DAMAGE_YES;
357 	self->monsterinfo.pausetime = level.time + 1;
358 	gi.linkentity (self);
359 }
360 
gunner_duck_hold(edict_t * self)361 void gunner_duck_hold (edict_t *self)
362 {
363 	if (level.time >= self->monsterinfo.pausetime)
364 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
365 	else
366 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
367 }
368 
gunner_duck_up(edict_t * self)369 void gunner_duck_up (edict_t *self)
370 {
371 	self->monsterinfo.aiflags &= ~AI_DUCKED;
372 	self->maxs[2] += 32;
373 	self->takedamage = DAMAGE_AIM;
374 	gi.linkentity (self);
375 }
376 
377 mframe_t gunner_frames_duck [] =
378 {
379 	ai_move, 1,  gunner_duck_down,
380 	ai_move, 1,  NULL,
381 	ai_move, 1,  gunner_duck_hold,
382 	ai_move, 0,  NULL,
383 	ai_move, -1, NULL,
384 	ai_move, -1, NULL,
385 	ai_move, 0,  gunner_duck_up,
386 	ai_move, -1, NULL
387 };
388 mmove_t	gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run};
389 
gunner_dodge(edict_t * self,edict_t * attacker,float eta)390 void gunner_dodge (edict_t *self, edict_t *attacker, float eta)
391 {
392 	if (random() > 0.25)
393 		return;
394 
395 	if (!self->enemy)
396 		self->enemy = attacker;
397 
398 	self->monsterinfo.currentmove = &gunner_move_duck;
399 }
400 
401 
gunner_opengun(edict_t * self)402 void gunner_opengun (edict_t *self)
403 {
404 	gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
405 }
406 
GunnerFire(edict_t * self)407 void GunnerFire (edict_t *self)
408 {
409 	vec3_t	start;
410 	vec3_t	forward, right;
411 	vec3_t	target;
412 	vec3_t	aim;
413 	int		flash_number;
414 
415 	flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
416 
417 	AngleVectors (self->s.angles, forward, right, NULL);
418 	G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
419 
420 	// project enemy back a bit and target there
421 	VectorCopy (self->enemy->s.origin, target);
422 	VectorMA (target, -0.2, self->enemy->velocity, target);
423 	target[2] += self->enemy->viewheight;
424 
425 	VectorSubtract (target, start, aim);
426 	VectorNormalize (aim);
427 	monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
428 }
429 
GunnerGrenade(edict_t * self)430 void GunnerGrenade (edict_t *self)
431 {
432 	vec3_t	start;
433 	vec3_t	forward, right;
434 	vec3_t	aim;
435 	int		flash_number;
436 
437 	if (self->s.frame == FRAME_attak105)
438 		flash_number = MZ2_GUNNER_GRENADE_1;
439 	else if (self->s.frame == FRAME_attak108)
440 		flash_number = MZ2_GUNNER_GRENADE_2;
441 	else if (self->s.frame == FRAME_attak111)
442 		flash_number = MZ2_GUNNER_GRENADE_3;
443 	else // (self->s.frame == FRAME_attak114)
444 		flash_number = MZ2_GUNNER_GRENADE_4;
445 
446 	AngleVectors (self->s.angles, forward, right, NULL);
447 	G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
448 
449 	//FIXME : do a spread -225 -75 75 225 degrees around forward
450 	VectorCopy (forward, aim);
451 
452 	monster_fire_grenade (self, start, aim, 50, 600, flash_number);
453 }
454 
455 mframe_t gunner_frames_attack_chain [] =
456 {
457 	/*
458 	ai_charge, 0, NULL,
459 	ai_charge, 0, NULL,
460 	ai_charge, 0, NULL,
461 	ai_charge, 0, NULL,
462 	ai_charge, 0, NULL,
463 	ai_charge, 0, NULL,
464 	ai_charge, 0, NULL,
465 	ai_charge, 0, NULL,
466 	*/
467 	ai_charge, 0, gunner_opengun,
468 	ai_charge, 0, NULL,
469 	ai_charge, 0, NULL,
470 	ai_charge, 0, NULL,
471 	ai_charge, 0, NULL,
472 	ai_charge, 0, NULL,
473 	ai_charge, 0, NULL
474 };
475 mmove_t gunner_move_attack_chain = {FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain};
476 
477 mframe_t gunner_frames_fire_chain [] =
478 {
479 	ai_charge,   0, GunnerFire,
480 	ai_charge,   0, GunnerFire,
481 	ai_charge,   0, GunnerFire,
482 	ai_charge,   0, GunnerFire,
483 	ai_charge,   0, GunnerFire,
484 	ai_charge,   0, GunnerFire,
485 	ai_charge,   0, GunnerFire,
486 	ai_charge,   0, GunnerFire
487 };
488 mmove_t gunner_move_fire_chain = {FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain};
489 
490 mframe_t gunner_frames_endfire_chain [] =
491 {
492 	ai_charge, 0, NULL,
493 	ai_charge, 0, NULL,
494 	ai_charge, 0, NULL,
495 	ai_charge, 0, NULL,
496 	ai_charge, 0, NULL,
497 	ai_charge, 0, NULL,
498 	ai_charge, 0, NULL
499 };
500 mmove_t gunner_move_endfire_chain = {FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run};
501 
502 mframe_t gunner_frames_attack_grenade [] =
503 {
504 	ai_charge, 0, NULL,
505 	ai_charge, 0, NULL,
506 	ai_charge, 0, NULL,
507 	ai_charge, 0, NULL,
508 	ai_charge, 0, GunnerGrenade,
509 	ai_charge, 0, NULL,
510 	ai_charge, 0, NULL,
511 	ai_charge, 0, GunnerGrenade,
512 	ai_charge, 0, NULL,
513 	ai_charge, 0, NULL,
514 	ai_charge, 0, GunnerGrenade,
515 	ai_charge, 0, NULL,
516 	ai_charge, 0, NULL,
517 	ai_charge, 0, GunnerGrenade,
518 	ai_charge, 0, NULL,
519 	ai_charge, 0, NULL,
520 	ai_charge, 0, NULL,
521 	ai_charge, 0, NULL,
522 	ai_charge, 0, NULL,
523 	ai_charge, 0, NULL,
524 	ai_charge, 0, NULL
525 };
526 mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run};
527 
gunner_attack(edict_t * self)528 void gunner_attack(edict_t *self)
529 {
530 	if (range (self, self->enemy) == RANGE_MELEE)
531 	{
532 		self->monsterinfo.currentmove = &gunner_move_attack_chain;
533 	}
534 	else
535 	{
536 		if (random() <= 0.5)
537 			self->monsterinfo.currentmove = &gunner_move_attack_grenade;
538 		else
539 			self->monsterinfo.currentmove = &gunner_move_attack_chain;
540 	}
541 }
542 
gunner_fire_chain(edict_t * self)543 void gunner_fire_chain(edict_t *self)
544 {
545 	self->monsterinfo.currentmove = &gunner_move_fire_chain;
546 }
547 
gunner_refire_chain(edict_t * self)548 void gunner_refire_chain(edict_t *self)
549 {
550 	if (self->enemy->health > 0)
551 		if ( visible (self, self->enemy) )
552 			if (random() <= 0.5)
553 			{
554 				self->monsterinfo.currentmove = &gunner_move_fire_chain;
555 				return;
556 			}
557 	self->monsterinfo.currentmove = &gunner_move_endfire_chain;
558 }
559 
560 /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
561 */
SP_monster_gunner(edict_t * self)562 void SP_monster_gunner (edict_t *self)
563 {
564 	if (deathmatch->value)
565 	{
566 		G_FreeEdict (self);
567 		return;
568 	}
569 
570 	sound_death = gi.soundindex ("gunner/death1.wav");
571 	sound_pain = gi.soundindex ("gunner/gunpain2.wav");
572 	sound_pain2 = gi.soundindex ("gunner/gunpain1.wav");
573 	sound_idle = gi.soundindex ("gunner/gunidle1.wav");
574 	sound_open = gi.soundindex ("gunner/gunatck1.wav");
575 	sound_search = gi.soundindex ("gunner/gunsrch1.wav");
576 	sound_sight = gi.soundindex ("gunner/sight1.wav");
577 
578 	gi.soundindex ("gunner/gunatck2.wav");
579 	gi.soundindex ("gunner/gunatck3.wav");
580 
581 	self->movetype = MOVETYPE_STEP;
582 	self->solid = SOLID_BBOX;
583 	self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2");
584 	VectorSet (self->mins, -16, -16, -24);
585 	VectorSet (self->maxs, 16, 16, 32);
586 
587 	self->health = 175;
588 	self->gib_health = -70;
589 	self->mass = 200;
590 
591 	self->pain = gunner_pain;
592 	self->die = gunner_die;
593 
594 	self->monsterinfo.stand = gunner_stand;
595 	self->monsterinfo.walk = gunner_walk;
596 	self->monsterinfo.run = gunner_run;
597 	self->monsterinfo.dodge = gunner_dodge;
598 	self->monsterinfo.attack = gunner_attack;
599 	self->monsterinfo.melee = NULL;
600 	self->monsterinfo.sight = gunner_sight;
601 	self->monsterinfo.search = gunner_search;
602 
603 	gi.linkentity (self);
604 
605 	self->monsterinfo.currentmove = &gunner_move_stand;
606 	self->monsterinfo.scale = MODEL_SCALE;
607 
608 	walkmonster_start (self);
609 }
610