/*
 * pop_guard.c — СОСТОЯНИЕ и ЛОГИКА стража (W1/W2).  См. pop_guard.h.
 *
 * Здесь только то, что должно оставаться достижимым из банка (guards.c):
 * поля Guard, HP, вход в комнату, чит-убийство, чтение кадра из таблицы.
 * ОТРИСОВКА — в pop_gdraw.c, резиденте W3 (её зовёт лишь главный цикл).
 */
#include <stdint.h>
#include "pop_guard.h"
#include "pop_geom.h"
#include "pop_level.h"
#include "pop_kid.h"          /* Char/play_seq — интерпретатор общий */
#include "pop_map.h"          /* hitp_* — HP Кида */
#include "kid_data.h"

pop_char_t Guard;
uint8_t  guardhp_curr, guardhp_max;
int8_t   guardhp_delta;
int8_t   can_guard_see_kid;
int8_t   is_guard_notice;
uint8_t  guard_skill;
int8_t   guard_refrac, justblocked, kid_sword_strike;
int8_t   offguard, holding_sword;

/* Сид бросков боёвки.  В оригинале генератор один на всё, у нас
 * последовательности разведены по доменам (кладка / анимация тайлов / бой),
 * см. pop_geom.h — иначе перерисовка стены сбивала бы броски стража. */
pop_rnd_t pop_fight_seed;

/* Кадр стража (порт cur_frame для Char).  Глобальный, а не статик: его
 * заполняет логика (W1/W2), а читает отрисовка из резидента W3. */
kframe pop_gframe;

uint8_t pop_guard_present(void)
{
    return (uint8_t)(Guard.charid != 0 && Guard.alive < 0 && guardhp_curr != 0);
}

void pop_guard_reset(void)
{
    Guard.charid = 0;
    Guard.alive = 0;
    Guard.direction = 0;
    guardhp_curr = 0;
    guardhp_delta = 0;
    can_guard_see_kid = 0;
    is_guard_notice = 0;
    guard_refrac = justblocked = kid_sword_strike = 0;
    offguard = holding_sword = 0;
}

/* kill guard cheat (seg000:786):
 *     if (Guard.charid != charid_4_skeleton) {
 *         guardhp_delta = -guardhp_curr;
 *         Guard.alive = 0;
 *     }
 * Скелета чит не убивает — он и в оригинале бессмертен. */
void pop_guard_kill(void)
{
    if (Guard.charid == CHARID_4_SKELETON) return;
    if (!pop_guard_present()) return;
    /* Как в оригинале: HP НЕ обнуляем здесь — ставим дельту, её применит
     * pop_do_delta_hp.  alive = 0 переводит стража в «умирает», а дальше
     * общий control() ставит ему seq_71_dying. */
    guardhp_delta = (int8_t)-guardhp_curr;
    Guard.alive = 0;
}

/* do_delta_hp (seg003): применить накопленную за кадр дельту HP и обнулить
 * её.  Звать раз в кадр ПОСЛЕ тиков персонажей, как в play_frame. */
void pop_do_delta_hp(void)
{
    int16_t hp;
    if (hitp_delta) {
        hp = (int16_t)hitp_curr + hitp_delta;
        if (hp < 0) hp = 0;
        if (hp > (int16_t)hitp_max) hp = (int16_t)hitp_max;
        hitp_curr = (uint8_t)hp;
        pop_kid_hurt = 1;               /* -> «брызги» поверх Кида */
        hitp_delta = 0;
    }
    if (guardhp_delta) {
        hp = (int16_t)guardhp_curr + guardhp_delta;
        if (hp < 0) hp = 0;
        if (hp > (int16_t)guardhp_max) hp = (int16_t)guardhp_max;
        guardhp_curr = (uint8_t)hp;
        guardhp_delta = 0;
    }
}

/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у
 * стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где
 * add_frame = 70 для кадров 102..106 (seg006:535). */
void pop_guard_load_frame(void)
{
    int16_t idx = (int16_t)Guard.frame;
    if (idx >= 102 && idx < 107) idx += 70;      /* add_frame */
    idx -= 149;
    if (idx < 0 || idx >= KID_NGFRAMES) { pop_gframe.image = 255; return; }
    pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&pop_gframe);
}

/* leave_guard (seg002:02F5): вызывать ПЕРЕД сменой комнаты, пока Guard ещё
 * держит стража уходящей комнаты.  Мёртвого запоминаем вместе с
 * последовательностью — тогда при возврате он лежит там же, а не встаёт
 * живым. */
void pop_guard_leave(void)
{
    if (Guard.charid == 0) return;              /* стража не было */
    pop_guard_state_save(Guard.room,
                         (uint8_t)(Guard.curr_row * 10),   /* get_tilepos(0,row) */
                         Guard.direction, Guard.x, guard_skill,
                         Guard.curr_seq,
                         (uint8_t)(Guard.alive >= 0));
}

/* get_tile_div_mod_m7 (seg006:697): колонка комнаты по X персонажа —
 * floor((xpos − 7 − 58) / 14), где 58 = pop_x_bump[FIRST_ONSCREEN_COLUMN]. */
static int8_t guard_col_from_x(uint8_t xpos)
{
    int16_t x = (int16_t)xpos - 7 - pop_x_bump[FIRST_ONSCREEN_COLUMN];
    int16_t col = x / TILE_SIZEX;
    if (x % TILE_SIZEX < 0) --col;
    return (int8_t)col;
}

void pop_guard_enter(uint8_t room)
{
    uint8_t tile, color, skill;
    int8_t dir;
    pop_guard_reset();
    /* load_lev_spr (seg000:1112): tbl_guard_type == −1 — стражей на уровне
     * НЕТ вообще (14/15), их графика даже не грузится.  Без этой проверки
     * на таком уровне полезли бы стражи из данных комнат. */
    if (pop_level_guard_type(pop_current_level) < 0) return;
    if (!pop_level_guard(room, &tile, &dir, &color, &skill)) return;
    /* enter_guard (seg002:0112) + pos_guards (seg003): X считается из
     * колонки тайла, а не берётся из файла. */
    Guard.room      = room;
    Guard.curr_row  = (int8_t)(tile / 10);
    Guard.y         = (uint8_t)pop_y_land[Guard.curr_row + 1];
    Guard.x         = (uint8_t)(pop_x_bump[(tile % 10) + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX);
    Guard.curr_col  = (int8_t)(tile % 10);
    Guard.direction = dir;
    Guard.charid    = CHARID_2_GUARD;
    Guard.sword     = 0;                     /* sword_0_sheathed */
    Guard.alive     = -1;
    Guard.action    = 1;                     /* actions_1_run_jump */
    Guard.fall_x = Guard.fall_y = 0;
    /* enter_guard (seg002:214): мастерство из данных уровня, 12 градаций;
     * всё, что вне диапазона, оригинал сводит к 3. */
    guard_skill = (uint8_t)(skill < NUM_GUARD_SKILLS ? skill : 3);
    /* get_guard_hp (seg002:54): extrastrength[skill] + tbl_guard_hp[уровень].
     * extrastrength (seg002:38) ненулевой только у skill 4 (=1). */
    guardhp_max = guardhp_curr =
        (uint8_t)(pop_level_guard_hp(pop_current_level) + (guard_skill == 4 ? 1 : 0));
    guardhp_delta = 0;
    /* enter_guard (seg002:0208).  Живого поднимаем стандартной стойкой; у
     * ЗАПОМНЕННОГО МЁРТВОГО берём сохранённую последовательность — он лежит
     * ровно там, где лёг (порт ветки guards_seq_hi != 0). */
    {
        uint16_t saved_seq = pop_guard_state_seq(room);
        /* У ЗАПОМНЕННОГО трупа X берём сохранённую: pos_guards (seg003:620)
         * пересчитывает её из колонки только при загрузке уровня, а дальше
         * ею владеет leave_guard.  Иначе тело при возврате прыгает в центр
         * тайла. */
            if (saved_seq) Guard.x = pop_guard_state_x(room);
            /* ...и КОЛОНКУ пересчитываем из X (enter_guard, seg002:180:
             * `Char.curr_col = get_tile_div_mod_m7(Char.x)`).  Брать её из
             * тайла нельзя: leave_guard сохраняет get_tilepos(0, row), то
             * есть колонку 0 всегда.  У живого стража это незаметно (X там
             * сам считается из колонки), а у ЗАПОМНЕННОГО ТРУПА X
             * настоящая, и curr_col оставался нулём при теле, лежащем в
             * колонке 2.  Тайл трупа шёл в обходе раньше тайла Кида, и Кид
             * рисовался ПОВЕРХ тела вместо того, чтобы пройти за ним
             * (BUG-DRAWORDER-1).  Колонка нужна не только отрисовке — на
             * неё смотрят коллизия и check_can_guard_see_kid. */
            Guard.curr_col = guard_col_from_x(Guard.x);
        pop_loadshad();
        if (saved_seq) Char.curr_seq = saved_seq;
        else           pop_char_set_seq(SEQ_77_GUARD_STAND_INACTIVE);
        pop_saveshad();
        pop_guard_play();                    /* до первого кадра */
        /* enter_guard (seg002:218): кадр смерти -> страж поднят ТРУПОМ. */
        if (Guard.frame == 185 || Guard.frame == 177 || Guard.frame == 178) {
            Guard.alive = 1;
            guardhp_curr = 0;
        }
    }
}
