Доски приведены в соответствие с кодом; 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:
@@ -69,8 +69,8 @@ Princed для DAT v1.0 (контейнер/индекс/чек-сумма, ко
|
||||
- `roomtest/` — **активная разработка**: уровень 1 целиком (Kid, стражи,
|
||||
ловушки, ворота, loose-полы). Свой `CLAUDE.md`; текущие задачи —
|
||||
`roomtest/TASKS_OPEN.md` (закрытые с протоколами —
|
||||
`roomtest/TASKS_CLOSED.md`), открытые баги — `roomtest/bug_list.md`,
|
||||
закрытые с разбором корней — `roomtest/bug_closed.md`.
|
||||
`roomtest/TASKS_CLOSED.md`), открытые баги — `roomtest/BUGS_OPEN.md`,
|
||||
закрытые с разбором корней — `roomtest/BUGS_CLOSED.md`.
|
||||
- `poc/` — ранний proof-of-concept (снег/атлас/kbd_raw); ассеты в `poc/res/`.
|
||||
- `bgtest/`, `coltest/` — отдельные проверки фона/коллизии.
|
||||
- `toolchain/` — python-упаковщики ассетов + эталонные PNG (`1.1-2.png`).
|
||||
|
||||
@@ -463,7 +463,7 @@ tiny/small.
|
||||
перерисовку тайлов; замер 2026-07-30 — типичный кадр ~371 К тактов
|
||||
(~86 % периода). Инструмент замера уже в коде: полосы бордюра `PROF()`
|
||||
в `roomtest.c`. План выжимания — `../roomtest/TASKS_CLOSED.md` (CLIP-1) и
|
||||
`../roomtest/bug_list.md` (T-1/T-2).
|
||||
`../roomtest/BUGS_OPEN.md` (T-1/T-2).
|
||||
3. ~~**Ёмкость атласа на актора**~~ — **закрыт**: Kid разложен на 28
|
||||
атласов-страниц по 8 спрайтов (`pop_pack_kid.py`), страж — на 5;
|
||||
мульти-страничного формата `.atl` не потребовалось. Побочно
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
| Документ | О чём |
|
||||
|----------|-------|
|
||||
| [`../roomtest/TASKS_OPEN.md`](../roomtest/TASKS_OPEN.md) | **Что берётся в работу сейчас** (не в этой папке, но входная точка) |
|
||||
| [`../roomtest/bug_list.md`](../roomtest/bug_list.md) | Открытые баги roomtest (закрытые — в `bug_closed.md` рядом) |
|
||||
| [`../roomtest/BUGS_OPEN.md`](../roomtest/BUGS_OPEN.md) | Открытые баги roomtest (закрытые — в `BUGS_CLOSED.md` рядом) |
|
||||
| [`impl_diff.md`](impl_diff.md) | **Осознанные расхождения с SDLPoP**: где мы сделали не дословно и почему |
|
||||
| [`perf_backlog.md`](perf_backlog.md) | **Отложенная оптимизация отрисовки** с замерами + как мерить (wait-state'ы, границы кадра) |
|
||||
| [`levels_plan.md`](levels_plan.md) | Следующий этап: уровни 2+, второй тайлсет, читы SDLPoP |
|
||||
| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки |
|
||||
| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди |
|
||||
| [`host_tests_plan.md`](host_tests_plan.md) | Модульные тесты движка под ucsim_z80: два шва, регрессии из `bug_closed.md`, дифф против SDLPoP |
|
||||
| [`host_tests_plan.md`](host_tests_plan.md) | Модульные тесты движка под ucsim_z80: два шва, регрессии из `BUGS_CLOSED.md`, дифф против SDLPoP |
|
||||
| [`shadow_render.md`](shadow_render.md) | **Вид Тени (OR+XOR)** — отложено: почему XOR несовместим с прозрачностью `#FF`, замер подготовки источника, четыре варианта |
|
||||
| [`ideas_backlog.md`](ideas_backlog.md) | Осознанно отложенные гипотезы (мышь, PRNG) |
|
||||
| [`prng_alternatives.md`](prng_alternatives.md) | Запасные генераторы, если упрёмся в бюджет кадра |
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
Минимум, который надо перехватывать: `pop_set_redraw`,
|
||||
`pop_set_redraw_above`, `pop_loose_mob_spawn`, `pop_gate_redraw`.
|
||||
|
||||
## Фаза 2. Регрессионные кейсы из `bug_closed.md`
|
||||
## Фаза 2. Регрессионные кейсы из `BUGS_CLOSED.md`
|
||||
|
||||
После швов `bug_closed.md` превращается в готовую спецификацию: у каждой
|
||||
После швов `BUGS_CLOSED.md` превращается в готовую спецификацию: у каждой
|
||||
записи есть симптом и ожидаемое поведение. Кандидаты, которые ловятся
|
||||
логикой (без отрисовки и без железа):
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
| `BUG-LOOSE-2` | кусок, начавший падать, долетает и кладёт щебень ПОСЛЕ смены комнаты |
|
||||
| `BUG-CEIL-2` | loose-плита ряда 2 верхнего соседа живёт как «ряд −1» |
|
||||
|
||||
`BUG-LOOSE-2` стоит взять первым: он до сих пор помечен в `bug_list.md`
|
||||
`BUG-LOOSE-2` стоит взять первым: он до сих пор помечен в `BUGS_OPEN.md`
|
||||
как непроверенный именно потому, что гонку «уйти из комнаты раньше, чем
|
||||
долетит плита» через мост MAME воспроизвести не удалось. На уровне логики
|
||||
это несколько строк — заспавнить кусок, сменить комнату, тикать до
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
**Файлы:** `roomtest/pop_map.c` (`pop_coll_shift`, `pop_coll_invalidate`,
|
||||
`check_collisions`), `roomtest/roomtest.c` (`enter_room_side`).
|
||||
**Связанный баг:** BUG-GATE-PASS-1 (`roomtest/bug_closed.md`).
|
||||
**Связанный баг:** BUG-GATE-PASS-1 (`roomtest/BUGS_CLOSED.md`).
|
||||
**Дата:** 2026-08-09.
|
||||
|
||||
### Как в оригинале
|
||||
|
||||
@@ -525,8 +525,8 @@ memory `mame_mcp_bridge`); в самом `roomtest.c` для этого уже
|
||||
(до 7 блитов). Сгенерировать в атласе «столб решётки» и выводить одним
|
||||
`gfx_blit_part` с обрезкой по фазе `gate_bot_y & 7`: 7 блитов → 1.
|
||||
3. **Не перерисовывать статичные части шва** — грань ворот, пол и кромка при
|
||||
анимации решётки не меняются (см. OPT-1 в `../roomtest/bug_closed.md` —
|
||||
анимации решётки не меняются (см. OPT-1 в `../roomtest/BUGS_CLOSED.md` —
|
||||
решено не делать, стоимость транзиентная).
|
||||
4. **T-1 / T-2** (`../roomtest/bug_list.md`) — перерисовка пик по причине и
|
||||
4. **T-1 / T-2** (`../roomtest/BUGS_OPEN.md`) — перерисовка пик по причине и
|
||||
idle-skip Кида: самый большой оставшийся резерв, потому что убирает работу
|
||||
целиком, а не удешевляет её.
|
||||
|
||||
@@ -44,8 +44,8 @@ up_pressed/go_up_leveldoor`, `seg006.c: play_seq → SEQ_END_LEVEL`,
|
||||
| 1 | dungeon | обычный | — (текущая база) |
|
||||
| **2** | **dungeon** | **обычный** | **ничего нового: тот же набор объектов минус меч** |
|
||||
| 3 | dungeon | СКЕЛЕТ | чомперы |
|
||||
| 4 | palace | обычный | **тайлсет palace**, зеркало (спецсобытие `mirror_level`) |
|
||||
| 5 | palace | обычный | — |
|
||||
| 4 | palace | обычный | **тайлсет palace**, зеркало (спецсобытие `mirror_level`) — **СДЕЛАНО** |
|
||||
| 5 | palace | обычный | новых ТАЙЛОВ нет; спецсобытие **тень крадёт зелье** (комната 24) — [L5-SHADOW](../roomtest/TASKS_OPEN.md#l5-shadow) |
|
||||
| 6 | palace | ТОЛСТЫЙ | падение на входе (спецсобытие) |
|
||||
| 7 | dungeon | обычный | — |
|
||||
| 8, 9 | dungeon | обычный | — |
|
||||
@@ -111,7 +111,19 @@ pop_current_level) → load_level(pop_next_level)`. Порядок сноса/
|
||||
|
||||
---
|
||||
|
||||
## 3. Шаг 2 — второй тайлсет (palace, уровни 4+)
|
||||
## 3. Шаг 2 — второй тайлсет (palace, уровни 4+) — **СДЕЛАН**
|
||||
|
||||
> **Закрыт (ревизия 2026-08-11 по коду).** В дереве есть всё, что
|
||||
> проектировалось ниже: `pop_pack_bg.py` печёт ДВА набора атласов
|
||||
> (`pop_*` из VDUNGEON, `pal_*` из VPALACE, каскад с обменом приоритетов),
|
||||
> `pop_bg_load` принимает тип набора и ставит флаг `pop_palace`, по которому
|
||||
> `pop_room.c` выбирает дворцовую ветку (`wall_pattern` дворца — сплошная
|
||||
> заливка + пять mono-полос, `PALACE_WALL_MONO_IDS`), решётчатые тайлы
|
||||
> 25-29 есть и в `tile_table` (`pop_tile.c`), и в коллизии — `tile_is_floor`
|
||||
> совпадает с `seg006:0628` тайл в тайл. Уровень 4 проходится smoke-тестом.
|
||||
>
|
||||
> Текст ниже оставлен как справочник по раскрою палитры и по тому, почему
|
||||
> смена тайлсета — это перезапись 32 записей, а не переиндексация спрайтов.
|
||||
|
||||
**Ассеты.** `toolchain/pop_pack_bg.py` уже читает PNG каскадом
|
||||
VDUNGEON→VPALACE (та же логика, что в игре), но печёт ОДИН набор атласов
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
> - **S2/S3/S4 — не сделаны и не срочны.** Исходный повод (баг #4,
|
||||
> пинг-понг у шва) закрыт иначе — поправкой odd-pixel в
|
||||
> `char_x_forward_edge` + `pop_leave_timer` (разбор корня —
|
||||
> `../roomtest/bug_closed.md`, BUG-SEAM-PINGPONG).
|
||||
> `../roomtest/BUGS_CLOSED.md`, BUG-SEAM-PINGPONG).
|
||||
>
|
||||
> **Зачем документ остаётся.** Полная straddle-модель понадобится для:
|
||||
> (а) читов осмотра соседних комнат `H/J/U/N` (`levels_plan.md` §4),
|
||||
@@ -78,7 +78,7 @@ drawn_room с `curr_col=-1/10` + снапшоты соседей `g_lcol/g_rcol`
|
||||
### S4. Полировка
|
||||
- Окклюзия/ceiling у шва при straddle, BUG-OCCL-1 (глубина), правый край.
|
||||
|
||||
## Связанные баги — все ЗАКРЫТЫ (`../roomtest/bug_closed.md`)
|
||||
## Связанные баги — все ЗАКРЫТЫ (`../roomtest/BUGS_CLOSED.md`)
|
||||
BUG-CEIL-1 (руки при прыжке вверх), BUG-CEIL-2 (loose в потолке),
|
||||
BUG-CEIL-3 (потолок над анимируемыми воротами), BUG-OCCL-1 (тень дальней
|
||||
колонны) — починены без полной straddle-модели. То есть S4 «полировка
|
||||
|
||||
+30
-10
@@ -2,7 +2,7 @@
|
||||
|
||||
Сюда переезжает всё, что **закрыто**: подтверждённые фиксы, снятые
|
||||
диагнозы, осознанные решения «не делать». Открытые баги — в
|
||||
[`bug_list.md`](bug_list.md), текущие задачи — в
|
||||
[`BUGS_OPEN.md`](BUGS_OPEN.md), текущие задачи — в
|
||||
[`TASKS_OPEN.md`](TASKS_OPEN.md), закрытые задачи с протоколами — в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md).
|
||||
|
||||
@@ -377,7 +377,7 @@ kid_col = 8 guard_col = 12
|
||||
> **Итог 2026-08-05.** Физика тут ни при чём — виновата клавиатура.
|
||||
> Зажатый Shift снимался автоповтором зажатой стрелки, поэтому к кадрам
|
||||
> 102…106 (окно зацепа) движок видел Shift отпущенным. Полный разбор и
|
||||
> фикс — [BUG-KBD-5](bug_closed.md#bug-kbd-5); поведение Shift в MAME
|
||||
> фикс — [BUG-KBD-5](BUGS_CLOSED.md#bug-kbd-5); поведение Shift в MAME
|
||||
> проверено замером карты `_kbdraw_down`. Осталось подтвердить сам зацеп
|
||||
> живой игрой; версии 2 и 3 ниже проверять только если он всё ещё не выйдет.
|
||||
|
||||
@@ -426,7 +426,7 @@ kid_col = 8 guard_col = 12
|
||||
Харнесс подменяет клавиатуру и потому этот путь не проверяет вовсе, а у
|
||||
нас есть история проблем ровно с «Shift + стрелки» (KBD-1, BUG-KBD-3/4).
|
||||
Замер в MAME: при зажатом Shift и зажатой стрелке бит `LSh` в
|
||||
`_kbdraw_down` стоял в нуле. Разбор — [BUG-KBD-5](bug_closed.md#bug-kbd-5).
|
||||
`_kbdraw_down` стоял в нуле. Разбор — [BUG-KBD-5](BUGS_CLOSED.md#bug-kbd-5).
|
||||
- **Сцена харнесса не равна комнате 9.** Там изолированная комната
|
||||
(соседи — стена), а в игре слева комната 8; кромки шва участвуют в
|
||||
`get_tile`. Проверять чтением `Kid.x` в момент прыжка: попал ли он в
|
||||
@@ -1450,7 +1450,7 @@ MAME по кромкам.
|
||||
**Оговорка о полноте проверки.** Каждый корень закрыт тем, что его СОБСТВЕННЫЙ
|
||||
сценарий больше не воспроизводится; сквозного прохождения уровня и поиска
|
||||
регрессий в соседней механике автоматика не делала. Чек-лист для ручной
|
||||
перепроверки — в [`bug_list.md`](bug_list.md), раздел «Ручная перепроверка
|
||||
перепроверки — в [`BUGS_OPEN.md`](BUGS_OPEN.md), раздел «Ручная перепроверка
|
||||
фиксов». Особое внимание — порту `check_collisions`: он переписал ВСЮ
|
||||
горизонтальную коллизию.
|
||||
|
||||
@@ -1545,7 +1545,7 @@ foretable — в той же странице по смещению `0x1000` (с
|
||||
|
||||
**Остаток:** экран при этом перелистывается на соседнюю комнату, и Кид в шве
|
||||
не рисуется — отдельный баг BUG-SEAM-DRAW-1 (модель straddle S3), см.
|
||||
`bug_list.md`.
|
||||
`BUGS_OPEN.md`.
|
||||
|
||||
### BUG-STANDUP-1. Вставание из приседа у стены роняло сквозь пол — **ЗАКРЫТ**
|
||||
|
||||
@@ -1887,10 +1887,10 @@ dir_56_none`. То есть стражи — такая же часть дан
|
||||
# Прогон всех комнат уровней 1 и 2 (пользователь, 2026-08-07) — ЗАКРЫЛ ТРИ РЕВИЗИИ РАЗОМ
|
||||
|
||||
Результат прогона: **крупных багов нет**. Тем самым закрыты и переехали сюда
|
||||
из `bug_list.md` три накопившихся хвоста — чек-листы ручной перепроверки
|
||||
из `BUGS_OPEN.md` три накопившихся хвоста — чек-листы ручной перепроверки
|
||||
фиксов, таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений прогона
|
||||
2026-08-03. Всё, что с прогона 2026-08-07 осталось открытым, — три записи
|
||||
уровня 2 в [`bug_list.md`](bug_list.md) (BUG-GUARD-COLOR-1,
|
||||
уровня 2 в [`BUGS_OPEN.md`](BUGS_OPEN.md) (BUG-GUARD-COLOR-1,
|
||||
BUG-GUARD-SPLASH-1, BUG-CHEAT-FIGHT-1); ни одна из них не мешает играть.
|
||||
|
||||
Сырые формулировки пользователя лежат рядом: [`bugs_level1.md`](bugs_level1.md)
|
||||
@@ -2040,7 +2040,7 @@ pop_guard_hurt = 0;`), а не безусловно — это и экономи
|
||||
2407 → 3369 (+962 Б, банк занят на 20.6 %, свободно 13 015 Б).
|
||||
|
||||
**Цвет брызг** зависит от палитры стража (слоты `0x90..0x9F`), поэтому он
|
||||
изменится вместе с [BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1) —
|
||||
изменится вместе с [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1) —
|
||||
отдельной работы не требует.
|
||||
|
||||
|
||||
@@ -2141,7 +2141,7 @@ if (curr_row_coll_room[column] >= 0 &&
|
||||
Расхождение с дословным вариантом A записано в `../docs/impl_diff.md` (D-1).
|
||||
|
||||
**Осторожно:** это сердце коллизии, вокруг которого разбирался
|
||||
BUG-SEAM-PINGPONG (см. `bug_closed.md`). Приёмка:
|
||||
BUG-SEAM-PINGPONG (см. `BUGS_CLOSED.md`). Приёмка:
|
||||
|
||||
1. Комнаты 6 ↔ 8 уровня 1, закрытая решётка, **обе** стороны.
|
||||
2. Мелким шагом (упереться) И с разбега (не пройти насквозь) — сценарий
|
||||
@@ -2252,5 +2252,25 @@ draw_tile_anim(); <- СВОЯ графика тайла: челюсти
|
||||
чомпера. Безусловная пометка = перерисовка тайла каждый кадр на каждый
|
||||
факел; слой `draw_tile_anim` рисует ещё пики, зелье и меч (seg008:0644),
|
||||
поэтому теоретически «застыть под пламенем» могут и они — заведено открытым
|
||||
в `bug_list.md` (TORCH-ANIM-RIGHT). На уровнях 1–4 такого соседства не
|
||||
в `BUGS_OPEN.md` (TORCH-ANIM-RIGHT). На уровнях 1–4 такого соседства не
|
||||
встретилось.
|
||||
|
||||
---
|
||||
|
||||
<a id="t-2"></a>
|
||||
## T-2. Idle-skip — **ЗАКРЫТ 2026-08-08**
|
||||
|
||||
Сделан как шаг 1 задачи [DRAW-COST](TASKS_OPEN.md#draw-cost) (коммит
|
||||
`a25ce58`), и шире, чем формулировался здесь: пропускается не только Кид, а
|
||||
ЛЮБОЙ персонаж, у которого с прошлой отрисовки этой страницы дабл-буфера не
|
||||
изменился ни один вход отрисовки, — включая труп стража и ждущего стража.
|
||||
Условие «обе страницы уже получили это состояние», которого требовала эта
|
||||
запись, выполнено само собой: снимок входов хранится ПО СТРАНИЦАМ.
|
||||
|
||||
Замер: комната 1.3 с трупом стража, Кид стоит — 210 % -> 116 % кадрового
|
||||
периода, ноль вызовов `pop_heal_fast` за кадр. Контракт — шапка
|
||||
`pop_cdraw.h`, разбор и что делать дальше — `TASKS_OPEN.md#draw-cost`.
|
||||
|
||||
Связь с T-1 сработала как и предсказано: пока Кида не перерисовываем, heal'а
|
||||
нет, стирать пики нечем. Но T-1 остаётся открытым — трогать пики
|
||||
безусловно мы всё ещё продолжаем, когда Кид рядом ДВИЖЕТСЯ.
|
||||
@@ -1,56 +1,37 @@
|
||||
# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации
|
||||
|
||||
Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней)
|
||||
переехало в [`bug_closed.md`](bug_closed.md): прежде чем заводить новый баг,
|
||||
грепни там по симптому.
|
||||
**Здесь ТОЛЬКО незакрытое.** Всё закрытое (и, что важнее, разбор корней)
|
||||
живёт в [`BUGS_CLOSED.md`](BUGS_CLOSED.md) — прежде чем заводить новый баг,
|
||||
грепни там по симптому. Сырые формулировки пользователя с прогонов —
|
||||
[`bugs_level1.md`](bugs_level1.md) / [`bugs_level2.md`](bugs_level2.md).
|
||||
|
||||
Приоритеты работ — в [`TASKS_OPEN.md`](TASKS_OPEN.md) (закрытые задачи с
|
||||
протоколами — [`TASKS_CLOSED.md`](TASKS_CLOSED.md)), а не здесь. Правило
|
||||
проекта: механику сверять с `../SDLPoP/src/` ДО кодинга.
|
||||
|
||||
**Ревизия списка: 2026-08-07 — прогон ВСЕХ комнат уровней 1 и 2**
|
||||
(пользователь). Крупных багов нет. Поэтому в [`bug_closed.md`](bug_closed.md)
|
||||
уехали разом: чек-листы ручной перепроверки фиксов (2026-08-03, обе волны),
|
||||
таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений того же прогона —
|
||||
все они закрыты этим проходом. С прогона пришли **три новые записи**, все по
|
||||
уровню 2 (сырые формулировки — [`bugs_level2.md`](bugs_level2.md)).
|
||||
**Ревизия 2026-08-11:** файл вычищен от закрытых записей (правило «в `_OPEN`
|
||||
только открытое»). Уровни 1-4 приняты smoke-тестами; крупных багов нет.
|
||||
|
||||
| ID | что | тип | статус |
|
||||
|----|-----|-----|--------|
|
||||
| ~~BUG-CHEAT-FIGHT-1~~ | `+`/`−` в бою с вынутым мечом → Кид теряет управление | Major (чит) | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-cheat-fight-1) |
|
||||
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
|
||||
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводимый: на повторе не поднялся; на пререлиз |
|
||||
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
|
||||
| ~~T-2~~ | Кид перерисовывается в покое | оптимизация | **ЗАКРЫТ 2026-08-08** — [DRAW-COST шаг 1](TASKS_OPEN.md#draw-cost) |
|
||||
| ~~BUG-GATE-PASS-1~~ | проход сквозь закрытую решётку шва | Major | **ЗАКРЫТ 2026-08-09** — [bug_closed.md](bug_closed.md#bug-gate-pass-1), смоук уровня 1 пройден |
|
||||
| ~~BUG-GUARD-IX-1~~ | «зависание» в бою со стражем: затёрт IX главного цикла | Blocker | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-guard-ix-1) |
|
||||
| ~~BUG-GATE-SEAM-ROW1~~ | решётка в шве не анимируется, если ворота не в ряду 0 | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-gate-seam-row1) |
|
||||
| ~~BUG-LOOSE-BUTTON-1~~ | упавшая плита не нажимает кнопку под собой | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-loose-button-1) |
|
||||
| ~~BUG-GATE-FF-1~~ | ворота «открыты навсегда» непроходимы (0xFF перегружен) | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-gate-ff-1) |
|
||||
| ~~BUG-TORCH-CHOMP-1~~ | чомпер стирает пламя соседнего факела | Minor | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-torch-chomp-1) |
|
||||
| [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить |
|
||||
| [MIRROR-FG-STALE](#mirror-fg-stale) | зеркало поставлено, пока игрок В комнате 4 → коллизия его не видит | **низкий** (в обычном прохождении недостижимо) | открыт, фикс на несколько строк |
|
||||
| [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт |
|
||||
| [TORCH-ANIM-RIGHT](#torch-anim-right) | под запечённым пламенем застывают не только челюсти чомпера | **низкий** | открыт: на уровнях 1-4 такого соседства нет |
|
||||
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
|
||||
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводим: ни сценарием, ни попиксельной подгонкой X не поднимается |
|
||||
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводим: на повторе не поднялся; на пререлиз |
|
||||
|
||||
---
|
||||
|
||||
<a id="уровень-2"></a>
|
||||
# Уровень 2 — баги с приёмки
|
||||
# С приёмки уровня 2 (2026-08-07)
|
||||
|
||||
Приёмка уровня 2 закрыта ([L2-PASS](TASKS_CLOSED.md#l2-pass)): smoke
|
||||
2026-08-05 + обход всех комнат 2026-08-07. Карта содержимого уровня (что где
|
||||
стоит по `res2002.bin`, какие кнопки какие ворота открывают) — там же, по ней
|
||||
видно, «механика не сработала» это или «так и задумано».
|
||||
|
||||
**Закрыто с этой волны:** [BUG-LOOSE-3](bug_closed.md) — чёрный бар под
|
||||
упавшей плитой-потолком; [BUG-GUARD-DEAF-1](bug_closed.md) — страж не
|
||||
оборачивался на вернувшегося Кида;
|
||||
[BUG-GUARD-SPLASH-1](bug_closed.md#bug-guard-splash-1) — «брызги» при
|
||||
попадании по стражу; [BUG-SWORD-GHOST-1](bug_closed.md#bug-sword-ghost-1) —
|
||||
меч, спрятанный посреди боя после перехода комнаты (корень — мнимая стена за
|
||||
краем комнаты в `get_tile`; кэш соседей расширен до полных комнат);
|
||||
[BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1) — цвет стража и его
|
||||
полосы HP теперь берётся из `guards_color` уровня (2026-08-07).
|
||||
|
||||
Ниже — то, что осталось открытым после прогона 2026-08-07.
|
||||
Всё найденное тем прогоном закрыто, кроме двух записей ниже
|
||||
(`BUG-SPIKE-1` — уровень 2, комната 6). Карта содержимого уровня 2 (что где
|
||||
стоит по `res2002.bin`, какие кнопки какие ворота открывают) — в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#l2-pass), по ней видно, «механика не
|
||||
сработала» это или «так и задумано».
|
||||
|
||||
<a id="bug-spike-1"></a>
|
||||
## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
|
||||
@@ -141,20 +122,6 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
|
||||
---
|
||||
|
||||
<a id="уровень-3"></a>
|
||||
# Уровень 3 — не баги, а неначатые задачи
|
||||
|
||||
Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал четыре наблюдения. Два
|
||||
были настоящими багами и **закрыты в тот же день** (разбор — в
|
||||
[`bug_closed.md`](bug_closed.md): **BUG-JUMPWALL-1** и **BUG-SEAM-WEDGE-1**).
|
||||
Оставшиеся два — не баги, а неначатые задачи:
|
||||
|
||||
| наблюдение | что это на самом деле |
|
||||
|------------|------------------------|
|
||||
| к.22: чомпер (2,6) не анимируется и **вообще не рисуется** | [L3-CHOMP](TASKS_OPEN.md#l3-chomp) — механики чомперов НЕТ. В таблице тайлов `pop_bg.c:211` строка `12 chomper` рисует только основание, правую грань и низ; самих челюстей (спрайт из `chtab`, `draw_tile_anim` seg008) нет вовсе. Так и должно выглядеть до порта |
|
||||
| к.10: скелет не оживает | [L3-SKEL](TASKS_OPEN.md#l3-skel) — `check_skel` (seg002:0E1F) не портирован. **И комната другая:** тайл `skeleton(21)`, который оживает, лежит в **к.1 (1,5)** — это `skeleton_room=1, skeleton_column=5, skeleton_row=1`. Ещё два скелета уровня (к.17 (2,7), к.19 (2,2)) — просто декорация, они не оживают никогда. Плюс условие: скелет встаёт, только когда **дверь уровня уже открыта** и Кид стоит в колонке 2 или 3 |
|
||||
|
||||
Приёмки уровня 3 (полного обхода комнат) ещё не было — она осмысленна только
|
||||
после L3-CHOMP/L3-SKEL.
|
||||
|
||||
---
|
||||
|
||||
@@ -193,22 +160,6 @@ Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал
|
||||
**Приоритет:** отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается.
|
||||
|
||||
<a id="t-2"></a>
|
||||
## T-2. Idle-skip — **ЗАКРЫТ 2026-08-08**
|
||||
|
||||
Сделан как шаг 1 задачи [DRAW-COST](TASKS_OPEN.md#draw-cost) (коммит
|
||||
`a25ce58`), и шире, чем формулировался здесь: пропускается не только Кид, а
|
||||
ЛЮБОЙ персонаж, у которого с прошлой отрисовки этой страницы дабл-буфера не
|
||||
изменился ни один вход отрисовки, — включая труп стража и ждущего стража.
|
||||
Условие «обе страницы уже получили это состояние», которого требовала эта
|
||||
запись, выполнено само собой: снимок входов хранится ПО СТРАНИЦАМ.
|
||||
|
||||
Замер: комната 1.3 с трупом стража, Кид стоит — 210 % -> 116 % кадрового
|
||||
периода, ноль вызовов `pop_heal_fast` за кадр. Контракт — шапка
|
||||
`pop_cdraw.h`, разбор и что делать дальше — `TASKS_OPEN.md#draw-cost`.
|
||||
|
||||
Связь с T-1 сработала как и предсказано: пока Кида не перерисовываем, heal'а
|
||||
нет, стирать пики нечем. Но T-1 остаётся открытым — трогать пики
|
||||
безусловно мы всё ещё продолжаем, когда Кид рядом ДВИЖЕТСЯ.
|
||||
|
||||
---
|
||||
|
||||
@@ -272,9 +223,9 @@ Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал
|
||||
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
|
||||
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
|
||||
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
|
||||
Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](bug_closed.md#bug-cheat-fight-1).
|
||||
Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1).
|
||||
- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство
|
||||
данных уровня (разбор — «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md));
|
||||
данных уровня (разбор — «НЕ БАГИ» в [`BUGS_CLOSED.md`](BUGS_CLOSED.md));
|
||||
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.
|
||||
|
||||
|
||||
@@ -283,7 +234,7 @@ Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал
|
||||
<a id="died-on-button"></a>
|
||||
## DIED-ON-BUTTON. `died_on_button` (seg007:776) не портирован
|
||||
|
||||
Обнаружено при разборе [BUG-LOOSE-BUTTON-1](bug_closed.md#bug-loose-button-1).
|
||||
Обнаружено при разборе [BUG-LOOSE-BUTTON-1](BUGS_CLOSED.md#bug-loose-button-1).
|
||||
`check_press` (seg006:1707) разбирает ЛЮБОГО мёртвого `Char`, не только Кида:
|
||||
|
||||
```c
|
||||
@@ -306,10 +257,11 @@ if (curr_tile2 == tiles_15_opener || curr_tile2 == tiles_6_closer) {
|
||||
|
||||
---
|
||||
|
||||
<a id="torch-anim-right"></a>
|
||||
## TORCH-ANIM-RIGHT. Под пламенем могут застыть не только челюсти чомпера
|
||||
|
||||
Открыто 2026-08-10 при закрытии
|
||||
[BUG-TORCH-CHOMP-2](bug_closed.md#bug-torch-chomp-2).
|
||||
[BUG-TORCH-CHOMP-2](BUGS_CLOSED.md).
|
||||
|
||||
Пламя факела запекается в фон и рисуется в ячейке ПРАВОГО СОСЕДА. Оригинал
|
||||
после каждого кадра факела метит этого соседа (`set_redraw_anim_right`,
|
||||
@@ -332,6 +284,7 @@ seg007:0101) и перерисовывает весь его слой `anim` п
|
||||
|
||||
---
|
||||
|
||||
<a id="fore-dup"></a>
|
||||
## FORE-DUP. Передний слой тайла рисуется ДВАЖДЫ при перекрытии объектов
|
||||
|
||||
Найдено пользователем 2026-08-10 (вопросом, а не по симптому — картинка
|
||||
@@ -381,3 +334,40 @@ for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row)
|
||||
счётчиком, сцена «бой в комнате 3 уровня 1» и «Кид на тайле зеркала,
|
||||
ур. 4»; сравнить число нарисованных кусков с числом уникальных тайлов.
|
||||
Если дубль мал — оставить как есть и закрыть запись.
|
||||
|
||||
---
|
||||
|
||||
<a id="mirror-fg-stale"></a>
|
||||
## MIRROR-FG-STALE. Зеркало, поставленное при игроке В комнате 4, не попадает в коллизию
|
||||
|
||||
Заведено 2026-08-11 как краевой случай, оставшийся открытым от
|
||||
[L4-MIRROR](TASKS_CLOSED.md#l4-mirror) (шаг 2). **Низкий приоритет: в
|
||||
обычном прохождении недостижимо** — дверь выхода уровня 4 стоит не в той
|
||||
комнате, где зеркало, поэтому игрок физически не может быть в комнате 4 в
|
||||
момент её открытия.
|
||||
|
||||
**Причина (по коду, не по симптому).** `place_mirror` (`pop_trob.c`) пишет
|
||||
тайл зеркала в ДАННЫЕ УРОВНЯ (`pop_level_set_tile` — страница уровня в W0) и,
|
||||
если комната зеркала на экране, помечает тайл на перерисовку. А коллизия
|
||||
работает не с данными уровня, а со СНИМКОМ комнаты `room_fg`, который
|
||||
`pop_room_load` делает один раз при входе в комнату (`pop_map_set(room_fg)`
|
||||
в `roomtest.c`). Снимок в этот момент не обновляется.
|
||||
|
||||
**Что будет:** зеркало нарисуется (перерисовка тайла отработает), но для
|
||||
коллизии его как бы нет — `wall_type` не вернёт «стена слева», а ветка
|
||||
`is_obstacle` для прыжка сквозь зеркало не сработает. То есть Кид пробежит
|
||||
сквозь зеркало насквозь и **тень не родится**, пока комнату не перезайти.
|
||||
|
||||
**Сценарий проверки** (нужен чит `+`/`−` ROOMNAV, иначе не собрать):
|
||||
1. уровень 4, нажать кнопку, открывающую дверь выхода;
|
||||
2. **пока дверь анимируется** (43 тика ≈ 3.5 с) уйти читом в комнату 4;
|
||||
3. дождаться, когда дверь дорисует открытие — зеркало появится на экране;
|
||||
4. разбежаться справа налево и прыгнуть в зеркало.
|
||||
- Ожидание при баге: Кид пролетает насквозь как через пустоту, тени нет.
|
||||
- После выхода из комнаты и возврата в неё всё работает нормально.
|
||||
|
||||
**Фикс — несколько строк:** в `place_mirror`, в ветке
|
||||
`cur_room == MIRROR_ROOM`, обновить и живую карту, а не только данные уровня
|
||||
(в `pop_map.c` уже есть внутренние точки записи `g_fg[tilepos] = ...`,
|
||||
нужна публичная «поставить тайл в текущей комнате»). Делать вместе с любой
|
||||
следующей правкой `pop_trob.c` — отдельного захода не стоит.
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
**Что в работе сейчас — `TASKS_OPEN.md`** (доска задач: приоритеты, критерии
|
||||
готовности); сделанное с протоколами замеров — `TASKS_CLOSED.md`. Баги —
|
||||
`bug_list.md` (только ОТКРЫТЫЕ) и `bug_closed.md` (закрытые + разбор корней:
|
||||
`BUGS_OPEN.md` (только ОТКРЫТЫЕ) и `BUGS_CLOSED.md` (закрытые + разбор корней:
|
||||
перед заведением нового бага грепни там по симптому); сырые формулировки
|
||||
пользователя с прогонов — `bugs_level1.md` / `bugs_level2.md`. План
|
||||
следующих уровней — `../docs/levels_plan.md`.
|
||||
|
||||
Правило разнесения: как только задача/баг закрыт — запись целиком переезжает
|
||||
в `TASKS_CLOSED.md` / `bug_closed.md`, а в открытом файле остаётся ссылка.
|
||||
в `TASKS_CLOSED.md` / `BUGS_CLOSED.md`, а в открытом файле остаётся ссылка.
|
||||
Открытые файлы обязаны читаться целиком за раз.
|
||||
|
||||
## Сборка и запуск
|
||||
|
||||
@@ -1,173 +1,114 @@
|
||||
# Точка входа для следующей сессии (записано 2026-08-10, поздний вечер)
|
||||
# Точка входа для следующей сессии (обновлено 2026-08-11)
|
||||
|
||||
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
|
||||
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
|
||||
`TASKS_OPEN.md` (доска), `bug_list.md` (открытые баги), `CLAUDE.md`.
|
||||
`TASKS_OPEN.md` (доска), `BUGS_OPEN.md` (открытые баги), `CLAUDE.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Состояние репозитория
|
||||
## 1. СЛЕДУЮЩАЯ ЗАДАЧА: [L5-SHADOW](TASKS_OPEN.md#l5-shadow) — уровень 5
|
||||
|
||||
Всё закоммичено и запушено, `main` в синхроне с `origin/main`.
|
||||
Последний коммит сессии — `8beb66a`.
|
||||
Уровни 1-4 приняты **smoke-тестами**; полные обходы всех комнат делаются по
|
||||
готовности ВСЕХ уровней ([политика приёмок](TASKS_CLOSED.md#pass-policy),
|
||||
решение пользователя 2026-08-11) — отдельных `L3-PASS`/`L4-PASS` больше нет.
|
||||
|
||||
`tests-host`: 5 наборов, все проходят (`[geom] 3144`).
|
||||
`make size-check`: чисто, роста нет.
|
||||
Уровень 5 **не приносит ни одного нового тайла** (инвентарь `res2005.bin`
|
||||
сверен: всё уже есть с уровней 1-4). Вся работа — одно спецсобытие: тень
|
||||
появляется в комнате 24, ждёт открытия двери, идёт к зелью, **выпивает его**
|
||||
и уходит за левый край; боя нет. Писать: `do_auto_moves` + таблица
|
||||
`shad_drink_move`, `do_init_shad`, ветку `check_shadow` для уровня 5,
|
||||
`autocontrol_shadow_level5`, плюс ветку ТЕНИ в `check_guard_fallout`.
|
||||
Всё расписано по SDLPoP прямо в задаче, включая готовые константы.
|
||||
|
||||
Коммиты за день, по порядку:
|
||||
Опора: механизм «спецсобытие порождает персонажа в слоте соперника» уже
|
||||
отработан на скелете (`pop_check_skel`), а сама тень как `charid_1_shadow`
|
||||
заведена на уровне 4.
|
||||
|
||||
## 2. Состояние репозитория
|
||||
|
||||
Всё закоммичено, `main` чист. `tests-host`: 5 наборов проходят
|
||||
(`[char] 65`, `[geom] 3144`, `[grab] 53`, `[phys] 1723`, `[wall] 1`).
|
||||
`make size-check`: OK (65 программ, роста нет).
|
||||
|
||||
Коммиты 2026-08-11:
|
||||
|
||||
| хеш | что |
|
||||
|---|---|
|
||||
| `8cac51d` | убраны три последних `/63 %4` в `pop_room.c` |
|
||||
| `72797e1` | инвентаризация ВСЕХ делений по `.asm`, три убраны |
|
||||
| `8175121` | `scr_x` таблицей на весь диапазон, включая отрицательные |
|
||||
| `fc0ede9` | байтовая таблица `x/7` вместо словарной (−1152 Б, на такт быстрее) |
|
||||
| `e261a35` | замер в MAME: A/B со сборкой до правок, делений в горячем пути 0 |
|
||||
| `67a4c71` | `BUG-TORCH-CHOMP-2`: застывший чомпер накрывался пламенем |
|
||||
| `1b2111f` | L4-MIRROR шаги 1-2: зеркало в атласе + постановка тайла |
|
||||
| `844fa6d` | L4-MIRROR шаг 4: прыжок сквозь зеркало и рождение тени |
|
||||
| `8f0362f` | L4-MIRROR шаги 3 и 5 + **левый клип колонок в libbgi** |
|
||||
| `3913f1e` | fore-проход поверх отражения (ноги/голова вылезали из арки) |
|
||||
| `8beb66a` | заведён `FORE-DUP` с разбором |
|
||||
| `a823e7e` | libbgi: блочные AND/OR/XOR акселератора (строками и колонками) |
|
||||
| `860468f` | libbgi: NOT_PUT тоже через акселератор + `docs/shadow_render.md` |
|
||||
| `c0075c7` | NEXT_SESSION: op-блиты сделаны, вид тени отложен |
|
||||
|
||||
## 2. Состояние окружения
|
||||
Накануне (2026-08-10) закрыт `L4-MIRROR`: шаги 1-5 + фикс fore-прохода над
|
||||
отражением; протокол — [`TASKS_CLOSED.md`](TASKS_CLOSED.md#l4-mirror).
|
||||
|
||||
- **MAME запущена** с образом уровня 4 (`FIRST_LEVEL=4`, `PROF_BORDER=1`).
|
||||
Пересобрать образ: `make PROF_FLAGS="-DPROF_BORDER=1 -DFIRST_LEVEL=4" hdd`,
|
||||
после этого MAME **обязан** полный рестарт (memory
|
||||
`mame_hdd_rebuild_restart`). Загрузка: `keyseq d:{ENTER}`, потом
|
||||
`keyseq roomtest{ENTER}`, ждать ~18 с.
|
||||
- **SDLPoP пересобран с отладочной информацией** (`-O0 -g3`). pkg-config на
|
||||
машине НЕТ, собирать так:
|
||||
```
|
||||
cd applications/PoP/SDLPoP/src
|
||||
SDLC="-I/opt/homebrew/include -I/opt/homebrew/include/SDL2 -D_THREAD_SAFE"
|
||||
SDLL="-L/opt/homebrew/lib -lSDL2main -lSDL2 -Wl,-framework,Cocoa \
|
||||
-L/opt/homebrew/Cellar/sdl2_image/2.8.12_1/lib -lSDL2_image"
|
||||
make -j8 CFLAGS="-std=gnu99 -D_DARWIN_C_SOURCE -O0 -g3 $SDLC" LIBS="$SDLL"
|
||||
```
|
||||
`-std=c99` НЕ работает (прячет `strncasecmp` на Darwin), нужен `gnu99`.
|
||||
- **НЕ ПРИБРАНО:** в `SDLPoP/src/seg008.c` мой диагностический `fprintf`
|
||||
с меткой `DBGMIRROR` в начале `add_objtable`. Убрать перед следующей
|
||||
сборкой SDLPoP (или оставить — он гейтится по `obj_type == 1 || == 4`).
|
||||
## 3. Состояние окружения
|
||||
|
||||
## 3. XOR/OR-блит через акселератор — СДЕЛАНО 2026-08-11, тень ОТЛОЖЕНА
|
||||
- **MAME запущена с мостом**, но на её HDD сейчас лежат ТЕСТЫ libbgi
|
||||
(`accop.exe`, `bgiimg.exe`), а не roomtest. Под roomtest пересобрать
|
||||
образ: `make hdd` (или `make PROF_FLAGS="-DFIRST_LEVEL=5" hdd` — при смене
|
||||
`FIRST_LEVEL` нужен `touch roomtest.c`, иначе не переедет), после чего
|
||||
MAME **обязана** пройти полный рестарт (memory `mame_hdd_rebuild_restart`).
|
||||
- **Остановка MAME — `mame/v306/bridge_cmd.sh 'cmd exit'`**, НЕ `kill`
|
||||
(правило пользователя). `pkill -f` бывает не срабатывает, и остаются ДВА
|
||||
инстанса, оба отвечающих на файловый IPC — тогда скриншоты приходят от
|
||||
чужой машины. Проверять `pgrep -f mame.arm` после остановки.
|
||||
- Прогон без MCP-инструментов: `bridge_cmd.sh 'cont'` → sleep ~45 с (бут
|
||||
DSS) → `bridge_cmd.sh 'keyseq d:{ENTER}'` → `keyseq <имя>{ENTER}` →
|
||||
`snap /tmp/mame_snap/x.png`. **Имя .exe на образе — не длиннее 8 символов
|
||||
(FAT 8.3) и БЕЗ подчёркивания** (`keyseq` не умеет `_`).
|
||||
- **SDLPoP собран с отладочной информацией** (`-O0 -g3`); pkg-config на
|
||||
машине нет, команда сборки — в истории этого файла (`git log -p`).
|
||||
- **НЕ ПРИБРАНО:** в `SDLPoP/src/seg008.c` диагностический `fprintf` с меткой
|
||||
`DBGMIRROR` в начале `add_objtable` (гейтится по `obj_type == 1 || == 4`).
|
||||
|
||||
**Библиотечная часть закрыта.** В libbgi поднят полный набор блочных
|
||||
операций акселератора — AND/OR/XOR/NOT, строками и колонками:
|
||||
`gfx_blit_op` / `gfx_blit_part_op`, `gfx_blit_cols_op` /
|
||||
`gfx_blit_cols_part_wx_op` (клип-окно и флип — как у копирующих близнецов);
|
||||
опкод операции патчится SMC, одна функция на все операции. `putimage`
|
||||
лишился попиксельного пути целиком (закрыт пункт 2d-1 `docs/TODO.md`).
|
||||
Регресс — `tests/accop`, 10/10 PASS в MAME, побайтно; `tests/bgi_img`
|
||||
получил две байтовые самопроверки. Механика и три ловушки — memory
|
||||
`accel_block_ops`.
|
||||
## 4. Что сделано в libbgi и чем это полезно дальше
|
||||
|
||||
**Вид тени (два блиттера OR+XOR) отложен решением пользователя** до того,
|
||||
как будут сделаны все уровни: пока тень рисуется обычной копией из атласов
|
||||
Кида. Причина не в примитивах — XOR несовместим с нашей прозрачностью
|
||||
`#FF`, а операция читает ОЗУ-копию экрана, из-за чего два прохода
|
||||
оригинала вырождаются в один XOR. Всё выясненное, замеры и четыре
|
||||
варианта — [`../docs/shadow_render.md`](../docs/shadow_render.md). Ключ к
|
||||
выбору варианта — список кадров, которыми тень реально пользуется
|
||||
(ожидание: бег, длинный прыжок из зеркала, питьё зелья, боёвка).
|
||||
Блочные операции акселератора — полный набор AND/OR/XOR/NOT, строками и
|
||||
колонками: `gfx_blit_op` / `gfx_blit_part_op`, `gfx_blit_cols_op` /
|
||||
`gfx_blit_cols_part_wx_op` (клип-окно и флип — как у копирующих близнецов),
|
||||
опкод патчится SMC. `putimage` лишился попиксельного пути целиком (закрыт
|
||||
пункт 2d-1 `docs/TODO.md`). Регресс — `tests/accop` (10/10 PASS в MAME,
|
||||
побайтно). Механика и три ловушки — memory `accel_block_ops`; главные:
|
||||
операция читает ОЗУ-КОПИЮ экрана (не VRAM), а прозрачность `#FF` совместима
|
||||
с AND/OR, но не с XOR.
|
||||
|
||||
Ниже — исходная постановка задачи, оставлена как справка.
|
||||
**Вид тени (OR+XOR) отложен** до готовности всех уровней — разбор, замеры и
|
||||
варианты в [`../docs/shadow_render.md`](../docs/shadow_render.md).
|
||||
|
||||
**Что установлено (замером, не гипотезой).** Тень уровня 4 в оригинале
|
||||
рисуется ДВУМЯ блитами одного и того же спрайта Кида (seg008:1602):
|
||||
## 5. Остальное открытое
|
||||
|
||||
```c
|
||||
case 1: // shadow
|
||||
add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl, obj_y, blitters_2_or, 1);
|
||||
add_midtable(obj_chtab, obj_id + 1, obj_xh, obj_xl + 1, obj_y, blitters_3_xor, 1);
|
||||
```
|
||||
|
||||
OR на месте, XOR со сдвигом на пиксель вправо — XOR гасит совпавшее, остаются
|
||||
края, отсюда «контурный» вид. Это ЗАМЫСЕЛ оригинала, не артефакт SDLPoP.
|
||||
|
||||
Подтверждено печатью из живого SDLPoP (`DBGMIRROR`): и тень, и отражение идут
|
||||
из `chtab=2` (собственные спрайты Кида), `swordbits=0`, обычными кадрами:
|
||||
|
||||
```
|
||||
type=4 chtab=2 img=40 dir=0 clipL=137 clipT=3 charid=0 frame=41 <- отражение
|
||||
type=1 chtab=2 img=41 dir=0 clipL=137 clipT=3 charid=1 frame=42 <- тень
|
||||
```
|
||||
|
||||
Различие между ними — ТОЛЬКО блиттер. Наша тень сейчас рисуется обычной
|
||||
прозрачной копией, то есть выглядит вторым Кидом.
|
||||
|
||||
**Механизм на Sprinter** (`docs/part2/accelerator_doc.txt`, memory
|
||||
`sprinter_accelerator` дополнена сегодня). Акселератор умеет блочные
|
||||
AND/OR/XOR; операцию задаёт ОПКОД CPU между триггерами:
|
||||
|
||||
```asm
|
||||
LD A,(DE) ; триггер чтения: блок из спрайта -> память акселератора
|
||||
XOR (HL) ; триггер операции: блок XOR с тем, что по адресу приёмника
|
||||
LD (HL),A ; триггер записи: результат обратно
|
||||
```
|
||||
|
||||
Цена — «число байт / 7 МГц», попиксельного цикла CPU НЕТ. **Операция
|
||||
ортогональна направлению**: горизонтальный/вертикальный режим (`LD L,L` /
|
||||
`LD A,A`) выбирается отдельно и на операцию не влияет — то есть с нашими
|
||||
column-major спрайтами ([[accel_vertical_copy]]) это работает так же, как
|
||||
копия. Мнемоника: `XOR (HL)` даёт `A = A ^ (HL)`; «xor (hl),a» на Z80 нет.
|
||||
|
||||
### План
|
||||
|
||||
1. **Эксперимент в MAME на маленьком тесте в `tests/`, НЕ сразу в PoP.**
|
||||
Примитив трогает ассемблерное ядро libbgi, проверять его надо в изоляции.
|
||||
Цель: убедиться, что связка read-триггер / `XOR` / запись даёт ожидаемый
|
||||
блок в вертикальном режиме.
|
||||
2. **libbgi: `_bgi_blit_cols_op_raw`** — клон `_bgi_blit_cols_raw` (asm), где
|
||||
write-триггер `LD (DE),A` заменён парой «`XOR (dst)` + `LD (dst),A`».
|
||||
Наружу — `gfx_blit_cols_part_op(...)` с параметром операции
|
||||
(COPY / OR / XOR), чтобы одним примитивом закрыть оба блиттера тени.
|
||||
Побочно закрывается давний пункт `2d-1` из `docs/TODO.md`
|
||||
(`putimage` с `XOR/OR/AND_PUT` до сих пор на попиксельном пути).
|
||||
После правки libbgi — `make size-check` ОБЯЗАТЕЛЕН.
|
||||
3. **PoP:** тень двумя блитами, OR на месте + XOR со сдвигом `+1` по X.
|
||||
Место — ветка слота соперника в `pop_char_draw` (`pop_cdraw.c`), где уже
|
||||
стоит выбор атласа и клип тени по `CHARID_1_SHADOW`.
|
||||
4. **Смотреть на палитру глазами.** Тут предсказать нельзя: акселератор
|
||||
XOR-ит ИНДЕКСЫ, а SDLPoP делает XOR в 24-битном RGB (`blit_xor`,
|
||||
seg009:3190). DOS-оригинал (режим 13h) тоже XOR-ил индексы, то есть мы
|
||||
будем БЛИЖЕ к DOS, чем SDLPoP, но конкретные цвета контура определит
|
||||
раскладка нашей палитры (атласы перепакованы `pop_pack_kid.py`).
|
||||
Может выйти и лучше, и мусорнее — это надо увидеть.
|
||||
|
||||
## 4. Остальное открытое
|
||||
|
||||
- **`FORE-DUP`** (`bug_list.md`) — передний слой тайла рисуется дважды при
|
||||
перекрытии объектов (Кид+отражение всегда, Кид+соперник — весь ближний
|
||||
бой). Картинку не портит, тратит такты. В оригинале невозможно: там
|
||||
`redraw_at_char` только ПОМЕЧАЕТ тайлы. **Сначала замерить, потом чинить**
|
||||
— окно клипа вместо перебора тайлов в своё время дало 3.2×.
|
||||
- **`TORCH-ANIM-RIGHT`** (`bug_list.md`) — под запечённым пламенем могут
|
||||
застыть не только челюсти чомпера, но и пики/меч/зелье справа от факела.
|
||||
На уровнях 1-4 такого соседства нет.
|
||||
- **`DIED-ON-BUTTON`** — не портирован `died_on_button` (seg007:776).
|
||||
- **[FORE-DUP](BUGS_OPEN.md#fore-dup)** — передний слой тайла рисуется дважды
|
||||
при перекрытии объектов (Кид+отражение всегда, Кид+соперник — весь ближний
|
||||
бой). Картинку не портит, тратит такты. **Сначала замерить, потом
|
||||
чинить** — окно клипа в своё время дало 3.2×.
|
||||
- **[MIRROR-FG-STALE](BUGS_OPEN.md#mirror-fg-stale)** — зеркало, поставленное
|
||||
пока игрок В комнате 4, не попадает в снимок `room_fg` и потому невидимо
|
||||
для коллизии. В обычном прохождении недостижимо; фикс на несколько строк,
|
||||
делать вместе со следующей правкой `pop_trob.c`.
|
||||
- **[DIED-ON-BUTTON](BUGS_OPEN.md#died-on-button)** — `died_on_button`
|
||||
(seg007:776) не портирован.
|
||||
- **TORCH-ANIM-RIGHT** — под запечённым пламенем могут застыть пики/меч/зелье
|
||||
справа от факела; на уровнях 1-4 такого соседства нет.
|
||||
- **[GUARD-PHYS](TASKS_OPEN.md#guard-phys)** — остаток: ветка тени в
|
||||
`check_guard_fallout` (уходит в L5-SHADOW) и живая проверка стража,
|
||||
нажимающего напольную кнопку.
|
||||
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1` (оба низкий приоритет),
|
||||
`L3-PASS`, `L3-COLOR`, `L1-SPEED`, `TUNE-1`.
|
||||
- **Не проверено в MAME** из вчерашнего: отражение с fore-проходом и клип
|
||||
тени слева (собрано и залито, но живьём не смотрели).
|
||||
`L3-COLOR`, `L1-SPEED` (игра на ~39 % быстрее оригинала), `TUNE-1`.
|
||||
|
||||
## 5. Грабли, собранные сегодня
|
||||
## 6. Грабли, собранные 2026-08-10/11
|
||||
|
||||
- **Не оценивать железо по своей же memory-заметке.** Я заявил, что accel
|
||||
- **Не оценивать железо по своей же memory-заметке.** Заявил, что accel
|
||||
умеет только копирование и XOR потребует ~25 % кадра на CPU — неверно,
|
||||
поправил пользователь. Заметка описывала копирование, я принял её
|
||||
неполноту за свойство железа.
|
||||
неполноту за свойство железа. Проверять по драйверу MAME
|
||||
(`mame/sources/MAME/src/mame/sinclair/sprinter.cpp`) и `docs/new/06-accel.md`.
|
||||
- **lldb через FIFO — плохая идея.** Повторяющиеся `-o` при
|
||||
`breakpoint command add` записываются НЕПОЛНЫМИ (берётся последний), а
|
||||
оставшийся от неудачной попытки `script print(... lldb.frame ...)` уронил
|
||||
lldb прямо в обработчике точки останова. Три прыжка пользователя ушли
|
||||
впустую. **Работает надёжно:** добавить `fprintf(stderr, ...)` прямо в
|
||||
SDLPoP, пересобрать (он собирается за секунды) и читать stdout. В дереве
|
||||
уже есть такие метки (`DBG kidobj`).
|
||||
`breakpoint command add` записываются НЕПОЛНЫМИ. **Работает надёжно:**
|
||||
`fprintf(stderr, ...)` прямо в SDLPoP, пересборка (секунды) и чтение stdout.
|
||||
- **`make` без `hdd` не обновляет образ MAME**, а `FIRST_LEVEL` живёт в
|
||||
`roomtest.c` — при смене нужен `touch roomtest.c`. Я дважды сказал
|
||||
«образ пересобран», когда он не был.
|
||||
- Диапазон `obj_x` = **−416..695** (посчитан из `kid_data.bin`: `dx` кадров
|
||||
Кида −5..+10, стража −2..+10, плюс `render_dx ∈ {−140,0,+140}`).
|
||||
Пригодится всякий раз, когда нужна таблица по экранной X.
|
||||
`roomtest.c` — при смене нужен `touch roomtest.c`.
|
||||
- **Доска отставала от кода на три задачи** (`L3-CHOMP`, `L3-SKEL`, шаги 3/5
|
||||
`L4-MIRROR` значились несделанными). Перед планированием — сверять
|
||||
`TASKS_OPEN.md` с кодом грепом, а не верить записи.
|
||||
- Диапазон `obj_x` = **−416..695** (посчитан из `kid_data.bin`). Пригодится
|
||||
всякий раз, когда нужна таблица по экранной X.
|
||||
|
||||
@@ -52,6 +52,6 @@ HP и зелья, чекпойнт уровня 3.
|
||||
|
||||
**Что берём в работу сейчас — [`TASKS_OPEN.md`](TASKS_OPEN.md)** (сделанное с
|
||||
протоколами замеров — [`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/levels_plan.md`](../docs/levels_plan.md).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# roomtest — ЗАКРЫТЫЕ задачи (архив досок, обновлено 2026-08-08)
|
||||
# roomtest — ЗАКРЫТЫЕ задачи (архив досок, обновлено 2026-08-11)
|
||||
|
||||
Сделанное — с протоколами замеров, граблями и причинами решений. Файл
|
||||
существует не ради истории: половина записей ниже — это ЧИСЛА (сколько тактов
|
||||
@@ -6,13 +6,27 @@
|
||||
перечень того, что делать НЕЛЬЗЯ, потому что уже пробовали.
|
||||
|
||||
Открытые задачи — [`TASKS_OPEN.md`](TASKS_OPEN.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).
|
||||
|
||||
---
|
||||
|
||||
## Приёмка уровней
|
||||
|
||||
<a id="pass-policy"></a>
|
||||
### ПОЛИТИКА ПРИЁМОК — решение пользователя 2026-08-11
|
||||
|
||||
**Уровни 1-4: smoke-тесты пройдены.** Полные прогоны (обход всех комнат
|
||||
каждого уровня) делаются **по готовности ВСЕХ уровней**, а не по одному за
|
||||
этапом — отдельных задач `L3-PASS`/`L4-PASS` больше нет.
|
||||
|
||||
Основание: сквозные обходы дорогие, а половина находок на неполном наборе
|
||||
уровней всё равно оказывается «механики ещё нет». Smoke (пройти уровень от
|
||||
старта до двери) остаётся обязательным на каждом новом уровне — он снимает
|
||||
блокеры, а не косметику.
|
||||
|
||||
Уже сделанные полные обходы уровней 1 и 2 (ниже) остаются регресс-базой.
|
||||
|
||||
<a id="l1-pass"></a>
|
||||
### L1-PASS. Сквозное прохождение уровня 1 — **ЗАКРЫТ 2026-08-07**
|
||||
|
||||
@@ -23,30 +37,30 @@
|
||||
> регресс-базой для уровня 2.
|
||||
>
|
||||
> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.** Этим
|
||||
> же прогоном закрыта [таблица обхода 24 комнат](bug_closed.md#обход-всех-24-комнат-уровня-1)
|
||||
> и [чек-листы ручной перепроверки фиксов](bug_closed.md#ручная-перепроверка-2026-08-03)
|
||||
> — обе уехали в `bug_closed.md`.
|
||||
> же прогоном закрыта [таблица обхода 24 комнат](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1)
|
||||
> и [чек-листы ручной перепроверки фиксов](BUGS_CLOSED.md#ручная-перепроверка-2026-08-03)
|
||||
> — обе уехали в `BUGS_CLOSED.md`.
|
||||
|
||||
Приёмка этапа 1 и одновременно регресс-база для уровня 2: от старта до двери
|
||||
уровня одним заходом — подбор меча, страж, кнопки/ворота, пики, loose-полы,
|
||||
зелье, падения. Точки, где смотрели внимательно, — закрытая косметика
|
||||
окклюзии (потолок при прыжке вверх, шов при анимации решётки, грани дальней
|
||||
колонны) и подъём на тайл-кнопку (оговорка к BUG-3 в `bug_closed.md`).
|
||||
колонны) и подъём на тайл-кнопку (оговорка к BUG-3 в `BUGS_CLOSED.md`).
|
||||
|
||||
<a id="l2-pass"></a>
|
||||
### L2-PASS. Приёмка уровня 2 — **ЗАКРЫТ 2026-08-07**
|
||||
|
||||
> **Smoke 2026-08-05 (пользователь): уровень 2 пройден.** Из smoke пришли
|
||||
> BUG-LOOSE-3 и BUG-GUARD-DEAF-1 — оба закрыты (`bug_closed.md`). Следом
|
||||
> BUG-LOOSE-3 и BUG-GUARD-DEAF-1 — оба закрыты (`BUGS_CLOSED.md`). Следом
|
||||
> прогнан smoke уровня 3.
|
||||
>
|
||||
> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.**
|
||||
> Открытыми с этого прогона остались три записи в
|
||||
> [`bug_list.md`](bug_list.md): [BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1)
|
||||
> [`BUGS_OPEN.md`](BUGS_OPEN.md): [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)
|
||||
> (страж всегда одного цвета), BUG-GUARD-SPLASH-1 (нет брызг при попадании по
|
||||
> стражу — **закрыт 2026-08-07**, разбор в
|
||||
> [`bug_closed.md`](bug_closed.md#bug-guard-splash-1)) и
|
||||
> [BUG-CHEAT-FIGHT-1](bug_list.md#bug-cheat-fight-1) (наш чит `+`/`−` в бою
|
||||
> [`BUGS_CLOSED.md`](BUGS_CLOSED.md#bug-guard-splash-1)) и
|
||||
> [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1) (наш чит `+`/`−` в бою
|
||||
> отнимает управление). Ни одна играть не мешает.
|
||||
|
||||
Ниже — **карта содержимого уровня, снятая прямо с `res2002.bin`**. Она
|
||||
@@ -56,7 +70,7 @@
|
||||
|
||||
**Стражи — 5, в комнатах 4, 7, 11, 15, 24** (skill 1/2/1/1/3, цвета
|
||||
1/3/1/1/6 — цвет мы пока игнорируем, атлас один, см.
|
||||
[BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1)). Заметить: страж
|
||||
[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)). Заметить: страж
|
||||
комнаты 24 со skill 3 — первый по-настоящему опасный.
|
||||
|
||||
**Кнопки и что они открывают** (декодировано из LINKLOC/LINKMAP):
|
||||
@@ -100,8 +114,135 @@
|
||||
|
||||
---
|
||||
|
||||
### <a id="rooms-graph"></a>Справка: связность комнат уровней 1–3 (снято 2026-08-05)
|
||||
|
||||
Обход графа `roomlinks` (@1952, по 4 байта на комнату: L, R, U, D; 0 = нет
|
||||
соседа) от стартовой комнаты — тем же методом, которым на уровне 1 нашлись
|
||||
13/18/24 (см. «НЕ БАГИ» в [`BUGS_CLOSED.md`](BUGS_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-11**
|
||||
|
||||
Пять шагов из шести сделаны и **проверены пользователем в MAME 2026-08-11**;
|
||||
шестой (вид тени) осознанно отложен.
|
||||
|
||||
| шаг | что | коммит |
|
||||
|---|---|---|
|
||||
| 1 | зеркало в атласе: `tile_table[0x0D]` база 75 / фронт 77, `MIRROR_ENV_IDS` + `FORE_ENV_IDS` в `pop_pack_bg.py` | `1b2111f` |
|
||||
| 2 | постановка тайла: `place_mirror` в `pop_trob.c` по переходу `pop_leveldoor_open` 0/2 → 1 (`animate_leveldoor`, seg007:0457) | `1b2111f` |
|
||||
| 3 | отражение (`check_mirror`, seg003:0798) — отдельной функцией `pop_mirror_draw`, со своим heal | `8f0362f`, `3913f1e` |
|
||||
| 4 | прыжок сквозь зеркало и рождение тени (seg004:0239 + seg003:0798..08A9 + seg002:081D/1131) | `844fa6d` |
|
||||
| 5 | клип тени слева `obj_clip_left = 137 + (mirror_column−4)*32` (seg008:1699) + новый примитив `gfx_blit_cols_part_wx` в libbgi | `8f0362f` |
|
||||
|
||||
**Почему зеркало пришлось добавлять в атлас явно:** тайла 13 нет ни в одном
|
||||
уровне статически — проверено перебором всех 15 `res200N.bin`, ноль
|
||||
попаданий, поэтому `render_room.py` его не видит и на месте зеркала был бы
|
||||
чёрный провал (memory `pop_atlas_dynamic_ids`).
|
||||
|
||||
**Почему отражение — отдельная функция, а не третий слот `Char`:** это
|
||||
структура самого оригинала — отражение идёт сокращённым путём
|
||||
`load_frame_to_obj` + `add_objtable(4)`, без клинка, брызг, пропуска кадра;
|
||||
гейтить всё это в общем теле `pop_cdraw` значило бы добавить ветки в самый
|
||||
горячий путь. Побочный эффект — [FORE-DUP](BUGS_OPEN.md#fore-dup): передний
|
||||
слой тайла рисуется дважды, когда футпринты Кида и отражения накрывают один
|
||||
тайл (всегда, они стоят на одном тайле). Картинку не портит, тратит такты.
|
||||
|
||||
**Почему левый клип сделан примитивом libbgi, а не «нарисовать и вернуть фон
|
||||
поверх лишнего»** (подсказка пользователя): для column-major левая обрезка
|
||||
стоит ровно столько же, сколько правая — колонка это непрерывный кусок ОЗУ,
|
||||
меняются стартовая колонка источника и экранная X. Внутри это уже было (так
|
||||
клипается левый край экрана), наружу не было выведено.
|
||||
|
||||
**Отложено — вид тени** (шаг 6): в оригинале она рисуется ДВУМЯ блитами
|
||||
одного спрайта, `blitters_2_or` на месте и `blitters_3_xor` со сдвигом +1 px
|
||||
(seg008:1602); у нас пока обычная копия, то есть тень выглядит вторым Кидом.
|
||||
Разбор, замеры и варианты — [`../docs/shadow_render.md`](../docs/shadow_render.md);
|
||||
блочные операции акселератора под это в libbgi уже есть (`tests/accop`).
|
||||
|
||||
**Открытый краевой случай** — [MIRROR-FG-STALE](BUGS_OPEN.md#mirror-fg-stale):
|
||||
`place_mirror` пишет тайл в данные уровня, но не в снимок комнаты `room_fg`,
|
||||
по которому работает коллизия.
|
||||
|
||||
<a id="l3-chomp"></a>
|
||||
### L3-CHOMP. Чомперы — **СДЕЛАНЫ 2026-08-08**
|
||||
|
||||
Коммиты `dc0bd47` (анимация, отрисовка, смерть в челюстях), `4d4323f`
|
||||
(передние зубья через `pop_fore_b` + ветка в `draw_tile`), `db4106a`
|
||||
(перед чомпером Кид разбегается сразу, без осторожного шага — `safe_step` по
|
||||
оригиналу), `1461ed5` (фикс регрессии, см. ниже).
|
||||
|
||||
Портировано: `animate_chomper` (seg007:0448), `next_chomper_timing`
|
||||
(seg007:0F9A — 15,12,9,6,13,10,7,14,11,8 по кругу), `start_anim_chomper`
|
||||
(seg007:08C7), `start_chompers` (seg007:0F13) — все в `pop_trob.c`,
|
||||
состояние в `room_modif`, как у пик и ворот. Смерть: `SEQ_54_CHOMPED` /
|
||||
`FRAME_178_CHOMPED` + `check_chomped_guard` для соперника (`pop_map.c`).
|
||||
Отрисовка — `pop_chomp_pose` (`pop_bg.c`) и холодная перерисовка тайла в
|
||||
`pop_room.c`. Ассеты упакованы явным набором кадров (`CHOMPER_BOT_IDS`
|
||||
101-105, `TOP` 111-113, `FORE` 106-110 + кровь 114-123 mono-силуэтом).
|
||||
|
||||
**Грабли, стоившие регрессии (`1461ed5`):** `start_chompers` вызывается на
|
||||
смене ряда персонажа, а у нас `seqtbl` читается ЧЕРЕЗ ОКНО W0 — прямой вызов
|
||||
из `play_seq` переключал окно посреди чтения байткода. Вызов отложен через
|
||||
флаг `chomp_pending` (`pop_kid.c`) и делается ПОСЛЕ цикла интерпретатора.
|
||||
|
||||
Хвосты в багах: [BUG-CHOMP-JUMP-1](BUGS_OPEN.md#bug-chomp-jump-1) (низкий,
|
||||
маловоспроизводим) и закрытые BUG-TORCH-CHOMP-1/2 (пламя факела и застывший
|
||||
чомпер — [`BUGS_CLOSED.md`](BUGS_CLOSED.md)).
|
||||
|
||||
<a id="l3-skel"></a>
|
||||
### L3-SKEL. Скелет уровня 3 — **СДЕЛАН 2026-08-07, принят smoke-прогоном**
|
||||
|
||||
В данных уровня 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) — декорация, они не
|
||||
оживают. Атлас — `pop_pack_guard.py SKEL` → `poc/res/skel/g0..g3.atl`.
|
||||
|
||||
Проверено в MAME: бой, падение в пропасть, окклюзия (2026-08-07); принят
|
||||
smoke-прогоном уровней 1-4 (2026-08-11). **Этот же механизм** —
|
||||
«спецсобытие порождает персонажа в слоте соперника» — база для тени уровня 5
|
||||
([L5-SHADOW](TASKS_OPEN.md#l5-shadow)).
|
||||
|
||||
<a id="l3-chkp"></a>
|
||||
### L3-CHKP. Чекпойнт уровня 3 — **СДЕЛАНО 2026-08-06** (коммит 0cd6b2d)
|
||||
|
||||
@@ -119,7 +260,7 @@ SDLPoP: смерть В комнате 7 вернула его в стартов
|
||||
**Проверено в MAME:** вход в 7 флаг не ставит, уход влево — ставит; респавн в
|
||||
комнате 2; после обычной смерти (без чекпойнта) плиты восстанавливаются как
|
||||
раньше. Вживую в игре (не читом) — потрогать на приёмке уровня 3
|
||||
([L3-PASS](TASKS_OPEN.md#l3-pass)).
|
||||
([L3-PASS](TASKS_CLOSED.md#pass-policy)).
|
||||
|
||||
### L2. Переход на уровень 2 и его игра — **МАШИНЕРИЯ СДЕЛАНА 2026-08-04**
|
||||
|
||||
@@ -163,7 +304,7 @@ Shift+L → уровень 3 (комната 9). То есть цепочка
|
||||
|
||||
Контент уровня 2 (сквозное прохождение, выход через дверь) закрыт отдельно —
|
||||
[L2-PASS](#l2-pass). Цвет стража из данных остался открытым багом —
|
||||
[BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1).
|
||||
[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1).
|
||||
|
||||
### L1-START. Старт по данным уровня — **СДЕЛАНО 2026-08-01**
|
||||
|
||||
@@ -203,30 +344,30 @@ Shift+L → уровень 3 (комната 9). То есть цепочка
|
||||
✅ Косметика тоже закрыта: BUG-DOOR-CLIP (обрезка силуэта правым косяком
|
||||
проёма) — недоставало второй половины `clip_char` (`obj_clip_right`) и
|
||||
обрезки СПРАВА у колоночного блита. В libbgi добавлен
|
||||
`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`bug_closed.md`](bug_closed.md).
|
||||
`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md).
|
||||
|
||||
### L1-TRIAGE. Ревизия багов — **ЗАКРЫТА** (часть 1 — 2026-08-01, хвост — 2026-08-07)
|
||||
|
||||
✅ **Три Critical'а прогнаны в MAME и закрыты** (протокол с числами — в
|
||||
[`bug_closed.md`](bug_closed.md), раздел «Проверено в MAME 2026-08-01»):
|
||||
[`BUGS_CLOSED.md`](BUGS_CLOSED.md), раздел «Проверено в MAME 2026-08-01»):
|
||||
BUG-1 (провал на row 1 при переходе через открытые ворота) и BUG-2
|
||||
(ping-pong при возврате) **не воспроизводятся**, BUG-3 (окклюзия climb-up на
|
||||
кнопке) закрыт фиксом `tile_code_drawn` от 2026-07-28. Заодно снят неверный
|
||||
диагноз BUG-1: репроекция Y при БОКОВОМ переходе — не наш пробел, а точное
|
||||
поведение `goto_other_room` (`SDLPoP/src/seg002.c:390` меняет только `x`).
|
||||
Список разделён на [`bug_list.md`](bug_list.md) (открытое) и
|
||||
[`bug_closed.md`](bug_closed.md) (закрытое + разбор корней).
|
||||
Список разделён на [`BUGS_OPEN.md`](BUGS_OPEN.md) (открытое) и
|
||||
[`BUGS_CLOSED.md`](BUGS_CLOSED.md) (закрытое + разбор корней).
|
||||
|
||||
✅ **Косметика окклюзии тоже закрыта** — BUG-CEIL-1/2/3 и BUG-OCCL-1 были
|
||||
починены кодом ещё в июле, а записи никто не снял: `ceil_over_kid_tile`
|
||||
(`pop_bg.c:1275`), `pop_ceil_modif` + `pop_ceil_shake_draw`/`_bake_empty`
|
||||
(`:547,815,827`), `bar` с `POP_YOFF+3` в `pop_room_redraw_seam_left` (`:793`),
|
||||
разделение слоёв по `add_backtable` vs `ptr_add_table` в `overlay_mid_tile`
|
||||
(`:1367`). Разбор — в [`bug_closed.md`](bug_closed.md).
|
||||
(`:1367`). Разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md).
|
||||
|
||||
✅ **Хвост закрыт 2026-08-07:** таблица обхода 24 комнат уровня 1 — прогоном
|
||||
всех комнат уровней 1 и 2 (крупных багов нет), см.
|
||||
[`bug_closed.md`](bug_closed.md#обход-всех-24-комнат-уровня-1).
|
||||
[`BUGS_CLOSED.md`](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1).
|
||||
|
||||
---
|
||||
|
||||
@@ -301,7 +442,7 @@ BANK4 pop_cdraw 4 625 5 905 +1 280 (сюда переехала отрис
|
||||
То есть слияние двух копий дало ~3.2 КБ, но **главный выигрыш не в банке 2,
|
||||
а в резиденте**: отрисовка Кида уехала из W1 в банк, и куча выросла втрое
|
||||
(2 023 → 6 333 Б). Банку 2 при этом досталось всего +265 Б свободного
|
||||
места — **под [L3-CHOMP](TASKS_OPEN.md#l3-chomp) этого мало**, разгрузку
|
||||
места — **под [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) этого мало**, разгрузку
|
||||
`pop_bg` придётся делать отдельно (свободные номера банков — 7+).
|
||||
|
||||
**Проверено:** `tests-host` — все 5 наборов зелёные, трассы физики не
|
||||
@@ -615,7 +756,7 @@ make ALLOCS=100000 — как раньше: минимальный код, д
|
||||
> переполняется» отпадает; сам ФИКС (плотный опрос `kbd_raw_poll`) остаётся
|
||||
> верным и нужным — переполнение вызывает не Shift, а короткая жизнь
|
||||
> импульса IRQ (пункт 3 гипотезы) плюс DI-окна графики. Разбор и следствия
|
||||
> — [BUG-KBD-5](bug_closed.md).
|
||||
> — [BUG-KBD-5](BUGS_CLOSED.md).
|
||||
>
|
||||
> **Итог (2026-08-01).** Причина — не наш код и не DI-окна графики: импульс
|
||||
> запроса прерывания здесь теряется примерно в 44 % случаев, и трёхбайтовый
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
Активные баги на уровне 1 -
|
||||
|
||||
- После того как кувшин со здоровьем выпит и Кид попадает в комнату во второй раз
|
||||
то иногда кувшин мы не показываем (это правильно) но показываем анимацию пузырьков над
|
||||
кувшином (чего показывать больше не должны - нет кувшина, нет пузырьков),
|
||||
а в комнате 14 пузырек вообще восстанавливается полностью после того как Кид
|
||||
выйдет из комнаты и вернется в нее обратно (есть ощущение что даже в одной комнате
|
||||
поведение пузырька - будет ли он восстановлен или нет зависит от чего-то непонятного.
|
||||
(есть еще ощущение что первый выпитый кувшин не восстанавливается а остальные
|
||||
восстанавливаются)
|
||||
- Меч - один раз подобранный он больше не должен появляться в своей (15-ой) комнате
|
||||
иногда он появляется снова, иногда он больше не появляется но на дальней стене начинают
|
||||
моргать контуры кирпичной кладки (странно что она не впечатана в heal уровень потому
|
||||
что она должна быть отрисована всегда). причем мерцает по странному - не как double-buff
|
||||
а как буд-то вместо искры на мече - изредка на несколько (один логический?) кадров.
|
||||
- комната 12 - опять проблема с телепортацией через решетку - решетка в 0,9 - Кид стоит
|
||||
за шаг (пол-шага) от решетки - бежит и пробегает через закрытую решетку.
|
||||
- после respawn - остаются разбитыми плиты что были разбиты, выпитыми пузырьки и прочее -
|
||||
это надо сравнить с оригиналом (SDLPoP) - но главное другое - если до respawn какая-то
|
||||
плита не была разбита то теперь если ее разбить, выйти из комнаты и вернуться - то
|
||||
плита восстановится (еще интереснее вариант когда плита-потолок в комнате 12 разбивается,
|
||||
Кид через проем поднимается в верхнюю комнату и поднявшись плита в новой комнате опять
|
||||
закрывает проем.
|
||||
- проблема с плитой из комнаты 12 в верхнюю и пузырек в комнате 22 - они возникают даже
|
||||
без respawn - когда Кид заходит в комнату 22 из комнаты 16, выпивает пузырек, выходит опять
|
||||
в комнату 16 и опять возвращается в комнату 22 - пузырек возникает снова. Когда Кид разбивает
|
||||
плиту из комнаты 12 в комнату 16 то после подъема в комнату 16 плита опять возникает.
|
||||
аналогично комната 17 - если войти в нее из комнаты 23, разбить плиты 1,5 и 1,6, опуститься на
|
||||
2,3 и выпить пузырек. вернуться в комнату 23 и опять в 17 - то и плиты вернутся и пузырек.
|
||||
но могут плита и пузырек не вернуться - такое ощущение что если это первая разбитая плита
|
||||
(Кида мы перенесли в комнату 12 через cheats) - то разбитая плита остается разбитой.
|
||||
- комната 6 - Кид бежит по уровню ноль справа налево (начало бега 0,9), делает длинный
|
||||
прыжок между 0,6 и 0,7 - он пролетает во внутрь стены и респавнится - скриншоты в bugscreens
|
||||
- иногда залипают все-таки клавиши (пока замечено на кнопке вверх - отлипает только после
|
||||
следующего нажатия). надо проверить - есть ли возможность не только отслеживать события
|
||||
нажатие/отжатие но и текущий статус нажатых кнопок. Может вообще отказаться от прямой работы
|
||||
с клавиатурой а работать с нею через BIOS ?
|
||||
- иногда таки залипает и shift =((((
|
||||
- комната 5 - кнопка на 0,4 не открывает решетку/ворота на 0,5 - это двойная кнопка - она
|
||||
должна сразу открывать ворота на 0,5 и на 0,9 (возможно у нас она работает только на 0,9)
|
||||
возможно что логически решетка на 0,5 открывается но анимации ее открытия нету =(
|
||||
- комната 5 - Кид падает с кнопки 0,6 на 2,7 - получает урон, присяд в 2,8 - когда встает
|
||||
он проваливается еще ниже в комнату 6 на между 2,7-2,8.
|
||||
- если стражник убивает Кида то Кид респавнится на месте смерти и стражник тут же его убивает
|
||||
еще раз и так по циклу
|
||||
|
||||
--------
|
||||
|
||||
- проверить как отрисовывается Кид вместе с мертвым стражником - в оригинале стражник поверх
|
||||
Кида (Кид проходит за телом стража), у нас Кид поверх (перед) стражем;
|
||||
- сейчас у нас шифт не залипает но теперь он теряет статус нажатости - то есть при нажатом шифте
|
||||
второе-треть-четвертое нажатие стрелки отрабатывает не как осторожный шаг а переводит Кида на бег
|
||||
по идее с новым алгоритмом должно было бы корректно отрабатывать нажатие шифта ? теперь же залипаний
|
||||
нету но есть фантомный отжим ? такое поведение работы с клавиатурой хуже чем залипание шифта (потому
|
||||
что игрок надеется что шифт нажат и будет короткий шаг а Кид начинает бежать и может провалиться в
|
||||
ловушку/яму или набежать на пики и погибнуть). то есть надо как-то более точно проверять статус нажатости
|
||||
шифта.
|
||||
то есть - когда нажат шифт и нажимаются стрелки то вариант редких пропусков стрелок предпочтительнее чем
|
||||
вариант когда мы перестаем понимать что шифт нажат.
|
||||
- после respawn - должны ли оживать стражники ? сейчас они остаются мертвые.
|
||||
- небольшой баг отображения (такой баг у нас уже был и ты его фиксил) - комната 12 - две падающие плиты
|
||||
рядом (0,1 и 0,2) - после падения осколки только от одной на 1,2 а от 0,1 на 1,1 осколков нету.
|
||||
при том что в комнате 7 две плиты 0,5 и 0,6 падая на 2,5 и 2,6 дают двое осколков.
|
||||
возможная причина - когда Кид пробегает в комнате 12 по падающим плитам то он убегает сразу в комнату 15
|
||||
и плита 0,2 успевает упасть и поменять статус пола 1,2 на пол с осколками а плита 0,1 еще остается в падении
|
||||
когда Кид уже уходит в комнату 15 и потому статус пола на 1,1 на 'пол с осколками' измениться не успевает
|
||||
|
||||
--------
|
||||
|
||||
Подумай вот над чем -
|
||||
интересует возможность покрытия уже написаного кода автотестами (тесткейсами).
|
||||
автотесты внутри MAME я думаю будут достаточно затруднительными, но поскольку это у нас plain-C
|
||||
то можно наверное делать автотесты и на host OS.
|
||||
возможно это нам поможет не создавать ситуации когда нам каждый раз приходится фиксить баги которые
|
||||
мы уже фиксили (по крайней мере постараемся избегать регресса при реализации новых фич)
|
||||
я понимаю что это скорее всего будет достаточно сложно совместить с тем что финальный продукт идет
|
||||
для Sprinter. но подумать над этим надо.
|
||||
|
||||
Пока ничего не предпринимай, только подумай что и как можно сделать.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
Баги/недоработки на уровне 2
|
||||
|
||||
- комната 4 - бой со стражем - страж другого цвета/другого цвета у него HP (сравнение с SDLPoP)
|
||||
- комната 4 - бой со стражем - при нанесении урона стражу должен рисоваться сплеш (типа звезды) - что бы игрок
|
||||
видел что удар дошел до цели без перевода внимания на HP (
|
||||
|
||||
- баг нашего cheats - если мы нажимаем +/- (переход по комнатам) в момент когда Кид вытащил меч для битвы -
|
||||
Кид теряет управление (он переключается в режим боя но в новой комнате нету стража - в результате нажатия
|
||||
кнопок движения игнорятся)
|
||||
|
||||
@@ -33,7 +33,7 @@ void pop_map_set_edges(uint8_t left, uint8_t right, uint8_t up, uint8_t down) __
|
||||
* сквозь закрытые ворота шва — BUG-GATE-PASS-1). dcol — на сколько
|
||||
* изменились номера колонок: ушли ВПРАВО −10, ВЛЕВО +10. Звать СРАЗУ после
|
||||
* pop_map_set_edges и только для боковых переходов; вверх/вниз и все прочие
|
||||
* входы в комнату остаются на полной инвалидации. Разбор — bug_closed.md,
|
||||
* входы в комнату остаются на полной инвалидации. Разбор — BUGS_CLOSED.md,
|
||||
* расхождение с дословным портом — ../docs/impl_diff.md. */
|
||||
void pop_coll_shift(int8_t dcol) __banked;
|
||||
|
||||
|
||||
@@ -642,7 +642,7 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
* безусловно, мы — только когда там чомпер. Безусловная
|
||||
* пометка = перерисовка тайла каждый кадр на каждый факел,
|
||||
* а слой anim рисует ещё пики/зелье/меч (seg008:644). Их
|
||||
* случай «застыл справа от факела» в bug_list как открытый:
|
||||
* случай «застыл справа от факела» в BUGS_OPEN.md как открытый:
|
||||
* пики и меч рядом с факелом на уровнях 1-4 не встретились. */
|
||||
if (trob_rcode[i] == TILE_CHOMP)
|
||||
pop_set_redraw((uint8_t)(tp + 1), POP_RD_CHOMP, 1);
|
||||
|
||||
@@ -112,7 +112,7 @@ pop_loose_tick(); // досчёт тряски и снятие провалив
|
||||
потребовалось — карту комнаты в него ИНЪЕКТИРУЮТ через `pop_map_set`, —
|
||||
но набор на сам `pop_level`/`pop_trob` в это упрётся. Решение то же, что
|
||||
для `kid_data`: положить страницу по нужному адресу и поднять `CODE_LOC`.
|
||||
- **Сценарии из `bug_closed.md`.** У каждой закрытой записи есть симптом и
|
||||
- **Сценарии из `BUGS_CLOSED.md`.** У каждой закрытой записи есть симптом и
|
||||
ожидаемое поведение — готовый список регрессионных кейсов, который стоит
|
||||
переносить в `t_phys` по мере надобности.
|
||||
- **BUG-LOOSE-2** (гонка «уйти из комнаты раньше, чем долетит плита») —
|
||||
|
||||
Reference in New Issue
Block a user