From 608d96329fc728431bfa6ebc158141fc41da106a Mon Sep 17 00:00:00 2001 From: pepperpow Date: Mon, 23 Nov 2020 20:05:04 -0600 Subject: [PATCH] Shop Item Pool fill feature + multiworld player compatability --- BaseClasses.py | 33 +++++++++++++++-- EntranceRandomizer.py | 7 +++- Main.py | 1 + Mystery.py | 2 ++ Regions.py | 25 ++++++++++++- WebHostLib/static/static/playerSettings.json | 38 ++++++++++++++++++++ playerSettings.yaml | 13 +++---- 7 files changed, 109 insertions(+), 10 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index dd7b24e2..d8d6c94f 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -131,6 +131,7 @@ class World(object): set_player_attr('triforce_pieces_available', 30) set_player_attr('triforce_pieces_required', 20) set_player_attr('shop_shuffle', 'off') + set_player_attr('shop_shuffle_slots', 0) set_player_attr('shuffle_prizes', "g") set_player_attr('sprite_pool', []) set_player_attr('dark_room_logic', "lamp") @@ -340,6 +341,27 @@ class World(object): if collect: self.state.collect(item, location.event, location) + # TODO: Prevents fast_filling certain items. Move this to a proper filter. + if location.parent_region.shop is not None and location.name != 'Potion Shop': # includes potion shop slots but not potion shop powder + slot_num = int(location.name[-1]) - 1 + my_item = location.parent_region.shop.inventory[slot_num] + if (my_item is not None and my_item['item'] == item.name) or 'Rupee' in item.name: + # this will filter items that match the item in the shop or Rupees + # really not a way for the player to know a renewable item from a world item + # bombs can be sitting on top of arrows or a potion refill, but dunno if that's a big deal + logging.debug('skipping item shop {}'.format(item.name)) + else: + if my_item is None: + location.parent_region.shop.add_inventory(slot_num, 'None', 0) + my_item = location.parent_region.shop.inventory[slot_num] + else: + my_item['replacement'] = my_item['item'] + my_item['replacement_price'] = my_item['price'] + my_item['item'] = item.name + my_item['price'] = self.random.randrange(1, 61) * 5 # can probably replace this with a price chart + my_item['max'] = 1 + my_item['player'] = item.player if item.player != location.player else 0 + logging.debug('Placed %s at %s', item, location) else: raise RuntimeError('Cannot assign item %s to location %s.' % (item, location)) @@ -1140,7 +1162,8 @@ class Shop(): 'max': max, 'replacement': replacement, 'replacement_price': replacement_price, - 'create_location': create_location + 'create_location': create_location, + 'player': 0 } def push_inventory(self, slot: int, item: str, price: int, max: int = 1): @@ -1153,7 +1176,8 @@ class Shop(): 'max': max, 'replacement': self.inventory[slot]["item"], 'replacement_price': self.inventory[slot]["price"], - 'create_location': self.inventory[slot]["create_location"] + 'create_location': self.inventory[slot]["create_location"], + 'player': self.inventory[slot]["player"] } @@ -1239,6 +1263,10 @@ class Spoiler(object): if item is None: continue shopdata['item_{}'.format(index)] = "{} — {}".format(item['item'], item['price']) if item['price'] else item['item'] + + if item['player'] > 0: + shopdata['item_{}'.format(index)] = shopdata['item_{}'.format(index)].replace('—', '(Player {}) — '.format(item['player'])) + if item['max'] == 0: continue shopdata['item_{}'.format(index)] += " x {}".format(item['max']) @@ -1312,6 +1340,7 @@ class Spoiler(object): 'triforce_pieces_available': self.world.triforce_pieces_available, 'triforce_pieces_required': self.world.triforce_pieces_required, 'shop_shuffle': self.world.shop_shuffle, + 'shop_shuffle_slots': self.world.shop_shuffle_slots, 'shuffle_prizes': self.world.shuffle_prizes, 'sprite_pool': self.world.sprite_pool, 'restrict_dungeon_item_on_boss': self.world.restrict_dungeon_item_on_boss diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 5c9ab56c..b94ed0db 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -330,6 +330,11 @@ def parse_arguments(argv, no_defaults=False): p: randomize the prices of the items in shop inventories u: shuffle capacity upgrades into the item pool ''') + parser.add_argument('--shop_shuffle_slots', default=defval(0), + type=lambda value: min(max(int(value), 1), 96), + help=''' + Maximum amount of shop slots able to be filled by items from the item pool. + ''') parser.add_argument('--shuffle_prizes', default=defval('g'), choices=['', 'g', 'b', 'gb']) parser.add_argument('--sprite_pool', help='''\ Specifies a colon separated list of sprites used for random/randomonevent. If not specified, the full sprite pool is used.''') @@ -382,7 +387,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", "shop_shuffle", + "triforce_pieces_required", "shop_shuffle", "shop_shuffle_slots", 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'restrict_dungeon_item_on_boss', 'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes']: diff --git a/Main.py b/Main.py index e58dcdaf..e66e2181 100644 --- a/Main.py +++ b/Main.py @@ -83,6 +83,7 @@ def main(args, seed=None): 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.shop_shuffle_slots = args.shop_shuffle_slots.copy() world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()} world.shuffle_prizes = args.shuffle_prizes.copy() world.sprite_pool = args.sprite_pool.copy() diff --git a/Mystery.py b/Mystery.py index d74c9973..f28ca26a 100644 --- a/Mystery.py +++ b/Mystery.py @@ -362,6 +362,8 @@ def roll_settings(weights): # change minimum to required pieces to avoid problems ret.triforce_pieces_available = min(max(ret.triforce_pieces_required, int(ret.triforce_pieces_available)), 90) + ret.shop_shuffle_slots = int(get_choice('shop_shuffle_slots', weights, '0')) + ret.shop_shuffle = get_choice('shop_shuffle', weights, '') if not ret.shop_shuffle: ret.shop_shuffle = '' diff --git a/Regions.py b/Regions.py index bdef78ac..a0e97d01 100644 --- a/Regions.py +++ b/Regions.py @@ -368,7 +368,17 @@ def create_shops(world, player: int): cls_mapping = {ShopType.UpgradeShop: UpgradeShop, ShopType.Shop: Shop, ShopType.TakeAny: TakeAny} - for region_name, (room_id, type, shopkeeper, custom, locked, inventory) in shop_table.items(): + option = world.shop_shuffle[player] + my_shop_table = dict(shop_table) + + num_slots = int(world.shop_shuffle_slots[player]) + + my_shop_slots = ([True] * num_slots + [False] * (len(shop_table) * 3))[:len(shop_table)*3 - 2] + + world.random.shuffle(my_shop_slots) + + from Items import ItemFactory + for region_name, (room_id, type, shopkeeper, custom, locked, inventory) in my_shop_table.items(): if world.mode[player] == 'inverted' and region_name == 'Dark Lake Hylia Shop': locked = True inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)] @@ -378,6 +388,19 @@ def create_shops(world, player: int): world.shops.append(shop) for index, item in enumerate(inventory): shop.add_inventory(index, *item) + if region_name == 'Potion Shop': + pass + elif region_name == 'Capacity Upgrade': + pass + else: + if my_shop_slots.pop(): + additional_item = world.random.choice(['Rupees (20)', 'Rupees (50)', 'Rupees (100)']) + world.itempool.append(ItemFactory(additional_item, player)) + loc = Location(player, "{} Slot Item {}".format(shop.region.name, index+1), parent=shop.region) + shop.region.locations.append(loc) + world.dynamic_locations.append(loc) + + world.clear_location_cache() # (type, room_id, shopkeeper, custom, locked, [items]) # item = (item, price, max=0, replacement=None, replacement_price=0) diff --git a/WebHostLib/static/static/playerSettings.json b/WebHostLib/static/static/playerSettings.json index e6ee539f..7a3a88d4 100644 --- a/WebHostLib/static/static/playerSettings.json +++ b/WebHostLib/static/static/playerSettings.json @@ -1124,6 +1124,44 @@ } } }, + "shop_shuffle_slots": { + "keyString": "shop_shuffle_slots", + "friendlyName": "Shop Shuffle Slots", + "description": "How Many Slots in Shops are dedicated to items from the item pool", + "inputType": "range", + "subOptions": { + "0": { + "keyString": "shop_shuffle_slots.0", + "friendlyName": 0, + "description": "0 slots", + "defaultValue": 50 + }, + "15": { + "keyString": "shop_shuffle_slots.3", + "friendlyName": 3, + "description": "3 slots", + "defaultValue": 0 + }, + "20": { + "keyString": "shop_shuffle_slots.6", + "friendlyName": 6, + "description": "6 slots", + "defaultValue": 0 + }, + "30": { + "keyString": "shop_shuffle_slots.12", + "friendlyName": 12, + "description": "12 slots", + "defaultValue": 0 + }, + "40": { + "keyString": "shop_shuffle_slots.96", + "friendlyName": 96, + "description": "96 slots", + "defaultValue": 0 + } + } + }, "shuffle_prizes": { "keyString": "shuffle_prizes", "friendlyName": "Prize Shuffle", diff --git a/playerSettings.yaml b/playerSettings.yaml index 469a96de..723ea7db 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -211,9 +211,15 @@ 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 +### Item Shuffle (shop) +shop_shuffle_slots: + 0: 50 + 5: 0 + 15: 0 + 999: 0 shop_shuffle: none: 50 - i: 0 # Shuffle the inventories of the shops around + i: 0 # Shuffle default inventories of the shops around p: 0 # Randomize the prices of the items in shop inventories u: 0 # Shuffle capacity upgrades into the item pool (and allow them to traverse the multiworld) ip: 0 # Shuffle inventories and randomize prices @@ -253,11 +259,6 @@ green_clock_time: # For all timer modes, the amount of time in minutes to gain o # - "Small Keys" # - "Big Keys" # Can be uncommented to use it -# non_local_items: # Force certain items to appear outside your world only, always across the multiworld. Recognizes some group names, like "Swords" -# - "Moon Pearl" -# - "Small Keys" -# - "Big Keys" -# Can be uncommented to use it # startinventory: # Begin the file with the listed items/upgrades # Pegasus Boots: on # Bomb Upgrade (+10): 4