This repository has been archived on 2024-10-29. You can view files and clone it, but cannot push or open issues or pull requests.
scoresaber-reloadedv3/projects/website/src/common/song-utils.ts

102 lines
3.1 KiB
TypeScript
Raw Normal View History

2024-10-04 17:25:37 +00:00
type Difficulty = {
name: DifficultyName;
gamemode?: string;
color: string;
};
type DifficultyName = "Easy" | "Normal" | "Hard" | "Expert" | "Expert+";
const difficulties: Difficulty[] = [
2024-10-19 17:17:43 +00:00
{ name: "Easy", color: "#3cb371" },
2024-10-04 17:25:37 +00:00
{ name: "Normal", color: "#59b0f4" },
{ name: "Hard", color: "#FF6347" },
{ name: "Expert", color: "#bf2a42" },
{ name: "Expert+", color: "#8f48db" },
];
export type ScoreBadge = {
name: string;
min: number | null;
max: number | null;
color: string;
};
const scoreBadges: ScoreBadge[] = [
{ name: "SS+", min: 95, max: null, color: getDifficulty("Expert+")!.color },
{ name: "SS", min: 90, max: 95, color: getDifficulty("Expert")!.color },
{ name: "S+", min: 85, max: 90, color: getDifficulty("Hard")!.color },
{ name: "S", min: 80, max: 85, color: getDifficulty("Normal")!.color },
{ name: "A", min: 70, max: 80, color: getDifficulty("Easy")!.color },
{ name: "-", min: null, max: 70, color: "hsl(var(--accent))" },
];
/**
* Returns the color based on the accuracy provided.
*
* @param acc - The accuracy for the score
* @returns The corresponding color for the accuracy.
*/
export function getScoreBadgeFromAccuracy(acc: number): ScoreBadge {
// Check for SS+ first since it has no upper limit
if (acc >= 95) {
return scoreBadges[0]; // SS+ color
}
// Iterate through the rest of the badges
for (const badge of scoreBadges) {
const min = badge.min ?? -Infinity; // Treat null `min` as -Infinity
const max = badge.max ?? Infinity; // Treat null `max` as Infinity
// Check if the accuracy falls within the badge's range
if (acc >= min && acc < (max === null ? Infinity : max)) {
return badge; // Return the color of the matching badge
}
}
// Fallback color if no badge matches (should not happen)
return scoreBadges[scoreBadges.length - 1];
}
/**
* Parses a raw difficulty into a {@link Difficulty}
* Example: _Easy_SoloStandard -> { name: "Easy", type: "Standard", color: "#59b0f4" }
*
* @param rawDifficulty the raw difficulty to parse
* @return the parsed difficulty
*/
export function getDifficultyFromRawDifficulty(rawDifficulty: string): Difficulty {
const [name, ...type] = rawDifficulty
.replace("Plus", "+") // Replaces Plus with + so we can match it to our difficulty names
.replace("Solo", "") // Removes "Solo"
.replace(/^_+|_+$/g, "") // Removes leading and trailing underscores
.split("_");
const difficulty = difficulties.find(d => d.name === name);
if (!difficulty) {
throw new Error(`Unknown difficulty: ${rawDifficulty}`);
}
return {
...difficulty,
gamemode: type.join("_"),
};
}
/**
* Gets a {@link Difficulty} from its name
*
* @param diff the name of the difficulty
* @returns the difficulty
*/
export function getDifficulty(diff: DifficultyName) {
return difficulties.find(d => d.name === diff);
}
/**
* Turns the difficulty of a song into a color
*
* @param diff the difficulty to get the color for
* @returns the color for the difficulty
*/
export function songDifficultyToColor(diff: string) {
return getDifficultyFromRawDifficulty(diff).color;
}