Implement the 029-on-demand-bestiary feature that replaces the bundled XMM bestiary JSON with a compact search index (~350KB) and on-demand source loading, where users explicitly provide a URL or upload a JSON file to fetch full stat block data per source, which is then normalized and cached in IndexedDB (with in-memory fallback) so creature stat blocks load instantly on subsequent visits while keeping the app bundle small and never auto-fetching copyrighted content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ export { adjustHpUseCase } from "./adjust-hp-use-case.js";
|
||||
export { advanceTurnUseCase } from "./advance-turn-use-case.js";
|
||||
export { clearEncounterUseCase } from "./clear-encounter-use-case.js";
|
||||
export { editCombatantUseCase } from "./edit-combatant-use-case.js";
|
||||
export type { EncounterStore } from "./ports.js";
|
||||
export type { BestiarySourceCache, EncounterStore } from "./ports.js";
|
||||
export { removeCombatantUseCase } from "./remove-combatant-use-case.js";
|
||||
export { retreatTurnUseCase } from "./retreat-turn-use-case.js";
|
||||
export { rollAllInitiativeUseCase } from "./roll-all-initiative-use-case.js";
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { Encounter } from "@initiative/domain";
|
||||
import type { Creature, CreatureId, Encounter } from "@initiative/domain";
|
||||
|
||||
export interface EncounterStore {
|
||||
get(): Encounter;
|
||||
save(encounter: Encounter): void;
|
||||
}
|
||||
|
||||
export interface BestiarySourceCache {
|
||||
getCreature(creatureId: CreatureId): Creature | undefined;
|
||||
isSourceCached(sourceCode: string): boolean;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,23 @@ export interface Creature {
|
||||
readonly spellcasting?: readonly SpellcastingBlock[];
|
||||
}
|
||||
|
||||
export interface BestiaryIndexEntry {
|
||||
readonly name: string;
|
||||
readonly source: string;
|
||||
readonly ac: number;
|
||||
readonly hp: number;
|
||||
readonly dex: number;
|
||||
readonly cr: string;
|
||||
readonly initiativeProficiency: number;
|
||||
readonly size: string;
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
export interface BestiaryIndex {
|
||||
readonly sources: Readonly<Record<string, string>>;
|
||||
readonly creatures: readonly BestiaryIndexEntry[];
|
||||
}
|
||||
|
||||
/** Maps a CR string to the corresponding proficiency bonus. */
|
||||
export function proficiencyBonus(cr: string): number {
|
||||
const numericCr = cr.includes("/")
|
||||
|
||||
@@ -13,6 +13,8 @@ export {
|
||||
VALID_CONDITION_IDS,
|
||||
} from "./conditions.js";
|
||||
export {
|
||||
type BestiaryIndex,
|
||||
type BestiaryIndexEntry,
|
||||
type BestiarySource,
|
||||
type Creature,
|
||||
type CreatureId,
|
||||
|
||||
Reference in New Issue
Block a user