File: game\m_berserk.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 BERSERK
   24 
   25 ==============================================================================
   26 */
   27 
   28 #include "g_local.h"
   29 #include "m_berserk.h"
   30 
   31 
   32 static int sound_pain;
   33 static int sound_die;
   34 static int sound_idle;
   35 static int sound_punch;
   36 static int sound_sight;
   37 static int sound_search;
   38 
   39 void berserk_sight (edict_t *self, edict_t *other)
   40 {
   41         gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
   42 }
   43 
   44 void berserk_search (edict_t *self)
   45 {
   46         gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
   47 }
   48 
   49 
   50 void berserk_fidget (edict_t *self);
   51 mframe_t berserk_frames_stand [] =
   52 {
   53         ai_stand, 0, berserk_fidget,
   54         ai_stand, 0, NULL,
   55         ai_stand, 0, NULL,
   56         ai_stand, 0, NULL,
   57         ai_stand, 0, NULL
   58 };
   59 mmove_t berserk_move_stand = {FRAME_stand1, FRAME_stand5, berserk_frames_stand, NULL};
   60 
   61 void berserk_stand (edict_t *self)
   62 {
   63         self->monsterinfo.currentmove = &berserk_move_stand;
   64 }
   65 
   66 mframe_t berserk_frames_stand_fidget [] =
   67 {
   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 };
   89 mmove_t berserk_move_stand_fidget = {FRAME_standb1, FRAME_standb20, berserk_frames_stand_fidget, berserk_stand};
   90 
   91 void berserk_fidget (edict_t *self)
   92 {
   93         if (self->monsterinfo.aiflags & AI_STAND_GROUND)
   94                 return;
   95         if (random() > 0.15)
   96                 return;
   97 
   98         self->monsterinfo.currentmove = &berserk_move_stand_fidget;
   99         gi.sound (self, CHAN_WEAPON, sound_idle, 1, ATTN_IDLE, 0);
  100 }
  101 
  102 
  103 mframe_t berserk_frames_walk [] =
  104 {
  105         ai_walk, 9.1, NULL,
  106         ai_walk, 6.3, NULL,
  107         ai_walk, 4.9, NULL,
  108         ai_walk, 6.7, NULL,
  109         ai_walk, 6.0, NULL,
  110         ai_walk, 8.2, NULL,
  111         ai_walk, 7.2, NULL,
  112         ai_walk, 6.1, NULL,
  113         ai_walk, 4.9, NULL,
  114         ai_walk, 4.7, NULL,
  115         ai_walk, 4.7, NULL,
  116         ai_walk, 4.8, NULL
  117 };
  118 mmove_t berserk_move_walk = {FRAME_walkc1, FRAME_walkc11, berserk_frames_walk, NULL};
  119 
  120 void berserk_walk (edict_t *self)
  121 {
  122         self->monsterinfo.currentmove = &berserk_move_walk;
  123 }
  124 
  125 /*
  126 
  127   *****************************
  128   SKIPPED THIS FOR NOW!
  129   *****************************
  130 
  131    Running -> Arm raised in air
  132 
  133 void()  berserk_runb1   =[      $r_att1 ,       berserk_runb2   ] {ai_run(21);};
  134 void()  berserk_runb2   =[      $r_att2 ,       berserk_runb3   ] {ai_run(11);};
  135 void()  berserk_runb3   =[      $r_att3 ,       berserk_runb4   ] {ai_run(21);};
  136 void()  berserk_runb4   =[      $r_att4 ,       berserk_runb5   ] {ai_run(25);};
  137 void()  berserk_runb5   =[      $r_att5 ,       berserk_runb6   ] {ai_run(18);};
  138 void()  berserk_runb6   =[      $r_att6 ,       berserk_runb7   ] {ai_run(19);};
  139 // running with arm in air : start loop
  140 void()  berserk_runb7   =[      $r_att7 ,       berserk_runb8   ] {ai_run(21);};
  141 void()  berserk_runb8   =[      $r_att8 ,       berserk_runb9   ] {ai_run(11);};
  142 void()  berserk_runb9   =[      $r_att9 ,       berserk_runb10  ] {ai_run(21);};
  143 void()  berserk_runb10  =[      $r_att10 ,      berserk_runb11  ] {ai_run(25);};
  144 void()  berserk_runb11  =[      $r_att11 ,      berserk_runb12  ] {ai_run(18);};
  145 void()  berserk_runb12  =[      $r_att12 ,      berserk_runb7   ] {ai_run(19);};
  146 // running with arm in air : end loop
  147 */
  148 
  149 
  150 mframe_t berserk_frames_run1 [] =
  151 {
  152         ai_run, 21, NULL,
  153         ai_run, 11, NULL,
  154         ai_run, 21, NULL,
  155         ai_run, 25, NULL,
  156         ai_run, 18, NULL,
  157         ai_run, 19, NULL
  158 };
  159 mmove_t berserk_move_run1 = {FRAME_run1, FRAME_run6, berserk_frames_run1, NULL};
  160 
  161 void berserk_run (edict_t *self)
  162 {
  163         if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  164                 self->monsterinfo.currentmove = &berserk_move_stand;
  165         else
  166                 self->monsterinfo.currentmove = &berserk_move_run1;
  167 }
  168 
  169 
  170 void berserk_attack_spike (edict_t *self)
  171 {
  172         static  vec3_t  aim = {MELEE_DISTANCE, 0, -24};
  173         fire_hit (self, aim, (15 + (rand() % 6)), 400);         //      Faster attack -- upwards and backwards
  174 }
  175 
  176 
  177 void berserk_swing (edict_t *self)
  178 {
  179         gi.sound (self, CHAN_WEAPON, sound_punch, 1, ATTN_NORM, 0);
  180 }
  181 
  182 mframe_t berserk_frames_attack_spike [] =
  183 {
  184                 ai_charge, 0, NULL,
  185                 ai_charge, 0, NULL,
  186                 ai_charge, 0, berserk_swing,
  187                 ai_charge, 0, berserk_attack_spike,
  188                 ai_charge, 0, NULL,
  189                 ai_charge, 0, NULL,
  190                 ai_charge, 0, NULL,
  191                 ai_charge, 0, NULL
  192 };
  193 mmove_t berserk_move_attack_spike = {FRAME_att_c1, FRAME_att_c8, berserk_frames_attack_spike, berserk_run};
  194 
  195 
  196 void berserk_attack_club (edict_t *self)
  197 {
  198         vec3_t  aim;
  199 
  200         VectorSet (aim, MELEE_DISTANCE, self->mins[0], -4);
  201         fire_hit (self, aim, (5 + (rand() % 6)), 400);          // Slower attack
  202 }
  203 
  204 mframe_t berserk_frames_attack_club [] =
  205 {       
  206         ai_charge, 0, NULL,
  207         ai_charge, 0, NULL,
  208         ai_charge, 0, NULL,
  209         ai_charge, 0, NULL,
  210         ai_charge, 0, berserk_swing,
  211         ai_charge, 0, NULL,
  212         ai_charge, 0, NULL,
  213         ai_charge, 0, NULL,
  214         ai_charge, 0, berserk_attack_club,
  215         ai_charge, 0, NULL,
  216         ai_charge, 0, NULL,
  217         ai_charge, 0, NULL
  218 };
  219 mmove_t berserk_move_attack_club = {FRAME_att_c9, FRAME_att_c20, berserk_frames_attack_club, berserk_run};
  220 
  221 
  222 void berserk_strike (edict_t *self)
  223 {
  224         //FIXME play impact sound
  225 }
  226 
  227 
  228 mframe_t berserk_frames_attack_strike [] =
  229 {
  230         ai_move, 0, NULL,
  231         ai_move, 0, NULL,
  232         ai_move, 0, NULL,
  233         ai_move, 0, berserk_swing,
  234         ai_move, 0, NULL,
  235         ai_move, 0, NULL,
  236         ai_move, 0, NULL,
  237         ai_move, 0, berserk_strike,
  238         ai_move, 0, NULL,
  239         ai_move, 0, NULL,
  240         ai_move, 0, NULL,
  241         ai_move, 0, NULL,
  242         ai_move, 9.7, NULL,
  243         ai_move, 13.6, NULL
  244 };
  245         
  246 mmove_t berserk_move_attack_strike = {FRAME_att_c21, FRAME_att_c34, berserk_frames_attack_strike, berserk_run};
  247 
  248 
  249 void berserk_melee (edict_t *self)
  250 {
  251         if ((rand() % 2) == 0)
  252                 self->monsterinfo.currentmove = &berserk_move_attack_spike;
  253         else
  254                 self->monsterinfo.currentmove = &berserk_move_attack_club;
  255 }
  256 
  257 
  258 /*
  259 void()  berserk_atke1   =[      $r_attb1,       berserk_atke2   ] {ai_run(9);};
  260 void()  berserk_atke2   =[      $r_attb2,       berserk_atke3   ] {ai_run(6);};
  261 void()  berserk_atke3   =[      $r_attb3,       berserk_atke4   ] {ai_run(18.4);};
  262 void()  berserk_atke4   =[      $r_attb4,       berserk_atke5   ] {ai_run(25);};
  263 void()  berserk_atke5   =[      $r_attb5,       berserk_atke6   ] {ai_run(14);};
  264 void()  berserk_atke6   =[      $r_attb6,       berserk_atke7   ] {ai_run(20);};
  265 void()  berserk_atke7   =[      $r_attb7,       berserk_atke8   ] {ai_run(8.5);};
  266 void()  berserk_atke8   =[      $r_attb8,       berserk_atke9   ] {ai_run(3);};
  267 void()  berserk_atke9   =[      $r_attb9,       berserk_atke10  ] {ai_run(17.5);};
  268 void()  berserk_atke10  =[      $r_attb10,      berserk_atke11  ] {ai_run(17);};
  269 void()  berserk_atke11  =[      $r_attb11,      berserk_atke12  ] {ai_run(9);};
  270 void()  berserk_atke12  =[      $r_attb12,      berserk_atke13  ] {ai_run(25);};
  271 void()  berserk_atke13  =[      $r_attb13,      berserk_atke14  ] {ai_run(3.7);};
  272 void()  berserk_atke14  =[      $r_attb14,      berserk_atke15  ] {ai_run(2.6);};
  273 void()  berserk_atke15  =[      $r_attb15,      berserk_atke16  ] {ai_run(19);};
  274 void()  berserk_atke16  =[      $r_attb16,      berserk_atke17  ] {ai_run(25);};
  275 void()  berserk_atke17  =[      $r_attb17,      berserk_atke18  ] {ai_run(19.6);};
  276 void()  berserk_atke18  =[      $r_attb18,      berserk_run1    ] {ai_run(7.8);};
  277 */
  278 
  279 
  280 mframe_t berserk_frames_pain1 [] =
  281 {
  282         ai_move, 0, NULL,
  283         ai_move, 0, NULL,
  284         ai_move, 0, NULL,
  285         ai_move, 0, NULL
  286 };
  287 mmove_t berserk_move_pain1 = {FRAME_painc1, FRAME_painc4, berserk_frames_pain1, berserk_run};
  288 
  289 
  290 mframe_t berserk_frames_pain2 [] =
  291 {
  292         ai_move, 0, NULL,
  293         ai_move, 0, NULL,
  294         ai_move, 0, NULL,
  295         ai_move, 0, NULL,
  296         ai_move, 0, NULL,
  297         ai_move, 0, NULL,
  298         ai_move, 0, NULL,
  299         ai_move, 0, NULL,
  300         ai_move, 0, NULL,
  301         ai_move, 0, NULL,
  302         ai_move, 0, NULL,
  303         ai_move, 0, NULL,
  304         ai_move, 0, NULL,
  305         ai_move, 0, NULL,
  306         ai_move, 0, NULL,
  307         ai_move, 0, NULL,
  308         ai_move, 0, NULL,
  309         ai_move, 0, NULL,
  310         ai_move, 0, NULL,
  311         ai_move, 0, NULL
  312 };
  313 mmove_t berserk_move_pain2 = {FRAME_painb1, FRAME_painb20, berserk_frames_pain2, berserk_run};
  314 
  315 void berserk_pain (edict_t *self, edict_t *other, float kick, int damage)
  316 {
  317         if (self->health < (self->max_health / 2))
  318                 self->s.skinnum = 1;
  319 
  320         if (level.time < self->pain_debounce_time)
  321                 return;
  322 
  323         self->pain_debounce_time = level.time + 3;
  324         gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
  325 
  326         if (skill->value == 3)
  327                 return;         // no pain anims in nightmare
  328 
  329         if ((damage < 20) || (random() < 0.5))
  330                 self->monsterinfo.currentmove = &berserk_move_pain1;
  331         else
  332                 self->monsterinfo.currentmove = &berserk_move_pain2;
  333 }
  334 
  335 
  336 void berserk_dead (edict_t *self)
  337 {
  338         VectorSet (self->mins, -16, -16, -24);
  339         VectorSet (self->maxs, 16, 16, -8);
  340         self->movetype = MOVETYPE_TOSS;
  341         self->svflags |= SVF_DEADMONSTER;
  342         self->nextthink = 0;
  343         gi.linkentity (self);
  344 }
  345 
  346 
  347 mframe_t berserk_frames_death1 [] =
  348 {
  349         ai_move, 0, NULL,
  350         ai_move, 0, NULL,
  351         ai_move, 0, NULL,
  352         ai_move, 0, NULL,
  353         ai_move, 0, NULL,
  354         ai_move, 0, NULL,
  355         ai_move, 0, NULL,
  356         ai_move, 0, NULL,
  357         ai_move, 0, NULL,
  358         ai_move, 0, NULL,
  359         ai_move, 0, NULL,
  360         ai_move, 0, NULL,
  361         ai_move, 0, NULL
  362         
  363 };
  364 mmove_t berserk_move_death1 = {FRAME_death1, FRAME_death13, berserk_frames_death1, berserk_dead};
  365 
  366 
  367 mframe_t berserk_frames_death2 [] =
  368 {
  369         ai_move, 0, NULL,
  370         ai_move, 0, NULL,
  371         ai_move, 0, NULL,
  372         ai_move, 0, NULL,
  373         ai_move, 0, NULL,
  374         ai_move, 0, NULL,
  375         ai_move, 0, NULL,
  376         ai_move, 0, NULL
  377 };
  378 mmove_t berserk_move_death2 = {FRAME_deathc1, FRAME_deathc8, berserk_frames_death2, berserk_dead};
  379 
  380 
  381 void berserk_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  382 {
  383         int             n;
  384 
  385         if (self->health <= self->gib_health)
  386         {
  387                 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  388                 for (n= 0; n < 2; n++)
  389                         ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
  390                 for (n= 0; n < 4; n++)
  391                         ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  392                 ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
  393                 self->deadflag = DEAD_DEAD;
  394                 return;
  395         }
  396 
  397         if (self->deadflag == DEAD_DEAD)
  398                 return;
  399 
  400         gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
  401         self->deadflag = DEAD_DEAD;
  402         self->takedamage = DAMAGE_YES;
  403 
  404         if (damage >= 50)
  405                 self->monsterinfo.currentmove = &berserk_move_death1;
  406         else
  407                 self->monsterinfo.currentmove = &berserk_move_death2;
  408 }
  409 
  410 
  411 /*QUAKED monster_berserk (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  412 */
  413 void SP_monster_berserk (edict_t *self)
  414 {
  415         if (deathmatch->value)
  416         {
  417                 G_FreeEdict (self);
  418                 return;
  419         }
  420 
  421         // pre-caches
  422         sound_pain  = gi.soundindex ("berserk/berpain2.wav");
  423         sound_die   = gi.soundindex ("berserk/berdeth2.wav");
  424         sound_idle  = gi.soundindex ("berserk/beridle1.wav");
  425         sound_punch = gi.soundindex ("berserk/attack.wav");
  426         sound_search = gi.soundindex ("berserk/bersrch1.wav");
  427         sound_sight = gi.soundindex ("berserk/sight.wav");
  428 
  429         self->s.modelindex = gi.modelindex("models/monsters/berserk/tris.md2");
  430         VectorSet (self->mins, -16, -16, -24);
  431         VectorSet (self->maxs, 16, 16, 32);
  432         self->movetype = MOVETYPE_STEP;
  433         self->solid = SOLID_BBOX;
  434 
  435         self->health = 240;
  436         self->gib_health = -60;
  437         self->mass = 250;
  438 
  439         self->pain = berserk_pain;
  440         self->die = berserk_die;
  441 
  442         self->monsterinfo.stand = berserk_stand;
  443         self->monsterinfo.walk = berserk_walk;
  444         self->monsterinfo.run = berserk_run;
  445         self->monsterinfo.dodge = NULL;
  446         self->monsterinfo.attack = NULL;
  447         self->monsterinfo.melee = berserk_melee;
  448         self->monsterinfo.sight = berserk_sight;
  449         self->monsterinfo.search = berserk_search;
  450 
  451         self->monsterinfo.currentmove = &berserk_move_stand;
  452         self->monsterinfo.scale = MODEL_SCALE;
  453 
  454         gi.linkentity (self);
  455 
  456         walkmonster_start (self);
  457 }
  458