From 26ab3dd69a0f68c6f685a6b827f0cf654e0f2226 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 23 Aug 2020 15:03:06 +0200 Subject: [PATCH] Shop Shuffle - more can be done here, but this works fine as a sometimes fun proof of concept --- BaseClasses.py | 2 ++ EntranceRandomizer.py | 3 ++- Gui.py | 15 ++++++++++++++- ItemPool.py | 38 +++++++++++++++++++++++++++++++++++++- Main.py | 1 + Mystery.py | 4 ++++ playerSettings.yaml | 5 +++++ 7 files changed, 65 insertions(+), 3 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index c8c6a0cb..30e6f216 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -124,6 +124,7 @@ class World(object): set_player_attr('local_items', set()) set_player_attr('triforce_pieces_available', 30) set_player_attr('triforce_pieces_required', 20) + set_player_attr('shop_shuffle', 'off') def secure(self): self.random = secrets.SystemRandom() @@ -1052,6 +1053,7 @@ class ShopType(Enum): UpgradeShop = 2 class Shop(object): + slots = 3 def __init__(self, region, room_id, type, shopkeeper_config, custom, locked): self.region = region self.room_id = room_id diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index e6fc67dc..77741199 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -312,6 +312,7 @@ def parse_arguments(argv, no_defaults=False): parser.add_argument('--enemy_damage', default=defval('default'), choices=['default', 'shuffled', 'chaos']) parser.add_argument('--shufflepots', default=defval(False), action='store_true') parser.add_argument('--beemizer', default=defval(0), type=lambda value: min(max(int(value), 0), 4)) + parser.add_argument('--shop_shuffle', default='off', choices=['off', 'inventory', 'price', 'inventory_price']) parser.add_argument('--remote_items', default=defval(False), action='store_true') parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255)) parser.add_argument('--names', default=defval('')) @@ -355,7 +356,7 @@ def parse_arguments(argv, no_defaults=False): 'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', "skip_progression_balancing", "triforce_pieces_available", - "triforce_pieces_required", + "triforce_pieces_required", "shop_shuffle", 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'tile_shuffle', 'bush_shuffle']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) diff --git a/Gui.py b/Gui.py index 29a623f0..bcb0317c 100755 --- a/Gui.py +++ b/Gui.py @@ -344,11 +344,22 @@ def guiMain(args=None): shuffleFrame = Frame(drowDownFrame) shuffleVar = StringVar() shuffleVar.set('vanilla') - shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple') + shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'crossed', + 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', + 'dungeonsfull', 'dungeonssimple') shuffleOptionMenu.pack(side=RIGHT) shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm') shuffleLabel.pack(side=LEFT) + shop_shuffleFrame = Frame(drowDownFrame) + shop_shuffleVar = StringVar() + shop_shuffleVar.set('off') + shop_shuffleOptionMenu = OptionMenu(shop_shuffleFrame, shop_shuffleVar, 'off', 'inventory', 'price', + 'price and inventory') + shop_shuffleOptionMenu.pack(side=RIGHT) + shop_shuffleLabel = Label(shop_shuffleFrame, text='Shop Shuffle') + shop_shuffleLabel.pack(side=LEFT) + modeFrame.pack(expand=True, anchor=E) logicFrame.pack(expand=True, anchor=E) goalFrame.pack(expand=True, anchor=E) @@ -363,6 +374,7 @@ def guiMain(args=None): accessibilityFrame.pack(expand=True, anchor=E) algorithmFrame.pack(expand=True, anchor=E) shuffleFrame.pack(expand=True, anchor=E) + shop_shuffleFrame.pack(expand=True, anchor=E) enemizerFrame = LabelFrame(randomizerWindow, text="Enemizer", padx=5, pady=2) @@ -479,6 +491,7 @@ def guiMain(args=None): guiargs.accessibility = accessibilityVar.get() guiargs.algorithm = algorithmVar.get() guiargs.shuffle = shuffleVar.get() + guiargs.shop_shuffle = shop_shuffleVar.get() guiargs.heartbeep = heartbeepVar.get() guiargs.heartcolor = heartcolorVar.get() guiargs.fastmenu = fastMenuVar.get() diff --git a/ItemPool.py b/ItemPool.py index f4f9b3a1..89a54cf1 100644 --- a/ItemPool.py +++ b/ItemPool.py @@ -323,9 +323,45 @@ def generate_itempool(world, player: int): if world.retro[player]: set_up_take_anys(world, player) - + if world.shop_shuffle[player] != 'off': + shuffle_shops(world, player) create_dynamic_shop_locations(world, player) + +def shuffle_shops(world, player: int): + option = world.shop_shuffle[player] + shops = [] + total_inventory = [] + for shop in world.shops: + if shop.type == ShopType.Shop and shop.region.player == player: + shops.append(shop) + total_inventory.extend(shop.inventory) + + if 'price' in option: + def price_adjust(price: int) -> int: + # it is important that a base price of 0 always returns 0 as new price! + return int(price * (0.5 + world.random.random() * 2)) + + for item in total_inventory: + if item: + item["price"] = price_adjust(item["price"]) + item['replacement_price'] = price_adjust(item["price"]) + for shop in world.shops: + if shop.type == ShopType.UpgradeShop and shop.region.player == player: + for item in shop.inventory: + if item: + item['price'] = price_adjust(item["price"]) + item['replacement_price'] = price_adjust(item["price"]) + + if 'inventory' in option: + world.random.shuffle(total_inventory) + i = 0 + for shop in shops: + slots = shop.slots + shop.inventory = total_inventory[i:i + slots] + i += slots + + take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', 'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)', diff --git a/Main.py b/Main.py index 17f16ef0..55320328 100644 --- a/Main.py +++ b/Main.py @@ -79,6 +79,7 @@ def main(args, seed=None): world.glitch_boots = args.glitch_boots.copy() world.triforce_pieces_available = args.triforce_pieces_available.copy() world.triforce_pieces_required = args.triforce_pieces_required.copy() + world.shop_shuffle = args.shop_shuffle.copy() world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()} world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)} diff --git a/Mystery.py b/Mystery.py index 504f1d3e..652c5070 100644 --- a/Mystery.py +++ b/Mystery.py @@ -310,6 +310,10 @@ def roll_settings(weights): ret.triforce_pieces_required = get_choice('triforce_pieces_required', weights, 20) ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90) + ret.shop_shuffle = get_choice('shop_shuffle', weights, False) + if not ret.shop_shuffle: + ret.shop_shuffle = 'off' + ret.mode = get_choice('world_state', weights, None) # legacy support if ret.mode == 'retro': ret.mode = 'open' diff --git a/playerSettings.yaml b/playerSettings.yaml index 35ce23e8..58181f6d 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -189,6 +189,11 @@ beemizer: # Remove items from the global item pool and replace them with single 2: 0 # 60% of the non-essential item pool is replaced with bee traps, of which 20% could be single bees 3: 0 # 100% of the non-essential item pool is replaced with bee traps, of which 50% could be single bees 4: 0 # 100% of the non-essential item pool is replaced with bee traps +shop_shuffle: + off: 1 + inventory: 0 # shuffle the inventories of the shops around + price: 0 # randomize the prices of the items in shop inventories + inventory_price: 0 # shuffle inventories and randomize prices timer: none: 1 timed: 0