From 6095cfc58693e768d81e5c01f9c84ce5c7e2bf0a Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Fri, 22 Jan 2021 05:40:50 -0800 Subject: [PATCH] If any shop shuffle slots are defined, define ALL of them to avoid information leakage. --- BaseClasses.py | 1 + Main.py | 4 +++- Shops.py | 31 +++++++++++++++++++++---------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index fc1cdd71..a08b0cbe 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1074,6 +1074,7 @@ class Boss(): class Location(): shop_slot: bool = False + shop_slot_disabled: bool = False event: bool = False locked: bool = False diff --git a/Main.py b/Main.py index c2113094..4718bcdc 100644 --- a/Main.py +++ b/Main.py @@ -10,7 +10,7 @@ import zlib import concurrent.futures from BaseClasses import World, CollectionState, Item, Region, Location -from Shops import ShopSlotFill, create_shops, SHOP_ID_START +from Shops import ShopSlotFill, create_shops, SHOP_ID_START, FillDisabledShopSlots from Items import ItemFactory, item_table, item_name_groups from Regions import create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance from InvertedRegions import create_inverted_regions, mark_dark_world_regions @@ -389,6 +389,8 @@ def main(args, seed=None): for item in world.precollected_items: precollected_items[item.player - 1].append(item.code) + FillDisabledShopSlots(world) + def write_multidata(roms): for future in roms: rom_name = future.result() diff --git a/Shops.py b/Shops.py index ad65ada6..7d346e5f 100644 --- a/Shops.py +++ b/Shops.py @@ -125,6 +125,17 @@ shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop, ShopType.TakeAny: TakeAny} +def FillDisabledShopSlots(world): + shop_slots: Set[Location] = {location for shop_locations in (shop.region.locations for shop in world.shops) + for location in shop_locations if location.shop_slot and location.shop_slot_disabled} + for location in shop_slots: + location.shop_slot_disabled = True + slot_num = int(location.name[-1]) - 1 + shop: Shop = location.parent_region.shop + location.item = ItemFactory(shop.inventory[slot_num]['item'], location.player) + location.item_rule = lambda item: item.name == location.item.name and item.player == location.player + + def ShopSlotFill(world): shop_slots: Set[Location] = {location for shop_locations in (shop.region.locations for shop in world.shops) for location in shop_locations if location.shop_slot} @@ -132,15 +143,11 @@ def ShopSlotFill(world): for location in shop_slots: slot_num = int(location.name[-1]) - 1 shop: Shop = location.parent_region.shop - if not shop.can_push_inventory(slot_num): + if not shop.can_push_inventory(slot_num) or location.shop_slot_disabled: removed.add(location) - shop.region.locations.remove(location) if removed: shop_slots -= removed - # remove locations that may no longer exist from caches, by flushing them entirely - world.clear_location_cache() - world._location_cache = {} if shop_slots: from Fill import swap_location_item @@ -168,7 +175,7 @@ def ShopSlotFill(world): world.random.shuffle(sphere) for i, sphere in enumerate(candidates_per_sphere): - current_shop_slots = [location for location in sphere if location.shop_slot] + current_shop_slots = [location for location in sphere if location.shop_slot and not location.shop_slot_disabled] if current_shop_slots: for location in current_shop_slots: @@ -183,7 +190,7 @@ def ShopSlotFill(world): else: # This *should* never happen. But let's fail safely just in case. logger.warning("Ran out of ShopShuffle Item candidate locations.") - shop.region.locations.remove(location) + location.shop_slot_disabled = True continue item_name = location.item.name if any(x in item_name for x in ['Single Bomb', 'Single Arrow']): @@ -239,14 +246,18 @@ def create_shops(world, player: int): world.shops.append(shop) for index, item in enumerate(inventory): shop.add_inventory(index, *item) - if not locked and single_purchase_slots.pop(): - additional_item = 'Rupees (50)' # world.random.choice(['Rupees (50)', 'Rupees (100)', 'Rupees (300)']) + if not locked and num_slots: slot_name = "{} Slot {}".format(region.name, index + 1) loc = Location(player, slot_name, address=shop_table_by_location[slot_name], parent=region, hint_text="for sale") loc.shop_slot = True loc.locked = True - loc.item = ItemFactory(additional_item, player) + if single_purchase_slots.pop(): + additional_item = 'Rupees (50)' # world.random.choice(['Rupees (50)', 'Rupees (100)', 'Rupees (300)']) + loc.item = ItemFactory(additional_item, player) + else: + loc.item = ItemFactory('Nothing', player) + loc.shop_slot_disabled = True shop.region.locations.append(loc) world.dynamic_locations.append(loc) world.clear_location_cache()