Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 20 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ff043b6
Pokemon Emerald: Rework location tags to categories
Zunawe Apr 30, 2024
21e6c79
Pokemon Emerald: Rework item tags, automatically create item/location…
Zunawe May 1, 2024
9116def
Pokemon Emerald: Move item and location groups to data.py, add some r…
Zunawe May 1, 2024
bfef626
Map Regions
Tsukino-uwu May 2, 2024
bc4df45
Merge pull request #3 from Tsukino-uwu/emerald-groups
Zunawe May 4, 2024
915df9b
Pokemon Emerald: Fix up location groups
Zunawe May 4, 2024
33d7d4d
Pokemon Emerald: Move groups to their own file
Zunawe May 4, 2024
a5b5f47
Pokemon Emerald: Add meta groups for location groups
Zunawe May 4, 2024
c3e3234
Pokemon Emerald: Fix has_group using updated item group name
Zunawe May 4, 2024
5d5543f
Pokemon Emerald: Add sanity check for maps in location groups
Zunawe May 4, 2024
a89d82f
Pokemon Emerald: Remove missed use of location.tags
Zunawe May 4, 2024
89f3c2b
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe May 5, 2024
6c006a1
Pokemon Emerald: Reclassify white and black flutes
Zunawe May 5, 2024
a0a7ba5
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe May 17, 2024
759fc95
Pokemon Emerald: Update changelog
Zunawe May 17, 2024
1f12402
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe Jun 1, 2024
663f687
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe Jul 1, 2024
a1dfd70
Pokemon Emerald: Adjust changelog
Zunawe Jul 1, 2024
341f0cb
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe Sep 8, 2024
3947695
Merge remote-tracking branch 'upstream/main' into emerald-groups
Zunawe Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions worlds/pokemon_emerald/CHANGELOG.md
Zunawe marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- Added many new item and location groups.
- Added a Swedish translation of the setup guide.
- 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.
Expand Down
83 changes: 47 additions & 36 deletions worlds/pokemon_emerald/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
from worlds.AutoWorld import WebWorld, World

from .client import PokemonEmeraldClient # Unused, but required to register with BizHawkClient
from .data import LEGENDARY_POKEMON, MapData, SpeciesData, TrainerData, data as emerald_data
from .items import (ITEM_GROUPS, 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,
create_locations_with_tags, set_free_fly, set_legendary_cave_entrances)
from .data import LEGENDARY_POKEMON, MapData, SpeciesData, TrainerData, LocationCategory, data as emerald_data
from .groups import ITEM_GROUPS, LOCATION_GROUPS
from .items import PokemonEmeraldItem, create_item_label_to_code_map, get_item_classification, offset_item_value
from .locations import (PokemonEmeraldLocation, create_location_label_to_id_map, create_locations_by_category,
set_free_fly, set_legendary_cave_entrances)
from .opponents import randomize_opponent_parties
from .options import (Goal, DarkCavesRequireFlash, HmRequirements, ItemPoolType, PokemonEmeraldOptions,
RandomizeWildPokemon, RandomizeBadges, RandomizeHms, NormanRequirement)
Expand Down Expand Up @@ -133,9 +133,10 @@ def __init__(self, multiworld, player):

@classmethod
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_group_maps()

def get_filler_item_name(self) -> str:
return "Great Ball"
Expand Down Expand Up @@ -237,24 +238,32 @@ def generate_early(self) -> None:

def create_regions(self) -> None:
from .regions import create_regions
regions = create_regions(self)

tags = {"Badge", "HM", "KeyItem", "Rod", "Bike", "EventTicket"} # Tags with progression items always included
all_regions = create_regions(self)

# Categories with progression items always included
categories = {
LocationCategory.BADGE,
LocationCategory.HM,
LocationCategory.KEY,
LocationCategory.ROD,
LocationCategory.BIKE,
LocationCategory.TICKET
}
if self.options.overworld_items:
tags.add("OverworldItem")
categories.add(LocationCategory.OVERWORLD_ITEM)
if self.options.hidden_items:
tags.add("HiddenItem")
categories.add(LocationCategory.HIDDEN_ITEM)
if self.options.npc_gifts:
tags.add("NpcGift")
categories.add(LocationCategory.GIFT)
if self.options.berry_trees:
tags.add("BerryTree")
categories.add(LocationCategory.BERRY_TREE)
if self.options.dexsanity:
tags.add("Pokedex")
categories.add(LocationCategory.POKEDEX)
if self.options.trainersanity:
tags.add("Trainer")
create_locations_with_tags(self, regions, tags)
categories.add(LocationCategory.TRAINER)
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
def exclude_locations(location_names: List[str]):
Expand Down Expand Up @@ -325,39 +334,39 @@ def create_items(self) -> None:
# Filter progression items which shouldn't be shuffled into the itempool.
# Their locations will still exist, but event items will be placed and
# locked at their vanilla locations instead.
filter_tags = set()
filter_categories = set()

if not self.options.key_items:
filter_tags.add("KeyItem")
filter_categories.add(LocationCategory.KEY)
if not self.options.rods:
filter_tags.add("Rod")
filter_categories.add(LocationCategory.ROD)
if not self.options.bikes:
filter_tags.add("Bike")
filter_categories.add(LocationCategory.BIKE)
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}:
filter_tags.add("Badge")
filter_categories.add(LocationCategory.BADGE)
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
# the normal item pool, but do create their items and save them and
# their locations for use in `pre_fill` later.
if self.options.badges == RandomizeBadges.option_shuffle:
self.badge_shuffle_info = [
(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:
self.hm_shuffle_info = [
(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
# 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]

# Take the itempool as-is
Expand All @@ -366,7 +375,8 @@ def create_items(self) -> None:

# Recreate the itempool from random items
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
item_category_counter = Counter()
Expand Down Expand Up @@ -436,25 +446,26 @@ def generate_basic(self) -> None:

# 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.
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):
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.progress_type = LocationProgressType.DEFAULT
location.address = None

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:
convert_unrandomized_items_to_events("HM")
convert_unrandomized_items_to_events(LocationCategory.HM)
if not self.options.rods:
convert_unrandomized_items_to_events("Rod")
convert_unrandomized_items_to_events(LocationCategory.ROD)
if not self.options.bikes:
convert_unrandomized_items_to_events("Bike")
convert_unrandomized_items_to_events(LocationCategory.BIKE)
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:
convert_unrandomized_items_to_events("KeyItem")
convert_unrandomized_items_to_events(LocationCategory.KEY)

def pre_fill(self) -> None:
# Badges and HMs that are set to shuffle need to be placed at
Expand Down
19 changes: 19 additions & 0 deletions worlds/pokemon_emerald/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,29 @@ class ItemData(NamedTuple):
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):
name: str
label: str
parent_region: str
default_item: int
address: Union[int, List[int]]
flag: int
category: LocationCategory
tags: FrozenSet[str]


Expand Down Expand Up @@ -431,6 +447,7 @@ def _init() -> None:
location_json["default_item"],
[location_json["address"]] + [j["address"] for j in alternate_rival_jsons],
location_json["flag"],
LocationCategory[location_attributes_json[location_name]["category"]],
frozenset(location_attributes_json[location_name]["tags"])
)
else:
Expand All @@ -441,6 +458,7 @@ def _init() -> None:
location_json["default_item"],
location_json["address"],
location_json["flag"],
LocationCategory[location_attributes_json[location_name]["category"]],
frozenset(location_attributes_json[location_name]["tags"])
)
new_region.locations.append(location_name)
Expand Down Expand Up @@ -948,6 +966,7 @@ def _init() -> None:
evo_stage_to_ball_map[evo_stage],
data.locations[dex_location_name].address,
data.locations[dex_location_name].flag,
data.locations[dex_location_name].category,
data.locations[dex_location_name].tags
)

Expand Down
Loading
Loading