/*********************************************************************************************/
/*                                                                                           */
/* Cleaner Control - TS  - 16/9/98                                                           */
/*                                                                                           */
/*********************************************************************************************/

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

#ifdef PSX_VERSION
#include "../spec_psx/typedefs.h"
#else
#include "../specific/stypes.h"
#endif

#include "objects.h"
#include "lara.h"
#include "sound.h"
#include "control.h"
#include "sphere.h"
#include "effect2.h"

extern uchar HeavyTriggered;

static void TriggerElectricSparks(PHD_VECTOR *pos, sint16 item_number, sint16 Node);
void InitialiseCleaner(sint16 item_number);
//void CobraCleaner(sint16 item_number);	// Eh?
void CleanerControl(sint16 item_number);

#if defined(PSX_VERSION) && defined(RELOC)

void *func[] __attribute__((section(".header"))) = {
	&InitialiseCleaner,
	&CleanerControl
};

#endif

/*********************************** TYPE DEFINITIONS ****************************************/

#define CLEANER_TURN (0x400)
//#define CLEANER_SPEED (2)
#define CLEANER_SPEED (STEP_L / 4)
#define CLEANER_TOUCH (0xfffc)

//#define DEBUG_CLEANER

#ifdef DEBUG_CLEANER
extern char exit_message[];
#endif

/*
#ifdef DEBUG_CLEANER
static char *CleanerStrings[] = {
	"RISE",
	"WAIT",
	"STRIKE",
	"DOWN",
	"DEATH"
};
#endif
*/

uchar elecspark[3] = {	0,0,0	};

//BITE_INFO cleaner_hit = {0,0,0,13};

//Gibby - the spark mesh numbers are : 5, 9 & 13
//I left the Initialise routine cos I thought you might need it for effects stuff

void InitialiseCleaner(sint16 item_number)
{
	ITEM_INFO *item;
	ROOM_INFO *r;
	long		x,z;

	item = &items[item_number];
	item->pos.x_pos &= ~(1023);
	item->pos.z_pos &= ~(1023);    //
	item->pos.x_pos |= 512;		// Just to make sure it's in the middle of a block.
	item->pos.z_pos |= 512;    	//
	item->item_flags[0] = CLEANER_TURN;		// Direction of rotation flag (-ve = left, +ve = right).
	item->item_flags[1] = 0;					// Just reached a new block flag.
	item->item_flags[2] = CLEANER_SPEED;		// Speed of object.

	item->collidable = 1;
	item->data = NULL;

//	x = item->pos.x_pos;
//	z = item->pos.z_pos;
//	r = &room[item->room_number];
//	r->floor[((z - r->z) >> WALL_SHIFT) + ((x - r->x) >> WALL_SHIFT) * r->x_size].stopper = 1;	// Mark block as blockable.
}

void CleanerControl(sint16 item_number)
{
	ITEM_INFO 	*item;
	CREATURE_INFO 	*cleaner;
	FLOOR_INFO 	*floor;
	ROOM_INFO 	*r;
	sint16 	angle, room_number, stop;
	sint32 	height, left_ok, forward_ok;
	long		x,z;

	if (!CreatureActive(item_number) || items[item_number].item_flags[2] == 0)
		return;

	item = &items[item_number];
	cleaner = (CREATURE_INFO *)item->data;
	angle = 0;
/*
	if (item->item_flags[2] == 0)
	{
		AnimateItem(item);
		return;
	}
*/
	if ((item->pos.y_rot & 0x3fff) == 0)
	{
		if ( ((item->pos.z_pos & 0x3ff) == 512 && (item->pos.y_rot == 0x0 || item->pos.y_rot == -0x8000)) ||
    			((item->pos.x_pos & 0x3ff) == 512 && (item->pos.y_rot == 0x4000 || item->pos.y_rot == -0x4000)))
		{
			if (item->item_flags[1] == 1)
			{
				x = item->pos.x_pos + ((1024 * phd_sin(item->pos.y_rot+0x8000)) >> W2V_SHIFT);
				z = item->pos.z_pos + ((1024 * phd_cos(item->pos.y_rot+0x8000)) >> W2V_SHIFT);
				room_number = item->room_number;
				GetFloor(x,item->pos.y_pos,z,&room_number);
				r = &room[room_number];
				r->floor[((z - r->z) >> WALL_SHIFT) + ((x - r->x) >> WALL_SHIFT) * r->x_size].stopper = 0;
				item->item_flags[1] = 0;
			}

			switch (item->pos.y_rot)
			{
				case 0x0:
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos - WALL_L, item->pos.y_pos, item->pos.z_pos, &room_number);
					height = GetHeight(floor, item->pos.x_pos - WALL_L, item->pos.y_pos, item->pos.z_pos);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos-WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					left_ok = (height == item->pos.y_pos && stop == 0);
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos + WALL_L, &room_number);
					height = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos + WALL_L);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos+WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					forward_ok = (height == item->pos.y_pos && stop == 0);

					if (!forward_ok && !left_ok && item->item_flags[0] > 0)	// Can't go left or forward and not just turned left - turn right.
					{
						item->pos.y_rot += CLEANER_TURN;
					 	item->item_flags[0] = CLEANER_TURN;	// Turning right.
					}
					else if (!forward_ok && !left_ok && item->item_flags[0] < 0)	// Can't go left or forward and has just turned left - turn left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else if (left_ok && item->item_flags[0] > 0)	// Can go left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else			// Carry on going forward.
					{
					 	item->item_flags[0] = CLEANER_TURN;
						item->pos.z_pos += item->item_flags[2];
						r->floor[((item->pos.z_pos+WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper = 1;
						item->item_flags[1] = 1;
					}
					break;

				case 0x4000:
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos + WALL_L, &room_number);
					height = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos + WALL_L);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos+WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					left_ok = (height == item->pos.y_pos && stop == 0);
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos + WALL_L, item->pos.y_pos, item->pos.z_pos, &room_number);
					height = GetHeight(floor, item->pos.x_pos + WALL_L, item->pos.y_pos, item->pos.z_pos);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos+WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					forward_ok = (height == item->pos.y_pos && stop == 0);

					if (!forward_ok && !left_ok && item->item_flags[0] > 0)	// Can't go left or forward and not just turned left - turn right.
					{
						item->pos.y_rot += CLEANER_TURN;
					 	item->item_flags[0] = CLEANER_TURN;	// Turning right.
					}
					else if (!forward_ok && !left_ok && item->item_flags[0] < 0)	// Can't go left or forward and has just turned left - turn left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else if (left_ok && item->item_flags[0] > 0)	// Can go left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else			// Carry on going forward.
					{
					 	item->item_flags[0] = CLEANER_TURN;
						item->pos.x_pos += item->item_flags[2];
						r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos+WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper = 1;
						item->item_flags[1] = 1;
					}
					break;

				case -0x8000:
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos + WALL_L, item->pos.y_pos, item->pos.z_pos, &room_number);
					height = GetHeight(floor, item->pos.x_pos + WALL_L, item->pos.y_pos, item->pos.z_pos);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos+WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					left_ok = (height == item->pos.y_pos && stop == 0);
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos - WALL_L, &room_number);
					height = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos - WALL_L);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos-WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					forward_ok = (height == item->pos.y_pos && stop == 0);

					if (!forward_ok && !left_ok && item->item_flags[0] > 0)	// Can't go left or forward and not just turned left - turn right.
					{
						item->pos.y_rot += CLEANER_TURN;
					 	item->item_flags[0] = CLEANER_TURN;	// Turning right.
					}
					else if (!forward_ok && !left_ok && item->item_flags[0] < 0)	// Can't go left or forward and has just turned left - turn left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else if (left_ok && item->item_flags[0] > 0)	// Can go left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else			// Carry on going forward.
					{
					 	item->item_flags[0] = CLEANER_TURN;
						item->pos.z_pos -= item->item_flags[2];
						r->floor[((item->pos.z_pos-WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper = 1;
						item->item_flags[1] = 1;
					}
					break;

				case -0x4000:
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos - WALL_L, &room_number);
					height = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos - WALL_L);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos-WALL_L - r->z) >> WALL_SHIFT) + ((item->pos.x_pos - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					left_ok = (height == item->pos.y_pos && stop == 0);
					room_number = item->room_number;
					floor = GetFloor(item->pos.x_pos - WALL_L, item->pos.y_pos, item->pos.z_pos, &room_number);
					height = GetHeight(floor, item->pos.x_pos - WALL_L, item->pos.y_pos, item->pos.z_pos);
					r = &room[room_number];
					stop = (r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos-WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper);
					forward_ok = (height == item->pos.y_pos && stop == 0);

					if (!forward_ok && !left_ok && item->item_flags[0] > 0)	// Can't go left or forward and not just turned left - turn right.
					{
						item->pos.y_rot += CLEANER_TURN;
					 	item->item_flags[0] = CLEANER_TURN;	// Turning right.
					}
					else if (!forward_ok && !left_ok && item->item_flags[0] < 0)	// Can't go left or forward and has just turned left - turn left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else if (left_ok && item->item_flags[0] > 0)	// Can go left.
					{
						item->pos.y_rot -= CLEANER_TURN;
					 	item->item_flags[0] = -CLEANER_TURN;	// Turning left.
					}
					else			// Carry on going forward.
					{
					 	item->item_flags[0] = CLEANER_TURN;
						item->pos.x_pos -= item->item_flags[2];
						r->floor[((item->pos.z_pos - r->z) >> WALL_SHIFT) + ((item->pos.x_pos-WALL_L - r->x) >> WALL_SHIFT) * r->x_size].stopper = 1;
						item->item_flags[1] = 1;
					}
					break;

				default:
					break;
			}

			// Test for HEAVY triggers
			floor = GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
			height = GetHeight(floor, item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
  			TestTriggers(trigger_index, 1);
			if (HeavyTriggered)
			{
				item->item_flags[2] = 0;
				SoundEffect(131, &item->pos, 0);
			}
		}
		else
		{
			switch (item->pos.y_rot)
			{
				case 0x0:
					item->pos.z_pos += item->item_flags[2];
					break;

				case 0x4000:
					item->pos.x_pos += item->item_flags[2];
					break;

				case -0x8000:
					item->pos.z_pos -= item->item_flags[2];
					break;

				case -0x4000:
					item->pos.x_pos -= item->item_flags[2];
					break;
			}
		}
	}
	else
	{
		item->pos.y_rot += item->item_flags[0];
	}

#ifdef DEBUG_CLEANER
//	sprintf(exit_message, "y_rot: %d, in hex: %x", item->pos.y_rot, item->pos.y_rot);
//	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "Mood: %d", cleaner->mood);
//	PrintDbug(2, 2, exit_message);
//	sprintf(exit_message, "%s", CleanerStrings[item->current_anim_state]);
//	PrintDbug(2, 3, exit_message);
//	sprintf(exit_message, "%s", CleanerStrings[item->goal_anim_state]);
//	PrintDbug(2, 4, exit_message);
//	sprintf(exit_message, "%s", CleanerStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Anim Number; %d", item->anim_number - objects[ELECTRIC_CLEANER].anim_index);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Alert:%d, Goal:%d, Hurt:%d", cleaner->alerted, cleaner->reached_goal, cleaner->hurt_by_lara);
//	PrintDbug(2,6, exit_message);
//	sprintf(exit_message, "AI Bits: %d", item->ai_bits);
//	PrintDbug(2,7, exit_message);
#endif

	if ((item->touch_bits & CLEANER_TOUCH) && !lara.electric)
	{
		lara.electric = 1;			// Electrify Lara.
		lara_item->hit_points = 0;	// Kill her.
		item->item_flags[2] = 0;
		SoundEffect(131, &item->pos, 0);
	}


	/* Actually do animation allowing for collisions */
	AnimateItem(item);

	room_number = item->room_number;
	GetFloor(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, &room_number);
	if (room_number != item->room_number)
		ItemNewRoom(item_number, room_number);

// Do dynamic lights & elecsparks.

	{
		PHD_VECTOR	pos;
		long		r,g,b;

		SoundEffect(191, &item->pos, 0);

		if (((GetRandomControl()&7)==0 && elecspark[0] == 0) || elecspark[0])
		{
			if (elecspark[0] == 0)
				elecspark[0] = (GetRandomControl()&7)+4;
			else
				elecspark[0]--;
			pos.x = -160;
			pos.y = -8;
			pos.z = 16;
			GetJointAbsPosition(item,&pos,5);
			TriggerElectricSparks(&pos,item_number,SPN_CLEANER5);
			pos.x += (GetRandomControl()&31)-16;
			pos.y += (GetRandomControl()&31)-16;
			pos.z += (GetRandomControl()&31)-16;
			b = (GetRandomControl()&15)+16;
			r = b>>2;
			g = b>>1;
			TriggerDynamic(pos.x,pos.y,pos.z,10,r,g,b);
		}

		if (((GetRandomControl()&7)==0 && elecspark[1] == 0) || elecspark[1])
		{
			if (elecspark[1] == 0)
				elecspark[1] = (GetRandomControl()&7)+4;
			else
				elecspark[1]--;
			pos.x = -160;
			pos.y = -8;
			pos.z = 16;
			GetJointAbsPosition(item,&pos,9);
			TriggerElectricSparks(&pos,item_number,SPN_CLEANER9);
			pos.x += (GetRandomControl()&31)-16;
			pos.y += (GetRandomControl()&31)-16;
			pos.z += (GetRandomControl()&31)-16;
			b = (GetRandomControl()&15)+16;
			r = b>>2;
			g = b>>1;
			TriggerDynamic(pos.x,pos.y,pos.z,10,r,g,b);
		}

		if (((GetRandomControl()&7)==0 && elecspark[2] == 0) || elecspark[2])
		{
			if (elecspark[2] == 0)
				elecspark[2] = (GetRandomControl()&7)+4;
			else
				elecspark[2]--;
			pos.x = -160;
			pos.y = -8;
			pos.z = 16;
			GetJointAbsPosition(item,&pos,13);
			TriggerElectricSparks(&pos,item_number,SPN_CLEANER13);
			pos.x += (GetRandomControl()&31)-16;
			pos.y += (GetRandomControl()&31)-16;
			pos.z += (GetRandomControl()&31)-16;
			b = (GetRandomControl()&15)+16;
			r = b>>2;
			g = b>>1;
			TriggerDynamic(pos.x,pos.y,pos.z,10,r,g,b);
		}
	}

}


static void TriggerElectricSparks(PHD_VECTOR *pos, sint16 item_number, sint16 Node)
{
	SPARKS	*sptr;
	long		dx,dz;

	dx = lara_item->pos.x_pos - pos->x;
	dz = lara_item->pos.z_pos - pos->z;

	if (dx < -0x5000 || dx > 0x5000 || dz < -0x5000 || dz > 0x5000)
		return;

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	sptr->sB = (GetRandomControl()&63)+192;
	sptr->sR = sptr->sB;
	sptr->sG = sptr->sB;

	sptr->dB = (GetRandomControl()&63)+192;
	sptr->dR = sptr->sB>>2;
	sptr->dG = sptr->sB>>1;

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 8;
	sptr->sLife = sptr->Life = 20+(GetRandomControl()&7);
	sptr->TransType = COLADD;
	sptr->Dynamic = -1;

	sptr->x = (GetRandomControl()&31) - 16;
	sptr->y = (GetRandomControl()&31) - 16;
	sptr->z = (GetRandomControl()&31) - 16;
	sptr->Xvel = ((GetRandomControl()&255)-128)<<2;
	sptr->Yvel = (GetRandomControl()&7)-4;
	sptr->Zvel = ((GetRandomControl()&255)-128)<<2;

	sptr->Friction = 4;//|(4<<4);
	sptr->Flags = SP_SCALE|SP_ITEM|SP_NODEATTATCH;
	sptr->FxObj = item_number;
	sptr->NodeNumber = Node;
	sptr->Scalar = 1;
	sptr->Width = sptr->sWidth = (GetRandomControl()&3)+4;
	sptr->dWidth = sptr->sWidth >> 1;
	sptr->Height = sptr->sHeight = sptr->Width;
	sptr->dHeight = sptr->dWidth;
	sptr->Gravity = 4+(GetRandomControl()&3);
	sptr->MaxYvel = 0;
}
