From b716096a4b16d8d2aab9717859de0ecebbc087ac Mon Sep 17 00:00:00 2001 From: Oshroth Date: Thu, 30 Apr 2026 05:01:23 +1000 Subject: [PATCH] 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 --- worlds/marioland2/__init__.py | 115 +++++++++++++++++++++++----------- worlds/marioland2/logic.py | 17 +++-- 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index ea1354db6e..82ce2ea5b0 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -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" diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 4ccb2eb4c9..ea4fc7cdd3 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -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: