PoP roomtest: клинок в атласе целиком + раздельный heal накладных спрайтов
Меч в оригинале ОДИН на всех: и Кид, и страж рисуют клинок из chtab_0 (add_sword_to_objtable, seg006:1798). Раньше sword.atl содержал только кадры подъёма/ножен (sword_tbl 35..42), поэтому у стража меча не было видно вовсе. - pop_pack_kid.py пакует chtab_0 целиком (id 0..33, 5168 Б), индекс в атласе = id; - pop_extract_kid_data.py вытаскивает sword_tbl (53 строки) в kid_data.h макро-инициализаторами — таблица ложится в один TU, а не в каждый; - pop_sword_draw (pop_kid.c) — общая точка отрисовки клинка с полным условием оригинала (кадры 229..237 ИЛИ меч обнажён ИЛИ живой страж); зовут и kid_draw, и pop_guard_draw. Раздельный heal накладных спрайтов. Клинок и брызги урона раньше объединялись в один прямоугольник с персонажем, а объединение почти вдвое больше суммы двух (клинок уходит вперёд-вверх) — heal же стоит ровно по площади. Теперь у накладных свой прямоугольник и свой gfx_heal, а объединение осталось ТОЛЬКО для окна fore-клипа: там это 4 сравнения без рисования, но покрыть клинок обязано, иначе он полезет поверх столба. Отладка: DEBUG_SWORD_ROOM — Киду выдаётся меч при входе в комнату 3 (там страж), чтобы не бегать за ним в комнату 15. Бюджет (225 кадров, комната 3): 415 284 – 416 370 = 0.966–0.968 кадра. Против 384 168 – 384 636 до этого шага, то есть +31 700. Разложение по фазам (медианы): process_trobs 83 190, pop_guard_draw 92 483 (спрайт 28 763 + клинок 23 820 + fore_over_char 39 906), kid_draw 59 089, fore поверх Кида + борта 47 483, heal 40 068, логика 76 338, ввод 13 050. Видно, что клинок 21x8 стоит почти как спрайт стража — это фиксированные накладные расходы клипающего блита, а не пиксели; лечится noclip-путём для column-major (см. gfx_blit_noclip_fast). Отдельным шагом. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,5 +37,5 @@ static const char *const kid_atl[KID_PAGES] = {
|
|||||||
};
|
};
|
||||||
#define KID_PAL_FILE "kid.pal"
|
#define KID_PAL_FILE "kid.pal"
|
||||||
#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */
|
#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */
|
||||||
#define KID_SWORD_ID0 20 /* индекс в атласе = sword_tbl.id - ID0 */
|
#define KID_SWORD_ID0 0 /* индекс в атласе = sword_tbl.id - ID0 */
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Binary file not shown.
@@ -42,4 +42,31 @@ static const uint16_t kid_seq_off[KID_NSEQ] = {
|
|||||||
0x21FC,0x2240,0x2257,
|
0x21FC,0x2240,0x2257,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* sword_tbl (seg006:2047): клинок отдельным спрайтом chtab_0.
|
||||||
|
* Индекс — sword_frame (кадр.sword & 0x3F), id 255 = клинка нет.
|
||||||
|
* Смещения x/y — ЭКРАННЫЕ (применяются после calc_screen_x_coord),
|
||||||
|
* x — «вперёд» по направлению персонажа. */
|
||||||
|
#define KID_NSWORD 51
|
||||||
|
#define KID_SWORD_ID_INIT { \
|
||||||
|
0xFF,0x00,0x05,0x01,0x02,0x06,0x07,0x03,0x04,0x1E,0x08,0x09, \
|
||||||
|
0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x13,0x11, \
|
||||||
|
0x11,0x07,0x01,0x1C,0x08,0x04,0x04,0x0A,0x0D,0x0D,0x0D,0x14, \
|
||||||
|
0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0xFF,0xFF,0x1D,0x08,0x1F, \
|
||||||
|
0x20,0x21,0x08, \
|
||||||
|
}
|
||||||
|
#define KID_SWORD_DX_INIT { \
|
||||||
|
0,0,-9,7,17,7,0,17,16,12,13,7, \
|
||||||
|
10,10,22,28,13,0,0,21,14,21,22,7, \
|
||||||
|
15,0,7,14,7,6,9,11,24,19,21,7, \
|
||||||
|
14,14,14,28,28,21,14,14,21,0,8,14, \
|
||||||
|
14,7,8, \
|
||||||
|
}
|
||||||
|
#define KID_SWORD_DY_INIT { \
|
||||||
|
0,-9,-29,-25,-26,-14,-5,-16,-19,-9,-34,-25, \
|
||||||
|
-16,-11,-21,-23,-35,-38,-29,-19,-23,-22,-23,-13, \
|
||||||
|
-18,-8,-27,-28,-27,-23,-21,-18,-23,-23,-23,-32, \
|
||||||
|
-32,-31,-29,-28,-28,-25,-22,-25,-25,-16,-37,-24, \
|
||||||
|
-24,-14,-37, \
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <gfx.h>
|
#include <gfx.h>
|
||||||
#include <sprite.h>
|
#include <sprite.h>
|
||||||
#include "pop_guard.h"
|
#include "pop_guard.h"
|
||||||
|
#include "pop_kid.h" /* pop_sword_draw — клинок общий с Кидом */
|
||||||
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
||||||
#include "kid_data.h"
|
#include "kid_data.h"
|
||||||
|
|
||||||
@@ -29,6 +30,13 @@ static int gx_l[2], gy_l[2];
|
|||||||
static uint16_t gw_l[2], gh_l[2];
|
static uint16_t gw_l[2], gh_l[2];
|
||||||
static uint8_t gvalid[2];
|
static uint8_t gvalid[2];
|
||||||
|
|
||||||
|
/* Клинок — ОТДЕЛЬНЫМ прямоугольником, не объединением со стражем: объединение
|
||||||
|
* почти вдвое больше суммы двух (клинок уходит вперёд-вверх), а heal стоит
|
||||||
|
* ровно по площади. Объединение считаем только для окна fore-клипа. */
|
||||||
|
static int sx_l[2], sy_l[2];
|
||||||
|
static uint16_t sw_l[2], sh_l[2];
|
||||||
|
static uint8_t svalid[2];
|
||||||
|
|
||||||
int pop_guard_load(void) __banked
|
int pop_guard_load(void) __banked
|
||||||
{
|
{
|
||||||
static const char *const names[GUARD_PAGES] = {
|
static const char *const names[GUARD_PAGES] = {
|
||||||
@@ -54,15 +62,20 @@ void pop_guard_heal(void) __banked
|
|||||||
{
|
{
|
||||||
uint8_t p = gfx_get_draw_page() & 1;
|
uint8_t p = gfx_get_draw_page() & 1;
|
||||||
if (gvalid[p]) gfx_heal(gx_l[p], gy_l[p] + POP_YOFF, gw_l[p], gh_l[p]);
|
if (gvalid[p]) gfx_heal(gx_l[p], gy_l[p] + POP_YOFF, gw_l[p], gh_l[p]);
|
||||||
gvalid[p] = 0;
|
if (svalid[p]) gfx_heal(sx_l[p], sy_l[p] + POP_YOFF, sw_l[p], sh_l[p]);
|
||||||
|
gvalid[p] = svalid[p] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_guard_draw(void) __banked
|
void pop_guard_draw(void) __banked
|
||||||
{
|
{
|
||||||
int obj_x, obj_y, top, fwd, fp_x, bx0 = 0, top0 = 0;
|
int obj_x, obj_y, top, fwd, fp_x;
|
||||||
uint8_t page, idx, p, flip;
|
uint8_t page, idx, p, flip;
|
||||||
const uint8_t *img;
|
const uint8_t *img;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
|
/* Окно fore-клипа — по ОБЪЕДИНЁННОМУ габариту «страж + клинок»; сам
|
||||||
|
* футпринт (pop_fore_over_char) считается по спрайту стража, поэтому
|
||||||
|
* w/h ниже НЕ трогаем. */
|
||||||
|
int clip_x = 0, clip_y = 0; uint16_t clip_w = 0, clip_h = 0;
|
||||||
|
|
||||||
if (!g_ok || Guard.charid == 0 || pop_gframe.image == 255) return;
|
if (!g_ok || Guard.charid == 0 || pop_gframe.image == 255) return;
|
||||||
/* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */
|
/* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */
|
||||||
@@ -85,17 +98,41 @@ void pop_guard_draw(void) __banked
|
|||||||
flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */
|
flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */
|
||||||
if (flip) bx -= (int)w;
|
if (flip) bx -= (int)w;
|
||||||
top = obj_y - (int)h + 1;
|
top = obj_y - (int)h + 1;
|
||||||
bx0 = bx; top0 = top;
|
|
||||||
p = gfx_get_draw_page() & 1;
|
p = gfx_get_draw_page() & 1;
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
gx_l[p] = bx; gy_l[p] = top; gw_l[p] = w; gh_l[p] = h; gvalid[p] = 1;
|
gx_l[p] = bx; gy_l[p] = top; gw_l[p] = w; gh_l[p] = h; gvalid[p] = 1;
|
||||||
|
clip_x = bx; clip_y = top; clip_w = w; clip_h = h;
|
||||||
|
}
|
||||||
|
/* Клинок стража — тот же chtab_0, что у Кида (add_sword_to_objtable,
|
||||||
|
* seg006:1798): у ЖИВОГО стража меч в руке всегда. Свой прямоугольник
|
||||||
|
* heal; окно fore-клипа — объединение (без него клинок полез бы поверх
|
||||||
|
* переднего столба). */
|
||||||
|
{
|
||||||
|
int sx, sy; uint16_t sw, sh;
|
||||||
|
if (pop_sword_draw(&Guard, &pop_gframe, obj_x, obj_y,
|
||||||
|
&sx, &sy, &sw, &sh)) {
|
||||||
|
p = gfx_get_draw_page() & 1;
|
||||||
|
sx_l[p] = sx; sy_l[p] = sy; sw_l[p] = sw; sh_l[p] = sh;
|
||||||
|
svalid[p] = 1;
|
||||||
|
if (!clip_w) {
|
||||||
|
clip_x = sx; clip_y = sy; clip_w = sw; clip_h = sh;
|
||||||
|
} else {
|
||||||
|
int x1 = clip_x + (int)clip_w, y1 = clip_y + (int)clip_h;
|
||||||
|
if (sx < clip_x) clip_x = sx;
|
||||||
|
if (sy < clip_y) clip_y = sy;
|
||||||
|
if (sx + (int)sw > x1) x1 = sx + (int)sw;
|
||||||
|
if (sy + (int)sh > y1) y1 = sy + (int)sh;
|
||||||
|
clip_w = (uint16_t)(x1 - clip_x);
|
||||||
|
clip_h = (uint16_t)(y1 - clip_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
/* Окно fore-слоя = прямоугольник стража (см. pop_bg.c, fclip_on):
|
/* Окно fore-слоя = прямоугольник стража с клинком (см. pop_bg.c,
|
||||||
* без него проход возвращал ЦЕЛЫЕ тайлы — 432 К тактов на кадр. */
|
* fclip_on): без него проход возвращал ЦЕЛЫЕ тайлы — 432 К тактов. */
|
||||||
pop_fore_set_clip(bx0, top0 + POP_YOFF, (int)w, (int)h);
|
pop_fore_set_clip(clip_x, clip_y + POP_YOFF, (int)clip_w, (int)clip_h);
|
||||||
/* Передние грани тайлов ПОВЕРХ стража: в оригинале персонаж — запись
|
/* Передние грани тайлов ПОВЕРХ стража: в оригинале персонаж — запись
|
||||||
* midtable, а foretable рисуется после всех midtable-записей, поэтому
|
* midtable, а foretable рисуется после всех midtable-записей, поэтому
|
||||||
* колонна/кромка пола перекрывает ЛЮБОГО персонажа, не только Kid
|
* колонна/кромка пола перекрывает ЛЮБОГО персонажа, не только Kid
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ int8_t knock;
|
|||||||
static atlas_t kidp[KID_MAXPAGES];
|
static atlas_t kidp[KID_MAXPAGES];
|
||||||
static uint8_t kid_npages;
|
static uint8_t kid_npages;
|
||||||
|
|
||||||
/* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): отдельный
|
/* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): клинок —
|
||||||
* спрайт клинка поверх Kid в кадрах «нашёл меч»/убирание в ножны (229..237).
|
* ОТДЕЛЬНЫЙ спрайт поверх персонажа, один и тот же атлас у Кида и у стража.
|
||||||
* Из sword_tbl взяты только строки 35..42 — те, что адресуют эти кадры (бой
|
* Таблица sword_tbl (kid_data.h, генерится из seg006) индексируется
|
||||||
* не портирован); индекс в атласе = sword_frame − 35. Смещения таблицы —
|
* sword_frame = кадр.sword & 0x3F; id 255 = клинка в этом кадре нет.
|
||||||
* в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ calc_screen_x_coord. */
|
* Смещения — в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ
|
||||||
#define SWORD_SF_LO 35
|
* calc_screen_x_coord, x — «вперёд» по направлению персонажа. */
|
||||||
#define SWORD_SF_HI 42
|
static const uint8_t SWORD_ID[KID_NSWORD] = KID_SWORD_ID_INIT;
|
||||||
static const int8_t SWORD_DX[8] = { 7, 14, 14, 14, 28, 28, 21, 14 };
|
static const int8_t SWORD_DX[KID_NSWORD] = KID_SWORD_DX_INIT;
|
||||||
static const int8_t SWORD_DY[8] = {-32,-32,-31,-29,-28,-28,-25,-22 };
|
static const int8_t SWORD_DY[KID_NSWORD] = KID_SWORD_DY_INIT;
|
||||||
static atlas_t swordp;
|
static atlas_t swordp;
|
||||||
static uint8_t sword_ok;
|
static uint8_t sword_ok;
|
||||||
|
|
||||||
@@ -47,6 +47,46 @@ int pop_sword_load(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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->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);
|
||||||
|
gfx_w0_map(swordp.page);
|
||||||
|
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;
|
||||||
|
if (flip) bx -= (int)w;
|
||||||
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
|
gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, 0, 0);
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
*rx = bx; *ry = top; *rw = w; *rh = h;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int pop_kid_load(uint8_t npages)
|
int pop_kid_load(uint8_t npages)
|
||||||
{
|
{
|
||||||
static const char *const names[KID_MAXPAGES] = {
|
static const char *const names[KID_MAXPAGES] = {
|
||||||
@@ -371,6 +411,64 @@ static int kid_lx[2], kid_ly[2];
|
|||||||
static uint16_t kid_lw[2], kid_lh[2];
|
static uint16_t kid_lw[2], kid_lh[2];
|
||||||
static uint8_t kid_lvalid[2];
|
static uint8_t kid_lvalid[2];
|
||||||
|
|
||||||
|
/* ОТДЕЛЬНЫЙ прямоугольник накладных спрайтов (клинок chtab_0, брызги урона).
|
||||||
|
* Раньше он объединялся с прямоугольником персонажа — но объединение сильно
|
||||||
|
* больше суммы двух: клинок уходит вперёд-вверх, и общий габарит выходил
|
||||||
|
* почти вдвое шире и выше самого Kid, а heal стоит ровно по площади.
|
||||||
|
* Поэтому heal делаем двумя вызовами по своим прямоугольникам, а объединение
|
||||||
|
* оставляем ТОЛЬКО для окна fore-клипа (там это 4 сравнения, без рисования —
|
||||||
|
* зато клинок/брызги честно уходят под передние грани тайлов). */
|
||||||
|
static int kid_ox[2], kid_oy[2];
|
||||||
|
static uint16_t kid_ow[2], kid_oh[2];
|
||||||
|
static uint8_t kid_ovalid[2];
|
||||||
|
|
||||||
|
/* Добавить накладной спрайт в overlay-прямоугольник страницы dp. Клинок и
|
||||||
|
* брызги в одном кадре встречаются редко и оба маленькие — их объединение
|
||||||
|
* дешевле третьего слота. */
|
||||||
|
static void kid_overlay_add(uint8_t dp, int x, int y, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
if (!kid_ovalid[dp]) {
|
||||||
|
kid_ox[dp] = x; kid_oy[dp] = y; kid_ow[dp] = w; kid_oh[dp] = h;
|
||||||
|
kid_ovalid[dp] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int x0 = kid_ox[dp], y0 = kid_oy[dp];
|
||||||
|
int x1 = x0 + (int)kid_ow[dp], y1 = y0 + (int)kid_oh[dp];
|
||||||
|
if (x < x0) x0 = x;
|
||||||
|
if (y < y0) y0 = y;
|
||||||
|
if (x + (int)w > x1) x1 = x + (int)w;
|
||||||
|
if (y + (int)h > y1) y1 = y + (int)h;
|
||||||
|
kid_ox[dp] = x0; kid_oy[dp] = y0;
|
||||||
|
kid_ow[dp] = (uint16_t)(x1 - x0); kid_oh[dp] = (uint16_t)(y1 - y0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Окно fore-клипа = объединение «персонаж + накладные». Считается по
|
||||||
|
* прямоугольникам, ничего не рисует. */
|
||||||
|
static void kid_fore_clip(uint8_t dp)
|
||||||
|
{
|
||||||
|
int x0, y0, x1, y1;
|
||||||
|
if (!kid_lvalid[dp] && !kid_ovalid[dp]) { pop_fore_set_clip(0, 0, 0, 0); return; }
|
||||||
|
if (!kid_ovalid[dp]) {
|
||||||
|
pop_fore_set_clip(kid_lx[dp], kid_ly[dp] + POP_YOFF,
|
||||||
|
(int)kid_lw[dp], (int)kid_lh[dp]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!kid_lvalid[dp]) {
|
||||||
|
pop_fore_set_clip(kid_ox[dp], kid_oy[dp] + POP_YOFF,
|
||||||
|
(int)kid_ow[dp], (int)kid_oh[dp]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x0 = kid_lx[dp] < kid_ox[dp] ? kid_lx[dp] : kid_ox[dp];
|
||||||
|
y0 = kid_ly[dp] < kid_oy[dp] ? kid_ly[dp] : kid_oy[dp];
|
||||||
|
x1 = kid_lx[dp] + (int)kid_lw[dp];
|
||||||
|
y1 = kid_ly[dp] + (int)kid_lh[dp];
|
||||||
|
if (kid_ox[dp] + (int)kid_ow[dp] > x1) x1 = kid_ox[dp] + (int)kid_ow[dp];
|
||||||
|
if (kid_oy[dp] + (int)kid_oh[dp] > y1) y1 = kid_oy[dp] + (int)kid_oh[dp];
|
||||||
|
pop_fore_set_clip(x0, y0 + POP_YOFF, x1 - x0, y1 - y0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Метрики последнего kid_draw для футпринта fore-over-Kid (pop_bg):
|
/* Метрики последнего kid_draw для футпринта fore-over-Kid (pop_bg):
|
||||||
* obj_x — ЛОГИЧЕСКАЯ X кадра (ДО ×8/7), obj_y — низ, w/h — размер. */
|
* obj_x — ЛОГИЧЕСКАЯ X кадра (ДО ×8/7), obj_y — низ, w/h — размер. */
|
||||||
static int kid_fp_x, kid_fp_y;
|
static int kid_fp_x, kid_fp_y;
|
||||||
@@ -417,23 +515,12 @@ void kid_draw_splash(void)
|
|||||||
gfx_blit_cols(bx, top + POP_YOFF, img, flip);
|
gfx_blit_cols(bx, top + POP_YOFF, img, flip);
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
pop_clip_sprite(bx, w, top + POP_YOFF, (int)h);
|
pop_clip_sprite(bx, w, top + POP_YOFF, (int)h);
|
||||||
/* Прямоугольник splash расширяет область heal ЭТОЙ страницы: иначе
|
/* Брызги — накладной спрайт: свой прямоугольник heal (см.
|
||||||
* брызги останутся на экране после кадра. */
|
* kid_overlay_add), окно fore пересчитываем объединением. */
|
||||||
{
|
{
|
||||||
uint8_t dp = gfx_get_draw_page();
|
uint8_t dp = gfx_get_draw_page();
|
||||||
if (kid_lvalid[dp]) {
|
kid_overlay_add(dp, bx, top, w, h);
|
||||||
int x0 = kid_lx[dp] < bx ? kid_lx[dp] : bx;
|
kid_fore_clip(dp);
|
||||||
int y0 = kid_ly[dp] < top ? kid_ly[dp] : top;
|
|
||||||
int x1 = kid_lx[dp] + (int)kid_lw[dp];
|
|
||||||
int y1 = kid_ly[dp] + (int)kid_lh[dp];
|
|
||||||
if (bx + (int)w > x1) x1 = bx + (int)w;
|
|
||||||
if (top + (int)h > y1) y1 = top + (int)h;
|
|
||||||
kid_lx[dp] = x0; kid_ly[dp] = y0;
|
|
||||||
kid_lw[dp] = (uint16_t)(x1 - x0); kid_lh[dp] = (uint16_t)(y1 - y0);
|
|
||||||
/* Окно fore-слоя расширяем тем же объединением: брызги
|
|
||||||
* тоже должны уходить под передние грани тайлов. */
|
|
||||||
pop_fore_set_clip(x0, y0 + POP_YOFF, x1 - x0, y1 - y0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
@@ -445,7 +532,9 @@ void kid_heal(void)
|
|||||||
uint8_t p = gfx_get_draw_page();
|
uint8_t p = gfx_get_draw_page();
|
||||||
if (kid_lvalid[p])
|
if (kid_lvalid[p])
|
||||||
gfx_heal(kid_lx[p], kid_ly[p] + POP_YOFF, kid_lw[p], kid_lh[p]); /* +YOFF: центрирование */
|
gfx_heal(kid_lx[p], kid_ly[p] + POP_YOFF, kid_lw[p], kid_lh[p]); /* +YOFF: центрирование */
|
||||||
kid_lvalid[p] = 0;
|
if (kid_ovalid[p])
|
||||||
|
gfx_heal(kid_ox[p], kid_oy[p] + POP_YOFF, kid_ow[p], kid_oh[p]);
|
||||||
|
kid_lvalid[p] = kid_ovalid[p] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kid_draw(void)
|
void kid_draw(void)
|
||||||
@@ -518,57 +607,16 @@ void kid_draw(void)
|
|||||||
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
||||||
}
|
}
|
||||||
/* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх
|
/* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх
|
||||||
* Kid. Прямоугольник heal расширяем объединением — иначе клинок, выйдя
|
* Kid — со СВОИМ прямоугольником heal (kid_overlay_add). */
|
||||||
* за габарит Kid, оставит след. */
|
{
|
||||||
if (sword_ok && Kid.frame >= 229 && Kid.frame < 238) {
|
int sx, sy; uint16_t sw, sh;
|
||||||
uint8_t sf = (uint8_t)(fr.sword & 0x3F);
|
if (pop_sword_draw(&Kid, &fr, obj_x, obj_y, &sx, &sy, &sw, &sh))
|
||||||
if (sf >= SWORD_SF_LO && sf <= SWORD_SF_HI) {
|
kid_overlay_add(gfx_get_draw_page() & 1, sx, sy, sw, sh);
|
||||||
uint8_t si = (uint8_t)(sf - SWORD_SF_LO);
|
|
||||||
const uint8_t *simg = (const uint8_t *)atlas_image(&swordp, si);
|
|
||||||
uint16_t sw, sh;
|
|
||||||
gfx_w0_map(swordp.page);
|
|
||||||
sw = (uint16_t)(simg[0] | ((uint16_t)simg[1] << 8));
|
|
||||||
sh = (uint16_t)(simg[2] | ((uint16_t)simg[3] << 8));
|
|
||||||
if (sw && sh) {
|
|
||||||
uint8_t sflip = (Kid.direction >= 0);
|
|
||||||
int sbx = obj_x + ((Kid.direction < 0) ? -(int)SWORD_DX[si]
|
|
||||||
: (int)SWORD_DX[si]);
|
|
||||||
int stop = obj_y + (int)SWORD_DY[si] - (int)sh + 1;
|
|
||||||
uint8_t dp = gfx_get_draw_page() & 1;
|
|
||||||
if (sflip) sbx -= (int)sw;
|
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
|
||||||
gfx_blit_cols_part(sbx, stop + POP_YOFF, simg, sflip, 0, 0);
|
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
|
||||||
if (!kid_lvalid[dp]) {
|
|
||||||
kid_lx[dp] = sbx; kid_ly[dp] = stop;
|
|
||||||
kid_lw[dp] = sw; kid_lh[dp] = sh;
|
|
||||||
kid_lvalid[dp] = 1;
|
|
||||||
} else {
|
|
||||||
int x0 = kid_lx[dp], y0 = kid_ly[dp];
|
|
||||||
int x1 = x0 + (int)kid_lw[dp], y1 = y0 + (int)kid_lh[dp];
|
|
||||||
if (sbx < x0) x0 = sbx;
|
|
||||||
if (stop < y0) y0 = stop;
|
|
||||||
if (sbx + (int)sw > x1) x1 = sbx + (int)sw;
|
|
||||||
if (stop + (int)sh > y1) y1 = stop + (int)sh;
|
|
||||||
kid_lx[dp] = x0; kid_ly[dp] = y0;
|
|
||||||
kid_lw[dp] = (uint16_t)(x1 - x0);
|
|
||||||
kid_lh[dp] = (uint16_t)(y1 - y0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
/* Окно fore-слоя = итоговый прямоугольник спрайта (с клинком и
|
/* Окно fore-слоя = объединение «Kid + клинок» (с обрезкой clip_char):
|
||||||
* обрезкой clip_char): fore-проход вернёт куски тайлов ТОЛЬКО поверх
|
* fore-проход вернёт куски тайлов ТОЛЬКО поверх него, а не целыми
|
||||||
* него, а не целыми тайлами — см. pop_bg.c, fclip_on. Ставим и когда
|
* тайлами — см. pop_bg.c, fclip_on. Ставим и когда рисовать нечего:
|
||||||
* рисовать нечего (kid_lvalid=0): нулевое окно = fore-проход не
|
* нулевое окно = fore-проход не трогает экран, что и правильно. */
|
||||||
* трогает экран, что и правильно — перекрывать нечего. */
|
kid_fore_clip(gfx_get_draw_page() & 1);
|
||||||
{
|
|
||||||
uint8_t dp = gfx_get_draw_page() & 1;
|
|
||||||
if (kid_lvalid[dp])
|
|
||||||
pop_fore_set_clip(kid_lx[dp], kid_ly[dp] + POP_YOFF,
|
|
||||||
(int)kid_lw[dp], (int)kid_lh[dp]);
|
|
||||||
else
|
|
||||||
pop_fore_set_clip(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "pop_char.h"
|
#include "pop_char.h"
|
||||||
|
#include "kid_data.h" /* kframe — тип кадра для pop_sword_draw */
|
||||||
|
|
||||||
/* Состояние персонажа — общий тип для Кида, стража и Char (pop_char.h). */
|
/* Состояние персонажа — общий тип для Кида, стража и Char (pop_char.h). */
|
||||||
extern pop_char_t Kid;
|
extern pop_char_t Kid;
|
||||||
@@ -43,6 +44,15 @@ void pop_kid_data_free(void);
|
|||||||
int pop_sword_load(void);
|
int pop_sword_load(void);
|
||||||
void pop_kid_free(void);
|
void pop_kid_free(void);
|
||||||
|
|
||||||
|
/* Нарисовать клинок chtab_0 поверх персонажа (порт add_sword_to_objtable,
|
||||||
|
* seg006:1798) — общая точка для Кида и стража: меч в оригинале ОДИН на
|
||||||
|
* всех. obj_x/obj_y — та же опорная точка, что у спрайта персонажа.
|
||||||
|
* Возвращает 1 и прямоугольник клинка (для heal), 0 — клинка нет.
|
||||||
|
* Страницу 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);
|
||||||
|
|
||||||
/* Инициализировать Kid: последовательность seq_id, клетка col/row,
|
/* Инициализировать Kid: последовательность seq_id, клетка col/row,
|
||||||
* направление dir (-1/0). Ставит x/y по x_bump/y_land. */
|
* направление dir (-1/0). Ставит x/y по x_bump/y_land. */
|
||||||
void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir);
|
void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir);
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ uint8_t pop_cheats;
|
|||||||
* Убирается закомментированием ОДНОЙ строки ниже. */
|
* Убирается закомментированием ОДНОЙ строки ниже. */
|
||||||
#define ROOMNAV 1
|
#define ROOMNAV 1
|
||||||
|
|
||||||
|
/* ОТЛАДКА БОЁВКИ: выдавать Киду меч сразу при входе в комнату с этим
|
||||||
|
* номером (в комнате 3 стоит страж уровня 1). Иначе каждый прогон надо
|
||||||
|
* бежать за мечом в комнату 15. Убрать, когда боёвку можно будет
|
||||||
|
* проверять штатным путём. */
|
||||||
|
#define DEBUG_SWORD_ROOM 3
|
||||||
|
|
||||||
/* Рабочая копия ТЕКУЩЕЙ комнаты (W2, мутабельная: loose→empty) + срезы
|
/* Рабочая копия ТЕКУЩЕЙ комнаты (W2, мутабельная: loose→empty) + срезы
|
||||||
* соседей для кромок. Заполняются pop_room_load из уровня. File-scope,
|
* соседей для кромок. Заполняются pop_room_load из уровня. File-scope,
|
||||||
* чтобы enter_room переключал комнату без длинного списка параметров. */
|
* чтобы enter_room переключал комнату без длинного списка параметров. */
|
||||||
@@ -196,6 +202,9 @@ static void enter_room(uint8_t room)
|
|||||||
* Kid.room == Guard.room нужно уже сейчас: без него луч видимости
|
* Kid.room == Guard.room нужно уже сейчас: без него луч видимости
|
||||||
* стража никогда не срабатывает. */
|
* стража никогда не срабатывает. */
|
||||||
Kid.room = room;
|
Kid.room = room;
|
||||||
|
#ifdef DEBUG_SWORD_ROOM
|
||||||
|
if (room == DEBUG_SWORD_ROOM) pop_have_sword = 1;
|
||||||
|
#endif
|
||||||
pop_loose_reset(); /* loose-состояние не тянем из старой комнаты */
|
pop_loose_reset(); /* loose-состояние не тянем из старой комнаты */
|
||||||
pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */
|
pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */
|
||||||
pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */
|
pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ seqtbl адресуется базой SEQTBL_BASE=0x196E: элемент = kid_
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
SRC = "/Users/alex/Projects/DIY/Z80/Sprinter/C-Compiler/applications/PoP/SDLPoP/src"
|
SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "SDLPoP", "src")
|
||||||
OUT_BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
OUT_BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
"..", "poc", "res", "kid")
|
"..", "poc", "res", "kid")
|
||||||
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
@@ -44,6 +44,18 @@ def parse_frame_table(txt, name="frame_table_kid"):
|
|||||||
return frames
|
return frames
|
||||||
|
|
||||||
|
|
||||||
|
def parse_sword_tbl(txt):
|
||||||
|
"""sword_tbl[] (seg006:2047) -> список (id, x, y). id 255 = клинка нет.
|
||||||
|
Индекс — sword_frame (cur_frame.sword & 0x3F); один и тот же chtab_0 и у
|
||||||
|
Кида, и у стража."""
|
||||||
|
m = re.search(r'const sword_table_type sword_tbl\[\]\s*=\s*\{(.*?)\n\};', txt, re.S)
|
||||||
|
rows = []
|
||||||
|
for row in re.findall(r'\{([^}]*)\}', m.group(1)):
|
||||||
|
a, b, c = [int(p.strip(), 0) for p in row.split(',')]
|
||||||
|
rows.append((a & 0xFF, b & 0xFF, c & 0xFF))
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def parse_byte_array(txt, name):
|
def parse_byte_array(txt, name):
|
||||||
m = re.search(r'const byte %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S)
|
m = re.search(r'const byte %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S)
|
||||||
return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))]
|
return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))]
|
||||||
@@ -69,6 +81,7 @@ def main():
|
|||||||
gframes = parse_frame_table(seg006, "frame_tbl_guard")
|
gframes = parse_frame_table(seg006, "frame_tbl_guard")
|
||||||
seqtbl = parse_byte_array(seqc, "original_seqtbl")
|
seqtbl = parse_byte_array(seqc, "original_seqtbl")
|
||||||
offs = parse_word_array(seqc, "original_seqtbl_offsets")
|
offs = parse_word_array(seqc, "original_seqtbl_offsets")
|
||||||
|
swtbl = parse_sword_tbl(seg006)
|
||||||
|
|
||||||
with open(OUT, "w") as f:
|
with open(OUT, "w") as f:
|
||||||
f.write("/* kid_data.h — данные анимации Kid (frame_table + seqtbl +\n"
|
f.write("/* kid_data.h — данные анимации Kid (frame_table + seqtbl +\n"
|
||||||
@@ -102,7 +115,22 @@ def main():
|
|||||||
f.write("#define KID_NSEQ %d\n" % len(offs))
|
f.write("#define KID_NSEQ %d\n" % len(offs))
|
||||||
f.write("static const uint16_t kid_seq_off[KID_NSEQ] = {\n")
|
f.write("static const uint16_t kid_seq_off[KID_NSEQ] = {\n")
|
||||||
f.write(emit_c(offs, 8, "0x%04X") + "\n};\n\n")
|
f.write(emit_c(offs, 8, "0x%04X") + "\n};\n\n")
|
||||||
f.write("#endif\n")
|
|
||||||
|
# sword_tbl — макро-инициализаторами, а не готовыми массивами: так
|
||||||
|
# 159 байт таблицы лягут в ОДИН TU (pop_kid.c), а не в каждый, кто
|
||||||
|
# включил заголовок.
|
||||||
|
f.write("/* sword_tbl (seg006:2047): клинок отдельным спрайтом chtab_0.\n"
|
||||||
|
" * Индекс — sword_frame (кадр.sword & 0x3F), id 255 = клинка нет.\n"
|
||||||
|
" * Смещения x/y — ЭКРАННЫЕ (применяются после calc_screen_x_coord),\n"
|
||||||
|
" * x — «вперёд» по направлению персонажа. */\n")
|
||||||
|
f.write("#define KID_NSWORD %d\n" % len(swtbl))
|
||||||
|
for i, (nm, fmt) in enumerate((("ID", "0x%02X"), ("DX", "%d"), ("DY", "%d"))):
|
||||||
|
# dx/dy — знаковые: печатаем со знаком, иначе int8_t = overflow
|
||||||
|
vals = [r[i] if i == 0 else (r[i] - 256 if r[i] > 127 else r[i])
|
||||||
|
for r in swtbl]
|
||||||
|
f.write("#define KID_SWORD_%s_INIT { \\\n" % nm)
|
||||||
|
f.write(emit_c(vals, 12, fmt).replace("\n", " \\\n") + " \\\n}\n")
|
||||||
|
f.write("\n#endif\n")
|
||||||
|
|
||||||
# kid_data.bin: кадры (по 5 байт) + seqtbl подряд
|
# kid_data.bin: кадры (по 5 байт) + seqtbl подряд
|
||||||
os.makedirs(OUT_BIN_DIR, exist_ok=True)
|
os.makedirs(OUT_BIN_DIR, exist_ok=True)
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ OUT_DIR = os.path.join(HERE, "..", "poc", "res", "kid")
|
|||||||
PAL_BASE = 0x70 # слоты палитры Sprinter под Kid
|
PAL_BASE = 0x70 # слоты палитры Sprinter под Kid
|
||||||
SWORD_PAL_BASE = 0x80 # chtab_0 (меч в руке) -> слоты 0x80..0x8F
|
SWORD_PAL_BASE = 0x80 # chtab_0 (меч в руке) -> слоты 0x80..0x8F
|
||||||
GUARD_PAL_BASE = 0x90 # chtab_5 (страж) -> слоты 0x90..0x9F
|
GUARD_PAL_BASE = 0x90 # chtab_5 (страж) -> слоты 0x90..0x9F
|
||||||
SWORD_IDS = tuple(range(20, 28)) # sword_tbl для кадров 229..236 (найти-убрать)
|
# chtab_0 целиком: sword_tbl (seg006:2047) адресует id 0..33 — подъём меча,
|
||||||
|
# ножны И вся боёвка. Меч в оригинале ОДИН на всех: и Кид, и страж рисуют
|
||||||
|
# клинок из этого же chtab_0 (add_sword_to_objtable, seg006:1798).
|
||||||
|
SWORD_IDS = tuple(range(0, 34))
|
||||||
SHIFT = 3 # kid[img>>3], idx img&7 (Kid-кадры крупные: 8/стр ≤16КБ)
|
SHIFT = 3 # kid[img>>3], idx img&7 (Kid-кадры крупные: 8/стр ≤16КБ)
|
||||||
MASK = (1 << SHIFT) - 1
|
MASK = (1 << SHIFT) - 1
|
||||||
|
|
||||||
@@ -150,8 +153,7 @@ def main():
|
|||||||
names.append(None)
|
names.append(None)
|
||||||
print(f" (kid стр {pg}: пусто)")
|
print(f" (kid стр {pg}: пусто)")
|
||||||
|
|
||||||
# chtab_0: меч в руке. Пакуем ТОЛЬКО кадры подъёма/ножен (sword_tbl
|
# chtab_0: меч в руке — весь chtab (id 0..33), индекс в атласе = id.
|
||||||
# 35..42 -> id 20..27); индекс в атласе = id - 20 (бой не портирован).
|
|
||||||
sw = {}
|
sw = {}
|
||||||
for i, id in enumerate(SWORD_IDS):
|
for i, id in enumerate(SWORD_IDS):
|
||||||
w, h, pix = load_sword(id)
|
w, h, pix = load_sword(id)
|
||||||
|
|||||||
Reference in New Issue
Block a user