rg-stats
Version:
A library for calculating various rhythm game stats.
71 lines (57 loc) • 2.25 kB
text/typescript
import { ThrowIf } from "../util/throw-if";
import { integer } from "../util/types";
export type PopnLamps = "FAILED" | "EASY CLEAR" | "CLEAR" | "FULL COMBO" | "PERFECT";
/**
* Calculate the pop'n class points for an individual score.
*
* @param score - The score value. Between 0 and 100k.
* @param lamp - The lamp for this score.
* @param level - The level for this chart. Typically between 1 and 50,
* but the upper bound is not enforced here.
*/
export function calculate(score: integer, lamp: PopnLamps, level: integer) {
ThrowIf.negative(score, "Score cannot be negative.", { score });
ThrowIf(score > 100_000, "Score cannot be better than 100k.", { score });
ThrowIf.negative(level, "Chart level cannot be negative.", { level });
if (score <= 50000) {
return 0;
}
const clearBonus = GetClearBonus(lamp);
return (10_000 * level + score - 50_000 + clearBonus) / 5440;
}
function GetClearBonus(lamp: PopnLamps) {
if (lamp === "CLEAR" || lamp === "EASY CLEAR") {
return 3000;
} else if (lamp === "FULL COMBO" || lamp === "PERFECT") {
return 5000;
} else if (lamp === "FAILED") {
return 0;
}
throw new Error(
`Unknown lamp of ${lamp} passed to pop'n Class Points calculations. Expected any of FAILED, EASY CLEAR, CLEAR, FULL COMBO, PERFECT.`
);
}
/**
* Given a pop'n class points value, expected lamp and chart level, return the
* score necessary to get that amount of class points.
*
* @param classPoints - The class points to invert.
* @param lamp - The lamp to invert for. The lamp affects how much score is needed
* to achieve a given class points value.
* @param level - The level for the chart. Typically between 1 and 50,
* but the upper bound is not enforced here.
*/
export function inverse(classPoints: number, lamp: PopnLamps, level: integer) {
ThrowIf.negative(level, "Chart level cannot be negative.", { level });
if (classPoints === 0) {
return 0;
}
const clearBonus = GetClearBonus(lamp);
const expectedScore = 5440 * classPoints - 10_000 * level + 50_000 - clearBonus;
ThrowIf(
expectedScore > 100_000,
`${classPoints} class points is not achievable on a chart with level ${level} and lamp ${lamp}.`,
{ classPoints, level, lamp }
);
return Math.round(expectedScore);
}