af5f0a4638
Разгрузка 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>
85 lines
3.7 KiB
Python
85 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
pop_pack_guard.py — упаковщик спрайтов СТРАЖА (chtab_5_guard, база 750).
|
||
|
||
Оригинал грузит стражей из отдельного .DAT по типу уровня
|
||
(GUARD/FAT/SKEL/VIZIER/SHADOW, seg000:1092); уровень 1 — обычный «guard»,
|
||
то есть data/GUARD/res(750+id).png. Палитра — НЕ из PNG: оригинал
|
||
подменяет её из res10.bin (guard_palettes, 7 палитр по 16 цветов, 6-бит) по
|
||
level.guards_color; на уровне 1 у обоих стражей color = 2.
|
||
|
||
Выход: poc/res/guard/g0..gN.atl (по 8 спрайтов на страницу, адресация
|
||
id>>3 / id&7 — как у Kid) + группа палитры 0x90..0x9F в общий kid.pal
|
||
(его собирает pop_pack_kid.py, см. GUARD_PAL_BASE там).
|
||
"""
|
||
import os
|
||
import sys
|
||
from PIL import Image
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, HERE)
|
||
import pop_pack_bg as B
|
||
from pop_pack_kid import transpose_cols
|
||
|
||
GUARD_DIR = os.path.join(HERE, "..", "SDLPoP", "data", "GUARD")
|
||
PAL_BIN = os.path.join(HERE, "..", "SDLPoP", "data", "PRINCE", "res10.bin")
|
||
OUT_DIR = os.path.join(HERE, "..", "poc", "res", "guard")
|
||
PAL_BASE = 0x90 # слоты палитры Sprinter под стража
|
||
COLOR = 2 # level.guards_color обоих стражей уровня 1
|
||
SHIFT = 3 # g[id>>3], idx id&7 (спрайты крупные)
|
||
MASK = (1 << SHIFT) - 1
|
||
MAX_ID = 33 # атлас-индексы 0..33 = res751..res784
|
||
|
||
|
||
def load_guard(id):
|
||
"""Атлас-индекс 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)
|
||
if im.mode != "P":
|
||
B.die(f"res{750+id}: mode {im.mode}, ожидался P")
|
||
src = im.tobytes()
|
||
rowmajor = bytes((0xFF if v == 0 else PAL_BASE + v) for v in src)
|
||
return im.width, im.height, transpose_cols(im.width, im.height, rowmajor)
|
||
|
||
|
||
def guard_palette():
|
||
"""16 цветов (r,g,b 0..63) из res10.bin для COLOR."""
|
||
b = open(PAL_BIN, "rb").read()
|
||
off = 0x30 * COLOR - 0x30
|
||
return [(b[off + i * 3], b[off + i * 3 + 1], b[off + i * 3 + 2]) for i in range(16)]
|
||
|
||
|
||
def main():
|
||
os.makedirs(OUT_DIR, exist_ok=True)
|
||
pages = {}
|
||
n = 0
|
||
for id in range(0, MAX_ID + 1):
|
||
g = load_guard(id)
|
||
if g is None:
|
||
continue
|
||
pages.setdefault(id >> SHIFT, {})[id & MASK] = g
|
||
n += 1
|
||
if not pages:
|
||
B.die("нет спрайтов стража")
|
||
total = 0
|
||
for pg in range(max(pages) + 1):
|
||
nm = f"g{pg}.atl"
|
||
cnt, sz = B.pack_atlas(os.path.join(OUT_DIR, nm), pages.get(pg, {}))
|
||
total += sz
|
||
print(f" {nm}: {len(pages.get(pg, {}))} спрайтов, каталог {cnt}, {sz} Б")
|
||
print(f"ИТОГО: {n} спрайтов стража, {max(pages)+1} EMM-страниц, {total} Б -> {OUT_DIR}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|