applications/PoP: порт Prince of Persia — PoC (roomtest) + пайплайн
Порт PoP на Sprinter. Текущий PoC — applications/PoP/roomtest/: комната 1 (фон-композиция тайлов) + Kid с управлением на raw-клавиатуре и коллизией с картой. - roomtest — pop_bg (фон), pop_kid (спрайты Kid, column-major флип, seqtbl-анимация), pop_ctrl (порт control() PoP на held-state kbd_raw), pop_map (коллизия seg004/005: бег/стоп у стены, падение/приземление, отскок seq_47, вертикальный прыжок K4.1). MEMORY=small (DATA сразу за CODE, ~23КБ кода не лезет в huge). - toolchain (PoP) — pop_pack_kid/pop_pack_bg/render_room/extract — распаковка res-графики MSDOS в атласы + композиция комнат. - toolchain/ (корень) — make_hdd.sh (быстрый HDD-тест вместо FDD), png_strip.py / room_compose.py (ассет-пайплайн). - docs — PORT_PLAN, KID_PLAN, форматы ресурсов (Apple II / MSDOS / DAT). - bgtest/coltest/poc — ранние PoC (фон, коллизия, первый прототип). .gitignore: build-артефакты applications/*/*/*; исключены внешние референс-репозитории (SDLPoP/mininim/PR/Apple-II — свои git-клоны) и оригинальные game-данные MSDOS/ (копирайт, только для реверса форматов). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
build_room_palette.py — сводит kid.pal (уже собран
|
||||
gen_kid_placeholder.py) + Floor + Wall тайлы в ОДНУ merged-палитру
|
||||
room.pal, грузимую ОДИН раз через gfx_pal_fload (см. poc.c) — экран
|
||||
показывает Kid и тайлы ОДНОВРЕМЕННО, значит их "свои" цвета обязаны
|
||||
жить в одной палитре (см. --seed-pal в toolchain/png_strip.py).
|
||||
|
||||
На вход — только типы, которые Фаза 1 реально рисует (room_draw
|
||||
рисует только загруженные атласы; Floor/Wall — единственные твёрдые
|
||||
типы с нетривиальной картинкой заглушки, см. gen_tile_placeholders.py
|
||||
draw_floor_like/draw_wall_like). Остальные 30 типов из Фазы 1
|
||||
исключены полностью (не грузятся, не рисуются, PORT_PLAN.md §5) —
|
||||
добавить сюда, когда понадобятся.
|
||||
|
||||
Перезаписывает res/tiles/tile01.raw/.atl (Floor) и tile14.raw/.atl
|
||||
(Wall, TILE_WALL=0x14 — room.c ищет "tile%02x.atl" ОБЯЗАТЕЛЬНО в hex,
|
||||
не decimal!) с индексами палитры, согласованными с kid.pal; пишет
|
||||
kid+floor+wall.pal рядом с kid.pal (poc.c грузит её вместо отдельной
|
||||
kid.pal).
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
PROJ_ROOT = HERE.parents[3]
|
||||
TILES_DIR = HERE.parent / "res" / "tiles"
|
||||
PNG_STRIP = PROJ_ROOT / "toolchain" / "png_strip.py"
|
||||
MKATLAS = PROJ_ROOT / "toolchain" / "mkatlas.py"
|
||||
|
||||
ROOM_TILE_W = 32
|
||||
ROOM_ROW_H = 64
|
||||
|
||||
KID_PAL = HERE / "kid.pal"
|
||||
OUT_PAL = HERE / "room.pal"
|
||||
|
||||
TYPES = [0x01, 0x14, 0x03, 0x13, 0x0E, 0x0B] # Floor, Wall, Pillar, Torch, Debris, Loose
|
||||
# (2026-07-16: временное ограничение до 2 типов снято — корень
|
||||
# вероятностного "снега" найден: tile_atlas_load()/room_draw() трогали
|
||||
# W3 транзитивно (через atlas_load/gfx_blit_part) ИЗ БАНКА, который сам
|
||||
# живёт в W3 — конфликт. Обе функции переехали в room_draw.c (HOME),
|
||||
# см. её шапку.)
|
||||
|
||||
|
||||
def run(cmd):
|
||||
print("+", " ".join(str(c) for c in cmd))
|
||||
subprocess.run([str(c) for c in cmd], check=True)
|
||||
|
||||
|
||||
def main():
|
||||
if not KID_PAL.exists():
|
||||
sys.exit(f"build_room_palette: нет {KID_PAL} — сначала gen_kid_placeholder.py")
|
||||
|
||||
seed = KID_PAL
|
||||
for t in TYPES:
|
||||
base = f"tile{t:02x}" # room.c ищет "tile%02x.atl" — hex, не decimal
|
||||
png = TILES_DIR / f"{t:X}-0.png"
|
||||
raw = TILES_DIR / f"{base}.raw"
|
||||
pal = TILES_DIR / f"{base}_merged.pal"
|
||||
atl = TILES_DIR / f"{base}.atl"
|
||||
|
||||
run([sys.executable, PNG_STRIP, raw, pal, png,
|
||||
"--canvas", f"{ROOM_TILE_W}x{ROOM_ROW_H}",
|
||||
"--anchor", "top-left", "--seed-pal", seed])
|
||||
run([sys.executable, MKATLAS, atl, f"{raw}:{ROOM_TILE_W}x{ROOM_ROW_H}:1x1"])
|
||||
seed = pal
|
||||
|
||||
import shutil
|
||||
shutil.copyfile(seed, OUT_PAL)
|
||||
print(f"build_room_palette: итоговая палитра -> {OUT_PAL}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
build_tile_atlases.py — собирает tileNN.atl для всех 32 типов тайлов
|
||||
из applications/PoP/poc/res/tiles/{TYPE:X}-{N}.png (см.
|
||||
gen_tile_placeholders.py). Для каждого типа находит ВСЕ файлы
|
||||
{TYPE:X}-*.png (сколько бы вариантов ни было — 1 заглушка или много
|
||||
настоящих), сортирует по номеру варианта и собирает через
|
||||
png_strip.py + mkatlas.py. Типы без ни одного файла — пропускаются
|
||||
(room_draw их и так не рисует).
|
||||
|
||||
Запускать заново каждый раз, когда добавили/заменили картинки в
|
||||
res/tiles/ — атласы просто пересоберутся.
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
PROJ_ROOT = HERE.parents[3] # .../C-Compiler
|
||||
TILES_DIR = HERE.parent / "res" / "tiles"
|
||||
OUT_DIR = HERE.parent / "res" / "tiles"
|
||||
PNG_STRIP = PROJ_ROOT / "toolchain" / "png_strip.py"
|
||||
MKATLAS = PROJ_ROOT / "toolchain" / "mkatlas.py"
|
||||
|
||||
ROOM_TILE_W = 32
|
||||
ROOM_ROW_H = 64 # см. room.c room_row_h — держать в синхроне
|
||||
|
||||
PATTERN = re.compile(r"^([0-9A-Fa-f]+)-(\d+)\.png$")
|
||||
|
||||
|
||||
def run(cmd):
|
||||
print("+", " ".join(str(c) for c in cmd))
|
||||
subprocess.run([str(c) for c in cmd], check=True)
|
||||
|
||||
|
||||
def main():
|
||||
by_type = {}
|
||||
for f in TILES_DIR.glob("*.png"):
|
||||
m = PATTERN.match(f.name)
|
||||
if not m:
|
||||
continue
|
||||
t = int(m.group(1), 16)
|
||||
n = int(m.group(2))
|
||||
by_type.setdefault(t, {})[n] = f
|
||||
|
||||
if not by_type:
|
||||
sys.exit(f"build_tile_atlases: нет файлов TYPE-N.png в {TILES_DIR}")
|
||||
|
||||
built = 0
|
||||
for t in sorted(by_type):
|
||||
variants = by_type[t]
|
||||
files = [variants[n] for n in sorted(variants)]
|
||||
strip_raw = OUT_DIR / f"tile{t:02x}.raw"
|
||||
strip_pal = OUT_DIR / f"tile{t:02x}.pal"
|
||||
atl = OUT_DIR / f"tile{t:02x}.atl"
|
||||
|
||||
run([sys.executable, PNG_STRIP, strip_raw, strip_pal,
|
||||
*files, "--canvas", f"{ROOM_TILE_W}x{ROOM_ROW_H}",
|
||||
"--anchor", "top-left"])
|
||||
run([sys.executable, MKATLAS, atl,
|
||||
f"{strip_raw}:{ROOM_TILE_W}x{ROOM_ROW_H}:{len(files)}x1"])
|
||||
built += 1
|
||||
|
||||
print(f"build_tile_atlases: собрано {built} атласов "
|
||||
f"(типы: {', '.join('%X' % t for t in sorted(by_type))})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
extract_room.py — вырезает ОДНУ комнату (foretable+backtable, 30+30
|
||||
байт) + стартовую позицию уровня (start_room/start_pos/start_dir, 3
|
||||
байта) из level-ресурса формата "res20NN.bin" (SDLPoP data/LEVELS/).
|
||||
|
||||
Layout подтверждён структурным разбором: файл начинается СРАЗУ с
|
||||
foretable[720] (24 комнаты * 30 тайлов), затем backtable[720] — без
|
||||
заголовка (applications/PoP/docs/POP-DAT-FormatSpecifications.pdf §3.4
|
||||
+ декод байт 2026-07-15). Тайл tileOffset 0-9 = верхний ряд, 10-19 =
|
||||
средний, 20-29 = нижний (см. level.h level_tile()).
|
||||
|
||||
Смещение start_room/start_pos/start_dir НЕ сразу после links (как
|
||||
предполагалось изначально) — сверено со структурой level_type в
|
||||
applications/PoP/SDLPoP/src/types.h (маленький структурный факт,
|
||||
порядок полей, не объём кода): fg[720]+bg[720]+doorlinks1[256]+
|
||||
doorlinks2[256]+roomlinks[96]+used_rooms[1]+roomxs[24]+roomys[24]+
|
||||
fill_1[15] = 2112 -> start_room(u8), start_pos(u8), start_dir(s8)
|
||||
(2026-07-16, подтверждено на res2001.bin: start_room=1, start_pos=0,
|
||||
start_dir=-1 — согласуется с links[room1] и решулось эмпирически
|
||||
через таблицу room-links, которая идеально легла на смещение 1952).
|
||||
|
||||
Использование:
|
||||
extract_room.py LEVEL.bin ROOM OUT.dat
|
||||
|
||||
Выход: OUT.dat — 63 байта (fore[30]+back[30]+start_room+start_pos+
|
||||
start_dir), формат level_load_room() (room.c). ROOM — 1-based (как в
|
||||
файле/level.h); start_* — общеуровневые (не зависят от ROOM).
|
||||
"""
|
||||
import sys
|
||||
|
||||
FORETABLE_OFF = 0
|
||||
BACKTABLE_OFF = 720
|
||||
ROOM_TILES = 30
|
||||
START_OFF = 720 + 720 + 256 + 256 + 96 + 1 + 24 + 24 + 15 # 2112
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
sys.exit(__doc__.strip())
|
||||
src, room_s, out = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
room = int(room_s)
|
||||
|
||||
data = open(src, "rb").read()
|
||||
if len(data) < START_OFF + 3:
|
||||
sys.exit(f"extract_room: {src}: {len(data)} Б — меньше ожидаемых "
|
||||
f"{START_OFF + 3} (foretable+backtable+...+start_position)")
|
||||
|
||||
base = (room - 1) * ROOM_TILES
|
||||
fore = data[FORETABLE_OFF + base:FORETABLE_OFF + base + ROOM_TILES]
|
||||
back = data[BACKTABLE_OFF + base:BACKTABLE_OFF + base + ROOM_TILES]
|
||||
start = data[START_OFF:START_OFF + 3]
|
||||
open(out, "wb").write(fore + back + start)
|
||||
print(f"extract_room: комната {room} из {src} -> {out} "
|
||||
f"({len(fore)}+{len(back)}+{len(start)} Б); "
|
||||
f"start_room={start[0]} start_pos={start[1]} "
|
||||
f"start_dir={start[2] if start[2] < 128 else start[2] - 256}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
gen_kid_placeholder.py — временная ЗАГЛУШКА персонажа для PoC.
|
||||
|
||||
НЕ используем графику оригинальной Prince of Persia (авторские права
|
||||
Broderbund/Ubisoft) — PoC проверяет управление/коллизии/движок, не
|
||||
визуальную точность (applications/PoP/docs/PORT_PLAN.md §5). Вместо
|
||||
этого берём уже лицензированный спрайт-пак, который в проекте и так
|
||||
используется (examples/rpgwalk/conv_sprites.py) —
|
||||
third_party/16x16-RPG-characters, один персонаж (индекс 0) той же
|
||||
раскладкой: 12 кадров (dir*3+frame, dir 0=вниз/1=влево/2=вправо/3=вверх,
|
||||
3 кадра маятника на направление).
|
||||
|
||||
Замену на настоящую графику Кида (если/когда появится легально чистый
|
||||
источник) пользователь делает сам — вне этого скрипта.
|
||||
|
||||
Выход: kid.raw (16×192, 12 кадров) + kid.pal (EGA 16 + цвета спрайта).
|
||||
"""
|
||||
from PIL import Image
|
||||
|
||||
SRC = ("../../../../third_party/16x16-RPG-characters/sprites/"
|
||||
"old-style/02-bard.png")
|
||||
CHAR = 0 # какой из 8 персонажей листа берём
|
||||
|
||||
EGA = [ # B, G, R, pad — как в libbgi/bgi256/_bgi_mode_set.c
|
||||
(0, 0, 0), (168, 0, 0), (0, 168, 0), (168, 168, 0),
|
||||
(0, 0, 168), (168, 0, 168), (0, 84, 168), (168, 168, 168),
|
||||
(84, 84, 84), (255, 84, 84), (84, 255, 84), (255, 255, 84),
|
||||
(84, 84, 255), (255, 84, 255), (84, 255, 255), (255, 255, 255),
|
||||
]
|
||||
BASE = 16
|
||||
|
||||
im = Image.open(SRC).convert("RGBA")
|
||||
assert im.size == (192, 128), im.size
|
||||
|
||||
colors = {}
|
||||
|
||||
|
||||
def px(x, y):
|
||||
r, g, b, a = im.getpixel((x, y))
|
||||
if a < 128:
|
||||
return 0xFF
|
||||
key = (r, g, b)
|
||||
if key not in colors:
|
||||
assert BASE + len(colors) < 0xFF
|
||||
colors[key] = BASE + len(colors)
|
||||
return colors[key]
|
||||
|
||||
|
||||
bx, by = (CHAR % 4) * 48, (CHAR // 4) * 64
|
||||
data = bytearray()
|
||||
for d in range(4):
|
||||
for f in range(3):
|
||||
for y in range(16):
|
||||
for x in range(16):
|
||||
data.append(px(bx + f * 16 + x, by + d * 16 + y))
|
||||
open("kid.raw", "wb").write(data)
|
||||
|
||||
pal = bytearray()
|
||||
for b, g, r in EGA:
|
||||
pal += bytes((b, g, r, 0))
|
||||
for (r, g, b) in colors:
|
||||
pal += bytes((b, g, r, 0))
|
||||
open("kid.pal", "wb").write(pal)
|
||||
|
||||
print(f"gen_kid_placeholder: kid.raw 16x192 (12 кадров), "
|
||||
f"kid.pal EGA+{len(colors)} цветов ({len(pal)} Б)")
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
gen_tile_placeholders.py — ЗАГЛУШКИ для 32 типов тайлов (не графика
|
||||
оригинала!) — простая процедурная геометрия (кирпичная кладка линиями,
|
||||
плоская поверхность и т.п.), чтобы визуально читалось "это стена"/
|
||||
"это пол", пока не готовы настоящие ассеты (см. room.h/room.c:
|
||||
tile_atlas[TYPE], вариант 0 — "index"-картинка на тип).
|
||||
|
||||
Выход: applications/PoP/poc/res/tiles/{TYPE:X}-0.png — 32 файла,
|
||||
32x64 (ROOM_TILE_W x room_row_h), RGBA.
|
||||
|
||||
Для твёрдых типов (Фаза 1: Floor/Wall/Pillar/PillarBot/PillarTop/
|
||||
BalconyL/R) — функциональная заглушка (см. draw_wall_like/draw_floor
|
||||
ниже); Lattice-типы (0x19-0x1D) — узкая проходимая колонна той же
|
||||
кладки (draw_lattice_like, см. reference-скриншот оригинала); для
|
||||
остальных (декоративные/интерактивные, вне объёма Фазы 1) — просто
|
||||
цвет + подпись, этого достаточно для отладки конвейера. Empty (0x00)
|
||||
— полностью прозрачно (ничего не рисуется).
|
||||
|
||||
По мере готовности настоящих ассетов — рядом кладутся 0-1.png,
|
||||
0-2.png и т.д. (следующие варианты той же ленты), см. room.h.
|
||||
"""
|
||||
import colorsys
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
W, H = 32, 64
|
||||
|
||||
NAMES = [
|
||||
"Empty", "Floor", "Spikes", "Pillar", "Gate", "StuckBtn", "DropBtn",
|
||||
"Tapestry", "PillarBot", "PillarTop", "Potion", "Loose", "TapestryTop",
|
||||
"Mirror", "Debris", "RaiseBtn", "ExitL", "ExitR", "Chopper", "Torch",
|
||||
"Wall", "Skeleton", "Sword", "BalconyL", "BalconyR", "LatticePillar",
|
||||
"LatticeSupport", "LatticeSmall", "LatticeL", "LatticeR", "TorchDebris",
|
||||
"Null",
|
||||
]
|
||||
assert len(NAMES) == 32
|
||||
|
||||
WALL_LIKE = {0x03, 0x08, 0x09, 0x14, 0x17, 0x18} # Pillar/PillarBot/Top/Wall/BalconyL/R
|
||||
FLOOR_LIKE = {0x01} # Floor
|
||||
# Lattice — узкие декоративные каменные колонны (см. reference-скриншот
|
||||
# оригинала 2026-07-16): Кид физически проходит МИМО них (не полноценная
|
||||
# стена на всю ширину тайла) — колонна той же STONE/MORTAR кладки, но
|
||||
# ýже тайла, с прозрачными полями по бокам. (col_w, align):
|
||||
# align: "center"/"left"/"right".
|
||||
LATTICE_LIKE = {
|
||||
0x19: (14, "center"), # LatticePillar
|
||||
0x1A: (18, "center"), # LatticeSupport — потолще
|
||||
0x1B: (10, "center"), # LatticeSmall — потоньше
|
||||
0x1C: (14, "left"), # LatticeLeft
|
||||
0x1D: (14, "right"), # LatticeRight
|
||||
}
|
||||
# Плоский однотонный цвет БЕЗ текста (не через антиалиасинг шрифта) —
|
||||
# держит "своих" цветов в палитре по 1 на тип, иначе объединённая
|
||||
# палитра (--seed-pal) быстро упирается в лимит 0xFF (см.
|
||||
# pop_tile_atlas_palette_merge в памяти).
|
||||
FLAT_LIKE = {0x13: (200, 90, 30, 255), # Torch — тёплый оранжевый
|
||||
0x0E: (110, 90, 70, 255), # Debris — серо-бурый
|
||||
0x0B: (140, 120, 60, 255)} # Loose — тускло-жёлтый (плита)
|
||||
|
||||
STONE = (150, 150, 158, 255)
|
||||
MORTAR = (90, 90, 98, 255)
|
||||
GROUND = (120, 100, 80, 255)
|
||||
GROUND_TOP = (170, 150, 120, 255)
|
||||
|
||||
out_dir = Path(__file__).resolve().parent.parent / "res" / "tiles"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
font = ImageFont.load_default()
|
||||
except Exception:
|
||||
font = None
|
||||
|
||||
|
||||
def draw_wall_like(im):
|
||||
"""Кирпичная кладка — просто ряды прямоугольников со смещением
|
||||
в шахматном порядке и линиями раствора между ними (обобщённый
|
||||
геометрический приём, не стиль конкретной игры)."""
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle([0, 0, W - 1, H - 1], fill=STONE)
|
||||
brick_h = 12
|
||||
brick_w = 16
|
||||
row = 0
|
||||
y = 0
|
||||
while y < H:
|
||||
offset = (brick_w // 2) if (row % 2) else 0
|
||||
x = -brick_w + offset
|
||||
while x < W:
|
||||
draw.rectangle([x, y, x + brick_w - 2, y + brick_h - 2],
|
||||
outline=MORTAR, width=1)
|
||||
x += brick_w
|
||||
y += brick_h
|
||||
row += 1
|
||||
|
||||
|
||||
def draw_lattice_like(im, col_w, align):
|
||||
"""Узкая колонна кирпичной кладки шириной col_w<W, остальное — прозрачно
|
||||
(проходимо мимо). align выбирает, к какому краю тайла её прижать.
|
||||
Колонна ýже одного "кирпича" — рисуем стопкой блоков (горизонтальные
|
||||
линии раствора), без горизонтального смещения рядов, как у широкой
|
||||
стены (там оно не помещается)."""
|
||||
if align == "left":
|
||||
x0 = 0
|
||||
elif align == "right":
|
||||
x0 = W - col_w
|
||||
else:
|
||||
x0 = (W - col_w) // 2
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle([x0, 0, x0 + col_w - 1, H - 1], fill=STONE)
|
||||
brick_h = 12
|
||||
y = brick_h
|
||||
while y < H:
|
||||
draw.line([(x0, y), (x0 + col_w - 1, y)], fill=MORTAR, width=1)
|
||||
y += brick_h
|
||||
draw.rectangle([x0, 0, x0 + col_w - 1, H - 1], outline=MORTAR, width=1)
|
||||
|
||||
|
||||
def draw_floor_like(im):
|
||||
"""Пол — плоская поверхность: верхние ~12px твёрдые (то, на чём
|
||||
стоишь), светлая полоса-хайлайт сверху, ниже — темнее/пусто."""
|
||||
draw = ImageDraw.Draw(im)
|
||||
surf_h = 14
|
||||
draw.rectangle([0, 0, W - 1, surf_h - 1], fill=GROUND)
|
||||
draw.line([(0, 0), (W - 1, 0)], fill=GROUND_TOP, width=2)
|
||||
draw.rectangle([0, surf_h, W - 1, H - 1], fill=(GROUND[0]//2, GROUND[1]//2, GROUND[2]//2, 255))
|
||||
|
||||
|
||||
for t in range(32):
|
||||
if t == 0:
|
||||
im = Image.new("RGBA", (W, H), (0, 0, 0, 0)) # Empty — прозрачно
|
||||
elif t in WALL_LIKE:
|
||||
im = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
draw_wall_like(im)
|
||||
elif t in FLOOR_LIKE:
|
||||
im = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
draw_floor_like(im)
|
||||
elif t in FLAT_LIKE:
|
||||
im = Image.new("RGBA", (W, H), FLAT_LIKE[t])
|
||||
elif t in LATTICE_LIKE:
|
||||
im = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
col_w, align = LATTICE_LIKE[t]
|
||||
draw_lattice_like(im, col_w, align)
|
||||
else:
|
||||
hue = (t - 1) / 31.0
|
||||
r, g, b = (int(c * 255) for c in colorsys.hsv_to_rgb(hue, 0.55, 0.85))
|
||||
im = Image.new("RGBA", (W, H), (r, g, b, 255))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle([0, 0, W - 1, H - 1], outline=(0, 0, 0, 255))
|
||||
draw.text((2, 2), "%X" % t, fill=(0, 0, 0, 255), font=font)
|
||||
draw.text((2, H - 12), NAMES[t][:6], fill=(0, 0, 0, 255), font=font)
|
||||
im.save(out_dir / ("%X-0.png" % t))
|
||||
|
||||
print(f"gen_tile_placeholders: 32 файла -> {out_dir}")
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
|
||||
Binary file not shown.
Reference in New Issue
Block a user