Files
buzz-sheet/lib/catalog/lunaris.ts
T
gunshiz 9896e8a4ad
CI / Verify (push) Successful in 1m35s
CI / Build immutable images and deploy (push) Successful in 5m27s
feat(guides) : add safer publishing and discovery
2026-09-19 13:32:57 +07:00

177 lines
4.8 KiB
TypeScript

import * as z from "zod";
export interface CatalogConstellation {
level: number;
name: string;
description: string;
iconKey: string | null;
}
export interface CatalogTalent {
type: "NA" | "E" | "Q";
name: string;
description: string;
iconKey: string | null;
}
export interface CatalogAscensionMaterial {
key: string;
amount: number;
}
export interface CatalogAscensionGroup {
type: string;
items: CatalogAscensionMaterial[];
}
export type CatalogLevelingGroup = CatalogAscensionGroup;
export interface CatalogMaterial {
key: string;
name: string;
description: string | null;
rarity: number | null;
type: string | null;
imageKey: string;
}
export interface CatalogBaseStat {
key: string;
value: number | string;
}
const constellationSchema = z.object({
name: z.string(),
description: z.string(),
icon: z.string(),
});
const skillSchema = z.object({
name: z.string(),
description: z.string(),
icon: z.string(),
});
const attributeSchema = z.record(z.string(), z.union([z.number(), z.string()]));
const levelingSchema = z.record(z.string(), z.record(z.string(), z.number().int()));
export const characterDetailSchema = z.object({
info: z.object({
element: z.string().optional(),
weapon: z.string().optional(),
attributes: z.array(attributeSchema).nullish(),
}).nullish(),
constellations: z.record(z.string(), constellationSchema).nullish(),
skills: z.object({
normalattack: skillSchema.optional(),
elementalskill: skillSchema.optional(),
elementalburst: skillSchema.optional(),
leveling: levelingSchema.optional(),
}).nullish(),
ascension: z.record(
z.string(),
z.union([z.number().int(), z.record(z.string(), z.number().int())]),
).nullish(),
leveling: levelingSchema.nullish(),
attributes: z.array(attributeSchema).nullish(),
});
export function cleanLunarisText(value: string): string {
return value
.replace(/\\n/gu, "\n")
.replace(/\{\/?LINK[^}]*\}/gu, "")
.replace(/<\/?[a-z]+(?:=[^>]*)?>/giu, "")
.replace(/^"|"$/gu, "")
.trim();
}
export function avatarIconName(value: string): string {
return value.replace(/^UI_Gacha_AvatarIcon_/u, "UI_AvatarIcon_");
}
function characterAssetName(value: string): string {
return avatarIconName(value).replace(/^UI_AvatarIcon_/u, "");
}
export function splashImageName(value: string): string {
return `UI_Gacha_AvatarImg_${characterAssetName(value)}.png`;
}
export function nameCardImageName(value: string): string {
return `UI_NameCardPic_${characterAssetName(value)}_P.png`;
}
export function supportsWeaponType(
characterWeaponType: string | null | undefined,
weaponType: string | null | undefined,
): boolean {
return Boolean(characterWeaponType && characterWeaponType === weaponType);
}
export function constellationSnapshot(
value: z.infer<typeof characterDetailSchema>,
iconKey: (icon: string) => string,
): CatalogConstellation[] {
const result: CatalogConstellation[] = [];
for (const index of Array.from({ length: 6 }, (_, itemIndex) => itemIndex)) {
const level = index + 1;
const item = value.constellations?.[`const${level}`];
if (!item) continue;
result.push({
level,
name: cleanLunarisText(item.name),
description: cleanLunarisText(item.description),
iconKey: iconKey(item.icon),
});
}
return result;
}
export function talentSnapshot(
value: z.infer<typeof characterDetailSchema>,
iconKey: (icon: string) => string,
): CatalogTalent[] {
const talents = [
["NA", value.skills?.normalattack],
["E", value.skills?.elementalskill],
["Q", value.skills?.elementalburst],
] as const;
return talents.flatMap(([type, talent]) => talent ? [{
type,
name: cleanLunarisText(talent.name),
description: cleanLunarisText(talent.description),
iconKey: iconKey(talent.icon),
}] : []);
}
export function ascensionSnapshot(
value: z.infer<typeof characterDetailSchema>,
): CatalogAscensionGroup[] {
return Object.entries(value.ascension ?? {}).map(([type, entries]) => ({
type,
items:
typeof entries === "number"
? [{ key: type, amount: entries }]
: Object.entries(entries).map(([key, amount]) => ({ key, amount })),
}));
}
export function levelingSnapshot(
value: z.infer<typeof characterDetailSchema>,
): CatalogLevelingGroup[] {
return Object.entries(value.skills?.leveling ?? value.leveling ?? {}).map(([type, entries]) => ({
type,
items: Object.entries(entries).map(([key, amount]) => ({ key, amount })),
}));
}
export function baseStatsSnapshot(
value: z.infer<typeof characterDetailSchema>,
): CatalogBaseStat[] {
const level90 = (value.info?.attributes ?? value.attributes)?.find((entry) => entry.level === 90);
if (!level90) return [];
return Object.entries(level90)
.filter(([key]) => key !== "level" && key !== "ascension")
.map(([key, stat]) => ({ key, value: stat }));
}