From 4db60c750f780dda3e1a575e7ed54c8e49c9fdce Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Tue, 18 Aug 2026 23:10:41 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20B:?= =?UTF-8?q?=20=D0=BA=D1=83=D1=81=D0=BE=D0=BA=20=D1=81=20=D0=B7=D0=B0=D0=B2?= =?UTF-8?q?=D1=91=D1=80=D0=BD=D1=83=D1=82=D1=8B=D0=BC=20=D1=80=D1=8F=D0=B4?= =?UTF-8?q?=D0=BE=D0=BC=20=D1=80=D0=B8=D1=81=D1=83=D0=B5=D1=82=D1=81=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=20=D0=B2=D1=81=D0=B5=D0=BC=20(=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D0=B7=D0=B8=D0=BD=D0=B0=2030)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Порт правила оригинала, разобранного в 272cf8f. y_to_row_mod4 даёт −1 и для куска выше потолка, и для ушедшего ниже комнаты; get_tilepos_nominus сводит оба в тайл 30, а объекты тайла 30 рисуются в redraw_needed_tiles ПЕРВЫМИ, до всего обхода тайлов. Что сделано: - defer = 0 для таких кусков: они под всем, включая Кида. Раньше сравнение рядов читало −1 как «обходится последним» = «поверх всего»; - оверлею отдаётся ориентир 3 («раньше любого ряда 2,1,0») вместо сырого −1 — гейт other_overlay_tile перестал отбрасывать возврат соседа, из-за чего тело плиты не возвращалось и оставался только её торец из переднего слоя; - в набор перекрываемых тайлов добавлена СВОЯ клетка (только для этого случая: у куска в обычном ряду объект вливается в midtable после частей своего тайла, и перерисовывать её нельзя). Отладочная обвязка разбора (журнал решений оверлея, маска перекрывающих тайлов) снята; счётчик перерисовок за кадр в pop_redraw_needed оставлен — он дешёвый и пригодится для HEAL-WIDTH. ЗАМЕР 13/23, 3032 кадра, против тега mob-order-B-start: работа 888 984 -> 913 848 (+24 864) синяя 159 804 -> 159 810 зелёная 427 242 -> 440 418 (+13 176) циан 378 864 -> 393 000 (+14 136) Период кадра не изменился: 4 растра в 23 кадрах, 5 в двух. Зелёная вышла за растровый кадр (440 418 против 430 000). Детализация: pop_loose_tick 185 826, из них pop_loose_mob_tick 168 180; тробы + redraw_needed 337 800. Разбор и план возврата тактов — HEAL-WIDTH. Визуальная проверка комнаты 14 за пользователем: поймать кадр с куском снимками мне не удалось. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/BUGS_CLOSED.md | 43 +++++++++++ applications/PoP/roomtest/pop_room.c | 96 +++++++++++------------- 2 files changed, 87 insertions(+), 52 deletions(-) diff --git a/applications/PoP/roomtest/BUGS_CLOSED.md b/applications/PoP/roomtest/BUGS_CLOSED.md index cd128cf..5f1f5b6 100644 --- a/applications/PoP/roomtest/BUGS_CLOSED.md +++ b/applications/PoP/roomtest/BUGS_CLOSED.md @@ -3337,3 +3337,46 @@ draw_tile_anim(); draw_tile_bottom(0); draw_loose(0); `draw_tile_anim_right` — то есть анимированной правой грани СОСЕДА (пики, ворота, loose). Симптома на это пока не видели; если всплывёт «правая грань соседа под персонажем/куском» — смотреть сюда. + +### Вариант B: порт корзины 30 (2026-08-18) + +Реализовано то, что разобрано в коммите `272cf8f`. Кусок, у которого ряд +объекта вне 0..2 (`y_to_row_mod4` даёт −1 и «выше потолка», и «ниже пола»), +считается объектом ТАЙЛА 30 — а такие оригинал рисует ПЕРВЫМИ, до всего +обхода тайлов. Два следствия: + +- `defer = 0` — кусок идёт под всем, включая Кида (раньше сравнение рядов + давало обратное: «−1 обходится последним» читалось как «поверх»); +- оверлею отдаётся ориентир **3** («раньше любого ряда обхода 2,1,0») + вместо сырого −1, и в набор перекрываемых тайлов добавляется **своя + клетка** — у куска в обычном ряду её перерисовывать нельзя, там объект + вливается в midtable уже после частей своего тайла. + +**Замер 13/23 (3032 кадра, зонды фаз + детализация зелёной):** + +| секция | тег `mob-order-B-start` | после B | дельта | +|---|---:|---:|---:| +| работа | 888 984 | **913 848** | +24 864 | +| синяя | 159 804 | 159 810 | +6 | +| зелёная | 427 242 | **440 418** | +13 176 | +| циан | 378 864 | 393 000 | +14 136 | + +Период кадра не изменился: 4 растра в 23 кадрах, 5 в двух — как и до правки. + +**Зелёная вышла за растровый кадр** (440 418 против 430 000) при цели +400 000. Детализация показывает, где именно: + +| участок зелёной | максимум | +|---|---:| +| `pop_loose_tick` целиком | 185 826 | +| — из них `pop_loose_mob_tick` | **168 180** | +| — циклы по тайлам | 23 736 | +| — `check_loose_fall_on_kid` | 5 868 | +| тробы + `pop_redraw_needed` | 337 800 | +| шов и прочее | 11 964 | + +(максимумы взяты по разным кадрам, поэтому не складываются в общий). + +Основной кандидат на возврат этих тактов — задача +[HEAL-WIDTH](TASKS_OPEN.md#heal-width): heal'ы кусков и плит берут ширину по +клеткам, а фактический след — 58/57. diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 4ab7fe9..2007c21 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -1203,13 +1203,6 @@ static void mob_spawn(uint8_t room, int row, int col) m->defer = 0; m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; m->draw_y = MOB_Y_NONE; - { /* ВРЕМЕННО: журнал решения оверлея — копим за весь полёт */ - uint8_t k = (uint8_t)(m - mobs); - pop_dbg_pass[k] = 0; - pop_dbg_ovl[k * 4 + 2] = 0; - pop_dbg_ovl[k * 4 + 3] = 0; - pop_dbg_tiles[k] = 0; - } } /* Отрыв куска в ТЕКУЩЕЙ комнате (обычный случай — pop_loose_tick). */ @@ -1497,7 +1490,18 @@ static void mob_tick_one(mob_t *m, uint8_t pg) { int8_t mrow = pop_y_to_row((int16_t)mt_m->draw_y); /* y_to_row_mod4 */ uint8_t over; - if (mrow != pop_bg_obj_row) over = (mrow < pop_bg_obj_row); /* ряды идут 2,1,0 */ + if (mrow < 0 || mrow > 2) { + /* КОРЗИНА 30 (get_tilepos_nominus, seg006:110). Ряд объекта вне + * комнаты: y_to_row_mod4 берёт остаток по 4, поэтому «выше + * потолка» и «ниже пола» дают одно и то же −1, а оригинал сводит + * оба в тайл 30. Объекты этого тайла рисуются ПЕРВЫМИ — + * draw_objtable_items_at_tile(30) стоит в redraw_needed_tiles до + * всего обхода тайлов. Значит кусок под ВСЕМ, включая Кида; до + * сих пор сравнение рядов давало обратное («−1 обходится + * последним» = поверх). */ + over = 0; + } + else if (mrow != pop_bg_obj_row) over = (mrow < pop_bg_obj_row); /* ряды идут 2,1,0 */ else if (mt_m->col != pop_bg_obj_col) over = (mt_m->col > pop_bg_obj_col); else over = (mt_m->draw_y > pop_cd[POP_CH_KID].fpy); /* РИСОВАНИЯ ЗДЕСЬ БОЛЬШЕ НЕТ — только решение, в какой проход кусок @@ -1586,30 +1590,30 @@ static void mob_overlay_neighbour(const mob_t *m) { int8_t r = pop_y_to_row((int16_t)m->draw_y); int8_t rt = pop_y_to_row((int16_t)(m->draw_y - 18)); - int8_t c = (int8_t)(m->col + 1); + /* КОРЗИНА 30 оригинала (get_tilepos_nominus, seg006:110): ряд объекта вне + * комнаты. y_to_row_mod4 берёт остаток по 4, поэтому «выше потолка» и + * «ниже пола» дают одно и то же −1 — оригинал сводит оба случая в тайл 30 + * и рисует такие объекты ПЕРВЫМИ, до всего обхода тайлов + * (draw_objtable_items_at_tile(30) в redraw_needed_tiles). + * + * Отсюда два отличия от обычного куска: + * - ориентир «тайл объекта» = 3, то есть РАНЬШЕ любого ряда обхода + * (2,1,0). Сюда уходил сырой −1, и гейт other_overlay_tile + * `row > pop_bg_obj_row` читал его наоборот — «объект поверх всего» — + * и соседа не возвращал вовсе (тело плиты пропадало, оставался + * только её торец из переднего слоя); + * - перекрыть кусок должна и СВОЯ клетка, а не только сосед справа: + * после объекта у оригинала рисуются ВСЕ тайлы. У куска в обычном + * ряду своя клетка не перерисовывается — там объект вливается в + * midtable уже ПОСЛЕ частей своего тайла. */ + uint8_t b30 = (uint8_t)(r < 0 || r > 2); + int8_t oref = b30 ? (int8_t)3 : r; + int8_t c, cend = (int8_t)(m->col + 1); int ytop, ybot; - uint8_t *dbg = &pop_dbg_ovl[(m - mobs) * 4]; /* ВРЕМЕННО: журнал решения */ - { /* ВРЕМЕННО: перебор ВСЕХ тайлов, чьи габариты пересекаются со спрайтом - * куска — чтобы понять, скольким тайлам реально нужно лечь поверх. */ - int8_t rr, kk; - int t0 = m->draw_y - (int)mob_spr[2] + 1, t1 = m->draw_y; - for (rr = -1; rr <= 2; rr++) - for (kk = 0; kk <= 1; kk++) { - int8_t cc = (int8_t)(m->col + kk); - if (cc > 9) continue; - if (pop_tile_code(rr, cc) && - t1 >= 63 * rr + 2 && t0 <= 63 * rr + 66) - pop_dbg_tiles[m - mobs] |= (uint8_t)(1 << ((rr + 1) * 2 + kk)); - } - } - dbg[0] = (uint8_t)(r + 128); dbg[1] = (uint8_t)(rt + 128); - dbg[2] |= 1; - if (c > 9) return; - dbg[2] |= 2; - /* Габарит куска — по ЭКРАННОЙ координате, как и ряд выше: у куска, - * видимого из соседней комнаты, draw_y отличается от y на 192, и - * сравнение с габаритом тайла по y давало заведомо ложный ответ. */ - ytop = m->draw_y - (int)mob_spr[2] + 1; /* верх спрайта куска */ + if (cend > 9) cend = 9; + /* Габарит куска — по ЭКРАННОЙ координате: у куска, видимого из соседней + * комнаты, draw_y отличается от y на 192. */ + ytop = m->draw_y - (int)mob_spr[2] + 1; ybot = m->draw_y; /* ПРЕДУСЛОВИЯ ПРОВЕРЯЕМ ЗДЕСЬ, до вызова в банк 2. pop_mob_overlay_tile * живёт в банке 2, то есть каждый вызов из банка 7 платит межбанковый @@ -1620,21 +1624,15 @@ static void mob_overlay_neighbour(const mob_t *m) * ничего не рисовало. * * Габарит тайла берём ПОЛНЫМ (63*row+2 .. +66) — какая именно часть - * соседа окажется поверх куска, решит уже окно клипа внутри. Сужать по - * реальной высоте кусков нельзя: у колонн и зеркала база высокая. */ - if (r >= 0 && r <= 2 && pop_tile_code(r, c)) dbg[2] |= 4; - if (r >= 0 && r <= 2 && ybot >= 63 * r + 2 && ytop <= 63 * r + 66) dbg[2] |= 8; - if (r >= 0 && r <= 2 && pop_tile_code(r, c) && - ybot >= 63 * r + 2 && ytop <= 63 * r + 66) { - dbg[2] |= 0x10; dbg[3]++; - pop_mob_overlay_tile(r, c, r, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); - } - if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c)) dbg[2] |= 0x20; - if (rt != r && rt >= 0 && rt <= 2 && ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) dbg[2] |= 0x40; - if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c) && - ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) { - dbg[2] |= 0x80; dbg[3]++; - pop_mob_overlay_tile(rt, c, r, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + * соседа окажется поверх куска, решит уже окно клипа внутри. */ + for (c = b30 ? (int8_t)m->col : cend; c <= cend; c++) { + if (c < 0) continue; + if (r >= 0 && r <= 2 && pop_tile_code(r, c) && + ybot >= 63 * r + 2 && ytop <= 63 * r + 66) + pop_mob_overlay_tile(r, c, oref, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c) && + ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) + pop_mob_overlay_tile(rt, c, oref, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); } } @@ -1650,10 +1648,6 @@ static void mob_draw_pass(uint8_t pg, uint8_t want_defer) mob_t *m = &mobs[i]; /* Отбор по draw_y, а НЕ по комнате: кусок, только что провалившийся * в комнату снизу, ещё виден из нашей (см. mob_tick_one). */ - pop_dbg_pass[i] |= 1; - if (m->active) pop_dbg_pass[i] |= 2; - if (m->draw_y != MOB_Y_NONE) pop_dbg_pass[i] |= 4; - if ((uint8_t)(m->defer != 0) == want_defer) pop_dbg_pass[i] |= 8; if (!m->active || m->draw_y == MOB_Y_NONE) continue; if ((uint8_t)(m->defer != 0) != want_defer) continue; for (j = n; j > 0 && mobs[order[j - 1]].draw_y < m->draw_y; j--) @@ -1670,10 +1664,8 @@ static void mob_draw_pass(uint8_t pg, uint8_t want_defer) * в кадр пересечения границы пометка указывала на прежний тайл, и * передняя часть колонны кусок не перекрывала — ровно ОДИН кадр, * как и наблюдалось (прогон 2026-08-13, комната 23, колонка 8). */ - pop_dbg_pass[order[i]] |= 0x10; mob_mark_neighbour(&mobs[order[i]]); mob_render(&mobs[order[i]], pg); - pop_dbg_pass[order[i]] |= 0x20; mob_overlay_neighbour(&mobs[order[i]]); } }