The Witness: Make Elevators Come To You an OptionSet (#4000)
* Split elevators come to you * . * unit test * mypy stuff * Fine. I'll fix the fcking commented out code. Happy? * ruff * """""Backwards compatibility""""" * ruff * make it look better * # * fix presets * fix a unit test * Make that explicit in the code * Improve description
This commit is contained in:
		
							parent
							
								
									a5231a27cc
								
							
						
					
					
						commit
						b605fb1032
					
				| 
						 | 
				
			
			@ -1,11 +0,0 @@
 | 
			
		|||
New Connections:
 | 
			
		||||
Quarry - Quarry Elevator - TrueOneWay
 | 
			
		||||
Outside Quarry - Quarry Elevator - TrueOneWay
 | 
			
		||||
Outside Bunker - Bunker Elevator - TrueOneWay
 | 
			
		||||
Outside Swamp - Swamp Long Bridge - TrueOneWay
 | 
			
		||||
Swamp Near Boat - Swamp Long Bridge - TrueOneWay
 | 
			
		||||
Town Red Rooftop - Town Maze Rooftop - TrueOneWay
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Requirement Changes:
 | 
			
		||||
0x035DE - 0x17E2B - True
 | 
			
		||||
| 
						 | 
				
			
			@ -204,10 +204,6 @@ def get_caves_except_path_to_challenge_exclusion_list() -> List[str]:
 | 
			
		|||
    return get_adjustment_file("settings/Exclusions/Caves_Except_Path_To_Challenge.txt")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_elevators_come_to_you() -> List[str]:
 | 
			
		||||
    return get_adjustment_file("settings/Door_Shuffle/Elevators_Come_To_You.txt")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_entity_hunt() -> List[str]:
 | 
			
		||||
    return get_adjustment_file("settings/Entity_Hunt.txt")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,18 @@ from dataclasses import dataclass
 | 
			
		|||
 | 
			
		||||
from schema import And, Schema
 | 
			
		||||
 | 
			
		||||
from Options import Choice, DefaultOnToggle, OptionDict, OptionGroup, PerGameCommonOptions, Range, Toggle, Visibility
 | 
			
		||||
from Options import (
 | 
			
		||||
    Choice,
 | 
			
		||||
    DefaultOnToggle,
 | 
			
		||||
    OptionDict,
 | 
			
		||||
    OptionError,
 | 
			
		||||
    OptionGroup,
 | 
			
		||||
    OptionSet,
 | 
			
		||||
    PerGameCommonOptions,
 | 
			
		||||
    Range,
 | 
			
		||||
    Toggle,
 | 
			
		||||
    Visibility,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from .data import static_logic as static_witness_logic
 | 
			
		||||
from .data.item_definition_classes import ItemCategory, WeightedItemDefinition
 | 
			
		||||
| 
						 | 
				
			
			@ -294,12 +305,33 @@ class ChallengeLasers(Range):
 | 
			
		|||
    default = 11
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ElevatorsComeToYou(Toggle):
 | 
			
		||||
class ElevatorsComeToYou(OptionSet):
 | 
			
		||||
    """
 | 
			
		||||
    If on, the Quarry Elevator, Bunker Elevator and Swamp Long Bridge will "come to you" if you approach them.
 | 
			
		||||
    This does actually affect logic as it allows unintended backwards / early access into these areas.
 | 
			
		||||
    In vanilla, some bridges/elevators come to you if you walk up to them when they are not currently there.
 | 
			
		||||
    However, there are some that don't. Notably, this prevents Quarry Elevator from being a logical access method into Quarry, because you can send it away without riding ot and then permanently be locked out of using it.
 | 
			
		||||
 | 
			
		||||
    This option allows you to change specific elevators/bridges to "come to you" as well.
 | 
			
		||||
 | 
			
		||||
    - Quarry Elevator: Makes the Quarry Elevator come down when you approach it from lower Quarry and back up when you approach it from above
 | 
			
		||||
    - Swamp Long Bridge: Rotates the side you approach it from towards you, but also rotates the other side away
 | 
			
		||||
    - Bunker Elevator: Makes the Bunker Elevator come to any floor that you approach it from, meaning it can be accessed from the roof immediately
 | 
			
		||||
    """
 | 
			
		||||
    display_name = "All Bridges & Elevators come to you"
 | 
			
		||||
 | 
			
		||||
    # Used to be a toggle
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_text(cls, text: str):
 | 
			
		||||
        if text.lower() in {"off", "0", "false", "none", "null", "no"}:
 | 
			
		||||
            raise OptionError('elevators_come_to_you is an OptionSet now. The equivalent of "false" is {}')
 | 
			
		||||
        if text.lower() in {"on", "1", "true", "yes"}:
 | 
			
		||||
            raise OptionError(
 | 
			
		||||
                f'elevators_come_to_you is an OptionSet now. The equivalent of "true" is {set(cls.valid_keys)}'
 | 
			
		||||
            )
 | 
			
		||||
        return super().from_text(text)
 | 
			
		||||
 | 
			
		||||
    display_name = "Elevators come to you"
 | 
			
		||||
 | 
			
		||||
    valid_keys = frozenset({"Quarry Elevator", "Swamp Long Bridge", "Bunker Elevator"})
 | 
			
		||||
    default = frozenset({"Quarry Elevator"})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrapPercentage(Range):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,6 @@ from .data.utils import (
 | 
			
		|||
    get_discard_exclusion_list,
 | 
			
		||||
    get_early_caves_list,
 | 
			
		||||
    get_early_caves_start_list,
 | 
			
		||||
    get_elevators_come_to_you,
 | 
			
		||||
    get_entity_hunt,
 | 
			
		||||
    get_ep_all_individual,
 | 
			
		||||
    get_ep_easy,
 | 
			
		||||
| 
						 | 
				
			
			@ -626,8 +625,29 @@ class WitnessPlayerLogic:
 | 
			
		|||
        if world.options.early_caves == "add_to_pool" and not remote_doors:
 | 
			
		||||
            adjustment_linesets_in_order.append(get_early_caves_list())
 | 
			
		||||
 | 
			
		||||
        if world.options.elevators_come_to_you:
 | 
			
		||||
            adjustment_linesets_in_order.append(get_elevators_come_to_you())
 | 
			
		||||
        if "Quarry Elevator" in world.options.elevators_come_to_you:
 | 
			
		||||
            adjustment_linesets_in_order.append([
 | 
			
		||||
                "New Connections:",
 | 
			
		||||
                "Quarry - Quarry Elevator - TrueOneWay",
 | 
			
		||||
                "Outside Quarry - Quarry Elevator - TrueOneWay",
 | 
			
		||||
            ])
 | 
			
		||||
        if "Bunker Elevator" in world.options.elevators_come_to_you:
 | 
			
		||||
            adjustment_linesets_in_order.append([
 | 
			
		||||
                "New Connections:",
 | 
			
		||||
                "Outside Bunker - Bunker Elevator - TrueOneWay",
 | 
			
		||||
            ])
 | 
			
		||||
        if "Swamp Long Bridge" in world.options.elevators_come_to_you:
 | 
			
		||||
            adjustment_linesets_in_order.append([
 | 
			
		||||
                "New Connections:",
 | 
			
		||||
                "Outside Swamp - Swamp Long Bridge - TrueOneWay",
 | 
			
		||||
                "Swamp Near Boat - Swamp Long Bridge - TrueOneWay",
 | 
			
		||||
                "Requirement Changes:",
 | 
			
		||||
                "0x035DE - 0x17E2B - True",  # Swamp Purple Sand Bottom EP
 | 
			
		||||
            ])
 | 
			
		||||
        # if "Town Maze Rooftop Bridge" in world.options.elevators_come_to_you:
 | 
			
		||||
        #     adjustment_linesets_in_order.append([
 | 
			
		||||
        #         "New Connections:"
 | 
			
		||||
        #         "Town Red Rooftop - Town Maze Rooftop - TrueOneWay"
 | 
			
		||||
 | 
			
		||||
        if world.options.victory_condition == "panel_hunt":
 | 
			
		||||
            adjustment_linesets_in_order.append(get_entity_hunt())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,8 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
 | 
			
		|||
        "challenge_lasers": 11,
 | 
			
		||||
 | 
			
		||||
        "early_caves": EarlyCaves.option_off,
 | 
			
		||||
        "elevators_come_to_you": False,
 | 
			
		||||
 | 
			
		||||
        "elevators_come_to_you": ElevatorsComeToYou.default,
 | 
			
		||||
 | 
			
		||||
        "trap_percentage": TrapPercentage.default,
 | 
			
		||||
        "puzzle_skip_amount": PuzzleSkipAmount.default,
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +74,8 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
 | 
			
		|||
        "challenge_lasers": 9,
 | 
			
		||||
 | 
			
		||||
        "early_caves": EarlyCaves.option_off,
 | 
			
		||||
        "elevators_come_to_you": False,
 | 
			
		||||
 | 
			
		||||
        "elevators_come_to_you": ElevatorsComeToYou.default,
 | 
			
		||||
 | 
			
		||||
        "trap_percentage": TrapPercentage.default,
 | 
			
		||||
        "puzzle_skip_amount": 15,
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +113,8 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
 | 
			
		|||
        "challenge_lasers": 9,
 | 
			
		||||
 | 
			
		||||
        "early_caves": EarlyCaves.option_off,
 | 
			
		||||
        "elevators_come_to_you": True,
 | 
			
		||||
 | 
			
		||||
        "elevators_come_to_you": ElevatorsComeToYou.valid_keys,
 | 
			
		||||
 | 
			
		||||
        "trap_percentage": TrapPercentage.default,
 | 
			
		||||
        "puzzle_skip_amount": 15,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,49 +1,25 @@
 | 
			
		|||
from ..test import WitnessMultiworldTestBase, WitnessTestBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestElevatorsComeToYou(WitnessTestBase):
 | 
			
		||||
    options = {
 | 
			
		||||
        "elevators_come_to_you": True,
 | 
			
		||||
        "shuffle_doors": "mixed",
 | 
			
		||||
        "shuffle_symbols": False,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def test_bunker_laser(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        In elevators_come_to_you, Bunker can be entered from the back.
 | 
			
		||||
        This means that you can access the laser with just Bunker Elevator Control (Panel).
 | 
			
		||||
        It also means that you can, for example, access UV Room with the Control and the Elevator Room Entry Door.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", self.player))
 | 
			
		||||
 | 
			
		||||
        self.collect_by_name("Bunker Elevator Control (Panel)")
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", self.player))
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker UV Room 2", "Location", self.player))
 | 
			
		||||
 | 
			
		||||
        self.collect_by_name("Bunker Elevator Room Entry (Door)")
 | 
			
		||||
        self.collect_by_name("Bunker Drop-Down Door Controls (Panel)")
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.multiworld.state.can_reach("Bunker UV Room 2", "Location", self.player))
 | 
			
		||||
from ..test import WitnessMultiworldTestBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestElevatorsComeToYouBleed(WitnessMultiworldTestBase):
 | 
			
		||||
    options_per_world = [
 | 
			
		||||
        {
 | 
			
		||||
            "elevators_come_to_you": False,
 | 
			
		||||
            "elevators_come_to_you": {},
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "elevators_come_to_you": True,
 | 
			
		||||
            "elevators_come_to_you": {"Quarry Elevator", "Swamp Long Bridge", "Bunker Elevator"},
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "elevators_come_to_you": False,
 | 
			
		||||
            "elevators_come_to_you": {}
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    common_options = {
 | 
			
		||||
        "shuffle_symbols": False,
 | 
			
		||||
        "shuffle_doors": "panels",
 | 
			
		||||
        "shuffle_boat": True,
 | 
			
		||||
        "shuffle_EPs": "individual",
 | 
			
		||||
        "obelisk_keys": False,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def test_correct_access_per_player(self) -> None:
 | 
			
		||||
| 
						 | 
				
			
			@ -53,14 +29,22 @@ class TestElevatorsComeToYouBleed(WitnessMultiworldTestBase):
 | 
			
		|||
        (This is essentially a "does connection info bleed over" test).
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 1))
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 2))
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 3))
 | 
			
		||||
        combinations = [
 | 
			
		||||
            ("Quarry Elevator Control (Panel)", "Quarry Boathouse Intro Left"),
 | 
			
		||||
            ("Swamp Long Bridge (Panel)", "Swamp Long Bridge Side EP"),
 | 
			
		||||
            ("Bunker Elevator Control (Panel)", "Bunker Laser Panel"),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        self.collect_by_name(["Bunker Elevator Control (Panel)"], 1)
 | 
			
		||||
        self.collect_by_name(["Bunker Elevator Control (Panel)"], 2)
 | 
			
		||||
        self.collect_by_name(["Bunker Elevator Control (Panel)"], 3)
 | 
			
		||||
        for item, location in combinations:
 | 
			
		||||
            with self.subTest(f"Test that {item} only locks {location} for player 2"):
 | 
			
		||||
                self.assertFalse(self.multiworld.state.can_reach_location(location, 1))
 | 
			
		||||
                self.assertFalse(self.multiworld.state.can_reach_location(location, 2))
 | 
			
		||||
                self.assertFalse(self.multiworld.state.can_reach_location(location, 3))
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 1))
 | 
			
		||||
        self.assertTrue(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 2))
 | 
			
		||||
        self.assertFalse(self.multiworld.state.can_reach("Bunker Laser Panel", "Location", 3))
 | 
			
		||||
                self.collect_by_name(item, 1)
 | 
			
		||||
                self.collect_by_name(item, 2)
 | 
			
		||||
                self.collect_by_name(item, 3)
 | 
			
		||||
 | 
			
		||||
                self.assertFalse(self.multiworld.state.can_reach_location(location, 1))
 | 
			
		||||
                self.assertTrue(self.multiworld.state.can_reach_location(location, 2))
 | 
			
		||||
                self.assertFalse(self.multiworld.state.can_reach_location(location, 3))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
from ..options import ElevatorsComeToYou
 | 
			
		||||
from ..test import WitnessTestBase
 | 
			
		||||
 | 
			
		||||
# These are just some random options combinations, just to catch whether I broke anything obvious
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +20,7 @@ class TestExpertNonRandomizedEPs(WitnessTestBase):
 | 
			
		|||
class TestVanillaAutoElevatorsPanels(WitnessTestBase):
 | 
			
		||||
    options = {
 | 
			
		||||
        "puzzle_randomization": "none",
 | 
			
		||||
        "elevators_come_to_you": True,
 | 
			
		||||
        "elevators_come_to_you": ElevatorsComeToYou.valid_keys - ElevatorsComeToYou.default,  # Opposite of default
 | 
			
		||||
        "shuffle_doors": "panels",
 | 
			
		||||
        "victory_condition": "mountain_box_short",
 | 
			
		||||
        "early_caves": True,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue