1146c57544
kid_heal/pop_guard_heal звали gfx_heal всегда, хотя рисуют ровно тот прямоугольник, который блит в большинстве кадров кладёт noclip-ядром. Все три места heal (+ heal_off фона) сведены к общему pop_heal_fast. Замер в MAME, счётчики totalcycles на входах kid_heal и kid_tick (вся группа heal за кадр), комната 1, Кид стоит: клипающее ядро 26 200 тактов/кадр (149 кадров) noclip 15 848 тактов/кадр (239 кадров) −10 352 такта, −39.5 %. A/B в одном прогоне: вторая половина снята с пропатченным в памяти условием (jr nz → jr), то есть на той же геометрии. Размер СУММАРНО −362 Б: _CODE +17, BANK2 −116 (свободно 2708 — это тесный банк из рисков levels_plan §5), BANK3 −263, BANK4 без изменений. Грабли по дороге: первым заходом хелпер был static inline в pop_bg.h — SDCC 4.5 И встраивает тело (181 Б) в каждый вызов, И оставляет копию в каждом TU, который видит заголовок. pop_guard_heal раздулся с ~60 до 663 Б, итого +1091 Б в _CODE и +636 Б в банке стража. Отсюда pop_draw.c: обычная функция в резиденте W1, из банков это прямой call без трамплина. pop_room_clip_borders оставлен клипающим осознанно (320 не лезет в 8 бит, гейт border_dirty редкий) — причина записана в коде. Проверено визуально: ходьба, прыжок, спуск, позиция за решёткой шва (straddle — там работает клипающий фолбэк) — артефактов нет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
354 lines
26 KiB
Markdown
354 lines
26 KiB
Markdown
# roomtest — архив закрытых багов
|
||
|
||
Сюда переезжает всё, что **закрыто**: подтверждённые фиксы, снятые
|
||
диагнозы, осознанные решения «не делать». Открытые баги — в
|
||
[`bug_list.md`](bug_list.md), текущие задачи — в [`TASKS.md`](TASKS.md).
|
||
|
||
Файл существует не ради истории как таковой: половина записей ниже — это
|
||
разбор КОРНЯ (odd-pixel арифметика `char_x`, подстановка тайла нажатой
|
||
кнопки, баг кодогенератора SDCC), и он экономит часы, когда похожий симптом
|
||
всплывёт снова. Прежде чем заводить новый баг — грепни здесь по симптому.
|
||
|
||
---
|
||
|
||
## Проверено в MAME 2026-08-01 (ревизия L1-TRIAGE)
|
||
|
||
Три бага стояли как **Critical** с 2026-07-21 и по исходникам выглядели
|
||
закрытыми, но переподтверждены не были. Прогон в MAME (roomtest, `ROOMNAV`,
|
||
чтение `_Kid` через мост) закрыл все три.
|
||
|
||
### BUG-1. Боковой переход через ворота: Kid проваливается на row 1 — **ЗАКРЫТ (не воспроизводится)**
|
||
|
||
**Был симптом:** при проходе через ОТКРЫТЫЕ ворота в соседнюю комнату
|
||
(через шов) Kid оказывался на ряду row 1 вместо row 0.
|
||
|
||
**Проверка 2026-08-01, точный сценарий бага.** Комната 6, Kid на ряду 0;
|
||
осторожный шаг на кнопку (0,2) — решётка room8 (0,9) поднимается; удержание
|
||
← через открытый шов:
|
||
|
||
| момент | `room` | `x` | `y` | `curr_col` | `curr_row` |
|
||
|--------|--------|-----|-----|-----------|-----------|
|
||
| на кнопке в room6 | 6 | 97 | 55 | 2 | 0 |
|
||
| после перехода | **8** | 181 | **55** | 8 | **0** |
|
||
|
||
Ряд и Y сохранены, Kid стоит на полу ряда 0 (скриншот `triage_06.png`).
|
||
Провала нет.
|
||
|
||
**Заодно снят и сам диагноз записи** — он был неверен. В записи стояло:
|
||
«Y/`curr_row` при боковом переходе НЕ репроецируются, в отличие от
|
||
`check_leave_below`». Это не дефект, а **точное поведение оригинала**:
|
||
`goto_other_room` (`SDLPoP/src/seg002.c:390`) для направлений left/right
|
||
меняет ТОЛЬКО `Char.x` (±140), а `Char.y`/`curr_row` трогает исключительно
|
||
для up/down. Наш `check_leave` (`pop_map.c:1402`) делает ровно то же.
|
||
Реальной причиной симптома была, судя по всему, кромочная коллизия — её
|
||
закрыл `char_x_forward_edge` (см. BUG-SEAM-PINGPONG ниже).
|
||
|
||
### BUG-2. Возврат из комнаты назад: Kid отбрасывается обратно (ping-pong) — **ЗАКРЫТ (не воспроизводится)**
|
||
|
||
**Был симптом:** после перехода в соседнюю комнату попытка сразу вернуться
|
||
приводила к тому, что Kid снова закидывался в ту же комнату — выйти нельзя.
|
||
|
||
**Проверка 2026-08-01.** Шов room2↔room3 (ряд 1, без ворот — чистый
|
||
горизонтальный переход), с намеренным разворотом СРАЗУ после пересечения:
|
||
|
||
| действие | `room` | `x` | `curr_col` |
|
||
|----------|--------|-----|-----------|
|
||
| старт в room2 | 2 | 86 | 1 |
|
||
| держим → | **3** | 122 | 3 |
|
||
| сразу держим ← | **2** | 195 | 9 |
|
||
| сразу держим → | **3** | 85 | 1 |
|
||
| сразу держим ← | **2** | 183 | 8 |
|
||
|
||
Комната меняется РОВНО один раз на пересечение, туда и обратно, без
|
||
осцилляции. Механизм на месте: `pop_leave_timer` (порт `exit_room_timer`,
|
||
`pop_map.c:112,1553`) + `char_x_forward_edge` (`pop_map.c:365`).
|
||
|
||
### BUG-3. Climb-up на тайл-кнопку: неправильная окклюзия — **ЗАКРЫТ фиксом от 2026-07-28**
|
||
|
||
**Был симптом:** при подтягивании на тайл, верх которого — кнопка
|
||
(opener/closer), Kid рисовался ПОВЕРХ кнопки вместо того, чтобы быть
|
||
перекрытым её передней гранью.
|
||
|
||
**Почему закрыт.** Запись требовала: «трактовать нажатую кнопку как
|
||
floor-тайл в climb-overlay, учесть подстановку из `get_tile_to_draw`».
|
||
Ровно это и сделано `tile_code_drawn()` — `climb_overlay_tile`
|
||
(`pop_bg.c:1346`) берёт ПОДСТАВЛЕННЫЙ код тайла, а не сырой. То есть
|
||
BUG-3 — дубль пункта «спуск с кнопки (room8, кромка (0,6))» из раздела
|
||
«Исправлено», заведённый до фикса.
|
||
|
||
**Оговорка, чтобы не выдавать желаемое:** покадрово в MAME снимался СПУСК
|
||
(кадры 148..138). Подъём идёт через ту же ветку и ту же таблицу
|
||
`FLOOR_LEFT_OVERLAY[fidx]`, поэтому отдельного дефекта тут быть не может,
|
||
но визуально направление «вверх» не переснималось. Если при сквозном
|
||
прохождении (L1-PASS) увидишь Kid поверх кнопки на подъёме — заводи заново.
|
||
|
||
---
|
||
|
||
## Косметика окклюзии — закрыта кодом, список отставал (сверено 2026-08-01)
|
||
|
||
Четыре записи от 2026-07-22 висели как открытые Medium. Каждая описывала
|
||
недостающий кусок порта; каждый из них с тех пор написан, но записи никто не
|
||
снял. Ниже — что именно закрывает каждую.
|
||
|
||
### BUG-CEIL-1. Прыжок вверх: руки Kid рисуются ПОВЕРХ потолка — **ЗАКРЫТ**
|
||
|
||
**Был симптом:** при прыжке вверх (SEQ up, кадры 67..79) руки/голова Kid
|
||
заходили в полосу кладки у потолка (row −1) и рисовались ПОВЕРХ неё.
|
||
|
||
**Чем закрыт:** `ceil_over_kid_tile()` (`pop_bg.c:1275`) — порт «нижняя грань
|
||
потолка и кадр плиты-потолка идут в FOREtable», т.е. поверх персонажа.
|
||
Зовётся из `pop_fore_over_kid` (`pop_bg.c:1575`) и из fore-прохода стража
|
||
(`:1607`). Комментарий на месте прямо называет причину: «иначе руки
|
||
прыгающего Kid лезут на кромку потолка».
|
||
|
||
### BUG-CEIL-2. Тряска/разбитие loose-плиты в потолке (row −1) — **ЗАКРЫТ**
|
||
|
||
**Был симптом:** loose-плита в ряду 2 верхнего соседа (room5 (2,5) → потолок
|
||
room6 над (0,5)) при прыжках Kid на (0,5) не тряслась и не разбивалась —
|
||
loose-состояние соседней комнаты не тянулось.
|
||
|
||
**Чем закрыт:** отдельное состояние плиты-потолка `pop_ceil_modif[10]`
|
||
(`pop_bg.c:547`, ведёт `pop_map`) + пара `pop_ceil_shake_draw()` /
|
||
`pop_ceil_bake_empty()` (`pop_bg.c:815,827`): дрожание рисуется на текущей
|
||
странице поверх фона, а провал «запекается» в ОЗУ-копию, чтобы heal его
|
||
сохранял. В полосе у потолка виден только НИЗ плиты (`draw_tile_aboveroom`),
|
||
всё выше режется клипом `POP_YOFF` — как в оригинале.
|
||
|
||
Из этого следует, что и запись «требует персистентного per-room modifier
|
||
соседей, это Фаза P0 gates_spikes_plan» больше не верна: понадобился не общий
|
||
механизм, а один массив на 10 байт под конкретный случай.
|
||
|
||
### BUG-CEIL-3. Анимация ворот стирает потолок над ними — **ЗАКРЫТ**
|
||
|
||
**Был симптом:** при анимации решётки шва полоса кладки у потолка НАД
|
||
воротами пропадала — чёрный `bar` по col0 стирал ряд −1, а redraw его не
|
||
восстанавливал.
|
||
|
||
**Чем закрыт:** `pop_room_redraw_seam_left()` (`pop_bg.c:793`) больше не
|
||
трогает полосу потолка — `bar` идёт с `POP_YOFF + 3`, а не с `POP_YOFF`:
|
||
низ полосы кладки на room-space y=2, бары ворот начинаются с y=3
|
||
(`gate_top_y = dby − 62`). Приятный побочный эффект: отпала необходимость
|
||
перерисовывать `draw_tile(-1,0)` на КАЖДОМ кадре анимации решётки — то есть
|
||
фикс не только косметический, но и вдвое дешевле прежнего.
|
||
|
||
### BUG-OCCL-1. Тень дальней колонны перекрывает Kid — **ЗАКРЫТ**
|
||
|
||
**Был симптом:** Kid у (0,4)-(0,5) частично перекрыт тёмной штриховкой —
|
||
боковой гранью ДАЛЬНЕЙ колонны, которая окклюдить персонажа не должна
|
||
(over-occlusion fore-слоя, рисовавшего fore футпринт-тайлов без учёта
|
||
глубины).
|
||
|
||
**Чем закрыт:** разделение слоёв по признаку из оригинала, а не по нашим
|
||
соображениям о глубине (`overlay_mid_tile`, `pop_bg.c:1367`). Ключ,
|
||
подтверждённый трассой оригинала: `draw_tile_right`, `draw_tile_anim_right` и
|
||
`draw_loose` кладут спрайты через `add_backtable` НАПРЯМУЮ, минуя
|
||
`ptr_add_table` — значит правая грань левого соседа («шахматка» столба 93,
|
||
blueline, грани пик/loose соседа, кадр loose) **всегда** рисуется ПОД
|
||
персонажем. Через `ptr_add_table` (→ midtable при `draw_other_overlay`) идут
|
||
только «floor B» 42 при левом соседе-поле, `draw_tile_base`,
|
||
`draw_tile_anim` (свои пики) и `draw_tile_bottom`.
|
||
|
||
---
|
||
|
||
## BUG-SEAM-PINGPONG (#4). Пинг-понг drawn_room у шва с закрытыми воротами — **РЕШЁН 2026-07-22**
|
||
|
||
Настоящий корень найден потиковой трассой ЖИВОГО SDLPoP 1.23
|
||
(lldb-брейкпоинты на leave_room/bumped/safe_step с логом Char +
|
||
char_x_left/right; fixes выключены = vanilla). Прежние гипотезы оставлены
|
||
ниже для истории — они НЕ были причиной.
|
||
|
||
### КОРЕНЬ (подтверждён трассой + исходником)
|
||
|
||
`set_char_collision` (seg006:0723): `char_x_right = obj_x/2 + 58`, где
|
||
`load_frame_to_obj` (seg008:1728) считает `obj_x = 2*char_dx_forward(dx) - 116`
|
||
и **добавляет +1** для кадров «чётного пикселя»:
|
||
`if ((sbyte)(cur_frame.flags ^ obj_direction) >= 0) ++obj_x;`
|
||
(бит 0x80 флагов кадра XOR направление; вправо: +1 если бит НЕ стоит).
|
||
Деление `obj_x/2` — C-усечение К НУЛЮ, поэтому при `e = x+dx <= 57`
|
||
(obj_x < 0, зона левого шва) поправка +1 даёт `char_x_right = e+1`, а при
|
||
e >= 58 формула сокращается к чистому `e`.
|
||
|
||
Итог: Kid, осевший после отскока от ворот шва на x=57 (frame15, флаги 0x43 —
|
||
бит 0x80 не стоит), имеет **char_x_right = 58** и порога leave-left (<=57)
|
||
НЕ достигает. Наш движок считал передний край как `Kid.x + dx` без поправки
|
||
→ 57 → ложный leave → пинг-понг.
|
||
|
||
Эталонный цикл SDLPoP (нормализовано по трассе): стойка x=61 → тап вправо →
|
||
safe_step(d=0) → step, на первом dx(1) x=62 → bump (edge-триггер) → align 61
|
||
→ seq47 dx(-4) → **x=57** (скрыт за кромкой) → кадры 50/51/52 (cxr 61/60/58,
|
||
у всех бит 0x80 снят, e>57 — без сдвига) → стойка cxr=58 → leave НЕ
|
||
срабатывает; тап → safe_step d=3 → x=60 (1/3 видно); тап → step1 → x=61
|
||
(2/3 видно); тап → bump → 57 … по кругу. Char.room и drawn_room НЕ меняются.
|
||
|
||
### Фикс (pop_map.c)
|
||
|
||
`char_x_forward_edge()`: `e = char_dx_forward(dx); if (((flags ^ (dir<0 ?
|
||
0x80 : 0)) & 0x80) == 0 && e <= 57) e++;` — используется в `char_front_coll`
|
||
(коллизия/bump/edge_distance) и в `check_leave` (порог ухода). Плюс порт
|
||
doortop-гарда leave-right из leave_room (тайл (9,row) = doortop → правого
|
||
выхода нет). `pop_leave_timer` (exit_room_timer) оставлен — он реален в
|
||
seg002/seg003.
|
||
|
||
### Симптом (как выглядел)
|
||
|
||
Kid стоит за решёткой закрытых ворот шва (левый сосед room8 виден в кромке
|
||
room6). При удержании/нажатии ВПРАВО экран пинг-понгует между двумя
|
||
состояниями:
|
||
- **A**: показывается room6, Kid у левой кромки за решёткой (спрайт на 2/3);
|
||
- **B**: показывается room8, Kid у его правой кромки.
|
||
|
||
Эталон SDLPoP: drawn_room **всегда остаётся room6**, Kid осциллирует у
|
||
кромки (1/3→2/3→отступил→по кругу), в room8 экран НЕ переключается.
|
||
|
||
### Инструментальный диагноз (watchpoint на pop_leave_dir)
|
||
|
||
В момент лишнего свитча A→B: `pop_leave_dir=1 (LEFT)`, Kid **frame=15 (СТОЯ,
|
||
не transient!), Kid.x=57** (до репроекции +140). То есть:
|
||
- `char_x_right = char_dx_forward(kid_cur_dx()) = Kid.x + frame15.dx = 57+0 = 57`.
|
||
- Порог leave-left (взгляд вправо): `char_x_right <= 57` → срабатывает РОВНО на 57.
|
||
- Грань ворот (где их держит коллизия) = **61** (`wall_dist_from_left[1]=10 +
|
||
coll_tile_left_xpos=51`). Между 57 и 61 — **зазор 4px**: Kid НЕ удержан
|
||
воротами (d=61−57=4≥0 → check_bumped не бампит), но уже на пороге ухода.
|
||
- Kid оседает на 57 из-за recoil отскока: seq_47 = `act(bumped), dx(-4),
|
||
frame_50, 51, 52`; SEQ_DX(−4) двигает Char.x на −4 суммарно; frame_50.dx=4
|
||
компенсирует ТОЛЬКО точку коллизии НА кадре 50, но при возврате в стойку
|
||
(frame15, dx=0) `char_x_right = Char.x = aligned−4 = 57`.
|
||
|
||
### Что было ИСКЛЮЧЕНО (сверено с исходниками SDLPoP, НЕ причина)
|
||
|
||
- Формула char_x: `char_x_right = obj_x/2+58 = Char.x+frame.dx` (seg006
|
||
set_char_collision) — совпадает с нашим char_dx_forward.
|
||
- Позиция грани ворот: `get_left_wall_xpos = wall_dist_from_left[1](10) +
|
||
xpos_in_drawn_room(x_bump[9+5])+7 = 10+(184−140)+7 = 61` — совпадает с нашим
|
||
(`x_bump[−1+5]=44`, +7, +10 = 61).
|
||
- Порог leave: SDLPoP leave_room looking-right `char_x_right<=57` — совпадает.
|
||
- Данные кадров: frame_50 (image=49,dx=4,flags=0x67) и frame_15
|
||
(image=14,dx=0,flags=0x43,sword=9) — БАЙТ-В-БАЙТ как в SDLPoP frame_table_kid.
|
||
- seq_47 (act bumped, dx(-4), frame 50/51/52) — совпадает.
|
||
|
||
### Почему точечные фиксы НЕ работали
|
||
|
||
- Гард в check_leave (подавить leave на закрытых воротах) — это ОТСЕБЯТИНА,
|
||
не SDLPoP (в leave_room такого нет); откачено.
|
||
- `exit_room_timer=2` (порт seg002 exit_room — РЕАЛЬНЫЙ механизм, оставлен как
|
||
pop_leave_timer): блокирует leave 2 кадра после входа в комнату. НЕ спасал:
|
||
положение Char.x=57 **устойчивое** (Kid стоит), а не transient.
|
||
|
||
### Задел, который НЕ понадобился: порт coll_room + отложенный drawn_room
|
||
|
||
Трасса показала, что в эталоне `Char.room`/`drawn_room` вообще не меняются,
|
||
поэтому план S2/S3 для этого бага не потребовался. Остаётся заготовкой под
|
||
стражей/двух персонажей в кадре:
|
||
|
||
1. **Раздельные комнаты.** `Char.room` ≠ `drawn_room`. Уже есть `kid_room`
|
||
(S1) + рендер-смещение `pop_kid_set_render_dx(∓140)`.
|
||
2. **Коллизия по Char.room через coll_room (seg004, S2).** Портировать
|
||
`check_collisions` → `get_row_collision_data` → `get_left_wall_xpos`/
|
||
`get_right_wall_xpos` → `curr_row_coll_room[]`/`_flags[]`,
|
||
`bump_col_left_of_wall`/`bump_col_right_of_wall` → `check_bumped_look_*` →
|
||
`bumped()`. Тайлы — из РЕАЛЬНОЙ комнаты колонки, НЕ из снапшота.
|
||
3. **Отрисовка по drawn_room**, персонаж со сдвигом ±140.
|
||
4. **Отложенная смена drawn_room (seg002/seg000, S3):** `leave_room` →
|
||
`goto_other_room` → `exit_room` (`next_room`) → `check_the_end`.
|
||
|
||
Реализовано на 2026-07-22: S1 + `pop_leave_timer`.
|
||
Память: [[pop_seam_room_model]], [[sdlpop_odd_pixel_char_x]].
|
||
|
||
---
|
||
|
||
## Решено НЕ делать
|
||
|
||
### OPT-1. Хирургический редрой левого шва (ворота соседа) — 2026-07-22
|
||
|
||
**Возможность:** `pop_room_redraw_seam_left()` (pop_bg.c) на каждое изменение
|
||
openness рисует `bar(BLACK)` по всему col0 + **полный `draw_tile(0,0)`**
|
||
(стены, `topright`, `wall_pattern` с `prandom()` — десятки блитов). Реально
|
||
анимируются только бары решётки — `draw_gate_back` (~9 `env_b`).
|
||
|
||
**Стоимость:** seam-блок (синий io_border) занимает ~30-50% кадрового периода,
|
||
**но только пока openness меняется** — во время открытия и медленного
|
||
авто-закрытия (~5 сек после схода с кнопки). В покое — 0%. Замерено в MAME:
|
||
брейк на `_pop_room_redraw_seam_left` (0x5A54) срабатывает ⟺ сегмент дорогой.
|
||
|
||
**Приём** (heal НЕ годится: печёные бары устаревшие, поэтому и стоит
|
||
`bar(BLACK)`+redraw): `bar(BLACK)` только по полосе баров (x=0,
|
||
gate_top..gate_bot) + `draw_gate_back(lmod,...)` + дорисовать статику тайла
|
||
(0,0), задетую полосой. **Пиксель-чувствительно** — обязательна выверка в
|
||
MAME по кромкам.
|
||
|
||
**Решение:** оставляем как есть — стоимость транзиентная, в бюджет
|
||
помещаемся. Делать, только если упрёмся в кадровый бюджет на сценах с
|
||
воротами.
|
||
|
||
---
|
||
|
||
## Исправлено
|
||
|
||
- **Спуск с кнопки (room8, кромка (0,6)): Кид просвечивал в щель, ближняя рука
|
||
срезана до одного пикселя** — ИСПРАВЛЕНО 2026-07-28. Две причины:
|
||
(а) не был портирован `clip_char()` (seg006:1749) — верхняя обрезка спрайта по
|
||
`y_clip[curr_row+1]`, когда тайл над головой стена/пол; сделано
|
||
(`pop_clip_char_top` в pop_map.c + `gfx_blit_cols_part` в libbgi, heal чистит
|
||
уже обрезанный прямоугольник);
|
||
(б) `climb_overlay_tile` выбирал ветку `draw_floor_overlay` (seg008:1E3A) по
|
||
СЫРОМУ коду тайла — а нажатая кнопка в `get_tile_to_draw` (seg008:240)
|
||
подменяется на floor/stuck. Тайл-кнопка не проходил тест floor, уходил в
|
||
`draw_other_overlay` и закрашивал Kid ЦЕЛЫМ тайлом вместо узкой кромки
|
||
`floor_left_overlay[frame-137]`. Фикс — `tile_code_drawn()` (одна подстановка
|
||
на все слои). Проверено покадрово в MAME (кадры 148..138). **Этим же
|
||
фиксом закрыт BUG-3** (см. выше).
|
||
|
||
- **Шов ворот жёг 50% кадра в покое (закрытая решётка)** — ИСПРАВЛЕНО
|
||
2026-07-22. Причина — **баг кодогенератора SDCC z80** (memory
|
||
`sdcc_z80_cmp_store_a_bug`): `if (m[9] != seam_sig) seam_sig = m[9];`
|
||
компилировался в `sub (seam_sig)` (A ← разность) + `ld (seam_sig),a` —
|
||
сохранял РАЗНОСТЬ `m[9]-seam_sig`, не `m[9]`. `seam_sig` осциллировала
|
||
(напр. 25↔231), `m[9]!=seam_sig` истинно каждый кадр → `draw_tile(0,0)`
|
||
каждый кадр даже у неподвижной решётки. Фикс: store-до-сравнения
|
||
(`seam_sig = g;` из чистого `g` ДО `sub`), подтверждён в .asm. Прочёс всех
|
||
модулей PoP: других случайных compare-then-store нет.
|
||
|
||
- **Пики: 2×полный `draw_tile` на кадр анимации** → хирургический редрой,
|
||
2026-07-22. `pop_spike_redraw`: `heal_off` уже возвращает всю печёную
|
||
статику (база 127, пол, грани); поверх анимируются только два острия
|
||
(`SPIKES_FRAM_LEFT` в своей ячейке + `SPIKES_FRAM_RIGHT` в соседней).
|
||
Заменили 2×`draw_tile` на 2×`env_b` — пиксель-в-пиксель тот же результат,
|
||
в разы дешевле (шахта из нескольких пик больше не съедает полный кадр).
|
||
|
||
- **Отрисовка нажатой кнопки (0,2)/(0,3)** — ИСПРАВЛЕНО. Причина: `fore_tile`
|
||
(pop_bg.c, fore-слой поверх Kid) рисовал переднюю грань КНОПКИ (bottom_id
|
||
149) поверх уже нарисованной грани пола (43) — «остаток нажатой кнопки».
|
||
Фикс: `fore_tile` применяет ту же подстановку нажатой кнопки, что и
|
||
`draw_tile` (opener→floor / closer→stuck при таймере связи >1). Плюс
|
||
`pop_button_redraw` — wipe своей ячейки + правой грани (дальний угол в
|
||
0,3), низ строго yb+64 (не залезать в стену ряда 1).
|
||
|
||
---
|
||
|
||
## НЕ БАГИ (кривая картинка, но совпадает с оригиналом — НЕ чинить)
|
||
|
||
- **Голова стоящего Кида поверх падающей на него loose-плиты.** Комната 12:
|
||
зацеп не удался, Кид остался стоять, сбитая плита падает прямо на него —
|
||
голова рисуется ПОВЕРХ плиты. Сверено покадрово с SDLPoP v1.24 (2026-07-29):
|
||
там ровно то же самое. Артефакт оригинального движка (порядок midtable), а
|
||
не наш баг; «починка» увела бы от эталона. Отличать от соседних случаев,
|
||
которые БЫЛИ нашими багами и исправлены: вис/подтягивание на кромке плиты и
|
||
падение вместе с плитой — там плита обязана быть поверх Кида.
|
||
|
||
- **Комнаты 13, 18, 24 недостижимы в обычной игре** — свойство ДАННЫХ уровня 1,
|
||
не наш баг. Обход графа от стартовой комнаты (по `res2001.bin`, links @1952)
|
||
показывает: у всех трёх ссылки наружу есть, а на них не ссылается никто
|
||
(24: `L→9`, но у 9 `R=0`; 13 и 18 связаны только друг с другом). Признак
|
||
«комнату выкинули из компоновки, связи не почистили» — несимметричные ссылки
|
||
ровно у этих трёх, у остальных 21 симметрия полная:
|
||
|
||
```
|
||
13 L→22, у 22 R=16 | 18 L→15, у 15 R=12 | 24 L→9, у 9 R=0
|
||
13 R→16, у 16 L=22 | 18 R→12, у 12 L=15
|
||
| 18 D→19, у 19 U=12
|
||
```
|
||
|
||
Следствие: в 13/18/24 возможен «мусор в шве» — наш рендер кромки читает
|
||
крайнюю колонку соседа ПО ССЫЛКЕ, а сосед этих комнат соседом себя не
|
||
считает. Приоритет багов в этих трёх комнатах — низкий (в игре не видно).
|