docs: bug_list — только открытые баги; закрытые → bug_closed.md
Три Critical'а (BUG-1 провал на row 1 при боковом переходе, BUG-2 ping-pong при возврате, BUG-3 окклюзия climb-up на кнопке) висели непроверенными с 2026-07-21. Прогнал в MAME: - BUG-1 не воспроизводится: room6 → кнопка (0,2) → открытая решётка → переход влево даёт room8, y=55, curr_row=0. Заодно снят и сам диагноз записи — репроекция Y при БОКОВОМ переходе не нужна: goto_other_room (seg002.c:390) меняет только x, наш check_leave делает то же. - BUG-2 не воспроизводится: шов room2↔room3, четыре пересечения с разворотом сразу после входа — комната меняется ровно раз на пересечение. - BUG-3 закрыт фиксом tile_code_drawn от 2026-07-28 (это дубль уже записанного «спуск с кнопки»); оговорка про непереснятый подъём — в bug_closed.md. bug_list.md теперь только открытое (BUG-CEIL-1/2/3, BUG-OCCL-1, T-1, T-2, таблица обхода 24 комнат) + индекс с якорями. bug_closed.md — закрытое вместе с разбором корней (odd-pixel char_x, подстановка тайла кнопки, баг кодогенератора SDCC), он и есть главная ценность архива. TASKS.md: кросслинки на открытые баги в шапке, в L1-TRIAGE, L1-PASS и «Отложено». Указатели в CLAUDE.md/README/room_model_plan/layout_plan_v2 переведены на нужный из двух файлов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,8 @@ Princed для DAT v1.0 (контейнер/индекс/чек-сумма, ко
|
|||||||
статусами).
|
статусами).
|
||||||
- `roomtest/` — **активная разработка**: уровень 1 целиком (Kid, стражи,
|
- `roomtest/` — **активная разработка**: уровень 1 целиком (Kid, стражи,
|
||||||
ловушки, ворота, loose-полы). Свой `CLAUDE.md`; текущие задачи —
|
ловушки, ворота, loose-полы). Свой `CLAUDE.md`; текущие задачи —
|
||||||
`roomtest/TASKS.md`, баги — `roomtest/bug_list.md`.
|
`roomtest/TASKS.md`, открытые баги — `roomtest/bug_list.md`, закрытые с
|
||||||
|
разбором корней — `roomtest/bug_closed.md`.
|
||||||
- `poc/` — ранний proof-of-concept (снег/атлас/kbd_raw); ассеты в `poc/res/`.
|
- `poc/` — ранний proof-of-concept (снег/атлас/kbd_raw); ассеты в `poc/res/`.
|
||||||
- `bgtest/`, `coltest/` — отдельные проверки фона/коллизии.
|
- `bgtest/`, `coltest/` — отдельные проверки фона/коллизии.
|
||||||
- `toolchain/` — python-упаковщики ассетов + эталонные PNG (`1.1-2.png`).
|
- `toolchain/` — python-упаковщики ассетов + эталонные PNG (`1.1-2.png`).
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
| Документ | О чём |
|
| Документ | О чём |
|
||||||
|----------|-------|
|
|----------|-------|
|
||||||
| [`../roomtest/TASKS.md`](../roomtest/TASKS.md) | **Что берётся в работу сейчас** (не в этой папке, но входная точка) |
|
| [`../roomtest/TASKS.md`](../roomtest/TASKS.md) | **Что берётся в работу сейчас** (не в этой папке, но входная точка) |
|
||||||
|
| [`../roomtest/bug_list.md`](../roomtest/bug_list.md) | Открытые баги roomtest (закрытые — в `bug_closed.md` рядом) |
|
||||||
| [`levels_plan.md`](levels_plan.md) | Следующий этап: уровни 2+, второй тайлсет, читы SDLPoP |
|
| [`levels_plan.md`](levels_plan.md) | Следующий этап: уровни 2+, второй тайлсет, читы SDLPoP |
|
||||||
| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки |
|
| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки |
|
||||||
| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди |
|
| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди |
|
||||||
|
|||||||
@@ -506,7 +506,8 @@ memory `mame_mcp_bridge`); в самом `roomtest.c` для этого уже
|
|||||||
(до 7 блитов). Сгенерировать в атласе «столб решётки» и выводить одним
|
(до 7 блитов). Сгенерировать в атласе «столб решётки» и выводить одним
|
||||||
`gfx_blit_part` с обрезкой по фазе `gate_bot_y & 7`: 7 блитов → 1.
|
`gfx_blit_part` с обрезкой по фазе `gate_bot_y & 7`: 7 блитов → 1.
|
||||||
3. **Не перерисовывать статичные части шва** — грань ворот, пол и кромка при
|
3. **Не перерисовывать статичные части шва** — грань ворот, пол и кромка при
|
||||||
анимации решётки не меняются (см. OPT-1 в `../roomtest/bug_list.md`).
|
анимации решётки не меняются (см. OPT-1 в `../roomtest/bug_closed.md` —
|
||||||
|
решено не делать, стоимость транзиентная).
|
||||||
4. **T-1 / T-2** (`../roomtest/bug_list.md`) — перерисовка пик по причине и
|
4. **T-1 / T-2** (`../roomtest/bug_list.md`) — перерисовка пик по причине и
|
||||||
idle-skip Кида: самый большой оставшийся резерв, потому что убирает работу
|
idle-skip Кида: самый большой оставшийся резерв, потому что убирает работу
|
||||||
целиком, а не удешевляет её.
|
целиком, а не удешевляет её.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
> всегда 0.
|
> всегда 0.
|
||||||
> - **S2/S3/S4 — не сделаны и не срочны.** Исходный повод (баг #4,
|
> - **S2/S3/S4 — не сделаны и не срочны.** Исходный повод (баг #4,
|
||||||
> пинг-понг у шва) закрыт иначе — поправкой odd-pixel в
|
> пинг-понг у шва) закрыт иначе — поправкой odd-pixel в
|
||||||
> `char_x_forward_edge` + `pop_leave_timer` (см. `../roomtest/bug_list.md`,
|
> `char_x_forward_edge` + `pop_leave_timer` (разбор корня —
|
||||||
> BUG-SEAM-PINGPONG).
|
> `../roomtest/bug_closed.md`, BUG-SEAM-PINGPONG).
|
||||||
>
|
>
|
||||||
> **Зачем документ остаётся.** Полная straddle-модель понадобится для:
|
> **Зачем документ остаётся.** Полная straddle-модель понадобится для:
|
||||||
> (а) читов осмотра соседних комнат `H/J/U/N` (`levels_plan.md` §4),
|
> (а) читов осмотра соседних комнат `H/J/U/N` (`levels_plan.md` §4),
|
||||||
@@ -78,7 +78,7 @@ drawn_room с `curr_col=-1/10` + снапшоты соседей `g_lcol/g_rcol`
|
|||||||
### S4. Полировка
|
### S4. Полировка
|
||||||
- Окклюзия/ceiling у шва при straddle, BUG-OCCL-1 (глубина), правый край.
|
- Окклюзия/ceiling у шва при straddle, BUG-OCCL-1 (глубина), правый край.
|
||||||
|
|
||||||
## Связанные баги (bug_list.md)
|
## Связанные баги (`../roomtest/bug_list.md` — все открыты)
|
||||||
BUG-CEIL-1 (руки при прыжке вверх), BUG-CEIL-2 (loose в потолке),
|
BUG-CEIL-1 (руки при прыжке вверх), BUG-CEIL-2 (loose в потолке),
|
||||||
BUG-CEIL-3 (потолок над анимируемыми воротами), BUG-OCCL-1 (тень дальней
|
BUG-CEIL-3 (потолок над анимируемыми воротами), BUG-OCCL-1 (тень дальней
|
||||||
колонны). Memory: `pop_seam_room_model`.
|
колонны). Memory: `pop_seam_room_model`.
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
по умолчанию баг у нас.
|
по умолчанию баг у нас.
|
||||||
|
|
||||||
**Что в работе сейчас — `TASKS.md`** (доска задач: приоритеты, критерии
|
**Что в работе сейчас — `TASKS.md`** (доска задач: приоритеты, критерии
|
||||||
готовности). Баги — `bug_list.md` (его шапка честно говорит, что список
|
готовности). Баги — `bug_list.md` (только ОТКРЫТЫЕ) и `bug_closed.md`
|
||||||
отстал). План следующих уровней — `../docs/levels_plan.md`.
|
(закрытые + разбор корней: перед заведением нового бага грепни там по
|
||||||
|
симптому). План следующих уровней — `../docs/levels_plan.md`.
|
||||||
|
|
||||||
## Сборка и запуск
|
## Сборка и запуск
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
# roomtest — доска текущих задач (обновлено 2026-08-01)
|
# roomtest — доска текущих задач (обновлено 2026-08-01)
|
||||||
|
|
||||||
Не список багов (он в `bug_list.md`) и не план фаз (`../docs/PORT_PLAN.md`,
|
Не список багов (открытые — [`bug_list.md`](bug_list.md), закрытые с разбором
|
||||||
`../docs/layout_plan_v2.md`, `../docs/levels_plan.md`), а то, **что берём в
|
корней — [`bug_closed.md`](bug_closed.md)) и не план фаз
|
||||||
работу сейчас и в каком порядке**. Каждая запись: что сделать, почему
|
(`../docs/PORT_PLAN.md`, `../docs/layout_plan_v2.md`, `../docs/levels_plan.md`),
|
||||||
именно сейчас, чем подтверждать результат.
|
а то, **что берём в работу сейчас и в каком порядке**. Каждая запись: что
|
||||||
|
сделать, почему именно сейчас, чем подтверждать результат.
|
||||||
|
|
||||||
|
**Открытые баги** (все Medium/косметика, Critical'ов нет):
|
||||||
|
[BUG-CEIL-1](bug_list.md#bug-ceil-1) · [BUG-CEIL-3](bug_list.md#bug-ceil-3) ·
|
||||||
|
[BUG-OCCL-1](bug_list.md#bug-occl-1) ·
|
||||||
|
[BUG-CEIL-2](bug_list.md#bug-ceil-2) (отложен) ·
|
||||||
|
оптимизации [T-1](bug_list.md#t-1), [T-2](bug_list.md#t-2).
|
||||||
|
|
||||||
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
||||||
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
|
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
|
||||||
@@ -112,7 +119,7 @@
|
|||||||
`libc/irq/_irq_tramp.c` в общий кусок либо продублировать в
|
`libc/irq/_irq_tramp.c` в общий кусок либо продублировать в
|
||||||
`libc/kbd/kbd_raw_poll.c`; тело обязано идти под `DI` (гонка с ISR за
|
`libc/kbd/kbd_raw_poll.c`; тело обязано идти под `DI` (гонка с ISR за
|
||||||
деструктивное чтение порта 0x18).
|
деструктивное чтение порта 0x18).
|
||||||
5. Побочно сюда же играет **T-2 (idle-skip)** из `bug_list.md`: не
|
5. Побочно сюда же играет **[T-2](bug_list.md#t-2) (idle-skip)**: не
|
||||||
перерисовывать Кида, пока поза/координаты не менялись, — это минус
|
перерисовывать Кида, пока поза/координаты не менялись, — это минус
|
||||||
heal+blit (то есть минус DI-окна) в самых спокойных кадрах, где как раз
|
heal+blit (то есть минус DI-окна) в самых спокойных кадрах, где как раз
|
||||||
и тапают Shift+стрелку.
|
и тапают Shift+стрелку.
|
||||||
@@ -332,14 +339,25 @@
|
|||||||
`pop_next_level`, который взводит `END_LEVEL` (`seg006.c:662`). **Это же
|
`pop_next_level`, который взводит `END_LEVEL` (`seg006.c:662`). **Это же
|
||||||
первый шаг плана следующих уровней** — см. `../docs/levels_plan.md`.
|
первый шаг плана следующих уровней** — см. `../docs/levels_plan.md`.
|
||||||
|
|
||||||
### L1-TRIAGE. Ревизия `bug_list.md`
|
### L1-TRIAGE. Ревизия багов — **ЧАСТЬ 1 СДЕЛАНА 2026-08-01**
|
||||||
Список отстал от кода: BUG-1/BUG-2 (боковой переход, ping-pong) закрываются
|
|
||||||
`pop_leave_timer` + `char_x_forward_edge` (`pop_map.c:1402..1432,1553`),
|
✅ **Три Critical'а прогнаны в MAME и закрыты** (протокол с числами — в
|
||||||
BUG-3 (окклюзия climb-up на кнопке) — фиксом `tile_code_drawn` от 2026-07-28,
|
[`bug_closed.md`](bug_closed.md), раздел «Проверено в MAME 2026-08-01»):
|
||||||
но все три по-прежнему стоят как **Critical**. Пройти их в MAME, закрыть
|
BUG-1 (провал на row 1 при переходе через открытые ворота) и BUG-2
|
||||||
подтверждённые, оставшиеся (BUG-CEIL-1/2/3, BUG-OCCL-1 — косметика окклюзии)
|
(ping-pong при возврате) **не воспроизводятся**, BUG-3 (окклюзия climb-up на
|
||||||
переклассифицировать. Заодно закрыть таблицу обхода 24 комнат — она
|
кнопке) закрыт фиксом `tile_code_drawn` от 2026-07-28. Заодно снят неверный
|
||||||
заполнена на 5 строк из 24, а инструмент (`ROOMNAV`) готов.
|
диагноз BUG-1: репроекция Y при БОКОВОМ переходе — не наш пробел, а точное
|
||||||
|
поведение `goto_other_room` (`SDLPoP/src/seg002.c:390` меняет только `x`).
|
||||||
|
Список разделён на [`bug_list.md`](bug_list.md) (открытое) и
|
||||||
|
[`bug_closed.md`](bug_closed.md) (закрытое + разбор корней).
|
||||||
|
|
||||||
|
⏳ **Осталось:** закрыть таблицу обхода 24 комнат
|
||||||
|
([`bug_list.md`](bug_list.md#обход-всех-24-комнат-уровня-1)) — заполнена на
|
||||||
|
5 строк из 24, инструмент (`ROOMNAV`) готов. Открытая косметика окклюзии
|
||||||
|
([BUG-CEIL-1](bug_list.md#bug-ceil-1),
|
||||||
|
[BUG-CEIL-3](bug_list.md#bug-ceil-3),
|
||||||
|
[BUG-OCCL-1](bug_list.md#bug-occl-1)) остаётся Medium — трогать после
|
||||||
|
L1-PASS, обход комнат может добавить к ней новые случаи.
|
||||||
|
|
||||||
### L1-SPEED. Игра идёт быстрее оригинала (найдено 2026-08-01)
|
### L1-SPEED. Игра идёт быстрее оригинала (найдено 2026-08-01)
|
||||||
Сверка таймингов: оригинал — `BASE_FPS = 60` при `base_speed = 5` тиков на
|
Сверка таймингов: оригинал — `BASE_FPS = 60` при `base_speed = 5` тиков на
|
||||||
@@ -356,18 +374,25 @@ BUG-3 (окклюзия climb-up на кнопке) — фиксом `tile_code_
|
|||||||
пики, loose-полы, зелье, падения. Это приёмка этапа 1 и одновременно
|
пики, loose-полы, зелье, падения. Это приёмка этапа 1 и одновременно
|
||||||
регресс-база для уровня 2.
|
регресс-база для уровня 2.
|
||||||
|
|
||||||
|
Заодно проверить на живом проходе открытую косметику окклюзии —
|
||||||
|
[BUG-CEIL-1](bug_list.md#bug-ceil-1),
|
||||||
|
[BUG-CEIL-3](bug_list.md#bug-ceil-3),
|
||||||
|
[BUG-OCCL-1](bug_list.md#bug-occl-1): именно здесь станет видно, мешают ли
|
||||||
|
они играть или это придирки на стоп-кадре. И подъём на тайл-кнопку — см.
|
||||||
|
оговорку к BUG-3 в [`bug_closed.md`](bug_closed.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Отложено осознанно (не брать, пока не появится причина)
|
## Отложено осознанно (не брать, пока не появится причина)
|
||||||
|
|
||||||
- **Звук** (CBL-эффекты, Фаза 5 `PORT_PLAN.md`) — геймплей не блокирует.
|
- **Звук** (CBL-эффекты, Фаза 5 `PORT_PLAN.md`) — геймплей не блокирует.
|
||||||
- **Таймер уровня / HUD времени / меню / сохранения** — Фаза 6.
|
- **Таймер уровня / HUD времени / меню / сохранения** — Фаза 6.
|
||||||
- **T-1** (пики: перерисовка по причине) — `bug_list.md`; отдаётся почти
|
- **[T-1](bug_list.md#t-1)** (пики: перерисовка по причине) — отдаётся почти
|
||||||
бесплатно после T-2, отдельно не окупается.
|
бесплатно после [T-2](bug_list.md#t-2), отдельно не окупается.
|
||||||
- **BUG-CEIL-2** (loose-плита в потолке из комнаты сверху) — требует
|
- **[BUG-CEIL-2](bug_list.md#bug-ceil-2)** (loose-плита в потолке из комнаты
|
||||||
персистентного per-room modifier соседей, это Фаза P0 из
|
сверху) — требует персистентного per-room modifier соседей, это Фаза P0 из
|
||||||
`../docs/gates_spikes_plan.md`.
|
`../docs/gates_spikes_plan.md`.
|
||||||
- **Отключение мыши на время игры** и **замена PRNG** —
|
- **Отключение мыши на время игры** и **замена PRNG** —
|
||||||
`../docs/ideas_backlog.md` (оба дают доли процента кадра).
|
`../docs/ideas_backlog.md` (оба дают доли процента кадра).
|
||||||
- **OPT-1** (хирургический редрой шва) — стоимость транзиентная, решение от
|
- **OPT-1** (хирургический редрой шва) — решено НЕ делать, стоимость
|
||||||
2026-07-22 «оставляем».
|
транзиентная; разбор в [`bug_closed.md`](bug_closed.md).
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
# 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 поверх кнопки на подъёме — заводи заново.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 возможен «мусор в шве» — наш рендер кромки читает
|
||||||
|
крайнюю колонку соседа ПО ССЫЛКЕ, а сосед этих комнат соседом себя не
|
||||||
|
считает. Приоритет багов в этих трёх комнатах — низкий (в игре не видно).
|
||||||
@@ -1,168 +1,31 @@
|
|||||||
# roomtest — список известных багов (Фаза B и смежное)
|
# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации
|
||||||
|
|
||||||
> **Список отстал от кода (ревизия 2026-08-01, задача L1-TRIAGE в
|
Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней —
|
||||||
> [`TASKS.md`](TASKS.md)).** По исходникам похоже, что закрыты, но в MAME не
|
odd-pixel арифметика `char_x`, подстановка тайла нажатой кнопки, баг
|
||||||
> переподтверждены: **BUG-1** и **BUG-2** (боковой переход/ping-pong —
|
кодогенератора SDCC) переехало в [`bug_closed.md`](bug_closed.md): прежде чем
|
||||||
> `pop_map.c` `pop_leave_timer` + `char_x_forward_edge`, см. также
|
заводить новый баг, грепни там по симптому.
|
||||||
> BUG-SEAM-PINGPONG ниже) и **BUG-3** (окклюзия climb-up на кнопке — фикс
|
|
||||||
> `tile_code_drawn` от 2026-07-28, описан в разделе «Исправлено»). Пока не
|
|
||||||
> проверены — статус **Critical** оставлен как есть, не полагаться на него.
|
|
||||||
> Текущие приоритеты работ — в [`TASKS.md`](TASKS.md), а не здесь.
|
|
||||||
|
|
||||||
Найдено при тестировании Фазы B (кнопки/ворота) в MAME. **НЕ решаем сейчас**
|
Приоритеты работ — в [`TASKS.md`](TASKS.md), а не здесь. Правило проекта:
|
||||||
— задача следующего этапа. Правило проекта: механику сверять с
|
механику сверять с `../SDLPoP/src/` ДО кодинга.
|
||||||
`applications/PoP/SDLPoP/src/` ДО кодинга.
|
|
||||||
|
|
||||||
Статус на момент записи: 2026-07-21. Тестовый старт — комната 6 (`START_ROOM`
|
Ревизия списка: **2026-08-01** (задача L1-TRIAGE). BUG-1, BUG-2 и BUG-3
|
||||||
в `roomtest.c`). Кнопка room6(0,2, opener) → ворота room8(0,9), видны в левом
|
стояли как Critical с 2026-07-21 — все три прогнаны в MAME и закрыты,
|
||||||
шве room6.
|
протокол в [`bug_closed.md`](bug_closed.md). Critical'ов сейчас нет.
|
||||||
|
|
||||||
|
| ID | что | тяжесть |
|
||||||
|
|----|-----|---------|
|
||||||
|
| [BUG-CEIL-1](#bug-ceil-1) | прыжок вверх: руки Kid поверх потолка | Medium |
|
||||||
|
| [BUG-CEIL-3](#bug-ceil-3) | анимация ворот стирает потолок над ними | Medium |
|
||||||
|
| [BUG-OCCL-1](#bug-occl-1) | тень дальней колонны перекрывает Kid | Medium |
|
||||||
|
| [BUG-CEIL-2](#bug-ceil-2) | loose-плита в потолке не трясётся | Medium, отложен |
|
||||||
|
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация |
|
||||||
|
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация |
|
||||||
|
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BUG-1. Боковой переход через ворота: Kid проваливается на row 1 — **Critical**
|
<a id="bug-ceil-1"></a>
|
||||||
|
## BUG-CEIL-1. Прыжок вверх: руки Kid рисуются ПОВЕРХ потолка — **Medium**
|
||||||
**Симптом:** при проходе через ОТКРЫТЫЕ ворота в соседнюю комнату (через шов)
|
|
||||||
Kid оказывается на ряду **row 1** вместо row 0 — «проваливается» на этаж ниже
|
|
||||||
(стоит в дверном проёме на неправильной высоте).
|
|
||||||
|
|
||||||
**Где:** `pop_map.c` `check_leave()` (порт seg002 leave_room) — боковой уход
|
|
||||||
`pop_leave_dir`; репроекция координат `Kid.x ∓= 140` есть, но Y/`curr_row`
|
|
||||||
при боковом переходе НЕ репроецируются (в отличие от `check_leave_below`,
|
|
||||||
где `Kid.y -= 189; curr_row = y_to_row`). При входе в соседнюю комнату у
|
|
||||||
шва Kid, видимо, попадает в позицию, где `determine_col`/физика следующего
|
|
||||||
кадра сбрасывают его на row 1 (нет пола на row 0 у входа, либо неверный y).
|
|
||||||
|
|
||||||
**Сверить с SDLPoP:** `goto_other_room` (seg002) — как именно
|
|
||||||
пересчитываются `Char.curr_row`/`Char.y` при side-переходе (left/right).
|
|
||||||
Для вертикального перехода мы это сделали, для бокового — нет.
|
|
||||||
|
|
||||||
## BUG-2. Возврат из комнаты назад: Kid отбрасывается обратно (ping-pong) — **Critical**
|
|
||||||
|
|
||||||
**Симптом:** после перехода в соседнюю комнату попытка сразу вернуться
|
|
||||||
обратно приводит к тому, что Kid снова закидывается в ту же комнату —
|
|
||||||
переход «пинг-понгует», выйти нельзя.
|
|
||||||
|
|
||||||
**Где:** `pop_map.c` `check_leave()`. После перехода Kid репроецирован к
|
|
||||||
ПРОТИВОПОЛОЖНОЙ кромке новой комнаты (`x ∓ 140`). На СЛЕДУЮЩЕМ же кадре
|
|
||||||
передний край снова за порогом ухода в обратную сторону → `pop_leave_dir`
|
|
||||||
взводится опять → мгновенный обратный переход. Нужен guard «только что
|
|
||||||
перешёл» (подавить повторный leave на N кадров / пока Kid не отойдёт от
|
|
||||||
кромки), как в оригинале (`next_room`/leave обрабатываются раз за кадр +
|
|
||||||
Kid входит достаточно внутрь).
|
|
||||||
|
|
||||||
**Сверить с SDLPoP:** порядок `leave_room`/`goto_other_room` + на какой
|
|
||||||
X ставится персонаж при входе (должен быть НЕ на самой кромке, иначе
|
|
||||||
мгновенный обратный триггер).
|
|
||||||
|
|
||||||
## BUG-3. Climb-up на тайл-кнопку: неправильная окклюзия (Kid поверх кнопки) — **Critical**
|
|
||||||
|
|
||||||
**Симптом:** когда Kid подтягивается (climb-up) на тайл, верх которого —
|
|
||||||
КНОПКА (opener/closer), а не обычный пол, отрисовка неверная: Kid рисуется
|
|
||||||
ПОВЕРХ кнопки, хотя передняя грань пола/кнопки должна перекрывать нижнюю
|
|
||||||
часть Kid во время подъёма.
|
|
||||||
|
|
||||||
**Где:** `pop_bg.c` `climb_overlay_tile()` — floor-overlay при подъёме
|
|
||||||
рисуется только для floor-подобных тайлов `code ∈ {1,3,5,19}`. Кнопка
|
|
||||||
(opener 0x0F / closer 0x06) в этот набор НЕ входит → пол-оверлей кнопки не
|
|
||||||
рисуется поверх Kid. Нужно: для нажатой кнопки (draw как floor/stuck)
|
|
||||||
трактовать её как floor-тайл в climb-overlay (учесть подстановку из
|
|
||||||
`get_tile_to_draw`), и/или добавить opener/closer в набор.
|
|
||||||
|
|
||||||
**Сверить с SDLPoP:** `draw_floor_overlay` (seg008) + `get_tile_to_draw`
|
|
||||||
(нажатая кнопка → tiles_1_floor / tiles_5_stuck) — оверлей должен идти по
|
|
||||||
ПОДСТАВЛЕННОМУ типу тайла.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Оптимизации (отложено)
|
|
||||||
|
|
||||||
### 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 сек после схода с кнопки). В покое (ворота открыты-держим
|
|
||||||
кнопку / закрыты / FF) — 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), задетую полосой. Ожидаемо срезает бóльшую часть 30-50%.
|
|
||||||
**Пиксель-чувствительно** — полоса баров пересекается со статикой col0,
|
|
||||||
обязательна выверка в MAME по кромкам.
|
|
||||||
|
|
||||||
**Решение (2026-07-22):** оставляем как есть — стоимость транзиентная, в бюджет
|
|
||||||
помещаемся. Делать, если упрёмся в кадровый бюджет на сценах с воротами.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Исправлено
|
|
||||||
|
|
||||||
- **Спуск с кнопки (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).
|
|
||||||
|
|
||||||
- **Шов ворот жёг 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), а
|
|
||||||
не наш баг; «починка» увела бы от эталона. Отличать от соседних случаев,
|
|
||||||
которые БЫЛИ нашими багами и исправлены: вис/подтягивание на кромке плиты и
|
|
||||||
падение вместе с плитой — там плита обязана быть поверх Кида.
|
|
||||||
|
|
||||||
## Заметки
|
|
||||||
|
|
||||||
- Тестовые клавиши осторожного шага: **J** = шаг влево, **L** = шаг вправо
|
|
||||||
(эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в
|
|
||||||
сторону = разворот (как в оригинале safe_step), движение со второго.
|
|
||||||
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`).
|
|
||||||
- `START_ROOM=6` — временный тестовый старт; вернуть на
|
|
||||||
`pop_level_start_room()` после отладки объектов.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## BUG-CEIL-1. Прыжок вверх: руки Kid рисуются ПОВЕРХ потолка — **Medium**
|
|
||||||
|
|
||||||
**Симптом (2026-07-22):** при прыжке вверх (SEQ up, кадры 67..79) руки/голова
|
**Симптом (2026-07-22):** при прыжке вверх (SEQ up, кадры 67..79) руки/голова
|
||||||
Kid заходят в полосу кладки у потолка (row -1) и рисуются ПОВЕРХ неё — потолок
|
Kid заходят в полосу кладки у потолка (row -1) и рисуются ПОВЕРХ неё — потолок
|
||||||
@@ -173,29 +36,8 @@ Kid заходят в полосу кладки у потолка (row -1) и р
|
|||||||
`pop_fore_over_kid` (перерисовать срез полосы над Kid поверх спрайта), по
|
`pop_fore_over_kid` (перерисовать срез полосы над Kid поверх спрайта), по
|
||||||
аналогии с fore-тайлами. Инфраструктура `g_clip_top`/`gfx_blit_part` уже есть.
|
аналогии с fore-тайлами. Инфраструктура `g_clip_top`/`gfx_blit_part` уже есть.
|
||||||
|
|
||||||
## BUG-CEIL-2. Тряска/разбитие loose-плиты в потолке (row -1) не работает — **Medium**
|
<a id="bug-ceil-3"></a>
|
||||||
|
## BUG-CEIL-3. Анимация ворот стирает потолок над ними — **Medium**
|
||||||
**Симптом (2026-07-22):** loose-плита в ряду 2 верхнего соседа (room5 (2,5) →
|
|
||||||
потолок room6 над (0,5)) при прыжках Kid на (0,5) НЕ трясётся и не разбивается.
|
|
||||||
|
|
||||||
**Где:** верхний сосед не «живой» — `above_fg` берётся из статики уровня,
|
|
||||||
loose-состояние соседней комнаты не тянем (per-room loose/modif не персистятся,
|
|
||||||
P0). check_press/knock работают только по текущей комнате. Нужно: (а) пробой
|
|
||||||
loose сверху через границу комнаты, (б) персистентное loose-состояние соседа +
|
|
||||||
его отрисовка в полосе. Пока полоса рисует loose статически (кадр покоя).
|
|
||||||
|
|
||||||
## BUG-OCCL-1. Тень дальней колонны перекрывает Kid в (0,4)-(0,5) — **Medium**
|
|
||||||
|
|
||||||
**Симптом (2026-07-22):** Kid, стоя/прыгая у (0,4)-(0,5), частично перекрыт
|
|
||||||
тёмной штриховкой — это ТЕНЬ/боковая грань ДАЛЬНЕЙ колонны (сзади по глубине),
|
|
||||||
которая НЕ должна окклюдить персонажа. Over-occlusion fore-слоя.
|
|
||||||
|
|
||||||
**Где:** `pop_bg.c` `pop_fore_over_kid`/`fore_tile` — рисует fore футпринт-тайлов
|
|
||||||
без учёта глубины (какая грань РЕАЛЬНО перед Kid). Порт seg003 redraw_at_char
|
|
||||||
окклюдит только передние грани; дальняя колонна (за Kid) не должна. Сверить с
|
|
||||||
SDLPoP redraw_at_char (какие тайлы/грани идут в fore vs back по y-сортировке).
|
|
||||||
|
|
||||||
## BUG-CEIL-3. Анимация ворот стирает потолок над ними — **Medium**
|
|
||||||
|
|
||||||
**Симптом (2026-07-22):** при опускании/поднятии решётки шва (анимация openness)
|
**Симптом (2026-07-22):** при опускании/поднятии решётки шва (анимация openness)
|
||||||
полоса кладки у потолка НАД воротами пропадает (см. потолок над левым швом).
|
полоса кладки у потолка НАД воротами пропадает (см. потолок над левым швом).
|
||||||
@@ -206,167 +48,37 @@ SDLPoP redraw_at_char (какие тайлы/грани идут в fore vs back
|
|||||||
Нужно: при перерисовке шва/тайла у потолка перерисовать и срез потолочной полосы
|
Нужно: при перерисовке шва/тайла у потолка перерисовать и срез потолочной полосы
|
||||||
(draw_tile(-1,col) с g_clip_top) над затронутыми колонками.
|
(draw_tile(-1,col) с g_clip_top) над затронутыми колонками.
|
||||||
|
|
||||||
---
|
<a id="bug-occl-1"></a>
|
||||||
|
## BUG-OCCL-1. Тень дальней колонны перекрывает Kid в (0,4)-(0,5) — **Medium**
|
||||||
|
|
||||||
# BUG-SEAM-PINGPONG (#4). Пинг-понг drawn_room у шва с закрытыми воротами — **РЕШЁН 2026-07-22**
|
**Симптом (2026-07-22):** Kid, стоя/прыгая у (0,4)-(0,5), частично перекрыт
|
||||||
|
тёмной штриховкой — это ТЕНЬ/боковая грань ДАЛЬНЕЙ колонны (сзади по глубине),
|
||||||
|
которая НЕ должна окклюдить персонажа. Over-occlusion fore-слоя.
|
||||||
|
|
||||||
Статус: 2026-07-22, вечер. НАСТОЯЩИЙ корень найден потиковой трассой ЖИВОГО
|
**Где:** `pop_bg.c` `pop_fore_over_kid`/`fore_tile` — рисует fore футпринт-тайлов
|
||||||
SDLPoP 1.23 (lldb-брейкпоинты на leave_room/bumped/safe_step с логом Char +
|
без учёта глубины (какая грань РЕАЛЬНО перед Kid). Порт seg003 redraw_at_char
|
||||||
char_x_left/right; fixes выключены = vanilla). Все прежние гипотезы ниже
|
окклюдит только передние грани; дальняя колонна (за Kid) не должна. Сверить с
|
||||||
оставлены для истории — они НЕ были причиной.
|
SDLPoP redraw_at_char (какие тайлы/грани идут в fore vs back по y-сортировке).
|
||||||
|
|
||||||
## КОРЕНЬ (подтверждён трассой + исходником)
|
<a id="bug-ceil-2"></a>
|
||||||
|
## BUG-CEIL-2. Тряска/разбитие loose-плиты в потолке (row -1) не работает — **Medium, ОТЛОЖЕН**
|
||||||
|
|
||||||
`set_char_collision` (seg006:0723): `char_x_right = obj_x/2 + 58`, где
|
**Симптом (2026-07-22):** loose-плита в ряду 2 верхнего соседа (room5 (2,5) →
|
||||||
`load_frame_to_obj` (seg008:1728) считает `obj_x = 2*char_dx_forward(dx) - 116`
|
потолок room6 над (0,5)) при прыжках Kid на (0,5) НЕ трясётся и не разбивается.
|
||||||
и **добавляет +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 —
|
**Где:** верхний сосед не «живой» — `above_fg` берётся из статики уровня,
|
||||||
бит 0x80 не стоит), имеет **char_x_right = 58** и порога leave-left (<=57)
|
loose-состояние соседней комнаты не тянем (per-room loose/modif не персистятся).
|
||||||
НЕ достигает. Наш движок считал передний край как `Kid.x + dx` без поправки
|
check_press/knock работают только по текущей комнате. Нужно: (а) пробой
|
||||||
→ 57 → ложный leave → пинг-понг.
|
loose сверху через границу комнаты, (б) персистентное loose-состояние соседа +
|
||||||
|
его отрисовка в полосе. Пока полоса рисует loose статически (кадр покоя).
|
||||||
|
|
||||||
Эталонный цикл SDLPoP (сн normalized по трассе): стойка x=61 → тап вправо →
|
**Почему отложен:** требует персистентного per-room modifier соседей — это
|
||||||
safe_step(d=0) → step, на первом dx(1) x=62 → bump (edge-триггер) → align 61 →
|
фаза P0 из [`../docs/gates_spikes_plan.md`](../docs/gates_spikes_plan.md), а не
|
||||||
seq47 dx(-4) → **x=57** (скрыт за кромкой) → кадры 50/51/52 (cxr 61/60/58, у
|
точечный фикс. См. «Отложено осознанно» в [`TASKS.md`](TASKS.md).
|
||||||
всех бит 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 экран НЕ
|
|
||||||
переключается. То есть у нас лишний свитч A→B.
|
|
||||||
|
|
||||||
## Тестовый контекст
|
|
||||||
|
|
||||||
START_ROOM=6. Ворота room8(0,9) закрыты, видны в col0 room6. Kid-структура
|
|
||||||
kframe: см. pop_kid.h (frame,x,y,direction,curr_col,curr_row,action,...).
|
|
||||||
Адрес _Kid и статиков — в .sprinter-cc-roomtest/roomtest.map (сдвигается от
|
|
||||||
сборки к сборке). MAME: memory `mame_mcp_bridge`/`mame_hdd_test_disk`.
|
|
||||||
Отладочные: J/L — короткий шаг влево/вправо (эмуляция Shift+стрелка).
|
|
||||||
|
|
||||||
## Инструментальный диагноз (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 — после
|
|
||||||
истечения таймера leave срабатывает.
|
|
||||||
- По коду SDLPoP raw-рассуждением раз за разом выходит, что leave-left ДОЛЖЕН
|
|
||||||
сработать и там (Char.x=57), но эталон показывает, что нет. Значит различие
|
|
||||||
не в этих кусках, а в ДИНАМИКЕ: где именно оседает Char.x у шва И как
|
|
||||||
вычисляется drawn_room.
|
|
||||||
|
|
||||||
## КОРЕНЬ (гипотеза)
|
|
||||||
|
|
||||||
Наш движок держит `drawn_room == комната_Kid` ВСЕГДА, а шов подделан:
|
|
||||||
- коллизия — самодельные снапшоты кромок соседа (g_lcol/g_rcol, 1 колонка) +
|
|
||||||
`x_bump[tcol=-1]`, а НЕ порт `curr_row_coll_room[]`/`get_all_coll_row`/
|
|
||||||
`xpos_in_drawn_room` из seg004;
|
|
||||||
- уход — мгновенный `enter_room(сосед)` при пороге char_x, а НЕ отложенный
|
|
||||||
drawn_room через `next_room`/`check_the_end`.
|
|
||||||
|
|
||||||
Из-за упрощённой коллизии Char.x у шва оседает иначе, чем в SDLPoP (у нас ровно
|
|
||||||
57 = порог), и/или drawn_room переключается там, где в оригинале нет.
|
|
||||||
|
|
||||||
## План S2/S3 (порт coll_room + отложенный drawn_room) — НЕ потребовался для
|
|
||||||
## этого бага (трасса показала: в эталоне Char.room/drawn_room вообще не
|
|
||||||
## меняются). Остаётся как задел под стражей/двух персонажей в кадре.
|
|
||||||
|
|
||||||
Правило проекта: SDLPoP — источник истины, переводить 1:1, НЕ рассуждать «как
|
|
||||||
должно быть». Нужно портировать связку целиком:
|
|
||||||
|
|
||||||
1. **Раздельные комнаты.** `Char.room` (реальная комната персонажа) ≠
|
|
||||||
`drawn_room` (отрисованная). У нас уже есть `kid_room` (S1) + рендер-смещение
|
|
||||||
`pop_kid_set_render_dx(∓140)` (порт xpos_in_drawn_room для СПРАЙТА).
|
|
||||||
|
|
||||||
2. **Коллизия по Char.room через coll_room (seg004, S2).** Портировать
|
|
||||||
`check_collisions` → `get_row_collision_data` (строки curr/below/above) →
|
|
||||||
`get_left_wall_xpos`/`get_right_wall_xpos` (по `xpos_in_drawn_room(x_bump[
|
|
||||||
col+FIRST_ONSCREEN_COLUMN])`, ±140 когда колонка принадлежит соседу) →
|
|
||||||
массивы `curr_row_coll_room[]`/`_flags[]`, `bump_col_left_of_wall`/
|
|
||||||
`bump_col_right_of_wall` → `check_bumped_look_left/right` → `bumped()`.
|
|
||||||
Тайлы берутся из РЕАЛЬНОЙ комнаты колонки (get_tile(room,col,row) для любой
|
|
||||||
комнаты), НЕ из снапшота. Именно это определяет, где Char.x оседает у шва.
|
|
||||||
|
|
||||||
3. **Отрисовка по drawn_room, персонаж со сдвигом.** Рисуем drawn_room; Kid
|
|
||||||
(и будущие стражи) — со смещением ±140, если их комната ≠ drawn_room.
|
|
||||||
|
|
||||||
4. **Отложенная смена drawn_room (seg002/seg000, S3).** Уход =
|
|
||||||
`leave_room` (seg002:0504, ПОРТ ЦЕЛИКОМ, включая up/down/side, guard'ы
|
|
||||||
climb/standup/turn, doortop-check для right) → `goto_other_room`
|
|
||||||
(seg002:0486: Char.room=сосед, x±140/y±189). `exit_room` (seg002:03C7):
|
|
||||||
`exit_room_timer` + `next_room = Char.room`. `check_the_end` (seg000:0FBD):
|
|
||||||
`if (next_room != 0 && next_room != drawn_room) drawn_room = next_room;
|
|
||||||
redraw`. drawn_room меняется ТОЛЬКО так, а не по нашему прямому enter_room.
|
|
||||||
|
|
||||||
Ключевой момент, который надо переводить БУКВАЛЬНО (а не выводить): порядок
|
|
||||||
вызовов в play_frame (seg000:863 `play_frame`: … play_kid_frame → exit_room →
|
|
||||||
check_the_end) + set_char_collision перед leave_room в exit_room.
|
|
||||||
|
|
||||||
Карта сегментов: seg002 (leave/goto/exit_room), seg004 (коллизия/coll_room/
|
|
||||||
bumped), seg006 (set_char_collision/play_seq/frame_table_kid), seg000
|
|
||||||
(play_frame/check_the_end/draw). Наши файлы: pop_map.c (коллизия/физика/leave),
|
|
||||||
roomtest.c (enter_room/drawn_room/главный цикл), pop_kid.c (кадры/рендер-сдвиг).
|
|
||||||
|
|
||||||
Реализовано на 2026-07-22: S1 (kid_room + render_dx) + pop_leave_timer.
|
|
||||||
S2/S3 отложены — баг закрыт odd-pixel поправкой (см. КОРЕНЬ выше).
|
|
||||||
Память: [[pop_seam_room_model]], [[sdlpop_odd_pixel_char_x]].
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# TODO: оптимизация отрисовки (записано 2026-07-29)
|
# Оптимизация отрисовки (записано 2026-07-29)
|
||||||
|
|
||||||
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к
|
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к
|
||||||
перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый
|
перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый
|
||||||
@@ -374,6 +86,7 @@ S2/S3 отложены — баг закрыт odd-pixel поправкой (с
|
|||||||
ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем
|
ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем
|
||||||
безусловно» корректен, но дорог.
|
безусловно» корректен, но дорог.
|
||||||
|
|
||||||
|
<a id="t-1"></a>
|
||||||
## T-1. Пики: перерисовывать по причине, а не безусловно
|
## T-1. Пики: перерисовывать по причине, а не безусловно
|
||||||
|
|
||||||
**Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для
|
**Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для
|
||||||
@@ -397,6 +110,9 @@ S2/S3 отложены — баг закрыт odd-pixel поправкой (с
|
|||||||
сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений
|
сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений
|
||||||
прямоугольников (см. память `manual_hints_over_auto_detect`).
|
прямоугольников (см. память `manual_hints_over_auto_detect`).
|
||||||
|
|
||||||
|
**Приоритет:** отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается.
|
||||||
|
|
||||||
|
<a id="t-2"></a>
|
||||||
## T-2. Idle-skip: не перерисовывать Кида, когда ничего не происходит
|
## T-2. Idle-skip: не перерисовывать Кида, когда ничего не происходит
|
||||||
|
|
||||||
**Сейчас:** `kid_heal` → `kid_draw` → `pop_fore_over_kid` идут каждый кадр,
|
**Сейчас:** `kid_heal` → `kid_draw` → `pop_fore_over_kid` идут каждый кадр,
|
||||||
@@ -411,9 +127,11 @@ S2/S3 отложены — баг закрыт odd-pixel поправкой (с
|
|||||||
неизменного кадра — иначе одна из двух страниц останется со старым
|
неизменного кадра — иначе одна из двух страниц останется со старым
|
||||||
содержимым. Условие «обе страницы уже получили это состояние».
|
содержимым. Условие «обе страницы уже получили это состояние».
|
||||||
|
|
||||||
**Связь с T-1:** после T-1 idle-skip не требует ничего дополнительного для
|
**Связь с T-1:** после T-2 пики отпадают сами — раз Кида не перерисовываем,
|
||||||
пик: раз Кида не перерисовываем — heal'а нет — стирать пики нечем — редрой
|
heal'а нет, стирать пики нечем, редрой не нужен.
|
||||||
не нужен, он отпадает сам.
|
|
||||||
|
**Связь с KBD-1:** это ещё и минус DI-окна в самых спокойных кадрах — ровно
|
||||||
|
там, где тапают Shift+стрелку (см. остаток KBD-1 в [`TASKS.md`](TASKS.md)).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -425,24 +143,12 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
|
|||||||
исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа
|
исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа
|
||||||
единицы (`||` `||||` = 24). Убирается закомментированием одной строки.
|
единицы (`||` `||||` = 24). Убирается закомментированием одной строки.
|
||||||
|
|
||||||
|
Комнаты **13, 18, 24 недостижимы** в обычной игре — это свойство данных
|
||||||
|
уровня, разбор в [`bug_closed.md`](bug_closed.md) («НЕ БАГИ»). Приоритет
|
||||||
|
багов в них низкий.
|
||||||
|
|
||||||
**Связность уровня 1 (проверено по `res2001.bin`, links @1952).** Обход графа
|
Таблица заполнена на 5 строк из 24 — доделать в рамках L1-TRIAGE
|
||||||
от стартовой комнаты 1 показывает: комнаты **13, 18, 24 НЕДОСТИЖИМЫ** в
|
([`TASKS.md`](TASKS.md)).
|
||||||
обычной игре. У всех трёх ссылки наружу есть, а на них не ссылается никто
|
|
||||||
(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 возможен «мусор в шве» — наш рендер кромки
|
|
||||||
читает крайнюю колонку соседа ПО ССЫЛКЕ, а сосед этих комнат соседом себя не
|
|
||||||
считает. Это свойство данных, а не наш баг. Приоритет багов в этих трёх
|
|
||||||
комнатах — низкий (в игре их не видно).
|
|
||||||
|
|
||||||
| комната | статус | что не так |
|
| комната | статус | что не так |
|
||||||
|---------|--------|------------|
|
|---------|--------|------------|
|
||||||
@@ -457,7 +163,7 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
|
|||||||
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
|
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
|
||||||
| 10 | | |
|
| 10 | | |
|
||||||
| 11 | | |
|
| 11 | | |
|
||||||
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» |
|
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» в `bug_closed.md` |
|
||||||
| 13 | недостижима в игре | |
|
| 13 | недостижима в игре | |
|
||||||
| 14 | | |
|
| 14 | | |
|
||||||
| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` |
|
| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` |
|
||||||
@@ -470,3 +176,17 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
|
|||||||
| 22 | | |
|
| 22 | | |
|
||||||
| 23 | | |
|
| 23 | | |
|
||||||
| 24 | недостижима в игре | |
|
| 24 | недостижима в игре | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Заметки (отладка)
|
||||||
|
|
||||||
|
- Тестовые клавиши осторожного шага: **J** = шаг влево, **L** = шаг вправо
|
||||||
|
(эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в
|
||||||
|
сторону = разворот (как в оригинале safe_step), движение со второго.
|
||||||
|
- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle),
|
||||||
|
**S** — выдать меч.
|
||||||
|
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит
|
||||||
|
Kid в `START_ROOM`.
|
||||||
|
- `START_ROOM=1 / COL 3 / ROW 0` — жёстко зашитый тестовый старт; перевести
|
||||||
|
на `pop_level_start_*` (задача L1-START в [`TASKS.md`](TASKS.md)).
|
||||||
|
|||||||
Reference in New Issue
Block a user