455 lines
15 KiB
Python
455 lines
15 KiB
Python
#!/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()
|