1 /*
2 ==============================================================================
3
4 TANK
5
6 ==============================================================================
7 */
8
9 #include "g_local.h"
10 #include "m_tank.h"
11
12
13 void tank_refire_rocket (edict_t *self);
14 void tank_doattack_rocket (edict_t *self);
15 void tank_reattack_blaster (edict_t *self);
16
17 static int sound_thud;
18 static int sound_pain;
19 static int sound_idle;
20 static int sound_die;
21 static int sound_step;
22 static int sound_sight;
23 static int sound_windup;
24 static int sound_strike;
25
26 //
27 // misc
28 //
29
tank_sight(edict_t * self,edict_t * other)30 void tank_sight (edict_t *self, edict_t *other)
31 {
32 gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
33 }
34
35
tank_footstep(edict_t * self)36 void tank_footstep (edict_t *self)
37 {
38 gi.sound (self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0);
39 }
40
tank_thud(edict_t * self)41 void tank_thud (edict_t *self)
42 {
43 gi.sound (self, CHAN_BODY, sound_thud, 1, ATTN_NORM, 0);
44 }
45
tank_windup(edict_t * self)46 void tank_windup (edict_t *self)
47 {
48 gi.sound (self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0);
49 }
50
tank_idle(edict_t * self)51 void tank_idle (edict_t *self)
52 {
53 gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
54 }
55
56
57 //
58 // stand
59 //
60
61 mframe_t tank_frames_stand []=
62 {
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 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 ai_stand, 0, NULL,
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 ai_stand, 0, NULL,
92 ai_stand, 0, NULL
93 };
94 mmove_t tank_move_stand = {FRAME_stand01, FRAME_stand30, tank_frames_stand, NULL};
95
tank_stand(edict_t * self)96 void tank_stand (edict_t *self)
97 {
98 self->monsterinfo.currentmove = &tank_move_stand;
99 }
100
101
102 //
103 // walk
104 //
105
106 void tank_walk (edict_t *self);
107
108 mframe_t tank_frames_start_walk [] =
109 {
110 ai_walk, 0, NULL,
111 ai_walk, 6, NULL,
112 ai_walk, 6, NULL,
113 ai_walk, 11, tank_footstep
114 };
115 mmove_t tank_move_start_walk = {FRAME_walk01, FRAME_walk04, tank_frames_start_walk, tank_walk};
116
117 mframe_t tank_frames_walk [] =
118 {
119 ai_walk, 4, NULL,
120 ai_walk, 5, NULL,
121 ai_walk, 3, NULL,
122 ai_walk, 2, NULL,
123 ai_walk, 5, NULL,
124 ai_walk, 5, NULL,
125 ai_walk, 4, NULL,
126 ai_walk, 4, tank_footstep,
127 ai_walk, 3, NULL,
128 ai_walk, 5, NULL,
129 ai_walk, 4, NULL,
130 ai_walk, 5, NULL,
131 ai_walk, 7, NULL,
132 ai_walk, 7, NULL,
133 ai_walk, 6, NULL,
134 ai_walk, 6, tank_footstep
135 };
136 mmove_t tank_move_walk = {FRAME_walk05, FRAME_walk20, tank_frames_walk, NULL};
137
138 mframe_t tank_frames_stop_walk [] =
139 {
140 ai_walk, 3, NULL,
141 ai_walk, 3, NULL,
142 ai_walk, 2, NULL,
143 ai_walk, 2, NULL,
144 ai_walk, 4, tank_footstep
145 };
146 mmove_t tank_move_stop_walk = {FRAME_walk21, FRAME_walk25, tank_frames_stop_walk, tank_stand};
147
tank_walk(edict_t * self)148 void tank_walk (edict_t *self)
149 {
150 self->monsterinfo.currentmove = &tank_move_walk;
151 }
152
153
154 //
155 // run
156 //
157
158 void tank_run (edict_t *self);
159
160 mframe_t tank_frames_start_run [] =
161 {
162 ai_run, 0, NULL,
163 ai_run, 6, NULL,
164 ai_run, 6, NULL,
165 ai_run, 11, tank_footstep
166 };
167 mmove_t tank_move_start_run = {FRAME_walk01, FRAME_walk04, tank_frames_start_run, tank_run};
168
169 mframe_t tank_frames_run [] =
170 {
171 ai_run, 4, NULL,
172 ai_run, 5, NULL,
173 ai_run, 3, NULL,
174 ai_run, 2, NULL,
175 ai_run, 5, NULL,
176 ai_run, 5, NULL,
177 ai_run, 4, NULL,
178 ai_run, 4, tank_footstep,
179 ai_run, 3, NULL,
180 ai_run, 5, NULL,
181 ai_run, 4, NULL,
182 ai_run, 5, NULL,
183 ai_run, 7, NULL,
184 ai_run, 7, NULL,
185 ai_run, 6, NULL,
186 ai_run, 6, tank_footstep
187 };
188 mmove_t tank_move_run = {FRAME_walk05, FRAME_walk20, tank_frames_run, NULL};
189
190 mframe_t tank_frames_stop_run [] =
191 {
192 ai_run, 3, NULL,
193 ai_run, 3, NULL,
194 ai_run, 2, NULL,
195 ai_run, 2, NULL,
196 ai_run, 4, tank_footstep
197 };
198 mmove_t tank_move_stop_run = {FRAME_walk21, FRAME_walk25, tank_frames_stop_run, tank_walk};
199
tank_run(edict_t * self)200 void tank_run (edict_t *self)
201 {
202 if (self->enemy && self->enemy->client)
203 self->monsterinfo.aiflags |= AI_BRUTAL;
204 else
205 self->monsterinfo.aiflags &= ~AI_BRUTAL;
206
207 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
208 {
209 self->monsterinfo.currentmove = &tank_move_stand;
210 return;
211 }
212
213 if (self->monsterinfo.currentmove == &tank_move_walk ||
214 self->monsterinfo.currentmove == &tank_move_start_run)
215 {
216 self->monsterinfo.currentmove = &tank_move_run;
217 }
218 else
219 {
220 self->monsterinfo.currentmove = &tank_move_start_run;
221 }
222 }
223
224 //
225 // pain
226 //
227
228 mframe_t tank_frames_pain1 [] =
229 {
230 ai_move, 0, NULL,
231 ai_move, 0, NULL,
232 ai_move, 0, NULL,
233 ai_move, 0, NULL
234 };
235 mmove_t tank_move_pain1 = {FRAME_pain101, FRAME_pain104, tank_frames_pain1, tank_run};
236
237 mframe_t tank_frames_pain2 [] =
238 {
239 ai_move, 0, NULL,
240 ai_move, 0, NULL,
241 ai_move, 0, NULL,
242 ai_move, 0, NULL,
243 ai_move, 0, NULL
244 };
245 mmove_t tank_move_pain2 = {FRAME_pain201, FRAME_pain205, tank_frames_pain2, tank_run};
246
247 mframe_t tank_frames_pain3 [] =
248 {
249 ai_move, -7, NULL,
250 ai_move, 0, NULL,
251 ai_move, 0, NULL,
252 ai_move, 0, NULL,
253 ai_move, 2, NULL,
254 ai_move, 0, NULL,
255 ai_move, 0, NULL,
256 ai_move, 3, NULL,
257 ai_move, 0, NULL,
258 ai_move, 2, NULL,
259 ai_move, 0, NULL,
260 ai_move, 0, NULL,
261 ai_move, 0, NULL,
262 ai_move, 0, NULL,
263 ai_move, 0, NULL,
264 ai_move, 0, tank_footstep
265 };
266 mmove_t tank_move_pain3 = {FRAME_pain301, FRAME_pain316, tank_frames_pain3, tank_run};
267
268
tank_pain(edict_t * self,edict_t * other,float kick,int damage)269 void tank_pain (edict_t *self, edict_t *other, float kick, int damage)
270 {
271 if (self->health < (self->max_health / 2))
272 self->s.skinnum |= 1;
273
274 if (damage <= 10)
275 return;
276
277 if (level.time < self->pain_debounce_time)
278 return;
279
280 if (damage <= 30)
281 if (random() > 0.2)
282 return;
283
284 // If hard or nightmare, don't go into pain while attacking
285 if ( skill->value >= 2)
286 {
287 if ( (self->s.frame >= FRAME_attak301) && (self->s.frame <= FRAME_attak330) )
288 return;
289 if ( (self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak116) )
290 return;
291 }
292
293 self->pain_debounce_time = level.time + 3;
294 gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
295
296 if (skill->value == 3)
297 return; // no pain anims in nightmare
298
299 // PMM - blindfire cleanup
300 self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
301 // pmm
302
303 if (damage <= 30)
304 self->monsterinfo.currentmove = &tank_move_pain1;
305 else if (damage <= 60)
306 self->monsterinfo.currentmove = &tank_move_pain2;
307 else
308 self->monsterinfo.currentmove = &tank_move_pain3;
309 };
310
311
312 //
313 // attacks
314 //
315
TankBlaster(edict_t * self)316 void TankBlaster (edict_t *self)
317 {
318 vec3_t forward, right;
319 vec3_t start;
320 vec3_t end;
321 vec3_t dir;
322 int flash_number;
323
324 if(!self->enemy || !self->enemy->inuse) //PGM
325 return; //PGM
326
327 if (self->s.frame == FRAME_attak110)
328 flash_number = MZ2_TANK_BLASTER_1;
329 else if (self->s.frame == FRAME_attak113)
330 flash_number = MZ2_TANK_BLASTER_2;
331 else // (self->s.frame == FRAME_attak116)
332 flash_number = MZ2_TANK_BLASTER_3;
333
334 AngleVectors (self->s.angles, forward, right, NULL);
335 G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
336
337 VectorCopy (self->enemy->s.origin, end);
338 end[2] += self->enemy->viewheight;
339 VectorSubtract (end, start, dir);
340
341 monster_fire_blaster (self, start, dir, 30, 800, flash_number, EF_BLASTER);
342 }
343
TankStrike(edict_t * self)344 void TankStrike (edict_t *self)
345 {
346 gi.sound (self, CHAN_WEAPON, sound_strike, 1, ATTN_NORM, 0);
347 }
348
TankRocket(edict_t * self)349 void TankRocket (edict_t *self)
350 {
351 vec3_t forward, right;
352 vec3_t start;
353 vec3_t dir;
354 vec3_t vec;
355 int flash_number;
356 trace_t trace; // PGM
357 int rocketSpeed; // PGM
358 // pmm - blindfire support
359 vec3_t target;
360 qboolean blindfire = false;
361
362 if(!self->enemy || !self->enemy->inuse) //PGM
363 return; //PGM
364
365 // pmm - blindfire check
366 if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
367 blindfire = true;
368 else
369 blindfire = false;
370
371 if (self->s.frame == FRAME_attak324)
372 flash_number = MZ2_TANK_ROCKET_1;
373 else if (self->s.frame == FRAME_attak327)
374 flash_number = MZ2_TANK_ROCKET_2;
375 else // (self->s.frame == FRAME_attak330)
376 flash_number = MZ2_TANK_ROCKET_3;
377
378 AngleVectors (self->s.angles, forward, right, NULL);
379 G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
380
381 rocketSpeed = 500 + (100 * skill->value); // PGM rock & roll.... :)
382
383 // PMM
384 if (blindfire)
385 VectorCopy (self->monsterinfo.blind_fire_target, target);
386 else
387 VectorCopy (self->enemy->s.origin, target);
388 // pmm
389
390 // VectorCopy (self->enemy->s.origin, vec);
391 // vec[2] += self->enemy->viewheight;
392 // VectorSubtract (vec, start, dir);
393
394 //PGM
395 // PMM - blindfire shooting
396 if (blindfire)
397 {
398 VectorCopy (target, vec);
399 VectorSubtract (vec, start, dir);
400 }
401 // pmm
402 // don't shoot at feet if they're above me.
403 else if(random() < 0.66 || (start[2] < self->enemy->absmin[2]))
404 {
405 // gi.dprintf("normal shot\n");
406 VectorCopy (self->enemy->s.origin, vec);
407 vec[2] += self->enemy->viewheight;
408 VectorSubtract (vec, start, dir);
409 }
410 else
411 {
412 // gi.dprintf("shooting at feet!\n");
413 VectorCopy (self->enemy->s.origin, vec);
414 vec[2] = self->enemy->absmin[2];
415 VectorSubtract (vec, start, dir);
416 }
417 //PGM
418
419 //======
420 //PMM - lead target (not when blindfiring)
421 // 20, 35, 50, 65 chance of leading
422 if((!blindfire) && ((random() < (0.2 + ((3 - skill->value) * 0.15)))))
423 {
424 float dist;
425 float time;
426
427 // gi.dprintf ("leading target\n");
428 dist = VectorLength (dir);
429 time = dist/rocketSpeed;
430 VectorMA(vec, time, self->enemy->velocity, vec);
431 VectorSubtract(vec, start, dir);
432 }
433 //PMM - lead target
434 //======
435
436 VectorNormalize (dir);
437
438 // gi.WriteByte (svc_temp_entity);
439 // gi.WriteByte (TE_DEBUGTRAIL);
440 // gi.WritePosition (start);
441 // gi.WritePosition (vec);
442 // gi.multicast (start, MULTICAST_ALL);
443
444 // pmm blindfire doesn't check target (done in checkattack)
445 // paranoia, make sure we're not shooting a target right next to us
446 trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT);
447 if (blindfire)
448 {
449 // blindfire has different fail criteria for the trace
450 if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5)))
451 monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number);
452 else
453 {
454 // try shifting the target to the left a little (to help counter large offset)
455 VectorCopy (target, vec);
456 VectorMA (vec, -20, right, vec);
457 VectorSubtract(vec, start, dir);
458 VectorNormalize (dir);
459 trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT);
460 if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5)))
461 monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number);
462 else
463 {
464 // ok, that failed. try to the right
465 VectorCopy (target, vec);
466 VectorMA (vec, 20, right, vec);
467 VectorSubtract(vec, start, dir);
468 VectorNormalize (dir);
469 trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT);
470 if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5)))
471 monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number);
472 else if ((g_showlogic) && (g_showlogic->value))
473 // ok, I give up
474 gi.dprintf ("tank avoiding blindfire shot\n");
475 }
476 }
477 }
478 else
479 {
480 trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT);
481 if(trace.ent == self->enemy || trace.ent == world)
482 {
483 if(trace.fraction > 0.5 || (trace.ent && trace.ent->client))
484 monster_fire_rocket (self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
485 // else
486 // gi.dprintf("didn't make it halfway to target...aborting\n");
487 }
488 }
489 }
490
TankMachineGun(edict_t * self)491 void TankMachineGun (edict_t *self)
492 {
493 vec3_t dir;
494 vec3_t vec;
495 vec3_t start;
496 vec3_t forward, right;
497 int flash_number;
498
499 if(!self->enemy || !self->enemy->inuse) //PGM
500 return; //PGM
501
502 flash_number = MZ2_TANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak406);
503
504 AngleVectors (self->s.angles, forward, right, NULL);
505 G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
506
507 if (self->enemy)
508 {
509 VectorCopy (self->enemy->s.origin, vec);
510 vec[2] += self->enemy->viewheight;
511 VectorSubtract (vec, start, vec);
512 vectoangles (vec, vec);
513 dir[0] = vec[0];
514 }
515 else
516 {
517 dir[0] = 0;
518 }
519 if (self->s.frame <= FRAME_attak415)
520 dir[1] = self->s.angles[1] - 8 * (self->s.frame - FRAME_attak411);
521 else
522 dir[1] = self->s.angles[1] + 8 * (self->s.frame - FRAME_attak419);
523 dir[2] = 0;
524
525 AngleVectors (dir, forward, NULL, NULL);
526
527 monster_fire_bullet (self, start, forward, 20, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
528 }
529
530
531 mframe_t tank_frames_attack_blast [] =
532 {
533 ai_charge, 0, NULL,
534 ai_charge, 0, NULL,
535 ai_charge, 0, NULL,
536 ai_charge, 0, NULL,
537 ai_charge, -1, NULL,
538 ai_charge, -2, NULL,
539 ai_charge, -1, NULL,
540 ai_charge, -1, NULL,
541 ai_charge, 0, NULL,
542 ai_charge, 0, TankBlaster, // 10
543 ai_charge, 0, NULL,
544 ai_charge, 0, NULL,
545 ai_charge, 0, TankBlaster,
546 ai_charge, 0, NULL,
547 ai_charge, 0, NULL,
548 ai_charge, 0, TankBlaster // 16
549 };
550 mmove_t tank_move_attack_blast = {FRAME_attak101, FRAME_attak116, tank_frames_attack_blast, tank_reattack_blaster};
551
552 mframe_t tank_frames_reattack_blast [] =
553 {
554 ai_charge, 0, NULL,
555 ai_charge, 0, NULL,
556 ai_charge, 0, TankBlaster,
557 ai_charge, 0, NULL,
558 ai_charge, 0, NULL,
559 ai_charge, 0, TankBlaster // 16
560 };
561 mmove_t tank_move_reattack_blast = {FRAME_attak111, FRAME_attak116, tank_frames_reattack_blast, tank_reattack_blaster};
562
563 mframe_t tank_frames_attack_post_blast [] =
564 {
565 ai_move, 0, NULL, // 17
566 ai_move, 0, NULL,
567 ai_move, 2, NULL,
568 ai_move, 3, NULL,
569 ai_move, 2, NULL,
570 ai_move, -2, tank_footstep // 22
571 };
572 mmove_t tank_move_attack_post_blast = {FRAME_attak117, FRAME_attak122, tank_frames_attack_post_blast, tank_run};
573
tank_reattack_blaster(edict_t * self)574 void tank_reattack_blaster (edict_t *self)
575 {
576 if (skill->value >= 2)
577 if (visible (self, self->enemy))
578 if (self->enemy->health > 0)
579 if (random() <= 0.6)
580 {
581 self->monsterinfo.currentmove = &tank_move_reattack_blast;
582 return;
583 }
584 self->monsterinfo.currentmove = &tank_move_attack_post_blast;
585 }
586
587
tank_poststrike(edict_t * self)588 void tank_poststrike (edict_t *self)
589 {
590 self->enemy = NULL;
591 tank_run (self);
592 }
593
594 mframe_t tank_frames_attack_strike [] =
595 {
596 ai_move, 3, NULL,
597 ai_move, 2, NULL,
598 ai_move, 2, NULL,
599 ai_move, 1, NULL,
600 ai_move, 6, NULL,
601 ai_move, 7, NULL,
602 ai_move, 9, tank_footstep,
603 ai_move, 2, NULL,
604 ai_move, 1, NULL,
605 ai_move, 2, NULL,
606 ai_move, 2, tank_footstep,
607 ai_move, 2, NULL,
608 ai_move, 0, NULL,
609 ai_move, 0, NULL,
610 ai_move, 0, NULL,
611 ai_move, 0, NULL,
612 ai_move, -2, NULL,
613 ai_move, -2, NULL,
614 ai_move, 0, tank_windup,
615 ai_move, 0, NULL,
616 ai_move, 0, NULL,
617 ai_move, 0, NULL,
618 ai_move, 0, NULL,
619 ai_move, 0, NULL,
620 ai_move, 0, NULL,
621 ai_move, 0, TankStrike,
622 ai_move, 0, NULL,
623 ai_move, -1, NULL,
624 ai_move, -1, NULL,
625 ai_move, -1, NULL,
626 ai_move, -1, NULL,
627 ai_move, -1, NULL,
628 ai_move, -3, NULL,
629 ai_move, -10, NULL,
630 ai_move, -10, NULL,
631 ai_move, -2, NULL,
632 ai_move, -3, NULL,
633 ai_move, -2, tank_footstep
634 };
635 mmove_t tank_move_attack_strike = {FRAME_attak201, FRAME_attak238, tank_frames_attack_strike, tank_poststrike};
636
637 mframe_t tank_frames_attack_pre_rocket [] =
638 {
639 ai_charge, 0, NULL,
640 ai_charge, 0, NULL,
641 ai_charge, 0, NULL,
642 ai_charge, 0, NULL,
643 ai_charge, 0, NULL,
644 ai_charge, 0, NULL,
645 ai_charge, 0, NULL,
646 ai_charge, 0, NULL,
647 ai_charge, 0, NULL,
648 ai_charge, 0, NULL, // 10
649
650 ai_charge, 0, NULL,
651 ai_charge, 1, NULL,
652 ai_charge, 2, NULL,
653 ai_charge, 7, NULL,
654 ai_charge, 7, NULL,
655 ai_charge, 7, tank_footstep,
656 ai_charge, 0, NULL,
657 ai_charge, 0, NULL,
658 ai_charge, 0, NULL,
659 ai_charge, 0, NULL, // 20
660
661 ai_charge, -3, NULL
662 };
663 mmove_t tank_move_attack_pre_rocket = {FRAME_attak301, FRAME_attak321, tank_frames_attack_pre_rocket, tank_doattack_rocket};
664
665 mframe_t tank_frames_attack_fire_rocket [] =
666 {
667 ai_charge, -3, NULL, // Loop Start 22
668 ai_charge, 0, NULL,
669 ai_charge, 0, TankRocket, // 24
670 ai_charge, 0, NULL,
671 ai_charge, 0, NULL,
672 ai_charge, 0, TankRocket,
673 ai_charge, 0, NULL,
674 ai_charge, 0, NULL,
675 ai_charge, -1, TankRocket // 30 Loop End
676 };
677 mmove_t tank_move_attack_fire_rocket = {FRAME_attak322, FRAME_attak330, tank_frames_attack_fire_rocket, tank_refire_rocket};
678
679 mframe_t tank_frames_attack_post_rocket [] =
680 {
681 ai_charge, 0, NULL, // 31
682 ai_charge, -1, NULL,
683 ai_charge, -1, NULL,
684 ai_charge, 0, NULL,
685 ai_charge, 2, NULL,
686 ai_charge, 3, NULL,
687 ai_charge, 4, NULL,
688 ai_charge, 2, NULL,
689 ai_charge, 0, NULL,
690 ai_charge, 0, NULL, // 40
691
692 ai_charge, 0, NULL,
693 ai_charge, -9, NULL,
694 ai_charge, -8, NULL,
695 ai_charge, -7, NULL,
696 ai_charge, -1, NULL,
697 ai_charge, -1, tank_footstep,
698 ai_charge, 0, NULL,
699 ai_charge, 0, NULL,
700 ai_charge, 0, NULL,
701 ai_charge, 0, NULL, // 50
702
703 ai_charge, 0, NULL,
704 ai_charge, 0, NULL,
705 ai_charge, 0, NULL
706 };
707 mmove_t tank_move_attack_post_rocket = {FRAME_attak331, FRAME_attak353, tank_frames_attack_post_rocket, tank_run};
708
709 mframe_t tank_frames_attack_chain [] =
710 {
711 ai_charge, 0, NULL,
712 ai_charge, 0, NULL,
713 ai_charge, 0, NULL,
714 ai_charge, 0, NULL,
715 ai_charge, 0, NULL,
716 NULL, 0, TankMachineGun,
717 NULL, 0, TankMachineGun,
718 NULL, 0, TankMachineGun,
719 NULL, 0, TankMachineGun,
720 NULL, 0, TankMachineGun,
721 NULL, 0, TankMachineGun,
722 NULL, 0, TankMachineGun,
723 NULL, 0, TankMachineGun,
724 NULL, 0, TankMachineGun,
725 NULL, 0, TankMachineGun,
726 NULL, 0, TankMachineGun,
727 NULL, 0, TankMachineGun,
728 NULL, 0, TankMachineGun,
729 NULL, 0, TankMachineGun,
730 NULL, 0, TankMachineGun,
731 NULL, 0, TankMachineGun,
732 NULL, 0, TankMachineGun,
733 NULL, 0, TankMachineGun,
734 NULL, 0, TankMachineGun,
735 ai_charge, 0, NULL,
736 ai_charge, 0, NULL,
737 ai_charge, 0, NULL,
738 ai_charge, 0, NULL,
739 ai_charge, 0, NULL
740 };
741 mmove_t tank_move_attack_chain = {FRAME_attak401, FRAME_attak429, tank_frames_attack_chain, tank_run};
742
tank_refire_rocket(edict_t * self)743 void tank_refire_rocket (edict_t *self)
744 {
745 // PMM - blindfire cleanup
746 if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
747 {
748 self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
749 self->monsterinfo.currentmove = &tank_move_attack_post_rocket;
750 return;
751 }
752 // pmm
753
754 // Only on hard or nightmare
755 if ( skill->value >= 2 )
756 if (self->enemy->health > 0)
757 if (visible(self, self->enemy) )
758 if (random() <= 0.4)
759 {
760 self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
761 return;
762 }
763 self->monsterinfo.currentmove = &tank_move_attack_post_rocket;
764 }
765
tank_doattack_rocket(edict_t * self)766 void tank_doattack_rocket (edict_t *self)
767 {
768 self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
769 }
770
tank_attack(edict_t * self)771 void tank_attack(edict_t *self)
772 {
773 vec3_t vec;
774 float range;
775 float r;
776 // PMM
777 float chance;
778
779 // PMM
780 if (!self->enemy || !self->enemy->inuse)
781 return;
782
783 if (self->enemy->health < 0)
784 {
785 self->monsterinfo.currentmove = &tank_move_attack_strike;
786 self->monsterinfo.aiflags &= ~AI_BRUTAL;
787 return;
788 }
789
790 // PMM
791 if (self->monsterinfo.attack_state == AS_BLIND)
792 {
793 // setup shot probabilities
794 if (self->monsterinfo.blind_fire_delay < 1.0)
795 chance = 1.0;
796 else if (self->monsterinfo.blind_fire_delay < 7.5)
797 chance = 0.4;
798 else
799 chance = 0.1;
800
801 r = random();
802
803 self->monsterinfo.blind_fire_delay += 3.2 + 2.0 + random()*3.0;
804
805 // don't shoot at the origin
806 if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
807 return;
808
809 // don't shoot if the dice say not to
810 if (r > chance)
811 {
812 // if ((g_showlogic) && (g_showlogic->value))
813 // gi.dprintf ("blindfire - NO SHOT\n");
814 return;
815 }
816
817 // turn on manual steering to signal both manual steering and blindfire
818 self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
819 self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
820 self->monsterinfo.attack_finished = level.time + 3.0 + 2*random();
821 self->pain_debounce_time = level.time + 5.0; // no pain for a while
822 return;
823 }
824 // pmm
825
826 VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
827 range = VectorLength (vec);
828
829 r = random();
830
831 if (range <= 125)
832 {
833 if (r < 0.4)
834 self->monsterinfo.currentmove = &tank_move_attack_chain;
835 else
836 self->monsterinfo.currentmove = &tank_move_attack_blast;
837 }
838 else if (range <= 250)
839 {
840 if (r < 0.5)
841 self->monsterinfo.currentmove = &tank_move_attack_chain;
842 else
843 self->monsterinfo.currentmove = &tank_move_attack_blast;
844 }
845 else
846 {
847 if (r < 0.33)
848 self->monsterinfo.currentmove = &tank_move_attack_chain;
849 else if (r < 0.66)
850 {
851 self->monsterinfo.currentmove = &tank_move_attack_pre_rocket;
852 self->pain_debounce_time = level.time + 5.0; // no pain for a while
853 }
854 else
855 self->monsterinfo.currentmove = &tank_move_attack_blast;
856 }
857 }
858
859
860 //
861 // death
862 //
863
tank_dead(edict_t * self)864 void tank_dead (edict_t *self)
865 {
866 VectorSet (self->mins, -16, -16, -16);
867 VectorSet (self->maxs, 16, 16, -0);
868 self->movetype = MOVETYPE_TOSS;
869 self->svflags |= SVF_DEADMONSTER;
870 self->nextthink = 0;
871 gi.linkentity (self);
872 }
873
874 mframe_t tank_frames_death1 [] =
875 {
876 ai_move, -7, NULL,
877 ai_move, -2, NULL,
878 ai_move, -2, NULL,
879 ai_move, 1, NULL,
880 ai_move, 3, NULL,
881 ai_move, 6, NULL,
882 ai_move, 1, NULL,
883 ai_move, 1, NULL,
884 ai_move, 2, NULL,
885 ai_move, 0, NULL,
886 ai_move, 0, NULL,
887 ai_move, 0, NULL,
888 ai_move, -2, NULL,
889 ai_move, 0, NULL,
890 ai_move, 0, NULL,
891 ai_move, -3, NULL,
892 ai_move, 0, NULL,
893 ai_move, 0, NULL,
894 ai_move, 0, NULL,
895 ai_move, 0, NULL,
896 ai_move, 0, NULL,
897 ai_move, 0, NULL,
898 ai_move, -4, NULL,
899 ai_move, -6, NULL,
900 ai_move, -4, NULL,
901 ai_move, -5, NULL,
902 ai_move, -7, NULL,
903 ai_move, -15, tank_thud,
904 ai_move, -5, NULL,
905 ai_move, 0, NULL,
906 ai_move, 0, NULL,
907 ai_move, 0, NULL
908 };
909 mmove_t tank_move_death = {FRAME_death101, FRAME_death132, tank_frames_death1, tank_dead};
910
tank_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)911 void tank_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
912 {
913 int n;
914
915 // check for gib
916 if (self->health <= self->gib_health)
917 {
918 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
919 for (n= 0; n < 1 /*4*/; n++)
920 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
921 for (n= 0; n < 4; n++)
922 ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
923 ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
924 ThrowHead (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
925 self->deadflag = DEAD_DEAD;
926 return;
927 }
928
929 if (self->deadflag == DEAD_DEAD)
930 return;
931
932 // regular death
933 gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
934 self->deadflag = DEAD_DEAD;
935 self->takedamage = DAMAGE_YES;
936
937 self->monsterinfo.currentmove = &tank_move_death;
938
939 }
940
941 //===========
942 //PGM
tank_blocked(edict_t * self,float dist)943 qboolean tank_blocked (edict_t *self, float dist)
944 {
945 if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
946 return true;
947
948 if(blocked_checkplat (self, dist))
949 return true;
950
951 return false;
952 }
953 //PGM
954 //===========
955
956 //
957 // monster_tank
958 //
959
960 /*QUAKED monster_tank (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
961 */
962 /*QUAKED monster_tank_commander (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
963 */
SP_monster_tank(edict_t * self)964 void SP_monster_tank (edict_t *self)
965 {
966 if (deathmatch->value)
967 {
968 G_FreeEdict (self);
969 return;
970 }
971
972 self->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
973 VectorSet (self->mins, -32, -32, -16);
974 VectorSet (self->maxs, 32, 32, 72);
975 self->movetype = MOVETYPE_STEP;
976 self->solid = SOLID_BBOX;
977
978 sound_pain = gi.soundindex ("tank/tnkpain2.wav");
979 sound_thud = gi.soundindex ("tank/tnkdeth2.wav");
980 sound_idle = gi.soundindex ("tank/tnkidle1.wav");
981 sound_die = gi.soundindex ("tank/death.wav");
982 sound_step = gi.soundindex ("tank/step.wav");
983 sound_windup = gi.soundindex ("tank/tnkatck4.wav");
984 sound_strike = gi.soundindex ("tank/tnkatck5.wav");
985 sound_sight = gi.soundindex ("tank/sight1.wav");
986
987 gi.soundindex ("tank/tnkatck1.wav");
988 gi.soundindex ("tank/tnkatk2a.wav");
989 gi.soundindex ("tank/tnkatk2b.wav");
990 gi.soundindex ("tank/tnkatk2c.wav");
991 gi.soundindex ("tank/tnkatk2d.wav");
992 gi.soundindex ("tank/tnkatk2e.wav");
993 gi.soundindex ("tank/tnkatck3.wav");
994
995 if (strcmp(self->classname, "monster_tank_commander") == 0)
996 {
997 self->health = 1000;
998 self->gib_health = -225;
999 }
1000 else
1001 {
1002 self->health = 750;
1003 self->gib_health = -200;
1004 }
1005
1006 self->mass = 500;
1007
1008 self->pain = tank_pain;
1009 self->die = tank_die;
1010 self->monsterinfo.stand = tank_stand;
1011 self->monsterinfo.walk = tank_walk;
1012 self->monsterinfo.run = tank_run;
1013 self->monsterinfo.dodge = NULL;
1014 self->monsterinfo.attack = tank_attack;
1015 self->monsterinfo.melee = NULL;
1016 self->monsterinfo.sight = tank_sight;
1017 self->monsterinfo.idle = tank_idle;
1018 self->monsterinfo.blocked = tank_blocked; // PGM
1019
1020 gi.linkentity (self);
1021
1022 self->monsterinfo.currentmove = &tank_move_stand;
1023 self->monsterinfo.scale = MODEL_SCALE;
1024
1025 walkmonster_start(self);
1026
1027 // PMM
1028 self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
1029 self->monsterinfo.blindfire = true;
1030 //pmm
1031 if (strcmp(self->classname, "monster_tank_commander") == 0)
1032 self->s.skinnum = 2;
1033 }
1034