117 lines
3.0 KiB
JavaScript
117 lines
3.0 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;
|
||
|
}
|