From ed422e24cf84982f050eebb3d9f69758a51df7db Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 00:16:42 -0400 Subject: [PATCH 1/9] Fix interpret rules to correct check against new property "requirements_logic_any." --- worlds/sms/sms_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index 1cacb5204094..7db8e996bc09 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -225,7 +225,7 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): for sms_loc in sms_reg.locations: # Skip any event based locations that do not have this attribute if hasattr(sms_loc, "loc_reqs"): - interpret_requirements(sms_loc, sms_loc.loc_reqs, world) + interpret_requirements(sms_loc, sms_loc.loc_reqs, world, sms_loc.requirements_logic_any) # A Region cannot have its own ticket item in ticket mode, so prevent that. if hasattr(sms_reg, "ticket_str") or region_ticket: From 876c08a3597711a37b1f36cba7eeac4979c00385 Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 01:46:48 -0400 Subject: [PATCH 2/9] Requirements should always parse everything together. Implemented custom add_rule just to add parenthesis aaround the old rule because...reasons thanks python -_- --- worlds/sms/regions.py | 2 - worlds/sms/sms_regions/sms_region_helper.py | 3 - worlds/sms/sms_rules.py | 97 ++++++--------------- 3 files changed, 26 insertions(+), 76 deletions(-) diff --git a/worlds/sms/regions.py b/worlds/sms/regions.py index 2db1a05e16a1..10fefb07c73a 100644 --- a/worlds/sms/regions.py +++ b/worlds/sms/regions.py @@ -238,7 +238,6 @@ def create_region(region: SmsRegion, world: "SmsWorld"): f"{curr_region.name} - {shine.name}", region, get_correct_requirements(shine, world.options.difficulty), - True, ) if region.trade: shine_loc.trades_req = True @@ -251,7 +250,6 @@ def create_region(region: SmsRegion, world: "SmsWorld"): f"{curr_region.name} - {blue_coin.name}", region, get_correct_requirements(blue_coin, world.options.difficulty), - True, ) if world.options.blue_coin_sanity.value != 1: curr_region.add_event( diff --git a/worlds/sms/sms_regions/sms_region_helper.py b/worlds/sms/sms_regions/sms_region_helper.py index 2b9ec7f32752..d3f8b8c4e67d 100644 --- a/worlds/sms/sms_regions/sms_region_helper.py +++ b/worlds/sms/sms_regions/sms_region_helper.py @@ -14,7 +14,6 @@ class SmsLocation(Location): sms_region: "SmsRegion" loc_reqs: list["Requirements"] corona: bool - requirements_logic_any: bool def __init__( self, @@ -22,13 +21,11 @@ def __init__( name: str, parent_region: "SmsRegion", reqs: list["Requirements"], - requirements_logic_any: bool = False, ): self.address = world.location_name_to_id[name] self.loc_reqs = reqs self.sms_region = parent_region self.corona = False if not reqs else any(loc_req.corona for loc_req in reqs) - self.requirements_logic_any = requirements_logic_any if parent_region.requirements: self.corona = self.corona or any( reg_loc.corona for reg_loc in parent_region.requirements diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index 7db8e996bc09..201f0b167092 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -1,8 +1,8 @@ from typing import TYPE_CHECKING, Callable -from BaseClasses import Entrance, CollectionState +from BaseClasses import Entrance, CollectionState, CollectionRule from .sms_regions.sms_region_helper import SmsLocation, Requirements -from ..generic.Rules import set_rule, add_rule, add_item_rule +from ..generic.Rules import set_rule, add_item_rule from .items import TICKET_ITEMS, REGULAR_PROGRESSION_ITEMS if TYPE_CHECKING: @@ -13,7 +13,6 @@ def interpret_requirements( spot: Entrance | SmsLocation, requirement_set: list[Requirements], world: "SmsWorld", - apply_requirements_any: bool = False, ) -> None: """Correctly applies and interprets custom requirements namedtuple for a given entrance/location.""" # If a region/location does not have any items required, make the section(s) return no logic. @@ -119,9 +118,10 @@ def interpret_requirements( ) if ( - single_req.corona - or (hasattr(spot, "corona") and spot.corona) - and world.corona_mountain_shines > 0 + world.corona_mountain_shines > 0 and ( + single_req.corona or + (hasattr(spot, "corona") and spot.corona) + ) ): # Player requires all shine sprites that are required to reach corona mountain as well. req_rules.append( @@ -134,75 +134,30 @@ def interpret_requirements( if not req_rules: continue - if apply_requirements_any: - if ( - spot.access_rule is SmsLocation.access_rule - or spot.access_rule is Entrance.access_rule - ): - set_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): any( - req_rule(state) for req_rule in all_rules - ) - ), - ) - else: - if isinstance(spot, SmsLocation): - add_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): any( - req_rule(state) for req_rule in all_rules - ) - ), - combine="or", - ) - else: - add_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): any( - req_rule(state) for req_rule in all_rules - ) - ), - ) + if spot.access_rule is SmsLocation.access_rule or spot.access_rule is Entrance.access_rule: + set_rule(spot, (lambda state, all_rules=tuple(req_rules): all(req_rule(state) for req_rule in all_rules))) else: - if ( - spot.access_rule is SmsLocation.access_rule - or spot.access_rule is Entrance.access_rule - ): - set_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): all( - req_rule(state) for req_rule in all_rules - ) - ), - ) + if isinstance(spot, SmsLocation): + sms_add_rule(spot, (lambda state, all_rules=tuple(req_rules): + all(req_rule(state) for req_rule in req_rules)), combine="or") else: - if isinstance(spot, SmsLocation): - add_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): all( - req_rule(state) for req_rule in all_rules - ) - ), - combine="or", - ) - else: - add_rule( - spot, - ( - lambda state, all_rules=tuple(req_rules): all( - req_rule(state) for req_rule in all_rules - ) - ), - ) + sms_add_rule(spot, + (lambda state, all_rules=tuple(req_rules): all(req_rule(state) for req_rule in req_rules))) return +def sms_add_rule(spot: SmsLocation | Entrance, rule: CollectionRule, combine="and"): + old_rule = spot.access_rule + # empty rule, replace instead of add + if old_rule is SmsLocation.access_rule or old_rule is Entrance.access_rule: + spot.access_rule = rule if combine == "and" else old_rule + else: + if combine == "and": + spot.access_rule = lambda state: rule(state) and old_rule(state) + else: + spot.access_rule = lambda state: (rule(state)) or old_rule(state) + + def create_sms_region_and_entrance_rules(world: "SmsWorld"): for sms_reg in world.get_regions(): region_ticket: str = "" @@ -225,7 +180,7 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): for sms_loc in sms_reg.locations: # Skip any event based locations that do not have this attribute if hasattr(sms_loc, "loc_reqs"): - interpret_requirements(sms_loc, sms_loc.loc_reqs, world, sms_loc.requirements_logic_any) + interpret_requirements(sms_loc, sms_loc.loc_reqs, world) # A Region cannot have its own ticket item in ticket mode, so prevent that. if hasattr(sms_reg, "ticket_str") or region_ticket: From a5dbb8ca24eeff92d0ee4d9eb4f5adb324999bbc Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 18:33:21 -0400 Subject: [PATCH 3/9] Requirements for NOZZLE RULES had a lambda capture issue. Removed the item_rules for Pianta Village and Sirena, as they dont seem to be affecting logic anymore. --- worlds/sms/sms_rules.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index 201f0b167092..3898b9541480 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -76,7 +76,7 @@ def interpret_requirements( ), current_rule=nozz_rule: current_rule(state) or state.has_all( item_set, world.player ) - req_rules.append(lambda state: nozz_rule(state)) + req_rules.append(lambda state, captured_rule=nozz_rule: captured_rule(state)) if single_req.shines and world.corona_mountain_shines > 0: # Requires X amount of shine sprites to access @@ -231,28 +231,3 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): ) ), ) - - # Force Rocket to never be in Pianta Village - if "Pianta" in sms_reg.name: - add_item_rule( - sms_loc, - ( - lambda item: item.game != world.game - or ( - item.game == world.game and item.name != "Rocket Nozzle" - ) - ), - ) - - # Force Yoshi never to appear in Sirena 3+ - if "Sirena" in sms_reg.name and not sms_reg.name in [ - "Sirena 1 and 6", - "Sirena 2-8", - ]: - add_item_rule( - sms_loc, - ( - lambda item: item.game != world.game - or (item.game == world.game and item.name != "Yoshi") - ), - ) From 58e52390ac89bdd989f4e4563e2e46facef0f44d Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 18:54:10 -0400 Subject: [PATCH 4/9] remove trailing comma, creating an un-desired tuple. --- worlds/sms/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sms/__init__.py b/worlds/sms/__init__.py index ed38d012c82f..65616a4df5ad 100644 --- a/worlds/sms/__init__.py +++ b/worlds/sms/__init__.py @@ -292,7 +292,7 @@ def fill_slot_data(self) -> Dict[str, Any]: slot_data[child_option] = getattr(self.options, child_option).value slot_data["death_link"] = self.options.death_link.value - slot_data["ticket_chosen"] = self.ticket_chosen, + slot_data["ticket_chosen"] = self.ticket_chosen slot_data["seed"] = str(self.multiworld.seed_name) return slot_data From 5d07c6c4bcf38614c7358caa98cb448e4a54ef9e Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 18:55:45 -0400 Subject: [PATCH 5/9] remove a deprecated world property. --- worlds/sms/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/worlds/sms/__init__.py b/worlds/sms/__init__.py index 65616a4df5ad..abfdd70b950a 100644 --- a/worlds/sms/__init__.py +++ b/worlds/sms/__init__.py @@ -78,8 +78,6 @@ class SmsWorld(World): game = "Super Mario Sunshine" web = SmsWebWorld() - data_version = 1 - options_dataclass = SmsOptions options: SmsOptions From ecff1696bfbc3512fcdc660c2b37233528d6b3b2 Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 19:18:44 -0400 Subject: [PATCH 6/9] Optimized item_rule add/call. --- worlds/sms/sms_rules.py | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index 3898b9541480..e2155a5a7875 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -201,33 +201,16 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): if hasattr(sms_loc, "corona") and sms_loc.corona: # Since Corona requires Spray Nozzle and Hover Nozzle to complete, ensure those items can never # be placed. Additionally, Yoshi is required for basically every late game stage. - required_nozz: list[str] = [ - "Spray Nozzle", - "Hover Nozzle", - *TICKET_ITEMS, - ] - add_item_rule( - sms_loc, - ( - lambda item, nozzles=tuple(required_nozz): item.game - != world.game - or ( - item.game == world.game - and not item.name in required_nozz - ) - ), - ) + blocked: set[str] = {"Spray Nozzle", "Hover Nozzle", *TICKET_ITEMS} # If there is a high amount of progression items, the world is too restrictive for non-macguffin # items to be placed in corona, especially with previous levels required to be beaten. if world.large_shine_count: - add_item_rule( - sms_loc, - ( - lambda item: item.game != world.game - or ( - item.game == world.game - and not item.name in REGULAR_PROGRESSION_ITEMS - ) - ), + blocked |= set(REGULAR_PROGRESSION_ITEMS.keys()) + + add_item_rule( + sms_loc, + lambda item, blocked_items=frozenset(sorted(blocked)): ( + item.game != world.game or item.name not in blocked_items ) + ) From 12a288f9044f00c86d70bca3472b5ff301156f1c Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 19:19:40 -0400 Subject: [PATCH 7/9] Only add ticket requirement if level access is ticket mode. --- worlds/sms/sms_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index e2155a5a7875..c12484213a8d 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -183,7 +183,7 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): interpret_requirements(sms_loc, sms_loc.loc_reqs, world) # A Region cannot have its own ticket item in ticket mode, so prevent that. - if hasattr(sms_reg, "ticket_str") or region_ticket: + if world.options.level_access.value == 1 and hasattr(sms_reg, "ticket_str") or region_ticket: reg_ticket: str = ( sms_reg.ticket_str if hasattr(sms_reg, "ticket_str") From e0da22f59cb05564195008c8ae3596c66f841948 Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 19:25:36 -0400 Subject: [PATCH 8/9] Ensure the correct region ticket is grabbed from the correct entrance, then break once the first instance is found. --- worlds/sms/sms_rules.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index c12484213a8d..ebdfffb938cd 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -170,11 +170,14 @@ def create_sms_region_and_entrance_rules(world: "SmsWorld"): ) # If a parent region has any ticket str, get that ticket as well - if ( - hasattr(sms_entrance.parent_region, "ticket_str") + if (hasattr(sms_entrance.parent_region, "ticket_str") and sms_entrance.parent_region.ticket_str + and sms_entrance.parent_region == world.get_region( + sms_entrance.parent_region.name + ) ): region_ticket = sms_entrance.parent_region.ticket_str + break # Add the location rules within this region. for sms_loc in sms_reg.locations: From 892cef091c940f8ef550454be4181872eff43009 Mon Sep 17 00:00:00 2001 From: SomeJakeGuy Date: Fri, 3 Apr 2026 19:35:24 -0400 Subject: [PATCH 9/9] add_rule from AP used again instead of some un-necessary custom implementation. --- worlds/sms/sms_rules.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/worlds/sms/sms_rules.py b/worlds/sms/sms_rules.py index ebdfffb938cd..b643cb46bffe 100644 --- a/worlds/sms/sms_rules.py +++ b/worlds/sms/sms_rules.py @@ -1,8 +1,8 @@ from typing import TYPE_CHECKING, Callable -from BaseClasses import Entrance, CollectionState, CollectionRule +from BaseClasses import Entrance, CollectionState from .sms_regions.sms_region_helper import SmsLocation, Requirements -from ..generic.Rules import set_rule, add_item_rule +from ..generic.Rules import set_rule, add_item_rule, add_rule from .items import TICKET_ITEMS, REGULAR_PROGRESSION_ITEMS if TYPE_CHECKING: @@ -138,26 +138,13 @@ def interpret_requirements( set_rule(spot, (lambda state, all_rules=tuple(req_rules): all(req_rule(state) for req_rule in all_rules))) else: if isinstance(spot, SmsLocation): - sms_add_rule(spot, (lambda state, all_rules=tuple(req_rules): + add_rule(spot, (lambda state, all_rules=tuple(req_rules): all(req_rule(state) for req_rule in req_rules)), combine="or") else: - sms_add_rule(spot, + add_rule(spot, (lambda state, all_rules=tuple(req_rules): all(req_rule(state) for req_rule in req_rules))) return - -def sms_add_rule(spot: SmsLocation | Entrance, rule: CollectionRule, combine="and"): - old_rule = spot.access_rule - # empty rule, replace instead of add - if old_rule is SmsLocation.access_rule or old_rule is Entrance.access_rule: - spot.access_rule = rule if combine == "and" else old_rule - else: - if combine == "and": - spot.access_rule = lambda state: rule(state) and old_rule(state) - else: - spot.access_rule = lambda state: (rule(state)) or old_rule(state) - - def create_sms_region_and_entrance_rules(world: "SmsWorld"): for sms_reg in world.get_regions(): region_ticket: str = ""