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 qBool 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
medic_FindDeadMonster(edict_t * self)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
medic_idle(edict_t * self)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
medic_search(edict_t * self)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
medic_sight(edict_t * self,edict_t * other)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
medic_stand(edict_t * self)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.2f, NULL,
227 ai_walk, 18.1f, 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.6f, NULL,
234 ai_walk, 2, NULL,
235 ai_walk, 9.9f, NULL,
236 ai_walk, 14, NULL,
237 ai_walk, 9.3f, NULL
238 };
239 mmove_t medic_move_walk = {FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL};
240
medic_walk(edict_t * self)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.5f, NULL,
251 ai_run, 25.4f, NULL,
252 ai_run, 23.4f, NULL,
253 ai_run, 24, NULL,
254 ai_run, 35.6f, NULL
255
256 };
257 mmove_t medic_move_run = {FRAME_run1, FRAME_run6, medic_frames_run, NULL};
258
medic_run(edict_t * self)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
medic_pain(edict_t * self,edict_t * other,float kick,int damage)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->floatVal == 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
medic_fire_blaster(edict_t * self)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 Angles_Vectors (self->s.angles, forward, right, NULL);
358 G_ProjectSource (self->s.origin, dumb_and_hacky_monster_MuzzFlashOffset[MZ2_MEDIC_BLASTER_1], forward, right, start);
359
360 Vec3Copy (self->enemy->s.origin, end);
361 end[2] += self->enemy->viewheight;
362 Vec3Subtract (end, start, dir);
363
364 monster_fire_blaster (self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_1, effect);
365 }
366
367
medic_dead(edict_t * self)368 void medic_dead (edict_t *self)
369 {
370 Vec3Set (self->mins, -16, -16, -24);
371 Vec3Set (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
medic_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)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
medic_duck_down(edict_t * self)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
medic_duck_hold(edict_t * self)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
medic_duck_up(edict_t * self)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
medic_dodge(edict_t * self,edict_t * attacker,float eta)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
medic_continue(edict_t * self)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
medic_hook_launch(edict_t * self)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.0f, -9.2f, 15.5f,
565 48.4f, -9.7f, 15.2f,
566 47.8f, -9.8f, 15.8f,
567 47.3f, -9.3f, 14.3f,
568 45.4f, -10.1f, 13.1f,
569 41.9f, -12.7f, 12.0f,
570 37.8f, -15.8f, 11.2f,
571 34.3f, -18.4f, 10.7f,
572 32.7f, -19.7f, 10.4f,
573 32.7f, -19.7f, 10.4f
574 };
575
medic_cable_attack(edict_t * self)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 Angles_Vectors (self->s.angles, f, r, NULL);
587 Vec3Copy (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 Vec3Subtract (start, self->enemy->s.origin, dir);
592 distance = Vec3Length(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 Vec3MA (start, 8, f, start);
643
644 // adjust end z for end spot since the monster is currently dead
645 Vec3Copy (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
medic_hook_retract(edict_t * self)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.4f, NULL,
668 ai_charge, 4.7f, 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.5f, NULL,
685 ai_move, -1.2f, NULL,
686 ai_move, -3, NULL,
687 ai_move, -2, NULL,
688 ai_move, 0.3f, NULL,
689 ai_move, 0.7f, NULL,
690 ai_move, 1.2f, NULL,
691 ai_move, 1.3f, NULL
692 };
693 mmove_t medic_move_attackCable = {FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run};
694
695
medic_attack(edict_t * self)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
medic_checkattack(edict_t * self)704 qBool medic_checkattack (edict_t *self)
705 {
706 if (self->monsterinfo.aiflags & AI_MEDIC)
707 {
708 medic_attack(self);
709 return qTrue;
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 */
SP_monster_medic(edict_t * self)718 void SP_monster_medic (edict_t *self)
719 {
720 if (deathmatch->floatVal)
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 Vec3Set (self->mins, -24, -24, -24);
743 Vec3Set (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