Files
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

79 lines
2.4 KiB
TypeScript

export const targetStatOptions = [
"HP",
"ATK",
"DEF",
"EM",
"ER",
"CRIT RATE",
"CRIT DAMAGE",
] as const;
export type TargetStat = (typeof targetStatOptions)[number];
export const targetStatDefaults: Array<{ stat: TargetStat; value: string }> = [
{ stat: "HP", value: "" },
{ stat: "ATK", value: "" },
{ stat: "DEF", value: "" },
{ stat: "EM", value: "" },
{ stat: "ER", value: "100%" },
{ stat: "CRIT RATE", value: "5%" },
{ stat: "CRIT DAMAGE", value: "50%" },
];
const legacyTargetStatNames: Record<string, TargetStat> = {
"CRIT Rate": "CRIT RATE",
"CRIT DMG": "CRIT DAMAGE",
};
export function normalizeTargetStat(value: string): TargetStat | null {
const normalized = legacyTargetStatNames[value] ?? value;
return targetStatOptions.includes(normalized as TargetStat)
? (normalized as TargetStat)
: null;
}
export function targetStatIconName(value: string): string | null {
const stat = normalizeTargetStat(value);
if (!stat) return null;
return stat === "CRIT RATE"
? "critrate"
: stat === "CRIT DAMAGE"
? "critdmg"
: stat.toLowerCase();
}
export function recommendedStatIconName(value: string): string | null {
if (value === "CRIT Rate%/CRIT DMG%") return "critrate";
if (value === "CRIT Rate%") return "critrate";
if (value === "CRIT DMG%") return "critdmg";
if (value === "Healing Bonus%") return "healing-bonus";
if (value.endsWith(" DMG%")) return "elemental-bonus";
const base = value.replace(/%$/, "").toLowerCase();
if (value.endsWith("%") && ["hp", "atk", "def"].includes(base)) return `${base}-percent`;
return ["hp", "atk", "def", "em", "er"].includes(base) ? base : null;
}
export function recommendedStatOptionConflicts(option: string, selected: readonly string[]): boolean {
const combined = "CRIT Rate%/CRIT DMG%";
const individual = ["CRIT Rate%", "CRIT DMG%"];
return option === combined
? individual.some((stat) => selected.includes(stat))
: individual.includes(option) && selected.includes(combined);
}
export function recommendedStatElement(value: string): string | null {
const match = /^(Anemo|Geo|Electro|Dendro|Hydro|Pyro|Cryo) DMG%$/u.exec(value);
return match?.[1] ?? null;
}
export function statPriorityColorClass(index: number): string {
return index === 0
? "text-tier-gold"
: index === 1
? "text-tier-silver"
: index === 2
? "text-tier-copper"
: "text-foreground";
}