/**********************************************************************

	Title :			Action Module 
	Filename :		action.c
	Author :		Thomas Szirtes
	Revision :		1.0

	Notes :			Handles AI and scripting for Computer controlled
					objects

	Copyright :		(C) SEGA Europe 1998

  **********************************************************************/


/*
 *		INCLUDES
 */
#include	"ftypes.h"
#include	"future.h"
#include	"action.h"


/*
 *		DEFINES
 */
#define		LOOK_AHEAD	3


/*
 *		EXTERNAL DECLERATIONS
 */
extern		SHIP		*shipdb[];



/*--------------------------------------------------------------------------
 
  Uint16 ACTION_ReadScript(Script *s,Uint16 index, SHIPAI *shipai)
 
  Parameters:
 	s			Script
 	index		index into script
 	shipai		Ship's AI structure
 
  Returns:
 
  Notes:
 	Reads from specified position in script and then interprets data into
 	specified ship's AI structure.
 
  --------------------------------------------------------------------------*/

Uint16 ACTION_ReadScript(Script *s,Uint16 index, SHIPAI *shipai)
{
	Uint8 cmd;
	Uint8	readagain;

	do
	{
	
	readagain=0;

	
	cmd		=	s[index];
	if	(cmd==cmdEND)		return 3;  /*index + position; */

	index	++;
	
	switch(cmd)
	{
	/* cmdGOTO */
	case cmdGOTO :
		shipai->mode			=	SHIPAI_MODE_TRAVEL;				/* switch to travel mode  */
		shipai->destination.i	=	s[index];
		shipai->destination.j	=	s[index+1];
		readagain				=	1;
		index					+=	szeBYTE+szeBYTE;			/* increment parameter pointer */
		break;

	/* cmdPOSITION */
	case cmdPOSITION :
		shipai->mode			=	SHIPAI_MODE_TRANSPORT;
		shipai->destination.i	=	s[index];
		shipai->destination.j	=	s[index+1];
		shipai->timI			=	1;
		index					+=	szeBYTE + szeBYTE;

		break;

	/* cmdWAITFOR */
	case cmdWAITFOR :
		shipai->mode			=	SHIPAI_MODE_WAIT;				/* switch to travel mode  */
		shipai->timI			=	s[index];						/* store timer */
		index					+=	szeTIME;
		break;
	
	/* cmdWAITUNTIL */
	case cmdWAITUNTIL :
		/*		shipai->mode = SHIPAI_MODE_WAIT;					// switch to travel mode  */
		if(s[index]==0)			shipai->timI = -1;	
		index					+=	szeTIME;
		break;

	/* cmdORBIT */
	case cmdORBIT :
		shipai->mode			=	SHIPAI_MODE_STOP;				/* switch to travel mode  */
		break;

	/* cmdALTITUDE */
	case cmdALTITUDE :
		shipai->mode			=	SHIPAI_MODE_TRAVEL;				/* switch to travel mode  */
		shipai->destination.h	=	s[index];
		readagain				=	1;
		index					+=	szeBYTE;
		break;

	/* cmdLAND */
	case cmdLAND :
		shipai->timI			=	-1;
		shipai->mode			=	SHIPAI_MODE_LAND;				/* switch to land mode  */
		break;

	/* cmdLAUNCH */
	case cmdLAUNCH :
		shipai->timI			=	-1;
		shipai->mode			=	SHIPAI_MODE_LAUNCH;				/* switch to launch mode  */
		break;

	/* cmdFOLLOW */
	case cmdFOLLOW :
		shipai->mode			=	SHIPAI_MODE_FOLLOW;				/* switch to follow/travel mode  */
		shipai->timU			=	s[index];
		index					+=	szeBYTE;
		break;
	
	/* error */
	default :
		;
		return SCRIPT_ERROR;
	}

	} while (readagain!=0);

	return index;
}




/*--------------------------------------------------------------------------
 
  void action_navigator(SHIPAI *shipai, MOVESHIP *shipmove)
 
  Parameters:
 	shipai		ship AI structure to process
 	shipmove	ship current movement data
 
  Returns:
 
  Notes:
 	Analysis's map and trys to navigate the ship to the current specified
 	destination.  WORK IN PROGRESS - NOT 100% RELIABLE YET
  
  --------------------------------------------------------------------------*/


void action_navigator(SHIP *ship)
{
	NJS_VECTOR		v1,v2;
	Float			d,op;
	Uint8			r;
	int				i1,j1;
	Angle			a;				/* angle between heading and collision centre */
	Angle			b;				/* angle between collision centre and avoidance route */
	SHIPAI			*shipai;
	int				n;
	int				col;
	MOVESHIP		*move;

	shipai		=	&ship->ai;
	move		=	&ship->move;

	col=0;

	shipai->altitude	=	shipai->destination.h;

	/* Check if current position matches destination position - if so reset internal timer */
	if (ship->current.i == shipai->destination.i && ship->current.j == shipai->destination.j)
	{
		shipai->timI=0;
		return;
	}


	/* Calculate heading between user and destination */
	v1.x	=	GRID2P(shipai->destination.i) - move->position.x;
	v1.y	=	0.f;
	v1.z	=	GRID2P(shipai->destination.j) - move->position.z;
	d		=	1 / njUnitVector(&v1);

	for(n=1;n<=LOOK_AHEAD;n++)
	{
		/* look n units along that heading... */
		i1		=	P2GRID(move->position.x + ( v1.x * (GRID_SIZE*n)) );
		j1		=	P2GRID(move->position.z + ( v1.z * (GRID_SIZE*n)) );
/*		r		=	map.map[i1][j1].id; */
/*		r		=	build_inst[map.map[i1][j1].inst].id; */

		/* is there a collision? */
/*		if ( (map.map[i1][j1].attr&MAPITEM_ATTR_BIT_BUILDING)) */
		if (FALSE) /*map.map[i1][j1].inst!=0xffff) */
		{
			col++;
			/* Find centre of object we have hit */
/*			r		=	TraceToRoot(&i1, &j1) & MAPITEM_ID_MASK_BUILDING; */
			i1		=	build_inst[map.map[i1][j1].inst].i;
			j1		=	build_inst[map.map[i1][j1].inst].j;
			r		=	build_inst[map.map[i1][j1].inst].id;
			/* i and j = map coords of root */

			/* DECIDE WETHER TO GO ROUND OR OVER */

			/* Calculate avoidance height */
/*COLLISION DISABLED */
			/*			h = (Sint16)(buildings[r].bound.rady + buildings[r].bound.centroid.y) + (Sint16)map.map[i1][j1].gnd; */

#if 0
			/* if height difference less than 30 then try to avoid it */
			if (h < shipai->destination.h + 80)	
			{
				if (shipai->destination.h < h){
					/* GO OVER */
					shipai->altitude	=	(Sint16)h+10;
				}
			}
			else
			{
#endif

				/* DRIVE ROUND */
				/* Calculate vector from object root to position */
				v2.x	=	GRID2P(i1) - move->position.x;
				v2.y	=	0.f;
				v2.z	=	GRID2P(j1) - move->position.z;
				d		=	1 / njUnitVector(&v2);

				/* Check if left or right of destination heading */
 				op		=	- ((v1.x * v2.z) - (v1.z * v2.x));

				/* Calculate ANGLE heading from position to object centre */
				a		=		njArcTan( v2.x/v2.z );
				if(v2.z<0.0f)		
					a	+=		NJM_DEG_ANG(180.0f);

				/* Calculate angle from position to circumfrence  */
/*				b		=	njArcTan((GRID2P(buildings[r].buildsize)+8.f)/d); */

				/* negate angle depending on side of building we want to go */
				if (op<0.f)
					shipai->heading	=	a+b;
				else
  					shipai->heading	=	a-b;
	/*		} */

			break;	
		}

		if(col!=0) break;
	}

	if (col==0)
	{
	/*		set heading and speed to dest/max */
		if(v1.z>=0.0f)
			shipai->heading	=	njArcSin( v1.x );
		else
			shipai->heading	=	NJM_DEG_ANG(180.0f) - njArcSin( v1.x );
	}
}



/*--------------------------------------------------------------------------
 
  void ACTION_Ship(SHIP *ship, PERIPH_DATA *per)
 
  Parameters:
 	ship		ship structure to process
 	per			peripheral data output
 
  Returns:
 
  Notes:
 	Perform all cycles of ship AI.
 
  --------------------------------------------------------------------------*/

void ACTION_Ship(SHIP *ship, PERIPH_DATA *per)
{
	Uint16			ret;
	Sint16			t;
	Float			dh,sh;
	int				direction;
	SHIPAI			*shipai;
	MOVESHIP		*move;
	GRIDPOS			current;

	/* grrr... don't trust compilers */
	shipai			=	&ship->ai;
	move			=	&ship->move;

	/* reset peripheral controls */
	per->fire			=	0;
	per->yaw_left_right	=	0;
	per->pitch_up_down	=	0;

	/* convert position to grid */
	current.i	=	P2GRID(move->position.x);
	current.j	=	P2GRID(move->position.z);
	current.h	=	(Uint8)(move->position.y);

	/*------------------ SELECT COMMAND SOURCE -------------// */
	switch (shipai->type)
	{
	/* READ SCRIPT */
	case SHIPAI_TYPE_SCRIPT :
		if (shipai->timI==0)				/* if internal timer = 0 then read new command */
		{
			ret		=	ACTION_ReadScript( shipai->script.data, shipai->script.index, shipai );
			if (ret!=SCRIPT_ERROR)	shipai->script.index = ret;
		}
		break;

	case SHIPAI_TYPE_INDEPENDANT :
		/* Not implemented */
		break;

	case SHIPAI_TYPE_DISTANCE :
		/* Not implemented */
		break;

	case SHIPAI_TYPE_INERT :
		/* Not implemented */
		break;
	}

	/*---------------- PROCESS MODE -------------------// */
	switch (shipai->mode)
	{
	case SHIPAI_MODE_STOP :
		break;

	case SHIPAI_MODE_TRANSPORT :
		ship->move.position.x	=	(shipai->destination.i)*GRID_SIZE;
		ship->move.position.z	=	(shipai->destination.j)*GRID_SIZE;
		shipai->timI=0;
		break;

	case SHIPAI_MODE_WAIT :
		shipai->timI--;
		break;

	/* Lauch ship above ground */
	case SHIPAI_MODE_LAUNCH :
		if (move->position.y-30.f < (Float)map.map[current.i][current.j].gnd)
		{
			move->position.y	+=	0.5f;
		}
		else
		{
			shipai->timI			=	0;
			ship->flags				&=	~SHIP_FLAG_SHIELDDOWN;
		}
		break;

	/* Bring ship down to ground level */
	case SHIPAI_MODE_LAND :
		ship->flags	|=	SHIP_FLAG_SHIELDDOWN;
		if (move->position.y-10.f > (Float)map.map[current.i][current.j].gnd)
		{
			move->position.y	-=	0.5f;
		}
		else
		{
			shipai->timI			=	0;
		}
		break;

	/* Keep tracking another ships position */
	case SHIPAI_MODE_FOLLOW :

		shipai->destination.i = shipdb[shipai->timU]->current.i;
		shipai->destination.j = shipdb[shipai->timU]->current.j;
		shipai->destination.h = shipdb[shipai->timU]->current.h;
		/* GOTO TO TRAVEL MODE */

	/* Go from A to B */
	case SHIPAI_MODE_TRAVEL :
		action_navigator(ship);
		
		/*--------------------- DRIVER -----------------------// */

		/* I've taken most responsibility off this now to navigator */
		/* easy job just adjust to match heading and speed */
		
		/* ALTITUDE - adjust pitch to match change in altitude */
		t	=	shipai->altitude - current.h;
		if (t > 0) 
		{
			if (t>100) 
				per->pitch_up_down	=	100;
			else
				per->pitch_up_down	=	t;
		}
		else
		{
			if (t<-100) 
				per->pitch_up_down	=	-100;
			else
				per->pitch_up_down	=	t;
		}


		/* HEADING */
		/* delta heading */
 		sh	=	(Float)NJM_ANG_DEG(shipai->heading);
		dh	=	sh - (Float)NJM_ANG_DEG(NJM_DEG_ANG(move->yaw));
		
		per->fire	=	30;

		
		direction = 0;				/* clockwise , 1 = anticlockwise */

 		if (dh<0.f)	
			direction = 1; 
		else 
			direction = 0;

		dh	=	(Float)fabs(dh);
		
		/* If heading behind us - all engines off and turn round */
		if ( dh > 180.f )
		{
			dh			=	360.f - dh;
			direction	=	(direction+1)&1;			/* reverse direction */
		}

		/* Otherwise adjust heading */
		if (direction == 1)
		{
			per->yaw_left_right	=	(Sint16)dh;
			if ( per->yaw_left_right >  100 )	
				per->yaw_left_right	=	100;
			per->fire			=	100 - per->yaw_left_right;
		}
		else
		{
			per->yaw_left_right	=	-(Sint16)dh;
			if ( per->yaw_left_right < -100 )	
				per->yaw_left_right	=	-100;
			per->fire			=	100 + per->yaw_left_right;
		}

		/* have we reached target yet? */
		break;

	}



}
