ФИКС РЕГРЕССИИ: start_chompers ломал play_seq через окно W0
Симптом (уровень 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 не открыто.
This commit is contained in:
@@ -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 на ВЕСЬ разбор — один map/unmap за вызов
|
||||||
* (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце —
|
* (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце —
|
||||||
* load_frame: пока страница ещё замаплена, копируем кадр в cur_frame. */
|
* load_frame: пока страница ещё замаплена, копируем кадр в cur_frame. */
|
||||||
|
/* Отложенный start_chompers: взводится байткодом SEQ_UP/SEQ_DOWN, а
|
||||||
|
* исполняется после выхода из цикла (окно W0 уже свободно). */
|
||||||
|
static uint8_t chomp_pending;
|
||||||
|
|
||||||
void play_seq(void)
|
void play_seq(void)
|
||||||
{
|
{
|
||||||
if (!kdat_ok) return;
|
if (!kdat_ok) return;
|
||||||
@@ -425,11 +429,11 @@ void play_seq(void)
|
|||||||
}
|
}
|
||||||
case 0xFD: /* UP */
|
case 0xFD: /* UP */
|
||||||
Char.curr_row--;
|
Char.curr_row--;
|
||||||
pop_start_chompers(Char.room, Char.curr_row); /* seg006:0603 */
|
chomp_pending = 1; /* start_chompers — ПОСЛЕ цикла, см. ниже */
|
||||||
break;
|
break;
|
||||||
case 0xFC: /* DOWN */
|
case 0xFC: /* DOWN */
|
||||||
Char.curr_row++;
|
Char.curr_row++;
|
||||||
pop_start_chompers(Char.room, Char.curr_row); /* seg006:0607 */
|
chomp_pending = 1;
|
||||||
break;
|
break;
|
||||||
case 0xF9: /* ACTION */
|
case 0xF9: /* ACTION */
|
||||||
Char.action = SEQ(Char.curr_seq);
|
Char.action = SEQ(Char.curr_seq);
|
||||||
@@ -473,6 +477,16 @@ void play_seq(void)
|
|||||||
Char.frame = cmd;
|
Char.frame = cmd;
|
||||||
load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */
|
load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */
|
||||||
gfx_w0_unmap();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,9 +234,9 @@ static uint8_t next_chomper_timing(uint8_t timing)
|
|||||||
|
|
||||||
/* start_anim_chomper (seg007:08C7): запустить, только если стоит (0) или
|
/* start_anim_chomper (seg007:08C7): запустить, только если стоит (0) или
|
||||||
* уже прошёл фазу размыкания (>=6). */
|
* уже прошёл фазу размыкания (>=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);
|
uint8_t old = (uint8_t)(mod[tp] & 0x7F);
|
||||||
if (old == 0 || old >= 6) {
|
if (old == 0 || old >= 6) {
|
||||||
mod[tp] = modifier;
|
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 timing = 15, col, tp;
|
||||||
uint8_t *mod;
|
uint8_t *mod;
|
||||||
if ((uint8_t)row >= 3) return; /* вне комнаты — нечего */
|
if ((uint8_t)row >= 3) return; /* вне комнаты — нечего */
|
||||||
|
/* pop_trob_modif ДО открытия окна: при первом обращении к комнате он
|
||||||
|
* сам лезет за её bg в страницу уровня, то есть перемапит W0 и закроет
|
||||||
|
* его — а мы внутри окна читаем тайлы. */
|
||||||
mod = pop_trob_modif(room);
|
mod = pop_trob_modif(room);
|
||||||
pop_level_access_begin();
|
pop_level_access_begin();
|
||||||
for (col = 0; col < 10; col++) {
|
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) {
|
if ((pop_level_tile_raw(room, tp) & 0x1F) == TILE_CHOMP) {
|
||||||
uint8_t m = (uint8_t)(mod[tp] & 0x7F);
|
uint8_t m = (uint8_t)(mod[tp] & 0x7F);
|
||||||
if (m == 0 || m >= 6) {
|
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);
|
timing = next_chomper_timing(timing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user