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