feat(content): stub story graph with A/B branch and route-gated actions

This commit is contained in:
ginnoir
2026-06-11 18:59:17 -05:00
parent fdefe83b89
commit 1461510a4b
4 changed files with 99 additions and 3 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ describe('M1 stub content pack', () => {
it('defines two resources and four to five actions with costs and unlocks', () => {
expect(content.resources).toHaveLength(2);
expect(content.actions.length).toBeGreaterThanOrEqual(4);
expect(content.actions.length).toBeLessThanOrEqual(5);
expect(content.actions.length).toBeLessThanOrEqual(6);
const withCosts = content.actions.filter((a) => a.costs.length > 0);
const withUnlocks = content.actions.filter((a) => a.unlock !== undefined);
expect(withCosts.length).toBeGreaterThanOrEqual(2);
+37
View File
@@ -0,0 +1,37 @@
import { describe, expect, it } from 'vitest';
import { createGameState, isActionAvailable } from '../../engine/game';
import { applyChoice, enterStoryNode, evaluateTriggers, initStory } from '../../engine/story';
import { content } from '../index';
describe('stub story graph', () => {
it('route A unlocks fortify_camp but not push_onward', () => {
const state = createGameState(content);
initStory(state, content);
evaluateTriggers(state, content, { reason: 'boot' });
enterStoryNode(state, content, 'fork_choice');
applyChoice(state, content, 'pick_a');
expect(state.storyFlags.route_a).toBe(true);
expect(isActionAvailable(state, content, 'fortify_camp')).toBe(true);
expect(isActionAvailable(state, content, 'push_onward')).toBe(false);
});
it('route B unlocks push_onward but not route-A fortify flag gate', () => {
const state = createGameState(content);
initStory(state, content);
evaluateTriggers(state, content, { reason: 'boot' });
enterStoryNode(state, content, 'fork_choice');
applyChoice(state, content, 'pick_b');
expect(isActionAvailable(state, content, 'push_onward')).toBe(true);
expect(isActionAvailable(state, content, 'fortify_camp')).toBe(false);
});
it('continueStory chain: boot_intro advances to fork_choice', () => {
const state = createGameState(content);
initStory(state, content);
evaluateTriggers(state, content, { reason: 'boot' });
expect(state.currentStoryNodeId).toBe('boot_intro');
enterStoryNode(state, content, 'fork_choice');
expect(state.currentStoryNodeId).toBe('fork_choice');
expect(content.storyNodesById.fork_choice.choices).toHaveLength(2);
});
});