Release Healer Man 0.1.40
This commit is contained in:
1 parent
ed0d2fea3d
commit
197a64117c
215 files changed
+5131
-181
No files matched your search
@@ -0,0 +1,97 @@
|
||||
import { DUNGEON_DEFINITIONS } from "./dungeonRegistry";
|
||||
import type { DungeonDefinition } from "./dungeonTypes";
|
||||
import type { MobModelStyle } from "./mobPopulation";
|
||||
import { BOSS_CARD_RARITIES, type BossCardCollection, type BossCardRarity } from "./bossCards";
|
||||
|
||||
export interface BossCardDefinition {
|
||||
readonly key: string;
|
||||
readonly name: string;
|
||||
readonly category: "wow" | "rom";
|
||||
readonly model?: MobModelStyle;
|
||||
readonly dungeons: readonly { readonly id: string; readonly title: string }[];
|
||||
readonly sources: readonly { readonly dungeonId: string; readonly entityId: string }[];
|
||||
}
|
||||
|
||||
/** Same boss in multiple dungeons shares a collection, while WoW and RoM remain distinct. */
|
||||
export function buildBossCardCatalog(definitions: readonly DungeonDefinition[]): readonly BossCardDefinition[] {
|
||||
const bosses = new Map<string, BossCardDefinition>();
|
||||
for (const dungeon of definitions) {
|
||||
for (const entity of Object.values(dungeon.entities)) {
|
||||
if (entity.kind !== "boss") continue;
|
||||
const category = dungeon.expansion === "runewaker" ? "rom" : "wow";
|
||||
const key = `${category}:${entity.name.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
||||
const existing = bosses.get(key);
|
||||
bosses.set(key, {
|
||||
key,
|
||||
name: existing?.name ?? entity.name,
|
||||
category,
|
||||
model: existing?.model ?? entity.visual.model,
|
||||
dungeons: existing?.dungeons.some((entry) => entry.id === dungeon.id)
|
||||
? existing.dungeons
|
||||
: [...(existing?.dungeons ?? []), { id: dungeon.id, title: dungeon.title }],
|
||||
sources: [...(existing?.sources ?? []), { dungeonId: dungeon.id, entityId: entity.id }],
|
||||
});
|
||||
}
|
||||
}
|
||||
return [...bosses.values()].map((boss) => ({
|
||||
...boss,
|
||||
dungeons: [...boss.dungeons].sort((a, b) => a.title.localeCompare(b.title)),
|
||||
})).sort((a, b) => a.name.localeCompare(b.name) || a.key.localeCompare(b.key));
|
||||
}
|
||||
|
||||
export const BOSS_CARD_CATALOG = buildBossCardCatalog(DUNGEON_DEFINITIONS);
|
||||
const cardsByKey = new Map(BOSS_CARD_CATALOG.map((card) => [card.key, card]));
|
||||
|
||||
export function bossCardByKey(key: string): BossCardDefinition | null {
|
||||
return cardsByKey.get(key) ?? null;
|
||||
}
|
||||
|
||||
export function bossCardForSource(sourceId: string, dungeonId: string, name?: string): BossCardDefinition | null {
|
||||
return BOSS_CARD_CATALOG.find((card) => card.sources.some(
|
||||
(source) => source.dungeonId === dungeonId && source.entityId === sourceId,
|
||||
)) ?? BOSS_CARD_CATALOG.find((card) => card.sources.some((source) => source.entityId === sourceId))
|
||||
?? BOSS_CARD_CATALOG.find((card) => card.name === name && card.dungeons.some((entry) => entry.id === dungeonId))
|
||||
?? null;
|
||||
}
|
||||
|
||||
export interface BossCardRow {
|
||||
readonly boss: BossCardDefinition;
|
||||
readonly rarity: BossCardRarity;
|
||||
readonly copies: number;
|
||||
}
|
||||
export type BossCardGrouping = "all" | "dungeon" | "rarity";
|
||||
export interface BossCardGroup {
|
||||
readonly key: string;
|
||||
readonly title: string;
|
||||
readonly rows: readonly BossCardRow[];
|
||||
}
|
||||
|
||||
export function groupBossCards(
|
||||
collection: BossCardCollection,
|
||||
options: { readonly earnedOnly: boolean; readonly grouping: BossCardGrouping; readonly search?: string; readonly category?: "all" | "wow" | "rom" },
|
||||
catalog = BOSS_CARD_CATALOG,
|
||||
): readonly BossCardGroup[] {
|
||||
const query = options.search?.trim().toLowerCase() ?? "";
|
||||
const rows = catalog.flatMap((boss) => {
|
||||
if (options.category && options.category !== "all" && boss.category !== options.category) return [];
|
||||
if (query && !`${boss.name} ${boss.dungeons.map((entry) => entry.title).join(" ")}`.toLowerCase().includes(query)) return [];
|
||||
return BOSS_CARD_RARITIES.flatMap((rarity) => {
|
||||
const copies = collection.counts[boss.key]?.[rarity] ?? 0;
|
||||
return options.earnedOnly && !copies ? [] : [{ boss, rarity, copies }];
|
||||
});
|
||||
});
|
||||
if (options.grouping === "rarity") {
|
||||
return BOSS_CARD_RARITIES.map((rarity) => ({
|
||||
key: rarity, title: `${rarity.charAt(0).toUpperCase()}${rarity.slice(1)} cards`, rows: rows.filter((row) => row.rarity === rarity),
|
||||
})).filter((group) => group.rows.length);
|
||||
}
|
||||
if (options.grouping === "dungeon") {
|
||||
const groups = new Map<string, BossCardGroup>();
|
||||
for (const row of rows) for (const dungeon of row.boss.dungeons) {
|
||||
const existing = groups.get(dungeon.id);
|
||||
groups.set(dungeon.id, { key: dungeon.id, title: dungeon.title, rows: [...(existing?.rows ?? []), row] });
|
||||
}
|
||||
return [...groups.values()].sort((a, b) => a.title.localeCompare(b.title));
|
||||
}
|
||||
return rows.length ? [{ key: "all", title: "All boss cards", rows }] : [];
|
||||
}
|
||||
Reference in new issue
Block a user