Complete Asset Audit JAN 12 2026 - 3477 images cataloged, new asset gallery with all images, DNEVNIK and GAME_BIBLE updated
This commit is contained in:
454
scripts/generate_asset_gallery.py
Normal file
454
scripts/generate_asset_gallery.py
Normal file
@@ -0,0 +1,454 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate complete asset gallery HTML with all images
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
def main():
|
||||
assets_dir = Path("assets")
|
||||
|
||||
# Find all images
|
||||
extensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif']
|
||||
all_images = []
|
||||
|
||||
for ext in extensions:
|
||||
all_images.extend(assets_dir.rglob(f"*{ext}"))
|
||||
all_images.extend(assets_dir.rglob(f"*{ext.upper()}"))
|
||||
|
||||
# Remove duplicates and sort
|
||||
all_images = sorted(set(str(p) for p in all_images))
|
||||
|
||||
print(f"Found {len(all_images)} images")
|
||||
|
||||
# Categorize images
|
||||
categories = {}
|
||||
|
||||
def get_category(path):
|
||||
path_lower = path.lower()
|
||||
|
||||
if 'phase_packs/0_demo' in path_lower:
|
||||
if 'crops' in path_lower:
|
||||
return '🎮 DEMO - Crops'
|
||||
elif 'tools' in path_lower:
|
||||
return '🎮 DEMO - Tools'
|
||||
elif 'animals' in path_lower:
|
||||
return '🎮 DEMO - Animals'
|
||||
return '🎮 DEMO - Other'
|
||||
|
||||
elif 'phase_packs/1_faza_1' in path_lower:
|
||||
if 'crops' in path_lower:
|
||||
return '🌾 FAZA 1 - Crops'
|
||||
elif 'animals' in path_lower:
|
||||
return '🌾 FAZA 1 - Animals'
|
||||
elif 'tools' in path_lower:
|
||||
return '🌾 FAZA 1 - Tools'
|
||||
elif 'infrastructure' in path_lower:
|
||||
return '🌾 FAZA 1 - Infrastructure'
|
||||
return '🌾 FAZA 1 - Other'
|
||||
|
||||
elif 'phase_packs/2_faza_2' in path_lower:
|
||||
if 'buildings' in path_lower:
|
||||
return '🏘️ FAZA 2 - Buildings'
|
||||
elif 'npcs' in path_lower:
|
||||
return '🏘️ FAZA 2 - NPCs'
|
||||
elif 'infrastructure' in path_lower:
|
||||
return '🏘️ FAZA 2 - Infrastructure'
|
||||
return '🏘️ FAZA 2 - Other'
|
||||
|
||||
elif 'references/kai' in path_lower:
|
||||
return '👤 References - Kai'
|
||||
elif 'references/ana' in path_lower:
|
||||
return '👤 References - Ana'
|
||||
elif 'references/gronk' in path_lower:
|
||||
return '👤 References - Gronk'
|
||||
elif 'references/susi' in path_lower:
|
||||
return '👤 References - Susi'
|
||||
elif 'references/npcs' in path_lower:
|
||||
return '👥 References - NPCs'
|
||||
elif 'references/creatures' in path_lower:
|
||||
return '🐉 References - Creatures'
|
||||
elif 'references/buildings' in path_lower:
|
||||
return '🏠 References - Buildings'
|
||||
elif 'references/trees' in path_lower:
|
||||
return '🌳 References - Trees'
|
||||
elif 'references/ui' in path_lower:
|
||||
return '🎨 References - UI'
|
||||
elif 'references/' in path_lower:
|
||||
return '📸 References - Other'
|
||||
|
||||
elif 'sprites' in path_lower:
|
||||
return '🖼️ Sprites'
|
||||
elif 'crops' in path_lower:
|
||||
return '🌱 Crops'
|
||||
elif 'buildings' in path_lower:
|
||||
return '🏠 Buildings'
|
||||
elif 'characters' in path_lower:
|
||||
return '👤 Characters'
|
||||
elif 'grounds' in path_lower:
|
||||
return '🌍 Ground Tiles'
|
||||
elif 'terrain' in path_lower:
|
||||
return '⛰️ Terrain'
|
||||
elif 'ui' in path_lower:
|
||||
return '🎨 UI'
|
||||
elif 'vfx' in path_lower:
|
||||
return '✨ VFX'
|
||||
elif 'props' in path_lower:
|
||||
return '🪑 Props'
|
||||
elif 'slike' in path_lower:
|
||||
return '🖼️ Slike'
|
||||
|
||||
return '📦 Other'
|
||||
|
||||
for img_path in all_images:
|
||||
cat = get_category(img_path)
|
||||
if cat not in categories:
|
||||
categories[cat] = []
|
||||
categories[cat].append(img_path)
|
||||
|
||||
# Generate HTML
|
||||
html = f'''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🎮 NovaFarma - Vse Slike ({len(all_images)})</title>
|
||||
<style>
|
||||
* {{ box-sizing: border-box; }}
|
||||
body {{
|
||||
background: linear-gradient(135deg, #0a0a0a, #1a1a2e, #16213e);
|
||||
color: #fff;
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}}
|
||||
h1 {{
|
||||
text-align: center;
|
||||
color: #ff4444;
|
||||
font-size: 2.5em;
|
||||
text-shadow: 2px 2px 4px #000, 0 0 20px rgba(255, 68, 68, 0.5);
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
.stats {{
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.1em;
|
||||
}}
|
||||
.stats span {{
|
||||
color: #ff6666;
|
||||
font-weight: bold;
|
||||
}}
|
||||
.controls {{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
flex-wrap: wrap;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
padding: 15px;
|
||||
z-index: 100;
|
||||
border-radius: 10px;
|
||||
}}
|
||||
.controls input {{
|
||||
padding: 12px 20px;
|
||||
border: 2px solid #444;
|
||||
border-radius: 25px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
font-size: 1em;
|
||||
width: 400px;
|
||||
max-width: 90vw;
|
||||
transition: border-color 0.3s;
|
||||
}}
|
||||
.controls input:focus {{
|
||||
outline: none;
|
||||
border-color: #ff4444;
|
||||
box-shadow: 0 0 10px rgba(255, 68, 68, 0.3);
|
||||
}}
|
||||
.controls select {{
|
||||
padding: 12px 20px;
|
||||
border: 2px solid #444;
|
||||
border-radius: 25px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
}}
|
||||
.section {{
|
||||
margin: 30px 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
border: 1px solid #333;
|
||||
}}
|
||||
.section h2 {{
|
||||
color: #ff6666;
|
||||
border-bottom: 2px solid #444;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 0;
|
||||
cursor: pointer;
|
||||
}}
|
||||
.section h2:hover {{
|
||||
color: #ff8888;
|
||||
}}
|
||||
.section h2 .count {{
|
||||
background: #ff4444;
|
||||
color: #fff;
|
||||
padding: 3px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.7em;
|
||||
margin-left: 10px;
|
||||
}}
|
||||
.grid {{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 15px;
|
||||
}}
|
||||
.card {{
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid #333;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}}
|
||||
.card:hover {{
|
||||
transform: translateY(-3px) scale(1.02);
|
||||
border-color: #ff4444;
|
||||
box-shadow: 0 8px 25px rgba(255, 68, 68, 0.3);
|
||||
background: rgba(255, 68, 68, 0.1);
|
||||
}}
|
||||
.card img {{
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: contain;
|
||||
background: linear-gradient(45deg, #111 25%, #222 25%, #222 50%, #111 50%, #111 75%, #222 75%);
|
||||
background-size: 16px 16px;
|
||||
border-radius: 6px;
|
||||
}}
|
||||
.card .name {{
|
||||
margin: 8px 0 3px;
|
||||
font-size: 0.7em;
|
||||
color: #aaa;
|
||||
font-family: 'Consolas', monospace;
|
||||
word-break: break-all;
|
||||
max-height: 2.2em;
|
||||
overflow: hidden;
|
||||
}}
|
||||
.card button {{
|
||||
background: linear-gradient(135deg, #ff4444, #cc3333);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 5px 12px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 0.75em;
|
||||
margin-top: 6px;
|
||||
transition: all 0.2s;
|
||||
}}
|
||||
.card button:hover {{
|
||||
background: linear-gradient(135deg, #ff6666, #ff4444);
|
||||
}}
|
||||
.copied {{
|
||||
background: linear-gradient(135deg, #44ff44, #33cc33) !important;
|
||||
}}
|
||||
.modal {{
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}}
|
||||
.modal.active {{
|
||||
display: flex;
|
||||
}}
|
||||
.modal img {{
|
||||
max-width: 90%;
|
||||
max-height: 80%;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 50px rgba(255, 68, 68, 0.5);
|
||||
}}
|
||||
.modal .info {{
|
||||
color: #aaa;
|
||||
margin-top: 15px;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}}
|
||||
.modal .close {{
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
font-size: 3em;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
}}
|
||||
.modal .close:hover {{
|
||||
color: #ff4444;
|
||||
}}
|
||||
.hidden {{ display: none !important; }}
|
||||
.toc {{
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
}}
|
||||
.toc h3 {{ color: #ff6666; margin-top: 0; }}
|
||||
.toc ul {{ list-style: none; padding: 0; column-count: 3; }}
|
||||
.toc li {{ margin: 5px 0; }}
|
||||
.toc a {{ color: #aaa; text-decoration: none; }}
|
||||
.toc a:hover {{ color: #ff4444; }}
|
||||
@media (max-width: 768px) {{
|
||||
.toc ul {{ column-count: 1; }}
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎮 NOVAFARMA - VSE SLIKE 💀</h1>
|
||||
<div class="stats">
|
||||
<span>{len(all_images)}</span> slik v <span>{len(categories)}</span> kategorijah |
|
||||
Generirano: {datetime.now().strftime("%d.%m.%Y %H:%M")}
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" id="search" placeholder="🔍 Išči slike... (npr. 'kai', 'zombie', 'crop')" />
|
||||
<select id="categoryFilter">
|
||||
<option value="">📁 Vse kategorije ({len(all_images)})</option>
|
||||
'''
|
||||
|
||||
# Add category options
|
||||
for cat in sorted(categories.keys()):
|
||||
html += f' <option value="{cat}">{cat} ({len(categories[cat])})</option>\n'
|
||||
|
||||
html += ''' </select>
|
||||
</div>
|
||||
|
||||
<div class="toc">
|
||||
<h3>📋 Kazalo kategorij</h3>
|
||||
<ul>
|
||||
'''
|
||||
|
||||
# Add TOC
|
||||
for cat in sorted(categories.keys()):
|
||||
safe_id = cat.replace(' ', '_').replace('-', '_')
|
||||
html += f' <li><a href="#{safe_id}">{cat} ({len(categories[cat])})</a></li>\n'
|
||||
|
||||
html += ''' </ul>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
'''
|
||||
|
||||
# Add sections
|
||||
for cat in sorted(categories.keys()):
|
||||
safe_id = cat.replace(' ', '_').replace('-', '_')
|
||||
html += f'''
|
||||
<div class="section" data-category="{cat}" id="{safe_id}">
|
||||
<h2 onclick="toggleSection(this)">{cat} <span class="count">{len(categories[cat])}</span></h2>
|
||||
<div class="grid">
|
||||
'''
|
||||
|
||||
for img_path in sorted(categories[cat]):
|
||||
name = os.path.basename(img_path)
|
||||
html += f''' <div class="card" data-path="{img_path}" data-name="{name.lower()}" onclick="openModal('{img_path}', '{name}')">
|
||||
<img src="{img_path}" alt="{name}" loading="lazy" onerror="this.style.opacity='0.3'">
|
||||
<div class="name">{name}</div>
|
||||
<button onclick="event.stopPropagation(); copy('{img_path}', this)">📋</button>
|
||||
</div>
|
||||
'''
|
||||
|
||||
html += ''' </div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
html += ''' </div>
|
||||
|
||||
<div class="modal" id="modal" onclick="closeModal()">
|
||||
<span class="close">×</span>
|
||||
<img id="modalImg" src="" alt="Preview">
|
||||
<div class="info" id="modalInfo"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copy(text, btn) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
btn.textContent = '✅';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
btn.textContent = '📋';
|
||||
btn.classList.remove('copied');
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
function openModal(src, name) {
|
||||
document.getElementById('modalImg').src = src;
|
||||
document.getElementById('modalInfo').textContent = src;
|
||||
document.getElementById('modal').classList.add('active');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('modal').classList.remove('active');
|
||||
}
|
||||
|
||||
function toggleSection(el) {
|
||||
const grid = el.nextElementSibling;
|
||||
grid.classList.toggle('hidden');
|
||||
}
|
||||
|
||||
document.getElementById('search').addEventListener('input', filterImages);
|
||||
document.getElementById('categoryFilter').addEventListener('change', filterImages);
|
||||
|
||||
function filterImages() {
|
||||
const search = document.getElementById('search').value.toLowerCase();
|
||||
const category = document.getElementById('categoryFilter').value;
|
||||
|
||||
document.querySelectorAll('.card').forEach(card => {
|
||||
const name = card.dataset.name;
|
||||
const path = card.dataset.path.toLowerCase();
|
||||
const cardCategory = card.closest('.section').dataset.category;
|
||||
|
||||
const matchesSearch = !search || name.includes(search) || path.includes(search);
|
||||
const matchesCategory = !category || cardCategory === category;
|
||||
|
||||
card.classList.toggle('hidden', !(matchesSearch && matchesCategory));
|
||||
});
|
||||
|
||||
document.querySelectorAll('.section').forEach(section => {
|
||||
const visible = section.querySelectorAll('.card:not(.hidden)').length;
|
||||
section.classList.toggle('hidden', visible === 0);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape') closeModal();
|
||||
if (e.key === '/' && e.target.tagName !== 'INPUT') {
|
||||
e.preventDefault();
|
||||
document.getElementById('search').focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
# Write HTML file
|
||||
with open('tiled_assets_mini.html', 'w', encoding='utf-8') as f:
|
||||
f.write(html)
|
||||
|
||||
print(f"Generated tiled_assets_mini.html with {len(all_images)} images in {len(categories)} categories")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user