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