DRAW-COST шаг 1: пропуск неизменившегося персонажа — 210% -> 116% кадра
Персонаж, у которого с прошлой отрисовки ЭТОЙ страницы дабл-буфера не изменился ни один вход отрисовки, а фон в его прямоугольнике не трогали, уже нарисован правильно: heal, блит и fore-проход пропускаются целиком. Не спецкейс «мёртвый страж», а общее правило — покрывает и труп, и стоящего Кида, и ждущего стража. Механизм: снимок входов по страницам (pop_cdraw.c, cd_sig/cd_quiet) + позиционная метка «фон трогали вот здесь» (pop_cd_touch в резидентном pop_tile.c, зовёт сам pop_blit_b). Решение перепроверяется перед отрисовкой, а pop_char_draw страхуется собственным heal — если тик всё-таки сдвинул персонажа, прошлый кадр стирается там. Слоты рядом (32 px) — перерисовываем оба, иначе heal соседа выест кусок из «тихого». Метка обязана быть ПОЗИЦИОННОЙ: с флагом «фон трогали хоть где-то» выигрыш был ровно нулевым — факелы анимируются каждый кадр и гасили пропуск для всех сразу (597 684 такта, как без оптимизации). Замеры (MAME, брейкпоинты по totalcycles, бюджет кадра 430 000): комн. 1.3, труп стража, Кид стоит: 210 % -> 116 % (500 772 такта), ноль вызовов pop_heal_fast за кадр, весь фон — 2 блита (44 136); комн. 1.1, Кид стоит вдали от факелов: 404 112 (94 %), цикл 4 -> 3 кадра. Узкое место сместилось на ЛОГИКУ: 60 % кадра уходит на тик персонажей, которые СТОЯТ, ещё 28 % — на loose_tick + process_trobs в комнате без единой ловушки. Разбивка и план — TASKS_OPEN.md#draw-cost.
This commit is contained in:
@@ -60,12 +60,12 @@ Kid — `kid_data.h` (генерится `pop_extract_kid_data.py`).
|
|||||||
| Файл | Роль (порт SDLPoP) |
|
| Файл | Роль (порт SDLPoP) |
|
||||||
|------|--------------------|
|
|------|--------------------|
|
||||||
| `roomtest.c` | Главный цикл: дабл-буфер (2 страницы + флип на vsync), tick→draw порядок слоёв. |
|
| `roomtest.c` | Главный цикл: дабл-буфер (2 страницы + флип на vsync), tick→draw порядок слоёв. |
|
||||||
| `pop_tile.c/.h` | Общие ЛИСТЬЯ слоя фона — РЕЗИДЕНТ W1: блит куска атласа, чтение тайла комнаты, `tile_table`. Резидент потому, что их зовут обе половины слоя фона из разных банков, а `const`-таблицы банка из чужого банка не видны. |
|
| `pop_tile.c/.h` | Общие ЛИСТЬЯ слоя фона — РЕЗИДЕНТ W1: блит куска атласа, чтение тайла комнаты, `tile_table`, метка «фон трогали вот здесь» (`pop_cd_touch`). Резидент потому, что их зовут обе половины слоя фона из разных банков, а `const`-таблицы банка из чужого банка не видны. |
|
||||||
| `pop_bg.c/.h` | ГОРЯЧАЯ половина слоя фона (банк 2, каждый кадр): fore-окклюзия поверх ЛЮБОГО персонажа (`seg003` redraw_at_char/char2 — один проход на всех Char), оверлеи кромки, кладка стены (`wall_pattern`), клип. `pop_bg.h` — публичный API ВСЕГО слоя (обеих половин). |
|
| `pop_bg.c/.h` | ГОРЯЧАЯ половина слоя фона (банк 2, каждый кадр): fore-окклюзия поверх ЛЮБОГО персонажа (`seg003` redraw_at_char/char2 — один проход на всех Char), оверлеи кромки, кладка стены (`wall_pattern`), клип. `pop_bg.h` — публичный API ВСЕГО слоя (обеих половин). |
|
||||||
| `pop_room.c` | ХОЛОДНАЯ половина слоя фона (банк 7, раз на комнату/событие): полная отрисовка комнаты (`seg008` draw_tile), точечные перерисовки тайлов (пики, ворота, кнопки, дверь уровня, дрожащие плиты), падающие куски loose (`seg007` mob), загрузка атласов. |
|
| `pop_room.c` | ХОЛОДНАЯ половина слоя фона (банк 7, раз на комнату/событие): полная отрисовка комнаты (`seg008` draw_tile), точечные перерисовки тайлов (пики, ворота, кнопки, дверь уровня, дрожащие плиты), падающие куски loose (`seg007` mob), загрузка атласов. |
|
||||||
| `_pop_bg.h` | Внутренний контракт между половинами: общее состояние + три тонкие `__banked`-обёртки. Там же расписано, почему рез прошёл именно здесь и сколько стоит. |
|
| `_pop_bg.h` | Внутренний контракт между половинами: общее состояние + три тонкие `__banked`-обёртки. Там же расписано, почему рез прошёл именно здесь и сколько стоит. |
|
||||||
| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. |
|
| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. |
|
||||||
| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`, `seg008:22F0/2324`): спрайт, clip_char, брызги, клинок, heal; + полоса HP и палитры соперника. |
|
| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`, `seg008:22F0/2324`): спрайт, clip_char, брызги, клинок, heal; + полоса HP и палитры соперника. Там же ПРОПУСК неизменившегося кадра (`pop_char_skip_mask`): персонаж, у которого с прошлой отрисовки этой страницы ничего не изменилось и фон под ним не трогали, не перерисовывается вовсе. |
|
||||||
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
|
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
|
||||||
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
|
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
||||||
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
||||||
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||||
| — | [DRAW-COST](#draw-cost) | **кадр не укладывается в бюджет: ~210 % кадрового периода в ПОКОЕ** (комн. 3), 140 % без трупа стража (комн. 2) | плавность на ВСЕХ уровнях |
|
| — | [DRAW-COST](#draw-cost) | шаг 1 сделан (пропуск неизменившегося персонажа): в покое 210 % -> **116 %** кадрового периода; дальше узкое место — ЛОГИКА (60 % на тик стоящих персонажей) | плавность на ВСЕХ уровнях |
|
||||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||||
|
|
||||||
@@ -318,53 +318,73 @@ W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
|||||||
|
|
||||||
### <a id="draw-cost"></a>DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация
|
### <a id="draw-cost"></a>DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация
|
||||||
|
|
||||||
> **ЗАМЕР ПОЛЬЗОВАТЕЛЯ 2026-08-08 (после MEM-BANK2).** Уровень 1, комната 3,
|
> **ШАГ 1 СДЕЛАН 2026-08-08: пропуск неизменившегося персонажа.** Комната
|
||||||
> страж УБИТ, Кид СТОИТ (то есть это НЕ худший случай — ни боя, ни движения):
|
> 1.3, труп стража, Кид стоит: было **210 %** кадрового периода, стало
|
||||||
>
|
> **116 %** (500 772 такта при бюджете 430 000). Отрисовка перестала быть
|
||||||
> | полоса | фаза | доля кадрового периода |
|
> узким местом: персонажи в покое не рисуются ВООБЩЕ (ноль вызовов
|
||||||
> |---|---|---|
|
> `pop_heal_fast` за кадр), весь фон — ДВА блита факелов (44 136 тактов).
|
||||||
> | синяя (`PROF(2)`) | ввод + heal + логика | ~80 % |
|
> Механизм и почему метка позиционная — в шапке `pop_cdraw.h`.
|
||||||
> | зелёная (`PROF(4)`) | слой фона (loose/ловушки) | ~20 % |
|
|
||||||
> | циан (`PROF(6)`) | спрайты + fore-проход | **~110 %** |
|
|
||||||
>
|
|
||||||
> **Итого ~210 % — больше ДВУХ кадровых периодов на кадр.** Это много; в
|
|
||||||
> покое должно оставаться место под бой, движение и вторую фигуру.
|
|
||||||
> Приоритет задачи поднят: это уже не косметика профиля.
|
|
||||||
>
|
|
||||||
> **Контрольный замер там же: комната 2** — та же по устройству, но БЕЗ тела
|
|
||||||
> стража: 60 % / 20 % / 60 % = **~140 %**.
|
|
||||||
|
|
||||||
### Первый след: ТЕЛО УБИТОГО СТРАЖА стоит ~70 % кадра
|
**Исходные замеры пользователя (полосы бордюра, до шага 1).** Уровень 1,
|
||||||
|
Кид СТОИТ — то есть НЕ худший случай, ни боя, ни движения:
|
||||||
|
|
||||||
Комнаты 2 и 3 отличаются ровно одним — трупом, и он даёт +20 % синей и
|
| комната | синяя (ввод+heal+логика) | зелёная (фон) | циан (спрайты) | итого |
|
||||||
+50 % циана. Причина видна прямо в коде и НЕ требует замера: ни
|
|---|---|---|---|---|
|
||||||
`roomtest.c`, ни `pop_cdraw.c` не смотрят на `Char.alive`. Мёртвый страж
|
| 3, страж УБИТ | ~80 % | ~20 % | ~110 % | **~210 %** |
|
||||||
сохраняет `charid != 0`, поэтому КАЖДЫЙ кадр честно проходит весь путь
|
| 2, стража НЕТ | ~60 % | ~20 % | ~60 % | ~140 % |
|
||||||
живого персонажа — `pop_char_heal(OPP)` (стереть), `pop_char_draw(OPP)`
|
|
||||||
(блит + clip_char + брызги + клинок), `pop_char_fore(OPP)` (перебор тайлов
|
|
||||||
футпринта с разбором кладки). При этом труп НЕ МЕНЯЕТСЯ: его кадр
|
|
||||||
постоянен до выхода из комнаты.
|
|
||||||
|
|
||||||
**Что напрашивается** (порядок — от дешёвого к дорогому, каждое МЕРИТЬ):
|
Разница ровно в теле убитого стража: +20 % синей и +50 % циана. Труп
|
||||||
1. запечь труп в фон один раз, как делают loose-плиты (`pop_floor_bake`) —
|
сохраняет `charid != 0`, поэтому каждый кадр честно проходил весь путь
|
||||||
тогда heal сам его восстанавливает, а per-frame путь исчезает целиком.
|
живого персонажа (heal → спрайт+clip_char+брызги+клинок → fore-проход), хотя
|
||||||
Сверить с SDLPoP: там мёртвый страж остаётся обычной записью objtable, но
|
его кадр постоянен до выхода из комнаты.
|
||||||
у нас нет их системы «перерисовать только грязные тайлы», и запечка — наш
|
|
||||||
штатный ответ на «статичный элемент поверх фона» (см. `pop_loose_floors`);
|
|
||||||
2. если запечка не годится (труп надо стирать при выходе/рестарте) —
|
|
||||||
минимум пропустить `pop_char_fore(OPP)` для мёртвого: передние грани
|
|
||||||
вокруг неподвижного тела тоже не меняются.
|
|
||||||
|
|
||||||
Проверять на той же паре комнат 2/3 — разница должна схлопнуться.
|
**Что сделано (шаг 1).** Не спецкейс «мёртвый», а общее правило: у каждой
|
||||||
|
страницы дабл-буфера свой снимок ВХОДОВ отрисовки слота; совпал снимок,
|
||||||
|
спрайт этой страницы цел и фон в его прямоугольнике не трогали — heal, блит
|
||||||
|
и fore-проход пропускаются целиком. Покрывает и труп, и стоящего Кида, и
|
||||||
|
ждущего стража. Детали контракта — `pop_cdraw.h`, реализация —
|
||||||
|
`pop_char_skip_mask` / `cd_quiet` в `pop_cdraw.c`, метка фона —
|
||||||
|
`pop_cd_touch` в резидентном `pop_tile.c`.
|
||||||
|
|
||||||
|
Грабли, на которые наступили по дороге: сначала метка была ФЛАГОМ «фон
|
||||||
|
трогали хоть где-то» — и выигрыш оказался ровно нулевым, потому что факелы
|
||||||
|
анимируются каждый кадр и гасили пропуск для всех персонажей сразу (замер:
|
||||||
|
597 684 такта, как без оптимизации). Метка обязана быть ПОЗИЦИОННОЙ.
|
||||||
|
|
||||||
|
**Остаточный эффект от объединения прямоугольников.** Метка одна на
|
||||||
|
страницу — объединение всех правок фона. В комнате 1 два факела дают
|
||||||
|
прямоугольник x 40…88, а стоящий там Кид занимает x 32…44: пересечение 4 px,
|
||||||
|
и он перерисовывается каждый кадр (140 % против 94 %, если отойти). Если
|
||||||
|
понадобится — держать не один прямоугольник, а 3-4 отдельных и мержить
|
||||||
|
только при переполнении.
|
||||||
|
|
||||||
|
**Куда идти дальше — теперь это ЛОГИКА, а не отрисовка.** Разбивка работы
|
||||||
|
кадра брейкпоинтами (комната 3, труп, Кид стоит; всего 500 772 такта):
|
||||||
|
|
||||||
|
| участок | тактов | % кадра |
|
||||||
|
|---|---:|---:|
|
||||||
|
| ввод + `check_skel` + луч видимости + `ctrl_tick` + heal | 65 862 | 15 % |
|
||||||
|
| `kid_tick` + физика + страж + боёвка | **260 022** | **60 %** |
|
||||||
|
| `loose_tick` + `process_trobs` | 119 562 | 28 % |
|
||||||
|
| `redraw_needed` + шов + спрайты + метка | 55 152 | 13 % |
|
||||||
|
| — из них два блита факелов | 44 136 | 10 % |
|
||||||
|
|
||||||
|
1. **60 % на тик персонажей** при том, что оба СТОЯТ — первый кандидат.
|
||||||
|
Смотреть `pop_phys_tick`/`pop_guard_phys_tick` (банк 3) и `pop_guard_tick`
|
||||||
|
(банк 1): сколько там работы, которую неподвижный персонаж делать не
|
||||||
|
обязан, и сколько стоит трамплин на каждом шаге.
|
||||||
|
2. **28 % на `loose_tick` + `process_trobs`** в комнате БЕЗ единой ловушки —
|
||||||
|
явно перебор; разобрать, что там сканируется каждый кадр (список trob,
|
||||||
|
`pop_trob_modif` соседа для шва, `pop_room_link`).
|
||||||
|
3. Запечь труп в фон (как щебень: тело в фоне + fore-часть поверх Кида) —
|
||||||
|
тогда бесплатным станет и проход Кида ПО телу, который сейчас снимает
|
||||||
|
пропуск по правилу «слоты рядом». Предложено пользователем 2026-08-08.
|
||||||
|
|
||||||
Мерить в ЭТОЙ точке (комната 3, страж убит, Кид стоит) — она воспроизводима
|
Мерить в ЭТОЙ точке (комната 3, страж убит, Кид стоит) — она воспроизводима
|
||||||
и даёт нижнюю границу; худший случай (бой + бег + падающая плита) считать
|
и даёт нижнюю границу; худший случай (бой + бег + падающая плита) считать
|
||||||
отдельно. Метод — memory `z80_profiling_method` (брейкпоинты с
|
отдельно. Метод — memory `z80_profiling_method` (брейкпоинты с
|
||||||
`totalcycles`), полосы бордюра дают только раскладку по фазам.
|
`totalcycles`); полосы бордюра показывают только одну картинку из периода и
|
||||||
|
годятся лишь для раскладки по фазам.
|
||||||
Обратить внимание: **синие 80 % при стоящем Киде** — это подозрительно
|
|
||||||
много для «ввод + heal + логика», heal там наверняка чистит больше, чем
|
|
||||||
нужно. Разобрать синюю фазу отдельно от циана.
|
|
||||||
|
|
||||||
**Наблюдение пользователя на приёмке DRAW-CHAR 2026-08-08:** циан-полоса
|
**Наблюдение пользователя на приёмке DRAW-CHAR 2026-08-08:** циан-полоса
|
||||||
профиля (спрайты + fore) выросла — тогда «в пределах».
|
профиля (спрайты + fore) выросла — тогда «в пределах».
|
||||||
@@ -377,15 +397,10 @@ W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
|||||||
работы», а недостающая ранее корректность: тайл, который спрайт задевает,
|
работы», а недостающая ранее корректность: тайл, который спрайт задевает,
|
||||||
обязан вернуть свой передний слой поверх него.
|
обязан вернуть свой передний слой поверх него.
|
||||||
|
|
||||||
**Куда копать, когда возьмёмся** (сначала МЕРИТЬ, потом резать —
|
**Если fore-проход снова станет узким местом** (сейчас он в покое не
|
||||||
[`pop_fore_layer_cost`](../../../docs) уже показывал 78 % кадра до кэша
|
выполняется вовсе): расширять перебор ТОЛЬКО по накладным спрайтам
|
||||||
кладки):
|
(клинок/брызги), а не по всему окну; кэш «в этом тайле fore-слоя нет
|
||||||
1. расширять перебор ТОЛЬКО по накладным спрайтам (клинок/брызги), а не по
|
вовсе»; считать окно клипа в тайловых координатах один раз.
|
||||||
всему окну: габарит самого кадра уже покрыт футпринтом + правилом меча;
|
|
||||||
2. кэш «в этом тайле fore-слоя нет вовсе» — половина колонок пустые, а за
|
|
||||||
каждую платим `tile_in_fclip` + разбор кладки;
|
|
||||||
3. считать окно клипа в тайловых координатах один раз, а не сравнивать
|
|
||||||
пиксельные габариты в двух циклах.
|
|
||||||
|
|
||||||
### <a id="draw-char"></a>DRAW-CHAR — **ЗАКРЫТА И ПРОВЕРЕНА 2026-08-08**
|
### <a id="draw-char"></a>DRAW-CHAR — **ЗАКРЫТА И ПРОВЕРЕНА 2026-08-08**
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ void pop_fore_heal(void) __banked
|
|||||||
if (!(ov_pending & (1 << pg))) return;
|
if (!(ov_pending & (1 << pg))) return;
|
||||||
pop_heal_off(ov_x0, ov_y0, ov_x1 - ov_x0, ov_y1 - ov_y0);
|
pop_heal_off(ov_x0, ov_y0, ov_x1 - ov_x0, ov_y1 - ov_y0);
|
||||||
ov_pending = (uint8_t)(ov_pending & ~(1 << pg));
|
ov_pending = (uint8_t)(ov_pending & ~(1 << pg));
|
||||||
|
/* Восстановленный прямоугольник шире персонажа и запросто накрывает
|
||||||
|
* СОСЕДА — пометить область (DRAW-COST): heal идёт мимо pop_blit_b. */
|
||||||
|
pop_cd_touch(ov_x0, ov_y0 + POP_YOFF, ov_x1 - ov_x0, ov_y1 - ov_y0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -156,8 +156,12 @@ static void cd_clip_add(pop_cdraw_t *s, int x, int y, uint16_t w, uint16_t h)
|
|||||||
/* Стирание прошлого кадра персонажа и его накладных. pop_heal_fast, а не
|
/* Стирание прошлого кадра персонажа и его накладных. pop_heal_fast, а не
|
||||||
* голый gfx_heal: стираем ровно то, что рисовали, а рисовали в подавляющем
|
* голый gfx_heal: стираем ровно то, что рисовали, а рисовали в подавляющем
|
||||||
* большинстве кадров noclip-ядром; клипающий путь остаётся для straddle и
|
* большинстве кадров noclip-ядром; клипающий путь остаётся для straddle и
|
||||||
* выхода за поле. */
|
* выхода за поле.
|
||||||
void pop_char_heal(uint8_t who) __banked
|
*
|
||||||
|
* Тело — static: pop_char_draw зовёт его же «страховкой» (см. ниже), а
|
||||||
|
* трамплин выбирается ОБЪЯВЛЕНИЕМ, и вызов через `__banked`-имя стоил бы
|
||||||
|
* 654 такта внутри собственного банка (memory sdcc_banked_call_rules). */
|
||||||
|
static void cd_heal(uint8_t who)
|
||||||
{
|
{
|
||||||
pop_cdraw_t *s = &pop_cd[who];
|
pop_cdraw_t *s = &pop_cd[who];
|
||||||
uint8_t p = gfx_get_draw_page() & 1;
|
uint8_t p = gfx_get_draw_page() & 1;
|
||||||
@@ -166,6 +170,101 @@ void pop_char_heal(uint8_t who) __banked
|
|||||||
s->valid[p] = s->ovalid[p] = 0;
|
s->valid[p] = s->ovalid[p] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pop_char_heal(uint8_t who) __banked { cd_heal(who); }
|
||||||
|
|
||||||
|
/* ---- Пропуск неизменившегося кадра (DRAW-COST; контракт — pop_cdraw.h) -- */
|
||||||
|
|
||||||
|
/* Снимок ВХОДОВ отрисовки слота. Кадр анимации (kid_frame/pop_gframe)
|
||||||
|
* однозначно определяется полем frame, поэтому его самого в снимке нет;
|
||||||
|
* тайлы вокруг персонажа (clip_char, fore-проход) в снимок тоже не входят —
|
||||||
|
* их изменение ловит pop_cd_dirty. */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t frame, x, y, action, sword, charid, room, hurt;
|
||||||
|
int8_t dir, ccol, crow;
|
||||||
|
int dx; /* render_dx: straddle-смещение */
|
||||||
|
} cd_sig_t;
|
||||||
|
|
||||||
|
static cd_sig_t cd_sig[POP_CH_N][2];
|
||||||
|
|
||||||
|
static void cd_sig_make(uint8_t who, cd_sig_t *g)
|
||||||
|
{
|
||||||
|
const pop_char_t *ch = (who == POP_CH_KID) ? &Kid : &Guard;
|
||||||
|
g->frame = ch->frame; g->x = ch->x; g->y = ch->y;
|
||||||
|
g->action = ch->action; g->sword = ch->sword; g->charid = ch->charid;
|
||||||
|
g->room = ch->room;
|
||||||
|
g->hurt = (uint8_t)((who == POP_CH_KID) ? pop_kid_hurt : pop_guard_hurt);
|
||||||
|
g->dir = ch->direction;
|
||||||
|
g->ccol = ch->curr_col; g->crow = ch->curr_row;
|
||||||
|
g->dx = pop_cd[who].render_dx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Габарит слота на странице: спрайт + накладные (клинок/брызги). y —
|
||||||
|
* КОМНАТНЫЙ (как в pop_cd), перевод в экранный делает вызывающий. */
|
||||||
|
static void cd_bbox(uint8_t who, uint8_t p, int *x0, int *y0, int *x1, int *y1)
|
||||||
|
{
|
||||||
|
const pop_cdraw_t *s = &pop_cd[who];
|
||||||
|
*x0 = s->x[p]; *y0 = s->y[p];
|
||||||
|
*x1 = *x0 + (int)s->w[p]; *y1 = *y0 + (int)s->h[p];
|
||||||
|
if (s->ovalid[p]) {
|
||||||
|
if (s->ox[p] < *x0) *x0 = s->ox[p];
|
||||||
|
if (s->oy[p] < *y0) *y0 = s->oy[p];
|
||||||
|
if (s->ox[p] + (int)s->ow[p] > *x1) *x1 = s->ox[p] + (int)s->ow[p];
|
||||||
|
if (s->oy[p] + (int)s->oh[p] > *y1) *y1 = s->oy[p] + (int)s->oh[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* «Тихий» слот: спрайт этой страницы цел (heal его не трогал), фон в его
|
||||||
|
* прямоугольнике не трогали, и все входы отрисовки те же, что в момент,
|
||||||
|
* когда этот кадр на эту страницу и рисовали. valid[p] здесь ключевой: он
|
||||||
|
* снимается heal'ом, поэтому решение остаётся согласованным, сколько раз его
|
||||||
|
* ни перепроверяй за кадр. */
|
||||||
|
static uint8_t cd_quiet(uint8_t who, uint8_t p)
|
||||||
|
{
|
||||||
|
cd_sig_t g;
|
||||||
|
const uint8_t *a, *b;
|
||||||
|
uint8_t i;
|
||||||
|
if (!pop_cd[who].valid[p]) return 0;
|
||||||
|
if (pop_cd_dirty & (1 << p)) { /* фон правили — но задели ли нас? */
|
||||||
|
int x0, y0, x1, y1;
|
||||||
|
cd_bbox(who, p, &x0, &y0, &x1, &y1);
|
||||||
|
y0 += POP_YOFF; y1 += POP_YOFF; /* метка фона — в ЭКРАННЫХ */
|
||||||
|
if (x0 < pop_cd_dx1[p] && pop_cd_dx0[p] < x1 &&
|
||||||
|
y0 < pop_cd_dy1[p] && pop_cd_dy0[p] < y1) return 0;
|
||||||
|
}
|
||||||
|
cd_sig_make(who, &g);
|
||||||
|
a = (const uint8_t *)&g; b = (const uint8_t *)&cd_sig[who][p];
|
||||||
|
for (i = 0; i < sizeof(cd_sig_t); i++)
|
||||||
|
if (*a++ != *b++) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Запас вокруг габарита соседа: за кадр персонаж проходит заметно меньше
|
||||||
|
* (бег — ~11 экранных px), а прыжок с падением укладывается сюда же. */
|
||||||
|
#define CD_MARGIN 32
|
||||||
|
|
||||||
|
uint8_t pop_char_skip_mask(void) __banked
|
||||||
|
{
|
||||||
|
uint8_t p = gfx_get_draw_page() & 1;
|
||||||
|
uint8_t q0 = cd_quiet(POP_CH_KID, p);
|
||||||
|
uint8_t q1 = cd_quiet(POP_CH_OPP, p);
|
||||||
|
if (q0 && q1) return 3; /* оба стоят — пересечься нечему */
|
||||||
|
if (q0 || q1) {
|
||||||
|
/* Один перерисовывается: его heal (и fore-проход по его окну) может
|
||||||
|
* задеть «тихого» соседа и выесть из него кусок. Прямоугольники
|
||||||
|
* рядом — перерисовываем ОБОИХ. */
|
||||||
|
uint8_t st = (uint8_t)(q0 ? POP_CH_KID : POP_CH_OPP);
|
||||||
|
uint8_t mv = (uint8_t)(st ^ 1);
|
||||||
|
if (pop_cd[mv].valid[p]) {
|
||||||
|
int ax0, ay0, ax1, ay1, bx0, by0, bx1, by1;
|
||||||
|
cd_bbox(st, p, &ax0, &ay0, &ax1, &ay1);
|
||||||
|
cd_bbox(mv, p, &bx0, &by0, &bx1, &by1);
|
||||||
|
if (ax0 < bx1 + CD_MARGIN && bx0 < ax1 + CD_MARGIN &&
|
||||||
|
ay0 < by1 + CD_MARGIN && by0 < ay1 + CD_MARGIN) return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (uint8_t)((q0 ? 1 : 0) | (q1 ? 2 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
void pop_char_set_render_dx(uint8_t who, int dx) __banked
|
void pop_char_set_render_dx(uint8_t who, int dx) __banked
|
||||||
{
|
{
|
||||||
pop_cd[who].render_dx = dx;
|
pop_cd[who].render_dx = dx;
|
||||||
@@ -243,6 +342,12 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
uint16_t w, h, vis_w;
|
uint16_t w, h, vis_w;
|
||||||
int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr;
|
int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr;
|
||||||
|
|
||||||
|
/* Страховка пропуска (DRAW-COST): главный цикл мог решить, что слот
|
||||||
|
* «тихий», и heal не звать — а тик после этого персонажа всё-таки
|
||||||
|
* сдвинул. Здесь прошлый кадр уже точно надо стереть. Если heal в этом
|
||||||
|
* кадре был, вызов бесплатен: valid/ovalid уже сняты. */
|
||||||
|
cd_heal(who);
|
||||||
|
|
||||||
s->cw = 0; /* окно fore-клипа: пусто, пока не нарисовали */
|
s->cw = 0; /* окно fore-клипа: пусто, пока не нарисовали */
|
||||||
s->fpw = 0; /* габарит кадра: нечего перекрывать */
|
s->fpw = 0; /* габарит кадра: нечего перекрывать */
|
||||||
|
|
||||||
@@ -360,6 +465,10 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
|
/* Что именно нарисовано на ЭТОЙ странице — снимок для пропуска
|
||||||
|
* следующих кадров (DRAW-COST). Только на полном пути: ранние выходы
|
||||||
|
* оставляют valid[dp] снятым, и cd_quiet отсеет слот и без снимка. */
|
||||||
|
cd_sig_make(who, &cd_sig[who][dp]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_char_fore(uint8_t who) __banked
|
void pop_char_fore(uint8_t who) __banked
|
||||||
|
|||||||
@@ -90,4 +90,45 @@ void pop_char_fore(uint8_t who) __banked;
|
|||||||
/* Straddle-смещение спрайта (см. render_dx). */
|
/* Straddle-смещение спрайта (см. render_dx). */
|
||||||
void pop_char_set_render_dx(uint8_t who, int dx) __banked;
|
void pop_char_set_render_dx(uint8_t who, int dx) __banked;
|
||||||
|
|
||||||
|
/* ---- Пропуск НЕИЗМЕНИВШЕГОСЯ кадра (DRAW-COST) ---------------------- *
|
||||||
|
* heal + спрайт + fore-проход стоят ~50 % кадрового периода НА ПЕРСОНАЖА
|
||||||
|
* (замер пользователя 2026-08-08: комната 1.3 с телом убитого стража — 210 %
|
||||||
|
* кадра, соседняя 1.2 без тела — 140 %). Платили мы это и за то, что не
|
||||||
|
* менялось: труп лежит неподвижно до выхода из комнаты, стоящий Кид — пока
|
||||||
|
* игрок не нажал клавишу, а перерисовывались они каждый кадр.
|
||||||
|
*
|
||||||
|
* Оптимизация: у каждой страницы дабл-буфера свой снимок ВХОДОВ отрисовки
|
||||||
|
* слота (поза, координаты, направление, меч, брызги, straddle). Если снимок
|
||||||
|
* совпал, спрайт этой страницы не стёрт и фона под ним никто не трогал —
|
||||||
|
* нужный кадр на странице УЖЕ нарисован, и heal/draw/fore пропускаются
|
||||||
|
* целиком.
|
||||||
|
*
|
||||||
|
* `pop_cd_touch` — «фон трогали ВОТ ЗДЕСЬ»: слой фона копит ПРЯМОУГОЛЬНИК
|
||||||
|
* своих правок по страницам дабл-буфера, и слот перерисовывается, только
|
||||||
|
* если тот задел его самого. Флага «трогали хоть что-то» не хватило:
|
||||||
|
* факелы анимируются КАЖДЫЙ кадр, и один факел в комнате обнулял пропуск
|
||||||
|
* для всех персонажей сразу (замер 2026-08-08: комната 1.1, Кид стоит —
|
||||||
|
* 597 684 такта, то есть ровно как без оптимизации).
|
||||||
|
*
|
||||||
|
* Метку ставит сам блит фона (`pop_blit_b`, по признаку «идёт не
|
||||||
|
* fore-проход») плюс те немногие места, что стирают фон голым `bar` или
|
||||||
|
* своим heal (падающий кусок, оверлей fore-heal). Координаты — ЭКРАННЫЕ
|
||||||
|
* (с POP_YOFF). Прямоугольник копится сразу для ОБЕИХ страниц: правку фона
|
||||||
|
* почти всегда повторяют на второй, а лишняя перерисовка персонажа дешевле
|
||||||
|
* лишнего состояния. Снимает метку главный цикл — после того, как
|
||||||
|
* персонажи на этой странице отрисованы.
|
||||||
|
*
|
||||||
|
* Резидент (pop_tile.c): зовёт и банковый слой фона, и резидентные листья,
|
||||||
|
* а трамплин на КАЖДЫЙ кусок фона стоил бы дороже самой метки. */
|
||||||
|
extern uint8_t pop_cd_dirty; /* биты страниц: прямоугольник взведён */
|
||||||
|
extern int pop_cd_dx0[2], pop_cd_dy0[2], pop_cd_dx1[2], pop_cd_dy1[2];
|
||||||
|
void pop_cd_touch(int x, int y, int w, int h);
|
||||||
|
#define POP_CD_TOUCH_ALL() pop_cd_touch(0, 0, 320, 256)
|
||||||
|
|
||||||
|
/* Кого в этом кадре можно НЕ перерисовывать: бит 0 — Кид, бит 1 — соперник.
|
||||||
|
* Звать ДВАЖДЫ: перед heal (стирать нечего) и перед отрисовкой (тик мог
|
||||||
|
* персонажа всё-таки сдвинуть — тогда бит снимется сам, а pop_char_draw
|
||||||
|
* сотрёт прошлый кадр за нас). */
|
||||||
|
uint8_t pop_char_skip_mask(void) __banked;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ void pop_spike_redraw(int row, int col) __banked
|
|||||||
* оставались бы; чёрная плита стирает старые бары в VRAM+ОЗУ-копию. */
|
* оставались бы; чёрная плита стирает старые бары в VRAM+ОЗУ-копию. */
|
||||||
void pop_room_redraw_seam_left(void) __banked
|
void pop_room_redraw_seam_left(void) __banked
|
||||||
{
|
{
|
||||||
|
pop_cd_touch(0, POP_YOFF + 3, 32, 62); /* bar мимо pop_blit_b (DRAW-COST) */
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
setfillstyle(SOLID_FILL, BLACK);
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
/* Полосу кладки У ПОТОЛКА (ряд −1) НЕ трогаем: её низ на room-space y=2,
|
/* Полосу кладки У ПОТОЛКА (ряд −1) НЕ трогаем: её низ на room-space y=2,
|
||||||
@@ -369,6 +370,7 @@ void pop_ceil_shake_draw(int col) __banked
|
|||||||
void pop_ceil_bake_empty(int col) __banked
|
void pop_ceil_bake_empty(int col) __banked
|
||||||
{
|
{
|
||||||
int x = POP_COL_XH[col] * 8;
|
int x = POP_COL_XH[col] * 8;
|
||||||
|
pop_cd_touch(x, POP_YOFF, 64, CEIL_BAND_H + 1); /* bar мимо pop_blit_b */
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
setfillstyle(SOLID_FILL, BLACK);
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
bar(x, POP_YOFF, x + 63, POP_YOFF + CEIL_BAND_H); /* стереть плиту в ОЗУ-копии */
|
bar(x, POP_YOFF, x + 63, POP_YOFF + CEIL_BAND_H); /* стереть плиту в ОЗУ-копии */
|
||||||
@@ -441,6 +443,7 @@ void pop_potion_draw(int row, int col, uint8_t modif) __banked
|
|||||||
void pop_floor_bake(int row, int col) __banked
|
void pop_floor_bake(int row, int col) __banked
|
||||||
{
|
{
|
||||||
int x = POP_COL_XH[col] * 8;
|
int x = POP_COL_XH[col] * 8;
|
||||||
|
pop_cd_touch(x, POP_YOFF + 63 * row + 26, 60, 39); /* bar мимо pop_blit_b */
|
||||||
int yb = POP_YOFF + 63 * row;
|
int yb = POP_YOFF + 63 * row;
|
||||||
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
||||||
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
||||||
@@ -490,6 +493,7 @@ void pop_gate_redraw(int row, int col) __banked
|
|||||||
if (col + 1 > 9 || row < 0 || row > 2) return;
|
if (col + 1 > 9 || row < 0 || row > 2) return;
|
||||||
x = POP_COL_XH[col + 1] * 8;
|
x = POP_COL_XH[col + 1] * 8;
|
||||||
yt = POP_YOFF + 63 * row;
|
yt = POP_YOFF + 63 * row;
|
||||||
|
pop_cd_touch(x, yt + 3, 32, 62); /* bar мимо pop_blit_b (DRAW-COST) */
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
setfillstyle(SOLID_FILL, BLACK);
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
bar(x, yt + 3, x + 31, yt + 64);
|
bar(x, yt + 3, x + 31, yt + 64);
|
||||||
@@ -521,6 +525,8 @@ void pop_loose_bake_empty(int row, int col) __banked
|
|||||||
* снова, и на второй странице дабл-буфера его не было → мерцание.
|
* снова, и на второй странице дабл-буфера его не было → мерцание.
|
||||||
* Тот же урок уже учтён в pop_floor_bake. */
|
* Тот же урок уже учтён в pop_floor_bake. */
|
||||||
yb += POP_YOFF;
|
yb += POP_YOFF;
|
||||||
|
/* bar идёт мимо pop_blit_b — пометить область самим (DRAW-COST). */
|
||||||
|
pop_cd_touch(x, yb, 72, POP_YOFF + 63 * row + 66 - yb);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
setfillstyle(SOLID_FILL, BLACK);
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
bar(x, yb, x + 39, POP_YOFF + 63 * row + 65); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */
|
bar(x, yb, x + 39, POP_YOFF + 63 * row + 65); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */
|
||||||
@@ -716,9 +722,16 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
|||||||
void pop_loose_mob_tick(void) __banked
|
void pop_loose_mob_tick(void) __banked
|
||||||
{
|
{
|
||||||
uint8_t pg = gfx_get_draw_page() & 1, i;
|
uint8_t pg = gfx_get_draw_page() & 1, i;
|
||||||
for (i = 0; i < MOB_MAX; i++)
|
for (i = 0; i < MOB_MAX; i++) {
|
||||||
|
/* Летящий кусок и его дочистка ходят по чужим пикселям СВОИМ heal'ом,
|
||||||
|
* мимо pop_blit_b — пока он в кадре, персонажей пропускать нельзя
|
||||||
|
* (DRAW-COST). Взводим ДО тика: кусок может исчезнуть прямо в нём, а
|
||||||
|
* стереть себя ему всё равно надо будет. */
|
||||||
|
if (mobs[i].active || mobs[i].clean)
|
||||||
|
pop_cd_touch(MOB_X0(mobs[i].x), mobs[i].y - 27 + POP_YOFF, MOB_W, 64);
|
||||||
mob_tick_one(&mobs[i], pg);
|
mob_tick_one(&mobs[i], pg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mob_render(mob_t *m, uint8_t pg)
|
static void mob_render(mob_t *m, uint8_t pg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <gfx.h>
|
#include <gfx.h>
|
||||||
#include <sprite.h>
|
#include <sprite.h>
|
||||||
#include "pop_bg.h" /* POP_YOFF */
|
#include "pop_bg.h" /* POP_YOFF */
|
||||||
|
#include "pop_cdraw.h" /* POP_CD_TOUCH — фон тронут, персонажей перерисовать */
|
||||||
#include "pop_tile.h"
|
#include "pop_tile.h"
|
||||||
#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */
|
#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */
|
||||||
#include "_pop_draw.h" /* pop_heal_fast */
|
#include "_pop_draw.h" /* pop_heal_fast */
|
||||||
@@ -88,6 +89,27 @@ uint8_t pop_loose_frame(uint8_t m)
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- «Фон трогали ВОТ ЗДЕСЬ» (DRAW-COST; контракт — pop_cdraw.h) ----- */
|
||||||
|
uint8_t pop_cd_dirty;
|
||||||
|
int pop_cd_dx0[2], pop_cd_dy0[2], pop_cd_dx1[2], pop_cd_dy1[2];
|
||||||
|
|
||||||
|
void pop_cd_touch(int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
uint8_t p;
|
||||||
|
for (p = 0; p < 2; p++) {
|
||||||
|
if (!(pop_cd_dirty & (1 << p))) {
|
||||||
|
pop_cd_dx0[p] = x; pop_cd_dy0[p] = y;
|
||||||
|
pop_cd_dx1[p] = x + w; pop_cd_dy1[p] = y + h;
|
||||||
|
} else {
|
||||||
|
if (x < pop_cd_dx0[p]) pop_cd_dx0[p] = x;
|
||||||
|
if (y < pop_cd_dy0[p]) pop_cd_dy0[p] = y;
|
||||||
|
if (x + w > pop_cd_dx1[p]) pop_cd_dx1[p] = x + w;
|
||||||
|
if (y + h > pop_cd_dy1[p]) pop_cd_dy1[p] = y + h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pop_cd_dirty = 3;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Блит куска атласа ---------------------------------------------- */
|
/* ---- Блит куска атласа ---------------------------------------------- */
|
||||||
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||||
{
|
{
|
||||||
@@ -137,6 +159,11 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
|||||||
} else {
|
} else {
|
||||||
gfx_blit_part(dx, dy, img, sx, sy, dw, dh); /* край экрана — с клипом */
|
gfx_blit_part(dx, dy, img, sx, sy, dw, dh); /* край экрана — с клипом */
|
||||||
}
|
}
|
||||||
|
/* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж —
|
||||||
|
* пометить область, чтобы его перерисовали (DRAW-COST, pop_cdraw.h).
|
||||||
|
* Признак «это не fore-проход поверх персонажа» — окно клипа: его
|
||||||
|
* ставит только он. */
|
||||||
|
if (!pop_t_fclip_on) pop_cd_touch(dx, dy, dw, dh);
|
||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -464,6 +464,7 @@ int main(void)
|
|||||||
* по умолчанию (без мерцания); SPACE переключает в однобуфер для
|
* по умолчанию (без мерцания); SPACE переключает в однобуфер для
|
||||||
* отладки багов рисования (edge по sp_prev, чтоб не дёргать). */
|
* отладки багов рисования (edge по sp_prev, чтоб не дёргать). */
|
||||||
uint8_t front = 0, back, dbuf = 1, sp_prev = 0, frozen = 0;
|
uint8_t front = 0, back, dbuf = 1, sp_prev = 0, frozen = 0;
|
||||||
|
uint8_t skip = 0; /* маска «слот перерисовывать не надо» */
|
||||||
uint16_t dead_frames = 0;
|
uint16_t dead_frames = 0;
|
||||||
#ifdef ROOMNAV
|
#ifdef ROOMNAV
|
||||||
uint8_t nav_prev = 0;
|
uint8_t nav_prev = 0;
|
||||||
@@ -645,8 +646,13 @@ int main(void)
|
|||||||
pop_check_skel(); /* спецсобытие ур.3: скелет встаёт */
|
pop_check_skel(); /* спецсобытие ур.3: скелет встаёт */
|
||||||
pop_check_can_guard_see_kid(); /* луч видимости — ДО логики персонажей */
|
pop_check_can_guard_see_kid(); /* луч видимости — ДО логики персонажей */
|
||||||
pop_ctrl_tick(); /* ввод -> control(): смена seq */
|
pop_ctrl_tick(); /* ввод -> control(): смена seq */
|
||||||
pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */
|
/* Кто в этом кадре не изменился с прошлой отрисовки ЭТОЙ страницы —
|
||||||
pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */
|
* тот на ней уже нарисован правильно: ни стирать, ни рисовать заново
|
||||||
|
* не надо (DRAW-COST, контракт в pop_cdraw.h). Решение
|
||||||
|
* перепроверяется перед самой отрисовкой — тик ещё впереди. */
|
||||||
|
skip = pop_char_skip_mask();
|
||||||
|
if (!(skip & 1)) pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */
|
||||||
|
if (!(skip & 2)) pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */
|
||||||
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
||||||
PROF(2); /* логика (тот же синий) */
|
PROF(2); /* логика (тот же синий) */
|
||||||
kid_tick(); /* play_seq: следующий кадр */
|
kid_tick(); /* play_seq: следующий кадр */
|
||||||
@@ -819,10 +825,14 @@ int main(void)
|
|||||||
* персонажа (см. guard_over_kid). Отрисовка одна на всех Char
|
* персонажа (см. guard_over_kid). Отрисовка одна на всех Char
|
||||||
* (pop_cdraw.c), различает их только слот. */
|
* (pop_cdraw.c), различает их только слот. */
|
||||||
uint8_t g_after = guard_over_kid();
|
uint8_t g_after = guard_over_kid();
|
||||||
if (!g_after) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); }
|
/* Пересчёт после тика: слот, за который heal не платили, мог
|
||||||
|
* всё-таки сдвинуться — тогда бит снимется, а прошлый кадр
|
||||||
|
* сотрёт сам pop_char_draw (страховка cd_heal). */
|
||||||
|
skip = pop_char_skip_mask();
|
||||||
|
if (!g_after && !(skip & 2)) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); }
|
||||||
PROF(6); /* кадр Кида (тот же циан) */
|
PROF(6); /* кадр Кида (тот же циан) */
|
||||||
pop_char_draw(POP_CH_KID); /* спрайт + брызги + клинок */
|
if (!(skip & 1)) pop_char_draw(POP_CH_KID); /* спрайт + брызги + клинок */
|
||||||
if (g_after) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); }
|
if (g_after && !(skip & 2)) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); }
|
||||||
/* Брызги урона рисует сама отрисовка персонажа (порядок objtable:
|
/* Брызги урона рисует сама отрисовка персонажа (порядок objtable:
|
||||||
* сразу после него и до клинка), а флаги снимаем здесь: соперника
|
* сразу после него и до клинка), а флаги снимаем здесь: соперника
|
||||||
* могло не оказаться в отрисованной комнате — тогда брызги просто
|
* могло не оказаться в отрисованной комнате — тогда брызги просто
|
||||||
@@ -840,7 +850,12 @@ int main(void)
|
|||||||
* pop_char_fore(KID) заодно возвращает окно Кида (при «соперник
|
* pop_char_fore(KID) заодно возвращает окно Кида (при «соперник
|
||||||
* поверх» в нём остался бы соперник, и передние грани у Кида
|
* поверх» в нём остался бы соперник, и передние грани у Кида
|
||||||
* отсеклись бы целиком — он рисовался бы поверх столбов). */
|
* отсеклись бы целиком — он рисовался бы поверх столбов). */
|
||||||
pop_char_fore(POP_CH_KID);
|
if (!(skip & 1)) pop_char_fore(POP_CH_KID);
|
||||||
|
/* Персонажи на этой странице приведены в порядок — след «фон трогали»
|
||||||
|
* для неё снят. ПОСЛЕ отрисовки, а не до: слой фона этого же кадра
|
||||||
|
* взводит бит и обязан быть учтён. Всё, что рисуется ниже
|
||||||
|
* (mob поверх Кида, борта), взводит его заново само. */
|
||||||
|
pop_cd_dirty = (uint8_t)(pop_cd_dirty & ~(1 << back));
|
||||||
pop_room_clip_borders(); /* почистить борта — только в кадрах падения (гейт) */
|
pop_room_clip_borders(); /* почистить борта — только в кадрах падения (гейт) */
|
||||||
|
|
||||||
/* Пейсинг 3 кадра, затем tear-free флип на vblank: draw уже
|
/* Пейсинг 3 кадра, затем tear-free флип на vblank: draw уже
|
||||||
|
|||||||
Reference in New Issue
Block a user