Files
L2-SPP/import-crests.py

128 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Импорт своего пака гербов в бот-кланы.
Использование:
python import-crests.py C:\\путь\\к\\папке_с_гербами
python import-crests.py C:\\путь --ally (только альянсовые, 8x16)
Принимает PNG / BMP / DDS. Всё, что не 16x16 (клан) или 8x16 (альянс),
масштабируется автоматически. Результат: server/db_installer/sql/spp_crest_seed.sql
Дальше: docker compose ... up -d --build и reset-bots.ps1
Требует: pip install pillow
"""
import sys, os, glob, struct, re, hashlib, random
try:
from PIL import Image
except ImportError:
print("Нужен Pillow: pip install pillow"); sys.exit(1)
HERE = os.path.dirname(os.path.abspath(__file__))
SEED_CLAN = os.path.join(HERE, "server", "db_installer", "sql", "spp_clan_seed.sql")
SEED_OUT = os.path.join(HERE, "server", "db_installer", "sql", "spp_crest_seed.sql")
def dds_header(w, h, linear):
hdr = bytearray(128)
hdr[0:4] = b'DDS '
struct.pack_into('<I', hdr, 4, 124)
struct.pack_into('<I', hdr, 8, 0x0081007)
struct.pack_into('<I', hdr, 12, h)
struct.pack_into('<I', hdr, 16, w)
struct.pack_into('<I', hdr, 20, linear)
struct.pack_into('<I', hdr, 76, 32)
struct.pack_into('<I', hdr, 80, 0x4)
hdr[84:88] = b'DXT1'
struct.pack_into('<I', hdr, 108, 0x1000)
return bytes(hdr)
def to565(c):
return ((c[0] >> 3) << 11) | ((c[1] >> 2) << 5) | (c[2] >> 3)
def encode_dxt1(img):
"""Простое DXT1-сжатие: в каждом блоке 4x4 берём две крайние по яркости краски."""
w, h = img.size
px = img.convert("RGB").load()
body = b''
for by in range(h // 4):
for bx in range(w // 4):
cells = [px[bx * 4 + c, by * 4 + r] for r in range(4) for c in range(4)]
lum = [0.299 * c[0] + 0.587 * c[1] + 0.114 * c[2] for c in cells]
cmax = cells[lum.index(max(lum))]
cmin = cells[lum.index(min(lum))]
c0, c1 = to565(cmax), to565(cmin)
if c0 <= c1:
c0 = min(0xFFFF, c1 + 1)
idx = 0
for i, cell in enumerate(cells):
l = 0.299 * cell[0] + 0.587 * cell[1] + 0.114 * cell[2]
lo, hi = min(lum), max(lum)
t = 0 if hi == lo else (l - lo) / (hi - lo)
sel = 0 if t > 0.83 else (2 if t > 0.5 else (3 if t > 0.17 else 1))
idx |= (sel & 3) << (i * 2)
body += struct.pack('<HH', c0, c1) + struct.pack('<I', idx)
return dds_header(w, h, len(body)) + body
def load_crest(path, w, h):
if path.lower().endswith(".dds"):
data = open(path, "rb").read()
dw = int.from_bytes(data[16:20], 'little'); dh = int.from_bytes(data[12:16], 'little')
if (dw, dh) == (w, h) and len(data) <= (256 if w == 16 else 192):
return data # уже готовый файл нужного размера
print(f" ! {os.path.basename(path)}: {dw}x{dh} -> пересобираю")
return None
img = Image.open(path)
if img.size != (w, h):
img = img.resize((w, h), Image.LANCZOS)
return encode_dxt1(img)
def main():
if len(sys.argv) < 2:
print(__doc__); sys.exit(1)
folder = sys.argv[1]
files = []
for ext in ("png", "bmp", "dds", "jpg", "gif"):
files += sorted(glob.glob(os.path.join(folder, "**", "*." + ext), recursive=True))
if not files:
print("В папке нет картинок"); sys.exit(1)
print(f"Найдено файлов: {len(files)}")
clans = re.findall(r"INSERT IGNORE INTO clan_data \(clan_id, clan_name.*?VALUES \((\d+), '([^']+)'",
open(SEED_CLAN, encoding="utf-8").read())
print(f"Кланов в сиде: {len(clans)}")
out = ["-- SPP crests: imported from " + folder.replace("\\", "/"),
"DELETE FROM crests WHERE crest_id BETWEEN 9000 AND 9999;"]
crest_id = 9000
rnd = random.Random(1)
ally_size = 8
allies = [clans[i:i + ally_size] for i in range(0, len(clans), ally_size)]
A = ["Free","Iron","Silver","Northern","Old","Wild","Royal","United","Dark","Golden","Storm","Silent"]
B = ["Union","League","Pact","Alliance","Circle","Banner","Order","Coalition"]
ok = 0
for ai, group in enumerate(allies):
if len(group) < 3:
continue
name = A[ai % len(A)] + B[(ai // len(A)) % len(B)]
ally_src = files[ai % len(files)]
ally_data = load_crest(ally_src, 8, 16) or encode_dxt1(Image.open(ally_src).resize((8, 16)))
ally_id = crest_id; crest_id += 1
out.append(f"INSERT INTO crests (crest_id, data, type) VALUES ({ally_id}, 0x{ally_data.hex()}, 3);")
leader = int(group[0][0])
for cid, cname in group:
src = files[hash(cname) % len(files)]
data = load_crest(src, 16, 16)
if data is None:
data = encode_dxt1(Image.open(src).convert("RGB").resize((16, 16), Image.LANCZOS))
cc = crest_id; crest_id += 1
out.append(f"INSERT INTO crests (crest_id, data, type) VALUES ({cc}, 0x{data.hex()}, 1);")
out.append(f"UPDATE clan_data SET crest_id={cc}, ally_id={leader}, ally_name='{name}', ally_crest_id={ally_id} WHERE clan_id={cid};")
ok += 1
open(SEED_OUT, "w", encoding="utf-8").write("\n".join(out) + "\n")
print(f"Готово: {ok} клановых гербов -> {SEED_OUT}")
print("Дальше: docker compose -f server\\docker\\docker-compose.yml up --build -d и reset-bots.ps1")
if __name__ == "__main__":
main()