/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006 Pat Raynor <raynorpat@sbcglobal.net>

This file is part of the Fortress Evolution source code.

Fortress Evolution 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.

The Fortress Evolution source code 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 Fortress Evolution; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// g_weapon.c - perform the server side effects of a weapon firing
#include "g_local.h"

static float    s_quadFactor;
static vec3_t   forward, right, up;
static vec3_t   muzzle;

#define NUM_NAILSHOTS 15

/*
================
G_BounceProjectile
================
*/
void G_BounceProjectile(vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout)
{
	vec3_t          v, newv;
	float           dot;

	VectorSubtract(impact, start, v);
	dot = DotProduct(v, dir);
	VectorMA(v, -2 * dot, dir, newv);

	VectorNormalize(newv);
	VectorMA(impact, 8192, newv, endout);
}


/*
======================================================================

GAUNTLET

======================================================================
*/

void Weapon_Gauntlet(gentity_t * ent)
{

}

/*
===============
CheckGauntletAttack
===============
*/
qboolean CheckGauntletAttack(gentity_t * ent)
{
	trace_t         tr;
	vec3_t          end;
	gentity_t      *tent;
	gentity_t      *traceEnt;
	int             damage;

	// set aiming directions
	AngleVectors(ent->client->ps.viewangles, forward, right, up);

	CalcMuzzlePoint(ent, forward, right, up, muzzle);

	VectorMA(muzzle, 32, forward, end);

	trap_Trace(&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
	if(tr.surfaceFlags & SURF_NOIMPACT)
	{
		return qfalse;
	}

	traceEnt = &g_entities[tr.entityNum];

	// send blood impact
	if(traceEnt->takedamage && traceEnt->client)
	{
		tent = G_TempEntity(tr.endpos, EV_MISSILE_HIT);
		tent->s.otherEntityNum = traceEnt->s.number;
		tent->s.eventParm = DirToByte(tr.plane.normal);
		tent->s.weapon = ent->s.weapon;
	}

	if(!traceEnt->takedamage)
	{
		return qfalse;
	}

	if(ent->client->ps.powerups[PW_QUAD])
	{
		G_AddEvent(ent, EV_POWERUP_QUAD, 0);
		s_quadFactor = g_quadfactor.value;
	}
	else
	{
		s_quadFactor = 1;
	}
#ifdef MISSIONPACK
	if(ent->client->persistantPowerup && ent->client->persistantPowerup->item &&
	   ent->client->persistantPowerup->item->giTag == PW_DOUBLER)
	{
		s_quadFactor *= 2;
	}
#endif

	damage = 50 * s_quadFactor;
	G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_GAUNTLET);

	return qtrue;
}


/*
======================================================================

MACHINEGUN

======================================================================
*/

/*
======================
SnapVectorTowards

Round a vector to integers for more efficient network
transmission, but make sure that it rounds towards a given point
rather than blindly truncating.  This prevents it from truncating 
into a wall.
======================
*/
void SnapVectorTowards(vec3_t v, vec3_t to)
{
	int             i;

	for(i = 0; i < 3; i++)
	{
		if(to[i] <= v[i])
		{
			v[i] = (int)v[i];
		}
		else
		{
			v[i] = (int)v[i] + 1;
		}
	}
}

#define CHAINGUN_SPREAD			600
#define MACHINEGUN_SPREAD		200
#define	MACHINEGUN_DAMAGE		7
#define	MACHINEGUN_TEAM_DAMAGE	5	// wimpier MG in teamplay

void Bullet_Fire(gentity_t * ent, float spread, int damage)
{
	trace_t         tr;
	vec3_t          end;

#ifdef MISSIONPACK
	vec3_t          impactpoint, bouncedir;
#endif
	float           r;
	float           u;
	gentity_t      *tent;
	gentity_t      *traceEnt;
	int             i, passent;

	damage *= s_quadFactor;

	r = random() * M_PI * 2.0f;
	u = sin(r) * crandom() * spread * 16;
	r = cos(r) * crandom() * spread * 16;
	VectorMA(muzzle, 8192 * 16, forward, end);
	VectorMA(end, r, right, end);
	VectorMA(end, u, up, end);

	passent = ent->s.number;
	for(i = 0; i < 10; i++)
	{

		trap_Trace(&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
		if(tr.surfaceFlags & SURF_NOIMPACT)
		{
			return;
		}

		traceEnt = &g_entities[tr.entityNum];

		// snap the endpos to integers, but nudged towards the line
		SnapVectorTowards(tr.endpos, muzzle);

		// send bullet impact
		if(traceEnt->takedamage && traceEnt->client)
		{
			tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_FLESH);
			tent->s.eventParm = traceEnt->s.number;
			if(LogAccuracyHit(traceEnt, ent))
			{
				ent->client->accuracy_hits++;
			}
		}
		else
		{
			tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_WALL);
			tent->s.eventParm = DirToByte(tr.plane.normal);
		}
		tent->s.otherEntityNum = ent->s.number;

		if(traceEnt->takedamage)
		{
#ifdef MISSIONPACK
			if(traceEnt->client && traceEnt->client->invulnerabilityTime > level.time)
			{
				if(G_InvulnerabilityEffect(traceEnt, forward, tr.endpos, impactpoint, bouncedir))
				{
					G_BounceProjectile(muzzle, impactpoint, bouncedir, end);
					VectorCopy(impactpoint, muzzle);
					// the player can hit him/herself with the bounced rail
					passent = ENTITYNUM_NONE;
				}
				else
				{
					VectorCopy(tr.endpos, muzzle);
					passent = traceEnt->s.number;
				}
				continue;
			}
			else
			{
#endif
				G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_MACHINEGUN);
#ifdef MISSIONPACK
			}
#endif
		}
		break;
	}
}


/*
======================================================================

BFG

======================================================================
*/

void BFG_Fire(gentity_t * ent)
{
	gentity_t      *m;

	m = fire_bfg(ent, muzzle, forward);
	m->damage *= s_quadFactor;
	m->splashDamage *= s_quadFactor;

//  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
}


/*
======================================================================

SHOTGUN

======================================================================
*/

// DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because
// client predicts same spreads
#define	DEFAULT_SHOTGUN_DAMAGE	10

qboolean ShotgunPellet(vec3_t start, vec3_t end, gentity_t * ent)
{
	trace_t         tr;
	int             damage, i, passent;
	gentity_t      *traceEnt;

#ifdef MISSIONPACK
	vec3_t          impactpoint, bouncedir;
#endif
	vec3_t          tr_start, tr_end;

	passent = ent->s.number;
	VectorCopy(start, tr_start);
	VectorCopy(end, tr_end);
	for(i = 0; i < 10; i++)
	{
		trap_Trace(&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
		traceEnt = &g_entities[tr.entityNum];

		// send bullet impact
		if(tr.surfaceFlags & SURF_NOIMPACT)
		{
			return qfalse;
		}

		if(traceEnt->takedamage)
		{
			damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
#ifdef MISSIONPACK
			if(traceEnt->client && traceEnt->client->invulnerabilityTime > level.time)
			{
				if(G_InvulnerabilityEffect(traceEnt, forward, tr.endpos, impactpoint, bouncedir))
				{
					G_BounceProjectile(tr_start, impactpoint, bouncedir, tr_end);
					VectorCopy(impactpoint, tr_start);
					// the player can hit him/herself with the bounced rail
					passent = ENTITYNUM_NONE;
				}
				else
				{
					VectorCopy(tr.endpos, tr_start);
					passent = traceEnt->s.number;
				}
				continue;
			}
			else
			{
				G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
				if(LogAccuracyHit(traceEnt, ent))
				{
					return qtrue;
				}
			}
#else
			G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
			if(LogAccuracyHit(traceEnt, ent))
			{
				return qtrue;
			}
#endif
		}
		return qfalse;
	}
	return qfalse;
}

// this should match CG_ShotgunPattern
void ShotgunPattern(vec3_t origin, vec3_t origin2, int seed, gentity_t * ent)
{
	int             i;
	float           r, u;
	vec3_t          end;
	vec3_t          forward, right, up;
	int             oldScore;
	qboolean        hitClient = qfalse;

	// derive the right and up vectors from the forward vector, because
	// the client won't have any other information
	VectorNormalize2(origin2, forward);
	PerpendicularVector(right, forward);
	CrossProduct(forward, right, up);

	oldScore = ent->client->ps.persistant[PERS_SCORE];

	// generate the "random" spread pattern
	for(i = 0; i < DEFAULT_SHOTGUN_COUNT; i++)
	{
		r = Q_crandom(&seed) * DEFAULT_SHOTGUN_SPREAD * 16;
		u = Q_crandom(&seed) * DEFAULT_SHOTGUN_SPREAD * 16;
		VectorMA(origin, 8192 * 16, forward, end);
		VectorMA(end, r, right, end);
		VectorMA(end, u, up, end);
		if(ShotgunPellet(origin, end, ent) && !hitClient)
		{
			hitClient = qtrue;
			ent->client->accuracy_hits++;
		}
	}
}


void weapon_supershotgun_fire(gentity_t * ent)
{
	gentity_t      *tent;

	// send shotgun blast
	tent = G_TempEntity(muzzle, EV_SHOTGUN);
	VectorScale(forward, 4096, tent->s.origin2);
	SnapVector(tent->s.origin2);
	tent->s.eventParm = rand() & 255;	// seed for spread pattern
	tent->s.otherEntityNum = ent->s.number;

	ShotgunPattern(tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent);
}


/*
======================================================================

GRENADE LAUNCHER

======================================================================
*/

void weapon_grenadelauncher_fire(gentity_t * ent)
{
	gentity_t      *m;

	// extra vertical velocity
	forward[2] += 0.2f;
	VectorNormalize(forward);

	m = fire_grenade(ent, muzzle, forward);
	m->damage *= s_quadFactor;
	m->splashDamage *= s_quadFactor;

//  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
}

/*
======================================================================

ROCKET

======================================================================
*/

void Weapon_RocketLauncher_Fire(gentity_t * ent)
{
	gentity_t      *m;

	m = fire_rocket(ent, muzzle, forward);
	m->damage *= s_quadFactor;
	m->splashDamage *= s_quadFactor;

//  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
}


/*
======================================================================

PLASMA GUN

======================================================================
*/

void Weapon_Plasmagun_Fire(gentity_t * ent)
{
	gentity_t      *m;

	m = fire_plasma(ent, muzzle, forward);
	m->damage *= s_quadFactor;
	m->splashDamage *= s_quadFactor;

//  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
}

/*
======================================================================

RAILGUN

======================================================================
*/


/*
=================
weapon_railgun_fire
=================
*/
#define	MAX_RAIL_HITS	4
void weapon_railgun_fire(gentity_t * ent)
{
	vec3_t          end;

#ifdef MISSIONPACK
	vec3_t          impactpoint, bouncedir;
#endif
	trace_t         trace;
	gentity_t      *tent;
	gentity_t      *traceEnt;
	int             damage;
	int             i;
	int             hits;
	int             unlinked;
	int             passent;
	gentity_t      *unlinkedEntities[MAX_RAIL_HITS];

	damage = 100 * s_quadFactor;

	VectorMA(muzzle, 8192, forward, end);

	// trace only against the solids, so the railgun will go through people
	unlinked = 0;
	hits = 0;
	passent = ent->s.number;
	do
	{
		trap_Trace(&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT);
		if(trace.entityNum >= ENTITYNUM_MAX_NORMAL)
		{
			break;
		}
		traceEnt = &g_entities[trace.entityNum];
		if(traceEnt->takedamage)
		{
#ifdef MISSIONPACK
			if(traceEnt->client && traceEnt->client->invulnerabilityTime > level.time)
			{
				if(G_InvulnerabilityEffect(traceEnt, forward, trace.endpos, impactpoint, bouncedir))
				{
					G_BounceProjectile(muzzle, impactpoint, bouncedir, end);
					// snap the endpos to integers to save net bandwidth, but nudged towards the line
					SnapVectorTowards(trace.endpos, muzzle);
					// send railgun beam effect
					tent = G_TempEntity(trace.endpos, EV_RAILTRAIL);
					// set player number for custom colors on the railtrail
					tent->s.clientNum = ent->s.clientNum;
					VectorCopy(muzzle, tent->s.origin2);
					// move origin a bit to come closer to the drawn gun muzzle
					VectorMA(tent->s.origin2, 4, right, tent->s.origin2);
					VectorMA(tent->s.origin2, -1, up, tent->s.origin2);
					tent->s.eventParm = 255;	// don't make the explosion at the end
					//
					VectorCopy(impactpoint, muzzle);
					// the player can hit him/herself with the bounced rail
					passent = ENTITYNUM_NONE;
				}
			}
			else
			{
				if(LogAccuracyHit(traceEnt, ent))
				{
					hits++;
				}
				G_Damage(traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
			}
#else
			if(LogAccuracyHit(traceEnt, ent))
			{
				hits++;
			}
			G_Damage(traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
#endif
		}
		if(trace.contents & CONTENTS_SOLID)
		{
			break;				// we hit something solid enough to stop the beam
		}
		// unlink this entity, so the next trace will go past it
		trap_UnlinkEntity(traceEnt);
		unlinkedEntities[unlinked] = traceEnt;
		unlinked++;
	} while(unlinked < MAX_RAIL_HITS);

	// link back in any entities we unlinked
	for(i = 0; i < unlinked; i++)
	{
		trap_LinkEntity(unlinkedEntities[i]);
	}

	// the final trace endpos will be the terminal point of the rail trail

	// snap the endpos to integers to save net bandwidth, but nudged towards the line
	SnapVectorTowards(trace.endpos, muzzle);

	// send railgun beam effect
	tent = G_TempEntity(trace.endpos, EV_RAILTRAIL);

	// set player number for custom colors on the railtrail
	tent->s.clientNum = ent->s.clientNum;

	VectorCopy(muzzle, tent->s.origin2);
	// move origin a bit to come closer to the drawn gun muzzle
	VectorMA(tent->s.origin2, 4, right, tent->s.origin2);
	VectorMA(tent->s.origin2, -1, up, tent->s.origin2);

	// no explosion at end if SURF_NOIMPACT, but still make the trail
	if(trace.surfaceFlags & SURF_NOIMPACT)
	{
		tent->s.eventParm = 255;	// don't make the explosion at the end
	}
	else
	{
		tent->s.eventParm = DirToByte(trace.plane.normal);
	}
	tent->s.clientNum = ent->s.clientNum;

	// give the shooter a reward sound if they have made two railgun hits in a row
	if(hits == 0)
	{
		// complete miss
		ent->client->accurateCount = 0;
	}
	else
	{
		// check for "impressive" reward sound
		ent->client->accurateCount += hits;
		if(ent->client->accurateCount >= 2)
		{
			ent->client->accurateCount -= 2;
			ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
			// add the sprite over the player's head
			ent->client->ps.eFlags &=
				~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND |
				  EF_AWARD_CAP);
			ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
			ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
		}
		ent->client->accuracy_hits++;
	}

}


/*
======================================================================

GRAPPLING HOOK

======================================================================
*/

void Weapon_GrapplingHook_Fire(gentity_t * ent)
{
	if(!ent->client->fireHeld && !ent->client->hook)
		fire_grapple(ent, muzzle, forward);

	ent->client->fireHeld = qtrue;
}

void Weapon_HookFree(gentity_t * ent)
{
	ent->parent->client->hook = NULL;
	ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
	G_FreeEntity(ent);
}

void Weapon_HookThink(gentity_t * ent)
{
	if(ent->enemy)
	{
		vec3_t          v, oldorigin;

		VectorCopy(ent->r.currentOrigin, oldorigin);
		v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
		v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
		v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
		SnapVectorTowards(v, oldorigin);	// save net bandwidth

		G_SetOrigin(ent, v);
	}

	VectorCopy(ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
}

/*
======================================================================

LIGHTNING GUN

======================================================================
*/

void Weapon_LightningFire(gentity_t * ent)
{
	trace_t         tr;
	vec3_t          end;

#ifdef MISSIONPACK
	vec3_t          impactpoint, bouncedir;
#endif
	gentity_t      *traceEnt, *tent;
	int             damage, i, passent;

	damage = 8 * s_quadFactor;

	passent = ent->s.number;
	for(i = 0; i < 10; i++)
	{
		VectorMA(muzzle, LIGHTNING_RANGE, forward, end);

		// The SARACEN's Lightning Discharge
		if(trap_PointContents(muzzle, -1) & MASK_WATER)
		{
			int zaps;
			gentity_t *tent;

			zaps = ent->client->ps.ammo[WP_LIGHTNING];	// determines size/power of discharge

			if(!zaps)
				return;	// prevents any subsequent frames causing second discharge + error

			zaps++;		// pmove does an ammo[gun]--, so we must compensate
			SnapVectorTowards(muzzle, ent->s.origin);	// save bandwidth

			tent = G_TempEntity(muzzle, EV_LIGHTNING_DISCHARGE);
			tent->s.eventParm = zaps;				// duration / size of explosion graphic

			ent->client->ps.ammo[WP_LIGHTNING] = 0;		// drain ent's lightning count

			if(G_WaterRadiusDamage(muzzle, ent, (float)(damage * zaps), (float)((damage * zaps) + 16), NULL, MOD_LIGHTNING_DISCHARGE))
			{
				// give an "impressive" award
				ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;

				// add the sprite over the player's head
				ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP);
				ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
				ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
			}
			
			return;
		}

		trap_Trace(&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);

#ifdef MISSIONPACK
		// if not the first trace (the lightning bounced of an invulnerability sphere)
		if(i)
		{
			// add bounced off lightning bolt temp entity
			// the first lightning bolt is a cgame only visual
			//
			tent = G_TempEntity(muzzle, EV_LIGHTNINGBOLT);
			VectorCopy(tr.endpos, end);
			SnapVector(end);
			VectorCopy(end, tent->s.origin2);
		}
#endif
		if(tr.entityNum == ENTITYNUM_NONE)
		{
			return;
		}

		traceEnt = &g_entities[tr.entityNum];

		if(traceEnt->takedamage)
		{
#ifdef MISSIONPACK
			if(traceEnt->client && traceEnt->client->invulnerabilityTime > level.time)
			{
				if(G_InvulnerabilityEffect(traceEnt, forward, tr.endpos, impactpoint, bouncedir))
				{
					G_BounceProjectile(muzzle, impactpoint, bouncedir, end);
					VectorCopy(impactpoint, muzzle);
					VectorSubtract(end, impactpoint, forward);
					VectorNormalize(forward);
					// the player can hit him/herself with the bounced lightning
					passent = ENTITYNUM_NONE;
				}
				else
				{
					VectorCopy(tr.endpos, muzzle);
					passent = traceEnt->s.number;
				}
				continue;
			}
			else
			{
				G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING);
			}
#else
			G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING);
#endif
		}

		if(traceEnt->takedamage && traceEnt->client)
		{
			tent = G_TempEntity(tr.endpos, EV_MISSILE_HIT);
			tent->s.otherEntityNum = traceEnt->s.number;
			tent->s.eventParm = DirToByte(tr.plane.normal);
			tent->s.weapon = ent->s.weapon;
			if(LogAccuracyHit(traceEnt, ent))
			{
				ent->client->accuracy_hits++;
			}
		}
		else if(!(tr.surfaceFlags & SURF_NOIMPACT))
		{
			tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS);
			tent->s.eventParm = DirToByte(tr.plane.normal);
		}

		break;
	}
}

/*
======================================================================

NAILGUN

======================================================================
*/

void Weapon_Nailgun_Fire(gentity_t * ent)
{
	gentity_t      *m;
	int             count;

	for(count = 0; count < NUM_NAILSHOTS; count++)
	{
		m = fire_nail(ent, muzzle, forward, right, up);
		m->damage *= s_quadFactor;
		m->splashDamage *= s_quadFactor;
	}

	//VectorAdd(m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta);  // "real" physics
}

#ifdef MISSIONPACK
/*
======================================================================

PROXIMITY MINE LAUNCHER

======================================================================
*/

void weapon_proxlauncher_fire(gentity_t * ent)
{
	gentity_t      *m;

	// extra vertical velocity
	forward[2] += 0.2f;
	VectorNormalize(forward);

	m = fire_prox(ent, muzzle, forward);
	m->damage *= s_quadFactor;
	m->splashDamage *= s_quadFactor;

//  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
}

#endif

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


/*
===============
LogAccuracyHit
===============
*/
qboolean LogAccuracyHit(gentity_t * target, gentity_t * attacker)
{
	if(!target->takedamage)
	{
		return qfalse;
	}

	if(target == attacker)
	{
		return qfalse;
	}

	if(!target->client)
	{
		return qfalse;
	}

	if(!attacker->client)
	{
		return qfalse;
	}

	if(target->client->ps.stats[STAT_HEALTH] <= 0)
	{
		return qfalse;
	}

	if(OnSameTeam(target, attacker))
	{
		return qfalse;
	}

	return qtrue;
}


/*
===============
CalcMuzzlePoint

set muzzle location relative to pivoting eye
===============
*/
void CalcMuzzlePoint(gentity_t * ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint)
{
	VectorCopy(ent->s.pos.trBase, muzzlePoint);
	muzzlePoint[2] += ent->client->ps.viewheight;
	VectorMA(muzzlePoint, 14, forward, muzzlePoint);
	// snap to integer coordinates for more efficient network bandwidth usage
	SnapVector(muzzlePoint);
}

/*
===============
CalcMuzzlePointOrigin

set muzzle location relative to pivoting eye
===============
*/
void CalcMuzzlePointOrigin(gentity_t * ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint)
{
	VectorCopy(ent->s.pos.trBase, muzzlePoint);
	muzzlePoint[2] += ent->client->ps.viewheight;
	VectorMA(muzzlePoint, 14, forward, muzzlePoint);
	// snap to integer coordinates for more efficient network bandwidth usage
	SnapVector(muzzlePoint);
}

/*
===============
FireWeapon
===============
*/
void FireWeapon(gentity_t * ent)
{
	if(ent->client->ps.powerups[PW_QUAD])
	{
		s_quadFactor = g_quadfactor.value;
	}
	else
	{
		s_quadFactor = 1;
	}
#ifdef MISSIONPACK
	if(ent->client->persistantPowerup && ent->client->persistantPowerup->item &&
	   ent->client->persistantPowerup->item->giTag == PW_DOUBLER)
	{
		s_quadFactor *= 2;
	}
#endif

	// track shots taken for accuracy tracking. Grapple is not a weapon and gauntet is just not tracked
	if(ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET)
	{
		if(ent->s.weapon == WP_NAILGUN)
		{
			ent->client->accuracy_shots += NUM_NAILSHOTS;
		}
		else
		{
			ent->client->accuracy_shots++;
		}
	}

	// set aiming directions
	AngleVectors(ent->client->ps.viewangles, forward, right, up);

	CalcMuzzlePointOrigin(ent, ent->client->oldOrigin, forward, right, up, muzzle);

	// fire the specific weapon
	switch (ent->s.weapon)
	{
		case WP_GAUNTLET:
			Weapon_Gauntlet(ent);
			break;

		case WP_LIGHTNING:
			Weapon_LightningFire(ent);
			break;

		case WP_SHOTGUN:
			weapon_supershotgun_fire(ent);
			break;

		case WP_MACHINEGUN:
			if(g_gametype.integer != GT_TEAM)
			{
				Bullet_Fire(ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE);
			}
			else
			{
				Bullet_Fire(ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE);
			}
			break;

		case WP_GRENADE_LAUNCHER:
			weapon_grenadelauncher_fire(ent);
			break;

		case WP_ROCKET_LAUNCHER:
			Weapon_RocketLauncher_Fire(ent);
			break;

		case WP_PLASMAGUN:
			Weapon_Plasmagun_Fire(ent);
			break;

		case WP_RAILGUN:
			weapon_railgun_fire(ent);
			break;

		case WP_BFG:
			BFG_Fire(ent);
			break;

		case WP_GRAPPLING_HOOK:
			Weapon_GrapplingHook_Fire(ent);
			break;

		case WP_CHAINGUN:
			Bullet_Fire(ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE);
			break;

		case WP_NAILGUN:
			Weapon_Nailgun_Fire(ent);
			break;

#ifdef MISSIONPACK
		case WP_PROX_LAUNCHER:
			weapon_proxlauncher_fire(ent);
			break;
#endif

		default:
// FIXME        G_Error( "Bad ent->s.weapon" );
			break;
	}
}


#ifdef MISSIONPACK

/*
===============
KamikazeRadiusDamage
===============
*/
static void KamikazeRadiusDamage(vec3_t origin, gentity_t * attacker, float damage, float radius)
{
	float           dist;
	gentity_t      *ent;
	int             entityList[MAX_GENTITIES];
	int             numListedEntities;
	vec3_t          mins, maxs;
	vec3_t          v;
	vec3_t          dir;
	int             i, e;

	if(radius < 1)
	{
		radius = 1;
	}

	for(i = 0; i < 3; i++)
	{
		mins[i] = origin[i] - radius;
		maxs[i] = origin[i] + radius;
	}

	numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES);

	for(e = 0; e < numListedEntities; e++)
	{
		ent = &g_entities[entityList[e]];

		if(!ent->takedamage)
		{
			continue;
		}

		// dont hit things we have already hit
		if(ent->kamikazeTime > level.time)
		{
			continue;
		}

		// find the distance from the edge of the bounding box
		for(i = 0; i < 3; i++)
		{
			if(origin[i] < ent->r.absmin[i])
			{
				v[i] = ent->r.absmin[i] - origin[i];
			}
			else if(origin[i] > ent->r.absmax[i])
			{
				v[i] = origin[i] - ent->r.absmax[i];
			}
			else
			{
				v[i] = 0;
			}
		}

		dist = VectorLength(v);
		if(dist >= radius)
		{
			continue;
		}

//      if( CanDamage (ent, origin) ) {
		VectorSubtract(ent->r.currentOrigin, origin, dir);
		// push the center of mass higher than the origin so players
		// get knocked into the air more
		dir[2] += 24;
		G_Damage(ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS | DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE);
		ent->kamikazeTime = level.time + 3000;
//      }
	}
}

/*
===============
KamikazeShockWave
===============
*/
static void KamikazeShockWave(vec3_t origin, gentity_t * attacker, float damage, float push, float radius)
{
	float           dist;
	gentity_t      *ent;
	int             entityList[MAX_GENTITIES];
	int             numListedEntities;
	vec3_t          mins, maxs;
	vec3_t          v;
	vec3_t          dir;
	int             i, e;

	if(radius < 1)
		radius = 1;

	for(i = 0; i < 3; i++)
	{
		mins[i] = origin[i] - radius;
		maxs[i] = origin[i] + radius;
	}

	numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES);

	for(e = 0; e < numListedEntities; e++)
	{
		ent = &g_entities[entityList[e]];

		// dont hit things we have already hit
		if(ent->kamikazeShockTime > level.time)
		{
			continue;
		}

		// find the distance from the edge of the bounding box
		for(i = 0; i < 3; i++)
		{
			if(origin[i] < ent->r.absmin[i])
			{
				v[i] = ent->r.absmin[i] - origin[i];
			}
			else if(origin[i] > ent->r.absmax[i])
			{
				v[i] = origin[i] - ent->r.absmax[i];
			}
			else
			{
				v[i] = 0;
			}
		}

		dist = VectorLength(v);
		if(dist >= radius)
		{
			continue;
		}

//      if( CanDamage (ent, origin) ) {
		VectorSubtract(ent->r.currentOrigin, origin, dir);
		dir[2] += 24;
		G_Damage(ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS | DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE);
		//
		dir[2] = 0;
		VectorNormalize(dir);
		if(ent->client)
		{
			ent->client->ps.velocity[0] = dir[0] * push;
			ent->client->ps.velocity[1] = dir[1] * push;
			ent->client->ps.velocity[2] = 100;
		}
		ent->kamikazeShockTime = level.time + 3000;
//      }
	}
}

/*
===============
KamikazeDamage
===============
*/
static void KamikazeDamage(gentity_t * self)
{
	int             i;
	float           t;
	gentity_t      *ent;
	vec3_t          newangles;

	self->count += 100;

	if(self->count >= KAMI_SHOCKWAVE_STARTTIME)
	{
		// shockwave push back
		t = self->count - KAMI_SHOCKWAVE_STARTTIME;
		KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400,
						  (int)(float)t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME));
	}
	//
	if(self->count >= KAMI_EXPLODE_STARTTIME)
	{
		// do our damage
		t = self->count - KAMI_EXPLODE_STARTTIME;
		KamikazeRadiusDamage(self->s.pos.trBase, self->activator, 400,
							 (int)(float)t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME));
	}

	// either cycle or kill self
	if(self->count >= KAMI_SHOCKWAVE_ENDTIME)
	{
		G_FreeEntity(self);
		return;
	}
	self->nextthink = level.time + 100;

	// add earth quake effect
	newangles[0] = crandom() * 2;
	newangles[1] = crandom() * 2;
	newangles[2] = 0;
	for(i = 0; i < MAX_CLIENTS; i++)
	{
		ent = &g_entities[i];
		if(!ent->inuse)
			continue;
		if(!ent->client)
			continue;

		if(ent->client->ps.groundEntityNum != ENTITYNUM_NONE)
		{
			ent->client->ps.velocity[0] += crandom() * 120;
			ent->client->ps.velocity[1] += crandom() * 120;
			ent->client->ps.velocity[2] = 30 + random() * 25;
		}

		ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
		ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
		ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
	}
	VectorCopy(newangles, self->movedir);
}

/*
===============
G_StartKamikaze
===============
*/
void G_StartKamikaze(gentity_t * ent)
{
	gentity_t      *explosion;
	gentity_t      *te;
	vec3_t          snapped;

	// start up the explosion logic
	explosion = G_Spawn();

	explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
	explosion->eventTime = level.time;

	if(ent->client)
	{
		VectorCopy(ent->s.pos.trBase, snapped);
	}
	else
	{
		VectorCopy(ent->activator->s.pos.trBase, snapped);
	}
	SnapVector(snapped);		// save network bandwidth
	G_SetOrigin(explosion, snapped);

	explosion->classname = "kamikaze";
	explosion->s.pos.trType = TR_STATIONARY;

	explosion->kamikazeTime = level.time;

	explosion->think = KamikazeDamage;
	explosion->nextthink = level.time + 100;
	explosion->count = 0;
	VectorClear(explosion->movedir);

	trap_LinkEntity(explosion);

	if(ent->client)
	{
		//
		explosion->activator = ent;
		//
		ent->s.eFlags &= ~EF_KAMIKAZE;
		// nuke the guy that used it
		G_Damage(ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE);
	}
	else
	{
		if(!strcmp(ent->activator->classname, "bodyque"))
		{
			explosion->activator = &g_entities[ent->activator->r.ownerNum];
		}
		else
		{
			explosion->activator = ent->activator;
		}
	}

	// play global sound at all clients
	te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND);
	te->r.svFlags |= SVF_BROADCAST;
	te->s.eventParm = GTS_KAMIKAZE;
}
#endif
