From 965e9e3a5c677ea638cc6b0ee1b07d8a264c92fe Mon Sep 17 00:00:00 2001 From: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> Date: Wed, 7 May 2025 21:02:27 -0400 Subject: [PATCH] Violet code review updates part 2. --- worlds/jakanddaxter/Items.py | 26 ++++- worlds/jakanddaxter/__init__.py | 96 +++++++++---------- worlds/jakanddaxter/regs/BoggySwampRegions.py | 13 +-- worlds/jakanddaxter/regs/GeyserRockRegions.py | 4 +- .../regs/GolAndMaiasCitadelRegions.py | 24 ++--- .../regs/LostPrecursorCityRegions.py | 18 ++-- .../jakanddaxter/regs/MistyIslandRegions.py | 6 +- .../jakanddaxter/regs/RockVillageRegions.py | 8 +- .../regs/SandoverVillageRegions.py | 14 +-- .../jakanddaxter/regs/SentinelBeachRegions.py | 12 +-- .../jakanddaxter/regs/SnowyMountainRegions.py | 20 ++-- worlds/jakanddaxter/regs/SpiderCaveRegions.py | 6 +- 12 files changed, 136 insertions(+), 111 deletions(-) diff --git a/worlds/jakanddaxter/Items.py b/worlds/jakanddaxter/Items.py index 7d72fc433f..81543c7fe4 100644 --- a/worlds/jakanddaxter/Items.py +++ b/worlds/jakanddaxter/Items.py @@ -1,4 +1,5 @@ -from BaseClasses import Item +from enum import IntEnum +from BaseClasses import Item, ItemClassification from .GameID import jak1_name, jak1_max from .locs import (OrbLocations as Orbs, CellLocations as Cells, @@ -7,8 +8,31 @@ from .locs import (OrbLocations as Orbs, OrbCacheLocations as Caches) +class OrbAssoc(IntEnum): + """ + Identifies an item's association to unlocking new sources of Precursor Orbs. For example, Double Jump will unlock + new orbs, but Freed the Green Sage will not. Power Cells conditionally unlock new orbs if they get you across + connector levels. + """ + NEVER_UNLOCKS_ORBS = 0 + ALWAYS_UNLOCKS_ORBS = 1 + IS_POWER_CELL = 2 + + class JakAndDaxterItem(Item): game: str = jak1_name + orb_assoc: OrbAssoc + orb_amount: int # Only non-zero for Orb Bundle items. + + def __init__(self, name: str, + classification: ItemClassification, + code: int | None, + player: int, + orb_assoc: OrbAssoc = OrbAssoc.NEVER_UNLOCKS_ORBS, + orb_amount: int = 0): + super().__init__(name, classification, code, player) + self.orb_assoc = orb_assoc + self.orb_amount = orb_amount # Power Cells are generic, fungible, interchangeable items. Every cell is indistinguishable from every other. diff --git a/worlds/jakanddaxter/__init__.py b/worlds/jakanddaxter/__init__.py index 5bf70d33f0..bb2542e238 100644 --- a/worlds/jakanddaxter/__init__.py +++ b/worlds/jakanddaxter/__init__.py @@ -18,6 +18,7 @@ from Options import OptionGroup from .Options import * from .GameID import jak1_id, jak1_name, jak1_max from .Items import (JakAndDaxterItem, + OrbAssoc, item_table, cell_item_table, scout_item_table, @@ -224,6 +225,7 @@ class JakAndDaxterWorld(World): total_trap_cells: int = 0 total_filler_cells: int = 0 power_cell_thresholds: list[int] + power_cell_thresholds_minus_one: list[int] trap_weights: tuple[list[str], list[int]] # Store these dictionaries for speed improvements. @@ -252,6 +254,9 @@ class JakAndDaxterWorld(World): self.options.mountain_pass_cell_count.value = self.power_cell_thresholds[1] self.options.lava_tube_cell_count.value = self.power_cell_thresholds[2] + # Store this for remove function. + self.power_cell_thresholds_minus_one = [x - 1 for x in self.power_cell_thresholds] + # For the fairness of other players in a multiworld game, enforce some friendly limitations on our options, # so we don't cause chaos during seed generation. These friendly limits should **guarantee** a successful gen. # We would have done this earlier, but we needed to sort the power cell thresholds first. @@ -327,52 +332,55 @@ class JakAndDaxterWorld(World): # from Utils import visualize_regions # visualize_regions(self.multiworld.get_region("Menu", self.player), "jakanddaxter.puml") - def item_type_helper(self, item: int) -> list[tuple[int, ItemClass]]: + def item_data_helper(self, item: int) -> list[tuple[int, ItemClass, OrbAssoc, int]]: """ - 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. + Helper function to reuse some nasty if/else trees. This outputs a list of pairs of item count and class. + 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]] = [] + data: list[tuple[int, ItemClass, OrbAssoc, int]] = [] # Make N Power Cells. We only want AP's Progression Fill routine to handle the amount of cells we need # to reach the furthest possible region. Even for early completion goals, all areas in the game must be # reachable or generation will fail. TODO - Option-driven region creation would be an enormous refactor. if item in range(jak1_id, jak1_id + Scouts.fly_offset): - counts_and_classes.append((self.total_prog_cells, ItemClass.progression_skip_balancing)) - counts_and_classes.append((self.total_filler_cells, ItemClass.filler)) + data.append((self.total_prog_cells, ItemClass.progression_skip_balancing, OrbAssoc.IS_POWER_CELL, 0)) + data.append((self.total_filler_cells, ItemClass.filler, OrbAssoc.IS_POWER_CELL, 0)) # Make 7 Scout Flies per level. elif item in range(jak1_id + Scouts.fly_offset, jak1_id + Specials.special_offset): - counts_and_classes.append((7, ItemClass.progression_skip_balancing)) + data.append((7, ItemClass.progression_skip_balancing, OrbAssoc.NEVER_UNLOCKS_ORBS, 0)) # Make only 1 of each Special Item. elif item in range(jak1_id + Specials.special_offset, jak1_id + Caches.orb_cache_offset): - counts_and_classes.append((1, ItemClass.progression | ItemClass.useful)) + data.append((1, ItemClass.progression | ItemClass.useful, OrbAssoc.ALWAYS_UNLOCKS_ORBS, 0)) # Make only 1 of each Move Item. elif item in range(jak1_id + Caches.orb_cache_offset, jak1_id + Orbs.orb_offset): - counts_and_classes.append((1, ItemClass.progression | ItemClass.useful)) + data.append((1, ItemClass.progression | ItemClass.useful, OrbAssoc.ALWAYS_UNLOCKS_ORBS, 0)) # Make N Precursor Orb bundles. Like Power Cells, only a fraction of these will be marked as Progression - # with the remainder as Filler, but they are still entirely fungible. + # with the remainder as Filler, but they are still entirely fungible. See collect function for why these + # are OrbAssoc.NEVER_UNLOCKS_ORBS. elif item in range(jak1_id + Orbs.orb_offset, jak1_max - max(trap_item_table)): - counts_and_classes.append((self.total_prog_orb_bundles, ItemClass.progression_skip_balancing)) - counts_and_classes.append((self.total_filler_orb_bundles, ItemClass.filler)) + data.append((self.total_prog_orb_bundles, ItemClass.progression_skip_balancing, + OrbAssoc.NEVER_UNLOCKS_ORBS, self.orb_bundle_size)) + data.append((self.total_filler_orb_bundles, ItemClass.filler, + OrbAssoc.NEVER_UNLOCKS_ORBS, self.orb_bundle_size)) # We will manually create trap items as needed. elif item in range(jak1_max - max(trap_item_table), jak1_max): - counts_and_classes.append((0, ItemClass.trap)) + data.append((0, ItemClass.trap, OrbAssoc.NEVER_UNLOCKS_ORBS, 0)) # We will manually create filler items as needed. elif item == jak1_max: - counts_and_classes.append((0, ItemClass.filler)) + data.append((0, ItemClass.filler, OrbAssoc.NEVER_UNLOCKS_ORBS, 0)) # If we try to make items with ID's higher than we've defined, something has gone wrong. else: raise KeyError(f"Tried to fill item pool with unknown ID {item}.") - return counts_and_classes + return data def create_items(self) -> None: items_made: int = 0 @@ -401,9 +409,10 @@ class JakAndDaxterWorld(World): continue # In almost every other scenario, do this. Not all items with the same name will have the same item class. - counts_and_classes = self.item_type_helper(item_id) - for (count, classification) in counts_and_classes: - self.multiworld.itempool += [JakAndDaxterItem(item_name, classification, item_id, self.player) + data = self.item_data_helper(item_id) + for (count, classification, orb_assoc, orb_amount) in data: + self.multiworld.itempool += [JakAndDaxterItem(item_name, classification, item_id, + self.player, orb_assoc, orb_amount) for _ in range(count)] items_made += count @@ -427,13 +436,15 @@ class JakAndDaxterWorld(World): def create_item(self, name: str) -> Item: item_id = self.item_name_to_id[name] - _, classification = self.item_type_helper(item_id)[0] # Use first tuple (will likely be the most important). - return JakAndDaxterItem(name, classification, item_id, self.player) + + # Use first tuple (will likely be the most important). + _, classification, orb_assoc, orb_amount = self.item_data_helper(item_id)[0] + return JakAndDaxterItem(name, classification, item_id, self.player, orb_assoc, orb_amount) def get_filler_item_name(self) -> str: return "Green Eco Pill" - def collect(self, state: CollectionState, item: Item) -> bool: + def collect(self, state: CollectionState, item: JakAndDaxterItem) -> bool: change = super().collect(state, item) if change: # Orbsanity as an option is no-factor to these conditions. Matching the item name implies Orbsanity is ON, @@ -442,47 +453,36 @@ class JakAndDaxterWorld(World): # Orb items do not intrinsically unlock anything that contains more Reachable Orbs, so they do not need to # set the cache to stale. They just change how many orbs you have to trade with. - if item.name == self.orb_bundle_item_name: + if item.orb_amount > 0: state.prog_items[self.player]["Tradeable Orbs"] += self.orb_bundle_size # Give a bundle of Trade Orbs - # Scout Flies ALSO do not unlock anything that contains more Reachable Orbs, NOR do they give you more - # tradeable orbs. So let's just pass on them. - elif item.name in self.item_name_groups["Scout Flies"]: - pass - # Power Cells DO unlock new regions that contain more Reachable Orbs - the connector levels and new # hub levels - BUT they only do that when you have a number of them equal to one of the threshold values. - elif (item.name == "Power Cell" - and state.count("Power Cell", self.player) not in self.power_cell_thresholds): - pass - - # However, every other item that changes the CollectionState should set the cache to stale, because they - # likely made it possible to reach more orb locations (level unlocks, region unlocks, etc.). - else: + elif (item.orb_assoc == OrbAssoc.ALWAYS_UNLOCKS_ORBS + or (item.orb_assoc == OrbAssoc.IS_POWER_CELL + and state.count("Power Cell", self.player) in self.power_cell_thresholds)): state.prog_items[self.player]["Reachable Orbs Fresh"] = False + + # However, every other item that does not have an appropriate OrbAssoc that changes the CollectionState + # should NOT set the cache to stale, because they did not make it possible to reach more orb locations + # (level unlocks, region unlocks, etc.). return change - def remove(self, state: CollectionState, item: Item) -> bool: + def remove(self, state: CollectionState, item: JakAndDaxterItem) -> bool: change = super().remove(state, item) if change: # Do the same thing we did in collect, except subtract trade orbs instead of add. - if item.name == self.orb_bundle_item_name: + if item.orb_amount > 0: state.prog_items[self.player]["Tradeable Orbs"] -= self.orb_bundle_size # Take a bundle of Trade Orbs - # Ditto Scout Flies. - elif item.name in self.item_name_groups["Scout Flies"]: - pass - - # Ditto Power Cells, but check count + 1, because we potentially crossed the threshold in the opposite + # Ditto Power Cells, but check thresholds - 1, because we potentially crossed the threshold in the opposite # direction. E.g. we've removed the 20th power cell, our count is now 19, so we should stale the cache. - elif (item.name == "Power Cell" - and state.count("Power Cell", self.player) + 1 not in self.power_cell_thresholds): - pass - - # Ditto everything else. - else: + elif (item.orb_assoc == OrbAssoc.ALWAYS_UNLOCKS_ORBS + or (item.orb_assoc == OrbAssoc.IS_POWER_CELL + and state.count("Power Cell", self.player) in self.power_cell_thresholds_minus_one)): state.prog_items[self.player]["Reachable Orbs Fresh"] = False + return change def fill_slot_data(self) -> dict[str, Any]: diff --git a/worlds/jakanddaxter/regs/BoggySwampRegions.py b/worlds/jakanddaxter/regs/BoggySwampRegions.py index 5909a6a356..9f8dce154a 100644 --- a/worlds/jakanddaxter/regs/BoggySwampRegions.py +++ b/worlds/jakanddaxter/regs/BoggySwampRegions.py @@ -13,13 +13,14 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # This level is full of short-medium gaps that cannot be crossed by single jump alone. # These helper functions list out the moves that can cross all these gaps (painting with a broad brush but...) def can_jump_farther(state: CollectionState, p: int) -> bool: - return state.has_any({"Double Jump", "Jump Kick"}, p) or state.has_all({"Punch", "Punch Uppercut"}, p) + return (state.has_any(("Double Jump", "Jump Kick"), p) + or state.has_all(("Punch", "Punch Uppercut"), p)) def can_jump_higher(state: CollectionState, p: int) -> bool: return (state.has("Double Jump", p) - or state.has_all({"Crouch", "Crouch Jump"}, p) - or state.has_all({"Crouch", "Crouch Uppercut"}, p) - or state.has_all({"Punch", "Punch Uppercut"}, p)) + or state.has_all(("Crouch", "Crouch Jump"), p) + or state.has_all(("Crouch", "Crouch Uppercut"), p) + or state.has_all(("Punch", "Punch Uppercut"), p)) # Orb crates and fly box in this area can be gotten with yellow eco and goggles. # Start with the first yellow eco cluster near first_bats and work your way backward toward the entrance. @@ -94,8 +95,8 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi first_tether.connect(first_bats) first_tether.connect(first_tether_rat_colony, rule=lambda state: - (state.has_all({"Roll", "Roll Jump"}, player) - or state.has_all({"Double Jump", "Jump Kick"}, player))) + (state.has_all(("Roll", "Roll Jump"), player) + or state.has_all(("Double Jump", "Jump Kick"), player))) first_tether.connect(second_jump_pad) first_tether.connect(first_pole_course) diff --git a/worlds/jakanddaxter/regs/GeyserRockRegions.py b/worlds/jakanddaxter/regs/GeyserRockRegions.py index 68094a1c46..c680c5f3cf 100644 --- a/worlds/jakanddaxter/regs/GeyserRockRegions.py +++ b/worlds/jakanddaxter/regs/GeyserRockRegions.py @@ -20,8 +20,8 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi main_area.connect(cliff, rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) - or state.has_all({"Crouch", "Crouch Uppercut"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player) + or state.has_all(("Crouch", "Crouch Uppercut"), player)) cliff.connect(main_area) # Jump down or ride blue eco elevator. diff --git a/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py b/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py index 1bb2c6fc45..2bc73ad5cb 100644 --- a/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py +++ b/worlds/jakanddaxter/regs/GolAndMaiasCitadelRegions.py @@ -14,17 +14,17 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt # This level is full of short-medium gaps that cannot be crossed by single jump alone. # These helper functions list out the moves that can cross all these gaps (painting with a broad brush but...) def can_jump_farther(state: CollectionState, p: int) -> bool: - return (state.has_any({"Double Jump", "Jump Kick"}, p) - or state.has_all({"Punch", "Punch Uppercut"}, p)) + return (state.has_any(("Double Jump", "Jump Kick"), p) + or state.has_all(("Punch", "Punch Uppercut"), p)) def can_triple_jump(state: CollectionState, p: int) -> bool: - return state.has_all({"Double Jump", "Jump Kick"}, p) + return state.has_all(("Double Jump", "Jump Kick"), p) def can_jump_stairs(state: CollectionState, p: int) -> bool: return (state.has("Double Jump", p) or state.has("Jump Dive", p) - or state.has_all({"Crouch", "Crouch Jump"}, p) - or state.has_all({"Crouch", "Crouch Uppercut"}, p)) + or state.has_all(("Crouch", "Crouch Jump"), p) + or state.has_all(("Crouch", "Crouch Uppercut"), p)) main_area = JakAndDaxterRegion("Main Area", player, multiworld, level_name, 0) main_area.add_fly_locations([91], access_rule=lambda state: can_free_scout_flies(state, player)) @@ -60,36 +60,36 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt # Jump Dive required for a lot of buttons, prepare yourself. main_area.connect(robot_scaffolding, rule=lambda state: - state.has("Jump Dive", player) or state.has_all({"Roll", "Roll Jump"}, player)) + state.has("Jump Dive", player) or state.has_all(("Roll", "Roll Jump"), player)) main_area.connect(jump_pad_room) robot_scaffolding.connect(main_area, rule=lambda state: state.has("Jump Dive", player)) robot_scaffolding.connect(blast_furnace, rule=lambda state: state.has("Jump Dive", player) and can_jump_farther(state, player) - and (can_triple_jump(state, player) or state.has_all({"Roll", "Roll Jump"}, player))) + and (can_triple_jump(state, player) or state.has_all(("Roll", "Roll Jump"), player))) robot_scaffolding.connect(bunny_room, rule=lambda state: state.has("Jump Dive", player) and can_jump_farther(state, player) - and (can_triple_jump(state, player) or state.has_all({"Roll", "Roll Jump"}, player))) + and (can_triple_jump(state, player) or state.has_all(("Roll", "Roll Jump"), player))) jump_pad_room.connect(main_area) jump_pad_room.connect(robot_scaffolding, rule=lambda state: state.has("Jump Dive", player) - and (can_triple_jump(state, player) or state.has_all({"Roll", "Roll Jump"}, player))) + and (can_triple_jump(state, player) or state.has_all(("Roll", "Roll Jump"), player))) blast_furnace.connect(robot_scaffolding) # Blue eco elevator takes you right back. bunny_room.connect(robot_scaffolding, rule=lambda state: state.has("Jump Dive", player) - and (can_jump_farther(state, player) or state.has_all({"Roll", "Roll Jump"}, player))) + and (can_jump_farther(state, player) or state.has_all(("Roll", "Roll Jump"), player))) # Final climb. robot_scaffolding.connect(rotating_tower, rule=lambda state: can_jump_stairs(state, player) - and state.has_all({"Freed The Blue Sage", + and state.has_all(("Freed The Blue Sage", "Freed The Red Sage", - "Freed The Yellow Sage"}, player)) + "Freed The Yellow Sage"), player)) rotating_tower.connect(main_area) # Take stairs back down. diff --git a/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py b/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py index 5aa603106e..f2db996375 100644 --- a/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py +++ b/worlds/jakanddaxter/regs/LostPrecursorCityRegions.py @@ -21,7 +21,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Need jump dive to activate button, double jump to reach blue eco to unlock cache. first_room_orb_cache.add_cache_locations([14507], access_rule=lambda state: - state.has_all({"Jump Dive", "Double Jump"}, player)) + state.has_all(("Jump Dive", "Double Jump"), player)) first_hallway = JakAndDaxterRegion("First Hallway", player, multiworld, level_name, 10) first_hallway.add_fly_locations([131121], access_rule=lambda state: can_free_scout_flies(state, player)) @@ -60,16 +60,16 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Use jump dive to activate button inside the capsule. Blue eco vent can ready the chamber and get the scout fly. capsule_room.add_cell_locations([47], access_rule=lambda state: state.has("Jump Dive", player) - and (state.has_any({"Double Jump", "Jump Kick"}, player) - or state.has_all({"Punch", "Punch Uppercut"}, player))) + and (state.has_any(("Double Jump", "Jump Kick"), player) + or state.has_all(("Punch", "Punch Uppercut"), player))) capsule_room.add_fly_locations([327729]) # You can slide to the bottom of the city, but if you spawn down there, you have no momentum from the slide. # So you need some kind of jump to reach this cell. second_slide = JakAndDaxterRegion("Second Slide", player, multiworld, level_name, 31) second_slide.add_cell_locations([46], access_rule=lambda state: - state.has_any({"Double Jump", "Jump Kick"}, player) - or state.has_all({"Punch", "Punch Uppercut"}, player)) + state.has_any(("Double Jump", "Jump Kick"), player) + or state.has_all(("Punch", "Punch Uppercut"), player)) # If you can enter the helix room, you can jump or fight your way to the top. But you need some kind of movement # to enter it in the first place. @@ -88,9 +88,9 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Needs some movement to reach these orbs and orb cache. first_room_lower.connect(first_room_orb_cache, rule=lambda state: - state.has_all({"Jump Dive", "Double Jump"}, player)) + state.has_all(("Jump Dive", "Double Jump"), player)) first_room_orb_cache.connect(first_room_lower, rule=lambda state: - state.has_all({"Jump Dive", "Double Jump"}, player)) + state.has_all(("Jump Dive", "Double Jump"), player)) first_hallway.connect(first_room_upper) # Run and jump down. first_hallway.connect(second_room) # Run and jump (floating platforms). @@ -114,8 +114,8 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi state.has("Jump Dive", player)) # (Assume one-way for sanity.) second_slide.connect(helix_room, rule=lambda state: # As stated above, you need to jump - state.has_any({"Double Jump", "Jump Kick"}, player) # across the dark eco pool before - or state.has_all({"Punch", "Punch Uppercut"}, player)) # you can climb the helix room. + state.has_any(("Double Jump", "Jump Kick"), player) # across the dark eco pool before + or state.has_all(("Punch", "Punch Uppercut"), player)) # you can climb the helix room. helix_room.connect(quick_platforms, rule=lambda state: # Escape to get back to here. state.has("Double Jump", player) # Capsule is a convenient exit to the level. diff --git a/worlds/jakanddaxter/regs/MistyIslandRegions.py b/worlds/jakanddaxter/regs/MistyIslandRegions.py index 72191aab1f..1bd294947d 100644 --- a/worlds/jakanddaxter/regs/MistyIslandRegions.py +++ b/worlds/jakanddaxter/regs/MistyIslandRegions.py @@ -61,7 +61,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi muse_course.connect(main_area) # Run and jump down. # The zoomer pad is low enough that it requires Crouch Jump specifically. - zoomer.connect(main_area, rule=lambda state: state.has_all({"Crouch", "Crouch Jump"}, player)) + zoomer.connect(main_area, rule=lambda state: state.has_all(("Crouch", "Crouch Jump"), player)) ship.connect(main_area) # Run and jump down. ship.connect(far_side) # Run and jump down. @@ -73,7 +73,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Only if you can use the seesaw or Crouch Jump from the seesaw's edge. far_side.connect(far_side_cliff, rule=lambda state: state.has("Jump Dive", player) - or state.has_all({"Crouch", "Crouch Jump"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player)) # Only if you can break the bone bridges to carry blue eco over the mud pit. far_side.connect(far_side_cache, rule=lambda state: can_fight(state, player)) @@ -90,7 +90,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi upper_approach.connect(arena) # Jump down. # One cliff is accessible, but only via Crouch Jump. - lower_approach.connect(upper_approach, rule=lambda state: state.has_all({"Crouch", "Crouch Jump"}, player)) + lower_approach.connect(upper_approach, rule=lambda state: state.has_all(("Crouch", "Crouch Jump"), player)) # Requires breaking bone bridges. lower_approach.connect(arena, rule=lambda state: can_fight(state, player)) diff --git a/worlds/jakanddaxter/regs/RockVillageRegions.py b/worlds/jakanddaxter/regs/RockVillageRegions.py index 7778bea35d..0053a3efa1 100644 --- a/worlds/jakanddaxter/regs/RockVillageRegions.py +++ b/worlds/jakanddaxter/regs/RockVillageRegions.py @@ -28,7 +28,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt orb_cache = JakAndDaxterRegion("Orb Cache", player, multiworld, level_name, 20) # You need roll jump to be able to reach this before the blue eco runs out. - orb_cache.add_cache_locations([10945], access_rule=lambda state: state.has_all({"Roll", "Roll Jump"}, player)) + orb_cache.add_cache_locations([10945], access_rule=lambda state: state.has_all(("Roll", "Roll Jump"), player)) # Fly here can be gotten with Yellow Eco from Boggy, goggles, and no extra movement options (see fly ID 43). pontoon_bridge = JakAndDaxterRegion("Pontoon Bridge", player, multiworld, level_name, 7) @@ -36,7 +36,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt klaww_cliff = JakAndDaxterRegion("Klaww's Cliff", player, multiworld, level_name, 0) - main_area.connect(orb_cache, rule=lambda state: state.has_all({"Roll", "Roll Jump"}, player)) + main_area.connect(orb_cache, rule=lambda state: state.has_all(("Roll", "Roll Jump"), player)) main_area.connect(pontoon_bridge, rule=lambda state: state.has("Warrior's Pontoons", player)) orb_cache.connect(main_area) @@ -44,8 +44,8 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt pontoon_bridge.connect(main_area, rule=lambda state: state.has("Warrior's Pontoons", player)) pontoon_bridge.connect(klaww_cliff, rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) - or state.has_all({"Crouch", "Crouch Uppercut", "Jump Kick"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player) + or state.has_all(("Crouch", "Crouch Uppercut", "Jump Kick"), player)) klaww_cliff.connect(pontoon_bridge) # Just jump back down. diff --git a/worlds/jakanddaxter/regs/SandoverVillageRegions.py b/worlds/jakanddaxter/regs/SandoverVillageRegions.py index 42d4e497fe..a727afcea4 100644 --- a/worlds/jakanddaxter/regs/SandoverVillageRegions.py +++ b/worlds/jakanddaxter/regs/SandoverVillageRegions.py @@ -22,7 +22,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # The farmer's scout fly. You can either get the Orb Cache Cliff blue eco, or break it normally. main_area.add_fly_locations([196683], access_rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) + or state.has_all(("Crouch", "Crouch Jump"), player) or can_free_scout_flies(state, player)) orb_cache_cliff = JakAndDaxterRegion("Orb Cache Cliff", player, multiworld, level_name, 15) @@ -41,17 +41,17 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi main_area.connect(orb_cache_cliff, rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) - or state.has_all({"Crouch", "Crouch Uppercut", "Jump Kick"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player) + or state.has_all(("Crouch", "Crouch Uppercut", "Jump Kick"), player)) main_area.connect(yakow_cliff, rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) - or state.has_all({"Crouch", "Crouch Uppercut", "Jump Kick"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player) + or state.has_all(("Crouch", "Crouch Uppercut", "Jump Kick"), player)) main_area.connect(oracle_platforms, rule=lambda state: - state.has_all({"Roll", "Roll Jump"}, player) - or state.has_all({"Double Jump", "Jump Kick"}, player)) + state.has_all(("Roll", "Roll Jump"), player) + or state.has_all(("Double Jump", "Jump Kick"), player)) # All these can go back to main_area immediately. orb_cache_cliff.connect(main_area) diff --git a/worlds/jakanddaxter/regs/SentinelBeachRegions.py b/worlds/jakanddaxter/regs/SentinelBeachRegions.py index 8f8312baeb..d651bca9a6 100644 --- a/worlds/jakanddaxter/regs/SentinelBeachRegions.py +++ b/worlds/jakanddaxter/regs/SentinelBeachRegions.py @@ -31,9 +31,9 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Only these specific attacks can push the flut flut egg off the cliff. flut_flut_egg = JakAndDaxterRegion("Flut Flut Egg", player, multiworld, level_name, 0) flut_flut_egg.add_cell_locations([17], access_rule=lambda state: - state.has_any({"Punch", "Kick", "Jump Kick"}, player)) + state.has_any(("Punch", "Kick", "Jump Kick"), player)) flut_flut_egg.add_special_locations([17], access_rule=lambda state: - state.has_any({"Punch", "Kick", "Jump Kick"}, player)) + state.has_any(("Punch", "Kick", "Jump Kick"), player)) eco_harvesters = JakAndDaxterRegion("Eco Harvesters", player, multiworld, level_name, 0) eco_harvesters.add_cell_locations([15], access_rule=lambda state: can_fight(state, player)) @@ -55,14 +55,14 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # We need a helper function for the uppercut logs. def can_uppercut_and_jump_logs(state: CollectionState, p: int) -> bool: - return (state.has_any({"Double Jump", "Jump Kick"}, p) - and (state.has_all({"Crouch", "Crouch Uppercut"}, p) - or state.has_all({"Punch", "Punch Uppercut"}, p))) + return (state.has_any(("Double Jump", "Jump Kick"), p) + and (state.has_all(("Crouch", "Crouch Uppercut"), p) + or state.has_all(("Punch", "Punch Uppercut"), p))) # If you have double jump or crouch jump, you don't need the logs to reach this place. main_area.connect(green_ridge, rule=lambda state: state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player) + or state.has_all(("Crouch", "Crouch Jump"), player) or can_uppercut_and_jump_logs(state, player)) # If you have the blue eco jump pad, you don't need the logs to reach this place. diff --git a/worlds/jakanddaxter/regs/SnowyMountainRegions.py b/worlds/jakanddaxter/regs/SnowyMountainRegions.py index 37f942cfbf..67782195ec 100644 --- a/worlds/jakanddaxter/regs/SnowyMountainRegions.py +++ b/worlds/jakanddaxter/regs/SnowyMountainRegions.py @@ -13,13 +13,13 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # We need a few helper functions. def can_cross_long_gap(state: CollectionState, p: int) -> bool: - return (state.has_all({"Roll", "Roll Jump"}, p) - or state.has_all({"Double Jump", "Jump Kick"}, p)) + return (state.has_all(("Roll", "Roll Jump"), p) + or state.has_all(("Double Jump", "Jump Kick"), p)) def can_jump_blockers(state: CollectionState, p: int) -> bool: - return (state.has_any({"Double Jump", "Jump Kick"}, p) - or state.has_all({"Crouch", "Crouch Jump"}, p) - or state.has_all({"Punch", "Punch Uppercut"}, p)) + return (state.has_any(("Double Jump", "Jump Kick"), p) + or state.has_all(("Crouch", "Crouch Jump"), p) + or state.has_all(("Punch", "Punch Uppercut"), p)) main_area = JakAndDaxterRegion("Main Area", player, multiworld, level_name, 0) main_area.add_fly_locations([65], access_rule=lambda state: can_free_scout_flies(state, player)) @@ -142,13 +142,13 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi fort_interior.connect(fort_interior_caches, rule=lambda state: # Just need a little height. state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player)) fort_interior.connect(fort_interior_base, rule=lambda state: # Just need a little height. state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player)) + or state.has_all(("Crouch", "Crouch Jump"), player)) fort_interior.connect(fort_interior_course_end, rule=lambda state: # Just need a little distance. - state.has_any({"Double Jump", "Jump Kick"}, player) - or state.has_all({"Punch", "Punch Uppercut"}, player)) + state.has_any(("Double Jump", "Jump Kick"), player) + or state.has_all(("Punch", "Punch Uppercut"), player)) flut_flut_course.connect(fort_exterior) # Ride the elevator. @@ -156,7 +156,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi bunny_cave_start.connect(bunny_cave_end, rule=lambda state: can_fight(state, player) and (state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player))) + or state.has_all(("Crouch", "Crouch Jump"), player))) # All jump down. fort_interior_caches.connect(fort_interior) diff --git a/worlds/jakanddaxter/regs/SpiderCaveRegions.py b/worlds/jakanddaxter/regs/SpiderCaveRegions.py index 6a65965557..e99fbcf62d 100644 --- a/worlds/jakanddaxter/regs/SpiderCaveRegions.py +++ b/worlds/jakanddaxter/regs/SpiderCaveRegions.py @@ -24,7 +24,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # The rest of the crystals can be destroyed with yellow eco in main_area. dark_crystals.add_cell_locations([79], access_rule=lambda state: can_fight(state, player) - and state.has_all({"Roll", "Roll Jump"}, player)) + and state.has_all(("Roll", "Roll Jump"), player)) dark_cave = JakAndDaxterRegion("Dark Cave", player, multiworld, level_name, 5) dark_cave.add_cell_locations([80]) @@ -60,7 +60,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi main_area.connect(dark_cave, rule=lambda state: can_fight(state, player) and (state.has("Double Jump", player) - or state.has_all({"Crouch", "Crouch Jump"}, player))) + or state.has_all(("Crouch", "Crouch Jump"), player))) robot_cave.connect(main_area) robot_cave.connect(pole_course) # Nothing special required. @@ -73,7 +73,7 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi # Elevator, but the orbs need double jump or jump kick. scaffolding_level_one.connect(scaffolding_level_zero, rule=lambda state: - state.has_any({"Double Jump", "Jump Kick"}, player)) + state.has_any(("Double Jump", "Jump Kick"), player)) # Narrow enough that enemies are unavoidable. scaffolding_level_one.connect(scaffolding_level_two, rule=lambda state: can_fight(state, player))