98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
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),
|
|
refinement: z.number().int().min(1).max(5).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) }),
|
|
]);
|
|
|
|
function recommendationKey(item: z.infer<typeof catalogItemSchema>) {
|
|
if (item.kind === "element") return `${item.kind}:${item.element}`;
|
|
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 }] : [];
|
|
}),
|
|
}));
|
|
});
|
|
}
|