PoP roomtest: отрисовка стража в резидент W3 + fore-окклюзия + off-by-one спрайта
Разгрузка W1/W2 перед ИИ стражей (вариант 2 из двух обсуждённых). 1. pop_guard.c разделён по окнам: состояние/логика (Guard, HP, enter, kill, load_frame) остаются в W1/W2 — их обязан видеть банк guards.c; ОТРИСОВКА уехала в новый pop_gdraw.c, собираемый как --w3 (резидент). Правило границы: резидент = только то, что рисует и зовётся исключительно из главного цикла. Кадр стража стал глобальным (pop_gframe): заполняет логика, читает резидент. 2. Страж не окклюдировался передними гранями тайлов — рисовался поверх столба. В оригинале любой Char это запись midtable, а foretable рисуется после всех midtable (draw_tile_fore, seg008:690), т.е. столб перекрывает всех одинаково. Футпринт персонажа выделен из pop_fore_over_kid в char_footprint(), поверх него добавлен pop_fore_over_char() — слой fore + полоса потолка, без оверлеев поз виса/полёта/подъёма (у стража их нет; появятся — портируем redraw_at_char2 общим кодом, а не догадками). 3. Упаковщик стража: тот же off-by-one, что уже ловили у Kid. load_chtab_from_file(id_chtab_5_guard, 750) даёт images[0] = res751, а рисование индексирует images[frame.image] — значит image=N это res(751+N), а не res(750+N). Из-за сдвига frame_166_stand_inactive рисовался как res767 (выпад) вместо res768 (стойка). Проверено в MAME: страж в комнатах 3 и 21 стоит в правильной позе; окклюзия подтверждена патчем Guard.x в живой сессии — при заходе за столб спрайт корректно срезается его передней гранью. Память: _CODE 26 149 -> 25 703, куча W2 805 -> 1245 Б, резидент W3 11 656 -> 12 819 (свободно 3565 Б), банк 1 236/16384 Б. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
| 1. `kid_data.h` → EMM-страница + `load_frame`/`cur_frame` | **сделан** | `_CODE` −3 247 Б; попутно найден и обойдён баг кодогенерации SDCC (см. ниже) |
|
||||
| 2. `pop_geom.c` (дедуп геометрии + PRNG) | **сделан** | −41 Б `_CODE`, −11 Б W3; ценность — не байты, а bank-safe слой |
|
||||
| 3. `pop_map` без вызовов графики | **сделан** (фазы 1a/1b) | через пометки перерисовки, см. ниже |
|
||||
| 4. Разгрузка/перебалансировка W3 | **сделан** | в резиденте остался ТОЛЬКО `pop_bg`; W3 14 376 → 11 632 (свободно 4 752 Б) |
|
||||
| 4. Разгрузка/перебалансировка W3 | **сделан** | резидент = `pop_bg` + `pop_gdraw` (отрисовка стража, 2026-07-29); W3 14 376 → 12 819 (свободно 3 565 Б) |
|
||||
| 5. Данные (`room_modif`, `dl1/dl2`, `_kbdraw_down`) | не начат | потенциал ~1.5 КБ |
|
||||
| 6. Контракт банка стражей + пробник | **пробник сделан** | `tests/w3bankgfx` — модель подтверждена в MAME, см. ниже |
|
||||
| 7. Дедуп семейства `draw_tile` | отложен по решению пользователя | «мороки много, выгода не так велика» |
|
||||
@@ -27,6 +27,15 @@
|
||||
W3-резидент 14 376 → 11 632 (свободно 2 008 → 4 752 Б). Сумма кода упала
|
||||
на ~3.2 КБ (данные Kid уехали в EMM), остальное — перераспределение.
|
||||
|
||||
**Замер 2026-07-29 (после стража).** Появление стража съело кучу до 805 Б;
|
||||
разгрузка — вынос ОТРИСОВКИ стража в резидент (`pop_gdraw.c`, `--w3`), логика
|
||||
и состояние остались в W1/W2, чтобы банк `guards.c` их видел (R2). Итог:
|
||||
`_CODE` 26 149 → 25 703, куча **805 → 1 245 Б**, W3-резидент 11 656 → 12 819
|
||||
(свободно 4 728 → 3 565 Б), банк 1 — 236 / 16 384 Б. Граница «что резидент»
|
||||
теперь формулируется одним правилом: **резидент = только то, что рисует и
|
||||
зовётся исключительно из главного цикла**; всё, что может понадобиться банку,
|
||||
остаётся в W1/W2.
|
||||
|
||||
### Что сделано вместо §5.3 (вынос loose в W3)
|
||||
|
||||
Вместо переноса кода между окнами выбран (по обсуждению с пользователем)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,12 +6,13 @@ EXAMPLE := roomtest
|
||||
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
||||
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
||||
MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи)
|
||||
# --w3 берёт ОДИН файл на флаг. В W3-резиденте держим ТОЛЬКО pop_bg —
|
||||
# отрисовку тайлов: она большая и её зовёт лишь главный цикл. Всё
|
||||
# остальное живёт в W1/W2, чтобы оставаться достижимым из будущих
|
||||
# __banked-модулей (стражи): из банка резидентная страница W3 не видна —
|
||||
# ни напрямую, ни транзитивно. См. docs/layout_plan_v2.md §2 (R2), §4.
|
||||
EXTRA_FLAGS ?= --gfx 256 --w3 pop_bg.c --bank 1=guards.c
|
||||
# --w3 берёт ОДИН файл на флаг (флагов можно несколько). В W3-резиденте
|
||||
# держим ТОЛЬКО отрисовку, которую зовёт лишь главный цикл: pop_bg (тайлы)
|
||||
# и pop_gdraw (спрайт стража). Всё остальное живёт в W1/W2, чтобы
|
||||
# оставаться достижимым из __banked-модулей (guards.c): из банка
|
||||
# резидентная страница W3 не видна — ни напрямую, ни транзитивно.
|
||||
# См. docs/layout_plan_v2.md §2 (R2), §4.
|
||||
EXTRA_FLAGS ?= --gfx 256 --w3 pop_bg.c --w3 pop_gdraw.c --bank 1=guards.c
|
||||
EXTRA_SRCS := pop_kid.c pop_ctrl.c pop_level.c pop_map.c pop_trob.c pop_redraw.c pop_geom.c pop_guard.c
|
||||
|
||||
BG_DIR := $(CURDIR)/../poc/res/bg
|
||||
@@ -62,4 +63,4 @@ $(KID_DATA): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py
|
||||
kid_data.h $(KID_BIN): $(TC)/pop_extract_kid_data.py
|
||||
cd $(TC) && python3 pop_extract_kid_data.py
|
||||
|
||||
$(EXAMPLE).exe: guards.c pop_guard.c pop_guard.h pop_cheat.h pop_bg.c pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA)
|
||||
$(EXAMPLE).exe: guards.c pop_guard.c pop_guard.h pop_gdraw.c pop_cheat.h pop_bg.c pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA)
|
||||
|
||||
@@ -1325,13 +1325,20 @@ static void other_overlay_tile(int row, int col)
|
||||
}
|
||||
}
|
||||
|
||||
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir, uint8_t frame, uint8_t action)
|
||||
/* Футпринт персонажа в тайлах (порт set_char_collision, seg006:0723).
|
||||
* Выделено из pop_fore_over_kid, потому что тем же счётом пользуется страж
|
||||
* (pop_fore_over_char): в оригинале это общий код для любого Char.
|
||||
* Результат — в файловых статиках, а не через 6 указателей: на Z80 такой
|
||||
* возврат стоил бы дороже самой функции. */
|
||||
static int8_t fp_cL, fp_cR, fp_cLraw, fp_rT, fp_rB, fp_rTraw;
|
||||
|
||||
static void char_footprint(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir)
|
||||
{
|
||||
int wh = (int)((w + 1) >> 1); /* char_width_half */
|
||||
int xl = obj_x / 2 + 58; /* char_x_left (58-база) */
|
||||
int xr;
|
||||
int8_t cL, cR, cLraw, rT, rB, r, c, rTraw;
|
||||
int8_t cL, cR, cLraw, rT, rB, rTraw;
|
||||
if (dir >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
|
||||
xr = xl + wh;
|
||||
cLraw = col_from_x(xl); /* до клампа: <0 = футпринт за левым швом */
|
||||
@@ -1359,6 +1366,17 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
* (нижняя грань стены / кромка пола) не перерисуется поверх Kid. Форсим
|
||||
* включение ряда rB-1. */
|
||||
if (rB > 0 && rT >= rB) rT = rB - 1;
|
||||
fp_cL = cL; fp_cR = cR; fp_cLraw = cLraw;
|
||||
fp_rT = rT; fp_rB = rB; fp_rTraw = rTraw;
|
||||
}
|
||||
|
||||
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir, uint8_t frame, uint8_t action)
|
||||
{
|
||||
int8_t cL, cR, cLraw, rT, rB, rTraw, r, c;
|
||||
char_footprint(obj_x, obj_y, w, h, dir);
|
||||
cL = fp_cL; cR = fp_cR; cLraw = fp_cLraw;
|
||||
rT = fp_rT; rB = fp_rB; rTraw = fp_rTraw;
|
||||
/* set_objtile_at_char (seg006:1833): тайл, при обработке которого спрайт
|
||||
* Kid попадает в midtable — база порядка окклюзии в other_overlay_tile.
|
||||
* run_jump (action 1) — нижний ряд/левая колонка габарита; вис/полёт/
|
||||
@@ -1427,3 +1445,33 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
}
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Передние грани тайлов поверх ПРОИЗВОЛЬНОГО персонажа (страж, дальше —
|
||||
* тень/скелет). В оригинале любой Char — запись midtable, а foretable
|
||||
* рисуется после всех midtable (draw_tile_fore, seg008:690), поэтому
|
||||
* колонна и кромка пола перекрывают всех одинаково.
|
||||
*
|
||||
* Отличие от pop_fore_over_kid: только слой fore (+ полоса потолка).
|
||||
* Оверлеи тела пола (draw_other_overlay) завязаны на позы Kid — вис,
|
||||
* полёт, подъём, — которых у стража нет: он ходит и дерётся на полу
|
||||
* (seq_77..seq_84). Появятся падающие/висящие стражи — тогда и общий
|
||||
* порт redraw_at_char2, а не догадки сейчас. */
|
||||
void pop_fore_over_char(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir)
|
||||
{
|
||||
int8_t r, c;
|
||||
char_footprint(obj_x, obj_y, w, h, dir);
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
for (r = fp_rT; r <= fp_rB; r++)
|
||||
for (c = fp_cL; c <= fp_cR; c++)
|
||||
fore_tile(r, c);
|
||||
if (fp_rTraw < 0) {
|
||||
g_clip_top = POP_YOFF;
|
||||
for (c = fp_cL; c <= fp_cR; c++) {
|
||||
ov_mark(COL_XH[c] * 8, 0, 64, CEIL_BAND_H);
|
||||
ceil_over_kid_tile(c);
|
||||
}
|
||||
g_clip_top = 0;
|
||||
}
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_
|
||||
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir, uint8_t frame, uint8_t action);
|
||||
|
||||
/* То же для НЕ-Kid персонажа (страж): только слой fore + полоса потолка,
|
||||
* без оверлеев поз виса/полёта/подъёма. obj_x — ЛОГИЧЕСКАЯ координата
|
||||
* (до ×8/7), как у kid_fp_obj_x(). */
|
||||
void pop_fore_over_char(int obj_x, int obj_y, uint16_t w, uint16_t h,
|
||||
int8_t dir);
|
||||
|
||||
/* Восстановить фон там, где в прошлых кадрах рисовался ОВЕРЛЕЙ
|
||||
* (draw_other_overlay/floor_overlay/полоса потолка): он выходит за
|
||||
* прямоугольник Kid, поэтому kid_heal его не убирает — на второй странице
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* pop_gdraw.c — ОТРИСОВКА стража. Резидент окна W3 (--w3 pop_gdraw.c).
|
||||
*
|
||||
* Почему отдельным файлом, а не внутри pop_guard.c: --w3 забирает МОДУЛЬ
|
||||
* целиком (код+rodata в 0xC000), а состояние стража и его логика обязаны
|
||||
* остаться в W1/W2 — из банка (guards.c) резидент W3 недостижим ни прямо,
|
||||
* ни транзитивно (docs/layout_plan_v2.md §2 R2). Значит граница проходит
|
||||
* ровно между «что рисует» (зовёт только главный цикл → резидент) и «что
|
||||
* знает» (зовёт и банк → W1/W2).
|
||||
*
|
||||
* Писучие статики резидента (--dataseg не трогаем) остаются в обычном
|
||||
* _DATA, то есть в W2 — сюда переезжает КОД, не данные.
|
||||
*/
|
||||
#include <graphics.h>
|
||||
#include <gfx.h>
|
||||
#include <sprite.h>
|
||||
#include "pop_guard.h"
|
||||
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
||||
#include "kid_data.h"
|
||||
|
||||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||||
#define GUARD_PAGES 5
|
||||
static atlas_t gp[GUARD_PAGES];
|
||||
static uint8_t g_ok;
|
||||
|
||||
/* Прямоугольник последней отрисовки ПО СТРАНИЦЕ дабл-буфера (как у Kid:
|
||||
* heal обязан стирать спрайт именно той страницы, в которую рисуем). */
|
||||
static int gx_l[2], gy_l[2];
|
||||
static uint16_t gw_l[2], gh_l[2];
|
||||
static uint8_t gvalid[2];
|
||||
|
||||
int pop_guard_load(void)
|
||||
{
|
||||
static const char *const names[GUARD_PAGES] = {
|
||||
"GUARD\\g0.atl", "GUARD\\g1.atl", "GUARD\\g2.atl",
|
||||
"GUARD\\g3.atl", "GUARD\\g4.atl"
|
||||
};
|
||||
uint8_t i;
|
||||
for (i = 0; i < GUARD_PAGES; i++)
|
||||
if (atlas_load(&gp[i], names[i]) != 0) return -1;
|
||||
g_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pop_guard_free(void)
|
||||
{
|
||||
uint8_t i;
|
||||
if (!g_ok) return;
|
||||
for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]);
|
||||
g_ok = 0;
|
||||
}
|
||||
|
||||
void pop_guard_heal(void)
|
||||
{
|
||||
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]);
|
||||
gvalid[p] = 0;
|
||||
}
|
||||
|
||||
void pop_guard_draw(void)
|
||||
{
|
||||
int obj_x, obj_y, top, fwd, fp_x;
|
||||
uint8_t page, idx, p, flip;
|
||||
const uint8_t *img;
|
||||
uint16_t w, h;
|
||||
|
||||
if (!g_ok || Guard.charid == 0 || pop_gframe.image == 255) return;
|
||||
/* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */
|
||||
fwd = (int)Guard.x + ((Guard.direction < 0) ? -pop_gframe.dx : pop_gframe.dx);
|
||||
obj_x = (fwd << 1) - 116;
|
||||
obj_y = (int)Guard.y + pop_gframe.dy;
|
||||
if ((int8_t)(pop_gframe.flags ^ Guard.direction) >= 0) obj_x++;
|
||||
fp_x = obj_x; /* ЛОГИЧЕСКАЯ X (до ×8/7) — футпринт */
|
||||
obj_x = obj_x * 8 / 7; /* calc_screen_x_coord */
|
||||
|
||||
page = (uint8_t)(pop_gframe.image >> 3);
|
||||
idx = (uint8_t)(pop_gframe.image & 7);
|
||||
if (page >= GUARD_PAGES) return;
|
||||
img = (const uint8_t *)atlas_image(&gp[page], idx);
|
||||
gfx_w0_map(gp[page].page);
|
||||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||
if (w && h) {
|
||||
int bx = obj_x;
|
||||
flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */
|
||||
if (flip) bx -= (int)w;
|
||||
top = obj_y - (int)h + 1;
|
||||
p = gfx_get_draw_page() & 1;
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||
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;
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
/* Передние грани тайлов ПОВЕРХ стража: в оригинале персонаж — запись
|
||||
* midtable, а foretable рисуется после всех midtable-записей, поэтому
|
||||
* колонна/кромка пола перекрывает ЛЮБОГО персонажа, не только Kid
|
||||
* (seg008:690 draw_tile_fore). Без этого страж виден сквозь передний
|
||||
* столб. */
|
||||
if (w && h)
|
||||
pop_fore_over_char(fp_x, obj_y, w, h, Guard.direction);
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
/*
|
||||
* pop_guard.c — состояние стража (W1/W2). См. pop_guard.h.
|
||||
* Логика — в банке (guards.c), отрисовка — из главного цикла.
|
||||
* pop_guard.c — СОСТОЯНИЕ и ЛОГИКА стража (W1/W2). См. pop_guard.h.
|
||||
*
|
||||
* Здесь только то, что должно оставаться достижимым из банка (guards.c):
|
||||
* поля Guard, HP, вход в комнату, чит-убийство, чтение кадра из таблицы.
|
||||
* ОТРИСОВКА — в pop_gdraw.c, резиденте W3 (её зовёт лишь главный цикл).
|
||||
*/
|
||||
#include <graphics.h>
|
||||
#include <gfx.h>
|
||||
#include <sprite.h>
|
||||
#include <stdint.h>
|
||||
#include "pop_guard.h"
|
||||
#include "pop_geom.h"
|
||||
#include "pop_level.h"
|
||||
#include "pop_kid.h" /* play_seq/kid_set_seq пока общие (seqtbl один) */
|
||||
#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */
|
||||
#include "pop_kid.h" /* pop_kid_data_frame — таблицы в EMM-странице */
|
||||
#include "kid_data.h"
|
||||
|
||||
pop_char_t Guard;
|
||||
uint8_t guardhp_curr, guardhp_max;
|
||||
int8_t guardhp_delta;
|
||||
|
||||
/* Кадр стража (порт cur_frame для Char). Глобальный, а не статик: его
|
||||
* заполняет логика (W1/W2), а читает отрисовка из резидента W3. */
|
||||
kframe pop_gframe;
|
||||
|
||||
uint8_t pop_guard_present(void)
|
||||
{
|
||||
return (uint8_t)(Guard.charid != 0 && Guard.alive < 0 && guardhp_curr != 0);
|
||||
@@ -45,51 +49,16 @@ void pop_guard_kill(void)
|
||||
Guard.alive = 0;
|
||||
}
|
||||
|
||||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||||
#define GUARD_PAGES 5
|
||||
static atlas_t gp[GUARD_PAGES];
|
||||
static uint8_t g_ok;
|
||||
|
||||
/* Прямоугольник последней отрисовки ПО СТРАНИЦЕ дабл-буфера (как у Kid:
|
||||
* heal обязан стирать спрайт именно той страницы, в которую рисуем). */
|
||||
static int gx_l[2], gy_l[2];
|
||||
static uint16_t gw_l[2], gh_l[2];
|
||||
static uint8_t gvalid[2];
|
||||
|
||||
/* Кадр стража (порт cur_frame для Char): таблица у стражей СВОЯ
|
||||
* (frame_tbl_guard), и индексируется как frame + add_frame − 149, где
|
||||
/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у
|
||||
* стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где
|
||||
* add_frame = 70 для кадров 102..106 (seg006:535). */
|
||||
static kframe gframe;
|
||||
|
||||
int pop_guard_load(void)
|
||||
{
|
||||
static const char *const names[GUARD_PAGES] = {
|
||||
"GUARD\\g0.atl", "GUARD\\g1.atl", "GUARD\\g2.atl",
|
||||
"GUARD\\g3.atl", "GUARD\\g4.atl"
|
||||
};
|
||||
uint8_t i;
|
||||
for (i = 0; i < GUARD_PAGES; i++)
|
||||
if (atlas_load(&gp[i], names[i]) != 0) return -1;
|
||||
g_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pop_guard_free(void)
|
||||
{
|
||||
uint8_t i;
|
||||
if (!g_ok) return;
|
||||
for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]);
|
||||
g_ok = 0;
|
||||
}
|
||||
|
||||
/* load_frame для стража (seg006:0293 ветка charid_2_guard). */
|
||||
static void guard_load_frame(void)
|
||||
void pop_guard_load_frame(void)
|
||||
{
|
||||
int16_t idx = (int16_t)Guard.frame;
|
||||
if (idx >= 102 && idx < 107) idx += 70; /* add_frame */
|
||||
idx -= 149;
|
||||
if (idx < 0 || idx >= KID_NGFRAMES) { gframe.image = 255; return; }
|
||||
pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&gframe);
|
||||
if (idx < 0 || idx >= KID_NGFRAMES) { pop_gframe.image = 255; return; }
|
||||
pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&pop_gframe);
|
||||
}
|
||||
|
||||
void pop_guard_enter(uint8_t room)
|
||||
@@ -118,48 +87,5 @@ void pop_guard_enter(uint8_t room)
|
||||
Guard.frame = 166;
|
||||
guardhp_max = guardhp_curr = 3; /* get_guard_hp: skill 0, уровень 1 */
|
||||
guardhp_delta = 0;
|
||||
guard_load_frame();
|
||||
}
|
||||
|
||||
void pop_guard_heal(void)
|
||||
{
|
||||
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]);
|
||||
gvalid[p] = 0;
|
||||
}
|
||||
|
||||
void pop_guard_draw(void)
|
||||
{
|
||||
int obj_x, obj_y, top, fwd;
|
||||
uint8_t page, idx, p, flip;
|
||||
const uint8_t *img;
|
||||
uint16_t w, h;
|
||||
|
||||
if (!g_ok || Guard.charid == 0 || gframe.image == 255) return;
|
||||
/* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */
|
||||
fwd = (int)Guard.x + ((Guard.direction < 0) ? -gframe.dx : gframe.dx);
|
||||
obj_x = (fwd << 1) - 116;
|
||||
obj_y = (int)Guard.y + gframe.dy;
|
||||
if ((int8_t)(gframe.flags ^ Guard.direction) >= 0) obj_x++;
|
||||
obj_x = obj_x * 8 / 7; /* calc_screen_x_coord */
|
||||
|
||||
page = (uint8_t)(gframe.image >> 3);
|
||||
idx = (uint8_t)(gframe.image & 7);
|
||||
if (page >= GUARD_PAGES) return;
|
||||
img = (const uint8_t *)atlas_image(&gp[page], idx);
|
||||
gfx_w0_map(gp[page].page);
|
||||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||
if (w && h) {
|
||||
int bx = obj_x;
|
||||
flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */
|
||||
if (flip) bx -= (int)w;
|
||||
top = obj_y - (int)h + 1;
|
||||
p = gfx_get_draw_page() & 1;
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0);
|
||||
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;
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
pop_guard_load_frame();
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
* линкуются В СТРАНИЦУ БАНКА и снаружи не читаются, поэтому банк —
|
||||
* только код;
|
||||
* - ЛОГИКА (ИИ, боёвка) — в guards.c, собираемом как --bank 1=guards.c;
|
||||
* - ОТРИСОВКА стража — из W1/W2 (рядом с kid_draw): из банка примитивы
|
||||
* libbgi звать можно, но резидент W3 (pop_bg) — нельзя, поэтому слой
|
||||
* фона/окклюзии за стража делает главный цикл.
|
||||
* - ОТРИСОВКА стража — в pop_gdraw.c, РЕЗИДЕНТЕ W3 (--w3): её зовёт
|
||||
* только главный цикл, а из банка резидент недостижим. Так код
|
||||
* отрисовки не занимает дефицитные W1/W2 (куча).
|
||||
*
|
||||
* Поля char_type — как в оригинале (types.h:302), чтобы порт seg005/seg006
|
||||
* ложился один в один.
|
||||
@@ -18,6 +18,7 @@
|
||||
#define POP_GUARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kid_data.h" /* kframe */
|
||||
|
||||
/* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */
|
||||
#define CHARID_0_KID 0
|
||||
@@ -45,6 +46,11 @@ extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты
|
||||
extern uint8_t guardhp_curr, guardhp_max;
|
||||
extern int8_t guardhp_delta; /* <0 = в этом кадре отняли HP */
|
||||
|
||||
/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2,
|
||||
* читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */
|
||||
extern kframe pop_gframe;
|
||||
void pop_guard_load_frame(void);
|
||||
|
||||
/* Есть ли живой страж в отрисованной комнате (для главного цикла). */
|
||||
uint8_t pop_guard_present(void);
|
||||
|
||||
@@ -58,9 +64,8 @@ void pop_guard_free(void);
|
||||
void pop_guard_enter(uint8_t room);
|
||||
|
||||
/* Нарисовать текущий кадр стража на back-странице (банк GFX_BANK_SPRITE,
|
||||
* как kid_draw) и стереть прошлый (heal). Звать из главного цикла рядом
|
||||
* с kid_draw: рисовать из БАНКА нельзя только резидентом W3, а тут мы в
|
||||
* W1/W2. */
|
||||
* как kid_draw) и стереть прошлый (heal). Реализация — pop_gdraw.c
|
||||
* (резидент W3), поэтому звать ТОЛЬКО из главного цикла, не из банка. */
|
||||
void pop_guard_heal(void);
|
||||
void pop_guard_draw(void);
|
||||
|
||||
|
||||
@@ -28,12 +28,20 @@ PAL_BASE = 0x90 # слоты палитры Sprinter под страж
|
||||
COLOR = 2 # level.guards_color обоих стражей уровня 1
|
||||
SHIFT = 3 # g[id>>3], idx id&7 (спрайты крупные)
|
||||
MASK = (1 << SHIFT) - 1
|
||||
MAX_ID = 34 # res751..res784
|
||||
MAX_ID = 33 # атлас-индексы 0..33 = res751..res784
|
||||
|
||||
|
||||
def load_guard(id):
|
||||
"""id -> res(750+id).png -> (w, h, 8bpp COLUMN-MAJOR); 0 -> 0xFF (прозр.)."""
|
||||
path = os.path.join(GUARD_DIR, f"res{750 + id}.png")
|
||||
"""Атлас-индекс id (== frame.image) -> res(751+id).png -> (w, h, 8bpp
|
||||
COLUMN-MAJOR); 0 -> 0xFF (прозрачность).
|
||||
|
||||
Off-by-one как у Kid (см. pop_pack_kid.load_kid): load_chtab_from_file
|
||||
зовётся с ресурсом 750, а images[i-1] = res(750+i), то есть images[0] =
|
||||
res751. Рисование берёт get_image(chtab, obj_id+1 − 1) = images[image],
|
||||
значит frame.image=N -> res(751+N). С res(750+N) страж стоял в позе
|
||||
предыдущего кадра (замечено на frame_166_stand_inactive: image 17
|
||||
рисовался как res767 — выпад — вместо res768). """
|
||||
path = os.path.join(GUARD_DIR, f"res{751 + id}.png")
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
im = Image.open(path)
|
||||
@@ -55,7 +63,7 @@ def main():
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
pages = {}
|
||||
n = 0
|
||||
for id in range(1, MAX_ID + 1):
|
||||
for id in range(0, MAX_ID + 1):
|
||||
g = load_guard(id)
|
||||
if g is None:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user