145 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			145 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."""
 | 
						||
    STARTING_CODE = 2900000
 | 
						||
 | 
						||
    MUSIC_SHEET_NAME: str = "Music Sheet"
 | 
						||
    MUSIC_SHEET_CODE: int = STARTING_CODE
 | 
						||
 | 
						||
    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": STARTING_CODE + 1,
 | 
						||
        "Pixelate Trap": STARTING_CODE + 2,
 | 
						||
        "Random Wave Trap": STARTING_CODE + 3,
 | 
						||
        "Shadow Edge Trap": STARTING_CODE + 4,
 | 
						||
        "Chromatic Aberration Trap": STARTING_CODE + 5,
 | 
						||
        "Background Freeze Trap": STARTING_CODE + 6,
 | 
						||
        "Gray Scale Trap": STARTING_CODE + 7,
 | 
						||
    }
 | 
						||
 | 
						||
    sfx_trap_items: Dict[str, int] = {
 | 
						||
        "Nyaa SFX Trap": STARTING_CODE + 8,
 | 
						||
        "Error SFX Trap": STARTING_CODE + 9,
 | 
						||
    }
 | 
						||
 | 
						||
    item_names_to_id = ChainMap({}, sfx_trap_items, vfx_trap_items)
 | 
						||
    location_names_to_id = ChainMap(song_locations, album_locations)
 | 
						||
 | 
						||
    def __init__(self) -> None:
 | 
						||
        self.item_names_to_id[self.MUSIC_SHEET_NAME] = self.MUSIC_SHEET_CODE
 | 
						||
 | 
						||
        item_id_index = self.STARTING_CODE + 50
 | 
						||
        full_file = load_text_file("MuseDashData.txt")
 | 
						||
        seen_albums = set()
 | 
						||
        for line in full_file.splitlines():
 | 
						||
            line = line.strip()
 | 
						||
            sections = line.split("|")
 | 
						||
 | 
						||
            album = sections[2]
 | 
						||
            if album not in seen_albums:
 | 
						||
                seen_albums.add(album)
 | 
						||
                self.album_items[album] = 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 = album 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()})
 | 
						||
 | 
						||
        location_id_index = self.STARTING_CODE
 | 
						||
        for name in self.album_items.keys():
 | 
						||
            self.album_locations[f"{name}-0"] = location_id_index
 | 
						||
            self.album_locations[f"{name}-1"] = location_id_index + 1
 | 
						||
            location_id_index += 2
 | 
						||
 | 
						||
        for name in self.song_items.keys():
 | 
						||
            self.song_locations[f"{name}-0"] = location_id_index
 | 
						||
            self.song_locations[f"{name}-1"] = location_id_index + 1
 | 
						||
            location_id_index += 2
 | 
						||
 | 
						||
    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)
 |