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