#include "game.h"

extern int	DemoPlay;

	sint16		next_item_free;
	sint16		next_item_active;
	sint16		body_bag;

	FX_INFO		*effects;
	sint16		next_fx_free;
	sint16		next_fx_active;

	int level_items=0;

#if defined(PC_VERSION) && defined(GAMEDEBUG)
LEVEL_INFO level;
#endif

/********************************************************************
 *				Initialise Item Array for Loading
 *******************************************************************/
void	InitialiseItemArray( int numitems )
{
	int			i;
	ITEM_INFO	*item;

	body_bag = NO_ITEM;
	next_item_active = NO_ITEM;		// No Items Active Yet
	next_item_free = level_items;	// initialise Free Links
	item = &items[level_items];
	for ( i=level_items+1; i<numitems; i++,item++ )
	{
		item->next_item = i;
		item->active = 0;
		item->il.init = 0;
	}
	item->next_item = NO_ITEM;

#if defined(PC_VERSION) && defined(GAMEDEBUG)
	level.hit_points = level.health = level.ammo = level.health_used = level.ammo_used = 0;
#endif
}


/********************************************************************
 *		Remove Item from  Rooms ITEM list
 *			and free its structure space for another effect
 * NOTE doesnt free *data in ITEM_INFO
 *******************************************************************/
void	KillItem( sint16 item_num )
{
	sint16		linknum;
	ITEM_INFO	*item;

	DetatchSpark(item_num,SP_ITEM);

	item = &items[ item_num ];
	item->active = 0;
	linknum = next_item_active;							// First Remove from
	if ( linknum==item_num )                            // active list if necessary
		next_item_active = item->next_active;
	else
	{
		for  ( ; linknum!=NO_ITEM; linknum=items[linknum].next_active )
		{
			if ( items[linknum].next_active==item_num )
			{
				items[linknum].next_active = item->next_active;
				break;
			}
		}
	}

	if (item->room_number != NO_ROOM)
	{
		linknum = room[ item->room_number ].item_number;	// Remove Item from Draw list
		if ( linknum==item_num )
			room[item->room_number].item_number = item->next_item;
		else
		{
			for ( ; linknum!=NO_ITEM; linknum=items[linknum].next_item )
			{
				if ( items[linknum].next_item==item_num )
				{
					items[linknum].next_item = item->next_item;
					break;
				}
			}
		}
	}

	if ( item==lara.target ) 			// Make sure Lara cant target things
		lara.target = NULL;             // that have disappeared from GameWorld
	if (item_num < level_items)
	{
		/* Cannot reuse loaded item slots, but need to mark item as destroyed for save game */
		item->flags |= KILLED_ITEM;
	}
	else
	{
		item->next_item = next_item_free;					// Insert into Free Link...
		next_item_free = item_num;
	}
}

/********************************************************************
 *				Create an Item( of object_number type ).
 *			NOTE. does not link structure to any game links...
 *			use InitialiseItem for this...
 *******************************************************************/
sint16	CreateItem( void )
{
	sint16		item_num;

	if ( (item_num = next_item_free)==NO_ITEM )		// Get Item from Free List
	{
#ifdef GAMEDEBUG
		wsprintf(exit_message,"Item list is Full\n");
#endif
		return( item_num );
	}
	items[item_num].flags = 0;
	items[item_num].il.init = 0;
	next_item_free = items[item_num].next_item;		// Unlink from Free List
	return( item_num );
}

/********************************************************************
 *			Initialise Individual Item...
 * 		This Assumes Position, Object_Number and Room_number, flags
 *					have all been set up..
 *******************************************************************/
void	InitialiseItem( sint16	item_num )
{
	ITEM_INFO	*item;
	ROOM_INFO	*r;
	FLOOR_INFO	*floor;

	item = &items[item_num];
	item->anim_number = objects[item->object_number].anim_index;
	item->frame_number = anims[item->anim_number].frame_base;
	item->current_anim_state = item->goal_anim_state = anims[item->anim_number].current_anim_state;
	item->required_anim_state = 0;
	item->pos.x_rot = item->pos.z_rot = 0;      // These are defaults and can be changed by
	item->speed = item->fallspeed = 0;			// Specific initialisation routine
	item->status = NOT_ACTIVE;
	item->active = 0;
	item->gravity_status = item->hit_status = item->looked_at = 0;
	item->hit_points = objects[item->object_number].hit_points;
	item->collidable = 1;                       // Make all items collidable for now
	item->clear_body = 0;
	item->timer = 0;
	item->mesh_bits = 0xffffffff; // All meshes visible
	item->touch_bits = 0;
	item->after_death = 0;
	item->il.init = 0;
	item->data = NULL;

	/* Set INVISIBLE status if required */
	if (item->flags & NOT_VISIBLE)
	{
		item->status = INVISIBLE;
		item->flags -= NOT_VISIBLE;
	}
	else if (objects[item->object_number].intelligent) // 20/9/97: make all unactivated baddies INVISIBLE by default
		item->status = INVISIBLE;

	/* Set clear body flag */
	if (item->flags & CLEAR_BODY)
	{
		item->clear_body = 1;
		item->flags -= CLEAR_BODY;
	}

	/* Set REVERSE bit if all CODE_BITS set and add to ActiveItem list */
	if ((item->flags & CODE_BITS) == CODE_BITS)
	{
		item->flags -= CODE_BITS;
	   item->flags |= REVERSE;
//	   AddActiveItem(item_num);
//	   item->status = ACTIVE;
	}

#if defined(GAMEDEBUG) && defined(PC_VERSION)
	// Log some stats for this level
	if (item->hit_points>0)
		level.hit_points += item->hit_points;
	switch (item->object_number)
	{
	case MEDI_ITEM: level.health += 500; break;
	case BIGMEDI_ITEM: level.health += 1000; break;
	case SG_AMMO_ITEM: level.ammo += NUM_SG_SHELLS * 18; break;
	case MAG_AMMO_ITEM: level.ammo += MAGNUM_AMMO_QTY * 2; break;
	case UZI_AMMO_ITEM: level.ammo += UZI_AMMO_QTY; break;
	case M16_AMMO_ITEM: level.ammo += M16_AMMO_QTY * 3; break;
	case ROCKET_AMMO_ITEM: level.ammo += 30; break;
	}
#endif

	r = &room[item->room_number];				// Add Item to the
	item->next_item = r->item_number;           // Rooms Draw List
	r->item_number = item_num;
	floor = &r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size];
	item->floor = floor->floor << 8;

	if ( savegame.bonus_flag && !DemoPlay)		// Bonus increase in baddie hitpoints!!! (EXCEPT FOR DEMOS)
		item->hit_points *= 2;
	/* Setup initial data in object specific structures */
	if (objects[item->object_number].initialise)
		(*objects[item->object_number].initialise)(item_num);
}


/********************************************************************
 *			Remove Item From Active List
 *******************************************************************/
void	RemoveActiveItem( sint16 item_num )
{
	sint16		linknum;

	if ( items[item_num].active==0 )
	{
#ifdef GAMEDEBUG
		S_ExitSystem("Item already deactive\n");
#endif
		return;
	}

	items[item_num].active = 0;
	linknum = next_item_active;
	if ( linknum==item_num )              // Remove from active link-chain
	{
		next_item_active = items[item_num].next_active;
		return;
	}

	for  ( ; linknum!=NO_ITEM; linknum=items[linknum].next_active )
	{
		if ( items[linknum].next_active==item_num )
		{
			items[linknum].next_active = items[item_num].next_active;
			return;
		}
	}
}

/*********************************************************************
 *			Remove Item From Draw List ( IE When picked Up!! )
 ********************************************************************/
void	RemoveDrawnItem( sint16 item_num )
{
	sint16 		linknum;
	ITEM_INFO 	*item;


	item = &items[item_num];

	linknum = room[item->room_number].item_number;						// Unlink From Current Room
	if ( linknum==item_num )
		room[item->room_number].item_number = item->next_item;
	else
	{
		for ( ; linknum!=NO_ITEM; linknum=items[linknum].next_item )
		{
			if ( items[linknum].next_item==item_num )
			{
				items[linknum].next_item = item->next_item;
				return;
			}
		}
	}
}


/********************************************************************
 *			Add Item to Active List
 *******************************************************************/
void	AddActiveItem( sint16 item_num )
{
	ITEM_INFO *item;

	item = &items[item_num];

	/* NULL control routine implies we don't want this item activated */
	if (objects[item->object_number].control == NULL)
	{
		item->status = NOT_ACTIVE;
		return;
	}

	if ( item->active==1 )
	{
#ifdef GAMEDEBUG
		wsprintf( exit_message,"Item(%d)(Obj%d) already Active\n", item_num, item->object_number);
		S_ExitSystem(exit_message);
#endif
		return;
	}
	item->active = 1;
//	item->il.init = 0;
	item->next_active = next_item_active;
	next_item_active = item_num;
}


/********************************************************************
 *			Move Item to A New Room... adjusts Linked Lists...
 *******************************************************************/
void	ItemNewRoom(sint16 item_num, sint16 room_number)
{
	sint16 		linknum;
	ROOM_INFO 	*r;
	ITEM_INFO 	*item;

	item = &items[item_num];

	if (item->room_number != NO_ROOM)				// pickups may be carried, so unattached to room till dropped
	{
		r = &room[item->room_number];

		linknum = r->item_number;						// Unlink From Current Room
		if ( linknum==item_num )
			r->item_number = item->next_item;
		else
		{
			for ( ; linknum!=NO_ITEM; linknum=items[linknum].next_item )
			{
				if ( items[linknum].next_item==item_num )
				{
					items[linknum].next_item = item->next_item;
					break;
				}
			}
		}
	}

	item->room_number = room_number;				// Link To New Room
	r = &room[room_number];
	item->next_item = r->item_number;
	r->item_number = item_num;
}

/********************************************************************
 *		Spawn an Item at another items position
 *******************************************************************/
sint16	SpawnItem( ITEM_INFO *item, int object_number )
{
	ITEM_INFO	*spawn;
	sint16		spawn_num;

	spawn_num = CreateItem();
	if ( spawn_num!=NO_ITEM )
	{
		spawn = &items[spawn_num];

		spawn->object_number = object_number;
		spawn->room_number = item->room_number;
		S_MemCpy( &spawn->pos, &item->pos, sizeof(PHD_3DPOS) );
		InitialiseItem( spawn_num );
		spawn->status = NOT_ACTIVE;
      spawn->shade = 16*256;
	}

	return(spawn_num);
}

/******************************************************************************
 *			Replace all occurrences of items of Object Type IN
 *				with objects of type OUT
 *					returns number changed!!!
 *****************************************************************************/
int		GlobalItemReplace( int in_objnum, int out_objnum )
{
	ROOM_INFO	*r;
	int			i,changed;
	sint16		item_num;

	changed = 0;
	r = room;
	for ( i=0; i<number_rooms; i++,r++ )
	{
		for (item_num = r->item_number;item_num!=NO_ITEM; item_num=items[item_num].next_item )
		{
			if ( items[item_num].object_number==in_objnum )
			{
				items[item_num].object_number = out_objnum;
				changed++;
			}
		}
	}
	return( changed );
}


/********************************************************************
 ********************************************************************
 *				Initialise Effects Array for Loading
 ********************************************************************
 *******************************************************************/
void	InitialiseFXArray( void )
{
	int			i;
	FX_INFO		*fx;

	next_fx_active = NO_ITEM;		// No Items Active Yet
	next_fx_free = 0;				// initialise Free Links
	fx = effects;
	for ( i=1; i<NUM_EFFECTS; i++,fx++ )
		fx->next_fx = i;
	fx->next_fx = NO_ITEM;
}

/********************************************************************
 *		Create an Effect and Links to Draw and Control Lists
 *******************************************************************/
sint16	CreateEffect( sint16 room_num )
{
	ROOM_INFO	*r;
   	FX_INFO		*fx;
	sint16		fx_num;

	if ( (fx_num = next_fx_free)==NO_ITEM )		// Get Item from Free List
	{
#ifdef GAMEDEBUG
		wsprintf(exit_message,"Effect List is Full Room:%d\n",room_num );
#endif
//		printf("Effect List is Full\n");
		return( fx_num );
	}
	fx = &effects[fx_num];
	next_fx_free = fx->next_fx;					// Unlink From Free List

	r = &room[room_num];		  	  			// Add Effect to Rooms Draw List
	fx->room_number = room_num;
	fx->next_fx = r->fx_number;
	r->fx_number = fx_num;

	fx->next_active = next_fx_active;	  		// Add Effect to Active List
	next_fx_active = fx_num;

	// fill out default shade (as older routines don't set this for sprites)
	fx->shade = 16*256;

	return( fx_num );
}

/********************************************************************
 *		Remove Effect from  Rooms FX list And Active FX List
 *			and free its structure space for another effect
 *******************************************************************/
void	KillEffect( sint16 fx_num )
{
	sint16		linknum;
	FX_INFO		*fx;

	DetatchSpark(fx_num,SP_FX);

	fx = &effects[ fx_num ];
	linknum = next_fx_active;							// First Remove from
	if ( linknum==fx_num )                            	// active list if necessary
		next_fx_active = fx->next_active;
	else
	{
		for  ( ; linknum!=NO_ITEM; linknum=effects[linknum].next_active )
		{
			if ( effects[linknum].next_active==fx_num )
			{
				effects[linknum].next_active = fx->next_active;
				break;
			}
		}
	}

	linknum = room[ fx->room_number ].fx_number;		// Remove Item from Draw list
	if ( linknum==fx_num )
		room[fx->room_number].fx_number = fx->next_fx;
	else
	{
		for ( ; linknum!=NO_ITEM; linknum=effects[linknum].next_fx )
		{
			if ( effects[linknum].next_fx==fx_num )
			{
				effects[linknum].next_fx = fx->next_fx;
				break;
			}
		}
	}

	fx->next_fx = next_fx_free;					// Insert into Free Link...
	next_fx_free = fx_num;
}

/********************************************************************
 *			Move Effect to A New Room... adjusts Linked Lists...
 *******************************************************************/
void	EffectNewRoom(sint16 fx_num, sint16 room_number)
{
	sint16 		linknum;
	ROOM_INFO 	*r;
	FX_INFO 	*fx;


	fx = &effects[fx_num];
	r = &room[fx->room_number];

	linknum = r->fx_number;						// Unlink From Current Room
	if ( linknum==fx_num )
		r->fx_number = fx->next_fx;
	else
	{
		for ( ; linknum!=NO_ITEM; linknum=effects[linknum].next_fx )
		{
			if ( effects[linknum].next_fx==fx_num )
			{
				effects[linknum].next_fx = fx->next_fx;
				break;
			}
		}
	}
	fx->room_number = room_number;				// Link To New Room
	r = &room[room_number];
	fx->next_fx = r->fx_number;
	r->fx_number = fx_num;
}


void ClearBodyBag(void)
{
	/* Clear away bodies that need clearing */
	sint16 item_number;
	ITEM_INFO *item;

	if (body_bag == NO_ITEM)
		return;

	item_number = body_bag;
	while (item_number != NO_ITEM)
	{
		item = &items[item_number];

		/* Clear item away */
		KillItem(item_number);
		item_number = item->next_active;
		item->next_active = NO_ITEM;
	}
	body_bag = NO_ITEM;
}
