|
|
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();
1.1.1.2 ! root 334: bolt->svflags = SVF_DEADMONSTER;
! 335: // yes, I know it looks weird that projectiles are deadmonsters
! 336: // what this means is that when prediction is used against the object
! 337: // (blaster/hyperblaster shots), the player won't be solid clipped against
! 338: // the object. Right now trying to run into a firing hyperblaster
! 339: // is very jerky since you are predicted 'against' the shots.
1.1 root 340: VectorCopy (start, bolt->s.origin);
341: VectorCopy (start, bolt->s.old_origin);
342: vectoangles (dir, bolt->s.angles);
343: VectorScale (dir, speed, bolt->velocity);
344: bolt->movetype = MOVETYPE_FLYMISSILE;
345: bolt->clipmask = MASK_SHOT;
346: bolt->solid = SOLID_BBOX;
347: bolt->s.effects |= effect;
348: VectorClear (bolt->mins);
349: VectorClear (bolt->maxs);
350: bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
351: bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
352: bolt->owner = self;
353: bolt->touch = blaster_touch;
354: bolt->nextthink = level.time + 2;
355: bolt->think = G_FreeEdict;
356: bolt->dmg = damage;
357: bolt->classname = "bolt";
358: if (hyper)
359: bolt->spawnflags = 1;
360: gi.linkentity (bolt);
361:
362: if (self->client)
363: check_dodge (self, bolt->s.origin, dir, speed);
364:
365: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
366: if (tr.fraction < 1.0)
367: {
368: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
369: bolt->touch (bolt, tr.ent, NULL, NULL);
370: }
371: }
372:
373:
374: /*
375: =================
376: fire_grenade
377: =================
378: */
379: static void Grenade_Explode (edict_t *ent)
380: {
381: vec3_t origin;
382: int mod;
383:
384: if (ent->owner->client)
385: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
386:
387: //FIXME: if we are onground then raise our Z just a bit since we are a point?
388: if (ent->enemy)
389: {
390: float points;
391: vec3_t v;
392: vec3_t dir;
393:
394: VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
395: VectorMA (ent->enemy->s.origin, 0.5, v, v);
396: VectorSubtract (ent->s.origin, v, v);
397: points = ent->dmg - 0.5 * VectorLength (v);
398: VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
399: if (ent->spawnflags & 1)
400: mod = MOD_HANDGRENADE;
401: else
402: mod = MOD_GRENADE;
403: T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
404: }
405:
406: if (ent->spawnflags & 2)
407: mod = MOD_HELD_GRENADE;
408: else if (ent->spawnflags & 1)
409: mod = MOD_HG_SPLASH;
410: else
411: mod = MOD_G_SPLASH;
412: T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
413:
414: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
415: gi.WriteByte (svc_temp_entity);
416: if (ent->waterlevel)
417: {
418: if (ent->groundentity)
419: gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
420: else
421: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
422: }
423: else
424: {
425: if (ent->groundentity)
426: gi.WriteByte (TE_GRENADE_EXPLOSION);
427: else
428: gi.WriteByte (TE_ROCKET_EXPLOSION);
429: }
430: gi.WritePosition (origin);
431: gi.multicast (ent->s.origin, MULTICAST_PHS);
432:
433: G_FreeEdict (ent);
434: }
435:
436: static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
437: {
438: if (other == ent->owner)
439: return;
440:
441: if (surf && (surf->flags & SURF_SKY))
442: {
443: G_FreeEdict (ent);
444: return;
445: }
446:
447: if (!other->takedamage)
448: {
449: if (ent->spawnflags & 1)
450: {
451: if (random() > 0.5)
452: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
453: else
454: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
455: }
456: else
457: {
458: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
459: }
460: return;
461: }
462:
463: ent->enemy = other;
464: Grenade_Explode (ent);
465: }
466:
467: void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
468: {
469: edict_t *grenade;
470: vec3_t dir;
471: vec3_t forward, right, up;
472:
473: vectoangles (aimdir, dir);
474: AngleVectors (dir, forward, right, up);
475:
476: grenade = G_Spawn();
477: VectorCopy (start, grenade->s.origin);
478: VectorScale (aimdir, speed, grenade->velocity);
479: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
480: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
481: VectorSet (grenade->avelocity, 300, 300, 300);
482: grenade->movetype = MOVETYPE_BOUNCE;
483: grenade->clipmask = MASK_SHOT;
484: grenade->solid = SOLID_BBOX;
485: grenade->s.effects |= EF_GRENADE;
486: VectorClear (grenade->mins);
487: VectorClear (grenade->maxs);
488: grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
489: grenade->owner = self;
490: grenade->touch = Grenade_Touch;
491: grenade->nextthink = level.time + timer;
492: grenade->think = Grenade_Explode;
493: grenade->dmg = damage;
494: grenade->dmg_radius = damage_radius;
495: grenade->classname = "grenade";
496:
497: gi.linkentity (grenade);
498: }
499:
500: void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
501: {
502: edict_t *grenade;
503: vec3_t dir;
504: vec3_t forward, right, up;
505:
506: vectoangles (aimdir, dir);
507: AngleVectors (dir, forward, right, up);
508:
509: grenade = G_Spawn();
510: VectorCopy (start, grenade->s.origin);
511: VectorScale (aimdir, speed, grenade->velocity);
512: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
513: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
514: VectorSet (grenade->avelocity, 300, 300, 300);
515: grenade->movetype = MOVETYPE_BOUNCE;
516: grenade->clipmask = MASK_SHOT;
517: grenade->solid = SOLID_BBOX;
518: grenade->s.effects |= EF_GRENADE;
519: VectorClear (grenade->mins);
520: VectorClear (grenade->maxs);
521: grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
522: grenade->owner = self;
523: grenade->touch = Grenade_Touch;
524: grenade->nextthink = level.time + timer;
525: grenade->think = Grenade_Explode;
526: grenade->dmg = damage;
527: grenade->dmg_radius = damage_radius;
528: grenade->classname = "hgrenade";
529: if (held)
530: grenade->spawnflags = 3;
531: else
532: grenade->spawnflags = 1;
533: grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
534:
535: if (timer <= 0.0)
536: Grenade_Explode (grenade);
537: else
538: {
539: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
540: gi.linkentity (grenade);
541: }
542: }
543:
544:
545: /*
546: =================
547: fire_rocket
548: =================
549: */
550: void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
551: {
552: vec3_t origin;
553: int n;
554:
555: if (other == ent->owner)
556: return;
557:
558: if (surf && (surf->flags & SURF_SKY))
559: {
560: G_FreeEdict (ent);
561: return;
562: }
563:
564: if (ent->owner->client)
565: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
566:
567: // calculate position for the explosion entity
568: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
569:
570: if (other->takedamage)
571: {
572: T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
573: }
574: else
575: {
576: // don't throw any debris in net games
577: if (!deathmatch->value && !coop->value)
578: {
579: if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
580: {
581: n = rand() % 5;
582: while(n--)
583: ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
584: }
585: }
586: }
587:
588: T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
589:
590: gi.WriteByte (svc_temp_entity);
591: if (ent->waterlevel)
592: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
593: else
594: gi.WriteByte (TE_ROCKET_EXPLOSION);
595: gi.WritePosition (origin);
596: gi.multicast (ent->s.origin, MULTICAST_PHS);
597:
598: G_FreeEdict (ent);
599: }
600:
601: void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
602: {
603: edict_t *rocket;
604:
605: rocket = G_Spawn();
606: VectorCopy (start, rocket->s.origin);
607: VectorCopy (dir, rocket->movedir);
608: vectoangles (dir, rocket->s.angles);
609: VectorScale (dir, speed, rocket->velocity);
610: rocket->movetype = MOVETYPE_FLYMISSILE;
611: rocket->clipmask = MASK_SHOT;
612: rocket->solid = SOLID_BBOX;
613: rocket->s.effects |= EF_ROCKET;
614: VectorClear (rocket->mins);
615: VectorClear (rocket->maxs);
616: rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
617: rocket->owner = self;
618: rocket->touch = rocket_touch;
619: rocket->nextthink = level.time + 8000/speed;
620: rocket->think = G_FreeEdict;
621: rocket->dmg = damage;
622: rocket->radius_dmg = radius_damage;
623: rocket->dmg_radius = damage_radius;
624: rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
625: rocket->classname = "rocket";
626:
627: if (self->client)
628: check_dodge (self, rocket->s.origin, dir, speed);
629:
630: gi.linkentity (rocket);
631: }
632:
633:
634: /*
635: =================
636: fire_rail
637: =================
638: */
639: void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
640: {
641: vec3_t from;
642: vec3_t end;
643: trace_t tr;
644: edict_t *ignore;
645: int mask;
646: qboolean water;
647:
648: VectorMA (start, 8192, aimdir, end);
649: VectorCopy (start, from);
650: ignore = self;
651: water = false;
652: mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
653: while (ignore)
654: {
655: tr = gi.trace (from, NULL, NULL, end, ignore, mask);
656:
657: if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
658: {
659: mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
660: water = true;
661: }
662: else
663: {
664: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
665: ignore = tr.ent;
666: else
667: ignore = NULL;
668:
669: if ((tr.ent != self) && (tr.ent->takedamage))
670: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
671: }
672:
673: VectorCopy (tr.endpos, from);
674: }
675:
676: // send gun puff / flash
677: gi.WriteByte (svc_temp_entity);
678: gi.WriteByte (TE_RAILTRAIL);
679: gi.WritePosition (start);
680: gi.WritePosition (tr.endpos);
681: gi.multicast (self->s.origin, MULTICAST_PHS);
682: // gi.multicast (start, MULTICAST_PHS);
683: if (water)
684: {
685: gi.WriteByte (svc_temp_entity);
686: gi.WriteByte (TE_RAILTRAIL);
687: gi.WritePosition (start);
688: gi.WritePosition (tr.endpos);
689: gi.multicast (tr.endpos, MULTICAST_PHS);
690: }
691:
692: if (self->client)
693: PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
694: }
695:
696:
697: /*
698: =================
699: fire_bfg
700: =================
701: */
702: void bfg_explode (edict_t *self)
703: {
704: edict_t *ent;
705: float points;
706: vec3_t v;
707: float dist;
708:
709: if (self->s.frame == 0)
710: {
711: // the BFG effect
712: ent = NULL;
713: while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
714: {
715: if (!ent->takedamage)
716: continue;
717: if (ent == self->owner)
718: continue;
719: if (!CanDamage (ent, self))
720: continue;
721: if (!CanDamage (ent, self->owner))
722: continue;
723:
724: VectorAdd (ent->mins, ent->maxs, v);
725: VectorMA (ent->s.origin, 0.5, v, v);
726: VectorSubtract (self->s.origin, v, v);
727: dist = VectorLength(v);
728: points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
729: if (ent == self->owner)
730: points = points * 0.5;
731:
732: gi.WriteByte (svc_temp_entity);
733: gi.WriteByte (TE_BFG_EXPLOSION);
734: gi.WritePosition (ent->s.origin);
735: gi.multicast (ent->s.origin, MULTICAST_PHS);
736: T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
737: }
738: }
739:
740: self->nextthink = level.time + FRAMETIME;
741: self->s.frame++;
742: if (self->s.frame == 5)
743: self->think = G_FreeEdict;
744: }
745:
746: void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
747: {
748: if (other == self->owner)
749: return;
750:
751: if (surf && (surf->flags & SURF_SKY))
752: {
753: G_FreeEdict (self);
754: return;
755: }
756:
757: if (self->owner->client)
758: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
759:
760: // core explosion - prevents firing it into the wall/floor
761: if (other->takedamage)
762: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
763: T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
764:
765: gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
766: self->solid = SOLID_NOT;
767: self->touch = NULL;
768: VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
769: VectorClear (self->velocity);
770: self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2");
771: self->s.frame = 0;
772: self->s.sound = 0;
773: self->s.effects &= ~EF_ANIM_ALLFAST;
774: self->think = bfg_explode;
775: self->nextthink = level.time + FRAMETIME;
776: self->enemy = other;
777:
778: gi.WriteByte (svc_temp_entity);
779: gi.WriteByte (TE_BFG_BIGEXPLOSION);
780: gi.WritePosition (self->s.origin);
781: gi.multicast (self->s.origin, MULTICAST_PVS);
782: }
783:
784:
785: void bfg_think (edict_t *self)
786: {
787: edict_t *ent;
788: edict_t *ignore;
789: vec3_t point;
790: vec3_t dir;
791: vec3_t start;
792: vec3_t end;
793: int dmg;
794: trace_t tr;
795:
796: if (deathmatch->value)
797: dmg = 5;
798: else
799: dmg = 10;
800:
801: ent = NULL;
802: while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
803: {
804: if (ent == self)
805: continue;
806:
807: if (ent == self->owner)
808: continue;
809:
810: if (!ent->takedamage)
811: continue;
812:
813: if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
814: continue;
815:
816: VectorMA (ent->absmin, 0.5, ent->size, point);
817:
818: VectorSubtract (point, self->s.origin, dir);
819: VectorNormalize (dir);
820:
821: ignore = self;
822: VectorCopy (self->s.origin, start);
823: VectorMA (start, 2048, dir, end);
824: while(1)
825: {
826: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
827:
828: if (!tr.ent)
829: break;
830:
831: // hurt it if we can
832: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
833: T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
834:
835: // if we hit something that's not a monster or player we're done
836: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
837: {
838: gi.WriteByte (svc_temp_entity);
839: gi.WriteByte (TE_LASER_SPARKS);
840: gi.WriteByte (4);
841: gi.WritePosition (tr.endpos);
842: gi.WriteDir (tr.plane.normal);
843: gi.WriteByte (self->s.skinnum);
844: gi.multicast (tr.endpos, MULTICAST_PVS);
845: break;
846: }
847:
848: ignore = tr.ent;
849: VectorCopy (tr.endpos, start);
850: }
851:
852: gi.WriteByte (svc_temp_entity);
853: gi.WriteByte (TE_BFG_LASER);
854: gi.WritePosition (self->s.origin);
855: gi.WritePosition (tr.endpos);
856: gi.multicast (self->s.origin, MULTICAST_PHS);
857: }
858:
859: self->nextthink = level.time + FRAMETIME;
860: }
861:
862:
863: void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
864: {
865: edict_t *bfg;
866:
867: bfg = G_Spawn();
868: VectorCopy (start, bfg->s.origin);
869: VectorCopy (dir, bfg->movedir);
870: vectoangles (dir, bfg->s.angles);
871: VectorScale (dir, speed, bfg->velocity);
872: bfg->movetype = MOVETYPE_FLYMISSILE;
873: bfg->clipmask = MASK_SHOT;
874: bfg->solid = SOLID_BBOX;
875: bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
876: VectorClear (bfg->mins);
877: VectorClear (bfg->maxs);
878: bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
879: bfg->owner = self;
880: bfg->touch = bfg_touch;
881: bfg->nextthink = level.time + 8000/speed;
882: bfg->think = G_FreeEdict;
883: bfg->radius_dmg = damage;
884: bfg->dmg_radius = damage_radius;
885: bfg->classname = "bfg blast";
886: bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
887:
888: bfg->think = bfg_think;
889: bfg->nextthink = level.time + FRAMETIME;
890: bfg->teammaster = bfg;
891: bfg->teamchain = NULL;
892:
893: if (self->client)
894: check_dodge (self, bfg->s.origin, dir, speed);
895:
896: gi.linkentity (bfg);
897: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.