Fix crashes, remove Gronk, add Water/Wind anims, fix Tiled paths, clean plants assets

This commit is contained in:
2026-01-14 01:15:55 +01:00
parent cbecdc3ac7
commit 4ef1adc413
480 changed files with 34600 additions and 545 deletions

View File

@@ -0,0 +1,48 @@
from rembg import remove, new_session
from PIL import Image
import os
import glob
# Paths to clean (Recursive search for crops)
SEARCH_PATTERNS = [
"assets/PHASE_PACKS/*/crops/**/*.png",
"assets/crops/**/*.png",
"assets/references/trees/dead/dead_tree.png" # Also clean the tree while we're at it
]
# Initialize session for speed
session = new_session()
def clean_image(path):
try:
print(f"🌿 Processing: {path}")
img = Image.open(path)
# Use simple removal first (rabbit method)
# If image is already transparent, rembg might do weird things, but usually safe.
output = remove(img, session=session, alpha_matting=True)
# Save inplace
output.save(path)
print(f"✨ Cleaned: {os.path.basename(path)}")
except Exception as e:
print(f"❌ Error cleaning {path}: {e}")
if __name__ == "__main__":
print("🐇 The Rabbit Method: Cleaning all plants...")
files = []
for pattern in SEARCH_PATTERNS:
found = glob.glob(pattern, recursive=True)
files.extend(found)
print(f"Found {len(files)} plant assets.")
for f in files:
# Skip if it's not a file
if not os.path.isfile(f): continue
if "_sheet" in f: continue # Skip spritesheets if any
clean_image(f)
print("✅ All plants cleaned!")

33
scripts/clean_plant.py Normal file
View File

@@ -0,0 +1,33 @@
from rembg import remove
from PIL import Image
import os
# Paths to clean
paths = [
"assets/PHASE_PACKS/0_DEMO/crops/cannabis/growth_stages/cannabis_stage4_ready.png",
"assets/PHASE_PACKS/1_FAZA_1/crops/cannabis/growth_stages/cannabis_stage4_ready.png"
]
def clean_image(path):
if not os.path.exists(path):
print(f"⚠️ File not found: {path}")
return
print(f"🍀 Processing: {path}")
try:
input_img = Image.open(path)
# Remove background using rembg (Zajček metoda)
output_img = remove(input_img)
# Save over original
output_img.save(path)
print(f"✨ Cleaned and saved: {path}")
except Exception as e:
print(f"❌ Error processing {path}: {e}")
if __name__ == "__main__":
for p in paths:
clean_image(p)

View File

@@ -0,0 +1,134 @@
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()

View File

@@ -0,0 +1,123 @@
import os
from rembg import remove, new_session
from PIL import Image
import numpy as np
# --- CONFIG ---
GRONK_INPUT_DIR = "assets/slike 🟢/demo 🔴/characters 🔴/gronk/"
GRONK_OUTPUT_FILE = "assets/characters/gronk_pro_sheet.png"
CANNABIS_PATHS = [
"assets/PHASE_PACKS/0_DEMO/crops/cannabis/growth_stages/cannabis_stage4_ready.png",
"assets/PHASE_PACKS/1_FAZA_1/crops/cannabis/growth_stages/cannabis_stage4_ready.png",
"references/crops/cannabis/growth_stages/cannabis_stage4_ready.png"
]
FRAME_SIZE = 128
SHEET_COLS = 4
SHEET_ROWS = 5 # 0:WalkDown, 1:WalkRight, 2:WalkUp, 3:Vape, 4:Idle
# Initialize rembg session
session = new_session()
def clean_and_center(img_path, target_size=(128, 128)):
print(f"Processing {os.path.basename(img_path)}...")
# 1. Load and Remove Background
original = Image.open(img_path).convert("RGBA")
# Use alpha matting for better edge detection (no white halos)
cleaned = remove(original, session=session, alpha_matting=True, alpha_matting_foreground_threshold=240, alpha_matting_background_threshold=10)
# 2. Find Bounding Box (Non-transparent pixels)
bbox = cleaned.getbbox()
if not bbox:
return cleaned.resize(target_size)
# Crop to content
content = cleaned.crop(bbox)
w, h = content.size
# 3. Create Canvas (128x128)
canvas = Image.new("RGBA", target_size, (0, 0, 0, 0))
# 4. Center on Feet logic
# We want the bottom-center of the content to align with the bottom-center of the tile
# Target X center: 64
# Target Bottom Y: 115 (leave some margin)
target_x = (target_size[0] - w) // 2
target_y = target_size[1] - h - 10 # 10px from bottom margin
# Paste content onto canvas
canvas.paste(content, (target_x, target_y), content)
return canvas
def process_gronk():
print("🧛‍♂️ STARTING GRONK PRO PROCESSING...")
# Expected ordering based on file names or hardcoded list to ensure animation sync
# We need 19 images.
# Logic: Sort files properly.
files = sorted([f for f in os.listdir(GRONK_INPUT_DIR) if f.lower().endswith(".png")])
# Mapping based on typical file naming (assuming 1..19 or similar)
# If filenames are random, we might have issues. Let's assume user provided them in order or standard naming.
# Previous script used sorted list.
# Create big spritesheet
sheet = Image.new("RGBA", (SHEET_COLS * FRAME_SIZE, SHEET_ROWS * FRAME_SIZE))
# Layout Mapping (Row -> Image Indices)
# Row 0 (Down): 0,1,2,3
# Row 1 (Right): 4,5,6,7
# Row 2 (Up): 8,9,10,11
# Row 3 (Vape): 12,13,14,15
# Row 4 (Idle): 16,17,18 (19th unused)
for i, filename in enumerate(files):
if i >= 19: break
input_path = os.path.join(GRONK_INPUT_DIR, filename)
processed_frame = clean_and_center(input_path)
# Calculate grid position
# Frames 0-15 map nicely. Frames 16-18 go to Row 4.
row = i // SHEET_COLS
col = i % SHEET_COLS
if row >= SHEET_ROWS: break
x = col * FRAME_SIZE
y = row * FRAME_SIZE
sheet.paste(processed_frame, (x, y))
output_dir = os.path.dirname(GRONK_OUTPUT_FILE)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
sheet.save(GRONK_OUTPUT_FILE)
print(f"✅ Gronk Pro Sheet Saved: {GRONK_OUTPUT_FILE}")
def process_cannabis():
print("🌿 CLEANING CANNABIS...")
for path in CANNABIS_PATHS:
full_path = path # Assuming relative to cwd
if os.path.exists(full_path):
try:
img = Image.open(full_path)
# Rembg with aggressive cutout
out = remove(img, session=session, alpha_matting=True)
out.save(full_path)
print(f"✨ Cleaned: {path}")
except Exception as e:
print(f"❌ Error cleaning {path}: {e}")
else:
print(f"⚠️ Not found: {path}")
if __name__ == "__main__":
process_cannabis()
try:
process_gronk()
except Exception as e:
print(f"❌ Gronk Error: {e}")

70
scripts/process_gronk.py Normal file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""
🧛‍♂️ GRONK PROCESSING SCRIPT V2 (RESIZE)
1. Remove background from vape frames using AI (rembg).
2. Resize to game-ready size (128x128 per frame).
3. Create spritesheet (4 frames horizontal).
"""
import os
from pathlib import Path
from rembg import remove
from PIL import Image
# Input files (found previously)
INPUT_DIR = "assets/slike 🟢/demo 🔴/characters 🔴/gronk"
FILES = [
"gronk_vape_01_1767408553955.png",
"gronk_vape_02_1767408567935.png",
"gronk_vape_03_1767408582938.png",
"gronk_vape_04_1767408599735.png"
]
OUTPUT_DIR = "assets/characters"
OUTPUT_SHEET = "gronk_sheet.png"
TARGET_SIZE = (128, 128)
def main():
print("🧛‍♂️ PROCESSING GRONK (RESIZE)...")
os.makedirs(OUTPUT_DIR, exist_ok=True)
processed_images = []
# 1. Remove Background & Resize
print(" 🧹 Removing backgrounds & Resizing...")
for fname in FILES:
path = os.path.join(INPUT_DIR, fname)
if not os.path.exists(path):
print(f"❌ Missing file: {path}")
return
img = Image.open(path)
img_clean = remove(img) # AI magic
img_resized = img_clean.resize(TARGET_SIZE, Image.LANCZOS)
processed_images.append(img_resized)
print(f" ✅ Cleaned & Resized {fname}")
# 2. Create Spritesheet
print(" 🎞️ Creating spritesheet...")
w, h = TARGET_SIZE
# Create strip (4 x 1)
sheet_width = w * len(processed_images)
sheet_height = h
sheet = Image.new("RGBA", (sheet_width, sheet_height))
for i, img in enumerate(processed_images):
sheet.paste(img, (i * w, 0))
output_path = os.path.join(OUTPUT_DIR, OUTPUT_SHEET)
sheet.save(output_path)
print(f" ✨ Saved spritesheet to: {output_path}")
print(f" Dimensions: {sheet_width}x{sheet_height}")
print(f" Frame size: {w}x{h}")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""
🧛‍♂️ GRONK FULL PROCESSING
Generates a complete sprite sheet for Gronk with background removal.
Output Format: 5 Rows x 4 Columns
Row 0: Walk Down (4 frames)
Row 1: Walk Right (4 frames)
Row 2: Walk Up (4 frames - note: only 3 source files found, will dup frame 2)
Row 3: Vape (4 frames)
Row 4: Idle (3 frames: Down, Right, Up)
"""
import os
from rembg import remove
from PIL import Image
INPUT_DIR = "assets/slike 🟢/demo 🔴/characters 🔴/gronk"
OUTPUT_DIR = "assets/characters"
OUTPUT_SHEET = "gronk_full_sheet.png"
# Target frame size
W, H = 128, 128
# File mappings corresponding to rows
ROWS = [
# Row 0: Walk Down
[
"gronk_walk_down_01_1767408359188.png",
"gronk_walk_down_02_1767408376011.png",
"gronk_walk_down_03_1767408392737.png",
"gronk_walk_down_04_1767408407769.png"
],
# Row 1: Walk Right
[
"gronk_walk_right_01_1767408494468.png",
"gronk_walk_right_02_1767408509256.png",
"gronk_walk_right_03_1767408524931.png",
"gronk_walk_right_04_1767408540208.png"
],
# Row 2: Walk Up (Only 3 files found in list, duplicating middle for 4-frame cycle 1-2-3-2)
[
"gronk_walk_up_01_1767408423382.png",
"gronk_walk_up_02_1767408438192.png",
"gronk_walk_up_03_1767408452582.png",
"gronk_walk_up_02_1767408438192.png" # Duplicate for loop
],
# Row 3: Vape
[
"gronk_vape_01_1767408553955.png",
"gronk_vape_02_1767408567935.png",
"gronk_vape_03_1767408582938.png",
"gronk_vape_04_1767408599735.png"
],
# Row 4: Idle (Down, Right, Up) + 1 Empty
[
"gronk_idle_down_1767408310253.png",
"gronk_idle_right_1767408343383.png",
"gronk_idle_up_1767408324452.png",
None # Empty slot
]
]
def main():
print("🧛‍♂️ PROCESSING GRONK FULL SHEET...")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Create master sheet
sheet_width = W * 4
sheet_height = H * 5
full_sheet = Image.new("RGBA", (sheet_width, sheet_height))
for row_idx, file_list in enumerate(ROWS):
print(f" 📌 Processing Row {row_idx}...")
for col_idx, filename in enumerate(file_list):
if not filename:
continue
path = os.path.join(INPUT_DIR, filename)
if not os.path.exists(path):
print(f" ❌ File not found: {filename}")
continue
# Process Image
try:
img = Image.open(path)
# 1. Remove Background
img = remove(img)
# 2. Resize
img = img.resize((W, H), Image.LANCZOS)
# 3. Paste into sheet
x = col_idx * W
y = row_idx * H
full_sheet.paste(img, (x, y))
print(f" ✅ Placed {filename} at ({x}, {y})")
except Exception as e:
print(f" ⚠️ Error processing {filename}: {e}")
# Save
out_path = os.path.join(OUTPUT_DIR, OUTPUT_SHEET)
full_sheet.save(out_path)
print(f"✨ Saved FULL spritesheet to: {out_path}")
print(f" Size: {sheet_width}x{sheet_height}")
if __name__ == '__main__':
main()