feat: bootstrap walking skeleton
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Content schemas.
|
||||
*
|
||||
* Game content (resources, actions, later story nodes) is data, validated at
|
||||
* load time with zod so a malformed definition fails loudly instead of corrupting
|
||||
* runtime state. The engine consumes the validated `Content` object and never
|
||||
* reaches for raw definitions.
|
||||
*/
|
||||
|
||||
export const resourceDefSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
startAmount: z.number().nonnegative().default(0),
|
||||
});
|
||||
|
||||
export const actionDefSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
durationMs: z.number().positive(),
|
||||
yields: z.object({
|
||||
resourceId: z.string().min(1),
|
||||
amount: z.number().positive(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type ResourceDef = z.infer<typeof resourceDefSchema>;
|
||||
export type ActionDef = z.infer<typeof actionDefSchema>;
|
||||
|
||||
export interface Content {
|
||||
resources: ResourceDef[];
|
||||
actions: ActionDef[];
|
||||
resourcesById: Record<string, ResourceDef>;
|
||||
actionsById: Record<string, ActionDef>;
|
||||
}
|
||||
|
||||
function indexById<T extends { id: string }>(items: T[], kind: string): Record<string, T> {
|
||||
const byId: Record<string, T> = {};
|
||||
for (const item of items) {
|
||||
if (byId[item.id]) {
|
||||
throw new Error(`Duplicate ${kind} id "${item.id}"`);
|
||||
}
|
||||
byId[item.id] = item;
|
||||
}
|
||||
return byId;
|
||||
}
|
||||
|
||||
/** Validate raw definitions and build the indexed, referentially-checked Content. */
|
||||
export function buildContent(input: { resources: unknown[]; actions: unknown[] }): Content {
|
||||
const resources = input.resources.map((r) => resourceDefSchema.parse(r));
|
||||
const actions = input.actions.map((a) => actionDefSchema.parse(a));
|
||||
|
||||
const resourcesById = indexById(resources, 'resource');
|
||||
const actionsById = indexById(actions, 'action');
|
||||
|
||||
for (const action of actions) {
|
||||
if (!resourcesById[action.yields.resourceId]) {
|
||||
throw new Error(
|
||||
`Action "${action.id}" yields unknown resource "${action.yields.resourceId}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { resources, actions, resourcesById, actionsById };
|
||||
}
|
||||
Reference in New Issue
Block a user