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