diff --git a/app/api/v1/api.py b/app/api/v1/api.py index 60203d738..21537e81b 100644 --- a/app/api/v1/api.py +++ b/app/api/v1/api.py @@ -21,6 +21,8 @@ import app.usecases.performance from app.constants import regexes from app.constants.gamemodes import GameMode +from app.constants.level import get_level +from app.constants.level import get_level_precise from app.constants.mods import Mods from app.objects.beatmap import Beatmap from app.objects.beatmap import ensure_osu_file_is_available @@ -263,6 +265,14 @@ async def api_get_player_info( # extra fields are added to the api response "rank": rank + 1 if rank is not None else 0, "country_rank": country_rank + 1 if country_rank is not None else 0, + "level": get_level(int(mode_stats["tscore"])), + "level_progress": int( + ( + get_level_precise(mode_stats["tscore"]) + - get_level(mode_stats["tscore"]) + ) + * 100, + ), } return ORJSONResponse({"status": "success", "player": api_data}) diff --git a/app/api/v2/models/players.py b/app/api/v2/models/players.py index 3eb9cc6cb..ed85b36b1 100644 --- a/app/api/v2/models/players.py +++ b/app/api/v2/models/players.py @@ -58,3 +58,5 @@ class PlayerStats(BaseModel): sh_count: int s_count: int a_count: int + level: int + level_progress: int diff --git a/app/api/v2/players.py b/app/api/v2/players.py index 706077f3c..57df7004e 100644 --- a/app/api/v2/players.py +++ b/app/api/v2/players.py @@ -13,6 +13,8 @@ from app.api.v2.models.players import Player from app.api.v2.models.players import PlayerStats from app.api.v2.models.players import PlayerStatus +from app.constants.level import get_level +from app.constants.level import get_level_precise from app.repositories import stats as stats_repo from app.repositories import users as users_repo @@ -108,6 +110,13 @@ async def get_player_mode_stats( ) response = PlayerStats.from_mapping(data) + + # NOTE: kinda cursed, but that should do it + response.level = get_level(int(data["tscore"])) + response.level_progress = int( + (get_level_precise(data["tscore"]) - get_level(data["tscore"])) * 100, + ) + return responses.success(response) diff --git a/app/constants/level.py b/app/constants/level.py new file mode 100644 index 000000000..dd4e08d09 --- /dev/null +++ b/app/constants/level.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +import math + +LEVEL_GRAPH = [ + 0, + 30000, + 130000, + 340000, + 700000, + 1250000, + 2030000, + 3080000, + 4440000, + 6150000, + 8250000, + 10780000, + 13780000, + 17290000, + 21350000, + 26000000, + 31280000, + 37230000, + 43890000, + 51300000, + 59500000, + 68530000, + 78430000, + 89240000, + 101000000, + 113750000, + 127530000, + 142380000, + 158340000, + 175450000, + 193750000, + 213280000, + 234080000, + 256190000, + 279650000, + 304500000, + 330780000, + 358530000, + 387790000, + 418600000, + 451000000, + 485030000, + 520730000, + 558140000, + 597300000, + 638250000, + 681030000, + 725680000, + 772240000, + 820750000, + 871250000, + 923780000, + 978380000, + 1035090000, + 1093950000, + 1155000000, + 1218280000, + 1283830000, + 1351690001, + 1421900001, + 1494500002, + 1569530004, + 1647030007, + 1727040013, + 1809600024, + 1894750043, + 1982530077, + 2072980138, + 2166140248, + 2262050446, + 2360750803, + 2462281446, + 2566682603, + 2673994685, + 2784258433, + 2897515180, + 3013807324, + 3133179183, + 3255678529, + 3381359353, + 3510286835, + 3642546304, + 3778259346, + 3917612824, + 4060911082, + 4208669948, + 4361785907, + 4521840633, + 4691649139, + 4876246450, + 5084663609, + 5333124496, + 5650800094, + 6090166168, + 6745647103, + 7787174786, + 9520594614, + 12496396305, + 17705429349, + 26931190829, +] + + +def get_required_score_for_level(level: int) -> int: + if level <= 0: + return 0 + if level <= 100: + return LEVEL_GRAPH[level - 1] + return LEVEL_GRAPH[99] + 100000000000 * int(level - 100) + + +def get_level(score: int) -> int: + if score <= 0: + return 1 + + if score >= LEVEL_GRAPH[99]: + return 100 + int((score - LEVEL_GRAPH[99]) / 100000000000) + + for idx, v in enumerate(LEVEL_GRAPH, start=0): + if v > score: + return idx + + return 1 + + +def get_level_precise(score: int) -> float: + baseLevel = get_level(score) + baseLevelScore = get_required_score_for_level(baseLevel) + scoreProgress = score - baseLevelScore + scoreLevelDifference = get_required_score_for_level(baseLevel + 1) - baseLevelScore + + res = float(scoreProgress) / float(scoreLevelDifference) + float(baseLevel) + if math.isinf(res) or math.isnan(res): + return 0 + + return res