Доски приведены в соответствие с кодом; bug_list/bug_closed → BUGS_OPEN/BUGS_CLOSED
Доска отставала от кода на три задачи — планировать по ней было нельзя. Сверка проведена грепом по исходникам, а не по записям: - L4-MIRROR ЗАКРЫТА: шаги 1-5 сделаны и проверены пользователем в MAME (зеркало в атласе, постановка тайла, отражение, прыжок сквозь зеркало с рождением тени, левый клип тени). Протокол с разбором решений — в архиве; - L3-CHOMP и L3-SKEL закрыты ещё 2026-08-08/07 (коммитыdc0bd47,4d4323f,db4106a,1461ed5), на доске значились как предстоящие; - тайлсет palace (шаг 2 levels_plan) в коде есть целиком — pop_bg_load(type), pal_*.atl, дворцовый wall_pattern, решётки 25-29 и в tile_table, и в коллизии (tile_is_floor совпадает с seg006:0628); - в GUARD-PHYS остаток пересобран по факту: check_chomped_guard сделан, скелет в check_guard_fallout сделан, ветки ТЕНИ нет — она уехала в L5-SHADOW. Приёмки: по решению пользователя уровни 1-4 приняты SMOKE-тестами, полные обходы всех комнат делаются по готовности ВСЕХ уровней — L3-PASS/L4-PASS как отдельные задачи отменены, вместо них политика приёмок в архиве. Новая цель — уровень 5. Инвентарь res2005.bin: НИ ОДНОГО нового тайла, всё портировано на уровнях 1-4. Единственная новая механика — спецсобытие «тень крадёт зелье» (комната 24): заведена задача L5-SHADOW с портом по SDLPoP (check_shadow / do_init_shad / do_auto_moves + shad_drink_move / autocontrol_shadow_level5 + ветка тени в check_guard_fallout), включая готовые константы и то, что у нас уже есть под это. Заведён MIRROR-FG-STALE (низкий): place_mirror пишет тайл в данные уровня, но не в снимок room_fg, по которому работает коллизия — если зеркало поставлено, пока игрок В комнате 4, оно невидимо для коллизии (тень не родится). В обычном прохождении недостижимо: дверь выхода в другой комнате. Записан точный сценарий воспроизведения читом ROOMNAV и фикс на несколько строк. Правило «в _OPEN только незакрытое» теперь выполняется буквально: - bug_list.md → BUGS_OPEN.md, bug_closed.md → BUGS_CLOSED.md (ссылки обновлены во всех документах и в комментарии pop_trob.c); - из TASKS_OPEN убраны блоки закрытых задач (L3-CHOMP, L3-SKEL, L3-PASS, L4-MIRROR, DRAW-CHAR), справка по связности комнат уехала в архив; - из BUGS_OPEN убраны 8 строк таблицы закрытых багов, закрытый T-2 (уехал в BUGS_CLOSED) и раздел «уровень 3 — неначатые задачи» (обе записи закрыты); сводная таблица пересобрана по реально открытым записям. Все внутренние ссылки проверены скриптом: битых якорей 0. make size-check OK (65 программ), tests-host 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,30 +1,33 @@
|
||||
# roomtest — доска ОТКРЫТЫХ задач (обновлено 2026-08-08)
|
||||
# roomtest — доска ОТКРЫТЫХ задач (обновлено 2026-08-11)
|
||||
|
||||
Что берём в работу сейчас и в каком порядке. Каждая запись: что сделать,
|
||||
почему именно сейчас, чем подтверждать результат.
|
||||
|
||||
- закрытые задачи с протоколами и замерами — [`TASKS_CLOSED.md`](TASKS_CLOSED.md);
|
||||
- открытые баги — [`bug_list.md`](bug_list.md), закрытые с разбором корней —
|
||||
[`bug_closed.md`](bug_closed.md);
|
||||
- открытые баги — [`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней —
|
||||
[`BUGS_CLOSED.md`](BUGS_CLOSED.md);
|
||||
- планы фаз — `../docs/PORT_PLAN.md`, `../docs/layout_plan_v2.md`,
|
||||
`../docs/levels_plan.md`.
|
||||
|
||||
**Состояние на 2026-08-07:** пользователь прогнал ВСЕ комнаты уровней 1 и 2 —
|
||||
крупных багов нет. Приёмки [L1-PASS](TASKS_CLOSED.md#l1-pass) и
|
||||
[L2-PASS](TASKS_CLOSED.md#l2-pass) закрыты; с прогона открыты три записи по
|
||||
уровню 2, из них цвет стража и брызги уже закрыты (см.
|
||||
[`bug_closed.md`](bug_closed.md)); открытым остался чит `+`/`−` в бою.
|
||||
Уровень 3: **скелет сделан и проверен в MAME 2026-08-07** (бой, падение в
|
||||
пропасть, окклюзия), чомперов ещё нет.
|
||||
**Состояние на 2026-08-11 (сверено с кодом, не только с доской):**
|
||||
|
||||
**Разгрузка банка 2 сделана 2026-08-08** ([MEM-BANK2](TASKS_CLOSED.md#mem-bank2)): 90.4 % →
|
||||
**35.8 %, свободно 10 512 Б** — холодная половина уехала в банк 7
|
||||
(`pop_room.c`), чомперам места с запасом.
|
||||
|
||||
**DRAW-CHAR сделана и ПРОВЕРЕНА 2026-08-08** (протокол и замеры —
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#draw-char)): отрисовка теперь одна на
|
||||
всех `Char`. Банку 2 она дала всего +265 Б — место под чомперов дала уже
|
||||
[MEM-BANK2](TASKS_CLOSED.md#mem-bank2).
|
||||
- **уровни 1-4 приняты smoke-тестами** (пользователь). Полные обходы всех
|
||||
комнат делаются по готовности ВСЕХ уровней — политика приёмок в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#pass-policy); отдельных задач
|
||||
`L3-PASS`/`L4-PASS` больше нет. Уже сделанные полные обходы уровней 1 и 2
|
||||
остаются регресс-базой;
|
||||
- **[L4-MIRROR](TASKS_CLOSED.md#l4-mirror) закрыта**: зеркало, отражение,
|
||||
прыжок сквозь него и рождение тени проверены в MAME. Хвосты — вид тени
|
||||
(отложен, [`../docs/shadow_render.md`](../docs/shadow_render.md)) и
|
||||
[MIRROR-FG-STALE](BUGS_OPEN.md#mirror-fg-stale);
|
||||
- **[L3-CHOMP](TASKS_CLOSED.md#l3-chomp) и [L3-SKEL](TASKS_CLOSED.md#l3-skel)
|
||||
закрыты** (2026-08-08 / 2026-08-07);
|
||||
- **тайлсет palace сделан** — `pop_bg_load(type)`, `pal_*.atl`, дворцовая
|
||||
кладка `wall_pattern`, решётчатые тайлы 25-29 и в `tile_table`, и в
|
||||
коллизии (`tile_is_floor` совпадает с seg006:0628). То есть шаг 2
|
||||
`levels_plan.md` закрыт;
|
||||
- **libbgi:** блочные AND/OR/XOR/NOT акселератора (2026-08-11, `tests/accop`)
|
||||
— задел под вид тени и под любые эффекты «поверх того, что уже нарисовано».
|
||||
|
||||
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
||||
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
|
||||
@@ -32,41 +35,40 @@
|
||||
|
||||
---
|
||||
|
||||
## ТЕКУЩАЯ ЦЕЛЬ: уровни 1–3 (подземелье) отлажены целиком
|
||||
## ТЕКУЩАЯ ЦЕЛЬ: уровень 5
|
||||
|
||||
Решение 2026-08-04: **palace (уровни 4+) откладываем**, доводим до
|
||||
играбельности три dungeon-уровня. Основание — они не требуют ни одного
|
||||
нового ассета фона: инвентарь тайлов, снятый с `res200N.bin`, показывает,
|
||||
что новое появляется только так —
|
||||
Уровни 1-4 играются (smoke). Дальше идём по порядку уровней; уровень 5 —
|
||||
следующий.
|
||||
|
||||
**Хорошая новость по ассетам: уровень 5 не приносит НИ ОДНОГО нового тайла.**
|
||||
Инвентарь, снятый перебором `res2005.bin` (fg & 0x1F):
|
||||
|
||||
```
|
||||
ур. 1 empty, floor, spike, pillar, gate, closer, potion, loose, debris,
|
||||
opener, level_door L/R, torch, wall, skeleton, sword ← всё есть
|
||||
ур. 2 bigpillar_bottom(8), bigpillar_top(9), doortop(12) ← есть (2026-08-04)
|
||||
ур. 3 chomper(18) ← НЕТ механики
|
||||
ур. 4 lattice_pillar(25)…lattice_right(29) + тайлсет palace ← отложено
|
||||
ур. 5: empty, floor, spike, pillar, gate, closer, doortop_with_floor(7),
|
||||
bigpillar_bottom(8), bigpillar_top(9), potion, loose, doortop(12),
|
||||
debris, opener, level_door L/R, chomper(18), torch, wall,
|
||||
lattice_pillar(25)…lattice_right(29)
|
||||
```
|
||||
|
||||
— всё это уже встречалось на уровнях 1-4 и портировано. Единственное новое
|
||||
на уровне 5 — **спецсобытие «тень крадёт зелье»**.
|
||||
|
||||
| # | Задача | Что | Блокирует |
|
||||
|---|--------|-----|-----------|
|
||||
| 1 | [GUARD-PHYS](#guard-phys) | физика стража = физика Кида (одна над `Char`) | **ядро сделано 2026-08-07**; открыт живой сценарий в MAME |
|
||||
| — | [DRAW-CHAR](TASKS_CLOSED.md#draw-char) | отрисовка одна на всех `Char` — **закрыта и проверена 2026-08-08** | — |
|
||||
| — | [MEM-BANK2](TASKS_CLOSED.md#mem-bank2) | разгрузка банка 2 — **закрыта 2026-08-08**: 90.4 % → 35.8 %, свободно 10 512 Б | — |
|
||||
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
||||
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
||||
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||
| 4 | [L4-MIRROR](#l4-mirror) | **АКТИВНА с 2026-08-10 по решению пользователя**: зеркало уровня 4 + тень | прохождение ур. 4 |
|
||||
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3** (игра быстрее на треть). Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
|
||||
| 1 | [L5-SHADOW](#l5-shadow) | **СЛЕДУЮЩАЯ**: тень уровня 5 (крадёт зелье в комнате 24) + движок автодвижений | прохождение ур. 5 |
|
||||
| 2 | [GUARD-PHYS](#guard-phys) | физика стража = физика Кида — ядро сделано, остаток: ветка ТЕНИ в `check_guard_fallout` и живая проверка кнопки под стражем | ур. 5+ (тень) |
|
||||
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3**. Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
|
||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
| — | [MEM](#mem-next) | следующий шаг разгрузки W1/W2 | берётся по факту нехватки места |
|
||||
|
||||
Сделанное по этой цели — в [`TASKS_CLOSED.md`](TASKS_CLOSED.md): L2 (машинерия
|
||||
уровней), L2-PASS, L1-PASS, [L3-CHKP](TASKS_CLOSED.md#l3-chkp) (чекпойнт
|
||||
уровня 3), MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS.
|
||||
Сделанное — в [`TASKS_CLOSED.md`](TASKS_CLOSED.md): L4-MIRROR, L3-CHOMP,
|
||||
L3-SKEL, L3-CHKP, L2 (машинерия уровней), L2-PASS, L1-PASS, DRAW-CHAR,
|
||||
MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS.
|
||||
|
||||
---
|
||||
|
||||
## Ждёт ФИНАЛЬНОЙ приёмки уровней 1–3
|
||||
## Ждёт ФИНАЛЬНОЙ приёмки (полные обходы по готовности всех уровней)
|
||||
|
||||
Сюда попадает то, что уже работает в проверочном прогоне, но должно быть
|
||||
подтверждено на сквозных прогонах уровней — потому что задевает механику
|
||||
@@ -87,6 +89,63 @@
|
||||
|
||||
## P0 — делаем сейчас
|
||||
|
||||
### <a id="l5-shadow"></a>L5-SHADOW. Тень уровня 5: крадёт зелье — **СЛЕДУЮЩАЯ**
|
||||
|
||||
Единственная новая механика уровня 5 (тайлов новых нет вовсе, см. цель выше).
|
||||
Тень появляется в комнате 24, дожидается, пока откроется дверь, идёт к зелью,
|
||||
**выпивает его** и уходит за левый край. Боя нет.
|
||||
|
||||
**Как это в оригинале** (всё сверено по коду, `custom->*` — это дефолты 1.0):
|
||||
|
||||
| что | где | суть |
|
||||
|---|---|---|
|
||||
| появление | `check_shadow`, seg002:0064 | при СМЕНЕ КОМНАТЫ: если `current_level == 5` и `drawn_room == 24`, и тайл (кол 3, ряд 0) всё ещё `tiles_10_potion` — породить тень |
|
||||
| порождение | `do_init_shad`, seg002:0000 | `memcpy(&Char, init_shad_5, 7)` + `seqtbl_offset_char(2 /*stand*/)`, `charid = charid_1_shadow`, `demo_time = 0`, `guard_skill = 3`, `guardhp_* = 4`, `saveshad()` |
|
||||
| данные | `init_shad_5` | `{0x0F, 0x37, 0x37, 0, 0xFF, 0, 0}` = frame 15, x 55, y 55, direction 0, curr_col −1, curr_row 0, action 0 |
|
||||
| поведение | `autocontrol_shadow_level5`, seg002:1157 | в комнате 24: пока `demo_time == 0` — ждать, пока дверь (кол 1, ряд 0) не откроется (`modif >= 80`), затем `demo_index = 0`; дальше каждый кадр `do_auto_moves(shad_drink_move)`; при `Char.x < 15` — `clear_char()` |
|
||||
| движения | `do_auto_moves`, seg002:1089 | крошечный интерпретатор: `demo_time++`, по таблице `{time, move}` выбирается запись, `move` = 0 nothing / 1 forward / 2 backward / 3 up / 4 down / 5 up+forward / 6 shift / 7 move_7; −1 = ничего, −2 = конец |
|
||||
| таблица | `shad_drink_move` (data.h:866) | `{0x00,0} {0x01,1} {0x0E,0} {0x12,6} {0x1D,7} {0x2D,2} {0x31,1} {0xFF,−2}` — 8 записей по 2 байта |
|
||||
|
||||
**Что из этого у нас уже есть:**
|
||||
|
||||
- **механизм «спецсобытие порождает персонажа в слоте соперника»** —
|
||||
`pop_check_skel` (`guards.c`, зовётся из `roomtest.c` в тике); тень
|
||||
уровня 5 садится на тот же шов, только условие другое;
|
||||
- **тень как `charid_1_shadow`** — заведена под уровень 4
|
||||
([L4-MIRROR](TASKS_CLOSED.md#l4-mirror)): своя ветка ИИ
|
||||
(`autocontrol_shadow` + `autocontrol_shadow_level4`), выбор таблицы кадров
|
||||
Кида (`pop_frame_tbl_is_guard`), отрисовка спрайтами Кида;
|
||||
- **питьё зелья** — `SEQ_78_DRINK` и `get_item` в `pop_ctrl.c` (Кид уже
|
||||
умеет); тень «нажимает» те же кнопки через автодвижения;
|
||||
- **зелья как trob** — фаза пузырька, тип в старших битах (`pop_trob.c`).
|
||||
|
||||
**Что писать:**
|
||||
|
||||
1. `do_auto_moves` + таблица `shad_drink_move` + `demo_time`/`demo_index` —
|
||||
интерпретатор ~30 строк, кладётся рядом с `autocontrol_shadow` в
|
||||
`guards.c` (банк 1). «Движения» — это те же переменные управления, что
|
||||
заполняет `read_user_control` (`pop_ctrl.c`), так что `move_*` сводятся к
|
||||
присваиваниям.
|
||||
2. `do_init_shad(init_shad_5, seq stand)` — общий порождатель тени; пригодится
|
||||
и на уровнях 6 и 12 (`init_shad_6`, `init_shad_12` — те же 7 байт).
|
||||
3. Ветка `check_shadow` для уровня 5 — по образцу `pop_check_skel`, вызов из
|
||||
того же места тика.
|
||||
4. `autocontrol_shadow_level5`.
|
||||
5. **Ветка ТЕНИ в `check_guard_fallout`** (seg002:0241): тень падает, только
|
||||
если она в свободном полёте (`action == 4`), и тогда
|
||||
`loadshad(); clear_char(); saveshad()`. Сейчас в `pop_guard_fallout`
|
||||
(`pop_guard.c`) есть ветки стража и скелета, а тени нет — комментарий там
|
||||
обещает её «вместе с L3-SKEL», но она относится именно к тени.
|
||||
|
||||
**Чем подтверждать:** smoke уровня 5 — дойти до комнаты 24, увидеть, как
|
||||
тень выходит после открытия двери, выпивает зелье (тайл зелья исчезает) и
|
||||
уходит влево. Сверять последовательность движений с живым SDLPoP на том же
|
||||
месте — таблица `shad_drink_move` короткая, расхождение будет видно сразу.
|
||||
|
||||
**Оговорка по виду:** тень пока рисуется обычной копией спрайтов Кида, то
|
||||
есть выглядит вторым Кидом — это [отложенный](../docs/shadow_render.md)
|
||||
вопрос, к механике уровня 5 отношения не имеет.
|
||||
|
||||
### <a id="guard-phys"></a>GUARD-PHYS. Страж живёт по тем же правилам, что Кид — **ЯДРО СДЕЛАНО 2026-08-07**
|
||||
|
||||
> **Что уже работает** (решение пользователя: переносим физику на `Char`,
|
||||
@@ -126,13 +185,17 @@
|
||||
> 3 → 2, X перенесён через шов), ровно как в SDLPoP. Условия отбора
|
||||
> покрыты тестами `t_char` (7 сценариев: пороги 91/165, «не бой», мёртвый,
|
||||
> вверх/вниз, занятая соседняя комната). **Сцена вскрыла отдельный баг —
|
||||
> [BUG-SWORD-GHOST-1](bug_list.md#bug-sword-ghost-1): при переходе в бою Кид
|
||||
> [BUG-SWORD-GHOST-1](BUGS_CLOSED.md#bug-sword-ghost-1): при переходе в бою Кид
|
||||
> прячет меч и дальше дерётся пустой рукой.**
|
||||
>
|
||||
> **Осталось (потому и запись открыта):**
|
||||
> 1. `check_chomped_guard` — вместе с [L3-CHOMP](#l3-chomp);
|
||||
> 2. ветки `check_guard_fallout` для тени и скелета (скелет возрождается в
|
||||
> комнате 3) — вместе с [L3-SKEL](#l3-skel);
|
||||
> **Осталось (потому и запись открыта) — ревизия 2026-08-11 по коду:**
|
||||
> 1. ~~`check_chomped_guard`~~ — **сделан** вместе с
|
||||
> [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) (`pop_map.c`);
|
||||
> 2. ветки `check_guard_fallout`: **скелет сделан** (возрождается в комнате 3,
|
||||
> `pop_guard_fallout` в `pop_guard.c`), **ветки ТЕНИ нет** — падает только
|
||||
> в свободном полёте, `loadshad/clear_char/saveshad`; идёт в
|
||||
> [L5-SHADOW](#l5-shadow) п. 5 (комментарий в коде обещает её «вместе с
|
||||
> L3-SKEL» — устарел, это про тень);
|
||||
> 3. страж, нажимающий напольную кнопку, вживую не проверялся (код —
|
||||
> общий `check_press`).
|
||||
|
||||
@@ -187,90 +250,6 @@
|
||||
скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ
|
||||
живой страж — переход не происходит.
|
||||
|
||||
### <a id="l3-chomp"></a>L3-CHOMP. Чомперы — механика уровня 3
|
||||
|
||||
**Где:** 5 штук, комнаты 5(тайл 4), 16(23,24,25), 22(26). Дальше они почти
|
||||
на каждом уровне, так что это вложение не только в ур. 3.
|
||||
|
||||
**Что портировать:** `animate_chomper` (seg007) — состояние в `room_modif`,
|
||||
как у пик/ворот, значит шаблон уже отработан (`gates_spikes_plan.md`);
|
||||
коллизия и смерть Кида в сомкнутых челюстях (`seg004`/`seg006`); отрисовка
|
||||
кадра по модификатору (`draw_tile_anim`, ветка `tiles_18_chomper`).
|
||||
|
||||
**Место в банке 2 расчищено** ([MEM-BANK2](TASKS_CLOSED.md#mem-bank2), 2026-08-08):
|
||||
35.8 %, **свободно 10 512 Б** (было 1074 Б) — холодная половина слоя фона
|
||||
уехала в банк 7 (`pop_room.c`). Считать место ДО кодинга всё равно
|
||||
обязательно (`levels_plan.md` §5.1); чомпер — это анимация тайла (холодная
|
||||
перерисовка), поэтому его отрисовка ложится в `pop_room.c`, а состояние —
|
||||
в `room_modif`, как у пик и ворот.
|
||||
|
||||
**Ассеты УЖЕ упакованы (2026-08-07):** `pop_pack_bg.py` кладёт в атлас весь
|
||||
набор кадров чомпера явно (`CHOMPER_BOT_IDS` 101-105, `CHOMPER_TOP_IDS`
|
||||
111-113, `CHOMPER_FORE_IDS` 106-110 + кровь 114-123 mono-силуэтом цветом 12).
|
||||
Раньше в атласе не было НИ ОДНОГО его кадра: `render_room.py` пропускает
|
||||
анимированные тайлы, а `tile_table[0x12].base_id = 0` — отсюда и «чомпера не
|
||||
видно вовсе». Рост: `pop_env3.atl` 5200 -> 12937 Б, `pop_fore.atl` 7763 ->
|
||||
12359 Б, число EMM-страниц НЕ изменилось (7).
|
||||
|
||||
**Чем подтверждать:** комната 22 уровня 3 — чомпер (2,6) анимируется и
|
||||
рисуется (сейчас его не видно вовсе, см. раздел «Уровень 3» в
|
||||
[`bug_list.md`](bug_list.md)); смерть Кида в сомкнутых челюстях.
|
||||
|
||||
### <a id="l3-skel"></a>L3-SKEL. Скелет — единственный противник уровня 3
|
||||
|
||||
**Важно:** в данных уровня 3 **стражей нет вообще** (`guards_tile` пуст во
|
||||
всех 24 комнатах). Единственный враг — скелет, и он не «страж из данных»,
|
||||
а **спецсобытие** `check_skel` (seg002:1044):
|
||||
|
||||
> в комнате 1, когда `Kid.curr_col` == 2 или 3 и дверь уровня открыта,
|
||||
> тайл `tiles_21_skeleton` (комната 1, тайлпос 15) стирается в пол, а на
|
||||
> его месте поднимается персонаж: `charid_4_skeleton`, меч сразу вынут,
|
||||
> `seq_88_skel_wake_up`, skill 2, HP 3.
|
||||
|
||||
Ещё два тайла скелета (комнаты 17 и 19) — декорация, они не оживают.
|
||||
|
||||
> **Сделано 2026-08-07 (ждёт живой проверки на уровне 3):**
|
||||
> - **атлас скелета** — `pop_pack_guard.py SKEL` собирает `poc/res/skel/g0..g3.atl`
|
||||
> (28 кадров, 30 КБ, 4 EMM-страницы); упаковщик получил параметр набора
|
||||
> (`GUARD`/`SKEL`). Палитра у скелета СВОЯ (`SKEL/res750.pal`), а не из
|
||||
> `res10.bin`: на уровне 3 `tbl_guard_type != 0`, значит
|
||||
> `curr_guard_color = 0` и оригинал палитру не подменяет вовсе;
|
||||
> - `pop_guard_load` выбирает набор по типу уровня и заливает палитру
|
||||
> скелета; Makefile кладёт атласы в `SKEL\` на диск;
|
||||
> - **`check_skel`** (seg002:1042) — порт в `guards.c`: уровень 3, комната 1,
|
||||
> дверь уровня открыта, `Kid.curr_col` 2 или 3, тайл 21 на (5,1) → тайл
|
||||
> стирается в пол (обе страницы), персонаж встаёт с `seq_88_skel_wake_up`,
|
||||
> мечом наголо, skill 2, HP 3. Зовётся из главного цикла ДО логики
|
||||
> персонажей, как в `play_frame`;
|
||||
> - **`leveldoor_open`** — флаг появился (`pop_state.c`), взводит анимация
|
||||
> двери при `modif >= 43` (seg007:456);
|
||||
> - **`enter_guard`** — ветка `charid_4_skeleton`: встаёт сразу активным
|
||||
> (меч вынут), а не в стойке покоя;
|
||||
> - **возрождение** — `check_guard_fallout`: упавший скелет, под комнатой
|
||||
> которого лежит комната 3, появляется там снова (x 133, ряд 1);
|
||||
> - **ИИ** — `autocontrol_skeleton` (seg002:685): меч у скелета вынут всегда.
|
||||
>
|
||||
> Регресс: `tests-host` зелёные, `t_char` вырос до 65 проверок (добавлены два
|
||||
> сценария возрождения). Цена: `_CODE` +80 Б, банк 1 (`guards`) 2342 → 2519,
|
||||
> банк 4 (`gdraw`) 3875 → 4269.
|
||||
>
|
||||
> **Не проверено вживую:** сцена требует пройти уровень 3 до кнопки, которая
|
||||
> открывает выход — без открытой двери скелет по условию не встаёт.
|
||||
|
||||
**Что нужно:**
|
||||
- `charid_4_skeleton` в `enter_guard`/`pop_guard_enter` (seg002:196): при
|
||||
`tbl_guard_type[level] == 2` персонаж поднимается **с вынутым мечом** и
|
||||
последовательностью `seq_63_guard_active_after_fall`, а не
|
||||
`seq_77_guard_stand_inactive`;
|
||||
- скелет **бессмертен** — чит `K` его уже не берёт (`pop_guard_kill`
|
||||
проверяет `CHARID_4_SKELETON`), но и боёвка должна возвращать его к
|
||||
жизни (seg002:252);
|
||||
- **новый атлас**: `../SDLPoP/data/SKEL/` (29 файлов) — `pop_pack_guard.py`
|
||||
сейчас прибит к `GUARD/`, нужен параметр набора (`tbl_guard_dat` =
|
||||
GUARD/FAT/SKEL/VIZIER/SHADOW). Здесь же удобно закрыть
|
||||
[BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1): палитра стража
|
||||
подменяется по `guards_color`, и оба изменения живут в одном упаковщике.
|
||||
|
||||
### <a id="l3-color"></a>L3-COLOR. Палитра КЛАДКИ уровня 3 (в оригинале он зелёный)
|
||||
|
||||
**Наблюдение (пользователь, 2026-08-07, со сравнением карт VGA).** В
|
||||
@@ -315,7 +294,7 @@ offset 22790, **240 байт** = 5 палитр × 16 цветов × 3 байт
|
||||
**Грабли, уже пойманные на цвете стражей:** `gfx_pal_load` отдаёт указатель
|
||||
в BIOS (`$A4` через `rst #0x08`), а BIOS читает только `#4000-#BFFF` —
|
||||
таблицу нельзя передавать прямо из банка (0xC000+), надо копировать в
|
||||
W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
||||
W1/W2 (см. [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)).
|
||||
|
||||
### <a id="draw-cost"></a>DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация
|
||||
|
||||
@@ -660,165 +639,6 @@ tp/10 у факелов таблицей, пустой слот соперник
|
||||
(клинок/брызги), а не по всему окну; кэш «в этом тайле fore-слоя нет
|
||||
вовсе»; считать окно клипа в тайловых координатах один раз.
|
||||
|
||||
### <a id="draw-char"></a>DRAW-CHAR — **ЗАКРЫТА И ПРОВЕРЕНА 2026-08-08**
|
||||
|
||||
Отрисовка сведена к одному набору функций над `Char` (`pop_cdraw.c`), проход
|
||||
окклюзии — один на всех (`pop_fore_over_char`). Разбор, таблица «было →
|
||||
стало», пять починенных расхождений и замеры — в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#draw-char). Оттуда же список того, что
|
||||
надо потрогать вживую при приёмке уровней.
|
||||
|
||||
### <a id="l3-pass"></a>L3-PASS. Приёмка уровня 3 — обход всех комнат
|
||||
|
||||
Как L1-PASS/L2-PASS: сквозной проход руками плюс обход комнат читом ROOMNAV.
|
||||
Осмысленна ТОЛЬКО после L3-CHOMP и L3-SKEL — без них уровень заведомо
|
||||
неполон, и половина наблюдений будет «механики нет».
|
||||
|
||||
Что уже снято и пригодится (справка ниже): комнаты **23 и 24 недостижимы** и
|
||||
полностью пусты — баги в них не в приоритете; **чекпойнт** уровня 3 сделан
|
||||
([L3-CHKP](TASKS_CLOSED.md#l3-chkp)), его тоже надо потрогать вживую: уйти
|
||||
влево из комнаты 7, умереть, проверить респавн в комнате 2 и снятую
|
||||
loose-плиту (7, кол 4, ряд 0).
|
||||
|
||||
### <a id="rooms-graph"></a>Справка: связность комнат уровней 1–3 (снято 2026-08-05)
|
||||
|
||||
Обход графа `roomlinks` (@1952, по 4 байта на комнату: L, R, U, D; 0 = нет
|
||||
соседа) от стартовой комнаты — тем же методом, которым на уровне 1 нашлись
|
||||
13/18/24 (см. «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md)). Скрипт разовый,
|
||||
в репозиторий не клался: чтение трёх массивов, BFS и проверка симметрии.
|
||||
|
||||
| уровень | старт | недостижимы | признак |
|
||||
|---------|-------|-------------|---------|
|
||||
| 1 | к.1 (0,0) | **13, 18, 24** | ссылки наружу есть, обратных нет |
|
||||
| 2 | к.5 (1,3) | **нет** | граф полностью симметричен, все 24 достижимы |
|
||||
| 3 | к.9 (2,4) | **23, 24** | то же, что на 1: односторонние ссылки, обе комнаты **полностью пустые** |
|
||||
|
||||
```
|
||||
ур.3: 23 L→4, у 4 R=22 | 24 L→2, у 2 R=7
|
||||
23 R→22, у 22 L=4 | 24 R→7, у 7 L=2
|
||||
| 24 U→16, у 16 D=0
|
||||
на 23 ссылается только 24, на 24 — только 23; тайлы обеих = все empty
|
||||
```
|
||||
|
||||
То есть на уровне 3 это даже более чистый случай, чем на уровне 1: там в
|
||||
брошенных комнатах была геометрия, здесь — пустота. Практический вывод тот
|
||||
же: **в 23/24 возможен «мусор в шве»** (наш рендер кромки читает крайнюю
|
||||
колонку соседа ПО ССЫЛКЕ, а сосед соседом себя не считает), приоритет багов
|
||||
там низкий, в игре их не видно.
|
||||
|
||||
**Уровень 2 — недостижимых нет, но есть три КОЛОДЦА без выхода** (единственная
|
||||
связь — вверх, откуда Кид падает):
|
||||
|
||||
```
|
||||
к.10 U→4 пики(2,2) + пол — падение из комнаты 4
|
||||
к.14 U→21 шахта 2 тайла шириной, дно = обломки
|
||||
к.17 U→15 то же
|
||||
```
|
||||
|
||||
Это не баги данных: в 14/17 попадают только падением насмерть, а из 10
|
||||
(если выжил) выхода нет вовсе — так в оригинале. При приёмке не считать
|
||||
«застрял» багом.
|
||||
|
||||
---
|
||||
|
||||
### <a id="l4-mirror"></a>L4-MIRROR. Зеркало уровня 4 и тень — **АКТИВНА с 2026-08-10**
|
||||
|
||||
Приоритет переставлен пользователем: palace-уровни были отложены решением
|
||||
2026-08-04, но уровень 4 уже гоняется в MAME, и зеркало — единственное, что
|
||||
мешает пройти его сюжетно.
|
||||
|
||||
**Как это работает в оригинале** (SDLPoP, всё сверено по коду):
|
||||
|
||||
| что | где | суть |
|
||||
|---|---|---|
|
||||
| постановка тайла | `animate_leveldoor`, seg007:0457 | дверь выхода ДОРИСОВАЛА открытие (`modif >= 43`, `leveldoor_open` 0/2 -> 1) -> в комнату 4, колонку 4, ряд 0 пишется `tiles_13_mirror` |
|
||||
| отражение | `check_mirror`, seg003:0798 | пока Кид на тайле зеркала — каждый кадр рисуется зеркальная копия: `Char.x = (xpos<<1) - Char.x`, `direction` инвертируется, копия идёт в objtable типом 4 с клипом `left = (curr_col<<5)+9`, `top = y_clip[row+1]` |
|
||||
| прыжок сквозь | seg004:0240 | засчитывается ТОЛЬКО `run-jump` СПРАВА НАЛЕВО (`frame 39..43`, `direction < 0`) -> `modif = 0x56` (разбитое), `jumped_through_mirror = -1` |
|
||||
| рождение тени | `jump_through_mirror`, seg003:080A | Кид копируется в слот Guard как `charid_1_shadow`; `guardhp = hitp_max`, у Кида `hitp_curr = 1` |
|
||||
| поведение тени | `autocontrol_shadow_level4`, seg002:1131 | в зеркальной комнате при `x < 80` исчезает (`clear_char`), иначе бежит вперёд. Боя нет |
|
||||
| клип тени | seg008:1699 | `obj_clip_left = 137 + (mirror_column-4)*32` — тень видна только СПРАВА от зеркала |
|
||||
| коллизия | `wall_type`, seg006:1632 | зеркало = 2 «стена слева» — **уже портировано** (`pop_map.c`), как и спецкейс `can_climb_up` |
|
||||
|
||||
**Шаги.**
|
||||
|
||||
1. **АТЛАС — СДЕЛАНО 2026-08-10.** `tile_table[0x0D]` = база 75, фронт 77
|
||||
(наша таблица совпадает с SDLPoP такт в такт). Тайла 13 **нет ни в одном
|
||||
уровне статически** — проверено перебором всех 15 `res200N.bin`, ноль
|
||||
попаданий; санити-проверка разбора: ур.1 без чомпера, ур.3 с 18, ур.4 с
|
||||
паласными 25..29. Значит `render_room` эти id не увидит и на месте
|
||||
зеркала был бы чёрный провал (memory `pop_atlas_dynamic_ids`). Добавлены
|
||||
`MIRROR_ENV_IDS = {75, 77}` в `pop_pack_bg.py`, 77 — ещё и в
|
||||
`FORE_ENV_IDS`. Оба набора переупакованы: fore 17 -> 18 спрайтов, все
|
||||
страницы EMM в пределах 16 КБ.
|
||||
2. **ПОСТАНОВКА ТАЙЛА — СДЕЛАНО И ПРОВЕРЕНО ПОЛЬЗОВАТЕЛЕМ 2026-08-10.**
|
||||
`place_mirror()` в `pop_trob.c`, вызывается из `animate_leveldoor` по
|
||||
переходу `pop_leveldoor_open` 0/2 -> 1 (условие оригинала — иначе тайл
|
||||
ставился бы заново каждый кадр открытой двери). Если комната зеркала уже
|
||||
на экране, ставится `POP_RD_FLOOR` на обе страницы.
|
||||
Зеркало появляется, спрайты из атласа читаются. Остался незакрытый
|
||||
краевой вопрос: не устареет ли `g_fg` коллизии, если игрок окажется в
|
||||
комнате 4 ровно в момент постановки (в обычном прохождении дверь в другой
|
||||
комнате).
|
||||
3. **ОТРАЖЕНИЕ.** Самый дорогой шаг: это ЧЕТВЁРТЫЙ рисуемый Char помимо
|
||||
Кида и соперника, со своим клипом. Отрисовка у нас уже общая
|
||||
(`pop_cdraw.c`, слоты — memory `pop_char_draw_unified`), вопрос в числе
|
||||
слотов и в том, что отражение меняется вместе с Кидом каждый кадр, то
|
||||
есть `pop_char_skip_mask` на нём не сработает. ЧИСТО КОСМЕТИКА —
|
||||
делается последним.
|
||||
4. **ПРЫЖОК СКВОЗЬ + РОЖДЕНИЕ ТЕНИ — СДЕЛАНО 2026-08-10, НЕ ПРОВЕРЕНО В MAME.**
|
||||
- `is_obstacle` (`pop_map.c`): ветка зеркала (seg004:0239) — Кид, кадры
|
||||
бегового прыжка 39..43, направление влево -> `modif = 0x56`,
|
||||
`pop_jumped_mirror = -1`, препятствия нет.
|
||||
- `mirror_image` / `jump_through_mirror` / `pop_check_mirror`
|
||||
(`pop_map.c`, порт seg003:0798..08A9). Отражённый Char уходит в слот
|
||||
Guard как `CHARID_1_SHADOW`; `guardhp = hitp_max`, у Кида `hitp_curr = 1`.
|
||||
`savekid` НЕ делается — как в оригинале.
|
||||
- `pop_check_mirror()` зовётся из главного цикла ПЕРЕД отрисовкой
|
||||
персонажей (в оригинале — первая строка `draw_people`).
|
||||
- `autocontrol_shadow` + `autocontrol_shadow_level4` + `clear_char`
|
||||
(`guards.c`, seg002:081D/1131/seg006:1945). Тень идёт СВОЕЙ веткой
|
||||
целиком: к стражьему ИИ она не сводится, на ур. 4 не дерётся вовсе.
|
||||
- **АТЛАС ТЕНИ.** Вскрылось при чтении seg006:0532: тень вне боевых
|
||||
кадров 150..189 ходит по таблице КИДА, и `image` оттуда индексирует
|
||||
спрайты Кида, а не стража. Выбор атласа в `pop_cdraw` шёл по СЛОТУ —
|
||||
тень рисовалась бы спрайтами стража. Условие вынесено в
|
||||
`pop_frame_tbl_is_guard()` (`pop_kid.c`), его теперь читают и
|
||||
`load_frame`, и отрисовка — разъехаться не могут.
|
||||
- Звука (`sound_45_jump_through_mirror`) в порте нет, пропущен.
|
||||
Цена: `_CODE` +18 Б, банк 1 2311 -> 2367, банк 3 10328 -> 10551,
|
||||
банк 4 8243 -> 8267.
|
||||
**Что проверить в MAME:** на ур. 4 открыть дверь выхода, дойти до
|
||||
комнаты 4, разбежаться СПРАВА НАЛЕВО и прыгнуть в зеркало — должна
|
||||
появиться тень (спрайтами Кида, зеркально) и убежать влево, растворившись
|
||||
при `x < 80`. У Кида после этого 1 HP, у тени полная полоса.
|
||||
5. **КЛИП ТЕНИ** (seg008:1699): `obj_clip_left = 137 + (mirror_column-4)*32`
|
||||
— тень видна только СПРАВА от зеркала. **Не сделано и не однострочник:**
|
||||
в `pop_cdraw` есть клип сверху, снизу и СПРАВА (`vis_w`), а левого нет
|
||||
вовсе — `gfx_blit_cols_part_w` умеет ограничить ширину, но не пропустить
|
||||
колонки слева. Нужен либо новый примитив, либо сдвиг `bx` со срезом
|
||||
исходных колонок. Пока без него тень будет вылезать левее зеркала.
|
||||
|
||||
6. **ВИД ТЕНИ (два блиттера OR + XOR) — ОТЛОЖЕН 2026-08-11 по решению
|
||||
пользователя.** Пока тень рисуется как обычный персонаж, простой копией
|
||||
из атласов Кида — сюжетно уровень это не задерживает. Все изыскания
|
||||
собраны в [`../docs/shadow_render.md`](../docs/shadow_render.md):
|
||||
почему XOR несовместим с нашей прозрачностью `#FF` (нужен источник с
|
||||
прозрачным `0x00`), почему два прохода оригинала не воспроизводятся в лоб
|
||||
(операция читает ОЗУ-копию, а персонажи рисуются банком 0x5C) и во что
|
||||
это выливается — однопроходный композит `s | bg ^ s(сдвиг)`. Там же
|
||||
ЗАМЕР стоимости подготовки такого источника в рантайме (145.3 такта/байт:
|
||||
0.11 с на страницу, 1.3 с на реальные 186 КБ атласа) и четыре варианта.
|
||||
Блочные операции акселератора для этого уже есть в libbgi
|
||||
(`gfx_blit_cols_op` и семейство, `tests/accop` 10/10 PASS).
|
||||
Вернуться, когда будут сделаны все уровни и станет известно, какими
|
||||
кадрами тень реально пользуется (ожидание: бег, длинный прыжок из
|
||||
зеркала, питьё зелья, боёвка — тогда готовить надо единицы страниц).
|
||||
|
||||
Порядок: 1 -> 2 -> 4 -> 5 -> 3. После 4 и 5 уровень уже проходится, потому
|
||||
что сюжетно достаточно прыгнуть сквозь зеркало.
|
||||
|
||||
---
|
||||
|
||||
## P1 — берётся в любой момент
|
||||
|
||||
### <a id="l1-speed"></a>L1-SPEED. Игра идёт быстрее оригинала (найдено 2026-08-01)
|
||||
@@ -889,9 +709,9 @@ HP/минуты, номера «особых» комнат и уровней (
|
||||
где баг» вместо ручной подгонки позы.
|
||||
- **Звук** (CBL-эффекты, Фаза 5 `PORT_PLAN.md`) — геймплей не блокирует.
|
||||
- **Таймер уровня / HUD времени / меню / сохранения** — Фаза 6.
|
||||
- **[T-1](bug_list.md#t-1)** (пики: перерисовка по причине) — отдаётся почти
|
||||
бесплатно после [T-2](bug_list.md#t-2), отдельно не окупается.
|
||||
- **[T-1](BUGS_OPEN.md#t-1)** (пики: перерисовка по причине) — отдаётся почти
|
||||
бесплатно после [T-2](BUGS_OPEN.md#t-2), отдельно не окупается.
|
||||
- **Отключение мыши на время игры** и **замена PRNG** —
|
||||
`../docs/ideas_backlog.md` (оба дают доли процента кадра).
|
||||
- **OPT-1** (хирургический редрой шва) — решено НЕ делать, стоимость
|
||||
транзиентная; разбор в [`bug_closed.md`](bug_closed.md).
|
||||
транзиентная; разбор в [`BUGS_CLOSED.md`](BUGS_CLOSED.md).
|
||||
|
||||
Reference in New Issue
Block a user