File: game\m_tank.c

    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 TANK
   24 
   25 ==============================================================================
   26 */
   27 
   28 #include "g_local.h"
   29 #include "m_tank.h"
   30 
   31 
   32 void tank_refire_rocket (edict_t *self);
   33 void tank_doattack_rocket (edict_t *self);
   34 void tank_reattack_blaster (edict_t *self);
   35 
   36 static int      sound_thud;
   37 static int      sound_pain;
   38 static int      sound_idle;
   39 static int      sound_die;
   40 static int      sound_step;
   41 static int      sound_sight;
   42 static int      sound_windup;
   43 static int      sound_strike;
   44 
   45 //
   46 // misc
   47 //
   48 
   49 void tank_sight (edict_t *self, edict_t *other)
   50 {
   51         gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
   52 }
   53 
   54 
   55 void tank_footstep (edict_t *self)
   56 {
   57         gi.sound (self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0);
   58 }
   59 
   60 void tank_thud (edict_t *self)
   61 {
   62         gi.sound (self, CHAN_BODY, sound_thud, 1, ATTN_NORM, 0);
   63 }
   64 
   65 void tank_windup (edict_t *self)
   66 {
   67         gi.sound (self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0);
   68 }
   69 
   70 void tank_idle (edict_t *self)
   71 {
   72         gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
   73 }
   74 
   75 
   76 //
   77 // stand
   78 //
   79 
   80 mframe_t tank_frames_stand []=
   81 {
   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         ai_stand, 0, NULL,
   94         ai_stand, 0, NULL,
   95         ai_stand, 0, NULL,
   96         ai_stand, 0, NULL,
   97         ai_stand, 0, NULL,
   98         ai_stand, 0, NULL,
   99         ai_stand, 0, NULL,
  100         ai_stand, 0, NULL,
  101         ai_stand, 0, NULL,
  102         ai_stand, 0, NULL,
  103         ai_stand, 0, NULL,
  104         ai_stand, 0, NULL,
  105         ai_stand, 0, NULL,
  106         ai_stand, 0, NULL,
  107         ai_stand, 0, NULL,
  108         ai_stand, 0, NULL,
  109         ai_stand, 0, NULL,
  110         ai_stand, 0, NULL,
  111         ai_stand, 0, NULL
  112 };
  113 mmove_t tank_move_stand = {FRAME_stand01, FRAME_stand30, tank_frames_stand, NULL};
  114         
  115 void tank_stand (edict_t *self)
  116 {
  117         self->monsterinfo.currentmove = &tank_move_stand;
  118 }
  119 
  120 
  121 //
  122 // walk
  123 //
  124 
  125 void tank_walk (edict_t *self);
  126 
  127 mframe_t tank_frames_start_walk [] =
  128 {
  129         ai_walk,  0, NULL,
  130         ai_walk,  6, NULL,
  131         ai_walk,  6, NULL,
  132         ai_walk, 11, tank_footstep
  133 };
  134 mmove_t tank_move_start_walk = {FRAME_walk01, FRAME_walk04, tank_frames_start_walk, tank_walk};
  135 
  136 mframe_t tank_frames_walk [] =
  137 {
  138         ai_walk, 4,     NULL,
  139         ai_walk, 5,     NULL,
  140         ai_walk, 3,     NULL,
  141         ai_walk, 2,     NULL,
  142         ai_walk, 5,     NULL,
  143         ai_walk, 5,     NULL,
  144         ai_walk, 4,     NULL,
  145         ai_walk, 4,     tank_footstep,
  146         ai_walk, 3,     NULL,
  147         ai_walk, 5,     NULL,
  148         ai_walk, 4,     NULL,
  149         ai_walk, 5,     NULL,
  150         ai_walk, 7,     NULL,
  151         ai_walk, 7,     NULL,
  152         ai_walk, 6,     NULL,
  153         ai_walk, 6,     tank_footstep
  154 };
  155 mmove_t tank_move_walk = {FRAME_walk05, FRAME_walk20, tank_frames_walk, NULL};
  156 
  157 mframe_t tank_frames_stop_walk [] =
  158 {
  159         ai_walk,  3, NULL,
  160         ai_walk,  3, NULL,
  161         ai_walk,  2, NULL,
  162         ai_walk,  2, NULL,
  163         ai_walk,  4, tank_footstep
  164 };
  165 mmove_t tank_move_stop_walk = {FRAME_walk21, FRAME_walk25, tank_frames_stop_walk, tank_stand};
  166 
  167 void tank_walk (edict_t *self)
  168 {
  169                 self->monsterinfo.currentmove = &tank_move_walk;
  170 }
  171 
  172 
  173 //
  174 // run
  175 //
  176 
  177 void tank_run (edict_t *self);
  178 
  179 mframe_t tank_frames_start_run [] =
  180 {
  181         ai_run,  0, NULL,
  182         ai_run,  6, NULL,
  183         ai_run,  6, NULL,
  184         ai_run, 11, tank_footstep
  185 };
  186 mmove_t tank_move_start_run = {FRAME_walk01, FRAME_walk04, tank_frames_start_run, tank_run};
  187 
  188 mframe_t tank_frames_run [] =
  189 {
  190         ai_run, 4,      NULL,
  191         ai_run, 5,      NULL,
  192         ai_run, 3,      NULL,
  193         ai_run, 2,      NULL,
  194         ai_run, 5,      NULL,
  195         ai_run, 5,      NULL,
  196         ai_run, 4,      NULL,
  197         ai_run, 4,      tank_footstep,
  198         ai_run, 3,      NULL,
  199         ai_run, 5,      NULL,
  200         ai_run, 4,      NULL,
  201         ai_run, 5,      NULL,
  202         ai_run, 7,      NULL,
  203         ai_run, 7,      NULL,
  204         ai_run, 6,      NULL,
  205         ai_run, 6,      tank_footstep
  206 };
  207 mmove_t tank_move_run = {FRAME_walk05, FRAME_walk20, tank_frames_run, NULL};
  208 
  209 mframe_t tank_frames_stop_run [] =
  210 {
  211         ai_run,  3, NULL,
  212         ai_run,  3, NULL,
  213         ai_run,  2, NULL,
  214         ai_run,  2, NULL,
  215         ai_run,  4, tank_footstep
  216 };
  217 mmove_t tank_move_stop_run = {FRAME_walk21, FRAME_walk25, tank_frames_stop_run, tank_walk};
  218 
  219 void tank_run (edict_t *self)
  220 {
  221         if (self->enemy && self->enemy->client)
  222                 self->monsterinfo.aiflags |= AI_BRUTAL;
  223         else
  224                 self->monsterinfo.aiflags &= ~AI_BRUTAL;
  225 
  226         if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  227         {
  228                 self->monsterinfo.currentmove = &tank_move_stand;
  229                 return;
  230         }
  231 
  232         if (self->monsterinfo.currentmove == &tank_move_walk ||
  233                 self->monsterinfo.currentmove == &tank_move_start_run)
  234         {
  235                 self->monsterinfo.currentmove = &tank_move_run;
  236         }
  237         else
  238         {
  239                 self->monsterinfo.currentmove = &tank_move_start_run;
  240         }
  241 }
  242 
  243 //
  244 // pain
  245 //
  246 
  247 mframe_t tank_frames_pain1 [] =
  248 {
  249         ai_move, 0, NULL,
  250         ai_move, 0, NULL,
  251         ai_move, 0, NULL,
  252         ai_move, 0, NULL
  253 };
  254 mmove_t tank_move_pain1 = {FRAME_pain101, FRAME_pain104, tank_frames_pain1, tank_run};
  255 
  256 mframe_t tank_frames_pain2 [] =
  257 {
  258         ai_move, 0, NULL,
  259         ai_move, 0, NULL,
  260         ai_move, 0, NULL,
  261         ai_move, 0, NULL,
  262         ai_move, 0, NULL
  263 };
  264 mmove_t tank_move_pain2 = {FRAME_pain201, FRAME_pain205, tank_frames_pain2, tank_run};
  265 
  266 mframe_t tank_frames_pain3 [] =
  267 {
  268         ai_move, -7, NULL,
  269         ai_move, 0,  NULL,
  270         ai_move, 0,  NULL,
  271         ai_move, 0,  NULL,
  272         ai_move, 2,  NULL,
  273         ai_move, 0,  NULL,
  274         ai_move, 0,  NULL,
  275         ai_move, 3,  NULL,
  276         ai_move, 0,  NULL,
  277         ai_move, 2,  NULL,
  278         ai_move, 0,  NULL,
  279         ai_move, 0,  NULL,
  280         ai_move, 0,  NULL,
  281         ai_move, 0,  NULL,
  282         ai_move, 0,  NULL,
  283         ai_move, 0,  tank_footstep
  284 };
  285 mmove_t tank_move_pain3 = {FRAME_pain301, FRAME_pain316, tank_frames_pain3, tank_run};
  286 
  287 
  288 void tank_pain (edict_t *self, edict_t *other, float kick, int damage)
  289 {
  290         if (self->health < (self->max_health / 2))
  291                         self->s.skinnum |= 1;
  292 
  293         if (damage <= 10)
  294                 return;
  295 
  296         if (level.time < self->pain_debounce_time)
  297                         return;
  298 
  299         if (damage <= 30)
  300                 if (random() > 0.2)
  301                         return;
  302         
  303         // If hard or nightmare, don't go into pain while attacking
  304         if ( skill->value >= 2)
  305         {
  306                 if ( (self->s.frame >= FRAME_attak301) && (self->s.frame <= FRAME_attak330) )
  307                         return;
  308                 if ( (self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak116) )
  309                         return;
  310         }
  311 
  312         self->pain_debounce_time = level.time + 3;
  313         gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
  314 
  315         if (skill->value == 3)
  316                 return;         // no pain anims in nightmare
  317 
  318         if (damage <= 30)
  319                 self->monsterinfo.currentmove = &tank_move_pain1;
  320         else if (damage <= 60)
  321                 self->monsterinfo.currentmove = &tank_move_pain2;
  322         else
  323                 self->monsterinfo.currentmove = &tank_move_pain3;
  324 };
  325 
  326 
  327 //
  328 // attacks
  329 //
  330 
  331 void TankBlaster (edict_t *self)
  332 {
  333         vec3_t  forward, right;
  334         vec3_t  start;
  335         vec3_t  end;
  336         vec3_t  dir;
  337         int             flash_number;
  338 
  339         if (self->s.frame == FRAME_attak110)
  340                 flash_number = MZ2_TANK_BLASTER_1;
  341         else if (self->s.frame == FRAME_attak113)
  342                 flash_number = MZ2_TANK_BLASTER_2;
  343         else // (self->s.frame == FRAME_attak116)
  344                 flash_number = MZ2_TANK_BLASTER_3;
  345 
  346         AngleVectors (self->s.angles, forward, right, NULL);
  347         G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
  348 
  349         VectorCopy (self->enemy->s.origin, end);
  350         end[2] += self->enemy->viewheight;
  351         VectorSubtract (end, start, dir);
  352 
  353         monster_fire_blaster (self, start, dir, 30, 800, flash_number, EF_BLASTER);
  354 }       
  355 
  356 void TankStrike (edict_t *self)
  357 {
  358         gi.sound (self, CHAN_WEAPON, sound_strike, 1, ATTN_NORM, 0);
  359 }       
  360 
  361 void TankRocket (edict_t *self)
  362 {
  363         vec3_t  forward, right;
  364         vec3_t  start;
  365         vec3_t  dir;
  366         vec3_t  vec;
  367         int             flash_number;
  368 
  369         if (self->s.frame == FRAME_attak324)
  370                 flash_number = MZ2_TANK_ROCKET_1;
  371         else if (self->s.frame == FRAME_attak327)
  372                 flash_number = MZ2_TANK_ROCKET_2;
  373         else // (self->s.frame == FRAME_attak330)
  374                 flash_number = MZ2_TANK_ROCKET_3;
  375 
  376         AngleVectors (self->s.angles, forward, right, NULL);
  377         G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
  378 
  379         VectorCopy (self->enemy->s.origin, vec);
  380         vec[2] += self->enemy->viewheight;
  381         VectorSubtract (vec, start, dir);
  382         VectorNormalize (dir);
  383 
  384         monster_fire_rocket (self, start, dir, 50, 550, flash_number);
  385 }       
  386 
  387 void TankMachineGun (edict_t *self)
  388 {
  389         vec3_t  dir;
  390         vec3_t  vec;
  391         vec3_t  start;
  392         vec3_t  forward, right;
  393         int             flash_number;
  394 
  395         flash_number = MZ2_TANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak406);
  396 
  397         AngleVectors (self->s.angles, forward, right, NULL);
  398         G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
  399 
  400         if (self->enemy)
  401         {
  402                 VectorCopy (self->enemy->s.origin, vec);
  403                 vec[2] += self->enemy->viewheight;
  404                 VectorSubtract (vec, start, vec);
  405                 vectoangles (vec, vec);
  406                 dir[0] = vec[0];
  407         }
  408         else
  409         {
  410                 dir[0] = 0;
  411         }
  412         if (self->s.frame <= FRAME_attak415)
  413                 dir[1] = self->s.angles[1] - 8 * (self->s.frame - FRAME_attak411);
  414         else
  415                 dir[1] = self->s.angles[1] + 8 * (self->s.frame - FRAME_attak419);
  416         dir[2] = 0;
  417 
  418         AngleVectors (dir, forward, NULL, NULL);
  419 
  420         monster_fire_bullet (self, start, forward, 20, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
  421 }       
  422 
  423 
  424 mframe_t tank_frames_attack_blast [] =
  425 {
  426         ai_charge, 0,   NULL,
  427         ai_charge, 0,   NULL,
  428         ai_charge, 0,   NULL,
  429         ai_charge, 0,   NULL,
  430         ai_charge, -1,  NULL,
  431         ai_charge, -2,  NULL,
  432         ai_charge, -1,  NULL,
  433         ai_charge, -1,  NULL,
  434         ai_charge, 0,   NULL,
  435         ai_charge, 0,   TankBlaster,            // 10
  436         ai_charge, 0,   NULL,
  437         ai_charge, 0,   NULL,
  438         ai_charge, 0,   TankBlaster,
  439         ai_charge, 0,   NULL,
  440         ai_charge, 0,   NULL,
  441         ai_charge, 0,   TankBlaster                     // 16
  442 };
  443 mmove_t tank_move_attack_blast = {FRAME_attak101, FRAME_attak116, tank_frames_attack_blast, tank_reattack_blaster};
  444 
  445 mframe_t tank_frames_reattack_blast [] =
  446 {
  447         ai_charge, 0,   NULL,
  448         ai_charge, 0,   NULL,
  449         ai_charge, 0,   TankBlaster,
  450         ai_charge, 0,   NULL,
  451         ai_charge, 0,   NULL,
  452         ai_charge, 0,   TankBlaster                     // 16
  453 };
  454 mmove_t tank_move_reattack_blast = {FRAME_attak111, FRAME_attak116, tank_frames_reattack_blast, tank_reattack_blaster};
  455 
  456 mframe_t tank_frames_attack_post_blast [] =     
  457 {
  458         ai_move, 0,             NULL,                           // 17
  459         ai_move, 0,             NULL,
  460         ai_move, 2,             NULL,
  461         ai_move, 3,             NULL,
  462         ai_move, 2,             NULL,
  463         ai_move, -2,    tank_footstep           // 22
  464 };
  465 mmove_t tank_move_attack_post_blast = {FRAME_attak117, FRAME_attak122, tank_frames_attack_post_blast, tank_run};
  466 
  467 void tank_reattack_blaster (edict_t *self)
  468 {
  469         if (skill->value >= 2)
  470                 if (visible (self, self->enemy))
  471                         if (self->enemy->health > 0)
  472                                 if (random() <= 0.6)
  473                                 {
  474                                         self->monsterinfo.currentmove = &tank_move_reattack_blast;
  475                                         return;
  476                                 }
  477         self->monsterinfo.currentmove = &tank_move_attack_post_blast;
  478 }
  479 
  480 
  481 void tank_poststrike (edict_t *self)
  482 {
  483         self->enemy = NULL;
  484         tank_run (self);
  485 }
  486 
  487 mframe_t tank_frames_attack_strike [] =
  488 {
  489         ai_move, 3,   NULL,
  490         ai_move, 2,   NULL,
  491         ai_move, 2,   NULL,
  492         ai_move, 1,   NULL,
  493         ai_move, 6,   NULL,
  494         ai_move, 7,   NULL,
  495         ai_move, 9,   tank_footstep,
  496         ai_move, 2,   NULL,
  497         ai_move, 1,   NULL,
  498         ai_move, 2,   NULL,
  499         ai_move, 2,   tank_footstep,
  500         ai_move, 2,   NULL,
  501         ai_move, 0,   NULL,
  502         ai_move, 0,   NULL,
  503         ai_move, 0,   NULL,
  504         ai_move, 0,   NULL,
  505         ai_move, -2,  NULL,
  506         ai_move, -2,  NULL,
  507         ai_move, 0,   tank_windup,
  508         ai_move, 0,   NULL,
  509         ai_move, 0,   NULL,
  510         ai_move, 0,   NULL,
  511         ai_move, 0,   NULL,
  512         ai_move, 0,   NULL,
  513         ai_move, 0,   NULL,
  514         ai_move, 0,   TankStrike,
  515         ai_move, 0,   NULL,
  516         ai_move, -1,  NULL,
  517         ai_move, -1,  NULL,
  518         ai_move, -1,  NULL,
  519         ai_move, -1,  NULL,
  520         ai_move, -1,  NULL,
  521         ai_move, -3,  NULL,
  522         ai_move, -10, NULL,
  523         ai_move, -10, NULL,
  524         ai_move, -2,  NULL,
  525         ai_move, -3,  NULL,
  526         ai_move, -2,  tank_footstep
  527 };
  528 mmove_t tank_move_attack_strike = {FRAME_attak201, FRAME_attak238, tank_frames_attack_strike, tank_poststrike};
  529 
  530 mframe_t tank_frames_attack_pre_rocket [] =
  531 {
  532         ai_charge, 0,  NULL,
  533         ai_charge, 0,  NULL,
  534         ai_charge, 0,  NULL,
  535         ai_charge, 0,  NULL,
  536         ai_charge, 0,  NULL,
  537         ai_charge, 0,  NULL,
  538         ai_charge, 0,  NULL,
  539         ai_charge, 0,  NULL,
  540         ai_charge, 0,  NULL,
  541         ai_charge, 0,  NULL,                    // 10
  542 
  543         ai_charge, 0,  NULL,
  544         ai_charge, 1,  NULL,
  545         ai_charge, 2,  NULL,
  546         ai_charge, 7,  NULL,
  547         ai_charge, 7,  NULL,
  548         ai_charge, 7,  tank_footstep,
  549         ai_charge, 0,  NULL,
  550         ai_charge, 0,  NULL,
  551         ai_charge, 0,  NULL,
  552         ai_charge, 0,  NULL,                    // 20
  553 
  554         ai_charge, -3, NULL
  555 };
  556 mmove_t tank_move_attack_pre_rocket = {FRAME_attak301, FRAME_attak321, tank_frames_attack_pre_rocket, tank_doattack_rocket};
  557 
  558 mframe_t tank_frames_attack_fire_rocket [] =
  559 {
  560         ai_charge, -3, NULL,                    // Loop Start   22 
  561         ai_charge, 0,  NULL,
  562         ai_charge, 0,  TankRocket,              // 24
  563         ai_charge, 0,  NULL,
  564         ai_charge, 0,  NULL,
  565         ai_charge, 0,  TankRocket,
  566         ai_charge, 0,  NULL,
  567         ai_charge, 0,  NULL,
  568         ai_charge, -1, TankRocket               // 30   Loop End
  569 };
  570 mmove_t tank_move_attack_fire_rocket = {FRAME_attak322, FRAME_attak330, tank_frames_attack_fire_rocket, tank_refire_rocket};
  571 
  572 mframe_t tank_frames_attack_post_rocket [] =
  573 {       
  574         ai_charge, 0,  NULL,                    // 31
  575         ai_charge, -1, NULL,
  576         ai_charge, -1, NULL,
  577         ai_charge, 0,  NULL,
  578         ai_charge, 2,  NULL,
  579         ai_charge, 3,  NULL,
  580         ai_charge, 4,  NULL,
  581         ai_charge, 2,  NULL,
  582         ai_charge, 0,  NULL,
  583         ai_charge, 0,  NULL,                    // 40
  584 
  585         ai_charge, 0,  NULL,
  586         ai_charge, -9, NULL,
  587         ai_charge, -8, NULL,
  588         ai_charge, -7, NULL,
  589         ai_charge, -1, NULL,
  590         ai_charge, -1, tank_footstep,
  591         ai_charge, 0,  NULL,
  592         ai_charge, 0,  NULL,
  593         ai_charge, 0,  NULL,
  594         ai_charge, 0,  NULL,                    // 50
  595 
  596         ai_charge, 0,  NULL,
  597         ai_charge, 0,  NULL,
  598         ai_charge, 0,  NULL
  599 };
  600 mmove_t tank_move_attack_post_rocket = {FRAME_attak331, FRAME_attak353, tank_frames_attack_post_rocket, tank_run};
  601 
  602 mframe_t tank_frames_attack_chain [] =
  603 {
  604         ai_charge, 0, NULL,
  605         ai_charge, 0, NULL,
  606         ai_charge, 0, NULL,
  607         ai_charge, 0, NULL,
  608         ai_charge, 0, NULL,
  609         NULL,      0, TankMachineGun,
  610         NULL,      0, TankMachineGun,
  611         NULL,      0, TankMachineGun,
  612         NULL,      0, TankMachineGun,
  613         NULL,      0, TankMachineGun,
  614         NULL,      0, TankMachineGun,
  615         NULL,      0, TankMachineGun,
  616         NULL,      0, TankMachineGun,
  617         NULL,      0, TankMachineGun,
  618         NULL,      0, TankMachineGun,
  619         NULL,      0, TankMachineGun,
  620         NULL,      0, TankMachineGun,
  621         NULL,      0, TankMachineGun,
  622         NULL,      0, TankMachineGun,
  623         NULL,      0, TankMachineGun,
  624         NULL,      0, TankMachineGun,
  625         NULL,      0, TankMachineGun,
  626         NULL,      0, TankMachineGun,
  627         NULL,      0, TankMachineGun,
  628         ai_charge, 0, NULL,
  629         ai_charge, 0, NULL,
  630         ai_charge, 0, NULL,
  631         ai_charge, 0, NULL,
  632         ai_charge, 0, NULL
  633 };
  634 mmove_t tank_move_attack_chain = {FRAME_attak401, FRAME_attak429, tank_frames_attack_chain, tank_run};
  635 
  636 void tank_refire_rocket (edict_t *self)
  637 {
  638         // Only on hard or nightmare
  639         if ( skill->value >= 2 )
  640                 if (self->enemy->health > 0)
  641                         if (visible(self, self->enemy) )
  642                                 if (random() <= 0.4)
  643                                 {
  644                                         self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
  645                                         return;
  646                                 }
  647         self->monsterinfo.currentmove = &tank_move_attack_post_rocket;
  648 }
  649 
  650 void tank_doattack_rocket (edict_t *self)
  651 {
  652         self->monsterinfo.currentmove = &tank_move_attack_fire_rocket;
  653 }
  654 
  655 void tank_attack(edict_t *self)
  656 {
  657         vec3_t  vec;
  658         float   range;
  659         float   r;
  660 
  661         if (self->enemy->health < 0)
  662         {
  663                 self->monsterinfo.currentmove = &tank_move_attack_strike;
  664                 self->monsterinfo.aiflags &= ~AI_BRUTAL;
  665                 return;
  666         }
  667 
  668         VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
  669         range = VectorLength (vec);
  670 
  671         r = random();
  672 
  673         if (range <= 125)
  674         {
  675                 if (r < 0.4)
  676                         self->monsterinfo.currentmove = &tank_move_attack_chain;
  677                 else 
  678                         self->monsterinfo.currentmove = &tank_move_attack_blast;
  679         }
  680         else if (range <= 250)
  681         {
  682                 if (r < 0.5)
  683                         self->monsterinfo.currentmove = &tank_move_attack_chain;
  684                 else
  685                         self->monsterinfo.currentmove = &tank_move_attack_blast;
  686         }
  687         else
  688         {
  689                 if (r < 0.33)
  690                         self->monsterinfo.currentmove = &tank_move_attack_chain;
  691                 else if (r < 0.66)
  692                 {
  693                         self->monsterinfo.currentmove = &tank_move_attack_pre_rocket;
  694                         self->pain_debounce_time = level.time + 5.0;    // no pain for a while
  695                 }
  696                 else
  697                         self->monsterinfo.currentmove = &tank_move_attack_blast;
  698         }
  699 }
  700 
  701 
  702 //
  703 // death
  704 //
  705 
  706 void tank_dead (edict_t *self)
  707 {
  708         VectorSet (self->mins, -16, -16, -16);
  709         VectorSet (self->maxs, 16, 16, -0);
  710         self->movetype = MOVETYPE_TOSS;
  711         self->svflags |= SVF_DEADMONSTER;
  712         self->nextthink = 0;
  713         gi.linkentity (self);
  714 }
  715 
  716 mframe_t tank_frames_death1 [] =
  717 {
  718         ai_move, -7,  NULL,
  719         ai_move, -2,  NULL,
  720         ai_move, -2,  NULL,
  721         ai_move, 1,   NULL,
  722         ai_move, 3,   NULL,
  723         ai_move, 6,   NULL,
  724         ai_move, 1,   NULL,
  725         ai_move, 1,   NULL,
  726         ai_move, 2,   NULL,
  727         ai_move, 0,   NULL,
  728         ai_move, 0,   NULL,
  729         ai_move, 0,   NULL,
  730         ai_move, -2,  NULL,
  731         ai_move, 0,   NULL,
  732         ai_move, 0,   NULL,
  733         ai_move, -3,  NULL,
  734         ai_move, 0,   NULL,
  735         ai_move, 0,   NULL,
  736         ai_move, 0,   NULL,
  737         ai_move, 0,   NULL,
  738         ai_move, 0,   NULL,
  739         ai_move, 0,   NULL,
  740         ai_move, -4,  NULL,
  741         ai_move, -6,  NULL,
  742         ai_move, -4,  NULL,
  743         ai_move, -5,  NULL,
  744         ai_move, -7,  NULL,
  745         ai_move, -15, tank_thud,
  746         ai_move, -5,  NULL,
  747         ai_move, 0,   NULL,
  748         ai_move, 0,   NULL,
  749         ai_move, 0,   NULL
  750 };
  751 mmove_t tank_move_death = {FRAME_death101, FRAME_death132, tank_frames_death1, tank_dead};
  752 
  753 void tank_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  754 {
  755         int             n;
  756 
  757 // check for gib
  758         if (self->health <= self->gib_health)
  759         {
  760                 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  761                 for (n= 0; n < 1 /*4*/; n++)
  762                         ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  763                 for (n= 0; n < 4; n++)
  764                         ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
  765                 ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
  766                 ThrowHead (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
  767                 self->deadflag = DEAD_DEAD;
  768                 return;
  769         }
  770 
  771         if (self->deadflag == DEAD_DEAD)
  772                 return;
  773 
  774 // regular death
  775         gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
  776         self->deadflag = DEAD_DEAD;
  777         self->takedamage = DAMAGE_YES;
  778 
  779         self->monsterinfo.currentmove = &tank_move_death;
  780         
  781 }
  782 
  783 
  784 //
  785 // monster_tank
  786 //
  787 
  788 /*QUAKED monster_tank (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
  789 */
  790 /*QUAKED monster_tank_commander (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight
  791 */
  792 void SP_monster_tank (edict_t *self)
  793 {
  794         if (deathmatch->value)
  795         {
  796                 G_FreeEdict (self);
  797                 return;
  798         }
  799 
  800         self->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
  801         VectorSet (self->mins, -32, -32, -16);
  802         VectorSet (self->maxs, 32, 32, 72);
  803         self->movetype = MOVETYPE_STEP;
  804         self->solid = SOLID_BBOX;
  805 
  806         sound_pain = gi.soundindex ("tank/tnkpain2.wav");
  807         sound_thud = gi.soundindex ("tank/tnkdeth2.wav");
  808         sound_idle = gi.soundindex ("tank/tnkidle1.wav");
  809         sound_die = gi.soundindex ("tank/death.wav");
  810         sound_step = gi.soundindex ("tank/step.wav");
  811         sound_windup = gi.soundindex ("tank/tnkatck4.wav");
  812         sound_strike = gi.soundindex ("tank/tnkatck5.wav");
  813         sound_sight = gi.soundindex ("tank/sight1.wav");
  814 
  815         gi.soundindex ("tank/tnkatck1.wav");
  816         gi.soundindex ("tank/tnkatk2a.wav");
  817         gi.soundindex ("tank/tnkatk2b.wav");
  818         gi.soundindex ("tank/tnkatk2c.wav");
  819         gi.soundindex ("tank/tnkatk2d.wav");
  820         gi.soundindex ("tank/tnkatk2e.wav");
  821         gi.soundindex ("tank/tnkatck3.wav");
  822 
  823         if (strcmp(self->classname, "monster_tank_commander") == 0)
  824         {
  825                 self->health = 1000;
  826                 self->gib_health = -225;
  827         }
  828         else
  829         {
  830                 self->health = 750;
  831                 self->gib_health = -200;
  832         }
  833 
  834         self->mass = 500;
  835 
  836         self->pain = tank_pain;
  837         self->die = tank_die;
  838         self->monsterinfo.stand = tank_stand;
  839         self->monsterinfo.walk = tank_walk;
  840         self->monsterinfo.run = tank_run;
  841         self->monsterinfo.dodge = NULL;
  842         self->monsterinfo.attack = tank_attack;
  843         self->monsterinfo.melee = NULL;
  844         self->monsterinfo.sight = tank_sight;
  845         self->monsterinfo.idle = tank_idle;
  846 
  847         gi.linkentity (self);
  848         
  849         self->monsterinfo.currentmove = &tank_move_stand;
  850         self->monsterinfo.scale = MODEL_SCALE;
  851 
  852         walkmonster_start(self);
  853 
  854         if (strcmp(self->classname, "monster_tank_commander") == 0)
  855                 self->s.skinnum = 2;
  856 }
  857