Уровень 6: переход на 7-й, портал, многокомнатное падение плиты
Всё проверено пользователем в MAME. 1. Переход 6 -> 7 падением. leave_room (seg002:0504) для «вниз» на уровне 6 из комнаты 1 даёт особый результат −2, главный цикл (seg000:0893) читает его как ++next_level; вход на 7-м (set_start_pos, seg003:0196) ставит Кида в комнату 17 и сразу переводит экран на комнату под ней. Проверка ОБЯЗАНА идти раньше связи вниз: у комнаты 1 сосед снизу есть (шахта, комната 3). 2. BUG-BALCONY-RIGHT: правая половина портала не рисовалась. id 12 стоял в FORE_ENV_IDS (в tile_table он fore_id ЗЕЛЬЯ), но зелье рисуется из chtab_1, а в chtab_6 под этим номером — правая часть арки балкона 32x62, идущая в backtable. Спрайт уезжал в fore-атлас, env-страница получала дырку (idx 12: w=0 h=0). Найдено трассой POP_TRACE_BT + чтением каталога .atl; дифф комнаты с оригиналом 1151 -> 375. 3. BUG-BELOWROW-WALL: жёлтые треугольники в шахте падения. load_rowbelow (seg008:368) при отсутствующей комнате снизу подставляет tiles_0_empty колонкам 1..9 и tiles_20_wall только левому краю; у нас стеной забивались все 11 позиций. 4. BUG-MOB-MULTIROOM: честный mob_down_a_row (seg007:1387) — кусок переходит в комнату снизу и летит дальше. Плита (1,7) комнаты 6 пролетает шахту комнаты 7 и жмёт кнопку (2,7) комнаты 11, открывая ворота комнаты 18. 5. BUG-MOB-STALE-PAGE: чистка следа куска стояла под гейтом «в отрисованной комнате» — улетевший вниз оставлял себя на одной из страниц навсегда. 6. BUG-CHAR-STALE-PAGE: тень застывала на двух страницах в РАЗНЫХ позах. Прямоугольник и valid пишутся внутри `if (w && h)`, а снимок состояния брался безусловно — при нулевом спрайте слот залипал «тихим». Остаток (почему спрайт нулевой) заведён как SPRITE-ZERO-W0 в BUGS_OPEN. NEXT_SESSION переписан: следующая задача — зацеп за край пола в свободном падении (вход на уровень 7). tests-host 5/5, size-check чист. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2461,3 +2461,132 @@ if (tile_left == tiles_26_lattice_down && curr_tile == tiles_12_doortop) {
|
|||||||
4. Что именно рисует оригинал в тайле — трасса `POP_TRACE_BT=1` (печать в
|
4. Что именно рисует оригинал в тайле — трасса `POP_TRACE_BT=1` (печать в
|
||||||
`add_backtable`, `src/seg008.c`, выводит room/row/col/id/размер). Она
|
`add_backtable`, `src/seg008.c`, выводит room/row/col/id/размер). Она
|
||||||
сразу показала `id=6 w=32 h=63` рядом со «своим» `id=85 w=32 h=4`.
|
сразу показала `id=6 w=32 h=63` рядом со «своим» `id=85 w=32 h=4`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="bug-belowrow-wall"></a>
|
||||||
|
## BUG-BELOWROW-WALL. Жёлтые треугольники в шахте падения — ЗАКРЫТ
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-11).** Уровень 6, комната 3 (шахта под
|
||||||
|
комнатой 1): в нижнем ряду под пустыми тайлами (2,4)-(2,6) торчат жёлтые
|
||||||
|
треугольники.
|
||||||
|
|
||||||
|
**Корень.** `load_rowbelow` (seg008:368) при ОТСУТСТВУЮЩЕЙ комнате снизу
|
||||||
|
подставляет РАЗНЫЕ кромки: колонкам 1..9 — `tiles_0_empty`, и только левому
|
||||||
|
краю (тайл из `room_BL`) — `tiles_20_wall`. У нас `pop_room_load` забивал
|
||||||
|
стеной все одиннадцать позиций `below_fg`, а стена в роли соседа снизу-слева
|
||||||
|
даёт `topright` стены — её грань и лезла треугольниками.
|
||||||
|
|
||||||
|
**Фикс.** `below_fg[0..9] = 0`, `below_fg[10] = 20`.
|
||||||
|
|
||||||
|
**Проверено:** комната 3 совпала с картой SDLPoP — 300 пикселей расхождения,
|
||||||
|
все они силуэт Кида. Опасение «не пропадёт ли законный треугольник в (1,3)»
|
||||||
|
снято тем же сравнением: грань стены в колонке 3 и скос у её основания на
|
||||||
|
месте у обоих. Оговорка пользователя: эталон брался из `--screenshot-level`,
|
||||||
|
живая сверка в SDLPoP — потом.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="bug-balcony-right"></a>
|
||||||
|
## BUG-BALCONY-RIGHT. Правая половина портала не рисовалась — ЗАКРЫТ
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-11).** Уровень 6, комната 18: «закрытый
|
||||||
|
портал показывается некорректно» — у арки балкона нет правой половины,
|
||||||
|
решётка обрывается, справа чёрный провал.
|
||||||
|
|
||||||
|
**Корень — не в движке, а в УПАКОВЩИКЕ АТЛАСОВ.** `id 12` стоял в
|
||||||
|
`FORE_ENV_IDS` (`toolchain/pop_pack_bg.py`), потому что в `tile_table` он
|
||||||
|
числится `fore_id` ЗЕЛЬЯ (0x0A). Но зелье рисуется из `chtab_1`
|
||||||
|
(`add_foretable(id_chtab_1_...)`; у нас `pop_potion_flask`), а в `chtab_6`
|
||||||
|
под номером 12 лежит ПРАВАЯ ЧАСТЬ АРКИ БАЛКОНА 32x62, и она идёт в
|
||||||
|
backtable. Из-за списка спрайт уезжал в `pal_fore.atl`, а `pal_env0.atl`
|
||||||
|
получал дырку: `idx 12: w=0 h=0`. Движок берёт правую грань соседа из
|
||||||
|
env-атласа — и не рисовал ничего.
|
||||||
|
|
||||||
|
**Как найдено.** Трасса оригинала `POP_TRACE_BT=1` показала в спорном тайле
|
||||||
|
`BT room=18 r=1 c=7 ch=6 id=12 w=32 h=62`; дальше — чтение каталога
|
||||||
|
`pal_env0.atl` напрямую (запись 12 пустая) и `pal_fore.atl` (она там).
|
||||||
|
|
||||||
|
**Фикс.** `id 12` убран из `FORE_ENV_IDS`; в env-атласе он занял своё место.
|
||||||
|
Дифф комнаты с оригиналом 1151 -> 375 (остаток — Кид и пламя).
|
||||||
|
**Проверено пользователем:** «портал отрисовался корректно».
|
||||||
|
|
||||||
|
**Урок:** список `FORE_ENV_IDS` собирается по `tile_table.fore_id`, но
|
||||||
|
`fore_id` НЕ значит «спрайт из chtab_6» — у зелья, меча и пламени он
|
||||||
|
указывает в `chtab_1`. Прежде чем вносить номер в этот список, проверять,
|
||||||
|
из какой таблицы спрайт берётся.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="bug-mob-multiroom"></a>
|
||||||
|
## BUG-MOB-MULTIROOM. Плита не пролетала несколько комнат — ЗАКРЫТ
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-11).** Уровень 6: плита (1,7) комнаты 6
|
||||||
|
должна пролететь НЕСКОЛЬКО комнат и упасть на кнопку, открывающую решётку.
|
||||||
|
«Плита просто падает и ничего не открывается.»
|
||||||
|
|
||||||
|
**Корень.** У оригинала кусок живёт в списке mobs и спускается ряд за рядом:
|
||||||
|
`mob_down_a_row` (seg007:1387) из НИЖНЕГО ряда переводит его в комнату снизу
|
||||||
|
(`y -= 192`, ряд 0), и так сколько угодно комнат. У нас кусок на границе
|
||||||
|
комнаты ГАСИЛСЯ, а «переход вниз» подменялся сигналом главному циклу — он
|
||||||
|
искал посадку в комнате снизу через `pop_room_col_landing`. Для колонки 7
|
||||||
|
комнаты 7 (сквозная шахта) посадки нет — кусок исчезал.
|
||||||
|
|
||||||
|
**Фикс.** Честный `mob_down_a_row` в `mob_tick_one` (`pop_room.c`);
|
||||||
|
приземление в НЕ отрисованной комнате сигналит комнатой и тайлом, а щебень
|
||||||
|
и кнопку ставит главный цикл (порт `loose_land`, seg007:11E8).
|
||||||
|
|
||||||
|
Путь в этом сценарии: комната 6 (1,7) -> сквозная шахта комнаты 7 -> кнопка
|
||||||
|
(2,7) комнаты 11, а она по LINKLOC открывает ворота (1,9) комнаты 18 — те
|
||||||
|
самые у портала. **Проверено пользователем:** ворота открываются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="bug-mob-stale-page"></a>
|
||||||
|
## BUG-MOB-STALE-PAGE. Улетевшая плита оставалась на одной странице — ЗАКРЫТ
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-11).** Сразу после предыдущего фикса:
|
||||||
|
«в одном из кадров дабл-буфера осталась падающая плита».
|
||||||
|
|
||||||
|
**Корень.** Чистка прошлого кадра куска стояла под гейтом «кусок в
|
||||||
|
ОТРИСОВАННОЙ комнате»: `if (here && m->prev_y[pg] != MOB_Y_NONE)`. Как
|
||||||
|
только кусок уходил вниз через `mob_down_a_row`, гейт закрывался — и след,
|
||||||
|
оставленный им на этой странице, не стирал уже никто.
|
||||||
|
|
||||||
|
**Фикс.** Гейт убран: `prev_y[pg]` — след ИМЕННО НА ЭТОЙ странице, стирать
|
||||||
|
его надо независимо от того, где кусок сейчас. Плюс `clean = 2` во всех
|
||||||
|
ветках гашения, чтобы дочищались обе страницы. **Проверено пользователем.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="bug-char-stale-page"></a>
|
||||||
|
## BUG-CHAR-STALE-PAGE. Тень застывала на двух страницах в РАЗНЫХ позах
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-11, редкий — пойман дважды).** Уровень
|
||||||
|
6, комната с тенью: «Тень застряла в двух разных кадрах в разных позициях».
|
||||||
|
|
||||||
|
**Замер в отладчике (состояние было живым).** `Guard` = кадр 15 (stand),
|
||||||
|
x = 0x51 — то есть персонаж В ПОКОЕ. А слот отрисовки `pop_cd[OPP]`:
|
||||||
|
|
||||||
|
```
|
||||||
|
страница 0: x=25 y=85 w=34 h=38 valid=1 <- ЛЕЖАЩАЯ поза
|
||||||
|
страница 1: x=41 y=78 w=12 h=41 valid=1 <- стойка
|
||||||
|
```
|
||||||
|
|
||||||
|
**Корень.** В `pop_char_draw` (`pop_cdraw.c`) прямоугольник и `valid[dp]`
|
||||||
|
пишутся ВНУТРИ `if (w && h)`, а снимок состояния для пропуска перерисовки
|
||||||
|
(`cd_sig_make`) брался БЕЗУСЛОВНО, в самом конце функции. Если спрайт
|
||||||
|
кадра нулевой (пустая запись атласа), блок рисования пропускался — `x/y/w/h`
|
||||||
|
и `valid` оставались от ПРОШЛОГО кадра этой страницы, а сигнатура
|
||||||
|
обновлялась на текущее состояние. Дальше `cd_quiet` видел `valid = 1` и
|
||||||
|
совпавшую сигнатуру, то есть считал «на странице нарисовано ровно то, что
|
||||||
|
надо», и слот залипал навсегда: `heal` не звался, старый спрайт оставался.
|
||||||
|
|
||||||
|
**Фикс.** `cd_sig_make` вызывается только когда кадр реально рисовали
|
||||||
|
(`if (w && h)`). Без снимка слот не будет «тихим», и следующий кадр
|
||||||
|
начнётся с `heal` — старое сотрётся само.
|
||||||
|
|
||||||
|
**ОСТАЁТСЯ ОТКРЫТЫМ (в BUGS_OPEN):** почему спрайт кадра оказался нулевым.
|
||||||
|
Фикс убирает залипание, но кадр, для которого атлас отдал 0x0, всё равно
|
||||||
|
не нарисуется. Подозрение — конкуренция за окно W0 между `atlas_image` и
|
||||||
|
чтением данных уровня; проверять трассировкой маппинга.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
| ID | что | тип | статус |
|
| ID | что | тип | статус |
|
||||||
|----|-----|-----|--------|
|
|----|-----|-----|--------|
|
||||||
|
| [SPRITE-ZERO-W0](#sprite-zero-w0) | кадр персонажа изредка отдаёт спрайт 0x0 (залипание уже вылечено) | **редкий** | открыт: нужна трасса маппинга W0 |
|
||||||
| [GATE-FORE-KID](#gate-fore-kid) | Кид в проёме ворот виден поверх решётки | окклюзия | открыт: портирован только шовный случай |
|
| [GATE-FORE-KID](#gate-fore-kid) | Кид в проёме ворот виден поверх решётки | окклюзия | открыт: портирован только шовный случай |
|
||||||
| [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить |
|
| [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить |
|
||||||
| [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт |
|
| [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт |
|
||||||
@@ -339,6 +340,26 @@ for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<a id="sprite-zero-w0"></a>
|
||||||
|
## SPRITE-ZERO-W0. Кадр персонажа изредка отдаёт спрайт 0x0 — РЕДКИЙ
|
||||||
|
|
||||||
|
Всплыло при разборе [BUG-CHAR-STALE-PAGE](BUGS_CLOSED.md#bug-char-stale-page)
|
||||||
|
(тень застыла на двух страницах в разных позах). Залипание слота вылечено —
|
||||||
|
`cd_sig_make` больше не берёт снимок, если кадр не рисовали, — но САМА
|
||||||
|
причина нулевого спрайта не найдена: `atlas_image` для валидного кадра
|
||||||
|
изредка отдаёт запись 0x0.
|
||||||
|
|
||||||
|
Подозрение: конкуренция за окно W0. `pop_char_draw` маппит страницу атласа
|
||||||
|
(`gfx_w0_map(pages[page].page)`) и держит её до `gfx_w0_unmap()` в конце, а
|
||||||
|
внутри этого окна успевают отработать `cd_splash` и `pop_sword_draw`; рядом
|
||||||
|
в кадре W0 маппят чтение данных уровня (`pop_level_tile`, `pop_level_set_tile`)
|
||||||
|
и mob-тик. Если какой-то путь размапливает окно раньше времени, чтение
|
||||||
|
заголовка спрайта даст нули.
|
||||||
|
|
||||||
|
Как ловить: watchpoint на порт окна 0 либо счётчик «w==0 при непустом кадре»
|
||||||
|
в `pop_char_draw` с записью кадра/страницы/idx в отладочную ячейку, дальше
|
||||||
|
читать её из MAME. Симптом редкий (пользователь поймал дважды).
|
||||||
|
|
||||||
<a id="gate-fore-kid"></a>
|
<a id="gate-fore-kid"></a>
|
||||||
## GATE-FORE-KID. Кид, стоящий В ПРОЁМЕ ворот, виден ПОВЕРХ решётки
|
## GATE-FORE-KID. Кид, стоящий В ПРОЁМЕ ворот, виден ПОВЕРХ решётки
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Точка входа для следующей сессии (обновлено 2026-08-11)
|
# Точка входа для следующей сессии (обновлено 2026-08-11, вечер)
|
||||||
|
|
||||||
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
|
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
|
||||||
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
|
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
|
||||||
@@ -6,23 +6,22 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. СЛЕДУЮЩАЯ ЗАДАЧА: [L5-SHADOW](TASKS_OPEN.md#l5-shadow) — уровень 5
|
## 1. СЛЕДУЮЩАЯ ЗАДАЧА: зацеп в ПАДЕНИИ (вход на уровень 7)
|
||||||
|
|
||||||
Уровни 1-4 приняты **smoke-тестами**; полные обходы всех комнат делаются по
|
Уровень 6 пройден и принят: спецсобытие с тенью работает, переход на уровень
|
||||||
готовности ВСЕХ уровней ([политика приёмок](TASKS_CLOSED.md#pass-policy),
|
7 падением работает. Но **вход на 7-й ломается на механике**: Кид влетает в
|
||||||
решение пользователя 2026-08-11) — отдельных `L3-PASS`/`L4-PASS` больше нет.
|
комнату сверху и, пролетая мимо края пола, ДОЛЖЕН иметь возможность
|
||||||
|
зацепиться за него руками. Похоже, этого у нас нет вовсе — проверить и
|
||||||
|
реализовать.
|
||||||
|
|
||||||
Уровень 5 **не приносит ни одного нового тайла** (инвентарь `res2005.bin`
|
Куда смотреть: `check_grab` / ветка `action == actions_3_in_midair` в
|
||||||
сверен: всё уже есть с уровней 1-4). Вся работа — одно спецсобытие: тень
|
seg005-seg006 SDLPoP (у нас `POP_ENABLE_JUMP_GRAB` и `check_grab_run_jump`
|
||||||
появляется в комнате 24, ждёт открытия двери, идёт к зелью, **выпивает его**
|
в `pop_map.c` — это ДРУГОЙ случай, зацеп из прыжка). Начинать, как всегда,
|
||||||
и уходит за левый край; боя нет. Писать: `do_auto_moves` + таблица
|
с чтения оригинала, а не с догадок.
|
||||||
`shad_drink_move`, `do_init_shad`, ветку `check_shadow` для уровня 5,
|
|
||||||
`autocontrol_shadow_level5`, плюс ветку ТЕНИ в `check_guard_fallout`.
|
|
||||||
Всё расписано по SDLPoP прямо в задаче, включая готовые константы.
|
|
||||||
|
|
||||||
Опора: механизм «спецсобытие порождает персонажа в слоте соперника» уже
|
Опора: вход падением уже портирован — `FALLING_ENTRY_LEVEL/ROOM` в
|
||||||
отработан на скелете (`pop_check_skel`), а сама тень как `charid_1_shadow`
|
`pop_guard.h` + ветка в `pop_start_level` (`roomtest.c`), Кид появляется в
|
||||||
заведена на уровне 4.
|
комнате 17 и экран сразу переводится на комнату под ней.
|
||||||
|
|
||||||
## 2. Состояние репозитория
|
## 2. Состояние репозитория
|
||||||
|
|
||||||
@@ -85,9 +84,10 @@
|
|||||||
(seg007:776) не портирован.
|
(seg007:776) не портирован.
|
||||||
- **TORCH-ANIM-RIGHT** — под запечённым пламенем могут застыть пики/меч/зелье
|
- **TORCH-ANIM-RIGHT** — под запечённым пламенем могут застыть пики/меч/зелье
|
||||||
справа от факела; на уровнях 1-4 такого соседства нет.
|
справа от факела; на уровнях 1-4 такого соседства нет.
|
||||||
- **[GUARD-PHYS](TASKS_OPEN.md#guard-phys)** — остаток: ветка тени в
|
- **GUARD-PHYS ЗАКРЫТ** 2026-08-11: нажатие напольной кнопки НЕ-Кидом
|
||||||
`check_guard_fallout` (уходит в L5-SHADOW) и живая проверка стража,
|
проверено живьём на уровне 6 (тень встаёт на `closer` и роняет решётку).
|
||||||
нажимающего напольную кнопку.
|
- **[GATE-FORE-KID](BUGS_OPEN.md#gate-fore-kid)** — Кид в проёме ворот виден
|
||||||
|
поверх решётки; портирован только шовный случай.
|
||||||
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1` (оба низкий приоритет),
|
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1` (оба низкий приоритет),
|
||||||
`L3-COLOR`, `L1-SPEED` (игра на ~39 % быстрее оригинала), `TUNE-1`.
|
`L3-COLOR`, `L1-SPEED` (игра на ~39 % быстрее оригинала), `TUNE-1`.
|
||||||
|
|
||||||
|
|||||||
@@ -1170,3 +1170,29 @@ make-бита для кода `0x6C`. Скратч-байты — хвост `o
|
|||||||
см. [`../docs/shadow_render.md`](../docs/shadow_render.md)).
|
см. [`../docs/shadow_render.md`](../docs/shadow_render.md)).
|
||||||
|
|
||||||
**Цена:** банк 1 2755 -> 2877 Б, резидент без изменений.
|
**Цена:** банк 1 2755 -> 2877 Б, резидент без изменений.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### <a id="l6-l7-falling"></a>Переход 6 -> 7 падением — СДЕЛАН 2026-08-11
|
||||||
|
|
||||||
|
Уровень 6 не заканчивается дверью: Кид проваливается вниз из комнаты 1 и
|
||||||
|
этим попадает на 7-й. Порт двух половин:
|
||||||
|
|
||||||
|
- **выход** — `leave_room` (seg002:0504) для направления «вниз» на уровне 6
|
||||||
|
из комнаты 1 возвращает особый результат −2, а главный цикл (seg000:0893)
|
||||||
|
разбирает его как `Kid.y = -1; ++next_level`. У нас это ветка в обработке
|
||||||
|
`pop_fell_out` (`roomtest.c`), и она ОБЯЗАНА идти раньше связи вниз: у
|
||||||
|
комнаты 1 сосед снизу есть (шахта, комната 3), и без спецсобытия Кид
|
||||||
|
улетал туда, а оттуда — в рестарт уровня;
|
||||||
|
- **вход** — `set_start_pos` (seg003:0196): на 7-м уровне Кид ставится в
|
||||||
|
комнату 17, после чего экран сразу переводится на комнату ПОД ней
|
||||||
|
(`goto_other_room(3)`: y −= 189, ряд пересчитать) — он влетает сверху.
|
||||||
|
|
||||||
|
Константы `FALLING_EXIT_LEVEL/ROOM`, `FALLING_ENTRY_LEVEL/ROOM` — в
|
||||||
|
`pop_guard.h`.
|
||||||
|
|
||||||
|
**Проверено пользователем в MAME:** «переход на уровень 7 сработал».
|
||||||
|
|
||||||
|
**Остаток на следующую сессию:** влетая в комнату, Кид должен уметь
|
||||||
|
зацепиться за край пола, мимо которого пролетает — этой механики у нас,
|
||||||
|
похоже, нет (см. `NEXT_SESSION.md`, п. 1).
|
||||||
|
|||||||
@@ -610,9 +610,16 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
/* Что именно нарисовано на ЭТОЙ странице — снимок для пропуска
|
/* Что именно нарисовано на ЭТОЙ странице — снимок для пропуска
|
||||||
* следующих кадров (DRAW-COST). Только на полном пути: ранние выходы
|
* следующих кадров (DRAW-COST). Только если кадр РЕАЛЬНО рисовали:
|
||||||
* оставляют valid[dp] снятым, и cd_quiet отсеет слот и без снимка. */
|
* при нулевом спрайте (`w && h` ложно — пустая запись атласа) блок выше
|
||||||
cd_sig_make(who, &cd_sig[who][dp]);
|
* не выполняется, и x/y/w/h/valid остаются от ПРОШЛОГО кадра этой
|
||||||
|
* страницы. Снимок же брался всегда — и слот залипал: cd_quiet видел
|
||||||
|
* valid = 1 и совпавшую сигнатуру, то есть считал «на странице
|
||||||
|
* нарисовано ровно то, что нужно», а на ней стоял старый спрайт. Так
|
||||||
|
* тень оставалась на двух страницах дабл-буфера в РАЗНЫХ позах и мигала
|
||||||
|
* между ними (BUG-CHAR-STALE-PAGE). Без снимка слот просто не будет
|
||||||
|
* «тихим», и следующий кадр начнётся с heal — старое сотрётся. */
|
||||||
|
if (w && h) cd_sig_make(who, &cd_sig[who][dp]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_char_fore(uint8_t who) __banked
|
void pop_char_fore(uint8_t who) __banked
|
||||||
|
|||||||
@@ -103,6 +103,17 @@
|
|||||||
#define SHADOW_STEP_LEVEL 6
|
#define SHADOW_STEP_LEVEL 6
|
||||||
#define SHADOW_STEP_ROOM 1
|
#define SHADOW_STEP_ROOM 1
|
||||||
|
|
||||||
|
/* Спецсобытие «выход/вход падением» (custom->falling_*, дефолты SDLPoP).
|
||||||
|
* Уровень 6 не заканчивается дверью: Кид проваливается вниз из комнаты 1 и
|
||||||
|
* этим переходит на уровень 7, где появляется в комнате 17 — но экран сразу
|
||||||
|
* переводится на комнату ПОД ней, так что он влетает сверху. Комната 3
|
||||||
|
* (шахта под комнатой 1) в переходе не участвует: leave_room перехватывает
|
||||||
|
* уход раньше, чем сработала бы связь вниз. */
|
||||||
|
#define FALLING_EXIT_LEVEL 6
|
||||||
|
#define FALLING_EXIT_ROOM 1
|
||||||
|
#define FALLING_ENTRY_LEVEL 7
|
||||||
|
#define FALLING_ENTRY_ROOM 17
|
||||||
|
|
||||||
/* Единственный уровень, где тень — БОЕВОЙ персонаж (seg003:702). На всех
|
/* Единственный уровень, где тень — БОЕВОЙ персонаж (seg003:702). На всех
|
||||||
* остальных (4 зеркало, 5 зелье, 6 плита) она Кида «не видит»: см.
|
* остальных (4 зеркало, 5 зелье, 6 плита) она Кида «не видит»: см.
|
||||||
* pop_check_can_guard_see_kid. */
|
* pop_check_can_guard_see_kid. */
|
||||||
|
|||||||
@@ -879,7 +879,13 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
|||||||
* страницы своя копия фона). Раньше коридор был фиксирован под ряд 2;
|
* страницы своя копия фона). Раньше коридор был фиксирован под ряд 2;
|
||||||
* плита-потолок падает через весь ряд 0, поэтому чистим по фактической
|
* плита-потолок падает через весь ряд 0, поэтому чистим по фактической
|
||||||
* прошлой позиции. */
|
* прошлой позиции. */
|
||||||
if (here && m->prev_y[pg] != MOB_Y_NONE) {
|
/* Гейта по `here` здесь БЫТЬ НЕ ДОЛЖНО: prev_y[pg] — это след, который
|
||||||
|
* кусок оставил ИМЕННО НА ЭТОЙ странице, и стереть его надо независимо
|
||||||
|
* от того, где кусок сейчас. Пока чистка была под `here`, кусок,
|
||||||
|
* улетевший в комнату снизу (mob_down_a_row), навсегда оставлял себя
|
||||||
|
* на одной из двух страниц дабл-буфера — «застрявшая плита», видимая
|
||||||
|
* через кадр (BUG-MOB-STALE-PAGE). */
|
||||||
|
if (m->prev_y[pg] != MOB_Y_NONE) {
|
||||||
pop_heal_off(MOB_X0(m->x), m->prev_y[pg] - 27, MOB_W, 32);
|
pop_heal_off(MOB_X0(m->x), m->prev_y[pg] - 27, MOB_W, 32);
|
||||||
m->prev_y[pg] = MOB_Y_NONE;
|
m->prev_y[pg] = MOB_Y_NONE;
|
||||||
}
|
}
|
||||||
@@ -894,14 +900,34 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
|||||||
if (m->row < 3 && m->y < 226 && MOB_Y_BOUND[m->row + 1] <= m->y) {
|
if (m->row < 3 && m->y < 226 && MOB_Y_BOUND[m->row + 1] <= m->y) {
|
||||||
uint8_t t = mob_tile_at(m);
|
uint8_t t = mob_tile_at(m);
|
||||||
if (t == 0 || t == 11) {
|
if (t == 0 || t == 11) {
|
||||||
m->row++; /* mob_down_a_row */
|
/* mob_down_a_row (seg007:1387): ряд ниже, а из НИЖНЕГО ряда —
|
||||||
|
* в комнату СНИЗУ (y -= 192, ряд 0). Именно так кусок пролетает
|
||||||
|
* несколько комнат подряд: уровень 6, плита (1,7) комнаты 6
|
||||||
|
* проваливается сквозь пустую шахту комнаты 7 и приземляется на
|
||||||
|
* кнопку (2,7) комнаты 11. Раньше мы кусок на границе комнаты
|
||||||
|
* ГАСИЛИ и подменяли одним «переходом вниз» из главного цикла —
|
||||||
|
* дальше первой комнаты он не летел (BUG-MOB-MULTIROOM). */
|
||||||
|
m->row++;
|
||||||
|
if (m->row >= 3) {
|
||||||
|
m->y = (int16_t)(m->y - 192);
|
||||||
|
m->row = 0;
|
||||||
|
m->room = pop_room_link(m->room, 3 /* вниз */);
|
||||||
|
if (!m->room) { /* улетел из уровня — просто гаснет */
|
||||||
|
m->active = 0; m->clean = 2; /* дочистить ОБЕ страницы */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
here = (uint8_t)(m->room == pop_t_room);
|
||||||
|
}
|
||||||
} else if (!here) {
|
} else if (!here) {
|
||||||
/* Приземление в ПОКИНУТОЙ комнате: рисовать нечего, сигнальная
|
/* Приземление в ПОКИНУТОЙ комнате: рисовать нечего, но щебень и
|
||||||
* пара pop_loose_landed/pop_debris_at обслуживает только текущую
|
* КНОПКА под куском обязаны сработать — сигналим главному циклу
|
||||||
* комнату — щебень кладём прямо в данные уровня (loose_land пишет
|
* комнатой и тайлом (loose_land, seg007:11E8, работает с
|
||||||
* в curr_room_tiles комнаты curmob.room, а не отрисованной). */
|
* curmob.room, а не с отрисованной). */
|
||||||
pop_level_set_tile(m->room, (uint8_t)(m->row * 10 + m->col), 0x0E);
|
pop_loose_exit_room = m->room;
|
||||||
m->active = 0; m->clean = 0;
|
pop_loose_exit = (uint8_t)(m->row * 10 + m->col + 1);
|
||||||
|
/* clean = 2 даже для чужой комнаты: пока кусок летел, он мог
|
||||||
|
* рисоваться в ЭТОЙ, и след остался на обеих страницах. */
|
||||||
|
m->active = 0; m->clean = 2;
|
||||||
return;
|
return;
|
||||||
} else if (pop_loose_landed) {
|
} else if (pop_loose_landed) {
|
||||||
/* сигнал прошлого приземления ещё не разобран (один за кадр) —
|
/* сигнал прошлого приземления ещё не разобран (один за кадр) —
|
||||||
@@ -914,15 +940,11 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m->y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */
|
/* Страховка: кусок ушёл ниже поля, не поймав границу ряда (в норме этого
|
||||||
/* Приземление в комнате СНИЗУ разбирает главный цикл — но ИМЕННО
|
* не бывает — mob_down_a_row уводит его в комнату снизу). Просто гаснет,
|
||||||
* сейчас, когда кусок долетел, а не в момент отрыва плиты: иначе
|
* как у оригинала кусок в комнате 0 (move_loose: `speed = -2`). */
|
||||||
* кнопка внизу срабатывала, пока плита ещё в воздухе. */
|
if (m->y - 27 >= POP_PLAYFIELD_H) {
|
||||||
if (!pop_loose_exit) {
|
m->active = 0; m->clean = 2;
|
||||||
pop_loose_exit = (uint8_t)(m->col + 1);
|
|
||||||
pop_loose_exit_room = m->room;
|
|
||||||
}
|
|
||||||
m->active = 0; m->clean = (uint8_t)(here ? 2 : 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!here) return; /* чужая комната — считаем, но не рисуем */
|
if (!here) return; /* чужая комната — считаем, но не рисуем */
|
||||||
|
|||||||
@@ -496,6 +496,19 @@ static void pop_start_level(void)
|
|||||||
|
|
||||||
enter_room(start_room);
|
enter_room(start_room);
|
||||||
kid_init(seq, (int8_t)(pos % 10), (int8_t)(pos / 10), dir);
|
kid_init(seq, (int8_t)(pos % 10), (int8_t)(pos / 10), dir);
|
||||||
|
/* Спецсобытие «вход падением» (set_start_pos, seg003:0196): на 7-м
|
||||||
|
* уровне Кид ставится в комнату 17, а экран тут же переводится на
|
||||||
|
* комнату ПОД ней (`goto_other_room(3)`: y −= 189, ряд пересчитать) —
|
||||||
|
* он влетает в неё сверху, продолжая падение с шестого уровня. */
|
||||||
|
if (pop_current_level == FALLING_ENTRY_LEVEL &&
|
||||||
|
start_room == FALLING_ENTRY_ROOM) {
|
||||||
|
uint8_t below = pop_room_link(start_room, LINK_DOWN);
|
||||||
|
if (below) {
|
||||||
|
Kid.y = (uint8_t)(Kid.y - 189);
|
||||||
|
Kid.curr_row = pop_y_to_row((int16_t)Kid.y);
|
||||||
|
enter_room(below);
|
||||||
|
}
|
||||||
|
}
|
||||||
Kid.sword = 0; /* set_start_pos: sword_0_sheathed */
|
Kid.sword = 0; /* set_start_pos: sword_0_sheathed */
|
||||||
/* play_level (seg003:106): have_sword = level >= have_sword_from_level
|
/* play_level (seg003:106): have_sword = level >= have_sword_from_level
|
||||||
* (=2) — начиная со второго уровня меч у Кида уже есть. */
|
* (=2) — начиная со второго уровня меч у Кида уже есть. */
|
||||||
@@ -759,7 +772,19 @@ int main(void)
|
|||||||
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
||||||
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
||||||
pop_fell_out = 0;
|
pop_fell_out = 0;
|
||||||
if (down) { /* L2: переход в комнату снизу — продолжает падать */
|
/* Спецсобытие «выход падением» (leave_room, seg002:0504 —
|
||||||
|
* направление вниз даёт особый результат −2; разбор в главном
|
||||||
|
* цикле, seg000:0893). Уровень 6 не заканчивается дверью: Кид
|
||||||
|
* проваливается вниз из комнаты 1 и этим переходит на 7-й.
|
||||||
|
* Проверка ОБЯЗАНА идти раньше связи вниз: у комнаты 1 сосед
|
||||||
|
* снизу есть (шахта, комната 3), и без спецсобытия Кид улетал
|
||||||
|
* туда, а оттуда — в рестарт уровня. */
|
||||||
|
if (pop_current_level == FALLING_EXIT_LEVEL &&
|
||||||
|
cur_room == FALLING_EXIT_ROOM) {
|
||||||
|
Kid.y = 0xFF; /* seg000:0896 — Kid.y = −1 */
|
||||||
|
pop_next_level = (uint8_t)(pop_current_level + 1);
|
||||||
|
}
|
||||||
|
else if (down) { /* L2: переход в комнату снизу — продолжает падать */
|
||||||
enter_room(down); /* координаты Kid уже репроецированы pop_map */
|
enter_room(down); /* координаты Kid уже репроецированы pop_map */
|
||||||
gfx_set_draw_page(back); /* вернуть текущую back для отрисовки */
|
gfx_set_draw_page(back); /* вернуть текущую back для отрисовки */
|
||||||
} else { /* выпал из уровня (нет комнаты снизу) — рестарт */
|
} else { /* выпал из уровня (нет комнаты снизу) — рестарт */
|
||||||
@@ -886,32 +911,28 @@ int main(void)
|
|||||||
pop_loose_fell = 0;
|
pop_loose_fell = 0;
|
||||||
pop_level_set_tile(cur_room, fpos, TILE_EMPTY);
|
pop_level_set_tile(cur_room, fpos, TILE_EMPTY);
|
||||||
}
|
}
|
||||||
if (pop_loose_exit) { /* кусок долетел до низа комнаты — посадка ниже */
|
if (pop_loose_exit) { /* кусок приземлился в НЕ отрисованной комнате */
|
||||||
uint8_t fcol = (uint8_t)(pop_loose_exit - 1);
|
/* Комнату и тайл посадки считает сам кусок (pop_room.c): он летит
|
||||||
uint8_t from = pop_loose_exit_room;
|
* через комнаты честным mob_down_a_row, а не «одним переходом
|
||||||
uint8_t down = pop_room_link(from, LINK_DOWN);
|
* вниз», как было раньше. Здесь остаётся то, что положено
|
||||||
|
* loose_land (seg007:11E8): щебень на месте посадки и КНОПКА
|
||||||
|
* под ним. */
|
||||||
|
uint8_t lroom = pop_loose_exit_room;
|
||||||
|
uint8_t lpos = (uint8_t)(pop_loose_exit - 1);
|
||||||
|
uint8_t tt = pop_level_tile(lroom, lpos);
|
||||||
pop_loose_exit = 0;
|
pop_loose_exit = 0;
|
||||||
if (down) {
|
/* На opener подставляется button_type = щебень, то есть «открыть
|
||||||
int8_t lrow = pop_room_col_landing(down, fcol);
|
* НАСОВСЕМ» (modifier 0xFF). Второе место того же порта: своя
|
||||||
if (lrow >= 0) {
|
* комната — в pop_map.c. */
|
||||||
uint8_t lpos = (uint8_t)(lrow * 10 + fcol);
|
if (tt == TILE_OPENER || tt == TILE_CLOSER)
|
||||||
uint8_t tt = pop_level_tile(down, lpos);
|
pop_trigger_button(lroom, lpos,
|
||||||
/* Кусок сел в комнате СНИЗУ — кнопка там нажимается так
|
(uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt),
|
||||||
* же, как в своей (loose_land, seg007:11E8): на opener
|
pop_trob_modif(lroom)[lpos]);
|
||||||
* подставляется button_type = щебень, то есть «открыть
|
/* На факеле остаётся отдельный тайл «факел с щебнем»
|
||||||
* НАСОВСЕМ» (modifier 0xFF). Второе место того же
|
* (seg007:1067), а не обычный щебень. */
|
||||||
* порта: своя комната — в pop_map.c. */
|
pop_level_set_tile(lroom, lpos,
|
||||||
if (tt == TILE_OPENER || tt == TILE_CLOSER)
|
(uint8_t)((tt == TILE_TORCH || tt == TILE_TORCH_DEBRIS)
|
||||||
pop_trigger_button(down, lpos,
|
? TILE_TORCH_DEBRIS : TILE_DEBRIS));
|
||||||
(uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt),
|
|
||||||
pop_trob_modif(down)[lpos]);
|
|
||||||
/* На факеле остаётся отдельный тайл «факел с щебнем»
|
|
||||||
* (seg007:1067), а не обычный щебень. */
|
|
||||||
pop_level_set_tile(down, lpos,
|
|
||||||
(uint8_t)((tt == TILE_TORCH || tt == TILE_TORCH_DEBRIS)
|
|
||||||
? TILE_TORCH_DEBRIS : TILE_DEBRIS));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (pop_ceil_fell) { /* провалилась плита-ПОТОЛОК (ряд 2 комнаты сверху) */
|
if (pop_ceil_fell) { /* провалилась плита-ПОТОЛОК (ряд 2 комнаты сверху) */
|
||||||
uint8_t ccol = (uint8_t)(pop_ceil_fell - 1);
|
uint8_t ccol = (uint8_t)(pop_ceil_fell - 1);
|
||||||
|
|||||||
@@ -44,7 +44,15 @@ OUT_DIR = os.path.join(HERE, "..", "poc", "res", "bg")
|
|||||||
# Спрайты FORE-слоя (add_foretable в seg008 — «перед персонажем»): фронты
|
# Спрайты FORE-слоя (add_foretable в seg008 — «перед персонажем»): фронты
|
||||||
# столбов, ворот, дебриса. См. tile_table.fore_id + анализ pop_background_strategy.
|
# столбов, ворот, дебриса. См. tile_table.fore_id + анализ pop_background_strategy.
|
||||||
# 119..123 — фронт чомпера и его кровь; 77 — фронт ЗЕРКАЛА (MIRROR_ENV_IDS).
|
# 119..123 — фронт чомпера и его кровь; 77 — фронт ЗЕРКАЛА (MIRROR_ENV_IDS).
|
||||||
FORE_ENV_IDS = {9, 12, 49, 88, 91, 95, 100} | {106, 107, 108, 109, 110} \
|
#
|
||||||
|
# id 12 ЗДЕСЬ НЕТ намеренно. В tile_table он стоит как fore_id ЗЕЛЬЯ
|
||||||
|
# (0x0A), но зелье рисуется из chtab_1 (`add_foretable(id_chtab_1_...)`,
|
||||||
|
# seg008 draw_tile_fore; у нас — pop_potion_flask), а в chtab_6 под этим
|
||||||
|
# номером лежит ПРАВАЯ ЧАСТЬ АРКИ БАЛКОНА (32x62), и она идёт в backtable:
|
||||||
|
# `BT room=18 r=1 c=7 ch=6 id=12` в трассе оригинала. Пока 12 был в этом
|
||||||
|
# списке, он уезжал в fore-атлас, а env-страница получала дырку — правая
|
||||||
|
# половина портала не рисовалась вовсе (BUG-BALCONY-RIGHT).
|
||||||
|
FORE_ENV_IDS = {9, 49, 88, 91, 95, 100} | {106, 107, 108, 109, 110} \
|
||||||
| {119, 120, 121, 122, 123} | {77}
|
| {119, 120, 121, 122, 123} | {77}
|
||||||
|
|
||||||
# Пол-оверлей подъёма (floor_left_overlay, draw_floor_overlay seg008): рисуется
|
# Пол-оверлей подъёма (floor_left_overlay, draw_floor_overlay seg008): рисуется
|
||||||
|
|||||||
Reference in New Issue
Block a user