diff --git a/src/curve/BeatLeaderCurve.js b/src/curve/BeatLeaderCurve.js index 0ab9230..c68117c 100644 --- a/src/curve/BeatLeaderCurve.js +++ b/src/curve/BeatLeaderCurve.js @@ -1,53 +1,121 @@ import Utils from "../utils/utils"; /** - * I'm not even sure what this shit does, ask BL - * @see https://github.com/BeatLeader/beatleader-server/blob/16123a792b1a837faf6287e5bcd58e2e06e6a6f0/Utils/ReplayUtils.cs for more info + * The pp curve of this leaderboard + */ +const ppCurve = [ + [1.0, 7.424], + [0.999, 6.241], + [0.9975, 5.158], + [0.995, 4.01], + [0.9925, 3.241], + [0.99, 2.7], + [0.9875, 2.303], + [0.985, 2.007], + [0.9825, 1.786], + [0.98, 1.618], + [0.9775, 1.49], + [0.975, 1.392], + [0.9725, 1.315], + [0.97, 1.256], + [0.965, 1.167], + [0.96, 1.101], + [0.955, 1.047], + [0.95, 1.0], + [0.94, 0.919], + [0.93, 0.847], + [0.92, 0.786], + [0.91, 0.734], + [0.9, 0.692], + [0.875, 0.606], + [0.85, 0.537], + [0.825, 0.48], + [0.8, 0.429], + [0.75, 0.345], + [0.7, 0.286], + [0.65, 0.246], + [0.6, 0.217], + [0.0, 0.0], +]; + +/** + * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45 + * funky shit from ^ * - * @param {Number} acc the accuracy of the score - * @param {Number} stars the ranked star count + * @param {Number} accuracy the accuracy of the score + * @param {Number} accRating the acc rating of the difficulty + * @param {Number} passRating the pass rating of the difficulty + * @param {Number} techRating the tech rating of the difficulty * @returns */ -function curve(acc, stars) { - var l = 1 - (0.03 * (stars - 3.0)) / 11.0; - var a = 0.96 * l; - var f = 1.2 - (0.6 * stars) / 14.0; - return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f); +export function getBeatLeaderPP(accuracy, accRating, passRating, techRating) { + const modifierBonus = Utils.calculateModifierBonus(); + const ppValues = getPP( + accuracy, + accRating, + passRating * modifierBonus, + techRating * modifierBonus + ); + const pp = inflate(ppValues.passPP + ppValues.accPP + ppValues.techPP); + return isNaN(pp) ? 1024 : pp; } /** - * Gets the raw pp from the given score + * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45 + * funky shit from ^ * - * @param {Number} acc the accuracy of the score - * @param {Number} stars the ranked star count + * @param {Number} accuracy the accuracy of the score + * @param {Number} accRating the acc rating of the difficulty + * @param {Number} passRating the pass rating of the difficulty + * @param {Number} techRating the tech rating of the difficulty * @returns */ -export function getBeatLeaderPP(acc, stars) { - if (stars === undefined || acc === undefined) { - return undefined; +function getPP(accuracy, accRating, passRating, techRating) { + let passPP = 15.2 * Math.exp(Math.pow(passRating, 1 / 2.62)) - 30; + if (isNaN(passPP) || passPP == Infinity) { + passPP = 0; } - const modifierBonus = Utils.calculateModifierBonus(); - let rawPP = curve(acc, stars - 0.5) * (stars + 0.5) * 42; - let fullPP = - curve(acc, stars * modifierBonus - 0.5) * - (stars * modifierBonus + 0.5) * - 42; + let accPP = curve(accuracy) * accRating * 34; + let techPP = Math.exp(1.9 * accuracy) * techRating; - const isNegativeAcc = acc < 0; - if (isNegativeAcc) { - acc *= -1; - } - - if (isNaN(rawPP) || rawPP == Infinity) { - return 1024; - } - if (isNaN(fullPP) || fullPP == Infinity) { - return 1024; - } - - if (isNegativeAcc) { - fullPP *= -1; - } - - return fullPP; + return { + passPP: passPP, + accPP: accPP, + techPP: techPP, + }; +} + +/** + * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45 + * funky shit from ^ + * + * @param {Number} acc the accuracy of the score + * @returns something + */ +function curve(acc) { + let i = 0; + for (; i < ppCurve.length; i++) { + if (ppCurve[i][0] <= acc) { + break; + } + } + + if (i == 0) { + i = 1; + } + + const middle_dis = + (acc - ppCurve[i - 1][0]) / (ppCurve[i][0] - ppCurve[i - 1][0]); + return ppCurve[i - 1][1] + middle_dis * (ppCurve[i][1] - ppCurve[i - 1][1]); +} + +/** + * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L77 + * funky shit from ^ + * + * @param {Number} peepee + * @returns funny number, idk + */ +function inflate(peepee) { + return (650 * Math.pow(peepee, 1.3)) / Math.pow(650, 1.3); } diff --git a/src/pages/api/beatleader/data.js b/src/pages/api/beatleader/data.js index abfa5fc..cbc4eb5 100644 --- a/src/pages/api/beatleader/data.js +++ b/src/pages/api/beatleader/data.js @@ -33,6 +33,9 @@ export default async function handler(req, res) { difficulty: difficulty, stars: json.stars, modifiers: json.modifiers, + passRating: json.passRating, + accRating: json.accRating, + techRating: json.techRating, }); } @@ -54,6 +57,10 @@ export default async function handler(req, res) { const json = reesponse.data; let starCount = undefined; let modifiers = undefined; + let passRating = undefined; + let accRating = undefined; + let techRating = undefined; + for (const diff of json.difficulties) { if ( diff.difficultyName === difficulty && @@ -61,6 +68,9 @@ export default async function handler(req, res) { ) { starCount = diff.stars; modifiers = diff.modifierValues; + passRating = diff.passRating; + accRating = diff.accRating; + techRating = diff.techRating; } } if (starCount === undefined) { @@ -74,6 +84,9 @@ export default async function handler(req, res) { JSON.stringify({ stars: starCount, modifiers: modifiers, + passRating: passRating, + accRating: accRating, + techRating: techRating, }) ); console.log( @@ -87,5 +100,8 @@ export default async function handler(req, res) { difficulty: difficulty, stars: starCount, modifiers: modifiers, + passRating: passRating, + accRating: accRating, + techRating: techRating, }); } diff --git a/src/store/songDataStore.ts b/src/store/songDataStore.ts index 0b3c996..b7084ed 100644 --- a/src/store/songDataStore.ts +++ b/src/store/songDataStore.ts @@ -20,6 +20,9 @@ interface SongDataState { beatleader: { stars: Number | undefined; modifiers: Object; + passRating: number | undefined; + accRating: number | undefined; + techRating: number | undefined; }; }; mapArt: string; @@ -82,6 +85,9 @@ export const useSongDataStore = create()((set) => ({ beatleader: { stars: 0, modifiers: {}, + passRating: undefined, + accRating: undefined, + techRating: undefined, }, }, mapArt: "", @@ -134,6 +140,16 @@ export const useSongDataStore = create()((set) => ({ } const { bsr, mapArt } = mapDataresponse.data.data; + console.log({ + beatleader: { + stars: beatLeaderLeaderboardData.stars, + modifiers: beatLeaderLeaderboardData.modifiers, + passRating: beatLeaderLeaderboardData.passRating, + accRating: beatLeaderLeaderboardData.accRating, + techRating: beatLeaderLeaderboardData.techRating, + }, + }); + set({ isLoading: false, hasError: hasError, @@ -141,6 +157,9 @@ export const useSongDataStore = create()((set) => ({ beatleader: { stars: beatLeaderLeaderboardData.stars, modifiers: beatLeaderLeaderboardData.modifiers, + passRating: beatLeaderLeaderboardData.passRating, + accRating: beatLeaderLeaderboardData.accRating, + techRating: beatLeaderLeaderboardData.techRating, }, scoresaber: { stars: scoreSaberLeaderboardData.stars, @@ -244,6 +263,9 @@ export const useSongDataStore = create()((set) => ({ beatleader: { stars: undefined, modifiers: {}, + passRating: undefined, + accRating: undefined, + techRating: undefined, }, }, mapArt: "", diff --git a/src/utils/utils.js b/src/utils/utils.js index f3d262c..df0a94e 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -37,7 +37,15 @@ export default class Utils { return undefined; } if (type === "BeatLeader") { - return getBeatLeaderPP(acc, stars); + const leaderboardData = + useSongDataStore.getState().mapLeaderboardData.beatleader; + + return getBeatLeaderPP( + acc, + leaderboardData.accRating, + leaderboardData.passRating, + leaderboardData.techRating + ); } if (type === "ScoreSaber") { return getScoreSaberPP(acc, stars); @@ -48,7 +56,7 @@ export default class Utils { static calculateModifierBonus() { const songMods = useSongDataStore.getState().songModifiers; const modifierMulipliers = - useSongDataStore.getState().mapLeaderboardData.modifiers; + useSongDataStore.getState().mapLeaderboardData.beatleader.modifiers; let bonus = 1; // No Fail