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
+41 -48
View File
@@ -20,7 +20,13 @@
// npx tsx examples/tetris/main.ts
import { World } from "../../src/index";
import { buildTree } from "../../src/bt/index";
import {
buildTree,
leaf,
parallel,
repeat,
sequential,
} from "../../src/bt/index";
import { CommandQueue } from "../../src/commands/index";
import {
@@ -64,56 +70,43 @@ registerCommands(world, commands, pieces);
pieces.spawnPiece();
// ── Behaviour Tree ───────────────────────────────────
const runner = buildTree(world, {
kind: "parallel",
children: [
{
kind: "leaf",
*run() {
while (true) {
const dt: number = yield;
if (world.hasSingleton(GameOver) || world.hasSingleton(Paused)) {
continue;
}
const timer = world.getSingleton(TickTimer);
timer.accumulator += dt;
if (timer.accumulator >= timer.interval) {
timer.accumulator -= timer.interval;
if (world.hasSingleton(Piece)) {
const piece = world.getSingleton(Piece);
const board = world.getSingleton(Board);
if (!collides(board.grid, piece.shape, piece.x, piece.y + 1)) {
piece.y++;
} else {
pieces.lockAndSpawn();
}
const runner = buildTree(
world,
parallel([
leaf(function* gravityTick() {
while (true) {
const dt: number = yield;
if (world.hasSingleton(GameOver) || world.hasSingleton(Paused)) {
continue;
}
const timer = world.getSingleton(TickTimer);
timer.accumulator += dt;
if (timer.accumulator >= timer.interval) {
timer.accumulator -= timer.interval;
if (world.hasSingleton(Piece)) {
const piece = world.getSingleton(Piece);
const board = world.getSingleton(Board);
if (!collides(board.grid, piece.shape, piece.x, piece.y + 1)) {
piece.y++;
} else {
pieces.lockAndSpawn();
}
}
}
},
},
{
kind: "repeat",
child: {
kind: "sequential",
children: [
{
kind: "leaf",
run: () => {
commands.execute();
},
},
{
kind: "leaf",
run: () => {
render(world, ui);
},
},
],
},
},
],
});
}
}),
repeat(
sequential([
leaf(() => {
commands.execute();
}),
leaf(() => {
render(world, ui);
}),
]),
),
]),
);
// ── Input → Command mapping ──────────────────────────
const keyToCommand: Partial<Record<Key, typeof MoveLeft>> = {