From 3ca34171721d7b8f8062010fe9f94eb83463940b Mon Sep 17 00:00:00 2001
From: Ishigh1 <bonjour940@yahoo.fr>
Date: Tue, 13 Feb 2024 22:46:18 +0100
Subject: [PATCH 1/5] LADX: Added some resilience to non-ASCII player names
 (#2642)

* Added some resilience to non-ASCII player names or items

* Also the client, not even sure if switching to ascii is useful here

* Split a long line in two
---
 LinksAwakeningClient.py    | 3 ++-
 worlds/ladx/LADXR/utils.py | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/LinksAwakeningClient.py b/LinksAwakeningClient.py
index f3fc9d2cdb72..a51645feac92 100644
--- a/LinksAwakeningClient.py
+++ b/LinksAwakeningClient.py
@@ -348,7 +348,8 @@ async def wait_for_retroarch_connection(self):
                         await asyncio.sleep(1.0)
                         continue
                 self.stop_bizhawk_spam = False
-                logger.info(f"Connected to Retroarch {version.decode('ascii')} running {rom_name.decode('ascii')}")
+                logger.info(f"Connected to Retroarch {version.decode('ascii', errors='replace')} "
+                            f"running {rom_name.decode('ascii', errors='replace')}")
                 return
             except (BlockingIOError, TimeoutError, ConnectionResetError):
                 await asyncio.sleep(1.0)
diff --git a/worlds/ladx/LADXR/utils.py b/worlds/ladx/LADXR/utils.py
index fcf1d2bb56e7..5f8b2685550d 100644
--- a/worlds/ladx/LADXR/utils.py
+++ b/worlds/ladx/LADXR/utils.py
@@ -146,7 +146,7 @@ def setReplacementName(key: str, value: str) -> None:
 
 def formatText(instr: str, *, center: bool = False, ask: Optional[str] = None) -> bytes:
     instr = instr.format(**_NAMES)
-    s = instr.encode("ascii")
+    s = instr.encode("ascii", errors="replace")
     s = s.replace(b"'", b"^")
 
     def padLine(line: bytes) -> bytes:
@@ -169,7 +169,7 @@ def padLine(line: bytes) -> bytes:
         if result_line:
             result += padLine(result_line)
     if ask is not None:
-        askbytes = ask.encode("ascii")
+        askbytes = ask.encode("ascii", errors="replace")
         result = result.rstrip()
         while len(result) % 32 != 16:
             result += b' '

From 74e79bff06612e312f0b9bbb52254a3bfa770c11 Mon Sep 17 00:00:00 2001
From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
Date: Tue, 13 Feb 2024 22:47:19 +0100
Subject: [PATCH 2/5] The Witness: Event System & Item Classification System
 revamp (#2652)

Two things have been happening.

**Incorrect Events**
Spoiler logs containing events that just straight up have an incorrect name and shouldn't be there. E.g. "Symmetry Island Yellow 3 solved - Monastery Laser Activation" when playing Laser Shuffle where this event should not exist, because Laser Activations are governed by the Laser items.

Now to be clear - There are no logic issues with it. The event will be in the spoiler log, but it won't actually be used in the way that its name suggests.
Basically, every panel in the game has exactly one event name. If the panel is referenced by another panel, it will reference the event instead. So, the Symmetry Laser Panel location will reference Symmetry Island Yellow 3, and an event is created for Symmetry Island Yellow 3. The only problem is the **name**: The canonical name for the event is related to "Symmetry Island Yellow 3" is "Monastery Laser Activation", because that's another thing that panel does sometimes.

From now on, event names are tied to both the panel referencing and the panel being referenced. Only once the referincing panel actually references the dependent panel (during the dependency reduction process in generate_early), is the event actually created.

This also removes some spoiler log clutter where unused events were just in the location list.

**Item classifications**
When playing shuffle_doors, there are a lot of doors in the game that are logically useless depending on settings. When that happens, they should get downgraded from progression to useful. The previous system for this was jank and terrible. Now there is a better system for it, and many items have been added to it. :)
---
 worlds/witness/items.py                       |  28 +----
 worlds/witness/locations.py                   |   2 +-
 worlds/witness/player_logic.py                | 119 ++++++++++++++----
 worlds/witness/rules.py                       |   2 +-
 .../Exclusions/Disable_Unrandomized.txt       |  10 +-
 5 files changed, 108 insertions(+), 53 deletions(-)

diff --git a/worlds/witness/items.py b/worlds/witness/items.py
index 3a8b35793a3b..41bc3c1bb8da 100644
--- a/worlds/witness/items.py
+++ b/worlds/witness/items.py
@@ -112,30 +112,12 @@ def __init__(self, world: "WitnessWorld", logic: WitnessPlayerLogic, locat: Witn
             or name in logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME
         }
 
-        # Adjust item classifications based on game settings.
-        eps_shuffled = self._world.options.shuffle_EPs
-        come_to_you = self._world.options.elevators_come_to_you
-        difficulty = self._world.options.puzzle_randomization
+        # Downgrade door items
         for item_name, item_data in self.item_data.items():
-            if not eps_shuffled and item_name in {"Monastery Garden Entry (Door)",
-                                                  "Monastery Shortcuts",
-                                                  "Quarry Boathouse Hook Control (Panel)",
-                                                  "Windmill Turn Control (Panel)"}:
-                # Downgrade doors that only gate progress in EP shuffle.
-                item_data.classification = ItemClassification.useful
-            elif not come_to_you and not eps_shuffled and item_name in {"Quarry Elevator Control (Panel)",
-                                                                        "Swamp Long Bridge (Panel)"}:
-                # These Bridges/Elevators are not logical access because they may leave you stuck.
-                item_data.classification = ItemClassification.useful
-            elif item_name in {"River Monastery Garden Shortcut (Door)",
-                               "Monastery Laser Shortcut (Door)",
-                               "Orchard Second Gate (Door)",
-                               "Jungle Bamboo Laser Shortcut (Door)",
-                               "Caves Elevator Controls (Panel)"}:
-                # Downgrade doors that don't gate progress.
-                item_data.classification = ItemClassification.useful
-            elif item_name == "Keep Pressure Plates 2 Exit (Door)" and not (difficulty == "none" and eps_shuffled):
-                # PP2EP requires the door in vanilla puzzles, otherwise it's unnecessary
+            if not isinstance(item_data.definition, DoorItemDefinition):
+                continue
+
+            if all(not self._logic.solvability_guaranteed(e_hex) for e_hex in item_data.definition.panel_id_hexes):
                 item_data.classification = ItemClassification.useful
 
         # Build the mandatory item list.
diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py
index 1f73c2c031a2..781cc4e25d94 100644
--- a/worlds/witness/locations.py
+++ b/worlds/witness/locations.py
@@ -543,7 +543,7 @@ def __init__(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic):
         )
 
         event_locations = {
-            p for p in player_logic.EVENT_PANELS
+            p for p in player_logic.USED_EVENT_NAMES_BY_HEX
         }
 
         self.EVENT_LOCATION_TABLE = {
diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py
index 24dfa7d9c871..229da0a2879a 100644
--- a/worlds/witness/player_logic.py
+++ b/worlds/witness/player_logic.py
@@ -101,8 +101,11 @@ def reduce_req_within_region(self, panel_hex: str) -> FrozenSet[FrozenSet[str]]:
             for option_entity in option:
                 dep_obj = self.REFERENCE_LOGIC.ENTITIES_BY_HEX.get(option_entity)
 
-                if option_entity in self.EVENT_NAMES_BY_HEX:
+                if option_entity in self.ALWAYS_EVENT_NAMES_BY_HEX:
                     new_items = frozenset({frozenset([option_entity])})
+                elif (panel_hex, option_entity) in self.CONDITIONAL_EVENTS:
+                    new_items = frozenset({frozenset([option_entity])})
+                    self.USED_EVENT_NAMES_BY_HEX[option_entity] = self.CONDITIONAL_EVENTS[(panel_hex, option_entity)]
                 elif option_entity in {"7 Lasers", "11 Lasers", "7 Lasers + Redirect", "11 Lasers + Redirect",
                                        "PP2 Weirdness", "Theater to Tunnels"}:
                     new_items = frozenset({frozenset([option_entity])})
@@ -170,14 +173,11 @@ def make_single_adjustment(self, adj_type: str, line: str):
         if adj_type == "Event Items":
             line_split = line.split(" - ")
             new_event_name = line_split[0]
-            hex_set = line_split[1].split(",")
-
-            for entity, event_name in self.EVENT_NAMES_BY_HEX.items():
-                if event_name == new_event_name:
-                    self.DONT_MAKE_EVENTS.add(entity)
+            entity_hex = line_split[1]
+            dependent_hex_set = line_split[2].split(",")
 
-            for hex_code in hex_set:
-                self.EVENT_NAMES_BY_HEX[hex_code] = new_event_name
+            for dependent_hex in dependent_hex_set:
+                self.CONDITIONAL_EVENTS[(entity_hex, dependent_hex)] = new_event_name
 
             return
 
@@ -437,7 +437,7 @@ def make_options_adjustments(self, world: "WitnessWorld"):
                 obelisk = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[self.REFERENCE_LOGIC.EP_TO_OBELISK_SIDE[ep_hex]]
                 obelisk_name = obelisk["checkName"]
                 ep_name = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[ep_hex]["checkName"]
-                self.EVENT_NAMES_BY_HEX[ep_hex] = f"{obelisk_name} - {ep_name}"
+                self.ALWAYS_EVENT_NAMES_BY_HEX[ep_hex] = f"{obelisk_name} - {ep_name}"
         else:
             adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_obelisks()[1:])
 
@@ -505,7 +505,8 @@ def make_dependency_reduced_checklist(self):
                 for option in connection[1]:
                     individual_entity_requirements = []
                     for entity in option:
-                        if entity in self.EVENT_NAMES_BY_HEX or entity not in self.REFERENCE_LOGIC.ENTITIES_BY_HEX:
+                        if (entity in self.ALWAYS_EVENT_NAMES_BY_HEX
+                                or entity not in self.REFERENCE_LOGIC.ENTITIES_BY_HEX):
                             individual_entity_requirements.append(frozenset({frozenset({entity})}))
                         else:
                             entity_req = self.reduce_req_within_region(entity)
@@ -522,6 +523,72 @@ def make_dependency_reduced_checklist(self):
 
             self.CONNECTIONS_BY_REGION_NAME[region] = new_connections
 
+    def solvability_guaranteed(self, entity_hex: str):
+        return not (
+            entity_hex in self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY
+            or entity_hex in self.COMPLETELY_DISABLED_ENTITIES
+            or entity_hex in self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES
+        )
+
+    def determine_unrequired_entities(self, world: "WitnessWorld"):
+        """Figure out which major items are actually useless in this world's settings"""
+
+        # Gather quick references to relevant options
+        eps_shuffled = world.options.shuffle_EPs
+        come_to_you = world.options.elevators_come_to_you
+        difficulty = world.options.puzzle_randomization
+        discards_shuffled = world.options.shuffle_discarded_panels
+        boat_shuffled = world.options.shuffle_boat
+        symbols_shuffled = world.options.shuffle_symbols
+        disable_non_randomized = world.options.disable_non_randomized_puzzles
+        postgame_included = world.options.shuffle_postgame
+        goal = world.options.victory_condition
+        doors = world.options.shuffle_doors
+        shortbox_req = world.options.mountain_lasers
+        longbox_req = world.options.challenge_lasers
+
+        # Make some helper booleans so it is easier to follow what's going on
+        mountain_upper_is_in_postgame = (
+                goal == "mountain_box_short"
+                or goal == "mountain_box_long" and longbox_req <= shortbox_req
+        )
+        mountain_upper_included = postgame_included or not mountain_upper_is_in_postgame
+        remote_doors = doors >= 2
+        door_panels = doors == "panels" or doors == "mixed"
+
+        # It is easier to think about when these items *are* required, so we make that dict first
+        # If the entity is disabled anyway, we don't need to consider that case
+        is_item_required_dict = {
+            "0x03750": eps_shuffled,  # Monastery Garden Entry Door
+            "0x275FA": eps_shuffled,  # Boathouse Hook Control
+            "0x17D02": eps_shuffled,  # Windmill Turn Control
+            "0x0368A": symbols_shuffled or door_panels,  # Quarry Stoneworks Stairs Door
+            "0x3865F": symbols_shuffled or door_panels or eps_shuffled,  # Quarry Boathouse 2nd Barrier
+            "0x17CC4": come_to_you or eps_shuffled,  # Quarry Elevator Panel
+            "0x17E2B": come_to_you and boat_shuffled or eps_shuffled,  # Swamp Long Bridge
+            "0x0CF2A": False,  # Jungle Monastery Garden Shortcut
+            "0x17CAA": remote_doors,  # Jungle Monastery Garden Shortcut Panel
+            "0x0364E": False,  # Monastery Laser Shortcut Door
+            "0x03713": remote_doors,  # Monastery Laser Shortcut Panel
+            "0x03313": False,  # Orchard Second Gate
+            "0x337FA": remote_doors,  # Jungle Bamboo Laser Shortcut Panel
+            "0x3873B": False,  # Jungle Bamboo Laser Shortcut Door
+            "0x335AB": False,  # Caves Elevator Controls
+            "0x335AC": False,  # Caves Elevator Controls
+            "0x3369D": False,  # Caves Elevator Controls
+            "0x01BEA": difficulty == "none" and eps_shuffled,  # Keep PP2
+            "0x0A0C9": eps_shuffled or discards_shuffled or disable_non_randomized,  # Cargo Box Entry Door
+            "0x09EEB": discards_shuffled or mountain_upper_included,  # Mountain Floor 2 Elevator Control Panel
+            "0x09EDD": mountain_upper_included,  # Mountain Floor 2 Exit Door
+            "0x17CAB": symbols_shuffled or not disable_non_randomized or "0x17CAB" not in self.DOOR_ITEMS_BY_ID,
+            # Jungle Popup Wall Panel
+        }
+
+        # Now, return the keys of the dict entries where the result is False to get unrequired major items
+        self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY |= {
+            item_name for item_name, is_required in is_item_required_dict.items() if not is_required
+        }
+
     def make_event_item_pair(self, panel: str):
         """
         Makes a pair of an event panel and its event item
@@ -529,21 +596,23 @@ def make_event_item_pair(self, panel: str):
         action = " Opened" if self.REFERENCE_LOGIC.ENTITIES_BY_HEX[panel]["entityType"] == "Door" else " Solved"
 
         name = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[panel]["checkName"] + action
-        if panel not in self.EVENT_NAMES_BY_HEX:
+        if panel not in self.USED_EVENT_NAMES_BY_HEX:
             warning("Panel \"" + name + "\" does not have an associated event name.")
-            self.EVENT_NAMES_BY_HEX[panel] = name + " Event"
-        pair = (name, self.EVENT_NAMES_BY_HEX[panel])
+            self.USED_EVENT_NAMES_BY_HEX[panel] = name + " Event"
+        pair = (name, self.USED_EVENT_NAMES_BY_HEX[panel])
         return pair
 
     def make_event_panel_lists(self):
-        self.EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory"
+        self.ALWAYS_EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory"
 
-        for event_hex, event_name in self.EVENT_NAMES_BY_HEX.items():
-            if event_hex in self.COMPLETELY_DISABLED_ENTITIES or event_hex in self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES:
-                continue
-            self.EVENT_PANELS.add(event_hex)
+        self.USED_EVENT_NAMES_BY_HEX.update(self.ALWAYS_EVENT_NAMES_BY_HEX)
+
+        self.USED_EVENT_NAMES_BY_HEX = {
+            event_hex: event_name for event_hex, event_name in self.USED_EVENT_NAMES_BY_HEX.items()
+            if self.solvability_guaranteed(event_hex)
+        }
 
-        for panel in self.EVENT_PANELS:
+        for panel in self.USED_EVENT_NAMES_BY_HEX:
             pair = self.make_event_item_pair(panel)
             self.EVENT_ITEM_PAIRS[pair[0]] = pair[1]
 
@@ -556,6 +625,8 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
 
         self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES = set()
 
+        self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY = set()
+
         self.THEORETICAL_ITEMS = set()
         self.THEORETICAL_ITEMS_NO_MULTI = set()
         self.MULTI_AMOUNTS = defaultdict(lambda: 1)
@@ -580,16 +651,14 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
 
         # Determining which panels need to be events is a difficult process.
         # At the end, we will have EVENT_ITEM_PAIRS for all the necessary ones.
-        self.EVENT_PANELS = set()
         self.EVENT_ITEM_PAIRS = dict()
-        self.DONT_MAKE_EVENTS = set()
         self.COMPLETELY_DISABLED_ENTITIES = set()
         self.PRECOMPLETED_LOCATIONS = set()
         self.EXCLUDED_LOCATIONS = set()
         self.ADDED_CHECKS = set()
         self.VICTORY_LOCATION = "0x0356B"
 
-        self.EVENT_NAMES_BY_HEX = {
+        self.ALWAYS_EVENT_NAMES_BY_HEX = {
             "0x00509": "+1 Laser (Symmetry Laser)",
             "0x012FB": "+1 Laser (Desert Laser)",
             "0x09F98": "Desert Laser Redirection",
@@ -602,10 +671,14 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
             "0x0C2B2": "+1 Laser (Bunker Laser)",
             "0x00BF6": "+1 Laser (Swamp Laser)",
             "0x028A4": "+1 Laser (Treehouse Laser)",
-            "0x09F7F": "Mountain Entry",
+            "0x17C34": "Mountain Entry",
             "0xFFF00": "Bottom Floor Discard Turns On",
         }
 
+        self.USED_EVENT_NAMES_BY_HEX = {}
+        self.CONDITIONAL_EVENTS = {}
+
         self.make_options_adjustments(world)
+        self.determine_unrequired_entities(world)
         self.make_dependency_reduced_checklist()
         self.make_event_panel_lists()
diff --git a/worlds/witness/rules.py b/worlds/witness/rules.py
index 5eded11ad412..8636829a4ef1 100644
--- a/worlds/witness/rules.py
+++ b/worlds/witness/rules.py
@@ -170,7 +170,7 @@ def _has_item(item: str, world: "WitnessWorld", player: int,
         return lambda state: _can_do_expert_pp2(state, world)
     elif item == "Theater to Tunnels":
         return lambda state: _can_do_theater_to_tunnels(state, world)
-    if item in player_logic.EVENT_PANELS:
+    if item in player_logic.USED_EVENT_NAMES_BY_HEX:
         return _can_solve_panel(item, world, player, player_logic, locat)
 
     prog_item = StaticWitnessLogic.get_parent_progressive_item(item)
diff --git a/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt b/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
index 2419bde06c14..09c366cfaabd 100644
--- a/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
+++ b/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
@@ -1,9 +1,9 @@
 Event Items:
-Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9
-Bunker Laser Activation - 0x00061,0x17D01,0x17C42
-Shadows Laser Activation - 0x00021,0x17D28,0x17C71
-Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x17CF7
-Jungle Popup Wall Lifts - 0x17FA0,0x17D27,0x17F9B,0x17CAB
+Monastery Laser Activation - 0x17C65 - 0x00A5B,0x17CE7,0x17FA9
+Bunker Laser Activation - 0x0C2B2 - 0x00061,0x17D01,0x17C42
+Shadows Laser Activation - 0x181B3 - 0x00021,0x17D28,0x17C71
+Town Tower 4th Door Opens - 0x2779A - 0x17CFB,0x3C12B,0x17CF7
+Jungle Popup Wall Lifts - 0x1475B - 0x17FA0,0x17D27,0x17F9B,0x17CAB
 
 Requirement Changes:
 0x17C65 - 0x00A5B | 0x17CE7 | 0x17FA9

From 57fcdf4fbe6778fe45db54309677f877df51fbf0 Mon Sep 17 00:00:00 2001
From: Bryce Wilson <gyroscope15@gmail.com>
Date: Tue, 13 Feb 2024 14:47:57 -0700
Subject: [PATCH 3/5] Pokemon Emerald: Add missed locations to postgame
 locations group (#2654)

---
 worlds/pokemon_emerald/locations.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/worlds/pokemon_emerald/locations.py b/worlds/pokemon_emerald/locations.py
index bfe5be754585..3d842ecbac98 100644
--- a/worlds/pokemon_emerald/locations.py
+++ b/worlds/pokemon_emerald/locations.py
@@ -114,8 +114,10 @@ def create_location_label_to_id_map() -> Dict[str, int]:
         "Littleroot Town - S.S. Ticket from Norman",
         "SS Tidal - Hidden Item in Lower Deck Trash Can",
         "SS Tidal - TM49 from Thief",
+        "Safari Zone NE - Item on Ledge",
         "Safari Zone NE - Hidden Item North",
         "Safari Zone NE - Hidden Item East",
+        "Safari Zone SE - Item in Grass",
         "Safari Zone SE - Hidden Item in South Grass 1",
         "Safari Zone SE - Hidden Item in South Grass 2",
     }

From 2165253961e3c99ecae4c1c7c41112d9ae782ca9 Mon Sep 17 00:00:00 2001
From: Star Rauchenberger <fefferburbia@gmail.com>
Date: Tue, 13 Feb 2024 19:55:19 -0500
Subject: [PATCH 4/5] Lingo: Detach Art Gallery Exit from Progressive Art
 Gallery (#2739)

The final stage of Progressive Art Gallery opens up the four-way intersection between the Art Gallery, Orange Tower Fifth Floor, The Bearer, and Outside The Initiated. This is a very useful door, and it would be cool to be able to open it without having to get five progressive items. The original reason this was included in the progression was because getting into the back of Art Gallery early would cause sequence breaks. At this point, the way the client handles the Art Gallery has changed enough that it does not matter if the player can go through this door before getting all progressive art galleries.
---
 worlds/lingo/data/LL1.yaml           | 2 +-
 worlds/lingo/test/TestProgressive.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml
index da78a5123df1..6d74a3f0da54 100644
--- a/worlds/lingo/data/LL1.yaml
+++ b/worlds/lingo/data/LL1.yaml
@@ -6193,6 +6193,7 @@
       Exit:
         id: Tower Room Area Doors/Door_painting_exit
         include_reduce: True
+        item_name: Orange Tower Fifth Floor - Quadruple Intersection
         panels:
           - ONE ROAD MANY TURNS
     paintings:
@@ -6212,7 +6213,6 @@
         - Third Floor
         - Fourth Floor
         - Fifth Floor
-        - Exit
   Art Gallery (Second Floor):
     entrances:
       Art Gallery:
diff --git a/worlds/lingo/test/TestProgressive.py b/worlds/lingo/test/TestProgressive.py
index 8edc7ce6ccef..0aaebe9319b5 100644
--- a/worlds/lingo/test/TestProgressive.py
+++ b/worlds/lingo/test/TestProgressive.py
@@ -142,7 +142,7 @@ def test_item(self):
         self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
         self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
 
-        self.collect(progressive_gallery_room[4])
+        self.collect_by_name("Orange Tower Fifth Floor - Quadruple Intersection")
         self.assertTrue(self.multiworld.state.can_reach("Art Gallery", "Region", self.player))
         self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
         self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))

From 2167db5a881870e7eaf34b8eccbf98957a50cd0b Mon Sep 17 00:00:00 2001
From: Star Rauchenberger <fefferburbia@gmail.com>
Date: Tue, 13 Feb 2024 19:56:24 -0500
Subject: [PATCH 5/5] Lingo: Split up Color Hunt and Champion's Rest (#2745)

---
 worlds/lingo/data/LL1.yaml   | 91 ++++++++++++++++--------------------
 worlds/lingo/data/ids.yaml   |  6 +--
 worlds/lingo/player_logic.py |  5 +-
 3 files changed, 46 insertions(+), 56 deletions(-)

diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml
index 6d74a3f0da54..2e18766c017c 100644
--- a/worlds/lingo/data/LL1.yaml
+++ b/worlds/lingo/data/LL1.yaml
@@ -1166,7 +1166,7 @@
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: PURPLE
       Hallway Door:
         id: Red Blue Purple Room Area Doors/Door_room_2
@@ -1957,7 +1957,7 @@
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: RED
       Rhyme Room Entrance:
         id: Double Room Area Doors/Door_room_entry_stairs2
@@ -1975,9 +1975,9 @@
           - Color Arrow Room Doors/Door_orange_hider_1
           - Color Arrow Room Doors/Door_orange_hider_2
           - Color Arrow Room Doors/Door_orange_hider_3
-        location_name: Color Hunt - RED and YELLOW
-        group: Champion's Rest - Color Barriers
-        item_name: Champion's Rest - Orange Barrier
+        location_name: Color Barriers - RED and YELLOW
+        group: Color Hunt Barriers
+        item_name: Color Hunt - Orange Barrier
         panels:
           - RED
           - room: Directional Gallery
@@ -2382,7 +2382,7 @@
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: GREEN
     paintings:
       - id: flower_painting_7
@@ -2893,14 +2893,14 @@
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: BLUE
       Orange Barrier:
         id: Color Arrow Room Doors/Door_orange_3
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: ORANGE
       Initiated Entrance:
         id: Red Blue Purple Room Area Doors/Door_locked_knocked
@@ -2912,9 +2912,9 @@
       # containing region.
       Green Barrier:
         id: Color Arrow Room Doors/Door_green_hider_1
-        location_name: Color Hunt - BLUE and YELLOW
-        item_name: Champion's Rest - Green Barrier
-        group: Champion's Rest - Color Barriers
+        location_name: Color Barriers - BLUE and YELLOW
+        item_name: Color Hunt - Green Barrier
+        group: Color Hunt Barriers
         panels:
           - BLUE
           - room: Directional Gallery
@@ -2924,9 +2924,9 @@
           - Color Arrow Room Doors/Door_purple_hider_1
           - Color Arrow Room Doors/Door_purple_hider_2
           - Color Arrow Room Doors/Door_purple_hider_3
-        location_name: Color Hunt - RED and BLUE
-        item_name: Champion's Rest - Purple Barrier
-        group: Champion's Rest - Color Barriers
+        location_name: Color Barriers - RED and BLUE
+        item_name: Color Hunt - Purple Barrier
+        group: Color Hunt Barriers
         panels:
           - BLUE
           - room: Orange Tower Third Floor
@@ -2936,7 +2936,7 @@
           - Color Arrow Room Doors/Door_all_hider_1
           - Color Arrow Room Doors/Door_all_hider_2
           - Color Arrow Room Doors/Door_all_hider_3
-        location_name: Color Hunt - GREEN, ORANGE and PURPLE
+        location_name: Color Barriers - GREEN, ORANGE and PURPLE
         item_name: Champion's Rest - Entrance
         panels:
           - ORANGE
@@ -3176,8 +3176,8 @@
   Outside The Bold:
     entrances:
       Color Hallways: True
-      Champion's Rest:
-        room: Champion's Rest
+      Color Hunt:
+        room: Color Hunt
         door: Shortcut to The Steady
       The Bearer:
         room: The Bearer
@@ -4002,7 +4002,7 @@
         group: Color Hunt Barriers
         skip_location: True
         panels:
-          - room: Champion's Rest
+          - room: Color Hunt
             panel: YELLOW
     paintings:
       - id: smile_painting_7
@@ -4020,12 +4020,15 @@
         orientation: south
       - id: cherry_painting
         orientation: east
-  Champion's Rest:
+  Color Hunt:
     entrances:
       Outside The Bold:
         door: Shortcut to The Steady
       Orange Tower Fourth Floor: True # sunwarp
       Roof: True # through ceiling of sunwarp
+      Champion's Rest:
+        room: Outside The Initiated
+        door: Entrance
     panels:
       EXIT:
         id: Rock Room/Panel_red_red
@@ -4066,11 +4069,28 @@
         required_door:
           room: Orange Tower Third Floor
           door: Orange Barrier
-      YOU:
-        id: Color Arrow Room/Panel_you
+    doors:
+      Shortcut to The Steady:
+        id: Rock Room Doors/Door_hint
+        panels:
+          - EXIT
+    paintings:
+      - id: arrows_painting_7
+        orientation: east
+      - id: fruitbowl_painting3
+        orientation: west
+        enter_only: True
         required_door:
           room: Outside The Initiated
           door: Entrance
+  Champion's Rest:
+    entrances:
+      Color Hunt:
+        room: Outside The Initiated
+        door: Entrance
+    panels:
+      YOU:
+        id: Color Arrow Room/Panel_you
         check: True
         colors: gray
         tag: forbid
@@ -4078,49 +4098,20 @@
         id: Color Arrow Room/Panel_me
         colors: gray
         tag: forbid
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
       SECRET BLUE:
         # Pretend this and the other two are white, because they are snipes.
         # TODO: Extract them and randomize them?
         id: Color Arrow Room/Panel_secret_blue
         tag: forbid
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
       SECRET YELLOW:
         id: Color Arrow Room/Panel_secret_yellow
         tag: forbid
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
       SECRET RED:
         id: Color Arrow Room/Panel_secret_red
         tag: forbid
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
-    doors:
-      Shortcut to The Steady:
-        id: Rock Room Doors/Door_hint
-        panels:
-          - EXIT
     paintings:
-      - id: arrows_painting_7
-        orientation: east
-      - id: fruitbowl_painting3
-        orientation: west
-        enter_only: True
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
       - id: colors_painting
         orientation: south
-        enter_only: True
-        required_door:
-          room: Outside The Initiated
-          door: Entrance
   The Bearer:
     entrances:
       Outside The Bold:
diff --git a/worlds/lingo/data/ids.yaml b/worlds/lingo/data/ids.yaml
index 2b9e7f3d8ca0..56c22ad1bec5 100644
--- a/worlds/lingo/data/ids.yaml
+++ b/worlds/lingo/data/ids.yaml
@@ -489,7 +489,7 @@ panels:
     WINDWARD: 444803
     LIGHT: 444804
     REWIND: 444805
-  Champion's Rest:
+  Color Hunt:
     EXIT: 444806
     HUES: 444807
     RED: 444808
@@ -498,6 +498,7 @@ panels:
     GREEN: 444811
     PURPLE: 444812
     ORANGE: 444813
+  Champion's Rest:
     YOU: 444814
     ME: 444815
     SECRET BLUE: 444816
@@ -1286,7 +1287,7 @@ doors:
       location: 445246
     Yellow Barrier:
       item: 444538
-  Champion's Rest:
+  Color Hunt:
     Shortcut to The Steady:
       item: 444539
       location: 444806
@@ -1442,7 +1443,6 @@ door_groups:
   Fearless Doors: 444469
   Backside Doors: 444473
   Orange Tower First Floor - Shortcuts: 444484
-  Champion's Rest - Color Barriers: 444489
   Welcome Back Doors: 444492
   Colorful Doors: 444498
   Directional Gallery Doors: 444531
diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py
index f3efc2914c3d..d87aa5672c75 100644
--- a/worlds/lingo/player_logic.py
+++ b/worlds/lingo/player_logic.py
@@ -196,9 +196,8 @@ def __init__(self, world: "LingoWorld"):
             ["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"]
+            ["Color Hunt", "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: