Композит куска: точный габарит + ИСПРАВЛЕНИЕ размеров в b2da0b8
ПОПРАВКА К b2da0b8. Там я снял размеры частей падающего куска из каталогов
атласов с НЕВЕРНЫМ сдвигом раскладки (page = id>>5 вместо id>>4 —
POP_ENV_SHIFT равен 4). Настоящие размеры:
оба тайлсета 70 = 32x13 74 = 32x3
72 = 26x16 в подземелье, 25x16 во дворце
То есть прежний комментарий в mob_render (32x13 / 32x3 / 26x16) был ВЕРЕН, а
«исправление» — нет. Следствия:
- НИКАКОГО БАГА КОРИДОРА НЕ БЫЛО: след куска по x — mob_x .. mob_x+57, и
старый коридор mob_x-4 .. mob_x+59 его покрывал. Заявление про «три
пикселя, не стиравшиеся во дворце» неверно, снимаю.
- Сам коридор оставляю как стало (mob_x .. mob_x+63): площадь та же, но
четыре пикселя запаса переехали слева, где кусок не рисует ничего, вправо,
где их было всего два. Это не исправление бага, а перекладка запаса.
- КОД во всех случаях работал правильно: он читает раскладку через
POP_ENV_SHIFT/POP_ENV_MASK, ошибка была только в моём анализе.
Само дело: композит собирается с ТОЧНЫМ габаритом вместо буфера-максимума.
Габариты частей читаются первым проходом (у тайлсетов правая часть разная),
из них считаются ширина и высота блоба, и страйд равен ширине. Раньше блоб
объявлялся 63 px шириной при фактических 58 — пять прозрачных колонок
переносились на каждом кадре каждого куска.
циан пик 273 108 -> 270 510
работа пик 920 862 -> 916 458
Проверено: 8 наборов tests-host зелёные; кадр с четырьмя плитами в воздухе
совпадает пиксельно с прежним.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -111,81 +111,74 @@ static int bg_load_tile_pal(const char *name)
|
||||
static uint8_t mob_heal_up = 20;
|
||||
static uint8_t mob_heal_h = 24;
|
||||
|
||||
#define MOBS_MAXW 63
|
||||
#define MOBS_MAXH 19
|
||||
/* Размер буфера — верхняя граница по обоим тайлсетам с запасом; фактические
|
||||
* w/h композита считаются при сборке и кладутся в его шапку. */
|
||||
#define MOBS_MAXW 64
|
||||
#define MOBS_MAXH 20
|
||||
static uint8_t mob_spr[4 + MOBS_MAXW * MOBS_MAXH];
|
||||
static uint8_t mob_spr_ok; /* 0 — не собрался, рисуем тремя блитами */
|
||||
|
||||
/* Одна часть в композит. xo — смещение по x, ybo — смещение НИЗА части
|
||||
* относительно низа композита (0 = вплотную к низу, отрицательное = выше).
|
||||
* top_off — смещение верхней строки композита относительно mob_y. */
|
||||
static uint8_t mob_spr_put(uint8_t id, uint8_t xo, int8_t ybo, int8_t top_off)
|
||||
{
|
||||
atlas_t *a = &pop_env[id >> POP_ENV_SHIFT];
|
||||
uint8_t idx = (uint8_t)(id & POP_ENV_MASK);
|
||||
const uint8_t *src;
|
||||
uint8_t w, h, r, c, v;
|
||||
int row0;
|
||||
if (idx >= a->count) return 0;
|
||||
src = (const uint8_t *)atlas_image(a, idx);
|
||||
gfx_w0_map(a->page);
|
||||
w = src[0]; h = src[2];
|
||||
if (src[1] || src[3] || !w || !h ||
|
||||
(uint16_t)xo + w > MOBS_MAXW) { gfx_w0_unmap(); return 0; }
|
||||
row0 = (int)ybo - (int)h + 1 - (int)top_off; /* верхняя строка части */
|
||||
if (row0 < 0 || row0 + h > MOBS_MAXH) { gfx_w0_unmap(); return 0; }
|
||||
src += 4;
|
||||
for (r = 0; r < h; r++) {
|
||||
const uint8_t *sp = src + (uint16_t)r * w;
|
||||
uint8_t *dp = mob_spr + 4 + (uint16_t)(row0 + r) * MOBS_MAXW + xo;
|
||||
for (c = 0; c < w; c++) { v = sp[c]; if (v != 0xFF) dp[c] = v; }
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Высота композита: самый верхний пиксель даёт часть 70 (ybo −3), у неё же
|
||||
* самая большая высота. Считаем по фактическим габаритам, а не по константам
|
||||
* — они у тайлсетов разные. */
|
||||
static int8_t mob_spr_top_off(void)
|
||||
{
|
||||
static const uint8_t ids[3] = {74, 70, 72};
|
||||
static const int8_t ybo[3] = {0, -3, -1};
|
||||
int8_t best = 0;
|
||||
uint8_t k;
|
||||
for (k = 0; k < 3; k++) {
|
||||
atlas_t *a = &pop_env[ids[k] >> POP_ENV_SHIFT];
|
||||
uint8_t idx = (uint8_t)(ids[k] & POP_ENV_MASK);
|
||||
const uint8_t *src;
|
||||
int t;
|
||||
if (idx >= a->count) continue;
|
||||
src = (const uint8_t *)atlas_image(a, idx);
|
||||
gfx_w0_map(a->page);
|
||||
t = (int)ybo[k] - (int)src[2] + 1;
|
||||
gfx_w0_unmap();
|
||||
if (t < best) best = (int8_t)t;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
/* Части композита: id, смещение по x, смещение НИЗА части относительно mob_y.
|
||||
* Порядок — как в mob_render (низ, левая часть, правая), он важен: части
|
||||
* перекрываются. */
|
||||
static const uint8_t MOBS_ID[3] = {74, 70, 72};
|
||||
static const uint8_t MOBS_XO[3] = {0, 0, 32};
|
||||
static const int8_t MOBS_YBO[3] = {0, -3, -1};
|
||||
|
||||
static void mob_spr_build(void)
|
||||
{
|
||||
uint16_t i;
|
||||
int8_t top_off;
|
||||
uint8_t w[3], h[3], k, r, c, v, cw, ch;
|
||||
const uint8_t *src;
|
||||
int top = 0, row0;
|
||||
|
||||
mob_spr_ok = 0;
|
||||
top_off = mob_spr_top_off();
|
||||
if (top_off <= -MOBS_MAXH) return; /* не влезло — общий путь */
|
||||
mob_spr[0] = MOBS_MAXW; mob_spr[1] = 0;
|
||||
mob_spr[2] = (uint8_t)(1 - top_off); /* низ композита = mob_y */
|
||||
mob_spr[3] = 0;
|
||||
for (i = 0; i < (uint16_t)MOBS_MAXW * MOBS_MAXH; i++) mob_spr[4 + i] = 0xFF;
|
||||
/* Порядок как в mob_render: низ, левая часть, правая часть. */
|
||||
if (!mob_spr_put(74, 0, 0, top_off)) return;
|
||||
if (!mob_spr_put(70, 0, -3, top_off)) return;
|
||||
if (!mob_spr_put(72, 32, -1, top_off)) return;
|
||||
/* Проход 1 — габариты частей. Считать их обязательно, а не брать
|
||||
* константами: у тайлсетов они РАЗНЫЕ (напр. правая часть 72 — 26 px в
|
||||
* подземелье и 25 во дворце). */
|
||||
for (k = 0; k < 3; k++) {
|
||||
atlas_t *a = &pop_env[MOBS_ID[k] >> POP_ENV_SHIFT];
|
||||
uint8_t idx = (uint8_t)(MOBS_ID[k] & POP_ENV_MASK);
|
||||
if (idx >= a->count) return;
|
||||
src = (const uint8_t *)atlas_image(a, idx);
|
||||
gfx_w0_map(a->page);
|
||||
w[k] = src[0]; h[k] = src[2];
|
||||
v = (uint8_t)(src[1] | src[3]);
|
||||
gfx_w0_unmap();
|
||||
if (v || !w[k] || !h[k]) return; /* кадр >255 или пустой */
|
||||
row0 = (int)MOBS_YBO[k] - (int)h[k] + 1;
|
||||
if (row0 < top) top = row0;
|
||||
}
|
||||
/* Габарит композита: низ = mob_y, верх = самая высокая часть. */
|
||||
ch = (uint8_t)(1 - top);
|
||||
cw = 0;
|
||||
for (k = 0; k < 3; k++)
|
||||
if ((uint8_t)(MOBS_XO[k] + w[k]) > cw) cw = (uint8_t)(MOBS_XO[k] + w[k]);
|
||||
if (cw > MOBS_MAXW || ch > MOBS_MAXH) return;
|
||||
mob_spr[0] = cw; mob_spr[1] = 0;
|
||||
mob_spr[2] = ch; mob_spr[3] = 0;
|
||||
/* Пустое место — ПРОЗРАЧНОЕ (0xFF), иначе композит закрасил бы фон вокруг
|
||||
* частей. Страйд блоба = его ширина, поэтому чистим ровно cw*ch. */
|
||||
for (r = 0; r < ch; r++)
|
||||
for (c = 0; c < cw; c++) mob_spr[4 + (uint16_t)r * cw + c] = 0xFF;
|
||||
/* Проход 2 — копирование ПОПИКСЕЛЬНО с пропуском 0xFF: части
|
||||
* ПЕРЕКРЫВАЮТСЯ (74 и 70 обе идут от mob_x), и прозрачные пиксели верхней
|
||||
* не должны стирать нижнюю — ровно как три прозрачных блита друг поверх
|
||||
* друга. */
|
||||
for (k = 0; k < 3; k++) {
|
||||
atlas_t *a = &pop_env[MOBS_ID[k] >> POP_ENV_SHIFT];
|
||||
src = (const uint8_t *)atlas_image(a, (uint8_t)(MOBS_ID[k] & POP_ENV_MASK));
|
||||
gfx_w0_map(a->page);
|
||||
row0 = (int)MOBS_YBO[k] - (int)h[k] + 1 - top;
|
||||
for (r = 0; r < h[k]; r++) {
|
||||
const uint8_t *sp = src + 4 + (uint16_t)r * w[k];
|
||||
uint8_t *dp = mob_spr + 4 + (uint16_t)(row0 + r) * cw + MOBS_XO[k];
|
||||
for (c = 0; c < w[k]; c++) { v = sp[c]; if (v != 0xFF) dp[c] = v; }
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
}
|
||||
/* Коридор heal — по композиту: две строки запаса сверху, одна снизу. */
|
||||
mob_heal_up = (uint8_t)(mob_spr[2] + 1);
|
||||
mob_heal_h = (uint8_t)(mob_spr[2] + 3);
|
||||
mob_heal_up = (uint8_t)(ch + 1);
|
||||
mob_heal_h = (uint8_t)(ch + 3);
|
||||
mob_spr_ok = 1;
|
||||
}
|
||||
|
||||
@@ -1040,25 +1033,24 @@ static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25};
|
||||
/* Габарит падающего куска. Части и их места (порт draw_mob, seg007:13E5;
|
||||
* индексы loose_fram_*[10], seg008:518/596/608):
|
||||
*
|
||||
* env 70 левая часть при mob_y-3
|
||||
* env 74 низ при mob_y
|
||||
* env 72 правая часть при mob_x+32, mob_y-1
|
||||
* env 70 левая часть при (mob_x, mob_y-3)
|
||||
* env 74 низ при (mob_x, mob_y)
|
||||
* env 72 правая часть при (mob_x+32, mob_y-1)
|
||||
*
|
||||
* РАЗМЕРЫ У ТАЙЛСЕТОВ РАЗНЫЕ (снято из каталогов атласов 2026-08-17; прежний
|
||||
* комментарий «41=32x13, 42=26x15, 43=32x3» был неверен вдвойне — и id, и
|
||||
* размеры):
|
||||
* Размеры (сняты из каталогов атласов; РАСКЛАДКА env: page = id>>4,
|
||||
* idx = id&15 — POP_ENV_SHIFT равен 4, не 5):
|
||||
*
|
||||
* подземелье 70 = 32x16 74 = 26x15 72 = 26x16
|
||||
* дворец 70 = 32x13 74 = 25x15 72 = 31x13
|
||||
* оба тайлсета 70 = 32x13 74 = 32x3
|
||||
* 72 = 26x16 в подземелье, 25x16 во дворце
|
||||
*
|
||||
* Отсюда фактический след: по x mob_x .. mob_x+62 (дворец: 32+31),
|
||||
* по y mob_y-18 .. mob_y (подземелье: 3+16-1).
|
||||
* Отсюда след: по x mob_x .. mob_x+57 (32+26−1), по y mob_y-16 .. mob_y
|
||||
* (правая часть выше всех: 1+16−1). Именно эти числа и стоят в шапке
|
||||
* mob_render — composite их пересчитывает сам и кладёт в шапку блоба.
|
||||
*
|
||||
* КОРИДОР ИСПРАВЛЕН 2026-08-17: было MOB_X0 = mob_x-4 при MOB_W = 64, то есть
|
||||
* mob_x-4 .. mob_x+59 — на ДВОРЦОВЫХ уровнях (10, 11, 13, 14) правые три
|
||||
* пикселя куска 72 не попадали в heal и оставались висеть на странице.
|
||||
* Четыре пикселя слева при этом чистились впустую: левее mob_x кусок не
|
||||
* рисует ничего. */
|
||||
* Коридор: MOB_X0 = mob_x при MOB_W = 64 даёт mob_x .. mob_x+63, то есть
|
||||
* шесть пикселей запаса справа. Раньше было mob_x-4 при той же ширине
|
||||
* (mob_x-4 .. mob_x+59): четыре пикселя слева, где кусок не рисует ничего,
|
||||
* и всего два справа. Площадь та же, запас переехал туда, где он нужен. */
|
||||
#define MOB_X0(mx) (mx)
|
||||
#define MOB_W 64 /* mob_x .. mob_x+63 */
|
||||
/* Коридор heal по высоте: mob_y-20 .. mob_y+3 при следе mob_y-18 .. mob_y.
|
||||
@@ -1484,7 +1476,8 @@ static void mob_render(mob_t *m, uint8_t pg)
|
||||
* Здесь стояли 41/43/42 — это индекс 0, то есть плита В ПОКОЕ. Отсюда и
|
||||
* «цельная ровная плита» вместо двух частей со сдвигом правой половины
|
||||
* на пиксель (замечено пользователем 2026-08-13 сравнением с оригиналом).
|
||||
* Габариты частей — в блоке MOB_X0 выше (у тайлсетов они РАЗНЫЕ).
|
||||
* Габариты частей — в блоке MOB_X0 выше (правая часть у тайлсетов
|
||||
* РАЗНАЯ: 26 px в подземелье, 25 во дворце).
|
||||
*
|
||||
* Рисуем ОДНИМ блитом по композиту (mob_spr_build): три вызова блита
|
||||
* стоили 258 708 такта в кадре при шести кусках — больше половины
|
||||
@@ -1494,7 +1487,7 @@ static void mob_render(mob_t *m, uint8_t pg)
|
||||
* (MOB_X0 .. +MOB_W, y−27 .. +37) уже помечен ОДНИМ вызовом — из
|
||||
* pop_loose_mob_tick для летящих кусков и из mob_spawn_copy для куска,
|
||||
* рождённого внутри тика. Спрайт целиком внутри коридора: по x он
|
||||
* занимает m->x .. m->x+62, по y m->y−18 .. m->y, коридор — m->x ..
|
||||
* занимает m->x .. m->x+57, по y m->y−16 .. m->y, коридор — m->x ..
|
||||
* m->x+63 и m->y−27 .. m->y+37 (запас на ускорение, speed ≤ 31 за кадр).
|
||||
* Замер 2026-08-17: три настоящих pop_cd_touch стоили 13 506 тактов на
|
||||
* кусок, то есть 81 000 в кадре при шести кусках. */
|
||||
@@ -1510,7 +1503,7 @@ static void mob_render(mob_t *m, uint8_t pg)
|
||||
* Окно ставим только когда кусок реально задевает борт: внутри поля блиты
|
||||
* идут быстрым путём (10 422 против 14 088). */
|
||||
{
|
||||
int ytop = m->y - 18 + POP_YOFF, ybot = m->y + POP_YOFF;
|
||||
int ytop = m->y - 16 + POP_YOFF, ybot = m->y + POP_YOFF;
|
||||
uint8_t clipped = (uint8_t)(ytop < POP_YOFF || ybot >= POP_CLIP_BOTTOM);
|
||||
if (clipped) pop_t_win_set(0, POP_YOFF, 320, POP_PLAYFIELD_H);
|
||||
if (mob_spr_ok) {
|
||||
|
||||
Reference in New Issue
Block a user