File: game\m_actor.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 // 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 }
  610