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

extern TYPE_InitialiseCreature InitialiseCreature;
extern TYPE_CreatureCollision CreatureCollision;
extern TYPE_CreatureActive CreatureActive;
extern TYPE_CreatureAIInfo CreatureAIInfo;
extern TYPE_GetCreatureMood GetCreatureMood;
extern TYPE_CreatureMood CreatureMood;
extern TYPE_CreatureTurn CreatureTurn;
extern TYPE_SoundEffect SoundEffect;
extern TYPE_GetRandomControl GetRandomControl;
extern TYPE_CreatureEffect CreatureEffect;
extern TYPE2_DoBloodSplat DoBloodSplat_2;
extern TYPE_CreatureJoint CreatureJoint;
extern TYPE_GetWaterHeight GetWaterHeight;
extern TYPE_CreatureAnimation CreatureAnimation;

#define RAT_BITE_DAMAGE 20
#define RAT_CHARGE_DAMAGE 20
#define RAT_TOUCH 0x300018F
#define RAT_DIE_ANIM 8
#define RAT_RUN_TURN (ONE_DEGREE * 6) // = 1092
#define RAT_BITE_RANGE SQUARE(341) // = 116281
#define RAT_CHARGE_RANGE SQUARE(WALL_L * 3 / 2) // = 2359296
#define RAT_POSE_CHANCE 256
#define RAT_HITPOINTS 5
#define RAT_RADIUS (WALL_L / 5) // = 204
#define RAT_SMARTNESS 0x2000

#define VOLE_DIE_ANIM 2
#define VOLE_SWIM_TURN (ONE_DEGREE * 3) // = 546
#define VOLE_ATTACK_RANGE SQUARE(300) // = 90000

static BITE_INFO RatBite = { 0, -11, 108, 3 };

enum RAT_ANIM {
    RAT_EMPTY = 0,
    RAT_STOP = 1,
    RAT_ATTACK2 = 2,
    RAT_RUN = 3,
    RAT_ATTACK1 = 4,
    RAT_DEATH = 5,
    RAT_POSE = 6,
};

enum VOLE_ANIM {
    VOLE_EMPTY = 0,
    VOLE_SWIM = 1,
    VOLE_ATTACK = 2,
    VOLE_DEATH = 3,
};

void CtrlTR1_Rat(short item_num)
{
    if (!CreatureActive(item_num))
        return;

    ITEM_INFO* item = &Ptr_VetItems[item_num];
    CREATURE_INFO* rat = (CREATURE_INFO*) item->data;
    short head = 0;
    short angle = 0;
    short object_number = item->object_number;

	if (!item->reserved_4) {
		short dmg1 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG1);
		short dmg2 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG2);
	    short snd1 = Utils.getObjectProperty(tsu_slots[item->object_number], PTY_SND1);
        if (item->hit_points <= 0) {
            if (item->current_anim_state != RAT_DEATH) {
                item->current_anim_state = RAT_DEATH;
                item->anim_number = Ptr_VetObjects[item->object_number].anim_index + RAT_DIE_ANIM;
                item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
            }
        } else {
            AI_INFO info;
            CreatureAIInfo(item, &info);

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

			GetCreatureMood(item, &info, VIOLENT);
            CreatureMood(item, &info, VIOLENT);

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

			if (item->hit_status && snd1 != NO_ITEM) {
				SoundEffect(snd1, &item->pos, 0);
			}

            switch (item->current_anim_state) {
            case RAT_STOP:
				rat->maximum_turn = 0;
                if (item->required_anim_state) {
                    item->goal_anim_state = item->required_anim_state;
                } else if (info.bite && info.distance < RAT_BITE_RANGE) {
                    item->goal_anim_state = RAT_ATTACK1;
                } else {
                    item->goal_anim_state = RAT_RUN;
                }
                break;

            case RAT_RUN:
				rat->maximum_turn = RAT_RUN_TURN;
                if (info.ahead && (item->touch_bits & RAT_TOUCH)) {
                    item->goal_anim_state = RAT_STOP;
                } else if (info.bite && info.distance < RAT_CHARGE_RANGE) {
                    item->goal_anim_state = RAT_ATTACK2;
                } else if (info.ahead && GetRandomControl() < RAT_POSE_CHANCE) {
                    item->required_anim_state = RAT_POSE;
                    item->goal_anim_state = RAT_STOP;
                }
                break;

            case RAT_ATTACK1:
				rat->maximum_turn = RAT_RUN_TURN;
                if (item->required_anim_state == RAT_EMPTY && info.ahead
                    && (item->touch_bits & RAT_TOUCH)) {
                    CreatureEffect(item, &RatBite, DoBloodSplat_2);
                    LaraItem->hit_points -= DOUBLE_VALUE_ITEM(dmg1, RAT_BITE_DAMAGE);
                    LaraItem->hit_status = 1;
                    item->required_anim_state = RAT_STOP;
                }
                break;

            case RAT_ATTACK2:
                if (item->required_anim_state == RAT_EMPTY && info.ahead
                    && (item->touch_bits & RAT_TOUCH)) {
                    CreatureEffect(item, &RatBite, DoBloodSplat_2);
                    LaraItem->hit_points -= DOUBLE_VALUE_ITEM(dmg2, RAT_CHARGE_DAMAGE);
                    LaraItem->hit_status = 1;
                    item->required_anim_state = RAT_RUN;
                }
                break;

            case RAT_POSE:
                if (rat->mood != MOOD_BORED
                    || GetRandomControl() < RAT_POSE_CHANCE) {
                    item->goal_anim_state = RAT_STOP;
                }
                break;
            }

			int wh = GetWaterHeight(
				item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);
			if (wh != NO_HEIGHT) {
				item->reserved_4 = 1;
				item->object_number = Utils.getObjects(Utils.getLinks(tsu_slots[object_number], LNK_OBJECT1));
				item->anim_number = Ptr_VetObjects[item->object_number].anim_index;
				item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
				item->current_anim_state = Ptr_VetAnimations[item->anim_number].current_anim_state;
				item->goal_anim_state = item->current_anim_state;
				item->pos.y_pos = wh;
			}
        }

        CreatureJoint(item, 0, head);
        CreatureAnimation(item_num, angle, 0);
	} else {
        item->object_number = Utils.getObjects(Utils.getLinks(tsu_slots[object_number], LNK_OBJECT1));
		short dmg1 = Utils.getObjectAttribute(tsu_slots[item->object_number], ATT_DMG1);

        if (item->hit_points <= 0) {
            if (item->current_anim_state != VOLE_DEATH) {
                item->current_anim_state = VOLE_DEATH;
                item->anim_number = Ptr_VetObjects[item->object_number].anim_index + VOLE_DIE_ANIM;
                item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
            }
        } else {
            AI_INFO info;
            CreatureAIInfo(item, &info);

			if (!(Ptr_VetRoom[LaraItem->room_number].FlagsRoom & SUNDERWATER) && Lara.getWaterSurfaceDist() > -STEP_L)
				info.target_zone |= BLOCKED;

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

			GetCreatureMood(item, &info, VIOLENT);
            CreatureMood(item, &info, VIOLENT);

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

            switch (item->current_anim_state) {
            case VOLE_SWIM:
				rat->maximum_turn = VOLE_SWIM_TURN;
                if (info.ahead && (item->touch_bits & RAT_TOUCH)) {
                    item->goal_anim_state = VOLE_ATTACK;
                }
                break;

            case VOLE_ATTACK:
				rat->maximum_turn = 0;
                if (item->required_anim_state == VOLE_EMPTY && info.ahead
                    && (item->touch_bits & RAT_TOUCH)) {
                    CreatureEffect(item, &RatBite, DoBloodSplat_2);
                    LaraItem->hit_points -= DOUBLE_VALUE_ITEM(dmg1, RAT_BITE_DAMAGE);
                    LaraItem->hit_status = 1;
                    item->required_anim_state = VOLE_SWIM;
                }
                item->goal_anim_state = VOLE_EMPTY;
                break;
            }
        }

        int wh = GetWaterHeight(
            item->pos.x_pos, item->pos.y_pos, item->pos.z_pos, item->room_number);
        if (item->hit_points > 0 && wh == NO_HEIGHT) {
			item->reserved_4 = 0;
			item->object_number = object_number;
            item->anim_number = Ptr_VetObjects[item->object_number].anim_index;
            item->frame_number = Ptr_VetAnimations[item->anim_number].frame_base;
            item->current_anim_state =
                Ptr_VetAnimations[item->anim_number].current_anim_state;
            item->goal_anim_state = item->current_anim_state;
        }

        int height = item->pos.y_pos;
        item->pos.y_pos = item->floor;

        CreatureJoint(item, 0, head);
        CreatureAnimation(item_num, angle, 0);

        if (wh != NO_HEIGHT) {
            if (wh - height < -STEP_L / 8) {
                item->pos.y_pos = height - STEP_L / 8;
            } else if (wh - height > STEP_L / 8) {
                item->pos.y_pos = height + STEP_L / 8;
            } else {
                item->pos.y_pos = wh;
            }
        }
	}

    item->object_number = object_number;
}

void DrawTR1_Rat(ITEM_INFO* item)
{
	short object_number = item->object_number;
	if (item->reserved_4)
		item->object_number = Utils.getObjects(Utils.getLinks(tsu_slots[object_number], LNK_OBJECT1));
	DrawAnimatingItemCustom(item);
	if (item->reserved_4)
		item->object_number = object_number;
}

void InitialiseTR1_Vole()
{
    for (int index = 0; index < SLOTS_NUMBER; ++index)
    {
        tomb[TR1_GIANT_RAT_WATER1 + index].set_name("Vole");
        tomb[TR1_GIANT_RAT_WATER1 + index].initialise(TR1_GIANT_RAT_WATER1 + index, FUNCTION_SHADOW, 0);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_shadow_size(128);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_health(RAT_HITPOINTS);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_radius_and_pivot(RAT_RADIUS, 200);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_hit_effect(HIT_BLOOD);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_creature(TRUE);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_rotation_meshtree(1, ROT_Y);
        tomb[TR1_GIANT_RAT_WATER1 + index].set_save(TRUE, TRUE, TRUE, FALSE, TRUE);
    }
}

void InitialiseTR1_Rat()
{
    for (int index = 0; index < SLOTS_NUMBER; ++index)
    {
        short dependency = Utils.getLinks(TR1_GIANT_RAT_LAND1 + index, LNK_OBJECT1);
        if (dependency >= 0 && dependency < TR_OBJECTS_NUMBER && MapToFirst(dependency, SLOTS_NUMBER) == TR1_GIANT_RAT_WATER1 && tomb[dependency].get_object())
        {
            tomb[TR1_GIANT_RAT_LAND1 + index].set_name("Rat");
            tomb[TR1_GIANT_RAT_LAND1 + index].initialise(TR1_GIANT_RAT_LAND1 + index, FUNCTION_SHADOW, 0);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_shadow_size(128);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_func_initialise(InitialiseCreature);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_func_collide(CreatureCollision);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_func_control(CtrlTR1_Rat);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_func_draw(DrawTR1_Rat, NULL);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_health(RAT_HITPOINTS);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_radius_and_pivot(RAT_RADIUS, 200);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_hit_effect(HIT_BLOOD);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_creature(TRUE);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_rotation_meshtree(1, ROT_Y);
            tomb[TR1_GIANT_RAT_LAND1 + index].set_save(TRUE, TRUE, TRUE, FALSE, TRUE);
        }
    }
}
