feat(engine): loop idle runner with enabledLoopActionIds

This commit is contained in:
ginnoir
2026-06-11 20:44:20 -05:00
parent 8d9484171f
commit 0de61d3e41
6 changed files with 101 additions and 0 deletions
+23
View File
@@ -26,6 +26,8 @@ export interface GameState {
currentStoryNodeId: string;
/** Story node ids the player has already seen. */
seenStoryNodeIds: string[];
/** loop-kind action id -> whether the player has enabled it for idle running. */
enabledLoopActionIds: Record<string, boolean>;
}
export function createGameState(content: Content): GameState {
@@ -41,6 +43,7 @@ export function createGameState(content: Content): GameState {
storyFlags: {},
currentStoryNodeId: '',
seenStoryNodeIds: [],
enabledLoopActionIds: {},
};
}
@@ -197,6 +200,26 @@ export function executeStoryAction(
applyChoice(state, content, action.storyChoiceId);
}
/**
* Start the highest-priority enabled, available loop action when the game is idle.
* Invoked by the runtime AFTER each live tick — never from `tickGame`, so loop
* actions do not run during offline catch-up (which replays `tickGame` directly).
*/
export function maybeStartLoopAction(state: GameState, content: Content): void {
if (state.activeActionId !== null || state.actionQueue.length > 0) return;
const candidates = content.actions
.filter((a) => a.kind === 'loop' && state.enabledLoopActionIds[a.id])
.sort((a, b) => (a.loopPriority ?? 0) - (b.loopPriority ?? 0));
for (const action of candidates) {
if (isActionAvailable(state, content, action.id)) {
beginAction(state, content, action.id);
return;
}
}
}
/**
* Advance the active action by `tickMs`. On completion, grants yields and
* advances the queue — actions do not auto-repeat when the queue is empty.