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