Files
Sprinter-SDCC/applications/PoP/roomtest/pop_kdraw.c
T
snark13 a6e39070af Фиксированный логический кадр по лучу + режимы FASTEST/FAST/NORMAL
Период стал max(n, ceil(W)) вместо ceil(W)+2: три gfx_wait_vsync после
работы отсчитывались от её КОНЦА, поэтому бюджет кадра был один растр.
Теперь ждём от якоря начала кадра, и при n=3 бюджет 1 290 240 тактов.

Счёт кадров — программный, по биту 5 порта 0xFE (положение луча), а не по
кадровым прерываниям: те теряются в DI-окнах акселератора фазозависимо
(замер: 0..2,8 %, на полной перерисовке три подряд).  Условие точности
одно — зазор между выборками меньше 64 512 тактов; точки выборки
расставлены по замеру, а не на глаз.

Режимы (pop_pace.h), клавиша P по кругу, дефолт FASTEST.  Условие боя
взято у оригинала буквально (SDLPoP seg003.c:363): Kid.sword ==
SWORD_2_DRAWN, а не «идёт бой».

Проверено в MAME на 11/15: счётчик без недосчёта на 270 кадров, период
ровно 3 растра на 302 логических кадрах (ни длиннее 3,1, ни короче 2,9),
NORMAL даёт ровно 4, с вынутым мечом — ровно 5.
2026-08-19 23:06:27 +03:00

144 lines
7.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* pop_kdraw.c — отрисовка ИЗ АТЛАСОВ КИДА, которая нужна только слою
* персонажей: клинок в руке (add_sword_to_objtable, seg006:1798) и блит
* спрайта chtab_2 по его id (полоса HP). БАНК 4, вместе с pop_cdraw.c.
*
* Почему здесь. Оба вызова приходят РОВНО из pop_cdraw.c и больше ниоткуда
* (проверено грепом), а лежали они в pop_kid.c — то есть в резиденте W1,
* съедая ~1.2 КБ дефицитного окна ради кода, который зовёт сосед по банку.
* Раз модуль в том же банке 4 — вызовы прямые, без трамплина: объявления в
* _pop_kdraw.h НЕ помечены __banked (см. там же условие корректности).
*
* Загрузка атласа меча (pop_sword_load) осталась публичной и __banked: её
* зовёт инициализация, один раз за игру.
*/
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
#include "pop_kid.h"
#include "_pop_kdraw.h"
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
#include "pop_guard.h" /* CHARID_* — клинок есть и у стража */
#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */
#include "_pop_draw.h" /* pop_onscreen_cols */
#include "pop_map.h" /* pop_upside — зелье инверсии */
#include "pop_vflip.h" /* зеркальные страницы атласов */
#include "kid_data.h"
/* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): клинок —
* ОТДЕЛЬНЫЙ спрайт поверх персонажа, один и тот же атлас у Кида и у стража.
* Таблица sword_tbl (kid_data.h, генерится из seg006) индексируется
* sword_frame = кадр.sword & 0x3F; id 255 = клинка в этом кадре нет.
* Смещения — в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ
* calc_screen_x_coord, x — «вперёд» по направлению персонажа. */
static const uint8_t SWORD_ID[KID_NSWORD] = KID_SWORD_ID_INIT;
static const int8_t SWORD_DX[KID_NSWORD] = KID_SWORD_DX_INIT;
static const int8_t SWORD_DY[KID_NSWORD] = KID_SWORD_DY_INIT;
static atlas_t swordp;
static uint8_t sword_ok;
int pop_sword_load(void) __banked
{
if (atlas_load(&swordp, "KID\\sword.atl") != 0) return -1;
sword_ok = 1;
return 0;
}
/* Зеркальная страница меча (зелье инверсии) — пара к pop_sword_load. */
int pop_sword_vflip_load(void) __banked
{
if (!sword_ok) return 0;
return pop_vflip_add(&swordp, "KID\\sword_v.atl");
}
/* add_sword_to_objtable (seg006:1798). Клинок рисуется, если персонаж в
* кадрах подъёма/ножен, ИЛИ держит меч, ИЛИ это ЖИВОЙ страж (у стража меч
* в руке всегда, даже пока sword ещё sheathed). Страницу W0 оставляем
* замапленной — вызывающий делает gfx_w0_unmap сам. */
uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr,
int obj_x, int obj_y,
int *rx, int *ry, uint16_t *rw, uint16_t *rh)
{
uint8_t sf, id, flip;
const uint8_t *simg;
uint16_t w, h;
int bx, top;
if (!sword_ok) return 0;
if (!((ch->frame >= 229 && ch->frame < 238) ||
ch->sword != 0 ||
((ch->charid == CHARID_2_GUARD || ch->charid == CHARID_4_SKELETON) &&
ch->alive < 0)))
return 0;
sf = (uint8_t)(fr->sword & 0x3F);
if (sf == 0 || sf >= KID_NSWORD) return 0;
id = SWORD_ID[sf];
if (id == 0xFF) return 0;
simg = (const uint8_t *)atlas_image(&swordp, id);
{ /* ПЕРЕВОРОТ: зеркальная копия страницы меча (см. pop_vflip.h) */
uint8_t vpg = swordp.page;
if (pop_upside) {
uint8_t f = pop_vflip_page(vpg);
if (f) vpg = f;
}
gfx_w0_map(vpg);
}
w = (uint16_t)(simg[0] | ((uint16_t)simg[1] << 8));
h = (uint16_t)(simg[2] | ((uint16_t)simg[3] << 8));
if (!w || !h) return 0;
flip = (uint8_t)(ch->direction >= 0);
bx = obj_x + ((ch->direction < 0) ? -(int)SWORD_DX[sf] : (int)SWORD_DX[sf]);
top = obj_y + (int)SWORD_DY[sf] - (int)h + 1;
/* Та же формула, что у самого персонажа (pop_cdraw.c): верх кадра
* [top, top+h) уезжает в 192 - top - h. */
if (pop_upside) top = POP_PLAYFIELD_H - top - (int)h;
if (flip) bx -= (int)w;
/* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507): клинок
* обрезается по нему так же, как сам персонаж. Без этого у падающего
* в пропасть скелета меч оставался виден НИЖЕ поля, на полосе HP. */
{
int skip = 0, rows = (int)h;
if (top < 0) { skip = -top; rows -= skip; top = 0; }
if (top + rows > 192) rows = 192 - top;
if (rows <= 0) return 0;
gfx_set_bank(GFX_BANK_SPRITE);
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
/* Клинок почти всегда целиком на экране — noclip-путь (см. gfx.h:
* подготовка клипающего варианта стоит ~5.6 К тактов на вызов). */
if (pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows))
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, simg, flip,
(uint8_t)skip, (uint8_t)rows);
else
gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, skip, rows);
gfx_set_bank(GFX_BANK_NORMAL);
*rx = bx; *ry = top; *rw = w; *rh = (uint16_t)rows;
}
return 1;
}
/* Блит спрайта chtab_2 (атласы Kid) по ЕГО id, левым верхним углом в (x,y).
* Нужен полосе HP: она рисует id 216/217 из тех же атласов, что и кадры
* Кида, а держать вторую копию раскладки в pop_cdraw ни к чему. */
void pop_kid_img_blit(uint8_t image, int x, int y)
{
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
uint8_t pg = (uint8_t)(image >> 3); /* раскладка атласов: 8 спрайтов/стр. */
uint8_t idx = (uint8_t)(image & 7);
const uint8_t *img;
uint16_t w, h;
if (pg >= kid_npages) return;
img = (const uint8_t *)atlas_image(&kidp[pg], idx);
gfx_w0_map(kidp[pg].page);
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
/* Атласы Кида хранятся COLUMN-MAJOR (transpose_cols в pop_pack_kid —
* ради бесплатного горизонтального флипа), поэтому блит обязан быть
* колоночным. Row-major gfx_blit_noclip выдал бы марку транспонированной.
* noclip БЕЗ проверки (CLIP-1): единственный вызывающий — полоса HP
* (pop_cdraw.c), у неё координаты фиксированные и заведомо на экране. */
if (w && h) gfx_blit_cols_part_noclip(x, y, img, 0, 0, 0);
gfx_w0_unmap();
}