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.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.locked = True location.item.location = location placed_mons[location.item.name] += 1