PoP roomtest: clip_char (верхняя обрезка спрайта) + подстановка нажатой кнопки
Спуск Кида с кнопки (room8, кромка (0,6)) рисовался неверно: Кид просвечивал в щель между кнопкой и ближним столбом, а ближняя рука была срезана до одного оторванного пикселя. Две независимые причины. 1. Не был портирован clip_char() (seg006:1749) — оригинал перед add_objtable обрезает спрайт персонажа сверху по y_clip[curr_row+1], когда тайл над головой стена или пол. Порт: pop_clip_char_top() (pop_map.c, метрики по set_char_collision) + новый примитив gfx_blit_cols_part() в libbgi (блит column-major с пропуском skip верхних строк; обрезка сверху бесплатна — колонка непрерывна в ОЗУ, сдвигается только старт). gfx_blit_cols стал тонкой обёрткой над ним. kid_heal чистит уже ОБРЕЗАННЫЙ прямоугольник, иначе стирается кромка пола над срезом. 2. climb_overlay_tile (порт draw_floor_overlay, seg008:1E3A) выбирал ветку по СЫРОМУ коду тайла, а get_tile_to_draw (seg008:240) подменяет нажатую кнопку на floor/stuck. Тайл-кнопка не проходил тест floor → уходил в draw_other_overlay, который кладёт поверх Кида ВЕСЬ тайл вместо узкой кромки floor_left_overlay[frame-137]. Фикс — tile_code_drawn(): одна подстановка на все слои (fore_only_tile/overlay_mid_tile перестали её дублировать, W3 −293 Б). Проверено покадрово в MAME (шаг gv + снимок): кадры спуска 148..138 чистые, после приземления на кнопке мусора нет. Заодно: gfx_heal_noclip() (пара к gfx_blit_noclip; heal 11658 -> 8982 тактов) и rest_pending в pop_trob — холостой проход по 30 тайлам только когда есть отложенные редрои пик/кнопок (pop_process_trobs 89-111К -> 70-92К тактов). START_ROOM временно = 8 (отладка спуска с кнопки), вернуть на 6/старт уровня. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,46 +16,14 @@
|
||||
* ПОЛНОЙ высоте (колонки в ОЗУ лежат full_h друг от друга), длина accel-
|
||||
* блока — по обрезанной. Спрайты Kid < 256 по обеим осям → нарезка на
|
||||
* полосы >256 не нужна (raw принимает uint8_t w,h; 0 => 256). img вне W3.
|
||||
*
|
||||
* Тело (вместе с клипом) — в gfx_blit_cols_part.c: этот вызов = «весь кадр,
|
||||
* без обрезки сверху».
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_blit_cols(int x, int y, const void *img, uint8_t flip)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)img;
|
||||
int full_w = p[0] | (p[1] << 8);
|
||||
int full_h = p[2] | (p[3] << 8);
|
||||
const uint8_t *px = p + 4;
|
||||
int w = full_w, h = full_h;
|
||||
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
|
||||
int sy = 0; /* пропуск строк сверху колонки */
|
||||
int cx = 0; /* пропуск колонок слева экрана */
|
||||
const uint8_t *src;
|
||||
uint8_t *dst;
|
||||
|
||||
if (full_w <= 0 || full_h <= 0)
|
||||
return;
|
||||
|
||||
/* Клип по экрану — только если хоть один край вылезает. */
|
||||
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
|
||||
if (x < 0) { cx = -x; w += x; x = 0; }
|
||||
if (y < 0) { sy = -y; h += y; y = 0; }
|
||||
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
|
||||
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
}
|
||||
|
||||
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка.
|
||||
* cx экранных колонок слева пропускаем шагом sstride (в флипе он
|
||||
* отрицателен — уходим к более ранним колонкам источника); sy строк
|
||||
* сверху — простым +sy внутри колонки (порядок строк флип не меняет). */
|
||||
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
|
||||
if (cx)
|
||||
src += cx * sstride;
|
||||
src += sy;
|
||||
|
||||
_bgi_begin();
|
||||
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
|
||||
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
|
||||
_bgi_end();
|
||||
gfx_blit_cols_part(x, y, img, flip, 0, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* gfx_blit_cols_part — блит спрайта column-major с ОБРЕЗКОЙ СВЕРХУ: рисуются
|
||||
* только строки [skip, skip+rows) кадра, и (x,y) — экранная позиция ПЕРВОЙ
|
||||
* нарисованной строки (как у gfx_blit_part: координата назначения под-
|
||||
* прямоугольника). rows <= 0 — «до низа кадра».
|
||||
*
|
||||
* Нужно для порта clip_char (PoP): персонаж, чья голова ушла под пол/стену,
|
||||
* рисуется обрезанным по линии пола (obj_clip_top), а не целиком. Обрезка
|
||||
* сверху для column-major бесплатна: колонка = непрерывный кусок ОЗУ, старт
|
||||
* просто сдвигается на skip байт, длина accel-блока уменьшается.
|
||||
*
|
||||
* Здесь же живёт полное тело блита колонками — gfx_blit_cols() тонкая
|
||||
* обёртка (skip=0, rows=0), чтобы не дублировать клип-логику.
|
||||
* Подробности зеркала/клипа — см. шапку gfx_blit_cols.c.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
||||
int skip, int rows)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)img;
|
||||
int full_w = p[0] | (p[1] << 8);
|
||||
int full_h = p[2] | (p[3] << 8);
|
||||
const uint8_t *px = p + 4;
|
||||
int w = full_w, h;
|
||||
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
|
||||
int sy; /* пропуск строк сверху колонки */
|
||||
int cx = 0; /* пропуск колонок слева экрана */
|
||||
const uint8_t *src;
|
||||
uint8_t *dst;
|
||||
|
||||
if (full_w <= 0 || full_h <= 0)
|
||||
return;
|
||||
if (skip < 0) skip = 0;
|
||||
if (skip >= full_h)
|
||||
return; /* обрезано целиком */
|
||||
sy = skip;
|
||||
h = full_h - skip;
|
||||
if (rows > 0 && rows < h)
|
||||
h = rows;
|
||||
|
||||
/* Клип по экрану — только если хоть один край вылезает. */
|
||||
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
|
||||
if (x < 0) { cx = -x; w += x; x = 0; }
|
||||
if (y < 0) { sy -= y; h += y; y = 0; }
|
||||
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
|
||||
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
}
|
||||
|
||||
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
|
||||
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
|
||||
if (cx)
|
||||
src += cx * sstride;
|
||||
src += sy;
|
||||
|
||||
_bgi_begin();
|
||||
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
|
||||
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* gfx_heal_noclip — восстановить прямоугольник экрана из ОЗУ-копии БЕЗ
|
||||
* клипа: вызывающий гарантирует, что прямоугольник целиком на экране,
|
||||
* w,h ≤ 255. Быстрый вариант gfx_heal для тайловых движков (пара к
|
||||
* gfx_blit_noclip).
|
||||
*
|
||||
* Зачем: gfx_heal идёт через общее ядро _gfx_heal_full (клип, нарезка
|
||||
* полос >256, 16-битная арифметика) — замер в MAME (PoP roomtest,
|
||||
* 2026-07-27): heal области 22×22 стоил 11 658 тактов при кадре 430 080,
|
||||
* т.е. цена почти вся в обвязке, а не в пикселях. Линейное спрайтовое
|
||||
* ядро _gfx_heal_sprite_noclip делает ту же работу без этого груза.
|
||||
*
|
||||
* Банк — как у gfx_heal: на время операции ВСЕГДА GFX_BANK_NORMAL
|
||||
* (чтение отдаёт ОЗУ-копию, запись должна дойти и до ОЗУ), затем
|
||||
* пользовательский банк восстанавливается. W3-скобка ставится ЗДЕСЬ:
|
||||
* из модуля, собранного с --w3, её вызывать нельзя (после _bgi_begin
|
||||
* окно W3 занято видеобанком и код вызывающего исчезает).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_heal_noclip(int x, int y, uint8_t w, uint8_t h)
|
||||
{
|
||||
uint8_t saved = gfx_get_bank();
|
||||
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
_bgi_begin();
|
||||
_gfx_heal_sprite_noclip(x, y, w, h);
|
||||
_bgi_end();
|
||||
gfx_set_bank(saved);
|
||||
}
|
||||
Reference in New Issue
Block a user