diff --git a/worlds/gstla/BizClient.py b/worlds/gstla/BizClient.py index e14ec823582b..c36bb7964da7 100644 --- a/worlds/gstla/BizClient.py +++ b/worlds/gstla/BizClient.py @@ -31,7 +31,8 @@ class _MemDomain(str, Enum): ROM = 'ROM' class _DataLocations(IntEnum): - IN_GAME = (0x428, 0x2, 0x0, _MemDomain.EWRAM) + MAP_ID = (0x428, 0x2, 0x0, _MemDomain.EWRAM) + ENTRANCE_ID = (0x42A, 0x2, 0x0, _MemDomain.EWRAM) DJINN_FLAGS = (FLAG_START + (0x30 >> 3), 0x0A, 0x30, _MemDomain.EWRAM) AP_ITEM_SLOT = (0xA96, 0x2, 0x0, _MemDomain.EWRAM) # two unused bytes in save data @@ -293,6 +294,8 @@ def __init__(self): self.coop: int = 0 self.remote_blacklist: Set[int] = remote_blacklist self.goals = GoalManager() + self.last_map: int = 0 + self.last_entrance: int = 0 async def validate_rom(self, ctx: 'BizHawkClientContext'): from worlds._bizhawk.context import TextCategory @@ -338,10 +341,9 @@ async def _load_djinn(self, ctx: 'BizHawkClientContext') -> None: self.djinn_ram_to_rom[rom_flag] = djinn_flag def _is_in_game(self, data: List[bytes]) -> bool: - # What the emo tracker pack does; seems like it also verifies the player - # has opened a save file - flag = int.from_bytes(data[_DataLocations.IN_GAME], 'little') - return flag > 1 + # Map ID 0x00 and 0x01 are Title Screen and Clear Data respectively, everything greater is safe + current_map = int.from_bytes(data[_DataLocations.MAP_ID], 'little') + return current_map > 1 def get_goal_flags_key(self, ctx: 'BizHawkClientContext') -> str: return f"gstla_goal_flags_status_{ctx.slot}_{ctx.team}" @@ -355,6 +357,9 @@ def get_djinn_location_key(self, ctx: 'BizHawkClientContext') -> str: def get_djinn_possessed_key(self, ctx: 'BizHawkClientContext') -> str: return f"gstla_djinn_held_{ctx.slot}_{ctx.team}" + def get_map_info_key(self, ctx: 'BizHawkClientContext') -> str: + return f"gstla_map_info_{ctx.slot}_{ctx.team}" + def check_summon_count(self, ctx: "BizHawkClientContext") -> None: if self.summon_item_index >= len(ctx.items_received): return @@ -606,6 +611,25 @@ async def game_watcher(self, ctx: 'BizHawkClientContext') -> None: await self.goals.check_goals(self, ctx) + current_map = int.from_bytes(result[_DataLocations.MAP_ID], 'little') + current_entrance = int.from_bytes(result[_DataLocations.ENTRANCE_ID], 'little') + if current_map != self.last_map or current_entrance != self.last_entrance: + self.last_map = current_map + self.last_entrance = current_entrance + await ctx.send_msgs([{ + "cmd": "Set", + "key": self.get_map_info_key(ctx), + "default": {}, + "want_reply": False, + "operations": [{ + "operation": "replace", + "value": { + "map": current_map, + "entrance": current_entrance + } + }] + }]) + if not ctx.finished_game and self.goals.is_done(self, ctx): await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) ctx.finished_game = True diff --git a/worlds/gstla/Options.py b/worlds/gstla/Options.py index 263915c39a39..0ffe40eac596 100644 --- a/worlds/gstla/Options.py +++ b/worlds/gstla/Options.py @@ -617,9 +617,13 @@ class AutoRun(Toggle): class Coop(Choice): """Enables cooping a seed by making local items the same as remote items, enabling - your friends to receive items you pick up. Also helpful if trying to play a world from multiple devices. + your friends to receive items you pick up. Also helpful if trying to play a world from multiple devices. You will still need to pickup djinn the other person picks up. - off - vanilla + + Note: Due to current limitations, if you are enabling this to coop a seed and you want to use this game's PopTracker pack, + you should turn automatic tabbing off in the pack's settings to avoid constant map changes. + + off - Vanilla progression - Only progression items are treated as remote items prog_useful - Progression and useful items are treated as remote items all - All items are treated as remote items