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:
@@ -19,6 +19,7 @@
|
||||
#include "pop_kid.h"
|
||||
#include "pop_map.h"
|
||||
#include "pop_ctrl.h" /* pop_ctrl_shift_held() — для check_grab */
|
||||
#include "pop_bg.h" /* pop_loose_shake_draw/bake_empty — перерисовка */
|
||||
|
||||
/* ---- Тайлы / действия / seq id (подмножество PoP) ------------------ */
|
||||
#define TILE_EMPTY 0
|
||||
@@ -28,6 +29,8 @@
|
||||
#define TILE_MIRROR 13
|
||||
#define TILE_CHOMPER 18
|
||||
#define TILE_WALL 20
|
||||
#define TILE_LOOSE 11 /* проваливающийся пол */
|
||||
#define TILE_DEBRIS 14 /* щебень (упавший loose) */
|
||||
#define ACT_STAND 0
|
||||
#define ACT_RUNJUMP 1
|
||||
#define ACT_HANGCLIMB 2
|
||||
@@ -82,12 +85,12 @@ static int8_t g_infrontx; /* колонка «перед персонаже
|
||||
|
||||
/* ---- Карта комнаты ------------------------------------------------- */
|
||||
uint8_t pop_fell_out; /* 1 = Kid упал за низ комнаты (сброс приложением) */
|
||||
static const uint8_t *g_fg; /* fg[30], row*10+col */
|
||||
static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */
|
||||
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
|
||||
static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */
|
||||
static uint8_t grab_timer; /* после зацепа (12): блокирует climb-up */
|
||||
|
||||
void pop_map_set(const uint8_t *fg) { g_fg = fg; }
|
||||
void pop_map_set(uint8_t *fg) { g_fg = fg; }
|
||||
|
||||
static uint8_t get_tile(int8_t col, int8_t row)
|
||||
{
|
||||
@@ -274,18 +277,26 @@ static void start_fall(void)
|
||||
kid_set_seq(seq);
|
||||
play_seq();
|
||||
determine_col();
|
||||
if (get_tile_at_char() == TILE_WALL) {
|
||||
in_wall();
|
||||
return;
|
||||
}
|
||||
/* Хвост seg006:1099: тайл ПЕРЕД персонажем — стена → отодвинуть на 1
|
||||
* назад (иначе передний край зайдёт в грань). (seq_104 для frame 44 с
|
||||
* малым зазором не портирован — в этом тесте не встречается.) */
|
||||
tile = get_tile_infrontof_char();
|
||||
if (tile == TILE_WALL) {
|
||||
if (frame != 44 || distance_to_edge_weight() >= 6)
|
||||
Kid.x = (uint8_t)char_dx_forward(-1);
|
||||
determine_col();
|
||||
/* Проверки «упал в стену» — ТОЛЬКО пока персонаж В комнате (curr_row<=2).
|
||||
* Ниже нижнего ряда (curr_row=3 после inc — падение сквозь дыру loose)
|
||||
* наш get_tile отдаёт WALL как СЕНТИНЕЛ кромки уровня, но в оригинале там
|
||||
* КОМНАТА СНИЗУ (у дыры — пол/пусто, не стена). Без гарда in_wall() ложно
|
||||
* срабатывал и откатывал X назад (6−d) → дайв падения смещался на тайл
|
||||
* влево (Кид «клевал» рано над колонной вместо дыры). */
|
||||
if (Kid.curr_row <= 2) {
|
||||
if (get_tile_at_char() == TILE_WALL) {
|
||||
in_wall();
|
||||
return;
|
||||
}
|
||||
/* Хвост seg006:1099: тайл ПЕРЕД персонажем — стена → отодвинуть на 1
|
||||
* назад (иначе передний край зайдёт в грань). (seq_104 для frame 44 с
|
||||
* малым зазором не портирован — в этом тесте не встречается.) */
|
||||
tile = get_tile_infrontof_char();
|
||||
if (tile == TILE_WALL) {
|
||||
if (frame != 44 || distance_to_edge_weight() >= 6)
|
||||
Kid.x = (uint8_t)char_dx_forward(-1);
|
||||
determine_col();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,6 +627,116 @@ static void check_bumped(void)
|
||||
determine_col();
|
||||
}
|
||||
|
||||
/* ---- Loose floors (seg007) ---------------------------------------- *
|
||||
* Состояние на тайл: 0 = покой; 0x80..0x83 = тряска (do_knock); 1..11 =
|
||||
* отсчёт до падения (на тайле стоят/пробили). Порт animate_loose/
|
||||
* make_loose_fall/check_press. Перерисовка — в pop_bg (shake/bake). */
|
||||
uint8_t pop_loose_modif[30]; /* публично: читает pop_bg при отрисовке */
|
||||
static uint8_t loose_bake[30]; /* счётчик «запечь пусто» (2 = обе страницы) */
|
||||
static uint8_t loose_rest[30]; /* счётчик «перерисовать ПОКОЙ» после тряски (2 = обе стр.) */
|
||||
|
||||
/* make_loose_fall (seg007:0EF6): взвести отсчёт падения, если ещё не идёт. */
|
||||
static void make_loose_fall(int pos, uint8_t modifier)
|
||||
{
|
||||
if (pos < 0 || pos >= 30) return;
|
||||
if ((g_fg[pos] & 0x1F) != TILE_LOOSE) return;
|
||||
if ((g_fg[pos] & 0x20)) return; /* «solid» loose — не от шага */
|
||||
if ((int8_t)pop_loose_modif[pos] <= 0) /* покой/тряска, не отсчёт */
|
||||
pop_loose_modif[pos] = modifier;
|
||||
}
|
||||
|
||||
/* check_press (seg006:0EC8, упрощ.): Kid стоит на loose → make_loose_fall;
|
||||
* frame 79 (прыжок вверх) с loose СВЕРХУ → пробить сверху. */
|
||||
static void check_press(void)
|
||||
{
|
||||
uint8_t frame = Kid.frame, action = Kid.action;
|
||||
if (frame == 79) { /* прыжок вверх — тайл сверху */
|
||||
if (get_tile_above_char() == TILE_LOOSE)
|
||||
make_loose_fall((Kid.curr_row - 1) * 10 + Kid.curr_col, 1);
|
||||
return;
|
||||
}
|
||||
/* стоя́ние: action turn/bumped или < hang_climb (0/1), с FRAME_NEEDS_FLOOR */
|
||||
if (action != ACT_BUMPED && action >= ACT_HANGCLIMB) return;
|
||||
if (!(kid_cur_flags() & FRAME_NEEDS_FLOOR)) return;
|
||||
if (get_tile_at_char() == TILE_LOOSE)
|
||||
make_loose_fall(Kid.curr_row * 10 + Kid.curr_col, 1);
|
||||
}
|
||||
|
||||
/* do_knock (seg007:0FE0) + loose_make_shake (seg007:0FB4): сотрясение от
|
||||
* приземления/удара трясёт ВСЕ loose-полы в ряду tile_row (не роняя их —
|
||||
* modif 0x80..0x83 = чистая тряска, сбрасывается в pop_loose_tick). Трясём
|
||||
* только из ПОКОЯ (modif==0): если тайл уже дрожит/падает — не перебиваем. */
|
||||
static void do_knock(int tile_row)
|
||||
{
|
||||
int col, pos;
|
||||
if (tile_row < 0 || tile_row > 2) return;
|
||||
for (col = 0; col < 10; col++) {
|
||||
pos = tile_row * 10 + col;
|
||||
if ((g_fg[pos] & 0x1F) != TILE_LOOSE) continue;
|
||||
if (pop_loose_modif[pos] == 0)
|
||||
pop_loose_modif[pos] = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
/* check_knock (seg003:0706): по флагу knock (ставит play_seq на seqtbl-
|
||||
* командах KNOCK_UP/DOWN) тряхнуть ряд. KNOCK_DOWN (knock=-1, приземление) →
|
||||
* ряд curr_row; KNOCK_UP (knock=+1) → ряд curr_row-1. */
|
||||
void pop_check_knock(void)
|
||||
{
|
||||
if (knock) {
|
||||
do_knock(Kid.curr_row - (knock > 0));
|
||||
knock = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* animate_loose (seg007:0CBA) + перерисовка изменившихся тайлов на ТЕКУЩЕЙ
|
||||
* (back) странице. Звать из основного цикла ПОСЛЕ set_draw_page(back). */
|
||||
void pop_loose_tick(void)
|
||||
{
|
||||
int pos;
|
||||
for (pos = 0; pos < 30; pos++) {
|
||||
if (loose_bake[pos]) { /* упал: запечь пусто (2 кадра=2 стр.) */
|
||||
pop_loose_bake_empty(pos / 10, pos % 10);
|
||||
loose_bake[pos]--;
|
||||
continue;
|
||||
}
|
||||
if (loose_rest[pos]) { /* тряска кончилась: перерисовать ПОКОЙ */
|
||||
/* modif уже 0 → shake_draw рисует базовый кадр (покой) обоих тайлов
|
||||
* (col и col+1: правая грань loose живёт в 2,7). 2 кадра = обе
|
||||
* страницы дабл-буфера — иначе на одной остаётся застывший
|
||||
* дрожащий кадр правой грани → мерцание («бесконечная тряска»). */
|
||||
pop_loose_shake_draw(pos / 10, pos % 10);
|
||||
loose_rest[pos]--;
|
||||
continue;
|
||||
}
|
||||
if (pop_loose_modif[pos] == 0) continue;
|
||||
{
|
||||
uint8_t m = ++pop_loose_modif[pos];
|
||||
if (m & 0x80) { /* тряска (do_knock) */
|
||||
if (m >= 0x84) { /* конец тряски: сброс + перерисовать покой */
|
||||
pop_loose_modif[pos] = 0;
|
||||
loose_rest[pos] = 2; /* покой на ОБЕИХ страницах */
|
||||
continue;
|
||||
}
|
||||
} else if (m >= 11) { /* loose_floor_delay — падение */
|
||||
/* remove_loose (seg007:0EB8): тайл → EMPTY (не debris!).
|
||||
* Debris появляется ТОЛЬКО там, где приземлился падающий
|
||||
* кусок (mob), т.е. рядом ниже. Loose в нижнем ряду (2,6)
|
||||
* — кусок улетает из комнаты вниз, на месте остаётся пусто
|
||||
* (стена-вниз от тайла снизу + чёрный фон). */
|
||||
g_fg[pos] = TILE_EMPTY;
|
||||
pop_loose_modif[pos] = 0;
|
||||
pop_loose_bake_empty(pos / 10, pos % 10); /* запечь пусто на ТЕКУЩЕЙ странице сразу */
|
||||
loose_bake[pos] = 1; /* + вторая страница следующим кадром */
|
||||
pop_loose_mob_spawn(pos / 10, pos % 10); /* отрыв падающего куска (L4) */
|
||||
continue;
|
||||
}
|
||||
pop_loose_shake_draw(pos / 10, pos % 10); /* дрожащий кадр */
|
||||
}
|
||||
}
|
||||
pop_loose_mob_tick(); /* продвинуть+нарисовать падающий кусок (после тайлов) */
|
||||
}
|
||||
|
||||
void pop_phys_tick(void)
|
||||
{
|
||||
if (grab_timer) grab_timer--;
|
||||
@@ -624,4 +745,6 @@ void pop_phys_tick(void)
|
||||
determine_col();
|
||||
check_bumped(); /* удержать у стены до check_action (порядок PoP) */
|
||||
check_action();
|
||||
check_press(); /* seg006: стойка/пробой loose → make_loose_fall */
|
||||
pop_check_knock(); /* seg003: сотрясение приземления → тряска loose в ряду */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user