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