diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-10-06 09:58:45 -0400 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-10-06 09:58:45 -0400 |
| commit | 9864382c9e4b2015c05214ebc177b410edc24bce (patch) | |
| tree | 9c9ca3f93abc078a07d8a5a94c83152fd3eee642 | |
| parent | d861b203529add969038278dba9d2696d1248cc3 (diff) | |
| download | lingo2-archipelago-cc-colors.tar.gz lingo2-archipelago-cc-colors.tar.bz2 lingo2-archipelago-cc-colors.zip | |
trying something cc-colors
| -rw-r--r-- | apworld/regions.py | 75 | ||||
| -rw-r--r-- | apworld/rules.py | 189 |
2 files changed, 242 insertions, 22 deletions
| diff --git a/apworld/regions.py b/apworld/regions.py index 1215f5a..b5ec9c6 100644 --- a/apworld/regions.py +++ b/apworld/regions.py | |||
| @@ -6,14 +6,14 @@ from entrance_rando import randomize_entrances | |||
| 6 | from .items import Lingo2Item | 6 | from .items import Lingo2Item |
| 7 | from .locations import Lingo2Location | 7 | from .locations import Lingo2Location |
| 8 | from .player_logic import AccessRequirements | 8 | from .player_logic import AccessRequirements |
| 9 | from .rules import make_location_lambda | 9 | from .rules import make_location_lambda, Lingo2Entrance, ControlCenterColor, CONTROL_CENTER_COLOR_NAMES, Lingo2Region |
| 10 | 10 | ||
| 11 | if TYPE_CHECKING: | 11 | if TYPE_CHECKING: |
| 12 | from . import Lingo2World | 12 | from . import Lingo2World |
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | def create_region(room, world: "Lingo2World") -> Region: | 15 | def create_region(room, world: "Lingo2World") -> Region: |
| 16 | return Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) | 16 | return Lingo2Region(world.static_logic.get_room_region_name(room.id), world.player, world.multiworld) |
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | def create_locations(room, new_region: Region, world: "Lingo2World", regions: dict[str, Region]): | 19 | def create_locations(room, new_region: Region, world: "Lingo2World", regions: dict[str, Region]): |
| @@ -53,7 +53,7 @@ def create_locations(room, new_region: Region, world: "Lingo2World", regions: di | |||
| 53 | 53 | ||
| 54 | def create_regions(world: "Lingo2World"): | 54 | def create_regions(world: "Lingo2World"): |
| 55 | regions = { | 55 | regions = { |
| 56 | "Menu": Region("Menu", world.player, world.multiworld) | 56 | "Menu": Lingo2Region("Menu", world.player, world.multiworld) |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | region_and_room = [] | 59 | region_and_room = [] |
| @@ -69,7 +69,10 @@ def create_regions(world: "Lingo2World"): | |||
| 69 | for (region, room) in region_and_room: | 69 | for (region, room) in region_and_room: |
| 70 | create_locations(room, region, world, regions) | 70 | create_locations(room, region, world, regions) |
| 71 | 71 | ||
| 72 | regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") | 72 | start_game = Lingo2Entrance(world.player, "Start Game", regions["Menu"]) |
| 73 | start_game.set_lingo2_rule(world, regions, AccessRequirements(), ControlCenterColor.ALL) | ||
| 74 | regions["Menu"].exits.append(start_game) | ||
| 75 | start_game.connect(regions["The Entry - Starting Room"]) | ||
| 73 | 76 | ||
| 74 | for connection in world.static_logic.objects.connections: | 77 | for connection in world.static_logic.objects.connections: |
| 75 | if connection.roof_access and not world.options.daedalus_roof_access: | 78 | if connection.roof_access and not world.options.daedalus_roof_access: |
| @@ -87,6 +90,7 @@ def create_regions(world: "Lingo2World"): | |||
| 87 | connection_name = f"{from_region} -> {to_region}" | 90 | connection_name = f"{from_region} -> {to_region}" |
| 88 | 91 | ||
| 89 | reqs = AccessRequirements() | 92 | reqs = AccessRequirements() |
| 93 | control_center_colors = ControlCenterColor.ALL | ||
| 90 | 94 | ||
| 91 | if connection.HasField("required_door"): | 95 | if connection.HasField("required_door"): |
| 92 | reqs.merge(world.player_logic.get_door_open_reqs(connection.required_door)) | 96 | reqs.merge(world.player_logic.get_door_open_reqs(connection.required_door)) |
| @@ -95,6 +99,10 @@ def create_regions(world: "Lingo2World"): | |||
| 95 | wmap = world.static_logic.objects.maps[door.map_id] | 99 | wmap = world.static_logic.objects.maps[door.map_id] |
| 96 | connection_name = f"{connection_name} (using {wmap.name} - {door.name})" | 100 | connection_name = f"{connection_name} (using {wmap.name} - {door.name})" |
| 97 | 101 | ||
| 102 | if door.HasField("control_center_color"): | ||
| 103 | control_center_colors = control_center_colors & CONTROL_CENTER_COLOR_NAMES.get( | ||
| 104 | door.control_center_color, ControlCenterColor.NONE) | ||
| 105 | |||
| 98 | if connection.HasField("port"): | 106 | if connection.HasField("port"): |
| 99 | port = world.static_logic.objects.ports[connection.port] | 107 | port = world.static_logic.objects.ports[connection.port] |
| 100 | connection_name = f"{connection_name} (via {port.display_name})" | 108 | connection_name = f"{connection_name} (via {port.display_name})" |
| @@ -105,6 +113,11 @@ def create_regions(world: "Lingo2World"): | |||
| 105 | if port.HasField("required_door"): | 113 | if port.HasField("required_door"): |
| 106 | reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) | 114 | reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) |
| 107 | 115 | ||
| 116 | req_door = world.static_logic.objects.doors[port.required_door] | ||
| 117 | if req_door.HasField("control_center_color"): | ||
| 118 | control_center_colors = control_center_colors & CONTROL_CENTER_COLOR_NAMES.get( | ||
| 119 | req_door.control_center_color, ControlCenterColor.NONE) | ||
| 120 | |||
| 108 | if connection.HasField("painting"): | 121 | if connection.HasField("painting"): |
| 109 | painting = world.static_logic.objects.paintings[connection.painting] | 122 | painting = world.static_logic.objects.paintings[connection.painting] |
| 110 | connection_name = f"{connection_name} (via painting {painting.name})" | 123 | connection_name = f"{connection_name} (via painting {painting.name})" |
| @@ -112,6 +125,11 @@ def create_regions(world: "Lingo2World"): | |||
| 112 | if painting.HasField("required_door"): | 125 | if painting.HasField("required_door"): |
| 113 | reqs.merge(world.player_logic.get_door_open_reqs(painting.required_door)) | 126 | reqs.merge(world.player_logic.get_door_open_reqs(painting.required_door)) |
| 114 | 127 | ||
| 128 | req_door = world.static_logic.objects.doors[painting.required_door] | ||
| 129 | if req_door.HasField("control_center_color"): | ||
| 130 | control_center_colors = control_center_colors & CONTROL_CENTER_COLOR_NAMES.get( | ||
| 131 | req_door.control_center_color, ControlCenterColor.NONE) | ||
| 132 | |||
| 115 | if connection.HasField("panel"): | 133 | if connection.HasField("panel"): |
| 116 | proxy = connection.panel | 134 | proxy = connection.panel |
| 117 | reqs.merge(world.player_logic.get_panel_reqs(proxy.panel, | 135 | reqs.merge(world.player_logic.get_panel_reqs(proxy.panel, |
| @@ -132,14 +150,14 @@ def create_regions(world: "Lingo2World"): | |||
| 132 | reqs.simplify() | 150 | reqs.simplify() |
| 133 | reqs.remove_room(from_region) | 151 | reqs.remove_room(from_region) |
| 134 | 152 | ||
| 135 | connection = Entrance(world.player, connection_name, regions[from_region]) | 153 | entrance = Lingo2Entrance(world.player, connection_name, regions[from_region]) |
| 136 | connection.access_rule = make_location_lambda(reqs, world, regions) | 154 | entrance.set_lingo2_rule(world, regions, reqs, control_center_colors) |
| 137 | 155 | ||
| 138 | regions[from_region].exits.append(connection) | 156 | regions[from_region].exits.append(entrance) |
| 139 | connection.connect(regions[to_region]) | 157 | entrance.connect(regions[to_region]) |
| 140 | 158 | ||
| 141 | for region in reqs.get_referenced_rooms(): | 159 | for region in reqs.get_referenced_rooms(): |
| 142 | world.multiworld.register_indirect_condition(regions[region], connection) | 160 | world.multiworld.register_indirect_condition(regions[region], entrance) |
| 143 | 161 | ||
| 144 | world.multiworld.regions += regions.values() | 162 | world.multiworld.regions += regions.values() |
| 145 | 163 | ||
| @@ -160,19 +178,29 @@ def shuffle_entrances(world: "Lingo2World"): | |||
| 160 | connection_name = f"{port_region_name} - {port.display_name}" | 178 | connection_name = f"{port_region_name} - {port.display_name}" |
| 161 | port_id_by_name[connection_name] = port.id | 179 | port_id_by_name[connection_name] = port.id |
| 162 | 180 | ||
| 163 | entrance = port_region.create_er_target(connection_name) | 181 | entrance = Lingo2Entrance(world.player, connection_name) |
| 182 | entrance.connect(port_region) | ||
| 164 | entrance.randomization_type = BaseClasses.EntranceType.TWO_WAY | 183 | entrance.randomization_type = BaseClasses.EntranceType.TWO_WAY |
| 165 | 184 | ||
| 166 | er_exit = port_region.create_exit(connection_name) | 185 | er_exit = Lingo2Entrance(world.player, connection_name, port_region) |
| 186 | port_region.exits.append(er_exit) | ||
| 167 | er_exit.randomization_type = BaseClasses.EntranceType.TWO_WAY | 187 | er_exit.randomization_type = BaseClasses.EntranceType.TWO_WAY |
| 168 | 188 | ||
| 189 | door_reqs = AccessRequirements() | ||
| 190 | control_center_colors = ControlCenterColor.ALL | ||
| 169 | if port.HasField("required_door"): | 191 | if port.HasField("required_door"): |
| 170 | door_reqs = world.player_logic.get_door_open_reqs(port.required_door) | 192 | door_reqs = world.player_logic.get_door_open_reqs(port.required_door) |
| 171 | er_exit.access_rule = make_location_lambda(door_reqs, world, None) | ||
| 172 | 193 | ||
| 173 | for region in door_reqs.get_referenced_rooms(): | 194 | req_door = world.static_logic.objects.doors[port.required_door] |
| 174 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | 195 | if req_door.HasField("control_center_color"): |
| 175 | er_exit) | 196 | control_center_colors = CONTROL_CENTER_COLOR_NAMES.get(req_door.control_center_color, |
| 197 | ControlCenterColor.NONE) | ||
| 198 | |||
| 199 | er_exit.set_lingo2_rule(world, None, door_reqs, control_center_colors) | ||
| 200 | |||
| 201 | for region in door_reqs.get_referenced_rooms(): | ||
| 202 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | ||
| 203 | er_exit) | ||
| 176 | 204 | ||
| 177 | er_entrances.append(entrance) | 205 | er_entrances.append(entrance) |
| 178 | er_exits.append(er_exit) | 206 | er_exits.append(er_exit) |
| @@ -195,21 +223,26 @@ def connect_ports_from_ut(port_pairings: dict[int, int], world: "Lingo2World"): | |||
| 195 | from_region = world.multiworld.get_region(from_region_name, world.player) | 223 | from_region = world.multiworld.get_region(from_region_name, world.player) |
| 196 | to_region = world.multiworld.get_region(to_region_name, world.player) | 224 | to_region = world.multiworld.get_region(to_region_name, world.player) |
| 197 | 225 | ||
| 198 | connection = Entrance(world.player, f"{from_region_name} - {from_port.display_name}", from_region) | 226 | connection = Lingo2Entrance(world.player, f"{from_region_name} - {from_port.display_name}", from_region) |
| 199 | 227 | ||
| 200 | reqs = AccessRequirements() | 228 | reqs = AccessRequirements() |
| 229 | control_center_colors = ControlCenterColor.ALL | ||
| 201 | if from_port.HasField("required_door"): | 230 | if from_port.HasField("required_door"): |
| 202 | reqs = world.player_logic.get_door_open_reqs(from_port.required_door).copy() | 231 | reqs = world.player_logic.get_door_open_reqs(from_port.required_door).copy() |
| 203 | 232 | ||
| 233 | req_door = world.static_logic.objects.doors[from_port.required_door] | ||
| 234 | if req_door.HasField("control_center_color"): | ||
| 235 | control_center_colors = CONTROL_CENTER_COLOR_NAMES.get(req_door.control_center_color, | ||
| 236 | ControlCenterColor.NONE) | ||
| 237 | |||
| 204 | if world.for_tracker: | 238 | if world.for_tracker: |
| 205 | reqs.items.add(f"Worldport {fpid} Entered") | 239 | reqs.items.add(f"Worldport {fpid} Entered") |
| 206 | 240 | ||
| 207 | if not reqs.is_empty(): | 241 | connection.set_lingo2_rule(world, None, reqs, control_center_colors) |
| 208 | connection.access_rule = make_location_lambda(reqs, world, None) | ||
| 209 | 242 | ||
| 210 | for region in reqs.get_referenced_rooms(): | 243 | for region in reqs.get_referenced_rooms(): |
| 211 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), | 244 | world.multiworld.register_indirect_condition(world.multiworld.get_region(region, world.player), |
| 212 | connection) | 245 | connection) |
| 213 | 246 | ||
| 214 | from_region.exits.append(connection) | 247 | from_region.exits.append(connection) |
| 215 | connection.connect(to_region) | 248 | connection.connect(to_region) |
| diff --git a/apworld/rules.py b/apworld/rules.py index f859e75..a84cfbb 100644 --- a/apworld/rules.py +++ b/apworld/rules.py | |||
| @@ -1,13 +1,199 @@ | |||
| 1 | from collections.abc import Callable | 1 | from collections.abc import Callable |
| 2 | from enum import IntFlag, auto | ||
| 2 | from typing import TYPE_CHECKING | 3 | from typing import TYPE_CHECKING |
| 3 | 4 | ||
| 4 | from BaseClasses import CollectionState, Region | 5 | from BaseClasses import CollectionState, Region, MultiWorld, Entrance |
| 5 | from .player_logic import AccessRequirements | 6 | from .player_logic import AccessRequirements |
| 7 | from ..AutoWorld import LogicMixin | ||
| 6 | 8 | ||
| 7 | if TYPE_CHECKING: | 9 | if TYPE_CHECKING: |
| 8 | from . import Lingo2World | 10 | from . import Lingo2World |
| 9 | 11 | ||
| 10 | 12 | ||
| 13 | class ControlCenterColor(IntFlag): | ||
| 14 | RED = auto() | ||
| 15 | BLUE = auto() | ||
| 16 | ORANGE = auto() | ||
| 17 | MAGENTA = auto() | ||
| 18 | PURPLE = auto() | ||
| 19 | GREEN = auto() | ||
| 20 | BROWN = auto() | ||
| 21 | WHITE = auto() | ||
| 22 | |||
| 23 | NONE = 0 | ||
| 24 | ALL = RED + BLUE + ORANGE + MAGENTA + PURPLE + GREEN + BROWN + WHITE | ||
| 25 | |||
| 26 | |||
| 27 | CONTROL_CENTER_COLOR_NAMES: dict[str, ControlCenterColor] = { | ||
| 28 | "red": ControlCenterColor.RED, | ||
| 29 | "blue": ControlCenterColor.BLUE, | ||
| 30 | "orange": ControlCenterColor.ORANGE, | ||
| 31 | "magenta": ControlCenterColor.MAGENTA, | ||
| 32 | "purple": ControlCenterColor.PURPLE, | ||
| 33 | "green": ControlCenterColor.GREEN, | ||
| 34 | "brown": ControlCenterColor.BROWN, | ||
| 35 | "white": ControlCenterColor.WHITE, | ||
| 36 | } | ||
| 37 | |||
| 38 | |||
| 39 | class Lingo2Entrance(Entrance): | ||
| 40 | world: "Lingo2World" | ||
| 41 | reqs: AccessRequirements | None | ||
| 42 | required_regions: list[Region] | ||
| 43 | control_center_colors: ControlCenterColor | ||
| 44 | |||
| 45 | def set_lingo2_rule(self, world: "Lingo2World", regions: dict[str, Region] | None, reqs: AccessRequirements, | ||
| 46 | control_center_colors: ControlCenterColor): | ||
| 47 | self.world = world | ||
| 48 | |||
| 49 | if reqs.is_empty(): | ||
| 50 | self.reqs = None | ||
| 51 | else: | ||
| 52 | self.reqs = reqs.copy() | ||
| 53 | self.reqs.rooms.clear() | ||
| 54 | |||
| 55 | if regions is None: | ||
| 56 | self.required_regions = [world.multiworld.get_region(room_name, world.player) | ||
| 57 | for room_name in reqs.rooms] | ||
| 58 | else: | ||
| 59 | self.required_regions = [regions[room_name] for room_name in reqs.rooms] | ||
| 60 | |||
| 61 | self.control_center_colors = control_center_colors | ||
| 62 | |||
| 63 | self.access_rule = self._lingo2_rule | ||
| 64 | |||
| 65 | def _lingo2_rule(self, state: CollectionState) -> bool: | ||
| 66 | if self.reqs is not None and not lingo2_can_satisfy_requirements(state, self.reqs, self.required_regions, | ||
| 67 | self.world): | ||
| 68 | return False | ||
| 69 | |||
| 70 | if self.control_center_colors != ControlCenterColor.ALL\ | ||
| 71 | and not self.world.options.shuffle_control_center_colors: | ||
| 72 | from_region_state = state.lingo2_get_region_state(self.parent_region) | ||
| 73 | |||
| 74 | if from_region_state.control_center_colors & self.control_center_colors == ControlCenterColor.NONE: | ||
| 75 | return False | ||
| 76 | |||
| 77 | state.lingo2_use_entrance(self) | ||
| 78 | return True | ||
| 79 | |||
| 80 | |||
| 81 | class Lingo2Region(Region): | ||
| 82 | def can_reach(self, state) -> bool: | ||
| 83 | if self in state.reachable_regions[self.player]: | ||
| 84 | return True | ||
| 85 | |||
| 86 | if not state.stale[self.player] and not state.lingo2_state[self.player].stale: | ||
| 87 | # if the cache is updated we can use the cache | ||
| 88 | return super().can_reach(state) | ||
| 89 | |||
| 90 | if state.lingo2_state[self.player].stale: | ||
| 91 | state.lingo2_sweep(self.player) | ||
| 92 | |||
| 93 | return super().can_reach(state) | ||
| 94 | |||
| 95 | |||
| 96 | class PerPlayerRegionState: | ||
| 97 | control_center_colors: ControlCenterColor | ||
| 98 | |||
| 99 | def __init__(self): | ||
| 100 | self.control_center_colors = ControlCenterColor.NONE | ||
| 101 | |||
| 102 | def copy(self) -> "PerPlayerRegionState": | ||
| 103 | result = PerPlayerRegionState() | ||
| 104 | result.control_center_colors = self.control_center_colors | ||
| 105 | return result | ||
| 106 | |||
| 107 | |||
| 108 | class PerPlayerState: | ||
| 109 | world: "Lingo2World" | ||
| 110 | regions: dict[str, PerPlayerRegionState] | ||
| 111 | stale: bool | ||
| 112 | sweeping: bool | ||
| 113 | sweepable_entrances: set[Entrance] | ||
| 114 | check_colors: bool | ||
| 115 | |||
| 116 | def __init__(self, world: "Lingo2World"): | ||
| 117 | self.world = world | ||
| 118 | self.regions = {} | ||
| 119 | self.stale = True | ||
| 120 | self.sweeping = False | ||
| 121 | self.sweepable_entrances = set() | ||
| 122 | self.check_colors = not world.options.shuffle_control_center_colors | ||
| 123 | |||
| 124 | self.regions["Menu"] = PerPlayerRegionState() | ||
| 125 | self.regions["Menu"].control_center_colors = ControlCenterColor.ALL | ||
| 126 | |||
| 127 | def copy(self) -> "PerPlayerState": | ||
| 128 | result = PerPlayerState(self.world) | ||
| 129 | result.regions = {region_name: region_state.copy() for region_name, region_state in self.regions.items()} | ||
| 130 | result.stale = self.stale | ||
| 131 | result.sweeping = self.sweeping | ||
| 132 | result.sweepable_entrances = self.sweepable_entrances.copy() | ||
| 133 | result.check_colors = self.check_colors | ||
| 134 | return result | ||
| 135 | |||
| 136 | def get_region_state(self, region: Region): | ||
| 137 | return self.regions.setdefault(region.name, PerPlayerRegionState()) | ||
| 138 | |||
| 139 | |||
| 140 | class Lingo2LogicMixin(LogicMixin): | ||
| 141 | multiworld: MultiWorld | ||
| 142 | reachable_regions: dict[int, set[Region]] | ||
| 143 | |||
| 144 | lingo2_state: dict[int, PerPlayerState] | ||
| 145 | |||
| 146 | def init_mixin(self, multiworld: MultiWorld): | ||
| 147 | self.lingo2_state = {player: PerPlayerState(multiworld.worlds[player]) | ||
| 148 | for player in multiworld.get_game_players("Lingo 2")} | ||
| 149 | |||
| 150 | def copy_mixin(self, new_state: CollectionState) -> CollectionState: | ||
| 151 | new_state.lingo2_state = {player: pps.copy() for player, pps in self.lingo2_state.items()} | ||
| 152 | |||
| 153 | return new_state | ||
| 154 | |||
| 155 | def lingo2_get_region_state(self, region: Region): | ||
| 156 | return self.lingo2_state[region.player].get_region_state(region) | ||
| 157 | |||
| 158 | def lingo2_use_entrance(self, entrance: Lingo2Entrance): | ||
| 159 | player_state = self.lingo2_state[entrance.player] | ||
| 160 | |||
| 161 | from_region_state = player_state.get_region_state(entrance.parent_region) | ||
| 162 | to_region_state = player_state.get_region_state(entrance.connected_region) | ||
| 163 | |||
| 164 | should_update = False | ||
| 165 | |||
| 166 | if player_state.check_colors: | ||
| 167 | avail_colors = from_region_state.control_center_colors & entrance.control_center_colors | ||
| 168 | if avail_colors & ~to_region_state.control_center_colors != ControlCenterColor.NONE: | ||
| 169 | if to_region_state.control_center_colors != ControlCenterColor.NONE or avail_colors != ControlCenterColor.ALL: | ||
| 170 | #print(f"entrance {entrance.name} takes region from {to_region_state.control_center_colors} to {avail_colors}") | ||
| 171 | pass | ||
| 172 | |||
| 173 | should_update = True | ||
| 174 | to_region_state.control_center_colors = to_region_state.control_center_colors | avail_colors | ||
| 175 | |||
| 176 | if should_update: | ||
| 177 | player_state.stale = True | ||
| 178 | |||
| 179 | for adjacent in entrance.parent_region.exits: | ||
| 180 | player_state.sweepable_entrances.add(adjacent) | ||
| 181 | |||
| 182 | def lingo2_sweep(self, player: int): | ||
| 183 | player_state = self.lingo2_state[player] | ||
| 184 | if player_state.sweeping: | ||
| 185 | return | ||
| 186 | player_state.sweeping = True | ||
| 187 | |||
| 188 | while player_state.sweepable_entrances: | ||
| 189 | next_entrance = player_state.sweepable_entrances.pop() | ||
| 190 | if next_entrance.parent_region in self.reachable_regions[player]: | ||
| 191 | next_entrance.can_reach(self) | ||
| 192 | |||
| 193 | player_state.stale = False | ||
| 194 | player_state.sweeping = False | ||
| 195 | |||
| 196 | |||
| 11 | def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, regions: list[Region], | 197 | def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, regions: list[Region], |
| 12 | world: "Lingo2World") -> bool: | 198 | world: "Lingo2World") -> bool: |
| 13 | if not all(state.has(item, world.player) for item in reqs.items): | 199 | if not all(state.has(item, world.player) for item in reqs.items): |
| @@ -53,6 +239,7 @@ def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirem | |||
| 53 | 239 | ||
| 54 | return True | 240 | return True |
| 55 | 241 | ||
| 242 | |||
| 56 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World", | 243 | def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World", |
| 57 | regions: dict[str, Region] | None) -> Callable[[CollectionState], bool]: | 244 | regions: dict[str, Region] | None) -> Callable[[CollectionState], bool]: |
| 58 | # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule | 245 | # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule |
