diff --git a/HintList.py b/HintList.py index fd5cfe69a4..39aea013d2 100644 --- a/HintList.py +++ b/HintList.py @@ -1871,55 +1871,81 @@ def rainbow_bridge_hint_kind(world: World) -> str: misc_location_hint_table: dict[str, dict[str, Any]] = { '10_skulltulas': { 'id': 0x9004, - 'hint_location': '10 Skulltulas Reward Hint', 'item_location': 'Kak 10 Gold Skulltula Reward', 'location_text': "Yeaaarrgh! I'm cursed!! Please save me by destroying \x05\x4110 Spiders of the Curse\x05\x40 and I will give you \x05\x42{item}\x05\x40.", 'location_fallback': "Yeaaarrgh! I'm cursed!!", }, '20_skulltulas': { 'id': 0x9005, - 'hint_location': '20 Skulltulas Reward Hint', 'item_location': 'Kak 20 Gold Skulltula Reward', 'location_text': "Yeaaarrgh! I'm cursed!! Please save me by destroying \x05\x4120 Spiders of the Curse\x05\x40 and I will give you \x05\x42{item}\x05\x40.", 'location_fallback': "Yeaaarrgh! I'm cursed!!", }, '30_skulltulas': { 'id': 0x9006, - 'hint_location': '30 Skulltulas Reward Hint', 'item_location': 'Kak 30 Gold Skulltula Reward', 'location_text': "Yeaaarrgh! I'm cursed!! Please save me by destroying \x05\x4130 Spiders of the Curse\x05\x40 and I will give you \x05\x42{item}\x05\x40.", 'location_fallback': "Yeaaarrgh! I'm cursed!!", }, '40_skulltulas': { 'id': 0x9007, - 'hint_location': '40 Skulltulas Reward Hint', 'item_location': 'Kak 40 Gold Skulltula Reward', 'location_text': "Yeaaarrgh! I'm cursed!! Please save me by destroying \x05\x4140 Spiders of the Curse\x05\x40 and I will give you \x05\x42{item}\x05\x40.", 'location_fallback': "Yeaaarrgh! I'm cursed!!", }, '50_skulltulas': { 'id': 0x9008, - 'hint_location': '50 Skulltulas Reward Hint', 'item_location': 'Kak 50 Gold Skulltula Reward', 'location_text': "Yeaaarrgh! I'm cursed!! Please save me by destroying \x05\x4150 Spiders of the Curse\x05\x40 and I will give you \x05\x42{item}\x05\x40.", 'location_fallback': "Yeaaarrgh! I'm cursed!!", }, 'frogs2': { 'id': 0x022E, - 'hint_location': 'ZR Frogs Ocarina Minigame Hint', 'item_location': 'ZR Frogs Ocarina Game', 'location_text': "Some frogs holding \x05\x42{item}\x05\x40 are looking at you from underwater...", 'location_fallback': "Some frogs are looking at you from underwater...", }, 'big_poes': { 'id': 0x70F5, - 'hint_location': 'Market 10 Big Poes Hint', 'item_location': 'Market 10 Big Poes', 'location_text': "\x08Hey, young man. What's happening \x01today? Do you want\x01\x05\x41{item}\x05\x40?\x04\x1AIf you earn \x05\x41{poe_points} points\x05\x40, you'll\x01be a happy man! Heh heh.\x04\x08Your card now has \x05\x45\x1E\x01 \x05\x40points.\x01Come back again!\x01Heh heh heh!\x02", 'location_fallback': "\x08Hey, young man. What's happening \x01today? If you have a \x05\x41Poe\x05\x40, I will \x01buy it.\x04\x1AIf you earn \x05\x41{poe_points} points\x05\x40, you'll\x01be a happy man! Heh heh.\x04\x08Your card now has \x05\x45\x1E\x01 \x05\x40points.\x01Come back again!\x01Heh heh heh!\x02", }, } +unique_merchant_hint_table: dict[str, dict[str, Any]] = { + 'bean_salesman': { + 'id': 0x405E, + 'item_location': 'ZR Magic Bean Salesman', + 'location_text': '\x1AChomp chomp chomp...We have...\x01\x05\x41{item}\x05\x40!\x04\x05\x41\x0860 Rupees\x05\x40 and it\'s yours!\x01Keyahahah!\x01\x1B\x05\x42Yes\x01No\x05\x40\x02', + 'location_fallback': '\x1AChomp chomp chomp...\x01We have... \x05\x41a mysterious item\x05\x40! \x01Do you want it...huh? Huh?\x04\x05\x41\x0860 Rupees\x05\x40 and it\'s yours!\x01Keyahahah!\x01\x1B\x05\x42Yes\x01No\x05\x40\x02', + }, + 'carpet_salesman': { + 'id': 0x6077, + 'item_location': 'Wasteland Bombchu Salesman', + 'location_text': '\x06\x41Well Come!\x04I am selling stuff, strange and \x01rare. Today\'s special is...\x01\x05\x41{item}\x05\x40!\x04How about \x05\x41200 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + 'location_fallback': '\x06\x41Well Come!\x04I am selling stuff, strange and \x01rare, from all over the world to \x01everybody.\x01Today\'s special is...\x04A mysterious item! \x01Intriguing! \x01I won\'t tell you what it is until \x01I see the money....\x04How about \x05\x41200 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + }, + 'medigoron': { + 'id': 0x304F, + 'item_location': 'GC Medigoron', + 'location_text': 'For 200 Rupees, how about buying...\x04\x05\x41{item}\x05\x40?\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + 'location_fallback': 'How about buying this cool item for \x01200 Rupees?\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + }, + 'granny': { + 'id': 0x500C, + 'item_location': 'Kak Granny Buy Blue Potion', + 'location_text': 'How about \x05\x41100 Rupees\x05\x40 for...\x04\x05\x41{item}\x05\x40?\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + 'location_fallback': 'Mysterious item! How about\x01\x05\x41100 Rupees\x05\x40?\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02', + }, + 'chest_game': { + 'id': 0x006D, + 'item_location': 'Market Treasure Chest Game Salesman', + 'location_text': 'I seem to have misplaced my\x01keys, but I have a fun item to\x01sell instead.\x01How about \x05\x4110 Rupees\x05\x40 for...\x04\x05\x41{item}\x05\x40?\x01\x1B\x05\x42Buy\x01Don\'t Buy\x05\x40\x02', + 'location_fallback': 'I seem to have misplaced my\x01keys, but I have a fun item to\x01sell instead.\x04How about \x05\x4110 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don\'t Buy\x05\x40\x02', + }, +} + # Separate table for goal names to avoid duplicates in the hint table. # Link's Pocket will always be an empty goal, but it's included here to # prevent key errors during the dungeon reward lookup. diff --git a/Hints.py b/Hints.py index 309cd44448..bab353fad7 100644 --- a/Hints.py +++ b/Hints.py @@ -13,7 +13,7 @@ from urllib.error import URLError, HTTPError from HintList import Hint, get_hint, get_multi, get_hint_group, get_upgrade_hint_list, hint_exclusions, \ - misc_item_hint_table, misc_location_hint_table + misc_item_hint_table, misc_location_hint_table, unique_merchant_hint_table from Item import Item, make_event_item from ItemList import REWARD_COLORS from Messages import Message, COLOR_MAP, update_message_by_id @@ -1318,9 +1318,16 @@ def build_gossip_hints(spoiler: Spoiler, worlds: list[World]) -> None: if item_world.id not in checked_locations: checked_locations[item_world.id] = set() checked_locations[item_world.id].add(location.name) - for hint_type in world.misc_hint_location_items.keys(): + for hint_type in world.misc_hint_location_items: location = world.get_location(misc_location_hint_table[hint_type]['item_location']) - if hint_type in world.settings.misc_hints and can_reach_hint(worlds, world.get_location(misc_location_hint_table[hint_type]['hint_location']), location): + if hint_type in world.settings.misc_hints and can_reach_hint(worlds, world.get_location(misc_location_hint_table[hint_type]['item_location'] + ' Hint'), location): + item_world = location.world + if item_world.id not in checked_locations: + checked_locations[item_world.id] = set() + checked_locations[item_world.id].add(location.name) + for hint_type in world.unique_merchant_items: + location = world.get_location(unique_merchant_hint_table[hint_type]['item_location']) + if 'unique_merchants' in world.settings.misc_hints and can_reach_hint(worlds, world.get_location(unique_merchant_hint_table[hint_type]['item_location'] + ' Hint'), location): item_world = location.world if item_world.id not in checked_locations: checked_locations[item_world.id] = set() @@ -1353,7 +1360,7 @@ def build_world_gossip_hints(spoiler: Spoiler, world: World, checked_locations: checked_locations = set() checked_always_locations = set() - stone_ids = list(gossipLocations.keys()) + stone_ids = list(gossipLocations) world.distribution.configure_gossip(spoiler, stone_ids) @@ -1825,8 +1832,10 @@ def build_misc_location_hints(world: World, messages: list[Message]) -> None: item = world.misc_hint_location_items[hint_type] poe_points = world.settings.big_poe_count * 100 if hint_type in world.settings.misc_hints: - text = data['location_text'].format(item=get_hint(get_item_generic_name(item), - world.settings.clearer_hints).text, poe_points=poe_points) + text = data['location_text'].format( + item=get_hint(get_item_generic_name(item), world.settings.clearer_hints).text, + poe_points=poe_points, + ) else: text = data['location_fallback'].format(poe_points=poe_points) update_message_by_id(messages, data['id'], text) @@ -1835,12 +1844,35 @@ def build_misc_location_hints(world: World, messages: list[Message]) -> None: if hint_type in world.settings.misc_hints: if hint_type in world.misc_hint_location_items: item = world.misc_hint_location_items[hint_type] - text = data['location_text'].format(item=get_hint(get_item_generic_name(item), - world.settings.clearer_hints).text) + text = data['location_text'].format( + item=get_hint(get_item_generic_name(item), world.settings.clearer_hints).text, + ) update_message_by_id(messages, data['id'], str(GossipText(text, ['Green'], prefix='')), 0x23) +def build_unique_merchants_hints(world: World, messages: list[Message]) -> None: + unique_merchants = { + 'bean_salesman': world.settings.shuffle_beans, + 'carpet_salesman': world.settings.shuffle_expensive_merchants, + 'medigoron': world.settings.shuffle_expensive_merchants, + 'granny': world.settings.shuffle_expensive_merchants, + 'chest_game': world.settings.shuffle_tcgkeys != 'vanilla', + } + + for hint_type, data in unique_merchant_hint_table.items(): + if unique_merchants[hint_type]: + if 'unique_merchants' in world.settings.misc_hints and hint_type in world.misc_hint_unique_merchant_locations: + item = world.misc_hint_unique_merchant_locations[hint_type].item + text = data['location_text'].format( + item=get_hint(get_item_generic_name(item), world.settings.clearer_hints).text, + ) + else: + text = data['location_fallback'] + + update_message_by_id(messages, data['id'], text) + + def get_raw_text(string: str) -> str: text = '' for char in string: diff --git a/Location.py b/Location.py index fdca55f29c..fc96b0d597 100644 --- a/Location.py +++ b/Location.py @@ -4,7 +4,7 @@ from enum import Enum from typing import TYPE_CHECKING, Optional, Any, overload -from HintList import misc_item_hint_table, misc_location_hint_table +from HintList import misc_item_hint_table, misc_location_hint_table, unique_merchant_hint_table from LocationList import location_table, location_is_viewable, LocationAddress, LocationDefault, LocationFilterTags if TYPE_CHECKING: @@ -140,11 +140,16 @@ def maybe_set_misc_hints(self) -> None: if hint_type not in self.item.world.misc_hint_item_locations and self.item.name == item: self.item.world.misc_hint_item_locations[hint_type] = self logging.getLogger('').debug(f'{item} [{self.item.world.id}] set to [{self.name}]') - for hint_type in misc_location_hint_table: - the_location = self.world.misc_hint_locations[hint_type] + for hint_type, data in misc_location_hint_table.items(): + the_location = data['item_location'] if hint_type not in self.world.misc_hint_location_items and self.name == the_location: self.world.misc_hint_location_items[hint_type] = self.item logging.getLogger('').debug(f'{the_location} [{self.world.id}] set to [{self.item.name}]') + for hint_type, data in unique_merchant_hint_table.items(): + the_location = data['item_location'] + if hint_type not in self.world.unique_merchant_items and self.name == the_location: + self.world.unique_merchant_items[hint_type] = self.item + logging.getLogger('').debug(f'{the_location} [{self.world.id}] set to [{self.item.name}]') def __str__(self) -> str: return self.name diff --git a/LocationList.py b/LocationList.py index 34635d50b7..fbd3d10578 100644 --- a/LocationList.py +++ b/LocationList.py @@ -2599,14 +2599,19 @@ def shop_address(shop_id: int, shelf_id: int) -> int: ("ToT Child Altar Hint", ("Hint", None, None, None, None, None)), ("ToT Adult Altar Hint", ("Hint", None, None, None, None, None)), ("Dampe Diary Hint", ("Hint", None, None, None, None, None)), - ("10 Skulltulas Reward Hint", ("Hint", None, None, None, None, None)), - ("20 Skulltulas Reward Hint", ("Hint", None, None, None, None, None)), - ("30 Skulltulas Reward Hint", ("Hint", None, None, None, None, None)), - ("40 Skulltulas Reward Hint", ("Hint", None, None, None, None, None)), - ("50 Skulltulas Reward Hint", ("Hint", None, None, None, None, None)), - ("ZR Frogs Ocarina Minigame Hint", ("Hint", None, None, None, None, None)), - ("Market 10 Big Poes Hint", ("Hint", None, None, None, None, None)), ("Ganondorf Hint", ("Hint", None, None, None, None, None)), + ("Kak 10 Gold Skulltula Reward Hint", ("Hint", None, None, None, None, None)), + ("Kak 20 Gold Skulltula Reward Hint", ("Hint", None, None, None, None, None)), + ("Kak 30 Gold Skulltula Reward Hint", ("Hint", None, None, None, None, None)), + ("Kak 40 Gold Skulltula Reward Hint", ("Hint", None, None, None, None, None)), + ("Kak 50 Gold Skulltula Reward Hint", ("Hint", None, None, None, None, None)), + ("ZR Frogs Ocarina Game Hint", ("Hint", None, None, None, None, None)), + ("Market 10 Big Poes Hint", ("Hint", None, None, None, None, None)), + ("ZR Magic Bean Salesman Hint", ("Hint", None, None, None, None, None)), + ("Wasteland Bombchu Salesman Hint", ("Hint", None, None, None, None, None)), + ("GC Medigoron Hint", ("Hint", None, None, None, None, None)), + ("Kak Granny Buy Blue Potion Hint", ("Hint", None, None, None, None, None)), + ("Market Treasure Chest Game Salesman Hint", ("Hint", None, None, None, None, None)), ]) location_sort_order: dict[str, int] = { diff --git a/Main.py b/Main.py index c9289bfb2f..f1635e75d3 100644 --- a/Main.py +++ b/Main.py @@ -186,7 +186,12 @@ def make_spoiler(settings: Settings, worlds: list[World]) -> Spoiler: logger.info('Calculating hint data.') update_goal_items(spoiler) build_gossip_hints(spoiler, worlds) - elif any(world.dungeon_rewards_hinted for world in worlds) or any(hint_type in settings.misc_hints for hint_type in misc_item_hint_table) or any(hint_type in settings.misc_hints for hint_type in misc_location_hint_table): + elif ( + any(world.dungeon_rewards_hinted for world in worlds) + or any(hint_type in settings.misc_hints for hint_type in misc_item_hint_table) + or any(hint_type in settings.misc_hints for hint_type in misc_location_hint_table) + or 'unique_merchants' in settings.misc_hints + ): spoiler.find_misc_hint_items() spoiler.build_file_hash() spoiler.build_password(settings.password_lock) diff --git a/Messages.py b/Messages.py index 499e379a18..e0a91df562 100644 --- a/Messages.py +++ b/Messages.py @@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable from typing import TYPE_CHECKING, Optional, Any -from HintList import misc_item_hint_table, misc_location_hint_table +from HintList import misc_item_hint_table, misc_location_hint_table, unique_merchant_hint_table from TextBox import line_wrap from Utils import find_last @@ -1303,6 +1303,7 @@ def shuffle_messages(messages: list[Message], except_hints: bool = True) -> list GOSSIP_STONE_MESSAGES + TEMPLE_HINTS_MESSAGES + [data['id'] for data in misc_item_hint_table.values()] + [data['id'] for data in misc_location_hint_table.values()] + + [data['id'] for data in unique_merchant_hint_table.values()] + [message_id for (message_id, message) in KEYSANITY_MESSAGES] + shuffle_messages.shop_item_messages + shuffle_messages.scrubs_message_ids + [0x5036, 0x70F5] # Chicken count and poe count respectively @@ -1315,6 +1316,7 @@ def is_exempt(m: Message) -> bool: GOSSIP_STONE_MESSAGES + TEMPLE_HINTS_MESSAGES + [data['id'] for data in misc_item_hint_table.values()] + [data['id'] for data in misc_location_hint_table.values()] + + [data['id'] for data in unique_merchant_hint_table.values()] + [message_id for (message_id, message) in KEYSANITY_MESSAGES] + shuffle_messages.shop_item_messages + shuffle_messages.scrubs_message_ids + diff --git a/Patches.py b/Patches.py index 28ff0cd3db..71b81deed0 100644 --- a/Patches.py +++ b/Patches.py @@ -13,7 +13,8 @@ from Entrance import Entrance from HintList import get_hint from Hints import GossipText, HintArea, write_gossip_stone_hints, build_altar_hints, \ - build_ganon_text, build_misc_item_hints, build_misc_location_hints, get_simple_hint_no_prefix, get_item_generic_name + build_ganon_text, build_misc_item_hints, build_misc_location_hints, get_simple_hint_no_prefix, \ + get_item_generic_name, build_unique_merchants_hints from Item import Item from ItemList import REWARD_COLORS from ItemPool import reward_list, song_list, trade_items, child_trade_items @@ -1432,6 +1433,7 @@ def calculate_traded_flags(world): # build misc. location hints build_misc_location_hints(world, messages) + build_unique_merchants_hints(world, messages) if 'mask_shop' in world.settings.misc_hints: rom.write_int32(rom.sym('CFG_MASK_SHOP_HINT'), 1) @@ -1760,16 +1762,6 @@ def update_scrub_text(message: bytearray, text_replacement: list[str], default_p if world.settings.shuffle_beans: rom.write_byte(rom.sym('SHUFFLE_BEANS'), 0x01) # Update bean salesman messages to better fit the fact that he sells a randomized item - if 'unique_merchants' not in world.settings.misc_hints: - update_message_by_id(messages, 0x405E, "\x1AChomp chomp chomp...\x01We have... \x05\x41a mysterious item\x05\x40! \x01Do you want it...huh? Huh?\x04\x05\x41\x0860 Rupees\x05\x40 and it's yours!\x01Keyahahah!\x01\x1B\x05\x42Yes\x01No\x05\x40\x02") - else: - location = world.get_location("ZR Magic Bean Salesman") - item_text = get_hint(get_item_generic_name(location.item), True).text - wrapped_item_text = line_wrap(item_text, False, False, False) - if wrapped_item_text != item_text: - update_message_by_id(messages, 0x405E, "\x1AChomp chomp chomp...We have...\x01\x05\x41" + wrapped_item_text + "\x05\x40!\x04\x05\x41\x0860 Rupees\x05\x40 and it's yours!\x01Keyahahah!\x01\x1B\x05\x42Yes\x01No\x05\x40\x02") - else: - update_message_by_id(messages, 0x405E, "\x1AChomp chomp chomp...We have...\x01\x05\x41" + item_text + "\x05\x40! \x01Do you want it...huh? Huh?\x04\x05\x41\x0860 Rupees\x05\x40 and it's yours!\x01Keyahahah!\x01\x1B\x05\x42Yes\x01No\x05\x40\x02") update_message_by_id(messages, 0x4069, "You don't have enough money.\x01I can't sell it to you.\x01Chomp chomp...\x02") update_message_by_id(messages, 0x406C, "We hope you like it!\x01Chomp chomp chomp.\x02") # Change first magic bean to cost 60 (is used as the price for the one time item when beans are shuffled) @@ -1778,44 +1770,14 @@ def update_scrub_text(message: bytearray, text_replacement: list[str], default_p if world.settings.shuffle_expensive_merchants: rom.write_byte(rom.sym('SHUFFLE_CARPET_SALESMAN'), 0x01) # Update carpet salesman messages to better fit the fact that he sells a randomized item - if 'unique_merchants' not in world.settings.misc_hints: - update_message_by_id(messages, 0x6077, "\x06\x41Well Come!\x04I am selling stuff, strange and \x01rare, from all over the world to \x01everybody.\x01Today's special is...\x04A mysterious item! \x01Intriguing! \x01I won't tell you what it is until \x01I see the money....\x04How about \x05\x41200 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - location = world.get_location("Wasteland Bombchu Salesman") - item_text = get_hint(get_item_generic_name(location.item), True).text - wrapped_item_text = line_wrap(item_text, False, False, False) - if wrapped_item_text != item_text: - update_message_by_id(messages, 0x6077, "\x06\x41Well Come!\x04I am selling stuff, strange and \x01rare. Today's special is...\x01\x05\x41"+ wrapped_item_text + "\x05\x40!\x04How about \x05\x41200 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - update_message_by_id(messages, 0x6077, "\x06\x41Well Come!\x04I am selling stuff, strange and \x01rare, from all over the world to \x01everybody. Today's special is...\x01\x05\x41"+ wrapped_item_text + "\x05\x40! \x01\x04How about \x05\x41200 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") update_message_by_id(messages, 0x6078, "Thank you very much!\x04The mark that will lead you to\x01the Spirit Temple is the \x05\x41flag on\x01the left \x05\x40outside the shop.\x01Be seeing you!\x02") rom.write_byte(rom.sym('SHUFFLE_MEDIGORON'), 0x01) # Update medigoron messages to better fit the fact that he sells a randomized item update_message_by_id(messages, 0x304C, "I have something cool right here.\x01How about it...\x07\x30\x4F\x02") update_message_by_id(messages, 0x304D, "How do you like it?\x02") - if 'unique_merchants' not in world.settings.misc_hints: - update_message_by_id(messages, 0x304F, "How about buying this cool item for \x01200 Rupees?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - location = world.get_location("GC Medigoron") - item_text = get_hint(get_item_generic_name(location.item), True).text - wrapped_item_text = line_wrap(item_text, False, False, False) - if wrapped_item_text != item_text: - update_message_by_id(messages, 0x304F, "For 200 Rupees, how about buying...\x04\x05\x41" + wrapped_item_text + "\x05\x40?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - update_message_by_id(messages, 0x304F, "For 200 Rupees, how about buying \x01\x05\x41" + item_text + "\x05\x40?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") rom.write_byte(rom.sym('SHUFFLE_GRANNYS_POTION_SHOP'), 0x01) - if 'unique_merchants' not in world.settings.misc_hints: - update_message_by_id(messages, 0x500C, "Mysterious item! How about\x01\x05\x41100 Rupees\x05\x40?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - location = world.get_location("Kak Granny Buy Blue Potion") - item_text = get_hint(get_item_generic_name(location.item), True).text - wrapped_item_text = line_wrap(item_text, False, False, False) - if wrapped_item_text != item_text: - update_message_by_id(messages, 0x500C, "How about \x05\x41100 Rupees\x05\x40 for...\x04\x05\x41"+ wrapped_item_text +"\x05\x40?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") - else: - update_message_by_id(messages, 0x500C, "How about \x05\x41100 Rupees\x05\x40 for\x01\x05\x41"+ item_text +"\x05\x40?\x01\x1B\x05\x42Buy\x01Don't buy\x05\x40\x02") new_message = "All right. You don't have to play\x01if you don't want to.\x0B\x02" update_message_by_id(messages, 0x908B, new_message, 0x00) @@ -1825,16 +1787,6 @@ def update_scrub_text(message: bytearray, text_replacement: list[str], default_p else: rom.write_byte(rom.sym('SHUFFLE_CHEST_GAME'), 0x01) # Update Chest Game Salesman to better fit the fact he sells a randomized item - if 'unique_merchants' not in world.settings.misc_hints: - update_message_by_id(messages, 0x6D, "I seem to have misplaced my\x01keys, but I have a fun item to\x01sell instead.\x04How about \x05\x4110 Rupees\x05\x40?\x01\x01\x1B\x05\x42Buy\x01Don't Buy\x05\x40\x02") - else: - location = world.get_location("Market Treasure Chest Game Salesman") - item_text = get_hint(get_item_generic_name(location.item), True).text - wrapped_item_text = line_wrap(item_text, False, False, False) - if wrapped_item_text != item_text: - update_message_by_id(messages, 0x6D, "I seem to have misplaced my\x01keys, but I have a fun item to\x01sell instead.\x01How about \x05\x4110 Rupees\x05\x40 for...\x04\x05\x41" + wrapped_item_text + "\x05\x40?\x01\x1B\x05\x42Buy\x01Don't Buy\x05\x40\x02") - else: - update_message_by_id(messages, 0x6D, "I seem to have misplaced my\x01keys, but I have a fun item to\x01sell instead.\x04How about \x05\x4110 Rupees\x05\x40 for\x01\x05\x41" + item_text + "\x05\x40?\x01\x1B\x05\x42Buy\x01Don't Buy\x05\x40\x02") update_message_by_id(messages, 0x908B, "That's OK!\x01More fun for me.\x0B\x02", 0x00, allow_duplicates=True) update_message_by_id(messages, 0x6E, "Wait, that room was off limits!\x02") update_message_by_id(messages, 0x704C, "I hope you like it!\x02") diff --git a/World.py b/World.py index 759c30de46..1f7a55023b 100644 --- a/World.py +++ b/World.py @@ -11,7 +11,7 @@ from Dungeon import Dungeon from Entrance import Entrance from Goals import Goal, GoalCategory -from HintList import get_required_hints, misc_item_hint_table, misc_location_hint_table +from HintList import get_required_hints, misc_item_hint_table from Hints import HintArea, hint_dist_keys, hint_dist_files from Item import Item, ItemFactory, ItemInfo, make_event_item from ItemList import REWARD_COLORS @@ -50,6 +50,7 @@ def __init__(self, world_id: int, settings: Settings, resolve_randomized_setting } self.misc_hint_item_locations: dict[str, Location] = {} self.misc_hint_location_items: dict[str, Item] = {} + self.unique_merchant_items: dict[str, Item] = {} self.triforce_count: int = 0 self.total_starting_triforce_count: int = 0 self.empty_areas: dict[HintArea, dict[str, Any]] = {} @@ -262,7 +263,6 @@ def __missing__(self, dungeon_name: str) -> EmptyDungeonInfo: self.dungeon_rewards_hinted: bool = settings.shuffle_mapcompass != 'remove' if settings.enhance_map_compass else 'altar' in settings.misc_hints self.misc_hint_items: dict[str, str] = {hint_type: self.hint_dist_user.get('misc_hint_items', {}).get(hint_type, data['default_item']) for hint_type, data in misc_item_hint_table.items()} - self.misc_hint_locations: dict[str, str] = {hint_type: self.hint_dist_user.get('misc_hint_locations', {}).get(hint_type, data['item_location']) for hint_type, data in misc_location_hint_table.items()} self.state: State = State(self) diff --git a/data/Glitched World/Overworld.json b/data/Glitched World/Overworld.json index 2eb841b403..1a2e6341af 100644 --- a/data/Glitched World/Overworld.json +++ b/data/Glitched World/Overworld.json @@ -361,6 +361,7 @@ "locations": { "Wasteland Chest": "has_fire_source", "Wasteland Bombchu Salesman": "Progressive_Wallet and can_jumpslash", + "Wasteland Bombchu Salesman Hint": "can_jumpslash", "Wasteland GS": "can_use(Boomerang) or (is_adult and (Progressive_Hookshot or (Bombs and can_shield)))" }, "exits": { @@ -620,6 +621,7 @@ "region_name": "Market Treasure Chest Game", "locations": { "Market Treasure Chest Game Salesman": "True", + "Market Treasure Chest Game Salesman Hint": "True", "Market Treasure Chest Game Room 1 Bottom": "(Small_Key_Treasure_Chest_Game, 1) and (shuffle_tcgkeys != 'vanilla' or can_use(Lens_of_Truth))", "Market Treasure Chest Game Room 1 Top": "(Small_Key_Treasure_Chest_Game, 1) and (shuffle_tcgkeys != 'vanilla' or can_use(Lens_of_Truth))", "Market Treasure Chest Game Room 2 Bottom": "(Small_Key_Treasure_Chest_Game, 2) and (shuffle_tcgkeys != 'vanilla' or can_use(Lens_of_Truth))", @@ -712,15 +714,15 @@ "region_name": "Kak House of Skulltula", "locations": { "Kak 10 Gold Skulltula Reward": "(Gold_Skulltula_Token, 10)", + "Kak 10 Gold Skulltula Reward Hint": "True", "Kak 20 Gold Skulltula Reward": "(Gold_Skulltula_Token, 20)", + "Kak 20 Gold Skulltula Reward Hint": "True", "Kak 30 Gold Skulltula Reward": "(Gold_Skulltula_Token, 30)", + "Kak 30 Gold Skulltula Reward Hint": "True", "Kak 40 Gold Skulltula Reward": "(Gold_Skulltula_Token, 40)", + "Kak 40 Gold Skulltula Reward Hint": "True", "Kak 50 Gold Skulltula Reward": "(Gold_Skulltula_Token, 50)", - "10 Skulltulas Reward Hint": "True", - "20 Skulltulas Reward Hint": "True", - "30 Skulltulas Reward Hint": "True", - "40 Skulltulas Reward Hint": "True", - "50 Skulltulas Reward Hint": "True" + "Kak 50 Gold Skulltula Reward Hint": "True" } }, { @@ -789,6 +791,9 @@ # allow any of the later sequence items to satisfy logic. The patcher # sets the flag needed for her to sell stuff if odd mushroom can't be obtained. "Kak Granny Buy Blue Potion": "Progressive_Wallet and is_adult and + (Odd_Mushroom or ((Odd_Potion or Poachers_Saw or Broken_Sword or + Prescription or Eyeball_Frog or Eyedrops or Claim_Check) and not adult_trade_shuffle))", + "Kak Granny Buy Blue Potion Hint": "is_adult and (Odd_Mushroom or ((Odd_Potion or Poachers_Saw or Broken_Sword or Prescription or Eyeball_Frog or Eyedrops or Claim_Check) and not adult_trade_shuffle))" } @@ -947,6 +952,9 @@ "GC Medigoron": " is_adult and Progressive_Wallet and (can_blast_or_smash or Progressive_Strength_Upgrade or Blue_Fire)", + "GC Medigoron Hint": " + is_adult and + (can_blast_or_smash or Progressive_Strength_Upgrade or Blue_Fire)", "GC GS Boulder Maze": "is_child and has_explosives", "GC GS Center Platform": "is_adult", "GC Maze Gossip Stone": " @@ -1091,10 +1099,12 @@ "time_passes": true, "locations": { "ZR Magic Bean Salesman": "is_child", + "ZR Magic Bean Salesman Hint": "is_child", "ZR Frogs Ocarina Game": " is_child and can_play(Zeldas_Lullaby) and can_play(Sarias_Song) and can_play(Suns_Song) and can_play(Eponas_Song) and can_play(Song_of_Time) and can_play(Song_of_Storms)", + "ZR Frogs Ocarina Game Hint": "is_child", "ZR Frogs Zeldas Lullaby": "is_child and can_play(Zeldas_Lullaby)", "ZR Frogs Eponas Song": "is_child and can_play(Eponas_Song)", "ZR Frogs Sarias Song": "is_child and can_play(Sarias_Song)", @@ -1107,8 +1117,7 @@ "ZR GS Near Raised Grottos": "is_adult and (Progressive_Hookshot or can_hover) and at_night", "ZR GS Above Bridge": "can_use(Hookshot) and at_night", "ZR Near Grottos Gossip Stone": "True", - "ZR Near Domain Gossip Stone": "True", - "ZR Frogs Ocarina Minigame Hint": "True" + "ZR Near Domain Gossip Stone": "True" }, "exits": { "ZR Front": "True", diff --git a/data/World/Overworld.json b/data/World/Overworld.json index b7e0aebed5..bab2c05a11 100644 --- a/data/World/Overworld.json +++ b/data/World/Overworld.json @@ -1111,6 +1111,7 @@ "Wasteland Bombchu Salesman": " Progressive_Wallet and can_jumpslash and (shuffle_expensive_merchants or not free_bombchu_drops or has_bombchus)", + "Wasteland Bombchu Salesman Hint": "can_jumpslash", "Wasteland Near GS Pot 1": "True", "Wasteland Near GS Pot 2": "True", "Wasteland Near GS Pot 3": "True", @@ -1620,6 +1621,7 @@ "scene": "Market Treasure Chest Game", "locations": { "Market Treasure Chest Game Salesman": "True", + "Market Treasure Chest Game Salesman Hint": "True", "Market Treasure Chest Game Room 1 Bottom": " (Small_Key_Treasure_Chest_Game, 1) and (shuffle_tcgkeys != 'vanilla' or can_use(Lens_of_Truth))", "Market Treasure Chest Game Room 1 Top": " @@ -1826,15 +1828,15 @@ "scene": "Kak House of Skulltula", "locations": { "Kak 10 Gold Skulltula Reward": "(Gold_Skulltula_Token, 10)", + "Kak 10 Gold Skulltula Reward Hint": "True", "Kak 20 Gold Skulltula Reward": "(Gold_Skulltula_Token, 20)", + "Kak 20 Gold Skulltula Reward Hint": "True", "Kak 30 Gold Skulltula Reward": "(Gold_Skulltula_Token, 30)", + "Kak 30 Gold Skulltula Reward Hint": "True", "Kak 40 Gold Skulltula Reward": "(Gold_Skulltula_Token, 40)", + "Kak 40 Gold Skulltula Reward Hint": "True", "Kak 50 Gold Skulltula Reward": "(Gold_Skulltula_Token, 50)", - "10 Skulltulas Reward Hint": "True", - "20 Skulltulas Reward Hint": "True", - "30 Skulltulas Reward Hint": "True", - "40 Skulltulas Reward Hint": "True", - "50 Skulltulas Reward Hint": "True" + "Kak 50 Gold Skulltula Reward Hint": "True" }, "exits": { "Kakariko Village": "True" @@ -1953,7 +1955,12 @@ ((selected_adult_trade_item == 'Odd Potion' or selected_adult_trade_item == 'Poachers Saw' or selected_adult_trade_item == 'Broken Sword' or selected_adult_trade_item == 'Prescription' or selected_adult_trade_item == 'Eyeball Frog' or selected_adult_trade_item == 'Eyedrops' or - selected_adult_trade_item == 'Claim Check') and not adult_trade_shuffle))" + selected_adult_trade_item == 'Claim Check') and not adult_trade_shuffle))", + "Kak Granny Buy Blue Potion Hint": "'Odd Potion Access' or + ((selected_adult_trade_item == 'Odd Potion' or selected_adult_trade_item == 'Poachers Saw' or + selected_adult_trade_item == 'Broken Sword' or selected_adult_trade_item == 'Prescription' or + selected_adult_trade_item == 'Eyeball Frog' or selected_adult_trade_item == 'Eyedrops' or + selected_adult_trade_item == 'Claim Check') and not adult_trade_shuffle)" }, "exits": { "Kak Backyard": "True" @@ -2195,6 +2202,7 @@ "GC Medigoron": " is_adult and Progressive_Wallet and (can_blast_or_smash or Progressive_Strength_Upgrade)", + "GC Medigoron Hint": "is_adult and (can_blast_or_smash or Progressive_Strength_Upgrade)", "GC Rolling Goron as Adult": "'Stop GC Rolling Goron as Adult'", "GC Lower Staircase Pot 1": "True", "GC Lower Staircase Pot 2": "True", @@ -2501,10 +2509,12 @@ "time_passes": true, "locations": { "ZR Magic Bean Salesman": "is_child", + "ZR Magic Bean Salesman Hint": "is_child", "ZR Frogs Ocarina Game": " is_child and can_play(Zeldas_Lullaby) and can_play(Eponas_Song) and can_play(Sarias_Song) and can_play(Suns_Song) and can_play(Song_of_Time) and can_play(Song_of_Storms) and Ocarina_A_Button and Ocarina_C_left_Button and Ocarina_C_right_Button and Ocarina_C_down_Button", + "ZR Frogs Ocarina Game Hint": "is_child", "ZR Frogs Zeldas Lullaby": "is_child and can_play(Zeldas_Lullaby)", "ZR Frogs Eponas Song": "is_child and can_play(Eponas_Song)", "ZR Frogs Sarias Song": "is_child and can_play(Sarias_Song)", @@ -2555,8 +2565,7 @@ "Butterfly Fairy": "can_use(Sticks) and has_bottle", "Bug Shrub": " (is_child or here(can_plant_bean) or Hover_Boots or logic_zora_river_lower) and - can_cut_shrubs and has_bottle", - "ZR Frogs Ocarina Minigame Hint": "True" + can_cut_shrubs and has_bottle" }, "exits": { "ZR Front": "True",