diff --git a/MultiClient.py b/MultiClient.py index 520e5c86..a1736984 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -342,7 +342,40 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10), 'Ganons Tower - Mini Helmasaur Room - Left': (0x3d, 0x10), 'Ganons Tower - Mini Helmasaur Room - Right': (0x3d, 0x20), 'Ganons Tower - Pre-Moldorm Chest': (0x3d, 0x40), - 'Ganons Tower - Validation Chest': (0x4d, 0x10)} + 'Ganons Tower - Validation Chest': (0x4d, 0x10), + 'Hyrule Castle - Map Guard Key Drop': (0x72, 0x400), + 'Hyrule Castle - Boomerang Guard Key Drop': (0x71, 0x400), + 'Hyrule Castle - Key Rat Key Drop': (0x21, 0x400), + 'Hyrule Castle - Big Key Drop': (0x80, 0x400), + 'Eastern Palace - Dark Square Pot Key': (0xba, 0x400), + 'Eastern Palace - Dark Eyegore Key Drop': (0x99, 0x400), + 'Desert Palace - Desert Tiles 1 Pot Key': (0x63, 0x400), + 'Desert Palace - Beamos Hall Pot Key': (0x53, 0x400), + 'Desert Palace - Desert Tiles 2 Pot Key': (0x43, 0x400), + 'Castle Tower - Dark Archer Key Drop': (0xc0, 0x400), + 'Castle Tower - Circle of Pots Key Drop': (0xb0, 0x400), + 'Swamp Palace - Pot Row Pot Key': (0x38, 0x400), + 'Swamp Palace - Trench 1 Pot Key': (0x37, 0x400), + 'Swamp Palace - Hookshot Pot Key': (0x36, 0x400), + 'Swamp Palace - Trench 2 Pot Key': (0x35, 0x400), + 'Swamp Palace - Waterway Pot Key': (0x16, 0x400), + 'Skull Woods - West Lobby Pot Key': (0x56, 0x400), + 'Skull Woods - Spike Corner Key Drop': (0x39, 0x400), + 'Thieves\' Town - Hallway Pot Key': (0xbc, 0x400), + 'Thieves\' Town - Spike Switch Pot Key': (0xab, 0x400), + 'Ice Palace - Jelly Key Drop': (0x0e, 0x400), + 'Ice Palace - Conveyor Key Drop': (0x3e, 0x400), + 'Ice Palace - Hammer Block Key Drop': (0x3f, 0x400), + 'Ice Palace - Many Pots Pot Key': (0x9f, 0x400), + 'Misery Mire - Spikes Pot Key': (0xb3, 0x400), + 'Misery Mire - Fishbone Pot Key': (0xa1, 0x400), + 'Misery Mire - Conveyor Crystal Key Drop': (0xc1, 0x400), + 'Turtle Rock - Pokey 1 Key Drop': (0xb6, 0x400), + 'Turtle Rock - Pokey 2 Key Drop': (0x13, 0x400), + 'Ganons Tower - Conveyor Cross Pot Key': (0x8b, 0x400), + 'Ganons Tower - Double Switch Pot Key': (0x9b, 0x400), + 'Ganons Tower - Conveyor Star Pits Pot Key': (0x7b, 0x400), + 'Ganons Tower - Mini Helmasaur Key Drop': (0x3d, 0x400)} location_table_npc = {'Mushroom': 0x1000, 'King Zora': 0x2, 'Sahasrahla': 0x10, @@ -822,7 +855,7 @@ async def process_server_cmd(ctx: Context, cmd, args): ctx.player_names = {p: n for p, n in args[1]} msgs = [] if ctx.locations_checked: - msgs.append(['LocationChecks', [Regions.location_table[loc][0] for loc in ctx.locations_checked]]) + msgs.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) if ctx.locations_scouted: msgs.append(['LocationScouts', list(ctx.locations_scouted)]) if msgs: @@ -837,7 +870,7 @@ async def process_server_cmd(ctx: Context, cmd, args): elif start_index != len(ctx.items_received): sync_msg = [['Sync']] if ctx.locations_checked: - sync_msg.append(['LocationChecks', [Regions.location_table[loc][0] for loc in ctx.locations_checked]]) + sync_msg.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) await ctx.send_msgs(sync_msg) if start_index == len(ctx.items_received): for item in items: @@ -1014,6 +1047,11 @@ class ClientCommandProcessor(CommandProcessor): self.output('Missing: ' + location) count += 1 + for location in [k for k, v in Regions.key_drop_data.items()]: + if location not in self.ctx.locations_checked: + self.output('Missing: ' + location) + count += 1 + if count: self.output(f"Found {count} missing location checks") else: @@ -1077,11 +1115,14 @@ async def track_locations(ctx : Context, roomid, roomdata): ctx.locations_checked.add(location) ctx.ui_node.log_info("New check: %s (%d/216)" % (location, len(ctx.locations_checked))) ctx.ui_node.send_location_check(ctx, location) - new_locations.append(Regions.location_table[location][0]) + new_locations.append(Regions.lookup_name_to_id[location]) 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) + try: + if location not in ctx.locations_checked and loc_roomid == roomid and (roomdata << 4) & loc_mask != 0: + new_check(location) + except Exception as e: + ctx.ui_node.log_info(f"Exception: {e}") uw_begin = 0x129 uw_end = 0 diff --git a/MultiServer.py b/MultiServer.py index 44b221f3..6f87c88c 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -29,7 +29,7 @@ import Utils from Utils import get_item_name_from_id, get_location_name_from_address, ReceivedItem, _version_tuple from NetUtils import Node, Endpoint -console_names = frozenset(set(Items.item_table) | set(Regions.location_table) | set(Items.item_name_groups)) +console_names = frozenset(set(Items.item_table) | set(Regions.location_table) | set(Items.item_name_groups) | set(Regions.key_drop_data)) CLIENT_PLAYING = 0 CLIENT_GOAL = 1 @@ -439,6 +439,7 @@ def send_new_items(ctx: Context): def forfeit_player(ctx: Context, team: int, slot: int): all_locations = {values[0] for values in Regions.location_table.values() if type(values[0]) is int} + all_locations.update({values[1] for values in Regions.key_drop_data.values()}) ctx.notify_all("%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1)) register_location_checks(ctx, team, slot, all_locations) @@ -514,7 +515,7 @@ def collect_hints(ctx: Context, team: int, slot: int, item: str) -> typing.List[ def collect_hints_location(ctx: Context, team: int, slot: int, location: str) -> typing.List[Utils.Hint]: hints = [] - seeked_location = Regions.location_table[location][0] + seeked_location = Regions.lookup_name_to_id[location] for check, result in ctx.locations.items(): location_id, finding_player = check if finding_player == slot and location_id == seeked_location: diff --git a/Regions.py b/Regions.py index 58049039..459b42ca 100644 --- a/Regions.py +++ b/Regions.py @@ -397,6 +397,42 @@ shop_table = { 'Capacity Upgrade': (0x0115, ShopType.UpgradeShop, 0x04, True, True, [('Bomb Upgrade (+5)', 100, 7), ('Arrow Upgrade (+5)', 100, 7)]) } +key_drop_data = { + 'Hyrule Castle - Map Guard Key Drop': [0x140036, 0x140037], + 'Hyrule Castle - Boomerang Guard Key Drop': [0x140033, 0x140034], + 'Hyrule Castle - Key Rat Key Drop': [0x14000c, 0x14000d], + 'Hyrule Castle - Big Key Drop': [0x14003c, 0x14003d], + 'Eastern Palace - Dark Square Pot Key': [0x14005a, 0x14005b], + 'Eastern Palace - Dark Eyegore Key Drop': [0x140048, 0x140049], + 'Desert Palace - Desert Tiles 1 Pot Key': [0x140030, 0x140031], + 'Desert Palace - Beamos Hall Pot Key': [0x14002a, 0x14002b], + 'Desert Palace - Desert Tiles 2 Pot Key': [0x140027, 0x140028], + 'Castle Tower - Dark Archer Key Drop': [0x140060, 0x140061], + 'Castle Tower - Circle of Pots Key Drop': [0x140051, 0x140052], + 'Swamp Palace - Pot Row Pot Key': [0x140018, 0x140019], + 'Swamp Palace - Trench 1 Pot Key': [0x140015, 0x140016], + 'Swamp Palace - Hookshot Pot Key': [0x140012, 0x140013], + 'Swamp Palace - Trench 2 Pot Key': [0x14000f, 0x140010], + 'Swamp Palace - Waterway Pot Key': [0x140009, 0x14000a], + 'Skull Woods - West Lobby Pot Key': [0x14002d, 0x14002e], + 'Skull Woods - Spike Corner Key Drop': [0x14001b, 0x14001c], + 'Thieves\' Town - Hallway Pot Key': [0x14005d, 0x14005e], + 'Thieves\' Town - Spike Switch Pot Key': [0x14004e, 0x14004f], + 'Ice Palace - Jelly Key Drop': [0x140003, 0x140004], + 'Ice Palace - Conveyor Key Drop': [0x140021, 0x140022], + 'Ice Palace - Hammer Block Key Drop': [0x140024, 0x140025], + 'Ice Palace - Many Pots Pot Key': [0x140045, 0x140046], + 'Misery Mire - Spikes Pot Key': [0x140054, 0x140055], + 'Misery Mire - Fishbone Pot Key': [0x14004b, 0x14004c], + 'Misery Mire - Conveyor Crystal Key Drop': [0x140063, 0x140064], + 'Turtle Rock - Pokey 1 Key Drop': [0x140057, 0x140058], + 'Turtle Rock - Pokey 2 Key Drop': [0x140006, 0x140007], + 'Ganons Tower - Conveyor Cross Pot Key': [0x14003f, 0x140040], + 'Ganons Tower - Double Switch Pot Key': [0x140042, 0x140043], + 'Ganons Tower - Conveyor Star Pits Pot Key': [0x140039, 0x14003a], + 'Ganons Tower - Mini Helmasaur Key Drop': [0x14001e, 0x14001f] +} + location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Bottle Merchant': (0x2eb18, 0x186339, False, 'with a merchant'), 'Flute Spot': (0x18014a, 0x18633d, False, 'underground'), @@ -640,7 +676,9 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), [0x120A7, 0x53F24, 0x53F25, 0x18005C, 0x180079, 0xC708], None, True, 'Turtle Rock')} lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} -lookup_id_to_name[-1] = "cheat console" +lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()}, -1: "cheat console"} +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()}, "cheat console": -1} lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 191256: 'Kings Grave Inner Rocks', 1573194: 'Kings Grave Inner Rocks', 1573189: 'Kings Grave Inner Rocks', @@ -745,7 +783,28 @@ lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 19125 60103: 'Ganons Tower', 60106: 'Ganons Tower', 60109: 'Ganons Tower', 60127: 'Ganons Tower', 60118: 'Ganons Tower', 60148: 'Ganons Tower', 60151: 'Ganons Tower', 60145: 'Ganons Tower', 60157: 'Ganons Tower', - 60160: 'Ganons Tower', 60163: 'Ganons Tower', 60166: 'Ganons Tower'} + 60160: 'Ganons Tower', 60163: 'Ganons Tower', 60166: 'Ganons Tower', + 0x140037: 'Hyrule Castle Entrance (South)', + 0x140034: 'Hyrule Castle Entrance (South)', + 0x14000d: 'Hyrule Castle Entrance (South)', + 0x14003d: 'Hyrule Castle Entrance (South)', + 0x14005b: 'Eastern Palace', 0x140049: 'Eastern Palace', + 0x140031: 'Desert Palace Entrance (North)', + 0x14002b: 'Desert Palace Entrance (North)', + 0x140028: 'Desert Palace Entrance (North)', + 0x140061: 'Agahnims Tower', 0x140052: 'Agahnims Tower', + 0x140019: 'Swamp Palace', 0x140016: 'Swamp Palace', 0x140013: 'Swamp Palace', + 0x140010: 'Swamp Palace', 0x14000a: 'Swamp Palace', + 0x14002e: 'Skull Woods Second Section Door (East)', + 0x14001c: 'Skull Woods Final Section', + 0x14005e: 'Thieves Town', 0x14004f: 'Thieves Town', + 0x140004: 'Ice Palace', 0x140022: 'Ice Palace', + 0x140025: 'Ice Palace', 0x140046: 'Ice Palace', + 0x140055: 'Misery Mire', 0x14004c: 'Misery Mire', + 0x140064: 'Misery Mire', + 0x140058: 'Turtle Rock', 0x140007: 'Dark Death Mountain Ledge (West)', + 0x140040: 'Ganons Tower', 0x140043: 'Ganons Tower', + 0x14003a: 'Ganons Tower', 0x14001f: 'Ganons Tower'} lookup_prizes = {location for location in location_table if location.endswith(" - Prize")} lookup_boss_drops = {location for location in location_table if location.endswith(" - Boss")} \ No newline at end of file diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index d7277910..f80d1a99 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -180,6 +180,25 @@ default_locations = { 60121, 60124, 60127, 1573217, 60130, 60133, 60136, 60139, 60142, 60145, 60148, 60151, 60157}, 'Total': set()} +key_only_locations = { + 'Light World': set(), + 'Dark World': set(), + 'Desert Palace': {0x140031, 0x14002b, 0x140061, 0x140028}, + 'Eastern Palace': {0x14005b, 0x140049}, + 'Hyrule Castle': {0x140037, 0x140034, 0x14000d, 0x14003d}, + 'Agahnims Tower': {0x140061, 0x140052}, + 'Tower of Hera': set(), + 'Swamp Palace': {0x140019, 0x140016, 0x140013, 0x140010, 0x14000a}, + 'Thieves Town': {0x14005e, 0x14004f}, + 'Skull Woods': {0x14002e, 0x14001c}, + 'Ice Palace': {0x140004, 0x140022, 0x140025, 0x140046}, + 'Misery Mire': {0x140055, 0x14004c, 0x140064}, + 'Turtle Rock': {0x140058, 0x140007}, + 'Palace of Darkness': set(), + 'Ganons Tower': {0x140040, 0x140043, 0x14003a, 0x14001f}, + 'Total': set() +} + key_locations = {"Desert Palace", "Eastern Palace", "Hyrule Castle", "Agahnims Tower", "Tower of Hera", "Swamp Palace", "Thieves Town", "Skull Woods", "Ice Palace", "Misery Mire", "Turtle Rock", "Palace of Darkness", "Ganons Tower"} @@ -191,6 +210,10 @@ for area, locations in default_locations.items(): for location in locations: location_to_area[location] = area +for area, locations in key_only_locations.items(): + for location in locations: + location_to_area[location] = area + checks_in_area = {area: len(checks) for area, checks in default_locations.items()} checks_in_area["Total"] = 216 @@ -244,11 +267,16 @@ def get_static_room_data(room: Room): # in > 100 players this can take a bit of time and is the main reason for the cache locations = {tuple(k): tuple(v) for k, v in multidata['locations']} names = multidata["names"] + seed_checks_in_area = checks_in_area.copy() use_door_tracker = False if "tags" in multidata: use_door_tracker = "DR" in multidata["tags"] - result = locations, names, use_door_tracker + if use_door_tracker: + for area, checks in key_only_locations.items(): + seed_checks_in_area[area] += len(checks) + seed_checks_in_area["Total"] = 249 + result = locations, names, use_door_tracker, seed_checks_in_area _multidata_cache[room.seed.id] = result return result @@ -259,7 +287,7 @@ def get_tracker(tracker: UUID): room = Room.get(tracker=tracker) if not room: abort(404) - locations, names, use_door_tracker = get_static_room_data(room) + locations, names, use_door_tracker, seed_checks_in_area = get_static_room_data(room) inventory = {teamnumber: {playernumber: collections.Counter() for playernumber in range(1, len(team) + 1)} for teamnumber, team in enumerate(names)} @@ -280,6 +308,9 @@ def get_tracker(tracker: UUID): for item_id in precollected: attribute_item(inventory, team, player, item_id) for location in locations_checked: + if (location, player) not in locations or location not in location_to_area: + continue + item, recipient = locations[location, player] attribute_item(inventory, team, recipient, item) checks_done[team][player][location_to_area[location]] += 1 @@ -311,7 +342,7 @@ def get_tracker(tracker: UUID): lookup_id_to_name=Items.lookup_id_to_name, player_names=player_names, tracking_names=tracking_names, tracking_ids=tracking_ids, room=room, icons=icons, multi_items=multi_items, checks_done=checks_done, ordered_areas=ordered_areas, - checks_in_area=checks_in_area, activity_timers=activity_timers, + checks_in_area=seed_checks_in_area, activity_timers=activity_timers, key_locations=key_locations, small_key_ids=small_key_ids, big_key_ids=big_key_ids, video=video, big_key_locations=key_locations if use_door_tracker else big_key_locations, hints=hints, long_player_names = long_player_names)