From f5d8cfb71007e667838b0cf5bd78cbb0bca67b5b Mon Sep 17 00:00:00 2001 From: Scipio Wright Date: Sat, 15 Jun 2024 15:26:47 -0400 Subject: [PATCH] Shops my beloved --- worlds/tunic/__init__.py | 8 +++++++ worlds/tunic/er_data.py | 2 ++ worlds/tunic/er_scripts.py | 45 +++++++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/worlds/tunic/__init__.py b/worlds/tunic/__init__.py index aa993cd5b6..175712d4d8 100644 --- a/worlds/tunic/__init__.py +++ b/worlds/tunic/__init__.py @@ -71,6 +71,7 @@ class TunicWorld(World): tunic_portal_pairs: Dict[str, str] er_portal_hints: Dict[int, str] seed_groups: Dict[str, SeedGroup] = {} + shop_num: int = 1 # need to make it so that you can walk out of shops, but also that they aren't all connected def generate_early(self) -> None: if self.options.plando_connections: @@ -362,6 +363,13 @@ class TunicWorld(World): hint_data[self.player][location.address] = hint_text def fill_slot_data(self) -> Dict[str, Any]: + # todo: remove this + # if self.player_name == "Player22": + # import Utils + # state = self.multiworld.get_all_state(False) + # state.update_reachable_regions(self.player) + # Utils.visualize_regions(self.multiworld.get_region("Menu", self.player), "tunctest.puml", + # show_entrance_names=True, highlight_regions=state.reachable_regions[self.player]) slot_data: Dict[str, Any] = { "seed": self.random.randint(0, 2147483647), "start_with_sword": self.options.start_with_sword.value, diff --git a/worlds/tunic/er_data.py b/worlds/tunic/er_data.py index 3c128ef8c5..b3e2877429 100644 --- a/worlds/tunic/er_data.py +++ b/worlds/tunic/er_data.py @@ -10,6 +10,8 @@ class Portal(NamedTuple): elevation: Optional[int] = None # elevation of the entrance, for ladder storage rule generation def scene(self) -> str: # the actual scene name in Tunic + if self.region.startswith("Shop"): + return tunic_er_regions["Shop"].game_scene return tunic_er_regions[self.region].game_scene def scene_destination(self) -> str: # full, nonchanging name to interpret by the mod diff --git a/worlds/tunic/er_scripts.py b/worlds/tunic/er_scripts.py index 85e876fe94..f6326a8ec0 100644 --- a/worlds/tunic/er_scripts.py +++ b/worlds/tunic/er_scripts.py @@ -22,17 +22,17 @@ class TunicERLocation(Location): def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]: regions: Dict[str, Region] = {} + for region_name, region_data in tunic_er_regions.items(): + regions[region_name] = Region(region_name, world.player, world.multiworld) + if world.options.entrance_rando: - portal_pairs = pair_portals(world) + portal_pairs = pair_portals(world, regions) # output the entrances to the spoiler log here for convenience for portal1, portal2 in portal_pairs.items(): world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player) else: - portal_pairs = vanilla_portals() - - for region_name, region_data in tunic_er_regions.items(): - regions[region_name] = Region(region_name, world.player, world.multiworld) + portal_pairs = vanilla_portals(world, regions) set_er_region_rules(world, regions, portal_pairs) @@ -51,6 +51,7 @@ def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]: victory_region = regions["Spirit Arena Victory"] victory_location = TunicERLocation(world.player, "The Heir", None, victory_region) victory_location.place_locked_item(TunicERItem("Victory", ItemClassification.progression, None, world.player)) + # todo: set the victory condition back world.multiworld.completion_condition[world.player] = lambda state: True # state.has("Victory", world.player) victory_region.locations.append(victory_location) @@ -93,7 +94,17 @@ def place_event_items(world: "TunicWorld", regions: Dict[str, Region]) -> None: region.locations.append(location) -def vanilla_portals() -> Dict[Portal, Portal]: +# all shops are the same shop. however, you cannot get to all shops from the same shop entrance. +# so, we need a bunch of shop regions that connect to the actual shop, but the actual shop cannot connect back +def create_shop_region(world: "TunicWorld", regions: Dict[str, Region]) -> None: + new_shop_name = f"Shop {world.shop_num}" + new_shop_region = Region(new_shop_name, world.player, world.multiworld) + new_shop_region.connect(regions["Shop"]) + regions[new_shop_name] = new_shop_region + world.shop_num += 1 + + +def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]: portal_pairs: Dict[Portal, Portal] = {} # we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here portal_map = [portal for portal in portal_mapping if portal.name != "Ziggurat Lower Falling Entrance"] @@ -105,8 +116,9 @@ def vanilla_portals() -> Dict[Portal, Portal]: portal2_sdt = portal1.destination_scene() if portal2_sdt.startswith("Shop,"): - portal2 = Portal(name="Shop", region="Shop", + portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", destination="Previous Region", tag="_") + create_shop_region(world, regions) elif portal2_sdt == "Purgatory, Purgatory_bottom": portal2_sdt = "Purgatory, Purgatory_top" @@ -125,8 +137,7 @@ def vanilla_portals() -> Dict[Portal, Portal]: # pairing off portals, starting with dead ends -def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]: - # separate the portals into dead ends and non-dead ends +def pair_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]: portal_pairs: Dict[Portal, Portal] = {} dead_ends: List[Portal] = [] two_plus: List[Portal] = [] @@ -156,6 +167,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]: if laurels_location == "10_fairies" and not hasattr(world.multiworld, "re_gen_passthrough"): has_laurels = False + # need to keep track of which scenes have shops, since you shouldn't have multiple shops connected to the same scene shop_scenes: Set[str] = set() shop_count = 6 if fixed_shop: @@ -293,8 +305,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]: break # if it's not a dead end, it might be a shop if p_exit == "Shop Portal": - portal2 = Portal(name="Shop Portal", region="Shop", + portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", destination="Previous Region", tag="_") + create_shop_region(world, regions) shop_count -= 1 # need to maintain an even number of portals total if shop_count < 0: @@ -351,7 +364,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]: raise Exception(f"Failed to do Fixed Shop option. " f"Did {player_name} plando connection the Windmill Shop entrance?") - portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_") + portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", + destination="Previous Region", tag="_") + create_shop_region(world, regions) portal_pairs[portal1] = portal2 two_plus.remove(portal1) @@ -435,7 +450,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]: break if portal1 is None: raise Exception("Too many shops in the pool, or something else went wrong.") - portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_") + portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", + destination="Previous Region", tag="_") + create_shop_region(world, regions) portal_pairs[portal1] = portal2 @@ -468,9 +485,7 @@ def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dic region1 = regions[portal1.region] region2 = regions[portal2.region] region1.connect(connecting_region=region2, name=portal1.name) - # prevent the logic from thinking you can get to any shop-connected region from the shop - if portal2.name not in {"Shop", "Shop Portal"}: - region2.connect(connecting_region=region1, name=portal2.name) + region2.connect(connecting_region=region1, name=portal2.name) def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[str, Dict[str, List[List[str]]]],