roomtest: loose-полы — тряска (knock), падение с окклюзией, фикс дабл-буфера
Проваливающиеся полы в roomtest (порт SDLPoP seg007/seg008), проверено в MAME: - Падающий кусок (mob): правый край env-42 (w=26, доходит до mob_x+57) теперь полностью покрыт heal-коридором (MOB_W 56->64) — убран тёмный хвост-тень на полу 2,7 ПОСЛЕ падения. - Окклюзия правого куска во ВРЕМЯ падения: после отрисовки плиты перерисовывается сосед-пол draw_tile(row,col+1) в том же кадре — край прячется за полом 2,7 (порт redraw_at_cur_mob: set_redraw_full+1). - Тряска от сотрясения (knock): seqtbl-команды KNOCK_DOWN/UP в play_seq -> флаг knock -> do_knock(ряд) трясёт loose ряда из покоя (modif=0x80). KNOCK_DOWN в land-seq (приземление) и runcyc (footstep). - Фикс «бесконечной тряски» (дабл-буфер): при завершении тряски (0x84->0) перерисовать покойный кадр плиты на ОБЕИХ страницах (loose_rest, 2 кадра) — иначе на одной странице застревает дрожащий кадр правой грани (живёт в тайлах col и col+1) -> мерцание через флип. Инфраструктура/документация: - app.mk: цель `make hdd` (упаковка в D: для MCP-моста MAME). - docs: POP-DAT-FormatSpecifications.pdf/.txt как каноническая спецификация форматов ресурсов; ссылки в README/MSDOS_RESOURCE_FORMAT. - README.md/CLAUDE.md для applications/PoP и roomtest (правило «SDLPoP — источник истины», порядок слоёв, режим отладки freeze 1/2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -57,10 +57,13 @@ static void blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||
if (w && h)
|
||||
gfx_blit(x, ybottom - (int)h + 1, img);
|
||||
gfx_blit(x, ybottom - (int)h + 1 + POP_YOFF, img); /* +YOFF: центрирование */
|
||||
gfx_w0_unmap();
|
||||
}
|
||||
|
||||
/* heal в room-space (со сдвигом POP_YOFF) — все heal слоя фона/loose. */
|
||||
static void heal_off(int x, int y, int w, int h) { gfx_heal(x, y + POP_YOFF, w, h); }
|
||||
|
||||
static void env_b(uint8_t id, int x, int yb) { blit_b(&env[id >> 5], (uint8_t)(id & 31), x, yb); }
|
||||
static void wall_b(uint8_t id, int x, int yb) { blit_b(&wall_at, id, x, yb); }
|
||||
static void fore_b(uint8_t id, int x, int yb) { blit_b(&fore_at, id, x, yb); }
|
||||
@@ -129,8 +132,15 @@ static const uint8_t RPOS[4] = {52, 42, 31, 21};
|
||||
|
||||
/* ---- Текущая комната (globals для tile_code/mod) ------------------- */
|
||||
static const uint8_t *g_fg, *g_bg, *g_lfg, *g_lbg;
|
||||
static const uint8_t *g_below; /* row0-коды комнаты СНИЗУ (для topright под нижним рядом) */
|
||||
static uint8_t g_room;
|
||||
|
||||
/* Задать ряд 0 комнаты снизу (fg-коды) — нужен для draw_tile_topright/
|
||||
* floorright, когда тайл нижнего ряда стал видимым (empty после падения
|
||||
* loose): «стена вниз» = топрайт тайла room_B(0, col-1). 0 — нет комнаты
|
||||
* снизу (кромка уровня = стена). */
|
||||
void pop_room_set_below(const uint8_t *below_row0_fg) { g_below = below_row0_fg; }
|
||||
|
||||
static uint8_t tile_code(int row, int col)
|
||||
{
|
||||
if (col < 0) {
|
||||
@@ -287,6 +297,24 @@ static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
|
||||
env_b(DOOR_FRAM_SLICE[gate_frame], x, yb);
|
||||
}
|
||||
|
||||
/* ---- Loose floors: кадры дрожащей плиты (seg008 loose_fram_*) ------- *
|
||||
* Состояние pop_loose_modif[] живёт в pop_map. draw_tile рисует грани
|
||||
* loose АНИМИРОВАННО по modif (как SDLPoP: сам тайл — лево/низ, ПРАВЫЙ
|
||||
* СОСЕД — правую грань через draw_tile_anim_right). При modif==0 (покой/
|
||||
* статич. запекание) get_loose_frame=0 → базовые кадры 41/42/43. */
|
||||
extern uint8_t pop_loose_modif[30];
|
||||
|
||||
static const uint8_t LOOSE_FRAM_LEFT[12] = {41,69,41,70,70,41,41,41,70,70,70,0};
|
||||
static const uint8_t LOOSE_FRAM_RIGHT[12] = {42,71,42,72,72,42,42,42,72,72,72,0};
|
||||
static const uint8_t LOOSE_FRAM_BOTTOM[12] = {43,73,43,74,74,43,43,43,74,74,74,0};
|
||||
|
||||
/* get_loose_frame (seg008:761): индекс дрожащего кадра по модификатору. */
|
||||
static uint8_t get_loose_frame(uint8_t m)
|
||||
{
|
||||
if (m & 0x80) { m &= 0x7F; if (m > 10) return 1; }
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
|
||||
static void draw_tile(int row, int col)
|
||||
{
|
||||
@@ -302,9 +330,14 @@ static void draw_tile(int row, int col)
|
||||
uint8_t rbl_code;
|
||||
uint8_t wmod;
|
||||
|
||||
/* тайл снизу-слева (для topright) */
|
||||
if (row + 1 > 2) rbl_code = 1; /* ниже нижнего ряда = пол */
|
||||
else rbl_code = tile_code(row + 1, col - 1);
|
||||
/* тайл снизу-слева (для topright). Ниже нижнего ряда — берём ряд 0
|
||||
* комнаты снизу (load_rowbelow, seg008:368): row_below_left_[col] =
|
||||
* room_B(0, col-1); col-1<0 → левая комната снизу = стена уровня. */
|
||||
if (row + 1 > 2) {
|
||||
int lc = col - 1;
|
||||
if (lc < 0) rbl_code = 20; /* кромка уровня = стена */
|
||||
else rbl_code = g_below ? (uint8_t)(g_below[lc] & 0x1F) : 1;
|
||||
} else rbl_code = tile_code(row + 1, col - 1);
|
||||
|
||||
/* --- floorright (seg008.c:392) — только can_see_bottomleft --- */
|
||||
if (code == 0 || code == 9 || code == 12 || code == 26) {
|
||||
@@ -337,8 +370,8 @@ static void draw_tile(int row, int col)
|
||||
/* --- draw_tile_anim_right (seg008.c:530) — статичные части --- */
|
||||
if (lcode == 4) { /* gate: решётка */
|
||||
draw_gate_back(lmod, xh, dby, dmy);
|
||||
} else if (lcode == 11) { /* loose: правый край */
|
||||
env_b(42, x, dby - 1);
|
||||
} else if (lcode == 11) { /* loose: правая грань (анимир. по modif соседа-loose) */
|
||||
env_b(LOOSE_FRAM_RIGHT[get_loose_frame(pop_loose_modif[row * 10 + (col - 1)])], x, dby - 1);
|
||||
} else if (lcode == 2) { /* spike: статич. кадр */
|
||||
uint8_t sf = (lmod & 0x80) ? 5 : lmod;
|
||||
if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
|
||||
@@ -354,14 +387,15 @@ static void draw_tile(int row, int col)
|
||||
env_b(t->bottom_id, x, dby);
|
||||
}
|
||||
|
||||
/* --- draw_loose (seg008.c:600) — статич. кадр --- */
|
||||
/* --- draw_loose (seg008.c:600) — нижняя грань (анимир. по modif) --- */
|
||||
if (code == 11)
|
||||
env_b(43, x, dby); /* loose_fram_bottom[0] */
|
||||
env_b(LOOSE_FRAM_BOTTOM[get_loose_frame(pop_loose_modif[row * 10 + col])], x, dby);
|
||||
|
||||
/* --- draw_tile_base (seg008.c:611) --- */
|
||||
{
|
||||
uint8_t base_id = t->base_id;
|
||||
if (code == 11) base_id = 41; /* loose_fram_left[0] */
|
||||
if (code == 11) /* loose: левая грань (анимир.) */
|
||||
base_id = LOOSE_FRAM_LEFT[get_loose_frame(pop_loose_modif[row * 10 + col])];
|
||||
if (base_id)
|
||||
env_b(base_id, x, t->base_y + dmy);
|
||||
}
|
||||
@@ -376,6 +410,149 @@ static void draw_tile(int row, int col)
|
||||
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
|
||||
}
|
||||
|
||||
/* ---- Loose floors — динамическая перерисовка (seg008 draw_loose) ---- *
|
||||
* Порт SDLPoP redraw_tile_height: каждый кадр анимации перерисовать
|
||||
* ПОЛНОСТЬЮ и loose-тайл, и ПРАВЫЙ СОСЕД (грани loose анимируются по modif
|
||||
* внутри draw_tile). */
|
||||
void pop_loose_shake_draw(int row, int col)
|
||||
{
|
||||
int x = COL_XH[col] * 8;
|
||||
/* heal возвращает печёный фон обоих тайлов из ОЗУ-копии, поверх —
|
||||
* перерисовка обоих тайлов с дрожащим кадром (draw_tile берёт кадр из
|
||||
* pop_loose_modif). Порт redraw_tile_height (loose + правый сосед).
|
||||
* NB: печёный кадр-0 может слегка просвечивать по кромкам — чистое
|
||||
* решение (bake тайла как empty + рисовать loose всегда динамически)
|
||||
* отложено; wipe-в-чёрный делал плиту невидимой — не годится. */
|
||||
heal_off(x, 63 * row + 46, 64, 24);
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
draw_tile(row, col); /* loose: лево+низ (анимир.) */
|
||||
if (col + 1 < 10)
|
||||
draw_tile(row, col + 1); /* сосед: правая грань loose (анимир.) + его пол */
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Запечь ПУСТОТУ на месте упавшего loose (текущая страница). Порт
|
||||
* redraw_at_cur_mob (seg007:132C): перерисовать сам тайл (теперь empty) И
|
||||
* тайл СПРАВА (его левая грань зависит от нашего кода: пока был loose —
|
||||
* рисовал loose-правый-край env_b(42); теперь пусто → чистая грань).
|
||||
* g_fg[pos] уже = 0 (pop_map). Чёрная плита (NORMAL) стирает старые
|
||||
* пиксели пола в видео-ОЗУ + ОЗУ-копию, поверх — новый статич. фон. */
|
||||
void pop_loose_bake_empty(int row, int col)
|
||||
{
|
||||
int x = COL_XH[col] * 8;
|
||||
int xr = COL_XH[col + 1] * 8;
|
||||
int yb = 63 * row + 46;
|
||||
/* Чёрная плита (opaque NORMAL — пишет в VRAM И ОЗУ-копию акселератора):
|
||||
* стирает ЗАПЕЧЁННЫЙ пиксель-в-пиксель loose-пол с обоих тайлов, иначе
|
||||
* heal вернул бы старый пол из ОЗУ-копии. Ширина 40 — кусок пола
|
||||
* заходит на x+32..+40. */
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(x, yb + POP_YOFF, x + 39, yb + 28 + POP_YOFF); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */
|
||||
if (col + 1 < 10)
|
||||
bar(xr, yb + POP_YOFF, xr + 31, yb + 28 + POP_YOFF); /* сосед: РОВНО 32 — не залезть в col+2 (его не перерисовываем) */
|
||||
/* Новый статич. фон: сам тайл (empty → «стена вниз» topright комнаты
|
||||
* снизу) + правый сосед (его левая грань: был loose-край env_b(42),
|
||||
* теперь пусто). TRANSPARENT — 0xFF не забивает чёрный, пишет и в
|
||||
* ОЗУ-копию → heal сохранит. */
|
||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||
draw_tile(row, col);
|
||||
if (col + 1 < 10)
|
||||
draw_tile(row, col + 1);
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* ---- Падающий кусок loose (mob, seg007 move_loose) ------------------ *
|
||||
* Упрощ. одиночный кусок (в комнате 1 — один loose). Модель fixed-
|
||||
* corridor: коридор падения (колонка тайла) heal'ится каждый кадр (стирает
|
||||
* прошлый кусок, возвращает печёный фон — empty+«стена вниз»), поверх —
|
||||
* кадр 0 loose (41/42/43) на текущей y. Гравитация: speed 0→+3/кадр
|
||||
* (cap 29), y+=speed. Двоебуфер: коридор фиксирован → heal чистит любой
|
||||
* прошлый кадр ЭТОЙ страницы. Ниже поля кусок прячет clip_borders. */
|
||||
static uint8_t mob_active; /* 0 нет; 1 падает; 2 = очистка второй страницы */
|
||||
static int mob_x; /* левый край (COL_XH*8) */
|
||||
static int mob_y; /* main-y (dmy покоя), растёт вниз */
|
||||
static int8_t mob_speed;
|
||||
static int8_t mob_row, mob_col; /* тайл-позиция куска (для окклюзии соседа) */
|
||||
|
||||
void pop_loose_mob_spawn(int row, int col)
|
||||
{
|
||||
mob_x = COL_XH[col] * 8;
|
||||
mob_y = 63 * row + 62; /* dmy покоя — откуда отрывается кусок */
|
||||
mob_row = (int8_t)row;
|
||||
mob_col = (int8_t)col;
|
||||
mob_speed = 0;
|
||||
mob_active = 1;
|
||||
}
|
||||
|
||||
/* Габарит куска по X: left/bottom (41/43, w=32) с mob_x; right (42, w=26)
|
||||
* с mob_x+32 → ПРАВЫЙ пиксель = mob_x+57 (замерено в pop_env1.atl, НЕ +46:
|
||||
* ширина 42 = 26, не ~14). Коридор heal и клип ОБЯЗАНЫ покрыть ВСЮ ширину
|
||||
* [mob_x-4 .. mob_x+57], иначе правая грань 42 остаётся хвостом на одной
|
||||
* странице → тёмная тень на полу 2,7 + двоебуфер-мерцание. */
|
||||
#define MOB_X0(mx) ((mx) - 4)
|
||||
#define MOB_W 64 /* mob_x-4 .. mob_x+59 (крышка +57 с запасом 2px) */
|
||||
|
||||
void pop_loose_mob_tick(void)
|
||||
{
|
||||
if (!mob_active) return;
|
||||
/* стереть прошлый кусок этой страницы + вернуть печёный фон коридора
|
||||
* (фикс rect [160,192] по ВСЕЙ ширине куска; overflow прошлого кадра
|
||||
* стёрт его же clip'ом той же ширины). */
|
||||
heal_off(MOB_X0(mob_x), 160, MOB_W, 32);
|
||||
if (mob_active == 2) { mob_active = 0; return; } /* очистили вторую стр. — всё */
|
||||
/* гравитация (move_loose): ускорение до 29 */
|
||||
if (mob_speed < 29) mob_speed += 3;
|
||||
mob_y += mob_speed;
|
||||
if (mob_y - 24 >= POP_PLAYFIELD_H) { mob_active = 2; return; } /* весь ниже поля → +1 кадр очистки */
|
||||
/* Падающий кусок (SDLPoP draw_mobs/add_midtable, case 0x80): ТРИ куска —
|
||||
* left(41) на obj_y-3, bottom(43) на obj_y, right(42) на obj_x+4 (=col+1,
|
||||
* заходит в тайл СПРАВА 2,7) на obj_y-1. У нас bottom=dby(mob_y+3) →
|
||||
* left=mob_y, right=mob_y+2 в mob_x+32 (col+1). */
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
env_b(43, mob_x, mob_y + 3);
|
||||
env_b(41, mob_x, mob_y);
|
||||
env_b(42, mob_x + 32, mob_y + 2);
|
||||
/* Окклюзия правого куска соседним полом (SDLPoP redraw_at_cur_mob:
|
||||
* set_redraw_full(curr_tilepos+1) — сосед перерисовывается ПОВЕРХ mob).
|
||||
* Правый кусок 42 целиком лежит в тайле col+1; пока плита у уровня пола,
|
||||
* её правый край ПРЯЧЕТСЯ за передней гранью пола 2,7. Перерисовываем
|
||||
* сосед-тайл В ТОМ ЖЕ КАДРЕ поверх куска (иначе край торчит на полу
|
||||
* до heal'а следующего кадра — виден как хвост во время падения). */
|
||||
if (mob_col + 1 < 10)
|
||||
draw_tile(mob_row, mob_col + 1);
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
/* клип СВОЕГО выхода за низ поля (в статус-полосу) — по ВСЕЙ ширине куска */
|
||||
pop_clip_sprite(MOB_X0(mob_x), MOB_W, mob_y - 23 + POP_YOFF, 30);
|
||||
}
|
||||
|
||||
/* Спрайт вышел за поле? → взвести флаг на 2 кадра (обе страницы дабл-
|
||||
* буфера). Узкий per-спрайт heal НЕ используем: accel-выравнивание блока
|
||||
* оставляет 1–2px слайверы правой грани → на одной странице мерцает.
|
||||
* Вместо этого борта чистит pop_room_clip_borders (полоса, но ТОЛЬКО в
|
||||
* кадрах падения — гейт по флагу). */
|
||||
static uint8_t border_dirty;
|
||||
|
||||
void pop_clip_sprite(int x, int w, int sy_top, int h)
|
||||
{
|
||||
int sy_bot = sy_top + h;
|
||||
(void)x; (void)w;
|
||||
if (sy_bot > POP_CLIP_BOTTOM || sy_top < POP_YOFF)
|
||||
border_dirty = 2; /* 2 кадра = обе страницы */
|
||||
}
|
||||
|
||||
/* Клип бортов (см. pop_bg.h): работает ТОЛЬКО пока border_dirty (кто-то
|
||||
* вышел за поле в последние 2 кадра) — иначе ноль стоимости. Возвращает
|
||||
* чёрный из ОЗУ-копии (вне поля она чёрная) в верхний борт и статус-полосу
|
||||
* + нижний борт. Звать КАЖДЫЙ кадр ПОСЛЕ всей отрисовки спрайтов. */
|
||||
void pop_room_clip_borders(void)
|
||||
{
|
||||
if (!border_dirty) return;
|
||||
border_dirty--;
|
||||
gfx_heal(0, 0, 320, POP_YOFF); /* верхний борт */
|
||||
gfx_heal(0, POP_CLIP_BOTTOM, 320, 256 - POP_CLIP_BOTTOM);/* статус-полоса + нижний борт */
|
||||
}
|
||||
|
||||
void pop_room_draw(uint8_t room_num,
|
||||
const uint8_t *fg, const uint8_t *bg,
|
||||
const uint8_t *lcol_fg, const uint8_t *lcol_bg)
|
||||
|
||||
Reference in New Issue
Block a user