Files
Sprinter-SDCC/applications/PoP/roomtest/pop_map.h
T
Александр Петров e67117219f BUG-CTRL-FRAME-1: геометрия Кида считалась по кадру стража
cur_frame — один глобал на всех персонажей (как в оригинале), владелец —
тот, кто последним прошёл load_frame.  Последним в кадре тикает страж,
поэтому к моменту control() Кида там лежал кадр СТРАЖА, а через
kid_cur_dx/dy/flags по нему считается вся геометрия управления:
dx_weight -> determine_col -> distance_to_edge_weight -> get_edge_distance
-> выбор ветки в check_jump_up.

Оригинал зовёт load_fram_det_col() (seg006:0144) сразу после
loadkid/loadshad и ДО control() — play_kid_frame (seg000:1211) и
play_guard_frame (seg000:1248).  У нас этого не было.

Замерено брейкпоинтом на pop_jump_up_seq: Kid x=156 col=6 кадр 15
(dx=0 weight_x=3) при кадре стража image17 (dx=-1 weight_x=8) дал
curr_col=7 и distance=2 вместо 6 и 10 — то есть jump_up_plain (вернулось
A=28, пустой прыжок) вместо «шаг назад на x=160 + зацеп».  Предсказание
по кадру стража совпало с намеренным до единицы.

Отсюда же плавающее поведение: кадр стража меняется каждый тик, distance
Кида скакал через порог 6 — то прыжок, то попытка зацепа с неверной X.
И «голова Кида поверх плиты (1,6)» — не баг отрисовки, а следствие позы,
которой в оригинале в этом месте не бывает.

Фикс: pop_load_fram_det_col() (pop_kid.c) + вызовы в pop_ctrl_tick и
pop_guard_tick.  determine_col — только на ветке Кида: у нас он
существует лишь для него (pop_map работает с Kid, а не с Char).

Разбор с числами — bug_closed.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 12:55:43 +03:00

178 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* pop_map.h — коллизия Kid с картой комнаты (Фаза K3): тайловые запросы
* + физика падения + приземление. Порт SDLPoP seg005/seg006
* (check_on_floor/start_fall/do_fall/fall_accel/fall_speed/land/in_wall).
*
* K3.2 (падение): Kid, сойдя/сбежав с края пола (тайл под ним не floor),
* начинает падать (fall_y-ускорение, Char.y растёт), приземляется на пол
* ниже (soft/medium land) или продолжает вниз. Стена под персонажем —
* in_wall (выталкивание). Бег В стену (check_bumped) — отдельным шагом.
*/
#ifndef POP_MAP_H
#define POP_MAP_H
#include <stdint.h>
/* Задать карту текущей комнаты: fg[30] = коды тайлов (row*10+col, 3×10).
* col<0/>9 или row<0/>2 трактуются как стена (край уровня). Массив
* МУТАБЕЛЬНЫЙ (loose-пол при падении → empty) — передавать ту же копию,
* что и в pop_room_draw, чтобы отрисовка видела изменения. */
void pop_map_set(uint8_t *fg) __banked;
/* Связи комнаты (0=нет соседа; иначе номер) + кромки швов для коллизии:
* lcol_fg[6] = col9 левого соседа (за col0) в [0..2] и его col8 (колонка −2)
* в [3..5]; rcol_fg[6] симметрично (col0 / col1 = колонки 10 и 11). Вторая
* колонка нужна, когда Kid стоит В шве и смотрит наружу. Звать в enter_room. */
void pop_map_set_edges(uint8_t left, uint8_t right, uint8_t up, uint8_t down,
const uint8_t *lcol_fg, const uint8_t *rcol_fg) __banked;
/* Номер ТЕКУЩЕЙ комнаты (для доступа к room_modif пик/ворот в pop_trob).
* Звать в enter_room. */
void pop_map_set_room(uint8_t room) __banked;
/* Тайл-код текущей комнаты по клетке (col 0..9, row 0..2) — порт
* get_tile(room,col,row) для текущей комнаты. Нужен лучу видимости стража
* (guards.c): он идёт по ряду между Кидом и стражем. */
uint8_t pop_tile_at(int8_t col, int8_t row) __banked;
/* determine_col (seg006:014D): Kid.curr_col = m7(dx_weight()). Наружу — для
* pop_load_fram_det_col (pop_kid.c), порт load_fram_det_col. */
void pop_determine_col(void) __banked;
/* HP/смерть. pop_kid_dead=1 когда Kid убит (пики); hitp_curr — текущее HP.
* pop_kid_hp_reset() ставит старт HP и снимает смерть (звать в kid_init/
* респавн). */
extern uint8_t pop_kid_dead;
extern uint8_t hitp_curr;
extern uint8_t hitp_max; /* стартовое HP (для индикатора) */
extern int8_t hitp_delta; /* урон за кадр от меча; применяет pop_do_delta_hp */
void pop_kid_hp_reset(void) __banked;
/* hitp_beg_lev (seg003): HP, с которым НАЧАТ текущий уровень. Рестарт
* уровня (смерть/выпадение) откатывает HP ровно к нему; пройденный уровень
* подтягивает его к hitp_max (play_level_2, seg003:410) — так «жизнь» из
* большой склянки переезжает на следующий уровень. Правит главный цикл. */
extern uint8_t hitp_beg_lev;
#define POP_START_HITP 3 /* custom->start_hitp */
#define POP_MAX_HITP 10 /* custom->max_hitp_allowed */
void pop_kid_hp_newgame(void) __banked; /* начало ИГРЫ: hitp_beg_lev = 3 */
/* Предметы (seg005 check_get_item/get_item, seg006 do_pickup/proc_get_object).
* pop_have_sword — «меч у Кида» (порт have_sword). pop_item_taken — 0 или
* tilepos+1 только что поднятого предмета: вызывающий обязан запечь тайл
* (он стал полом) на ОБЕИХ страницах и записать override комнаты. */
extern uint8_t pop_have_sword;
extern uint8_t pop_item_taken;
/* Вспышка фона (do_flash/set_bg_attr, seg001:0908): счётчик кадров, пока
* НУЛЕВАЯ запись палитры подменена ярко-жёлтым. Тикает вызывающий. */
extern uint8_t pop_flash_time;
/* Цвет вспышки: 0 = ярко-жёлтая (подобран меч), 1 = красная (зелье). */
extern uint8_t pop_flash_red;
uint8_t pop_get_item_action(void) __banked; /* 0 нет / 1 присесть / 2 поднять меч / 3 пить */
void pop_proc_get_object(void) __banked; /* эффект — из play_seq (опкод GET_ITEM) */
/* Уход из комнаты ВБОК/ВВЕРХ: 0=нет; 1=left,2=right,3=up (= side+1 для
* pop_room_link). Координаты Kid уже репроецированы (x∓140 / y+189).
* Приложение переключает комнату по соответствующей связи и снимает флаг. */
extern uint8_t pop_leave_dir;
/* exit_room_timer (порт seg002 exit_room): пока >0, боковой/нижний leave
* заблокирован (декремент в pop_phys_tick). Приложение ставит =2 при входе
* в комнату (enter_room) — 2 кадра защиты от повторного/транзиентного leave
* (ping-pong у шва, транзиент отскока о ворота, баг #4/BUG-2). */
extern uint8_t pop_leave_timer;
/* Loose floors (проваливающиеся полы). pop_loose_modif[30] — состояние на
* тайл (0 покой / 0x80.. тряска / 1..11 отсчёт падения); читает pop_bg при
* отрисовке. pop_loose_tick() — анимация + перерисовка изменившихся тайлов
* на ТЕКУЩЕЙ странице; звать в основном цикле ПОСЛЕ set_draw_page(back),
* рядом с kid-отрисовкой. */
extern uint8_t pop_loose_modif[30];
void pop_loose_tick(void) __banked;
void pop_loose_reset(void) __banked; /* сброс loose-состояния при смене комнаты */
/* Установлен в tilepos+1 в кадре, когда loose-пол упал (кусок улетел ВНИЗ).
* Приложение: определить debris-приземление в комнате снизу. Снять (=0). */
extern uint8_t pop_loose_fell;
/* Плита-ПОТОЛОК: loose ряда 2 комнаты СВЕРХУ (в оригинале — тот же loose с
* отрицательным tilepos, seg006 check_press frame 79 / seg007 animate_loose).
* pop_map_set_above даёт МУТАБЕЛЬНУЮ копию ряда 2 верхнего соседа (ту же, что
* ушла в pop_room_set_above): провалившаяся плита становится в ней EMPTY —
* сверху открывается колодец. pop_ceil_modif[col] — состояние (как
* pop_loose_modif), читает pop_bg. Сигналы приложению (для персистентности
* между входами в комнату): pop_ceil_fell = col+1 провалившейся плиты,
* pop_debris_at = tilepos+1 щебня, оставшегося в ЭТОЙ комнате. Снимать (=0). */
void pop_map_set_above(uint8_t *above_row2_fg) __banked;
extern uint8_t pop_ceil_modif[10];
extern uint8_t pop_ceil_fell;
extern uint8_t pop_debris_at;
/* Взводится в кадре, когда Kid получил урон (hitp_delta<0 оригинала) —
* приложение рисует «брызги» kid_draw_splash() поверх Kid и снимает флаг. */
extern uint8_t pop_kid_hurt;
/* check_knock (seg003): по флагу knock (play_seq) тряхнуть loose-полы ряда
* приземления/удара. Зовётся из pop_phys_tick. */
void pop_check_knock(void) __banked;
/* Установлен в 1, когда Kid пересёк НИЖНЮЮ границу комнаты (провал в дыру
* или спуск-зацеп с кромки). Координаты Kid УЖЕ репроецированы в кадр
* комнаты снизу (y -= 189, curr_row = 0). Приложение решает: если есть
* links.down — сменить комнату (pop_room_load+перерисовка) и продолжить;
* иначе — выпал из уровня (респавн). Снять флаг после обработки. */
extern uint8_t pop_fell_out;
/* Пер-кадровая физика ПОСЛЕ kid_tick(play_seq): fall_accel + fall_speed +
* determine_col + check_action (триггер падения / do_fall / приземление).
* Порядок как в оригинале (seg000 play_kid_frame). */
void pop_phys_tick(void) __banked;
/* clip_char (seg006:1749), верхняя граница клипа спрайта персонажа: если тайл
* над головой — стена/пол, всё выше линии своей полосы не рисуется (Kid,
* ушедший головой под пол при спуске/висе, не просвечивает сквозь щели).
* obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ спрайта, w/h — размер.
* Возврат: КОМНАТНЫЙ y границы, 0 = клипа нет. Зовёт kid_draw. */
int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked;
/* Парная половина clip_char: ЭКРАННЫЙ X, правее которого спрайт не рисуется
* (правый косяк двери уровня в кадрах подъёма 224..228). 0 = обрезки нет. */
int pop_clip_char_right(void) __banked;
/* Тип препятствия/края ВПЕРЕДИ по направлению (pop_edge_type после
* pop_edge_distance): для control (бежать / осторожный шаг / стоп у стены). */
#define EDGE_CLOSER 0 /* край уступа над ямой (дистанция = до кромки) */
#define EDGE_WALL 1 /* стена впереди/под персонажем */
#define EDGE_FLOOR 2 /* пол (нет препятствия рядом, дистанция = 11) */
/* Расстояние до края/стены впереди + побочно g_edge_type (pop_edge_type). */
int pop_edge_distance(void) __banked;
uint8_t pop_edge_type(void) __banked;
/* Стена впереди в пределах стопа? Если да — прижимает Kid.x к корректной
* позиции (передний край у стены, симметрично) и возвращает 1. Для
* control_running (чистый стоп у стены). */
int pop_wall_ahead(void) __banked;
/* Прыжок вверх в стойке (↑): seq чистого прыжка (seq_28/seq_14) ЛИБО
* прыжка-с-зацепом за уступ выше (seq_8/24/16) + выравнивание Kid.x.
* check_jump_up/grab_up (K4.4). control_up гасит caller. */
uint8_t pop_jump_up_seq(void) __banked;
/* ---- Вис/зацеп/спуск (K4.2/K4.3) — тайловые решения для pop_ctrl --- *
* Управление (control_hanging/can_climb_up/hang_fall/down_pressed) в
* pop_ctrl; здесь — предикаты + выбор seq (+ выравнивание Kid.x). */
uint8_t pop_hang_grab_ready(void) __banked; /* grab_timer истёк — можно climb-up */
uint8_t pop_hang_climb_up_seq(void) __banked; /* seq_10 / seq_73 (подтягивание) */
int8_t pop_hang_wall(void) __banked; /* Shift в висе: 1=у стены,-1=fall,0=держ.*/
uint8_t pop_hang_fall_seq(void) __banked; /* seq_23 / seq_11 (сорваться), +Kid.x */
uint8_t pop_down_action(void) __banked; /* 1=обработано(спуск/отступ), 0=присед */
/* Дверь уровня рядом и ОТКРЫТА ПОЛНОСТЬЮ? → поставить Кида в проём (x +
* разворот влево, go_up_leveldoor) и вернуть 1; иначе 0, Кид не тронут.
* Ветка двери уровня из up_pressed (seg005:0482). Последовательность
* seq_70 ставит вызывающий (pop_ctrl) — тайлы наши, управление его. */
uint8_t pop_leveldoor_enter(void) __banked;
#endif