259 lines
11 KiB
Python
259 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
🦖 ANOMALOUS ZONE FAUNA GENERATOR
|
||
Generates creatures for all 8 remaining anomalous zones
|
||
Based on DLC_TO_BASE_GAME_COMPLETE.md specifications
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import time
|
||
from pathlib import Path
|
||
from PIL import Image
|
||
|
||
try:
|
||
import google.generativeai as genai
|
||
except ImportError:
|
||
print("❌ google-generativeai not installed!")
|
||
print(" Run: pip install google-generativeai")
|
||
sys.exit(1)
|
||
|
||
# Configuration
|
||
REPO = Path("/Users/davidkotnik/repos/novafarma")
|
||
ASSETS = REPO / "assets/slike"
|
||
|
||
# Setup API
|
||
api_key = os.environ.get("GEMINI_API_KEY")
|
||
if not api_key:
|
||
print("❌ GEMINI_API_KEY not set!")
|
||
print(" Run: export GEMINI_API_KEY='your-key-here'")
|
||
sys.exit(1)
|
||
|
||
genai.configure(api_key=api_key)
|
||
|
||
# Style prompts
|
||
STYLE_A = """ISOLATED ON PURE WHITE BACKGROUND. 2D indie game creature sprite, cartoon vector art style, bold black outlines,
|
||
flat colors, cute and playful aesthetic, NO shadows, NO gradients, simple clean design, centered, full body visible, game asset"""
|
||
|
||
STYLE_B = """ISOLATED ON PURE WHITE BACKGROUND. 2D indie game creature sprite, dark hand-drawn gritty noir style,
|
||
dramatic shadows, high contrast, sketchy atmospheric lines, mature 90s cartoon aesthetic, centered, full body visible, game asset"""
|
||
|
||
# All anomalous zones with their creatures
|
||
ANOMALOUS_ZONES = {
|
||
"mythical_highlands": [
|
||
("unicorn", "white unicorn with rainbow mane, golden spiral horn, magical glowing"),
|
||
("dragon_fire", "fire dragon, red scales, breathing flames, large wings"),
|
||
("dragon_ice", "ice dragon, blue scales, frost breath, majestic wings"),
|
||
("griffin", "griffin, eagle head, lion body, large wings, fierce"),
|
||
("phoenix", "phoenix bird, fire feathers, glowing orange-red, rebirth flames"),
|
||
("pegasus", "pegasus flying horse, white with large wings, graceful"),
|
||
("yeti", "yeti creature, white fur, friendly peaceful, mountain dweller"),
|
||
],
|
||
|
||
"endless_forest": [
|
||
("bigfoot", "bigfoot sasquatch, massive brown fur, gentle giant, forest guardian"),
|
||
("wendigo", "wendigo horror creature, skeletal antlers, dark menacing, night stalker"),
|
||
("forest_spirit", "forest spirit, ethereal glowing green, nature guardian, mystical"),
|
||
("tree_ent", "tree ent, giant walking tree, ancient wise, moss covered"),
|
||
("will_o_wisp", "will-o-wisp floating light, blue ethereal glow, mysterious"),
|
||
],
|
||
|
||
"loch_ness": [
|
||
("nessie", "loch ness monster, long neck, friendly, water serpent, scottish"),
|
||
("leprechaun", "leprechaun small irish, green suit, red beard, gold pot guardian"),
|
||
("kelpie", "kelpie water horse, mystical, celtic mythology"),
|
||
("selkie", "selkie seal-human, shapeshifter, scottish folklore"),
|
||
],
|
||
|
||
"egyptian_desert": [
|
||
("mummy_pharaoh", "mummy pharaoh boss, golden crown, wrapped bandages, ancient curse"),
|
||
("mummy_soldier", "mummy soldier guard, spear weapon, bandaged warrior"),
|
||
("scarab_giant", "giant scarab beetle, massive, egyptian sacred"),
|
||
("scorpion_giant", "giant scorpion, desert predator, venomous stinger"),
|
||
("sand_serpent", "sand serpent, desert snake, camouflaged sand colored"),
|
||
("sphinx", "sphinx creature, lion body human head, riddle guardian"),
|
||
("anubis_guardian", "anubis guardian, jackal headed, egyptian god, temple protector"),
|
||
],
|
||
|
||
"amazonas": [
|
||
("jaguar", "jaguar big cat, spotted, stealth predator, jungle hunter"),
|
||
("anaconda", "anaconda giant snake, massive coiled, green patterned"),
|
||
("poison_dart_frog", "poison dart frog, vibrant toxic colors, small deadly"),
|
||
("macaw", "macaw parrot, colorful red blue, large tropical bird"),
|
||
("piranha", "piranha fish, sharp teeth, aggressive swarm"),
|
||
("mega_jaguar", "mega jaguar boss, car sized, alpha predator"),
|
||
("carnivorous_plant", "carnivorous plant, venus flytrap giant, mobile threat"),
|
||
],
|
||
|
||
"atlantis": [
|
||
("mermaid", "mermaid, beautiful fish tail, underwater dweller, aquatic"),
|
||
("sea_serpent", "sea serpent, long coiled, underwater dragon, oceanic"),
|
||
("kraken", "kraken giant octopus, massive tentacles, deep sea monster"),
|
||
("seahorse_giant", "giant seahorse, rideable, graceful underwater"),
|
||
("dolphin", "dolphin, friendly intelligent, ocean companion"),
|
||
("anglerfish_giant", "giant anglerfish, glowing lure, deep sea predator"),
|
||
],
|
||
|
||
"chernobyl": [
|
||
("radioactive_zombie", "radioactive zombie, glowing green, hazmat torn, mutated"),
|
||
("mutant_dog", "mutant dog, two heads, radiation scarred, aggressive"),
|
||
("nuclear_rat_giant", "giant nuclear rat, glowing mutations, toxic"),
|
||
("radiation_stalker", "radiation stalker, invisible shimmer, hazmat creature"),
|
||
("alpha_troll_king", "alpha troll king BOSS, massive green, tribal crown, 50000 HP"),
|
||
],
|
||
|
||
"catacombs": [
|
||
("skeleton_warrior", "skeleton warrior, armor bones, sword shield, undead fighter"),
|
||
("skeleton_knight", "skeleton knight, heavy armor, mounted, elite undead"),
|
||
("ghost", "ghost spirit, ethereal transparent, floating, supernatural"),
|
||
("bone_golem", "bone golem, massive skeleton construct, regenerating"),
|
||
("necromancer", "necromancer dark mage, skull staff, raising dead, boss"),
|
||
("revenant", "revenant cursed soul, dark armor, vengeance spirit"),
|
||
("shadow_stalker", "shadow stalker, invisible in dark, sneaky wraith"),
|
||
],
|
||
}
|
||
|
||
def create_preview(image_path: Path, size=256):
|
||
"""Create preview version"""
|
||
try:
|
||
img = Image.open(image_path)
|
||
preview = img.resize((size, size), Image.Resampling.LANCZOS)
|
||
preview_path = image_path.parent / f"{image_path.stem}_preview_{size}x{size}.png"
|
||
preview.save(preview_path, 'PNG', optimize=True)
|
||
return preview_path
|
||
except Exception as e:
|
||
print(f" ⚠️ Preview failed: {e}")
|
||
return None
|
||
|
||
def generate_creature(zone_name: str, creature_name: str, description: str, style: str, log_file) -> bool:
|
||
"""Generate a single creature sprite"""
|
||
|
||
start = time.time()
|
||
style_suffix = "stylea" if style == "A" else "styleb"
|
||
output_path = ASSETS / zone_name / f"{creature_name}_{style_suffix}.png"
|
||
|
||
# Skip if exists
|
||
if output_path.exists():
|
||
print(f" ⏭️ Already exists")
|
||
return True
|
||
|
||
# Create prompt
|
||
style_prompt = STYLE_A if style == "A" else STYLE_B
|
||
full_prompt = f"{style_prompt}\n\n{description}\n\nGame sprite asset."
|
||
|
||
try:
|
||
print(f" 🎨 Generating {style_suffix}: {creature_name}")
|
||
log_file.write(f"{time.strftime('%H:%M:%S')} - Generating {output_path.name}\n")
|
||
log_file.flush()
|
||
|
||
# Generate image
|
||
model = genai.GenerativeModel("gemini-1.5-flash")
|
||
response = model.generate_content([full_prompt])
|
||
|
||
if hasattr(response, '_result') and response._result.candidates:
|
||
image_data = response._result.candidates[0].content.parts[0].inline_data.data
|
||
|
||
# Save original
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
with open(output_path, 'wb') as f:
|
||
f.write(image_data)
|
||
|
||
# Create preview
|
||
preview = create_preview(output_path)
|
||
|
||
elapsed = time.time() - start
|
||
print(f" ✅ Saved ({elapsed:.1f}s)")
|
||
log_file.write(f"{time.strftime('%H:%M:%S')} - SUCCESS {output_path.name} ({elapsed:.1f}s)\n")
|
||
log_file.flush()
|
||
|
||
return True
|
||
else:
|
||
print(f" ❌ No image data")
|
||
log_file.write(f"{time.strftime('%H:%M:%S')} - FAILED {output_path.name} - No data\n")
|
||
log_file.flush()
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f" ❌ Error: {e}")
|
||
log_file.write(f"{time.strftime('%H:%M:%S')} - ERROR {output_path.name} - {e}\n")
|
||
log_file.flush()
|
||
return False
|
||
|
||
def main():
|
||
# Create log
|
||
log_dir = REPO / "logs"
|
||
log_dir.mkdir(exist_ok=True)
|
||
log_file = open(log_dir / f"anomalous_fauna_{time.strftime('%Y%m%d_%H%M%S')}.log", 'w')
|
||
|
||
print("="*70)
|
||
print("🦖 ANOMALOUS ZONE FAUNA GENERATOR")
|
||
print("="*70)
|
||
|
||
total_creatures = sum(len(creatures) for creatures in ANOMALOUS_ZONES.values())
|
||
total_images = total_creatures * 2 # styleA + styleB
|
||
|
||
print(f"\n{len(ANOMALOUS_ZONES)} zones | {total_creatures} creatures × 2 styles = {total_images} images")
|
||
print()
|
||
|
||
log_file.write(f"ANOMALOUS FAUNA GENERATION - {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||
log_file.write(f"Total: {total_images} images\n")
|
||
log_file.write("="*70 + "\n\n")
|
||
log_file.flush()
|
||
|
||
success = 0
|
||
failed = 0
|
||
current = 0
|
||
|
||
try:
|
||
for zone_name, creatures in ANOMALOUS_ZONES.items():
|
||
print(f"\n{'='*70}")
|
||
print(f"🌍 {zone_name.upper().replace('_', ' ')}")
|
||
print(f"{'='*70}")
|
||
print(f" {len(creatures)} creatures")
|
||
|
||
log_file.write(f"\n{'='*70}\n")
|
||
log_file.write(f"ZONE: {zone_name}\n")
|
||
log_file.write(f"{'='*70}\n\n")
|
||
log_file.flush()
|
||
|
||
for creature_name, description in creatures:
|
||
current += 1
|
||
print(f"\n[{current}/{total_creatures}] {creature_name}")
|
||
|
||
# Generate both styles
|
||
for style in ["A", "B"]:
|
||
if generate_creature(zone_name, creature_name, description, style, log_file):
|
||
success += 1
|
||
else:
|
||
failed += 1
|
||
|
||
# Rate limiting
|
||
time.sleep(2)
|
||
|
||
# Progress
|
||
progress = ((current / total_creatures) * 100)
|
||
print(f"\n📊 Progress: {progress:.1f}% | Success: {success}/{total_images} | Failed: {failed}")
|
||
|
||
# Final summary
|
||
print("\n" + "="*70)
|
||
print("🎉 GENERATION COMPLETE!")
|
||
print("="*70)
|
||
print(f"✅ Success: {success}/{total_images}")
|
||
print(f"❌ Failed: {failed}/{total_images}")
|
||
print(f"📊 Success rate: {(success/total_images)*100:.1f}%")
|
||
|
||
log_file.write(f"\n{'='*70}\n")
|
||
log_file.write(f"GENERATION COMPLETE\n")
|
||
log_file.write(f"Success: {success}\n")
|
||
log_file.write(f"Failed: {failed}\n")
|
||
|
||
except KeyboardInterrupt:
|
||
print(f"\n\n⚠️ INTERRUPTED at creature {current}")
|
||
log_file.write(f"\n\nINTERRUPTED at creature {current}\n")
|
||
|
||
finally:
|
||
log_file.close()
|
||
|
||
if __name__ == "__main__":
|
||
main()
|