| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | import os | 
					
						
							|  |  |  | import hashlib | 
					
						
							|  |  |  | import Utils | 
					
						
							|  |  |  | import bsdiff4 | 
					
						
							|  |  |  | from copy import deepcopy | 
					
						
							|  |  |  | from Patch import APDeltaPatch | 
					
						
							|  |  |  | from .text import encode_text | 
					
						
							|  |  |  | from .rom_addresses import rom_addresses | 
					
						
							|  |  |  | from .locations import location_data | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | from .items import item_table | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 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(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 poke_data.moves[move]["accuracy"] > 80 and poke_data.moves[move]["power"] > 0 or not starting_move: | 
					
						
							|  |  |  |             moves.remove(move) | 
					
						
							|  |  |  |             return move | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return get_move(moves, [], random, starting_move) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_encounter_slots(self): | 
					
						
							| 
									
										
										
										
											2022-12-08 04:51:01 -05:00
										 |  |  |     encounter_slots = deepcopy([location for location in location_data if location.type == "Wild Encounter"]) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for location in encounter_slots: | 
					
						
							|  |  |  |         if isinstance(location.original_item, list): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location.original_item = location.original_item[not self.multiworld.game_version[self.player].value] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     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"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  | def randomize_pokemon(self, mon, mons_list, randomize_type, random): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     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)) | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |         mon = type_mons[round(random.triangular(0, len(type_mons) - 1, 0))] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     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)) | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |         mon = mons_list[round(random.triangular(0, 50, 0))] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     elif randomize_type == 4: | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |         mon = random.choice(mons_list) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     return mon | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  | def process_trainer_data(self, data, random): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                  or self.multiworld.trainer_legendaries[self.player].value] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     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: | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                         mon = " ".join(self.multiworld.get_location(f"Pallet Town - Starter {i}", | 
					
						
							|  |  |  |                                                                     self.player).item.name.split()[1:]) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                         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] | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             if mon is None and self.multiworld.randomize_trainer_parties[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 mon = poke_data.id_to_mon[data[address]] | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                 mon = randomize_pokemon(self, mon, mons_list, | 
					
						
							|  |  |  |                                         self.multiworld.randomize_trainer_parties[self.player].value, random) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             if mon is not None: | 
					
						
							|  |  |  |                 data[address] = poke_data.pokemon_data[mon]["id"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def process_static_pokemon(self): | 
					
						
							| 
									
										
										
										
											2022-12-08 04:51:01 -05:00
										 |  |  |     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]) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |     tower_6F_mons = set() | 
					
						
							|  |  |  |     for i in range(1, 11): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         tower_6F_mons.add(self.multiworld.get_location(f"Pokemon Tower 6F - Wild Pokemon - {i}", self.player).item.name) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                  or self.multiworld.randomize_legendary_pokemon[self.player].value == 3] | 
					
						
							|  |  |  |     if self.multiworld.randomize_legendary_pokemon[self.player].value == 0: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         for slot in legendary_slots: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             location.place_locked_item(self.create_item("Missable " + slot.original_item)) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     elif self.multiworld.randomize_legendary_pokemon[self.player].value == 1: | 
					
						
							|  |  |  |         self.multiworld.random.shuffle(legendary_mons) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         for slot in legendary_slots: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             location.place_locked_item(self.create_item("Missable " + legendary_mons.pop())) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     elif self.multiworld.randomize_legendary_pokemon[self.player].value == 2: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         static_slots = static_slots + legendary_slots | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.random.shuffle(static_slots) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |         static_slots.sort(key=lambda s: 0 if s.name == "Pokemon Tower 6F - Restless Soul" else 1) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         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" | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             location.place_locked_item(self.create_item(slot_type + " " + swap_slot.original_item)) | 
					
						
							|  |  |  |             swap_slot.original_item = slot.original_item | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     elif self.multiworld.randomize_legendary_pokemon[self.player].value == 3: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         static_slots = static_slots + legendary_slots | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for slot in static_slots: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							|  |  |  |         randomize_type = self.multiworld.randomize_static_pokemon[self.player].value | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         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: | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |             mon = self.create_item(slot_type + " " + | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                                        randomize_pokemon(self, slot.original_item, mons_list, randomize_type, | 
					
						
							|  |  |  |                                                          self.multiworld.random)) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |             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, | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                                                                            randomize_type, self.multiworld.random)) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |             location.place_locked_item(mon) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for slot in starter_slots: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							|  |  |  |         randomize_type = self.multiworld.randomize_starter_pokemon[self.player].value | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         slot_type = "Missable" | 
					
						
							|  |  |  |         if not randomize_type: | 
					
						
							|  |  |  |             location.place_locked_item(self.create_item(slot_type + " " + slot.original_item)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             location.place_locked_item(self.create_item(slot_type + " " + | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                                        randomize_pokemon(self, slot.original_item, mons_list, randomize_type, | 
					
						
							|  |  |  |                                                          self.multiworld.random))) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | def process_wild_pokemon(self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     encounter_slots = get_encounter_slots(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()} | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.randomize_wild_pokemon[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                      or self.multiworld.randomize_legendary_pokemon[self.player].value == 3] | 
					
						
							|  |  |  |         self.multiworld.random.shuffle(encounter_slots) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         locations = [] | 
					
						
							|  |  |  |         for slot in encounter_slots: | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |             mon = randomize_pokemon(self, slot.original_item, mons_list, | 
					
						
							|  |  |  |                                     self.multiworld.randomize_wild_pokemon[self.player].value, self.multiworld.random) | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |             # 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 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             while (self.multiworld.randomize_static_pokemon[self.player].value == 0 and mon == "Marowak" | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |                    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 | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |                 mon = randomize_pokemon(self, slot.original_item, mons_list, 2, self.multiworld.random) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             placed_mons[mon] += 1 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             location.item = self.create_item(mon) | 
					
						
							|  |  |  |             location.event = True | 
					
						
							|  |  |  |             location.locked = True | 
					
						
							|  |  |  |             location.item.location = location | 
					
						
							|  |  |  |             locations.append(location) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mons_to_add = [] | 
					
						
							|  |  |  |         remaining_pokemon = [pokemon for pokemon in poke_data.pokemon_data.keys() if placed_mons[pokemon] == 0 and | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                              (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: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             mons_to_add = [pokemon for pokemon in poke_data.first_stage_pokemon if placed_mons[pokemon] == 0 and | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                             (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: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             mons_to_add = remaining_pokemon.copy() | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         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": | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             logic_needed_mons = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.random.shuffle(remaining_pokemon) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             if self.multiworld.randomize_wild_pokemon[self.player].value in [1, 3]: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 candidate_locations = [slot for slot in candidate_locations if 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"]]])] | 
					
						
							|  |  |  |             if not candidate_locations: | 
					
						
							| 
									
										
										
										
											2022-12-08 04:51:01 -05:00
										 |  |  |                 candidate_locations = get_encounter_slots(self) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base)) | 
					
						
							|  |  |  |             for location in candidate_locations: | 
					
						
							|  |  |  |                 if placed_mons[location.item.name] > 1 or location.item.name not in poke_data.first_stage_pokemon: | 
					
						
							|  |  |  |                     placed_mons[location.item.name] -= 1 | 
					
						
							|  |  |  |                     location.item = self.create_item(mon) | 
					
						
							|  |  |  |                     location.item.location = location | 
					
						
							|  |  |  |                     placed_mons[mon] += 1 | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for slot in encounter_slots: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location = self.multiworld.get_location(slot.name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             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_pokemon_data(self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     local_poke_data = deepcopy(poke_data.pokemon_data) | 
					
						
							|  |  |  |     learnsets = deepcopy(poke_data.learnsets) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for mon, mon_data in local_poke_data.items(): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         if self.multiworld.randomize_pokemon_stats[self.player].value == 1: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]] | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             self.multiworld.random.shuffle(stats) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             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] | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         elif self.multiworld.randomize_pokemon_stats[self.player].value == 2: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             old_stats = mon_data["hp"] + mon_data["atk"] + mon_data["def"] + mon_data["spd"] + mon_data["spc"] - 5 | 
					
						
							|  |  |  |             stats = [1, 1, 1, 1, 1] | 
					
						
							|  |  |  |             while old_stats > 0: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 stat = self.multiworld.random.randint(0, 4) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 if stats[stat] < 255: | 
					
						
							|  |  |  |                     old_stats -= 1 | 
					
						
							|  |  |  |                     stats[stat] += 1 | 
					
						
							|  |  |  |             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] | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         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: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 type1 = local_poke_data[poke_data.evolves_from[mon]]["type1"] | 
					
						
							|  |  |  |                 type2 = local_poke_data[poke_data.evolves_from[mon]]["type2"] | 
					
						
							|  |  |  |                 if type1 == type2: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                     if self.multiworld.secondary_type_chance[self.player].value == -1: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                         if mon_data["type1"] != mon_data["type2"]: | 
					
						
							|  |  |  |                             while type2 == type1: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                                 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())) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 type1 = self.multiworld.random.choice(list(poke_data.type_names.values())) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 type2 = type1 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 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): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                     while type2 == type1: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                         type2 = self.multiworld.random.choice(list(poke_data.type_names.values())) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             mon_data["type1"] = type1 | 
					
						
							|  |  |  |             mon_data["type2"] = type2 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         if self.multiworld.randomize_pokemon_movesets[self.player].value: | 
					
						
							|  |  |  |             if self.multiworld.randomize_pokemon_movesets[self.player].value == 1: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 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 = [] | 
					
						
							| 
									
										
										
										
											2022-10-13 13:55:21 -04:00
										 |  |  |             moves = list(poke_data.moves.keys()) | 
					
						
							|  |  |  |             for move in ["No Move"] + poke_data.hm_moves: | 
					
						
							|  |  |  |                 moves.remove(move) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             mon_data["start move 1"] = get_move(moves, chances, self.multiworld.random, True) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             for i in range(2, 5): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 if mon_data[f"start move {i}"] != "No Move" or self.multiworld.start_with_four_moves[ | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                         self.player].value == 1: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                     mon_data[f"start move {i}"] = get_move(moves, chances, self.multiworld.random) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             if mon in learnsets: | 
					
						
							|  |  |  |                 for move_num in range(0, len(learnsets[mon])): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                     learnsets[mon][move_num] = get_move(moves, chances, self.multiworld.random) | 
					
						
							|  |  |  |         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) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             mon_data["catch rate"] = max(self.multiworld.minimum_catch_rate[self.player], mon_data["catch rate"]) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-16 13:59:59 -05:00
										 |  |  |         if mon != "Mew": | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |             tms_hms = poke_data.tm_moves + poke_data.hm_moves | 
					
						
							|  |  |  |             for flag, tm_move in enumerate(tms_hms): | 
					
						
							| 
									
										
										
										
											2023-01-16 13:59:59 -05:00
										 |  |  |                 if ((mon in poke_data.evolves_from.keys() and mon_data["type1"] == | 
					
						
							|  |  |  |                      local_poke_data[poke_data.evolves_from[mon]]["type1"] and mon_data["type2"] == | 
					
						
							|  |  |  |                      local_poke_data[poke_data.evolves_from[mon]]["type2"]) and ( | 
					
						
							| 
									
										
										
										
											2023-01-16 14:02:37 -05:00
										 |  |  |                         (flag < 50 and self.multiworld.tm_compatibility[self.player].value in [1, 2]) or ( | 
					
						
							|  |  |  |                         flag >= 51 and self.multiworld.hm_compatibility[self.player].value in [1, 2]))): | 
					
						
							| 
									
										
										
										
											2023-01-16 13:59:59 -05:00
										 |  |  |                     bit = 1 if local_poke_data[poke_data.evolves_from[mon]]["tms"][int(flag / 8)] & 1 << (flag % 8) else 0 | 
					
						
							|  |  |  |                 elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 1) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 1): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                     type_match = poke_data.moves[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                     bit = int(self.multiworld.random.randint(1, 100) < [[90, 50, 25], [100, 75, 25]][flag >= 50][0 if type_match else 1 if poke_data.moves[tm_move]["type"] == "Normal" else 2]) | 
					
						
							|  |  |  |                 elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 2): | 
					
						
							| 
									
										
										
										
											2023-01-16 13:59:59 -05:00
										 |  |  |                     bit = self.multiworld.random.randint(0, 1) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 elif (flag < 50 and self.multiworld.tm_compatibility[self.player].value == 3) or (flag >= 50 and self.multiworld.hm_compatibility[self.player].value == 3): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                     bit = 1 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if bit: | 
					
						
							|  |  |  |                     mon_data["tms"][int(flag / 8)] |= 1 << (flag % 8) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     mon_data["tms"][int(flag / 8)] &= ~(1 << (flag % 8)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     self.local_poke_data = local_poke_data | 
					
						
							|  |  |  |     self.learnsets = learnsets | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_output(self, output_directory: str): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     random = self.multiworld.slot_seeds[self.player] | 
					
						
							|  |  |  |     game_version = self.multiworld.game_version[self.player].current_key | 
					
						
							| 
									
										
										
										
											2022-11-06 03:07:41 -05:00
										 |  |  |     data = bytes(get_base_rom_bytes(game_version)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with open(os.path.join(os.path.dirname(__file__), f'basepatch_{game_version}.bsdiff4'), 'rb') as stream: | 
					
						
							|  |  |  |         base_patch = bytes(stream.read()) | 
					
						
							|  |  |  |     data = bytearray(bsdiff4.patch(data, base_patch)) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     basemd5 = hashlib.md5() | 
					
						
							|  |  |  |     basemd5.update(data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     for location in self.multiworld.get_locations(): | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         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: | 
					
						
							|  |  |  |                         data[address] = self.item_name_to_id[location.item.name] - 172000000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             data[location.rom_address] = 0x2C  # AP Item | 
					
						
							|  |  |  |     data[rom_addresses['Fly_Location']] = self.fly_map_code | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.tea[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     data[rom_addresses["Fossils_Needed_For_Second_Item"]] = ( | 
					
						
							|  |  |  |         self.multiworld.second_fossil_check_condition[self.player].value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-22 10:14:33 -05:00
										 |  |  |     data[rom_addresses["Option_Lose_Money"]] = int(not self.multiworld.lose_money_on_blackout[self.player].value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.extra_key_items[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         data[rom_addresses['Options']] |= 4 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     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_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_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: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         data[rom_addresses['Option_Itemfinder']] = 0 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.extra_strength_boulders[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         for i in range(0, 3): | 
					
						
							|  |  |  |             data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.extra_key_items[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         for i in range(0, 4): | 
					
						
							|  |  |  |             data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.old_man[self.player].value == 2: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         data[rom_addresses['Option_Old_Man']] = 0x11 | 
					
						
							|  |  |  |         data[rom_addresses['Option_Old_Man_Lying']] = 0x15 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     money = str(self.multiworld.starting_money[self.player].value).zfill(6) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16) | 
					
						
							|  |  |  |     data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16) | 
					
						
							|  |  |  |     data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16) | 
					
						
							| 
									
										
										
										
											2022-11-01 02:02:15 -04:00
										 |  |  |     data[rom_addresses["Text_Badges_Needed_Viridian_Gym"]] = encode_text( | 
					
						
							|  |  |  |         str(self.multiworld.viridian_gym_condition[self.player].value))[0] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     data[rom_addresses["Text_Badges_Needed"]] = encode_text( | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         str(max(self.multiworld.victory_road_condition[self.player].value, | 
					
						
							|  |  |  |                 self.multiworld.elite_four_condition[self.player].value)))[0] | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     write_bytes(data, encode_text( | 
					
						
							|  |  |  |         " ".join(self.multiworld.get_location("Route 3 - Pokemon For Sale", self.player).item.name.upper().split()[1:])), | 
					
						
							|  |  |  |                 rom_addresses["Text_Magikarp_Salesman"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         for hm_move in poke_data.hm_moves: | 
					
						
							|  |  |  |             write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), | 
					
						
							|  |  |  |                         rom_addresses["HM_" + hm_move + "_Badge_a"]) | 
					
						
							|  |  |  |     elif self.extra_badges: | 
					
						
							|  |  |  |         written_badges = {} | 
					
						
							|  |  |  |         for hm_move, badge in self.extra_badges.items(): | 
					
						
							|  |  |  |             data[rom_addresses["HM_" + hm_move + "_Badge_b"]] = {"Boulder Badge": 0x47, "Cascade Badge": 0x4F, | 
					
						
							|  |  |  |                                                                  "Thunder Badge": 0x57, "Rainbow Badge": 0x5F, | 
					
						
							|  |  |  |                                                                  "Soul Badge": 0x67, "Marsh Badge": 0x6F, | 
					
						
							|  |  |  |                                                                  "Volcano Badge": 0x77, "Earth Badge": 0x7F}[badge] | 
					
						
							|  |  |  |             move_text = hm_move | 
					
						
							|  |  |  |             if badge not in ["Marsh Badge", "Volcano Badge", "Earth Badge"]: | 
					
						
							|  |  |  |                 move_text = ", " + move_text | 
					
						
							|  |  |  |             rom_address = rom_addresses["Badge_Text_" + badge.replace(" ", "_")] | 
					
						
							|  |  |  |             if badge in written_badges: | 
					
						
							|  |  |  |                 rom_address += len(written_badges[badge]) | 
					
						
							|  |  |  |                 move_text = ", " + move_text | 
					
						
							|  |  |  |             write_bytes(data, encode_text(move_text.upper()), rom_address) | 
					
						
							|  |  |  |             written_badges[badge] = move_text | 
					
						
							|  |  |  |         for badge in ["Marsh Badge", "Volcano Badge", "Earth Badge"]: | 
					
						
							|  |  |  |             if badge not in written_badges: | 
					
						
							|  |  |  |                 write_bytes(data, encode_text("Nothing"), rom_addresses["Badge_Text_" + badge.replace(" ", "_")]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     type_loc = rom_addresses["Type_Chart"] | 
					
						
							| 
									
										
										
										
											2022-12-11 14:51:28 -05:00
										 |  |  |     for matchup in self.type_chart: | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |         if matchup[2] != 10:  # don't needlessly divide damage by 10 and multiply by 10 | 
					
						
							|  |  |  |             data[type_loc] = poke_data.type_ids[matchup[0]] | 
					
						
							|  |  |  |             data[type_loc + 1] = poke_data.type_ids[matchup[1]] | 
					
						
							|  |  |  |             data[type_loc + 2] = matchup[2] | 
					
						
							|  |  |  |             type_loc += 3 | 
					
						
							|  |  |  |     data[type_loc] = 0xFF | 
					
						
							|  |  |  |     data[type_loc + 1] = 0xFF | 
					
						
							|  |  |  |     data[type_loc + 2] = 0xFF | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.normalize_encounter_chances[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         chances = [25, 51, 77, 103, 129, 155, 180, 205, 230, 255] | 
					
						
							|  |  |  |         for i, chance in enumerate(chances): | 
					
						
							|  |  |  |             data[rom_addresses['Encounter_Chances'] + (i * 2)] = chance | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for mon, mon_data in self.local_poke_data.items(): | 
					
						
							|  |  |  |         if mon == "Mew": | 
					
						
							|  |  |  |             address = rom_addresses["Base_Stats_Mew"] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             address = rom_addresses["Base_Stats"] + (28 * (mon_data["dex"] - 1)) | 
					
						
							|  |  |  |         data[address + 1] = self.local_poke_data[mon]["hp"] | 
					
						
							|  |  |  |         data[address + 2] = self.local_poke_data[mon]["atk"] | 
					
						
							|  |  |  |         data[address + 3] = self.local_poke_data[mon]["def"] | 
					
						
							|  |  |  |         data[address + 4] = self.local_poke_data[mon]["spd"] | 
					
						
							|  |  |  |         data[address + 5] = self.local_poke_data[mon]["spc"] | 
					
						
							|  |  |  |         data[address + 6] = poke_data.type_ids[self.local_poke_data[mon]["type1"]] | 
					
						
							|  |  |  |         data[address + 7] = poke_data.type_ids[self.local_poke_data[mon]["type2"]] | 
					
						
							|  |  |  |         data[address + 8] = self.local_poke_data[mon]["catch rate"] | 
					
						
							|  |  |  |         data[address + 15] = poke_data.moves[self.local_poke_data[mon]["start move 1"]]["id"] | 
					
						
							|  |  |  |         data[address + 16] = poke_data.moves[self.local_poke_data[mon]["start move 2"]]["id"] | 
					
						
							|  |  |  |         data[address + 17] = poke_data.moves[self.local_poke_data[mon]["start move 3"]]["id"] | 
					
						
							|  |  |  |         data[address + 18] = poke_data.moves[self.local_poke_data[mon]["start move 4"]]["id"] | 
					
						
							|  |  |  |         write_bytes(data, self.local_poke_data[mon]["tms"], address + 20) | 
					
						
							|  |  |  |         if mon in self.learnsets: | 
					
						
							|  |  |  |             address = rom_addresses["Learnset_" + mon.replace(" ", "")] | 
					
						
							|  |  |  |             for i, move in enumerate(self.learnsets[mon]): | 
					
						
							|  |  |  |                 data[(address + 1) + i * 2] = poke_data.moves[move]["id"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player].value | 
					
						
							|  |  |  |     data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player].value | 
					
						
							|  |  |  |     data[rom_addresses["Option_Aide_Rt15"]] = self.multiworld.oaks_aide_rt_15[self.player].value | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.safari_zone_normal_battles[self.player].value == 1: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.reusable_tms[self.player].value: | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         data[rom_addresses["Option_Reusable_TMs"]] = 0xC9 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     data[rom_addresses["Option_Trainersanity"]] = self.multiworld.trainersanity[self.player].value | 
					
						
							|  |  |  |     data[rom_addresses["Option_Trainersanity2"]] = self.multiworld.trainersanity[self.player].value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     data[rom_addresses["Option_Always_Half_STAB"]] = int(not self.multiworld.same_type_attack_bonus[self.player].value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if self.multiworld.better_shops[self.player].value: | 
					
						
							|  |  |  |         inventory = ["Poke Ball", "Great Ball", "Ultra Ball"] | 
					
						
							|  |  |  |         if self.multiworld.better_shops[self.player].value == 2: | 
					
						
							|  |  |  |             inventory.append("Master Ball") | 
					
						
							| 
									
										
										
										
											2023-01-02 13:21:08 -05:00
										 |  |  |         inventory += ["Potion", "Super Potion", "Hyper Potion", "Max Potion", "Full Restore", "Revive", "Antidote", | 
					
						
							|  |  |  |                       "Awakening", "Burn Heal", "Ice Heal", "Paralyze Heal", "Full Heal", "Repel", "Super Repel", | 
					
						
							|  |  |  |                       "Max Repel", "Escape Rope"] | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |         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): | 
					
						
							|  |  |  |             write_bytes(data, shop_data, rom_addresses[f"Shop{shop}"]) | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for item in reversed(self.multiworld.precollected_items[self.player]): | 
					
						
							|  |  |  |         if data[rom_addresses["Start_Inventory"] + item.code - 172000000] < 255: | 
					
						
							|  |  |  |             data[rom_addresses["Start_Inventory"] + item.code - 172000000] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 12:59:00 -05:00
										 |  |  |     process_trainer_data(self, data, random) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     mons = [mon["id"] for mon in poke_data.pokemon_data.values()] | 
					
						
							|  |  |  |     random.shuffle(mons) | 
					
						
							|  |  |  |     data[rom_addresses['Title_Mon_First']] = mons.pop() | 
					
						
							|  |  |  |     for mon in range(0, 16): | 
					
						
							|  |  |  |         data[rom_addresses['Title_Mons'] + mon] = mons.pop() | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2022-11-01 02:02:15 -04:00
										 |  |  |     write_bytes(data, encode_text(self.multiworld.seed_name[-20:], 20, True), rom_addresses['Title_Seed']) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     slot_name = self.multiworld.player_name[self.player] | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     slot_name.replace("@", " ") | 
					
						
							|  |  |  |     slot_name.replace("<", " ") | 
					
						
							|  |  |  |     slot_name.replace(">", " ") | 
					
						
							|  |  |  |     write_bytes(data, encode_text(slot_name, 16, True, True), rom_addresses['Title_Slot_Name']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     if self.trainer_name == "choose_in_game": | 
					
						
							|  |  |  |         data[rom_addresses["Skip_Player_Name"]] = 0 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         write_bytes(data, self.trainer_name, rom_addresses['Player_Name']) | 
					
						
							|  |  |  |     if self.rival_name == "choose_in_game": | 
					
						
							|  |  |  |         data[rom_addresses["Skip_Rival_Name"]] = 0 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         write_bytes(data, self.rival_name, rom_addresses['Rival_Name']) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     data[0xFF00] = 1 # client compatibility version | 
					
						
							| 
									
										
										
										
											2022-11-01 02:02:15 -04:00
										 |  |  |     write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDB) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     outfilepname = f'_P{self.player}' | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \ | 
					
						
							|  |  |  |         if self.multiworld.player_name[self.player] != 'Player%d' % self.player else '' | 
					
						
							|  |  |  |     rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb') | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     with open(rompath, 'wb') as outfile: | 
					
						
							|  |  |  |         outfile.write(data) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     if self.multiworld.game_version[self.player].current_key == "red": | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=self.player, | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                               player_name=self.multiworld.player_name[self.player], patched_path=rompath) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     else: | 
					
						
							|  |  |  |         patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=self.player, | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                                player_name=self.multiworld.player_name[self.player], patched_path=rompath) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     patch.write() | 
					
						
							|  |  |  |     os.unlink(rompath) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def write_bytes(data, byte_array, address): | 
					
						
							|  |  |  |     for byte in byte_array: | 
					
						
							|  |  |  |         data[address] = byte | 
					
						
							|  |  |  |         address += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_base_rom_bytes(game_version: str, hash: str="") -> bytes: | 
					
						
							|  |  |  |     file_name = get_base_rom_path(game_version) | 
					
						
							|  |  |  |     with open(file_name, "rb") as file: | 
					
						
							|  |  |  |         base_rom_bytes = bytes(file.read()) | 
					
						
							|  |  |  |     if hash: | 
					
						
							|  |  |  |         basemd5 = hashlib.md5() | 
					
						
							|  |  |  |         basemd5.update(base_rom_bytes) | 
					
						
							|  |  |  |         if hash != basemd5.hexdigest(): | 
					
						
							| 
									
										
										
										
											2022-12-11 14:51:28 -05:00
										 |  |  |             raise Exception(f"Supplied Base Rom does not match known MD5 for Pokémon {game_version.title()} UE " | 
					
						
							|  |  |  |                             "release. Get the correct game and version, then dump it") | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     return base_rom_bytes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_base_rom_path(game_version: str) -> str: | 
					
						
							|  |  |  |     options = Utils.get_options() | 
					
						
							|  |  |  |     file_name = options["pokemon_rb_options"][f"{game_version}_rom_file"] | 
					
						
							|  |  |  |     if not os.path.exists(file_name): | 
					
						
							|  |  |  |         file_name = Utils.local_path(file_name) | 
					
						
							|  |  |  |     return file_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BlueDeltaPatch(APDeltaPatch): | 
					
						
							|  |  |  |     patch_file_ending = ".apblue" | 
					
						
							|  |  |  |     hash = "50927e843568814f7ed45ec4f944bd8b" | 
					
						
							|  |  |  |     game_version = "blue" | 
					
						
							|  |  |  |     game = "Pokemon Red and Blue" | 
					
						
							|  |  |  |     result_file_ending = ".gb" | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def get_source_data(cls) -> bytes: | 
					
						
							|  |  |  |         return get_base_rom_bytes(cls.game_version, cls.hash) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RedDeltaPatch(APDeltaPatch): | 
					
						
							|  |  |  |     patch_file_ending = ".apred" | 
					
						
							|  |  |  |     hash = "3d45c1ee9abd5738df46d2bdda8b57dc" | 
					
						
							|  |  |  |     game_version = "red" | 
					
						
							|  |  |  |     game = "Pokemon Red and Blue" | 
					
						
							|  |  |  |     result_file_ending = ".gb" | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def get_source_data(cls) -> bytes: | 
					
						
							|  |  |  |         return get_base_rom_bytes(cls.game_version, cls.hash) |