#include <StdAfx.h>
#include "lizman.h"
#include <trng_core.h>
#include "utility.h"
#include "Class/TRClass.h"
#include "phd_math.h"

extern TYPE_CreatureCollision CreatureCollision;
extern TYPE_InitialiseCreature InitialiseCreature;
extern TYPE_CreatureActive CreatureActive;
extern TYPE_CreatureAIInfo CreatureAIInfo;
extern TYPE_GetCreatureMood GetCreatureMood;
extern TYPE_CreatureMood CreatureMood;
extern TYPE_CreatureTurn CreatureTurn;
extern TYPE_GetRandomControl GetRandomControl;
extern TYPE_Targetable Targetable;
extern TYPE_CreatureEffect CreatureEffect;
extern TYPE2_DoBloodSplat DoBloodSplat_2;
extern TYPE_CreatureTilt CreatureTilt;
extern TYPE_CreatureJoint CreatureJoint;
extern TYPE_CreatureAnimation CreatureAnimation;
extern TYPE_CreatureVault CreatureVault;
extern TYPE_CreateEffect CreateEffect;
extern TYPE2_GetJointAbsPosition GetJointAbsPosition_2;
extern TYPE_phd_GetVectorAngles phd_GetVectorAngles;
extern TYPE_GetFreeSparks GetFreeSparks;

static BITE_INFO lizman_bite_hit = { 0, -120, 120, 10 };
static BITE_INFO lizman_swipe_hit = { 0, 0, 0, 5 };
static BITE_INFO lizman_gas = { 0, -64, 56, 9 };
static short GasSlot;

enum LIZMAN_STATES
{
	LIZMAN_EMPTY,
	LIZMAN_STOP,
	LIZMAN_WALK,
	LIZMAN_PUNCH2,
	LIZMAN_AIM2,
	LIZMAN_WAIT,
	LIZMAN_AIM1,
	LIZMAN_AIM0,
	LIZMAN_PUNCH1,
	LIZMAN_PUNCH0,
	LIZMAN_RUN,
	LIZMAN_DEATH,
	LIZMAN_CLIMB3,
	LIZMAN_CLIMB1,
	LIZMAN_CLIMB2,
	LIZMAN_FALL3
};

static void TriggerLizmanGas(long x, long y, long z, long xv, long yv, long zv, long FxObj)
{
	SPARKS* sparks;
	SPARKS* sptr;
	long size;
	short slot, index;

	sparks = (SPARKS*)Trng.pGlobTomb4->BaseRemap.Old_8012E0.Start;
	slot = Utils.getObjectSprite(tsu_slots[GasSlot], SPR_SLOT);
	index = Utils.getObjectSprite(tsu_slots[GasSlot], SPR_INDEX);
	if (slot <= SLOT_LARA || slot >= SLOT_NUMBER_OBJECTS || index < 0)
		return;

	sptr = &sparks[GetFreeSparks()];
	sptr->On = 1;
	sptr->sR = 0;
	sptr->sG = (GetRandomControl() & 0x3F) + 128;
	sptr->sB = 32;
	sptr->dR = 0;
	sptr->dG = (GetRandomControl() & 0xF) + 32;
	sptr->dB = 0;

	if (xv || yv || zv)
	{
		sptr->ColFadeSpeed = 6;
		sptr->FadeToBlack = 2;
		sptr->Life = (GetRandomControl() & 1) + 12;
	}
	else
	{
		sptr->ColFadeSpeed = 8;
		sptr->FadeToBlack = 16;
		sptr->Life = (GetRandomControl() & 3) + 20;
	}

	sptr->sLife = sptr->Life;
	sptr->TransType = 2;
	sptr->extras = 0;
	sptr->Dynamic = -1;
	sptr->x = (GetRandomControl() & 0x1F) + x - 16;
	sptr->y = y;
	sptr->z = (GetRandomControl() & 0x1F) + z - 16;
	sptr->Xvel = short((GetRandomControl() & 0xF) + xv - 16);
	sptr->Yvel = (short)yv;
	sptr->Zvel = short((GetRandomControl() & 0xF) + zv - 16);
	sptr->Friction = 0;

	if (GetRandomControl() & 1)
	{
		if (FxObj < 0)
			sptr->Flags = SF_ROTATE | SF_DEF | SF_SCALE;
		else
			sptr->Flags = SF_FX | SF_ROTATE | SF_DEF | SF_SCALE;

		sptr->rotAng = GetRandomControl() & 0xFFF;

		if (GetRandomControl() & 1)
			sptr->RotAdd = -16 - (GetRandomControl() & 0xF);
		else
			sptr->RotAdd = (GetRandomControl() & 0xF) + 16;
	}
	else if (FxObj < 0)
		sptr->Flags = SF_DEF | SF_SCALE;
	else
		sptr->Flags = SF_FX | SF_DEF | SF_SCALE;

	sptr->MaxYvel = 0;
	sptr->FxObj = (uchar)FxObj;
	sptr->Gravity = 0;
	sptr->Def = Ptr_VetObjects[slot].mesh_index + index;
	size = (GetRandomControl() & 0x1F) + 48;

	if (xv || yv || zv)
	{
		sptr->Size = uchar(size >> 5);
		sptr->sSize = sptr->Size;
		sptr->dSize = uchar(size >> 1);

		if (FxObj == -2)
			sptr->Scalar = 2;
		else
			sptr->Scalar = 3;
	}
	else
	{
		sptr->Scalar = 4;
		sptr->Size = uchar(size >> 4);
		sptr->sSize = sptr->Size;
		sptr->dSize = uchar(size >> 1);
	}
}

static short TriggerLizmanGasThrower(ITEM_INFO* item, BITE_INFO* bite, short speed)
{
	FX_INFO* fx;
	StrMovePosition pos;
	StrMovePosition pos1;
	long s, r, xv, yv, zv, lp;
	short fxNum;
	short angles[2];

	fxNum = CreateEffect(item->room_number);

	if (fxNum == NO_ITEM)
		return NO_ITEM;

	fx = &Ptr_VetEffects[fxNum];

	pos.RelX = bite->x;
	pos.RelY = bite->y;
	pos.RelZ = bite->z;
	GetJointAbsPosition_2(item, &pos, bite->mesh_num);

	pos1.RelX = bite->x;
	pos1.RelY = bite->y << 3;
	pos1.RelZ = bite->z << 2;
	GetJointAbsPosition_2(item, &pos1, bite->mesh_num);

	phd_GetVectorAngles(pos1.RelX - pos.RelX, pos1.RelY - pos.RelY, pos1.RelZ - pos.RelZ, angles);

	fx->pos.x_pos = pos.RelX;
	fx->pos.y_pos = pos.RelY;
	fx->pos.z_pos = pos.RelZ;
	fx->room_number = item->room_number;
	fx->pos.x_rot = angles[1];
	fx->pos.z_rot = 0;
	fx->pos.y_rot = angles[0];
	fx->speed = speed << 2;
	fx->object_number = Utils.getObjects(Utils.getLinks(tsu_slots[item->object_number], LNK_OBJECT1));
	fx->fallspeed = 20;
	GasSlot = fx->object_number;
	TriggerLizmanGas(0, 0, 0, 0, 0, 0, fxNum);

	for (lp = 0; lp < 2; lp++)
	{
		s = GetRandomControl() % (speed << 2) + 32;
		r = (s * phd_cos_tr2(fx->pos.x_rot)) >> W2V_SHIFT;
		xv = (r * phd_sin_tr2(fx->pos.y_rot)) >> W2V_SHIFT;
		yv = -((s * phd_sin_tr2(fx->pos.x_rot)) >> W2V_SHIFT);
		zv = (r * phd_cos_tr2(fx->pos.y_rot)) >> W2V_SHIFT;
		TriggerLizmanGas(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, xv << 5, yv << 5, zv << 5, -1);
	}

	r = ((speed << 1) * phd_cos_tr2(fx->pos.x_rot)) >> W2V_SHIFT;
	xv = (r * phd_sin_tr2(fx->pos.y_rot)) >> W2V_SHIFT;
	yv = -(((speed << 1) * phd_sin_tr2(fx->pos.x_rot)) >> W2V_SHIFT);
	zv = (r * phd_cos_tr2(fx->pos.y_rot)) >> W2V_SHIFT;
	TriggerLizmanGas(fx->pos.x_pos, fx->pos.y_pos, fx->pos.z_pos, xv << 5, yv << 5, zv << 5, -2);

	return fxNum;
}

void CtrlTR3_LizMan(short item_number)
{
	ITEM_INFO* item;
	CREATURE_INFO* lizman;
	AI_INFO info;
	short tilt, angle, neck, f;
	short dmg1, dmg2;

	if (!CreatureActive(item_number))
		return;

	item = &Ptr_VetItems[item_number];
	lizman = (CREATURE_INFO*)item->data;
	tilt = 0;
	angle = 0;
	neck = 0;
	dmg1 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG1);
    dmg2 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG2);

	if (item->hit_points <= 0)
	{
		if (item->current_anim_state != LIZMAN_DEATH)
		{
			item->anim_number = Ptr_VetObjects[item->object_number].anim_index + 26;
			item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
			item->current_anim_state = LIZMAN_DEATH;
		}
	}
	else
	{
		CreatureAIInfo(item, &info);
		GetCreatureMood(item, &info, VIOLENT);
		CreatureMood(item, &info, VIOLENT);
		angle = CreatureTurn(item, lizman->maximum_turn);

		switch (item->current_anim_state)
		{
		case LIZMAN_STOP:
			lizman->flags = 0;

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 0;

			if (lizman->mood == MOOD_ESCAPE)
				item->goal_anim_state = LIZMAN_RUN;
			else if (lizman->mood == MOOD_BORED)
			{
				if (item->required_anim_state)
					item->goal_anim_state = item->required_anim_state;
				else if (GetRandomControl() < 0x4000)
					item->goal_anim_state = LIZMAN_WALK;
				else
					item->goal_anim_state = LIZMAN_WAIT;
			}
			else if (info.bite && info.distance < 0x90000)
				item->goal_anim_state = LIZMAN_AIM1;
			else if (Targetable(item, &info) && info.bite && info.distance < 0x510000 && Utils.GetPoison() < 256)
				item->goal_anim_state = LIZMAN_AIM0;
			else
				item->goal_anim_state = LIZMAN_RUN;

			break;

		case LIZMAN_WALK:

			if (info.ahead)
				neck = info.angle;

			if (item->anim_number == Ptr_VetObjects[item->object_number].anim_index + 23 || item->anim_number == Ptr_VetObjects[item->object_number].anim_index + 31)
				lizman->maximum_turn = 0;
			else
				lizman->maximum_turn = 1820;

			if (lizman->mood == MOOD_ESCAPE)
				item->goal_anim_state = LIZMAN_RUN;
			else if (lizman->mood == MOOD_BORED)
			{
				if (GetRandomControl() < 256)
				{
					item->required_anim_state = LIZMAN_WAIT;
					item->goal_anim_state = LIZMAN_STOP;
				}
			}
			else if (info.bite && info.distance < 0x90000)
				item->goal_anim_state = LIZMAN_STOP;
			else if (info.bite && info.distance < 0x240000)
				item->goal_anim_state = LIZMAN_AIM2;
			else if (Targetable(item, &info) && info.distance < 0x510000 && Utils.GetPoison() < 256)
				item->goal_anim_state = LIZMAN_STOP;
			else if (info.distance > 0x400000)
				item->goal_anim_state = LIZMAN_RUN;

			break;

		case LIZMAN_PUNCH2:

			if (info.ahead)
				neck = info.angle;

			if (lizman->flags != 2 && item->touch_bits & 0xC00)
			{
				LaraItem->hit_points -= DOUBLE_VALUE_ITEM(dmg1, 100);
				LaraItem->hit_status = 1;
				CreatureEffect(item, &lizman_bite_hit, DoBloodSplat_2);
				lizman->flags = 2;
			}

			break;

		case LIZMAN_AIM2:

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 1820;
			lizman->flags = 0;

			if (info.bite && info.distance < 0x240000)
				item->goal_anim_state = LIZMAN_PUNCH2;
			else
				item->goal_anim_state = LIZMAN_WALK;

			break;

		case LIZMAN_WAIT:

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 0;

			if (lizman->mood != MOOD_BORED)
				item->goal_anim_state = LIZMAN_STOP;
			else if (GetRandomControl() < 256)
			{
				item->required_anim_state = LIZMAN_WALK;
				item->goal_anim_state = LIZMAN_STOP;
			}

			break;

		case LIZMAN_AIM1:

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 1820;
			lizman->flags = 0;

			if (info.ahead && info.distance < 0x90000)
				item->goal_anim_state = LIZMAN_PUNCH1;
			else
				item->goal_anim_state = LIZMAN_STOP;
			
			break;

		case LIZMAN_AIM0:

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 0;

			if (ABS(info.angle) < 728)
				item->pos.y_rot += info.angle;
			else if (info.angle < 0)
				item->pos.y_rot -= 728;
			else
				item->pos.y_rot += 728;

			lizman->flags = 0;

			if (info.bite && info.distance < 0x510000 && Utils.GetPoison() < 256)
				item->goal_anim_state = LIZMAN_PUNCH0;
			else
				item->goal_anim_state = LIZMAN_STOP;

			break;

		case LIZMAN_PUNCH1:
			if (info.ahead)
				neck = info.angle;

			if (!lizman->flags && item->touch_bits & 0x20)
			{
				LaraItem->hit_points -= DOUBLE_VALUE_ITEM(dmg2, 120);
				LaraItem->hit_status = 1;
				CreatureEffect(item, &lizman_swipe_hit, DoBloodSplat_2);
				lizman->flags = 1;
			}

			if (info.distance < 0x240000)
				item->goal_anim_state = LIZMAN_PUNCH2;

			break;

		case LIZMAN_PUNCH0:

			if (info.ahead)
				neck = info.angle;

			if (ABS(info.angle) < 728)
				item->pos.y_rot += info.angle;
			else if (info.angle < 0)
				item->pos.y_rot -= 728;
			else
				item->pos.y_rot += 728;

			if (item->frame_number >= Ptr_VetAnimations[item->anim_number].frame_base + 7 && item->frame_number <= Ptr_VetAnimations[item->anim_number].frame_base + 28)
			{
				if (lizman->flags < 24)
					lizman->flags += 2;

				if (lizman->flags < 24)
					f = lizman->flags;
				else
					f = (GetRandomControl() & 0xF) + 8;

				TriggerLizmanGasThrower(item, &lizman_gas, f);
			}

			if (item->frame_number > Ptr_VetAnimations[item->anim_number].frame_base + 28)
				lizman->flags = 0;

			break;

		case LIZMAN_RUN:

			if (info.ahead)
				neck = info.angle;

			lizman->maximum_turn = 728;
			tilt = angle / 2;

			if (lizman->mood != MOOD_ESCAPE)
			{
				if (lizman->mood == MOOD_BORED)
					item->goal_anim_state = LIZMAN_WALK;
				else if (info.bite && info.distance < 0x90000)
					item->goal_anim_state = LIZMAN_STOP;
				else if (Targetable(item, &info) && info.distance < 0x510000 && Utils.GetPoison() < 256)
					item->goal_anim_state = LIZMAN_STOP;
				else if (info.ahead && info.distance < 0x400000)
					item->goal_anim_state = LIZMAN_WALK;
			}

			break;
		}
	}

	CreatureTilt(item, tilt);
	CreatureJoint(item, 1, neck);

	if (item->current_anim_state >= LIZMAN_DEATH)
		CreatureAnimation(item_number, angle, 0);
	else
	{
		switch (CreatureVault(item_number, angle, 2, 260))
		{
		case -4:
			lizman->maximum_turn = 0;
			item->anim_number = Ptr_VetObjects[item->object_number].anim_index + 30;
			item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
			item->current_anim_state = LIZMAN_FALL3;
			break;

		case 2:
			lizman->maximum_turn = 0;
			item->anim_number = Ptr_VetObjects[item->object_number].anim_index + 28;
			item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
			item->current_anim_state = LIZMAN_CLIMB1;
			break;

		case 3:
			lizman->maximum_turn = 0;
			item->anim_number = Ptr_VetObjects[item->object_number].anim_index + 29;
			item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
			item->current_anim_state = LIZMAN_CLIMB2;
			break;

		case 4:
			lizman->maximum_turn = 0;
			item->anim_number = Ptr_VetObjects[item->object_number].anim_index + 27;
			item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
			item->current_anim_state = LIZMAN_CLIMB3;
			break;
		}
	}
}

void InitialiseTR3_LizMan()
{
    for (int index = 0; index < SLOTS_NUMBER; ++index)
    {
        short dependency = Utils.getLinks(TR3_LIZARD1 + index, LNK_OBJECT1);
        if (dependency >= 0 && dependency < TR_OBJECTS_NUMBER && MapToFirst(dependency, SLOTS_NUMBER) == TR3_GAS1 && tomb[dependency].get_object())
		{
			tomb[TR3_LIZARD1 + index].set_name("Lizard");
			tomb[TR3_LIZARD1 + index].initialise(TR3_LIZARD1 + index, FUNCTION_ZONE_BEFORE | FUNCTION_SHADOW, QUERY_VAULT);
			tomb[TR3_LIZARD1 + index].set_shadow_size(128);
			tomb[TR3_LIZARD1 + index].set_func_initialise(InitialiseCreature);
			tomb[TR3_LIZARD1 + index].set_func_collide(CreatureCollision);
			tomb[TR3_LIZARD1 + index].set_func_control(CtrlTR3_LizMan);
			tomb[TR3_LIZARD1 + index].set_func_draw(DrawAnimatingItemCustom, NULL);
			tomb[TR3_LIZARD1 + index].set_health(36);
			tomb[TR3_LIZARD1 + index].set_radius_and_pivot(204, 0);
			tomb[TR3_LIZARD1 + index].set_hit_effect(HIT_BLOOD);
			tomb[TR3_LIZARD1 + index].set_creature(TRUE);
			tomb[TR3_LIZARD1 + index].set_rotation_meshtree(1, ROT_Z);
			tomb[TR3_LIZARD1 + index].set_rotation_meshtree(9, ROT_Z);
			tomb[TR3_LIZARD1 + index].set_save(TRUE, TRUE, TRUE, FALSE, TRUE);
		}
    }
}
