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