1 #include "g_local.h"
2 #include "z_anim.h"
3
4 #include "z_boss.h"
5
6 static int sound_pain1;
7 static int sound_pain2;
8 static int sound_pain3;
9 static int sound_die1;
10 static int sound_die2;
11 static int sound_hookimpact;
12 static int sound_sight;
13 static int sound_hooklaunch;
14 static int sound_hookfly;
15 static int sound_swing;
16 static int sound_idle1;
17 static int sound_idle2;
18 static int sound_walk;
19 static int sound_raisegun;
20 static int sound_lowergun;
21 static int sound_switchattacks;
22 static int sound_plamsaballfly;
23 static int sound_plamsaballexplode;
24 static int sound_plamsaballfire;
25 static int sound_taunt1;
26 static int sound_taunt2;
27 static int sound_taunt3;
28
29
30 void fire_empnuke(edict_t *ent, vec3_t center, int radius);
31 void SV_AddGravity (edict_t *ent);
32
33
34 void zboss_stand (edict_t *self);
35 void zboss_run (edict_t *self);
36 void zboss_run2 (edict_t *self);
37 void zboss_walk (edict_t *self);
38 void zboss_walk2(edict_t *self);
39 void zboss_chooseNextAttack(edict_t *self);
40 void zboss_reelInGraaple(edict_t *self);
41 void zboss_posthook(edict_t *self);
42 void HookDragThink (edict_t *self);
43 void zboss_attack (edict_t *self);
44
45
zboss_walksound(edict_t * self)46 void zboss_walksound (edict_t *self)
47 {
48 gi.sound (self, CHAN_BODY, sound_walk, 1, ATTN_NORM, 0);
49 }
50
51
zboss_sight(edict_t * self,edict_t * other)52 void zboss_sight (edict_t *self, edict_t *other)
53 {
54 gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
55 }
56
57
possibleBossTaunt(edict_t * self)58 void possibleBossTaunt(edict_t *self)
59 {
60 float r = random();
61
62 if(random() < 0.10)
63 {
64 if(r < 0.33)
65 {
66 gi.sound (self, CHAN_VOICE, sound_taunt1, 1, ATTN_NORM, 0);
67 }
68 else if(r < 0.66)
69 {
70 gi.sound (self, CHAN_VOICE, sound_taunt2, 1, ATTN_NORM, 0);
71 }
72 else
73 {
74 gi.sound (self, CHAN_VOICE, sound_taunt3, 1, ATTN_NORM, 0);
75 }
76 }
77 }
78
79 //
80 // STAND
81 //
82
83
84 mframe_t zboss_frames_stand1 [] =
85 {
86 ai_stand, 0, NULL,
87 ai_stand, 0, NULL,
88 ai_stand, 0, NULL,
89 ai_stand, 0, NULL,
90 ai_stand, 0, NULL,
91 ai_stand, 0, NULL,
92 ai_stand, 0, NULL,
93 ai_stand, 0, NULL,
94 ai_stand, 0, NULL,
95 ai_stand, 0, NULL, // 9
96
97 ai_stand, 0, NULL,
98 ai_stand, 0, NULL,
99 ai_stand, 0, NULL,
100 ai_stand, 0, NULL,
101 ai_stand, 0, NULL,
102 ai_stand, 0, NULL,
103 ai_stand, 0, NULL,
104 ai_stand, 0, NULL,
105 ai_stand, 0, NULL,
106 ai_stand, 0, NULL, // 19
107
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, // 29
118
119 ai_stand, 0, NULL,
120 ai_stand, 0, NULL,
121 };
122 mmove_t zboss_stand1 = {FRAME_stand1start, FRAME_stand1end, zboss_frames_stand1, zboss_stand};
123
124
125
126
127 mframe_t zboss_frames_stand2 [] =
128 {
129 ai_stand, 0, NULL, // 32
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, // 41
139
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, // 51
150
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 // 56
156 };
157 mmove_t zboss_stand2 = {FRAME_stand2start, FRAME_stand2end, zboss_frames_stand2, zboss_stand};
158
159
zboss_standidle(edict_t * self)160 void zboss_standidle (edict_t *self)
161 {
162 if (random() < 0.8)
163 {
164 gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_NORM, 0);
165 self->monsterinfo.currentmove = &zboss_stand1;
166 }
167 else
168 {
169 gi.sound (self, CHAN_VOICE, sound_idle2, 1, ATTN_NORM, 0);
170 self->monsterinfo.currentmove = &zboss_stand2;
171 }
172 }
173
174
175 //
176 // Post WALK/RUN leading into ilde.
177 //
178
179 mframe_t zboss_frames_postwalk [] =
180 {
181 ai_walk, 3, NULL, // 177
182 ai_walk, 3, NULL,
183 ai_walk, 3, NULL,
184 ai_walk, 3, NULL,
185 ai_walk, 3, NULL,
186 ai_walk, 3, NULL,
187 ai_walk, 3, NULL,
188 ai_walk, 3, NULL, // 184
189 };
190 mmove_t zboss_move_postwalk = {FRAME_postWalkStart, FRAME_postWalkEnd, zboss_frames_postwalk, zboss_standidle};
191
192
zboss_postWalkRun(edict_t * self)193 void zboss_postWalkRun (edict_t *self)
194 {
195 self->monsterinfo.currentmove = &zboss_move_postwalk;
196 }
197
198
199
200 //
201 // WALK
202 //
203
204 mframe_t zboss_frames_prewalk [] =
205 {
206 ai_walk, 3, NULL, //154
207 ai_walk, 3, NULL,
208 ai_walk, 3, NULL,
209 ai_walk, 3, NULL,
210 ai_walk, 3, NULL,
211 ai_walk, 3, NULL,
212 ai_walk, 3, NULL, // 160
213 };
214 mmove_t zboss_move_prewalk = {FRAME_preWalkStart, FRAME_preWalkEnd, zboss_frames_prewalk, zboss_walk2};
215
216
217
218 mframe_t zboss_frames_walk [] =
219 {
220 ai_walk, 2, NULL, //161
221 ai_walk, 3, NULL,
222 ai_walk, 3, NULL,
223 ai_walk, 4, NULL,
224 ai_walk, 4, NULL,
225 ai_walk, 4, NULL,
226 ai_walk, 4, NULL,
227 ai_walk, 3, zboss_walksound,
228 ai_walk, 4, NULL,
229 ai_walk, 4, NULL, // 170
230 ai_walk, 4, NULL,
231 ai_walk, 4, NULL,
232 ai_walk, 3, NULL,
233 ai_walk, 2, NULL,
234 ai_walk, 2, NULL,
235 ai_walk, 3, zboss_walksound, // 176
236 };
237 mmove_t zboss_move_walk = {FRAME_walkStart, FRAME_walkEnd, zboss_frames_walk, zboss_walk2};
238
239
240
zboss_walk(edict_t * self)241 void zboss_walk (edict_t *self)
242 {
243 self->monsterinfo.currentmove = &zboss_move_prewalk;
244 }
245
zboss_walk2(edict_t * self)246 void zboss_walk2(edict_t *self)
247 {
248 self->monsterinfo.currentmove = &zboss_move_walk;
249 }
250
251
252
253 //
254 // RUN
255 //
256
257
258 mframe_t zboss_frames_prerun [] =
259 {
260 ai_run, 3, NULL, //154
261 ai_run, 3, NULL,
262 ai_run, 3, NULL,
263 ai_run, 3, NULL,
264 ai_run, 3, NULL,
265 ai_run, 3, NULL,
266 ai_run, 3, NULL, // 160
267 };
268 mmove_t zboss_move_prerun = {FRAME_preWalkStart, FRAME_preWalkEnd, zboss_frames_prerun, zboss_run2};
269
270
271 mframe_t zboss_frames_run [] =
272 {
273 ai_run, 2, NULL, //161
274 ai_run, 3, NULL,
275 ai_run, 3, NULL,
276 ai_run, 4, NULL,
277 ai_run, 4, NULL,
278 ai_run, 4, NULL,
279 ai_run, 4, NULL,
280 ai_run, 3, zboss_walksound,
281 ai_run, 4, NULL,
282 ai_run, 4, NULL, // 170
283 ai_run, 4, NULL,
284 ai_run, 4, NULL,
285 ai_run, 3, NULL,
286 ai_run, 2, NULL,
287 ai_run, 2, NULL,
288 ai_run, 3, zboss_walksound, // 176
289 };
290 mmove_t zboss_move_run = {FRAME_walkStart, FRAME_walkEnd, zboss_frames_run, NULL};
291
292
zboss_run(edict_t * self)293 void zboss_run (edict_t *self)
294 {
295 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
296 zboss_stand(self);
297 else
298 self->monsterinfo.currentmove = &zboss_move_prerun;
299 }
300
301
zboss_run2(edict_t * self)302 void zboss_run2 (edict_t *self)
303 {
304 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
305 zboss_stand(self);
306 else
307 self->monsterinfo.currentmove = &zboss_move_run;
308 }
309
310
311 //
312 // main stand function
313 //
zboss_stand(edict_t * self)314 void zboss_stand (edict_t *self)
315 {
316 if(self->monsterinfo.currentmove == &zboss_move_prewalk ||
317 self->monsterinfo.currentmove == &zboss_move_walk ||
318 self->monsterinfo.currentmove == &zboss_move_prerun ||
319 self->monsterinfo.currentmove == &zboss_move_run)
320 {
321 zboss_postWalkRun(self);
322 }
323 else
324 {
325 zboss_standidle(self);
326 }
327 }
328
329
330
331
332
333 //
334 // PAIN
335 //
336
337
338 mframe_t zboss_frames_pain1 [] =
339 {
340 ai_move, 0, NULL, // 185
341 ai_move, 0, NULL,
342 ai_move, 0, NULL // 187
343 };
344 mmove_t zboss_move_pain1 = {FRAME_pain1Start, FRAME_pain1End, zboss_frames_pain1, zboss_run};
345
346
347
348 mframe_t zboss_frames_pain2 [] =
349 {
350 ai_move, 0, NULL, // 188
351 ai_move, 0, NULL,
352 ai_move, 0, NULL,
353 ai_move, 0, NULL,
354 ai_move, 0, NULL // 192
355 };
356 mmove_t zboss_move_pain2 = {FRAME_pain2Start, FRAME_pain2End, zboss_frames_pain2, zboss_run};
357
358
359
360 mframe_t zboss_frames_pain3 [] =
361 {
362 ai_move, 0, NULL, // 193
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, // 202
372
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, // 212
383
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 // 217
389 };
390 mmove_t zboss_move_pain3 = {FRAME_pain3Start, FRAME_pain3End, zboss_frames_pain3, zboss_run};
391
392
393
394
zboss_pain(edict_t * self,edict_t * other,float kick,int damage)395 void zboss_pain (edict_t *self, edict_t *other, float kick, int damage)
396 {
397 float r;
398 float hbreak = (self->max_health / 3.0);
399
400 // set the skin
401 if (self->health < hbreak)
402 {
403 self->s.skinnum = 2;
404 }
405 else if (self->health < hbreak * 2)
406 {
407 self->s.skinnum = 1;
408 }
409 else
410 {
411 self->s.skinnum = 0;
412 }
413
414 r = random();
415 if(r < 0.125)
416 {
417 gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
418 }
419 else if(r < 0.25)
420 {
421 gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
422 }
423 else if(r < 0.375)
424 {
425 gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
426 }
427 else if(r < 0.5)
428 {
429 gi.sound (self, CHAN_VOICE, sound_taunt1, 1, ATTN_NORM, 0);
430 }
431 else if(r < 0.625)
432 {
433 gi.sound (self, CHAN_VOICE, sound_taunt2, 1, ATTN_NORM, 0);
434 }
435 else if(r < 0.75)
436 {
437 gi.sound (self, CHAN_VOICE, sound_taunt3, 1, ATTN_NORM, 0);
438 }
439
440 if(self->bossFireCount && self->bossFireTimeout < level.time)
441 {
442 self->bossFireCount = 0;
443 }
444
445 if(self->bossFireCount > 40 && self->bossFireTimeout > level.time)
446 {
447 // that's it, we are pissed...
448 if(self->zDistance < level.time)
449 {
450 fire_empnuke(self, self->s.origin, 1024);
451 self->zDistance = level.time + 30 + (random() * 5);
452 }
453
454 zboss_attack(self);
455 self->bossFireCount = 0;
456 self->bossFireTimeout = 0;
457 return;
458 }
459
460 self->bossFireCount++;
461 self->bossFireTimeout = level.time + 1;
462
463
464 if(self->health < (self->max_health / 4) && self->zDistance < level.time)
465 {
466 fire_empnuke(self, self->s.origin, 1024);
467 self->zDistance = level.time + 30 + (random() * 5);
468 }
469
470 if (level.time < self->pain_debounce_time)
471 return;
472
473 self->pain_debounce_time = level.time + 5;
474
475 if (skill->value == 3)
476 return; // no pain anims in nightmare
477
478 if(self->laser)
479 return; // while hook is out.
480
481 r = random();
482
483 if(damage > 150 && r < 0.33)
484 {
485 self->monsterinfo.currentmove = &zboss_move_pain3;
486 }
487 else if(damage > 80 && r < 0.66)
488 {
489 self->monsterinfo.currentmove = &zboss_move_pain2;
490 }
491 else if(r < 0.60)
492 {
493 self->monsterinfo.currentmove = &zboss_move_pain1;
494 }
495 }
496
497 //
498 // MELEE
499 //
500
501
zboss_swing(edict_t * self)502 void zboss_swing (edict_t *self)
503 {
504 static vec3_t aim = {MELEE_DISTANCE, 0, -24};
505 fire_hit (self, aim, (15 + (rand() % 6)), 800);
506 }
507
508
509 mframe_t zboss_frames_attack2c [] =
510 {
511 ai_charge, 0, NULL, // 110
512 ai_charge, 0, zboss_swing,
513 ai_charge, 0, NULL,
514 ai_charge, 0, NULL,
515 ai_charge, 0, possibleBossTaunt,
516 ai_charge, 0, NULL,
517 ai_charge, 0, NULL,
518 ai_charge, 0, zboss_swing,
519 ai_charge, 0, NULL // 118
520 };
521 mmove_t zboss_move_attack2c = {FRAME_attack2cStart, FRAME_attack2cEnd, zboss_frames_attack2c, zboss_posthook};
522
523
524
525
zboss_melee2(edict_t * self)526 void zboss_melee2 (edict_t *self)
527 {
528 self->monsterinfo.currentmove = &zboss_move_attack2c;
529 gi.sound (self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0);
530 }
531
532
533 mframe_t zboss_frames_premelee [] =
534 {
535 ai_charge, 0, NULL, // 57
536 ai_charge, 0, NULL,
537 ai_charge, 0, NULL,
538 ai_charge, 0, NULL,
539 ai_charge, 0, NULL,
540 ai_charge, 0, possibleBossTaunt,
541 ai_charge, 0, NULL,
542 ai_charge, 0, NULL,
543 ai_charge, 0, NULL,
544 ai_charge, 0, NULL, // 66
545 };
546 mmove_t zboss_move_premelee = {FRAME_preHookStart, FRAME_preHookEnd, zboss_frames_premelee, zboss_melee2 };
547
548 // todo, pickup player, and throw...
549
550
zboss_melee(edict_t * self)551 void zboss_melee (edict_t *self)
552 {
553 gi.sound (self, CHAN_BODY, sound_raisegun, 1, ATTN_NORM, 0);
554 self->monsterinfo.currentmove = &zboss_move_premelee;
555 }
556
557
558
559 //
560 // ATTACK
561 //
562
563
564
565
566 // Rocket / Hook
567
568
569
570 // Rocket attack
571
572 mframe_t zboss_frames_attack1b [] =
573 {
574 ai_charge, 0, NULL, // 92
575 ai_charge, 0, NULL,
576 ai_charge, 0, NULL,
577 ai_charge, 0, NULL,
578 ai_charge, 0, NULL,
579 ai_charge, 0, NULL,
580 ai_charge, 0, NULL // 98
581 };
582 mmove_t zboss_move_attack1b = {FRAME_attack1bStart, FRAME_attack1bEnd, zboss_frames_attack1b, zboss_chooseNextAttack };
583
584
zboss_reloadRockets(edict_t * self)585 void zboss_reloadRockets(edict_t *self)
586 {
587 self->monsterinfo.aiflags &= ~AI_ONESHOTTARGET;
588 self->monsterinfo.currentmove = &zboss_move_attack1b;
589 }
590
591
592 static vec3_t rocketoffset[] =
593 {
594 {-5, -50, 33},
595 {-5, -39, 27},
596 {-5, -39, 39},
597 {-5, -44, 27},
598 {-5, -44, 39},
599 {-5, -48, 29},
600 {-5, -48, 29},
601 };
602
603
604
FireFlare(edict_t * self)605 void FireFlare(edict_t *self)
606 {
607 vec3_t forward, right;
608 vec3_t start;
609 vec3_t dir;
610 vec3_t vec;
611
612 int offset = (self->s.frame - 71) / 3;
613
614 AngleVectors (self->s.angles, forward, right, NULL);
615
616 G_ProjectSource (self->s.origin, rocketoffset[offset], forward, right, start);
617
618 if(self->monsterinfo.aiflags & AI_ONESHOTTARGET)
619 {
620 VectorCopy( self->monsterinfo.shottarget, vec );
621 }
622 else
623 {
624 VectorCopy (self->enemy->s.origin, vec);
625 vec[2] += self->enemy->viewheight;
626 }
627
628 VectorSubtract (vec, start, dir);
629 VectorNormalize (dir);
630
631 if(!(self->monsterinfo.aiflags & AI_ONESHOTTARGET))
632 {
633 ANIM_AIM(self, dir);
634 }
635 fire_flare (self, start, dir, 10, 1000, 10, 10);
636
637 // play shooting sound
638 gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0);
639 }
640
641
642
FireRocket(edict_t * self)643 void FireRocket(edict_t *self)
644 {
645 vec3_t forward, right;
646 vec3_t start;
647 vec3_t dir;
648 vec3_t vec;
649
650 int offset = (self->s.frame - 71) / 3;
651
652 AngleVectors (self->s.angles, forward, right, NULL);
653
654 G_ProjectSource (self->s.origin, rocketoffset[offset], forward, right, start);
655
656 if(self->monsterinfo.aiflags & AI_ONESHOTTARGET)
657 {
658 VectorCopy( self->monsterinfo.shottarget, vec );
659 }
660 else
661 {
662 VectorCopy (self->enemy->s.origin, vec);
663 vec[2] += self->enemy->viewheight;
664 }
665
666 vec[0] += (100 - (200 * random()));
667 vec[1] += (100 - (200 * random()));
668 vec[2] += (40 - (80 * random()));
669 VectorSubtract (vec, start, dir);
670 VectorNormalize (dir);
671
672
673 // ANIM_AIM(self, dir);
674 fire_rocket (self, start, dir, 70, 500, 70+20, 70);
675
676 gi.WriteByte (svc_muzzleflash2);
677 gi.WriteShort (self - g_edicts);
678 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
679 gi.multicast (start, MULTICAST_PVS);
680 }
681
682
683
684
685
686 mframe_t zboss_frames_attack1a [] =
687 {
688 ai_charge, 0, FireFlare, // 71
689 ai_charge, 0, NULL,
690 ai_charge, 0, NULL,
691 ai_charge, 0, FireRocket,
692 ai_charge, 0, NULL,
693 ai_charge, 0, NULL,
694 ai_charge, 0, FireRocket,
695 ai_charge, 0, NULL,
696 ai_charge, 0, NULL,
697 ai_charge, 0, FireRocket,
698 ai_charge, 0, possibleBossTaunt,
699 ai_charge, 0, NULL,
700 ai_charge, 0, FireFlare,
701 ai_charge, 0, NULL,
702 ai_charge, 0, NULL,
703 ai_charge, 0, FireRocket,
704 ai_charge, 0, NULL,
705 ai_charge, 0, NULL,
706 ai_charge, 0, FireRocket,
707 ai_charge, 0, NULL,
708 ai_charge, 0, NULL, // 91
709 };
710 mmove_t zboss_move_attack1a = {FRAME_attack1aStart, FRAME_attack1aEnd, zboss_frames_attack1a, zboss_reloadRockets };
711
712
713
714
715
716
717 // hook
718
719
zboss_reelInGraaple2(edict_t * self)720 void zboss_reelInGraaple2(edict_t *self)
721 {
722 vec3_t vec, dir;
723 float length;
724 edict_t *enemy = self->laser->enemy;
725 vec3_t hookoffset = {-5, -24, 34};
726 vec3_t forward, right;
727
728 AngleVectors (self->s.angles, forward, right, NULL);
729 G_ProjectSource(self->s.origin, hookoffset, forward, right, vec);
730 VectorSubtract (vec, self->laser->s.origin, dir);
731 length = VectorLength (dir);
732
733 if(length <= 80 || (self->laser->think == HookDragThink && self->laser->powerarmor_time < level.time))
734 {
735 G_FreeEdict(self->laser);
736 self->laser = NULL;
737
738 self->s.modelindex3 = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
739
740 if(enemy)
741 {
742 VectorClear(enemy->velocity);
743 zboss_melee2(self);
744 }
745 else
746 {
747 zboss_chooseNextAttack(self);
748 }
749 }
750 else
751 {
752 zboss_reelInGraaple(self);
753 }
754 }
755
756
757 mframe_t zboss_frames_attack2b [] =
758 {
759 ai_charge, 0, NULL, // 107
760 ai_charge, 0, NULL,
761 ai_charge, 0, NULL // 109
762 };
763 mmove_t zboss_move_attack2b = {FRAME_attack2bStart, FRAME_attack2bEnd, zboss_frames_attack2b, zboss_reelInGraaple2 };
764
765
HookDragThink(edict_t * self)766 void HookDragThink (edict_t *self)
767 {
768 vec3_t dir, vec;
769 float length, speed;
770 vec3_t hookoffset = {-5, -24, 34};
771 vec3_t forward, right;
772 vec3_t offset = {0, 0, 0};
773
774 if(self->enemy && self->enemy->health > 0)
775 {
776 VectorCopy (self->enemy->s.origin, self->s.origin);
777 }
778
779 VectorSubtract (self->owner->s.origin, self->s.origin, dir);
780 length = VectorLength (dir);
781
782 AngleVectors (self->owner->s.angles, forward, right, NULL);
783 G_ProjectSource(self->owner->s.origin, hookoffset, forward, right, vec);
784
785 VectorSubtract (vec, self->s.origin, dir);
786 speed = VectorLength (dir);
787 VectorNormalize (dir);
788 /*
789 if(speed > 1000)
790 {
791 speed = 1000;
792 }
793 else if(speed < 500)
794 {
795 speed = (speed * 2) + 100;
796 }
797 */
798 speed = 1000;
799 VectorScale (dir, speed, self->velocity);
800
801 if(self->enemy && self->enemy->health > 0)
802 {
803 VectorCopy (self->velocity, self->enemy->velocity);
804 self->enemy->velocity[2] *= 1.3;
805 }
806
807 gi.WriteByte (svc_temp_entity);
808 #if 0 //def USE_GRAPPLE_CABLE
809 gi.WriteByte (TE_GRAPPLE_CABLE);
810 gi.WriteShort (self - g_edicts);
811 gi.WritePosition (self->s.origin);
812 gi.WritePosition (vec);
813 gi.WritePosition (offset);
814 #else
815 gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
816 gi.WriteShort (self - g_edicts);
817 gi.WritePosition (self->s.origin);
818 gi.WritePosition (vec);
819 #endif
820 gi.multicast (self->s.origin, MULTICAST_PVS);
821
822 self->nextthink = level.time + FRAMETIME;
823 }
824
825
HookTouch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)826 void HookTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
827 {
828 if (other == ent->owner)
829 return;
830
831 if (other->takedamage)
832 {
833 gi.sound (ent, CHAN_WEAPON, sound_hookimpact, 1, ATTN_NORM, 0);
834 T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_ROCKET);
835 }
836
837 if(other->client && other->health > 0)
838 { // alive... Let's drag the bastard back...
839 ent->enemy = other;
840 }
841
842 ent->powerarmor_time = level.time + 15;
843 VectorClear(ent->velocity);
844 ent->nextthink = level.time + FRAMETIME;
845 ent->think = HookDragThink;
846 ent->s.frame = 283;
847 }
848
849
HookThink(edict_t * self)850 void HookThink(edict_t *self)
851 {
852 vec3_t vec;
853 vec3_t hookoffset = {-3, -24, 34};
854 vec3_t forward, right;
855 vec3_t offset = {0, 0, 0};
856
857 if(self->powerarmor_time < level.time)
858 {
859 self->powerarmor_time = level.time + 15;
860 VectorClear(self->velocity);
861 self->enemy = NULL;
862 self->think = HookDragThink;
863 self->s.frame = 283;
864 }
865
866
867 AngleVectors (self->owner->s.angles, forward, right, NULL);
868 G_ProjectSource(self->owner->s.origin, hookoffset, forward, right, vec);
869
870 gi.WriteByte (svc_temp_entity);
871 #if 0 //def USE_GRAPPLE_CABLE
872 gi.WriteByte (TE_GRAPPLE_CABLE);
873 gi.WriteShort (self - g_edicts);
874 gi.WritePosition (self->s.origin);
875 gi.WritePosition (vec);
876 gi.WritePosition (offset);
877 #else
878 gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
879 gi.WriteShort (self - g_edicts);
880 gi.WritePosition (self->s.origin);
881 gi.WritePosition (vec);
882 #endif
883 gi.multicast (self->s.origin, MULTICAST_PVS);
884
885 self->nextthink = level.time + FRAMETIME;
886 }
887
888
FireHook(edict_t * self)889 void FireHook(edict_t *self)
890 {
891 vec3_t forward, right;
892 vec3_t start;
893 vec3_t dir;
894 vec3_t vec;
895 vec3_t hookoffset = {-1, -24, 34};
896 edict_t *hook;
897 float speed;
898
899 AngleVectors (self->s.angles, forward, right, NULL);
900
901 G_ProjectSource (self->s.origin, hookoffset, forward, right, start);
902 VectorCopy (self->enemy->s.origin, vec);
903 vec[2] += self->enemy->viewheight;
904 VectorSubtract (vec, start, dir);
905 VectorNormalize (dir);
906
907 ANIM_AIM(self, dir);
908
909 self->s.modelindex3 = 0;
910
911 speed = 1000;
912
913 gi.sound (self, CHAN_WEAPON, sound_hooklaunch, 1, ATTN_NORM, 0);
914
915 self->laser = hook = G_Spawn();
916 VectorCopy (start, hook->s.origin);
917 VectorCopy (dir, hook->movedir);
918 vectoangles (dir, hook->s.angles);
919 VectorScale (dir, speed, hook->velocity);
920 hook->movetype = MOVETYPE_FLYMISSILE;
921 hook->clipmask = MASK_SHOT;
922 hook->solid = SOLID_BBOX;
923 VectorClear (hook->mins);
924 VectorClear (hook->maxs);
925 hook->s.modelindex = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
926 hook->s.frame = 282;
927 hook->owner = self;
928 hook->touch = HookTouch;
929 hook->powerarmor_time = level.time + 8000 / speed;
930 hook->nextthink = level.time + FRAMETIME;
931 hook->think = HookThink;
932 hook->s.sound = sound_hookfly; // replace...
933 hook->classname = "bosshook";
934
935 gi.linkentity (hook);
936 }
937
938
zboss_reelInGraaple(edict_t * self)939 void zboss_reelInGraaple(edict_t *self)
940 {
941 self->monsterinfo.currentmove = &zboss_move_attack2b;
942 }
943
944
945 mframe_t zboss_frames_attack2a [] =
946 {
947 ai_charge, 0, NULL, // 99
948 ai_charge, 0, NULL,
949 ai_charge, 0, NULL,
950 ai_charge, 0, possibleBossTaunt,
951 ai_charge, 0, NULL,
952 ai_charge, 0, FireHook, // 104
953 ai_charge, 0, NULL,
954 ai_charge, 0, NULL, // 106
955 };
956 mmove_t zboss_move_attack2a = {FRAME_attack2aStart, FRAME_attack2aEnd, zboss_frames_attack2a, zboss_reelInGraaple };
957
958
959
960
961
962 mframe_t zboss_frames_posthook [] =
963 {
964 ai_charge, 0, NULL, // 136
965 ai_charge, 0, NULL,
966 ai_charge, 0, NULL,
967 ai_charge, 0, NULL,
968 ai_charge, 0, NULL,
969 ai_charge, 0, NULL, // 141
970 };
971 mmove_t zboss_move_posthook = {FRAME_postHookStart, FRAME_postHookEnd, zboss_frames_posthook, zboss_run };
972
973
zboss_posthook(edict_t * self)974 void zboss_posthook(edict_t *self)
975 {
976 self->monsterinfo.currentmove = &zboss_move_posthook;
977 }
978
979
980
981
zboss_chooseHookRocket(edict_t * self)982 void zboss_chooseHookRocket(edict_t *self)
983 {
984 if(random() < 0.2 && !(self->monsterinfo.aiflags & AI_ONESHOTTARGET))
985 {
986 self->monsterinfo.currentmove = &zboss_move_attack2a;
987 }
988 else
989 {
990 self->monsterinfo.currentmove = &zboss_move_attack1a;
991 }
992 }
993
994
995 mframe_t zboss_frames_prehook [] =
996 {
997 ai_charge, 0, NULL, // 57
998 ai_charge, 0, NULL,
999 ai_charge, 0, NULL,
1000 ai_charge, 0, NULL,
1001 ai_charge, 0, NULL,
1002 ai_charge, 0, NULL,
1003 ai_charge, 0, NULL,
1004 ai_charge, 0, NULL,
1005 ai_charge, 0, NULL,
1006 ai_charge, 0, NULL, // 66
1007 };
1008 mmove_t zboss_move_prehook = {FRAME_preHookStart, FRAME_preHookEnd, zboss_frames_prehook, zboss_chooseHookRocket };
1009
1010
1011
1012
1013
1014 // Plasma Cannon
1015
1016
PlasmaballBlastAnim(edict_t * ent)1017 void PlasmaballBlastAnim(edict_t *ent)
1018 {
1019 ent->s.frame++;
1020 ent->s.skinnum++;
1021
1022 if(ent->s.frame > 1)
1023 {
1024 G_FreeEdict(ent);
1025 return;
1026 }
1027 else
1028 {
1029 ent->nextthink = level.time + FRAMETIME;
1030 }
1031 }
1032
1033
Plasmaball_Explode(edict_t * ent)1034 void Plasmaball_Explode (edict_t *ent)
1035 {
1036 //FIXME: if we are onground then raise our Z just a bit since we are a point?
1037 if (ent->enemy)
1038 {
1039 float points;
1040 vec3_t v;
1041 vec3_t dir;
1042
1043 VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
1044 VectorMA (ent->enemy->s.origin, 0.5, v, v);
1045 VectorSubtract (ent->s.origin, v, v);
1046 points = ent->dmg - 0.5 * VectorLength (v);
1047 VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
1048 T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, MOD_UNKNOWN);
1049 }
1050
1051 T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_UNKNOWN);
1052
1053 VectorMA (ent->s.origin, -0.02, ent->velocity, ent->s.origin);
1054 VectorClear(ent->velocity);
1055
1056 ent->movetype = MOVETYPE_NONE;
1057 ent->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
1058 ent->s.effects &= ~EF_BFG & ~EF_ANIM_ALLFAST;
1059 ent->s.frame = 0;
1060 ent->s.skinnum = 6;
1061 // ent->s.renderfx = RF_TRANSLUCENT | RF_FULLBRIGHT;
1062 // ent->s.renderfx = RF_TRANSLUCENT;
1063
1064 gi.sound (ent, CHAN_AUTO, sound_plamsaballexplode, 1, ATTN_NORM, 0);
1065
1066 ent->think = PlasmaballBlastAnim;
1067 ent->nextthink = level.time + FRAMETIME;
1068 }
1069
1070
Plasmaball_Touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1071 static void Plasmaball_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1072 {
1073 if (other == ent->owner)
1074 return;
1075
1076 if (surf && (surf->flags & SURF_SKY))
1077 {
1078 G_FreeEdict (ent);
1079 return;
1080 }
1081
1082 ent->enemy = other;
1083 Plasmaball_Explode (ent);
1084 }
1085
1086
fire_plasmaCannon(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,float timer,float damage_radius,float distance)1087 void fire_plasmaCannon (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, float distance)
1088 {
1089 edict_t *plasmaball;
1090 vec3_t dir;
1091 vec3_t forward, right, up;
1092
1093 vectoangles (aimdir, dir);
1094 AngleVectors (dir, forward, right, up);
1095
1096 plasmaball = G_Spawn();
1097 VectorCopy (start, plasmaball->s.origin);
1098 VectorScale (aimdir, speed, plasmaball->velocity);
1099 VectorMA (plasmaball->velocity, (distance - 500) + crandom() * 10.0, up, plasmaball->velocity);
1100 VectorMA (plasmaball->velocity, crandom() * 10.0, right, plasmaball->velocity);
1101 VectorSet (plasmaball->avelocity, 300, 300, 300);
1102 plasmaball->movetype = MOVETYPE_BOUNCE;
1103 plasmaball->clipmask = MASK_SHOT;
1104 plasmaball->solid = SOLID_BBOX;
1105 VectorClear (plasmaball->mins);
1106 VectorClear (plasmaball->maxs);
1107 plasmaball->s.modelindex = gi.modelindex ("sprites/plasma1.sp2");
1108 plasmaball->s.effects = EF_BFG | EF_ANIM_ALLFAST;
1109 plasmaball->owner = self;
1110 plasmaball->touch = Plasmaball_Touch;
1111 plasmaball->nextthink = level.time + timer;
1112 plasmaball->think = Plasmaball_Explode;
1113 plasmaball->dmg = damage;
1114 plasmaball->dmg_radius = damage_radius;
1115 plasmaball->classname = "plasmaball";
1116 plasmaball->s.sound = sound_plamsaballfly;
1117
1118 gi.sound (self, CHAN_AUTO, sound_plamsaballfire, 1, ATTN_NORM, 0);
1119 gi.linkentity (plasmaball);
1120 }
1121
1122
1123
1124 static vec3_t cannonoffset[] =
1125 {
1126 {-19, -44, 30},
1127 {-14, -33, 32},
1128 {-4 , -45, 32},
1129 {-2 , -34, 32},
1130 { 7, -49, 32},
1131 { 6, -36, 34},
1132 { 6, -36, 34},
1133 };
1134
1135
FireCannon(edict_t * self)1136 void FireCannon(edict_t *self)
1137 {
1138 vec3_t forward, right;
1139 vec3_t start;
1140 vec3_t dir;
1141 vec3_t vec;
1142 float distance;
1143
1144 int offset = (self->s.frame - 119) / 2;
1145
1146 AngleVectors (self->s.angles, forward, right, NULL);
1147
1148 G_ProjectSource (self->s.origin, cannonoffset[offset], forward, right, start);
1149
1150 if(self->monsterinfo.aiflags & AI_ONESHOTTARGET)
1151 {
1152 VectorCopy( self->monsterinfo.shottarget, vec );
1153 }
1154 else
1155 {
1156 VectorCopy (self->enemy->s.origin, vec);
1157 vec[2] += self->enemy->viewheight;
1158 }
1159
1160 if(self->timeout)
1161 {
1162 if(self->seq)
1163 {
1164 VectorNegate(right, right);
1165 }
1166 VectorMA (vec, self->timeout, right, vec);
1167 }
1168 self->timeout -= 50;
1169
1170 VectorSubtract (vec, start, dir);
1171 VectorNormalize (dir);
1172
1173 VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
1174 distance = VectorLength (vec);
1175
1176 if(distance < 700)
1177 {
1178 distance = 700;
1179 }
1180
1181 // ANIM_AIM(self, dir);
1182 if(skill->value < 2)
1183 {
1184 fire_plasmaCannon (self, start, dir, 90, 700, 2.5, 90+40, distance);
1185 }
1186 else if(skill->value < 3)
1187 {
1188 fire_plasmaCannon (self, start, dir, 90, (int)(distance * 1.2), 2.5, 90+40, distance);
1189 }
1190 else
1191 {
1192 fire_plasmaCannon (self, start, dir, 90, (int)(distance * 1.6), 2.5, 90+40, distance);
1193 }
1194 }
1195
1196
1197
1198 mframe_t zboss_frames_attack3 [] =
1199 {
1200 ai_charge, 0, FireCannon, // 119
1201 ai_charge, 0, NULL,
1202 ai_charge, 0, FireCannon, // 121
1203 ai_charge, 0, NULL,
1204 ai_charge, 0, FireCannon, // 123
1205 ai_charge, 0, NULL,
1206 ai_charge, 0, FireCannon, // 125
1207 ai_charge, 0, possibleBossTaunt,
1208 ai_charge, 0, FireCannon, // 127
1209 ai_charge, 0, NULL,
1210 ai_charge, 0, FireCannon, // 129
1211 ai_charge, 0, NULL,
1212 ai_charge, 0, FireCannon, // 131
1213 ai_charge, 0, NULL, // 132
1214 };
1215 mmove_t zboss_move_attack3 = {FRAME_attack3Start, FRAME_attack3End, zboss_frames_attack3, zboss_chooseNextAttack };
1216
1217
zboss_fireCannons(edict_t * self)1218 void zboss_fireCannons(edict_t *self)
1219 {
1220 self->monsterinfo.currentmove = &zboss_move_attack3;
1221
1222 // self->seq = (random() > 0.5);
1223 self->seq = 0;
1224 self->timeout = 150;
1225 }
1226
1227
1228 mframe_t zboss_frames_precannon [] =
1229 {
1230 ai_charge, 0, NULL, // 67
1231 ai_charge, 0, NULL,
1232 ai_charge, 0, NULL,
1233 ai_charge, 0, NULL, // 70
1234 };
1235 mmove_t zboss_move_precannon = {FRAME_preCannonStart, FRAME_preCannonEnd, zboss_frames_precannon, zboss_fireCannons };
1236
1237
1238
1239
1240 mframe_t zboss_frames_postcannon [] =
1241 {
1242 ai_charge, 0, NULL, // 133
1243 ai_charge, 0, NULL,
1244 ai_charge, 0, NULL, // 135
1245 };
1246 mmove_t zboss_move_postcannon = {FRAME_postCannonStart, FRAME_postCannonEnd, zboss_frames_postcannon, zboss_run };
1247
1248
1249
zboss_postcannon(edict_t * self)1250 void zboss_postcannon(edict_t *self)
1251 {
1252 self->monsterinfo.currentmove = &zboss_move_postcannon;
1253 }
1254
1255
1256
1257
1258 // switching in mid attack...
1259
1260
1261 mframe_t zboss_frames_h2c [] =
1262 {
1263 ai_charge, 0, NULL, // 142
1264 ai_charge, 0, NULL,
1265 ai_charge, 0, NULL,
1266 ai_charge, 0, NULL,
1267 ai_charge, 0, NULL,
1268 ai_charge, 0, NULL, // 147
1269 };
1270 mmove_t zboss_move_h2c = {FRAME_attackH2CStart, FRAME_attackH2CEnd, zboss_frames_h2c, zboss_fireCannons };
1271
1272
1273 mframe_t zboss_frames_c2h [] =
1274 {
1275 ai_charge, 0, NULL, // 148
1276 ai_charge, 0, NULL,
1277 ai_charge, 0, NULL,
1278 ai_charge, 0, possibleBossTaunt,
1279 ai_charge, 0, NULL,
1280 ai_charge, 0, NULL, // 153
1281 };
1282 mmove_t zboss_move_c2h = {FRAME_attackC2HStart, FRAME_attackC2HEnd, zboss_frames_c2h, zboss_chooseHookRocket };
1283
1284
1285
1286
zboss_chooseNextAttack(edict_t * self)1287 void zboss_chooseNextAttack(edict_t *self)
1288 {
1289 if (self->enemy == NULL)
1290 return;
1291
1292 self->monsterinfo.aiflags &= ~AI_ONESHOTTARGET;
1293
1294 if(random() < 0.5 && self->enemy)
1295 {
1296 if(random() < 0.4)
1297 {
1298 if(self->monsterinfo.currentmove == &zboss_move_attack3)
1299 {
1300 gi.sound (self, CHAN_BODY, sound_switchattacks, 1, ATTN_NORM, 0);
1301 self->monsterinfo.currentmove = &zboss_move_c2h;
1302 }
1303 else
1304 {
1305 zboss_chooseHookRocket(self);
1306 }
1307 }
1308 else
1309 {
1310 if(self->monsterinfo.currentmove == &zboss_move_attack3)
1311 {
1312 zboss_fireCannons(self);
1313 }
1314 else
1315 {
1316 gi.sound (self, CHAN_BODY, sound_switchattacks, 1, ATTN_NORM, 0);
1317 self->monsterinfo.currentmove = &zboss_move_h2c;
1318 }
1319 }
1320 }
1321 else
1322 {
1323 gi.sound (self, CHAN_BODY, sound_lowergun, 1, ATTN_NORM, 0);
1324
1325 if(self->monsterinfo.currentmove == &zboss_move_attack3)
1326 {
1327 zboss_postcannon(self);
1328 }
1329 else
1330 {
1331 zboss_posthook(self);
1332 }
1333 }
1334 }
1335
1336
1337
1338
zboss_attack(edict_t * self)1339 void zboss_attack (edict_t *self)
1340 {
1341 if (self->enemy == NULL)
1342 return;
1343
1344 gi.sound (self, CHAN_BODY, sound_raisegun, 1, ATTN_NORM, 0);
1345
1346 if(random() < 0.4)
1347 {
1348 self->monsterinfo.currentmove = &zboss_move_prehook;
1349 }
1350 else
1351 {
1352 self->monsterinfo.currentmove = &zboss_move_precannon;
1353 }
1354 }
1355
1356
1357
1358
1359
1360 /*
1361 ===
1362 Death Stuff Starts
1363 ===
1364 */
1365
zboss_dead(edict_t * self)1366 void zboss_dead (edict_t *self)
1367 {
1368 VectorSet (self->mins, -32, -74, -30);
1369 VectorSet (self->maxs, 32, 40, 12);
1370 self->movetype = MOVETYPE_TOSS;
1371 self->svflags |= SVF_DEADMONSTER;
1372 self->nextthink = 0;
1373 gi.linkentity (self);
1374 }
1375
1376
1377 mframe_t zboss_frames_death1 [] =
1378 {
1379 ai_move, 0, NULL, // 218
1380 ai_move, 0, NULL,
1381 ai_move, 0, NULL,
1382 ai_move, 0, NULL,
1383 ai_move, 0, NULL,
1384 ai_move, 0, NULL,
1385 ai_move, 0, NULL,
1386 ai_move, 0, NULL,
1387 ai_move, 0, NULL,
1388 ai_move, 0, NULL, // 227
1389
1390 ai_move, 0, NULL, // 228
1391 ai_move, 0, NULL,
1392 ai_move, 0, NULL,
1393 ai_move, 0, NULL,
1394 ai_move, 0, NULL,
1395 ai_move, 0, NULL,
1396 ai_move, 0, NULL,
1397 ai_move, 0, NULL,
1398 ai_move, 0, NULL, // 236
1399 };
1400 mmove_t zboss_move_death1 = {FRAME_die1Start, FRAME_die1End, zboss_frames_death1, zboss_dead};
1401
1402
1403
FireDeadRocket1(edict_t * self)1404 void FireDeadRocket1(edict_t *self)
1405 {
1406 vec3_t forward, right;
1407 vec3_t start;
1408 vec3_t rocketoffset = {-26, -26, 25};
1409
1410 AngleVectors (self->s.angles, forward, right, NULL);
1411
1412 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1413
1414 fire_rocket (self, start, forward, 70, 500, 70+20, 70);
1415
1416 gi.WriteByte (svc_muzzleflash2);
1417 gi.WriteShort (self - g_edicts);
1418 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1419 gi.multicast (start, MULTICAST_PVS);
1420 }
1421
1422
FireDeadRocket2(edict_t * self)1423 void FireDeadRocket2(edict_t *self)
1424 {
1425 vec3_t forward, right;
1426 vec3_t start;
1427 vec3_t rocketoffset = {-16, -21, 20};
1428
1429 AngleVectors (self->s.angles, forward, right, NULL);
1430
1431 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1432
1433 forward[1] += 10;
1434 fire_rocket (self, start, forward, 70, 500, 70+20, 70);
1435
1436 gi.WriteByte (svc_muzzleflash2);
1437 gi.WriteShort (self - g_edicts);
1438 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1439 gi.multicast (start, MULTICAST_PVS);
1440 }
1441
1442
FireDeadRocket3(edict_t * self)1443 void FireDeadRocket3(edict_t *self)
1444 {
1445 vec3_t forward, right, up;
1446 vec3_t start;
1447 vec3_t rocketoffset = {-17, -20, 30};
1448
1449 AngleVectors (self->s.angles, forward, right, up);
1450
1451 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1452
1453 fire_rocket (self, start, up, 70, 500, 70+20, 70);
1454
1455 gi.WriteByte (svc_muzzleflash2);
1456 gi.WriteShort (self - g_edicts);
1457 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1458 gi.multicast (start, MULTICAST_PVS);
1459 }
1460
1461
1462
FireDeadRocket4(edict_t * self)1463 void FireDeadRocket4(edict_t *self)
1464 {
1465 vec3_t forward, right, up;
1466 vec3_t start;
1467 vec3_t rocketoffset = {-8, -16, 17};
1468
1469 AngleVectors (self->s.angles, forward, right, up);
1470
1471 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1472
1473 fire_rocket (self, start, up, 70, 500, 70+20, 70);
1474
1475 gi.WriteByte (svc_muzzleflash2);
1476 gi.WriteShort (self - g_edicts);
1477 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1478 gi.multicast (start, MULTICAST_PVS);
1479 }
1480
1481
FireDeadRocket5(edict_t * self)1482 void FireDeadRocket5(edict_t *self)
1483 {
1484 vec3_t forward, right, up;
1485 vec3_t start;
1486 vec3_t rocketoffset = {-10, -16, 30};
1487
1488 AngleVectors (self->s.angles, forward, right, up);
1489
1490 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1491 VectorNegate(forward, forward);
1492
1493 fire_rocket (self, start, forward, 70, 500, 70+20, 70);
1494
1495 gi.WriteByte (svc_muzzleflash2);
1496 gi.WriteShort (self - g_edicts);
1497 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1498 gi.multicast (start, MULTICAST_PVS);
1499 }
1500
FireDeadRocket6(edict_t * self)1501 void FireDeadRocket6(edict_t *self)
1502 {
1503 vec3_t forward, right, up;
1504 vec3_t start;
1505 vec3_t rocketoffset = {0, -18, 25};
1506
1507 AngleVectors (self->s.angles, forward, right, up);
1508
1509 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1510 VectorNegate(forward, forward);
1511 forward[1] -= 10;
1512
1513 fire_rocket (self, start, forward, 70, 500, 70+20, 70);
1514
1515 gi.WriteByte (svc_muzzleflash2);
1516 gi.WriteShort (self - g_edicts);
1517 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1518 gi.multicast (start, MULTICAST_PVS);
1519 }
1520
1521
FireDeadRocket7(edict_t * self)1522 void FireDeadRocket7(edict_t *self)
1523 {
1524 vec3_t forward, right, up;
1525 vec3_t start;
1526 vec3_t rocketoffset = {17, -27, 30};
1527
1528 AngleVectors (self->s.angles, forward, right, up);
1529
1530 G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
1531 VectorNegate(forward, forward);
1532 forward[1] -= 10;
1533
1534 fire_rocket (self, start, forward, 70, 500, 70+20, 70);
1535
1536 gi.WriteByte (svc_muzzleflash2);
1537 gi.WriteShort (self - g_edicts);
1538 gi.WriteByte (MZ2_BOSS2_ROCKET_1);
1539 gi.multicast (start, MULTICAST_PVS);
1540 }
1541
FireDeadCannon1(edict_t * self)1542 void FireDeadCannon1(edict_t *self)
1543 {
1544 vec3_t forward, right;
1545 vec3_t start;
1546 vec3_t cannonoffset = {9, -46, 33};
1547
1548 AngleVectors (self->s.angles, forward, right, NULL);
1549
1550 G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
1551
1552 fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
1553
1554 gi.WriteByte (svc_muzzleflash2);
1555 gi.WriteShort (self - g_edicts);
1556 gi.WriteByte (MZ2_GUNNER_GRENADE_1);
1557 gi.multicast (start, MULTICAST_PVS);
1558 }
1559
1560
FireDeadCannon2(edict_t * self)1561 void FireDeadCannon2(edict_t *self)
1562 {
1563 vec3_t forward, right;
1564 vec3_t start;
1565 vec3_t cannonoffset = {3, -31, 37};
1566
1567 AngleVectors (self->s.angles, forward, right, NULL);
1568
1569 G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
1570
1571 fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
1572
1573 gi.WriteByte (svc_muzzleflash2);
1574 gi.WriteShort (self - g_edicts);
1575 gi.WriteByte (MZ2_GUNNER_GRENADE_1);
1576 gi.multicast (start, MULTICAST_PVS);
1577 }
1578
1579
FireDeadCannon3(edict_t * self)1580 void FireDeadCannon3(edict_t *self)
1581 {
1582 vec3_t forward, right;
1583 vec3_t start;
1584 vec3_t cannonoffset = {-21, -19, 24};
1585
1586 AngleVectors (self->s.angles, forward, right, NULL);
1587
1588 G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
1589
1590 fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
1591
1592 gi.WriteByte (svc_muzzleflash2);
1593 gi.WriteShort (self - g_edicts);
1594 gi.WriteByte (MZ2_GUNNER_GRENADE_1);
1595 gi.multicast (start, MULTICAST_PVS);
1596 }
1597
1598
1599
DeadHookTouch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1600 void DeadHookTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1601 {
1602 if (other == ent->owner)
1603 return;
1604
1605 if (other->takedamage)
1606 {
1607 gi.sound (ent, CHAN_WEAPON, sound_hookimpact, 1, ATTN_NORM, 0);
1608 T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_ROCKET);
1609 }
1610
1611 G_FreeEdict(ent);
1612 }
1613
FireDeadGrapple(edict_t * self)1614 void FireDeadGrapple(edict_t *self)
1615 {
1616 vec3_t forward, right, up;
1617 vec3_t start;
1618 vec3_t hookoffset = {-35, 8, 28};
1619 edict_t *hook;
1620 float speed;
1621
1622 if(self->s.modelindex3 == 0) // hook already out...
1623 return;
1624
1625 AngleVectors (self->s.angles, forward, right, up);
1626
1627 G_ProjectSource (self->s.origin, hookoffset, forward, right, start);
1628
1629 self->s.modelindex3 = 0;
1630
1631 speed = 500;
1632
1633 gi.sound (self, CHAN_WEAPON, sound_hooklaunch, 1, ATTN_NORM, 0);
1634
1635 hook = G_Spawn();
1636 VectorCopy (start, hook->s.origin);
1637 VectorCopy (up, hook->movedir);
1638 vectoangles (up, hook->s.angles);
1639 VectorScale (up, speed, hook->velocity);
1640 hook->movetype = MOVETYPE_FLYMISSILE;
1641 hook->clipmask = MASK_SHOT;
1642 hook->solid = SOLID_BBOX;
1643 VectorClear (hook->mins);
1644 VectorClear (hook->maxs);
1645 hook->s.modelindex = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
1646 hook->s.frame = 282;
1647 hook->owner = self;
1648 hook->touch = DeadHookTouch;
1649 hook->nextthink = level.time + 8000 / speed;
1650 hook->think = G_FreeEdict;
1651 hook->s.sound = sound_hookfly; // replace...
1652 hook->classname = "bosshook";
1653
1654 gi.linkentity (hook);
1655 }
1656
1657
1658
1659 mframe_t zboss_frames_death2 [] =
1660 {
1661 ai_move, 0, NULL, // 237
1662 ai_move, 0, NULL,
1663 ai_move, 0, NULL,
1664 ai_move, 0, NULL,
1665 ai_move, 0, NULL,
1666 ai_move, 0, NULL,
1667 ai_move, 0, NULL,
1668 ai_move, 0, NULL,
1669 ai_move, 0, NULL,
1670 ai_move, 0, NULL, // 246
1671
1672 ai_move, 0, NULL, // 247
1673 ai_move, 0, NULL,
1674 ai_move, 0, FireDeadRocket1, // 249
1675 ai_move, 0, FireDeadRocket2, // 250
1676 ai_move, 0, FireDeadRocket3, // 251
1677 ai_move, 0, FireDeadRocket4, // 252
1678 ai_move, 0, FireDeadRocket5, // 253
1679 ai_move, 0, FireDeadRocket6, // 254
1680 ai_move, 0, FireDeadRocket7, // 255
1681 ai_move, 0, NULL, // 256
1682
1683 ai_move, 0, FireDeadCannon1, // 257
1684 ai_move, 0, FireDeadCannon2, // 258
1685 ai_move, 0, NULL,
1686 ai_move, 0, NULL,
1687 ai_move, 0, NULL,
1688 ai_move, 0, NULL,
1689 ai_move, 0, NULL,
1690 ai_move, 0, FireDeadCannon3, // 264
1691 ai_move, 0, NULL,
1692 ai_move, 0, NULL, // 266
1693
1694 ai_move, 0, NULL, // 267
1695 ai_move, 0, NULL,
1696 ai_move, 0, NULL,
1697 ai_move, 0, NULL,
1698 ai_move, 0, NULL,
1699 ai_move, 0, NULL,
1700 ai_move, 0, NULL,
1701 ai_move, 0, NULL,
1702 ai_move, 0, NULL,
1703 ai_move, 0, NULL, // 276
1704
1705 ai_move, 0, NULL, // 277
1706 ai_move, 0, NULL,
1707 ai_move, 0, NULL,
1708 ai_move, 0, NULL,
1709 ai_move, 0, FireDeadGrapple, // 281
1710 };
1711 mmove_t zboss_move_death2 = {FRAME_die2Start, FRAME_die2End, zboss_frames_death2, zboss_dead};
1712
1713
zboss_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1714 void zboss_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1715 {
1716 int n;
1717
1718 if(self->laser)
1719 {
1720 G_FreeEdict(self->laser);
1721 self->laser = NULL;
1722 }
1723
1724 // check for gib
1725 if (self->health <= self->gib_health)
1726 {
1727 self->s.modelindex2 = 0;
1728 self->s.modelindex3 = 0;
1729
1730 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1731 for (n= 0; n < 2; n++)
1732 ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
1733 for (n= 0; n < 4; n++)
1734 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1735 ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
1736 self->deadflag = DEAD_DEAD;
1737 return;
1738 }
1739
1740 if (self->deadflag == DEAD_DEAD)
1741 return;
1742
1743 // regular death
1744 self->deadflag = DEAD_DEAD;
1745 self->takedamage = DAMAGE_YES;
1746
1747 // todo
1748 if (random() < 0.5)
1749 {
1750 gi.sound (self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0);
1751 self->monsterinfo.currentmove = &zboss_move_death1;
1752 }
1753 else
1754 {
1755 gi.sound (self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
1756 self->monsterinfo.currentmove = &zboss_move_death2;
1757 }
1758 }
1759
1760
1761 /*
1762 ===
1763 End Death Stuff
1764 ===
1765 */
1766
SP_monster_zboss_precache(void)1767 void SP_monster_zboss_precache(void)
1768 {
1769 sound_pain1 = gi.soundindex ("monsters/bossz/bpain1.wav");
1770 sound_pain2 = gi.soundindex ("monsters/bossz/bpain2.wav");
1771 sound_pain3 = gi.soundindex ("monsters/bossz/bpain3.wav");
1772 sound_die1 = gi.soundindex ("monsters/bossz/bdeth1.wav");
1773 sound_die2 = gi.soundindex ("monsters/bossz/bdeth2.wav");
1774 sound_hooklaunch = gi.soundindex("monsters/bossz/bhlaunch.wav");
1775 sound_hookimpact = gi.soundindex("monsters/bossz/bhimpact.wav");
1776 sound_hookfly = gi.soundindex("monsters/bossz/bhfly.wav");
1777 sound_sight = gi.soundindex("monsters/bossz/bsight1.wav");
1778 sound_swing = gi.soundindex("monsters/bossz/bswing.wav");
1779 sound_idle1 = gi.soundindex("monsters/bossz/bidle1.wav");
1780 sound_idle2 = gi.soundindex("monsters/bossz/bidle2.wav");
1781 sound_walk = gi.soundindex("monsters/bossz/bwalk.wav");
1782 sound_raisegun = gi.soundindex("monsters/bossz/braisegun.wav");
1783 sound_lowergun = gi.soundindex("monsters/bossz/blowergun.wav");
1784 sound_switchattacks = gi.soundindex("monsters/bossz/bswitch.wav");
1785 sound_plamsaballfly = gi.soundindex("monsters/bossz/bpbfly.wav");
1786 sound_plamsaballexplode = gi.soundindex("monsters/bossz/bpbexplode.wav");
1787 sound_plamsaballfire = gi.soundindex("monsters/bossz/bpbfire.wav");
1788 sound_taunt1 = gi.soundindex("monsters/bossz/btaunt1.wav");
1789 sound_taunt2 = gi.soundindex("monsters/bossz/btaunt2.wav");
1790 sound_taunt3 = gi.soundindex("monsters/bossz/btaunt3.wav");
1791 }
1792
1793
1794 /*QUAKED monster_zboss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
1795 */
SP_monster_zboss(edict_t * self)1796 void SP_monster_zboss (edict_t *self)
1797 {
1798 if (deathmatch->value)
1799 {
1800 G_FreeEdict (self);
1801 return;
1802 }
1803
1804 SP_monster_zboss_precache();
1805
1806 // precache some models and sounds
1807 gi.modelindex("sprites/plasma1.sp2");
1808 gi.modelindex("models/objects/b_explode/tris.md2");
1809 gi.soundindex("items/empnuke/emp_trg.wav");
1810
1811 self->s.modelindex = gi.modelindex ("models/monsters/bossz/mech/tris.md2");
1812 self->s.modelindex2 = gi.modelindex ("models/monsters/bossz/pilot/tris.md2");
1813 self->s.modelindex3 = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
1814 VectorSet (self->mins, -32, -74, -30);
1815 VectorSet (self->maxs, 32, 50, 74);
1816 self->movetype = MOVETYPE_STEP;
1817 self->solid = SOLID_BBOX;
1818 self->monsterinfo.aiflags = AI_MONREDUCEDDAMAGE;
1819 self->monsterinfo.reducedDamageAmount = 0.25;
1820
1821 if(skill->value == 0)
1822 {
1823 self->health = 3000;
1824 }
1825 else if(skill->value == 1)
1826 {
1827 self->health = 4500;
1828 }
1829 else if(skill->value == 2)
1830 {
1831 self->health = 6000;
1832 }
1833 else
1834 {
1835 self->health = 8000;
1836 }
1837
1838 self->gib_health = -700;
1839 self->mass = 1000;
1840
1841 self->pain = zboss_pain;
1842 self->die = zboss_die;
1843
1844 self->monsterinfo.stand = zboss_stand;
1845 self->monsterinfo.walk = zboss_walk;
1846 self->monsterinfo.run = zboss_run;
1847 self->monsterinfo.attack = zboss_attack;
1848 self->monsterinfo.melee = zboss_melee;
1849 self->monsterinfo.sight = zboss_sight;
1850 self->monsterinfo.idle = possibleBossTaunt;
1851
1852 gi.linkentity (self);
1853
1854 self->monsterinfo.currentmove = &zboss_stand1;
1855 self->monsterinfo.scale = MODEL_SCALE;
1856
1857 walkmonster_start (self);
1858 }
1859
1860
1861
1862 /*QUAKED target_zboss_target
1863 */
1864
trigger_zboss(edict_t * self,edict_t * other,edict_t * activator)1865 void trigger_zboss (edict_t *self, edict_t *other, edict_t *activator)
1866 {
1867 edict_t *boss = NULL;
1868
1869 while ((boss = G_Find (boss, FOFS(targetname), self->target)) != NULL)
1870 {
1871 if(boss->health > 0)
1872 {
1873 VectorCopy( self->s.origin, boss->monsterinfo.shottarget );
1874 boss->monsterinfo.aiflags |= AI_ONESHOTTARGET;
1875 boss->monsterinfo.attack(boss);
1876 }
1877 }
1878 }
1879
1880
SP_target_zboss_target(edict_t * self)1881 void SP_target_zboss_target(edict_t *self)
1882 {
1883 if(!self->target)
1884 {
1885 gi.dprintf("target_zboss_target does not have a target");
1886 G_FreeEdict (self);
1887 return;
1888 }
1889
1890 self->movetype = MOVETYPE_NONE;
1891 self->svflags |= SVF_NOCLIENT;
1892
1893 self->solid = SOLID_NOT;
1894 self->use = trigger_zboss;
1895
1896 gi.linkentity (self);
1897 }
1898