diff --git a/worlds/tmc/Client.py b/worlds/tmc/Client.py index 640dc034d367..395d1b667f6b 100644 --- a/worlds/tmc/Client.py +++ b/worlds/tmc/Client.py @@ -4,7 +4,9 @@ from NetUtils import ClientStatus import worlds._bizhawk as bizhawk +import Utils from worlds._bizhawk.client import BizHawkClient +from Options import Toggle from .Locations import all_locations, LocationData, events from .Items import items_by_id @@ -120,6 +122,20 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None: if ctx.server is None or ctx.server.socket.closed or ctx.slot_data is None: return + if ctx.slot_data["remote_items"] == Toggle.option_true and not ctx.items_handling & 0b010: + ctx.items_handling = 0b011 + Utils.async_start(ctx.send_msgs([{ + "cmd": "ConnectUpdate", + "items_handling": ctx.items_handling + }])) + + # Need to make sure items handling updates and we get the correct list of received items + # before continuing. Otherwise we might give some duplicate items and skip others. + # Should patch remote_items option value into the ROM in the future to guarantee we get the + # right item list before entering this part of the code + await asyncio.sleep(0.75) + return + try: if ctx.seed_name is None: return diff --git a/worlds/tmc/Options.py b/worlds/tmc/Options.py index f5a35def87fa..927ca0e48b66 100644 --- a/worlds/tmc/Options.py +++ b/worlds/tmc/Options.py @@ -163,6 +163,14 @@ class DeathLinkGameover(Toggle): """ display_name = "Deathlink is Gameover" +class RemoteItems(Toggle): + """ + Should all randomized items be handled through AP? This will require a connection to a server during all gameplay. + If you crash or lose your savefile, all items should be sent back to you. + When finding items placed in world, your items will use the remote item sprite instead of their usual sprite. + """ + display_name = "Remote Items" + @dataclass class MinishCapOptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool @@ -182,6 +190,7 @@ class MinishCapOptions(PerGameCommonOptions): dungeon_big_keys: BigKeys dungeon_maps: DungeonMaps dungeon_compasses: DungeonCompasses + remote_items: RemoteItems def get_option_data(options: MinishCapOptions): """ diff --git a/worlds/tmc/Rom.py b/worlds/tmc/Rom.py index ecc05cccb209..40f7eba6c46e 100644 --- a/worlds/tmc/Rom.py +++ b/worlds/tmc/Rom.py @@ -35,6 +35,14 @@ def write_tokens(world: "MinishCapWorld", patch: MinishCapProcedurePatch) -> Non # Bake seed name into ROM patch.write_token(APTokenTypes.WRITE, 0x000620, world.multiworld.seed_name.encode("UTF-8")) + if world.options.remote_items.value: + # Write remote items flag, causes the remote item pickup to be skipped. + # Otherwise it would cause the pickup animation will play twice, once for the remote item, then the actual item. + patch.write_token(APTokenTypes.WRITE, 0x000710, bytes([0x01])) + # Skip chest opening delay, required otherwise the player's input is awkwardly locked in front of chests with + # remote items in them... all of them + patch.write_token(APTokenTypes.WRITE, 0x0A74E2, bytes([0x00, 0x20, 0x00, 0x20])) + # Patch Items into Locations for location_name, loc in location_table_by_name.items(): if loc.rom_addr is None: @@ -58,7 +66,7 @@ def item_inject(world: "MinishCapWorld", patch: MinishCapProcedurePatch, locatio item_byte_first = 0x00 item_byte_second = 0x00 - if item.player == world.player: + if item.player == world.player and not world.options.remote_items.value: # The item belongs to this player's world, it should use local item ids item_byte_first = item_table[item.name].byte_ids[0] item_byte_second = item_table[item.name].byte_ids[1] diff --git a/worlds/tmc/__init__.py b/worlds/tmc/__init__.py index dae5d550dea0..af0e17fd61a4 100644 --- a/worlds/tmc/__init__.py +++ b/worlds/tmc/__init__.py @@ -104,7 +104,7 @@ def fill_slot_data(self) -> Dict[str, any]: "GoalVaati": self.options.goal_vaati.value, } data |= self.options.as_dict("death_link", "death_link_gameover", "rupeesanity", "obscure_spots", "goal_vaati", - "dungeon_small_keys", "dungeon_big_keys", "dungeon_compasses", "dungeon_maps", + "dungeon_small_keys", "dungeon_big_keys", "dungeon_compasses", "dungeon_maps", "remote_items", casing="snake") data |= get_option_data(self.options) # If Element location should be known, add locations to slot data for tracker diff --git a/worlds/tmc/data/basepatch.bsdiff b/worlds/tmc/data/basepatch.bsdiff index b12fffada8df..ec6374f54fa4 100644 Binary files a/worlds/tmc/data/basepatch.bsdiff and b/worlds/tmc/data/basepatch.bsdiff differ