1 /*
2 ==============================================================================
3 
4 INFANTRY
5 
6 ==============================================================================
7 */
8 
9 #include "g_local.h"
10 #include "m_infantry.h"
11 
12 void InfantryMachineGun (edict_t *self);
13 
14 
15 static int	sound_pain1;
16 static int	sound_pain2;
17 static int	sound_die1;
18 static int	sound_die2;
19 
20 static int	sound_gunshot;
21 static int	sound_weapon_cock;
22 static int	sound_punch_swing;
23 static int	sound_punch_hit;
24 static int	sound_sight;
25 static int	sound_search;
26 static int	sound_idle;
27 
28 
29 mframe_t infantry_frames_stand [] =
30 {
31 	ai_stand, 0, NULL,
32 	ai_stand, 0, NULL,
33 	ai_stand, 0, NULL,
34 	ai_stand, 0, NULL,
35 	ai_stand, 0, NULL,
36 	ai_stand, 0, NULL,
37 	ai_stand, 0, NULL,
38 	ai_stand, 0, NULL,
39 	ai_stand, 0, NULL,
40 	ai_stand, 0, NULL,
41 	ai_stand, 0, NULL,
42 	ai_stand, 0, NULL,
43 	ai_stand, 0, NULL,
44 	ai_stand, 0, NULL,
45 	ai_stand, 0, NULL,
46 	ai_stand, 0, NULL,
47 	ai_stand, 0, NULL,
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 };
54 mmove_t infantry_move_stand = {FRAME_stand50, FRAME_stand71, infantry_frames_stand, NULL};
55 
infantry_stand(edict_t * self)56 void infantry_stand (edict_t *self)
57 {
58 	self->monsterinfo.currentmove = &infantry_move_stand;
59 }
60 
61 
62 mframe_t infantry_frames_fidget [] =
63 {
64 	ai_stand, 1,  NULL,
65 	ai_stand, 0,  NULL,
66 	ai_stand, 1,  NULL,
67 	ai_stand, 3,  NULL,
68 	ai_stand, 6,  NULL,
69 	ai_stand, 3,  NULL,
70 	ai_stand, 0,  NULL,
71 	ai_stand, 0,  NULL,
72 	ai_stand, 0,  NULL,
73 	ai_stand, 0,  NULL,
74 	ai_stand, 1,  NULL,
75 	ai_stand, 0,  NULL,
76 	ai_stand, 0,  NULL,
77 	ai_stand, 0,  NULL,
78 	ai_stand, 0,  NULL,
79 	ai_stand, 1,  NULL,
80 	ai_stand, 0,  NULL,
81 	ai_stand, -1, NULL,
82 	ai_stand, 0,  NULL,
83 	ai_stand, 0,  NULL,
84 	ai_stand, 1,  NULL,
85 	ai_stand, 0,  NULL,
86 	ai_stand, -2, NULL,
87 	ai_stand, 1,  NULL,
88 	ai_stand, 1,  NULL,
89 	ai_stand, 1,  NULL,
90 	ai_stand, -1, NULL,
91 	ai_stand, 0,  NULL,
92 	ai_stand, 0,  NULL,
93 	ai_stand, -1, 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, -1, NULL,
100 	ai_stand, 0,  NULL,
101 	ai_stand, 0,  NULL,
102 	ai_stand, 1,  NULL,
103 	ai_stand, 0,  NULL,
104 	ai_stand, 0,  NULL,
105 	ai_stand, -1, NULL,
106 	ai_stand, -1, NULL,
107 	ai_stand, 0,  NULL,
108 	ai_stand, -3, NULL,
109 	ai_stand, -2, NULL,
110 	ai_stand, -3, NULL,
111 	ai_stand, -3, NULL,
112 	ai_stand, -2, NULL
113 };
114 mmove_t infantry_move_fidget = {FRAME_stand01, FRAME_stand49, infantry_frames_fidget, infantry_stand};
115 
infantry_fidget(edict_t * self)116 void infantry_fidget (edict_t *self)
117 {
118 	self->monsterinfo.currentmove = &infantry_move_fidget;
119 	gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
120 }
121 
122 mframe_t infantry_frames_walk [] =
123 {
124 	ai_walk, 5,  NULL,
125 	ai_walk, 4,  NULL,
126 	ai_walk, 4,  NULL,
127 	ai_walk, 5,  NULL,
128 	ai_walk, 4,  NULL,
129 	ai_walk, 5,  NULL,
130 	ai_walk, 6,  NULL,
131 	ai_walk, 4,  NULL,
132 	ai_walk, 4,  NULL,
133 	ai_walk, 4,  NULL,
134 	ai_walk, 4,  NULL,
135 	ai_walk, 5,  NULL
136 };
137 mmove_t infantry_move_walk = {FRAME_walk03, FRAME_walk14, infantry_frames_walk, NULL};
138 
infantry_walk(edict_t * self)139 void infantry_walk (edict_t *self)
140 {
141 	self->monsterinfo.currentmove = &infantry_move_walk;
142 }
143 
144 mframe_t infantry_frames_run [] =
145 {
146 	ai_run, 10, NULL,
147 	ai_run, 20, NULL,
148 	ai_run, 5,  NULL,
149 	ai_run, 7,  NULL,
150 	ai_run, 30, NULL,
151 	ai_run, 35, NULL,
152 	ai_run, 2,  NULL,
153 	ai_run, 6,  NULL
154 };
155 mmove_t infantry_move_run = {FRAME_run01, FRAME_run08, infantry_frames_run, NULL};
156 
infantry_run(edict_t * self)157 void infantry_run (edict_t *self)
158 {
159 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
160 		self->monsterinfo.currentmove = &infantry_move_stand;
161 	else
162 		self->monsterinfo.currentmove = &infantry_move_run;
163 }
164 
165 
166 mframe_t infantry_frames_pain1 [] =
167 {
168 	ai_move, -3, NULL,
169 	ai_move, -2, NULL,
170 	ai_move, -1, NULL,
171 	ai_move, -2, NULL,
172 	ai_move, -1, NULL,
173 	ai_move, 1,  NULL,
174 	ai_move, -1, NULL,
175 	ai_move, 1,  NULL,
176 	ai_move, 6,  NULL,
177 	ai_move, 2,  NULL
178 };
179 mmove_t infantry_move_pain1 = {FRAME_pain101, FRAME_pain110, infantry_frames_pain1, infantry_run};
180 
181 mframe_t infantry_frames_pain2 [] =
182 {
183 	ai_move, -3, NULL,
184 	ai_move, -3, NULL,
185 	ai_move, 0,  NULL,
186 	ai_move, -1, NULL,
187 	ai_move, -2, NULL,
188 	ai_move, 0,  NULL,
189 	ai_move, 0,  NULL,
190 	ai_move, 2,  NULL,
191 	ai_move, 5,  NULL,
192 	ai_move, 2,  NULL
193 };
194 mmove_t infantry_move_pain2 = {FRAME_pain201, FRAME_pain210, infantry_frames_pain2, infantry_run};
195 
infantry_pain(edict_t * self,edict_t * other,float kick,int damage)196 void infantry_pain (edict_t *self, edict_t *other, float kick, int damage)
197 {
198 	int		n;
199 
200 	if (self->health < (self->max_health / 2))
201 		self->s.skinnum = 1;
202 
203 	if (level.time < self->pain_debounce_time)
204 		return;
205 
206 	self->pain_debounce_time = level.time + 3;
207 
208 	if (skill->value == 3)
209 		return;		// no pain anims in nightmare
210 
211 	n = rand() % 2;
212 	if (n == 0)
213 	{
214 		self->monsterinfo.currentmove = &infantry_move_pain1;
215 		gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
216 	}
217 	else
218 	{
219 		self->monsterinfo.currentmove = &infantry_move_pain2;
220 		gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
221 	}
222 }
223 
224 
225 vec3_t	aimangles[] =
226 {
227 	0.0, 5.0, 0.0,
228 	10.0, 15.0, 0.0,
229 	20.0, 25.0, 0.0,
230 	25.0, 35.0, 0.0,
231 	30.0, 40.0, 0.0,
232 	30.0, 45.0, 0.0,
233 	25.0, 50.0, 0.0,
234 	20.0, 40.0, 0.0,
235 	15.0, 35.0, 0.0,
236 	40.0, 35.0, 0.0,
237 	70.0, 35.0, 0.0,
238 	90.0, 35.0, 0.0
239 };
240 
InfantryMachineGun(edict_t * self)241 void InfantryMachineGun (edict_t *self)
242 {
243 	vec3_t	start, target;
244 	vec3_t	forward, right;
245 	vec3_t	vec;
246 	int		flash_number;
247 
248 	if (self->s.frame == FRAME_attak111)
249 	{
250 		flash_number = MZ2_INFANTRY_MACHINEGUN_1;
251 		AngleVectors (self->s.angles, forward, right, NULL);
252 		G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
253 
254 		if (self->enemy)
255 		{
256 			VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target);
257 			target[2] += self->enemy->viewheight;
258 			VectorSubtract (target, start, forward);
259 			VectorNormalize (forward);
260 		}
261 		else
262 		{
263 			AngleVectors (self->s.angles, forward, right, NULL);
264 		}
265 	}
266 	else
267 	{
268 		flash_number = MZ2_INFANTRY_MACHINEGUN_2 + (self->s.frame - FRAME_death211);
269 
270 		AngleVectors (self->s.angles, forward, right, NULL);
271 		G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
272 
273 		VectorSubtract (self->s.angles, aimangles[flash_number-MZ2_INFANTRY_MACHINEGUN_2], vec);
274 		AngleVectors (vec, forward, NULL, NULL);
275 	}
276 
277 	monster_fire_bullet (self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
278 }
279 
infantry_sight(edict_t * self,edict_t * other)280 void infantry_sight (edict_t *self, edict_t *other)
281 {
282 	gi.sound (self, CHAN_BODY, sound_sight, 1, ATTN_NORM, 0);
283 }
284 
infantry_dead(edict_t * self)285 void infantry_dead (edict_t *self)
286 {
287 	VectorSet (self->mins, -16, -16, -24);
288 	VectorSet (self->maxs, 16, 16, -8);
289 	self->movetype = MOVETYPE_TOSS;
290 	self->svflags |= SVF_DEADMONSTER;
291 	gi.linkentity (self);
292 
293 	M_FlyCheck (self);
294 }
295 
296 mframe_t infantry_frames_death1 [] =
297 {
298 	ai_move, -4, NULL,
299 	ai_move, 0,  NULL,
300 	ai_move, 0,  NULL,
301 	ai_move, -1, NULL,
302 	ai_move, -4, NULL,
303 	ai_move, 0,  NULL,
304 	ai_move, 0,  NULL,
305 	ai_move, 0,  NULL,
306 	ai_move, -1, NULL,
307 	ai_move, 3,  NULL,
308 	ai_move, 1,  NULL,
309 	ai_move, 1,  NULL,
310 	ai_move, -2, NULL,
311 	ai_move, 2,  NULL,
312 	ai_move, 2,  NULL,
313 	ai_move, 9,  NULL,
314 	ai_move, 9,  NULL,
315 	ai_move, 5,  NULL,
316 	ai_move, -3, NULL,
317 	ai_move, -3, NULL
318 };
319 mmove_t infantry_move_death1 = {FRAME_death101, FRAME_death120, infantry_frames_death1, infantry_dead};
320 
321 // Off with his head
322 mframe_t infantry_frames_death2 [] =
323 {
324 	ai_move, 0,   NULL,
325 	ai_move, 1,   NULL,
326 	ai_move, 5,   NULL,
327 	ai_move, -1,  NULL,
328 	ai_move, 0,   NULL,
329 	ai_move, 1,   NULL,
330 	ai_move, 1,   NULL,
331 	ai_move, 4,   NULL,
332 	ai_move, 3,   NULL,
333 	ai_move, 0,   NULL,
334 	ai_move, -2,  InfantryMachineGun,
335 	ai_move, -2,  InfantryMachineGun,
336 	ai_move, -3,  InfantryMachineGun,
337 	ai_move, -1,  InfantryMachineGun,
338 	ai_move, -2,  InfantryMachineGun,
339 	ai_move, 0,   InfantryMachineGun,
340 	ai_move, 2,   InfantryMachineGun,
341 	ai_move, 2,   InfantryMachineGun,
342 	ai_move, 3,   InfantryMachineGun,
343 	ai_move, -10, InfantryMachineGun,
344 	ai_move, -7,  InfantryMachineGun,
345 	ai_move, -8,  InfantryMachineGun,
346 	ai_move, -6,  NULL,
347 	ai_move, 4,   NULL,
348 	ai_move, 0,   NULL
349 };
350 mmove_t infantry_move_death2 = {FRAME_death201, FRAME_death225, infantry_frames_death2, infantry_dead};
351 
352 mframe_t infantry_frames_death3 [] =
353 {
354 	ai_move, 0,   NULL,
355 	ai_move, 0,   NULL,
356 	ai_move, 0,   NULL,
357 	ai_move, -6,  NULL,
358 	ai_move, -11, NULL,
359 	ai_move, -3,  NULL,
360 	ai_move, -11, NULL,
361 	ai_move, 0,   NULL,
362 	ai_move, 0,   NULL
363 };
364 mmove_t infantry_move_death3 = {FRAME_death301, FRAME_death309, infantry_frames_death3, infantry_dead};
365 
366 
infantry_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)367 void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
368 {
369 	int		n;
370 
371 // check for gib
372 	if (self->health <= self->gib_health)
373 	{
374 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
375 		for (n= 0; n < 2; n++)
376 			ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
377 		for (n= 0; n < 4; n++)
378 			ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
379 		ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
380 		self->deadflag = DEAD_DEAD;
381 		return;
382 	}
383 
384 	if (self->deadflag == DEAD_DEAD)
385 		return;
386 
387 // regular death
388 	self->deadflag = DEAD_DEAD;
389 	self->takedamage = DAMAGE_YES;
390 
391 	n = rand() % 3;
392 	if (n == 0)
393 	{
394 		self->monsterinfo.currentmove = &infantry_move_death1;
395 		gi.sound (self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
396 	}
397 	else if (n == 1)
398 	{
399 		self->monsterinfo.currentmove = &infantry_move_death2;
400 		gi.sound (self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0);
401 	}
402 	else
403 	{
404 		self->monsterinfo.currentmove = &infantry_move_death3;
405 		gi.sound (self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
406 	}
407 }
408 
409 
infantry_duck_down(edict_t * self)410 void infantry_duck_down (edict_t *self)
411 {
412 	if (self->monsterinfo.aiflags & AI_DUCKED)
413 		return;
414 	self->monsterinfo.aiflags |= AI_DUCKED;
415 	self->maxs[2] -= 32;
416 	self->takedamage = DAMAGE_YES;
417 	self->monsterinfo.pausetime = level.time + 1;
418 	gi.linkentity (self);
419 }
420 
infantry_duck_hold(edict_t * self)421 void infantry_duck_hold (edict_t *self)
422 {
423 	if (level.time >= self->monsterinfo.pausetime)
424 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
425 	else
426 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
427 }
428 
infantry_duck_up(edict_t * self)429 void infantry_duck_up (edict_t *self)
430 {
431 	self->monsterinfo.aiflags &= ~AI_DUCKED;
432 	self->maxs[2] += 32;
433 	self->takedamage = DAMAGE_AIM;
434 	gi.linkentity (self);
435 }
436 
437 mframe_t infantry_frames_duck [] =
438 {
439 	ai_move, -2, infantry_duck_down,
440 	ai_move, -5, infantry_duck_hold,
441 	ai_move, 3,  NULL,
442 	ai_move, 4,  infantry_duck_up,
443 	ai_move, 0,  NULL
444 };
445 mmove_t infantry_move_duck = {FRAME_duck01, FRAME_duck05, infantry_frames_duck, infantry_run};
446 
infantry_dodge(edict_t * self,edict_t * attacker,float eta)447 void infantry_dodge (edict_t *self, edict_t *attacker, float eta)
448 {
449 	if (random() > 0.25)
450 		return;
451 
452 	if (!self->enemy)
453 		self->enemy = attacker;
454 
455 	self->monsterinfo.currentmove = &infantry_move_duck;
456 }
457 
458 
infantry_cock_gun(edict_t * self)459 void infantry_cock_gun (edict_t *self)
460 {
461 	int		n;
462 
463 	gi.sound (self, CHAN_WEAPON, sound_weapon_cock, 1, ATTN_NORM, 0);
464 	n = (rand() & 15) + 3 + 7;
465 	self->monsterinfo.pausetime = level.time + n * FRAMETIME;
466 }
467 
infantry_fire(edict_t * self)468 void infantry_fire (edict_t *self)
469 {
470 	InfantryMachineGun (self);
471 
472 	if (level.time >= self->monsterinfo.pausetime)
473 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
474 	else
475 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
476 }
477 
478 mframe_t infantry_frames_attack1 [] =
479 {
480 	ai_charge, 4,  NULL,
481 	ai_charge, -1, NULL,
482 	ai_charge, -1, NULL,
483 	ai_charge, 0,  infantry_cock_gun,
484 	ai_charge, -1, NULL,
485 	ai_charge, 1,  NULL,
486 	ai_charge, 1,  NULL,
487 	ai_charge, 2,  NULL,
488 	ai_charge, -2, NULL,
489 	ai_charge, -3, NULL,
490 	ai_charge, 1,  infantry_fire,
491 	ai_charge, 5,  NULL,
492 	ai_charge, -1, NULL,
493 	ai_charge, -2, NULL,
494 	ai_charge, -3, NULL
495 };
496 mmove_t infantry_move_attack1 = {FRAME_attak101, FRAME_attak115, infantry_frames_attack1, infantry_run};
497 
498 
infantry_swing(edict_t * self)499 void infantry_swing (edict_t *self)
500 {
501 	gi.sound (self, CHAN_WEAPON, sound_punch_swing, 1, ATTN_NORM, 0);
502 }
503 
infantry_smack(edict_t * self)504 void infantry_smack (edict_t *self)
505 {
506 	vec3_t	aim;
507 
508 	VectorSet (aim, MELEE_DISTANCE, 0, 0);
509 	if (fire_hit (self, aim, (5 + (rand() % 5)), 50))
510 		gi.sound (self, CHAN_WEAPON, sound_punch_hit, 1, ATTN_NORM, 0);
511 }
512 
513 mframe_t infantry_frames_attack2 [] =
514 {
515 	ai_charge, 3, NULL,
516 	ai_charge, 6, NULL,
517 	ai_charge, 0, infantry_swing,
518 	ai_charge, 8, NULL,
519 	ai_charge, 5, NULL,
520 	ai_charge, 8, infantry_smack,
521 	ai_charge, 6, NULL,
522 	ai_charge, 3, NULL,
523 };
524 mmove_t infantry_move_attack2 = {FRAME_attak201, FRAME_attak208, infantry_frames_attack2, infantry_run};
525 
infantry_attack(edict_t * self)526 void infantry_attack(edict_t *self)
527 {
528 	if (range (self, self->enemy) == RANGE_MELEE)
529 		self->monsterinfo.currentmove = &infantry_move_attack2;
530 	else
531 		self->monsterinfo.currentmove = &infantry_move_attack1;
532 }
533 
534 
535 /*QUAKED monster_infantry (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
536 */
SP_monster_infantry(edict_t * self)537 void SP_monster_infantry (edict_t *self)
538 {
539 	if (deathmatch->value)
540 	{
541 		G_FreeEdict (self);
542 		return;
543 	}
544 
545 	sound_pain1 = gi.soundindex ("infantry/infpain1.wav");
546 	sound_pain2 = gi.soundindex ("infantry/infpain2.wav");
547 	sound_die1 = gi.soundindex ("infantry/infdeth1.wav");
548 	sound_die2 = gi.soundindex ("infantry/infdeth2.wav");
549 
550 	sound_gunshot = gi.soundindex ("infantry/infatck1.wav");
551 	sound_weapon_cock = gi.soundindex ("infantry/infatck3.wav");
552 	sound_punch_swing = gi.soundindex ("infantry/infatck2.wav");
553 	sound_punch_hit = gi.soundindex ("infantry/melee2.wav");
554 
555 	sound_sight = gi.soundindex ("infantry/infsght1.wav");
556 	sound_search = gi.soundindex ("infantry/infsrch1.wav");
557 	sound_idle = gi.soundindex ("infantry/infidle1.wav");
558 
559 
560 	self->movetype = MOVETYPE_STEP;
561 	self->solid = SOLID_BBOX;
562 	self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
563 	VectorSet (self->mins, -16, -16, -24);
564 	VectorSet (self->maxs, 16, 16, 32);
565 
566 	self->health = 100;
567 	self->gib_health = -40;
568 	self->mass = 200;
569 
570 	self->pain = infantry_pain;
571 	self->die = infantry_die;
572 
573 	self->monsterinfo.stand = infantry_stand;
574 	self->monsterinfo.walk = infantry_walk;
575 	self->monsterinfo.run = infantry_run;
576 	self->monsterinfo.dodge = infantry_dodge;
577 	self->monsterinfo.attack = infantry_attack;
578 	self->monsterinfo.melee = NULL;
579 	self->monsterinfo.sight = infantry_sight;
580 	self->monsterinfo.idle = infantry_fidget;
581 
582 	gi.linkentity (self);
583 
584 	self->monsterinfo.currentmove = &infantry_move_stand;
585 	self->monsterinfo.scale = MODEL_SCALE;
586 
587 	walkmonster_start (self);
588 }
589