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