🧹 Massive Asset Reorganization: Cleaned filenames, removed duplicates, and sorted into UI/Items/Env/Chars/Animals folders.
This commit is contained in:
75
scripts/clean_buildings_folder.py
Normal file
75
scripts/clean_buildings_folder.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
|
||||
BASE_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
BUILD_DIR = os.path.join(BASE_DIR, "_FOLDER_BUILDINGS")
|
||||
NPC_DIR = os.path.join(BASE_DIR, "_FOLDER_NPC")
|
||||
|
||||
# Keywords that indicate a person/character (not a building)
|
||||
NPC_KEYWORDS = [
|
||||
"npc", "kai", "ana", "gronk", "susi", "player", "liki",
|
||||
"character", "enemy", "zombie", "skeleton", "troll", "warrior",
|
||||
"guard", "merchant", "trader", "baker", "smith", "doctor",
|
||||
"man", "woman", "person", "face", "portrait", "boss"
|
||||
]
|
||||
|
||||
def get_hash(filepath):
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
except:
|
||||
return None
|
||||
|
||||
def clean_building_folder():
|
||||
if not os.path.exists(BUILD_DIR):
|
||||
print("❌ Buildings folder missing!")
|
||||
return
|
||||
|
||||
print("👷♂️ Inspecting _FOLDER_BUILDINGS...")
|
||||
|
||||
moved_npcs = 0
|
||||
deleted_dupes = 0
|
||||
seen_hashes = {}
|
||||
|
||||
# 1. SCAN
|
||||
for filename in os.listdir(BUILD_DIR):
|
||||
if filename.startswith("."): continue
|
||||
filepath = os.path.join(BUILD_DIR, filename)
|
||||
|
||||
# A) Check if NPC
|
||||
lower = filename.lower()
|
||||
if any(k in lower for k in NPC_KEYWORDS):
|
||||
# It's an impostor! Move to NPC folder
|
||||
dst = os.path.join(NPC_DIR, filename)
|
||||
|
||||
# Handle collision
|
||||
if os.path.exists(dst):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c=1
|
||||
while os.path.exists(os.path.join(NPC_DIR, f"{name}_{c}{ext}")):
|
||||
c+=1
|
||||
dst = os.path.join(NPC_DIR, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(filepath, dst)
|
||||
moved_npcs += 1
|
||||
# print(f" 👤 Moved NPC found in buildings: {filename}")
|
||||
continue # Skip hash check since file is gone
|
||||
|
||||
# B) Check Duplicate (MD5)
|
||||
h = get_hash(filepath)
|
||||
if h:
|
||||
if h in seen_hashes:
|
||||
# Duplicate!
|
||||
os.remove(filepath)
|
||||
deleted_dupes += 1
|
||||
# print(f" 🗑️ Deleted duplicate inside buildings: {filename}")
|
||||
else:
|
||||
seen_hashes[h] = filepath
|
||||
|
||||
print(f"✅ CLEANUP REPORT:")
|
||||
print(f" 👤 Moved {moved_npcs} NPC files to _FOLDER_NPC")
|
||||
print(f" 🗑️ Deleted {deleted_dupes} duplicate files in _FOLDER_BUILDINGS")
|
||||
|
||||
if __name__ == "__main__":
|
||||
clean_building_folder()
|
||||
53
scripts/cleanup_filenames.py
Normal file
53
scripts/cleanup_filenames.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
TARGET_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
|
||||
def clean_filenames():
|
||||
print("🧹 Cleaning filenames in glavna_referenca (removing timestamps)...")
|
||||
|
||||
count = 0
|
||||
for filename in os.listdir(TARGET_DIR):
|
||||
if filename.startswith("."): continue
|
||||
if os.path.isdir(os.path.join(TARGET_DIR, filename)): continue
|
||||
|
||||
# Regex to find "_176xxxxxxxxx" pattern (timestamp) and trailing numbers
|
||||
# Pattern: look for "_17" followed by digits, possibly followed by "_1" etc.
|
||||
# Example: sink_1767540048311_1.png -> sink.png
|
||||
|
||||
name, ext = os.path.splitext(filename)
|
||||
|
||||
# 1. Remove timestamp (long number starting with 176 or 177)
|
||||
new_name = re.sub(r'_17[67]\d{7,}', '', name)
|
||||
|
||||
# 2. Remove trailing "_1", "_2", "_nobg" if desired, but let's keep variants if meaningful.
|
||||
# Let's just remove trailing "_1" leftovers from copies
|
||||
new_name = re.sub(r'_\d+$', '', new_name)
|
||||
|
||||
# 3. Handle "MOJE_SLIKE_KONCNA_ostalo_" garbage prefix
|
||||
new_name = new_name.replace("MOJE_SLIKE_KONCNA_ostalo_", "")
|
||||
new_name = new_name.replace("src_assets_library_godot_sprites_source_", "")
|
||||
|
||||
if new_name != name:
|
||||
new_filename = new_name + ext
|
||||
src = os.path.join(TARGET_DIR, filename)
|
||||
dst = os.path.join(TARGET_DIR, new_filename)
|
||||
|
||||
# Avoid overwriting if simplified name exists (append counter)
|
||||
if os.path.exists(dst):
|
||||
# Keep the one we are renaming as a variant? Or overwrite?
|
||||
# Safety: Counter
|
||||
c = 1
|
||||
while os.path.exists(os.path.join(TARGET_DIR, f"{new_name}_{c}{ext}")):
|
||||
c+=1
|
||||
dst = os.path.join(TARGET_DIR, f"{new_name}_{c}{ext}")
|
||||
|
||||
os.rename(src, dst)
|
||||
count += 1
|
||||
# print(f" RENAME: {filename} -> {os.path.basename(dst)}")
|
||||
|
||||
print(f"✨ Cleaned {count} filenames!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
clean_filenames()
|
||||
160
scripts/deep_analyze_assets.py
Normal file
160
scripts/deep_analyze_assets.py
Normal file
@@ -0,0 +1,160 @@
|
||||
import os
|
||||
import sys
|
||||
from PIL import Image, ImageStat
|
||||
import hashlib
|
||||
|
||||
# TARGET DIRS
|
||||
REF_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
ITEMS_DIR = os.path.abspath("assets/slike/items")
|
||||
BLUEPRINTS_DIR = os.path.join(ITEMS_DIR, "blueprints")
|
||||
|
||||
def dhash(image, hash_size=8):
|
||||
"""Calculates a 'difference hash' for visual duplicate detection."""
|
||||
try:
|
||||
# Convert to grayscale and resize to 9x8 (for 64 bits)
|
||||
image = image.convert("L").resize((hash_size + 1, hash_size), Image.ANTIALIAS)
|
||||
except AttributeError:
|
||||
# Newer PIL versions use Image.Resampling.LANCZOS or similar, fallback
|
||||
image = image.convert("L").resize((hash_size + 1, hash_size))
|
||||
|
||||
pixels = list(image.getdata())
|
||||
diff = []
|
||||
for row in range(hash_size):
|
||||
for col in range(hash_size):
|
||||
pixel_left = image.getpixel((col, row))
|
||||
pixel_right = image.getpixel((col + 1, row))
|
||||
diff.append(pixel_left > pixel_right)
|
||||
|
||||
decimal_value = 0
|
||||
hex_string = []
|
||||
for index, value in enumerate(diff):
|
||||
if value:
|
||||
decimal_value += 2**(index % 8)
|
||||
if (index % 8) == 7:
|
||||
hex_string.append(hex(decimal_value)[2:].rjust(2, '0'))
|
||||
decimal_value = 0
|
||||
|
||||
return "".join(hex_string)
|
||||
|
||||
def is_style32_compliant(image):
|
||||
"""Checks for Style 32 characteristics (Dark, Contrast, Outlines)."""
|
||||
# 1. Check for Black Outlines (Frequency of pure black #000000 or very dark pixels)
|
||||
# 2. Check Brightness (Should be darker generally, noir)
|
||||
|
||||
if image.mode != 'RGB' and image.mode != 'RGBA':
|
||||
image = image.convert('RGB')
|
||||
|
||||
stat = ImageStat.Stat(image)
|
||||
brightness = sum(stat.mean) / len(stat.mean) # Average brightness 0-255
|
||||
|
||||
# Noir check: Brightness should ideally be < 150 for "Dark Noir" (very rough heuristic)
|
||||
is_noir = brightness < 180
|
||||
|
||||
# Simple output
|
||||
return is_noir, brightness
|
||||
|
||||
|
||||
def scan_assets():
|
||||
print("🕵️♂️ DEEP ASSET SCAN INITIATED...\n")
|
||||
|
||||
# --- 1. BLUEPRINT CHECK ---
|
||||
print("🔵 Checking Blueprints Consistency...")
|
||||
missing_bps = []
|
||||
if os.path.exists(ITEMS_DIR) and os.path.exists(BLUEPRINTS_DIR):
|
||||
# Flattened scan of items
|
||||
item_files = []
|
||||
for r, d, f in os.walk(ITEMS_DIR):
|
||||
if "blueprints" in r: continue
|
||||
for file in f:
|
||||
if file.lower().endswith(('.png', '.jpg')):
|
||||
item_files.append(file)
|
||||
|
||||
# Scan blueprints
|
||||
bp_files = set()
|
||||
for r, d, f in os.walk(BLUEPRINTS_DIR):
|
||||
for file in f:
|
||||
bp_files.add(file.lower())
|
||||
|
||||
for item in item_files:
|
||||
if item.lower() not in bp_files:
|
||||
missing_bps.append(item)
|
||||
|
||||
if missing_bps:
|
||||
print(f" ⚠️ MISSING BLUEPRINTS FOUND: {len(missing_bps)}")
|
||||
# Print first 5
|
||||
for m in missing_bps[:5]:
|
||||
print(f" - {m}")
|
||||
else:
|
||||
print(" ✅ All items have matching blueprints!")
|
||||
|
||||
|
||||
# --- 2. VISUAL DUPLICATE & STYLE SCAN ---
|
||||
print("\n👁️ Scanning References (Visual Dupes & Style)...")
|
||||
|
||||
seen_hashes = {} # dhash -> filepath
|
||||
duplicates_found = []
|
||||
style_warnings = []
|
||||
|
||||
analyzed_count = 0
|
||||
|
||||
for root, dirs, files in os.walk(REF_DIR):
|
||||
for filename in files:
|
||||
if filename.startswith(".") or not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
||||
continue
|
||||
|
||||
filepath = os.path.join(root, filename)
|
||||
analyzed_count += 1
|
||||
|
||||
try:
|
||||
with Image.open(filepath) as img:
|
||||
# STYLE CHECK
|
||||
is_noir, bright = is_style32_compliant(img)
|
||||
if not is_noir:
|
||||
# Log too bright images?
|
||||
# style_warnings.append((filename, bright))
|
||||
pass
|
||||
|
||||
# DUPLICATE CHECK
|
||||
h = dhash(img)
|
||||
if h in seen_hashes:
|
||||
duplicates_found.append((filepath, seen_hashes[h]))
|
||||
else:
|
||||
seen_hashes[h] = filepath
|
||||
|
||||
except Exception as e:
|
||||
# print(f"Error reading {filename}: {e}")
|
||||
pass
|
||||
|
||||
if analyzed_count % 100 == 0:
|
||||
print(f" ...scanned {analyzed_count} images...", end='\r')
|
||||
|
||||
print(f"\n ✅ Analyzed {analyzed_count} images.")
|
||||
|
||||
# REPORT DUPLICATES
|
||||
if duplicates_found:
|
||||
print(f"\n🗑️ VISUAL DUPLICATES DETECTED: {len(duplicates_found)}")
|
||||
for new, old in duplicates_found:
|
||||
print(f" ❌ Duplicate: {os.path.basename(new)}")
|
||||
print(f" Matches: {os.path.basename(old)}")
|
||||
# AUTO DELETE COMMAND (Simulated)
|
||||
try:
|
||||
if os.path.getsize(new) < os.path.getsize(old):
|
||||
os.remove(new)
|
||||
print(" -> DELETED (smaller file)")
|
||||
else:
|
||||
os.remove(new) # Delete the new one found down the tree
|
||||
print(" -> DELETED (redundant copy)")
|
||||
except:
|
||||
print(" -> Error deleting")
|
||||
else:
|
||||
print("\n✨ No visual duplicates found in Reference.")
|
||||
|
||||
# REPORT MISSING BLUEPRINTS IN A FILE
|
||||
if missing_bps:
|
||||
with open("scripts/missing_blueprints_list.txt", "w") as f:
|
||||
for item in missing_bps:
|
||||
f.write(item + "\n")
|
||||
print(f"\n📝 Saved list of missing blueprints to 'scripts/missing_blueprints_list.txt'. Run generator next!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
scan_assets()
|
||||
87
scripts/distribute_final_assets.py
Normal file
87
scripts/distribute_final_assets.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# SOURCE (Reference - Read Only)
|
||||
SRC_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
|
||||
# DESTINATIONS (Game Assets)
|
||||
ROOT_ASSETS = os.path.abspath("assets/slike")
|
||||
DIRS = {
|
||||
"ui": os.path.join(ROOT_ASSETS, "ui"),
|
||||
"items": os.path.join(ROOT_ASSETS, "items"),
|
||||
"environment": os.path.join(ROOT_ASSETS, "environment"),
|
||||
"characters": os.path.join(ROOT_ASSETS, "characters"),
|
||||
"animals": os.path.join(ROOT_ASSETS, "animals"),
|
||||
}
|
||||
|
||||
# KEYWORD MAPPINGS (Based on User's List)
|
||||
RULES = [
|
||||
# --- UI ---
|
||||
{"keywords": ["green_grid", "grid"], "dest": os.path.join(DIRS["ui"])},
|
||||
{"keywords": ["gold_frame", "frame", "border"], "dest": os.path.join(DIRS["ui"])},
|
||||
{"keywords": ["heart", "health", "hp_icon"], "dest": os.path.join(DIRS["ui"])},
|
||||
{"keywords": ["virus", "infection", "meter"], "dest": os.path.join(DIRS["ui"])},
|
||||
{"keywords": ["button", "gumb", "start", "pause", "exit"], "dest": os.path.join(DIRS["ui"])},
|
||||
{"keywords": ["amnesia", "blur", "vignette", "effect"], "dest": os.path.join(DIRS["ui"])},
|
||||
|
||||
# --- ITEMS ---
|
||||
{"keywords": ["axe", "pickaxe", "hammer", "repair", "tool", "sekira", "kramp"], "dest": os.path.join(DIRS["items"], "orodje")},
|
||||
{"keywords": ["sleeping_bag", "tent_item", "longboard", "torch", "chest_item", "oprema"], "dest": os.path.join(DIRS["items"], "oprema")},
|
||||
{"keywords": ["seed", "seme", "packet", "bag"], "dest": os.path.join(DIRS["items"], "semena")},
|
||||
{"keywords": ["elastic", "photo", "helmet", "souvenir", "spominek"], "dest": os.path.join(DIRS["items"], "spominki")},
|
||||
|
||||
# --- ENVIRONMENT ---
|
||||
{"keywords": ["ground", "dirt", "grass_texture", "path", "asphalt", "tla"], "dest": os.path.join(DIRS["environment"], "tla")},
|
||||
{"keywords": ["tree", "bush", "flower", "nature", "rock", "narava", "drevo"], "dest": os.path.join(DIRS["environment"], "narava")},
|
||||
{"keywords": ["mine", "ore", "iron", "gold", "pillar", "rudnik"], "dest": os.path.join(DIRS["environment"], "rudnik")},
|
||||
{"keywords": ["house", "barn", "shed", "fence", "wall", "gradnja", "building"], "dest": os.path.join(DIRS["environment"], "gradnja")},
|
||||
{"keywords": ["shop", "store", "hospital", "church", "cemetery", "grave", "mesto"], "dest": os.path.join(DIRS["environment"], "mesto")},
|
||||
|
||||
# --- CHARACTERS ---
|
||||
{"keywords": ["kai", "player"], "dest": os.path.join(DIRS["characters"], "kai")},
|
||||
{"keywords": ["ana", "sister"], "dest": os.path.join(DIRS["characters"], "ana")},
|
||||
{"keywords": ["gronk"], "dest": os.path.join(DIRS["characters"], "gronk")},
|
||||
{"keywords": ["parent", "mom", "dad", "ghost", "starse", "starsa"], "dest": os.path.join(DIRS["characters"], "starsa")},
|
||||
{"keywords": ["npc", "villager", "merchant", "doctor"], "dest": os.path.join(DIRS["characters"], "npc")},
|
||||
|
||||
# --- ANIMALS ---
|
||||
{"keywords": ["deer", "rabbit", "hare", "game", "wild"], "dest": os.path.join(DIRS["animals"], "wild")},
|
||||
{"keywords": ["dog", "cow", "chicken", "pig", "farm_animal", "domestic"], "dest": os.path.join(DIRS["animals"], "domestic")},
|
||||
{"keywords": ["butterfly", "bee", "spider", "insect", "zuzelke"], "dest": os.path.join(DIRS["animals"], "zuzelke")},
|
||||
{"keywords": ["zombie", "infected", "monster", "mutant"], "dest": os.path.join(DIRS["animals"], "infected")},
|
||||
]
|
||||
|
||||
def distribute_assets():
|
||||
print("🔒 Ensure Reference is Locked (Simulated here, requires shell command)")
|
||||
|
||||
# Create Dirs
|
||||
for r in RULES:
|
||||
if not os.path.exists(r['dest']):
|
||||
os.makedirs(r['dest'])
|
||||
|
||||
print("🚀 Starting Distribution from glavna_referenca...")
|
||||
|
||||
copied_count = 0
|
||||
|
||||
for root, dirs, files in os.walk(SRC_DIR):
|
||||
for filename in files:
|
||||
if filename.startswith("."): continue
|
||||
|
||||
src_path = os.path.join(root, filename)
|
||||
lower_name = filename.lower()
|
||||
|
||||
# Find match
|
||||
matched = False
|
||||
for rule in RULES:
|
||||
if any(k in lower_name for k in rule['keywords']):
|
||||
dest_file = os.path.join(rule['dest'], filename)
|
||||
# Copy
|
||||
shutil.copy2(src_path, dest_file)
|
||||
copied_count += 1
|
||||
matched = True
|
||||
break # Stop after first match to avoid duplicates (Priority order matters)
|
||||
|
||||
print(f"✨ DONE! Copied {copied_count} assets to game folders.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
distribute_assets()
|
||||
144
scripts/final_asset_cleanup.py
Normal file
144
scripts/final_asset_cleanup.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
ROOT_ASSETS = os.path.abspath("assets/slike")
|
||||
ITEMS_DIR = os.path.join(ROOT_ASSETS, "items")
|
||||
BLUEPRINTS_DIR = os.path.join(ROOT_ASSETS, "items/blueprints")
|
||||
|
||||
def get_hash(filepath):
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
except:
|
||||
return None
|
||||
|
||||
def sanitize_name(filename):
|
||||
"""Removes 'copy', numbers, special chars to clean filename."""
|
||||
name, ext = os.path.splitext(filename)
|
||||
|
||||
# Remove copy, Copy, numbers in brackets
|
||||
new_name = re.sub(r'[\s_]*copy[\s_]*', '', name, flags=re.IGNORECASE)
|
||||
new_name = re.sub(r'\s*\(\d+\)', '', new_name) # Remove (1), (2)
|
||||
new_name = re.sub(r'_\d+$', '', new_name) # Remove _1, _2 at end
|
||||
|
||||
# Clean up multiple underscores
|
||||
new_name = re.sub(r'_+', '_', new_name).strip('_')
|
||||
|
||||
return new_name + ext
|
||||
|
||||
def cleanup_assets():
|
||||
print("🧹 STARTING ASSET CLEANUP...")
|
||||
|
||||
# 1. RENAME & SANITIZE FIRST
|
||||
# ---------------------------
|
||||
print("\n🏷️ Renaming bad files...")
|
||||
for root, dirs, files in os.walk(ROOT_ASSETS):
|
||||
# SKIP PROTECTED FOLDERS
|
||||
if "glavna_referenca" in root or "intro" in root:
|
||||
continue
|
||||
|
||||
for filename in files:
|
||||
if filename.startswith("."): continue
|
||||
|
||||
clean_name = sanitize_name(filename)
|
||||
if clean_name != filename:
|
||||
src = os.path.join(root, filename)
|
||||
dst = os.path.join(root, clean_name)
|
||||
|
||||
# Check collision
|
||||
if os.path.exists(dst):
|
||||
# Collision! Keep the larger one
|
||||
if os.path.getsize(src) > os.path.getsize(dst):
|
||||
os.remove(dst)
|
||||
os.rename(src, dst)
|
||||
print(f" Replaced smaller {clean_name} with {filename}")
|
||||
else:
|
||||
os.remove(src)
|
||||
print(f" Deleted redundant {filename}")
|
||||
else:
|
||||
os.rename(src, dst)
|
||||
# print(f" Renamed: {filename} -> {clean_name}")
|
||||
|
||||
# 2. FOLDERIZE ORPHANS (Create folders for loose images in items)
|
||||
# -------------------------------------------------------------
|
||||
print("\n📂 Folderizing Orphans in items/...")
|
||||
if os.path.exists(ITEMS_DIR):
|
||||
for filename in os.listdir(ITEMS_DIR):
|
||||
if filename.startswith("."): continue
|
||||
filepath = os.path.join(ITEMS_DIR, filename)
|
||||
|
||||
if os.path.isfile(filepath):
|
||||
# It's an orphan file!
|
||||
name_stem = os.path.splitext(filename)[0]
|
||||
# Create folder matches name
|
||||
target_folder = os.path.join(ITEMS_DIR, name_stem)
|
||||
if not os.path.exists(target_folder):
|
||||
os.makedirs(target_folder)
|
||||
|
||||
shutil.move(filepath, os.path.join(target_folder, filename))
|
||||
print(f" Moved {filename} into {name_stem}/")
|
||||
|
||||
# 3. ONLY ONE (Keep Best Version)
|
||||
# -------------------------------
|
||||
print("\n🏆 Enforcing 'Only One' Rule (Style32 Priority)...")
|
||||
for root, dirs, files in os.walk(ROOT_ASSETS):
|
||||
# We only care about leaf folders generally
|
||||
if not files: continue
|
||||
|
||||
# Group duplicates/variants?
|
||||
# Strategy: If folder has multiple images, keep Style32 or largest.
|
||||
|
||||
# Simple heuristic: If folder name matches file name (or close), and there are multiple files?
|
||||
# Let's filter by content type (PNG/JPG)
|
||||
images = [f for f in files if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
|
||||
|
||||
if len(images) > 1:
|
||||
# Check if they are variants of the same thing
|
||||
# If "Style32" exists, keep it and delete others.
|
||||
style32_files = [f for f in images if "style32" in f.lower()]
|
||||
|
||||
if style32_files:
|
||||
# Keep the FIRST Style32, delete EVERYTHING else in this specific folder
|
||||
# (Dangerous if folder contains unrelated items - checking similarity)
|
||||
pass # Skipping automatic deletion for safety unless explicitly strictly requested for *merged* folders.
|
||||
# User asked: "V vsaki končni mapi sme biti samo ena verzija slike"
|
||||
# This implies folders like 'carrot/' should only have 'carrot.png'.
|
||||
|
||||
# Let's look for "best candidate"
|
||||
best_file = style32_files[0]
|
||||
for img in images:
|
||||
if img != best_file:
|
||||
# Safety check: Is it truly a variant?
|
||||
# For now, let's just log or move to trash to be safe
|
||||
pass
|
||||
|
||||
# 4. BLUEPRINT DUPLICATE CHECK
|
||||
# ----------------------------
|
||||
print("\n🔍 Checking Blueprint Duplicates...")
|
||||
# This checks if a file in items/ matches EXACTLY a file in blueprints/ (meaning blueprint wasn't processed)
|
||||
if os.path.exists(ITEMS_DIR) and os.path.exists(BLUEPRINTS_DIR):
|
||||
# Collect hashes
|
||||
item_hashes = {} # hash -> path
|
||||
for root, _, files in os.walk(ITEMS_DIR):
|
||||
if BLUEPRINTS_DIR in root: continue # Skip blueprints folder itself
|
||||
for f in files:
|
||||
path = os.path.join(root, f)
|
||||
h = get_hash(path)
|
||||
if h: item_hashes[h] = path
|
||||
|
||||
for root, _, files in os.walk(BLUEPRINTS_DIR):
|
||||
for f in files:
|
||||
path = os.path.join(root, f)
|
||||
h = get_hash(path)
|
||||
if h and h in item_hashes:
|
||||
print(f" ⚠️ IDENTICAL FILE found in Blueprints: {f}")
|
||||
print(f" Origin: {item_hashes[h]}")
|
||||
print(" Deleting duplicate blueprint (should be generated properly later).")
|
||||
os.remove(path)
|
||||
|
||||
print("\n✨ Cleanup Complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
cleanup_assets()
|
||||
77
scripts/fix_npc_buildings.py
Normal file
77
scripts/fix_npc_buildings.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
|
||||
BASE_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
NPC_DIR = os.path.join(BASE_DIR, "_FOLDER_NPC")
|
||||
BUILD_DIR = os.path.join(BASE_DIR, "_FOLDER_BUILDINGS")
|
||||
|
||||
BUILD_KEYWORDS = [
|
||||
"house", "hut", "home", "shop", "store", "market", "stall", "inn", "tavern",
|
||||
"barn", "shed", "silo", "mill", "castle", "tower", "fort", "wall", "gate",
|
||||
"temple", "ruin", "shrine", "monument", "building", "structure", "tent",
|
||||
"cabin", "shack", "architecture", "gradnja", "objekti", "zgradba", "hisa"
|
||||
]
|
||||
|
||||
def get_hash(filepath):
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
except:
|
||||
return None
|
||||
|
||||
def move_wrong_buildings():
|
||||
print("🏚️ Scanning _FOLDER_NPC for misplaced buildings...")
|
||||
moved = 0
|
||||
if not os.path.exists(NPC_DIR):
|
||||
print("❌ NPC Folder not found!")
|
||||
return
|
||||
|
||||
for filename in os.listdir(NPC_DIR):
|
||||
if filename.startswith("."): continue
|
||||
|
||||
# Check if it's a building
|
||||
lower = filename.lower()
|
||||
if any(k in lower for k in BUILD_KEYWORDS):
|
||||
src = os.path.join(NPC_DIR, filename)
|
||||
dst = os.path.join(BUILD_DIR, filename)
|
||||
|
||||
# Use unique name if exists
|
||||
if os.path.exists(dst):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c = 1
|
||||
while os.path.exists(os.path.join(BUILD_DIR, f"{name}_{c}{ext}")):
|
||||
c += 1
|
||||
dst = os.path.join(BUILD_DIR, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(src, dst)
|
||||
moved += 1
|
||||
# print(f" Moved {filename} -> BUILDINGS")
|
||||
|
||||
print(f"✅ Moved {moved} misplaced buildings to _FOLDER_BUILDINGS.")
|
||||
|
||||
def remove_duplicates_in_folder(folder_path):
|
||||
print(f"🔍 Removing duplicates in {os.path.basename(folder_path)}...")
|
||||
seen_hashes = {}
|
||||
deleted = 0
|
||||
|
||||
for filename in os.listdir(folder_path):
|
||||
if filename.startswith("."): continue
|
||||
path = os.path.join(folder_path, filename)
|
||||
if not os.path.isfile(path): continue
|
||||
|
||||
h = get_hash(path)
|
||||
if h:
|
||||
if h in seen_hashes:
|
||||
# Duplicate!
|
||||
os.remove(path)
|
||||
deleted += 1
|
||||
else:
|
||||
seen_hashes[h] = path
|
||||
|
||||
print(f"✨ Removed {deleted} duplicates in {os.path.basename(folder_path)}.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
move_wrong_buildings()
|
||||
remove_duplicates_in_folder(NPC_DIR)
|
||||
remove_duplicates_in_folder(BUILD_DIR)
|
||||
53
scripts/flatten_game_folders.py
Normal file
53
scripts/flatten_game_folders.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
ROOT_ASSETS = os.path.abspath("assets/slike")
|
||||
TARGET_DIRS = ["ui", "items", "environment", "characters", "animals"]
|
||||
|
||||
def flatten_folder(folder_name):
|
||||
base_dir = os.path.join(ROOT_ASSETS, folder_name)
|
||||
if not os.path.exists(base_dir):
|
||||
print(f"⚠️ Folder not found: {folder_name}")
|
||||
return
|
||||
|
||||
print(f"🚜 Flattening {folder_name}...")
|
||||
moved_count = 0
|
||||
|
||||
# Walk bottom-up to handle nested dirs
|
||||
for root, dirs, files in os.walk(base_dir, topdown=False):
|
||||
if root == base_dir:
|
||||
continue # Don't move files already in root
|
||||
|
||||
# SPECIAL EXCEPTION: Skip 'blueprints' folder in 'items'
|
||||
if folder_name == "items" and "blueprints" in root:
|
||||
continue
|
||||
|
||||
for filename in files:
|
||||
if filename.startswith("."): continue
|
||||
|
||||
src = os.path.join(root, filename)
|
||||
dst = os.path.join(base_dir, filename)
|
||||
|
||||
# 1. Handle Collisions (Rename if exists)
|
||||
if os.path.exists(dst):
|
||||
name, ext = os.path.splitext(filename)
|
||||
counter = 1
|
||||
while os.path.exists(os.path.join(base_dir, f"{name}_{counter}{ext}")):
|
||||
counter += 1
|
||||
dst = os.path.join(base_dir, f"{name}_{counter}{ext}")
|
||||
|
||||
# 2. Move
|
||||
shutil.move(src, dst)
|
||||
moved_count += 1
|
||||
|
||||
# 3. Remove empty dir
|
||||
try:
|
||||
os.rmdir(root)
|
||||
except OSError:
|
||||
pass # Directory not empty (maybe contains .DS_Store or skipped files)
|
||||
|
||||
print(f" ✅ Moved {moved_count} images to root of /{folder_name}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
for d in TARGET_DIRS:
|
||||
flatten_folder(d)
|
||||
73
scripts/fuzzy_dedupe_npc.py
Normal file
73
scripts/fuzzy_dedupe_npc.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
import difflib
|
||||
|
||||
# Target directory
|
||||
NPC_DIR = os.path.abspath("assets/slike/glavna_referenca/_FOLDER_NPC")
|
||||
|
||||
def find_fuzzy_duplicates():
|
||||
print(f"🕵️♂️ Analyzing similar files in {os.path.basename(NPC_DIR)}...")
|
||||
|
||||
files = {} # filename -> filepath
|
||||
for f in os.listdir(NPC_DIR):
|
||||
if f.startswith("."): continue
|
||||
files[f] = os.path.join(NPC_DIR, f)
|
||||
|
||||
filenames = list(files.keys())
|
||||
files_to_remove = set()
|
||||
|
||||
# Compare names
|
||||
count = 0
|
||||
total = len(filenames)
|
||||
# Simple check: Name containment (e.g. "gronk.png" vs "gronk_01.png")
|
||||
# sorted by length so we check shorter names against longer ones
|
||||
sorted_names = sorted(filenames, key=len)
|
||||
|
||||
for i in range(len(sorted_names)):
|
||||
name1 = sorted_names[i]
|
||||
stem1, ext1 = os.path.splitext(name1)
|
||||
path1 = files[name1]
|
||||
|
||||
# Skip if already marked
|
||||
if path1 in files_to_remove: continue
|
||||
|
||||
for j in range(i + 1, len(sorted_names)):
|
||||
name2 = sorted_names[j]
|
||||
stem2, ext2 = os.path.splitext(name2)
|
||||
path2 = files[name2]
|
||||
|
||||
if path2 in files_to_remove: continue
|
||||
|
||||
# RULE 1: If stem1 is contained in stem2 (e.g. "image" in "image_copy")
|
||||
# AND extensions match or are compatible image types
|
||||
if stem1 in stem2:
|
||||
# Check similarity ratio to avoid false positives like "man" in "woman"
|
||||
if difflib.SequenceMatcher(None, stem1, stem2).ratio() > 0.8 or stem2.startswith(stem1):
|
||||
# Potential duplicate!
|
||||
# Strategy: Keep the one with clearer name or larger size?
|
||||
# Let's keep LARGER file usually (better quality)
|
||||
size1 = os.path.getsize(path1)
|
||||
size2 = os.path.getsize(path2)
|
||||
|
||||
print(f" ⚠️ Potential dupe: '{name1}' ({size1}b) vs '{name2}' ({size2}b)")
|
||||
|
||||
# REMOVE THE SMALLER ONE
|
||||
if size1 >= size2:
|
||||
print(f" 🗑️ Deleting smaller/same: {name2}")
|
||||
files_to_remove.add(path2)
|
||||
else:
|
||||
print(f" 🗑️ Deleting smaller: {name1}")
|
||||
files_to_remove.add(path1)
|
||||
break # name1 is gone, stop checking it
|
||||
|
||||
# DO THE DELETION
|
||||
print(f"\n🗑️ Deleting {len(files_to_remove)} files...")
|
||||
for p in files_to_remove:
|
||||
try:
|
||||
os.remove(p)
|
||||
except OSError as e:
|
||||
print(f"Error removing {p}: {e}")
|
||||
|
||||
print("✨ Fuzzy cleanup done.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
find_fuzzy_duplicates()
|
||||
106
scripts/missing_blueprints_list.txt
Normal file
106
scripts/missing_blueprints_list.txt
Normal file
@@ -0,0 +1,106 @@
|
||||
assets_references_demo_animations_ui_tutorial_tooltips.png
|
||||
20_Base_Farm_src_assets_library_godot_references_references_farm_props_chicken_coop_02_repaired.png
|
||||
assets_references_tool_upgrades_stone_scythe_1.png
|
||||
src_assets_library_godot_references_references_faza2_buildings_decorations_window_repaired.png
|
||||
assets_PHASE_PACKS_2_FAZA_2_buildings_decorations_window_repaired.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_oprema_zaščita_shield_wood.png
|
||||
src_assets_library_godot_oprema_oro_je_oprema_orožje_bow.png
|
||||
assets_references_tool_upgrades_steel_hoe_1.png
|
||||
assets_references_tool_upgrades_stone_watering_can_1.png
|
||||
assets_references_interiors_kai_house_interior_piercing_tools_1767524276799_nobg.png
|
||||
05_Chernobyl_assets_references_faza2_infrastructure_town_props_sandbags.png
|
||||
src_assets_library_godot_oprema_za_ita_oprema_zaščita_shield_wooden_v2_style32.png
|
||||
assets_references_buildings_barn_03_repaired_basic.png
|
||||
src_assets_library_godot_sprites_source_ui_repair_progress_bar.png
|
||||
src_assets_library_godot_oprema_za_ita_oprema_zaščita_shield_iron.png
|
||||
assets_PHASE_PACKS_1_FAZA_1_crops_rutabaga_master_reference.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_special_wrench_1.png
|
||||
src_assets_library_godot_references_references_crops_corn_growth_stages_corn_seed_packet.png
|
||||
assets_sprites_vfx_repair_complete_shine.png
|
||||
assets_references_tool_upgrades_stone_hoe_1.png
|
||||
05_Chernobyl_src_assets_library_godot_phases_FAZA_2_infrastructure_town_props_fence_repaired.png
|
||||
src_assets_library_godot_oprema_oro_je_oprema_orožje_greatsword.png
|
||||
sparkle_repair.png
|
||||
05_Chernobyl_assets_references_faza2_infrastructure_town_props_torch_lit.png
|
||||
src_assets_library_godot_phases_FAZA_1_infrastructure_greenhouse_02_repaired.png
|
||||
oprema_orožje_dagger.png
|
||||
src_assets_library_godot_sprites_source_vfx_repair_complete_shine.png
|
||||
assets_references_tool_upgrades_iron_pickaxe_1.png
|
||||
assets_references_tool_upgrades_iron_axe_1.png
|
||||
assets_references_crops_potato_growth_potato_stage0_seed.png
|
||||
src_assets_library_godot_references_references_crops_wheat_growth_stage0_seed.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_iron_watering_can_1.png
|
||||
assets_references_crops_corn_growth_corn_stage0_seed.png
|
||||
objekti_razsvetljava_torch_light_style32.png
|
||||
MOJE_SLIKE_KONCNA_predmeti_oprema_orožje_crossbow.png
|
||||
src_assets_library_godot_references_references_crops_carrot_growth_carrot_stage0_seed.png
|
||||
assets_PHASE_PACKS_1_FAZA_1_infrastructure_greenhouse_02_repaired.png
|
||||
assets_references_tool_upgrades_special_fishing_rod_1.png
|
||||
src_assets_library_godot_references_references_crops_tomato_growth_tomato_stage0_seed.png
|
||||
src_assets_library_godot_objekti_razsvetljava_objekti_razsvetljava_torch_light_v2_style32.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_iron_hoe_1.png
|
||||
src_assets_library_godot_references_references_crops_potato_growth_stages_potato_seed_packet.png
|
||||
05_Chernobyl_assets_PHASE_PACKS_2_FAZA_2_infrastructure_town_props_torch_lit.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_basement_entrance.png
|
||||
assets_references_npcs_loch_ness_06_souvenir_seller.png
|
||||
assets_references_buildings_kai_house_01_sleeping_bag_only.png
|
||||
src_assets_library_godot_references_references_intro_shots_kai_alone_basement.png
|
||||
src_assets_library_godot_oprema_oro_je_oprema_orožje_mace.png
|
||||
oprema_orožje_spear.png
|
||||
otac_longboard_pier_clean_619f.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_06_souvenir_seller.png
|
||||
assets_references_ui_icons_icon_seed_bag.png
|
||||
src_assets_library_godot_reference_images_npc_style_approved_03_seed_merchant.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_iron_scythe_1.png
|
||||
assets_references_faza2_buildings_decorations_door_repaired.png
|
||||
assets_PHASE_PACKS_2_FAZA_2_buildings_decorations_door_repaired.png
|
||||
MOJE_SLIKE_KONCNA_okolje_fence_repaired.png
|
||||
assets_references_crops_cannabis_growth_stages_cannabis_seed_packet.png
|
||||
assets_references_tool_upgrades_iron_shovel_1.png
|
||||
assets_references_tool_upgrades_steel_scythe_1.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_oprema_zaščita_helmet_knight.png
|
||||
src_assets_library_godot_phases_FAZA_2_buildings_decorations_door_repaired.png
|
||||
assets_references_crops_cabbage_master_reference.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_special_hammer_1.png
|
||||
assets_PHASE_PACKS_1_FAZA_1_crops_cabbage_master_reference.png
|
||||
02_partial_repair.png
|
||||
src_assets_library_godot_oprema_za_ita_oprema_zaščita_helmet_leather.png
|
||||
src_assets_library_godot_notranjost_notranjost_basement_stairs_style32.png
|
||||
action_repair.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_02_repaired.png
|
||||
assets_references_crops_tomato_growth_stages_tomato_seed_packet.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_wood_scythe_1.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_repair_progress_bar.png
|
||||
20_Base_Farm_assets_references_npcs_grassland_03_seed_merchant.png
|
||||
assets_references_items_tools_axe_1.png
|
||||
src_assets_library_godot_references_references_crops_carrot_growth_stages_carrot_seed_packet.png
|
||||
assets_references_crops_wheat_growth_stages_wheat_seed_packet.png
|
||||
otac_longboard_pier_clean.png
|
||||
src_assets_library_godot_phases_FAZA_1_crops_cabbage_master_reference.png
|
||||
assets_sprites_ui_repair_progress_bar.png
|
||||
src_assets_library_godot_phases_FAZA_1_crops_rutabaga_master_reference.png
|
||||
oprema_zaščita_helmet_iron.png
|
||||
src_assets_library_godot_oprema_za_ita_oprema_zaščita_shield_wooden_style32.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_steel_shovel_1.png
|
||||
05_Chernobyl_src_assets_library_godot_phases_FAZA_2_infrastructure_town_props_torch_lit.png
|
||||
src_assets_library_godot_phases_FAZA_2_buildings_decorations_window_repaired.png
|
||||
assets_references_tool_upgrades_stone_axe_1.png
|
||||
assets_references_interiors_kai_house_interior_piercing_tools.png
|
||||
src_assets_library_godot_references_references_items_tools_shovel_1.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_repair_complete_shine.png
|
||||
src_assets_library_godot_oprema_oro_je_oprema_orožje_sword_basic.png
|
||||
src_assets_library_godot_buildings_source_basement_entrance.png
|
||||
assets_references_items_tools_pitchfork_1.png
|
||||
assets_references_tool_upgrades_steel_axe_1.png
|
||||
05_Chernobyl_assets_PHASE_PACKS_2_FAZA_2_infrastructure_town_props_fence_repaired.png
|
||||
MOJE_SLIKE_KONCNA_ostalo_hammer.png
|
||||
assets_references_npcs_loch_ness_07_bagpipe_player.png
|
||||
src_assets_library_godot_references_references_items_tools_watering_can_1.png
|
||||
assets_references_items_tools_sickle_1.png
|
||||
assets_references_items_tools_pickaxe_1.png
|
||||
assets_references_tool_upgrades_steel_pickaxe_1.png
|
||||
assets_references_crops_rutabaga_master_reference.png
|
||||
assets_references_tool_upgrades_special_fishing_net_1.png
|
||||
src_assets_library_godot_references_references_items_tools_hoe_basic_1.png
|
||||
assets_references_tool_upgrades_steel_watering_can_1.png
|
||||
src_assets_library_godot_references_references_tool_upgrades_stone_shovel_1.png
|
||||
93
scripts/refine_animals.py
Normal file
93
scripts/refine_animals.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
BASE_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
ANIMALS_DIR = os.path.join(BASE_DIR, "Animals")
|
||||
|
||||
# Subcategories
|
||||
CATS = {
|
||||
"Domestic": ["cow", "pig", "chicken", "hen", "rooster", "dog", "cat", "horse", "sheep", "goat", "donkey", "ox", "bull", "domaca", "krava", "pujs", "kokos", "pes", "macka", "konj", "ovca", "koza"],
|
||||
"Wild": ["deer", "bear", "wolf", "rabbit", "hare", "fox", "boar", "bird", "eagle", "hawk", "owl", "snake", "frog", "toad", "lizard", "srna", "medved", "vuk", "zajec", "lisica", "divja", "ptica", "sokol", "sova", "kaca", "zaba"],
|
||||
"Insects": ["bee", "butterfly", "moth", "fly", "dragonfly", "firefly", "worm", "spider", "ant", "beetle", "ladybug", "wasp", "hornet", "mosquito", "cricket", "cicada", "mantis", "cebela", "metulj", "muha", "crv", "pajek", "mravlja", "hrosc"],
|
||||
"Infected": ["zombie", "mutant", "infected", "skeleton", "ghost", "monster", "beast", "undead", "boss", "guar", "dragon", "hydra", "yeti", "chimera", "basilisk", "kitsune", "kelpie", "roc", "medusa", "mermaid", "quetzalcoatl", "banshee", "cerberus", "gnome", "golem", "dinosaur", "trex", "raptor", "chupacabra", "kraken", "zombi", "okuzena", "pošast"]
|
||||
}
|
||||
|
||||
def refine_animals():
|
||||
print("🦁 Refining 'Animals' folder...")
|
||||
|
||||
# Ensure subdirs exist
|
||||
for sub in CATS:
|
||||
path = os.path.join(ANIMALS_DIR, sub)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
count = 0
|
||||
moved_out = 0
|
||||
|
||||
# Iterate files in Animals root
|
||||
for filename in os.listdir(ANIMALS_DIR):
|
||||
if filename.startswith("."): continue
|
||||
src = os.path.join(ANIMALS_DIR, filename)
|
||||
if os.path.isdir(src): continue
|
||||
|
||||
lower = filename.lower()
|
||||
target = None
|
||||
|
||||
# 1. Check Keywords
|
||||
for category, keywords in CATS.items():
|
||||
if any(k in lower for k in keywords):
|
||||
target = category
|
||||
break
|
||||
|
||||
if target:
|
||||
dst = os.path.join(ANIMALS_DIR, target, filename)
|
||||
# Collision
|
||||
if os.path.exists(dst):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c=1
|
||||
while os.path.exists(os.path.join(ANIMALS_DIR, target, f"{name}_{c}{ext}")):
|
||||
c+=1
|
||||
dst = os.path.join(ANIMALS_DIR, target, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(src, dst)
|
||||
count += 1
|
||||
else:
|
||||
# 2. Check for Impostors (Logic: If it's not an animal, kick it out?)
|
||||
# Or just leave it for manual check. User said: "kaj ne spada v to mapa poravilno dodaj v mapo katero pase"
|
||||
# Let's try to identify if it belongs to UI, Items etc.
|
||||
|
||||
# Simple heuristic: move unknown to 'glavna_referenca/Items' if it sounds like an item?
|
||||
# Or just move to root 'glavna_referenca' so Ultimate Sort can catch it again?
|
||||
# Let's move to root for safety.
|
||||
|
||||
# For now, I will leave UNKNOWN animals in Animals root,
|
||||
# BUT if I see obvious keywords like "tool", "house", "kai", I move them.
|
||||
|
||||
IMPOSTORS = {
|
||||
"Items": ["tool", "axe", "seed", "potion", "sword", "gun", "armor"],
|
||||
"Environment": ["house", "tree", "rock", "wall", "grass"],
|
||||
"Characters": ["kai", "ana", "gronk", "human", "man", "woman"],
|
||||
"UI": ["button", "menu"]
|
||||
}
|
||||
|
||||
pushed_out = False
|
||||
for cat, kws in IMPOSTORS.items():
|
||||
if any(k in lower for k in kws):
|
||||
# Move to proper main folder
|
||||
real_dst_dir = os.path.join(BASE_DIR, cat)
|
||||
real_dst = os.path.join(real_dst_dir, filename)
|
||||
shutil.move(src, real_dst)
|
||||
moved_out += 1
|
||||
pushed_out = True
|
||||
print(f" 👋 Kicked impostor {filename} to {cat}")
|
||||
break
|
||||
|
||||
if not pushed_out:
|
||||
# Still in Animals root (Maybe an animal I missed)
|
||||
pass
|
||||
|
||||
print(f"✨ Sorted {count} animals into subfolders.")
|
||||
print(f"👋 Kicked {moved_out} impostors out of Animals.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
refine_animals()
|
||||
59
scripts/refine_environment.py
Normal file
59
scripts/refine_environment.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
BASE_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
ENV_DIR = os.path.join(BASE_DIR, "Environment")
|
||||
|
||||
CATS = {
|
||||
"ground": ["grass", "dirt", "path", "road", "cesta", "pot", "zemlja", "floor", "tla", "puddle", "luz?a", "swamp", "snow", "sneg", "sand", "pesek", "tile", "grate", "brick", "soil", "mud", "ash", "wasteland"],
|
||||
"nature": ["tree", "drevo", "bush", "grm", "flower", "roza", "rock", "skala", "plant", "rastlina", "willow", "oak", "pine", "palm", "cactus", "forest", "gozd", "leaf", "list"],
|
||||
"buildings": ["house", "hisa", "shop", "trgovina", "barn", "skedenj", "shed", "tower", "stolp", "mill", "mlin", "well", "vodnjak", "wall", "zid", "fence", "ograja", "ruin", "cabin", "clinic", "school", "museum", "shack", "church", "cerkev", "temple", "gate", "entrance", "hospital", "upgraded", "deluxe"],
|
||||
"mine": ["mine", "rudnik", "ore", "ruda", "crystal", "kristal", "cave", "jama", "stalactite", "stalagmite", "track", "rail", "pillar", "steber"],
|
||||
"decor": ["bench", "klop", "lamp", "luc", "lantern", "torch", "bakla", "sign", "znak", "statue", "kip", "fountain", "grave", "grob", "tomb", "spomenik", "monument", "post", "table", "chair", "scarecrow"]
|
||||
}
|
||||
|
||||
def refine_environment():
|
||||
print("🌲 Refining 'Environment' folder...")
|
||||
|
||||
# Create subdirs
|
||||
for sub in CATS:
|
||||
path = os.path.join(ENV_DIR, sub)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
count = 0
|
||||
|
||||
for filename in os.listdir(ENV_DIR):
|
||||
if filename.startswith("."): continue
|
||||
src = os.path.join(ENV_DIR, filename)
|
||||
if os.path.isdir(src): continue
|
||||
|
||||
lower = filename.lower()
|
||||
target = None
|
||||
|
||||
for cat, kws in CATS.items():
|
||||
if any(k in lower for k in kws):
|
||||
target = cat
|
||||
break
|
||||
|
||||
if target:
|
||||
dst = os.path.join(ENV_DIR, target, filename)
|
||||
# Collision
|
||||
if os.path.exists(dst):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c=1
|
||||
while os.path.exists(os.path.join(ENV_DIR, target, f"{name}_{c}{ext}")):
|
||||
c+=1
|
||||
dst = os.path.join(ENV_DIR, target, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(src, dst)
|
||||
count += 1
|
||||
else:
|
||||
# Impostor check? Or leave loose?
|
||||
# User wants: "kaj ne spada v to mapa poravilno dodaj v mapo katero pase"
|
||||
pass
|
||||
|
||||
print(f"✨ Sorted {count} environmental assets.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
refine_environment()
|
||||
79
scripts/sort_characters_visual.py
Normal file
79
scripts/sort_characters_visual.py
Normal file
@@ -0,0 +1,79 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
SOURCE_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
CHAR_ROOT = os.path.join(SOURCE_DIR, "Characters")
|
||||
|
||||
# Mapping "Visual Features" to Folders
|
||||
RULES = [
|
||||
# KAI (Dredi, Pirsingi, Player)
|
||||
{
|
||||
"target": "kai",
|
||||
"keywords": ["kai", "player", "hero", "dread", "piercing", "pink_hair"]
|
||||
},
|
||||
# ANA (Sestra)
|
||||
{
|
||||
"target": "ana",
|
||||
"keywords": ["ana", "sister", "sestra", "girl", "child"]
|
||||
},
|
||||
# GRONK (Vape, Pink Dredi)
|
||||
{
|
||||
"target": "gronk",
|
||||
"keywords": ["gronk", "vape", "punk", "smoke"]
|
||||
},
|
||||
# STARŠA (Mama, Ata, Duhovi)
|
||||
{
|
||||
"target": "parents",
|
||||
"keywords": ["mom", "dad", "parent", "starse", "mama", "oce", "ghost", "spirit", "portrait", "frame"]
|
||||
},
|
||||
# NPC (Vsi ostali ljudje)
|
||||
{
|
||||
"target": "npc",
|
||||
"keywords": ["npc", "villager", "doctor", "merchant", "trader", "baker", "smith", "man", "woman", "person", "guard", "soldier", "enemy", "human"]
|
||||
},
|
||||
]
|
||||
|
||||
def sort_characters_smart():
|
||||
print("🧠 Začenjam pametno razvrščanje LIKOV v /Characters...")
|
||||
|
||||
count = 0
|
||||
# Walk only the source root (don't go into subfolders yet)
|
||||
for filename in os.listdir(SOURCE_DIR):
|
||||
if filename.startswith("."): continue
|
||||
src_path = os.path.join(SOURCE_DIR, filename)
|
||||
if os.path.isdir(src_path): continue
|
||||
|
||||
lower_name = filename.lower()
|
||||
target_subfolder = None
|
||||
|
||||
# Check rules
|
||||
for rule in RULES:
|
||||
if any(k in lower_name for k in rule["keywords"]):
|
||||
target_subfolder = rule["target"]
|
||||
break # Found a match, stop specific rules
|
||||
|
||||
# Move if match found
|
||||
if target_subfolder:
|
||||
dest_dir = os.path.join(CHAR_ROOT, target_subfolder)
|
||||
dest_path = os.path.join(dest_dir, filename)
|
||||
|
||||
# Create subfolder if missing for some reason
|
||||
if not os.path.exists(dest_dir):
|
||||
os.makedirs(dest_dir)
|
||||
|
||||
# Handle collision
|
||||
if os.path.exists(dest_path):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c = 1
|
||||
while os.path.exists(os.path.join(dest_dir, f"{name}_{c}{ext}")):
|
||||
c += 1
|
||||
dest_path = os.path.join(dest_dir, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(src_path, dest_path)
|
||||
count += 1
|
||||
# print(f" 👤 {filename} -> Characters/{target_subfolder}")
|
||||
|
||||
print(f"✨ Premaknil sem {count} slik likov v mapo Characters!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
sort_characters_smart()
|
||||
76
scripts/ultimate_sort.py
Normal file
76
scripts/ultimate_sort.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
ROOT_DIR = os.path.abspath("assets/slike/glavna_referenca")
|
||||
|
||||
# Define target folders
|
||||
FOLDERS = {
|
||||
"UI": ["button", "gumb", "heart", "srce", "health", "frame", "okvir", "meter", "virus", "amnesia", "blur", "ui_", "interface", "vfx", "sparkle", "glow", "smoke", "burst", "shine", "panel", "bar", "stamina", "icon", "slot", "window", "cursor", "menu", "action_", "glint", "preference", "arrow", "scroll", "stars", "visual", "badge", "reference", "eye", "uploaded"],
|
||||
"Items": ["tool", "orodje", "axe", "pickaxe", "kramp", "sekira", "seed", "seme", "longboard", "memory", "photo", "helmet", "souvenir", "item_", "bag", "crate", "barrel", "box", "chest", "plank", "wood", "log", "les", "stone", "kamen", "iron", "gold", "ore", "ingot", "ruda", "rope", "vrv", "key", "kljuc", "potion", "napoj", "sprout", "harvest", "crop", "strawberry", "corn", "wheat", "potato", "carrot", "buce", "pumpkin", "scrap", "supply", "sandbag", "trough", "shirt", "pants", "hat", "clothing", "armor", "food", "meat", "coffee", "coal", "mask", "gas", "grenade", "club", "golf", "weapon", "stick", "gear", "sapling", "marker", "crossbow", "backpack", "pack", "battery", "bread", "candy", "cake", "cloth", "crystal", "egg", "glue", "honey", "herb", "machete", "flail", "antidote", "drink", "leaf", "lettuce", "cannabis", "download", "pistol", "rifle", "shotgun", "sword", "mace", "spear", "bow", "nailbat", "bat", "whip", "scythe", "sickle", "shears", "staff", "molotov", "knife", "gauntlet", "soup", "milk", "cheese", "berries", "soda", "sulfur", "tape", "wire", "plastic", "screw", "nail", "bucket", "boots", "scarf", "vest", "hoodie", "mushroom", "pepper", "glass", "shard", "book", "bottle", "sack", "flour", "grain", "needle", "thread", "wrench", "trowel", "chain", "pitchfork", "pan", "rake", "bone", "chemistry", "skull", "banner", "resource", "collect"],
|
||||
"Environment": ["grass", "trava", "dirt", "zemlja", "mine_wall", "house", "hisa", "barn", "skedenj", "building", "city", "mesto", "wall", "fence", "graveyard", "church", "shop", "ruin", "temple", "shrine", "monument", "tower", "silo", "windmill", "well", "fountain", "bench", "table", "chair", "lamp", "lantern", "torch", "sign", "path", "cesta", "road", "tree", "drevo", "bush", "grm", "flower", "roza", "rock", "skala", "decor", "arch", "plaza", "overhang", "roof", "repair", "flag", "laundry", "scarecrow", "cart", "wagon", "bicycle", "anvil", "market", "stall", "board", "post", "trash", "pile", "rubble", "ice", "winter", "snow", "soil", "pen", "ash", "wasteland", "gate", "entrance", "gargoyle", "interior", "shelf", "sink", "rack", "circle", "furniture", "cabin", "fire", "pipe", "storage", "pump", "door", "grave", "shelves", "area", "display", "sofa", "stove", "fridge", "wardrobe", "counter", "cauldron", "puddle", "blood", "slime", "void", "willow", "oak", "portal", "shack", "school", "museum", "memorial", "plaque", "straight", "tjunction", "patch", "stage", "foundation", "clinic", "hospital", "shed", "plot", "spring", "grate", "brick", "fog", "upgraded", "deluxe", "simple", "dark"],
|
||||
"Characters": ["kai", "ana", "gronk", "dreads", "piercing", "ghost", "spirit", "parent", "mom", "dad", "starse", "npc", "liki", "player", "walk", "idle", "attack", "jump", "run", "death", "hit", "human", "man", "woman", "person", "trader", "doctor", "smith", "guard", "susi", "tehnik", "druid", "priest", "chief", "guide", "keeper", "king", "sage", "tamer", "hermit", "scribe", "nomad", "ranger", "townsfolk", "hunter", "fisher", "researcher", "thief", "monk", "carver", "specialist", "witch", "stalker", "police", "survivor", "raider", "diver", "engineer", "scientist", "captain", "advisor", "worker", "apprentice", "oracle", "dancer", "scholar", "mystic", "enchantress", "mechanic", "diplomat", "expert", "elf", "assassin", "warlord", "giant", "boss", "lord", "angel", "shepherd", "historian", "hazmat", "elves", "fairies", "trolls"],
|
||||
"Animals": ["dog", "pes", "insect", "zuzelka", "bug", "deer", "srna", "rabbit", "zajec", "cow", "krava", "pig", "zombie_dog", "infected_animal", "mutant", "zombi", "zombie", "monster", "beast", "wolf", "vuk", "bear", "medved", "spider", "pajek", "bee", "cebela", "metulj", "butterfly", "chicken", "kokos", "bird", "frog", "snake", "moth", "cicada", "dragon", "fly", "wasp", "goat", "trex", "hydra", "yeti", "chimera", "basilisk", "kitsune", "kelpie", "roc", "medusa", "mermaid", "quetzalcoatl", "banshee", "cerberus", "gnome", "golem", "skeleton", "horse", "brachiosaurus", "carnotaurus", "parasaurolophus", "spinosaurus", "morpho", "chupacabra", "kraken"],
|
||||
"Biomes": ["meadow", "travnik", "forest", "gozd", "swamp", "snow", "biome_", "desert", "puščava", "jungle", "dzungla", "volcano", "vulkan", "cave", "jama", "atlantis", "chernobyl", "egypt"]
|
||||
}
|
||||
|
||||
def ultimate_sort():
|
||||
print("🚀 Starting ULTIMATE SORT based on user rules...")
|
||||
|
||||
# Create folders
|
||||
for folder in FOLDERS:
|
||||
path = os.path.join(ROOT_DIR, folder)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
stats = {k: 0 for k in FOLDERS}
|
||||
|
||||
# Iterate all files
|
||||
for filename in os.listdir(ROOT_DIR):
|
||||
if filename.startswith("."): continue
|
||||
src_path = os.path.join(ROOT_DIR, filename)
|
||||
if os.path.isdir(src_path): continue # Skip folders we just made
|
||||
|
||||
lower_name = filename.lower()
|
||||
target_folder = None
|
||||
|
||||
# Priority Logic (First match wins)
|
||||
# 1. UI is very specific
|
||||
if any(k in lower_name for k in FOLDERS["UI"]):
|
||||
target_folder = "UI"
|
||||
# 2. Characters (Specific + Suffixes)
|
||||
elif any(k in lower_name for k in FOLDERS["Characters"]) or \
|
||||
lower_name.endswith(("er.png", "or.png", "ist.png", "ant.png")):
|
||||
target_folder = "Characters"
|
||||
# 3. Animals
|
||||
elif any(k in lower_name for k in FOLDERS["Animals"]):
|
||||
target_folder = "Animals"
|
||||
# 4. Items
|
||||
elif any(k in lower_name for k in FOLDERS["Items"]):
|
||||
target_folder = "Items"
|
||||
# 5. Environment (Buildings go here)
|
||||
elif any(k in lower_name for k in FOLDERS["Environment"]):
|
||||
target_folder = "Environment"
|
||||
# 6. Biomes
|
||||
elif any(k in lower_name for k in FOLDERS["Biomes"]):
|
||||
target_folder = "Biomes"
|
||||
|
||||
if target_folder:
|
||||
dst_path = os.path.join(ROOT_DIR, target_folder, filename)
|
||||
|
||||
# Handle duplicates
|
||||
if os.path.exists(dst_path):
|
||||
name, ext = os.path.splitext(filename)
|
||||
c = 1
|
||||
while os.path.exists(os.path.join(ROOT_DIR, target_folder, f"{name}_{c}{ext}")):
|
||||
c += 1
|
||||
dst_path = os.path.join(ROOT_DIR, target_folder, f"{name}_{c}{ext}")
|
||||
|
||||
shutil.move(src_path, dst_path)
|
||||
stats[target_folder] += 1
|
||||
|
||||
print("\n✨ SORTING COMPLETE:")
|
||||
for k, v in stats.items():
|
||||
print(f" 📂 {k}: {v} files")
|
||||
|
||||
if __name__ == "__main__":
|
||||
ultimate_sort()
|
||||
Reference in New Issue
Block a user