SprPoP: глубокое ревью находок А/Б — исправимость и цена по скорости
КОД НЕ МЕНЯЛСЯ. Разбор одиннадцати находок рангов А и Б: что менять, во что это обойдётся по скорости и памяти, каков риск. СНЯТО ГЛАВНОЕ ПРЕПЯТСТВИЕ. Обоснование двух упрощений (guards.c:1011 — «нужны тайловые запросы от Char, а pop_map умеет только от Kid») УСТАРЕЛО: get_tile_at_char, get_tile_infrontof_char, get_tile_behind_char и distance_to_edge_weight в pop_map.c уже работают от Char, они лишь не выведены в заголовок. Данные тоже на месте — pop_char_set_seq ставит любую из 115 последовательностей, то есть seq_81 и seq_64 доступны без единого нового байта. Три находки упираются не в архитектуру, а в четыре строки объявлений. СКОРОСТЬ. Места классифицированы по частоте вызова: play_seq и ИИ стража — горячие, land/in_wall/bumped/hurt_by_sword — событийные. Из одиннадцати правок две УСКОРЯЮТ код (уходит условие из горячего цикла; звук перестаёт играть в двух случаях из трёх), большинство бесплатны (перестановка строк), и ни одна не требует переделки архитектуры. Единственный конфликт со скоростью — отложенная побудка чомперов: play_seq маппит страницу байткода в W0 один раз перед циклом, и звать start_chompers внутри цикла значило бы снимать и возвращать окно на каждый переход ряда. Дешёвая замена: копить не один флаг, а битовую маску рядов и разбудить их после цикла — теряться ряды перестанут, цена в цикле нулевая. Для стражей аналогично: не межбанковый вызов wall_type, а копия таблицы в 32 байта в своём банке. Порядок работ — от «одна-две строки, низкий риск» (13, 24) к тем, где правка может компенсировать наши отличия в другом месте (1, 7). Политика: для критичных фиксов скорость не вето — такие выносятся в отдельный разбор с поиском дешёвого способа. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
This commit is contained in:
@@ -547,3 +547,165 @@
|
||||
слоёв, `clip_char`.
|
||||
4. **`do_fall`/`check_bumped`/`check_grab`** — остаток физики.
|
||||
5. **ИИ стражей** и особенности скелета/Тени/Джафара.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Глубокое ревью находок А и Б: можно ли починить и чем платим
|
||||
|
||||
> 2026-08-31. КОД ПО-ПРЕЖНЕМУ НЕ МЕНЯЛСЯ. Здесь только оценка.
|
||||
>
|
||||
> **Главное ограничение (требование пользователя): фикс не должен заметно
|
||||
> замедлять игру.** Поэтому у каждой находки первым делом указана ЧАСТОТА
|
||||
> вызова места, а уже потом сама правка.
|
||||
>
|
||||
> **Оговорка к ограничению:** для КРИТИЧНЫХ фиксов скорость — не вето.
|
||||
> Если такой фикс всерьёз бьёт по производительности, он выносится в
|
||||
> отдельный разбор, где ищется способ получить правильное поведение
|
||||
> дёшево (иной момент вызова, кэш, предвычисление, перенос в холодный
|
||||
> путь). То есть порядок такой: сначала решаем, критично ли поведение, и
|
||||
> только потом — какой ценой его добиться.
|
||||
|
||||
## Снятое препятствие
|
||||
|
||||
Обоснование сразу двух упрощений (`guards.c:1011` — «нужны тайловые
|
||||
запросы ОТ Char, а pop_map умеет только от Kid») **устарело**. Проверено:
|
||||
`get_tile_at_char`, `get_tile_infrontof_char`, `get_tile_behind_char` и
|
||||
`distance_to_edge_weight` в `pop_map.c` УЖЕ работают от `Char`
|
||||
(строки 460, 465, 477, 561). Они лишь не выведены в `pop_map.h`.
|
||||
|
||||
Так же обстоит с данными: `pop_char_set_seq()` ставит любую из 115
|
||||
последовательностей по индексу, то есть `seq_81` и `seq_64` доступны без
|
||||
единого нового байта данных — таблица генерируется из оригинала целиком.
|
||||
|
||||
То есть три находки (12, 14, 20) упираются не в архитектуру, а в четыре
|
||||
строки объявлений.
|
||||
|
||||
## Классификация мест по частоте вызова
|
||||
|
||||
| место | частота | вывод |
|
||||
|---|---|---|
|
||||
| `play_seq` (находки 7, 8) | КАЖДЫЙ кадр каждого персонажа | правка обязана быть бесплатной |
|
||||
| `check_hurting` (18) | каждый кадр боя, дважды | почти горячий |
|
||||
| ИИ стража (20) | каждый кадр, пока страж активен | почти горячий |
|
||||
| `land`, `in_wall`, `bumped` (1, 2, 3, 14, 24) | событие раз в несколько секунд | холодный, цена не важна |
|
||||
| `hurt_by_sword` (12, 13) | момент попадания | холодный |
|
||||
|
||||
## Разбор по находкам
|
||||
|
||||
### 12 + 13 (ранг А) — смерть на краю. Чинится, цена нулевая
|
||||
|
||||
*Место:* `guards.c`, `hurt_by_sword` — холодный путь.
|
||||
|
||||
*Правка:* (а) перенести две строки прижатия к полу внутрь ветки
|
||||
выжившего удара — это чистое перемещение, минус ноль байт; (б) добавить
|
||||
ветку выбора `seq_81` по тайлу позади и расстоянию до кромки.
|
||||
|
||||
*Что нужно:* экспорт `get_tile_behind_char()` и `distance_to_edge_weight()`
|
||||
из `pop_map.c` в `pop_map.h` как `__banked`.
|
||||
|
||||
*Цена скорости:* два межбанковых вызова (`guards.c` — банк 1, `pop_map.c`
|
||||
— банк 3) в момент попадания мечом, то есть несколько раз за бой.
|
||||
Незаметно.
|
||||
|
||||
*Цена памяти:* банк 1 занят на 19,8 % (13 142 Б свободно) — места вдоволь;
|
||||
банк 3 занят на 81,1 % (3 100 Б), но там прибавятся только две обёртки.
|
||||
|
||||
*Риск:* низкий. Ветка симметрична существующей, данные есть.
|
||||
|
||||
### 1 (ранг А) — лишний `determine_col()` в `land`
|
||||
|
||||
*Место:* холодный путь. *Правка:* убрать вызов и проверить, не
|
||||
понадобился ли он нам вместо оригинального `load_fram_det_col`, который
|
||||
оригинал делает в другом месте цепочки. *Цена:* отрицательная (кода
|
||||
меньше). *Риск:* СРЕДНИЙ — вызов мог компенсировать наш иной порядок
|
||||
загрузки кадра; убирать только с прогоном сцен падения и приземления.
|
||||
|
||||
### 2, 3 (ранг Б) — `land`: `fall_x` и порядок проверки пик
|
||||
|
||||
*Место:* холодный. *Правка 2:* сбрасывать только `fall_y` и после
|
||||
`play_seq`, как оригинал. *Правка 3:* перенести проверку пик после
|
||||
коррекции X. *Цена:* нулевая, это перестановка строк. *Риск:* низкий,
|
||||
но обе меняют поведение на краю тайла — нужны прогоны с пиками.
|
||||
|
||||
### 24 (ранг Б) — `in_wall` не перезагружает кадр. Одна строка
|
||||
|
||||
*Место:* холодный. *Правка:* заменить `determine_col()` на
|
||||
`pop_load_fram_det_col()` — он УЖЕ экспортирован (`pop_kid.h:109`) и, что
|
||||
важно, НЕ банковый, то есть вызов прямой. *Цена скорости:* одна
|
||||
перезагрузка кадра при выталкивании из стены — доли процента кадра.
|
||||
*Риск:* низкий; это возврат к оригиналу.
|
||||
|
||||
### 14 (ранг Б) — нет `seq_64`
|
||||
|
||||
*Место:* `bumped`, холодный. *Правка:* добавить выбор между 64 и 65 по
|
||||
направлению толчка. *Цена:* нулевая. *Риск:* низкий.
|
||||
|
||||
### 18 (ранг Б) — звук удара
|
||||
|
||||
*Место:* `check_hurting` — дважды за кадр боя. *Правка:* перенести звук
|
||||
в конец функции и обвесить тремя условиями оригинала. *Цена:*
|
||||
ОТРИЦАТЕЛЬНАЯ — звук перестанет играть в двух случаях из трёх, то есть
|
||||
уменьшится и число обращений к звуковой очереди. *Риск:* низкий.
|
||||
|
||||
### 20 (ранг Б) — «стена» у стражей. Требует осторожности со скоростью
|
||||
|
||||
*Место:* ИИ стража — вызывается каждый кадр, пока страж активен.
|
||||
|
||||
*Плохой вариант:* экспортировать `wall_type` из `pop_map.c` и звать из
|
||||
`guards.c`. Это МЕЖБАНКОВЫЙ вызов (банк 1 → банк 3) в почти горячем
|
||||
пути — трамплин с переключением W3 на каждый шаг ИИ. Против требования
|
||||
по скорости.
|
||||
|
||||
*Хороший вариант:* завести копию таблицы `wall_type_tbl` (32 байта) в
|
||||
rodata банка 1 и обращаться к ней напрямую — стоимость чтения байта,
|
||||
ноль переключений банка. Дублирование данных здесь оправдано: таблица
|
||||
константная и вшита в формат уровней.
|
||||
|
||||
*Риск:* низкий, но нужно следить, чтобы копия не разошлась с оригиналом —
|
||||
лучше генерировать обе из одного места или снабдить перекрёстным
|
||||
комментарием.
|
||||
|
||||
### 7 (ранг Б) — перо только для Кида. Правка ускоряет
|
||||
|
||||
*Место:* `play_seq`, самый горячий путь. *Правка:* убрать лишнее условие
|
||||
по `charid`. *Цена:* ОТРИЦАТЕЛЬНАЯ — из горячего цикла уходит сравнение.
|
||||
*Риск:* средний: надо убедиться, что физика пера у нас применяется к
|
||||
любому персонажу так же, как в оригинале, иначе анимация разойдётся с
|
||||
физикой.
|
||||
|
||||
### 8 (ранг Б) — отложенные чомперы. Чинить ДЕШЁВЫМ способом
|
||||
|
||||
*Место:* `play_seq`, горячий путь.
|
||||
|
||||
*Почему отложено:* `play_seq` маппит страницу байткода в окно W0 ОДИН раз
|
||||
перед циклом (`pop_kid.c:284`) и снимает после (`:389`). Вызвать
|
||||
`start_chompers` внутри цикла — значит снять окно, позвать, вернуть окно,
|
||||
и так на каждый переход ряда. Это прямая деградация горячего пути и
|
||||
против требования по скорости.
|
||||
|
||||
*Дешёвая замена:* сейчас копится ОДИН флаг, из-за чего теряются
|
||||
промежуточные ряды. Достаточно копить не флаг, а НОМЕРА рядов — один
|
||||
байт-битовую маску (рядов всего 0..3) плюс запомненную колонку. После
|
||||
цикла пройти по взведённым битам и разбудить чомперов в каждом. Цена в
|
||||
цикле: одна операция «выставить бит» вместо присваивания флага, то есть
|
||||
ноль. Разница с оригиналом останется только в МОМЕНТЕ побудки (после
|
||||
цикла, а не внутри), но ряды перестанут теряться.
|
||||
|
||||
*Риск:* низкий. Полное совпадение с оригиналом здесь недостижимо без
|
||||
потери скорости — это осознанный компромисс, который надо записать в
|
||||
`docs/impl_diff.md`.
|
||||
|
||||
## Сводка: что делать в каком порядке
|
||||
|
||||
| приоритет | находки | почему |
|
||||
|---|---|---|
|
||||
| 1 | 13, 24 | по одной-две строки, риск низкий, оба в узле «смерть/стена» |
|
||||
| 2 | 12, 14 | нужен экспорт двух функций; закрывают наблюдения пользователя |
|
||||
| 3 | 18, 8 | обе УСКОРЯЮТ или бесплатны; 8 — по дешёвому варианту |
|
||||
| 4 | 2, 3, 20 | перестановки и копия таблицы; нужны прогоны |
|
||||
| 5 | 1, 7 | риск средний: обе могут компенсировать наши отличия в другом месте |
|
||||
|
||||
**Ни один фикс не требует переделки архитектуры и ни один не ложится на
|
||||
горячий путь с накладными расходами** — при условии, что находка 20
|
||||
делается копией таблицы, а находка 8 — битовой маской рядов.
|
||||
|
||||
Reference in New Issue
Block a user