Pokemon R/B: Version 5 Update (#3566)

* Quiz updates

* Enable Partial Trainersanity

* Losable Key Items Still Count

* New options api

* Type Chart Seed

* Continue switching to new options API

* Level Scaling and Quiz fixes

* Level Scaling and Quiz fixes

* Clarify that palettes are only for Super Gameboy

* Type chart seed groups use one random players' options

* remove goal option again

* Text updates

* Trainersanity Trainers ignore Blind Trainers setting

* Re-order simple connecting interiors so that directions are preserved when possible

* Dexsanity exact number

* Year update

* Dexsanity Doc update

* revert accidental file deletion

* Fixes

* Add world parameter to logic calls

* restore correct seeded random object

* missing world.options changes

* Trainersanity table bug fix

* delete entrances as well as exits when restarting door shuffle

* Do not collect route 25 item for level scaling if trainer is trainersanity

* world.options in level_scaling.py

* Update worlds/pokemon_rb/level_scaling.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/pokemon_rb/encounters.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/pokemon_rb/encounters.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* world -> multiworld

* Fix Cerulean Cave Hidden Item Center Rocks region

* Fix Cerulean Cave Hidden Item Center Rocks region for real

* Remove "self-locking" rules

* Update worlds/pokemon_rb/regions.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Fossil events

* Update worlds/pokemon_rb/level_scaling.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

---------

Co-authored-by: alchav <alchav@jalchavware.com>
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
Alchav 2024-09-18 14:37:17 -04:00 committed by GitHub
parent 51a6dc150c
commit db5d9fbf70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1435 additions and 1335 deletions

View File

@ -3,6 +3,7 @@ import settings
import typing
import threading
import base64
import random
from copy import deepcopy
from typing import TextIO
@ -14,7 +15,7 @@ from worlds.generic.Rules import add_item_rule
from .items import item_table, item_groups
from .locations import location_data, PokemonRBLocation
from .regions import create_regions
from .options import pokemon_rb_options
from .options import PokemonRBOptions
from .rom_addresses import rom_addresses
from .text import encode_text
from .rom import generate_output, get_base_rom_bytes, get_base_rom_path, RedDeltaPatch, BlueDeltaPatch
@ -71,7 +72,10 @@ class PokemonRedBlueWorld(World):
Elite Four to become the champion!"""
# -MuffinJets#4559
game = "Pokemon Red and Blue"
option_definitions = pokemon_rb_options
options_dataclass = PokemonRBOptions
options: PokemonRBOptions
settings: typing.ClassVar[PokemonSettings]
required_client_version = (0, 4, 2)
@ -85,8 +89,8 @@ class PokemonRedBlueWorld(World):
web = PokemonWebWorld()
def __init__(self, world: MultiWorld, player: int):
super().__init__(world, player)
def __init__(self, multiworld: MultiWorld, player: int):
super().__init__(multiworld, player)
self.item_pool = []
self.total_key_items = None
self.fly_map = None
@ -101,11 +105,11 @@ class PokemonRedBlueWorld(World):
self.learnsets = None
self.trainer_name = None
self.rival_name = None
self.type_chart = None
self.traps = None
self.trade_mons = {}
self.finished_level_scaling = threading.Event()
self.dexsanity_table = []
self.trainersanity_table = []
self.local_locs = []
@classmethod
@ -113,11 +117,109 @@ class PokemonRedBlueWorld(World):
versions = set()
for player in multiworld.player_ids:
if multiworld.worlds[player].game == "Pokemon Red and Blue":
versions.add(multiworld.game_version[player].current_key)
versions.add(multiworld.worlds[player].options.game_version.current_key)
for version in versions:
if not os.path.exists(get_base_rom_path(version)):
raise FileNotFoundError(get_base_rom_path(version))
@classmethod
def stage_generate_early(cls, multiworld: MultiWorld):
seed_groups = {}
pokemon_rb_worlds = multiworld.get_game_worlds("Pokemon Red and Blue")
for world in pokemon_rb_worlds:
if not (world.options.type_chart_seed.value.isdigit() or world.options.type_chart_seed.value == "random"):
seed_groups[world.options.type_chart_seed.value] = seed_groups.get(world.options.type_chart_seed.value,
[]) + [world]
copy_chart_worlds = {}
for worlds in seed_groups.values():
chosen_world = multiworld.random.choice(worlds)
for world in worlds:
if world is not chosen_world:
copy_chart_worlds[world.player] = chosen_world
for world in pokemon_rb_worlds:
if world.player in copy_chart_worlds:
continue
tc_random = world.random
if world.options.type_chart_seed.value.isdigit():
tc_random = random.Random()
tc_random.seed(int(world.options.type_chart_seed.value))
if world.options.randomize_type_chart == "vanilla":
chart = deepcopy(poke_data.type_chart)
elif world.options.randomize_type_chart == "randomize":
types = poke_data.type_names.values()
matchups = []
for type1 in types:
for type2 in types:
matchups.append([type1, type2])
tc_random.shuffle(matchups)
immunities = world.options.immunity_matchups.value
super_effectives = world.options.super_effective_matchups.value
not_very_effectives = world.options.not_very_effective_matchups.value
normals = world.options.normal_matchups.value
while super_effectives + not_very_effectives + normals < 225 - immunities:
if super_effectives == not_very_effectives == normals == 0:
super_effectives = 225
not_very_effectives = 225
normals = 225
else:
super_effectives += world.options.super_effective_matchups.value
not_very_effectives += world.options.not_very_effective_matchups.value
normals += world.options.normal_matchups.value
if super_effectives + not_very_effectives + normals > 225 - immunities:
total = super_effectives + not_very_effectives + normals
excess = total - (225 - immunities)
subtract_amounts = (
int((excess / (super_effectives + not_very_effectives + normals)) * super_effectives),
int((excess / (super_effectives + not_very_effectives + normals)) * not_very_effectives),
int((excess / (super_effectives + not_very_effectives + normals)) * normals))
super_effectives -= subtract_amounts[0]
not_very_effectives -= subtract_amounts[1]
normals -= subtract_amounts[2]
while super_effectives + not_very_effectives + normals > 225 - immunities:
r = tc_random.randint(0, 2)
if r == 0 and super_effectives:
super_effectives -= 1
elif r == 1 and not_very_effectives:
not_very_effectives -= 1
elif normals:
normals -= 1
chart = []
for matchup_list, matchup_value in zip([immunities, normals, super_effectives, not_very_effectives],
[0, 10, 20, 5]):
for _ in range(matchup_list):
matchup = matchups.pop()
matchup.append(matchup_value)
chart.append(matchup)
elif world.options.randomize_type_chart == "chaos":
types = poke_data.type_names.values()
matchups = []
for type1 in types:
for type2 in types:
matchups.append([type1, type2])
chart = []
values = list(range(21))
tc_random.shuffle(matchups)
tc_random.shuffle(values)
for matchup in matchups:
value = values.pop(0)
values.append(value)
matchup.append(value)
chart.append(matchup)
# sort so that super-effective matchups occur first, to prevent dual "not very effective" / "super effective"
# matchups from leading to damage being ultimately divided by 2 and then multiplied by 2, which can lead to
# damage being reduced by 1 which leads to a "not very effective" message appearing due to my changes
# to the way effectiveness messages are generated.
world.type_chart = sorted(chart, key=lambda matchup: -matchup[2])
for player in copy_chart_worlds:
multiworld.worlds[player].type_chart = copy_chart_worlds[player].type_chart
def generate_early(self):
def encode_name(name, t):
try:
@ -126,33 +228,33 @@ class PokemonRedBlueWorld(World):
return encode_text(name, length=8, whitespace="@", safety=True)
except KeyError as e:
raise KeyError(f"Invalid character(s) in {t} name for player {self.multiworld.player_name[self.player]}") from e
if self.multiworld.trainer_name[self.player] == "choose_in_game":
if self.options.trainer_name == "choose_in_game":
self.trainer_name = "choose_in_game"
else:
self.trainer_name = encode_name(self.multiworld.trainer_name[self.player].value, "Player")
if self.multiworld.rival_name[self.player] == "choose_in_game":
self.trainer_name = encode_name(self.options.trainer_name.value, "Player")
if self.options.rival_name == "choose_in_game":
self.rival_name = "choose_in_game"
else:
self.rival_name = encode_name(self.multiworld.rival_name[self.player].value, "Rival")
self.rival_name = encode_name(self.options.rival_name.value, "Rival")
if not self.multiworld.badgesanity[self.player]:
self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"]
if not self.options.badgesanity:
self.options.non_local_items.value -= self.item_name_groups["Badges"]
if self.multiworld.key_items_only[self.player]:
self.multiworld.trainersanity[self.player] = self.multiworld.trainersanity[self.player].from_text("off")
self.multiworld.dexsanity[self.player].value = 0
self.multiworld.randomize_hidden_items[self.player] = \
self.multiworld.randomize_hidden_items[self.player].from_text("off")
if self.options.key_items_only:
self.options.trainersanity.value = 0
self.options.dexsanity.value = 0
self.options.randomize_hidden_items = \
self.options.randomize_hidden_items.from_text("off")
if self.multiworld.badges_needed_for_hm_moves[self.player].value >= 2:
if self.options.badges_needed_for_hm_moves.value >= 2:
badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"]
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 3:
if self.options.badges_needed_for_hm_moves.value == 3:
badges = ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
"Soul Badge", "Volcano Badge", "Earth Badge"]
self.multiworld.random.shuffle(badges)
self.random.shuffle(badges)
badges_to_add += [badges.pop(), badges.pop()]
hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"]
self.multiworld.random.shuffle(hm_moves)
self.random.shuffle(hm_moves)
self.extra_badges = {}
for badge in badges_to_add:
self.extra_badges[hm_moves.pop()] = badge
@ -160,79 +262,17 @@ class PokemonRedBlueWorld(World):
process_move_data(self)
process_pokemon_data(self)
if self.multiworld.randomize_type_chart[self.player] == "vanilla":
chart = deepcopy(poke_data.type_chart)
elif self.multiworld.randomize_type_chart[self.player] == "randomize":
types = poke_data.type_names.values()
matchups = []
for type1 in types:
for type2 in types:
matchups.append([type1, type2])
self.multiworld.random.shuffle(matchups)
immunities = self.multiworld.immunity_matchups[self.player].value
super_effectives = self.multiworld.super_effective_matchups[self.player].value
not_very_effectives = self.multiworld.not_very_effective_matchups[self.player].value
normals = self.multiworld.normal_matchups[self.player].value
while super_effectives + not_very_effectives + normals < 225 - immunities:
if super_effectives == not_very_effectives == normals == 0:
super_effectives = 225
not_very_effectives = 225
normals = 225
else:
super_effectives += self.multiworld.super_effective_matchups[self.player].value
not_very_effectives += self.multiworld.not_very_effective_matchups[self.player].value
normals += self.multiworld.normal_matchups[self.player].value
if super_effectives + not_very_effectives + normals > 225 - immunities:
total = super_effectives + not_very_effectives + normals
excess = total - (225 - immunities)
subtract_amounts = (
int((excess / (super_effectives + not_very_effectives + normals)) * super_effectives),
int((excess / (super_effectives + not_very_effectives + normals)) * not_very_effectives),
int((excess / (super_effectives + not_very_effectives + normals)) * normals))
super_effectives -= subtract_amounts[0]
not_very_effectives -= subtract_amounts[1]
normals -= subtract_amounts[2]
while super_effectives + not_very_effectives + normals > 225 - immunities:
r = self.multiworld.random.randint(0, 2)
if r == 0 and super_effectives:
super_effectives -= 1
elif r == 1 and not_very_effectives:
not_very_effectives -= 1
elif normals:
normals -= 1
chart = []
for matchup_list, matchup_value in zip([immunities, normals, super_effectives, not_very_effectives],
[0, 10, 20, 5]):
for _ in range(matchup_list):
matchup = matchups.pop()
matchup.append(matchup_value)
chart.append(matchup)
elif self.multiworld.randomize_type_chart[self.player] == "chaos":
types = poke_data.type_names.values()
matchups = []
for type1 in types:
for type2 in types:
matchups.append([type1, type2])
chart = []
values = list(range(21))
self.multiworld.random.shuffle(matchups)
self.multiworld.random.shuffle(values)
for matchup in matchups:
value = values.pop(0)
values.append(value)
matchup.append(value)
chart.append(matchup)
# sort so that super-effective matchups occur first, to prevent dual "not very effective" / "super effective"
# matchups from leading to damage being ultimately divided by 2 and then multiplied by 2, which can lead to
# damage being reduced by 1 which leads to a "not very effective" message appearing due to my changes
# to the way effectiveness messages are generated.
self.type_chart = sorted(chart, key=lambda matchup: -matchup[2])
self.dexsanity_table = [
*(True for _ in range(round(self.multiworld.dexsanity[self.player].value * 1.51))),
*(False for _ in range(151 - round(self.multiworld.dexsanity[self.player].value * 1.51)))
*(True for _ in range(round(self.options.dexsanity.value))),
*(False for _ in range(151 - round(self.options.dexsanity.value)))
]
self.multiworld.random.shuffle(self.dexsanity_table)
self.random.shuffle(self.dexsanity_table)
self.trainersanity_table = [
*(True for _ in range(self.options.trainersanity.value)),
*(False for _ in range(317 - self.options.trainersanity.value))
]
self.random.shuffle(self.trainersanity_table)
def create_items(self):
self.multiworld.itempool += self.item_pool
@ -275,9 +315,9 @@ class PokemonRedBlueWorld(World):
filleritempool += [item for item in unplaced_items if (not item.advancement) and (not item.useful)]
def fill_hook(self, progitempool, usefulitempool, filleritempool, fill_locations):
if not self.multiworld.badgesanity[self.player]:
if not self.options.badgesanity:
# Door Shuffle options besides Simple place badges during door shuffling
if self.multiworld.door_shuffle[self.player] in ("off", "simple"):
if self.options.door_shuffle in ("off", "simple"):
badges = [item for item in progitempool if "Badge" in item.name and item.player == self.player]
for badge in badges:
self.multiworld.itempool.remove(badge)
@ -297,8 +337,8 @@ class PokemonRedBlueWorld(World):
for mon in poke_data.pokemon_data.keys():
state.collect(self.create_item(mon), True)
state.sweep_for_advancements()
self.multiworld.random.shuffle(badges)
self.multiworld.random.shuffle(badgelocs)
self.random.shuffle(badges)
self.random.shuffle(badgelocs)
badgelocs_copy = badgelocs.copy()
# allow_partial so that unplaced badges aren't lost, for debugging purposes
fill_restrictive(self.multiworld, state, badgelocs_copy, badges, True, True, allow_partial=True)
@ -318,7 +358,7 @@ class PokemonRedBlueWorld(World):
raise FillError(f"Failed to place badges for player {self.player}")
verify_hm_moves(self.multiworld, self, self.player)
if self.multiworld.key_items_only[self.player]:
if self.options.key_items_only:
return
tms = [item for item in usefulitempool + filleritempool if item.name.startswith("TM") and (item.player ==
@ -340,7 +380,7 @@ class PokemonRedBlueWorld(World):
int((int(tm.name[2:4]) - 1) / 8)] & 1 << ((int(tm.name[2:4]) - 1) % 8)]
if not learnable_tms:
learnable_tms = tms
tm = self.multiworld.random.choice(learnable_tms)
tm = self.random.choice(learnable_tms)
loc.place_locked_item(tm)
fill_locations.remove(loc)
@ -370,9 +410,9 @@ class PokemonRedBlueWorld(World):
if not all_state.can_reach(location, player=self.player):
evolutions_region.locations.remove(location)
if self.multiworld.old_man[self.player] == "early_parcel":
if self.options.old_man == "early_parcel":
self.multiworld.local_early_items[self.player]["Oak's Parcel"] = 1
if self.multiworld.dexsanity[self.player]:
if self.options.dexsanity:
for i, mon in enumerate(poke_data.pokemon_data):
if self.dexsanity_table[i]:
location = self.multiworld.get_location(f"Pokedex - {mon}", self.player)
@ -384,13 +424,13 @@ class PokemonRedBlueWorld(World):
locs = {self.multiworld.get_location("Fossil - Choice A", self.player),
self.multiworld.get_location("Fossil - Choice B", self.player)}
if not self.multiworld.key_items_only[self.player]:
if not self.options.key_items_only:
rule = None
if self.multiworld.fossil_check_item_types[self.player] == "key_items":
if self.options.fossil_check_item_types == "key_items":
rule = lambda i: i.advancement
elif self.multiworld.fossil_check_item_types[self.player] == "unique_items":
elif self.options.fossil_check_item_types == "unique_items":
rule = lambda i: i.name in item_groups["Unique"]
elif self.multiworld.fossil_check_item_types[self.player] == "no_key_items":
elif self.options.fossil_check_item_types == "no_key_items":
rule = lambda i: not i.advancement
if rule:
for loc in locs:
@ -406,16 +446,16 @@ class PokemonRedBlueWorld(World):
if loc.item is None:
locs.add(loc)
if not self.multiworld.key_items_only[self.player]:
if not self.options.key_items_only:
loc = self.multiworld.get_location("Player's House 2F - Player's PC", self.player)
if loc.item is None:
locs.add(loc)
for loc in sorted(locs):
if loc.name in self.multiworld.priority_locations[self.player].value:
if loc.name in self.options.priority_locations.value:
add_item_rule(loc, lambda i: i.advancement)
add_item_rule(loc, lambda i: i.player == self.player)
if self.multiworld.old_man[self.player] == "early_parcel" and loc.name != "Player's House 2F - Player's PC":
if self.options.old_man == "early_parcel" and loc.name != "Player's House 2F - Player's PC":
add_item_rule(loc, lambda i: i.name != "Oak's Parcel")
self.local_locs = locs
@ -440,10 +480,10 @@ class PokemonRedBlueWorld(World):
else:
region_mons.add(location.item.name)
self.multiworld.elite_four_pokedex_condition[self.player].total = \
int((len(reachable_mons) / 100) * self.multiworld.elite_four_pokedex_condition[self.player].value)
self.options.elite_four_pokedex_condition.total = \
int((len(reachable_mons) / 100) * self.options.elite_four_pokedex_condition.value)
if self.multiworld.accessibility[self.player] == "full":
if self.options.accessibility == "full":
balls = [self.create_item(ball) for ball in ["Poke Ball", "Great Ball", "Ultra Ball"]]
traps = [self.create_item(trap) for trap in item_groups["Traps"]]
locations = [location for location in self.multiworld.get_locations(self.player) if "Pokedex - " in
@ -469,7 +509,7 @@ class PokemonRedBlueWorld(World):
else:
break
else:
self.multiworld.random.shuffle(traps)
self.random.shuffle(traps)
for trap in traps:
try:
self.multiworld.itempool.remove(trap)
@ -497,22 +537,22 @@ class PokemonRedBlueWorld(World):
found_mons.add(key)
def create_regions(self):
if (self.multiworld.old_man[self.player] == "vanilla" or
self.multiworld.door_shuffle[self.player] in ("full", "insanity")):
fly_map_codes = self.multiworld.random.sample(range(2, 11), 2)
elif (self.multiworld.door_shuffle[self.player] == "simple" or
self.multiworld.route_3_condition[self.player] == "boulder_badge" or
(self.multiworld.route_3_condition[self.player] == "any_badge" and
self.multiworld.badgesanity[self.player])):
fly_map_codes = self.multiworld.random.sample(range(3, 11), 2)
if (self.options.old_man == "vanilla" or
self.options.door_shuffle in ("full", "insanity")):
fly_map_codes = self.random.sample(range(2, 11), 2)
elif (self.options.door_shuffle == "simple" or
self.options.route_3_condition == "boulder_badge" or
(self.options.route_3_condition == "any_badge" and
self.options.badgesanity)):
fly_map_codes = self.random.sample(range(3, 11), 2)
else:
fly_map_codes = self.multiworld.random.sample([4, 6, 7, 8, 9, 10], 2)
if self.multiworld.free_fly_location[self.player]:
fly_map_codes = self.random.sample([4, 6, 7, 8, 9, 10], 2)
if self.options.free_fly_location:
fly_map_code = fly_map_codes[0]
else:
fly_map_code = 0
if self.multiworld.town_map_fly_location[self.player]:
if self.options.town_map_fly_location:
town_map_fly_map_code = fly_map_codes[1]
else:
town_map_fly_map_code = 0
@ -528,7 +568,7 @@ class PokemonRedBlueWorld(World):
self.multiworld.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player)
def set_rules(self):
set_rules(self.multiworld, self.player)
set_rules(self.multiworld, self, self.player)
def create_item(self, name: str) -> Item:
return PokemonRBItem(name, self.player)
@ -548,19 +588,19 @@ class PokemonRedBlueWorld(World):
multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def write_spoiler_header(self, spoiler_handle: TextIO):
spoiler_handle.write(f"Cerulean Cave Total Key Items: {self.multiworld.cerulean_cave_key_items_condition[self.player].total}\n")
spoiler_handle.write(f"Elite Four Total Key Items: {self.multiworld.elite_four_key_items_condition[self.player].total}\n")
spoiler_handle.write(f"Elite Four Total Pokemon: {self.multiworld.elite_four_pokedex_condition[self.player].total}\n")
if self.multiworld.free_fly_location[self.player]:
spoiler_handle.write(f"Cerulean Cave Total Key Items: {self.options.cerulean_cave_key_items_condition.total}\n")
spoiler_handle.write(f"Elite Four Total Key Items: {self.options.elite_four_key_items_condition.total}\n")
spoiler_handle.write(f"Elite Four Total Pokemon: {self.options.elite_four_pokedex_condition.total}\n")
if self.options.free_fly_location:
spoiler_handle.write(f"Free Fly Location: {self.fly_map}\n")
if self.multiworld.town_map_fly_location[self.player]:
if self.options.town_map_fly_location:
spoiler_handle.write(f"Town Map Fly Location: {self.town_map_fly_map}\n")
if self.extra_badges:
for hm_move, badge in self.extra_badges.items():
spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n")
def write_spoiler(self, spoiler_handle):
if self.multiworld.randomize_type_chart[self.player].value:
if self.options.randomize_type_chart:
spoiler_handle.write(f"\n\nType matchups ({self.multiworld.player_name[self.player]}):\n\n")
for matchup in self.type_chart:
spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n")
@ -571,39 +611,39 @@ class PokemonRedBlueWorld(World):
spoiler_handle.write(location.name + ": " + location.item.name + "\n")
def get_filler_item_name(self) -> str:
combined_traps = (self.multiworld.poison_trap_weight[self.player].value
+ self.multiworld.fire_trap_weight[self.player].value
+ self.multiworld.paralyze_trap_weight[self.player].value
+ self.multiworld.ice_trap_weight[self.player].value
+ self.multiworld.sleep_trap_weight[self.player].value)
combined_traps = (self.options.poison_trap_weight.value
+ self.options.fire_trap_weight.value
+ self.options.paralyze_trap_weight.value
+ self.options.ice_trap_weight.value
+ self.options.sleep_trap_weight.value)
if (combined_traps > 0 and
self.multiworld.random.randint(1, 100) <= self.multiworld.trap_percentage[self.player].value):
self.random.randint(1, 100) <= self.options.trap_percentage.value):
return self.select_trap()
banned_items = item_groups["Unique"]
if (((not self.multiworld.tea[self.player]) or "Saffron City" not in [self.fly_map, self.town_map_fly_map])
and (not self.multiworld.door_shuffle[self.player])):
if (((not self.options.tea) or "Saffron City" not in [self.fly_map, self.town_map_fly_map])
and (not self.options.door_shuffle)):
# under these conditions, you should never be able to reach the Copycat or Pokémon Tower without being
# able to reach the Celadon Department Store, so Poké Dolls would not allow early access to anything
banned_items.append("Poke Doll")
if not self.multiworld.tea[self.player]:
if not self.options.tea:
banned_items += item_groups["Vending Machine Drinks"]
return self.multiworld.random.choice([item for item in item_table if item_table[item].id and item_table[
return self.random.choice([item for item in item_table if item_table[item].id and item_table[
item].classification == ItemClassification.filler and item not in banned_items])
def select_trap(self):
if self.traps is None:
self.traps = []
self.traps += ["Poison Trap"] * self.multiworld.poison_trap_weight[self.player].value
self.traps += ["Fire Trap"] * self.multiworld.fire_trap_weight[self.player].value
self.traps += ["Paralyze Trap"] * self.multiworld.paralyze_trap_weight[self.player].value
self.traps += ["Ice Trap"] * self.multiworld.ice_trap_weight[self.player].value
self.traps += ["Sleep Trap"] * self.multiworld.sleep_trap_weight[self.player].value
return self.multiworld.random.choice(self.traps)
self.traps += ["Poison Trap"] * self.options.poison_trap_weight.value
self.traps += ["Fire Trap"] * self.options.fire_trap_weight.value
self.traps += ["Paralyze Trap"] * self.options.paralyze_trap_weight.value
self.traps += ["Ice Trap"] * self.options.ice_trap_weight.value
self.traps += ["Sleep Trap"] * self.options.sleep_trap_weight.value
return self.random.choice(self.traps)
def extend_hint_information(self, hint_data):
if self.multiworld.dexsanity[self.player] or self.multiworld.door_shuffle[self.player]:
if self.options.dexsanity or self.options.door_shuffle:
hint_data[self.player] = {}
if self.multiworld.dexsanity[self.player]:
if self.options.dexsanity:
mon_locations = {mon: set() for mon in poke_data.pokemon_data.keys()}
for loc in location_data:
if loc.type in ["Wild Encounter", "Static Pokemon", "Legendary Pokemon", "Missable Pokemon"]:
@ -616,57 +656,59 @@ class PokemonRedBlueWorld(World):
hint_data[self.player][self.multiworld.get_location(f"Pokedex - {mon}", self.player).address] =\
", ".join(mon_locations[mon])
if self.multiworld.door_shuffle[self.player]:
if self.options.door_shuffle:
for location in self.multiworld.get_locations(self.player):
if location.parent_region.entrance_hint and location.address:
hint_data[self.player][location.address] = location.parent_region.entrance_hint
def fill_slot_data(self) -> dict:
return {
"second_fossil_check_condition": self.multiworld.second_fossil_check_condition[self.player].value,
"require_item_finder": self.multiworld.require_item_finder[self.player].value,
"randomize_hidden_items": self.multiworld.randomize_hidden_items[self.player].value,
"badges_needed_for_hm_moves": self.multiworld.badges_needed_for_hm_moves[self.player].value,
"oaks_aide_rt_2": self.multiworld.oaks_aide_rt_2[self.player].value,
"oaks_aide_rt_11": self.multiworld.oaks_aide_rt_11[self.player].value,
"oaks_aide_rt_15": self.multiworld.oaks_aide_rt_15[self.player].value,
"extra_key_items": self.multiworld.extra_key_items[self.player].value,
"extra_strength_boulders": self.multiworld.extra_strength_boulders[self.player].value,
"tea": self.multiworld.tea[self.player].value,
"old_man": self.multiworld.old_man[self.player].value,
"elite_four_badges_condition": self.multiworld.elite_four_badges_condition[self.player].value,
"elite_four_key_items_condition": self.multiworld.elite_four_key_items_condition[self.player].total,
"elite_four_pokedex_condition": self.multiworld.elite_four_pokedex_condition[self.player].total,
"victory_road_condition": self.multiworld.victory_road_condition[self.player].value,
"route_22_gate_condition": self.multiworld.route_22_gate_condition[self.player].value,
"route_3_condition": self.multiworld.route_3_condition[self.player].value,
"robbed_house_officer": self.multiworld.robbed_house_officer[self.player].value,
"viridian_gym_condition": self.multiworld.viridian_gym_condition[self.player].value,
"cerulean_cave_badges_condition": self.multiworld.cerulean_cave_badges_condition[self.player].value,
"cerulean_cave_key_items_condition": self.multiworld.cerulean_cave_key_items_condition[self.player].total,
ret = {
"second_fossil_check_condition": self.options.second_fossil_check_condition.value,
"require_item_finder": self.options.require_item_finder.value,
"randomize_hidden_items": self.options.randomize_hidden_items.value,
"badges_needed_for_hm_moves": self.options.badges_needed_for_hm_moves.value,
"oaks_aide_rt_2": self.options.oaks_aide_rt_2.value,
"oaks_aide_rt_11": self.options.oaks_aide_rt_11.value,
"oaks_aide_rt_15": self.options.oaks_aide_rt_15.value,
"extra_key_items": self.options.extra_key_items.value,
"extra_strength_boulders": self.options.extra_strength_boulders.value,
"tea": self.options.tea.value,
"old_man": self.options.old_man.value,
"elite_four_badges_condition": self.options.elite_four_badges_condition.value,
"elite_four_key_items_condition": self.options.elite_four_key_items_condition.total,
"elite_four_pokedex_condition": self.options.elite_four_pokedex_condition.total,
"victory_road_condition": self.options.victory_road_condition.value,
"route_22_gate_condition": self.options.route_22_gate_condition.value,
"route_3_condition": self.options.route_3_condition.value,
"robbed_house_officer": self.options.robbed_house_officer.value,
"viridian_gym_condition": self.options.viridian_gym_condition.value,
"cerulean_cave_badges_condition": self.options.cerulean_cave_badges_condition.value,
"cerulean_cave_key_items_condition": self.options.cerulean_cave_key_items_condition.total,
"free_fly_map": self.fly_map_code,
"town_map_fly_map": self.town_map_fly_map_code,
"extra_badges": self.extra_badges,
"type_chart": self.type_chart,
"randomize_pokedex": self.multiworld.randomize_pokedex[self.player].value,
"trainersanity": self.multiworld.trainersanity[self.player].value,
"death_link": self.multiworld.death_link[self.player].value,
"prizesanity": self.multiworld.prizesanity[self.player].value,
"key_items_only": self.multiworld.key_items_only[self.player].value,
"poke_doll_skip": self.multiworld.poke_doll_skip[self.player].value,
"bicycle_gate_skips": self.multiworld.bicycle_gate_skips[self.player].value,
"stonesanity": self.multiworld.stonesanity[self.player].value,
"door_shuffle": self.multiworld.door_shuffle[self.player].value,
"warp_tile_shuffle": self.multiworld.warp_tile_shuffle[self.player].value,
"dark_rock_tunnel_logic": self.multiworld.dark_rock_tunnel_logic[self.player].value,
"split_card_key": self.multiworld.split_card_key[self.player].value,
"all_elevators_locked": self.multiworld.all_elevators_locked[self.player].value,
"require_pokedex": self.multiworld.require_pokedex[self.player].value,
"area_1_to_1_mapping": self.multiworld.area_1_to_1_mapping[self.player].value,
"blind_trainers": self.multiworld.blind_trainers[self.player].value,
"randomize_pokedex": self.options.randomize_pokedex.value,
"trainersanity": self.options.trainersanity.value,
"death_link": self.options.death_link.value,
"prizesanity": self.options.prizesanity.value,
"key_items_only": self.options.key_items_only.value,
"poke_doll_skip": self.options.poke_doll_skip.value,
"bicycle_gate_skips": self.options.bicycle_gate_skips.value,
"stonesanity": self.options.stonesanity.value,
"door_shuffle": self.options.door_shuffle.value,
"warp_tile_shuffle": self.options.warp_tile_shuffle.value,
"dark_rock_tunnel_logic": self.options.dark_rock_tunnel_logic.value,
"split_card_key": self.options.split_card_key.value,
"all_elevators_locked": self.options.all_elevators_locked.value,
"require_pokedex": self.options.require_pokedex.value,
"area_1_to_1_mapping": self.options.area_1_to_1_mapping.value,
"blind_trainers": self.options.blind_trainers.value,
}
if self.options.type_chart_seed == "random" or self.options.type_chart_seed.value.isdigit():
ret["type_chart"] = self.type_chart
return ret
class PokemonRBItem(Item):
game = "Pokemon Red and Blue"

View File

@ -60,11 +60,12 @@ and Safari Zone. Adds 4 extra item locations to Rock Tunnel B1F
* Split Card Key: Splits the Card Key into 10 different Card Keys, one for each floor of Silph Co that has locked doors.
Adds 9 location checks to friendly NPCs in Silph Co. You can also choose Progressive Card Keys to always obtain the
keys in order from Card Key 2F to Card Key 11F.
* Trainersanity: Adds location checks to 317 trainers. Does not include scripted trainers, most of which disappear
* Trainersanity: Adds location checks to trainers. You may choose between 0 and 317 trainersanity checks. Trainers
will be randomly selected to be given checks. Does not include scripted trainers, most of which disappear
after battling them, but also includes Gym Leaders. You must talk to the trainer after defeating them to receive
your prize. Adds 317 random filler items to the item pool
* Dexsanity: Location checks occur when registering Pokémon as owned in the Pokédex. You can choose a percentage
of Pokémon to have checks added to, chosen randomly. You can identify which Pokémon have location checks by an empty
your prize. Adds random filler items to the item pool.
* Dexsanity: Location checks occur when registering Pokémon as owned in the Pokédex. You can choose between 0 and 151
Pokémon to have checks added to, chosen randomly. You can identify which Pokémon have location checks by an empty
Poké Ball icon shown in battle or in the Pokédex menu.
## Which items can be in another player's world?

View File

@ -8,7 +8,7 @@ def get_encounter_slots(self):
for location in encounter_slots:
if isinstance(location.original_item, list):
location.original_item = location.original_item[not self.multiworld.game_version[self.player].value]
location.original_item = location.original_item[not self.options.game_version.value]
return encounter_slots
@ -39,16 +39,16 @@ def randomize_pokemon(self, mon, mons_list, randomize_type, random):
return mon
def process_trainer_data(self):
def process_trainer_data(world):
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
or self.multiworld.trainer_legendaries[self.player].value]
or world.options.trainer_legendaries.value]
unevolved_mons = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
or world.options.randomize_legendary_pokemon.value == 3]
evolved_mons = [mon for mon in mons_list if mon not in unevolved_mons]
rival_map = {
"Charmander": self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name[9:], # strip the
"Squirtle": self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name[9:], # 'Missable'
"Bulbasaur": self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name[9:], # from the name
"Charmander": world.multiworld.get_location("Oak's Lab - Starter 1", world.player).item.name[9:], # strip the
"Squirtle": world.multiworld.get_location("Oak's Lab - Starter 2", world.player).item.name[9:], # 'Missable'
"Bulbasaur": world.multiworld.get_location("Oak's Lab - Starter 3", world.player).item.name[9:], # from the name
}
def add_evolutions():
@ -60,7 +60,7 @@ def process_trainer_data(self):
rival_map[poke_data.evolves_to[a]] = b
add_evolutions()
add_evolutions()
parties_objs = [location for location in self.multiworld.get_locations(self.player)
parties_objs = [location for location in world.multiworld.get_locations(world.player)
if location.type == "Trainer Parties"]
# Process Rival parties in order "Route 22 " is not a typo
parties_objs.sort(key=lambda i: 0 if "Oak's Lab" in i.name else 1 if "Route 22 " in i.name else 2 if "Cerulean City"
@ -75,25 +75,25 @@ def process_trainer_data(self):
for i, mon in enumerate(rival_party):
if mon in ("Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmeleon", "Charizard",
"Squirtle", "Wartortle", "Blastoise"):
if self.multiworld.randomize_starter_pokemon[self.player]:
if world.options.randomize_starter_pokemon:
rival_party[i] = rival_map[mon]
elif self.multiworld.randomize_trainer_parties[self.player]:
elif world.options.randomize_trainer_parties:
if mon in rival_map:
rival_party[i] = rival_map[mon]
else:
new_mon = randomize_pokemon(self, mon,
new_mon = randomize_pokemon(world, mon,
unevolved_mons if mon in unevolved_mons else evolved_mons,
self.multiworld.randomize_trainer_parties[self.player].value,
self.multiworld.random)
world.options.randomize_trainer_parties.value,
world.random)
rival_map[mon] = new_mon
rival_party[i] = new_mon
add_evolutions()
else:
if self.multiworld.randomize_trainer_parties[self.player]:
if world.options.randomize_trainer_parties:
for i, mon in enumerate(party["party"]):
party["party"][i] = randomize_pokemon(self, mon, mons_list,
self.multiworld.randomize_trainer_parties[self.player].value,
self.multiworld.random)
party["party"][i] = randomize_pokemon(world, mon, mons_list,
world.options.randomize_trainer_parties.value,
world.random)
def process_pokemon_locations(self):
@ -106,21 +106,21 @@ def process_pokemon_locations(self):
placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()}
mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
if self.multiworld.randomize_legendary_pokemon[self.player] == "vanilla":
or self.options.randomize_legendary_pokemon.value == 3]
if self.options.randomize_legendary_pokemon == "vanilla":
for slot in legendary_slots:
location = self.multiworld.get_location(slot.name, self.player)
location.place_locked_item(self.create_item("Static " + slot.original_item))
elif self.multiworld.randomize_legendary_pokemon[self.player] == "shuffle":
self.multiworld.random.shuffle(legendary_mons)
elif self.options.randomize_legendary_pokemon == "shuffle":
self.random.shuffle(legendary_mons)
for slot in legendary_slots:
location = self.multiworld.get_location(slot.name, self.player)
mon = legendary_mons.pop()
location.place_locked_item(self.create_item("Static " + mon))
placed_mons[mon] += 1
elif self.multiworld.randomize_legendary_pokemon[self.player] == "static":
elif self.options.randomize_legendary_pokemon == "static":
static_slots = static_slots + legendary_slots
self.multiworld.random.shuffle(static_slots)
self.random.shuffle(static_slots)
static_slots.sort(key=lambda s: s.name != "Pokemon Tower 6F - Restless Soul")
while legendary_slots:
swap_slot = legendary_slots.pop()
@ -131,12 +131,12 @@ def process_pokemon_locations(self):
location = self.multiworld.get_location(slot.name, self.player)
location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item))
swap_slot.original_item = slot.original_item
elif self.multiworld.randomize_legendary_pokemon[self.player] == "any":
elif self.options.randomize_legendary_pokemon == "any":
static_slots = static_slots + legendary_slots
for slot in static_slots:
location = self.multiworld.get_location(slot.name, self.player)
randomize_type = self.multiworld.randomize_static_pokemon[self.player].value
randomize_type = self.options.randomize_static_pokemon.value
slot_type = slot.type.split()[0]
if slot_type == "Legendary":
slot_type = "Static"
@ -145,7 +145,7 @@ def process_pokemon_locations(self):
else:
mon = self.create_item(slot_type + " " +
randomize_pokemon(self, slot.original_item, mons_list, randomize_type,
self.multiworld.random))
self.random))
location.place_locked_item(mon)
if slot_type != "Missable":
placed_mons[mon.name.replace("Static ", "")] += 1
@ -153,16 +153,16 @@ def process_pokemon_locations(self):
chosen_mons = set()
for slot in starter_slots:
location = self.multiworld.get_location(slot.name, self.player)
randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
randomize_type = self.options.randomize_starter_pokemon.value
slot_type = "Missable"
if not randomize_type:
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
else:
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
randomize_type, self.multiworld.random))
randomize_type, self.random))
while mon.name in chosen_mons:
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
randomize_type, self.multiworld.random))
randomize_type, self.random))
chosen_mons.add(mon.name)
location.place_locked_item(mon)
@ -170,10 +170,10 @@ def process_pokemon_locations(self):
encounter_slots = encounter_slots_master.copy()
zone_mapping = {}
if self.multiworld.randomize_wild_pokemon[self.player]:
if self.options.randomize_wild_pokemon:
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
self.multiworld.random.shuffle(encounter_slots)
or self.options.randomize_legendary_pokemon.value == 3]
self.random.shuffle(encounter_slots)
locations = []
for slot in encounter_slots:
location = self.multiworld.get_location(slot.name, self.player)
@ -181,11 +181,11 @@ def process_pokemon_locations(self):
if zone not in zone_mapping:
zone_mapping[zone] = {}
original_mon = slot.original_item
if self.multiworld.area_1_to_1_mapping[self.player] and original_mon in zone_mapping[zone]:
if self.options.area_1_to_1_mapping and original_mon in zone_mapping[zone]:
mon = zone_mapping[zone][original_mon]
else:
mon = randomize_pokemon(self, original_mon, mons_list,
self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random)
self.options.randomize_wild_pokemon.value, self.random)
#
while ("Pokemon Tower 6F" in slot.name and
self.multiworld.get_location("Pokemon Tower 6F - Restless Soul", self.player).item.name
@ -194,7 +194,7 @@ def process_pokemon_locations(self):
# the battle is treates as the Restless Soul battle and you cannot catch it. So, prevent any wild mons
# from being the same species as the Restless Soul.
# to account for the possibility that only one ground type Pokemon exists, match only stats for this fix
mon = randomize_pokemon(self, original_mon, mons_list, 2, self.multiworld.random)
mon = randomize_pokemon(self, original_mon, mons_list, 2, self.random)
placed_mons[mon] += 1
location.item = self.create_item(mon)
location.locked = True
@ -204,28 +204,28 @@ def process_pokemon_locations(self):
mons_to_add = []
remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
if self.multiworld.catch_em_all[self.player] == "first_stage":
(pokemon not in poke_data.legendary_pokemon or self.options.randomize_legendary_pokemon.value == 3)]
if self.options.catch_em_all == "first_stage":
mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
elif self.multiworld.catch_em_all[self.player] == "all_pokemon":
(pokemon not in poke_data.legendary_pokemon or self.options.randomize_legendary_pokemon.value == 3)]
elif self.options.catch_em_all == "all_pokemon":
mons_to_add = remaining_pokemon.copy()
logic_needed_mons = max(self.multiworld.oaks_aide_rt_2[self.player].value,
self.multiworld.oaks_aide_rt_11[self.player].value,
self.multiworld.oaks_aide_rt_15[self.player].value)
if self.multiworld.accessibility[self.player] == "minimal":
logic_needed_mons = max(self.options.oaks_aide_rt_2.value,
self.options.oaks_aide_rt_11.value,
self.options.oaks_aide_rt_15.value)
if self.options.accessibility == "minimal":
logic_needed_mons = 0
self.multiworld.random.shuffle(remaining_pokemon)
self.random.shuffle(remaining_pokemon)
while (len([pokemon for pokemon in placed_mons if placed_mons[pokemon] > 0])
+ len(mons_to_add) < logic_needed_mons):
mons_to_add.append(remaining_pokemon.pop())
for mon in mons_to_add:
stat_base = get_base_stat_total(mon)
candidate_locations = encounter_slots_master.copy()
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_base_stats", "match_types_and_base_stats"]:
if self.options.randomize_wild_pokemon.current_key in ["match_base_stats", "match_types_and_base_stats"]:
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.original_item) - stat_base))
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_types", "match_types_and_base_stats"]:
if self.options.randomize_wild_pokemon.current_key in ["match_types", "match_types_and_base_stats"]:
candidate_locations.sort(key=lambda slot: not any([poke_data.pokemon_data[slot.original_item]["type1"] in
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
poke_data.pokemon_data[slot.original_item]["type2"] in
@ -233,12 +233,12 @@ def process_pokemon_locations(self):
candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations]
for location in candidate_locations:
zone = " - ".join(location.name.split(" - ")[:-1])
if self.multiworld.catch_em_all[self.player] == "all_pokemon" and self.multiworld.area_1_to_1_mapping[self.player]:
if self.options.catch_em_all == "all_pokemon" and self.options.area_1_to_1_mapping:
if not [self.multiworld.get_location(l.name, self.player) for l in encounter_slots_master
if (not l.name.startswith(zone)) and
self.multiworld.get_location(l.name, self.player).item.name == location.item.name]:
continue
if self.multiworld.catch_em_all[self.player] == "first_stage" and self.multiworld.area_1_to_1_mapping[self.player]:
if self.options.catch_em_all == "first_stage" and self.options.area_1_to_1_mapping:
if not [self.multiworld.get_location(l.name, self.player) for l in encounter_slots_master
if (not l.name.startswith(zone)) and
self.multiworld.get_location(l.name, self.player).item.name == location.item.name and l.name
@ -246,10 +246,10 @@ def process_pokemon_locations(self):
continue
if placed_mons[location.item.name] < 2 and (location.item.name in poke_data.first_stage_pokemon
or self.multiworld.catch_em_all[self.player]):
or self.options.catch_em_all):
continue
if self.multiworld.area_1_to_1_mapping[self.player]:
if self.options.area_1_to_1_mapping:
place_locations = [place_location for place_location in candidate_locations if
place_location.name.startswith(zone) and
place_location.item.name == location.item.name]

View File

@ -194,6 +194,8 @@ item_table = {
"Fuji Saved": ItemData(None, ItemClassification.progression, []),
"Silph Co Liberated": ItemData(None, ItemClassification.progression, []),
"Become Champion": ItemData(None, ItemClassification.progression, []),
"Mt Moon Fossils": ItemData(None, ItemClassification.progression, []),
"Cinnabar Lab": ItemData(None, ItemClassification.progression, []),
"Trainer Parties": ItemData(None, ItemClassification.filler, [])
}

View File

@ -10,9 +10,9 @@ def level_scaling(multiworld):
while locations:
sphere = set()
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
if (multiworld.level_scaling[world.player] != "by_spheres_and_distance"
and (multiworld.level_scaling[world.player] != "auto" or multiworld.door_shuffle[world.player]
in ("off", "simple"))):
if (world.options.level_scaling != "by_spheres_and_distance"
and (world.options.level_scaling != "auto"
or world.options.door_shuffle in ("off", "simple"))):
continue
regions = {multiworld.get_region("Menu", world.player)}
checked_regions = set()
@ -41,7 +41,8 @@ def level_scaling(multiworld):
# reach them earlier. We treat them both as reachable right away for this purpose
return True
if (location.name == "Route 25 - Item" and state.can_reach("Route 25", "Region", location.player)
and multiworld.blind_trainers[location.player].value < 100):
and multiworld.worlds[location.player].options.blind_trainers.value < 100
and "Route 25 - Jr. Trainer M" not in multiworld.regions.location_cache[location.player]):
# Assume they will take their one chance to get the trainer to walk out of the way to reach
# the item behind them
return True
@ -95,9 +96,9 @@ def level_scaling(multiworld):
if (location.item.game == "Pokemon Red and Blue" and (location.item.name.startswith("Missable ") or
location.item.name.startswith("Static ")) and location.name !=
"Pokemon Tower 6F - Restless Soul"):
# Normally, missable Pokemon (starters, the dojo rewards) are not considered in logic static Pokemon
# are not considered for moves or evolutions, as you could release them and potentially soft lock
# the game. However, for level scaling purposes, we will treat them as not missable or static.
# Normally, missable Pokemon (starters, the dojo rewards) are not considered in logic, and static
# Pokemon are not considered for moves or evolutions, as you could release them and potentially soft
# lock the game. However, for level scaling purposes, we will treat them as not missable or static.
# We would not want someone playing a minimal accessibility Dexsanity game to get what would be
# technically an "out of logic" Mansion Key from selecting Bulbasaur at the beginning of the game
# and end up in the Mansion early and encountering level 67 Pokémon
@ -106,7 +107,7 @@ def level_scaling(multiworld):
else:
state.collect(location.item, True, location)
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
if multiworld.level_scaling[world.player] == "off":
if world.options.level_scaling == "off":
continue
level_list_copy = level_list.copy()
for sphere in spheres:
@ -136,4 +137,4 @@ def level_scaling(multiworld):
else:
sphere_objects[object].level = level_list_copy.pop(0)
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
world.finished_level_scaling.set()
world.finished_level_scaling.set()

View File

@ -5,46 +5,48 @@ from . import poke_data
loc_id_start = 172000000
def trainersanity(multiworld, player):
return multiworld.trainersanity[player]
def dexsanity(multiworld, player):
include = multiworld.worlds[player].dexsanity_table.pop(0)
multiworld.worlds[player].dexsanity_table.append(include)
def trainersanity(world, player):
include = world.trainersanity_table.pop(0)
world.trainersanity_table.append(include)
return include
def hidden_items(multiworld, player):
return multiworld.randomize_hidden_items[player]
def dexsanity(world, player):
include = world.dexsanity_table.pop(0)
world.dexsanity_table.append(include)
return include
def hidden_moon_stones(multiworld, player):
return multiworld.randomize_hidden_items[player] or multiworld.stonesanity[player]
def hidden_items(world, player):
return world.options.randomize_hidden_items
def tea(multiworld, player):
return multiworld.tea[player]
def hidden_moon_stones(world, player):
return world.options.randomize_hidden_items or world.options.stonesanity
def extra_key_items(multiworld, player):
return multiworld.extra_key_items[player]
def tea(world, player):
return world.options.tea
def always_on(multiworld, player):
def extra_key_items(world, player):
return world.options.extra_key_items
def always_on(world, player):
return True
def prizesanity(multiworld, player):
return multiworld.prizesanity[player]
def prizesanity(world, player):
return world.options.prizesanity
def split_card_key(multiworld, player):
return multiworld.split_card_key[player].value > 0
def split_card_key(world, player):
return world.options.split_card_key.value > 0
def not_stonesanity(multiworld, player):
return not multiworld.stonesanity[player]
def not_stonesanity(world, player):
return not world.options.stonesanity
class LocationData:
@ -395,7 +397,7 @@ location_data = [
LocationData("Silph Co 5F", "Hidden Item Pot Plant", "Elixir", rom_addresses['Hidden_Item_Silph_Co_5F'], Hidden(18), inclusion=hidden_items),
LocationData("Silph Co 9F-SW", "Hidden Item Nurse Bed", "Max Potion", rom_addresses['Hidden_Item_Silph_Co_9F'], Hidden(19), inclusion=hidden_items),
LocationData("Saffron Copycat's House 2F", "Hidden Item Desk", "Nugget", rom_addresses['Hidden_Item_Copycats_House'], Hidden(20), inclusion=hidden_items),
LocationData("Cerulean Cave 1F-NW", "Hidden Item Center Rocks", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_Cave_1F'], Hidden(21), inclusion=hidden_items),
LocationData("Cerulean Cave 1F-SW", "Hidden Item Center Rocks", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_Cave_1F'], Hidden(21), inclusion=hidden_items),
LocationData("Cerulean Cave B1F-E", "Hidden Item Northeast Rocks", "Ultra Ball", rom_addresses['Hidden_Item_Cerulean_Cave_B1F'], Hidden(22), inclusion=hidden_items),
LocationData("Power Plant", "Hidden Item Central Dead End", "Max Elixir", rom_addresses['Hidden_Item_Power_Plant_1'], Hidden(23), inclusion=hidden_items),
LocationData("Power Plant", "Hidden Item Before Zapdos", "PP Up", rom_addresses['Hidden_Item_Power_Plant_2'], Hidden(24), inclusion=hidden_items),
@ -786,6 +788,8 @@ location_data = [
LocationData("Celadon Game Corner", "", "Game Corner", event=True),
LocationData("Cinnabar Island", "", "Cinnabar Island", event=True),
LocationData("Cinnabar Lab", "", "Cinnabar Lab", event=True),
LocationData("Mt Moon B2F", "Mt Moon Fossils", "Mt Moon Fossils", event=True),
LocationData("Celadon Department Store 4F", "Buy Poke Doll", "Buy Poke Doll", event=True),
LocationData("Celadon Department Store 4F", "Buy Fire Stone", "Fire Stone", event=True, inclusion=not_stonesanity),
LocationData("Celadon Department Store 4F", "Buy Water Stone", "Water Stone", event=True, inclusion=not_stonesanity),

View File

@ -1,49 +1,47 @@
from . import poke_data
def can_surf(state, player):
return (((state.has("HM03 Surf", player) and can_learn_hm(state, "Surf", player))
or state.has("Flippers", player)) and (state.has("Soul Badge", player) or
state.has(state.multiworld.worlds[player].extra_badges.get("Surf"), player)
or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
def can_surf(state, world, player):
return (((state.has("HM03 Surf", player) and can_learn_hm(state, world, "Surf", player))) and (state.has("Soul Badge", player) or
state.has(world.extra_badges.get("Surf"), player)
or world.options.badges_needed_for_hm_moves.value == 0))
def can_cut(state, player):
return ((state.has("HM01 Cut", player) and can_learn_hm(state, "Cut", player) or state.has("Master Sword", player))
and (state.has("Cascade Badge", player) or
state.has(state.multiworld.worlds[player].extra_badges.get("Cut"), player) or
state.multiworld.badges_needed_for_hm_moves[player].value == 0))
def can_cut(state, world, player):
return ((state.has("HM01 Cut", player) and can_learn_hm(state, world, "Cut", player))
and (state.has("Cascade Badge", player) or state.has(world.extra_badges.get("Cut"), player) or
world.options.badges_needed_for_hm_moves.value == 0))
def can_fly(state, player):
return (((state.has("HM02 Fly", player) and can_learn_hm(state, "Fly", player)) or state.has("Flute", player)) and
(state.has("Thunder Badge", player) or state.has(state.multiworld.worlds[player].extra_badges.get("Fly"), player)
or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
def can_fly(state, world, player):
return (((state.has("HM02 Fly", player) and can_learn_hm(state, world, "Fly", player)) or state.has("Flute", player)) and
(state.has("Thunder Badge", player) or state.has(world.extra_badges.get("Fly"), player)
or world.options.badges_needed_for_hm_moves.value == 0))
def can_strength(state, player):
return ((state.has("HM04 Strength", player) and can_learn_hm(state, "Strength", player)) or
def can_strength(state, world, player):
return ((state.has("HM04 Strength", player) and can_learn_hm(state, world, "Strength", player)) or
state.has("Titan's Mitt", player)) and (state.has("Rainbow Badge", player) or
state.has(state.multiworld.worlds[player].extra_badges.get("Strength"), player)
or state.multiworld.badges_needed_for_hm_moves[player].value == 0)
state.has(world.extra_badges.get("Strength"), player)
or world.options.badges_needed_for_hm_moves.value == 0)
def can_flash(state, player):
return (((state.has("HM05 Flash", player) and can_learn_hm(state, "Flash", player)) or state.has("Lamp", player))
and (state.has("Boulder Badge", player) or state.has(state.multiworld.worlds[player].extra_badges.get("Flash"),
player) or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
def can_flash(state, world, player):
return (((state.has("HM05 Flash", player) and can_learn_hm(state, world, "Flash", player)) or state.has("Lamp", player))
and (state.has("Boulder Badge", player) or state.has(world.extra_badges.get("Flash"),
player) or world.options.badges_needed_for_hm_moves.value == 0))
def can_learn_hm(state, move, player):
for pokemon, data in state.multiworld.worlds[player].local_poke_data.items():
def can_learn_hm(state, world, move, player):
for pokemon, data in world.local_poke_data.items():
if state.has(pokemon, player) and data["tms"][6] & 1 << (["Cut", "Fly", "Surf", "Strength",
"Flash"].index(move) + 2):
return True
return False
def can_get_hidden_items(state, player):
return state.has("Item Finder", player) or not state.multiworld.require_item_finder[player].value
def can_get_hidden_items(state, world, player):
return state.has("Item Finder", player) or not world.options.require_item_finder.value
def has_key_items(state, count, player):
@ -53,13 +51,14 @@ def has_key_items(state, count, player):
"Hideout Key", "Card Key 2F", "Card Key 3F", "Card Key 4F", "Card Key 5F",
"Card Key 6F", "Card Key 7F", "Card Key 8F", "Card Key 9F", "Card Key 10F",
"Card Key 11F", "Exp. All", "Fire Stone", "Thunder Stone", "Water Stone",
"Leaf Stone", "Moon Stone"] if state.has(item, player)])
"Leaf Stone", "Moon Stone", "Oak's Parcel", "Helix Fossil", "Dome Fossil",
"Old Amber", "Tea", "Gold Teeth", "Bike Voucher"] if state.has(item, player)])
+ min(state.count("Progressive Card Key", player), 10))
return key_items >= count
def can_pass_guards(state, player):
if state.multiworld.tea[player]:
def can_pass_guards(state, world, player):
if world.options.tea:
return state.has("Tea", player)
else:
return state.has("Vending Machine Drinks", player)
@ -70,8 +69,8 @@ def has_badges(state, count, player):
"Soul Badge", "Volcano Badge", "Earth Badge"] if state.has(item, player)]) >= count
def oaks_aide(state, count, player):
return ((not state.multiworld.require_pokedex[player] or state.has("Pokedex", player))
def oaks_aide(state, world, count, player):
return ((not world.options.require_pokedex or state.has("Pokedex", player))
and has_pokemon(state, count, player))
@ -85,9 +84,7 @@ def has_pokemon(state, count, player):
def fossil_checks(state, count, player):
return (state.can_reach('Mt Moon B2F', 'Region', player) and
state.can_reach('Cinnabar Lab Fossil Room', 'Region', player) and
state.can_reach('Cinnabar Island', 'Region', player) and len(
return (state.has_all(["Mt Moon Fossils", "Cinnabar Lab", "Cinnabar Island"], player) and len(
[item for item in ["Dome Fossil", "Helix Fossil", "Old Amber"] if state.has(item, player)]) >= count)
@ -96,19 +93,19 @@ def card_key(state, floor, player):
state.has("Progressive Card Key", player, floor - 1)
def rock_tunnel(state, player):
return can_flash(state, player) or not state.multiworld.dark_rock_tunnel_logic[player]
def rock_tunnel(state, world, player):
return can_flash(state, world, player) or not world.options.dark_rock_tunnel_logic
def route_3(state, player):
if state.multiworld.route_3_condition[player] == "defeat_brock":
def route(state, world, player):
if world.options.route_3_condition == "defeat_brock":
return state.has("Defeat Brock", player)
elif state.multiworld.route_3_condition[player] == "defeat_any_gym":
elif world.options.route_3_condition == "defeat_any_gym":
return state.has_any(["Defeat Brock", "Defeat Misty", "Defeat Lt. Surge", "Defeat Erika", "Defeat Koga",
"Defeat Blaine", "Defeat Sabrina", "Defeat Viridian Gym Giovanni"], player)
elif state.multiworld.route_3_condition[player] == "boulder_badge":
elif world.options.route_3_condition == "boulder_badge":
return state.has("Boulder Badge", player)
elif state.multiworld.route_3_condition[player] == "any_badge":
elif world.options.route_3_condition == "any_badge":
return state.has_any(["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
"Soul Badge", "Volcano Badge", "Earth Badge"], player)
# open

View File

@ -1,4 +1,6 @@
from Options import Toggle, Choice, Range, NamedRange, TextChoice, DeathLink, ItemsAccessibility
from dataclasses import dataclass
from Options import (PerGameCommonOptions, Toggle, Choice, Range, NamedRange, FreeText, TextChoice, DeathLink,
ItemsAccessibility)
class GameVersion(Choice):
@ -263,12 +265,18 @@ class PrizeSanity(Toggle):
default = 0
class TrainerSanity(Toggle):
"""Add a location check to every trainer in the game, which can be obtained by talking to a trainer after defeating
them. Does not affect gym leaders and some scripted event battles (including all Rival, Giovanni, and
Cinnabar Gym battles)."""
class TrainerSanity(NamedRange):
"""Add location checks to trainers, which can be obtained by talking to a trainer after defeating them. Does not
affect gym leaders and some scripted event battles. You may specify a number of trainers to have checks, and in
this case they will be randomly selected. There is no in-game indication as to which trainers have checks."""
display_name = "Trainersanity"
default = 0
range_start = 0
range_end = 317
special_range_names = {
"disabled": 0,
"full": 317
}
class RequirePokedex(Toggle):
@ -286,19 +294,19 @@ class AllPokemonSeen(Toggle):
class DexSanity(NamedRange):
"""Adds location checks for Pokemon flagged "owned" on your Pokedex. You may specify a percentage of Pokemon to
have checks added. If Accessibility is set to full, this will be the percentage of all logically reachable
Pokemon that will get a location check added to it. With items or minimal Accessibility, it will be the percentage
of all 151 Pokemon.
If Pokedex is required, the items for Pokemon acquired before acquiring the Pokedex can be found by talking to
Professor Oak or evaluating the Pokedex via Oak's PC."""
"""Adds location checks for Pokemon flagged "owned" on your Pokedex. You may specify the exact number of Dexsanity
checks to add, and they will be distributed to Pokemon randomly.
If Accessibility is set to Full, Dexsanity checks for Pokemon that are not logically reachable will be removed,
so the number could be lower than you specified.
If Pokedex is required, the Dexsanity checks for Pokemon you acquired before acquiring the Pokedex can be found by
talking to Professor Oak or evaluating the Pokedex via Oak's PC."""
display_name = "Dexsanity"
default = 0
range_start = 0
range_end = 100
range_end = 151
special_range_names = {
"disabled": 0,
"full": 100
"full": 151
}
@ -519,7 +527,8 @@ class TrainerLegendaries(Toggle):
class BlindTrainers(Range):
"""Chance each frame that you are standing on a tile in a trainer's line of sight that they will fail to initiate a
battle. If you move into and out of their line of sight without stopping, this chance will only trigger once."""
battle. If you move into and out of their line of sight without stopping, this chance will only trigger once.
Trainers which have Trainersanity location checks ignore the Blind Trainers setting."""
display_name = "Blind Trainers"
range_start = 0
range_end = 100
@ -704,6 +713,15 @@ class RandomizeTypeChart(Choice):
default = 0
class TypeChartSeed(FreeText):
"""You can enter a number to use as a seed for the type chart. If you enter anything besides a number or "random",
it will be used as a type chart group name, and everyone using the same group name will get the same type chart,
made using the type chart options of one random player within the group. If a group name is used, the type matchup
information will not be made available for trackers."""
display_name = "Type Chart Seed"
default = "random"
class NormalMatchups(Range):
"""If 'randomize' is chosen for Randomize Type Chart, this will be the weight for neutral matchups.
No effect if 'chaos' is chosen"""
@ -850,8 +868,8 @@ class BicycleGateSkips(Choice):
class RandomizePokemonPalettes(Choice):
"""Modify palettes of Pokemon. Primary Type will set Pokemons' palettes based on their primary type, Follow
Evolutions will randomize palettes but palettes will remain the same through evolutions (except Eeveelutions),
"""Modify Super Gameboy palettes of Pokemon. Primary Type will set Pokemons' palettes based on their primary type,
Follow Evolutions will randomize palettes but they will remain the same through evolutions (except Eeveelutions),
Completely Random will randomize all Pokemons' palettes individually"""
display_name = "Randomize Pokemon Palettes"
option_vanilla = 0
@ -860,104 +878,105 @@ class RandomizePokemonPalettes(Choice):
option_completely_random = 3
pokemon_rb_options = {
"accessibility": ItemsAccessibility,
"game_version": GameVersion,
"trainer_name": TrainerName,
"rival_name": RivalName,
#"goal": Goal,
"elite_four_badges_condition": EliteFourBadgesCondition,
"elite_four_key_items_condition": EliteFourKeyItemsCondition,
"elite_four_pokedex_condition": EliteFourPokedexCondition,
"victory_road_condition": VictoryRoadCondition,
"route_22_gate_condition": Route22GateCondition,
"viridian_gym_condition": ViridianGymCondition,
"cerulean_cave_badges_condition": CeruleanCaveBadgesCondition,
"cerulean_cave_key_items_condition": CeruleanCaveKeyItemsCondition,
"route_3_condition": Route3Condition,
"robbed_house_officer": RobbedHouseOfficer,
"second_fossil_check_condition": SecondFossilCheckCondition,
"fossil_check_item_types": FossilCheckItemTypes,
"exp_all": ExpAll,
"old_man": OldMan,
"badgesanity": BadgeSanity,
"badges_needed_for_hm_moves": BadgesNeededForHMMoves,
"key_items_only": KeyItemsOnly,
"tea": Tea,
"extra_key_items": ExtraKeyItems,
"split_card_key": SplitCardKey,
"all_elevators_locked": AllElevatorsLocked,
"extra_strength_boulders": ExtraStrengthBoulders,
"require_item_finder": RequireItemFinder,
"randomize_hidden_items": RandomizeHiddenItems,
"prizesanity": PrizeSanity,
"trainersanity": TrainerSanity,
"dexsanity": DexSanity,
"randomize_pokedex": RandomizePokedex,
"require_pokedex": RequirePokedex,
"all_pokemon_seen": AllPokemonSeen,
"oaks_aide_rt_2": OaksAidRt2,
"oaks_aide_rt_11": OaksAidRt11,
"oaks_aide_rt_15": OaksAidRt15,
"stonesanity": Stonesanity,
"door_shuffle": DoorShuffle,
"warp_tile_shuffle": WarpTileShuffle,
"randomize_rock_tunnel": RandomizeRockTunnel,
"dark_rock_tunnel_logic": DarkRockTunnelLogic,
"free_fly_location": FreeFlyLocation,
"town_map_fly_location": TownMapFlyLocation,
"blind_trainers": BlindTrainers,
"minimum_steps_between_encounters": MinimumStepsBetweenEncounters,
"level_scaling": LevelScaling,
"exp_modifier": ExpModifier,
"randomize_wild_pokemon": RandomizeWildPokemon,
"area_1_to_1_mapping": Area1To1Mapping,
"randomize_starter_pokemon": RandomizeStarterPokemon,
"randomize_static_pokemon": RandomizeStaticPokemon,
"randomize_legendary_pokemon": RandomizeLegendaryPokemon,
"catch_em_all": CatchEmAll,
"randomize_pokemon_stats": RandomizePokemonStats,
"randomize_pokemon_catch_rates": RandomizePokemonCatchRates,
"minimum_catch_rate": MinimumCatchRate,
"randomize_trainer_parties": RandomizeTrainerParties,
"trainer_legendaries": TrainerLegendaries,
"move_balancing": MoveBalancing,
"fix_combat_bugs": FixCombatBugs,
"randomize_pokemon_movesets": RandomizePokemonMovesets,
"confine_transform_to_ditto": ConfineTranstormToDitto,
"start_with_four_moves": StartWithFourMoves,
"same_type_attack_bonus": SameTypeAttackBonus,
"randomize_tm_moves": RandomizeTMMoves,
"tm_same_type_compatibility": TMSameTypeCompatibility,
"tm_normal_type_compatibility": TMNormalTypeCompatibility,
"tm_other_type_compatibility": TMOtherTypeCompatibility,
"hm_same_type_compatibility": HMSameTypeCompatibility,
"hm_normal_type_compatibility": HMNormalTypeCompatibility,
"hm_other_type_compatibility": HMOtherTypeCompatibility,
"inherit_tm_hm_compatibility": InheritTMHMCompatibility,
"randomize_move_types": RandomizeMoveTypes,
"randomize_pokemon_types": RandomizePokemonTypes,
"secondary_type_chance": SecondaryTypeChance,
"randomize_type_chart": RandomizeTypeChart,
"normal_matchups": NormalMatchups,
"super_effective_matchups": SuperEffectiveMatchups,
"not_very_effective_matchups": NotVeryEffectiveMatchups,
"immunity_matchups": ImmunityMatchups,
"safari_zone_normal_battles": SafariZoneNormalBattles,
"normalize_encounter_chances": NormalizeEncounterChances,
"reusable_tms": ReusableTMs,
"better_shops": BetterShops,
"master_ball_price": MasterBallPrice,
"starting_money": StartingMoney,
"lose_money_on_blackout": LoseMoneyOnBlackout,
"poke_doll_skip": PokeDollSkip,
"bicycle_gate_skips": BicycleGateSkips,
"trap_percentage": TrapPercentage,
"poison_trap_weight": PoisonTrapWeight,
"fire_trap_weight": FireTrapWeight,
"paralyze_trap_weight": ParalyzeTrapWeight,
"sleep_trap_weight": SleepTrapWeight,
"ice_trap_weight": IceTrapWeight,
"randomize_pokemon_palettes": RandomizePokemonPalettes,
"death_link": DeathLink
}
@dataclass
class PokemonRBOptions(PerGameCommonOptions):
accessibility: ItemsAccessibility
game_version: GameVersion
trainer_name: TrainerName
rival_name: RivalName
# goal: Goal
elite_four_badges_condition: EliteFourBadgesCondition
elite_four_key_items_condition: EliteFourKeyItemsCondition
elite_four_pokedex_condition: EliteFourPokedexCondition
victory_road_condition: VictoryRoadCondition
route_22_gate_condition: Route22GateCondition
viridian_gym_condition: ViridianGymCondition
cerulean_cave_badges_condition: CeruleanCaveBadgesCondition
cerulean_cave_key_items_condition: CeruleanCaveKeyItemsCondition
route_3_condition: Route3Condition
robbed_house_officer: RobbedHouseOfficer
second_fossil_check_condition: SecondFossilCheckCondition
fossil_check_item_types: FossilCheckItemTypes
exp_all: ExpAll
old_man: OldMan
badgesanity: BadgeSanity
badges_needed_for_hm_moves: BadgesNeededForHMMoves
key_items_only: KeyItemsOnly
tea: Tea
extra_key_items: ExtraKeyItems
split_card_key: SplitCardKey
all_elevators_locked: AllElevatorsLocked
extra_strength_boulders: ExtraStrengthBoulders
require_item_finder: RequireItemFinder
randomize_hidden_items: RandomizeHiddenItems
prizesanity: PrizeSanity
trainersanity: TrainerSanity
dexsanity: DexSanity
randomize_pokedex: RandomizePokedex
require_pokedex: RequirePokedex
all_pokemon_seen: AllPokemonSeen
oaks_aide_rt_2: OaksAidRt2
oaks_aide_rt_11: OaksAidRt11
oaks_aide_rt_15: OaksAidRt15
stonesanity: Stonesanity
door_shuffle: DoorShuffle
warp_tile_shuffle: WarpTileShuffle
randomize_rock_tunnel: RandomizeRockTunnel
dark_rock_tunnel_logic: DarkRockTunnelLogic
free_fly_location: FreeFlyLocation
town_map_fly_location: TownMapFlyLocation
blind_trainers: BlindTrainers
minimum_steps_between_encounters: MinimumStepsBetweenEncounters
level_scaling: LevelScaling
exp_modifier: ExpModifier
randomize_wild_pokemon: RandomizeWildPokemon
area_1_to_1_mapping: Area1To1Mapping
randomize_starter_pokemon: RandomizeStarterPokemon
randomize_static_pokemon: RandomizeStaticPokemon
randomize_legendary_pokemon: RandomizeLegendaryPokemon
catch_em_all: CatchEmAll
randomize_pokemon_stats: RandomizePokemonStats
randomize_pokemon_catch_rates: RandomizePokemonCatchRates
minimum_catch_rate: MinimumCatchRate
randomize_trainer_parties: RandomizeTrainerParties
trainer_legendaries: TrainerLegendaries
move_balancing: MoveBalancing
fix_combat_bugs: FixCombatBugs
randomize_pokemon_movesets: RandomizePokemonMovesets
confine_transform_to_ditto: ConfineTranstormToDitto
start_with_four_moves: StartWithFourMoves
same_type_attack_bonus: SameTypeAttackBonus
randomize_tm_moves: RandomizeTMMoves
tm_same_type_compatibility: TMSameTypeCompatibility
tm_normal_type_compatibility: TMNormalTypeCompatibility
tm_other_type_compatibility: TMOtherTypeCompatibility
hm_same_type_compatibility: HMSameTypeCompatibility
hm_normal_type_compatibility: HMNormalTypeCompatibility
hm_other_type_compatibility: HMOtherTypeCompatibility
inherit_tm_hm_compatibility: InheritTMHMCompatibility
randomize_move_types: RandomizeMoveTypes
randomize_pokemon_types: RandomizePokemonTypes
secondary_type_chance: SecondaryTypeChance
randomize_type_chart: RandomizeTypeChart
normal_matchups: NormalMatchups
super_effective_matchups: SuperEffectiveMatchups
not_very_effective_matchups: NotVeryEffectiveMatchups
immunity_matchups: ImmunityMatchups
type_chart_seed: TypeChartSeed
safari_zone_normal_battles: SafariZoneNormalBattles
normalize_encounter_chances: NormalizeEncounterChances
reusable_tms: ReusableTMs
better_shops: BetterShops
master_ball_price: MasterBallPrice
starting_money: StartingMoney
lose_money_on_blackout: LoseMoneyOnBlackout
poke_doll_skip: PokeDollSkip
bicycle_gate_skips: BicycleGateSkips
trap_percentage: TrapPercentage
poison_trap_weight: PoisonTrapWeight
fire_trap_weight: FireTrapWeight
paralyze_trap_weight: ParalyzeTrapWeight
sleep_trap_weight: SleepTrapWeight
ice_trap_weight: IceTrapWeight
randomize_pokemon_palettes: RandomizePokemonPalettes
death_link: DeathLink

View File

@ -3,8 +3,8 @@ from . import poke_data, logic
from .rom_addresses import rom_addresses
def set_mon_palettes(self, random, data):
if self.multiworld.randomize_pokemon_palettes[self.player] == "vanilla":
def set_mon_palettes(world, random, data):
if world.options.randomize_pokemon_palettes == "vanilla":
return
pallet_map = {
"Poison": 0x0F,
@ -25,9 +25,9 @@ def set_mon_palettes(self, random, data):
}
palettes = []
for mon in poke_data.pokemon_data:
if self.multiworld.randomize_pokemon_palettes[self.player] == "primary_type":
pallet = pallet_map[self.local_poke_data[mon]["type1"]]
elif (self.multiworld.randomize_pokemon_palettes[self.player] == "follow_evolutions" and mon in
if world.options.randomize_pokemon_palettes == "primary_type":
pallet = pallet_map[world.local_poke_data[mon]["type1"]]
elif (world.options.randomize_pokemon_palettes == "follow_evolutions" and mon in
poke_data.evolves_from and poke_data.evolves_from[mon] != "Eevee"):
pallet = palettes[-1]
else: # completely_random or follow_evolutions and it is not an evolved form (except eeveelutions)
@ -93,40 +93,41 @@ def move_power(move_data):
return power
def process_move_data(self):
self.local_move_data = deepcopy(poke_data.moves)
def process_move_data(world):
world.local_move_data = deepcopy(poke_data.moves)
if self.multiworld.randomize_move_types[self.player]:
for move, data in self.local_move_data.items():
if world.options.randomize_move_types:
for move, data in world.local_move_data.items():
if move == "No Move":
continue
# The chance of randomized moves choosing a normal type move is high, so we want to retain having a higher
# rate of normal type moves
data["type"] = self.multiworld.random.choice(list(poke_data.type_ids) + (["Normal"] * 4))
data["type"] = world.random.choice(list(poke_data.type_ids) + (["Normal"] * 4))
if self.multiworld.move_balancing[self.player]:
self.local_move_data["Sing"]["accuracy"] = 30
self.local_move_data["Sleep Powder"]["accuracy"] = 40
self.local_move_data["Spore"]["accuracy"] = 50
self.local_move_data["Sonicboom"]["effect"] = 0
self.local_move_data["Sonicboom"]["power"] = 50
self.local_move_data["Dragon Rage"]["effect"] = 0
self.local_move_data["Dragon Rage"]["power"] = 80
self.local_move_data["Horn Drill"]["effect"] = 0
self.local_move_data["Horn Drill"]["power"] = 70
self.local_move_data["Horn Drill"]["accuracy"] = 90
self.local_move_data["Guillotine"]["effect"] = 0
self.local_move_data["Guillotine"]["power"] = 70
self.local_move_data["Guillotine"]["accuracy"] = 90
self.local_move_data["Fissure"]["effect"] = 0
self.local_move_data["Fissure"]["power"] = 70
self.local_move_data["Fissure"]["accuracy"] = 90
self.local_move_data["Blizzard"]["accuracy"] = 70
if self.multiworld.randomize_tm_moves[self.player]:
self.local_tms = self.multiworld.random.sample([move for move in poke_data.moves.keys() if move not in
["No Move"] + poke_data.hm_moves], 50)
if world.options.move_balancing:
world.local_move_data["Sing"]["accuracy"] = 30
world.local_move_data["Sleep Powder"]["accuracy"] = 40
world.local_move_data["Spore"]["accuracy"] = 50
world.local_move_data["Sonicboom"]["effect"] = 0
world.local_move_data["Sonicboom"]["power"] = 50
world.local_move_data["Dragon Rage"]["effect"] = 0
world.local_move_data["Dragon Rage"]["power"] = 80
world.local_move_data["Horn Drill"]["effect"] = 0
world.local_move_data["Horn Drill"]["power"] = 70
world.local_move_data["Horn Drill"]["accuracy"] = 90
world.local_move_data["Guillotine"]["effect"] = 0
world.local_move_data["Guillotine"]["power"] = 70
world.local_move_data["Guillotine"]["accuracy"] = 90
world.local_move_data["Fissure"]["effect"] = 0
world.local_move_data["Fissure"]["power"] = 70
world.local_move_data["Fissure"]["accuracy"] = 90
world.local_move_data["Blizzard"]["accuracy"] = 70
if world.options.randomize_tm_moves:
world.local_tms = world.random.sample([move for move in poke_data.moves.keys() if move not in
["No Move"] + poke_data.hm_moves], 50)
else:
self.local_tms = poke_data.tm_moves.copy()
world.local_tms = poke_data.tm_moves.copy()
def process_pokemon_data(self):
@ -138,12 +139,12 @@ def process_pokemon_data(self):
compat_hms = set()
for mon, mon_data in local_poke_data.items():
if self.multiworld.randomize_pokemon_stats[self.player] == "shuffle":
if self.options.randomize_pokemon_stats == "shuffle":
stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]]
if mon in poke_data.evolves_from:
stat_shuffle_map = local_poke_data[poke_data.evolves_from[mon]]["stat_shuffle_map"]
else:
stat_shuffle_map = self.multiworld.random.sample(range(0, 5), 5)
stat_shuffle_map = self.random.sample(range(0, 5), 5)
mon_data["stat_shuffle_map"] = stat_shuffle_map
mon_data["hp"] = stats[stat_shuffle_map[0]]
@ -151,7 +152,7 @@ def process_pokemon_data(self):
mon_data["def"] = stats[stat_shuffle_map[2]]
mon_data["spd"] = stats[stat_shuffle_map[3]]
mon_data["spc"] = stats[stat_shuffle_map[4]]
elif self.multiworld.randomize_pokemon_stats[self.player] == "randomize":
elif self.options.randomize_pokemon_stats == "randomize":
first_run = True
while (mon_data["hp"] > 255 or mon_data["atk"] > 255 or mon_data["def"] > 255 or mon_data["spd"] > 255
or mon_data["spc"] > 255 or first_run):
@ -168,9 +169,9 @@ def process_pokemon_data(self):
mon_data[stat] = 10
total_stats -= 10
assert total_stats >= 0, f"Error distributing stats for {mon} for player {self.player}"
dist = [self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
self.multiworld.random.randint(1, 101) / 100]
dist = [self.random.randint(1, 101) / 100, self.random.randint(1, 101) / 100,
self.random.randint(1, 101) / 100, self.random.randint(1, 101) / 100,
self.random.randint(1, 101) / 100]
total_dist = sum(dist)
mon_data["hp"] += int(round(dist[0] / total_dist * total_stats))
@ -178,30 +179,30 @@ def process_pokemon_data(self):
mon_data["def"] += int(round(dist[2] / total_dist * total_stats))
mon_data["spd"] += int(round(dist[3] / total_dist * total_stats))
mon_data["spc"] += int(round(dist[4] / total_dist * total_stats))
if self.multiworld.randomize_pokemon_types[self.player]:
if self.multiworld.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from:
if self.options.randomize_pokemon_types:
if self.options.randomize_pokemon_types.value == 1 and mon in poke_data.evolves_from:
type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"]
type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"]
if type1 == type2:
if self.multiworld.secondary_type_chance[self.player].value == -1:
if self.options.secondary_type_chance.value == -1:
if mon_data["type1"] != mon_data["type2"]:
while type2 == type1:
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
elif self.multiworld.random.randint(1, 100) <= self.multiworld.secondary_type_chance[self.player].value:
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
type2 = self.random.choice(list(poke_data.type_names.values()))
elif self.random.randint(1, 100) <= self.options.secondary_type_chance.value:
type2 = self.random.choice(list(poke_data.type_names.values()))
else:
type1 = self.multiworld.random.choice(list(poke_data.type_names.values()))
type1 = self.random.choice(list(poke_data.type_names.values()))
type2 = type1
if ((self.multiworld.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
!= mon_data["type2"]) or self.multiworld.random.randint(1, 100)
<= self.multiworld.secondary_type_chance[self.player].value):
if ((self.options.secondary_type_chance.value == -1 and mon_data["type1"]
!= mon_data["type2"]) or self.random.randint(1, 100)
<= self.options.secondary_type_chance.value):
while type2 == type1:
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
type2 = self.random.choice(list(poke_data.type_names.values()))
mon_data["type1"] = type1
mon_data["type2"] = type2
if self.multiworld.randomize_pokemon_movesets[self.player]:
if self.multiworld.randomize_pokemon_movesets[self.player] == "prefer_types":
if self.options.randomize_pokemon_movesets:
if self.options.randomize_pokemon_movesets == "prefer_types":
if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal":
chances = [[75, "Normal"]]
elif mon_data["type1"] == "Normal" or mon_data["type2"] == "Normal":
@ -219,9 +220,9 @@ def process_pokemon_data(self):
moves = list(poke_data.moves.keys())
for move in ["No Move"] + poke_data.hm_moves:
moves.remove(move)
if self.multiworld.confine_transform_to_ditto[self.player]:
if self.options.confine_transform_to_ditto:
moves.remove("Transform")
if self.multiworld.start_with_four_moves[self.player]:
if self.options.start_with_four_moves:
num_moves = 4
else:
num_moves = len([i for i in [mon_data["start move 1"], mon_data["start move 2"],
@ -231,12 +232,12 @@ def process_pokemon_data(self):
non_power_moves = []
learnsets[mon] = []
for i in range(num_moves):
if i == 0 and mon == "Ditto" and self.multiworld.confine_transform_to_ditto[self.player]:
if i == 0 and mon == "Ditto" and self.options.confine_transform_to_ditto:
move = "Transform"
else:
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
while move == "Transform" and self.multiworld.confine_transform_to_ditto[self.player]:
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
move = get_move(self.local_move_data, moves, chances, self.random)
while move == "Transform" and self.options.confine_transform_to_ditto:
move = get_move(self.local_move_data, moves, chances, self.random)
if self.local_move_data[move]["power"] < 5:
non_power_moves.append(move)
else:
@ -244,59 +245,58 @@ def process_pokemon_data(self):
learnsets[mon].sort(key=lambda move: move_power(self.local_move_data[move]))
if learnsets[mon]:
for move in non_power_moves:
learnsets[mon].insert(self.multiworld.random.randint(1, len(learnsets[mon])), move)
learnsets[mon].insert(self.random.randint(1, len(learnsets[mon])), move)
else:
learnsets[mon] = non_power_moves
for i in range(1, 5):
if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[self.player]:
if mon_data[f"start move {i}"] != "No Move" or self.options.start_with_four_moves:
mon_data[f"start move {i}"] = learnsets[mon].pop(0)
if self.multiworld.randomize_pokemon_catch_rates[self.player]:
mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player],
255)
if self.options.randomize_pokemon_catch_rates:
mon_data["catch rate"] = self.random.randint(self.options.minimum_catch_rate, 255)
else:
mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"])
mon_data["catch rate"] = max(self.options.minimum_catch_rate, mon_data["catch rate"])
def roll_tm_compat(roll_move):
if self.local_move_data[roll_move]["type"] in [mon_data["type1"], mon_data["type2"]]:
if roll_move in poke_data.hm_moves:
if self.multiworld.hm_same_type_compatibility[self.player].value == -1:
if self.options.hm_same_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_same_type_compatibility[self.player].value
r = self.random.randint(1, 100) <= self.options.hm_same_type_compatibility.value
if r and mon not in poke_data.legendary_pokemon:
compat_hms.add(roll_move)
return r
else:
if self.multiworld.tm_same_type_compatibility[self.player].value == -1:
if self.options.tm_same_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_same_type_compatibility[self.player].value
return self.random.randint(1, 100) <= self.options.tm_same_type_compatibility.value
elif self.local_move_data[roll_move]["type"] == "Normal" and "Normal" not in [mon_data["type1"], mon_data["type2"]]:
if roll_move in poke_data.hm_moves:
if self.multiworld.hm_normal_type_compatibility[self.player].value == -1:
if self.options.hm_normal_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_normal_type_compatibility[self.player].value
r = self.random.randint(1, 100) <= self.options.hm_normal_type_compatibility.value
if r and mon not in poke_data.legendary_pokemon:
compat_hms.add(roll_move)
return r
else:
if self.multiworld.tm_normal_type_compatibility[self.player].value == -1:
if self.options.tm_normal_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_normal_type_compatibility[self.player].value
return self.random.randint(1, 100) <= self.options.tm_normal_type_compatibility.value
else:
if roll_move in poke_data.hm_moves:
if self.multiworld.hm_other_type_compatibility[self.player].value == -1:
if self.options.hm_other_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_other_type_compatibility[self.player].value
r = self.random.randint(1, 100) <= self.options.hm_other_type_compatibility.value
if r and mon not in poke_data.legendary_pokemon:
compat_hms.add(roll_move)
return r
else:
if self.multiworld.tm_other_type_compatibility[self.player].value == -1:
if self.options.tm_other_type_compatibility.value == -1:
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_other_type_compatibility[self.player].value
return self.random.randint(1, 100) <= self.options.tm_other_type_compatibility.value
for flag, tm_move in enumerate(tms_hms):
if mon in poke_data.evolves_from and self.multiworld.inherit_tm_hm_compatibility[self.player]:
if mon in poke_data.evolves_from and self.options.inherit_tm_hm_compatibility:
if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8):
# always inherit learnable tms/hms
@ -310,7 +310,7 @@ def process_pokemon_data(self):
# so this gets full chance roll
bit = roll_tm_compat(tm_move)
# otherwise 50% reduced chance to add compatibility over pre-evolved form
elif self.multiworld.random.randint(1, 100) > 50 and roll_tm_compat(tm_move):
elif self.random.randint(1, 100) > 50 and roll_tm_compat(tm_move):
bit = 1
else:
bit = 0
@ -322,15 +322,13 @@ def process_pokemon_data(self):
mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8))
hm_verify = ["Surf", "Strength"]
if self.multiworld.accessibility[self.player] != "minimal" or ((not
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_badges_condition[self.player],
self.multiworld.route_22_gate_condition[self.player], self.multiworld.victory_road_condition[self.player])
> 7) or (self.multiworld.door_shuffle[self.player] not in ("off", "simple")):
if self.options.accessibility != "minimal" or ((not
self.options.badgesanity) and max(self.options.elite_four_badges_condition,
self.options.route_22_gate_condition, self.options.victory_road_condition)
> 7) or (self.options.door_shuffle not in ("off", "simple")):
hm_verify += ["Cut"]
if self.multiworld.accessibility[self.player] != "minimal" or (not
self.multiworld.dark_rock_tunnel_logic[self.player]) and ((self.multiworld.trainersanity[self.player] or
self.multiworld.extra_key_items[self.player])
or self.multiworld.door_shuffle[self.player]):
if (self.options.accessibility != "minimal" or (not self.options.dark_rock_tunnel_logic) and
((self.options.trainersanity or self.options.extra_key_items) or self.options.door_shuffle)):
hm_verify += ["Flash"]
# Fly does not need to be verified. Full/Insanity/Decoupled door shuffle connects reachable regions to unreachable
# regions, so if Fly is available and can be learned, the towns you can fly to would be considered reachable for
@ -339,8 +337,7 @@ def process_pokemon_data(self):
for hm_move in hm_verify:
if hm_move not in compat_hms:
mon = self.multiworld.random.choice([mon for mon in poke_data.pokemon_data if mon not in
poke_data.legendary_pokemon])
mon = self.random.choice([mon for mon in poke_data.pokemon_data if mon not in poke_data.legendary_pokemon])
flag = tms_hms.index(hm_move)
local_poke_data[mon]["tms"][int(flag / 8)] |= 1 << (flag % 8)
@ -352,7 +349,7 @@ def verify_hm_moves(multiworld, world, player):
def intervene(move, test_state):
move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
viable_mons = [mon for mon in world.local_poke_data if world.local_poke_data[mon]["tms"][6] & move_bit]
if multiworld.randomize_wild_pokemon[player] and viable_mons:
if world.options.randomize_wild_pokemon and viable_mons:
accessible_slots = [loc for loc in multiworld.get_reachable_locations(test_state, player) if
loc.type == "Wild Encounter"]
@ -364,7 +361,7 @@ def verify_hm_moves(multiworld, world, player):
placed_mons = [slot.item.name for slot in accessible_slots]
if multiworld.area_1_to_1_mapping[player]:
if world.options.area_1_to_1_mapping:
placed_mons.sort(key=lambda i: number_of_zones(i))
else:
# this sort method doesn't work if you reference the same list being sorted in the lambda
@ -372,10 +369,10 @@ def verify_hm_moves(multiworld, world, player):
placed_mons.sort(key=lambda i: placed_mons_copy.count(i))
placed_mon = placed_mons.pop()
replace_mon = multiworld.random.choice(viable_mons)
replace_slot = multiworld.random.choice([slot for slot in accessible_slots if slot.item.name
replace_mon = world.random.choice(viable_mons)
replace_slot = world.random.choice([slot for slot in accessible_slots if slot.item.name
== placed_mon])
if multiworld.area_1_to_1_mapping[player]:
if world.options.area_1_to_1_mapping:
zone = " - ".join(replace_slot.name.split(" - ")[:-1])
replace_slots = [slot for slot in accessible_slots if slot.name.startswith(zone) and slot.item.name
== placed_mon]
@ -387,7 +384,7 @@ def verify_hm_moves(multiworld, world, player):
tms_hms = world.local_tms + poke_data.hm_moves
flag = tms_hms.index(move)
mon_list = [mon for mon in poke_data.pokemon_data.keys() if test_state.has(mon, player)]
multiworld.random.shuffle(mon_list)
world.random.shuffle(mon_list)
mon_list.sort(key=lambda mon: world.local_move_data[move]["type"] not in
[world.local_poke_data[mon]["type1"], world.local_poke_data[mon]["type2"]])
for mon in mon_list:
@ -399,31 +396,31 @@ def verify_hm_moves(multiworld, world, player):
while True:
intervene_move = None
test_state = multiworld.get_all_state(False)
if not logic.can_learn_hm(test_state, "Surf", player):
if not logic.can_learn_hm(test_state, world, "Surf", player):
intervene_move = "Surf"
elif not logic.can_learn_hm(test_state, "Strength", player):
elif not logic.can_learn_hm(test_state, world, "Strength", player):
intervene_move = "Strength"
# cut may not be needed if accessibility is minimal, unless you need all 8 badges and badgesanity is off,
# as you will require cut to access celadon gyn
elif ((not logic.can_learn_hm(test_state, "Cut", player)) and
(multiworld.accessibility[player] != "minimal" or ((not
multiworld.badgesanity[player]) and max(
multiworld.elite_four_badges_condition[player],
multiworld.route_22_gate_condition[player],
multiworld.victory_road_condition[player])
> 7) or (multiworld.door_shuffle[player] not in ("off", "simple")))):
elif ((not logic.can_learn_hm(test_state, world, "Cut", player)) and
(world.options.accessibility != "minimal" or ((not
world.options.badgesanity) and max(
world.options.elite_four_badges_condition,
world.options.route_22_gate_condition,
world.options.victory_road_condition)
> 7) or (world.options.door_shuffle not in ("off", "simple")))):
intervene_move = "Cut"
elif ((not logic.can_learn_hm(test_state, "Flash", player))
and multiworld.dark_rock_tunnel_logic[player]
and (multiworld.accessibility[player] != "minimal"
or multiworld.door_shuffle[player])):
elif ((not logic.can_learn_hm(test_state, world, "Flash", player))
and world.options.dark_rock_tunnel_logic
and (world.options.accessibility != "minimal"
or world.options.door_shuffle)):
intervene_move = "Flash"
# If no Pokémon can learn Fly, then during door shuffle it would simply not treat the free fly maps
# as reachable, and if on no door shuffle or simple, fly is simply never necessary.
# We only intervene if a Pokémon is able to learn fly but none are reachable, as that would have been
# considered in door shuffle.
elif ((not logic.can_learn_hm(test_state, "Fly", player))
and multiworld.door_shuffle[player] not in
elif ((not logic.can_learn_hm(test_state, world, "Fly", player))
and world.options.door_shuffle not in
("off", "simple") and [world.fly_map, world.town_map_fly_map] != ["Pallet Town", "Pallet Town"]):
intervene_move = "Fly"
if intervene_move:
@ -432,4 +429,4 @@ def verify_hm_moves(multiworld, world, player):
intervene(intervene_move, test_state)
last_intervene = intervene_move
else:
break
break

File diff suppressed because it is too large Load Diff

View File

@ -13,22 +13,22 @@ from .regions import PokemonRBWarp, map_ids, town_map_coords
from . import poke_data
def write_quizzes(self, data, random):
def write_quizzes(world, data, random):
def get_quiz(q, a):
if q == 0:
r = random.randint(0, 3)
if r == 0:
mon = self.trade_mons["Trade_Dux"]
mon = world.trade_mons["Trade_Dux"]
text = "A woman in<LINE>Vermilion City<CONT>"
elif r == 1:
mon = self.trade_mons["Trade_Lola"]
mon = world.trade_mons["Trade_Lola"]
text = "A man in<LINE>Cerulean City<CONT>"
elif r == 2:
mon = self.trade_mons["Trade_Marcel"]
mon = world.trade_mons["Trade_Marcel"]
text = "Someone on Route 2<LINE>"
elif r == 3:
mon = self.trade_mons["Trade_Spot"]
mon = world.trade_mons["Trade_Spot"]
text = "Someone on Route 5<LINE>"
if not a:
answers.append(0)
@ -38,21 +38,30 @@ def write_quizzes(self, data, random):
return encode_text(f"{text}was looking for<CONT>{mon}?<DONE>")
elif q == 1:
for location in self.multiworld.get_filled_locations():
if location.item.name == "Secret Key" and location.item.player == self.player:
for location in world.multiworld.get_filled_locations():
if location.item.name == "Secret Key" and location.item.player == world.player:
break
player_name = self.multiworld.player_name[location.player]
player_name = world.multiworld.player_name[location.player]
if not a:
if len(self.multiworld.player_name) > 1:
if len(world.multiworld.player_name) > 1:
old_name = player_name
while old_name == player_name:
player_name = random.choice(list(self.multiworld.player_name.values()))
player_name = random.choice(list(world.multiworld.player_name.values()))
else:
return encode_text("You're playing<LINE>in a multiworld<CONT>with other<CONT>players?<DONE>")
if player_name == self.multiworld.player_name[self.player]:
player_name = "yourself"
player_name = encode_text(player_name, force=True, safety=True)
return encode_text(f"The Secret Key was<LINE>found by<CONT>") + player_name + encode_text("<DONE>")
if world.multiworld.get_entrance(
"Cinnabar Island-G to Cinnabar Gym", world.player).connected_region.name == "Cinnabar Gym":
if player_name == world.multiworld.player_name[world.player]:
player_name = "yourself"
player_name = encode_text(player_name, force=True, safety=True)
return encode_text(f"The Secret Key was<LINE>found by<CONT>") + player_name + encode_text("?<DONE>")
else:
# Might not have found it yet
if player_name == world.multiworld.player_name[world.player]:
return encode_text(f"The Secret Key was<LINE>placed in<CONT>your own world?<DONE>")
player_name = encode_text(player_name, force=True, safety=True)
return (encode_text(f"The Secret Key was<LINE>placed in<CONT>") + player_name
+ encode_text("'s<CONT>world?<DONE>"))
elif q == 2:
if a:
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-kay-mon?<DONE>")
@ -62,8 +71,8 @@ def write_quizzes(self, data, random):
else:
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-kuh-mon?<DONE>")
elif q == 3:
starters = [" ".join(self.multiworld.get_location(
f"Oak's Lab - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
starters = [" ".join(world.multiworld.get_location(
f"Oak's Lab - Starter {i}", world.player).item.name.split(" ")[1:]) for i in range(1, 4)]
mon = random.choice(starters)
nots = random.choice(range(8, 16, 2))
if random.randint(0, 1):
@ -82,10 +91,10 @@ def write_quizzes(self, data, random):
return encode_text(text)
elif q == 4:
if a:
tm_text = self.local_tms[27]
tm_text = world.local_tms[27]
else:
if self.multiworld.randomize_tm_moves[self.player]:
wrong_tms = self.local_tms.copy()
if world.options.randomize_tm_moves:
wrong_tms = world.local_tms.copy()
wrong_tms.pop(27)
tm_text = random.choice(wrong_tms)
else:
@ -102,12 +111,36 @@ def write_quizzes(self, data, random):
i = random.randint(0, random.choice([9, 99]))
return encode_text(f"POLIWAG evolves {i}<LINE>times?<DONE>")
elif q == 7:
entity = "Motor Carrier"
if not a:
entity = random.choice(["Driver", "Shipper"])
return encode_text("Title 49 of the<LINE>U.S. Code of<CONT>Federal<CONT>Regulations part<CONT>397.67 states"
f"<CONT>that the<CONT>{entity}<CONT>is responsible<CONT>for planning<CONT>routes when"
"<CONT>hazardous<CONT>materials are<CONT>transported?<DONE>")
q2 = random.randint(0, 2)
if q2 == 0:
entity = "Motor Carrier"
if not a:
entity = random.choice(["Driver", "Shipper"])
return encode_text("Title 49 of the<LINE>U.S. Code of<CONT>Federal<CONT>Regulations part<CONT>397.67 "
f"states<CONT>that the<CONT>{entity}<CONT>is responsible<CONT>for planning<CONT>"
"routes when<CONT>hazardous<CONT>materials are<CONT>transported?<DONE>")
elif q2 == 1:
if a:
state = random.choice(
['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut',
'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas',
'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota',
'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Jersey', 'New Mexico',
'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania',
'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont',
'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'])
else:
state = "New Hampshire"
return encode_text(
f"As of 2024,<LINE>{state}<CONT>has a law<CONT>requiring all<CONT>front seat vehicle<CONT>occupants to use<CONT>seatbelts?<DONE>")
elif q2 == 2:
if a:
country = random.choice(["The United States", "Mexico", "Canada", "Germany", "France", "China",
"Russia", "Spain", "Brazil", "Ukraine", "Saudi Arabia", "Egypt"])
else:
country = random.choice(["The U.K.", "Pakistan", "India", "Japan", "Australia",
"New Zealand", "Thailand"])
return encode_text(f"As of 2020,<LINE>drivers in<CONT>{country}<CONT>drive on the<CONT>right side of<CONT>the road?<DONE>")
elif q == 8:
mon = random.choice(list(poke_data.evolution_levels.keys()))
level = poke_data.evolution_levels[mon]
@ -115,17 +148,17 @@ def write_quizzes(self, data, random):
level += random.choice(range(1, 6)) * random.choice((-1, 1))
return encode_text(f"{mon} evolves<LINE>at level {level}?<DONE>")
elif q == 9:
move = random.choice(list(self.local_move_data.keys()))
actual_type = self.local_move_data[move]["type"]
move = random.choice(list(world.local_move_data.keys()))
actual_type = world.local_move_data[move]["type"]
question_type = actual_type
while question_type == actual_type and not a:
question_type = random.choice(list(poke_data.type_ids.keys()))
return encode_text(f"{move} is<LINE>{question_type} type?<DONE>")
elif q == 10:
mon = random.choice(list(poke_data.pokemon_data.keys()))
actual_type = self.local_poke_data[mon][random.choice(("type1", "type2"))]
actual_type = world.local_poke_data[mon][random.choice(("type1", "type2"))]
question_type = actual_type
while question_type in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]] and not a:
while question_type in [world.local_poke_data[mon]["type1"], world.local_poke_data[mon]["type2"]] and not a:
question_type = random.choice(list(poke_data.type_ids.keys()))
return encode_text(f"{mon} is<LINE>{question_type} type?<DONE>")
elif q == 11:
@ -147,8 +180,8 @@ def write_quizzes(self, data, random):
return encode_text(f"{equation}<LINE>= {question_result}?<DONE>")
elif q == 12:
route = random.choice((12, 16))
actual_mon = self.multiworld.get_location(f"Route {route} - Sleeping Pokemon",
self.player).item.name.split("Static ")[1]
actual_mon = world.multiworld.get_location(f"Route {route} - Sleeping Pokemon",
world.player).item.name.split("Static ")[1]
question_mon = actual_mon
while question_mon == actual_mon and not a:
question_mon = random.choice(list(poke_data.pokemon_data.keys()))
@ -157,7 +190,7 @@ def write_quizzes(self, data, random):
type1 = random.choice(list(poke_data.type_ids.keys()))
type2 = random.choice(list(poke_data.type_ids.keys()))
eff_msgs = ["super effective<CONT>", "no ", "not very<CONT>effective<CONT>", "normal "]
for matchup in self.type_chart:
for matchup in world.type_chart:
if matchup[0] == type1 and matchup[1] == type2:
if matchup[2] > 10:
eff = eff_msgs[0]
@ -175,15 +208,25 @@ def write_quizzes(self, data, random):
eff = random.choice(eff_msgs)
return encode_text(f"{type1} deals<LINE>{eff}damage to<CONT>{type2} type?<DONE>")
elif q == 14:
fossil_level = self.multiworld.get_location("Fossil Level - Trainer Parties",
self.player).party_data[0]['level']
fossil_level = world.multiworld.get_location("Fossil Level - Trainer Parties",
world.player).party_data[0]['level']
if not a:
fossil_level += random.choice((-5, 5))
return encode_text(f"Fossil #MON<LINE>revive at level<CONT>{fossil_level}?<DONE>")
elif q == 15:
if a:
fodmap = random.choice(["garlic", "onion", "milk", "watermelon", "cherries", "wheat", "barley",
"pistachios", "cashews", "kidney beans", "apples", "honey"])
else:
fodmap = random.choice(["carrots", "potatoes", "oranges", "pineapple", "blueberries", "parmesan",
"eggs", "beef", "chicken", "oat", "rice", "maple syrup", "peanuts"])
are_is = "are" if fodmap[-1] == "s" else "is"
return encode_text(f"According to<LINE>Monash Uni.,<CONT>{fodmap} {are_is}<CONT>considered high<CONT>in FODMAPs?<DONE>")
answers = [random.randint(0, 1) for _ in range(6)]
questions = random.sample((range(0, 15)), 6)
questions = random.sample((range(0, 16)), 6)
question_texts = []
for i, question in enumerate(questions):
question_texts.append(get_quiz(question, answers[i]))
@ -193,9 +236,9 @@ def write_quizzes(self, data, random):
write_bytes(data, question_texts[i], rom_addresses[f"Text_Quiz_{quiz}"])
def generate_output(self, output_directory: str):
random = self.multiworld.per_slot_randoms[self.player]
game_version = self.multiworld.game_version[self.player].current_key
def generate_output(world, output_directory: str):
random = world.random
game_version = world.options.game_version.current_key
data = bytes(get_base_rom_bytes(game_version))
base_patch = pkgutil.get_data(__name__, f'basepatch_{game_version}.bsdiff4')
@ -205,8 +248,8 @@ def generate_output(self, output_directory: str):
basemd5 = hashlib.md5()
basemd5.update(data)
pallet_connections = {entrance: self.multiworld.get_entrance(f"Pallet Town to {entrance}",
self.player).connected_region.name for
pallet_connections = {entrance: world.multiworld.get_entrance(f"Pallet Town to {entrance}",
world.player).connected_region.name for
entrance in ["Player's House 1F", "Oak's Lab",
"Rival's House"]}
paths = None
@ -222,11 +265,11 @@ def generate_output(self, output_directory: str):
elif pallet_connections["Oak's Lab"] == "Player's House 1F":
write_bytes(data, [0x5F, 0xC7, 0x0C, 0x0C, 0x00, 0x00], rom_addresses["Pallet_Fly_Coords"])
for region in self.multiworld.get_regions(self.player):
for region in world.multiworld.get_regions(world.player):
for entrance in region.exits:
if isinstance(entrance, PokemonRBWarp):
self.multiworld.spoiler.set_entrance(entrance.name, entrance.connected_region.name, "entrance",
self.player)
world.multiworld.spoiler.set_entrance(entrance.name, entrance.connected_region.name, "entrance",
world.player)
warp_ids = (entrance.warp_id,) if isinstance(entrance.warp_id, int) else entrance.warp_id
warp_to_ids = (entrance.target,) if isinstance(entrance.target, int) else entrance.target
for i, warp_id in enumerate(warp_ids):
@ -241,32 +284,32 @@ def generate_output(self, output_directory: str):
data[address] = 0 if "Elevator" in connected_map_name else warp_to_ids[i]
data[address + 1] = map_ids[connected_map_name]
if self.multiworld.door_shuffle[self.player] == "simple":
if world.options.door_shuffle == "simple":
for (entrance, _, _, map_coords_entries, map_name, _) in town_map_coords.values():
destination = self.multiworld.get_entrance(entrance, self.player).connected_region.name
destination = world.multiworld.get_entrance(entrance, world.player).connected_region.name
(_, x, y, _, _, map_order_entry) = town_map_coords[destination]
for map_coord_entry in map_coords_entries:
data[rom_addresses["Town_Map_Coords"] + (map_coord_entry * 4) + 1] = (y << 4) | x
data[rom_addresses["Town_Map_Order"] + map_order_entry] = map_ids[map_name]
if not self.multiworld.key_items_only[self.player]:
if not world.options.key_items_only:
for i, gym_leader in enumerate(("Pewter Gym - Brock TM", "Cerulean Gym - Misty TM",
"Vermilion Gym - Lt. Surge TM", "Celadon Gym - Erika TM",
"Fuchsia Gym - Koga TM", "Saffron Gym - Sabrina TM",
"Cinnabar Gym - Blaine TM", "Viridian Gym - Giovanni TM")):
item_name = self.multiworld.get_location(gym_leader, self.player).item.name
item_name = world.multiworld.get_location(gym_leader, world.player).item.name
if item_name.startswith("TM"):
try:
tm = int(item_name[2:4])
move = poke_data.moves[self.local_tms[tm - 1]]["id"]
move = poke_data.moves[world.local_tms[tm - 1]]["id"]
data[rom_addresses["Gym_Leader_Moves"] + (2 * i)] = move
except KeyError:
pass
def set_trade_mon(address, loc):
mon = self.multiworld.get_location(loc, self.player).item.name
mon = world.multiworld.get_location(loc, world.player).item.name
data[rom_addresses[address]] = poke_data.pokemon_data[mon]["id"]
self.trade_mons[address] = mon
world.trade_mons[address] = mon
if game_version == "red":
set_trade_mon("Trade_Terry", "Safari Zone Center - Wild Pokemon - 5")
@ -282,10 +325,10 @@ def generate_output(self, output_directory: str):
set_trade_mon("Trade_Doris", "Cerulean Cave 1F - Wild Pokemon - 9")
set_trade_mon("Trade_Crinkles", "Route 12 - Wild Pokemon - 4")
data[rom_addresses['Fly_Location']] = self.fly_map_code
data[rom_addresses['Map_Fly_Location']] = self.town_map_fly_map_code
data[rom_addresses['Fly_Location']] = world.fly_map_code
data[rom_addresses['Map_Fly_Location']] = world.town_map_fly_map_code
if self.multiworld.fix_combat_bugs[self.player]:
if world.options.fix_combat_bugs:
data[rom_addresses["Option_Fix_Combat_Bugs"]] = 1
data[rom_addresses["Option_Fix_Combat_Bugs_Focus_Energy"]] = 0x28 # jr z
data[rom_addresses["Option_Fix_Combat_Bugs_HP_Drain_Dream_Eater"]] = 0x1A # ld a, (de)
@ -298,25 +341,25 @@ def generate_output(self, output_directory: str):
data[rom_addresses["Option_Fix_Combat_Bugs_Heal_Effect"] + 1] = 5 # 5 bytes ahead
data[rom_addresses["Option_Fix_Combat_Bugs_Heal_Stat_Modifiers"]] = 1
if self.multiworld.poke_doll_skip[self.player] == "in_logic":
if world.options.poke_doll_skip == "in_logic":
data[rom_addresses["Option_Silph_Scope_Skip"]] = 0x00 # nop
data[rom_addresses["Option_Silph_Scope_Skip"] + 1] = 0x00 # nop
data[rom_addresses["Option_Silph_Scope_Skip"] + 2] = 0x00 # nop
if self.multiworld.bicycle_gate_skips[self.player] == "patched":
if world.options.bicycle_gate_skips == "patched":
data[rom_addresses["Option_Route_16_Gate_Fix"]] = 0x00 # nop
data[rom_addresses["Option_Route_16_Gate_Fix"] + 1] = 0x00 # nop
data[rom_addresses["Option_Route_18_Gate_Fix"]] = 0x00 # nop
data[rom_addresses["Option_Route_18_Gate_Fix"] + 1] = 0x00 # nop
if self.multiworld.door_shuffle[self.player]:
if world.options.door_shuffle:
data[rom_addresses["Entrance_Shuffle_Fuji_Warp"]] = 1 # prevent warping to Fuji's House from Pokemon Tower 7F
if self.multiworld.all_elevators_locked[self.player]:
if world.options.all_elevators_locked:
data[rom_addresses["Option_Locked_Elevator_Celadon"]] = 0x20 # jr nz
data[rom_addresses["Option_Locked_Elevator_Silph"]] = 0x20 # jr nz
if self.multiworld.tea[self.player].value:
if world.options.tea:
data[rom_addresses["Option_Tea"]] = 1
data[rom_addresses["Guard_Drink_List"]] = 0x54
data[rom_addresses["Guard_Drink_List"] + 1] = 0
@ -325,90 +368,94 @@ def generate_output(self, output_directory: str):
"<PARA>Oh wait there,<LINE>the road's closed.<DONE>"),
rom_addresses["Text_Saffron_Gate"])
data[rom_addresses["Tea_Key_Item_A"]] = 0x28 # jr .z
data[rom_addresses["Tea_Key_Item_B"]] = 0x28 # jr .z
data[rom_addresses["Tea_Key_Item_C"]] = 0x28 # jr .z
data[rom_addresses["Fossils_Needed_For_Second_Item"]] = (
self.multiworld.second_fossil_check_condition[self.player].value)
world.options.second_fossil_check_condition.value)
data[rom_addresses["Option_Lose_Money"]] = int(not self.multiworld.lose_money_on_blackout[self.player].value)
data[rom_addresses["Option_Lose_Money"]] = int(not world.options.lose_money_on_blackout.value)
if self.multiworld.extra_key_items[self.player]:
if world.options.extra_key_items:
data[rom_addresses['Option_Extra_Key_Items_A']] = 1
data[rom_addresses['Option_Extra_Key_Items_B']] = 1
data[rom_addresses['Option_Extra_Key_Items_C']] = 1
data[rom_addresses['Option_Extra_Key_Items_D']] = 1
data[rom_addresses["Option_Split_Card_Key"]] = self.multiworld.split_card_key[self.player].value
data[rom_addresses["Option_Blind_Trainers"]] = round(self.multiworld.blind_trainers[self.player].value * 2.55)
data[rom_addresses["Option_Cerulean_Cave_Badges"]] = self.multiworld.cerulean_cave_badges_condition[self.player].value
data[rom_addresses["Option_Cerulean_Cave_Key_Items"]] = self.multiworld.cerulean_cave_key_items_condition[self.player].total
write_bytes(data, encode_text(str(self.multiworld.cerulean_cave_badges_condition[self.player].value)), rom_addresses["Text_Cerulean_Cave_Badges"])
write_bytes(data, encode_text(str(self.multiworld.cerulean_cave_key_items_condition[self.player].total) + " key items."), rom_addresses["Text_Cerulean_Cave_Key_Items"])
data[rom_addresses['Option_Encounter_Minimum_Steps']] = self.multiworld.minimum_steps_between_encounters[self.player].value
data[rom_addresses['Option_Route23_Badges']] = self.multiworld.victory_road_condition[self.player].value
data[rom_addresses['Option_Victory_Road_Badges']] = self.multiworld.route_22_gate_condition[self.player].value
data[rom_addresses['Option_Elite_Four_Pokedex']] = self.multiworld.elite_four_pokedex_condition[self.player].total
data[rom_addresses['Option_Elite_Four_Key_Items']] = self.multiworld.elite_four_key_items_condition[self.player].total
data[rom_addresses['Option_Elite_Four_Badges']] = self.multiworld.elite_four_badges_condition[self.player].value
write_bytes(data, encode_text(str(self.multiworld.elite_four_badges_condition[self.player].value)), rom_addresses["Text_Elite_Four_Badges"])
write_bytes(data, encode_text(str(self.multiworld.elite_four_key_items_condition[self.player].total) + " key items, and"), rom_addresses["Text_Elite_Four_Key_Items"])
write_bytes(data, encode_text(str(self.multiworld.elite_four_pokedex_condition[self.player].total) + " #MON"), rom_addresses["Text_Elite_Four_Pokedex"])
write_bytes(data, encode_text(str(self.total_key_items), length=2), rom_addresses["Trainer_Screen_Total_Key_Items"])
data[rom_addresses["Option_Split_Card_Key"]] = world.options.split_card_key.value
data[rom_addresses["Option_Blind_Trainers"]] = round(world.options.blind_trainers.value * 2.55)
data[rom_addresses["Option_Cerulean_Cave_Badges"]] = world.options.cerulean_cave_badges_condition.value
data[rom_addresses["Option_Cerulean_Cave_Key_Items"]] = world.options.cerulean_cave_key_items_condition.total
write_bytes(data, encode_text(str(world.options.cerulean_cave_badges_condition.value)), rom_addresses["Text_Cerulean_Cave_Badges"])
write_bytes(data, encode_text(str(world.options.cerulean_cave_key_items_condition.total) + " key items."), rom_addresses["Text_Cerulean_Cave_Key_Items"])
data[rom_addresses['Option_Encounter_Minimum_Steps']] = world.options.minimum_steps_between_encounters.value
data[rom_addresses['Option_Route23_Badges']] = world.options.victory_road_condition.value
data[rom_addresses['Option_Victory_Road_Badges']] = world.options.route_22_gate_condition.value
data[rom_addresses['Option_Elite_Four_Pokedex']] = world.options.elite_four_pokedex_condition.total
data[rom_addresses['Option_Elite_Four_Key_Items']] = world.options.elite_four_key_items_condition.total
data[rom_addresses['Option_Elite_Four_Badges']] = world.options.elite_four_badges_condition.value
write_bytes(data, encode_text(str(world.options.elite_four_badges_condition.value)), rom_addresses["Text_Elite_Four_Badges"])
write_bytes(data, encode_text(str(world.options.elite_four_key_items_condition.total) + " key items, and"), rom_addresses["Text_Elite_Four_Key_Items"])
write_bytes(data, encode_text(str(world.options.elite_four_pokedex_condition.total) + " #MON"), rom_addresses["Text_Elite_Four_Pokedex"])
write_bytes(data, encode_text(str(world.total_key_items), length=2), rom_addresses["Trainer_Screen_Total_Key_Items"])
data[rom_addresses['Option_Viridian_Gym_Badges']] = self.multiworld.viridian_gym_condition[self.player].value
data[rom_addresses['Option_EXP_Modifier']] = self.multiworld.exp_modifier[self.player].value
if not self.multiworld.require_item_finder[self.player]:
data[rom_addresses['Option_Viridian_Gym_Badges']] = world.options.viridian_gym_condition.value
data[rom_addresses['Option_EXP_Modifier']] = world.options.exp_modifier.value
if not world.options.require_item_finder:
data[rom_addresses['Option_Itemfinder']] = 0 # nop
if self.multiworld.extra_strength_boulders[self.player]:
if world.options.extra_strength_boulders:
for i in range(0, 3):
data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15
if self.multiworld.extra_key_items[self.player]:
if world.options.extra_key_items:
for i in range(0, 4):
data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15
if self.multiworld.old_man[self.player] == "open_viridian_city":
if world.options.old_man == "open_viridian_city":
data[rom_addresses['Option_Old_Man']] = 0x11
data[rom_addresses['Option_Old_Man_Lying']] = 0x15
data[rom_addresses['Option_Route3_Guard_B']] = self.multiworld.route_3_condition[self.player].value
if self.multiworld.route_3_condition[self.player] == "open":
data[rom_addresses['Option_Route3_Guard_B']] = world.options.route_3_condition.value
if world.options.route_3_condition == "open":
data[rom_addresses['Option_Route3_Guard_A']] = 0x11
if not self.multiworld.robbed_house_officer[self.player]:
if not world.options.robbed_house_officer:
data[rom_addresses['Option_Trashed_House_Guard_A']] = 0x15
data[rom_addresses['Option_Trashed_House_Guard_B']] = 0x11
if self.multiworld.require_pokedex[self.player]:
if world.options.require_pokedex:
data[rom_addresses["Require_Pokedex_A"]] = 1
data[rom_addresses["Require_Pokedex_B"]] = 1
data[rom_addresses["Require_Pokedex_C"]] = 1
else:
data[rom_addresses["Require_Pokedex_D"]] = 0x18 # jr
if self.multiworld.dexsanity[self.player]:
if world.options.dexsanity:
data[rom_addresses["Option_Dexsanity_A"]] = 1
data[rom_addresses["Option_Dexsanity_B"]] = 1
if self.multiworld.all_pokemon_seen[self.player]:
if world.options.all_pokemon_seen:
data[rom_addresses["Option_Pokedex_Seen"]] = 1
money = str(self.multiworld.starting_money[self.player].value).zfill(6)
money = str(world.options.starting_money.value).zfill(6)
data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16)
data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16)
data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16)
data[rom_addresses["Text_Badges_Needed_Viridian_Gym"]] = encode_text(
str(self.multiworld.viridian_gym_condition[self.player].value))[0]
str(world.options.viridian_gym_condition.value))[0]
data[rom_addresses["Text_Rt23_Badges_A"]] = encode_text(
str(self.multiworld.victory_road_condition[self.player].value))[0]
str(world.options.victory_road_condition.value))[0]
data[rom_addresses["Text_Rt23_Badges_B"]] = encode_text(
str(self.multiworld.victory_road_condition[self.player].value))[0]
str(world.options.victory_road_condition.value))[0]
data[rom_addresses["Text_Rt23_Badges_C"]] = encode_text(
str(self.multiworld.victory_road_condition[self.player].value))[0]
str(world.options.victory_road_condition.value))[0]
data[rom_addresses["Text_Rt23_Badges_D"]] = encode_text(
str(self.multiworld.victory_road_condition[self.player].value))[0]
str(world.options.victory_road_condition.value))[0]
data[rom_addresses["Text_Badges_Needed"]] = encode_text(
str(self.multiworld.elite_four_badges_condition[self.player].value))[0]
str(world.options.elite_four_badges_condition.value))[0]
write_bytes(data, encode_text(
" ".join(self.multiworld.get_location("Route 4 Pokemon Center - Pokemon For Sale", self.player).item.name.upper().split()[1:])),
" ".join(world.multiworld.get_location("Route 4 Pokemon Center - Pokemon For Sale", world.player).item.name.upper().split()[1:])),
rom_addresses["Text_Magikarp_Salesman"])
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0:
if world.options.badges_needed_for_hm_moves.value == 0:
for hm_move in poke_data.hm_moves:
write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
rom_addresses["HM_" + hm_move + "_Badge_a"])
elif self.extra_badges:
elif world.extra_badges:
written_badges = {}
for hm_move, badge in self.extra_badges.items():
for hm_move, badge in world.extra_badges.items():
data[rom_addresses["HM_" + hm_move + "_Badge_b"]] = {"Boulder Badge": 0x47, "Cascade Badge": 0x4F,
"Thunder Badge": 0x57, "Rainbow Badge": 0x5F,
"Soul Badge": 0x67, "Marsh Badge": 0x6F,
@ -427,7 +474,7 @@ def generate_output(self, output_directory: str):
write_bytes(data, encode_text("Nothing"), rom_addresses["Badge_Text_" + badge.replace(" ", "_")])
type_loc = rom_addresses["Type_Chart"]
for matchup in self.type_chart:
for matchup in world.type_chart:
if matchup[2] != 10: # don't needlessly divide damage by 10 and multiply by 10
data[type_loc] = poke_data.type_ids[matchup[0]]
data[type_loc + 1] = poke_data.type_ids[matchup[1]]
@ -437,52 +484,49 @@ def generate_output(self, output_directory: str):
data[type_loc + 1] = 0xFF
data[type_loc + 2] = 0xFF
if self.multiworld.normalize_encounter_chances[self.player].value:
if world.options.normalize_encounter_chances.value:
chances = [25, 51, 77, 103, 129, 155, 180, 205, 230, 255]
for i, chance in enumerate(chances):
data[rom_addresses['Encounter_Chances'] + (i * 2)] = chance
for mon, mon_data in self.local_poke_data.items():
for mon, mon_data in world.local_poke_data.items():
if mon == "Mew":
address = rom_addresses["Base_Stats_Mew"]
else:
address = rom_addresses["Base_Stats"] + (28 * (mon_data["dex"] - 1))
data[address + 1] = self.local_poke_data[mon]["hp"]
data[address + 2] = self.local_poke_data[mon]["atk"]
data[address + 3] = self.local_poke_data[mon]["def"]
data[address + 4] = self.local_poke_data[mon]["spd"]
data[address + 5] = self.local_poke_data[mon]["spc"]
data[address + 6] = poke_data.type_ids[self.local_poke_data[mon]["type1"]]
data[address + 7] = poke_data.type_ids[self.local_poke_data[mon]["type2"]]
data[address + 8] = self.local_poke_data[mon]["catch rate"]
data[address + 15] = poke_data.moves[self.local_poke_data[mon]["start move 1"]]["id"]
data[address + 16] = poke_data.moves[self.local_poke_data[mon]["start move 2"]]["id"]
data[address + 17] = poke_data.moves[self.local_poke_data[mon]["start move 3"]]["id"]
data[address + 18] = poke_data.moves[self.local_poke_data[mon]["start move 4"]]["id"]
write_bytes(data, self.local_poke_data[mon]["tms"], address + 20)
if mon in self.learnsets and self.learnsets[mon]:
data[address + 1] = world.local_poke_data[mon]["hp"]
data[address + 2] = world.local_poke_data[mon]["atk"]
data[address + 3] = world.local_poke_data[mon]["def"]
data[address + 4] = world.local_poke_data[mon]["spd"]
data[address + 5] = world.local_poke_data[mon]["spc"]
data[address + 6] = poke_data.type_ids[world.local_poke_data[mon]["type1"]]
data[address + 7] = poke_data.type_ids[world.local_poke_data[mon]["type2"]]
data[address + 8] = world.local_poke_data[mon]["catch rate"]
data[address + 15] = poke_data.moves[world.local_poke_data[mon]["start move 1"]]["id"]
data[address + 16] = poke_data.moves[world.local_poke_data[mon]["start move 2"]]["id"]
data[address + 17] = poke_data.moves[world.local_poke_data[mon]["start move 3"]]["id"]
data[address + 18] = poke_data.moves[world.local_poke_data[mon]["start move 4"]]["id"]
write_bytes(data, world.local_poke_data[mon]["tms"], address + 20)
if mon in world.learnsets and world.learnsets[mon]:
address = rom_addresses["Learnset_" + mon.replace(" ", "")]
for i, move in enumerate(self.learnsets[mon]):
for i, move in enumerate(world.learnsets[mon]):
data[(address + 1) + i * 2] = poke_data.moves[move]["id"]
data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player].value
data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player].value
data[rom_addresses["Option_Aide_Rt15"]] = self.multiworld.oaks_aide_rt_15[self.player].value
data[rom_addresses["Option_Aide_Rt2"]] = world.options.oaks_aide_rt_2.value
data[rom_addresses["Option_Aide_Rt11"]] = world.options.oaks_aide_rt_11.value
data[rom_addresses["Option_Aide_Rt15"]] = world.options.oaks_aide_rt_15.value
if self.multiworld.safari_zone_normal_battles[self.player].value == 1:
if world.options.safari_zone_normal_battles.value == 1:
data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255
if self.multiworld.reusable_tms[self.player].value:
if world.options.reusable_tms.value:
data[rom_addresses["Option_Reusable_TMs"]] = 0xC9
for i in range(1, 10):
data[rom_addresses[f"Option_Trainersanity{i}"]] = self.multiworld.trainersanity[self.player].value
data[rom_addresses["Option_Always_Half_STAB"]] = int(not world.options.same_type_attack_bonus.value)
data[rom_addresses["Option_Always_Half_STAB"]] = int(not self.multiworld.same_type_attack_bonus[self.player].value)
if self.multiworld.better_shops[self.player]:
if world.options.better_shops:
inventory = ["Poke Ball", "Great Ball", "Ultra Ball"]
if self.multiworld.better_shops[self.player].value == 2:
if world.options.better_shops.value == 2:
inventory.append("Master Ball")
inventory += ["Potion", "Super Potion", "Hyper Potion", "Max Potion", "Full Restore", "Revive", "Antidote",
"Awakening", "Burn Heal", "Ice Heal", "Paralyze Heal", "Full Heal", "Repel", "Super Repel",
@ -492,30 +536,30 @@ def generate_output(self, output_directory: str):
shop_data.append(0xFF)
for shop in range(1, 11):
write_bytes(data, shop_data, rom_addresses[f"Shop{shop}"])
if self.multiworld.stonesanity[self.player]:
if world.options.stonesanity:
write_bytes(data, bytearray([0xFE, 1, item_table["Poke Doll"].id - 172000000, 0xFF]), rom_addresses[f"Shop_Stones"])
price = str(self.multiworld.master_ball_price[self.player].value).zfill(6)
price = str(world.options.master_ball_price.value).zfill(6)
price = bytearray([int(price[:2], 16), int(price[2:4], 16), int(price[4:], 16)])
write_bytes(data, price, rom_addresses["Price_Master_Ball"]) # Money values in Red and Blue are weird
for item in reversed(self.multiworld.precollected_items[self.player]):
for item in reversed(world.multiworld.precollected_items[world.player]):
if data[rom_addresses["Start_Inventory"] + item.code - 172000000] < 255:
data[rom_addresses["Start_Inventory"] + item.code - 172000000] += 1
set_mon_palettes(self, random, data)
set_mon_palettes(world, random, data)
for move_data in self.local_move_data.values():
for move_data in world.local_move_data.values():
if move_data["id"] == 0:
continue
address = rom_addresses["Move_Data"] + ((move_data["id"] - 1) * 6)
write_bytes(data, bytearray([move_data["id"], move_data["effect"], move_data["power"],
poke_data.type_ids[move_data["type"]], round(move_data["accuracy"] * 2.55), move_data["pp"]]), address)
TM_IDs = bytearray([poke_data.moves[move]["id"] for move in self.local_tms])
TM_IDs = bytearray([poke_data.moves[move]["id"] for move in world.local_tms])
write_bytes(data, TM_IDs, rom_addresses["TM_Moves"])
if self.multiworld.randomize_rock_tunnel[self.player]:
if world.options.randomize_rock_tunnel:
seed = randomize_rock_tunnel(data, random)
write_bytes(data, encode_text(f"SEED: <LINE>{seed}"), rom_addresses["Text_Rock_Tunnel_Sign"])
@ -524,44 +568,44 @@ def generate_output(self, output_directory: str):
data[rom_addresses['Title_Mon_First']] = mons.pop()
for mon in range(0, 16):
data[rom_addresses['Title_Mons'] + mon] = mons.pop()
if self.multiworld.game_version[self.player].value:
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name
else 1 if mon == self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name else
2 if mon == self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name else 3)
if world.options.game_version.value:
mons.sort(key=lambda mon: 0 if mon == world.multiworld.get_location("Oak's Lab - Starter 1", world.player).item.name
else 1 if mon == world.multiworld.get_location("Oak's Lab - Starter 2", world.player).item.name else
2 if mon == world.multiworld.get_location("Oak's Lab - Starter 3", world.player).item.name else 3)
else:
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name
else 1 if mon == self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name else
2 if mon == self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name else 3)
write_bytes(data, encode_text(self.multiworld.seed_name[-20:], 20, True), rom_addresses['Title_Seed'])
mons.sort(key=lambda mon: 0 if mon == world.multiworld.get_location("Oak's Lab - Starter 2", world.player).item.name
else 1 if mon == world.multiworld.get_location("Oak's Lab - Starter 1", world.player).item.name else
2 if mon == world.multiworld.get_location("Oak's Lab - Starter 3", world.player).item.name else 3)
write_bytes(data, encode_text(world.multiworld.seed_name[-20:], 20, True), rom_addresses['Title_Seed'])
slot_name = self.multiworld.player_name[self.player]
slot_name = world.multiworld.player_name[world.player]
slot_name.replace("@", " ")
slot_name.replace("<", " ")
slot_name.replace(">", " ")
write_bytes(data, encode_text(slot_name, 16, True, True), rom_addresses['Title_Slot_Name'])
if self.trainer_name == "choose_in_game":
if world.trainer_name == "choose_in_game":
data[rom_addresses["Skip_Player_Name"]] = 0
else:
write_bytes(data, self.trainer_name, rom_addresses['Player_Name'])
if self.rival_name == "choose_in_game":
write_bytes(data, world.trainer_name, rom_addresses['Player_Name'])
if world.rival_name == "choose_in_game":
data[rom_addresses["Skip_Rival_Name"]] = 0
else:
write_bytes(data, self.rival_name, rom_addresses['Rival_Name'])
write_bytes(data, world.rival_name, rom_addresses['Rival_Name'])
data[0xFF00] = 2 # client compatibility version
rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0',
rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{world.player}_{world.multiworld.seed:11}\0',
'utf8')[:21]
rom_name.extend([0] * (21 - len(rom_name)))
write_bytes(data, rom_name, 0xFFC6)
write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDB)
write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0)
write_bytes(data, world.multiworld.seed_name.encode(), 0xFFDB)
write_bytes(data, world.multiworld.player_name[world.player].encode(), 0xFFF0)
self.finished_level_scaling.wait()
world.finished_level_scaling.wait()
write_quizzes(self, data, random)
write_quizzes(world, data, random)
for location in self.multiworld.get_locations(self.player):
for location in world.multiworld.get_locations(world.player):
if location.party_data:
for party in location.party_data:
if not isinstance(party["party_address"], list):
@ -588,7 +632,7 @@ def generate_output(self, output_directory: str):
continue
elif location.rom_address is None:
continue
if location.item and location.item.player == self.player:
if location.item and location.item.player == world.player:
if location.rom_address:
rom_address = location.rom_address
if not isinstance(rom_address, list):
@ -599,7 +643,7 @@ def generate_output(self, output_directory: str):
elif " ".join(location.item.name.split()[1:]) in poke_data.pokemon_data.keys():
data[address] = poke_data.pokemon_data[" ".join(location.item.name.split()[1:])]["id"]
else:
item_id = self.item_name_to_id[location.item.name] - 172000000
item_id = world.item_name_to_id[location.item.name] - 172000000
if item_id > 255:
item_id -= 256
data[address] = item_id
@ -613,18 +657,18 @@ def generate_output(self, output_directory: str):
for address in rom_address:
data[address] = 0x2C # AP Item
outfilepname = f'_P{self.player}'
outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \
if self.multiworld.player_name[self.player] != 'Player%d' % self.player else ''
rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb')
outfilepname = f'_P{world.player}'
outfilepname += f"_{world.multiworld.get_file_safe_player_name(world.player).replace(' ', '_')}" \
if world.multiworld.player_name[world.player] != 'Player%d' % world.player else ''
rompath = os.path.join(output_directory, f'AP_{world.multiworld.seed_name}{outfilepname}.gb')
with open(rompath, 'wb') as outfile:
outfile.write(data)
if self.multiworld.game_version[self.player].current_key == "red":
patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=self.player,
player_name=self.multiworld.player_name[self.player], patched_path=rompath)
if world.options.game_version.current_key == "red":
patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=world.player,
player_name=world.multiworld.player_name[world.player], patched_path=rompath)
else:
patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=self.player,
player_name=self.multiworld.player_name[self.player], patched_path=rompath)
patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=world.player,
player_name=world.multiworld.player_name[world.player], patched_path=rompath)
patch.write()
os.unlink(rompath)

View File

@ -1,10 +1,9 @@
rom_addresses = {
"Option_Encounter_Minimum_Steps": 0x3c1,
"Option_Pitch_Black_Rock_Tunnel": 0x76a,
"Option_Blind_Trainers": 0x30d5,
"Option_Trainersanity1": 0x3165,
"Option_Split_Card_Key": 0x3e1e,
"Option_Fix_Combat_Bugs": 0x3e1f,
"Option_Blind_Trainers": 0x32f0,
"Option_Split_Card_Key": 0x3e19,
"Option_Fix_Combat_Bugs": 0x3e1a,
"Option_Lose_Money": 0x40d4,
"Base_Stats_Mew": 0x4260,
"Title_Mon_First": 0x4373,
@ -115,9 +114,10 @@ rom_addresses = {
"HM_Strength_Badge_b": 0x131ed,
"HM_Flash_Badge_a": 0x131fc,
"HM_Flash_Badge_b": 0x13201,
"Trainer_Screen_Total_Key_Items": 0x135dc,
"TM_Moves": 0x137b1,
"Encounter_Chances": 0x13950,
"Tea_Key_Item_A": 0x135ac,
"Trainer_Screen_Total_Key_Items": 0x1361b,
"TM_Moves": 0x137f0,
"Encounter_Chances": 0x1398f,
"Warps_CeladonCity": 0x18026,
"Warps_PalletTown": 0x182c7,
"Warps_ViridianCity": 0x18388,
@ -128,52 +128,54 @@ rom_addresses = {
"Option_Viridian_Gym_Badges": 0x1901d,
"Event_Sleepy_Guy": 0x191d1,
"Option_Route3_Guard_B": 0x1928a,
"Starter2_K": 0x19611,
"Starter3_K": 0x19619,
"Event_Rocket_Thief": 0x19733,
"Option_Cerulean_Cave_Badges": 0x19861,
"Option_Cerulean_Cave_Key_Items": 0x19868,
"Text_Cerulean_Cave_Badges": 0x198d7,
"Text_Cerulean_Cave_Key_Items": 0x198e5,
"Event_Stranded_Man": 0x19b3c,
"Event_Rivals_Sister": 0x19d0f,
"Warps_BluesHouse": 0x19d65,
"Warps_VermilionTradeHouse": 0x19dbc,
"Require_Pokedex_D": 0x19e53,
"Option_Elite_Four_Key_Items": 0x19e9d,
"Option_Elite_Four_Pokedex": 0x19ea4,
"Option_Elite_Four_Badges": 0x19eab,
"Text_Elite_Four_Badges": 0x19f47,
"Text_Elite_Four_Key_Items": 0x19f51,
"Text_Elite_Four_Pokedex": 0x19f64,
"Shop10": 0x1a018,
"Warps_IndigoPlateauLobby": 0x1a044,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM": 0x1a16c,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM": 0x1a17a,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM": 0x1a188,
"Event_SKC4F": 0x1a19b,
"Warps_SilphCo4F": 0x1a21d,
"Missable_Silph_Co_4F_Item_1": 0x1a25d,
"Missable_Silph_Co_4F_Item_2": 0x1a264,
"Missable_Silph_Co_4F_Item_3": 0x1a26b,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM": 0x1a3c3,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM": 0x1a3d1,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM": 0x1a3df,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM": 0x1a3ed,
"Event_SKC5F": 0x1a400,
"Warps_SilphCo5F": 0x1a4aa,
"Missable_Silph_Co_5F_Item_1": 0x1a4f2,
"Missable_Silph_Co_5F_Item_2": 0x1a4f9,
"Missable_Silph_Co_5F_Item_3": 0x1a500,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM": 0x1a630,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM": 0x1a63e,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM": 0x1a64c,
"Event_SKC6F": 0x1a66d,
"Warps_SilphCo6F": 0x1a74b,
"Missable_Silph_Co_6F_Item_1": 0x1a79b,
"Missable_Silph_Co_6F_Item_2": 0x1a7a2,
"Path_Pallet_Oak": 0x1a928,
"Path_Pallet_Player": 0x1a935,
"Starter2_K": 0x19618,
"Starter3_K": 0x19620,
"Event_Rocket_Thief": 0x1973a,
"Tea_Key_Item_C": 0x1988f,
"Option_Cerulean_Cave_Badges": 0x198a0,
"Option_Cerulean_Cave_Key_Items": 0x198a7,
"Text_Cerulean_Cave_Badges": 0x19916,
"Text_Cerulean_Cave_Key_Items": 0x19924,
"Event_Stranded_Man": 0x19b7b,
"Event_Rivals_Sister": 0x19d4e,
"Warps_BluesHouse": 0x19da4,
"Warps_VermilionTradeHouse": 0x19dfb,
"Require_Pokedex_D": 0x19e99,
"Tea_Key_Item_B": 0x19f13,
"Option_Elite_Four_Key_Items": 0x19f1b,
"Option_Elite_Four_Pokedex": 0x19f22,
"Option_Elite_Four_Badges": 0x19f29,
"Text_Elite_Four_Badges": 0x19fc5,
"Text_Elite_Four_Key_Items": 0x19fcf,
"Text_Elite_Four_Pokedex": 0x19fe2,
"Shop10": 0x1a096,
"Warps_IndigoPlateauLobby": 0x1a0c2,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM": 0x1a1ea,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM": 0x1a1f8,
"Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM": 0x1a206,
"Event_SKC4F": 0x1a219,
"Warps_SilphCo4F": 0x1a29b,
"Missable_Silph_Co_4F_Item_1": 0x1a2db,
"Missable_Silph_Co_4F_Item_2": 0x1a2e2,
"Missable_Silph_Co_4F_Item_3": 0x1a2e9,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM": 0x1a441,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM": 0x1a44f,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM": 0x1a45d,
"Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM": 0x1a46b,
"Event_SKC5F": 0x1a47e,
"Warps_SilphCo5F": 0x1a528,
"Missable_Silph_Co_5F_Item_1": 0x1a570,
"Missable_Silph_Co_5F_Item_2": 0x1a577,
"Missable_Silph_Co_5F_Item_3": 0x1a57e,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM": 0x1a6ae,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM": 0x1a6bc,
"Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM": 0x1a6ca,
"Event_SKC6F": 0x1a6eb,
"Warps_SilphCo6F": 0x1a7c9,
"Missable_Silph_Co_6F_Item_1": 0x1a819,
"Missable_Silph_Co_6F_Item_2": 0x1a820,
"Path_Pallet_Oak": 0x1a9a6,
"Path_Pallet_Player": 0x1a9b3,
"Warps_CinnabarIsland": 0x1c026,
"Warps_Route1": 0x1c0e9,
"Option_Extra_Key_Items_B": 0x1ca46,
@ -191,75 +193,75 @@ rom_addresses = {
"Starter2_E": 0x1d2f7,
"Starter3_E": 0x1d2ff,
"Event_Pokedex": 0x1d363,
"Event_Oaks_Gift": 0x1d393,
"Starter2_P": 0x1d481,
"Starter3_P": 0x1d489,
"Warps_OaksLab": 0x1d6af,
"Event_Pokemart_Quest": 0x1d76b,
"Shop1": 0x1d795,
"Warps_ViridianMart": 0x1d7d8,
"Warps_ViridianSchoolHouse": 0x1d82b,
"Warps_ViridianNicknameHouse": 0x1d889,
"Warps_PewterNidoranHouse": 0x1d8e4,
"Warps_PewterSpeechHouse": 0x1d927,
"Warps_CeruleanTrashedHouse": 0x1d98d,
"Warps_CeruleanTradeHouse": 0x1d9de,
"Event_Bicycle_Shop": 0x1da2f,
"Bike_Shop_Item_Display": 0x1da8a,
"Warps_BikeShop": 0x1db45,
"Event_Fuji": 0x1dbfd,
"Warps_MrFujisHouse": 0x1dc44,
"Warps_LavenderCuboneHouse": 0x1dcc0,
"Warps_NameRatersHouse": 0x1ddae,
"Warps_VermilionPidgeyHouse": 0x1ddf8,
"Trainersanity_EVENT_BEAT_MEW_ITEM": 0x1de4e,
"Warps_VermilionDock": 0x1de70,
"Static_Encounter_Mew": 0x1de7e,
"Gift_Eevee": 0x1def7,
"Warps_CeladonMansionRoofHouse": 0x1df0e,
"Shop7": 0x1df49,
"Warps_FuchsiaMart": 0x1df74,
"Warps_SaffronPidgeyHouse": 0x1dfdd,
"Event_Mr_Psychic": 0x1e020,
"Warps_MrPsychicsHouse": 0x1e05d,
"Warps_DiglettsCaveRoute2": 0x1e092,
"Warps_Route2TradeHouse": 0x1e0da,
"Warps_Route5Gate": 0x1e1db,
"Warps_Route6Gate": 0x1e2ad,
"Warps_Route7Gate": 0x1e383,
"Warps_Route8Gate": 0x1e454,
"Warps_UndergroundPathRoute8": 0x1e4a5,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_0_ITEM": 0x1e511,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_1_ITEM": 0x1e51f,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_2_ITEM": 0x1e52d,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_3_ITEM": 0x1e53b,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_4_ITEM": 0x1e549,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_5_ITEM": 0x1e557,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_6_ITEM": 0x1e565,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_7_ITEM": 0x1e573,
"Trainersanity_EVENT_BEAT_ZAPDOS_ITEM": 0x1e581,
"Warps_PowerPlant": 0x1e5de,
"Static_Encounter_Voltorb_A": 0x1e5f0,
"Static_Encounter_Voltorb_B": 0x1e5f8,
"Static_Encounter_Voltorb_C": 0x1e600,
"Static_Encounter_Electrode_A": 0x1e608,
"Static_Encounter_Voltorb_D": 0x1e610,
"Static_Encounter_Voltorb_E": 0x1e618,
"Static_Encounter_Electrode_B": 0x1e620,
"Static_Encounter_Voltorb_F": 0x1e628,
"Static_Encounter_Zapdos": 0x1e630,
"Missable_Power_Plant_Item_1": 0x1e638,
"Missable_Power_Plant_Item_2": 0x1e63f,
"Missable_Power_Plant_Item_3": 0x1e646,
"Missable_Power_Plant_Item_4": 0x1e64d,
"Missable_Power_Plant_Item_5": 0x1e654,
"Warps_DiglettsCaveRoute11": 0x1e7e9,
"Event_Rt16_House_Woman": 0x1e827,
"Warps_Route16FlyHouse": 0x1e870,
"Option_Victory_Road_Badges": 0x1e8f3,
"Warps_Route22Gate": 0x1e9e3,
"Event_Bill": 0x1eb24,
"Warps_BillsHouse": 0x1eb83,
"Event_Oaks_Gift": 0x1d398,
"Starter2_P": 0x1d486,
"Starter3_P": 0x1d48e,
"Warps_OaksLab": 0x1d6b4,
"Event_Pokemart_Quest": 0x1d770,
"Shop1": 0x1d79a,
"Warps_ViridianMart": 0x1d7dd,
"Warps_ViridianSchoolHouse": 0x1d830,
"Warps_ViridianNicknameHouse": 0x1d88e,
"Warps_PewterNidoranHouse": 0x1d8e9,
"Warps_PewterSpeechHouse": 0x1d92c,
"Warps_CeruleanTrashedHouse": 0x1d992,
"Warps_CeruleanTradeHouse": 0x1d9e3,
"Event_Bicycle_Shop": 0x1da34,
"Bike_Shop_Item_Display": 0x1da8f,
"Warps_BikeShop": 0x1db4a,
"Event_Fuji": 0x1dc02,
"Warps_MrFujisHouse": 0x1dc49,
"Warps_LavenderCuboneHouse": 0x1dcc5,
"Warps_NameRatersHouse": 0x1ddb3,
"Warps_VermilionPidgeyHouse": 0x1ddfd,
"Trainersanity_EVENT_BEAT_MEW_ITEM": 0x1de53,
"Warps_VermilionDock": 0x1de75,
"Static_Encounter_Mew": 0x1de83,
"Gift_Eevee": 0x1defc,
"Warps_CeladonMansionRoofHouse": 0x1df13,
"Shop7": 0x1df4e,
"Warps_FuchsiaMart": 0x1df79,
"Warps_SaffronPidgeyHouse": 0x1dfe2,
"Event_Mr_Psychic": 0x1e025,
"Warps_MrPsychicsHouse": 0x1e062,
"Warps_DiglettsCaveRoute2": 0x1e097,
"Warps_Route2TradeHouse": 0x1e0df,
"Warps_Route5Gate": 0x1e1e0,
"Warps_Route6Gate": 0x1e2b2,
"Warps_Route7Gate": 0x1e388,
"Warps_Route8Gate": 0x1e459,
"Warps_UndergroundPathRoute8": 0x1e4aa,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_0_ITEM": 0x1e516,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_1_ITEM": 0x1e524,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_2_ITEM": 0x1e532,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_3_ITEM": 0x1e540,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_4_ITEM": 0x1e54e,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_5_ITEM": 0x1e55c,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_6_ITEM": 0x1e56a,
"Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_7_ITEM": 0x1e578,
"Trainersanity_EVENT_BEAT_ZAPDOS_ITEM": 0x1e586,
"Warps_PowerPlant": 0x1e5e3,
"Static_Encounter_Voltorb_A": 0x1e5f5,
"Static_Encounter_Voltorb_B": 0x1e5fd,
"Static_Encounter_Voltorb_C": 0x1e605,
"Static_Encounter_Electrode_A": 0x1e60d,
"Static_Encounter_Voltorb_D": 0x1e615,
"Static_Encounter_Voltorb_E": 0x1e61d,
"Static_Encounter_Electrode_B": 0x1e625,
"Static_Encounter_Voltorb_F": 0x1e62d,
"Static_Encounter_Zapdos": 0x1e635,
"Missable_Power_Plant_Item_1": 0x1e63d,
"Missable_Power_Plant_Item_2": 0x1e644,
"Missable_Power_Plant_Item_3": 0x1e64b,
"Missable_Power_Plant_Item_4": 0x1e652,
"Missable_Power_Plant_Item_5": 0x1e659,
"Warps_DiglettsCaveRoute11": 0x1e7ee,
"Event_Rt16_House_Woman": 0x1e82c,
"Warps_Route16FlyHouse": 0x1e875,
"Option_Victory_Road_Badges": 0x1e8f8,
"Warps_Route22Gate": 0x1e9e8,
"Event_Bill": 0x1eb29,
"Warps_BillsHouse": 0x1eb88,
"Starter1_O": 0x372b0,
"Starter2_O": 0x372b4,
"Starter3_O": 0x372b8,
@ -1470,74 +1472,73 @@ rom_addresses = {
"Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_3_ITEM": 0x609ea,
"Warps_PokemonTower5F": 0x60a5e,
"Missable_Pokemon_Tower_5F_Item": 0x60a92,
"Option_Trainersanity2": 0x60b2a,
"Ghost_Battle1": 0x60b83,
"Ghost_Battle_Level": 0x60b88,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_0_ITEM": 0x60c25,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_1_ITEM": 0x60c33,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_2_ITEM": 0x60c41,
"Ghost_Battle2": 0x60c69,
"Warps_PokemonTower6F": 0x60cbe,
"Missable_Pokemon_Tower_6F_Item_1": 0x60ce4,
"Missable_Pokemon_Tower_6F_Item_2": 0x60ceb,
"Entrance_Shuffle_Fuji_Warp": 0x60deb,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_0_ITEM": 0x60edf,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_1_ITEM": 0x60eed,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_2_ITEM": 0x60efb,
"Warps_PokemonTower7F": 0x60f8b,
"Warps_CeladonMart1F": 0x61033,
"Gift_Aerodactyl": 0x610f5,
"Gift_Omanyte": 0x610f9,
"Gift_Kabuto": 0x610fd,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_0_ITEM": 0x611de,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_1_ITEM": 0x611ec,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_2_ITEM": 0x611fa,
"Warps_ViridianForest": 0x61273,
"Missable_Viridian_Forest_Item_1": 0x612c1,
"Missable_Viridian_Forest_Item_2": 0x612c8,
"Missable_Viridian_Forest_Item_3": 0x612cf,
"Warps_SSAnne1F": 0x61310,
"Starter2_M": 0x614e5,
"Starter3_M": 0x614ed,
"Warps_SSAnne2F": 0x615ab,
"Warps_SSAnneB1F": 0x616c9,
"Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_0_ITEM": 0x61771,
"Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_1_ITEM": 0x6177f,
"Warps_SSAnneBow": 0x617c6,
"Warps_SSAnneKitchen": 0x618b6,
"Event_SS_Anne_Captain": 0x6194e,
"Warps_SSAnneCaptainsRoom": 0x619d5,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_0_ITEM": 0x61a3d,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_1_ITEM": 0x61a4b,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_2_ITEM": 0x61a59,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_3_ITEM": 0x61a67,
"Warps_SSAnne1FRooms": 0x61af7,
"Missable_SS_Anne_1F_Item": 0x61b53,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_0_ITEM": 0x61c24,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_1_ITEM": 0x61c32,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_2_ITEM": 0x61c40,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_3_ITEM": 0x61c4e,
"Warps_SSAnne2FRooms": 0x61d2c,
"Missable_SS_Anne_2F_Item_1": 0x61d88,
"Missable_SS_Anne_2F_Item_2": 0x61d9b,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_0_ITEM": 0x61e2c,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_1_ITEM": 0x61e3a,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_2_ITEM": 0x61e48,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_3_ITEM": 0x61e56,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_4_ITEM": 0x61e64,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_5_ITEM": 0x61e72,
"Warps_SSAnneB1FRooms": 0x61f20,
"Missable_SS_Anne_B1F_Item_1": 0x61f8a,
"Missable_SS_Anne_B1F_Item_2": 0x61f91,
"Missable_SS_Anne_B1F_Item_3": 0x61f98,
"Warps_UndergroundPathNorthSouth": 0x61fd5,
"Warps_UndergroundPathWestEast": 0x61ff9,
"Warps_DiglettsCave": 0x6201d,
"Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_0_ITEM": 0x62358,
"Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_1_ITEM": 0x62366,
"Event_Silph_Co_President": 0x62373,
"Event_SKC11F": 0x623bd,
"Warps_SilphCo11F": 0x62446,
"Ghost_Battle1": 0x60b93,
"Ghost_Battle_Level": 0x60b98,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_0_ITEM": 0x60c35,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_1_ITEM": 0x60c43,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_2_ITEM": 0x60c51,
"Ghost_Battle2": 0x60c79,
"Warps_PokemonTower6F": 0x60cce,
"Missable_Pokemon_Tower_6F_Item_1": 0x60cf4,
"Missable_Pokemon_Tower_6F_Item_2": 0x60cfb,
"Entrance_Shuffle_Fuji_Warp": 0x60dfb,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_0_ITEM": 0x60eef,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_1_ITEM": 0x60efd,
"Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_2_ITEM": 0x60f0b,
"Warps_PokemonTower7F": 0x60f9b,
"Warps_CeladonMart1F": 0x61043,
"Gift_Aerodactyl": 0x61105,
"Gift_Omanyte": 0x61109,
"Gift_Kabuto": 0x6110d,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_0_ITEM": 0x61209,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_1_ITEM": 0x61217,
"Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_2_ITEM": 0x61225,
"Warps_ViridianForest": 0x6129e,
"Missable_Viridian_Forest_Item_1": 0x612ec,
"Missable_Viridian_Forest_Item_2": 0x612f3,
"Missable_Viridian_Forest_Item_3": 0x612fa,
"Warps_SSAnne1F": 0x6133b,
"Starter2_M": 0x61510,
"Starter3_M": 0x61518,
"Warps_SSAnne2F": 0x615d6,
"Warps_SSAnneB1F": 0x616f4,
"Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_0_ITEM": 0x6179c,
"Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_1_ITEM": 0x617aa,
"Warps_SSAnneBow": 0x617f1,
"Warps_SSAnneKitchen": 0x618e1,
"Event_SS_Anne_Captain": 0x61979,
"Warps_SSAnneCaptainsRoom": 0x61a00,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_0_ITEM": 0x61a68,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_1_ITEM": 0x61a76,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_2_ITEM": 0x61a84,
"Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_3_ITEM": 0x61a92,
"Warps_SSAnne1FRooms": 0x61b22,
"Missable_SS_Anne_1F_Item": 0x61b7e,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_0_ITEM": 0x61c4f,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_1_ITEM": 0x61c5d,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_2_ITEM": 0x61c6b,
"Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_3_ITEM": 0x61c79,
"Warps_SSAnne2FRooms": 0x61d57,
"Missable_SS_Anne_2F_Item_1": 0x61db3,
"Missable_SS_Anne_2F_Item_2": 0x61dc6,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_0_ITEM": 0x61e57,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_1_ITEM": 0x61e65,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_2_ITEM": 0x61e73,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_3_ITEM": 0x61e81,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_4_ITEM": 0x61e8f,
"Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_5_ITEM": 0x61e9d,
"Warps_SSAnneB1FRooms": 0x61f4b,
"Missable_SS_Anne_B1F_Item_1": 0x61fb5,
"Missable_SS_Anne_B1F_Item_2": 0x61fbc,
"Missable_SS_Anne_B1F_Item_3": 0x61fc3,
"Warps_UndergroundPathNorthSouth": 0x62000,
"Warps_UndergroundPathWestEast": 0x62024,
"Warps_DiglettsCave": 0x62048,
"Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_0_ITEM": 0x62383,
"Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_1_ITEM": 0x62391,
"Event_Silph_Co_President": 0x6239e,
"Event_SKC11F": 0x623e8,
"Warps_SilphCo11F": 0x62471,
"Ghost_Battle4": 0x708e1,
"Town_Map_Order": 0x70f0f,
"Town_Map_Coords": 0x71381,
@ -1589,44 +1590,37 @@ rom_addresses = {
"Warps_FuchsiaMeetingRoom": 0x75879,
"Badge_Cinnabar_Gym": 0x759de,
"Event_Cinnabar_Gym": 0x759f2,
"Option_Trainersanity4": 0x75ace,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_B_ITEM": 0x75ada,
"Option_Trainersanity3": 0x75b1e,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_A_ITEM": 0x75b2a,
"Option_Trainersanity5": 0x75b85,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_2_ITEM": 0x75b91,
"Option_Trainersanity6": 0x75bd5,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_3_ITEM": 0x75be1,
"Option_Trainersanity7": 0x75c25,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_4_ITEM": 0x75c31,
"Option_Trainersanity8": 0x75c75,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_5_ITEM": 0x75c81,
"Option_Trainersanity9": 0x75cc5,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_6_ITEM": 0x75cd1,
"Warps_CinnabarGym": 0x75d1b,
"Warps_CinnabarLab": 0x75e02,
"Warps_CinnabarLabTradeRoom": 0x75e94,
"Event_Lab_Scientist": 0x75ee9,
"Warps_CinnabarLabMetronomeRoom": 0x75f35,
"Fossils_Needed_For_Second_Item": 0x75fb6,
"Fossil_Level": 0x76017,
"Event_Dome_Fossil_B": 0x76031,
"Event_Helix_Fossil_B": 0x76051,
"Warps_CinnabarLabFossilRoom": 0x760d2,
"Warps_CinnabarPokecenter": 0x76128,
"Shop8": 0x7616f,
"Warps_CinnabarMart": 0x7619b,
"Warps_CopycatsHouse1F": 0x761ed,
"Starter2_N": 0x762a2,
"Starter3_N": 0x762aa,
"Warps_ChampionsRoom": 0x764d5,
"Trainersanity_EVENT_BEAT_LORELEIS_ROOM_TRAINER_0_ITEM": 0x76604,
"Warps_LoreleisRoom": 0x76628,
"Trainersanity_EVENT_BEAT_BRUNOS_ROOM_TRAINER_0_ITEM": 0x7675d,
"Warps_BrunosRoom": 0x76781,
"Trainersanity_EVENT_BEAT_AGATHAS_ROOM_TRAINER_0_ITEM": 0x768bc,
"Warps_AgathasRoom": 0x768e0,
"Option_Itemfinder": 0x76a33,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_B_ITEM": 0x75adc,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_A_ITEM": 0x75b2e,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_2_ITEM": 0x75b97,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_3_ITEM": 0x75be9,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_4_ITEM": 0x75c3b,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_5_ITEM": 0x75c8d,
"Trainersanity_EVENT_BEAT_CINNABAR_GYM_TRAINER_6_ITEM": 0x75cdf,
"Warps_CinnabarGym": 0x75d29,
"Warps_CinnabarLab": 0x75e10,
"Warps_CinnabarLabTradeRoom": 0x75ea2,
"Event_Lab_Scientist": 0x75ef7,
"Warps_CinnabarLabMetronomeRoom": 0x75f43,
"Fossils_Needed_For_Second_Item": 0x75fc4,
"Fossil_Level": 0x76025,
"Event_Dome_Fossil_B": 0x7603f,
"Event_Helix_Fossil_B": 0x7605f,
"Warps_CinnabarLabFossilRoom": 0x760e0,
"Warps_CinnabarPokecenter": 0x76136,
"Shop8": 0x7617d,
"Warps_CinnabarMart": 0x761a9,
"Warps_CopycatsHouse1F": 0x761fb,
"Starter2_N": 0x762b0,
"Starter3_N": 0x762b8,
"Warps_ChampionsRoom": 0x764e3,
"Trainersanity_EVENT_BEAT_LORELEIS_ROOM_TRAINER_0_ITEM": 0x76612,
"Warps_LoreleisRoom": 0x76636,
"Trainersanity_EVENT_BEAT_BRUNOS_ROOM_TRAINER_0_ITEM": 0x7676b,
"Warps_BrunosRoom": 0x7678f,
"Trainersanity_EVENT_BEAT_AGATHAS_ROOM_TRAINER_0_ITEM": 0x768ca,
"Warps_AgathasRoom": 0x768ee,
"Option_Itemfinder": 0x76a41,
"Text_Quiz_A": 0x88806,
"Text_Quiz_B": 0x8893a,
"Text_Quiz_C": 0x88a6e,

View File

@ -3,7 +3,7 @@ from .items import item_groups
from . import logic
def set_rules(multiworld, player):
def set_rules(multiworld, world, player):
item_rules = {
# Some items do special things when they are passed into the GiveItem function in the game, but
@ -15,54 +15,46 @@ def set_rules(multiworld, player):
not in i.name)
}
if multiworld.prizesanity[player]:
if world.options.prizesanity:
def prize_rule(i):
return i.player != player or i.name in item_groups["Unique"]
item_rules["Celadon Prize Corner - Item Prize 1"] = prize_rule
item_rules["Celadon Prize Corner - Item Prize 2"] = prize_rule
item_rules["Celadon Prize Corner - Item Prize 3"] = prize_rule
if multiworld.accessibility[player] != "full":
multiworld.get_location("Cerulean Bicycle Shop", player).always_allow = (lambda state, item:
item.name == "Bike Voucher"
and item.player == player)
multiworld.get_location("Fuchsia Warden's House - Safari Zone Warden", player).always_allow = (lambda state, item:
item.name == "Gold Teeth" and
item.player == player)
access_rules = {
"Rival's House - Rival's Sister": lambda state: state.has("Oak's Parcel", player),
"Oak's Lab - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player),
"Viridian City - Sleepy Guy": lambda state: logic.can_cut(state, player) or logic.can_surf(state, player),
"Route 2 Gate - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_2[player].value + 5, player),
"Viridian City - Sleepy Guy": lambda state: logic.can_cut(state, world, player) or logic.can_surf(state, world, player),
"Route 2 Gate - Oak's Aide": lambda state: logic.oaks_aide(state, world, world.options.oaks_aide_rt_2.value + 5, player),
"Cerulean Bicycle Shop": lambda state: state.has("Bike Voucher", player)
or location_item_name(state, "Cerulean Bicycle Shop", player) == ("Bike Voucher", player),
"Lavender Mr. Fuji's House - Mr. Fuji": lambda state: state.has("Fuji Saved", player),
"Route 11 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_11[player].value + 5, player),
"Celadon City - Stranded Man": lambda state: logic.can_surf(state, player),
"Route 11 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, world, world.options.oaks_aide_rt_11.value + 5, player),
"Celadon City - Stranded Man": lambda state: logic.can_surf(state, world, player),
"Fuchsia Warden's House - Safari Zone Warden": lambda state: state.has("Gold Teeth", player)
or location_item_name(state, "Fuchsia Warden's House - Safari Zone Warden", player) == ("Gold Teeth", player),
"Route 12 - Island Item": lambda state: logic.can_surf(state, player),
"Route 15 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_15[player].value + 5, player),
"Route 25 - Item": lambda state: logic.can_cut(state, player),
"Fuchsia Warden's House - Behind Boulder Item": lambda state: logic.can_strength(state, player),
"Safari Zone Center - Island Item": lambda state: logic.can_surf(state, player),
"Route 12 - Island Item": lambda state: logic.can_surf(state, world, player),
"Route 15 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, world, world.options.oaks_aide_rt_15.value + 5, player),
"Route 25 - Item": lambda state: logic.can_cut(state, world, player),
"Fuchsia Warden's House - Behind Boulder Item": lambda state: logic.can_strength(state, world, player),
"Safari Zone Center - Island Item": lambda state: logic.can_surf(state, world, player),
"Saffron Copycat's House 2F - Copycat": lambda state: state.has("Buy Poke Doll", player),
"Celadon Game Corner - West Gambler's Gift": lambda state: state.has("Coin Case", player),
"Celadon Game Corner - Center Gambler's Gift": lambda state: state.has("Coin Case", player),
"Celadon Game Corner - East Gambler's Gift": lambda state: state.has("Coin Case", player),
"Celadon Game Corner - Hidden Item Northwest By Counter": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Southwest Corner": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near Rumor Man": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near Speculating Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near West Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near Wonderful Time Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near Failing Gym Information Guy": lambda state: state.has( "Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near East Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Near Hooked Guy": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item at End of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item in Front of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
"Celadon Game Corner - Hidden Item Northwest By Counter": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Southwest Corner": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near Rumor Man": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near Speculating Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near West Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near Wonderful Time Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near Failing Gym Information Guy": lambda state: state.has( "Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near East Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item Near Hooked Guy": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item at End of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Game Corner - Hidden Item in Front of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, world, player),
"Celadon Prize Corner - Item Prize 1": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
"Celadon Prize Corner - Item Prize 2": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
@ -79,9 +71,9 @@ def set_rules(multiworld, player):
"Cinnabar Lab Fossil Room - Dome Fossil Pokemon": lambda state: state.has("Dome Fossil", player) and state.has("Cinnabar Island", player),
"Route 12 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player),
"Route 16 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player),
"Seafoam Islands B4F - Legendary Pokemon": lambda state: logic.can_strength(state, player) and state.has("Seafoam Boss Boulders", player),
"Vermilion Dock - Legendary Pokemon": lambda state: logic.can_surf(state, player),
"Cerulean Cave B1F - Legendary Pokemon": lambda state: logic.can_surf(state, player),
"Seafoam Islands B4F - Legendary Pokemon": lambda state: logic.can_strength(state, world, player) and state.has("Seafoam Boss Boulders", player),
"Vermilion Dock - Legendary Pokemon": lambda state: logic.can_surf(state, world, player),
"Cerulean Cave B1F - Legendary Pokemon": lambda state: logic.can_surf(state, world, player),
**{f"Pokemon Tower {floor}F - Wild Pokemon - {slot}": lambda state: state.has("Silph Scope", player) for floor in range(3, 8) for slot in range(1, 11)},
"Pokemon Tower 6F - Restless Soul": lambda state: state.has("Silph Scope", player), # just for level scaling
@ -103,101 +95,101 @@ def set_rules(multiworld, player):
"Route 22 - Trainer Parties": lambda state: state.has("Oak's Parcel", player),
# # Rock Tunnel
"Rock Tunnel 1F - PokeManiac": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Hiker 1": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Hiker 2": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Hiker 3": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - Jr. Trainer F 3": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - PokeManiac 1": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - PokeManiac 2": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - PokeManiac 3": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Hiker 1": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Hiker 2": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Hiker 3": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - North Item": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Northwest Item": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - Southwest Item": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel B1F - West Item": lambda state: logic.rock_tunnel(state, player),
"Rock Tunnel 1F - PokeManiac": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Hiker 1": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Hiker 2": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Hiker 3": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel 1F - Jr. Trainer F 3": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - PokeManiac 1": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - PokeManiac 2": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - PokeManiac 3": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Hiker 1": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Hiker 2": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Hiker 3": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - North Item": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Northwest Item": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - Southwest Item": lambda state: logic.rock_tunnel(state, world, player),
"Rock Tunnel B1F - West Item": lambda state: logic.rock_tunnel(state, world, player),
# Pokédex check
"Oak's Lab - Oak's Parcel Reward": lambda state: state.has("Oak's Parcel", player),
# Hidden items
"Viridian Forest - Hidden Item Northwest by Trainer": lambda state: logic.can_get_hidden_items(state,
"Viridian Forest - Hidden Item Northwest by Trainer": lambda state: logic.can_get_hidden_items(state, world,
player),
"Viridian Forest - Hidden Item Entrance Tree": lambda state: logic.can_get_hidden_items(state, player),
"Mt Moon B2F - Hidden Item Dead End Before Fossils": lambda state: logic.can_get_hidden_items(state,
"Viridian Forest - Hidden Item Entrance Tree": lambda state: logic.can_get_hidden_items(state, world, player),
"Mt Moon B2F - Hidden Item Dead End Before Fossils": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 25 - Hidden Item Fence Outside Bill's House": lambda state: logic.can_get_hidden_items(state,
"Route 25 - Hidden Item Fence Outside Bill's House": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 9 - Hidden Item Bush By Grass": lambda state: logic.can_get_hidden_items(state, player),
"S.S. Anne Kitchen - Hidden Item Kitchen Trash": lambda state: logic.can_get_hidden_items(state, player),
"S.S. Anne B1F Rooms - Hidden Item Under Pillow": lambda state: logic.can_get_hidden_items(state, player),
"Route 9 - Hidden Item Bush By Grass": lambda state: logic.can_get_hidden_items(state, world, player),
"S.S. Anne Kitchen - Hidden Item Kitchen Trash": lambda state: logic.can_get_hidden_items(state, world, player),
"S.S. Anne B1F Rooms - Hidden Item Under Pillow": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 10 - Hidden Item Behind Rock Tunnel Entrance Cuttable Tree": lambda
state: logic.can_get_hidden_items(state, player) and logic.can_cut(state, player),
"Route 10 - Hidden Item Bush": lambda state: logic.can_get_hidden_items(state, player),
"Rocket Hideout B1F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, player),
"Rocket Hideout B3F - Hidden Item Near East Item": lambda state: logic.can_get_hidden_items(state, player),
state: logic.can_get_hidden_items(state, world, player) and logic.can_cut(state, world, player),
"Route 10 - Hidden Item Bush": lambda state: logic.can_get_hidden_items(state, world, player),
"Rocket Hideout B1F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, world, player),
"Rocket Hideout B3F - Hidden Item Near East Item": lambda state: logic.can_get_hidden_items(state, world, player),
"Rocket Hideout B4F - Hidden Item Behind Giovanni": lambda state:
logic.can_get_hidden_items(state, player),
"Pokemon Tower 5F - Hidden Item Near West Staircase": lambda state: logic.can_get_hidden_items(state,
logic.can_get_hidden_items(state, world, player),
"Pokemon Tower 5F - Hidden Item Near West Staircase": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 13 - Hidden Item Dead End Bush": lambda state: logic.can_get_hidden_items(state, player),
"Route 13 - Hidden Item Dead End By Water Corner": lambda state: logic.can_get_hidden_items(state, player),
"Pokemon Mansion B1F - Hidden Item Secret Key Room Corner": lambda state: logic.can_get_hidden_items(state,
"Route 13 - Hidden Item Dead End Bush": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 13 - Hidden Item Dead End By Water Corner": lambda state: logic.can_get_hidden_items(state, world, player),
"Pokemon Mansion B1F - Hidden Item Secret Key Room Corner": lambda state: logic.can_get_hidden_items(state, world,
player),
"Safari Zone West - Hidden Item Secret House Statue": lambda state: logic.can_get_hidden_items(state,
"Safari Zone West - Hidden Item Secret House Statue": lambda state: logic.can_get_hidden_items(state, world,
player),
"Silph Co 5F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, player),
"Silph Co 9F - Hidden Item Nurse Bed": lambda state: logic.can_get_hidden_items(state, player),
"Saffron Copycat's House 2F - Hidden Item Desk": lambda state: logic.can_get_hidden_items(state, player),
"Cerulean Cave 1F - Hidden Item Center Rocks": lambda state: logic.can_get_hidden_items(state, player),
"Cerulean Cave B1F - Hidden Item Northeast Rocks": lambda state: logic.can_get_hidden_items(state, player),
"Power Plant - Hidden Item Central Dead End": lambda state: logic.can_get_hidden_items(state, player),
"Power Plant - Hidden Item Before Zapdos": lambda state: logic.can_get_hidden_items(state, player),
"Seafoam Islands B2F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, player),
"Seafoam Islands B3F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, player),
"Silph Co 5F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, world, player),
"Silph Co 9F - Hidden Item Nurse Bed": lambda state: logic.can_get_hidden_items(state, world, player),
"Saffron Copycat's House 2F - Hidden Item Desk": lambda state: logic.can_get_hidden_items(state, world, player),
"Cerulean Cave 1F - Hidden Item Center Rocks": lambda state: logic.can_get_hidden_items(state, world, player),
"Cerulean Cave B1F - Hidden Item Northeast Rocks": lambda state: logic.can_get_hidden_items(state, world, player),
"Power Plant - Hidden Item Central Dead End": lambda state: logic.can_get_hidden_items(state, world, player),
"Power Plant - Hidden Item Before Zapdos": lambda state: logic.can_get_hidden_items(state, world, player),
"Seafoam Islands B2F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, world, player),
"Seafoam Islands B3F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, world, player),
# if you can reach any exit boulders, that means you can drop into the water tunnel and auto-surf
"Seafoam Islands B4F - Hidden Item Corner Island": lambda state: logic.can_get_hidden_items(state, player),
"Seafoam Islands B4F - Hidden Item Corner Island": lambda state: logic.can_get_hidden_items(state, world, player),
"Pokemon Mansion 1F - Hidden Item Block Near Entrance Carpet": lambda
state: logic.can_get_hidden_items(state, player),
"Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: logic.can_get_hidden_items(state, player),
"Route 23 - Hidden Item Rocks Before Victory Road": lambda state: logic.can_get_hidden_items(state,
state: logic.can_get_hidden_items(state, world, player),
"Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 23 - Hidden Item Rocks Before Victory Road": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 23 - Hidden Item East Bush After Water": lambda state: logic.can_get_hidden_items(state,
"Route 23 - Hidden Item East Bush After Water": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 23 - Hidden Item On Island": lambda state: logic.can_get_hidden_items(state,
player) and logic.can_surf(state, player),
"Victory Road 2F - Hidden Item Rock Before Moltres": lambda state: logic.can_get_hidden_items(state,
"Route 23 - Hidden Item On Island": lambda state: logic.can_get_hidden_items(state, world,
player) and logic.can_surf(state, world, player),
"Victory Road 2F - Hidden Item Rock Before Moltres": lambda state: logic.can_get_hidden_items(state, world,
player),
"Victory Road 2F - Hidden Item Rock In Final Room": lambda state: logic.can_get_hidden_items(state, player),
"Viridian City - Hidden Item Cuttable Tree": lambda state: logic.can_get_hidden_items(state, player),
"Route 11 - Hidden Item Isolated Bush Near Gate": lambda state: logic.can_get_hidden_items(state, player),
"Route 12 - Hidden Item Bush Near Gate": lambda state: logic.can_get_hidden_items(state, player),
"Route 17 - Hidden Item In Grass": lambda state: logic.can_get_hidden_items(state, player),
"Route 17 - Hidden Item Near Northernmost Sign": lambda state: logic.can_get_hidden_items(state, player),
"Route 17 - Hidden Item East Center": lambda state: logic.can_get_hidden_items(state, player),
"Route 17 - Hidden Item West Center": lambda state: logic.can_get_hidden_items(state, player),
"Route 17 - Hidden Item Before Final Bridge": lambda state: logic.can_get_hidden_items(state, player),
"Victory Road 2F - Hidden Item Rock In Final Room": lambda state: logic.can_get_hidden_items(state, world, player),
"Viridian City - Hidden Item Cuttable Tree": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 11 - Hidden Item Isolated Bush Near Gate": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 12 - Hidden Item Bush Near Gate": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 17 - Hidden Item In Grass": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 17 - Hidden Item Near Northernmost Sign": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 17 - Hidden Item East Center": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 17 - Hidden Item West Center": lambda state: logic.can_get_hidden_items(state, world, player),
"Route 17 - Hidden Item Before Final Bridge": lambda state: logic.can_get_hidden_items(state, world, player),
"Underground Path North South - Hidden Item Near Northern Stairs": lambda
state: logic.can_get_hidden_items(state, player),
state: logic.can_get_hidden_items(state, world, player),
"Underground Path North South - Hidden Item Near Southern Stairs": lambda
state: logic.can_get_hidden_items(state, player),
"Underground Path West East - Hidden Item West": lambda state: logic.can_get_hidden_items(state, player),
"Underground Path West East - Hidden Item East": lambda state: logic.can_get_hidden_items(state, player),
"Celadon City - Hidden Item Dead End Near Cuttable Tree": lambda state: logic.can_get_hidden_items(state,
state: logic.can_get_hidden_items(state, world, player),
"Underground Path West East - Hidden Item West": lambda state: logic.can_get_hidden_items(state, world, player),
"Underground Path West East - Hidden Item East": lambda state: logic.can_get_hidden_items(state, world, player),
"Celadon City - Hidden Item Dead End Near Cuttable Tree": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 25 - Hidden Item Northeast Of Grass": lambda state: logic.can_get_hidden_items(state, player),
"Mt Moon B2F - Hidden Item Lone Rock": lambda state: logic.can_get_hidden_items(state, player),
"Vermilion City - Hidden Item In Water Near Fan Club": lambda state: logic.can_get_hidden_items(state,
player) and logic.can_surf(state, player),
"Cerulean City - Hidden Item Gym Badge Guy's Backyard": lambda state: logic.can_get_hidden_items(state,
"Route 25 - Hidden Item Northeast Of Grass": lambda state: logic.can_get_hidden_items(state, world, player),
"Mt Moon B2F - Hidden Item Lone Rock": lambda state: logic.can_get_hidden_items(state, world, player),
"Vermilion City - Hidden Item In Water Near Fan Club": lambda state: logic.can_get_hidden_items(state, world,
player) and logic.can_surf(state, world, player),
"Cerulean City - Hidden Item Gym Badge Guy's Backyard": lambda state: logic.can_get_hidden_items(state, world,
player),
"Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: logic.can_get_hidden_items(state, player),
"Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: logic.can_get_hidden_items(state, world, player),
# Evolutions
"Evolution - Ivysaur": lambda state: state.has("Bulbasaur", player) and logic.evolve_level(state, 16, player),
@ -281,5 +273,4 @@ def set_rules(multiworld, player):
if loc.name.startswith("Pokedex"):
mon = loc.name.split(" - ")[1]
add_rule(loc, lambda state, i=mon: (state.has("Pokedex", player) or not
state.multiworld.require_pokedex[player]) and (state.has(i, player)
or state.has(f"Static {i}", player)))
world.options.require_pokedex) and (state.has(i, player) or state.has(f"Static {i}", player)))