forked from mirror/Archipelago
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
2102 lines
112 KiB
Python
2102 lines
112 KiB
Python
from collections import defaultdict
|
|
from typing import TYPE_CHECKING
|
|
|
|
from BaseClasses import CollectionState
|
|
from worlds.generic.Rules import add_rule, set_rule, CollectionRule
|
|
from .data import data, EvolutionType, EvolutionData, FishingRodType, EncounterKey, LogicalAccess, EncounterType
|
|
from .evolution import evolution_location_name
|
|
from .items import PokemonCrystalGlitchedToken
|
|
from .options import Goal, JohtoOnly, Route32Condition, UndergroundsRequirePower, Route2Access, \
|
|
BlackthornDarkCaveAccess, NationalParkAccess, KantoAccessRequirement, Route3Access, BreedingMethodsRequired, \
|
|
MtSilverRequirement, FreeFlyLocation, HMBadgeRequirements, EliteFourRequirement, RedRequirement, \
|
|
Route44AccessRequirement, RandomizeBadges, RadioTowerRequirement, PokemonCrystalOptions, Shopsanity, FlyCheese, \
|
|
RequireFlash, RequireItemfinder, Route42Access, RedGyaradosAccess, RandomizePhoneCalls, Route30Access, \
|
|
SouthKantoCondition, SouthKantoAccess
|
|
from .pokemon import add_hm_compatibility, get_chamber_event_for_unown
|
|
from .pokemon_data import ALL_UNOWN
|
|
from .utils import get_fly_regions, get_mart_slot_location_name
|
|
|
|
if TYPE_CHECKING:
|
|
from .world import PokemonCrystalWorld
|
|
|
|
|
|
class PokemonCrystalLogic:
|
|
available_pokemon: set[str]
|
|
all_pokemon: set[str]
|
|
evolution: dict[str, list[tuple[EvolutionData, LogicalAccess]]]
|
|
breeding: dict[str, list[tuple[str, LogicalAccess, bool]]]
|
|
wild_regions: dict[EncounterKey, LogicalAccess]
|
|
guaranteed_hm_access: bool
|
|
|
|
pokemon_hm_use: dict[str, list[str]]
|
|
compatible_hm_pokemon: dict[str, list[str]]
|
|
|
|
badge_items: dict[str, str]
|
|
hm_badge_requirements_johto: dict[str, tuple]
|
|
hm_badge_requirements_kanto: dict[str, tuple]
|
|
pokemon_hm_use: dict[str, list[str]]
|
|
gym_events: dict[str, str]
|
|
|
|
map_card_fly_unlocks: tuple
|
|
expn_components: tuple
|
|
|
|
fishing_rod_rules: dict[FishingRodType, CollectionRule]
|
|
|
|
player: int
|
|
options: PokemonCrystalOptions
|
|
|
|
def __init__(self, world: "PokemonCrystalWorld"):
|
|
self.available_pokemon = set()
|
|
self.all_pokemon = set(world.generated_pokemon.keys())
|
|
self.evolution = defaultdict(list)
|
|
self.breeding = defaultdict(list)
|
|
self.wild_regions = defaultdict(lambda: LogicalAccess.Inaccessible)
|
|
self.compatible_hm_pokemon = defaultdict(list)
|
|
self.guaranteed_hm_access = False
|
|
|
|
self.hm_badge_requirements_johto = {}
|
|
self.hm_badge_requirements_kanto = {}
|
|
self.pokemon_hm_use = {}
|
|
|
|
self.player = world.player
|
|
self.options = world.options
|
|
self.is_universal_tracker = world.is_universal_tracker
|
|
|
|
if self.options.randomize_badges == RandomizeBadges.option_vanilla:
|
|
self.badge_items = {
|
|
"zephyr": "EVENT_ZEPHYR_BADGE_FROM_FALKNER",
|
|
"hive": "EVENT_HIVE_BADGE_FROM_BUGSY",
|
|
"plain": "EVENT_PLAIN_BADGE_FROM_WHITNEY",
|
|
"fog": "EVENT_FOG_BADGE_FROM_MORTY",
|
|
"mineral": "EVENT_STORM_BADGE_FROM_CHUCK",
|
|
"storm": "EVENT_MINERAL_BADGE_FROM_JASMINE",
|
|
"glacier": "EVENT_GLACIER_BADGE_FROM_PRYCE",
|
|
"rising": "EVENT_RISING_BADGE_FROM_CLAIR" if world.options.vanilla_clair else "EVENT_RISING_BADGE_FROM_CLAIR_GYM",
|
|
|
|
"boulder": "EVENT_BOULDER_BADGE_FROM_BROCK",
|
|
"cascade": "EVENT_CASCADE_BADGE_FROM_MISTY",
|
|
"thunder": "EVENT_THUNDER_BADGE_FROM_LTSURGE",
|
|
"rainbow": "EVENT_RAINBOW_BADGE_FROM_ERIKA",
|
|
"soul": "EVENT_SOUL_BADGE_FROM_JANINE",
|
|
"marsh": "EVENT_MARSH_BADGE_FROM_SABRINA",
|
|
"volcano": "EVENT_VOLCANO_BADGE_FROM_BLAINE",
|
|
"earth": "EVENT_EARTH_BADGE_FROM_BLUE"
|
|
}
|
|
else:
|
|
self.badge_items = {
|
|
"zephyr": "Zephyr Badge",
|
|
"hive": "Hive Badge",
|
|
"plain": "Plain Badge",
|
|
"fog": "Fog Badge",
|
|
"mineral": "Mineral Badge",
|
|
"storm": "Storm Badge",
|
|
"glacier": "Glacier Badge",
|
|
"rising": "Rising Badge",
|
|
|
|
"boulder": "Boulder Badge",
|
|
"cascade": "Cascade Badge",
|
|
"thunder": "Thunder Badge",
|
|
"rainbow": "Rainbow Badge",
|
|
"soul": "Soul Badge",
|
|
"marsh": "Marsh Badge",
|
|
"volcano": "Volcano Badge",
|
|
"earth": "Earth Badge"
|
|
}
|
|
|
|
self.gym_events = {
|
|
"falkner": "EVENT_BEAT_FALKNER",
|
|
"bugsy": "EVENT_BEAT_BUGSY",
|
|
"whitney": "EVENT_BEAT_WHITNEY",
|
|
"morty": "EVENT_BEAT_MORTY",
|
|
"jasmine": "EVENT_BEAT_JASMINE",
|
|
"chuck": "EVENT_BEAT_CHUCK",
|
|
"pryce": "EVENT_BEAT_PRYCE",
|
|
"clair": "EVENT_BEAT_CLAIR",
|
|
|
|
"brock": "EVENT_BEAT_BROCK",
|
|
"misty": "EVENT_BEAT_MISTY",
|
|
"ltsurge": "EVENT_BEAT_LTSURGE",
|
|
"erika": "EVENT_BEAT_ERIKA",
|
|
"janine": "EVENT_BEAT_JANINE",
|
|
"sabrina": "EVENT_BEAT_SABRINA",
|
|
"blaine": "EVENT_BEAT_BLAINE",
|
|
"blue": "EVENT_BEAT_BLUE"
|
|
}
|
|
|
|
if world.options.hm_badge_requirements != HMBadgeRequirements.option_no_badges:
|
|
if "Cut" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["CUT"] = ("hive",)
|
|
self.hm_badge_requirements_kanto["CUT"] = ("hive",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["CUT"] = ("hive", "cascade")
|
|
self.hm_badge_requirements_kanto["CUT"] = ("hive", "cascade")
|
|
else:
|
|
self.hm_badge_requirements_johto["CUT"] = ("hive",)
|
|
self.hm_badge_requirements_kanto["CUT"] = ("cascade",)
|
|
|
|
if "Fly" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["FLY"] = ("storm",)
|
|
self.hm_badge_requirements_kanto["FLY"] = ("storm",)
|
|
else:
|
|
self.hm_badge_requirements_johto["FLY"] = ("storm", "thunder")
|
|
self.hm_badge_requirements_kanto["FLY"] = ("storm", "thunder")
|
|
|
|
if "Surf" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["SURF"] = ("fog",)
|
|
self.hm_badge_requirements_kanto["SURF"] = ("fog",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["SURF"] = ("fog", "soul")
|
|
self.hm_badge_requirements_kanto["SURF"] = ("fog", "soul")
|
|
else:
|
|
self.hm_badge_requirements_johto["SURF"] = ("fog",)
|
|
self.hm_badge_requirements_kanto["SURF"] = ("soul",)
|
|
|
|
if "Strength" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["STRENGTH"] = ("plain",)
|
|
self.hm_badge_requirements_kanto["STRENGTH"] = ("plain",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["STRENGTH"] = ("plain", "rainbow")
|
|
self.hm_badge_requirements_kanto["STRENGTH"] = ("plain", "rainbow")
|
|
else:
|
|
self.hm_badge_requirements_johto["STRENGTH"] = ("plain",)
|
|
self.hm_badge_requirements_kanto["STRENGTH"] = ("rainbow",)
|
|
|
|
if "Flash" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["FLASH"] = ("zephyr",)
|
|
self.hm_badge_requirements_kanto["FLASH"] = ("zephyr",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["FLASH"] = ("zephyr", "boulder")
|
|
self.hm_badge_requirements_kanto["FLASH"] = ("zephyr", "boulder")
|
|
else:
|
|
self.hm_badge_requirements_johto["FLASH"] = ("zephyr",)
|
|
self.hm_badge_requirements_kanto["FLASH"] = ("boulder",)
|
|
|
|
if "Whirlpool" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["WHIRLPOOL"] = ("glacier",)
|
|
self.hm_badge_requirements_kanto["WHIRLPOOL"] = ("glacier",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["WHIRLPOOL"] = ("glacier", "volcano")
|
|
self.hm_badge_requirements_kanto["WHIRLPOOL"] = ("glacier", "volcano")
|
|
else:
|
|
self.hm_badge_requirements_johto["WHIRLPOOL"] = ("glacier",)
|
|
self.hm_badge_requirements_kanto["WHIRLPOOL"] = ("volcano",)
|
|
|
|
if "Waterfall" not in world.options.remove_badge_requirement:
|
|
if world.options.hm_badge_requirements == HMBadgeRequirements.option_vanilla:
|
|
self.hm_badge_requirements_johto["WATERFALL"] = ("rising",)
|
|
self.hm_badge_requirements_kanto["WATERFALL"] = ("rising",)
|
|
elif world.options.hm_badge_requirements == HMBadgeRequirements.option_add_kanto:
|
|
self.hm_badge_requirements_johto["WATERFALL"] = ("rising", "earth")
|
|
self.hm_badge_requirements_kanto["WATERFALL"] = ("rising", "earth")
|
|
else:
|
|
self.hm_badge_requirements_johto["WATERFALL"] = ("rising",)
|
|
self.hm_badge_requirements_kanto["WATERFALL"] = ("earth",)
|
|
|
|
if world.options.randomize_pokegear:
|
|
self.map_card_fly_unlocks = ("Map Card", "Pokegear")
|
|
self.expn_components = ("Pokegear", "Radio Card", "EXPN Card")
|
|
if world.options.randomize_phone_call_items == RandomizePhoneCalls.option_on_simple:
|
|
self.phone_call_components = ("Pokegear", "Phone Card")
|
|
else:
|
|
self.phone_call_components = ("Pokegear", "Phone Card", "EVENT_CHANGE_DST")
|
|
else:
|
|
self.map_card_fly_unlocks = ("EVENT_GOT_MAP_CARD", "EVENT_GOT_POKEGEAR")
|
|
self.expn_components = ("EVENT_GOT_POKEGEAR", "EVENT_GOT_RADIO_CARD", "EVENT_GOT_EXPN_CARD")
|
|
if world.options.randomize_phone_call_items == RandomizePhoneCalls.option_on_simple:
|
|
self.phone_call_components = ("EVENT_GOT_POKEGEAR", "EVENT_GOT_PHONE_CARD")
|
|
else:
|
|
self.phone_call_components = ("EVENT_GOT_POKEGEAR", "EVENT_GOT_PHONE_CARD", "EVENT_CHANGE_DST")
|
|
|
|
if world.options.randomize_pokedex:
|
|
self.pokedex = "Pokedex"
|
|
else:
|
|
self.pokedex = "EVENT_GOT_POKEDEX"
|
|
|
|
if world.options.progressive_rods:
|
|
self.fishing_rod_rules = {
|
|
FishingRodType.Old: lambda state: state.has("Progressive Rod", self.player),
|
|
FishingRodType.Good: lambda state: state.has("Progressive Rod", self.player, 2),
|
|
FishingRodType.Super: lambda state: state.has("Progressive Rod", self.player, 3),
|
|
}
|
|
else:
|
|
self.fishing_rod_rules = {
|
|
FishingRodType.Old: lambda state: state.has("Old Rod", self.player),
|
|
FishingRodType.Good: lambda state: state.has("Good Rod", self.player),
|
|
FishingRodType.Super: lambda state: state.has("Super Rod", self.player),
|
|
}
|
|
|
|
def has_badge(self, state: CollectionState, badge: str):
|
|
return state.has(self.badge_items[badge], self.player)
|
|
|
|
def has_n_badges(self, state: CollectionState, n: int) -> bool:
|
|
return state.has_from_list_unique(self.badge_items.values(), self.player, n)
|
|
|
|
def has_beaten_gym(self, state: CollectionState, leader: str):
|
|
return state.has(self.gym_events[leader], self.player)
|
|
|
|
def has_beaten_n_gyms(self, state: CollectionState, n: int):
|
|
return state.has_from_list_unique(self.gym_events.values(), self.player, n)
|
|
|
|
def has_n_pokemon(self, state: CollectionState, n: int):
|
|
return state.has_from_list_unique(self.all_pokemon, self.player, n)
|
|
|
|
def has_hm_badge_requirement(self, hm: str, kanto: bool) -> CollectionRule:
|
|
if kanto:
|
|
return lambda state: state.has_any(
|
|
(self.badge_items[badge] for badge in self.hm_badge_requirements_kanto[hm]),
|
|
self.player) if hm in self.hm_badge_requirements_kanto else True
|
|
else:
|
|
return lambda state: state.has_any(
|
|
(self.badge_items[badge] for badge in self.hm_badge_requirements_johto[hm]),
|
|
self.player) if hm in self.hm_badge_requirements_johto else True
|
|
|
|
def can_cut(self, kanto: bool = False) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("CUT", kanto=kanto)
|
|
required_items = {"HM01 Cut"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach CUT")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_fly(self) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("FLY", kanto=False)
|
|
required_items = {"HM02 Fly"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach FLY")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_surf(self, kanto: bool = False) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("SURF", kanto=kanto)
|
|
required_items = {"HM03 Surf"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach SURF")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_strength(self, kanto: bool = False) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("STRENGTH", kanto=kanto)
|
|
required_items = {"HM04 Strength"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach STRENGTH")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_flash(self, kanto: bool = False, allow_ool: bool = True) -> CollectionRule:
|
|
if self.options.require_flash == RequireFlash.option_not_required and allow_ool:
|
|
return lambda _: True
|
|
badge_requirement = self.has_hm_badge_requirement("FLASH", kanto=kanto)
|
|
required_items = {"HM05 Flash"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach FLASH")
|
|
if self.is_universal_tracker and allow_ool and self.options.require_flash == RequireFlash.option_logically_required:
|
|
return lambda state: (state.has_all(required_items, self.player) and badge_requirement(
|
|
state)) or state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, self.player)
|
|
else:
|
|
return lambda state: (state.has_all(required_items, self.player) and badge_requirement(state))
|
|
|
|
def can_whirlpool(self, kanto: bool = False) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("WHIRLPOOL", kanto=kanto)
|
|
required_items = {"HM06 Whirlpool"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach WHIRLPOOL")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_waterfall(self, kanto: bool = False) -> CollectionRule:
|
|
badge_requirement = self.has_hm_badge_requirement("WATERFALL", kanto=kanto)
|
|
required_items = {"HM07 Waterfall"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach WATERFALL")
|
|
return lambda state: state.has_all(required_items, self.player) and badge_requirement(state)
|
|
|
|
def can_headbutt(self) -> CollectionRule:
|
|
required_items = {"TM02"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach HEADBUTT")
|
|
return lambda state: state.has_all(required_items, self.player)
|
|
|
|
def can_rock_smash(self) -> CollectionRule:
|
|
required_items = {"TM08"}
|
|
if not self.options.field_moves_always_usable:
|
|
required_items.add("Teach ROCK_SMASH")
|
|
return lambda state: state.has_all(required_items, self.player)
|
|
|
|
def has_tea(self) -> CollectionRule:
|
|
return lambda state: state.has("Tea", self.player)
|
|
|
|
def can_map_card_fly(self) -> CollectionRule:
|
|
return lambda state: state.has_all(self.map_card_fly_unlocks, self.player)
|
|
|
|
def has_expn(self) -> CollectionRule:
|
|
return lambda state: state.has_all(self.expn_components, self.player)
|
|
|
|
def has_rockets_requirement(self) -> CollectionRule:
|
|
if self.options.radio_tower_requirement == RadioTowerRequirement.option_badges:
|
|
return lambda state: self.has_n_badges(state, self.options.radio_tower_count.value)
|
|
else:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.radio_tower_count.value)
|
|
|
|
def has_route_44_access(self) -> CollectionRule:
|
|
if self.options.route_44_access_requirement == Route44AccessRequirement.option_badges:
|
|
return lambda state: self.has_n_badges(state, self.options.route_44_access_count.value)
|
|
else:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.route_44_access_count.value)
|
|
|
|
def has_elite_four_requirement(self) -> CollectionRule:
|
|
if self.options.elite_four_requirement == EliteFourRequirement.option_gyms:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.elite_four_count.value)
|
|
elif self.options.elite_four_requirement == EliteFourRequirement.option_badges:
|
|
return lambda state: self.has_n_badges(state, self.options.elite_four_count.value)
|
|
else:
|
|
johto_badges = list(self.badge_items.values())[:8]
|
|
return lambda state: state.has_from_list_unique(johto_badges, self.player,
|
|
self.options.elite_four_count.value)
|
|
|
|
def has_red_requirement(self) -> CollectionRule:
|
|
if self.options.red_requirement == RedRequirement.option_gyms:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.red_count.value)
|
|
else:
|
|
return lambda state: self.has_n_badges(state, self.options.red_count.value)
|
|
|
|
def has_mt_silver_requirement(self) -> CollectionRule:
|
|
if self.options.mt_silver_requirement == MtSilverRequirement.option_gyms:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.mt_silver_count.value)
|
|
else:
|
|
return lambda state: self.has_n_badges(state, self.options.mt_silver_count.value)
|
|
|
|
def has_kanto_access_requirement(self) -> CollectionRule:
|
|
if self.options.kanto_access_requirement == KantoAccessRequirement.option_wake_snorlax:
|
|
return lambda state: state.has("EVENT_FOUGHT_SNORLAX", self.player)
|
|
elif self.options.kanto_access_requirement == KantoAccessRequirement.option_badges:
|
|
return lambda state: self.has_n_badges(state, self.options.kanto_access_count.value)
|
|
elif self.options.kanto_access_requirement == KantoAccessRequirement.option_gyms:
|
|
return lambda state: self.has_beaten_n_gyms(state, self.options.kanto_access_count.value)
|
|
else:
|
|
return lambda state: state.has("EVENT_BEAT_ELITE_FOUR", self.player)
|
|
|
|
def has_route_32_condition(self) -> CollectionRule | None:
|
|
if self.options.route_32_condition == Route32Condition.option_egg_from_aide:
|
|
return lambda state: state.has("EVENT_GOT_TOGEPI_EGG_FROM_ELMS_AIDE", self.player)
|
|
elif self.options.route_32_condition == Route32Condition.option_any_badge:
|
|
return lambda state: self.has_n_badges(state, 1)
|
|
elif self.options.route_32_condition == Route32Condition.option_any_gym:
|
|
return lambda state: self.has_beaten_n_gyms(state, 1)
|
|
elif self.options.route_32_condition == Route32Condition.option_zephyr_badge:
|
|
return lambda state: self.has_badge(state, "zephyr")
|
|
else:
|
|
return None
|
|
|
|
def set_hm_compatible_pokemon(self, world: "PokemonCrystalWorld"):
|
|
hms = ("CUT", "FLY", "SURF", "STRENGTH", "FLASH", "WHIRLPOOL", "WATERFALL", "HEADBUTT", "ROCK_SMASH")
|
|
for hm in hms:
|
|
for pokemon_id, pokemon_data in world.generated_pokemon.items():
|
|
if hm in pokemon_data.tm_hm:
|
|
self.compatible_hm_pokemon[hm].append(pokemon_id)
|
|
|
|
pokemon_hm_use = defaultdict(list)
|
|
for hm, species_list in self.compatible_hm_pokemon.items():
|
|
for species in species_list:
|
|
pokemon_hm_use[species].append(f"Teach {hm}")
|
|
self.pokemon_hm_use = pokemon_hm_use
|
|
|
|
def add_hm_compatible_pokemon(self, hm: str, pokemon_id: str):
|
|
self.compatible_hm_pokemon[hm].append(pokemon_id)
|
|
self.pokemon_hm_use.setdefault(pokemon_id, []).append(f"Teach {hm}")
|
|
|
|
|
|
def set_rules(world: "PokemonCrystalWorld") -> None:
|
|
unown_unlocks = ("ENGINE_UNLOCKED_UNOWNS_A_TO_K",
|
|
"ENGINE_UNLOCKED_UNOWNS_L_TO_R",
|
|
"ENGINE_UNLOCKED_UNOWNS_S_TO_W",
|
|
"ENGINE_UNLOCKED_UNOWNS_X_TO_Z")
|
|
evolution_item_unlocks = ("EVENT_GOLDENROD_EVOLUTION_ITEMS", "EVENT_CELADON_EVOLUTION_ITEMS")
|
|
happiness_unlocks = ("EVENT_DAISY_GROOMING", "EVENT_HAIRCUT_BROTHERS")
|
|
|
|
def get_entrance(entrance: str):
|
|
return world.multiworld.get_entrance(entrance, world.player)
|
|
|
|
def get_location(location: str):
|
|
if location in data.locations:
|
|
location = data.locations[location].label
|
|
|
|
return world.multiworld.get_location(location, world.player)
|
|
|
|
def safe_set_location_rule(spot: str, rule: CollectionRule) -> None:
|
|
try:
|
|
location = world.get_location(spot)
|
|
except KeyError:
|
|
return
|
|
set_rule(location, rule)
|
|
|
|
def hidden():
|
|
return world.options.randomize_hidden_items
|
|
|
|
def johto_only():
|
|
return world.options.johto_only.value
|
|
|
|
def rematchsanity():
|
|
return world.options.rematchsanity or world.options.randomize_phone_call_items
|
|
|
|
can_cut = world.logic.can_cut()
|
|
can_cut_kanto = world.logic.can_cut(kanto=True)
|
|
|
|
can_fly = world.logic.can_fly()
|
|
|
|
can_surf = world.logic.can_surf()
|
|
can_surf_kanto = world.logic.can_surf(kanto=True)
|
|
|
|
can_strength = world.logic.can_strength()
|
|
|
|
can_flash = world.logic.can_flash()
|
|
can_flash_kanto = world.logic.can_flash(kanto=True)
|
|
|
|
can_whirlpool = world.logic.can_whirlpool()
|
|
|
|
can_waterfall = world.logic.can_waterfall()
|
|
|
|
can_headbutt = world.logic.can_headbutt()
|
|
can_rock_smash = world.logic.can_rock_smash()
|
|
|
|
can_surf_and_whirlpool = lambda state: can_surf(state) and can_whirlpool(state)
|
|
can_surf_and_waterfall = lambda state: can_surf(state) and can_waterfall(state)
|
|
|
|
kanto_gyms_access = lambda state: state.has_any(
|
|
("EVENT_SILVER_CAVE_ACCESS", "EVENT_FOUGHT_SNORLAX", "EVENT_FOUGHT_LUGIA", "EVENT_FOUGHT_HO_OH",
|
|
"EVENT_FOUGHT_SUICUNE", "EVENT_VICTORY_ROAD_ACCESS"), world.player
|
|
)
|
|
|
|
can_phone_call = lambda state: state.has_all(world.logic.phone_call_components, world.player)
|
|
|
|
has_pokedex = lambda state: state.has(world.logic.pokedex, world.player)
|
|
|
|
# Goal
|
|
if world.options.goal == Goal.option_red:
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has("EVENT_BEAT_RED", world.player)
|
|
elif world.options.goal == Goal.option_diploma:
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has(
|
|
"EVENT_OBTAINED_DIPLOMA", world.player)
|
|
elif world.options.goal == Goal.option_rival:
|
|
rival_events = [
|
|
"EVENT_BEAT_CHERRYGROVE_RIVAL",
|
|
"EVENT_BEAT_AZALEA_RIVAL",
|
|
"EVENT_RIVAL_BURNED_TOWER",
|
|
"EVENT_BEAT_GOLDENROD_UNDERGROUND_RIVAL",
|
|
"EVENT_BEAT_VICTORY_ROAD_RIVAL",
|
|
]
|
|
if world.options.johto_only == JohtoOnly.option_off:
|
|
rival_events.extend([
|
|
"EVENT_BEAT_RIVAL_IN_MT_MOON",
|
|
"EVENT_BEAT_RIVAL_IN_INDIGO_PLATEAU"
|
|
])
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has_all(rival_events, world.player)
|
|
elif world.options.goal == Goal.option_defeat_team_rocket:
|
|
rocket_events = [
|
|
"EVENT_CLEARED_SLOWPOKE_WELL",
|
|
"EVENT_CLEARED_ROCKET_HIDEOUT",
|
|
"EVENT_BEAT_ROCKET_EXECUTIVEM_3",
|
|
"EVENT_CLEARED_RADIO_TOWER",
|
|
]
|
|
if world.options.johto_only == JohtoOnly.option_off:
|
|
rocket_events.extend([
|
|
"EVENT_ROUTE_24_ROCKET"
|
|
])
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has_all(rocket_events, world.player)
|
|
elif world.options.goal == Goal.option_unown_hunt:
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has("EVENT_GOT_ALL_UNOWN",
|
|
world.player)
|
|
else:
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has(
|
|
"EVENT_BEAT_ELITE_FOUR", world.player)
|
|
|
|
# Free Fly
|
|
set_rule(get_entrance("Fly"), can_fly)
|
|
if world.options.free_fly_location.value in (FreeFlyLocation.option_free_fly_and_map_card,
|
|
FreeFlyLocation.option_map_card):
|
|
map_card_fly_entrance = f"Free Fly {world.map_card_fly_location.exit_region}"
|
|
add_rule(get_entrance(map_card_fly_entrance), world.logic.can_map_card_fly())
|
|
|
|
# Fly Unlocks
|
|
|
|
if world.options.randomize_fly_unlocks or world.options.remote_items:
|
|
for fly_region in get_fly_regions(world):
|
|
set_rule(get_entrance(f"REGION_FLY -> {fly_region.exit_region}"),
|
|
lambda state, fly_unlock=f"Fly {fly_region.name}": state.has(fly_unlock, world.player))
|
|
|
|
# New Bark Town
|
|
|
|
set_rule(get_entrance("REGION_NEW_BARK_TOWN -> REGION_ROUTE_27:WEST"), can_surf)
|
|
|
|
set_rule(get_location("EVENT_GAVE_MYSTERY_EGG_TO_ELM"), lambda state: state.has("Mystery Egg", world.player))
|
|
|
|
set_rule(get_location("Elm's Lab - Everstone from Elm"),
|
|
lambda state: state.has("EVENT_GOT_TOGEPI_EGG_FROM_ELMS_AIDE", world.player))
|
|
|
|
set_rule(get_location("Elm's Lab - Gift from Aide after returning Mystery Egg"),
|
|
lambda state: state.has("Mystery Egg", world.player))
|
|
|
|
set_rule(get_location("Elm's Lab - Master Ball from Elm"), lambda state: world.logic.has_badge(state, "rising"))
|
|
|
|
set_rule(get_location("Elm's Lab - S.S. Ticket from Elm"),
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
# Route 29
|
|
set_rule(get_location("Route 29 - Pink Bow from Tuscany"), lambda state: world.logic.has_badge(state, "zephyr"))
|
|
|
|
# Route 30
|
|
if world.options.route_30_access == Route30Access.option_mr_pokemon:
|
|
route_30_rule = lambda state: (state.has("EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON", world.player)
|
|
or can_cut(state))
|
|
else:
|
|
route_30_rule = lambda state: state.has("EVENT_GAVE_MYSTERY_EGG_TO_ELM", world.player) or can_cut(state)
|
|
|
|
if world.options.route_30_battle:
|
|
set_rule(get_entrance("REGION_ROUTE_30:NORTHWEST -> REGION_ROUTE_30"), route_30_rule)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_30 -> REGION_ROUTE_30:NORTHWEST"), route_30_rule)
|
|
if world.options.route_30_access == Route30Access.option_mr_pokemon:
|
|
route_30_unblock = "EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON"
|
|
else:
|
|
route_30_unblock = "EVENT_GAVE_MYSTERY_EGG_TO_ELM"
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_30 -> REGION_ROUTE_30:POST_MYSTERY_EGG"),
|
|
lambda state: state.has(route_30_unblock, world.player))
|
|
|
|
set_rule(get_location("Route 30 - Exp Share from Mr Pokemon"), lambda state: state.has("Red Scale", world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("YOUNGSTER_JOEY_GOLDENROD",
|
|
lambda state: state.has("ENGINE_FLYPOINT_GOLDENROD", world.player))
|
|
safe_set_location_rule("YOUNGSTER_JOEY_OLIVINE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_OLIVINE", world.player))
|
|
safe_set_location_rule("YOUNGSTER_JOEY_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("YOUNGSTER_JOEY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 30 - HP Up from Joey"),
|
|
lambda state: can_phone_call(state) and state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
# Cherrygrove
|
|
set_rule(get_location("Cherrygrove City - Mystic Water from Island Man"), can_surf)
|
|
|
|
safe_set_location_rule("Cherrygrove City - Rival",
|
|
lambda state: state.has("EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON", world.player))
|
|
set_rule(get_location("EVENT_BEAT_CHERRYGROVE_RIVAL"),
|
|
lambda state: state.has("EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON", world.player))
|
|
|
|
# Route 31
|
|
if "Dark Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_31 -> REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST"), can_flash)
|
|
|
|
set_rule(get_location("EVENT_GAVE_KENYA"), lambda state: state.has("EVENT_GOT_KENYA", world.player))
|
|
set_rule(get_location("Route 31 - TM50 for delivering Kenya"),
|
|
lambda state: state.has("EVENT_GOT_KENYA", world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("BUG_CATCHER_WADE_GOLDENROD",
|
|
lambda state: state.has("ENGINE_FLYPOINT_GOLDENROD", world.player))
|
|
safe_set_location_rule("BUG_CATCHER_WADE_MAHOGANY",
|
|
lambda state: state.has("ENGINE_FLYPOINT_MAHOGANY", world.player))
|
|
safe_set_location_rule("BUG_CATCHER_WADE_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
# Dark Cave Violet
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("BUG_CATCHER_WADE_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 31 - Berry from Wade"), can_phone_call)
|
|
|
|
set_rule(get_location("Dark Cave Violet Entrance - Southeast Item (Left)"), can_rock_smash)
|
|
set_rule(get_location("Dark Cave Violet Entrance - Southeast Item (Right)"), can_rock_smash)
|
|
set_rule(get_location("Dark Cave Violet Entrance - Northeast Item"), can_rock_smash)
|
|
if hidden():
|
|
set_rule(get_location("Dark Cave Violet Entrance - Hidden Item"), can_rock_smash)
|
|
|
|
set_rule(get_entrance("REGION_DARK_CAVE_VIOLET_ENTRANCE:EAST -> REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST"),
|
|
can_rock_smash)
|
|
set_rule(get_entrance("REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST -> REGION_DARK_CAVE_VIOLET_ENTRANCE:EAST"),
|
|
can_rock_smash)
|
|
|
|
if "Dark Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_46:NORTH -> REGION_DARK_CAVE_VIOLET_ENTRANCE:EAST"),
|
|
can_flash)
|
|
set_rule(get_entrance("REGION_ROUTE_45 -> REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_EAST"),
|
|
can_flash)
|
|
|
|
set_rule(get_entrance(
|
|
"REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_EAST -> REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:SOUTH_EAST"), can_surf)
|
|
set_rule(get_entrance(
|
|
"REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:SOUTH_EAST -> REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_EAST"), can_surf)
|
|
set_rule(get_entrance(
|
|
"REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_EAST -> REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_WEST"), can_surf)
|
|
set_rule(get_entrance(
|
|
"REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_WEST -> REGION_DARK_CAVE_BLACKTHORN_ENTRANCE:NORTH_EAST"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_DARK_CAVE_VIOLET_ENTRANCE:WATER -> REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST"), can_surf)
|
|
|
|
if world.options.blackthorn_dark_cave_access.value == BlackthornDarkCaveAccess.option_waterfall:
|
|
set_rule(get_entrance("REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST -> REGION_DARK_CAVE_VIOLET_ENTRANCE:WATER"),
|
|
can_surf_and_waterfall)
|
|
else:
|
|
set_rule(get_entrance("REGION_DARK_CAVE_VIOLET_ENTRANCE:WEST -> REGION_DARK_CAVE_VIOLET_ENTRANCE:WATER"),
|
|
can_surf)
|
|
|
|
# Violet City
|
|
if hidden():
|
|
set_rule(get_location("Violet City - Hidden Item behind Cut Tree"), can_cut)
|
|
set_rule(get_location("Violet City - Northwest Item across Water"), can_surf)
|
|
set_rule(get_location("Violet City - Northeast Item across Water"), can_surf)
|
|
|
|
set_rule(get_location("EVENT_GOT_TOGEPI_EGG_FROM_ELMS_AIDE"),
|
|
lambda state: world.logic.has_beaten_gym(state, "falkner"))
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_OUTSIDE:NORTH -> REGION_RUINS_OF_ALPH_OUTSIDE:SOUTH"),
|
|
can_surf)
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_OUTSIDE:SOUTH -> REGION_RUINS_OF_ALPH_OUTSIDE:NORTH"),
|
|
can_surf)
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_KABUTO_CHAMBER -> REGION_RUINS_OF_ALPH_KABUTO_ITEM_ROOM"),
|
|
lambda state: state.has("EVENT_MART_ESCAPE_ROPE", world.player))
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_OMANYTE_CHAMBER -> REGION_RUINS_OF_ALPH_OMANYTE_ITEM_ROOM"),
|
|
lambda state: state.has_any(("EVENT_GOLDENROD_EVOLUTION_ITEMS", "EVENT_CELADON_EVOLUTION_ITEMS"),
|
|
world.player))
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_AERODACTYL_CHAMBER -> REGION_RUINS_OF_ALPH_AERODACTYL_ITEM_ROOM"),
|
|
world.logic.can_flash(allow_ool=False))
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_HO_OH_CHAMBER -> REGION_RUINS_OF_ALPH_HO_OH_ITEM_ROOM"),
|
|
lambda state: state.has("Rainbow Wing", world.player))
|
|
|
|
if world.options.goal == Goal.option_unown_hunt:
|
|
set_rule(get_location("EVENT_GOT_ALL_UNOWN"), lambda state: state.has_all(ALL_UNOWN, world.player))
|
|
set_rule(get_location("ENGINE_UNLOCKED_UNOWNS_A_TO_K"),
|
|
lambda state: state.has("Kabuto Tile", world.player, 16))
|
|
set_rule(get_location("ENGINE_UNLOCKED_UNOWNS_L_TO_R"),
|
|
lambda state: state.has("Omanyte Tile", world.player, 16))
|
|
set_rule(get_location("ENGINE_UNLOCKED_UNOWNS_S_TO_W"),
|
|
lambda state: state.has("Aerodactyl Tile", world.player, 16))
|
|
set_rule(get_location("ENGINE_UNLOCKED_UNOWNS_X_TO_Z"),
|
|
lambda state: state.has("Ho-Oh Tile", world.player, 16))
|
|
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_KABUTO_CHAMBER -> REGION_RUINS_OF_ALPH_INNER_CHAMBER"),
|
|
lambda state: state.has("Kabuto Tile", world.player, 16))
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_AERODACTYL_CHAMBER -> REGION_RUINS_OF_ALPH_INNER_CHAMBER"),
|
|
lambda state: state.has("Aerodactyl Tile", world.player, 16))
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_OMANYTE_CHAMBER -> REGION_RUINS_OF_ALPH_INNER_CHAMBER"),
|
|
lambda state: state.has("Omanyte Tile", world.player, 16))
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_HO_OH_CHAMBER -> REGION_RUINS_OF_ALPH_INNER_CHAMBER"),
|
|
lambda state: state.has("Ho-Oh Tile", world.player, 16))
|
|
|
|
# Route 32
|
|
route_32_access_rule = world.logic.has_route_32_condition()
|
|
if route_32_access_rule:
|
|
set_rule(get_entrance("REGION_ROUTE_32:NORTH -> REGION_ROUTE_32:SOUTH"), route_32_access_rule)
|
|
set_rule(get_entrance("REGION_ROUTE_32:SOUTH -> REGION_ROUTE_32:NORTH"), route_32_access_rule)
|
|
|
|
set_rule(get_location("Route 32 - Miracle Seed from Man in North"),
|
|
lambda state: world.logic.has_badge(state, "zephyr"))
|
|
set_rule(get_location("Route 32 - TM05 from Roar Guy"), can_cut)
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("FISHER_RALPH_ECRUTEAK",
|
|
lambda state: state.has("ENGINE_FLYPOINT_ECRUTEAK", world.player))
|
|
safe_set_location_rule("FISHER_RALPH_LAKE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_LAKE_OF_RAGE", world.player))
|
|
safe_set_location_rule("PICNICKER_LIZ_ECRUTEAK",
|
|
lambda state: state.has("ENGINE_FLYPOINT_ECRUTEAK", world.player))
|
|
safe_set_location_rule("PICNICKER_LIZ_ROCKETHQ",
|
|
lambda state: state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player))
|
|
safe_set_location_rule("PICNICKER_LIZ_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("FISHER_RALPH_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("PICNICKER_LIZ_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("FISHER_RALPH_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Union Cave
|
|
set_rule(get_entrance("REGION_UNION_CAVE_1F -> REGION_UNION_CAVE_B1F:SOUTH"), can_surf)
|
|
set_rule(get_entrance("REGION_UNION_CAVE_B1F -> REGION_UNION_CAVE_B1F:NORTH"), can_surf)
|
|
set_rule(get_entrance("REGION_UNION_CAVE_B1F:NORTH -> REGION_RUINS_OF_ALPH_OUTSIDE:SOUTH:UNION_LEDGE"),
|
|
can_strength)
|
|
set_rule(get_entrance("REGION_UNION_CAVE_B1F:SOUTH -> REGION_UNION_CAVE_B2F"), can_surf)
|
|
|
|
if "Union Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_32:SOUTH -> REGION_UNION_CAVE_1F"), can_flash)
|
|
set_rule(get_entrance("REGION_ROUTE_33 -> REGION_UNION_CAVE_1F"), can_flash)
|
|
set_rule(get_entrance("REGION_RUINS_OF_ALPH_OUTSIDE:TRAINER -> REGION_UNION_CAVE_B1F:NORTH"), can_flash)
|
|
|
|
# Route 33
|
|
if rematchsanity():
|
|
safe_set_location_rule("HIKER_ANTHONY_OLIVINE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_OLIVINE", world.player))
|
|
safe_set_location_rule("HIKER_ANTHONY_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("HIKER_ANTHONY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("HIKER_ANTHONY_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Azalea Town
|
|
if "Slowpoke Well" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_AZALEA_TOWN -> REGION_SLOWPOKE_WELL_B1F"), can_flash)
|
|
|
|
slowpoke_well_rule = lambda state: can_strength(state) and can_surf(state) and state.has(
|
|
"EVENT_CLEARED_SLOWPOKE_WELL", world.player)
|
|
|
|
set_rule(get_entrance("REGION_SLOWPOKE_WELL_B1F -> REGION_SLOWPOKE_WELL_B1F:WEST"),
|
|
slowpoke_well_rule)
|
|
|
|
set_rule(get_entrance("REGION_SLOWPOKE_WELL_B1F:WEST -> REGION_SLOWPOKE_WELL_B1F"), slowpoke_well_rule)
|
|
|
|
set_rule(get_entrance("REGION_AZALEA_TOWN -> REGION_AZALEA_GYM"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
safe_set_location_rule("Azalea Town - Rival",
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
set_rule(get_location("EVENT_BEAT_AZALEA_RIVAL"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
set_rule(get_location("Azalea Town - Lure Ball from Kurt"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
if Shopsanity.apricorns in world.options.shopsanity.value:
|
|
set_rule(get_entrance("REGION_KURTS_HOUSE -> REGION_MART_KURTS_BALLS"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
if world.options.randomize_berry_trees:
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Red Apricorn"),
|
|
lambda state: state.has("Red Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Grn Apricorn"),
|
|
lambda state: state.has("Grn Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Blu Apricorn"),
|
|
lambda state: state.has("Blu Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Ylw Apricorn"),
|
|
lambda state: state.has("Ylw Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Blk Apricorn"),
|
|
lambda state: state.has("Blk Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Wht Apricorn"),
|
|
lambda state: state.has("Wht Apricorn", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Pnk Apricorn"),
|
|
lambda state: state.has("Pnk Apricorn", world.player))
|
|
else:
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Red Apricorn"),
|
|
lambda state: state.has("EVENT_RED_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Grn Apricorn"),
|
|
lambda state: state.has("EVENT_GRN_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Blu Apricorn"),
|
|
lambda state: state.has("EVENT_BLU_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Ylw Apricorn"),
|
|
lambda state: state.has("EVENT_YLW_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Blk Apricorn"),
|
|
lambda state: state.has("EVENT_BLK_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Wht Apricorn"),
|
|
lambda state: state.has("EVENT_WHT_APRICORN", world.player))
|
|
set_rule(get_location("Azalea Town - Kurt's Ball Shop - Pnk Apricorn"),
|
|
lambda state: state.has("EVENT_PNK_APRICORN", world.player))
|
|
|
|
set_rule(get_location("Charcoal Kiln - Charcoal"), lambda state: state.has("EVENT_HERDED_FARFETCHD", world.player))
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("RIVAL_BAYLEEF_AZALEA"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
set_rule(get_location("RIVAL_CROCONAW_AZALEA"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
set_rule(get_location("RIVAL_QUILAVA_AZALEA"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
# Ilex Forest
|
|
|
|
if "Ilex Forest" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ILEX_FOREST_AZALEA_GATE -> REGION_ILEX_FOREST:SOUTH"), can_flash)
|
|
set_rule(get_entrance("REGION_ROUTE_34_ILEX_FOREST_GATE -> REGION_ILEX_FOREST:NORTH"), can_flash)
|
|
|
|
if not world.options.remove_ilex_cut_tree:
|
|
set_rule(get_entrance("REGION_ILEX_FOREST:NORTH -> REGION_ILEX_FOREST:SOUTH"), can_cut)
|
|
set_rule(get_entrance("REGION_ILEX_FOREST:SOUTH -> REGION_ILEX_FOREST:NORTH"), can_cut)
|
|
|
|
celebi_rule = lambda state: state.has("GS Ball", world.player) and state.has("EVENT_CLEARED_SLOWPOKE_WELL",
|
|
world.player)
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Celebi"), celebi_rule)
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Celebi_1"), celebi_rule)
|
|
|
|
set_rule(get_location("EVENT_HERDED_FARFETCHD"),
|
|
lambda state: state.has("EVENT_CLEARED_SLOWPOKE_WELL", world.player))
|
|
|
|
set_rule(get_location("Ilex Forest - HM01 from Farfetch'd Guy"),
|
|
lambda state: state.has("EVENT_HERDED_FARFETCHD", world.player))
|
|
|
|
# Route 34
|
|
set_rule(get_entrance("REGION_ROUTE_34 -> REGION_ROUTE_34:WATER"), can_surf)
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("PICNICKER_GINA_MAHOGANY",
|
|
lambda state: state.has("ENGINE_FLYPOINT_MAHOGANY", world.player))
|
|
safe_set_location_rule("PICNICKER_GINA_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
safe_set_location_rule("CAMPER_TODD_CIANWOOD",
|
|
lambda state: state.has("ENGINE_FLYPOINT_CIANWOOD", world.player))
|
|
safe_set_location_rule("CAMPER_TODD_BLACKTHORN",
|
|
lambda state: state.has("ENGINE_FLYPOINT_BLACKTHORN", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("CAMPER_TODD_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("PICNICKER_GINA_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("CAMPER_TODD_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("PICNICKER_GINA_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 34 - Leaf Stone from Gina"),
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player) and can_phone_call(state))
|
|
|
|
# Goldenrod City
|
|
set_rule(get_location("Goldenrod City - Squirtbottle from Flower Shop"),
|
|
lambda state: world.logic.has_badge(state, "plain"))
|
|
set_rule(get_location("Goldenrod City - Post-E4 GS Ball from Trade Corner Receptionist"),
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Eevee"), lambda state: state.has("EVENT_MET_BILL", world.player))
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Eevee_1"), lambda state: state.has("EVENT_MET_BILL", world.player))
|
|
|
|
if Shopsanity.johto_marts in world.options.shopsanity.value:
|
|
set_rule(get_entrance("REGION_GOLDENROD_DEPT_STORE_ROOF -> REGION_MART_ROOFTOP_SALE"),
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
if world.options.magnet_train_access:
|
|
rule = lambda state: state.has("Pass", world.player) and state.has("EVENT_RESTORED_POWER_TO_KANTO",
|
|
world.player)
|
|
else:
|
|
rule = lambda state: state.has("Pass", world.player)
|
|
set_rule(get_entrance("REGION_GOLDENROD_MAGNET_TRAIN_STATION -> REGION_SAFFRON_MAGNET_TRAIN_STATION"), rule)
|
|
|
|
set_rule(get_location("Goldenrod City - Exchange Eon Mail in Pokecenter"),
|
|
lambda state: state.has("EVENT_GOT_EON_MAIL_FROM_EUSINE", world.player))
|
|
|
|
# Underground
|
|
|
|
if "Goldenrod Underground" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_GOLDENROD_CITY -> REGION_GOLDENROD_UNDERGROUND"), can_flash)
|
|
set_rule(get_entrance("REGION_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES -> REGION_GOLDENROD_UNDERGROUND"),
|
|
can_flash)
|
|
|
|
set_rule(get_entrance("REGION_GOLDENROD_UNDERGROUND -> REGION_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES"),
|
|
lambda state: state.has("Basement Key", world.player))
|
|
|
|
set_rule(get_entrance("REGION_GOLDENROD_DEPT_STORE_B1F -> REGION_GOLDENROD_DEPT_STORE_B1F:WAREHOUSE"),
|
|
lambda state: state.has("Card Key", world.player))
|
|
|
|
set_rule(get_entrance("REGION_GOLDENROD_DEPT_STORE_B1F:WAREHOUSE -> REGION_GOLDENROD_DEPT_STORE_B1F"),
|
|
lambda state: state.has("Card Key", world.player))
|
|
|
|
has_rockets_requirement = world.logic.has_rockets_requirement()
|
|
|
|
set_rule(get_entrance("REGION_GOLDENROD_UNDERGROUND_WAREHOUSE -> REGION_GOLDENROD_UNDERGROUND_WAREHOUSE:TAKEOVER"),
|
|
has_rockets_requirement)
|
|
|
|
set_rule(get_entrance(
|
|
"REGION_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES -> REGION_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES:TAKEOVER"),
|
|
has_rockets_requirement)
|
|
|
|
if Shopsanity.game_corners in world.options.shopsanity.value:
|
|
set_rule(get_entrance("REGION_GOLDENROD_GAME_CORNER -> REGION_MART_GOLDENROD_GAME_CORNER"),
|
|
lambda state: state.has("Coin Case", world.player))
|
|
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_GoldenrodGameCorner1_1"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("Static_GoldenrodGameCorner2_1"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("Static_GoldenrodGameCorner3_1"), lambda state: state.has("Coin Case", world.player))
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("GoldenrodGameCorner1"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("GoldenrodGameCorner2"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("GoldenrodGameCorner2"), lambda state: state.has("Coin Case", world.player))
|
|
|
|
# Radio Tower
|
|
set_rule(get_entrance("REGION_RADIO_TOWER_2F -> REGION_RADIO_TOWER_2F:TAKEOVER"), has_rockets_requirement)
|
|
|
|
if Shopsanity.blue_card in world.options.shopsanity.value:
|
|
set_rule(get_entrance("REGION_RADIO_TOWER_2F -> REGION_MART_BLUE_CARD"),
|
|
lambda state: state.has("Blue Card", world.player))
|
|
|
|
blue_card_points = (2, 2, 3, 3, 5, 5, 5, 5, 5)
|
|
|
|
for i, points in enumerate(blue_card_points):
|
|
slot_name = get_mart_slot_location_name("MART_BLUE_CARD", i)
|
|
set_rule(get_location(f"Radio Tower 2F - Blue Card Shop - {slot_name}"),
|
|
lambda state, num_points=points: state.has("Blue Card Point", world.player, count=num_points))
|
|
|
|
set_rule(get_entrance("REGION_RADIO_TOWER_3F:NOCARDKEY -> REGION_RADIO_TOWER_3F:CARDKEY"),
|
|
lambda state: state.has("Card Key", world.player))
|
|
|
|
set_rule(get_location("Radio Tower 3F - TM11 from Woman"),
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
set_rule(get_location("Radio Tower 4F - Pink Bow from Mary"),
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("GRUNTM_3"), has_rockets_requirement)
|
|
|
|
safe_set_location_rule("Radio Tower 1F - Grunt", has_rockets_requirement)
|
|
|
|
# Route 35
|
|
set_rule(get_location("Route 35 - HP Up after delivering Kenya"),
|
|
lambda state: state.has("EVENT_GAVE_KENYA", world.player))
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_35 -> REGION_ROUTE_35:FRUITTREE"), can_surf)
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("BUG_CATCHER_ARNIE_LAKE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_LAKE_OF_RAGE", world.player))
|
|
safe_set_location_rule("BUG_CATCHER_ARNIE_BLACKTHORN",
|
|
lambda state: state.has("ENGINE_FLYPOINT_BLACKTHORN", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("BUG_CATCHER_ARNIE_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("BUG_CATCHER_ARNIE_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# National Park
|
|
if world.options.national_park_access.value == NationalParkAccess.option_bicycle:
|
|
set_rule(get_entrance("REGION_ROUTE_35_NATIONAL_PARK_GATE -> REGION_ROUTE_35_NATIONAL_PARK_GATE:BIKE"),
|
|
lambda state: state.has("Bicycle", world.player))
|
|
set_rule(get_entrance("REGION_ROUTE_36_NATIONAL_PARK_GATE -> REGION_NATIONAL_PARK"),
|
|
lambda state: state.has("Bicycle", world.player))
|
|
set_rule(get_entrance("REGION_ROUTE_36_NATIONAL_PARK_GATE -> REGION_NATIONAL_PARK:CONTEST"),
|
|
lambda state: state.has("Bicycle", world.player))
|
|
|
|
if world.options.randomize_phone_call_items and world.options.randomize_pokemon_requests:
|
|
request_pokemon = world.generated_request_pokemon[5]
|
|
set_rule(get_location("National Park - Nugget from Beverly"),
|
|
lambda state, request=request_pokemon: can_phone_call(state)
|
|
and state.has(request, world.player)
|
|
and has_pokedex(state))
|
|
|
|
if "Bug Catching Contest" not in world.options.wild_encounter_methods_required and world.is_universal_tracker:
|
|
for i in range(len(world.generated_contest)):
|
|
set_rule(get_location(f"Bug Catching Contest Slot {i + 1}"),
|
|
lambda state: state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("SCHOOLBOY_JACK_OLIVINE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_OLIVINE", world.player))
|
|
safe_set_location_rule("SCHOOLBOY_JACK_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("SCHOOLBOY_JACK_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("SCHOOLBOY_JACK_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Sudowoodo
|
|
has_squirtbottle = lambda state: state.has("Squirtbottle", world.player)
|
|
set_rule(get_entrance("REGION_ROUTE_36:EAST -> REGION_ROUTE_37"), has_squirtbottle)
|
|
set_rule(get_entrance("REGION_ROUTE_36:EAST -> REGION_ROUTE_36:WEST"), has_squirtbottle)
|
|
set_rule(get_entrance("REGION_ROUTE_36:WEST -> REGION_ROUTE_36:EAST"), has_squirtbottle)
|
|
set_rule(get_entrance("REGION_ROUTE_36:WEST -> REGION_ROUTE_37"), has_squirtbottle)
|
|
set_rule(get_entrance("REGION_ROUTE_37 -> REGION_ROUTE_36:EAST"), has_squirtbottle)
|
|
set_rule(get_entrance("REGION_ROUTE_37 -> REGION_ROUTE_36:WEST"), has_squirtbottle)
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Sudowoodo"), has_squirtbottle)
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Sudowoodo_1"), has_squirtbottle)
|
|
|
|
# Route 36
|
|
set_rule(get_entrance("REGION_ROUTE_35 -> REGION_ROUTE_36:WEST"), can_cut)
|
|
set_rule(get_entrance("REGION_ROUTE_36:WEST -> REGION_ROUTE_35"), can_cut)
|
|
|
|
set_rule(get_location("EVENT_SAW_SUICUNE_ON_ROUTE_36"),
|
|
lambda state: state.has("EVENT_RELEASED_THE_BEASTS", world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("SCHOOLBOY_ALAN_OLIVINE",
|
|
lambda state: state.has("ENGINE_FLYPOINT_OLIVINE", world.player))
|
|
safe_set_location_rule("SCHOOLBOY_ALAN_BLACKTHORN",
|
|
lambda state: state.has("ENGINE_FLYPOINT_BLACKTHORN", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("SCHOOLBOY_ALAN_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("SCHOOLBOY_ALAN_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 36 - Fire Stone from Alan"), can_phone_call)
|
|
|
|
set_rule(get_location("Route 36 - TM08 from Rock Smash Guy"), has_squirtbottle)
|
|
|
|
# Ecruteak City
|
|
set_rule(get_entrance("REGION_ECRUTEAK_CITY -> REGION_ECRUTEAK_GYM"),
|
|
lambda state: state.has("EVENT_BURNED_TOWER_MORTY", world.player))
|
|
|
|
if "Burned Tower" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ECRUTEAK_CITY -> REGION_BURNED_TOWER_1F"), can_flash)
|
|
|
|
set_rule(get_location("Burned Tower 1F - Item"), can_rock_smash)
|
|
set_rule(get_location("Burned Tower B1F - Item"), can_strength)
|
|
|
|
set_rule(get_entrance("REGION_ECRUTEAK_TIN_TOWER_ENTRANCE -> REGION_WISE_TRIOS_ROOM"),
|
|
lambda state: state.has("Clear Bell", world.player))
|
|
set_rule(get_entrance("REGION_TIN_TOWER_1F -> REGION_TIN_TOWER_2F"),
|
|
lambda state: state.has("Rainbow Wing", world.player))
|
|
|
|
set_rule(get_location("EVENT_FOUGHT_HO_OH"), lambda state: state.has("Rainbow Wing", world.player))
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Ho_Oh"), lambda state: state.has("Rainbow Wing", world.player))
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Ho_Oh_1"), lambda state: state.has("Rainbow Wing", world.player))
|
|
|
|
set_rule(get_location("Tin Tower 1F - Rainbow Wing"),
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
set_rule(get_location("EVENT_GOT_EON_MAIL_FROM_EUSINE"), lambda state: state.has_all(
|
|
("EVENT_SAW_SUICUNE_ON_ROUTE_36", "EVENT_SAW_SUICUNE_ON_ROUTE_42", "EVENT_SAW_SUICUNE_AT_CIANWOOD_CITY"),
|
|
world.player))
|
|
|
|
# Route 38
|
|
if rematchsanity():
|
|
safe_set_location_rule("SCHOOLBOY_CHAD_MAHOGANY",
|
|
lambda state: state.has("ENGINE_FLYPOINT_MAHOGANY", world.player))
|
|
safe_set_location_rule("SCHOOLBOY_CHAD_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
safe_set_location_rule("LASS_DANA_CIANWOOD",
|
|
lambda state: state.has("ENGINE_FLYPOINT_CIANWOOD", world.player))
|
|
safe_set_location_rule("LASS_DANA_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("SCHOOLBOY_CHAD_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("LASS_DANA_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("SCHOOLBOY_CHAD_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("LASS_DANA_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 38 - Thunderstone from Dana"), can_phone_call)
|
|
|
|
# Route 39
|
|
if world.options.randomize_phone_call_items and world.options.randomize_pokemon_requests:
|
|
request_pokemon = world.generated_request_pokemon[6]
|
|
set_rule(get_location("Route 39 - Nugget from Derek"),
|
|
lambda state, request=request_pokemon: can_phone_call(state)
|
|
and state.has(request, world.player)
|
|
and has_pokedex(state))
|
|
|
|
# Olivine City
|
|
set_rule(get_location("EVENT_JASMINE_RETURNED_TO_GYM"), lambda state: state.has("Secretpotion", world.player))
|
|
|
|
if "Olivine Lighthouse" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_OLIVINE_CITY -> REGION_OLIVINE_LIGHTHOUSE_1F"), can_flash)
|
|
|
|
if not world.options.johto_only and world.options.randomize_phone_call_items:
|
|
set_rule(get_entrance("REGION_OLIVINE_LIGHTHOUSE_2F -> REGION_OLIVINE_LIGHTHOUSE_2F:POWER"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player) and can_phone_call(state))
|
|
|
|
if not johto_only():
|
|
|
|
if world.options.ss_aqua_access:
|
|
ship_rule = lambda state: state.has("S.S. Ticket", world.player) and state.has(
|
|
"EVENT_JASMINE_RETURNED_TO_GYM", world.player)
|
|
else:
|
|
ship_rule = lambda state: state.has("S.S. Ticket", world.player)
|
|
|
|
set_rule(get_entrance("REGION_OLIVINE_PORT -> REGION_FAST_SHIP_1F"), ship_rule)
|
|
|
|
set_rule(get_entrance("REGION_FAST_SHIP_1F -> REGION_OLIVINE_PORT"),
|
|
lambda state: state.has("EVENT_FAST_SHIP_LAZY_SAILOR", world.player))
|
|
|
|
if hidden():
|
|
set_rule(get_location("Olivine Port - Hidden Item in Buoy"),
|
|
lambda state: ship_rule(state) and can_surf(state))
|
|
|
|
set_rule(get_entrance("REGION_OLIVINE_GYM -> REGION_OLIVINE_GYM:JASMINE"),
|
|
lambda state: state.has("EVENT_JASMINE_RETURNED_TO_GYM", world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("SAILOR_HUEY_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("SAILOR_HUEY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("SAILOR_HUEY_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Route 40
|
|
set_rule(get_entrance("REGION_ROUTE_40 -> REGION_ROUTE_40:WATER"), can_surf)
|
|
|
|
if hidden():
|
|
set_rule(get_location("Route 40 - Hidden Item in Rock"), can_rock_smash)
|
|
|
|
# Route 41
|
|
if hidden():
|
|
set_rule(get_location("Route 41 - Hidden Item on Southwest Island"), can_surf_and_whirlpool)
|
|
if "Whirl Islands" in world.options.dark_areas:
|
|
whirl_access = lambda state: can_surf_and_whirlpool(state) and can_flash(state)
|
|
else:
|
|
whirl_access = lambda state: can_surf_and_whirlpool(state)
|
|
set_rule(get_entrance("REGION_ROUTE_41 -> REGION_WHIRL_ISLAND_NW"), whirl_access)
|
|
set_rule(get_entrance("REGION_ROUTE_41 -> REGION_WHIRL_ISLAND_NE"), whirl_access)
|
|
set_rule(get_entrance("REGION_ROUTE_41 -> REGION_WHIRL_ISLAND_SW"), whirl_access)
|
|
set_rule(get_entrance("REGION_ROUTE_41 -> REGION_WHIRL_ISLAND_SE"), whirl_access)
|
|
|
|
set_rule(get_location("EVENT_FOUGHT_LUGIA"), lambda state: state.has("Silver Wing", world.player))
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Lugia"), lambda state: state.has("Silver Wing", world.player))
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Lugia_1"), lambda state: state.has("Silver Wing", world.player))
|
|
|
|
# Cianwood
|
|
set_rule(get_entrance("REGION_CIANWOOD_CITY -> REGION_ROUTE_41"), can_surf)
|
|
if hidden():
|
|
set_rule(get_location("Cianwood City - Hidden Item in West Rock"), can_rock_smash)
|
|
set_rule(get_location("Cianwood City - Hidden Item in North Rock"), can_rock_smash)
|
|
|
|
set_rule(get_location("Cianwood City - HM02 from Chuck's Wife"),
|
|
lambda state: world.logic.has_beaten_gym(state, "chuck"))
|
|
|
|
set_rule(get_entrance("REGION_CIANWOOD_GYM -> REGION_CIANWOOD_GYM:STRENGTH"), can_strength)
|
|
|
|
safe_set_location_rule("Cianwood City - Mysticalman Eusine",
|
|
lambda state: state.has("EVENT_RELEASED_THE_BEASTS", world.player))
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("MYSTICALMAN_EUSINE"),
|
|
lambda state: state.has("EVENT_RELEASED_THE_BEASTS", world.player))
|
|
|
|
set_rule(get_location("EVENT_SAW_SUICUNE_AT_CIANWOOD_CITY"),
|
|
lambda state: state.has("EVENT_RELEASED_THE_BEASTS", world.player))
|
|
|
|
# Route 42
|
|
if world.options.route_42_access.value == Route42Access.option_vanilla:
|
|
set_rule(get_entrance("REGION_ROUTE_42:WEST -> REGION_ROUTE_42:CENTER"), can_surf)
|
|
set_rule(get_entrance("REGION_ROUTE_42:CENTER -> REGION_ROUTE_42:WEST"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_42:EAST -> REGION_ROUTE_42:CENTER"), can_surf)
|
|
set_rule(get_entrance("REGION_ROUTE_42:CENTER -> REGION_ROUTE_42:EAST"), can_surf)
|
|
elif world.options.route_42_access.value in \
|
|
(Route42Access.option_whirlpool, Route42Access.option_whirlpool_open_mortar):
|
|
set_rule(get_entrance("REGION_ROUTE_42:WEST -> REGION_ROUTE_42:CENTER"), can_surf_and_whirlpool)
|
|
set_rule(get_entrance("REGION_ROUTE_42:CENTER -> REGION_ROUTE_42:WEST"), can_surf_and_whirlpool)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_42:EAST -> REGION_ROUTE_42:CENTER"), can_surf_and_whirlpool)
|
|
set_rule(get_entrance("REGION_ROUTE_42:CENTER -> REGION_ROUTE_42:EAST"), can_surf_and_whirlpool)
|
|
# else: blocked -> connection doesn't even exist
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_42:CENTER -> REGION_ROUTE_42:CENTERFRUIT"), can_cut)
|
|
|
|
west_to_headbutt = get_entrance("REGION_ROUTE_42:WEST -> REGION_ROUTE_42:HEADBUTT")
|
|
set_rule(west_to_headbutt, lambda state: state.can_reach_region("REGION_ROUTE_42:CENTERFRUIT", world.player))
|
|
world.multiworld.register_indirect_condition(world.get_region("REGION_ROUTE_42:CENTERFRUIT"), west_to_headbutt)
|
|
|
|
center_to_headbutt = get_entrance("REGION_ROUTE_42:CENTERFRUIT -> REGION_ROUTE_42:HEADBUTT")
|
|
set_rule(center_to_headbutt, lambda state: state.can_reach_region("REGION_ROUTE_42:WEST", world.player))
|
|
world.multiworld.register_indirect_condition(world.get_region("REGION_ROUTE_42:WEST"), center_to_headbutt)
|
|
|
|
set_rule(get_location("EVENT_SAW_SUICUNE_ON_ROUTE_42"),
|
|
lambda state: state.has("EVENT_RELEASED_THE_BEASTS", world.player))
|
|
|
|
if hidden():
|
|
set_rule(get_location("Route 42 - Hidden Item in Pond Rock"), can_surf)
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("FISHER_TULLY_ROCKETHQ",
|
|
lambda state: state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("FISHER_TULLY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("FISHER_TULLY_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 42 - Water Stone from Tully"), can_phone_call)
|
|
|
|
# Mt Mortar
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:CENTER -> REGION_MOUNT_MORTAR_2F_OUTSIDE"),
|
|
can_surf_and_waterfall)
|
|
|
|
if world.options.mount_mortar_access:
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:WEST:ENTRANCE -> REGION_MOUNT_MORTAR_1F_OUTSIDE:WEST"),
|
|
can_rock_smash)
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:WEST -> REGION_MOUNT_MORTAR_1F_OUTSIDE:WEST:ENTRANCE"),
|
|
can_rock_smash)
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:EAST:ENTRANCE -> REGION_MOUNT_MORTAR_1F_OUTSIDE:EAST"),
|
|
can_rock_smash)
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:EAST -> REGION_MOUNT_MORTAR_1F_OUTSIDE:EAST:ENTRANCE"),
|
|
can_rock_smash)
|
|
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:CENTER -> REGION_MOUNT_MORTAR_1F_OUTSIDE:BELOW_WATERFALL"),
|
|
can_surf)
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:BELOW_WATERFALL -> REGION_MOUNT_MORTAR_1F_OUTSIDE:CENTER"),
|
|
can_surf)
|
|
|
|
# 1F Inside Front
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_INSIDE:FRONT -> REGION_MOUNT_MORTAR_1F_INSIDE:STRENGTH"),
|
|
can_strength)
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_INSIDE:STRENGTH -> REGION_MOUNT_MORTAR_1F_INSIDE:FRONT"),
|
|
can_strength)
|
|
|
|
# 1F C -> B1F Everything needs surf so im being lazy
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:CENTER -> REGION_MOUNT_MORTAR_B1F"), can_surf)
|
|
|
|
# Behind boulder, need to come down from 2F for this
|
|
set_rule(get_entrance("REGION_MOUNT_MORTAR_B1F:BACK -> REGION_MOUNT_MORTAR_B1F"),
|
|
lambda state: can_strength(state) and can_surf_and_waterfall(state))
|
|
|
|
if "Mount Mortar" in world.options.dark_areas:
|
|
add_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:WEST -> REGION_MOUNT_MORTAR_1F_INSIDE:FRONT"), can_flash)
|
|
add_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:EAST -> REGION_MOUNT_MORTAR_1F_INSIDE:FRONT"), can_flash)
|
|
add_rule(get_entrance("REGION_MOUNT_MORTAR_2F_OUTSIDE -> REGION_MOUNT_MORTAR_2F_INSIDE"), can_flash)
|
|
add_rule(get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:CENTER -> REGION_MOUNT_MORTAR_B1F"), can_flash)
|
|
|
|
if world.options.route_42_access.value in (Route42Access.option_blocked,
|
|
Route42Access.option_whirlpool_open_mortar):
|
|
set_rule(
|
|
get_entrance("REGION_MOUNT_MORTAR_1F_OUTSIDE:BELOW_WATERFALL -> REGION_MOUNT_MORTAR_1F_INSIDE:FRONT"),
|
|
can_flash)
|
|
|
|
# Mahogany Town
|
|
if Shopsanity.johto_marts in world.options.shopsanity.value:
|
|
set_rule(get_entrance("REGION_MAHOGANY_MART_1F -> REGION_MART_MAHOGANY_2"),
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
set_rule(get_entrance("REGION_MAHOGANY_TOWN -> REGION_MAHOGANY_GYM"),
|
|
lambda state: state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player))
|
|
|
|
set_rule(get_entrance("REGION_MAHOGANY_MART_1F -> REGION_TEAM_ROCKET_BASE_B1F"),
|
|
lambda state: state.has("EVENT_DECIDED_TO_HELP_LANCE", world.player))
|
|
|
|
has_route_44_access = world.logic.has_route_44_access()
|
|
|
|
set_rule(get_entrance("REGION_MAHOGANY_TOWN -> REGION_ROUTE_44"), has_route_44_access)
|
|
if (not (
|
|
world.options.randomize_fly_unlocks and world.options.remote_items)
|
|
and world.options.fly_cheese == FlyCheese.option_in_logic):
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_MAHOGANY_TOWN"),
|
|
lambda state: has_route_44_access(state) or can_fly(state))
|
|
elif (not (world.options.randomize_fly_unlocks and world.options.remote_items)
|
|
and world.options.fly_cheese == FlyCheese.option_out_of_logic and world.is_universal_tracker):
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_MAHOGANY_TOWN"),
|
|
lambda state: has_route_44_access(state) or (
|
|
state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player) and can_fly(state)))
|
|
else:
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_MAHOGANY_TOWN"), has_route_44_access)
|
|
|
|
if not world.options.johto_only and world.options.randomize_phone_call_items:
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_ROUTE_44:POWER"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player) and can_phone_call(state))
|
|
|
|
# Route 43
|
|
set_rule(get_entrance("REGION_ROUTE_43 -> REGION_ROUTE_43:FRUITTREE"),
|
|
lambda state: can_cut(state) and can_surf(state))
|
|
|
|
set_rule(get_location("Route 43 - TM36 from Guard in Gate"),
|
|
lambda state: state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player))
|
|
|
|
if rematchsanity():
|
|
safe_set_location_rule("POKEMANIAC_BRENT_ROCKETHQ",
|
|
lambda state: state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player))
|
|
safe_set_location_rule("PICNICKER_TIFFANY_RADIO",
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("POKEMANIAC_BRENT_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("PICNICKER_TIFFANY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("POKEMANIAC_BRENT_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("PICNICKER_TIFFANY_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if world.options.randomize_phone_call_items and world.options.randomize_pokemon_requests:
|
|
request_pokemon = world.generated_request_pokemon[7]
|
|
set_rule(get_location("Route 43 - Pink Bow from Tiffany"),
|
|
lambda state, request=request_pokemon: can_phone_call(state)
|
|
and state.has(request, world.player)
|
|
and has_pokedex(state))
|
|
|
|
# Lake of Rage
|
|
if world.options.red_gyarados_access == RedGyaradosAccess.option_whirlpool:
|
|
set_rule(get_entrance("REGION_LAKE_OF_RAGE -> REGION_LAKE_OF_RAGE:WATER"), can_surf_and_whirlpool)
|
|
elif world.options.red_gyarados_access == RedGyaradosAccess.option_vanilla:
|
|
set_rule(get_entrance("REGION_LAKE_OF_RAGE -> REGION_LAKE_OF_RAGE:WATER"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_LAKE_OF_RAGE -> REGION_LAKE_OF_RAGE:CUT"), can_cut)
|
|
|
|
if world.options.randomize_pokemon_requests:
|
|
set_rule(get_location("Lake of Rage - Magikarp Prize"),
|
|
lambda state: state.has("MAGIKARP", world.player)
|
|
and state.has("EVENT_CLEARED_ROCKET_HIDEOUT", world.player)
|
|
and has_pokedex(state))
|
|
|
|
# Route 44
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_ROUTE_44:WATER"), can_surf)
|
|
|
|
if rematchsanity():
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("BIRD_KEEPER_VANCE_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("FISHER_WILTON_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("BIRD_KEEPER_VANCE_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("FISHER_WILTON_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 44 - Poke Ball from Wilton"), can_phone_call)
|
|
|
|
# Ice Path
|
|
|
|
if "Ice Path" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_44 -> REGION_ICE_PATH_1F:WEST"), can_flash)
|
|
set_rule(get_entrance("REGION_BLACKTHORN_CITY -> REGION_ICE_PATH_1F:EAST"), can_flash)
|
|
set_rule(get_entrance("REGION_ICE_PATH_B2F_MAHOGANY_SIDE -> REGION_ICE_PATH_B2F_MAHOGANY_SIDE:MIDDLE"),
|
|
can_strength)
|
|
|
|
# Blackthorn
|
|
set_rule(get_entrance("REGION_BLACKTHORN_CITY -> REGION_BLACKTHORN_GYM_1F"),
|
|
lambda state: state.has("EVENT_CLEARED_RADIO_TOWER", world.player))
|
|
|
|
set_rule(get_entrance("REGION_BLACKTHORN_GYM_2F -> REGION_BLACKTHORN_GYM_1F:STRENGTH"), can_strength)
|
|
|
|
if "Dragons Den" in world.options.dark_areas:
|
|
dragons_den_access = lambda state: world.logic.has_beaten_gym(state, "clair") and can_flash(state)
|
|
else:
|
|
dragons_den_access = lambda state: world.logic.has_beaten_gym(state, "clair")
|
|
|
|
set_rule(get_entrance("REGION_BLACKTHORN_CITY:DRAGONS_DEN_ENTRANCE -> REGION_DRAGONS_DEN_1F"), dragons_den_access)
|
|
set_rule(get_entrance("REGION_BLACKTHORN_CITY -> REGION_BLACKTHORN_CITY:DRAGONS_DEN_ENTRANCE"), can_surf)
|
|
set_rule(get_entrance("REGION_BLACKTHORN_CITY:DRAGONS_DEN_ENTRANCE -> REGION_BLACKTHORN_CITY"), can_surf)
|
|
|
|
# Dragons Den
|
|
set_rule(get_entrance("REGION_DRAGONS_DEN_B1F -> REGION_DRAGONS_DEN_B1F:WATER"), can_surf)
|
|
set_rule(get_entrance("REGION_DRAGONS_DEN_B1F:WATER -> REGION_DRAGONS_DEN_B1F:WHIRLPOOL"), can_surf_and_whirlpool)
|
|
|
|
# Route 45
|
|
if hidden():
|
|
set_rule(get_location("Route 45 - Hidden Item in Southeast Pond"), can_surf)
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 45 - PP Up from Kenji"), can_phone_call)
|
|
|
|
if not world.options.johto_only:
|
|
set_rule(get_entrance("REGION_ROUTE_45 -> REGION_ROUTE_45:POWER"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player) and can_phone_call(state))
|
|
|
|
if rematchsanity():
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("HIKER_PARRY_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("HIKER_PARRY_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Route 46
|
|
if world.options.randomize_phone_call_items and not world.options.johto_only:
|
|
set_rule(get_location("Route 46 - Calcium from Erin"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player) and can_phone_call(state))
|
|
|
|
if rematchsanity():
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("PICNICKER_ERIN_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("PICNICKER_ERIN_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if not world.options.johto_only and world.options.randomize_phone_call_items:
|
|
set_rule(get_entrance("REGION_ROUTE_45 -> REGION_ROUTE_45:POWER"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player) and can_phone_call(state))
|
|
|
|
# Route 26
|
|
if rematchsanity():
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("COOLTRAINERM_GAVEN_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("COOLTRAINERF_BETH_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("COOLTRAINERM_GAVEN_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("COOLTRAINERF_BETH_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Route 27
|
|
set_rule(get_entrance("REGION_ROUTE_27:WEST -> REGION_NEW_BARK_TOWN"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_27:WEST -> REGION_ROUTE_27:WESTWATER"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_27:CENTER -> REGION_ROUTE_27:EAST"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_27:EAST -> REGION_ROUTE_27:CENTER"), can_surf)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_27:EAST -> REGION_ROUTE_27:EASTWHIRLPOOL"), can_surf_and_whirlpool)
|
|
|
|
set_rule(get_location("Route 27 - West Item across Water"), can_surf)
|
|
|
|
if world.options.randomize_phone_call_items:
|
|
set_rule(get_location("Route 27 - Star Piece from Jose"), can_phone_call)
|
|
|
|
if rematchsanity():
|
|
if world.options.goal == Goal.option_red:
|
|
safe_set_location_rule("BIRD_KEEPER_JOSE_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
safe_set_location_rule("COOLTRAINERF_REENA_CHAMPION",
|
|
lambda state: state.has("EVENT_BEAT_ELITE_FOUR", world.player))
|
|
|
|
if not johto_only():
|
|
safe_set_location_rule("BIRD_KEEPER_JOSE_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
safe_set_location_rule("COOLTRAINERF_REENA_POWER",
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
set_rule(get_location("Tohjo Falls - Item"), can_surf)
|
|
|
|
if "Tohjo Falls" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_27:WEST -> REGION_TOHJO_FALLS:WEST"), can_flash)
|
|
set_rule(get_entrance("REGION_ROUTE_27:CENTER -> REGION_TOHJO_FALLS:EAST"), can_flash)
|
|
set_rule(get_entrance("REGION_TOHJO_FALLS:WEST -> REGION_TOHJO_FALLS:EAST"), can_surf_and_waterfall)
|
|
set_rule(get_entrance("REGION_TOHJO_FALLS:EAST -> REGION_TOHJO_FALLS:WEST"), can_surf_and_waterfall)
|
|
|
|
# Victory Road
|
|
has_e4_requirement = world.logic.has_elite_four_requirement()
|
|
if "Victory Road" in world.options.dark_areas:
|
|
vroad_access = lambda state: can_flash(state) and has_e4_requirement(state)
|
|
else:
|
|
vroad_access = has_e4_requirement
|
|
|
|
set_rule(get_entrance("REGION_VICTORY_ROAD_GATE -> REGION_VICTORY_ROAD:ENTRANCE"), vroad_access)
|
|
|
|
if world.options.victory_road_access:
|
|
set_rule(get_entrance("REGION_VICTORY_ROAD:ENTRANCE -> REGION_VICTORY_ROAD"), can_strength)
|
|
|
|
if "Victory Road" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_23 -> REGION_VICTORY_ROAD"), can_flash)
|
|
|
|
if johto_only() != JohtoOnly.option_on:
|
|
has_mt_silver_requirement = world.logic.has_mt_silver_requirement()
|
|
set_rule(get_entrance("REGION_ROUTE_28 -> REGION_VICTORY_ROAD_GATE"), has_mt_silver_requirement)
|
|
set_rule(get_entrance("REGION_VICTORY_ROAD_GATE -> REGION_ROUTE_28"), has_mt_silver_requirement)
|
|
set_rule(get_location("EVENT_OPENED_MT_SILVER"), has_mt_silver_requirement)
|
|
|
|
set_rule(get_location("EVENT_BEAT_RED"), world.logic.has_red_requirement())
|
|
# set_rule(get_location("RED_1"), has_red_requirement)
|
|
|
|
# Route 28
|
|
set_rule(get_location("Route 28 - TM47 from Celebrity in House"), can_cut)
|
|
if hidden():
|
|
set_rule(get_location("Route 28 - Hidden Item behind Cut Tree"), can_cut)
|
|
|
|
# Silver Cave
|
|
if "Silver Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_SILVER_CAVE_OUTSIDE -> REGION_SILVER_CAVE_ROOM_1"), can_flash)
|
|
|
|
set_rule(get_entrance("REGION_SILVER_CAVE_OUTSIDE -> REGION_SILVER_CAVE_OUTSIDE:SURF"), can_surf)
|
|
set_rule(get_entrance("REGION_SILVER_CAVE_OUTSIDE:SURF -> REGION_SILVER_CAVE_OUTSIDE"), can_surf)
|
|
|
|
set_rule(get_location("Silver Cave 2F - Northeast Item"), can_surf_and_waterfall)
|
|
set_rule(get_location("Silver Cave 2F - West Item"), can_surf_and_waterfall)
|
|
|
|
set_rule(get_entrance("REGION_SILVER_CAVE_ROOM_2 -> REGION_SILVER_CAVE_ITEM_ROOMS"), can_surf_and_waterfall)
|
|
|
|
if not johto_only():
|
|
|
|
has_kanto_access_requirement = world.logic.has_kanto_access_requirement()
|
|
set_rule(get_entrance("REGION_ROUTE_22 -> REGION_VICTORY_ROAD_GATE"), has_kanto_access_requirement)
|
|
set_rule(get_entrance("REGION_VICTORY_ROAD_GATE -> REGION_ROUTE_22"), has_kanto_access_requirement)
|
|
|
|
set_rule(get_entrance("REGION_INDIGO_PLATEAU_POKECENTER_1F -> REGION_INDIGO_PLATEAU_POKECENTER_1F:RIVAL"),
|
|
lambda state: state.has("EVENT_BEAT_RIVAL_IN_MT_MOON", world.player))
|
|
|
|
# Viridian
|
|
set_rule(get_location("Viridian City - TM42 from Sleepy Guy"),
|
|
lambda state: can_surf_kanto(state) or can_cut_kanto(state))
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_VIRIDIAN_CITY -> REGION_VIRIDIAN_GYM"), kanto_gyms_access)
|
|
set_rule(get_entrance("REGION_TRAINER_HOUSE_B1F -> REGION_TRAINER_HOUSE_B1F:CAL"), kanto_gyms_access)
|
|
|
|
set_rule(get_entrance("REGION_VIRIDIAN_GYM -> REGION_VIRIDIAN_GYM:BLUE"),
|
|
lambda state: state.has("EVENT_VIRIDIAN_GYM_BLUE", world.player))
|
|
|
|
# Route 2
|
|
if world.options.route_2_access.value != Route2Access.option_open:
|
|
set_rule(get_entrance("REGION_ROUTE_2:WEST -> REGION_ROUTE_2:NORTHEAST"), can_cut_kanto)
|
|
if world.options.route_2_access.value == Route2Access.option_vanilla:
|
|
set_rule(get_entrance("REGION_ROUTE_2:NORTHEAST -> REGION_ROUTE_2:WEST"), can_cut_kanto)
|
|
|
|
if "Digletts Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_2:NORTHEAST -> REGION_DIGLETTS_CAVE"), can_flash_kanto)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_2:WEST -> REGION_ROUTE_2:SOUTHEAST"), can_cut_kanto)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_2:SOUTHEAST -> REGION_ROUTE_2:WEST"), can_cut_kanto)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_2:SOUTHEAST -> REGION_ROUTE_2:NORTHEAST"), can_cut_kanto)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_2:NORTHEAST -> REGION_ROUTE_2:SOUTHEAST"), can_cut_kanto)
|
|
|
|
# Pewter City
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_PEWTER_CITY -> REGION_PEWTER_GYM"), kanto_gyms_access)
|
|
|
|
# Route 3
|
|
if world.options.route_3_access.value == Route3Access.option_boulder_badge:
|
|
set_rule(get_entrance("REGION_PEWTER_CITY -> REGION_ROUTE_3"),
|
|
lambda state: world.logic.has_badge(state, "boulder"))
|
|
set_rule(get_entrance("REGION_ROUTE_3 -> REGION_PEWTER_CITY"),
|
|
lambda state: world.logic.has_badge(state, "boulder"))
|
|
|
|
if hidden():
|
|
set_rule(get_location("Mount Moon Square - Hidden Item under Rock"), can_rock_smash)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_4:WEST -> REGION_ROUTE_4:EAST"),
|
|
lambda state: state.has("EVENT_CLEARED_ROUTE_4", world.player))
|
|
|
|
if "Mount Moon" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_3 -> REGION_MOUNT_MOON"), can_flash_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_4:WEST -> REGION_MOUNT_MOON"), can_flash_kanto)
|
|
set_rule(get_entrance("REGION_MOUNT_MOON_SQUARE -> REGION_MOUNT_MOON"), can_flash_kanto)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
add_rule(get_entrance("REGION_ROUTE_3 -> REGION_MOUNT_MOON"), kanto_gyms_access)
|
|
add_rule(get_entrance("REGION_ROUTE_4:WEST -> REGION_MOUNT_MOON"), kanto_gyms_access)
|
|
add_rule(get_entrance("REGION_MOUNT_MOON_SQUARE -> REGION_MOUNT_MOON"), kanto_gyms_access)
|
|
|
|
# Cerulean
|
|
set_rule(get_entrance("REGION_ROUTE_24 -> REGION_CERULEAN_CITY:SURF"), can_surf_kanto)
|
|
safe_set_location_rule("Route 24 - Grunt",
|
|
lambda state: state.has("EVENT_CERULEAN_GYM_ROCKET", world.player))
|
|
|
|
set_rule(get_entrance("REGION_CERULEAN_CITY -> REGION_ROUTE_9"), can_cut_kanto)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_CERULEAN_CITY -> REGION_CERULEAN_GYM"), kanto_gyms_access)
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_9 -> REGION_CERULEAN_CITY"), can_cut_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_9 -> REGION_ROUTE_10_NORTH:SURF"), can_surf_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_10_NORTH:SURF -> REGION_ROUTE_9"), can_surf_kanto)
|
|
|
|
# Route 25
|
|
set_rule(get_location("Route 25 - Item behind Cut Tree"), can_cut_kanto)
|
|
|
|
# Power Plant
|
|
set_rule(get_location("EVENT_RESTORED_POWER_TO_KANTO"), lambda state: state.has("Machine Part", world.player))
|
|
|
|
set_rule(get_location("Power Plant - TM07 from Manager"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Rock Tunnel
|
|
if "Rock Tunnel" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_ROUTE_9 -> REGION_ROCK_TUNNEL_1F"), can_flash_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_10_SOUTH -> REGION_ROCK_TUNNEL_1F"), can_flash_kanto)
|
|
|
|
# Lavender
|
|
if world.options.randomize_pokegear:
|
|
set_rule(get_location("Lavender Radio Tower - EXPN Card"), lambda state: state.has(
|
|
"EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
else:
|
|
set_rule(get_location("EVENT_GOT_EXPN_CARD"), lambda state: state.has(
|
|
"EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Route 12
|
|
if world.options.route_12_access:
|
|
set_rule(get_entrance("REGION_ROUTE_12:NORTH -> REGION_ROUTE_12:SOUTH"),
|
|
lambda state: state.has("Squirtbottle", world.player) or can_surf_kanto(state))
|
|
set_rule(get_entrance("REGION_ROUTE_12:SOUTH -> REGION_ROUTE_12:NORTH"),
|
|
lambda state: state.has("Squirtbottle", world.player) or can_surf_kanto(state))
|
|
|
|
set_rule(get_location("Route 12 - Item behind North Cut Tree"), can_cut_kanto)
|
|
|
|
set_rule(get_location("Route 12 - Item behind South Cut Tree across Water"),
|
|
lambda state: can_cut_kanto(state) and can_surf_kanto(state))
|
|
|
|
if hidden():
|
|
set_rule(get_location("Route 12 - Hidden Item on Island"), can_surf_kanto)
|
|
|
|
# Route 13
|
|
set_rule(get_entrance("REGION_ROUTE_13 -> REGION_ROUTE_13:CUT"), can_cut_kanto)
|
|
|
|
# Route 14
|
|
set_rule(get_entrance("REGION_ROUTE_14 -> REGION_ROUTE_14:CUT"), can_cut_kanto)
|
|
|
|
# Vermilion
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_VERMILION_CITY -> REGION_VERMILION_GYM"),
|
|
lambda state: (can_cut_kanto(state) or can_surf_kanto(state)) and kanto_gyms_access(state))
|
|
else:
|
|
set_rule(get_entrance("REGION_VERMILION_CITY -> REGION_VERMILION_GYM"),
|
|
lambda state: can_cut_kanto(state) or can_surf_kanto(state))
|
|
|
|
kanto_badges = list(world.logic.badge_items.values())[8:]
|
|
set_rule(get_location("Vermilion City - HP Up from Man nowhere near PokeCenter"),
|
|
lambda state: state.has_all(kanto_badges, world.player))
|
|
|
|
set_rule(get_location("Vermilion City - Lost Item from Guy in Fan Club"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
has_expn = world.logic.has_expn()
|
|
set_rule(get_location("EVENT_FOUGHT_SNORLAX"), has_expn)
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("Snorlax"), has_expn)
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_Snorlax_1"), has_expn)
|
|
|
|
set_rule(get_entrance("REGION_VERMILION_CITY -> REGION_ROUTE_11"), has_expn)
|
|
set_rule(get_entrance("REGION_VERMILION_CITY -> REGION_VERMILION_CITY:DIGLETTS_CAVE_ENTRANCE"), has_expn)
|
|
|
|
if "Digletts Cave" in world.options.dark_areas:
|
|
set_rule(get_entrance("REGION_VERMILION_CITY:DIGLETTS_CAVE_ENTRANCE -> REGION_DIGLETTS_CAVE"),
|
|
can_flash_kanto)
|
|
|
|
if (not (
|
|
world.options.randomize_fly_unlocks and world.options.remote_items)
|
|
and world.options.fly_cheese == FlyCheese.option_in_logic):
|
|
digletts_cave_rule = lambda state: has_expn(state) or can_fly(state)
|
|
elif (not (world.options.randomize_fly_unlocks and world.options.remote_items)
|
|
and world.options.fly_cheese == FlyCheese.option_out_of_logic and world.is_universal_tracker):
|
|
digletts_cave_rule = lambda state: has_expn(state) or (
|
|
state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player) and can_fly(state))
|
|
else:
|
|
digletts_cave_rule = has_expn
|
|
|
|
set_rule(get_entrance("REGION_VERMILION_CITY:DIGLETTS_CAVE_ENTRANCE -> REGION_VERMILION_CITY"),
|
|
digletts_cave_rule)
|
|
set_rule(get_entrance("REGION_ROUTE_11 -> REGION_VERMILION_CITY"), digletts_cave_rule)
|
|
set_rule(get_entrance("REGION_VERMILION_PORT -> REGION_FAST_SHIP_1F"), ship_rule)
|
|
|
|
if hidden():
|
|
set_rule(get_location("Vermilion Port - Hidden Item in Buoy"),
|
|
lambda state: ship_rule(state) and can_surf_kanto(state))
|
|
|
|
set_rule(get_entrance("REGION_FAST_SHIP_1F -> REGION_VERMILION_PORT"),
|
|
lambda state: state.has("EVENT_FAST_SHIP_LAZY_SAILOR", world.player))
|
|
|
|
# Saffron
|
|
set_rule(get_location("Copycat's House - Pass from Copycat"),
|
|
lambda state: state.has("Lost Item", world.player))
|
|
|
|
if world.options.magnet_train_access:
|
|
rule = lambda state: state.has("Pass", world.player) and state.has("EVENT_RESTORED_POWER_TO_KANTO",
|
|
world.player)
|
|
else:
|
|
rule = lambda state: state.has("Pass", world.player)
|
|
set_rule(get_entrance("REGION_SAFFRON_MAGNET_TRAIN_STATION -> REGION_GOLDENROD_MAGNET_TRAIN_STATION"), rule)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_SAFFRON_CITY -> REGION_SAFFRON_GYM"), kanto_gyms_access)
|
|
|
|
has_tea = world.logic.has_tea()
|
|
|
|
if "North" in world.options.saffron_gatehouse_tea.value:
|
|
set_rule(get_entrance("REGION_SAFFRON_CITY -> REGION_ROUTE_5_SAFFRON_GATE"), has_tea)
|
|
set_rule(get_entrance("REGION_ROUTE_5_SAFFRON_GATE -> REGION_SAFFRON_CITY"), has_tea)
|
|
|
|
if "East" in world.options.saffron_gatehouse_tea.value:
|
|
set_rule(get_entrance("REGION_SAFFRON_CITY -> REGION_ROUTE_8_SAFFRON_GATE"), has_tea)
|
|
set_rule(get_entrance("REGION_ROUTE_8_SAFFRON_GATE -> REGION_SAFFRON_CITY"), has_tea)
|
|
|
|
if "South" in world.options.saffron_gatehouse_tea.value:
|
|
set_rule(get_entrance("REGION_SAFFRON_CITY -> REGION_ROUTE_6_SAFFRON_GATE"), has_tea)
|
|
set_rule(get_entrance("REGION_ROUTE_6_SAFFRON_GATE -> REGION_SAFFRON_CITY"), has_tea)
|
|
|
|
if "West" in world.options.saffron_gatehouse_tea.value:
|
|
set_rule(get_entrance("REGION_SAFFRON_CITY -> REGION_ROUTE_7_SAFFRON_GATE"), has_tea)
|
|
set_rule(get_entrance("REGION_ROUTE_7_SAFFRON_GATE -> REGION_SAFFRON_CITY"), has_tea)
|
|
|
|
# Underground Paths
|
|
if world.options.undergrounds_require_power.value in (UndergroundsRequirePower.option_north_south,
|
|
UndergroundsRequirePower.option_both):
|
|
set_rule(get_entrance("REGION_ROUTE_5 -> REGION_ROUTE_5_UNDERGROUND_PATH_ENTRANCE"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_6 -> REGION_ROUTE_6_UNDERGROUND_PATH_ENTRANCE"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
if (world.options.east_west_underground
|
|
and world.options.undergrounds_require_power.value in (
|
|
UndergroundsRequirePower.option_east_west,
|
|
UndergroundsRequirePower.option_both)):
|
|
set_rule(get_entrance("REGION_ROUTE_7 -> REGION_ROUTE_8"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_8 -> REGION_ROUTE_7"),
|
|
lambda state: state.has("EVENT_RESTORED_POWER_TO_KANTO", world.player))
|
|
|
|
# Route 8
|
|
set_rule(get_entrance("REGION_ROUTE_8 -> REGION_ROUTE_8:CUT"), can_cut_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_8:CUT -> REGION_ROUTE_8"), can_cut_kanto)
|
|
|
|
# Celadon
|
|
set_rule(get_entrance("REGION_CELADON_CITY -> REGION_CELADON_CITY:GYM_ENTRANCE"), can_cut_kanto)
|
|
set_rule(get_entrance("REGION_CELADON_CITY:GYM_ENTRANCE -> REGION_CELADON_CITY"), can_cut_kanto)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_CELADON_CITY:GYM_ENTRANCE -> REGION_CELADON_GYM"), kanto_gyms_access)
|
|
|
|
if Shopsanity.game_corners in world.options.shopsanity.value:
|
|
set_rule(
|
|
get_entrance("REGION_CELADON_GAME_CORNER_PRIZE_ROOM -> REGION_MART_CELADON_GAME_CORNER_PRIZE_ROOM"),
|
|
lambda state: state.has("Coin Case", world.player))
|
|
|
|
if world.options.static_pokemon_required:
|
|
set_rule(get_location("Static_CeladonGameCornerPrizeRoom1_1"),
|
|
lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("Static_CeladonGameCornerPrizeRoom2_1"),
|
|
lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("Static_CeladonGameCornerPrizeRoom3_1"),
|
|
lambda state: state.has("Coin Case", world.player))
|
|
|
|
if world.options.level_scaling:
|
|
set_rule(get_location("CeladonGameCornerPrizeRoom1"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("CeladonGameCornerPrizeRoom2"), lambda state: state.has("Coin Case", world.player))
|
|
set_rule(get_location("CeladonGameCornerPrizeRoom3"), lambda state: state.has("Coin Case", world.player))
|
|
|
|
diploma_count = len(world.logic.available_pokemon) if not world.is_universal_tracker else world.ut_slot_data[
|
|
"logically_available_pokemon_count"]
|
|
set_rule(get_location("EVENT_OBTAINED_DIPLOMA"),
|
|
lambda state: world.logic.has_n_pokemon(state, diploma_count))
|
|
|
|
# Route 16
|
|
set_rule(get_entrance("REGION_ROUTE_16 -> REGION_ROUTE_16:CUT"), can_cut_kanto)
|
|
set_rule(get_entrance("REGION_ROUTE_16:CUT -> REGION_ROUTE_16"), can_cut_kanto)
|
|
|
|
# Cycling Road
|
|
set_rule(get_entrance("REGION_ROUTE_16 -> REGION_ROUTE_17"), lambda state: state.has("Bicycle", world.player))
|
|
|
|
set_rule(get_entrance("REGION_ROUTE_17_ROUTE_18_GATE -> REGION_ROUTE_17"),
|
|
lambda state: state.has("Bicycle", world.player))
|
|
|
|
# Route 15
|
|
set_rule(get_location("Route 15 - Item"), can_cut_kanto)
|
|
|
|
# Fuchsia City
|
|
set_rule(get_entrance("REGION_FUCHSIA_CITY -> REGION_FUCHSIA_CITY:CUT"), can_cut_kanto)
|
|
set_rule(get_entrance("REGION_FUCHSIA_CITY:CUT -> REGION_FUCHSIA_CITY"), can_cut_kanto)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_FUCHSIA_CITY -> REGION_FUCHSIA_GYM"), kanto_gyms_access)
|
|
|
|
if world.options.south_kanto_condition == SouthKantoCondition.option_enter_south_kanto:
|
|
south_kanto_condition = "EVENT_CINNABAR_ROCKS_CLEARED"
|
|
else:
|
|
south_kanto_condition = "EVENT_RESTORED_POWER_TO_KANTO"
|
|
|
|
if world.options.south_kanto_access == SouthKantoAccess.option_route_19:
|
|
set_rule(get_entrance("REGION_ROUTE_19:GATE_ENTRANCE -> REGION_ROUTE_19"),
|
|
lambda state: state.has(south_kanto_condition, world.player))
|
|
if world.options.south_kanto_condition != SouthKantoCondition.option_enter_south_kanto:
|
|
set_rule(get_entrance("REGION_ROUTE_19 -> REGION_ROUTE_19:GATE_ENTRANCE"),
|
|
lambda state: state.has(south_kanto_condition, world.player))
|
|
elif world.options.south_kanto_access == SouthKantoAccess.option_route_21:
|
|
set_rule(get_entrance("REGION_ROUTE_21:NORTH -> REGION_ROUTE_21:SOUTH"),
|
|
lambda state: state.has(south_kanto_condition, world.player))
|
|
if world.options.south_kanto_condition != SouthKantoCondition.option_enter_south_kanto:
|
|
set_rule(get_entrance("REGION_ROUTE_21:SOUTH -> REGION_ROUTE_21:NORTH"),
|
|
lambda state: state.has(south_kanto_condition, world.player))
|
|
|
|
add_rule(get_entrance("REGION_ROUTE_19:GATE_ENTRANCE -> REGION_ROUTE_19"), can_surf_kanto)
|
|
|
|
|
|
# Cinnabar
|
|
set_rule(get_entrance("REGION_CINNABAR_ISLAND -> REGION_ROUTE_20"), can_surf_kanto)
|
|
set_rule(get_entrance("REGION_CINNABAR_ISLAND -> REGION_ROUTE_21:SOUTH"), can_surf_kanto)
|
|
set_rule(get_entrance("REGION_PALLET_TOWN -> REGION_ROUTE_21:NORTH"), can_surf_kanto)
|
|
|
|
if world.options.lock_kanto_gyms:
|
|
set_rule(get_entrance("REGION_ROUTE_20 -> REGION_SEAFOAM_GYM"), kanto_gyms_access)
|
|
|
|
if world.options.randomize_pokemon_requests:
|
|
bills_grandpa_locations = (
|
|
"Bill's House - Everstone from Bill's Grandpa",
|
|
"Bill's House - Leaf Stone from Bill's Grandpa",
|
|
"Bill's House - Water Stone from Bill's Grandpa",
|
|
"Bill's House - Fire Stone from Bill's Grandpa",
|
|
"Bill's House - Thunderstone from Bill's Grandpa"
|
|
)
|
|
|
|
for i, location in enumerate(bills_grandpa_locations):
|
|
required_pokemon = world.generated_request_pokemon[:i + 1]
|
|
set_rule(get_location(location),
|
|
lambda state, pokemon=required_pokemon: state.has_all(pokemon, world.player) and has_pokedex(
|
|
state))
|
|
|
|
if world.options.goal == Goal.option_unown_hunt:
|
|
for location, unown in world.generated_unown_signs.items():
|
|
chamber_event = get_chamber_event_for_unown(unown)
|
|
set_rule(get_location(location),
|
|
lambda state, event=chamber_event: state.has(event, world.player))
|
|
set_rule(get_location(f"{location}_Encounter"),
|
|
lambda state, event=chamber_event: state.has(event, world.player))
|
|
|
|
for trade_id, trade in world.generated_trades.items():
|
|
safe_set_location_rule(
|
|
trade_id,
|
|
lambda state, request=trade.requested_pokemon: (state.has(request, world.player) and has_pokedex(state)))
|
|
|
|
if world.options.require_itemfinder:
|
|
if world.options.require_itemfinder == RequireItemfinder.option_logically_required and world.is_universal_tracker:
|
|
rule = lambda state: state.has("Itemfinder", world.player) or state.has(
|
|
PokemonCrystalGlitchedToken.TOKEN_NAME, world.player)
|
|
else:
|
|
rule = lambda state: state.has("Itemfinder", world.player)
|
|
|
|
for location in world.multiworld.get_locations(world.player):
|
|
if "Hidden" in location.tags:
|
|
add_rule(location, rule)
|
|
|
|
if world.options.grasssanity:
|
|
for region in world.get_regions():
|
|
if region.name in data.grass_tiles:
|
|
region_data = data.regions[region.name]
|
|
rule = can_cut if region_data.johto or region_data.silver_cave else can_cut_kanto
|
|
add_rule(get_entrance(f"{region.name} -> {region.name}:GRASS"), rule)
|
|
|
|
if world.options.dexsanity or world.options.dexcountsanity:
|
|
set_rule(get_entrance("Menu -> Pokedex"), has_pokedex)
|
|
|
|
for pokemon_id in world.generated_dexsanity:
|
|
pokemon_data = world.generated_pokemon[pokemon_id]
|
|
set_rule(get_location(f"Pokedex - {pokemon_data.friendly_name}"),
|
|
lambda state, species_id=pokemon_id: state.has(species_id, world.player))
|
|
|
|
logically_available_pokemon_count = len(world.logic.available_pokemon) if not world.is_universal_tracker else \
|
|
world.ut_slot_data["logically_available_pokemon_count"]
|
|
|
|
for dexcountsanity_count in world.generated_dexcountsanity[:-1]:
|
|
logical_count = min(logically_available_pokemon_count,
|
|
dexcountsanity_count + world.options.dexcountsanity_leniency)
|
|
set_rule(get_location(f"Pokedex - Catch {dexcountsanity_count} Pokemon"),
|
|
lambda state, count=logical_count: world.logic.has_n_pokemon(state, count))
|
|
|
|
if world.generated_dexcountsanity:
|
|
logical_count = min(logically_available_pokemon_count,
|
|
world.generated_dexcountsanity[-1] + world.options.dexcountsanity_leniency)
|
|
set_rule(get_location("Pokedex - Final Catch"),
|
|
lambda state, count=logical_count: world.logic.has_n_pokemon(state, count))
|
|
|
|
for encounter_key, encounter_access in world.logic.wild_regions.items():
|
|
|
|
if encounter_access is LogicalAccess.Inaccessible: continue
|
|
if encounter_access is LogicalAccess.OutOfLogic and not world.is_universal_tracker: continue
|
|
|
|
rule = None
|
|
|
|
if encounter_key.encounter_type is EncounterType.Water:
|
|
region = world.get_region(encounter_key.region_name())
|
|
parent_region = region.entrances[0].parent_region
|
|
region_data = data.regions[parent_region.name]
|
|
rule = can_surf if (region_data.johto or region_data.silver_cave) else can_surf_kanto
|
|
elif encounter_key.encounter_type is EncounterType.Fish:
|
|
rule = world.logic.fishing_rod_rules[encounter_key.fishing_rod]
|
|
elif encounter_key.encounter_type is EncounterType.Tree:
|
|
rule = can_headbutt
|
|
elif encounter_key.encounter_type is EncounterType.RockSmash:
|
|
rule = can_rock_smash
|
|
elif encounter_key.encounter_type is EncounterType.Static:
|
|
if not world.is_universal_tracker: continue
|
|
|
|
location = get_location(f"{encounter_key.region_name()}_1")
|
|
if encounter_access is LogicalAccess.OutOfLogic:
|
|
add_rule(location, lambda state: state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player))
|
|
continue
|
|
|
|
region_name = encounter_key.region_name()
|
|
for i, encounter in enumerate(world.generated_wild[encounter_key]):
|
|
location = get_location(f"{region_name}_{i + 1}")
|
|
|
|
if rule:
|
|
set_rule(location, rule)
|
|
|
|
if encounter.pokemon == "UNOWN":
|
|
add_rule(location, lambda state: state.has_any(unown_unlocks, world.player))
|
|
|
|
if encounter_access is LogicalAccess.OutOfLogic:
|
|
add_rule(location, lambda state: state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player))
|
|
|
|
def evolution_logic(state: CollectionState, evolved_from: str,
|
|
evolutions: list[tuple[EvolutionData, LogicalAccess]]) -> bool:
|
|
if not state.has(evolved_from, world.player): return False
|
|
|
|
for evo, access in evolutions:
|
|
if (access is LogicalAccess.OutOfLogic
|
|
and not state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player)): continue
|
|
ool = access is LogicalAccess.OutOfLogic
|
|
if evo.evo_type is EvolutionType.Level:
|
|
if (world.logic.has_beaten_n_gyms(
|
|
state, ((evo.level - 1) // world.options.evolution_gym_levels) + 1) or ool): return True
|
|
elif evo.evo_type is EvolutionType.Stats:
|
|
if (state.has_any(evolution_item_unlocks, world.player)
|
|
and world.logic.has_beaten_n_gyms(
|
|
state, ((evo.level - 1) // world.options.evolution_gym_levels) + 1) or ool): return True
|
|
elif evo.evo_type is EvolutionType.Item:
|
|
if state.has_any(evolution_item_unlocks, world.player): return True
|
|
elif evo.evo_type is EvolutionType.Happiness:
|
|
if state.has_any(happiness_unlocks, world.player) or ool: return True
|
|
|
|
return False
|
|
|
|
locations_to_evolutions = defaultdict[str, list[tuple[EvolutionData, LogicalAccess]]](list)
|
|
locations_to_pokemon = dict[str, str]()
|
|
|
|
for evolvee, evolutions in world.logic.evolution.items():
|
|
for evolution, logical_access in evolutions:
|
|
if not world.is_universal_tracker and logical_access is LogicalAccess.OutOfLogic: continue
|
|
location_name = evolution_location_name(world, evolvee, evolution.pokemon)
|
|
locations_to_pokemon[location_name] = evolvee
|
|
locations_to_evolutions[location_name].append((evolution, logical_access))
|
|
|
|
for location_name, evo_data in locations_to_evolutions.items():
|
|
evolves_from = locations_to_pokemon[location_name]
|
|
set_rule(
|
|
get_location(location_name),
|
|
lambda state, from_pokemon=evolves_from, evolutions=evo_data:
|
|
evolution_logic(state, from_pokemon, evolutions)
|
|
)
|
|
|
|
def breeding_logic(state: CollectionState, breeders_access: list[tuple[str, LogicalAccess, bool]]) -> bool:
|
|
for breeder_access in breeders_access:
|
|
breeder, access, requires_ditto = breeder_access
|
|
if state.has(breeder, world.player) and ((not requires_ditto) or state.has("DITTO", world.player)):
|
|
if access is LogicalAccess.InLogic:
|
|
return True
|
|
elif (access is LogicalAccess.OutOfLogic
|
|
and state.has(PokemonCrystalGlitchedToken.TOKEN_NAME, world.player)):
|
|
return True
|
|
return False
|
|
|
|
if world.options.breeding_methods_required or world.is_universal_tracker:
|
|
set_rule(get_entrance("Menu -> Breeding"), lambda state: state.has("EVENT_UNLOCKED_DAY_CARE", world.player))
|
|
|
|
if world.options.breeding_methods_required == BreedingMethodsRequired.option_with_ditto:
|
|
add_rule(get_entrance("Menu -> Breeding"),
|
|
lambda state: state.has("DITTO", world.player) or state.has(PokemonCrystalGlitchedToken.TOKEN_NAME,
|
|
world.player))
|
|
|
|
for base_form_id, breeders in world.logic.breeding.items():
|
|
logical_access = [access for _, access, _ in breeders]
|
|
if not world.is_universal_tracker and (LogicalAccess.InLogic not in logical_access): continue
|
|
set_rule(
|
|
get_location(f"Hatch {world.generated_pokemon[base_form_id].friendly_name}"),
|
|
lambda state, b=breeders: breeding_logic(state, b)
|
|
)
|
|
|
|
|
|
def verify_hm_accessibility(world: "PokemonCrystalWorld") -> None:
|
|
if world.options.field_moves_always_usable: return
|
|
|
|
logic = world.logic
|
|
|
|
def can_use_hm(state: CollectionState, hm: str) -> bool:
|
|
if hm == "CUT":
|
|
return logic.can_cut()(state) or logic.can_cut(True)(state)
|
|
elif hm == "FLY":
|
|
return logic.can_fly()(state)
|
|
elif hm == "SURF":
|
|
return logic.can_surf()(state) or logic.can_surf(True)(state)
|
|
elif hm == "STRENGTH":
|
|
return logic.can_strength()(state) or logic.can_strength(True)(state)
|
|
elif hm == "FLASH":
|
|
return logic.can_flash(allow_ool=False)(state) or logic.can_flash(True, allow_ool=False)(state)
|
|
elif hm == "WHIRLPOOL":
|
|
return logic.can_whirlpool()(state) or logic.can_whirlpool(True)(state)
|
|
elif hm == "WATERFALL":
|
|
return logic.can_waterfall()(state) or logic.can_waterfall(True)(state)
|
|
elif hm == "HEADBUTT":
|
|
return logic.can_headbutt()(state)
|
|
elif hm == "ROCK_SMASH":
|
|
return logic.can_rock_smash()(state)
|
|
return False
|
|
|
|
def do_verify(hms: list[str]):
|
|
hms_to_verify = hms.copy()
|
|
unverified_hms = []
|
|
last_hm = None
|
|
|
|
while hms_to_verify:
|
|
state = world.get_world_collection_state()
|
|
hm_to_verify = hms_to_verify[0]
|
|
if not can_use_hm(state, hm_to_verify):
|
|
|
|
if last_hm == hm_to_verify:
|
|
unverified_hms.append(hms_to_verify.pop(0))
|
|
continue
|
|
|
|
last_hm = hm_to_verify
|
|
logical_pokemon = sorted(logic.available_pokemon)
|
|
world.random.shuffle(logical_pokemon)
|
|
valid_pokemon = [mon for mon in logical_pokemon if state.has(mon, world.player)
|
|
and mon not in logic.compatible_hm_pokemon[hm_to_verify]]
|
|
if valid_pokemon:
|
|
pokemon = world.random.choice(valid_pokemon)
|
|
add_hm_compatibility(world, pokemon, hm_to_verify)
|
|
else:
|
|
hms_to_verify.pop(0)
|
|
|
|
if unverified_hms and unverified_hms == hms:
|
|
state = world.get_world_collection_state()
|
|
if any((logic.has_hm_badge_requirement(hm, False)(state)
|
|
or logic.has_hm_badge_requirement(hm, True)(state)) for hm in unverified_hms):
|
|
unverified_hms_list = ",".join(unverified_hms)
|
|
raise Exception(f"Failed to ensure access to {unverified_hms_list} for player {world.player}")
|
|
elif unverified_hms:
|
|
unverified_hms.reverse()
|
|
do_verify(unverified_hms)
|
|
|
|
hms = ["CUT", "FLY", "SURF", "STRENGTH", "FLASH", "WHIRLPOOL", "WATERFALL", "HEADBUTT", "ROCK_SMASH"]
|
|
world.random.shuffle(hms)
|
|
do_verify(hms)
|