""" Looks through data object to double-check it makes sense. Will fail for missing or duplicate definitions or duplicate claims and give warnings for unused and unignored locations or warps. """ import logging from typing import List from .data import load_json_data, data _IGNORABLE_LOCATIONS = frozenset({ "HIDDEN_ITEM_TRICK_HOUSE_NUGGET", # Is permanently mssiable and has special behavior that sets the flag early # Duplicate rival fights. All variations are represented by the Brandon + Mudkip version "TRAINER_BRENDAN_ROUTE_103_TREECKO_REWARD", "TRAINER_BRENDAN_ROUTE_103_TORCHIC_REWARD", "TRAINER_MAY_ROUTE_103_MUDKIP_REWARD", "TRAINER_MAY_ROUTE_103_TREECKO_REWARD", "TRAINER_MAY_ROUTE_103_TORCHIC_REWARD", "TRAINER_BRENDAN_ROUTE_110_TREECKO_REWARD", "TRAINER_BRENDAN_ROUTE_110_TORCHIC_REWARD", "TRAINER_MAY_ROUTE_110_MUDKIP_REWARD", "TRAINER_MAY_ROUTE_110_TREECKO_REWARD", "TRAINER_MAY_ROUTE_110_TORCHIC_REWARD", "TRAINER_BRENDAN_ROUTE_119_TREECKO_REWARD", "TRAINER_BRENDAN_ROUTE_119_TORCHIC_REWARD", "TRAINER_MAY_ROUTE_119_MUDKIP_REWARD", "TRAINER_MAY_ROUTE_119_TREECKO_REWARD", "TRAINER_MAY_ROUTE_119_TORCHIC_REWARD", "TRAINER_BRENDAN_RUSTBORO_TREECKO_REWARD", "TRAINER_BRENDAN_RUSTBORO_TORCHIC_REWARD", "TRAINER_MAY_RUSTBORO_MUDKIP_REWARD", "TRAINER_MAY_RUSTBORO_TREECKO_REWARD", "TRAINER_MAY_RUSTBORO_TORCHIC_REWARD", "TRAINER_BRENDAN_LILYCOVE_TREECKO_REWARD", "TRAINER_BRENDAN_LILYCOVE_TORCHIC_REWARD", "TRAINER_MAY_LILYCOVE_MUDKIP_REWARD", "TRAINER_MAY_LILYCOVE_TREECKO_REWARD", "TRAINER_MAY_LILYCOVE_TORCHIC_REWARD", }) _IGNORABLE_WARPS = frozenset({ # Trick House "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!", "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:2/MAP_ROUTE110_TRICK_HOUSE_END:0!", # Department store elevator "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!", "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:3/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!", "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!", "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!", "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!", "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!", # Intro truck "MAP_INSIDE_OF_TRUCK:0,1,2/MAP_DYNAMIC:-1!", # Battle Frontier "MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!", "MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!", "MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2", "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2", "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0", "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:3/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0!", "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0", # Terra Cave and Marine Cave "MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!", "MAP_ROUTE113:1/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE113:2/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE114:3/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE114:4/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE115:1/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE115:2/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE116:3/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE116:4/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE118:0/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_ROUTE118:1/MAP_TERRA_CAVE_ENTRANCE:0!", "MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!", "MAP_UNDERWATER_ROUTE105:0/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE105:1/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE125:0/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE125:1/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE127:0/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE127:1/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE129:0/MAP_UNDERWATER_MARINE_CAVE:0!", "MAP_UNDERWATER_ROUTE129:1/MAP_UNDERWATER_MARINE_CAVE:0!", # Altering Cave "MAP_ALTERING_CAVE:0/MAP_ROUTE103:0", "MAP_ROUTE103:0/MAP_ALTERING_CAVE:0", # Event islands "MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0", "MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0", "MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1", "MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1", "MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1", "MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1", "MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0", "MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1", "MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0", "MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2", "MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0", "MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1", "MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0", "MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1", "MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0", "MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1", "MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0", "MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1", "MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0", "MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1", "MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0", "MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1", "MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0", "MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1", "MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0", "MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1", "MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0", "MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1", "MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1", "MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0", "MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1", "MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0", "MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1", "MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0", "MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1", "MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0", "MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1", "MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0", "MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0", "MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1", "MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0", "MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0", "MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1", "MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0", "MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1", "MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0", "MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1", "MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0", # Secret bases "MAP_SECRET_BASE_BROWN_CAVE1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BROWN_CAVE2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BROWN_CAVE3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BROWN_CAVE4:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BLUE_CAVE1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BLUE_CAVE2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BLUE_CAVE3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_BLUE_CAVE4:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_YELLOW_CAVE1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_YELLOW_CAVE2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_YELLOW_CAVE3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_YELLOW_CAVE4:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_RED_CAVE1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_RED_CAVE2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_RED_CAVE3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_RED_CAVE4:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_SHRUB1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_SHRUB2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_SHRUB3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_SHRUB4:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_TREE1:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_TREE2:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_TREE3:0/MAP_DYNAMIC:-2!", "MAP_SECRET_BASE_TREE4:0/MAP_DYNAMIC:-2!", # Multiplayer rooms "MAP_RECORD_CORNER:0,1,2,3/MAP_DYNAMIC:-1!", "MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!", "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_PETALBURG_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:1/MAP_UNION_ROOM:0!", "MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_OLDALE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_FORTREE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!", "MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!", "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_PETALBURG_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:2/MAP_TRADE_CENTER:0!", "MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_OLDALE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_FORTREE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!", "MAP_BATTLE_COLOSSEUM_2P:0,1/MAP_DYNAMIC:-1!", "MAP_BATTLE_COLOSSEUM_4P:0,1,2,3/MAP_DYNAMIC:-1!", # Unused content "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:0/MAP_CAVE_OF_ORIGIN_1F:1!", "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0", "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1", "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0", "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1", "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:1/MAP_CAVE_OF_ORIGIN_B1F:0!", "MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!", }) def validate_regions() -> bool: """ Verifies that Emerald's data doesn't have duplicate or missing regions/warps/locations. Meant to catch problems during development like forgetting to add a new location or incorrectly splitting a region. """ extracted_data_json = load_json_data("extracted_data.json") error_messages: List[str] = [] warn_messages: List[str] = [] failed = False def error(message: str) -> None: nonlocal failed failed = True error_messages.append(message) def warn(message: str) -> None: warn_messages.append(message) # Check regions for name, region in data.regions.items(): for region_exit in region.exits: if region_exit not in data.regions: error(f"Pokemon Emerald: Region [{region_exit}] referenced by [{name}] was not defined") # Check warps for warp_source, warp_dest in data.warp_map.items(): if warp_source in _IGNORABLE_WARPS: continue if warp_dest is None: error(f"Pokemon Emerald: Warp [{warp_source}] has no destination") elif not data.warps[warp_dest].connects_to(data.warps[warp_source]) and not data.warps[warp_source].is_one_way: error(f"Pokemon Emerald: Warp [{warp_source}] appears to be a one-way warp but was not marked as one") # Check locations claimed_locations = [location for region in data.regions.values() for location in region.locations] claimed_locations_set = set() for location_name in claimed_locations: if location_name in claimed_locations_set: error(f"Pokemon Emerald: Location [{location_name}] was claimed by multiple regions") claimed_locations_set.add(location_name) for location_name in extracted_data_json["locations"]: if location_name not in claimed_locations and location_name not in _IGNORABLE_LOCATIONS: warn(f"Pokemon Emerald: Location [{location_name}] was not claimed by any region") warn_messages.sort() error_messages.sort() for message in warn_messages: logging.warning(message) for message in error_messages: logging.error(message) logging.debug("Pokemon Emerald sanity check done. Found %s errors and %s warnings.", len(error_messages), len(warn_messages)) return not failed