79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
export const DEX_LEVELS = [
|
|
[0, 'navy', 'F-'],
|
|
[350, 'navy', 'F'],
|
|
[1_375, 'navy', 'F+'],
|
|
[3_500, 'skyblue', 'E-'],
|
|
[7_125, 'skyblue', 'E'],
|
|
[12_650, 'skyblue', 'E+'],
|
|
[20_475, 'seagreen', 'D-'],
|
|
[31_000, 'seagreen', 'D'],
|
|
[44_625, 'seagreen', 'D+'],
|
|
[61_750, 'teal', 'C-'],
|
|
[82_775, 'teal', 'C'],
|
|
[108_100, 'teal', 'C+'],
|
|
[138_125, 'limegreen', 'B-'],
|
|
[173_250, 'limegreen', 'B'],
|
|
[213_875, 'limegreen', 'B+'],
|
|
[260_400, 'darkorange', 'A-'],
|
|
[313_225, 'darkorange', 'A'],
|
|
[372_750, 'darkorange', 'A+'],
|
|
[439_375, 'tomato', 'S-'],
|
|
[513_500, 'tomato', 'S'],
|
|
[595_525, 'tomato', 'S+'],
|
|
[685_850, 'darkviolet', 'Z-'],
|
|
[784_875, 'darkviolet', 'Z'],
|
|
[893_000, 'darkviolet', 'Z+'],
|
|
[1_010_625, 'gold', 'EX-'],
|
|
[1_138_150, 'gold', 'EX'],
|
|
[1_275_975, 'white', 'EX+'],
|
|
] as const;
|
|
|
|
export const DEX_EX_PLUS = DEX_LEVELS.at(-1)![0];
|
|
|
|
export type DexProgress = {
|
|
level: number;
|
|
name: string;
|
|
color: string;
|
|
overallPercent: number;
|
|
gradePercent: number;
|
|
nextName: string | null;
|
|
remaining: number;
|
|
};
|
|
|
|
const finiteNonNegative = (value: number): number => (Number.isFinite(value) ? Math.max(0, value) : 0);
|
|
|
|
export const clampPercent = (value: number): number => Math.min(100, Math.max(0, Number.isFinite(value) ? value : 0));
|
|
|
|
export const ratioPercent = (current: number, maximum: number): number =>
|
|
maximum > 0 ? clampPercent((finiteNonNegative(current) / maximum) * 100) : 0;
|
|
|
|
export const legacyExperiencePercent = (experience: number, level: number): number => {
|
|
const safeExperience = finiteNonNegative(experience);
|
|
const safeLevel = Math.max(0, Math.floor(finiteNonNegative(level)));
|
|
if (safeExperience < 100) return clampPercent(safeExperience);
|
|
if (safeExperience < 1_000) return clampPercent(safeExperience - safeLevel * 100);
|
|
return clampPercent(((safeExperience - 10 * safeLevel ** 2) / (2 * safeLevel + 1)) * 10);
|
|
};
|
|
|
|
export const dexProgress = (rawDex: number): DexProgress => {
|
|
const dex = finiteNonNegative(rawDex);
|
|
let level = 0;
|
|
for (let index = 1; index < DEX_LEVELS.length; index += 1) {
|
|
if (dex < DEX_LEVELS[index]![0]) break;
|
|
level = index;
|
|
}
|
|
|
|
const [floor, color, name] = DEX_LEVELS[level]!;
|
|
const next = DEX_LEVELS[level + 1];
|
|
const gradePercent = next ? ratioPercent(dex - floor, next[0] - floor) : 100;
|
|
return {
|
|
level,
|
|
name,
|
|
color,
|
|
overallPercent: ratioPercent(dex, DEX_EX_PLUS),
|
|
gradePercent,
|
|
nextName: next?.[2] ?? null,
|
|
remaining: next ? Math.max(0, next[0] - dex) : 0,
|
|
};
|
|
};
|