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