Files
g0v0-server/app/achievements/skill.py
2025-08-22 08:21:52 +00:00

465 lines
15 KiB
Python

from __future__ import annotations
from functools import partial
from typing import Literal, cast
from app.database.beatmap import calculate_beatmap_attributes
from app.database.score import Beatmap, Score
from app.dependencies.database import get_redis
from app.dependencies.fetcher import get_fetcher
from app.models.achievement import Achievement, Medals
from app.models.mods import API_MODS, mods_can_get_pp_vanilla
from app.models.score import GameMode
from sqlmodel.ext.asyncio.session import AsyncSession
async def process_skill(
target_gamemode: GameMode,
star: int,
type: Literal["pass", "fc"],
session: AsyncSession,
score: Score,
beatmap: Beatmap,
) -> bool:
if target_gamemode != score.gamemode:
return False
ruleset_id = int(score.gamemode)
if not score.passed:
return False
if not beatmap.beatmap_status.has_pp():
return False
if not mods_can_get_pp_vanilla(ruleset_id, score.mods):
return False
difficulty_reduction_mods = [
mod["Acronym"]
for mod in API_MODS[cast(Literal[0, 1, 2, 3], ruleset_id)].values()
if mod["Type"] == "DifficultyReduction"
]
for mod in score.mods:
if mod["acronym"] in difficulty_reduction_mods:
return False
fetcher = await get_fetcher()
redis = get_redis()
mods_ = score.mods.copy()
mods_.sort(key=lambda x: x["acronym"])
attribute = await calculate_beatmap_attributes(beatmap.id, score.gamemode, mods_, redis, fetcher)
if attribute.star_rating < star or attribute.star_rating >= star + 1:
return False
if type == "fc" and not score.is_perfect_combo:
return False
return True
MEDALS: Medals = {
Achievement(
id=1,
name="Rising Star",
desc="Can't go forward without the first steps.",
assets_id="osu-skill-pass-1",
): partial(process_skill, GameMode.OSU, 1, "pass"),
Achievement(
id=2,
name="Constellation Prize",
desc="Definitely not a consolation prize. Now things start getting hard!",
assets_id="osu-skill-pass-2",
): partial(process_skill, GameMode.OSU, 2, "pass"),
Achievement(
id=3,
name="Building Confidence",
desc="Oh, you've SO got this.",
assets_id="osu-skill-pass-3",
): partial(process_skill, GameMode.OSU, 3, "pass"),
Achievement(
id=4,
name="Insanity Approaches",
desc="You're not twitching, you're just ready.",
assets_id="osu-skill-pass-4",
): partial(process_skill, GameMode.OSU, 4, "pass"),
Achievement(
id=5,
name="These Clarion Skies",
desc="Everything seems so clear now.",
assets_id="osu-skill-pass-5",
): partial(process_skill, GameMode.OSU, 5, "pass"),
Achievement(
id=6,
name="Above and Beyond",
desc="A cut above the rest.",
assets_id="osu-skill-pass-6",
): partial(process_skill, GameMode.OSU, 6, "pass"),
Achievement(
id=7,
name="Supremacy",
desc="All marvel before your prowess.",
assets_id="osu-skill-pass-7",
): partial(process_skill, GameMode.OSU, 7, "pass"),
Achievement(
id=8,
name="Absolution",
desc="My god, you're full of stars!",
assets_id="osu-skill-pass-8",
): partial(process_skill, GameMode.OSU, 8, "pass"),
Achievement(
id=9,
name="Event Horizon",
desc="No force dares to pull you under.",
assets_id="osu-skill-pass-9",
): partial(process_skill, GameMode.OSU, 9, "pass"),
Achievement(
id=10,
name="Phantasm",
desc="Fevered is your passion, extraordinary is your skill.",
assets_id="osu-skill-pass-10",
): partial(process_skill, GameMode.OSU, 10, "pass"),
Achievement(
id=11,
name="Totality",
desc="All the notes. Every single one.",
assets_id="osu-skill-fc-1",
): partial(process_skill, GameMode.OSU, 1, "fc"),
Achievement(
id=12,
name="Business As Usual",
desc="Two to go, please.",
assets_id="osu-skill-fc-2",
): partial(process_skill, GameMode.OSU, 2, "fc"),
Achievement(
id=13,
name="Building Steam",
desc="Hey, this isn't so bad.",
assets_id="osu-skill-fc-3",
): partial(process_skill, GameMode.OSU, 3, "fc"),
Achievement(
id=14,
name="Moving Forward",
desc="Bet you feel good about that.",
assets_id="osu-skill-fc-4",
): partial(process_skill, GameMode.OSU, 4, "fc"),
Achievement(
id=15,
name="Paradigm Shift",
desc="Surprisingly difficult.",
assets_id="osu-skill-fc-5",
): partial(process_skill, GameMode.OSU, 5, "fc"),
Achievement(
id=16,
name="Anguish Quelled",
desc="Don't choke.",
assets_id="osu-skill-fc-6",
): partial(process_skill, GameMode.OSU, 6, "fc"),
Achievement(
id=17,
name="Never Give Up",
desc="Excellence is its own reward.",
assets_id="osu-skill-fc-7",
): partial(process_skill, GameMode.OSU, 7, "fc"),
Achievement(
id=18,
name="Aberration",
desc="They said it couldn't be done. They were wrong.",
assets_id="osu-skill-fc-8",
): partial(process_skill, GameMode.OSU, 8, "fc"),
Achievement(
id=19,
name="Chosen",
desc="Reign among the Prometheans, where you belong.",
assets_id="osu-skill-fc-9",
): partial(process_skill, GameMode.OSU, 9, "fc"),
Achievement(
id=20,
name="Unfathomable",
desc="You have no equal.",
assets_id="osu-skill-fc-10",
): partial(process_skill, GameMode.OSU, 10, "fc"),
Achievement(
id=25,
name="My First Don",
desc="Marching to the beat of your own drum. Literally.",
assets_id="taiko-skill-pass-1",
): partial(process_skill, GameMode.TAIKO, 1, "pass"),
Achievement(
id=26,
name="Katsu Katsu Katsu",
desc="Hora! Izuko!",
assets_id="taiko-skill-pass-2",
): partial(process_skill, GameMode.TAIKO, 2, "pass"),
Achievement(
id=27,
name="Not Even Trying",
desc="Muzukashii? Not even.",
assets_id="taiko-skill-pass-3",
): partial(process_skill, GameMode.TAIKO, 3, "pass"),
Achievement(
id=28,
name="Face Your Demons",
desc="The first trials are now behind you, but are you a match for the Oni?",
assets_id="taiko-skill-pass-4",
): partial(process_skill, GameMode.TAIKO, 4, "pass"),
Achievement(
id=29,
name="The Demon Within",
desc="No rest for the wicked.",
assets_id="taiko-skill-pass-5",
): partial(process_skill, GameMode.TAIKO, 5, "pass"),
Achievement(
id=30,
name="Drumbreaker",
desc="Too strong.",
assets_id="taiko-skill-pass-6",
): partial(process_skill, GameMode.TAIKO, 6, "pass"),
Achievement(
id=31,
name="The Godfather",
desc="You are the Don of Dons.",
assets_id="taiko-skill-pass-7",
): partial(process_skill, GameMode.TAIKO, 7, "pass"),
Achievement(
id=32,
name="Rhythm Incarnate",
desc="Feel the beat. Become the beat.",
assets_id="taiko-skill-pass-8",
): partial(process_skill, GameMode.TAIKO, 8, "pass"),
Achievement(
id=33,
name="Keeping Time",
desc="Don, then katsu. Don, then katsu..",
assets_id="taiko-skill-fc-1",
): partial(process_skill, GameMode.TAIKO, 1, "fc"),
Achievement(
id=34,
name="To Your Own Beat",
desc="Straight and steady.",
assets_id="taiko-skill-fc-2",
): partial(process_skill, GameMode.TAIKO, 2, "fc"),
Achievement(
id=35,
name="Big Drums",
desc="Bigger scores to match.",
assets_id="taiko-skill-fc-3",
): partial(process_skill, GameMode.TAIKO, 3, "fc"),
Achievement(
id=36,
name="Adversity Overcome",
desc="Difficult? Not for you.",
assets_id="taiko-skill-fc-4",
): partial(process_skill, GameMode.TAIKO, 4, "fc"),
Achievement(
id=37,
name="Demonslayer",
desc="An Oni felled forevermore.",
assets_id="taiko-skill-fc-5",
): partial(process_skill, GameMode.TAIKO, 5, "fc"),
Achievement(
id=38,
name="Rhythm's Call",
desc="Heralding true skill.",
assets_id="taiko-skill-fc-6",
): partial(process_skill, GameMode.TAIKO, 6, "fc"),
Achievement(
id=39,
name="Time Everlasting",
desc="Not a single beat escapes you.",
assets_id="taiko-skill-fc-7",
): partial(process_skill, GameMode.TAIKO, 7, "fc"),
Achievement(
id=40,
name="The Drummer's Throne",
desc="Percussive brilliance befitting royalty alone.",
assets_id="taiko-skill-fc-8",
): partial(process_skill, GameMode.TAIKO, 8, "fc"),
Achievement(
id=41,
name="A Slice Of Life",
desc="Hey, this fruit catching business isn't bad.",
assets_id="fruits-skill-pass-1",
): partial(process_skill, GameMode.FRUITS, 1, "pass"),
Achievement(
id=42,
name="Dashing Ever Forward",
desc="Fast is how you do it.",
assets_id="fruits-skill-pass-2",
): partial(process_skill, GameMode.FRUITS, 2, "pass"),
Achievement(
id=43,
name="Zesty Disposition",
desc="No scurvy for you, not with that much fruit.",
assets_id="fruits-skill-pass-3",
): partial(process_skill, GameMode.FRUITS, 3, "pass"),
Achievement(
id=44,
name="Hyperdash ON!",
desc="Time and distance is no obstacle to you.",
assets_id="fruits-skill-pass-4",
): partial(process_skill, GameMode.FRUITS, 4, "pass"),
Achievement(
id=45,
name="It's Raining Fruit",
desc="And you can catch them all.",
assets_id="fruits-skill-pass-5",
): partial(process_skill, GameMode.FRUITS, 5, "pass"),
Achievement(
id=46,
name="Fruit Ninja",
desc="Legendary techniques.",
assets_id="fruits-skill-pass-6",
): partial(process_skill, GameMode.FRUITS, 6, "pass"),
Achievement(
id=47,
name="Dreamcatcher",
desc="No fruit, only dreams now.",
assets_id="fruits-skill-pass-7",
): partial(process_skill, GameMode.FRUITS, 7, "pass"),
Achievement(
id=48,
name="Lord of the Catch",
desc="Your kingdom kneels before you.",
assets_id="fruits-skill-pass-8",
): partial(process_skill, GameMode.FRUITS, 8, "pass"),
Achievement(
id=49,
name="Sweet And Sour",
desc="Apples and oranges, literally.",
assets_id="fruits-skill-fc-1",
): partial(process_skill, GameMode.FRUITS, 1, "fc"),
Achievement(
id=50,
name="Reaching The Core",
desc="The seeds of future success.",
assets_id="fruits-skill-fc-2",
): partial(process_skill, GameMode.FRUITS, 2, "fc"),
Achievement(
id=51,
name="Clean Platter",
desc="Clean only of failure. It is completely full, otherwise.",
assets_id="fruits-skill-fc-3",
): partial(process_skill, GameMode.FRUITS, 3, "fc"),
Achievement(
id=52,
name="Between The Rain",
desc="No umbrella needed.",
assets_id="fruits-skill-fc-4",
): partial(process_skill, GameMode.FRUITS, 4, "fc"),
Achievement(
id=53,
name="Addicted",
desc="That was an overdose?",
assets_id="fruits-skill-fc-5",
): partial(process_skill, GameMode.FRUITS, 5, "fc"),
Achievement(
id=54,
name="Quickening",
desc="A dash above normal limits.",
assets_id="fruits-skill-fc-6",
): partial(process_skill, GameMode.FRUITS, 6, "fc"),
Achievement(
id=55,
name="Supersonic",
desc="Faster than is reasonably necessary.",
assets_id="fruits-skill-fc-7",
): partial(process_skill, GameMode.FRUITS, 7, "fc"),
Achievement(
id=56,
name="Dashing Scarlet",
desc="Speed beyond mortal reckoning.",
assets_id="fruits-skill-fc-8",
): partial(process_skill, GameMode.FRUITS, 8, "fc"),
Achievement(
id=57,
name="First Steps",
desc="It isn't 9-to-5, but 1-to-9. Keys, that is.",
assets_id="mania-skill-pass-1",
): partial(process_skill, GameMode.MANIA, 1, "pass"),
Achievement(
id=58,
name="No Normal Player",
desc="Not anymore, at least.",
assets_id="mania-skill-pass-2",
): partial(process_skill, GameMode.MANIA, 2, "pass"),
Achievement(
id=59,
name="Impulse Drive",
desc="Not quite hyperspeed, but getting close.",
assets_id="mania-skill-pass-3",
): partial(process_skill, GameMode.MANIA, 3, "pass"),
Achievement(
id=60,
name="Hyperspeed",
desc="Woah.",
assets_id="mania-skill-pass-4",
): partial(process_skill, GameMode.MANIA, 4, "pass"),
Achievement(
id=61,
name="Ever Onwards",
desc="Another challenge is just around the corner.",
assets_id="mania-skill-pass-5",
): partial(process_skill, GameMode.MANIA, 5, "pass"),
Achievement(
id=62,
name="Another Surpassed",
desc="Is there no limit to your skills?",
assets_id="mania-skill-pass-6",
): partial(process_skill, GameMode.MANIA, 6, "pass"),
Achievement(
id=63,
name="Extra Credit",
desc="See me after class.",
assets_id="mania-skill-pass-7",
): partial(process_skill, GameMode.MANIA, 7, "pass"),
Achievement(
id=64,
name="Maniac",
desc="There's just no stopping you.",
assets_id="mania-skill-pass-8",
): partial(process_skill, GameMode.MANIA, 8, "pass"),
Achievement(
id=65,
name="Keystruck",
desc="The beginning of a new story",
assets_id="mania-skill-fc-1",
): partial(process_skill, GameMode.MANIA, 1, "fc"),
Achievement(
id=66,
name="Keying In",
desc="Finding your groove.",
assets_id="mania-skill-fc-2",
): partial(process_skill, GameMode.MANIA, 2, "fc"),
Achievement(
id=67,
name="Hyperflow",
desc="You can *feel* the rhythm.",
assets_id="mania-skill-fc-3",
): partial(process_skill, GameMode.MANIA, 3, "fc"),
Achievement(
id=68,
name="Breakthrough",
desc="Many skills mastered, rolled into one.",
assets_id="mania-skill-fc-4",
): partial(process_skill, GameMode.MANIA, 4, "fc"),
Achievement(
id=69,
name="Everything Extra",
desc="Giving your all is giving everything you have.",
assets_id="mania-skill-fc-5",
): partial(process_skill, GameMode.MANIA, 5, "fc"),
Achievement(
id=70,
name="Level Breaker",
desc="Finesse beyond reason",
assets_id="mania-skill-fc-6",
): partial(process_skill, GameMode.MANIA, 6, "fc"),
Achievement(
id=71,
name="Step Up",
desc="A precipice rarely seen.",
assets_id="mania-skill-fc-7",
): partial(process_skill, GameMode.MANIA, 7, "fc"),
Achievement(
id=72,
name="Behind The Veil",
desc="Supernatural!",
assets_id="mania-skill-fc-8",
): partial(process_skill, GameMode.MANIA, 8, "fc"),
}