Files
novafarma/scripts/generate_anomalous_fauna.py

259 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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-2.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()