m_berserk.c (11972B)
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 }