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

extern TYPE_InitialiseCreature InitialiseCreature;
extern TYPE_CreatureActive CreatureActive;
extern TYPE_CreatureAnimation CreatureAnimation;
extern TYPE_GetCreatureMood GetCreatureMood;
extern TYPE_CreatureMood CreatureMood;
extern TYPE_CreatureTurn CreatureTurn;
extern TYPE_CreatureAIInfo CreatureAIInfo;
extern TYPE_CreatureEffect CreatureEffect;
extern TYPE_CreatureCollision CreatureCollision;
extern TYPE2_DoBloodSplat DoBloodSplat_2;
extern TYPE_DrawAnimatingItem DrawAnimatingItem;
extern TYPE_GetFrames GetFrames;
extern TYPE_S_PrintShadow S_PrintShadow;
extern TYPE_phd_PushMatrix phd_PushMatrix;
extern TYPE_phd_TranslateAbs phd_TranslateAbs;
extern TYPE_phd_TranslateRel phd_TranslateRel;
extern TYPE_phd_RotYXZ phd_RotYXZ;
extern TYPE_phd_RotY phd_RotY;
extern TYPE_phd_RotX phd_RotX;
extern TYPE_phd_RotZ phd_RotZ;
extern TYPE_phd_RotY_I phd_RotY_I;
extern TYPE_phd_RotX_I phd_RotX_I;
extern TYPE_phd_RotZ_I phd_RotZ_I;
extern TYPE_CreatureTilt CreatureTilt;
extern TYPE_ExplodingDeath ExplodingDeath;
extern TYPE_gar_RotYXZsuperpack gar_RotYXZsuperpack;
extern TYPE2_S_GetObjectBounds S_GetObjectBounds_2;
extern TYPE_CalculateObjectLighting CalculateObjectLighting;
extern TYPE_phd_PutPolygons phd_PutPolygons;
extern TYPE_phd_TranslateRel_ID phd_TranslateRel_ID;
extern TYPE_gar_RotYXZsuperpack_I gar_RotYXZsuperpack_I;
extern TYPE_phd_PutPolygons_I phd_PutPolygons_I;
extern TYPE_phd_PushMatrix_I phd_PushMatrix_I;
extern TYPE_phd_TranslateRel_I phd_TranslateRel_I;
extern TYPE_CreatureJoint CreatureJoint;
extern TYPE_InitInterpolate InitInterpolate;
extern TYPE_KillItem KillItem;
extern TYPE2_TriggerActive TriggerActive_2;
extern TYPE_DisableBaddieAI DisableBaddieAI;
extern TYPE_CreatureKill CreatureKill;
extern TYPE_GetRandomControl GetRandomControl;
extern TYPE_phd_PopMatrix_I phd_PopMatrix_I;
extern TYPE_SoundEffect SoundEffect;

#define HP_SPEAR_GUARDIAN (100)
#define SPEAR_DAMAGE 75
#define SPEAR_LONG_DAMAGE 120
#define SPEAR_STATUE_ANIM 48
#define SPEAR_SPECIAL_KILL_ANIM 49
#define SPEAR_WALK_TURN (546)
#define SPEAR_RUN_TURN (910)
#define SPEAR_RUN_DISTANCE (0x900000)
#define SPEAR_MIDDLEATTACK_DISTANCE (0x240000)
#define SPEAR_MIDDLELONGATTACK_DISTANCE (0x300000)
#define SPEAR_LONGATTACK_DISTANCE (0x400000)
#define SPEAR_WALK_DISTANCE (0x100000)
#define SPEAR_TOUCH_RIGHT (0x800)
#define SPEAR_TOUCH_LEFT (0x40000)

static BITE_INFO spear_biteL = { 0, 0, 920, 18 };
static BITE_INFO spear_biteR = { 0, 0, 920, 11 };

enum XIAN_SPEAR_STATE {
    ST_SPEAR_EMPTY1,
    ST_SPEAR_STOP,
    ST_SPEAR_WAIT,
    ST_SPEAR_WALK,
    ST_SPEAR_RUN,
    ST_SPEAR_AIM1,
    ST_SPEAR_ATTACK1,
    ST_SPEAR_AIM2,
    ST_SPEAR_ATTACK2,
    ST_SPEAR_AIM3,
    ST_SPEAR_ATTACK3,
    ST_SPEAR_AIM4,
    ST_SPEAR_ATTACK4,
    ST_SPEAR_AIM5,
    ST_SPEAR_ATTACK5,
    ST_SPEAR_AIM6,
    ST_SPEAR_ATTACK6,
    ST_SPEAR_DEATH,
    ST_SPEAR_AWAKE,
    ST_SPEAR_SPECIAL_KILL
};

void SpearGuardian_CheckDamage(ITEM_INFO* item, CREATURE_INFO* creature, int damage)
{
    short snd1, snd3, snd4, snd5, snd6, snd7, snd;

    snd1 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND1);
    snd3 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND3);
    snd4 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND4);
    snd5 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND5);
    snd6 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND6);
    snd7 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND7);

	if (item->current_anim_state == ST_SPEAR_ATTACK1)
		snd = snd1;
	else if (item->current_anim_state == ST_SPEAR_ATTACK2)
		snd = snd3;
	else if (item->current_anim_state == ST_SPEAR_ATTACK3)
		snd = snd4;
	else if (item->current_anim_state == ST_SPEAR_ATTACK4)
		snd = snd5;
	else if (item->current_anim_state == ST_SPEAR_ATTACK5)
		snd = snd6;
	else
		snd = snd7;

    if (!(creature->flags & 1) && (item->touch_bits & SPEAR_TOUCH_LEFT))
    {
        LaraItem->hit_points -= damage;
        LaraItem->hit_status = 1;
        CreatureEffect(item, &spear_biteL, DoBloodSplat_2);
        creature->flags |= 1;
        if (snd != NO_ITEM)
            SoundEffect(snd, &item->pos, 0);
    }

    if (!(creature->flags & 2) && (item->touch_bits & SPEAR_TOUCH_RIGHT))
    {
        LaraItem->hit_points -= damage;
        LaraItem->hit_status = 1;
        CreatureEffect(item, &spear_biteR, DoBloodSplat_2);
        creature->flags |= 2;
        if (snd != NO_ITEM)
            SoundEffect(snd, &item->pos, 0);
    }
}

void CtrlTR2_SpearGuardian(short ItemIndex)
{
    if (!CreatureActive(ItemIndex))
        return;

    ITEM_INFO* item;
    OBJECT_INFO* slot;
    CREATURE_INFO* spear;
    AI_INFO info;
    short angle, head, torso, tilt;
    int rnd;
    bool lara_alive;
    short dmg1, dmg2, dmg3, dmg4, dmg5, dmg6, anim, snd2;

    item = &Ptr_VetItems[ItemIndex];
    slot = &Ptr_VetObjects[item->object_number];
    spear = (CREATURE_INFO*)item->data;
    angle = head = torso = tilt = 0;
    lara_alive = LaraItem->hit_points > 0;
    dmg1 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG1);
    dmg2 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG2);
    dmg3 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG3);
    dmg4 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG4);
    dmg5 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG5);
    dmg6 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG6);
    anim = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_ANIM);
    snd2 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND2);

    if (item->hit_points <= 0)
    {
        item->meshswap_meshbits >>= 1;
        item->current_anim_state = ST_SPEAR_DEATH;
        if (!item->meshswap_meshbits)
        {
            if (snd2 != NO_ITEM)
                SoundEffect(snd2, &item->pos, 0);
            ExplodingDeath(ItemIndex, -1, 256);
            DisableBaddieAI(ItemIndex);
            KillItem(ItemIndex);
            item->status = STS_DESACTIVATED;
        }
    }
    else
    {
        CreatureAIInfo(item, &info);
        GetCreatureMood(item, &info, VIOLENT);
        CreatureMood(item, &info, VIOLENT);
        angle = CreatureTurn(item, spear->maximum_turn);

        switch (item->current_anim_state)
        {
            case ST_SPEAR_STOP:
                spear->maximum_turn = 0;

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

                if (spear->mood != MOOD_BORED)
                {
                    if (!info.ahead || info.distance >= SPEAR_WALK_DISTANCE)
                        item->goal_anim_state = ST_SPEAR_WALK;
                    else
                        item->goal_anim_state = ST_SPEAR_AIM1;
                }
                else
                {
                    rnd = GetRandomControl();
                    if (rnd >= 0x200)
                    {
                        if (rnd < 0x400)
                            item->goal_anim_state = ST_SPEAR_WALK;
                    }
                    else
                        item->goal_anim_state = ST_SPEAR_WAIT;
                }
                break;

            case ST_SPEAR_WAIT:
                spear->maximum_turn = 0;

                if (info.ahead)
                    head = info.angle;
                
                if (spear->mood == MOOD_ESCAPE)
                {
                    item->goal_anim_state = ST_SPEAR_WALK;
                }
                else if (spear->mood != MOOD_BORED)
                {
                    if (!info.ahead || info.distance >= SPEAR_WALK_DISTANCE)
                        item->goal_anim_state = ST_SPEAR_WALK;
                    else
                        item->goal_anim_state = ST_SPEAR_AIM5;
                }
                else
                {
                    rnd = GetRandomControl();
                    if (rnd >= 0x200)
                    {
                        if (rnd < 0x400)
                            item->goal_anim_state = ST_SPEAR_WALK;
                    }
                    else
                    {
                        item->goal_anim_state = ST_SPEAR_STOP;
                    }
                }
                break;

            case ST_SPEAR_WALK:
                spear->maximum_turn = SPEAR_WALK_TURN;

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

                if (spear->mood == MOOD_ESCAPE)
                {
                    item->goal_anim_state = ST_SPEAR_RUN;
                }
                else if (spear->mood != MOOD_BORED)
                {
                    if (info.ahead && info.distance < SPEAR_LONGATTACK_DISTANCE)
                    {
                        if (info.distance >= SPEAR_MIDDLEATTACK_DISTANCE)
                        {
                            if (GetRandomControl() >= 0x4000)
                                item->goal_anim_state = ST_SPEAR_AIM4;
                            else
                                item->goal_anim_state = ST_SPEAR_AIM3;
                        }
                        else
                        {
                            item->goal_anim_state = ST_SPEAR_AIM2;
                        }
                    }
                    else if (!info.ahead || info.distance > SPEAR_RUN_DISTANCE)
                    {
                        item->goal_anim_state = ST_SPEAR_RUN;
                    }
                }
                else
                {
                    rnd = GetRandomControl();
                    if (rnd >= 0x200)
                    {
                        if (rnd < 0x400)
                            item->goal_anim_state = ST_SPEAR_WAIT;
                    }
                    else
                    {
                        item->goal_anim_state = ST_SPEAR_STOP;
                    }
                }
                break;

            case ST_SPEAR_RUN:
                spear->maximum_turn = SPEAR_RUN_TURN;

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

                if (spear->mood != MOOD_ESCAPE)
                {
                    if (spear->mood != MOOD_BORED)
                    {
                        if (info.ahead && info.distance < SPEAR_LONGATTACK_DISTANCE)
                            item->goal_anim_state = ST_SPEAR_AIM6;
                    }
                    else
                    {
                        if (GetRandomControl() >= 0x4000)
                            item->goal_anim_state = ST_SPEAR_WAIT;
                        else
                            item->goal_anim_state = ST_SPEAR_STOP;
                    }
                }
                break;

            case ST_SPEAR_AIM1:
                spear->flags = 0;
                
                if (info.ahead)
                    torso = info.angle;

                if (!info.ahead || info.distance > SPEAR_WALK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_STOP;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK1;
                break;

            case ST_SPEAR_AIM2:
                spear->flags = 0;

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

                if (!info.ahead || info.distance > SPEAR_MIDDLEATTACK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_WALK;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK2;
                break;

            case ST_SPEAR_AIM3:
                spear->flags = 0;

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

                if (!info.ahead || info.distance > SPEAR_LONGATTACK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_WALK;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK2;
                break;

            case ST_SPEAR_AIM4:
                spear->flags = 0;

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

                if (!info.ahead || info.distance > SPEAR_LONGATTACK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_WALK;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK2;
                break;

            case ST_SPEAR_AIM5:
                spear->flags = 0;

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

                if (!info.ahead || info.distance > SPEAR_WALK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_WAIT;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK5;
                break;

            case ST_SPEAR_AIM6:
                spear->flags = 0;

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

                if (!info.ahead || info.distance > SPEAR_LONGATTACK_DISTANCE)
                    item->goal_anim_state = ST_SPEAR_RUN;
                else
                    item->goal_anim_state = ST_SPEAR_ATTACK6;
                break;

            case ST_SPEAR_ATTACK1:
                SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg1, SPEAR_DAMAGE));
                break;

            case ST_SPEAR_ATTACK2:
            case ST_SPEAR_ATTACK3:
            case ST_SPEAR_ATTACK4:
                if (info.ahead)
                    torso = info.angle;

				if (item->current_anim_state == ST_SPEAR_ATTACK2)
					SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg4, SPEAR_DAMAGE));
				else if (item->current_anim_state == ST_SPEAR_ATTACK3)
					SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg5, SPEAR_DAMAGE));
				else
					SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg6, SPEAR_DAMAGE));

                if (!info.ahead || info.distance >= SPEAR_WALK_DISTANCE)
                {
                    item->goal_anim_state = ST_SPEAR_WALK;
                }
                else
                {
                    rnd = GetRandomControl();
                    if (rnd >= 0x4000)
                        item->goal_anim_state = ST_SPEAR_WAIT;
                    else
                        item->goal_anim_state = ST_SPEAR_STOP;
                }
                break;

            case ST_SPEAR_ATTACK5:
                if (info.ahead)
                    torso = info.angle;

                SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg2, SPEAR_DAMAGE));

                if (info.ahead)
                {
                    if (info.distance < SPEAR_WALK_DISTANCE)
                        item->goal_anim_state = ST_SPEAR_STOP;
                    else
                        item->goal_anim_state = ST_SPEAR_WAIT;
                }
                else
                {
                    item->goal_anim_state = ST_SPEAR_WAIT;
                }
                break;

            case ST_SPEAR_ATTACK6:
                if (info.ahead)
                    torso = info.angle;

                SpearGuardian_CheckDamage(item, spear, DOUBLE_VALUE_ITEM(dmg3, SPEAR_LONG_DAMAGE));

                if (info.ahead && info.distance < SPEAR_WALK_DISTANCE)
                {
                    rnd = GetRandomControl();
                    if (rnd >= 0x4000)
                        item->goal_anim_state = ST_SPEAR_WAIT;
                    else
                        item->goal_anim_state = ST_SPEAR_STOP;
                }
                else if (info.ahead && info.distance < SPEAR_LONGATTACK_DISTANCE)
                {
                    item->goal_anim_state = ST_SPEAR_WALK;
                }
                else
                {
                    item->goal_anim_state = ST_SPEAR_RUN;
                }
                break;

            case ST_SPEAR_AWAKE:
                spear->maximum_turn = 0;

                if (!spear->flags)
                {
                    if (!item->meshswap_meshbits)
                        item->poisoned = false;
                    item->meshswap_meshbits = (item->meshswap_meshbits << 1) + 1;
                    spear->flags = 3;
                }
                else
                {
                    spear->flags--;
                }
                break;
        }

        // special kill animation:
        if (lara_alive && LaraItem->hit_points <= 0 && anim != NO_ITEM)
        {
            spear->maximum_turn = 0;
            CreatureKill(item, SPEAR_SPECIAL_KILL_ANIM, ST_SPEAR_SPECIAL_KILL, anim);
			tsu_killer = tsu_slots[item->object_number];
        }
        // normal animation:
        else
        {
            CreatureTilt(item, tilt);
            CreatureJoint(item, 0, torso);
            CreatureJoint(item, 1, head);
            CreatureAnimation(ItemIndex, angle, 0);
        }
    }
}

void InitTR2_SpearGuardian(short ItemIndex)
{
    ITEM_INFO* item = &Ptr_VetItems[ItemIndex];
    InitialiseCreature(ItemIndex);
    item->anim_number = Ptr_VetObjects[item->object_number].anim_index + SPEAR_STATUE_ANIM;
    ANIM_STRUCT* anims = &Ptr_VetAnimations[item->anim_number];
    item->frame_number = anims->frame_base;
    item->current_anim_state = anims->current_anim_state;
    item->goal_anim_state = anims->current_anim_state;
    item->status = STS_NOT_ACTIVE;
    item->really_active = FALSE; // cant be targeted unless is triggered.
}

void DrawTR2_XianSpearGuardian(ITEM_INFO* item)
{
    DrawAnimatingItem(item);
}

void InitialiseTR2_SpearGuardianStatue()
{
    for (int index = 0; index < SLOTS_NUMBER; ++index)
    {
        tomb[TR2_SPEAR_XIAN_GUARD_STATUE1 + index].set_name("Spear Guardian Statue");
        tomb[TR2_SPEAR_XIAN_GUARD_STATUE1 + index].initialise(TR2_SPEAR_XIAN_GUARD_STATUE1 + index, 0, 0);
    }
}

void InitialiseTR2_SpearGuardian()
{
    for (int index = 0; index < SLOTS_NUMBER; ++index)
    {
        short dependency = Utils.getLinks(TR2_SPEAR_XIAN_GUARD1 + index, LNK_OBJECT1);
        if (dependency >= 0 && dependency < TR_OBJECTS_NUMBER && MapToFirst(dependency, SLOTS_NUMBER) == TR2_SPEAR_XIAN_GUARD_STATUE1)
        {
            OBJECT_INFO* object_statue = tomb[dependency].get_object();
            if (object_statue)
            {
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_name("Spear Guardian");
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].initialise(TR2_SPEAR_XIAN_GUARD1 + index, FUNCTION_SHADOW, QUERY_ZOMBIE | QUERY_KILLER);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_shadow_size(128);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_func_initialise(InitTR2_SpearGuardian);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_func_collide(CreatureCollision);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_func_control(CtrlTR2_SpearGuardian);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_func_draw(DrawTR2_XianSpearGuardian, NULL);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_health(HP_SPEAR_GUARDIAN);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_radius_and_pivot(204, 0);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_hit_effect(HIT_BLOOD);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_creature(TRUE);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_rotation_meshtree(6, ROT_Y);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_rotation_meshtree(12, ROT_Y);
                tomb[TR2_SPEAR_XIAN_GUARD1 + index].set_save(TRUE, TRUE, TRUE, TRUE, TRUE);

                OBJECT_INFO* object = tomb[TR2_SPEAR_XIAN_GUARD1 + index].get_object();
                if (object)
                {
                    for (int mesh = 0; mesh < object->nmeshes; ++mesh)
                        Ptr_VetMeshes[object->mesh_index + mesh * 2] = Ptr_VetMeshes[object_statue->mesh_index + mesh * 2];
                }
            }
        }
    }
}