feat(engine): loop idle runner with enabledLoopActionIds
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user