m_actor.c (13963B)
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 // g_actor.c 21 22 #include "g_local.h" 23 #include "m_actor.h" 24 25 #define MAX_ACTOR_NAMES 8 26 char *actor_names[MAX_ACTOR_NAMES] = 27 { 28 "Hellrot", 29 "Tokay", 30 "Killme", 31 "Disruptor", 32 "Adrianator", 33 "Rambear", 34 "Titus", 35 "Bitterman" 36 }; 37 38 39 mframe_t actor_frames_stand [] = 40 { 41 ai_stand, 0, NULL, 42 ai_stand, 0, NULL, 43 ai_stand, 0, NULL, 44 ai_stand, 0, NULL, 45 ai_stand, 0, NULL, 46 ai_stand, 0, NULL, 47 ai_stand, 0, NULL, 48 ai_stand, 0, NULL, 49 ai_stand, 0, NULL, 50 ai_stand, 0, NULL, 51 52 ai_stand, 0, NULL, 53 ai_stand, 0, NULL, 54 ai_stand, 0, NULL, 55 ai_stand, 0, NULL, 56 ai_stand, 0, NULL, 57 ai_stand, 0, NULL, 58 ai_stand, 0, NULL, 59 ai_stand, 0, NULL, 60 ai_stand, 0, NULL, 61 ai_stand, 0, NULL, 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 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 }; 85 mmove_t actor_move_stand = {FRAME_stand101, FRAME_stand140, actor_frames_stand, NULL}; 86 87 void actor_stand (edict_t *self) 88 { 89 self->monsterinfo.currentmove = &actor_move_stand; 90 91 // randomize on startup 92 if (level.time < 1.0) 93 self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1)); 94 } 95 96 97 mframe_t actor_frames_walk [] = 98 { 99 ai_walk, 0, NULL, 100 ai_walk, 6, NULL, 101 ai_walk, 10, NULL, 102 ai_walk, 3, NULL, 103 ai_walk, 2, NULL, 104 ai_walk, 7, NULL, 105 ai_walk, 10, NULL, 106 ai_walk, 1, NULL, 107 ai_walk, 4, NULL, 108 ai_walk, 0, NULL, 109 ai_walk, 0, NULL 110 }; 111 mmove_t actor_move_walk = {FRAME_walk01, FRAME_walk08, actor_frames_walk, NULL}; 112 113 void actor_walk (edict_t *self) 114 { 115 self->monsterinfo.currentmove = &actor_move_walk; 116 } 117 118 119 mframe_t actor_frames_run [] = 120 { 121 ai_run, 4, NULL, 122 ai_run, 15, NULL, 123 ai_run, 15, NULL, 124 ai_run, 8, NULL, 125 ai_run, 20, NULL, 126 ai_run, 15, NULL, 127 ai_run, 8, NULL, 128 ai_run, 17, NULL, 129 ai_run, 12, NULL, 130 ai_run, -2, NULL, 131 ai_run, -2, NULL, 132 ai_run, -1, NULL 133 }; 134 mmove_t actor_move_run = {FRAME_run02, FRAME_run07, actor_frames_run, NULL}; 135 136 void actor_run (edict_t *self) 137 { 138 if ((level.time < self->pain_debounce_time) && (!self->enemy)) 139 { 140 if (self->movetarget) 141 actor_walk(self); 142 else 143 actor_stand(self); 144 return; 145 } 146 147 if (self->monsterinfo.aiflags & AI_STAND_GROUND) 148 { 149 actor_stand(self); 150 return; 151 } 152 153 self->monsterinfo.currentmove = &actor_move_run; 154 } 155 156 157 mframe_t actor_frames_pain1 [] = 158 { 159 ai_move, -5, NULL, 160 ai_move, 4, NULL, 161 ai_move, 1, NULL 162 }; 163 mmove_t actor_move_pain1 = {FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run}; 164 165 mframe_t actor_frames_pain2 [] = 166 { 167 ai_move, -4, NULL, 168 ai_move, 4, NULL, 169 ai_move, 0, NULL 170 }; 171 mmove_t actor_move_pain2 = {FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run}; 172 173 mframe_t actor_frames_pain3 [] = 174 { 175 ai_move, -1, NULL, 176 ai_move, 1, NULL, 177 ai_move, 0, NULL 178 }; 179 mmove_t actor_move_pain3 = {FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run}; 180 181 mframe_t actor_frames_flipoff [] = 182 { 183 ai_turn, 0, NULL, 184 ai_turn, 0, NULL, 185 ai_turn, 0, NULL, 186 ai_turn, 0, NULL, 187 ai_turn, 0, NULL, 188 ai_turn, 0, NULL, 189 ai_turn, 0, NULL, 190 ai_turn, 0, NULL, 191 ai_turn, 0, NULL, 192 ai_turn, 0, NULL, 193 ai_turn, 0, NULL, 194 ai_turn, 0, NULL, 195 ai_turn, 0, NULL, 196 ai_turn, 0, NULL 197 }; 198 mmove_t actor_move_flipoff = {FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run}; 199 200 mframe_t actor_frames_taunt [] = 201 { 202 ai_turn, 0, NULL, 203 ai_turn, 0, NULL, 204 ai_turn, 0, NULL, 205 ai_turn, 0, NULL, 206 ai_turn, 0, NULL, 207 ai_turn, 0, NULL, 208 ai_turn, 0, NULL, 209 ai_turn, 0, NULL, 210 ai_turn, 0, NULL, 211 ai_turn, 0, NULL, 212 ai_turn, 0, NULL, 213 ai_turn, 0, NULL, 214 ai_turn, 0, NULL, 215 ai_turn, 0, NULL, 216 ai_turn, 0, NULL, 217 ai_turn, 0, NULL, 218 ai_turn, 0, NULL 219 }; 220 mmove_t actor_move_taunt = {FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run}; 221 222 char *messages[] = 223 { 224 "Watch it", 225 "#$@*&", 226 "Idiot", 227 "Check your targets" 228 }; 229 230 void actor_pain (edict_t *self, edict_t *other, float kick, int damage) 231 { 232 int n; 233 234 if (self->health < (self->max_health / 2)) 235 self->s.skinnum = 1; 236 237 if (level.time < self->pain_debounce_time) 238 return; 239 240 self->pain_debounce_time = level.time + 3; 241 // gi.sound (self, CHAN_VOICE, actor.sound_pain, 1, ATTN_NORM, 0); 242 243 if ((other->client) && (random() < 0.4)) 244 { 245 vec3_t v; 246 char *name; 247 248 VectorSubtract (other->s.origin, self->s.origin, v); 249 self->ideal_yaw = vectoyaw (v); 250 if (random() < 0.5) 251 self->monsterinfo.currentmove = &actor_move_flipoff; 252 else 253 self->monsterinfo.currentmove = &actor_move_taunt; 254 name = actor_names[(self - g_edicts)%MAX_ACTOR_NAMES]; 255 gi.cprintf (other, PRINT_CHAT, "%s: %s!\n", name, messages[rand()%3]); 256 return; 257 } 258 259 n = rand() % 3; 260 if (n == 0) 261 self->monsterinfo.currentmove = &actor_move_pain1; 262 else if (n == 1) 263 self->monsterinfo.currentmove = &actor_move_pain2; 264 else 265 self->monsterinfo.currentmove = &actor_move_pain3; 266 } 267 268 269 void actorMachineGun (edict_t *self) 270 { 271 vec3_t start, target; 272 vec3_t forward, right; 273 274 AngleVectors (self->s.angles, forward, right, NULL); 275 G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right, start); 276 if (self->enemy) 277 { 278 if (self->enemy->health > 0) 279 { 280 VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target); 281 target[2] += self->enemy->viewheight; 282 } 283 else 284 { 285 VectorCopy (self->enemy->absmin, target); 286 target[2] += (self->enemy->size[2] / 2); 287 } 288 VectorSubtract (target, start, forward); 289 VectorNormalize (forward); 290 } 291 else 292 { 293 AngleVectors (self->s.angles, forward, NULL, NULL); 294 } 295 monster_fire_bullet (self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1); 296 } 297 298 299 void actor_dead (edict_t *self) 300 { 301 VectorSet (self->mins, -16, -16, -24); 302 VectorSet (self->maxs, 16, 16, -8); 303 self->movetype = MOVETYPE_TOSS; 304 self->svflags |= SVF_DEADMONSTER; 305 self->nextthink = 0; 306 gi.linkentity (self); 307 } 308 309 mframe_t actor_frames_death1 [] = 310 { 311 ai_move, 0, NULL, 312 ai_move, 0, NULL, 313 ai_move, -13, NULL, 314 ai_move, 14, NULL, 315 ai_move, 3, NULL, 316 ai_move, -2, NULL, 317 ai_move, 1, NULL 318 }; 319 mmove_t actor_move_death1 = {FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead}; 320 321 mframe_t actor_frames_death2 [] = 322 { 323 ai_move, 0, NULL, 324 ai_move, 7, NULL, 325 ai_move, -6, NULL, 326 ai_move, -5, NULL, 327 ai_move, 1, NULL, 328 ai_move, 0, NULL, 329 ai_move, -1, NULL, 330 ai_move, -2, NULL, 331 ai_move, -1, NULL, 332 ai_move, -9, NULL, 333 ai_move, -13, NULL, 334 ai_move, -13, NULL, 335 ai_move, 0, NULL 336 }; 337 mmove_t actor_move_death2 = {FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead}; 338 339 void actor_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) 340 { 341 int n; 342 343 // check for gib 344 if (self->health <= -80) 345 { 346 // gi.sound (self, CHAN_VOICE, actor.sound_gib, 1, ATTN_NORM, 0); 347 for (n= 0; n < 2; n++) 348 ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); 349 for (n= 0; n < 4; n++) 350 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); 351 ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); 352 self->deadflag = DEAD_DEAD; 353 return; 354 } 355 356 if (self->deadflag == DEAD_DEAD) 357 return; 358 359 // regular death 360 // gi.sound (self, CHAN_VOICE, actor.sound_die, 1, ATTN_NORM, 0); 361 self->deadflag = DEAD_DEAD; 362 self->takedamage = DAMAGE_YES; 363 364 n = rand() % 2; 365 if (n == 0) 366 self->monsterinfo.currentmove = &actor_move_death1; 367 else 368 self->monsterinfo.currentmove = &actor_move_death2; 369 } 370 371 372 void actor_fire (edict_t *self) 373 { 374 actorMachineGun (self); 375 376 if (level.time >= self->monsterinfo.pausetime) 377 self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; 378 else 379 self->monsterinfo.aiflags |= AI_HOLD_FRAME; 380 } 381 382 mframe_t actor_frames_attack [] = 383 { 384 ai_charge, -2, actor_fire, 385 ai_charge, -2, NULL, 386 ai_charge, 3, NULL, 387 ai_charge, 2, NULL 388 }; 389 mmove_t actor_move_attack = {FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run}; 390 391 void actor_attack(edict_t *self) 392 { 393 int n; 394 395 self->monsterinfo.currentmove = &actor_move_attack; 396 n = (rand() & 15) + 3 + 7; 397 self->monsterinfo.pausetime = level.time + n * FRAMETIME; 398 } 399 400 401 void actor_use (edict_t *self, edict_t *other, edict_t *activator) 402 { 403 vec3_t v; 404 405 self->goalentity = self->movetarget = G_PickTarget(self->target); 406 if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0)) 407 { 408 gi.dprintf ("%s has bad target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); 409 self->target = NULL; 410 self->monsterinfo.pausetime = 100000000; 411 self->monsterinfo.stand (self); 412 return; 413 } 414 415 VectorSubtract (self->goalentity->s.origin, self->s.origin, v); 416 self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); 417 self->monsterinfo.walk (self); 418 self->target = NULL; 419 } 420 421 422 /*QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32) 423 */ 424 425 void SP_misc_actor (edict_t *self) 426 { 427 if (deathmatch->value) 428 { 429 G_FreeEdict (self); 430 return; 431 } 432 433 if (!self->targetname) 434 { 435 gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin)); 436 G_FreeEdict (self); 437 return; 438 } 439 440 if (!self->target) 441 { 442 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); 443 G_FreeEdict (self); 444 return; 445 } 446 447 self->movetype = MOVETYPE_STEP; 448 self->solid = SOLID_BBOX; 449 self->s.modelindex = gi.modelindex("players/male/tris.md2"); 450 VectorSet (self->mins, -16, -16, -24); 451 VectorSet (self->maxs, 16, 16, 32); 452 453 if (!self->health) 454 self->health = 100; 455 self->mass = 200; 456 457 self->pain = actor_pain; 458 self->die = actor_die; 459 460 self->monsterinfo.stand = actor_stand; 461 self->monsterinfo.walk = actor_walk; 462 self->monsterinfo.run = actor_run; 463 self->monsterinfo.attack = actor_attack; 464 self->monsterinfo.melee = NULL; 465 self->monsterinfo.sight = NULL; 466 467 self->monsterinfo.aiflags |= AI_GOOD_GUY; 468 469 gi.linkentity (self); 470 471 self->monsterinfo.currentmove = &actor_move_stand; 472 self->monsterinfo.scale = MODEL_SCALE; 473 474 walkmonster_start (self); 475 476 // actors always start in a dormant state, they *must* be used to get going 477 self->use = actor_use; 478 } 479 480 481 /*QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD BRUTAL 482 JUMP jump in set direction upon reaching this target 483 SHOOT take a single shot at the pathtarget 484 ATTACK attack pathtarget until it or actor is dead 485 486 "target" next target_actor 487 "pathtarget" target of any action to be taken at this point 488 "wait" amount of time actor should pause at this point 489 "message" actor will "say" this to the player 490 491 for JUMP only: 492 "speed" speed thrown forward (default 200) 493 "height" speed thrown upwards (default 200) 494 */ 495 496 void target_actor_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 497 { 498 vec3_t v; 499 500 if (other->movetarget != self) 501 return; 502 503 if (other->enemy) 504 return; 505 506 other->goalentity = other->movetarget = NULL; 507 508 if (self->message) 509 { 510 int n; 511 edict_t *ent; 512 513 for (n = 1; n <= game.maxclients; n++) 514 { 515 ent = &g_edicts[n]; 516 if (!ent->inuse) 517 continue; 518 gi.cprintf (ent, PRINT_CHAT, "%s: %s\n", actor_names[(other - g_edicts)%MAX_ACTOR_NAMES], self->message); 519 } 520 } 521 522 if (self->spawnflags & 1) //jump 523 { 524 other->velocity[0] = self->movedir[0] * self->speed; 525 other->velocity[1] = self->movedir[1] * self->speed; 526 527 if (other->groundentity) 528 { 529 other->groundentity = NULL; 530 other->velocity[2] = self->movedir[2]; 531 gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0); 532 } 533 } 534 535 if (self->spawnflags & 2) //shoot 536 { 537 } 538 else if (self->spawnflags & 4) //attack 539 { 540 other->enemy = G_PickTarget(self->pathtarget); 541 if (other->enemy) 542 { 543 other->goalentity = other->enemy; 544 if (self->spawnflags & 32) 545 other->monsterinfo.aiflags |= AI_BRUTAL; 546 if (self->spawnflags & 16) 547 { 548 other->monsterinfo.aiflags |= AI_STAND_GROUND; 549 actor_stand (other); 550 } 551 else 552 { 553 actor_run (other); 554 } 555 } 556 } 557 558 if (!(self->spawnflags & 6) && (self->pathtarget)) 559 { 560 char *savetarget; 561 562 savetarget = self->target; 563 self->target = self->pathtarget; 564 G_UseTargets (self, other); 565 self->target = savetarget; 566 } 567 568 other->movetarget = G_PickTarget(self->target); 569 570 if (!other->goalentity) 571 other->goalentity = other->movetarget; 572 573 if (!other->movetarget && !other->enemy) 574 { 575 other->monsterinfo.pausetime = level.time + 100000000; 576 other->monsterinfo.stand (other); 577 } 578 else if (other->movetarget == other->goalentity) 579 { 580 VectorSubtract (other->movetarget->s.origin, other->s.origin, v); 581 other->ideal_yaw = vectoyaw (v); 582 } 583 } 584 585 void SP_target_actor (edict_t *self) 586 { 587 if (!self->targetname) 588 gi.dprintf ("%s with no targetname at %s\n", self->classname, vtos(self->s.origin)); 589 590 self->solid = SOLID_TRIGGER; 591 self->touch = target_actor_touch; 592 VectorSet (self->mins, -8, -8, -8); 593 VectorSet (self->maxs, 8, 8, 8); 594 self->svflags = SVF_NOCLIENT; 595 596 if (self->spawnflags & 1) 597 { 598 if (!self->speed) 599 self->speed = 200; 600 if (!st.height) 601 st.height = 200; 602 if (self->s.angles[YAW] == 0) 603 self->s.angles[YAW] = 360; 604 G_SetMovedir (self->s.angles, self->movedir); 605 self->movedir[2] = st.height; 606 } 607 608 gi.linkentity (self); 609 }