fix(content): guard optional durationMs and cover loop validation
This commit is contained in:
@@ -208,4 +208,21 @@ describe('action kind schema', () => {
|
|||||||
});
|
});
|
||||||
expect(content.actionsById.rest.kind).toBe('loop');
|
expect(content.actionsById.rest.kind).toBe('loop');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rejects loop action without durationMs', () => {
|
||||||
|
expect(() =>
|
||||||
|
buildContent({
|
||||||
|
resources: [{ id: 'supplies', name: 'Supplies' }],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: 'broken_loop',
|
||||||
|
name: 'Broken loop',
|
||||||
|
kind: 'loop',
|
||||||
|
group: { id: 'camp', label: 'Camp' },
|
||||||
|
yields: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
).toThrow(/durationMs/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const actionDefSchema = z
|
|||||||
path: ['durationMs'],
|
path: ['durationMs'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (action.kind === 'story' && !action.storyChoiceId) {
|
if (action.kind === 'story' && action.storyChoiceId === undefined) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: 'custom',
|
code: 'custom',
|
||||||
message: 'story actions require storyChoiceId',
|
message: 'story actions require storyChoiceId',
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export function tickGame(state: GameState, content: Content, tickMs: number): Ti
|
|||||||
const actionId = state.activeActionId;
|
const actionId = state.activeActionId;
|
||||||
const action = content.actionsById[actionId];
|
const action = content.actionsById[actionId];
|
||||||
if (!action) return { completedActionIds };
|
if (!action) return { completedActionIds };
|
||||||
|
if (action.durationMs === undefined) return { completedActionIds };
|
||||||
if (state.actionElapsedMs < action.durationMs) return { completedActionIds };
|
if (state.actionElapsedMs < action.durationMs) return { completedActionIds };
|
||||||
|
|
||||||
state.actionElapsedMs -= action.durationMs;
|
state.actionElapsedMs -= action.durationMs;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export function toView(state: GameState, content: GameContent): GameView {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const action = state.activeActionId ? content.actionsById[state.activeActionId] : undefined;
|
const action = state.activeActionId ? content.actionsById[state.activeActionId] : undefined;
|
||||||
const actionProgress = action ? Math.min(1, state.actionElapsedMs / action.durationMs) : 0;
|
const actionProgress = action && action.durationMs ? Math.min(1, state.actionElapsedMs / action.durationMs) : 0;
|
||||||
const queuedActionIds = [...state.actionQueue];
|
const queuedActionIds = [...state.actionQueue];
|
||||||
const queuedActionNames = queuedActionIds.map((id) => content.actionsById[id]?.name ?? id);
|
const queuedActionNames = queuedActionIds.map((id) => content.actionsById[id]?.name ?? id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user