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