Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Muse Dash: Update song list to v3.12.0 and fix up a missing song #2641

Closed
36 changes: 30 additions & 6 deletions worlds/musedash/MuseDashCollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class MuseDashCollections:
"Rush-Hour",
"Find this Month's Featured Playlist",
"PeroPero in the Universe",
"umpopoff"
]

album_items: Dict[str, AlbumData] = {}
Expand All @@ -56,7 +57,19 @@ class MuseDashCollections:
"Error SFX Trap": STARTING_CODE + 9,
}

item_names_to_id: ChainMap = ChainMap({}, sfx_trap_items, vfx_trap_items)
filler_items: Dict[str, int] = {
"Great To Perfect (5 Pack)": STARTING_CODE + 30,
"Miss To Great (5 Pack)": STARTING_CODE + 31,
"Extra Life": STARTING_CODE + 32,
}

filler_item_weights: Dict[str, int] = {
"Great To Perfect (5 Pack)": 10,
"Miss To Great (5 Pack)": 3,
"Extra Life": 1,
}

item_names_to_id: ChainMap = ChainMap({}, filler_items, sfx_trap_items, vfx_trap_items)
location_names_to_id: ChainMap = ChainMap(song_locations, album_locations)

def __init__(self) -> None:
Expand All @@ -81,11 +94,22 @@ def __init__(self) -> None:
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
# These songs use non-standard difficulty values. Which are being overriden with standard values.
# But also avoid filling any missing difficulties (i.e. 0s) with a difficulty value.
if sections[4] != '0':
diff_of_easy = 4
else:
diff_of_easy = None

if sections[5] != '0':
diff_of_hard = 7
else:
diff_of_hard = None

if sections[6] != '0':
diff_of_master = 10
else:
diff_of_master = None
else:
diff_of_easy = self.parse_song_difficulty(sections[4])
diff_of_hard = self.parse_song_difficulty(sections[5])
Expand Down
12 changes: 10 additions & 2 deletions worlds/musedash/MuseDashData.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Prestige and Vestige|56-4|Give Up TREATMENT Vol.11|True|6|8|11|
Tiny Fate|56-5|Give Up TREATMENT Vol.11|False|7|9|11|
Tsuki ni Murakumo Hana ni Kaze|55-0|Touhou Mugakudan -2-|False|3|5|7|
Patchouli's - Best Hit GSK|55-1|Touhou Mugakudan -2-|False|3|5|8|
Monosugoi Space Shuttle de Koishi ga Monosugoi uta|55-2|Touhou Mugakudan -2-|False|3|5|7|
Monosugoi Space Shuttle de Koishi ga Monosugoi uta|55-2|Touhou Mugakudan -2-|False|3|5|7|11
Kakoinaki Yo wa Ichigo no Tsukikage|55-3|Touhou Mugakudan -2-|False|3|6|8|
Psychedelic Kizakura Doumei|55-4|Touhou Mugakudan -2-|False|4|7|10|
Mischievous Sensation|55-5|Touhou Mugakudan -2-|False|5|7|9|
Expand Down Expand Up @@ -501,4 +501,12 @@ slic.hertz|68-1|Gambler's Tricks|True|5|7|9|
Fuzzy-Navel|68-2|Gambler's Tricks|True|6|8|10|11
Swing Edge|68-3|Gambler's Tricks|True|4|8|10|
Twisted Escape|68-4|Gambler's Tricks|True|5|8|10|11
Swing Sweet Twee Dance|68-5|Gambler's Tricks|False|4|7|10|
Swing Sweet Twee Dance|68-5|Gambler's Tricks|False|4|7|10|
Sanyousei SAY YA!!!|43-42|MD Plus Project|False|4|6|8|
YUKEMURI TAMAONSEN II|43-43|MD Plus Project|False|3|6|9|
Samayoi no mei Amatsu|69-0|Touhou Mugakudan -3-|False|4|6|9|
INTERNET SURVIVOR|69-1|Touhou Mugakudan -3-|False|5|8|10|
Shuki*RaiRai|69-2|Touhou Mugakudan -3-|False|5|7|9|
HELLOHELL|69-3|Touhou Mugakudan -3-|False|4|7|10|
Calamity Fortune|69-4|Touhou Mugakudan -3-|True|6|8|10|11
Tsurupettan|69-5|Touhou Mugakudan -3-|True|2|5|8|
2 changes: 1 addition & 1 deletion worlds/musedash/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AdditionalSongs(Range):
- The final song count may be lower due to other settings.
"""
range_start = 15
range_end = 500 # Note will probably not reach this high if any other settings are done.
range_end = 508 # Note will probably not reach this high if any other settings are done.
default = 40
display_name = "Additional Song Count"

Expand Down
64 changes: 47 additions & 17 deletions worlds/musedash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class MuseDashWorld(World):

# Necessary Data
md_collection = MuseDashCollections()
filler_item_names = list(md_collection.filler_item_weights.keys())
filler_item_weights = list(md_collection.filler_item_weights.values())

item_name_to_id = {name: code for name, code in md_collection.item_names_to_id.items()}
location_name_to_id = {name: code for name, code in md_collection.location_names_to_id.items()}
Expand Down Expand Up @@ -174,6 +176,10 @@ def create_item(self, name: str) -> Item:
return MuseDashFixedItem(name, ItemClassification.progression_skip_balancing,
self.md_collection.MUSIC_SHEET_CODE, self.player)

filler = self.md_collection.filler_items.get(name)
if filler:
return MuseDashFixedItem(name, ItemClassification.filler, filler, self.player)

trap = self.md_collection.vfx_trap_items.get(name)
if trap:
return MuseDashFixedItem(name, ItemClassification.trap, trap, self.player)
Expand All @@ -189,6 +195,9 @@ def create_item(self, name: str) -> Item:
song = self.md_collection.song_items.get(name)
return MuseDashSongItem(name, self.player, song)

def get_filler_item_name(self) -> str:
return self.random.choices(self.filler_item_names, self.filler_item_weights)[0]

def create_items(self) -> None:
song_keys_in_pool = self.included_songs.copy()

Expand All @@ -199,8 +208,13 @@ def create_items(self) -> None:
for _ in range(0, item_count):
self.multiworld.itempool.append(self.create_item(self.md_collection.MUSIC_SHEET_NAME))

# Then add all traps
trap_count = self.get_trap_count()
# Then add 1 copy of every song
item_count += len(self.included_songs)
for song in self.included_songs:
self.multiworld.itempool.append(self.create_item(song))

# Then add all traps, making sure we don't over fill
trap_count = min(self.location_count - item_count, self.get_trap_count())
trap_list = self.get_available_traps()
if len(trap_list) > 0 and trap_count > 0:
for _ in range(0, trap_count):
Expand All @@ -209,23 +223,38 @@ def create_items(self) -> None:

item_count += trap_count

# Next fill all remaining slots with song items
needed_item_count = self.location_count
while item_count < needed_item_count:
# If we have more items needed than keys, just iterate the list and add them all
if len(song_keys_in_pool) <= needed_item_count - item_count:
for key in song_keys_in_pool:
self.multiworld.itempool.append(self.create_item(key))
# At this point, if a player is using traps, it's possible that they have filled all locations
items_left = self.location_count - item_count
if items_left <= 0:
return

# When it comes to filling remaining spaces, we have 2 options. A useless filler or additional songs.
# First fill 50% with the filler. The rest is to be duplicate songs.
filler_count = floor(0.5 * items_left)
items_left -= filler_count

for _ in range(0, filler_count):
self.multiworld.itempool.append(self.create_item(self.get_filler_item_name()))

# All remaining spots are filled with duplicate songs. Duplicates are set to useful instead of progression
# to cut down on the number of progression items that Muse Dash puts into the pool.

item_count += len(song_keys_in_pool)
continue
# This is for the extraordinary case of needing to fill a lot of items.
while items_left > len(song_keys_in_pool):
for key in song_keys_in_pool:
item = self.create_item(key)
item.classification = ItemClassification.useful
self.multiworld.itempool.append(item)

# Otherwise add a random assortment of songs
self.random.shuffle(song_keys_in_pool)
for i in range(0, needed_item_count - item_count):
self.multiworld.itempool.append(self.create_item(song_keys_in_pool[i]))
items_left -= len(song_keys_in_pool)
continue

item_count = needed_item_count
# Otherwise add a random assortment of songs
self.random.shuffle(song_keys_in_pool)
for i in range(0, items_left):
item = self.create_item(song_keys_in_pool[i])
item.classification = ItemClassification.useful
self.multiworld.itempool.append(item)

def create_regions(self) -> None:
menu_region = Region("Menu", self.player, self.multiworld)
Expand Down Expand Up @@ -328,5 +357,6 @@ def fill_slot_data(self):
"victoryLocation": self.victory_song_name,
"deathLink": self.options.death_link.value,
"musicSheetWinCount": self.get_music_sheet_win_count(),
"gradeNeeded": self.options.grade_needed.value
"gradeNeeded": self.options.grade_needed.value,
"hasFiller": True,
}
8 changes: 7 additions & 1 deletion worlds/musedash/test/TestDifficultyRanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,11 @@ def test_songs_have_difficulty(self) -> None:
for song_name in muse_dash_world.md_collection.DIFF_OVERRIDES:
song = muse_dash_world.md_collection.song_items[song_name]

self.assertTrue(song.easy is not None and song.hard is not None and song.master is not None,
# umpopoff is a one time weird song. Its currently the only song in the game
# with non-standard difficulties and also doesn't have 3 or more difficulties.
if song_name == 'umpopoff':
self.assertTrue(song.easy is None and song.hard is not None and song.master is None,
f"Song '{song_name}' difficulty not set when it should be.")
else:
self.assertTrue(song.easy is not None and song.hard is not None and song.master is not None,
f"Song '{song_name}' difficulty not set when it should be.")
Loading