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