From 99128f67a3b4478a5561b1ba33a2f3588a61a1df Mon Sep 17 00:00:00 2001 From: Star Rauchenberger <fefferburbia@gmail.com> Date: Sun, 10 Dec 2023 13:15:42 -0500 Subject: Lingo: Fix entrance checking being broken on default settings (#2506) The most serious issue this PR addresses is that entrances that use doors without items (a small subset of doors when door shuffle is on, but *every* door when door shuffle is off, which is the default) underestimate the requirements needed to use that entrance. The logic would calculate the panels needed to open the door, but would neglect to keep track of the rooms those panels were in, meaning that doors would be considered openable if you had the colors needed to solve a panel that's in a room you have no access to. Another issue is that, previously, logic would always consider the "ANOTHER TRY" panel accessible for the purposes of the LEVEL 2 panel hunt. This could result in seeds where the player is expected to have exactly the correct number of solves to reach LEVEL 2, but in reality is short by one because ANOTHER TRY itself is not revealed until the panel hunt is complete. This change marks ANOTHER TRY as non-counting, because even though it is technically a counting panel in-game, it can never contribute to the LEVEL 2 panel hunt. This issue could also apply to THE MASTER, since it is the only other counting panel with special access rules, although it is much less likely. This change adds special handling for counting THE MASTER. These issues were possible to manifest whenever the LEVEL 2 panel hunt was enabled, which it is by default. Smaller logic issues also fixed in this PR: * The Orange Tower Basement MASTERY panel was marked as requiring the mastery doors to be opened, when it was actually possible to get it without them by using a painting to get into the room. * The Pilgrim Room painting item was incorrectly being marked as a filler item, despite it being progression. * There has been another update to the game that adds connections between areas that were previously not connected. These changes were additive, which is why they are not critical. * The panel stacks in the rhyme room now require both colours on each panel. --- __init__.py | 6 ++- data/LL1.yaml | 112 +++++++++++++++++++++++++++++++++++++---------- data/ids.yaml | 5 ++- player_logic.py | 31 ++++++++++--- regions.py | 15 ++----- rules.py | 23 +++------- utils/validate_config.rb | 2 +- 7 files changed, 131 insertions(+), 63 deletions(-) diff --git a/__init__.py b/__init__.py index a8dac86..f22d344 100644 --- a/__init__.py +++ b/__init__.py @@ -104,9 +104,11 @@ class LingoWorld(World): classification = item.classification if hasattr(self, "options") and self.options.shuffle_paintings and len(item.painting_ids) > 0\ and len(item.door_ids) == 0 and all(painting_id not in self.player_logic.painting_mapping - for painting_id in item.painting_ids): + for painting_id in item.painting_ids)\ + and "pilgrim_painting2" not in item.painting_ids: # If this is a "door" that just moves one or more paintings, and painting shuffle is on and those paintings - # go nowhere, then this item should not be progression. + # go nowhere, then this item should not be progression. The Pilgrim Room painting is special and needs to be + # excluded from this. classification = ItemClassification.filler return LingoItem(name, classification, item.code, self.player) diff --git a/data/LL1.yaml b/data/LL1.yaml index 8a4f831..ea5886f 100644 --- a/data/LL1.yaml +++ b/data/LL1.yaml @@ -373,6 +373,7 @@ ANOTHER TRY: id: Entry Room/Panel_advance tag: topwhite + non_counting: True # This is a counting panel in-game, but it can never count towards the LEVEL 2 panel hunt. LEVEL 2: # We will set up special rules for this in code. id: EndPanel/Panel_level_2 @@ -1033,6 +1034,8 @@ Hallway Room (3): True Hallway Room (4): True Hedge Maze: True # through the door to the sectioned-off part of the hedge maze + Cellar: + door: Lookout Entrance panels: MASSACRED: id: Palindrome Room/Panel_massacred_sacred @@ -1168,11 +1171,21 @@ - KEEP - BAILEY - TOWER + Lookout Entrance: + id: Cross Room Doors/Door_missing + location_name: Outside The Agreeable - Lookout Panels + panels: + - NORTH + - WINTER + - DIAMONDS + - FIRE paintings: - id: panda_painting orientation: south - id: eyes_yellow_painting orientation: east + - id: pencil_painting7 + orientation: north progression: Progressive Hallway Room: - Hallway Door @@ -2043,7 +2056,7 @@ door: Sixth Floor Cellar: room: Room Room - door: Shortcut to Fifth Floor + door: Cellar Exit Welcome Back Area: door: Welcome Back Art Gallery: @@ -2302,9 +2315,6 @@ id: Master Room/Panel_mastery_mastery3 tag: midwhite hunt: True - required_door: - room: Orange Tower Seventh Floor - door: Mastery THE LIBRARY: id: EndPanel/Panel_library check: True @@ -2675,6 +2685,10 @@ Outside The Undeterred: True Outside The Agreeable: True Outside The Wanderer: True + The Observant: True + Art Gallery: True + The Scientific: True + Cellar: True Orange Tower Fifth Floor: room: Orange Tower Fifth Floor door: Welcome Back @@ -2991,8 +3005,7 @@ PATS: id: Rhyme Room/Panel_wrath_path colors: purple - tag: midpurp and rhyme - copy_to_sign: sign15 + tag: forbid KNIGHT: id: Rhyme Room/Panel_knight_write colors: purple @@ -3158,6 +3171,8 @@ door: Painting Shortcut painting: True Room Room: True # trapdoor + Outside The Agreeable: + painting: True panels: UNOPEN: id: Truncate Room/Panel_unopened_open @@ -6299,17 +6314,22 @@ SKELETON: id: Double Room/Panel_bones_syn tag: syn rhyme + colors: purple subtag: bot link: rhyme BONES REPENTANCE: id: Double Room/Panel_sentence_rhyme - colors: purple + colors: + - purple + - blue tag: whole rhyme subtag: top link: rhyme SENTENCE WORD: id: Double Room/Panel_sentence_whole - colors: blue + colors: + - purple + - blue tag: whole rhyme subtag: bot link: rhyme SENTENCE @@ -6321,6 +6341,7 @@ link: rhyme DREAM FANTASY: id: Double Room/Panel_dream_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme DREAM @@ -6332,6 +6353,7 @@ link: rhyme MYSTERY SECRET: id: Double Room/Panel_mystery_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme MYSTERY @@ -6386,25 +6408,33 @@ door: Nines FERN: id: Double Room/Panel_return_rhyme - colors: purple + colors: + - purple + - black tag: ant rhyme subtag: top link: rhyme RETURN STAY: id: Double Room/Panel_return_ant - colors: black + colors: + - purple + - black tag: ant rhyme subtag: bot link: rhyme RETURN FRIEND: id: Double Room/Panel_descend_rhyme - colors: purple + colors: + - purple + - black tag: ant rhyme subtag: top link: rhyme DESCEND RISE: id: Double Room/Panel_descend_ant - colors: black + colors: + - purple + - black tag: ant rhyme subtag: bot link: rhyme DESCEND @@ -6416,6 +6446,7 @@ link: rhyme JUMP BOUNCE: id: Double Room/Panel_jump_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme JUMP @@ -6427,6 +6458,7 @@ link: rhyme FALL PLUNGE: id: Double Room/Panel_fall_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme FALL @@ -6456,13 +6488,17 @@ panels: BIRD: id: Double Room/Panel_word_rhyme - colors: purple + colors: + - purple + - blue tag: whole rhyme subtag: top link: rhyme WORD LETTER: id: Double Room/Panel_word_whole - colors: blue + colors: + - purple + - blue tag: whole rhyme subtag: bot link: rhyme WORD @@ -6474,6 +6510,7 @@ link: rhyme HIDDEN CONCEALED: id: Double Room/Panel_hidden_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme HIDDEN @@ -6485,6 +6522,7 @@ link: rhyme SILENT MUTE: id: Double Room/Panel_silent_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme SILENT @@ -6531,6 +6569,7 @@ link: rhyme BLOCKED OBSTRUCTED: id: Double Room/Panel_blocked_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme BLOCKED @@ -6542,6 +6581,7 @@ link: rhyme RISE SWELL: id: Double Room/Panel_rise_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme RISE @@ -6553,6 +6593,7 @@ link: rhyme ASCEND CLIMB: id: Double Room/Panel_ascend_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme ASCEND @@ -6564,6 +6605,7 @@ link: rhyme DOUBLE DUPLICATE: id: Double Room/Panel_double_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme DOUBLE @@ -6642,6 +6684,7 @@ link: rhyme CHILD KID: id: Double Room/Panel_child_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme CHILD @@ -6653,6 +6696,7 @@ link: rhyme CRYSTAL QUARTZ: id: Double Room/Panel_crystal_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme CRYSTAL @@ -6664,6 +6708,7 @@ link: rhyme CREATIVE INNOVATIVE (Bottom): id: Double Room/Panel_creative_syn + colors: purple tag: syn rhyme subtag: bot link: rhyme CREATIVE @@ -6882,7 +6927,7 @@ event: True panels: - WALL (1) - Shortcut to Fifth Floor: + Cellar Exit: id: - Tower Room Area Doors/Door_panel_basement - Tower Room Area Doors/Door_panel_basement2 @@ -6895,7 +6940,10 @@ door: Excavation Orange Tower Fifth Floor: room: Room Room - door: Shortcut to Fifth Floor + door: Cellar Exit + Outside The Agreeable: + room: Outside The Agreeable + door: Lookout Entrance Outside The Wise: entrances: Orange Tower Sixth Floor: @@ -7319,49 +7367,65 @@ link: change GRAVITY PART: id: Chemistry Room/Panel_physics_2 - colors: blue + colors: + - blue + - red tag: blue mid red bot subtag: mid link: xur PARTICLE MATTER: id: Chemistry Room/Panel_physics_1 - colors: red + colors: + - blue + - red tag: blue mid red bot subtag: bot link: xur PARTICLE ELECTRIC: id: Chemistry Room/Panel_physics_6 - colors: purple + colors: + - purple + - red tag: purple mid red bot subtag: mid link: xpr ELECTRON ATOM (1): id: Chemistry Room/Panel_physics_3 - colors: red + colors: + - purple + - red tag: purple mid red bot subtag: bot link: xpr ELECTRON NEUTRAL: id: Chemistry Room/Panel_physics_7 - colors: purple + colors: + - purple + - red tag: purple mid red bot subtag: mid link: xpr NEUTRON ATOM (2): id: Chemistry Room/Panel_physics_4 - colors: red + colors: + - purple + - red tag: purple mid red bot subtag: bot link: xpr NEUTRON PROPEL: id: Chemistry Room/Panel_physics_8 - colors: purple + colors: + - purple + - red tag: purple mid red bot subtag: mid link: xpr PROTON ATOM (3): id: Chemistry Room/Panel_physics_5 - colors: red + colors: + - purple + - red tag: purple mid red bot subtag: bot link: xpr PROTON diff --git a/data/ids.yaml b/data/ids.yaml index 1a1ceca..3239f21 100644 --- a/data/ids.yaml +++ b/data/ids.yaml @@ -1064,6 +1064,9 @@ doors: Hallway Door: item: 444459 location: 445214 + Lookout Entrance: + item: 444579 + location: 445271 Dread Hallway: Tenacious Entrance: item: 444462 @@ -1402,7 +1405,7 @@ doors: item: 444570 location: 445266 Room Room: - Shortcut to Fifth Floor: + Cellar Exit: item: 444571 location: 445076 Outside The Wise: diff --git a/player_logic.py b/player_logic.py index a0b33d1..b046f1c 100644 --- a/player_logic.py +++ b/player_logic.py @@ -190,6 +190,25 @@ class LingoPlayerLogic: if item.should_include(world): self.real_items.append(name) + # Calculate the requirements for the fake pilgrimage. + fake_pilgrimage = [ + ["Second Room", "Exit Door"], ["Crossroads", "Tower Entrance"], + ["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"], + ["Orange Tower First Floor", "Shortcut to Hub Room"], ["Directional Gallery", "Shortcut to The Undeterred"], + ["Orange Tower First Floor", "Salt Pepper Door"], ["Hub Room", "Crossroads Entrance"], + ["Champion's Rest", "Shortcut to The Steady"], ["The Bearer", "Shortcut to The Bold"], + ["Art Gallery", "Exit"], ["The Tenacious", "Shortcut to Hub Room"], + ["Outside The Agreeable", "Tenacious Entrance"] + ] + pilgrimage_reqs = AccessRequirements() + for door in fake_pilgrimage: + door_object = DOORS_BY_ROOM[door[0]][door[1]] + if door_object.event or world.options.shuffle_doors == ShuffleDoors.option_none: + pilgrimage_reqs.merge(self.calculate_door_requirements(door[0], door[1], world)) + else: + pilgrimage_reqs.doors.add(RoomAndDoor(door[0], door[1])) + self.door_reqs.setdefault("Pilgrim Antechamber", {})["Pilgrimage"] = pilgrimage_reqs + # Create the paintings mapping, if painting shuffle is on. if painting_shuffle: # Shuffle paintings until we get something workable. @@ -369,11 +388,9 @@ class LingoPlayerLogic: door_object = DOORS_BY_ROOM[room][door] for req_panel in door_object.panels: - if req_panel.room is not None and req_panel.room != room: - access_reqs.rooms.add(req_panel.room) - - sub_access_reqs = self.calculate_panel_requirements(room if req_panel.room is None else req_panel.room, - req_panel.panel, world) + panel_room = room if req_panel.room is None else req_panel.room + access_reqs.rooms.add(panel_room) + sub_access_reqs = self.calculate_panel_requirements(panel_room, req_panel.panel, world) access_reqs.merge(sub_access_reqs) self.door_reqs[room][door] = access_reqs @@ -397,8 +414,8 @@ class LingoPlayerLogic: unhindered_panels_by_color: dict[Optional[str], int] = {} for panel_name, panel_data in room_data.items(): - # We won't count non-counting panels. - if panel_data.non_counting: + # We won't count non-counting panels. THE MASTER has special access rules and is handled separately. + if panel_data.non_counting or panel_name == "THE MASTER": continue # We won't coalesce any panels that have requirements beyond colors. To simplify things for now, we will diff --git a/regions.py b/regions.py index c24144a..bdc42f4 100644 --- a/regions.py +++ b/regions.py @@ -4,7 +4,7 @@ from BaseClasses import Entrance, ItemClassification, Region from .items import LingoItem from .locations import LingoLocation from .player_logic import LingoPlayerLogic -from .rules import lingo_can_use_entrance, lingo_can_use_pilgrimage, make_location_lambda +from .rules import lingo_can_use_entrance, make_location_lambda from .static_logic import ALL_ROOMS, PAINTINGS, Room, RoomAndDoor if TYPE_CHECKING: @@ -25,15 +25,6 @@ def create_region(room: Room, world: "LingoWorld", player_logic: LingoPlayerLogi return new_region -def handle_pilgrim_room(regions: Dict[str, Region], world: "LingoWorld", player_logic: LingoPlayerLogic) -> None: - target_region = regions["Pilgrim Antechamber"] - source_region = regions["Outside The Agreeable"] - source_region.connect( - target_region, - "Pilgrimage", - lambda state: lingo_can_use_pilgrimage(state, world, player_logic)) - - def connect_entrance(regions: Dict[str, Region], source_region: Region, target_region: Region, description: str, door: Optional[RoomAndDoor], world: "LingoWorld", player_logic: LingoPlayerLogic): connection = Entrance(world.player, description, source_region) @@ -91,7 +82,9 @@ def create_regions(world: "LingoWorld", player_logic: LingoPlayerLogic) -> None: connect_entrance(regions, regions[entrance.room], regions[room.name], entrance_name, entrance.door, world, player_logic) - handle_pilgrim_room(regions, world, player_logic) + # Add the fake pilgrimage. + connect_entrance(regions, regions["Outside The Agreeable"], regions["Pilgrim Antechamber"], "Pilgrimage", + RoomAndDoor("Pilgrim Antechamber", "Pilgrimage"), world, player_logic) if early_color_hallways: regions["Starting Room"].connect(regions["Outside The Undeterred"], "Early Color Hallways") diff --git a/rules.py b/rules.py index ee9dcc4..481fab1 100644 --- a/rules.py +++ b/rules.py @@ -17,23 +17,6 @@ def lingo_can_use_entrance(state: CollectionState, room: str, door: RoomAndDoor, return _lingo_can_open_door(state, effective_room, door.door, world, player_logic) -def lingo_can_use_pilgrimage(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic): - fake_pilgrimage = [ - ["Second Room", "Exit Door"], ["Crossroads", "Tower Entrance"], - ["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"], - ["Orange Tower First Floor", "Shortcut to Hub Room"], ["Directional Gallery", "Shortcut to The Undeterred"], - ["Orange Tower First Floor", "Salt Pepper Door"], ["Hub Room", "Crossroads Entrance"], - ["Champion's Rest", "Shortcut to The Steady"], ["The Bearer", "Shortcut to The Bold"], - ["Art Gallery", "Exit"], ["The Tenacious", "Shortcut to Hub Room"], - ["Outside The Agreeable", "Tenacious Entrance"] - ] - for entrance in fake_pilgrimage: - if not _lingo_can_open_door(state, entrance[0], entrance[1], world, player_logic): - return False - - return True - - def lingo_can_use_location(state: CollectionState, location: PlayerLocation, world: "LingoWorld", player_logic: LingoPlayerLogic): return _lingo_can_satisfy_requirements(state, location.access, world, player_logic) @@ -56,6 +39,12 @@ def lingo_can_use_level_2_location(state: CollectionState, world: "LingoWorld", counted_panels += panel_count if counted_panels >= world.options.level_2_requirement.value - 1: return True + # THE MASTER has to be handled separately, because it has special access rules. + if state.can_reach("Orange Tower Seventh Floor", "Region", world.player)\ + and lingo_can_use_mastery_location(state, world, player_logic): + counted_panels += 1 + if counted_panels >= world.options.level_2_requirement.value - 1: + return True return False diff --git a/utils/validate_config.rb b/utils/validate_config.rb index bed5188..3ac49dc 100644 --- a/utils/validate_config.rb +++ b/utils/validate_config.rb @@ -40,7 +40,7 @@ mentioned_panels = Set[] door_groups = {} directives = Set["entrances", "panels", "doors", "paintings", "progression"] -panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting"] +panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt"] door_directives = Set["id", "painting_id", "panels", "item_name", "location_name", "skip_location", "skip_item", "group", "include_reduce", "junk_item", "event"] painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"] -- cgit 1.4.1