diff --git a/BaseClasses.py b/BaseClasses.py index 130a7657..350260d4 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1056,7 +1056,7 @@ class Spoiler(): listed_locations.update(other_locations) self.shops = [] - from worlds.alttp.Shops import ShopType, price_type_display_name + from worlds.alttp.Shops import ShopType, price_type_display_name, price_rate_display for shop in self.world.shops: if not shop.custom: continue @@ -1067,7 +1067,8 @@ class Spoiler(): for index, item in enumerate(shop.inventory): if item is None: continue - shopdata['item_{}'.format(index)] = f"{item['item']} — {item['price']} {price_type_display_name[item['price_type']]}" + my_price = item['price'] // price_rate_display.get(item['price_type'], 1) + shopdata['item_{}'.format(index)] = f"{item['item']} — {my_price} {price_type_display_name[item['price_type']]}" if item['player'] > 0: shopdata['item_{}'.format(index)] = shopdata['item_{}'.format(index)].replace('—', '(Player {}) — '.format(item['player'])) diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index e8a697e3..2f96c4c4 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -22,7 +22,7 @@ from typing import Optional from BaseClasses import CollectionState, Region from worlds.alttp.SubClasses import ALttPLocation -from worlds.alttp.Shops import ShopType +from worlds.alttp.Shops import ShopType, ShopPriceType from worlds.alttp.Dungeons import dungeon_music_addresses from worlds.alttp.Regions import location_table, old_location_address_to_new_location_address from worlds.alttp.Text import MultiByteTextMapper, text_addresses, Credits, TextTable @@ -1711,7 +1711,13 @@ def write_custom_shops(rom, world, player): for index, item in enumerate(shop.inventory): if item is None: break - price_data = int16_as_bytes(0x8000 | 0x100 * (item["price_type"] - 1) | item['price']) + if item['price_type'] != ShopPriceType.Rupees: + # Set special price flag 0x8000 + # Then set the type of price we're setting 0x7F00 (this starts from Hearts, not Rupees, subtract 1) + # Then append the price/index into the second byte 0x00FF + price_data = int16_as_bytes(0x8000 | 0x100 * (item["price_type"] - 1) | item['price']) + else: + price_data = int16_as_bytes(item['price']) slot = 0 if shop.type == ShopType.TakeAny else index if not item['item'] in item_table: # item not native to ALTTP item_code = get_nonnative_item_sprite(item['item']) diff --git a/worlds/alttp/Shops.py b/worlds/alttp/Shops.py index e62e8402..b4dca40c 100644 --- a/worlds/alttp/Shops.py +++ b/worlds/alttp/Shops.py @@ -458,7 +458,7 @@ def shuffle_shops(world, items, player: int): for item in new_items: world.push_precollected(ItemFactory(item, player)) - if 'p' in option or 'i' in option: + if any(setting in option for setting in 'ipP'): shops = [] upgrade_shops = [] total_inventory = [] @@ -490,9 +490,12 @@ def shuffle_shops(world, items, player: int): if 'P' in option: for item in total_inventory: price_to_funny_price(item, world, player) + # Don't apply to upgrade shops + # Upgrade shop is only one place, and will generally be too easy to + # replenish hearts and bombs for shop in upgrade_shops: for item in shop.inventory: - price_to_funny_price(item, world, player) + pass if 'i' in option: world.random.shuffle(total_inventory) @@ -519,14 +522,14 @@ price_blacklist = { price_chart = { ShopPriceType.Rupees: lambda p: p, - ShopPriceType.Hearts: lambda p: min(1, p // 5) * 4, - ShopPriceType.Magic: lambda p: min(8, p // 5) * 4, - ShopPriceType.Bombs: lambda p: min(10, p // 5), - ShopPriceType.Arrows: lambda p: min(30, p // 5), + ShopPriceType.Hearts: lambda p: min(5, p // 5) * 8, # Each heart is 0x8 in memory, Max of 5 hearts (20 total??) + ShopPriceType.Magic: lambda p: min(15, p // 5) * 8, # Each pip is 0x8 in memory, Max of 15 pips (16 total...) + ShopPriceType.Bombs: lambda p: max(1, min(10, p // 5)), # 10 Bombs max + ShopPriceType.Arrows: lambda p: max(1, min(30, p // 5)), # 30 Arrows Max ShopPriceType.HeartContainer: lambda p: 0x8, ShopPriceType.BombUpgrade: lambda p: 0x1, ShopPriceType.ArrowUpgrade: lambda p: 0x1, - ShopPriceType.Keys: lambda p: min(3, (p // 100) + 1), + ShopPriceType.Keys: lambda p: min(3, (p // 100) + 1), # Max of 3 keys for a price ShopPriceType.Potion: lambda p: (p // 5) % 5, } @@ -535,6 +538,13 @@ price_type_display_name = { ShopPriceType.Hearts: "Hearts", ShopPriceType.Bombs: "Bombs", ShopPriceType.Arrows: "Arrows", + ShopPriceType.Keys: "Keys", +} + +# price division +price_rate_display = { + ShopPriceType.Hearts: 8, + ShopPriceType.Magic: 8, } # prices with no? logic requirements @@ -543,20 +553,34 @@ simple_price_types = [ ShopPriceType.Hearts, ShopPriceType.Bombs, ShopPriceType.Arrows, + ShopPriceType.Keys ] def price_to_funny_price(item: dict, world, player: int): + """ + Converts a raw Rupee price into a special price type + """ if item: my_price_types = simple_price_types.copy() world.random.shuffle(my_price_types) - for p in my_price_types: - if p in [ShopPriceType.Rupees, ShopPriceType.BombUpgrade, ShopPriceType.ArrowUpgrade] or ( - p in [ShopPriceType.Keys] and world.smallkey_shuffle[player] == smallkey_shuffle.option_universal): + for p_type in my_price_types: + # Ignore rupee prices, logic-based prices or Keys (if we're not on universal keys) + if p_type in [ShopPriceType.Rupees, ShopPriceType.BombUpgrade, ShopPriceType.ArrowUpgrade]: return - if any(x in item['item'] for x in price_blacklist[p]): + # If we're using keys... + # Check if we're in universal, check if our replacement isn't a Small Key + # Check if price isn't super small... (this will ideally be handled in a future table) + if p_type in [ShopPriceType.Keys]: + if world.smallkey_shuffle[player] != smallkey_shuffle.option_universal: + continue + elif item['replacement'] and 'Small Key' in item['replacement']: + continue + if item['price'] < 50: + continue + if any(x in item['item'] for x in price_blacklist[p_type]): continue else: - item['price'] = price_chart[p](item['price']) - item['price_type'] = p + item['price'] = min(price_chart[p_type](item['price']) , 255) + item['price_type'] = p_type break