Files
novafarma/scripts/generate_water_anim.py

135 lines
4.2 KiB
Python

import os
import xml.etree.ElementTree as ET
from PIL import Image, ImageChops
import numpy as np
def generate_water_animation():
# Paths
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
src_path = os.path.join(base_dir, 'assets', 'grounds', 'water.png')
out_dir = os.path.join(base_dir, 'assets', 'maps', 'tilesets')
out_img_path = os.path.join(out_dir, 'Water_Animated.png')
out_tsx_path = os.path.join(out_dir, 'Water_Animated.tsx')
if not os.path.exists(src_path):
print(f"Error: Source image not found at {src_path}")
return
print(f"Processing: {src_path}")
# Load Image
img = Image.open(src_path).convert('RGBA')
width, height = img.size
tile_w, tile_h = 32, 32
frames = []
# Frame 1: Original
frames.append(img)
# Function to add subtle glimmer (No shifting!)
def create_glimmer(image, intensity=30, seed=0):
# Convert to numpy
arr = np.array(image, dtype=np.int16) # Use int16 to avoid overflow
# Create random noise
np.random.seed(seed)
noise = np.random.randint(-intensity, intensity, (height, width), dtype=np.int16)
# Apply noise only to RGB channels, leave Alpha alone
# arr shape is (H, W, 4)
# Only apply where alpha > 0 (existing content)
alpha = arr[:, :, 3]
mask = alpha > 0
for c in range(3): # R, G, B
channel = arr[:, :, c]
# Add noise
channel[mask] = channel[mask] + noise[mask]
# Clip
arr[:, :, c] = np.clip(channel, 0, 255)
return Image.fromarray(arr.astype(np.uint8))
# Generate 3 variations
print("Generating Glimmer Frames...")
f2 = create_glimmer(img, intensity=20, seed=42)
f3 = create_glimmer(img, intensity=20, seed=101)
f4 = create_glimmer(img, intensity=20, seed=777)
frames.append(f2)
frames.append(f3)
frames.append(f4)
# Combine into vertical strip
total_height = height * 4
combined = Image.new('RGBA', (width, total_height))
for i, f in enumerate(frames):
combined.paste(f, (0, i * height))
# Save Image
if not os.path.exists(out_dir):
os.makedirs(out_dir)
combined.save(out_img_path)
print(f"Saved animated spritesheet: {out_img_path}")
# Generate TSX
cols = width // tile_w
rows = height // tile_h
total_tiles_per_frame = cols * rows
total_tiles_all = total_tiles_per_frame * 4
root = ET.Element("tileset")
root.set("version", "1.10")
root.set("tiledversion", "1.11.0")
root.set("name", "Water_Animated")
root.set("tilewidth", str(tile_w))
root.set("tileheight", str(tile_h))
root.set("tilecount", str(total_tiles_all))
root.set("columns", str(cols))
image_node = ET.SubElement(root, "image")
image_node.set("source", "Water_Animated.png")
image_node.set("width", str(width))
image_node.set("height", str(total_height))
# Generate Animations for the first frame's tiles (0 to total_tiles_per_frame-1)
for i in range(total_tiles_per_frame):
tile_node = ET.SubElement(root, "tile")
tile_node.set("id", str(i))
anim_node = ET.SubElement(tile_node, "animation")
# 4 Frames loop
# Frame 1 (Original)
f1 = ET.SubElement(anim_node, "frame")
f1.set("tileid", str(i))
f1.set("duration", "150")
# Frame 2
f2 = ET.SubElement(anim_node, "frame")
f2.set("tileid", str(i + total_tiles_per_frame))
f2.set("duration", "150")
# Frame 3
f3 = ET.SubElement(anim_node, "frame")
f3.set("tileid", str(i + 2 * total_tiles_per_frame))
f3.set("duration", "150")
# Frame 4
f4 = ET.SubElement(anim_node, "frame")
f4.set("tileid", str(i + 3 * total_tiles_per_frame))
f4.set("duration", "150")
# Formatting
tree = ET.ElementTree(root)
ET.indent(tree, space=" ", level=0)
tree.write(out_tsx_path, encoding='UTF-8', xml_declaration=True)
print(f"Saved TSX definition: {out_tsx_path}")
if __name__ == "__main__":
generate_water_animation()