/** * CharacterRegistry — central lookup for Character objects. * * Usage: * import { CharacterRegistry } from "@registry/CharacterRegistry"; * * const char = CharacterRegistry.find("«Flash»"); * const chars = CharacterRegistry.forUser("flash"); * const active = CharacterRegistry.getActive("flash"); */ import fs from "fs"; import path from "path"; import { Character, UserKey, CharName } from "@types"; import { Paths } from "@helpers/paths"; const CHARS_PATH = Paths.data("characters.json"); let _cache: Record | null = null; function loadChars(): Record { if (!_cache) { try { _cache = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8")); console.log("[CharacterRegistry] cache miss — loading from disk"); } catch { _cache = {}; } } return _cache!; } export const CharacterRegistry = { /** * Find a character by name across all users. * Character names are unique across users (enforced by conflict system). */ find(charName: CharName): Character | null { const chars = loadChars(); for (const data of Object.values(chars)) { const found = data.characters?.find( (c) => c.name.toLowerCase() === charName.toLowerCase() ); if (found) return found; } return null; }, all(): Character[] { const chars = loadChars(); return Object.values(chars).flatMap((data: any) => data.characters ?? []); }, /** * Find a character by name for a specific user. */ findForUser(userKey: UserKey, charName: CharName): Character | null { const chars = loadChars(); return chars[userKey]?.characters?.find( (c) => c.name.toLowerCase() === charName.toLowerCase() ) ?? null; }, /** * Get all characters for a user. */ forUser(userKey: UserKey): Character[] { const chars = loadChars(); return chars[userKey]?.characters ?? []; }, /** * Get the active character for a user (from characters.json only, no borrow). * For effective char (including borrows), use getEffectiveCharacter from borrow.ts. */ getActive(userKey: UserKey): Character | null { return CharacterRegistry.forUser(userKey).find((c) => c.active) ?? null; }, /** * Get all characters shared with a given user across all owners. */ sharedWith(userKey: UserKey): { char: Character; ownerKey: UserKey }[] { const chars = loadChars(); const result: { char: Character; ownerKey: UserKey }[] = []; for (const [ownerKey, data] of Object.entries(chars)) { if (ownerKey === userKey) continue; for (const char of data.characters ?? []) { if (char.sharedWith?.includes(userKey)) { result.push({ char, ownerKey }); } } } return result; }, invalidateCache(): void { _cache = null; }, };