roomtest: обломки упавшего loose в комнате снизу (debris)

Loose-пол room1(2,6) при падении даёт кусок, который в оригинале улетает в
комнату снизу (links.down) и приземляется на первый floor, превращая его в
debris (порт move_loose/loose_land seg007).  Для room1(2,6): room2(0,6)=empty
пролёт → room2(1,6)=floor приземление → debris.

Минимальная реализация (без анимации полёта куска, только персистентный
результат — мини-версия P0 из docs/gates_spikes_plan.md):
- pop_map: сигнал pop_loose_fell (tilepos+1 упавшего loose).
- pop_level: pop_room_col_landing(room,col) — ряд первого floor сверху вниз
  (пустые ряды пролетаются), иначе -1.
- roomtest: при pop_loose_fell вычисляет debris-приземление в комнате снизу →
  персистентный override (dbr_room/dbr_pos, живёт между переходами) →
  enter_room применяет поверх загруженных тайлов (floor→debris 0x0E).

Проверено в MAME: провал loose room1(2,6) → debris в room2(1,6), сохраняется
при повторных входах.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:26:21 +03:00
parent bf7ddfd4da
commit d1bc97c589
5 changed files with 58 additions and 1 deletions
+17
View File
@@ -137,6 +137,23 @@ int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg,
return 0; return 0;
} }
int8_t pop_room_col_landing(uint8_t room, uint8_t col)
{
const uint8_t *rf;
int8_t row, res = -1;
if (!lvl_ok || room < 1 || room > 24 || col > 9) return -1;
gfx_w0_map(lvl_page);
rf = room_fg_ptr(room);
for (row = 0; row < 3; row++) { /* кусок пролетает пустые ряды */
uint8_t t = (uint8_t)(rf[row * 10 + col] & 0x1F);
if (t == 0) continue; /* empty — дальше вниз */
if (t == 1) res = row; /* floor — приземление → debris */
break; /* первый не-пустой тайл — стоп */
}
gfx_w0_unmap();
return res;
}
uint8_t pop_room_link(uint8_t room, uint8_t side) uint8_t pop_room_link(uint8_t room, uint8_t side)
{ {
uint8_t v; uint8_t v;
+5
View File
@@ -41,6 +41,11 @@ int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg,
* 0, если стороны нет. Для переходов между комнатами (L2/L3). */ * 0, если стороны нет. Для переходов между комнатами (L2/L3). */
uint8_t pop_room_link(uint8_t room, uint8_t side); uint8_t pop_room_link(uint8_t room, uint8_t side);
/* Ряд приземления падающего loose-куска в колонке col комнаты room: первый
* НЕ-пустой тайл сверху вниз; возвращает его ряд, если это floor (→ debris),
* иначе -1 (пролетел/крушение о стену). */
int8_t pop_room_col_landing(uint8_t room, uint8_t col);
/* Стартовая позиция уровня (блок @2112): комната / поза / направление. */ /* Стартовая позиция уровня (блок @2112): комната / поза / направление. */
uint8_t pop_level_start_room(void); uint8_t pop_level_start_room(void);
uint8_t pop_level_start_pos(void); uint8_t pop_level_start_pos(void);
+2
View File
@@ -86,6 +86,7 @@ static int8_t g_infrontx; /* колонка «перед персонаже
/* ---- Карта комнаты ------------------------------------------------- */ /* ---- Карта комнаты ------------------------------------------------- */
uint8_t pop_fell_out; /* 1 = Kid пересёк НИЖНЮЮ границу (см. pop_map.h) */ uint8_t pop_fell_out; /* 1 = Kid пересёк НИЖНЮЮ границу (см. pop_map.h) */
uint8_t pop_leave_dir; /* уход вбок/вверх: 0=нет; 1=left,2=right,3=up (side+1) */ uint8_t pop_leave_dir; /* уход вбок/вверх: 0=нет; 1=left,2=right,3=up (side+1) */
uint8_t pop_loose_fell; /* 0=нет; иначе tilepos+1 упавшего loose (кусок улетел вниз) */
static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */ static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */ static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */ static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */
@@ -763,6 +764,7 @@ void pop_loose_tick(void)
pop_loose_bake_empty(pos / 10, pos % 10); /* запечь пусто на ТЕКУЩЕЙ странице сразу */ pop_loose_bake_empty(pos / 10, pos % 10); /* запечь пусто на ТЕКУЩЕЙ странице сразу */
loose_bake[pos] = 1; /* + вторая страница следующим кадром */ loose_bake[pos] = 1; /* + вторая страница следующим кадром */
pop_loose_mob_spawn(pos / 10, pos % 10); /* отрыв падающего куска (L4) */ pop_loose_mob_spawn(pos / 10, pos % 10); /* отрыв падающего куска (L4) */
pop_loose_fell = (uint8_t)(pos + 1); /* сигнал приложению: кусок улетел */
continue; continue;
} }
pop_loose_shake_draw(pos / 10, pos % 10); /* дрожащий кадр */ pop_loose_shake_draw(pos / 10, pos % 10); /* дрожащий кадр */
+4
View File
@@ -39,6 +39,10 @@ extern uint8_t pop_loose_modif[30];
void pop_loose_tick(void); void pop_loose_tick(void);
void pop_loose_reset(void); /* сброс loose-состояния при смене комнаты */ void pop_loose_reset(void); /* сброс loose-состояния при смене комнаты */
/* Установлен в tilepos+1 в кадре, когда loose-пол упал (кусок улетел ВНИЗ).
* Приложение: определить debris-приземление в комнате снизу. Снять (=0). */
extern uint8_t pop_loose_fell;
/* check_knock (seg003): по флагу knock (play_seq) тряхнуть loose-полы ряда /* check_knock (seg003): по флагу knock (play_seq) тряхнуть loose-полы ряда
* приземления/удара. Зовётся из pop_phys_tick. */ * приземления/удара. Зовётся из pop_phys_tick. */
void pop_check_knock(void); void pop_check_knock(void);
+30 -1
View File
@@ -27,13 +27,33 @@ static uint8_t room_fg[30], room_bg[30];
static uint8_t lcol_fg[3], lcol_bg[3], rcol_fg[3], rcol_bg[3], below_fg[10]; static uint8_t lcol_fg[3], lcol_bg[3], rcol_fg[3], rcol_bg[3], below_fg[10];
static uint8_t cur_room; /* номер текущей комнаты (для переходов) */ static uint8_t cur_room; /* номер текущей комнаты (для переходов) */
/* Персистентные debris-переопределения (упавший loose-кусок приземлился в
* комнате снизу → floor стал debris). Минимальный per-room override: живёт
* между переходами, применяется в enter_room поверх загруженных тайлов.
* (Полноценное per-room modifier-состояние — в docs/gates_spikes_plan.md, P0.) */
#define TILE_DEBRIS 0x0E
#define DEBRIS_OVR_MAX 4
static uint8_t dbr_room[DEBRIS_OVR_MAX], dbr_pos[DEBRIS_OVR_MAX], dbr_cnt;
static void add_debris_override(uint8_t room, uint8_t pos)
{
uint8_t i;
for (i = 0; i < dbr_cnt; i++)
if (dbr_room[i] == room && dbr_pos[i] == pos) return; /* уже есть */
if (dbr_cnt < DEBRIS_OVR_MAX) {
dbr_room[dbr_cnt] = room; dbr_pos[dbr_cnt] = pos; dbr_cnt++;
}
}
/* Загрузить комнату room в рабочие массивы и отрисовать её фон в ОБЕ /* Загрузить комнату room в рабочие массивы и отрисовать её фон в ОБЕ
* страницы дабл-буфера (у каждой своя видео-ОЗУ/теневая копия). Оставляет * страницы дабл-буфера (у каждой своя видео-ОЗУ/теневая копия). Оставляет
* активной страницу 1 — вызывающий восстанавливает нужную draw-страницу. */ * активной страницу 1 — вызывающий восстанавливает нужную draw-страницу. */
static void enter_room(uint8_t room) static void enter_room(uint8_t room)
{ {
uint8_t pg; uint8_t pg, i;
pop_room_load(room, room_fg, room_bg, lcol_fg, lcol_bg, rcol_fg, rcol_bg, below_fg); pop_room_load(room, room_fg, room_bg, lcol_fg, lcol_bg, rcol_fg, rcol_bg, below_fg);
for (i = 0; i < dbr_cnt; i++) /* применить debris-переопределения */
if (dbr_room[i] == room) room_fg[dbr_pos[i]] = TILE_DEBRIS;
pop_map_set(room_fg); pop_map_set(room_fg);
pop_map_set_edges(pop_room_link(room, 0), pop_room_link(room, 1), pop_map_set_edges(pop_room_link(room, 0), pop_room_link(room, 1),
pop_room_link(room, 2), pop_room_link(room, 3), pop_room_link(room, 2), pop_room_link(room, 3),
@@ -151,6 +171,15 @@ int main(void)
} }
pop_loose_tick(); /* loose-полы: анимация + перерисовка (СЛОЙ ФОНА */ pop_loose_tick(); /* loose-полы: анимация + перерисовка (СЛОЙ ФОНА */
/* — ДО Kid, чтобы Kid всегда был поверх плиты) */ /* — ДО Kid, чтобы Kid всегда был поверх плиты) */
if (pop_loose_fell) { /* упавший кусок → debris в комнате снизу */
uint8_t fcol = (uint8_t)((pop_loose_fell - 1) % 10);
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
pop_loose_fell = 0;
if (down) {
int8_t lrow = pop_room_col_landing(down, fcol);
if (lrow >= 0) add_debris_override(down, (uint8_t)(lrow * 10 + fcol));
}
}
kid_draw(); /* нарисовать новый кадр (детект выхода за поле) */ kid_draw(); /* нарисовать новый кадр (детект выхода за поле) */
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(), pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
kid_fp_width(), kid_fp_height(), kid_fp_width(), kid_fp_height(),