Files
AIGame/Tools/enemy_static_pipeline.py
T
udxandClaude Opus 5.5 89e0fa246f Add formal enemies, remaining formal partners and synthesized UI SFX
- UI SFX v3: procedurally synthesized one-shots (Tools/synth_ui_sfx.py) wired to
  buttons, rewards, errors and red dots via UiSfx, gated by the SFX setting.
- Five formal enemies (Hornlet, Dashlet, Shellhorn, HornCaptain, GreatHorn):
  Hunyuan 50k -> 2000-tri XPbr, DGXA TokenRig, Idle/Walk/Attack, nested per type
  in PrototypeArena; PrototypeBattle.Enemy gains presentation-only Moving/Strikes.
- Formal partners for all ten pets in the arena and portraits, including new
  VineSprite, ThunderLeopard and TideWhale.
- Pipeline fixes: reject local TokenRig fallback, drop loose mesh islands,
  configurable Unity run_script timeout, companion static stage.
- Also includes concurrent work from another tool in this working tree
  (FlameLion, enemy combat FX, challenge mode, home explorer, server updates).
- Resources registered in Doc/ResDesc_*.md; verified on isolated :18081.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:23:55 +08:00

61 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""P0 enemy (or companion, --kind Animal) static stage: Hunyuan high GLB -> 2000-tri XPbr bake -> Bundles/Characters/Enemy/{Name} -> static Prefab -> static audit.
Usage:
python Tools/enemy_static_pipeline.py Dashlet 0.5 # bake + install + SetupCharacter + AuditCharacterVisual
python Tools/enemy_static_pipeline.py Dashlet 0.5 --source Tools/output/enemy_3d/Dashlet/dashlet_forward.glb
The high model must already face -Y in Blender (check with renders first; rotate with model_tools/bl_orient_character.py).
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PROJECT = ROOT / "AIClient"
def run(cmd: list[str], cwd: Path = ROOT) -> str:
print("$", " ".join(cmd), flush=True)
done = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, encoding="utf-8", errors="replace")
if done.returncode != 0:
raise SystemExit(f"failed ({done.returncode}): {' '.join(cmd)}\n{done.stdout[-2000:]}\n{done.stderr[-2000:]}")
return done.stdout
def unity(file: str, entry: str, args: list) -> str:
out = run(["unity", "command", "run_script", "--project-path", str(PROJECT), "--file", file, "--entry", entry,
"--args", json.dumps(args), "--timeout_ms", "90000", "--timeout", "120"], PROJECT)
if '"success":true' not in out.replace(" ", ""):
raise SystemExit(f"Unity {entry} failed:\n{out[-2000:]}")
return out
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("name")
ap.add_argument("height", type=float)
ap.add_argument("--source", type=Path)
ap.add_argument("--kind", default="Enemy", choices=("Enemy", "Animal"), help="Animal installs a companion under Characters/Animal")
a = ap.parse_args()
low = a.name.lower()
tag = "enemy" if a.kind == "Enemy" else "companion"
source = a.source or Path(f"Tools/output/{tag}_3d/{a.name}/{low}.glb")
game = Path(f"Tools/output/{tag}_game/{low}_game")
stem = source.stem
run([sys.executable, "Tools/model_tools/model_lowpoly_bake.py", str(source), "-o", str(game), "--tris", "2000", "--size", "1024",
"--callback-file", str(game / "bake_status.json")])
run([sys.executable, "Tools/install_character_bundles.py", "-a", a.name, "--kind", a.kind, "-b", str(game / f"{stem}_xpbr")])
unity("AgentScripts/SetupCharacter.cs", "SetupCharacter.Run", [a.kind, a.name, a.height])
audit = ROOT / (f"Tools/output/enemy_characters/{a.name}/static_audit" if a.kind == "Enemy" else f"Tools/output/companions/{a.name}/static_audit")
unity("AgentScripts/AuditCharacterVisual.cs", "AuditCharacterVisual.Capture",
[f"Assets/Bundles/Characters/{a.kind}/{a.name}/{a.name}.prefab", str(audit).replace("\\", "/"), a.height])
print((audit / "static_verification.txt").read_text(encoding="utf-8"))
if __name__ == "__main__":
main()