#!/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('> 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(' пересобираю") 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()