diff --git a/OoTClient.py b/OoTClient.py index c05151a28511..f940ce121410 100644 --- a/OoTClient.py +++ b/OoTClient.py @@ -179,6 +179,12 @@ async def parse_payload(payload: dict, ctx: OoTContext, force: bool): locations = payload['locations'] collectibles = payload['collectibles'] + # The Lua JSON library serializes an empty table into a list instead of a dict. Verify types for safety: + if isinstance(locations, list): + locations = {} + if isinstance(collectibles, list): + collectibles = {} + if ctx.location_table != locations or ctx.collectible_table != collectibles: ctx.location_table = locations ctx.collectible_table = collectibles @@ -293,10 +299,15 @@ async def patch_and_run_game(apz5_file): if not os.path.exists(rom_file_name): rom_file_name = Utils.user_path(rom_file_name) rom = Rom(rom_file_name) - apply_patch_file(rom, apz5_file, - sub_file=(os.path.basename(base_name) + '.zpf' - if zipfile.is_zipfile(apz5_file) - else None)) + + sub_file = None + if zipfile.is_zipfile(apz5_file): + for name in zipfile.ZipFile(apz5_file).namelist(): + if name.endswith('.zpf'): + sub_file = name + break + + apply_patch_file(rom, apz5_file, sub_file=sub_file) rom.write_to_file(decomp_path) os.chdir(data_path("Compress")) compress_rom_file(decomp_path, comp_path) diff --git a/worlds/oot/Hints.py b/worlds/oot/Hints.py index 35451f3d8f8d..4627abd00f25 100644 --- a/worlds/oot/Hints.py +++ b/worlds/oot/Hints.py @@ -15,7 +15,7 @@ from .HintList import getHint, getHintGroup, Hint, hintExclusions, \ misc_item_hint_table, misc_location_hint_table from .Messages import COLOR_MAP, update_message_by_id -from .TextBox import line_wrap +from .TextBox import line_wrap, character_table, rom_safe_text from .Utils import data_path, read_json @@ -149,11 +149,11 @@ def isRestrictedDungeonItem(dungeon, item): # Attach a player name to the item or location text. # If the associated player of the item/location and the world are the same, does nothing. -# Otherwise, attaches the object's player's name to the string. +# Otherwise, attaches the object's player's name to the string, calling rom_safe_text for foreign items/locations. def attach_name(text, hinted_object, world): if hinted_object.player == world.player: return text - return f"{text} for {world.multiworld.get_player_name(hinted_object.player)}" + return rom_safe_text(f"{world.multiworld.get_player_name(hinted_object.player)}'s {text}") def add_hint(world, groups, gossip_text, count, location=None, force_reachable=False): @@ -1144,7 +1144,7 @@ def buildMiscItemHints(world, messages): area = HintArea.at(location, use_alt_hint=data['use_alt_hint']).text(world.clearer_hints, world=None) else: area = location.name - text = data['default_item_text'].format(area=(player_text + area)) + text = data['default_item_text'].format(area=rom_safe_text(player_text + area)) elif 'default_item_fallback' in data: text = data['default_item_fallback'] else: @@ -1167,7 +1167,7 @@ def buildMiscLocationHints(world, messages): item_text = getHint(getItemGenericName(item), world.clearer_hints).text if item.player != world.player: item_text += f' for {world.multiworld.get_player_name(item.player)}' - text = data['location_text'].format(item=item_text) + text = data['location_text'].format(item=rom_safe_text(item_text)) update_message_by_id(messages, data['id'], str(GossipText(text, ['Green'], prefix='')), 0x23) diff --git a/worlds/oot/Options.py b/worlds/oot/Options.py index 35b477ae58a0..03f5346ceeed 100644 --- a/worlds/oot/Options.py +++ b/worlds/oot/Options.py @@ -1037,7 +1037,7 @@ class AdultTradeStart(OptionSet): "Pocket Cucco", "Cojiro", "Odd Mushroom", - "Poacher's Saw", + "Poachers Saw", "Broken Sword", "Prescription", "Eyeball Frog", diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py index c02512a7887d..ab1e75d1b997 100644 --- a/worlds/oot/Patches.py +++ b/worlds/oot/Patches.py @@ -25,7 +25,7 @@ from .SaveContext import SaveContext, Scenes, FlagType from .SceneFlags import get_alt_list_bytes, get_collectible_flag_table, get_collectible_flag_table_bytes, \ get_collectible_flag_addresses -from .TextBox import character_table, NORMAL_LINE_WIDTH +from .TextBox import character_table, NORMAL_LINE_WIDTH, rom_safe_text from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch from .Utils import __version__ @@ -2771,7 +2771,7 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F split_item_name[0] = create_fake_name(split_item_name[0]) if len(world.multiworld.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[] - description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], world.multiworld.get_player_name(location.item.player)) + description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], rom_safe_text(world.multiworld.get_player_name(location.item.player))) else: description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1]) purchase_text = '\x08%s %d Rupees\x09\x01%s\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (split_item_name[0], location.price, split_item_name[1]) @@ -2785,9 +2785,9 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F shop_item_name = create_fake_name(shop_item_name) if len(world.multiworld.worlds) > 1: - shop_item_name = ''.join(filter(lambda char: char in character_table, shop_item_name)) + shop_item_name = rom_safe_text(shop_item_name) do_line_break = sum(character_table[char] for char in f"{shop_item_name} {location.price} Rupees") > NORMAL_LINE_WIDTH - description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, world.multiworld.get_player_name(location.item.player)) + description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, rom_safe_text(world.multiworld.get_player_name(location.item.player))) else: description_text = '\x08\x05\x41%s %d Rupees\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (shop_item_name, location.price) purchase_text = '\x08%s %d Rupees\x09\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (shop_item_name, location.price) diff --git a/worlds/oot/TextBox.py b/worlds/oot/TextBox.py index 4ea99757d853..a9db47996299 100644 --- a/worlds/oot/TextBox.py +++ b/worlds/oot/TextBox.py @@ -367,3 +367,10 @@ def test_support_long_words(): print('"Support Long Words" test failed: Got ' + result + ', wanted ' + expected) else: print('"Support Long Words" test passed!') + + +# AP additions + +rom_safe_lambda = lambda c: c if c in character_table else '?' +def rom_safe_text(text): + return ''.join(map(rom_safe_lambda, text)) diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index cae67e1e65f2..c967dfe18204 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -118,7 +118,7 @@ class OOTWorld(World): data_version = 3 - required_client_version = (0, 3, 7) + required_client_version = (0, 4, 0) item_name_groups = { # internal groups