Пункт 0: отсев кладки по окну fore-клипа; закрыт BUG-CHEAT-FIGHT-1
Три шага пункта 0 из docs/perf_backlog.md. 1. Ранний выход из wall_pattern_palace по окну fore-клипа: узор целиком лежит в x [xh*8, xh*8+32), y [dmy-59, dby], и если окно его не задевает — возврат до первой заливки. Замер: от входа в узор до конца всего прохода 6 055 тактов вместо ~60 000 на тайл. 2. Отсев КАЖДОГО декаля (wp_blit) по реальному габариту. pop_blit_b тоже отсеивает до маппинга страницы, но по заведомо большему 64x64 — куски кладки высотой 3..12 px он пропускал и платил полный atlas_image + gfx_w0_map (~9 760 тактов на блит), чтобы там обнаружить, что рисовать нечего. Размеры сняты из каталогов атласов и заданы верхней границей по группе. 3. То же для ПОДЗЕМЕЛЬЯ: ранний выход wall_pattern (габарит выше — левая марка уходит на dby+POP_YOFF-67) плюс wp_blit на RNDBLOCK, обоих разделителях и обеих марках. Замер (уровень 4, комната 18, Кид сдвигается читом ] по пикселю, skip выключен, шесть тайлов в fore-окне, три из них — дворцовая стена): тайл стены ~12 700 вместо 60 000-74 000, fore-проход целиком 70 681 вместо 171 693, работа за кадр 419 839, период 3 растровых кадра вместо 4. Уровень 1 (подземелье) проверен снимком — кладка, марки, разделители на месте. Остаток в проходе — пролог pop_char_fore 17 208 тактов (пункт 1 backlog'а). Отдельно записано: на ШВЕ пролог разбухает до 134 730, причина не разобрана. BUG-CHEAT-FIGHT-1 (заведён 2026-08-07) закрыт по своему же плану: ветка ROOMNAV после kid_init/pop_kid_hp_reset гасит состояние схватки (Kid.sword = SWORD_0_SHEATHED, holding_sword, offguard, guard_refrac). Меч в инвентаре не теряется. Это болезнь чита: в оригинале телепорта между комнатами нет и в режим боя без соперника попасть нечем. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -97,11 +97,14 @@ MAME (`z80_profiling_method`) на роомтесте, уровень 1 комн
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Дворцовая кладка в fore-проходе — 27 % кадра НА НОЛЬ ПИКСЕЛЕЙ
|
## 0. Дворцовая кладка в fore-проходе — **СДЕЛАНО 2026-08-10**
|
||||||
|
|
||||||
**Цена: 139 863 такта за кадр (32 % растрового кадра), полезных пикселей —
|
Все три шага выполнены; замер после — в конце пункта. Ниже сохранён исходный
|
||||||
ровно ноль.** Самая дорогая известная позиция; снятие её одной уводит
|
разбор: он объясняет, почему предфильтра `fore_tile` мало и откуда взялись
|
||||||
работу с 517 609 до ~378 000, то есть период с **4 растровых кадров на 3**.
|
габариты кусков.
|
||||||
|
|
||||||
|
**Было: 139 863 такта за кадр (32 % растрового кадра), полезных пикселей —
|
||||||
|
ровно ноль.**
|
||||||
|
|
||||||
Замер (уровень 4, Кид на (1,7)). Окно fore-клипа в этот момент —
|
Замер (уровень 4, Кид на (1,7)). Окно fore-клипа в этот момент —
|
||||||
`x 229..241, y 106..147` (прочитано из `pop_t_fclip_*` брейкпоинтом).
|
`x 229..241, y 106..147` (прочитано из `pop_t_fclip_*` брейкпоинтом).
|
||||||
@@ -154,6 +157,38 @@ fclip_y0`) намеренно грубый — габарит 40×78 на тай
|
|||||||
составом, а тем, что оригинал складывает всё в `foretable` и рисует одним
|
составом, а тем, что оригинал складывает всё в `foretable` и рисует одним
|
||||||
`draw_table()`, у которого «посетить тайл» стоит копейки (см. п. 6).
|
`draw_table()`, у которого «посетить тайл» стоит копейки (см. п. 6).
|
||||||
|
|
||||||
|
### Что сделано и сколько дало
|
||||||
|
|
||||||
|
1. **Ранний выход** из `wall_pattern_palace` по окну fore-клипа (узор целиком
|
||||||
|
в `x [xh*8, xh*8+32)`, `y [dmy−59, dby]`).
|
||||||
|
2. **Отсев каждого декаля** (`wp_blit`) по реальному габариту вместо
|
||||||
|
заведомо большего 64×64 в `pop_blit_b`. Размеры сняты из каталогов
|
||||||
|
атласов: `pal_wall.atl` — группы 3..5 = 8×7, 6..8 и 9..11 = 32×12,
|
||||||
|
12..14 = 30×5, 15..17 = 32×3.
|
||||||
|
3. **То же для ПОДЗЕМЕЛЬЯ**: ранний выход `wall_pattern` (габарит там выше —
|
||||||
|
левая марка уходит на `dby+POP_YOFF−67`) плюс `wp_blit` на RNDBLOCK
|
||||||
|
(32×21), обоих разделителях (9×21) и обеих марках (`pop_wall.atl`:
|
||||||
|
16/17 = 7×10, 14/15 = 14×5).
|
||||||
|
|
||||||
|
Замер: уровень 4, комната 18, Кид сдвигается читом `]` по пикселю (skip
|
||||||
|
выключен, идёт полный путь), в fore-окне ШЕСТЬ тайлов, ТРИ из них —
|
||||||
|
дворцовая стена.
|
||||||
|
|
||||||
|
| участок | тактов |
|
||||||
|
|---|---:|
|
||||||
|
| пролог `pop_char_fore` до первого `fore_tile` | 17 208 |
|
||||||
|
| обычный тайл | 4 000 – 4 600 |
|
||||||
|
| **тайл стены (было 60 000 – 74 000)** | **~12 700** |
|
||||||
|
| хвост + чистка бортов | 6 055 |
|
||||||
|
| **fore-проход целиком (было 171 693)** | **70 681** |
|
||||||
|
|
||||||
|
Кадр целиком в этой сцене: работа **419 839**, период **3** растровых кадра
|
||||||
|
(было 517 609 и 4).
|
||||||
|
|
||||||
|
Остаток в проходе — пролог 17 208, это уже пункт 1 ниже (футпринт из физики).
|
||||||
|
Отдельная находка: когда Кид стоит НА ШВЕ (окно `x −8..5`), пролог разбухает
|
||||||
|
до **134 730** — 86 % прохода; причина не разобрана, см. пункт 1.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Футпринт персонажа — брать из физики, а не считать заново
|
## 1. Футпринт персонажа — брать из физики, а не считать заново
|
||||||
|
|||||||
@@ -13,6 +13,51 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<a id="bug-cheat-fight-1"></a>
|
||||||
|
## BUG-CHEAT-FIGHT-1. Чит `+`/`−` в бою: Кид остаётся в режиме боя и теряет управление
|
||||||
|
|
||||||
|
**Наблюдение (пользователь, 2026-08-07).** Если нажать `+`/`−` (ROOMNAV,
|
||||||
|
переход по комнатам) в момент, когда Кид вытащил меч для битвы, — в новой
|
||||||
|
комнате Кид не управляется: нажатия стрелок игнорируются.
|
||||||
|
|
||||||
|
**Корень (прочитан по коду, сверен с seg005).** Чит ROOMNAV
|
||||||
|
(`roomtest.c:576`) телепортирует и сбрасывает позу и HP —
|
||||||
|
`enter_room()`, `kid_init(SEQ_STAND, …)`, `pop_kid_hp_reset()`, — но **не
|
||||||
|
трогает состояние боя**: `Kid.sword` остаётся `SWORD_2_DRAWN`. Диспетчер
|
||||||
|
`pop_control` (`pop_ctrl.c:578`) при вынутом мече уходит в
|
||||||
|
`control_with_sword`, а там единственный выход из режима боя —
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (Char.frame == FRAME_171_STAND_WITH_SWORD) { /* seg005:987 */
|
||||||
|
Char.sword = SWORD_0_SHEATHED;
|
||||||
|
seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Стража в новой комнате нет (`can_guard_see_kid` = 0), кадр после
|
||||||
|
`kid_init(SEQ_STAND)` — обычная стойка, а не 171, поэтому не срабатывает ни
|
||||||
|
`swordfight()`, ни ветка «убрать меч»: `control_with_sword` каждый кадр
|
||||||
|
не делает НИЧЕГО, и ввод не доходит до движения. Заклинивание вечное.
|
||||||
|
|
||||||
|
Это баг **нашего чита**, а не порта: в оригинале телепорта между комнатами
|
||||||
|
нет, и в режим боя без соперника попасть нечем.
|
||||||
|
|
||||||
|
**Как чинить.** В ветке ROOMNAV телепорт трактовать как выход из боя (то же
|
||||||
|
самое, что делает `pop_start_level`): `Kid.sword = SWORD_0_SHEATHED`,
|
||||||
|
`holding_sword = 0`, сбросить `offguard`/`guard_refrac` и состояние стража
|
||||||
|
(`pop_guard_reset()` вызывается по входу в комнату — проверить, что он
|
||||||
|
обнуляет `Opp`). Меч в инвентаре (`pop_have_sword`) при этом НЕ терять.
|
||||||
|
Второй кандидат на ту же болезнь — чит `K` (убить стража) в момент, когда
|
||||||
|
Кид в стойке с мечом, но не в кадре 171: там оригинал сам доводит до 171
|
||||||
|
через `swordfight`, так что проверить сценарием, а не менять вслепую.
|
||||||
|
|
||||||
|
**ЗАКРЫТ 2026-08-10.** Сделано ровно по плану выше: ветка ROOMNAV после
|
||||||
|
`kid_init`/`pop_kid_hp_reset` гасит состояние схватки —
|
||||||
|
`Kid.sword = SWORD_0_SHEATHED`, `holding_sword = 0`, `offguard = 0`,
|
||||||
|
`guard_refrac = 0`. Меч в инвентаре (`pop_have_sword`) не теряется, только
|
||||||
|
режим боя.
|
||||||
|
|
||||||
|
|
||||||
<a id="bug-loose-button-1"></a>
|
<a id="bug-loose-button-1"></a>
|
||||||
## BUG-LOOSE-BUTTON-1. Упавшая плита не нажимает кнопку — **ЗАКРЫТ 2026-08-10**
|
## BUG-LOOSE-BUTTON-1. Упавшая плита не нажимает кнопку — **ЗАКРЫТ 2026-08-10**
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
| ID | что | тип | статус |
|
| ID | что | тип | статус |
|
||||||
|----|-----|-----|--------|
|
|----|-----|-----|--------|
|
||||||
| [BUG-CHEAT-FIGHT-1](#bug-cheat-fight-1) | `+`/`−` в бою с вынутым мечом → Кид теряет управление | Major (чит) | открыт |
|
| ~~BUG-CHEAT-FIGHT-1~~ | `+`/`−` в бою с вынутым мечом → Кид теряет управление | Major (чит) | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-cheat-fight-1) |
|
||||||
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
|
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
|
||||||
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводимый: на повторе не поднялся; на пререлиз |
|
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводимый: на повторе не поднялся; на пререлиз |
|
||||||
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
|
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
|
||||||
@@ -52,44 +52,6 @@
|
|||||||
|
|
||||||
Ниже — то, что осталось открытым после прогона 2026-08-07.
|
Ниже — то, что осталось открытым после прогона 2026-08-07.
|
||||||
|
|
||||||
<a id="bug-cheat-fight-1"></a>
|
|
||||||
## BUG-CHEAT-FIGHT-1. Чит `+`/`−` в бою: Кид остаётся в режиме боя и теряет управление
|
|
||||||
|
|
||||||
**Наблюдение (пользователь, 2026-08-07).** Если нажать `+`/`−` (ROOMNAV,
|
|
||||||
переход по комнатам) в момент, когда Кид вытащил меч для битвы, — в новой
|
|
||||||
комнате Кид не управляется: нажатия стрелок игнорируются.
|
|
||||||
|
|
||||||
**Корень (прочитан по коду, сверен с seg005).** Чит ROOMNAV
|
|
||||||
(`roomtest.c:576`) телепортирует и сбрасывает позу и HP —
|
|
||||||
`enter_room()`, `kid_init(SEQ_STAND, …)`, `pop_kid_hp_reset()`, — но **не
|
|
||||||
трогает состояние боя**: `Kid.sword` остаётся `SWORD_2_DRAWN`. Диспетчер
|
|
||||||
`pop_control` (`pop_ctrl.c:578`) при вынутом мече уходит в
|
|
||||||
`control_with_sword`, а там единственный выход из режима боя —
|
|
||||||
|
|
||||||
```c
|
|
||||||
if (Char.frame == FRAME_171_STAND_WITH_SWORD) { /* seg005:987 */
|
|
||||||
Char.sword = SWORD_0_SHEATHED;
|
|
||||||
seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Стража в новой комнате нет (`can_guard_see_kid` = 0), кадр после
|
|
||||||
`kid_init(SEQ_STAND)` — обычная стойка, а не 171, поэтому не срабатывает ни
|
|
||||||
`swordfight()`, ни ветка «убрать меч»: `control_with_sword` каждый кадр
|
|
||||||
не делает НИЧЕГО, и ввод не доходит до движения. Заклинивание вечное.
|
|
||||||
|
|
||||||
Это баг **нашего чита**, а не порта: в оригинале телепорта между комнатами
|
|
||||||
нет, и в режим боя без соперника попасть нечем.
|
|
||||||
|
|
||||||
**Как чинить.** В ветке ROOMNAV телепорт трактовать как выход из боя (то же
|
|
||||||
самое, что делает `pop_start_level`): `Kid.sword = SWORD_0_SHEATHED`,
|
|
||||||
`holding_sword = 0`, сбросить `offguard`/`guard_refrac` и состояние стража
|
|
||||||
(`pop_guard_reset()` вызывается по входу в комнату — проверить, что он
|
|
||||||
обнуляет `Opp`). Меч в инвентаре (`pop_have_sword`) при этом НЕ терять.
|
|
||||||
Второй кандидат на ту же болезнь — чит `K` (убить стража) в момент, когда
|
|
||||||
Кид в стойке с мечом, но не в кадре 171: там оригинал сам доводит до 171
|
|
||||||
через `swordfight`, так что проверить сценарием, а не менять вслепую.
|
|
||||||
|
|
||||||
<a id="bug-spike-1"></a>
|
<a id="bug-spike-1"></a>
|
||||||
## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
|
## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
|
||||||
|
|
||||||
@@ -310,7 +272,7 @@ Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал
|
|||||||
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
|
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
|
||||||
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
|
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
|
||||||
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
|
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
|
||||||
Известный баг этого чита — [BUG-CHEAT-FIGHT-1](#bug-cheat-fight-1).
|
Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](bug_closed.md#bug-cheat-fight-1).
|
||||||
- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство
|
- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство
|
||||||
данных уровня (разбор — «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md));
|
данных уровня (разбор — «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md));
|
||||||
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.
|
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ static const uint8_t RPOS[4] = {52, 42, 31, 21};
|
|||||||
* читает кэш раскладки кладки. */
|
* читает кэш раскладки кладки. */
|
||||||
#define prandom(maxv) pop_prandom(&pop_t_seed, (maxv))
|
#define prandom(maxv) pop_prandom(&pop_t_seed, (maxv))
|
||||||
|
|
||||||
|
/* Отсев кусков кладки по окну fore-клипа — тела ниже, у wpp_fill. */
|
||||||
|
static uint8_t wp_vis(int x, int w, int ytop, int ybot);
|
||||||
|
static void wp_blit(uint8_t id, int x, int ybot, uint8_t w, uint8_t h);
|
||||||
|
|
||||||
/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */
|
/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */
|
||||||
static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
|
static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
|
||||||
{
|
{
|
||||||
@@ -87,7 +91,8 @@ static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
|
|||||||
int dby = 63 * row + 65;
|
int dby = 63 * row + 65;
|
||||||
if (dv > 3) lv2 = arg1 + 6;
|
if (dv > 3) lv2 = arg1 + 6;
|
||||||
else if (dv > 1) lv2 = arg2 + 6;
|
else if (dv > 1) lv2 = arg2 + 6;
|
||||||
pop_wall_b(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2, dby - LPOS[dv]);
|
wp_blit(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2,
|
||||||
|
dby - LPOS[dv], 7, 10); /* pop_wall.atl: 16/17 = 7x10 */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
|
static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
|
||||||
@@ -96,7 +101,8 @@ static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
|
|||||||
int a = (dv < 2) ? 24 : arg1 - 3;
|
int a = (dv < 2) ? 24 : arg1 - 3;
|
||||||
int xh = POP_COL_XH[col];
|
int xh = POP_COL_XH[col];
|
||||||
int dby = 63 * row + 65;
|
int dby = 63 * row + 65;
|
||||||
pop_wall_b(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a, dby - RPOS[dv]);
|
wp_blit(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a,
|
||||||
|
dby - RPOS[dv], 14, 5); /* pop_wall.atl: 14/15 = 14x5 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */
|
/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */
|
||||||
@@ -290,6 +296,32 @@ static void wall_rnd_palace(int row, int col)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Прямоугольник [x, x+w) x [ytop, ybot] (ЭКРАННЫЕ) задевает окно fore-клипа?
|
||||||
|
* Клипа нет — задевает всегда. */
|
||||||
|
static uint8_t wp_vis(int x, int w, int ytop, int ybot)
|
||||||
|
{
|
||||||
|
if (!pop_t_fclip_on) return 1;
|
||||||
|
return (uint8_t)(x < pop_t_fclip_x1 && x + w > pop_t_fclip_x0 &&
|
||||||
|
ytop < pop_t_fclip_y1 && ybot >= pop_t_fclip_y0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Декаль кладки: ybot — БЕЗ POP_YOFF (как ждёт pop_wall_b), w/h — реальный
|
||||||
|
* габарит куска. Размеры взяты из каталогов атласов (pal_wall.atl /
|
||||||
|
* pop_wall.atl) и заданы верхней границей по группе, поэтому промахнуться в
|
||||||
|
* меньшую сторону нельзя: хуже отсева не будет, лишнего не сотрём.
|
||||||
|
*
|
||||||
|
* Зачем: pop_blit_b тоже отсеивает до маппинга страницы, но по ЗАВЕДОМО
|
||||||
|
* большему габариту 64x64 — куски кладки высотой 3..12 px он пропускает и
|
||||||
|
* платит полный atlas_image + gfx_w0_map + чтение w/h (~9 760 тактов на
|
||||||
|
* блит), чтобы там обнаружить, что рисовать нечего. Пять таких блитов на
|
||||||
|
* тайл — 48 500 тактов в никуда (docs/perf_backlog.md, пункт 0 шаг 2). */
|
||||||
|
static void wp_blit(uint8_t id, int x, int ybot, uint8_t w, uint8_t h)
|
||||||
|
{
|
||||||
|
int yb = ybot + POP_YOFF;
|
||||||
|
if (!wp_vis(x, (int)w, yb - (int)h + 1, yb)) return;
|
||||||
|
pop_wall_b(id, x, ybot);
|
||||||
|
}
|
||||||
|
|
||||||
/* Заливка куска стены. x/ytop — уже ЭКРАННЫЕ (с POP_YOFF). В fore-проходе
|
/* Заливка куска стены. x/ytop — уже ЭКРАННЫЕ (с POP_YOFF). В fore-проходе
|
||||||
* режем по окну клипа: без этого заливка легла бы поверх областей, которые
|
* режем по окну клипа: без этого заливка легла бы поверх областей, которые
|
||||||
* в этом кадре никто не восстанавливает. */
|
* в этом кадре никто не восстанавливает. */
|
||||||
@@ -319,6 +351,23 @@ static void wall_pattern_palace(int row, int col, int which_part)
|
|||||||
int dbys = 63 * row + 65; /* база БЕЗ POP_YOFF — для блитов */
|
int dbys = 63 * row + 65; /* база БЕЗ POP_YOFF — для блитов */
|
||||||
int dmys = dbys - 3;
|
int dmys = dbys - 3;
|
||||||
int dby = dbys + POP_YOFF, dmy = dmys + POP_YOFF; /* экранные — для заливок */
|
int dby = dbys + POP_YOFF, dmy = dmys + POP_YOFF; /* экранные — для заливок */
|
||||||
|
/* РАННИЙ ВЫХОД по окну fore-клипа. Весь узор лежит в прямоугольнике
|
||||||
|
* x [xh*8, xh*8+32), y [dmy-59, dby]: сверху — первая заливка, снизу —
|
||||||
|
* нижний декаль, чей низ ровно на dby.
|
||||||
|
*
|
||||||
|
* Без этой проверки тайл стены под ногами Кида стоил ~60 000 тактов НА
|
||||||
|
* НОЛЬ ПИКСЕЛЕЙ: предфильтр fore_tile грубый (габарит 40x78), wpp_fill
|
||||||
|
* честно режет по окну, но платит ~3 100 тактов за арифметику клипа, а
|
||||||
|
* pop_wall_b об окне не знает вовсе и идёт в atlas_image + gfx_w0_map,
|
||||||
|
* чтобы там обнаружить, что рисовать нечего. Два таких тайла (ряд 2 под
|
||||||
|
* Кидом попадает в fore-окно всегда) = 139 863 такта за кадр, 32 %
|
||||||
|
* растрового кадра. См. docs/perf_backlog.md, пункт 0. */
|
||||||
|
if (pop_t_fclip_on) {
|
||||||
|
int x0 = xh * 8;
|
||||||
|
if (x0 >= pop_t_fclip_x1 || x0 + 32 <= pop_t_fclip_x0 ||
|
||||||
|
dmy - 59 >= pop_t_fclip_y1 || dby + 1 <= pop_t_fclip_y0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* Цвета лежат по рядам 0..2; полоса у потолка (row −1) — это ряд 2
|
/* Цвета лежат по рядам 0..2; полоса у потолка (row −1) — это ряд 2
|
||||||
* комнаты сверху, оригинал и сид для неё берёт по ряду 2. */
|
* комнаты сверху, оригинал и сид для неё берёт по ряду 2. */
|
||||||
const uint8_t *cl = pop_pal_wall_col + 44 * (row < 0 ? 2 : row) + col;
|
const uint8_t *cl = pop_pal_wall_col + 44 * (row < 0 ? 2 : row) + col;
|
||||||
@@ -329,13 +378,16 @@ static void wall_pattern_palace(int row, int col, int which_part)
|
|||||||
wpp_fill((xh + 2) * 8, dmy - 39, 16, 21, cl[12]);
|
wpp_fill((xh + 2) * 8, dmy - 39, 16, 21, cl[12]);
|
||||||
wpp_fill(xh * 8, dmy - 18, 8, 19, cl[22]);
|
wpp_fill(xh * 8, dmy - 18, 8, 19, cl[22]);
|
||||||
wpp_fill((xh + 1) * 8, dmy - 18, 24, 19, cl[23]);
|
wpp_fill((xh + 1) * 8, dmy - 18, 24, 19, cl[23]);
|
||||||
pop_wall_b((uint8_t)(wpp_d[0] + 3), (xh + 3) * 8, dmys - 53);
|
/* Габариты групп из pal_wall.atl: 3..5 = 8x7, 6..8 = 32x12,
|
||||||
pop_wall_b((uint8_t)(wpp_d[1] + 6), xh * 8, dmys - 34);
|
* 9..11 = 32x12 (11-й 28x11), 12..14 = 30x5 (14-й 30x4),
|
||||||
pop_wall_b((uint8_t)(wpp_d[2] + 9), xh * 8, dmys - 13);
|
* 15..17 = 32x3. Берём максимум по группе. */
|
||||||
pop_wall_b((uint8_t)(wpp_d[3] + 12), xh * 8, dmys);
|
wp_blit((uint8_t)(wpp_d[0] + 3), (xh + 3) * 8, dmys - 53, 8, 7);
|
||||||
|
wp_blit((uint8_t)(wpp_d[1] + 6), xh * 8, dmys - 34, 32, 12);
|
||||||
|
wp_blit((uint8_t)(wpp_d[2] + 9), xh * 8, dmys - 13, 32, 12);
|
||||||
|
wp_blit((uint8_t)(wpp_d[3] + 12), xh * 8, dmys, 30, 5);
|
||||||
}
|
}
|
||||||
wpp_fill(xh * 8, dby - 2, 32, 3, cl[33]);
|
wpp_fill(xh * 8, dby - 2, 32, 3, cl[33]);
|
||||||
pop_wall_b((uint8_t)((which_part ? wpp_d[4] : wpp_d[0]) + 15), xh * 8, dbys);
|
wp_blit((uint8_t)((which_part ? wpp_d[4] : wpp_d[0]) + 15), xh * 8, dbys, 32, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wall_pattern(int row, int col, int which_part)
|
static void wall_pattern(int row, int col, int which_part)
|
||||||
@@ -344,18 +396,29 @@ static void wall_pattern(int row, int col, int which_part)
|
|||||||
int dby = 63 * row + 65;
|
int dby = 63 * row + 65;
|
||||||
uint8_t parts;
|
uint8_t parts;
|
||||||
if (pop_palace) { wall_pattern_palace(row, col, which_part); return; }
|
if (pop_palace) { wall_pattern_palace(row, col, which_part); return; }
|
||||||
|
/* Ранний выход по окну fore-клипа — то же, что у паласной ветки, но свой
|
||||||
|
* габарит. Самый верхний кусок здесь — ЛЕВАЯ марка: LPOS[4] = 58 плюс её
|
||||||
|
* высота 10, то есть dby+POP_YOFF−67; самый нижний — нижний разделитель,
|
||||||
|
* низ ровно на dby+POP_YOFF. По x правая марка уходит до xh*8+46
|
||||||
|
* (a = 24 при dv<2, ширина 14), берём 48. */
|
||||||
|
{
|
||||||
|
int x0 = xh * 8, dbs = dby + POP_YOFF;
|
||||||
|
if (!wp_vis(x0, 48, dbs - 67, dbs)) return;
|
||||||
|
}
|
||||||
parts = WP_PARTS[pop_wall_modifier(row, col)];
|
parts = WP_PARTS[pop_wall_modifier(row, col)];
|
||||||
|
|
||||||
wall_rnd(row, col);
|
wall_rnd(row, col);
|
||||||
if (which_part) {
|
if (which_part) {
|
||||||
|
/* Габариты из pop_wall.atl: 13 = 32x21, 11/12 = 9x21. */
|
||||||
if ((parts & WP_RND) && wr_rndblk)
|
if ((parts & WP_RND) && wr_rndblk)
|
||||||
pop_wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
|
wp_blit(RES_WALL_RNDBLOCK, xh * 8, dby - 42, 32, 21);
|
||||||
if (parts & WP_MID)
|
if (parts & WP_MID)
|
||||||
pop_wall_b((uint8_t)(RES_WALL_DIVIDER1 + wr_mid_div),
|
wp_blit((uint8_t)(RES_WALL_DIVIDER1 + wr_mid_div),
|
||||||
(xh + 1) * 8 + wr_mid_off, dby - 21);
|
(xh + 1) * 8 + wr_mid_off, dby - 21, 9, 21);
|
||||||
}
|
}
|
||||||
if (parts & WP_BOT)
|
if (parts & WP_BOT)
|
||||||
pop_wall_b((uint8_t)(RES_WALL_DIVIDER1 + wr_bot_div), xh * 8 + wr_bot_off, dby);
|
wp_blit((uint8_t)(RES_WALL_DIVIDER1 + wr_bot_div),
|
||||||
|
xh * 8 + wr_bot_off, dby, 9, 21);
|
||||||
if (which_part) {
|
if (which_part) {
|
||||||
if ((parts & WP_RIGHT) && wr_right)
|
if ((parts & WP_RIGHT) && wr_right)
|
||||||
draw_right_mark(wr_rightv, wr_mid_off, row, col);
|
draw_right_mark(wr_rightv, wr_mid_off, row, col);
|
||||||
|
|||||||
@@ -663,6 +663,21 @@ int main(void)
|
|||||||
kid_init(SEQ_STAND, (int8_t)(pos % 10), (int8_t)(pos / 10), 0);
|
kid_init(SEQ_STAND, (int8_t)(pos % 10), (int8_t)(pos / 10), 0);
|
||||||
pop_kid_hp_reset();
|
pop_kid_hp_reset();
|
||||||
dead_frames = 0;
|
dead_frames = 0;
|
||||||
|
/* ВЫХОД ИЗ БОЯ (BUG-CHEAT-FIGHT-1). Телепорт с вынутым мечом
|
||||||
|
* заклинивал управление намертво: pop_control при
|
||||||
|
* Kid.sword == SWORD_2_DRAWN уходит в control_with_sword, а
|
||||||
|
* там единственный выход — ветка «убрать меч» на кадре
|
||||||
|
* FRAME_171_STAND_WITH_SWORD. После kid_init(SEQ_STAND) кадр
|
||||||
|
* обычный, стража в новой комнате нет, swordfight не
|
||||||
|
* запускается — и диспетчер каждый кадр не делает НИЧЕГО.
|
||||||
|
* Это болезнь ЧИТА: в оригинале телепорта между комнатами
|
||||||
|
* нет, попасть в режим боя без соперника нечем. Меч в
|
||||||
|
* инвентаре (pop_have_sword) НЕ теряем — только состояние
|
||||||
|
* схватки. */
|
||||||
|
Kid.sword = SWORD_0_SHEATHED;
|
||||||
|
holding_sword = 0;
|
||||||
|
offguard = 0;
|
||||||
|
guard_refrac = 0;
|
||||||
}
|
}
|
||||||
nav_prev = nav;
|
nav_prev = nav;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user