File: game\m_medic.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 MEDIC
   24 
   25 ==============================================================================
   26 */
   27 
   28 #include "g_local.h"
   29 #include "m_medic.h"
   30 
   31 qboolean visible (edict_t *self, edict_t *other);
   32 
   33 
   34 static int      sound_idle1;
   35 static int      sound_pain1;
   36 static int      sound_pain2;
   37 static int      sound_die;
   38 static int      sound_sight;
   39 static int      sound_search;
   40 static int      sound_hook_launch;
   41 static int      sound_hook_hit;
   42 static int      sound_hook_heal;
   43 static int      sound_hook_retract;
   44 
   45 
   46 edict_t *medic_FindDeadMonster (edict_t *self)
   47 {
   48         edict_t *ent = NULL;
   49         edict_t *best = NULL;
   50 
   51         while ((ent = findradius(ent, self->s.origin, 1024)) != NULL)
   52         {
   53                 if (ent == self)
   54                         continue;
   55                 if (!(ent->svflags & SVF_MONSTER))
   56                         continue;
   57                 if (ent->monsterinfo.aiflags & AI_GOOD_GUY)
   58                         continue;
   59                 if (ent->owner)
   60                         continue;
   61                 if (ent->health > 0)
   62                         continue;
   63                 if (ent->nextthink)
   64                         continue;
   65                 if (!visible(self, ent))
   66                         continue;
   67                 if (!best)
   68                 {
   69                         best = ent;
   70                         continue;
   71                 }
   72                 if (ent->max_health <= best->max_health)
   73                         continue;
   74                 best = ent;
   75         }
   76 
   77         return best;
   78 }
   79 
   80 void medic_idle (edict_t *self)
   81 {
   82         edict_t *ent;
   83 
   84         gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0);
   85 
   86         ent = medic_FindDeadMonster(self);
   87         if (ent)
   88         {
   89                 self->enemy = ent;
   90                 self->enemy->owner = self;
   91                 self->monsterinfo.aiflags |= AI_MEDIC;
   92                 FoundTarget (self);
   93         }
   94 }
   95 
   96 void medic_search (edict_t *self)
   97 {
   98         edict_t *ent;
   99 
  100         gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0);
  101 
  102         if (!self->oldenemy)
  103         {
  104                 ent = medic_FindDeadMonster(self);
  105                 if (ent)
  106                 {
  107                         self->oldenemy = self->enemy;
  108                         self->enemy = ent;
  109                         self->enemy->owner = self;
  110                         self->monsterinfo.aiflags |= AI_MEDIC;
  111                         FoundTarget (self);
  112                 }
  113         }
  114 }
  115 
  116 void medic_sight (edict_t *self, edict_t *other)
  117 {
  118         gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  119 }
  120 
  121 
  122 mframe_t medic_frames_stand [] =
  123 {
  124         ai_stand, 0, medic_idle,
  125         ai_stand, 0, NULL,
  126         ai_stand, 0, NULL,
  127         ai_stand, 0, NULL,
  128         ai_stand, 0, NULL,
  129         ai_stand, 0, NULL,
  130         ai_stand, 0, NULL,
  131         ai_stand, 0, NULL,
  132         ai_stand, 0, NULL,
  133         ai_stand, 0, NULL,
  134         ai_stand, 0, NULL,
  135         ai_stand, 0, NULL,
  136         ai_stand, 0, NULL,
  137         ai_stand, 0, NULL,
  138         ai_stand, 0, NULL,
  139         ai_stand, 0, NULL,
  140         ai_stand, 0, NULL,
  141         ai_stand, 0, NULL,
  142         ai_stand, 0, NULL,
  143         ai_stand, 0, NULL,
  144         ai_stand, 0, NULL,
  145         ai_stand, 0, NULL,
  146         ai_stand, 0, NULL,
  147         ai_stand, 0, NULL,
  148         ai_stand, 0, NULL,
  149         ai_stand, 0, NULL,
  150         ai_stand, 0, NULL,
  151         ai_stand, 0, NULL,
  152         ai_stand, 0, NULL,
  153         ai_stand, 0, NULL,
  154         ai_stand, 0, NULL,
  155         ai_stand, 0, NULL,
  156         ai_stand, 0, NULL,
  157         ai_stand, 0, NULL,
  158         ai_stand, 0, NULL,
  159         ai_stand, 0, NULL,
  160         ai_stand, 0, NULL,
  161         ai_stand, 0, NULL,
  162         ai_stand, 0, NULL,
  163         ai_stand, 0, NULL,
  164         ai_stand, 0, NULL,
  165         ai_stand, 0, NULL,
  166         ai_stand, 0, NULL,
  167         ai_stand, 0, NULL,
  168         ai_stand, 0, NULL,
  169         ai_stand, 0, NULL,
  170         ai_stand, 0, NULL,
  171         ai_stand, 0, NULL,
  172         ai_stand, 0, NULL,
  173         ai_stand, 0, NULL,
  174         ai_stand, 0, NULL,
  175         ai_stand, 0, NULL,
  176         ai_stand, 0, NULL,
  177         ai_stand, 0, NULL,
  178         ai_stand, 0, NULL,
  179         ai_stand, 0, NULL,
  180         ai_stand, 0, NULL,
  181         ai_stand, 0, NULL,
  182         ai_stand, 0, NULL,
  183         ai_stand, 0, NULL,
  184         ai_stand, 0, NULL,
  185         ai_stand, 0, NULL,
  186         ai_stand, 0, NULL,
  187         ai_stand, 0, NULL,
  188         ai_stand, 0, NULL,
  189         ai_stand, 0, NULL,
  190         ai_stand, 0, NULL,
  191         ai_stand, 0, NULL,
  192         ai_stand, 0, NULL,
  193         ai_stand, 0, NULL,
  194         ai_stand, 0, NULL,
  195         ai_stand, 0, NULL,
  196         ai_stand, 0, NULL,
  197         ai_stand, 0, NULL,
  198         ai_stand, 0, NULL,
  199         ai_stand, 0, NULL,
  200         ai_stand, 0, NULL,
  201         ai_stand, 0, NULL,
  202         ai_stand, 0, NULL,
  203         ai_stand, 0, NULL,
  204         ai_stand, 0, NULL,
  205         ai_stand, 0, NULL,
  206         ai_stand, 0, NULL,
  207         ai_stand, 0, NULL,
  208         ai_stand, 0, NULL,
  209         ai_stand, 0, NULL,
  210         ai_stand, 0, NULL,
  211         ai_stand, 0, NULL,
  212         ai_stand, 0, NULL,
  213         ai_stand, 0, NULL,
  214 
  215 };
  216 mmove_t medic_move_stand = {FRAME_wait1, FRAME_wait90, medic_frames_stand, NULL};
  217 
  218 void medic_stand (edict_t *self)
  219 {
  220         self->monsterinfo.currentmove = &medic_move_stand;
  221 }
  222 
  223 
  224 mframe_t medic_frames_walk [] =
  225 {
  226         ai_walk, 6.2,   NULL,
  227         ai_walk, 18.1,  NULL,
  228         ai_walk, 1,             NULL,
  229         ai_walk, 9,             NULL,
  230         ai_walk, 10,    NULL,
  231         ai_walk, 9,             NULL,
  232         ai_walk, 11,    NULL,
  233         ai_walk, 11.6,  NULL,
  234         ai_walk, 2,             NULL,
  235         ai_walk, 9.9,   NULL,
  236         ai_walk, 14,    NULL,
  237         ai_walk, 9.3,   NULL
  238 };
  239 mmove_t medic_move_walk = {FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL};
  240 
  241 void medic_walk (edict_t *self)
  242 {
  243         self->monsterinfo.currentmove = &medic_move_walk;
  244 }
  245 
  246 
  247 mframe_t medic_frames_run [] =
  248 {
  249         ai_run, 18,             NULL,
  250         ai_run, 22.5,   NULL,
  251         ai_run, 25.4,   NULL,
  252         ai_run, 23.4,   NULL,
  253         ai_run, 24,             NULL,
  254         ai_run, 35.6,   NULL
  255         
  256 };
  257 mmove_t medic_move_run = {FRAME_run1, FRAME_run6, medic_frames_run, NULL};
  258 
  259 void medic_run (edict_t *self)
  260 {
  261         if (!(self->monsterinfo.aiflags & AI_MEDIC))
  262         {
  263                 edict_t *ent;
  264 
  265                 ent = medic_FindDeadMonster(self);
  266                 if (ent)
  267                 {
  268                         self->oldenemy = self->enemy;
  269                         self->enemy = ent;
  270                         self->enemy->owner = self;
  271                         self->monsterinfo.aiflags |= AI_MEDIC;
  272                         FoundTarget (self);
  273                         return;
  274                 }
  275         }
  276 
  277         if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  278                 self->monsterinfo.currentmove = &medic_move_stand;
  279         else
  280                 self->monsterinfo.currentmove = &medic_move_run;
  281 }
  282 
  283 
  284 mframe_t medic_frames_pain1 [] =
  285 {
  286         ai_move, 0, NULL,
  287         ai_move, 0, NULL,
  288         ai_move, 0, NULL,
  289         ai_move, 0, NULL,
  290         ai_move, 0, NULL,
  291         ai_move, 0, NULL,
  292         ai_move, 0, NULL,
  293         ai_move, 0, NULL
  294 };
  295 mmove_t medic_move_pain1 = {FRAME_paina1, FRAME_paina8, medic_frames_pain1, medic_run};
  296 
  297 mframe_t medic_frames_pain2 [] =
  298 {
  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         ai_move, 0, NULL,
  313         ai_move, 0, NULL
  314 };
  315 mmove_t medic_move_pain2 = {FRAME_painb1, FRAME_painb15, medic_frames_pain2, medic_run};
  316 
  317 void medic_pain (edict_t *self, edict_t *other, float kick, int damage)
  318 {
  319         if (self->health < (self->max_health / 2))
  320                 self->s.skinnum = 1;
  321 
  322         if (level.time < self->pain_debounce_time)
  323                 return;
  324 
  325         self->pain_debounce_time = level.time + 3;
  326 
  327         if (skill->value == 3)
  328                 return;         // no pain anims in nightmare
  329 
  330         if (random() < 0.5)
  331         {
  332                 self->monsterinfo.currentmove = &medic_move_pain1;
  333                 gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
  334         }
  335         else
  336         {
  337                 self->monsterinfo.currentmove = &medic_move_pain2;
  338                 gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
  339         }
  340 }
  341 
  342 void medic_fire_blaster (edict_t *self)
  343 {
  344         vec3_t  start;
  345         vec3_t  forward, right;
  346         vec3_t  end;
  347         vec3_t  dir;
  348         int             effect;
  349 
  350         if ((self->s.frame == FRAME_attack9) || (self->s.frame == FRAME_attack12))
  351                 effect = EF_BLASTER;
  352         else if ((self->s.frame == FRAME_attack19) || (self->s.frame == FRAME_attack22) || (self->s.frame == FRAME_attack25) || (self->s.frame == FRAME_attack28))
  353                 effect = EF_HYPERBLASTER;
  354         else
  355                 effect = 0;
  356 
  357         AngleVectors (self->s.angles, forward, right, NULL);
  358         G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MEDIC_BLASTER_1], forward, right, start);
  359 
  360         VectorCopy (self->enemy->s.origin, end);
  361         end[2] += self->enemy->viewheight;
  362         VectorSubtract (end, start, dir);
  363 
  364         monster_fire_blaster (self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_1, effect);
  365 }
  366 
  367 
  368 void medic_dead (edict_t *self)
  369 {
  370         VectorSet (self->mins, -16, -16, -24);
  371         VectorSet (self->maxs, 16, 16, -8);
  372         self->movetype = MOVETYPE_TOSS;
  373         self->svflags |= SVF_DEADMONSTER;
  374         self->nextthink = 0;
  375         gi.linkentity (self);
  376 }
  377 
  378 mframe_t medic_frames_death [] =
  379 {
  380         ai_move, 0, NULL,
  381         ai_move, 0, NULL,
  382         ai_move, 0, NULL,
  383         ai_move, 0, NULL,
  384         ai_move, 0, NULL,
  385         ai_move, 0, NULL,
  386         ai_move, 0, NULL,
  387         ai_move, 0, NULL,
  388         ai_move, 0, NULL,
  389         ai_move, 0, NULL,
  390         ai_move, 0, NULL,
  391         ai_move, 0, NULL,
  392         ai_move, 0, NULL,
  393         ai_move, 0, NULL,
  394         ai_move, 0, NULL,
  395         ai_move, 0, NULL,
  396         ai_move, 0, NULL,
  397         ai_move, 0, NULL,
  398         ai_move, 0, NULL,
  399         ai_move, 0, NULL,
  400         ai_move, 0, NULL,
  401         ai_move, 0, NULL,
  402         ai_move, 0, NULL,
  403         ai_move, 0, NULL,
  404         ai_move, 0, NULL,
  405         ai_move, 0, NULL,
  406         ai_move, 0, NULL,
  407         ai_move, 0, NULL,
  408         ai_move, 0, NULL,
  409         ai_move, 0, NULL
  410 };
  411 mmove_t medic_move_death = {FRAME_death1, FRAME_death30, medic_frames_death, medic_dead};
  412 
  413 void medic_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  414 {
  415         int             n;
  416 
  417         // if we had a pending patient, free him up for another medic
  418         if ((self->enemy) && (self->enemy->owner == self))
  419                 self->enemy->owner = NULL;
  420 
  421 // check for gib
  422         if (self->health <= self->gib_health)
  423         {
  424                 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  425                 for (n= 0; n < 2; n++)
  426                         ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
  427                 for (n= 0; n < 4; n++)
  428                         ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  429                 ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
  430                 self->deadflag = DEAD_DEAD;
  431                 return;
  432         }
  433 
  434         if (self->deadflag == DEAD_DEAD)
  435                 return;
  436 
  437 // regular death
  438         gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
  439         self->deadflag = DEAD_DEAD;
  440         self->takedamage = DAMAGE_YES;
  441 
  442         self->monsterinfo.currentmove = &medic_move_death;
  443 }
  444 
  445 
  446 void medic_duck_down (edict_t *self)
  447 {
  448         if (self->monsterinfo.aiflags & AI_DUCKED)
  449                 return;
  450         self->monsterinfo.aiflags |= AI_DUCKED;
  451         self->maxs[2] -= 32;
  452         self->takedamage = DAMAGE_YES;
  453         self->monsterinfo.pausetime = level.time + 1;
  454         gi.linkentity (self);
  455 }
  456 
  457 void medic_duck_hold (edict_t *self)
  458 {
  459         if (level.time >= self->monsterinfo.pausetime)
  460                 self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  461         else
  462                 self->monsterinfo.aiflags |= AI_HOLD_FRAME;
  463 }
  464 
  465 void medic_duck_up (edict_t *self)
  466 {
  467         self->monsterinfo.aiflags &= ~AI_DUCKED;
  468         self->maxs[2] += 32;
  469         self->takedamage = DAMAGE_AIM;
  470         gi.linkentity (self);
  471 }
  472 
  473 mframe_t medic_frames_duck [] =
  474 {
  475         ai_move, -1,    NULL,
  476         ai_move, -1,    NULL,
  477         ai_move, -1,    medic_duck_down,
  478         ai_move, -1,    medic_duck_hold,
  479         ai_move, -1,    NULL,
  480         ai_move, -1,    NULL,
  481         ai_move, -1,    medic_duck_up,
  482         ai_move, -1,    NULL,
  483         ai_move, -1,    NULL,
  484         ai_move, -1,    NULL,
  485         ai_move, -1,    NULL,
  486         ai_move, -1,    NULL,
  487         ai_move, -1,    NULL,
  488         ai_move, -1,    NULL,
  489         ai_move, -1,    NULL,
  490         ai_move, -1,    NULL
  491 };
  492 mmove_t medic_move_duck = {FRAME_duck1, FRAME_duck16, medic_frames_duck, medic_run};
  493 
  494 void medic_dodge (edict_t *self, edict_t *attacker, float eta)
  495 {
  496         if (random() > 0.25)
  497                 return;
  498 
  499         if (!self->enemy)
  500                 self->enemy = attacker;
  501 
  502         self->monsterinfo.currentmove = &medic_move_duck;
  503 }
  504 
  505 mframe_t medic_frames_attackHyperBlaster [] =
  506 {
  507         ai_charge, 0,   NULL,
  508         ai_charge, 0,   NULL,
  509         ai_charge, 0,   NULL,
  510         ai_charge, 0,   NULL,
  511         ai_charge, 0,   medic_fire_blaster,
  512         ai_charge, 0,   medic_fire_blaster,
  513         ai_charge, 0,   medic_fire_blaster,
  514         ai_charge, 0,   medic_fire_blaster,
  515         ai_charge, 0,   medic_fire_blaster,
  516         ai_charge, 0,   medic_fire_blaster,
  517         ai_charge, 0,   medic_fire_blaster,
  518         ai_charge, 0,   medic_fire_blaster,
  519         ai_charge, 0,   medic_fire_blaster,
  520         ai_charge, 0,   medic_fire_blaster,
  521         ai_charge, 0,   medic_fire_blaster,
  522         ai_charge, 0,   medic_fire_blaster
  523 };
  524 mmove_t medic_move_attackHyperBlaster = {FRAME_attack15, FRAME_attack30, medic_frames_attackHyperBlaster, medic_run};
  525 
  526 
  527 void medic_continue (edict_t *self)
  528 {
  529         if (visible (self, self->enemy) )
  530                 if (random() <= 0.95)
  531                         self->monsterinfo.currentmove = &medic_move_attackHyperBlaster;
  532 }
  533 
  534 
  535 mframe_t medic_frames_attackBlaster [] =
  536 {
  537         ai_charge, 0,   NULL,
  538         ai_charge, 5,   NULL,
  539         ai_charge, 5,   NULL,
  540         ai_charge, 3,   NULL,
  541         ai_charge, 2,   NULL,
  542         ai_charge, 0,   NULL,
  543         ai_charge, 0,   NULL,
  544         ai_charge, 0,   NULL,
  545         ai_charge, 0,   medic_fire_blaster,
  546         ai_charge, 0,   NULL,
  547         ai_charge, 0,   NULL,
  548         ai_charge, 0,   medic_fire_blaster,     
  549         ai_charge, 0,   NULL,
  550         ai_charge, 0,   medic_continue  // Change to medic_continue... Else, go to frame 32
  551 };
  552 mmove_t medic_move_attackBlaster = {FRAME_attack1, FRAME_attack14, medic_frames_attackBlaster, medic_run};
  553 
  554 
  555 void medic_hook_launch (edict_t *self)
  556 {
  557         gi.sound (self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0);
  558 }
  559 
  560 void ED_CallSpawn (edict_t *ent);
  561 
  562 static vec3_t   medic_cable_offsets[] =
  563 {
  564         45.0,  -9.2, 15.5,
  565         48.4,  -9.7, 15.2,
  566         47.8,  -9.8, 15.8,
  567         47.3,  -9.3, 14.3,
  568         45.4, -10.1, 13.1,
  569         41.9, -12.7, 12.0,
  570         37.8, -15.8, 11.2,
  571         34.3, -18.4, 10.7,
  572         32.7, -19.7, 10.4,
  573         32.7, -19.7, 10.4
  574 };
  575 
  576 void medic_cable_attack (edict_t *self)
  577 {
  578         vec3_t  offset, start, end, f, r;
  579         trace_t tr;
  580         vec3_t  dir, angles;
  581         float   distance;
  582 
  583         if (!self->enemy->inuse)
  584                 return;
  585 
  586         AngleVectors (self->s.angles, f, r, NULL);
  587         VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset);
  588         G_ProjectSource (self->s.origin, offset, f, r, start);
  589 
  590         // check for max distance
  591         VectorSubtract (start, self->enemy->s.origin, dir);
  592         distance = VectorLength(dir);
  593         if (distance > 256)
  594                 return;
  595 
  596         // check for min/max pitch
  597         vectoangles (dir, angles);
  598         if (angles[0] < -180)
  599                 angles[0] += 360;
  600         if (fabs(angles[0]) > 45)
  601                 return;
  602 
  603         tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SHOT);
  604         if (tr.fraction != 1.0 && tr.ent != self->enemy)
  605                 return;
  606 
  607         if (self->s.frame == FRAME_attack43)
  608         {
  609                 gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0);
  610                 self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
  611         }
  612         else if (self->s.frame == FRAME_attack50)
  613         {
  614                 self->enemy->spawnflags = 0;
  615                 self->enemy->monsterinfo.aiflags = 0;
  616                 self->enemy->target = NULL;
  617                 self->enemy->targetname = NULL;
  618                 self->enemy->combattarget = NULL;
  619                 self->enemy->deathtarget = NULL;
  620                 self->enemy->owner = self;
  621                 ED_CallSpawn (self->enemy);
  622                 self->enemy->owner = NULL;
  623                 if (self->enemy->think)
  624                 {
  625                         self->enemy->nextthink = level.time;
  626                         self->enemy->think (self->enemy);
  627                 }
  628                 self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
  629                 if (self->oldenemy && self->oldenemy->client)
  630                 {
  631                         self->enemy->enemy = self->oldenemy;
  632                         FoundTarget (self->enemy);
  633                 }
  634         }
  635         else
  636         {
  637                 if (self->s.frame == FRAME_attack44)
  638                         gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0);
  639         }
  640 
  641         // adjust start for beam origin being in middle of a segment
  642         VectorMA (start, 8, f, start);
  643 
  644         // adjust end z for end spot since the monster is currently dead
  645         VectorCopy (self->enemy->s.origin, end);
  646         end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2;
  647 
  648         gi.WriteByte (svc_temp_entity);
  649         gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
  650         gi.WriteShort (self - g_edicts);
  651         gi.WritePosition (start);
  652         gi.WritePosition (end);
  653         gi.multicast (self->s.origin, MULTICAST_PVS);
  654 }
  655 
  656 void medic_hook_retract (edict_t *self)
  657 {
  658         gi.sound (self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0);
  659         self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
  660 }
  661 
  662 mframe_t medic_frames_attackCable [] =
  663 {
  664         ai_move, 2,             NULL,
  665         ai_move, 3,             NULL,
  666         ai_move, 5,             NULL,
  667         ai_move, 4.4,   NULL,
  668         ai_charge, 4.7, NULL,
  669         ai_charge, 5,   NULL,
  670         ai_charge, 6,   NULL,
  671         ai_charge, 4,   NULL,
  672         ai_charge, 0,   NULL,
  673         ai_move, 0,             medic_hook_launch,
  674         ai_move, 0,             medic_cable_attack,
  675         ai_move, 0,             medic_cable_attack,
  676         ai_move, 0,             medic_cable_attack,
  677         ai_move, 0,             medic_cable_attack,
  678         ai_move, 0,             medic_cable_attack,
  679         ai_move, 0,             medic_cable_attack,
  680         ai_move, 0,             medic_cable_attack,
  681         ai_move, 0,             medic_cable_attack,
  682         ai_move, 0,             medic_cable_attack,
  683         ai_move, -15,   medic_hook_retract,
  684         ai_move, -1.5,  NULL,
  685         ai_move, -1.2,  NULL,
  686         ai_move, -3,    NULL,
  687         ai_move, -2,    NULL,
  688         ai_move, 0.3,   NULL,
  689         ai_move, 0.7,   NULL,
  690         ai_move, 1.2,   NULL,
  691         ai_move, 1.3,   NULL
  692 };
  693 mmove_t medic_move_attackCable = {FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run};
  694 
  695 
  696 void medic_attack(edict_t *self)
  697 {
  698         if (self->monsterinfo.aiflags & AI_MEDIC)
  699                 self->monsterinfo.currentmove = &medic_move_attackCable;
  700         else
  701                 self->monsterinfo.currentmove = &medic_move_attackBlaster;
  702 }
  703 
  704 qboolean medic_checkattack (edict_t *self)
  705 {
  706         if (self->monsterinfo.aiflags & AI_MEDIC)
  707         {
  708                 medic_attack(self);
  709                 return true;
  710         }
  711 
  712         return M_CheckAttack (self);
  713 }
  714 
  715 
  716 /*QUAKED monster_medic (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  717 */
  718 void SP_monster_medic (edict_t *self)
  719 {
  720         if (deathmatch->value)
  721         {
  722                 G_FreeEdict (self);
  723                 return;
  724         }
  725 
  726         sound_idle1 = gi.soundindex ("medic/idle.wav");
  727         sound_pain1 = gi.soundindex ("medic/medpain1.wav");
  728         sound_pain2 = gi.soundindex ("medic/medpain2.wav");
  729         sound_die = gi.soundindex ("medic/meddeth1.wav");
  730         sound_sight = gi.soundindex ("medic/medsght1.wav");
  731         sound_search = gi.soundindex ("medic/medsrch1.wav");
  732         sound_hook_launch = gi.soundindex ("medic/medatck2.wav");
  733         sound_hook_hit = gi.soundindex ("medic/medatck3.wav");
  734         sound_hook_heal = gi.soundindex ("medic/medatck4.wav");
  735         sound_hook_retract = gi.soundindex ("medic/medatck5.wav");
  736 
  737         gi.soundindex ("medic/medatck1.wav");
  738 
  739         self->movetype = MOVETYPE_STEP;
  740         self->solid = SOLID_BBOX;
  741         self->s.modelindex = gi.modelindex ("models/monsters/medic/tris.md2");
  742         VectorSet (self->mins, -24, -24, -24);
  743         VectorSet (self->maxs, 24, 24, 32);
  744 
  745         self->health = 300;
  746         self->gib_health = -130;
  747         self->mass = 400;
  748 
  749         self->pain = medic_pain;
  750         self->die = medic_die;
  751 
  752         self->monsterinfo.stand = medic_stand;
  753         self->monsterinfo.walk = medic_walk;
  754         self->monsterinfo.run = medic_run;
  755         self->monsterinfo.dodge = medic_dodge;
  756         self->monsterinfo.attack = medic_attack;
  757         self->monsterinfo.melee = NULL;
  758         self->monsterinfo.sight = medic_sight;
  759         self->monsterinfo.idle = medic_idle;
  760         self->monsterinfo.search = medic_search;
  761         self->monsterinfo.checkattack = medic_checkattack;
  762 
  763         gi.linkentity (self);
  764 
  765         self->monsterinfo.currentmove = &medic_move_stand;
  766         self->monsterinfo.scale = MODEL_SCALE;
  767 
  768         walkmonster_start (self);
  769 }
  770