Core: don't allow region, location, or entrance with duplicate names (#2453)
This commit is contained in:
parent
86a7ac466e
commit
8f7b0ee489
|
@ -110,10 +110,14 @@ class MultiWorld():
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def append(self, region: Region):
|
def append(self, region: Region):
|
||||||
|
assert region.name not in self.region_cache[region.player], \
|
||||||
|
f"{region.name} already exists in region cache."
|
||||||
self.region_cache[region.player][region.name] = region
|
self.region_cache[region.player][region.name] = region
|
||||||
|
|
||||||
def extend(self, regions: Iterable[Region]):
|
def extend(self, regions: Iterable[Region]):
|
||||||
for region in regions:
|
for region in regions:
|
||||||
|
assert region.name not in self.region_cache[region.player], \
|
||||||
|
f"{region.name} already exists in region cache."
|
||||||
self.region_cache[region.player][region.name] = region
|
self.region_cache[region.player][region.name] = region
|
||||||
|
|
||||||
def add_group(self, new_id: int):
|
def add_group(self, new_id: int):
|
||||||
|
@ -877,6 +881,8 @@ class Region:
|
||||||
del(self.region_manager.location_cache[location.player][location.name])
|
del(self.region_manager.location_cache[location.player][location.name])
|
||||||
|
|
||||||
def insert(self, index: int, value: Location) -> None:
|
def insert(self, index: int, value: Location) -> None:
|
||||||
|
assert value.name not in self.region_manager.location_cache[value.player], \
|
||||||
|
f"{value.name} already exists in the location cache."
|
||||||
self._list.insert(index, value)
|
self._list.insert(index, value)
|
||||||
self.region_manager.location_cache[value.player][value.name] = value
|
self.region_manager.location_cache[value.player][value.name] = value
|
||||||
|
|
||||||
|
@ -887,6 +893,8 @@ class Region:
|
||||||
del(self.region_manager.entrance_cache[entrance.player][entrance.name])
|
del(self.region_manager.entrance_cache[entrance.player][entrance.name])
|
||||||
|
|
||||||
def insert(self, index: int, value: Entrance) -> None:
|
def insert(self, index: int, value: Entrance) -> None:
|
||||||
|
assert value.name not in self.region_manager.entrance_cache[value.player], \
|
||||||
|
f"{value.name} already exists in the entrance cache."
|
||||||
self._list.insert(index, value)
|
self._list.insert(index, value)
|
||||||
self.region_manager.entrance_cache[value.player][value.name] = value
|
self.region_manager.entrance_cache[value.player][value.name] = value
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ class TestHelpers(unittest.TestCase):
|
||||||
"event_loc": None,
|
"event_loc": None,
|
||||||
},
|
},
|
||||||
"TestRegion2": {
|
"TestRegion2": {
|
||||||
"loc_1": 321,
|
"loc_3": 321,
|
||||||
"loc_2": 654,
|
"loc_4": 654,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,9 +100,7 @@ class MMBN3World(World):
|
||||||
for region_info in regions:
|
for region_info in regions:
|
||||||
region = name_to_region[region_info.name]
|
region = name_to_region[region_info.name]
|
||||||
for connection in region_info.connections:
|
for connection in region_info.connections:
|
||||||
connection_region = name_to_region[connection]
|
entrance = region.connect(name_to_region[connection])
|
||||||
entrance = Entrance(self.player, connection, region)
|
|
||||||
entrance.connect(connection_region)
|
|
||||||
|
|
||||||
# ACDC Pending with Start Randomizer
|
# ACDC Pending with Start Randomizer
|
||||||
# if connection == RegionName.ACDC_Overworld:
|
# if connection == RegionName.ACDC_Overworld:
|
||||||
|
@ -141,7 +139,6 @@ class MMBN3World(World):
|
||||||
if connection == RegionName.WWW_Island:
|
if connection == RegionName.WWW_Island:
|
||||||
entrance.access_rule = lambda state:\
|
entrance.access_rule = lambda state:\
|
||||||
state.has(ItemName.Progressive_Undernet_Rank, self.player, 8)
|
state.has(ItemName.Progressive_Undernet_Rank, self.player, 8)
|
||||||
region.exits.append(entrance)
|
|
||||||
|
|
||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
# First add in all progression and useful items
|
# First add in all progression and useful items
|
||||||
|
|
|
@ -90,13 +90,7 @@ class Overcooked2World(World):
|
||||||
def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None):
|
def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None):
|
||||||
sourceRegion = self.multiworld.get_region(source, self.player)
|
sourceRegion = self.multiworld.get_region(source, self.player)
|
||||||
targetRegion = self.multiworld.get_region(target, self.player)
|
targetRegion = self.multiworld.get_region(target, self.player)
|
||||||
|
sourceRegion.connect(targetRegion, rule=rule)
|
||||||
connection = Entrance(self.player, '', sourceRegion)
|
|
||||||
if rule:
|
|
||||||
connection.access_rule = rule
|
|
||||||
|
|
||||||
sourceRegion.exits.append(connection)
|
|
||||||
connection.connect(targetRegion)
|
|
||||||
|
|
||||||
def add_level_location(
|
def add_level_location(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -140,11 +140,7 @@ def create_explore_region(multiworld: MultiWorld, player: int, name: str, data:
|
||||||
def create_connections_in_regions(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData) -> None:
|
def create_connections_in_regions(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData) -> None:
|
||||||
region = multiworld.get_region(name, player)
|
region = multiworld.get_region(name, player)
|
||||||
if data.region_exits:
|
if data.region_exits:
|
||||||
for region_exit in data.region_exits:
|
region.add_exits(data.region_exits)
|
||||||
r_exit_stage = Entrance(player, region_exit, region)
|
|
||||||
exit_region = multiworld.get_region(region_exit, player)
|
|
||||||
r_exit_stage.connect(exit_region)
|
|
||||||
region.exits.append(r_exit_stage)
|
|
||||||
|
|
||||||
|
|
||||||
def create_classic_regions(ror2_world: "RiskOfRainWorld") -> None:
|
def create_classic_regions(ror2_world: "RiskOfRainWorld") -> None:
|
||||||
|
|
|
@ -9,14 +9,16 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
# Rule to see if it has access to the previous stage
|
# Rule to see if it has access to the previous stage
|
||||||
def has_entrance_access_rule(multiworld: MultiWorld, stage: str, entrance: str, player: int) -> None:
|
def has_entrance_access_rule(multiworld: MultiWorld, stage: str, region: str, player: int) -> None:
|
||||||
multiworld.get_entrance(entrance, player).access_rule = \
|
rule = lambda state: state.has(region, player) and state.has(stage, player)
|
||||||
lambda state: state.has(entrance, player) and state.has(stage, player)
|
for entrance in multiworld.get_region(region, player).entrances:
|
||||||
|
entrance.access_rule = rule
|
||||||
|
|
||||||
|
|
||||||
def has_all_items(multiworld: MultiWorld, items: Set[str], entrance: str, player: int) -> None:
|
def has_all_items(multiworld: MultiWorld, items: Set[str], region: str, player: int) -> None:
|
||||||
multiworld.get_entrance(entrance, player).access_rule = \
|
rule = lambda state: state.has_all(items, player) and state.has(region, player)
|
||||||
lambda state: state.has_all(items, player) and state.has(entrance, player)
|
for entrance in multiworld.get_region(region, player).entrances:
|
||||||
|
entrance.access_rule = rule
|
||||||
|
|
||||||
|
|
||||||
# Checks to see if chest/shrine are accessible
|
# Checks to see if chest/shrine are accessible
|
||||||
|
@ -45,8 +47,9 @@ def check_location(state, environment: str, player: int, item_number: int, item_
|
||||||
def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int) -> None:
|
def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int) -> None:
|
||||||
if stage_number == 4:
|
if stage_number == 4:
|
||||||
return
|
return
|
||||||
multiworld.get_entrance(f"OrderedStage_{stage_number + 1}", player).access_rule = \
|
rule = lambda state: state.has(f"Stage {stage_number + 1}", player)
|
||||||
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:
|
def set_rules(ror2_world: "RiskOfRainWorld") -> None:
|
||||||
|
|
|
@ -8,8 +8,8 @@ class LimboGoalTest(RoR2TestBase):
|
||||||
|
|
||||||
def test_limbo(self) -> None:
|
def test_limbo(self) -> None:
|
||||||
self.collect_all_but(["Hidden Realm: A Moment, Whole", "Victory"])
|
self.collect_all_but(["Hidden Realm: A Moment, Whole", "Victory"])
|
||||||
self.assertFalse(self.can_reach_entrance("Hidden Realm: A Moment, Whole"))
|
self.assertFalse(self.can_reach_region("Hidden Realm: A Moment, Whole"))
|
||||||
self.assertBeatable(False)
|
self.assertBeatable(False)
|
||||||
self.collect_by_name("Hidden Realm: A Moment, Whole")
|
self.collect_by_name("Hidden Realm: A Moment, Whole")
|
||||||
self.assertTrue(self.can_reach_entrance("Hidden Realm: A Moment, Whole"))
|
self.assertTrue(self.can_reach_region("Hidden Realm: A Moment, Whole"))
|
||||||
self.assertBeatable(True)
|
self.assertBeatable(True)
|
||||||
|
|
|
@ -8,18 +8,18 @@ class MithrixGoalTest(RoR2TestBase):
|
||||||
|
|
||||||
def test_mithrix(self) -> None:
|
def test_mithrix(self) -> None:
|
||||||
self.collect_all_but(["Commencement", "Victory"])
|
self.collect_all_but(["Commencement", "Victory"])
|
||||||
self.assertFalse(self.can_reach_entrance("Commencement"))
|
self.assertFalse(self.can_reach_region("Commencement"))
|
||||||
self.assertBeatable(False)
|
self.assertBeatable(False)
|
||||||
self.collect_by_name("Commencement")
|
self.collect_by_name("Commencement")
|
||||||
self.assertTrue(self.can_reach_entrance("Commencement"))
|
self.assertTrue(self.can_reach_region("Commencement"))
|
||||||
self.assertBeatable(True)
|
self.assertBeatable(True)
|
||||||
|
|
||||||
def test_stage5(self) -> None:
|
def test_stage5(self) -> None:
|
||||||
self.collect_all_but(["Stage 4", "Sky Meadow", "Victory"])
|
self.collect_all_but(["Stage 4", "Sky Meadow", "Victory"])
|
||||||
self.assertFalse(self.can_reach_entrance("Sky Meadow"))
|
self.assertFalse(self.can_reach_region("Sky Meadow"))
|
||||||
self.assertBeatable(False)
|
self.assertBeatable(False)
|
||||||
self.collect_by_name("Sky Meadow")
|
self.collect_by_name("Sky Meadow")
|
||||||
self.assertFalse(self.can_reach_entrance("Sky Meadow"))
|
self.assertFalse(self.can_reach_region("Sky Meadow"))
|
||||||
self.collect_by_name("Stage 4")
|
self.collect_by_name("Stage 4")
|
||||||
self.assertTrue(self.can_reach_entrance("Sky Meadow"))
|
self.assertTrue(self.can_reach_region("Sky Meadow"))
|
||||||
self.assertBeatable(True)
|
self.assertBeatable(True)
|
||||||
|
|
|
@ -9,17 +9,17 @@ class VoidlingGoalTest(RoR2TestBase):
|
||||||
|
|
||||||
def test_planetarium(self) -> None:
|
def test_planetarium(self) -> None:
|
||||||
self.collect_all_but(["The Planetarium", "Victory"])
|
self.collect_all_but(["The Planetarium", "Victory"])
|
||||||
self.assertFalse(self.can_reach_entrance("The Planetarium"))
|
self.assertFalse(self.can_reach_region("The Planetarium"))
|
||||||
self.assertBeatable(False)
|
self.assertBeatable(False)
|
||||||
self.collect_by_name("The Planetarium")
|
self.collect_by_name("The Planetarium")
|
||||||
self.assertTrue(self.can_reach_entrance("The Planetarium"))
|
self.assertTrue(self.can_reach_region("The Planetarium"))
|
||||||
self.assertBeatable(True)
|
self.assertBeatable(True)
|
||||||
|
|
||||||
def test_void_locus_to_victory(self) -> None:
|
def test_void_locus_to_victory(self) -> None:
|
||||||
self.collect_all_but(["Void Locus", "Commencement"])
|
self.collect_all_but(["Void Locus", "Commencement"])
|
||||||
self.assertFalse(self.can_reach_location("Victory"))
|
self.assertFalse(self.can_reach_location("Victory"))
|
||||||
self.collect_by_name("Void Locus")
|
self.collect_by_name("Void Locus")
|
||||||
self.assertTrue(self.can_reach_entrance("Victory"))
|
self.assertTrue(self.can_reach_location("Victory"))
|
||||||
|
|
||||||
def test_commencement_to_victory(self) -> None:
|
def test_commencement_to_victory(self) -> None:
|
||||||
self.collect_all_but(["Void Locus", "Commencement"])
|
self.collect_all_but(["Void Locus", "Commencement"])
|
||||||
|
|
|
@ -200,7 +200,6 @@ def create_regions(world: MultiWorld, player: int):
|
||||||
create_locs(thi_large_top, "THI: 100 Coins")
|
create_locs(thi_large_top, "THI: 100 Coins")
|
||||||
|
|
||||||
regFloor3 = create_region("Third Floor", player, world)
|
regFloor3 = create_region("Third Floor", player, world)
|
||||||
world.regions.append(regFloor3)
|
|
||||||
|
|
||||||
regTTC = create_region("Tick Tock Clock", player, world)
|
regTTC = create_region("Tick Tock Clock", player, world)
|
||||||
create_locs(regTTC, "TTC: Stop Time for Red Coins")
|
create_locs(regTTC, "TTC: Stop Time for Red Coins")
|
||||||
|
@ -230,13 +229,7 @@ def create_regions(world: MultiWorld, player: int):
|
||||||
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None):
|
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None):
|
||||||
sourceRegion = world.get_region(source, player)
|
sourceRegion = world.get_region(source, player)
|
||||||
targetRegion = world.get_region(target, player)
|
targetRegion = world.get_region(target, player)
|
||||||
|
sourceRegion.connect(targetRegion, rule=rule)
|
||||||
connection = Entrance(player, '', sourceRegion)
|
|
||||||
if rule:
|
|
||||||
connection.access_rule = rule
|
|
||||||
|
|
||||||
sourceRegion.exits.append(connection)
|
|
||||||
connection.connect(targetRegion)
|
|
||||||
|
|
||||||
|
|
||||||
def create_region(name: str, player: int, world: MultiWorld) -> Region:
|
def create_region(name: str, player: int, world: MultiWorld) -> Region:
|
||||||
|
|
|
@ -247,13 +247,7 @@ def connect(world: MultiWorld, player: int, source: str, target: str,
|
||||||
|
|
||||||
sourceRegion = world.get_region(source, player)
|
sourceRegion = world.get_region(source, player)
|
||||||
targetRegion = world.get_region(target, player)
|
targetRegion = world.get_region(target, player)
|
||||||
|
sourceRegion.connect(targetRegion, rule=rule)
|
||||||
connection = Entrance(player, "", sourceRegion)
|
|
||||||
|
|
||||||
if rule:
|
|
||||||
connection.access_rule = rule
|
|
||||||
sourceRegion.exits.append(connection)
|
|
||||||
connection.connect(targetRegion)
|
|
||||||
|
|
||||||
|
|
||||||
def split_location_datas_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
|
def split_location_datas_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
|
||||||
|
|
Loading…
Reference in New Issue