Портировано опциональное исправление SDLPoP (fix_glide_through_wall, seg005 в do_fall). В ванили персонаж, падающий после разворота в беге, может оказаться внутри кладки и лететь «в стене» — баг оригинала, воспроизведённый пользователем в игре и затем на host-тесте. Решением 2026-08-31 фикс взят в ТЕКУЩИЙ билд: играбельность важнее буквальности. Реализация вынесена отдельной функцией glide_through_wall_guard() в pop_map.c намеренно — при разделении VANILLA/ENHANCED это готовая точка отвязки, достаточно не звать её в ванильном режиме. ПРОВЕРКА. Набор t_wall был заранее написан так, чтобы сторожить ЧИСЛО заходов в кладку: до фикса их было ровно два из четырнадцати стартовых позиций, после — ноль. Остальные 15 наборов (в том числе phys с 1733 проверками и grab) остались зелёными. Живая проверка в MAME пользователем: корректно. Ожидание в тесте обновлено ОСОЗНАННО, прежнее число сохранено рядом отдельной константой с пометкой «сколько было до фикса»: оно измерено, и понадобится, когда появится режим VANILLA — там ожидание станет зависеть от режима. ЦЕНА: +57 байт в банке 3 (свободно 3043), резидент и куча не изменились. По скорости попадание только на кадры падения: пересчёт колонки — одно деление, дистанция до кромки считается лишь если персонаж действительно внутри кладки. Документы: в аудите находка 21 переведена в «портировано» с сохранением исходного разбора; в vanilla_vs_bugfixed статус фикса стал ВЗЯТ, сводка пересчитана (5 взято, 32 кандидата в ENHANCED). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
20 KiB
Два поведения: VANILLA и BUGFIXED
Заведено 2026-08-31. Задача поставлена, работа НЕ начата.
Зачем
У оригинального PoP есть баги, которые игроки знают наизусть и на которых построены известные трюки. SDLPoP чинит их не молча, а ОПЦИОНАЛЬНО: каждое исправление отдельным переключателем, по умолчанию часть включена, часть нет. Мы до сих пор повторяли ваниль — сознательно, чтобы порт вёл себя как оригинал.
Задача: дать ДВА поведения на выбор, не размазывая условия по всему коду:
- VANILLA — как в оригинале 1989 года, со всеми его багами;
- BUGFIXED — с портированными исправлениями SDLPoP.
Что уже известно (из аудита)
Разбор расхождений — docs/sdlpop_audit.md. Оттуда прямо в эту задачу
переезжает следующее.
Опциональные фиксы SDLPoP, которых у нас НЕТ
Все три относятся к прохождению сквозь препятствия и живут в do_fall
(seg005) и рядом:
| фикс SDLPoP | что чинит |
|---|---|
| «скольжение сквозь стену» | Кид падает сквозь стены после разворота в беге, особенно под зельем медленного падения |
| «прыжок сквозь стену над воротами» | пролёт в тайл над воротами |
| «проваливание сквозь гобелен» | падение сквозь тайл гобелена |
Ни один не портирован — это и есть наше нынешнее VANILLA.
Находка 24 — отложена сюда
in_wall у нас пересчитывает только колонку, а оригинал перезагружает
ещё и данные кадра (load_fram_det_col, seg006). Правка сделана и
ОТКАЧЕНА 2026-08-31 по такой причине:
- по букве оригинала находка верна;
- но практического эффекта показать НЕ УДАЛОСЬ — все 15 наборов host-тестов дают одинаковый результат до и после, включая специально написанный тест на заход в кладку;
- при этом правка не бесплатна: добавляет маппинг окна и перезагрузку кадра на каждое выталкивание из стены.
Платить за недоказанное не стали. Вернуться к ней имеет смысл именно здесь: при работе над BUGFIXED появится сценарий, где кадр меняется перед выталкиванием, и тогда эффект станет наблюдаемым.
Готовый детектор поведения
tests/host/t_wall.c расширен с одной проверки до трёх. Ключевая —
wall_stops_jump_from_left_side: она НЕ требует нуля заходов в кладку, а
сторожит их ЧИСЛО (сейчас ровно два случая из четырнадцати стартовых
позиций).
Это и есть переключатель ожиданий между режимами:
- больше двух — правка сделала нас хуже ванили, регресс;
- ровно два — ведём себя как оригинал (режим VANILLA);
- меньше двух — кто-то портировал фикс; в режиме BUGFIXED тест должен ждать нуля.
То есть когда появится BUGFIXED, этому тесту понадобится ожидание, зависящее от режима, — и он готов стать первым таким.
Переключатель уже есть — второго не нужно
Уточнено 2026-08-31: в настройках игры ПЕРЕКЛЮЧАТЕЛЬ VANILLA/ENHANCED
СУЩЕСТВУЕТ (docs/menu_settings_plan.md), просто сейчас он жёстко
зафиксирован в положении VANILLA. Отдельную сущность заводить не надо —
эта задача про то, чтобы наполнить смыслом уже имеющееся положение
ENHANCED.
Наш «ванильный» билд уже не чистая ваниль. Часть ванильных багов у нас пофикшена по ходу портирования. Значит:
- нельзя считать текущее поведение эталоном ванили — оно смешанное;
- при разделении режимов придётся пройтись по уже сделанным фиксам и решить по каждому, остаётся он в VANILLA или уезжает в ENHANCED;
- и наоборот: отдельные исправления (например, падение сквозь стену) вполне могут быть сделаны прямо в нынешнем «ванильном» билде, если сочтём, что играбельность важнее буквальности.
Что предстоит решить
- Что считать ванилью на практике. Составить список уже сделанных отступлений от оригинала и распределить их между режимами.
- Цена рантайм-проверки. Условия попадают в физику и
play_seq, то есть в горячий путь. Если переключать в рантайме, проверка должна быть дешевле самого фикса: флаг в резиденте, а не вызов через банк. - Что считать умолчанием. Оригинальное поведение честнее для порта, но часть фиксов SDLPoP включает по умолчанию.
- Как тестировать оба режима. Host-тесты гоняются одним прогоном; для двух режимов нужен либо параметр сборки тестов, либо ожидания, зависящие от флага.
Инвентаризация: что уже решено по каждому фиксу SDLPoP
Составлено 2026-08-31 обходом кода. В движке эти решения УЖЕ приняты и задокументированы прямо в комментариях — таблица лишь сводит их в одно место, чтобы при разделении режимов не перечитывать исходники.
| фикс SDLPoP | где у нас | что взято |
|---|---|---|
fix_feather_fall_affects_guards |
pop_map.c:941 |
ФИКС — перо действует только на Кида |
fix_exit_door |
pop_map.c:1212 |
ФИКС — ветка фикса вместо ванильного глобала |
FIX_GATE_SOUNDS |
pop_trob.c:579 |
ФИКС — условия через ИЛИ |
fix_sound_priorities |
pop_sfx.c:196 |
ФИКС — в SDLPoP включён безусловно, сравниваемся с исправленным |
FIX_STAND_ON_THIN_AIR |
pop_map.c:1499 |
ВАНИЛЬ — взяты 2 части из 3, третья ждёт патча seqtbl |
fix_painless_fall_on_guard |
pop_map.c:1611 |
ВАНИЛЬ — намеренно |
fix_jumping_over_guard |
pop_map.c:1612 |
ВАНИЛЬ — намеренно |
FIX_RETREAT_WITHOUT_LEAVING_ROOM |
pop_map.c:3036 |
ВАНИЛЬ — в SDLPoP выключен по умолчанию; трюк 35 сохраняем |
fix_skeleton_chomper_blood |
pop_map.c:3177 |
ВАНИЛЬ — кровь скелета в ванили есть |
| потеря HP скелетом при падении с двух этажей | pop_map.c:676 |
ВАНИЛЬ — баг оригинала, сохраняем сознательно |
Фиксы, которым нужна правка байткода
FIX_STAND_ON_THIN_AIR не взят НЕ потому, что мы выбрали ваниль, а
потому что его нельзя применить наполовину: он состоит из трёх частей, и
третья — правка самого байткода seqtbl (смещения в последовательности
вставания). Взяв только первые две, мы получим вставание, уносящее
весовую точку в стену, то есть ХУЖЕ ванили.
Это выполнимо, и раньше здесь стояла неверная оценка (уточнено 2026-08-31): байткод можно менять и у нас. Способов три:
- Патч в рантайме — после загрузки
kid.aniв EMM-страницу пропатчить нужные байты прямо там. Речь о единицах байт, страница уже наша, и патч обратим — то есть режим переключается без пересборки ресурсов. Это и делает фикс пригодным для VANILLA/ENHANCED. - Патч в упаковщике — готовить два варианта
kid.ani. Привязывает режим к файлам на диске, поэтому хуже: переключатель в меню перестаёт быть чисто кодовым. - Две копии в одной странице — и это, пожалуй, лучший вариант
(решено 2026-08-31).
kid.aniцеликом около 4 КБ, а страница EMM — 16 КБ, то есть обе версии байткода спокойно помещаются рядом в уже выделенной странице. Переключение режима сводится к смене базового смещения, патчить ничего не нужно, откат мгновенный.
Для сравнения: у SDLPoP рабочая таблица и неизменная копия оригинала
существуют раздельно (seqtbl и original_seqtbl), причём вторая нужна
для сверки — то есть сама идея «оригинальный байткод отдельно, рабочий
отдельно» там уже заложена.
Общее правило для BUGFIXED: фиксы, требующие правки seqtbl, доступны
через рантайм-патч страницы; закладывать это стоит сразу, чтобы не
упереться при первом же таком фиксе.
Вывод для разделения режимов
Четыре фикса уже взяты, шесть позиций оставлены ванильными. Значит нынешний билд — это не VANILLA, а «ваниль плюс четыре исправления». При разделении:
- взятые четыре надо либо оставить в обоих режимах (если считаем их безусловными улучшениями), либо увести в ENHANCED и вернуть ванильное поведение в VANILLA — второе честнее, но потребует обратной работы;
- ванильные шесть — кандидаты в ENHANCED;
FIX_STAND_ON_THIN_AIRтоже, но ему дополнительно нужен рантайм-патч байткода.
ВСЕ исправления SDLPoP и их статус у нас
Полный перечень опциональных исправлений оригинала, какие есть в SDLPoP (43 позиции), со статусом в нашем порте. Названия — идентификаторы опций SDLPoP, описание — своими словами.
Статусы: ВЗЯТ — портирован; ВАНИЛЬ — сознательно не берём, держим
поведение оригинала; НЕТ — не реализован, кандидат в ENHANCED;
НЕДОСТУПЕН — требует правки байткода seqtbl (см. ограничение выше);
В РАБОТЕ — решено делать сейчас.
Стены и препятствия
| опция | что чинит | статус |
|---|---|---|
fix_glide_through_wall |
проход сквозь стену при падении после разворота в беге | ВЗЯТ 2026-08-31 — glide_through_wall_guard() в pop_map.c, точка отвязки для VANILLA |
fix_jump_through_wall_above_gate |
прыжок в тайл над воротами | НЕТ |
fix_drop_through_tapestry |
проваливание сквозь гобелен | НЕТ |
fix_running_jump_through_tapestry |
прыжок с разбега сквозь гобелен | НЕТ |
fix_turn_running_near_wall |
разворот в беге вплотную к стене | НЕТ |
fix_wall_bump_triggers_tile_below |
удар о стену срабатывает на тайл ниже | НЕТ |
fix_bigpillar_climb |
подъём на большую колонну | НЕТ |
fix_land_against_gate_or_tapestry |
приземление вплотную к воротам или гобелену | НЕТ |
fix_caped_prince_sliding_through_gate |
проскальзывание сквозь ворота | НЕТ |
Падение, прыжки, зацепы
| опция | что чинит | статус |
|---|---|---|
fix_stand_on_thin_air |
стояние на воздухе после отмены падения | НЕТ — нужен рантайм-патч seqtbl (см. выше) |
fix_jump_distance_at_edge |
дальность прыжка у самой кромки | НЕТ |
fix_edge_distance_check_when_climbing |
проверка расстояния до кромки при подъёме | НЕТ |
fix_grab_falling_speed |
зацеп на слишком большой скорости падения | НЕТ |
fix_drop_2_rooms_climbing_loose_tile |
провал через две комнаты при подъёме на шаткой плите | НЕТ |
fix_infinite_down_bug |
бесконечное падение вниз | НЕТ |
fix_falling_through_floor_during_sword_strike |
провал сквозь пол во время удара мечом | НЕТ |
fix_safe_landing_on_spikes |
безопасное приземление на невыдвинутые пики | НЕТ |
fix_dead_floating_in_air |
мёртвый зависает в воздухе | НЕТ |
Бой и стражи
| опция | что чинит | статус |
|---|---|---|
fix_painless_fall_on_guard |
падение на стража с высоты без урона | ВАНИЛЬ |
fix_jumping_over_guard |
перепрыгивание через стража | ВАНИЛЬ |
fix_skeleton_chomper_blood |
кровь скелета в челюстях | ВАНИЛЬ |
fix_push_guard_into_wall |
вталкивание стража в стену | НЕТ |
fix_guard_following_through_closed_gates |
страж идёт сквозь закрытые ворота | НЕТ |
fix_doortop_disabling_guard |
верх двери отключает стража | НЕТ |
fix_offscreen_guards_disappearing |
стражи пропадают за краем экрана | НЕТ |
fix_unintended_sword_strike |
непреднамеренный удар мечом | НЕТ |
fix_two_coll_bug |
двойная проверка столкновения | НЕТ |
fix_move_after_sheathe |
движение сразу после убирания меча | НЕТ |
Ворота, двери, плиты
| опция | что чинит | статус |
|---|---|---|
fix_exit_door |
дверь выхода с уровня | ВЗЯТ |
fix_gate_sounds |
звуки ворот | ВЗЯТ |
fix_gate_drawing_bug |
отрисовка ворот | НЕТ |
fix_press_through_closed_gates |
нажатие плиты сквозь закрытые ворота | НЕТ |
fix_chompers_not_starting |
челюсти не заводятся | НЕТ |
fix_loose_left_of_potion |
шаткая плита слева от зелья | НЕТ |
fix_hidden_floors_during_flashing |
скрытые полы во время вспышки | НЕТ |
fix_retreat_without_leaving_room |
отступление без смены комнаты (трюк 35) | ВАНИЛЬ |
Зелья, перо, спецэффекты
| опция | что чинит | статус |
|---|---|---|
fix_feather_fall_affects_guards |
перо действует и на стражей | ВЗЯТ |
fix_feather_interrupted_by_leveldoor |
перо прерывается дверью уровня | НЕТ |
fix_move_after_drink |
движение сразу после питья | НЕТ |
fix_quicksave_during_feather |
быстрое сохранение под пером | НЕТ |
fix_hang_on_teleport |
зависание при телепорте | НЕТ (телепортов у нас нет) |
Интерфейс и ввод
| опция | что чинит | статус |
|---|---|---|
fix_one_hp_stops_blinking |
индикатор перестаёт мигать на одном HP | НЕТ |
fix_register_quick_input |
учёт быстрого ввода | НЕТ |
Отдельно: приоритеты звуков
fix_sound_priorities в SDLPoP включён БЕЗУСЛОВНО (не опция), и мы
сравниваемся с исправленным вариантом — pop_sfx.c:196. Статус:
ВЗЯТ, вернуть ванильное поведение отдельным режимом было бы
дополнительной работой.
Сводка
| статус | сколько |
|---|---|
| ВЗЯТ | 5 |
| ВАНИЛЬ (сознательно) | 4 |
требует патча seqtbl (выполнимо) |
1 |
| НЕТ (кандидаты в ENHANCED) | 32 |
Список кандидатов на BUGFIXED
Пополняется по мере аудита. Пока:
- три опциональных фикса SDLPoP выше;
- находка 24 (перезагрузка кадра в
in_wall); - находка 21 из аудита — общая рамка «мы намеренно повторяем ваниль».
Не относятся сюда находки, где мы расходимся с оригиналом НЕ в его пользу (ранги А и Б аудита): их надо чинить в обоих режимах, потому что это не баги оригинала, а наши.