Pokemon Emerald: Rework tags/dynamically create item and location groups (#3263)

* Pokemon Emerald: Rework location tags to categories

* Pokemon Emerald: Rework item tags, automatically create item/location groups

* Pokemon Emerald: Move item and location groups to data.py, add some regional location groups

* Map Regions

* Pokemon Emerald: Fix up location groups

* Pokemon Emerald: Move groups to their own file

* Pokemon Emerald: Add meta groups for location groups

* Pokemon Emerald: Fix has_group using updated item group name

* Pokemon Emerald: Add sanity check for maps in location groups

* Pokemon Emerald: Remove missed use of location.tags

* Pokemon Emerald: Reclassify white and black flutes

* Pokemon Emerald: Update changelog

* Pokemon Emerald: Adjust changelog

---------

Co-authored-by: Tsukino <16899482+Tsukino-uwu@users.noreply.github.com>
This commit is contained in:
Bryce Wilson 2024-11-29 00:24:24 -08:00 committed by GitHub
parent 91185f4f7c
commit 6f2464d4ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 3550 additions and 1511 deletions

View File

@ -2,6 +2,7 @@
### Features ### Features
- Added many new item and location groups.
- Added a Swedish translation of the setup guide. - Added a Swedish translation of the setup guide.
- The client communicates map transitions to any trackers connected to the slot. - The client communicates map transitions to any trackers connected to the slot.
- Added the player's Normalize Encounter Rates option to slot data for trackers. - Added the player's Normalize Encounter Rates option to slot data for trackers.

View File

@ -15,11 +15,11 @@ import settings
from worlds.AutoWorld import WebWorld, World from worlds.AutoWorld import WebWorld, World
from .client import PokemonEmeraldClient # Unused, but required to register with BizHawkClient from .client import PokemonEmeraldClient # Unused, but required to register with BizHawkClient
from .data import LEGENDARY_POKEMON, MapData, SpeciesData, TrainerData, data as emerald_data from .data import LEGENDARY_POKEMON, MapData, SpeciesData, TrainerData, LocationCategory, data as emerald_data
from .items import (ITEM_GROUPS, PokemonEmeraldItem, create_item_label_to_code_map, get_item_classification, from .groups import ITEM_GROUPS, LOCATION_GROUPS
offset_item_value) from .items import PokemonEmeraldItem, create_item_label_to_code_map, get_item_classification, offset_item_value
from .locations import (LOCATION_GROUPS, PokemonEmeraldLocation, create_location_label_to_id_map, from .locations import (PokemonEmeraldLocation, create_location_label_to_id_map, create_locations_by_category,
create_locations_with_tags, set_free_fly, set_legendary_cave_entrances) set_free_fly, set_legendary_cave_entrances)
from .opponents import randomize_opponent_parties from .opponents import randomize_opponent_parties
from .options import (Goal, DarkCavesRequireFlash, HmRequirements, ItemPoolType, PokemonEmeraldOptions, from .options import (Goal, DarkCavesRequireFlash, HmRequirements, ItemPoolType, PokemonEmeraldOptions,
RandomizeWildPokemon, RandomizeBadges, RandomizeHms, NormanRequirement) RandomizeWildPokemon, RandomizeBadges, RandomizeHms, NormanRequirement)
@ -133,9 +133,10 @@ class PokemonEmeraldWorld(World):
@classmethod @classmethod
def stage_assert_generate(cls, multiworld: MultiWorld) -> None: def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
from .sanity_check import validate_regions from .sanity_check import validate_regions, validate_group_maps
assert validate_regions() assert validate_regions()
assert validate_group_maps()
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
return "Great Ball" return "Great Ball"
@ -237,24 +238,32 @@ class PokemonEmeraldWorld(World):
def create_regions(self) -> None: def create_regions(self) -> None:
from .regions import create_regions from .regions import create_regions
regions = create_regions(self) all_regions = create_regions(self)
tags = {"Badge", "HM", "KeyItem", "Rod", "Bike", "EventTicket"} # Tags with progression items always included # Categories with progression items always included
categories = {
LocationCategory.BADGE,
LocationCategory.HM,
LocationCategory.KEY,
LocationCategory.ROD,
LocationCategory.BIKE,
LocationCategory.TICKET
}
if self.options.overworld_items: if self.options.overworld_items:
tags.add("OverworldItem") categories.add(LocationCategory.OVERWORLD_ITEM)
if self.options.hidden_items: if self.options.hidden_items:
tags.add("HiddenItem") categories.add(LocationCategory.HIDDEN_ITEM)
if self.options.npc_gifts: if self.options.npc_gifts:
tags.add("NpcGift") categories.add(LocationCategory.GIFT)
if self.options.berry_trees: if self.options.berry_trees:
tags.add("BerryTree") categories.add(LocationCategory.BERRY_TREE)
if self.options.dexsanity: if self.options.dexsanity:
tags.add("Pokedex") categories.add(LocationCategory.POKEDEX)
if self.options.trainersanity: if self.options.trainersanity:
tags.add("Trainer") categories.add(LocationCategory.TRAINER)
create_locations_with_tags(self, regions, tags) create_locations_by_category(self, all_regions, categories)
self.multiworld.regions.extend(regions.values()) self.multiworld.regions.extend(all_regions.values())
# Exclude locations which are always locked behind the player's goal # Exclude locations which are always locked behind the player's goal
def exclude_locations(location_names: List[str]): def exclude_locations(location_names: List[str]):
@ -325,21 +334,21 @@ class PokemonEmeraldWorld(World):
# Filter progression items which shouldn't be shuffled into the itempool. # Filter progression items which shouldn't be shuffled into the itempool.
# Their locations will still exist, but event items will be placed and # Their locations will still exist, but event items will be placed and
# locked at their vanilla locations instead. # locked at their vanilla locations instead.
filter_tags = set() filter_categories = set()
if not self.options.key_items: if not self.options.key_items:
filter_tags.add("KeyItem") filter_categories.add(LocationCategory.KEY)
if not self.options.rods: if not self.options.rods:
filter_tags.add("Rod") filter_categories.add(LocationCategory.ROD)
if not self.options.bikes: if not self.options.bikes:
filter_tags.add("Bike") filter_categories.add(LocationCategory.BIKE)
if not self.options.event_tickets: if not self.options.event_tickets:
filter_tags.add("EventTicket") filter_categories.add(LocationCategory.TICKET)
if self.options.badges in {RandomizeBadges.option_vanilla, RandomizeBadges.option_shuffle}: if self.options.badges in {RandomizeBadges.option_vanilla, RandomizeBadges.option_shuffle}:
filter_tags.add("Badge") filter_categories.add(LocationCategory.BADGE)
if self.options.hms in {RandomizeHms.option_vanilla, RandomizeHms.option_shuffle}: if self.options.hms in {RandomizeHms.option_vanilla, RandomizeHms.option_shuffle}:
filter_tags.add("HM") filter_categories.add(LocationCategory.HM)
# If Badges and HMs are set to the `shuffle` option, don't add them to # If Badges and HMs are set to the `shuffle` option, don't add them to
# the normal item pool, but do create their items and save them and # the normal item pool, but do create their items and save them and
@ -347,17 +356,17 @@ class PokemonEmeraldWorld(World):
if self.options.badges == RandomizeBadges.option_shuffle: if self.options.badges == RandomizeBadges.option_shuffle:
self.badge_shuffle_info = [ self.badge_shuffle_info = [
(location, self.create_item_by_code(location.default_item_code)) (location, self.create_item_by_code(location.default_item_code))
for location in [l for l in item_locations if "Badge" in l.tags] for location in [l for l in item_locations if emerald_data.locations[l.key].category == LocationCategory.BADGE]
] ]
if self.options.hms == RandomizeHms.option_shuffle: if self.options.hms == RandomizeHms.option_shuffle:
self.hm_shuffle_info = [ self.hm_shuffle_info = [
(location, self.create_item_by_code(location.default_item_code)) (location, self.create_item_by_code(location.default_item_code))
for location in [l for l in item_locations if "HM" in l.tags] for location in [l for l in item_locations if emerald_data.locations[l.key].category == LocationCategory.HM]
] ]
# Filter down locations to actual items that will be filled and create # Filter down locations to actual items that will be filled and create
# the itempool. # the itempool.
item_locations = [location for location in item_locations if len(filter_tags & location.tags) == 0] item_locations = [location for location in item_locations if emerald_data.locations[location.key].category not in filter_categories]
default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations] default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations]
# Take the itempool as-is # Take the itempool as-is
@ -366,7 +375,8 @@ class PokemonEmeraldWorld(World):
# Recreate the itempool from random items # Recreate the itempool from random items
elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced): elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced):
item_categories = ["Ball", "Heal", "Candy", "Vitamin", "EvoStone", "Money", "TM", "Held", "Misc", "Berry"] item_categories = ["Ball", "Healing", "Rare Candy", "Vitamin", "Evolution Stone",
"Money", "TM", "Held", "Misc", "Berry"]
# Count occurrences of types of vanilla items in pool # Count occurrences of types of vanilla items in pool
item_category_counter = Counter() item_category_counter = Counter()
@ -436,25 +446,26 @@ class PokemonEmeraldWorld(World):
# Key items which are considered in access rules but not randomized are converted to events and placed # Key items which are considered in access rules but not randomized are converted to events and placed
# in their vanilla locations so that the player can have them in their inventory for logic. # in their vanilla locations so that the player can have them in their inventory for logic.
def convert_unrandomized_items_to_events(tag: str) -> None: def convert_unrandomized_items_to_events(category: LocationCategory) -> None:
for location in self.multiworld.get_locations(self.player): for location in self.multiworld.get_locations(self.player):
if location.tags is not None and tag in location.tags: assert isinstance(location, PokemonEmeraldLocation)
if location.key is not None and emerald_data.locations[location.key].category == category:
location.place_locked_item(self.create_event(self.item_id_to_name[location.default_item_code])) location.place_locked_item(self.create_event(self.item_id_to_name[location.default_item_code]))
location.progress_type = LocationProgressType.DEFAULT location.progress_type = LocationProgressType.DEFAULT
location.address = None location.address = None
if self.options.badges == RandomizeBadges.option_vanilla: if self.options.badges == RandomizeBadges.option_vanilla:
convert_unrandomized_items_to_events("Badge") convert_unrandomized_items_to_events(LocationCategory.BADGE)
if self.options.hms == RandomizeHms.option_vanilla: if self.options.hms == RandomizeHms.option_vanilla:
convert_unrandomized_items_to_events("HM") convert_unrandomized_items_to_events(LocationCategory.HM)
if not self.options.rods: if not self.options.rods:
convert_unrandomized_items_to_events("Rod") convert_unrandomized_items_to_events(LocationCategory.ROD)
if not self.options.bikes: if not self.options.bikes:
convert_unrandomized_items_to_events("Bike") convert_unrandomized_items_to_events(LocationCategory.BIKE)
if not self.options.event_tickets: if not self.options.event_tickets:
convert_unrandomized_items_to_events("EventTicket") convert_unrandomized_items_to_events(LocationCategory.TICKET)
if not self.options.key_items: if not self.options.key_items:
convert_unrandomized_items_to_events("KeyItem") convert_unrandomized_items_to_events(LocationCategory.KEY)
def pre_fill(self) -> None: def pre_fill(self) -> None:
# Badges and HMs that are set to shuffle need to be placed at # Badges and HMs that are set to shuffle need to be placed at

View File

@ -117,6 +117,21 @@ class ItemData(NamedTuple):
tags: FrozenSet[str] tags: FrozenSet[str]
class LocationCategory(IntEnum):
BADGE = 0
HM = 1
KEY = 2
ROD = 3
BIKE = 4
TICKET = 5
OVERWORLD_ITEM = 6
HIDDEN_ITEM = 7
GIFT = 8
BERRY_TREE = 9
TRAINER = 10
POKEDEX = 11
class LocationData(NamedTuple): class LocationData(NamedTuple):
name: str name: str
label: str label: str
@ -124,6 +139,7 @@ class LocationData(NamedTuple):
default_item: int default_item: int
address: Union[int, List[int]] address: Union[int, List[int]]
flag: int flag: int
category: LocationCategory
tags: FrozenSet[str] tags: FrozenSet[str]
@ -431,6 +447,7 @@ def _init() -> None:
location_json["default_item"], location_json["default_item"],
[location_json["address"]] + [j["address"] for j in alternate_rival_jsons], [location_json["address"]] + [j["address"] for j in alternate_rival_jsons],
location_json["flag"], location_json["flag"],
LocationCategory[location_attributes_json[location_name]["category"]],
frozenset(location_attributes_json[location_name]["tags"]) frozenset(location_attributes_json[location_name]["tags"])
) )
else: else:
@ -441,6 +458,7 @@ def _init() -> None:
location_json["default_item"], location_json["default_item"],
location_json["address"], location_json["address"],
location_json["flag"], location_json["flag"],
LocationCategory[location_attributes_json[location_name]["category"]],
frozenset(location_attributes_json[location_name]["tags"]) frozenset(location_attributes_json[location_name]["tags"])
) )
new_region.locations.append(location_name) new_region.locations.append(location_name)
@ -948,6 +966,7 @@ def _init() -> None:
evo_stage_to_ball_map[evo_stage], evo_stage_to_ball_map[evo_stage],
data.locations[dex_location_name].address, data.locations[dex_location_name].address,
data.locations[dex_location_name].flag, data.locations[dex_location_name].flag,
data.locations[dex_location_name].category,
data.locations[dex_location_name].tags data.locations[dex_location_name].tags
) )

View File

@ -52,49 +52,49 @@
"ITEM_HM_CUT": { "ITEM_HM_CUT": {
"label": "HM01 Cut", "label": "HM01 Cut",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM01", "Unique"],
"modern_id": 420 "modern_id": 420
}, },
"ITEM_HM_FLY": { "ITEM_HM_FLY": {
"label": "HM02 Fly", "label": "HM02 Fly",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM02", "Unique"],
"modern_id": 421 "modern_id": 421
}, },
"ITEM_HM_SURF": { "ITEM_HM_SURF": {
"label": "HM03 Surf", "label": "HM03 Surf",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM03", "Unique"],
"modern_id": 422 "modern_id": 422
}, },
"ITEM_HM_STRENGTH": { "ITEM_HM_STRENGTH": {
"label": "HM04 Strength", "label": "HM04 Strength",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM04", "Unique"],
"modern_id": 423 "modern_id": 423
}, },
"ITEM_HM_FLASH": { "ITEM_HM_FLASH": {
"label": "HM05 Flash", "label": "HM05 Flash",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM05", "Unique"],
"modern_id": 424 "modern_id": 424
}, },
"ITEM_HM_ROCK_SMASH": { "ITEM_HM_ROCK_SMASH": {
"label": "HM06 Rock Smash", "label": "HM06 Rock Smash",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM06", "Unique"],
"modern_id": 425 "modern_id": 425
}, },
"ITEM_HM_WATERFALL": { "ITEM_HM_WATERFALL": {
"label": "HM07 Waterfall", "label": "HM07 Waterfall",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM07", "Unique"],
"modern_id": 737 "modern_id": 737
}, },
"ITEM_HM_DIVE": { "ITEM_HM_DIVE": {
"label": "HM08 Dive", "label": "HM08 Dive",
"classification": "PROGRESSION", "classification": "PROGRESSION",
"tags": ["HM", "Unique"], "tags": ["HM", "HM08", "Unique"],
"modern_id": null "modern_id": null
}, },
@ -375,169 +375,169 @@
"ITEM_POTION": { "ITEM_POTION": {
"label": "Potion", "label": "Potion",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 17 "modern_id": 17
}, },
"ITEM_ANTIDOTE": { "ITEM_ANTIDOTE": {
"label": "Antidote", "label": "Antidote",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 18 "modern_id": 18
}, },
"ITEM_BURN_HEAL": { "ITEM_BURN_HEAL": {
"label": "Burn Heal", "label": "Burn Heal",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 19 "modern_id": 19
}, },
"ITEM_ICE_HEAL": { "ITEM_ICE_HEAL": {
"label": "Ice Heal", "label": "Ice Heal",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 20 "modern_id": 20
}, },
"ITEM_AWAKENING": { "ITEM_AWAKENING": {
"label": "Awakening", "label": "Awakening",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 21 "modern_id": 21
}, },
"ITEM_PARALYZE_HEAL": { "ITEM_PARALYZE_HEAL": {
"label": "Paralyze Heal", "label": "Paralyze Heal",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 22 "modern_id": 22
}, },
"ITEM_FULL_RESTORE": { "ITEM_FULL_RESTORE": {
"label": "Full Restore", "label": "Full Restore",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 23 "modern_id": 23
}, },
"ITEM_MAX_POTION": { "ITEM_MAX_POTION": {
"label": "Max Potion", "label": "Max Potion",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 24 "modern_id": 24
}, },
"ITEM_HYPER_POTION": { "ITEM_HYPER_POTION": {
"label": "Hyper Potion", "label": "Hyper Potion",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 25 "modern_id": 25
}, },
"ITEM_SUPER_POTION": { "ITEM_SUPER_POTION": {
"label": "Super Potion", "label": "Super Potion",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 26 "modern_id": 26
}, },
"ITEM_FULL_HEAL": { "ITEM_FULL_HEAL": {
"label": "Full Heal", "label": "Full Heal",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 27 "modern_id": 27
}, },
"ITEM_REVIVE": { "ITEM_REVIVE": {
"label": "Revive", "label": "Revive",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 28 "modern_id": 28
}, },
"ITEM_MAX_REVIVE": { "ITEM_MAX_REVIVE": {
"label": "Max Revive", "label": "Max Revive",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 29 "modern_id": 29
}, },
"ITEM_FRESH_WATER": { "ITEM_FRESH_WATER": {
"label": "Fresh Water", "label": "Fresh Water",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 30 "modern_id": 30
}, },
"ITEM_SODA_POP": { "ITEM_SODA_POP": {
"label": "Soda Pop", "label": "Soda Pop",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 31 "modern_id": 31
}, },
"ITEM_LEMONADE": { "ITEM_LEMONADE": {
"label": "Lemonade", "label": "Lemonade",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 32 "modern_id": 32
}, },
"ITEM_MOOMOO_MILK": { "ITEM_MOOMOO_MILK": {
"label": "Moomoo Milk", "label": "Moomoo Milk",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 33 "modern_id": 33
}, },
"ITEM_ENERGY_POWDER": { "ITEM_ENERGY_POWDER": {
"label": "Energy Powder", "label": "Energy Powder",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 34 "modern_id": 34
}, },
"ITEM_ENERGY_ROOT": { "ITEM_ENERGY_ROOT": {
"label": "Energy Root", "label": "Energy Root",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 35 "modern_id": 35
}, },
"ITEM_HEAL_POWDER": { "ITEM_HEAL_POWDER": {
"label": "Heal Powder", "label": "Heal Powder",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 36 "modern_id": 36
}, },
"ITEM_REVIVAL_HERB": { "ITEM_REVIVAL_HERB": {
"label": "Revival Herb", "label": "Revival Herb",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 37 "modern_id": 37
}, },
"ITEM_ETHER": { "ITEM_ETHER": {
"label": "Ether", "label": "Ether",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 38 "modern_id": 38
}, },
"ITEM_MAX_ETHER": { "ITEM_MAX_ETHER": {
"label": "Max Ether", "label": "Max Ether",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 39 "modern_id": 39
}, },
"ITEM_ELIXIR": { "ITEM_ELIXIR": {
"label": "Elixir", "label": "Elixir",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 40 "modern_id": 40
}, },
"ITEM_MAX_ELIXIR": { "ITEM_MAX_ELIXIR": {
"label": "Max Elixir", "label": "Max Elixir",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 41 "modern_id": 41
}, },
"ITEM_LAVA_COOKIE": { "ITEM_LAVA_COOKIE": {
"label": "Lava Cookie", "label": "Lava Cookie",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 42 "modern_id": 42
}, },
"ITEM_BERRY_JUICE": { "ITEM_BERRY_JUICE": {
"label": "Berry Juice", "label": "Berry Juice",
"classification": "FILLER", "classification": "FILLER",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 43 "modern_id": 43
}, },
"ITEM_SACRED_ASH": { "ITEM_SACRED_ASH": {
"label": "Sacred Ash", "label": "Sacred Ash",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Heal"], "tags": ["Healing"],
"modern_id": 44 "modern_id": 44
}, },
@ -736,19 +736,19 @@
}, },
"ITEM_BLACK_FLUTE": { "ITEM_BLACK_FLUTE": {
"label": "Black Flute", "label": "Black Flute",
"classification": "FILLER", "classification": "USEFUL",
"tags": ["Misc"], "tags": ["Misc"],
"modern_id": 68 "modern_id": 68
}, },
"ITEM_WHITE_FLUTE": { "ITEM_WHITE_FLUTE": {
"label": "White Flute", "label": "White Flute",
"classification": "FILLER", "classification": "USEFUL",
"tags": ["Misc"], "tags": ["Misc"],
"modern_id": 69 "modern_id": 69
}, },
"ITEM_HEART_SCALE": { "ITEM_HEART_SCALE": {
"label": "Heart Scale", "label": "Heart Scale",
"classification": "FILLER", "classification": "USEFUL",
"tags": ["Misc"], "tags": ["Misc"],
"modern_id": 93 "modern_id": 93
}, },
@ -757,37 +757,37 @@
"ITEM_SUN_STONE": { "ITEM_SUN_STONE": {
"label": "Sun Stone", "label": "Sun Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 80 "modern_id": 80
}, },
"ITEM_MOON_STONE": { "ITEM_MOON_STONE": {
"label": "Moon Stone", "label": "Moon Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 81 "modern_id": 81
}, },
"ITEM_FIRE_STONE": { "ITEM_FIRE_STONE": {
"label": "Fire Stone", "label": "Fire Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 82 "modern_id": 82
}, },
"ITEM_THUNDER_STONE": { "ITEM_THUNDER_STONE": {
"label": "Thunder Stone", "label": "Thunder Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 83 "modern_id": 83
}, },
"ITEM_WATER_STONE": { "ITEM_WATER_STONE": {
"label": "Water Stone", "label": "Water Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 84 "modern_id": 84
}, },
"ITEM_LEAF_STONE": { "ITEM_LEAF_STONE": {
"label": "Leaf Stone", "label": "Leaf Stone",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["EvoStone"], "tags": ["Evolution Stone"],
"modern_id": 85 "modern_id": 85
}, },
@ -1215,7 +1215,7 @@
"ITEM_KINGS_ROCK": { "ITEM_KINGS_ROCK": {
"label": "King's Rock", "label": "King's Rock",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 221 "modern_id": 221
}, },
"ITEM_SILVER_POWDER": { "ITEM_SILVER_POWDER": {
@ -1245,13 +1245,13 @@
"ITEM_DEEP_SEA_TOOTH": { "ITEM_DEEP_SEA_TOOTH": {
"label": "Deep Sea Tooth", "label": "Deep Sea Tooth",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 226 "modern_id": 226
}, },
"ITEM_DEEP_SEA_SCALE": { "ITEM_DEEP_SEA_SCALE": {
"label": "Deep Sea Scale", "label": "Deep Sea Scale",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 227 "modern_id": 227
}, },
"ITEM_SMOKE_BALL": { "ITEM_SMOKE_BALL": {
@ -1287,7 +1287,7 @@
"ITEM_METAL_COAT": { "ITEM_METAL_COAT": {
"label": "Metal Coat", "label": "Metal Coat",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 233 "modern_id": 233
}, },
"ITEM_LEFTOVERS": { "ITEM_LEFTOVERS": {
@ -1299,7 +1299,7 @@
"ITEM_DRAGON_SCALE": { "ITEM_DRAGON_SCALE": {
"label": "Dragon Scale", "label": "Dragon Scale",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 235 "modern_id": 235
}, },
"ITEM_LIGHT_BALL": { "ITEM_LIGHT_BALL": {
@ -1401,7 +1401,7 @@
"ITEM_UP_GRADE": { "ITEM_UP_GRADE": {
"label": "Up-Grade", "label": "Up-Grade",
"classification": "USEFUL", "classification": "USEFUL",
"tags": ["Held"], "tags": ["Held", "Evolution Stone"],
"modern_id": 252 "modern_id": 252
}, },
"ITEM_SHELL_BELL": { "ITEM_SHELL_BELL": {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,721 @@
from typing import Dict, Set
from .data import LocationCategory, data
# Item Groups
ITEM_GROUPS: Dict[str, Set[str]] = {}
for item in data.items.values():
for tag in item.tags:
if tag not in ITEM_GROUPS:
ITEM_GROUPS[tag] = set()
ITEM_GROUPS[tag].add(item.label)
# Location Groups
_LOCATION_GROUP_MAPS = {
"Abandoned Ship": {
"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE",
"MAP_ABANDONED_SHIP_CORRIDORS_1F",
"MAP_ABANDONED_SHIP_CORRIDORS_B1F",
"MAP_ABANDONED_SHIP_DECK",
"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS",
"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
"MAP_ABANDONED_SHIP_ROOMS2_1F",
"MAP_ABANDONED_SHIP_ROOMS2_B1F",
"MAP_ABANDONED_SHIP_ROOMS_1F",
"MAP_ABANDONED_SHIP_ROOMS_B1F",
"MAP_ABANDONED_SHIP_ROOM_B1F",
"MAP_ABANDONED_SHIP_UNDERWATER1",
"MAP_ABANDONED_SHIP_UNDERWATER2",
},
"Aqua Hideout": {
"MAP_AQUA_HIDEOUT_1F",
"MAP_AQUA_HIDEOUT_B1F",
"MAP_AQUA_HIDEOUT_B2F",
},
"Battle Frontier": {
"MAP_ARTISAN_CAVE_1F",
"MAP_ARTISAN_CAVE_B1F",
"MAP_BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_NORMAL",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS",
"MAP_BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR",
"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_TOP",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_ELEVATOR",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_CORRIDOR",
"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM",
"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER",
"MAP_BATTLE_FRONTIER_LOUNGE1",
"MAP_BATTLE_FRONTIER_LOUNGE2",
"MAP_BATTLE_FRONTIER_LOUNGE3",
"MAP_BATTLE_FRONTIER_LOUNGE4",
"MAP_BATTLE_FRONTIER_LOUNGE5",
"MAP_BATTLE_FRONTIER_LOUNGE6",
"MAP_BATTLE_FRONTIER_LOUNGE7",
"MAP_BATTLE_FRONTIER_LOUNGE8",
"MAP_BATTLE_FRONTIER_LOUNGE9",
"MAP_BATTLE_FRONTIER_MART",
"MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
"MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F",
"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F",
"MAP_BATTLE_FRONTIER_RANKING_HALL",
"MAP_BATTLE_FRONTIER_RECEPTION_GATE",
"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE",
"MAP_BATTLE_PYRAMID_SQUARE01",
"MAP_BATTLE_PYRAMID_SQUARE02",
"MAP_BATTLE_PYRAMID_SQUARE03",
"MAP_BATTLE_PYRAMID_SQUARE04",
"MAP_BATTLE_PYRAMID_SQUARE05",
"MAP_BATTLE_PYRAMID_SQUARE06",
"MAP_BATTLE_PYRAMID_SQUARE07",
"MAP_BATTLE_PYRAMID_SQUARE08",
"MAP_BATTLE_PYRAMID_SQUARE09",
"MAP_BATTLE_PYRAMID_SQUARE10",
"MAP_BATTLE_PYRAMID_SQUARE11",
"MAP_BATTLE_PYRAMID_SQUARE12",
"MAP_BATTLE_PYRAMID_SQUARE13",
"MAP_BATTLE_PYRAMID_SQUARE14",
"MAP_BATTLE_PYRAMID_SQUARE15",
"MAP_BATTLE_PYRAMID_SQUARE16",
},
"Birth Island": {
"MAP_BIRTH_ISLAND_EXTERIOR",
"MAP_BIRTH_ISLAND_HARBOR",
},
"Contest Hall": {
"MAP_CONTEST_HALL",
"MAP_CONTEST_HALL_BEAUTY",
"MAP_CONTEST_HALL_COOL",
"MAP_CONTEST_HALL_CUTE",
"MAP_CONTEST_HALL_SMART",
"MAP_CONTEST_HALL_TOUGH",
},
"Dewford Town": {
"MAP_DEWFORD_TOWN",
"MAP_DEWFORD_TOWN_GYM",
"MAP_DEWFORD_TOWN_HALL",
"MAP_DEWFORD_TOWN_HOUSE1",
"MAP_DEWFORD_TOWN_HOUSE2",
"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F",
"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F",
},
"Ever Grande City": {
"MAP_EVER_GRANDE_CITY",
"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM",
"MAP_EVER_GRANDE_CITY_DRAKES_ROOM",
"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM",
"MAP_EVER_GRANDE_CITY_HALL1",
"MAP_EVER_GRANDE_CITY_HALL2",
"MAP_EVER_GRANDE_CITY_HALL3",
"MAP_EVER_GRANDE_CITY_HALL4",
"MAP_EVER_GRANDE_CITY_HALL5",
"MAP_EVER_GRANDE_CITY_HALL_OF_FAME",
"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM",
"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F",
"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F",
"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F",
"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F",
"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM",
},
"Fallarbor Town": {
"MAP_FALLARBOR_TOWN",
"MAP_FALLARBOR_TOWN_BATTLE_TENT_BATTLE_ROOM",
"MAP_FALLARBOR_TOWN_BATTLE_TENT_CORRIDOR",
"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY",
"MAP_FALLARBOR_TOWN_COZMOS_HOUSE",
"MAP_FALLARBOR_TOWN_MART",
"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE",
"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F",
"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F",
},
"Faraway Island": {
"MAP_FARAWAY_ISLAND_ENTRANCE",
"MAP_FARAWAY_ISLAND_INTERIOR",
},
"Fiery Path": {"MAP_FIERY_PATH"},
"Fortree City": {
"MAP_FORTREE_CITY",
"MAP_FORTREE_CITY_DECORATION_SHOP",
"MAP_FORTREE_CITY_GYM",
"MAP_FORTREE_CITY_HOUSE1",
"MAP_FORTREE_CITY_HOUSE2",
"MAP_FORTREE_CITY_HOUSE3",
"MAP_FORTREE_CITY_HOUSE4",
"MAP_FORTREE_CITY_HOUSE5",
"MAP_FORTREE_CITY_MART",
"MAP_FORTREE_CITY_POKEMON_CENTER_1F",
"MAP_FORTREE_CITY_POKEMON_CENTER_2F",
},
"Granite Cave": {
"MAP_GRANITE_CAVE_1F",
"MAP_GRANITE_CAVE_B1F",
"MAP_GRANITE_CAVE_B2F",
"MAP_GRANITE_CAVE_STEVENS_ROOM",
},
"Jagged Pass": {"MAP_JAGGED_PASS"},
"Lavaridge Town": {
"MAP_LAVARIDGE_TOWN",
"MAP_LAVARIDGE_TOWN_GYM_1F",
"MAP_LAVARIDGE_TOWN_GYM_B1F",
"MAP_LAVARIDGE_TOWN_HERB_SHOP",
"MAP_LAVARIDGE_TOWN_HOUSE",
"MAP_LAVARIDGE_TOWN_MART",
"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F",
"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F",
},
"Lilycove City": {
"MAP_LILYCOVE_CITY",
"MAP_LILYCOVE_CITY_CONTEST_HALL",
"MAP_LILYCOVE_CITY_CONTEST_LOBBY",
"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F",
"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR",
"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP",
"MAP_LILYCOVE_CITY_HARBOR",
"MAP_LILYCOVE_CITY_HOUSE1",
"MAP_LILYCOVE_CITY_HOUSE2",
"MAP_LILYCOVE_CITY_HOUSE3",
"MAP_LILYCOVE_CITY_HOUSE4",
"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F",
"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F",
"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE",
"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F",
"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F",
"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB",
},
"Littleroot Town": {
"MAP_INSIDE_OF_TRUCK",
"MAP_LITTLEROOT_TOWN",
"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F",
"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F",
"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F",
"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F",
"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB",
},
"Magma Hideout": {
"MAP_MAGMA_HIDEOUT_1F",
"MAP_MAGMA_HIDEOUT_2F_1R",
"MAP_MAGMA_HIDEOUT_2F_2R",
"MAP_MAGMA_HIDEOUT_2F_3R",
"MAP_MAGMA_HIDEOUT_3F_1R",
"MAP_MAGMA_HIDEOUT_3F_2R",
"MAP_MAGMA_HIDEOUT_3F_3R",
"MAP_MAGMA_HIDEOUT_4F",
},
"Marine Cave": {
"MAP_MARINE_CAVE_END",
"MAP_MARINE_CAVE_ENTRANCE",
"MAP_UNDERWATER_MARINE_CAVE",
},
"Mauville City": {
"MAP_MAUVILLE_CITY",
"MAP_MAUVILLE_CITY_BIKE_SHOP",
"MAP_MAUVILLE_CITY_GAME_CORNER",
"MAP_MAUVILLE_CITY_GYM",
"MAP_MAUVILLE_CITY_HOUSE1",
"MAP_MAUVILLE_CITY_HOUSE2",
"MAP_MAUVILLE_CITY_MART",
"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F",
"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F",
},
"Meteor Falls": {
"MAP_METEOR_FALLS_1F_1R",
"MAP_METEOR_FALLS_1F_2R",
"MAP_METEOR_FALLS_B1F_1R",
"MAP_METEOR_FALLS_B1F_2R",
"MAP_METEOR_FALLS_STEVENS_CAVE",
},
"Mirage Tower": {
"MAP_MIRAGE_TOWER_1F",
"MAP_MIRAGE_TOWER_2F",
"MAP_MIRAGE_TOWER_3F",
"MAP_MIRAGE_TOWER_4F",
},
"Mossdeep City": {
"MAP_MOSSDEEP_CITY",
"MAP_MOSSDEEP_CITY_GAME_CORNER_1F",
"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F",
"MAP_MOSSDEEP_CITY_GYM",
"MAP_MOSSDEEP_CITY_HOUSE1",
"MAP_MOSSDEEP_CITY_HOUSE2",
"MAP_MOSSDEEP_CITY_HOUSE3",
"MAP_MOSSDEEP_CITY_HOUSE4",
"MAP_MOSSDEEP_CITY_MART",
"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F",
"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F",
"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F",
"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F",
"MAP_MOSSDEEP_CITY_STEVENS_HOUSE",
},
"Mt. Chimney": {
"MAP_MT_CHIMNEY",
"MAP_MT_CHIMNEY_CABLE_CAR_STATION",
},
"Mt. Pyre": {
"MAP_MT_PYRE_1F",
"MAP_MT_PYRE_2F",
"MAP_MT_PYRE_3F",
"MAP_MT_PYRE_4F",
"MAP_MT_PYRE_5F",
"MAP_MT_PYRE_6F",
"MAP_MT_PYRE_EXTERIOR",
"MAP_MT_PYRE_SUMMIT",
},
"Navel Rock": {
"MAP_NAVEL_ROCK_B1F",
"MAP_NAVEL_ROCK_BOTTOM",
"MAP_NAVEL_ROCK_DOWN01",
"MAP_NAVEL_ROCK_DOWN02",
"MAP_NAVEL_ROCK_DOWN03",
"MAP_NAVEL_ROCK_DOWN04",
"MAP_NAVEL_ROCK_DOWN05",
"MAP_NAVEL_ROCK_DOWN06",
"MAP_NAVEL_ROCK_DOWN07",
"MAP_NAVEL_ROCK_DOWN08",
"MAP_NAVEL_ROCK_DOWN09",
"MAP_NAVEL_ROCK_DOWN10",
"MAP_NAVEL_ROCK_DOWN11",
"MAP_NAVEL_ROCK_ENTRANCE",
"MAP_NAVEL_ROCK_EXTERIOR",
"MAP_NAVEL_ROCK_FORK",
"MAP_NAVEL_ROCK_HARBOR",
"MAP_NAVEL_ROCK_TOP",
"MAP_NAVEL_ROCK_UP1",
"MAP_NAVEL_ROCK_UP2",
"MAP_NAVEL_ROCK_UP3",
"MAP_NAVEL_ROCK_UP4",
},
"New Mauville": {
"MAP_NEW_MAUVILLE_ENTRANCE",
"MAP_NEW_MAUVILLE_INSIDE",
},
"Oldale Town": {
"MAP_OLDALE_TOWN",
"MAP_OLDALE_TOWN_HOUSE1",
"MAP_OLDALE_TOWN_HOUSE2",
"MAP_OLDALE_TOWN_MART",
"MAP_OLDALE_TOWN_POKEMON_CENTER_1F",
"MAP_OLDALE_TOWN_POKEMON_CENTER_2F",
},
"Pacifidlog Town": {
"MAP_PACIFIDLOG_TOWN",
"MAP_PACIFIDLOG_TOWN_HOUSE1",
"MAP_PACIFIDLOG_TOWN_HOUSE2",
"MAP_PACIFIDLOG_TOWN_HOUSE3",
"MAP_PACIFIDLOG_TOWN_HOUSE4",
"MAP_PACIFIDLOG_TOWN_HOUSE5",
"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F",
"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F",
},
"Petalburg City": {
"MAP_PETALBURG_CITY",
"MAP_PETALBURG_CITY_GYM",
"MAP_PETALBURG_CITY_HOUSE1",
"MAP_PETALBURG_CITY_HOUSE2",
"MAP_PETALBURG_CITY_MART",
"MAP_PETALBURG_CITY_POKEMON_CENTER_1F",
"MAP_PETALBURG_CITY_POKEMON_CENTER_2F",
"MAP_PETALBURG_CITY_WALLYS_HOUSE",
},
"Petalburg Woods": {"MAP_PETALBURG_WOODS"},
"Route 101": {"MAP_ROUTE101"},
"Route 102": {"MAP_ROUTE102"},
"Route 103": {"MAP_ROUTE103"},
"Route 104": {
"MAP_ROUTE104",
"MAP_ROUTE104_MR_BRINEYS_HOUSE",
"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP",
},
"Route 105": {
"MAP_ISLAND_CAVE",
"MAP_ROUTE105",
"MAP_UNDERWATER_ROUTE105",
},
"Route 106": {"MAP_ROUTE106"},
"Route 107": {"MAP_ROUTE107"},
"Route 108": {"MAP_ROUTE108"},
"Route 109": {
"MAP_ROUTE109",
"MAP_ROUTE109_SEASHORE_HOUSE",
},
"Route 110": {
"MAP_ROUTE110",
"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE",
"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE",
},
"Trick House": {
"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR",
"MAP_ROUTE110_TRICK_HOUSE_END",
"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE3",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE4",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE5",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE6",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE8",
},
"Route 111": {
"MAP_DESERT_RUINS",
"MAP_ROUTE111",
"MAP_ROUTE111_OLD_LADYS_REST_STOP",
"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE",
},
"Route 112": {
"MAP_ROUTE112",
"MAP_ROUTE112_CABLE_CAR_STATION",
},
"Route 113": {
"MAP_ROUTE113",
"MAP_ROUTE113_GLASS_WORKSHOP",
},
"Route 114": {
"MAP_DESERT_UNDERPASS",
"MAP_ROUTE114",
"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE",
"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL",
"MAP_ROUTE114_LANETTES_HOUSE",
},
"Route 115": {"MAP_ROUTE115"},
"Route 116": {
"MAP_ROUTE116",
"MAP_ROUTE116_TUNNELERS_REST_HOUSE",
},
"Route 117": {
"MAP_ROUTE117",
"MAP_ROUTE117_POKEMON_DAY_CARE",
},
"Route 118": {"MAP_ROUTE118"},
"Route 119": {
"MAP_ROUTE119",
"MAP_ROUTE119_HOUSE",
"MAP_ROUTE119_WEATHER_INSTITUTE_1F",
"MAP_ROUTE119_WEATHER_INSTITUTE_2F",
},
"Route 120": {
"MAP_ANCIENT_TOMB",
"MAP_ROUTE120",
"MAP_SCORCHED_SLAB",
},
"Route 121": {
"MAP_ROUTE121",
},
"Route 122": {"MAP_ROUTE122"},
"Route 123": {
"MAP_ROUTE123",
"MAP_ROUTE123_BERRY_MASTERS_HOUSE",
},
"Route 124": {
"MAP_ROUTE124",
"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE",
"MAP_UNDERWATER_ROUTE124",
},
"Route 125": {
"MAP_ROUTE125",
"MAP_UNDERWATER_ROUTE125",
},
"Route 126": {
"MAP_ROUTE126",
"MAP_UNDERWATER_ROUTE126",
},
"Route 127": {
"MAP_ROUTE127",
"MAP_UNDERWATER_ROUTE127",
},
"Route 128": {
"MAP_ROUTE128",
"MAP_UNDERWATER_ROUTE128",
},
"Route 129": {
"MAP_ROUTE129",
"MAP_UNDERWATER_ROUTE129",
},
"Route 130": {"MAP_ROUTE130"},
"Route 131": {"MAP_ROUTE131"},
"Route 132": {"MAP_ROUTE132"},
"Route 133": {"MAP_ROUTE133"},
"Route 134": {
"MAP_ROUTE134",
"MAP_UNDERWATER_ROUTE134",
"MAP_SEALED_CHAMBER_INNER_ROOM",
"MAP_SEALED_CHAMBER_OUTER_ROOM",
"MAP_UNDERWATER_SEALED_CHAMBER",
},
"Rustboro City": {
"MAP_RUSTBORO_CITY",
"MAP_RUSTBORO_CITY_CUTTERS_HOUSE",
"MAP_RUSTBORO_CITY_DEVON_CORP_1F",
"MAP_RUSTBORO_CITY_DEVON_CORP_2F",
"MAP_RUSTBORO_CITY_DEVON_CORP_3F",
"MAP_RUSTBORO_CITY_FLAT1_1F",
"MAP_RUSTBORO_CITY_FLAT1_2F",
"MAP_RUSTBORO_CITY_FLAT2_1F",
"MAP_RUSTBORO_CITY_FLAT2_2F",
"MAP_RUSTBORO_CITY_FLAT2_3F",
"MAP_RUSTBORO_CITY_GYM",
"MAP_RUSTBORO_CITY_HOUSE1",
"MAP_RUSTBORO_CITY_HOUSE2",
"MAP_RUSTBORO_CITY_HOUSE3",
"MAP_RUSTBORO_CITY_MART",
"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F",
"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F",
"MAP_RUSTBORO_CITY_POKEMON_SCHOOL",
},
"Rusturf Tunnel": {"MAP_RUSTURF_TUNNEL"},
"Safari Zone": {
"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE",
"MAP_SAFARI_ZONE_NORTH",
"MAP_SAFARI_ZONE_NORTHEAST",
"MAP_SAFARI_ZONE_NORTHWEST",
"MAP_SAFARI_ZONE_REST_HOUSE",
"MAP_SAFARI_ZONE_SOUTH",
"MAP_SAFARI_ZONE_SOUTHEAST",
"MAP_SAFARI_ZONE_SOUTHWEST",
},
"Seafloor Cavern": {
"MAP_SEAFLOOR_CAVERN_ENTRANCE",
"MAP_SEAFLOOR_CAVERN_ROOM1",
"MAP_SEAFLOOR_CAVERN_ROOM2",
"MAP_SEAFLOOR_CAVERN_ROOM3",
"MAP_SEAFLOOR_CAVERN_ROOM4",
"MAP_SEAFLOOR_CAVERN_ROOM5",
"MAP_SEAFLOOR_CAVERN_ROOM6",
"MAP_SEAFLOOR_CAVERN_ROOM7",
"MAP_SEAFLOOR_CAVERN_ROOM8",
"MAP_SEAFLOOR_CAVERN_ROOM9",
"MAP_UNDERWATER_SEAFLOOR_CAVERN",
},
"Shoal Cave": {
"MAP_SHOAL_CAVE_HIGH_TIDE_ENTRANCE_ROOM",
"MAP_SHOAL_CAVE_HIGH_TIDE_INNER_ROOM",
"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM",
"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM",
"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM",
},
"Sky Pillar": {
"MAP_SKY_PILLAR_1F",
"MAP_SKY_PILLAR_2F",
"MAP_SKY_PILLAR_3F",
"MAP_SKY_PILLAR_4F",
"MAP_SKY_PILLAR_5F",
"MAP_SKY_PILLAR_ENTRANCE",
"MAP_SKY_PILLAR_OUTSIDE",
"MAP_SKY_PILLAR_TOP",
},
"Slateport City": {
"MAP_SLATEPORT_CITY",
"MAP_SLATEPORT_CITY_BATTLE_TENT_BATTLE_ROOM",
"MAP_SLATEPORT_CITY_BATTLE_TENT_CORRIDOR",
"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY",
"MAP_SLATEPORT_CITY_HARBOR",
"MAP_SLATEPORT_CITY_HOUSE",
"MAP_SLATEPORT_CITY_MART",
"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE",
"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F",
"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F",
"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F",
"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F",
"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB",
"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F",
"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F",
},
"Sootopolis City": {
"MAP_CAVE_OF_ORIGIN_1F",
"MAP_CAVE_OF_ORIGIN_B1F",
"MAP_CAVE_OF_ORIGIN_ENTRANCE",
"MAP_SOOTOPOLIS_CITY",
"MAP_SOOTOPOLIS_CITY_GYM_1F",
"MAP_SOOTOPOLIS_CITY_GYM_B1F",
"MAP_SOOTOPOLIS_CITY_HOUSE1",
"MAP_SOOTOPOLIS_CITY_HOUSE2",
"MAP_SOOTOPOLIS_CITY_HOUSE3",
"MAP_SOOTOPOLIS_CITY_HOUSE4",
"MAP_SOOTOPOLIS_CITY_HOUSE5",
"MAP_SOOTOPOLIS_CITY_HOUSE6",
"MAP_SOOTOPOLIS_CITY_HOUSE7",
"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE",
"MAP_SOOTOPOLIS_CITY_MART",
"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F",
"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F",
"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F",
"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F",
"MAP_UNDERWATER_SOOTOPOLIS_CITY",
},
"Southern Island": {
"MAP_SOUTHERN_ISLAND_EXTERIOR",
"MAP_SOUTHERN_ISLAND_INTERIOR",
},
"S.S. Tidal": {
"MAP_SS_TIDAL_CORRIDOR",
"MAP_SS_TIDAL_LOWER_DECK",
"MAP_SS_TIDAL_ROOMS",
},
"Terra Cave": {
"MAP_TERRA_CAVE_END",
"MAP_TERRA_CAVE_ENTRANCE",
},
"Trainer Hill": {
"MAP_TRAINER_HILL_2F",
"MAP_TRAINER_HILL_3F",
"MAP_TRAINER_HILL_4F",
"MAP_TRAINER_HILL_ELEVATOR",
"MAP_TRAINER_HILL_ENTRANCE",
"MAP_TRAINER_HILL_ROOF",
},
"Verdanturf Town": {
"MAP_VERDANTURF_TOWN",
"MAP_VERDANTURF_TOWN_BATTLE_TENT_BATTLE_ROOM",
"MAP_VERDANTURF_TOWN_BATTLE_TENT_CORRIDOR",
"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY",
"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE",
"MAP_VERDANTURF_TOWN_HOUSE",
"MAP_VERDANTURF_TOWN_MART",
"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F",
"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F",
"MAP_VERDANTURF_TOWN_WANDAS_HOUSE",
},
"Victory Road": {
"MAP_VICTORY_ROAD_1F",
"MAP_VICTORY_ROAD_B1F",
"MAP_VICTORY_ROAD_B2F",
},
}
_LOCATION_CATEGORY_TO_GROUP_NAME = {
LocationCategory.BADGE: "Badges",
LocationCategory.HM: "HMs",
LocationCategory.KEY: "Key Items",
LocationCategory.ROD: "Fishing Rods",
LocationCategory.BIKE: "Bikes",
LocationCategory.TICKET: "Tickets",
LocationCategory.OVERWORLD_ITEM: "Overworld Items",
LocationCategory.HIDDEN_ITEM: "Hidden Items",
LocationCategory.GIFT: "NPC Gifts",
LocationCategory.BERRY_TREE: "Berry Trees",
LocationCategory.TRAINER: "Trainers",
LocationCategory.POKEDEX: "Pokedex",
}
LOCATION_GROUPS: Dict[str, Set[str]] = {group_name: set() for group_name in _LOCATION_CATEGORY_TO_GROUP_NAME.values()}
for location in data.locations.values():
# Category groups
LOCATION_GROUPS[_LOCATION_CATEGORY_TO_GROUP_NAME[location.category]].add(location.label)
# Tag groups
for tag in location.tags:
if tag not in LOCATION_GROUPS:
LOCATION_GROUPS[tag] = set()
LOCATION_GROUPS[tag].add(location.label)
# Geographic groups
if location.parent_region != "REGION_POKEDEX":
map_name = data.regions[location.parent_region].parent_map.name
for group, maps in _LOCATION_GROUP_MAPS.items():
if map_name in maps:
if group not in LOCATION_GROUPS:
LOCATION_GROUPS[group] = set()
LOCATION_GROUPS[group].add(location.label)
break
# Meta-groups
LOCATION_GROUPS["Cities"] = {
*LOCATION_GROUPS.get("Littleroot Town", set()),
*LOCATION_GROUPS.get("Oldale Town", set()),
*LOCATION_GROUPS.get("Petalburg City", set()),
*LOCATION_GROUPS.get("Rustboro City", set()),
*LOCATION_GROUPS.get("Dewford Town", set()),
*LOCATION_GROUPS.get("Slateport City", set()),
*LOCATION_GROUPS.get("Mauville City", set()),
*LOCATION_GROUPS.get("Verdanturf Town", set()),
*LOCATION_GROUPS.get("Fallarbor Town", set()),
*LOCATION_GROUPS.get("Lavaridge Town", set()),
*LOCATION_GROUPS.get("Fortree City", set()),
*LOCATION_GROUPS.get("Mossdeep City", set()),
*LOCATION_GROUPS.get("Sootopolis City", set()),
*LOCATION_GROUPS.get("Pacifidlog Town", set()),
*LOCATION_GROUPS.get("Ever Grande City", set()),
}
LOCATION_GROUPS["Dungeons"] = {
*LOCATION_GROUPS.get("Petalburg Woods", set()),
*LOCATION_GROUPS.get("Rusturf Tunnel", set()),
*LOCATION_GROUPS.get("Granite Cave", set()),
*LOCATION_GROUPS.get("Fiery Path", set()),
*LOCATION_GROUPS.get("Meteor Falls", set()),
*LOCATION_GROUPS.get("Jagged Pass", set()),
*LOCATION_GROUPS.get("Mt. Chimney", set()),
*LOCATION_GROUPS.get("Abandoned Ship", set()),
*LOCATION_GROUPS.get("New Mauville", set()),
*LOCATION_GROUPS.get("Mt. Pyre", set()),
*LOCATION_GROUPS.get("Seafloor Cavern", set()),
*LOCATION_GROUPS.get("Sky Pillar", set()),
*LOCATION_GROUPS.get("Victory Road", set()),
}
LOCATION_GROUPS["Routes"] = {
*LOCATION_GROUPS.get("Route 101", set()),
*LOCATION_GROUPS.get("Route 102", set()),
*LOCATION_GROUPS.get("Route 103", set()),
*LOCATION_GROUPS.get("Route 104", set()),
*LOCATION_GROUPS.get("Route 105", set()),
*LOCATION_GROUPS.get("Route 106", set()),
*LOCATION_GROUPS.get("Route 107", set()),
*LOCATION_GROUPS.get("Route 108", set()),
*LOCATION_GROUPS.get("Route 109", set()),
*LOCATION_GROUPS.get("Route 110", set()),
*LOCATION_GROUPS.get("Route 111", set()),
*LOCATION_GROUPS.get("Route 112", set()),
*LOCATION_GROUPS.get("Route 113", set()),
*LOCATION_GROUPS.get("Route 114", set()),
*LOCATION_GROUPS.get("Route 115", set()),
*LOCATION_GROUPS.get("Route 116", set()),
*LOCATION_GROUPS.get("Route 117", set()),
*LOCATION_GROUPS.get("Route 118", set()),
*LOCATION_GROUPS.get("Route 119", set()),
*LOCATION_GROUPS.get("Route 120", set()),
*LOCATION_GROUPS.get("Route 121", set()),
*LOCATION_GROUPS.get("Route 122", set()),
*LOCATION_GROUPS.get("Route 123", set()),
*LOCATION_GROUPS.get("Route 124", set()),
*LOCATION_GROUPS.get("Route 125", set()),
*LOCATION_GROUPS.get("Route 126", set()),
*LOCATION_GROUPS.get("Route 127", set()),
*LOCATION_GROUPS.get("Route 128", set()),
*LOCATION_GROUPS.get("Route 129", set()),
*LOCATION_GROUPS.get("Route 130", set()),
*LOCATION_GROUPS.get("Route 131", set()),
*LOCATION_GROUPS.get("Route 132", set()),
*LOCATION_GROUPS.get("Route 133", set()),
*LOCATION_GROUPS.get("Route 134", set()),
}

View File

@ -1,7 +1,7 @@
""" """
Classes and functions related to AP items for Pokemon Emerald Classes and functions related to AP items for Pokemon Emerald
""" """
from typing import Dict, FrozenSet, Optional from typing import Dict, FrozenSet, Set, Optional
from BaseClasses import Item, ItemClassification from BaseClasses import Item, ItemClassification
@ -46,30 +46,6 @@ def create_item_label_to_code_map() -> Dict[str, int]:
return label_to_code_map return label_to_code_map
ITEM_GROUPS = {
"Badges": {
"Stone Badge", "Knuckle Badge",
"Dynamo Badge", "Heat Badge",
"Balance Badge", "Feather Badge",
"Mind Badge", "Rain Badge",
},
"HMs": {
"HM01 Cut", "HM02 Fly",
"HM03 Surf", "HM04 Strength",
"HM05 Flash", "HM06 Rock Smash",
"HM07 Waterfall", "HM08 Dive",
},
"HM01": {"HM01 Cut"},
"HM02": {"HM02 Fly"},
"HM03": {"HM03 Surf"},
"HM04": {"HM04 Strength"},
"HM05": {"HM05 Flash"},
"HM06": {"HM06 Rock Smash"},
"HM07": {"HM07 Waterfall"},
"HM08": {"HM08 Dive"},
}
def get_item_classification(item_code: int) -> ItemClassification: def get_item_classification(item_code: int) -> ItemClassification:
""" """
Returns the item classification for a given AP item id (code) Returns the item classification for a given AP item id (code)

View File

@ -1,59 +1,17 @@
""" """
Classes and functions related to AP locations for Pokemon Emerald Classes and functions related to AP locations for Pokemon Emerald
""" """
from typing import TYPE_CHECKING, Dict, Optional, FrozenSet, Iterable from typing import TYPE_CHECKING, Dict, Optional, Set
from BaseClasses import Location, Region from BaseClasses import Location, Region
from .data import BASE_OFFSET, NATIONAL_ID_TO_SPECIES_ID, POKEDEX_OFFSET, data from .data import BASE_OFFSET, NATIONAL_ID_TO_SPECIES_ID, POKEDEX_OFFSET, LocationCategory, data
from .items import offset_item_value from .items import offset_item_value
if TYPE_CHECKING: if TYPE_CHECKING:
from . import PokemonEmeraldWorld from . import PokemonEmeraldWorld
LOCATION_GROUPS = {
"Badges": {
"Rustboro Gym - Stone Badge",
"Dewford Gym - Knuckle Badge",
"Mauville Gym - Dynamo Badge",
"Lavaridge Gym - Heat Badge",
"Petalburg Gym - Balance Badge",
"Fortree Gym - Feather Badge",
"Mossdeep Gym - Mind Badge",
"Sootopolis Gym - Rain Badge",
},
"Gym TMs": {
"Rustboro Gym - TM39 from Roxanne",
"Dewford Gym - TM08 from Brawly",
"Mauville Gym - TM34 from Wattson",
"Lavaridge Gym - TM50 from Flannery",
"Petalburg Gym - TM42 from Norman",
"Fortree Gym - TM40 from Winona",
"Mossdeep Gym - TM04 from Tate and Liza",
"Sootopolis Gym - TM03 from Juan",
},
"Trick House": {
"Trick House Puzzle 1 - Item",
"Trick House Puzzle 2 - Item 1",
"Trick House Puzzle 2 - Item 2",
"Trick House Puzzle 3 - Item 1",
"Trick House Puzzle 3 - Item 2",
"Trick House Puzzle 4 - Item",
"Trick House Puzzle 6 - Item",
"Trick House Puzzle 7 - Item",
"Trick House Puzzle 8 - Item",
"Trick House Puzzle 1 - Reward",
"Trick House Puzzle 2 - Reward",
"Trick House Puzzle 3 - Reward",
"Trick House Puzzle 4 - Reward",
"Trick House Puzzle 5 - Reward",
"Trick House Puzzle 6 - Reward",
"Trick House Puzzle 7 - Reward",
}
}
VISITED_EVENT_NAME_TO_ID = { VISITED_EVENT_NAME_TO_ID = {
"EVENT_VISITED_LITTLEROOT_TOWN": 0, "EVENT_VISITED_LITTLEROOT_TOWN": 0,
"EVENT_VISITED_OLDALE_TOWN": 1, "EVENT_VISITED_OLDALE_TOWN": 1,
@ -80,7 +38,7 @@ class PokemonEmeraldLocation(Location):
game: str = "Pokemon Emerald" game: str = "Pokemon Emerald"
item_address: Optional[int] item_address: Optional[int]
default_item_code: Optional[int] default_item_code: Optional[int]
tags: FrozenSet[str] key: Optional[str]
def __init__( def __init__(
self, self,
@ -88,13 +46,13 @@ class PokemonEmeraldLocation(Location):
name: str, name: str,
address: Optional[int], address: Optional[int],
parent: Optional[Region] = None, parent: Optional[Region] = None,
key: Optional[str] = None,
item_address: Optional[int] = None, item_address: Optional[int] = None,
default_item_value: Optional[int] = None, default_item_value: Optional[int] = None) -> None:
tags: FrozenSet[str] = frozenset()) -> None:
super().__init__(player, name, address, parent) super().__init__(player, name, address, parent)
self.default_item_code = None if default_item_value is None else offset_item_value(default_item_value) self.default_item_code = None if default_item_value is None else offset_item_value(default_item_value)
self.item_address = item_address self.item_address = item_address
self.tags = tags self.key = key
def offset_flag(flag: int) -> int: def offset_flag(flag: int) -> int:
@ -115,16 +73,14 @@ def reverse_offset_flag(location_id: int) -> int:
return location_id - BASE_OFFSET return location_id - BASE_OFFSET
def create_locations_with_tags(world: "PokemonEmeraldWorld", regions: Dict[str, Region], tags: Iterable[str]) -> None: def create_locations_by_category(world: "PokemonEmeraldWorld", regions: Dict[str, Region], categories: Set[LocationCategory]) -> None:
""" """
Iterates through region data and adds locations to the multiworld if Iterates through region data and adds locations to the multiworld if
those locations include any of the provided tags. those locations include any of the provided tags.
""" """
tags = set(tags)
for region_name, region_data in data.regions.items(): for region_name, region_data in data.regions.items():
region = regions[region_name] region = regions[region_name]
filtered_locations = [loc for loc in region_data.locations if len(tags & data.locations[loc].tags) > 0] filtered_locations = [loc for loc in region_data.locations if data.locations[loc].category in categories]
for location_name in filtered_locations: for location_name in filtered_locations:
location_data = data.locations[location_name] location_data = data.locations[location_name]
@ -144,9 +100,9 @@ def create_locations_with_tags(world: "PokemonEmeraldWorld", regions: Dict[str,
location_data.label, location_data.label,
location_id, location_id,
region, region,
location_name,
location_data.address, location_data.address,
location_data.default_item, location_data.default_item
location_data.tags
) )
region.locations.append(location) region.locations.append(location)

View File

@ -6,7 +6,8 @@ from typing import TYPE_CHECKING, Callable, Dict
from BaseClasses import CollectionState from BaseClasses import CollectionState
from worlds.generic.Rules import add_rule, set_rule from worlds.generic.Rules import add_rule, set_rule
from .data import NATIONAL_ID_TO_SPECIES_ID, NUM_REAL_SPECIES, data from .data import LocationCategory, NATIONAL_ID_TO_SPECIES_ID, NUM_REAL_SPECIES, data
from .locations import PokemonEmeraldLocation
from .options import DarkCavesRequireFlash, EliteFourRequirement, NormanRequirement, Goal from .options import DarkCavesRequireFlash, EliteFourRequirement, NormanRequirement, Goal
if TYPE_CHECKING: if TYPE_CHECKING:
@ -23,7 +24,7 @@ def set_rules(world: "PokemonEmeraldWorld") -> None:
state.has(hm, world.player) and state.has_all(badges, world.player) state.has(hm, world.player) and state.has_all(badges, world.player)
else: else:
hm_rules[hm] = lambda state, hm=hm, badges=badges: \ hm_rules[hm] = lambda state, hm=hm, badges=badges: \
state.has(hm, world.player) and state.has_group_unique("Badges", world.player, badges) state.has(hm, world.player) and state.has_group_unique("Badge", world.player, badges)
def has_acro_bike(state: CollectionState): def has_acro_bike(state: CollectionState):
return state.has("Acro Bike", world.player) return state.has("Acro Bike", world.player)
@ -236,11 +237,11 @@ def set_rules(world: "PokemonEmeraldWorld") -> None:
if world.options.norman_requirement == NormanRequirement.option_badges: if world.options.norman_requirement == NormanRequirement.option_badges:
set_rule( set_rule(
get_entrance("MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3"), get_entrance("MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3"),
lambda state: state.has_group_unique("Badges", world.player, world.options.norman_count.value) lambda state: state.has_group_unique("Badge", world.player, world.options.norman_count.value)
) )
set_rule( set_rule(
get_entrance("MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6"), get_entrance("MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6"),
lambda state: state.has_group_unique("Badges", world.player, world.options.norman_count.value) lambda state: state.has_group_unique("Badge", world.player, world.options.norman_count.value)
) )
else: else:
set_rule( set_rule(
@ -1506,7 +1507,7 @@ def set_rules(world: "PokemonEmeraldWorld") -> None:
if world.options.elite_four_requirement == EliteFourRequirement.option_badges: if world.options.elite_four_requirement == EliteFourRequirement.option_badges:
set_rule( set_rule(
get_entrance("REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/MAIN -> REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/BEHIND_BADGE_CHECKERS"), get_entrance("REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/MAIN -> REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/BEHIND_BADGE_CHECKERS"),
lambda state: state.has_group_unique("Badges", world.player, world.options.elite_four_count.value) lambda state: state.has_group_unique("Badge", world.player, world.options.elite_four_count.value)
) )
else: else:
set_rule( set_rule(
@ -1659,7 +1660,8 @@ def set_rules(world: "PokemonEmeraldWorld") -> None:
# Add Itemfinder requirement to hidden items # Add Itemfinder requirement to hidden items
if world.options.require_itemfinder: if world.options.require_itemfinder:
for location in world.multiworld.get_locations(world.player): for location in world.multiworld.get_locations(world.player):
if location.tags is not None and "HiddenItem" in location.tags: assert isinstance(location, PokemonEmeraldLocation)
if location.key is not None and data.locations[location.key].category == LocationCategory.HIDDEN_ITEM:
add_rule( add_rule(
location, location,
lambda state: state.has("Itemfinder", world.player) lambda state: state.has("Itemfinder", world.player)

View File

@ -5,8 +5,6 @@ duplicate claims and give warnings for unused and unignored locations or warps.
import logging import logging
from typing import List from typing import List
from .data import load_json_data, data
_IGNORABLE_LOCATIONS = frozenset({ _IGNORABLE_LOCATIONS = frozenset({
"HIDDEN_ITEM_TRICK_HOUSE_NUGGET", # Is permanently mssiable and has special behavior that sets the flag early "HIDDEN_ITEM_TRICK_HOUSE_NUGGET", # Is permanently mssiable and has special behavior that sets the flag early
@ -247,12 +245,29 @@ _IGNORABLE_WARPS = frozenset({
}) })
def validate_group_maps() -> bool:
from .data import data
from .groups import _LOCATION_GROUP_MAPS
failed = False
for group_name, map_set in _LOCATION_GROUP_MAPS.items():
for map_name in map_set:
if map_name not in data.maps:
failed = True
logging.error("Pokemon Emerald: Map named %s in location group %s does not exist", map_name, group_name)
return not failed
def validate_regions() -> bool: def validate_regions() -> bool:
""" """
Verifies that Emerald's data doesn't have duplicate or missing Verifies that Emerald's data doesn't have duplicate or missing
regions/warps/locations. Meant to catch problems during development like regions/warps/locations. Meant to catch problems during development like
forgetting to add a new location or incorrectly splitting a region. forgetting to add a new location or incorrectly splitting a region.
""" """
from .data import load_json_data, data
extracted_data_json = load_json_data("extracted_data.json") extracted_data_json = load_json_data("extracted_data.json")
error_messages: List[str] = [] error_messages: List[str] = []
warn_messages: List[str] = [] warn_messages: List[str] = []