UNPKG

osu-pp-calculator

Version:

calculates performance points for given beatmap

129 lines (128 loc) 6.24 kB
"use strict"; var mods_1 = require('./mods'); var diff_calc_1 = require('./diff-calc'); var beatmap_1 = require('./beatmap'); exports.Beatmap = beatmap_1.default; var PPCalculator; (function (PPCalculator) { // turns the beatmaps' strain attributes into a larger value, suitable // for pp calc. not 100% sure what is going on here, but it probably makes // strain values scale a bit exponentially. var calculateBaseStrain = function (strain) { return Math.pow(5.0 * Math.max(1.0, strain * 14.8148148) - 4.0, 3.0) * 0.00001; }; var accuracyCalc = function (c300, c100, c50, misses) { var totalHits = c300 + c100 + c50 + misses; var accuracy = 0.0; if (totalHits > 0) { accuracy = (c50 * 50.0 + c100 * 100.0 + c300 * 300.0) / (totalHits * 300.0); } return accuracy; }; var calc100Count = function (accuracy, maxHits, misses) { return Math.round(-3 / 2 * ((accuracy - 1) * maxHits + misses)); }; function calculate(beatmap, accuracyPercent, modifiers, combo, misses, scoreVersion) { if (accuracyPercent === void 0) { accuracyPercent = 100; } if (modifiers === void 0) { modifiers = mods_1.default.None; } if (combo === void 0) { combo = -1; } if (misses === void 0) { misses = -1; } if (scoreVersion === void 0) { scoreVersion = 1; } beatmap.applyMods(modifiers); var diff = diff_calc_1.default.calculate(beatmap); accuracyPercent = Math.max(0.0, Math.min(100.0, accuracyPercent)); var c100 = calc100Count(accuracyPercent / 100.0, beatmap.hitObjects.length, misses); var c300 = beatmap.hitObjects.length - c100; return calculateWithCounts(diff.aim, diff.speed, beatmap, modifiers, combo, misses, c300, c100, 0, scoreVersion); } PPCalculator.calculate = calculate; function calculateWithCounts(aim, speed, beatmap, modifiers, combo, misses, c300, c100, c50, scoreVersion) { if (modifiers === void 0) { modifiers = mods_1.default.None; } if (combo === void 0) { combo = -1; } if (misses === void 0) { misses = 0; } if (c300 === void 0) { c300 = -1; } if (c100 === void 0) { c100 = 0; } if (c50 === void 0) { c50 = 0; } if (scoreVersion === void 0) { scoreVersion = 1; } if (!beatmap.combo) throw new Error("Max combo cannot be zero"); if (scoreVersion != 1 && scoreVersion != 2) throw new Error("This score version does not exist or isn't supported"); var overallDifficulty = beatmap.overallDifficulty; var approachRate = beatmap.approachRate; var circles = beatmap.circleCount; if (c300 <= 0) c300 = beatmap.hitObjects.length - c100 - c50 - misses; combo = combo <= 0 ? beatmap.combo : combo; var totalHits = c300 + c100 + c50 + misses; // accuracy (not in percentage, ranges between 0 and 1) var accuracy = accuracyCalc(c300, c100, c50, misses); // length bonus (reused in speed pp) var totalHitsOver2k = totalHits / 2000.0; var lengthBonus = 0.95 + 0.4 * Math.min(1.0, totalHitsOver2k) + (totalHits > 2000 ? Math.log(totalHitsOver2k) / Math.LN10 * 0.5 : 0.0); // miss penality (reused in speed pp) var missPenalty = Math.pow(0.97, misses); // combo break penality (reused in speed pp) var comboBreakPenalty = Math.pow(combo, 0.8) / Math.pow(beatmap.combo, 0.8); var approachRateBonus = 1.0; // high ar bonus if (approachRate > 10.33) { approachRateBonus += 0.45 * (approachRate - 10.33); } else if (approachRate < 8.0) { var lowArBonus = 0.01 * (8.0 - approachRate); if (modifiers & mods_1.default.Hidden) { lowArBonus *= 2.0; } approachRateBonus += lowArBonus; } // accuracy bonus (bad aim can lead to bad accuracy, reused in speed for same reason) var accuracyBonus = 0.5 + accuracy / 2.0; // od bonus (low od is easy to accuracy even with shit aim, reused in speed ...) var overallDifficultyBonus = 0.98 + Math.pow(overallDifficulty, 2) / 2500.0; var aimValue = calculateBaseStrain(aim) * lengthBonus * approachRateBonus * accuracyBonus * overallDifficultyBonus * missPenalty * comboBreakPenalty * (modifiers & mods_1.default.Hidden ? 1.18 : 1) * (modifiers & mods_1.default.Flashlight ? 1.45 * lengthBonus : 1); var speedValue = calculateBaseStrain(speed) * lengthBonus * missPenalty * comboBreakPenalty * accuracyBonus * overallDifficultyBonus; var realAccuracy = 0.0; // accuracy calculation changes from scorev1 to scorev2 if (scoreVersion == 2) { circles = totalHits; realAccuracy = accuracy; } else { // scorev1 ignores sliders since they are free 300s if (circles) { realAccuracy = ((c300 - (totalHits - circles)) * 300.0 + c100 * 100.0 + c50 * 50.0) / (circles * 300); } // can go negative if we miss everything realAccuracy = Math.max(0.0, realAccuracy); } // arbitrary values tom crafted out of trial and error var accuracyValue = Math.pow(1.52163, overallDifficulty) * Math.pow(realAccuracy, 24.0) * 2.83 * Math.min(1.15, Math.pow(circles / 1000.0, 0.3)) * (modifiers & mods_1.default.Hidden ? 1.02 : 1) * (modifiers & mods_1.default.Flashlight ? 1.02 : 1); var finalMultiplier = 1.12 * (modifiers & mods_1.default.NoFail ? 0.90 : 1) * (modifiers & mods_1.default.SpunOut ? 0.95 : 1); return Math.pow(Math.pow(aimValue, 1.1) + Math.pow(speedValue, 1.1) + Math.pow(accuracyValue, 1.1), 1.0 / 1.1) * finalMultiplier; } PPCalculator.calculateWithCounts = calculateWithCounts; })(PPCalculator || (PPCalculator = {})); exports.PPCalculator = PPCalculator; ;