Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

m_medic.c (17997B)


      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 }