Files
Sprinter-SDCC/applications/PoP/docs/impl_diff.md
T
snark13 5348feb5f4 Доски приведены в соответствие с кодом; 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>
2026-08-11 16:10:10 +03:00

120 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Осознанные расхождения с SDLPoP
Правило подпроекта (`../CLAUDE.md`): расхождение нашей реализации с
`SDLPoP/src/` — по умолчанию **баг у нас**. Этот файл — список исключений:
мест, где мы сознательно сделали иначе, потому что платформа/ABI/бюджет
кадра требуют другого, а НАБЛЮДАЕМОЕ поведение обязано совпадать.
Формат записи: что делает оригинал → что делаем мы → почему → чем платим и
что проверять при регрессе. Если запись перестала быть верной (портировали
дословно, отказались от обхода) — удалять, а не оставлять «для истории»:
история в git.
---
## D-1. История флагов перекрытия у бокового шва: сдвиг вместо тега комнаты
**Файлы:** `roomtest/pop_map.c` (`pop_coll_shift`, `pop_coll_invalidate`,
`check_collisions`), `roomtest/roomtest.c` (`enter_room_side`).
**Связанный баг:** BUG-GATE-PASS-1 (`roomtest/BUGS_CLOSED.md`).
**Дата:** 2026-08-09.
### Как в оригинале
`check_collisions` (seg004:0004) вместе с `get_row_collision_data`
(seg004:0185) держит **10 слотов** флагов перекрытия и рядом —
**параллельный массив номера комнаты**:
```c
row_coll_flags_ptr[tile_col] = curr_flags; /* tile_col — колонка ВНУТРИ разрешённой комнаты (0..9) */
row_coll_room_ptr [tile_col] = curr_room; /* и номер этой комнаты */
...
for (short column = 9; column >= 0; --column) {
if (curr_row_coll_room[column] >= 0 &&
prev_coll_room[column] == curr_row_coll_room[column]) {
if ((prev_coll_flags[column] & 0x0F) == 0 &&
(curr_row_coll_flags[column] & 0x0F) != 0)
bump_col_left_of_wall = column;
...
```
Ключ слота — пара **(колонка в своей комнате, номер комнаты)**. Решётка
комнаты 8 и до перехода 8→6, и после лежит в слоте 9 с `room = 8`: история
переживает смену комнаты, переход флага 0→1 виден, `bumped()` срабатывает.
Комнату оригинал резолвит на лету через `find_room_of_tile` (seg006:005D),
никакого кэша всех комнат у него нет.
### Что делаем мы
Индекс — **колонка ОТРИСОВАННОЙ комнаты**, диапазон −2…11 (14 слотов,
`COLL_C0`/`COLL_N`/`COLL_IDX`), номер комнаты рядом не хранится. При смене
комнаты тот же физический тайл менял бы слот на ±10, поэтому раньше история
просто выбрасывалась (`pop_coll_invalidate``prev = 3` = «уже
перекрывал» → бампа нет). Именно это и был BUG-GATE-PASS-1.
Теперь при **боковом** переходе история не выбрасывается, а
**перенумеровывается**: `pop_coll_shift(∓10)` сдвигает `coll_curr`,
`coll_above`, `coll_below` на 10 слотов и заполняет освободившиеся
тройками. `enter_room_side` зовёт её сразу после `pop_map_set_edges`.
Корректность держится на том, что `check_leave` двигает `Char.x` ровно на
∓140 = 10 тайлов по 14 px, и координата грани (`pop_x_bump[col + …]`)
сдвигается на те же 140 вместе с габаритом Кида, — **сами флаги
инвариантны**, меняется только номер слота. Сдвигаются `curr/above/below`,
а не `prev`: `prev` на следующем кадре всё равно перезапишет
`move_coll_to_prev`, выбирая источник как раз из этих трёх.
Переходы **вверх/вниз** и все прочие входы в комнату (старт уровня,
респавн, чит-навигация) остаются на полной инвалидации: там колонки не
сдвигаются, но тайлы под ними принадлежат другой комнате — история
действительно недействительна.
### Почему не дословно (вариант A)
Дословный порт — 10 слотов + параллельный массив номера комнаты, индекс по
колонке разрешённой комнаты, бамп только при совпадении номеров; тогда
`pop_coll_invalidate` не нужен вовсе, история сама «не совпадает» там, где
колонка сменила комнату.
Не взяли по одной причине: **десяти слотов нам не хватит**. Оригинал
перебирает узкое окно вокруг Кида (от `col(char_x_left_coll) 1` до
`col(char_x_right_coll) + 2`), поэтому коллизии слотов у него практически
не случаются. Мы держим четырнадцать колонок (−2…11) — при узком окне это
не мешает, а вот в десять слотов колонки −2/−1 и 8/9 сядут поверх 8/9.
**Окно перебора с 2026-08-09 у нас такое же, как в оригинале** (было: все
четырнадцать колонок каждый кадр). Признак годности слота при этом не
массив номеров комнат, как у оригинала, а ГРАНИЦЫ окна — четыре байта,
которые `move_coll_to_prev` переносит в `prev` вместе с флагами; сравнение
идёт по пересечению двух окон. Очистки массивов нет вовсе, то есть это
дешевле оригинала, а смысл тот же (у него слот вне окна помечен
`row_coll_room = 1` и в цикл бампа не попадает). `check_chomped_flags`
тоже ограничен окном — иначе протухшие слоты дали бы фантомный перемол.
### Чем платим
- Расхождение структур: если в будущем понадобится знать, из какой комнаты
пришёл тайл конкретного слота, этого у нас нет — придётся идти в вариант A.
- Границы окна надо переносить везде, где переносятся флаги: `pop_coll_shift`
двигает и их, `move_coll_to_prev` снимает их в `prev`. Забыть один из
переносов = молча потерять или, наоборот, разрешить лишний бамп.
- Сдвиг работает только для чисто горизонтальных переходов на ровно 10
колонок. Любая будущая диагональ/иная ширина комнаты его сломает молча.
- `coll_last_row`: `pop_coll_invalidate` прячет прошлый ряд, чтобы
`pop_coll_shift` мог отменить инвалидацию. Порядок вызовов в
`enter_room_side` (сначала `pop_map_set_edges`, потом `pop_coll_shift`)
стал значимым.
### Что проверять при регрессе
Это сердце коллизии, вокруг которого разбирался BUG-SEAM-PINGPONG. После
любой правки здесь — прогон швов:
1. Уровень 1, комнаты 6 ↔ 8, закрытая решётка, **обе** стороны.
2. Оба режима подхода: мелким шагом (упереться) и с разбега (не пройти
насквозь).
3. Проверить, что пинг-понг у шва не вернулся (экран не перескакивает
туда-сюда на кадре бампа о ворота).
4. `make -C roomtest/tests-host` — наборы `t_wall`/`t_char` ходят по этой же
геометрии.