@@ -57,6 +57,8 @@ class MuseDashWorld(World):
57
57
58
58
# Necessary Data
59
59
md_collection = MuseDashCollections ()
60
+ filler_item_names = list (md_collection .filler_item_weights .keys ())
61
+ filler_item_weights = list (md_collection .filler_item_weights .values ())
60
62
61
63
item_name_to_id = {name : code for name , code in md_collection .item_names_to_id .items ()}
62
64
location_name_to_id = {name : code for name , code in md_collection .location_names_to_id .items ()}
@@ -70,7 +72,7 @@ class MuseDashWorld(World):
70
72
71
73
def generate_early (self ):
72
74
dlc_songs = {key for key in self .options .dlc_packs .value }
73
- if ( self .options .allow_just_as_planned_dlc_songs .value ) :
75
+ if self .options .allow_just_as_planned_dlc_songs .value :
74
76
dlc_songs .add (self .md_collection .MUSE_PLUS_DLC )
75
77
76
78
streamer_mode = self .options .streamer_mode_enabled
@@ -84,7 +86,7 @@ def generate_early(self):
84
86
while True :
85
87
# In most cases this should only need to run once
86
88
available_song_keys = self .md_collection .get_songs_with_settings (
87
- dlc_songs , streamer_mode , lower_diff_threshold , higher_diff_threshold )
89
+ dlc_songs , bool ( streamer_mode . value ) , lower_diff_threshold , higher_diff_threshold )
88
90
89
91
available_song_keys = self .handle_plando (available_song_keys )
90
92
@@ -161,19 +163,17 @@ def create_song_pool(self, available_song_keys: List[str]):
161
163
break
162
164
self .included_songs .append (available_song_keys .pop ())
163
165
164
- self .location_count = len (self .starting_songs ) + len (self .included_songs )
165
- location_multiplier = 1 + (self .get_additional_item_percentage () / 100.0 )
166
- self .location_count = floor (self .location_count * location_multiplier )
167
-
168
- minimum_location_count = len (self .included_songs ) + self .get_music_sheet_count ()
169
- if self .location_count < minimum_location_count :
170
- self .location_count = minimum_location_count
166
+ self .location_count = 2 * (len (self .starting_songs ) + len (self .included_songs ))
171
167
172
168
def create_item (self , name : str ) -> Item :
173
169
if name == self .md_collection .MUSIC_SHEET_NAME :
174
170
return MuseDashFixedItem (name , ItemClassification .progression_skip_balancing ,
175
171
self .md_collection .MUSIC_SHEET_CODE , self .player )
176
172
173
+ filler = self .md_collection .filler_items .get (name )
174
+ if filler :
175
+ return MuseDashFixedItem (name , ItemClassification .filler , filler , self .player )
176
+
177
177
trap = self .md_collection .vfx_trap_items .get (name )
178
178
if trap :
179
179
return MuseDashFixedItem (name , ItemClassification .trap , trap , self .player )
@@ -189,6 +189,9 @@ def create_item(self, name: str) -> Item:
189
189
song = self .md_collection .song_items .get (name )
190
190
return MuseDashSongItem (name , self .player , song )
191
191
192
+ def get_filler_item_name (self ) -> str :
193
+ return self .random .choices (self .filler_item_names , self .filler_item_weights )[0 ]
194
+
192
195
def create_items (self ) -> None :
193
196
song_keys_in_pool = self .included_songs .copy ()
194
197
@@ -199,8 +202,13 @@ def create_items(self) -> None:
199
202
for _ in range (0 , item_count ):
200
203
self .multiworld .itempool .append (self .create_item (self .md_collection .MUSIC_SHEET_NAME ))
201
204
202
- # Then add all traps
203
- trap_count = self .get_trap_count ()
205
+ # Then add 1 copy of every song
206
+ item_count += len (self .included_songs )
207
+ for song in self .included_songs :
208
+ self .multiworld .itempool .append (self .create_item (song ))
209
+
210
+ # Then add all traps, making sure we don't over fill
211
+ trap_count = min (self .location_count - item_count , self .get_trap_count ())
204
212
trap_list = self .get_available_traps ()
205
213
if len (trap_list ) > 0 and trap_count > 0 :
206
214
for _ in range (0 , trap_count ):
@@ -209,23 +217,38 @@ def create_items(self) -> None:
209
217
210
218
item_count += trap_count
211
219
212
- # Next fill all remaining slots with song items
213
- needed_item_count = self .location_count
214
- while item_count < needed_item_count :
215
- # If we have more items needed than keys, just iterate the list and add them all
216
- if len (song_keys_in_pool ) <= needed_item_count - item_count :
217
- for key in song_keys_in_pool :
218
- self .multiworld .itempool .append (self .create_item (key ))
220
+ # At this point, if a player is using traps, it's possible that they have filled all locations
221
+ items_left = self .location_count - item_count
222
+ if items_left <= 0 :
223
+ return
224
+
225
+ # When it comes to filling remaining spaces, we have 2 options. A useless filler or additional songs.
226
+ # First fill 50% with the filler. The rest is to be duplicate songs.
227
+ filler_count = floor (0.5 * items_left )
228
+ items_left -= filler_count
219
229
220
- item_count += len ( song_keys_in_pool )
221
- continue
230
+ for _ in range ( 0 , filler_count ):
231
+ self . multiworld . itempool . append ( self . create_item ( self . get_filler_item_name ()))
222
232
223
- # Otherwise add a random assortment of songs
224
- self .random .shuffle (song_keys_in_pool )
225
- for i in range (0 , needed_item_count - item_count ):
226
- self .multiworld .itempool .append (self .create_item (song_keys_in_pool [i ]))
233
+ # All remaining spots are filled with duplicate songs. Duplicates are set to useful instead of progression
234
+ # to cut down on the number of progression items that Muse Dash puts into the pool.
227
235
228
- item_count = needed_item_count
236
+ # This is for the extraordinary case of needing to fill a lot of items.
237
+ while items_left > len (song_keys_in_pool ):
238
+ for key in song_keys_in_pool :
239
+ item = self .create_item (key )
240
+ item .classification = ItemClassification .useful
241
+ self .multiworld .itempool .append (item )
242
+
243
+ items_left -= len (song_keys_in_pool )
244
+ continue
245
+
246
+ # Otherwise add a random assortment of songs
247
+ self .random .shuffle (song_keys_in_pool )
248
+ for i in range (0 , items_left ):
249
+ item = self .create_item (song_keys_in_pool [i ])
250
+ item .classification = ItemClassification .useful
251
+ self .multiworld .itempool .append (item )
229
252
230
253
def create_regions (self ) -> None :
231
254
menu_region = Region ("Menu" , self .player , self .multiworld )
@@ -245,19 +268,18 @@ def create_regions(self) -> None:
245
268
self .random .shuffle (included_song_copy )
246
269
all_selected_locations .extend (included_song_copy )
247
270
248
- two_item_location_count = self .location_count - len (all_selected_locations )
249
-
250
271
# Make a region per song/album, then adds 1-2 item locations to them
251
272
for i in range (0 , len (all_selected_locations )):
252
273
name = all_selected_locations [i ]
253
274
region = Region (name , self .player , self .multiworld )
254
275
self .multiworld .regions .append (region )
255
276
song_select_region .connect (region , name , lambda state , place = name : state .has (place , self .player ))
256
277
257
- # Up to 2 Locations are defined per song
258
- region .add_locations ({name + "-0" : self .md_collection .song_locations [name + "-0" ]}, MuseDashLocation )
259
- if i < two_item_location_count :
260
- region .add_locations ({name + "-1" : self .md_collection .song_locations [name + "-1" ]}, MuseDashLocation )
278
+ # Muse Dash requires 2 locations per song to be *interesting*. Balanced out by filler.
279
+ region .add_locations ({
280
+ name + "-0" : self .md_collection .song_locations [name + "-0" ],
281
+ name + "-1" : self .md_collection .song_locations [name + "-1" ]
282
+ }, MuseDashLocation )
261
283
262
284
def set_rules (self ) -> None :
263
285
self .multiworld .completion_condition [self .player ] = lambda state : \
@@ -276,19 +298,14 @@ def get_available_traps(self) -> List[str]:
276
298
277
299
return trap_list
278
300
279
- def get_additional_item_percentage (self ) -> int :
280
- trap_count = self .options .trap_count_percentage .value
281
- song_count = self .options .music_sheet_count_percentage .value
282
- return max (trap_count + song_count , self .options .additional_item_percentage .value )
283
-
284
301
def get_trap_count (self ) -> int :
285
302
multiplier = self .options .trap_count_percentage .value / 100.0
286
- trap_count = ( len (self .starting_songs ) * 2 ) + len (self .included_songs )
303
+ trap_count = len (self .starting_songs ) + len (self .included_songs )
287
304
return max (0 , floor (trap_count * multiplier ))
288
305
289
306
def get_music_sheet_count (self ) -> int :
290
307
multiplier = self .options .music_sheet_count_percentage .value / 100.0
291
- song_count = ( len (self .starting_songs ) * 2 ) + len (self .included_songs )
308
+ song_count = len (self .starting_songs ) + len (self .included_songs )
292
309
return max (1 , floor (song_count * multiplier ))
293
310
294
311
def get_music_sheet_win_count (self ) -> int :
@@ -329,5 +346,4 @@ def fill_slot_data(self):
329
346
"deathLink" : self .options .death_link .value ,
330
347
"musicSheetWinCount" : self .get_music_sheet_win_count (),
331
348
"gradeNeeded" : self .options .grade_needed .value ,
332
- "hasFiller" : True ,
333
349
}
0 commit comments