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

qboolean	Pickup_Weapon( edict_t *ent, edict_t *other );
void		Use_Weapon( edict_t *ent, gitem_t *inv );
void		Drop_Weapon( edict_t *ent, gitem_t *item );

void		G_InitWeapons( void );
void		Weapon_Generic( edict_t *ent );

#define		ARMOR_SHARD_VALUE	5
#ifdef UNBALANCED_ARMORS 


#ifdef ARMOR_SYSTEM_OF_THE_DAY
gitem_armor_t g_armor_info	= {  25, 75, 0.80f, ARMOR_GA };
gitem_armor_t y_armor_info	= { 50, 100, 0.80f, ARMOR_YA };
gitem_armor_t r_armor_info	= { 100, 200, 0.80f, ARMOR_RA }; // has armor decay
#else
gitem_armor_t g_armor_info	= {  25, 75, 0.50f, ARMOR_GA };
gitem_armor_t y_armor_info	= { 50, 100, 0.66f, ARMOR_YA };
gitem_armor_t r_armor_info	= { 100, 150, 0.75f, ARMOR_RA };
#endif

#else
gitem_armor_t g_armor_info	= {  50, 100, 0.50f, ARMOR_GA };
gitem_armor_t y_armor_info	= { 100, 150, 0.66f, ARMOR_YA };
gitem_armor_t r_armor_info	= { 150, 200, 0.75f, ARMOR_RA };
#endif

#define HEALTH_IGNORE_MAX	1
#define HEALTH_TIMED		2

void Use_Quad( edict_t *ent, gitem_t *item );
static int	quad_drop_timeout_hack;


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

void DoRespawn( edict_t *ent )
{
	if( ent->team )
	{
		edict_t	*master;
		int	count;
		int choice;

		master = ent->teammaster;

		//in ctf, when we are weapons stay, only the master of a team of weapons is spawned
		if( game.gametype == GAMETYPE_CTF &&
			(dmflags->integer & DF_WEAPONS_STAY) &&
			master->item && (master->item->type & IT_WEAPON) )
			ent = master;
		else {
			for( count = 0, ent = master; ent; ent = ent->chain, count++ );

			choice = rand() % count;

			for (count = 0, ent = master; count < choice; ent = ent->chain, count++);
		}
	}

	ent->r.svflags &= ~SVF_NOCLIENT;
	ent->r.solid = SOLID_TRIGGER;
	trap_LinkEntity(ent);

	// send an effect
	G_AddEvent( ent, EV_ITEM_RESPAWN, ent->item ? ent->item->tag : 0, qtrue );
}

void SetRespawn( edict_t *ent, float delay )
{
	if( delay < 0 ) {
		G_FreeEdict(ent);
		return;
	}
	ent->flags |= FL_RESPAWN;
	ent->r.svflags |= SVF_NOCLIENT;
	ent->r.solid = SOLID_NOT;
	ent->nextthink = level.timemsec + 1000 * delay;
	ent->think = DoRespawn;
	trap_LinkEntity(ent);
}


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

qboolean Pickup_Powerup( edict_t *ent, edict_t *other )
{
	int		quantity;

	if( !ent->item || !ent->item->tag )
		return qfalse;

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

	//if ((game.gametype == GAMETYPE_COOP) && (ent->item->flags & ITFLAG_STAY_COOP) && (quantity > 0))
	//	return qfalse;

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

	if( !G_Gametype_CanRespawnItem(ent->item) )
		return qtrue;

	//set for respawn if in multiplayer mode
	if( !(ent->spawnflags & DROPPED_ITEM) )
		SetRespawn( ent, G_Gametype_RespawnTimeForItem(ent->item) );

	if( (dmflags->integer & DF_INSTANT_ITEMS) 
		|| ( (ent->item->tag == POWERUP_QUAD) && (ent->spawnflags & DROPPED_PLAYER_ITEM) ) )
	{
		if( (ent->item->tag == POWERUP_QUAD) && (ent->spawnflags & DROPPED_PLAYER_ITEM) )
			quad_drop_timeout_hack = (ent->nextthink - level.timemsec);

		G_UseItem( other, ent->item );
	}

	return qtrue;
}

void Drop_General( edict_t *ent, gitem_t *item )
{
	if( Drop_Item( ent, item ) ) {
		ent->r.client->inventory[item->tag]--;
		ValidateSelectedItem(ent);
	}
}


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

qboolean Pickup_AmmoPack( edict_t *ent, edict_t *other )
{
	gclient_t *client = other->r.client;
	firedef_t *firedef;
	int		i;

	if( !client )
		return qfalse;

	for( i=1; i < AMMO_TOTAL; i++ ) {
		firedef = G_FiredefForAmmo( i );
		if( firedef ) {
			client->inventory[i] += ent->invpak[i];
			if( client->inventory[i] > firedef->ammo_max )
				client->inventory[i] = firedef->ammo_max;
		}
	}

	if( !(ent->spawnflags & DROPPED_ITEM) && G_Gametype_CanRespawnItem(ent->item) )
		SetRespawn( ent, G_Gametype_RespawnTimeForItem(ent->item) );

	return qtrue;
}

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

void Use_Quad( edict_t *ent, gitem_t *item )
{
	int		timeout;

	ent->r.client->inventory[item->tag]--;
	ValidateSelectedItem(ent);

	if( quad_drop_timeout_hack )
	{
		timeout = quad_drop_timeout_hack;
		quad_drop_timeout_hack = 0;
	}
	else
	{
		timeout = 30000;
	}

	if( ent->r.client->quad_timeout > level.timemsec )
		ent->r.client->quad_timeout += timeout;
	else
		ent->r.client->quad_timeout = level.timemsec + timeout;

	G_Sound( ent, CHAN_ITEM, trap_SoundIndex(S_QUAD_USE), 1, ATTN_NORM );
}

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

void Use_Silencer( edict_t *ent, gitem_t *item )
{
	ent->r.client->inventory[item->tag]--;
	ValidateSelectedItem(ent);
	ent->r.client->silencer_shots += 30;
}

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

qboolean Add_Ammo( edict_t *ent, gitem_t *item, int count, qboolean add_it )
{
	firedef_t	*firedef;
	int			max;

	if( !ent->r.client )
		return qfalse;

	firedef = G_FiredefForAmmo( item->tag );
	if( firedef )
		max = firedef->ammo_max;
	else
		max = -1;

	if( max < 0 )
		return qfalse;

	if( ent->r.client->inventory[item->tag] >= max )
		return qfalse;

	if( add_it )
	{
		ent->r.client->inventory[item->tag] += count;
		
		if( ent->r.client->inventory[item->tag] > max )
			ent->r.client->inventory[item->tag] = max;
	}

	return qtrue;
}

qboolean Pickup_Ammo( edict_t *ent, edict_t *other )
{
	int			oldcount;
	int			count;
	qboolean	weapon;

	weapon = (ent->item->type & IT_WEAPON);

	if( (weapon) && ( dmflags->integer & DF_INFINITE_AMMO ) )
		count = 1000;
	else if( ent->count )
		count = ent->count;
	else
		count = ent->item->quantity;

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

	if( !Add_Ammo (other, ent->item, count, qtrue) )
		return qfalse;

	if( weapon && !oldcount )
	{
		if( !other->s.weapon || (other->s.weapon != ent->item->tag && other->s.weapon == WEAP_GUNBLADE) )//newgametypes
			other->r.client->latched_weapon = ent->item->tag;
	}

	if( !(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) && G_Gametype_CanRespawnItem(ent->item) )
		SetRespawn( ent, G_Gametype_RespawnTimeForItem(ent->item) );

	return qtrue;
}

void Drop_Ammo( edict_t *ent, gitem_t *item )
{
	edict_t	*dropped;
	int		index;

	index = item->tag;
	dropped = Drop_Item( ent, item );
	if( dropped ) {
		if( ent->r.client->inventory[index] >= item->quantity )
			dropped->count = item->quantity;
		else
			dropped->count = ent->r.client->inventory[index];

		ent->r.client->inventory[index] -= dropped->count;
	}
	ValidateSelectedItem(ent);
}


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

void MegaHealth_think( edict_t *self )
{
	if( HEALTH_TO_INT(self->r.owner->health) > self->r.owner->max_health )
	{
		self->nextthink = level.timemsec + G_FRAMETIME;
		//self->r.owner->health -= 1; // done in G_GameType_ClientHealthRule now
		return;
	}
	
	// player is back under max health so we can set respawn time for next MH
	if( !(self->spawnflags & DROPPED_ITEM) && G_Gametype_CanRespawnItem(self->item) )
		SetRespawn( self, G_Gametype_RespawnTimeForItem(self->item) );
	else
		G_FreeEdict(self);
}

qboolean Pickup_Health( edict_t *ent, edict_t *other )
{
	if( !(ent->style & HEALTH_IGNORE_MAX) )
		if( HEALTH_TO_INT(other->health) >= other->max_health )
			return qfalse;

	other->health += ent->item->quantity;

	if( !(ent->style & HEALTH_IGNORE_MAX) )
	{ 
		if( other->health > other->max_health )
			other->health = other->max_health;
	}
	else
	{
		if( game.gametype == GAMETYPE_MIDAIR ) {
			if( other->health > 300 )
				other->health = 300;
		} else {
			if( other->health > 200 )
				other->health = 200;
		}
	}

	if( ent->style & HEALTH_TIMED )
	{
		ent->think = MegaHealth_think;
		ent->nextthink = level.timemsec + G_FRAMETIME;
		ent->r.owner = other;
		ent->flags |= FL_RESPAWN;
		ent->r.svflags |= SVF_NOCLIENT;
		ent->r.solid = SOLID_NOT;
	}
	else
	{
		if( !(ent->spawnflags & DROPPED_ITEM) && G_Gametype_CanRespawnItem(ent->item) )
			SetRespawn( ent, G_Gametype_RespawnTimeForItem(ent->item) );
	}

	return qtrue;
}

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

#define SALVAGE_AVERAGE
qboolean Add_Armor( edict_t *ent, edict_t *other, qboolean pick_it )
{
	gclient_t		*client;
	int				player_armor_tag;
	int				item_armor_tag;
	gitem_armor_t	*item_armor_info = NULL;
	int				newarmortag = 0;
	int				newcount = 0;

	if( !other->r.client )
		return qfalse;

	client = other->r.client;

	// organize our data
	player_armor_tag = client->armortag;
	item_armor_tag = ent->item->tag;
	item_armor_info = (gitem_armor_t *)ent->item->info;

	// if player has no armor, just use it
	if( !player_armor_tag )
	{
		// handle armor shards specially
		if( ent->item->tag == ARMOR_SHARD ) {
			newarmortag = ARMOR_GA;
			newcount = ARMOR_SHARD_VALUE;
		} else {
			newarmortag = item_armor_info->armor;
			newcount = item_armor_info->base_count;
		}
	}

	//player had armor. Mix up.
	else
	{
		//find player armor infos
		gitem_armor_t	*player_armor_info = (gitem_armor_t *)game.items[player_armor_tag]->info;
		int				player_armor_count = client->armor;
		
		// handle armor shards specially
		if( ent->item->tag == ARMOR_SHARD )
		{
			//can't pick up shards if topped of RA
			if( player_armor_count >= ((gitem_armor_t *)game.items[ARMOR_RA]->info)->max_count )
				return qfalse;

			newarmortag = player_armor_tag;
			newcount = min( player_armor_count + ARMOR_SHARD_VALUE, ((gitem_armor_t *)game.items[ARMOR_RA]->info)->max_count );
		} 

		// can't pick if he's topped up of item's armor
		else if( player_armor_tag >= item_armor_tag
			&& player_armor_count >= item_armor_info->max_count ) 
		{
			return qfalse;

		} else { //player's armor is pickable

			newarmortag = item_armor_info->armor;
#ifdef SALVAGE_AVERAGE
			// convert the old armor quantity to it's quantity based in the new protection
			newcount = item_armor_info->base_count + (player_armor_count * (player_armor_info->protection / item_armor_info->protection));
#else //SALVAGE_AVERAGE
			newcount = item_armor_info->base_count + player_armor_count;
#endif //SALVAGE_AVERAGE
			if( newcount > item_armor_info->max_count )
				newcount = item_armor_info->max_count;
		}
	}

	// compute result
	if( !newarmortag || !newcount )
		return qfalse;

	if( pick_it )
	{
		//update player armor
		client->armor = newcount;
		client->armortag = newarmortag;

		// picked. Set up for respawn
		if( !(ent->spawnflags & DROPPED_ITEM) && G_Gametype_CanRespawnItem(ent->item) )//newgametypes
			SetRespawn( ent, G_Gametype_RespawnTimeForItem(ent->item) );
	}

	return qtrue;
}

qboolean Pickup_Armor( edict_t *ent, edict_t *other )
{
	return Add_Armor( ent, other, qtrue );
}

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

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

//===============
//Touch_Item
//===============
void Touch_Item( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	qboolean	taken;

	if( !other->r.client )
		return;
	if( G_IsDead(other) )
		return;		// dead people can't pickup
	if( !ent->item )
		return;

	if( !(ent->item->flags & ITFLAG_PICKABLE) )
		return;		// not a grabbable item

	if( !G_Gametype_CanPickUpItem( ent->item ) )
		return;

	taken = G_PickupItem( ent, other );
	if( taken )
	{
		// flash the screen
		G_AddPlayerStateEvent( other->r.client, PSEV_PICKUP, 0 );

		// for messages
		other->r.client->resp.last_pickup = ent;

		// show icon and name on status bar
		other->r.client->ps.stats[STAT_PICKUP_ITEM] = ent->item->tag;
		other->r.client->pickup_msg_time = level.time + 3.0;

		if (ent->item->pickup_sound)
		{
			if( ent->item->type & IT_POWERUP )
				G_Sound( other, CHAN_ITEM, trap_SoundIndex(ent->item->pickup_sound), 1, ATTN_NORM );
			else
				G_Sound( other, CHAN_AUTO, trap_SoundIndex(ent->item->pickup_sound), 1, ATTN_NORM );
		}
	}

	if( !(ent->spawnflags & ITEM_TARGETS_USED) )
	{
		G_UseTargets( ent, other );
		ent->spawnflags |= ITEM_TARGETS_USED;
	}

	if (!taken)
		return;

	// wsw: Medar: I don't really know what this does tbh :P
	if( (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) )
	{
		if( ent->flags & FL_RESPAWN )
			ent->flags &= ~FL_RESPAWN;
		else
			G_FreeEdict(ent);
	}
}

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

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

static void drop_make_touchable( edict_t *ent )
{
	int timeout;
	ent->touch = Touch_Item;
	timeout = G_Gametype_DroppedItemTimeout( ent->item );
	if( timeout )
	{
		ent->nextthink = level.timemsec + 1000 * timeout;
		ent->think = G_FreeEdict;
	}
}

edict_t *Drop_Item( edict_t *ent, gitem_t *item )
{
	edict_t	*dropped;
	vec3_t	forward, right;
	vec3_t	offset;

	if( !G_Gametype_CanDropItem(item) )
		return NULL;

	dropped = G_Spawn();
	dropped->classname = item->classname;
	dropped->item = item;
	dropped->spawnflags = DROPPED_ITEM;
	VectorCopy( item_box_mins, dropped->r.mins );
	VectorCopy( item_box_maxs, dropped->r.maxs );
	dropped->r.solid = SOLID_TRIGGER;
	dropped->movetype = MOVETYPE_TOSS;  
	dropped->touch = drop_temp_touch;
	dropped->r.owner = ent;
	dropped->s.team = ent->s.team;	// wsw : jal : newgametypes
	dropped->s.type = ET_ITEM;			// wsw : jal : simpleitems
	dropped->s.skinnum = item->tag;
	dropped->s.effects = 0;	// default effects are applied client side
	dropped->s.renderfx = 0;
	dropped->s.modelindex = trap_ModelIndex( dropped->item->world_model[0] );
	dropped->s.modelindex2 = trap_ModelIndex( dropped->item->world_model[1] );

	if( ent->r.client )
	{
		trace_t	trace;

		AngleVectors( ent->r.client->v_angle, forward, right, NULL );
		VectorSet( offset, 24, 0, -16 );
		G_ProjectSource( ent->s.origin, offset, forward, right, dropped->s.origin );
		trap_Trace (&trace, ent->s.origin, dropped->r.mins, dropped->r.maxs,
			dropped->s.origin, ent, CONTENTS_SOLID);
		VectorCopy( trace.endpos, dropped->s.origin );
	}
	else
	{
		AngleVectors( ent->s.angles, forward, right, NULL );
		VectorCopy( ent->s.origin, dropped->s.origin );
	}

	VectorScale( forward, 100, dropped->velocity );
	dropped->velocity[2] = 300;

	dropped->think = drop_make_touchable;
	dropped->nextthink = level.timemsec + 1000;

	ent->r.client->resp.last_drop_item = item;
	VectorCopy( dropped->s.origin, ent->r.client->resp.last_drop_location );

	trap_LinkEntity( dropped );

	return dropped;
}

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

//================
//G_PickupItem
//================
qboolean G_PickupItem( edict_t *ent, edict_t *other )
{
	gitem_t	*it;

	if( !ent || !other )
		return qfalse;

	if( !ent->item || !(ent->item->flags & ITFLAG_PICKABLE) )
		return qfalse;

	it = ent->item;

	if( it->type & IT_WEAPON ) {
		return Pickup_Weapon( ent, other );
	}
	else if( it->type & IT_AMMO ) {
		if( !Q_stricmp(it->classname, "item_ammopack") )
			return Pickup_AmmoPack( ent, other );
		else
			return Pickup_Ammo( ent, other );
	}
	else if( it->type & IT_ARMOR ) {
		return Pickup_Armor( ent, other );
	}
	else if( it->type & IT_HEALTH ) {
		return Pickup_Health( ent, other );
	}
	else if( it->type & IT_POWERUP ) {
		return Pickup_Powerup( ent, other );
	}
	else if( it->type & IT_FLAG ) {
		return G_Gametype_CTF_Pickup_Flag( ent, other );
	}

	return qfalse;
}

//================
//G_DropItem
//================
void G_DropItem( edict_t *ent, gitem_t *it )
{
	if( !it || !(it->flags & ITFLAG_DROPABLE) )
		return;

	if( !G_Gametype_CanDropItem(it) )
		return;

	if( it->type & IT_WEAPON ) {
		Drop_Weapon( ent, it );
	} 
	else if( it->type & IT_AMMO ) {
		Drop_Ammo( ent, it );
	} 
	else if( it->type & IT_FLAG ) {
		G_Gametype_CTF_Drop_Flag( ent, it );
	}
	else {
		Drop_General( ent, it );
	}
}

//================
//G_UseItem
//================
void G_UseItem( edict_t *ent, gitem_t *it )
{
	if( !it || !(it->flags & ITFLAG_USABLE) )
		return;

	if( it->type & IT_WEAPON ) {
		Use_Weapon( ent, it );
	}
	else if( it->type & IT_POWERUP ) {
		if( it->tag == POWERUP_QUAD )
			Use_Quad( ent, it );
	}
}

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


//================
//Finish_SpawningItem
//================
void Finish_SpawningItem( edict_t *ent )
{
	trace_t		tr;
	vec3_t		dest;

	VectorCopy( item_box_mins, ent->r.mins );
	VectorCopy( item_box_maxs, ent->r.maxs );

	if( ent->model ) {
		ent->s.modelindex = trap_ModelIndex( ent->model );
	} else {
		if( ent->item->world_model[0] )
			ent->s.modelindex = trap_ModelIndex( ent->item->world_model[0] );
		if( ent->item->world_model[1] )
			ent->s.modelindex2 = trap_ModelIndex( ent->item->world_model[1] );
	}

	ent->s.renderfx = 0;
	ent->r.solid = SOLID_TRIGGER;
	ent->movetype = MOVETYPE_TOSS;  
	ent->touch = Touch_Item;

	if( !(ent->spawnflags & 1) ) {	// droptofloor
		// see if they start in a solid position
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, ent->s.origin, ent, MASK_SOLID );
		if( tr.startsolid ) {
			// move it 1 unit up, cause it's typical they share the leaf with the floor
			VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] + 1 );
			trap_Trace( &tr, dest, ent->r.mins, ent->r.maxs, ent->s.origin, ent, MASK_SOLID );
			if( tr.startsolid ) {
				if( developer->integer )
					G_Printf( "droptofloor(1): %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );
				G_FreeEdict( ent );
				return;
			}
			VectorCopy( dest, ent->s.origin );
		}
		

		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 128 );
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent, MASK_SOLID );
		if( tr.startsolid ) {
			if( developer->integer )
				G_Printf( "droptofloor: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );
			G_FreeEdict( ent );
			return;
		}

		VectorCopy( tr.endpos, ent->s.origin );
	} else {
		ent->gravity = 0;
	}

	if( ent->team ) {
		ent->flags &= ~FL_TEAMSLAVE;
		ent->chain = ent->teamchain;
		ent->teamchain = NULL;

		ent->r.svflags |= SVF_NOCLIENT;
		ent->r.solid = SOLID_NOT;

		if( ent == ent->teammaster ) {
			ent->nextthink = level.timemsec + game.framemsec;
			ent->think = DoRespawn;
		}
	}

	trap_LinkEntity(ent);
}


//===============
//PrecacheItem
//
//Precaches all data needed for a given item.
//This will be called for each item spawned in a level,
//and for each item in each client's inventory.
//===============
void PrecacheItem( gitem_t *it )
{
	int i;
	char	*s, *start;
	char	data[MAX_QPATH];
	int		len;
	gitem_t	*ammo;

	if( !it )
		return;

	if( it->pickup_sound )
		trap_SoundIndex( it->pickup_sound );
	for ( i = 0; i < MAX_ITEM_MODELS; i++ ) {
		if( it->world_model[i] )
			trap_ModelIndex( it->world_model[i] );
	}

	if( it->icon )
		trap_ImageIndex( it->icon );

	// parse everything for its ammo
	if( it->ammo_tag )
	{
		ammo = game.items[it->ammo_tag];
		if( ammo != it )
			PrecacheItem( ammo );
	}

	// parse the space separated precache string for other items
	for( i = 0; i < 3; i++ )
	{
		if( i == 0 )
			s = it->precache_models;
		else if( i == 1 )
			s = it->precache_sounds;
		else
			s = it->precache_images;

		if( !s || !s[0] )
			continue;

		while( *s )
		{
			start = s;
			while( *s && *s != ' ' )
				s++;

			len = s-start;
			if( len >= MAX_QPATH || len < 5 )
				G_Error( "PrecacheItem: %s has bad precache string", it->classname );
			memcpy( data, start, len );
			data[len] = 0;
			if( *s )
				s++;

			if( i == 0 )
				trap_ModelIndex( data );
			else if (i == 1)
				trap_SoundIndex( data );
			else
				trap_ImageIndex( data );
		}
	}
}

//============
//SpawnItem
//
//Sets the clipping size and plants the object on the floor.
//
//Items can't be immediately dropped to floor, because they might
//be on an entity that hasn't spawned yet.
//============
void SpawnItem( edict_t *ent, gitem_t *item )
{
	// wsw : jal : set items as ET_ITEM for simpleitems
	ent->s.type = ET_ITEM;
	ent->s.skinnum = item->tag;
	ent->item = item;
	ent->nextthink = level.timemsec + 2 * game.framemsec;    // items start after other solids
	ent->think = Finish_SpawningItem;
	ent->s.effects = 0; 	// default effects are applied client side

	// flags are server animated and have special handling
	if( item->type & IT_FLAG ) {
		ent->think = G_Gametype_CTF_FlagSetup;
	}

	if( item->type & IT_HEALTH )
	{
		if( item->tag == HEALTH_SMALL )
			ent->style = HEALTH_IGNORE_MAX;
		else if( item->tag == HEALTH_MEGA )
			ent->style = HEALTH_IGNORE_MAX|HEALTH_TIMED;
	}
}

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

//===============
//InitItems
//
//Validate item tags and sort by tag into game.items list
//===============
void InitItems( void )
{
	int	tag, count;
	gitem_t	*item;
	int	num_items;

	// count items
	for( num_items = 1, item = &itemdefs[1]; item->classname; item++, num_items++ ) {
	}

	//clear
	for( tag = 0; tag < MAX_ITEMS; tag++ )	//zero will always be NULL
		game.items[tag] = NULL;
	
	// store item with tags in the array at position of tag value
	for( tag = 1, count = 0; tag < num_items; tag++ )
	{
		game.items[tag] = NULL;
		for( item = &itemdefs[1]; item->classname; item++ )
		{
			if( item->tag == tag ) {

				// found the same tag value twice
				if( game.items[tag] != NULL )
					G_Error( "InitItems: Found two different items with the same tag value: %s -> %s ", game.items[tag]->classname, item->classname );

				game.items[tag] = item;
				count++;
				//break;
			}
		}
	}

	//find the first with tag being zero
	for( item = &itemdefs[1]; item->classname; item++ ) {
		if( !item->tag )
			break;
	}

	//asign tag values to items without tags
	while( item->classname )
	{
		for( item = &itemdefs[1]; item->classname; item++ )
		{
			if( !item->tag ) 
			{
				//find a free spot for it
				for( tag = 1; tag < MAX_ITEMS; tag++ ) {
					if( !game.items[tag] )
						break;
				}
				if( tag == MAX_ITEMS )
					G_Error( "InitItems: Couldn't find a free spot into game.items array for %s\n", item->classname );

				item->tag = tag;
				game.items[tag] = item; //put it on the list

				if( developer->value )
					Com_Printf("WARNING: InitItems found item '%s' without a tag value. Assigned: %i\n", item->classname, tag );
				
				count++;
				break;
			}
		}
	}

	game.numItems = count + 1; //item zero was not counted
	Com_Printf("Initialized %i Items\n", count );

	// Init armor items
	game.items[ARMOR_GA]->info = (gitem_armor_t *)&g_armor_info;
	game.items[ARMOR_YA]->info = (gitem_armor_t *)&y_armor_info;
	game.items[ARMOR_RA]->info = (gitem_armor_t *)&r_armor_info;
	
	// Init weapon items
	G_InitWeapons();
}

//===============
//SetItemNames
//
//Called by worldspawn
//===============
void SetItemNames( void )
{
	int		i;

	for( i = 0; i < game.numItems; i++ ) {
		if( game.items[i] )
			trap_ConfigString( CS_ITEMS+i, game.items[i]->pickup_name );
	}
}
