diff --git a/src/buddy/companion.ts b/src/buddy/companion.ts index 09c3838..a0fa798 100644 --- a/src/buddy/companion.ts +++ b/src/buddy/companion.ts @@ -116,18 +116,21 @@ export function rollWithSeed(seed: string): Roll { return rollFrom(mulberry32(hashString(seed))) } +export function generateSeed(): string { + return `rehatch-${Date.now()}-${Math.random().toString(36).slice(2, 10)}` +} + export function companionUserId(): string { const config = getGlobalConfig() return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon' } -// Regenerate bones from userId, merge with stored soul. Bones never persist -// so species renames and SPECIES-array edits can't break stored companions, -// and editing config.companion can't fake a rarity. +// Regenerate bones from seed or userId, merge with stored soul. export function getCompanion(): Companion | undefined { const stored = getGlobalConfig().companion if (!stored) return undefined - const { bones } = roll(companionUserId()) + const seed = stored.seed ?? companionUserId() + const { bones } = rollWithSeed(seed) // bones last so stale bones fields in old-format configs get overridden return { ...stored, ...bones } } diff --git a/src/buddy/types.ts b/src/buddy/types.ts index 8f1c82a..46c53c7 100644 --- a/src/buddy/types.ts +++ b/src/buddy/types.ts @@ -111,6 +111,7 @@ export type CompanionBones = { export type CompanionSoul = { name: string personality: string + seed?: string } export type Companion = CompanionBones & diff --git a/src/commands/buddy/buddy.ts b/src/commands/buddy/buddy.ts index 2b371f2..134a3ff 100644 --- a/src/commands/buddy/buddy.ts +++ b/src/commands/buddy/buddy.ts @@ -1,8 +1,8 @@ import { getCompanion, - roll, + rollWithSeed, + generateSeed, type Roll, - companionUserId, } from '../../buddy/companion.js' import { type StoredCompanion, @@ -133,7 +133,8 @@ export const call: LocalCommandCall = async (args, _context) => { } } - const r = roll(companionUserId()) + const seed = generateSeed() + const r = rollWithSeed(seed) const name = SPECIES_NAMES[r.bones.species] ?? 'Buddy' const personality = SPECIES_PERSONALITY[r.bones.species] ?? 'Mysterious and code-savvy.' @@ -141,6 +142,7 @@ export const call: LocalCommandCall = async (args, _context) => { const stored: StoredCompanion = { name, personality, + seed, hatchedAt: Date.now(), } @@ -212,7 +214,8 @@ export const call: LocalCommandCall = async (args, _context) => { // /buddy rehatch — re-roll a new companion (replaces existing) if (sub === 'rehatch') { - const r = roll(companionUserId()) + const seed = generateSeed() + const r = rollWithSeed(seed) const name = SPECIES_NAMES[r.bones.species] ?? 'Buddy' const personality = SPECIES_PERSONALITY[r.bones.species] ?? 'Mysterious and code-savvy.' @@ -220,6 +223,7 @@ export const call: LocalCommandCall = async (args, _context) => { const stored: StoredCompanion = { name, personality, + seed, hatchedAt: Date.now(), }