diff --git a/worlds/jakanddaxter/Locations.py b/worlds/jakanddaxter/Locations.py index 501a864316..a5b7f71305 100644 --- a/worlds/jakanddaxter/Locations.py +++ b/worlds/jakanddaxter/Locations.py @@ -10,13 +10,6 @@ from .locs import (OrbLocations as Orbs, class JakAndDaxterLocation(Location): game: str = jak1_name - # In AP 0.5.0, the base Location.can_reach function had its two boolean conditions swapped for a faster - # short-circuit for better performance. However, Jak seeds actually generate faster using the older method, - # which has been re-implemented below. - def can_reach(self, state: CollectionState) -> bool: - assert self.parent_region, "Can't reach location without region" - return self.parent_region.can_reach(state) and self.access_rule(state) - # Different tables for location groups. # Each Item ID == its corresponding Location ID. While we're here, do all the ID conversions needed. diff --git a/worlds/jakanddaxter/Regions.py b/worlds/jakanddaxter/Regions.py index 9c7322dfc8..38f2f77fd8 100644 --- a/worlds/jakanddaxter/Regions.py +++ b/worlds/jakanddaxter/Regions.py @@ -55,10 +55,11 @@ def create_regions(world: JakAndDaxterWorld): for bundle_index in range(bundle_count): # Unlike Per-Level Orbsanity, Global Orbsanity Locations always have a level_index of 16. + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(16, bundle_index, - access_rule=lambda state, bundle=bundle_index: - can_reach_orbs_global(state, player, world, bundle)) + access_rule=lambda state, orb_amount=amount: + can_reach_orbs_global(state, player, world, orb_amount)) multiworld.regions.append(orbs) menu.connect(orbs) diff --git a/worlds/jakanddaxter/Rules.py b/worlds/jakanddaxter/Rules.py index 5aa3d041ca..56dd0ec848 100644 --- a/worlds/jakanddaxter/Rules.py +++ b/worlds/jakanddaxter/Rules.py @@ -29,27 +29,26 @@ def set_orb_trade_rule(world: JakAndDaxterWorld): def recalculate_reachable_orbs(state: CollectionState, player: int, world: JakAndDaxterWorld) -> None: - if not state.prog_items[player]["Reachable Orbs Fresh"]: + # Recalculate every level, every time the cache is stale, because you don't know + # when a specific bundle of orbs in one level may unlock access to another. + accessible_total_orbs = 0 + for level in level_table: + accessible_level_orbs = count_reachable_orbs_level(state, world, level) + accessible_total_orbs += accessible_level_orbs + state.prog_items[player][f"{level} Reachable Orbs".lstrip()] = accessible_level_orbs - # Recalculate every level, every time the cache is stale, because you don't know - # when a specific bundle of orbs in one level may unlock access to another. - for level in level_table: - state.prog_items[player][f"{level} Reachable Orbs".strip()] = ( - count_reachable_orbs_level(state, world, level)) - - # Also recalculate the global count, still used even when Orbsanity is Off. - state.prog_items[player]["Reachable Orbs"] = count_reachable_orbs_global(state, world) - state.prog_items[player]["Reachable Orbs Fresh"] = True + # Also recalculate the global count, still used even when Orbsanity is Off. + state.prog_items[player]["Reachable Orbs"] = accessible_total_orbs + state.prog_items[player]["Reachable Orbs Fresh"] = True def count_reachable_orbs_global(state: CollectionState, world: JakAndDaxterWorld) -> int: accessible_orbs = 0 - for level_regions in world.level_to_regions.values(): + for level_regions in world.level_to_orb_regions.values(): for region in level_regions: - # Rely on short-circuiting to skip region.can_reach whenever possible. - if region.orb_count > 0 and region.can_reach(state): + if region.can_reach(state): accessible_orbs += region.orb_count return accessible_orbs @@ -59,9 +58,8 @@ def count_reachable_orbs_level(state: CollectionState, level_name: str = "") -> int: accessible_orbs = 0 - for region in world.level_to_regions[level_name]: - # Rely on short-circuiting to skip region.can_reach whenever possible. - if region.orb_count > 0 and region.can_reach(state): + for region in world.level_to_orb_regions[level_name]: + if region.can_reach(state): accessible_orbs += region.orb_count return accessible_orbs @@ -69,20 +67,24 @@ def count_reachable_orbs_level(state: CollectionState, def can_reach_orbs_global(state: CollectionState, player: int, world: JakAndDaxterWorld, - bundle: int) -> bool: + orb_amount: int) -> bool: - recalculate_reachable_orbs(state, player, world) - return state.has("Reachable Orbs", player, world.orb_bundle_size * (bundle + 1)) + if not state.prog_items[player]["Reachable Orbs Fresh"]: + recalculate_reachable_orbs(state, player, world) + + return state.has("Reachable Orbs", player, orb_amount) def can_reach_orbs_level(state: CollectionState, player: int, world: JakAndDaxterWorld, level_name: str, - bundle: int) -> bool: + orb_amount: int) -> bool: - recalculate_reachable_orbs(state, player, world) - return state.has(f"{level_name} Reachable Orbs", player, world.orb_bundle_size * (bundle + 1)) + if not state.prog_items[player]["Reachable Orbs Fresh"]: + recalculate_reachable_orbs(state, player, world) + + return state.has(f"{level_name} Reachable Orbs", player, orb_amount) def can_trade_vanilla(state: CollectionState, @@ -92,7 +94,9 @@ def can_trade_vanilla(state: CollectionState, required_previous_trade: typing.Optional[int] = None) -> bool: # With Orbsanity Off, Reachable Orbs are in fact Tradeable Orbs. - recalculate_reachable_orbs(state, player, world) + if not state.prog_items[player]["Reachable Orbs Fresh"]: + recalculate_reachable_orbs(state, player, world) + if required_previous_trade: name_of_previous_trade = location_table[Cells.to_ap_id(required_previous_trade)] return (state.has("Reachable Orbs", player, required_orbs) @@ -107,7 +111,9 @@ def can_trade_orbsanity(state: CollectionState, required_previous_trade: typing.Optional[int] = None) -> bool: # Yes, even Orbsanity trades may unlock access to new Reachable Orbs. - recalculate_reachable_orbs(state, player, world) + if not state.prog_items[player]["Reachable Orbs Fresh"]: + recalculate_reachable_orbs(state, player, world) + if required_previous_trade: name_of_previous_trade = location_table[Cells.to_ap_id(required_previous_trade)] return (state.has("Tradeable Orbs", player, required_orbs) diff --git a/worlds/jakanddaxter/__init__.py b/worlds/jakanddaxter/__init__.py index 7e533eb097..5bf70d33f0 100644 --- a/worlds/jakanddaxter/__init__.py +++ b/worlds/jakanddaxter/__init__.py @@ -226,14 +226,16 @@ class JakAndDaxterWorld(World): power_cell_thresholds: list[int] trap_weights: tuple[list[str], list[int]] - # Store a dictionary of levels to regions for faster access. - level_to_regions: dict[str, list[JakAndDaxterRegion]] + # Store these dictionaries for speed improvements. + level_to_regions: dict[str, list[JakAndDaxterRegion]] # Contains all levels and regions. + level_to_orb_regions: dict[str, list[JakAndDaxterRegion]] # Contains only regions which contain orbs. # Handles various options validation, rules enforcement, and caching of important information. def generate_early(self) -> None: # Initialize the level-region dictionary. self.level_to_regions = defaultdict(list) + self.level_to_orb_regions = defaultdict(list) # Cache the power cell threshold values for quicker reference. self.power_cell_thresholds = [ @@ -315,16 +317,22 @@ class JakAndDaxterWorld(World): create_regions(self) # Don't forget to add the created regions to the multiworld! - for level_regions in self.level_to_regions.values(): - self.multiworld.regions.extend(level_regions) + for level in self.level_to_regions: + self.multiworld.regions.extend(self.level_to_regions[level]) + + # As a lazy measure, let's also fill level_to_orb_regions here. + # This should help speed up orbsanity calculations. + self.level_to_orb_regions[level] = [reg for reg in self.level_to_regions[level] if reg.orb_count > 0] # from Utils import visualize_regions # visualize_regions(self.multiworld.get_region("Menu", self.player), "jakanddaxter.puml") - # Helper function to reuse some nasty if/else trees. This outputs a list of pairs of item count and classification. - # For instance, not all 101 power cells need to be marked progression if you only need 72 to beat the game. So we - # will have 72 Progression Power Cells, and 29 Filler Power Cells. def item_type_helper(self, item: int) -> list[tuple[int, ItemClass]]: + """ + Helper function to reuse some nasty if/else trees. This outputs a list of pairs of item count and classification. + For instance, not all 101 power cells need to be marked progression if you only need 72 to beat the game. So we + will have 72 Progression Power Cells, and 29 Filler Power Cells. + """ counts_and_classes: list[tuple[int, ItemClass]] = [] # Make N Power Cells. We only want AP's Progression Fill routine to handle the amount of cells we need diff --git a/worlds/jakanddaxter/regs/BoggySwampRegions.py b/worlds/jakanddaxter/regs/BoggySwampRegions.py index dff4e154de..5909a6a356 100644 --- a/worlds/jakanddaxter/regs/BoggySwampRegions.py +++ b/worlds/jakanddaxter/regs/BoggySwampRegions.py @@ -160,10 +160,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(8, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/FireCanyonRegions.py b/worlds/jakanddaxter/regs/FireCanyonRegions.py index 836a2135a2..d2d58fc83e 100644 --- a/worlds/jakanddaxter/regs/FireCanyonRegions.py +++ b/worlds/jakanddaxter/regs/FireCanyonRegions.py @@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(5, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/ForbiddenJungleRegions.py b/worlds/jakanddaxter/regs/ForbiddenJungleRegions.py index 730d621d27..d73212b029 100644 --- a/worlds/jakanddaxter/regs/ForbiddenJungleRegions.py +++ b/worlds/jakanddaxter/regs/ForbiddenJungleRegions.py @@ -90,10 +90,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt bundle_count = 150 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(3, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/GeyserRockRegions.py b/worlds/jakanddaxter/regs/GeyserRockRegions.py index 288d46f525..68094a1c46 100644 --- a/worlds/jakanddaxter/regs/GeyserRockRegions.py +++ b/worlds/jakanddaxter/regs/GeyserRockRegions.py @@ -35,10 +35,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(0, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py b/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py index 89533fc3de..1bb2c6fc45 100644 --- a/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py +++ b/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py @@ -115,10 +115,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(15, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/LavaTubeRegions.py b/worlds/jakanddaxter/regs/LavaTubeRegions.py index 7a833e18ce..cf504113b4 100644 --- a/worlds/jakanddaxter/regs/LavaTubeRegions.py +++ b/worlds/jakanddaxter/regs/LavaTubeRegions.py @@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(14, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py b/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py index 2b6acb7414..5aa603106e 100644 --- a/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py +++ b/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py @@ -142,10 +142,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(7, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/MistyIslandRegions.py b/worlds/jakanddaxter/regs/MistyIslandRegions.py index 4b16617051..72191aab1f 100644 --- a/worlds/jakanddaxter/regs/MistyIslandRegions.py +++ b/worlds/jakanddaxter/regs/MistyIslandRegions.py @@ -118,10 +118,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 150 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(4, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/MountainPassRegions.py b/worlds/jakanddaxter/regs/MountainPassRegions.py index 9f543199aa..1cf795baa0 100644 --- a/worlds/jakanddaxter/regs/MountainPassRegions.py +++ b/worlds/jakanddaxter/regs/MountainPassRegions.py @@ -53,10 +53,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(10, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/PrecursorBasinRegions.py b/worlds/jakanddaxter/regs/PrecursorBasinRegions.py index c498e30340..f7fb97180d 100644 --- a/worlds/jakanddaxter/regs/PrecursorBasinRegions.py +++ b/worlds/jakanddaxter/regs/PrecursorBasinRegions.py @@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(9, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/RockVillageRegions.py b/worlds/jakanddaxter/regs/RockVillageRegions.py index feda8a2224..7778bea35d 100644 --- a/worlds/jakanddaxter/regs/RockVillageRegions.py +++ b/worlds/jakanddaxter/regs/RockVillageRegions.py @@ -61,10 +61,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(6, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/SandoverVillageRegions.py b/worlds/jakanddaxter/regs/SandoverVillageRegions.py index fbdb6c368d..42d4e497fe 100644 --- a/worlds/jakanddaxter/regs/SandoverVillageRegions.py +++ b/worlds/jakanddaxter/regs/SandoverVillageRegions.py @@ -70,10 +70,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(1, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/SentinelBeachRegions.py b/worlds/jakanddaxter/regs/SentinelBeachRegions.py index 1ccbfe2820..8f8312baeb 100644 --- a/worlds/jakanddaxter/regs/SentinelBeachRegions.py +++ b/worlds/jakanddaxter/regs/SentinelBeachRegions.py @@ -95,10 +95,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 150 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(2, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/SnowyMountainRegions.py b/worlds/jakanddaxter/regs/SnowyMountainRegions.py index f8f91f7186..37f942cfbf 100644 --- a/worlds/jakanddaxter/regs/SnowyMountainRegions.py +++ b/worlds/jakanddaxter/regs/SnowyMountainRegions.py @@ -190,10 +190,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(12, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/SpiderCaveRegions.py b/worlds/jakanddaxter/regs/SpiderCaveRegions.py index 9674eee2c7..6a65965557 100644 --- a/worlds/jakanddaxter/regs/SpiderCaveRegions.py +++ b/worlds/jakanddaxter/regs/SpiderCaveRegions.py @@ -114,10 +114,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 200 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(13, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs) diff --git a/worlds/jakanddaxter/regs/VolcanicCraterRegions.py b/worlds/jakanddaxter/regs/VolcanicCraterRegions.py index c871e00e25..4c310c4b22 100644 --- a/worlds/jakanddaxter/regs/VolcanicCraterRegions.py +++ b/worlds/jakanddaxter/regs/VolcanicCraterRegions.py @@ -39,10 +39,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bundle_count = 50 // world.orb_bundle_size for bundle_index in range(bundle_count): + amount = world.orb_bundle_size * (bundle_index + 1) orbs.add_orb_locations(11, bundle_index, - access_rule=lambda state, level=level_name, bundle=bundle_index: - can_reach_orbs_level(state, player, world, level, bundle)) + access_rule=lambda state, level=level_name, orb_amount=amount: + can_reach_orbs_level(state, player, world, level, orb_amount)) multiworld.regions.append(orbs) main_area.connect(orbs)