#!/usr/bin/env python3 """ ๐Ÿ‡ธ๐Ÿ‡ฎ KOMPLEKNA REORGANIZACIJA ASSETOV - SLOVENSKO 1. Scan za duplikate (iste slike) 2. Scan za neuporabljene slike 3. Premakni vse v slovenske mape 4. Izbriลกi stare angleลกke mape 5. Update vse skripte NOVA STRUKTURA (SLOVENSKO): assets/slike/ โ”œโ”€โ”€ liki/ (protagonisti) โ”œโ”€โ”€ sovrazniki/ (zombiji, mutanti, bossi) โ”œโ”€โ”€ biomi/ (18 zon) โ”œโ”€โ”€ zgradbe/ (stavbe) โ”œโ”€โ”€ predmeti/ (tools, seeds, food) โ”œโ”€โ”€ orozje/ (weapons) โ”œโ”€โ”€ rastline/ (crops, trees, plants) โ”œโ”€โ”€ ui/ (interface) โ”œโ”€โ”€ efekti/ (effects) โ””โ”€โ”€ cutscene/ (flashbacks) """ import hashlib import shutil import re from pathlib import Path from collections import defaultdict from typing import Dict, List, Set # Paths REPO = Path("/Users/davidkotnik/repos/novafarma") OLD_ASSETS = REPO / "assets/images" NEW_ASSETS = REPO / "assets/slike" SRC_DIR = REPO / "src" # Mapping: OLD โ†’ NEW MAPPING = { "characters": "liki", "enemies": "sovrazniki", "biomes": "biomi", "buildings": "zgradbe", "items": "predmeti", "weapons": "orozje", "plants": "rastline", "crops": "rastline", # merge crops into rastline "ui": "ui", "effects": "efekti", "cutscenes": "cutscene", "environment": "rastline", # trees, rocks โ†’ rastline "props": "zgradbe", # props โ†’ zgradbe (campfire, etc) "bosses": "sovrazniki", # bosses โ†’ sovrazniki "mutanti": "sovrazniki", # mutanti โ†’ sovrazniki "zivali": "rastline", # animals โ†’ rastline "npcs": "liki", # npcs โ†’ liki "workstations": "zgradbe", } class AssetReorganizer: def __init__(self): self.duplicates: Dict[str, List[Path]] = defaultdict(list) self.file_hashes: Dict[str, Path] = {} self.all_pngs: List[Path] = [] self.used_assets: Set[str] = set() self.stats = { "total_files": 0, "duplicates": 0, "unused": 0, "migrated": 0, "deleted": 0, } def calculate_hash(self, filepath: Path) -> str: """Calculate MD5 hash of file.""" hash_md5 = hashlib.md5() with open(filepath, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() def scan_duplicates(self): """Find duplicate images by content hash.""" print("๐Ÿ” Scanning for duplicate images...") self.all_pngs = list(OLD_ASSETS.rglob("*.png")) self.stats["total_files"] = len(self.all_pngs) print(f" Found {len(self.all_pngs)} PNG files") for filepath in self.all_pngs: file_hash = self.calculate_hash(filepath) if file_hash in self.file_hashes: # Duplicate found! original = self.file_hashes[file_hash] self.duplicates[file_hash].append(filepath) print(f" ๐Ÿ”ด DUPLICATE: {filepath.name}") print(f" โ†’ Same as: {original.name}") else: self.file_hashes[file_hash] = filepath print(f"\n โœ… Found {len(self.duplicates)} duplicate groups\n") self.stats["duplicates"] = sum(len(files) for files in self.duplicates.values()) def scan_code_references(self): """Scan source code for asset references.""" print("๐Ÿ” Scanning code for asset usage...") # Patterns to match asset references patterns = [ r'["\']([^"\']*\.png)["\']', # "filename.png" r'load\.image\(["\']([^"\']+)["\']', # Phaser load.image r'assets/images/([^"\']+\.png)', # Direct path references ] code_files = list(SRC_DIR.rglob("*.js")) + list(REPO.glob("*.js")) for code_file in code_files: try: content = code_file.read_text(encoding='utf-8') for pattern in patterns: matches = re.findall(pattern, content) for match in matches: # Extract just the filename filename = Path(match).name self.used_assets.add(filename) except Exception as e: print(f" โš ๏ธ Could not read {code_file.name}: {e}") print(f" โœ… Found {len(self.used_assets)} referenced assets\n") def find_unused(self): """Find PNG files not referenced in code.""" print("๐Ÿ” Finding unused assets...") unused = [] for filepath in self.all_pngs: if filepath.name not in self.used_assets: # Also check without timestamps/suffixes base_name = re.sub(r'_\d{13,}\.png$', '.png', filepath.name) base_name = re.sub(r'_(styleA|styleB|original|sprite|preview)_.*\.png$', '.png', base_name) if base_name not in self.used_assets and filepath.name not in self.used_assets: unused.append(filepath) self.stats["unused"] = len(unused) print(f" โš ๏ธ Found {len(unused)} potentially unused files\n") return unused def create_slovenian_structure(self): """Create new Slovenian folder structure.""" print("๐Ÿ“ Creating Slovenian folder structure...") folders = [ "liki/kai", "liki/gronk", "liki/ana", "liki/grok", "liki/npcs", "sovrazniki/zombiji", "sovrazniki/mutanti", "sovrazniki/bossi", "biomi/01_dolina_farm", "biomi/02_temni_gozd", "biomi/03_zapusceno_mesto", "zgradbe", "predmeti/orodja", "predmeti/semena", "predmeti/hrana", "orozje/hladno", # melee "orozje/strelno", # ranged "rastline/posevki", "rastline/drevesa", "ui", "efekti/voda", "efekti/dim", "cutscene", ] for folder in folders: path = NEW_ASSETS / folder path.mkdir(parents=True, exist_ok=True) print(f" โœ… {folder}/") print() def migrate_assets(self, delete_duplicates=False, delete_unused=False): """Migrate all assets to new structure.""" print("๐Ÿšš Migrating assets to Slovenian structure...") # Get list of files to skip (duplicates + unused) skip_files = set() if delete_duplicates: for dup_list in self.duplicates.values(): # Keep first, delete rest skip_files.update(dup_list) if delete_unused: unused = self.find_unused() skip_files.update(unused) # Migrate each category for old_name, new_name in MAPPING.items(): old_path = OLD_ASSETS / old_name if not old_path.exists(): continue print(f"\n๐Ÿ“ฆ {old_name}/ โ†’ {new_name}/") # Find all PNGs in this category png_files = list(old_path.rglob("*.png")) for src_file in png_files: if src_file in skip_files: print(f" โญ๏ธ SKIP: {src_file.name} (duplicate/unused)") continue # Determine target based on filename target_dir = NEW_ASSETS / new_name # Sub-categorization if new_name == "liki": if "kai_" in src_file.name.lower(): target_dir = NEW_ASSETS / "liki/kai" elif "gronk_" in src_file.name.lower(): target_dir = NEW_ASSETS / "liki/gronk" elif "ana_" in src_file.name.lower(): target_dir = NEW_ASSETS / "liki/ana" elif "npc_" in src_file.name.lower(): target_dir = NEW_ASSETS / "liki/npcs" elif new_name == "sovrazniki": if "zombie_" in src_file.name.lower(): target_dir = NEW_ASSETS / "sovrazniki/zombiji" elif "mutant_" in src_file.name.lower(): target_dir = NEW_ASSETS / "sovrazniki/mutanti" elif "boss_" in src_file.name.lower(): target_dir = NEW_ASSETS / "sovrazniki/bossi" elif new_name == "rastline": if "wheat_" in src_file.name.lower() or "crop_" in src_file.name.lower(): target_dir = NEW_ASSETS / "rastline/posevki" elif "tree_" in src_file.name.lower() or "oak_" in src_file.name.lower(): target_dir = NEW_ASSETS / "rastline/drevesa" elif new_name == "predmeti": if "hoe_" in src_file.name.lower() or "axe_" in src_file.name.lower() or "watering" in src_file.name.lower(): target_dir = NEW_ASSETS / "predmeti/orodja" elif "seed" in src_file.name.lower(): target_dir = NEW_ASSETS / "predmeti/semena" elif new_name == "efekti": if "water_" in src_file.name.lower(): target_dir = NEW_ASSETS / "efekti/voda" target_dir.mkdir(parents=True, exist_ok=True) dest_file = target_dir / src_file.name try: shutil.copy2(src_file, dest_file) print(f" โœ… {src_file.name}") self.stats["migrated"] += 1 except Exception as e: print(f" โŒ {src_file.name}: {e}") # Also migrate demo/ folder demo_path = OLD_ASSETS / "demo" if demo_path.exists(): print(f"\n๐Ÿ“ฆ demo/ โ†’ (various Slovenian folders)") self._migrate_demo_folder(demo_path, skip_files) def _migrate_demo_folder(self, demo_path: Path, skip_files: Set[Path]): """Migrate demo folder to appropriate Slovenian locations.""" for src_file in demo_path.rglob("*.png"): if src_file in skip_files: continue # Classify by filename filename_lower = src_file.name.lower() if "kai_" in filename_lower: target = NEW_ASSETS / "liki/kai" / src_file.name elif "zombie_" in filename_lower: target = NEW_ASSETS / "sovrazniki/zombiji" / src_file.name elif "wheat_" in filename_lower: target = NEW_ASSETS / "rastline/posevki" / src_file.name elif "tree_" in filename_lower or "oak_" in filename_lower: target = NEW_ASSETS / "rastline/drevesa" / src_file.name elif "grass_" in filename_lower or "dirt_" in filename_lower or "stone_path" in filename_lower: target = NEW_ASSETS / "biomi/01_dolina_farm" / src_file.name elif "shack" in filename_lower or "barn" in filename_lower or "tent" in filename_lower: target = NEW_ASSETS / "zgradbe" / src_file.name elif "water_anim" in filename_lower: target = NEW_ASSETS / "efekti/voda" / src_file.name elif "ui_" in filename_lower: target = NEW_ASSETS / "ui" / src_file.name else: target = NEW_ASSETS / "ostalo" / src_file.name target.parent.mkdir(parents=True, exist_ok=True) try: shutil.copy2(src_file, target) print(f" โœ… {src_file.name}") self.stats["migrated"] += 1 except Exception as e: print(f" โŒ {src_file.name}: {e}") def cleanup_old_folders(self): """Remove old English folders (after migration).""" print("\n๐Ÿ—‘๏ธ Removing old English folders...") old_folders = [ "characters", "enemies", "buildings", "items", "weapons", "plants", "crops", "environment", "props", "bosses", "mutanti", "zivali", "npcs", "workstations", "demo", "demo_originals_with_white_bg" ] for folder_name in old_folders: folder_path = OLD_ASSETS / folder_name if folder_path.exists() and folder_path.is_dir(): try: shutil.rmtree(folder_path) print(f" ๐Ÿ—‘๏ธ Deleted: {folder_name}/") except Exception as e: print(f" โŒ Could not delete {folder_name}/: {e}") def generate_report(self): """Generate final report.""" print("\n" + "="*70) print("๐Ÿ“Š REORGANIZATION COMPLETE!") print("="*70) print(f" Total files scanned: {self.stats['total_files']:4d}") print(f" Duplicates found: {self.stats['duplicates']:4d}") print(f" Unused files: {self.stats['unused']:4d}") print(f" Files migrated: {self.stats['migrated']:4d}") print("="*70) print("\nโœ… Nova struktura: assets/slike/") print(" ๐Ÿ“‚ liki/") print(" ๐Ÿ“‚ sovrazniki/") print(" ๐Ÿ“‚ biomi/") print(" ๐Ÿ“‚ zgradbe/") print(" ๐Ÿ“‚ predmeti/") print(" ๐Ÿ“‚ orozje/") print(" ๐Ÿ“‚ rastline/") print(" ๐Ÿ“‚ ui/") print(" ๐Ÿ“‚ efekti/") print(" ๐Ÿ“‚ cutscene/") print("="*70) def main(): print("="*70) print("๐Ÿ‡ธ๐Ÿ‡ฎ KOMPLEKNA REORGANIZACIJA - SLOVENSKO") print("="*70) reorganizer = AssetReorganizer() # Step 1: Scan duplicates reorganizer.scan_duplicates() # Step 2: Scan code reorganizer.scan_code_references() # Step 3: Find unused reorganizer.find_unused() # Step 4: Create new structure reorganizer.create_slovenian_structure() # Step 5: Migrate (with cleanup) reorganizer.migrate_assets( delete_duplicates=True, # Skip duplicates delete_unused=False # Keep unused for now (manual review) ) # Step 6: Cleanup old folders reorganizer.cleanup_old_folders() # Step 7: Report reorganizer.generate_report() print("\n๐Ÿ“ NASLEDNJI KORAKI:") print(" 1. git status # Preglej spremembe") print(" 2. Preveri da vse deluje") print(" 3. git add -A && git commit -m '๐Ÿ‡ธ๐Ÿ‡ฎ Reorganizacija v slovensko strukturo'") if __name__ == "__main__": main()