/***********************************************************************************************************/
/*                                                                                                         */
/* LOT Stuff                                                                               G.Rummery 1997 */
/*                                                                                                         */
/***********************************************************************************************************/

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#ifdef PSX_VERSION
#include "../spec_psx/typedefs.h"
#include "../spec_psx/malloc.h"
#else
#include "../specific/standard.h"
#include "../specific/global.h"
#endif

#include "objects.h"
#include "lara.h"
#include "camera.h"
#include "gameflow.h"
#include "lot.h"

/*---------------------------------------------------------------------------
 *	Externals
\*--------------------------------------------------------------------------*/

extern int GnGameMode;

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

int slots_used=0;
int nonlot_slots_used=0;
CREATURE_INFO	*baddie_slots;
CREATURE_INFO	*non_lot_slots;

/******************************************************************************
 *				Intialise LOTs for Creatures
 *		Do this after weve loaded level so we know size of LOT
 *****************************************************************************/
void	InitialiseLOTarray( void )
{
	int i;
	CREATURE_INFO	*creature;

	baddie_slots = (CREATURE_INFO *)game_malloc( NUM_SLOTS*sizeof(CREATURE_INFO), CREATURE_DATA );
	creature = baddie_slots;

	for ( i=0; i<NUM_SLOTS; i++,creature++ )
	{
		creature->item_num = NO_ITEM;
		creature->LOT.node = (BOX_NODE *)game_malloc( sizeof(BOX_NODE)*number_boxes, CREATURE_LOT );
	}
	slots_used = 0;

	non_lot_slots = (CREATURE_INFO *)game_malloc( NUM_NONLOT_SLOTS*sizeof(CREATURE_INFO), CREATURE_DATA );
	creature = non_lot_slots;

	for ( i=0; i<NUM_NONLOT_SLOTS; i++,creature++ )
	{
		creature->item_num = NO_ITEM;
	}
	nonlot_slots_used = 0;

}

/******************************************************************************
 *		Pauses a given Baddie by removing its Creature Structure
 *   	 It is up to Calling routine to change status of Baddie
 *         		To NOT_ACTIVE,INVISIBLE or DEACTIVATED etc
 *****************************************************************************/
void	DisableBaddieAI( sint16 item_num )
{
	ITEM_INFO		*item;
	CREATURE_INFO	*creature;

#ifdef LOGFILE
//	fprintf( logfile,"Item%d DisableBaddieAI()\n",item_num );
#endif
	item = &items[item_num];

	if (item_num==lara.item_number)
	{
		creature = lara.creature;
		lara.creature = NULL;
	}
	else
	{
		creature = (CREATURE_INFO *)item->data;
		item->data = NULL;
	}

	if ( creature )
	{
		creature->item_num = NO_ITEM;
		if (objects[item->object_number].non_lot)
			nonlot_slots_used--;
		else
			slots_used--;
	}
}

/******************************************************************************
 *			 Enable new Baddie AI structures etc
 *   		Returns TRUE if Baddie now active
 *		Goes through list finding Either Free slot or furthest baddie from
 *         Camera. if Always set then uses furthest baddie else
 *          compares distance to see if should add this Baddie
 *****************************************************************************/
int		EnableBaddieAI( sint16 item_number, int Always )
{
	int				x,y,z;
	int				worstslot,slot;
	int				worstdist,dist;
	ITEM_INFO		*item;
	CREATURE_INFO	*creature;


#ifdef LOGFILE
//	fprintf( logfile,"Item%d EnableBaddieAI()\n",item_number );
#endif
	/* Return now if Baddie already got AI */
	if (lara.item_number==item_number)
	{
		if (lara.creature)
			return 1;
	}
	else if (items[item_number].data)
		return 1;

	/* TS - Hand over control to EnableNonLotAI if this is a non-LOT creature */
	else if (objects[items[item_number].object_number].non_lot)
	{
		return EnableNonLotAI(item_number, Always);
	}

	/* If any slots are free use one */
	if ( slots_used<NUM_SLOTS )
	{
		creature = baddie_slots;
		for ( slot=0;slot<NUM_SLOTS;slot++,creature++ )
		{
			if ( creature->item_num==NO_ITEM )
			{
				InitialiseSlot( item_number,slot );
				return(1);
			}
		}
//		S_ExitSystem("UnpauseBaddie() grimmer!");
	}

	if ( !Always ) 										// Get Distance at above which
	{                                                   // we swap creatures
		item = &items[item_number];
		x = (item->pos.x_pos - camera.pos.x)>>8;
		y = (item->pos.y_pos - camera.pos.y)>>8;
		z = (item->pos.z_pos - camera.pos.z)>>8;
		worstdist = (x*x) + (y*y) + (z*z);
	}
	else
		worstdist = 0;


	worstslot = -1;
	creature = baddie_slots;
	for ( slot=0;slot<NUM_SLOTS;slot++,creature++ )
	{
		item = &items[creature->item_num];
		x = (item->pos.x_pos - camera.pos.x)>>8;
		y = (item->pos.y_pos - camera.pos.y)>>8;
		z = (item->pos.z_pos - camera.pos.z)>>8;
		dist = (x*x) + (y*y) + (z*z);
		if ( dist>worstdist )
		{
			worstdist = dist;
			worstslot = slot;
		}
	}

	/* Convert Baddie on worst slot to INVISIBLE and take over slot */
	if ( worstslot>=0  )
	{
		items[baddie_slots[worstslot].item_num].status = INVISIBLE;
		DisableBaddieAI( baddie_slots[worstslot].item_num );
		InitialiseSlot( item_number,worstslot );
		return(1);
	}
	return(0);
}


/******************************************************************************
 *			Initialise Creature and LOT for new ACTIVE Baddie
 *****************************************************************************/

void	InitialiseSlot( sint16 item_number, int slot )
{
	CREATURE_INFO	*creature;
	ITEM_INFO		*item;
	int i;

	creature = &baddie_slots[slot];
	item = &items[item_number];

	if (item_number == lara.item_number)
		lara.creature = creature;
	else
		item->data = (void *)creature;

	creature->item_num = item_number;
	creature->mood = BORED_MOOD;
	for(i=0; i<4; i++)
		creature->joint_rotation[i] = 0;
	creature->maximum_turn = ONE_DEGREE;
	creature->flags = 0;
	creature->enemy = NULL;
	creature->alerted = 0;
	creature->head_left = 0;
	creature->head_right = 0;
	creature->reached_goal = 0;
	creature->hurt_by_lara = 0;
	creature->patrol2 = 0;


	/* Default settings for LOT */
	creature->LOT.step = STEP_L;
	creature->LOT.drop = -STEP_L*2;
	creature->LOT.block_mask = BLOCKED;
	creature->LOT.fly = NO_FLYING;

	switch ( item->object_number )
	{
	case LARA:
		/* Water current control */
		creature->LOT.step = WALL_L*20;
		creature->LOT.drop = -WALL_L*20;
		creature->LOT.fly = STEP_L;
		break;

	case DIVER:
	case WHALE:
	case VULTURE:
	case CROW:
	case MUTANT1:
		/* Swimming or flying baddie */
		creature->LOT.step = WALL_L*20;
		creature->LOT.drop = -WALL_L*20;
		creature->LOT.fly = STEP_L/16;
		if (item->object_number == WHALE)
			creature->LOT.block_mask = BLOCKABLE; // they cannot pass over splitters ever
		break;

	// NOTE: if baddies added here, change code in LoadBoxes() in file.c to load ground_zone
//	case SPIDER:
//		/* Baddies that can go up/down half blocks */
//		creature->LOT.step = STEP_L*2;
//		creature->LOT.drop = -WALL_L;
//		break;

	case LIZARD_MAN:
	case BOB:
	case PUNK1:
	case CIVVIE:
	case MONKEY:
	case MP1:
	case WILLARD_BOSS:			// ## Remove this - TS!
		/* Baddies that can do a full block climb */
		creature->LOT.step = WALL_L;
		creature->LOT.drop = -WALL_L;
		creature->LOT.block_mask = BLOCKABLE; //## TODO: remove this complete hack used for the 25/8/98 demo!!!
		break;
	case MP2:
	case SWAT_GUN:
		creature->LOT.block_mask = BLOCKABLE; //## TODO: remove this complete hack used for the 25/8/98 demo!!!
	case TREX:
		creature->LOT.block_mask = BLOCKABLE;
		break;
	}

	ClearLOT(&creature->LOT);

	if (item_number != lara.item_number)
		CreateZone(item);

	slots_used++;
}


void CreateZone(ITEM_INFO *item)
{
	/* Create list of all the boxes in this creature's zone (in both normal and flipped map) - allows quicker reactions */
	int i;
	CREATURE_INFO *creature;
	BOX_NODE *node;
	sint16 *zone, zone_number;
	sint16 *flip, flip_number;
	ROOM_INFO *r;

	creature = (CREATURE_INFO *)item->data;

	if (creature->LOT.fly == NO_FLYING)
	{
		zone = ground_zone[ZONE(creature->LOT.step)][0];
		flip = ground_zone[ZONE(creature->LOT.step)][1];
	}
	else
	{
		zone = fly_zone[0];
		flip = fly_zone[1];
	}

	/* Basic information about baddie */
	r = &room[item->room_number];
	item->box_number = r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) +
							 ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].box;

	zone_number = zone[item->box_number];
	flip_number = flip[item->box_number];

	for (i=0, creature->LOT.zone_count=0, node=creature->LOT.node; i<number_boxes; i++, zone++, flip++)
		if (*zone == zone_number || *flip == flip_number)
		{
			node->box_number = i;
			node++;
			creature->LOT.zone_count++;
		}

}


/*******************************************************************************
 *			Initialise A LOT only used by LARA at moment...
 ******************************************************************************/
#ifdef OLD_STUFF
int InitialiseLOT(LOT_INFO *LOT)
{
	/* Allocate and clear LOT table for individual beasties */
	LOT->node = (BOX_NODE *)game_malloc(sizeof(BOX_NODE) * number_boxes, CREATURE_LOT);
	ClearLOT(LOT);
	return (1);
}
#endif

/*******************************************************************************
 *					Clear LOT ready for Fresh usage
 ******************************************************************************/
void ClearLOT(LOT_INFO *LOT)
{
	/* Reset LOT */
	int i;
	BOX_NODE *node;

	LOT->head = LOT->tail = NO_BOX;
	LOT->search_number = 0;
	LOT->target_box = NO_BOX;
	LOT->required_box = NO_BOX;

	/* Clear LOT */
	for (i=0, node=LOT->node; i<number_boxes; i++, node++)
	{
		node->exit_box = node->next_expansion = NO_BOX;
		node->search_number = 0;
	}
}

#ifdef PSX_VERSION
/*******************************************************************************
 *		Dude's handy idle time recycler...  Update some LOTs while we wait.
 ******************************************************************************/
int OnIdleLOTUpdate(int (*pfnIdleCheckRoutine)(void))
	{
	static int nSlot=0;			// keep this so that LOT[0] isn't updated more than LOT[4]
	CREATURE_INFO* pBaddie;
	int i,nNeededUpdate;

	if (GnGameMode!=GAMEMODE_IN_GAME)	// don't do this if in demo mode, because it will affect creature behaviour
		return (*pfnIdleCheckRoutine)();	// but still return idle status

	do
	{
		nNeededUpdate=0;
		pBaddie=baddie_slots+nSlot;
		for (i=NUM_SLOTS;i;--i)				// check all slots
		{
			if (pBaddie->item_num!=NO_ITEM)	// if this baddie is active...
				nNeededUpdate+=UpdateLOT(&(pBaddie->LOT),1);	// update its LOT a bit
			++pBaddie;
			if (++nSlot==NUM_SLOTS)			// move to next slot
			{
				nSlot=0;
				pBaddie=baddie_slots;
			}
			if (!(*pfnIdleCheckRoutine)())	// return straight away if no longer idle
				return FALSE;				// false means non-idle event has occurred
		}
	}
	while (nNeededUpdate!=0);				// loop until all LOTs are fully expanded
	return TRUE;							// still idle!
	}
#endif

/*******************************************************************************
 * TS 16-7-98	Tom's non-LOT baddie AI (for Compys etc.) initialisers		   *
 ******************************************************************************/

int		EnableNonLotAI( sint16 item_number, int Always )
{
	int				x,y,z;
	int				worstslot,slot;
	int				worstdist,dist;
	ITEM_INFO		*item;
	CREATURE_INFO	*creature;

	/* If any slots are free use one */
	if ( nonlot_slots_used<NUM_NONLOT_SLOTS )
	{
		creature = non_lot_slots;
		for ( slot=0;slot<NUM_NONLOT_SLOTS;slot++,creature++ )
		{
			if ( creature->item_num==NO_ITEM )
			{
				InitialiseNonLotAI( item_number,slot );
				return(1);
			}
		}
//		S_ExitSystem("UnpauseBaddie() grimmer!");
	}


	if ( !Always ) 										// Get Distance at above which
	{                                                   // we swap creatures
		item = &items[item_number];
		x = (item->pos.x_pos - camera.pos.x)>>8;
		y = (item->pos.y_pos - camera.pos.y)>>8;
		z = (item->pos.z_pos - camera.pos.z)>>8;
		worstdist = (x*x) + (y*y) + (z*z);
	}
	else
		worstdist = 0;


	worstslot = -1;
	creature = non_lot_slots;
	for ( slot=0;slot<NUM_NONLOT_SLOTS;slot++,creature++ )
	{
		item = &items[creature->item_num];
		x = (item->pos.x_pos - camera.pos.x)>>8;
		y = (item->pos.y_pos - camera.pos.y)>>8;
		z = (item->pos.z_pos - camera.pos.z)>>8;
		dist = (x*x) + (y*y) + (z*z);
		if ( dist>worstdist )
		{
			worstdist = dist;
			worstslot = slot;
		}
	}

	/* Convert Baddie on worst slot to INVISIBLE and take over slot */
	if ( worstslot>=0  )
	{
		items[non_lot_slots[worstslot].item_num].status = INVISIBLE;
		DisableBaddieAI( non_lot_slots[worstslot].item_num );
		InitialiseNonLotAI( item_number,worstslot );
		return(1);
	}
	return(0);
}


/******************************************************************************
 *			Initialise Creature and LOT for new ACTIVE Baddie
 *****************************************************************************/

void	InitialiseNonLotAI( sint16 item_number, int slot)
{
	CREATURE_INFO	*creature;
	ITEM_INFO		*item;
	int i;

	creature = &non_lot_slots[slot];

	item = &items[item_number];

	if (item_number == lara.item_number)
		lara.creature = creature;
	else
		item->data = (void *)creature;

	creature->item_num = item_number;
	creature->mood = BORED_MOOD;
	for(i=0; i<4; i++)
		creature->joint_rotation[i] = 0;
	creature->maximum_turn = ONE_DEGREE;
	creature->flags = 0;
	creature->enemy = NULL;
	creature->alerted = 0;
	creature->head_left = 0;
	creature->head_right = 0;
	creature->reached_goal = 0;
	creature->hurt_by_lara = 0;
	creature->patrol2 = 0;

	/* Default settings for LOT */
	creature->LOT.step = STEP_L;
	creature->LOT.drop = -STEP_L*2;
	creature->LOT.block_mask = BLOCKED;
	creature->LOT.fly = NO_FLYING;

	switch ( item->object_number )
	{
	case LARA:
		/* Water current control */
		creature->LOT.step = WALL_L*20;
		creature->LOT.drop = -WALL_L*20;
		creature->LOT.fly = STEP_L;
		break;

	case DIVER:
	case WHALE:
//	case BARACUDDA:
	case VULTURE:
	case CROW:
		/* Swimming or flying baddie */
		creature->LOT.step = WALL_L*20;
		creature->LOT.drop = -WALL_L*20;
		creature->LOT.fly = STEP_L/16;
		if (item->object_number == WHALE)
			creature->LOT.block_mask = BLOCKABLE; // they cannot pass over splitters ever
		break;

	// NOTE: if baddies added here, change code in LoadBoxes() in file.c to load ground_zone
//	case SPIDER:
//		/* Baddies that can go up/down half blocks */
//		creature->LOT.step = STEP_L*2;
//		creature->LOT.drop = -WALL_L;
//		break;

	case LIZARD_MAN:
	case MP1:
		/* Baddies that can do a full block climb */
		creature->LOT.step = WALL_L;
		creature->LOT.drop = -WALL_L;
		break;

//	case DINOSAUR:
//		creature->LOT.block_mask = BLOCKABLE;
//		break;
	}

nonlot_slots_used++;

}
