PoP: скелет уровня 3, цвета стражей, окклюзия соперника; кэш соседних комнат
Скелет (L3-SKEL, ассеты + механика): - pop_pack_guard.py получил параметр набора (GUARD/SKEL): атлас скелета poc/res/skel/g0..g3.atl (28 кадров), палитра — из его res750.pal (на ур. 3 curr_guard_color = 0, оригинал палитру не подменяет); - pop_guard_load выбирает набор по tbl_guard_type и перезагружается ПРИ СМЕНЕ УРОВНЯ (load_lev_spr, seg000:1092) — без этого скелет рисовался атласом стража и был невидим; - load_frame: charid_4_skeleton идёт по таблице стража (seg006:529), тень — только в кадрах 150..189. Пока ветка была одна (charid_2_guard), скелет получал image из таблицы Кида (180 при 28 спрайтах) и не рисовался; - check_skel (seg002:1042), ветка charid_4 в enter_guard, возрождение в комнате 3 при падении (seg002:252), autocontrol_skeleton; - leveldoor_open (seg007:456) — новый флаг, сбрасывается стартом уровня. Цвета стражей (BUG-GUARD-COLOR-1, закрыт): - все 7 палитр res10.bin -> pop_guard_pal.h, заливка 16 слотов по guards_color комнаты перед отрисовкой (set_chtab_palette, seg003:257). Проверено в MAME: ур. 2 комн. 11 = цвет 1, комн. 7 = цвет 3, полоса HP меняется вместе со стражем. Грабля: gfx_pal_load отдаёт указатель в BIOS, а тот читает только #4000-#BFFF — таблицу из банка копируем в стек. Кэш соседних комнат (BUG-SWORD-GHOST-1, закрыт): - pop_map кэширует fg соседей слева/справа ЦЕЛИКОМ и резолвит col -10..19. Было -2..11, дальше мнимая стена: луч видимости упирался в неё (страж после follow_guard в col 12), Кид прятал меч посреди боя и не мог достать обратно. +48 байт W2. Окклюзия соперника: - pop_fore_over_char получил проход other_overlay_tile (порядок midtable, seg008:1B06) и расширение перебора объединённым прямоугольником «персонаж + клинок + брызги» — падающий скелет больше не рисуется поверх кладки и верхней грани пола; - клип полем 192 строк (reset_obj_clip, seg006:0507) для спрайта, клинка (общий pop_sword_draw) и брызг — спрайт не залезает на полосу HP; - ROOMNAV после смерти Кида делает честный pop_start_level: телепорт «оживлял» мёртвого мимо старта уровня, оставляя живого скелета рядом с вернувшейся кучей костей. Ассеты чомпера (под L3-CHOMP): весь набор кадров в атласе явным списком (101-105 низ, 111-113 верх, 106-110 фронт, 114-123 кровь mono-силуэтом) — render_room анимированные тайлы пропускает, и в атласе не было ни одного. Число EMM-страниц не изменилось. Тесты: tests-host все 5 наборов зелёные, t_char вырос до 65 проверок (резолв колонок за краем комнаты, возрождение скелета); в testkit добавлен гард «код наехал на данные» (DATA_LOC). Доски: TASKS.md разнесён на TASKS_OPEN/TASKS_CLOSED, закрытые баги с разбором корней — в bug_closed.md; заведены DRAW-CHAR (отрисовка одна на всех Char, как физика после GUARD-PHYS) и L3-COLOR (зелёная кладка уровня 3: level_var_palettes = ресурс 20, есть в MSDOS/PRINCE.DAT). В roomtest.c временно оставлен автостоп на падении соперника (отладка падений скелета) — помечен ВРЕМЕННО. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
Сюда переезжает всё, что **закрыто**: подтверждённые фиксы, снятые
|
||||
диагнозы, осознанные решения «не делать». Открытые баги — в
|
||||
[`bug_list.md`](bug_list.md), текущие задачи — в [`TASKS.md`](TASKS.md).
|
||||
[`bug_list.md`](bug_list.md), текущие задачи — в
|
||||
[`TASKS_OPEN.md`](TASKS_OPEN.md), закрытые задачи с протоколами — в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md).
|
||||
|
||||
Файл существует не ради истории как таковой: половина записей ниже — это
|
||||
разбор КОРНЯ (odd-pixel арифметика `char_x`, подстановка тайла нажатой
|
||||
@@ -11,6 +13,127 @@
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-guard-color-1"></a>
|
||||
## BUG-GUARD-COLOR-1. Страж и его полоса HP — всегда одного цвета — **ЗАКРЫТ 2026-08-07**
|
||||
|
||||
**Симптом (пользователь, прогон уровня 2).** Комната 4: страж не того цвета,
|
||||
что в SDLPoP, и полоса его HP тоже.
|
||||
|
||||
**Корень.** Оригинал держит ОДИН набор спрайтов стража и подменяет 16 цветов
|
||||
палитры: `redraw_screen` (seg003:255) зовёт `set_chtab_palette(chtab_5_guard,
|
||||
&guard_palettes[0x30*curr_guard_color − 0x30], 16)` ПЕРЕД отрисовкой комнаты,
|
||||
где `curr_guard_color = level.guards_color[room−1] & 0x0F` (enter_guard,
|
||||
seg002:184), а `guard_palettes` — ресурс 10 из `PRINCE.DAT` (7 палитр × 16
|
||||
цветов, 6-битные каналы). У нас `pop_pack_guard.py` брал `COLOR = 2`
|
||||
константой (цвет стражей уровня 1) и запекал одну палитру в слоты
|
||||
`0x90..0x9F`. Полоса HP рисуется тем же атласом — отдельного бага не было.
|
||||
|
||||
**Фикс.**
|
||||
1. `pop_pack_guard.py` выгружает ВСЕ 7 палитр в `pop_guard_pal.h`
|
||||
(7 × 16 × 4 = 448 Б, записи `B,G,R,0` — формат `gfx_pal_load`);
|
||||
каналы масштабируются `scale6to8`, как в `pop_pack_kid` для `kid.pal`.
|
||||
2. `pop_guard_set_palette(color)` (`pop_gdraw.c`) заливает 16 слотов в ОБЕ
|
||||
палитры дабл-буфера; `color == 0` — не трогать (так и оригинал).
|
||||
3. Зовётся из `pop_guard_enter` сразу после чтения данных комнаты, то есть
|
||||
ДО отрисовки — как `redraw_screen`. `pop_level_guard` цвет отдавал уже
|
||||
давно, его просто никто не использовал.
|
||||
|
||||
**Грабли, стоившие итерации (записать на будущее).** Первая версия передавала
|
||||
`gfx_pal_load` указатель прямо на таблицу — а таблица лежит в rodata
|
||||
БАНКОВОГО модуля, то есть по 0xC000+. `gfx_pal_load` отдаёт указатель в BIOS
|
||||
(`$A4` через `rst #0x08`), а BIOS читает только `#4000-#BFFF` (корневой
|
||||
CLAUDE.md). BIOS забирал мусор с чужой страницы, палитра уезжала в тёмное и
|
||||
страж становился НЕВИДИМЫМ. Лечится копией записи в локальный буфер — стек
|
||||
гарантированно в W2.
|
||||
|
||||
**Проверено в MAME (уровень 2, ROOMNAV):**
|
||||
|
||||
| комната | цвет из уровня | что на экране |
|
||||
|---------|----------------|---------------|
|
||||
| 11 | 1 | страж сине-фиолетовый, полоса HP синяя |
|
||||
| 7 | 3 | страж оранжевый, полоса HP оранжевая |
|
||||
| ур. 1 | 2 | охра, как было (регрессии нет) |
|
||||
|
||||
Цвета сходятся с таблицей `pop_guard_pal.h`: цвет 1 = (72,145,255),
|
||||
цвет 3 = (255,80,0), цвет 2 = (170,48,0).
|
||||
|
||||
**Смежное, НЕ входит сюда:** палитра КЛАДКИ уровня 3 (в оригинале зелёная) —
|
||||
другой механизм и другой ресурс, заведена отдельной задачей
|
||||
[L3-COLOR](TASKS_OPEN.md#l3-color).
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-sword-ghost-1"></a>
|
||||
## BUG-SWORD-GHOST-1. Переход комнаты В БОЮ: Кид прячет меч и дерётся пустой рукой — **ЗАКРЫТ 2026-08-07**
|
||||
|
||||
**Проверено в игре (пользователь):** «вроде проблема не воспроизводится —
|
||||
всё корректно». Замер на замороженном кадре: оба персонажа в комнате 2,
|
||||
ряд 1, колонки 4 и 7, `Kid.sword = 2`, `Guard.sword = 2`, луч видимости
|
||||
прошёл, счётчик уборок меча за прогон — **0**.
|
||||
|
||||
**Симптом.** Кида вытеснили из комнаты 3 в комнату 2 в бою, он спрятал
|
||||
меч; страж вошёл следом, и дальше Кид «отбивался» без клинка. Зависимость
|
||||
от расстояния между Кидом и стражем в момент перехода: близко — бой
|
||||
продолжался нормально, далеко — меч убирался и страж не шёл, а в
|
||||
промежутке страж приходил, но меч оставался в ножнах.
|
||||
|
||||
**Корень — мнимая стена за краем комнаты, а не боёвка.** Ложным было
|
||||
`can_guard_see_kid`. Луч видимости (`check_can_guard_see_kid`, seg003:688)
|
||||
идёт по тайлам между колонками Кида и стража, а колонка считается из `x`:
|
||||
`floor((x − 7 − 58) / 14)`. На всём диапазоне `x` 0..255 это даёт `col`
|
||||
от **−5 до 13**, то есть колонка регулярно уходит ЗА комнату. Оригинал
|
||||
такие колонки резолвит через `find_room_of_tile` (seg006:005D), гуляя по
|
||||
`roomlinks` на любую глубину; наш `get_tile` знал соседей только на две
|
||||
колонки (`−2..11`), а дальше отдавал `TILE_WALL`. Луч упирался в эту
|
||||
мнимую стену, `can_guard_see_kid` падал в 0 — и `control_with_sword`
|
||||
(ветка «противника не видно») штатно убирал меч.
|
||||
|
||||
Тот же ноль не давал достать меч обратно: `control_standing` зовёт
|
||||
`draw_sword` только при `can_guard_see_kid >= 2`. Отсюда и «драка без
|
||||
меча», и зависимость от расстояния — на самом деле не от расстояния, а от
|
||||
того, вышла ли колонка за кэш.
|
||||
|
||||
**Артефакт (регистратор в памяти, снят с живой сцены).** Брейкпоинт тут
|
||||
бесполезен — баг ловится руками и редко, поэтому в код были временно
|
||||
вшиты байты «кто убрал меч и почему оборвался луч»; читались после сцены:
|
||||
|
||||
```
|
||||
src = 2 control_with_sword, ветка «противника не видно»
|
||||
why = 2 луч упёрся в стену
|
||||
tile = 0x14 = 20 = TILE_WALL
|
||||
col = 12 ← колонка стража ЗА кэшем (было −2..11)
|
||||
kid_col = 8 guard_col = 12
|
||||
```
|
||||
|
||||
**Фикс.** `pop_map` кэширует `fg` соседних комнат слева и справа ЦЕЛИКОМ
|
||||
(по 30 байт, раскладка комнаты) и резолвит `col` от −10 до 19 в реальный
|
||||
тайл соседа; за этими пределами — по-прежнему стена (у оригинала там
|
||||
следующий `roomlink`, у нас край кэша). Данные забирает сама
|
||||
`pop_map_set_edges(left, right, up, down)`, читая уровень — отдельной
|
||||
копии в приложении нет. Заодно `gate_modif` перестал читать за границу
|
||||
массива модификаторов: openness ворот на дальних колонках берётся из
|
||||
`room_modif` СОСЕДА (`pop_gate_modif`, им же пользуется луч).
|
||||
|
||||
Цена: **+48 байт** в W2 (кэш 6+6 → 30+30), код W1 даже уменьшился.
|
||||
Комнаты сверху/снизу оставлены как были (`above_fg` / `below_fg`): по
|
||||
вертикали `curr_row` за −1..3 не выходит, мнимых стен там не возникает.
|
||||
|
||||
Попутно вернули строку оригинала, потерянную при порте: `control_with_sword`
|
||||
сбрасывает `holding_sword` для живого Кида (seg005:980) — от неё зависит
|
||||
индикатор HP стража.
|
||||
|
||||
**Регресс:** `tests-host` — все 5 наборов зелёные, характеризационные
|
||||
трассы Кида не сдвинулись (`[phys] ok: 1723`). Новый набор проверок
|
||||
`char_tiles_resolve_across_rooms` в `t_char` держит резолв колонок
|
||||
−11..20 в тайлы соседей и стену на краю кэша — чтобы кэш нельзя было
|
||||
молча сузить обратно.
|
||||
|
||||
**Почему в SDLPoP не воспроизводилось** (пользователь пробовал): там этой
|
||||
границы просто нет — `find_room_of_tile` уходит по `roomlinks` сколько
|
||||
нужно, и луч никогда не встречает мнимой стены.
|
||||
|
||||
---
|
||||
|
||||
## BUG-GRAB-1. Прыжок с места через провал в 3 тайла: зацепа нет — **ЗАКРЫТ 2026-08-05**
|
||||
|
||||
**Проверено в игре (пользователь):** «зацеп работает». Физика была верна с
|
||||
@@ -404,6 +527,7 @@ K3; K2b просто запускает run-jump». Полировкой это
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-fall-sword-1"></a>
|
||||
## BUG-FALL-SWORD-1. Отход с мечом в провал: не та последовательность падения — **ЗАКРЫТ 2026-08-04**
|
||||
|
||||
**Симптом (приёмка уровня 2, комната 4).** Кид с вынутым мечом отступает
|
||||
@@ -550,6 +674,7 @@ pop_gframe (кадр СТРАЖА, image 17): dx=-1 weight_x=8
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-land-sword-1"></a>
|
||||
## BUG-LAND-SWORD-1. Кид навсегда застревает в приседе после падения с мечом — **ЗАКРЫТ 2026-08-04**
|
||||
|
||||
**Симптом (приёмка уровня 2).** Страж ударил Кида, Кид потерял HP,
|
||||
@@ -1521,3 +1646,164 @@ dir_56_none`. То есть стражи — такая же часть дан
|
||||
`../docs/room_model_plan.md`): держать `Kid.room` отдельно от `drawn_room` и
|
||||
рисовать со смещением ±140, как `xpos_in_drawn_room` (seg004:0405). Пока
|
||||
поводов для этого нет — заводить обратно только по живому наблюдению.
|
||||
|
||||
---
|
||||
|
||||
# Прогон всех комнат уровней 1 и 2 (пользователь, 2026-08-07) — ЗАКРЫЛ ТРИ РЕВИЗИИ РАЗОМ
|
||||
|
||||
Результат прогона: **крупных багов нет**. Тем самым закрыты и переехали сюда
|
||||
из `bug_list.md` три накопившихся хвоста — чек-листы ручной перепроверки
|
||||
фиксов, таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений прогона
|
||||
2026-08-03. Всё, что с прогона 2026-08-07 осталось открытым, — три записи
|
||||
уровня 2 в [`bug_list.md`](bug_list.md) (BUG-GUARD-COLOR-1,
|
||||
BUG-GUARD-SPLASH-1, BUG-CHEAT-FIGHT-1); ни одна из них не мешает играть.
|
||||
|
||||
Сырые формулировки пользователя лежат рядом: [`bugs_level1.md`](bugs_level1.md)
|
||||
и [`bugs_level2.md`](bugs_level2.md).
|
||||
|
||||
<a id="ручная-перепроверка-2026-08-03"></a>
|
||||
## Ручная перепроверка фиксов (2026-08-03) — ПРОЙДЕНА
|
||||
|
||||
Шесть корней прогона 2026-08-03 были закрыты автоматической проверкой в MAME
|
||||
(мост `mame-z80`: чтение `_Kid`, потиковые трассы, скриншоты) — этого хватает,
|
||||
чтобы показать, что конкретный сценарий больше не воспроизводится, но НЕ
|
||||
хватает, чтобы поймать регрессии в соседней механике. Отсюда список сценариев
|
||||
ровно в тех формулировках, в которых баги были заведены. Прогон 2026-08-07
|
||||
прошёл все комнаты уровней 1 и 2 и ни одного из этих симптомов не показал.
|
||||
|
||||
| # | что проверялось | ожидаемо |
|
||||
|---|-----------------|----------|
|
||||
| 1 | комната 22: выпить зелье (0,6), выйти в 16/23 и вернуться | кувшина нет, пузырька над пустым местом нет |
|
||||
| 2 | то же для комнат 14 (0,5) и 17 (2,3) | так же |
|
||||
| 3 | комната 15: подобрать меч (2,2), выйти и вернуться | меча нет; кладка на дальней стене НЕ мигает |
|
||||
| 4 | комната 12: разбить плиты (0,1)/(0,2) и потолок в 16, выйти-вернуться | остаются разбитыми, проём не закрывается |
|
||||
| 5 | комната 17 из 23: разбить (1,5)/(1,6), выпить зелье (2,3), вернуться | всё остаётся |
|
||||
| 6 | **после смерти** зайти в те же комнаты | ВСЁ восстановлено (в оригинале смерть = `load_level`) |
|
||||
| 7 | комната 12: разбег в закрытую решётку (0,9) с полушага | не проходит насквозь; перелистывание экрана штатно (см. BUG-SEAM-DRAW-1) |
|
||||
| 8 | комната 6: бег справа налево от (0,9), длинный прыжок (0,6)→(0,7) | не влетает внутрь стены |
|
||||
| 9 | комната 5: с кнопки (0,6) падение на (2,7), присед, вставание | остаётся в комнате 5 (проверено трассой: `fr=111 x=177` → `seq_47` → `fr=15 x=173`) |
|
||||
| 10 | комната 5: нажать кнопку (0,4) | поднимаются ОБЕ решётки — (0,5) видно на экране, (0,9) проверять из комнаты 1 |
|
||||
| 11 | страж (комнаты 3, 21) убивает Кида | смерть доигрывается, респавн в стартовой позиции уровня; цикла «убил-воскрес» нет |
|
||||
| 12 | клавиатура: долгая игра с Shift+стрелка | ↑ и Shift не залипают; осторожный шаг не превращается в бег |
|
||||
| 17 | **[BUG-KBD-4]** долго играть Shift+стрелка, много тапов подряд | Shift не «отваливается»; если однажды залипнет — снимается первым же нажатием стрелки |
|
||||
| 18 | **[BUG-RESPAWN-2]** убить стража (комнаты 3/21), умереть, вернуться в ту же комнату | страж снова жив, стоит на исходном тайле, HP полные |
|
||||
|
||||
Отдельно смотрели **регрессии от порта `check_collisions`** — он трогает всю
|
||||
горизонтальную коллизию: бамп в стену на бегу и в прыжке, осторожный шаг у
|
||||
стены, проход через ОТКРЫТЫЕ ворота, разворот в проёме решётки, переходы через
|
||||
швы (старый BUG-SEAM-PINGPONG). Не всплыло.
|
||||
|
||||
<a id="обход-всех-24-комнат-уровня-1"></a>
|
||||
## Обход всех 24 комнат уровня 1 — ЗАКРЫТ прогоном 2026-08-07
|
||||
|
||||
Инструмент: `#define ROOMNAV` в `roomtest.c` — `+`/`-` (цифровой блок либо
|
||||
`=`/`-` основного ряда) переключают комнату по номеру (1..24, с обёрткой),
|
||||
Kid ставится на первый пол. Номер комнаты — полосками в верхнем борте: слева
|
||||
десятки, справа единицы (`||` `||||` = 24). Инструмент остаётся в сборке:
|
||||
он же нужен для приёмки уровня 3.
|
||||
|
||||
Таблица заполнялась по ходу отладки и осталась незакрытой на 19 строк из 24;
|
||||
её закрыл ручной прогон всех комнат уровней 1 и 2 (2026-08-07, крупных багов
|
||||
нет). Ниже — то, что таблица успела зафиксировать: это не «отчёт по
|
||||
комнатам», а разбор корней, полезный при похожем симптоме.
|
||||
|
||||
| комната | статус | что не так |
|
||||
|---------|--------|------------|
|
||||
| 1 | пофикшено | падающая плита (2,6): правая грань видна через пол (2,7) и перекрывает его переднюю грань — `mob_render` брал ряд соседа из `m->row` (счётчик, уже ушедший на ряд вперёд), а не из координаты |
|
||||
| 5 | пофикшено | прыжок в решётку: Kid оставался стоять на 6 px ВЫШЕ пола и без приземления-приседания — от `bumped()` (seg004) был портирован только хвост (`seq_47`), не хватало `bumped_floor` (прижатие Y к полу + `seq_46_hardbump` на кадрах прыжка 24/25/40..42/102..106) и `bumped_fall` |
|
||||
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
|
||||
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» выше |
|
||||
| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` |
|
||||
| 13, 18, 24 | недостижимы в игре | свойство данных уровня, разбор — «НЕ БАГИ» выше |
|
||||
|
||||
<a id="сырые-наблюдения-прогон-2026-08-03"></a>
|
||||
## Сырые наблюдения (прогон 2026-08-03) → корень
|
||||
|
||||
Одиннадцать формулировок с прогона свелись к шести корням; все шесть закрыты
|
||||
и проверены в MAME — разборы выше по файлу.
|
||||
|
||||
| # | наблюдение (кратко) | корень |
|
||||
|---|---------------------|--------|
|
||||
| 1 | кувшин выпит, а пузырёк рисуется / кувшин возвращается (14, 22, 17) | BUG-LVLSTATE-1 |
|
||||
| 2 | меч возвращается в 15 / мигает контур кладки | BUG-LVLSTATE-1 |
|
||||
| 3 | комната 12: пробегает сквозь закрытую решётку (0,9) | BUG-COLL-1 |
|
||||
| 4 | после respawn плиты остаются разбитыми, зелья выпитыми | BUG-RESPAWN-1 |
|
||||
| 5 | плита/зелье возвращаются и БЕЗ respawn (12→16, 17, 22) | BUG-LVLSTATE-1 |
|
||||
| 6 | комната 6: длинный прыжок (0,6)→(0,7) — влёт в стену, респавн | BUG-COLL-1 |
|
||||
| 7 | залипает ↑ | BUG-KBD-3 |
|
||||
| 8 | залипает Shift | BUG-KBD-3 / BUG-KBD-4 (поправка — BUG-KBD-5) |
|
||||
| 9 | комната 5: кнопка (0,4) не открывает решётку (0,5) | BUG-GATE-ANIM-1 |
|
||||
| 10 | комната 5: с кнопки (0,6) на (2,7), присед — провал в комнату 6 | BUG-STANDUP-1 |
|
||||
| 11 | страж убил Кида → Кид воскресает на месте и его убивают снова | BUG-DEATH-1 |
|
||||
|
||||
Вторая волна того же прогона (вечер) дала ещё четыре наблюдения — BUG-KBD-4,
|
||||
BUG-RESPAWN-2, BUG-DRAWORDER-1, BUG-LOOSE-2, — все закрыты, разборы выше.
|
||||
Тогда же закрыт как невоспроизводящийся BUG-SEAM-DRAW-1 и заведён
|
||||
BUG-GATE-PASS-1, который остаётся открытым (ждёт сценария).
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-guard-splash-1"></a>
|
||||
## BUG-GUARD-SPLASH-1. Нет «брызг» при попадании по стражу — **ЗАКРЫТ 2026-08-07**
|
||||
|
||||
**Как было заведено (пользователь, 2026-08-07).** При попадании по стражу
|
||||
должен рисоваться сплеш («звёздочка»), чтобы игрок видел, что удар дошёл до
|
||||
цели, не переводя взгляд на полосу HP. У Кида такое есть, у стража — нет.
|
||||
|
||||
**Корень.** `draw_hurt_splash` (seg006:16CE) в оригинале зовётся ИЗ ДВУХ
|
||||
мест: `draw_kid` при `hitp_delta < 0` (seg008:1643) и `draw_guard` при
|
||||
`guardhp_delta < 0` (seg008:1654). У нас была портирована только первая
|
||||
половина — `kid_draw_splash()`; для стража вызова не было вовсе, хотя
|
||||
`guardhp_delta` считался и уже использовался.
|
||||
|
||||
**Фикс** (`pop_guard.c`, `pop_gdraw.c`, `roomtest.c`):
|
||||
|
||||
- флаг `pop_guard_hurt` — симметрия `pop_kid_hurt`: ставится в
|
||||
`pop_do_delta_hp`, когда `guardhp_delta < 0`, снимается главным циклом
|
||||
после отрисовки (стража могло не оказаться в отрисованной комнате —
|
||||
тогда брызги просто пропадают, как в оригинале);
|
||||
- отрисовка — ВНУТРИ `pop_guard_draw`, между спрайтом стража и клинком:
|
||||
`draw_guard` кладёт брызги в objtable сразу после стража и ДО клинка, а
|
||||
порядок записей задаёт порядок рисования. Заодно это решает fore-слой:
|
||||
`pop_fore_over_char` в конце той же функции накрывает и брызги;
|
||||
- спрайт — `chtab_5_guard` `obj_id = 1`, то есть **image 1 нашего атласа
|
||||
стража** (`res752.png`, звезда 28×26): у `add_midtable` аргумент
|
||||
`obj_id + 1`, а `get_image` вычитает единицу обратно — тот же off-by-one,
|
||||
что расписан в шапке `pop_pack_guard.py`. У Кида это image 218;
|
||||
- смещения — три ветки оригинала: кадр 178 (chomped) брызг не даёт вовсе;
|
||||
185 и 106..110 (смерть/падение) — `obj_y + 4`; 177 (пики) — сдвиг НАЗАД
|
||||
на 5; иначе `obj_y − 11` (у Кида 15: `((charid == kid) << 2) + 11`);
|
||||
- свой прямоугольник heal по страницам (`qx_l/qvalid`) — брызги живут один
|
||||
кадр, но стирать их обязана та страница, в которую рисовали.
|
||||
|
||||
**Побочно исправлено:** `pop_kid_hurt` взводился при ЛЮБОЙ ненулевой дельте
|
||||
HP, поэтому зелье здоровья (+1) давало Киду и брызги, и красную вспышку.
|
||||
И `draw_kid` (seg008:1643), и `flash_if_hurt` (seg003:785) смотрят именно
|
||||
`hitp_delta < 0` — условие приведено к оригиналу.
|
||||
|
||||
**Проверено в MAME (2026-08-07)**, мост `mame-z80`, уровень 1, комната 21,
|
||||
живой страж (HP 3/3):
|
||||
|
||||
```
|
||||
bp 51BA ← адрес снятия pop_guard_hurt в main: срабатывает ТОЛЬКО
|
||||
в кадре удара, то есть уже после отрисовки брызг
|
||||
key k ← чит «убить стража» = guardhp_delta < 0
|
||||
→ state=stop PC=0x51BA, guardhp: curr=0 max=3 delta=0 hurt=1
|
||||
bp 8BDF (_gfx_set_visible_page) + out ← домотать до флипа страницы
|
||||
snap ← звезда нарисована поверх стража
|
||||
```
|
||||
|
||||
Следующие два кадра (обе страницы дабл-буфера) — чисто, следов брызг нет.
|
||||
|
||||
Приём на будущее: снятие флага сделано **по факту** (`if (pop_guard_hurt)
|
||||
pop_guard_hurt = 0;`), а не безусловно — это и экономит запись каждый кадр,
|
||||
и даёт готовую точку останова для отладки в MAME. Условные брейкпоинты
|
||||
мост не принимает (`bp ADDR cond` вешает плагин), поэтому точка останова,
|
||||
срабатывающая сама по себе только в нужном кадре, — рабочий обходной путь.
|
||||
|
||||
**Цена:** `_CODE` 24 179 → 24 209 (+30 Б резидента), BANK4 (`pop_gdraw`)
|
||||
2407 → 3369 (+962 Б, банк занят на 20.6 %, свободно 13 015 Б).
|
||||
|
||||
**Цвет брызг** зависит от палитры стража (слоты `0x90..0x9F`), поэтому он
|
||||
изменится вместе с [BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1) —
|
||||
отдельной работы не требует.
|
||||
|
||||
Reference in New Issue
Block a user