From b007a42487f35ddf464ba63e0d4bf1850c9116ff Mon Sep 17 00:00:00 2001 From: Rjosephson Date: Tue, 9 Apr 2024 13:14:18 -0600 Subject: [PATCH] Ror2: Add progressive stages option (#2813) --- worlds/ror2/__init__.py | 27 +++++++++++++++++++++------ worlds/ror2/docs/en_Risk of Rain 2.md | 1 - worlds/ror2/items.py | 2 +- worlds/ror2/options.py | 13 +++++++++++++ worlds/ror2/rules.py | 23 ++++++++++------------- worlds/ror2/test/test_mithrix_goal.py | 4 +++- 6 files changed, 48 insertions(+), 22 deletions(-) diff --git a/worlds/ror2/__init__.py b/worlds/ror2/__init__.py index 6574a176..5afdb797 100644 --- a/worlds/ror2/__init__.py +++ b/worlds/ror2/__init__.py @@ -44,8 +44,8 @@ class RiskOfRainWorld(World): } location_name_to_id = item_pickups - data_version = 8 - required_client_version = (0, 4, 4) + data_version = 9 + required_client_version = (0, 4, 5) web = RiskOfWeb() total_revivals: int @@ -91,6 +91,17 @@ class RiskOfRainWorld(World): # only mess with the environments if they are set as items if self.options.goal == "explore": + # check to see if the user doesn't want to use stages, and to figure out what type of stages are being used. + if not self.options.require_stages: + if not self.options.progressive_stages: + self.multiworld.push_precollected(self.multiworld.create_item("Stage 1", self.player)) + self.multiworld.push_precollected(self.multiworld.create_item("Stage 2", self.player)) + self.multiworld.push_precollected(self.multiworld.create_item("Stage 3", self.player)) + self.multiworld.push_precollected(self.multiworld.create_item("Stage 4", self.player)) + else: + for _ in range(4): + self.multiworld.push_precollected(self.multiworld.create_item("Progressive Stage", self.player)) + # figure out all available ordered stages for each tier environment_available_orderedstages_table = environment_vanilla_orderedstages_table if self.options.dlc_sotv: @@ -121,8 +132,12 @@ class RiskOfRainWorld(World): total_locations = self.options.total_locations.value else: # explore mode - # Add Stage items for logic gates - itempool += ["Stage 1", "Stage 2", "Stage 3", "Stage 4"] + + # Add Stage items to the pool + if self.options.require_stages: + itempool += ["Stage 1", "Stage 2", "Stage 3", "Stage 4"] if not self.options.progressive_stages else \ + ["Progressive Stage"] * 4 + total_locations = len( get_locations( chests=self.options.chests_per_stage.value, @@ -206,8 +221,8 @@ class RiskOfRainWorld(World): options_dict = self.options.as_dict("item_pickup_step", "shrine_use_step", "goal", "victory", "total_locations", "chests_per_stage", "shrines_per_stage", "scavengers_per_stage", "scanner_per_stage", "altars_per_stage", "total_revivals", - "start_with_revive", "final_stage_death", "death_link", - casing="camel") + "start_with_revive", "final_stage_death", "death_link", "require_stages", + "progressive_stages", casing="camel") return { **options_dict, "seed": "".join(self.random.choice(string.digits) for _ in range(16)), diff --git a/worlds/ror2/docs/en_Risk of Rain 2.md b/worlds/ror2/docs/en_Risk of Rain 2.md index b2210e34..651c89a3 100644 --- a/worlds/ror2/docs/en_Risk of Rain 2.md +++ b/worlds/ror2/docs/en_Risk of Rain 2.md @@ -57,7 +57,6 @@ options apply, so each Risk of Rain 2 player slot in the multiworld needs to be for example, have two players trade off hosting and making progress on each other's player slot, but a single co-op instance can't make progress towards multiple player slots in the multiworld. -Explore mode is untested in multiplayer and will likely not work until a later release. ## What Risk of Rain items can appear in other players' worlds? diff --git a/worlds/ror2/items.py b/worlds/ror2/items.py index 449686d0..35860308 100644 --- a/worlds/ror2/items.py +++ b/worlds/ror2/items.py @@ -59,7 +59,7 @@ stage_table: Dict[str, RiskOfRainItemData] = { "Stage 2": RiskOfRainItemData("Stage", 2 + stage_offset, ItemClassification.progression), "Stage 3": RiskOfRainItemData("Stage", 3 + stage_offset, ItemClassification.progression), "Stage 4": RiskOfRainItemData("Stage", 4 + stage_offset, ItemClassification.progression), - + "Progressive Stage": RiskOfRainItemData("Stage", 5 + stage_offset, ItemClassification.progression), } item_table = {**upgrade_table, **other_table, **filler_table, **trap_table, **stage_table} diff --git a/worlds/ror2/options.py b/worlds/ror2/options.py index abb8e91d..066c8c85 100644 --- a/worlds/ror2/options.py +++ b/worlds/ror2/options.py @@ -151,6 +151,17 @@ class DLC_SOTV(Toggle): display_name = "Enable DLC - SOTV" +class RequireStages(DefaultOnToggle): + """Add Stage items to the pool to block access to the next set of environments.""" + display_name = "Require Stages" + + +class ProgressiveStages(DefaultOnToggle): + """This will convert Stage items to be a progressive item. For example instead of "Stage 2" it would be + "Progressive Stage" """ + display_name = "Progressive Stages" + + class GreenScrap(Range): """Weight of Green Scraps in the item pool. @@ -378,6 +389,8 @@ class ROR2Options(PerGameCommonOptions): start_with_revive: StartWithRevive final_stage_death: FinalStageDeath dlc_sotv: DLC_SOTV + require_stages: RequireStages + progressive_stages: ProgressiveStages death_link: DeathLink item_pickup_step: ItemPickupStep shrine_use_step: ShrineUseStep diff --git a/worlds/ror2/rules.py b/worlds/ror2/rules.py index b4d5fe68..2e6b018f 100644 --- a/worlds/ror2/rules.py +++ b/worlds/ror2/rules.py @@ -15,6 +15,13 @@ def has_entrance_access_rule(multiworld: MultiWorld, stage: str, region: str, pl entrance.access_rule = rule +def has_stage_access_rule(multiworld: MultiWorld, stage: str, amount: int, region: str, player: int) -> None: + rule = lambda state: state.has(region, player) and \ + (state.has(stage, player) or state.count("Progressive Stage", player) >= amount) + for entrance in multiworld.get_region(region, player).entrances: + entrance.access_rule = rule + + def has_all_items(multiworld: MultiWorld, items: Set[str], region: str, player: int) -> None: rule = lambda state: state.has_all(items, player) and state.has(region, player) for entrance in multiworld.get_region(region, player).entrances: @@ -43,15 +50,6 @@ def check_location(state, environment: str, player: int, item_number: int, item_ return state.can_reach(f"{environment}: {item_name} {item_number - 1}", "Location", player) -# unlock event to next set of stages -def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int) -> None: - if stage_number == 4: - return - rule = lambda state: state.has(f"Stage {stage_number + 1}", player) - for entrance in multiworld.get_region(f"OrderedStage_{stage_number + 1}", player).entrances: - entrance.access_rule = rule - - def set_rules(ror2_world: "RiskOfRainWorld") -> None: player = ror2_world.player multiworld = ror2_world.multiworld @@ -124,8 +122,7 @@ def set_rules(ror2_world: "RiskOfRainWorld") -> None: for newt in range(1, newts + 1): has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar") if i > 0: - has_entrance_access_rule(multiworld, f"Stage {i}", environment_name, player) - get_stage_event(multiworld, player, i) + has_stage_access_rule(multiworld, f"Stage {i}", i, environment_name, player) if ror2_options.dlc_sotv: for i in range(len(environment_sotv_orderedstages_table)): @@ -143,10 +140,10 @@ def set_rules(ror2_world: "RiskOfRainWorld") -> None: for newt in range(1, newts + 1): has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar") if i > 0: - has_entrance_access_rule(multiworld, f"Stage {i}", environment_name, player) + has_stage_access_rule(multiworld, f"Stage {i}", i, environment_name, player) has_entrance_access_rule(multiworld, "Hidden Realm: A Moment, Fractured", "Hidden Realm: A Moment, Whole", player) - has_entrance_access_rule(multiworld, "Stage 1", "Hidden Realm: Bazaar Between Time", player) + has_stage_access_rule(multiworld, "Stage 1", 1, "Hidden Realm: Bazaar Between Time", player) has_entrance_access_rule(multiworld, "Hidden Realm: Bazaar Between Time", "Void Fields", player) has_entrance_access_rule(multiworld, "Stage 5", "Commencement", player) has_entrance_access_rule(multiworld, "Stage 5", "Hidden Realm: A Moment, Fractured", player) diff --git a/worlds/ror2/test/test_mithrix_goal.py b/worlds/ror2/test/test_mithrix_goal.py index 03b82311..a52301be 100644 --- a/worlds/ror2/test/test_mithrix_goal.py +++ b/worlds/ror2/test/test_mithrix_goal.py @@ -3,7 +3,9 @@ from . import RoR2TestBase class MithrixGoalTest(RoR2TestBase): options = { - "victory": "mithrix" + "victory": "mithrix", + "require_stages": "true", + "progressive_stages": "false" } def test_mithrix(self) -> None: