Files
initiative/apps/web/src/adapters/pf2e-bestiary-index-adapter.ts
Lukas c3707cf0b6 Add PF2e attack effects, ability frequency, and perception details
Show inline on-hit effects on attack lines (e.g., "plus Grab"), frequency
limits on abilities (e.g., "(1/day)"), and perception details text alongside
senses. Strip redundant frequency lines from Foundry descriptions.

Also add resilient PF2e source fetching: batched requests with retry,
graceful handling of ad-blocker-blocked creature files (partial success
with toast warning and re-fetch prompt for missing creatures).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:37:03 +02:00

88 lines
2.1 KiB
TypeScript

import type {
Pf2eBestiaryIndex,
Pf2eBestiaryIndexEntry,
} from "@initiative/domain";
import rawIndex from "../../../../data/bestiary/pf2e-index.json";
interface CompactCreature {
readonly n: string;
readonly s: string;
readonly lv: number;
readonly ac: number;
readonly hp: number;
readonly pc: number;
readonly sz: string;
readonly tp: string;
readonly f: string;
readonly li: string;
}
interface CompactIndex {
readonly sources: Record<string, string>;
readonly creatures: readonly CompactCreature[];
}
function mapCreature(c: CompactCreature): Pf2eBestiaryIndexEntry {
return {
name: c.n,
source: c.s,
level: c.lv,
ac: c.ac,
hp: c.hp,
perception: c.pc,
size: c.sz,
type: c.tp,
};
}
let cachedIndex: Pf2eBestiaryIndex | undefined;
export function loadPf2eBestiaryIndex(): Pf2eBestiaryIndex {
if (cachedIndex) return cachedIndex;
const compact = rawIndex as unknown as CompactIndex;
cachedIndex = {
sources: compact.sources,
creatures: compact.creatures.map(mapCreature),
};
return cachedIndex;
}
export function getAllPf2eSourceCodes(): string[] {
const index = loadPf2eBestiaryIndex();
return Object.keys(index.sources);
}
export function getDefaultPf2eFetchUrl(
_sourceCode: string,
baseUrl?: string,
): string {
if (baseUrl !== undefined) {
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
}
return "https://raw.githubusercontent.com/foundryvtt/pf2e/v13-dev/packs/pf2e/";
}
export function getCreaturePathsForSource(sourceCode: string): string[] {
const compact = rawIndex as unknown as CompactIndex;
return compact.creatures.filter((c) => c.s === sourceCode).map((c) => c.f);
}
export function getCreatureNamesByPaths(paths: string[]): Map<string, string> {
const compact = rawIndex as unknown as CompactIndex;
const pathSet = new Set(paths);
const result = new Map<string, string>();
for (const c of compact.creatures) {
if (pathSet.has(c.f)) {
result.set(c.f, c.n);
}
}
return result;
}
export function getPf2eSourceDisplayName(sourceCode: string): string {
const index = loadPf2eBestiaryIndex();
return index.sources[sourceCode] ?? sourceCode;
}