"""Generate the tileset palette `twu01palstd.itp` from tiles.py. python make_itp.py [out.itp] # default: twu01palstd.itp The toolset loads `palstd.itp` when it registers a tileset; without it the set fails to register (blank name in the New Area wizard, "could not create area"). Every stock/custom set on disk ships one. It's a GFF `ITP ` file: a MAIN list of palette categories, each a struct with an ID byte and a LIST of {RESREF, STRREF} tile entries. We emit one flat category listing all 40 tile model resrefs. Each entry carries a local NAME cexostring (NOT a STRREF) — this is what stock/custom sets without a tileset TLK do (verified against wut01palstd.itp). Entries with neither NAME nor a valid STRREF render blank AND non-selectable in the toolset palette. Built as JSON and converted to GFF with neverwinter.nim's nwn_gff (must be on PATH). """ import json import os import subprocess import sys import tempfile import tiles NO_STRREF = 0xFFFFFFFF # category folder: no dialog.tlk name (cosmetic, blank folder) def tile_label(s): """Readable palette name, e.g. '[flat] cobble_plain'.""" return f"[{s['kind']}] {s['name']}" def build(): entries = [{"__struct_id": 0, "NAME": {"type": "cexostring", "value": tile_label(s)}, "RESREF": {"type": "resref", "value": tiles.resref(s)}} for s in tiles.TILES] return { "__data_type": "ITP ", "MAIN": {"type": "list", "value": [ {"__struct_id": 0, "ID": {"type": "byte", "value": 0}, "STRREF": {"type": "dword", "value": NO_STRREF}, "LIST": {"type": "list", "value": entries}}, ]}, } def main(): out = sys.argv[1] if len(sys.argv) > 1 else f"{tiles.PREFIX}palstd.itp" with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as tf: json.dump(build(), tf) js = tf.name try: subprocess.run(["nwn_gff", "-i", js, "-l", "json", "-k", "gff", "-o", out], check=True) finally: os.unlink(js) print(f"wrote {out} ({len(tiles.TILES)} tile palette entries)") if __name__ == "__main__": main()