|
|
1.1 root 1: #include "g_local.h"
2:
3: #define INCLUDE_ETF_RIFLE 1
4: #define INCLUDE_PROX 1
5: //#define INCLUDE_FLAMETHROWER 1
6: //#define INCLUDE_INCENDIARY 1
7: #define INCLUDE_NUKE 1
8: #define INCLUDE_MELEE 1
9: #define INCLUDE_TESLA 1
10: #define INCLUDE_BEAMS 1
11:
12: extern void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
13: extern void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
14: extern void droptofloor (edict_t *ent);
15: extern void Grenade_Explode (edict_t *ent);
16:
17: extern void drawbbox (edict_t *ent);
18:
19: #ifdef INCLUDE_ETF_RIFLE
20: /*
21: ========================
22: fire_flechette
23: ========================
24: */
25: void flechette_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
26: {
27: vec3_t dir;
28:
29: if (other == self->owner)
30: return;
31:
32: if (surf && (surf->flags & SURF_SKY))
33: {
34: G_FreeEdict (self);
35: return;
36: }
37:
38: if (self->client)
39: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
40:
41: if (other->takedamage)
42: {
43: //gi.dprintf("t_damage %s\n", other->classname);
44: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
45: self->dmg, self->dmg_radius, DAMAGE_NO_REG_ARMOR, MOD_ETF_RIFLE);
46: }
47: else
48: {
49: if(!plane)
50: VectorClear (dir);
51: else
52: VectorScale (plane->normal, 256, dir);
53: gi.WriteByte (svc_temp_entity);
54: gi.WriteByte (TE_FLECHETTE);
55: gi.WritePosition (self->s.origin);
56: gi.WriteDir (dir);
57: gi.multicast (self->s.origin, MULTICAST_PVS);
58:
59: // T_RadiusDamage(self, self->owner, 24, self, 48, MOD_ETF_RIFLE);
60: }
61:
62: G_FreeEdict (self);
63: }
64:
65: void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick)
66: {
67: edict_t *flechette;
68:
69: VectorNormalize (dir);
70:
71: flechette = G_Spawn();
72: VectorCopy (start, flechette->s.origin);
73: VectorCopy (start, flechette->s.old_origin);
74: vectoangles2 (dir, flechette->s.angles);
75:
76: VectorScale (dir, speed, flechette->velocity);
77: flechette->movetype = MOVETYPE_FLYMISSILE;
78: flechette->clipmask = MASK_SHOT;
79: flechette->solid = SOLID_BBOX;
80: flechette->s.renderfx = RF_FULLBRIGHT;
81: VectorClear (flechette->mins);
82: VectorClear (flechette->maxs);
83:
84: flechette->s.modelindex = gi.modelindex ("models/proj/flechette/tris.md2");
85:
86: // flechette->s.sound = gi.soundindex (""); // FIXME - correct sound!
87: flechette->owner = self;
88: flechette->touch = flechette_touch;
89: flechette->nextthink = level.time + 8000/speed;
90: flechette->think = G_FreeEdict;
91: flechette->dmg = damage;
92: flechette->dmg_radius = kick;
93:
94: gi.linkentity (flechette);
95:
96: if (self->client)
97: check_dodge (self, flechette->s.origin, dir, speed);
98: }
99: #endif
100:
101: // **************************
102: // PROX
103: // **************************
104:
105: #ifdef INCLUDE_PROX
106: #define PROX_TIME_TO_LIVE 45 // 45, 30, 15, 10
107: #define PROX_TIME_DELAY 0.5
108: #define PROX_BOUND_SIZE 96
109: #define PROX_DAMAGE_RADIUS 192
110: #define PROX_HEALTH 20
111: #define PROX_DAMAGE 90
112:
113: //===============
114: //===============
115: void Prox_Explode (edict_t *ent)
116: {
117: vec3_t origin;
118: edict_t *owner;
119:
120: // free the trigger field
121:
122: //PMM - changed teammaster to "mover" .. owner of the field is the prox
123: if(ent->teamchain && ent->teamchain->owner == ent)
124: G_FreeEdict(ent->teamchain);
125:
126: owner = ent;
127: if(ent->teammaster)
128: {
129: owner = ent->teammaster;
130: PlayerNoise(owner, ent->s.origin, PNOISE_IMPACT);
131: }
132:
133: // play quad sound if appopriate
134: if (ent->dmg > PROX_DAMAGE)
135: gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
136:
137: ent->takedamage = DAMAGE_NO;
138: T_RadiusDamage(ent, owner, ent->dmg, ent, PROX_DAMAGE_RADIUS, MOD_PROX);
139:
140: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
141: gi.WriteByte (svc_temp_entity);
142: if (ent->groundentity)
143: gi.WriteByte (TE_GRENADE_EXPLOSION);
144: else
145: gi.WriteByte (TE_ROCKET_EXPLOSION);
146: gi.WritePosition (origin);
147: gi.multicast (ent->s.origin, MULTICAST_PVS);
148:
149: G_FreeEdict (ent);
150: }
151:
152: //===============
153: //===============
154: void prox_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
155: {
156: // gi.dprintf("prox_die\n");
157: // if set off by another prox, delay a little (chained explosions)
158: if (strcmp(inflictor->classname, "prox"))
159: {
160: self->takedamage = DAMAGE_NO;
161: Prox_Explode(self);
162: }
163: else
164: {
165: self->takedamage = DAMAGE_NO;
166: self->think = Prox_Explode;
167: self->nextthink = level.time + FRAMETIME;
168: }
169: }
170:
171: //===============
172: //===============
173: void Prox_Field_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
174: {
175: edict_t *prox;
176:
177: if (!(other->svflags & SVF_MONSTER) && !other->client)
178: return;
179:
180: // trigger the prox mine if it's still there, and still mine.
181: prox = ent->owner;
182:
183: if (other == prox) // don't set self off
184: return;
185:
186: if (prox->think == Prox_Explode) // we're set to blow!
187: {
188: // if ((g_showlogic) && (g_showlogic->value))
189: // gi.dprintf ("%f - prox already gone off!\n", level.time);
190: return;
191: }
192:
193: if(prox->teamchain == ent)
194: {
195: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
196: prox->think = Prox_Explode;
197: prox->nextthink = level.time + PROX_TIME_DELAY;
198: return;
199: }
200:
201: ent->solid = SOLID_NOT;
202: G_FreeEdict(ent);
203: }
204:
205: //===============
206: //===============
207: void prox_seek (edict_t *ent)
208: {
209: if(level.time > ent->wait)
210: {
211: Prox_Explode(ent);
212: }
213: else
214: {
215: ent->s.frame++;
216: if(ent->s.frame > 13)
217: ent->s.frame = 9;
218: ent->think = prox_seek;
219: ent->nextthink = level.time + 0.1;
220: }
221: }
222:
223: //===============
224: //===============
225: void prox_open (edict_t *ent)
226: {
227: edict_t *search;
228:
229: search = NULL;
230: // gi.dprintf("prox_open %d\n", ent->s.frame);
231: // gi.dprintf("%f\n", ent->velocity[2]);
232: if(ent->s.frame == 9) // end of opening animation
233: {
234: // set the owner to NULL so the owner can shoot it, etc. needs to be done here so the owner
235: // doesn't get stuck on it while it's opening if fired at point blank wall
236: ent->owner = NULL;
237: if(ent->teamchain)
238: ent->teamchain->touch = Prox_Field_Touch;
239: while ((search = findradius(search, ent->s.origin, PROX_DAMAGE_RADIUS+10)) != NULL)
240: {
241: if (!search->classname) // tag token and other weird shit
242: continue;
243:
244: // if (!search->takedamage)
245: // continue;
246: // if it's a monster or player with health > 0
247: // or it's a player start point
248: // and we can see it
249: // blow up
250: if (
251: (
252: (((search->svflags & SVF_MONSTER) || (search->client)) && (search->health > 0)) ||
253: (
254: (deathmatch->value) &&
255: (
256: (!strcmp(search->classname, "info_player_deathmatch")) ||
257: (!strcmp(search->classname, "info_player_start")) ||
258: (!strcmp(search->classname, "info_player_coop"))
259: )
260: )
261: )
262: && (visible (search, ent))
263: )
264: {
265: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0);
266: Prox_Explode (ent);
267: return;
268: }
269: }
270:
271: switch (ent->dmg/PROX_DAMAGE)
272: {
273: case 1:
274: ent->wait = level.time + PROX_TIME_TO_LIVE;
275: break;
276: case 2:
277: ent->wait = level.time + 30;
278: break;
279: case 4:
280: ent->wait = level.time + 15;
281: break;
282: case 8:
283: ent->wait = level.time + 10;
284: break;
285: default:
286: if ((g_showlogic) && (g_showlogic->value))
287: gi.dprintf ("prox with unknown multiplier %d!\n", ent->dmg/PROX_DAMAGE);
288: ent->wait = level.time + PROX_TIME_TO_LIVE;
289: break;
290: }
291:
292: // ent->wait = level.time + PROX_TIME_TO_LIVE;
293: ent->think = prox_seek;
294: ent->nextthink = level.time + 0.2;
295: }
296: else
297: {
298: ent->s.frame++;
299: ent->think = prox_open;
300: ent->nextthink = level.time + 0.05;
301: }
302: }
303:
304: //===============
305: //===============
306: void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
307: {
308: edict_t *field;
309: vec3_t dir;
310: vec3_t forward, right, up;
311: int makeslave = 0;
312: int movetype = MOVETYPE_NONE;
313: int stick_ok = 0;
314: vec3_t land_point;
315:
316: // must turn off owner so owner can shoot it and set it off
317: // moved to prox_open so owner can get away from it if fired at pointblank range into
318: // wall
319: // ent->owner = NULL;
320:
321: // if ((g_showlogic) && (g_showlogic->value))
322: // gi.dprintf ("land - %2.2f %2.2f %2.2f\n", ent->velocity[0], ent->velocity[1], ent->velocity[2]);
323:
324: if (surf && (surf->flags & SURF_SKY))
325: {
326: G_FreeEdict(ent);
327: return;
328: }
329:
330: if (plane->normal)
331: {
332: VectorMA (ent->s.origin, -10.0, plane->normal, land_point);
333: if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
334: {
335: Prox_Explode (ent);
336: return;
337: }
338: }
339:
340: if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE))
341: {
342: if(other != ent->teammaster)
343: Prox_Explode(ent);
344:
345: return;
346: }
347:
348: #define STOP_EPSILON 0.1
349:
350: else if (other != world)
351: {
352: //Here we need to check to see if we can stop on this entity.
353: //Note that plane can be NULL
354:
355: //PMM - code stolen from g_phys (ClipVelocity)
356: vec3_t out;
357: float backoff, change;
358: int i;
359:
360: if (!plane->normal) // this happens if you hit a point object, maybe other cases
361: {
362: // Since we can't tell what's going to happen, just blow up
363: if ((g_showlogic) && (g_showlogic->value))
364: gi.dprintf ("bad normal for surface, exploding!\n");
365:
366: Prox_Explode(ent);
367: return;
368: }
369:
370: if ((other->movetype == MOVETYPE_PUSH) && (plane->normal[2] > 0.7))
371: stick_ok = 1;
372: else
373: stick_ok = 0;
374:
375: backoff = DotProduct (ent->velocity, plane->normal) * 1.5;
376: for (i=0 ; i<3 ; i++)
377: {
378: change = plane->normal[i]*backoff;
379: out[i] = ent->velocity[i] - change;
380: if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
381: out[i] = 0;
382: }
383:
384: if (out[2] > 60)
385: return;
386:
387: movetype = MOVETYPE_BOUNCE;
388:
389: // if we're here, we're going to stop on an entity
390: if (stick_ok)
391: { // it's a happy entity
392: VectorCopy (vec3_origin, ent->velocity);
393: VectorCopy (vec3_origin, ent->avelocity);
394: }
395: else // no-stick. teflon time
396: {
397: if (plane->normal[2] > 0.7)
398: {
399: if ((g_showlogic) && (g_showlogic->value))
400: gi.dprintf ("stuck on entity, blowing up!\n");
401:
402: Prox_Explode(ent);
403: return;
404: }
405: return;
406: }
407: }
408: else if (other->s.modelindex != 1)
409: return;
410:
411: vectoangles2 (plane->normal, dir);
412: AngleVectors (dir, forward, right, up);
413:
414: if (gi.pointcontents (ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
415: {
416: Prox_Explode (ent);
417: return;
418: }
419:
420: field = G_Spawn();
421:
422: VectorCopy (ent->s.origin, field->s.origin);
423: VectorClear(field->velocity);
424: VectorClear(field->avelocity);
425: VectorSet(field->mins, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE);
426: VectorSet(field->maxs, PROX_BOUND_SIZE, PROX_BOUND_SIZE, PROX_BOUND_SIZE);
427: field->movetype = MOVETYPE_NONE;
428: field->solid = SOLID_TRIGGER;
429: field->owner = ent;
430: field->classname = "prox_field";
431: field->teammaster = ent;
432: gi.linkentity (field);
433:
434: VectorClear(ent->velocity);
435: VectorClear(ent->avelocity);
436: // rotate to vertical
437: dir[PITCH] = dir[PITCH] + 90;
438: VectorCopy (dir, ent->s.angles);
439: ent->takedamage = DAMAGE_AIM;
440: ent->movetype = movetype; // either bounce or none, depending on whether we stuck to something
441: ent->die = prox_die;
442: ent->teamchain = field;
443: ent->health = PROX_HEALTH;
444: ent->nextthink = level.time + 0.05;
445: ent->think = prox_open;
446: ent->touch = NULL;
447: ent->solid = SOLID_BBOX;
448: // record who we're attached to
449: // ent->teammaster = other;
450:
451: gi.linkentity(ent);
452: }
453:
454: //===============
455: //===============
456: void fire_prox (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
457: {
458: edict_t *prox;
459: vec3_t dir;
460: vec3_t forward, right, up;
461:
462: vectoangles2 (aimdir, dir);
463: AngleVectors (dir, forward, right, up);
464:
465: // if ((g_showlogic) && (g_showlogic->value))
466: // gi.dprintf ("start %s aim %s speed %d\n", vtos(start), vtos(aimdir), speed);
467: prox = G_Spawn();
468: VectorCopy (start, prox->s.origin);
469: VectorScale (aimdir, speed, prox->velocity);
470: VectorMA (prox->velocity, 200 + crandom() * 10.0, up, prox->velocity);
471: VectorMA (prox->velocity, crandom() * 10.0, right, prox->velocity);
472: VectorCopy (dir, prox->s.angles);
473: prox->s.angles[PITCH]-=90;
474: prox->movetype = MOVETYPE_BOUNCE;
475: prox->solid = SOLID_BBOX;
476: prox->s.effects |= EF_GRENADE;
477: prox->clipmask = MASK_SHOT|CONTENTS_LAVA|CONTENTS_SLIME;
478: prox->s.renderfx |= RF_IR_VISIBLE;
479: //FIXME - this needs to be bigger. Has other effects, though. Maybe have to change origin to compensate
480: // so it sinks in correctly. Also in lavacheck, might have to up the distance
481: VectorSet (prox->mins, -6, -6, -6);
482: VectorSet (prox->maxs, 6, 6, 6);
483: prox->s.modelindex = gi.modelindex ("models/weapons/g_prox/tris.md2");
484: prox->owner = self;
485: prox->teammaster = self;
486: prox->touch = prox_land;
487: // prox->nextthink = level.time + PROX_TIME_TO_LIVE;
488: prox->think = Prox_Explode;
489: prox->dmg = PROX_DAMAGE*damage_multiplier;
490: prox->classname = "prox";
491: prox->svflags |= SVF_DAMAGEABLE;
492: prox->flags |= FL_MECHANICAL;
493:
494: switch (damage_multiplier)
495: {
496: case 1:
497: prox->nextthink = level.time + PROX_TIME_TO_LIVE;
498: break;
499: case 2:
500: prox->nextthink = level.time + 30;
501: break;
502: case 4:
503: prox->nextthink = level.time + 15;
504: break;
505: case 8:
506: prox->nextthink = level.time + 10;
507: break;
508: default:
509: if ((g_showlogic) && (g_showlogic->value))
510: gi.dprintf ("prox with unknown multiplier %d!\n", damage_multiplier);
511: prox->nextthink = level.time + PROX_TIME_TO_LIVE;
512: break;
513: }
514:
515: gi.linkentity (prox);
516: }
517: #endif
518:
519: // *************************
520: // FLAMETHROWER
521: // *************************
522:
523: #ifdef INCLUDE_FLAMETHROWER
524: #define FLAMETHROWER_RADIUS 8
525:
526: void fire_remove (edict_t *ent)
527: {
528: if(ent == ent->owner->teamchain)
529: ent->owner->teamchain = NULL;
530:
531: G_FreeEdict(ent);
532: }
533:
534: void fire_flame (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
535: {
536: edict_t *flame;
537: vec3_t dir;
538: vec3_t forward, right, up;
539:
540: vectoangles2 (aimdir, dir);
541: AngleVectors (dir, forward, right, up);
542:
543: flame = G_Spawn();
544:
545: // the origin is the first control point, put it speed forward.
546: VectorMA(start, speed, forward, flame->s.origin);
547:
548: // record that velocity
549: VectorScale (aimdir, speed, flame->velocity);
550:
551: VectorCopy (dir, flame->s.angles);
552: flame->movetype = MOVETYPE_NONE;
553: flame->solid = SOLID_NOT;
554:
555: VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
556: VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
557:
558: flame->s.sound = gi.soundindex ("weapons/flame.wav");
559: flame->owner = self;
560: flame->dmg = damage;
561: flame->classname = "flame";
562:
563: // clear control points and velocities
564: VectorCopy (flame->s.origin, flame->flameinfo.pos1);
565: VectorCopy (flame->velocity, flame->flameinfo.vel1);
566: VectorCopy (flame->s.origin, flame->flameinfo.pos2);
567: VectorCopy (flame->velocity, flame->flameinfo.vel2);
568: VectorCopy (flame->s.origin, flame->flameinfo.pos3);
569: VectorCopy (flame->velocity, flame->flameinfo.vel3);
570: VectorCopy (flame->s.origin, flame->flameinfo.pos4);
571:
572: // hook flame stream to owner
573: self->teamchain = flame;
574:
575: gi.linkentity (flame);
576: }
577:
578: // fixme - change to use start location, not entity origin
579: void fire_maintain (edict_t *ent, edict_t *flame, vec3_t start, vec3_t aimdir, int damage, int speed)
580: {
581: trace_t tr;
582:
583: // move the control points out the appropriate direction and velocity
584: VectorAdd(flame->flameinfo.pos3, flame->flameinfo.vel3, flame->flameinfo.pos4);
585: VectorAdd(flame->flameinfo.pos2, flame->flameinfo.vel2, flame->flameinfo.pos3);
586: VectorAdd(flame->flameinfo.pos1, flame->flameinfo.vel1, flame->flameinfo.pos2);
587: VectorAdd(flame->s.origin, flame->velocity, flame->flameinfo.pos1);
588:
589: // move the velocities for the control points
590: VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
591: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
592: VectorCopy(flame->velocity, flame->flameinfo.vel1);
593:
594: // set velocity and location for new control point 0.
595: VectorMA(start, speed, aimdir, flame->s.origin);
596: VectorScale(aimdir, speed, flame->velocity);
597:
598: //
599: // does it hit a wall? if so, when?
600: //
601:
602: // player fire point to flame origin.
603: tr = gi.trace(start, flame->mins, flame->maxs,
604: flame->s.origin, flame, MASK_SHOT);
605: if(tr.fraction == 1.0)
606: {
607: // origin to point 1
608: tr = gi.trace(flame->s.origin, flame->mins, flame->maxs,
609: flame->flameinfo.pos1, flame, MASK_SHOT);
610: if(tr.fraction == 1.0)
611: {
612: // point 1 to point 2
613: tr = gi.trace(flame->flameinfo.pos1, flame->mins, flame->maxs,
614: flame->flameinfo.pos2, flame, MASK_SHOT);
615: if(tr.fraction == 1.0)
616: {
617: // point 2 to point 3
618: tr = gi.trace(flame->flameinfo.pos2, flame->mins, flame->maxs,
619: flame->flameinfo.pos3, flame, MASK_SHOT);
620: if(tr.fraction == 1.0)
621: {
622: // point 3 to point 4, point 3 valid
623: tr = gi.trace(flame->flameinfo.pos3, flame->mins, flame->maxs,
624: flame->flameinfo.pos4, flame, MASK_SHOT);
625: if(tr.fraction < 1.0) // point 4 blocked
626: {
627: VectorCopy(tr.endpos, flame->flameinfo.pos4);
628: }
629: }
630: else // point 3 blocked, point 2 valid
631: {
632: VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3);
633: VectorCopy(tr.endpos, flame->flameinfo.pos3);
634: VectorCopy(tr.endpos, flame->flameinfo.pos4);
635: }
636: }
637: else // point 2 blocked, point 1 valid
638: {
639: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2);
640: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel3);
641: VectorCopy(tr.endpos, flame->flameinfo.pos2);
642: VectorCopy(tr.endpos, flame->flameinfo.pos3);
643: VectorCopy(tr.endpos, flame->flameinfo.pos4);
644: }
645: }
646: else // point 1 blocked, origin valid
647: {
648: VectorCopy(flame->velocity, flame->flameinfo.vel1);
649: VectorCopy(flame->velocity, flame->flameinfo.vel2);
650: VectorCopy(flame->velocity, flame->flameinfo.vel3);
651: VectorCopy(tr.endpos, flame->flameinfo.pos1);
652: VectorCopy(tr.endpos, flame->flameinfo.pos2);
653: VectorCopy(tr.endpos, flame->flameinfo.pos3);
654: VectorCopy(tr.endpos, flame->flameinfo.pos4);
655: }
656: }
657: else // origin blocked!
658: {
659: // gi.dprintf("point 2 blocked\n");
660: VectorCopy(flame->velocity, flame->flameinfo.vel1);
661: VectorCopy(flame->velocity, flame->flameinfo.vel2);
662: VectorCopy(flame->velocity, flame->flameinfo.vel3);
663: VectorCopy(tr.endpos, flame->s.origin);
664: VectorCopy(tr.endpos, flame->flameinfo.pos1);
665: VectorCopy(tr.endpos, flame->flameinfo.pos2);
666: VectorCopy(tr.endpos, flame->flameinfo.pos3);
667: VectorCopy(tr.endpos, flame->flameinfo.pos4);
668: }
669:
670: if(tr.fraction < 1.0 && tr.ent->takedamage)
671: {
672: T_Damage (tr.ent, flame, ent, flame->velocity, tr.endpos, tr.plane.normal,
673: damage, 0, DAMAGE_NO_KNOCKBACK | DAMAGE_ENERGY | DAMAGE_FIRE);
674: }
675:
676: gi.linkentity(flame);
677:
678: gi.WriteByte (svc_temp_entity);
679: gi.WriteByte (TE_FLAME);
680: gi.WriteShort(ent - g_edicts);
681: gi.WriteShort(6);
682: gi.WritePosition (start);
683: gi.WritePosition (flame->s.origin);
684: gi.WritePosition (flame->flameinfo.pos1);
685: gi.WritePosition (flame->flameinfo.pos2);
686: gi.WritePosition (flame->flameinfo.pos3);
687: gi.WritePosition (flame->flameinfo.pos4);
688: gi.multicast (flame->s.origin, MULTICAST_PVS);
689: }
690:
691: /*QUAKED trap_flameshooter (1 0 0) (-8 -8 -8) (8 8 8)
692: */
693: #define FLAMESHOOTER_VELOCITY 50
694: #define FLAMESHOOTER_DAMAGE 20
695: #define FLAMESHOOTER_BURST_VELOCITY 300
696: #define FLAMESHOOTER_BURST_DAMAGE 30
697:
698: //#define FLAMESHOOTER_PUFF 1
699: #define FLAMESHOOTER_STREAM 1
700:
701: void flameshooter_think (edict_t *self)
702: {
703: vec3_t forward, right, up;
704: edict_t *flame;
705:
706: if(self->delay)
707: {
708: if(self->teamchain)
709: fire_remove (self->teamchain);
710: return;
711: }
712:
713: self->s.angles[1] += self->speed;
714: if(self->s.angles[1] > 135 || self->s.angles[1] < 45)
715: self->speed = -self->speed;
716:
717: AngleVectors (self->s.angles, forward, right, up);
718:
719: #ifdef FLAMESHOOTER_STREAM
720: flame = self->teamchain;
721: if(!self->teamchain)
722: fire_flame (self, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
723: else
724: fire_maintain (self, flame, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY);
725:
726: self->think = flameshooter_think;
727: self->nextthink = level.time + 0.05;
728: #else
729: fire_burst (self, self->s.origin, forward, FLAMESHOOTER_BURST_DAMAGE, FLAMESHOOTER_BURST_VELOCITY);
730:
731: self->think = flameshooter_think;
732: self->nextthink = level.time + 0.1;
733: #endif
734: }
735:
736: void flameshooter_use (edict_t *self, edict_t *other, edict_t *activator)
737: {
738: if(self->delay)
739: {
740: self->delay = 0;
741: self->think = flameshooter_think;
742: self->nextthink = level.time + 0.1;
743: }
744: else
745: self->delay = 1;
746: }
747:
748: void SP_trap_flameshooter(edict_t *self)
749: {
750: vec3_t tempAngles;
751:
752: self->solid = SOLID_NOT;
753: self->movetype = MOVETYPE_NONE;
754:
755: self->delay = 0;
756:
757: self->use = flameshooter_use;
758: if(self->delay == 0)
759: {
760: self->think = flameshooter_think;
761: self->nextthink = level.time + 0.1;
762: }
763:
764: // self->flags |= FL_NOCLIENT;
765:
766: self->speed = 10;
767:
768: // self->speed = 0; // FIXME this stops the spraying
769:
770: VectorCopy(self->s.angles, tempAngles);
771:
772: if (!VectorCompare(self->s.angles, vec3_origin))
773: G_SetMovedir (self->s.angles, self->movedir);
774:
775: VectorCopy(tempAngles, self->s.angles);
776:
777: // gi.setmodel (self, self->model);
778: gi.linkentity (self);
779: }
780:
781: // *************************
782: // fire_burst
783: // *************************
784:
785: #define FLAME_BURST_MAX_SIZE 64
786: #define FLAME_BURST_FRAMES 20
787: #define FLAME_BURST_MIDPOINT 10
788:
789: void fire_burst_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
790: {
791: int powerunits;
792: int damage, radius;
793: vec3_t origin;
794:
795: if (surf && (surf->flags & SURF_SKY))
796: {
797: // gi.dprintf("Hit sky. Removed\n");
798: G_FreeEdict (ent);
799: return;
800: }
801:
802: if(other == ent->owner || ent == other)
803: return;
804:
805: // don't let flame puffs blow each other up
806: if(other->classname && !strcmp(other->classname, ent->classname))
807: return;
808:
809: if(ent->waterlevel)
810: {
811: // gi.dprintf("Hit water. Removed\n");
812: G_FreeEdict(ent);
813: }
814:
815: if(!(other->svflags & SVF_MONSTER) && !other->client)
816: {
817: powerunits = FLAME_BURST_FRAMES - ent->s.frame;
818: damage = powerunits * 6;
819: radius = powerunits * 4;
820:
821: // T_RadiusDamage (inflictor, attacker, damage, ignore, radius)
822: T_RadiusDamage(ent, ent->owner, damage, ent, radius, DAMAGE_FIRE);
823:
824: // gi.dprintf("Hit world: %d pts, %d rad\n", damage, radius);
825:
826: // calculate position for the explosion entity
827: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
828:
829: gi.WriteByte (svc_temp_entity);
830: gi.WriteByte (TE_PLAIN_EXPLOSION);
831: gi.WritePosition (origin);
832: gi.multicast (ent->s.origin, MULTICAST_PVS);
833:
834: G_FreeEdict (ent);
835: }
836: }
837:
838: void fire_burst_think (edict_t *self)
839: {
840: int current_radius;
841:
842: if(self->waterlevel)
843: {
844: G_FreeEdict(self);
845: return;
846: }
847:
848: self->s.frame++;
849: if(self->s.frame >= FLAME_BURST_FRAMES)
850: {
851: G_FreeEdict(self);
852: return;
853: }
854:
855: else if(self->s.frame < FLAME_BURST_MIDPOINT)
856: {
857: current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * self->s.frame;
858: }
859: else
860: {
861: current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * (FLAME_BURST_FRAMES - self->s.frame);
862: }
863:
864: if(self->s.frame == 3)
865: self->s.skinnum = 1;
866: else if (self->s.frame == 7)
867: self->s.skinnum = 2;
868: else if (self->s.frame == 10)
869: self->s.skinnum = 3;
870: else if (self->s.frame == 13)
871: self->s.skinnum = 4;
872: else if (self->s.frame == 16)
873: self->s.skinnum = 5;
874: else if (self->s.frame == 19)
875: self->s.skinnum = 6;
876:
877: if(current_radius < 8)
878: current_radius = 8;
879: else if(current_radius > FLAME_BURST_MAX_SIZE)
880: current_radius = FLAME_BURST_MAX_SIZE;
881:
882: T_RadiusDamage(self, self->owner, self->dmg, self, current_radius, DAMAGE_FIRE);
883:
884: self->think = fire_burst_think;
885: self->nextthink = level.time + 0.1;
886: }
887:
888: void fire_burst (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
889: {
890: edict_t *flame;
891: vec3_t dir;
892: vec3_t baseVel;
893: vec3_t forward, right, up;
894:
895: vectoangles2 (aimdir, dir);
896: AngleVectors (dir, forward, right, up);
897:
898: flame = G_Spawn();
899: VectorCopy(start, flame->s.origin);
900: // VectorScale (aimdir, speed, flame->velocity);
901:
902: // scale down so only 30% of player's velocity is taken into account.
903: VectorScale (self->velocity, 0.3, baseVel);
904: VectorMA(baseVel, speed, aimdir, flame->velocity);
905:
906: VectorCopy (dir, flame->s.angles);
907: flame->movetype = MOVETYPE_FLY;
908: flame->solid = SOLID_TRIGGER;
909:
910: VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS);
911: VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS);
912:
913: flame->s.sound = gi.soundindex ("weapons/flame.wav");
914: flame->s.modelindex = gi.modelindex ("models/projectiles/puff/tris.md2");
915: flame->owner = self;
916: flame->touch = fire_burst_touch;
917: flame->think = fire_burst_think;
918: flame->nextthink = level.time + 0.1;
919: flame->dmg = damage;
920: flame->classname = "flameburst";
921: flame->s.effects = EF_FIRE_PUFF;
922:
923: gi.linkentity (flame);
924: }
925: #endif
926:
927: // *************************
928: // INCENDIARY GRENADES
929: // *************************
930:
931: #ifdef INCLUDE_INCENDIARY
932: void FireThink (edict_t *ent)
933: {
934: if(level.time > ent->wait)
935: G_FreeEdict(ent);
936: else
937: {
938: ent->s.frame++;
939: if(ent->s.frame>10)
940: ent->s.frame = 0;
941: ent->nextthink = level.time + 0.05;
942: ent->think = FireThink;
943: }
944: }
945:
946: #define FIRE_HEIGHT 64
947: #define FIRE_RADIUS 64
948: #define FIRE_DAMAGE 3
949: #define FIRE_DURATION 15
950:
951: edict_t *StartFire(edict_t *fireOwner, vec3_t fireOrigin, float fireDuration, float fireDamage)
952: {
953: edict_t *fire;
954:
955: fire = G_Spawn();
956: VectorCopy (fireOrigin, fire->s.origin);
957: fire->movetype = MOVETYPE_TOSS;
958: fire->solid = SOLID_TRIGGER;
959: VectorSet(fire->mins, -FIRE_RADIUS, -FIRE_RADIUS, 0);
960: VectorSet(fire->maxs, FIRE_RADIUS, FIRE_RADIUS, FIRE_HEIGHT);
961:
962: fire->s.sound = gi.soundindex ("weapons/incend.wav");
963: fire->s.modelindex = gi.modelindex ("models/objects/fire/tris.md2");
964:
965: fire->owner = fireOwner;
966: fire->touch = hurt_touch;
967: fire->nextthink = level.time + 0.05;
968: fire->wait = level.time + fireDuration;
969: fire->think = FireThink;
970: // fire->nextthink = level.time + fireDuration;
971: // fire->think = G_FreeEdict;
972: fire->dmg = fireDamage;
973: fire->classname = "incendiary_fire";
974:
975: gi.linkentity (fire);
976:
977: // gi.sound (fire, CHAN_VOICE, gi.soundindex ("weapons/incend.wav"), 1, ATTN_NORM, 0);
978: return fire;
979: }
980:
981: static void Incendiary_Explode (edict_t *ent)
982: {
983: vec3_t origin;
984:
985: if (ent->owner->client)
986: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
987:
988: //FIXME: if we are onground then raise our Z just a bit since we are a point?
989: T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius, DAMAGE_FIRE);
990:
991: VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
992: gi.WriteByte (svc_temp_entity);
993: if (ent->groundentity)
994: gi.WriteByte (TE_GRENADE_EXPLOSION);
995: else
996: gi.WriteByte (TE_ROCKET_EXPLOSION);
997: gi.WritePosition (origin);
998: gi.multicast (ent->s.origin, MULTICAST_PVS);
999:
1000: StartFire(ent->owner, ent->s.origin, FIRE_DURATION, FIRE_DAMAGE);
1001:
1002: G_FreeEdict (ent);
1003:
1004: }
1005:
1006: static void Incendiary_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1007: {
1008: if (other == ent->owner)
1009: return;
1010:
1011: if (surf && (surf->flags & SURF_SKY))
1012: {
1013: G_FreeEdict (ent);
1014: return;
1015: }
1016:
1017: if (!(other->svflags & SVF_MONSTER) && !(ent->client))
1018: // if (!other->takedamage)
1019: {
1020: if (ent->spawnflags & 1)
1021: {
1022: if (random() > 0.5)
1023: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1024: else
1025: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1026: }
1027: else
1028: {
1029: if (random() > 0.5)
1030: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
1031: else
1032: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb2b.wav"), 1, ATTN_NORM, 0);
1033: }
1034: return;
1035: }
1036:
1037: Incendiary_Explode (ent);
1038: }
1039:
1040: void fire_incendiary_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
1041: {
1042: edict_t *grenade;
1043: vec3_t dir;
1044: vec3_t forward, right, up;
1045:
1046: vectoangles2 (aimdir, dir);
1047: AngleVectors (dir, forward, right, up);
1048:
1049: grenade = G_Spawn();
1050: VectorCopy (start, grenade->s.origin);
1051: VectorScale (aimdir, speed, grenade->velocity);
1052: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
1053: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
1054: VectorSet (grenade->avelocity, 300, 300, 300);
1055: grenade->movetype = MOVETYPE_BOUNCE;
1056: grenade->clipmask = MASK_SHOT;
1057: grenade->solid = SOLID_BBOX;
1058: grenade->s.effects |= EF_GRENADE;
1059: // if (self->client)
1060: // grenade->s.effects &= ~EF_TELEPORT;
1061: VectorClear (grenade->mins);
1062: VectorClear (grenade->maxs);
1063: grenade->s.modelindex = gi.modelindex ("models/projectiles/incend/tris.md2");
1064: grenade->owner = self;
1065: grenade->touch = Incendiary_Touch;
1066: grenade->nextthink = level.time + timer;
1067: grenade->think = Incendiary_Explode;
1068: grenade->dmg = damage;
1069: grenade->dmg_radius = damage_radius;
1070: grenade->classname = "incendiary_grenade";
1071:
1072: gi.linkentity (grenade);
1073: }
1074: #endif
1075:
1076: // *************************
1077: // MELEE WEAPONS
1078: // *************************
1079:
1080: #ifdef INCLUDE_MELEE
1081: void fire_player_melee (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod)
1082: {
1083: vec3_t forward, right, up;
1084: vec3_t v;
1085: vec3_t point;
1086: trace_t tr;
1087:
1088: vectoangles2 (aim, v);
1089: AngleVectors (v, forward, right, up);
1090: VectorNormalize (forward);
1091: VectorMA( start, reach, forward, point);
1092:
1093: //see if the hit connects
1094: tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT);
1095: if(tr.fraction == 1.0)
1096: {
1097: if(!quiet)
1098: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0);
1099: //FIXME some sound here?
1100: return;
1101: }
1102:
1103: if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM)
1104: {
1105: // pull the player forward if you do damage
1106: VectorMA(self->velocity, 75, forward, self->velocity);
1107: VectorMA(self->velocity, 75, up, self->velocity);
1108:
1109: // do the damage
1110: // FIXME - make the damage appear at right spot and direction
1111: if(mod == MOD_CHAINFIST)
1112: T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2,
1113: DAMAGE_DESTROY_ARMOR | DAMAGE_NO_KNOCKBACK, mod);
1114: else
1115: T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, mod);
1116:
1117: if(!quiet)
1118: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0);
1119: }
1120: else
1121: {
1122: if(!quiet)
1123: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0);
1124:
1125: VectorScale (tr.plane.normal, 256, point);
1126: gi.WriteByte (svc_temp_entity);
1127: gi.WriteByte (TE_GUNSHOT);
1128: gi.WritePosition (tr.endpos);
1129: gi.WriteDir (point);
1130: gi.multicast (tr.endpos, MULTICAST_PVS);
1131: }
1132: }
1133: #endif
1134:
1135: // *************************
1136: // NUKE
1137: // *************************
1138:
1139: #ifdef INCLUDE_NUKE
1140: #define NUKE_DELAY 4
1141: #define NUKE_TIME_TO_LIVE 6
1142: //#define NUKE_TIME_TO_LIVE 40
1143: #define NUKE_RADIUS 512
1144: #define NUKE_DAMAGE 400
1145: #define NUKE_QUAKE_TIME 3
1146: #define NUKE_QUAKE_STRENGTH 100
1147:
1148: void Nuke_Quake (edict_t *self)
1149: {
1150: int i;
1151: edict_t *e;
1152:
1153: if (self->last_move_time < level.time)
1154: {
1155: gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0);
1156: self->last_move_time = level.time + 0.5;
1157: }
1158:
1159: for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
1160: {
1161: if (!e->inuse)
1162: continue;
1163: if (!e->client)
1164: continue;
1165: if (!e->groundentity)
1166: continue;
1167:
1168: e->groundentity = NULL;
1169: e->velocity[0] += crandom()* 150;
1170: e->velocity[1] += crandom()* 150;
1171: e->velocity[2] = self->speed * (100.0 / e->mass);
1172: }
1173:
1174: if (level.time < self->timestamp)
1175: self->nextthink = level.time + FRAMETIME;
1176: else
1177: G_FreeEdict (self);
1178: }
1179:
1180:
1181: static void Nuke_Explode (edict_t *ent)
1182: {
1183: // vec3_t origin;
1184:
1185: // nuke_framenum = level.framenum + 20;
1186:
1187: if (ent->teammaster->client)
1188: PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT);
1189:
1190: T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NUKE);
1191:
1192: // VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
1193: if (ent->dmg > NUKE_DAMAGE)
1194: gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1195:
1196: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0);
1197: /*
1198: gi.WriteByte (svc_temp_entity);
1199: if (ent->groundentity)
1200: gi.WriteByte (TE_GRENADE_EXPLOSION);
1201: else
1202: gi.WriteByte (TE_ROCKET_EXPLOSION);
1203: gi.WritePosition (origin);
1204: gi.multicast (ent->s.origin, MULTICAST_PVS);
1205: */
1206:
1207: // BecomeExplosion1(ent);
1208:
1209: gi.WriteByte (svc_temp_entity);
1210: gi.WriteByte (TE_EXPLOSION1_BIG);
1211: gi.WritePosition (ent->s.origin);
1212: gi.multicast (ent->s.origin, MULTICAST_PVS);
1213:
1214: gi.WriteByte (svc_temp_entity);
1215: gi.WriteByte (TE_NUKEBLAST);
1216: gi.WritePosition (ent->s.origin);
1217: gi.multicast (ent->s.origin, MULTICAST_ALL);
1218:
1219: // become a quake
1220: ent->svflags |= SVF_NOCLIENT;
1221: ent->noise_index = gi.soundindex ("world/rumble.wav");
1222: ent->think = Nuke_Quake;
1223: ent->speed = NUKE_QUAKE_STRENGTH;
1224: ent->timestamp = level.time + NUKE_QUAKE_TIME;
1225: ent->nextthink = level.time + FRAMETIME;
1226: ent->last_move_time = 0;
1227: }
1228:
1229: void nuke_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1230: {
1231: self->takedamage = DAMAGE_NO;
1232: if ((attacker) && !(strcmp(attacker->classname, "nuke")))
1233: {
1234: if ((g_showlogic) && (g_showlogic->value))
1235: gi.dprintf ("nuke nuked by a nuke, not nuking\n");
1236: G_FreeEdict (self);
1237: return;
1238: }
1239: Nuke_Explode(self);
1240: }
1241:
1242: void Nuke_Think(edict_t *ent)
1243: {
1244: float attenuation, default_atten = 1.8;
1245: int damage_multiplier, muzzleflash;
1246:
1247: // gi.dprintf ("player range: %2.2f damage radius: %2.2f\n", realrange (ent, ent->teammaster), ent->dmg_radius*2);
1248:
1249: damage_multiplier = ent->dmg/NUKE_DAMAGE;
1250: switch (damage_multiplier)
1251: {
1252: case 1:
1253: attenuation = default_atten/1.4;
1254: muzzleflash = MZ_NUKE1;
1255: break;
1256: case 2:
1257: attenuation = default_atten/2.0;
1258: muzzleflash = MZ_NUKE2;
1259: break;
1260: case 4:
1261: attenuation = default_atten/3.0;
1262: muzzleflash = MZ_NUKE4;
1263: break;
1264: case 8:
1265: attenuation = default_atten/5.0;
1266: muzzleflash = MZ_NUKE8;
1267: break;
1268: default:
1269: if ((g_showlogic) && (g_showlogic->value))
1270: gi.dprintf ("default attenuation used for nuke!\n");
1271: attenuation = default_atten;
1272: muzzleflash = MZ_NUKE1;
1273: break;
1274: }
1275:
1276: if(ent->wait < level.time)
1277: Nuke_Explode(ent);
1278: else if (level.time >= (ent->wait - NUKE_TIME_TO_LIVE))
1279: {
1280: ent->s.frame++;
1281: // if ((g_showlogic) && (g_showlogic->value))
1282: // gi.dprintf ("nuke frame %d\n", ent->s.frame);
1283: if(ent->s.frame > 11)
1284: ent->s.frame = 6;
1285:
1286: if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
1287: {
1288: Nuke_Explode (ent);
1289: return;
1290: }
1291:
1292: ent->think = Nuke_Think;
1293: ent->nextthink = level.time + 0.1;
1294: ent->health = 1;
1295: ent->owner = NULL;
1296:
1297: gi.WriteByte (svc_muzzleflash);
1298: gi.WriteShort (ent-g_edicts);
1299: gi.WriteByte (muzzleflash);
1300: gi.multicast (ent->s.origin, MULTICAST_PVS);
1301:
1302: if (ent->timestamp <= level.time)
1303: {
1304: /* gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0);
1305: ent->timestamp += 10.0;
1306: }
1307: */
1308:
1309: if ((ent->wait - level.time) <= (NUKE_TIME_TO_LIVE/2.0))
1310: {
1311: // ent->s.sound = gi.soundindex ("weapons/nukewarn.wav");
1312: // gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1313: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1314: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1315: // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1316: ent->timestamp = level.time + 0.3;
1317: }
1318: else
1319: {
1320: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1321: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1322: ent->timestamp = level.time + 0.5;
1323: // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1324: }
1325: }
1326: }
1327: else
1328: {
1329: if (ent->timestamp <= level.time)
1330: {
1331: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0);
1332: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0);
1333: // gi.dprintf ("time %2.2f\n", ent->wait-level.time);
1334: ent->timestamp = level.time + 1.0;
1335: }
1336: ent->nextthink = level.time + FRAMETIME;
1337: }
1338: }
1339:
1340: void nuke_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1341: {
1342: if (random() > 0.5)
1343: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1344: else
1345: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1346: }
1347:
1348:
1349: extern byte P_DamageModifier(edict_t *ent);
1350:
1351: void fire_nuke (edict_t *self, vec3_t start, vec3_t aimdir, int speed)
1352: {
1353: edict_t *nuke;
1354: vec3_t dir;
1355: vec3_t forward, right, up;
1356: int damage_modifier;
1357:
1358: damage_modifier = (int) P_DamageModifier (self);
1359:
1360: vectoangles2 (aimdir, dir);
1361: AngleVectors (dir, forward, right, up);
1362:
1363: nuke = G_Spawn();
1364: VectorCopy (start, nuke->s.origin);
1365: VectorScale (aimdir, speed, nuke->velocity);
1366:
1367: VectorMA (nuke->velocity, 200 + crandom() * 10.0, up, nuke->velocity);
1368: VectorMA (nuke->velocity, crandom() * 10.0, right, nuke->velocity);
1369: VectorClear (nuke->avelocity);
1370: VectorClear (nuke->s.angles);
1371: nuke->movetype = MOVETYPE_BOUNCE;
1372: nuke->clipmask = MASK_SHOT;
1373: nuke->solid = SOLID_BBOX;
1374: nuke->s.effects |= EF_GRENADE;
1375: nuke->s.renderfx |= RF_IR_VISIBLE;
1376: VectorSet (nuke->mins, -8, -8, 0);
1377: VectorSet (nuke->maxs, 8, 8, 16);
1378: nuke->s.modelindex = gi.modelindex ("models/weapons/g_nuke/tris.md2");
1379: nuke->owner = self;
1380: nuke->teammaster = self;
1381: nuke->nextthink = level.time + FRAMETIME;
1382: nuke->wait = level.time + NUKE_DELAY + NUKE_TIME_TO_LIVE;
1383: nuke->think = Nuke_Think;
1384: nuke->touch = nuke_bounce;
1385:
1386: nuke->health = 10000;
1387: nuke->takedamage = DAMAGE_YES;
1388: nuke->svflags |= SVF_DAMAGEABLE;
1389: nuke->dmg = NUKE_DAMAGE * damage_modifier;
1390: if (damage_modifier == 1)
1391: nuke->dmg_radius = NUKE_RADIUS;
1392: else
1393: nuke->dmg_radius = NUKE_RADIUS + NUKE_RADIUS*(0.25*(float)damage_modifier);
1394: // this yields 1.0, 1.5, 2.0, 3.0 times radius
1395:
1396: if ((g_showlogic) && (g_showlogic->value))
1397: gi.dprintf ("nuke modifier = %d, damage = %d, radius = %f\n", damage_modifier, nuke->dmg, nuke->dmg_radius);
1398:
1399: nuke->classname = "nuke";
1400: nuke->die = nuke_die;
1401:
1402: gi.linkentity (nuke);
1403: }
1404: #endif
1405:
1406: // *************************
1407: // TESLA
1408: // *************************
1409:
1410: #ifdef INCLUDE_TESLA
1411: #define TESLA_TIME_TO_LIVE 30
1412: #define TESLA_DAMAGE_RADIUS 128
1413: #define TESLA_DAMAGE 3 // 3
1414: #define TESLA_KNOCKBACK 8
1415:
1416: #define TESLA_ACTIVATE_TIME 3
1417:
1418: #define TESLA_EXPLOSION_DAMAGE_MULT 50 // this is the amount the damage is multiplied by for underwater explosions
1419: #define TESLA_EXPLOSION_RADIUS 200
1420:
1421: void tesla_remove (edict_t *self)
1422: {
1423: edict_t *cur, *next;
1424:
1425: self->takedamage = DAMAGE_NO;
1426: if(self->teamchain)
1427: {
1428: cur = self->teamchain;
1429: while(cur)
1430: {
1431: next = cur->teamchain;
1432: G_FreeEdict ( cur );
1433: cur = next;
1434: }
1435: }
1436: else if (self->air_finished)
1437: gi.dprintf ("tesla without a field!\n");
1438:
1439: self->owner = self->teammaster; // Going away, set the owner correctly.
1440: // PGM - grenade explode does damage to self->enemy
1441: self->enemy = NULL;
1442:
1443: // play quad sound if quadded and an underwater explosion
1444: if ((self->dmg_radius) && (self->dmg > (TESLA_DAMAGE*TESLA_EXPLOSION_DAMAGE_MULT)))
1445: gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1446:
1447: Grenade_Explode(self);
1448: }
1449:
1450: void tesla_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1451: {
1452: // gi.dprintf("tesla killed\n");
1453: tesla_remove(self);
1454: }
1455:
1456: void tesla_blow (edict_t *self)
1457: {
1458: // T_RadiusDamage(self, self->owner, TESLA_EXPLOSION_DAMAGE, NULL, TESLA_EXPLOSION_RADIUS, MOD_TESLA);
1459: self->dmg = self->dmg * TESLA_EXPLOSION_DAMAGE_MULT;
1460: self->dmg_radius = TESLA_EXPLOSION_RADIUS;
1461: tesla_remove(self);
1462: }
1463:
1464:
1465: void tesla_zap (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1466: {
1467: }
1468:
1469: void tesla_think_active (edict_t *self)
1470: {
1471: int i,num;
1472: edict_t *touch[MAX_EDICTS], *hit;
1473: vec3_t dir, start;
1474: trace_t tr;
1475:
1476: if(level.time > self->air_finished)
1477: {
1478: tesla_remove(self);
1479: return;
1480: }
1481:
1482: VectorCopy(self->s.origin, start);
1483: start[2] += 16;
1484:
1485: num = gi.BoxEdicts(self->teamchain->absmin, self->teamchain->absmax, touch, MAX_EDICTS, AREA_SOLID);
1486: for(i=0;i<num;i++)
1487: {
1488: hit=touch[i];
1489: if(!hit->inuse)
1490: continue;
1491: if(hit == self)
1492: continue;
1493: if(hit->health < 1)
1494: continue;
1495: // don't hit clients in single-player or coop
1496: if(hit->client)
1497: if (coop->value || !deathmatch->value)
1498: continue;
1499: if(!(hit->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)) && !hit->client)
1500: continue;
1501:
1502: tr = gi.trace(start, vec3_origin, vec3_origin, hit->s.origin, self, MASK_SHOT);
1503: if(tr.fraction==1 || tr.ent==hit)// || tr.ent->client || (tr.ent->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)))
1504: {
1505: VectorSubtract(hit->s.origin, start, dir);
1506:
1507: // PMM - play quad sound if it's above the "normal" damage
1508: if (self->dmg > TESLA_DAMAGE)
1509: gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1510:
1511: // PGM - don't do knockback to walking monsters
1512: if((hit->svflags & SVF_MONSTER) && !(hit->flags & (FL_FLY|FL_SWIM)))
1513: T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
1514: self->dmg, 0, 0, MOD_TESLA);
1515: else
1516: T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal,
1517: self->dmg, TESLA_KNOCKBACK, 0, MOD_TESLA);
1518:
1519: gi.WriteByte (svc_temp_entity);
1520: gi.WriteByte (TE_LIGHTNING);
1521: gi.WriteShort (hit - g_edicts); // destination entity
1522: gi.WriteShort (self - g_edicts); // source entity
1523: gi.WritePosition (tr.endpos);
1524: gi.WritePosition (start);
1525: gi.multicast (start, MULTICAST_PVS);
1526: }
1527: }
1528:
1529: if(self->inuse)
1530: {
1531: self->think = tesla_think_active;
1532: self->nextthink = level.time + FRAMETIME;
1533: }
1534: }
1535:
1536: void tesla_activate (edict_t *self)
1537: {
1538: edict_t *trigger;
1539: edict_t *search;
1540:
1541: if (gi.pointcontents (self->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER))
1542: {
1543: tesla_blow (self);
1544: return;
1545: }
1546:
1547: // only check for spawn points in deathmatch
1548: if (deathmatch->value)
1549: {
1550: search = NULL;
1551: while ((search = findradius(search, self->s.origin, 1.5*TESLA_DAMAGE_RADIUS)) != NULL)
1552: {
1553: //if (!search->takedamage)
1554: // continue;
1555: // if it's a monster or player with health > 0
1556: // or it's a deathmatch start point
1557: // and we can see it
1558: // blow up
1559: if(search->classname)
1560: {
1561: if ( ( (!strcmp(search->classname, "info_player_deathmatch"))
1562: || (!strcmp(search->classname, "info_player_start"))
1563: || (!strcmp(search->classname, "info_player_coop"))
1564: || (!strcmp(search->classname, "misc_teleporter_dest"))
1565: )
1566: && (visible (search, self))
1567: )
1568: {
1569: if ((g_showlogic) && (g_showlogic->value))
1570: gi.dprintf ("Tesla to close to %s, removing!\n", search->classname);
1571: tesla_remove (self);
1572: return;
1573: }
1574: }
1575: }
1576: }
1577:
1578: trigger = G_Spawn();
1579: // if (trigger->nextthink)
1580: // {
1581: // if ((g_showlogic) && (g_showlogic->value))
1582: // gi.dprintf ("tesla_activate: fixing nextthink\n");
1583: // trigger->nextthink = 0;
1584: // }
1585: VectorCopy (self->s.origin, trigger->s.origin);
1586: VectorSet (trigger->mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, self->mins[2]);
1587: VectorSet (trigger->maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
1588: trigger->movetype = MOVETYPE_NONE;
1589: trigger->solid = SOLID_TRIGGER;
1590: trigger->owner = self;
1591: trigger->touch = tesla_zap;
1592: trigger->classname = "tesla trigger";
1593: // doesn't need to be marked as a teamslave since the move code for bounce looks for teamchains
1594: gi.linkentity (trigger);
1595:
1596: VectorClear (self->s.angles);
1597: // clear the owner if in deathmatch
1598: if (deathmatch->value)
1599: self->owner = NULL;
1600: self->teamchain = trigger;
1601: self->think = tesla_think_active;
1602: self->nextthink = level.time + FRAMETIME;
1603: self->air_finished = level.time + TESLA_TIME_TO_LIVE;
1604: }
1605:
1606: void tesla_think (edict_t *ent)
1607: {
1608: if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA))
1609: {
1610: tesla_remove (ent);
1611: return;
1612: }
1613: VectorClear (ent->s.angles);
1614: ent->s.frame++;
1615: if(ent->s.frame > 14)
1616: {
1617: ent->s.frame = 14;
1618: ent->think = tesla_activate;
1619: ent->nextthink = level.time + 0.1;
1620: }
1621: else
1622: {
1623: if(ent->s.frame > 9)
1624: {
1625: if(ent->s.frame == 10)
1626: {
1627: if (ent->owner && ent->owner->client)
1628: {
1629: PlayerNoise(ent->owner, ent->s.origin, PNOISE_WEAPON); // PGM
1630: }
1631: ent->s.skinnum = 1;
1632: }
1633: else if(ent->s.frame == 12)
1634: ent->s.skinnum = 2;
1635: else if(ent->s.frame == 14)
1636: ent->s.skinnum = 3;
1637: }
1638: ent->think = tesla_think;
1639: ent->nextthink = level.time + 0.1;
1640: }
1641: }
1642:
1643: void tesla_lava (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1644: {
1645: vec3_t land_point;
1646:
1647: if (plane->normal)
1648: {
1649: VectorMA (ent->s.origin, -20.0, plane->normal, land_point);
1650: if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA))
1651: {
1652: tesla_blow (ent);
1653: return;
1654: }
1655: }
1656: if (random() > 0.5)
1657: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
1658: else
1659: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
1660: }
1661:
1662: void fire_tesla (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed)
1663: {
1664: edict_t *tesla;
1665: vec3_t dir;
1666: vec3_t forward, right, up;
1667:
1668: vectoangles2 (aimdir, dir);
1669: AngleVectors (dir, forward, right, up);
1670:
1671: tesla = G_Spawn();
1672: VectorCopy (start, tesla->s.origin);
1673: VectorScale (aimdir, speed, tesla->velocity);
1674: VectorMA (tesla->velocity, 200 + crandom() * 10.0, up, tesla->velocity);
1675: VectorMA (tesla->velocity, crandom() * 10.0, right, tesla->velocity);
1676: // VectorCopy (dir, tesla->s.angles);
1677: VectorClear (tesla->s.angles);
1678: tesla->movetype = MOVETYPE_BOUNCE;
1679: tesla->solid = SOLID_BBOX;
1680: tesla->s.effects |= EF_GRENADE;
1681: tesla->s.renderfx |= RF_IR_VISIBLE;
1682: // VectorClear (tesla->mins);
1683: // VectorClear (tesla->maxs);
1684: VectorSet (tesla->mins, -12, -12, 0);
1685: VectorSet (tesla->maxs, 12, 12, 20);
1686: tesla->s.modelindex = gi.modelindex ("models/weapons/g_tesla/tris.md2");
1687:
1688: tesla->owner = self; // PGM - we don't want it owned by self YET.
1689: tesla->teammaster = self;
1690:
1691: tesla->wait = level.time + TESLA_TIME_TO_LIVE;
1692: tesla->think = tesla_think;
1693: tesla->nextthink = level.time + TESLA_ACTIVATE_TIME;
1694:
1695: // blow up on contact with lava & slime code
1696: tesla->touch = tesla_lava;
1697:
1698: if(deathmatch->value)
1699: // PMM - lowered from 50 - 7/29/1998
1700: tesla->health = 20;
1701: else
1702: tesla->health = 30; // FIXME - change depending on skill?
1703:
1704: tesla->takedamage = DAMAGE_YES;
1705: tesla->die = tesla_die;
1706: tesla->dmg = TESLA_DAMAGE*damage_multiplier;
1707: // tesla->dmg = 0;
1708: tesla->classname = "tesla";
1709: tesla->svflags |= SVF_DAMAGEABLE;
1710: tesla->clipmask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
1711: tesla->flags |= FL_MECHANICAL;
1712:
1713: gi.linkentity (tesla);
1714: }
1715: #endif
1716:
1717: // *************************
1718: // HEATBEAM
1719: // *************************
1720:
1721: #ifdef INCLUDE_BEAMS
1722: static void fire_beams (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, int te_beam, int te_impact, int mod)
1723: {
1724: trace_t tr;
1725: vec3_t dir;
1726: vec3_t forward, right, up;
1727: vec3_t end;
1728: vec3_t water_start, endpoint;
1729: qboolean water = false, underwater = false;
1730: int content_mask = MASK_SHOT | MASK_WATER;
1731: vec3_t beam_endpt;
1732:
1733: // tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
1734: // if (!(tr.fraction < 1.0))
1735: // {
1736: vectoangles2 (aimdir, dir);
1737: AngleVectors (dir, forward, right, up);
1738:
1739: VectorMA (start, 8192, forward, end);
1740:
1741: if (gi.pointcontents (start) & MASK_WATER)
1742: {
1743: // gi.dprintf ("Heat beam under water\n");
1744: underwater = true;
1745: VectorCopy (start, water_start);
1746: content_mask &= ~MASK_WATER;
1747: }
1748:
1749: tr = gi.trace (start, NULL, NULL, end, self, content_mask);
1750:
1751: // see if we hit water
1752: if (tr.contents & MASK_WATER)
1753: {
1754: water = true;
1755: VectorCopy (tr.endpos, water_start);
1756:
1757: if (!VectorCompare (start, tr.endpos))
1758: {
1759: gi.WriteByte (svc_temp_entity);
1760: gi.WriteByte (TE_HEATBEAM_SPARKS);
1761: // gi.WriteByte (50);
1762: gi.WritePosition (water_start);
1763: gi.WriteDir (tr.plane.normal);
1764: // gi.WriteByte (8);
1765: // gi.WriteShort (60);
1766: gi.multicast (tr.endpos, MULTICAST_PVS);
1767: }
1768: // re-trace ignoring water this time
1769: tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
1770: }
1771: VectorCopy (tr.endpos, endpoint);
1772: // }
1773:
1774: // halve the damage if target underwater
1775: if (water)
1776: {
1777: damage = damage /2;
1778: }
1779:
1780: // send gun puff / flash
1781: if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
1782: {
1783: if (tr.fraction < 1.0)
1784: {
1785: if (tr.ent->takedamage)
1786: {
1787: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_ENERGY, mod);
1788: }
1789: else
1790: {
1791: if ((!water) && (strncmp (tr.surface->name, "sky", 3)))
1792: {
1793: // This is the truncated steam entry - uses 1+1+2 extra bytes of data
1794: gi.WriteByte (svc_temp_entity);
1795: gi.WriteByte (TE_HEATBEAM_STEAM);
1796: // gi.WriteByte (20);
1797: gi.WritePosition (tr.endpos);
1798: gi.WriteDir (tr.plane.normal);
1799: // gi.WriteByte (0xe0);
1800: // gi.WriteShort (60);
1801: gi.multicast (tr.endpos, MULTICAST_PVS);
1802:
1803: if (self->client)
1804: PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
1805: }
1806: }
1807: }
1808: }
1809:
1810: // if went through water, determine where the end and make a bubble trail
1811: if ((water) || (underwater))
1812: {
1813: vec3_t pos;
1814:
1815: VectorSubtract (tr.endpos, water_start, dir);
1816: VectorNormalize (dir);
1817: VectorMA (tr.endpos, -2, dir, pos);
1818: if (gi.pointcontents (pos) & MASK_WATER)
1819: VectorCopy (pos, tr.endpos);
1820: else
1821: tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
1822:
1823: VectorAdd (water_start, tr.endpos, pos);
1824: VectorScale (pos, 0.5, pos);
1825:
1826: gi.WriteByte (svc_temp_entity);
1827: gi.WriteByte (TE_BUBBLETRAIL2);
1828: // gi.WriteByte (8);
1829: gi.WritePosition (water_start);
1830: gi.WritePosition (tr.endpos);
1831: gi.multicast (pos, MULTICAST_PVS);
1832: }
1833:
1834: if ((!underwater) && (!water))
1835: {
1836: VectorCopy (tr.endpos, beam_endpt);
1837: }
1838: else
1839: {
1840: VectorCopy (endpoint, beam_endpt);
1841: }
1842:
1843: gi.WriteByte (svc_temp_entity);
1844: gi.WriteByte (te_beam);
1845: gi.WriteShort (self - g_edicts);
1846: gi.WritePosition (start);
1847: gi.WritePosition (beam_endpt);
1848: gi.multicast (self->s.origin, MULTICAST_ALL);
1849:
1850: }
1851:
1852:
1853: /*
1854: =================
1855: fire_heat
1856:
1857: Fires a single heat beam. Zap.
1858: =================
1859: */
1860: void fire_heat (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, qboolean monster)
1861: {
1862: if (monster)
1863: fire_beams (self, start, aimdir, offset, damage, kick, TE_MONSTER_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
1864: else
1865: fire_beams (self, start, aimdir, offset, damage, kick, TE_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM);
1866: }
1867:
1868: #endif
1869:
1870:
1871: // *************************
1872: // BLASTER 2
1873: // *************************
1874:
1875: /*
1876: =================
1877: fire_blaster2
1878:
1879: Fires a single green blaster bolt. Used by monsters, generally.
1880: =================
1881: */
1882: void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1883: {
1884: int mod;
1885: int damagestat;
1886:
1887: if (other == self->owner)
1888: return;
1889:
1890: if (surf && (surf->flags & SURF_SKY))
1891: {
1892: G_FreeEdict (self);
1893: return;
1894: }
1895:
1896: if (self->owner && self->owner->client)
1897: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
1898:
1899: if (other->takedamage)
1900: {
1901: // the only time players will be firing blaster2 bolts will be from the
1902: // defender sphere.
1903: if(self->owner->client)
1904: mod = MOD_DEFENDER_SPHERE;
1905: else
1906: mod = MOD_BLASTER2;
1907:
1908: if (self->owner)
1909: {
1910: damagestat = self->owner->takedamage;
1911: self->owner->takedamage = DAMAGE_NO;
1912: if (self->dmg >= 5)
1913: T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
1914: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
1915: self->owner->takedamage = damagestat;
1916: }
1917: else
1918: {
1919: if (self->dmg >= 5)
1920: T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0);
1921: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
1922: }
1923: }
1924: else
1925: {
1926: //PMM - yeowch this will get expensive
1927: if (self->dmg >= 5)
1928: T_RadiusDamage(self, self->owner, self->dmg*3, self->owner, self->dmg_radius, 0);
1929:
1930: gi.WriteByte (svc_temp_entity);
1931: gi.WriteByte (TE_BLASTER2);
1932: gi.WritePosition (self->s.origin);
1933: if (!plane)
1934: gi.WriteDir (vec3_origin);
1935: else
1936: gi.WriteDir (plane->normal);
1937: gi.multicast (self->s.origin, MULTICAST_PVS);
1938: }
1939:
1940: G_FreeEdict (self);
1941: }
1942:
1943: void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
1944: {
1945: edict_t *bolt;
1946: trace_t tr;
1947:
1948: VectorNormalize (dir);
1949:
1950: bolt = G_Spawn();
1951: VectorCopy (start, bolt->s.origin);
1952: VectorCopy (start, bolt->s.old_origin);
1953: vectoangles2 (dir, bolt->s.angles);
1954: VectorScale (dir, speed, bolt->velocity);
1955: bolt->movetype = MOVETYPE_FLYMISSILE;
1956: bolt->clipmask = MASK_SHOT;
1957: bolt->solid = SOLID_BBOX;
1958: bolt->s.effects |= effect;
1959: VectorClear (bolt->mins);
1960: VectorClear (bolt->maxs);
1961:
1962: if (effect)
1963: bolt->s.effects |= EF_TRACKER;
1964: bolt->dmg_radius = 128;
1965: bolt->s.modelindex = gi.modelindex ("models/proj/laser2/tris.md2");
1966: bolt->touch = blaster2_touch;
1967:
1968: bolt->owner = self;
1969: bolt->nextthink = level.time + 2;
1970: bolt->think = G_FreeEdict;
1971: bolt->dmg = damage;
1972: bolt->classname = "bolt";
1973: gi.linkentity (bolt);
1974:
1975: if (self->client)
1976: check_dodge (self, bolt->s.origin, dir, speed);
1977:
1978: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
1979: if (tr.fraction < 1.0)
1980: {
1981: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
1982: bolt->touch (bolt, tr.ent, NULL, NULL);
1983: }
1984: }
1985:
1986: // *************************
1987: // tracker
1988: // *************************
1989:
1990: /*
1991: void tracker_boom_think (edict_t *self)
1992: {
1993: self->s.frame--;
1994: if(self->s.frame < 0)
1995: G_FreeEdict(self);
1996: else
1997: self->nextthink = level.time + 0.1;
1998: }
1999:
2000: void tracker_boom_spawn (vec3_t origin)
2001: {
2002: edict_t *boom;
2003:
2004: boom = G_Spawn();
2005: VectorCopy (origin, boom->s.origin);
2006: boom->s.modelindex = gi.modelindex ("models/items/spawngro/tris.md2");
2007: boom->s.skinnum = 1;
2008: boom->s.frame = 2;
2009: boom->classname = "tracker boom";
2010: gi.linkentity (boom);
2011:
2012: boom->think = tracker_boom_think;
2013: boom->nextthink = level.time + 0.1;
2014: //PMM
2015: // boom->s.renderfx |= RF_TRANSLUCENT;
2016: boom->s.effects |= EF_SPHERETRANS;
2017: //pmm
2018: }
2019: */
2020:
2021: #define TRACKER_DAMAGE_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY | DAMAGE_NO_KNOCKBACK)
2022: #define TRACKER_IMPACT_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY)
2023:
2024: #define TRACKER_DAMAGE_TIME 0.5 // seconds
2025:
2026: void tracker_pain_daemon_think (edict_t *self)
2027: {
2028: static vec3_t pain_normal = { 0, 0, 1 };
2029: int hurt;
2030:
2031: if(!self->inuse)
2032: return;
2033:
2034: if((level.time - self->timestamp) > TRACKER_DAMAGE_TIME)
2035: {
2036: if(!self->enemy->client)
2037: self->enemy->s.effects &= ~EF_TRACKERTRAIL;
2038: G_FreeEdict (self);
2039: }
2040: else
2041: {
2042: if(self->enemy->health > 0)
2043: {
2044: // gi.dprintf("ouch %x\n", self);
2045: T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, pain_normal,
2046: self->dmg, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
2047:
2048: // if we kill the player, we'll be removed.
2049: if(self->inuse)
2050: {
2051: // if we killed a monster, gib them.
2052: if (self->enemy->health < 1)
2053: {
2054: if(self->enemy->gib_health)
2055: hurt = - self->enemy->gib_health;
2056: else
2057: hurt = 500;
2058:
2059: // gi.dprintf("non-player killed. ensuring gib! %d\n", hurt);
2060: T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin,
2061: pain_normal, hurt, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER);
2062: }
2063:
2064: if(self->enemy->client)
2065: self->enemy->client->tracker_pain_framenum = level.framenum + 1;
2066: else
2067: self->enemy->s.effects |= EF_TRACKERTRAIL;
2068:
2069: self->nextthink = level.time + FRAMETIME;
2070: }
2071: }
2072: else
2073: {
2074: if(!self->enemy->client)
2075: self->enemy->s.effects &= ~EF_TRACKERTRAIL;
2076: G_FreeEdict (self);
2077: }
2078: }
2079: }
2080:
2081: void tracker_pain_daemon_spawn (edict_t *owner, edict_t *enemy, int damage)
2082: {
2083: edict_t *daemon;
2084:
2085: if(enemy == NULL)
2086: return;
2087:
2088: daemon = G_Spawn();
2089: daemon->classname = "pain daemon";
2090: daemon->think = tracker_pain_daemon_think;
2091: daemon->nextthink = level.time + FRAMETIME;
2092: daemon->timestamp = level.time;
2093: daemon->owner = owner;
2094: daemon->enemy = enemy;
2095: daemon->dmg = damage;
2096: }
2097:
2098: void tracker_explode (edict_t *self, cplane_t *plane)
2099: {
2100: vec3_t dir;
2101:
2102: if(!plane)
2103: VectorClear (dir);
2104: else
2105: VectorScale (plane->normal, 256, dir);
2106:
2107: gi.WriteByte (svc_temp_entity);
2108: gi.WriteByte (TE_TRACKER_EXPLOSION);
2109: gi.WritePosition (self->s.origin);
2110: gi.multicast (self->s.origin, MULTICAST_PVS);
2111:
2112: // gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/disrupthit.wav"), 1, ATTN_NORM, 0);
2113: // tracker_boom_spawn(self->s.origin);
2114:
2115: G_FreeEdict (self);
2116: }
2117:
2118: void tracker_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
2119: {
2120: float damagetime;
2121:
2122: if (other == self->owner)
2123: return;
2124:
2125: if (surf && (surf->flags & SURF_SKY))
2126: {
2127: G_FreeEdict (self);
2128: return;
2129: }
2130:
2131: if (self->client)
2132: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
2133:
2134: if (other->takedamage)
2135: {
2136: if((other->svflags & SVF_MONSTER) || other->client)
2137: {
2138: if(other->health > 0) // knockback only for living creatures
2139: {
2140: // PMM - kickback was times 4 .. reduced to 3
2141: // now this does no damage, just knockback
2142: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2143: /* self->dmg */ 0, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2144:
2145: if (!(other->flags & (FL_FLY|FL_SWIM)))
2146: other->velocity[2] += 140;
2147:
2148: damagetime = ((float)self->dmg)*FRAMETIME;
2149: damagetime = damagetime / TRACKER_DAMAGE_TIME;
2150: // gi.dprintf ("damage is %f\n", damagetime);
2151:
2152: tracker_pain_daemon_spawn (self->owner, other, (int)damagetime);
2153: }
2154: else // lots of damage (almost autogib) for dead bodies
2155: {
2156: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2157: self->dmg*4, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2158: }
2159: }
2160: else // full damage in one shot for inanimate objects
2161: {
2162: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
2163: self->dmg, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER);
2164: }
2165: }
2166:
2167: tracker_explode (self, plane);
2168: return;
2169: }
2170:
2171: void tracker_fly (edict_t *self)
2172: {
2173: vec3_t dest;
2174: vec3_t dir;
2175: vec3_t center;
2176:
2177: if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->health < 1))
2178: {
2179: tracker_explode (self, NULL);
2180: return;
2181: }
2182: /*
2183: VectorCopy (self->enemy->s.origin, dest);
2184: if(self->enemy->client)
2185: dest[2] += self->enemy->viewheight;
2186: */
2187: // PMM - try to hunt for center of enemy, if possible and not client
2188: if(self->enemy->client)
2189: {
2190: VectorCopy (self->enemy->s.origin, dest);
2191: dest[2] += self->enemy->viewheight;
2192: }
2193: // paranoia
2194: else if (VectorCompare(self->enemy->absmin, vec3_origin) || VectorCompare(self->enemy->absmax, vec3_origin))
2195: {
2196: VectorCopy (self->enemy->s.origin, dest);
2197: }
2198: else
2199: {
2200: VectorMA (vec3_origin, 0.5, self->enemy->absmin, center);
2201: VectorMA (center, 0.5, self->enemy->absmax, center);
2202: VectorCopy (center, dest);
2203: }
2204:
2205: VectorSubtract (dest, self->s.origin, dir);
2206: VectorNormalize (dir);
2207: vectoangles2 (dir, self->s.angles);
2208: VectorScale (dir, self->speed, self->velocity);
2209: VectorCopy(dest, self->monsterinfo.saved_goal);
2210:
2211: self->nextthink = level.time + 0.1;
2212: }
2213:
2214: void fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy)
2215: {
2216: edict_t *bolt;
2217: trace_t tr;
2218:
2219: VectorNormalize (dir);
2220:
2221: bolt = G_Spawn();
2222: VectorCopy (start, bolt->s.origin);
2223: VectorCopy (start, bolt->s.old_origin);
2224: vectoangles2 (dir, bolt->s.angles);
2225: VectorScale (dir, speed, bolt->velocity);
2226: bolt->movetype = MOVETYPE_FLYMISSILE;
2227: bolt->clipmask = MASK_SHOT;
2228: bolt->solid = SOLID_BBOX;
2229: bolt->speed = speed;
2230: bolt->s.effects = EF_TRACKER;
2231: bolt->s.sound = gi.soundindex ("weapons/disrupt.wav");
2232: VectorClear (bolt->mins);
2233: VectorClear (bolt->maxs);
2234:
2235: bolt->s.modelindex = gi.modelindex ("models/proj/disintegrator/tris.md2");
2236: bolt->touch = tracker_touch;
2237: bolt->enemy = enemy;
2238: bolt->owner = self;
2239: bolt->dmg = damage;
2240: bolt->classname = "tracker";
2241: gi.linkentity (bolt);
2242:
2243: if(enemy)
2244: {
2245: bolt->nextthink = level.time + 0.1;
2246: bolt->think = tracker_fly;
2247: }
2248: else
2249: {
2250: bolt->nextthink = level.time + 10;
2251: bolt->think = G_FreeEdict;
2252: }
2253:
2254: if (self->client)
2255: check_dodge (self, bolt->s.origin, dir, speed);
2256:
2257: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
2258: if (tr.fraction < 1.0)
2259: {
2260: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
2261: bolt->touch (bolt, tr.ent, NULL, NULL);
2262: }
2263: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.