""" Wifipiano 2 This file has been written taking by reference code from osu-performance (https://github.com/ppy/osu-performance) by Tom94, licensed under the GNU AGPL 3 License. """ from common.constants import mods from common.log import logUtils as log from constants import exceptions from helpers import mapsHelper class piano: __slots__ = ["beatmap", "score", "pp"] def __init__(self, __beatmap, __score): self.beatmap = __beatmap self.score = __score self.pp = 0 self.getPP() def getPP(self): try: stars = self.beatmap.starsMania if stars == 0: # This beatmap can't be converted to mania raise exceptions.invalidBeatmapException() # Cache beatmap for cono mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID) mapsHelper.cacheMap(mapFile, self.beatmap) od = self.beatmap.OD objects = self.score.c50+self.score.c100+self.score.c300+self.score.cKatu+self.score.cGeki+self.score.cMiss score = self.score.score accuracy = self.score.accuracy scoreMods = self.score.mods log.debug("[WIFIPIANO2] SCORE DATA: Stars: {stars}, OD: {od}, obj: {objects}, score: {score}, acc: {acc}, mods: {mods}".format(stars=stars, od=od, objects=objects, score=score, acc=accuracy, mods=scoreMods)) # ---------- STRAIN PP # Scale score to mods multiplier scoreMultiplier = 1.0 # Doubles score if EZ/HT if scoreMods & mods.EASY != 0: scoreMultiplier *= 0.50 #if scoreMods & mods.HALFTIME != 0: # scoreMultiplier *= 0.50 # Calculate strain PP if scoreMultiplier <= 0: strainPP = 0 else: score *= int(1.0 / scoreMultiplier) strainPP = pow(5.0 * max(1.0, stars / 0.0825) - 4.0, 3.0) / 110000.0 strainPP *= 1 + 0.1 * min(1.0, float(objects) / 1500.0) if score <= 500000: strainPP *= (float(score) / 500000.0) * 0.1 elif score <= 600000: strainPP *= 0.1 + float(score - 500000) / 100000.0 * 0.2 elif score <= 700000: strainPP *= 0.3 + float(score - 600000) / 100000.0 * 0.35 elif score <= 800000: strainPP *= 0.65 + float(score - 700000) / 100000.0 * 0.20 elif score <= 900000: strainPP *= 0.85 + float(score - 800000) / 100000.0 * 0.1 else: strainPP *= 0.95 + float(score - 900000) / 100000.0 * 0.05 # ---------- ACC PP # Makes sure OD is in range 0-10. If this is done elsewhere, remove this. scrubbedOD = min(10.0, max(0, 10.0 - od)) # Old formula but done backwards. hitWindow300 = (34 + 3 * scrubbedOD) # Increases hitWindow if EZ is on if scoreMods & mods.EASY != 0: hitWindow300 *= 1.4 # Fiddles with DT and HT to make them match hitWindow300's ingame. if scoreMods & mods.DOUBLETIME != 0: hitWindow300 *= 1.5 elif scoreMods & mods.HALFTIME != 0: hitWindow300 *= 0.75 # makes hit window match what it is ingame. hitWindow300 = int(hitWindow300) + 0.5 if scoreMods & mods.DOUBLETIME != 0: hitWindow300 /= 1.5 elif scoreMods & mods.HALFTIME != 0: hitWindow300 /= 0.75 # Calculate accuracy PP accPP = pow((150.0 / hitWindow300) * pow(accuracy, 16), 1.8) * 2.5 accPP *= min(1.15, pow(float(objects) / 1500.0, 0.3)) # ---------- TOTAL PP multiplier = 1.1 if scoreMods & mods.NOFAIL != 0: multiplier *= 0.90 if scoreMods & mods.SPUNOUT != 0: multiplier *= 0.95 if scoreMods & mods.EASY != 0: multiplier *= 0.50 if scoreMods & mods.DOUBLETIME != 0: multiplier *= 2.45 if scoreMods & mods.HALFTIME != 0: multiplier *= 0.50 pp = pow(pow(strainPP, 1.1) + pow(accPP, 1.1), 1.0 / 1.1) * multiplier log.debug("[WIFIPIANO2] Calculated PP: {}".format(pp)) self.pp = pp except exceptions.invalidBeatmapException: log.warning("Invalid beatmap {}".format(self.beatmap.beatmapID)) self.pp = 0 finally: return self.pp