Pokémon Red and Blue: Version 4 update (#1963)
## What is this fixing or adding? Adds a large number of new options, including: - Door Shuffle - Sphere-based level scaling - Key Item and Pokedex requirement options to reach the Elite Four - Split Card Key option - Dexsanity option can be set to a percentage of Pokémon that will be checks - Stonesanity: remove the stones from the Celadon Department Store and shuffle them into the item pool, replacing 4 of the 5 Moon Stone items - Sleep Trap items option - Randomize Move Types option - Town Map Fly Location option, to unlock a flight location when finding/receiving the Town Map Many enhancements have been made, including: - Game allows you to continue your save file _from Pallet Town_ as a way to save warp back to the beginning of the game. The one-way drop from Diglett's Cave to north Route 2 that had been added to the randomizer has been removed. - Client auto-hints some locations when you are able to see the item before you can obtain it (but would only show AP Item if it is for another player), including Bike Shop, Oak's Aides, Celadon Prize Corner, and the unchosen Fossil location. Various bugs have been fixed, including: - Route 13 wild Pokémon not correctly logically requiring Cut - Vanilla tm/hm compatibility options giving compatibility for many TMs/HMs erroneously - If an item that exists in multiple quantities in the item pool is chosen for one of the locations that are pre-filled with local items, it will continue placing that same item in the remaining locations as long as more of that item exist - `start_with` option for `randomize_pokedex` still shuffling a Pokédex into the item pool - The obedience threshold levels being incorrect with 0-2 badges, with Pokémon up to level 30 obeying with 0-1 badges and up to 10 with 2 badges - Receiving a DeathLink trigger in the Safari Zone causing issues. Now, you will have your steps remaining set to 0 instead of blacking out when you're in the Safari Zone. Many location names have been changed, as location names are automatically prepended using the Region name and a large number of areas have been split into new regions as part of the overhaul to add Door Shuffle.
This commit is contained in:
parent
cf8ac49f76
commit
85b92e2696
|
@ -29,6 +29,9 @@ for location in location_data:
|
|||
location_map[type(location.ram_address).__name__][location.ram_address.flag] = location.address
|
||||
location_bytes_bits[location.address] = {'byte': location.ram_address.byte, 'bit': location.ram_address.bit}
|
||||
|
||||
location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"
|
||||
and location.address is not None}
|
||||
|
||||
SYSTEM_MESSAGE_ID = 0
|
||||
|
||||
CONNECTION_TIMING_OUT_STATUS = "Connection timing out. Please restart your emulator, then restart pkmn_rb.lua"
|
||||
|
@ -72,6 +75,7 @@ class GBContext(CommonContext):
|
|||
self.items_handling = 0b001
|
||||
self.sent_release = False
|
||||
self.sent_collect = False
|
||||
self.auto_hints = set()
|
||||
|
||||
async def server_auth(self, password_requested: bool = False):
|
||||
if password_requested and not self.password:
|
||||
|
@ -153,6 +157,31 @@ async def parse_locations(data: List, ctx: GBContext):
|
|||
locations.append(loc_id)
|
||||
elif flags[flag_type][location_bytes_bits[loc_id]['byte']] & 1 << location_bytes_bits[loc_id]['bit']:
|
||||
locations.append(loc_id)
|
||||
|
||||
hints = []
|
||||
if flags["EventFlag"][280] & 16:
|
||||
hints.append("Cerulean Bicycle Shop")
|
||||
if flags["EventFlag"][280] & 32:
|
||||
hints.append("Route 2 Gate - Oak's Aide")
|
||||
if flags["EventFlag"][280] & 64:
|
||||
hints.append("Route 11 Gate 2F - Oak's Aide")
|
||||
if flags["EventFlag"][280] & 128:
|
||||
hints.append("Route 15 Gate 2F - Oak's Aide")
|
||||
if flags["EventFlag"][281] & 1:
|
||||
hints += ["Celadon Prize Corner - Item Prize 1", "Celadon Prize Corner - Item Prize 2",
|
||||
"Celadon Prize Corner - Item Prize 3"]
|
||||
if (location_name_to_id["Fossil - Choice A"] in ctx.checked_locations and location_name_to_id["Fossil - Choice B"]
|
||||
not in ctx.checked_locations):
|
||||
hints.append("Fossil - Choice B")
|
||||
elif (location_name_to_id["Fossil - Choice B"] in ctx.checked_locations and location_name_to_id["Fossil - Choice A"]
|
||||
not in ctx.checked_locations):
|
||||
hints.append("Fossil - Choice A")
|
||||
hints = [location_name_to_id[loc] for loc in hints if loc not in ctx.auto_hints and location_name_to_id[loc] in
|
||||
ctx.missing_locations and location_name_to_id[loc] not in ctx.locations_checked]
|
||||
if hints:
|
||||
await ctx.send_msgs([{"cmd": "LocationScouts", "locations": hints, "create_as_hint": 2}])
|
||||
ctx.auto_hints.update(hints)
|
||||
|
||||
if flags["EventFlag"][280] & 1 and not ctx.finished_game:
|
||||
await ctx.send_msgs([
|
||||
{"cmd": "StatusUpdate",
|
||||
|
|
1
setup.py
1
setup.py
|
@ -77,7 +77,6 @@ non_apworlds: set = {
|
|||
"Meritous",
|
||||
"Ocarina of Time",
|
||||
"Overcooked! 2",
|
||||
"Pokemon Red and Blue",
|
||||
"Raft",
|
||||
"Secret of Evermore",
|
||||
"Slay the Spire",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Alex "Alchav" Avery
|
||||
Copyright (c) 2022-2023 Alex "Alchav" Avery
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import os
|
||||
import settings
|
||||
import typing
|
||||
|
||||
import threading
|
||||
from copy import deepcopy
|
||||
from typing import TextIO
|
||||
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification, LocationProgressType
|
||||
from Fill import fill_restrictive, FillError, sweep_from_pool
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from ..generic.Rules import add_item_rule
|
||||
from .items import item_table, item_groups
|
||||
from .locations import location_data, PokemonRBLocation
|
||||
from .regions import create_regions
|
||||
from .logic import PokemonLogic
|
||||
from .options import pokemon_rb_options
|
||||
from .rom_addresses import rom_addresses
|
||||
from .text import encode_text
|
||||
from .rom import generate_output, get_base_rom_bytes, get_base_rom_path, process_pokemon_data, process_wild_pokemon,\
|
||||
process_static_pokemon, process_move_data, RedDeltaPatch, BlueDeltaPatch
|
||||
from .rom import generate_output, get_base_rom_bytes, get_base_rom_path, RedDeltaPatch, BlueDeltaPatch
|
||||
from .pokemon import process_pokemon_data, process_move_data
|
||||
from .encounters import process_pokemon_locations, process_trainer_data
|
||||
from .rules import set_rules
|
||||
|
||||
import worlds.pokemon_rb.poke_data as poke_data
|
||||
from .level_scaling import level_scaling
|
||||
from . import logic
|
||||
from . import poke_data
|
||||
|
||||
|
||||
class PokemonSettings(settings.Group):
|
||||
|
@ -67,10 +68,10 @@ class PokemonRedBlueWorld(World):
|
|||
option_definitions = pokemon_rb_options
|
||||
settings: typing.ClassVar[PokemonSettings]
|
||||
|
||||
data_version = 8
|
||||
data_version = 9
|
||||
required_client_version = (0, 3, 9)
|
||||
|
||||
topology_present = False
|
||||
topology_present = True
|
||||
|
||||
item_name_to_id = {name: data.id for name, data in item_table.items()}
|
||||
location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"
|
||||
|
@ -81,8 +82,12 @@ class PokemonRedBlueWorld(World):
|
|||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
super().__init__(world, player)
|
||||
self.item_pool = []
|
||||
self.total_key_items = None
|
||||
self.fly_map = None
|
||||
self.fly_map_code = None
|
||||
self.town_map_fly_map = None
|
||||
self.town_map_fly_map_code = None
|
||||
self.extra_badges = {}
|
||||
self.type_chart = None
|
||||
self.local_poke_data = None
|
||||
|
@ -94,6 +99,9 @@ class PokemonRedBlueWorld(World):
|
|||
self.type_chart = None
|
||||
self.traps = None
|
||||
self.trade_mons = {}
|
||||
self.finished_level_scaling = threading.Event()
|
||||
self.dexsanity_table = []
|
||||
self.local_locs = []
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
|
@ -125,11 +133,14 @@ class PokemonRedBlueWorld(World):
|
|||
if len(self.multiworld.player_name[self.player].encode()) > 16:
|
||||
raise Exception(f"Player name too long for {self.multiworld.get_player_name(self.player)}. Player name cannot exceed 16 bytes for Pokémon Red and Blue.")
|
||||
|
||||
if (self.multiworld.dexsanity[self.player] and self.multiworld.accessibility[self.player] == "locations"
|
||||
and (self.multiworld.catch_em_all[self.player] != "all_pokemon"
|
||||
or self.multiworld.randomize_wild_pokemon[self.player] == "vanilla"
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player] != "any")):
|
||||
self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("items")
|
||||
if not self.multiworld.badgesanity[self.player]:
|
||||
self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"]
|
||||
|
||||
if self.multiworld.key_items_only[self.player]:
|
||||
self.multiworld.trainersanity[self.player] = self.multiworld.trainersanity[self.player].from_text("off")
|
||||
self.multiworld.dexsanity[self.player] = self.multiworld.dexsanity[self.player].from_text("false")
|
||||
self.multiworld.randomize_hidden_items[self.player] = \
|
||||
self.multiworld.randomize_hidden_items[self.player].from_text("off")
|
||||
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value >= 2:
|
||||
badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"]
|
||||
|
@ -161,9 +172,14 @@ class PokemonRedBlueWorld(World):
|
|||
not_very_effectives = self.multiworld.not_very_effective_matchups[self.player].value
|
||||
normals = self.multiworld.normal_matchups[self.player].value
|
||||
while super_effectives + not_very_effectives + normals < 225 - immunities:
|
||||
super_effectives += self.multiworld.super_effective_matchups[self.player].value
|
||||
not_very_effectives += self.multiworld.not_very_effective_matchups[self.player].value
|
||||
normals += self.multiworld.normal_matchups[self.player].value
|
||||
if super_effectives == not_very_effectives == normals == 0:
|
||||
super_effectives = 225
|
||||
not_very_effectives = 225
|
||||
normals = 225
|
||||
else:
|
||||
super_effectives += self.multiworld.super_effective_matchups[self.player].value
|
||||
not_very_effectives += self.multiworld.not_very_effective_matchups[self.player].value
|
||||
normals += self.multiworld.normal_matchups[self.player].value
|
||||
if super_effectives + not_very_effectives + normals > 225 - immunities:
|
||||
total = super_effectives + not_very_effectives + normals
|
||||
excess = total - (225 - immunities)
|
||||
|
@ -209,143 +225,228 @@ class PokemonRedBlueWorld(World):
|
|||
# damage being reduced by 1 which leads to a "not very effective" message appearing due to my changes
|
||||
# to the way effectiveness messages are generated.
|
||||
self.type_chart = sorted(chart, key=lambda matchup: -matchup[2])
|
||||
self.multiworld.early_items[self.player]["Exp. All"] = 1
|
||||
|
||||
def create_items(self) -> None:
|
||||
start_inventory = self.multiworld.start_inventory[self.player].value.copy()
|
||||
if self.multiworld.randomize_pokedex[self.player] == "start_with":
|
||||
start_inventory["Pokedex"] = 1
|
||||
self.multiworld.push_precollected(self.create_item("Pokedex"))
|
||||
self.dexsanity_table = [
|
||||
*(True for _ in range(round(self.multiworld.dexsanity[self.player].value * 1.51))),
|
||||
*(False for _ in range(151 - round(self.multiworld.dexsanity[self.player].value * 1.51)))
|
||||
]
|
||||
self.multiworld.random.shuffle(self.dexsanity_table)
|
||||
|
||||
locations = [location for location in location_data if location.type == "Item"]
|
||||
item_pool = []
|
||||
combined_traps = (self.multiworld.poison_trap_weight[self.player].value
|
||||
+ self.multiworld.fire_trap_weight[self.player].value
|
||||
+ self.multiworld.paralyze_trap_weight[self.player].value
|
||||
+ self.multiworld.ice_trap_weight[self.player].value)
|
||||
for location in locations:
|
||||
event = location.event
|
||||
if not location.inclusion(self.multiworld, self.player):
|
||||
def create_items(self):
|
||||
self.multiworld.itempool += self.item_pool
|
||||
|
||||
@classmethod
|
||||
def stage_fill_hook(cls, multiworld, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
locs = []
|
||||
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
|
||||
locs += world.local_locs
|
||||
for loc in sorted(locs):
|
||||
if loc.item:
|
||||
continue
|
||||
if location.original_item in self.multiworld.start_inventory[self.player].value and \
|
||||
location.original_item in item_groups["Unique"]:
|
||||
start_inventory[location.original_item] -= 1
|
||||
item = self.create_filler()
|
||||
elif location.original_item is None:
|
||||
item = self.create_filler()
|
||||
elif location.original_item == "Pokedex":
|
||||
if self.multiworld.randomize_pokedex[self.player] == "vanilla":
|
||||
self.multiworld.get_location(location.name, self.player).event = True
|
||||
event = True
|
||||
item = self.create_item("Pokedex")
|
||||
elif location.original_item.startswith("TM"):
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
item = self.create_item(location.original_item.split(" ")[0])
|
||||
itempool = progitempool + usefulitempool + filleritempool
|
||||
multiworld.random.shuffle(itempool)
|
||||
unplaced_items = []
|
||||
for item in itempool:
|
||||
if item.player == loc.player and loc.can_fill(multiworld.state, item, False):
|
||||
if item in progitempool:
|
||||
progitempool.remove(item)
|
||||
elif item in usefulitempool:
|
||||
usefulitempool.remove(item)
|
||||
elif item in filleritempool:
|
||||
filleritempool.remove(item)
|
||||
if item.advancement:
|
||||
state = sweep_from_pool(multiworld.state, progitempool + unplaced_items)
|
||||
if (not item.advancement) or state.can_reach(loc, "Location", loc.player):
|
||||
multiworld.push_item(loc, item, False)
|
||||
loc.event = item.advancement
|
||||
fill_locations.remove(loc)
|
||||
break
|
||||
else:
|
||||
unplaced_items.append(item)
|
||||
progitempool += [item for item in unplaced_items if item.advancement]
|
||||
usefulitempool += [item for item in unplaced_items if item.useful]
|
||||
filleritempool += [item for item in unplaced_items if (not item.advancement) and (not item.useful)]
|
||||
|
||||
def fill_hook(self, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
if not self.multiworld.badgesanity[self.player]:
|
||||
# Door Shuffle options besides Simple place badges during door shuffling
|
||||
if not self.multiworld.door_shuffle[self.player] not in ("off", "simple"):
|
||||
badges = [item for item in progitempool if "Badge" in item.name and item.player == self.player]
|
||||
for badge in badges:
|
||||
self.multiworld.itempool.remove(badge)
|
||||
progitempool.remove(badge)
|
||||
for _ in range(5):
|
||||
badgelocs = [self.multiworld.get_location(loc, self.player) for loc in [
|
||||
"Pewter Gym - Brock Prize", "Cerulean Gym - Misty Prize",
|
||||
"Vermilion Gym - Lt. Surge Prize", "Celadon Gym - Erika Prize",
|
||||
"Fuchsia Gym - Koga Prize", "Saffron Gym - Sabrina Prize",
|
||||
"Cinnabar Gym - Blaine Prize", "Viridian Gym - Giovanni Prize"]]
|
||||
state = self.multiworld.get_all_state(False)
|
||||
self.multiworld.random.shuffle(badges)
|
||||
self.multiworld.random.shuffle(badgelocs)
|
||||
badgelocs_copy = badgelocs.copy()
|
||||
# allow_partial so that unplaced badges aren't lost, for debugging purposes
|
||||
fill_restrictive(self.multiworld, state, badgelocs_copy, badges, True, True, allow_partial=True)
|
||||
if badges:
|
||||
for location in badgelocs:
|
||||
if location.item:
|
||||
badges.append(location.item)
|
||||
location.item = None
|
||||
continue
|
||||
else:
|
||||
for location in badgelocs:
|
||||
if location.item:
|
||||
fill_locations.remove(location)
|
||||
break
|
||||
else:
|
||||
item = self.create_item(location.original_item)
|
||||
else:
|
||||
item = self.create_item(location.original_item)
|
||||
if (item.classification == ItemClassification.filler and self.multiworld.random.randint(1, 100)
|
||||
<= self.multiworld.trap_percentage[self.player].value and combined_traps != 0):
|
||||
item = self.create_item(self.select_trap())
|
||||
if event:
|
||||
self.multiworld.get_location(location.name, self.player).place_locked_item(item)
|
||||
elif "Badge" not in item.name or self.multiworld.badgesanity[self.player].value:
|
||||
item_pool.append(item)
|
||||
raise FillError(f"Failed to place badges for player {self.player}")
|
||||
|
||||
self.multiworld.random.shuffle(item_pool)
|
||||
if self.multiworld.key_items_only[self.player]:
|
||||
return
|
||||
|
||||
self.multiworld.itempool += item_pool
|
||||
tms = [item for item in usefulitempool + filleritempool if item.name.startswith("TM") and (item.player ==
|
||||
self.player or (item.player in self.multiworld.groups and self.player in
|
||||
self.multiworld.groups[item.player]["players"]))]
|
||||
if len(tms) > 7:
|
||||
for gym_leader in (("Pewter Gym", "Brock"), ("Cerulean Gym", "Misty"), ("Vermilion Gym", "Lt. Surge"),
|
||||
("Celadon Gym-C", "Erika"), ("Fuchsia Gym", "Koga"), ("Saffron Gym-C", "Sabrina"),
|
||||
("Cinnabar Gym", "Blaine"), ("Viridian Gym", "Giovanni")):
|
||||
loc = self.multiworld.get_location(f"{gym_leader[0].split('-')[0]} - {gym_leader[1]} TM",
|
||||
self.player)
|
||||
if loc.item:
|
||||
continue
|
||||
for party in self.multiworld.get_location(gym_leader[0] + " - Trainer Parties", self.player).party_data:
|
||||
if party["party_address"] == \
|
||||
f"Trainer_Party_{gym_leader[1].replace('. ', '').replace('Giovanni', 'Viridian_Gym_Giovanni')}_A":
|
||||
mon = party["party"][-1]
|
||||
learnable_tms = [tm for tm in tms if self.local_poke_data[mon]["tms"][
|
||||
int((int(tm.name[2:4]) - 1) / 8)] & 1 << ((int(tm.name[2:4]) - 1) % 8)]
|
||||
if not learnable_tms:
|
||||
learnable_tms = tms
|
||||
tm = self.multiworld.random.choice(learnable_tms)
|
||||
|
||||
loc.place_locked_item(tm)
|
||||
fill_locations.remove(loc)
|
||||
tms.remove(tm)
|
||||
if tm.useful:
|
||||
usefulitempool.remove(tm)
|
||||
else:
|
||||
filleritempool.remove(tm)
|
||||
break
|
||||
else:
|
||||
raise Exception("Missing Gym Leader data")
|
||||
|
||||
def pre_fill(self) -> None:
|
||||
process_wild_pokemon(self)
|
||||
process_static_pokemon(self)
|
||||
pokemon_locs = [location.name for location in location_data if location.type != "Item"]
|
||||
process_pokemon_locations(self)
|
||||
process_trainer_data(self)
|
||||
locs = [location.name for location in location_data if location.type != "Item"]
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.name in pokemon_locs:
|
||||
location.show_in_spoiler = False
|
||||
if location.name in locs:
|
||||
location.show_in_spoiler = False
|
||||
|
||||
def intervene(move):
|
||||
accessible_slots = [loc for loc in self.multiworld.get_reachable_locations(test_state, self.player) if loc.type == "Wild Encounter"]
|
||||
move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
|
||||
viable_mons = [mon for mon in self.local_poke_data if self.local_poke_data[mon]["tms"][6] & move_bit]
|
||||
placed_mons = [slot.item.name for slot in accessible_slots]
|
||||
# this sort method doesn't seem to work if you reference the same list being sorted in the lambda
|
||||
placed_mons_copy = placed_mons.copy()
|
||||
placed_mons.sort(key=lambda i: placed_mons_copy.count(i))
|
||||
placed_mon = placed_mons.pop()
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
zone = " - ".join(placed_mon.split(" - ")[:-1])
|
||||
replace_slots = [slot for slot in accessible_slots if slot.name.startswith(zone) and slot.item.name ==
|
||||
placed_mon]
|
||||
def intervene(move, test_state):
|
||||
if self.multiworld.randomize_wild_pokemon[self.player]:
|
||||
accessible_slots = [loc for loc in self.multiworld.get_reachable_locations(test_state, self.player) if
|
||||
loc.type == "Wild Encounter"]
|
||||
|
||||
def number_of_zones(mon):
|
||||
zones = set()
|
||||
for loc in [slot for slot in accessible_slots if slot.item.name == mon]:
|
||||
zones.add(loc.name.split(" - ")[0])
|
||||
return len(zones)
|
||||
|
||||
move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
|
||||
viable_mons = [mon for mon in self.local_poke_data if self.local_poke_data[mon]["tms"][6] & move_bit]
|
||||
placed_mons = [slot.item.name for slot in accessible_slots]
|
||||
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
placed_mons.sort(key=lambda i: number_of_zones(i))
|
||||
else:
|
||||
# this sort method doesn't work if you reference the same list being sorted in the lambda
|
||||
placed_mons_copy = placed_mons.copy()
|
||||
placed_mons.sort(key=lambda i: placed_mons_copy.count(i))
|
||||
|
||||
placed_mon = placed_mons.pop()
|
||||
replace_mon = self.multiworld.random.choice(viable_mons)
|
||||
replace_slot = self.multiworld.random.choice([slot for slot in accessible_slots if slot.item.name
|
||||
== placed_mon])
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
zone = " - ".join(replace_slot.name.split(" - ")[:-1])
|
||||
replace_slots = [slot for slot in accessible_slots if slot.name.startswith(zone) and slot.item.name
|
||||
== placed_mon]
|
||||
for replace_slot in replace_slots:
|
||||
replace_slot.item = self.create_item(replace_mon)
|
||||
else:
|
||||
replace_slot.item = self.create_item(replace_mon)
|
||||
else:
|
||||
replace_slots = [self.multiworld.random.choice([slot for slot in accessible_slots if slot.item.name ==
|
||||
placed_mon])]
|
||||
replace_mon = self.multiworld.random.choice(viable_mons)
|
||||
for replace_slot in replace_slots:
|
||||
replace_slot.item = self.create_item(replace_mon)
|
||||
tms_hms = self.local_tms + poke_data.hm_moves
|
||||
flag = tms_hms.index(move)
|
||||
mon_list = [mon for mon in poke_data.pokemon_data.keys() if test_state.has(mon, self.player)]
|
||||
self.multiworld.random.shuffle(mon_list)
|
||||
mon_list.sort(key=lambda mon: self.local_move_data[move]["type"] not in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]])
|
||||
for mon in mon_list:
|
||||
if test_state.has(mon, self.player):
|
||||
self.local_poke_data[mon]["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
break
|
||||
|
||||
last_intervene = None
|
||||
while True:
|
||||
intervene_move = None
|
||||
test_state = self.multiworld.get_all_state(False)
|
||||
if not self.multiworld.badgesanity[self.player]:
|
||||
for badge in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge",
|
||||
"Marsh Badge", "Volcano Badge", "Earth Badge"]:
|
||||
test_state.collect(self.create_item(badge))
|
||||
if not test_state.pokemon_rb_can_surf(self.player):
|
||||
if not logic.can_learn_hm(test_state, "Surf", self.player):
|
||||
intervene_move = "Surf"
|
||||
if not test_state.pokemon_rb_can_strength(self.player):
|
||||
elif not logic.can_learn_hm(test_state, "Strength", self.player):
|
||||
intervene_move = "Strength"
|
||||
# cut may not be needed if accessibility is minimal, unless you need all 8 badges and badgesanity is off,
|
||||
# as you will require cut to access celadon gyn
|
||||
if (self.multiworld.accessibility[self.player] != "minimal" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_condition[self.player],
|
||||
self.multiworld.victory_road_condition[self.player]) > 7)):
|
||||
if not test_state.pokemon_rb_can_cut(self.player):
|
||||
intervene_move = "Cut"
|
||||
if (self.multiworld.accessibility[self.player].current_key != "minimal" and
|
||||
(self.multiworld.trainersanity[self.player] or self.multiworld.extra_key_items[self.player])):
|
||||
if not test_state.pokemon_rb_can_flash(self.player):
|
||||
intervene_move = "Flash"
|
||||
elif ((not logic.can_learn_hm(test_state, "Cut", self.player)) and
|
||||
(self.multiworld.accessibility[self.player] != "minimal" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(
|
||||
self.multiworld.elite_four_badges_condition[self.player],
|
||||
self.multiworld.route_22_gate_condition[self.player],
|
||||
self.multiworld.victory_road_condition[self.player])
|
||||
> 7) or (self.multiworld.door_shuffle[self.player] not in ("off", "simple")))):
|
||||
intervene_move = "Cut"
|
||||
elif ((not logic.can_learn_hm(test_state, "Flash", self.player)) and self.multiworld.dark_rock_tunnel_logic[self.player]
|
||||
and (((self.multiworld.accessibility[self.player] != "minimal" and
|
||||
(self.multiworld.trainersanity[self.player] or self.multiworld.extra_key_items[self.player])) or
|
||||
self.multiworld.door_shuffle[self.player]))):
|
||||
intervene_move = "Flash"
|
||||
# If no Pokémon can learn Fly, then during door shuffle it would simply not treat the free fly maps
|
||||
# as reachable, and if on no door shuffle or simple, fly is simply never necessary.
|
||||
# We only intervene if a Pokémon is able to learn fly but none are reachable, as that would have been
|
||||
# considered in door shuffle.
|
||||
elif ((not logic.can_learn_hm(test_state, "Fly", self.player)) and logic.can_learn_hm(test_state, "Fly", self.player)
|
||||
and self.multiworld.door_shuffle[self.player] not in
|
||||
("off", "simple") and [self.fly_map, self.town_map_fly_map] != ["Pallet Town", "Pallet Town"]):
|
||||
intervene_move = "Fly"
|
||||
if intervene_move:
|
||||
if intervene_move == last_intervene:
|
||||
raise Exception(f"Caught in infinite loop attempting to ensure {intervene_move} is available to player {self.player}")
|
||||
intervene(intervene_move)
|
||||
intervene(intervene_move, test_state)
|
||||
last_intervene = intervene_move
|
||||
else:
|
||||
break
|
||||
|
||||
# Delete evolution events for Pokémon that are not in logic in an all_state so that accessibility check does not
|
||||
# fail. Re-use test_state from previous final loop.
|
||||
evolutions_region = self.multiworld.get_region("Evolution", self.player)
|
||||
clear_cache = False
|
||||
for location in evolutions_region.locations.copy():
|
||||
if not test_state.can_reach(location, player=self.player):
|
||||
evolutions_region.locations.remove(location)
|
||||
clear_cache = True
|
||||
if clear_cache:
|
||||
self.multiworld.clear_location_cache()
|
||||
|
||||
if self.multiworld.old_man[self.player] == "early_parcel":
|
||||
self.multiworld.local_early_items[self.player]["Oak's Parcel"] = 1
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
for location in [self.multiworld.get_location(f"Pokedex - {mon}", self.player)
|
||||
for mon in poke_data.pokemon_data.keys()]:
|
||||
add_item_rule(location, lambda item: item.name != "Oak's Parcel" or item.player != self.player)
|
||||
|
||||
if not self.multiworld.badgesanity[self.player].value:
|
||||
self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"]
|
||||
for i in range(5):
|
||||
try:
|
||||
badges = []
|
||||
badgelocs = []
|
||||
for badge in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge",
|
||||
"Marsh Badge", "Volcano Badge", "Earth Badge"]:
|
||||
badges.append(self.create_item(badge))
|
||||
for loc in ["Pewter Gym - Brock 1", "Cerulean Gym - Misty 1", "Vermilion Gym - Lt. Surge 1",
|
||||
"Celadon Gym - Erika 1", "Fuchsia Gym - Koga 1", "Saffron Gym - Sabrina 1",
|
||||
"Cinnabar Gym - Blaine 1", "Viridian Gym - Giovanni 1"]:
|
||||
badgelocs.append(self.multiworld.get_location(loc, self.player))
|
||||
state = self.multiworld.get_all_state(False)
|
||||
self.multiworld.random.shuffle(badges)
|
||||
self.multiworld.random.shuffle(badgelocs)
|
||||
fill_restrictive(self.multiworld, state, badgelocs.copy(), badges, True, True)
|
||||
except FillError:
|
||||
for location in badgelocs:
|
||||
location.item = None
|
||||
continue
|
||||
break
|
||||
else:
|
||||
raise FillError(f"Failed to place badges for player {self.player}")
|
||||
for i, mon in enumerate(poke_data.pokemon_data):
|
||||
if self.dexsanity_table[i]:
|
||||
location = self.multiworld.get_location(f"Pokedex - {mon}", self.player)
|
||||
add_item_rule(location, lambda item: item.name != "Oak's Parcel" or item.player != self.player)
|
||||
|
||||
# Place local items in some locations to prevent save-scumming. Also Oak's PC to prevent an "AP Item" from
|
||||
# entering the player's inventory.
|
||||
|
@ -353,52 +454,144 @@ class PokemonRedBlueWorld(World):
|
|||
locs = {self.multiworld.get_location("Fossil - Choice A", self.player),
|
||||
self.multiworld.get_location("Fossil - Choice B", self.player)}
|
||||
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
for mon in ([" ".join(self.multiworld.get_location(
|
||||
f"Pallet Town - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
+ [" ".join(self.multiworld.get_location(
|
||||
f"Fighting Dojo - Gift {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 3)]):
|
||||
for loc in locs:
|
||||
if self.multiworld.fossil_check_item_types[self.player] == "key_items":
|
||||
add_item_rule(loc, lambda i: i.advancement)
|
||||
elif self.multiworld.fossil_check_item_types[self.player] == "unique_items":
|
||||
add_item_rule(loc, lambda i: i.name in item_groups["Unique"])
|
||||
elif self.multiworld.fossil_check_item_types[self.player] == "no_key_items":
|
||||
add_item_rule(loc, lambda i: not i.advancement)
|
||||
|
||||
for mon in ([" ".join(self.multiworld.get_location(
|
||||
f"Oak's Lab - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
+ [" ".join(self.multiworld.get_location(
|
||||
f"Saffron Fighting Dojo - Gift {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 3)]
|
||||
+ ["Vaporeon", "Jolteon", "Flareon"]):
|
||||
if self.dexsanity_table[poke_data.pokemon_dex[mon] - 1]:
|
||||
loc = self.multiworld.get_location(f"Pokedex - {mon}", self.player)
|
||||
if loc.item is None:
|
||||
locs.add(loc)
|
||||
|
||||
loc = self.multiworld.get_location("Pallet Town - Player's PC", self.player)
|
||||
if loc.item is None:
|
||||
locs.add(loc)
|
||||
if not self.multiworld.key_items_only[self.player]:
|
||||
loc = self.multiworld.get_location("Player's House 2F - Player's PC", self.player)
|
||||
if loc.item is None:
|
||||
locs.add(loc)
|
||||
|
||||
for loc in sorted(locs):
|
||||
unplaced_items = []
|
||||
if loc.name in self.multiworld.priority_locations[self.player].value:
|
||||
add_item_rule(loc, lambda i: i.advancement)
|
||||
for item in reversed(self.multiworld.itempool):
|
||||
if item.player == self.player and loc.can_fill(self.multiworld.state, item, False):
|
||||
self.multiworld.itempool.remove(item)
|
||||
if item.advancement:
|
||||
state = sweep_from_pool(self.multiworld.state, self.multiworld.itempool + unplaced_items)
|
||||
if (not item.advancement) or state.can_reach(loc, "Location", self.player):
|
||||
loc.place_locked_item(item)
|
||||
break
|
||||
add_item_rule(loc, lambda i: i.player == self.player)
|
||||
if self.multiworld.old_man[self.player] == "early_parcel" and loc.name != "Player's House 2F - Player's PC":
|
||||
add_item_rule(loc, lambda i: i.name != "Oak's Parcel")
|
||||
|
||||
self.local_locs = locs
|
||||
|
||||
all_state = self.multiworld.get_all_state(False)
|
||||
|
||||
reachable_mons = set()
|
||||
for mon in poke_data.pokemon_data:
|
||||
if all_state.has(mon, self.player) or all_state.has(f"Static {mon}", self.player):
|
||||
reachable_mons.add(mon)
|
||||
|
||||
# The large number of wild Pokemon can make sweeping for events time-consuming, and is especially bad in
|
||||
# the spoiler playthrough calculation because it removes each advancement item one at a time to verify
|
||||
# if the game is beatable without it. We go through each zone and flag any duplicates as useful.
|
||||
# Especially with area 1-to-1 mapping / vanilla wild Pokémon, this should cut down significantly on wasted time.
|
||||
for region in self.multiworld.get_regions(self.player):
|
||||
region_mons = set()
|
||||
for location in region.locations:
|
||||
if "Wild Pokemon" in location.name:
|
||||
if location.item.name in region_mons:
|
||||
location.item.classification = ItemClassification.useful
|
||||
else:
|
||||
unplaced_items.append(item)
|
||||
self.multiworld.itempool += unplaced_items
|
||||
region_mons.add(location.item.name)
|
||||
|
||||
self.multiworld.elite_four_pokedex_condition[self.player].total = \
|
||||
int((len(reachable_mons) / 100) * self.multiworld.elite_four_pokedex_condition[self.player].value)
|
||||
|
||||
if self.multiworld.accessibility[self.player] == "locations":
|
||||
balls = [self.create_item(ball) for ball in ["Poke Ball", "Great Ball", "Ultra Ball"]]
|
||||
traps = [self.create_item(trap) for trap in item_groups["Traps"]]
|
||||
locations = [location for location in self.multiworld.get_locations(self.player) if "Pokedex - " in
|
||||
location.name]
|
||||
pokedex = self.multiworld.get_region("Pokedex", self.player)
|
||||
remove_items = 0
|
||||
|
||||
for location in locations:
|
||||
if not location.can_reach(all_state):
|
||||
pokedex.locations.remove(location)
|
||||
self.dexsanity_table[poke_data.pokemon_dex[location.name.split(" - ")[1]] - 1] = False
|
||||
remove_items += 1
|
||||
|
||||
for _ in range(remove_items - 5):
|
||||
balls.append(balls.pop(0))
|
||||
for ball in balls:
|
||||
try:
|
||||
self.multiworld.itempool.remove(ball)
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
self.multiworld.random.shuffle(traps)
|
||||
for trap in traps:
|
||||
try:
|
||||
self.multiworld.itempool.remove(trap)
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise Exception("Failed to remove corresponding item while deleting unreachable Dexsanity location")
|
||||
|
||||
self.multiworld._recache()
|
||||
|
||||
if self.multiworld.door_shuffle[self.player] == "decoupled":
|
||||
swept_state = self.multiworld.state.copy()
|
||||
swept_state.sweep_for_events(player=self.player)
|
||||
locations = [location for location in
|
||||
self.multiworld.get_reachable_locations(swept_state, self.player) if location.item is
|
||||
None]
|
||||
self.multiworld.random.shuffle(locations)
|
||||
while len(locations) > 10:
|
||||
location = locations.pop()
|
||||
location.progress_type = LocationProgressType.EXCLUDED
|
||||
|
||||
if self.multiworld.key_items_only[self.player]:
|
||||
locations = [location for location in self.multiworld.get_unfilled_locations(self.player) if
|
||||
location.progress_type == LocationProgressType.DEFAULT]
|
||||
for location in locations:
|
||||
location.progress_type = LocationProgressType.PRIORITY
|
||||
|
||||
def create_regions(self):
|
||||
if self.multiworld.free_fly_location[self.player].value:
|
||||
if self.multiworld.old_man[self.player].value == 0:
|
||||
fly_map_code = self.multiworld.random.randint(1, 9)
|
||||
else:
|
||||
fly_map_code = self.multiworld.random.randint(5, 9)
|
||||
if fly_map_code == 5:
|
||||
fly_map_code = 4
|
||||
if fly_map_code == 9:
|
||||
fly_map_code = 10
|
||||
if (self.multiworld.old_man[self.player] == "vanilla" or
|
||||
self.multiworld.door_shuffle[self.player] in ("full", "insanity")):
|
||||
fly_map_codes = self.multiworld.random.sample(range(2, 11), 2)
|
||||
elif (self.multiworld.door_shuffle[self.player] == "simple" or
|
||||
self.multiworld.route_3_condition[self.player] == "boulder_badge" or
|
||||
(self.multiworld.route_3_condition[self.player] == "any_badge" and
|
||||
self.multiworld.badgesanity[self.player])):
|
||||
fly_map_codes = self.multiworld.random.sample(range(3, 11), 2)
|
||||
|
||||
else:
|
||||
fly_map_codes = self.multiworld.random.sample([4, 6, 7, 8, 9, 10], 2)
|
||||
if self.multiworld.free_fly_location[self.player]:
|
||||
fly_map_code = fly_map_codes[0]
|
||||
else:
|
||||
fly_map_code = 0
|
||||
self.fly_map = ["Pallet Town", "Viridian City", "Pewter City", "Cerulean City", "Lavender Town",
|
||||
"Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau",
|
||||
"Saffron City"][fly_map_code]
|
||||
if self.multiworld.town_map_fly_location[self.player]:
|
||||
town_map_fly_map_code = fly_map_codes[1]
|
||||
else:
|
||||
town_map_fly_map_code = 0
|
||||
fly_maps = ["Pallet Town", "Viridian City", "Pewter City", "Cerulean City", "Lavender Town",
|
||||
"Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau",
|
||||
"Saffron City"]
|
||||
self.fly_map = fly_maps[fly_map_code]
|
||||
self.town_map_fly_map = fly_maps[town_map_fly_map_code]
|
||||
self.fly_map_code = fly_map_code
|
||||
create_regions(self.multiworld, self.player)
|
||||
self.town_map_fly_map_code = town_map_fly_map_code
|
||||
|
||||
create_regions(self)
|
||||
self.multiworld.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player)
|
||||
|
||||
def set_rules(self):
|
||||
|
@ -407,12 +600,21 @@ class PokemonRedBlueWorld(World):
|
|||
def create_item(self, name: str) -> Item:
|
||||
return PokemonRBItem(name, self.player)
|
||||
|
||||
@classmethod
|
||||
def stage_generate_output(cls, multiworld, output_directory):
|
||||
level_scaling(multiworld)
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
generate_output(self, output_directory)
|
||||
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO):
|
||||
if self.multiworld.free_fly_location[self.player].value:
|
||||
spoiler_handle.write('Fly unlocks: %s\n' % self.fly_map)
|
||||
spoiler_handle.write(f"Cerulean Cave Total Key Items: {self.multiworld.cerulean_cave_key_items_condition[self.player].total}\n")
|
||||
spoiler_handle.write(f"Elite Four Total Key Items: {self.multiworld.elite_four_key_items_condition[self.player].total}\n")
|
||||
spoiler_handle.write(f"Elite Four Total Pokemon: {self.multiworld.elite_four_pokedex_condition[self.player].total}\n")
|
||||
if self.multiworld.free_fly_location[self.player]:
|
||||
spoiler_handle.write(f"Free Fly Location: {self.fly_map}\n")
|
||||
if self.multiworld.town_map_fly_location[self.player]:
|
||||
spoiler_handle.write(f"Town Map Fly Location: {self.town_map_fly_map}\n")
|
||||
if self.extra_badges:
|
||||
for hm_move, badge in self.extra_badges.items():
|
||||
spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n")
|
||||
|
@ -423,20 +625,30 @@ class PokemonRedBlueWorld(World):
|
|||
for matchup in self.type_chart:
|
||||
spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n")
|
||||
spoiler_handle.write(f"\n\nPokémon locations ({self.multiworld.player_name[self.player]}):\n\n")
|
||||
pokemon_locs = [location.name for location in location_data if location.type != "Item"]
|
||||
pokemon_locs = [location.name for location in location_data if location.type not in ("Item", "Trainer Parties")]
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.name in pokemon_locs:
|
||||
spoiler_handle.write(location.name + ": " + location.item.name + "\n")
|
||||
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
combined_traps = self.multiworld.poison_trap_weight[self.player].value + self.multiworld.fire_trap_weight[self.player].value + self.multiworld.paralyze_trap_weight[self.player].value + self.multiworld.ice_trap_weight[self.player].value
|
||||
if self.multiworld.random.randint(1, 100) <= self.multiworld.trap_percentage[self.player].value and combined_traps != 0:
|
||||
combined_traps = (self.multiworld.poison_trap_weight[self.player].value
|
||||
+ self.multiworld.fire_trap_weight[self.player].value
|
||||
+ self.multiworld.paralyze_trap_weight[self.player].value
|
||||
+ self.multiworld.ice_trap_weight[self.player].value
|
||||
+ self.multiworld.sleep_trap_weight[self.player].value)
|
||||
if (combined_traps > 0 and
|
||||
self.multiworld.random.randint(1, 100) <= self.multiworld.trap_percentage[self.player].value):
|
||||
return self.select_trap()
|
||||
|
||||
return self.multiworld.random.choice([item for item in item_table if item_table[
|
||||
item].classification == ItemClassification.filler and item not in item_groups["Vending Machine Drinks"] +
|
||||
item_groups["Unique"]])
|
||||
banned_items = item_groups["Unique"]
|
||||
if (((not self.multiworld.tea[self.player]) or "Saffron City" not in [self.fly_map, self.town_map_fly_map])
|
||||
and (not self.multiworld.door_shuffle[self.player])):
|
||||
# under these conditions, you should never be able to reach the Copycat or Pokémon Tower without being
|
||||
# able to reach the Celadon Department Store, so Poké Dolls would not allow early access to anything
|
||||
banned_items.append("Poke Doll")
|
||||
if not self.multiworld.tea[self.player]:
|
||||
banned_items += item_groups["Vending Machine Drinks"]
|
||||
return self.multiworld.random.choice([item for item in item_table if item_table[item].id and item_table[
|
||||
item].classification == ItemClassification.filler and item not in banned_items])
|
||||
|
||||
def select_trap(self):
|
||||
if self.traps is None:
|
||||
|
@ -445,23 +657,30 @@ class PokemonRedBlueWorld(World):
|
|||
self.traps += ["Fire Trap"] * self.multiworld.fire_trap_weight[self.player].value
|
||||
self.traps += ["Paralyze Trap"] * self.multiworld.paralyze_trap_weight[self.player].value
|
||||
self.traps += ["Ice Trap"] * self.multiworld.ice_trap_weight[self.player].value
|
||||
self.traps += ["Sleep Trap"] * self.multiworld.sleep_trap_weight[self.player].value
|
||||
return self.multiworld.random.choice(self.traps)
|
||||
|
||||
def extend_hint_information(self, hint_data):
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
if self.multiworld.dexsanity[self.player] or self.multiworld.door_shuffle[self.player]:
|
||||
hint_data[self.player] = {}
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
mon_locations = {mon: set() for mon in poke_data.pokemon_data.keys()}
|
||||
for loc in location_data: #self.multiworld.get_locations(self.player):
|
||||
for loc in location_data:
|
||||
if loc.type in ["Wild Encounter", "Static Pokemon", "Legendary Pokemon", "Missable Pokemon"]:
|
||||
mon = self.multiworld.get_location(loc.name, self.player).item.name
|
||||
if mon.startswith("Static ") or mon.startswith("Missable "):
|
||||
mon = " ".join(mon.split(" ")[1:])
|
||||
mon_locations[mon].add(loc.name.split(" -")[0])
|
||||
for mon in mon_locations:
|
||||
if mon_locations[mon]:
|
||||
hint_data[self.player][self.multiworld.get_location(f"Pokedex - {mon}", self.player).address] = \
|
||||
for i, mon in enumerate(mon_locations):
|
||||
if self.dexsanity_table[i] and mon_locations[mon]:
|
||||
hint_data[self.player][self.multiworld.get_location(f"Pokedex - {mon}", self.player).address] =\
|
||||
", ".join(mon_locations[mon])
|
||||
|
||||
if self.multiworld.door_shuffle[self.player]:
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.parent_region.entrance_hint and location.address:
|
||||
hint_data[self.player][location.address] = location.parent_region.entrance_hint
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
return {
|
||||
"second_fossil_check_condition": self.multiworld.second_fossil_check_condition[self.player].value,
|
||||
|
@ -475,17 +694,25 @@ class PokemonRedBlueWorld(World):
|
|||
"extra_strength_boulders": self.multiworld.extra_strength_boulders[self.player].value,
|
||||
"tea": self.multiworld.tea[self.player].value,
|
||||
"old_man": self.multiworld.old_man[self.player].value,
|
||||
"elite_four_condition": self.multiworld.elite_four_condition[self.player].value,
|
||||
"elite_four_badges_condition": self.multiworld.elite_four_badges_condition[self.player].value,
|
||||
"elite_four_key_items_condition": self.multiworld.elite_four_key_items_condition[self.player].total,
|
||||
"elite_four_pokedex_condition": self.multiworld.elite_four_pokedex_condition[self.player].total,
|
||||
"victory_road_condition": self.multiworld.victory_road_condition[self.player].value,
|
||||
"route_22_gate_condition": self.multiworld.route_22_gate_condition[self.player].value,
|
||||
"route_3_condition": self.multiworld.route_3_condition[self.player].value,
|
||||
"robbed_house_officer": self.multiworld.robbed_house_officer[self.player].value,
|
||||
"viridian_gym_condition": self.multiworld.viridian_gym_condition[self.player].value,
|
||||
"cerulean_cave_condition": self.multiworld.cerulean_cave_condition[self.player].value,
|
||||
"cerulean_cave_badges_condition": self.multiworld.cerulean_cave_badges_condition[self.player].value,
|
||||
"cerulean_cave_key_items_condition": self.multiworld.cerulean_cave_key_items_condition[self.player].total,
|
||||
"free_fly_map": self.fly_map_code,
|
||||
"town_map_fly_map": self.town_map_fly_map_code,
|
||||
"extra_badges": self.extra_badges,
|
||||
"type_chart": self.type_chart,
|
||||
"randomize_pokedex": self.multiworld.randomize_pokedex[self.player].value,
|
||||
"trainersanity": self.multiworld.trainersanity[self.player].value,
|
||||
"death_link": self.multiworld.death_link[self.player].value,
|
||||
"prizesanity": self.multiworld.prizesanity[self.player].value
|
||||
"prizesanity": self.multiworld.prizesanity[self.player].value,
|
||||
"key_items_only": self.multiworld.key_items_only[self.player].value,
|
||||
}
|
||||
|
||||
|
||||
|
@ -499,4 +726,4 @@ class PokemonRBItem(Item):
|
|||
name,
|
||||
item_data.classification,
|
||||
item_data.id, player
|
||||
)
|
||||
)
|
Binary file not shown.
Binary file not shown.
|
@ -11,35 +11,59 @@ Items which the player would normally acquire throughout the game have been move
|
|||
always able to be completed, but because of the item shuffle the player may need to access certain areas before they
|
||||
would in the vanilla game.
|
||||
|
||||
A great many things besides item placement can be randomized, such as the location of Pokémon, their stats, types, etc., depending on your yaml settings.
|
||||
A great many things besides item placement can be randomized, such as the location of Pokémon, their stats, types, etc.,
|
||||
depending on your yaml settings.
|
||||
|
||||
Many baseline changes are made to the game, including:
|
||||
|
||||
* Bag item space increased to 128 slots (up from 20)
|
||||
* PC item storage increased to 64 slots (up from 50)
|
||||
* Bag item space increased to 128 slots (up from 20).
|
||||
* PC item storage increased to 64 slots (up from 50).
|
||||
* You can hold B to run (or bike extra fast!).
|
||||
* You can hold select while talking to a trainer to re-battle them.
|
||||
* You can return to route 2 from Diglett's Cave without the use of Cut.
|
||||
* You can select "Pallet Warp" below the "Continue" option to warp to Pallet Towna s you load your save.
|
||||
* Mew can be encountered at the S.S. Anne dock truck. This can be randomized depending on your settings.
|
||||
* The S.S. Anne will never depart.
|
||||
* Seafoam Islands entrances are swapped. This means you need Strength to travel through from Cinnabar Island to Fuchsia
|
||||
City
|
||||
City. You also cannot Surf onto the water from the end of Seafoam Islands going backwards if you have not yet dropped
|
||||
the boulders.
|
||||
* After obtaining one of the fossil item checks in Mt Moon, the remaining item can be received from the Cinnabar Lab
|
||||
fossil scientist. This may require reviving a number of fossils, depending on your settings.
|
||||
* Obedience depends on the total number of badges you have obtained instead of depending on specific badges.
|
||||
* Pokémon that evolve by trading can also evolve by reaching level 35.
|
||||
* Evolution stones are reusable.
|
||||
* Evolution stones are reusable key items.
|
||||
* Much of the dialogue throughout the game has been removed or shortened.
|
||||
* If the Old Man is blocking your way through Viridian City, you do not have Oak's Parcel in your inventory, and you've
|
||||
exhausted your money and Poké Balls, you can get a free Poké Ball from your mom.
|
||||
* The Pokédex shows which HMs can be learned by any Pokémon registered as seen.
|
||||
* HM moves can be overwritten if you have the HM for it in your bag.
|
||||
* The NPC on the left behind the Celadon Game Corner counter will sell 1500 coins at once instead of giving information
|
||||
about the Prize Corner
|
||||
about the Prize Corner.
|
||||
* A woman in Oak's Lab can send you back in time to replay the first rival battle, in case you have no other reachable
|
||||
and repeatable source of money.
|
||||
* You can disable and re-enable experience gains by talking to an aide in Oak's Lab.
|
||||
* You can reset static encounters (Poké Flute encounter, legendaries, and the trap Poké Ball battles in Power Plant)
|
||||
for any Pokémon you have defeated but not caught, by talking to an aide in Oak's Lab.
|
||||
|
||||
## What items and locations get shuffled?
|
||||
|
||||
All items that go into your bags given by NPCs or found on the ground, as well as gym badges.
|
||||
Optionally, hidden items (those located with the Item Finder) can be shuffled as well.
|
||||
Various options add more items / location checks to the pool, including:
|
||||
* Randomize Hidden Items.
|
||||
* Stonesanity: Replace 4 of the 5 Moon Stones in the item pool with the other 4 stones, and remove them from the
|
||||
Celadon Department Store shop. Will shuffle the hidden item locations that contain Moon Stones in the original game
|
||||
regardless of the Randomize Hidden Items option.
|
||||
* Prizesanity: Shuffle the three item prizes from the Celadon Prize Corner.
|
||||
* Tea: Adds a Tea item to the item pool which is required to pass the Saffron Gate guards instead of vending machine
|
||||
drinks. Adds a location check to the woman in Celadon Mansion 1F, where the Tea item is found in FireRed and LeafGreen.
|
||||
* Extra Key Items: Adds key items that will be required to access the Power Plant, Pokémon Mansion, Rocket Hideout,
|
||||
and Safari Zone. Adds 4 extra item locations to Rock Tunnel B1F
|
||||
* Split Card Key: Splits the Card Key into 10 different Card Keys, one for each floor of Silph Co that has locked doors.
|
||||
Adds 9 location checks to friendly NPCs in Silph Co. You can also choose Progressive Card Keys to always obtain the
|
||||
keys in order from Card Key 2F to Card Key 11F.
|
||||
* Trainersanity: Adds location checks to 317 trainers. Does not include scripted trainers, most of which disappear
|
||||
after battling them, but also includes Gym Leaders. You must talk to the trainer after defeating them to receive
|
||||
your prize. Adds 317 random filler items to the item pool
|
||||
* Dexsanity: Location checks occur when registering Pokémon as owned in the Pokédex. You can choose a percentage
|
||||
of Pokémon to have checks added to, chosen randomly. You can identify which Pokémon have location checks by an empty
|
||||
Poké Ball icon shown in battle or in the Pokédex menu.
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
from copy import deepcopy
|
||||
from . import poke_data
|
||||
from .locations import location_data
|
||||
|
||||
|
||||
def get_encounter_slots(self):
|
||||
encounter_slots = deepcopy([location for location in location_data if location.type == "Wild Encounter"])
|
||||
|
||||
for location in encounter_slots:
|
||||
if isinstance(location.original_item, list):
|
||||
location.original_item = location.original_item[not self.multiworld.game_version[self.player].value]
|
||||
return encounter_slots
|
||||
|
||||
|
||||
def get_base_stat_total(mon):
|
||||
return (poke_data.pokemon_data[mon]["atk"] + poke_data.pokemon_data[mon]["def"]
|
||||
+ poke_data.pokemon_data[mon]["hp"] + poke_data.pokemon_data[mon]["spd"]
|
||||
+ poke_data.pokemon_data[mon]["spc"])
|
||||
|
||||
|
||||
def randomize_pokemon(self, mon, mons_list, randomize_type, random):
|
||||
if randomize_type in [1, 3]:
|
||||
type_mons = [pokemon for pokemon in mons_list if any([poke_data.pokemon_data[mon][
|
||||
"type1"] in [self.local_poke_data[pokemon]["type1"], self.local_poke_data[pokemon]["type2"]],
|
||||
poke_data.pokemon_data[mon]["type2"] in [self.local_poke_data[pokemon]["type1"],
|
||||
self.local_poke_data[pokemon]["type2"]]])]
|
||||
if not type_mons:
|
||||
type_mons = mons_list.copy()
|
||||
if randomize_type == 3:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
type_mons.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
|
||||
mon = type_mons[round(random.triangular(0, len(type_mons) - 1, 0))]
|
||||
if randomize_type == 2:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
mons_list.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
|
||||
mon = mons_list[round(random.triangular(0, 50, 0))]
|
||||
elif randomize_type == 4:
|
||||
mon = random.choice(mons_list)
|
||||
return mon
|
||||
|
||||
|
||||
def process_trainer_data(self):
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.trainer_legendaries[self.player].value]
|
||||
unevolved_mons = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
evolved_mons = [mon for mon in mons_list if mon not in unevolved_mons]
|
||||
rival_map = {
|
||||
"Charmander": self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name[9:], # strip the
|
||||
"Squirtle": self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name[9:], # 'Missable'
|
||||
"Bulbasaur": self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name[9:], # from the name
|
||||
}
|
||||
|
||||
def add_evolutions():
|
||||
for a, b in rival_map.copy().items():
|
||||
if a in poke_data.evolves_to and poke_data.evolves_to[a] not in rival_map:
|
||||
if b in poke_data.evolves_to:
|
||||
rival_map[poke_data.evolves_to[a]] = poke_data.evolves_to[b]
|
||||
else:
|
||||
rival_map[poke_data.evolves_to[a]] = b
|
||||
add_evolutions()
|
||||
add_evolutions()
|
||||
parties_objs = [location for location in self.multiworld.get_locations(self.player)
|
||||
if location.type == "Trainer Parties"]
|
||||
# Process Rival parties in order "Route 22 " is not a typo
|
||||
parties_objs.sort(key=lambda i: 0 if "Oak's Lab" in i.name else 1 if "Route 22 " in i.name else 2 if "Cerulean City"
|
||||
in i.name else 3 if "Anne" in i.name else 4 if "Pokemon Tower" in i.name else 5 if "Silph" in
|
||||
i.name else 6 if "Route 22-F" in i.name else 7 if "Champion" in i.name else 8)
|
||||
for parties in parties_objs:
|
||||
parties_data = parties.party_data
|
||||
for party in parties_data:
|
||||
if party["party"] and isinstance(party["party"][0], list):
|
||||
# only for Rival parties
|
||||
for rival_party in party["party"]:
|
||||
for i, mon in enumerate(rival_party):
|
||||
if mon in ("Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmeleon", "Charizard",
|
||||
"Squirtle", "Wartortle", "Blastoise"):
|
||||
if self.multiworld.randomize_starter_pokemon[self.player]:
|
||||
rival_party[i] = rival_map[mon]
|
||||
elif self.multiworld.randomize_trainer_parties[self.player]:
|
||||
if mon in rival_map:
|
||||
rival_party[i] = rival_map[mon]
|
||||
else:
|
||||
new_mon = randomize_pokemon(self, mon,
|
||||
unevolved_mons if mon in unevolved_mons else evolved_mons,
|
||||
self.multiworld.randomize_trainer_parties[self.player].value,
|
||||
self.multiworld.random)
|
||||
rival_map[mon] = new_mon
|
||||
rival_party[i] = new_mon
|
||||
add_evolutions()
|
||||
else:
|
||||
if self.multiworld.randomize_trainer_parties[self.player]:
|
||||
for i, mon in enumerate(party["party"]):
|
||||
party["party"][i] = randomize_pokemon(self, mon, mons_list,
|
||||
self.multiworld.randomize_trainer_parties[self.player].value,
|
||||
self.multiworld.random)
|
||||
|
||||
|
||||
def process_pokemon_locations(self):
|
||||
starter_slots = deepcopy([location for location in location_data if location.type == "Starter Pokemon"])
|
||||
legendary_slots = deepcopy([location for location in location_data if location.type == "Legendary Pokemon"])
|
||||
static_slots = deepcopy([location for location in location_data if location.type in
|
||||
["Static Pokemon", "Missable Pokemon"]])
|
||||
legendary_mons = deepcopy([slot.original_item for slot in legendary_slots])
|
||||
|
||||
placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()}
|
||||
|
||||
mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
if self.multiworld.randomize_legendary_pokemon[self.player] == "vanilla":
|
||||
for slot in legendary_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.place_locked_item(self.create_item("Static " + slot.original_item))
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player] == "shuffle":
|
||||
self.multiworld.random.shuffle(legendary_mons)
|
||||
for slot in legendary_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
mon = legendary_mons.pop()
|
||||
location.place_locked_item(self.create_item("Static " + mon))
|
||||
placed_mons[mon] += 1
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player] == "static":
|
||||
static_slots = static_slots + legendary_slots
|
||||
self.multiworld.random.shuffle(static_slots)
|
||||
static_slots.sort(key=lambda s: s.name != "Pokemon Tower 6F - Restless Soul")
|
||||
while legendary_slots:
|
||||
swap_slot = legendary_slots.pop()
|
||||
slot = static_slots.pop()
|
||||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
slot_type = "Static"
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item))
|
||||
swap_slot.original_item = slot.original_item
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player] == "any":
|
||||
static_slots = static_slots + legendary_slots
|
||||
|
||||
for slot in static_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_static_pokemon[self.player].value
|
||||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
slot_type = "Static"
|
||||
if not randomize_type:
|
||||
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
|
||||
else:
|
||||
mon = self.create_item(slot_type + " " +
|
||||
randomize_pokemon(self, slot.original_item, mons_list, randomize_type,
|
||||
self.multiworld.random))
|
||||
location.place_locked_item(mon)
|
||||
if slot_type != "Missable":
|
||||
placed_mons[mon.name.replace("Static ", "")] += 1
|
||||
|
||||
chosen_mons = set()
|
||||
for slot in starter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
|
||||
slot_type = "Missable"
|
||||
if not randomize_type:
|
||||
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
|
||||
else:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
while mon.name in chosen_mons:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
chosen_mons.add(mon.name)
|
||||
location.place_locked_item(mon)
|
||||
|
||||
encounter_slots_master = get_encounter_slots(self)
|
||||
encounter_slots = encounter_slots_master.copy()
|
||||
|
||||
zone_mapping = {}
|
||||
if self.multiworld.randomize_wild_pokemon[self.player]:
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
self.multiworld.random.shuffle(encounter_slots)
|
||||
locations = []
|
||||
for slot in encounter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if zone not in zone_mapping:
|
||||
zone_mapping[zone] = {}
|
||||
original_mon = slot.original_item
|
||||
if self.multiworld.area_1_to_1_mapping[self.player] and original_mon in zone_mapping[zone]:
|
||||
mon = zone_mapping[zone][original_mon]
|
||||
else:
|
||||
mon = randomize_pokemon(self, original_mon, mons_list,
|
||||
self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random)
|
||||
#
|
||||
while ("Pokemon Tower 6F" in slot.name and
|
||||
self.multiworld.get_location("Pokemon Tower 6F - Restless Soul", self.player).item.name
|
||||
== f"Missable {mon}"):
|
||||
# If you're fighting the Pokémon defined as the Restless Soul, and you're on the 6th floor of the tower,
|
||||
# the battle is treates as the Restless Soul battle and you cannot catch it. So, prevent any wild mons
|
||||
# from being the same species as the Restless Soul.
|
||||
# to account for the possibility that only one ground type Pokemon exists, match only stats for this fix
|
||||
mon = randomize_pokemon(self, original_mon, mons_list, 2, self.multiworld.random)
|
||||
placed_mons[mon] += 1
|
||||
location.item = self.create_item(mon)
|
||||
location.event = True
|
||||
location.locked = True
|
||||
location.item.location = location
|
||||
locations.append(location)
|
||||
zone_mapping[zone][original_mon] = mon
|
||||
|
||||
mons_to_add = []
|
||||
remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
if self.multiworld.catch_em_all[self.player] == "first_stage":
|
||||
mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
elif self.multiworld.catch_em_all[self.player] == "all_pokemon":
|
||||
mons_to_add = remaining_pokemon.copy()
|
||||
logic_needed_mons = max(self.multiworld.oaks_aide_rt_2[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_11[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_15[self.player].value)
|
||||
if self.multiworld.accessibility[self.player] == "minimal":
|
||||
logic_needed_mons = 0
|
||||
|
||||
self.multiworld.random.shuffle(remaining_pokemon)
|
||||
while (len([pokemon for pokemon in placed_mons if placed_mons[pokemon] > 0])
|
||||
+ len(mons_to_add) < logic_needed_mons):
|
||||
mons_to_add.append(remaining_pokemon.pop())
|
||||
for mon in mons_to_add:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
candidate_locations = encounter_slots_master.copy()
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_base_stats", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.original_item) - stat_base))
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_types", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: not any([poke_data.pokemon_data[slot.original_item]["type1"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
|
||||
poke_data.pokemon_data[slot.original_item]["type2"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]]]))
|
||||
candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations]
|
||||
for location in candidate_locations:
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if self.multiworld.catch_em_all[self.player] == "all_pokemon" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in encounter_slots_master
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name]:
|
||||
continue
|
||||
if self.multiworld.catch_em_all[self.player] == "first_stage" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in encounter_slots_master
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name and l.name
|
||||
not in poke_data.evolves_from]:
|
||||
continue
|
||||
|
||||
if placed_mons[location.item.name] < 2 and (location.item.name in poke_data.first_stage_pokemon
|
||||
or self.multiworld.catch_em_all[self.player]):
|
||||
continue
|
||||
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
place_locations = [place_location for place_location in candidate_locations if
|
||||
place_location.name.startswith(zone) and
|
||||
place_location.item.name == location.item.name]
|
||||
else:
|
||||
place_locations = [location]
|
||||
for place_location in place_locations:
|
||||
placed_mons[place_location.item.name] -= 1
|
||||
place_location.item = self.create_item(mon)
|
||||
place_location.item.location = place_location
|
||||
placed_mons[mon] += 1
|
||||
break
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
else:
|
||||
for slot in encounter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.item = self.create_item(slot.original_item)
|
||||
location.event = True
|
||||
location.locked = True
|
||||
location.item.location = location
|
||||
placed_mons[location.item.name] += 1
|
|
@ -1,11 +1,13 @@
|
|||
from BaseClasses import ItemClassification
|
||||
from .poke_data import pokemon_data
|
||||
|
||||
|
||||
class ItemData:
|
||||
def __init__(self, id, classification, groups):
|
||||
def __init__(self, item_id, classification, groups):
|
||||
self.groups = groups
|
||||
self.classification = classification
|
||||
self.id = None if id is None else id + 172000000
|
||||
self.id = None if item_id is None else item_id + 172000000
|
||||
|
||||
|
||||
item_table = {
|
||||
"Master Ball": ItemData(1, ItemClassification.useful, ["Consumables", "Poke Balls"]),
|
||||
|
@ -15,9 +17,9 @@ item_table = {
|
|||
"Town Map": ItemData(5, ItemClassification.progression_skip_balancing, ["Unique", "Key Items"]),
|
||||
"Bicycle": ItemData(6, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
# "Flippers": ItemData(7, ItemClassification.progression),
|
||||
#"Safari Ball": ItemData(8, ItemClassification.filler),
|
||||
# "Safari Ball": ItemData(8, ItemClassification.filler),
|
||||
"Pokedex": ItemData(9, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Moon Stone": ItemData(10, ItemClassification.useful, ["Unique", "Evolution Stones"]),
|
||||
"Moon Stone": ItemData(10, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
|
||||
"Antidote": ItemData(11, ItemClassification.filler, ["Consumables"]),
|
||||
"Burn Heal": ItemData(12, ItemClassification.filler, ["Consumables"]),
|
||||
"Ice Heal": ItemData(13, ItemClassification.filler, ["Consumables"]),
|
||||
|
@ -38,23 +40,23 @@ item_table = {
|
|||
"Earth Badge": ItemData(28, ItemClassification.progression, ["Unique", "Key Items", "Badges"]),
|
||||
"Escape Rope": ItemData(29, ItemClassification.filler, ["Consumables"]),
|
||||
"Repel": ItemData(30, ItemClassification.filler, ["Consumables"]),
|
||||
"Old Amber": ItemData(31, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]),
|
||||
"Fire Stone": ItemData(32, ItemClassification.useful, ["Unique", "Evolution Stones"]),
|
||||
"Thunder Stone": ItemData(33, ItemClassification.useful, ["Unique", "Evolution Stones"]),
|
||||
"Water Stone": ItemData(34, ItemClassification.useful, ["Unique", "Evolution Stones"]),
|
||||
"Old Amber": ItemData(31, ItemClassification.progression_skip_balancing, ["Unique", "Fossils", "Key Items"]),
|
||||
"Fire Stone": ItemData(32, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
|
||||
"Thunder Stone": ItemData(33, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones" "Key Items"]),
|
||||
"Water Stone": ItemData(34, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
|
||||
"HP Up": ItemData(35, ItemClassification.filler, ["Consumables", "Vitamins"]),
|
||||
"Protein": ItemData(36, ItemClassification.filler, ["Consumables", "Vitamins"]),
|
||||
"Iron": ItemData(37, ItemClassification.filler, ["Consumables", "Vitamins"]),
|
||||
"Carbos": ItemData(38, ItemClassification.filler, ["Consumables", "Vitamins"]),
|
||||
"Calcium": ItemData(39, ItemClassification.filler, ["Consumables", "Vitamins"]),
|
||||
"Rare Candy": ItemData(40, ItemClassification.useful, ["Consumables"]),
|
||||
"Dome Fossil": ItemData(41, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]),
|
||||
"Helix Fossil": ItemData(42, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]),
|
||||
"Rare Candy": ItemData(40, ItemClassification.filler, ["Consumables"]),
|
||||
"Dome Fossil": ItemData(41, ItemClassification.progression_skip_balancing, ["Unique", "Fossils", "Key Items"]),
|
||||
"Helix Fossil": ItemData(42, ItemClassification.progression_skip_balancing, ["Unique", "Fossils", "Key Items"]),
|
||||
"Secret Key": ItemData(43, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Bike Voucher": ItemData(45, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"X Accuracy": ItemData(46, ItemClassification.filler, ["Consumables", "Battle Items"]),
|
||||
"Leaf Stone": ItemData(47, ItemClassification.useful, ["Unique", "Evolution Stones"]),
|
||||
"Card Key": ItemData(48, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Leaf Stone": ItemData(47, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
|
||||
"Card Key": ItemData(48, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Nugget": ItemData(49, ItemClassification.filler, []),
|
||||
#"Laptop": ItemData(50, ItemClassification.useful, ["Unique"]),
|
||||
"Poke Doll": ItemData(51, ItemClassification.filler, ["Consumables"]),
|
||||
|
@ -75,13 +77,13 @@ item_table = {
|
|||
"X Defend": ItemData(66, ItemClassification.filler, ["Consumables", "Battle Items"]),
|
||||
"X Speed": ItemData(67, ItemClassification.filler, ["Consumables", "Battle Items"]),
|
||||
"X Special": ItemData(68, ItemClassification.filler, ["Consumables", "Battle Items"]),
|
||||
"Coin Case": ItemData(69, ItemClassification.progression_skip_balancing, ["Unique", "Key Items"]),
|
||||
"Coin Case": ItemData(69, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Oak's Parcel": ItemData(70, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Item Finder": ItemData(71, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Silph Scope": ItemData(72, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Poke Flute": ItemData(73, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Lift Key": ItemData(74, ItemClassification.progression, ["Unique", "Key Items"]),
|
||||
"Exp. All": ItemData(75, ItemClassification.useful, ["Unique"]),
|
||||
"Exp. All": ItemData(75, ItemClassification.progression_skip_balancing, ["Unique", "Key Items"]),
|
||||
"Old Rod": ItemData(76, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]),
|
||||
"Good Rod": ItemData(77, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]),
|
||||
"Super Rod": ItemData(78, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]),
|
||||
|
@ -105,11 +107,23 @@ item_table = {
|
|||
"Fire Trap": ItemData(97, ItemClassification.trap, ["Traps"]),
|
||||
"20 Coins": ItemData(98, ItemClassification.filler, ["Coins"]),
|
||||
"100 Coins": ItemData(99, ItemClassification.filler, ["Coins"]),
|
||||
"HM01 Cut": ItemData(196, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM02 Fly": ItemData(197, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM03 Surf": ItemData(198, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM04 Strength": ItemData(199, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"HM05 Flash": ItemData(200, ItemClassification.progression, ["Unique", "HMs"]),
|
||||
"Card Key 2F": ItemData(100, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 3F": ItemData(101, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 4F": ItemData(102, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 5F": ItemData(103, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 6F": ItemData(104, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 7F": ItemData(105, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 8F": ItemData(106, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 9F": ItemData(107, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 10F": ItemData(108, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Card Key 11F": ItemData(109, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Progressive Card Key": ItemData(110, ItemClassification.progression, ["Unique", "Key Items", "Card Keys"]),
|
||||
"Sleep Trap": ItemData(111, ItemClassification.trap, ["Traps"]),
|
||||
"HM01 Cut": ItemData(196, ItemClassification.progression, ["Unique", "HMs", "Key Items"]),
|
||||
"HM02 Fly": ItemData(197, ItemClassification.progression, ["Unique", "HMs", "Key Items"]),
|
||||
"HM03 Surf": ItemData(198, ItemClassification.progression, ["Unique", "HMs", "Key Items"]),
|
||||
"HM04 Strength": ItemData(199, ItemClassification.progression, ["Unique", "HMs", "Key Items"]),
|
||||
"HM05 Flash": ItemData(200, ItemClassification.progression, ["Unique", "HMs", "Key Items"]),
|
||||
"TM01 Mega Punch": ItemData(201, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM02 Razor Wind": ItemData(202, ItemClassification.filler, ["Unique", "TMs"]),
|
||||
"TM03 Swords Dance": ItemData(203, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
|
@ -161,9 +175,27 @@ item_table = {
|
|||
"TM49 Tri Attack": ItemData(249, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
"TM50 Substitute": ItemData(250, ItemClassification.useful, ["Unique", "TMs"]),
|
||||
|
||||
"Game Corner": ItemData(None, ItemClassification.progression, []),
|
||||
"Cinnabar Island": ItemData(None, ItemClassification.progression, []),
|
||||
"Buy Poke Doll": ItemData(None, ItemClassification.progression, []),
|
||||
"Vending Machine Drinks": ItemData(None, ItemClassification.progression, []),
|
||||
"Help Bill": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Brock": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Misty": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Lt. Surge": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Erika": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Koga": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Blaine": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Sabrina": ItemData(None, ItemClassification.progression, []),
|
||||
"Defeat Viridian Gym Giovanni": ItemData(None, ItemClassification.progression, []),
|
||||
"Seafoam Exit Boulder": ItemData(None, ItemClassification.progression, []),
|
||||
"Seafoam Boss Boulders": ItemData(None, ItemClassification.progression, []),
|
||||
"Victory Road Boulder": ItemData(None, ItemClassification.progression, []),
|
||||
"Fuji Saved": ItemData(None, ItemClassification.progression, []),
|
||||
"Silph Co Liberated": ItemData(None, ItemClassification.progression, []),
|
||||
"Become Champion": ItemData(None, ItemClassification.progression, [])
|
||||
"Become Champion": ItemData(None, ItemClassification.progression, []),
|
||||
|
||||
"Trainer Parties": ItemData(None, ItemClassification.filler, [])
|
||||
}
|
||||
|
||||
item_table.update({f"TM{str(i).zfill(2)}": ItemData(i + 456, ItemClassification.filler, ["Unique", "TMs"])
|
||||
|
@ -183,4 +215,4 @@ item_table.update(
|
|||
item_groups = {}
|
||||
for item, data in item_table.items():
|
||||
for group in data.groups:
|
||||
item_groups[group] = item_groups.get(group, []) + [item]
|
||||
item_groups[group] = item_groups.get(group, []) + [item]
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
from BaseClasses import CollectionState
|
||||
from .locations import level_name_list, level_list
|
||||
|
||||
|
||||
def level_scaling(multiworld):
|
||||
state = CollectionState(multiworld)
|
||||
locations = set(multiworld.get_filled_locations())
|
||||
spheres = []
|
||||
|
||||
while locations:
|
||||
sphere = set()
|
||||
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
|
||||
if multiworld.level_scaling[world.player] != "by_spheres_and_distance":
|
||||
continue
|
||||
regions = {multiworld.get_region("Menu", world.player)}
|
||||
checked_regions = set()
|
||||
distance = 0
|
||||
while regions:
|
||||
next_regions = set()
|
||||
for region in regions:
|
||||
if not getattr(region, "distance"):
|
||||
region.distance = distance
|
||||
next_regions.update({e.connected_region for e in region.exits if e.connected_region not in
|
||||
checked_regions and e.access_rule(state)})
|
||||
checked_regions.update(regions)
|
||||
regions = next_regions
|
||||
distance += 1
|
||||
|
||||
distances = {}
|
||||
|
||||
for location in locations:
|
||||
def reachable():
|
||||
if location.can_reach(state):
|
||||
return True
|
||||
if location.parent_region.name == "Fossil" and state.can_reach("Mt Moon B2F", "Region",
|
||||
location.player):
|
||||
# We want areas that are accessible earlier to have lower levels. If an important item is at a
|
||||
# fossil location, it may not be in logic until much later, despite your ability to potentially
|
||||
# reach them earlier. We treat them both as reachable right away for this purpose
|
||||
return True
|
||||
if (location.name == "Route 25 - Item" and state.can_reach("Route 25", "Region", location.player)
|
||||
and multiworld.blind_trainers[location.player].value < 100):
|
||||
# Assume they will take their one chance to get the trainer to walk out of the way to reach
|
||||
# the item behind them
|
||||
return True
|
||||
if (("Rock Tunnel 1F - Wild Pokemon" in location.name
|
||||
and any([multiworld.get_entrance(e, location.player).connected_region.can_reach(state)
|
||||
for e in ['Rock Tunnel 1F-NE to Route 10-N',
|
||||
'Rock Tunnel 1F-NE to Rock Tunnel B1F-E',
|
||||
'Rock Tunnel 1F-NW to Rock Tunnel B1F-E',
|
||||
'Rock Tunnel 1F-NW to Rock Tunnel B1F-W',
|
||||
'Rock Tunnel 1F-S to Route 10-S',
|
||||
'Rock Tunnel 1F-S to Rock Tunnel B1F-W']])) or
|
||||
("Rock Tunnel B1F - Wild Pokemon" in location.name and
|
||||
any([multiworld.get_entrance(e, location.player).connected_region.can_reach(state)
|
||||
for e in ['Rock Tunnel B1F-E to Rock Tunnel 1F-NE',
|
||||
'Rock Tunnel B1F-E to Rock Tunnel 1F-NW',
|
||||
'Rock Tunnel B1F-W to Rock Tunnel 1F-NW',
|
||||
'Rock Tunnel B1F-W to Rock Tunnel 1F-S']]))):
|
||||
# Even if checks in Rock Tunnel are out of logic due to lack of Flash, it is very easy to
|
||||
# wander in the dark and encounter wild Pokémon, even unintentionally while attempting to
|
||||
# leave the way you entered. We'll count the wild Pokémon as reachable as soon as the Rock
|
||||
# Tunnel is reachable, so you don't have an opportunity to catch high level Pokémon early.
|
||||
# If the connections between Rock Tunnel floors are vanilla, you will still potentially
|
||||
# have very high level Pokémon in B1F if you reach it out of logic, but that would always
|
||||
# mean intentionally breaking the logic you picked in your yaml, and may require
|
||||
# defeating trainers in 1F that would be at the higher levels.
|
||||
return True
|
||||
return False
|
||||
if reachable():
|
||||
sphere.add(location)
|
||||
parent_region = location.parent_region
|
||||
if getattr(parent_region, "distance", None) is None:
|
||||
distance = 0
|
||||
else:
|
||||
distance = parent_region.distance
|
||||
if distance not in distances:
|
||||
distances[distance] = {location}
|
||||
else:
|
||||
distances[distance].add(location)
|
||||
|
||||
if sphere:
|
||||
for distance in sorted(distances.keys()):
|
||||
spheres.append(distances[distance])
|
||||
locations -= distances[distance]
|
||||
else:
|
||||
spheres.append(locations)
|
||||
break
|
||||
|
||||
for location in sphere:
|
||||
if not location.item:
|
||||
continue
|
||||
if (location.item.game == "Pokemon Red and Blue" and (location.item.name.startswith("Missable ") or
|
||||
location.item.name.startswith("Static ")) and location.name !=
|
||||
"Pokemon Tower 6F - Restless Soul"):
|
||||
# Normally, missable Pokemon (starters, the dojo rewards) are not considered in logic static Pokemon
|
||||
# are not considered for moves or evolutions, as you could release them and potentially soft lock
|
||||
# the game. However, for level scaling purposes, we will treat them as not missable or static.
|
||||
# We would not want someone playing a minimal accessibility Dexsanity game to get what would be
|
||||
# technically an "out of logic" Mansion Key from selecting Bulbasaur at the beginning of the game
|
||||
# and end up in the Mansion early and encountering level 67 Pokémon
|
||||
state.collect(multiworld.worlds[location.item.player].create_item(
|
||||
location.item.name.split("Missable ")[-1].split("Static ")[-1]), True, location)
|
||||
else:
|
||||
state.collect(location.item, True, location)
|
||||
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
|
||||
if multiworld.level_scaling[world.player] == "off":
|
||||
continue
|
||||
level_list_copy = level_list.copy()
|
||||
for sphere in spheres:
|
||||
sphere_objects = {loc.name: loc for loc in sphere if loc.player == world.player
|
||||
and (loc.type == "Wild Encounter" or "Pokemon" in loc.type) and loc.level is not None}
|
||||
party_objects = [loc for loc in sphere if loc.player == world.player and loc.type == "Trainer Parties"]
|
||||
for parties in party_objects:
|
||||
for party in parties.party_data:
|
||||
if isinstance(party["level"], int):
|
||||
sphere_objects[(party["party_address"][0] if isinstance(party["party_address"], list)
|
||||
else party["party_address"], 0)] = parties
|
||||
else:
|
||||
for i, level in enumerate(party["level"]):
|
||||
sphere_objects[(party["party_address"][0] if isinstance(party["party_address"], list)
|
||||
else party["party_address"], i)] = parties
|
||||
ordered_sphere_objects = list(sphere_objects.keys())
|
||||
ordered_sphere_objects.sort(key=lambda obj: level_name_list.index(obj))
|
||||
for object in ordered_sphere_objects:
|
||||
if sphere_objects[object].type == "Trainer Parties":
|
||||
for party in sphere_objects[object].party_data:
|
||||
if (isinstance(party["party_address"], list) and party["party_address"][0] == object[0]) or party["party_address"] == object[0]:
|
||||
if isinstance(party["level"], int):
|
||||
party["level"] = level_list_copy.pop(0)
|
||||
else:
|
||||
party["level"][object[1]] = level_list_copy.pop(0)
|
||||
break
|
||||
else:
|
||||
sphere_objects[object].level = level_list_copy.pop(0)
|
||||
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
|
||||
world.finished_level_scaling.set()
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,86 +1,121 @@
|
|||
from ..AutoWorld import LogicMixin
|
||||
import worlds.pokemon_rb.poke_data as poke_data
|
||||
from . import poke_data
|
||||
|
||||
|
||||
class PokemonLogic(LogicMixin):
|
||||
def pokemon_rb_can_surf(self, player):
|
||||
return (((self.has("HM03 Surf", player) and self.can_learn_hm("10000", player))
|
||||
or self.has("Flippers", player)) and (self.has("Soul Badge", player) or
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Surf"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
def can_surf(state, player):
|
||||
return (((state.has("HM03 Surf", player) and can_learn_hm(state, "Surf", player))
|
||||
or state.has("Flippers", player)) and (state.has("Soul Badge", player) or
|
||||
state.has(state.multiworld.worlds[player].extra_badges.get("Surf"), player)
|
||||
or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def pokemon_rb_can_cut(self, player):
|
||||
return ((self.has("HM01 Cut", player) and self.can_learn_hm("100", player) or self.has("Master Sword", player))
|
||||
and (self.has("Cascade Badge", player) or
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Cut"), player) or
|
||||
self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def pokemon_rb_can_fly(self, player):
|
||||
return (((self.has("HM02 Fly", player) and self.can_learn_hm("1000", player)) or self.has("Flute", player)) and
|
||||
(self.has("Thunder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Fly"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
def can_cut(state, player):
|
||||
return ((state.has("HM01 Cut", player) and can_learn_hm(state, "Cut", player) or state.has("Master Sword", player))
|
||||
and (state.has("Cascade Badge", player) or
|
||||
state.has(state.multiworld.worlds[player].extra_badges.get("Cut"), player) or
|
||||
state.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def pokemon_rb_can_strength(self, player):
|
||||
return ((self.has("HM04 Strength", player) and self.can_learn_hm("100000", player)) or
|
||||
self.has("Titan's Mitt", player)) and (self.has("Rainbow Badge", player) or
|
||||
self.has(self.multiworld.worlds[player].extra_badges.get("Strength"), player)
|
||||
or self.multiworld.badges_needed_for_hm_moves[player].value == 0)
|
||||
|
||||
def pokemon_rb_can_flash(self, player):
|
||||
return (((self.has("HM05 Flash", player) and self.can_learn_hm("1000000", player)) or self.has("Lamp", player))
|
||||
and (self.has("Boulder Badge", player) or self.has(self.multiworld.worlds[player].extra_badges.get("Flash"),
|
||||
player) or self.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
def can_fly(state, player):
|
||||
return (((state.has("HM02 Fly", player) and can_learn_hm(state, "Fly", player)) or state.has("Flute", player)) and
|
||||
(state.has("Thunder Badge", player) or state.has(state.multiworld.worlds[player].extra_badges.get("Fly"), player)
|
||||
or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def can_learn_hm(self, move, player):
|
||||
for pokemon, data in self.multiworld.worlds[player].local_poke_data.items():
|
||||
if self.has(pokemon, player) and data["tms"][6] & int(move, 2):
|
||||
return True
|
||||
return False
|
||||
|
||||
def pokemon_rb_can_get_hidden_items(self, player):
|
||||
return self.has("Item Finder", player) or not self.multiworld.require_item_finder[player].value
|
||||
def can_strength(state, player):
|
||||
return ((state.has("HM04 Strength", player) and can_learn_hm(state, "Strength", player)) or
|
||||
state.has("Titan's Mitt", player)) and (state.has("Rainbow Badge", player) or
|
||||
state.has(state.multiworld.worlds[player].extra_badges.get("Strength"), player)
|
||||
or state.multiworld.badges_needed_for_hm_moves[player].value == 0)
|
||||
|
||||
def pokemon_rb_cerulean_cave(self, count, player):
|
||||
return len([item for item in
|
||||
["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge", "Marsh Badge",
|
||||
"Volcano Badge", "Earth Badge", "Bicycle", "Silph Scope", "Item Finder", "Super Rod", "Good Rod",
|
||||
"Old Rod", "Lift Key", "Card Key", "Town Map", "Coin Case", "S.S. Ticket", "Secret Key",
|
||||
"Poke Flute", "Mansion Key", "Safari Pass", "Plant Key", "Hideout Key", "HM01 Cut", "HM02 Fly",
|
||||
"HM03 Surf", "HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count
|
||||
|
||||
def pokemon_rb_can_pass_guards(self, player):
|
||||
if self.multiworld.tea[player].value:
|
||||
return self.has("Tea", player)
|
||||
else:
|
||||
return self.can_reach("Celadon City - Counter Man", "Location", player)
|
||||
def can_flash(state, player):
|
||||
return (((state.has("HM05 Flash", player) and can_learn_hm(state, "Flash", player)) or state.has("Lamp", player))
|
||||
and (state.has("Boulder Badge", player) or state.has(state.multiworld.worlds[player].extra_badges.get("Flash"),
|
||||
player) or state.multiworld.badges_needed_for_hm_moves[player].value == 0))
|
||||
|
||||
def pokemon_rb_has_badges(self, count, player):
|
||||
return len([item for item in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
|
||||
"Soul Badge", "Volcano Badge", "Earth Badge"] if self.has(item, player)]) >= count
|
||||
|
||||
def pokemon_rb_oaks_aide(self, count, player):
|
||||
return ((not self.multiworld.require_pokedex[player] or self.has("Pokedex", player))
|
||||
and self.pokemon_rb_has_pokemon(count, player))
|
||||
def can_learn_hm(state, move, player):
|
||||
for pokemon, data in state.multiworld.worlds[player].local_poke_data.items():
|
||||
if state.has(pokemon, player) and data["tms"][6] & 1 << (["Cut", "Fly", "Surf", "Strength",
|
||||
"Flash"].index(move) + 2):
|
||||
return True
|
||||
return False
|
||||
|
||||
def pokemon_rb_has_pokemon(self, count, player):
|
||||
obtained_pokemon = set()
|
||||
for pokemon in poke_data.pokemon_data.keys():
|
||||
if self.has(pokemon, player) or self.has(f"Static {pokemon}", player):
|
||||
obtained_pokemon.add(pokemon)
|
||||
|
||||
return len(obtained_pokemon) >= count
|
||||
def can_get_hidden_items(state, player):
|
||||
return state.has("Item Finder", player) or not state.multiworld.require_item_finder[player].value
|
||||
|
||||
def pokemon_rb_fossil_checks(self, count, player):
|
||||
return (self.can_reach('Mt Moon 1F - Southwest Item', 'Location', player) and
|
||||
self.can_reach('Cinnabar Island - Lab Scientist', 'Location', player) and len(
|
||||
[item for item in ["Dome Fossil", "Helix Fossil", "Old Amber"] if self.has(item, player)]) >= count)
|
||||
|
||||
def pokemon_rb_cinnabar_gym(self, player):
|
||||
# ensures higher level Pokémon are obtainable before Cinnabar Gym is in logic
|
||||
return ((self.multiworld.old_man[player] != "vanilla") or (not self.multiworld.extra_key_items[player]) or
|
||||
self.has("Mansion Key", player) or self.has("Oak's Parcel", player) or self.pokemon_rb_can_surf(player))
|
||||
def has_key_items(state, count, player):
|
||||
key_items = (len([item for item in ["Bicycle", "Silph Scope", "Item Finder", "Super Rod", "Good Rod",
|
||||
"Old Rod", "Lift Key", "Card Key", "Town Map", "Coin Case", "S.S. Ticket",
|
||||
"Secret Key", "Poke Flute", "Mansion Key", "Safari Pass", "Plant Key",
|
||||
"Hideout Key", "Card Key 2F", "Card Key 3F", "Card Key 4F", "Card Key 5F",
|
||||
"Card Key 6F", "Card Key 7F", "Card Key 8F", "Card Key 9F", "Card Key 10F",
|
||||
"Card Key 11F", "Exp. All", "Fire Stone", "Thunder Stone", "Water Stone",
|
||||
"Leaf Stone"] if state.has(item, player)])
|
||||
+ min(state.count("Progressive Card Key", player), 10))
|
||||
return key_items >= count
|
||||
|
||||
def pokemon_rb_dojo(self, player):
|
||||
# ensures higher level Pokémon are obtainable before Fighting Dojo is in logic
|
||||
return (self.pokemon_rb_can_pass_guards(player) or self.has("Oak's Parcel", player) or
|
||||
self.pokemon_rb_can_surf(player))
|
||||
|
||||
def can_pass_guards(state, player):
|
||||
if state.multiworld.tea[player]:
|
||||
return state.has("Tea", player)
|
||||
else:
|
||||
return state.has("Vending Machine Drinks", player)
|
||||
|
||||
|
||||
def has_badges(state, count, player):
|
||||
return len([item for item in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
|
||||
"Soul Badge", "Volcano Badge", "Earth Badge"] if state.has(item, player)]) >= count
|
||||
|
||||
|
||||
def oaks_aide(state, count, player):
|
||||
return ((not state.multiworld.require_pokedex[player] or state.has("Pokedex", player))
|
||||
and has_pokemon(state, count, player))
|
||||
|
||||
|
||||
def has_pokemon(state, count, player):
|
||||
obtained_pokemon = set()
|
||||
for pokemon in poke_data.pokemon_data.keys():
|
||||
if state.has(pokemon, player) or state.has(f"Static {pokemon}", player):
|
||||
obtained_pokemon.add(pokemon)
|
||||
|
||||
return len(obtained_pokemon) >= count
|
||||
|
||||
|
||||
def fossil_checks(state, count, player):
|
||||
return (state.can_reach('Mt Moon B2F', 'Region', player) and
|
||||
state.can_reach('Cinnabar Lab Fossil Room', 'Region', player) and
|
||||
state.can_reach('Cinnabar Island', 'Region', player) and len(
|
||||
[item for item in ["Dome Fossil", "Helix Fossil", "Old Amber"] if state.has(item, player)]) >= count)
|
||||
|
||||
|
||||
def card_key(state, floor, player):
|
||||
return state.has(f"Card Key {floor}F", player) or state.has("Card Key", player) or \
|
||||
state.has("Progressive Card Key", player, floor - 1)
|
||||
|
||||
|
||||
def rock_tunnel(state, player):
|
||||
return can_flash(state, player) or not state.multiworld.dark_rock_tunnel_logic[player]
|
||||
|
||||
|
||||
def route_3(state, player):
|
||||
if state.multiworld.route_3_condition[player] == "defeat_brock":
|
||||
return state.has("Defeat Brock", player)
|
||||
elif state.multiworld.route_3_condition[player] == "defeat_any_gym":
|
||||
return state.has_any(["Defeat Brock", "Defeat Misty", "Defeat Lt. Surge", "Defeat Erika", "Defeat Koga",
|
||||
"Defeat Blaine", "Defeat Sabrina", "Defeat Viridian Gym Giovanni"], player)
|
||||
elif state.multiworld.route_3_condition[player] == "boulder_badge":
|
||||
return state.has("Boulder Badge", player)
|
||||
elif state.multiworld.route_3_condition[player] == "any_badge":
|
||||
return state.has_any(["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge",
|
||||
"Soul Badge", "Volcano Badge", "Earth Badge"], player)
|
||||
# open
|
||||
return True
|
||||
|
||||
|
||||
def evolve_level(state, level, player):
|
||||
return len([item for item in (
|
||||
"Defeat Brock", "Defeat Misty", "Defeat Lt. Surge", "Defeat Erika", "Defeat Koga", "Defeat Blaine",
|
||||
"Defeat Sabrina", "Defeat Viridian Gym Giovanni") if state.has(item, player)]) > level / 7
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
from Options import Toggle, Choice, Range, SpecialRange, TextChoice, DeathLink
|
||||
|
||||
|
||||
|
@ -35,21 +34,49 @@ class Goal(Choice):
|
|||
default = 0
|
||||
|
||||
|
||||
class EliteFourCondition(Range):
|
||||
class EliteFourBadgesCondition(Range):
|
||||
"""Number of badges required to challenge the Elite Four once the Indigo Plateau has been reached.
|
||||
Your rival will reveal the amount needed on the first Route 22 battle (after turning in Oak's Parcel)."""
|
||||
display_name = "Elite Four Condition"
|
||||
display_name = "Elite Four Badges Condition"
|
||||
range_start = 0
|
||||
range_end = 8
|
||||
default = 8
|
||||
|
||||
|
||||
class EliteFourKeyItemsCondition(Range):
|
||||
"""Percentage of available key items (not counting items you can lose) required to challenge the Elite Four. Does
|
||||
not count HMs. Evolution stones and Exp. All are key items in Archipelago."""
|
||||
display_name = "Elite Four Key Items Condition"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
total = 0
|
||||
|
||||
|
||||
class EliteFourPokedexCondition(Range):
|
||||
"""Percentage of logically-reachable Pokemon that must be registered as "owned" in the Pokedex in order to
|
||||
challenge the Elite Four."""
|
||||
display_name = "Elite Four Pokedex Condition"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
total = 0
|
||||
|
||||
|
||||
class VictoryRoadCondition(Range):
|
||||
"""Number of badges required to reach Victory Road."""
|
||||
display_name = "Victory Road Condition"
|
||||
"""Number of badges required to reach the front entrance of Victory Road."""
|
||||
display_name = "Route 23 Condition"
|
||||
range_start = 0
|
||||
range_end = 8
|
||||
default = 8
|
||||
default = 7
|
||||
|
||||
|
||||
class Route22GateCondition(Range):
|
||||
"""Number of badges required to pass through the Route 22 Gate"""
|
||||
display_name = "Route 22 Gate Condition"
|
||||
range_start = 0
|
||||
range_end = 7
|
||||
default = 7
|
||||
|
||||
|
||||
class ViridianGymCondition(Range):
|
||||
|
@ -60,13 +87,40 @@ class ViridianGymCondition(Range):
|
|||
default = 7
|
||||
|
||||
|
||||
class CeruleanCaveCondition(Range):
|
||||
"""Number of badges, HMs, and key items (not counting items you can lose) required to access Cerulean Cave.
|
||||
If extra_key_items is turned on, the number chosen will be increased by 4."""
|
||||
display_name = "Cerulean Cave Condition"
|
||||
class CeruleanCaveBadgesCondition(Range):
|
||||
"""Number of badges needed to access the Cerulean Cave entrance in addition to the required Key Items."""
|
||||
display_name = "Cerulean Cave Badges Condition"
|
||||
range_start = 0
|
||||
range_end = 26
|
||||
default = 20
|
||||
range_end = 8
|
||||
default = 4
|
||||
|
||||
|
||||
class CeruleanCaveKeyItemsCondition(Range):
|
||||
"""Percentage of available key items (not counting items you can lose) required to access the Cerulean Cave
|
||||
entrance in addition to the required badges. Does not count HMs.
|
||||
Evolution stones and Exp. All are key items in Archipelago."""
|
||||
display_name = "Cerulean Cave Key Items Condition"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 50
|
||||
total = 0
|
||||
|
||||
|
||||
class Route3Condition(Choice):
|
||||
"""Set a condition to pass through from Pewter City to Route 3."""
|
||||
display_name = "Route 3 Condition"
|
||||
option_open = 0
|
||||
option_defeat_brock = 1
|
||||
option_defeat_any_gym = 2
|
||||
option_boulder_badge = 3
|
||||
option_any_badge = 4
|
||||
default = 1
|
||||
|
||||
|
||||
class RobbedHouseOfficer(Toggle):
|
||||
"""You can disable to remove the requirement to help Bill before you can enter the robbed house in Cerulean City."""
|
||||
display_name = "Robbed House Officer"
|
||||
default = 1
|
||||
|
||||
|
||||
class SecondFossilCheckCondition(Range):
|
||||
|
@ -78,6 +132,18 @@ class SecondFossilCheckCondition(Range):
|
|||
default = 3
|
||||
|
||||
|
||||
class FossilCheckItemTypes(Choice):
|
||||
"""The two fossil checks always contain items for your own game. Here, you can choose what types of items can
|
||||
appear. Key Items means only advancement items can appear. Unique means key items or TMs may appear. No Key Items
|
||||
means no advancement items may appear."""
|
||||
display_name = "Fossil Check Item Types"
|
||||
option_any = 0
|
||||
option_key_items = 1
|
||||
option_unique_items = 2
|
||||
option_no_key_items = 3
|
||||
default = 0
|
||||
|
||||
|
||||
class BadgeSanity(Toggle):
|
||||
"""Shuffle gym badges into the general item pool. If turned off, badges will be shuffled across the 8 gyms."""
|
||||
display_name = "Badgesanity"
|
||||
|
@ -108,8 +174,18 @@ class OldMan(Choice):
|
|||
default = 1
|
||||
|
||||
|
||||
class ExpAll(Choice):
|
||||
"""Choose how the Exp. All item is handled. It can be removed entirely, shuffled into the item pool, or you can
|
||||
start with it."""
|
||||
display_name = "Exp. All"
|
||||
option_remove = 0
|
||||
option_randomize = 1
|
||||
option_start_with = 2
|
||||
default = 1
|
||||
|
||||
|
||||
class RandomizePokedex(Choice):
|
||||
"""Randomize the location of the Pokedex, or start with it. It is required to receive items from Oak's Aides."""
|
||||
"""Randomize the location of the Pokedex, or start with it."""
|
||||
display_name = "Randomize Pokedex"
|
||||
option_vanilla = 0
|
||||
option_randomize = 1
|
||||
|
@ -117,6 +193,14 @@ class RandomizePokedex(Choice):
|
|||
default = 0
|
||||
|
||||
|
||||
class KeyItemsOnly(Toggle):
|
||||
"""Shuffle only Key Items. This overrides Randomize Hidden Items, Trainersanity, and Dexsanity.
|
||||
Sets all non-excluded locations in your game to Priority Locations.
|
||||
May have high generation failure rates for solo games or small multiworlds, especially with Door Shuffle."""
|
||||
display_name = "Key Items Only"
|
||||
default = 0
|
||||
|
||||
|
||||
class Tea(Toggle):
|
||||
"""Adds a Tea item to the item pool which the Saffron guards require instead of the vending machine drinks.
|
||||
Adds a location check to the Celadon Mansion 1F, where Tea is acquired in FireRed and LeafGreen."""
|
||||
|
@ -131,6 +215,24 @@ class ExtraKeyItems(Toggle):
|
|||
default = 0
|
||||
|
||||
|
||||
class SplitCardKey(Choice):
|
||||
"""Splits the Card Key item into 10 different Keys, one for each Silph Co floor 2F through 11F.
|
||||
Adds location checks to 9 NPCs in Silph Co.
|
||||
With Progressive, you will always obtain the keys in order from 2F to 11F."""
|
||||
display_name = "Split Card Key"
|
||||
option_off = 0
|
||||
option_on = 1
|
||||
option_progressive = 2
|
||||
default = 0
|
||||
|
||||
|
||||
class AllElevatorsLocked(Toggle):
|
||||
"""Adds requirements to the Celadon Department Store elevator and Silph Co elevators to have the Lift Key.
|
||||
No logical implications normally, but may have a significant impact on Insanity Door Shuffle."""
|
||||
display_name = "All Elevators Locked"
|
||||
default = 1
|
||||
|
||||
|
||||
class ExtraStrengthBoulders(Toggle):
|
||||
"""Adds Strength Boulders blocking the Route 11 gate, and in Route 13 (can be bypassed with Surf).
|
||||
This potentially increases the usefulness of Strength as well as the Bicycle."""
|
||||
|
@ -177,42 +279,95 @@ class RequirePokedex(Toggle):
|
|||
|
||||
class AllPokemonSeen(Toggle):
|
||||
"""Start with all Pokemon "seen" in your Pokedex. This allows you to see where Pokemon can be encountered in the
|
||||
wild. Pokemon found by fishing or in the Cerulean Cave are not displayed."""
|
||||
wild. Pokemon found by fishing or in the Cerulean Cave are not displayed.
|
||||
The Pokedex also shows which HMs can be learned by Pokemon registered as seen."""
|
||||
default = 0
|
||||
display_name = "All Pokemon Seen"
|
||||
|
||||
|
||||
class DexSanity(Toggle):
|
||||
"""Adds a location check for each Pokemon flagged "Owned" on your Pokedex. If accessibility is set to `locations`
|
||||
and randomize_wild_pokemon is off, catch_em_all is not `all_pokemon` or randomize_legendary_pokemon is not `any`,
|
||||
accessibility will be forced to `items` instead, as not all Dexsanity locations can be guaranteed to be considered
|
||||
reachable in logic.
|
||||
class DexSanity(SpecialRange):
|
||||
"""Adds location checks for Pokemon flagged "owned" on your Pokedex. You may specify a percentage of Pokemon to
|
||||
have checks added. If Accessibility is set to locations, this will be the percentage of all logically reachable
|
||||
Pokemon that will get a location check added to it. With items or minimal Accessibility, it will be the percentage
|
||||
of all 151 Pokemon.
|
||||
If Pokedex is required, the items for Pokemon acquired before acquiring the Pokedex can be found by talking to
|
||||
Professor Oak or evaluating the Pokedex via Oak's PC."""
|
||||
display_name = "Dexsanity"
|
||||
default = 0
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
special_range_names = {
|
||||
"false": 0,
|
||||
"true": 100
|
||||
}
|
||||
|
||||
|
||||
class FreeFlyLocation(Toggle):
|
||||
"""One random fly destination will be unlocked by default."""
|
||||
"""One random Fly destination will be unlocked by default."""
|
||||
display_name = "Free Fly Location"
|
||||
default = 1
|
||||
|
||||
|
||||
class TownMapFlyLocation(Toggle):
|
||||
"""One random Fly destination will be unlocked when you obtain the Town Map."""
|
||||
display_name = "Town Map Fly Location"
|
||||
default = 0
|
||||
|
||||
|
||||
class DoorShuffle(Choice):
|
||||
"""Simple: entrances are randomized together in groups: Pokemarts, Gyms, single exit dungeons, dual exit dungeons,
|
||||
single exit misc interiors, dual exit misc interiors are all shuffled separately. Safari Zone is not shuffled.
|
||||
Full: Any outdoor entrance may lead to any interior.
|
||||
Insanity: All rooms in the game are shuffled."""
|
||||
display_name = "Door Shuffle"
|
||||
option_off = 0
|
||||
option_simple = 1
|
||||
option_full = 2
|
||||
option_insanity = 3
|
||||
# Disabled for now, has issues with elevators that need to be resolved
|
||||
# option_decoupled = 4
|
||||
default = 0
|
||||
|
||||
# remove assertions that blow up checks for decoupled
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return other.value == self.value
|
||||
elif isinstance(other, str):
|
||||
return other == self.current_key
|
||||
elif isinstance(other, int):
|
||||
return other == self.value
|
||||
elif isinstance(other, bool):
|
||||
return other == bool(self.value)
|
||||
else:
|
||||
raise TypeError(f"Can't compare {self.__class__.__name__} with {other.__class__.__name__}")
|
||||
|
||||
|
||||
class WarpTileShuffle(Toggle):
|
||||
"""Shuffle the warp tiles in Silph Co and Sabrina's Gym among themselves, separately.
|
||||
On Insanity, turning this off means they are mixed into the general door shuffle instead of only being shuffled
|
||||
among themselves."""
|
||||
display_name = "Warp Tile Shuffle"
|
||||
default = 0
|
||||
|
||||
|
||||
class RandomizeRockTunnel(Toggle):
|
||||
"""Randomize the layout of Rock Tunnel. This is highly experimental, if you encounter any issues (items or trainers
|
||||
unreachable, trainers walking over walls, inability to reach end of tunnel, anything looking strange) to
|
||||
Alchav#8826 in the Archipelago Discord (directly or in #pkmn-red-blue) along with the seed number found on the
|
||||
signs outside the tunnel."""
|
||||
"""Randomize the layout of Rock Tunnel.
|
||||
If Insanity Door Shuffle is on, this will cause only the main entrances to Rock Tunnel to be shuffled."""
|
||||
display_name = "Randomize Rock Tunnel"
|
||||
default = 0
|
||||
|
||||
|
||||
class DarkRockTunnelLogic(Toggle):
|
||||
"""Logically require Flash to traverse the Rock Tunnel, so you are never forced to traverse it in the dark."""
|
||||
display_name = "Dark Rock Tunnel Logic"
|
||||
default = 1
|
||||
|
||||
|
||||
class OaksAidRt2(Range):
|
||||
"""Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 2.
|
||||
Vanilla is 10."""
|
||||
display_name = "Oak's Aide Route 2"
|
||||
range_start = 0
|
||||
range_start = 1
|
||||
range_end = 80
|
||||
default = 10
|
||||
|
||||
|
@ -221,7 +376,7 @@ class OaksAidRt11(Range):
|
|||
"""Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 11.
|
||||
Vanilla is 30."""
|
||||
display_name = "Oak's Aide Route 11"
|
||||
range_start = 0
|
||||
range_start = 1
|
||||
range_end = 80
|
||||
default = 20
|
||||
|
||||
|
@ -230,17 +385,39 @@ class OaksAidRt15(Range):
|
|||
"""Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 15.
|
||||
Vanilla is 50."""
|
||||
display_name = "Oak's Aide Route 15"
|
||||
range_start = 0
|
||||
range_start = 1
|
||||
range_end = 80
|
||||
default = 30
|
||||
|
||||
|
||||
class Stonesanity(Toggle):
|
||||
"""Removes the four evolution stones from the Celadon Department Store and replaces four of the five Moon Stones
|
||||
in the item pool with the four shop stones. If randomize_hidden_items is off, this will cause the two hidden
|
||||
Moon Stone locations to be randomized anyway. These are in Pokemon Mansion 1F and Mt Moon B2F."""
|
||||
display_name = "Stonesanity"
|
||||
default = 0
|
||||
|
||||
|
||||
class LevelScaling(Choice):
|
||||
"""Off: Encounters use vanilla game levels.
|
||||
By Spheres: Levels are scaled by access sphere. Areas reachable in later spheres will have higher levels.
|
||||
Spheres and Distance: Levels are scaled by access spheres as well as distance from Pallet Town, measured by number
|
||||
of internal region connections. This is a much more severe curving of levels and may lead to much less variation in
|
||||
levels found in a particular map. However, it may make the higher door shuffle settings significantly more bearable,
|
||||
as these options more often result in a smaller number of larger access spheres."""
|
||||
display_name = "Level Scaling"
|
||||
option_off = 0
|
||||
option_by_spheres = 1
|
||||
option_by_spheres_and_distance = 2
|
||||
default = 1
|
||||
|
||||
|
||||
class ExpModifier(SpecialRange):
|
||||
"""Modifier for EXP gained. When specifying a number, exp is multiplied by this amount and divided by 16."""
|
||||
display_name = "Exp Modifier"
|
||||
range_start = 0
|
||||
range_end = 255
|
||||
default = 16
|
||||
range_start = default / 4
|
||||
range_end = 255
|
||||
special_range_names = {
|
||||
"half": default / 2,
|
||||
"normal": default,
|
||||
|
@ -351,7 +528,7 @@ class MinimumStepsBetweenEncounters(Range):
|
|||
"""Minimum number of steps between wild Pokemon encounters."""
|
||||
display_name = "Minimum Steps Between Encounters"
|
||||
default = 3
|
||||
range_start = 0
|
||||
range_start = 1
|
||||
range_end = 255
|
||||
|
||||
|
||||
|
@ -387,6 +564,14 @@ class MoveBalancing(Toggle):
|
|||
default = 0
|
||||
|
||||
|
||||
class FixCombatBugs(Toggle):
|
||||
"""Fixes a variety of combat-related bugs. Note that this fixes the Focus Energy bug. The Focus Energy bug causes
|
||||
critical strike chances to be doubled when Focus Energy has not been used and halved when it is used.
|
||||
Fixing this bug means critical strike chances outside the use of Focus Energy are quartered from the vanilla rate."""
|
||||
display_name = "Fix Combat Bugs"
|
||||
default = 1
|
||||
|
||||
|
||||
class RandomizePokemonMovesets(Choice):
|
||||
"""Randomize the moves learned by Pokemon. prefer_types will prefer moves that match the type of the Pokemon."""
|
||||
display_name = "Randomize Pokemon Movesets"
|
||||
|
@ -402,6 +587,7 @@ class ConfineTranstormToDitto(Toggle):
|
|||
display_name = "Confine Transform to Ditto"
|
||||
default = 1
|
||||
|
||||
|
||||
class StartWithFourMoves(Toggle):
|
||||
"""If movesets are randomized, this will give all Pokemon 4 starting moves."""
|
||||
display_name = "Start With Four Moves"
|
||||
|
@ -483,6 +669,12 @@ class RandomizePokemonTypes(Choice):
|
|||
default = 0
|
||||
|
||||
|
||||
class RandomizeMoveTypes(Toggle):
|
||||
"""Randomize the types of each move."""
|
||||
display_name = "Randomize Move Types"
|
||||
default = 0
|
||||
|
||||
|
||||
class SecondaryTypeChance(SpecialRange):
|
||||
"""If randomize_pokemon_types is on, this is the chance each Pokemon will have a secondary type. If follow_evolutions
|
||||
is selected, it is the chance a second type will be added at each evolution stage. vanilla will give secondary types
|
||||
|
@ -511,7 +703,7 @@ class RandomizeTypeChart(Choice):
|
|||
|
||||
|
||||
class NormalMatchups(Range):
|
||||
"""If 'randomize' is chosen for randomize_type_chart, this will be the weight for neutral matchups.
|
||||
"""If 'randomize' is chosen for Randomize Type Chart, this will be the weight for neutral matchups.
|
||||
No effect if 'chaos' is chosen"""
|
||||
display_name = "Normal Matchups"
|
||||
default = 143
|
||||
|
@ -520,7 +712,7 @@ class NormalMatchups(Range):
|
|||
|
||||
|
||||
class SuperEffectiveMatchups(Range):
|
||||
"""If 'randomize' is chosen for randomize_type_chart, this will be the weight for super effective matchups.
|
||||
"""If 'randomize' is chosen for Randomize Type Chart, this will be the weight for super effective matchups.
|
||||
No effect if 'chaos' is chosen"""
|
||||
display_name = "Super Effective Matchups"
|
||||
default = 38
|
||||
|
@ -529,7 +721,7 @@ class SuperEffectiveMatchups(Range):
|
|||
|
||||
|
||||
class NotVeryEffectiveMatchups(Range):
|
||||
"""If 'randomize' is chosen for randomize_type_chart, this will be the weight for not very effective matchups.
|
||||
"""If 'randomize' is chosen for Randomize Type Chart, this will be the weight for not very effective matchups.
|
||||
No effect if 'chaos' is chosen"""
|
||||
display_name = "Not Very Effective Matchups"
|
||||
default = 38
|
||||
|
@ -538,7 +730,7 @@ class NotVeryEffectiveMatchups(Range):
|
|||
|
||||
|
||||
class ImmunityMatchups(Range):
|
||||
"""If 'randomize' is chosen for randomize_type_chart, this will be the exact number of immunities.
|
||||
"""If 'randomize' is chosen for Randomize Type Chart, this will be the exact number of immunities.
|
||||
No effect if 'chaos' is chosen"""
|
||||
display_name = "Immunity Matchups"
|
||||
default = 6
|
||||
|
@ -547,7 +739,7 @@ class ImmunityMatchups(Range):
|
|||
|
||||
|
||||
class SafariZoneNormalBattles(Toggle):
|
||||
"""Change the Safari Zone to have standard wild pokemon battles."""
|
||||
"""Change the Safari Zone to have standard wild Pokemon battles."""
|
||||
display_name = "Safari Zone Normal Battles"
|
||||
default = 0
|
||||
|
||||
|
@ -576,7 +768,7 @@ class BetterShops(Choice):
|
|||
|
||||
|
||||
class MasterBallPrice(Range):
|
||||
"""Price for Master Balls. Can only be bought if better_shops is set to add_master_ball, but this will affect the
|
||||
"""Price for Master Balls. Can only be bought if Better Shops is set to Add Master Ball, but this will affect the
|
||||
sell price regardless. Vanilla is 0"""
|
||||
display_name = "Master Ball Price"
|
||||
range_end = 999999
|
||||
|
@ -628,12 +820,33 @@ class ParalyzeTrapWeight(TrapWeight):
|
|||
display_name = "Paralyze Trap Weight"
|
||||
|
||||
|
||||
class SleepTrapWeight(TrapWeight):
|
||||
"""Weights for Sleep Traps. These apply the Sleep status to all your party members, for randomly between 1 and 7 turns."""
|
||||
display_name = "Sleep Trap Weight"
|
||||
|
||||
|
||||
class IceTrapWeight(TrapWeight):
|
||||
"""Weights for Ice Traps. These apply the Ice status to all your party members. Don't forget to buy Ice Heals!"""
|
||||
display_name = "Ice Trap Weight"
|
||||
default = 0
|
||||
|
||||
|
||||
class PokeDollSkip(Choice):
|
||||
"""Patch out the Pokemon Tower Poke Doll skip or have this skip considered in logic."""
|
||||
display_name = "Poke Doll Skip"
|
||||
option_patched = 0
|
||||
option_in_logic = 1
|
||||
default = 0
|
||||
|
||||
|
||||
class BicycleGateSkips(Choice):
|
||||
"""Patch out the Route 16/18 Bicycle Gate skips or have these skips considered in logic."""
|
||||
display_name = "Bicycle Gate Skips"
|
||||
option_patched = 0
|
||||
option_in_logic = 1
|
||||
default = 0
|
||||
|
||||
|
||||
class RandomizePokemonPalettes(Choice):
|
||||
"""Modify palettes of Pokemon. Primary Type will set Pokemons' palettes based on their primary type, Follow
|
||||
Evolutions will randomize palettes but palettes will remain the same through evolutions (except Eeveelutions),
|
||||
|
@ -650,32 +863,49 @@ pokemon_rb_options = {
|
|||
"trainer_name": TrainerName,
|
||||
"rival_name": RivalName,
|
||||
#"goal": Goal,
|
||||
"elite_four_condition": EliteFourCondition,
|
||||
"elite_four_badges_condition": EliteFourBadgesCondition,
|
||||
"elite_four_key_items_condition": EliteFourKeyItemsCondition,
|
||||
"elite_four_pokedex_condition": EliteFourPokedexCondition,
|
||||
"victory_road_condition": VictoryRoadCondition,
|
||||
"route_22_gate_condition": Route22GateCondition,
|
||||
"viridian_gym_condition": ViridianGymCondition,
|
||||
"cerulean_cave_condition": CeruleanCaveCondition,
|
||||
"cerulean_cave_badges_condition": CeruleanCaveBadgesCondition,
|
||||
"cerulean_cave_key_items_condition": CeruleanCaveKeyItemsCondition,
|
||||
"route_3_condition": Route3Condition,
|
||||
"robbed_house_officer": RobbedHouseOfficer,
|
||||
"second_fossil_check_condition": SecondFossilCheckCondition,
|
||||
"badgesanity": BadgeSanity,
|
||||
"fossil_check_item_types": FossilCheckItemTypes,
|
||||
"exp_all": ExpAll,
|
||||
"old_man": OldMan,
|
||||
"randomize_pokedex": RandomizePokedex,
|
||||
"badgesanity": BadgeSanity,
|
||||
"badges_needed_for_hm_moves": BadgesNeededForHMMoves,
|
||||
"key_items_only": KeyItemsOnly,
|
||||
"tea": Tea,
|
||||
"extra_key_items": ExtraKeyItems,
|
||||
"split_card_key": SplitCardKey,
|
||||
"all_elevators_locked": AllElevatorsLocked,
|
||||
"extra_strength_boulders": ExtraStrengthBoulders,
|
||||
"require_item_finder": RequireItemFinder,
|
||||
"randomize_hidden_items": RandomizeHiddenItems,
|
||||
"prizesanity": PrizeSanity,
|
||||
"trainersanity": TrainerSanity,
|
||||
"dexsanity": DexSanity,
|
||||
"randomize_pokedex": RandomizePokedex,
|
||||
"require_pokedex": RequirePokedex,
|
||||
"all_pokemon_seen": AllPokemonSeen,
|
||||
"dexsanity": DexSanity,
|
||||
"oaks_aide_rt_2": OaksAidRt2,
|
||||
"oaks_aide_rt_11": OaksAidRt11,
|
||||
"oaks_aide_rt_15": OaksAidRt15,
|
||||
"badges_needed_for_hm_moves": BadgesNeededForHMMoves,
|
||||
"free_fly_location": FreeFlyLocation,
|
||||
"stonesanity": Stonesanity,
|
||||
"door_shuffle": DoorShuffle,
|
||||
"warp_tile_shuffle": WarpTileShuffle,
|
||||
"randomize_rock_tunnel": RandomizeRockTunnel,
|
||||
"dark_rock_tunnel_logic": DarkRockTunnelLogic,
|
||||
"free_fly_location": FreeFlyLocation,
|
||||
"town_map_fly_location": TownMapFlyLocation,
|
||||
"blind_trainers": BlindTrainers,
|
||||
"minimum_steps_between_encounters": MinimumStepsBetweenEncounters,
|
||||
"level_scaling": LevelScaling,
|
||||
"exp_modifier": ExpModifier,
|
||||
"randomize_wild_pokemon": RandomizeWildPokemon,
|
||||
"area_1_to_1_mapping": Area1To1Mapping,
|
||||
|
@ -689,6 +919,7 @@ pokemon_rb_options = {
|
|||
"randomize_trainer_parties": RandomizeTrainerParties,
|
||||
"trainer_legendaries": TrainerLegendaries,
|
||||
"move_balancing": MoveBalancing,
|
||||
"fix_combat_bugs": FixCombatBugs,
|
||||
"randomize_pokemon_movesets": RandomizePokemonMovesets,
|
||||
"confine_transform_to_ditto": ConfineTranstormToDitto,
|
||||
"start_with_four_moves": StartWithFourMoves,
|
||||
|
@ -701,6 +932,7 @@ pokemon_rb_options = {
|
|||
"hm_normal_type_compatibility": HMNormalTypeCompatibility,
|
||||
"hm_other_type_compatibility": HMOtherTypeCompatibility,
|
||||
"inherit_tm_hm_compatibility": InheritTMHMCompatibility,
|
||||
"randomize_move_types": RandomizeMoveTypes,
|
||||
"randomize_pokemon_types": RandomizePokemonTypes,
|
||||
"secondary_type_chance": SecondaryTypeChance,
|
||||
"randomize_type_chart": RandomizeTypeChart,
|
||||
|
@ -715,11 +947,14 @@ pokemon_rb_options = {
|
|||
"master_ball_price": MasterBallPrice,
|
||||
"starting_money": StartingMoney,
|
||||
"lose_money_on_blackout": LoseMoneyOnBlackout,
|
||||
"poke_doll_skip": PokeDollSkip,
|
||||
"bicycle_gate_skips": BicycleGateSkips,
|
||||
"trap_percentage": TrapPercentage,
|
||||
"poison_trap_weight": PoisonTrapWeight,
|
||||
"fire_trap_weight": FireTrapWeight,
|
||||
"paralyze_trap_weight": ParalyzeTrapWeight,
|
||||
"sleep_trap_weight": SleepTrapWeight,
|
||||
"ice_trap_weight": IceTrapWeight,
|
||||
"randomize_pokemon_palettes": RandomizePokemonPalettes,
|
||||
"death_link": DeathLink
|
||||
}
|
||||
}
|
|
@ -1204,28 +1204,17 @@ tm_moves = [
|
|||
'Selfdestruct', 'Egg Bomb', 'Fire Blast', 'Swift', 'Skull Bash', 'Softboiled', 'Dream Eater', 'Sky Attack', 'Rest',
|
||||
'Thunder Wave', 'Psywave', 'Explosion', 'Rock Slide', 'Tri Attack', 'Substitute'
|
||||
]
|
||||
#['No Move', 'Pound', 'Karate Chop', 'Doubleslap', 'Comet Punch', 'Fire Punch', 'Ice Punch', 'Thunderpunch', 'Scratch',
|
||||
# 'Vicegrip', 'Guillotine', 'Cut', 'Gust', 'Wing Attack', 'Fly', 'Bind', 'Slam', 'Vine Whip', 'Stomp', 'Double Kick', 'Jump Kick',
|
||||
# 'Rolling Kick', 'Sand Attack', 'Headbutt', 'Horn Attack', 'Fury Attack', 'Tackle', 'Wrap', 'Thrash', 'Tail Whip', 'Poison Sting',
|
||||
# 'Twineedle', 'Pin Missile', 'Leer', 'Bite', 'Growl', 'Roar', 'Sing', 'Supersonic', 'Sonicboom', 'Disable', 'Acid', 'Ember', 'Flamethrower',
|
||||
# 'Mist', 'Hydro Pump', 'Surf', 'Psybeam', 'Aurora Beam', 'Peck', 'Drill Peck', 'Low Kick', 'Strength', 'Absorb', 'Leech Seed', 'Growth',
|
||||
# 'Razor Leaf', 'Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Petal Dance', 'String Shot', 'Fire Spin', 'Thundershock', 'Rock Throw', 'Confusion',
|
||||
# 'Hypnosis', 'Meditate', 'Agility', 'Quick Attack', 'Night Shade', 'Screech', 'Recover', 'Harden', 'Minimize', 'Smokescreen', 'Confuse Ray', 'Withdraw',
|
||||
# 'Defense Curl', 'Barrier', 'Light Screen', 'Haze', 'Focus Energy', 'Mirror Move', 'Lick', 'Smog', 'Sludge', 'Bone Club', 'Waterfall', 'Clamp', 'Spike Cannon',
|
||||
# 'Constrict', 'Amnesia', 'Kinesis', 'Hi Jump Kick', 'Glare', 'Poison Gas', 'Barrage', 'Leech Life', 'Lovely Kiss', 'Transform', 'Bubble', 'Dizzy Punch', 'Spore', 'Flash',
|
||||
# 'Splash', 'Acid Armor', 'Crabhammer', 'Fury Swipes', 'Bonemerang', 'Hyper Fang', 'Sharpen', 'Conversion', 'Super Fang', 'Slash']
|
||||
|
||||
# print([i for i in list(moves.keys()) if i not in tm_moves])
|
||||
# filler_moves = [
|
||||
# "Razor Wind", "Whirlwind", "Counter", "Teleport", "Bide", "Skull Bash", "Sky Attack", "Psywave",
|
||||
# "Pound", "Karate Chop", "Doubleslap", "Comet Punch", "Scratch", "Vicegrip", "Gust", "Wing Attack", "Bind",
|
||||
# "Vine Whip", "Sand Attack", "Fury Attack", "Tackle", "Wrap", "Tail Whip", "Poison Sting", "Twineedle",
|
||||
# "Leer", "Growl", "Roar", "Sing", "Supersonic", "Sonicboom", "Disable", "Acid", "Ember", "Mist", "Peck", "Absorb",
|
||||
# "Growth", "Poisonpowder", "String Shot", "Meditate", "Agility", "Screech", "Double Team", "Harden", "Minimize",
|
||||
# "Smokescreen", "Confuse Ray", "Withdraw", "Defense Curl", "Barrier", "Light Screen", "Haze", "Reflect",
|
||||
# "Focus Energy", "Lick", "Smog", "Clamp", "Spike Cannon", "Constrict"
|
||||
#
|
||||
# ]
|
||||
evolution_levels = {
|
||||
'Bulbasaur': 16, 'Ivysaur': 32, 'Charmander': 16, 'Charmeleon': 36, 'Squirtle': 16, 'Wartortle': 36, 'Caterpie': 7,
|
||||
'Metapod': 10, 'Weedle': 7, 'Kakuna': 10, 'Pidgey': 18, 'Pidgeotto': 36, 'Rattata': 20, 'Spearow': 20, 'Ekans': 22,
|
||||
'Sandshrew': 22, 'Nidoran F': 16, 'Nidoran M': 16, 'Zubat': 22, 'Oddish': 21, 'Paras': 24, 'Venonat': 31,
|
||||
'Diglett': 26, 'Meowth': 28, 'Psyduck': 33, 'Mankey': 28, 'Poliwag': 25, 'Abra': 16, 'Kadabra': 35, 'Machop': 28,
|
||||
'Machoke': 35, 'Bellsprout': 21, 'Tentacool': 30, 'Geodude': 25, 'Graveler': 35, 'Ponyta': 40, 'Slowpoke': 37,
|
||||
'Magnemite': 30, 'Doduo': 31, 'Seel': 34, 'Grimer': 38, 'Gastly': 25, 'Haunter': 35, 'Drowzee': 26, 'Krabby': 28,
|
||||
'Voltorb': 30, 'Cubone': 28, 'Koffing': 35, 'Rhyhorn': 42, 'Horsea': 32, 'Goldeen': 33, 'Magikarp': 33,
|
||||
'Omanyte': 40, 'Kabuto': 40, 'Dratini': 30, 'Dragonair': 55
|
||||
}
|
||||
|
||||
|
||||
first_stage_pokemon = [pokemon for pokemon in pokemon_data.keys() if pokemon not in evolves_from]
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
from copy import deepcopy
|
||||
from . import poke_data
|
||||
from .rom_addresses import rom_addresses
|
||||
|
||||
|
||||
def set_mon_palettes(self, random, data):
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "vanilla":
|
||||
return
|
||||
pallet_map = {
|
||||
"Poison": 0x0F,
|
||||
"Normal": 0x10,
|
||||
"Ice": 0x11,
|
||||
"Fire": 0x12,
|
||||
"Water": 0x13,
|
||||
"Ghost": 0x14,
|
||||
"Ground": 0x15,
|
||||
"Grass": 0x16,
|
||||
"Psychic": 0x17,
|
||||
"Electric": 0x18,
|
||||
"Rock": 0x19,
|
||||
"Dragon": 0x1F,
|
||||
"Flying": 0x20,
|
||||
"Fighting": 0x21,
|
||||
"Bug": 0x22
|
||||
}
|
||||
palettes = []
|
||||
for mon in poke_data.pokemon_data:
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "primary_type":
|
||||
pallet = pallet_map[self.local_poke_data[mon]["type1"]]
|
||||
elif (self.multiworld.randomize_pokemon_palettes[self.player] == "follow_evolutions" and mon in
|
||||
poke_data.evolves_from and poke_data.evolves_from[mon] != "Eevee"):
|
||||
pallet = palettes[-1]
|
||||
else: # completely_random or follow_evolutions and it is not an evolved form (except eeveelutions)
|
||||
pallet = random.choice(list(pallet_map.values()))
|
||||
palettes.append(pallet)
|
||||
address = rom_addresses["Mon_Palettes"]
|
||||
for pallet in palettes:
|
||||
data[address] = pallet
|
||||
address += 1
|
||||
|
||||
|
||||
def choose_forced_type(chances, random):
|
||||
n = random.randint(1, 100)
|
||||
for chance in chances:
|
||||
if chance[0] >= n:
|
||||
return chance[1]
|
||||
return None
|
||||
|
||||
|
||||
def filter_moves(local_move_data, moves, type, random):
|
||||
ret = []
|
||||
for move in moves:
|
||||
if local_move_data[move]["type"] == type or type is None:
|
||||
ret.append(move)
|
||||
random.shuffle(ret)
|
||||
return ret
|
||||
|
||||
|
||||
def get_move(local_move_data, moves, chances, random, starting_move=False):
|
||||
type = choose_forced_type(chances, random)
|
||||
filtered_moves = filter_moves(local_move_data, moves, type, random)
|
||||
for move in filtered_moves:
|
||||
if (not starting_move) or (local_move_data[move]["accuracy"] > 80 and local_move_data[move]["power"] > 0):
|
||||
moves.remove(move)
|
||||
return move
|
||||
else:
|
||||
return get_move(local_move_data, moves, [], random, starting_move)
|
||||
|
||||
|
||||
def move_power(move_data):
|
||||
power = move_data["power"]
|
||||
if move_data["effect"] in (29, 42):
|
||||
# 29: two-to-five attacks. 42: trapping effect, two-to-five turns.
|
||||
power *= 3
|
||||
elif move_data["effect"] in (77, 44):
|
||||
# 77: Twineedle. Two attacks and poison chance. 44: Just two attacks
|
||||
power *= 2
|
||||
elif move_data["effect"] == 48:
|
||||
# 25% recoil damage taken. Reduce power considered by that amount
|
||||
power *= 0.75
|
||||
elif move_data["effect"] == 3:
|
||||
# 50% absorb. Increase power considered by that amount
|
||||
power *= 1.5
|
||||
elif move_data["effect"] == 39 and move_data["id"] != 91:
|
||||
# Takes two turns while vulnerable. Dig uses this effect ID but is semi-invulnerable
|
||||
power *= 0.66
|
||||
elif move_data["effect"] == 7:
|
||||
# Faint user
|
||||
power *= 0.5
|
||||
elif move_data["id"] in (2, 75, 152, 163,):
|
||||
# High critical strike moves: Karate Chop, Razor Leaf, Crabhammer, Slash
|
||||
power *= 2
|
||||
return power
|
||||
|
||||
|
||||
def process_move_data(self):
|
||||
self.local_move_data = deepcopy(poke_data.moves)
|
||||
|
||||
if self.multiworld.randomize_move_types[self.player]:
|
||||
for move, data in self.local_move_data.items():
|
||||
if move == "No Move":
|
||||
continue
|
||||
# The chance of randomized moves choosing a normal type move is high, so we want to retain having a higher
|
||||
# rate of normal type moves
|
||||
data["type"] = self.multiworld.random.choice(list(poke_data.type_ids) + (["Normal"] * 4))
|
||||
|
||||
if self.multiworld.move_balancing[self.player]:
|
||||
self.local_move_data["Sing"]["accuracy"] = 30
|
||||
self.local_move_data["Sleep Powder"]["accuracy"] = 40
|
||||
self.local_move_data["Spore"]["accuracy"] = 50
|
||||
self.local_move_data["Sonicboom"]["effect"] = 0
|
||||
self.local_move_data["Sonicboom"]["power"] = 50
|
||||
self.local_move_data["Dragon Rage"]["effect"] = 0
|
||||
self.local_move_data["Dragon Rage"]["power"] = 80
|
||||
self.local_move_data["Horn Drill"]["effect"] = 0
|
||||
self.local_move_data["Horn Drill"]["power"] = 70
|
||||
self.local_move_data["Horn Drill"]["accuracy"] = 90
|
||||
self.local_move_data["Guillotine"]["effect"] = 0
|
||||
self.local_move_data["Guillotine"]["power"] = 70
|
||||
self.local_move_data["Guillotine"]["accuracy"] = 90
|
||||
self.local_move_data["Fissure"]["effect"] = 0
|
||||
self.local_move_data["Fissure"]["power"] = 70
|
||||
self.local_move_data["Fissure"]["accuracy"] = 90
|
||||
self.local_move_data["Blizzard"]["accuracy"] = 70
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
self.local_tms = self.multiworld.random.sample([move for move in poke_data.moves.keys() if move not in
|
||||
["No Move"] + poke_data.hm_moves], 50)
|
||||
else:
|
||||
self.local_tms = poke_data.tm_moves.copy()
|
||||
|
||||
|
||||
def process_pokemon_data(self):
|
||||
|
||||
local_poke_data = deepcopy(poke_data.pokemon_data)
|
||||
learnsets = deepcopy(poke_data.learnsets)
|
||||
tms_hms = self.local_tms + poke_data.hm_moves
|
||||
|
||||
|
||||
compat_hms = set()
|
||||
|
||||
for mon, mon_data in local_poke_data.items():
|
||||
if self.multiworld.randomize_pokemon_stats[self.player] == "shuffle":
|
||||
stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]]
|
||||
if mon in poke_data.evolves_from:
|
||||
stat_shuffle_map = local_poke_data[poke_data.evolves_from[mon]]["stat_shuffle_map"]
|
||||
else:
|
||||
stat_shuffle_map = self.multiworld.random.sample(range(0, 5), 5)
|
||||
|
||||
mon_data["stat_shuffle_map"] = stat_shuffle_map
|
||||
mon_data["hp"] = stats[stat_shuffle_map[0]]
|
||||
mon_data["atk"] = stats[stat_shuffle_map[1]]
|
||||
mon_data["def"] = stats[stat_shuffle_map[2]]
|
||||
mon_data["spd"] = stats[stat_shuffle_map[3]]
|
||||
mon_data["spc"] = stats[stat_shuffle_map[4]]
|
||||
elif self.multiworld.randomize_pokemon_stats[self.player] == "randomize":
|
||||
first_run = True
|
||||
while (mon_data["hp"] > 255 or mon_data["atk"] > 255 or mon_data["def"] > 255 or mon_data["spd"] > 255
|
||||
or mon_data["spc"] > 255 or first_run):
|
||||
first_run = False
|
||||
total_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"]
|
||||
for stat in ("hp", "atk", "def", "spd", "spc"):
|
||||
if mon in poke_data.evolves_from:
|
||||
mon_data[stat] = local_poke_data[poke_data.evolves_from[mon]][stat]
|
||||
total_stats -= mon_data[stat]
|
||||
elif stat == "hp":
|
||||
mon_data[stat] = 20
|
||||
total_stats -= 20
|
||||
else:
|
||||
mon_data[stat] = 10
|
||||
total_stats -= 10
|
||||
assert total_stats >= 0, f"Error distributing stats for {mon} for player {self.player}"
|
||||
dist = [self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100]
|
||||
total_dist = sum(dist)
|
||||
|
||||
mon_data["hp"] += int(round(dist[0] / total_dist * total_stats))
|
||||
mon_data["atk"] += int(round(dist[1] / total_dist * total_stats))
|
||||
mon_data["def"] += int(round(dist[2] / total_dist * total_stats))
|
||||
mon_data["spd"] += int(round(dist[3] / total_dist * total_stats))
|
||||
mon_data["spc"] += int(round(dist[4] / total_dist * total_stats))
|
||||
if self.multiworld.randomize_pokemon_types[self.player]:
|
||||
if self.multiworld.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from:
|
||||
type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"]
|
||||
type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"]
|
||||
if type1 == type2:
|
||||
if self.multiworld.secondary_type_chance[self.player].value == -1:
|
||||
if mon_data["type1"] != mon_data["type2"]:
|
||||
while type2 == type1:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
elif self.multiworld.random.randint(1, 100) <= self.multiworld.secondary_type_chance[self.player].value:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
else:
|
||||
type1 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
type2 = type1
|
||||
if ((self.multiworld.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
|
||||
!= mon_data["type2"]) or self.multiworld.random.randint(1, 100)
|
||||
<= self.multiworld.secondary_type_chance[self.player].value):
|
||||
while type2 == type1:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
|
||||
mon_data["type1"] = type1
|
||||
mon_data["type2"] = type2
|
||||
if self.multiworld.randomize_pokemon_movesets[self.player]:
|
||||
if self.multiworld.randomize_pokemon_movesets[self.player] == "prefer_types":
|
||||
if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal":
|
||||
chances = [[75, "Normal"]]
|
||||
elif mon_data["type1"] == "Normal" or mon_data["type2"] == "Normal":
|
||||
if mon_data["type1"] == "Normal":
|
||||
second_type = mon_data["type2"]
|
||||
else:
|
||||
second_type = mon_data["type1"]
|
||||
chances = [[30, "Normal"], [85, second_type]]
|
||||
elif mon_data["type1"] == mon_data["type2"]:
|
||||
chances = [[60, mon_data["type1"]], [80, "Normal"]]
|
||||
else:
|
||||
chances = [[50, mon_data["type1"]], [80, mon_data["type2"]], [85, "Normal"]]
|
||||
else:
|
||||
chances = []
|
||||
moves = list(poke_data.moves.keys())
|
||||
for move in ["No Move"] + poke_data.hm_moves:
|
||||
moves.remove(move)
|
||||
if self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
moves.remove("Transform")
|
||||
if self.multiworld.start_with_four_moves[self.player]:
|
||||
num_moves = 4
|
||||
else:
|
||||
num_moves = len([i for i in [mon_data["start move 1"], mon_data["start move 2"],
|
||||
mon_data["start move 3"], mon_data["start move 4"]] if i != "No Move"])
|
||||
if mon in learnsets:
|
||||
num_moves += len(learnsets[mon])
|
||||
non_power_moves = []
|
||||
learnsets[mon] = []
|
||||
for i in range(num_moves):
|
||||
if i == 0 and mon == "Ditto" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = "Transform"
|
||||
else:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
while move == "Transform" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
if self.local_move_data[move]["power"] < 5:
|
||||
non_power_moves.append(move)
|
||||
else:
|
||||
learnsets[mon].append(move)
|
||||
learnsets[mon].sort(key=lambda move: move_power(self.local_move_data[move]))
|
||||
if learnsets[mon]:
|
||||
for move in non_power_moves:
|
||||
learnsets[mon].insert(self.multiworld.random.randint(1, len(learnsets[mon])), move)
|
||||
else:
|
||||
learnsets[mon] = non_power_moves
|
||||
for i in range(1, 5):
|
||||
if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[self.player]:
|
||||
mon_data[f"start move {i}"] = learnsets[mon].pop(0)
|
||||
|
||||
if self.multiworld.randomize_pokemon_catch_rates[self.player]:
|
||||
mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player],
|
||||
255)
|
||||
else:
|
||||
mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"])
|
||||
|
||||
def roll_tm_compat(roll_move):
|
||||
if self.local_move_data[roll_move]["type"] in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_same_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_same_type_compatibility[self.player].value
|
||||
elif self.local_move_data[roll_move]["type"] == "Normal" and "Normal" not in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_normal_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_normal_type_compatibility[self.player].value
|
||||
else:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_other_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)] & 1 << (flag % 8)
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_other_type_compatibility[self.player].value
|
||||
|
||||
for flag, tm_move in enumerate(tms_hms):
|
||||
if mon in poke_data.evolves_from and self.multiworld.inherit_tm_hm_compatibility[self.player]:
|
||||
|
||||
if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8):
|
||||
# always inherit learnable tms/hms
|
||||
bit = 1
|
||||
else:
|
||||
if self.local_move_data[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] and \
|
||||
self.local_move_data[tm_move]["type"] not in [
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type1"],
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type2"]]:
|
||||
# the tm/hm is for a move whose type matches current mon, but not pre-evolved form
|
||||
# so this gets full chance roll
|
||||
bit = roll_tm_compat(tm_move)
|
||||
# otherwise 50% reduced chance to add compatibility over pre-evolved form
|
||||
elif self.multiworld.random.randint(1, 100) > 50 and roll_tm_compat(tm_move):
|
||||
bit = 1
|
||||
else:
|
||||
bit = 0
|
||||
else:
|
||||
bit = roll_tm_compat(tm_move)
|
||||
if bit:
|
||||
mon_data["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
else:
|
||||
mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8))
|
||||
|
||||
hm_verify = ["Surf", "Strength"]
|
||||
if self.multiworld.accessibility[self.player] == "locations" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_badges_condition[self.player],
|
||||
self.multiworld.route_22_gate_condition[self.player], self.multiworld.victory_road_condition[self.player])
|
||||
> 7) or (self.multiworld.door_shuffle[self.player] not in ("off", "simple")):
|
||||
hm_verify += ["Cut"]
|
||||
if self.multiworld.accessibility[self.player] == "locations" or (not
|
||||
self.multiworld.dark_rock_tunnel_logic[self.player]) and ((self.multiworld.trainersanity[self.player] or
|
||||
self.multiworld.extra_key_items[self.player])
|
||||
or self.multiworld.door_shuffle[self.player]):
|
||||
hm_verify += ["Flash"]
|
||||
# Fly does not need to be verified. Full/Insanity door shuffle connects reachable regions to unreachable regions,
|
||||
# so if Fly is available and can be learned, the towns you can fly to would be reachable, but if no Pokémon can
|
||||
# learn it this simply would not occur
|
||||
|
||||
for hm_move in hm_verify:
|
||||
if hm_move not in compat_hms:
|
||||
mon = self.multiworld.random.choice([mon for mon in poke_data.pokemon_data if mon not in
|
||||
poke_data.legendary_pokemon])
|
||||
flag = tms_hms.index(hm_move)
|
||||
local_poke_data[mon]["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
|
||||
self.local_poke_data = local_poke_data
|
||||
self.learnsets = learnsets
|
File diff suppressed because it is too large
Load Diff
|
@ -5,548 +5,12 @@ import bsdiff4
|
|||
from copy import deepcopy
|
||||
from worlds.Files import APDeltaPatch
|
||||
from .text import encode_text
|
||||
from .rom_addresses import rom_addresses
|
||||
from .locations import location_data
|
||||
from .items import item_table
|
||||
from .pokemon import set_mon_palettes
|
||||
from .rock_tunnel import randomize_rock_tunnel
|
||||
import worlds.pokemon_rb.poke_data as poke_data
|
||||
|
||||
|
||||
def choose_forced_type(chances, random):
|
||||
n = random.randint(1, 100)
|
||||
for chance in chances:
|
||||
if chance[0] >= n:
|
||||
return chance[1]
|
||||
return None
|
||||
|
||||
|
||||
def filter_moves(moves, type, random):
|
||||
ret = []
|
||||
for move in moves:
|
||||
if poke_data.moves[move]["type"] == type or type is None:
|
||||
ret.append(move)
|
||||
random.shuffle(ret)
|
||||
return ret
|
||||
|
||||
|
||||
def get_move(local_move_data, moves, chances, random, starting_move=False):
|
||||
type = choose_forced_type(chances, random)
|
||||
filtered_moves = filter_moves(moves, type, random)
|
||||
for move in filtered_moves:
|
||||
if local_move_data[move]["accuracy"] > 80 and local_move_data[move]["power"] > 0 or not starting_move:
|
||||
moves.remove(move)
|
||||
return move
|
||||
else:
|
||||
return get_move(local_move_data, moves, [], random, starting_move)
|
||||
|
||||
|
||||
def get_encounter_slots(self):
|
||||
encounter_slots = deepcopy([location for location in location_data if location.type == "Wild Encounter"])
|
||||
|
||||
for location in encounter_slots:
|
||||
if isinstance(location.original_item, list):
|
||||
location.original_item = location.original_item[not self.multiworld.game_version[self.player].value]
|
||||
return encounter_slots
|
||||
|
||||
|
||||
def get_base_stat_total(mon):
|
||||
return (poke_data.pokemon_data[mon]["atk"] + poke_data.pokemon_data[mon]["def"]
|
||||
+ poke_data.pokemon_data[mon]["hp"] + poke_data.pokemon_data[mon]["spd"]
|
||||
+ poke_data.pokemon_data[mon]["spc"])
|
||||
|
||||
|
||||
def randomize_pokemon(self, mon, mons_list, randomize_type, random):
|
||||
if randomize_type in [1, 3]:
|
||||
type_mons = [pokemon for pokemon in mons_list if any([poke_data.pokemon_data[mon][
|
||||
"type1"] in [self.local_poke_data[pokemon]["type1"], self.local_poke_data[pokemon]["type2"]],
|
||||
poke_data.pokemon_data[mon]["type2"] in [self.local_poke_data[pokemon]["type1"],
|
||||
self.local_poke_data[pokemon]["type2"]]])]
|
||||
if not type_mons:
|
||||
type_mons = mons_list.copy()
|
||||
if randomize_type == 3:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
type_mons.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
|
||||
mon = type_mons[round(random.triangular(0, len(type_mons) - 1, 0))]
|
||||
if randomize_type == 2:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
mons_list.sort(key=lambda mon: abs(get_base_stat_total(mon) - stat_base))
|
||||
mon = mons_list[round(random.triangular(0, 50, 0))]
|
||||
elif randomize_type == 4:
|
||||
mon = random.choice(mons_list)
|
||||
return mon
|
||||
|
||||
|
||||
def set_mon_palettes(self, random, data):
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "vanilla":
|
||||
return
|
||||
pallet_map = {
|
||||
"Poison": 0x0F,
|
||||
"Normal": 0x10,
|
||||
"Ice": 0x11,
|
||||
"Fire": 0x12,
|
||||
"Water": 0x13,
|
||||
"Ghost": 0x14,
|
||||
"Ground": 0x15,
|
||||
"Grass": 0x16,
|
||||
"Psychic": 0x17,
|
||||
"Electric": 0x18,
|
||||
"Rock": 0x19,
|
||||
"Dragon": 0x1F,
|
||||
"Flying": 0x20,
|
||||
"Fighting": 0x21,
|
||||
"Bug": 0x22
|
||||
}
|
||||
palettes = []
|
||||
for mon in poke_data.pokemon_data:
|
||||
if self.multiworld.randomize_pokemon_palettes[self.player] == "primary_type":
|
||||
pallet = pallet_map[self.local_poke_data[mon]["type1"]]
|
||||
elif (self.multiworld.randomize_pokemon_palettes[self.player] == "follow_evolutions" and mon in
|
||||
poke_data.evolves_from and poke_data.evolves_from[mon] != "Eevee"):
|
||||
pallet = palettes[-1]
|
||||
else: # completely_random or follow_evolutions and it is not an evolved form (except eeveelutions)
|
||||
pallet = random.choice(list(pallet_map.values()))
|
||||
palettes.append(pallet)
|
||||
address = rom_addresses["Mon_Palettes"]
|
||||
for pallet in palettes:
|
||||
data[address] = pallet
|
||||
address += 1
|
||||
|
||||
|
||||
def process_trainer_data(self, data, random):
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.trainer_legendaries[self.player].value]
|
||||
address = rom_addresses["Trainer_Data"]
|
||||
while address < rom_addresses["Trainer_Data_End"]:
|
||||
if data[address] == 255:
|
||||
mode = 1
|
||||
else:
|
||||
mode = 0
|
||||
while True:
|
||||
address += 1
|
||||
if data[address] == 0:
|
||||
address += 1
|
||||
break
|
||||
address += mode
|
||||
mon = None
|
||||
for i in range(1, 4):
|
||||
for l in ["A", "B", "C", "D", "E", "F", "G", "H"]:
|
||||
if rom_addresses[f"Rival_Starter{i}_{l}"] == address:
|
||||
mon = " ".join(self.multiworld.get_location(f"Pallet Town - Starter {i}",
|
||||
self.player).item.name.split()[1:])
|
||||
if l in ["D", "E", "F", "G", "H"] and mon in poke_data.evolves_to:
|
||||
mon = poke_data.evolves_to[mon]
|
||||
if l in ["F", "G", "H"] and mon in poke_data.evolves_to:
|
||||
mon = poke_data.evolves_to[mon]
|
||||
if mon is None and self.multiworld.randomize_trainer_parties[self.player].value:
|
||||
mon = poke_data.id_to_mon[data[address]]
|
||||
mon = randomize_pokemon(self, mon, mons_list,
|
||||
self.multiworld.randomize_trainer_parties[self.player].value, random)
|
||||
if mon is not None:
|
||||
data[address] = poke_data.pokemon_data[mon]["id"]
|
||||
|
||||
|
||||
def process_static_pokemon(self):
|
||||
starter_slots = deepcopy([location for location in location_data if location.type == "Starter Pokemon"])
|
||||
legendary_slots = deepcopy([location for location in location_data if location.type == "Legendary Pokemon"])
|
||||
static_slots = deepcopy([location for location in location_data if location.type in
|
||||
["Static Pokemon", "Missable Pokemon"]])
|
||||
legendary_mons = deepcopy([slot.original_item for slot in legendary_slots])
|
||||
|
||||
tower_6F_mons = set()
|
||||
for i in range(1, 11):
|
||||
tower_6F_mons.add(self.multiworld.get_location(f"Pokemon Tower 6F - Wild Pokemon - {i}", self.player).item.name)
|
||||
|
||||
mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
if self.multiworld.randomize_legendary_pokemon[self.player].value == 0:
|
||||
for slot in legendary_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.place_locked_item(self.create_item("Missable " + slot.original_item))
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player].value == 1:
|
||||
self.multiworld.random.shuffle(legendary_mons)
|
||||
for slot in legendary_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.place_locked_item(self.create_item("Missable " + legendary_mons.pop()))
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player].value == 2:
|
||||
static_slots = static_slots + legendary_slots
|
||||
self.multiworld.random.shuffle(static_slots)
|
||||
static_slots.sort(key=lambda s: 0 if s.name == "Pokemon Tower 6F - Restless Soul" else 1)
|
||||
while legendary_slots:
|
||||
swap_slot = legendary_slots.pop()
|
||||
slot = static_slots.pop()
|
||||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
slot_type = "Missable"
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item))
|
||||
swap_slot.original_item = slot.original_item
|
||||
elif self.multiworld.randomize_legendary_pokemon[self.player].value == 3:
|
||||
static_slots = static_slots + legendary_slots
|
||||
|
||||
for slot in static_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_static_pokemon[self.player].value
|
||||
slot_type = slot.type.split()[0]
|
||||
if slot_type == "Legendary":
|
||||
slot_type = "Missable"
|
||||
if not randomize_type:
|
||||
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
|
||||
else:
|
||||
mon = self.create_item(slot_type + " " +
|
||||
randomize_pokemon(self, slot.original_item, mons_list, randomize_type,
|
||||
self.multiworld.random))
|
||||
while location.name == "Pokemon Tower 6F - Restless Soul" and mon in tower_6F_mons:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
location.place_locked_item(mon)
|
||||
|
||||
chosen_mons = set()
|
||||
for slot in starter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value
|
||||
slot_type = "Missable"
|
||||
if not randomize_type:
|
||||
location.place_locked_item(self.create_item(slot_type + " " + slot.original_item))
|
||||
else:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
while mon.name in chosen_mons:
|
||||
mon = self.create_item(slot_type + " " + randomize_pokemon(self, slot.original_item, mons_list,
|
||||
randomize_type, self.multiworld.random))
|
||||
chosen_mons.add(mon.name)
|
||||
location.place_locked_item(mon)
|
||||
|
||||
|
||||
def process_wild_pokemon(self):
|
||||
|
||||
encounter_slots = get_encounter_slots(self)
|
||||
|
||||
placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()}
|
||||
zone_mapping = {}
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].value:
|
||||
mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon
|
||||
or self.multiworld.randomize_legendary_pokemon[self.player].value == 3]
|
||||
self.multiworld.random.shuffle(encounter_slots)
|
||||
locations = []
|
||||
for slot in encounter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if zone not in zone_mapping:
|
||||
zone_mapping[zone] = {}
|
||||
original_mon = slot.original_item
|
||||
if self.multiworld.area_1_to_1_mapping[self.player] and original_mon in zone_mapping[zone]:
|
||||
mon = zone_mapping[zone][original_mon]
|
||||
else:
|
||||
mon = randomize_pokemon(self, original_mon, mons_list,
|
||||
self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random)
|
||||
# if static Pokemon are not randomized, we make sure nothing on Pokemon Tower 6F is a Marowak
|
||||
# if static Pokemon are randomized we deal with that during static encounter randomization
|
||||
while (self.multiworld.randomize_static_pokemon[self.player].value == 0 and mon == "Marowak"
|
||||
and "Pokemon Tower 6F" in slot.name):
|
||||
# to account for the possibility that only one ground type Pokemon exists, match only stats for this fix
|
||||
mon = randomize_pokemon(self, original_mon, mons_list, 2, self.multiworld.random)
|
||||
placed_mons[mon] += 1
|
||||
location.item = self.create_item(mon)
|
||||
location.event = True
|
||||
location.locked = True
|
||||
location.item.location = location
|
||||
locations.append(location)
|
||||
zone_mapping[zone][original_mon] = mon
|
||||
|
||||
mons_to_add = []
|
||||
remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
if self.multiworld.catch_em_all[self.player].value == 1:
|
||||
mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and
|
||||
(pokemon not in poke_data.legendary_pokemon or self.multiworld.randomize_legendary_pokemon[self.player].value == 3)]
|
||||
elif self.multiworld.catch_em_all[self.player].value == 2:
|
||||
mons_to_add = remaining_pokemon.copy()
|
||||
logic_needed_mons = max(self.multiworld.oaks_aide_rt_2[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_11[self.player].value,
|
||||
self.multiworld.oaks_aide_rt_15[self.player].value)
|
||||
if self.multiworld.accessibility[self.player] == "minimal":
|
||||
logic_needed_mons = 0
|
||||
|
||||
self.multiworld.random.shuffle(remaining_pokemon)
|
||||
while (len([pokemon for pokemon in placed_mons if placed_mons[pokemon] > 0])
|
||||
+ len(mons_to_add) < logic_needed_mons):
|
||||
mons_to_add.append(remaining_pokemon.pop())
|
||||
for mon in mons_to_add:
|
||||
stat_base = get_base_stat_total(mon)
|
||||
candidate_locations = get_encounter_slots(self)
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_base_stats", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.original_item) - stat_base))
|
||||
if self.multiworld.randomize_wild_pokemon[self.player].current_key in ["match_types", "match_types_and_base_stats"]:
|
||||
candidate_locations.sort(key=lambda slot: not any([poke_data.pokemon_data[slot.original_item]["type1"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]],
|
||||
poke_data.pokemon_data[slot.original_item]["type2"] in
|
||||
[self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]]]))
|
||||
candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations]
|
||||
for location in candidate_locations:
|
||||
zone = " - ".join(location.name.split(" - ")[:-1])
|
||||
if self.multiworld.catch_em_all[self.player] == "all_pokemon" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in get_encounter_slots(self)
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name]:
|
||||
continue
|
||||
if self.multiworld.catch_em_all[self.player] == "first_stage" and self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
if not [self.multiworld.get_location(l.name, self.player) for l in get_encounter_slots(self)
|
||||
if (not l.name.startswith(zone)) and
|
||||
self.multiworld.get_location(l.name, self.player).item.name == location.item.name and l.name
|
||||
not in poke_data.evolves_from]:
|
||||
continue
|
||||
|
||||
if placed_mons[location.item.name] < 2 and (location.item.name in poke_data.first_stage_pokemon
|
||||
or self.multiworld.catch_em_all[self.player]):
|
||||
continue
|
||||
|
||||
if self.multiworld.area_1_to_1_mapping[self.player]:
|
||||
place_locations = [place_location for place_location in candidate_locations if
|
||||
place_location.name.startswith(zone) and
|
||||
place_location.item.name == location.item.name]
|
||||
else:
|
||||
place_locations = [location]
|
||||
for place_location in place_locations:
|
||||
placed_mons[place_location.item.name] -= 1
|
||||
place_location.item = self.create_item(mon)
|
||||
place_location.item.location = place_location
|
||||
placed_mons[mon] += 1
|
||||
break
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
else:
|
||||
for slot in encounter_slots:
|
||||
location = self.multiworld.get_location(slot.name, self.player)
|
||||
location.item = self.create_item(slot.original_item)
|
||||
location.event = True
|
||||
location.locked = True
|
||||
location.item.location = location
|
||||
placed_mons[location.item.name] += 1
|
||||
|
||||
|
||||
def process_move_data(self):
|
||||
self.local_move_data = deepcopy(poke_data.moves)
|
||||
if self.multiworld.move_balancing[self.player]:
|
||||
self.local_move_data["Sing"]["accuracy"] = 30
|
||||
self.local_move_data["Sleep Powder"]["accuracy"] = 40
|
||||
self.local_move_data["Spore"]["accuracy"] = 50
|
||||
self.local_move_data["Sonicboom"]["effect"] = 0
|
||||
self.local_move_data["Sonicboom"]["power"] = 50
|
||||
self.local_move_data["Dragon Rage"]["effect"] = 0
|
||||
self.local_move_data["Dragon Rage"]["power"] = 80
|
||||
self.local_move_data["Horn Drill"]["effect"] = 0
|
||||
self.local_move_data["Horn Drill"]["power"] = 70
|
||||
self.local_move_data["Horn Drill"]["accuracy"] = 90
|
||||
self.local_move_data["Guillotine"]["effect"] = 0
|
||||
self.local_move_data["Guillotine"]["power"] = 70
|
||||
self.local_move_data["Guillotine"]["accuracy"] = 90
|
||||
self.local_move_data["Fissure"]["effect"] = 0
|
||||
self.local_move_data["Fissure"]["power"] = 70
|
||||
self.local_move_data["Fissure"]["accuracy"] = 90
|
||||
self.local_move_data["Blizzard"]["accuracy"] = 70
|
||||
if self.multiworld.randomize_tm_moves[self.player]:
|
||||
self.local_tms = self.multiworld.random.sample([move for move in poke_data.moves.keys() if move not in
|
||||
["No Move"] + poke_data.hm_moves], 50)
|
||||
else:
|
||||
self.local_tms = poke_data.tm_moves.copy()
|
||||
|
||||
|
||||
def process_pokemon_data(self):
|
||||
|
||||
local_poke_data = deepcopy(poke_data.pokemon_data)
|
||||
learnsets = deepcopy(poke_data.learnsets)
|
||||
tms_hms = self.local_tms + poke_data.hm_moves
|
||||
|
||||
|
||||
compat_hms = set()
|
||||
|
||||
for mon, mon_data in local_poke_data.items():
|
||||
if self.multiworld.randomize_pokemon_stats[self.player].value == 1:
|
||||
stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]]
|
||||
self.multiworld.random.shuffle(stats)
|
||||
mon_data["hp"] = stats[0]
|
||||
mon_data["atk"] = stats[1]
|
||||
mon_data["def"] = stats[2]
|
||||
mon_data["spd"] = stats[3]
|
||||
mon_data["spc"] = stats[4]
|
||||
elif self.multiworld.randomize_pokemon_stats[self.player].value == 2:
|
||||
first_run = True
|
||||
while (mon_data["hp"] > 255 or mon_data["atk"] > 255 or mon_data["def"] > 255 or mon_data["spd"] > 255
|
||||
or mon_data["spc"] > 255 or first_run):
|
||||
first_run = False
|
||||
total_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 60
|
||||
dist = [self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100, self.multiworld.random.randint(1, 101) / 100,
|
||||
self.multiworld.random.randint(1, 101) / 100]
|
||||
total_dist = sum(dist)
|
||||
|
||||
mon_data["hp"] = int(round(dist[0] / total_dist * total_stats) + 20)
|
||||
mon_data["atk"] = int(round(dist[1] / total_dist * total_stats) + 10)
|
||||
mon_data["def"] = int(round(dist[2] / total_dist * total_stats) + 10)
|
||||
mon_data["spd"] = int(round(dist[3] / total_dist * total_stats) + 10)
|
||||
mon_data["spc"] = int(round(dist[4] / total_dist * total_stats) + 10)
|
||||
if self.multiworld.randomize_pokemon_types[self.player].value:
|
||||
if self.multiworld.randomize_pokemon_types[self.player].value == 1 and mon in poke_data.evolves_from:
|
||||
type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"]
|
||||
type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"]
|
||||
if type1 == type2:
|
||||
if self.multiworld.secondary_type_chance[self.player].value == -1:
|
||||
if mon_data["type1"] != mon_data["type2"]:
|
||||
while type2 == type1:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
elif self.multiworld.random.randint(1, 100) <= self.multiworld.secondary_type_chance[self.player].value:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
else:
|
||||
type1 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
type2 = type1
|
||||
if ((self.multiworld.secondary_type_chance[self.player].value == -1 and mon_data["type1"]
|
||||
!= mon_data["type2"]) or self.multiworld.random.randint(1, 100)
|
||||
<= self.multiworld.secondary_type_chance[self.player].value):
|
||||
while type2 == type1:
|
||||
type2 = self.multiworld.random.choice(list(poke_data.type_names.values()))
|
||||
|
||||
mon_data["type1"] = type1
|
||||
mon_data["type2"] = type2
|
||||
if self.multiworld.randomize_pokemon_movesets[self.player].value:
|
||||
if self.multiworld.randomize_pokemon_movesets[self.player].value == 1:
|
||||
if mon_data["type1"] == "Normal" and mon_data["type2"] == "Normal":
|
||||
chances = [[75, "Normal"]]
|
||||
elif mon_data["type1"] == "Normal" or mon_data["type2"] == "Normal":
|
||||
if mon_data["type1"] == "Normal":
|
||||
second_type = mon_data["type2"]
|
||||
else:
|
||||
second_type = mon_data["type1"]
|
||||
chances = [[30, "Normal"], [85, second_type]]
|
||||
elif mon_data["type1"] == mon_data["type2"]:
|
||||
chances = [[60, mon_data["type1"]], [80, "Normal"]]
|
||||
else:
|
||||
chances = [[50, mon_data["type1"]], [80, mon_data["type2"]], [85, "Normal"]]
|
||||
else:
|
||||
chances = []
|
||||
moves = list(poke_data.moves.keys())
|
||||
for move in ["No Move"] + poke_data.hm_moves:
|
||||
moves.remove(move)
|
||||
if self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
moves.remove("Transform")
|
||||
if self.multiworld.start_with_four_moves[self.player]:
|
||||
num_moves = 4
|
||||
else:
|
||||
num_moves = len([i for i in [mon_data["start move 1"], mon_data["start move 2"],
|
||||
mon_data["start move 3"], mon_data["start move 4"]] if i != "No Move"])
|
||||
if mon in learnsets:
|
||||
num_moves += len(learnsets[mon])
|
||||
non_power_moves = []
|
||||
learnsets[mon] = []
|
||||
for i in range(num_moves):
|
||||
if i == 0 and mon == "Ditto" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = "Transform"
|
||||
else:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
while move == "Transform" and self.multiworld.confine_transform_to_ditto[self.player]:
|
||||
move = get_move(self.local_move_data, moves, chances, self.multiworld.random)
|
||||
if self.local_move_data[move]["power"] < 5:
|
||||
non_power_moves.append(move)
|
||||
else:
|
||||
learnsets[mon].append(move)
|
||||
learnsets[mon].sort(key=lambda move: self.local_move_data[move]["power"])
|
||||
if learnsets[mon]:
|
||||
for move in non_power_moves:
|
||||
learnsets[mon].insert(self.multiworld.random.randint(1, len(learnsets[mon])), move)
|
||||
else:
|
||||
learnsets[mon] = non_power_moves
|
||||
for i in range(1, 5):
|
||||
if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[self.player]:
|
||||
mon_data[f"start move {i}"] = learnsets[mon].pop(0)
|
||||
|
||||
if self.multiworld.randomize_pokemon_catch_rates[self.player].value:
|
||||
mon_data["catch rate"] = self.multiworld.random.randint(self.multiworld.minimum_catch_rate[self.player], 255)
|
||||
else:
|
||||
mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"])
|
||||
|
||||
def roll_tm_compat(roll_move):
|
||||
if self.local_move_data[roll_move]["type"] in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_same_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_same_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_same_type_compatibility[self.player].value
|
||||
elif self.local_move_data[roll_move]["type"] == "Normal" and "Normal" not in [mon_data["type1"], mon_data["type2"]]:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_normal_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_normal_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_normal_type_compatibility[self.player].value
|
||||
else:
|
||||
if roll_move in poke_data.hm_moves:
|
||||
if self.multiworld.hm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
r = self.multiworld.random.randint(1, 100) <= self.multiworld.hm_other_type_compatibility[self.player].value
|
||||
if r and mon not in poke_data.legendary_pokemon:
|
||||
compat_hms.add(roll_move)
|
||||
return r
|
||||
else:
|
||||
if self.multiworld.tm_other_type_compatibility[self.player].value == -1:
|
||||
return mon_data["tms"][int(flag / 8)]
|
||||
return self.multiworld.random.randint(1, 100) <= self.multiworld.tm_other_type_compatibility[self.player].value
|
||||
|
||||
|
||||
for flag, tm_move in enumerate(tms_hms):
|
||||
if mon in poke_data.evolves_from.keys() and self.multiworld.inherit_tm_hm_compatibility[self.player]:
|
||||
|
||||
if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8):
|
||||
# always inherit learnable tms/hms
|
||||
bit = 1
|
||||
else:
|
||||
if self.local_move_data[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] and \
|
||||
self.local_move_data[tm_move]["type"] not in [
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type1"],
|
||||
local_poke_data[poke_data.evolves_from[mon]]["type2"]]:
|
||||
# the tm/hm is for a move whose type matches current mon, but not pre-evolved form
|
||||
# so this gets full chance roll
|
||||
bit = roll_tm_compat(tm_move)
|
||||
# otherwise 50% reduced chance to add compatibility over pre-evolved form
|
||||
elif self.multiworld.random.randint(1, 100) > 50 and roll_tm_compat(tm_move):
|
||||
bit = 1
|
||||
else:
|
||||
bit = 0
|
||||
else:
|
||||
bit = roll_tm_compat(tm_move)
|
||||
if bit:
|
||||
mon_data["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
else:
|
||||
mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8))
|
||||
|
||||
hm_verify = ["Surf", "Strength"]
|
||||
if self.multiworld.accessibility[self.player] != "minimal" or ((not
|
||||
self.multiworld.badgesanity[self.player]) and max(self.multiworld.elite_four_condition[self.player],
|
||||
self.multiworld.victory_road_condition[self.player]) > 7):
|
||||
hm_verify += ["Cut"]
|
||||
if self.multiworld.accessibility[self.player] != "minimal" and (self.multiworld.trainersanity[self.player] or
|
||||
self.multiworld.extra_key_items[self.player]):
|
||||
hm_verify += ["Flash"]
|
||||
|
||||
for hm_move in hm_verify:
|
||||
if hm_move not in compat_hms:
|
||||
mon = self.multiworld.random.choice([mon for mon in poke_data.pokemon_data if mon not in
|
||||
poke_data.legendary_pokemon])
|
||||
flag = tms_hms.index(hm_move)
|
||||
local_poke_data[mon]["tms"][int(flag / 8)] |= 1 << (flag % 8)
|
||||
|
||||
self.local_poke_data = local_poke_data
|
||||
self.learnsets = learnsets
|
||||
from .rom_addresses import rom_addresses
|
||||
from .regions import PokemonRBWarp, map_ids
|
||||
from . import poke_data
|
||||
|
||||
|
||||
def write_quizzes(self, data, random):
|
||||
|
@ -599,7 +63,7 @@ def write_quizzes(self, data, random):
|
|||
return encode_text(f"#mon is<LINE>pronounced<CONT>Po-kuh-mon?<DONE>")
|
||||
elif q == 3:
|
||||
starters = [" ".join(self.multiworld.get_location(
|
||||
f"Pallet Town - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
f"Oak's Lab - Starter {i}", self.player).item.name.split(" ")[1:]) for i in range(1, 4)]
|
||||
mon = random.choice(starters)
|
||||
nots = random.choice(range(8, 16, 2))
|
||||
if random.randint(0, 1):
|
||||
|
@ -630,12 +94,12 @@ def write_quizzes(self, data, random):
|
|||
elif q == 5:
|
||||
i = 8
|
||||
while not a and i in [1, 8]:
|
||||
i = random.randint(0, 99999999)
|
||||
i = random.randint(0, int("99999999"[random.randint(0, 7):]))
|
||||
return encode_text(f"There are {i}<LINE>certified #MON<CONT>LEAGUE BADGEs?<DONE>")
|
||||
elif q == 6:
|
||||
i = 2
|
||||
while not a and i in [1, 2]:
|
||||
i = random.randint(0, 99)
|
||||
i = random.randint(0, random.choice([9, 99]))
|
||||
return encode_text(f"POLIWAG evolves {i}<LINE>times?<DONE>")
|
||||
elif q == 7:
|
||||
entity = "Motor Carrier"
|
||||
|
@ -644,11 +108,82 @@ def write_quizzes(self, data, random):
|
|||
return encode_text("Title 49 of the<LINE>U.S. Code of<CONT>Federal<CONT>Regulations part<CONT>397.67 states"
|
||||
f"<CONT>that the<CONT>{entity}<CONT>is responsible<CONT>for planning<CONT>routes when"
|
||||
"<CONT>hazardous<CONT>materials are<CONT>transported?<DONE>")
|
||||
elif q == 8:
|
||||
mon = random.choice(list(poke_data.evolution_levels.keys()))
|
||||
level = poke_data.evolution_levels[mon]
|
||||
if not a:
|
||||
level += random.choice(range(1, 6)) * random.choice((-1, 1))
|
||||
return encode_text(f"{mon} evolves<LINE>at level {level}?<DONE>")
|
||||
elif q == 9:
|
||||
move = random.choice(list(self.local_move_data.keys()))
|
||||
actual_type = self.local_move_data[move]["type"]
|
||||
question_type = actual_type
|
||||
while question_type == actual_type and not a:
|
||||
question_type = random.choice(list(poke_data.type_ids.keys()))
|
||||
return encode_text(f"{move} is<LINE>{question_type} type?<DONE>")
|
||||
elif q == 10:
|
||||
mon = random.choice(list(poke_data.pokemon_data.keys()))
|
||||
actual_type = self.local_poke_data[mon][random.choice(("type1", "type2"))]
|
||||
question_type = actual_type
|
||||
while question_type in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]] and not a:
|
||||
question_type = random.choice(list(poke_data.type_ids.keys()))
|
||||
return encode_text(f"{mon} is<LINE>{question_type} type?<DONE>")
|
||||
elif q == 11:
|
||||
equation = ""
|
||||
while "*" not in equation:
|
||||
equation = f"{random.randint(0, 9)} {random.choice(['+', '-', '*'])} {random.randint(0, 9)} {random.choice(['+', '-', '*'])} {random.randint(0, 9)} {random.choice(['+', '-', '*'])} {random.randint(0, 9)}"
|
||||
result = eval(equation)
|
||||
question_result = result
|
||||
if not a:
|
||||
modifiers = random.sample(range(3), 3)
|
||||
for modifier in modifiers:
|
||||
question_result = eval(equation[:modifier * 4] + "(" + equation[modifier * 4:(modifier * 4) + 5]
|
||||
+ ")" + equation[5 + (modifier * 4):])
|
||||
if question_result != result:
|
||||
break
|
||||
else:
|
||||
question_result += random.choice(range(1, 6)) * random.choice((-1, 1))
|
||||
|
||||
answers = [random.randint(0, 1), random.randint(0, 1), random.randint(0, 1),
|
||||
random.randint(0, 1), random.randint(0, 1), random.randint(0, 1)]
|
||||
return encode_text(f"{equation}<LINE>= {question_result}?<DONE>")
|
||||
elif q == 12:
|
||||
route = random.choice((12, 16))
|
||||
actual_mon = self.multiworld.get_location(f"Route {route} - Sleeping Pokemon",
|
||||
self.player).item.name.split("Static ")[1]
|
||||
question_mon = actual_mon
|
||||
while question_mon == actual_mon and not a:
|
||||
question_mon = random.choice(list(poke_data.pokemon_data.keys()))
|
||||
return encode_text(f"{question_mon} was<LINE>sleeping on route<CONT>{route}?<DONE>")
|
||||
elif q == 13:
|
||||
type1 = random.choice(list(poke_data.type_ids.keys()))
|
||||
type2 = random.choice(list(poke_data.type_ids.keys()))
|
||||
eff_msgs = ["super effective<CONT>", "no ", "not very<CONT>effective<CONT>", "normal "]
|
||||
for matchup in self.type_chart:
|
||||
if matchup[0] == type1 and matchup[1] == type2:
|
||||
if matchup[2] > 10:
|
||||
eff = eff_msgs[0]
|
||||
elif matchup[2] == 0:
|
||||
eff = eff_msgs[1]
|
||||
elif matchup[2] < 10:
|
||||
eff = eff_msgs[2]
|
||||
else:
|
||||
eff = eff_msgs[3]
|
||||
break
|
||||
else:
|
||||
eff = eff_msgs[3]
|
||||
if not a:
|
||||
eff_msgs.remove(eff)
|
||||
eff = random.choice(eff_msgs)
|
||||
return encode_text(f"{type1} deals<LINE>{eff}damage to<CONT>{type2} type?<DONE>")
|
||||
elif q == 14:
|
||||
fossil_level = self.multiworld.get_location("Fossil Level - Trainer Parties",
|
||||
self.player).party_data[0]['level']
|
||||
if not a:
|
||||
fossil_level += random.choice((-5, 5))
|
||||
return encode_text(f"Fossil #MON<LINE>revive at level<CONT>{fossil_level}?<DONE>")
|
||||
|
||||
questions = random.sample((range(0, 8)), 6)
|
||||
answers = [random.randint(0, 1) for _ in range(6)]
|
||||
|
||||
questions = random.sample((range(0, 15)), 6)
|
||||
question_texts = []
|
||||
for i, question in enumerate(questions):
|
||||
question_texts.append(get_quiz(question, answers[i]))
|
||||
|
@ -670,27 +205,52 @@ def generate_output(self, output_directory: str):
|
|||
basemd5 = hashlib.md5()
|
||||
basemd5.update(data)
|
||||
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.player != self.player or location.rom_address is None:
|
||||
continue
|
||||
if location.item and location.item.player == self.player:
|
||||
if location.rom_address:
|
||||
rom_address = location.rom_address
|
||||
if not isinstance(rom_address, list):
|
||||
rom_address = [rom_address]
|
||||
for address in rom_address:
|
||||
if location.item.name in poke_data.pokemon_data.keys():
|
||||
data[address] = poke_data.pokemon_data[location.item.name]["id"]
|
||||
elif " ".join(location.item.name.split()[1:]) in poke_data.pokemon_data.keys():
|
||||
data[address] = poke_data.pokemon_data[" ".join(location.item.name.split()[1:])]["id"]
|
||||
else:
|
||||
item_id = self.item_name_to_id[location.item.name] - 172000000
|
||||
if item_id > 255:
|
||||
item_id -= 256
|
||||
data[address] = item_id
|
||||
lab_loc = self.multiworld.get_entrance("Oak's Lab to Pallet Town", self.player).target
|
||||
paths = None
|
||||
if lab_loc == 0: # Player's House
|
||||
paths = ((0x00, 4, 0x80, 5, 0x40, 1, 0xE0, 1, 0xFF), (0x40, 2, 0x20, 5, 0x80, 5, 0xFF))
|
||||
elif lab_loc == 1: # Rival's House
|
||||
paths = ((0x00, 4, 0xC0, 3, 0x40, 1, 0xE0, 1, 0xFF), (0x40, 2, 0x10, 3, 0x80, 5, 0xFF))
|
||||
if paths:
|
||||
write_bytes(data, paths[0], rom_addresses["Path_Pallet_Oak"])
|
||||
write_bytes(data, paths[1], rom_addresses["Path_Pallet_Player"])
|
||||
home_loc = self.multiworld.get_entrance("Player's House 1F to Pallet Town", self.player).target
|
||||
if home_loc == 1: # Rival's House
|
||||
write_bytes(data, [0x2F, 0xC7, 0x06, 0x0D, 0x00, 0x01], rom_addresses["Pallet_Fly_Coords"])
|
||||
elif home_loc == 2: # Oak's Lab
|
||||
write_bytes(data, [0x5F, 0xC7, 0x0C, 0x0C, 0x00, 0x00], rom_addresses["Pallet_Fly_Coords"])
|
||||
|
||||
else:
|
||||
data[location.rom_address] = 0x2C # AP Item
|
||||
for region in self.multiworld.get_regions(self.player):
|
||||
for entrance in region.exits:
|
||||
if isinstance(entrance, PokemonRBWarp):
|
||||
self.multiworld.spoiler.set_entrance(entrance.name, entrance.connected_region.name, "entrance",
|
||||
self.player)
|
||||
warp_ids = (entrance.warp_id,) if isinstance(entrance.warp_id, int) else entrance.warp_id
|
||||
warp_to_ids = (entrance.target,) if isinstance(entrance.target, int) else entrance.target
|
||||
for i, warp_id in enumerate(warp_ids):
|
||||
address = rom_addresses[entrance.address]
|
||||
if "Elevator" in entrance.parent_region.name:
|
||||
address += (2 * warp_id)
|
||||
else:
|
||||
address += (4 * warp_id)
|
||||
while i > len(warp_to_ids) - 1:
|
||||
i -= len(warp_to_ids)
|
||||
connected_map_name = entrance.connected_region.name.split("-")[0]
|
||||
data[address] = 0 if "Elevator" in connected_map_name else warp_to_ids[i]
|
||||
data[address + 1] = map_ids[connected_map_name]
|
||||
|
||||
for i, gym_leader in enumerate(("Pewter Gym - Brock TM", "Cerulean Gym - Misty TM",
|
||||
"Vermilion Gym - Lt. Surge TM", "Celadon Gym - Erika TM",
|
||||
"Fuchsia Gym - Koga TM", "Saffron Gym - Sabrina TM",
|
||||
"Cinnabar Gym - Blaine TM", "Viridian Gym - Giovanni TM")):
|
||||
item_name = self.multiworld.get_location(gym_leader, self.player).item.name
|
||||
if item_name.startswith("TM"):
|
||||
try:
|
||||
tm = int(item_name[2:4])
|
||||
move = poke_data.moves[self.local_tms[tm - 1]]["id"]
|
||||
data[rom_addresses["Gym_Leader_Moves"] + (2 * i)] = move
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def set_trade_mon(address, loc):
|
||||
mon = self.multiworld.get_location(loc, self.player).item.name
|
||||
|
@ -706,48 +266,106 @@ def generate_output(self, output_directory: str):
|
|||
set_trade_mon("Trade_Marcel", "Route 24 - Wild Pokemon - 6")
|
||||
set_trade_mon("Trade_Sailor", "Pokemon Mansion 1F - Wild Pokemon - 3")
|
||||
set_trade_mon("Trade_Dux", "Route 3 - Wild Pokemon - 2")
|
||||
set_trade_mon("Trade_Marc", "Route 23 - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Lola", "Route 10 - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Marc", "Route 23/Cerulean Cave Fishing - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Lola", "Route 10/Celadon Fishing - Super Rod Pokemon - 1")
|
||||
set_trade_mon("Trade_Doris", "Cerulean Cave 1F - Wild Pokemon - 9")
|
||||
set_trade_mon("Trade_Crinkles", "Route 12 - Wild Pokemon - 4")
|
||||
|
||||
data[rom_addresses['Fly_Location']] = self.fly_map_code
|
||||
data[rom_addresses['Map_Fly_Location']] = self.town_map_fly_map_code
|
||||
|
||||
if self.multiworld.fix_combat_bugs[self.player]:
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs"]] = 1
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Focus_Energy"]] = 0x28 # jr z
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_HP_Drain_Dream_Eater"]] = 0x1A # ld a, (de)
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_PP_Restore"]] = 0xe6 # and a, direct
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_PP_Restore"] + 1] = 0b0011111
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Struggle"]] = 0xe6 # and a, direct
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Struggle"] + 1] = 0x3f
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Dig_Fly"]] = 0b10001100
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Heal_Effect"]] = 0x20 # jr nz,
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Heal_Effect"] + 1] = 5 # 5 bytes ahead
|
||||
data[rom_addresses["Option_Fix_Combat_Bugs_Heal_Stat_Modifiers"]] = 1
|
||||
|
||||
if self.multiworld.poke_doll_skip[self.player] == "in_logic":
|
||||
data[rom_addresses["Option_Silph_Scope_Skip"]] = 0x00 # nop
|
||||
data[rom_addresses["Option_Silph_Scope_Skip"] + 1] = 0x00 # nop
|
||||
data[rom_addresses["Option_Silph_Scope_Skip"] + 2] = 0x00 # nop
|
||||
|
||||
if self.multiworld.bicycle_gate_skips[self.player] == "patched":
|
||||
data[rom_addresses["Option_Route_16_Gate_Fix"]] = 0x00 # nop
|
||||
data[rom_addresses["Option_Route_16_Gate_Fix"] + 1] = 0x00 # nop
|
||||
data[rom_addresses["Option_Route_18_Gate_Fix"]] = 0x00 # nop
|
||||
data[rom_addresses["Option_Route_18_Gate_Fix"] + 1] = 0x00 # nop
|
||||
|
||||
if self.multiworld.door_shuffle[self.player]:
|
||||
data[rom_addresses["Entrance_Shuffle_Fuji_Warp"]] = 1 # prevent warping to Fuji's House from Pokemon Tower 7F
|
||||
|
||||
if self.multiworld.all_elevators_locked[self.player]:
|
||||
data[rom_addresses["Option_Locked_Elevator_Celadon"]] = 0x20 # jr nz
|
||||
data[rom_addresses["Option_Locked_Elevator_Silph"]] = 0x20 # jr nz
|
||||
|
||||
if self.multiworld.tea[self.player].value:
|
||||
data[rom_addresses["Option_Tea"]] = 1
|
||||
data[rom_addresses["Guard_Drink_List"]] = 0x54
|
||||
data[rom_addresses["Guard_Drink_List"] + 1] = 0
|
||||
data[rom_addresses["Guard_Drink_List"] + 2] = 0
|
||||
write_bytes(data, encode_text("<LINE>Gee, I have the<CONT>worst caffeine<CONT>headache though."
|
||||
"<PARA>Oh wait there,<LINE>the road's closed.<DONE>"),
|
||||
rom_addresses["Text_Saffron_Gate"])
|
||||
|
||||
data[rom_addresses["Fossils_Needed_For_Second_Item"]] = (
|
||||
self.multiworld.second_fossil_check_condition[self.player].value)
|
||||
|
||||
data[rom_addresses["Option_Lose_Money"]] = int(not self.multiworld.lose_money_on_blackout[self.player].value)
|
||||
|
||||
if self.multiworld.extra_key_items[self.player].value:
|
||||
data[rom_addresses['Options']] |= 4
|
||||
if self.multiworld.extra_key_items[self.player]:
|
||||
data[rom_addresses['Option_Extra_Key_Items_A']] = 1
|
||||
data[rom_addresses['Option_Extra_Key_Items_B']] = 1
|
||||
data[rom_addresses['Option_Extra_Key_Items_C']] = 1
|
||||
data[rom_addresses['Option_Extra_Key_Items_D']] = 1
|
||||
data[rom_addresses["Option_Split_Card_Key"]] = self.multiworld.split_card_key[self.player].value
|
||||
data[rom_addresses["Option_Blind_Trainers"]] = round(self.multiworld.blind_trainers[self.player].value * 2.55)
|
||||
data[rom_addresses['Option_Cerulean_Cave_Condition']] = self.multiworld.cerulean_cave_condition[self.player].value
|
||||
data[rom_addresses["Option_Cerulean_Cave_Badges"]] = self.multiworld.cerulean_cave_badges_condition[self.player].value
|
||||
data[rom_addresses["Option_Cerulean_Cave_Key_Items"]] = self.multiworld.cerulean_cave_key_items_condition[self.player].total
|
||||
write_bytes(data, encode_text(str(self.multiworld.cerulean_cave_badges_condition[self.player].value)), rom_addresses["Text_Cerulean_Cave_Badges"])
|
||||
write_bytes(data, encode_text(str(self.multiworld.cerulean_cave_key_items_condition[self.player].total) + " key items."), rom_addresses["Text_Cerulean_Cave_Key_Items"])
|
||||
data[rom_addresses['Option_Encounter_Minimum_Steps']] = self.multiworld.minimum_steps_between_encounters[self.player].value
|
||||
data[rom_addresses['Option_Victory_Road_Badges']] = self.multiworld.victory_road_condition[self.player].value
|
||||
data[rom_addresses['Option_Pokemon_League_Badges']] = self.multiworld.elite_four_condition[self.player].value
|
||||
data[rom_addresses['Option_Route23_Badges']] = self.multiworld.victory_road_condition[self.player].value
|
||||
data[rom_addresses['Option_Victory_Road_Badges']] = self.multiworld.route_22_gate_condition[self.player].value
|
||||
data[rom_addresses['Option_Elite_Four_Pokedex']] = self.multiworld.elite_four_pokedex_condition[self.player].total
|
||||
data[rom_addresses['Option_Elite_Four_Key_Items']] = self.multiworld.elite_four_key_items_condition[self.player].total
|
||||
data[rom_addresses['Option_Elite_Four_Badges']] = self.multiworld.elite_four_badges_condition[self.player].value
|
||||
write_bytes(data, encode_text(str(self.multiworld.elite_four_badges_condition[self.player].value)), rom_addresses["Text_Elite_Four_Badges"])
|
||||
write_bytes(data, encode_text(str(self.multiworld.elite_four_key_items_condition[self.player].total) + " key items, and"), rom_addresses["Text_Elite_Four_Key_Items"])
|
||||
write_bytes(data, encode_text(str(self.multiworld.elite_four_pokedex_condition[self.player].total) + " #MON"), rom_addresses["Text_Elite_Four_Pokedex"])
|
||||
write_bytes(data, encode_text(str(self.total_key_items), length=2), rom_addresses["Trainer_Screen_Total_Key_Items"])
|
||||
|
||||
data[rom_addresses['Option_Viridian_Gym_Badges']] = self.multiworld.viridian_gym_condition[self.player].value
|
||||
data[rom_addresses['Option_EXP_Modifier']] = self.multiworld.exp_modifier[self.player].value
|
||||
if not self.multiworld.require_item_finder[self.player].value:
|
||||
data[rom_addresses['Option_Itemfinder']] = 0
|
||||
if self.multiworld.extra_strength_boulders[self.player].value:
|
||||
if not self.multiworld.require_item_finder[self.player]:
|
||||
data[rom_addresses['Option_Itemfinder']] = 0 # nop
|
||||
if self.multiworld.extra_strength_boulders[self.player]:
|
||||
for i in range(0, 3):
|
||||
data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15
|
||||
if self.multiworld.extra_key_items[self.player].value:
|
||||
if self.multiworld.extra_key_items[self.player]:
|
||||
for i in range(0, 4):
|
||||
data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15
|
||||
if self.multiworld.old_man[self.player].value == 2:
|
||||
if self.multiworld.old_man[self.player] == "open_viridian_city":
|
||||
data[rom_addresses['Option_Old_Man']] = 0x11
|
||||
data[rom_addresses['Option_Old_Man_Lying']] = 0x15
|
||||
data[rom_addresses['Option_Route3_Guard_A']] = self.multiworld.route_3_condition[self.player].value
|
||||
if self.multiworld.route_3_condition[self.player] == "open":
|
||||
data[rom_addresses['Option_Route3_Guard_B']] = 0x11
|
||||
if not self.multiworld.robbed_house_officer[self.player]:
|
||||
data[rom_addresses['Option_Trashed_House_Guard_A']] = 0x15
|
||||
data[rom_addresses['Option_Trashed_House_Guard_B']] = 0x11
|
||||
if self.multiworld.require_pokedex[self.player]:
|
||||
data[rom_addresses["Require_Pokedex_A"]] = 1
|
||||
data[rom_addresses["Require_Pokedex_B"]] = 1
|
||||
data[rom_addresses["Require_Pokedex_C"]] = 1
|
||||
else:
|
||||
data[rom_addresses["Require_Pokedex_D"]] = 0x18 # jr
|
||||
if self.multiworld.dexsanity[self.player]:
|
||||
data[rom_addresses["Option_Dexsanity_A"]] = 1
|
||||
data[rom_addresses["Option_Dexsanity_B"]] = 1
|
||||
|
@ -759,13 +377,19 @@ def generate_output(self, output_directory: str):
|
|||
data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16)
|
||||
data[rom_addresses["Text_Badges_Needed_Viridian_Gym"]] = encode_text(
|
||||
str(self.multiworld.viridian_gym_condition[self.player].value))[0]
|
||||
data[rom_addresses["Text_Rt23_Badges_A"]] = encode_text(
|
||||
str(self.multiworld.victory_road_condition[self.player].value))[0]
|
||||
data[rom_addresses["Text_Rt23_Badges_B"]] = encode_text(
|
||||
str(self.multiworld.victory_road_condition[self.player].value))[0]
|
||||
data[rom_addresses["Text_Rt23_Badges_C"]] = encode_text(
|
||||
str(self.multiworld.victory_road_condition[self.player].value))[0]
|
||||
data[rom_addresses["Text_Rt23_Badges_D"]] = encode_text(
|
||||
str(self.multiworld.victory_road_condition[self.player].value))[0]
|
||||
data[rom_addresses["Text_Badges_Needed"]] = encode_text(
|
||||
str(max(self.multiworld.victory_road_condition[self.player].value,
|
||||
self.multiworld.elite_four_condition[self.player].value)))[0]
|
||||
str(self.multiworld.elite_four_badges_condition[self.player].value))[0]
|
||||
write_bytes(data, encode_text(
|
||||
" ".join(self.multiworld.get_location("Route 3 - Pokemon For Sale", self.player).item.name.upper().split()[1:])),
|
||||
" ".join(self.multiworld.get_location("Route 4 Pokemon Center - Pokemon For Sale", self.player).item.name.upper().split()[1:])),
|
||||
rom_addresses["Text_Magikarp_Salesman"])
|
||||
write_quizzes(self, data, random)
|
||||
|
||||
if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0:
|
||||
for hm_move in poke_data.hm_moves:
|
||||
|
@ -845,7 +469,7 @@ def generate_output(self, output_directory: str):
|
|||
|
||||
data[rom_addresses["Option_Always_Half_STAB"]] = int(not self.multiworld.same_type_attack_bonus[self.player].value)
|
||||
|
||||
if self.multiworld.better_shops[self.player].value:
|
||||
if self.multiworld.better_shops[self.player]:
|
||||
inventory = ["Poke Ball", "Great Ball", "Ultra Ball"]
|
||||
if self.multiworld.better_shops[self.player].value == 2:
|
||||
inventory.append("Master Ball")
|
||||
|
@ -855,8 +479,11 @@ def generate_output(self, output_directory: str):
|
|||
shop_data = bytearray([0xFE, len(inventory)])
|
||||
shop_data += bytearray([item_table[item].id - 172000000 for item in inventory])
|
||||
shop_data.append(0xFF)
|
||||
for shop in range(1, 10):
|
||||
for shop in range(1, 11):
|
||||
write_bytes(data, shop_data, rom_addresses[f"Shop{shop}"])
|
||||
if self.multiworld.stonesanity[self.player]:
|
||||
write_bytes(data, bytearray([0xFE, 1, item_table["Poke Doll"].id - 172000000, 0xFF]), rom_addresses[f"Shop_Stones"])
|
||||
|
||||
price = str(self.multiworld.master_ball_price[self.player].value).zfill(6)
|
||||
price = bytearray([int(price[:2], 16), int(price[2:4], 16), int(price[4:], 16)])
|
||||
write_bytes(data, price, rom_addresses["Price_Master_Ball"]) # Money values in Red and Blue are weird
|
||||
|
@ -866,7 +493,6 @@ def generate_output(self, output_directory: str):
|
|||
data[rom_addresses["Start_Inventory"] + item.code - 172000000] += 1
|
||||
|
||||
set_mon_palettes(self, random, data)
|
||||
process_trainer_data(self, data, random)
|
||||
|
||||
for move_data in self.local_move_data.values():
|
||||
if move_data["id"] == 0:
|
||||
|
@ -888,13 +514,13 @@ def generate_output(self, output_directory: str):
|
|||
for mon in range(0, 16):
|
||||
data[rom_addresses['Title_Mons'] + mon] = mons.pop()
|
||||
if self.multiworld.game_version[self.player].value:
|
||||
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Pallet Town - Starter 1", self.player).item.name
|
||||
else 1 if mon == self.multiworld.get_location("Pallet Town - Starter 2", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
|
||||
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name
|
||||
else 1 if mon == self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name else 3)
|
||||
else:
|
||||
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Pallet Town - Starter 2", self.player).item.name
|
||||
else 1 if mon == self.multiworld.get_location("Pallet Town - Starter 1", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Pallet Town - Starter 3", self.player).item.name else 3)
|
||||
mons.sort(key=lambda mon: 0 if mon == self.multiworld.get_location("Oak's Lab - Starter 2", self.player).item.name
|
||||
else 1 if mon == self.multiworld.get_location("Oak's Lab - Starter 1", self.player).item.name else
|
||||
2 if mon == self.multiworld.get_location("Oak's Lab - Starter 3", self.player).item.name else 3)
|
||||
write_bytes(data, encode_text(self.multiworld.seed_name[-20:], 20, True), rom_addresses['Title_Seed'])
|
||||
|
||||
slot_name = self.multiworld.player_name[self.player]
|
||||
|
@ -912,10 +538,68 @@ def generate_output(self, output_directory: str):
|
|||
else:
|
||||
write_bytes(data, self.rival_name, rom_addresses['Rival_Name'])
|
||||
|
||||
data[0xFF00] = 2 # client compatibility version
|
||||
data[0xFF00] = 2 # client compatibility version
|
||||
write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDB)
|
||||
write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0)
|
||||
|
||||
self.finished_level_scaling.wait()
|
||||
|
||||
write_quizzes(self, data, random)
|
||||
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.player != self.player:
|
||||
continue
|
||||
elif location.party_data:
|
||||
for party in location.party_data:
|
||||
if not isinstance(party["party_address"], list):
|
||||
addresses = [rom_addresses[party["party_address"]]]
|
||||
parties = [party["party"]]
|
||||
else:
|
||||
addresses = [rom_addresses[address] for address in party["party_address"]]
|
||||
parties = party["party"]
|
||||
levels = party["level"]
|
||||
for address, party in zip(addresses, parties):
|
||||
if isinstance(levels, int):
|
||||
data[address] = levels
|
||||
address += 1
|
||||
for mon in party:
|
||||
data[address] = poke_data.pokemon_data[mon]["id"]
|
||||
address += 1
|
||||
else:
|
||||
address += 1
|
||||
for level, mon in zip(levels, party):
|
||||
data[address] = level
|
||||
data[address + 1] = poke_data.pokemon_data[mon]["id"]
|
||||
address += 2
|
||||
assert data[address] == 0 or location.name == "Fossil Level - Trainer Parties"
|
||||
continue
|
||||
elif location.rom_address is None:
|
||||
continue
|
||||
if location.item and location.item.player == self.player:
|
||||
if location.rom_address:
|
||||
rom_address = location.rom_address
|
||||
if not isinstance(rom_address, list):
|
||||
rom_address = [rom_address]
|
||||
for address in rom_address:
|
||||
if location.item.name in poke_data.pokemon_data.keys():
|
||||
data[address] = poke_data.pokemon_data[location.item.name]["id"]
|
||||
elif " ".join(location.item.name.split()[1:]) in poke_data.pokemon_data.keys():
|
||||
data[address] = poke_data.pokemon_data[" ".join(location.item.name.split()[1:])]["id"]
|
||||
else:
|
||||
item_id = self.item_name_to_id[location.item.name] - 172000000
|
||||
if item_id > 255:
|
||||
item_id -= 256
|
||||
data[address] = item_id
|
||||
if location.level:
|
||||
data[location.level_address] = location.level
|
||||
|
||||
else:
|
||||
rom_address = location.rom_address
|
||||
if not isinstance(rom_address, list):
|
||||
rom_address = [rom_address]
|
||||
for address in rom_address:
|
||||
data[address] = 0x2C # AP Item
|
||||
|
||||
outfilepname = f'_P{self.player}'
|
||||
outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \
|
||||
if self.multiworld.player_name[self.player] != 'Player%d' % self.player else ''
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,229 +1,279 @@
|
|||
from ..generic.Rules import add_item_rule, add_rule, location_item_name
|
||||
from .items import item_groups
|
||||
from . import logic
|
||||
|
||||
|
||||
def set_rules(world, player):
|
||||
def set_rules(multiworld, player):
|
||||
|
||||
item_rules = {
|
||||
"Pallet Town - Player's PC": (lambda i: i.player == player and "Badge" not in i.name and "Trap" not in i.name
|
||||
and i.name != "Pokedex" and "Coins" not in i.name)
|
||||
# Some items do special things when they are passed into the GiveItem function in the game, but
|
||||
# withdrawing from the PC or buying from a shop will not call the function and will add the items
|
||||
# directly to the inventory, so we need to avoid placing these special items (including "AP Item") to
|
||||
# such places
|
||||
"Player's House 2F - Player's PC": (lambda i: i.player == player and "Badge" not in i.name and "Trap" not in
|
||||
i.name and i.name != "Pokedex" and "Coins" not in i.name and "Progressive"
|
||||
not in i.name)
|
||||
}
|
||||
|
||||
if world.prizesanity[player]:
|
||||
if multiworld.prizesanity[player]:
|
||||
def prize_rule(i):
|
||||
return i.player != player or i.name in item_groups["Unique"]
|
||||
item_rules["Celadon Prize Corner - Item Prize 1"] = prize_rule
|
||||
item_rules["Celadon Prize Corner - Item Prize 2"] = prize_rule
|
||||
item_rules["Celadon Prize Corner - Item Prize 3"] = prize_rule
|
||||
|
||||
if world.accessibility[player] != "locations":
|
||||
world.get_location("Cerulean City - Bicycle Shop", player).always_allow = (lambda state, item:
|
||||
if multiworld.accessibility[player] != "locations":
|
||||
multiworld.get_location("Cerulean Bicycle Shop", player).always_allow = (lambda state, item:
|
||||
item.name == "Bike Voucher"
|
||||
and item.player == player)
|
||||
world.get_location("Fuchsia City - Safari Zone Warden", player).always_allow = (lambda state, item:
|
||||
multiworld.get_location("Fuchsia Warden's House - Safari Zone Warden", player).always_allow = (lambda state, item:
|
||||
item.name == "Gold Teeth" and
|
||||
item.player == player)
|
||||
|
||||
access_rules = {
|
||||
"Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player),
|
||||
"Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player),
|
||||
"Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player),
|
||||
"Route 2 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_2[player].value + 5, player),
|
||||
"Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player)
|
||||
or location_item_name(state, "Cerulean City - Bicycle Shop", player) == ("Bike Voucher", player),
|
||||
"Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player),
|
||||
"Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
|
||||
"Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)),
|
||||
"Route 11 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_11[player].value + 5, player),
|
||||
"Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Silph Co 11F - Silph Co President (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player)
|
||||
or location_item_name(state, "Fuchsia City - Safari Zone Warden", player) == ("Gold Teeth", player),
|
||||
"Route 12 - Island Item": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Route 12 - Item Behind Cuttable Tree": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Route 15 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_15[player].value + 5, player),
|
||||
"Route 15 - Item": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Route 25 - Item": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Fuchsia City - Warden's House Item": lambda state: state.pokemon_rb_can_strength(player),
|
||||
"Rocket Hideout B4F - Southwest Item (Lift Key)": lambda state: state.has("Lift Key", player),
|
||||
"Rocket Hideout B4F - Giovanni Item (Lift Key)": lambda state: state.has("Lift Key", player),
|
||||
"Silph Co 3F - Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 4F - Left Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 4F - Middle Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 4F - Right Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 5F - Northwest Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 6F - West Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 6F - Southwest Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 7F - East Item (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Safari Zone Center - Island Item": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Celadon Prize Corner - Item Prize 1": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Item Prize 2": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Item Prize 3": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - West Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Center Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - East Gambler's Gift (Coin Case)": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Northwest By Counter (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Southwest Corner (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near Rumor Man (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near Speculating Woman (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near West Gifting Gambler (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near Wonderful Time Woman (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near Failing Gym Information Guy (Coin Case)": lambda state: state.has( "Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near East Gifting Gambler (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item Near Hooked Guy (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item at End of Horizontal Machine Row (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon Game Corner - Hidden Item in Front of Horizontal Machine Row (Coin Case)": lambda state: state.has("Coin Case", player) and state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Rival's House - Rival's Sister": lambda state: state.has("Oak's Parcel", player),
|
||||
"Oak's Lab - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player),
|
||||
"Viridian City - Sleepy Guy": lambda state: logic.can_cut(state, player) or logic.can_surf(state, player),
|
||||
"Route 2 Gate - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_2[player].value + 5, player),
|
||||
"Cerulean Bicycle Shop": lambda state: state.has("Bike Voucher", player)
|
||||
or location_item_name(state, "Cerulean Bicycle Shop", player) == ("Bike Voucher", player),
|
||||
"Lavender Mr. Fuji's House - Mr. Fuji": lambda state: state.has("Fuji Saved", player),
|
||||
"Route 11 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_11[player].value + 5, player),
|
||||
"Celadon City - Stranded Man": lambda state: logic.can_surf(state, player),
|
||||
"Fuchsia Warden's House - Safari Zone Warden": lambda state: state.has("Gold Teeth", player)
|
||||
or location_item_name(state, "Fuchsia Warden's House - Safari Zone Warden", player) == ("Gold Teeth", player),
|
||||
"Route 12 - Island Item": lambda state: logic.can_surf(state, player),
|
||||
"Route 15 Gate 2F - Oak's Aide": lambda state: logic.oaks_aide(state, state.multiworld.oaks_aide_rt_15[player].value + 5, player),
|
||||
"Route 25 - Item": lambda state: logic.can_cut(state, player),
|
||||
"Fuchsia Warden's House - Behind Boulder Item": lambda state: logic.can_strength(state, player),
|
||||
"Safari Zone Center - Island Item": lambda state: logic.can_surf(state, player),
|
||||
"Saffron Copycat's House 2F - Copycat": lambda state: state.has("Buy Poke Doll", player),
|
||||
|
||||
"Silph Co 11F - Silph Co Liberated": lambda state: state.has("Card Key", player),
|
||||
"Celadon Game Corner - West Gambler's Gift": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Center Gambler's Gift": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - East Gambler's Gift": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Game Corner - Hidden Item Northwest By Counter": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Southwest Corner": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near Rumor Man": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near Speculating Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near West Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near Wonderful Time Woman": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near Failing Gym Information Guy": lambda state: state.has( "Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near East Gifting Gambler": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item Near Hooked Guy": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item at End of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
"Celadon Game Corner - Hidden Item in Front of Horizontal Machine Row": lambda state: state.has("Coin Case", player) and logic.can_get_hidden_items(state, player),
|
||||
|
||||
"Pallet Town - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Pallet Town - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 22 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 22 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 24 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 24 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 24 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Route 6 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 6 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 10 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 10 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Safari Zone Center - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Safari Zone Center - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Safari Zone Center - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Safari Zone Center - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player),
|
||||
"Route 12 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 12 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 12 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Route 12 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player),
|
||||
"Route 19 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 19 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 19 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Route 19 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player),
|
||||
"Route 23 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Route 23 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Route 23 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Route 23 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player),
|
||||
"Fuchsia City - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player),
|
||||
"Fuchsia City - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player),
|
||||
"Fuchsia City - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player),
|
||||
"Fuchsia City - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player),
|
||||
"Anywhere - Good Rod Pokemon - 1": lambda state: state.has("Good Rod", player),
|
||||
"Anywhere - Good Rod Pokemon - 2": lambda state: state.has("Good Rod", player),
|
||||
"Anywhere - Old Rod Pokemon": lambda state: state.has("Old Rod", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 1": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 2": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 3": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 4": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 5": lambda state: state.has("Coin Case", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 6": lambda state: state.has("Coin Case", player),
|
||||
"Cinnabar Island - Old Amber Pokemon": lambda state: state.has("Old Amber", player),
|
||||
"Cinnabar Island - Helix Fossil Pokemon": lambda state: state.has("Helix Fossil", player),
|
||||
"Cinnabar Island - Dome Fossil Pokemon": lambda state: state.has("Dome Fossil", player),
|
||||
"Celadon Prize Corner - Item Prize 1": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Item Prize 2": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Item Prize 3": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
|
||||
"Celadon Prize Corner - Pokemon Prize - 1": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 2": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 3": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 4": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 5": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Celadon Prize Corner - Pokemon Prize - 6": lambda state: state.has("Coin Case", player) and state.has("Game Corner", player),
|
||||
"Cinnabar Lab Fossil Room - Old Amber Pokemon": lambda state: state.has("Old Amber", player) and state.has("Cinnabar Island", player),
|
||||
"Cinnabar Lab Fossil Room - Helix Fossil Pokemon": lambda state: state.has("Helix Fossil", player) and state.has("Cinnabar Island", player),
|
||||
"Cinnabar Lab Fossil Room - Dome Fossil Pokemon": lambda state: state.has("Dome Fossil", player) and state.has("Cinnabar Island", player),
|
||||
"Route 12 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player),
|
||||
"Route 16 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player),
|
||||
"Seafoam Islands B4F - Legendary Pokemon": lambda state: state.pokemon_rb_can_strength(player),
|
||||
"Vermilion City - Legendary Pokemon": lambda state: state.pokemon_rb_can_surf(player) and state.has("S.S. Ticket", player),
|
||||
"Seafoam Islands B4F - Legendary Pokemon": lambda state: logic.can_strength(state, player) and state.has("Seafoam Boss Boulders", player),
|
||||
"Vermilion Dock - Legendary Pokemon": lambda state: logic.can_surf(state, player),
|
||||
"Cerulean Cave B1F - Legendary Pokemon": lambda state: logic.can_surf(state, player),
|
||||
|
||||
**{f"Pokemon Tower {floor}F - Wild Pokemon - {slot}": lambda state: state.has("Silph Scope", player) for floor in range(3, 8) for slot in range(1, 11)},
|
||||
"Pokemon Tower 6F - Restless Soul": lambda state: state.has("Silph Scope", player), # just for level scaling
|
||||
|
||||
"Route 2 - Marcel Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Underground Tunnel West-East - Spot Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Route 11 - Terry Trade": lambda state: state.can_reach("Safari Zone Center - Wild Pokemon - 5", "Location", player),
|
||||
"Route 18 - Marc Trade": lambda state: state.can_reach("Route 23 - Super Rod Pokemon - 1", "Location", player),
|
||||
"Cinnabar Island - Sailor Trade": lambda state: state.can_reach("Pokemon Mansion 1F - Wild Pokemon - 3", "Location", player),
|
||||
"Cinnabar Island - Crinkles Trade": lambda state: state.can_reach("Route 12 - Wild Pokemon - 4", "Location", player),
|
||||
"Cinnabar Island - Doris Trade": lambda state: state.can_reach("Cerulean Cave 1F - Wild Pokemon - 9", "Location", player),
|
||||
"Vermilion City - Dux Trade": lambda state: state.can_reach("Route 3 - Wild Pokemon - 2", "Location", player),
|
||||
"Cerulean City - Lola Trade": lambda state: state.can_reach("Route 10 - Super Rod Pokemon - 1", "Location", player),
|
||||
"Silph Co 1F - Receptionist": lambda state: state.has("Silph Co Liberated", player),
|
||||
"Silph Co 5F - Hostage": lambda state: logic.card_key(state, 5, player),
|
||||
"Silph Co 7F - Hostage": lambda state: logic.card_key(state, 7, player),
|
||||
|
||||
"Route 2 Trade House - Marcel Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Underground Path Route 5 - Spot Trade": lambda state: state.can_reach("Route 24 - Wild Pokemon - 6", "Location", player),
|
||||
"Route 11 Gate 2F - Terry Trade": lambda state: state.can_reach("Safari Zone Center - Wild Pokemon - 5", "Location", player),
|
||||
"Route 18 Gate 2F - Marc Trade": lambda state: state.can_reach("Route 23/Cerulean Cave Fishing - Super Rod Pokemon - 1", "Location", player),
|
||||
"Cinnabar Lab Fossil Room - Sailor Trade": lambda state: state.can_reach("Pokemon Mansion 1F - Wild Pokemon - 3", "Location", player),
|
||||
"Cinnabar Lab Trade Room - Crinkles Trade": lambda state: state.can_reach("Route 12 - Wild Pokemon - 4", "Location", player),
|
||||
"Cinnabar Lab Trade Room - Doris Trade": lambda state: state.can_reach("Cerulean Cave 1F - Wild Pokemon - 9", "Location", player),
|
||||
"Vermilion Trade House - Dux Trade": lambda state: state.can_reach("Route 3 - Wild Pokemon - 2", "Location", player),
|
||||
"Cerulean Trade House - Lola Trade": lambda state: state.can_reach("Route 10/Celadon Fishing - Super Rod Pokemon - 1", "Location", player),
|
||||
|
||||
"Route 22 - Trainer Parties": lambda state: state.has("Oak's Parcel", player),
|
||||
|
||||
# # Rock Tunnel
|
||||
# "Rock Tunnel 1F - PokeManiac": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Hiker 1": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Hiker 2": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Hiker 3": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel 1F - Jr. Trainer F 3": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - PokeManiac 1": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - PokeManiac 2": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - PokeManiac 3": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Jr. Trainer F 1": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Jr. Trainer F 2": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Hiker 1": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Hiker 2": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Hiker 3": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - North Item": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Northwest Item": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - Southwest Item": lambda state: logic.rock_tunnel(state, player),
|
||||
# "Rock Tunnel B1F - West Item": lambda state: logic.rock_tunnel(state, player),
|
||||
|
||||
# Pokédex check
|
||||
"Pallet Town - Oak's Parcel Reward": lambda state: state.has("Oak's Parcel", player),
|
||||
"Oak's Lab - Oak's Parcel Reward": lambda state: state.has("Oak's Parcel", player),
|
||||
|
||||
# trainers
|
||||
"Route 4 - Cooltrainer F": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Route 15 - Jr. Trainer F 1": lambda state: state.pokemon_rb_can_cut(player),
|
||||
"Silph Co 11F - Rocket 2 (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 9F - Rocket 2 (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Silph Co 3F - Scientist (Card Key)": lambda state: state.has("Card Key", player),
|
||||
"Route 10 - Pokemaniac": lambda state: state.pokemon_rb_can_surf(player),
|
||||
"Rocket Hideout B1F - Rocket 5 (Lift Key)": lambda state: state.has("Lift Key", player),
|
||||
"Rocket Hideout B4F - Rocket 2 (Lift Key)": lambda state: state.has("Lift Key", player),
|
||||
"Rocket Hideout B4F - Rocket 3 (Lift Key)": lambda state: state.has("Lift Key", player),
|
||||
|
||||
# hidden items
|
||||
"Viridian Forest - Hidden Item Northwest by Trainer": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
# Hidden items
|
||||
"Viridian Forest - Hidden Item Northwest by Trainer": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Viridian Forest - Hidden Item Entrance Tree": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Mt Moon B2F - Hidden Item Dead End Before Fossils": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Viridian Forest - Hidden Item Entrance Tree": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Mt Moon B2F - Hidden Item Dead End Before Fossils": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 25 - Hidden Item Fence Outside Bill's House": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 25 - Hidden Item Fence Outside Bill's House": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 9 - Hidden Item Bush By Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"S.S. Anne 1F - Hidden Item Kitchen Trash": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"S.S. Anne B1F - Hidden Item Under Pillow": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 9 - Hidden Item Bush By Grass": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"S.S. Anne Kitchen - Hidden Item Kitchen Trash": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"S.S. Anne B1F Rooms - Hidden Item Under Pillow": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 10 - Hidden Item Behind Rock Tunnel Entrance Cuttable Tree": lambda
|
||||
state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 10 - Hidden Item Bush": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Rocket Hideout B1F - Hidden Item Pot Plant": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Rocket Hideout B3F - Hidden Item Near East Item": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Rocket Hideout B4F - Hidden Item Behind Giovanni (Lift Key)": lambda state:
|
||||
state.pokemon_rb_can_get_hidden_items(player) and state.has("Lift Key", player),
|
||||
"Pokemon Tower 5F - Hidden Item Near West Staircase": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
state: logic.can_get_hidden_items(state, player) and logic.can_cut(state, player),
|
||||
"Route 10 - Hidden Item Bush": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Rocket Hideout B1F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Rocket Hideout B3F - Hidden Item Near East Item": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Rocket Hideout B4F - Hidden Item Behind Giovanni": lambda state:
|
||||
logic.can_get_hidden_items(state, player),
|
||||
"Pokemon Tower 5F - Hidden Item Near West Staircase": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 13 - Hidden Item Dead End Bush": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 13 - Hidden Item Dead End By Water Corner": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Pokemon Mansion B1F - Hidden Item Secret Key Room Corner": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 13 - Hidden Item Dead End Bush": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 13 - Hidden Item Dead End By Water Corner": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Pokemon Mansion B1F - Hidden Item Secret Key Room Corner": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Safari Zone West - Hidden Item Secret House Statue": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Safari Zone West - Hidden Item Secret House Statue": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Silph Co 5F - Hidden Item Pot Plant": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Silph Co 9F - Hidden Item Nurse Bed (Card Key)": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
player) and state.has("Card Key", player),
|
||||
"Copycat's House - Hidden Item Desk": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Cerulean Cave 1F - Hidden Item Center Rocks": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Cerulean Cave B1F - Hidden Item Northeast Rocks": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Power Plant - Hidden Item Central Dead End": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Power Plant - Hidden Item Before Zapdos": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Seafoam Islands B2F - Hidden Item Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Seafoam Islands B4F - Hidden Item Corner Island": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Silph Co 5F - Hidden Item Pot Plant": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Silph Co 9F - Hidden Item Nurse Bed": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Saffron Copycat's House 2F - Hidden Item Desk": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Cerulean Cave 1F - Hidden Item Center Rocks": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Cerulean Cave B1F - Hidden Item Northeast Rocks": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Power Plant - Hidden Item Central Dead End": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Power Plant - Hidden Item Before Zapdos": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Seafoam Islands B2F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Seafoam Islands B3F - Hidden Item Rock": lambda state: logic.can_get_hidden_items(state, player),
|
||||
# if you can reach any exit boulders, that means you can drop into the water tunnel and auto-surf
|
||||
"Seafoam Islands B4F - Hidden Item Corner Island": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Pokemon Mansion 1F - Hidden Item Block Near Entrance Carpet": lambda
|
||||
state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 23 - Hidden Item Rocks Before Victory Road": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
state: logic.can_get_hidden_items(state, player),
|
||||
"Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 23 - Hidden Item Rocks Before Victory Road": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 23 - Hidden Item East Bush After Water": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 23 - Hidden Item East Bush After Water": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 23 - Hidden Item On Island": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Victory Road 2F - Hidden Item Rock Before Moltres": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 23 - Hidden Item On Island": lambda state: logic.can_get_hidden_items(state,
|
||||
player) and logic.can_surf(state, player),
|
||||
"Victory Road 2F - Hidden Item Rock Before Moltres": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Victory Road 2F - Hidden Item Rock In Final Room": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Viridian City - Hidden Item Cuttable Tree": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 11 - Hidden Item Isolated Bush Near Gate": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 12 - Hidden Item Bush Near Gate": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 17 - Hidden Item In Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 17 - Hidden Item Near Northernmost Sign": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 17 - Hidden Item East Center": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 17 - Hidden Item West Center": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 17 - Hidden Item Before Final Bridge": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Underground Tunnel North-South - Hidden Item Near Northern Stairs": lambda
|
||||
state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Underground Tunnel North-South - Hidden Item Near Southern Stairs": lambda
|
||||
state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Underground Tunnel West-East - Hidden Item West": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Underground Tunnel West-East - Hidden Item East": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Celadon City - Hidden Item Dead End Near Cuttable Tree": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Victory Road 2F - Hidden Item Rock In Final Room": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Viridian City - Hidden Item Cuttable Tree": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 11 - Hidden Item Isolated Bush Near Gate": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 12 - Hidden Item Bush Near Gate": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 17 - Hidden Item In Grass": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 17 - Hidden Item Near Northernmost Sign": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 17 - Hidden Item East Center": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 17 - Hidden Item West Center": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Route 17 - Hidden Item Before Final Bridge": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Underground Path North South - Hidden Item Near Northern Stairs": lambda
|
||||
state: logic.can_get_hidden_items(state, player),
|
||||
"Underground Path North South - Hidden Item Near Southern Stairs": lambda
|
||||
state: logic.can_get_hidden_items(state, player),
|
||||
"Underground Path West East - Hidden Item West": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Underground Path West East - Hidden Item East": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Celadon City - Hidden Item Dead End Near Cuttable Tree": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 25 - Hidden Item Northeast Of Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Mt Moon B2F - Hidden Item Lone Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Seafoam Islands B3F - Hidden Item Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Vermilion City - Hidden Item In Water Near Fan Club": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
player) and state.pokemon_rb_can_surf(player),
|
||||
"Cerulean City - Hidden Item Gym Badge Guy's Backyard": lambda state: state.pokemon_rb_can_get_hidden_items(
|
||||
"Route 25 - Hidden Item Northeast Of Grass": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Mt Moon B2F - Hidden Item Lone Rock": lambda state: logic.can_get_hidden_items(state, player),
|
||||
"Vermilion City - Hidden Item In Water Near Fan Club": lambda state: logic.can_get_hidden_items(state,
|
||||
player) and logic.can_surf(state, player),
|
||||
"Cerulean City - Hidden Item Gym Badge Guy's Backyard": lambda state: logic.can_get_hidden_items(state,
|
||||
player),
|
||||
"Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: state.pokemon_rb_can_get_hidden_items(player),
|
||||
"Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: logic.can_get_hidden_items(state, player),
|
||||
|
||||
# Evolutions
|
||||
"Evolution - Ivysaur": lambda state: state.has("Bulbasaur", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Venusaur": lambda state: state.has("Ivysaur", player) and logic.evolve_level(state, 32, player),
|
||||
"Evolution - Charmeleon": lambda state: state.has("Charmander", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Charizard": lambda state: state.has("Charmeleon", player) and logic.evolve_level(state, 36, player),
|
||||
"Evolution - Wartortle": lambda state: state.has("Squirtle", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Blastoise": lambda state: state.has("Wartortle", player) and logic.evolve_level(state, 36, player),
|
||||
"Evolution - Metapod": lambda state: state.has("Caterpie", player) and logic.evolve_level(state, 7, player),
|
||||
"Evolution - Butterfree": lambda state: state.has("Metapod", player) and logic.evolve_level(state, 10, player),
|
||||
"Evolution - Kakuna": lambda state: state.has("Weedle", player) and logic.evolve_level(state, 7, player),
|
||||
"Evolution - Beedrill": lambda state: state.has("Kakuna", player) and logic.evolve_level(state, 10, player),
|
||||
"Evolution - Pidgeotto": lambda state: state.has("Pidgey", player) and logic.evolve_level(state, 18, player),
|
||||
"Evolution - Pidgeot": lambda state: state.has("Pidgeotto", player) and logic.evolve_level(state, 36, player),
|
||||
"Evolution - Raticate": lambda state: state.has("Rattata", player) and logic.evolve_level(state, 20, player),
|
||||
"Evolution - Fearow": lambda state: state.has("Spearow", player) and logic.evolve_level(state, 20, player),
|
||||
"Evolution - Arbok": lambda state: state.has("Ekans", player) and logic.evolve_level(state, 22, player),
|
||||
"Evolution - Raichu": lambda state: state.has("Pikachu", player) and state.has("Thunder Stone", player),
|
||||
"Evolution - Sandslash": lambda state: state.has("Sandshrew", player) and logic.evolve_level(state, 22, player),
|
||||
"Evolution - Nidorina": lambda state: state.has("Nidoran F", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Nidoqueen": lambda state: state.has("Nidorina", player) and state.has("Moon Stone", player),
|
||||
"Evolution - Nidorino": lambda state: state.has("Nidoran M", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Nidoking": lambda state: state.has("Nidorino", player) and state.has("Moon Stone", player),
|
||||
"Evolution - Clefable": lambda state: state.has("Clefairy", player) and state.has("Moon Stone", player),
|
||||
"Evolution - Ninetales": lambda state: state.has("Vulpix", player) and state.has("Fire Stone", player),
|
||||
"Evolution - Wigglytuff": lambda state: state.has("Jigglypuff", player) and state.has("Moon Stone", player),
|
||||
"Evolution - Golbat": lambda state: state.has("Zubat", player) and logic.evolve_level(state, 22, player),
|
||||
"Evolution - Gloom": lambda state: state.has("Oddish", player) and logic.evolve_level(state, 21, player),
|
||||
"Evolution - Vileplume": lambda state: state.has("Gloom", player) and state.has("Leaf Stone", player),
|
||||
"Evolution - Parasect": lambda state: state.has("Paras", player) and logic.evolve_level(state, 24, player),
|
||||
"Evolution - Venomoth": lambda state: state.has("Venonat", player) and logic.evolve_level(state, 31, player),
|
||||
"Evolution - Dugtrio": lambda state: state.has("Diglett", player) and logic.evolve_level(state, 26, player),
|
||||
"Evolution - Persian": lambda state: state.has("Meowth", player) and logic.evolve_level(state, 28, player),
|
||||
"Evolution - Golduck": lambda state: state.has("Psyduck", player) and logic.evolve_level(state, 33, player),
|
||||
"Evolution - Primeape": lambda state: state.has("Mankey", player) and logic.evolve_level(state, 28, player),
|
||||
"Evolution - Arcanine": lambda state: state.has("Growlithe", player) and state.has("Fire Stone", player),
|
||||
"Evolution - Poliwhirl": lambda state: state.has("Poliwag", player) and logic.evolve_level(state, 25, player),
|
||||
"Evolution - Poliwrath": lambda state: state.has("Poliwhirl", player) and state.has("Water Stone", player),
|
||||
"Evolution - Kadabra": lambda state: state.has("Abra", player) and logic.evolve_level(state, 16, player),
|
||||
"Evolution - Alakazam": lambda state: state.has("Kadabra", player) and logic.evolve_level(state, 35, player),
|
||||
"Evolution - Machoke": lambda state: state.has("Machop", player) and logic.evolve_level(state, 28, player),
|
||||
"Evolution - Machamp": lambda state: state.has("Machoke", player) and logic.evolve_level(state, 35, player),
|
||||
"Evolution - Weepinbell": lambda state: state.has("Bellsprout", player) and logic.evolve_level(state, 21, player),
|
||||
"Evolution - Victreebel": lambda state: state.has("Weepinbell", player) and state.has("Leaf Stone", player),
|
||||
"Evolution - Tentacruel": lambda state: state.has("Tentacool", player) and logic.evolve_level(state, 30, player),
|
||||
"Evolution - Graveler": lambda state: state.has("Geodude", player) and logic.evolve_level(state, 25, player),
|
||||
"Evolution - Golem": lambda state: state.has("Graveler", player) and logic.evolve_level(state, 35, player),
|
||||
"Evolution - Rapidash": lambda state: state.has("Ponyta", player) and logic.evolve_level(state, 40, player),
|
||||
"Evolution - Slowbro": lambda state: state.has("Slowpoke", player) and logic.evolve_level(state, 37, player),
|
||||
"Evolution - Magneton": lambda state: state.has("Magnemite", player) and logic.evolve_level(state, 30, player),
|
||||
"Evolution - Dodrio": lambda state: state.has("Doduo", player) and logic.evolve_level(state, 31, player),
|
||||
"Evolution - Dewgong": lambda state: state.has("Seel", player) and logic.evolve_level(state, 34, player),
|
||||
"Evolution - Muk": lambda state: state.has("Grimer", player) and logic.evolve_level(state, 38, player),
|
||||
"Evolution - Cloyster": lambda state: state.has("Shellder", player) and state.has("Water Stone", player),
|
||||
"Evolution - Haunter": lambda state: state.has("Gastly", player) and logic.evolve_level(state, 25, player),
|
||||
"Evolution - Gengar": lambda state: state.has("Haunter", player) and logic.evolve_level(state, 35, player),
|
||||
"Evolution - Hypno": lambda state: state.has("Drowzee", player) and logic.evolve_level(state, 26, player),
|
||||
"Evolution - Kingler": lambda state: state.has("Krabby", player) and logic.evolve_level(state, 28, player),
|
||||
"Evolution - Electrode": lambda state: state.has("Voltorb", player) and logic.evolve_level(state, 30, player),
|
||||
"Evolution - Exeggutor": lambda state: state.has("Exeggcute", player) and state.has("Leaf Stone", player),
|
||||
"Evolution - Marowak": lambda state: state.has("Cubone", player) and logic.evolve_level(state, 28, player),
|
||||
"Evolution - Weezing": lambda state: state.has("Koffing", player) and logic.evolve_level(state, 35, player),
|
||||
"Evolution - Rhydon": lambda state: state.has("Rhyhorn", player) and logic.evolve_level(state, 42, player),
|
||||
"Evolution - Seadra": lambda state: state.has("Horsea", player) and logic.evolve_level(state, 32, player),
|
||||
"Evolution - Seaking": lambda state: state.has("Goldeen", player) and logic.evolve_level(state, 33, player),
|
||||
"Evolution - Starmie": lambda state: state.has("Staryu", player) and state.has("Water Stone", player),
|
||||
"Evolution - Gyarados": lambda state: state.has("Magikarp", player) and logic.evolve_level(state, 33, player),
|
||||
"Evolution - Vaporeon": lambda state: state.has("Eevee", player) and state.has("Water Stone", player),
|
||||
"Evolution - Jolteon": lambda state: state.has("Eevee", player) and state.has("Thunder Stone", player),
|
||||
"Evolution - Flareon": lambda state: state.has("Eevee", player) and state.has("Fire Stone", player),
|
||||
"Evolution - Omastar": lambda state: state.has("Omanyte", player) and logic.evolve_level(state, 40, player),
|
||||
"Evolution - Kabutops": lambda state: state.has("Kabuto", player) and logic.evolve_level(state, 40, player),
|
||||
"Evolution - Dragonair": lambda state: state.has("Dratini", player) and logic.evolve_level(state, 30, player),
|
||||
"Evolution - Dragonite": lambda state: state.has("Dragonair", player) and logic.evolve_level(state, 55, player),
|
||||
}
|
||||
for loc in world.get_locations(player):
|
||||
for loc in multiworld.get_locations(player):
|
||||
if loc.name in access_rules:
|
||||
add_rule(loc, access_rules[loc.name])
|
||||
if loc.name in item_rules:
|
||||
|
@ -233,4 +283,3 @@ def set_rules(world, player):
|
|||
add_rule(loc, lambda state, i=mon: (state.has("Pokedex", player) or not
|
||||
state.multiworld.require_pokedex[player]) and (state.has(i, player)
|
||||
or state.has(f"Static {i}", player)))
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
special_chars = {
|
||||
"PKMN": 0x4A,
|
||||
"LINE": 0x4F,
|
||||
"PARA": 0x51,
|
||||
"CONT": 0x55,
|
||||
"DONE": 0x57,
|
||||
"PROMPT": 0x58,
|
||||
|
@ -90,10 +91,13 @@ char_map = {
|
|||
"?": 0xE6,
|
||||
"!": 0xE7,
|
||||
".": 0xE8,
|
||||
"+": 0xEA,
|
||||
"=": 0xEB,
|
||||
"♂": 0xEF,
|
||||
"¥": 0xF0,
|
||||
"$": 0xF0,
|
||||
"×": 0xF1,
|
||||
"*": 0xF1, # alias
|
||||
"/": 0xF3,
|
||||
",": 0xF4,
|
||||
"♀": 0xF5,
|
||||
|
|
Loading…
Reference in New Issue