mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
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:
@@ -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 ''
|
||||
|
||||
Reference in New Issue
Block a user