Muse Dash: Option Groups and Options Rework (#3434)
* Ensure that included/starter songs only include those within enabled dlcs. * Allow filtering traps by trap instead of by category. * Add in the currently available limited time dlcs to the dlc list. * Add the option group to the webhost and cleanup some errors. * Fix trap list. * Update tests. Add new ones to test correctness of new features. * Remove the old Just As Planned option * Make traps order alphabetically. Also adjust the title for traps. * Adjust new lines to better fit the website. * Style fixes. * Test adjustments and a fix due to test no longer having just as planned dlc. * Undo spacing changes as it breaks yaml generation. * Fix indenting in webhost. * Add the old options in as removed. Also clean up unused import. * Remove references to the old allow_just_as_planned_dlc_songs option in Muse Dash tests. * Add newline to end of file. --------- Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
parent
f30f2d3a3f
commit
133167564c
|
@ -22,12 +22,15 @@ class MuseDashCollections:
|
|||
]
|
||||
|
||||
MUSE_PLUS_DLC: str = "Muse Plus"
|
||||
|
||||
# Ordering matters for webhost. Order goes: Muse Plus, Time Limited Muse Plus Dlcs, Paid Dlcs
|
||||
DLC: List[str] = [
|
||||
# MUSE_PLUS_DLC, # To be included when OptionSets are rendered as part of basic settings.
|
||||
# "maimai DX Limited-time Suite", # Part of Muse Plus. Goes away 31st Jan 2026.
|
||||
"Miku in Museland", # Paid DLC not included in Muse Plus
|
||||
"Rin Len's Mirrorland", # Paid DLC not included in Muse Plus
|
||||
"MSR Anthology", # Now no longer available.
|
||||
MUSE_PLUS_DLC,
|
||||
"CHUNITHM COURSE MUSE", # Part of Muse Plus. Goes away 22nd May 2027.
|
||||
"maimai DX Limited-time Suite", # Part of Muse Plus. Goes away 31st Jan 2026.
|
||||
"MSR Anthology", # Now no longer available.
|
||||
"Miku in Museland", # Paid DLC not included in Muse Plus
|
||||
"Rin Len's Mirrorland", # Paid DLC not included in Muse Plus
|
||||
]
|
||||
|
||||
DIFF_OVERRIDES: List[str] = [
|
||||
|
@ -50,7 +53,7 @@ class MuseDashCollections:
|
|||
song_items: Dict[str, SongData] = {}
|
||||
song_locations: Dict[str, int] = {}
|
||||
|
||||
vfx_trap_items: Dict[str, int] = {
|
||||
trap_items: Dict[str, int] = {
|
||||
"Bad Apple Trap": STARTING_CODE + 1,
|
||||
"Pixelate Trap": STARTING_CODE + 2,
|
||||
"Ripple Trap": STARTING_CODE + 3,
|
||||
|
@ -58,13 +61,15 @@ class MuseDashCollections:
|
|||
"Chromatic Aberration Trap": STARTING_CODE + 5,
|
||||
"Background Freeze Trap": STARTING_CODE + 6,
|
||||
"Gray Scale Trap": STARTING_CODE + 7,
|
||||
"Nyaa SFX Trap": STARTING_CODE + 8,
|
||||
"Error SFX Trap": STARTING_CODE + 9,
|
||||
"Focus Line Trap": STARTING_CODE + 10,
|
||||
}
|
||||
|
||||
sfx_trap_items: Dict[str, int] = {
|
||||
"Nyaa SFX Trap": STARTING_CODE + 8,
|
||||
"Error SFX Trap": STARTING_CODE + 9,
|
||||
}
|
||||
sfx_trap_items: List[str] = [
|
||||
"Nyaa SFX Trap",
|
||||
"Error SFX Trap",
|
||||
]
|
||||
|
||||
filler_items: Dict[str, int] = {
|
||||
"Great To Perfect (10 Pack)": STARTING_CODE + 30,
|
||||
|
@ -78,7 +83,7 @@ class MuseDashCollections:
|
|||
"Extra Life": 1,
|
||||
}
|
||||
|
||||
item_names_to_id: ChainMap = ChainMap({}, filler_items, sfx_trap_items, vfx_trap_items)
|
||||
item_names_to_id: ChainMap = ChainMap({}, filler_items, trap_items)
|
||||
location_names_to_id: ChainMap = ChainMap(song_locations, album_locations)
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
@ -171,6 +176,9 @@ class MuseDashCollections:
|
|||
|
||||
return filtered_list
|
||||
|
||||
def filter_songs_to_dlc(self, song_list: List[str], dlc_songs: Set[str]) -> List[str]:
|
||||
return [song for song in song_list if self.song_matches_dlc_filter(self.song_items[song], dlc_songs)]
|
||||
|
||||
def song_matches_dlc_filter(self, song: SongData, dlc_songs: Set[str]) -> bool:
|
||||
if song.album in self.FREE_ALBUMS:
|
||||
return True
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
from typing import Dict
|
||||
from Options import Toggle, Option, Range, Choice, DeathLink, ItemSet, OptionSet, PerGameCommonOptions
|
||||
from Options import Toggle, Range, Choice, DeathLink, ItemSet, OptionSet, PerGameCommonOptions, OptionGroup, Removed
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .MuseDashCollection import MuseDashCollections
|
||||
|
||||
|
||||
class AllowJustAsPlannedDLCSongs(Toggle):
|
||||
"""Whether [Muse Plus] DLC Songs, and all the albums included in it, can be chosen as randomised songs.
|
||||
Note: The [Just As Planned] DLC contains all [Muse Plus] songs."""
|
||||
display_name = "Allow [Muse Plus] DLC Songs"
|
||||
|
||||
|
||||
class DLCMusicPacks(OptionSet):
|
||||
"""Which non-[Muse Plus] DLC packs can be chosen as randomised songs."""
|
||||
"""
|
||||
Choose which DLC Packs will be included in the pool of chooseable songs.
|
||||
|
||||
Note: The [Just As Planned] DLC contains all [Muse Plus] songs.
|
||||
"""
|
||||
display_name = "DLC Packs"
|
||||
default = {}
|
||||
valid_keys = [dlc for dlc in MuseDashCollections.DLC]
|
||||
|
||||
|
||||
class StreamerModeEnabled(Toggle):
|
||||
"""In Muse Dash, an option named 'Streamer Mode' removes songs which may trigger copyright issues when streaming.
|
||||
If this is enabled, only songs available under Streamer Mode will be available for randomization."""
|
||||
"""
|
||||
In Muse Dash, an option named 'Streamer Mode' removes songs which may trigger copyright issues when streaming.
|
||||
|
||||
If this is enabled, only songs available under Streamer Mode will be available for randomization.
|
||||
"""
|
||||
display_name = "Streamer Mode Only Songs"
|
||||
|
||||
|
||||
|
@ -33,7 +33,8 @@ class StartingSongs(Range):
|
|||
|
||||
|
||||
class AdditionalSongs(Range):
|
||||
"""The total number of songs that will be placed in the randomization pool.
|
||||
"""
|
||||
The total number of songs that will be placed in the randomization pool.
|
||||
- This does not count any starting songs or the goal song.
|
||||
- The final song count may be lower due to other settings.
|
||||
"""
|
||||
|
@ -44,7 +45,8 @@ class AdditionalSongs(Range):
|
|||
|
||||
|
||||
class DifficultyMode(Choice):
|
||||
"""Ensures that at any chosen song has at least 1 value falling within these values.
|
||||
"""
|
||||
Ensures that at any chosen song has at least 1 value falling within these values.
|
||||
- Any: All songs are available
|
||||
- Easy: 1, 2 or 3
|
||||
- Medium: 4, 5
|
||||
|
@ -66,8 +68,11 @@ class DifficultyMode(Choice):
|
|||
|
||||
# Todo: Investigate options to make this non randomizable
|
||||
class DifficultyModeOverrideMin(Range):
|
||||
"""Ensures that 1 difficulty has at least 1 this value or higher per song.
|
||||
- Difficulty Mode must be set to Manual."""
|
||||
"""
|
||||
Ensures that 1 difficulty has at least 1 this value or higher per song.
|
||||
|
||||
Note: Difficulty Mode must be set to Manual.
|
||||
"""
|
||||
display_name = "Manual Difficulty Min"
|
||||
range_start = 1
|
||||
range_end = 11
|
||||
|
@ -76,8 +81,11 @@ class DifficultyModeOverrideMin(Range):
|
|||
|
||||
# Todo: Investigate options to make this non randomizable
|
||||
class DifficultyModeOverrideMax(Range):
|
||||
"""Ensures that 1 difficulty has at least 1 this value or lower per song.
|
||||
- Difficulty Mode must be set to Manual."""
|
||||
"""
|
||||
Ensures that 1 difficulty has at least 1 this value or lower per song.
|
||||
|
||||
Note: Difficulty Mode must be set to Manual.
|
||||
"""
|
||||
display_name = "Manual Difficulty Max"
|
||||
range_start = 1
|
||||
range_end = 11
|
||||
|
@ -85,7 +93,8 @@ class DifficultyModeOverrideMax(Range):
|
|||
|
||||
|
||||
class GradeNeeded(Choice):
|
||||
"""Completing a song will require a grade of this value or higher in order to unlock items.
|
||||
"""
|
||||
Completing a song will require a grade of this value or higher in order to unlock items.
|
||||
The grades are as follows:
|
||||
- Silver S (SS): >= 95% accuracy
|
||||
- Pink S (S): >= 90% accuracy
|
||||
|
@ -104,7 +113,9 @@ class GradeNeeded(Choice):
|
|||
|
||||
|
||||
class MusicSheetCountPercentage(Range):
|
||||
"""Controls how many music sheets are added to the pool based on the number of songs, including starting songs.
|
||||
"""
|
||||
Controls how many music sheets are added to the pool based on the number of songs, including starting songs.
|
||||
|
||||
Higher numbers leads to more consistent game lengths, but will cause individual music sheets to be less important.
|
||||
"""
|
||||
range_start = 10
|
||||
|
@ -121,19 +132,18 @@ class MusicSheetWinCountPercentage(Range):
|
|||
display_name = "Music Sheets Needed to Win"
|
||||
|
||||
|
||||
class TrapTypes(Choice):
|
||||
"""This controls the types of traps that can be added to the pool.
|
||||
class ChosenTraps(OptionSet):
|
||||
"""
|
||||
This controls the types of traps that can be added to the pool.
|
||||
- Traps last the length of a song, or until you die.
|
||||
- VFX Traps consist of visual effects that play over the song. (i.e. Grayscale.)
|
||||
- SFX Traps consist of changing your sfx setting to one possibly more annoying sfx.
|
||||
Traps last the length of a song, or until you die.
|
||||
|
||||
Note: SFX traps are only available if [Just as Planned] DLC songs are enabled.
|
||||
"""
|
||||
display_name = "Available Trap Types"
|
||||
option_None = 0
|
||||
option_VFX = 1
|
||||
option_SFX = 2
|
||||
option_All = 3
|
||||
default = 3
|
||||
display_name = "Chosen Traps"
|
||||
default = {}
|
||||
valid_keys = {trap for trap in MuseDashCollections.trap_items.keys()}
|
||||
|
||||
|
||||
class TrapCountPercentage(Range):
|
||||
|
@ -145,24 +155,49 @@ class TrapCountPercentage(Range):
|
|||
|
||||
|
||||
class IncludeSongs(ItemSet):
|
||||
"""Any song listed here will be guaranteed to be included as part of the seed.
|
||||
- Difficulty options will be skipped for these songs.
|
||||
- If there being too many included songs, songs will be randomly chosen without regard for difficulty.
|
||||
- If you want these songs immediately, use start_inventory instead.
|
||||
"""
|
||||
These songs will be guaranteed to show up within the seed.
|
||||
- You must have the DLC enabled to play these songs.
|
||||
- Difficulty options will not affect these songs.
|
||||
- If there are too many included songs, this will act as a whitelist ignoring song difficulty.
|
||||
"""
|
||||
verify_item_name = True
|
||||
display_name = "Include Songs"
|
||||
|
||||
|
||||
class ExcludeSongs(ItemSet):
|
||||
"""Any song listed here will be excluded from being a part of the seed."""
|
||||
"""
|
||||
These songs will be guaranteed to not show up within the seed.
|
||||
|
||||
Note: Does not affect songs within the "Include Songs" list.
|
||||
"""
|
||||
verify_item_name = True
|
||||
display_name = "Exclude Songs"
|
||||
|
||||
|
||||
md_option_groups = [
|
||||
OptionGroup("Song Choice", [
|
||||
DLCMusicPacks,
|
||||
StreamerModeEnabled,
|
||||
IncludeSongs,
|
||||
ExcludeSongs,
|
||||
]),
|
||||
OptionGroup("Difficulty", [
|
||||
GradeNeeded,
|
||||
DifficultyMode,
|
||||
DifficultyModeOverrideMin,
|
||||
DifficultyModeOverrideMax,
|
||||
DeathLink,
|
||||
]),
|
||||
OptionGroup("Traps", [
|
||||
ChosenTraps,
|
||||
TrapCountPercentage,
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MuseDashOptions(PerGameCommonOptions):
|
||||
allow_just_as_planned_dlc_songs: AllowJustAsPlannedDLCSongs
|
||||
dlc_packs: DLCMusicPacks
|
||||
streamer_mode_enabled: StreamerModeEnabled
|
||||
starting_song_count: StartingSongs
|
||||
|
@ -173,8 +208,12 @@ class MuseDashOptions(PerGameCommonOptions):
|
|||
grade_needed: GradeNeeded
|
||||
music_sheet_count_percentage: MusicSheetCountPercentage
|
||||
music_sheet_win_count_percentage: MusicSheetWinCountPercentage
|
||||
available_trap_types: TrapTypes
|
||||
chosen_traps: ChosenTraps
|
||||
trap_count_percentage: TrapCountPercentage
|
||||
death_link: DeathLink
|
||||
include_songs: IncludeSongs
|
||||
exclude_songs: ExcludeSongs
|
||||
|
||||
# Removed
|
||||
allow_just_as_planned_dlc_songs: Removed
|
||||
available_trap_types: Removed
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
MuseDashPresets: Dict[str, Dict[str, Any]] = {
|
||||
# An option to support Short Sync games. 40 songs.
|
||||
"No DLC - Short": {
|
||||
"allow_just_as_planned_dlc_songs": False,
|
||||
"dlc_packs": [],
|
||||
"starting_song_count": 5,
|
||||
"additional_song_count": 34,
|
||||
"music_sheet_count_percentage": 20,
|
||||
|
@ -11,7 +11,7 @@ MuseDashPresets: Dict[str, Dict[str, Any]] = {
|
|||
},
|
||||
# An option to support Short Sync games but adds variety. 40 songs.
|
||||
"DLC - Short": {
|
||||
"allow_just_as_planned_dlc_songs": True,
|
||||
"dlc_packs": ["Muse Plus"],
|
||||
"starting_song_count": 5,
|
||||
"additional_song_count": 34,
|
||||
"music_sheet_count_percentage": 20,
|
||||
|
@ -19,7 +19,7 @@ MuseDashPresets: Dict[str, Dict[str, Any]] = {
|
|||
},
|
||||
# An option to support Longer Sync/Async games. 100 songs.
|
||||
"DLC - Long": {
|
||||
"allow_just_as_planned_dlc_songs": True,
|
||||
"dlc_packs": ["Muse Plus"],
|
||||
"starting_song_count": 8,
|
||||
"additional_song_count": 91,
|
||||
"music_sheet_count_percentage": 20,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from worlds.AutoWorld import World, WebWorld
|
||||
from BaseClasses import Region, Item, ItemClassification, Entrance, Tutorial
|
||||
from typing import List, ClassVar, Type
|
||||
from BaseClasses import Region, Item, ItemClassification, Tutorial
|
||||
from typing import List, ClassVar, Type, Set
|
||||
from math import floor
|
||||
from Options import PerGameCommonOptions
|
||||
|
||||
from .Options import MuseDashOptions
|
||||
from .Options import MuseDashOptions, md_option_groups
|
||||
from .Items import MuseDashSongItem, MuseDashFixedItem
|
||||
from .Locations import MuseDashLocation
|
||||
from .MuseDashCollection import MuseDashCollections
|
||||
|
@ -35,6 +35,7 @@ class MuseDashWebWorld(WebWorld):
|
|||
|
||||
tutorials = [setup_en, setup_es]
|
||||
options_presets = MuseDashPresets
|
||||
option_groups = md_option_groups
|
||||
|
||||
|
||||
class MuseDashWorld(World):
|
||||
|
@ -72,8 +73,6 @@ class MuseDashWorld(World):
|
|||
|
||||
def generate_early(self):
|
||||
dlc_songs = {key for key in self.options.dlc_packs.value}
|
||||
if self.options.allow_just_as_planned_dlc_songs.value:
|
||||
dlc_songs.add(self.md_collection.MUSE_PLUS_DLC)
|
||||
|
||||
streamer_mode = self.options.streamer_mode_enabled
|
||||
(lower_diff_threshold, higher_diff_threshold) = self.get_difficulty_range()
|
||||
|
@ -88,7 +87,7 @@ class MuseDashWorld(World):
|
|||
available_song_keys = self.md_collection.get_songs_with_settings(
|
||||
dlc_songs, bool(streamer_mode.value), lower_diff_threshold, higher_diff_threshold)
|
||||
|
||||
available_song_keys = self.handle_plando(available_song_keys)
|
||||
available_song_keys = self.handle_plando(available_song_keys, dlc_songs)
|
||||
|
||||
count_needed_for_start = max(0, starter_song_count - len(self.starting_songs))
|
||||
if len(available_song_keys) + len(self.included_songs) >= count_needed_for_start + 11:
|
||||
|
@ -109,7 +108,7 @@ class MuseDashWorld(World):
|
|||
for song in self.starting_songs:
|
||||
self.multiworld.push_precollected(self.create_item(song))
|
||||
|
||||
def handle_plando(self, available_song_keys: List[str]) -> List[str]:
|
||||
def handle_plando(self, available_song_keys: List[str], dlc_songs: Set[str]) -> List[str]:
|
||||
song_items = self.md_collection.song_items
|
||||
|
||||
start_items = self.options.start_inventory.value.keys()
|
||||
|
@ -117,7 +116,9 @@ class MuseDashWorld(World):
|
|||
exclude_songs = self.options.exclude_songs.value
|
||||
|
||||
self.starting_songs = [s for s in start_items if s in song_items]
|
||||
self.starting_songs = self.md_collection.filter_songs_to_dlc(self.starting_songs, dlc_songs)
|
||||
self.included_songs = [s for s in include_songs if s in song_items and s not in self.starting_songs]
|
||||
self.included_songs = self.md_collection.filter_songs_to_dlc(self.included_songs, dlc_songs)
|
||||
|
||||
return [s for s in available_song_keys if s not in start_items
|
||||
and s not in include_songs and s not in exclude_songs]
|
||||
|
@ -148,7 +149,7 @@ class MuseDashWorld(World):
|
|||
self.victory_song_name = available_song_keys[chosen_song - included_song_count]
|
||||
del available_song_keys[chosen_song - included_song_count]
|
||||
|
||||
# Next, make sure the starting songs are fufilled
|
||||
# Next, make sure the starting songs are fulfilled
|
||||
if len(self.starting_songs) < starting_song_count:
|
||||
for _ in range(len(self.starting_songs), starting_song_count):
|
||||
if len(available_song_keys) > 0:
|
||||
|
@ -156,7 +157,7 @@ class MuseDashWorld(World):
|
|||
else:
|
||||
self.starting_songs.append(self.included_songs.pop())
|
||||
|
||||
# Then attempt to fufill any remaining songs for interim songs
|
||||
# Then attempt to fulfill any remaining songs for interim songs
|
||||
if len(self.included_songs) < additional_song_count:
|
||||
for _ in range(len(self.included_songs), self.options.additional_song_count):
|
||||
if len(available_song_keys) <= 0:
|
||||
|
@ -174,11 +175,7 @@ class MuseDashWorld(World):
|
|||
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)
|
||||
|
||||
trap = self.md_collection.sfx_trap_items.get(name)
|
||||
trap = self.md_collection.trap_items.get(name)
|
||||
if trap:
|
||||
return MuseDashFixedItem(name, ItemClassification.trap, trap, self.player)
|
||||
|
||||
|
@ -286,17 +283,11 @@ class MuseDashWorld(World):
|
|||
state.has(self.md_collection.MUSIC_SHEET_NAME, self.player, self.get_music_sheet_win_count())
|
||||
|
||||
def get_available_traps(self) -> List[str]:
|
||||
sfx_traps_available = self.options.allow_just_as_planned_dlc_songs.value
|
||||
full_trap_list = self.md_collection.trap_items.keys()
|
||||
if self.md_collection.MUSE_PLUS_DLC not in self.options.dlc_packs.value:
|
||||
full_trap_list = [trap for trap in full_trap_list if trap not in self.md_collection.sfx_trap_items]
|
||||
|
||||
trap_list = []
|
||||
if self.options.available_trap_types.value & 1 != 0:
|
||||
trap_list += self.md_collection.vfx_trap_items.keys()
|
||||
|
||||
# SFX options are only available under Just as Planned DLC.
|
||||
if sfx_traps_available and self.options.available_trap_types.value & 2 != 0:
|
||||
trap_list += self.md_collection.sfx_trap_items.keys()
|
||||
|
||||
return trap_list
|
||||
return [trap for trap in full_trap_list if trap in self.options.chosen_traps.value]
|
||||
|
||||
def get_trap_count(self) -> int:
|
||||
multiplier = self.options.trap_count_percentage.value / 100.0
|
||||
|
|
|
@ -9,25 +9,26 @@ class CollectionsTest(unittest.TestCase):
|
|||
for name in collection.song_items.keys():
|
||||
for c in name:
|
||||
# This is taken directly from OoT. Represents the generally excepted characters.
|
||||
if (0x20 <= ord(c) < 0x7e):
|
||||
if 0x20 <= ord(c) < 0x7e:
|
||||
continue
|
||||
|
||||
bad_names.append(name)
|
||||
break
|
||||
|
||||
self.assertEqual(len(bad_names), 0, f"Muse Dash has {len(bad_names)} songs with non-ASCII characters.\n{bad_names}")
|
||||
self.assertEqual(len(bad_names), 0,
|
||||
f"Muse Dash has {len(bad_names)} songs with non-ASCII characters.\n{bad_names}")
|
||||
|
||||
def test_ids_dont_change(self) -> None:
|
||||
collection = MuseDashCollections()
|
||||
itemsBefore = {name: code for name, code in collection.item_names_to_id.items()}
|
||||
locationsBefore = {name: code for name, code in collection.location_names_to_id.items()}
|
||||
items_before = {name: code for name, code in collection.item_names_to_id.items()}
|
||||
locations_before = {name: code for name, code in collection.location_names_to_id.items()}
|
||||
|
||||
collection.__init__()
|
||||
itemsAfter = {name: code for name, code in collection.item_names_to_id.items()}
|
||||
locationsAfter = {name: code for name, code in collection.location_names_to_id.items()}
|
||||
items_after = {name: code for name, code in collection.item_names_to_id.items()}
|
||||
locations_after = {name: code for name, code in collection.location_names_to_id.items()}
|
||||
|
||||
self.assertDictEqual(itemsBefore, itemsAfter, "Item ID changed after secondary init.")
|
||||
self.assertDictEqual(locationsBefore, locationsAfter, "Location ID changed after secondary init.")
|
||||
self.assertDictEqual(items_before, items_after, "Item ID changed after secondary init.")
|
||||
self.assertDictEqual(locations_before, locations_after, "Location ID changed after secondary init.")
|
||||
|
||||
def test_free_dlc_included_in_base_songs(self) -> None:
|
||||
collection = MuseDashCollections()
|
||||
|
|
|
@ -3,31 +3,31 @@ from . import MuseDashTestBase
|
|||
|
||||
class DifficultyRanges(MuseDashTestBase):
|
||||
def test_all_difficulty_ranges(self) -> None:
|
||||
muse_dash_world = self.multiworld.worlds[1]
|
||||
muse_dash_world = self.get_world()
|
||||
dlc_set = {x for x in muse_dash_world.md_collection.DLC}
|
||||
difficulty_choice = muse_dash_world.options.song_difficulty_mode
|
||||
difficulty_min = muse_dash_world.options.song_difficulty_min
|
||||
difficulty_max = muse_dash_world.options.song_difficulty_max
|
||||
|
||||
def test_range(inputRange, lower, upper):
|
||||
self.assertEqual(inputRange[0], lower)
|
||||
self.assertEqual(inputRange[1], upper)
|
||||
def test_range(input_range, lower, upper):
|
||||
self.assertEqual(input_range[0], lower)
|
||||
self.assertEqual(input_range[1], upper)
|
||||
|
||||
songs = muse_dash_world.md_collection.get_songs_with_settings(dlc_set, False, inputRange[0], inputRange[1])
|
||||
songs = muse_dash_world.md_collection.get_songs_with_settings(dlc_set, False, input_range[0], input_range[1])
|
||||
for songKey in songs:
|
||||
song = muse_dash_world.md_collection.song_items[songKey]
|
||||
if (song.easy is not None and inputRange[0] <= song.easy <= inputRange[1]):
|
||||
if song.easy is not None and input_range[0] <= song.easy <= input_range[1]:
|
||||
continue
|
||||
|
||||
if (song.hard is not None and inputRange[0] <= song.hard <= inputRange[1]):
|
||||
if song.hard is not None and input_range[0] <= song.hard <= input_range[1]:
|
||||
continue
|
||||
|
||||
if (song.master is not None and inputRange[0] <= song.master <= inputRange[1]):
|
||||
if song.master is not None and input_range[0] <= song.master <= input_range[1]:
|
||||
continue
|
||||
|
||||
self.fail(f"Invalid song '{songKey}' was given for range '{inputRange[0]} to {inputRange[1]}'")
|
||||
self.fail(f"Invalid song '{songKey}' was given for range '{input_range[0]} to {input_range[1]}'")
|
||||
|
||||
#auto ranges
|
||||
# auto ranges
|
||||
difficulty_choice.value = 0
|
||||
test_range(muse_dash_world.get_difficulty_range(), 0, 12)
|
||||
difficulty_choice.value = 1
|
||||
|
@ -61,7 +61,7 @@ class DifficultyRanges(MuseDashTestBase):
|
|||
test_range(muse_dash_world.get_difficulty_range(), 4, 6)
|
||||
|
||||
def test_songs_have_difficulty(self) -> None:
|
||||
muse_dash_world = self.multiworld.worlds[1]
|
||||
muse_dash_world = self.get_world()
|
||||
|
||||
for song_name in muse_dash_world.md_collection.DIFF_OVERRIDES:
|
||||
song = muse_dash_world.md_collection.song_items[song_name]
|
||||
|
@ -73,4 +73,4 @@ class DifficultyRanges(MuseDashTestBase):
|
|||
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.")
|
||||
f"Song '{song_name}' difficulty not set when it should be.")
|
||||
|
|
|
@ -4,7 +4,32 @@ from . import MuseDashTestBase
|
|||
class TestPlandoSettings(MuseDashTestBase):
|
||||
options = {
|
||||
"additional_song_count": 15,
|
||||
"allow_just_as_planned_dlc_songs": True,
|
||||
"dlc_packs": {"Muse Plus"},
|
||||
"include_songs": [
|
||||
"Lunatic",
|
||||
"Out of Sense",
|
||||
"Magic Knight Girl",
|
||||
]
|
||||
}
|
||||
|
||||
def test_included_songs_didnt_grow_item_count(self) -> None:
|
||||
muse_dash_world = self.get_world()
|
||||
self.assertEqual(len(muse_dash_world.included_songs), 15, "Logical songs size grew when it shouldn't.")
|
||||
|
||||
def test_included_songs_plando(self) -> None:
|
||||
muse_dash_world = self.get_world()
|
||||
songs = muse_dash_world.included_songs.copy()
|
||||
songs.append(muse_dash_world.victory_song_name)
|
||||
|
||||
self.assertIn("Lunatic", songs, "Logical songs is missing a plando song: Lunatic")
|
||||
self.assertIn("Out of Sense", songs, "Logical songs is missing a plando song: Out of Sense")
|
||||
self.assertIn("Magic Knight Girl", songs, "Logical songs is missing a plando song: Magic Knight Girl")
|
||||
|
||||
|
||||
class TestFilteredPlandoSettings(MuseDashTestBase):
|
||||
options = {
|
||||
"additional_song_count": 15,
|
||||
"dlc_packs": {"MSR Anthology"},
|
||||
"include_songs": [
|
||||
"Operation Blade",
|
||||
"Autumn Moods",
|
||||
|
@ -13,15 +38,15 @@ class TestPlandoSettings(MuseDashTestBase):
|
|||
}
|
||||
|
||||
def test_included_songs_didnt_grow_item_count(self) -> None:
|
||||
muse_dash_world = self.multiworld.worlds[1]
|
||||
self.assertEqual(len(muse_dash_world.included_songs), 15,
|
||||
f"Logical songs size grew when it shouldn't. Expected 15. Got {len(muse_dash_world.included_songs)}")
|
||||
muse_dash_world = self.get_world()
|
||||
self.assertEqual(len(muse_dash_world.included_songs), 15, "Logical songs size grew when it shouldn't.")
|
||||
|
||||
def test_included_songs_plando(self) -> None:
|
||||
muse_dash_world = self.multiworld.worlds[1]
|
||||
# Tests for excluding included songs when the right dlc isn't enabled
|
||||
def test_filtered_included_songs_plando(self) -> None:
|
||||
muse_dash_world = self.get_world()
|
||||
songs = muse_dash_world.included_songs.copy()
|
||||
songs.append(muse_dash_world.victory_song_name)
|
||||
|
||||
self.assertIn("Operation Blade", songs, "Logical songs is missing a plando song: Operation Blade")
|
||||
self.assertIn("Autumn Moods", songs, "Logical songs is missing a plando song: Autumn Moods")
|
||||
self.assertIn("Fireflies", songs, "Logical songs is missing a plando song: Fireflies")
|
||||
self.assertNotIn("Fireflies", songs, "Logical songs has added a filtered a plando song: Fireflies")
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
from . import MuseDashTestBase
|
||||
|
||||
|
||||
class TestNoTraps(MuseDashTestBase):
|
||||
def test_no_traps(self) -> None:
|
||||
md_world = self.get_world()
|
||||
md_world.options.chosen_traps.value.clear()
|
||||
self.assertEqual(len(md_world.get_available_traps()), 0, "Got an available trap when we expected none.")
|
||||
|
||||
def test_all_traps(self) -> None:
|
||||
md_world = self.get_world()
|
||||
md_world.options.dlc_packs.value.add(md_world.md_collection.MUSE_PLUS_DLC)
|
||||
|
||||
for trap in md_world.md_collection.trap_items.keys():
|
||||
md_world.options.chosen_traps.value.add(trap)
|
||||
|
||||
trap_count = len(md_world.get_available_traps())
|
||||
true_count = len(md_world.md_collection.trap_items.keys())
|
||||
|
||||
self.assertEqual(trap_count, true_count, "Got a different amount of traps than what was expected.")
|
||||
|
||||
def test_exclude_sfx_traps(self) -> None:
|
||||
md_world = self.get_world()
|
||||
if "Muse Plus" in md_world.options.dlc_packs.value:
|
||||
md_world.options.dlc_packs.value.remove("Muse Plus")
|
||||
|
||||
for trap in md_world.md_collection.trap_items.keys():
|
||||
md_world.options.chosen_traps.value.add(trap)
|
||||
|
||||
trap_count = len(md_world.get_available_traps())
|
||||
true_count = len(md_world.md_collection.trap_items.keys()) - len(md_world.md_collection.sfx_trap_items)
|
||||
|
||||
self.assertEqual(trap_count, true_count, "Got a different amount of traps than what was expected.")
|
|
@ -4,30 +4,33 @@ from . import MuseDashTestBase
|
|||
# This ends up with only 25 valid songs that can be chosen.
|
||||
# These tests ensure that this won't fail generation
|
||||
|
||||
|
||||
class TestWorstCaseHighDifficulty(MuseDashTestBase):
|
||||
options = {
|
||||
"starting_song_count": 10,
|
||||
"allow_just_as_planned_dlc_songs": False,
|
||||
"dlc_packs": [],
|
||||
"streamer_mode_enabled": True,
|
||||
"song_difficulty_mode": 6,
|
||||
"song_difficulty_min": 11,
|
||||
"song_difficulty_max": 11,
|
||||
}
|
||||
|
||||
|
||||
class TestWorstCaseMidDifficulty(MuseDashTestBase):
|
||||
options = {
|
||||
"starting_song_count": 10,
|
||||
"allow_just_as_planned_dlc_songs": False,
|
||||
"dlc_packs": [],
|
||||
"streamer_mode_enabled": True,
|
||||
"song_difficulty_mode": 6,
|
||||
"song_difficulty_min": 6,
|
||||
"song_difficulty_max": 6,
|
||||
}
|
||||
|
||||
|
||||
class TestWorstCaseLowDifficulty(MuseDashTestBase):
|
||||
options = {
|
||||
"starting_song_count": 10,
|
||||
"allow_just_as_planned_dlc_songs": False,
|
||||
"dlc_packs": [],
|
||||
"streamer_mode_enabled": True,
|
||||
"song_difficulty_mode": 6,
|
||||
"song_difficulty_min": 1,
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
from test.bases import WorldTestBase
|
||||
|
||||
from .. import MuseDashWorld
|
||||
from typing import cast
|
||||
|
||||
class MuseDashTestBase(WorldTestBase):
|
||||
game = "Muse Dash"
|
||||
|
||||
def get_world(self) -> MuseDashWorld:
|
||||
return cast(MuseDashWorld, self.multiworld.worlds[1])
|
||||
|
||||
|
|
Loading…
Reference in New Issue