Fix crashes, remove Gronk, add Water/Wind anims, fix Tiled paths, clean plants assets
This commit is contained in:
48
scripts/clean_all_plants.py
Normal file
48
scripts/clean_all_plants.py
Normal 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
33
scripts/clean_plant.py
Normal 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)
|
||||
134
scripts/generate_water_anim.py
Normal file
134
scripts/generate_water_anim.py
Normal 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()
|
||||
123
scripts/process_assets_pro.py
Normal file
123
scripts/process_assets_pro.py
Normal 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
70
scripts/process_gronk.py
Normal 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()
|
||||
109
scripts/process_gronk_full.py
Normal file
109
scripts/process_gronk_full.py
Normal 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()
|
||||
Reference in New Issue
Block a user