feat : add stygian guide
This commit is contained in:
@@ -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 }] : [];
|
||||
}),
|
||||
}));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user