From b5f3c752e600c66e2920b70f831a48e7ce312411 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 1 Feb 2021 09:57:20 -0700 Subject: [PATCH] Shop work: - Potion shop - Cap Fairy blacklist - Item counter increased over 255 - Inverted work - Static sram for shops - Price adjustments (and discounts) --- BaseClasses.py | 19 +++-- Fill.py | 39 ++++++--- InvertedRegions.py | 37 ++++++++- ItemList.py | 49 +++++++++--- Items.py | 178 +++++++++++++++++++++--------------------- Main.py | 12 ++- MultiClient.py | 26 +++++- Regions.py | 44 +++++++---- Rom.py | 41 ++++++---- asm/hudadditions.asm | 2 +- data/base2current.bps | Bin 131159 -> 131582 bytes 11 files changed, 292 insertions(+), 155 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 588ff955..a428b19b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1739,7 +1739,7 @@ class ShopType(Enum): UpgradeShop = 2 class Shop(object): - def __init__(self, region, room_id, type, shopkeeper_config, custom, locked): + def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address): self.region = region self.room_id = room_id self.type = type @@ -1747,6 +1747,7 @@ class Shop(object): self.shopkeeper_config = shopkeeper_config self.custom = custom self.locked = locked + self.sram_address = sram_address @property def item_count(self): @@ -1763,11 +1764,11 @@ class Shop(object): door_id = door_addresses[entrances[0].name][0]+1 else: door_id = 0 - config |= 0x40 # ignore door id + config |= 0x40 # ignore door id if self.type == ShopType.TakeAny: config |= 0x80 if self.type == ShopType.UpgradeShop: - config |= 0x10 # Alt. VRAM + config |= 0x10 # Alt. VRAM return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00] def has_unlimited(self, item): @@ -1783,14 +1784,16 @@ class Shop(object): def clear_inventory(self): self.inventory = [None, None, None] - def add_inventory(self, slot, item, price, max=0, replacement=None, replacement_price=0, create_location=False): + def add_inventory(self, slot: int, item, price, max=0, replacement=None, replacement_price=0, + create_location=False, player=0): self.inventory[slot] = { 'item': item, 'price': price, 'max': max, 'replacement': replacement, 'replacement_price': replacement_price, - 'create_location': create_location + 'create_location': create_location, + 'player': player } @@ -1885,8 +1888,10 @@ class Spoiler(object): for index, item in enumerate(shop.inventory): if item is None: continue - # todo: indicate player? might be fine - shopdata[f'item_{index}'] = f"{item['item']} — {item['price']}" if item['price'] else item['item'] + if self.world.players == 1: + shopdata[f'item_{index}'] = f"{item['item']} — {item['price']}" if item['price'] else item['item'] + else: + shopdata[f'item_{index}'] = f"{item['item']} (Player {item['player']}) — {item['price']}" self.shops.append(shopdata) for player in range(1, self.world.players + 1): diff --git a/Fill.py b/Fill.py index a4fe672f..d7bbcd75 100644 --- a/Fill.py +++ b/Fill.py @@ -2,7 +2,7 @@ import random import logging from BaseClasses import CollectionState -from Regions import shop_to_location_table +from Regions import shop_to_location_table, retro_shops class FillError(RuntimeError): @@ -376,18 +376,33 @@ def flood_items(world): break -def sell_keys(world, player): - choices = [] - shop_map = {} +def sell_potions(world, player): + loc_choices = [] for shop in world.shops[player]: - if shop.region.name in shop_to_location_table: - choices.append(shop.region.name) - shop_map[shop.region.name] = shop - key_seller = random.choice(choices) - location = random.choice(shop_to_location_table[key_seller]) - universal_key = next(item for item in world.itempool if item.name == 'Small Key (Universal)' and item.player == player) - world.push_item(world.get_location(location, player), universal_key, collect=False) - shop_map[key_seller].add_inventory(0, 'Small Key (Universal)', 100) # will be fixed later in customize shops + # potions are excluded from the cap fairy due to visual problem + if shop.region.name in shop_to_location_table and shop.region.name != 'Capacity Upgrade': + loc_choices += [world.get_location(loc, player) for loc in shop_to_location_table[shop.region.name]] + if world.retro[player] and shop.region.name in retro_shops: + loc_choices += [world.get_location(loc, player) for loc in retro_shops[shop.region.name]] + locations = [loc for loc in loc_choices if not loc.item] + for potion in ['Green Potion', 'Blue Potion', 'Red Potion']: + location = random.choice(locations) + locations.remove(location) + p_item = next(item for item in world.itempool if item.name == potion and item.player == player) + world.push_item(location, p_item, collect=False) + world.itempool.remove(p_item) + + +def sell_keys(world, player): + # exclude the old man or take any caves because free keys are too good + shop_names = [shop.region.name for shop in world.shops[player] if shop.region.name in shop_to_location_table] + choices = [world.get_location(loc, player) for shop in shop_names for loc in shop_to_location_table[shop]] + locations = [loc for loc in choices if not loc.item] + location = random.choice(locations) + universal_key = next(i for i in world.itempool if i.name == 'Small Key (Universal)' and i.player == player) + world.push_item(location, universal_key, collect=False) + # seems unnecessary + # shop_map[key_seller].add_inventory(0, 'Small Key (Universal)', 100) # will be fixed later in customize shops world.itempool.remove(universal_key) diff --git a/InvertedRegions.py b/InvertedRegions.py index ed70103f..4ae0bbdb 100644 --- a/InvertedRegions.py +++ b/InvertedRegions.py @@ -94,7 +94,7 @@ def create_inverted_regions(world, player): create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']), create_cave_region(player, 'Library', 'the library', ['Library']), create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), - create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']), + create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop', 'Potion Shop - Left', 'Potion Shop - Middle', 'Potion Shop - Right']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), @@ -473,4 +473,37 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Skull Woods - Prize': ([0x120A3, 0x53F12, 0x53F13, 0x180058, 0x18007B, 0xC704], None, True, 'Skull Woods'), 'Ice Palace - Prize': ([0x120A4, 0x53F5A, 0x53F5B, 0x180059, 0x180073, 0xC705], None, True, 'Ice Palace'), 'Misery Mire - Prize': ([0x120A2, 0x53F48, 0x53F49, 0x180057, 0x180075, 0xC703], None, True, 'Misery Mire'), - 'Turtle Rock - Prize': ([0x120A7, 0x53F24, 0x53F25, 0x18005C, 0x180079, 0xC708], None, True, 'Turtle Rock')} + 'Turtle Rock - Prize': ([0x120A7, 0x53F24, 0x53F25, 0x18005C, 0x180079, 0xC708], None, True, 'Turtle Rock'), + 'Kakariko Shop - Left': (None, None, False, 'for sale in Kakariko'), + 'Kakariko Shop - Middle': (None, None, False, 'for sale in Kakariko'), + 'Kakariko Shop - Right': (None, None, False, 'for sale in Kakariko'), + 'Lake Hylia Shop - Left': (None, None, False, 'for sale near the lake'), + 'Lake Hylia Shop - Middle': (None, None, False, 'for sale near the lake'), + 'Lake Hylia Shop - Right': (None, None, False, 'for sale near the lake'), + 'Paradox Shop - Left': (None, None, False, 'for sale near seven chests'), + 'Paradox Shop - Middle': (None, None, False, 'for sale near seven chests'), + 'Paradox Shop - Right': (None, None, False, 'for sale near seven chests'), + 'Capacity Upgrade - Left': (None, None, False, 'for sale near the queen'), + 'Capacity Upgrade - Right': (None, None, False, 'for sale near the queen'), + 'Village of Outcasts Shop - Left': (None, None, False, 'for sale near outcasts'), + 'Village of Outcasts Shop - Middle': (None, None, False, 'for sale near outcasts'), + 'Village of Outcasts Shop - Right': (None, None, False, 'for sale near outcasts'), + 'Dark Lumberjack Shop - Left': (None, None, False, 'for sale in the far north'), + 'Dark Lumberjack Shop - Middle': (None, None, False, 'for sale in the far north'), + 'Dark Lumberjack Shop - Right': (None, None, False, 'for sale in the far north'), + 'Dark Lake Hylia Shop - Left': (None, None, False, 'for sale near the dark lake'), + 'Dark Lake Hylia Shop - Middle': (None, None, False, 'for sale near the dark lake'), + 'Dark Lake Hylia Shop - Right': (None, None, False, 'for sale near the dark lake'), + 'Dark Potion Shop - Left': (None, None, False, 'for sale near a catfish'), + 'Dark Potion Shop - Middle': (None, None, False, 'for sale near a catfish'), + 'Dark Potion Shop - Right': (None, None, False, 'for sale near a catfish'), + 'Dark Death Mountain Shop - Left': (None, None, False, 'for sale on the dark mountain'), + 'Dark Death Mountain Shop - Middle': (None, None, False, 'for sale on the dark mountain'), + 'Dark Death Mountain Shop - Right': (None, None, False, 'for sale on the dark mountain'), + 'Red Shield Shop - Left': (None, None, False, 'for sale as a curiosity'), + 'Red Shield Shop - Middle': (None, None, False, 'for sale as a curiosity'), + 'Red Shield Shop - Right': (None, None, False, 'for sale as a curiosity'), + 'Potion Shop - Left': (None, None, False, 'for sale near the witch'), + 'Potion Shop - Middle': (None, None, False, 'for sale near the witch'), + 'Potion Shop - Right': (None, None, False, 'for sale near the witch'), + } diff --git a/ItemList.py b/ItemList.py index 7d914774..1b062152 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1,5 +1,6 @@ from collections import namedtuple import logging +import math import random from BaseClasses import Region, RegionType, Shop, ShopType, Location @@ -394,7 +395,7 @@ def set_up_take_anys(world, player): entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, old_man_take_any, player) entrance.target = 0x58 - old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True, not world.shopsanity[player]) + old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True, not world.shopsanity[player], 32) world.shops[player].append(old_man_take_any.shop) sword = next((item for item in world.itempool if item.type == 'Sword' and item.player == player), None) @@ -418,7 +419,7 @@ def set_up_take_anys(world, player): entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, take_any, player) entrance.target = target - take_any.shop = Shop(take_any, room_id, take_any_type, 0xE3, True, not world.shopsanity[player]) + take_any.shop = Shop(take_any, room_id, take_any_type, 0xE3, True, not world.shopsanity[player], 33 + num*2) world.shops[player].append(take_any.shop) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0, create_location=world.shopsanity[player]) take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=world.shopsanity[player]) @@ -524,7 +525,7 @@ def customize_shops(world, player): if shop_name not in retro_shops: if item.name in repeatable_shop_items: max_repeat = 0 - if item.name in ['Bomb Upgrade (+5)', 'Arrow Upgrade (+5)']: + if item.name in ['Bomb Upgrade (+5)', 'Arrow Upgrade (+5)'] and item.player == player: if item.name == 'Bomb Upgrade (+5)': found_bomb_upgrade = True if item.name == 'Arrow Upgrade (+5)': @@ -536,8 +537,8 @@ def customize_shops(world, player): price = 120 if shop_name == 'Potion Shop' and item.name == 'Red Potion' else item.price if world.retro[player] and item.name == 'Single Arrow': price = 80 - # randomize price - shop.add_inventory(idx, item.name, randomize_price(price), max_repeat) + # randomize price + shop.add_inventory(idx, item.name, randomize_price(price), max_repeat, player=item.player) if item.name in cap_replacements and shop_name not in retro_shops: possible_replacements.append((shop, idx, location, item)) # randomize shopkeeper @@ -554,7 +555,7 @@ def customize_shops(world, player): shop, idx, loc, item = random.choice(choices) upgrade = ItemFactory('Bomb Upgrade (+5)', player) shop.add_inventory(idx, upgrade.name, randomize_price(upgrade.price), 6, - item.name, randomize_price(item.price)) + item.name, randomize_price(item.price), player=item.player) loc.item = upgrade upgrade.location = loc if not found_arrow_upgrade and len(possible_replacements) > 0: @@ -566,10 +567,11 @@ def customize_shops(world, player): shop, idx, loc, item = random.choice(choices) upgrade = ItemFactory('Arrow Upgrade (+5)', player) shop.add_inventory(idx, upgrade.name, randomize_price(upgrade.price), 6, - item.name, randomize_price(item.price)) + item.name, randomize_price(item.price), player=item.player) loc.item = upgrade upgrade.location = loc change_shop_items_to_rupees(world, player, shops_to_customize) + todays_discounts(world, player) def randomize_price(price): @@ -579,7 +581,13 @@ def randomize_price(price): max_price //= 5 return random.randint(0, max_price) * 5 + half_price else: - return price + if price <= 10: + return price + else: + half_price = int(math.ceil(half_price / 10.0)) * 10 + max_price = price - half_price + max_price //= 5 + return random.randint(0, max_price) * 5 + half_price def change_shop_items_to_rupees(world, player, shops): @@ -588,17 +596,38 @@ def change_shop_items_to_rupees(world, player, shops): if location.item.name in shop_transfer.keys() and location.parent_region.name not in shops: new_item = ItemFactory(shop_transfer[location.item.name], location.item.player) location.item = new_item + if location.parent_region.name == 'Capacity Upgrade' and location.item.name in cap_blacklist: + new_item = ItemFactory('Rupees (300)', location.item.player) + location.item = new_item + shop = world.get_region('Capacity Upgrade', player).shop + slot = shop_to_location_table['Capacity Upgrade'].index(location.name) + shop.add_inventory(slot, new_item.name, randomize_price(new_item.price), 1, player=new_item.player) + + +def todays_discounts(world, player): + locs = [] + for shop, locations in shop_to_location_table.items(): + for slot, loc in enumerate(locations): + locs.append((world.get_location(loc, player), shop, slot)) + discount_number = random.randint(4, 7) + chosen_locations = random.choices(locs, k=discount_number) + for location, shop_name, slot in chosen_locations: + shop = world.get_region(shop_name, player).shop + orig = location.item.price + shop.inventory[slot]['price'] = randomize_price(orig // 10) repeatable_shop_items = ['Single Arrow', 'Arrows (10)', 'Bombs (3)', 'Bombs (10)', 'Red Potion', 'Small Heart', - 'Blue Shield', 'Red Shield', 'Bee', 'Small Key (Universal)'] + 'Blue Shield', 'Red Shield', 'Bee', 'Small Key (Universal)', 'Blue Potion', 'Green Potion'] cap_replacements = ['Single Arrow', 'Arrows (10)', 'Bombs (3)', 'Bombs (10)'] +cap_blacklist = ['Green Potion', 'Red Potion', 'Blue Potion'] + shop_transfer = {'Red Potion': 'Rupees (100)', 'Bee': 'Rupees (5)', 'Blue Potion': 'Rupees (100)', - 'Blue Shield': 'Rupees (50)', 'Red Shield': 'Rupees (300)'} + 'Blue Shield': 'Rupees (50)', 'Red Shield': 'Rupees (300)', 'Green Potion': 'Rupees (50)'} def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, door_shuffle): diff --git a/Items.py b/Items.py index e5d91d4a..1ed849e1 100644 --- a/Items.py +++ b/Items.py @@ -23,43 +23,43 @@ def ItemFactory(items, player): # Format: Name: (Advancement, Priority, Type, ItemCode, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text) -item_table = {'Bow': (True, False, None, 0x0B, 300, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), - 'Progressive Bow': (True, False, None, 0x64, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), - 'Progressive Bow (Alt)': (True, False, None, 0x65, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), - 'Book of Mudora': (True, False, None, 0x1D, 200, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), - 'Hammer': (True, False, None, 0x09, 300, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), - 'Hookshot': (True, False, None, 0x0A, 300, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), - 'Magic Mirror': (True, False, None, 0x1A, 300, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), - 'Ocarina': (True, False, None, 0x14, 300, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), - 'Pegasus Boots': (True, False, None, 0x4B, 300, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), - 'Power Glove': (True, False, None, 0x1B, 150, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the glove'), - 'Cape': (True, False, None, 0x19, 100, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), - 'Mushroom': (True, False, None, 0x29, 100, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the mushroom'), - 'Shovel': (True, False, None, 0x13, 100, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the shovel'), - 'Lamp': (True, False, None, 0x12, 200, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the lamp'), - 'Magic Powder': (True, False, None, 0x0D, 100, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the powder'), - 'Moon Pearl': (True, False, None, 0x1F, 300, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the moon pearl'), - 'Cane of Somaria': (True, False, None, 0x15, 300, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red cane'), - 'Fire Rod': (True, False, None, 0x07, 300, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the fire rod'), - 'Flippers': (True, False, None, 0x1E, 300, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the flippers'), - 'Ice Rod': (True, False, None, 0x08, 300, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the ice rod'), - 'Titans Mitts': (True, False, None, 0x1C, 300, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the mitts'), - 'Bombos': (True, False, None, 0x0F, 200, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), - 'Ether': (True, False, None, 0x10, 200, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), - 'Quake': (True, False, None, 0x11, 200, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), - 'Bottle': (True, False, None, 0x16, 100, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a Bottle'), - 'Bottle (Red Potion)': (True, False, None, 0x2B, 130, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a Bottle'), - 'Bottle (Green Potion)': (True, False, None, 0x2C, 110, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a Bottle'), - 'Bottle (Blue Potion)': (True, False, None, 0x2D, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a Bottle'), - 'Bottle (Fairy)': (True, False, None, 0x3D, 150, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a Bottle'), - 'Bottle (Bee)': (True, False, None, 0x3C, 100, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a Bottle'), - 'Bottle (Good Bee)': (True, False, None, 0x48, 110, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a Bottle'), - 'Master Sword': (True, False, 'Sword', 0x50, 150, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), - 'Tempered Sword': (True, False, 'Sword', 0x02, 250, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), +item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), + 'Progressive Bow': (True, False, None, 0x64, 100, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), + 'Progressive Bow (Alt)': (True, False, None, 0x65, 100, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), + 'Book of Mudora': (True, False, None, 0x1D, 100, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), + 'Hammer': (True, False, None, 0x09, 200, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), + 'Hookshot': (True, False, None, 0x0A, 200, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), + 'Magic Mirror': (True, False, None, 0x1A, 200, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), + 'Ocarina': (True, False, None, 0x14, 200, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), + 'Pegasus Boots': (True, False, None, 0x4B, 200, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), + 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the glove'), + 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), + 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the mushroom'), + 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the shovel'), + 'Lamp': (True, False, None, 0x12, 100, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the lamp'), + 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the powder'), + 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the moon pearl'), + 'Cane of Somaria': (True, False, None, 0x15, 200, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red cane'), + 'Fire Rod': (True, False, None, 0x07, 200, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the fire rod'), + 'Flippers': (True, False, None, 0x1E, 200, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the flippers'), + 'Ice Rod': (True, False, None, 0x08, 200, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the ice rod'), + 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the mitts'), + 'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), + 'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), + 'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), + 'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a Bottle'), + 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a Bottle'), + 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a Bottle'), + 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a Bottle'), + 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a Bottle'), + 'Bottle (Bee)': (True, False, None, 0x3C, 50, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a Bottle'), + 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a Bottle'), + 'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), + 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the small sword'), - 'Golden Sword': (True, False, 'Sword', 0x03, 300, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), - 'Progressive Sword': (True, False, 'Sword', 0x5E, 200, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), - 'Progressive Glove': (True, False, None, 0x61, 200, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), + 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), + 'Progressive Sword': (True, False, 'Sword', 0x5E, 100, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), + 'Progressive Glove': (True, False, None, 0x61, 100, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01], 999, None, None, None, None, None, None, None), 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02], 999, None, None, None, None, None, None, None), @@ -83,20 +83,20 @@ item_table = {'Bow': (True, False, None, 0x0B, 300, 'You have\nchosen the\narche 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), - 'Blue Mail': (False, True, None, 0x22, 100, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the blue mail'), - 'Red Mail': (False, True, None, 0x23, 200, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the red mail'), - 'Progressive Armor': (False, True, None, 0x60, 100, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), - 'Blue Boomerang': (True, False, None, 0x0C, 100, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'), - 'Red Boomerang': (True, False, None, 0x2A, 100, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'), + 'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the blue mail'), + 'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the red mail'), + 'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), + 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'), + 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'), 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'the blue shield'), 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the red shield'), - 'Mirror Shield': (True, False, None, 0x06, 300, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the mirror shield'), - 'Progressive Shield': (True, False, None, 0x5F, 100, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), - 'Bug Catching Net': (True, False, None, 0x21, 100, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'), - 'Cane of Byrna': (True, False, None, 0x18, 100, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue cane'), - 'Boss Heart Container': (False, False, None, 0x3E, 80, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), - 'Sanctuary Heart Container': (False, False, None, 0x3F, 100, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), - 'Piece of Heart': (False, False, None, 0x17, 20, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), + 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the mirror shield'), + 'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), + 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'), + 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue cane'), + 'Boss Heart Container': (False, False, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), + 'Sanctuary Heart Container': (False, False, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), + 'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), 'Rupee (1)': (False, False, None, 0x34, 0, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a green rupee'), 'Rupees (5)': (False, False, None, 0x35, 2, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a blue rupee'), 'Rupees (20)': (False, False, None, 0x36, 10, 'Just couch\ncash. Move\nright along.', 'and the couch cash', 'the piggy-bank kid', 'life lesson for sale', 'the witch buying drugs', 'destitute boy has lunch again', 'a red rupee'), @@ -109,60 +109,60 @@ item_table = {'Bow': (True, False, None, 0x0B, 300, 'You have\nchosen the\narche 'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), 'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), 'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), - 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 100, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'half magic'), # can be required to beat mothula in an open seed in very very rare circumstance - 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 200, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'quarter magic'), # can be required to beat mothula in an open seed in very very rare circumstance - 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 50, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'), - 'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 100, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'), + 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'half magic'), # can be required to beat mothula in an open seed in very very rare circumstance + 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'quarter magic'), # can be required to beat mothula in an open seed in very very rare circumstance + 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 30, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'), + 'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 50, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'), 'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'Now you can find the Armos Knights!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Eastern Palace'), - 'Map (Eastern Palace)': (False, True, 'Map', 0x7D, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Eastern Palace'), - 'Small Key (Desert Palace)': (False, False, 'SmallKey', 0xA3, 50, 'A small key to the desert', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Desert Palace'), - 'Big Key (Desert Palace)': (False, False, 'BigKey', 0x9C, 100, 'A big key to the desert', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Desert Palace'), + 'Map (Eastern Palace)': (False, True, 'Map', 0x7D, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Eastern Palace'), + 'Small Key (Desert Palace)': (False, False, 'SmallKey', 0xA3, 30, 'A small key to the desert', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Desert Palace'), + 'Big Key (Desert Palace)': (False, False, 'BigKey', 0x9C, 50, 'A big key to the desert', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Desert Palace'), 'Compass (Desert Palace)': (False, True, 'Compass', 0x8C, 10, 'Now you can find Lanmolas!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Desert Palace'), - 'Map (Desert Palace)': (False, True, 'Map', 0x7C, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Desert Palace'), - 'Small Key (Tower of Hera)': (False, False, 'SmallKey', 0xAA, 50, 'A small key to Hera', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Tower of Hera'), - 'Big Key (Tower of Hera)': (False, False, 'BigKey', 0x95, 100, 'A big key to Hera', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Tower of Hera'), + 'Map (Desert Palace)': (False, True, 'Map', 0x7C, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Desert Palace'), + 'Small Key (Tower of Hera)': (False, False, 'SmallKey', 0xAA, 30, 'A small key to Hera', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Tower of Hera'), + 'Big Key (Tower of Hera)': (False, False, 'BigKey', 0x95, 50, 'A big key to Hera', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Tower of Hera'), 'Compass (Tower of Hera)': (False, True, 'Compass', 0x85, 10, 'Now you can find Moldorm!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Tower of Hera'), - 'Map (Tower of Hera)': (False, True, 'Map', 0x75, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Tower of Hera'), - 'Small Key (Escape)': (False, False, 'SmallKey', 0xA0, 50, 'A small key to the castle', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Hyrule Castle'), - 'Big Key (Escape)': (False, False, 'BigKey', 0x9F, 100, 'A big key to the castle', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Hyrule Castle'), + 'Map (Tower of Hera)': (False, True, 'Map', 0x75, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Tower of Hera'), + 'Small Key (Escape)': (False, False, 'SmallKey', 0xA0, 30, 'A small key to the castle', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Hyrule Castle'), + 'Big Key (Escape)': (False, False, 'BigKey', 0x9F, 50, 'A big key to the castle', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Hyrule Castle'), 'Compass (Escape)': (False, True, 'Compass', 0x8F, 10, 'Now you can find no boss!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Hyrule Castle'), - 'Map (Escape)': (False, True, 'Map', 0x7F, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Hyrule Castle'), - 'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 50, 'A small key to Agahnim', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Castle Tower'), - 'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 100, 'A big key to Agahnim', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'), + 'Map (Escape)': (False, True, 'Map', 0x7F, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Hyrule Castle'), + 'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 30, 'A small key to Agahnim', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Castle Tower'), + 'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 50, 'A big key to Agahnim', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'), 'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 10, 'Now you can find Aga1!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Castle Tower'), - 'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'), - 'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 50, 'A small key to darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'), - 'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 100, 'A big key to darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'), + 'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'), + 'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 30, 'A small key to darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'), + 'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 50, 'A big key to darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'), 'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 10, 'Now you can find Helmasaur King!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'), - 'Map (Palace of Darkness)': (False, True, 'Map', 0x79, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Palace of Darkness'), - 'Small Key (Thieves Town)': (False, False, 'SmallKey', 0xAB, 50, 'A small key to thievery', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Thieves\' Town'), - 'Big Key (Thieves Town)': (False, False, 'BigKey', 0x94, 100, 'A big key to thievery', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Thieves\' Town'), + 'Map (Palace of Darkness)': (False, True, 'Map', 0x79, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Palace of Darkness'), + 'Small Key (Thieves Town)': (False, False, 'SmallKey', 0xAB, 30, 'A small key to thievery', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Thieves\' Town'), + 'Big Key (Thieves Town)': (False, False, 'BigKey', 0x94, 50, 'A big key to thievery', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Thieves\' Town'), 'Compass (Thieves Town)': (False, True, 'Compass', 0x84, 10, 'Now you can find Blind!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Thieves\' Town'), - 'Map (Thieves Town)': (False, True, 'Map', 0x74, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Thieves\' Town'), - 'Small Key (Skull Woods)': (False, False, 'SmallKey', 0xA8, 50, 'A small key to the woods', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Skull Woods'), - 'Big Key (Skull Woods)': (False, False, 'BigKey', 0x97, 100, 'A big key to the woods', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Skull Woods'), + 'Map (Thieves Town)': (False, True, 'Map', 0x74, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Thieves\' Town'), + 'Small Key (Skull Woods)': (False, False, 'SmallKey', 0xA8, 30, 'A small key to the woods', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Skull Woods'), + 'Big Key (Skull Woods)': (False, False, 'BigKey', 0x97, 50, 'A big key to the woods', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Skull Woods'), 'Compass (Skull Woods)': (False, True, 'Compass', 0x87, 10, 'Now you can find Mothula!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Skull Woods'), - 'Map (Skull Woods)': (False, True, 'Map', 0x77, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Skull Woods'), - 'Small Key (Swamp Palace)': (False, False, 'SmallKey', 0xA5, 50, 'A small key to the swamp', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Swamp Palace'), - 'Big Key (Swamp Palace)': (False, False, 'BigKey', 0x9A, 100, 'A big key to the swamp', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Swamp Palace'), + 'Map (Skull Woods)': (False, True, 'Map', 0x77, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Skull Woods'), + 'Small Key (Swamp Palace)': (False, False, 'SmallKey', 0xA5, 30, 'A small key to the swamp', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Swamp Palace'), + 'Big Key (Swamp Palace)': (False, False, 'BigKey', 0x9A, 50, 'A big key to the swamp', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Swamp Palace'), 'Compass (Swamp Palace)': (False, True, 'Compass', 0x8A, 10, 'Now you can find Arrghus!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Swamp Palace'), - 'Map (Swamp Palace)': (False, True, 'Map', 0x7A, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Swamp Palace'), - 'Small Key (Ice Palace)': (False, False, 'SmallKey', 0xA9, 50, 'A small key to the iceberg', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ice Palace'), - 'Big Key (Ice Palace)': (False, False, 'BigKey', 0x96, 100, 'A big key to the iceberg', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ice Palace'), + 'Map (Swamp Palace)': (False, True, 'Map', 0x7A, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Swamp Palace'), + 'Small Key (Ice Palace)': (False, False, 'SmallKey', 0xA9, 30, 'A small key to the iceberg', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ice Palace'), + 'Big Key (Ice Palace)': (False, False, 'BigKey', 0x96, 50, 'A big key to the iceberg', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ice Palace'), 'Compass (Ice Palace)': (False, True, 'Compass', 0x86, 10, 'Now you can find Kholdstare!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Ice Palace'), - 'Map (Ice Palace)': (False, True, 'Map', 0x76, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ice Palace'), - 'Small Key (Misery Mire)': (False, False, 'SmallKey', 0xA7, 50, 'A small key to the mire', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Misery Mire'), - 'Big Key (Misery Mire)': (False, False, 'BigKey', 0x98, 100, 'A big key to the mire', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Misery Mire'), + 'Map (Ice Palace)': (False, True, 'Map', 0x76, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ice Palace'), + 'Small Key (Misery Mire)': (False, False, 'SmallKey', 0xA7, 30, 'A small key to the mire', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Misery Mire'), + 'Big Key (Misery Mire)': (False, False, 'BigKey', 0x98, 50, 'A big key to the mire', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Misery Mire'), 'Compass (Misery Mire)': (False, True, 'Compass', 0x88, 10, 'Now you can find Vitreous!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Misery Mire'), - 'Map (Misery Mire)': (False, True, 'Map', 0x78, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Misery Mire'), - 'Small Key (Turtle Rock)': (False, False, 'SmallKey', 0xAC, 50, 'A small key to the pipe maze', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Turtle Rock'), - 'Big Key (Turtle Rock)': (False, False, 'BigKey', 0x93, 100, 'A big key to the pipe maze', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Turtle Rock'), + 'Map (Misery Mire)': (False, True, 'Map', 0x78, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Misery Mire'), + 'Small Key (Turtle Rock)': (False, False, 'SmallKey', 0xAC, 30, 'A small key to the pipe maze', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Turtle Rock'), + 'Big Key (Turtle Rock)': (False, False, 'BigKey', 0x93, 50, 'A big key to the pipe maze', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Turtle Rock'), 'Compass (Turtle Rock)': (False, True, 'Compass', 0x83, 10, 'Now you can find Trinexx!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Turtle Rock'), - 'Map (Turtle Rock)': (False, True, 'Map', 0x73, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Turtle Rock'), - 'Small Key (Ganons Tower)': (False, False, 'SmallKey', 0xAD, 50, 'A small key to the evil tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ganon\'s Tower'), - 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 100, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ganon\'s Tower'), + 'Map (Turtle Rock)': (False, True, 'Map', 0x73, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Turtle Rock'), + 'Small Key (Ganons Tower)': (False, False, 'SmallKey', 0xAD, 30, 'A small key to the evil tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ganon\'s Tower'), + 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 50, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ganon\'s Tower'), 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 10, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a comapss to Ganon\'s Tower'), - 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 50, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ganon\'s Tower'), + 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ganon\'s Tower'), 'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key'), 'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'), 'Bee Trap': (False, False, None, 0xB0, 0, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'), diff --git a/Main.py b/Main.py index 5cf4c918..3b780520 100644 --- a/Main.py +++ b/Main.py @@ -21,7 +21,8 @@ from DoorShuffle import link_doors, connect_portal from RoomData import create_rooms from Rules import set_rules from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive -from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, sell_keys, balance_multiworld_progression +from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items +from Fill import sell_potions, sell_keys, balance_multiworld_progression from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names @@ -149,8 +150,10 @@ def main(args, seed=None, fish=None): set_rules(world, player) for player in range(1, world.players + 1): - if world.retro[player] and world.shopsanity[player]: - sell_keys(world, player) + if world.shopsanity[player]: + sell_potions(world, player) + if world.retro[player]: + sell_keys(world, player) logger.info(world.fish.translate("cli","cli","placing.dungeon.prizes")) @@ -507,7 +510,8 @@ def copy_dynamic_regions_and_locations(world, ret): # Note: ideally exits should be copied here, but the current use case (Take anys) do not require this if region.shop: - new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.type, region.shop.shopkeeper_config, region.shop.custom, region.shop.locked) + new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.type, region.shop.shopkeeper_config, + region.shop.custom, region.shop.locked, region.shop.sram_address) ret.shops[region.player].append(new_reg.shop) for location in world.dynamic_locations: diff --git a/MultiClient.py b/MultiClient.py index 15840488..66c66e82 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -52,6 +52,7 @@ class Context: self.awaiting_rom = False self.rom = None self.auth = None + self.total_locations = None def color_code(*args): codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, @@ -86,6 +87,12 @@ SCOUT_LOCATION_ADDR = SAVEDATA_START + 0x4D7 # 1 byte SCOUTREPLY_LOCATION_ADDR = SAVEDATA_START + 0x4D8 # 1 byte SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9 # 1 byte SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA # 1 byte +SHOP_ADDR = SAVEDATA_START + 0x302 # 2 bytes? +DYNAMIC_TOTAL_ADDR = SAVEDATA_START + 0x33E + +SHOP_SRAM_LEN = 0x29 # 41 tracked items +location_shop_order = [Regions.shop_to_location_table.keys()] + [Regions.retro_shops.keys()] +location_shop_ids = {0x0112, 0x0110, 0x010F, 0x00FF, 0x011F, 0x0109, 0x0115} location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10), "Blind's Hideout - Left": (0x11d, 0x20), @@ -800,11 +807,28 @@ def get_location_name_from_address(address): async def track_locations(ctx : Context, roomid, roomdata): new_locations = [] + if ctx.total_locations is None: + total_data = await snes_read(ctx, DYNAMIC_TOTAL_ADDR, 2) + ttl = total_data[0] | (total_data[1] << 8) + if ttl > 0: + ctx.total_locations = ttl + def new_check(location): ctx.locations_checked.add(location) - logging.info("New check: %s (%d/216)" % (location, len(ctx.locations_checked))) + logging.info(f"New check: {location} ({len(ctx.locations_checked)}/{ctx.total_locations})") new_locations.append(Regions.lookup_name_to_id[location]) + try: + if roomid in location_shop_ids: + misc_data = await snes_read(ctx, SHOP_ADDR, SHOP_SRAM_LEN) + for cnt, b in enumerate(misc_data): + my_check = Regions.shop_table_by_location_id[0x400000 + cnt] + if int(b) > 0 and my_check not in ctx.locations_checked: + new_check(my_check) + except Exception as e: + print(e) + logging.warning(e) + for location, (loc_roomid, loc_mask) in location_table_uw.items(): if location not in ctx.locations_checked and loc_roomid == roomid and (roomdata << 4) & loc_mask != 0: new_check(location) diff --git a/Regions.py b/Regions.py index 0d75c6d4..b7050259 100644 --- a/Regions.py +++ b/Regions.py @@ -84,7 +84,7 @@ def create_regions(world, player): create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']), create_cave_region(player, 'Library', 'the library', ['Library']), create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), - create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']), + create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop', 'Potion Shop - Left', 'Potion Shop - Middle', 'Potion Shop - Right']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), @@ -859,12 +859,12 @@ def mark_light_world_regions(world, player): def create_shops(world, player): world.shops[player] = [] - for region_name, (room_id, type, shopkeeper, custom, locked, inventory) in shop_table.items(): + for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram) in 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)] region = world.get_region(region_name, player) - shop = Shop(region, room_id, type, shopkeeper, custom, locked) + shop = Shop(region, room_id, type, shopkeeper, custom, locked, sram) region.shop = shop world.shops[player].append(shop) for index, item in enumerate(inventory): @@ -905,17 +905,20 @@ def adjust_locations(world, player): _basic_shop_defaults = [('Red Potion', 150), ('Small Heart', 10), ('Bombs (10)', 50)] _dark_world_shop_defaults = [('Red Potion', 150), ('Blue Shield', 50), ('Bombs (10)', 50)] shop_table = { - 'Cave Shop (Dark Death Mountain)': (0x0112, ShopType.Shop, 0xC1, False, False, _basic_shop_defaults), - 'Red Shield Shop': (0x0110, ShopType.Shop, 0xC1, False, False, [('Red Shield', 500), ('Bee', 10), ('Arrows (10)', 30)]), - 'Dark Lake Hylia Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults), - 'Dark World Lumberjack Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults), - 'Village of Outcasts Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults), - 'Dark World Potion Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults), - 'Light World Death Mountain Shop': (0x00FF, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults), - 'Kakariko Shop': (0x011F, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults), - 'Cave Shop (Lake Hylia)': (0x0112, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults), - 'Potion Shop': (0x0109, ShopType.Shop, 0xFF, False, True, [('Red Potion', 120), ('Green Potion', 60), ('Blue Potion', 160)]), - 'Capacity Upgrade': (0x0115, ShopType.UpgradeShop, 0x04, True, True, [('Bomb Upgrade (+5)', 100, 7), ('Arrow Upgrade (+5)', 100, 7)]) + 'Cave Shop (Dark Death Mountain)': (0x0112, ShopType.Shop, 0xC1, False, False, _basic_shop_defaults, 0), + 'Red Shield Shop': (0x0110, ShopType.Shop, 0xC1, False, False, + [('Red Shield', 500), ('Bee', 10), ('Arrows (10)', 30)], 3), + 'Dark Lake Hylia Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults, 6), + 'Dark World Lumberjack Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults, 9), + 'Village of Outcasts Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults, 12), + 'Dark World Potion Shop': (0x010F, ShopType.Shop, 0xC1, False, False, _dark_world_shop_defaults, 15), + 'Light World Death Mountain Shop': (0x00FF, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults, 18), + 'Kakariko Shop': (0x011F, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults, 21), + 'Cave Shop (Lake Hylia)': (0x0112, ShopType.Shop, 0xA0, False, False, _basic_shop_defaults, 24), + 'Potion Shop': (0x0109, ShopType.Shop, 0xFF, False, True, + [('Red Potion', 120), ('Green Potion', 60), ('Blue Potion', 160)], 27), + 'Capacity Upgrade': (0x0115, ShopType.UpgradeShop, 0x04, True, True, + [('Bomb Upgrade (+5)', 100, 7), ('Arrow Upgrade (+5)', 100, 7)], 30) } @@ -929,6 +932,7 @@ shop_to_location_table = { 'Light World Death Mountain Shop': ['Paradox Shop - Left', 'Paradox Shop - Middle', 'Paradox Shop - Right'], 'Kakariko Shop': ['Kakariko Shop - Left', 'Kakariko Shop - Middle', 'Kakariko Shop - Right'], 'Cave Shop (Lake Hylia)': ['Lake Hylia Shop - Left', 'Lake Hylia Shop - Middle', 'Lake Hylia Shop - Right'], + 'Potion Shop': ['Potion Shop - Left', 'Potion Shop - Middle', 'Potion Shop - Right'], 'Capacity Upgrade': ['Capacity Upgrade - Left', 'Capacity Upgrade - Right'], } @@ -940,6 +944,11 @@ retro_shops = { 'Take-Any #4': ['Take-Any #4 Item 1', 'Take-Any #4 Item 2'], } +flat_normal_shops = [loc_name for name, location_list in shop_to_location_table.items() for loc_name in location_list] +flat_retro_shops = [loc_name for name, location_list in retro_shops.items() for loc_name in location_list] +shop_table_by_location_id = {0x400000+cnt: x for cnt, x in enumerate(flat_normal_shops)} +shop_table_by_location_id = {**shop_table_by_location_id, **{0x400020+cnt: x for cnt, x in enumerate(flat_retro_shops)}} +shop_table_by_location = {y: x for x, y in shop_table_by_location_id.items()} key_drop_data = { 'Hyrule Castle - Map Guard Key Drop': [0x140036, 0x140037, 'in Hyrule Castle', 'Small Key (Escape)'], @@ -1263,10 +1272,15 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Dark Death Mountain Shop - Right': (None, None, False, 'for sale on the dark mountain'), 'Red Shield Shop - Left': (None, None, False, 'for sale as a curiosity'), 'Red Shield Shop - Middle': (None, None, False, 'for sale as a curiosity'), - 'Red Shield Shop - Right': (None, None, False, 'for sale as a curiosity') + 'Red Shield Shop - Right': (None, None, False, 'for sale as a curiosity'), + 'Potion Shop - Left': (None, None, False, 'for sale near the witch'), + 'Potion Shop - Middle': (None, None, False, 'for sale near the witch'), + 'Potion Shop - Right': (None, None, False, 'for sale near the witch'), } lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()}} +lookup_id_to_name.update(shop_table_by_location_id) lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()}} +lookup_name_to_id.update(shop_table_by_location) diff --git a/Rom.py b/Rom.py index 0e96f520..bb158850 100644 --- a/Rom.py +++ b/Rom.py @@ -27,7 +27,7 @@ from EntranceShuffle import door_addresses, exit_ids JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '30147375153cc57197805eddf38c2a23' +RANDOMIZERBASEHASH = '7f00024960d910e4490be397c5561e1e' class JsonRom(object): @@ -743,14 +743,24 @@ def patch_rom(world, rom, player, team, enemized): def credits_digit(num): # top: $54 is 1, 55 2, etc , so 57=4, 5C=9 # bot: $7A is 1, 7B is 2, etc so 7D=4, 82=9 (zero unknown...) - return 0x53+num, 0x79+num + return 0x53+int(num), 0x79+int(num) + + credits_total = 216 + if world.keydropshuffle[player]: + credits_total += 33 + if world.shopsanity[player]: + credits_total += 32 + if world.retro[player]: + credits_total += 9 if world.shopsanity[player] else 5 if world.keydropshuffle[player]: rom.write_byte(0x140000, 1) - rom.write_byte(0x187010, 249) # dynamic credits + + write_int16(rom, 0x187010, credits_total) # dynamic credits + if credits_total != 216: # collection rate address: 238C37 - mid_top, mid_bot = credits_digit(4) - last_top, last_bot = credits_digit(9) + mid_top, mid_bot = credits_digit((credits_total // 10) % 10) + last_top, last_bot = credits_digit(credits_total % 10) # top half rom.write_byte(0x118C53, mid_top) rom.write_byte(0x118C54, last_top) @@ -1506,24 +1516,27 @@ def write_custom_shops(rom, world, player): shop_data = bytearray() items_data = bytearray() - sram_offset = 0 for shop_id, shop in enumerate(shops): if shop_id == len(shops) - 1: shop_id = 0xFF bytes = shop.get_bytes() bytes[0] = shop_id - bytes[-1] = sram_offset - if shop.type == ShopType.TakeAny: - sram_offset += 1 - else: - sram_offset += shop.item_count + bytes[-1] = shop.sram_address shop_data.extend(bytes) - # [id][item][price-low][price-high][max][repl_id][repl_price-low][repl_price-high] - for item in shop.inventory: + # [id][item][price-low][price-high][max][repl_id][repl_price-low][repl_price-high][player][sram] + for index, item in enumerate(shop.inventory): if item is None: break - item_data = [shop_id, ItemFactory(item['item'], player).code] + int16_as_bytes(item['price']) + [item['max'], ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF] + int16_as_bytes(item['replacement_price']) + if world.shopsanity[player] or shop.type == ShopType.TakeAny: + slot = 0 if shop.type == ShopType.TakeAny else index + rom.write_byte(0x186560 + shop.sram_address + slot, 1) + item_id = ItemFactory(item['item'], player).code + price = int16_as_bytes(item['price']) + replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF + replace_price = int16_as_bytes(item['replacement_price']) + item_player = 0 if item['player'] == player else item['player'] + item_data = [shop_id, item_id] + price + [item['max'], replace] + replace_price + [item_player] items_data.extend(item_data) rom.write_bytes(0x184800, shop_data) diff --git a/asm/hudadditions.asm b/asm/hudadditions.asm index 9b78a0a4..94edd3d0 100644 --- a/asm/hudadditions.asm +++ b/asm/hudadditions.asm @@ -8,7 +8,7 @@ DrHudOverride: HudAdditions: { lda.l DRFlags : and #$0008 : beq ++ - lda $7EF423 : and #$00ff + lda $7EF423 jsr HudHexToDec4DigitCopy LDX.b $05 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+10 ; draw 100's digit LDX.b $06 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+12 ; draw 10's digit diff --git a/data/base2current.bps b/data/base2current.bps index 74ee4e9a0243b2269d24e8feeb57a51a99cf4a00..a78021b41948b971adfb2f134e573dfd72d09b2f 100644 GIT binary patch delta 15979 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