From 1461ed5633c93d52ccba3eb79e32b3566948117c Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Sat, 8 Aug 2026 20:00:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=98=D0=9A=D0=A1=20=D0=A0=D0=95=D0=93?= =?UTF-8?q?=D0=A0=D0=95=D0=A1=D0=A1=D0=98=D0=98:=20start=5Fchompers=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=BC=D0=B0=D0=BB=20play=5Fseq=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=20=D0=BE=D0=BA=D0=BD=D0=BE=20W0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Симптом (уровень 3, комната 22): после спуска с уступа Кид проваливался СКВОЗЬ пол; при подъёме проскакивало лишнее движение вперёд с прыжком вверх. Корень. play_seq держит W0 замапленным на страницу данных Кида весь цикл и читает seqtbl прямо через окно (макрос SEQ). Вызов pop_start_chompers, поставленный в dc0bd47 внутрь обработки SEQ_UP/SEQ_DOWN, лезет за тайлами уровня: pop_level_access_begin/end — это ровно gfx_w0_map/unmap. После возврата цикл продолжал читать байткод из закрытого окна, то есть исполнял мусор. Фикс: SEQ_UP/SEQ_DOWN только взводят флаг, а pop_start_chompers зовётся после выхода из цикла, когда W0 уже размаплен. Эффект тот же — трасса кадра не меняется, чомперы заводятся в том же кадре. Заодно убрана вложенность того же рода внутри самого pop_start_chompers: start_anim_chomper получает mod параметром, а не зовёт pop_trob_modif — тот при первом обращении к комнате сам перемапливает W0 под её bg. Остальные точки вызова (start_fall, land, enter_room) проверены: там окно W0 не открыто. --- applications/PoP/roomtest/pop_kid.c | 18 ++++++++++++++++-- applications/PoP/roomtest/pop_trob.c | 10 +++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/applications/PoP/roomtest/pop_kid.c b/applications/PoP/roomtest/pop_kid.c index eec913a..53ccf33 100644 --- a/applications/PoP/roomtest/pop_kid.c +++ b/applications/PoP/roomtest/pop_kid.c @@ -395,6 +395,10 @@ void pop_char_set_seq(uint8_t seq_id) { Char.curr_seq = kid_seq_off[seq_id]; } * Страница данных маппится в W0 на ВЕСЬ разбор — один map/unmap за вызов * (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце — * load_frame: пока страница ещё замаплена, копируем кадр в cur_frame. */ +/* Отложенный start_chompers: взводится байткодом SEQ_UP/SEQ_DOWN, а + * исполняется после выхода из цикла (окно W0 уже свободно). */ +static uint8_t chomp_pending; + void play_seq(void) { if (!kdat_ok) return; @@ -425,11 +429,11 @@ void play_seq(void) } case 0xFD: /* UP */ Char.curr_row--; - pop_start_chompers(Char.room, Char.curr_row); /* seg006:0603 */ + chomp_pending = 1; /* start_chompers — ПОСЛЕ цикла, см. ниже */ break; case 0xFC: /* DOWN */ Char.curr_row++; - pop_start_chompers(Char.room, Char.curr_row); /* seg006:0607 */ + chomp_pending = 1; break; case 0xF9: /* ACTION */ Char.action = SEQ(Char.curr_seq); @@ -473,6 +477,16 @@ void play_seq(void) Char.frame = cmd; load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */ gfx_w0_unmap(); + /* start_chompers (seg006:0603/0607) — ТОЛЬКО здесь, после + * размаппинга. В оригинале он зовётся прямо из SEQ_UP/SEQ_DOWN, + * но у нас seqtbl читается ЧЕРЕЗ ОКНО W0, а start_chompers лезет + * за тайлами уровня и это окно перемапливает и закрывает — цикл + * дальше исполнял бы мусор вместо байткода. Наступили на это + * 2026-08-08: после спуска Кид проваливался сквозь пол. */ + if (chomp_pending) { + chomp_pending = 0; + pop_start_chompers(Char.room, Char.curr_row); + } return; } } diff --git a/applications/PoP/roomtest/pop_trob.c b/applications/PoP/roomtest/pop_trob.c index b91479b..fcd0254 100644 --- a/applications/PoP/roomtest/pop_trob.c +++ b/applications/PoP/roomtest/pop_trob.c @@ -234,9 +234,9 @@ static uint8_t next_chomper_timing(uint8_t timing) /* start_anim_chomper (seg007:08C7): запустить, только если стоит (0) или * уже прошёл фазу размыкания (>=6). */ -static void start_anim_chomper(uint8_t room, uint8_t tp, uint8_t modifier) +static void start_anim_chomper(uint8_t *mod, uint8_t room, uint8_t tp, + uint8_t modifier) { - uint8_t *mod = pop_trob_modif(room); uint8_t old = (uint8_t)(mod[tp] & 0x7F); if (old == 0 || old >= 6) { mod[tp] = modifier; @@ -252,6 +252,9 @@ void pop_start_chompers(uint8_t room, int8_t row) __banked uint8_t timing = 15, col, tp; uint8_t *mod; if ((uint8_t)row >= 3) return; /* вне комнаты — нечего */ + /* pop_trob_modif ДО открытия окна: при первом обращении к комнате он + * сам лезет за её bg в страницу уровня, то есть перемапит W0 и закроет + * его — а мы внутри окна читаем тайлы. */ mod = pop_trob_modif(room); pop_level_access_begin(); for (col = 0; col < 10; col++) { @@ -259,7 +262,8 @@ void pop_start_chompers(uint8_t room, int8_t row) __banked if ((pop_level_tile_raw(room, tp) & 0x1F) == TILE_CHOMP) { uint8_t m = (uint8_t)(mod[tp] & 0x7F); if (m == 0 || m >= 6) { - start_anim_chomper(room, tp, (uint8_t)(timing | (mod[tp] & 0x80))); + start_anim_chomper(mod, room, tp, + (uint8_t)(timing | (mod[tp] & 0x80))); timing = next_chomper_timing(timing); } }