1
0
mirror of synced 2024-12-24 19:54:50 +01:00
bemaniutils/bemani/frontend/ddr/ddr.py

290 lines
12 KiB
Python

# vim: set fileencoding=utf-8
import copy
from typing import Any, Dict, Iterator, List, Tuple
from bemani.backend.ddr import DDRFactory, DDRBase
from bemani.common import Profile, ValidatedDict, GameConstants, VersionConstants
from bemani.data import Attempt, Link, RemoteUser, Score, Song, UserID
from bemani.frontend.base import FrontendBase
class DDRFrontend(FrontendBase):
game: GameConstants = GameConstants.DDR
version: int = 0 # We use a virtual version for DDR to tie charts together
valid_charts: List[int] = [
DDRBase.CHART_SINGLE_BEGINNER,
DDRBase.CHART_SINGLE_BASIC,
DDRBase.CHART_SINGLE_DIFFICULT,
DDRBase.CHART_SINGLE_EXPERT,
DDRBase.CHART_SINGLE_CHALLENGE,
DDRBase.CHART_DOUBLE_BASIC,
DDRBase.CHART_DOUBLE_DIFFICULT,
DDRBase.CHART_DOUBLE_EXPERT,
DDRBase.CHART_DOUBLE_CHALLENGE,
]
valid_rival_types: List[str] = [f"friend_{i}" for i in range(10)]
max_active_rivals: Dict[int, int] = {
VersionConstants.DDR_X2: 1,
VersionConstants.DDR_X3_VS_2NDMIX: 3,
VersionConstants.DDR_2013: 3,
VersionConstants.DDR_2014: 3,
VersionConstants.DDR_ACE: 3,
VersionConstants.DDR_A20: 3,
}
def all_games(self) -> Iterator[Tuple[GameConstants, int, str]]:
yield from DDRFactory.all_games()
def update_name(self, profile: Profile, name: str) -> Profile:
newprofile = copy.deepcopy(profile)
newprofile.replace_str("name", name)
return newprofile
def update_weight(self, profile: Profile, weight: int, enabled: bool) -> Profile:
newprofile = copy.deepcopy(profile)
if newprofile.version in (VersionConstants.DDR_ACE, VersionConstants.DDR_A20):
if enabled:
newprofile.replace_int("weight", weight)
newprofile.replace_bool("workout_mode", True)
else:
newprofile.replace_int("weight", 0)
newprofile.replace_bool("workout_mode", False)
else:
if enabled:
newprofile.replace_int("weight", weight)
else:
if "weight" in newprofile:
del newprofile["weight"]
return newprofile
def update_early_late(self, profile: Profile, display_early_late: bool) -> Profile:
newprofile = copy.deepcopy(profile)
newprofile.replace_int("early_late", 1 if display_early_late else 0)
return newprofile
def update_background_combo(
self, profile: Profile, background_combo: bool
) -> Profile:
newprofile = copy.deepcopy(profile)
newprofile.replace_int("combo", 1 if background_combo else 0)
return newprofile
def update_settings(
self, profile: Profile, new_settings: Dict[str, Any]
) -> Profile:
newprofile = copy.deepcopy(profile)
if newprofile.version in (VersionConstants.DDR_ACE, VersionConstants.DDR_A20):
newprofile.replace_int("arrowskin", new_settings["arrowskin"])
newprofile.replace_int("guidelines", new_settings["guidelines"])
newprofile.replace_int("filter", new_settings["filter"])
newprofile.replace_int("character", new_settings["character"])
else:
# No other versions have extra options yet.
pass
return newprofile
def format_profile(
self, profile: Profile, playstats: ValidatedDict
) -> Dict[str, Any]:
formatted_profile = super().format_profile(profile, playstats)
if profile.version in (VersionConstants.DDR_ACE, VersionConstants.DDR_A20):
formatted_profile.update(
{
"sp": playstats.get_int("single_plays"),
"dp": playstats.get_int("double_plays"),
"early_late": profile.get_int("early_late") != 0,
"background_combo": profile.get_int("combo") != 0,
"workout_mode": profile.get_bool("workout_mode"),
"weight": profile.get_int("weight"),
"settings": {
"arrowskin": profile.get_int("arrowskin"),
"guidelines": profile.get_int("guidelines"),
"filter": profile.get_int("filter"),
"character": profile.get_int("character"),
},
}
)
else:
formatted_profile.update(
{
"sp": playstats.get_int("single_plays"),
"dp": playstats.get_int("double_plays"),
"early_late": profile.get_int("early_late") != 0,
"background_combo": profile.get_int("combo") != 0,
"workout_mode": "weight" in profile,
"weight": profile.get_int("weight"),
}
)
return formatted_profile
def format_score(self, userid: UserID, score: Score) -> Dict[str, Any]:
formatted_score = super().format_score(userid, score)
formatted_score["combo"] = score.data.get_int("combo", -1)
formatted_score["lamp"] = score.data.get_int("halo")
formatted_score["halo"] = {
DDRBase.HALO_NONE: None,
DDRBase.HALO_GOOD_FULL_COMBO: "GOOD FULL COMBO",
DDRBase.HALO_GREAT_FULL_COMBO: "GREAT FULL COMBO",
DDRBase.HALO_PERFECT_FULL_COMBO: "PERFECT FULL COMBO",
DDRBase.HALO_MARVELOUS_FULL_COMBO: "MARVELOUS FULL COMBO",
}.get(score.data.get_int("halo"))
formatted_score["status"] = score.data.get_int("rank")
formatted_score["rank"] = {
DDRBase.RANK_AAA: "AAA",
DDRBase.RANK_AA_PLUS: "AA+",
DDRBase.RANK_AA: "AA",
DDRBase.RANK_AA_MINUS: "AA-",
DDRBase.RANK_A_PLUS: "A+",
DDRBase.RANK_A: "A",
DDRBase.RANK_A_MINUS: "A-",
DDRBase.RANK_B_PLUS: "B+",
DDRBase.RANK_B: "B",
DDRBase.RANK_B_MINUS: "B-",
DDRBase.RANK_C_PLUS: "C+",
DDRBase.RANK_C: "C",
DDRBase.RANK_C_MINUS: "C-",
DDRBase.RANK_D_PLUS: "D+",
DDRBase.RANK_D: "D",
DDRBase.RANK_E: "E",
}.get(score.data.get_int("rank"), "NO PLAY")
return formatted_score
def format_attempt(self, userid: UserID, attempt: Attempt) -> Dict[str, Any]:
formatted_attempt = super().format_attempt(userid, attempt)
formatted_attempt["combo"] = attempt.data.get_int("combo", -1)
formatted_attempt["halo"] = {
DDRBase.HALO_NONE: None,
DDRBase.HALO_GOOD_FULL_COMBO: "GOOD FULL COMBO",
DDRBase.HALO_GREAT_FULL_COMBO: "GREAT FULL COMBO",
DDRBase.HALO_PERFECT_FULL_COMBO: "PERFECT FULL COMBO",
DDRBase.HALO_MARVELOUS_FULL_COMBO: "MARVELOUS FULL COMBO",
}.get(attempt.data.get_int("halo"))
formatted_attempt["rank"] = {
DDRBase.RANK_AAA: "AAA",
DDRBase.RANK_AA_PLUS: "AA+",
DDRBase.RANK_AA: "AA",
DDRBase.RANK_AA_MINUS: "AA-",
DDRBase.RANK_A_PLUS: "A+",
DDRBase.RANK_A: "A",
DDRBase.RANK_A_MINUS: "A-",
DDRBase.RANK_B_PLUS: "B+",
DDRBase.RANK_B: "B",
DDRBase.RANK_B_MINUS: "B-",
DDRBase.RANK_C_PLUS: "C+",
DDRBase.RANK_C: "C",
DDRBase.RANK_C_MINUS: "C-",
DDRBase.RANK_D_PLUS: "D+",
DDRBase.RANK_D: "D",
DDRBase.RANK_E: "E",
}.get(attempt.data.get_int("rank"), "NO PLAY")
return formatted_attempt
def format_song(self, song: Song) -> Dict[str, Any]:
difficulties = [0] * 10
difficulties[song.chart] = song.data.get_int("difficulty", 20)
formatted_song = super().format_song(song)
formatted_song["bpm_min"] = song.data.get_int("bpm_min", 120)
formatted_song["bpm_max"] = song.data.get_int("bpm_max", 120)
formatted_song["category"] = song.data.get_int("category", 0)
formatted_song["groove"] = song.data.get_dict(
"groove",
{
"voltage": 0,
"stream": 0,
"air": 0,
"chaos": 0,
"freeze": 0,
},
)
formatted_song["difficulties"] = difficulties
return formatted_song
def merge_song(self, existing: Dict[str, Any], new: Song) -> Dict[str, Any]:
new_song = super().merge_song(existing, new)
if existing["difficulties"][new.chart] == 0:
new_song["difficulties"][new.chart] = new.data.get_int("difficulty", 20)
return new_song
def activate_rival(self, profile: Profile, position: int) -> Profile:
newprofile = copy.deepcopy(profile)
if newprofile.version == VersionConstants.DDR_X2:
# X2 only has one active rival
lastdict = newprofile.get_dict("last")
lastdict.replace_int("fri", position + 1)
newprofile.replace_dict("last", lastdict)
elif newprofile.version in [
VersionConstants.DDR_X3_VS_2NDMIX,
VersionConstants.DDR_2013,
VersionConstants.DDR_2014,
VersionConstants.DDR_ACE,
VersionConstants.DDR_A20,
]:
# X3 has 3 active rivals, put this in the first open slot
lastdict = newprofile.get_dict("last")
if lastdict.get_int("rival1") < 1:
lastdict.replace_int("rival1", position + 1)
elif lastdict.get_int("rival2") < 1:
lastdict.replace_int("rival2", position + 1)
elif lastdict.get_int("rival3") < 1:
lastdict.replace_int("rival3", position + 1)
newprofile.replace_dict("last", lastdict)
return newprofile
def deactivate_rival(self, profile: Profile, position: int) -> Profile:
newprofile = copy.deepcopy(profile)
if newprofile.version == VersionConstants.DDR_X2:
# X2 only has one active rival
lastdict = newprofile.get_dict("last")
if lastdict.get_int("fri") == position + 1:
lastdict.replace_int("fri", 0)
newprofile.replace_dict("last", lastdict)
elif newprofile.version in [
VersionConstants.DDR_X3_VS_2NDMIX,
VersionConstants.DDR_2013,
VersionConstants.DDR_2014,
VersionConstants.DDR_ACE,
VersionConstants.DDR_A20,
]:
# X3 has 3 active rivals, put this in the first open slot
lastdict = newprofile.get_dict("last")
if lastdict.get_int("rival1") == position + 1:
lastdict.replace_int("rival1", 0)
elif lastdict.get_int("rival2") == position + 1:
lastdict.replace_int("rival2", 0)
elif lastdict.get_int("rival3") == position + 1:
lastdict.replace_int("rival3", 0)
newprofile.replace_dict("last", lastdict)
return newprofile
def format_rival(self, link: Link, profile: Profile) -> Dict[str, Any]:
pos = int(link.type[7:])
if profile.version == VersionConstants.DDR_X2:
active = pos == (profile.get_dict("last").get_int("fri") - 1)
elif profile.version in {
VersionConstants.DDR_X3_VS_2NDMIX,
VersionConstants.DDR_2013,
VersionConstants.DDR_2014,
VersionConstants.DDR_ACE,
VersionConstants.DDR_A20,
}:
actives = [
profile.get_dict("last").get_int("rival1") - 1,
profile.get_dict("last").get_int("rival2") - 1,
profile.get_dict("last").get_int("rival3") - 1,
]
active = pos in actives
else:
active = False
return {
"position": pos,
"active": active,
"userid": str(link.other_userid),
"remote": RemoteUser.is_remote(link.other_userid),
}