#!/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()