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