L9-INVERT: зеркальный слой фона + вход в комнату в банк 8
II.3: pop_upside проведён в единственную точку тайлового слоя (pop_blit_b / blit_b_clip): позиция через FLIP_TOP, блит — vflip-двойник. Этим зеркалятся полная отрисовка комнаты, точечные редрои, trob-анимации, fore-слой и кладка. В клипованном пути обрезка сверху экрана съедает нижние строки источника, поэтому кусок пересчитывается как sy' = h - sy - dh. MEM-COLD2 п.1: enter_room_side/enter_room — в банк 8 (куча 450 -> 1231 Б). Рабочие массивы комнаты остались в _DATA, в банк уехал только код. Проверено в MAME (уровень 9, чит U): комната перевёрнута целиком, пол вверху, дверь и кладка вверх ногами. tests-host 6/6, size-check OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,14 +124,14 @@ static void kbd_idle(void)
|
||||
* соседей для кромок. Заполняются pop_room_load из уровня. File-scope,
|
||||
* чтобы enter_room переключал комнату без длинного списка параметров. */
|
||||
uint8_t room_fg[30];
|
||||
static uint8_t room_bg[30];
|
||||
uint8_t room_bg[30];
|
||||
/* lcol_fg/rcol_fg — ШЕСТЬ записей: [0..2] кромочная колонка соседа (col9/col0),
|
||||
* [3..5] следующая за ней (col8/col1) для коллизии, когда Kid стоит В шве и
|
||||
* смотрит наружу (колонка −2 / 11). См. pop_level.h. */
|
||||
static uint8_t lcol_fg[6], lcol_bg[3], rcol_fg[6], rcol_bg[3], below_fg[11];
|
||||
static uint8_t above_fg[10], above_mod[10]; /* ряд 2 верхнего соседа (полоса кладки у потолка) */
|
||||
uint8_t lcol_fg[6], lcol_bg[3], rcol_fg[6], rcol_bg[3], below_fg[11];
|
||||
uint8_t above_fg[10], above_mod[10]; /* ряд 2 верхнего соседа (полоса кладки у потолка) */
|
||||
uint8_t cur_room; /* номер ОТРИСОВАННОЙ комнаты (drawn_room) */
|
||||
static uint8_t kid_room; /* РЕАЛЬНАЯ комната Kid (S1: == cur_room; расходятся в S3 straddle) */
|
||||
uint8_t kid_room; /* РЕАЛЬНАЯ комната Kid (S1: == cur_room; расходятся в S3 straddle) */
|
||||
|
||||
/* Рендер-смещение Kid по логической X (порт xpos_in_drawn_room): комната Kid
|
||||
* слева от отрисованной → −140 (спрайт за левой кромкой), справа → +140, та
|
||||
@@ -167,7 +167,7 @@ static void update_kid_render_dx(void)
|
||||
* Видимой её делает draw_tile: нажатый opener рисуется как ПОЛ (seg008
|
||||
* get_tile_to_draw), а у пола есть правая грань, у кнопки — нет.
|
||||
* Поэтому в сигнатуру ряда подмешиваем бит «кнопка нажата». */
|
||||
static uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r)
|
||||
uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r)
|
||||
{
|
||||
uint8_t code = (uint8_t)(lfg[r] & 0x1F);
|
||||
uint8_t mod = m[9 + r * 10];
|
||||
@@ -176,9 +176,9 @@ static uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r)
|
||||
return mod;
|
||||
}
|
||||
|
||||
static uint8_t seam_sig[3]; /* последняя нарисованная openness гейта в шве */
|
||||
static uint8_t seam_rows; /* маска рядов, ждущих редроя (переживает 2 кадра) */
|
||||
static uint8_t seam_redraw; /* оставшиеся кадры редроя (обе страницы) */
|
||||
uint8_t seam_sig[3]; /* последняя нарисованная openness гейта в шве */
|
||||
uint8_t seam_rows; /* маска рядов, ждущих редроя (переживает 2 кадра) */
|
||||
uint8_t seam_redraw; /* оставшиеся кадры редроя (обе страницы) */
|
||||
|
||||
/* checkpoint (seg000:270): взводится, когда Кид уходит ВЛЕВО в комнату 7
|
||||
* уровня 3 (level3_set_chkp, seg002:0665); по нему do_startpos подменяет
|
||||
@@ -214,149 +214,6 @@ static void flash_bg(uint8_t r, uint8_t g, uint8_t b)
|
||||
gfx_pal_set(1, 0, r, g, b);
|
||||
}
|
||||
|
||||
/* Загрузить комнату room в рабочие массивы и отрисовать её фон в ОБЕ
|
||||
* страницы дабл-буфера (у каждой своя видео-ОЗУ/теневая копия). Оставляет
|
||||
* активной страницу 1 — вызывающий восстанавливает нужную draw-страницу. */
|
||||
/* side — куда ушёл КИД (0 left, 1 right, 2 up, 3 down); 0xFF = «не переход
|
||||
* за Кидом» (старт уровня, респавн, чит-навигация). От него зависит ровно
|
||||
* одно: пойдёт ли страж следом (pop_guard_follow, порт exit_room+
|
||||
* follow_guard) или останется в прежней комнате. */
|
||||
static void enter_room_side(uint8_t room, uint8_t side)
|
||||
{
|
||||
uint8_t pg;
|
||||
/* Страж идёт за Кидом? — решаем ДО того, как что-либо загрузим: функции
|
||||
* нужны и старая комната (Guard.room), и целевая. Пошёл — ни leave, ни
|
||||
* enter звать нельзя: он уже перенесён и живёт дальше как есть. */
|
||||
uint8_t follow = (uint8_t)(side <= 3 ? pop_guard_follow(room, side) : 0);
|
||||
if (!follow) pop_guard_leave(); /* запомнить стража уходящей комнаты */
|
||||
uint8_t *room_mod;
|
||||
pop_room_load(room, room_fg, room_bg, lcol_fg, lcol_bg, rcol_fg, rcol_bg, below_fg);
|
||||
pop_map_set(room_fg);
|
||||
/* Соседей слева/справа карта забирает из уровня САМА (целиком, см.
|
||||
* pop_map_set_edges) — отдельной копии в приложении нет. */
|
||||
pop_map_set_edges(pop_room_link(room, 0), pop_room_link(room, 1),
|
||||
pop_room_link(room, 2), pop_room_link(room, 3));
|
||||
/* Боковой переход: колонки сдвинулись ровно на 10 (x ∓140), флаги
|
||||
* перекрытия при этом инвариантны — историю перенумеровываем, а не
|
||||
* теряем. Без этого решётка шва не бампит на кадре входа и Кид с
|
||||
* разбега проходит сквозь закрытые ворота (BUG-GATE-PASS-1). */
|
||||
if (side == 0) pop_coll_shift(+10); /* ушёл ВЛЕВО: col += 10 */
|
||||
else if (side == 1) pop_coll_shift(-10); /* ушёл ВПРАВО: col −= 10 */
|
||||
pop_room_set_below(below_fg);
|
||||
/* Ряд 2 верхнего соседа → полоса кладки у потолка (row0). fg — статика
|
||||
* уровня, mod — живой room_modif (ленивая инициализация). */
|
||||
{
|
||||
uint8_t up = pop_room_link(room, 2), c;
|
||||
if (up) {
|
||||
uint8_t *um = pop_trob_modif(up);
|
||||
pop_level_access_begin();
|
||||
for (c = 0; c < 10; c++) above_fg[c] = pop_level_tile_raw(up, (uint8_t)(20 + c));
|
||||
pop_level_access_end();
|
||||
for (c = 0; c < 10; c++) above_mod[c] = um[20 + c];
|
||||
pop_room_set_above(above_fg, above_mod);
|
||||
pop_map_set_above(above_fg); /* плита-потолок: пробой снизу + провал */
|
||||
} else {
|
||||
pop_room_set_above(0, 0); /* нет комнаты сверху → пол (кромка) */
|
||||
pop_map_set_above(0);
|
||||
}
|
||||
}
|
||||
pop_map_set_room(room);
|
||||
/* Kid.room — как в оригинале: «комната персонажа». Полная модель
|
||||
* (Kid.room ≠ отрисованной у шва) ещё не портирована, но сравнение
|
||||
* Kid.room == Guard.room нужно уже сейчас: без него луч видимости
|
||||
* стража никогда не срабатывает. */
|
||||
Kid.room = room;
|
||||
pop_loose_reset(); /* loose-состояние не тянем из старой комнаты */
|
||||
pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */
|
||||
pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */
|
||||
pop_start_chompers(room, Kid.curr_row); /* seg000:1288: чомперы РЯДА Кида */
|
||||
/* check_shadow (seg002:0064) стоит ПЕРЕД enter_guard и на «шадоу»-
|
||||
* уровнях забирает слот соперника себе: вернула 1 — стража поднимать
|
||||
* НЕЛЬЗЯ (в комнате 24 уровня 5 он есть в данных, но оригинал его туда
|
||||
* не пускает). Порядок ровно как в оригинале, не «сначала страж, потом
|
||||
* заменим». */
|
||||
if (!follow && !pop_check_shadow(room))
|
||||
pop_guard_enter(room); /* страж комнаты (порт enter_guard) */
|
||||
pop_hp_invalidate(); /* фон перерисован — полоса HP тоже */
|
||||
/* Модификаторы тайлов (openness ворот, состояние пик) — ПЕРСИСТЕНТНЫЕ
|
||||
* per-room (room_modif живёт между входами); рисуем и коллидируем по ним,
|
||||
* а не по свежему backtable. Фаза P0. */
|
||||
room_mod = pop_trob_modif(room);
|
||||
/* Модификаторы швов — из ЖИВОГО room_modif соседей (openness ворот,
|
||||
* видимых через шов, персистентен: ворота room8(0,9) видны в col0 room6).
|
||||
* pop_room_load заполнил lcol_bg/rcol_bg статикой уровня — перекрываем. */
|
||||
{
|
||||
uint8_t lft = pop_room_link(room, 0), rgt = pop_room_link(room, 1);
|
||||
if (lft) { uint8_t *m = pop_trob_modif(lft), i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
lcol_bg[i] = m[9 + i * 10];
|
||||
/* шов рисуется в обе страницы ниже — синхр. сигнатуру */
|
||||
seam_sig[i] = seam_row_sig(m, lcol_fg, i);
|
||||
} }
|
||||
if (rgt) { uint8_t *m = pop_trob_modif(rgt);
|
||||
rcol_bg[0] = m[0]; rcol_bg[1] = m[10]; rcol_bg[2] = m[20]; }
|
||||
}
|
||||
seam_redraw = 0; seam_rows = 0; /* шов свежий на обеих страницах — редрой не нужен */
|
||||
/* ENTER-ROOM-FAST: комната рисуется ОДИН раз — в скрытую страницу, — и
|
||||
* тут же показывается флипом; вторая страница дабл-буфера получает её
|
||||
* КОПИЕЙ акселератора (gfx_copy_page, ~384 burst-строки против почти
|
||||
* полумиллиона тактов на вторую отрисовку). Побочный эффект важнее
|
||||
* тактов: игрок больше не видит, КАК рисуются тайлы — только готовый
|
||||
* кадр.
|
||||
*
|
||||
* Копия идёт банком 0x50, то есть обновляет и VRAM, и ОЗУ-копию
|
||||
* страницы-приёмника — иначе heal на ней восстанавливал бы фон прошлой
|
||||
* комнаты (инвариант gfx_copy_page, регресс tests/pageflip).
|
||||
*
|
||||
* Видимая страница здесь МЕНЯЕТСЯ, поэтому главный цикл перечитывает её
|
||||
* в начале каждого кадра (front = gfx_get_visible_page()), а не ведёт
|
||||
* свой счёт. */
|
||||
{
|
||||
gfx_rect_t full;
|
||||
pg = (uint8_t)(gfx_get_visible_page() ^ 1); /* скрытая */
|
||||
gfx_set_draw_page(pg);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(0, 0, 319, 255);
|
||||
pop_room_draw(room, room_fg, room_mod, lcol_fg, lcol_bg);
|
||||
gfx_set_visible_page(pg); /* показать готовое */
|
||||
gfx_set_draw_page((uint8_t)(pg ^ 1));
|
||||
full.x = 0; full.y = 0; full.w = 320; full.h = 256;
|
||||
gfx_copy_page(&full, GFX_COPY_DIRECT); /* вторую — копией */
|
||||
}
|
||||
cur_room = room;
|
||||
kid_room = room; /* S1: комната Kid синхронна отрисованной (S3 разведёт) */
|
||||
pop_leave_timer = 2; /* exit_room_timer: 2 кадра блокировки leave (анти-ping-pong/#4) */
|
||||
}
|
||||
|
||||
/* Вход в комнату БЕЗ перехода за Кидом: старт уровня, респавн, чит-обход.
|
||||
* Страж такой смены не «видит» — он остаётся в своей комнате. */
|
||||
void enter_room(uint8_t room) { enter_room_side(room, 0xFF); }
|
||||
|
||||
/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos
|
||||
* (seg003:028A) — поставить Кида в стартовую позицию УРОВНЯ. Один вход и
|
||||
* для запуска, и для рестарта после смерти / выпадения из уровня: в
|
||||
* оригинале это буквально один и тот же путь (play_level_2 зовёт
|
||||
* start_level заново), поэтому и у нас не три копии четырёх строк.
|
||||
*
|
||||
* Позиция берётся ИЗ ДАННЫХ уровня (pop_level_start_*), а не из #define:
|
||||
* start_pos — тайл 0..29 (row*10+col), start_dir — направление, в котором
|
||||
* персонаж должен ОКАЗАТЬСЯ.
|
||||
*
|
||||
* Направление ИНВЕРТИРУЕТСЯ (`Char.direction = ~level.start_dir`): персонаж
|
||||
* ставится спиной и доворачивается кадрами позы входа. Сама поза — из
|
||||
* tbl_entry_pose (data.h:848 = {0,1,0,…,2,…}):
|
||||
* 1 (уровень 1) — падение внутрь (seq_7_fall) + нажатие кнопки room5(0,2);
|
||||
* это оно захлопывает решётку за спиной в первой комнате;
|
||||
* 2 (уровень 13) — вбегание (seq_84_run);
|
||||
* иначе — разворот на месте (seq_5_turn).
|
||||
* Таблица индексируется НОМЕРОМ УРОВНЯ — pop_level_entry_pose(). */
|
||||
#define SEQ_TURN 5
|
||||
#define SEQ_FALL 7
|
||||
#define SEQ_RUN 84
|
||||
#define ENTRY_POSE_FALL 1 /* tbl_entry_pose[1] */
|
||||
#define ENTRY_POSE_RUN 2 /* tbl_entry_pose[13] */
|
||||
#define TILE_LEVELDOOR_LEFT 16 /* tiles_16_level_door_left */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Дабл-буфер: front — видимая страница, back — куда рисуем. dbuf=1
|
||||
|
||||
Reference in New Issue
Block a user