|
|
1.1 root 1: /*
2: ==============================================================================
3:
4: black widow, part 2
5:
6: ==============================================================================
7: */
8:
9: // timestamp used to prevent rapid fire of melee attack
10:
11: #include "g_local.h"
12: #include "m_widow2.h"
13:
14: #define NUM_STALKERS_SPAWNED 6 // max # of stalkers she can spawn
15:
16: #define DISRUPT_TIME 3
17:
18: static int sound_pain1;
19: static int sound_pain2;
20: static int sound_pain3;
21: static int sound_death;
22: static int sound_search1;
23: static int sound_disrupt;
24: static int sound_tentacles_retract;
25:
26: // sqrt(64*64*2) + sqrt(28*28*2) => 130.1
27: static vec3_t spawnpoints[] = {
28: {30, 135, 0},
29: {30, -135, 0}
30: };
31:
32: static float sweep_angles[] = {
33: -40.0, -32.0, -24.0, -16.0, -8.0, 0.0, 8.0, 16.0, 24.0, 32.0, 40.0
34: };
35:
36: extern vec3_t stalker_mins, stalker_maxs;
37:
38: qboolean infront (edict_t *self, edict_t *other);
39: void WidowCalcSlots (edict_t *self);
40: void WidowPowerups (edict_t *self);
41:
42: void widow2_run (edict_t *self);
43: void widow2_stand (edict_t *self);
44: void widow2_dead (edict_t *self);
45: void widow2_attack (edict_t *self);
46: void widow2_attack_beam (edict_t *self);
47: void widow2_reattack_beam (edict_t *self);
48: void widow2_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
49: void widow_start_spawn (edict_t *self);
50: void widow_done_spawn (edict_t *self);
51: void widow2_spawn_check (edict_t *self);
52: void widow2_prep_spawn (edict_t *self);
53: void Widow2SaveBeamTarget(edict_t *self);
54:
55: // death stuff
56: void WidowExplode (edict_t *self);
57: void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
58: void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
59: void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean large, int hitsound, qboolean fade);
60: void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade);
61: void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade);
62: void WidowExplosion1 (edict_t *self);
63: void WidowExplosion2 (edict_t *self);
64: void WidowExplosion3 (edict_t *self);
65: void WidowExplosion4 (edict_t *self);
66: void WidowExplosion5 (edict_t *self);
67: void WidowExplosion6 (edict_t *self);
68: void WidowExplosion7 (edict_t *self);
69: void WidowExplosionLeg (edict_t *self);
70: void ThrowArm1 (edict_t *self);
71: void ThrowArm2 (edict_t *self);
72: void ClipGibVelocity (edict_t *ent);
73: // end of death stuff
74:
75: // these offsets used by the tongue
76: static vec3_t offsets[] = {
77: {17.48, 0.10, 68.92},
78: {17.47, 0.29, 68.91},
79: {17.45, 0.53, 68.87},
80: {17.42, 0.78, 68.81},
81: {17.39, 1.02, 68.75},
82: {17.37, 1.20, 68.70},
83: {17.36, 1.24, 68.71},
84: {17.37, 1.21, 68.72},
85: };
86:
87: void showme (edict_t *self);
88:
89: void pauseme (edict_t *self)
90: {
91: self->monsterinfo.aiflags |= AI_HOLD_FRAME;
92: }
93:
94: void widow2_search (edict_t *self)
95: {
96: if (random() < 0.5)
97: gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
98: }
99:
100: void Widow2Beam (edict_t *self)
101: {
102: vec3_t forward, right, target;
103: vec3_t start, targ_angles, vec;
104: int flashnum;
105:
106: if ((!self->enemy) || (!self->enemy->inuse))
107: return;
108:
109: AngleVectors (self->s.angles, forward, right, NULL);
110:
111: if ((self->s.frame >= FRAME_fireb05) && (self->s.frame <= FRAME_fireb09))
112: {
113: // regular beam attack
114: Widow2SaveBeamTarget(self);
115: flashnum = MZ2_WIDOW2_BEAMER_1 + self->s.frame - FRAME_fireb05;
116: G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
117: VectorCopy (self->pos2, target);
118: target[2] += self->enemy->viewheight-10;
119: VectorSubtract (target, start, forward);
120: VectorNormalize (forward);
121: monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
122: }
123: else if ((self->s.frame >= FRAME_spawn04) && (self->s.frame <= FRAME_spawn14))
124: {
125: // sweep
126: flashnum = MZ2_WIDOW2_BEAM_SWEEP_1 + self->s.frame - FRAME_spawn04;
127: G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
128: VectorSubtract (self->enemy->s.origin, start, target);
129: vectoangles2 (target, targ_angles);
130:
131: VectorCopy (self->s.angles, vec);
132:
133: vec[PITCH] += targ_angles[PITCH];
134: vec[YAW] -= sweep_angles[flashnum-MZ2_WIDOW2_BEAM_SWEEP_1];
135:
136: AngleVectors (vec, forward, NULL, NULL);
137: monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
138: /*
139: if (self->s.frame == FRAME_spawn04)
140: {
141: VectorMA (start, 1024, forward, debugend);
142:
143: gi.WriteByte (svc_temp_entity);
144: gi.WriteByte (TE_DEBUGTRAIL);
145: gi.WritePosition (start);
146: gi.WritePosition (debugend);
147: gi.multicast (start, MULTICAST_ALL);
148:
149: drawbbox (self);
150: self->monsterinfo.aiflags |= AI_HOLD_FRAME|AI_MANUAL_STEERING;
151: }
152: */
153: }
154: else
155: {
156: if ((g_showlogic) && (g_showlogic->value))
157: gi.dprintf ("bad fire frame for widow2 beam -- tell me you saw this!\n");
158:
159: Widow2SaveBeamTarget(self);
160: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW2_BEAMER_1], forward, right, start);
161:
162: VectorCopy (self->pos2, target);
163: target[2] += self->enemy->viewheight-10;
164:
165: VectorSubtract (target, start, forward);
166: VectorNormalize (forward);
167:
168: monster_fire_heat (self, start, forward, vec3_origin, 10, 50, 0);
169: }
170: }
171:
172: void Widow2Spawn (edict_t *self)
173: {
174: vec3_t f, r, u, offset, startpoint, spawnpoint;
175: edict_t *ent, *designated_enemy;
176: int i;
177:
178: AngleVectors (self->s.angles, f, r, u);
179:
180: for (i=0; i < 2; i++)
181: {
182: VectorCopy (spawnpoints[i], offset);
183:
184: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
185:
186: if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
187: {
188: ent = CreateGroundMonster (spawnpoint, self->s.angles, stalker_mins, stalker_maxs, "monster_stalker", 256);
189:
190: if (!ent)
191: continue;
192:
193: self->monsterinfo.monster_used++;
194: ent->monsterinfo.commander = self;
195: if ((g_showlogic) && (g_showlogic->value))
196: gi.dprintf ("widow: post-spawn : %d slots left\n", SELF_SLOTS_LEFT);
197:
198: ent->nextthink = level.time;
199: ent->think (ent);
200:
201: ent->monsterinfo.aiflags |= AI_SPAWNED_WIDOW|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS;
202:
203: if (!(coop && coop->value))
204: {
205: designated_enemy = self->enemy;
206: }
207: else
208: {
209: designated_enemy = PickCoopTarget(ent);
210: if (designated_enemy)
211: {
212: // try to avoid using my enemy
213: if (designated_enemy == self->enemy)
214: {
215: designated_enemy = PickCoopTarget(ent);
216: if (designated_enemy)
217: {
218: if ((g_showlogic) && (g_showlogic->value))
219: {
220: gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
221: if (designated_enemy->client)
222: gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
223: else
224: gi.dprintf ("NOT A CLIENT\n");
225: }
226: }
227: else
228: {
229: if ((g_showlogic) && (g_showlogic->value))
230: gi.dprintf ("pick coop failed, using my current enemy\n");
231: designated_enemy = self->enemy;
232: }
233: }
234: }
235: else
236: {
237: if ((g_showlogic) && (g_showlogic->value))
238: gi.dprintf ("pick coop failed, using my current enemy\n");
239: designated_enemy = self->enemy;
240: }
241: }
242:
243: if ((designated_enemy->inuse) && (designated_enemy->health > 0))
244: {
245: ent->enemy = designated_enemy;
246: FoundTarget (ent);
247: ent->monsterinfo.attack(ent);
248: }
249: }
250: }
251: }
252:
253: void widow2_spawn_check (edict_t *self)
254: {
255: Widow2Beam(self);
256: Widow2Spawn (self);
257: }
258:
259: void widow2_ready_spawn (edict_t *self)
260: {
261: vec3_t f, r, u, offset, startpoint, spawnpoint;
262: int i;
263:
264: Widow2Beam(self);
265: AngleVectors (self->s.angles, f, r, u);
266:
267: for (i=0; i < 2; i++)
268: {
269: VectorCopy (spawnpoints[i], offset);
270: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
271: if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
272: {
273: SpawnGrow_Spawn (spawnpoint, 1);
274: }
275: }
276: }
277:
278: mframe_t widow2_frames_stand [] =
279: {
280: // ai_stand, 0, drawbbox
281: ai_stand, 0, NULL
282: };
283: mmove_t widow2_move_stand = {FRAME_blackwidow3, FRAME_blackwidow3, widow2_frames_stand, NULL};
284:
285: mframe_t widow2_frames_walk [] =
286: {
287: // ai_walk, 9.01, drawbbox,
288: ai_walk, 9.01, NULL,
289: ai_walk, 7.55, NULL,
290: ai_walk, 7.01, NULL,
291: ai_walk, 6.66, NULL,
292: ai_walk, 6.20, NULL,
293: ai_walk, 5.78, NULL,
294: ai_walk, 7.25, NULL,
295: ai_walk, 8.37, NULL,
296: ai_walk, 10.41, NULL
297: };
298: mmove_t widow2_move_walk = {FRAME_walk01, FRAME_walk09, widow2_frames_walk, NULL};
299:
300:
301: mframe_t widow2_frames_run [] =
302: {
303: // ai_run, 9.01, drawbbox,
304: ai_run, 9.01, NULL,
305: ai_run, 7.55, NULL,
306: ai_run, 7.01, NULL,
307: ai_run, 6.66, NULL,
308: ai_run, 6.20, NULL,
309: ai_run, 5.78, NULL,
310: ai_run, 7.25, NULL,
311: ai_run, 8.37, NULL,
312: ai_run, 10.41, NULL
313: };
314: mmove_t widow2_move_run = {FRAME_walk01, FRAME_walk09, widow2_frames_run, NULL};
315:
316: mframe_t widow2_frames_attack_pre_beam [] =
317: {
318: ai_charge, 4, NULL,
319: ai_charge, 4, NULL,
320: ai_charge, 4, NULL,
321: ai_charge, 4, widow2_attack_beam
322: };
323: mmove_t widow2_move_attack_pre_beam = {FRAME_fireb01, FRAME_fireb04, widow2_frames_attack_pre_beam, NULL};
324:
325:
326: // Loop this
327: mframe_t widow2_frames_attack_beam [] =
328: {
329: ai_charge, 0, Widow2Beam,
330: ai_charge, 0, Widow2Beam,
331: ai_charge, 0, Widow2Beam,
332: ai_charge, 0, Widow2Beam,
333: ai_charge, 0, widow2_reattack_beam
334: };
335: mmove_t widow2_move_attack_beam = {FRAME_fireb05, FRAME_fireb09, widow2_frames_attack_beam, NULL};
336:
337: mframe_t widow2_frames_attack_post_beam [] =
338: {
339: ai_charge, 4, NULL,
340: ai_charge, 4, NULL,
341: ai_charge, 4, NULL
342: };
343: mmove_t widow2_move_attack_post_beam = {FRAME_fireb06, FRAME_fireb07, widow2_frames_attack_post_beam, widow2_run};
344:
345:
346: void WidowDisrupt (edict_t *self)
347: {
348: vec3_t start;
349: vec3_t dir;
350: vec3_t forward, right;
351: float len;
352:
353: AngleVectors (self->s.angles, forward, right, NULL);
354: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW_DISRUPTOR], forward, right, start);
355:
356: VectorSubtract (self->pos1, self->enemy->s.origin, dir);
357: len = VectorLength (dir);
358:
359: if (len < 30)
360: {
361: // if ((g_showlogic) && (g_showlogic->value))
362: // gi.dprintf ("target locked - dist %2.2f\n", len);
363: // calc direction to where we targeted
364: VectorSubtract (self->pos1, start, dir);
365: VectorNormalize (dir);
366:
367: monster_fire_tracker(self, start, dir, 20, 500, self->enemy, MZ2_WIDOW_DISRUPTOR);
368: }
369: else
370: {
371: // if ((g_showlogic) && (g_showlogic->value))
372: // gi.dprintf ("target missed - dist %2.2f\n", len);
373:
374: PredictAim (self->enemy, start, 1200, true, 0, dir, NULL);
375:
376: // VectorSubtract (self->enemy->s.origin, start, dir);
377: // VectorNormalize (dir);
378: monster_fire_tracker(self, start, dir, 20, 1200, NULL, MZ2_WIDOW_DISRUPTOR);
379: }
380: }
381:
382: void Widow2SaveDisruptLoc (edict_t *self)
383: {
384: if (self->enemy && self->enemy->inuse)
385: {
386: VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
387: self->pos1[2] += self->enemy->viewheight;
388: }
389: else
390: VectorCopy (vec3_origin, self->pos1);
391: };
392:
393: void widow2_disrupt_reattack (edict_t *self)
394: {
395: float luck;
396:
397: luck = random();
398:
399: if (luck < (0.25 + ((float)(skill->value))*0.15))
400: self->monsterinfo.nextframe = FRAME_firea01;
401: }
402:
403: mframe_t widow2_frames_attack_disrupt [] =
404: {
405: ai_charge, 2, NULL,
406: ai_charge, 2, NULL,
407: ai_charge, 2, Widow2SaveDisruptLoc,
408: ai_charge, -20, WidowDisrupt,
409: ai_charge, 2, NULL,
410: ai_charge, 2, NULL,
411: ai_charge, 2, widow2_disrupt_reattack
412: };
413: mmove_t widow2_move_attack_disrupt = {FRAME_firea01, FRAME_firea07, widow2_frames_attack_disrupt, widow2_run};
414:
415: void Widow2SaveBeamTarget (edict_t *self)
416: {
417: if (self->enemy && self->enemy->inuse)
418: {
419: VectorCopy (self->pos1, self->pos2);
420: VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
421: }
422: else
423: {
424: VectorCopy (vec3_origin, self->pos1);
425: VectorCopy (vec3_origin, self->pos2);
426: }
427: }
428:
429: void Widow2BeamTargetRemove (edict_t *self)
430: {
431: VectorCopy (vec3_origin, self->pos1);
432: VectorCopy (vec3_origin, self->pos2);
433: }
434:
435: void Widow2StartSweep (edict_t *self)
436: {
437: Widow2SaveBeamTarget (self);
438: }
439:
440: mframe_t widow2_frames_spawn [] =
441: {
442: ai_charge, 0, NULL,
443: ai_charge, 0, NULL,
444: ai_charge, 0, widow_start_spawn,
445: ai_charge, 0, Widow2Beam,
446: ai_charge, 0, Widow2Beam, //5
447: ai_charge, 0, Widow2Beam,
448: ai_charge, 0, Widow2Beam,
449: ai_charge, 0, Widow2Beam,
450: ai_charge, 0, Widow2Beam,
451: ai_charge, 0, widow2_ready_spawn, //10
452: ai_charge, 0, Widow2Beam,
453: ai_charge, 0, Widow2Beam,
454: ai_charge, 0, Widow2Beam,
455: ai_charge, 0, widow2_spawn_check,
456: ai_charge, 0, NULL, //15
457: ai_charge, 0, NULL,
458: ai_charge, 0, NULL,
459: ai_charge, 0, widow2_reattack_beam
460: };
461: mmove_t widow2_move_spawn = {FRAME_spawn01, FRAME_spawn18, widow2_frames_spawn, NULL};
462:
463: static qboolean widow2_tongue_attack_ok (vec3_t start, vec3_t end, float range)
464: {
465: vec3_t dir, angles;
466:
467: // check for max distance
468: VectorSubtract (start, end, dir);
469: if (VectorLength(dir) > range)
470: return false;
471:
472: // check for min/max pitch
473: vectoangles (dir, angles);
474: if (angles[0] < -180)
475: angles[0] += 360;
476: if (fabs(angles[0]) > 30)
477: return false;
478:
479: return true;
480: }
481:
482: void Widow2Tongue (edict_t *self)
483: {
484: vec3_t f, r, u;
485: vec3_t start, end, dir;
486: trace_t tr;
487:
488: AngleVectors (self->s.angles, f, r, u);
489: G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
490: VectorCopy (self->enemy->s.origin, end);
491: if (!widow2_tongue_attack_ok(start, end, 256))
492: {
493: end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
494: if (!widow2_tongue_attack_ok(start, end, 256))
495: {
496: end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
497: if (!widow2_tongue_attack_ok(start, end, 256))
498: return;
499: }
500: }
501: VectorCopy (self->enemy->s.origin, end);
502:
503: tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
504: if (tr.ent != self->enemy)
505: return;
506:
507: gi.sound (self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);
508:
509: gi.WriteByte (svc_temp_entity);
510: gi.WriteByte (TE_PARASITE_ATTACK);
511: gi.WriteShort (self - g_edicts);
512: gi.WritePosition (start);
513: gi.WritePosition (end);
514: gi.multicast (self->s.origin, MULTICAST_PVS);
515:
516: VectorSubtract (start, end, dir);
517: T_Damage (self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, 2, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
518: }
519:
520: void Widow2TonguePull (edict_t *self)
521: {
522: vec3_t vec;
523: float len;
524: vec3_t f, r, u;
525: vec3_t start, end;
526:
527: if ((!self->enemy) || (!self->enemy->inuse))
528: {
529: self->monsterinfo.run (self);
530: return;
531: }
532:
533: AngleVectors (self->s.angles, f, r, u);
534: G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
535: VectorCopy (self->enemy->s.origin, end);
536:
537: if (!widow2_tongue_attack_ok(start, end, 256))
538: {
539: return;
540: }
541:
542: if (self->enemy->groundentity)
543: {
544: self->enemy->s.origin[2] += 1;
545: self->enemy->groundentity = NULL;
546: // interesting, you don't have to relink the player
547: }
548:
549: VectorSubtract (self->s.origin, self->enemy->s.origin, vec);
550: len = VectorLength (vec);
551: if (self->enemy->client)
552: {
553: VectorNormalize (vec);
554: VectorMA (self->enemy->velocity, 1000, vec, self->enemy->velocity);
555: }
556: else
557: {
558: self->enemy->ideal_yaw = vectoyaw(vec);
559: M_ChangeYaw (self->enemy);
560: VectorScale (f, 1000, self->enemy->velocity);
561: }
562: }
563:
564: void Widow2Crunch (edict_t *self)
565: {
566: vec3_t aim;
567:
568: if ((!self->enemy) || (!self->enemy->inuse))
569: {
570: self->monsterinfo.run (self);
571: return;
572: }
573:
574: Widow2TonguePull (self);
575:
576: // 70 + 32
577: VectorSet (aim, 150, 0, 4);
578: if (self->s.frame != FRAME_tongs07)
579: fire_hit (self, aim, 20 + (rand() % 6), 0);
580: else
581: {
582: if (self->enemy->groundentity)
583: fire_hit (self, aim, (20 + (rand() % 6)), 500);
584: else // not as much kick if they're in the air .. makes it harder to land on her head
585: fire_hit (self, aim, (20 + (rand() % 6)), 250);
586: }
587: }
588:
589: void Widow2Toss (edict_t *self)
590: {
591: self->timestamp = level.time + 3;
592: return;
593: }
594:
595: mframe_t widow2_frames_tongs [] =
596: {
597: ai_charge, 0, Widow2Tongue,
598: ai_charge, 0, Widow2Tongue,
599: ai_charge, 0, Widow2Tongue,
600: ai_charge, 0, Widow2TonguePull,
601: ai_charge, 0, Widow2TonguePull, //5
602: ai_charge, 0, Widow2TonguePull,
603: ai_charge, 0, Widow2Crunch,
604: ai_charge, 0, Widow2Toss
605: };
606: mmove_t widow2_move_tongs = {FRAME_tongs01, FRAME_tongs08, widow2_frames_tongs, widow2_run};
607:
608: mframe_t widow2_frames_pain [] =
609: {
610: ai_move, 0, NULL,
611: ai_move, 0, NULL,
612: ai_move, 0, NULL,
613: ai_move, 0, NULL,
614: ai_move, 0, NULL
615: };
616: mmove_t widow2_move_pain = {FRAME_pain01, FRAME_pain05, widow2_frames_pain, widow2_run};
617:
618: mframe_t widow2_frames_death [] =
619: {
620: ai_move, 0, NULL,
621: ai_move, 0, NULL,
622: ai_move, 0, WidowExplosion1, // 3 boom
623: ai_move, 0, NULL,
624: ai_move, 0, NULL, // 5
625:
626: ai_move, 0, WidowExplosion2, // 6 boom
627: ai_move, 0, NULL,
628: ai_move, 0, NULL,
629: ai_move, 0, NULL,
630: ai_move, 0, NULL, // 10
631:
632: ai_move, 0, NULL,
633: ai_move, 0, NULL, // 12
634: ai_move, 0, NULL,
635: ai_move, 0, NULL,
636: ai_move, 0, NULL, // 15
637:
638: ai_move, 0, NULL,
639: ai_move, 0, NULL,
640: ai_move, 0, WidowExplosion3, // 18
641: ai_move, 0, NULL, // 19
642: ai_move, 0, NULL, // 20
643:
644: ai_move, 0, NULL,
645: ai_move, 0, NULL,
646: ai_move, 0, NULL,
647: ai_move, 0, NULL,
648: ai_move, 0, WidowExplosion4, // 25
649:
650: ai_move, 0, NULL, // 26
651: ai_move, 0, NULL,
652: ai_move, 0, NULL,
653: ai_move, 0, WidowExplosion5,
654: ai_move, 0, WidowExplosionLeg, // 30
655:
656: ai_move, 0, NULL,
657: ai_move, 0, NULL,
658: ai_move, 0, NULL,
659: ai_move, 0, WidowExplosion6,
660: ai_move, 0, NULL, // 35
661:
662: ai_move, 0, NULL,
663: ai_move, 0, NULL,
664: ai_move, 0, WidowExplosion7,
665: ai_move, 0, NULL,
666: ai_move, 0, NULL, // 40
667:
668: ai_move, 0, NULL,
669: ai_move, 0, NULL,
670: ai_move, 0, NULL,
671: ai_move, 0, WidowExplode // 44
672: };
673: mmove_t widow2_move_death = {FRAME_death01, FRAME_death44, widow2_frames_death, NULL};
674:
675: void widow2_start_searching (edict_t *self);
676: void widow2_keep_searching (edict_t *self);
677: void widow2_finaldeath (edict_t *self);
678:
679: mframe_t widow2_frames_dead [] =
680: {
681: ai_move, 0, widow2_start_searching,
682: ai_move, 0, NULL,
683: ai_move, 0, NULL,
684: ai_move, 0, NULL,
685: ai_move, 0, NULL,
686:
687: ai_move, 0, NULL,
688: ai_move, 0, NULL,
689: ai_move, 0, NULL,
690: ai_move, 0, NULL,
691: ai_move, 0, NULL,
692:
693: ai_move, 0, NULL,
694: ai_move, 0, NULL,
695: ai_move, 0, NULL,
696: ai_move, 0, NULL,
697: ai_move, 0, widow2_keep_searching
698: };
699: mmove_t widow2_move_dead = {FRAME_dthsrh01, FRAME_dthsrh15, widow2_frames_dead, NULL};
700:
701: mframe_t widow2_frames_really_dead [] =
702: {
703: ai_move, 0, NULL,
704: ai_move, 0, NULL,
705: ai_move, 0, NULL,
706: ai_move, 0, NULL,
707: ai_move, 0, NULL,
708:
709: ai_move, 0, NULL,
710: ai_move, 0, widow2_finaldeath
711: };
712: mmove_t widow2_move_really_dead = {FRAME_dthsrh16, FRAME_dthsrh22, widow2_frames_really_dead, NULL};
713:
714: void widow2_start_searching (edict_t *self)
715: {
716: self->count = 0;
717: }
718:
719: void widow2_keep_searching (edict_t *self)
720: {
721: if (self->count <= 2)
722: {
723: self->monsterinfo.currentmove = &widow2_move_dead;
724: self->s.frame = FRAME_dthsrh01;
725: self->count++;
726: return;
727: }
728:
729: self->monsterinfo.currentmove = &widow2_move_really_dead;
730: }
731:
732: void widow2_finaldeath (edict_t *self)
733: {
734: VectorSet (self->mins, -70, -70, 0);
735: VectorSet (self->maxs, 70, 70, 80);
736: self->movetype = MOVETYPE_TOSS;
737: // self->svflags |= SVF_DEADMONSTER;
738: self->takedamage = DAMAGE_YES;
739: self->nextthink = 0;
740: gi.linkentity (self);
741: }
742:
743: void widow2_stand (edict_t *self)
744: {
745: // gi.dprintf ("widow2 stand\n");
746: self->monsterinfo.currentmove = &widow2_move_stand;
747: }
748:
749: void widow2_run (edict_t *self)
750: {
751:
752: // gi.dprintf ("widow2 run - %2.2f - %s \n", level.time, self->enemy->classname);
753: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
754:
755: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
756: self->monsterinfo.currentmove = &widow2_move_stand;
757: else
758: self->monsterinfo.currentmove = &widow2_move_run;
759: }
760:
761: void widow2_walk (edict_t *self)
762: {
763: self->monsterinfo.currentmove = &widow2_move_walk;
764: }
765:
766: void widow2_melee (edict_t *self)
767: {
768: self->monsterinfo.currentmove = &widow2_move_tongs;
769: }
770:
771: void widow2_attack (edict_t *self)
772: {
773: float range, luck;
774: qboolean blocked = false;
775:
776: if (self->monsterinfo.aiflags & AI_BLOCKED)
777: {
778: blocked = true;
779: self->monsterinfo.aiflags &= ~AI_BLOCKED;
780: }
781:
782: // gi.dprintf ("widow2 attack\n");
783:
784: if (!self->enemy)
785: return;
786:
787: if (self->bad_area)
788: {
789: if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished))
790: self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
791: else
792: {
793: gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
794: self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
795: }
796: return;
797: }
798:
799: WidowCalcSlots(self);
800:
801: // if we can't see the target, spawn stuff
802: if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2))
803: {
804: self->monsterinfo.currentmove = &widow2_move_spawn;
805: return;
806: }
807:
808: // accept bias towards spawning
809: if (blocked && (SELF_SLOTS_LEFT >= 2))
810: {
811: self->monsterinfo.currentmove = &widow2_move_spawn;
812: return;
813: }
814:
815: range = realrange (self, self->enemy);
816:
817: if (range < 600)
818: {
819: luck = random();
820: if (SELF_SLOTS_LEFT >= 2)
821: {
822: if (luck <= 0.40)
823: self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
824: else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished))
825: {
826: gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
827: self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
828: }
829: else
830: self->monsterinfo.currentmove = &widow2_move_spawn;
831: }
832: else
833: {
834: if ((luck <= 0.50) || (level.time < self->monsterinfo.attack_finished))
835: self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
836: else
837: {
838: gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
839: self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
840: }
841: }
842: }
843: else
844: {
845: luck = random();
846: if (SELF_SLOTS_LEFT >= 2)
847: {
848: if (luck < 0.3)
849: self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
850: else if ((luck < 0.65) || (level.time < self->monsterinfo.attack_finished))
851: self->monsterinfo.currentmove = &widow2_move_spawn;
852: else
853: {
854: gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
855: self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
856: }
857: }
858: else
859: {
860: if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished))
861: self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
862: else
863: {
864: gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
865: self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
866: }
867: }
868: }
869: }
870:
871: void widow2_attack_beam (edict_t *self)
872: {
873: self->monsterinfo.currentmove = &widow2_move_attack_beam;
874: }
875:
876: void widow2_reattack_beam (edict_t *self)
877: {
878: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
879:
880: if ( infront(self, self->enemy) )
881: if (random() <= 0.5)
882: if ((random() < 0.7) || (SELF_SLOTS_LEFT < 2))
883: self->monsterinfo.currentmove = &widow2_move_attack_beam;
884: else
885: self->monsterinfo.currentmove = &widow2_move_spawn;
886: else
887: self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
888: else
889: self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
890: }
891:
892:
893:
894: void widow2_pain (edict_t *self, edict_t *other, float kick, int damage)
895: {
896: if (self->health < (self->max_health / 2))
897: self->s.skinnum = 1;
898:
899: if (skill->value == 3)
900: return; // no pain anims in nightmare
901:
902: // gi.dprintf ("widow2 pain\n");
903: if (level.time < self->pain_debounce_time)
904: return;
905:
906: self->pain_debounce_time = level.time + 3;
907:
908: if (damage < 15)
909: {
910: gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
911: }
912: else if (damage < 75)
913: {
914: gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
915: if ((skill->value < 3) && (random() < (0.6 - (0.2*((float)skill->value)))))
916: {
917: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
918: self->monsterinfo.currentmove = &widow2_move_pain;
919: }
920: }
921: else
922: {
923: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
924: if ((skill->value < 3) && (random() < (0.75 - (0.1*((float)skill->value)))))
925: {
926: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
927: self->monsterinfo.currentmove = &widow2_move_pain;
928: }
929: }
930: }
931:
932: void widow2_dead (edict_t *self)
933: {
934: }
935:
936: void KillChildren (edict_t *self)
937: {
938: edict_t *ent;
939: int field;
940:
941: ent = NULL;
942: field = FOFS(classname);
943: while (1)
944: {
945: ent = G_Find (ent, field, "monster_stalker");
946: if(!ent)
947: return;
948:
949: // FIXME - may need to stagger
950: if ((ent->inuse) && (ent->health > 0))
951: T_Damage (ent, self, self, vec3_origin, self->enemy->s.origin, vec3_origin, (ent->health + 1), 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
952: }
953: }
954:
955: void widow2_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
956: {
957: int n;
958:
959: // check for gib
960: if (self->health <= self->gib_health)
961: {
962: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
963: for (n= 0; n < 2; n++)
964: ThrowWidowGibLoc (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC, NULL, false);
965: for (n= 0; n < 3; n++)
966: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC, NULL, false);
967: for (n= 0; n < 3; n++)
968: {
969: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", damage, GIB_METALLIC, NULL,
970: 0, false);
971: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", damage, GIB_METALLIC, NULL,
972: gi.soundindex ("misc/fhit3.wav"), false);
973: }
974: for (n= 0; n < 2; n++)
975: {
976: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib3/tris.md2", damage, GIB_METALLIC, NULL,
977: 0, false);
978: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib3/tris.md2", damage, GIB_METALLIC, NULL,
979: 0, false);
980: }
981: ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
982: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
983: self->deadflag = DEAD_DEAD;
984: return;
985: }
986:
987: if (self->deadflag == DEAD_DEAD)
988: return;
989:
990: gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
991: self->deadflag = DEAD_DEAD;
992: self->takedamage = DAMAGE_NO;
993: self->count = 0;
994: KillChildren (self);
995: self->monsterinfo.quad_framenum = 0;
996: self->monsterinfo.double_framenum = 0;
997: self->monsterinfo.invincible_framenum = 0;
998: self->monsterinfo.currentmove = &widow2_move_death;
999: }
1000:
1001: qboolean Widow2_CheckAttack (edict_t *self)
1002: {
1003: vec3_t spot1, spot2;
1004: vec3_t temp;
1005: float chance;
1006: trace_t tr;
1007: qboolean enemy_infront;
1008: int enemy_range;
1009: float enemy_yaw;
1010: float real_enemy_range;
1011: vec3_t f, r, u;
1012:
1013: if (!self->enemy)
1014: return false;
1015:
1016: WidowPowerups(self);
1017:
1018: if ((random() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150))
1019: {
1020: self->monsterinfo.aiflags |= AI_BLOCKED;
1021: self->monsterinfo.attack_state = AS_MISSILE;
1022: return true;
1023: }
1024:
1025: if (self->enemy->health > 0)
1026: {
1027: // see if any entities are in the way of the shot
1028: VectorCopy (self->s.origin, spot1);
1029: spot1[2] += self->viewheight;
1030: VectorCopy (self->enemy->s.origin, spot2);
1031: spot2[2] += self->enemy->viewheight;
1032:
1033: tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);
1034:
1035: // do we have a clear shot?
1036: if (tr.ent != self->enemy)
1037: {
1038: // go ahead and spawn stuff if we're mad a a client
1039: if (self->enemy->client && SELF_SLOTS_LEFT >= 2)
1040: {
1041: self->monsterinfo.attack_state = AS_BLIND;
1042: return true;
1043: }
1044:
1045: // PGM - we want them to go ahead and shoot at info_notnulls if they can.
1046: if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM
1047: return false;
1048: }
1049: }
1050:
1051: enemy_infront = infront(self, self->enemy);
1052:
1053: enemy_range = range(self, self->enemy);
1054: VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
1055: enemy_yaw = vectoyaw2(temp);
1056:
1057: self->ideal_yaw = enemy_yaw;
1058:
1059: // melee attack
1060: if (self->timestamp < level.time)
1061: {
1062: real_enemy_range = realrange (self, self->enemy);
1063: if (real_enemy_range < 300)
1064: {
1065: AngleVectors (self->s.angles, f, r, u);
1066: G_ProjectSource2 (self->s.origin, offsets[0], f, r, u, spot1);
1067: VectorCopy (self->enemy->s.origin, spot2);
1068: if (widow2_tongue_attack_ok(spot1, spot2, 256))
1069: {
1070: // melee attack ok
1071:
1072: // be nice in easy mode
1073: if (skill->value == 0 && (rand()&3) )
1074: return false;
1075:
1076: if (self->monsterinfo.melee)
1077: self->monsterinfo.attack_state = AS_MELEE;
1078: else
1079: self->monsterinfo.attack_state = AS_MISSILE;
1080: return true;
1081: }
1082: }
1083: }
1084:
1085: if (level.time < self->monsterinfo.attack_finished)
1086: return false;
1087:
1088: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
1089: {
1090: chance = 0.4;
1091: }
1092: else if (enemy_range == RANGE_NEAR)
1093: {
1094: chance = 0.8;
1095: }
1096: else if (enemy_range == RANGE_MID)
1097: {
1098: chance = 0.8;
1099: }
1100: else if (enemy_range == RANGE_FAR)
1101: {
1102: chance = 0.5;
1103: }
1104:
1105: // PGM - go ahead and shoot every time if it's a info_notnull
1106: if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
1107: {
1108: self->monsterinfo.attack_state = AS_MISSILE;
1109: // self->monsterinfo.attack_finished = level.time + 1.0 + 2*random();
1110: return true;
1111: }
1112:
1113: return false;
1114: }
1115:
1116: void Widow2Precache ()
1117: {
1118: // cache in all of the stalker stuff, widow stuff, spawngro stuff, gibs
1119: gi.soundindex ("parasite/parpain1.wav");
1120: gi.soundindex ("parasite/parpain2.wav");
1121: gi.soundindex ("parasite/pardeth1.wav");
1122: gi.soundindex ("parasite/paratck1.wav");
1123: gi.soundindex ("parasite/parsght1.wav");
1124: gi.soundindex ("infantry/melee2.wav");
1125: gi.soundindex ("misc/fhit3.wav");
1126:
1127: gi.soundindex ("tank/tnkatck3.wav");
1128: gi.soundindex ("weapons/disrupt.wav");
1129: gi.soundindex ("weapons/disint2.wav");
1130:
1131: gi.modelindex ("models/monsters/stalker/tris.md2");
1132: gi.modelindex ("models/items/spawngro2/tris.md2");
1133: gi.modelindex ("models/objects/gibs/sm_metal/tris.md2");
1134: gi.modelindex ("models/proj/laser2/tris.md2");
1135: gi.modelindex ("models/proj/disintegrator/tris.md2");
1136:
1137: gi.modelindex ("models/monsters/blackwidow/gib1/tris.md2");
1138: gi.modelindex ("models/monsters/blackwidow/gib2/tris.md2");
1139: gi.modelindex ("models/monsters/blackwidow/gib3/tris.md2");
1140: gi.modelindex ("models/monsters/blackwidow/gib4/tris.md2");
1141: gi.modelindex ("models/monsters/blackwidow2/gib1/tris.md2");
1142: gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2");
1143: gi.modelindex ("models/monsters/blackwidow2/gib3/tris.md2");
1144: gi.modelindex ("models/monsters/blackwidow2/gib4/tris.md2");
1145: }
1146:
1147: /*QUAKED monster_widow2 (1 .5 0) (-70 -70 0) (70 70 144) Ambush Trigger_Spawn Sight
1148: */
1149: void SP_monster_widow2 (edict_t *self)
1150: {
1151: if (deathmatch->value)
1152: {
1153: G_FreeEdict (self);
1154: return;
1155: }
1156:
1157: sound_pain1 = gi.soundindex ("bosshovr/bhvpain1.wav");
1158: sound_pain2 = gi.soundindex ("bosshovr/bhvpain2.wav");
1159: sound_pain3 = gi.soundindex ("bosshovr/bhvpain3.wav");
1160: sound_death = gi.soundindex ("bosshovr/bhvdeth1.wav");
1161: sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav");
1162: sound_disrupt = gi.soundindex ("gladiator/railgun.wav");
1163: sound_tentacles_retract = gi.soundindex ("brain/brnatck3.wav");
1164:
1165: self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav");
1166:
1167: self->movetype = MOVETYPE_STEP;
1168: self->solid = SOLID_BBOX;
1169: self->s.modelindex = gi.modelindex ("models/monsters/blackwidow2/tris.md2");
1170: VectorSet (self->mins, -70, -70, 0);
1171: VectorSet (self->maxs, 70, 70, 144);
1172:
1173: self->health = 2000 + 1000*(skill->value);
1174: if (coop->value)
1175: self->health += 500*(skill->value);
1176: // self->health = 1;
1177: self->gib_health = -900;
1178: self->mass = 2500;
1179:
1180: /* if (skill->value == 2)
1181: {
1182: self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
1183: self->monsterinfo.power_armor_power = 500;
1184: }
1185: else */if (skill->value == 3)
1186: {
1187: self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
1188: self->monsterinfo.power_armor_power = 750;
1189: }
1190:
1191: self->yaw_speed = 30;
1192:
1193: self->flags |= FL_IMMUNE_LASER;
1194: self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
1195:
1196: self->pain = widow2_pain;
1197: self->die = widow2_die;
1198:
1199: self->monsterinfo.melee = widow2_melee;
1200: self->monsterinfo.stand = widow2_stand;
1201: self->monsterinfo.walk = widow2_walk;
1202: self->monsterinfo.run = widow2_run;
1203: self->monsterinfo.attack = widow2_attack;
1204: self->monsterinfo.search = widow2_search;
1205: self->monsterinfo.checkattack = Widow2_CheckAttack;
1206: gi.linkentity (self);
1207:
1208: self->monsterinfo.currentmove = &widow2_move_stand;
1209: self->monsterinfo.scale = MODEL_SCALE;
1210:
1211: Widow2Precache();
1212: WidowCalcSlots(self);
1213: walkmonster_start (self);
1214: }
1215:
1216: //
1217: // Death sequence stuff
1218: //
1219:
1220: void WidowVelocityForDamage (int damage, vec3_t v)
1221: {
1222: v[0] = damage * crandom();
1223: v[1] = damage * crandom();
1224: v[2] = damage * crandom() + 200.0;
1225: }
1226:
1227: void widow_gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1228: {
1229:
1230: self->solid = SOLID_NOT;
1231: self->touch = NULL;
1232: self->s.angles[PITCH] = 0;
1233: self->s.angles[ROLL] = 0;
1234: VectorClear (self->avelocity);
1235:
1236: if (self->plat2flags)
1237: gi.sound (self, CHAN_VOICE, self->plat2flags, 1, ATTN_NORM, 0);
1238: /*
1239: if (plane)
1240: {
1241: if (plane->normal[2] < -0.8)
1242: {
1243: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
1244: }
1245:
1246: //vectoangles (plane->normal, normal_angles);
1247: //AngleVectors (normal_angles, NULL, right, NULL);
1248: //vectoangles (right, self->s.angles);
1249: //VectorClear (self->avelocity);
1250: }
1251: */
1252: }
1253:
1254: void ThrowWidowGib (edict_t *self, char *gibname, int damage, int type)
1255: {
1256: ThrowWidowGibReal (self, gibname, damage, type, NULL, false, 0, true);
1257: }
1258:
1259: void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade)
1260: {
1261: ThrowWidowGibReal (self, gibname, damage, type, startpos, false, 0, fade);
1262: }
1263:
1264: void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade)
1265: {
1266: ThrowWidowGibReal (self, gibname, damage, type, startpos, true, hitsound, fade);
1267: }
1268:
1269: void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean sized, int hitsound, qboolean fade)
1270: {
1271: edict_t *gib;
1272: vec3_t vd;
1273: vec3_t origin;
1274: vec3_t size;
1275: float vscale;
1276:
1277: if (!gibname)
1278: return;
1279:
1280: gib = G_Spawn();
1281:
1282: if (startpos)
1283: VectorCopy (startpos, gib->s.origin);
1284: else
1285: {
1286: VectorScale (self->size, 0.5, size);
1287: VectorAdd (self->absmin, size, origin);
1288: gib->s.origin[0] = origin[0] + crandom() * size[0];
1289: gib->s.origin[1] = origin[1] + crandom() * size[1];
1290: gib->s.origin[2] = origin[2] + crandom() * size[2];
1291: }
1292:
1293: gib->solid = SOLID_NOT;
1294: gib->s.effects |= EF_GIB;
1295: gib->flags |= FL_NO_KNOCKBACK;
1296: gib->takedamage = DAMAGE_YES;
1297: gib->die = gib_die;
1298: gib->s.renderfx |= RF_IR_VISIBLE;
1299:
1300: if (fade)
1301: {
1302: gib->think = G_FreeEdict;
1303: // sized gibs last longer
1304: if (sized)
1305: gib->nextthink = level.time + 20 + random()*15;
1306: else
1307: gib->nextthink = level.time + 10 + random()*10;
1308: }
1309: else
1310: {
1311: gib->think = G_FreeEdict;
1312: // sized gibs last longer
1313: if (sized)
1314: gib->nextthink = level.time + 60 + random()*15;
1315: else
1316: gib->nextthink = level.time + 25 + random()*10;
1317: }
1318:
1319: if (type == GIB_ORGANIC)
1320: {
1321: gib->movetype = MOVETYPE_TOSS;
1322: gib->touch = gib_touch;
1323: vscale = 0.5;
1324: }
1325: else
1326: {
1327: gib->movetype = MOVETYPE_BOUNCE;
1328: vscale = 1.0;
1329: }
1330:
1331: WidowVelocityForDamage (damage, vd);
1332: VectorMA (self->velocity, vscale, vd, gib->velocity);
1333: ClipGibVelocity (gib);
1334:
1335: gi.setmodel (gib, gibname);
1336:
1337: if (sized)
1338: {
1339: gib->plat2flags = hitsound;
1340: gib->solid = SOLID_BBOX;
1341: gib->avelocity[0] = random()*400;
1342: gib->avelocity[1] = random()*400;
1343: gib->avelocity[2] = random()*200;
1344: if (gib->velocity[2] < 0)
1345: gib->velocity[2] *= -1;
1346: gib->velocity[2] = max((350 + (random()*100.0)), gib->velocity[2]);
1347: gib->gravity = 0.25;
1348: gib->touch = widow_gib_touch;
1349: gib->owner = self;
1350: if (gib->s.modelindex == gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2"))
1351: {
1352: VectorSet (gib->mins, -10, -10, 0);
1353: VectorSet (gib->maxs, 10, 10, 10);
1354: }
1355: else
1356: {
1357: VectorSet (gib->mins, -5, -5, 0);
1358: VectorSet (gib->maxs, 5, 5, 5);
1359: }
1360: }
1361: else
1362: {
1363: gib->velocity[0] *= 2;
1364: gib->velocity[1] *= 2;
1365: gib->avelocity[0] = random()*600;
1366: gib->avelocity[1] = random()*600;
1367: gib->avelocity[2] = random()*600;
1368: }
1369:
1370: // gib->think = G_FreeEdict;
1371: // gib->nextthink = level.time + 10 + random()*10;
1372:
1373: gi.linkentity (gib);
1374: }
1375:
1376: void BloodFountain (edict_t *self, int number, vec3_t startpos, int damage)
1377: {
1378: int n;
1379: vec3_t vd;
1380: vec3_t origin, size, velocity;
1381:
1382: return;
1383:
1384: for (n= 0; n < number; n++)
1385: {
1386: if (startpos)
1387: VectorCopy (startpos, origin);
1388: else
1389: {
1390: VectorScale (self->size, 0.5, size);
1391: VectorAdd (self->absmin, size, origin);
1392: origin[0] = origin[0] + crandom() * size[0];
1393: origin[1] = origin[1] + crandom() * size[1];
1394: origin[2] = origin[2] + crandom() * size[2];
1395: }
1396:
1397: WidowVelocityForDamage (damage, vd);
1398: VectorMA (self->velocity, 1.0, vd, velocity);
1399: velocity[0] *= 2;
1400: velocity[1] *= 2;
1401:
1402: // gi.WriteByte (svc_temp_entity);
1403: // gi.WriteByte (TE_BLOOD_FOUNTAIN);
1404: // gi.WritePosition (origin);
1405: // gi.WritePosition (velocity);
1406: // gi.WriteShort (50);
1407: // gi.multicast (self->s.origin, MULTICAST_ALL);
1408: }
1409: }
1410:
1411: void ThrowSmallStuff (edict_t *self, vec3_t point)
1412: {
1413: int n;
1414:
1415: for (n= 0; n < 2; n++)
1416: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
1417: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
1418: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 150, GIB_METALLIC, point, false);
1419:
1420: }
1421:
1422: void ThrowMoreStuff (edict_t *self, vec3_t point)
1423: {
1424: int n;
1425:
1426: if (coop && coop->value)
1427: {
1428: ThrowSmallStuff (self, point);
1429: return;
1430: }
1431:
1432: for (n= 0; n < 2; n++)
1433: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
1434: for (n= 0; n < 2; n++)
1435: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
1436: for (n= 0; n < 3; n++)
1437: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, point, false);
1438:
1439: }
1440:
1441: void WidowExplode (edict_t *self)
1442: {
1443: vec3_t org;
1444: int n;
1445:
1446: self->think = WidowExplode;
1447: // gi.dprintf ("count = %d\n");
1448:
1449: redo:
1450: VectorCopy (self->s.origin, org);
1451: org[2] += 24 + (rand()&15);
1452: if (self->count < 8)
1453: org[2] += 24 + (rand()&31);
1454: switch (self->count)
1455: {
1456: case 0:
1457: org[0] -= 24;
1458: org[1] -= 24;
1459: break;
1460: case 1:
1461: org[0] += 24;
1462: org[1] += 24;
1463: ThrowSmallStuff(self, org);
1464: break;
1465: case 2:
1466: org[0] += 24;
1467: org[1] -= 24;
1468: break;
1469: case 3:
1470: org[0] -= 24;
1471: org[1] += 24;
1472: ThrowMoreStuff(self, org);
1473: break;
1474: case 4:
1475: org[0] -= 48;
1476: org[1] -= 48;
1477: break;
1478: case 5:
1479: org[0] += 48;
1480: org[1] += 48;
1481: ThrowArm1 (self);
1482: break;
1483: case 6:
1484: org[0] -= 48;
1485: org[1] += 48;
1486: ThrowArm2 (self);
1487: break;
1488: case 7:
1489: org[0] += 48;
1490: org[1] -= 48;
1491: ThrowSmallStuff(self, org);
1492: break;
1493: case 8:
1494: org[0] += 18;
1495: org[1] += 18;
1496: org[2] = self->s.origin[2] + 48;
1497: ThrowMoreStuff(self, org);
1498: break;
1499: case 9:
1500: org[0] -= 18;
1501: org[1] += 18;
1502: org[2] = self->s.origin[2] + 48;
1503: break;
1504: case 10:
1505: org[0] += 18;
1506: org[1] -= 18;
1507: org[2] = self->s.origin[2] + 48;
1508: break;
1509: case 11:
1510: org[0] -= 18;
1511: org[1] -= 18;
1512: org[2] = self->s.origin[2] + 48;
1513: break;
1514: case 12:
1515: self->s.sound = 0;
1516: for (n= 0; n < 2; n++)
1517: ThrowWidowGib (self, "models/objects/gibs/sm_meat/tris.md2", 400, GIB_ORGANIC);
1518: for (n= 0; n < 2; n++)
1519: ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC);
1520: for (n= 0; n < 2; n++)
1521: ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 400, GIB_METALLIC);
1522: // ThrowGib (self, "models/objects/gibs/chest/tris.md2", 1000, GIB_ORGANIC);
1523: // ThrowHead (self, "models/objects/gibs/gear/tris.md2", 1000, GIB_METALLIC);
1524: self->deadflag = DEAD_DEAD;
1525: self->think = monster_think;
1526: self->nextthink = level.time + 0.1;
1527: self->monsterinfo.currentmove = &widow2_move_dead;
1528: return;
1529: }
1530:
1531: self->count++;
1532: if (self->count >=9 && self->count <=12)
1533: {
1534: gi.WriteByte (svc_temp_entity);
1535: gi.WriteByte (TE_EXPLOSION1_BIG);
1536: gi.WritePosition (org);
1537: gi.multicast (self->s.origin, MULTICAST_ALL);
1538: goto redo;
1539: }
1540: // else
1541: gi.WriteByte (svc_temp_entity);
1542: if (self->count %2)
1543: gi.WriteByte (TE_EXPLOSION1);
1544: else
1545: gi.WriteByte (TE_EXPLOSION1_NP);
1546: gi.WritePosition (org);
1547: gi.multicast (self->s.origin, MULTICAST_ALL);
1548:
1549: self->nextthink = level.time + 0.1;
1550: }
1551:
1552: void WidowExplosion1 (edict_t *self)
1553: {
1554: int n;
1555: vec3_t f,r,u, startpoint;
1556: vec3_t offset = {23.74, -37.67, 76.96};
1557:
1558: // gi.dprintf ("1\n");
1559: AngleVectors (self->s.angles, f, r, u);
1560: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1561:
1562: gi.WriteByte (svc_temp_entity);
1563: gi.WriteByte (TE_EXPLOSION1);
1564: gi.WritePosition (startpoint);
1565: gi.multicast (self->s.origin, MULTICAST_ALL);
1566:
1567: for (n= 0; n < 2; n++)
1568: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1569: for (n= 0; n < 2; n++)
1570: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1571: for (n= 0; n < 2; n++)
1572: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1573: }
1574:
1575: void WidowExplosion2 (edict_t *self)
1576: {
1577: int n;
1578: vec3_t f,r,u, startpoint;
1579: vec3_t offset = {-20.49, 36.92, 73.52};
1580:
1581: // gi.dprintf ("2\n");
1582:
1583: AngleVectors (self->s.angles, f, r, u);
1584: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1585:
1586: gi.WriteByte (svc_temp_entity);
1587: gi.WriteByte (TE_EXPLOSION1);
1588: gi.WritePosition (startpoint);
1589: gi.multicast (self->s.origin, MULTICAST_ALL);
1590:
1591: for (n= 0; n < 2; n++)
1592: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1593: for (n= 0; n < 2; n++)
1594: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1595: for (n= 0; n < 2; n++)
1596: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1597: }
1598:
1599: void WidowExplosion3 (edict_t *self)
1600: {
1601: int n;
1602: vec3_t f,r,u, startpoint;
1603: vec3_t offset = {2.11, 0.05, 92.20};
1604:
1605: // gi.dprintf ("3\n");
1606:
1607: AngleVectors (self->s.angles, f, r, u);
1608: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1609:
1610: gi.WriteByte (svc_temp_entity);
1611: gi.WriteByte (TE_EXPLOSION1);
1612: gi.WritePosition (startpoint);
1613: gi.multicast (self->s.origin, MULTICAST_ALL);
1614:
1615: for (n= 0; n < 2; n++)
1616: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1617: for (n= 0; n < 2; n++)
1618: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1619: for (n= 0; n < 2; n++)
1620: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1621: }
1622:
1623: void WidowExplosion4 (edict_t *self)
1624: {
1625: int n;
1626: vec3_t f,r,u, startpoint;
1627: vec3_t offset = {-28.04, -35.57, -77.56};
1628:
1629: // gi.dprintf ("4\n");
1630:
1631: AngleVectors (self->s.angles, f, r, u);
1632: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1633:
1634: gi.WriteByte (svc_temp_entity);
1635: gi.WriteByte (TE_EXPLOSION1);
1636: gi.WritePosition (startpoint);
1637: gi.multicast (self->s.origin, MULTICAST_ALL);
1638:
1639: for (n= 0; n < 2; n++)
1640: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1641: for (n= 0; n < 2; n++)
1642: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1643: for (n= 0; n < 2; n++)
1644: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1645: }
1646:
1647: void WidowExplosion5 (edict_t *self)
1648: {
1649: int n;
1650: vec3_t f,r,u, startpoint;
1651: vec3_t offset = {-20.11, -1.11, 40.76};
1652:
1653: // gi.dprintf ("5\n");
1654:
1655: AngleVectors (self->s.angles, f, r, u);
1656: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1657:
1658: gi.WriteByte (svc_temp_entity);
1659: gi.WriteByte (TE_EXPLOSION1);
1660: gi.WritePosition (startpoint);
1661: gi.multicast (self->s.origin, MULTICAST_ALL);
1662:
1663: for (n= 0; n < 2; n++)
1664: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1665: for (n= 0; n < 2; n++)
1666: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1667: for (n= 0; n < 2; n++)
1668: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1669: }
1670:
1671: void WidowExplosion6 (edict_t *self)
1672: {
1673: int n;
1674: vec3_t f,r,u, startpoint;
1675: vec3_t offset = {-20.11, -1.11, 40.76};
1676:
1677: //gi.dprintf ("6\n");
1678:
1679: AngleVectors (self->s.angles, f, r, u);
1680: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1681:
1682: gi.WriteByte (svc_temp_entity);
1683: gi.WriteByte (TE_EXPLOSION1);
1684: gi.WritePosition (startpoint);
1685: gi.multicast (self->s.origin, MULTICAST_ALL);
1686:
1687: for (n= 0; n < 2; n++)
1688: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1689: for (n= 0; n < 2; n++)
1690: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1691: for (n= 0; n < 2; n++)
1692: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1693: }
1694:
1695: void WidowExplosion7 (edict_t *self)
1696: {
1697: int n;
1698: vec3_t f,r,u, startpoint;
1699: vec3_t offset = {-20.11, -1.11, 40.76};
1700:
1701: //gi.dprintf ("7\n");
1702:
1703: AngleVectors (self->s.angles, f, r, u);
1704: G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
1705:
1706: gi.WriteByte (svc_temp_entity);
1707: gi.WriteByte (TE_EXPLOSION1);
1708: gi.WritePosition (startpoint);
1709: gi.multicast (self->s.origin, MULTICAST_ALL);
1710:
1711: for (n= 0; n < 2; n++)
1712: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1713: for (n= 0; n < 2; n++)
1714: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1715: for (n= 0; n < 2; n++)
1716: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
1717: }
1718:
1719: void WidowExplosionLeg (edict_t *self)
1720: {
1721: int n;
1722: vec3_t f,r,u, startpoint;
1723: vec3_t offset1 = {-31.89, -47.86, 67.02};
1724: vec3_t offset2 = {-44.9, -82.14, 54.72};
1725:
1726: //gi.dprintf ("Leg\n");
1727:
1728: AngleVectors (self->s.angles, f, r, u);
1729: G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
1730:
1731: gi.WriteByte (svc_temp_entity);
1732: gi.WriteByte (TE_EXPLOSION1_BIG);
1733: gi.WritePosition (startpoint);
1734: gi.multicast (self->s.origin, MULTICAST_ALL);
1735:
1736: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", 200, GIB_METALLIC, startpoint,
1737: gi.soundindex ("misc/fhit3.wav"), false);
1738: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1739: for (n= 0; n < 2; n++)
1740: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1741:
1742: G_ProjectSource2 (self->s.origin, offset2, f, r, u, startpoint);
1743:
1744: gi.WriteByte (svc_temp_entity);
1745: gi.WriteByte (TE_EXPLOSION1);
1746: gi.WritePosition (startpoint);
1747: gi.multicast (self->s.origin, MULTICAST_ALL);
1748:
1749: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", 300, GIB_METALLIC, startpoint,
1750: gi.soundindex ("misc/fhit3.wav"), false);
1751: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1752: for (n= 0; n < 2; n++)
1753: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1754: }
1755:
1756: void ThrowArm1 (edict_t *self)
1757: {
1758: int n;
1759: vec3_t f,r,u, startpoint;
1760: vec3_t offset1 = {65.76, 17.52, 7.56};
1761:
1762: AngleVectors (self->s.angles, f, r, u);
1763: G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
1764:
1765: gi.WriteByte (svc_temp_entity);
1766: gi.WriteByte (TE_EXPLOSION1_BIG);
1767: gi.WritePosition (startpoint);
1768: gi.multicast (self->s.origin, MULTICAST_ALL);
1769:
1770: for (n= 0; n < 2; n++)
1771: ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
1772: }
1773:
1774: void ThrowArm2 (edict_t *self)
1775: {
1776: int n;
1777: vec3_t f,r,u, startpoint;
1778: vec3_t offset1 = {65.76, 17.52, 7.56};
1779:
1780: AngleVectors (self->s.angles, f, r, u);
1781: G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
1782:
1783: ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib4/tris.md2", 200, GIB_METALLIC, startpoint,
1784: gi.soundindex ("misc/fhit3.wav"), false);
1785: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
1786: for (n= 0; n < 2; n++)
1787: ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 100, GIB_METALLIC, startpoint, false);
1788: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.