L1: фиксы прогона уровня 1 — уровень мутабелен, стражи, loose-плиты, порядок
Одиннадцать наблюдений первого прогона свелись к шести корням, четыре
наблюдения второго — ещё к четырём. Разбор каждого — bug_closed.md.
Первая волна:
- BUG-LVLSTATE-1: уровень стал мутабельным (эталонная копия foretable для
рестарта, pop_level_set_tile вместо таблицы оверрайдов);
- BUG-RESPAWN-1: рестарт = load_level, тайлы возвращаются из эталона;
- BUG-DEATH-1: смерть от меча доигрывается (порт control_kid, seg006:0CD1);
- BUG-GATE-ANIM-1: ворота в отрисованной комнате перерисовываются
(POP_RD_GATE, порт draw_trob seg007:01E6);
- BUG-COLL-1: полный порт check_collisions/bumped (seg004) вместо поиска
стены только в колонке переднего края;
- BUG-STANDUP-1: убран лишний guard в bumped_floor — вставание у стены
роняло Кида сквозь пол.
Вторая волна:
- BUG-RESPAWN-2: рестарт возвращает и СТРАЖЕЙ (в оригинале play_level на
каждой итерации делает load_level + pos_guards);
- BUG-LOOSE-2: падающий кусок привязан к своей комнате и долетает после
ухода Кида (do_mobs крутит mobs[] независимо от drawn_room);
- BUG-DRAWORDER-1: порядок «Кид / страж» задаётся обходом тайлов
(redraw_needed_tiles: ряды 2,1,0, колонки 0..9), а не ролью персонажа.
По BUG-DRAWORDER-1 понадобилось три захода, и два первых были неполны:
1) сам порядок — но общее окно fore-клипа осталось стражьим, и Кид
нарисовался поверх передних столбов (kid_fore_clip_restore);
2) enter_guard брал curr_col из тайла, а leave_guard пишет туда
get_tilepos(0,row) — у запомненного ТРУПА колонка была 0 при
настоящей X. Теперь колонка выводится из X, как в оригинале;
3) ветка actions_1_run_jump в set_objtile_at_char оказалась не
«упрощаемой»: в беге тайл берётся из нижнего ряда и ЛЕВОЙ колонки
габарита, поэтому бегущий Кид уходит за объекты справа. Считается
для обоих персонажей — enter_guard ставит action=1 и стражу.
Проверено в MAME: зелья/меч/плиты переживают выход из комнаты и
восстанавливаются после смерти; кнопка room5 поднимает решётку; падение с
кнопки больше не роняет в комнату 6; убитый страж жив после respawn;
Кид проходит за телом стража. make size-check — роста нет.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -922,6 +922,35 @@ void pop_leveldoor_redraw(int row, int col) __banked
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Ворота ВНУТРИ комнаты: перерисовать бары по живому modif (порт draw_trob,
|
||||
* seg007:01E6 — им заканчивается animate_door). (row,col) — сами ворота;
|
||||
* решётку рисует ячейка ПРАВОГО соседа (draw_tile, ветка lcode==4), поэтому
|
||||
* wipe и redraw идут по ней. Логика ровно как у левого шва
|
||||
* (pop_room_redraw_seam_left), только колонка произвольная.
|
||||
*
|
||||
* Bar-в-чёрный обязателен: бары висят в видео-ОЗУ и ОЗУ-копии, и при
|
||||
* ПОДЪЁМЕ решётки heal вернул бы запечённые нижние бары. Полоса — ровно
|
||||
* зона решётки: от gate_top_y (dby−62 = 63*row+3) до низа (63*row+64);
|
||||
* выше начинается ряд −1, ниже — стена ряда ниже.
|
||||
*
|
||||
* Ворота в col9: их бары рисуются уже в СОСЕДНЕЙ комнате, в отрисованной их
|
||||
* не видно — оригинал такой trob тоже пропускает
|
||||
* (get_trob_right_pos_in_drawn_room → 30). Для взгляда из комнаты справа
|
||||
* работает pop_room_redraw_seam_left. */
|
||||
void pop_gate_redraw(int row, int col) __banked
|
||||
{
|
||||
int x, yt;
|
||||
if (col + 1 > 9 || row < 0 || row > 2) return;
|
||||
x = COL_XH[col + 1] * 8;
|
||||
yt = POP_YOFF + 63 * row;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(x, yt + 3, x + 31, yt + 64);
|
||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||
draw_tile(row, col + 1);
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Запечь ПУСТОТУ на месте упавшего loose (текущая страница). Порт
|
||||
* redraw_at_cur_mob (seg007:132C): перерисовать сам тайл (теперь empty) И
|
||||
* тайл СПРАВА (его левая грань зависит от нашего кода: пока был loose —
|
||||
@@ -986,6 +1015,7 @@ typedef struct {
|
||||
int prev_y[2]; /* последняя нарисованная y на КАЖДОЙ странице (heal) */
|
||||
int8_t speed;
|
||||
int8_t row, col; /* тайл-позиция куска (для окклюзии соседа) */
|
||||
uint8_t room; /* curmob.room: кусок падает В СВОЕЙ комнате, не в текущей */
|
||||
} mob_t;
|
||||
static mob_t mobs[MOB_MAX];
|
||||
|
||||
@@ -1014,6 +1044,7 @@ void pop_loose_mob_spawn(int row, int col) __banked
|
||||
m->y = MOB_Y_LAND[row + 1]; /* y_loose_land: низ куска в момент отрыва */
|
||||
m->row = (int8_t)row;
|
||||
m->col = (int8_t)col;
|
||||
m->room = g_room; /* curmob.room (add_mob, seg007:1010) */
|
||||
m->speed = 0;
|
||||
m->active = 1;
|
||||
m->clean = 0;
|
||||
@@ -1047,14 +1078,42 @@ void pop_loose_mob_reset(void) __banked
|
||||
}
|
||||
}
|
||||
|
||||
/* Смена комнаты. Куски НЕ останавливаем: в оригинале mobs[] чистится только
|
||||
* в start_level (seg003:88), а do_mobs (seg007:1063) крутит их все независимо
|
||||
* от drawn_room — move_loose работает по curmob.room, и плита, за которой Кид
|
||||
* не досмотрел, всё равно долетает и кладёт щебень (BUG-LOOSE-2: в комнате 12
|
||||
* плита 0,1 не успевала упасть до ухода в комнату 15). Сбросить надо только
|
||||
* heal-историю: новая комната перерисована целиком, старых пикселей нет. */
|
||||
void pop_loose_mob_room_changed(void) __banked
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < MOB_MAX; i++) {
|
||||
mobs[i].defer = 0;
|
||||
mobs[i].prev_y[0] = mobs[i].prev_y[1] = MOB_Y_NONE;
|
||||
/* дочистка хвоста относилась к покинутой комнате — она уже не нужна */
|
||||
if (!mobs[i].active) mobs[i].clean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Тайл под куском. В своей комнате — обычный tile_code (он знает про кромки
|
||||
* и соседей), в чужой — прямо из данных уровня. */
|
||||
static uint8_t mob_tile_at(mob_t *m)
|
||||
{
|
||||
if (m->room == g_room) return (uint8_t)(tile_code(m->row, m->col) & 0x1F);
|
||||
if (m->row < 0 || m->row > 2 || m->col < 0 || m->col > 9) return 1; /* кромка = пол */
|
||||
return (uint8_t)(pop_level_tile(m->room, (uint8_t)(m->row * 10 + m->col)) & 0x1F);
|
||||
}
|
||||
|
||||
static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
{
|
||||
uint8_t here;
|
||||
if (!m->active && !m->clean) return;
|
||||
here = (uint8_t)(m->room == g_room);
|
||||
/* Стереть кусок, нарисованный на ЭТОЙ странице в прошлый раз (у каждой
|
||||
* страницы своя копия фона). Раньше коридор был фиксирован под ряд 2;
|
||||
* плита-потолок падает через весь ряд 0, поэтому чистим по фактической
|
||||
* прошлой позиции. */
|
||||
if (m->prev_y[pg] != MOB_Y_NONE) {
|
||||
if (here && m->prev_y[pg] != MOB_Y_NONE) {
|
||||
heal_off(MOB_X0(m->x), m->prev_y[pg] - 27, MOB_W, 32);
|
||||
m->prev_y[pg] = MOB_Y_NONE;
|
||||
}
|
||||
@@ -1067,9 +1126,17 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
* loose_fall «плита ломает плиту» не нужен: в ряду ниже её нет);
|
||||
* пол — приземление (loose_land: debris + do_knock). */
|
||||
if (m->row < 3 && m->y < 226 && MOB_Y_BOUND[m->row + 1] <= m->y) {
|
||||
uint8_t t = (uint8_t)(tile_code(m->row, m->col) & 0x1F);
|
||||
uint8_t t = mob_tile_at(m);
|
||||
if (t == 0 || t == 11) {
|
||||
m->row++; /* mob_down_a_row */
|
||||
} else if (!here) {
|
||||
/* Приземление в ПОКИНУТОЙ комнате: рисовать нечего, сигнальная
|
||||
* пара pop_loose_landed/pop_debris_at обслуживает только текущую
|
||||
* комнату — щебень кладём прямо в данные уровня (loose_land пишет
|
||||
* в curr_room_tiles комнаты curmob.room, а не отрисованной). */
|
||||
pop_level_set_tile(m->room, (uint8_t)(m->row * 10 + m->col), 0x0E);
|
||||
m->active = 0; m->clean = 0;
|
||||
return;
|
||||
} else if (pop_loose_landed) {
|
||||
/* сигнал прошлого приземления ещё не разобран (один за кадр) —
|
||||
* подержим кусок на границе и приземлим следующим кадром */
|
||||
@@ -1082,9 +1149,10 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
|
||||
}
|
||||
}
|
||||
if (m->y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */
|
||||
m->active = 0; m->clean = 2;
|
||||
m->active = 0; m->clean = (uint8_t)(here ? 2 : 0);
|
||||
return;
|
||||
}
|
||||
if (!here) return; /* чужая комната — считаем, но не рисуем */
|
||||
/* Порядок «плита vs Kid». В оригинале объекты рисуются при обходе СВОИХ
|
||||
* тайлов (redraw_needed_tiles: ряды 2,1,0; колонки 0..9), и только внутри
|
||||
* ОДНОГО тайла сортируются по obj_y (sort_curr_objs, seg008:1561: кто
|
||||
@@ -1165,8 +1233,13 @@ void pop_loose_mob_draw_over(void) __banked
|
||||
uint8_t pop_loose_mob_pos(int *col, int *y) __banked
|
||||
{
|
||||
uint8_t i;
|
||||
/* check_loose_fall_on_kid (seg007:1192) начинается с Char.room ==
|
||||
* curmob.room: кусок, падающий в покинутой комнате, Киду по голове не
|
||||
* бьёт. */
|
||||
for (i = 0; i < MOB_MAX; i++)
|
||||
if (mobs[i].active) { *col = mobs[i].col; *y = mobs[i].y; return 1; }
|
||||
if (mobs[i].active && mobs[i].room == g_room) {
|
||||
*col = mobs[i].col; *y = mobs[i].y; return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user