/*********************************************************************************************/
/*                                                                                           */
/* Shiva! - TS - 3/8/98                                                                      */
/*                                                                                           */
/*********************************************************************************************/

// Shiva statue transformation and animation code

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

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


#define SHIVA_PINCER_DAMAGE 150
#define SHIVA_CHOPPER_DAMAGE 180
#define SHIVA_FEELER_DISTANCE WALL_L

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

void DrawShiva(ITEM_INFO *item);
void InitialiseShiva(sint16 item_number);
void ShivaControl(sint16 item_number);

void *func[] = {
	&DrawShiva,
	&InitialiseShiva,
	&ShivaControl
};

#endif


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

// Shiva!
BITE_INFO shiva_left = {0,0,920, 13};
BITE_INFO shiva_right = {0,0,920, 22};
//BITE_INFO shiva_mouth = {0,0,200, 26};
int effect_mesh = 0;

enum shiva_anims {SHIVA_WAIT, SHIVA_WALK, SHIVA_WAIT_DEF, SHIVA_WALK_DEF, SHIVA_START, SHIVA_PINCER, SHIVA_KILL, SHIVA_CHOPPER, SHIVA_WALKBACK,
	SHIVA_DEATH};

#define SHIVA_WALK_TURN (ONE_DEGREE*4)

#define SHIVA_WAIT_CHANCE 0x200
#define SHIVA_WALK_CHANCE (SHIVA_WAIT_CHANCE + 0x200)

#define SHIVA_PINCER_RANGE SQUARE(WALL_L*5/4)
#define SHIVA_CHOPPER_RANGE SQUARE(WALL_L*4/3)

#define SHIVA_WALK_RANGE SQUARE(WALL_L*2)

#define SHIVA_DIE_ANIM 22
#define SHIVA_START_ANIM 14
#define SHIVA_KILL_ANIM 18

#define SHIVA_TOUCHL 0x2400
#define SHIVA_TOUCHR 0x2400000

#define SHIVA_HURT_FLAG 4

//#define DEBUG_SHIVA

#ifdef DEBUG_SHIVA
extern char exit_message[];
static char *ShivaStrings[] = {"WAIT", "WALK", "WAIT-DEF", "WALK-DEF", "START", "PINCER", "KILL", "CHOPPER", "WALKBACK", "DEATH"};
#endif


/*********************************** FUNCTION CODE *******************************************/

static void ShivaDamage(ITEM_INFO *item, CREATURE_INFO *shiva, int damage)
{
	if (!(shiva->flags) && (item->touch_bits & SHIVA_TOUCHR))
	{
		lara_item->hit_points -= damage;
		lara_item->hit_status = 1;
		CreatureEffect(item, &shiva_right, DoBloodSplat);
		shiva->flags = 1;
		SoundEffect(318, &item->pos, 0);
	}

	if (!(shiva->flags) && (item->touch_bits & SHIVA_TOUCHL))
	{
		lara_item->hit_points -= damage;
		lara_item->hit_status = 1;
		CreatureEffect(item, &shiva_left, DoBloodSplat);
		shiva->flags = 1;
		SoundEffect(318, &item->pos, 0);
	}
}



static void TriggerShivaSmoke(long x, long y, long z, long uw)
{
	long		size;
	SPARKS	*sptr;
	long		dx,dz;

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

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

	sptr = &spark[GetFreeSpark()];

	sptr->On = 1;
	if (uw)
	{
		sptr->sR = 0;
		sptr->sG = 0;
		sptr->sB = 0;
		sptr->dR = 192;
		sptr->dG = 192;
		sptr->dB = 208;
	}
	else
	{
		sptr->sR = 144;
		sptr->sG = 144;
		sptr->sB = 144;
		sptr->dR = 64;
		sptr->dG = 64;
		sptr->dB = 64;
	}

	sptr->ColFadeSpeed = 8;
	sptr->FadeToBlack = 64;
	sptr->sLife = sptr->Life = (GetRandomControl()&31)+96;
	if (uw)
		sptr->TransType = COLADD;
	else
		sptr->TransType = COLSUB;

	sptr->extras = 0;
	sptr->Dynamic = -1;

	sptr->x = x+(GetRandomControl()&31)-16;
	sptr->y = y+(GetRandomControl()&31)-16;
	sptr->z = z+(GetRandomControl()&31)-16;
	sptr->Xvel = ((GetRandomControl()&4095)-2048)>>2;
	sptr->Yvel = (GetRandomControl()&255)-128;
	sptr->Zvel = ((GetRandomControl()&4095)-2048)>>2;

	if (uw)
	{
		sptr->Yvel >>= 4;
		sptr->y +=32;
		sptr->Friction = 4|(1<<4);
	}
	else
		sptr->Friction = 6;

//#ifdef	PC_VERSION
	sptr->Flags = SP_SCALE|SP_DEF|SP_ROTATE|SP_EXPDEF;
	sptr->RotAng = GetRandomControl()&4095;
	if (GetRandomControl()&1)
		sptr->RotAdd = -(GetRandomControl()&15)-16;
	else
		sptr->RotAdd = (GetRandomControl()&15)+16;
//#endif

	sptr->Def = objects[EXPLOSION1].mesh_index;
	sptr->Scalar = 3;
	if (uw)
		sptr->Gravity = sptr->MaxYvel = 0;
	else
	{
		sptr->Gravity = -(GetRandomControl()&3)-3;
		sptr->MaxYvel = -(GetRandomControl()&3)-4;
	}
	size = (GetRandomControl()&31)+128;
	sptr->Width = sptr->sWidth = size>>2;
	sptr->dWidth = size;
	size += (GetRandomControl()&31)+32;
 	sptr->Height = sptr->sHeight = size>>3;
	sptr->dHeight = size;
}

void DrawShiva(ITEM_INFO *item)
{
	/* Big huge copy of DrawAnimatingItem() which swaps meshes */
	int 		clip,i,poppush;
	OBJECT_INFO *object;
	sint32 		*bone;
	sint16		*rotation1, *rotation2;
	sint16		*extra_rotation;
	sint16		**meshpp, **swappp;
	uint32 		bit;
	sint16		*frmptr[2];
	int			rate,frac;
	PHD_VECTOR	pos;

	frac=GetFrames( item,frmptr,&rate );
	#ifdef DEBUG_SHIVA
	sprintf(exit_message, "Status: %d, Mesh_Bits:%d", item->status, item->mesh_bits);
	PrintDbug(2,5, exit_message);
	#endif

	if (item->hit_points <= 0 && item->status != ACTIVE && item->mesh_bits != 0)
	{
		item->mesh_bits = item->mesh_bits >> 1;
		pos.x = 0;             // Copy Offsets from mesh
		pos.y = 0;             // Pivot
		pos.z = 256;
		GetJointAbsPosition(item, &pos, effect_mesh++);
		TriggerShivaSmoke(pos.x, pos.y, pos.z,1);
	}

	object = &objects[item->object_number];					// Draw Shadows...
	if (object->shadow_size)
		S_PrintShadow( object->shadow_size,frmptr[0],item,0 );

	phd_PushMatrix();
	phd_TranslateAbs(item->pos.x_pos, item->pos.y_pos, item->pos.z_pos);
	phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);

	if ( (clip=S_GetObjectBounds(frmptr[0])) )
	{
		CalculateObjectLighting( item,frmptr[0] );

		extra_rotation = (sint16 *)item->data;

		meshpp = &meshes[objects[SHIVA].mesh_index];
		swappp = &meshes[objects[MESHSWAP1].mesh_index];

		bone = bones + object->bone_index;              	// Bone pointer
		bit = 1;

		if ( !frac )
		{
			phd_TranslateRel((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8));
			rotation1 = frmptr[0]+9;
			gar_RotYXZsuperpack(&rotation1, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons( *meshpp, clip );
			else
				phd_PutPolygons( *swappp, clip );
			meshpp++;
			swappp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++,swappp++ )
			{
				poppush = *(bone);							// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix();
				if (poppush & 2)
					phd_PushMatrix();

				phd_TranslateRel( *(bone+1), *(bone+2), *(bone+3) );
				gar_RotYXZsuperpack(&rotation1, 0);

				if (extra_rotation && (poppush & (ROT_X|ROT_Y|ROT_Z)))			// If any Extra rotations required...
				{
					if (poppush & ROT_Y)
						phd_RotY(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ(*(extra_rotation++));
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons( *meshpp, clip );
				else
					phd_PutPolygons( *swappp, clip );
			}
		}
		else
		{
#ifdef PSX_VERSION
			InitInterpolate(frac, rate, &iMatrixStack[0]);
#else
			InitInterpolate( frac,rate );
#endif
			phd_TranslateRel_ID( (sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8),
								 (sint32)*(frmptr[1]+6), (sint32)*(frmptr[1]+7), (sint32)*(frmptr[1]+8) );
			rotation1 = frmptr[0]+9;
			rotation2 = frmptr[1]+9;
			gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

			if (bit & item->mesh_bits)
				phd_PutPolygons_I( *meshpp, clip );
			else
				phd_PutPolygons_I( *swappp, clip );
			meshpp++;
			swappp++;

			for ( i=object->nmeshes-1; i>0; i--,bone+=4,meshpp++,swappp++ )
			{
				poppush = *(bone);						// Do Automated Hierarchy
				if (poppush & 1)                			// Push Pop
					phd_PopMatrix_I();
				if (poppush & 2)
					phd_PushMatrix_I();

				phd_TranslateRel_I(*(bone+1), *(bone+2), *(bone+3));
				gar_RotYXZsuperpack_I(&rotation1, &rotation2, 0);

				if (extra_rotation && (poppush & (ROT_X|ROT_Y|ROT_Z)))			// If any Extra rotations required...
				{
#ifdef PSX_VERSION
					if (poppush & ROT_Y)
					{
						mRotY(*extra_rotation);
						iRotY(*extra_rotation++);
					}

					if (poppush & ROT_X)
					{
						mRotX(*extra_rotation);
						iRotX(*extra_rotation++);
					}

					if (poppush & ROT_Z)
					{
						mRotZ(*extra_rotation);
						iRotZ(*extra_rotation++);
					}
#else
					if (poppush & ROT_Y)
						phd_RotY_I(*(extra_rotation++));
					if (poppush & ROT_X)
						phd_RotX_I(*(extra_rotation++));
					if (poppush & ROT_Z)
						phd_RotZ_I(*(extra_rotation++));
#endif
				}

				bit <<= 1;
				if (bit & item->mesh_bits)
					phd_PutPolygons_I( *meshpp, clip );
				else if (item->hit_points > 0 || item->status == ACTIVE || bit != 0x400 || item->carried_item == -1)
					phd_PutPolygons_I( *swappp, clip );
			}

		}
	}
	phd_PopMatrix();
}


void InitialiseShiva(sint16 item_number)
{
	ITEM_INFO *item;

	item = &items[item_number];

	/* Start Shiva in still pose, and all his meshes will be stone (from MESHSWAP1) */
	if (item->object_number == SHIVA)
	{
		item->anim_number = objects[SHIVA].anim_index + SHIVA_START_ANIM;
		item->frame_number = anims[item->anim_number].frame_base;
		item->current_anim_state = item->goal_anim_state = SHIVA_START;
	}
	item->status = NOT_ACTIVE;

	item->mesh_bits = 0;
}


void ShivaControl(sint16 item_number)
{
	/* Shiva */
	ITEM_INFO *item;
	CREATURE_INFO *shiva;
	sint16 angle, head_x, head_y, torso_x, torso_y, tilt, room_number;
	sint32 x,z;
	int random, lara_alive;
	AI_INFO info;
	PHD_VECTOR	pos;
	FLOOR_INFO *floor;

	if (!CreatureActive(item_number))
		return;

	item = &items[item_number];
	shiva = (CREATURE_INFO *)item->data;
	head_x = head_y = torso_x = torso_y = angle = tilt = 0;

	lara_alive = (lara_item->hit_points>0);

	#ifdef DEBUG_SHIVA

	if (shiva->mood == BORED_MOOD)
		PrintDbug(2, 2, "Bored");
	else if (shiva->mood == ESCAPE_MOOD)
		PrintDbug(2, 2, "Escape");
	else if (shiva->mood == ATTACK_MOOD)
		PrintDbug(2, 2, "Attack");
	else if (shiva->mood == STALK_MOOD)
		PrintDbug(2, 2, "Stalk");

	sprintf(exit_message, "%s", ShivaStrings[item->current_anim_state]);
	PrintDbug(2, 4, exit_message);

//	sprintf(exit_message, "%s", ShivaStrings[item->goal_anim_state]);
//	PrintDbug(2, 5, exit_message);
//	sprintf(exit_message, "%s", ShivaStrings[item->required_anim_state]);
//	PrintDbug(2, 6, exit_message);
//	sprintf(exit_message, "Head Tilt:%d", shiva->joint_rotation[1]);
//	PrintDbug(2,7, exit_message);
//	sprintf(exit_message, "Flags:%d", shiva->flags);
//	PrintDbug(2,7, exit_message);
	sprintf(exit_message, "X_ANGLE:%d", info.x_angle);
	PrintDbug(2,7, exit_message);
	#endif

	if (item->hit_points <= 0)
	{
		/* Turn him back to a statue in whatever position he happens to be in
		item->current_anim_state = SHIVA_DEATH;
		item->mesh_bits >>= 1;

		if (!item->mesh_bits)
		{
			SoundEffect(105, NULL, 0);
			item->mesh_bits = 0xffffffff;
			item->object_number = MESHSWAP1; // just to fool ExplodingDeath to produce jade chunks
			ExplodingDeath(item_number, 0xffffffff, 0);
			item->object_number = SHIVA;
			DisableBaddieAI(item_number);
			KillItem(item_number);
			item->status = DEACTIVATED;
			item->flags |= ONESHOT;
		}
		return;
		*/
		if (item->current_anim_state != SHIVA_DEATH)
		{
			item->anim_number = objects[item->object_number].anim_index + SHIVA_DIE_ANIM;
			item->frame_number = anims[item->anim_number].frame_base;
			item->current_anim_state = SHIVA_DEATH;
		//	items[item_number].mesh_bits -= 0x400;

		}

	}
	else
	{
		CreatureAIInfo(item, &info);

		GetCreatureMood(item, &info, VIOLENT);

		CreatureMood(item, &info, VIOLENT);

		if (shiva->mood == ESCAPE_MOOD)
		{		// TS  - come back to this and actually work out the turn angle rather than use CreatureTurn
			shiva->target.x = lara_item->pos.x_pos;
			shiva->target.z = lara_item->pos.z_pos;
		}

		angle = CreatureTurn(item, shiva->maximum_turn);



		if (item->current_anim_state != SHIVA_START) // for reload
			item->mesh_bits = 0xffffffff;

		switch (item->current_anim_state)
		{
		case SHIVA_START:
			// Make Shiva statue come to life!!
			shiva->maximum_turn = 0;

			if (!shiva->flags)
			{
				if (item->mesh_bits == 0)
					effect_mesh = 0;
				item->mesh_bits = (item->mesh_bits<<1) + 1;
				shiva->flags = 1;

				pos.x = 0;             // Copy Offsets from mesh
				pos.y = 0;             // Pivot
				pos.z = 256;
				GetJointAbsPosition(item, &pos, effect_mesh++);

				TriggerExplosionSparks(pos.x, pos.y, pos.z,2,0,0,item->room_number);
				TriggerShivaSmoke(pos.x, pos.y, pos.z,1);

			}
			else
				shiva->flags--;

			if (item->mesh_bits == 0x7fffffff)
			{
				item->goal_anim_state = SHIVA_WAIT;
				effect_mesh = 0;
				shiva->flags = -45; //set up the delay before actually moving
			}
			break;

		case SHIVA_WAIT:
			if (info.ahead)
				head_y = info.angle;
			if (shiva->flags < 0) //delay before moving
			{
				shiva->flags++;
				TriggerShivaSmoke(item->pos.x_pos+(GetRandomControl() & 0x5FF)-0x300, pos.y - (GetRandomControl() & 0x5FF), item->pos.z_pos+(GetRandomControl() & 0x5FF)-0x300,1);
				break;
			}

			if (shiva->flags == 1)
			shiva->flags = 0;

			shiva->maximum_turn = 0;

			if (shiva->mood == ESCAPE_MOOD)
			{
				room_number = item->room_number;
				x = item->pos.x_pos + (SHIVA_FEELER_DISTANCE * phd_sin(item->pos.y_rot + 0x8000) >> W2V_SHIFT);
				z = item->pos.z_pos + (SHIVA_FEELER_DISTANCE * phd_cos(item->pos.y_rot + 0x8000) >> W2V_SHIFT);
				floor = GetFloor(x, item->pos.y_pos, z, &room_number);
//				height = GetHeight(floor, x, item->pos.y_pos, z);

				if (!shiva->flags && floor->box != NO_BOX && !(boxes[floor->box].overlap_index & BLOCKABLE))
					item->goal_anim_state = SHIVA_WALKBACK;
				else
					item->goal_anim_state = SHIVA_WAIT_DEF;
			}
			else if (shiva->mood == BORED_MOOD)
			{
				random = GetRandomControl();
				if (random < SHIVA_WALK_CHANCE)
					item->goal_anim_state = SHIVA_WALK;
			}
			else if (info.bite && info.distance < SHIVA_PINCER_RANGE)
			{
				item->goal_anim_state = SHIVA_PINCER;
				shiva->flags = 0;
			}
			else if (info.bite && info.distance < SHIVA_CHOPPER_RANGE)
			{
				item->goal_anim_state = SHIVA_CHOPPER;
				shiva->flags = 0;
			}
			else if (item->hit_status && info.ahead)
			{
				shiva->flags = SHIVA_HURT_FLAG;
				item->goal_anim_state = SHIVA_WAIT_DEF;
			}
			else
				item->goal_anim_state = SHIVA_WALK;
			break;

		case SHIVA_WAIT_DEF:
			if (info.ahead)
				head_y = info.angle;

			shiva->maximum_turn = 0;
			if (item->hit_status || shiva->mood == ESCAPE_MOOD)
				shiva->flags = SHIVA_HURT_FLAG;
			if ((info.bite && info.distance < SHIVA_CHOPPER_RANGE) || (item->frame_number == anims[item->anim_number].frame_base && !shiva->flags) || !info.ahead)
			{
				item->goal_anim_state = SHIVA_WAIT;
				shiva->flags = 0;
			}
			else if (shiva->flags)
				item->goal_anim_state = SHIVA_WAIT_DEF;
			if (item->frame_number == anims[item->anim_number].frame_base && shiva->flags > 1)
				shiva->flags -= 2;
			break;

		case SHIVA_WALK:
			if (info.ahead)
				head_y = info.angle;

			shiva->maximum_turn = SHIVA_WALK_TURN;

			if (shiva->mood == ESCAPE_MOOD)
				item->goal_anim_state = SHIVA_WAIT;
			else if (shiva->mood == BORED_MOOD)
			{
				item->goal_anim_state = SHIVA_WAIT;
			}
			else if (info.bite && info.distance < SHIVA_CHOPPER_RANGE)
			{
				item->goal_anim_state = SHIVA_WAIT;
				shiva->flags = 0;
			}
			else if (item->hit_status)
			{
				shiva->flags = SHIVA_HURT_FLAG;
				item->goal_anim_state = SHIVA_WALK_DEF;
			}

			break;

		case SHIVA_WALK_DEF:
			if (info.ahead)
				head_y = info.angle;

			shiva->maximum_turn = SHIVA_WALK_TURN;

			if (item->hit_status)
				shiva->flags = SHIVA_HURT_FLAG;
			if ((info.bite && info.distance < SHIVA_PINCER_RANGE) || (item->frame_number == anims[item->anim_number].frame_base && !shiva->flags))
			{
				item->goal_anim_state = SHIVA_WALK;
				shiva->flags = 0;
			}
			else if (shiva->flags)
				item->goal_anim_state = SHIVA_WALK_DEF;
			if (item->frame_number == anims[item->anim_number].frame_base)
				shiva->flags = 0;
			break;



		case SHIVA_WALKBACK:
			if (info.ahead)
				head_y = info.angle;

			shiva->maximum_turn = SHIVA_WALK_TURN;
			if ((info.ahead && info.distance < SHIVA_CHOPPER_RANGE) || (item->frame_number == anims[item->anim_number].frame_base && !shiva->flags))
				item->goal_anim_state = SHIVA_WAIT;
			else if (item->hit_status)
			{
				shiva->flags = SHIVA_HURT_FLAG;
				item->goal_anim_state = SHIVA_WAIT;
			}


			break;


		case SHIVA_PINCER:
			if (info.ahead)
			{
				torso_y = info.angle;
				torso_x = info.x_angle;
				head_y = info.angle;
			}

			shiva->maximum_turn = SHIVA_WALK_TURN;

			ShivaDamage(item, shiva, SHIVA_PINCER_DAMAGE);
			break;

		case SHIVA_CHOPPER:
			{
				torso_y = info.angle;
				head_y = info.angle;
				if (info.x_angle > 0)
					torso_x = info.x_angle;

			}

			shiva->maximum_turn = SHIVA_WALK_TURN;

			ShivaDamage(item, shiva, SHIVA_CHOPPER_DAMAGE);
			break;

		case SHIVA_KILL:
			torso_y = torso_x = head_x = head_y = shiva->maximum_turn = 0;
			if (item->frame_number == anims[item->anim_number].frame_base + 10 ||
				item->frame_number == anims[item->anim_number].frame_base + 21 ||
				item->frame_number == anims[item->anim_number].frame_base + 33)
			{
				CreatureEffect(item, &shiva_right, DoBloodSplat);
				CreatureEffect(item, &shiva_left, DoBloodSplat);
			}
		}
	}


 	/* Is Lara dead? We've killed her! */
	if (lara_alive && lara_item->hit_points <= 0)
	{
		CreatureKill(item, SHIVA_KILL_ANIM, SHIVA_KILL, EXTRA_YETIKILL); // uses EXTRA_YETIKILL slot
		return;
	}

	CreatureTilt(item, tilt);
	head_y -= torso_y;
	CreatureJoint(item, 0, torso_y);
	CreatureJoint(item, 1, torso_x);
	CreatureJoint(item, 2, head_y);
	CreatureJoint(item, 3, head_x);

//	CreatureEffect(item, &shiva_mouth, DoBloodSplat);

	/* Actually do animation allowing for collisions */
	CreatureAnimation(item_number, angle, 0);
}

