"""Generate battlefield concept boards with the project image generation flow. This script delegates image creation to ``Tools/gpt_image_generate.py`` with the project-standard ``gpt-image-2.5`` model. It deliberately does not draw placeholder diagrams with PIL. Use ``--dry-run`` to inspect requests without calling the remote service. """ from __future__ import annotations import argparse import json import subprocess import sys import time from pathlib import Path ROOT = Path(__file__).resolve().parents[1] OUT = ROOT / "Doc/Design/Scene/Battlefield" PROMPT_FILE = OUT / "battlefield_design_prompts.json" IMAGE_TOOL = ROOT / "Tools/gpt_image_generate.py" def load_prompts() -> dict: return json.loads(PROMPT_FILE.read_text(encoding="utf-8-sig")) def mark_generated(design_id: str, output: Path, status_file: Path) -> None: manifest_path = OUT / "battlefield_scene_manifest.json" if not manifest_path.is_file(): return manifest = json.loads(manifest_path.read_text(encoding="utf-8-sig")) for option in manifest.get("designOptions", []): if option.get("id") == design_id: option.setdefault("generation", {}).update( { "tool": "Tools/gpt_image_generate.py", "model": "gpt-image-2.5", "status": "generated", "output": str(output.relative_to(ROOT)).replace("\\", "/"), "statusFile": str(status_file.relative_to(ROOT)).replace("\\", "/"), "generatedAtUtc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), } ) if manifest.get("requestedDesign") == design_id: manifest["selectedDesign"] = design_id manifest.setdefault("designGeneration", {}).update( { "tool": "Tools/generate_battlefield_designs.py", "imageTool": "Tools/gpt_image_generate.py", "model": "gpt-image-2.5", "status": "generated", "generatedAtUtc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "note": "已生成并覆盖总览图。", } ) manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") def main() -> int: parser = argparse.ArgumentParser( description="Use gpt-image-2.5 to generate the three battlefield design boards." ) parser.add_argument("--design", choices=["A", "B", "C", "all"], default="all") parser.add_argument("--dry-run", action="store_true", help="print requests without calling the image API") parser.add_argument("--quality", default="auto", choices=["auto", "low", "medium", "high"]) args = parser.parse_args() data = load_prompts() selected = data["designs"] if args.design == "all" else [next(x for x in data["designs"] if x["id"] == args.design)] OUT.mkdir(parents=True, exist_ok=True) for item in selected: output = OUT / item["file"] callback = OUT / f"{output.stem}_gpt_image_status.json" command = [ sys.executable, str(IMAGE_TOOL), "--prompt", item["prompt"], "--model", "gpt-image-2.5", "--aspect-ratio", "1024x1024", "--quality", args.quality, "--output", str(output), "--callback-file", str(callback), "--result-only", ] print(f"[{item['id']}] {item['name']}") print(" ".join(json.dumps(part, ensure_ascii=False) for part in command)) if args.dry_run: continue try: subprocess.run(command, cwd=ROOT, check=True) except subprocess.CalledProcessError as exc: print( "image generation failed; configure GRSAI_API_KEY or " "GPT_IMAGE_API_KEY and rerun this command", file=sys.stderr, ) return exc.returncode or 1 mark_generated(item["id"], output, callback) print(f"saved: {output}") return 0 if __name__ == "__main__": raise SystemExit(main())