Files
botw/tools/ai_common.py
T
Léo Lam f4cd38c43b Actor: move all AI code under Game/Actor
Link order shows one sorted actor tree, not a separate AI project: the
Action/AI/Query folders and per-feature folders (Camera, Player, Horse,
...) come out of the same archive rounds as the actor classes.

(AI assisted using Claude Fable 5.1 and Claude Opus 5.5)
2026-09-14 00:02:22 +02:00

75 lines
2.3 KiB
Python

from typing import Dict, List
import yaml
from common.util import utils
from common.util.graph import Graph
BaseClasses = {
0x71024d8d68,
0x71025129f0,
0x7102513278,
0x71024d8ef0,
0x710243c9b8,
}
def check_vtable_name_dict(names: Dict[int, str]):
seen = set()
for k, v in names.items():
if v in seen:
raise ValueError(f"invalid vtable names: {v} appears twice")
seen.add(k)
seen.add(v)
def get_vtables() -> Dict[str, Dict[str, List[int]]]:
with (utils.get_repo_root() / "data" / "aidef_vtables.yml").open(encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.CSafeLoader)
def get_action_params() -> Dict[str, List[dict]]:
with (utils.get_repo_root() / "data" / "aidef_action_params.yml").open(encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.CSafeLoader)
def get_action_vtable_names() -> Dict[int, str]:
with (utils.get_repo_root() / "data" / "aidef_action_vtables.yml").open(encoding="utf-8") as f:
names = yaml.load(f, Loader=yaml.CSafeLoader)
check_vtable_name_dict(names)
return names
def get_ai_params() -> Dict[str, List[dict]]:
with (utils.get_repo_root() / "data" / "aidef_ai_params.yml").open(encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.CSafeLoader)
def get_ai_vtable_names() -> Dict[int, str]:
with (utils.get_repo_root() / "data" / "aidef_ai_vtables.yml").open(encoding="utf-8") as f:
names = yaml.load(f, Loader=yaml.CSafeLoader)
check_vtable_name_dict(names)
return names
def topologically_sort_vtables(all_vtables: dict, type_: str) -> List[int]:
graph = Graph()
for name, vtables in all_vtables[type_].items():
classes = list(dict.fromkeys(reversed(vtables)))
for i in range(len(classes) - 1):
graph.add_edge(classes[i + 1], classes[i])
return graph.topological_sort()
def find_header(class_dir, name: str) -> str:
"""Include path for an AI class header, wherever it lives under src/ (feature folders included)."""
src = class_dir
while src.name != "src":
src = src.parent
default = (class_dir / name).relative_to(src).as_posix()
if (class_dir / name).exists():
return default
hits = sorted(src.rglob(name))
return hits[0].relative_to(src).as_posix() if hits else default