diff options
| -rw-r--r-- | __init__.py | 2 | ||||
| -rw-r--r-- | regions.py | 38 | ||||
| -rw-r--r-- | rules.py | 52 |
3 files changed, 42 insertions, 50 deletions
| diff --git a/__init__.py b/__init__.py index b749418..25be166 100644 --- a/__init__.py +++ b/__init__.py | |||
| @@ -63,7 +63,7 @@ class LingoWorld(World): | |||
| 63 | self.player_logic = LingoPlayerLogic(self) | 63 | self.player_logic = LingoPlayerLogic(self) |
| 64 | 64 | ||
| 65 | def create_regions(self): | 65 | def create_regions(self): |
| 66 | create_regions(self, self.player_logic) | 66 | create_regions(self) |
| 67 | 67 | ||
| 68 | def create_items(self): | 68 | def create_items(self): |
| 69 | pool = [self.create_item(name) for name in self.player_logic.real_items] | 69 | pool = [self.create_item(name) for name in self.player_logic.real_items] |
| diff --git a/regions.py b/regions.py index 464e9a1..5fddabd 100644 --- a/regions.py +++ b/regions.py | |||
| @@ -4,7 +4,6 @@ from BaseClasses import Entrance, ItemClassification, Region | |||
| 4 | from .datatypes import Room, RoomAndDoor | 4 | from .datatypes import Room, RoomAndDoor |
| 5 | from .items import LingoItem | 5 | from .items import LingoItem |
| 6 | from .locations import LingoLocation | 6 | from .locations import LingoLocation |
| 7 | from .player_logic import LingoPlayerLogic | ||
| 8 | from .rules import lingo_can_use_entrance, make_location_lambda | 7 | from .rules import lingo_can_use_entrance, make_location_lambda |
| 9 | from .static_logic import ALL_ROOMS, PAINTINGS | 8 | from .static_logic import ALL_ROOMS, PAINTINGS |
| 10 | 9 | ||
| @@ -12,14 +11,14 @@ if TYPE_CHECKING: | |||
| 12 | from . import LingoWorld | 11 | from . import LingoWorld |
| 13 | 12 | ||
| 14 | 13 | ||
| 15 | def create_region(room: Room, world: "LingoWorld", player_logic: LingoPlayerLogic) -> Region: | 14 | def create_region(room: Room, world: "LingoWorld") -> Region: |
| 16 | new_region = Region(room.name, world.player, world.multiworld) | 15 | new_region = Region(room.name, world.player, world.multiworld) |
| 17 | for location in player_logic.locations_by_room.get(room.name, {}): | 16 | for location in world.player_logic.locations_by_room.get(room.name, {}): |
| 18 | new_location = LingoLocation(world.player, location.name, location.code, new_region) | 17 | new_location = LingoLocation(world.player, location.name, location.code, new_region) |
| 19 | new_location.access_rule = make_location_lambda(location, world, player_logic) | 18 | new_location.access_rule = make_location_lambda(location, world) |
| 20 | new_region.locations.append(new_location) | 19 | new_region.locations.append(new_location) |
| 21 | if location.name in player_logic.event_loc_to_item: | 20 | if location.name in world.player_logic.event_loc_to_item: |
| 22 | event_name = player_logic.event_loc_to_item[location.name] | 21 | event_name = world.player_logic.event_loc_to_item[location.name] |
| 23 | event_item = LingoItem(event_name, ItemClassification.progression, None, world.player) | 22 | event_item = LingoItem(event_name, ItemClassification.progression, None, world.player) |
| 24 | new_location.place_locked_item(event_item) | 23 | new_location.place_locked_item(event_item) |
| 25 | 24 | ||
| @@ -27,22 +26,21 @@ def create_region(room: Room, world: "LingoWorld", player_logic: LingoPlayerLogi | |||
| 27 | 26 | ||
| 28 | 27 | ||
| 29 | def connect_entrance(regions: Dict[str, Region], source_region: Region, target_region: Region, description: str, | 28 | def connect_entrance(regions: Dict[str, Region], source_region: Region, target_region: Region, description: str, |
| 30 | door: Optional[RoomAndDoor], world: "LingoWorld", player_logic: LingoPlayerLogic): | 29 | door: Optional[RoomAndDoor], world: "LingoWorld"): |
| 31 | connection = Entrance(world.player, description, source_region) | 30 | connection = Entrance(world.player, description, source_region) |
| 32 | connection.access_rule = lambda state: lingo_can_use_entrance(state, target_region.name, door, world, player_logic) | 31 | connection.access_rule = lambda state: lingo_can_use_entrance(state, target_region.name, door, world) |
| 33 | 32 | ||
| 34 | source_region.exits.append(connection) | 33 | source_region.exits.append(connection) |
| 35 | connection.connect(target_region) | 34 | connection.connect(target_region) |
| 36 | 35 | ||
| 37 | if door is not None: | 36 | if door is not None: |
| 38 | effective_room = target_region.name if door.room is None else door.room | 37 | effective_room = target_region.name if door.room is None else door.room |
| 39 | if door.door not in player_logic.item_by_door.get(effective_room, {}): | 38 | if door.door not in world.player_logic.item_by_door.get(effective_room, {}): |
| 40 | for region in player_logic.calculate_door_requirements(effective_room, door.door, world).rooms: | 39 | for region in world.player_logic.calculate_door_requirements(effective_room, door.door, world).rooms: |
| 41 | world.multiworld.register_indirect_condition(regions[region], connection) | 40 | world.multiworld.register_indirect_condition(regions[region], connection) |
| 42 | 41 | ||
| 43 | 42 | ||
| 44 | def connect_painting(regions: Dict[str, Region], warp_enter: str, warp_exit: str, world: "LingoWorld", | 43 | def connect_painting(regions: Dict[str, Region], warp_enter: str, warp_exit: str, world: "LingoWorld") -> None: |
| 45 | player_logic: LingoPlayerLogic) -> None: | ||
| 46 | source_painting = PAINTINGS[warp_enter] | 44 | source_painting = PAINTINGS[warp_enter] |
| 47 | target_painting = PAINTINGS[warp_exit] | 45 | target_painting = PAINTINGS[warp_exit] |
| 48 | 46 | ||
| @@ -50,11 +48,10 @@ def connect_painting(regions: Dict[str, Region], warp_enter: str, warp_exit: str | |||
| 50 | source_region = regions[source_painting.room] | 48 | source_region = regions[source_painting.room] |
| 51 | 49 | ||
| 52 | entrance_name = f"{source_painting.room} to {target_painting.room} ({source_painting.id} Painting)" | 50 | entrance_name = f"{source_painting.room} to {target_painting.room} ({source_painting.id} Painting)" |
| 53 | connect_entrance(regions, source_region, target_region, entrance_name, source_painting.required_door, world, | 51 | connect_entrance(regions, source_region, target_region, entrance_name, source_painting.required_door, world) |
| 54 | player_logic) | ||
| 55 | 52 | ||
| 56 | 53 | ||
| 57 | def create_regions(world: "LingoWorld", player_logic: LingoPlayerLogic) -> None: | 54 | def create_regions(world: "LingoWorld") -> None: |
| 58 | regions = { | 55 | regions = { |
| 59 | "Menu": Region("Menu", world.player, world.multiworld) | 56 | "Menu": Region("Menu", world.player, world.multiworld) |
| 60 | } | 57 | } |
| @@ -64,7 +61,7 @@ def create_regions(world: "LingoWorld", player_logic: LingoPlayerLogic) -> None: | |||
| 64 | 61 | ||
| 65 | # Instantiate all rooms as regions with their locations first. | 62 | # Instantiate all rooms as regions with their locations first. |
| 66 | for room in ALL_ROOMS: | 63 | for room in ALL_ROOMS: |
| 67 | regions[room.name] = create_region(room, world, player_logic) | 64 | regions[room.name] = create_region(room, world) |
| 68 | 65 | ||
| 69 | # Connect all created regions now that they exist. | 66 | # Connect all created regions now that they exist. |
| 70 | for room in ALL_ROOMS: | 67 | for room in ALL_ROOMS: |
| @@ -80,18 +77,17 @@ def create_regions(world: "LingoWorld", player_logic: LingoPlayerLogic) -> None: | |||
| 80 | else: | 77 | else: |
| 81 | entrance_name += f" (through {room.name} - {entrance.door.door})" | 78 | entrance_name += f" (through {room.name} - {entrance.door.door})" |
| 82 | 79 | ||
| 83 | connect_entrance(regions, regions[entrance.room], regions[room.name], entrance_name, entrance.door, world, | 80 | connect_entrance(regions, regions[entrance.room], regions[room.name], entrance_name, entrance.door, world) |
| 84 | player_logic) | ||
| 85 | 81 | ||
| 86 | # Add the fake pilgrimage. | 82 | # Add the fake pilgrimage. |
| 87 | connect_entrance(regions, regions["Outside The Agreeable"], regions["Pilgrim Antechamber"], "Pilgrimage", | 83 | connect_entrance(regions, regions["Outside The Agreeable"], regions["Pilgrim Antechamber"], "Pilgrimage", |
| 88 | RoomAndDoor("Pilgrim Antechamber", "Pilgrimage"), world, player_logic) | 84 | RoomAndDoor("Pilgrim Antechamber", "Pilgrimage"), world) |
| 89 | 85 | ||
| 90 | if early_color_hallways: | 86 | if early_color_hallways: |
| 91 | regions["Starting Room"].connect(regions["Outside The Undeterred"], "Early Color Hallways") | 87 | regions["Starting Room"].connect(regions["Outside The Undeterred"], "Early Color Hallways") |
| 92 | 88 | ||
| 93 | if painting_shuffle: | 89 | if painting_shuffle: |
| 94 | for warp_enter, warp_exit in player_logic.painting_mapping.items(): | 90 | for warp_enter, warp_exit in world.player_logic.painting_mapping.items(): |
| 95 | connect_painting(regions, warp_enter, warp_exit, world, player_logic) | 91 | connect_painting(regions, warp_enter, warp_exit, world) |
| 96 | 92 | ||
| 97 | world.multiworld.regions += regions.values() | 93 | world.multiworld.regions += regions.values() |
| diff --git a/rules.py b/rules.py index 054c330..4e12938 100644 --- a/rules.py +++ b/rules.py | |||
| @@ -2,61 +2,58 @@ from typing import TYPE_CHECKING | |||
| 2 | 2 | ||
| 3 | from BaseClasses import CollectionState | 3 | from BaseClasses import CollectionState |
| 4 | from .datatypes import RoomAndDoor | 4 | from .datatypes import RoomAndDoor |
| 5 | from .player_logic import AccessRequirements, LingoPlayerLogic, PlayerLocation | 5 | from .player_logic import AccessRequirements, PlayerLocation |
| 6 | from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS | 6 | from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS |
| 7 | 7 | ||
| 8 | if TYPE_CHECKING: | 8 | if TYPE_CHECKING: |
| 9 | from . import LingoWorld | 9 | from . import LingoWorld |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | def lingo_can_use_entrance(state: CollectionState, room: str, door: RoomAndDoor, world: "LingoWorld", | 12 | def lingo_can_use_entrance(state: CollectionState, room: str, door: RoomAndDoor, world: "LingoWorld"): |
| 13 | player_logic: LingoPlayerLogic): | ||
| 14 | if door is None: | 13 | if door is None: |
| 15 | return True | 14 | return True |
| 16 | 15 | ||
| 17 | effective_room = room if door.room is None else door.room | 16 | effective_room = room if door.room is None else door.room |
| 18 | return _lingo_can_open_door(state, effective_room, door.door, world, player_logic) | 17 | return _lingo_can_open_door(state, effective_room, door.door, world) |
| 19 | 18 | ||
| 20 | 19 | ||
| 21 | def lingo_can_use_location(state: CollectionState, location: PlayerLocation, world: "LingoWorld", | 20 | def lingo_can_use_location(state: CollectionState, location: PlayerLocation, world: "LingoWorld"): |
| 22 | player_logic: LingoPlayerLogic): | 21 | return _lingo_can_satisfy_requirements(state, location.access, world) |
| 23 | return _lingo_can_satisfy_requirements(state, location.access, world, player_logic) | ||
| 24 | 22 | ||
| 25 | 23 | ||
| 26 | def lingo_can_use_mastery_location(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic): | 24 | def lingo_can_use_mastery_location(state: CollectionState, world: "LingoWorld"): |
| 27 | satisfied_count = 0 | 25 | satisfied_count = 0 |
| 28 | for access_req in player_logic.mastery_reqs: | 26 | for access_req in world.player_logic.mastery_reqs: |
| 29 | if _lingo_can_satisfy_requirements(state, access_req, world, player_logic): | 27 | if _lingo_can_satisfy_requirements(state, access_req, world): |
| 30 | satisfied_count += 1 | 28 | satisfied_count += 1 |
| 31 | return satisfied_count >= world.options.mastery_achievements.value | 29 | return satisfied_count >= world.options.mastery_achievements.value |
| 32 | 30 | ||
| 33 | 31 | ||
| 34 | def lingo_can_use_level_2_location(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic): | 32 | def lingo_can_use_level_2_location(state: CollectionState, world: "LingoWorld"): |
| 35 | counted_panels = 0 | 33 | counted_panels = 0 |
| 36 | state.update_reachable_regions(world.player) | 34 | state.update_reachable_regions(world.player) |
| 37 | for region in state.reachable_regions[world.player]: | 35 | for region in state.reachable_regions[world.player]: |
| 38 | for access_req, panel_count in player_logic.counting_panel_reqs.get(region.name, []): | 36 | for access_req, panel_count in world.player_logic.counting_panel_reqs.get(region.name, []): |
| 39 | if _lingo_can_satisfy_requirements(state, access_req, world, player_logic): | 37 | if _lingo_can_satisfy_requirements(state, access_req, world): |
| 40 | counted_panels += panel_count | 38 | counted_panels += panel_count |
| 41 | if counted_panels >= world.options.level_2_requirement.value - 1: | 39 | if counted_panels >= world.options.level_2_requirement.value - 1: |
| 42 | return True | 40 | return True |
| 43 | # THE MASTER has to be handled separately, because it has special access rules. | 41 | # THE MASTER has to be handled separately, because it has special access rules. |
| 44 | if state.can_reach("Orange Tower Seventh Floor", "Region", world.player)\ | 42 | if state.can_reach("Orange Tower Seventh Floor", "Region", world.player)\ |
| 45 | and lingo_can_use_mastery_location(state, world, player_logic): | 43 | and lingo_can_use_mastery_location(state, world): |
| 46 | counted_panels += 1 | 44 | counted_panels += 1 |
| 47 | if counted_panels >= world.options.level_2_requirement.value - 1: | 45 | if counted_panels >= world.options.level_2_requirement.value - 1: |
| 48 | return True | 46 | return True |
| 49 | return False | 47 | return False |
| 50 | 48 | ||
| 51 | 49 | ||
| 52 | def _lingo_can_satisfy_requirements(state: CollectionState, access: AccessRequirements, world: "LingoWorld", | 50 | def _lingo_can_satisfy_requirements(state: CollectionState, access: AccessRequirements, world: "LingoWorld"): |
| 53 | player_logic: LingoPlayerLogic): | ||
| 54 | for req_room in access.rooms: | 51 | for req_room in access.rooms: |
| 55 | if not state.can_reach(req_room, "Region", world.player): | 52 | if not state.can_reach(req_room, "Region", world.player): |
| 56 | return False | 53 | return False |
| 57 | 54 | ||
| 58 | for req_door in access.doors: | 55 | for req_door in access.doors: |
| 59 | if not _lingo_can_open_door(state, req_door.room, req_door.door, world, player_logic): | 56 | if not _lingo_can_open_door(state, req_door.room, req_door.door, world): |
| 60 | return False | 57 | return False |
| 61 | 58 | ||
| 62 | if len(access.colors) > 0 and world.options.shuffle_colors: | 59 | if len(access.colors) > 0 and world.options.shuffle_colors: |
| @@ -67,15 +64,14 @@ def _lingo_can_satisfy_requirements(state: CollectionState, access: AccessRequir | |||
| 67 | return True | 64 | return True |
| 68 | 65 | ||
| 69 | 66 | ||
| 70 | def _lingo_can_open_door(state: CollectionState, room: str, door: str, world: "LingoWorld", | 67 | def _lingo_can_open_door(state: CollectionState, room: str, door: str, world: "LingoWorld"): |
| 71 | player_logic: LingoPlayerLogic): | ||
| 72 | """ | 68 | """ |
| 73 | Determines whether a door can be opened | 69 | Determines whether a door can be opened |
| 74 | """ | 70 | """ |
| 75 | if door not in player_logic.item_by_door.get(room, {}): | 71 | if door not in world.player_logic.item_by_door.get(room, {}): |
| 76 | return _lingo_can_satisfy_requirements(state, player_logic.door_reqs[room][door], world, player_logic) | 72 | return _lingo_can_satisfy_requirements(state, world.player_logic.door_reqs[room][door], world) |
| 77 | 73 | ||
| 78 | item_name = player_logic.item_by_door[room][door] | 74 | item_name = world.player_logic.item_by_door[room][door] |
| 79 | if item_name in PROGRESSIVE_ITEMS: | 75 | if item_name in PROGRESSIVE_ITEMS: |
| 80 | progression = PROGRESSION_BY_ROOM[room][door] | 76 | progression = PROGRESSION_BY_ROOM[room][door] |
| 81 | return state.has(item_name, world.player, progression.index) | 77 | return state.has(item_name, world.player, progression.index) |
| @@ -83,12 +79,12 @@ def _lingo_can_open_door(state: CollectionState, room: str, door: str, world: "L | |||
| 83 | return state.has(item_name, world.player) | 79 | return state.has(item_name, world.player) |
| 84 | 80 | ||
| 85 | 81 | ||
| 86 | def make_location_lambda(location: PlayerLocation, world: "LingoWorld", player_logic: LingoPlayerLogic): | 82 | def make_location_lambda(location: PlayerLocation, world: "LingoWorld"): |
| 87 | if location.name == player_logic.mastery_location: | 83 | if location.name == world.player_logic.mastery_location: |
| 88 | return lambda state: lingo_can_use_mastery_location(state, world, player_logic) | 84 | return lambda state: lingo_can_use_mastery_location(state, world) |
| 89 | 85 | ||
| 90 | if world.options.level_2_requirement > 1\ | 86 | if world.options.level_2_requirement > 1\ |
| 91 | and (location.name == "Second Room - ANOTHER TRY" or location.name == player_logic.level_2_location): | 87 | and (location.name == "Second Room - ANOTHER TRY" or location.name == world.player_logic.level_2_location): |
| 92 | return lambda state: lingo_can_use_level_2_location(state, world, player_logic) | 88 | return lambda state: lingo_can_use_level_2_location(state, world) |
| 93 | 89 | ||
| 94 | return lambda state: lingo_can_use_location(state, location, world, player_logic) | 90 | return lambda state: lingo_can_use_location(state, location, world) |
