DRAW-CHAR: отрисовка одна на всех Char; разгрузка банка 2 (90.4% -> 72.9%)
DRAW-CHAR. Отрисовка персонажа сведена к одному набору функций над Char — как физика после GUARD-PHYS. В оригинале add_kid_to_objtable (seg008:22F0) и add_guard_to_objtable (seg008:2324) имеют идентичное тело и различаются окном (loadkid/loadshad), набором спрайтов и типом объекта, а redraw_at_char/redraw_at_char2 гейтов по charid не имеют вовсе. pop_gdraw.c -> pop_cdraw.c: pop_char_draw/heal/fore(who), слот POP_CH_KID / POP_CH_OPP; состояние слотов pop_cd[] в _DATA — читается из любого банка без трамплина. Проход окклюзии тоже один (pop_fore_over_char), pop_fore_over_kid больше нет. Починилось само (расхождения, которые и были ценой дублирования): у соперника не было clip_char; у Кида не было клипа полем 192 и ветки брызг «мёртв/падение»; char_width_half СТРАЖА считался по спрайту КИДА. Замер: _CODE 24 881 -> 20 524 (куча 2023 -> 6333), BANK2 -265, итого -3.2 КБ. Проверено пользователем в MAME; циан-полоса профиля подросла — оптимизация заведена отдельной задачей DRAW-COST. MEM-BANK2, шаг 1: общие «листья» слоя фона в РЕЗИДЕНТ (pop_tile.c/.h). Ограничение платформы: писучие данные банка лежат в _DATA и видны всем, а const-таблицы — в странице банка, из другого банка их не прочитать; трамплин же выбирается объявлением, то есть __banked на листе бьёт и по горячим вызывающим (654 такта). W1 замаплено всегда — оттуда обе половины зовут листья прямым call и читают таблицы напрямую. MEM-BANK2, шаг 2: дедуп внутри банка. wall_pattern 808 -> 394 и wall_rnd 786 -> 654: четыре ветки по виду стены отличались только набором кусков и числами в одной серии prandom — сведены к таблицам WP_PARTS и WR_RULE, порядок вызовов prandom сохранён дословно. Заодно: kid_seq_off больше не static const в kid_data.h (230 Б мёртвой копии в каждом из 9 модулей) — генератор pop_extract_kid_data.py отдаёт макро-инициализатор, массив определяет один pop_kid.c. Итог: BANK2 14 815 -> 11 942 (72.9 %, свободно 4442 Б), _CODE 22 556, куча 4301 Б. tests-host зелёные (65/39/53/1723/1); в MAME комната 1 совпала с дорефакторным снимком попиксельно (0 из 227 520), комната 3 — та же раскладка кладки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# roomtest — доска ОТКРЫТЫХ задач (обновлено 2026-08-07)
|
||||
# roomtest — доска ОТКРЫТЫХ задач (обновлено 2026-08-08)
|
||||
|
||||
Что берём в работу сейчас и в каком порядке. Каждая запись: что сделать,
|
||||
почему именно сейчас, чем подтверждать результат.
|
||||
@@ -12,9 +12,18 @@
|
||||
**Состояние на 2026-08-07:** пользователь прогнал ВСЕ комнаты уровней 1 и 2 —
|
||||
крупных багов нет. Приёмки [L1-PASS](TASKS_CLOSED.md#l1-pass) и
|
||||
[L2-PASS](TASKS_CLOSED.md#l2-pass) закрыты; с прогона открыты три записи по
|
||||
уровню 2 (цвет стража, брызги по стражу, чит `+`/`−` в бою) — все в
|
||||
[`bug_list.md`](bug_list.md). Уровень 3 играется, но приёмки не было: сначала
|
||||
чомперы и скелет.
|
||||
уровню 2, из них цвет стража и брызги уже закрыты (см.
|
||||
[`bug_closed.md`](bug_closed.md)); открытым остался чит `+`/`−` в бою.
|
||||
Уровень 3: **скелет сделан и проверен в MAME 2026-08-07** (бой, падение в
|
||||
пропасть, окклюзия), чомперов ещё нет.
|
||||
|
||||
**Разгрузка банка 2 сделана 2026-08-08** ([MEM-BANK2](#mem-bank2)): 90.4 % →
|
||||
**72.9 %, свободно 4442 Б** — чомперам места хватает.
|
||||
|
||||
**DRAW-CHAR сделана и ПРОВЕРЕНА 2026-08-08** (протокол и замеры —
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#draw-char)): отрисовка теперь одна на
|
||||
всех `Char`. Банку 2 она дала всего +265 Б — место под чомперов дала уже
|
||||
[MEM-BANK2](#mem-bank2).
|
||||
|
||||
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
||||
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
|
||||
@@ -40,9 +49,11 @@
|
||||
| # | Задача | Что | Блокирует |
|
||||
|---|--------|-----|-----------|
|
||||
| 1 | [GUARD-PHYS](#guard-phys) | физика стража = физика Кида (одна над `Char`) | **ядро сделано 2026-08-07**; открыт живой сценарий в MAME |
|
||||
| 2 | [L3-CHOMP](#l3-chomp) | чомперы (5 шт) | прохождение ур. 3 |
|
||||
| 3 | [L3-SKEL](#l3-skel) | скелет (единственный противник ур. 3) | прохождение ур. 3 |
|
||||
| 4 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||
| — | [DRAW-CHAR](TASKS_CLOSED.md#draw-char) | отрисовка одна на всех `Char` — **закрыта и проверена 2026-08-08** | — |
|
||||
| — | [MEM-BANK2](#mem-bank2) | разгрузка банка 2 — **сделана 2026-08-08**: 90.4 % → 72.9 %, свободно 4442 Б | — |
|
||||
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
||||
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
||||
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
|
||||
@@ -173,6 +184,66 @@
|
||||
скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ
|
||||
живой страж — переход не происходит.
|
||||
|
||||
### <a id="mem-bank2"></a>MEM-BANK2. Разгрузка банка 2 — **шаги 1-2 сделаны 2026-08-08**
|
||||
|
||||
Банк 2 (`pop_bg.c`) упирался в потолок: 90.8 % ещё до чомперов. Разбор по
|
||||
символам показал, что он держит ДВЕ разные вещи — горячий fore-проход (каждый
|
||||
кадр) и холодную отрисовку тайлов (вход в комнату, точечные перерисовки), —
|
||||
а общие «листья» (`blit_b`, `tile_code`, `tile_table`, `wall_pattern`) нужны
|
||||
обеим.
|
||||
|
||||
**Ключевое ограничение платформы** (записано в memory
|
||||
`sdcc_banked_call_rules`): писучие данные банка лежат в `_DATA`/W2 и видны
|
||||
всем, а `const`-таблицы — в СТРАНИЦЕ банка, и из другого банка их не
|
||||
прочитать вовсе. Плюс трамплин выбирается ОБЪЯВЛЕНИЕМ: пометить лист
|
||||
`__banked` — значит заставить платить и горячих вызывающих (654 такта на
|
||||
круг).
|
||||
|
||||
**Шаг 1 — общие листья в РЕЗИДЕНТ** (`pop_tile.c` + внутренний `pop_tile.h`).
|
||||
W1 замаплен всегда, поэтому и горячая половина, и будущая холодная зовут их
|
||||
прямым `call` и читают таблицы напрямую — без дублей и без трамплинов.
|
||||
Уехали: `blit_b`, `env_b/wall_b/fore_b/pot_b`, `tile_code/tile_mod/
|
||||
tile_code_drawn/wall_modifier`, `heal_off`, `get_loose_frame`,
|
||||
`potion_flask`, `pop_fore_set_clip`, `pop_room_set_below/above` + таблицы
|
||||
`tile_table`, `COL_XH`, `WALL_FRAM_*`, `SPIKES_FRAM_LEFT`,
|
||||
`LOOSE_FRAM_LEFT/BOTTOM`. Заодно три функции перестали быть `__banked` —
|
||||
минус трамплин на кадр.
|
||||
|
||||
**Шаг 2 — дедуп внутри банка.** `wall_pattern` (808 → 394) и `wall_rnd`
|
||||
(786 → 654): четыре ветки по виду стены (SWS/SWW/WWS/WWW) отличались ТОЛЬКО
|
||||
набором кусков и числами в одной и той же серии `prandom` — сведены к
|
||||
таблицам `WP_PARTS` и `WR_RULE`. Порядок вызовов `prandom` сохранён
|
||||
дословно (иначе разъедется раскладка кладки).
|
||||
|
||||
**Грабля, пойманная замером:** после выноса `tile_table` из `static const` в
|
||||
глобальную SDCC перестала сворачивать `tile_table[10].fore_x/fore_y` в
|
||||
константы и стала читать их в рантайме — `potion_flask` раздулся 115 → 265 Б.
|
||||
Лечится макросами, которыми инициализируется та же запись таблицы (одно место
|
||||
правки, дублирования данных нет): 265 → 230.
|
||||
|
||||
**Замер (ALLOCS=3000):**
|
||||
|
||||
```
|
||||
было после ш.1 после ш.2
|
||||
BANK2 14 815 12 460 11 942 (90.4 % -> 72.9 %, свободно 4442 Б)
|
||||
_CODE 20 064 22 591 22 556 (куча 6793 -> 4301 Б)
|
||||
```
|
||||
|
||||
С релизным `ALLOCS=100000` банк 2 будет ещё примерно на 500-600 Б меньше
|
||||
(замер до разгрузки: 14 815 -> 14 176).
|
||||
|
||||
**Проверено:** `tests-host` зелёные; в MAME комната 1 уровня 1 после дедупа
|
||||
совпала с дорефакторным снимком **попиксельно (0 различий из 227 520)**,
|
||||
комната 3 (сплошная кладка, все четыре вида стены и марки) — та же раскладка
|
||||
кирпича.
|
||||
|
||||
**Что осталось в запасе, если места снова не хватит** (не делаем, пока нет
|
||||
причины): вынести ХОЛОДНУЮ половину (`draw_tile` 1800 + точечные
|
||||
перерисовки 1438 + падающие плиты 1898 ≈ 5.1 КБ) в свободный банк 7. Теперь
|
||||
это дёшево: листья уже в резиденте, а `wall_pattern`/`wall_rnd`/марки/
|
||||
`draw_gate_back` нужны обеим половинам — им понадобится тонкая `__banked`-
|
||||
обёртка (тело остаётся непомеченным, горячие вызывающие платят ноль).
|
||||
|
||||
### <a id="l3-chomp"></a>L3-CHOMP. Чомперы — механика уровня 3
|
||||
|
||||
**Где:** 5 штук, комнаты 5(тайл 4), 16(23,24,25), 22(26). Дальше они почти
|
||||
@@ -183,10 +254,10 @@
|
||||
коллизия и смерть Кида в сомкнутых челюстях (`seg004`/`seg006`); отрисовка
|
||||
кадра по модификатору (`draw_tile_anim`, ветка `tiles_18_chomper`).
|
||||
|
||||
**Риск:** банк 2 (`pop_bg`) занят на **90.8 %, свободно 1503 Б** (замер
|
||||
2026-08-07, было 2213 Б) — считать
|
||||
место ДО кодинга (`levels_plan.md` §5.1), иначе повторится «банк 2 упёрся
|
||||
в потолок» (коммит 2f3e854). Свободные номера банков есть (5+).
|
||||
**Место в банке 2 расчищено** ([MEM-BANK2](#mem-bank2), 2026-08-08): 72.9 %,
|
||||
**свободно 4442 Б** (было 1074 Б). Считать место ДО кодинга всё равно
|
||||
обязательно (`levels_plan.md` §5.1), и если не хватит — в запасе вынос
|
||||
холодной половины `pop_bg` в банк 7, там расписано как.
|
||||
|
||||
**Ассеты УЖЕ упакованы (2026-08-07):** `pop_pack_bg.py` кладёт в атлас весь
|
||||
набор кадров чомпера явно (`CHOMPER_BOT_IDS` 101-105, `CHOMPER_TOP_IDS`
|
||||
@@ -301,60 +372,36 @@ offset 22790, **240 байт** = 5 палитр × 16 цветов × 3 байт
|
||||
таблицу нельзя передавать прямо из банка (0xC000+), надо копировать в
|
||||
W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
||||
|
||||
### <a id="draw-char"></a>DRAW-CHAR. Отрисовка — ОДНА на всех Char, как физика после GUARD-PHYS
|
||||
### <a id="draw-cost"></a>DRAW-COST. Стоимость fore-прохода после DRAW-CHAR
|
||||
|
||||
**Почему заведено (пользователь, 2026-08-07).** После GUARD-PHYS физика стала
|
||||
общей над `Char`, а ОТРИСОВКА так и осталась продублированной: `kid_draw` и
|
||||
`pop_guard_draw` считают `load_frame_to_obj`, флип и heal-прямоугольники
|
||||
каждая по-своему, окклюзия — двумя проходами (`pop_fore_over_kid` /
|
||||
`pop_fore_over_char`). Общее вынесено лишь частично (`char_footprint`,
|
||||
`other_overlay_tile`, `pop_sword_draw`), а СПИСКИ ВЫЗОВОВ разные.
|
||||
**Наблюдение пользователя на приёмке 2026-08-08:** циан-полоса профиля
|
||||
(спрайты + fore) выросла — «в пределах, но оптимизация отдельной задачей».
|
||||
|
||||
**В оригинале она общая — проверено.** `seg008` держит два входа с
|
||||
ИДЕНТИЧНЫМ телом:
|
||||
**Отчего именно.** Раньше перебор тайлов у Кида шёл строго по футпринту
|
||||
кадра (`char_x_left/right`, seg006:1021), а он УЖЕ спрайта; расширение
|
||||
перебора окном спрайта (клинок и брызги уходят за габарит кадра) было
|
||||
только у соперника. DRAW-CHAR сделала его общим — то есть у Кида теперь на
|
||||
колонку-другую больше `fore_tile` за кадр. Это не регрессия «лишней
|
||||
работы», а недостающая ранее корректность: тайл, который спрайт задевает,
|
||||
обязан вернуть свой передний слой поверх него.
|
||||
|
||||
```
|
||||
add_kid_to_objtable (seg008:22F0) add_guard_to_objtable (seg008:2324)
|
||||
loadkid() loadshad()
|
||||
load_fram_det_col() load_fram_det_col()
|
||||
load_frame_to_obj() load_frame_to_obj()
|
||||
stuck_lower() stuck_lower()
|
||||
set_char_collision() set_char_collision()
|
||||
set_objtile_at_char() set_objtile_at_char()
|
||||
redraw_at_char() redraw_at_char()
|
||||
redraw_at_char2() redraw_at_char2()
|
||||
clip_char() clip_char()
|
||||
add_objtable(0) add_objtable(1 тень / 2 страж)
|
||||
```
|
||||
**Куда копать, когда возьмёмся** (сначала МЕРИТЬ, потом резать —
|
||||
[`pop_fore_layer_cost`](../../../docs) уже показывал 78 % кадра до кэша
|
||||
кладки):
|
||||
1. расширять перебор ТОЛЬКО по накладным спрайтам (клинок/брызги), а не по
|
||||
всему окну: габарит самого кадра уже покрыт футпринтом + правилом меча;
|
||||
2. кэш «в этом тайле fore-слоя нет вовсе» — половина колонок пустые, а за
|
||||
каждую платим `tile_in_fclip` + разбор кладки;
|
||||
3. считать окно клипа в тайловых координатах один раз, а не сравнивать
|
||||
пиксельные габариты в двух циклах.
|
||||
|
||||
Различаются ТОЛЬКО окно (`Kid`/`Guard`), тип объекта и спецкейс тени у
|
||||
зеркала (уровень 4). То есть та же схема, что `play_kid_frame` /
|
||||
`play_guard_frame` у физики.
|
||||
### <a id="draw-char"></a>DRAW-CHAR — **ЗАКРЫТА И ПРОВЕРЕНА 2026-08-08**
|
||||
|
||||
**Цена дублирования уже видна.** Скелет, падающий в пропасть (ур. 3),
|
||||
рисовался ПОВЕРХ верхней грани пола соседней колонки: проход
|
||||
`other_overlay_tile` (порядок midtable — «тайлы позже персонажа ложатся
|
||||
поверх него») вызывался только у Кида. Симптом закрыт 2026-08-07
|
||||
переносом прохода в `pop_fore_over_char`, но корень — именно дублирование.
|
||||
Раньше это не всплывало потому, что ОБЫЧНЫЙ страж в пропасть не падает: его
|
||||
физика гейтится `x ∈ [44,211)`, а провалившегося убирает
|
||||
`check_guard_fallout`. Тем же путём пойдут тень (ур. 4/5/6/12), визирь
|
||||
(13), толстяк (12) — расхождение будет повторяться на каждом.
|
||||
|
||||
**Что сделать:** свести к одному набору функций над `Char`, оставив
|
||||
параметрами ровно то, что различается в оригинале:
|
||||
- окно (`loadkid`/`loadshad` — уже есть);
|
||||
- набор атласов и таблица кадров (`kid*` / `GUARD` / `SKEL` — уже
|
||||
выбирается в `pop_guard_load`);
|
||||
- слот heal-прямоугольников (у каждого персонажа свой, по страницам
|
||||
дабл-буфера);
|
||||
- тип объекта для порядка (Kid / тень / страж).
|
||||
|
||||
**Не портированы вовсе и относятся сюда же:** `stuck_lower`, `redraw_at_char2`,
|
||||
`clip_char` (см. memory `pop_clip_char_todo`).
|
||||
|
||||
**Чем подтверждать:** падение скелета в пропасть с разных X (сегодняшний
|
||||
сценарий), бой стража на ур. 1-2 без регрессий, `tests-host` зелёные.
|
||||
Отрисовка сведена к одному набору функций над `Char` (`pop_cdraw.c`), проход
|
||||
окклюзии — один на всех (`pop_fore_over_char`). Разбор, таблица «было →
|
||||
стало», пять починенных расхождений и замеры — в
|
||||
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#draw-char). Оттуда же список того, что
|
||||
надо потрогать вживую при приёмке уровней.
|
||||
|
||||
### <a id="l3-pass"></a>L3-PASS. Приёмка уровня 3 — обход всех комнат
|
||||
|
||||
|
||||
Reference in New Issue
Block a user