from PIL import Image import csv import sys import os TILE_W = 8 TILE_H = 8 TILES_PER_ROW = 24 # because 24 tiles * 8 px = 192 px rows in the map def load_tileset(path): tileset = Image.open(path).convert("RGBA") ts_w, ts_h = tileset.size tiles_x = ts_w // TILE_W tiles_y = ts_h // TILE_H tiles = [] for ty in range(tiles_y): for tx in range(tiles_x): tile = tileset.crop(( tx * TILE_W, ty * TILE_H, tx * TILE_W + TILE_W, ty * TILE_H + TILE_H )) tiles.append(tile) print(f"Tileset loaded: {len(tiles)} tiles ({tiles_x}×{tiles_y})") return tiles def find_tile_index(block, tiles): """Return index of tile in tiles[] or raise error if not found.""" for i, t in enumerate(tiles): if list(block.getdata()) == list(t.getdata()): print(f"Made {i}, {t}") return i raise ValueError("Tile not found in tileset!") def build_csv_from_image(src_path, tileset_path, csv_out): img = Image.open(src_path).convert("RGBA") w, h = img.size tiles = load_tileset(tileset_path) # Must be aligned if w % TILE_W != 0 or h % TILE_H != 0: raise ValueError("Image dimensions must be multiples of 8.") map_w = w // TILE_W map_h = h // TILE_H print(f"Building CSV: {map_w}×{map_h} tiles") rows = [] tile_indices = [] # Scan image in 8×8 blocks for ty in range(map_h): row_tiles = [] for tx in range(map_w): block = img.crop(( tx * TILE_W, ty * TILE_H, tx * TILE_W + TILE_W, ty * TILE_H + TILE_H )) idx = find_tile_index(block, tiles) row_tiles.append(idx) tile_indices.append(row_tiles) # Output CSV with line breaks after 24 tiles with open(csv_out, "w", newline="") as f: writer = csv.writer(f) for row in tile_indices: # 24-tile chunks for i in range(0, len(row), TILES_PER_ROW): writer.writerow(row[i:i + TILES_PER_ROW]) print(f"CSV written to {csv_out}") def build_png_from_csv(csv_path, tileset_path, out_path): tiles = load_tileset(tileset_path) # Read CSV tile_rows = [] with open(csv_path, newline="") as f: reader = csv.reader(f) for row in reader: if row: tile_rows.append([int(v) for v in row]) height_blocks = len(tile_rows) width_blocks = len(tile_rows[0]) out_w = width_blocks * TILE_W out_h = height_blocks * TILE_H out_img = Image.new("RGBA", (out_w, out_h)) print(f"Reconstructing {out_w}×{out_h} image") for ty, row in enumerate(tile_rows): for tx, tile_index in enumerate(row): tile = tiles[tile_index] out_img.paste(tile, (tx * TILE_W, ty * TILE_H)) out_img.save(out_path) print(f"Reconstructed PNG saved to {out_path}") # ----------------------------------------------------------------------- # Command-line wrapper # ----------------------------------------------------------------------- if __name__ == "__main__": if len(sys.argv) < 5: print("Usage:") print(" python build_tilemap.py makemap ") print(" python build_tilemap.py makepng ") sys.exit(1) mode = sys.argv[1].lower() tile = sys.argv[2] a = sys.argv[3] b = sys.argv[4] if mode == "makemap": build_csv_from_image(a, tile, b) elif mode == "makepng": build_png_from_csv(a, tile, b) else: print("Unknown mode.")