1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * 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 along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20
21 /*
22 *
23 * ============================================================================
24 * ==
25 *
26 * parasite
27 *
28 * ========
29 * =====================================================================
30 */
31
32 #include "g_local.h"
33 #include "m_parasite.h"
34
35
36 static int sound_pain1;
37 static int sound_pain2;
38 static int sound_die;
39 static int sound_launch;
40 static int sound_impact;
41 static int sound_suck;
42 static int sound_reelin;
43 static int sound_sight;
44 static int sound_tap;
45 static int sound_scratch;
46 static int sound_search;
47
48
49 void parasite_stand(edict_t * self);
50 void parasite_start_run(edict_t * self);
51 void parasite_run(edict_t * self);
52 void parasite_walk(edict_t * self);
53 void parasite_start_walk(edict_t * self);
54 void parasite_end_fidget(edict_t * self);
55 void parasite_do_fidget(edict_t * self);
56 void parasite_refidget(edict_t * self);
57
58
59 void
parasite_launch(edict_t * self)60 parasite_launch(edict_t * self)
61 {
62 gi.sound(self, CHAN_WEAPON, sound_launch, 1, ATTN_NORM, 0);
63 }
64
65 void
parasite_reel_in(edict_t * self)66 parasite_reel_in(edict_t * self)
67 {
68 gi.sound(self, CHAN_WEAPON, sound_reelin, 1, ATTN_NORM, 0);
69 }
70
71 void
parasite_sight(edict_t * self,edict_t * other)72 parasite_sight(edict_t * self, edict_t * other)
73 {
74 gi.sound(self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
75 }
76
77 void
parasite_tap(edict_t * self)78 parasite_tap(edict_t * self)
79 {
80 gi.sound(self, CHAN_WEAPON, sound_tap, 1, ATTN_IDLE, 0);
81 }
82
83 void
parasite_scratch(edict_t * self)84 parasite_scratch(edict_t * self)
85 {
86 gi.sound(self, CHAN_WEAPON, sound_scratch, 1, ATTN_IDLE, 0);
87 }
88
89 void
parasite_search(edict_t * self)90 parasite_search(edict_t * self)
91 {
92 gi.sound(self, CHAN_WEAPON, sound_search, 1, ATTN_IDLE, 0);
93 }
94
95
96 mframe_t parasite_frames_start_fidget[] =
97 {
98 {ai_stand, 0, NULL},
99 {ai_stand, 0, NULL},
100 {ai_stand, 0, NULL},
101 {ai_stand, 0, NULL}
102 };
103 mmove_t parasite_move_start_fidget = {FRAME_stand18, FRAME_stand21, parasite_frames_start_fidget, parasite_do_fidget};
104
105 mframe_t parasite_frames_fidget[] =
106 {
107 {ai_stand, 0, parasite_scratch},
108 {ai_stand, 0, NULL},
109 {ai_stand, 0, NULL},
110 {ai_stand, 0, parasite_scratch},
111 {ai_stand, 0, NULL},
112 {ai_stand, 0, NULL}
113 };
114 mmove_t parasite_move_fidget = {FRAME_stand22, FRAME_stand27, parasite_frames_fidget, parasite_refidget};
115
116 mframe_t parasite_frames_end_fidget[] =
117 {
118 {ai_stand, 0, parasite_scratch},
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 };
127 mmove_t parasite_move_end_fidget = {FRAME_stand28, FRAME_stand35, parasite_frames_end_fidget, parasite_stand};
128
129 void
parasite_end_fidget(edict_t * self)130 parasite_end_fidget(edict_t * self)
131 {
132 self->monsterinfo.currentmove = ¶site_move_end_fidget;
133 }
134
135 void
parasite_do_fidget(edict_t * self)136 parasite_do_fidget(edict_t * self)
137 {
138 self->monsterinfo.currentmove = ¶site_move_fidget;
139 }
140
141 void
parasite_refidget(edict_t * self)142 parasite_refidget(edict_t * self)
143 {
144 if (random() <= 0.8)
145 self->monsterinfo.currentmove = ¶site_move_fidget;
146 else
147 self->monsterinfo.currentmove = ¶site_move_end_fidget;
148 }
149
150 void
parasite_idle(edict_t * self)151 parasite_idle(edict_t * self)
152 {
153 self->monsterinfo.currentmove = ¶site_move_start_fidget;
154 }
155
156
157 mframe_t parasite_frames_stand[] =
158 {
159 {ai_stand, 0, NULL},
160 {ai_stand, 0, NULL},
161 {ai_stand, 0, parasite_tap},
162 {ai_stand, 0, NULL},
163 {ai_stand, 0, parasite_tap},
164 {ai_stand, 0, NULL},
165 {ai_stand, 0, NULL},
166 {ai_stand, 0, NULL},
167 {ai_stand, 0, parasite_tap},
168 {ai_stand, 0, NULL},
169 {ai_stand, 0, parasite_tap},
170 {ai_stand, 0, NULL},
171 {ai_stand, 0, NULL},
172 {ai_stand, 0, NULL},
173 {ai_stand, 0, parasite_tap},
174 {ai_stand, 0, NULL},
175 {ai_stand, 0, parasite_tap}
176 };
177 mmove_t parasite_move_stand = {FRAME_stand01, FRAME_stand17, parasite_frames_stand, parasite_stand};
178
179 void
parasite_stand(edict_t * self)180 parasite_stand(edict_t * self)
181 {
182 self->monsterinfo.currentmove = ¶site_move_stand;
183 }
184
185
186 mframe_t parasite_frames_run[] =
187 {
188 {ai_run, 30, NULL},
189 {ai_run, 30, NULL},
190 {ai_run, 22, NULL},
191 {ai_run, 19, NULL},
192 {ai_run, 24, NULL},
193 {ai_run, 28, NULL},
194 {ai_run, 25, NULL}
195 };
196 mmove_t parasite_move_run = {FRAME_run03, FRAME_run09, parasite_frames_run, NULL};
197
198 mframe_t parasite_frames_start_run[] =
199 {
200 {ai_run, 0, NULL},
201 {ai_run, 30, NULL},
202 };
203 mmove_t parasite_move_start_run = {FRAME_run01, FRAME_run02, parasite_frames_start_run, parasite_run};
204
205 mframe_t parasite_frames_stop_run[] =
206 {
207 {ai_run, 20, NULL},
208 {ai_run, 20, NULL},
209 {ai_run, 12, NULL},
210 {ai_run, 10, NULL},
211 {ai_run, 0, NULL},
212 {ai_run, 0, NULL}
213 };
214 mmove_t parasite_move_stop_run = {FRAME_run10, FRAME_run15, parasite_frames_stop_run, NULL};
215
216 void
parasite_start_run(edict_t * self)217 parasite_start_run(edict_t * self)
218 {
219 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
220 self->monsterinfo.currentmove = ¶site_move_stand;
221 else
222 self->monsterinfo.currentmove = ¶site_move_start_run;
223 }
224
225 void
parasite_run(edict_t * self)226 parasite_run(edict_t * self)
227 {
228 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
229 self->monsterinfo.currentmove = ¶site_move_stand;
230 else
231 self->monsterinfo.currentmove = ¶site_move_run;
232 }
233
234
235 mframe_t parasite_frames_walk[] =
236 {
237 {ai_walk, 30, NULL},
238 {ai_walk, 30, NULL},
239 {ai_walk, 22, NULL},
240 {ai_walk, 19, NULL},
241 {ai_walk, 24, NULL},
242 {ai_walk, 28, NULL},
243 {ai_walk, 25, NULL}
244 };
245 mmove_t parasite_move_walk = {FRAME_run03, FRAME_run09, parasite_frames_walk, parasite_walk};
246
247 mframe_t parasite_frames_start_walk[] =
248 {
249 {ai_walk, 0, NULL},
250 {ai_walk, 30, parasite_walk}
251 };
252 mmove_t parasite_move_start_walk = {FRAME_run01, FRAME_run02, parasite_frames_start_walk, NULL};
253
254 mframe_t parasite_frames_stop_walk[] =
255 {
256 {ai_walk, 20, NULL},
257 {ai_walk, 20, NULL},
258 {ai_walk, 12, NULL},
259 {ai_walk, 10, NULL},
260 {ai_walk, 0, NULL},
261 {ai_walk, 0, NULL}
262 };
263 mmove_t parasite_move_stop_walk = {FRAME_run10, FRAME_run15, parasite_frames_stop_walk, NULL};
264
265 void
parasite_start_walk(edict_t * self)266 parasite_start_walk(edict_t * self)
267 {
268 self->monsterinfo.currentmove = ¶site_move_start_walk;
269 }
270
271 void
parasite_walk(edict_t * self)272 parasite_walk(edict_t * self)
273 {
274 self->monsterinfo.currentmove = ¶site_move_walk;
275 }
276
277
278 mframe_t parasite_frames_pain1[] =
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, 6, NULL},
287 {ai_move, 16, NULL},
288 {ai_move, -6, NULL},
289 {ai_move, -7, NULL},
290 {ai_move, 0, NULL}
291 };
292 mmove_t parasite_move_pain1 = {FRAME_pain101, FRAME_pain111, parasite_frames_pain1, parasite_start_run};
293
294 void
parasite_pain(edict_t * self,edict_t * other,float kick,int damage)295 parasite_pain(edict_t * self, edict_t * other, float kick, int damage)
296 {
297 if (self->health < (self->max_health / 2))
298 self->s.skinnum = 1;
299
300 if (level.time < self->pain_debounce_time)
301 return;
302
303 self->pain_debounce_time = level.time + 3;
304
305 if (skill->value == 3)
306 return; /* no pain anims in nightmare */
307
308 if (random() < 0.5)
309 gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
310 else
311 gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
312
313 self->monsterinfo.currentmove = ¶site_move_pain1;
314 }
315
316
317 static qboolean
parasite_drain_attack_ok(vec3_t start,vec3_t end)318 parasite_drain_attack_ok(vec3_t start, vec3_t end)
319 {
320 vec3_t dir , angles;
321
322 /* check for max distance */
323 VectorSubtract(start, end, dir);
324 if (VectorLength(dir) > 256)
325 return false;
326
327 /* check for min/max pitch */
328 vectoangles(dir, angles);
329 if (angles[0] < -180)
330 angles[0] += 360;
331 if (fabs(angles[0]) > 30)
332 return false;
333
334 return true;
335 }
336
337 void
parasite_drain_attack(edict_t * self)338 parasite_drain_attack(edict_t * self)
339 {
340 vec3_t offset , start, f, r, end, dir;
341 trace_t tr;
342 int damage;
343
344 AngleVectors(self->s.angles, f, r, NULL);
345 VectorSet(offset, 24, 0, 6);
346 G_ProjectSource(self->s.origin, offset, f, r, start);
347
348 VectorCopy(self->enemy->s.origin, end);
349 if (!parasite_drain_attack_ok(start, end)) {
350 end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
351 if (!parasite_drain_attack_ok(start, end)) {
352 end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
353 if (!parasite_drain_attack_ok(start, end))
354 return;
355 }
356 }
357 VectorCopy(self->enemy->s.origin, end);
358
359 tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
360 if (tr.ent != self->enemy)
361 return;
362
363 if (self->s.frame == FRAME_drain03) {
364 damage = 5;
365 gi.sound(self->enemy, CHAN_AUTO, sound_impact, 1, ATTN_NORM, 0);
366 } else {
367 if (self->s.frame == FRAME_drain04)
368 gi.sound(self, CHAN_WEAPON, sound_suck, 1, ATTN_NORM, 0);
369 damage = 2;
370 }
371
372 gi.WriteByte(svc_temp_entity);
373 gi.WriteByte(TE_PARASITE_ATTACK);
374 gi.WriteShort(self - g_edicts);
375 gi.WritePosition(start);
376 gi.WritePosition(end);
377 gi.multicast(self->s.origin, MULTICAST_PVS);
378
379 VectorSubtract(start, end, dir);
380 T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
381 }
382
383 mframe_t parasite_frames_drain[] =
384 {
385 {ai_charge, 0, parasite_launch},
386 {ai_charge, 0, NULL},
387 {ai_charge, 15, parasite_drain_attack}, /* Target hits */
388 {ai_charge, 0, parasite_drain_attack}, /* drain */
389 {ai_charge, 0, parasite_drain_attack}, /* drain */
390 {ai_charge, 0, parasite_drain_attack}, /* drain */
391 {ai_charge, 0, parasite_drain_attack}, /* drain */
392 {ai_charge, -2, parasite_drain_attack}, /* drain */
393 {ai_charge, -2, parasite_drain_attack}, /* drain */
394 {ai_charge, -3, parasite_drain_attack}, /* drain */
395 {ai_charge, -2, parasite_drain_attack}, /* drain */
396 {ai_charge, 0, parasite_drain_attack}, /* drain */
397 {ai_charge, -1, parasite_drain_attack}, /* drain */
398 {ai_charge, 0, parasite_reel_in}, /* let go */
399 {ai_charge, -2, NULL},
400 {ai_charge, -2, NULL},
401 {ai_charge, -3, NULL},
402 {ai_charge, 0, NULL}
403 };
404 mmove_t parasite_move_drain = {FRAME_drain01, FRAME_drain18, parasite_frames_drain, parasite_start_run};
405
406
407 mframe_t parasite_frames_break[] =
408 {
409 {ai_charge, 0, NULL},
410 {ai_charge, -3, NULL},
411 {ai_charge, 1, NULL},
412 {ai_charge, 2, NULL},
413 {ai_charge, -3, NULL},
414 {ai_charge, 1, NULL},
415 {ai_charge, 1, NULL},
416 {ai_charge, 3, NULL},
417 {ai_charge, 0, NULL},
418 {ai_charge, -18, NULL},
419 {ai_charge, 3, NULL},
420 {ai_charge, 9, NULL},
421 {ai_charge, 6, NULL},
422 {ai_charge, 0, NULL},
423 {ai_charge, -18, NULL},
424 {ai_charge, 0, NULL},
425 {ai_charge, 8, NULL},
426 {ai_charge, 9, NULL},
427 {ai_charge, 0, NULL},
428 {ai_charge, -18, NULL},
429 {ai_charge, 0, NULL},
430 {ai_charge, 0, NULL}, /* airborne */
431 {ai_charge, 0, NULL}, /* airborne */
432 {ai_charge, 0, NULL}, /* slides */
433 {ai_charge, 0, NULL}, /* slides */
434 {ai_charge, 0, NULL}, /* slides */
435 {ai_charge, 0, NULL}, /* slides */
436 {ai_charge, 4, NULL},
437 {ai_charge, 11, NULL},
438 {ai_charge, -2, NULL},
439 {ai_charge, -5, NULL},
440 {ai_charge, 1, NULL}
441 };
442 mmove_t parasite_move_break = {FRAME_break01, FRAME_break32, parasite_frames_break, parasite_start_run};
443
444 /*
445 * === Break Stuff Ends ===
446 */
447
448 void
parasite_attack(edict_t * self)449 parasite_attack(edict_t * self)
450 {
451 /* if (random() <= 0.2) */
452 /* self->monsterinfo.currentmove = ¶site_move_break; */
453 /* else */
454 self->monsterinfo.currentmove = ¶site_move_drain;
455 }
456
457
458
459 /*
460 * === Death Stuff Starts ===
461 */
462
463 void
parasite_dead(edict_t * self)464 parasite_dead(edict_t * self)
465 {
466 VectorSet(self->mins, -16, -16, -24);
467 VectorSet(self->maxs, 16, 16, -8);
468 self->movetype = MOVETYPE_TOSS;
469 self->svflags |= SVF_DEADMONSTER;
470 self->nextthink = 0;
471 gi.linkentity(self);
472 }
473
474 mframe_t parasite_frames_death[] =
475 {
476 {ai_move, 0, NULL},
477 {ai_move, 0, NULL},
478 {ai_move, 0, NULL},
479 {ai_move, 0, NULL},
480 {ai_move, 0, NULL},
481 {ai_move, 0, NULL},
482 {ai_move, 0, NULL}
483 };
484 mmove_t parasite_move_death = {FRAME_death101, FRAME_death107, parasite_frames_death, parasite_dead};
485
486 void
parasite_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)487 parasite_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
488 {
489 int n;
490
491 /* check for gib */
492 if (self->health <= self->gib_health) {
493 gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
494 for (n = 0; n < 2; n++)
495 ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
496 for (n = 0; n < 4; n++)
497 ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
498 ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
499 self->deadflag = DEAD_DEAD;
500 return;
501 }
502 if (self->deadflag == DEAD_DEAD)
503 return;
504
505 /* regular death */
506 gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
507 self->deadflag = DEAD_DEAD;
508 self->takedamage = DAMAGE_YES;
509 self->monsterinfo.currentmove = ¶site_move_death;
510 }
511
512 /*
513 * === End Death Stuff ===
514 */
515
516 /*
517 * QUAKED monster_parasite (1 .5 0) (-16 -16 -24) (16 16 32) Ambush
518 * Trigger_Spawn Sight
519 */
520 void
SP_monster_parasite(edict_t * self)521 SP_monster_parasite(edict_t * self)
522 {
523 if (deathmatch->value) {
524 G_FreeEdict(self);
525 return;
526 }
527 sound_pain1 = gi.soundindex("parasite/parpain1.wav");
528 sound_pain2 = gi.soundindex("parasite/parpain2.wav");
529 sound_die = gi.soundindex("parasite/pardeth1.wav");
530 sound_launch = gi.soundindex("parasite/paratck1.wav");
531 sound_impact = gi.soundindex("parasite/paratck2.wav");
532 sound_suck = gi.soundindex("parasite/paratck3.wav");
533 sound_reelin = gi.soundindex("parasite/paratck4.wav");
534 sound_sight = gi.soundindex("parasite/parsght1.wav");
535 sound_tap = gi.soundindex("parasite/paridle1.wav");
536 sound_scratch = gi.soundindex("parasite/paridle2.wav");
537 sound_search = gi.soundindex("parasite/parsrch1.wav");
538
539 self->s.modelindex = gi.modelindex("models/monsters/parasite/tris.md2");
540 VectorSet(self->mins, -16, -16, -24);
541 VectorSet(self->maxs, 16, 16, 24);
542 self->movetype = MOVETYPE_STEP;
543 self->solid = SOLID_BBOX;
544
545 self->health = 175;
546 self->gib_health = -50;
547 self->mass = 250;
548
549 self->pain = parasite_pain;
550 self->die = parasite_die;
551
552 self->monsterinfo.stand = parasite_stand;
553 self->monsterinfo.walk = parasite_start_walk;
554 self->monsterinfo.run = parasite_start_run;
555 self->monsterinfo.attack = parasite_attack;
556 self->monsterinfo.sight = parasite_sight;
557 self->monsterinfo.idle = parasite_idle;
558
559 gi.linkentity(self);
560
561 self->monsterinfo.currentmove = ¶site_move_stand;
562 self->monsterinfo.scale = MODEL_SCALE;
563
564 walkmonster_start(self);
565 }
566