122 lines
3.1 KiB
JavaScript
122 lines
3.1 KiB
JavaScript
import createScoresService from "./scores";
|
|
import makePendingPromisePool from "../../utils/pending-promises";
|
|
import { getTotalPpFromSortedPps } from "../../utils/scoresaber/pp";
|
|
|
|
let service = null;
|
|
let serviceCreationCount = 0;
|
|
export default () => {
|
|
serviceCreationCount++;
|
|
if (service) return service;
|
|
|
|
const scoresService = createScoresService();
|
|
|
|
const resolvePromiseOrWaitForPending = makePendingPromisePool();
|
|
|
|
const getTotalPp = (scores) =>
|
|
scores && Array.isArray(scores)
|
|
? getTotalPpFromSortedPps(
|
|
scores
|
|
.filter((s) => s.pp > 0)
|
|
.map((s) => s.pp)
|
|
.sort((a, b) => b - a)
|
|
)
|
|
: null;
|
|
|
|
const getTotalPlayerPp = async (playerId, modifiedScores = {}) =>
|
|
getTotalPp(
|
|
Object.values({
|
|
...(await resolvePromiseOrWaitForPending(`scores/${playerId}`, () =>
|
|
scoresService.getPlayerScoresAsObject(playerId)
|
|
)),
|
|
...modifiedScores,
|
|
})
|
|
);
|
|
|
|
async function getWhatIfScore(playerId, leaderboardId, pp = 0) {
|
|
const currentTotalPp = await getTotalPlayerPp(playerId);
|
|
if (!currentTotalPp) return null;
|
|
|
|
const newTotalPp = await getTotalPlayerPp(playerId, {
|
|
[leaderboardId]: { pp },
|
|
});
|
|
|
|
return {
|
|
currentTotalPp,
|
|
newTotalPp,
|
|
diff: newTotalPp - currentTotalPp,
|
|
};
|
|
}
|
|
|
|
// written by BaliBalo: https://github.com/BaliBalo/ScoreSaber/blob/master/pages/peepee.js
|
|
const PP_PER_STAR = 42.114296;
|
|
const ppCurve = [
|
|
{ at: 0, value: 0 },
|
|
{ at: 45, value: 0.015 },
|
|
{ at: 50, value: 0.03 },
|
|
{ at: 55, value: 0.06 },
|
|
{ at: 60, value: 0.105 },
|
|
{ at: 65, value: 0.16 },
|
|
{ at: 68, value: 0.24 },
|
|
{ at: 70, value: 0.285 },
|
|
{ at: 80, value: 0.563 },
|
|
{ at: 84, value: 0.695 },
|
|
{ at: 88, value: 0.826 },
|
|
{ at: 94.5, value: 1.015 },
|
|
{ at: 95, value: 1.046 },
|
|
{ at: 100, value: 1.12 },
|
|
{ at: 110, value: 1.18 },
|
|
{ at: 114, value: 1.25 },
|
|
];
|
|
function ppFactorFromAcc(acc) {
|
|
if (!acc || acc <= 0) {
|
|
return 0;
|
|
}
|
|
let index = ppCurve.findIndex((o) => o.at >= acc);
|
|
if (index === -1) {
|
|
return ppCurve[ppCurve.length - 1].value;
|
|
}
|
|
if (!index) {
|
|
return ppCurve[0].value;
|
|
}
|
|
let from = ppCurve[index - 1];
|
|
let to = ppCurve[index];
|
|
let progress = (acc - from.at) / (to.at - from.at);
|
|
return from.value + (to.value - from.value) * progress;
|
|
}
|
|
|
|
function accFromPpFactor(ppFactor) {
|
|
if (!ppFactor || ppFactor <= 0) return 0;
|
|
|
|
const idx = ppCurve.findIndex((o) => o.value >= ppFactor);
|
|
if (idx < 0) return ppCurve[ppCurve.length - 1].at;
|
|
|
|
const from = ppCurve[idx - 1];
|
|
const to = ppCurve[idx];
|
|
const progress = (ppFactor - from.value) / (to.value - from.value);
|
|
|
|
return from.at + (to.at - from.at) * progress;
|
|
}
|
|
|
|
const destroyService = () => {
|
|
serviceCreationCount--;
|
|
|
|
if (serviceCreationCount === 0) {
|
|
scoresService.destroyService();
|
|
|
|
service = null;
|
|
}
|
|
};
|
|
|
|
service = {
|
|
getWhatIfScore,
|
|
getTotalPlayerPp,
|
|
getTotalPp,
|
|
ppFactorFromAcc,
|
|
accFromPpFactor,
|
|
PP_PER_STAR,
|
|
destroyService,
|
|
};
|
|
|
|
return service;
|
|
};
|