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