Files
novafarma/scripts/batch_asset_generation.py
David Kotnik 4f0a430c42 🎨 Add 42 new demo assets - Kai animations, zombies, buildings, terrain, environment
Generated assets (dual-style):
- Kai run animations (East/West, 16 frames)
- Kai portrait (neutral, 2 styles)
- Zombie walk/attack cycles (6 frames)
- Buildings: shack, campfire, well, chest (8 assets)
- Terrain: stone path, grass variation (4 tiles)
- Environment: oak tree, rock, bush, storage (10 objects)

Total: 42 new PNG files (21 base × 2 styles)
+ Batch generation scripts and manifests
+ Demo readiness checklist
2025-12-31 00:53:19 +01:00

218 lines
10 KiB
Python
Raw Permalink 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
"""
Batch Asset Generation for NovaFarma Demo
Generates all missing assets with dual-style system
"""
import json
from pathlib import Path
# Asset definitions with proper organization
ASSET_QUEUE = {
"kai_animations": {
"category": "demo/characters",
"assets": [
# Run East (4 frames)
{"name": "kai_run_east_1", "prompt": "Kai running EAST, frame 1/4, left leg forward, right arm back"},
{"name": "kai_run_east_2", "prompt": "Kai running EAST, frame 2/4, mid-stride, both feet off ground"},
{"name": "kai_run_east_3", "prompt": "Kai running EAST, frame 3/4, right leg forward, left arm back"},
{"name": "kai_run_east_4", "prompt": "Kai running EAST, frame 4/4, landing pose"},
# Run West (4 frames)
{"name": "kai_run_west_1", "prompt": "Kai running WEST, frame 1/4, left leg forward"},
{"name": "kai_run_west_2", "prompt": "Kai running WEST, frame 2/4, mid-stride"},
{"name": "kai_run_west_3", "prompt": "Kai running WEST, frame 3/4, right leg forward"},
{"name": "kai_run_west_4", "prompt": "Kai running WEST, frame 4/4, landing"},
# Weapon actions
{"name": "kai_sword_swing_east", "prompt": "Kai swinging sword to the east, mid-swing action"},
{"name": "kai_sword_swing_west", "prompt": "Kai swinging sword to the west, mid-swing action"},
{"name": "kai_axe_chop", "prompt": "Kai chopping with axe overhead"},
# Portraits
{"name": "kai_portrait_neutral", "prompt": "Kai portrait, neutral serious expression, close-up face"},
{"name": "kai_portrait_happy", "prompt": "Kai portrait, slight smile, determined happy"},
{"name": "kai_portrait_sad", "prompt": "Kai portrait, sad expression, concerned"},
]
},
"zombies": {
"category": "demo/characters",
"assets": [
# Zombie walk
{"name": "zombie_walk_1", "prompt": "Zombie worker shambling, walk frame 1/4, left leg forward"},
{"name": "zombie_walk_2", "prompt": "Zombie worker shambling, walk frame 2/4, dragging feet"},
{"name": "zombie_walk_3", "prompt": "Zombie worker shambling, walk frame 3/4, right leg forward"},
{"name": "zombie_walk_4", "prompt": "Zombie worker shambling, walk frame 4/4, stumbling"},
# Zombie attack
{"name": "zombie_attack_1", "prompt": "Zombie worker attacking, frame 1/4, arms reaching forward"},
{"name": "zombie_attack_2", "prompt": "Zombie worker attacking, frame 2/4, lunging motion"},
{"name": "zombie_attack_3", "prompt": "Zombie worker attacking, frame 3/4, biting motion"},
{"name": "zombie_attack_4", "prompt": "Zombie worker attacking, frame 4/4, return stance"},
# Variants
{"name": "zombie_runner", "prompt": "Fast zombie runner, athletic pose, sprinting stance"},
{"name": "zombie_bloated", "prompt": "Bloated zombie, swollen belly, slow heavy build"},
{"name": "zombie_corpse", "prompt": "Zombie corpse on ground, defeated, lying down"},
]
},
"terrain": {
"category": "demo/terrain",
"assets": [
# Grass variations
{"name": "grass_tile_2", "prompt": "Grass tile variation 2, slightly different grass pattern"},
{"name": "grass_tile_3", "prompt": "Grass tile variation 3, with small flowers"},
{"name": "grass_tile_4", "prompt": "Grass tile variation 4, with rocks"},
# Stone path
{"name": "stone_path_straight", "prompt": "Stone path tile, straight section, cobblestones"},
{"name": "stone_path_corner", "prompt": "Stone path corner tile, 90 degree turn"},
{"name": "stone_path_cross", "prompt": "Stone path crossroads, 4-way intersection"},
{"name": "stone_path_end", "prompt": "Stone path end cap, rounded edge"},
# Corners
{"name": "grass_corner_ne", "prompt": "Grass to dirt corner, northeast transition"},
{"name": "grass_corner_nw", "prompt": "Grass to dirt corner, northwest transition"},
{"name": "grass_corner_se", "prompt": "Grass to dirt corner, southeast transition"},
{"name": "grass_corner_sw", "prompt": "Grass to dirt corner, southwest transition"},
]
},
"environment": {
"category": "demo/environment",
"assets": [
# Trees
{"name": "oak_tree", "prompt": "Large oak tree, full canopy, healthy green leaves"},
{"name": "oak_tree_stump", "prompt": "Cut oak tree stump, chopped down, rings visible"},
{"name": "pine_tree", "prompt": "Pine tree, conical shape, dark green needles"},
{"name": "dead_tree_2", "prompt": "Dead tree variant 2, bare twisted branches"},
# Rocks
{"name": "rock_small", "prompt": "Small rock, collectible stone, grey granite"},
{"name": "rock_medium", "prompt": "Medium rock, obstacle size, moss covered"},
{"name": "rock_large_2", "prompt": "Large boulder, imposing size, cracked surface"},
# Plants
{"name": "bush_green_2", "prompt": "Green bush, leafy shrub, decorative foliage"},
{"name": "bush_berries", "prompt": "Berry bush, red berries visible, harvestable"},
{"name": "flower_yellow", "prompt": "Yellow flowers, small cluster, wildflowers"},
{"name": "flower_purple", "prompt": "Purple flowers, delicate petals, garden flowers"},
{"name": "grass_tall", "prompt": "Tall grass patch, wild overgrown grass swaying"},
{"name": "weeds", "prompt": "Weed patch, unwanted plants, brown dried weeds"},
]
},
"buildings": {
"category": "demo/buildings",
"assets": [
{"name": "shack", "prompt": "Wooden shack, upgraded tent, small cabin with door"},
{"name": "campfire_lit", "prompt": "Campfire burning, orange flames, cooking fire active"},
{"name": "water_well", "prompt": "Stone water well, bucket and rope, medieval well"},
{"name": "storage_chest_large", "prompt": "Large storage chest, wooden trunk, iron reinforced"},
{"name": "scarecrow", "prompt": "Farm scarecrow, straw figure, tattered clothes, pole"},
{"name": "compost_bin", "prompt": "Wooden compost bin, organic waste, farm structure"},
]
},
"npcs": {
"category": "demo/npcs",
"assets": [
# Trader
{"name": "npc_trader_idle", "prompt": "Merchant trader NPC, standing idle, holding goods bag"},
{"name": "npc_trader_portrait", "prompt": "Trader portrait, friendly smile, merchant hat"},
# Blacksmith
{"name": "npc_blacksmith_idle", "prompt": "Blacksmith NPC, muscular build, leather apron, hammer"},
{"name": "npc_blacksmith_portrait", "prompt": "Blacksmith portrait, soot on face, serious expression"},
# Healer
{"name": "npc_healer_idle", "prompt": "Healer NPC, robed figure, staff with crystal"},
{"name": "npc_healer_portrait", "prompt": "Healer portrait, kind eyes, hood covering hair"},
# Traveler
{"name": "npc_traveler_idle", "prompt": "Mysterious traveler NPC, cloaked, walking staff"},
{"name": "npc_traveler_portrait", "prompt": "Traveler portrait, hood obscuring face, mysterious"},
]
}
}
# Style templates
STYLE_A_SUFFIX = "Style A: Bold cartoon vector art - thick black outlines, flat vibrant colors, clean cel-shaded style, cheerful indie game aesthetic, white background."
STYLE_B_SUFFIX = "Style B: Dark gritty noir art - bold black ink outlines, high-contrast desaturated tones, heavy shadows, sketchy crosshatch textures, moody post-apocalyptic atmosphere, black background."
# Character base descriptions
KAI_BASE = """Kai teenage survivor character, dark forest green thick dreadlocks (#2D5016),
medium skin tone, large ear gauges, nose piercing and lip piercing,
serious determined expression,
wearing weathered blue-grey denim jacket with dirt stains,
beige t-shirt underneath,
torn blue jeans ripped at knees,
brown leather combat boots,
brown survival backpack with straps and pockets,
athletic lean build."""
ZOMBIE_BASE = """Zombie worker character, decaying grey-green skin, tattered brown work clothes,
shambling posture, blank white eyes, exposed bones visible,
post-apocalyptic undead laborer."""
def generate_manifest():
"""Generate complete manifest for batch generation"""
manifest = {
"total_assets": 0,
"batches": []
}
for batch_name, batch_data in ASSET_QUEUE.items():
batch = {
"name": batch_name,
"category": batch_data["category"],
"assets": []
}
for asset in batch_data["assets"]:
# Determine character base
char_base = ""
if "kai" in asset["name"]:
char_base = KAI_BASE
elif "zombie" in asset["name"]:
char_base = ZOMBIE_BASE
# Create full prompts for both styles
base_prompt = f"{char_base}\n{asset['prompt']}" if char_base else asset["prompt"]
asset_entry = {
"name": asset["name"],
"styleA_prompt": f"{base_prompt}\n{STYLE_A_SUFFIX}",
"styleB_prompt": f"{base_prompt}\n{STYLE_B_SUFFIX}",
"target_dir": f"assets/images/{batch_data['category']}"
}
batch["assets"].append(asset_entry)
manifest["total_assets"] += 2 # Both styles
manifest["batches"].append(batch)
return manifest
def main():
manifest = generate_manifest()
# Save manifest
output_file = Path("BATCH_GENERATION_MANIFEST.json")
with open(output_file, 'w') as f:
json.dump(manifest, f, indent=2)
print(f"✅ Generated manifest: {output_file}")
print(f"📊 Total assets to generate: {manifest['total_assets']}")
print(f"📦 Total batches: {len(manifest['batches'])}")
for batch in manifest["batches"]:
print(f"{batch['name']}: {len(batch['assets'])} base assets (×2 styles = {len(batch['assets'])*2})")
if __name__ == "__main__":
main()