feat(bt): Add entity-based task factories
Add `leaf`, `sequential`, `parallel`, `selector`, `random`, and `repeat` functions that return `EntityDef` trees. Legacy object definitions are still accepted for backward compatibility. Non-task child entities are ignored by the runner, and `buildTree` now stores the root entity on `TaskRunner.root`.
This commit is contained in:
+59
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { World, type Entity } from "../src/index";
|
||||
import { World, defineComponent, entity, type Entity } from "../src/index";
|
||||
import {
|
||||
Task,
|
||||
Scheduled,
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
Cancelled,
|
||||
ChildOf,
|
||||
TaskRunner,
|
||||
buildTree,
|
||||
leaf,
|
||||
repeat,
|
||||
sequential,
|
||||
} from "../src/bt/index";
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────
|
||||
@@ -54,6 +58,60 @@ function makeSelector(world: World, parent?: Entity): Entity {
|
||||
return e;
|
||||
}
|
||||
|
||||
// ── Entity task factories ───────────────────────────
|
||||
describe("Entity task factories", () => {
|
||||
it("materializes non-task child entities and ignores them during execution", () => {
|
||||
const Label = defineComponent("testLabel", { value: "" });
|
||||
const world = new World();
|
||||
const calls: string[] = [];
|
||||
|
||||
const runner = buildTree(
|
||||
world,
|
||||
sequential([
|
||||
entity(Label, { value: "sequence metadata" }),
|
||||
leaf(() => calls.push("a")),
|
||||
entity(Label, { value: "between leaves" }),
|
||||
leaf(() => calls.push("b")),
|
||||
]),
|
||||
);
|
||||
|
||||
const root = runner.root!;
|
||||
const children = [...world.getRelatedTo(root, ChildOf)];
|
||||
const labels = children
|
||||
.filter((child) => world.has(child, Label))
|
||||
.map((child) => world.get(child, Label).value);
|
||||
|
||||
expect(labels).toEqual(["sequence metadata", "between leaves"]);
|
||||
|
||||
runner.schedule(root);
|
||||
for (let i = 0; i < 5; i++) runner.tick();
|
||||
|
||||
expect(calls).toEqual(["a", "b"]);
|
||||
expect(world.has(root, Succeeded)).toBe(true);
|
||||
});
|
||||
|
||||
it("repeat works with component child entities beside its task child", () => {
|
||||
const Label = defineComponent("repeatLabel", { value: "" });
|
||||
const world = new World();
|
||||
let calls = 0;
|
||||
|
||||
const runner = buildTree(
|
||||
world,
|
||||
repeat([
|
||||
entity(Label, { value: "repeat metadata" }),
|
||||
leaf(() => {
|
||||
calls++;
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
runner.schedule(runner.root!);
|
||||
for (let i = 0; i < 5; i++) runner.tick();
|
||||
|
||||
expect(calls).toBeGreaterThan(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Leaf tasks ──────────────────────────────────────
|
||||
describe("Leaf tasks", () => {
|
||||
let world: World;
|
||||
|
||||
Reference in New Issue
Block a user