|
|
1.1 root 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:
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:
36: void tank_footstep (edict_t *self)
37: {
38: gi.sound (self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0);
39: }
40:
41: void tank_thud (edict_t *self)
42: {
43: gi.sound (self, CHAN_BODY, sound_thud, 1, ATTN_NORM, 0);
44: }
45:
46: void tank_windup (edict_t *self)
47: {
48: gi.sound (self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0);
49: }
50:
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:
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:
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:
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:
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:
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:
344: void TankStrike (edict_t *self)
345: {
346: gi.sound (self, CHAN_WEAPON, sound_strike, 1, ATTN_NORM, 0);
347: }
348:
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:
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:
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:
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:
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:
766: void tank_doattack_rocket (edict_t *self)
767: {
768: self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
769: }
770:
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: // gi.dprintf ("chance = %2.2f, roll = %2.2f\n", chance, r);
804:
805: self->monsterinfo.blind_fire_delay += 3.2 + 2.0 + random()*3.0;
806:
807: // don't shoot at the origin
808: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
809: return;
810:
811: // don't shoot if the dice say not to
812: if (r > chance)
813: {
814: // if ((g_showlogic) && (g_showlogic->value))
815: // gi.dprintf ("blindfire - NO SHOT\n");
816: return;
817: }
818:
819: // turn on manual steering to signal both manual steering and blindfire
820: self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
821: self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
822: self->monsterinfo.attack_finished = level.time + 3.0 + 2*random();
823: self->pain_debounce_time = level.time + 5.0; // no pain for a while
824: return;
825: }
826: // pmm
827:
828: VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
829: range = VectorLength (vec);
830:
831: r = random();
832:
833: if (range <= 125)
834: {
835: if (r < 0.4)
836: self->monsterinfo.currentmove = &tank_move_attack_chain;
837: else
838: self->monsterinfo.currentmove = &tank_move_attack_blast;
839: }
840: else if (range <= 250)
841: {
842: if (r < 0.5)
843: self->monsterinfo.currentmove = &tank_move_attack_chain;
844: else
845: self->monsterinfo.currentmove = &tank_move_attack_blast;
846: }
847: else
848: {
849: if (r < 0.33)
850: self->monsterinfo.currentmove = &tank_move_attack_chain;
851: else if (r < 0.66)
852: {
853: self->monsterinfo.currentmove = &tank_move_attack_pre_rocket;
854: self->pain_debounce_time = level.time + 5.0; // no pain for a while
855: }
856: else
857: self->monsterinfo.currentmove = &tank_move_attack_blast;
858: }
859: }
860:
861:
862: //
863: // death
864: //
865:
866: void tank_dead (edict_t *self)
867: {
868: VectorSet (self->mins, -16, -16, -16);
869: VectorSet (self->maxs, 16, 16, -0);
870: self->movetype = MOVETYPE_TOSS;
871: self->svflags |= SVF_DEADMONSTER;
872: self->nextthink = 0;
873: gi.linkentity (self);
874: }
875:
876: mframe_t tank_frames_death1 [] =
877: {
878: ai_move, -7, NULL,
879: ai_move, -2, NULL,
880: ai_move, -2, NULL,
881: ai_move, 1, NULL,
882: ai_move, 3, NULL,
883: ai_move, 6, NULL,
884: ai_move, 1, NULL,
885: ai_move, 1, NULL,
886: ai_move, 2, NULL,
887: ai_move, 0, NULL,
888: ai_move, 0, NULL,
889: ai_move, 0, NULL,
890: ai_move, -2, NULL,
891: ai_move, 0, NULL,
892: ai_move, 0, NULL,
893: ai_move, -3, NULL,
894: ai_move, 0, NULL,
895: ai_move, 0, NULL,
896: ai_move, 0, NULL,
897: ai_move, 0, NULL,
898: ai_move, 0, NULL,
899: ai_move, 0, NULL,
900: ai_move, -4, NULL,
901: ai_move, -6, NULL,
902: ai_move, -4, NULL,
903: ai_move, -5, NULL,
904: ai_move, -7, NULL,
905: ai_move, -15, tank_thud,
906: ai_move, -5, NULL,
907: ai_move, 0, NULL,
908: ai_move, 0, NULL,
909: ai_move, 0, NULL
910: };
911: mmove_t tank_move_death = {FRAME_death101, FRAME_death132, tank_frames_death1, tank_dead};
912:
913: void tank_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
914: {
915: int n;
916:
917: // check for gib
918: if (self->health <= self->gib_health)
919: {
920: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
921: for (n= 0; n < 1 /*4*/; n++)
922: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
923: for (n= 0; n < 4; n++)
924: ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
925: ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
926: ThrowHead (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
927: self->deadflag = DEAD_DEAD;
928: return;
929: }
930:
931: if (self->deadflag == DEAD_DEAD)
932: return;
933:
934: // regular death
935: gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
936: self->deadflag = DEAD_DEAD;
937: self->takedamage = DAMAGE_YES;
938:
939: self->monsterinfo.currentmove = &tank_move_death;
940:
941: }
942:
943: //===========
944: //PGM
945: qboolean tank_blocked (edict_t *self, float dist)
946: {
947: if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
948: return true;
949:
950: if(blocked_checkplat (self, dist))
951: return true;
952:
953: return false;
954: }
955: //PGM
956: //===========
957:
958: //
959: // monster_tank
960: //
961:
962: /*QUAKED monster_tank (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
963: */
964: /*QUAKED monster_tank_commander (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
965: */
966: void SP_monster_tank (edict_t *self)
967: {
968: if (deathmatch->value)
969: {
970: G_FreeEdict (self);
971: return;
972: }
973:
974: self->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
975: VectorSet (self->mins, -32, -32, -16);
976: VectorSet (self->maxs, 32, 32, 72);
977: self->movetype = MOVETYPE_STEP;
978: self->solid = SOLID_BBOX;
979:
980: sound_pain = gi.soundindex ("tank/tnkpain2.wav");
981: sound_thud = gi.soundindex ("tank/tnkdeth2.wav");
982: sound_idle = gi.soundindex ("tank/tnkidle1.wav");
983: sound_die = gi.soundindex ("tank/death.wav");
984: sound_step = gi.soundindex ("tank/step.wav");
985: sound_windup = gi.soundindex ("tank/tnkatck4.wav");
986: sound_strike = gi.soundindex ("tank/tnkatck5.wav");
987: sound_sight = gi.soundindex ("tank/sight1.wav");
988:
989: gi.soundindex ("tank/tnkatck1.wav");
990: gi.soundindex ("tank/tnkatk2a.wav");
991: gi.soundindex ("tank/tnkatk2b.wav");
992: gi.soundindex ("tank/tnkatk2c.wav");
993: gi.soundindex ("tank/tnkatk2d.wav");
994: gi.soundindex ("tank/tnkatk2e.wav");
995: gi.soundindex ("tank/tnkatck3.wav");
996:
997: if (strcmp(self->classname, "monster_tank_commander") == 0)
998: {
999: self->health = 1000;
1000: self->gib_health = -225;
1001: }
1002: else
1003: {
1004: self->health = 750;
1005: self->gib_health = -200;
1006: }
1007:
1008: self->mass = 500;
1009:
1010: self->pain = tank_pain;
1011: self->die = tank_die;
1012: self->monsterinfo.stand = tank_stand;
1013: self->monsterinfo.walk = tank_walk;
1014: self->monsterinfo.run = tank_run;
1015: self->monsterinfo.dodge = NULL;
1016: self->monsterinfo.attack = tank_attack;
1017: self->monsterinfo.melee = NULL;
1018: self->monsterinfo.sight = tank_sight;
1019: self->monsterinfo.idle = tank_idle;
1020: self->monsterinfo.blocked = tank_blocked; // PGM
1021:
1022: gi.linkentity (self);
1023:
1024: self->monsterinfo.currentmove = &tank_move_stand;
1025: self->monsterinfo.scale = MODEL_SCALE;
1026:
1027: walkmonster_start(self);
1028:
1029: // PMM
1030: self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
1031: self->monsterinfo.blindfire = true;
1032: //pmm
1033: if (strcmp(self->classname, "monster_tank_commander") == 0)
1034: self->s.skinnum = 2;
1035: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.