PoP roomtest: блеск лежащего меча, клинок в руке, вспышка фона
Три расхождения с оригиналом на сцене подъёма меча (комната 15): 1. Лежащий меч не блестел. Порт animate_sword (seg007:0425) + start_anim_sword (seg007:087C): при входе в комнату тайлу даётся случайная фаза (prandom & 0x1F), каждый кадр счётчик вниз, на 0 — новый период 0x28..0x67. Кадр блеска рисуется РОВНО на modif==1 ((modif==1)+10 в draw_tile), т.е. одиночная вспышка раз в 40..103 тика. Перерисовка тайла — по смене видимого кадра, схемой кнопки (текущая страница сразу, вторая через rest-цикл). 2. В кадрах «нашёл меч» клинка не было видно. Порт add_sword_to_objtable (seg006:1798): клинок — ОТДЕЛЬНЫЙ спрайт chtab_0 поверх Kid со смещением из sword_tbl. Смещения в ЭКРАННЫХ пикселях (оригинал применяет их после calc_screen_x_coord), поэтому берём уже масштабированный obj_x. Из таблицы взяты только строки 35..42 (кадры 229..236) — бой не портирован. Новый атлас sword.atl (8 спрайтов, 1.6 КБ) + палитра chtab_0 в слотах 0x80..0x8F. Прямоугольник heal расширяется объединением с клинком, иначе он оставлял след за габаритом Kid. 3. Не было вспышки фона. do_flash = set_bg_attr(0, color) — оригинал подменяет НУЛЕВУЮ запись палитры, вспыхивает всё чёрное поле экрана; proc_get_object ставит flash_color=14, flash_time=8. У нас gfx_pal_set на обе страницы. ВАЖНО: вызов gfx_pal_set(0,0,0,0,0) пятью литералами ломает SDCC 4.5 (эмитит невалидный `ld hl, a`) — обёрнуто в функцию с параметрами. Попутно: TROBS_MAX 24 -> 30 (как в оригинале) + при входе в комнату из списка выбрасываются «декоративные» trob ДРУГИХ комнат (факелы/зелья/ меч анимируются только в отрисованной). Без этого отладочный обход всех 24 комнат забивал список, и новые анимации молча не заводились. Проверено в MAME: блеск (брейк на редрое тайла срабатывает), клинок в руке виден, фон вспыхивает жёлтым. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -36,4 +36,6 @@ static const char *const kid_atl[KID_PAGES] = {
|
|||||||
"kid27.atl",
|
"kid27.atl",
|
||||||
};
|
};
|
||||||
#define KID_PAL_FILE "kid.pal"
|
#define KID_PAL_FILE "kid.pal"
|
||||||
|
#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */
|
||||||
|
#define KID_SWORD_ID0 20 /* индекс в атласе = sword_tbl.id - ID0 */
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Binary file not shown.
@@ -21,7 +21,7 @@ BG_DATA := $(BG_DIR)/pop_env0.atl $(BG_DIR)/pop_env1.atl $(BG_DIR)/pop_env2.
|
|||||||
# не рисуются (kid_draw пропускает незагруженную страницу). Палитра —
|
# не рисуются (kid_draw пропускает незагруженную страницу). Палитра —
|
||||||
# ЕДИНАЯ игровая (env+wall+kid).
|
# ЕДИНАЯ игровая (env+wall+kid).
|
||||||
KID_PAGES := $(foreach n,0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27,$(KID_DIR)/kid$(n).atl)
|
KID_PAGES := $(foreach n,0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27,$(KID_DIR)/kid$(n).atl)
|
||||||
KID_DATA := $(KID_PAGES) $(KID_DIR)/kid.pal
|
KID_DATA := $(KID_PAGES) $(KID_DIR)/kid.pal $(KID_DIR)/sword.atl
|
||||||
|
|
||||||
EXTRA_DATA := $(BG_DATA) $(KID_DATA) $(LVL_DATA)
|
EXTRA_DATA := $(BG_DATA) $(KID_DATA) $(LVL_DATA)
|
||||||
include $(PROJ_ROOT)/app.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
|
|||||||
@@ -30,6 +30,25 @@ static const int16_t y_land[5] = {-8, 55, 118, 181, 244};
|
|||||||
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): отдельный
|
||||||
|
* спрайт клинка поверх Kid в кадрах «нашёл меч»/убирание в ножны (229..237).
|
||||||
|
* Из sword_tbl взяты только строки 35..42 — те, что адресуют эти кадры (бой
|
||||||
|
* не портирован); индекс в атласе = sword_frame − 35. Смещения таблицы —
|
||||||
|
* в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ calc_screen_x_coord. */
|
||||||
|
#define SWORD_SF_LO 35
|
||||||
|
#define SWORD_SF_HI 42
|
||||||
|
static const int8_t SWORD_DX[8] = { 7, 14, 14, 14, 28, 28, 21, 14 };
|
||||||
|
static const int8_t SWORD_DY[8] = {-32,-32,-31,-29,-28,-28,-25,-22 };
|
||||||
|
static atlas_t swordp;
|
||||||
|
static uint8_t sword_ok;
|
||||||
|
|
||||||
|
int pop_sword_load(void)
|
||||||
|
{
|
||||||
|
if (atlas_load(&swordp, "sword.atl") != 0) return -1;
|
||||||
|
sword_ok = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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] = {
|
||||||
@@ -324,5 +343,45 @@ void kid_draw(void)
|
|||||||
kid_lvalid[dp] = (skip < (int)h);
|
kid_lvalid[dp] = (skip < (int)h);
|
||||||
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
||||||
}
|
}
|
||||||
|
/* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх
|
||||||
|
* Kid. Прямоугольник heal расширяем объединением — иначе клинок, выйдя
|
||||||
|
* за габарит Kid, оставит след. */
|
||||||
|
if (sword_ok && Kid.frame >= 229 && Kid.frame < 238) {
|
||||||
|
uint8_t sf = (uint8_t)(fr.sword & 0x3F);
|
||||||
|
if (sf >= SWORD_SF_LO && sf <= SWORD_SF_HI) {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ extern int8_t knock;
|
|||||||
/* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph.
|
/* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph.
|
||||||
* 0 — OK, -1 — ошибка. */
|
* 0 — OK, -1 — ошибка. */
|
||||||
int pop_kid_load(uint8_t npages);
|
int pop_kid_load(uint8_t npages);
|
||||||
|
|
||||||
|
/* Атлас chtab_0 «меч в руке» (sword.atl) — спрайт клинка поверх Kid в кадрах
|
||||||
|
* подъёма меча/убирания в ножны (add_sword_to_objtable, seg006:1798).
|
||||||
|
* Грузить ДО initgraph, как и страницы Kid. 0 — OK, -1 — ошибка (тогда
|
||||||
|
* кадры рисуются без клинка). */
|
||||||
|
int pop_sword_load(void);
|
||||||
void pop_kid_free(void);
|
void pop_kid_free(void);
|
||||||
|
|
||||||
/* Инициализировать Kid: последовательность seq_id, клетка col/row,
|
/* Инициализировать Kid: последовательность seq_id, клетка col/row,
|
||||||
|
|||||||
@@ -774,6 +774,7 @@ uint8_t pop_down_action(void)
|
|||||||
* и возвратом в обычную стойку — как в оригинале до встречи со стражем. */
|
* и возвратом в обычную стойку — как в оригинале до встречи со стражем. */
|
||||||
uint8_t pop_have_sword;
|
uint8_t pop_have_sword;
|
||||||
uint8_t pop_item_taken; /* 0 = нет; иначе tilepos+1 поднятого */
|
uint8_t pop_item_taken; /* 0 = нет; иначе tilepos+1 поднятого */
|
||||||
|
uint8_t pop_flash_time; /* кадров вспышки фона (flash_time) */
|
||||||
static int8_t pickup_obj_type; /* -1 = меч (порт pickup_obj_type) */
|
static int8_t pickup_obj_type; /* -1 = меч (порт pickup_obj_type) */
|
||||||
|
|
||||||
/* do_pickup (seg006:1671): стереть предмет с карты (тайл → пол) и запомнить
|
/* do_pickup (seg006:1671): стереть предмет с карты (тайл → пол) и запомнить
|
||||||
@@ -789,8 +790,14 @@ static void do_pickup(int8_t obj_type, uint8_t tilepos)
|
|||||||
* play_seq по опкоду SEQ_GET_ITEM с аргументом 1 (pop_kid). */
|
* play_seq по опкоду SEQ_GET_ITEM с аргументом 1 (pop_kid). */
|
||||||
void pop_proc_get_object(void)
|
void pop_proc_get_object(void)
|
||||||
{
|
{
|
||||||
if (pickup_obj_type == -1)
|
if (pickup_obj_type == -1) {
|
||||||
pop_have_sword = 1; /* в оригинале have_sword = -1 */
|
pop_have_sword = 1; /* в оригинале have_sword = -1 */
|
||||||
|
/* Вспышка фона: flash_color = color_14_brightyellow, flash_time = 8
|
||||||
|
* (seg006 proc_get_object). do_flash = set_bg_attr(0, color) —
|
||||||
|
* оригинал подменяет НУЛЕВУЮ запись палитры, т.е. вспыхивает всё
|
||||||
|
* чёрное поле экрана. У нас так же (gfx_pal_set 0), см. roomtest. */
|
||||||
|
pop_flash_time = 8;
|
||||||
|
}
|
||||||
pickup_obj_type = 0; /* зелья (типы 1..6) не портированы */
|
pickup_obj_type = 0; /* зелья (типы 1..6) не портированы */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ void pop_kid_hp_reset(void);
|
|||||||
* (он стал полом) на ОБЕИХ страницах и записать override комнаты. */
|
* (он стал полом) на ОБЕИХ страницах и записать override комнаты. */
|
||||||
extern uint8_t pop_have_sword;
|
extern uint8_t pop_have_sword;
|
||||||
extern uint8_t pop_item_taken;
|
extern uint8_t pop_item_taken;
|
||||||
|
/* Вспышка фона (do_flash/set_bg_attr, seg001:0908): счётчик кадров, пока
|
||||||
|
* НУЛЕВАЯ запись палитры подменена ярко-жёлтым. Тикает вызывающий. */
|
||||||
|
extern uint8_t pop_flash_time;
|
||||||
uint8_t pop_get_item_action(void); /* 0 нет / 1 присесть / 2 поднять меч */
|
uint8_t pop_get_item_action(void); /* 0 нет / 1 присесть / 2 поднять меч */
|
||||||
void pop_proc_get_object(void); /* эффект — из play_seq (опкод GET_ITEM) */
|
void pop_proc_get_object(void); /* эффект — из play_seq (опкод GET_ITEM) */
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#define TILE_DEBRIS 0x0E /* button_type для «насовсем открыть» (died_on_button) */
|
#define TILE_DEBRIS 0x0E /* button_type для «насовсем открыть» (died_on_button) */
|
||||||
#define TILE_OPENER 0x0F
|
#define TILE_OPENER 0x0F
|
||||||
#define TILE_POTION 0x0A
|
#define TILE_POTION 0x0A
|
||||||
|
#define TILE_SWORD 0x16 /* меч-предмет на полу */
|
||||||
#define TILE_TORCH 0x13
|
#define TILE_TORCH 0x13
|
||||||
#define TILE_TORCH_DEBRIS 0x1E
|
#define TILE_TORCH_DEBRIS 0x1E
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ static uint8_t room_seen[POP_NROOMS]; /* modif проинициализиро
|
|||||||
|
|
||||||
/* ---- Список анимируемых тайлов ------------------------------------ */
|
/* ---- Список анимируемых тайлов ------------------------------------ */
|
||||||
typedef struct { uint8_t room, tilepos; int8_t type; } trob_t;
|
typedef struct { uint8_t room, tilepos; int8_t type; } trob_t;
|
||||||
#define TROBS_MAX 24
|
#define TROBS_MAX 30 /* как в оригинале (types.h) */
|
||||||
static trob_t trobs[TROBS_MAX];
|
static trob_t trobs[TROBS_MAX];
|
||||||
static uint8_t trobs_count;
|
static uint8_t trobs_count;
|
||||||
static uint8_t trob_code[TROBS_MAX]; /* префетч кодов тайлов на кадр (1 map W0) */
|
static uint8_t trob_code[TROBS_MAX]; /* префетч кодов тайлов на кадр (1 map W0) */
|
||||||
@@ -121,6 +122,25 @@ void pop_trob_anim_room(uint8_t room)
|
|||||||
uint8_t i, code;
|
uint8_t i, code;
|
||||||
if (!mod) return;
|
if (!mod) return;
|
||||||
pop_level_access_begin();
|
pop_level_access_begin();
|
||||||
|
/* Освободить список от «декоративных» trob ДРУГИХ комнат: факел, зелье и
|
||||||
|
* блеск меча анимируются только в ОТРИСОВАННОЙ комнате, а место в списке
|
||||||
|
* занимают. Оригинал их копит и на 30-й выдаёт «Trobs Overflow»; у нас
|
||||||
|
* отладочный обход всех 24 комнат упирается в лимит за пару минут, после
|
||||||
|
* чего новые анимации молча не заводятся. Механику (ворота/пики/кнопки,
|
||||||
|
* в т.ч. кросс-комнатные) чистка не трогает. */
|
||||||
|
{
|
||||||
|
uint8_t k, wr = 0;
|
||||||
|
for (k = 0; k < trobs_count; k++) {
|
||||||
|
uint8_t c = (uint8_t)(pop_level_tile_raw(trobs[k].room,
|
||||||
|
trobs[k].tilepos) & 0x1F);
|
||||||
|
if (trobs[k].room != room &&
|
||||||
|
(c == TILE_TORCH || c == TILE_TORCH_DEBRIS ||
|
||||||
|
c == TILE_POTION || c == TILE_SWORD))
|
||||||
|
continue;
|
||||||
|
trobs[wr++] = trobs[k];
|
||||||
|
}
|
||||||
|
trobs_count = wr;
|
||||||
|
}
|
||||||
for (i = 0; i < POP_ROOMTILES; i++) {
|
for (i = 0; i < POP_ROOMTILES; i++) {
|
||||||
code = (uint8_t)(pop_level_tile_raw(room, i) & 0x1F);
|
code = (uint8_t)(pop_level_tile_raw(room, i) & 0x1F);
|
||||||
if (code == TILE_TORCH || code == TILE_TORCH_DEBRIS) {
|
if (code == TILE_TORCH || code == TILE_TORCH_DEBRIS) {
|
||||||
@@ -129,6 +149,10 @@ void pop_trob_anim_room(uint8_t room)
|
|||||||
} else if (code == TILE_POTION) {
|
} else if (code == TILE_POTION) {
|
||||||
mod[i] = (uint8_t)((mod[i] & 0xF8) | (trob_prandom(6) + 1));
|
mod[i] = (uint8_t)((mod[i] & 0xF8) | (trob_prandom(6) + 1));
|
||||||
pop_add_trob(room, i, 1);
|
pop_add_trob(room, i, 1);
|
||||||
|
} else if (code == TILE_SWORD) {
|
||||||
|
/* start_anim_sword (seg007:087C): случайная фаза счётчика блеска. */
|
||||||
|
mod[i] = (uint8_t)(trob_prandom(0xFF) & 0x1F);
|
||||||
|
pop_add_trob(room, i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pop_level_access_end();
|
pop_level_access_end();
|
||||||
@@ -375,6 +399,15 @@ void pop_process_trobs(uint8_t cur_room)
|
|||||||
if (room == cur_room)
|
if (room == cur_room)
|
||||||
mod[tp] = (uint8_t)((mod[tp] & 0xF8) | bubble_next_frame(mod[tp] & 7));
|
mod[tp] = (uint8_t)((mod[tp] & 0xF8) | bubble_next_frame(mod[tp] & 7));
|
||||||
break;
|
break;
|
||||||
|
case TILE_SWORD:
|
||||||
|
/* animate_sword (seg007:0425): счётчик вниз; на 0 — новый период
|
||||||
|
* 0x28..0x67. Блеск рисуется РОВНО на modif==1 (draw_tile берёт
|
||||||
|
* кадр (modif==1)+10), т.е. одиночная вспышка раз в 40..103 тика. */
|
||||||
|
if (room == cur_room) {
|
||||||
|
if (--mod[tp] == 0)
|
||||||
|
mod[tp] = (uint8_t)((trob_prandom(255) & 0x3F) + 0x28);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
type = -1;
|
type = -1;
|
||||||
break;
|
break;
|
||||||
@@ -406,6 +439,19 @@ void pop_process_trobs(uint8_t cur_room)
|
|||||||
* его уже никто не положит: ставим отложенный редрой. */
|
* его уже никто не положит: ставим отложенный редрой. */
|
||||||
if (type < 0) { spike_rest[tp] = 1; rest_pending++; }
|
if (type < 0) { spike_rest[tp] = 1; rest_pending++; }
|
||||||
}
|
}
|
||||||
|
if (room == cur_room && code == TILE_SWORD) {
|
||||||
|
/* МЕЧ — редрой только на смене видимого кадра (блеск = modif==1):
|
||||||
|
* спрайт запечён в фон (draw_tile), поэтому перерисовываем тайл
|
||||||
|
* той же схемой, что и кнопку — текущая страница сразу, вторая
|
||||||
|
* следующим кадром через rest-цикл. */
|
||||||
|
uint8_t sig = (uint8_t)(mod[tp] == 1);
|
||||||
|
if (sig != trob_drawn[tp]) {
|
||||||
|
uint8_t trow = tp / 10, tcol = tp % 10;
|
||||||
|
pop_button_redraw(trow, tcol);
|
||||||
|
button_rest[tp] = 1; rest_pending++;
|
||||||
|
trob_drawn[tp] = sig;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (room == cur_room && (code == TILE_OPENER || code == TILE_CLOSER)) {
|
if (room == cur_room && (code == TILE_OPENER || code == TILE_CLOSER)) {
|
||||||
/* КНОПКА — редрой только при смене видимого состояния (нажата =
|
/* КНОПКА — редрой только при смене видимого состояния (нажата =
|
||||||
* таймер связи >1): пока Кид её держит, картинка стабильна.
|
* таймер связи >1): пока Кид её держит, картинка стабильна.
|
||||||
|
|||||||
@@ -89,8 +89,19 @@ static uint8_t room_label_redraw;
|
|||||||
#define OVR_MAX 8
|
#define OVR_MAX 8
|
||||||
static uint8_t ovr_room[OVR_MAX], ovr_pos[OVR_MAX], ovr_tile[OVR_MAX], ovr_cnt;
|
static uint8_t ovr_room[OVR_MAX], ovr_pos[OVR_MAX], ovr_tile[OVR_MAX], ovr_cnt;
|
||||||
|
|
||||||
/* Поднятый предмет (меч): tilepos и счётчик «запечь пол» по страницам. */
|
/* Поднятый предмет (меч): tilepos и счётчик «запечь пол» по страницам.
|
||||||
static uint8_t item_pos, item_bake;
|
* flash_on — вспышка фона активна (нулевая запись палитры подменена). */
|
||||||
|
static uint8_t item_pos, item_bake, flash_on;
|
||||||
|
|
||||||
|
/* Подмена НУЛЕВОЙ записи палитры на обеих страницах (do_flash/set_bg_attr).
|
||||||
|
* Через функцию с параметрами, а не пятью литералами в месте вызова: на
|
||||||
|
* gfx_pal_set(0,0,0,0,0) SDCC 4.5 выдаёт невалидный `ld hl, a`
|
||||||
|
* (см. .sprinter-cc-roomtest/roomtest.asm) и ассемблер падает. */
|
||||||
|
static void flash_bg(uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
gfx_pal_set(0, 0, r, g, b);
|
||||||
|
gfx_pal_set(1, 0, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
static void add_tile_override(uint8_t room, uint8_t pos, uint8_t tile)
|
static void add_tile_override(uint8_t room, uint8_t pos, uint8_t tile)
|
||||||
{
|
{
|
||||||
@@ -191,6 +202,7 @@ int main(void)
|
|||||||
puts("pop_bg_load failed");
|
puts("pop_bg_load failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
pop_sword_load(); /* меч в руке (без него кадры 229+ без клинка) */
|
||||||
if (pop_kid_load(28) != 0) { /* ВСЕ страницы: бег+стоп+разворот+присед+прыжок */
|
if (pop_kid_load(28) != 0) { /* ВСЕ страницы: бег+стоп+разворот+присед+прыжок */
|
||||||
puts("kid atlas load failed");
|
puts("kid atlas load failed");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -386,12 +398,27 @@ int main(void)
|
|||||||
item_pos = (uint8_t)(pop_item_taken - 1);
|
item_pos = (uint8_t)(pop_item_taken - 1);
|
||||||
item_bake = 2; /* запечь на ОБЕИХ страницах */
|
item_bake = 2; /* запечь на ОБЕИХ страницах */
|
||||||
add_tile_override(cur_room, item_pos, TILE_FLOOR);
|
add_tile_override(cur_room, item_pos, TILE_FLOOR);
|
||||||
|
pop_add_trob(cur_room, item_pos, -1); /* меч убран — блеска больше нет */
|
||||||
pop_item_taken = 0;
|
pop_item_taken = 0;
|
||||||
}
|
}
|
||||||
if (item_bake) { /* стереть спрайт меча из фона страницы */
|
if (item_bake) { /* стереть спрайт меча из фона страницы */
|
||||||
pop_floor_bake(item_pos / 10, item_pos % 10);
|
pop_floor_bake(item_pos / 10, item_pos % 10);
|
||||||
item_bake--;
|
item_bake--;
|
||||||
}
|
}
|
||||||
|
if (pop_flash_time) {
|
||||||
|
/* do_flash (seg001:0908) = set_bg_attr(0, color): оригинал подменяет
|
||||||
|
* НУЛЕВУЮ запись палитры — вспыхивает всё чёрное поле экрана.
|
||||||
|
* У нас то же самое, но правим обе страницы (у каждой своя
|
||||||
|
* палитра). color_14_brightyellow = VGA 14. */
|
||||||
|
if (!flash_on) {
|
||||||
|
flash_bg(255, 255, 85); /* color_14_brightyellow */
|
||||||
|
flash_on = 1;
|
||||||
|
}
|
||||||
|
if (--pop_flash_time == 0) {
|
||||||
|
flash_bg(0, 0, 0); /* вернуть чёрный фон */
|
||||||
|
flash_on = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
io_border = 4; /* зеленый */
|
io_border = 4; /* зеленый */
|
||||||
update_kid_render_dx(); /* straddle: смещение спрайта, если kid_room≠cur_room */
|
update_kid_render_dx(); /* straddle: смещение спрайта, если kid_room≠cur_room */
|
||||||
kid_draw(); /* нарисовать новый кадр (детект выхода за поле) */
|
kid_draw(); /* нарисовать новый кадр (детект выхода за поле) */
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import pop_pack_bg as B # pack_atlas, blob, scale6to8, ENV_SHIFT
|
|||||||
KID_DIR = "/Users/alex/Projects/DIY/Z80/Sprinter/C-Compiler/applications/PoP/SDLPoP/data/KID"
|
KID_DIR = "/Users/alex/Projects/DIY/Z80/Sprinter/C-Compiler/applications/PoP/SDLPoP/data/KID"
|
||||||
OUT_DIR = os.path.join(HERE, "..", "poc", "res", "kid")
|
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_IDS = tuple(range(20, 28)) # sword_tbl для кадров 229..236 (найти-убрать)
|
||||||
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
|
||||||
|
|
||||||
@@ -62,6 +64,22 @@ def load_kid(aidx):
|
|||||||
return im.width, im.height, transpose_cols(im.width, im.height, rowmajor)
|
return im.width, im.height, transpose_cols(im.width, im.height, rowmajor)
|
||||||
|
|
||||||
|
|
||||||
|
def load_sword(id):
|
||||||
|
"""chtab_0 (меч в руке): sword_tbl[].id = N -> res(700+N+1).png в data/PRINCE
|
||||||
|
(в оригинале add_midtable(chtab, obj_id+1) -> get_image(chtab, obj_id)).
|
||||||
|
Пиксели, как у Kid, COLUMN-MAJOR (gfx_blit_cols + бесплатный флип)."""
|
||||||
|
path = os.path.join(os.path.dirname(B.R.VDUNGEON_DIR), B.POT_DIR_NAME,
|
||||||
|
f"res{700 + id + 1}.png")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
B.die(f"нет файла {path}")
|
||||||
|
im = Image.open(path)
|
||||||
|
if im.mode != "P":
|
||||||
|
B.die(f"res{700+id+1}: mode {im.mode}, ожидался P")
|
||||||
|
src = im.tobytes()
|
||||||
|
rowmajor = bytes((0xFF if v == 0 else SWORD_PAL_BASE + v) for v in src)
|
||||||
|
return im.width, im.height, transpose_cols(im.width, im.height, rowmajor)
|
||||||
|
|
||||||
|
|
||||||
def _read16(path):
|
def _read16(path):
|
||||||
b = open(path, "rb").read()
|
b = open(path, "rb").read()
|
||||||
return [(b[4 + i * 3], b[4 + i * 3 + 1], b[4 + i * 3 + 2]) for i in range(16)]
|
return [(b[4 + i * 3], b[4 + i * 3 + 1], b[4 + i * 3 + 2]) for i in range(16)]
|
||||||
@@ -79,6 +97,8 @@ def build_palette(out_path):
|
|||||||
(PAL_BASE, os.path.join(KID_DIR, "res400.pal")), # kid 0x70
|
(PAL_BASE, os.path.join(KID_DIR, "res400.pal")), # kid 0x70
|
||||||
(B.POT_PAL_BASE, os.path.join(os.path.dirname(B.R.VDUNGEON_DIR),
|
(B.POT_PAL_BASE, os.path.join(os.path.dirname(B.R.VDUNGEON_DIR),
|
||||||
B.POT_DIR_NAME, "res150.pal")), # chtab_1 0x40
|
B.POT_DIR_NAME, "res150.pal")), # chtab_1 0x40
|
||||||
|
(SWORD_PAL_BASE, os.path.join(os.path.dirname(B.R.VDUNGEON_DIR),
|
||||||
|
B.POT_DIR_NAME, "res700.pal")), # chtab_0 0x80
|
||||||
]
|
]
|
||||||
# 16 базовых цветов экрана (mono-блит красит ими: пламя, пузырьки зелья)
|
# 16 базовых цветов экрана (mono-блит красит ими: пламя, пузырьки зелья)
|
||||||
for i, (r, g, bl) in enumerate(B.VGA16):
|
for i, (r, g, bl) in enumerate(B.VGA16):
|
||||||
@@ -121,6 +141,17 @@ def main():
|
|||||||
names.append(None)
|
names.append(None)
|
||||||
print(f" (kid стр {pg}: пусто)")
|
print(f" (kid стр {pg}: пусто)")
|
||||||
|
|
||||||
|
# chtab_0: меч в руке. Пакуем ТОЛЬКО кадры подъёма/ножен (sword_tbl
|
||||||
|
# 35..42 -> id 20..27); индекс в атласе = id - 20 (бой не портирован).
|
||||||
|
sw = {}
|
||||||
|
for i, id in enumerate(SWORD_IDS):
|
||||||
|
w, h, pix = load_sword(id)
|
||||||
|
sw[i] = (w, h, pix)
|
||||||
|
cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, "sword.atl"), sw)
|
||||||
|
total += sz
|
||||||
|
print(f" sword.atl (chtab_0, id {SWORD_IDS[0]}..{SWORD_IDS[-1]}): "
|
||||||
|
f"{len(sw)} спрайтов, каталог {cnt}, {sz} Б")
|
||||||
|
|
||||||
build_palette(os.path.join(OUT_DIR, "kid.pal"))
|
build_palette(os.path.join(OUT_DIR, "kid.pal"))
|
||||||
|
|
||||||
# C-заголовок
|
# C-заголовок
|
||||||
@@ -133,7 +164,10 @@ def main():
|
|||||||
f.write("static const char *const kid_atl[KID_PAGES] = {\n")
|
f.write("static const char *const kid_atl[KID_PAGES] = {\n")
|
||||||
for nm in names:
|
for nm in names:
|
||||||
f.write(" %s,\n" % ('"%s"' % nm if nm else "0"))
|
f.write(" %s,\n" % ('"%s"' % nm if nm else "0"))
|
||||||
f.write('};\n#define KID_PAL_FILE "kid.pal"\n#endif\n')
|
f.write('};\n#define KID_PAL_FILE "kid.pal"\n')
|
||||||
|
f.write('#define KID_SWORD_ATL "sword.atl" /* chtab_0: меч в руке */\n')
|
||||||
|
f.write(f"#define KID_SWORD_ID0 {SWORD_IDS[0]} /* индекс в атласе = sword_tbl.id - ID0 */\n")
|
||||||
|
f.write("#endif\n")
|
||||||
|
|
||||||
print(f"ИТОГО: {len(ids)} спрайтов Kid, {len(names)} EMM-страниц, "
|
print(f"ИТОГО: {len(ids)} спрайтов Kid, {len(names)} EMM-страниц, "
|
||||||
f"{total} Б диск (+1КБ палитра) -> {OUT_DIR}")
|
f"{total} Б диск (+1КБ палитра) -> {OUT_DIR}")
|
||||||
|
|||||||
Reference in New Issue
Block a user