|
|
1.1 root 1: #include "g_local.h"
2:
3:
4: /*
5: =================
6: check_dodge
7:
8: This is a support routine used when a client is firing
9: a non-instant attack weapon. It checks to see if a
10: monster's dodge function should be called.
11: =================
12: */
13: static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed)
14: {
15: vec3_t end;
16: vec3_t v;
17: trace_t tr;
18: float eta;
19:
20: // easy mode only ducks one quarter the time
21: if (skill->value == 0)
22: {
23: if (random() > 0.25)
24: return;
25: }
26: VectorMA (start, 8192, dir, end);
27: tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
28: if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self))
29: {
30: VectorSubtract (tr.endpos, start, v);
31: eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
32: tr.ent->monsterinfo.dodge (tr.ent, self, eta);
33: }
34: }
35:
36:
37: /*
38: =================
39: fire_hit
40:
41: Used for all impact (hit/punch/slash) attacks
42: =================
43: */
44: qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick)
45: {
46: trace_t tr;
47: vec3_t forward, right, up;
48: vec3_t v;
49: vec3_t point;
50: float range;
51: vec3_t dir;
52:
53: //see if enemy is in range
54: VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
55: range = VectorLength(dir);
56: if (range > aim[0])
57: return false;
58:
59: if (aim[1] > self->mins[0] && aim[1] < self->maxs[0])
60: {
61: // the hit is straight on so back the range up to the edge of their bbox
62: range -= self->enemy->maxs[0];
63: }
64: else
65: {
66: // this is a side hit so adjust the "right" value out to the edge of their bbox
67: if (aim[1] < 0)
68: aim[1] = self->enemy->mins[0];
69: else
70: aim[1] = self->enemy->maxs[0];
71: }
72:
73: VectorMA (self->s.origin, range, dir, point);
74:
75: tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT);
76: if (tr.fraction < 1)
77: {
78: if (!tr.ent->takedamage)
79: return false;
80: // if it will hit any client/monster then hit the one we wanted to hit
81: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
82: tr.ent = self->enemy;
83: }
84:
85: AngleVectors(self->s.angles, forward, right, up);
86: VectorMA (self->s.origin, range, forward, point);
87: VectorMA (point, aim[1], right, point);
88: VectorMA (point, aim[2], up, point);
89: VectorSubtract (point, self->enemy->s.origin, dir);
90:
91: // do the damage
92: T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
93:
94: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
95: return false;
96:
97: // do our special form of knockback here
98: VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v);
99: VectorSubtract (v, point, v);
100: VectorNormalize (v);
101: VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity);
102: if (self->enemy->velocity[2] > 0)
103: self->enemy->groundentity = NULL;
104: return true;
105: }
106:
107:
108: /*
109: =================
110: fire_lead
111:
112: This is an internal support routine used for bullet/pellet based weapons.
113: =================
114: */
115: static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod)
116: {
117: trace_t tr;
118: vec3_t dir;
119: vec3_t forward, right, up;
120: vec3_t end;
121: float r;
122: float u;
123: vec3_t water_start;
124: qboolean water = false;
125: int content_mask = MASK_SHOT | MASK_WATER;
126:
127: tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
128: if (!(tr.fraction < 1.0))
129: {
130: vectoangles (aimdir, dir);
131: AngleVectors (dir, forward, right, up);
132:
133: r = crandom()*hspread;
134: u = crandom()*vspread;
135: VectorMA (start, 8192, forward, end);
136: VectorMA (end, r, right, end);
137: VectorMA (end, u, up, end);
138:
139: if (gi.pointcontents (start) & MASK_WATER)
140: {
141: water = true;
142: VectorCopy (start, water_start);
143: content_mask &= ~MASK_WATER;
144: }
145:
146: tr = gi.trace (start, NULL, NULL, end, self, content_mask);
147:
148: // see if we hit water
149: if (tr.contents & MASK_WATER)
150: {
151: int color;
152:
153: water = true;
154: VectorCopy (tr.endpos, water_start);
155:
156: if (!VectorCompare (start, tr.endpos))
157: {
158: if (tr.contents & CONTENTS_WATER)
159: {
160: if (strcmp(tr.surface->name, "*brwater") == 0)
161: color = SPLASH_BROWN_WATER;
162: else
163: color = SPLASH_BLUE_WATER;
164: }
165: else if (tr.contents & CONTENTS_SLIME)
166: color = SPLASH_SLIME;
167: else if (tr.contents & CONTENTS_LAVA)
168: color = SPLASH_LAVA;
169: else
170: color = SPLASH_UNKNOWN;
171:
172: if (color != SPLASH_UNKNOWN)
173: {
174: gi.WriteByte (svc_temp_entity);
175: gi.WriteByte (TE_SPLASH);
176: gi.WriteByte (8);
177: gi.WritePosition (tr.endpos);
178: gi.WriteDir (tr.plane.normal);
179: gi.WriteByte (color);
180: gi.multicast (tr.endpos, MULTICAST_PVS);
181: }
182:
183: // change bullet's course when it enters water
184: VectorSubtract (end, start, dir);
185: vectoangles (dir, dir);
186: AngleVectors (dir, forward, right, up);
187: r = crandom()*hspread*2;
188: u = crandom()*vspread*2;
189: VectorMA (water_start, 8192, forward, end);
190: VectorMA (end, r, right, end);
191: VectorMA (end, u, up, end);
192: }
193:
194: // re-trace ignoring water this time
195: tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
196: }
197: }
198:
199: // send gun puff / flash
200: if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
201: {
202: if (tr.fraction < 1.0)
203: {
204: if (tr.ent->takedamage)
205: {
206: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
207: }
208: else
209: {
210: if (strncmp (tr.surface->name, "sky", 3) != 0)
211: {
212: gi.WriteByte (svc_temp_entity);
213: gi.WriteByte (te_impact);
214: gi.WritePosition (tr.endpos);
215: gi.WriteDir (tr.plane.normal);
216: gi.multicast (tr.endpos, MULTICAST_PVS);
217:
218: if (self->client)
219: PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
220: }
221: }
222: }
223: }
224:
225: // if went through water, determine where the end and make a bubble trail
226: if (water)
227: {
228: vec3_t pos;
229:
230: VectorSubtract (tr.endpos, water_start, dir);
231: VectorNormalize (dir);
232: VectorMA (tr.endpos, -2, dir, pos);
233: if (gi.pointcontents (pos) & MASK_WATER)
234: VectorCopy (pos, tr.endpos);
235: else
236: tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
237:
238: VectorAdd (water_start, tr.endpos, pos);
239: VectorScale (pos, 0.5, pos);
240:
241: gi.WriteByte (svc_temp_entity);
242: gi.WriteByte (TE_BUBBLETRAIL);
243: gi.WritePosition (water_start);
244: gi.WritePosition (tr.endpos);
245: gi.multicast (pos, MULTICAST_PVS);
246: }
247: }
248:
249:
250: /*
251: =================
252: fire_bullet
253:
254: Fires a single round. Used for machinegun and chaingun. Would be fine for
255: pistols, rifles, etc....
256: =================
257: */
258: void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod)
259: {
260: fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
261: }
262:
263:
264: /*
265: =================
266: fire_shotgun
267:
268: Shoots shotgun pellets. Used by shotgun and super shotgun.
269: =================
270: */
271: void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod)
272: {
273: int i;
274:
275: for (i = 0; i < count; i++)
276: fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
277: }
278:
279:
280: /*
281: =================
282: fire_blaster
283:
284: Fires a single blaster bolt. Used by the blaster and hyper blaster.
285: =================
286: */
287: void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
288: {
289: int mod;
290:
291: if (other == self->owner)
292: return;
293:
294: if (surf && (surf->flags & SURF_SKY))
295: {
296: G_FreeEdict (self);
297: return;
298: }
299:
300: if (self->owner->client)
301: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
302:
303: if (other->takedamage)
304: {
305: if (self->spawnflags & 1)
306: mod = MOD_HYPERBLASTER;
307: else
308: mod = MOD_BLASTER;
309: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
310: }
311: else
312: {
313: gi.WriteByte (svc_temp_entity);
314: gi.WriteByte (TE_BLASTER);
315: gi.WritePosition (self->s.origin);
316: if (!plane)
317: gi.WriteDir (vec3_origin);
318: else
319: gi.WriteDir (plane->normal);
320: gi.multicast (self->s.origin, MULTICAST_PVS);
321: }
322:
323: G_FreeEdict (self);
324: }
325:
326: void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
327: {
328: edict_t *bolt;
329: trace_t tr;
330:
331: VectorNormalize (dir);
332:
333: bolt = G_Spawn();
334: VectorCopy (start, bolt->s.origin);
335: VectorCopy (start, bolt->s.old_origin);
336: vectoangles (dir, bolt->s.angles);
337: VectorScale (dir, speed, bolt->velocity);
338: bolt->movetype = MOVETYPE_FLYMISSILE;
339: bolt->clipmask = MASK_SHOT;
340: bolt->solid = SOLID_BBOX;
341: bolt->s.effects |= effect;
342: VectorClear (bolt->mins);
343: VectorClear (bolt->maxs);
344: bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
345: bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
346: bolt->owner = self;
347: bolt->touch = blaster_touch;
348: bolt->nextthink = level.time + 2;
349: bolt->think = G_FreeEdict;
350: bolt->dmg = damage;
351: bolt->classname = "bolt";
352: if (hyper)
353: bolt->spawnflags = 1;
354: gi.linkentity (bolt);
355:
356: if (self->client)
357: check_dodge (self, bolt->s.origin, dir, speed);
358:
359: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
360: if (tr.fraction < 1.0)
361: {
362: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
363: bolt->touch (bolt, tr.ent, NULL, NULL);
364: }
365: }
366:
367:
368: /*
369: =================
370: fire_grenade
371: =================
372: */
373: static void Grenade_Explode (edict_t *ent)
374: {
375: vec3_t origin;
376: int mod;
377:
378: if (ent->owner->client)
379: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
380:
381: //FIXME: if we are onground then raise our Z just a bit since we are a point?
382: if (ent->enemy)
383: {
384: float points;
385: vec3_t v;
386: vec3_t dir;
387:
388: VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
389: VectorMA (ent->enemy->s.origin, 0.5, v, v);
390: VectorSubtract (ent->s.origin, v, v);
391: points = ent->dmg - 0.5 * VectorLength (v);
392: VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
393: if (ent->spawnflags & 1)
394: mod = MOD_HANDGRENADE;
395: else
396: mod = MOD_GRENADE;
397: T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
398: }
399:
400: if (ent->spawnflags & 2)
401: mod = MOD_HELD_GRENADE;
402: else if (ent->spawnflags & 1)
403: mod = MOD_HG_SPLASH;
404: else
405: mod = MOD_G_SPLASH;
406: T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
407:
408: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
409: gi.WriteByte (svc_temp_entity);
410: if (ent->waterlevel)
411: {
412: if (ent->groundentity)
413: gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
414: else
415: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
416: }
417: else
418: {
419: if (ent->groundentity)
420: gi.WriteByte (TE_GRENADE_EXPLOSION);
421: else
422: gi.WriteByte (TE_ROCKET_EXPLOSION);
423: }
424: gi.WritePosition (origin);
425: gi.multicast (ent->s.origin, MULTICAST_PHS);
426:
427: G_FreeEdict (ent);
428: }
429:
430: static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
431: {
432: if (other == ent->owner)
433: return;
434:
435: if (surf && (surf->flags & SURF_SKY))
436: {
437: G_FreeEdict (ent);
438: return;
439: }
440:
441: if (!other->takedamage)
442: {
443: if (ent->spawnflags & 1)
444: {
445: if (random() > 0.5)
446: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
447: else
448: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
449: }
450: else
451: {
452: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
453: }
454: return;
455: }
456:
457: ent->enemy = other;
458: Grenade_Explode (ent);
459: }
460:
461: void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
462: {
463: edict_t *grenade;
464: vec3_t dir;
465: vec3_t forward, right, up;
466:
467: vectoangles (aimdir, dir);
468: AngleVectors (dir, forward, right, up);
469:
470: grenade = G_Spawn();
471: VectorCopy (start, grenade->s.origin);
472: VectorScale (aimdir, speed, grenade->velocity);
473: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
474: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
475: VectorSet (grenade->avelocity, 300, 300, 300);
476: grenade->movetype = MOVETYPE_BOUNCE;
477: grenade->clipmask = MASK_SHOT;
478: grenade->solid = SOLID_BBOX;
479: grenade->s.effects |= EF_GRENADE;
480: VectorClear (grenade->mins);
481: VectorClear (grenade->maxs);
482: grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
483: grenade->owner = self;
484: grenade->touch = Grenade_Touch;
485: grenade->nextthink = level.time + timer;
486: grenade->think = Grenade_Explode;
487: grenade->dmg = damage;
488: grenade->dmg_radius = damage_radius;
489: grenade->classname = "grenade";
490:
491: gi.linkentity (grenade);
492: }
493:
494: void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
495: {
496: edict_t *grenade;
497: vec3_t dir;
498: vec3_t forward, right, up;
499:
500: vectoangles (aimdir, dir);
501: AngleVectors (dir, forward, right, up);
502:
503: grenade = G_Spawn();
504: VectorCopy (start, grenade->s.origin);
505: VectorScale (aimdir, speed, grenade->velocity);
506: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
507: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
508: VectorSet (grenade->avelocity, 300, 300, 300);
509: grenade->movetype = MOVETYPE_BOUNCE;
510: grenade->clipmask = MASK_SHOT;
511: grenade->solid = SOLID_BBOX;
512: grenade->s.effects |= EF_GRENADE;
513: VectorClear (grenade->mins);
514: VectorClear (grenade->maxs);
515: grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
516: grenade->owner = self;
517: grenade->touch = Grenade_Touch;
518: grenade->nextthink = level.time + timer;
519: grenade->think = Grenade_Explode;
520: grenade->dmg = damage;
521: grenade->dmg_radius = damage_radius;
522: grenade->classname = "hgrenade";
523: if (held)
524: grenade->spawnflags = 3;
525: else
526: grenade->spawnflags = 1;
527: grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
528:
529: if (timer <= 0.0)
530: Grenade_Explode (grenade);
531: else
532: {
533: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
534: gi.linkentity (grenade);
535: }
536: }
537:
538:
539: /*
540: =================
541: fire_rocket
542: =================
543: */
544: void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
545: {
546: vec3_t origin;
547: int n;
548:
549: if (other == ent->owner)
550: return;
551:
552: if (surf && (surf->flags & SURF_SKY))
553: {
554: G_FreeEdict (ent);
555: return;
556: }
557:
558: if (ent->owner->client)
559: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
560:
561: // calculate position for the explosion entity
562: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
563:
564: if (other->takedamage)
565: {
566: T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
567: }
568: else
569: {
570: // don't throw any debris in net games
571: if (!deathmatch->value && !coop->value)
572: {
573: if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
574: {
575: n = rand() % 5;
576: while(n--)
577: ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
578: }
579: }
580: }
581:
582: T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
583:
584: gi.WriteByte (svc_temp_entity);
585: if (ent->waterlevel)
586: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
587: else
588: gi.WriteByte (TE_ROCKET_EXPLOSION);
589: gi.WritePosition (origin);
590: gi.multicast (ent->s.origin, MULTICAST_PHS);
591:
592: G_FreeEdict (ent);
593: }
594:
595: void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
596: {
597: edict_t *rocket;
598:
599: rocket = G_Spawn();
600: VectorCopy (start, rocket->s.origin);
601: VectorCopy (dir, rocket->movedir);
602: vectoangles (dir, rocket->s.angles);
603: VectorScale (dir, speed, rocket->velocity);
604: rocket->movetype = MOVETYPE_FLYMISSILE;
605: rocket->clipmask = MASK_SHOT;
606: rocket->solid = SOLID_BBOX;
607: rocket->s.effects |= EF_ROCKET;
608: VectorClear (rocket->mins);
609: VectorClear (rocket->maxs);
610: rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
611: rocket->owner = self;
612: rocket->touch = rocket_touch;
613: rocket->nextthink = level.time + 8000/speed;
614: rocket->think = G_FreeEdict;
615: rocket->dmg = damage;
616: rocket->radius_dmg = radius_damage;
617: rocket->dmg_radius = damage_radius;
618: rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
619: rocket->classname = "rocket";
620:
621: if (self->client)
622: check_dodge (self, rocket->s.origin, dir, speed);
623:
624: gi.linkentity (rocket);
625: }
626:
627:
628: /*
629: =================
630: fire_rail
631: =================
632: */
633: void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
634: {
635: vec3_t from;
636: vec3_t end;
637: trace_t tr;
638: edict_t *ignore;
639: int mask;
640: qboolean water;
641:
642: VectorMA (start, 8192, aimdir, end);
643: VectorCopy (start, from);
644: ignore = self;
645: water = false;
646: mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
647: while (ignore)
648: {
649: tr = gi.trace (from, NULL, NULL, end, ignore, mask);
650:
651: if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
652: {
653: mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
654: water = true;
655: }
656: else
657: {
658: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
659: ignore = tr.ent;
660: else
661: ignore = NULL;
662:
663: if ((tr.ent != self) && (tr.ent->takedamage))
664: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
665: }
666:
667: VectorCopy (tr.endpos, from);
668: }
669:
670: // send gun puff / flash
671: gi.WriteByte (svc_temp_entity);
672: gi.WriteByte (TE_RAILTRAIL);
673: gi.WritePosition (start);
674: gi.WritePosition (tr.endpos);
675: gi.multicast (self->s.origin, MULTICAST_PHS);
676: // gi.multicast (start, MULTICAST_PHS);
677: if (water)
678: {
679: gi.WriteByte (svc_temp_entity);
680: gi.WriteByte (TE_RAILTRAIL);
681: gi.WritePosition (start);
682: gi.WritePosition (tr.endpos);
683: gi.multicast (tr.endpos, MULTICAST_PHS);
684: }
685:
686: if (self->client)
687: PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
688: }
689:
690:
691: /*
692: =================
693: fire_bfg
694: =================
695: */
696: void bfg_explode (edict_t *self)
697: {
698: edict_t *ent;
699: float points;
700: vec3_t v;
701: float dist;
702:
703: if (self->s.frame == 0)
704: {
705: // the BFG effect
706: ent = NULL;
707: while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
708: {
709: if (!ent->takedamage)
710: continue;
711: if (ent == self->owner)
712: continue;
713: if (!CanDamage (ent, self))
714: continue;
715: if (!CanDamage (ent, self->owner))
716: continue;
717:
718: VectorAdd (ent->mins, ent->maxs, v);
719: VectorMA (ent->s.origin, 0.5, v, v);
720: VectorSubtract (self->s.origin, v, v);
721: dist = VectorLength(v);
722: points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
723: if (ent == self->owner)
724: points = points * 0.5;
725:
726: gi.WriteByte (svc_temp_entity);
727: gi.WriteByte (TE_BFG_EXPLOSION);
728: gi.WritePosition (ent->s.origin);
729: gi.multicast (ent->s.origin, MULTICAST_PHS);
730: T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
731: }
732: }
733:
734: self->nextthink = level.time + FRAMETIME;
735: self->s.frame++;
736: if (self->s.frame == 5)
737: self->think = G_FreeEdict;
738: }
739:
740: void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
741: {
742: if (other == self->owner)
743: return;
744:
745: if (surf && (surf->flags & SURF_SKY))
746: {
747: G_FreeEdict (self);
748: return;
749: }
750:
751: if (self->owner->client)
752: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
753:
754: // core explosion - prevents firing it into the wall/floor
755: if (other->takedamage)
756: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
757: T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
758:
759: gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
760: self->solid = SOLID_NOT;
761: self->touch = NULL;
762: VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
763: VectorClear (self->velocity);
764: self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2");
765: self->s.frame = 0;
766: self->s.sound = 0;
767: self->s.effects &= ~EF_ANIM_ALLFAST;
768: self->think = bfg_explode;
769: self->nextthink = level.time + FRAMETIME;
770: self->enemy = other;
771:
772: gi.WriteByte (svc_temp_entity);
773: gi.WriteByte (TE_BFG_BIGEXPLOSION);
774: gi.WritePosition (self->s.origin);
775: gi.multicast (self->s.origin, MULTICAST_PVS);
776: }
777:
778:
779: void bfg_think (edict_t *self)
780: {
781: edict_t *ent;
782: edict_t *ignore;
783: vec3_t point;
784: vec3_t dir;
785: vec3_t start;
786: vec3_t end;
787: int dmg;
788: trace_t tr;
789:
790: if (deathmatch->value)
791: dmg = 5;
792: else
793: dmg = 10;
794:
795: ent = NULL;
796: while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
797: {
798: if (ent == self)
799: continue;
800:
801: if (ent == self->owner)
802: continue;
803:
804: if (!ent->takedamage)
805: continue;
806:
807: if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
808: continue;
809:
810: VectorMA (ent->absmin, 0.5, ent->size, point);
811:
812: VectorSubtract (point, self->s.origin, dir);
813: VectorNormalize (dir);
814:
815: ignore = self;
816: VectorCopy (self->s.origin, start);
817: VectorMA (start, 2048, dir, end);
818: while(1)
819: {
820: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
821:
822: if (!tr.ent)
823: break;
824:
825: // hurt it if we can
826: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
827: T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
828:
829: // if we hit something that's not a monster or player we're done
830: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
831: {
832: gi.WriteByte (svc_temp_entity);
833: gi.WriteByte (TE_LASER_SPARKS);
834: gi.WriteByte (4);
835: gi.WritePosition (tr.endpos);
836: gi.WriteDir (tr.plane.normal);
837: gi.WriteByte (self->s.skinnum);
838: gi.multicast (tr.endpos, MULTICAST_PVS);
839: break;
840: }
841:
842: ignore = tr.ent;
843: VectorCopy (tr.endpos, start);
844: }
845:
846: gi.WriteByte (svc_temp_entity);
847: gi.WriteByte (TE_BFG_LASER);
848: gi.WritePosition (self->s.origin);
849: gi.WritePosition (tr.endpos);
850: gi.multicast (self->s.origin, MULTICAST_PHS);
851: }
852:
853: self->nextthink = level.time + FRAMETIME;
854: }
855:
856:
857: void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
858: {
859: edict_t *bfg;
860:
861: bfg = G_Spawn();
862: VectorCopy (start, bfg->s.origin);
863: VectorCopy (dir, bfg->movedir);
864: vectoangles (dir, bfg->s.angles);
865: VectorScale (dir, speed, bfg->velocity);
866: bfg->movetype = MOVETYPE_FLYMISSILE;
867: bfg->clipmask = MASK_SHOT;
868: bfg->solid = SOLID_BBOX;
869: bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
870: VectorClear (bfg->mins);
871: VectorClear (bfg->maxs);
872: bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
873: bfg->owner = self;
874: bfg->touch = bfg_touch;
875: bfg->nextthink = level.time + 8000/speed;
876: bfg->think = G_FreeEdict;
877: bfg->radius_dmg = damage;
878: bfg->dmg_radius = damage_radius;
879: bfg->classname = "bfg blast";
880: bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
881:
882: bfg->think = bfg_think;
883: bfg->nextthink = level.time + FRAMETIME;
884: bfg->teammaster = bfg;
885: bfg->teamchain = NULL;
886:
887: if (self->client)
888: check_dodge (self, bfg->s.origin, dir, speed);
889:
890: gi.linkentity (bfg);
891: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.