import type { ContentPack } from "@/types/content"; import type { AdventurerState, InventoryEntry } from "@/types/state"; export type CharacterCreationChoiceSet = { name: string; weaponId: string; armourId: string; scrollId: string; }; const DEFAULT_STARTING_ITEM_IDS = [ "item.flint-and-steel", "item.lantern", "item.ration", "item.pouch", "item.wax-sealing-kit", "item.backpack", ]; function requireDefinition( entries: T[], id: string, label: string, ) { const entry = entries.find((candidate) => candidate.id === id); if (!entry) { throw new Error(`Unknown ${label} id: ${id}`); } return entry; } function makeInventoryEntry(definitionId: string, quantity = 1): InventoryEntry { return { definitionId, quantity, }; } export function getCharacterCreationOptions(content: ContentPack) { return { weapons: content.weapons.filter((weapon) => weapon.startingOption), armour: content.armour.filter((armour) => armour.startingOption), scrolls: content.scrolls.filter((scroll) => scroll.startingOption), }; } export function createStartingAdventurer( content: ContentPack, choices: CharacterCreationChoiceSet, ): AdventurerState { const trimmedName = choices.name.trim(); if (trimmedName.length === 0) { throw new Error("Adventurer name is required."); } const selectedWeapon = requireDefinition(content.weapons, choices.weaponId, "weapon"); const selectedArmour = requireDefinition(content.armour, choices.armourId, "armour"); const selectedScroll = requireDefinition(content.scrolls, choices.scrollId, "scroll"); if (!selectedWeapon.startingOption) { throw new Error(`Weapon ${selectedWeapon.id} is not a legal starting option.`); } if (!selectedArmour.startingOption) { throw new Error(`Armour ${selectedArmour.id} is not a legal starting option.`); } if (!selectedScroll.startingOption) { throw new Error(`Scroll ${selectedScroll.id} is not a legal starting option.`); } const allowedManoeuvreIds = selectedWeapon.allowedManoeuvreIds; if (allowedManoeuvreIds.length === 0) { throw new Error(`Weapon ${selectedWeapon.id} does not define starting manoeuvres.`); } DEFAULT_STARTING_ITEM_IDS.forEach((itemId) => requireDefinition(content.items, itemId, "item")); requireDefinition(content.potions, "potion.healing", "potion"); return { id: "adventurer.new", name: trimmedName, level: 1, xp: 0, hp: { current: 10, max: 10, base: 10, }, stats: { shift: 2, discipline: 1, precision: 0, }, weaponId: selectedWeapon.id, manoeuvreIds: allowedManoeuvreIds, armourId: selectedArmour.id, favour: {}, statuses: [], inventory: { carried: [ makeInventoryEntry("item.flint-and-steel"), makeInventoryEntry("item.lantern"), makeInventoryEntry("item.ration", 3), makeInventoryEntry("item.pouch"), makeInventoryEntry("item.wax-sealing-kit"), makeInventoryEntry("item.backpack"), makeInventoryEntry(selectedScroll.id), makeInventoryEntry("potion.healing"), ], equipped: [ makeInventoryEntry(selectedWeapon.id), makeInventoryEntry(selectedArmour.id), ], stored: [], currency: { gold: 0, }, rationCount: 3, lightSources: [makeInventoryEntry("item.lantern")], }, progressionFlags: [], }; }