feat(bt): add repeat and selector task kinds

This commit is contained in:
2026-06-01 22:57:14 +08:00
parent 4e37e03d3f
commit 3620e80807
3 changed files with 299 additions and 2 deletions
+56
View File
@@ -150,6 +150,12 @@ export class TaskRunner {
case "random":
this._executeRandom(entity);
break;
case "repeat":
this._executeRepeat(entity);
break;
case "selector":
this._executeSelector(entity);
break;
}
}
@@ -244,6 +250,56 @@ export class TaskRunner {
// If no eligible children (all running), wait for one to finish
}
private _executeRepeat(entity: Entity): void {
const children = childrenOf(this._world, entity);
// Repeat expects exactly one child
if (children.length === 0) return;
const child = children[0];
// If child reached a terminal, reset it and schedule again
if (isTerminal(this._world, child)) {
clearStatus(this._world, child);
this._world.add(child, Scheduled);
return;
}
// Schedule child if not already running or scheduled
if (
!this._world.has(child, Running) &&
!this._world.has(child, Scheduled)
) {
this._world.add(child, Scheduled);
}
// Repeat itself never terminates — it just keeps the child going
}
private _executeSelector(entity: Entity): void {
const children = childrenOf(this._world, entity);
// Find the first non-terminal child
for (const child of children) {
if (isTerminal(this._world, child)) {
const status = terminalStatus(this._world, child)!;
if (status === "succeeded") {
// First success — selector succeeds
this._finish(entity, Succeeded);
return;
}
// failed or cancelled — continue to next child
continue;
}
// Found a child that hasn't run yet — schedule it
this._world.add(child, Scheduled);
return;
}
// All children failed
this._finish(entity, Failed);
}
// ── Completion propagation ────────────────────────
private _finish(
+12 -2
View File
@@ -13,12 +13,22 @@ import { defineRelationship } from "../relationship";
* Succeeds when all children succeed; fails when any child fails.
* - `"random"` — picks one child at random each time it's scheduled.
* Succeeds/fails with that child's result.
* - `"repeat"` — runs its single child. When the child finishes, resets it
* and runs again. Never terminates on its own (only via cancel).
* - `"selector"` — runs children left to right. Succeeds on the first
* child that succeeds; fails only if all children fail.
*/
export const Task = defineComponent("task", {
kind: "leaf" as "leaf" | "sequential" | "parallel" | "random",
kind: "leaf" as
| "leaf"
| "sequential"
| "parallel"
| "random"
| "repeat"
| "selector",
});
export type TaskKind = typeof Task.type["kind"];
export type TaskKind = (typeof Task.type)["kind"];
// ── Status tags (zero-size — presence is the signal) ──
/** A task that should be executed this tick. */