feat(bt)!: Remove legacy tree definition format

The `LegacyTreeDef` type and its normalization path have been removed.
All tree definitions must now use the factory functions (`leaf`,
`sequential`, `parallel`, `selector`, `random`, `repeat`). The
`TreeDef` type is now simply `TaskEntityDef`.
This commit is contained in:
hyper
2026-06-28 10:19:40 +08:00
parent ddde3f7597
commit 1253bf82d2
5 changed files with 133 additions and 180 deletions
+1 -7
View File
@@ -23,10 +23,4 @@ export {
random,
repeat,
} from "./tree-def";
export type {
TreeDef,
LegacyTreeDef,
TaskEntityDef,
LeafTaskMeta,
LeafFn,
} from "./tree-def";
export type { TreeDef, TaskEntityDef, LeafTaskMeta, LeafFn } from "./tree-def";
+7 -40
View File
@@ -22,15 +22,6 @@ export type LeafFn =
| ((world: World, dt: number) => void)
| (() => Generator<number | void, void, number>);
/** Legacy declarative behaviour-tree definition. */
export type LegacyTreeDef =
| { kind: "leaf"; run: LeafFn }
| { kind: "sequential"; children: LegacyTreeDef[] }
| { kind: "parallel"; children: LegacyTreeDef[] }
| { kind: "selector"; children: LegacyTreeDef[] }
| { kind: "random"; children: LegacyTreeDef[] }
| { kind: "repeat"; child: LegacyTreeDef };
export interface LeafTaskMeta {
readonly run: LeafFn;
}
@@ -40,8 +31,8 @@ export type TaskEntityDef = EntityDef<
LeafTaskMeta | undefined
>;
/** Behaviour-tree definitions accepted by `buildTree`. */
export type TreeDef = LegacyTreeDef | TaskEntityDef;
/** Behaviour-tree definition accepted by `buildTree`. */
export type TreeDef = TaskEntityDef;
// ── Entity task factories ──────────────────────────────
@@ -118,37 +109,14 @@ export function repeat(
// ── Builder ───────────────────────────────────────────
function isEntityDef(def: TreeDef | EntityDef): def is EntityDef {
return def.kind === "entity";
}
function normalizeLegacyTree(def: LegacyTreeDef): TaskEntityDef {
switch (def.kind) {
case "leaf":
return leaf(def.run);
case "repeat":
return repeat(normalizeLegacyTree(def.child));
case "sequential":
return sequential(def.children.map(normalizeLegacyTree));
case "parallel":
return parallel(def.children.map(normalizeLegacyTree));
case "selector":
return selector(def.children.map(normalizeLegacyTree));
case "random":
return random(def.children.map(normalizeLegacyTree));
}
}
/**
* Materialize a behaviour-tree definition into ECS entities and return a
* fully-wired `TaskRunner`.
*
* The preferred definition format is an `EntityDef` tree produced by the task
* factories (`leaf`, `sequential`, `parallel`, `selector`, `random`, `repeat`)
* and generic single-component entity factories. Non-task child entities are
* materialized into the ECS tree but ignored by `TaskRunner` execution.
*
* Legacy object definitions are still accepted for compatibility.
* Definitions are `EntityDef` trees produced by the task factories (`leaf`,
* `sequential`, `parallel`, `selector`, `random`, `repeat`) and generic
* single-component entity factories. Non-task child entities are materialized
* into the ECS tree but ignored by `TaskRunner` execution.
*
* Leaf `run` functions:
* - **Plain function** — runs once per tick. `return` = success. `throw` = fail.
@@ -158,7 +126,6 @@ function normalizeLegacyTree(def: LegacyTreeDef): TaskEntityDef {
* Generator completion = success. `throw` = fail. `throw Cancel` = cancel.
*/
export function buildTree(world: World, def: TreeDef): TaskRunner {
const rootDef = isEntityDef(def) ? def : normalizeLegacyTree(def);
const leafHandlers = new Map<Entity, LeafFn>();
// Track generator iterators for multi-frame leaves
const generators = new Map<Entity, Generator<number | void, void, number>>();
@@ -189,7 +156,7 @@ export function buildTree(world: World, def: TreeDef): TaskRunner {
return entity;
}
const root = build(rootDef);
const root = build(def);
if (!world.has(root, Task)) {
throw new Error("buildTree root must be a task entity");