149 lines
5.4 KiB
Python
149 lines
5.4 KiB
Python
from .Items import SongData, AlbumData
|
||
from typing import Dict, List, Optional
|
||
from collections import ChainMap
|
||
|
||
|
||
def load_text_file(name: str) -> str:
|
||
import pkgutil
|
||
return pkgutil.get_data(__name__, name).decode()
|
||
|
||
|
||
class MuseDashCollections:
|
||
"""Contains all the data of Muse Dash, loaded from MuseDashData.txt."""
|
||
|
||
MUSIC_SHEET_NAME: str = "Music Sheet"
|
||
MUSIC_SHEET_CODE: int
|
||
|
||
FREE_ALBUMS = [
|
||
"Default Music",
|
||
"Budget Is Burning: Nano Core",
|
||
"Budget is Burning Vol.1"
|
||
]
|
||
|
||
DIFF_OVERRIDES = [
|
||
"MuseDash ka nanika hi",
|
||
"Rush-Hour",
|
||
"Find this Month's Featured Playlist",
|
||
"PeroPero in the Universe"
|
||
]
|
||
|
||
album_items: Dict[str, AlbumData] = {}
|
||
album_locations: Dict[str, int] = {}
|
||
song_items: Dict[str, SongData] = {}
|
||
song_locations: Dict[str, int] = {}
|
||
|
||
vfx_trap_items: Dict[str, int] = {
|
||
"Bad Apple Trap": 1,
|
||
"Pixelate Trap": 2,
|
||
"Random Wave Trap": 3,
|
||
"Shadow Edge Trap": 4,
|
||
"Chromatic Aberration Trap": 5,
|
||
"Background Freeze Trap": 6,
|
||
"Gray Scale Trap": 7,
|
||
}
|
||
|
||
sfx_trap_items: Dict[str, int] = {
|
||
"Nyaa SFX Trap": 8,
|
||
"Error SFX Trap": 9,
|
||
}
|
||
|
||
item_names_to_id = ChainMap({}, sfx_trap_items, vfx_trap_items)
|
||
location_names_to_id = ChainMap(song_locations, album_locations)
|
||
|
||
def __init__(self, start_item_id: int, items_per_location: int):
|
||
self.MUSIC_SHEET_CODE = start_item_id
|
||
self.item_names_to_id[self.MUSIC_SHEET_NAME] = self.MUSIC_SHEET_CODE
|
||
|
||
self.vfx_trap_items.update({k: (v + start_item_id) for (k, v) in self.vfx_trap_items.items()})
|
||
self.sfx_trap_items.update({k: (v + start_item_id) for (k, v) in self.sfx_trap_items.items()})
|
||
|
||
item_id_index = start_item_id + 50
|
||
location_id_index = start_item_id
|
||
|
||
full_file = load_text_file("MuseDashData.txt")
|
||
|
||
for line in full_file.splitlines():
|
||
line = line.strip()
|
||
sections = line.split("|")
|
||
|
||
if sections[2] not in self.album_items:
|
||
self.album_items[sections[2]] = AlbumData(item_id_index)
|
||
item_id_index += 1
|
||
|
||
# Data is in the format 'Song|UID|Album|StreamerMode|EasyDiff|HardDiff|MasterDiff|SecretDiff'
|
||
song_name = sections[0]
|
||
# [1] is used in the client copy to make sure item id's match.
|
||
song_is_free = sections[2] in self.FREE_ALBUMS
|
||
steamer_mode = sections[3] == "True"
|
||
|
||
if song_name in self.DIFF_OVERRIDES:
|
||
# Note: These difficulties may not actually be representative of these songs.
|
||
# The game does not provide these difficulties so they have to be filled in.
|
||
diff_of_easy = 4
|
||
diff_of_hard = 7
|
||
diff_of_master = 10
|
||
else:
|
||
diff_of_easy = self.parse_song_difficulty(sections[4])
|
||
diff_of_hard = self.parse_song_difficulty(sections[5])
|
||
diff_of_master = self.parse_song_difficulty(sections[6])
|
||
|
||
self.song_items[song_name] = SongData(item_id_index, song_is_free, steamer_mode,
|
||
diff_of_easy, diff_of_hard, diff_of_master)
|
||
item_id_index += 1
|
||
|
||
self.item_names_to_id.update({name: data.code for name, data in self.song_items.items()})
|
||
self.item_names_to_id.update({name: data.code for name, data in self.album_items.items()})
|
||
|
||
for name in self.album_items.keys():
|
||
for i in range(0, items_per_location):
|
||
new_name = f"{name}-{i}"
|
||
self.album_locations[new_name] = location_id_index
|
||
location_id_index += 1
|
||
|
||
for name in self.song_items.keys():
|
||
for i in range(0, items_per_location):
|
||
new_name = f"{name}-{i}"
|
||
self.song_locations[new_name] = location_id_index
|
||
location_id_index += 1
|
||
|
||
def get_songs_with_settings(self, dlc_songs: bool, streamer_mode_active: bool,
|
||
diff_lower: int, diff_higher: int) -> List[str]:
|
||
"""Gets a list of all songs that match the filter settings. Difficulty thresholds are inclusive."""
|
||
filtered_list = []
|
||
|
||
for songKey, songData in self.song_items.items():
|
||
if not dlc_songs and not songData.song_is_free:
|
||
continue
|
||
|
||
if streamer_mode_active and not songData.streamer_mode:
|
||
continue
|
||
|
||
if songData.easy is not None and diff_lower <= songData.easy <= diff_higher:
|
||
filtered_list.append(songKey)
|
||
continue
|
||
|
||
if songData.hard is not None and diff_lower <= songData.hard <= diff_higher:
|
||
filtered_list.append(songKey)
|
||
continue
|
||
|
||
if songData.master is not None and diff_lower <= songData.master <= diff_higher:
|
||
filtered_list.append(songKey)
|
||
continue
|
||
|
||
return filtered_list
|
||
|
||
def parse_song_difficulty(self, difficulty: str) -> Optional[int]:
|
||
"""Attempts to parse the song difficulty."""
|
||
if len(difficulty) <= 0 or difficulty == "?" or difficulty == "¿":
|
||
return None
|
||
|
||
# 0 is used as a filler and no songs actually have a 0 difficulty song.
|
||
if difficulty == "0":
|
||
return None
|
||
|
||
# Curse the 2023 april fools update. Used on 3rd Avenue.
|
||
if difficulty == "〇":
|
||
return 10
|
||
|
||
return int(difficulty)
|