c127a4b0d2
Графика chtab_1 (flame/sword/potion) — новый атлас pop_pot.atl: - склянка зелья берётся из chtab_1 (seg008 draw_tile_fore: id 12 малая / 13 большая при типах 2..4), а НЕ chtab_6 id 12 — res212 в VDUNGEON нет, каскад падал на VPALACE и рисовал кусок дворцовой декорации («мусорный объект» в комнате 5); - пакуем chtab_1 целиком (id 1..23): пламя, склянки, кадры пузырька; - MONO-блит (method_3_blit_mono, seg009:3040) красит спрайт цветом из ОБЩЕЙ 16-цветной палитры экрана — она добавлена в 0x30..0x3F, палитра chtab_1 в 0x40..0x4F; пузырёк пакуется силуэтом цвета 12 (красный), его маска — цвета 0; - в env-атлас добавлено основание факела (env 146, seg008:489 — рисует правый сосед); в статический render_room оно не попадало. Анимация (seg007 animate_torch/animate_potion + seg000:0B12 anim_tile_modif): при входе в комнату факелам и зельям задаётся случайная стартовая фаза и заводится trob; факел — get_torch_frame, пламя в ячейке ПРАВОГО соседа (xh = draw_xh+1, y = draw_main_y−40); зелье — bubble_next_frame по младшим 3 битам модификатора (старшие 5 = тип, при загрузке уровня modif <<= 3, seg009). Пламя и пузырёк не запекаются в фон: heal своей области + кадр поверх. Скорость пламени /2 — наш логический кадр короче игрового тика оригинала. Скорость отрисовки (профиль в MAME, кадр = 430 080 тактов): - замер показал, что цена блита почти НЕ зависит от размера — 13 288 тактов на спрайт 32×3 против 4 617 у линейного спрайтового ядра без клипа; платим за проход gfx_blit → gfx_blit_part → _gfx_blit_full; - в libbgi добавлен gfx_blit_noclip() — блит без клипа в ТЕКУЩЕМ банке (putsprite не годится: навязывает GFX_BANK_SPRITE, фону нужен TRANSPARENT ради ОЗУ-копии); pop_bg.blit_b уходит на него, когда спрайт целиком на экране и не нужен g_clip_top → ~2.9× на блит; - W3-скобку ставит САМА libbgi: из модуля с --w3 её вызывать нельзя — после _bgi_begin окно W3 занято видеобанком и код вызывающего исчезает (проявлялось белым экраном); - редрой шва больше не перерисовывает полосу потолка (bar начинается с POP_YOFF+3) — это удваивало стоимость блока при анимации решётки; - docs/size_optimization_plan.md §7–§8: замеры, сделанное и запас (батчинг W3-скобки, решётка одним спрайтом, лишние блиты). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1261 lines
70 KiB
C
1261 lines
70 KiB
C
/*
|
||
* pop_bg.c — статический фон комнаты PoP композицией слоёв (порт
|
||
* applications/PoP/toolchain/render_room.py -> seg008.c draw_tile/
|
||
* wall_pattern). Подробности алгоритма — в render_room.py и
|
||
* memory/pop_background_strategy.
|
||
*
|
||
* Только DUNGEON-стиль (Фаза 1). Анимация (пламя факелов, шипы, дверь-
|
||
* плита, сама решётка ворот) НЕ рисуется — отдельными sprite_t поверх
|
||
* (Фаза 2). Решётка ворот из ЛЕВОЙ комнаты рисуется статичным кадром.
|
||
*/
|
||
#include <graphics.h>
|
||
#include <gfx.h>
|
||
#include <sprite.h>
|
||
#include <fcntl.h>
|
||
#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];
|
||
static atlas_t wall_at;
|
||
static atlas_t fore_at;
|
||
static atlas_t pot_at; /* chtab_1: зелья (склянка + пузырёк) */
|
||
|
||
static const char *const ENV_ATL[5] = {
|
||
"pop_env0.atl", "pop_env1.atl", "pop_env2.atl",
|
||
"pop_env3.atl", "pop_env4.atl"
|
||
};
|
||
|
||
int pop_bg_load(void)
|
||
{
|
||
int i;
|
||
for (i = 0; i < 5; i++)
|
||
if (atlas_load(&env[i], ENV_ATL[i]) != 0) return -1;
|
||
if (atlas_load(&wall_at, "pop_wall.atl") != 0) return -1;
|
||
if (atlas_load(&fore_at, "pop_fore.atl") != 0) return -1;
|
||
if (atlas_load(&pot_at, "pop_pot.atl") != 0) return -1;
|
||
return 0;
|
||
}
|
||
|
||
void pop_bg_free(void)
|
||
{
|
||
int i;
|
||
for (i = 0; i < 5; i++)
|
||
atlas_free(&env[i]);
|
||
atlas_free(&wall_at);
|
||
atlas_free(&fore_at);
|
||
atlas_free(&pot_at);
|
||
}
|
||
|
||
/* Верхний клип блитов фона (ЭКРАННАЯ y): !=0 — строки кадра с screen_y <
|
||
* g_clip_top не рисуются (частичным блитом со сдвигом источника). Нужно
|
||
* полосе кладки у потолка (row<0): высокие кадры не должны лезть в верхний
|
||
* борт (за пределы игрового поля). 0 — без клипа. */
|
||
static int g_clip_top;
|
||
|
||
/* Блит ленты idx атласа a: НИЗ спрайта на ybottom, левый край x,
|
||
* прозрачно (0xFF). w/h читаются из getimage-заголовка ленты (в W0).
|
||
* При g_clip_top рисуем только часть кадра ниже границы (gfx_blit_part). */
|
||
static void blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||
{
|
||
const uint8_t *img;
|
||
uint16_t w, h;
|
||
int top;
|
||
if (idx >= a->count)
|
||
return;
|
||
img = (const uint8_t *)atlas_image(a, idx);
|
||
gfx_w0_map(a->page);
|
||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||
top = ybottom - (int)h + 1 + POP_YOFF; /* +YOFF: центрирование */
|
||
if (w && h) {
|
||
if (g_clip_top && top < g_clip_top) {
|
||
int sy = g_clip_top - top; /* пропустить верхние строки */
|
||
if (sy < (int)h)
|
||
gfx_blit_part(x, g_clip_top, img, 0, sy, (int)w, (int)h - sy);
|
||
} else if (x >= 0 && top >= 0 && w < 256 && h < 256 &&
|
||
x + (int)w <= 320 && top + (int)h <= 256) {
|
||
/* Спрайт целиком на экране — линейное ядро без клипа: ~2.9×
|
||
* быстрее (замер: 4617 против 13288 тактов на спрайт 32×3;
|
||
* общее ядро gfx_blit платит за клип/16-бит/split, а не за
|
||
* пиксели). W3-скобку ставит сама libbgi: из кода, собранного
|
||
* с --w3, её вызывать нельзя (окно W3 уходит под видеобанк). */
|
||
gfx_blit_noclip(x, top, img);
|
||
} else {
|
||
gfx_blit(x, top, img); /* край экрана — с клипом */
|
||
}
|
||
}
|
||
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); }
|
||
|
||
/* Область, куда в этом кадре рисовался ОВЕРЛЕЙ (не футпринт 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); }
|
||
static void pot_b(uint8_t id, int x, int yb) { blit_b(&pot_at, id, x, yb); }
|
||
|
||
|
||
|
||
/* ---- Таблица тайлов (piece, seg008.c:27) --------------------------- */
|
||
typedef struct {
|
||
uint8_t base_id, floor_left; int8_t base_y;
|
||
uint8_t right_id, floor_right; int8_t right_y;
|
||
uint8_t stripe_id, topright_id, bottom_id, fore_id, fore_x; int8_t fore_y;
|
||
} piece;
|
||
|
||
static const piece tile_table[31] = {
|
||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 00 empty */
|
||
{ 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 01 floor */
|
||
{127, 1, 0, 133, 1, 2, 145, 0, 43, 0, 0, 0}, /* 02 spike */
|
||
{ 92, 1, 0, 93, 1, 2, 0, 94, 43, 95, 1, 0}, /* 03 pillar */
|
||
{ 46, 1, 0, 47, 1, 2, 0, 48, 43, 49, 3, 0}, /* 04 gate */
|
||
{ 41, 1, 1, 35, 1, 3, 145, 0, 36, 0, 0, 0}, /* 05 stuck floor */
|
||
{ 41, 1, 0, 42, 1, 2, 145, 0, 96, 0, 0, 0}, /* 06 close button */
|
||
{ 46, 1, 0, 0, 0, 2, 0, 0, 43, 49, 3, 0}, /* 07 door top w/floor */
|
||
{ 86, 1, 0, 87, 1, 2, 0, 0, 43, 88, 1, 0}, /* 08 big pillar bottom */
|
||
{ 0, 0, 0, 89, 0, 3, 0, 90, 0, 91, 1, 3}, /* 09 big pillar top */
|
||
{ 41, 1, 0, 42, 1, 2, 145, 0, 43, 12, 2, -3}, /* 0A potion */
|
||
{ 0, 1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0}, /* 0B loose floor */
|
||
{ 0, 0, 0, 0, 0, 2, 0, 0, 85, 49, 3, 0}, /* 0C door top */
|
||
{ 75, 1, 0, 42, 1, 2, 0, 0, 43, 77, 0, 0}, /* 0D mirror */
|
||
{ 97, 1, 0, 98, 1, 2, 145, 0, 43,100, 0, 0}, /* 0E debris */
|
||
{147, 1, 0, 42, 1, 1, 145, 0,149, 0, 0, 0}, /* 0F open button */
|
||
{ 41, 1, 0, 37, 0, 0, 0, 38, 43, 0, 0, 0}, /* 10 leveldoor left */
|
||
{ 0, 0, 0, 39, 1, 2, 0, 40, 43, 0, 0, 0}, /* 11 leveldoor right */
|
||
{ 0, 0, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 12 chomper */
|
||
{ 41, 1, 0, 42, 1, 2, 0, 0, 43, 0, 0, 0}, /* 13 torch */
|
||
{ 0, 0, 0, 1, 1, 2, 0, 2, 0, 0, 0, 0}, /* 14 wall */
|
||
{ 30, 1, 0, 31, 1, 2, 0, 0, 43, 0, 0, 0}, /* 15 skeleton */
|
||
{ 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 16 sword */
|
||
{ 41, 1, 0, 10, 0, 0, 0, 11, 43, 0, 0, 0}, /* 17 balcony left */
|
||
{ 0, 0, 0, 12, 1, 2, 0, 13, 43, 0, 0, 0}, /* 18 balcony right */
|
||
{ 92, 1, 0, 42, 1, 2, 145, 0, 43, 95, 1, 0}, /* 19 lattice pillar */
|
||
{ 1, 0, 0, 0, 0, 0, 0, 0, 2, 9, 0,-53}, /* 1A lattice down */
|
||
{ 3, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1B lattice small */
|
||
{ 4, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1C lattice left */
|
||
{ 5, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1D lattice right */
|
||
{ 97, 1, 0, 98, 1, 2, 0, 0, 43,100, 0, 0}, /* 1E debris w/torch */
|
||
};
|
||
|
||
/* ---- Константные таблицы (render_room.py) -------------------------- */
|
||
static const uint8_t COL_XH[10] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36};
|
||
static const uint8_t TBL_LINE[3] = {0, 10, 20};
|
||
static const uint8_t WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3};
|
||
static const uint8_t WALL_FRAM_MAIN[4] = {8, 10, 6, 4};
|
||
static const uint8_t DOOR_FRAM_SLICE[9] = {67, 59, 58, 57, 56, 55, 54, 53, 52};
|
||
static const uint8_t SPIKES_FRAM_RIGHT[10] = {0, 134, 135, 136, 137, 138, 137, 135, 134, 0};
|
||
static const uint8_t SPIKES_FRAM_LEFT[10] = {0, 128, 129, 130, 131, 132, 131, 129, 128, 0};
|
||
static const uint8_t SPIKES_FRAM_FORE[10] = {0, 139, 140, 141, 142, 143, 142, 140, 139, 0};
|
||
static const uint8_t BLUELINE_FRAM1[4] = {0, 124, 125, 126};
|
||
static const int8_t BLUELINE_FRAM_Y[4] = {0, -20, -20, 0};
|
||
static const uint8_t BLUELINE_FRAM3[4] = {44, 44, 45, 45};
|
||
static const uint8_t LPOS[5] = {58, 41, 37, 20, 16};
|
||
static const uint8_t RPOS[4] = {52, 42, 31, 21};
|
||
|
||
/* Wall (chtab_7) id: делители/блок/марки. */
|
||
#define RES_WALL_RNDBLOCK 13
|
||
#define RES_WALL_DIVIDER1 11
|
||
#define RES_WALL_MARK_TL 14
|
||
#define RES_WALL_MARK_BL 15
|
||
#define RES_WALL_MARK_TR 16
|
||
#define RES_WALL_MARK_BR 17
|
||
|
||
/* ---- Зелье (tiles_10_potion, seg008 draw_tile_anim/draw_tile_fore) ----
|
||
* Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая»
|
||
* склянка (id 13) и pot_size=1, остальные — маленькая (id 12). Спрайты — из
|
||
* chtab_1 (атлас pop_pot), поэтому НЕ из env: раньше мы рисовали chtab_6 id 12
|
||
* (res212), которого в VDUNGEON нет — оттуда и «мусорный объект».
|
||
* Позиции оригинала: склянка xh = fore_x + draw_xh, xl = 6, ybottom = fore_y +
|
||
* draw_main_y; пузырёк/маска xh = draw_xh + 3, xl = 1,
|
||
* ybottom = draw_main_y − (pot_size << 2) − 14. */
|
||
#define POT_MASK_ID 23
|
||
static const uint8_t POTION_FRAM_BUBB[8] = {0, 16, 17, 18, 19, 20, 21, 22};
|
||
|
||
static uint8_t potion_type(uint8_t modif) { return (uint8_t)((modif & 0xF8) >> 3); }
|
||
|
||
static void potion_flask(int row, int col, uint8_t modif) /* draw_tile_fore */
|
||
{
|
||
uint8_t type = potion_type(modif);
|
||
uint8_t id = 12;
|
||
if (type == 0) return;
|
||
if (type >= 2 && type < 5) id = 13; /* большая склянка */
|
||
pot_b(id, (tile_table[10].fore_x + COL_XH[col]) * 8 + 6,
|
||
tile_table[10].fore_y + 63 * row + 62);
|
||
}
|
||
|
||
static void potion_bubble(int row, int col, uint8_t modif, uint8_t fore)
|
||
{
|
||
uint8_t type = potion_type(modif);
|
||
int pot_size = (type >= 2 && type < 5) ? 1 : 0;
|
||
int x = (COL_XH[col] + 3) * 8 + 1;
|
||
int yb = 63 * row + 62 - (pot_size << 2) - 14;
|
||
if (type == 0) return;
|
||
if (fore) pot_b(POTION_FRAM_BUBB[modif & 7], x, yb); /* кадр пузырька */
|
||
else pot_b(POT_MASK_ID, x, yb); /* маска (под ним) */
|
||
}
|
||
|
||
/* ---- Текущая комната (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 const uint8_t *g_above, *g_abovemod; /* row2 fg/mod комнаты СВЕРХУ (полоса кладки у потолка) */
|
||
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; }
|
||
|
||
/* Задать ряд 2 (fg-коды + модификаторы) комнаты СВЕРХУ — для полосы кладки
|
||
* у потолка (seg008 draw_room: доп. верхний ряд из room_A, draw_tile_aboveroom
|
||
* при draw_main_y=-1). 0 — нет комнаты сверху (кромка уровня → пол). */
|
||
void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod)
|
||
{ g_above = above_row2_fg; g_abovemod = above_row2_mod; }
|
||
|
||
static uint8_t tile_code(int row, int col)
|
||
{
|
||
if (col < 0) {
|
||
if (g_lfg && row >= 0 && row < 3) return g_lfg[row];
|
||
return 20; /* уровневая кромка = стена */
|
||
}
|
||
if (row < 0) { /* верхняя полоса: ряд 2 комнаты сверху */
|
||
if (row == -1 && g_above && col < 10) return g_above[col];
|
||
return 1; /* нет комнаты сверху → пол (кромка) */
|
||
}
|
||
if (row > 2 || col > 9) return 20;
|
||
return g_fg[row * 10 + col];
|
||
}
|
||
|
||
static uint8_t tile_mod(int row, int col)
|
||
{
|
||
if (col < 0) {
|
||
if (g_lbg && row >= 0 && row < 3) return g_lbg[row];
|
||
return 0;
|
||
}
|
||
if (row == -1 && g_abovemod && col < 10) return g_abovemod[col];
|
||
if (row < 0 || row > 2 || col > 9) return 0;
|
||
return g_bg[row * 10 + col];
|
||
}
|
||
|
||
/* seg008.c:1255 (без FAKE_TILES): сосед — стена, если его тип == 20.
|
||
* На кромке col0 сосед по умолчанию = стена (как render_room). */
|
||
static uint8_t wall_modifier(int row, int col)
|
||
{
|
||
uint8_t lw = (col > 0) ? (tile_code(row, col - 1) == 20) : 1;
|
||
uint8_t rw = (col < 9) ? (tile_code(row, col + 1) == 20) : 1;
|
||
if (lw && rw) return 3; /* WWW */
|
||
if (lw) return 2; /* WWS */
|
||
if (rw) return 1; /* SWW */
|
||
return 0; /* SWS */
|
||
}
|
||
|
||
/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */
|
||
static unsigned long rnd_seed;
|
||
|
||
static uint16_t prandom(uint16_t maxv)
|
||
{
|
||
rnd_seed = rnd_seed * 214013UL + 2531011UL;
|
||
return (uint16_t)((uint16_t)(rnd_seed >> 16) % (uint16_t)(maxv + 1));
|
||
}
|
||
|
||
/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */
|
||
static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
|
||
{
|
||
uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BL : RES_WALL_MARK_TL;
|
||
int lv2 = 0;
|
||
int xh = COL_XH[col];
|
||
int dby = 63 * row + 65;
|
||
if (dv > 3) lv2 = arg1 + 6;
|
||
else if (dv > 1) lv2 = arg2 + 6;
|
||
wall_b(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2, dby - LPOS[dv]);
|
||
}
|
||
|
||
static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
|
||
{
|
||
uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BR : RES_WALL_MARK_TR;
|
||
int a = (dv < 2) ? 24 : arg1 - 3;
|
||
int xh = COL_XH[col];
|
||
int dby = 63 * row + 65;
|
||
wall_b(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a, dby - RPOS[dv]);
|
||
}
|
||
|
||
/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */
|
||
static void wall_pattern(int row, int col, int which_part)
|
||
{
|
||
int xh = COL_XH[col];
|
||
int dby = 63 * row + 65;
|
||
unsigned long saved = rnd_seed;
|
||
uint16_t mid_div, mid_off, bot_div, bot_off;
|
||
uint8_t bg;
|
||
|
||
/* Верхняя полоса (row<0): SDLPoP рисует её как ряд 2 комнаты сверху
|
||
* (drawn_row=2 для seed, draw_main_y=-1 для позиции) — seed по row 2. */
|
||
rnd_seed = (unsigned long)(g_room + TBL_LINE[row < 0 ? 2 : row] + col);
|
||
prandom(1); /* сброс */
|
||
mid_div = prandom(1);
|
||
mid_off = prandom(4);
|
||
bot_div = prandom(1);
|
||
bot_off = prandom(4);
|
||
bg = wall_modifier(row, col);
|
||
|
||
if (bg == 3) { /* WWW */
|
||
if (which_part) {
|
||
if (prandom(4) == 0)
|
||
wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
|
||
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
|
||
}
|
||
wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
|
||
if (which_part) {
|
||
if (prandom(4) == 0)
|
||
draw_right_mark(prandom(3), mid_off, row, col);
|
||
if (prandom(4) == 0)
|
||
draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
|
||
}
|
||
} else if (bg == 0) { /* SWS */
|
||
if (which_part) {
|
||
if (prandom(6) == 0)
|
||
draw_left_mark(prandom(1), mid_off - mid_div, bot_off - bot_div, row, col);
|
||
}
|
||
} else if (bg == 1) { /* SWW */
|
||
if (which_part) {
|
||
if (prandom(4) == 0)
|
||
wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
|
||
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
|
||
if (prandom(4) == 0)
|
||
draw_right_mark(prandom(3), mid_off, row, col);
|
||
if (prandom(4) == 0)
|
||
draw_left_mark(prandom(3), mid_off - mid_div, bot_off - bot_div, row, col);
|
||
}
|
||
} else { /* WWS (bg==2) */
|
||
if (which_part)
|
||
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
|
||
wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
|
||
if (which_part) {
|
||
if (prandom(4) == 0)
|
||
draw_right_mark((uint16_t)(prandom(1) + 2), mid_off, row, col);
|
||
if (prandom(4) == 0)
|
||
draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
|
||
}
|
||
}
|
||
rnd_seed = saved;
|
||
}
|
||
|
||
/* topright тайла снизу-слева (seg008.c:410). */
|
||
static void draw_topright(uint8_t tt, int x, int dby)
|
||
{
|
||
if (tt == 7 || tt == 12) return;
|
||
if (tt == 20) { wall_b(2, x, dby); return; }
|
||
if (tile_table[tt].topright_id)
|
||
env_b(tile_table[tt].topright_id, x, dby);
|
||
}
|
||
|
||
/* Решётка ворот (portcullis) из ЛЕВОЙ комнаты (seg008.c:1110). */
|
||
static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
|
||
{
|
||
int x = xh * 8;
|
||
int gate_top_y = dby - 62;
|
||
int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
|
||
int gate_bot_y = dmy - openness;
|
||
int yb, gate_frame;
|
||
|
||
if (gate_bot_y + 12 < dmy)
|
||
env_b(50, x, gate_bot_y);
|
||
else
|
||
env_b(51, x, gate_bot_y - 2);
|
||
yb = gate_bot_y - 12;
|
||
if (yb < 192) {
|
||
while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) {
|
||
env_b(52, x, yb);
|
||
yb -= 8;
|
||
}
|
||
}
|
||
gate_frame = yb - gate_top_y + 1;
|
||
if (gate_frame > 0 && gate_frame < 9)
|
||
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];
|
||
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};
|
||
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)
|
||
{
|
||
uint8_t code = tile_code(row, col);
|
||
uint8_t lcode = tile_code(row, col - 1);
|
||
uint8_t lmod = tile_mod(row, col - 1);
|
||
/* Нажатая кнопка (seg008 get_tile_to_draw): при таймере связи >1 opener
|
||
* (0x0F) рисуется как ПОЛ (modif 0), closer (0x06) — как stuck (0x05).
|
||
* Модификатор кнопки = индекс LINKLOC. И для самого тайла, и для левого
|
||
* соседа (его правая грань). */
|
||
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(lmod) & 0x1F) > 1) { lcode = 1; lmod = 0; } }
|
||
else if (lcode == 0x06) { if ((pop_doorlink2(lmod) & 0x1F) > 1) lcode = 5; }
|
||
int xh = COL_XH[col];
|
||
int x = xh * 8;
|
||
int dby = 63 * row + 65;
|
||
int dmy = dby - 3;
|
||
const piece *t = &tile_table[code];
|
||
const piece *lt = &tile_table[lcode];
|
||
uint8_t rbl_code;
|
||
uint8_t wmod;
|
||
|
||
/* тайл снизу-слева (для 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) {
|
||
draw_topright(rbl_code, x, dby);
|
||
/* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op
|
||
* (чёрный силуэт по чёрному) — не рисуем. Если tile_left имеет
|
||
* floor_right: lt->floor_right. */
|
||
}
|
||
|
||
/* --- draw_tile_right (seg008.c:456) --- */
|
||
if (code != 20) {
|
||
if (lcode == 20) {
|
||
wall_b(1, x, tile_table[20].right_y + dmy); /* грань стены */
|
||
} else if (lcode == 0) {
|
||
if (lmod <= 3 && BLUELINE_FRAM1[lmod])
|
||
env_b(BLUELINE_FRAM1[lmod], x, BLUELINE_FRAM_Y[lmod] + dmy);
|
||
} else if (lcode == 1) {
|
||
uint8_t num;
|
||
env_b(42, x, tile_table[1].right_y + dmy); /* правый треуг. пола */
|
||
num = (lmod <= 3) ? lmod : 0;
|
||
if (num != 0) /* dungeon: level_type=0 */
|
||
env_b(BLUELINE_FRAM3[num], x, dmy - 20); /* силуэт кладки */
|
||
} else {
|
||
if (lt->right_id)
|
||
env_b(lt->right_id, x, lt->right_y + dmy);
|
||
/* Основание факела (seg008:489): рисует ПРАВЫЙ сосед. */
|
||
if (lcode == 19 || lcode == 30)
|
||
env_b(146, x, dby - 28);
|
||
/* stripe — только палас, dungeon пропускает */
|
||
}
|
||
}
|
||
|
||
/* --- draw_tile_anim_right (seg008.c:530) — статичные части. Для
|
||
* верхней полосы (row<0, draw_tile_aboveroom) НЕ рисуется + защита от
|
||
* отрицательного индекса в pop_loose_modif. --- */
|
||
if (row >= 0) {
|
||
if (lcode == 4) { /* gate: решётка */
|
||
draw_gate_back(lmod, xh, dby, dmy);
|
||
} 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])
|
||
env_b(SPIKES_FRAM_RIGHT[sf], x, dmy - 7);
|
||
}
|
||
/* Пламя факела (seg008:560, chtab_1 id modifier+1) в фон НЕ запекаем:
|
||
* оно анимируется каждый кадр, а heal вернул бы запечённый кадр из-под
|
||
* нового. Рисует pop_torch_draw (динамика, pop_process_trobs). */
|
||
}
|
||
|
||
/* --- draw_tile_bottom (seg008.c:570) --- */
|
||
if (code == 20) {
|
||
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_loose (seg008.c:600) — нижняя грань (анимир. по modif). В
|
||
* верхней полосе (row<0) это ПЛИТА-ПОТОЛОК (loose ряда 2 комнаты сверху):
|
||
* её состояние живёт отдельно, в pop_ceil_modif[col] (pop_map). --- */
|
||
if (code == 11)
|
||
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) — только нижние/правые грани. --- */
|
||
if (row >= 0) {
|
||
uint8_t base_id = t->base_id;
|
||
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);
|
||
}
|
||
|
||
/* --- draw_tile_anim (seg008.c:0B2B) — левый кадр выдвинутых пик в
|
||
* СВОЕЙ ячейке (анимир. по своему modif). Без него остриё пики
|
||
* рисуется только правым соседом со сдвигом → шахта над самой пикой
|
||
* пустая. */
|
||
if (row >= 0 && code == 2) {
|
||
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);
|
||
}
|
||
|
||
/* --- draw_tile_fore (seg008.c:690) --- */
|
||
if (code == 20) {
|
||
wmod = wall_modifier(row, col);
|
||
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */
|
||
wall_pattern(row, col, 1);
|
||
}
|
||
if (code == 10) { /* зелье: chtab_1, не env fore_id.
|
||
* Пузырёк — динамика (pop_potion_draw). */
|
||
potion_flask(row, col, tile_mod(row, col));
|
||
} else if (t->fore_id) {
|
||
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);
|
||
}
|
||
|
||
/* Перерисовать пики тайла (row,col) на back-странице по ЖИВОМУ modif
|
||
* (pop_trob). Кадр выдвижения рисует ПРАВЫЙ сосед (draw_tile ветка lcode==2
|
||
* читает lmod = g_bg[row*10+col] = живой modif пики). g_bg должен указывать
|
||
* на room_modif (enter_room передаёт его как bg). Порт redraw_21h/draw_tile.
|
||
* heal берёт печёный статич. фон, поверх — динамич. кадр (шипы над полом). */
|
||
void pop_spike_redraw(int row, int col)
|
||
{
|
||
int x = COL_XH[col] * 8;
|
||
int dmy = 63 * row + 62; /* как в draw_tile (dby-3) */
|
||
uint8_t sm = g_bg[row * 10 + col]; /* живой modif пики (room_modif) */
|
||
uint8_t sf = (sm & 0x80) ? 5 : sm; /* видимый кадр (hold 0x80..0x8F → 5) */
|
||
/* Шипы выдвигаются выше линии пола (env_b на dmy-7) — heal с запасом
|
||
* вверх (63*row+34 .. +74) покрывает и остриё, и сам пол; ширина 64 =
|
||
* тайл + правый сосед (остриё рисуется в его ячейке).
|
||
* heal вернул из ОЗУ-копии ВЕСЬ печёный статичный фон (база пик 127, пол,
|
||
* грани соседей — всё, что рисовал бы полный draw_tile при modif=0).
|
||
* Единственное, что реально анимируется — два спрайта острия (LEFT в своей
|
||
* ячейке + RIGHT в соседней, оба по sf). Поэтому вместо 2×draw_tile
|
||
* (десятки блитов статики поверх идентичного фона = идемпотентно) рисуем
|
||
* РОВНО эти два острия — пиксель-в-пиксель тот же результат, во много раз
|
||
* дешевле (порт redraw_21h: draw_tile_anim + draw_tile_anim_right). */
|
||
heal_off(x, 63 * row + 34, 64, 40);
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
if (sf > 0 && sf < 10) {
|
||
if (SPIKES_FRAM_LEFT[sf]) /* остриё в своей ячейке */
|
||
env_b(SPIKES_FRAM_LEFT[sf], x, dmy - 2);
|
||
if (col + 1 < 10 && SPIKES_FRAM_RIGHT[sf]) /* остриё в ячейке соседа */
|
||
env_b(SPIKES_FRAM_RIGHT[sf], COL_XH[col + 1] * 8, dmy - 7);
|
||
}
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
}
|
||
|
||
/* Перерисовать ЛЕВЫЙ шов (col0) на back-странице по живому modif соседа —
|
||
* ворота соседней комнаты слева (portcullis room8) видны в col0 текущей
|
||
* комнаты через draw_gate_back (draw_tile ветка lcode==4, lmod = g_lbg[0]).
|
||
* g_lbg (lcol_bg) должен указывать на живой modif (roomtest обновляет из
|
||
* room_modif соседа перед вызовом). Bar-в-чёрный + redraw (как loose bake):
|
||
* heal вернул бы ЗАПЕЧЁННОЕ состояние ворот → при открытии нижние бары
|
||
* оставались бы; чёрная плита стирает старые бары в VRAM+ОЗУ-копию. */
|
||
void pop_room_redraw_seam_left(void)
|
||
{
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
setfillstyle(SOLID_FILL, BLACK);
|
||
/* Полосу кладки У ПОТОЛКА (ряд −1) НЕ трогаем: её низ на room-space y=2,
|
||
* а бары ворот начинаются с y=3 (gate_top_y = dby−62). Раньше bar шёл от
|
||
* POP_YOFF и стирал потолок, из-за чего приходилось перерисовывать ещё и
|
||
* draw_tile(-1,0) — вдвое дороже на КАЖДОМ кадре анимации решётки. */
|
||
bar(0, POP_YOFF + 3, 31, POP_YOFF + 64); /* col0, ряд 0 (бары ворот) */
|
||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||
draw_tile(0, 0); /* фон col0 + бары ворот (живой modif) */
|
||
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);
|
||
}
|
||
|
||
/* ---- Анимация факела и зелья (динамика, каждый кадр) ---------------- *
|
||
* Оба кадра рисуются ПОВЕРХ статического фона и стираются heal'ом своей
|
||
* области (фон запечён БЕЗ них). Факел: пламя chtab_1 id modifier+1 в
|
||
* ячейке ПРАВОГО соседа (seg008:560: xh = draw_xh + 1, ybottom =
|
||
* draw_main_y − 40). Зелье: маска (23, «прорезает» фон) + кадр пузырька
|
||
* potion_fram_bubb[modif & 7] на xh+3/xl=1, ybottom = draw_main_y −
|
||
* (pot_size<<2) − 14 (seg008:668). */
|
||
void pop_torch_draw(int row, int col, uint8_t modif)
|
||
{
|
||
/* Пламя рисует ПРАВЫЙ сосед со своим draw_xh: xh = COL_XH[col+1] + 1
|
||
* (seg008:560), т.е. x = (col+1)*32 + 8 — как и основание факела (env
|
||
* 146), которое тоже лежит в ячейке соседа. */
|
||
int x = (col + 1 < 10 ? COL_XH[col + 1] : COL_XH[col] + 4) * 8 + 8;
|
||
int yb = 63 * row + 62 - 40;
|
||
if (modif >= 9) return;
|
||
heal_off(x - 2, yb - 19, 22, 22);
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
pot_b((uint8_t)(modif + 1), x, yb);
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
}
|
||
|
||
void pop_potion_draw(int row, int col, uint8_t modif)
|
||
{
|
||
uint8_t type = potion_type(modif);
|
||
int pot_size = (type >= 2 && type < 5) ? 1 : 0;
|
||
int x = (COL_XH[col] + 3) * 8 + 1;
|
||
int yb = 63 * row + 62 - (pot_size << 2) - 14;
|
||
if (type == 0) return;
|
||
heal_off(x - 1, yb - 10, 8, 12);
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
pot_b(POT_MASK_ID, x, yb); /* маска (под пузырьком) */
|
||
pot_b(POTION_FRAM_BUBB[modif & 7], x, yb); /* кадр пузырька */
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
}
|
||
|
||
/* Перерисовать тайл КНОПКИ (row,col) на back-странице: нажатая/отжатая
|
||
* (draw_tile подставляет пол/stuck по таймеру связи). Bar-в-чёрный +
|
||
* redraw — иначе «шишка» отжатой кнопки (base 147) осталась бы при переходе
|
||
* в пол. Звать из pop_process_trobs для кнопок текущей комнаты. */
|
||
void pop_floor_bake(int row, int col)
|
||
{
|
||
int x = COL_XH[col] * 8;
|
||
int yb = POP_YOFF + 63 * row;
|
||
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
||
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
||
* (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
setfillstyle(SOLID_FILL, BLACK);
|
||
bar(x, yb + 26, x + 59, yb + 64);
|
||
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);
|
||
}
|
||
|
||
void pop_button_redraw(int row, int col) { pop_floor_bake(row, col); }
|
||
|
||
/* Запечь ПУСТОТУ на месте упавшего 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 падает */
|
||
static uint8_t mob_clean; /* кадров дочистки после конца полёта (2 = обе стр.) */
|
||
static uint8_t mob_defer; /* кусок ниже Kid → рисуем ПОСЛЕ него (y-порядок) */
|
||
static int mob_x; /* левый край (COL_XH*8) */
|
||
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 = 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)
|
||
* с 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) */
|
||
|
||
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
|
||
* должно утечь в новую комнату). */
|
||
/* Тайл ОБЪЕКТА 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)
|
||
{
|
||
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;
|
||
/* 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,
|
||
* заходит в тайл СПРАВА) на obj_y-1. */
|
||
gfx_set_bank(GFX_BANK_SPRITE);
|
||
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; пока плита у уровня пола,
|
||
* её правый край ПРЯЧЕТСЯ за передней гранью пола. Перерисовываем
|
||
* сосед-тайл В ТОМ ЖЕ КАДРЕ поверх куска (иначе край торчит на полу
|
||
* до heal'а следующего кадра — виден как хвост во время падения). */
|
||
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 - 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 кадра (обе страницы дабл-
|
||
* буфера). Узкий 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)
|
||
{
|
||
int row, col;
|
||
g_room = room_num;
|
||
g_fg = fg; g_bg = bg;
|
||
g_lfg = lcol_fg; g_lbg = lcol_bg;
|
||
|
||
/* прозрачный блит: 0xFF не пишется (чёрный фон/нижние слои проступают) */
|
||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||
for (row = 2; row >= 0; row--) /* снизу вверх (2,1,0) */
|
||
for (col = 0; col < 10; col++)
|
||
draw_tile(row, col);
|
||
/* Полоса кладки у потолка (seg008 draw_room: доп. ряд из room_A, row2
|
||
* при draw_main_y=-1): нижние/правые грани ряда 2 комнаты сверху. Кадры
|
||
* высокие — верх ушёл бы в борт (за поле); клип по верхней границе поля
|
||
* (POP_YOFF) рисует ТОЛЬКО срез у потолка, без зачернения борта. */
|
||
g_clip_top = POP_YOFF;
|
||
for (col = 0; col < 10; col++)
|
||
draw_tile(-1, col);
|
||
g_clip_top = 0;
|
||
gfx_set_bank(GFX_BANK_NORMAL);
|
||
}
|
||
|
||
/* ---- fore-over-Kid (порт seg003 redraw_at_char + seg008 set_char_collision) --
|
||
* Передний слой (fore_id = foretable-кусок) тайлов ФУТПРИНТА спрайта Kid
|
||
* — рядов top..bottom × колонок left..right (<= 2×2 = 4 тайла) — рисуется
|
||
* ПОВЕРХ Kid: то, что по изометрии перед ним (передние грани колонн/ворот/
|
||
* большой колонны/щебня). Стены и факелы имеют fore_id=0 → остаются сзади.
|
||
* Рисуем в GFX_BANK_SPRITE (видео-ОЗУ, 0xFF прозрачен); kid_heal восстановит
|
||
* из теневого фона (fore в нём уже запечён pop_room_draw). obj_x — ЛОГИЧЕСКАЯ
|
||
* X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */
|
||
static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (58/14) */
|
||
{
|
||
int x = xpos - 58;
|
||
int xh = x / 14;
|
||
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
|
||
return (int8_t)xh;
|
||
}
|
||
|
||
static int8_t y_to_row(int y) /* y_to_row_mod4 (seg006, TILE_SIZEY=63) */
|
||
{
|
||
return (int8_t)((y + 60) / 63 % 4 - 1);
|
||
}
|
||
|
||
static void fore_only_tile(int row, int col)
|
||
{
|
||
uint8_t code = tile_code(row, col);
|
||
const piece *t;
|
||
/* Нажатая кнопка → пол/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;
|
||
if (code == 20) {
|
||
wall_b(WALL_FRAM_MAIN[wall_modifier(row, col)], x, dmy);
|
||
wall_pattern(row, col, 1);
|
||
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 (code == 10) { /* зелье: склянка (пузырёк — динамика) */
|
||
potion_flask(row, col, tile_mod(row, col));
|
||
} else 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
|
||
* подтягивается на floor-подобный тайл назначения (floor/pillar/stuck/torch),
|
||
* а тайл СЛЕВА пуст (т.е. кромка), передний край пола перекрывает нижнюю
|
||
* часть Kid — иначе видна часть, что должна быть за полом. Кадр подъёма
|
||
* 137..144 индексирует floor_left_overlay. */
|
||
static const uint8_t FLOOR_LEFT_OVERLAY[8] = {32, 151, 151, 150, 150, 151, 32, 32};
|
||
|
||
static void climb_overlay_tile(int row, int col, uint8_t fidx)
|
||
{
|
||
uint8_t code = tile_code(row, col);
|
||
int x, dmy, dby;
|
||
const piece *t;
|
||
if (code != 1 && code != 3 && code != 5 && code != 19) return; /* не floor-тайл */
|
||
if (tile_code(row, col - 1) != 0) return; /* слева не пусто → не кромка */
|
||
x = COL_XH[col] * 8;
|
||
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);
|
||
uint8_t left;
|
||
/* Пустой тайл НЕ окклюдирует: у него нет тела пола/стены, а draw_tile для
|
||
* него рисует лишь ФОНОВУЮ сетку (BLUELINE «силуэт кладки» — правая грань
|
||
* пустого соседа). В оригинале она уходит в y-сортируемый midtable и
|
||
* ложится ПОЗАДИ Kid; у нас draw_tile кладёт её прямо поверх → сетка на
|
||
* висящем/падающем 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) { 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,
|
||
int8_t dir, uint8_t frame, uint8_t action)
|
||
{
|
||
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, rTraw;
|
||
if (dir >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
|
||
xr = xl + wh;
|
||
cLraw = col_from_x(xl); /* до клампа: <0 = футпринт за левым швом */
|
||
cL = cLraw; if (cL < 0) cL = 0; else if (cL > 9) cL = 9;
|
||
cR = col_from_x(xr); if (cR < 0) cR = 0; else if (cR > 9) cR = 9;
|
||
/* Порт set_char_collision (seg006:0723): нижний ряд = y_to_row_mod4(obj_y),
|
||
* причём обёртка -1 (Kid НИЖЕ комнаты — climbdown/провал, obj_y в полосе
|
||
* «ряд 3») трактуется как ряд 3, НЕ ряд 0. Верхний ряд считается от
|
||
* char_top_y с ресетом в 0, если весь спрайт ниже комнаты (>=192). */
|
||
{
|
||
int top_y = obj_y - (int)h + 1;
|
||
if (top_y >= 192) top_y = 0;
|
||
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 (передняя грань пола, за которой нога). */
|
||
if (rB < 0) rB = 0; else if (rB > 2) rB = 2;
|
||
if (rT < 0) rT = 0; else if (rT > 2) rT = 2;
|
||
/* Голова/руки почти всегда торчат в ряд НАД опорным (Kid выше тайла).
|
||
* y_to_row(верх спрайта) из-за +60-сдвига может схлопнуться в опорный
|
||
* ряд (rT==rB) — тогда ряд над ним не попадёт в перебор и его окклюдер
|
||
* (нижняя грань стены / кромка пола) не перерисуется поверх 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 восстановит */
|
||
/* Шовная окклюзия ворот (порт redraw_at_char для шва): футпринт зашёл за
|
||
* ЛЕВЫЙ шов (cLraw<0) и у соседа слева в этом ряду решётка ворот (код 4) —
|
||
* перерисовать её бары ПОВЕРХ Kid. Иначе Kid, стоя за закрытыми воротами
|
||
* (col-1), рисуется поверх решётки. Бары = draw_gate_back по живому modif
|
||
* соседа (g_lbg = lcol_bg, openness). */
|
||
if (cLraw < 0 && g_lfg)
|
||
for (r = rT; r <= rB; r++)
|
||
if ((g_lfg[r] & 0x1F) == 4) {
|
||
int dby = 63 * r + 65;
|
||
draw_gate_back(g_lbg ? g_lbg[r] : 0, COL_XH[0], dby, dby - 3);
|
||
}
|
||
if (frame >= 137 && frame <= 144) { /* подъём: пол назначения поверх Kid */
|
||
uint8_t fidx = (uint8_t)(frame - 137);
|
||
for (r = rT; r <= rB; r++)
|
||
for (c = cL; c <= cR; c++)
|
||
climb_overlay_tile(r, c, fidx);
|
||
} else {
|
||
/* redraw_at_char2 (seg003:449) — путь set_redraw2/draw_other_overlay:
|
||
* тело пола на кромке перекрывает Kid в позах захвата (78-79), виса
|
||
* (action 2/6), полёта (3/4) и старта падения (bumped 102-106).
|
||
* Верхний ряд — всегда; опорный — кроме action 2 (hang_climb). */
|
||
uint8_t do_ov = 0;
|
||
if (frame >= 78 && frame < 80) do_ov = 1; /* захват */
|
||
else if (action == 2 || action == 3 || action == 4 || action == 6) do_ov = 1;
|
||
else if (action == 5 && frame >= 102 && frame <= 106) do_ov = 1;
|
||
else if (frame == 135 || frame == 136) do_ov = 1; /* начало подъёма (SEQ_UP */
|
||
/* на 141 — до него Kid ещё */
|
||
/* в ряду 1 ПОД полом [0,3]) */
|
||
if (do_ov) {
|
||
for (c = cR; c >= cL; c--) {
|
||
if (action != 2) other_overlay_tile(rB, c); /* опорный ряд */
|
||
if (rT != rB) other_overlay_tile(rT, c); /* верхний ряд */
|
||
}
|
||
}
|
||
}
|
||
/* 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);
|
||
}
|