SML2: Send logic affecting randomiser options as slot_data (#5751)

* Send logic affecting randomiser options as slot_data

* SML2 - Universal Tracker support

* Remove debug logs

* Only check generation_is_fake once

---------

Co-authored-by: alchav <alchav@jalchavware.com>
This commit is contained in:
Oshroth
2026-04-30 05:01:23 +10:00
committed by GitHub
parent c851eff521
commit b716096a4b
2 changed files with 92 additions and 40 deletions
+79 -36
View File
@@ -56,6 +56,9 @@ class MarioLand2World(World):
web = MarioLand2WebWorld()
ut_can_gen_without_yaml = True
glitches_item_name = "ut_glitch"
item_name_groups = {
"Level Progression": {
item_name for item_name in items if item_name.endswith(("Progression", "Secret", "Secret 1", "Secret 2"))
@@ -94,42 +97,51 @@ class MarioLand2World(World):
self.max_coin_locations = {}
self.sprite_data = {}
self.coin_fragments_required = 0
self.ut = False
def generate_early(self):
self.sprite_data = deepcopy(level_sprites)
if self.options.randomize_enemies:
randomize_enemies(self.sprite_data, self.random)
if self.options.randomize_platforms:
randomize_platforms(self.sprite_data, self.random)
if self.options.marios_castle_midway_bell:
self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell"
if self.options.auto_scroll_chances == "vanilla":
self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)]
if hasattr(self.multiworld, "re_gen_passthrough") and self.game in self.multiworld.re_gen_passthrough:
self.ut = True
for key, value in self.multiworld.re_gen_passthrough[self.game].items():
if hasattr(self.options, key):
getattr(self.options, key).value = value
else:
setattr(self, key, value)
else:
self.auto_scroll_levels = [int(self.random.randint(1, 100) <= self.options.auto_scroll_chances)
for _ in range(32)]
self.sprite_data = deepcopy(level_sprites)
self.auto_scroll_levels[level_name_to_id["Mario's Castle"]] = 0
unbeatable_scroll_levels = ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"]
if not self.options.shuffle_midway_bells:
unbeatable_scroll_levels.append("Pumpkin Zone 1")
for level, i in enumerate(self.auto_scroll_levels):
if i == 1:
if self.options.auto_scroll_mode in ("global_cancel_item", "level_cancel_items"):
self.auto_scroll_levels[level] = 2
elif self.options.auto_scroll_mode == "chaos":
if (self.options.accessibility == "full"
and level_id_to_name[level] in unbeatable_scroll_levels):
if self.options.marios_castle_midway_bell:
self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell"
if self.options.randomize_enemies:
randomize_enemies(self.sprite_data, self.random)
if self.options.randomize_platforms:
randomize_platforms(self.sprite_data, self.random)
if self.options.auto_scroll_chances == "vanilla":
self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)]
else:
self.auto_scroll_levels = [int(self.random.randint(1, 100) <= self.options.auto_scroll_chances)
for _ in range(32)]
self.auto_scroll_levels[level_name_to_id["Mario's Castle"]] = 0
unbeatable_scroll_levels = ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"]
if not self.options.shuffle_midway_bells:
unbeatable_scroll_levels.append("Pumpkin Zone 1")
for level, i in enumerate(self.auto_scroll_levels):
if i == 1:
if self.options.auto_scroll_mode in ("global_cancel_item", "level_cancel_items"):
self.auto_scroll_levels[level] = 2
else:
self.auto_scroll_levels[level] = self.random.randint(1, 3)
elif (self.options.accessibility == "full"
and level_id_to_name[level] in unbeatable_scroll_levels):
self.auto_scroll_levels[level] = 0
if self.auto_scroll_levels[level] == 1 and "trap" in self.options.auto_scroll_mode.current_key:
self.auto_scroll_levels[level] = 3
elif self.options.auto_scroll_mode == "chaos":
if (self.options.accessibility == "full"
and level_id_to_name[level] in unbeatable_scroll_levels):
self.auto_scroll_levels[level] = 2
else:
self.auto_scroll_levels[level] = self.random.randint(1, 3)
elif (self.options.accessibility == "full"
and level_id_to_name[level] in unbeatable_scroll_levels):
self.auto_scroll_levels[level] = 0
if self.auto_scroll_levels[level] == 1 and "trap" in self.options.auto_scroll_mode.current_key:
self.auto_scroll_levels[level] = 3
def create_regions(self):
menu_region = Region("Menu", self.player, self.multiworld)
@@ -178,7 +190,10 @@ class MarioLand2World(World):
wario.place_locked_item(MarioLand2Item("Wario Defeated", ItemClassification.progression, None, self.player))
if self.options.coinsanity:
coinsanity_checks = self.options.coinsanity_checks.value
if hasattr(self.multiworld, "generation_is_fake"):
coinsanity_checks = self.options.coinsanity_checks.range_end
else:
coinsanity_checks = self.options.coinsanity_checks.value
self.num_coin_locations = [[region, 1] for region in created_regions if region != "Mario's Castle"]
self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions
if region != "Mario's Castle"}
@@ -423,12 +438,40 @@ class MarioLand2World(World):
self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)]
def fill_slot_data(self):
return {
"energy_link": self.options.energy_link.value
}
# Expose settings for accurate tracker logic
shark_count: int = [
self.multiworld.worlds[self.player].sprite_data["Turtle Zone 1"][i]["sprite"]
for i in (27, 28)
].count("Shark")
crane_count: int = [
self.multiworld.worlds[self.player].sprite_data["Mario Zone 3"][i]["sprite"]
for i in (17, 18, 25)
].count("Claw Grabber")
options_dict = self.options.as_dict(
"energy_link",
"shuffle_golden_coins",
"required_golden_coins",
"coinsanity",
"shuffle_midway_bells",
"marios_castle_midway_bell",
"shuffle_pipe_traversal",
"auto_scroll_mode"
)
options_dict.update({
"auto_scroll_levels": self.auto_scroll_levels,
"turtle_zone_1_shark_count": shark_count,
"mario_zone_3_crane_count": crane_count,
"coin_fragments_required": self.coin_fragments_required
})
return options_dict
@staticmethod
def interpret_slot_data(slot_data):
return slot_data
def create_item(self, name: str) -> Item:
return MarioLand2Item(name, items[name], self.item_name_to_id[name], self.player)
return MarioLand2Item(name, items[name] if name in items else ItemClassification.progression,
self.item_name_to_id[name] if name in self.item_name_to_id else None, self.player)
def get_filler_item_name(self):
return "1 Coin"
+13 -4
View File
@@ -5,6 +5,8 @@ def is_auto_scroll(state, player, level):
level_id = level_name_to_id[level]
if state.has_any(["Cancel Auto Scroll", f"Cancel Auto Scroll - {level}"], player):
return False
if state.has("ut_glitch", player) and state.multiworld.worlds[player].auto_scroll_levels[level_id] == 3:
return state.has(f"Auto Scroll - {level}", player)
return state.multiworld.worlds[player].auto_scroll_levels[level_id] > 0
@@ -287,8 +289,12 @@ def mario_zone_3_coins(state, player, coins):
if state.has("Carrot", player):
reachable_spike_coins = 15
else:
sprites = state.multiworld.worlds[player].sprite_data["Mario Zone 3"]
reachable_spike_coins = min(3, len({sprites[i]["sprite"] == "Claw Grabber" for i in (17, 18, 25)})
if state.multiworld.worlds[player].ut:
claw_grabbers = state.multiworld.worlds[player].mario_zone_3_crane_count
else:
sprites = state.multiworld.worlds[player].sprite_data["Mario Zone 3"]
claw_grabbers = len({sprites[i]["sprite"] == "Claw Grabber" for i in (17, 18, 25)})
reachable_spike_coins = min(3, claw_grabbers
+ state.has("Mushroom", player) + state.has("Fire Flower", player)) * 5
reachable_coins += reachable_spike_coins
if not auto_scroll:
@@ -309,8 +315,11 @@ def mario_zone_4_coins(state, player, coins):
def not_blocked_by_sharks(state, player):
sharks = [state.multiworld.worlds[player].sprite_data["Turtle Zone 1"][i]["sprite"]
for i in (27, 28)].count("Shark")
if state.multiworld.worlds[player].ut:
sharks = state.multiworld.worlds[player].turtle_zone_1_shark_count
else:
sharks = [state.multiworld.worlds[player].sprite_data["Turtle Zone 1"][i]["sprite"]
for i in (27, 28)].count("Shark")
if state.has("Carrot", player) or not sharks:
return True
if sharks == 2: