/*
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.

*/
// g_weapon.c

#include "g_local.h"
#include "g_gametypes.h"


static qboolean	is_quad;
static qbyte	is_silenced;


void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
{
	vec3_t	_distance;

	VectorCopy (distance, _distance);
	if (client->pers.hand == LEFT_HANDED)
		_distance[1] *= -1;
	else if (client->pers.hand == CENTER_HANDED)
		_distance[1] = 0;
	G_ProjectSource (point, _distance, forward, right, result);
}


//======================================================================
//
//	NEW WEAPON SYSTEM
//
//======================================================================


//===============
//Pickup_Weapon
//===============
qboolean Pickup_Weapon (edict_t *ent, edict_t *other)
{
	int			ammo_tag;

	if ( ( (dmflags->integer & DF_WEAPONS_STAY) //|| game.gametype == GAMTYPE_COOP
	) 
		&& other->r.client->inventory[ent->item->tag])
	{
		if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) )
			return qfalse;	// leave the weapon for others to pickup
	}

	other->r.client->inventory[ent->item->tag]++;

	if (!(ent->spawnflags & DROPPED_ITEM) )
	{
		// give them some ammo with it
		ammo_tag = ent->item->weakammo_tag;

		if( ammo_tag ) { //jal: prevent null ammo crashes
			if ( dmflags->integer & DF_INFINITE_AMMO )
				Add_Ammo (other, game.items[ammo_tag], 1000, qtrue);
			else
				Add_Ammo (other, game.items[ammo_tag], game.items[ammo_tag]->quantity, qtrue);
		}

		if (! (ent->spawnflags & DROPPED_PLAYER_ITEM) )
		{
			if (G_Gametype_CanRespawnItem(ent->item))//newgametypes
			{
				if (dmflags->integer & DF_WEAPONS_STAY)
					ent->flags |= FL_RESPAWN;
				else
					SetRespawn (ent, G_Gametype_RespawnTimeForItem(ent->item));
			}
		}
	} else { //it's a dropped weapon
		ammo_tag = ent->item->weakammo_tag;
		if( ent->count && ammo_tag ) {
			if ( dmflags->integer & DF_INFINITE_AMMO )
				Add_Ammo (other, game.items[ammo_tag], 1000, qtrue);
			else
				Add_Ammo (other, game.items[ammo_tag], ent->count, qtrue);
		}
	}

	//if using gunblade or no weapon, change to the new one at picking up
	if( (!other->s.weapon || other->s.weapon == WEAP_GUNBLADE)
		&& other->r.client->inventory[ent->item->tag] == 1 )
		other->r.client->latched_weapon = ent->item->tag;

	return qtrue;
}

//===============
//ChangeWeapon
//
//The old weapon has been dropped all the way, so make the new one
//current
//===============
void ChangeWeapon (edict_t *ent)
{
	gclient_t *client = ent->r.client;
	weapon_info_t	*weaponinfo;

	//invalid
	if( client->latched_weapon < 0 || client->latched_weapon >= WEAP_TOTAL ) {
		return;
	}

	ent->s.weapon = client->latched_weapon;
	client->latched_weapon = -1;
	client->weapon_powered = 0;

	weaponinfo = &g_weaponInfos[ent->s.weapon];

	if (ent->s.weapon && weaponinfo->firedef->usage_count)
		client->ammo_index = weaponinfo->firedef->ammo_id;
	else 
		client->ammo_index = 0;

	if (ent->s.weapon && weaponinfo->firedef_weak->usage_count)
		client->ammo_weak_index = weaponinfo->firedef_weak->ammo_id;
	else
		client->ammo_weak_index = 0;

	if (!ent->s.weapon)
	{
		return;
	}

	// change selected item
	client->selected_item = client->ps.stats[STAT_SELECTED_ITEM] = ent->s.weapon;

	//remove the hold priority from weaponOUT and
	//set a standard animation for BASIC
	ent->pmAnim.anim_priority[UPPER] = ANIM_BASIC;
	ent->pmAnim.anim[UPPER] = TORSO_STAND;

	//Send the weaponIN animation and sound style through EVENTs
	G_AddEvent (ent, EV_WEAPONUP, 1, qtrue);
}

int SelectBestWeapon( gclient_t *client, int ignore_weapon ) 
{
	int		weap, weap_chosen;
	weapon_info_t	*weaponinfo;

	//find with strong ammos
	for( weap = WEAP_TOTAL-1; weap > WEAP_NONE; weap-- )
	{
		if( ignore_weapon > 0 && weap == ignore_weapon )
			continue;
		
		if( !client->inventory[weap] )
			continue;

		weaponinfo = &g_weaponInfos[weap];
		if( !weaponinfo->firedef )
			continue;

		// wsw : pb : fixed noammo autoswitch to gunblade
		if( weaponinfo->weapon_id == WEAP_GUNBLADE )
			continue;

		if( !weaponinfo->firedef->usage_count || client->inventory[weaponinfo->firedef->ammo_id] >= weaponinfo->firedef->usage_count ) {
			weap_chosen = weap;
			goto found;
		}
	}

	//repeat find with weak ammos
	for( weap = WEAP_TOTAL-1; weap > WEAP_NONE; weap-- )
	{
		if( ignore_weapon > 0 && weap == ignore_weapon )
			continue; 
		
		if( !client->inventory[weap] )
			continue;
		
		weaponinfo = &g_weaponInfos[weap];
		if( !weaponinfo->firedef_weak )
			continue;
		
		if( !weaponinfo->firedef_weak->usage_count || client->inventory[weaponinfo->firedef_weak->ammo_id] >= weaponinfo->firedef_weak->usage_count ) {
			weap_chosen = weap;
			goto found;
		}
	}

	// didnt found any weapon so use strong gunblade
	weap_chosen = WEAP_GUNBLADE;
found:
	return weap_chosen;
}

//=================
//NoAmmoWeaponChange
//=================
void NoAmmoWeaponChange( edict_t *ent )
{
	if( ent->r.client )
		G_AddPlayerStateEvent( ent->r.client, PSEV_NOAMMO, ent->s.weapon );
}


//================
//Use_Weapon
//
//Make the weapon ready if there is ammo
//================
void Use_Weapon( edict_t *ent, gitem_t *item )
{
	int				ammocount, weakammocount;
	weapon_info_t	*weaponinfo;

	//invalid weapon item
	if( item->tag < 0 || item->tag >= WEAP_TOTAL )
		return;

	// see if we're already changing to it
	if ( ent->r.client->latched_weapon == item->tag )
		return;

	// see if we're already using it and not changing away from it
	if( item == game.items[ent->s.weapon] && ent->r.client->latched_weapon == -1 )
		return;

	weaponinfo = &g_weaponInfos[item->tag];

	if( !g_select_empty->integer && !(item->type & IT_AMMO) ) 
	{
		if( weaponinfo->firedef->usage_count ) {
			if( weaponinfo->firedef->ammo_id )
				ammocount = ent->r.client->inventory[weaponinfo->firedef->ammo_id];
			else
				ammocount = weaponinfo->firedef->usage_count; // can change weapon
		} else
			ammocount = 1; // can change weapon
			
		if( weaponinfo->firedef_weak->usage_count ) {
			if( weaponinfo->firedef_weak->ammo_id )
				weakammocount = ent->r.client->inventory[weaponinfo->firedef_weak->ammo_id];
			else
				weakammocount = weaponinfo->firedef_weak->usage_count; // can change weapon
		}else
			weakammocount = 1; // can change weapon

		if (!ammocount && !weakammocount)
		{
			return;
		}

		if (ammocount < weaponinfo->firedef->usage_count && weakammocount < weaponinfo->firedef_weak->usage_count)
		{
			return;
		}
	}

	// change to this weapon when down
	ent->r.client->latched_weapon = item->tag;

	//if we don't have any weapon, no need to wait to be down
	if( !ent->s.weapon )
		ChangeWeapon( ent );
}

//================
//Drop_Weapon
//================
void Drop_Weapon( edict_t *ent, gitem_t *item )
{
	int	otherweapon;
	edict_t *drop;
	int		ammodrop = 0;

	if (dmflags->integer & DF_WEAPONS_STAY)
		return;

	if( item->tag < 1 || item->tag >= WEAP_TOTAL ) {
		G_PrintMsg( ent, "Can't drop unknown weapon\n" );
		return;
	}

	// see if we're already using it
	if( ( item->tag == ent->s.weapon || item->tag == ent->r.client->latched_weapon )
		&& ent->r.client->inventory[item->tag] == 1 ) 
	{
		if( item->tag != WEAP_GUNBLADE ) { // put the weapon down
			otherweapon = SelectBestWeapon( ent->r.client, item->tag );
			Use_Weapon( ent, game.items[otherweapon] );
			ChangeWeapon(ent);
		} else {
			G_PrintMsg( ent, "Can't drop current weapon\n" );
			return;
		}
	}

	// if we have weak_ammo for this weapon add some to the weapon drop
	// jalfixme: shouldn't be the same value for every weapon
	if( ent->r.client->inventory[game.items[item->tag]->weakammo_tag] > 5 ) {
		ammodrop = 5;
	}

	drop = Drop_Item( ent, item );
	if( drop ) {
		ent->r.client->inventory[game.items[item->tag]->weakammo_tag] -= ammodrop;
		drop->count = ammodrop;
		drop->spawnflags |= DROPPED_PLAYER_ITEM;
		ent->r.client->inventory[item->tag]--;
		if( GS_Gametype_IsTeamBased(game.gametype) ) {
			if( ent->r.client->inventory[item->tag] > 1 )
				ent->r.client->inventory[item->tag] = 1;
		}	
	}
}


qboolean Check_BladeAttack( edict_t *ent );

//=================
//Weapon_PowerFracToAmmoUsage
//Find the ammout of ammo we will consume, being it a powered weapon or not
//=================
static int Weapon_PowerFracToAmmoUsage( gclient_t *client, firedef_t *firedef ) 
{
	float ammocount;
	float power;

	if( !firedef || !firedef->usage_count || !firedef->ammo_id )
		return 0;

	if( firedef->powering_time <= game.framemsec )
		return firedef->usage_count;

	power = (float)client->weapon_powered / (float)firedef->powering_time;
	if( power > 1.0f )
		power = 1.0f;
	
	ammocount = power * (float)firedef->usage_count;
	
	// fix if off limits
	if( ammocount < 1.0f )
		ammocount = 1.0f;

	if( client->inventory[firedef->ammo_id] ) {
		if( ammocount > client->inventory[firedef->ammo_id] )
			ammocount = client->inventory[firedef->ammo_id];
	}

	return (int)ammocount;
}


//=================
//Weapon_GenericFrametime
//=================
static void Weapon_GenericFrametime( edict_t *ent, firedef_t *firedef )
{
	gclient_t	*client = ent->r.client;

	if( ent->deadflag )
		return;

	if( client->ps.pmove.stats[PM_STAT_SLOW] > 0 )
		client->weapon_nexttime += (int)G_FRAMETIME/5.0*4.0; // pretty hax

	// remove reload wait
	if( level.timemsec >= client->weapon_nexttime &&
		client->weaponstate == WEAPON_RELOADING ) 
	{
		if( firedef->cooldown_time ) {
			client->weaponstate = WEAPON_COOLDOWN;
			client->weapon_nexttime = level.timemsec + firedef->cooldown_time;
		} else
			client->weaponstate = WEAPON_READY;
	}

	// remove cooldown wait
	if( client->weaponstate == WEAPON_COOLDOWN
		&& level.timemsec >= client->weapon_nexttime )
		client->weaponstate = WEAPON_READY;

	// change weapon init
	if( client->latched_weapon != -1 && 
		(client->weaponstate == WEAPON_READY || client->weaponstate == WEAPON_ACTIVATING)
		&& level.timemsec >= client->weapon_nexttime)
	{
		client->weaponstate = WEAPON_DROPPING;
		client->weapon_nexttime = level.timemsec + firedef->weapondown_time;

		//weapdown animation
		ent->pmAnim.anim_priority[UPPER] = ANIM_HOLD;
		ent->pmAnim.anim[UPPER] = TORSO_FLIPOUT;
	}

	//change weapon do
	if (client->weaponstate == WEAPON_DROPPING
		&& level.timemsec >= client->weapon_nexttime)
	{
		ChangeWeapon (ent);
		client->weaponstate = WEAPON_ACTIVATING;
		client->weapon_nexttime = level.timemsec + firedef->weaponup_time;
		return; //FIXME: this one MUST return to refresh the fire function pointers after weapon change
	}

	// change weapon changed
	if( client->weaponstate == WEAPON_ACTIVATING
		&& level.timemsec >= client->weapon_nexttime )
	{
		client->weaponstate = WEAPON_READY;
		client->weapon_nexttime = level.timemsec; //no delay count
	}

	//check for firing
	if( level.timemsec >= client->weapon_nexttime && 
		(client->weaponstate == WEAPON_READY || client->weaponstate == WEAPON_POWERING) ) 
	{
		if( match.state == MATCH_STATE_COUNTDOWN ) {
			// don't allow firing while in countdown
			client->weaponstate = WEAPON_READY;
			return;
		}

		// player is pressing fire
		if( (client->latched_buttons|client->buttons) & BUTTON_ATTACK )
		{ 
			client->latched_buttons &= ~BUTTON_ATTACK;
			//client->buttons &= ~BUTTON_ATTACK;
			
			if( client->weaponstate != WEAPON_POWERING ) {
				client->weapon_powered = 0;
			}
			client->weaponstate = WEAPON_POWERING;
			client->weapon_powered += G_FRAMETIME;

			// still powering up
			if( client->weapon_powered < firedef->powering_time ) {
				if( Weapon_PowerFracToAmmoUsage( client, firedef ) < client->inventory[firedef->ammo_id] )
					return;
			}
		}

		//not pressing fire anymore, or powering time reached. if weapon is powered, fire it
		if( client->weaponstate == WEAPON_POWERING ) {
			client->weaponstate = WEAPON_FIRING;
		}
	}

	// must fire a projectile? (never leave from here with WEAPON_FIRING state, or having power)
	// note: power is removed after calling the fire function, so it's value can be used in it
	if( client->weaponstate == WEAPON_FIRING )
	{
		if( match.state == MATCH_STATE_COUNTDOWN ) {
			// don't allow firing while in countdown
			client->weaponstate = WEAPON_READY;
		} else if( firedef->ammo_id && (!firedef->usage_count ||
			client->inventory[firedef->ammo_id] >= Weapon_PowerFracToAmmoUsage( client, firedef ) ) )
		{
			if( client->quad_timeout > level.timemsec )
				G_Sound (ent, CHAN_ITEM, trap_SoundIndex(S_QUAD_FIRE), 1, ATTN_NORM);
			
			if( firedef->fire ) {
				firedef->fire(ent);
				client->resp.accuracy_shots[firedef->ammo_id-AMMO_CELLS] += firedef->projectile_count;
			}
			client->weaponstate = WEAPON_RELOADING;
			client->weapon_nexttime = level.timemsec + firedef->reload_time;
			
		} else {
			client->weaponstate = WEAPON_READY;
			NoAmmoWeaponChange ( ent );
		}
		
		client->weapon_powered = 0; //(remove power)
	}
}

firedef_t *Player_GetCurrentWeaponFiredef( edict_t *ent )
{
	firedef_t	*firedef = NULL;

	if( ent->deadflag )
		return NULL;

	if( ent->s.weapon < 0 || ent->s.weapon >= WEAP_TOTAL )	//invalid weapon
		return NULL;

	//find out our current fire mode
	if( ent->r.client->inventory[g_weaponInfos[ent->s.weapon].firedef->ammo_id] >=
			Weapon_PowerFracToAmmoUsage( ent->r.client, g_weaponInfos[ent->s.weapon].firedef ) )
		firedef = g_weaponInfos[ent->s.weapon].firedef;
	else
		firedef = g_weaponInfos[ent->s.weapon].firedef_weak;

	return firedef;
}

//=================
//Weapon_Generic
// Generic think for all weapons. It chooses the weapon firedef and calls the states machine
//=================
void Weapon_Generic( edict_t *ent )
{
	if( ent->deadflag )
		return;

	if( ent->s.weapon < 0 || ent->s.weapon >= WEAP_TOTAL )	//invalid weapon
		return;

	if( Check_BladeAttack( ent ) )
		return;

	Weapon_GenericFrametime( ent, Player_GetCurrentWeaponFiredef(ent) );
}

//=================
//Weapon_Generic_Fire
// Kurim : Muzzleflash, Playernoise and ProjectSource
//=================
void Weapon_Generic_Fire( edict_t *ent, int firemode, vec3_t start, vec3_t forward )
{
	vec3_t		offset;
	vec3_t		right;

	AngleVectors (ent->r.client->v_angle, forward, right, NULL);
	G_AddEvent (ent, EV_MUZZLEFLASH, firemode, qtrue);
	VectorSet(offset, 0,0, ent->viewheight);
	P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start);
}


//=================
//Think_Weapon
//
//Called by ClientBeginServerFrame and ClientThink
//=================
void Think_Weapon( edict_t *ent )
{
	if( ent->s.weapon > 0 && (game.items[ent->s.weapon]->type & IT_WEAPON) )
	{
		is_quad = (ent->r.client->quad_timeout > level.timemsec);
		if( ent->r.client->silencer_shots )
			is_silenced = MZ_SILENCED;
		else
			is_silenced = 0;

		//jal: ok, this is stupid. Every weapon is calling 
		// weapon_generic, so stop using a function pointer for it
		Weapon_Generic(ent);
	}
}


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

WEAPONS

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

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

GUNBLADE

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

//===============
//Check_BladeAttack
//===============
qboolean Check_BladeAttack( edict_t *ent )
{
	edict_t		*other = NULL;
	vec3_t		start, forward, offset, right, end;
	trace_t		tr;
	gclient_t	*client = ent->r.client;
	firedef_t	*firedef;
	int			damage;
	int			range;
	int			knockback;

	// wsw: pb disable collision in race mode
	if( game.gametype == GAMETYPE_RACE || match.state == MATCH_STATE_COUNTDOWN )
		return qfalse;

	if( ent->s.weapon != WEAP_GUNBLADE )
		return qfalse;

	firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	damage = firedef->damage;
	range = firedef->timeout;
	knockback = firedef->knockback;

	if( client->weaponstate != WEAPON_READY )
		return qfalse;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	AngleVectors( ent->r.client->v_angle, forward, right, NULL );
	VectorSet( offset, 0, 0, ent->viewheight );
	P_ProjectSource( ent->r.client, ent->s.origin, offset, forward, right, start );

	VectorMA( start, range, forward, end );
	trap_Trace( &tr, start, NULL, NULL, end, ent, MASK_SHOT );

	if( tr.fraction < 1.0 )
	{
		other = &game.edicts[tr.ent];
		if( other->r.client )
			if( other->s.team != ent->s.team || ent->s.team == TEAM_PLAYERS )
			{
				client->weaponstate = WEAPON_RELOADING;
				client->weapon_nexttime = level.timemsec + firedef->reload_time;

				client->resp.accuracy_shots[AMMO_WEAK_GUNBLADE-AMMO_CELLS]++;
				G_AddEvent( ent, EV_MUZZLEFLASH, FIRE_MODE_WEAK, qtrue );
				W_Fire_Blade( ent, range, start, forward, firedef->damage, firedef->knockback, MOD_GUNBLADE_W );
				return qtrue;
			}
	}
	return qfalse;
}


//=================
//Weapon_Fire_Gunblade_Weak
//=================
void Weapon_Fire_Gunblade_Weak( edict_t *ent )
{
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Blade( ent, firedef->timeout, start, forward, damage, knockback, MOD_GUNBLADE_W );
}

//=================
//Weapon_Fire_Gunblade_Strong
//=================
void Weapon_Fire_Gunblade_Strong( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	vec3_t		start, forward;
	float		power;
	int			damage;
	int			knockback;
	int			radius;
	int			speed = firedef->speed;

	//shut down power if not waited enough (works)
	//if( ent->r.client->weapon_powered < 5 * G_FRAMETIME )
	//	return;

	power = (float)ent->r.client->weapon_powered / (float)firedef->powering_time;
	if( power > 1.0f )
		power = 1.0f;
	if( power < 0.1f )
		power = 0.1f;

	damage = firedef->damage * power;
	knockback = firedef->knockback * power;
	radius = firedef->splash_radius * power;
	if( damage < firedef->splash_min_damage )
		damage = firedef->splash_min_damage;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	if( ent->waterlevel == 3 ) {
		damage = (int)( damage * 0.8f );
		knockback = (int)(knockback * 0.6f );
		speed = (int)(speed * 0.6f );
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_GunbladeBlast( ent, start, forward,
		damage,
		knockback,
		firedef->splash_min_damage,
		radius,
		speed,
		firedef->timeout,
		MOD_GUNBLADE_S );

	// ammo usage
	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) {
		client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef );
	}
}

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

SHOCKWAVE

======================================================================
*/
void Weapon_Fire_Shockwave_Strong( edict_t *ent )
{
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	gclient_t	*client = ent->r.client;

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_Shockwave( ent, start, forward, firedef->speed, firedef->splash_radius, firedef->timeout );
	// ammo usage
	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) {
		client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef );
	}
}

void Weapon_Fire_Shockwave_Weak( edict_t *ent )
{
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	gclient_t	*client = ent->r.client;

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Shockwave( ent, start, forward, firedef->speed, firedef->splash_radius, firedef->timeout );
	// ammo usage
	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) {
		client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef );
	}
}

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

RIOTGUN

======================================================================
*/
//=================
//Weapon_Fire_Riotgun_Strong
//=================
void Weapon_Fire_Riotgun_Strong( edict_t *ent )
{
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	if( ent->waterlevel == 3 )
		damage = (int)(damage * 0.8);

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_Riotgun( ent, start, forward, damage, knockback, firedef->h_spread, 
	firedef->v_spread, firedef->projectile_count, DAMAGE_SHELL, MOD_RIOTGUN_S );
	
	if( firedef->ammo_id && firedef->usage_count && !(dmflags->integer & DF_INFINITE_AMMO ) )
		ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

//=================
//Weapon_Fire_Riotgun_Weak
//=================
void Weapon_Fire_Riotgun_Weak( edict_t *ent )
{
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	if( ent->waterlevel == 3 )
		damage = (int)(damage * 0.8);

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Riotgun( ent, start, forward, damage, knockback,	firedef->h_spread, 
	firedef->v_spread, firedef->projectile_count, DAMAGE_SHELL, MOD_RIOTGUN_W );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

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

GRENADELAUNCHER

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

//=================
//Weapon_GrenadeLauncher_Fire_Strong
//=================
void Weapon_GrenadeLauncher_Fire_Strong( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		forward;
	vec3_t		start;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			damage = firedef->damage;
	int			mindmg = firedef->splash_min_damage;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
		mindmg *= 2;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_Grenade( ent, start, forward, firedef->speed,
		damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_GRENADE_S );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

//=================
//Weapon_GrenadeLauncher_Fire_Weak
//=================
void Weapon_GrenadeLauncher_Fire_Weak( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		forward;
	vec3_t		start;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			mindmg = firedef->splash_min_damage;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
		mindmg *= 2;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Grenade( ent, start, forward, firedef->speed,
		damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_GRENADE_W );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

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

ROCKETLAUNCHER

======================================================================
*/
//=================
//Weapon_RocketLauncher_Fire_Strong
//=================
void Weapon_RocketLauncher_Fire_Strong( edict_t *ent )
{
	vec3_t		start;
	vec3_t		forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			mindmg = firedef->splash_min_damage;
	int			damage = firedef->damage;
	int			speed = firedef->speed;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;

		if( game.gametype == GAMETYPE_MIDAIR ) {
			mindmg = 500;
			speed *= 1.3;
		}
	}

	//slow down under water
	if( ent->waterlevel == 3 )
		speed = (int)speed*0.5;

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_Rocket( ent, start, forward, speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_ROCKET_S );

	if( game.gametype == GAMETYPE_MIDAIR )
		return;

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

//=================
//Weapon_RocketLauncher_Fire_Weak
//=================
void Weapon_RocketLauncher_Fire_Weak( edict_t *ent )
{
	vec3_t		start;
	vec3_t		forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			mindmg = firedef->splash_min_damage;
	int			damage = firedef->damage;
	int			speed = firedef->speed;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	//slow down under water
	if ( ent->waterlevel == 3 )
		speed = (int)speed*0.5;

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Rocket( ent, start, forward, speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_ROCKET_W );

	if( game.gametype == GAMETYPE_MIDAIR )
		return;

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

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

PLASMAGUN

======================================================================
*/
//=================
//Weapon_Fire_Plasmagun_Strong
//=================
void Weapon_Fire_Plasmagun_Strong( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			damage = firedef->damage;
	int			mindmg = firedef->splash_min_damage;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
		mindmg *= 2;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );

	W_Fire_Plasma( ent, start, forward, damage, knockback, mindmg, firedef->splash_radius,
		firedef->speed, firedef->timeout, MOD_PLASMA_S );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

//=================
//Weapon_Fire_Plasmagun_Weak
//=================
void Weapon_Fire_Plasmagun_Weak( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			mindmg = firedef->splash_min_damage;
	int			knockback = firedef->knockback;

	if( is_quad )
	{
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
		mindmg *= 2;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Plasma( ent, start, forward, damage, knockback, mindmg, firedef->splash_radius,
		firedef->speed, firedef->timeout, MOD_PLASMA_W );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

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

LASERGUN

======================================================================
*/
//=================
//Weapon_Fire_Lasergun_Strong
//=================
void Weapon_Fire_Lasergun_Strong( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	// don't use Weapon_Generic_Fire because we don't want to send muzzleflash always
	{
		vec3_t		offset;
		vec3_t		right;

		AngleVectors (ent->r.client->v_angle, forward, right, NULL);
		VectorSet(offset, 0,0, ent->viewheight);
		P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start);
	}
	W_Fire_Lasergun( ent, start, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_S );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}

//=================
//Weapon_Fire_Lasergun_Weak
//=================
void Weapon_Fire_Lasergun_Weak( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;
	
	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	// don't use Weapon_Generic_Fire because we don't want to send muzzleflash always
	{
		vec3_t		offset;
		vec3_t		right;

		AngleVectors (ent->r.client->v_angle, forward, right, NULL);
		VectorSet(offset, 0,0, ent->viewheight);
		P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start);
	}

	// find the endpoint into the ones in the backup trail
	{
		vec3_t	end;
		int	backtime = 0;
		int framenum;
		int	backframes = (int)(backtime/game.framemsec);
		if( backframes > LASERGUN_WEAK_TRAIL_MASK )
			backframes = LASERGUN_WEAK_TRAIL_MASK;

		framenum = level.framenum - backframes;
		if( framenum < 0 )
			framenum = 0;

		VectorCopy( client->lasergunTrail[framenum & LASERGUN_WEAK_TRAIL_MASK], end );
		W_Fire_Lasergun_Weak( ent, start, end, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_W );
	}

	//W_Fire_Lasergun( ent, start, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_W );
	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}


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

ELECTROBOLT

======================================================================
*/
//=================
//Weapon_Fire_Electrobolt_Strong
//=================
void Weapon_Fire_Electrobolt_Strong( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;
	int			dmgflags = DAMAGE_ENERGY;

	if( g_instagib->integer ) {
		damage = 200;
		dmgflags |= DAMAGE_NO_ARMOR;
	}
	else if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}

	Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward );
	W_Fire_Electrobolt_Strong( ent, start, forward, firedef->speed, damage, knockback, firedef->timeout, dmgflags, MOD_ELECTROBOLT_S );

	if( !g_instagib->integer ) {
		if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
			client->inventory[firedef->ammo_id] -= firedef->usage_count;
	}
}

//=================
//Weapon_Fire_Electrobolt_Weak
//=================
void Weapon_Fire_Electrobolt_Weak( edict_t *ent )
{
	gclient_t	*client = ent->r.client;
	vec3_t		start, forward;
	firedef_t	*firedef = g_weaponInfos[ent->s.weapon].firedef_weak;
	int			damage = firedef->damage;
	int			knockback = firedef->knockback;

	if( is_quad ) {
		damage *= QUAD_DAMAGE_SCALE;
		knockback *= QUAD_KNOCKBACK_SCALE;
	}
	
	Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward );
	W_Fire_Electrobolt_Weak( ent, start, forward, firedef->speed, damage, knockback, firedef->timeout, DAMAGE_BOLT_WEAK, MOD_ELECTROBOLT_W );

	if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) )
		client->inventory[firedef->ammo_id] -= firedef->usage_count;
}



//======================================================
//
//	WEAPON DEFS
//
//======================================================

weapon_info_t g_weaponInfos[WEAP_TOTAL];

#define INSTANT 0

// WEAP_NONE
firedef_t noweaponFireDef = 
{
	"No Weapon",						// weapon name
	NULL,								// fire func
	0,									// fire mode
	AMMO_NONE,							// ammo tag
	0,									// ammo usage per shot
	0,									// projectiles fired each shot
		
	//timings (in msecs)
	100,								// weapon up frametime
	100,								// weapon down frametime
	0,									// reload frametime
	0,									// cooldown frametime
	0,									// max powering up time
	0,									// projectile timeout 
		
	//damages
	0,									// damage
	0,									// knockback
	0,									// splash radius
	0,									// splash minimum damage
		
	//projectile def
	0,									// speed
	0,									// h spread (I added a small spread while using the replacement gun)
	0,									// v spread

	//ammo
	0,									// pickup amount
	0									// max amount
};

firedef_t ammoFireDefs[] = 
{

	// WEAP_GUNBLADE (strong) (the gun)
	{
		"Gunblade",						// weapon name
		Weapon_Fire_Gunblade_Strong,	// fire func
		FIRE_MODE_STRONG,				// fire mode
		AMMO_CELLS,						// ammo tag
		20,						// ammo usage per shot
		1,						// projectiles fired each shot

		//timings (in msecs)
		50,						// weapon up frametime
		50,						// weapon down frametime
		250,						// reload frametime
		0,						// cooldown frametime
		2000,						// max powering up time
		5000,						// projectile timeout 

		//damages
		60,						// damage
		60,						// knockback
		80,						// splash radius
		8,						// splash minimum damage

		//projectile def
		3000,						// speed
		0,						// h spread
		0,						// v spread

		//ammo
		10,					// pickup amount
		100					// max amount
	},

	// WEAP_GUNBLADE (weak) (the blade)
	{
		"Gunblade",
		Weapon_Fire_Gunblade_Weak,		// fire func
		FIRE_MODE_WEAK,					// fire mode
		AMMO_WEAK_GUNBLADE,				// ammo tag
		0,					// ammo usage per shot
		0,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		500,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		64,					// projectile timeout  / projectile range for instant weapons 

		//damages
		50,					// damage
		100,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		0,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		0,					// pickup amount
		0					// max amount
	},

	// WEAP_SHOCKWAVE (strong)
	{
		"Shockwave",
		Weapon_Fire_Shockwave_Strong,		// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_WAVES,							// ammo tag
		1,									// ammo usage per shot
		1,									// projectiles fired each shot

		//timings (in msecs)
		100,								// weapon up frametime
		100,								// weapon down frametime
		500,								// reload frametime
		0,									// cooldown frametime
		0,									// max powering up time
		0,									// projectile timeout / projectile range for instant weapons  

		//damages
		5,									// damage
		0,									// knockback
		0,									// splash radius
		0,									// splash minimum damage

		//projectile def
		INSTANT,							// speed
		0,									// h spread
		0,									// v spread

		//ammo
		5,									// pickup amount
		5									// max amount
	},

	// WEAP_SHOCKWAVE (weak)
	{
		"Shockwave",
		Weapon_Fire_Shockwave_Weak,			// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_WAVES,					// ammo tag
		1,									// ammo usage per shot
		1,									// projectiles fired each shot

		//timings (in msecs)
		100,								// weapon up frametime
		100,								// weapon down frametime
		500,								// reload frametime
		0,									// cooldown frametime
		0,									// max powering up time
		0,									// projectile timeout / projectile range for instant weapons  

		//damages
		5,									// damage
		0,									// knockback
		0,									// splash radius
		0,									// splash minimum damage

		//projectile def
		INSTANT,							// speed
		0,									// h spread
		0,									// v spread

		//ammo
		10,									// pickup amount
		10									// max amount
	},

	// WEAP_RIOTGUN (strong)
	{
		"Riotgun",
		Weapon_Fire_Riotgun_Strong,			// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_SHELLS,						// ammo tag
		1,					// ammo usage per shot
		38,			//38*3=114	// projectiles fired each shot

		//timings (in msecs)
		100,					// weapon up frametime
		100,					// weapon down frametime
		1000,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		8192,				// projectile timeout / projectile range for instant weapons 

		//damages
		3,					// damage
		3,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		INSTANT,				// speed
		900,					// h spread
		900,					// v spread

		//ammo
		5,					// pickup amount
		10					// max amount
	},

	// WEAP_RIOTGUN (weak)
	{
		"Riotgun",
		Weapon_Fire_Riotgun_Weak,			// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_SHELLS,					// ammo tag
		1,					// ammo usage per shot
		26,			//26*3=78	// projectiles fired each shot

		//timings (in msecs)
		100,					// weapon up frametime
		100,					// weapon down frametime
		1000,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		8192,				// projectile timeout / projectile range for instant weapons  

		//damages
		3,					// damage
		3,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		INSTANT,				// speed
		900,					// h spread
		900,					// v spread

		//ammo
		10,					// pickup amount
		15					// max amount
	},

	// WEAP_GRENADELAUNCHER (strong)
	{
		"Grenade Launcher",
		Weapon_GrenadeLauncher_Fire_Strong,	// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_GRENADES,						// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		700,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		2000,					// projectile timeout 

		//damages
		110,					// damage
		120,					// knockback
		150,					// splash radius
		20,					// splash minimum damage

		//projectile def
		800,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		5,					// pickup amount
		10					// max amount
	},

	// WEAP_GRENADELAUNCHER (weak)
	{
		"Grenade Launcher",
		Weapon_GrenadeLauncher_Fire_Weak,	// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_GRENADES,					// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		800,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		2000,					// projectile timeout 

		//damages
		100,					// damage
		80,					// knockback
		150,					// splash radius
		5,					// splash minimum damage

		//projectile def
		800,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		10,					// pickup amount
		15					// max amount
	},

	// WEAP_ROCKETLAUNCHER (strong)
	{
		"Rocket Launcher",
		Weapon_RocketLauncher_Fire_Strong,	// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_ROCKETS,						// ammo tag
		1,								// ammo usage per shot
		1,								// projectiles fired each shot

		//timings (in msecs)
		50,								// weapon up frametime
		50,								// weapon down frametime
		800,								// reload frametime
		0,								// cooldown frametime
		0,								// max powering up time
		10000,								// projectile timeout 

		//damages
		105,								// damage
		90,								// knockback
		120,								// splash radius
		20,								// splash minimum damage

		//projectile def
		1000,								// speed
		0,								// h spread
		0,								// v spread

		//ammo
		5,					// pickup amount
		10					// max amount
	},

	// WEAP_ROCKETLAUNCHER (weak)
	{
		"Rocket Launcher",
		Weapon_RocketLauncher_Fire_Weak,	// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_ROCKETS,					// ammo tag
		1,								// ammo usage per shot
		1,								// projectiles fired each shot

		//timings (in msecs)
		50,								// weapon up frametime
		50,								// weapon down frametime
		800,								// reload frametime
		0,								// cooldown frametime
		0,								// max powering up time
		10000,				// projectile timeout 

		//damages
		100,								// damage
		80,								// knockback
		105,								// splash radius
		10,								// splash minimum damage

		//projectile def
		1000,								// speed
		0,								// h spread
		0,								// v spread

		//ammo
		10,					// pickup amount
		15					// max amount
	},

	// WEAP_PLASMAGUN (strong)
	{
		"Plasmagun",
		Weapon_Fire_Plasmagun_Strong,		// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_PLASMA,						// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		140,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		5000,					// projectile timeout 

		//damages
		23,					// damage
		24,					// knockback
		40,					// splash radius
		5,					// splash minimum damage

		//projectile def
		1900,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		25,					// pickup amount
		50					// max amount
	},

	// WEAP_PLASMAGUN (weak)
	{
		"Plasmagun",
		Weapon_Fire_Plasmagun_Weak,			// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_PLASMA,					// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		140,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		5000,					// projectile timeout 

		//damages
		17,					// damage
		15,					// knockback
		20,					// splash radius
		1,					// splash minimum damage

		//projectile def
		1900,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		50,					// pickup amount
		75					// max amount
	},

	// WEAP_ELECTROBOLT (strong)
	{
		"Electrobolt",
		Weapon_Fire_Electrobolt_Strong,		// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_BOLTS,							// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		1250,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		4096,					// projectile timeout / projectile range for instant weapons

		//damages
		100,					// damage
		100,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		INSTANT,				// speed
		0,					// h spread
		0,					// v spread

		//ammo
		5,					// pickup amount
		5					// max amount
	},

	// WEAP_ELECTROBOLT (weak)
	{
		"Electrobolt",
		Weapon_Fire_Electrobolt_Weak,		// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_BOLTS,					// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		1250,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		5000,					// projectile timeout 

		//damages
		100,					// damage
		100,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		5000,					// speed
		0,					// h spread
		0,					// v spread

		//ammo
		5,					// pickup amount
		10					// max amount
	},

	// WEAP_LASERGUN (strong)
	{
		"LaserGun",
		Weapon_Fire_Lasergun_Strong,		// fire func
		FIRE_MODE_STRONG,					// fire mode
		AMMO_LASERS,						// ammo tag
		1,						// ammo usage per shot
		1,						// projectiles fired each shot

		//timings (in msecs)
		50,						// weapon up frametime
		50,						// weapon down frametime
		50,						// reload frametime
		0,						// cooldown frametime
		0,						// max powering up time
		532,						// projectile timeout / projectile range for instant weapons

		//damages
		8,						// damage
		16,						// knockback
		0,						// splash radius
		0,						// splash minimum damage

		//projectile def
		INSTANT,					// speed
		0,						// h spread
		0,

		//ammo
		40,					// pickup amount
		80					// max amount
	},

	// WEAP_LASERGUN (weak)
	{
		"LaserGun",
		Weapon_Fire_Lasergun_Weak,			// fire func
		FIRE_MODE_WEAK,						// fire mode
		AMMO_WEAK_LASERS,					// ammo tag
		1,					// ammo usage per shot
		1,					// projectiles fired each shot

		//timings (in msecs)
		50,					// weapon up frametime
		50,					// weapon down frametime
		50,					// reload frametime
		0,					// cooldown frametime
		0,					// max powering up time
		532,					// projectile timeout / projectile range for instant weapons

		//damages
		8,					// damage
		16,					// knockback
		0,					// splash radius
		0,					// splash minimum damage

		//projectile def
		INSTANT,				// speed
		0,					// h spread
		0,					// v spread

		//ammo
		80,					// pickup amount
		120					// max amount
	},

	{NULL}
};






//==========================================
// 
// 
//==========================================
qbyte *G_LoadWeaponDefFile( char *defname )
{
	int		length, filenum;
	qbyte	*data;
	char	filename[MAX_QPATH];

	Q_snprintfz( filename, sizeof(filename), "weapondefs/%s.def", defname );
	length = trap_FS_FOpenFile( filename, &filenum, FS_READ );

	if (length == -1) {
		G_Printf ("Couldn't find script: %s.\n", filename);
		return NULL;
	}

	if( !length ) {
		G_Printf ("Found empty script: %s.\n", filename);
		trap_FS_FCloseFile( filenum );
		return NULL;
	}

	//load the script data into memory
	data = G_Malloc( length + 1 );
	trap_FS_Read( data, length, filenum );
	trap_FS_FCloseFile( filenum );

	if( !data[0] ) {
		G_Printf ("Found empty script: %s.\n", filename);
		return NULL;
	}

	return data;
}

#ifdef ALLOW_WEAPONDEFS_READED_FROM_DISK
#define WEAPONDEF_NUMPARMS 17
qboolean G_ParseFiredefFile( qbyte *buf, firedef_t *firedef )
{
	char		*ptr, *token;
	int			count = 0;
	int			parm[WEAPONDEF_NUMPARMS];

	// jal: this is quite ugly.
	ptr = ( char * )buf;
	while ( ptr )
	{
		token = COM_ParseExt ( &ptr, qtrue );
		if( !token )
			break;

		if( !token[0] )
			continue;

		//ignore spacing tokens
		if( !Q_stricmp( token, "," ) || 
			!Q_stricmp( token, "{" ) || 
			!Q_stricmp( token, "}" ) )
			continue;

		//some token sanity checks
		if( token[strlen(token)-1] == ',' )
			token[strlen(token)-1] = 0;
		if( token[strlen(token)-1] == '}' )
			token[strlen(token)-1] = 0;
		//(I don't fix these ones, but show the error)
		if( token[0] == ',' ) {
			G_Printf( "ERROR in script. Comma must be followed by space or newline\n" );
			return qfalse;
		}
		if( token[0] == '{' || token[0] == '}' ) {
			G_Printf( "ERROR in script. Scorches must be followed by space or newline\n" );
			return qfalse;
		}

		if( count > WEAPONDEF_NUMPARMS )
			return qfalse;

		if( !Q_stricmp( token, "instant" ) )
			parm[count] = 0;
		else
			parm[count] = atoi( token );

		if( parm[count] < 0 )
			return qfalse;

		count++;
	}

	// incomplete or wrong file
	if( count < WEAPONDEF_NUMPARMS )
		return qfalse;

	// validate

	// 2 and 3 are weapon switches. They MUST be G_FRAMETIME msecs at least
	if( parm[2] < G_FRAMETIME )
		parm[2] = G_FRAMETIME;

	if( parm[3] < G_FRAMETIME )
		parm[3] = G_FRAMETIME;

	count = 0;
	// put the data into the firedef
	firedef->usage_count = parm[count++];
	firedef->projectile_count = parm[count++];

	firedef->weaponup_time = parm[count++];
	firedef->weapondown_time = parm[count++];
	firedef->reload_time = parm[count++];
	firedef->cooldown_time = parm[count++];
	firedef->powering_time = parm[count++];
	firedef->timeout = parm[count++];

	firedef->damage = parm[count++];
	firedef->knockback = parm[count++];
	firedef->splash_radius = parm[count++];
	firedef->splash_min_damage = parm[count++];

	firedef->speed = parm[count++];
	firedef->h_spread = parm[count++];
	firedef->v_spread = parm[count++];

	firedef->ammo_pickup = parm[count++];
	firedef->ammo_max = parm[count++];

	return qtrue;
}

qboolean G_LoadFiredefFromFile( firedef_t *firedef )
{
	char	filename[MAX_QPATH];
	qbyte	*data;

	if( !firedef || !firedef->name )
		return qfalse;

	Q_snprintfz( filename, sizeof(filename), "%s %s", firedef->name,
		(firedef->fire_mode == FIRE_MODE_STRONG) ? "strong" : "weak" );

	data = G_LoadWeaponDefFile( filename );
	if( !data ) return qfalse;

	//parse the file updating the firedef
	if( !G_ParseFiredefFile( data, firedef ) ) 
	{
		G_Printf( "'InitWeapons': Error in definition file %s\n", filename );
		G_Free(data);
		return qfalse;
	}

	G_Free(data);
	return qtrue;
}
#endif //ALLOW_WEAPONDEFS_READED_FROM_DISK

//=================
//G_FiredefForAmmo
//=================
firedef_t *G_FiredefForAmmo( int tag )
{
	gitem_t *item = GS_FindItemByTag( tag );

	if( item->type == IT_AMMO)
		return (firedef_t *)item->info;
	else
		return NULL;
}

//=================
//G_FiredefForWeapon
//=================
weapon_info_t *G_FiredefForWeapon( int tag )
{
	gitem_t *item = GS_FindItemByTag( tag );

	if( item->type == IT_WEAPON )
		return (weapon_info_t *)item->info;
	else
		return NULL;
}

//=================
//G_InitWeapons
//=================
void G_InitWeapons( void )
{
	int	i;
	firedef_t		*firedef;

	//set up no weapon with no fires (not totally correct. I should not use item->info)
	g_weaponInfos[WEAP_NONE].firedef = &noweaponFireDef;
	g_weaponInfos[WEAP_NONE].firedef_weak = &noweaponFireDef;

	//assign the actual weapons
	for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ )
	{
		g_weaponInfos[i].firedef = g_weaponInfos[i].firedef_weak = NULL; //start clean

		//find predefined firedefs for this weapon
		for( firedef = &ammoFireDefs[0]; firedef->name; firedef++ )
		{
			if( !Q_stricmp( firedef->name, game.items[i]->pickup_name ) ) {
				if( firedef->ammo_id < AMMO_WEAK_GUNBLADE ) {
					g_weaponInfos[i].firedef = firedef;
					//G_Printf( "WEAPONS: Assigned firedef strong to weapon %s\n", firedef->name );
				} else {//if( firedef->fire_mode == FIRE_MODE_WEAK )
					g_weaponInfos[i].firedef_weak = firedef;
					//G_Printf( "WEAPONS: Assigned firedef weak to weapon %s\n", firedef->name );
				}
			}
		}

		if( !g_weaponInfos[i].firedef )
			G_Error( "'G_InitWeapons': Weapon %s doesn't have any strong fire defined\n", game.items[i]->pickup_name );

		if( !g_weaponInfos[i].firedef_weak )
			G_Error( "'G_InitWeapons': Weapon %s doesn't have any weak fire defined\n", game.items[i]->pickup_name );

		//link here too, but use g_weaponInfos better
		game.items[i]->info = (weapon_info_t *)&g_weaponInfos[i];
		if( game.items[i]->weakammo_tag ) {
			game.items[game.items[i]->weakammo_tag]->info = g_weaponInfos[i].firedef_weak;
			game.items[game.items[i]->weakammo_tag]->quantity = g_weaponInfos[i].firedef_weak->ammo_pickup;
		}
		if( game.items[i]->ammo_tag ) {
			game.items[game.items[i]->ammo_tag]->info = g_weaponInfos[i].firedef;
			game.items[game.items[i]->ammo_tag]->quantity = g_weaponInfos[i].firedef->ammo_pickup;
		}
	}

#ifdef ALLOW_WEAPONDEFS_READED_FROM_DISK
	// ok, we assigned the default ones, but meanwhile we are designing the
	// game we will load replacements from text files
	for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ )
	{
		if( G_LoadFiredefFromFile( g_weaponInfos[i].firedef ) )
			if( developer->integer )	
				G_Printf("'G_InitWeapons': Found firedef file for %s strong\n", g_weaponInfos[i].firedef->name);
		if( G_LoadFiredefFromFile( g_weaponInfos[i].firedef_weak ) )
			if( developer->integer )
				G_Printf("'G_InitWeapons': Found firedef file for %s weak\n", g_weaponInfos[i].firedef->name);
	}
#endif //ALLOW_WEAPONDEFS_READED_FROM_DISK
	G_Printf( "WEAPONS Initialized\n" );
}
