Падающие плиты: спрайты падающего набора, замеры слоёв, анализ midtable
Спрайты: у падающего куска obj_id = 10 (add_mob_to_objtable, seg007:1170), и три части берутся из таблиц по этому индексу — 70/74/72, а не 41/43/42. Мы рисовали плиту В ПОКОЕ, отсюда «цельная ровная» вместо двух половин со сдвигом правой на пиксель. Подпорка «перерисовать соседний тайл поверх куска» убрана: она тащила на плиту чужой узор и окно, да ещё стоила по одной полной отрисовке тайла на кусок за кадр. Клип объекта (clip.right = 40) НЕ портирован — единицы поля не выяснены, буквальные 40 пикселей срезают задний угол плиты. Цена отказа — правая грань куска и передние части чужих тайлов его не перекрывают (MOB-CLIP-RIGHT, BUGS_OPEN.md). Коридор heal сужен по высоте 32 -> 24: реальный след спрайта 16 px. Ширина оставлена 64 — из неё используются 62, экономить нечего; заодно в комментарии зафиксировано, что «выравнивание блока акселератора» ничем не подтверждается и требует проверки артефактом. docs/midtable_analysis.md — разбор слоёв оригинала и замеры: - логический кадр 1 289 526 тактов; - pop_redraw_needed 978 тактов (0,08 %); - fore-проход 128 778 тактов (10 %) на ОДНОГО персонажа, но выполняется лишь на 4 % кадров — гасит пропуск неизменившегося персонажа; - максимум объектов на одном тайле 2 (пара из loose_fall), значит сортировка внутри тайла бесплатна. Вывод: единственный риск порта objtable — сохранится ли окно клипа fore-прохода; до разбора redraw_frames_fore выбирать вариант рано. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
| [`perf_backlog.md`](perf_backlog.md) | **Отложенная оптимизация отрисовки** с замерами + как мерить (wait-state'ы, границы кадра) |
|
||||
| [`levels_plan.md`](levels_plan.md) | Следующий этап: уровни 2+, второй тайлсет, читы SDLPoP |
|
||||
| [`levels_12_15_plan.md`](levels_12_15_plan.md) | **Уровни 12/13** (тень, Джафар, падающие плиты) + что такое 14/15 и 0. 2026-08-13 |
|
||||
| [`midtable_analysis.md`](midtable_analysis.md) | **Слои отрисовки**: как устроены back/mid/fore и objtable в оригинале, чего стоит порт, развилки. 2026-08-13 |
|
||||
| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки |
|
||||
| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди |
|
||||
| [`host_tests_plan.md`](host_tests_plan.md) | Модульные тесты движка под ucsim_z80: два шва, регрессии из `BUGS_CLOSED.md`, дифф против SDLPoP |
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
# Слои отрисовки: как устроен оригинал и чего стоит порт
|
||||
|
||||
Разбор 2026-08-13, по `../SDLPoP/src/seg008.c`. Повод — семь дефектов
|
||||
падающих плит на уровне 13, из которых три оказались не багами кода, а
|
||||
следствием того, что у нас нет слоя, в котором объекты и куски тайлов
|
||||
сортируются между собой. Решение по этому документу ещё не принято.
|
||||
|
||||
---
|
||||
|
||||
## 1. Как это работает в оригинале
|
||||
|
||||
### 1.1 Три таблицы, а не «слои»
|
||||
|
||||
`draw_tables` (seg008:1373) рисует ровно в таком порядке:
|
||||
|
||||
```
|
||||
restore_peels();
|
||||
draw_wipes(0);
|
||||
draw_table(0); // BACKTABLE
|
||||
draw_table(3); // MIDTABLE
|
||||
draw_wipes(1);
|
||||
draw_table(1); // FORETABLE
|
||||
```
|
||||
|
||||
Это грубое разделение на три уровня глубины. Куда попадёт кусок тайла,
|
||||
решает переменная `ptr_add_table`, которую вызывающий переставляет перед
|
||||
`draw_tile*`: по умолчанию `add_backtable`, в оверлее кромки —
|
||||
`add_midtable` (`draw_other_overlay`, seg008:1499), а `add_foretable`
|
||||
вызывается явно и точечно.
|
||||
|
||||
### 1.2 Объекты живут НЕ в таблицах, а в objtable — и привязаны к ТАЙЛУ
|
||||
|
||||
Персонажи, падающие куски, мечи, брызги попадают в `objtable`, и у каждой
|
||||
записи есть поле `tilepos` — тайл, которому объект принадлежит.
|
||||
|
||||
Ключевое: объекты рисуются **не отдельным проходом после фона**, а ВНУТРИ
|
||||
обхода тайлов. В `redraw_needed_tile` (seg008:207) стоит:
|
||||
|
||||
```c
|
||||
if (tile_object_redraw[tilepos]) {
|
||||
if (tile_object_redraw[tilepos] == 0xFF)
|
||||
draw_objtable_items_at_tile(tilepos - 1);
|
||||
draw_objtable_items_at_tile(tilepos);
|
||||
tile_object_redraw[tilepos] = 0;
|
||||
}
|
||||
if (redraw_frames_fore[tilepos]) draw_tile_fore();
|
||||
```
|
||||
|
||||
То есть на каждом тайле: сначала его фоновые куски, потом объекты ЭТОГО
|
||||
тайла, потом его передние куски.
|
||||
|
||||
### 1.3 Порядок глубины складывается из ТРЁХ независимых механизмов
|
||||
|
||||
| механизм | что даёт |
|
||||
|---|---|
|
||||
| порядок обхода тайлов: ряды **2, 1, 0**, колонки 0..9 (seg008:129) | тайл, обойдённый позже, рисуется поверх |
|
||||
| сортировка объектов ВНУТРИ одного тайла (`sort_curr_objs`, seg008:1553) | кто из объектов одного тайла поверх кого |
|
||||
| три таблицы back/mid/fore | грубая глубина для кусков тайлов |
|
||||
|
||||
Сортировка внутри тайла (`compare_curr_objs`, seg008:1572) — пузырьком, и
|
||||
правил в ней три:
|
||||
|
||||
```
|
||||
объект типа 1 (ТЕНЬ) — всегда первым;
|
||||
оба объекта — падающие плиты (0x80): y1 < y2 → по УБЫВАНИЮ y;
|
||||
любая другая пара: y1 > y2 → по ВОЗРАСТАНИЮ y.
|
||||
```
|
||||
|
||||
Обратный порядок для пары плит — не описка: две плиты из `loose_fall` летят
|
||||
в 6 пикселях друг от друга, и верхняя обязана лечь поверх нижней.
|
||||
|
||||
### 1.4 Что из этого следует
|
||||
|
||||
**«Сортируемый midtable» — неточное имя.** Глобальной сортировки среднего
|
||||
слоя в оригинале нет. Есть привязка объекта к тайлу и сортировка внутри
|
||||
тайла; всё остальное решает порядок обхода. Это принципиально дешевле
|
||||
общей сортировки: объектов на один тайл обычно 1-2.
|
||||
|
||||
---
|
||||
|
||||
## 2. Что делаем мы
|
||||
|
||||
Наш кадр — жёсткая последовательность проходов, без привязки объектов к
|
||||
тайлам:
|
||||
|
||||
```
|
||||
фон: pop_loose_tick (физика кусков) -> pop_process_trobs -> pop_redraw_needed
|
||||
-> редрой шва
|
||||
объекты: pop_loose_mob_draw (куски ПОД Kid, отсортированы по y между собой)
|
||||
pop_char_draw(OPP/KID) (порядок задаёт guard_over_kid)
|
||||
pop_loose_mob_draw_over (куски ПОВЕРХ Kid)
|
||||
перед: pop_fore_over_char — ТОЛЬКО в окне вокруг персонажа
|
||||
```
|
||||
|
||||
Отличия, из которых растут все три оставшихся дефекта:
|
||||
|
||||
1. **Объект не знает своего тайла.** Глубина «кусок против Кида» считается
|
||||
отдельной формулой (`pop_room.c`, поле `defer`), а «кусок против КУСКА
|
||||
ТАЙЛА» не считается вовсе — куски тайлов рисуются раньше всех объектов.
|
||||
2. **Передний слой считается только вокруг персонажа.** Это наша
|
||||
оптимизация (memory `pop_fore_layer_cost`: полный проход стоил 78 %
|
||||
кадра). Падающая плита в чужом углу комнаты передних частей тайлов
|
||||
поверх себя не получает — отсюда «плита перед колонной».
|
||||
3. **Оверлей кромки идёт после персонажей** и потому безусловно поверх
|
||||
всех, тогда как у оригинала он в midtable и сортируется.
|
||||
|
||||
---
|
||||
|
||||
## 3. Что затрагивает порт
|
||||
|
||||
| участок | объём правки |
|
||||
|---|---|
|
||||
| `pop_room.c` — куски | привязать к тайлу, убрать `defer`, убрать собственную сортировку |
|
||||
| `pop_cdraw.c` — персонажи | то же: объект вместо слота, привязка к тайлу |
|
||||
| `pop_bg.c` — `overlay_mid_tile`, `fore_only_tile`, `ceil_over_kid_tile` | вызов из обхода тайлов, а не из отдельного прохода |
|
||||
| `pop_redraw.c` — пометки | добавить «на этом тайле есть объект» (порт `tile_object_redraw`) |
|
||||
| `roomtest.c` — главный цикл | вместо трёх проходов один: обход тайлов с объектами внутри |
|
||||
| окно fore-клипа (`pop_t_fclip_*`) | смысл меняется: клип по тайлу, а не по персонажу |
|
||||
|
||||
Плюс новая структура objtable и её сортировка — но маленькая, на тайл.
|
||||
|
||||
---
|
||||
|
||||
## 4. Плюсы
|
||||
|
||||
* **Уходят разом** MOB-CLIP-RIGHT, MID-OVERLAY-LAYER и «плита перед
|
||||
колонной»: все три — следствие отсутствия привязки к тайлу, а не
|
||||
самостоятельные баги.
|
||||
* **Уходят подпорки.** Перерисовка соседнего тайла поверх куска,
|
||||
`defer`, ручная сортировка кусков, отдельный проход `draw_over` —
|
||||
всё это заменяется одним механизмом.
|
||||
* **Совпадение с оригиналом по построению.** Дальше любой вопрос «что
|
||||
поверх чего» решается чтением seg008, а не экспериментом в MAME.
|
||||
* **Возможный выигрыш по кадру.** Сейчас fore-проход считает окно вокруг
|
||||
персонажа и всё равно перебирает до девяти тайлов; при привязке к тайлу
|
||||
передние части рисуются только там, где реально есть помеченный объект.
|
||||
Но это НАДО ЗАМЕРИТЬ, а не обещать.
|
||||
|
||||
---
|
||||
|
||||
## 5. Минусы и риски
|
||||
|
||||
* **Риск регресса широкий.** Трогается порядок отрисовки ВСЕГО: Кид,
|
||||
соперник, меч, брызги, зеркало, куски, оверлеи, полоса потолка.
|
||||
Уровни 1-11 приняты и держатся на текущем порядке.
|
||||
* **Наша оптимизация fore-окна может не пережить порт в прежнем виде.**
|
||||
Она даёт 3.2x на самом дорогом проходе (memory `pop_fore_layer_cost`).
|
||||
Если привязка к тайлу заставит рисовать передние части шире — можно
|
||||
потерять больше, чем выиграть.
|
||||
* **Дабл-буфер.** У оригинала один экран с dirty-rect, у нас две страницы
|
||||
со своими копиями фона и heal. Пометка «на тайле есть объект» обязана
|
||||
быть счётчиком страниц, как остальные наши пометки, — иначе объект
|
||||
перерисуется на одной странице и не перерисуется на другой.
|
||||
* **Банки.** Отрисовка размазана по трём банкам (`pop_bg` 2, `pop_cdraw` 4,
|
||||
`pop_room` 7) плюс резидент. Единый обход тайлов с объектами внутри
|
||||
означает, что цикл обхода зовёт код из всех трёх — надо проверить, что не
|
||||
упрёмся в границы банков и трамплины.
|
||||
* **Объём.** Это не правка, а этап: сопоставимо с тем, что делалось для
|
||||
fore-слоя.
|
||||
|
||||
---
|
||||
|
||||
## 6. Развилки
|
||||
|
||||
**A. Полный порт** — objtable с привязкой к тайлу, сортировка внутри тайла,
|
||||
объекты внутри обхода. Максимально близко к оригиналу, максимальный риск и
|
||||
объём.
|
||||
|
||||
**B. Частичный: только привязать КУСКИ к тайлам.** Персонажей оставить как
|
||||
есть. Закрывает MOB-CLIP-RIGHT и «плиту перед колонной», не трогает
|
||||
проверенный порядок персонажей. Дешевле и безопаснее; MID-OVERLAY-LAYER
|
||||
остаётся.
|
||||
|
||||
**C. Отложить** до этапа BG-ONCE и делать вместе — там всё равно
|
||||
пересматриваются слои, и два пересмотра подряд дороже одного.
|
||||
|
||||
Рекомендация: **B или C**. Вариант A целиком оправдан только если мы
|
||||
одновременно берёмся за BG-ONCE — тогда это один пересмотр слоёв вместо
|
||||
двух, и замер кадра делается один раз.
|
||||
|
||||
---
|
||||
|
||||
## 7. ЗАМЕРЫ (сделаны 2026-08-13, уровень 13)
|
||||
|
||||
Метод — маркеры-пустышки в РЕЗИДЕНТЕ вокруг измеряемого вызова плюс
|
||||
брейкпоинты с `printf totalcycles` (memory `z80_profiling_method`). В
|
||||
банковый код брейкпоинт ставить нельзя: 0xC000 — общее окно всех банков.
|
||||
|
||||
| что | такты | доля логического кадра |
|
||||
|---|---|---|
|
||||
| логический кадр целиком | **1 289 526** | 100 % |
|
||||
| `pop_redraw_needed` | **978** | 0,08 % |
|
||||
| **fore-проход, ОДИН персонаж** | **128 778** | **10 %** |
|
||||
|
||||
Плюс два счётчика за прогон ~229 логических кадров:
|
||||
|
||||
* fore-проход вызван **10 раз** — на 96 % кадров он не выполняется вовсе
|
||||
(пропуск неизменившегося персонажа, `pop_char_skip_mask`). Средняя цена
|
||||
по кадру выходит ~0,4 %, но КАК ТОЛЬКО персонаж движется — платим все 10 %
|
||||
каждый кадр, и при двух персонажах это ~20 %;
|
||||
* **максимум объектов на одном тайле = 2** (комната 16, пара из
|
||||
`loose_fall`: плита сбивает плиту, дальше летят обе). В комнате 23, где
|
||||
гряда падает в пустоту, максимум 1.
|
||||
|
||||
### 7.1 Что эти числа меняют в оценке
|
||||
|
||||
**Сортировка внутри тайла — бесплатна.** Два объекта, пузырёк на два
|
||||
элемента. Возражение против варианта A, которое закладывалось в §5, снято.
|
||||
|
||||
**`pop_redraw_needed` можно не считать вовсе.** 978 тактов против 128 778 у
|
||||
fore-прохода — соотношение 131 к 1.
|
||||
|
||||
**Единственный настоящий риск порта — окно клипа fore-прохода.** Если
|
||||
привязка объектов к тайлам заставит рисовать передние части шире нынешнего
|
||||
окна вокруг персонажа, мы потеряем 10 % кадра, и потеряем их НА ДВИЖЕНИИ,
|
||||
когда бюджет и так самый напряжённый.
|
||||
|
||||
### 7.2 Что осталось выяснить ДО выбора варианта
|
||||
|
||||
Как оригинал отсекает лишнюю работу переднего слоя: `redraw_frames_fore[tilepos]`
|
||||
рисует передние части ТОЛЬКО у помеченных тайлов (seg008:214). Надо понять,
|
||||
насколько узко эта пометка ставится — если не шире нашего окна, вариант A
|
||||
безопасен по кадру; если шире, остаёмся на B.
|
||||
|
||||
**Обновлённая рекомендация:** решение упирается в §7.2, и до его разбора
|
||||
выбирать между A и B преждевременно. B остаётся безопасным запасным
|
||||
вариантом.
|
||||
@@ -938,7 +938,22 @@ void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked
|
||||
* [mob_x-4 .. mob_x+57], иначе правая грань 42 остаётся хвостом на одной
|
||||
* странице → тёмная тень на полу 2,7 + двоебуфер-мерцание. */
|
||||
#define MOB_X0(mx) ((mx) - 4)
|
||||
#define MOB_W 64 /* mob_x-4 .. mob_x+59 (крышка +57 с запасом 2px) */
|
||||
/* Коридор heal — по РЕАЛЬНОМУ следу спрайта, а не «с запасом».
|
||||
* След: по x mob_x .. mob_x+58, по y mob_y-16 .. mob_y — это объединение
|
||||
* трёх частей (41 = 32x13 с y-3, 42 = 26x15 с x+32,y-1, 43 = 32x3 с y).
|
||||
*
|
||||
* Ширина оставлена прежней (64 = mob_x-4 .. mob_x+60): нужных 62 плюс поля.
|
||||
* А вот высота была 32 при нужных 16 — вдвое лишнего на самой дорогой
|
||||
* операции кадра, помноженной на число падающих плит; сужена до 24.
|
||||
*
|
||||
* ПРО ЗАПАС ПО ШИРИНЕ: обоснования «выравнивание блока акселератора» я не
|
||||
* подтвердил — ни pop_heal_fast, ни gfx_heal координаты не округляют, а в
|
||||
* memory про акселератор таких требований нет. Единственный источник —
|
||||
* наблюдение в pop_clip_sprite про 1-2 px слайверы правой грани. Сузить
|
||||
* ширину можно, но ТОЛЬКО подтвердив прогоном, что слайверы не появятся. */
|
||||
#define MOB_W 64 /* mob_x-4 .. mob_x+60 */
|
||||
#define MOB_HEAL_UP 20 /* верх коридора = mob_y − 20 */
|
||||
#define MOB_HEAL_H 24 /* .. mob_y + 4 */
|
||||
|
||||
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
|
||||
* должно утечь в новую комнату). */
|
||||
@@ -1031,7 +1046,7 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* ЗАДЕЛ: правильнее не перерисовывать соседа вовсе, а КЛИПОВАТЬ
|
||||
* правую часть куска — оригинал так и делает (add_mob_to_objtable,
|
||||
* seg007:1161: clip.right = 40). Тогда коридор вернётся к 64x32. */
|
||||
pop_heal_off(MOB_X0(m->x), m->prev_y[pg] - 27, MOB_W, 32);
|
||||
pop_heal_off(MOB_X0(m->x), m->prev_y[pg] - MOB_HEAL_UP, MOB_W, MOB_HEAL_H);
|
||||
m->prev_y[pg] = MOB_Y_NONE;
|
||||
}
|
||||
if (!m->active) { m->clean--; return; }
|
||||
@@ -1194,55 +1209,30 @@ static void mob_render(mob_t *m, uint8_t pg)
|
||||
* left(41) на obj_y-3, bottom(43) на obj_y, right(42) на obj_x+4 (=col+1,
|
||||
* заходит в тайл СПРАВА) на obj_y-1. */
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
pop_env_b(43, m->x, m->y);
|
||||
pop_env_b(41, m->x, m->y - 3);
|
||||
pop_env_b(42, m->x + 32, m->y - 1);
|
||||
m->prev_y[pg] = m->y;
|
||||
/* Окклюзия правого куска соседним полом (SDLPoP redraw_at_cur_mob:
|
||||
* set_redraw_full(curr_tilepos+1) — сосед перерисовывается ПОВЕРХ mob).
|
||||
* Правый кусок 42 целиком лежит в тайле col+1; пока плита у уровня пола,
|
||||
* её правый край ПРЯЧЕТСЯ за передней гранью пола. Перерисовываем
|
||||
* сосед-тайл В ТОМ ЖЕ КАДРЕ поверх куска (иначе край торчит на полу
|
||||
* до heal'а следующего кадра — виден как хвост во время падения). */
|
||||
if (m->col + 1 < 10) {
|
||||
/* Ряд соседа берём ИЗ КООРДИНАТЫ куска (y_to_row_mod4(ypos)), а НЕ из
|
||||
* m->row: последний — логический счётчик «сквозь какой ряд летим», и
|
||||
* mob_down_a_row уводит его на ряд ВПЕРЁД (для плиты нижнего ряда —
|
||||
* сразу в 3, которого нет). Тогда пол соседа не перерисовывался
|
||||
* вообще, и правый кусок 42 оставался ПОВЕРХ него: грань плиты видна
|
||||
* через пол + перекрыта передняя грань пола.
|
||||
* Плюс ВТОРОЙ тайл — под ВЕРХОМ куска (ypos-18), пока кусок висит на
|
||||
* границе рядов. Порт draw_mob (seg007:13E5). */
|
||||
int8_t r = pop_y_to_row((int16_t)m->y);
|
||||
int8_t rt = pop_y_to_row((int16_t)(m->y - 18));
|
||||
/* ОКНО = КОРИДОР HEAL ЭТОГО КУСКА, ни пикселем шире. draw_tile
|
||||
* рисует тайл ЦЕЛИКОМ (пол, грани, верх кладки ряда ниже — порядка
|
||||
* 64x63), причём в GFX_BANK_SPRITE, то есть мимо теневой копии фона.
|
||||
* Всё, что он клал за пределами коридора, не стиралось НИКОГДА:
|
||||
* кусок улетал, а в соседнем столбце оставался обрезок пола
|
||||
* (найдено прогоном 2026-08-13).
|
||||
/* КЛИПА ОБЪЕКТА ЗДЕСЬ НЕТ. У оригинала он есть — add_mob_to_objtable
|
||||
* (seg007:1161) ставит куску clip.right = 40, — но единицы этого поля я
|
||||
* НЕ выяснил: буквальные 40 экранных пикселей срезают правый задний угол
|
||||
* плиты (прогон 2026-08-13). У оригинала obj_x живёт в удвоенных
|
||||
* единицах (char_x_left = obj_x / 2 + 58), так что 40 может означать и
|
||||
* не пиксели. До выяснения рисуем кусок целиком.
|
||||
*
|
||||
* Расширять под это heal НЕЛЬЗЯ: heal — самая дорогая операция
|
||||
* кадра, а на 13-м уровне таких кусков одновременно шесть. Поэтому
|
||||
* режем не heal под перерисовку, а перерисовку под heal — тем же
|
||||
* окном, которым fore-слой отсекает лишние тайлы. */
|
||||
pop_t_fclip_x0 = MOB_X0(m->x);
|
||||
pop_t_fclip_x1 = MOB_X0(m->x) + MOB_W;
|
||||
pop_t_fclip_y0 = m->y - 27 + POP_YOFF;
|
||||
pop_t_fclip_y1 = pop_t_fclip_y0 + 32;
|
||||
pop_t_fclip_ly0 = pop_t_fclip_y0; /* переворота у кусков нет */
|
||||
pop_t_fclip_ly1 = pop_t_fclip_y1;
|
||||
pop_t_fclip_on = 1;
|
||||
if (r < 0) pop_t_clip_top = POP_YOFF; /* полоса у потолка — только срез */
|
||||
draw_tile(r, m->col + 1);
|
||||
pop_t_clip_top = 0;
|
||||
if (rt != r) {
|
||||
if (rt < 0) pop_t_clip_top = POP_YOFF;
|
||||
draw_tile(rt, m->col + 1);
|
||||
pop_t_clip_top = 0;
|
||||
}
|
||||
pop_t_fclip_on = 0;
|
||||
}
|
||||
* Подпорки «перерисовать соседний тайл поверх куска» тоже нет: она тащила
|
||||
* на плиту чужой узор и окно (MOB-CLIP-RIGHT). Цена отказа — правая
|
||||
* грань куска не прячется за передней гранью соседнего пола; артефакт
|
||||
* мельче прежнего и уйдёт вместе с сортируемым midtable. */
|
||||
/* Спрайты ПАДАЮЩЕГО куска, а не лежащей плиты. У объекта в objtable
|
||||
* obj_id = 10 (add_mob_to_objtable, seg007:1170), и три части берутся из
|
||||
* таблиц по этому индексу (seg008:518/596/608):
|
||||
* loose_fram_left[10] = 70 (32x13, y-3)
|
||||
* loose_fram_bottom[10] = 74 (32x3, y)
|
||||
* loose_fram_right[10] = 72 (26x16, x+32, y-1)
|
||||
* Здесь стояли 41/43/42 — это индекс 0, то есть плита В ПОКОЕ. Отсюда и
|
||||
* «цельная ровная плита» вместо двух частей со сдвигом правой половины
|
||||
* на пиксель (замечено пользователем 2026-08-13 сравнением с оригиналом). */
|
||||
pop_env_b(74, m->x, m->y);
|
||||
pop_env_b(70, m->x, m->y - 3);
|
||||
pop_env_b(72, m->x + 32, m->y - 1);
|
||||
m->prev_y[pg] = m->y;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
/* клип СВОЕГО выхода за поле (верхний борт / статус-полоса) — по ВСЕЙ ширине */
|
||||
pop_clip_sprite(MOB_X0(m->x), MOB_W, m->y - 26 + POP_YOFF, 30);
|
||||
|
||||
@@ -102,3 +102,4 @@ uint8_t pop_seamless;
|
||||
* pop_dbg_roomnav и ../docs/impl_diff.md. Снимается первой же СМЕНОЙ
|
||||
* КОМНАТЫ ОБЫЧНЫМ ХОДОМ и стартом уровня. */
|
||||
uint8_t pop_nav_hold;
|
||||
|
||||
|
||||
@@ -74,3 +74,4 @@ void pop_dbg_trap(void);
|
||||
* следующий pop_check_mirror родит тень; 0 — обычное состояние.
|
||||
* Межбанковый: пишет pop_map, читает pop_map же, но через главный цикл. */
|
||||
extern int8_t pop_jumped_mirror;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user