mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-27 05:13:22 -07:00
RoR2: Seekers of the Storm (SOTS) DLC Support (#5569)
This commit is contained in:
@@ -4,7 +4,10 @@ from .items import RiskOfRainItem, item_table, item_pool_weights, offset, filler
|
||||
from .locations import RiskOfRainLocation, item_pickups, get_locations
|
||||
from .rules import set_rules
|
||||
from .ror2environments import environment_vanilla_table, environment_vanilla_orderedstages_table, \
|
||||
environment_sotv_orderedstages_table, environment_sotv_table, collapse_dict_list_vertical, shift_by_offset
|
||||
environment_sotv_orderedstages_table, environment_sotv_table, environment_sost_orderedstages_table, \
|
||||
environment_sost_table, collapse_dict_list_vertical, shift_by_offset, environment_vanilla_variants_table, \
|
||||
environment_vanilla_variant_orderedstages_table, environment_sots_variants_table, \
|
||||
environment_sots_variants_orderedstages_table
|
||||
|
||||
from BaseClasses import Item, ItemClassification, Tutorial
|
||||
from .options import ItemWeights, ROR2Options, ror2_option_groups
|
||||
@@ -46,7 +49,7 @@ class RiskOfRainWorld(World):
|
||||
}
|
||||
location_name_to_id = item_pickups
|
||||
|
||||
required_client_version = (0, 5, 0)
|
||||
required_client_version = (0, 6, 4)
|
||||
web = RiskOfWeb()
|
||||
total_revivals: int
|
||||
|
||||
@@ -62,7 +65,9 @@ class RiskOfRainWorld(World):
|
||||
scavengers=self.options.scavengers_per_stage.value,
|
||||
scanners=self.options.scanner_per_stage.value,
|
||||
altars=self.options.altars_per_stage.value,
|
||||
dlc_sotv=bool(self.options.dlc_sotv.value)
|
||||
dlc_sotv=bool(self.options.dlc_sotv.value),
|
||||
dlc_sots=bool(self.options.dlc_sots.value),
|
||||
stage_variants=bool(self.options.stage_variants)
|
||||
)
|
||||
)
|
||||
self.total_revivals = int(self.options.total_revivals.value / 100 *
|
||||
@@ -71,6 +76,8 @@ class RiskOfRainWorld(World):
|
||||
self.total_revivals -= 1
|
||||
if self.options.victory == "voidling" and not self.options.dlc_sotv:
|
||||
self.options.victory.value = self.options.victory.option_any
|
||||
if self.options.victory == "falseson" and not self.options.dlc_sots:
|
||||
self.options.victory.value = self.options.victory.option_any
|
||||
|
||||
def create_regions(self) -> None:
|
||||
|
||||
@@ -105,16 +112,39 @@ class RiskOfRainWorld(World):
|
||||
|
||||
# figure out all available ordered stages for each tier
|
||||
environment_available_orderedstages_table = environment_vanilla_orderedstages_table
|
||||
environments_pool = shift_by_offset(environment_vanilla_table, environment_offset)
|
||||
# Vanilla Variants
|
||||
if self.options.stage_variants:
|
||||
environment_available_orderedstages_table = \
|
||||
collapse_dict_list_vertical(environment_available_orderedstages_table,
|
||||
environment_vanilla_variant_orderedstages_table)
|
||||
if self.options.dlc_sotv:
|
||||
environment_available_orderedstages_table = \
|
||||
collapse_dict_list_vertical(environment_available_orderedstages_table,
|
||||
environment_sotv_orderedstages_table)
|
||||
if self.options.dlc_sots:
|
||||
environment_available_orderedstages_table = \
|
||||
collapse_dict_list_vertical(environment_available_orderedstages_table,
|
||||
environment_sost_orderedstages_table)
|
||||
if self.options.dlc_sots and self.options.stage_variants:
|
||||
environment_available_orderedstages_table = \
|
||||
collapse_dict_list_vertical(environment_available_orderedstages_table,
|
||||
environment_sots_variants_orderedstages_table)
|
||||
|
||||
environments_pool = shift_by_offset(environment_vanilla_table, environment_offset)
|
||||
|
||||
if self.options.stage_variants:
|
||||
environment_offset_table = shift_by_offset(environment_vanilla_variants_table, environment_offset)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
if self.options.dlc_sotv:
|
||||
environment_offset_table = shift_by_offset(environment_sotv_table, environment_offset)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
if self.options.dlc_sots:
|
||||
environment_offset_table = shift_by_offset(environment_sost_table, environment_offset)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
# SOTS Variant Environments
|
||||
if self.options.dlc_sots and self.options.stage_variants:
|
||||
environment_offset_table = shift_by_offset(environment_sots_variants_table, environment_offset)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
|
||||
# percollect starting environment for stage 1
|
||||
unlock = self.random.choices(list(environment_available_orderedstages_table[0].keys()), k=1)
|
||||
self.multiworld.push_precollected(self.create_item(unlock[0]))
|
||||
@@ -146,7 +176,9 @@ class RiskOfRainWorld(World):
|
||||
scavengers=self.options.scavengers_per_stage.value,
|
||||
scanners=self.options.scanner_per_stage.value,
|
||||
altars=self.options.altars_per_stage.value,
|
||||
dlc_sotv=bool(self.options.dlc_sotv.value)
|
||||
dlc_sotv=bool(self.options.dlc_sotv.value),
|
||||
dlc_sots=bool(self.options.dlc_sots.value),
|
||||
stage_variants=bool(self.options.stage_variants)
|
||||
)
|
||||
)
|
||||
# Create junk items
|
||||
@@ -223,7 +255,7 @@ class RiskOfRainWorld(World):
|
||||
"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", "require_stages",
|
||||
"progressive_stages", casing="camel")
|
||||
"progressive_stages", "stage_variants", "show_seer_portals", casing="camel")
|
||||
return {
|
||||
**options_dict,
|
||||
"seed": "".join(self.random.choice(string.digits) for _ in range(16)),
|
||||
@@ -254,7 +286,7 @@ class RiskOfRainWorld(World):
|
||||
event_loc.place_locked_item(RiskOfRainItem("Stage 5", ItemClassification.progression, None, self.player))
|
||||
event_loc.show_in_spoiler = False
|
||||
event_region.locations.append(event_loc)
|
||||
event_loc.access_rule = lambda state: state.has("Sky Meadow", self.player)
|
||||
event_loc.access_rule = lambda state: state.has("Sky Meadow", self.player) or state.has("Helminth Hatchery", self.player)
|
||||
|
||||
victory_region = self.multiworld.get_region("Victory", self.player)
|
||||
victory_event = RiskOfRainLocation(self.player, "Victory", None, victory_region)
|
||||
|
||||
6
worlds/ror2/archipelago.json
Normal file
6
worlds/ror2/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "Risk of Rain 2",
|
||||
"minimum_ap_version": "0.6.4",
|
||||
"world_version": "1.5.0",
|
||||
"authors": ["Kindasneaki"]
|
||||
}
|
||||
@@ -88,12 +88,21 @@ Explore Mode items are:
|
||||
* `Commencement`
|
||||
* `All the Hidden Realms`
|
||||
|
||||
Dlc_Sotv items
|
||||
DLC Survivors of the Void (SOTV) items
|
||||
* `Siphoned Forest`
|
||||
* `Aphelian Sanctuary`
|
||||
* `Sulfur Pools`
|
||||
* `Void Locus`
|
||||
|
||||
DLC Seekers of the Storm (SOTS) items
|
||||
|
||||
* `Shattered Abodes`, `Vicious Falls`, `Disturbed Impact`
|
||||
* `Reformed Altar`
|
||||
* `Treeborn Colony`, `Golden Dieback`
|
||||
* `Prime Meridian`
|
||||
* `Helminth Hatchery`
|
||||
|
||||
|
||||
When an explore item is granted, it will unlock that environment and will now be accessible! The
|
||||
game will still pick randomly which environment is next, but it will first check to see if they are available. If you have
|
||||
multiple of the next environments unlocked, it will weight the game to have a ***higher chance*** to go to one you
|
||||
|
||||
@@ -23,6 +23,13 @@ all necessary dependencies as well.
|
||||
|
||||
Click on the `Start modded` button in the top left in `r2modman` to start the game with the Archipelago mod installed.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
* The mod doesn't show up in game!
|
||||
* `r2modman` looks for the game at its default directory. If you have the game installed somewhere else,
|
||||
you can update `r2modman` by going to `Settings > Change Risk of Rain 2 folder`
|
||||
and selecting the correct directory.
|
||||
|
||||
## Configuring your YAML File
|
||||
### What is a YAML and why do I need one?
|
||||
You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) here on the Archipelago website to learn
|
||||
@@ -59,6 +66,7 @@ also optionally connect to the multiworld using the text client, which can be fo
|
||||
|
||||
### In-Game Commands
|
||||
These commands are to be used in-game by using ``Ctrl + Alt + ` `` and then typing the following:
|
||||
- `archipelago_reconnect` Reconnect to AP.
|
||||
- `archipelago_connect <url> <port> <slot> [password]` example: "archipelago_connect archipelago.gg 38281 SlotName".
|
||||
- `archipelago_deathlink true/false` Toggle deathlink.
|
||||
- `archipelago_disconnect` Disconnect from AP.
|
||||
|
||||
@@ -3,7 +3,8 @@ from BaseClasses import Location
|
||||
from .options import TotalLocations, ChestsPerEnvironment, ShrinesPerEnvironment, ScavengersPerEnvironment, \
|
||||
ScannersPerEnvironment, AltarsPerEnvironment
|
||||
from .ror2environments import compress_dict_list_horizontal, environment_vanilla_orderedstages_table, \
|
||||
environment_sotv_orderedstages_table
|
||||
environment_sotv_orderedstages_table, environment_sost_orderedstages_table, \
|
||||
environment_sots_variants_orderedstages_table, environment_vanilla_variant_orderedstages_table
|
||||
|
||||
|
||||
class RiskOfRainLocation(Location):
|
||||
@@ -57,13 +58,20 @@ def get_environment_locations(chests: int, shrines: int, scavengers: int, scanne
|
||||
return locations
|
||||
|
||||
|
||||
def get_locations(chests: int, shrines: int, scavengers: int, scanners: int, altars: int, dlc_sotv: bool) \
|
||||
def get_locations(chests: int, shrines: int, scavengers: int, scanners: int, altars: int, dlc_sotv: bool,
|
||||
dlc_sots: bool, stage_variants: bool) \
|
||||
-> Dict[str, int]:
|
||||
"""Get a dictionary of locations for the orderedstage environments with the locations from the parameters."""
|
||||
locations = {}
|
||||
orderedstages = compress_dict_list_horizontal(environment_vanilla_orderedstages_table)
|
||||
if stage_variants:
|
||||
orderedstages.update(compress_dict_list_horizontal(environment_vanilla_variant_orderedstages_table))
|
||||
if dlc_sotv:
|
||||
orderedstages.update(compress_dict_list_horizontal(environment_sotv_orderedstages_table))
|
||||
if dlc_sots:
|
||||
orderedstages.update(compress_dict_list_horizontal(environment_sost_orderedstages_table))
|
||||
if dlc_sots and stage_variants:
|
||||
orderedstages.update(compress_dict_list_horizontal(environment_sots_variants_orderedstages_table))
|
||||
# for every environment, generate the respective locations
|
||||
for environment_name, environment_index in orderedstages.items():
|
||||
locations.update(get_environment_locations(
|
||||
@@ -86,4 +94,6 @@ location_table.update(get_locations(
|
||||
scanners=ScannersPerEnvironment.range_end,
|
||||
altars=AltarsPerEnvironment.range_end,
|
||||
dlc_sotv=True,
|
||||
dlc_sots=True,
|
||||
stage_variants=True
|
||||
))
|
||||
|
||||
@@ -22,8 +22,9 @@ class Goal(Choice):
|
||||
class Victory(Choice):
|
||||
"""
|
||||
Mithrix: Defeat Mithrix in Commencement
|
||||
Voidling: Defeat the Voidling in The Planetarium (DLC required! Will select any if not enabled.)
|
||||
Voidling: Defeat the Voidling in The Planetarium (SOTV DLC required! Will select any if not enabled.)
|
||||
Limbo: Defeat the Scavenger in Hidden Realm: A Moment, Whole
|
||||
Falseson: Defeat False son and gift an item to the altar in Prime Meridian (SOTS DLC required! Will select any if not enabled.)
|
||||
Any: Any victory in the game will count. See Final Stage Death for additional ways.
|
||||
"""
|
||||
display_name = "Victory Condition"
|
||||
@@ -31,6 +32,7 @@ class Victory(Choice):
|
||||
option_mithrix = 1
|
||||
option_voidling = 2
|
||||
option_limbo = 3
|
||||
option_falseson = 4
|
||||
default = 0
|
||||
|
||||
|
||||
@@ -138,18 +140,26 @@ class FinalStageDeath(Toggle):
|
||||
If not use the following to tell if final stage death will count:
|
||||
Victory: mithrix - only dying in Commencement will count.
|
||||
Victory: voidling - only dying in The Planetarium will count.
|
||||
Victory: limbo - Obliterating yourself will count."""
|
||||
Victory: limbo - Obliterating yourself will count.
|
||||
Victory: falseson - only dying in Prime Meridian will count."""
|
||||
display_name = "Final Stage Death is Win"
|
||||
|
||||
|
||||
class DLC_SOTV(Toggle):
|
||||
"""
|
||||
Enable if you are using SOTV DLC.
|
||||
Enable if you are using Survivors of the Void DLC.
|
||||
Affects environment availability for Explore Mode.
|
||||
Adds Void Items into the item pool
|
||||
"""
|
||||
display_name = "Enable DLC - SOTV"
|
||||
|
||||
class DLC_SOTS(Toggle):
|
||||
"""
|
||||
Enable if you are using Seekers of the Storm DLC.
|
||||
Affects environment availability for Explore Mode.
|
||||
"""
|
||||
display_name = "Enable DLC - SOTS"
|
||||
|
||||
|
||||
class RequireStages(DefaultOnToggle):
|
||||
"""Add Stage items to the pool to block access to the next set of environments."""
|
||||
@@ -162,6 +172,23 @@ class ProgressiveStages(DefaultOnToggle):
|
||||
display_name = "Progressive Stages"
|
||||
|
||||
|
||||
class StageVariants(Toggle):
|
||||
"""Enable if you want to include stage variants in the environment pool.
|
||||
Stages included are:
|
||||
- Distant Roost (2)
|
||||
- Titanic Plains (2)
|
||||
SOTS DLC Enabled:
|
||||
- Vicious Falls
|
||||
- Shattered Abodes
|
||||
- Golden Dieback"""
|
||||
display_name = "Include Stage Variants"
|
||||
|
||||
|
||||
class ShowSeerPortals(DefaultOnToggle):
|
||||
"""Shows Seer Portals at the teleporter to allow choosing the next environment."""
|
||||
display_name = "Show Seer Portals"
|
||||
|
||||
|
||||
class GreenScrap(Range):
|
||||
"""Weight of Green Scraps in the item pool.
|
||||
|
||||
@@ -384,6 +411,8 @@ ror2_option_groups = [
|
||||
AltarsPerEnvironment,
|
||||
RequireStages,
|
||||
ProgressiveStages,
|
||||
StageVariants,
|
||||
ShowSeerPortals,
|
||||
]),
|
||||
OptionGroup("Classic Mode Options", [
|
||||
TotalLocations,
|
||||
@@ -427,8 +456,11 @@ class ROR2Options(PerGameCommonOptions):
|
||||
start_with_revive: StartWithRevive
|
||||
final_stage_death: FinalStageDeath
|
||||
dlc_sotv: DLC_SOTV
|
||||
dlc_sots: DLC_SOTS
|
||||
require_stages: RequireStages
|
||||
progressive_stages: ProgressiveStages
|
||||
stage_variants: StageVariants
|
||||
show_seer_portals: ShowSeerPortals
|
||||
death_link: DeathLink
|
||||
item_pickup_step: ItemPickupStep
|
||||
shrine_use_step: ShrineUseStep
|
||||
|
||||
@@ -18,13 +18,10 @@ def create_explore_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||
multiworld = ror2_world.multiworld
|
||||
# Default Locations
|
||||
non_dlc_regions: Dict[str, RoRRegionData] = {
|
||||
"Menu": RoRRegionData(None, ["Distant Roost", "Distant Roost (2)",
|
||||
"Titanic Plains", "Titanic Plains (2)",
|
||||
"Menu": RoRRegionData(None, ["Distant Roost", "Titanic Plains",
|
||||
"Verdant Falls"]),
|
||||
"Distant Roost": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Distant Roost (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Titanic Plains": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Titanic Plains (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Verdant Falls": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Abandoned Aqueduct": RoRRegionData([], ["OrderedStage_2"]),
|
||||
"Wetland Aspect": RoRRegionData([], ["OrderedStage_2"]),
|
||||
@@ -35,12 +32,30 @@ def create_explore_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||
"Sundered Grove": RoRRegionData([], ["OrderedStage_4"]),
|
||||
"Sky Meadow": RoRRegionData([], ["Hidden Realm: Bulwark's Ambry", "OrderedStage_5"]),
|
||||
}
|
||||
non_dlc_variant_regions: Dict[str, RoRRegionData] = {
|
||||
"Distant Roost (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Titanic Plains (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
}
|
||||
# SOTV Regions
|
||||
dlc_regions: Dict[str, RoRRegionData] = {
|
||||
dlc_sotv_regions: Dict[str, RoRRegionData] = {
|
||||
"Siphoned Forest": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Aphelian Sanctuary": RoRRegionData([], ["OrderedStage_2"]),
|
||||
"Sulfur Pools": RoRRegionData([], ["OrderedStage_3"])
|
||||
}
|
||||
|
||||
dlc_sost_regions: Dict[str, RoRRegionData] = {
|
||||
"Shattered Abodes": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Reformed Altar": RoRRegionData([], ["OrderedStage_2", "Treeborn Colony"]),
|
||||
"Treeborn Colony": RoRRegionData([], ["OrderedStage_3", "Prime Meridian"]),
|
||||
"Helminth Hatchery": RoRRegionData([], ["Hidden Realm: Bulwark's Ambry", "OrderedStage_5"]),
|
||||
}
|
||||
|
||||
dlc_sots_variant_regions: Dict[str, RoRRegionData] = {
|
||||
"Viscous Falls": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Disturbed Impact": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Golden Dieback": RoRRegionData([], ["OrderedStage_3", "Prime Meridian"]),
|
||||
}
|
||||
|
||||
other_regions: Dict[str, RoRRegionData] = {
|
||||
"Commencement": RoRRegionData(None, ["Victory", "Petrichor V"]),
|
||||
"OrderedStage_5": RoRRegionData(None, ["Hidden Realm: A Moment, Fractured",
|
||||
@@ -61,10 +76,15 @@ def create_explore_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||
"Hidden Realm: Bazaar Between Time": RoRRegionData(None, ["Void Fields"]),
|
||||
"Hidden Realm: Gilded Coast": RoRRegionData(None, None)
|
||||
}
|
||||
dlc_other_regions: Dict[str, RoRRegionData] = {
|
||||
dlc_sotv_other_regions: Dict[str, RoRRegionData] = {
|
||||
"The Planetarium": RoRRegionData(None, ["Victory", "Petrichor V"]),
|
||||
"Void Locus": RoRRegionData(None, ["The Planetarium"])
|
||||
}
|
||||
|
||||
dlc_sost_other_regions: Dict[str, RoRRegionData] = {
|
||||
"Prime Meridian": RoRRegionData(None, ["Victory", "Petrichor V"]),
|
||||
}
|
||||
|
||||
# Totals of each item
|
||||
chests = int(ror2_options.chests_per_stage)
|
||||
shrines = int(ror2_options.shrines_per_stage)
|
||||
@@ -72,8 +92,14 @@ def create_explore_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||
scanners = int(ror2_options.scanner_per_stage)
|
||||
newt = int(ror2_options.altars_per_stage)
|
||||
all_location_regions = {**non_dlc_regions}
|
||||
if ror2_options.stage_variants:
|
||||
all_location_regions.update(non_dlc_variant_regions)
|
||||
if ror2_options.dlc_sotv:
|
||||
all_location_regions = {**non_dlc_regions, **dlc_regions}
|
||||
all_location_regions.update(dlc_sotv_regions)
|
||||
if ror2_options.dlc_sots:
|
||||
all_location_regions.update(dlc_sost_regions)
|
||||
if ror2_options.dlc_sots and ror2_options.stage_variants:
|
||||
all_location_regions.update(dlc_sots_variant_regions)
|
||||
|
||||
# Locations
|
||||
for key in all_location_regions:
|
||||
@@ -99,25 +125,52 @@ def create_explore_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||
all_location_regions[key].locations.append(f"{key}: Newt Altar {i + 1}")
|
||||
regions_pool: Dict = {**all_location_regions, **other_regions}
|
||||
|
||||
# DLC Locations
|
||||
# Non DLC Variant Locations
|
||||
if ror2_options.stage_variants:
|
||||
non_dlc_regions["Menu"].region_exits.append("Distant Roost (2)")
|
||||
non_dlc_regions["Menu"].region_exits.append("Titanic Plains (2)")
|
||||
# SOTV DLC Locations
|
||||
if ror2_options.dlc_sotv:
|
||||
non_dlc_regions["Menu"].region_exits.append("Siphoned Forest")
|
||||
other_regions["OrderedStage_1"].region_exits.append("Aphelian Sanctuary")
|
||||
other_regions["OrderedStage_2"].region_exits.append("Sulfur Pools")
|
||||
other_regions["Void Fields"].region_exits.append("Void Locus")
|
||||
other_regions["Commencement"].region_exits.append("The Planetarium")
|
||||
regions_pool: Dict = {**all_location_regions, **other_regions, **dlc_other_regions}
|
||||
|
||||
# SOTS DLC Locations
|
||||
if ror2_options.dlc_sots:
|
||||
non_dlc_regions["Menu"].region_exits.append("Shattered Abodes")
|
||||
other_regions["OrderedStage_1"].region_exits.append("Reformed Altar")
|
||||
other_regions["OrderedStage_4"].region_exits.append("Helminth Hatchery")
|
||||
|
||||
# SOTS Variant Locations
|
||||
if ror2_options.dlc_sots and ror2_options.stage_variants:
|
||||
non_dlc_regions["Menu"].region_exits.append("Viscous Falls")
|
||||
non_dlc_regions["Menu"].region_exits.append("Disturbed Impact")
|
||||
dlc_sost_regions["Reformed Altar"].region_exits.append("Golden Dieback")
|
||||
|
||||
if ror2_options.dlc_sotv:
|
||||
regions_pool.update(dlc_sotv_other_regions)
|
||||
if ror2_options.dlc_sots:
|
||||
regions_pool.update(dlc_sost_other_regions)
|
||||
|
||||
# Check to see if Victory needs to be removed from regions
|
||||
if ror2_options.victory == "mithrix":
|
||||
other_regions["Hidden Realm: A Moment, Whole"].region_exits.pop(0)
|
||||
dlc_other_regions["The Planetarium"].region_exits.pop(0)
|
||||
dlc_sotv_other_regions["The Planetarium"].region_exits.pop(0)
|
||||
dlc_sost_other_regions["Prime Meridian"].region_exits.pop(0)
|
||||
elif ror2_options.victory == "voidling":
|
||||
other_regions["Commencement"].region_exits.pop(0)
|
||||
other_regions["Hidden Realm: A Moment, Whole"].region_exits.pop(0)
|
||||
dlc_sost_other_regions["Prime Meridian"].region_exits.pop(0)
|
||||
elif ror2_options.victory == "limbo":
|
||||
other_regions["Commencement"].region_exits.pop(0)
|
||||
dlc_other_regions["The Planetarium"].region_exits.pop(0)
|
||||
dlc_sotv_other_regions["The Planetarium"].region_exits.pop(0)
|
||||
dlc_sost_other_regions["Prime Meridian"].region_exits.pop(0)
|
||||
elif ror2_options.victory == "falseson":
|
||||
other_regions["Commencement"].region_exits.pop(0)
|
||||
other_regions["Hidden Realm: A Moment, Whole"].region_exits.pop(0)
|
||||
dlc_sotv_other_regions["The Planetarium"].region_exits.pop(0)
|
||||
|
||||
# Create all the regions
|
||||
for name, data in regions_pool.items():
|
||||
|
||||
@@ -4,11 +4,14 @@ from typing import Dict, List, TypeVar
|
||||
|
||||
environment_vanilla_orderedstage_1_table: Dict[str, int] = {
|
||||
"Distant Roost": 7, # blackbeach
|
||||
"Distant Roost (2)": 8, # blackbeach2
|
||||
"Titanic Plains": 15, # golemplains
|
||||
"Titanic Plains (2)": 16, # golemplains2
|
||||
"Verdant Falls": 28, # lakes
|
||||
}
|
||||
environment_vanilla_variant_orderedstage_1_table: Dict[str, int] = {
|
||||
"Distant Roost (2)": 8, # blackbeach2
|
||||
"Titanic Plains (2)": 16, # golemplains2
|
||||
}
|
||||
|
||||
environment_vanilla_orderedstage_2_table: Dict[str, int] = {
|
||||
"Abandoned Aqueduct": 17, # goolake
|
||||
"Wetland Aspect": 12, # foggyswamp
|
||||
@@ -54,6 +57,34 @@ environment_sotv_special_table: Dict[str, int] = {
|
||||
"The Planetarium": 45, # voidraid
|
||||
}
|
||||
|
||||
environment_sost_orderstage_1_table: Dict[str, int] = {
|
||||
"Shattered Abodes": 54, # village
|
||||
|
||||
}
|
||||
environment_sost_variant_orderstage_1_table: Dict[str, int] = {
|
||||
"Viscous Falls": 34, # lakesnight
|
||||
"Disturbed Impact": 55, # villagenight
|
||||
}
|
||||
|
||||
environment_sost_orderstage_2_table: Dict[str, int] = {
|
||||
"Reformed Altar": 36, # lemuriantemple
|
||||
}
|
||||
|
||||
environment_sost_orderstage_3_table: Dict[str, int] = {
|
||||
"Treeborn Colony": 21, # habitat
|
||||
}
|
||||
environment_sost_variant_orderstage_3_table: Dict[str, int] = {
|
||||
"Golden Dieback": 22, # habitatfall
|
||||
}
|
||||
|
||||
environment_sost_orderstage_5_table: Dict[str, int] = {
|
||||
"Helminth Hatchery": 23, # helminthroost
|
||||
}
|
||||
|
||||
environment_sost_special_table: Dict[str, int] = {
|
||||
"Prime Meridian": 40, # meridian
|
||||
}
|
||||
|
||||
X = TypeVar("X")
|
||||
Y = TypeVar("Y")
|
||||
|
||||
@@ -100,18 +131,32 @@ environment_vanilla_orderedstages_table = \
|
||||
environment_vanilla_table = \
|
||||
{**compress_dict_list_horizontal(environment_vanilla_orderedstages_table),
|
||||
**environment_vanilla_hidden_realm_table, **environment_vanilla_special_table}
|
||||
# Vanilla Variants
|
||||
environment_vanilla_variant_orderedstages_table = \
|
||||
[environment_vanilla_variant_orderedstage_1_table]
|
||||
environment_vanilla_variants_table = \
|
||||
{**compress_dict_list_horizontal(environment_vanilla_variant_orderedstages_table)}
|
||||
|
||||
# SoTV
|
||||
environment_sotv_orderedstages_table = \
|
||||
[environment_sotv_orderedstage_1_table, environment_sotv_orderedstage_2_table,
|
||||
environment_sotv_orderedstage_3_table]
|
||||
environment_sotv_table = \
|
||||
{**compress_dict_list_horizontal(environment_sotv_orderedstages_table), **environment_sotv_special_table}
|
||||
# SoST
|
||||
environment_sost_orderedstages_table = \
|
||||
[environment_sost_orderstage_1_table, environment_sost_orderstage_2_table,
|
||||
environment_sost_orderstage_3_table, {}, environment_sost_orderstage_5_table] # There is no new stage 4 in SoST
|
||||
environment_sost_table = \
|
||||
{**compress_dict_list_horizontal(environment_sost_orderedstages_table), **environment_sost_special_table}
|
||||
# SOTS Variants
|
||||
environment_sots_variants_orderedstages_table = \
|
||||
[environment_sost_variant_orderstage_1_table, {}, environment_sost_variant_orderstage_3_table]
|
||||
environment_sots_variants_table = \
|
||||
{**compress_dict_list_horizontal(environment_sots_variants_orderedstages_table)}
|
||||
|
||||
environment_non_orderedstages_table = \
|
||||
{**environment_vanilla_hidden_realm_table, **environment_vanilla_special_table, **environment_sotv_special_table}
|
||||
environment_orderedstages_table = \
|
||||
collapse_dict_list_vertical(environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table)
|
||||
environment_all_table = {**environment_vanilla_table, **environment_sotv_table}
|
||||
environment_all_table = {**environment_vanilla_table, **environment_sotv_table, **environment_sost_table,
|
||||
**environment_vanilla_variants_table, **environment_sots_variants_table}
|
||||
|
||||
|
||||
def shift_by_offset(dictionary: Dict[str, int], offset: int) -> Dict[str, int]:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from worlds.generic.Rules import set_rule, add_rule
|
||||
from BaseClasses import MultiWorld
|
||||
from .locations import get_locations
|
||||
from .ror2environments import environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table
|
||||
from .ror2environments import environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table, \
|
||||
environment_sost_orderedstages_table, environment_vanilla_variant_orderedstages_table, \
|
||||
environment_sots_variants_orderedstages_table
|
||||
from typing import Set, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -43,6 +45,24 @@ def has_location_access_rule(multiworld: MultiWorld, environment: str, player: i
|
||||
multiworld.get_location(location_name, player).access_rule = \
|
||||
lambda state: state.has(environment, player)
|
||||
|
||||
def explore_environment_location_rules(table, multiworld, player, chests, shrines, newts, scavengers, scanners):
|
||||
for i in range(len(table)):
|
||||
for environment_name, _ in table[i].items():
|
||||
# Make sure to go through each location
|
||||
if scavengers == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger")
|
||||
if scanners == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner")
|
||||
for chest in range(1, chests + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, chest, "Chest")
|
||||
for shrine in range(1, shrines + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine")
|
||||
if newts > 0:
|
||||
for newt in range(1, newts + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar")
|
||||
if i > 0:
|
||||
has_stage_access_rule(multiworld, f"Stage {i}", i, environment_name, player)
|
||||
|
||||
|
||||
def set_rules(ror2_world: "RiskOfRainWorld") -> None:
|
||||
player = ror2_world.player
|
||||
@@ -60,7 +80,9 @@ def set_rules(ror2_world: "RiskOfRainWorld") -> None:
|
||||
scavengers=ror2_options.scavengers_per_stage.value,
|
||||
scanners=ror2_options.scanner_per_stage.value,
|
||||
altars=ror2_options.altars_per_stage.value,
|
||||
dlc_sotv=bool(ror2_options.dlc_sotv.value)
|
||||
dlc_sotv=bool(ror2_options.dlc_sotv.value),
|
||||
dlc_sots=bool(ror2_options.dlc_sots.value),
|
||||
stage_variants=bool(ror2_options.stage_variants)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -101,40 +123,25 @@ def set_rules(ror2_world: "RiskOfRainWorld") -> None:
|
||||
newts = ror2_options.altars_per_stage.value
|
||||
scavengers = ror2_options.scavengers_per_stage.value
|
||||
scanners = ror2_options.scanner_per_stage.value
|
||||
for i in range(len(environment_vanilla_orderedstages_table)):
|
||||
for environment_name, _ in environment_vanilla_orderedstages_table[i].items():
|
||||
# Make sure to go through each location
|
||||
if scavengers == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger")
|
||||
if scanners == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner")
|
||||
for chest in range(1, chests + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, chest, "Chest")
|
||||
for shrine in range(1, shrines + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine")
|
||||
if newts > 0:
|
||||
for newt in range(1, newts + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar")
|
||||
if i > 0:
|
||||
has_stage_access_rule(multiworld, f"Stage {i}", i, environment_name, player)
|
||||
|
||||
# Vanilla stages
|
||||
explore_environment_location_rules(environment_vanilla_orderedstages_table, multiworld, player, chests, shrines, newts,
|
||||
scavengers, scanners)
|
||||
# Vanilla Variant stages
|
||||
if ror2_options.stage_variants:
|
||||
explore_environment_location_rules(environment_vanilla_variant_orderedstages_table, multiworld, player, chests, shrines, newts,
|
||||
scavengers, scanners)
|
||||
# SoTv stages
|
||||
if ror2_options.dlc_sotv:
|
||||
for i in range(len(environment_sotv_orderedstages_table)):
|
||||
for environment_name, _ in environment_sotv_orderedstages_table[i].items():
|
||||
# Make sure to go through each location
|
||||
if scavengers == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger")
|
||||
if scanners == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner")
|
||||
for chest in range(1, chests + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, chest, "Chest")
|
||||
for shrine in range(1, shrines + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine")
|
||||
if newts > 0:
|
||||
for newt in range(1, newts + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar")
|
||||
if i > 0:
|
||||
has_stage_access_rule(multiworld, f"Stage {i}", i, environment_name, player)
|
||||
explore_environment_location_rules(environment_sotv_orderedstages_table, multiworld, player, chests, shrines,
|
||||
newts, scavengers, scanners)
|
||||
# SoTS stages
|
||||
if ror2_options.dlc_sots:
|
||||
explore_environment_location_rules(environment_sost_orderedstages_table, multiworld, player, chests, shrines,
|
||||
newts, scavengers, scanners)
|
||||
if ror2_options.dlc_sots and ror2_options.stage_variants:
|
||||
explore_environment_location_rules(environment_sots_variants_orderedstages_table, multiworld, player, chests, shrines,
|
||||
newts, scavengers, scanners)
|
||||
|
||||
has_entrance_access_rule(multiworld, "Hidden Realm: A Moment, Fractured", "Hidden Realm: A Moment, Whole",
|
||||
player)
|
||||
has_stage_access_rule(multiworld, "Stage 1", 1, "Hidden Realm: Bazaar Between Time", player)
|
||||
@@ -147,6 +154,8 @@ def set_rules(ror2_world: "RiskOfRainWorld") -> None:
|
||||
has_entrance_access_rule(multiworld, "Stage 5", "Void Locus", player)
|
||||
if ror2_options.victory == "voidling":
|
||||
has_all_items(multiworld, {"Stage 5", "The Planetarium"}, "Commencement", player)
|
||||
if ror2_options.dlc_sots:
|
||||
has_entrance_access_rule(multiworld, "Stage 5", "Prime Meridian", player)
|
||||
|
||||
# Win Condition
|
||||
multiworld.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
|
||||
@@ -4,23 +4,33 @@ from . import RoR2TestBase
|
||||
class DLCTest(RoR2TestBase):
|
||||
options = {
|
||||
"dlc_sotv": "true",
|
||||
"victory": "any"
|
||||
"victory": "any",
|
||||
"dlc_sots": "true",
|
||||
}
|
||||
|
||||
def test_commencement_victory(self) -> None:
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Victory"])
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Prime Meridian",
|
||||
"Victory"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name("Commencement")
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_planetarium_victory(self) -> None:
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Victory"])
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Prime Meridian",
|
||||
"Victory"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name("The Planetarium")
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_moment_whole_victory(self) -> None:
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Victory"])
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Prime Meridian",
|
||||
"Victory"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name("Hidden Realm: A Moment, Whole")
|
||||
self.assertBeatable(True)
|
||||
def test_false_son_victory(self) -> None:
|
||||
self.collect_all_but(["Commencement", "The Planetarium", "Hidden Realm: A Moment, Whole", "Prime Meridian",
|
||||
"Victory"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name("Prime Meridian")
|
||||
self.assertBeatable(True)
|
||||
|
||||
17
worlds/ror2/test/test_falseson_goal.py
Normal file
17
worlds/ror2/test/test_falseson_goal.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from . import RoR2TestBase
|
||||
|
||||
|
||||
class FalseSonGoalTest(RoR2TestBase):
|
||||
options = {
|
||||
"dlc_sots": "true",
|
||||
"victory": "falseson",
|
||||
"stage_variants": "true"
|
||||
}
|
||||
|
||||
def test_false_son(self) -> None:
|
||||
self.collect_all_but(["Prime Meridian", "Victory"])
|
||||
self.assertFalse(self.can_reach_region("Prime Meridian"))
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name("Prime Meridian")
|
||||
self.assertTrue(self.can_reach_region("Prime Meridian"))
|
||||
self.assertBeatable(True)
|
||||
Reference in New Issue
Block a user