feat : add stygian guide

This commit is contained in:
2026-09-29 21:23:18 +07:00 Unverified
parent 1654d3df3a
commit 69a4d34b97
27 changed files with 11196 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
import * as z from "zod";
export const stygianLevels = [105, 110] as const;
export const stygianElements = ["Anemo", "Geo", "Electro", "Dendro", "Hydro", "Pyro", "Cryo"] as const;
const levelConfigSchema = z.object({
id: z.number().int(),
specialMonsterIcon: z.string().min(1),
monsterStats: z.object({ hp: z.number().finite().positive() }),
enLevelName: z.string().min(1),
enMonsterSpecialDesc: z.string().default(""),
enMonsterSpecialMechanicName: z.array(z.string()),
enLevelMaxDescription: z.array(z.string()),
});
export const lunarisStygianScheduleSchema = z.object({
scheduleId: z.number().int().positive(),
challengeName: z.string().min(1),
scheduleStartTime: z.string().min(1),
scheduleEndTime: z.string().min(1),
levels: z.array(z.object({
monsterLevel: z.number().int(),
levelConfigs: z.array(levelConfigSchema),
})),
});
const characterRecommendationSchema = z.strictObject({
kind: z.literal("character"),
catalogKey: z.string().min(1).max(64),
weaponKey: z.string().min(1).max(64).nullable().default(null),
artifactKey: z.string().min(1).max(64).nullable().default(null),
constellationLevel: z.number().int().min(0).max(6).nullable().default(null),
});
const catalogItemSchema = z.discriminatedUnion("kind", [
characterRecommendationSchema,
z.strictObject({ kind: z.literal("weapon"), catalogKey: z.string().min(1).max(64) }),
z.strictObject({ kind: z.literal("artifact"), catalogKey: z.string().min(1).max(64) }),
z.strictObject({ kind: z.literal("element"), element: z.enum(stygianElements) }),
z.strictObject({
kind: z.literal("constellation"),
characterKey: z.string().min(1).max(64),
constellationLevel: z.number().int().min(1).max(6),
}),
]);
function recommendationKey(item: z.infer<typeof catalogItemSchema>) {
if (item.kind === "element") return `${item.kind}:${item.element}`;
if (item.kind === "constellation") return `${item.kind}:${item.characterKey}:${item.constellationLevel}`;
return `${item.kind}:${item.catalogKey}`;
}
const teamSchema = z.array(characterRecommendationSchema).min(1).max(4).refine(
(items) => new Set(items.map(recommendationKey)).size === items.length,
"รายการในทีมต้องไม่ซ้ำกัน",
);
export const stygianRecommendationsSchema = z.strictObject({
expectedVersion: z.number().int().positive(),
level: z.union([z.literal(105), z.literal(110)]),
bossPosition: z.number().int().min(0).max(2),
teams: z.array(teamSchema),
units: z.array(catalogItemSchema).refine(
(items) => new Set(items.map(recommendationKey)).size === items.length,
"รายการตัวแนะนำต้องไม่ซ้ำกัน",
),
});
export type StygianRecommendationItemInput = z.infer<typeof catalogItemSchema>;
export type StygianRecommendationsInput = z.infer<typeof stygianRecommendationsSchema>;
export function stygianImageName(icon: string): string {
return icon
.replace(/\.png$/iu, "")
.replace(/^UI_MonsterIcon_/u, "UI_Img_LeyLineChallenge_");
}
export function stygianImageFileName(icon: string): string {
return `${stygianImageName(icon)}.png`;
}
export function selectedStygianBosses(value: z.infer<typeof lunarisStygianScheduleSchema>) {
return stygianLevels.flatMap((level) => {
const source = value.levels.find((entry) => entry.monsterLevel === level);
if (!source || source.levelConfigs.length !== 3) {
throw new Error(`Stygian schedule ${value.scheduleId} must contain exactly three level ${level} bosses.`);
}
return source.levelConfigs.map((boss, position) => ({
level,
position,
configId: boss.id,
name: boss.enLevelName,
hp: boss.monsterStats.hp,
description: boss.enMonsterSpecialDesc,
imageName: stygianImageName(boss.specialMonsterIcon),
mechanics: boss.enMonsterSpecialMechanicName.flatMap((name, index) => {
const description = boss.enLevelMaxDescription[index]?.trim();
return name.trim() && description ? [{ name: name.trim(), description }] : [];
}),
}));
});
}