diff --git a/applications/PoP/roomtest/BUGS_OPEN.md b/applications/PoP/roomtest/BUGS_OPEN.md index 4e567a6..4166444 100644 --- a/applications/PoP/roomtest/BUGS_OPEN.md +++ b/applications/PoP/roomtest/BUGS_OPEN.md @@ -14,6 +14,7 @@ | ID | что | тип | статус | |----|-----|-----|--------| +| [GATE-FORE-KID](#gate-fore-kid) | Кид в проёме ворот виден поверх решётки | окклюзия | открыт: портирован только шовный случай | | [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить | | [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт | | [TORCH-ANIM-RIGHT](#torch-anim-right) | под запечённым пламенем застывают не только челюсти чомпера | **низкий** | открыт: на уровнях 1-4 такого соседства нет | @@ -335,3 +336,45 @@ for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row) Если дубль мал — оставить как есть и закрыть запись. --- + +--- + + +## GATE-FORE-KID. Кид, стоящий В ПРОЁМЕ ворот, виден ПОВЕРХ решётки + +Нашёл пользователь 2026-08-11 (уровень 5, комната 24, верхние ворота): +Кид стоит на тайле ворот, а решётка рисуется ПОД ним — он виден целиком, +хотя должен быть за прутьями. + +**Как в оригинале.** `draw_tile_fore` (seg008:0D15) первой же строкой: + +```c +if (tile_left == tiles_4_gate && Kid.curr_row == drawn_row && + Kid.curr_col == drawn_col - 1 && Kid.room != room_R) { + draw_gate_fore(); +} +``` + +то есть когда Кид стоит ИМЕННО на тайле ворот, их решётка дорисовывается +в **foretable** — поверх персонажа: `draw_gate_fore` (seg008) кладёт спрайт +51 (низ решётки) и дальше вверх кусками по 8 px спрайтом 52, до +`gate_top_y`, прозрачным блитом. + +**Что есть у нас.** Портирован только ШОВНЫЙ случай (`pop_bg.c`, +`pop_fore_over_char`): если футпринт зашёл за левый шов и у соседа слева в +этом ряду ворота — их бары перерисовываются поверх персонажа через +`draw_gate_back`. Случая «ворота ВНУТРИ комнаты, персонаж на их тайле» +нет вовсе, отсюда симптом. + +**Почему не чинится одной строкой.** Правка идёт в `pop_fore_over_char` — +самый горячий путь кадра (memory `pop_fore_layer_cost`: когда-то 78 % +кадра, окно клипа дало 3.2×). Добавлять проверку надо так, чтобы она не +стоила ничего на каждом тайле футпринта: условие дешёвое (`тайл под +персонажем == ворота`), но рисование — это ещё один блит на кадр, пока +Кид стоит в проёме. Плюс нужен свой расчёт полосы (`gate_top_y` по +живому openness), а не готовый `draw_gate_back`, который рисует ЗА +персонажем. + +**Проверять:** уровень 5, комната 24 — встать в проём верхних ворот +(колонка 1 ряда 0) при частично поднятой решётке; прутья должны +перекрывать Кида. Тот же случай — любые ворота на уровнях 1-3. diff --git a/applications/PoP/roomtest/pop_map.c b/applications/PoP/roomtest/pop_map.c index da104a1..43f97d2 100644 --- a/applications/PoP/roomtest/pop_map.c +++ b/applications/PoP/roomtest/pop_map.c @@ -286,6 +286,26 @@ uint8_t pop_tile_at(int8_t col, int8_t row) __banked * g_link_r col0; ВНУТРИ комнаты (0..9) — текущая комната g_room (иначе после * перехода Kid встаёт на ту же створку уже как на внутрикомнатную и его отбивает * обратно). Открыто ⇔ (modif>>2)+6 >= char_height (бары выше головы). */ +/* Комната и tilepos клетки (col,row) С УЧЁТОМ ШВА — порт того, что в + * оригинале делает сам get_tile: он зовёт find_room_of_tile (seg006:005D) и + * ПЕРЕСТАВЛЯЕТ curr_room/curr_tilepos, а дальше trigger_button работает уже + * с ними, а не с комнатой персонажа. Возвращает 0, если клетки нет. + * + * Кусало 2026-08-11 (нашёл пользователь на уровне 5): Кид стоит на кнопке, + * которая физически в СОСЕДНЕЙ комнате (col = −1, то есть колонка 9 левого + * соседа). Мы читали модификатор как (своя комната, tilepos 9) — там стена + * с bg = 0, то есть индекс связи 0. Цепочка от нуля в комнате 24 уровня 5 + * открывает И нижние ворота, И верхние, хотя настоящая кнопка (индекс 9) + * открывает только нижние. */ +static uint8_t tile_room_of(int8_t col, int8_t row, uint8_t *tp) +{ + if (row < 0 || row > 2) return 0; + if (col >= 0 && col <= 9) { *tp = (uint8_t)(row * 10 + col); return g_room; } + if (col < 0 && col >= -10 && g_link_l) { *tp = (uint8_t)(row * 10 + col + 10); return g_link_l; } + if (col > 9 && col <= 19 && g_link_r) { *tp = (uint8_t)(row * 10 + col - 10); return g_link_r; } + return 0; +} + /* Живой модификатор (openness) ворот в (col,row): своя комната или соседняя * через шов. 0xFF — тайла нет (за краем уровня). */ static uint8_t gate_modif(int8_t col, int8_t row) @@ -1969,11 +1989,17 @@ static void check_press(void) if (!(kid_cur_flags() & FRAME_NEEDS_FLOOR)) return; { uint8_t t = get_tile_at_char(); - uint8_t tp = (uint8_t)(Char.curr_row * 10 + Char.curr_col); - if (t == TILE_OPENER || t == TILE_CLOSER) - pop_trigger_button(g_room, tp, t, pop_trob_modif(g_room)[tp]); + uint8_t tp = 0; + /* Кнопка может лежать в СОСЕДНЕЙ комнате — Кид стоит в шве + * (curr_col = −1 или 10). Комнату и tilepos берём резолвнутые, + * иначе индекс связи читается из чужого тайла (см. tile_room_of). */ + uint8_t btn_room = tile_room_of(Char.curr_col, Char.curr_row, &tp); + if ((t == TILE_OPENER || t == TILE_CLOSER) && btn_room) + pop_trigger_button(btn_room, tp, t, pop_trob_modif(btn_room)[tp]); else if (t == TILE_LOOSE) { - make_loose_fall(tp, 1); + /* loose живёт в СВОЕЙ комнате (make_loose_fall работает с g_fg), + * поэтому здесь по-прежнему координаты персонажа. */ + make_loose_fall((uint8_t)(Char.curr_row * 10 + Char.curr_col), 1); is_guard_notice = 1; /* seg006:1734 */ } }