mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
Pokemon Emerald: Randomize rock smash encounters (#3912)
* Pokemon Emerald: WIP add rock smash encounter randomization * Pokemon Emerald: Refactor encounter data on maps * Pokemon Emerald: Remove unused import * Pokemon Emerald: Swap StrEnum for regular Enum and use .value
This commit is contained in:
@@ -27,6 +27,7 @@ from .pokemon import (get_random_move, get_species_id_by_label, randomize_abilit
|
||||
randomize_legendary_encounters, randomize_misc_pokemon, randomize_starters,
|
||||
randomize_tm_hm_compatibility,randomize_types, randomize_wild_encounters)
|
||||
from .rom import PokemonEmeraldProcedurePatch, write_tokens
|
||||
from .util import get_encounter_type_label
|
||||
|
||||
|
||||
class PokemonEmeraldWebWorld(WebWorld):
|
||||
@@ -636,32 +637,11 @@ class PokemonEmeraldWorld(World):
|
||||
|
||||
spoiler_handle.write(f"\n\nWild Pokemon ({self.player_name}):\n\n")
|
||||
|
||||
slot_to_rod_suffix = {
|
||||
0: " (Old Rod)",
|
||||
1: " (Old Rod)",
|
||||
2: " (Good Rod)",
|
||||
3: " (Good Rod)",
|
||||
4: " (Good Rod)",
|
||||
5: " (Super Rod)",
|
||||
6: " (Super Rod)",
|
||||
7: " (Super Rod)",
|
||||
8: " (Super Rod)",
|
||||
9: " (Super Rod)",
|
||||
}
|
||||
|
||||
species_maps = defaultdict(set)
|
||||
for map in self.modified_maps.values():
|
||||
if map.land_encounters is not None:
|
||||
for encounter in map.land_encounters.slots:
|
||||
species_maps[encounter].add(map.label + " (Land)")
|
||||
|
||||
if map.water_encounters is not None:
|
||||
for encounter in map.water_encounters.slots:
|
||||
species_maps[encounter].add(map.label + " (Water)")
|
||||
|
||||
if map.fishing_encounters is not None:
|
||||
for slot, encounter in enumerate(map.fishing_encounters.slots):
|
||||
species_maps[encounter].add(map.label + slot_to_rod_suffix[slot])
|
||||
for map_data in self.modified_maps.values():
|
||||
for encounter_type, encounter_data in map_data.encounters.items():
|
||||
for i, encounter in enumerate(encounter_data.slots):
|
||||
species_maps[encounter].add(f"{map_data.label} ({get_encounter_type_label(encounter_type, i)})")
|
||||
|
||||
lines = [f"{emerald_data.species[species].label}: {', '.join(sorted(maps))}\n"
|
||||
for species, maps in species_maps.items()]
|
||||
@@ -675,32 +655,11 @@ class PokemonEmeraldWorld(World):
|
||||
if self.options.dexsanity:
|
||||
from collections import defaultdict
|
||||
|
||||
slot_to_rod_suffix = {
|
||||
0: " (Old Rod)",
|
||||
1: " (Old Rod)",
|
||||
2: " (Good Rod)",
|
||||
3: " (Good Rod)",
|
||||
4: " (Good Rod)",
|
||||
5: " (Super Rod)",
|
||||
6: " (Super Rod)",
|
||||
7: " (Super Rod)",
|
||||
8: " (Super Rod)",
|
||||
9: " (Super Rod)",
|
||||
}
|
||||
|
||||
species_maps = defaultdict(set)
|
||||
for map in self.modified_maps.values():
|
||||
if map.land_encounters is not None:
|
||||
for encounter in map.land_encounters.slots:
|
||||
species_maps[encounter].add(map.label + " (Land)")
|
||||
|
||||
if map.water_encounters is not None:
|
||||
for encounter in map.water_encounters.slots:
|
||||
species_maps[encounter].add(map.label + " (Water)")
|
||||
|
||||
if map.fishing_encounters is not None:
|
||||
for slot, encounter in enumerate(map.fishing_encounters.slots):
|
||||
species_maps[encounter].add(map.label + slot_to_rod_suffix[slot])
|
||||
for map_data in self.modified_maps.values():
|
||||
for encounter_type, encounter_data in map_data.encounters.items():
|
||||
for i, encounter in enumerate(encounter_data.slots):
|
||||
species_maps[encounter].add(f"{map_data.label} ({get_encounter_type_label(encounter_type, i)})")
|
||||
|
||||
hint_data[self.player] = {
|
||||
self.location_name_to_id[f"Pokedex - {emerald_data.species[species].label}"]: ", ".join(sorted(maps))
|
||||
|
@@ -5,7 +5,7 @@ defined data (like location labels or usable pokemon species), some cleanup
|
||||
and sorting, and Warp methods.
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
from enum import IntEnum, Enum
|
||||
import orjson
|
||||
from typing import Dict, List, NamedTuple, Optional, Set, FrozenSet, Tuple, Any, Union
|
||||
import pkgutil
|
||||
@@ -148,14 +148,20 @@ class EncounterTableData(NamedTuple):
|
||||
address: int
|
||||
|
||||
|
||||
# class EncounterType(StrEnum): # StrEnum introduced in python 3.11
|
||||
class EncounterType(Enum):
|
||||
LAND = "LAND"
|
||||
WATER = "WATER"
|
||||
FISHING = "FISHING"
|
||||
ROCK_SMASH = "ROCK_SMASH"
|
||||
|
||||
|
||||
@dataclass
|
||||
class MapData:
|
||||
name: str
|
||||
label: str
|
||||
header_address: int
|
||||
land_encounters: Optional[EncounterTableData]
|
||||
water_encounters: Optional[EncounterTableData]
|
||||
fishing_encounters: Optional[EncounterTableData]
|
||||
encounters: Dict[EncounterType, EncounterTableData]
|
||||
|
||||
|
||||
class EventData(NamedTuple):
|
||||
@@ -348,25 +354,27 @@ def _init() -> None:
|
||||
if map_name in IGNORABLE_MAPS:
|
||||
continue
|
||||
|
||||
land_encounters = None
|
||||
water_encounters = None
|
||||
fishing_encounters = None
|
||||
|
||||
encounter_tables: Dict[EncounterType, EncounterTableData] = {}
|
||||
if "land_encounters" in map_json:
|
||||
land_encounters = EncounterTableData(
|
||||
encounter_tables[EncounterType.LAND] = EncounterTableData(
|
||||
map_json["land_encounters"]["slots"],
|
||||
map_json["land_encounters"]["address"]
|
||||
)
|
||||
if "water_encounters" in map_json:
|
||||
water_encounters = EncounterTableData(
|
||||
encounter_tables[EncounterType.WATER] = EncounterTableData(
|
||||
map_json["water_encounters"]["slots"],
|
||||
map_json["water_encounters"]["address"]
|
||||
)
|
||||
if "fishing_encounters" in map_json:
|
||||
fishing_encounters = EncounterTableData(
|
||||
encounter_tables[EncounterType.FISHING] = EncounterTableData(
|
||||
map_json["fishing_encounters"]["slots"],
|
||||
map_json["fishing_encounters"]["address"]
|
||||
)
|
||||
if "rock_smash_encounters" in map_json:
|
||||
encounter_tables[EncounterType.ROCK_SMASH] = EncounterTableData(
|
||||
map_json["rock_smash_encounters"]["slots"],
|
||||
map_json["rock_smash_encounters"]["address"]
|
||||
)
|
||||
|
||||
# Derive a user-facing label
|
||||
label = []
|
||||
@@ -398,9 +406,7 @@ def _init() -> None:
|
||||
map_name,
|
||||
" ".join(label),
|
||||
map_json["header_address"],
|
||||
land_encounters,
|
||||
water_encounters,
|
||||
fishing_encounters
|
||||
encounter_tables
|
||||
)
|
||||
|
||||
# Load/merge region json files
|
||||
|
File diff suppressed because one or more lines are too long
@@ -4,7 +4,8 @@ Functions related to pokemon species and moves
|
||||
import functools
|
||||
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Tuple
|
||||
|
||||
from .data import (NUM_REAL_SPECIES, OUT_OF_LOGIC_MAPS, EncounterTableData, LearnsetMove, SpeciesData, data)
|
||||
from .data import (NUM_REAL_SPECIES, OUT_OF_LOGIC_MAPS, EncounterType, EncounterTableData, LearnsetMove, SpeciesData,
|
||||
MapData, data)
|
||||
from .options import (Goal, HmCompatibility, LevelUpMoves, RandomizeAbilities, RandomizeLegendaryEncounters,
|
||||
RandomizeMiscPokemon, RandomizeStarters, RandomizeTypes, RandomizeWildPokemon,
|
||||
TmTutorCompatibility)
|
||||
@@ -226,6 +227,42 @@ def randomize_types(world: "PokemonEmeraldWorld") -> None:
|
||||
evolutions += [world.modified_species[evo.species_id] for evo in evolution.evolutions]
|
||||
|
||||
|
||||
_encounter_subcategory_ranges: Dict[EncounterType, Dict[range, Optional[str]]] = {
|
||||
EncounterType.LAND: {range(0, 12): None},
|
||||
EncounterType.WATER: {range(0, 5): None},
|
||||
EncounterType.FISHING: {range(0, 2): "OLD_ROD", range(2, 5): "GOOD_ROD", range(5, 10): "SUPER_ROD"},
|
||||
}
|
||||
|
||||
|
||||
def _rename_wild_events(world: "PokemonEmeraldWorld", map_data: MapData, new_slots: List[int], encounter_type: EncounterType):
|
||||
"""
|
||||
Renames the events that correspond to wild encounters to reflect the new species there after randomization
|
||||
"""
|
||||
for i, new_species_id in enumerate(new_slots):
|
||||
# Get the subcategory for rods
|
||||
subcategory_range, subcategory_name = next(
|
||||
(r, sc)
|
||||
for r, sc in _encounter_subcategory_ranges[encounter_type].items()
|
||||
if i in r
|
||||
)
|
||||
subcategory_species = []
|
||||
for k in subcategory_range:
|
||||
if new_slots[k] not in subcategory_species:
|
||||
subcategory_species.append(new_slots[k])
|
||||
|
||||
# Create the name of the location that corresponds to this encounter slot
|
||||
# Fishing locations include the rod name
|
||||
subcategory_str = "" if subcategory_name is None else "_" + subcategory_name
|
||||
encounter_location_index = subcategory_species.index(new_species_id) + 1
|
||||
encounter_location_name = f"{map_data.name}_{encounter_type.value}_ENCOUNTERS{subcategory_str}_{encounter_location_index}"
|
||||
try:
|
||||
# Get the corresponding location and change the event name to reflect the new species
|
||||
slot_location = world.multiworld.get_location(encounter_location_name, world.player)
|
||||
slot_location.item.name = f"CATCH_{data.species[new_species_id].name}"
|
||||
except KeyError:
|
||||
pass # Map probably isn't included; should be careful here about bad encounter location names
|
||||
|
||||
|
||||
def randomize_wild_encounters(world: "PokemonEmeraldWorld") -> None:
|
||||
if world.options.wild_pokemon == RandomizeWildPokemon.option_vanilla:
|
||||
return
|
||||
@@ -253,120 +290,96 @@ def randomize_wild_encounters(world: "PokemonEmeraldWorld") -> None:
|
||||
placed_priority_species = False
|
||||
map_data = world.modified_maps[map_name]
|
||||
|
||||
new_encounters: List[Optional[EncounterTableData]] = [None, None, None]
|
||||
old_encounters = [map_data.land_encounters, map_data.water_encounters, map_data.fishing_encounters]
|
||||
new_encounters: Dict[EncounterType, EncounterTableData] = {}
|
||||
|
||||
for i, table in enumerate(old_encounters):
|
||||
if table is not None:
|
||||
# Create a map from the original species to new species
|
||||
# instead of just randomizing every slot.
|
||||
# Force area 1-to-1 mapping, in other words.
|
||||
species_old_to_new_map: Dict[int, int] = {}
|
||||
for species_id in table.slots:
|
||||
if species_id not in species_old_to_new_map:
|
||||
if not placed_priority_species and len(priority_species) > 0 \
|
||||
and map_name not in OUT_OF_LOGIC_MAPS:
|
||||
new_species_id = priority_species.pop()
|
||||
placed_priority_species = True
|
||||
else:
|
||||
original_species = data.species[species_id]
|
||||
for encounter_type, table in map_data.encounters.items():
|
||||
# Create a map from the original species to new species
|
||||
# instead of just randomizing every slot.
|
||||
# Force area 1-to-1 mapping, in other words.
|
||||
species_old_to_new_map: Dict[int, int] = {}
|
||||
for species_id in table.slots:
|
||||
if species_id not in species_old_to_new_map:
|
||||
if not placed_priority_species and len(priority_species) > 0 \
|
||||
and encounter_type != EncounterType.ROCK_SMASH and map_name not in OUT_OF_LOGIC_MAPS:
|
||||
new_species_id = priority_species.pop()
|
||||
placed_priority_species = True
|
||||
else:
|
||||
original_species = data.species[species_id]
|
||||
|
||||
# Construct progressive tiers of blacklists that can be peeled back if they
|
||||
# collectively cover too much of the pokedex. A lower index in `blacklists`
|
||||
# indicates a more important set of species to avoid. Entries at `0` will
|
||||
# always be blacklisted.
|
||||
blacklists: Dict[int, List[Set[int]]] = defaultdict(list)
|
||||
# Construct progressive tiers of blacklists that can be peeled back if they
|
||||
# collectively cover too much of the pokedex. A lower index in `blacklists`
|
||||
# indicates a more important set of species to avoid. Entries at `0` will
|
||||
# always be blacklisted.
|
||||
blacklists: Dict[int, List[Set[int]]] = defaultdict(list)
|
||||
|
||||
# Blacklist pokemon already on this table
|
||||
blacklists[0].append(set(species_old_to_new_map.values()))
|
||||
# Blacklist pokemon already on this table
|
||||
blacklists[0].append(set(species_old_to_new_map.values()))
|
||||
|
||||
# If doing legendary hunt, blacklist Latios from wild encounters so
|
||||
# it can be tracked as the roamer. Otherwise it may be impossible
|
||||
# to tell whether a highlighted route is the roamer or a wild
|
||||
# encounter.
|
||||
if world.options.goal == Goal.option_legendary_hunt:
|
||||
blacklists[0].append({data.constants["SPECIES_LATIOS"]})
|
||||
# If doing legendary hunt, blacklist Latios from wild encounters so
|
||||
# it can be tracked as the roamer. Otherwise it may be impossible
|
||||
# to tell whether a highlighted route is the roamer or a wild
|
||||
# encounter.
|
||||
if world.options.goal == Goal.option_legendary_hunt:
|
||||
blacklists[0].append({data.constants["SPECIES_LATIOS"]})
|
||||
|
||||
# If dexsanity/catch 'em all mode, blacklist already placed species
|
||||
# until every species has been placed once
|
||||
if world.options.dexsanity and len(already_placed) < num_placeable_species:
|
||||
blacklists[1].append(already_placed)
|
||||
# If dexsanity/catch 'em all mode, blacklist already placed species
|
||||
# until every species has been placed once
|
||||
if world.options.dexsanity and len(already_placed) < num_placeable_species:
|
||||
blacklists[1].append(already_placed)
|
||||
|
||||
# Blacklist from player options
|
||||
blacklists[2].append(world.blacklisted_wilds)
|
||||
# Blacklist from player options
|
||||
blacklists[2].append(world.blacklisted_wilds)
|
||||
|
||||
# Type matching blacklist
|
||||
if should_match_type:
|
||||
blacklists[3].append({
|
||||
species.species_id
|
||||
for species in world.modified_species.values()
|
||||
if not bool(set(species.types) & set(original_species.types))
|
||||
})
|
||||
|
||||
merged_blacklist: Set[int] = set()
|
||||
for max_priority in reversed(sorted(blacklists.keys())):
|
||||
merged_blacklist = set()
|
||||
for priority in blacklists.keys():
|
||||
if priority <= max_priority:
|
||||
for blacklist in blacklists[priority]:
|
||||
merged_blacklist |= blacklist
|
||||
|
||||
if len(merged_blacklist) < NUM_REAL_SPECIES:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("This should never happen")
|
||||
|
||||
candidates = [
|
||||
species
|
||||
# Type matching blacklist
|
||||
if should_match_type:
|
||||
blacklists[3].append({
|
||||
species.species_id
|
||||
for species in world.modified_species.values()
|
||||
if species.species_id not in merged_blacklist
|
||||
]
|
||||
if not bool(set(species.types) & set(original_species.types))
|
||||
})
|
||||
|
||||
if should_match_bst:
|
||||
candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
|
||||
merged_blacklist: Set[int] = set()
|
||||
for max_priority in reversed(sorted(blacklists.keys())):
|
||||
merged_blacklist = set()
|
||||
for priority in blacklists.keys():
|
||||
if priority <= max_priority:
|
||||
for blacklist in blacklists[priority]:
|
||||
merged_blacklist |= blacklist
|
||||
|
||||
new_species_id = world.random.choice(candidates).species_id
|
||||
species_old_to_new_map[species_id] = new_species_id
|
||||
if len(merged_blacklist) < NUM_REAL_SPECIES:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("This should never happen")
|
||||
|
||||
if world.options.dexsanity and map_name not in OUT_OF_LOGIC_MAPS:
|
||||
already_placed.add(new_species_id)
|
||||
candidates = [
|
||||
species
|
||||
for species in world.modified_species.values()
|
||||
if species.species_id not in merged_blacklist
|
||||
]
|
||||
|
||||
# Actually create the new list of slots and encounter table
|
||||
new_slots: List[int] = []
|
||||
for species_id in table.slots:
|
||||
new_slots.append(species_old_to_new_map[species_id])
|
||||
if should_match_bst:
|
||||
candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
|
||||
|
||||
new_encounters[i] = EncounterTableData(new_slots, table.address)
|
||||
new_species_id = world.random.choice(candidates).species_id
|
||||
|
||||
# Rename event items for the new wild pokemon species
|
||||
slot_category: Tuple[str, List[Tuple[Optional[str], range]]] = [
|
||||
("LAND", [(None, range(0, 12))]),
|
||||
("WATER", [(None, range(0, 5))]),
|
||||
("FISHING", [("OLD_ROD", range(0, 2)), ("GOOD_ROD", range(2, 5)), ("SUPER_ROD", range(5, 10))]),
|
||||
][i]
|
||||
for j, new_species_id in enumerate(new_slots):
|
||||
# Get the subcategory for rods
|
||||
subcategory = next(sc for sc in slot_category[1] if j in sc[1])
|
||||
subcategory_species = []
|
||||
for k in subcategory[1]:
|
||||
if new_slots[k] not in subcategory_species:
|
||||
subcategory_species.append(new_slots[k])
|
||||
species_old_to_new_map[species_id] = new_species_id
|
||||
|
||||
# Create the name of the location that corresponds to this encounter slot
|
||||
# Fishing locations include the rod name
|
||||
subcategory_str = "" if subcategory[0] is None else "_" + subcategory[0]
|
||||
encounter_location_index = subcategory_species.index(new_species_id) + 1
|
||||
encounter_location_name = f"{map_data.name}_{slot_category[0]}_ENCOUNTERS{subcategory_str}_{encounter_location_index}"
|
||||
try:
|
||||
# Get the corresponding location and change the event name to reflect the new species
|
||||
slot_location = world.multiworld.get_location(encounter_location_name, world.player)
|
||||
slot_location.item.name = f"CATCH_{data.species[new_species_id].name}"
|
||||
except KeyError:
|
||||
pass # Map probably isn't included; should be careful here about bad encounter location names
|
||||
if world.options.dexsanity and encounter_type != EncounterType.ROCK_SMASH \
|
||||
and map_name not in OUT_OF_LOGIC_MAPS:
|
||||
already_placed.add(new_species_id)
|
||||
|
||||
map_data.land_encounters = new_encounters[0]
|
||||
map_data.water_encounters = new_encounters[1]
|
||||
map_data.fishing_encounters = new_encounters[2]
|
||||
# Actually create the new list of slots and encounter table
|
||||
new_slots: List[int] = []
|
||||
for species_id in table.slots:
|
||||
new_slots.append(species_old_to_new_map[species_id])
|
||||
|
||||
new_encounters[encounter_type] = EncounterTableData(new_slots, table.address)
|
||||
|
||||
# Rock smash encounters not used in logic, so they have no events
|
||||
if encounter_type != EncounterType.ROCK_SMASH:
|
||||
_rename_wild_events(world, map_data, new_slots, encounter_type)
|
||||
|
||||
map_data.encounters = new_encounters
|
||||
|
||||
|
||||
def randomize_abilities(world: "PokemonEmeraldWorld") -> None:
|
||||
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from BaseClasses import CollectionState, ItemClassification, Region
|
||||
|
||||
from .data import data
|
||||
from .data import EncounterType, data
|
||||
from .items import PokemonEmeraldItem
|
||||
from .locations import PokemonEmeraldLocation
|
||||
|
||||
@@ -19,11 +19,11 @@ def create_regions(world: "PokemonEmeraldWorld") -> Dict[str, Region]:
|
||||
Also creates and places events and connects regions via warps and the exits defined in the JSON.
|
||||
"""
|
||||
# Used in connect_to_map_encounters. Splits encounter categories into "subcategories" and gives them names
|
||||
# and rules so the rods can only access their specific slots.
|
||||
encounter_categories: Dict[str, List[Tuple[Optional[str], range, Optional[Callable[[CollectionState], bool]]]]] = {
|
||||
"LAND": [(None, range(0, 12), None)],
|
||||
"WATER": [(None, range(0, 5), None)],
|
||||
"FISHING": [
|
||||
# and rules so the rods can only access their specific slots. Rock smash encounters are not considered in logic.
|
||||
encounter_categories: Dict[EncounterType, List[Tuple[Optional[str], range, Optional[Callable[[CollectionState], bool]]]]] = {
|
||||
EncounterType.LAND: [(None, range(0, 12), None)],
|
||||
EncounterType.WATER: [(None, range(0, 5), None)],
|
||||
EncounterType.FISHING: [
|
||||
("OLD_ROD", range(0, 2), lambda state: state.has("Old Rod", world.player)),
|
||||
("GOOD_ROD", range(2, 5), lambda state: state.has("Good Rod", world.player)),
|
||||
("SUPER_ROD", range(5, 10), lambda state: state.has("Super Rod", world.player)),
|
||||
@@ -41,19 +41,19 @@ def create_regions(world: "PokemonEmeraldWorld") -> Dict[str, Region]:
|
||||
These regions are created lazily and dynamically so as not to bother with unused maps.
|
||||
"""
|
||||
# For each of land, water, and fishing, connect the region if indicated by include_slots
|
||||
for i, encounter_category in enumerate(encounter_categories.items()):
|
||||
for i, (encounter_type, subcategories) in enumerate(encounter_categories.items()):
|
||||
if include_slots[i]:
|
||||
region_name = f"{map_name}_{encounter_category[0]}_ENCOUNTERS"
|
||||
region_name = f"{map_name}_{encounter_type.value}_ENCOUNTERS"
|
||||
|
||||
# If the region hasn't been created yet, create it now
|
||||
try:
|
||||
encounter_region = world.multiworld.get_region(region_name, world.player)
|
||||
except KeyError:
|
||||
encounter_region = Region(region_name, world.player, world.multiworld)
|
||||
encounter_slots = getattr(data.maps[map_name], f"{encounter_category[0].lower()}_encounters").slots
|
||||
encounter_slots = data.maps[map_name].encounters[encounter_type].slots
|
||||
|
||||
# Subcategory is for splitting fishing rods; land and water only have one subcategory
|
||||
for subcategory in encounter_category[1]:
|
||||
for subcategory in subcategories:
|
||||
# Want to create locations per species, not per slot
|
||||
# encounter_categories includes info on which slots belong to which subcategory
|
||||
unique_species = []
|
||||
|
@@ -696,12 +696,10 @@ def _set_encounter_tables(world: "PokemonEmeraldWorld", patch: PokemonEmeraldPro
|
||||
}
|
||||
"""
|
||||
for map_data in world.modified_maps.values():
|
||||
tables = [map_data.land_encounters, map_data.water_encounters, map_data.fishing_encounters]
|
||||
for table in tables:
|
||||
if table is not None:
|
||||
for i, species_id in enumerate(table.slots):
|
||||
address = table.address + 2 + (4 * i)
|
||||
patch.write_token(APTokenTypes.WRITE, address, struct.pack("<H", species_id))
|
||||
for table in map_data.encounters.values():
|
||||
for i, species_id in enumerate(table.slots):
|
||||
address = table.address + 2 + (4 * i)
|
||||
patch.write_token(APTokenTypes.WRITE, address, struct.pack("<H", species_id))
|
||||
|
||||
|
||||
def _set_species_info(world: "PokemonEmeraldWorld", patch: PokemonEmeraldProcedurePatch, easter_egg: Tuple[int, int]) -> None:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import orjson
|
||||
from typing import Any, Dict, List, Optional, Tuple, Iterable
|
||||
|
||||
from .data import NATIONAL_ID_TO_SPECIES_ID, data
|
||||
from .data import NATIONAL_ID_TO_SPECIES_ID, EncounterType, data
|
||||
|
||||
|
||||
CHARACTER_DECODING_MAP = {
|
||||
@@ -86,6 +86,28 @@ def decode_string(string_data: Iterable[int]) -> str:
|
||||
return string
|
||||
|
||||
|
||||
def get_encounter_type_label(encounter_type: EncounterType, slot: int) -> str:
|
||||
if encounter_type == EncounterType.FISHING:
|
||||
return {
|
||||
0: "Old Rod",
|
||||
1: "Old Rod",
|
||||
2: "Good Rod",
|
||||
3: "Good Rod",
|
||||
4: "Good Rod",
|
||||
5: "Super Rod",
|
||||
6: "Super Rod",
|
||||
7: "Super Rod",
|
||||
8: "Super Rod",
|
||||
9: "Super Rod",
|
||||
}[slot]
|
||||
|
||||
return {
|
||||
EncounterType.LAND: 'Land',
|
||||
EncounterType.WATER: 'Water',
|
||||
EncounterType.ROCK_SMASH: 'Rock Smash',
|
||||
}[encounter_type]
|
||||
|
||||
|
||||
def get_easter_egg(easter_egg: str) -> Tuple[int, int]:
|
||||
easter_egg = easter_egg.upper()
|
||||
result1 = 0
|
||||
|
Reference in New Issue
Block a user