PoP roomtest: плита-потолок, порядок слоёв по SDLPoP, фиксы окклюзии и Y
Фича — плита-ПОТОЛОК (loose ряда 2 комнаты сверху, комн.5 (2,5) над комн.6): - check_press (seg006:1683) портирован целиком: кадры виса 87..99 и подъёма 135..140 «нажимают» тайл, за который Kid держится; кадр 79 — удар снизу (с проверкой action, как в оригинале); стояние — тайл под ним; - ряд −1 в pop_map.get_tile = ряд 2 комнаты сверху (порт find_room_of_tile), состояние тряски/падения — pop_ceil_modif[10]; do_knock(-1) трясёт потолок; - падение: mob переведён на координаты оригинала (y_loose_land/y_something), спавнится в ряду −1, по move_loose находит пол → loose_land: debris + do_knock; heal коридора — по прошлой позиции ДЛЯ КАЖДОЙ страницы; - урон: check_loose_fall_on_kid + fell_on_your_head (seq_52/seq_22, −1 HP) и «брызги» draw_hurt_splash (kid image 218); - колодец наверх: leave_room «вверх» проверяется ПЕРВЫМ (иначе y≈248 при подтягивании ловил check_leave_below и ронял Kid из уровня); - персистентность: общий tile-override (щебень внизу, пустота в комнате сверху) вместо debris-only. Окклюзия — приведена к оригиналу (найдено трассировкой собранного SDLPoP): - draw_tile_right/anim_right/draw_loose кладут спрайты в add_backtable НАПРЯМУЮ, минуя ptr_add_table → правая грань соседа («шахматка» столба 93), blueline и грани loose/пик соседа ВСЕГДА под персонажем. draw_other_overlay поверх Kid даёт только: 42 при левом соседе-поле, base, свои пики, bottom (overlay_mid_tile); - порядок объектов: тайл Kid (set_objtile_at_char) и порядок обхода (ряды 2→0, колонки 0→9), внутри тайла — сортировка по y (sort_curr_objs); то же правило для падающей плиты (pop_loose_mob_draw_over); - fore-слой рисуется ПОСЛЕ оверлеев (foretable после midtable), иначе тёмный скос floor_left_overlay ложился поверх колонны; - fore_tile больше не рисует bottom_id (в оригинале он в backtable) — минус 2..4 блита за кадр; - полоса потолка поверх Kid = draw_tile_bottom(1)+draw_loose(1)+draw_tile_fore (arg_0=1 добавляет в foretable), остальное — под ним; - следы оверлея восстанавливаются на ОБЕИХ страницах (pop_fore_heal). Прочее: - control(): при action bumped/in_freefall управление игнорируется целиком (seg005) — иначе прерванный присед терял dy(1)+dy(1) из medland и Kid навсегда оставался на 2px выше пола; - check_bumped: стена ищется по колонке переднего края (а не curr_col) + гард разворота — Kid больше не проходит сквозь опущенную решётку шва; - pop_floor_bake для смены floor→debris (bake_empty стирал верх стены ряда ниже — чёрный прямоугольник); - в pop_room_redraw_seam_left восстанавливается полоса потолка над решёткой. Проверено в MAME (bridge); эталон — собранный SDLPoP из applications/PoP/SDLPoP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <unistd.h>
|
||||
#include "pop_bg.h"
|
||||
#include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */
|
||||
#include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев (см. ниже) */
|
||||
|
||||
/* ---- Атласы (прямая адресация) ------------------------------------- */
|
||||
static atlas_t env[5];
|
||||
@@ -81,6 +82,36 @@ static void blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||
/* 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); }
|
||||
|
||||
/* Область, куда в этом кадре рисовался ОВЕРЛЕЙ (не футпринт fore, а
|
||||
* draw_other_overlay/floor_overlay/полоса потолка). Оверлей включается
|
||||
* только в позах виса/полёта/подъёма и рисует НЕ по прямоугольнику Kid,
|
||||
* поэтому kid_heal его следы не убирает: на второй странице дабл-буфера
|
||||
* остаётся кадр «с оверлеем», и он мерцает через кадр. Копим bbox и
|
||||
* восстанавливаем фон на ОБЕИХ страницах (pop_fore_heal). */
|
||||
static int ov_x0, ov_y0, ov_x1, ov_y1;
|
||||
static uint8_t ov_pending; /* биты страниц, где надо восстановить фон */
|
||||
|
||||
static void ov_mark(int x, int y, int w, int h)
|
||||
{
|
||||
if (!ov_pending) { ov_x0 = x; ov_y0 = y; ov_x1 = x + w; ov_y1 = y + h; }
|
||||
else {
|
||||
if (x < ov_x0) ov_x0 = x;
|
||||
if (y < ov_y0) ov_y0 = y;
|
||||
if (x + w > ov_x1) ov_x1 = x + w;
|
||||
if (y + h > ov_y1) ov_y1 = y + h;
|
||||
}
|
||||
ov_pending = 3; /* обе страницы */
|
||||
}
|
||||
|
||||
void pop_fore_heal(void)
|
||||
{
|
||||
uint8_t pg = gfx_get_draw_page() & 1;
|
||||
if (!(ov_pending & (1 << pg))) return;
|
||||
heal_off(ov_x0, ov_y0, ov_x1 - ov_x0, ov_y1 - ov_y0);
|
||||
ov_pending = (uint8_t)(ov_pending & ~(1 << pg));
|
||||
}
|
||||
|
||||
|
||||
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); }
|
||||
@@ -335,6 +366,7 @@ static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
|
||||
* СОСЕД — правую грань через draw_tile_anim_right). При modif==0 (покой/
|
||||
* статич. запекание) get_loose_frame=0 → базовые кадры 41/42/43. */
|
||||
extern uint8_t pop_loose_modif[30];
|
||||
extern uint8_t pop_ceil_modif[10]; /* плита-потолок: loose ряда 2 комнаты сверху */
|
||||
|
||||
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};
|
||||
@@ -432,11 +464,11 @@ static void draw_tile(int row, int col)
|
||||
}
|
||||
|
||||
/* --- draw_loose (seg008.c:600) — нижняя грань (анимир. по modif). В
|
||||
* верхней полосе (row<0) loose ряда 2 верхнего соседа рисуем статически
|
||||
* (кадр покоя 43): анимацию/разбитость соседа мы не тянем. --- */
|
||||
* верхней полосе (row<0) это ПЛИТА-ПОТОЛОК (loose ряда 2 комнаты сверху):
|
||||
* её состояние живёт отдельно, в pop_ceil_modif[col] (pop_map). --- */
|
||||
if (code == 11)
|
||||
env_b(row >= 0 ? LOOSE_FRAM_BOTTOM[get_loose_frame(pop_loose_modif[row * 10 + col])]
|
||||
: LOOSE_FRAM_BOTTOM[0], x, dby);
|
||||
env_b(LOOSE_FRAM_BOTTOM[get_loose_frame(row >= 0 ? pop_loose_modif[row * 10 + col]
|
||||
: pop_ceil_modif[col])], x, dby);
|
||||
|
||||
/* --- draw_tile_base (seg008.c:611). Верхняя полоса (row<0) базу НЕ
|
||||
* рисует (draw_tile_aboveroom) — только нижние/правые грани. --- */
|
||||
@@ -536,6 +568,46 @@ void pop_room_redraw_seam_left(void)
|
||||
bar(0, POP_YOFF, 31, POP_YOFF + 64); /* col0, ряд 0 (бары ворот) */
|
||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||
draw_tile(0, 0); /* фон col0 + бары ворот (живой modif) */
|
||||
/* Bar стёр и полосу кладки У ПОТОЛКА (ряд −1 = ряд 2 комнаты сверху,
|
||||
* draw_tile_aboveroom): без её перерисовки потолок над решёткой пропадал
|
||||
* после первой же анимации ворот. Клип — как в pop_room_draw. */
|
||||
g_clip_top = POP_YOFF;
|
||||
draw_tile(-1, 0);
|
||||
g_clip_top = 0;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* ---- Плита-потолок (loose ряда 2 комнаты СВЕРХУ) ------------------- *
|
||||
* В полосе кладки у потолка (наш ряд −1) виден только НИЗ плиты
|
||||
* (draw_tile_aboveroom: bottom+loose, всё выше отрезано клипом POP_YOFF).
|
||||
* Дрожащий кадр берётся из pop_ceil_modif[col] (см. draw_loose выше).
|
||||
* shake — на текущей (back) странице поверх фона; bake — «запечь» пустоту
|
||||
* после провала (NORMAL, в ОЗУ-копию, чтобы heal её сохранял). */
|
||||
#define CEIL_BAND_H 8 /* видимая высота полосы (room-space y 0..7) */
|
||||
|
||||
void pop_ceil_shake_draw(int col)
|
||||
{
|
||||
int x = COL_XH[col] * 8;
|
||||
heal_off(x, 0, 64, CEIL_BAND_H); /* фон полосы обоих тайлов */
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
g_clip_top = POP_YOFF;
|
||||
draw_tile(-1, col);
|
||||
if (col + 1 < 10) draw_tile(-1, col + 1);
|
||||
g_clip_top = 0;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
void pop_ceil_bake_empty(int col)
|
||||
{
|
||||
int x = COL_XH[col] * 8;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(x, POP_YOFF, x + 63, POP_YOFF + CEIL_BAND_H); /* стереть плиту в ОЗУ-копии */
|
||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||
g_clip_top = POP_YOFF;
|
||||
draw_tile(-1, col); /* тайл уже EMPTY → колодец наверх */
|
||||
if (col + 1 < 10) draw_tile(-1, col + 1);
|
||||
g_clip_top = 0;
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
@@ -543,12 +615,12 @@ void pop_room_redraw_seam_left(void)
|
||||
* (draw_tile подставляет пол/stuck по таймеру связи). Bar-в-чёрный +
|
||||
* redraw — иначе «шишка» отжатой кнопки (base 147) осталась бы при переходе
|
||||
* в пол. Звать из pop_process_trobs для кнопок текущей комнаты. */
|
||||
void pop_button_redraw(int row, int col)
|
||||
void pop_floor_bake(int row, int col)
|
||||
{
|
||||
int x = COL_XH[col] * 8;
|
||||
int yb = POP_YOFF + 63 * row;
|
||||
/* Wipe СВОЕЙ ячейки + правой грани кнопки (заходит в 0,3) + перерисовка
|
||||
* пола. НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда 1
|
||||
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
||||
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
||||
* (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
@@ -559,6 +631,8 @@ void pop_button_redraw(int row, int col)
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
void pop_button_redraw(int row, int col) { pop_floor_bake(row, col); }
|
||||
|
||||
/* Запечь ПУСТОТУ на месте упавшего loose (текущая страница). Порт
|
||||
* redraw_at_cur_mob (seg007:132C): перерисовать сам тайл (теперь empty) И
|
||||
* тайл СПРАВА (его левая грань зависит от нашего кода: пока был loose —
|
||||
@@ -597,20 +671,35 @@ void pop_loose_bake_empty(int row, int col)
|
||||
* кадр 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 uint8_t mob_active; /* 0 нет; 1 падает */
|
||||
static uint8_t mob_clean; /* кадров дочистки после конца полёта (2 = обе стр.) */
|
||||
static uint8_t mob_defer; /* кусок ниже Kid → рисуем ПОСЛЕ него (y-порядок) */
|
||||
static int mob_x; /* левый край (COL_XH*8) */
|
||||
static int mob_y; /* main-y (dmy покоя), растёт вниз */
|
||||
static int mob_y; /* = curmob.y оригинала (низ куска, dby), растёт вниз */
|
||||
static int mob_prev_y[2];/* последняя нарисованная y на КАЖДОЙ странице (heal) */
|
||||
static int8_t mob_speed;
|
||||
static int8_t mob_row, mob_col; /* тайл-позиция куска (для окклюзии соседа) */
|
||||
|
||||
/* Кусок «приземлился» (порт loose_land): 0 = нет, иначе tilepos+1 тайла, на
|
||||
* который он лёг → приложение/pop_map ставит debris + do_knock. */
|
||||
uint8_t pop_loose_landed;
|
||||
|
||||
/* y_loose_land / y_something (seg007): [row+1], row = −1..3. Ряд −1 —
|
||||
* плита-потолок (ряд 2 комнаты сверху): в оригинале это отрицательный
|
||||
* tilepos, у нас — свой ряд полосы у потолка. */
|
||||
static const int16_t MOB_Y_LAND[5] = {2, 65, 128, 191, 254};
|
||||
static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25};
|
||||
#define MOB_Y_NONE 0x7FFF
|
||||
|
||||
void pop_loose_mob_spawn(int row, int col)
|
||||
{
|
||||
mob_x = COL_XH[col] * 8;
|
||||
mob_y = 63 * row + 62; /* dmy покоя — откуда отрывается кусок */
|
||||
mob_y = MOB_Y_LAND[row + 1]; /* y_loose_land: низ куска в момент отрыва */
|
||||
mob_row = (int8_t)row;
|
||||
mob_col = (int8_t)col;
|
||||
mob_speed = 0;
|
||||
mob_active = 1;
|
||||
mob_clean = 0;
|
||||
}
|
||||
|
||||
/* Габарит куска по X: left/bottom (41/43, w=32) с mob_x; right (42, w=26)
|
||||
@@ -623,39 +712,114 @@ void pop_loose_mob_spawn(int row, int col)
|
||||
|
||||
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
|
||||
* должно утечь в новую комнату). */
|
||||
void pop_loose_mob_reset(void) { mob_active = 0; }
|
||||
/* Тайл ОБЪЕКТА Kid (set_objtile_at_char, seg006:1833) — в момент обработки
|
||||
* этого тайла спрайт Kid попадает в midtable. Задаётся в pop_fore_over_kid,
|
||||
* читается other_overlay_tile (порядок «кто поверх кого»). */
|
||||
static int8_t obj_col, obj_row;
|
||||
|
||||
static void mob_render(uint8_t pg);
|
||||
|
||||
void pop_loose_mob_reset(void)
|
||||
{
|
||||
mob_active = 0; mob_clean = 0; mob_defer = 0;
|
||||
mob_prev_y[0] = mob_prev_y[1] = MOB_Y_NONE;
|
||||
}
|
||||
|
||||
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; } /* очистили вторую стр. — всё */
|
||||
uint8_t pg = gfx_get_draw_page() & 1;
|
||||
if (!mob_active && !mob_clean) return;
|
||||
/* Стереть кусок, нарисованный на ЭТОЙ странице в прошлый раз (у каждой
|
||||
* страницы своя копия фона). Раньше коридор был фиксирован под ряд 2;
|
||||
* плита-потолок падает через весь ряд 0, поэтому чистим по фактической
|
||||
* прошлой позиции. */
|
||||
if (mob_prev_y[pg] != MOB_Y_NONE) {
|
||||
heal_off(MOB_X0(mob_x), mob_prev_y[pg] - 27, MOB_W, 32);
|
||||
mob_prev_y[pg] = MOB_Y_NONE;
|
||||
}
|
||||
if (!mob_active) { mob_clean--; 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 кадр очистки */
|
||||
/* move_loose (seg007:1126): низ куска пересёк границу своего ряда —
|
||||
* смотрим тайл этого ряда. Пусто/loose — валится дальше (порт
|
||||
* loose_fall «плита ломает плиту» не нужен: в ряду ниже её нет);
|
||||
* пол — приземление (loose_land: debris + do_knock). */
|
||||
if (mob_row < 3 && mob_y < 226 && MOB_Y_BOUND[mob_row + 1] <= mob_y) {
|
||||
uint8_t t = (uint8_t)(tile_code(mob_row, mob_col) & 0x1F);
|
||||
if (t == 0 || t == 11) {
|
||||
mob_row++; /* mob_down_a_row */
|
||||
} else {
|
||||
mob_y = MOB_Y_BOUND[mob_row + 1];
|
||||
pop_loose_landed = (uint8_t)(mob_row * 10 + mob_col + 1);
|
||||
mob_active = 0; mob_clean = 2; /* кусок исчез — на его месте debris */
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mob_y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */
|
||||
mob_active = 0; mob_clean = 2;
|
||||
return;
|
||||
}
|
||||
/* Порядок «плита vs Kid». В оригинале объекты рисуются при обходе СВОИХ
|
||||
* тайлов (redraw_needed_tiles: ряды 2,1,0; колонки 0..9), и только внутри
|
||||
* ОДНОГО тайла сортируются по obj_y (sort_curr_objs, seg008:1561: кто
|
||||
* ниже — тот позже, т.е. поверх). Тайл падающего куска — draw_mob
|
||||
* (seg007:13E5): row = y_to_row_mod4(y), col = xh>>2. Значит кусок
|
||||
* поверх Kid, если его тайл идёт в обходе ПОЗЖЕ тайла Kid, а при
|
||||
* совпадении тайлов — если он ниже по y. */
|
||||
{
|
||||
int8_t mrow = (int8_t)(((mob_y + 60) / 63) % 4 - 1); /* y_to_row_mod4 */
|
||||
uint8_t over;
|
||||
if (mrow != obj_row) over = (mrow < obj_row); /* ряды идут 2,1,0 */
|
||||
else if (mob_col != obj_col) over = (mob_col > obj_col);
|
||||
else over = (mob_y > kid_fp_obj_y());
|
||||
if (over) { mob_defer = 1; return; }
|
||||
}
|
||||
mob_render(pg);
|
||||
}
|
||||
|
||||
static void mob_render(uint8_t pg)
|
||||
{
|
||||
/* Падающий кусок (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). */
|
||||
* заходит в тайл СПРАВА) на obj_y-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);
|
||||
env_b(43, mob_x, mob_y);
|
||||
env_b(41, mob_x, mob_y - 3);
|
||||
env_b(42, mob_x + 32, mob_y - 1);
|
||||
mob_prev_y[pg] = mob_y;
|
||||
/* Окклюзия правого куска соседним полом (SDLPoP redraw_at_cur_mob:
|
||||
* set_redraw_full(curr_tilepos+1) — сосед перерисовывается ПОВЕРХ mob).
|
||||
* Правый кусок 42 целиком лежит в тайле col+1; пока плита у уровня пола,
|
||||
* её правый край ПРЯЧЕТСЯ за передней гранью пола 2,7. Перерисовываем
|
||||
* её правый край ПРЯЧЕТСЯ за передней гранью пола. Перерисовываем
|
||||
* сосед-тайл В ТОМ ЖЕ КАДРЕ поверх куска (иначе край торчит на полу
|
||||
* до heal'а следующего кадра — виден как хвост во время падения). */
|
||||
if (mob_col + 1 < 10)
|
||||
if (mob_col + 1 < 10) {
|
||||
if (mob_row < 0) g_clip_top = POP_YOFF; /* полоса у потолка — только срез */
|
||||
draw_tile(mob_row, mob_col + 1);
|
||||
g_clip_top = 0;
|
||||
}
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
/* клип СВОЕГО выхода за низ поля (в статус-полосу) — по ВСЕЙ ширине куска */
|
||||
pop_clip_sprite(MOB_X0(mob_x), MOB_W, mob_y - 23 + POP_YOFF, 30);
|
||||
/* клип СВОЕГО выхода за поле (верхний борт / статус-полоса) — по ВСЕЙ ширине */
|
||||
pop_clip_sprite(MOB_X0(mob_x), MOB_W, mob_y - 26 + POP_YOFF, 30);
|
||||
}
|
||||
|
||||
/* Отложенная отрисовка куска ПОВЕРХ Kid (он ниже Kid по obj_y). Звать
|
||||
* после kid_draw, до fore-слоя. */
|
||||
void pop_loose_mob_draw_over(void)
|
||||
{
|
||||
if (!mob_defer) return;
|
||||
mob_defer = 0;
|
||||
mob_render(gfx_get_draw_page() & 1);
|
||||
}
|
||||
|
||||
/* Позиция падающего куска для check_loose_fall_on_kid (pop_map): колонка и
|
||||
* y (низ куска). 0 — куска нет. */
|
||||
uint8_t pop_loose_mob_pos(int *col, int *y)
|
||||
{
|
||||
if (!mob_active) return 0;
|
||||
*col = mob_col; *y = mob_y;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Спрайт вышел за поле? → взвести флаг на 2 кадра (обе страницы дабл-
|
||||
@@ -731,37 +895,63 @@ static int8_t y_to_row(int y) /* y_to_row_mod4 (seg006, TILE_SIZEY=
|
||||
return (int8_t)((y + 60) / 63 % 4 - 1);
|
||||
}
|
||||
|
||||
static void fore_tile(int row, int col) /* передний слой одного тайла */
|
||||
static void fore_only_tile(int row, int col)
|
||||
{
|
||||
uint8_t code = tile_code(row, col);
|
||||
const piece *t;
|
||||
/* Нажатая кнопка → пол/stuck (как get_tile_to_draw), ИНАЧЕ fore рисует
|
||||
* переднюю грань КНОПКИ (bottom_id 149) поверх Kid, перекрывая уже
|
||||
* нарисованную грань пола 43 → «остаток нажатой кнопки» на грани. */
|
||||
/* Нажатая кнопка → пол/stuck (get_tile_to_draw), иначе поверх Kid лезет
|
||||
* передняя грань КНОПКИ. */
|
||||
if (code == 0x0F) { if ((pop_doorlink2(tile_mod(row, col)) & 0x1F) > 1) code = 1; }
|
||||
else if (code == 0x06) { if ((pop_doorlink2(tile_mod(row, col)) & 0x1F) > 1) code = 5; }
|
||||
t = &tile_table[code];
|
||||
int x = COL_XH[col] * 8;
|
||||
int dmy = 63 * row + 62;
|
||||
int dby = dmy + 3; /* = 63*row+65 (draw_tile_bottom) */
|
||||
if (code == 20) { /* стена: ОБЕ грани перекрывают Kid */
|
||||
uint8_t wmod = wall_modifier(row, col);
|
||||
wall_b(WALL_FRAM_BOTTOM[wmod], x, dby); /* нижняя грань-решётка (draw_tile_bottom): */
|
||||
wall_pattern(row, col, 0); /* перекрывает голову/руки в НИЖНЕЙ части ячейки */
|
||||
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань (draw_tile_fore) */
|
||||
if (code == 20) {
|
||||
wall_b(WALL_FRAM_MAIN[wall_modifier(row, col)], x, dmy);
|
||||
wall_pattern(row, col, 1);
|
||||
} else {
|
||||
if (t->bottom_id) /* передняя кромка пола (draw_tile_bottom) */
|
||||
env_b(t->bottom_id, x, dby);
|
||||
if (code == 2) { /* пики: fore-кадр поверх Kid (напоротый) */
|
||||
uint8_t sm = tile_mod(row, col);
|
||||
uint8_t sf = (sm & 0x80) ? 5 : sm;
|
||||
if (sf > 0 && sf < 10 && SPIKES_FRAM_FORE[sf])
|
||||
env_b(SPIKES_FRAM_FORE[sf], x, dmy - 2);
|
||||
}
|
||||
if (t->fore_id) /* передние объекты: колонны/ворота/щебень */
|
||||
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
|
||||
return;
|
||||
}
|
||||
if (code == 2) { /* пики: fore-кадр */
|
||||
uint8_t sm = tile_mod(row, col);
|
||||
uint8_t sf = (sm & 0x80) ? 5 : sm;
|
||||
if (sf > 0 && sf < 10 && SPIKES_FRAM_FORE[sf])
|
||||
env_b(SPIKES_FRAM_FORE[sf], x, dmy - 2);
|
||||
}
|
||||
if (t->fore_id)
|
||||
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
|
||||
}
|
||||
|
||||
/* Полоса кладки У ПОТОЛКА поверх Kid — порт redraw_needed_above (seg008:0221).
|
||||
* Там draw_tile_bottom(1) и draw_loose(1): аргумент 1 = «добавить ещё и в
|
||||
* FOREtable», т.е. нижняя грань потолка и кадр плиты-потолка рисуются ПОВЕРХ
|
||||
* персонажа (иначе руки прыгающего Kid лезут на кромку потолка). Всё
|
||||
* остальное из этой функции (floorright, anim_topright, right) идёт в
|
||||
* backtable — под персонажем. */
|
||||
static void ceil_over_kid_tile(int col)
|
||||
{
|
||||
uint8_t code = tile_code(-1, col);
|
||||
const piece *t = &tile_table[code];
|
||||
int x = COL_XH[col] * 8;
|
||||
int dby = 63 * (-1) + 65; /* = 2 */
|
||||
if (code == 20) { /* draw_tile_bottom: стена */
|
||||
wall_b(WALL_FRAM_BOTTOM[wall_modifier(-1, col)], x, dby);
|
||||
wall_pattern(-1, col, 0);
|
||||
} else if (t->bottom_id) {
|
||||
env_b(t->bottom_id, x, dby);
|
||||
}
|
||||
if (code == 11) /* draw_loose(1): плита-потолок */
|
||||
env_b(LOOSE_FRAM_BOTTOM[get_loose_frame(pop_ceil_modif[col])], x, dby);
|
||||
fore_only_tile(-1, col); /* draw_tile_fore */
|
||||
}
|
||||
|
||||
static void fore_tile(int row, int col) /* передний слой одного тайла */
|
||||
{
|
||||
/* draw_tile_fore (seg008:690) — ТОЛЬКО add_foretable-часть. Раньше здесь
|
||||
* рисовалась ещё и передняя кромка пола (bottom_id) «чтобы перекрывала
|
||||
* Kid», но в оригинале bottom идёт через draw_tile_bottom в backtable
|
||||
* (под персонажем), а поверх него попадает лишь при draw_other_overlay.
|
||||
* Из-за лишнего bottom у основания колонны проскакивала «тень». */
|
||||
fore_only_tile(row, col);
|
||||
}
|
||||
|
||||
/* Пол-оверлей при подъёме (draw_floor_overlay, seg008:1457): когда Kid
|
||||
@@ -782,15 +972,70 @@ static void climb_overlay_tile(int row, int col, uint8_t fidx)
|
||||
dmy = 63 * row + 62;
|
||||
dby = dmy + 3;
|
||||
t = &tile_table[code];
|
||||
ov_mark(x, 63 * row + 2, 64, 64);
|
||||
env_b(FLOOR_LEFT_OVERLAY[fidx], x, dmy + (code == 5 ? 1 : 0));
|
||||
if (t->bottom_id) env_b(t->bottom_id, x, dby); /* draw_tile_bottom */
|
||||
}
|
||||
|
||||
|
||||
/* draw_tile2 (seg008:1F48) — но ТОЛЬКО та его часть, что реально может лечь
|
||||
* ПОВЕРХ персонажа. Ключ (подтверждено трассой оригинала): draw_tile_right,
|
||||
* draw_tile_anim_right и draw_loose добавляют спрайты через add_backtable
|
||||
* НАПРЯМУЮ, игнорируя ptr_add_table — значит правая грань левого соседа
|
||||
* («шахматка» столба 93, blueline, грани пик/loose соседа, кадр loose)
|
||||
* ВСЕГДА рисуется ПОД персонажем. Через ptr_add_table (→ midtable при
|
||||
* draw_other_overlay) идут только: «floor B» 42 при левом соседе-поле,
|
||||
* draw_tile_base, draw_tile_anim (свои пики) и draw_tile_bottom. */
|
||||
static void overlay_mid_tile(int row, int col)
|
||||
{
|
||||
uint8_t code = tile_code(row, col);
|
||||
uint8_t lcode = tile_code(row, col - 1);
|
||||
const piece *t;
|
||||
int x = COL_XH[col] * 8;
|
||||
int dby = 63 * row + 65;
|
||||
int dmy = dby - 3;
|
||||
if (code == 0x0F) { if ((pop_doorlink2(tile_mod(row, col)) & 0x1F) > 1) code = 1; }
|
||||
else if (code == 0x06) { if ((pop_doorlink2(tile_mod(row, col)) & 0x1F) > 1) code = 5; }
|
||||
if (lcode == 0x0F) { if ((pop_doorlink2(tile_mod(row, col - 1)) & 0x1F) > 1) lcode = 1; }
|
||||
else if (lcode == 0x06) { if ((pop_doorlink2(tile_mod(row, col - 1)) & 0x1F) > 1) lcode = 5; }
|
||||
t = &tile_table[code];
|
||||
ov_mark(x, 63 * row + 2, 64, 64); /* тайл + правый сосед (грани) */
|
||||
if (code != 20 && lcode == 1) /* draw_tile_right: только 42 */
|
||||
env_b(42, x, tile_table[1].right_y + dmy);
|
||||
if (t->base_id) /* draw_tile_base (loose base = 0) */
|
||||
env_b(t->base_id, x, t->base_y + dmy);
|
||||
if (code == 2) { /* draw_tile_anim: свои пики */
|
||||
uint8_t sm = tile_mod(row, col);
|
||||
uint8_t sf = (sm & 0x80) ? 5 : sm;
|
||||
if (sf > 0 && sf < 10 && SPIKES_FRAM_LEFT[sf])
|
||||
env_b(SPIKES_FRAM_LEFT[sf], x, dmy - 2);
|
||||
}
|
||||
if (code == 20) { /* draw_tile_bottom */
|
||||
uint8_t wmod = wall_modifier(row, col);
|
||||
wall_b(WALL_FRAM_BOTTOM[wmod], x, dby);
|
||||
wall_pattern(row, col, 0);
|
||||
} else if (t->bottom_id) {
|
||||
env_b(t->bottom_id, x, dby);
|
||||
}
|
||||
}
|
||||
|
||||
/* draw_tile_fore (seg008:690) — ТОЛЬКО передний слой (add_foretable): он
|
||||
* рисуется после всего и поверх персонажа. Нужен для полосы кладки у
|
||||
* потолка: в redraw_needed_above всё остальное идёт в backtable (под Kid). */
|
||||
|
||||
/* draw_other_overlay (seg008:1492): для поз виса/прыжка/падения/захвата на
|
||||
* КРОМКЕ (сосед слева пуст, либо тайл через один слева пуст) — перерисовать
|
||||
* ВЕСЬ тайл поверх Kid. Тело пола перекрывает висящего/прыгающего/падающего
|
||||
* ПОД ним. Срабатывает только в этих позах (см. redraw_at_char2 ниже) —
|
||||
* поэтому при стоянии/залезании ложной окклюзии нет. */
|
||||
* поэтому при стоянии/залезании ложной окклюзии нет.
|
||||
*
|
||||
* ПОРЯДОК (redraw_needed_tiles, seg008:1B06 + redraw_needed, seg008:0211):
|
||||
* тайлы обходятся ряды 2,1,0, внутри ряда колонки 0..9; в тайле сначала
|
||||
* оверлей (redraw_frames2 -> midtable), потом ОБЪЕКТЫ этого тайла. Kid — тоже
|
||||
* запись midtable, вставляемая при обработке СВОЕГО тайла (obj_row/obj_col).
|
||||
* Значит оверлей ложится ПОВЕРХ Kid только у тайлов, идущих в обходе ПОЗЖЕ
|
||||
* его собственного; всё, что раньше, — под ним. У нас списков нет (рисуем
|
||||
* сразу), поэтому порядок эмулируется явной проверкой. */
|
||||
static void other_overlay_tile(int row, int col)
|
||||
{
|
||||
uint8_t code = tile_code(row, col);
|
||||
@@ -802,9 +1047,19 @@ static void other_overlay_tile(int row, int col)
|
||||
* висящем/падающем Kid. Фон запечён один раз в pop_room_draw — над
|
||||
* спрайтом его перерисовывать не нужно. */
|
||||
if (code == 0) return;
|
||||
/* Тайл идёт в обходе РАНЬШЕ тайла Kid → его оверлей лёг бы ПОД спрайт. */
|
||||
if (row > obj_row || (row == obj_row && col <= obj_col)) return;
|
||||
left = tile_code(row, col - 1);
|
||||
if (left == 0 || (col > 0 && tile_code(row, col - 2) == 0))
|
||||
draw_tile(row, col);
|
||||
if (left == 0) { overlay_mid_tile(row, col); return; } /* ветка 1: сосед слева пуст */
|
||||
if (col > 0 && tile_code(row, col - 2) == 0) {
|
||||
/* Ветка 2 (тайл ЧЕРЕЗ ОДИН слева пуст): в оригинале она ставит
|
||||
* tile_object_redraw[tilepos]=0xFF, т.е. объекты тайлов tilepos−1 и
|
||||
* tilepos рисуются СРАЗУ ПОСЛЕ оверлея — Kid возвращается наверх.
|
||||
* Без этого правая грань соседнего столба (кадр 93) остаётся поверх
|
||||
* прыгающего Kid (баг «тень на дальнем столбе поверх Кида»). */
|
||||
if (row == obj_row && (col == obj_col + 1)) return;
|
||||
overlay_mid_tile(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
@@ -813,7 +1068,7 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int wh = (int)((w + 1) >> 1); /* char_width_half */
|
||||
int xl = obj_x / 2 + 58; /* char_x_left (58-база) */
|
||||
int xr;
|
||||
int8_t cL, cR, cLraw, rT, rB, r, c;
|
||||
int8_t cL, cR, cLraw, rT, rB, r, c, rTraw;
|
||||
if (dir >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
|
||||
xr = xl + wh;
|
||||
cLraw = col_from_x(xl); /* до клампа: <0 = футпринт за левым швом */
|
||||
@@ -829,6 +1084,7 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
rT = y_to_row(top_y);
|
||||
rB = y_to_row(obj_y);
|
||||
if (rB == -1) rB = 3; /* обёртка → ряд 3 (ниже комнаты) */
|
||||
rTraw = rT; /* −1 = верх спрайта в полосе у потолка */
|
||||
}
|
||||
/* single-room roomtest: ряд 3 (комната снизу) в этом виде не рисуется →
|
||||
* окклюдер спереди = ряд 2 (передняя грань пола, за которой нога). */
|
||||
@@ -840,10 +1096,17 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
* (нижняя грань стены / кромка пола) не перерисуется поверх Kid. Форсим
|
||||
* включение ряда rB-1. */
|
||||
if (rB > 0 && rT >= rB) rT = rB - 1;
|
||||
/* set_objtile_at_char (seg006:1833): тайл, при обработке которого спрайт
|
||||
* Kid попадает в midtable — база порядка окклюзии в other_overlay_tile.
|
||||
* run_jump (action 1) — нижний ряд/левая колонка габарита; вис/полёт/
|
||||
* подъём — колонка на одну ЛЕВЕЕ curr_col. */
|
||||
obj_row = Kid.curr_row;
|
||||
obj_col = Kid.curr_col;
|
||||
if (action == 1) { obj_row = rB; obj_col = cL; }
|
||||
if ((frame >= 135 && frame < 149) ||
|
||||
action == 2 || action == 3 || action == 4 || action == 6)
|
||||
obj_col--;
|
||||
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, heal восстановит */
|
||||
for (r = rT; r <= rB; r++)
|
||||
for (c = cL; c <= cR; c++)
|
||||
fore_tile(r, c);
|
||||
/* Шовная окклюзия ворот (порт redraw_at_char для шва): футпринт зашёл за
|
||||
* ЛЕВЫЙ шов (cLraw<0) и у соседа слева в этом ряду решётка ворот (код 4) —
|
||||
* перерисовать её бары ПОВЕРХ Kid. Иначе Kid, стоя за закрытыми воротами
|
||||
@@ -879,5 +1142,25 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
}
|
||||
}
|
||||
}
|
||||
/* draw_tile_fore (seg008:690) — foretable, рисуется ПОСЛЕ midtable, т.е.
|
||||
* ПОСЛЕ оверлеев выше: передняя грань колонны перекрывает скос пола
|
||||
* (floor_left_overlay), а не наоборот. Раньше fore шёл первым, и тёмный
|
||||
* скос ложился поверх колонны — «тень у её основания». */
|
||||
for (r = rT; r <= rB; r++)
|
||||
for (c = cL; c <= cR; c++)
|
||||
fore_tile(r, c);
|
||||
/* redraw_needed_above (seg008:0221): полоса кладки У ПОТОЛКА (ряд −1 =
|
||||
* ряд 2 комнаты сверху). В оригинале она рисуется в самом конце обхода
|
||||
* тайлов — ПОСЛЕ объектов, т.е. поверх персонажа (redraw_frames_above,
|
||||
* куда set_redraw* уводит отрицательные tilepos). Без этого руки Kid,
|
||||
* прыгающего в ряду 0, лезли поверх потолка. */
|
||||
if (rTraw < 0) {
|
||||
g_clip_top = POP_YOFF;
|
||||
for (c = cL; c <= cR; c++) {
|
||||
ov_mark(COL_XH[c] * 8, 0, 64, CEIL_BAND_H);
|
||||
ceil_over_kid_tile(c);
|
||||
}
|
||||
g_clip_top = 0;
|
||||
}
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user