220 lines
6.8 KiB
TypeScript
220 lines
6.8 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import { logEntrySchema, rollResultSchema, ruleEffectSchema } from "./rules";
|
|
|
|
export const statusInstanceSchema = z.object({
|
|
id: z.string().min(1),
|
|
source: z.string().optional(),
|
|
duration: z.enum(["round", "combat", "room", "run", "permanent"]).optional(),
|
|
value: z.number().optional(),
|
|
notes: z.string().optional(),
|
|
});
|
|
|
|
export const inventoryEntrySchema = z.object({
|
|
definitionId: z.string().min(1),
|
|
quantity: z.number().int().nonnegative(),
|
|
identified: z.boolean().optional(),
|
|
charges: z.number().int().optional(),
|
|
notes: z.string().optional(),
|
|
});
|
|
|
|
export const inventoryStateSchema = z.object({
|
|
carried: z.array(inventoryEntrySchema),
|
|
equipped: z.array(inventoryEntrySchema),
|
|
stored: z.array(inventoryEntrySchema),
|
|
currency: z.object({
|
|
gold: z.number().int().nonnegative(),
|
|
}),
|
|
rationCount: z.number().int().nonnegative(),
|
|
lightSources: z.array(inventoryEntrySchema),
|
|
});
|
|
|
|
export const adventurerStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
name: z.string().min(1),
|
|
level: z.number().int().positive(),
|
|
xp: z.number().int().nonnegative(),
|
|
hp: z.object({
|
|
current: z.number().int(),
|
|
max: z.number().int().positive(),
|
|
base: z.number().int().positive(),
|
|
}),
|
|
stats: z.object({
|
|
shift: z.number().int(),
|
|
discipline: z.number().int(),
|
|
precision: z.number().int(),
|
|
}),
|
|
weaponId: z.string().min(1),
|
|
manoeuvreIds: z.array(z.string()),
|
|
armourId: z.string().optional(),
|
|
favour: z.record(z.string(), z.number().int()),
|
|
statuses: z.array(statusInstanceSchema),
|
|
inventory: inventoryStateSchema,
|
|
progressionFlags: z.array(z.string()),
|
|
});
|
|
|
|
export const townStateSchema = z.object({
|
|
visits: z.number().int().nonnegative(),
|
|
knownServices: z.array(z.string()),
|
|
stash: z.array(inventoryEntrySchema),
|
|
pendingSales: z.array(inventoryEntrySchema),
|
|
serviceFlags: z.array(z.string()),
|
|
});
|
|
|
|
export const questStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
title: z.string().min(1),
|
|
status: z.enum(["available", "active", "completed", "failed"]),
|
|
progressFlags: z.array(z.string()),
|
|
rewardText: z.string().optional(),
|
|
});
|
|
|
|
export const runSummarySchema = z.object({
|
|
runId: z.string().min(1),
|
|
startedAt: z.string().min(1),
|
|
endedAt: z.string().optional(),
|
|
deepestLevel: z.number().int().positive(),
|
|
roomsVisited: z.number().int().nonnegative(),
|
|
creaturesDefeated: z.array(z.string()),
|
|
xpGained: z.number().int().nonnegative(),
|
|
treasureValue: z.number().int().nonnegative(),
|
|
outcome: z.enum(["escaped", "defeated", "saved-in-progress"]),
|
|
});
|
|
|
|
export const campaignStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
createdAt: z.string().min(1),
|
|
updatedAt: z.string().min(1),
|
|
rulesVersion: z.string().min(1),
|
|
contentVersion: z.string().min(1),
|
|
adventurer: adventurerStateSchema,
|
|
unlockedLevels: z.array(z.number().int().positive()),
|
|
completedLevels: z.array(z.number().int().positive()),
|
|
townState: townStateSchema,
|
|
questState: z.array(questStateSchema),
|
|
campaignFlags: z.array(z.string()),
|
|
runHistory: z.array(runSummarySchema),
|
|
});
|
|
|
|
export const roomExitStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
direction: z.enum(["north", "east", "south", "west"]),
|
|
exitType: z.enum(["open", "door", "locked", "secret", "shaft", "stairs"]),
|
|
discovered: z.boolean(),
|
|
traversable: z.boolean(),
|
|
leadsToRoomId: z.string().optional(),
|
|
destinationLevel: z.number().int().optional(),
|
|
});
|
|
|
|
export const encounterStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
sourceTableCode: z.string().optional(),
|
|
creatureIds: z.array(z.string()),
|
|
creatureNames: z.array(z.string()).optional(),
|
|
resultLabel: z.string().optional(),
|
|
resolved: z.boolean(),
|
|
surprise: z.boolean().optional(),
|
|
rewardPending: z.boolean().optional(),
|
|
});
|
|
|
|
export const roomObjectStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
objectType: z.enum(["container", "altar", "corpse", "hazard", "feature", "quest"]),
|
|
sourceTableCode: z.string().optional(),
|
|
interacted: z.boolean(),
|
|
hidden: z.boolean().optional(),
|
|
effects: z.array(ruleEffectSchema).optional(),
|
|
notes: z.string().optional(),
|
|
});
|
|
|
|
export const roomStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
level: z.number().int().positive(),
|
|
templateId: z.string().optional(),
|
|
position: z.object({
|
|
x: z.number().int(),
|
|
y: z.number().int(),
|
|
}),
|
|
dimensions: z.object({
|
|
width: z.number().int().positive(),
|
|
height: z.number().int().positive(),
|
|
}),
|
|
roomClass: z.enum(["normal", "small", "large", "special", "start", "stairs"]),
|
|
exits: z.array(roomExitStateSchema),
|
|
discovery: z.object({
|
|
generated: z.boolean(),
|
|
entered: z.boolean(),
|
|
cleared: z.boolean(),
|
|
searched: z.boolean(),
|
|
}),
|
|
encounter: encounterStateSchema.optional(),
|
|
objects: z.array(roomObjectStateSchema),
|
|
notes: z.array(z.string()),
|
|
flags: z.array(z.string()),
|
|
});
|
|
|
|
export const dungeonLevelStateSchema = z.object({
|
|
level: z.number().int().positive(),
|
|
themeName: z.string().optional(),
|
|
rooms: z.record(z.string(), roomStateSchema),
|
|
discoveredRoomOrder: z.array(z.string()),
|
|
stairsUpRoomId: z.string().optional(),
|
|
stairsDownRoomId: z.string().optional(),
|
|
secretDoorUsed: z.boolean().optional(),
|
|
exhaustedExitSearch: z.boolean().optional(),
|
|
});
|
|
|
|
export const dungeonStateSchema = z.object({
|
|
levels: z.record(z.string(), dungeonLevelStateSchema),
|
|
revealedPercentByLevel: z.record(z.string(), z.number()),
|
|
globalFlags: z.array(z.string()),
|
|
});
|
|
|
|
export const combatantStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
name: z.string().min(1),
|
|
sourceDefinitionId: z.string().optional(),
|
|
hpCurrent: z.number().int(),
|
|
hpMax: z.number().int().positive(),
|
|
shift: z.number().int(),
|
|
discipline: z.number().int(),
|
|
precision: z.number().int(),
|
|
armourValue: z.number().int().optional(),
|
|
statuses: z.array(statusInstanceSchema),
|
|
traits: z.array(z.string()),
|
|
});
|
|
|
|
export const interruptStateSchema = z.object({
|
|
source: z.enum(["player", "enemy"]),
|
|
trigger: z.string().min(1),
|
|
effectText: z.string().min(1),
|
|
resolved: z.boolean(),
|
|
});
|
|
|
|
export const combatStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
round: z.number().int().positive(),
|
|
actingSide: z.enum(["player", "enemy"]),
|
|
fatigueDie: z.number().int().optional(),
|
|
player: combatantStateSchema,
|
|
enemies: z.array(combatantStateSchema),
|
|
selectedManoeuvreId: z.string().optional(),
|
|
lastRoll: rollResultSchema.optional(),
|
|
pendingInterrupt: interruptStateSchema.optional(),
|
|
combatLog: z.array(logEntrySchema),
|
|
});
|
|
|
|
export const runStateSchema = z.object({
|
|
id: z.string().min(1),
|
|
campaignId: z.string().min(1),
|
|
status: z.enum(["active", "paused", "completed", "failed"]),
|
|
startedAt: z.string().min(1),
|
|
currentLevel: z.number().int().positive(),
|
|
currentRoomId: z.string().optional(),
|
|
dungeon: dungeonStateSchema,
|
|
adventurerSnapshot: adventurerStateSchema,
|
|
activeCombat: combatStateSchema.optional(),
|
|
log: z.array(logEntrySchema),
|
|
pendingEffects: z.array(ruleEffectSchema),
|
|
});
|