/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include "g_local.h"

// bullet spreads
//#define DEFAULT_SHOTGUN_HSPREAD	1000
//#define DEFAULT_SHOTGUN_VSPREAD	500

/*
//
//=================
//check_dodge
//
//This is a support routine used when a client is firing
//a non-instant attack weapon. It checks to see if a
//monster's dodge function should be called.
//=================
//
static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed)
{
	vec3_t	end;
	vec3_t	v;
	trace_t	tr;
	float	eta;

	// easy mode only ducks one quarter the time
	if (skill->integer == 0)
	{
		if (random() > 0.25)
			return;
	}
	VectorMA (start, 8192, dir, end);
	trap_Trace (&tr, start, NULL, NULL, end, self, MASK_SHOT);
	if ((tr.fraction < 1) && (game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (game.edicts[tr.ent].health > 0) && (game.edicts[tr.ent].monsterinfo.dodge) && infront(&game.edicts[tr.ent], self))
	{
		VectorSubtract (tr.endpos, start, v);
		eta = (VectorLength(v) - game.edicts[tr.ent].r.maxs[0]) / speed;
		game.edicts[tr.ent].monsterinfo.dodge (&game.edicts[tr.ent], self, eta);
	}
}


//
//=================
//fire_hit
//
//Used for all impact (hit/punch/slash) attacks
//=================
//
qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick)
{
	trace_t		tr;
	vec3_t		forward, right, up;
	vec3_t		v;
	vec3_t		point;
	float		range;
	vec3_t		dir;

	//see if enemy is in range
	VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
	range = VectorLength(dir);
	if (range > aim[0])
		return qfalse;

	if (aim[1] > self->r.mins[0] && aim[1] < self->r.maxs[0])
	{
		// the hit is straight on so back the range up to the edge of their bbox
		range -= self->enemy->r.maxs[0];
	}
	else
	{
		// this is a side hit so adjust the "right" value out to the edge of their bbox
		if (aim[1] < 0)
			aim[1] = self->enemy->r.mins[0];
		else
			aim[1] = self->enemy->r.maxs[0];
	}

	VectorMA (self->s.origin, range, dir, point);

	trap_Trace (&tr, self->s.origin, NULL, NULL, point, self, MASK_SHOT);
	if (tr.fraction < 1)
	{
		if (!game.edicts[tr.ent].takedamage)
			return qfalse;
		// if it will hit any client/monster then hit the one we wanted to hit
		if ((game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client))
			tr.ent = self->enemy - game.edicts;
	}

	AngleVectors(self->s.angles, forward, right, up);
	VectorMA (self->s.origin, range, forward, point);
	VectorMA (point, aim[1], right, point);
	VectorMA (point, aim[2], up, point);
	VectorSubtract (point, self->enemy->s.origin, dir);

	// do the damage
	T_Damage (&game.edicts[tr.ent], self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);

	if (!(game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (!game.edicts[tr.ent].r.client))
		return qfalse;

	// do our special form of knockback here
	VectorMA (self->enemy->r.absmin, 0.5, self->enemy->r.size, v);
	VectorSubtract (v, point, v);
	VectorNormalize (v);
	VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity);
	if (self->enemy->velocity[2] > 0)
		self->enemy->groundentity = NULL;
	return qtrue;
}


//
//=================
//fire_lead
//
//This is an internal support routine used for bullet/pellet based weapons.
//Must match CG_fireLead in cg_events.c
//=================
//
static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int mod)
{
	trace_t		tr;
	vec3_t		dir;
	vec3_t		end;
	float		r;
	float		u;
	vec3_t		water_start;
	int			content_mask = MASK_SHOT | MASK_WATER;

	 trap_Trace (&tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT);
	if (!(tr.fraction < 1.0))
	{
		r = Q_crandom (seed) * hspread;
		u = Q_crandom (seed) * vspread;
		VectorMA (start, 8192, axis[0], end);
		VectorMA (end, r, axis[1], end);
		VectorMA (end, u, axis[2], end);

		if (trap_PointContents (start) & MASK_WATER)
		{
			VectorCopy (start, water_start);
			content_mask &= ~MASK_WATER;
		}

		trap_Trace (&tr, start, NULL, NULL, end, self, content_mask);

		// see if we hit water
		if (tr.contents & MASK_WATER)
		{
			VectorCopy (tr.endpos, water_start);

			if (!VectorCompare (start, tr.endpos))
			{
				vec3_t forward, right, up;

				// change bullet's course when it enters water
				VectorSubtract (end, start, dir);
				VecToAngles (dir, dir);
				AngleVectors (dir, forward, right, up);
				r = Q_crandom (seed) * hspread * 2;
				u = Q_crandom (seed) * vspread * 2;
				VectorMA (water_start, 8192, forward, end);
				VectorMA (end, r, right, end);
				VectorMA (end, u, up, end);
			}

			// re-trace ignoring water this time
			trap_Trace (&tr, water_start, NULL, NULL, end, self, MASK_SHOT);
		}
	}

	// send gun puff / flash
	if (tr.fraction < 1.0)
	{
		if (game.edicts[tr.ent].takedamage)
		{
			T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
		}
		else
		{
			if ( !(tr.surfFlags & SURF_NOIMPACT) )
			{
				if (self->r.client)
					PlayerNoise (self, tr.endpos, PNOISE_IMPACT);
			}
		}
	}
}


//
//=================
//fire_bullet
//
//Fires a single round.  Used for machinegun and chaingun.  Would be fine for
//pistols, rifles, etc....
//=================
//
void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int mod)
{
	vec3_t	dir, axis[3];
	edict_t *event;
	int		seed = rand() & 255;

	VecToAngles ( aimdir, dir );
	AngleVectors ( dir, axis[0], axis[1], axis[2] );

	// send the event
	event = G_SpawnEvent ( EV_FIRE_BULLET, seed, start );
	event->s.ownerNum = self - game.edicts;
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorScale ( axis[0], 1024, event->s.origin2 );	// DirToByte is too inaccurate

	fire_lead ( self, start, aimdir, axis, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, &seed, mod );
}


//
//=================
//fire_shotgun
//
//Shoots shotgun pellets.  Used by shotgun and super shotgun.
//=================
//
void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int count, int mod)
{
	vec3_t	dir, axis[3];
	edict_t *event;
	int		i, seed = rand() & 255;

	VecToAngles ( aimdir, dir );
	AngleVectors ( dir, axis[0], axis[1], axis[2] );

	// send the event
	event = G_SpawnEvent ( EV_FIRE_SHOTGUN, seed, start );
	event->s.eventCount = count;
	event->s.ownerNum = self - game.edicts;
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorScale ( axis[0], 4096, event->s.origin2 );	// DirToByte is too inaccurate

	for ( i = 0; i < count; i++ ) {
		fire_lead ( self, start, aimdir, axis, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, &seed, mod );
	}
}


//
//=================
//fire_blaster
//
//Fires a single blaster bolt.  Used by the blaster and hyper blaster.
//=================
//
void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, int surfFlags)
{
	if (other == self->r.owner)
		return;
	if (surfFlags & SURF_NOIMPACT)
	{
		G_FreeEdict (self);
		return;
	}

	if (self->r.owner->r.client)
		PlayerNoise(self->r.owner, self->s.origin, PNOISE_IMPACT);

	if (other->takedamage)
	{
		T_Damage (other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, self->style);
	}
	else
	{
		// turn entity into event
		G_TurnEntityIntoEvent ( self, EV_BLASTER, DirToByte (plane ? plane->normal : NULL) );
		return;
	}

	G_FreeEdict (self);
}

void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int type, int mod)
{
	edict_t	*bolt;
	trace_t	tr;

	VectorNormalize (dir);

	bolt = G_Spawn();
	bolt->r.svflags = SVF_PROJECTILE;
	// when prediction is used against the object
	// (blaster/hyperblaster shots), the player won't be solid clipped against
	// the object. Right now trying to run into a firing hyperblaster
	// is very jerky since you are predicted 'against' the shots.
	VectorCopy (start, bolt->s.origin);
	VectorCopy (start, bolt->s.old_origin);
	VecToAngles (dir, bolt->s.angles);
	VectorScale (dir, speed, bolt->velocity);
	bolt->movetype = MOVETYPE_FLYMISSILE;
	bolt->r.clipmask = MASK_SHOT;
	bolt->r.solid = SOLID_BBOX;
	bolt->s.type = type;
	bolt->s.renderfx |= RF_NOSHADOW;
	VectorClear (bolt->r.mins);
	VectorClear (bolt->r.maxs);
	bolt->s.modelindex = trap_ModelIndex ("models/objects/laser/tris.md2");
	bolt->s.sound = trap_SoundIndex ("sounds/misc/lasfly.wav");
	bolt->r.owner = self;
	bolt->touch = blaster_touch;
	bolt->nextthink = level.time + 2;
	bolt->think = G_FreeEdict;
	bolt->dmg = damage;
	bolt->classname = "bolt";
	bolt->style = mod;
	trap_LinkEntity (bolt);

	//newgametypes (this was for q2 monsters, who cares about them)

	trap_Trace (&tr, self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
	if (tr.fraction < 1.0)
	{
		VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
		bolt->touch (bolt, &game.edicts[tr.ent], &tr.plane, 0);
	}
}	
*/

/*
//=================
//fire_grenade
//=================
static void Grenade_Explode (edict_t *ent)
{
	int			mod;

	if (ent->r.owner->r.client)
		PlayerNoise(ent->r.owner, ent->s.origin, PNOISE_IMPACT);

	//FIXME: if we are onground then raise our Z just a bit since we are a point?
	if (ent->enemy)
	{
		float	points;
		vec3_t	v;
		vec3_t	dir;

		VectorAdd (ent->enemy->r.mins, ent->enemy->r.maxs, v);
		VectorMA (ent->enemy->s.origin, 0.5, v, v);
		VectorSubtract (ent->s.origin, v, v);
		points = ent->dmg - 0.5 * VectorLength (v);
		VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
		if (ent->spawnflags & 1)
			mod = MOD_HANDGRENADE;
		else
			mod = MOD_GRENADE;
		T_Damage (ent->enemy, ent, ent->r.owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
	}

	if (ent->spawnflags & 2)
		mod = MOD_HELD_GRENADE;
	else if (ent->spawnflags & 1)
		mod = MOD_HG_SPLASH;
	else
		mod = MOD_G_SPLASH;
	T_RadiusDamage(ent, ent->r.owner, NULL, ent->dmg, ent->radius_dmg, ent->enemy, ent->dmg_radius, mod);

	// turn entity into event
	VectorMA (ent->s.origin, -0.02, ent->velocity, ent->s.origin);
	G_TurnEntityIntoEvent ( ent, ent->groundentity ? EV_GRENADE_EXPLOSION : EV_ROCKET_EXPLOSION, DirToByte (ent->movedir) );
}

static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags)
{
	if (other == ent->r.owner)
		return;
	if (surfFlags & SURF_NOIMPACT)
	{
		G_FreeEdict (ent);
		return;
	}

	if (!other->takedamage)
	{
		G_AddEvent (ent, EV_GRENADE_BOUNCE, 0, qtrue);
		return;
	}

	ent->enemy = other;
	Grenade_Explode (ent);
}

static void LaserGrenade_SetupLaser (edict_t *ent)
{
	edict_t	*owner;
	edict_t *laser;

	if( !(owner = ent->r.owner) ) {
		ent->nextthink = level.time + FRAMETIME;
		ent->think = G_FreeEdict;
		return;
	}

	laser = G_Spawn();
	laser->s.sound = trap_SoundIndex ("world/laser.wav");
	laser->classname = "grenade_laser";
	laser->enemy = NULL;
	laser->target = NULL;
  	laser->r.owner = ent;
	laser->activator = owner;
  	laser->dmg = 25;
	laser->delay = 10;
	laser->count = MOD_LASER_TRAP;
	laser->spawnflags = 1;

//newgametypes[start] JALFIXME: just hacked it in being always color red. The color should match the team color
	//if( !ctf->integer || !owner->r.client || owner->r.client->resp.ctf_team == CTF_TEAM1 )
		laser->spawnflags |= 2;		// red
	//else		
		//laser->spawnflags |= 8;		// blue
//newgametypes[end]

	VectorCopy( ent->s.origin, laser->s.origin );
	VectorCopy( ent->s.angles, laser->s.angles );

	target_laser_start (laser);
}

static void LaserGrenade_Use (edict_t *self, edict_t *other, edict_t *activator)
{
	self->enemy = NULL;
	Grenade_Explode (self);
}

static void LaserGrenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags)
{
	if (other == ent->r.owner)
		return;
	if (surfFlags & SURF_NOIMPACT)
	{
		G_FreeEdict (ent);
		return;
	}

	if (other != world)
	{
		if (other->takedamage)
			ent->enemy = other;
		Grenade_Explode (ent);
		return;
	}

	ent->use = LaserGrenade_Use;
	ent->touch = NULL;
	ent->movetype = MOVETYPE_NONE;
	ent->s.sound = 0;
	G_AddEvent (ent, EV_GRENADE_BOUNCE, 0, qtrue);

	ent->nextthink = level.time + 2;
	ent->think = LaserGrenade_SetupLaser;

	VectorClear (ent->velocity);
	VectorClear (ent->avelocity);

	VecToAngles (plane->normal, ent->s.angles);
	VectorCopy (plane->normal, ent->movedir);

	trap_LinkEntity (ent);
}

void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
{
	edict_t	*grenade;
	vec3_t	dir;
	vec3_t	forward, right, up;

	VecToAngles (aimdir, dir);
	AngleVectors (dir, forward, right, up);

	grenade = G_Spawn();
	VectorCopy (start, grenade->s.origin);
	VectorScale (aimdir, speed, grenade->velocity);
	grenade->velocity[2] += 200 * up[2] + crandom() * 10.0;
	VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
	VectorSet (grenade->avelocity, 300, 300, 300);
	VectorSet (grenade->movedir, 0, 0, 1);
	grenade->movetype = MOVETYPE_BOUNCEGRENADE;
	grenade->r.clipmask = MASK_SHOT;
	grenade->r.solid = SOLID_BBOX;
	grenade->s.type = ET_GRENADE;
	grenade->s.renderfx |= RF_NOSHADOW;
	VectorClear (grenade->r.mins);
	VectorClear (grenade->r.maxs);
	grenade->s.modelindex = trap_ModelIndex ("models/ammo/grenade1.md3");
	grenade->r.owner = self;
	grenade->touch = Grenade_Touch;
	grenade->nextthink = level.time + timer;
	grenade->think = Grenade_Explode;
	grenade->use = NULL;
	grenade->dmg = damage;
	grenade->dmg_radius = damage_radius;
	grenade->classname = "grenade";

	trap_LinkEntity (grenade);
}


void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
{
	edict_t	*grenade;
	vec3_t	dir;
	vec3_t	forward, right, up;

	VecToAngles (aimdir, dir);
	AngleVectors (dir, forward, right, up);

	grenade = G_Spawn();
	VectorCopy (start, grenade->s.origin);
	VectorScale (aimdir, speed, grenade->velocity);
	VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
	VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
	VectorSet (grenade->avelocity, 300, 300, 300);
	VectorSet (grenade->movedir, 0, 0, 1);
	grenade->movetype = MOVETYPE_BOUNCE;
	grenade->r.clipmask = MASK_SHOT;
	grenade->r.solid = SOLID_BBOX;
	grenade->s.renderfx |= RF_NOSHADOW;
	VectorClear (grenade->r.mins);
	VectorClear (grenade->r.maxs);
	grenade->s.modelindex = trap_ModelIndex ("models/objects/grenade2/tris.md2");
	grenade->r.owner = self;
	grenade->touch = LaserGrenade_Touch;
	grenade->use = NULL;
	grenade->nextthink = level.time + timer;
	grenade->think = Grenade_Explode;
	grenade->dmg = damage;
	grenade->dmg_radius = damage_radius;
	grenade->classname = "hgrenade";
	if (held)
		grenade->spawnflags = 3;
	else
		grenade->spawnflags = 1;
	grenade->s.sound = trap_SoundIndex("sounds/weapons/hgrenc1b.wav");

	if (timer <= 0.0)
		Grenade_Explode (grenade);
	else
	{
		//splitmodels (handgrenade drop sound moved to cgame)
		trap_LinkEntity (grenade);
	}
}


//
//=================
//fire_rocket
//=================
//
void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags)
{
	if (other == ent->r.owner)
		return;
	if (surfFlags & SURF_NOIMPACT)
	{
		G_FreeEdict (ent);
		return;
	}

	if (ent->r.owner->r.client)
		PlayerNoise (ent->r.owner, ent->s.origin, PNOISE_IMPACT);

	if (other->takedamage)
		T_Damage (other, ent, ent->r.owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);

	T_RadiusDamage(ent, ent->r.owner, NULL, ent->dmg, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);

	// turn entity into event
	VectorMA ( ent->s.origin, -0.02, ent->velocity, ent->s.origin );
	G_TurnEntityIntoEvent ( ent, EV_ROCKET_EXPLOSION, DirToByte (plane ? plane->normal : NULL) );
}

void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
{
	edict_t	*rocket;

	rocket = G_Spawn();
	VectorCopy (start, rocket->s.origin);
	VectorCopy (dir, rocket->movedir);
	VecToAngles (dir, rocket->s.angles);
	VectorScale (dir, speed, rocket->velocity);
	rocket->movetype = MOVETYPE_FLYMISSILE;
	rocket->r.clipmask = MASK_SHOT;
	rocket->r.solid = SOLID_BBOX;
	rocket->s.type = ET_ROCKET;
	rocket->s.renderfx |= RF_NOSHADOW;
	VectorClear (rocket->r.mins);
	VectorClear (rocket->r.maxs);
	rocket->s.modelindex = trap_ModelIndex ("models/ammo/rocket/rocket.md3");
	rocket->r.owner = self;
	rocket->touch = rocket_touch;
	rocket->nextthink = level.time + 8000/speed;
	rocket->think = G_FreeEdict;
	rocket->dmg = damage;
	rocket->radius_dmg = radius_damage;
	rocket->dmg_radius = damage_radius;
	rocket->s.sound = trap_SoundIndex ("sounds/weapons/rocket/rockfly.wav");
	rocket->classname = "rocket";

	//newgametypes (this was for q2 monsters, who cares about them)

	trap_LinkEntity (rocket);
}


//
//=================
//fire_rail
//=================
//
void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
{
	vec3_t		from;
	vec3_t		end;
	trace_t		tr;
	edict_t		*ignore, *event;
	int			mask;

	VectorMA (start, 8192, aimdir, end);
	VectorCopy (start, from);
	ignore = self;
	mask = MASK_SHOT;
	while (ignore)
	{
		trap_Trace (&tr, from, NULL, NULL, end, ignore, mask);

		//ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc)
		if ((game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client) ||
			(game.edicts[tr.ent].r.solid == SOLID_BBOX))
			ignore = &game.edicts[tr.ent];
		else
			ignore = NULL;

		if ((&game.edicts[tr.ent] != self) && (game.edicts[tr.ent].takedamage))
			T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);

		VectorCopy (tr.endpos, from);
	}

	// send gun puff / flash
	event = G_SpawnEvent ( EV_RAILTRAIL, 0, start );
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorCopy ( tr.endpos, event->s.origin2 );
	event->s.ownerNum = self->s.number;//splitmodels (identify who fired for cgame)

	if (self->r.client)
		PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
}
*/

/*
//
//=================
//fire_bfg
//=================
//
void bfg_explode (edict_t *self)
{
	edict_t	*ent;
	float	points;
	vec3_t	v;
	float	dist;

	if (self->s.frame == 0)
	{
		// the BFG effect
		ent = NULL;
		while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
		{
			if (!ent->takedamage)
				continue;
			if (ent == self->r.owner)
				continue;
			if (!CanDamage (ent, self))
				continue;
			if (!CanDamage (ent, self->r.owner))
				continue;

			VectorAdd (ent->r.mins, ent->r.maxs, v);
			VectorMA (ent->s.origin, 0.5, v, v);
			VectorSubtract (self->s.origin, v, v);
			dist = VectorLength(v);
			points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
			if (ent == self->r.owner)
				points = points * 0.5;

			G_SpawnEvent ( EV_BFG_EXPLOSION, 0, ent->s.origin );
			T_Damage (ent, self, self->r.owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
		}
	}

	self->nextthink = level.time + FRAMETIME;
	self->count++;
	if (self->count == 5)
		self->think = G_FreeEdict;
}

void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, int surfFlags)
{
	if (other == self->r.owner)
		return;
	if (surfFlags & SURF_NOIMPACT)
	{
		G_FreeEdict (self);
		return;
	}

	if (self->r.owner->r.client)
		PlayerNoise(self->r.owner, self->s.origin, PNOISE_IMPACT);

	// core explosion - prevents firing it into the wall/floor
	if (other->takedamage)
		T_Damage (other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
	T_RadiusDamage(self, self->r.owner, NULL, 200, 0, other, 100, MOD_BFG_BLAST);

	G_Sound (self, CHAN_VOICE, trap_SoundIndex ("sounds/weapons/bfg__x1b.wav"), 1, ATTN_NORM);
	self->r.solid = SOLID_NOT;
	self->touch = NULL;
	self->r.svflags = SVF_NOCLIENT;
	VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
	VectorClear (self->velocity);
	self->think = bfg_explode;
	self->nextthink = level.time + FRAMETIME;
	self->enemy = other;
	self->count = 0;
	trap_LinkEntity (self);

	G_SpawnEvent ( EV_BFG_BIGEXPLOSION, 0, self->s.origin );
}


void bfg_think (edict_t *self)
{
	edict_t	*ent;
	edict_t	*ignore;
	edict_t *event;
	vec3_t	point;
	vec3_t	dir;
	vec3_t	start;
	vec3_t	end;
	int		dmg;
	trace_t	tr;

	dmg = 5;

	ent = NULL;
	while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
	{
		if (ent == self)
			continue;
		if (ent == self->r.owner)
			continue;
		if (!ent->takedamage)
			continue;
		if (!(ent->r.svflags & SVF_MONSTER) && !(ent->r.client) && (strcmp(ent->classname, "misc_explobox") != 0))
			continue;

//ZOID
//newgametypes
//ZOID

		VectorMA (ent->r.absmin, 0.5, ent->r.size, point);

		VectorSubtract (point, self->s.origin, dir);
		VectorNormalize (dir);

		ignore = self;
		VectorCopy (self->s.origin, start);
		VectorMA (start, 2048, dir, end);
		while(1)
		{
			trap_Trace (&tr, start, NULL, NULL, end, ignore, MASK_SHOT);

			if (!tr.ent)
				break;

			// hurt it if we can
			if ((game.edicts[tr.ent].takedamage) && !(game.edicts[tr.ent].flags & FL_IMMUNE_LASER) && (&game.edicts[tr.ent] != self->r.owner))
				T_Damage (&game.edicts[tr.ent], self, self->r.owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);

			// if we hit something that's not a monster or player we're done
			if (!(game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (!game.edicts[tr.ent].r.client))
			{
				edict_t *event;

				event = G_SpawnEvent ( EV_LASER_SPARKS, DirToByte (tr.plane.normal), tr.endpos );
				event->s.eventCount = 4;
				event->s.skinnum = self->s.skinnum;
				break;
			}

			ignore = &game.edicts[tr.ent];
			VectorCopy (tr.endpos, start);
		}

		event = G_SpawnEvent ( EV_BFG_LASER, 0, self->s.origin );
		event->r.svflags = SVF_FORCEOLDORIGIN;
		VectorCopy ( tr.endpos, event->s.origin2 );
	}

	self->nextthink = level.time + FRAMETIME;
}


void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
{
	edict_t	*bfg;

	bfg = G_Spawn();
	VectorCopy (start, bfg->s.origin);
	VectorCopy (dir, bfg->movedir);
	VectorClear (bfg->s.angles);
	VectorScale (dir, speed, bfg->velocity);
	bfg->movetype = MOVETYPE_FLYMISSILE;
	bfg->r.clipmask = MASK_SHOT;
	bfg->r.solid = SOLID_BBOX;
	bfg->s.type = ET_BFG;
	VectorClear (bfg->r.mins);
	VectorClear (bfg->r.maxs);
	bfg->s.modelindex = trap_ModelIndex ("sprites/s_bfg1.sp2");
	bfg->r.owner = self;
	bfg->touch = bfg_touch;
	bfg->nextthink = level.time + 8000/speed;
	bfg->think = G_FreeEdict;
	bfg->radius_dmg = damage;
	bfg->dmg_radius = damage_radius;
	bfg->classname = "bfg blast";
	bfg->s.sound = trap_SoundIndex ("sounds/weapons/bfg__l1a.wav");

	bfg->think = bfg_think;
	bfg->nextthink = level.time + FRAMETIME;
	bfg->teammaster = bfg;
	bfg->teamchain = NULL;

	//newgametypes (this was for q2 monsters, who cares about them)

	trap_LinkEntity (bfg);
}
*/

/*
// kept for GB
static void W_Fire_Lead_OLD( edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int dflags, int mod )
{
	trace_t		tr;
	//vec3_t		dir;
	vec3_t		end;
	float		r;
	float		u;
	vec3_t		water_start;
	int			content_mask = MASK_SHOT | MASK_WATER;

	trap_Trace( &tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT );
	if( !(tr.fraction < 1.0) )
	{
		r = Q_crandom(seed) * hspread;
		u = Q_crandom(seed) * vspread;
		VectorMA( start, 8192, axis[0], end );
		VectorMA( end, r, axis[1], end );
		VectorMA( end, u, axis[2], end );

		if( trap_PointContents (start) & MASK_WATER )
		{
			VectorCopy( start, water_start );
			content_mask &= ~MASK_WATER;
		}

		trap_Trace( &tr, start, NULL, NULL, end, self, content_mask );

		// see if we hit water
		//if( tr.contents & MASK_WATER )
		//{
		//VectorCopy( tr.endpos, water_start );

		//if( !VectorCompare(start, tr.endpos) )
		//{
		//vec3_t forward, right, up;

		// change bullet's course when it enters water
		//VectorSubtract( end, start, dir );
		//VecToAngles( dir, dir );
		//AngleVectors (dir, forward, right, up);
		//r = Q_crandom(seed) * hspread * 2;
		//u = Q_crandom(seed) * vspread * 2;
		//VectorMA( water_start, 8192, forward, end );
		//VectorMA( end, r, right, end );
		//VectorMA( end, u, up, end );
		//}

		// re-trace ignoring water this time
		//trap_Trace( &tr, water_start, NULL, NULL, end, self, MASK_SHOT );
		//}
	}

	// send gun puff / flash
	if( tr.fraction < 1.0 )
	{
		if( game.edicts[tr.ent].takedamage )
		{
			T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, dflags, mod );
		}
		else
		{
			if( !(tr.surfFlags & SURF_NOIMPACT) )
			{
			}
		}
	}
}
*/

//==================
//W_Fire_Lead
//the seed is important to be as pointer for cgame prediction accuracy
//==================
static void W_Fire_Lead( edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int dflags, int mod )
{
	trace_t		tr;
	vec3_t		dir;
	vec3_t		end;
	float		r;
	float		u;
	vec3_t		water_start;
	int			content_mask = MASK_SHOT | MASK_WATER;

	trap_Trace (&tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT);
	if (!(tr.fraction < 1.0))
	{
#if 1
		// circle
		double alpha=M_PI*Q_crandom(seed); // [-PI ..+PI]
		double s=fabs(Q_crandom(seed)); // [0..1]
		r= s*cos(alpha)*hspread;
		u= s*sin(alpha)*vspread;
#else
		// square
		r = Q_crandom (seed) * hspread;
		u = Q_crandom (seed) * vspread;
#endif
		VectorMA (start, 8192, axis[0], end);
		VectorMA (end, r, axis[1], end);
		VectorMA (end, u, axis[2], end);

		if (trap_PointContents (start) & MASK_WATER)
		{
			VectorCopy (start, water_start);
			content_mask &= ~MASK_WATER;
		}

		trap_Trace (&tr, start, NULL, NULL, end, self, content_mask);

		// see if we hit water
		if (tr.contents & MASK_WATER)
		{
			VectorCopy (tr.endpos, water_start);

			if (!VectorCompare (start, tr.endpos))
			{
				vec3_t forward, right, up;

				// change bullet's course when it enters water
				VectorSubtract (end, start, dir);
				VecToAngles (dir, dir);
				AngleVectors (dir, forward, right, up);
				r = Q_crandom (seed) * hspread * 2;
				u = Q_crandom (seed) * vspread * 2;
				VectorMA (water_start, 8192, forward, end);
				VectorMA (end, r, right, end);
				VectorMA (end, u, up, end);
			}

			// re-trace ignoring water this time
			trap_Trace (&tr, water_start, NULL, NULL, end, self, MASK_SHOT);
		}
	}

	// send gun puff / flash
	if (tr.fraction < 1.0)
	{
		if (game.edicts[tr.ent].takedamage)
		{
			T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, dflags, mod);
		}
		else
		{
			if ( !(tr.surfFlags & SURF_NOIMPACT) )
			{
			}
		}
	}
}

//==================
//W_Touch_Projectile - Generic projectile touch func. Only for replacement in tests
//==================
void W_Touch_Projectile( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	vec3_t dir;
	int		radius;

	//don't hurt onwner for the 1st second?
	if( other == ent->r.owner ) {
		if( !g_projectile_touch_owner->integer ||
			(g_projectile_touch_owner->integer && ent->timestamp + 1.0 > level.time) )
			return;
	}

	if( surfFlags & SURF_NOIMPACT )
	{
		G_FreeEdict( ent );
		return;
	}

	if( other->takedamage )
	{
		VectorSubtract( other->s.origin, ent->s.origin, dir );
		VectorNormalize( dir );
		T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg, 0, MOD_EXPLOSIVE );
	}
	T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_EXPLOSIVE );
	
	// turn entity into event
	radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
	VectorMA( ent->s.origin, -0.02, ent->velocity, ent->s.origin );
	G_TurnEntityIntoEvent( ent, EV_EXPLOSION1, DirToByte (plane ? plane->normal : NULL) );
	ent->s.firemode = FIRE_MODE_STRONG;
	ent->s.weapon = radius;
}

//==================
//W_Prestep
//==================
#define PRESTEP_BY_TIME
static void W_Prestep( edict_t *ent, edict_t *ignore )
{
	trace_t	tr;
	vec3_t	dest;
#ifndef PRESTEP_BY_TIME
	vec3_t	dir;
	VectorCopy( ent->velocity, dir );
	VectorNormalize( dir );
	VectorMA( ent->s.origin, g_projectile_prestep->value, dir, dest );
#else
	VectorMA( ent->s.origin, g_projectile_prestep->value * 0.001, ent->velocity, dest );
#endif
	trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ignore, MASK_SHOT );

	VectorCopy( tr.endpos, ent->s.origin );
	VectorCopy( tr.endpos, ent->s.old_origin );

	if( tr.allsolid || tr.startsolid ) {
		if( ent->touch )
			ent->touch( ent, &game.edicts[tr.ent], NULL, 0 );
	} else if( tr.fraction != 1.0 ) {
		if( ent->touch )
			ent->touch( ent, &game.edicts[tr.ent], &tr.plane, tr.surfFlags );
	}
}

//==================
//W_Fire_Projectile - Spawn a generic projectile without a model, touch func, sound nor mod
//==================
edict_t *W_Fire_Projectile( edict_t *self, vec3_t start, vec3_t dir, int speed,
					   int damage, int knockback, int radius_damage, int radius, int timeout )
{
	edict_t	*projectile;

	VectorNormalize( dir );

	projectile = G_Spawn();
	VectorCopy( start, projectile->s.origin );
	VectorCopy( start, projectile->s.old_origin );
	VecToAngles( dir, projectile->s.angles );
	VectorScale( dir, speed, projectile->velocity );
	projectile->movetype = MOVETYPE_FLYMISSILE;

	// wsw: make missile fly through players in race
	if(game.gametype==GAMETYPE_RACE)
		projectile->r.clipmask = MASK_SOLID;
	else
		projectile->r.clipmask = MASK_SHOT;

	projectile->r.solid = SOLID_BBOX;
	projectile->s.renderfx = RF_NOSHADOW;
	projectile->r.svflags = SVF_PROJECTILE;
	VectorClear( projectile->r.mins );
	VectorClear( projectile->r.maxs );
	//projectile->s.modelindex = trap_ModelIndex ("models/objects/projectile/plasmagun/proj_plasmagun2.md3");
	projectile->s.modelindex = 0;
	projectile->r.owner = self;
	projectile->touch = W_Touch_Projectile; //generic one. Should be replaced after calling this func
	projectile->nextthink = level.timemsec + timeout;
	projectile->think = G_FreeEdict;
	projectile->dmg = damage;
	projectile->dmg_knockback = knockback;
	projectile->radius_dmg = radius_damage;
	projectile->dmg_radius = radius;
	projectile->classname = "projectile"; //generic one. Should be replaced after calling this func.
	projectile->style = 0;
	projectile->s.sound = 0;
	projectile->timestamp = level.time;

	trap_LinkEntity( projectile );

	return projectile;
}


//	------------ the actual weapons --------------

//==================
//W_Touch_Generic
// Generic projectile touch function, that should be called once before projectile is removed
// Returns false if normal function shouldn't continue
//==================
static qboolean W_Touch_Generic( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	// don't hurt owner for the first second
	if( other == ent->r.owner ) {
		if( !g_projectile_touch_owner->integer ||
			(g_projectile_touch_owner->integer && ent->timestamp + 1.0 > level.time) )
			return qfalse;
	}

	if( other->takedamage && !G_IsTeamDamage(ent->r.owner, other)
		&& other != ent->r.owner && G_ModToAmmo(ent->style) != AMMO_NONE ) {
		ent->r.owner->r.client->resp.accuracy_hits_direct[G_ModToAmmo(ent->style)-AMMO_CELLS]++;
	}

	return qtrue;
}

//==================
//W_Fire_Blade
//==================
void W_Fire_Blade( edict_t *self, int range, vec3_t start, vec3_t aimdir, int damage, int knockback, int mod)
{
	edict_t *event, *other = NULL;
	vec3_t	end;
	trace_t	trace;

	VectorMA( start, range, aimdir, end );
	trap_Trace( &trace, start, NULL, NULL, end, self, MASK_SHOT );
	if( trace.fraction == 1.0 ) //didn't touch anything
		return;

	// find out what touched
	other = &game.edicts[trace.ent];
	if( !other->takedamage ) // it was the world
	{
		// wall impact
		VectorMA( trace.endpos, -0.02, aimdir, end );
		event = G_SpawnEvent( EV_BLADE_IMPACT, 0, end );
		event->s.ownerNum = self - game.edicts;
		VectorCopy( trace.plane.normal, event->s.origin2);
		event->r.svflags = SVF_FORCEOLDORIGIN;
		return;
	}

	// it was a player
	T_Damage( other, self, self, aimdir, other->s.origin, vec3_origin, damage, knockback, 0, mod );
}

//==================
//W_Touch_GunbladeBlast
//==================
void W_Touch_GunbladeBlast( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	vec3_t	dir;
//	int		power;

	if( !W_Touch_Generic(ent, other, plane, surfFlags) )
		return;

	//if (surfFlags & SURF_NOIMPACT)
	//{
	//	G_FreeEdict (ent);
	//	return;
	//}

	if( other->takedamage )
	{
		float pushFrac;
		pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius );
		T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style );
	}
	T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_GUNBLADE_S ); //fixme : splash mod
	
	// add explosion event
	if( !(surfFlags & SURF_NOIMPACT) && !other->takedamage )
	{
		edict_t *event;

		//power = (int)(128.0f * ((float)ent->dmg / (float)g_weaponInfos[WEAP_GUNBLADE].firedef->damage) );
		//if( power > 128 ) power = 128;
		
		event = G_SpawnEvent( EV_GUNBLADEBLAST_IMPACT, DirToByte(plane ? plane->normal : NULL), ent->s.origin );
		event->s.weapon = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
		event->s.skinnum = ((ent->dmg_knockback*1/8) > 255) ? 255 : (ent->dmg_knockback*1/8);
	}

	// free at next frame
	ent->think = G_FreeEdict;
	ent->touch = NULL;
	ent->nextthink = level.timemsec + game.framemsec;
}

//==================
//W_Fire_GunbladeBlast
//==================
void W_Fire_GunbladeBlast( edict_t *self, vec3_t start, vec3_t dir, int damage, int knockback, int radius_damage, int radius, int speed, int timeout, int mod )
{
	edict_t	*blast;

	blast = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout );
	blast->s.modelindex = trap_ModelIndex( PATH_GUNBLADEBLAST_STRONG_MODEL );
	blast->s.type = ET_BLASTER;
	blast->touch = W_Touch_GunbladeBlast;
	blast->classname = "Gunblade blast";
	blast->s.renderfx |= RF_FULLBRIGHT;
	blast->style = mod;

	blast->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY );

	W_Prestep( blast, self );
}

//==================
//W_Fire_Gunblade_Bullet
//==================
void W_Fire_Gunblade_Bullet( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int mod)
{
	vec3_t	dir, axis[3];
	edict_t *event;
	int		seed = rand() & 255;

	VecToAngles( aimdir, dir );
	AngleVectors( dir, axis[0], axis[1], axis[2] );

	// send the event
	event = G_SpawnEvent( EV_FIRE_BULLET, seed, start );
	event->s.ownerNum = self - game.edicts;
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorScale( axis[0], 1024, event->s.origin2 );	// DirToByte is too inaccurate

	W_Fire_Lead( self, start, aimdir, axis, damage, knockback, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, &seed, DAMAGE_BULLET, mod );
}

//==================
//W_Touch_Shockwave
//==================
void W_Touch_Shockwave( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags  )
{
	G_FreeEdict(ent);
}

//==================
//W_Think_Shockwave
//==================
void W_Think_Shockwave( edict_t *wave )
{
	edict_t *ent;

	if( wave->timestamp + wave->timeout < level.time )
	{
		G_FreeEdict(wave);
		return;
	}

	wave->nextthink = level.timemsec + G_FRAMETIME;
	wave->think = W_Think_Shockwave;

	ent = NULL;
	while( (ent = G_FindBoxInRadius(ent, wave->s.origin, wave->dmg_radius)) != NULL )
	{
		//don't hurt owner for the one second
		if( ent == wave->r.owner ) {
			if( !g_projectile_touch_owner->integer ||
				(g_projectile_touch_owner->integer && wave->timestamp + 1.0 > level.time) )
				continue;
		}

		if( !ent->takedamage || !ent->r.client )
			continue;

		ent->r.client->ps.pmove.stats[PM_STAT_SLOW] = G_FRAMETIME;
	}
}

//==================
//W_Fire_Shockwave
//==================
void W_Fire_Shockwave( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int radius, int timeout )
{
	edict_t	*wave;

	wave = W_Fire_Projectile( self, start, aimdir, (int)speed, 0, 0, 0, radius, 0 );

	wave->timeout = timeout/1000.0;
	wave->accel = -750.0;
	wave->r.clipmask = MASK_SOLID;
	wave->nextthink = level.timemsec + G_FRAMETIME;
	wave->think = W_Think_Shockwave;

	wave->s.modelindex = trap_ModelIndex( PATH_ROCKET_EXPLOSION_MODEL );
	wave->touch = W_Touch_Shockwave;
	wave->classname = "wave";
	wave->s.renderfx |= RF_FULLBRIGHT;
	wave->s.type = ET_SHOCKWAVE;
	//wave->style = mod;
	
	W_Prestep( wave, self );
}


void W_Fire_Riotgun2( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int hspread, int vspread, int count, int dflags, int mod )
{
	vec3_t	dir, axis[3];
	edict_t *event;
	int		i, seed = rand() & 255;

	VecToAngles( aimdir, dir );
	AngleVectors( dir, axis[0], axis[1], axis[2] );
	
	// send the event
	event = G_SpawnEvent( EV_FIRE_RIOTGUN, seed, start );
	event->s.eventCount = count;
	event->s.ownerNum = self - game.edicts;
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorScale( axis[0], 4096, event->s.origin2 );	// DirToByte is too inaccurate

	//send spreads inside these (the free shorts I found)
	event->s.light = hspread;
	event->s.skinnum = vspread;

	for( i = 0; i < count; i++ )
		W_Fire_Lead( self, start, aimdir, axis, damage, knockback, hspread, vspread, &seed, dflags, mod );
}

void W_Fire_Riotgun( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int hspread, int vspread, int count, int dflags, int mod )
{
	W_Fire_Riotgun2( self, start, aimdir, damage, knockback, hspread, vspread, count, dflags, mod );
}

//==================
//W_Grenade_Explode
//==================
static void W_Grenade_Explode( edict_t *ent )
{
	vec3_t		origin;
	int			radius;

	//FIXME: if we are onground then raise our Z just a bit since we are a point?
	if( ent->enemy )
	{
		vec3_t dir;
		float pushFrac;
		pushFrac = G_KnockbackPushFrac( ent->s.origin, ent->enemy->s.origin, ent->enemy->r.mins, ent->enemy->r.maxs, dir, ent->dmg_radius );
		T_Damage( ent->enemy, ent, ent->r.owner, dir, ent->s.origin, vec3_origin, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style);
	}

	T_RadiusDamage( ent,
		ent->r.owner,
		NULL,
		ent->dmg,
		ent->dmg_knockback,
		ent->radius_dmg,
		ent->enemy,
		ent->dmg_radius,
		(ent->style == MOD_GRENADE_S) ? MOD_GRENADE_SPLASH_S : MOD_GRENADE_SPLASH_W 
		);

	radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
	VectorMA( ent->s.origin, -0.02, ent->velocity, origin );
	G_TurnEntityIntoEvent( ent, EV_GRENADE_EXPLOSION, ent->groundentity ? DirToByte (ent->movedir) : 0 );
	ent->s.firemode = (ent->style == MOD_GRENADE_S) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK ;
	ent->s.weapon = radius;
}

//==================
//W_Touch_Grenade
//==================
static void W_Touch_Grenade( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	if( !W_Touch_Generic(ent, other, plane, surfFlags) )
		return;

	if( surfFlags & SURF_NOIMPACT )
	{
		G_FreeEdict(ent);
		return;
	}

	if( !other->takedamage )
	{	
#if 1
			if( ent->style == MOD_GRENADE_S )
				G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_STRONG, qtrue );
			else
				G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_WEAK, qtrue );
#else
		if( plane->normal[2] < 0.7 || abs(ent->velocity[2]) > 50 ) {
			VectorScale( ent->velocity, 1.2, ent->velocity );
			if( ent->style == MOD_GRENADE_S )
				G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_STRONG, qtrue );
			else
				G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_WEAK, qtrue );
		}
#endif

		return;
	}

	ent->enemy = other;
	W_Grenade_Explode(ent);
}

//==================
//W_Fire_Grenade
//==================
void W_Fire_Grenade( edict_t *self, vec3_t start, vec3_t dir, int speed, int damage, int knockback, int radius_damage, float radius, int timeout, int mod )
{
	edict_t	*grenade;
	vec3_t	up, angles;
	cvar_t *g_grenade_gravity = trap_Cvar_Get( "g_grenade_gravity", "1.3", CVAR_ARCHIVE );
	//cvar_t *g_grenade_relative = trap_Cvar_Get( "g_grenade_relative", "1.0", CVAR_ARCHIVE );

	grenade = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout );
	VecToAngles( grenade->velocity, angles );
	AngleVectors( angles, NULL, NULL, up );
	//VectorMA( grenade->velocity, 200, up, grenade->velocity );
	//VectorMA( grenade->velocity, g_grenade_relative->value, self->velocity, grenade->velocity );
	VectorSet( grenade->avelocity, 300, 300, 300 );
	VectorSet( grenade->movedir, 0, 0, 1 );
	grenade->style = mod;
	grenade->s.type = ET_GRENADE;
	grenade->movetype = MOVETYPE_BOUNCEGRENADE;
	grenade->s.renderfx |= RF_FULLBRIGHT;
	grenade->touch = W_Touch_Grenade;
	grenade->use = NULL;
	grenade->think = W_Grenade_Explode;
	grenade->classname = "grenade";
	grenade->gravity = g_grenade_gravity->value;

	if( mod == MOD_GRENADE_S )	grenade->s.modelindex = trap_ModelIndex( PATH_GRENADE_STRONG_MODEL );
	else						grenade->s.modelindex = trap_ModelIndex( PATH_GRENADE_WEAK_MODEL );

	//grenade->s.sound = trap_SoundIndex ( S_WEAPON_GRENADE_FLY );

	W_Prestep( grenade, self );

	trap_LinkEntity( grenade );
}

//==================
//W_Touch_Rocket
//==================
void W_Touch_Rocket( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	int mod_splash, fire_mode;
	vec3_t dir;
	float dmg_radius;

	if( !W_Touch_Generic(ent, other, plane, surfFlags) )
		return;

	if( ent->style == MOD_ROCKET_S )
	{
		fire_mode = FIRE_MODE_STRONG;
		mod_splash = MOD_ROCKET_SPLASH_S;
	}
	else
	{
		fire_mode = FIRE_MODE_WEAK;
		mod_splash = MOD_ROCKET_SPLASH_W;
	}

	dmg_radius=ent->dmg_radius;

	// wsw: pb midair hack
	if(game.gametype == GAMETYPE_MIDAIR)
	{
		dmg_radius*=2.0f;
	}

	if( other->takedamage )
	{
		float pushFrac;
		pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, dmg_radius );
		T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style );
	}
	T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, dmg_radius, mod_splash );
	
	// spawn the explosion
	if( !(surfFlags & SURF_NOIMPACT) )
	{
		edict_t *event;
		vec3_t	explosion_origin;
		
		VectorMA( ent->s.origin, -0.02, ent->velocity, explosion_origin );
		event = G_SpawnEvent( EV_ROCKET_EXPLOSION, DirToByte (plane ? plane->normal : NULL), explosion_origin );
		event->s.firemode = fire_mode;
		event->s.weapon = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
	}

	// free the rocket at next frame
	ent->think = G_FreeEdict;
	ent->touch = NULL;
	ent->nextthink = level.timemsec + game.framemsec;
}

//==================
//W_Fire_Rocket
//==================
void W_Fire_Rocket( edict_t *self, vec3_t start, vec3_t dir, int speed, int damage, int knockback, int radius_damage, int radius, int timeout, int mod )
{
	edict_t	*rocket;

	rocket = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout );

	rocket->s.type = ET_ROCKET; //rocket trail sfx
	if ( mod == MOD_ROCKET_S )	rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_STRONG_MODEL );
	else						rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_WEAK_MODEL );
	rocket->touch = W_Touch_Rocket;
	rocket->think = G_FreeEdict;
	rocket->s.renderfx |= RF_FULLBRIGHT;
	rocket->classname = "rocket";
	rocket->style = mod;

	if ( mod == MOD_ROCKET_S )	rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_S_FLY );
	else						rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_W_FLY );

	W_Prestep( rocket, self );
}


//==================
//W_Touch_Plasma
//==================
void W_Touch_Plasma( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	int radius;

	if( !W_Touch_Generic(ent, other, plane, surfFlags) )
		return;

	if( surfFlags & SURF_NOIMPACT )
	{
		G_FreeEdict( ent );
		return;
	}

	if( ent->style == MOD_PLASMA_S ) {

		T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_PLASMA_SPLASH_S );
		if( other->takedamage ) {
			vec3_t	dir;
			float pushFrac;
			pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius );
			T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, MOD_PLASMA_S );
		} else {
			radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
			G_TurnEntityIntoEvent( ent, EV_PLASMA_EXPLOSION, DirToByte (plane ? plane->normal : NULL) );
			ent->s.firemode = FIRE_MODE_STRONG;
			ent->s.weapon = radius;
			return;
		}
	
	} else {

		T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_PLASMA_SPLASH_W );
		if( other->takedamage ) {
			vec3_t	dir;
			float pushFrac;
			pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius );
			T_Damage( other, ent, ent->r.owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback, 0, MOD_PLASMA_W );
		} else {	
			radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8);
			G_TurnEntityIntoEvent( ent, EV_PLASMA_EXPLOSION, DirToByte (plane ? plane->normal : NULL) );
			ent->s.firemode = FIRE_MODE_WEAK;
			ent->s.weapon = radius;
			return;
		}
	}

	G_FreeEdict( ent );
}

//==================
//W_Fire_Plasma
//==================
void W_Fire_Plasma( edict_t *self, vec3_t start, vec3_t dir, int damage, int knockback, int radius_damage, int radius, int speed, int timeout, int mod )
{
	edict_t	*plasma;

	plasma = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout );
	plasma->s.type = ET_PLASMA;
	plasma->touch = W_Touch_Plasma;
	plasma->classname = "plasma";
	plasma->s.renderfx |= RF_FULLBRIGHT;
	plasma->style = mod;

	if( mod == MOD_PLASMA_S ) {
		plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_STRONG_MODEL );
		plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY );
	} else {
		plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_WEAK_MODEL );
		plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_W_FLY );
	}

	W_Prestep( plasma, self );
}

//==================
//W_Touch_Bolt
//==================
void W_Touch_Bolt( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	if( !W_Touch_Generic(self, other, plane, surfFlags) )
		return;

	if( other->takedamage ) {
		T_Damage( other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, self->dmg, self->dmg_knockback, 0, MOD_ELECTROBOLT_W );
	} else if( !(surfFlags & SURF_NOIMPACT) ) // add explosion event
	{
		edict_t *event;
		event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(plane ? plane->normal : NULL), self->s.origin );
		event->s.firemode = FIRE_MODE_WEAK;
	}

	// free at next frame
	self->think = G_FreeEdict;
	self->touch = NULL;
	self->nextthink = level.timemsec + game.framemsec;
}

//==================
//W_Fire_Electrobolt_Strong
//==================
void W_Fire_Electrobolt_Strong( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int range, int dflags, int mod )
{
	vec3_t		from;
	vec3_t		end;
	trace_t		tr;
	edict_t		*ignore, *event;
	int			mask;

	VectorMA( start, range, aimdir, end );
	VectorCopy( start, from );
	ignore = self;
	mask = MASK_SHOT;
	while( ignore )
	{
		trap_Trace( &tr, from, NULL, NULL, end, ignore, mask );

		// allow trail to go through SOLID_BBOX entities (players, gibs, etc)
		if( (game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client) ||
			(game.edicts[tr.ent].r.solid == SOLID_BBOX) )
			ignore = &game.edicts[tr.ent];
		else
			ignore = NULL;

		if( (&game.edicts[tr.ent] != self) && (game.edicts[tr.ent].takedamage) ) {
			T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod );
			// spawn a impact event on each damaged ent
			event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(tr.plane.normal), self->s.origin );
			event->s.firemode = FIRE_MODE_STRONG;
		}

		VectorCopy( tr.endpos, from );
	}

	// send the weapon fire effect
	event = G_SpawnEvent( EV_ELECTROTRAIL, 0, start );
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorCopy( from, event->s.origin2 );
	event->s.ownerNum = ENTNUM(self); // identify who fired for cgame
}

//==================
//W_Fire_Electrobolt_Strong
//==================
void Old_W_Fire_Electrobolt_Strong( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int range, int dflags, int mod )
{
	vec3_t	end;
	edict_t *event;
	trace_t		tr;
	
	// hitscan
	VectorMA( start, range, aimdir, end );
	trap_Trace( &tr, start, NULL, NULL, end, self, MASK_SHOT );
	
	// send gun puff / flash
	if( tr.fraction < 1.0 )
	{
		if( game.edicts[tr.ent].takedamage )
		{
			T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod );
		}
		else
		{
			if( !(tr.surfFlags & SURF_NOIMPACT) )
			{
			}
		}
	}
	
	// send the weapon fire effect
	event = G_SpawnEvent( EV_ELECTROTRAIL, 0, start );
	event->r.svflags = SVF_FORCEOLDORIGIN;
	VectorCopy( tr.endpos, event->s.origin2 );
	event->s.ownerNum = ENTNUM(self);//identify who fired for cgame
}

//==================
//W_Fire_Electrobolt_Weak
//==================
void W_Fire_Electrobolt_Weak( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int timeout, int dflags, int mod )
{
	edict_t	*bolt;

	// projectile, weak mode
	bolt = W_Fire_Projectile( self, start, aimdir, speed, damage, knockback, 0, 0, timeout );
	bolt->s.modelindex = trap_ModelIndex( PATH_ELECTROBOLT_WEAK_MODEL );
	bolt->s.type = ET_ELECTRO_WEAK; //add particle trail and light
	bolt->touch = W_Touch_Bolt;
	bolt->classname = "bolt";
	bolt->s.renderfx |= RF_FULLBRIGHT;
	bolt->style = mod;
	
	W_Prestep( bolt, self );
}

//==================
//W_Fire_Lasergun
//==================
void W_Fire_Lasergun( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int range, int dflags, int mod )
{
	int			i, playernum;
	edict_t		*e, *laser;
	trace_t		tr;
	vec3_t		end;

	// first of all, see if we already have a beam entity for this laser
	laser = NULL;
	playernum = ENTNUM(self);
	for( i = game.maxclients; i < game.maxentities; i++ )
	{
		e = &game.edicts[i];
		
		if( !e->r.inuse )
			continue;

		if( e->s.ownerNum == playernum && e->s.type == ET_LASERBEAM )
		{
			laser = e;
			break;
		}
	}

	// if no ent was found we have to create one
	if( laser == NULL )
	{
		// we send the muzzleflash event only when new laserbeam is created
		G_AddEvent( self, EV_MUZZLEFLASH, FIRE_MODE_STRONG, qtrue );

		laser = G_Spawn();
		laser->s.type = ET_LASERBEAM;
		laser->s.ownerNum = playernum;
		laser->movetype = MOVETYPE_NONE;
		laser->r.solid = SOLID_NOT;
		laser->r.svflags = SVF_FORCEOLDORIGIN;
		laser->s.modelindex = trap_ModelIndex( PATH_LASERBEAM_STRONG_MODEL );
		laser->s.sound = trap_SoundIndex( S_WEAPON_LASERGUN_S_HUM );
	}

	VectorMA( start, range, aimdir, end );
	trap_Trace( &tr, start, NULL, NULL, end, self, MASK_SHOT );
	// send gun puff / flash
	if( tr.fraction < 1.0 )
	{
		if( game.edicts[tr.ent].takedamage ) {
			T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod );
		} else {
			if( !(tr.surfFlags & SURF_NOIMPACT) )
			{
			}
		}
	}

	// give it 2 server frames before freeing itself, so we can relink it if we are still firing
	laser->think = G_FreeEdict;
	laser->nextthink = level.timemsec + ( game.framemsec * 3 );
	trap_LinkEntity( laser );

	//copy points (warning: origin2 must be copied after linking the entity)
	VectorCopy( tr.endpos, laser->s.origin );
	VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS
	laser->s.skinnum = range;
}

void AITools_DrawLine(vec3_t origin, vec3_t dest);
void W_Fire_Lasergun_Weak( edict_t *self, vec3_t start, vec3_t end, vec3_t aimdir, int damage, int knockback, int range, int dflags, int mod )
{
	int			i, playernum;
	edict_t		*e, *laser;
	vec3_t		from;

	// first of all, see if we already have a beam entity for this laser
	laser = NULL;
	playernum = ENTNUM(self);
	for( i = game.maxclients; i < game.maxentities; i++ )
	{
		e = &game.edicts[i];

		if( !e->r.inuse )
			continue;

		if( e->s.ownerNum == playernum && e->s.type == ET_CURVELASERBEAM ) {
			laser = e;
			break;
		}
	}

	// if no ent was found we have to create one
	if( laser == NULL )
	{
		// we send the muzzleflash event only when new laserbeam is created
		G_AddEvent( self, EV_MUZZLEFLASH, FIRE_MODE_WEAK, qtrue );

		laser = G_Spawn();
		laser->s.type = ET_CURVELASERBEAM;
		laser->s.ownerNum = playernum;
		laser->movetype = MOVETYPE_NONE;
		laser->r.solid = SOLID_NOT;
		laser->r.svflags = SVF_FORCEOLDORIGIN;
		laser->s.modelindex = trap_ModelIndex( PATH_LASERBEAM_STRONG_MODEL );
		laser->s.sound = trap_SoundIndex( S_WEAPON_LASERGUN_W_HUM );
		G_AddEvent( laser, EV_TELEPORT, 0, qtrue );
	}

	// trace the beam curve
	{
		int		j;
		float	subdivisions = 5, frac;
		vec3_t	dir, impactangles, tmpangles;
		vec3_t	segmentStart, segmentEnd;
		edict_t	*segmentIgnore;

		VectorSubtract( end, start, dir );
		VecToAngles( dir, impactangles );

		segmentIgnore = self;

		VectorCopy( start, segmentStart );
		for( i = 1; i <= (int)subdivisions; i++ ) {
			frac = ( ((float)range/subdivisions)*(float)i ) / (float)range;
			for( j = 0; j < 3; j++ )tmpangles[j] = LerpAngle( self->s.angles[j], impactangles[j], frac );
			AngleVectors( tmpangles, dir, NULL, NULL );
			VectorMA( start, range*frac, dir, segmentEnd );

			//segment is ready here
			{
				trace_t		trace;
				edict_t		*ignore;
				int			mask;

				VectorCopy( segmentStart, from );
				ignore = segmentIgnore;
				mask = MASK_SHOT;
				while( ignore )
				{
					trap_Trace( &trace, from, NULL, NULL, segmentEnd, ignore, mask );

					// allow trail to go through SOLID_BBOX entities (players, gibs, etc)
					if( (game.edicts[trace.ent].r.svflags & SVF_MONSTER) || (game.edicts[trace.ent].r.client) ||
						(game.edicts[trace.ent].r.solid == SOLID_BBOX) ) {
							ignore = &game.edicts[trace.ent];
							segmentIgnore = ignore;
						}
					else
						ignore = NULL;

					if( (&game.edicts[trace.ent] != self) && (game.edicts[trace.ent].takedamage) ) {
						T_Damage( &game.edicts[trace.ent], self, self, dir, trace.endpos, trace.plane.normal, damage, knockback, dflags, mod );
						// spawn a impact event on each damaged ent
						//event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(trace.plane.normal), self->s.origin );
						//event->s.firemode = FIRE_MODE_STRONG;
					}

					VectorCopy( trace.endpos, from );
					// if found the world, stop
					if( ignore == NULL && trace.fraction < 1.0f ) {
						goto beamfinished;
					}
				}
			}

			//AITools_DrawLine( segmentStart, from );
			// copy start point for next segment
			VectorCopy ( segmentEnd, segmentStart );
		}
	}

beamfinished:
	// give it 2 server frames before freeing itself, so we can relink it if we are still firing
	laser->think = G_FreeEdict;
	laser->nextthink = level.timemsec + ( game.framemsec * 3 );
	trap_LinkEntity( laser );

	//copy points (warning: origin2 must be copied after linking the entity)
	VectorCopy( end, laser->s.origin );
	VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS
	VectorCopy( self->s.angles, laser->s.angles ); // (need player angles for curved) used in the case the owner isn't in the PVS
	laser->s.skinnum = range;

	//laser->r.svflags |= SVF_BROADCAST; // ignore PVS

	VectorCopy( from, laser->s.origin );
	VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS
	VectorCopy( self->s.angles, laser->s.angles ); // (need player angles for curved) used in the case the owner isn't in the PVS
	VectorSubtract( from, start, from );
	laser->s.skinnum = (int)VectorLengthFast( from );
}

//==============================================================================
