diff --git a/.gitignore b/.gitignore index 8a724621..925a4bd0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ *.z64 *.n64 *.nes +*.gb +*.gbc +*.gba *.wixobj *.lck *.db3 diff --git a/Launcher.py b/Launcher.py index 9f9aaa4f..c24e0c81 100644 --- a/Launcher.py +++ b/Launcher.py @@ -145,6 +145,8 @@ components: Iterable[Component] = ( Component('OoT Adjuster', 'OoTAdjuster'), # FF1 Component('FF1 Client', 'FF1Client'), + # Pokémon + Component('Pokemon Client', 'PokemonClient', file_identifier=SuffixIdentifier('.apred', '.apblue')), # ChecksFinder Component('ChecksFinder Client', 'ChecksFinderClient'), # Starcraft 2 diff --git a/PokemonClient.py b/PokemonClient.py new file mode 100644 index 00000000..2328243d --- /dev/null +++ b/PokemonClient.py @@ -0,0 +1,319 @@ +import asyncio +import json +import time +import os +import bsdiff4 +import subprocess +import zipfile +import hashlib +from asyncio import StreamReader, StreamWriter +from typing import List + + +import Utils +from CommonClient import CommonContext, server_loop, gui_enabled, ClientCommandProcessor, logger, \ + get_base_parser + +from worlds.pokemon_rb.locations import location_data + +location_map = {"Rod": {}, "EventFlag": {}, "Missable": {}, "Hidden": {}, "list": {}} +location_bytes_bits = {} +for location in location_data: + if location.ram_address is not None: + if type(location.ram_address) == list: + location_map[type(location.ram_address).__name__][(location.ram_address[0].flag, location.ram_address[1].flag)] = location.address + location_bytes_bits[location.address] = [{'byte': location.ram_address[0].byte, 'bit': location.ram_address[0].bit}, + {'byte': location.ram_address[1].byte, 'bit': location.ram_address[1].bit}] + else: + location_map[type(location.ram_address).__name__][location.ram_address.flag] = location.address + location_bytes_bits[location.address] = {'byte': location.ram_address.byte, 'bit': location.ram_address.bit} + +SYSTEM_MESSAGE_ID = 0 + +CONNECTION_TIMING_OUT_STATUS = "Connection timing out. Please restart your emulator, then restart pkmn_rb.lua" +CONNECTION_REFUSED_STATUS = "Connection Refused. Please start your emulator and make sure pkmn_rb.lua is running" +CONNECTION_RESET_STATUS = "Connection was reset. Please restart your emulator, then restart pkmn_rb.lua" +CONNECTION_TENTATIVE_STATUS = "Initial Connection Made" +CONNECTION_CONNECTED_STATUS = "Connected" +CONNECTION_INITIAL_STATUS = "Connection has not been initiated" + +DISPLAY_MSGS = True + + +class GBCommandProcessor(ClientCommandProcessor): + def __init__(self, ctx: CommonContext): + super().__init__(ctx) + + def _cmd_gb(self): + """Check Gameboy Connection State""" + if isinstance(self.ctx, GBContext): + logger.info(f"Gameboy Status: {self.ctx.gb_status}") + + +class GBContext(CommonContext): + command_processor = GBCommandProcessor + game = 'Pokemon Red and Blue' + items_handling = 0b101 + + def __init__(self, server_address, password): + super().__init__(server_address, password) + self.gb_streams: (StreamReader, StreamWriter) = None + self.gb_sync_task = None + self.messages = {} + self.locations_array = None + self.gb_status = CONNECTION_INITIAL_STATUS + self.awaiting_rom = False + self.display_msgs = True + + async def server_auth(self, password_requested: bool = False): + if password_requested and not self.password: + await super(GBContext, self).server_auth(password_requested) + if not self.auth: + self.awaiting_rom = True + logger.info('Awaiting connection to Bizhawk to get Player information') + return + + await self.send_connect() + + def _set_message(self, msg: str, msg_id: int): + if DISPLAY_MSGS: + self.messages[(time.time(), msg_id)] = msg + + def on_package(self, cmd: str, args: dict): + if cmd == 'Connected': + self.locations_array = None + elif cmd == "RoomInfo": + self.seed_name = args['seed_name'] + elif cmd == 'Print': + msg = args['text'] + if ': !' not in msg: + self._set_message(msg, SYSTEM_MESSAGE_ID) + elif cmd == "ReceivedItems": + msg = f"Received {', '.join([self.item_names[item.item] for item in args['items']])}" + self._set_message(msg, SYSTEM_MESSAGE_ID) + + def run_gui(self): + from kvui import GameManager + + class GBManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Archipelago Pokémon Client" + + self.ui = GBManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + +def get_payload(ctx: GBContext): + current_time = time.time() + return json.dumps( + { + "items": [item.item for item in ctx.items_received], + "messages": {f'{key[0]}:{key[1]}': value for key, value in ctx.messages.items() + if key[0] > current_time - 10} + } + ) + + +async def parse_locations(data: List, ctx: GBContext): + locations = [] + flags = {"EventFlag": data[:0x140], "Missable": data[0x140:0x140 + 0x20], + "Hidden": data[0x140 + 0x20: 0x140 + 0x20 + 0x0E], "Rod": data[0x140 + 0x20 + 0x0E:]} + + # Check for clear problems + if len(flags['Rod']) > 1: + return + if flags["EventFlag"][1] + flags["EventFlag"][8] + flags["EventFlag"][9] + flags["EventFlag"][12] \ + + flags["EventFlag"][61] + flags["EventFlag"][62] + flags["EventFlag"][63] + flags["EventFlag"][64] \ + + flags["EventFlag"][65] + flags["EventFlag"][66] + flags["EventFlag"][67] + flags["EventFlag"][68] \ + + flags["EventFlag"][69] + flags["EventFlag"][70] != 0: + return + + for flag_type, loc_map in location_map.items(): + for flag, loc_id in loc_map.items(): + if flag_type == "list": + if (flags["EventFlag"][location_bytes_bits[loc_id][0]['byte']] & 1 << location_bytes_bits[loc_id][0]['bit'] + and flags["Missable"][location_bytes_bits[loc_id][1]['byte']] & 1 << location_bytes_bits[loc_id][1]['bit']): + locations.append(loc_id) + elif flags[flag_type][location_bytes_bits[loc_id]['byte']] & 1 << location_bytes_bits[loc_id]['bit']: + locations.append(loc_id) + if flags["EventFlag"][280] & 1 and not ctx.finished_game: + await ctx.send_msgs([ + {"cmd": "StatusUpdate", + "status": 30} + ]) + ctx.finished_game = True + if locations == ctx.locations_array: + return + ctx.locations_array = locations + if locations is not None: + await ctx.send_msgs([{"cmd": "LocationChecks", "locations": locations}]) + + +async def gb_sync_task(ctx: GBContext): + logger.info("Starting GB connector. Use /gb for status information") + while not ctx.exit_event.is_set(): + error_status = None + if ctx.gb_streams: + (reader, writer) = ctx.gb_streams + msg = get_payload(ctx).encode() + writer.write(msg) + writer.write(b'\n') + try: + await asyncio.wait_for(writer.drain(), timeout=1.5) + try: + # Data will return a dict with up to two fields: + # 1. A keepalive response of the Players Name (always) + # 2. An array representing the memory values of the locations area (if in game) + data = await asyncio.wait_for(reader.readline(), timeout=5) + data_decoded = json.loads(data.decode()) + #print(data_decoded) + + if ctx.seed_name and ctx.seed_name != bytes(data_decoded['seedName']).decode(): + msg = "The server is running a different multiworld than your client is. (invalid seed_name)" + logger.info(msg, extra={'compact_gui': True}) + ctx.gui_error('Error', msg) + error_status = CONNECTION_RESET_STATUS + ctx.seed_name = bytes(data_decoded['seedName']).decode() + if not ctx.auth: + ctx.auth = ''.join([chr(i) for i in data_decoded['playerName'] if i != 0]) + if ctx.auth == '': + logger.info("Invalid ROM detected. No player name built into the ROM.") + if ctx.awaiting_rom: + await ctx.server_auth(False) + if 'locations' in data_decoded and ctx.game and ctx.gb_status == CONNECTION_CONNECTED_STATUS \ + and not error_status and ctx.auth: + # Not just a keep alive ping, parse + asyncio.create_task(parse_locations(data_decoded['locations'], ctx)) + except asyncio.TimeoutError: + logger.debug("Read Timed Out, Reconnecting") + error_status = CONNECTION_TIMING_OUT_STATUS + writer.close() + ctx.gb_streams = None + except ConnectionResetError as e: + logger.debug("Read failed due to Connection Lost, Reconnecting") + error_status = CONNECTION_RESET_STATUS + writer.close() + ctx.gb_streams = None + except TimeoutError: + logger.debug("Connection Timed Out, Reconnecting") + error_status = CONNECTION_TIMING_OUT_STATUS + writer.close() + ctx.gb_streams = None + except ConnectionResetError: + logger.debug("Connection Lost, Reconnecting") + error_status = CONNECTION_RESET_STATUS + writer.close() + ctx.gb_streams = None + if ctx.gb_status == CONNECTION_TENTATIVE_STATUS: + if not error_status: + logger.info("Successfully Connected to Gameboy") + ctx.gb_status = CONNECTION_CONNECTED_STATUS + else: + ctx.gb_status = f"Was tentatively connected but error occured: {error_status}" + elif error_status: + ctx.gb_status = error_status + logger.info("Lost connection to Gameboy and attempting to reconnect. Use /gb for status updates") + else: + try: + logger.debug("Attempting to connect to Gameboy") + ctx.gb_streams = await asyncio.wait_for(asyncio.open_connection("localhost", 17242), timeout=10) + ctx.gb_status = CONNECTION_TENTATIVE_STATUS + except TimeoutError: + logger.debug("Connection Timed Out, Trying Again") + ctx.gb_status = CONNECTION_TIMING_OUT_STATUS + continue + except ConnectionRefusedError: + logger.debug("Connection Refused, Trying Again") + ctx.gb_status = CONNECTION_REFUSED_STATUS + continue + + +async def run_game(romfile): + auto_start = Utils.get_options()["pokemon_rb_options"].get("rom_start", True) + if auto_start is True: + import webbrowser + webbrowser.open(romfile) + elif os.path.isfile(auto_start): + subprocess.Popen([auto_start, romfile], + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + +async def patch_and_run_game(game_version, patch_file, ctx): + base_name = os.path.splitext(patch_file)[0] + comp_path = base_name + '.gb' + with open(Utils.local_path(Utils.get_options()["pokemon_rb_options"][f"{game_version}_rom_file"]), "rb") as stream: + base_rom = bytes(stream.read()) + try: + with open(Utils.local_path('lib', 'worlds', 'pokemon_rb', f'basepatch_{game_version}.bsdiff4'), 'rb') as stream: + base_patch = bytes(stream.read()) + except FileNotFoundError: + with open(Utils.local_path('worlds', 'pokemon_rb', f'basepatch_{game_version}.bsdiff4'), 'rb') as stream: + base_patch = bytes(stream.read()) + base_patched_rom_data = bsdiff4.patch(base_rom, base_patch) + basemd5 = hashlib.md5() + basemd5.update(base_patched_rom_data) + + with zipfile.ZipFile(patch_file, 'r') as patch_archive: + with patch_archive.open('delta.bsdiff4', 'r') as stream: + patch = stream.read() + patched_rom_data = bsdiff4.patch(base_patched_rom_data, patch) + + written_hash = patched_rom_data[0xFFCC:0xFFDC] + if written_hash == basemd5.digest(): + with open(comp_path, "wb") as patched_rom_file: + patched_rom_file.write(patched_rom_data) + + asyncio.create_task(run_game(comp_path)) + else: + msg = "Patch supplied was not generated with the same base patch version as this client. Patching failed." + logger.warning(msg) + ctx.gui_error('Error', msg) + + +if __name__ == '__main__': + + Utils.init_logging("PokemonClient") + + options = Utils.get_options() + + async def main(): + parser = get_base_parser() + parser.add_argument('patch_file', default="", type=str, nargs="?", + help='Path to an APRED or APBLUE patch file') + args = parser.parse_args() + + ctx = GBContext(args.connect, args.password) + ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") + if gui_enabled: + ctx.run_gui() + ctx.run_cli() + ctx.gb_sync_task = asyncio.create_task(gb_sync_task(ctx), name="GB Sync") + + if args.patch_file: + ext = args.patch_file.split(".")[len(args.patch_file.split(".")) - 1].lower() + if ext == "apred": + logger.info("APRED file supplied, beginning patching process...") + asyncio.create_task(patch_and_run_game("red", args.patch_file, ctx)) + elif ext == "apblue": + logger.info("APBLUE file supplied, beginning patching process...") + asyncio.create_task(patch_and_run_game("blue", args.patch_file, ctx)) + else: + logger.warning(f"Unknown patch file extension {ext}") + + await ctx.exit_event.wait() + ctx.server_address = None + + await ctx.shutdown() + + if ctx.gb_sync_task: + await ctx.gb_sync_task + + + import colorama + + colorama.init() + + asyncio.run(main()) + colorama.deinit() diff --git a/README.md b/README.md index a8228203..9615b0fb 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Currently, the following games are supported: * Donkey Kong Country 3 * Dark Souls 3 * Super Mario World +* Pokémon Red and Blue For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/). Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled diff --git a/Utils.py b/Utils.py index 70741545..7df5ce59 100644 --- a/Utils.py +++ b/Utils.py @@ -295,6 +295,11 @@ def get_default_options() -> OptionsType: "sni": "SNI", "rom_start": True, }, + "pokemon_rb_options": { + "red_rom_file": "Pokemon Red (UE) [S][!].gb", + "blue_rom_file": "Pokemon Blue (UE) [S][!].gb", + "rom_start": True + } } return options diff --git a/data/lua/PKMN_RB/core.dll b/data/lua/PKMN_RB/core.dll new file mode 100644 index 00000000..3e956957 Binary files /dev/null and b/data/lua/PKMN_RB/core.dll differ diff --git a/data/lua/PKMN_RB/json.lua b/data/lua/PKMN_RB/json.lua new file mode 100644 index 00000000..a1f6e4ed --- /dev/null +++ b/data/lua/PKMN_RB/json.lua @@ -0,0 +1,389 @@ +-- +-- json.lua +-- +-- Copyright (c) 2015 rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- + +local json = { _version = "0.1.0" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +function error(err) + print(err) +end + +local escape_char_map = { + [ "\\" ] = "\\\\", + [ "\"" ] = "\\\"", + [ "\b" ] = "\\b", + [ "\f" ] = "\\f", + [ "\n" ] = "\\n", + [ "\r" ] = "\\r", + [ "\t" ] = "\\t", +} + +local escape_char_map_inv = { [ "\\/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return escape_char_map[c] or string.format("\\u%04x", c:byte()) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if val[1] ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + print("invalid table: sparse array") + print(n) + print("VAL:") + print(val) + print("STACK:") + print(stack) + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + --local line_count = 1 + --local col_count = 1 + --for i = 1, idx - 1 do + -- col_count = col_count + 1 + -- if str:sub(i, i) == "\n" then + -- line_count = line_count + 1 + -- col_count = 1 + -- end + -- end + -- emu.message( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(3, 6), 16 ) + local n2 = tonumber( s:sub(9, 12), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local has_unicode_escape = false + local has_surrogate_escape = false + local has_escape = false + local last + for j = i + 1, #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + end + + if last == 92 then -- "\\" (escape char) + if x == 117 then -- "u" (unicode escape sequence) + local hex = str:sub(j + 1, j + 5) + if not hex:find("%x%x%x%x") then + decode_error(str, j, "invalid unicode escape in string") + end + if hex:find("^[dD][89aAbB]") then + has_surrogate_escape = true + else + has_unicode_escape = true + end + else + local c = string.char(x) + if not escape_chars[c] then + decode_error(str, j, "invalid escape char '" .. c .. "' in string") + end + has_escape = true + end + last = nil + + elseif x == 34 then -- '"' (end of string) + local s = str:sub(i + 1, j - 1) + if has_surrogate_escape then + s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape) + end + if has_unicode_escape then + s = s:gsub("\\u....", parse_unicode_escape) + end + if has_escape then + s = s:gsub("\\.", escape_char_map_inv) + end + return s, j + 1 + + else + last = x + end + end + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + return ( parse(str, next_char(str, 1, space_chars, true)) ) +end + + +return json \ No newline at end of file diff --git a/data/lua/PKMN_RB/pkmn_rb.lua b/data/lua/PKMN_RB/pkmn_rb.lua new file mode 100644 index 00000000..7518a5f1 --- /dev/null +++ b/data/lua/PKMN_RB/pkmn_rb.lua @@ -0,0 +1,238 @@ +local socket = require("socket") +local json = require('json') +local math = require('math') + +local STATE_OK = "Ok" +local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected" +local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made" +local STATE_UNINITIALIZED = "Uninitialized" + +local APIndex = 0x1A6E +local APItemAddress = 0x00FF +local EventFlagAddress = 0x1735 +local MissableAddress = 0x161A +local HiddenItemsAddress = 0x16DE +local RodAddress = 0x1716 +local InGame = 0x1A71 + +local ItemsReceived = nil +local playerName = nil +local seedName = nil + +local prevstate = "" +local curstate = STATE_UNINITIALIZED +local gbSocket = nil +local frame = 0 + +local u8 = nil +local wU8 = nil +local u16 + +--Sets correct memory access functions based on whether NesHawk or QuickNES is loaded +local function defineMemoryFunctions() + local memDomain = {} + local domains = memory.getmemorydomainlist() + --if domains[1] == "System Bus" then + -- --NesHawk + -- isNesHawk = true + -- memDomain["systembus"] = function() memory.usememorydomain("System Bus") end + -- memDomain["saveram"] = function() memory.usememorydomain("Battery RAM") end + -- memDomain["rom"] = function() memory.usememorydomain("PRG ROM") end + --elseif domains[1] == "WRAM" then + -- --QuickNES + -- memDomain["systembus"] = function() memory.usememorydomain("System Bus") end + -- memDomain["saveram"] = function() memory.usememorydomain("WRAM") end + -- memDomain["rom"] = function() memory.usememorydomain("PRG ROM") end + --end + memDomain["rom"] = function() memory.usememorydomain("ROM") end + memDomain["wram"] = function() memory.usememorydomain("WRAM") end + return memDomain +end + +local memDomain = defineMemoryFunctions() +u8 = memory.read_u8 +wU8 = memory.write_u8 +u16 = memory.read_u16_le +function uRange(address, bytes) + data = memory.readbyterange(address - 1, bytes + 1) + data[0] = nil + return data +end + + +function table.empty (self) + for _, _ in pairs(self) do + return false + end + return true +end + +function slice (tbl, s, e) + local pos, new = 1, {} + for i = s + 1, e do + new[pos] = tbl[i] + pos = pos + 1 + end + return new +end + +function processBlock(block) + if block == nil then + return + end + local itemsBlock = block["items"] + memDomain.wram() + if itemsBlock ~= nil then-- and u8(0x116B) ~= 0x00 then + -- print(itemsBlock) + ItemsReceived = itemsBlock + + end +end + +function difference(a, b) + local aa = {} + for k,v in pairs(a) do aa[v]=true end + for k,v in pairs(b) do aa[v]=nil end + local ret = {} + local n = 0 + for k,v in pairs(a) do + if aa[v] then n=n+1 ret[n]=v end + end + return ret +end + +function generateLocationsChecked() + memDomain.wram() + events = uRange(EventFlagAddress, 0x140) + missables = uRange(MissableAddress, 0x20) + hiddenitems = uRange(HiddenItemsAddress, 0x0E) + rod = u8(RodAddress) + + data = {} + + table.foreach(events, function(k, v) table.insert(data, v) end) + table.foreach(missables, function(k, v) table.insert(data, v) end) + table.foreach(hiddenitems, function(k, v) table.insert(data, v) end) + table.insert(data, rod) + + return data +end +function generateSerialData() + memDomain.wram() + status = u8(0x1A73) + if status == 0 then + return nil + end + return uRange(0x1A76, u8(0x1A74)) +end +local function arrayEqual(a1, a2) + if #a1 ~= #a2 then + return false + end + + for i, v in ipairs(a1) do + if v ~= a2[i] then + return false + end + end + + return true +end + +function receive() + l, e = gbSocket:receive() + if e == 'closed' then + if curstate == STATE_OK then + print("Connection closed") + end + curstate = STATE_UNINITIALIZED + return + elseif e == 'timeout' then + --print("timeout") -- this keeps happening for some reason? just hide it + return + elseif e ~= nil then + print(e) + curstate = STATE_UNINITIALIZED + return + end + if l ~= nil then + processBlock(json.decode(l)) + end + -- Determine Message to send back + memDomain.rom() + newPlayerName = uRange(0xFFF0, 0x10) + newSeedName = uRange(0xFFDC, 20) + if (playerName ~= nil and not arrayEqual(playerName, newPlayerName)) or (seedName ~= nil and not arrayEqual(seedName, newSeedName)) then + print("ROM changed, quitting") + curstate = STATE_UNINITIALIZED + return + end + playerName = newPlayerName + seedName = newSeedName + local retTable = {} + retTable["playerName"] = playerName + retTable["seedName"] = seedName + memDomain.wram() + if u8(InGame) == 0xAC then + retTable["locations"] = generateLocationsChecked() + serialData = generateSerialData() + if serialData ~= nil then + retTable["serial"] = serialData + end + end + msg = json.encode(retTable).."\n" + local ret, error = gbSocket:send(msg) + if ret == nil then + print(error) + elseif curstate == STATE_INITIAL_CONNECTION_MADE then + curstate = STATE_TENTATIVELY_CONNECTED + elseif curstate == STATE_TENTATIVELY_CONNECTED then + print("Connected!") + curstate = STATE_OK + end +end + +function main() + if (is23Or24Or25 or is26To28) == false then + print("Must use a version of bizhawk 2.3.1 or higher") + return + end + server, error = socket.bind('localhost', 17242) + + while true do + if not (curstate == prevstate) then + print("Current state: "..curstate) + prevstate = curstate + end + if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then + if (frame % 60 == 0) then + receive() + if u8(InGame) == 0xAC then + ItemIndex = u16(APIndex) + if ItemsReceived[ItemIndex + 1] ~= nil then + wU8(APItemAddress, ItemsReceived[ItemIndex + 1] - 172000000) + end + end + end + elseif (curstate == STATE_UNINITIALIZED) then + if (frame % 60 == 0) then + + print("Waiting for client.") + + emu.frameadvance() + server:settimeout(2) + print("Attempting to connect") + local client, timeout = server:accept() + if timeout == nil then + -- print('Initial Connection Made') + curstate = STATE_INITIAL_CONNECTION_MADE + gbSocket = client + gbSocket:settimeout(0) + end + end + end + emu.frameadvance() + end +end + +main() diff --git a/data/lua/PKMN_RB/socket.lua b/data/lua/PKMN_RB/socket.lua new file mode 100644 index 00000000..a98e9521 --- /dev/null +++ b/data/lua/PKMN_RB/socket.lua @@ -0,0 +1,132 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") +module("socket") + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then return nil, err end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then return nil, err end + end + local res, err = sock:connect(address, port) + if not res then return nil, err end + return sock +end + +function bind(host, port, backlog) + local sock, err = socket.tcp() + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + local res, err = sock:bind(host, port) + if not res then return nil, err end + res, err = sock:listen(backlog) + if not res then return nil, err end + return sock +end + +try = newtry() + +function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +sourcet = {} +sinkt = {} + +BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +sink = choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +source = choose(sourcet) diff --git a/host.yaml b/host.yaml index b1141355..f36f014a 100644 --- a/host.yaml +++ b/host.yaml @@ -138,6 +138,14 @@ dkc3_options: # True for operating system default program # Alternatively, a path to a program to open the .sfc file with rom_start: true +pokemon_rb_options: + # File names of the Pokemon Red and Blue roms + red_rom_file: "Pokemon Red (UE) [S][!].gb" + blue_rom_file: "Pokemon Blue (UE) [S][!].gb" + # Set this to false to never autostart a rom (such as after patching) + # True for operating system default program + # Alternatively, a path to a program to open the .gb file with + rom_start: true smw_options: # File name of the SMW US rom rom_file: "Super Mario World (USA).sfc" diff --git a/inno_setup.iss b/inno_setup.iss index 9a2a4044..e097798c 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -59,6 +59,8 @@ Name: "generator/smw"; Description: "Super Mario World ROM Setup"; Types: ful Name: "generator/soe"; Description: "Secret of Evermore ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning Name: "generator/lttp"; Description: "A Link to the Past ROM Setup and Enemizer"; Types: full hosting; ExtraDiskSpaceRequired: 5191680 Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 100663296; Flags: disablenouninstallwarning +Name: "generator/pkmn_r"; Description: "Pokemon Red ROM Setup"; Types: full hosting +Name: "generator/pkmn_b"; Description: "Pokemon Blue ROM Setup"; Types: full hosting Name: "server"; Description: "Server"; Types: full hosting Name: "client"; Description: "Clients"; Types: full playing Name: "client/sni"; Description: "SNI Client"; Types: full playing @@ -70,6 +72,9 @@ Name: "client/factorio"; Description: "Factorio"; Types: full playing Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278 Name: "client/oot"; Description: "Ocarina of Time"; Types: full playing Name: "client/ff1"; Description: "Final Fantasy 1"; Types: full playing +Name: "client/pkmn"; Description: "Pokemon Client" +Name: "client/pkmn/red"; Description: "Pokemon Client - Pokemon Red Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 +Name: "client/pkmn/blue"; Description: "Pokemon Client - Pokemon Blue Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 Name: "client/cf"; Description: "ChecksFinder"; Types: full playing Name: "client/sc2"; Description: "Starcraft 2"; Types: full playing Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing @@ -84,6 +89,8 @@ Source: "{code:GetDKC3ROMPath}"; DestDir: "{app}"; DestName: "Donkey Kong Countr Source: "{code:GetSMWROMPath}"; DestDir: "{app}"; DestName: "Super Mario World (USA).sfc"; Flags: external; Components: client/sni/smw or generator/smw Source: "{code:GetSoEROMPath}"; DestDir: "{app}"; DestName: "Secret of Evermore (USA).sfc"; Flags: external; Components: generator/soe Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: client/oot or generator/oot +Source: "{code:GetRedROMPath}"; DestDir: "{app}"; DestName: "Pokemon Red (UE) [S][!].gb"; Flags: external; Components: client/pkmn/red or generator/pkmn_r +Source: "{code:GetBlueROMPath}"; DestDir: "{app}"; DestName: "Pokemon Blue (UE) [S][!].gb"; Flags: external; Components: client/pkmn/blue or generator/pkmn_b Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "{#source_path}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni Source: "{#source_path}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp @@ -98,6 +105,7 @@ Source: "{#source_path}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags Source: "{#source_path}\ArchipelagoOoTClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot Source: "{#source_path}\ArchipelagoOoTAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot Source: "{#source_path}\ArchipelagoFF1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ff1 +Source: "{#source_path}\ArchipelagoPokemonClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/pkmn Source: "{#source_path}\ArchipelagoChecksFinderClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/cf Source: "{#source_path}\ArchipelagoStarcraft2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sc2 Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall @@ -111,6 +119,7 @@ Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactor Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft Name: "{group}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Components: client/oot Name: "{group}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Components: client/ff1 +Name: "{group}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Components: client/pkmn Name: "{group}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Components: client/cf Name: "{group}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Components: client/sc2 @@ -121,6 +130,7 @@ Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\Archipela Name: "{commondesktop}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Tasks: desktopicon; Components: client/minecraft Name: "{commondesktop}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Tasks: desktopicon; Components: client/oot Name: "{commondesktop}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Tasks: desktopicon; Components: client/ff1 +Name: "{commondesktop}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Tasks: desktopicon; Components: client/pkmn Name: "{commondesktop}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Tasks: desktopicon; Components: client/cf Name: "{commondesktop}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Tasks: desktopicon; Components: client/sc2 @@ -179,6 +189,16 @@ Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archip Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: ""; Components: client/oot Root: HKCR; Subkey: "{#MyAppName}n64zpf\shell\open\command"; ValueData: """{app}\ArchipelagoOoTClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/oot +Root: HKCR; Subkey: ".apred"; ValueData: "{#MyAppName}pkmnrpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn/red +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch"; ValueData: "Archipelago Pokemon Red Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn/red +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn/red +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn/red + +Root: HKCR; Subkey: ".apblue"; ValueData: "{#MyAppName}pkmnbpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn/blue +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Archipelago Pokemon Blue Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn/blue +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn/blue +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn/blue + Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: server Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: server Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: ""; Components: server @@ -234,6 +254,12 @@ var SoERomFilePage: TInputFileWizardPage; var ootrom: string; var OoTROMFilePage: TInputFileWizardPage; +var redrom: string; +var RedROMFilePage: TInputFileWizardPage; + +var bluerom: string; +var BlueROMFilePage: TInputFileWizardPage; + function GetSNESMD5OfFile(const rom: string): string; var data: AnsiString; begin @@ -281,6 +307,21 @@ begin '.sfc'); end; +function AddGBRomPage(name: string): TInputFileWizardPage; +begin + Result := + CreateInputFilePage( + wpSelectComponents, + 'Select ROM File', + 'Where is your ' + name + ' located?', + 'Select the file, then click Next.'); + + Result.Add( + 'Location of ROM file:', + 'GB ROM files|*.gb;*.gbc|All files|*.*', + '.gb'); +end; + procedure AddOoTRomPage(); begin ootrom := FileSearch('The Legend of Zelda - Ocarina of Time.z64', WizardDirValue()); @@ -425,6 +466,38 @@ begin Result := ''; end; +function GetRedROMPath(Param: string): string; +begin + if Length(redrom) > 0 then + Result := redrom + else if Assigned(RedRomFilePage) then + begin + R := CompareStr(GetMD5OfFile(RedROMFilePage.Values[0]), '3d45c1ee9abd5738df46d2bdda8b57dc') + if R <> 0 then + MsgBox('Pokemon Red ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); + + Result := RedROMFilePage.Values[0] + end + else + Result := ''; + end; + +function GetBlueROMPath(Param: string): string; +begin + if Length(bluerom) > 0 then + Result := bluerom + else if Assigned(BlueRomFilePage) then + begin + R := CompareStr(GetMD5OfFile(BlueROMFilePage.Values[0]), '50927e843568814f7ed45ec4f944bd8b') + if R <> 0 then + MsgBox('Pokemon Blue ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); + + Result := BlueROMFilePage.Values[0] + end + else + Result := ''; + end; + procedure InitializeWizard(); begin AddOoTRomPage(); @@ -448,6 +521,14 @@ begin soerom := CheckRom('Secret of Evermore (USA).sfc', '6e9c94511d04fac6e0a1e582c170be3a'); if Length(soerom) = 0 then SoEROMFilePage:= AddRomPage('Secret of Evermore (USA).sfc'); + + redrom := CheckRom('Pokemon Red (UE) [S][!].gb','3d45c1ee9abd5738df46d2bdda8b57dc'); + if Length(redrom) = 0 then + RedROMFilePage:= AddGBRomPage('Pokemon Red (UE) [S][!].gb'); + + bluerom := CheckRom('Pokemon Blue (UE) [S][!].gb','50927e843568814f7ed45ec4f944bd8b'); + if Length(redrom) = 0 then + BlueROMFilePage:= AddGBRomPage('Pokemon Blue (UE) [S][!].gb'); end; @@ -466,4 +547,8 @@ begin Result := not (WizardIsComponentSelected('generator/soe')); if (assigned(OoTROMFilePage)) and (PageID = OoTROMFilePage.ID) then Result := not (WizardIsComponentSelected('generator/oot') or WizardIsComponentSelected('client/oot')); -end; \ No newline at end of file + if (assigned(RedROMFilePage)) and (PageID = RedROMFilePage.ID) then + Result := not (WizardIsComponentSelected('generator/pkmn_r') or WizardIsComponentSelected('client/pkmn/red')); + if (assigned(BlueROMFilePage)) and (PageID = BlueROMFilePage.ID) then + Result := not (WizardIsComponentSelected('generator/pkmn_b') or WizardIsComponentSelected('client/pkmn/blue')); +end; diff --git a/worlds/Files.py b/worlds/Files.py index 6f81a0e2..ac1acbf3 100644 --- a/worlds/Files.py +++ b/worlds/Files.py @@ -99,7 +99,7 @@ class APContainer: "player_name": self.player_name, "game": self.game, # minimum version of patch system expected for patching to be successful - "compatible_version": 4, + "compatible_version": 5, "version": current_patch_version, } diff --git a/worlds/pokemon_rb/LICENSE b/worlds/pokemon_rb/LICENSE new file mode 100644 index 00000000..0dc1b2dc --- /dev/null +++ b/worlds/pokemon_rb/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alex "Alchav" Avery + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py new file mode 100644 index 00000000..d4ca79ff --- /dev/null +++ b/worlds/pokemon_rb/__init__.py @@ -0,0 +1,254 @@ +from typing import TextIO +import os +import logging + +from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification +from Fill import fill_restrictive, FillError, sweep_from_pool +from ..AutoWorld import World, WebWorld +from ..generic.Rules import add_item_rule +from .items import item_table, item_groups +from .locations import location_data, PokemonRBLocation +from .regions import create_regions +from .logic import PokemonLogic +from .options import pokemon_rb_options +from .rom_addresses import rom_addresses +from .text import encode_text +from .rom import generate_output, get_base_rom_bytes, get_base_rom_path, process_pokemon_data, process_wild_pokemon,\ + process_static_pokemon +from .rules import set_rules + +import worlds.pokemon_rb.poke_data as poke_data + + +class PokemonWebWorld(WebWorld): + tutorials = [Tutorial( + "Multiworld Setup Guide", + "A guide to playing Pokemon Red and Blue with Archipelago.", + "English", + "setup_en.md", + "setup/en", + ["Alchav"] + )] + + +class PokemonRedBlueWorld(World): + """Pokémon Red and Pokémon Blue are the original monster-collecting turn-based RPGs. Explore the Kanto region with + your Pokémon, catch more than 150 unique creatures, earn badges from the region's Gym Leaders, and challenge the + Elite Four to become the champion!""" + # -MuffinJets#4559 + game = "Pokemon Red and Blue" + option_definitions = pokemon_rb_options + remote_items = False + data_version = 1 + topology_present = False + + item_name_to_id = {name: data.id for name, data in item_table.items()} + location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"} + item_name_groups = item_groups + + web = PokemonWebWorld() + + def __init__(self, world: MultiWorld, player: int): + super().__init__(world, player) + self.fly_map = None + self.fly_map_code = None + self.extra_badges = {} + self.type_chart = None + self.local_poke_data = None + self.learnsets = None + self.trainer_name = None + self.rival_name = None + + @classmethod + def stage_assert_generate(cls, world): + versions = set() + for player in world.player_ids: + if world.worlds[player].game == "Pokemon Red and Blue": + versions.add(world.game_version[player].current_key) + for version in versions: + if not os.path.exists(get_base_rom_path(version)): + raise FileNotFoundError(get_base_rom_path(version)) + + def generate_early(self): + def encode_name(name, t): + try: + if len(encode_text(name)) > 7: + raise IndexError(f"{t} name too long for player {self.world.player_name[self.player]}. Must be 7 characters or fewer.") + return encode_text(name, length=8, whitespace="@", safety=True) + except KeyError as e: + raise KeyError(f"Invalid character(s) in {t} name for player {self.world.player_name[self.player]}") from e + self.trainer_name = encode_name(self.world.trainer_name[self.player].value, "Player") + self.rival_name = encode_name(self.world.rival_name[self.player].value, "Rival") + + if self.world.badges_needed_for_hm_moves[self.player].value >= 2: + badges_to_add = ["Marsh Badge", "Volcano Badge", "Earth Badge"] + if self.world.badges_needed_for_hm_moves[self.player].value == 3: + badges = ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge", + "Soul Badge", "Volcano Badge", "Earth Badge"] + self.world.random.shuffle(badges) + badges_to_add += [badges.pop(), badges.pop()] + hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"] + self.world.random.shuffle(hm_moves) + self.extra_badges = {} + for badge in badges_to_add: + self.extra_badges[hm_moves.pop()] = badge + + process_pokemon_data(self) + + def create_items(self) -> None: + locations = [location for location in location_data if location.type == "Item"] + item_pool = [] + for location in locations: + if "Hidden" in location.name and not self.world.randomize_hidden_items[self.player].value: + continue + if "Rock Tunnel B1F" in location.region and not self.world.extra_key_items[self.player].value: + continue + if location.name == "Celadon City - Mansion Lady" and not self.world.tea[self.player].value: + continue + item = self.create_item(location.original_item) + if location.event: + self.world.get_location(location.name, self.player).place_locked_item(item) + elif ("Badge" not in item.name or self.world.badgesanity[self.player].value) and \ + (item.name != "Oak's Parcel" or self.world.old_man[self.player].value != 1): + item_pool.append(item) + self.world.random.shuffle(item_pool) + + self.world.itempool += item_pool + + + def pre_fill(self): + + process_wild_pokemon(self) + process_static_pokemon(self) + + if self.world.old_man[self.player].value == 1: + item = self.create_item("Oak's Parcel") + locations = [] + for location in self.world.get_locations(): + if location.player == self.player and location.item is None and location.can_reach(self.world.state) \ + and location.item_rule(item): + locations.append(location) + self.world.random.choice(locations).place_locked_item(item) + + + + if not self.world.badgesanity[self.player].value: + self.world.non_local_items[self.player].value -= self.item_name_groups["Badges"] + for i in range(5): + try: + badges = [] + badgelocs = [] + for badge in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge", + "Marsh Badge", "Volcano Badge", "Earth Badge"]: + badges.append(self.create_item(badge)) + for loc in ["Pewter Gym - Brock 1", "Cerulean Gym - Misty 1", "Vermilion Gym - Lt. Surge 1", + "Celadon Gym - Erika 1", "Fuchsia Gym - Koga 1", "Saffron Gym - Sabrina 1", + "Cinnabar Gym - Blaine 1", "Viridian Gym - Giovanni 1"]: + badgelocs.append(self.world.get_location(loc, self.player)) + state = self.world.get_all_state(False) + self.world.random.shuffle(badges) + self.world.random.shuffle(badgelocs) + fill_restrictive(self.world, state, badgelocs.copy(), badges, True, True) + except FillError: + for location in badgelocs: + location.item = None + continue + break + else: + raise FillError(f"Failed to place badges for player {self.player}") + + locs = [self.world.get_location("Fossil - Choice A", self.player), + self.world.get_location("Fossil - Choice B", self.player)] + for loc in locs: + add_item_rule(loc, lambda i: i.advancement or i.name in self.item_name_groups["Unique"] + or i.name == "Master Ball") + + loc = self.world.get_location("Pallet Town - Player's PC", self.player) + if loc.item is None: + locs.append(loc) + + for loc in locs: + unplaced_items = [] + if loc.name in self.world.priority_locations[self.player].value: + add_item_rule(loc, lambda i: i.advancement) + for item in self.world.itempool: + if item.player == self.player and loc.item_rule(item): + self.world.itempool.remove(item) + state = sweep_from_pool(self.world.state, self.world.itempool + unplaced_items) + if state.can_reach(loc, "Location", self.player): + loc.place_locked_item(item) + break + else: + unplaced_items.append(item) + self.world.itempool += unplaced_items + + intervene = False + test_state = self.world.get_all_state(False) + if not test_state.pokemon_rb_can_surf(self.player) or not test_state.pokemon_rb_can_strength(self.player): + intervene = True + elif self.world.accessibility[self.player].current_key != "minimal": + if not test_state.pokemon_rb_can_cut(self.player) or not test_state.pokemon_rb_can_flash(self.player): + intervene = True + if intervene: + # the way this is handled will be improved significantly in the future when I add options to + # let you choose the exact weights for HM compatibility + logging.warning( + f"HM-compatible Pokémon possibly missing, placing Mew on Route 1 for player {self.player}") + loc = self.world.get_location("Route 1 - Wild Pokemon - 1", self.player) + loc.item = self.create_item("Mew") + + def create_regions(self): + if self.world.free_fly_location[self.player].value: + fly_map_code = self.world.random.randint(5, 9) + if fly_map_code == 9: + fly_map_code = 10 + if fly_map_code == 5: + fly_map_code = 4 + else: + fly_map_code = 0 + self.fly_map = ["Pallet Town", "Viridian City", "Pewter City", "Cerulean City", "Lavender Town", + "Vermilion City", "Celadon City", "Fuchsia City", "Cinnabar Island", "Indigo Plateau", + "Saffron City"][fly_map_code] + self.fly_map_code = fly_map_code + create_regions(self.world, self.player) + self.world.completion_condition[self.player] = lambda state, player=self.player: state.has("Become Champion", player=player) + + def set_rules(self): + set_rules(self.world, self.player) + + def create_item(self, name: str) -> Item: + return PokemonRBItem(name, self.player) + + def generate_output(self, output_directory: str): + generate_output(self, output_directory) + + def write_spoiler_header(self, spoiler_handle: TextIO): + if self.world.free_fly_location[self.player].value: + spoiler_handle.write('Fly unlocks: %s\n' % self.fly_map) + if self.extra_badges: + for hm_move, badge in self.extra_badges.items(): + spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n") + + def write_spoiler(self, spoiler_handle): + if self.world.randomize_type_matchup_types[self.player].value or \ + self.world.randomize_type_matchup_type_effectiveness[self.player].value: + spoiler_handle.write(f"\n\nType matchups ({self.world.player_name[self.player]}):\n\n") + for matchup in self.type_chart: + spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n") + + def get_filler_item_name(self) -> str: + return self.world.random.choice([item for item in item_table if item_table[item].classification in + [ItemClassification.filler, ItemClassification.trap]]) + + +class PokemonRBItem(Item): + game = "Pokemon Red and Blue" + type = None + + def __init__(self, name, player: int = None): + item_data = item_table[name] + super(PokemonRBItem, self).__init__( + name, + item_data.classification, + item_data.id, player + ) diff --git a/worlds/pokemon_rb/basepatch_blue.bsdiff4 b/worlds/pokemon_rb/basepatch_blue.bsdiff4 new file mode 100644 index 00000000..c688ebed Binary files /dev/null and b/worlds/pokemon_rb/basepatch_blue.bsdiff4 differ diff --git a/worlds/pokemon_rb/basepatch_red.bsdiff4 b/worlds/pokemon_rb/basepatch_red.bsdiff4 new file mode 100644 index 00000000..a9005d37 Binary files /dev/null and b/worlds/pokemon_rb/basepatch_red.bsdiff4 differ diff --git a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md new file mode 100644 index 00000000..fe2550d4 --- /dev/null +++ b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md @@ -0,0 +1,55 @@ +# Pokémon Red and Blue + +## Where is the settings page? + +The [player settings page for this game](../player-settings) contains all the options you need to configure and export a +config file. + +## What does randomization do to this game? + +Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game is +always able to be completed, but because of the item shuffle the player may need to access certain areas before they +would in the vanilla game. + +A great many things besides item placement can be randomized, such as the location of Pokémon, their stats, types, etc., depending on your yaml settings. + +Many baseline changes are made to the game, including: + +* Bag item space increased to 128 slots (up from 20) +* PC item storage increased to 64 slots (up from 50) +* You can hold B to run (or bike extra fast!). +* You can hold select while talking to a trainer to re-battle them. +* You can return to route 2 from Diglett's Cave without the use of Cut. +* Mew can be encountered at the S.S. Anne dock truck. This can be randomized depending on your settings. +* The S.S. Anne will never depart. +* Seafoam Islands entrances are swapped. This means you need Strength to travel through from Cinnabar Island to Fuchsia +City +* After obtaining one of the fossil item checks in Mt Moon, the remaining item can be received from the Cinnabar Lab +fossil scientist. This may require reviving a number of fossils, depending on your settings. +* Obedience depends on the total number of badges you have obtained instead of depending on specific badges. +* Pokémon that evolve by trading can also evolve by reaching level 35. +* Evolution stones are reusable. +* Much of the dialogue throughout the game has been removed or shortened. +* If the Old Man is blocking your way through Viridian City, you do not have Oak's Parcel in your inventory, and you've +exhausted your money and Poké Balls, you can get a free Poké Ball from your mom. + +## What items and locations get shuffled? + +All items that go into your bags given by NPCs or found on the ground, as well as gym badges. +Optionally, hidden items (those located with the Item Finder) can be shuffled as well. + +## Which items can be in another player's world? + +Any of the items which can be shuffled may also be placed into another player's world. +By default, gym badges are shuffled across only the 8 gyms, but you can turn on Badgesanity in your yaml to shuffle them +into the general item pool. + +## What does another world's item look like in Pokémon Red and Blue? + +All items for other games will display simply as "AP ITEM," including those for other Pokémon Red and Blue games. + +## When the player receives an item, what happens? + +A "received item" sound effect will play. Currently, there is no in-game message informing you of what the item is. +If you are in battle, have menus or text boxes opened, or scripted events are occurring, the items will not be given to +you until these have ended. diff --git a/worlds/pokemon_rb/docs/setup_en.md b/worlds/pokemon_rb/docs/setup_en.md new file mode 100644 index 00000000..58ed3b0d --- /dev/null +++ b/worlds/pokemon_rb/docs/setup_en.md @@ -0,0 +1,84 @@ +# Setup Guide for Pokémon Red and Blue: Archipelago + +## Important + +As we are using Bizhawk, this guide is only applicable to Windows and Linux systems. + +## Required Software + +- Bizhawk: [Bizhawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) + - Version 2.3.1 and later are supported. Version 2.7 is recommended for stability. + - Detailed installation instructions for Bizhawk can be found at the above link. + - Windows users must run the prereq installer first, which can also be found at the above link. +- The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases) + (select `Pokemon Client` during installation). +- Pokémon Red and/or Blue ROM files. The Archipelago community cannot provide these. + +## Configuring Bizhawk + +Once Bizhawk has been installed, open Bizhawk and change the following settings: + +- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button. + This reduces the possibility of losing save data in emulator crashes. +- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to + continue playing in the background, even if another window is selected. + +It is strongly recommended to associate GB rom extensions (\*.gb) to the Bizhawk we've just installed. +To do so, we simply have to search any Gameboy rom we happened to own, right click and select "Open with...", unfold +the list that appears and select the bottom option "Look for another application", then browse to the Bizhawk folder +and select EmuHawk.exe. + +## Configuring your YAML file + +### What is a YAML file and why do I need one? + +Your YAML file contains a set of configuration options which provide the generator with information about how it should +generate your game. Each player of a multiworld will provide their own YAML file. This setup allows each player to enjoy +an experience customized for their taste, and different players in the same multiworld can all have different options. + +### Where do I get a YAML file? + +You can generate a yaml or download a template by visiting the [Pokemon Red and Blue Player Settings Page](/games/Pokemon Red and Blue/player-settings) + +It is important to note that the `game_version` option determines the ROM file that will be patched. +Both the player and the person generating (if they are generating locally) will need the corresponding ROM file. + +For `trainer_name` and `rival_name` the following regular characters are allowed: + +* `‘’“”·… ABCDEFGHIJKLMNOPQRSTUVWXYZ():;[]abcdefghijklmnopqrstuvwxyzé'-?!.♂$×/,♀0123456789` + +And the following special characters (these each take up one character): +* `<'d>` +* `<'l>` +* `<'t>` +* `<'v>` +* `<'r>` +* `<'m>` +* `` +* `` +* `` alias for `♂` +* `` alias for `♀` + +## Joining a MultiWorld Game + +### Obtain your Pokémon patch file + +When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done, +the host will provide you with either a link to download your data file, or with a zip file containing everyone's data +files. Your data file should have a `.apred` or `.apblue` extension. + +Double-click on your patch file to start your client and start the ROM patch process. Once the process is finished +(this can take a while), the client and the emulator will be started automatically (if you associated the extension +to the emulator as recommended). + +### Connect to the Multiserver + +Once both the client and the emulator are started, you must connect them. Within the emulator click on the "Tools" +menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script. + +Navigate to your Archipelago install folder and open `data/lua/PKMN_RB/pkmr_rb.lua`. + +To connect the client to the multiserver simply put `
:` on the textfield on top and press enter (if the +server uses password, type in the bottom textfield `/connect
: [password]`) + +Now you are ready to start your adventure in Kanto. diff --git a/worlds/pokemon_rb/items.py b/worlds/pokemon_rb/items.py new file mode 100644 index 00000000..53722c02 --- /dev/null +++ b/worlds/pokemon_rb/items.py @@ -0,0 +1,176 @@ +from BaseClasses import ItemClassification +from .poke_data import pokemon_data + +class ItemData: + def __init__(self, id, classification, groups): + self.groups = groups + self.classification = classification + self.id = None if id is None else id + 172000000 + +item_table = { + "Master Ball": ItemData(1, ItemClassification.useful, ["Consumables", "Poke Balls"]), + "Ultra Ball": ItemData(2, ItemClassification.filler, ["Consumables", "Poke Balls"]), + "Great Ball": ItemData(3, ItemClassification.filler, ["Consumables", "Poke Balls"]), + "Poke Ball": ItemData(4, ItemClassification.filler, ["Consumables", "Poke Balls"]), + "Town Map": ItemData(5, ItemClassification.progression_skip_balancing, ["Unique", "Key Items"]), + "Bicycle": ItemData(6, ItemClassification.progression, ["Unique", "Key Items"]), + # "Flippers": ItemData(7, ItemClassification.progression), + #"Safari Ball": ItemData(8, ItemClassification.filler), + #"Pokedex": ItemData(9, ItemClassification.filler), + "Moon Stone": ItemData(10, ItemClassification.useful, ["Unique", "Evolution Stones"]), + "Antidote": ItemData(11, ItemClassification.filler, ["Consumables"]), + "Burn Heal": ItemData(12, ItemClassification.filler, ["Consumables"]), + "Ice Heal": ItemData(13, ItemClassification.filler, ["Consumables"]), + "Awakening": ItemData(14, ItemClassification.filler, ["Consumables"]), + "Paralyze Heal": ItemData(15, ItemClassification.filler, ["Consumables"]), + "Full Restore": ItemData(16, ItemClassification.filler, ["Consumables"]), + "Max Potion": ItemData(17, ItemClassification.filler, ["Consumables"]), + "Hyper Potion": ItemData(18, ItemClassification.filler, ["Consumables"]), + "Super Potion": ItemData(19, ItemClassification.filler, ["Consumables"]), + "Potion": ItemData(20, ItemClassification.filler, ["Consumables"]), + "Boulder Badge": ItemData(21, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Cascade Badge": ItemData(22, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Thunder Badge": ItemData(23, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Rainbow Badge": ItemData(24, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Soul Badge": ItemData(25, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Marsh Badge": ItemData(26, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Volcano Badge": ItemData(27, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Earth Badge": ItemData(28, ItemClassification.progression, ["Unique", "Key Items", "Badges"]), + "Escape Rope": ItemData(29, ItemClassification.filler, ["Consumables"]), + "Repel": ItemData(30, ItemClassification.filler, ["Consumables"]), + "Old Amber": ItemData(31, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]), + "Fire Stone": ItemData(32, ItemClassification.useful, ["Unique", "Evolution Stones"]), + "Thunder Stone": ItemData(33, ItemClassification.useful, ["Unique", "Evolution Stones"]), + "Water Stone": ItemData(34, ItemClassification.useful, ["Unique", "Evolution Stones"]), + "HP Up": ItemData(35, ItemClassification.filler, ["Consumables", "Vitamins"]), + "Protein": ItemData(36, ItemClassification.filler, ["Consumables", "Vitamins"]), + "Iron": ItemData(37, ItemClassification.filler, ["Consumables", "Vitamins"]), + "Carbos": ItemData(38, ItemClassification.filler, ["Consumables", "Vitamins"]), + "Calcium": ItemData(39, ItemClassification.filler, ["Consumables", "Vitamins"]), + "Rare Candy": ItemData(40, ItemClassification.useful, ["Consumables"]), + "Dome Fossil": ItemData(41, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]), + "Helix Fossil": ItemData(42, ItemClassification.progression_skip_balancing, ["Unique", "Fossils"]), + "Secret Key": ItemData(43, ItemClassification.progression, ["Unique", "Key Items"]), + "Bike Voucher": ItemData(45, ItemClassification.progression, ["Unique", "Key Items"]), + "X Accuracy": ItemData(46, ItemClassification.filler, ["Consumables", "Battle Items"]), + "Leaf Stone": ItemData(47, ItemClassification.useful, ["Unique", "Evolution Stones"]), + "Card Key": ItemData(48, ItemClassification.progression, ["Unique", "Key Items"]), + "Nugget": ItemData(49, ItemClassification.filler, []), + #"Laptop": ItemData(50, ItemClassification.useful, ["Unique"]), + "Poke Doll": ItemData(51, ItemClassification.filler, ["Consumables"]), + "Full Heal": ItemData(52, ItemClassification.filler, ["Consumables"]), + "Revive": ItemData(53, ItemClassification.filler, ["Consumables"]), + "Max Revive": ItemData(54, ItemClassification.filler, ["Consumables"]), + "Guard Spec": ItemData(55, ItemClassification.filler, ["Consumables", "Battle Items"]), + "Super Repel": ItemData(56, ItemClassification.filler, ["Consumables"]), + "Max Repel": ItemData(57, ItemClassification.filler, ["Consumables"]), + "Dire Hit": ItemData(58, ItemClassification.filler, ["Consumables", "Battle Items"]), + #"Coin": ItemData(59, ItemClassification.filler), + "Fresh Water": ItemData(60, ItemClassification.filler, ["Consumables"]), + "Soda Pop": ItemData(61, ItemClassification.filler, ["Consumables"]), + "Lemonade": ItemData(62, ItemClassification.filler, ["Consumables"]), + "S.S. Ticket": ItemData(63, ItemClassification.progression, ["Unique", "Key Items"]), + "Gold Teeth": ItemData(64, ItemClassification.progression, ["Unique", "Key Items"]), + "X Attack": ItemData(65, ItemClassification.filler, ["Consumables", "Battle Items"]), + "X Defend": ItemData(66, ItemClassification.filler, ["Consumables", "Battle Items"]), + "X Speed": ItemData(67, ItemClassification.filler, ["Consumables", "Battle Items"]), + "X Special": ItemData(68, ItemClassification.filler, ["Consumables", "Battle Items"]), + "Coin Case": ItemData(69, ItemClassification.progression_skip_balancing, ["Unique", "Key Items"]), + "Oak's Parcel": ItemData(70, ItemClassification.progression, ["Unique", "Key Items"]), + "Item Finder": ItemData(71, ItemClassification.progression, ["Unique", "Key Items"]), + "Silph Scope": ItemData(72, ItemClassification.progression, ["Unique", "Key Items"]), + "Poke Flute": ItemData(73, ItemClassification.progression, ["Unique", "Key Items"]), + "Lift Key": ItemData(74, ItemClassification.progression, ["Unique", "Key Items"]), + "Exp. All": ItemData(75, ItemClassification.useful, ["Unique"]), + "Old Rod": ItemData(76, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]), + "Good Rod": ItemData(77, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]), + "Super Rod": ItemData(78, ItemClassification.progression_skip_balancing, ["Unique", "Key Items", "Rods"]), + "PP Up": ItemData(79, ItemClassification.filler, ["Consumables"]), + "Ether": ItemData(80, ItemClassification.filler, ["Consumables"]), + "Max Ether": ItemData(81, ItemClassification.filler, ["Consumables"]), + "Elixir": ItemData(82, ItemClassification.filler, ["Consumables"]), + "Max Elixir": ItemData(83, ItemClassification.filler, ["Consumables"]), + "Tea": ItemData(84, ItemClassification.progression, ["Unique", "Key Items"]), + # "Master Sword": ItemData(85, ItemClassification.progression), + # "Flute": ItemData(86, ItemClassification.progression), + # "Titan's Mitt": ItemData(87, ItemClassification.progression), + # "Lamp": ItemData(88, ItemClassification.progression), + "Plant Key": ItemData(89, ItemClassification.progression, ["Unique", "Key Items"]), + "Mansion Key": ItemData(90, ItemClassification.progression, ["Unique", "Key Items"]), + "Hideout Key": ItemData(91, ItemClassification.progression, ["Unique", "Key Items"]), + "Safari Pass": ItemData(93, ItemClassification.progression, ["Unique", "Key Items"]), + "HM01 Cut": ItemData(196, ItemClassification.progression, ["Unique", "HMs"]), + "HM02 Fly": ItemData(197, ItemClassification.progression, ["Unique", "HMs"]), + "HM03 Surf": ItemData(198, ItemClassification.progression, ["Unique", "HMs"]), + "HM04 Strength": ItemData(199, ItemClassification.progression, ["Unique", "HMs"]), + "HM05 Flash": ItemData(200, ItemClassification.progression, ["Unique", "HMs"]), + "TM01 Mega Punch": ItemData(201, ItemClassification.useful, ["Unique", "TMs"]), + "TM02 Razor Wind": ItemData(202, ItemClassification.useful, ["Unique", "TMs"]), + "TM03 Swords Dance": ItemData(203, ItemClassification.useful, ["Unique", "TMs"]), + "TM04 Whirlwind": ItemData(204, ItemClassification.filler, ["Unique", "TMs"]), + "TM05 Mega Kick": ItemData(205, ItemClassification.useful, ["Unique", "TMs"]), + "TM06 Toxic": ItemData(206, ItemClassification.useful, ["Unique", "TMs"]), + "TM07 Horn Drill": ItemData(207, ItemClassification.useful, ["Unique", "TMs"]), + "TM08 Body Slam": ItemData(208, ItemClassification.useful, ["Unique", "TMs"]), + "TM09 Take Down": ItemData(209, ItemClassification.useful, ["Unique", "TMs"]), + "TM10 Double Edge": ItemData(210, ItemClassification.useful, ["Unique", "TMs"]), + "TM11 Bubble Beam": ItemData(211, ItemClassification.useful, ["Unique", "TMs"]), + "TM12 Water Gun": ItemData(212, ItemClassification.useful, ["Unique", "TMs"]), + "TM13 Ice Beam": ItemData(213, ItemClassification.useful, ["Unique", "TMs"]), + "TM14 Blizzard": ItemData(214, ItemClassification.useful, ["Unique", "TMs"]), + "TM15 Hyper Beam": ItemData(215, ItemClassification.useful, ["Unique", "TMs"]), + "TM16 Pay Day": ItemData(216, ItemClassification.useful, ["Unique", "TMs"]), + "TM17 Submission": ItemData(217, ItemClassification.useful, ["Unique", "TMs"]), + "TM18 Counter": ItemData(218, ItemClassification.filler, ["Unique", "TMs"]), + "TM19 Seismic Toss": ItemData(219, ItemClassification.useful, ["Unique", "TMs"]), + "TM20 Rage": ItemData(220, ItemClassification.useful, ["Unique", "TMs"]), + "TM21 Mega Drain": ItemData(221, ItemClassification.useful, ["Unique", "TMs"]), + "TM22 Solar Beam": ItemData(222, ItemClassification.useful, ["Unique", "TMs"]), + "TM23 Dragon Rage": ItemData(223, ItemClassification.useful, ["Unique", "TMs"]), + "TM24 Thunderbolt": ItemData(224, ItemClassification.useful, ["Unique", "TMs"]), + "TM25 Thunder": ItemData(225, ItemClassification.useful, ["Unique", "TMs"]), + "TM26 Earthquake": ItemData(226, ItemClassification.useful, ["Unique", "TMs"]), + "TM27 Fissure": ItemData(227, ItemClassification.useful, ["Unique", "TMs"]), + "TM28 Dig": ItemData(228, ItemClassification.useful, ["Unique", "TMs"]), + "TM29 Psychic": ItemData(229, ItemClassification.useful, ["Unique", "TMs"]), + "TM30 Teleport": ItemData(230, ItemClassification.filler, ["Unique", "TMs"]), + "TM31 Mimic": ItemData(231, ItemClassification.useful, ["Unique", "TMs"]), + "TM32 Double Team": ItemData(232, ItemClassification.useful, ["Unique", "TMs"]), + "TM33 Reflect": ItemData(233, ItemClassification.useful, ["Unique", "TMs"]), + "TM34 Bide": ItemData(234, ItemClassification.filler, ["Unique", "TMs"]), + "TM35 Metronome": ItemData(235, ItemClassification.useful, ["Unique", "TMs"]), + "TM36 Self Destruct": ItemData(236, ItemClassification.useful, ["Unique", "TMs"]), + "TM37 Egg Bomb": ItemData(237, ItemClassification.useful, ["Unique", "TMs"]), + "TM38 Fire Blast": ItemData(238, ItemClassification.useful, ["Unique", "TMs"]), + "TM39 Swift": ItemData(239, ItemClassification.useful, ["Unique", "TMs"]), + "TM40 Skull Bash": ItemData(240, ItemClassification.filler, ["Unique", "TMs"]), + "TM41 Soft Boiled": ItemData(241, ItemClassification.useful, ["Unique", "TMs"]), + "TM42 Dream Eater": ItemData(242, ItemClassification.useful, ["Unique", "TMs"]), + "TM43 Sky Attack": ItemData(243, ItemClassification.filler, ["Unique", "TMs"]), + "TM44 Rest": ItemData(244, ItemClassification.useful, ["Unique", "TMs"]), + "TM45 Thunder Wave": ItemData(245, ItemClassification.useful, ["Unique", "TMs"]), + "TM46 Psywave": ItemData(246, ItemClassification.filler, ["Unique", "TMs"]), + "TM47 Explosion": ItemData(247, ItemClassification.useful, ["Unique", "TMs"]), + "TM48 Rock Slide": ItemData(248, ItemClassification.useful, ["Unique", "TMs"]), + "TM49 Tri Attack": ItemData(249, ItemClassification.useful, ["Unique", "TMs"]), + "TM50 Substitute": ItemData(250, ItemClassification.useful, ["Unique", "TMs"]), + + "Fuji Saved": ItemData(None, ItemClassification.progression, []), + "Silph Co Liberated": ItemData(None, ItemClassification.progression, []), + "Become Champion": ItemData(None, ItemClassification.progression, []) +} +item_table.update( + {pokemon: ItemData(None, ItemClassification.progression, []) for pokemon in pokemon_data.keys()} +) +item_table.update( + {f"Missable {pokemon}": ItemData(None, ItemClassification.useful, []) for pokemon in pokemon_data.keys()} +) +item_table.update( + {f"Static {pokemon}": ItemData(None, ItemClassification.progression, []) for pokemon in pokemon_data.keys()} +) + + +item_groups = {} +for item, data in item_table.items(): + for group in data.groups: + item_groups[group] = item_groups.get(group, []) + [item] \ No newline at end of file diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py new file mode 100644 index 00000000..a619336f --- /dev/null +++ b/worlds/pokemon_rb/locations.py @@ -0,0 +1,1719 @@ + +from BaseClasses import Location +from .rom_addresses import rom_addresses +loc_id_start = 17200000 + +class LocationData: + def __init__(self, region, name, original_item, rom_address=None, ram_address=None, event=False, type="Item"): + self.region = region + if "Route" in region: + region = " ".join(region.split()[:2]) + self.name = region + " - " + name + self.original_item = original_item + self.rom_address = rom_address + self.ram_address = ram_address + self.event = event + self.type = type + +class EventFlag: + def __init__(self, flag): + self.byte = int(flag / 8) + self.bit = flag % 8 + self.flag = flag + + +class Missable: + def __init__(self, flag): + self.byte = int(flag / 8) + self.bit = flag % 8 + self.flag = flag + + +class Hidden: + def __init__(self, flag): + self.byte = int(flag / 8) + self.bit = flag % 8 + self.flag = flag + + +class Rod: + def __init__(self, flag): + self.byte = 0 + self.bit = flag + self.flag = flag + +# def get_locations(player=None): +location_data = [ + + LocationData("Vermilion City", "Fishing Guru", "Old Rod", rom_addresses["Rod_Vermilion_City_Fishing_Guru"], Rod(3)), + LocationData("Fuchsia City", "Fishing Guru's Brother", "Good Rod", rom_addresses["Rod_Fuchsia_City_Fishing_Brother"], Rod(4)), + LocationData("Route 12 South", "Fishing Guru's Brother", "Super Rod", rom_addresses["Rod_Route12_Fishing_Brother"], Rod(5)), + + LocationData("Pallet Town", "Player's PC", "Potion", rom_addresses['PC_Item'], EventFlag(1),), + LocationData("Celadon City", "Mansion Lady", "Tea", rom_addresses["Event_Mansion_Lady"], EventFlag(2)), + LocationData("Pallet Town", "Rival's Sister", "Town Map", rom_addresses["Event_Rivals_Sister"], EventFlag(24)), + LocationData("Pallet Town", "Oak's Post-Route-22-Rival Gift", "Poke Ball", rom_addresses["Event_Oaks_Gift"], EventFlag(36)), + LocationData("Route 1", "Free Sample Man", "Potion", rom_addresses["Event_Free_Sample"], EventFlag(960)), + LocationData("Viridian City", "Sleepy Guy", "TM42 Dream Eater", rom_addresses["Event_Sleepy_Guy"], + EventFlag(41)), + LocationData("Viridian City", "Pokemart", "Oak's Parcel", rom_addresses["Event_Pokemart_Quest"], + EventFlag(57)), + LocationData("Viridian Gym", "Giovanni 2", "TM27 Fissure", rom_addresses["Event_Viridian_Gym"], EventFlag(80)), + LocationData("Route 2 East", "Oak's Aide", "HM05 Flash", rom_addresses["Event_Route_2_Oaks_Aide"], + EventFlag(984)), + LocationData("Pewter City", "Museum", "Old Amber", rom_addresses["Event_Museum"], EventFlag(105)), + LocationData("Pewter Gym", "Brock 2", "TM34 Bide", rom_addresses["Event_Pewter_Gym"], EventFlag(118)), + LocationData("Cerulean City", "Bicycle Shop", "Bicycle", rom_addresses["Event_Bicycle_Shop"], EventFlag(192)), + LocationData("Cerulean Gym", "Misty 2", "TM11 Bubble Beam", rom_addresses["Event_Cerulean_Gym"], + EventFlag(190)), + LocationData("Route 24", "Nugget Bridge", "Nugget", rom_addresses["Event_Nugget_Bridge"], EventFlag(1344)), + LocationData("Route 25", "Bill", "S.S. Ticket", rom_addresses["Event_Bill"], EventFlag(1372)), + LocationData("Lavender Town", "Mr. Fuji", "Poke Flute", rom_addresses["Event_Fuji"], EventFlag(296)), + LocationData("Route 12 North", "Mourning Girl", "TM39 Swift", rom_addresses["Event_Mourning_Girl"], + EventFlag(1152)), + LocationData("Vermilion City", "Pokemon Fan Club", "Bike Voucher", rom_addresses["Event_Pokemon_Fan_Club"], + EventFlag(337)), + LocationData("Vermilion Gym", "Lt. Surge 2", "TM24 Thunderbolt", rom_addresses["Event_Vermillion_Gym"], + EventFlag(358)), + LocationData("S.S. Anne 2F", "Captain", "HM01 Cut", rom_addresses["Event_SS_Anne_Captain"], EventFlag(1504)), + LocationData("Route 11 East", "Oak's Aide", "Item Finder", rom_addresses["Event_Rt11_Oaks_Aide"], + EventFlag(1151)), + LocationData("Celadon City", "Stranded Man", "TM41 Soft Boiled", rom_addresses["Event_Stranded_Man"], + EventFlag(384)), + LocationData("Celadon City", "Thirsty Girl Gets Water", "TM13 Ice Beam", + rom_addresses["Event_Thirsty_Girl_Water"], EventFlag(396)), + LocationData("Celadon City", "Thirsty Girl Gets Soda Pop", "TM48 Rock Slide", + rom_addresses["Event_Thirsty_Girl_Soda"], EventFlag(397)), + LocationData("Celadon City", "Thirsty Girl Gets Lemonade", "TM49 Tri Attack", + rom_addresses["Event_Thirsty_Girl_Lemonade"], EventFlag(398)), + LocationData("Celadon City", "Counter Man", "TM18 Counter", rom_addresses["Event_Counter"], EventFlag(399)), + LocationData("Celadon City", "Gambling Addict", "Coin Case", rom_addresses["Event_Gambling_Addict"], + EventFlag(480)), + LocationData("Celadon Gym", "Erika 2", "TM21 Mega Drain", rom_addresses["Event_Celadon_Gym"], EventFlag(424)), + LocationData("Silph Co 11F", "Silph Co President", "Master Ball", rom_addresses["Event_Silph_Co_President"], + EventFlag(1933)), + LocationData("Silph Co 2F", "Woman", "TM36 Self Destruct", rom_addresses["Event_Scared_Woman"], + EventFlag(1791)), + LocationData("Route 16 North", "House Woman", "HM02 Fly", rom_addresses["Event_Rt16_House_Woman"], EventFlag(1230)), + LocationData("Route 15", "Oak's Aide", "Exp. All", rom_addresses["Event_Rt_15_Oaks_Aide"], EventFlag(1200)), + LocationData("Fuchsia City", "Safari Zone Warden", "HM04 Strength", rom_addresses["Event_Warden"], EventFlag(568)), + LocationData("Fuchsia Gym", "Koga 2", "TM06 Toxic", rom_addresses["Event_Fuschia_Gym"], EventFlag(600)), + LocationData("Safari Zone West", "Secret House", "HM03 Surf", rom_addresses["Event_Safari_Zone_Secret_House"], EventFlag(2176)), + LocationData("Cinnabar Island", "Lab Scientist", "TM35 Metronome", rom_addresses["Event_Lab_Scientist"], EventFlag(727)), + LocationData("Cinnabar Gym", "Blaine 2", "TM38 Fire Blast", rom_addresses["Event_Cinnabar_Gym"], + EventFlag(664)), + LocationData("Copycat's House", "Copycat", "TM31 Mimic", rom_addresses["Event_Copycat"], EventFlag(832)), + LocationData("Saffron City", "Mr. Psychic", "TM29 Psychic", rom_addresses["Event_Mr_Psychic"], EventFlag(944)), + LocationData("Saffron Gym", "Sabrina 2", "TM46 Psywave", rom_addresses["Event_Saffron_Gym"], EventFlag(864)), + LocationData("Fossil", "Choice A", "Dome Fossil", + [rom_addresses["Event_Dome_Fossil"], rom_addresses["Event_Dome_Fossil_B"], + rom_addresses["Dome_Fossil_Text"]], EventFlag(0x57E)), + LocationData("Fossil", "Choice B", "Helix Fossil", + [rom_addresses["Event_Helix_Fossil"], rom_addresses["Event_Helix_Fossil_B"], + rom_addresses["Helix_Fossil_Text"]], EventFlag(0x57F)), + + LocationData("Cerulean City", "Rocket Thief", "TM28 Dig", rom_addresses["Event_Rocket_Thief"], + Missable(6)), + LocationData("Route 2 East", "South Item", "Moon Stone", rom_addresses["Missable_Route_2_Item_1"], + Missable(25)), + LocationData("Route 2 East", "North Item", "HP Up", rom_addresses["Missable_Route_2_Item_2"], Missable(26)), + LocationData("Route 4", "Item", "TM04 Whirlwind", rom_addresses["Missable_Route_4_Item"], Missable(27)), + LocationData("Route 9", "Item", "TM30 Teleport", rom_addresses["Missable_Route_9_Item"], Missable(28)), + LocationData("Route 12 North", "Island Item", "TM16 Pay Day", rom_addresses["Missable_Route_12_Item_1"], Missable(30)), + LocationData("Route 12 South", "Item Behind Cuttable Tree", "Iron", rom_addresses["Missable_Route_12_Item_2"], Missable(31)), + LocationData("Route 15", "Item", "TM20 Rage", rom_addresses["Missable_Route_15_Item"], Missable(32)), + LocationData("Route 24", "Item", "TM45 Thunder Wave", rom_addresses["Missable_Route_24_Item"], Missable(37)), + LocationData("Route 25", "Item", "TM19 Seismic Toss", rom_addresses["Missable_Route_25_Item"], Missable(38)), + LocationData("Viridian Gym", "Item", "Revive", rom_addresses["Missable_Viridian_Gym_Item"], Missable(51)), + LocationData("Cerulean Cave 1F", "Southwest Item", "Full Restore", rom_addresses["Missable_Cerulean_Cave_1F_Item_1"], + Missable(53)), + LocationData("Cerulean Cave 1F", "Northeast Item", "Max Elixir", rom_addresses["Missable_Cerulean_Cave_1F_Item_2"], + Missable(54)), + LocationData("Cerulean Cave 1F", "Northwest Item", "Nugget", rom_addresses["Missable_Cerulean_Cave_1F_Item_3"], + Missable(55)), + LocationData("Pokemon Tower 3F", "North Item", "Escape Rope", rom_addresses["Missable_Pokemon_Tower_3F_Item"], + Missable(57)), + LocationData("Pokemon Tower 4F", "East Item", "Elixir", rom_addresses["Missable_Pokemon_Tower_4F_Item_1"], + Missable(58)), + LocationData("Pokemon Tower 4F", "West Item", "Awakening", rom_addresses["Missable_Pokemon_Tower_4F_Item_2"], + Missable(59)), + LocationData("Pokemon Tower 4F", "South Item", "HP Up", rom_addresses["Missable_Pokemon_Tower_4F_Item_3"], + Missable(60)), + LocationData("Pokemon Tower 5F", "Southwest Item", "Nugget", rom_addresses["Missable_Pokemon_Tower_5F_Item"], + Missable(61)), + LocationData("Pokemon Tower 6F", "West Item", "Rare Candy", rom_addresses["Missable_Pokemon_Tower_6F_Item_1"], + Missable(62)), + LocationData("Pokemon Tower 6F", "Southeast Item", "X Accuracy", rom_addresses["Missable_Pokemon_Tower_6F_Item_2"], + Missable(63)), + LocationData("Fuchsia City", "Warden's House Item", "Rare Candy", rom_addresses["Missable_Wardens_House_Item"], + Missable(71)), + LocationData("Pokemon Mansion 1F", "North Item", "Escape Rope", + rom_addresses["Missable_Pokemon_Mansion_1F_Item_1"], Missable(72)), + LocationData("Pokemon Mansion 1F", "South Item", "Carbos", rom_addresses["Missable_Pokemon_Mansion_1F_Item_2"], + Missable(73)), + LocationData("Power Plant", "Southwest Item", "Carbos", rom_addresses["Missable_Power_Plant_Item_1"], Missable(86)), + LocationData("Power Plant", "North Item", "HP Up", rom_addresses["Missable_Power_Plant_Item_2"], Missable(87)), + LocationData("Power Plant", "Northeast Item", "Rare Candy", rom_addresses["Missable_Power_Plant_Item_3"], + Missable(88)), + LocationData("Power Plant", "Southeast Item", "TM25 Thunder", rom_addresses["Missable_Power_Plant_Item_4"], + Missable(89)), + LocationData("Power Plant", "South Item", "TM33 Reflect", rom_addresses["Missable_Power_Plant_Item_5"], + Missable(90)), + LocationData("Victory Road 2F", "Northeast Item", "TM17 Submission", rom_addresses["Missable_Victory_Road_2F_Item_1"], + Missable(92)), + LocationData("Victory Road 2F", "East Item", "Full Heal", rom_addresses["Missable_Victory_Road_2F_Item_2"], + Missable(93)), + LocationData("Victory Road 2F", "West Item", "TM05 Mega Kick", rom_addresses["Missable_Victory_Road_2F_Item_3"], + Missable(94)), + LocationData("Victory Road 2F", "North Item Near Moltres", "Guard Spec", rom_addresses["Missable_Victory_Road_2F_Item_4"], + Missable(95)), + LocationData("Viridian Forest", "East Item", "Antidote", rom_addresses["Missable_Viridian_Forest_Item_1"], + Missable(100)), + LocationData("Viridian Forest", "Northwest Item", "Potion", rom_addresses["Missable_Viridian_Forest_Item_2"], + Missable(101)), + LocationData("Viridian Forest", "Southwest Item", "Poke Ball", + rom_addresses["Missable_Viridian_Forest_Item_3"], Missable(102)), + LocationData("Mt Moon 1F", "West Item", "Potion", rom_addresses["Missable_Mt_Moon_1F_Item_1"], Missable(103)), + LocationData("Mt Moon 1F", "Northwest Item", "Moon Stone", rom_addresses["Missable_Mt_Moon_1F_Item_2"], Missable(104)), + LocationData("Mt Moon 1F", "Southeast Item", "Rare Candy", rom_addresses["Missable_Mt_Moon_1F_Item_3"], Missable(105)), + LocationData("Mt Moon 1F", "East Item", "Escape Rope", rom_addresses["Missable_Mt_Moon_1F_Item_4"], + Missable(106)), + LocationData("Mt Moon 1F", "South Item", "Potion", rom_addresses["Missable_Mt_Moon_1F_Item_5"], Missable(107)), + LocationData("Mt Moon 1F", "Southwest Item", "TM12 Water Gun", rom_addresses["Missable_Mt_Moon_1F_Item_6"], + Missable(108)), + LocationData("Mt Moon B2F", "South Item", "HP Up", rom_addresses["Missable_Mt_Moon_B2F_Item_1"], Missable(111)), + LocationData("Mt Moon B2F", "North Item", "TM01 Mega Punch", rom_addresses["Missable_Mt_Moon_B2F_Item_2"], + Missable(112)), + LocationData("S.S. Anne 1F", "Item", "TM08 Body Slam", rom_addresses["Missable_SS_Anne_1F_Item"], + Missable(114)), + LocationData("S.S. Anne 2F", "Item 1", "Max Ether", rom_addresses["Missable_SS_Anne_2F_Item_1"], + Missable(115)), + LocationData("S.S. Anne 2F", "Item 2", "Rare Candy", rom_addresses["Missable_SS_Anne_2F_Item_2"], + Missable(116)), + LocationData("S.S. Anne B1F", "Item 1", "Ether", rom_addresses["Missable_SS_Anne_B1F_Item_1"], Missable(117)), + LocationData("S.S. Anne B1F", "Item 2", "TM44 Rest", rom_addresses["Missable_SS_Anne_B1F_Item_2"], + Missable(118)), + LocationData("S.S. Anne B1F", "Item 3", "Max Potion", rom_addresses["Missable_SS_Anne_B1F_Item_3"], + Missable(119)), + LocationData("Victory Road 3F", "Northeast Item", "Max Revive", rom_addresses["Missable_Victory_Road_3F_Item_1"], + Missable(120)), + LocationData("Victory Road 3F", "Northwest Item", "TM47 Explosion", rom_addresses["Missable_Victory_Road_3F_Item_2"], + Missable(121)), + LocationData("Rocket Hideout B1F", "West Item", "Escape Rope", + rom_addresses["Missable_Rocket_Hideout_B1F_Item_1"], Missable(123)), + LocationData("Rocket Hideout B1F", "Southwest Item", "Hyper Potion", + rom_addresses["Missable_Rocket_Hideout_B1F_Item_2"], Missable(124)), + LocationData("Rocket Hideout B2F", "Northwest Left Item", "Moon Stone", rom_addresses["Missable_Rocket_Hideout_B2F_Item_1"], + Missable(125)), + LocationData("Rocket Hideout B2F", "Northeast Item", "Nugget", rom_addresses["Missable_Rocket_Hideout_B2F_Item_2"], + Missable(126)), + LocationData("Rocket Hideout B2F", "Northwest Right Item", "TM07 Horn Drill", + rom_addresses["Missable_Rocket_Hideout_B2F_Item_3"], Missable(127)), + LocationData("Rocket Hideout B2F", "Southwest Item", "Super Potion", + rom_addresses["Missable_Rocket_Hideout_B2F_Item_4"], Missable(128)), + LocationData("Rocket Hideout B3F", "East Item", "TM10 Double Edge", + rom_addresses["Missable_Rocket_Hideout_B3F_Item_1"], Missable(129)), + LocationData("Rocket Hideout B3F", "Center Item", "Rare Candy", rom_addresses["Missable_Rocket_Hideout_B3F_Item_2"], + Missable(130)), + LocationData("Rocket Hideout B4F", "West Item", "HP Up", rom_addresses["Missable_Rocket_Hideout_B4F_Item_1"], + Missable(132)), + LocationData("Rocket Hideout B4F", "Northwest Item", "TM02 Razor Wind", + rom_addresses["Missable_Rocket_Hideout_B4F_Item_2"], Missable(133)), + LocationData("Rocket Hideout B4F", "Southwest Item (Lift Key)", "Iron", rom_addresses["Missable_Rocket_Hideout_B4F_Item_3"], + Missable(134)), + LocationData("Rocket Hideout B4F", "Giovanni Item (Lift Key)", "Silph Scope", + rom_addresses["Missable_Rocket_Hideout_B4F_Item_4"], [EventFlag(0x6A7), Missable(135)]), + LocationData("Rocket Hideout B4F", "Rocket Grunt Item", "Lift Key", rom_addresses["Missable_Rocket_Hideout_B4F_Item_5"], + [EventFlag(0x6A6), Missable(136)]), + LocationData("Silph Co 3F", "Item (Card Key)", "Hyper Potion", rom_addresses["Missable_Silph_Co_3F_Item"], Missable(144)), + LocationData("Silph Co 4F", "Left Item (Card Key)", "Full Heal", rom_addresses["Missable_Silph_Co_4F_Item_1"], + Missable(148)), + LocationData("Silph Co 4F", "Middle Item (Card Key)", "Max Revive", rom_addresses["Missable_Silph_Co_4F_Item_2"], + Missable(149)), + LocationData("Silph Co 4F", "Right Item (Card Key)", "Escape Rope", rom_addresses["Missable_Silph_Co_4F_Item_3"], + Missable(150)), + LocationData("Silph Co 5F", "Southwest Item", "TM09 Take Down", rom_addresses["Missable_Silph_Co_5F_Item_1"], + Missable(155)), + LocationData("Silph Co 5F", "Northwest Item (Card Key)", "Protein", rom_addresses["Missable_Silph_Co_5F_Item_2"], Missable(156)), + LocationData("Silph Co 5F", "Southeast Item", "Card Key", rom_addresses["Missable_Silph_Co_5F_Item_3"], Missable(157)), + LocationData("Silph Co 6F", "West Item (Card Key)", "HP Up", rom_addresses["Missable_Silph_Co_6F_Item_1"], Missable(161)), + LocationData("Silph Co 6F", "Southwest Item (Card Key)", "X Accuracy", rom_addresses["Missable_Silph_Co_6F_Item_2"], + Missable(162)), + LocationData("Silph Co 7F", "West Item", "Calcium", rom_addresses["Missable_Silph_Co_7F_Item_1"], Missable(168)), + LocationData("Silph Co 7F", "East Item (Card Key)", "TM03 Swords Dance", rom_addresses["Missable_Silph_Co_7F_Item_2"], + Missable(169)), + LocationData("Silph Co 10F", "Left Item", "TM26 Earthquake", rom_addresses["Missable_Silph_Co_10F_Item_1"], + Missable(180)), + LocationData("Silph Co 10F", "Bottom Item", "Rare Candy", rom_addresses["Missable_Silph_Co_10F_Item_2"], + Missable(181)), + LocationData("Silph Co 10F", "Right Item", "Carbos", rom_addresses["Missable_Silph_Co_10F_Item_3"], Missable(182)), + LocationData("Pokemon Mansion 2F", "Northeast Item", "Calcium", rom_addresses["Missable_Pokemon_Mansion_2F_Item"], + Missable(187)), + LocationData("Pokemon Mansion 3F", "Southwest Item", "Max Potion", rom_addresses["Missable_Pokemon_Mansion_3F_Item_1"], + Missable(188)), + LocationData("Pokemon Mansion 3F", "Northeast Item", "Iron", rom_addresses["Missable_Pokemon_Mansion_3F_Item_2"], + Missable(189)), + LocationData("Pokemon Mansion B1F", "North Item", "Rare Candy", + rom_addresses["Missable_Pokemon_Mansion_B1F_Item_1"], Missable(190)), + LocationData("Pokemon Mansion B1F", "Southwest Item", "Full Restore", + rom_addresses["Missable_Pokemon_Mansion_B1F_Item_2"], Missable(191)), + LocationData("Pokemon Mansion B1F", "South Item", "TM14 Blizzard", + rom_addresses["Missable_Pokemon_Mansion_B1F_Item_3"], Missable(192)), + LocationData("Pokemon Mansion B1F", "Northwest Item", "TM22 Solar Beam", + rom_addresses["Missable_Pokemon_Mansion_B1F_Item_4"], Missable(193)), + LocationData("Pokemon Mansion B1F", "West Item", "Secret Key", + rom_addresses["Missable_Pokemon_Mansion_B1F_Item_5"], Missable(194)), + LocationData("Safari Zone East", "Northeast Item", "Full Restore", rom_addresses["Missable_Safari_Zone_East_Item_1"], + Missable(195)), + LocationData("Safari Zone East", "West Item", "Max Potion", rom_addresses["Missable_Safari_Zone_East_Item_2"], + Missable(196)), + LocationData("Safari Zone East", "East Item", "Carbos", rom_addresses["Missable_Safari_Zone_East_Item_3"], + Missable(197)), + LocationData("Safari Zone East", "Center Item", "TM37 Egg Bomb", rom_addresses["Missable_Safari_Zone_East_Item_4"], + Missable(198)), + LocationData("Safari Zone North", "Northeast Item", "Protein", rom_addresses["Missable_Safari_Zone_North_Item_1"], + Missable(199)), + LocationData("Safari Zone North", "North Item", "TM40 Skull Bash", + rom_addresses["Missable_Safari_Zone_North_Item_2"], Missable(200)), + LocationData("Safari Zone West", "Southwest Item", "Max Potion", rom_addresses["Missable_Safari_Zone_West_Item_1"], + Missable(201)), + LocationData("Safari Zone West", "Northwest Item", "TM32 Double Team", + rom_addresses["Missable_Safari_Zone_West_Item_2"], Missable(202)), + LocationData("Safari Zone West", "Southeast Item", "Max Revive", rom_addresses["Missable_Safari_Zone_West_Item_3"], + Missable(203)), + LocationData("Safari Zone West", "Northeast Item", "Gold Teeth", rom_addresses["Missable_Safari_Zone_West_Item_4"], + Missable(204)), + LocationData("Safari Zone Center", "Island Item", "Nugget", rom_addresses["Missable_Safari_Zone_Center_Item"], + Missable(205)), + LocationData("Cerulean Cave 2F", "East Item", "PP Up", rom_addresses["Missable_Cerulean_Cave_2F_Item_1"], + Missable(206)), + LocationData("Cerulean Cave 2F", "Southwest Item", "Ultra Ball", rom_addresses["Missable_Cerulean_Cave_2F_Item_2"], + Missable(207)), + LocationData("Cerulean Cave 2F", "North Item", "Full Restore", rom_addresses["Missable_Cerulean_Cave_2F_Item_3"], + Missable(208)), + LocationData("Cerulean Cave B1F", "Center Item", "Ultra Ball", rom_addresses["Missable_Cerulean_Cave_B1F_Item_1"], + Missable(210)), + LocationData("Cerulean Cave B1F", "North Item", "Max Revive", rom_addresses["Missable_Cerulean_Cave_B1F_Item_2"], + Missable(211)), + LocationData("Victory Road 1F", "Top Item", "TM43 Sky Attack", rom_addresses["Missable_Victory_Road_1F_Item_1"], + Missable(212)), + LocationData("Victory Road 1F", "Left Item", "Rare Candy", rom_addresses["Missable_Victory_Road_1F_Item_2"], + Missable(213)), + LocationData("Rock Tunnel B1F", "Southwest Item", "Hideout Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_1"], + Missable(231)), + LocationData("Rock Tunnel B1F", "West Item", "Mansion Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_2"], + Missable(232)), + LocationData("Rock Tunnel B1F", "Northwest Item", "Plant Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_3"], + Missable(233)), + LocationData("Rock Tunnel B1F", "North Item", "Safari Pass", rom_addresses["Missable_Rock_Tunnel_B1F_Item_4"], + Missable(234)), + + LocationData("Pewter Gym", "Brock 1", "Boulder Badge", rom_addresses['Badge_Pewter_Gym'], EventFlag(0x8A0)), + LocationData("Cerulean Gym", "Misty 1", "Cascade Badge", rom_addresses['Badge_Cerulean_Gym'], EventFlag(0x8A1)), + LocationData("Vermilion Gym", "Lt. Surge 1", "Thunder Badge", rom_addresses['Badge_Vermilion_Gym'], EventFlag(0x8A2)), + LocationData("Celadon Gym", "Erika 1", "Rainbow Badge", rom_addresses['Badge_Celadon_Gym'], EventFlag(0x8A3)), + LocationData("Fuchsia Gym", "Koga 1", "Soul Badge", rom_addresses['Badge_Fuchsia_Gym'], EventFlag(0x8A4)), + LocationData("Saffron Gym", "Sabrina 1", "Marsh Badge", rom_addresses['Badge_Saffron_Gym'], EventFlag(0x8A5)), + LocationData("Cinnabar Gym", "Blaine 1", "Volcano Badge", rom_addresses['Badge_Cinnabar_Gym'], EventFlag(0x8A6)), + LocationData("Viridian Gym", "Giovanni 1", "Earth Badge", rom_addresses['Badge_Viridian_Gym'], EventFlag(0x8A7)), + + LocationData("Viridian Forest", "Hidden Item Northwest by Trainer", "Potion", rom_addresses['Hidden_Item_Viridian_Forest_1'], Hidden(0)), + LocationData("Viridian Forest", "Hidden Item Entrance Tree", "Antidote", rom_addresses['Hidden_Item_Viridian_Forest_2'], Hidden(1)), + LocationData("Mt Moon B2F", "Hidden Item Dead End Before Fossils", "Moon Stone", rom_addresses['Hidden_Item_MtMoonB2F_1'], Hidden(2)), + LocationData("Route 25", "Hidden Item Fence Outside Bill's House", "Ether", rom_addresses['Hidden_Item_Route_25_1'], Hidden(3)), + LocationData("Route 9", "Hidden Item Rock By Grass", "Ether", rom_addresses['Hidden_Item_Route_9'], Hidden(4)), + LocationData("S.S. Anne 1F", "Hidden Item Kitchen Trash", "Great Ball", rom_addresses['Hidden_Item_SS_Anne_Kitchen'], Hidden(5)), + LocationData("S.S. Anne B1F", "Hidden Item Under Pillow", "Hyper Potion", rom_addresses['Hidden_Item_SS_Anne_B1F'], Hidden(6)), + LocationData("Route 10 North", "Hidden Item Behind Rock Tunnel Entrance Tree", "Super Potion", rom_addresses['Hidden_Item_Route_10_1'], Hidden(7)), + LocationData("Route 10 South", "Hidden Item Rock", "Max Ether", rom_addresses['Hidden_Item_Route_10_2'], Hidden(8)), + LocationData("Rocket Hideout B1F", "Hidden Item Pot Plant", "PP Up", rom_addresses['Hidden_Item_Rocket_Hideout_B1F'], Hidden(9)), + LocationData("Rocket Hideout B3F", "Hidden Item Near East Item", "Nugget", rom_addresses['Hidden_Item_Rocket_Hideout_B3F'], Hidden(10)), + LocationData("Rocket Hideout B4F", "Hidden Item Behind Giovanni", "Super Potion", rom_addresses['Hidden_Item_Rocket_Hideout_B4F'], Hidden(11)), + LocationData("Pokemon Tower 5F", "Hidden Item Near West Staircase", "Elixir", rom_addresses['Hidden_Item_Pokemon_Tower_5F'], Hidden(12)), + LocationData("Route 13", "Hidden Item Dead End Boulder", "PP Up", rom_addresses['Hidden_Item_Route_13_1'], Hidden(13)), + LocationData("Route 13", "Hidden Item Dead End By Water Corner", "Calcium", rom_addresses['Hidden_Item_Route_13_2'], Hidden(14)), + LocationData("Pokemon Mansion B1F", "Hidden Item Secret Key Room Corner", "Rare Candy", rom_addresses['Hidden_Item_Pokemon_Mansion_B1F'], Hidden(15)), + LocationData("Safari Zone West", "Hidden Item Secret House Statue", "Revive", rom_addresses['Hidden_Item_Safari_Zone_West'], Hidden(17)), + LocationData("Silph Co 5F", "Hidden Item Pot Plant", "Elixir", rom_addresses['Hidden_Item_Silph_Co_5F'], Hidden(18)), + LocationData("Silph Co 9F", "Hidden Item Nurse Bed", "Max Potion", rom_addresses['Hidden_Item_Silph_Co_9F'], Hidden(19)), + LocationData("Copycat's House", "Hidden Item Desk", "Nugget", rom_addresses['Hidden_Item_Copycats_House'], Hidden(20)), + LocationData("Cerulean Cave 1F", "Hidden Item Center Rocks", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_Cave_1F'], Hidden(21)), + LocationData("Cerulean Cave B1F", "Hidden Item Northeast Rocks", "Ultra Ball", rom_addresses['Hidden_Item_Cerulean_Cave_B1F'], Hidden(22)), + LocationData("Power Plant", "Hidden Item Central Dead End", "Max Elixir", rom_addresses['Hidden_Item_Power_Plant_1'], Hidden(23)), + LocationData("Power Plant", "Hidden Item Before Zapdos", "PP Up", rom_addresses['Hidden_Item_Power_Plant_2'], Hidden(24)), + LocationData("Seafoam Islands B2F", "Hidden Item Rock", "Nugget", rom_addresses['Hidden_Item_Seafoam_Islands_B2F'], Hidden(25)), + LocationData("Seafoam Islands B4F", "Hidden Item Corner Island", "Ultra Ball", rom_addresses['Hidden_Item_Seafoam_Islands_B4F'], Hidden(26)), + LocationData("Pokemon Mansion 1F", "Hidden Item Block Near Entrance Carpet", "Moon Stone", rom_addresses['Hidden_Item_Pokemon_Mansion_1F'], Hidden(27)), + LocationData("Pokemon Mansion 3F", "Hidden Item Behind Burglar", "Max Revive", rom_addresses['Hidden_Item_Pokemon_Mansion_3F'], Hidden(28)), + LocationData("Route 23 North", "Hidden Item Rocks Before Final Guard", "Full Restore", rom_addresses['Hidden_Item_Route_23_1'], Hidden(29)), + LocationData("Route 23 North", "Hidden Item East Tree After Water", "Ultra Ball", rom_addresses['Hidden_Item_Route_23_2'], Hidden(30)), + LocationData("Route 23 South", "Hidden Item On Island", "Max Ether", rom_addresses['Hidden_Item_Route_23_3'], Hidden(31)), + LocationData("Victory Road 2F", "Hidden Item Rock Before Moltres", "Ultra Ball", rom_addresses['Hidden_Item_Victory_Road_2F_1'], Hidden(32)), + LocationData("Victory Road 2F", "Hidden Item Rock In Final Room", "Full Restore", rom_addresses['Hidden_Item_Victory_Road_2F_2'], Hidden(33)), + #LocationData("Vermilion City", "Hidden Item The Truck", "Max Elixir", rom_addresses['Hidden_Item_Unused_6F'], Hidden(34)), + LocationData("Viridian City", "Hidden Item Cuttable Tree", "Potion", rom_addresses['Hidden_Item_Viridian_City'], Hidden(35)), + LocationData("Route 11", "Hidden Item Isolated Tree Near Gate", "Potion", rom_addresses['Hidden_Item_Route_11'], Hidden(36)), + LocationData("Route 12 West", "Hidden Item Tree Near Gate", "Hyper Potion", rom_addresses['Hidden_Item_Route_12'], Hidden(37)), + LocationData("Route 17", "Hidden Item In Grass", "Rare Candy", rom_addresses['Hidden_Item_Route_17_1'], Hidden(38)), + LocationData("Route 17", "Hidden Item Near Northernmost Sign", "Full Restore", rom_addresses['Hidden_Item_Route_17_2'], Hidden(39)), + LocationData("Route 17", "Hidden Item East Center", "PP Up", rom_addresses['Hidden_Item_Route_17_3'], Hidden(40)), + LocationData("Route 17", "Hidden Item West Center", "Max Revive", rom_addresses['Hidden_Item_Route_17_4'], Hidden(41)), + LocationData("Route 17", "Hidden Item Before Final Bridge", "Max Elixir", rom_addresses['Hidden_Item_Route_17_5'], Hidden(42)), + LocationData("Underground Tunnel North-South", "Hidden Item Near Northern Stairs", "Full Restore", rom_addresses['Hidden_Item_Underground_Path_NS_1'], Hidden(43)), + LocationData("Underground Tunnel North-South", "Hidden Item Near Southern Stairs", "X Special", rom_addresses['Hidden_Item_Underground_Path_NS_2'], Hidden(44)), + LocationData("Underground Tunnel West-East", "Hidden Item West", "Nugget", rom_addresses['Hidden_Item_Underground_Path_WE_1'], Hidden(45)), + LocationData("Underground Tunnel West-East", "Hidden Item East", "Elixir", rom_addresses['Hidden_Item_Underground_Path_WE_2'], Hidden(46)), + LocationData("Celadon City", "Hidden Item Dead End Near Cuttable Tree", "PP Up", rom_addresses['Hidden_Item_Celadon_City'], Hidden(47)), + LocationData("Route 25", "Hidden Item Northeast Of Grass", "Elixir", rom_addresses['Hidden_Item_Route_25_2'], Hidden(48)), + LocationData("Mt Moon B2F", "Hidden Item Lone Rock", "Ether", rom_addresses['Hidden_Item_MtMoonB2F_2'], Hidden(49)), + LocationData("Seafoam Islands B3F", "Hidden Item Rock", "Max Elixir", rom_addresses['Hidden_Item_Seafoam_Islands_B3F'], Hidden(50)), + LocationData("Vermilion City", "Hidden Item In Water Near Fan Club", "Max Ether", rom_addresses['Hidden_Item_Vermilion_City'], Hidden(51)), + LocationData("Cerulean City", "Hidden Item Gym Badge Guy's Backyard", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_City'], Hidden(52)), + LocationData("Route 4", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53)), + + LocationData("Indigo Plateau", "Become Champion", "Become Champion", event=True), + LocationData("Pokemon Tower 7F", "Fuji Saved", "Fuji Saved", event=True), + LocationData("Silph Co 11F", "Silph Co Liberated", "Silph Co Liberated", event=True), + + LocationData("Pallet Town", "Super Rod Pokemon - 1", "Tentacool", rom_addresses["Wild_Super_Rod_A"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pallet Town", "Super Rod Pokemon - 2", "Poliwag", rom_addresses["Wild_Super_Rod_A"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 22", "Super Rod Pokemon - 1", "Goldeen", rom_addresses["Wild_Super_Rod_B"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 22", "Super Rod Pokemon - 2", "Poliwag", rom_addresses["Wild_Super_Rod_B"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Super Rod Pokemon - 1", "Psyduck", rom_addresses["Wild_Super_Rod_C"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Super Rod Pokemon - 2", "Goldeen", rom_addresses["Wild_Super_Rod_C"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Super Rod Pokemon - 3", "Krabby", rom_addresses["Wild_Super_Rod_C"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Super Rod Pokemon - 1", "Krabby", rom_addresses["Wild_Super_Rod_D"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Super Rod Pokemon - 2", "Shellder", rom_addresses["Wild_Super_Rod_D"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Super Rod Pokemon - 1", "Poliwhirl", rom_addresses["Wild_Super_Rod_E"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Super Rod Pokemon - 2", "Slowpoke", rom_addresses["Wild_Super_Rod_E"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Super Rod Pokemon - 1", "Dratini", rom_addresses["Wild_Super_Rod_F"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Super Rod Pokemon - 2", "Krabby", rom_addresses["Wild_Super_Rod_F"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Super Rod Pokemon - 3", "Psyduck", rom_addresses["Wild_Super_Rod_F"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Super Rod Pokemon - 4", "Slowpoke", rom_addresses["Wild_Super_Rod_F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 North", "Super Rod Pokemon - 1", "Tentacool", rom_addresses["Wild_Super_Rod_G"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 North", "Super Rod Pokemon - 2", "Krabby", rom_addresses["Wild_Super_Rod_G"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 North", "Super Rod Pokemon - 3", "Goldeen", rom_addresses["Wild_Super_Rod_G"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 North", "Super Rod Pokemon - 4", "Magikarp", rom_addresses["Wild_Super_Rod_G"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 19", "Super Rod Pokemon - 1", "Staryu", rom_addresses["Wild_Super_Rod_H"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Route 19", "Super Rod Pokemon - 2", "Horsea", rom_addresses["Wild_Super_Rod_H"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Route 19", "Super Rod Pokemon - 3", "Shellder", rom_addresses["Wild_Super_Rod_H"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Route 19", "Super Rod Pokemon - 4", "Goldeen", rom_addresses["Wild_Super_Rod_H"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 South", "Super Rod Pokemon - 1", "Slowbro", rom_addresses["Wild_Super_Rod_I"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 South", "Super Rod Pokemon - 2", "Seaking", rom_addresses["Wild_Super_Rod_I"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 South", "Super Rod Pokemon - 3", "Kingler", rom_addresses["Wild_Super_Rod_I"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 South", "Super Rod Pokemon - 4", "Seadra", rom_addresses["Wild_Super_Rod_I"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Fuchsia City", "Super Rod Pokemon - 1", "Seaking", rom_addresses["Wild_Super_Rod_J"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Fuchsia City", "Super Rod Pokemon - 2", "Krabby", rom_addresses["Wild_Super_Rod_J"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Fuchsia City", "Super Rod Pokemon - 3", "Goldeen", rom_addresses["Wild_Super_Rod_J"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Fuchsia City", "Super Rod Pokemon - 4", "Magikarp", rom_addresses["Wild_Super_Rod_J"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 1", "Pidgey", rom_addresses["Wild_Route1"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 2", "Rattata", rom_addresses["Wild_Route1"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route1"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 4", "Rattata", rom_addresses["Wild_Route1"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 5", "Pidgey", rom_addresses["Wild_Route1"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 6", "Pidgey", rom_addresses["Wild_Route1"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 7", "Pidgey", rom_addresses["Wild_Route1"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 8", "Rattata", rom_addresses["Wild_Route1"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 9", "Pidgey", rom_addresses["Wild_Route1"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 1", "Wild Pokemon - 10", "Pidgey", rom_addresses["Wild_Route1"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route2"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route2"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route2"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 4", "Rattata", rom_addresses["Wild_Route2"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 5", "Pidgey", rom_addresses["Wild_Route2"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 6", ["Weedle", "Caterpie"], rom_addresses["Wild_Route2"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 7", "Rattata", rom_addresses["Wild_Route2"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 8", "Rattata", rom_addresses["Wild_Route2"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 9", ["Weedle", "Caterpie"], rom_addresses["Wild_Route2"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 2", "Wild Pokemon - 10", ["Weedle", "Caterpie"], rom_addresses["Wild_Route2"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route22"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 2", ["Nidoran M", "Nidoran F"], rom_addresses["Wild_Route22"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route22"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 4", ["Nidoran M", "Nidoran F"], rom_addresses["Wild_Route22"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 5", "Rattata", rom_addresses["Wild_Route22"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 6", ["Nidoran M", "Nidoran F"], rom_addresses["Wild_Route22"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 7", "Spearow", rom_addresses["Wild_Route22"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route22"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 9", ["Nidoran F", "Nidoran M"], rom_addresses["Wild_Route22"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Route 22", "Wild Pokemon - 10", ["Nidoran F", "Nidoran M"], rom_addresses["Wild_Route22"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 1", ["Weedle", "Caterpie"], + rom_addresses["Wild_ViridianForest"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 2", ["Kakuna", "Metapod"], + rom_addresses["Wild_ViridianForest"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 3", ["Weedle", "Caterpie"], + rom_addresses["Wild_ViridianForest"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 4", ["Weedle", "Caterpie"], + rom_addresses["Wild_ViridianForest"] + 7, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 5", ["Kakuna", "Metapod"], + rom_addresses["Wild_ViridianForest"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 6", ["Kakuna", "Metapod"], + rom_addresses["Wild_ViridianForest"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 7", ["Metapod", "Kakuna"], + rom_addresses["Wild_ViridianForest"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 8", ["Caterpie", "Weedle"], + rom_addresses["Wild_ViridianForest"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 9", "Pikachu", rom_addresses["Wild_ViridianForest"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Viridian Forest", "Wild Pokemon - 10", "Pikachu", rom_addresses["Wild_ViridianForest"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 1", "Pidgey", rom_addresses["Wild_Route3"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route3"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route3"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 4", "Spearow", rom_addresses["Wild_Route3"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route3"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 6", "Pidgey", rom_addresses["Wild_Route3"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 7", "Spearow", rom_addresses["Wild_Route3"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 8", "Jigglypuff", rom_addresses["Wild_Route3"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 9", "Jigglypuff", rom_addresses["Wild_Route3"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 3", "Wild Pokemon - 10", "Jigglypuff", rom_addresses["Wild_Route3"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 1", "Zubat", rom_addresses["Wild_MtMoon1F"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 2", "Zubat", rom_addresses["Wild_MtMoon1F"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 3", "Zubat", rom_addresses["Wild_MtMoon1F"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 4", "Geodude", rom_addresses["Wild_MtMoon1F"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 5", "Zubat", rom_addresses["Wild_MtMoon1F"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 6", "Zubat", rom_addresses["Wild_MtMoon1F"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 7", "Geodude", rom_addresses["Wild_MtMoon1F"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 8", "Paras", rom_addresses["Wild_MtMoon1F"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 9", "Zubat", rom_addresses["Wild_MtMoon1F"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon 1F", "Wild Pokemon - 10", "Clefairy", rom_addresses["Wild_MtMoon1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 1", "Zubat", rom_addresses["Wild_MtMoonB1F"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 2", "Zubat", rom_addresses["Wild_MtMoonB1F"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 3", "Geodude", rom_addresses["Wild_MtMoonB1F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 4", "Geodude", rom_addresses["Wild_MtMoonB1F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 5", "Zubat", rom_addresses["Wild_MtMoonB1F"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 6", "Paras", rom_addresses["Wild_MtMoonB1F"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 7", "Zubat", rom_addresses["Wild_MtMoonB1F"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 8", "Zubat", rom_addresses["Wild_MtMoonB1F"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 9", "Clefairy", rom_addresses["Wild_MtMoonB1F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B1F", "Wild Pokemon - 10", "Geodude", rom_addresses["Wild_MtMoonB1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 1", "Zubat", rom_addresses["Wild_MtMoonB2F"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 2", "Geodude", rom_addresses["Wild_MtMoonB2F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 3", "Zubat", rom_addresses["Wild_MtMoonB2F"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 4", "Geodude", rom_addresses["Wild_MtMoonB2F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 5", "Zubat", rom_addresses["Wild_MtMoonB2F"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 6", "Paras", rom_addresses["Wild_MtMoonB2F"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 7", "Paras", rom_addresses["Wild_MtMoonB2F"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 8", "Clefairy", rom_addresses["Wild_MtMoonB2F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 9", "Zubat", rom_addresses["Wild_MtMoonB2F"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Mt Moon B2F", "Wild Pokemon - 10", "Clefairy", rom_addresses["Wild_MtMoonB2F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route4"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route4"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route4"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 4", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route4"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 6", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 7", "Rattata", rom_addresses["Wild_Route4"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route4"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 9", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 4", "Wild Pokemon - 10", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 1", ["Weedle", "Caterpie"], rom_addresses["Wild_Route24"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 2", ["Kakuna", "Metapod"], rom_addresses["Wild_Route24"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route24"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 4", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route24"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route24"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 6", "Abra", rom_addresses["Wild_Route24"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route24"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 8", "Pidgey", rom_addresses["Wild_Route24"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 9", "Abra", rom_addresses["Wild_Route24"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 24", "Wild Pokemon - 10", "Abra", rom_addresses["Wild_Route24"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 1", ["Weedle", "Caterpie"], rom_addresses["Wild_Route25"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 2", ["Kakuna", "Metapod"], rom_addresses["Wild_Route25"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route25"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 4", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route25"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route25"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 6", "Abra", rom_addresses["Wild_Route25"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route25"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 8", "Abra", rom_addresses["Wild_Route25"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 9", ["Metapod", "Kakuna"], rom_addresses["Wild_Route25"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 25", "Wild Pokemon - 10", ["Caterpie", "Weedle"], rom_addresses["Wild_Route25"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route9"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route9"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route9"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 4", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route9"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route9"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 6", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route9"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 7", "Rattata", rom_addresses["Wild_Route9"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route9"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 9", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route9"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 9", "Wild Pokemon - 10", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route9"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route5"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route5"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route5"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 4", ["Mankey", "Meowth"], rom_addresses["Wild_Route5"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 5", ["Mankey", "Meowth"], rom_addresses["Wild_Route5"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 6", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route5"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route5"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 8", "Pidgey", rom_addresses["Wild_Route5"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 9", ["Mankey", "Meowth"], rom_addresses["Wild_Route5"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 5", "Wild Pokemon - 10", ["Mankey", "Meowth"], rom_addresses["Wild_Route5"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route6"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route6"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route6"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 4", ["Mankey", "Meowth"], rom_addresses["Wild_Route6"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 5", ["Mankey", "Meowth"], rom_addresses["Wild_Route6"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 6", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route6"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route6"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 8", "Pidgey", rom_addresses["Wild_Route6"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 9", ["Mankey", "Meowth"], rom_addresses["Wild_Route6"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 6", "Wild Pokemon - 10", ["Mankey", "Meowth"], rom_addresses["Wild_Route6"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 1", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route11"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route11"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 3", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route11"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 4", "Drowzee", rom_addresses["Wild_Route11"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route11"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 6", "Drowzee", rom_addresses["Wild_Route11"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 7", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route11"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route11"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 9", "Drowzee", rom_addresses["Wild_Route11"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 11", "Wild Pokemon - 10", "Drowzee", rom_addresses["Wild_Route11"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 1", "Zubat", rom_addresses["Wild_RockTunnel1F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 2", "Zubat", rom_addresses["Wild_RockTunnel1F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 3", "Geodude", rom_addresses["Wild_RockTunnel1F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 4", "Machop", rom_addresses["Wild_RockTunnel1F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 5", "Geodude", rom_addresses["Wild_RockTunnel1F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 6", "Zubat", rom_addresses["Wild_RockTunnel1F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 7", "Zubat", rom_addresses["Wild_RockTunnel1F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 8", "Machop", rom_addresses["Wild_RockTunnel1F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 9", "Onix", rom_addresses["Wild_RockTunnel1F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel 1F", "Wild Pokemon - 10", "Onix", rom_addresses["Wild_RockTunnel1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 1", "Zubat", rom_addresses["Wild_RockTunnelB1F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 2", "Zubat", rom_addresses["Wild_RockTunnelB1F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 3", "Geodude", rom_addresses["Wild_RockTunnelB1F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 4", "Machop", rom_addresses["Wild_RockTunnelB1F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 5", "Geodude", rom_addresses["Wild_RockTunnelB1F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 6", "Zubat", rom_addresses["Wild_RockTunnelB1F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 7", "Machop", rom_addresses["Wild_RockTunnelB1F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 8", "Onix", rom_addresses["Wild_RockTunnelB1F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 9", "Onix", rom_addresses["Wild_RockTunnelB1F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Rock Tunnel B1F", "Wild Pokemon - 10", "Geodude", rom_addresses["Wild_RockTunnelB1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 1", "Voltorb", rom_addresses["Wild_Route10"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route10"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 3", "Voltorb", rom_addresses["Wild_Route10"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 4", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route10"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route10"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 6", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route10"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 7", "Voltorb", rom_addresses["Wild_Route10"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route10"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 9", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route10"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Route 10 North", "Wild Pokemon - 10", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route10"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route12"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route12"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route12"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route12"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route12"] + 9, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route12"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route12"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 8", "Pidgey", rom_addresses["Wild_Route12"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 9", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route12"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Route 12 Grass", "Wild Pokemon - 10", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route12"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 1", "Pidgey", rom_addresses["Wild_Route8"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 2", ["Mankey", "Meowth"], rom_addresses["Wild_Route8"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 3", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route8"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 4", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route8"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 5", "Pidgey", rom_addresses["Wild_Route8"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 6", ["Mankey", "Meowth"], rom_addresses["Wild_Route8"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 7", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route8"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 8", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route8"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 9", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route8"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 8 Grass", "Wild Pokemon - 10", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route8"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 1", "Pidgey", rom_addresses["Wild_Route7"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 2", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route7"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 3", ["Mankey", "Meowth"], rom_addresses["Wild_Route7"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 4", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route7"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 5", "Pidgey", rom_addresses["Wild_Route7"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 6", ["Mankey", "Meowth"], rom_addresses["Wild_Route7"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 7", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route7"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 8", ["Growlithe", "Vulpix"], rom_addresses["Wild_Route7"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 9", ["Mankey", "Meowth"], rom_addresses["Wild_Route7"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 7", "Wild Pokemon - 10", ["Mankey", "Meowth"], rom_addresses["Wild_Route7"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 1", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 2", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 3", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 4", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 5", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 6", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 7", "Gastly", rom_addresses["Wild_PokemonTower3F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 8", "Cubone", rom_addresses["Wild_PokemonTower3F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 9", "Cubone", rom_addresses["Wild_PokemonTower3F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 3F", "Wild Pokemon - 10", "Haunter", rom_addresses["Wild_PokemonTower3F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 1", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 2", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 3", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 4", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 5", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 6", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 7", "Haunter", rom_addresses["Wild_PokemonTower4F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 8", "Cubone", rom_addresses["Wild_PokemonTower4F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 9", "Cubone", rom_addresses["Wild_PokemonTower4F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 4F", "Wild Pokemon - 10", "Gastly", rom_addresses["Wild_PokemonTower4F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 1", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 2", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 3", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 4", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 5", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 6", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 7", "Haunter", rom_addresses["Wild_PokemonTower5F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 8", "Cubone", rom_addresses["Wild_PokemonTower5F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 9", "Cubone", rom_addresses["Wild_PokemonTower5F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 5F", "Wild Pokemon - 10", "Gastly", rom_addresses["Wild_PokemonTower5F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 1", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 2", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 3", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 4", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 5", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 6", "Gastly", rom_addresses["Wild_PokemonTower6F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 7", "Haunter", rom_addresses["Wild_PokemonTower6F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 8", "Cubone", rom_addresses["Wild_PokemonTower6F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 9", "Cubone", rom_addresses["Wild_PokemonTower6F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 6F", "Wild Pokemon - 10", "Haunter", rom_addresses["Wild_PokemonTower6F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 1", "Gastly", rom_addresses["Wild_PokemonTower7F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 2", "Gastly", rom_addresses["Wild_PokemonTower7F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 3", "Gastly", rom_addresses["Wild_PokemonTower7F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 4", "Gastly", rom_addresses["Wild_PokemonTower7F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 5", "Gastly", rom_addresses["Wild_PokemonTower7F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 6", "Haunter", rom_addresses["Wild_PokemonTower7F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 7", "Cubone", rom_addresses["Wild_PokemonTower7F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 8", "Cubone", rom_addresses["Wild_PokemonTower7F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 9", "Haunter", rom_addresses["Wild_PokemonTower7F"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Tower 7F", "Wild Pokemon - 10", "Haunter", rom_addresses["Wild_PokemonTower7F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route13"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route13"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route13"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route13"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route13"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route13"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route13"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 8", "Ditto", rom_addresses["Wild_Route13"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 9", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route13"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 13", "Wild Pokemon - 10", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route13"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route14"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 3", "Ditto", rom_addresses["Wild_Route14"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route14"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route14"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 8", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route14"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 9", "Pidgeotto", rom_addresses["Wild_Route14"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 14", "Wild Pokemon - 10", "Pidgeotto", rom_addresses["Wild_Route14"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route15"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 2", "Ditto", rom_addresses["Wild_Route15"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 3", "Pidgey", rom_addresses["Wild_Route15"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route15"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route15"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route15"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route15"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 8", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route15"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 9", "Pidgeotto", rom_addresses["Wild_Route15"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 15", "Wild Pokemon - 10", "Pidgeotto", rom_addresses["Wild_Route15"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 1", "Spearow", rom_addresses["Wild_Route16"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route16"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route16"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 4", "Doduo", rom_addresses["Wild_Route16"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 5", "Rattata", rom_addresses["Wild_Route16"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 6", "Doduo", rom_addresses["Wild_Route16"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 7", "Doduo", rom_addresses["Wild_Route16"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 8", "Rattata", rom_addresses["Wild_Route16"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 9", "Raticate", rom_addresses["Wild_Route16"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 16 North", "Wild Pokemon - 10", "Raticate", rom_addresses["Wild_Route16"] + 19, None, + event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 1", "Spearow", rom_addresses["Wild_Route17"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route17"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 3", "Raticate", rom_addresses["Wild_Route17"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 4", "Doduo", rom_addresses["Wild_Route17"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 5", "Raticate", rom_addresses["Wild_Route17"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 6", "Doduo", rom_addresses["Wild_Route17"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 7", "Doduo", rom_addresses["Wild_Route17"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 8", "Raticate", rom_addresses["Wild_Route17"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 9", "Fearow", rom_addresses["Wild_Route17"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 17", "Wild Pokemon - 10", "Fearow", rom_addresses["Wild_Route17"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 1", "Spearow", rom_addresses["Wild_Route18"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route18"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 3", "Raticate", rom_addresses["Wild_Route18"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 4", "Doduo", rom_addresses["Wild_Route18"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 5", "Fearow", rom_addresses["Wild_Route18"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 6", "Doduo", rom_addresses["Wild_Route18"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 7", "Doduo", rom_addresses["Wild_Route18"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 8", "Raticate", rom_addresses["Wild_Route18"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 9", "Fearow", rom_addresses["Wild_Route18"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 18", "Wild Pokemon - 10", "Fearow", rom_addresses["Wild_Route18"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 1", ["Nidoran M", "Nidoran F"], + rom_addresses["Wild_SafariZoneCenter"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 2", "Rhyhorn", rom_addresses["Wild_SafariZoneCenter"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 3", "Venonat", rom_addresses["Wild_SafariZoneCenter"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 4", "Exeggcute", rom_addresses["Wild_SafariZoneCenter"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 5", ["Nidorino", "Nidorina"], + rom_addresses["Wild_SafariZoneCenter"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 6", "Exeggcute", rom_addresses["Wild_SafariZoneCenter"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 7", ["Nidorina", "Nidorino"], + rom_addresses["Wild_SafariZoneCenter"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 8", "Parasect", rom_addresses["Wild_SafariZoneCenter"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 9", ["Scyther", "Pinsir"], + rom_addresses["Wild_SafariZoneCenter"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone Center", "Wild Pokemon - 10", "Chansey", rom_addresses["Wild_SafariZoneCenter"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 1", ["Nidoran M", "Nidoran F"], + rom_addresses["Wild_SafariZoneEast"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 2", "Doduo", rom_addresses["Wild_SafariZoneEast"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 3", "Paras", rom_addresses["Wild_SafariZoneEast"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 4", "Exeggcute", rom_addresses["Wild_SafariZoneEast"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 5", ["Nidorino", "Nidorina"], + rom_addresses["Wild_SafariZoneEast"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 6", "Exeggcute", rom_addresses["Wild_SafariZoneEast"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 7", ["Nidoran F", "Nidoran M"], + rom_addresses["Wild_SafariZoneEast"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 8", "Parasect", rom_addresses["Wild_SafariZoneEast"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 9", "Kangaskhan", rom_addresses["Wild_SafariZoneEast"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone East", "Wild Pokemon - 10", ["Scyther", "Pinsir"], + rom_addresses["Wild_SafariZoneEast"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 1", ["Nidoran M", "Nidoran F"], + rom_addresses["Wild_SafariZoneNorth"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 2", "Rhyhorn", rom_addresses["Wild_SafariZoneNorth"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 3", "Paras", rom_addresses["Wild_SafariZoneNorth"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 4", "Exeggcute", rom_addresses["Wild_SafariZoneNorth"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 5", ["Nidorino", "Nidorina"], + rom_addresses["Wild_SafariZoneNorth"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 6", "Exeggcute", rom_addresses["Wild_SafariZoneNorth"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 7", ["Nidorina", "Nidorino"], + rom_addresses["Wild_SafariZoneNorth"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 8", "Venomoth", rom_addresses["Wild_SafariZoneNorth"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 9", "Chansey", rom_addresses["Wild_SafariZoneNorth"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone North", "Wild Pokemon - 10", "Tauros", rom_addresses["Wild_SafariZoneNorth"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 1", ["Nidoran M", "Nidoran F"], + rom_addresses["Wild_SafariZoneWest"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 2", "Doduo", rom_addresses["Wild_SafariZoneWest"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 3", "Venonat", rom_addresses["Wild_SafariZoneWest"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 4", "Exeggcute", rom_addresses["Wild_SafariZoneWest"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 5", ["Nidorino", "Nidorina"], + rom_addresses["Wild_SafariZoneWest"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 6", "Exeggcute", rom_addresses["Wild_SafariZoneWest"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 7", ["Nidoran F", "Nidoran M"], + rom_addresses["Wild_SafariZoneWest"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 8", "Venomoth", rom_addresses["Wild_SafariZoneWest"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 9", "Tauros", rom_addresses["Wild_SafariZoneWest"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Safari Zone West", "Wild Pokemon - 10", "Kangaskhan", rom_addresses["Wild_SafariZoneWest"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 1", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 2", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 3", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 4", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 5", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 6", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 7", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 8", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 9", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 20 West", "Surf Pokemon - 10", "Tentacool", rom_addresses["Wild_SeaRoutes"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 1", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 2", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 3", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 4", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 5", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 6", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 7", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 8", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 9", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 21", "Surf Pokemon - 10", "Tentacool", rom_addresses["Wild_Surf_Route21"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 1", "Seel", rom_addresses["Wild_SeafoamIslands1F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 2", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslands1F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 3", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslands1F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 4", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslands1F"] + 7, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 5", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslands1F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 6", "Zubat", rom_addresses["Wild_SeafoamIslands1F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 7", "Golbat", rom_addresses["Wild_SeafoamIslands1F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 8", ["Psyduck", "Slowpoke"], + rom_addresses["Wild_SeafoamIslands1F"] + 15, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 9", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslands1F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands 1F", "Wild Pokemon - 10", ["Golduck", "Slowbro"], + rom_addresses["Wild_SeafoamIslands1F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 1", ["Staryu", "Shellder"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 2", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 3", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 4", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 7, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 5", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 6", "Seel", rom_addresses["Wild_SeafoamIslandsB1F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 7", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 8", "Seel", rom_addresses["Wild_SeafoamIslandsB1F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 9", "Dewgong", rom_addresses["Wild_SeafoamIslandsB1F"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B1F", "Wild Pokemon - 10", ["Seadra", "Kingler"], + rom_addresses["Wild_SeafoamIslandsB1F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 1", "Seel", rom_addresses["Wild_SeafoamIslandsB2F"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 2", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 3", "Seel", rom_addresses["Wild_SeafoamIslandsB2F"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 4", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 7, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 5", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 6", ["Staryu", "Shellder"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 7", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 8", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 15, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 9", "Golbat", rom_addresses["Wild_SeafoamIslandsB2F"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B2F", "Wild Pokemon - 10", ["Slowbro", "Golduck"], + rom_addresses["Wild_SeafoamIslandsB2F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 1", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 2", "Seel", rom_addresses["Wild_SeafoamIslandsB3F"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 3", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 4", "Seel", rom_addresses["Wild_SeafoamIslandsB3F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 5", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 6", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 7", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 8", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 15, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 9", ["Seadra", "Kingler"], + rom_addresses["Wild_SeafoamIslandsB3F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B3F", "Wild Pokemon - 10", "Dewgong", + rom_addresses["Wild_SeafoamIslandsB3F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 1", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 2", ["Shellder", "Staryu"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 3", ["Horsea", "Krabby"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 4", "Shellder", rom_addresses["Wild_SeafoamIslandsB4F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 5", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 6", "Seel", rom_addresses["Wild_SeafoamIslandsB4F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 7", ["Slowpoke", "Psyduck"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 8", "Seel", rom_addresses["Wild_SeafoamIslandsB4F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 9", ["Slowbro", "Golduck"], + rom_addresses["Wild_SeafoamIslandsB4F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Seafoam Islands B4F", "Wild Pokemon - 10", "Golbat", rom_addresses["Wild_SeafoamIslandsB4F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 1", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion1F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 2", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion1F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 3", "Ponyta", rom_addresses["Wild_PokemonMansion1F"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 4", "Ponyta", rom_addresses["Wild_PokemonMansion1F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 5", ["Growlithe", "Vulpix"], + rom_addresses["Wild_PokemonMansion1F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 6", "Ponyta", rom_addresses["Wild_PokemonMansion1F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 7", ["Grimer", "Koffing"], + rom_addresses["Wild_PokemonMansion1F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 8", "Ponyta", rom_addresses["Wild_PokemonMansion1F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 9", ["Weezing", "Muk"], + rom_addresses["Wild_PokemonMansion1F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 1F", "Wild Pokemon - 10", ["Muk", "Weezing"], + rom_addresses["Wild_PokemonMansion1F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 1", ["Growlithe", "Vulpix"], + rom_addresses["Wild_PokemonMansion2F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 2", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion2F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 3", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion2F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 4", "Ponyta", rom_addresses["Wild_PokemonMansion2F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 5", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion2F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 6", "Ponyta", rom_addresses["Wild_PokemonMansion2F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 7", ["Grimer", "Koffing"], + rom_addresses["Wild_PokemonMansion2F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 8", "Ponyta", rom_addresses["Wild_PokemonMansion2F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 9", ["Weezing", "Muk"], + rom_addresses["Wild_PokemonMansion2F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 2F", "Wild Pokemon - 10", ["Muk", "Weezing"], + rom_addresses["Wild_PokemonMansion2F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 1", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion3F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 2", ["Growlithe", "Vulpix"], + rom_addresses["Wild_PokemonMansion3F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 3", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansion3F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 4", "Ponyta", rom_addresses["Wild_PokemonMansion3F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 5", ["Ponyta", "Magmar"], + rom_addresses["Wild_PokemonMansion3F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 6", ["Weezing", "Muk"], + rom_addresses["Wild_PokemonMansion3F"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 7", ["Grimer", "Koffing"], + rom_addresses["Wild_PokemonMansion3F"] + 13, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 8", ["Weezing", "Muk"], + rom_addresses["Wild_PokemonMansion3F"] + 15, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 9", "Ponyta", rom_addresses["Wild_PokemonMansion3F"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion 3F", "Wild Pokemon - 10", ["Muk", "Weezing"], + rom_addresses["Wild_PokemonMansion3F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 1", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansionB1F"] + 1, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 2", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansionB1F"] + 3, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 3", ["Growlithe", "Vulpix"], + rom_addresses["Wild_PokemonMansionB1F"] + 5, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 4", "Ponyta", rom_addresses["Wild_PokemonMansionB1F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 5", ["Koffing", "Grimer"], + rom_addresses["Wild_PokemonMansionB1F"] + 9, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 6", ["Weezing", "Muk"], + rom_addresses["Wild_PokemonMansionB1F"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 7", "Ponyta", rom_addresses["Wild_PokemonMansionB1F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 8", ["Grimer", "Koffing"], + rom_addresses["Wild_PokemonMansionB1F"] + 15, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 9", ["Weezing", "Magmar"], + rom_addresses["Wild_PokemonMansionB1F"] + 17, None, event=True, type="Wild Encounter"), + LocationData("Pokemon Mansion B1F", "Wild Pokemon - 10", ["Muk", "Weezing"], + rom_addresses["Wild_PokemonMansionB1F"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route21"] + 1, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route21"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 3", "Raticate", rom_addresses["Wild_Route21"] + 5, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 4", "Rattata", rom_addresses["Wild_Route21"] + 7, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 5", "Pidgey", rom_addresses["Wild_Route21"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 6", "Pidgeotto", rom_addresses["Wild_Route21"] + 11, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 7", "Pidgeotto", rom_addresses["Wild_Route21"] + 13, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 8", "Tangela", rom_addresses["Wild_Route21"] + 15, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 9", "Tangela", rom_addresses["Wild_Route21"] + 17, None, event=True, + type="Wild Encounter"), + LocationData("Route 21", "Wild Pokemon - 10", "Tangela", rom_addresses["Wild_Route21"] + 19, None, event=True, + type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 1", "Golbat", rom_addresses["Wild_CeruleanCave1F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 2", "Hypno", rom_addresses["Wild_CeruleanCave1F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 3", "Magneton", rom_addresses["Wild_CeruleanCave1F"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 4", "Dodrio", rom_addresses["Wild_CeruleanCave1F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 5", "Venomoth", rom_addresses["Wild_CeruleanCave1F"] + 9, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 6", ["Arbok", "Sandslash"], + rom_addresses["Wild_CeruleanCave1F"] + 11, None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 7", "Kadabra", rom_addresses["Wild_CeruleanCave1F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 8", "Parasect", rom_addresses["Wild_CeruleanCave1F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 9", "Raichu", rom_addresses["Wild_CeruleanCave1F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 1F", "Wild Pokemon - 10", "Ditto", rom_addresses["Wild_CeruleanCave1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 1", "Dodrio", rom_addresses["Wild_CeruleanCave2F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 2", "Venomoth", rom_addresses["Wild_CeruleanCave2F"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 3", "Kadabra", rom_addresses["Wild_CeruleanCave2F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 4", "Rhydon", rom_addresses["Wild_CeruleanCave2F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 5", "Marowak", rom_addresses["Wild_CeruleanCave2F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 6", "Electrode", rom_addresses["Wild_CeruleanCave2F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 7", "Chansey", rom_addresses["Wild_CeruleanCave2F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 8", "Wigglytuff", rom_addresses["Wild_CeruleanCave2F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 9", "Ditto", rom_addresses["Wild_CeruleanCave2F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave 2F", "Wild Pokemon - 10", "Ditto", rom_addresses["Wild_CeruleanCave2F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 1", "Rhydon", rom_addresses["Wild_CeruleanCaveB1F"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 2", "Marowak", rom_addresses["Wild_CeruleanCaveB1F"] + 3, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 3", "Electrode", rom_addresses["Wild_CeruleanCaveB1F"] + 5, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 4", "Chansey", rom_addresses["Wild_CeruleanCaveB1F"] + 7, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 5", "Parasect", rom_addresses["Wild_CeruleanCaveB1F"] + 9, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 6", "Raichu", rom_addresses["Wild_CeruleanCaveB1F"] + 11, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 7", ["Arbok", "Sandslash"], + rom_addresses["Wild_CeruleanCaveB1F"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 8", "Ditto", rom_addresses["Wild_CeruleanCaveB1F"] + 15, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 9", "Ditto", rom_addresses["Wild_CeruleanCaveB1F"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Cerulean Cave B1F", "Wild Pokemon - 10", "Ditto", rom_addresses["Wild_CeruleanCaveB1F"] + 19, + None, event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 1", "Voltorb", rom_addresses["Wild_PowerPlant"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 2", "Magnemite", rom_addresses["Wild_PowerPlant"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 3", "Pikachu", rom_addresses["Wild_PowerPlant"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 4", "Pikachu", rom_addresses["Wild_PowerPlant"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 5", "Magnemite", rom_addresses["Wild_PowerPlant"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 6", "Voltorb", rom_addresses["Wild_PowerPlant"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 7", "Magneton", rom_addresses["Wild_PowerPlant"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 8", "Magneton", rom_addresses["Wild_PowerPlant"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 9", ["Electabuzz", "Raichu"], rom_addresses["Wild_PowerPlant"] + 17, + None, event=True, type="Wild Encounter"), + LocationData("Power Plant", "Wild Pokemon - 10", ["Electabuzz", "Raichu"], + rom_addresses["Wild_PowerPlant"] + 19, None, event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 1", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route23"] + 1, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 2", "Ditto", rom_addresses["Wild_Route23"] + 3, None, event=True, + type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 3", "Spearow", rom_addresses["Wild_Route23"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 4", "Fearow", rom_addresses["Wild_Route23"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 5", "Ditto", rom_addresses["Wild_Route23"] + 9, None, event=True, + type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 6", "Fearow", rom_addresses["Wild_Route23"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 7", ["Arbok", "Sandslash"], rom_addresses["Wild_Route23"] + 13, + None, event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 8", "Ditto", rom_addresses["Wild_Route23"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 9", "Fearow", rom_addresses["Wild_Route23"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Route 23 North", "Wild Pokemon - 10", "Fearow", rom_addresses["Wild_Route23"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 1", "Machop", rom_addresses["Wild_VictoryRoad2F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 2", "Geodude", rom_addresses["Wild_VictoryRoad2F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 3", "Zubat", rom_addresses["Wild_VictoryRoad2F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 4", "Onix", rom_addresses["Wild_VictoryRoad2F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 5", "Onix", rom_addresses["Wild_VictoryRoad2F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 6", "Onix", rom_addresses["Wild_VictoryRoad2F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 7", "Machoke", rom_addresses["Wild_VictoryRoad2F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 8", "Golbat", rom_addresses["Wild_VictoryRoad2F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 9", "Marowak", rom_addresses["Wild_VictoryRoad2F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 2F", "Wild Pokemon - 10", "Graveler", rom_addresses["Wild_VictoryRoad2F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 1", "Machop", rom_addresses["Wild_VictoryRoad3F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 2", "Geodude", rom_addresses["Wild_VictoryRoad3F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 3", "Zubat", rom_addresses["Wild_VictoryRoad3F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 4", "Onix", rom_addresses["Wild_VictoryRoad3F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 5", "Venomoth", rom_addresses["Wild_VictoryRoad3F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 6", "Onix", rom_addresses["Wild_VictoryRoad3F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 7", "Graveler", rom_addresses["Wild_VictoryRoad3F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 8", "Golbat", rom_addresses["Wild_VictoryRoad3F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 9", "Machoke", rom_addresses["Wild_VictoryRoad3F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 3F", "Wild Pokemon - 10", "Machoke", rom_addresses["Wild_VictoryRoad3F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 1", "Machop", rom_addresses["Wild_VictoryRoad1F"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 2", "Geodude", rom_addresses["Wild_VictoryRoad1F"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 3", "Zubat", rom_addresses["Wild_VictoryRoad1F"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 4", "Onix", rom_addresses["Wild_VictoryRoad1F"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 5", "Onix", rom_addresses["Wild_VictoryRoad1F"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 6", "Onix", rom_addresses["Wild_VictoryRoad1F"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 7", "Graveler", rom_addresses["Wild_VictoryRoad1F"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 8", "Golbat", rom_addresses["Wild_VictoryRoad1F"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 9", "Machoke", rom_addresses["Wild_VictoryRoad1F"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Victory Road 1F", "Wild Pokemon - 10", "Marowak", rom_addresses["Wild_VictoryRoad1F"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 1", "Diglett", rom_addresses["Wild_DiglettsCave"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 2", "Diglett", rom_addresses["Wild_DiglettsCave"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 3", "Diglett", rom_addresses["Wild_DiglettsCave"] + 5, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 4", "Diglett", rom_addresses["Wild_DiglettsCave"] + 7, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 5", "Diglett", rom_addresses["Wild_DiglettsCave"] + 9, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 6", "Diglett", rom_addresses["Wild_DiglettsCave"] + 11, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 7", "Diglett", rom_addresses["Wild_DiglettsCave"] + 13, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 8", "Diglett", rom_addresses["Wild_DiglettsCave"] + 15, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 9", "Dugtrio", rom_addresses["Wild_DiglettsCave"] + 17, None, + event=True, type="Wild Encounter"), + LocationData("Diglett's Cave", "Wild Pokemon - 10", "Dugtrio", rom_addresses["Wild_DiglettsCave"] + 19, None, + event=True, type="Wild Encounter"), + LocationData("Anywhere", "Good Rod Pokemon - 1", "Goldeen", rom_addresses["Wild_Good_Rod"] + 1, None, + event=True, type="Wild Encounter"), + LocationData("Anywhere", "Good Rod Pokemon - 2", "Poliwag", rom_addresses["Wild_Good_Rod"] + 3, None, + event=True, type="Wild Encounter"), + LocationData("Anywhere", "Old Rod Pokemon", "Magikarp", rom_addresses["Wild_Old_Rod"] + 1, None, + event=True, type="Wild Encounter"), + + # "Wild encounters" means a Pokemon you cannot miss or release and lose - re-use it for these + LocationData("Celadon Prize Corner", "Pokemon Prize - 1", "Abra", [rom_addresses["Prize_Mon_A"], + rom_addresses["Prize_Mon_A2"]], None, event=True, + type="Wild Encounter"), + LocationData("Celadon Prize Corner", "Pokemon Prize - 2", "Clefairy", [rom_addresses["Prize_Mon_B"], + rom_addresses["Prize_Mon_B2"]], None, + event=True, type="Wild Encounter"), + LocationData("Celadon Prize Corner", "Pokemon Prize - 3", ["Nidorina", "Nidorino"], [rom_addresses["Prize_Mon_C"], + rom_addresses["Prize_Mon_C2"]], + None, + event=True, type="Wild Encounter"), + LocationData("Celadon Prize Corner", "Pokemon Prize - 4", ["Dratini", "Pinsir"], [rom_addresses["Prize_Mon_D"], + rom_addresses["Prize_Mon_D2"]], + None, event=True, type="Wild Encounter"), + LocationData("Celadon Prize Corner", "Pokemon Prize - 5", ["Scyther", "Dratini"], [rom_addresses["Prize_Mon_E"], + rom_addresses["Prize_Mon_E2"]], + None, event=True, type="Wild Encounter"), + LocationData("Celadon Prize Corner", "Pokemon Prize - 6", "Porygon", [rom_addresses["Prize_Mon_F"], + rom_addresses["Prize_Mon_F2"]], None, + event=True, type="Wild Encounter"), + + # counted for pokedex, because they cannot be permanently "missed" but not for HMs since they can be released + # or evolved forms may not be able to learn the same HMs + LocationData("Celadon City", "Celadon Mansion Pokemon", "Eevee", rom_addresses["Gift_Eevee"], None, + event=True, type="Static Pokemon"), + LocationData("Silph Co 7F", "Gift Pokemon", "Lapras", rom_addresses["Gift_Lapras"], None, + event=True, type="Static Pokemon"), + LocationData("Route 3", "Pokemon For Sale", "Magikarp", rom_addresses["Gift_Magikarp"], None, + event=True, type="Static Pokemon"), + LocationData("Cinnabar Island", "Old Amber Pokemon", "Aerodactyl", rom_addresses["Gift_Aerodactyl"], None, + event=True, type="Static Pokemon"), + LocationData("Cinnabar Island", "Helix Fossil Pokemon", "Omanyte", rom_addresses["Gift_Omanyte"], None, + event=True, type="Static Pokemon"), + LocationData("Cinnabar Island", "Dome Fossil Pokemon", "Kabuto", rom_addresses["Gift_Kabuto"], None, + event=True, type="Static Pokemon"), + + # not counted for logic currently. Could perhaps make static encounters resettable in the future? + LocationData("Power Plant", "Fake Pokeball Battle 1", "Voltorb", rom_addresses["Static_Encounter_Voltorb_A"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 2", "Voltorb", rom_addresses["Static_Encounter_Voltorb_B"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 3", "Voltorb", rom_addresses["Static_Encounter_Voltorb_C"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 4", "Voltorb", rom_addresses["Static_Encounter_Voltorb_D"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 5", "Voltorb", rom_addresses["Static_Encounter_Voltorb_E"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 6", "Voltorb", rom_addresses["Static_Encounter_Voltorb_F"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 7", "Voltorb", rom_addresses["Static_Encounter_Electrode_A"], + None, event=True, type="Missable Pokemon"), + LocationData("Power Plant", "Fake Pokeball Battle 8", "Voltorb", rom_addresses["Static_Encounter_Electrode_B"], + None, event=True, type="Missable Pokemon"), + + LocationData("Pokemon Tower 6F", "Restless Soul", "Marowak", [rom_addresses["Ghost_Battle1"], + rom_addresses["Ghost_Battle2"], + rom_addresses["Ghost_Battle3"], + rom_addresses["Ghost_Battle4"], + rom_addresses["Ghost_Battle5"], + rom_addresses["Ghost_Battle6"]], None, event=True, + type="Missable Pokemon"), + + LocationData("Route 12 West", "Sleeping Pokemon", "Snorlax", rom_addresses["Static_Encounter_Snorlax_A"], + None, event=True, type="Missable Pokemon"), + LocationData("Route 16", "Sleeping Pokemon", "Snorlax", rom_addresses["Static_Encounter_Snorlax_B"], + None, event=True, type="Missable Pokemon"), + + LocationData("Saffron City", "Fighting Dojo Gift 1", "Hitmonlee", rom_addresses["Gift_Hitmonlee"], + None, event=True, type="Missable Pokemon"), + LocationData("Saffron City", "Fighting Dojo Gift 2", "Hitmonchan", rom_addresses["Gift_Hitmonchan"], + None, event=True, type="Missable Pokemon"), + + LocationData("Pallet Town", "Starter 1", "Charmander", [rom_addresses["Starter1_A"], + rom_addresses["Starter1_B"], rom_addresses["Starter1_C"], + rom_addresses["Starter1_D"], + rom_addresses["Starter1_F"], rom_addresses["Starter1_H"]], + None, event=True, + type="Starter Pokemon"), + + LocationData("Pallet Town", "Starter 2", "Squirtle", [rom_addresses["Starter2_A"], + rom_addresses["Starter2_B"], rom_addresses["Starter2_E"], + rom_addresses["Starter2_F"], + rom_addresses["Starter2_G"], rom_addresses["Starter2_H"], + rom_addresses["Starter2_I"], + rom_addresses["Starter2_J"], rom_addresses["Starter2_K"], + rom_addresses["Starter2_L"], + rom_addresses["Starter2_M"], rom_addresses["Starter2_N"], + rom_addresses["Starter2_O"]], None, + event=True, type="Starter Pokemon"), + + LocationData("Pallet Town", "Starter 3", "Bulbasaur", [rom_addresses["Starter3_A"], + rom_addresses["Starter3_B"], rom_addresses["Starter3_C"], + rom_addresses["Starter3_D"], + rom_addresses["Starter3_E"], rom_addresses["Starter3_G"], + rom_addresses["Starter3_I"], + rom_addresses["Starter3_J"], rom_addresses["Starter3_K"], + rom_addresses["Starter3_L"], + rom_addresses["Starter3_M"], rom_addresses["Starter3_N"], + rom_addresses["Starter3_O"]], None, + event=True, type="Starter Pokemon"), + + LocationData("Power Plant", "Legendary Pokemon", "Zapdos", rom_addresses["Static_Encounter_Zapdos"], + None, event=True, type="Legendary Pokemon"), + LocationData("Seafoam Islands B4F", "Legendary Pokemon", "Articuno", rom_addresses["Static_Encounter_Articuno"], + None, event=True, type="Legendary Pokemon"), + LocationData("Victory Road 2F", "Legendary Pokemon", "Moltres", rom_addresses["Static_Encounter_Moltres"], + None, event=True, type="Legendary Pokemon"), + LocationData("Cerulean Cave B1F", "Legendary Pokemon", "Mewtwo", rom_addresses["Static_Encounter_Mewtwo"], + None, event=True, type="Legendary Pokemon"), + LocationData("Vermilion City", "Legendary Pokemon", "Mew", rom_addresses["Static_Encounter_Mew"], + None, event=True, type="Legendary Pokemon"), +] + +for i, location in enumerate(location_data): + if location.event or location.rom_address is None: + location.address = None + else: + location.address = loc_id_start + i + + + +class PokemonRBLocation(Location): + game = "Pokemon Red and Blue" + + def __init__(self, player, name, address, rom_address): + super(PokemonRBLocation, self).__init__( + player, name, + address + ) + self.rom_address = rom_address \ No newline at end of file diff --git a/worlds/pokemon_rb/logic.py b/worlds/pokemon_rb/logic.py new file mode 100644 index 00000000..3b1a3594 --- /dev/null +++ b/worlds/pokemon_rb/logic.py @@ -0,0 +1,73 @@ +from ..AutoWorld import LogicMixin +import worlds.pokemon_rb.poke_data as poke_data + + +class PokemonLogic(LogicMixin): + def pokemon_rb_can_surf(self, player): + return (((self.has("HM03 Surf", player) and self.can_learn_hm("10000", player)) + or self.has("Flippers", player)) and (self.has("Soul Badge", player) or + self.has(self.world.worlds[player].extra_badges.get("Surf"), player) + or self.world.badges_needed_for_hm_moves[player].value == 0)) + + def pokemon_rb_can_cut(self, player): + return ((self.has("HM01 Cut", player) and self.can_learn_hm("100", player) or self.has("Master Sword", player)) + and (self.has("Cascade Badge", player) or + self.has(self.world.worlds[player].extra_badges.get("Cut"), player) or + self.world.badges_needed_for_hm_moves[player].value == 0)) + + def pokemon_rb_can_fly(self, player): + return (((self.has("HM02 Fly", player) and self.can_learn_hm("1000", player)) or self.has("Flute", player)) and + (self.has("Thunder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Fly"), player) + or self.world.badges_needed_for_hm_moves[player].value == 0)) + + def pokemon_rb_can_strength(self, player): + return ((self.has("HM04 Strength", player) and self.can_learn_hm("100000", player)) or + self.has("Titan's Mitt", player)) and (self.has("Rainbow Badge", player) or + self.has(self.world.worlds[player].extra_badges.get("Strength"), player) + or self.world.badges_needed_for_hm_moves[player].value == 0) + + def pokemon_rb_can_flash(self, player): + return (((self.has("HM05 Flash", player) and self.can_learn_hm("1000000", player)) or self.has("Lamp", player)) + and (self.has("Boulder Badge", player) or self.has(self.world.worlds[player].extra_badges.get("Flash"), + player) or self.world.badges_needed_for_hm_moves[player].value == 0)) + + def can_learn_hm(self, move, player): + for pokemon, data in self.world.worlds[player].local_poke_data.items(): + if self.has(pokemon, player) and data["tms"][6] & int(move, 2): + return True + return False + + def pokemon_rb_can_get_hidden_items(self, player): + return self.has("Item Finder", player) or not self.world.require_item_finder[player].value + + def pokemon_rb_cerulean_cave(self, count, player): + return len([item for item in + ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Soul Badge", "Marsh Badge", + "Volcano Badge", "Earth Badge", "Bicycle", "Silph Scope", "Item Finder", "Super Rod", "Good Rod", + "Old Rod", "Lift Key", "Card Key", "Town Map", "Coin Case", "S.S. Ticket", "Secret Key", + "Mansion Key", "Safari Pass", "Plant Key", "Hideout Key", "HM01 Cut", "HM02 Fly", "HM03 Surf", + "HM04 Strength", "HM05 Flash"] if self.has(item, player)]) >= count + + def pokemon_rb_can_pass_guards(self, player): + if self.world.tea[player].value: + return self.has("Tea", player) + else: + # this could just be "True", but you never know what weird options I might introduce later ;) + return self.can_reach("Celadon City - Counter Man", "Location", player) + + def pokemon_rb_has_badges(self, count, player): + return len([item for item in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge", + "Soul Badge", "Volcano Badge", "Earth Badge"] if self.has(item, player)]) >= count + + def pokemon_rb_has_pokemon(self, count, player): + obtained_pokemon = set() + for pokemon in poke_data.pokemon_data.keys(): + if self.has(pokemon, player) or self.has(f"Static {pokemon}", player): + obtained_pokemon.add(pokemon) + + return len(obtained_pokemon) >= count + + def pokemon_rb_fossil_checks(self, count, player): + return (self.can_reach('Mt Moon 1F - Southwest Item', 'Location', player) and + self.can_reach('Cinnabar Island - Lab Scientist', 'Location', player) and len( + [item for item in ["Dome Fossil", "Helix Fossil", "Old Amber"] if self.has(item, player)]) >= count) diff --git a/worlds/pokemon_rb/options.py b/worlds/pokemon_rb/options.py new file mode 100644 index 00000000..37672c25 --- /dev/null +++ b/worlds/pokemon_rb/options.py @@ -0,0 +1,481 @@ + +from Options import Toggle, Choice, Range, SpecialRange, FreeText, TextChoice + + +class GameVersion(Choice): + """Select Red or Blue version.""" + display_name = "Game Version" + option_red = 1 + option_blue = 0 + default = "random" + + +class TrainerName(FreeText): + """Your trainer name. Cannot exceed 7 characters. + See the setup guide on archipelago.gg for a list of allowed characters.""" + display_name = "Trainer Name" + default = "ASH" + + +class RivalName(FreeText): + """Your rival's name. Cannot exceed 7 characters. + See the setup guide on archipelago.gg for a list of allowed characters.""" + display_name = "Rival's Name" + default = "GARY" + + +class Goal(Choice): + """If Professor Oak is selected, your victory condition will require challenging and defeating Oak after becoming""" + """Champion and defeating or capturing the Pokemon at the end of Cerulean Cave.""" + display_name = "Goal" + option_pokemon_league = 0 + option_professor_oak = 1 + default = 0 + + +class EliteFourCondition(Range): + """Number of badges required to challenge the Elite Four once the Indigo Plateau has been reached. + Your rival will reveal the amount needed on the first Route 22 battle (after turning in Oak's Parcel).""" + display_name = "Elite Four Condition" + range_start = 0 + range_end = 8 + default = 8 + + +class VictoryRoadCondition(Range): + """Number of badges required to reach Victory Road.""" + display_name = "Victory Road Condition" + range_start = 0 + range_end = 8 + default = 8 + + +class ViridianGymCondition(Range): + """Number of badges required to enter Viridian Gym.""" + display_name = "Viridian Gym Condition" + range_start = 0 + range_end = 7 + default = 7 + + +class CeruleanCaveCondition(Range): + """Number of badges, HMs, and key items (not counting items you can lose) required to access Cerulean Cave.""" + """If extra_key_items is turned on, the number chosen will be increased by 4.""" + display_name = "Cerulean Cave Condition" + range_start = 0 + range_end = 25 + default = 20 + + +class SecondFossilCheckCondition(Range): + """After choosing one of the fossil location items, you can obtain the remaining item from the Cinnabar Lab + Scientist after reviving this number of fossils.""" + display_name = "Second Fossil Check Condition" + range_start = 0 + range_end = 3 + default = 3 + + +class BadgeSanity(Toggle): + """Shuffle gym badges into the general item pool. If turned off, badges will be shuffled across the 8 gyms.""" + display_name = "Badgesanity" + default = 0 + + +class BadgesNeededForHMMoves(Choice): + """Off will remove the requirement for badges to use HM moves. Extra will give the Marsh, Volcano, and Earth + Badges a random HM move to enable. Extra Plus will additionally pick two random badges to enable a second HM move. + A man in Cerulean City will reveal the moves enabled by each Badge.""" + display_name = "Badges Needed For HM Moves" + default = 1 + option_on = 1 + alias_true = 1 + option_off = 0 + alias_false = 0 + option_extra = 2 + option_extra_plus = 3 + + +class OldMan(Choice): + """With Open Viridian City, the Old Man will let you through without needing to turn in Oak's Parcel.""" + """Early Parcel will ensure Oak's Parcel is available at the beginning of your game.""" + display_name = "Old Man" + option_vanilla = 0 + option_early_parcel = 1 + option_open_viridian_city = 2 + default = 1 + + +class Tea(Toggle): + """Adds a Tea item to the item pool which the Saffron guards require instead of the vending machine drinks. + Adds a location check to the Celadon Mansion 1F, where Tea is acquired in FireRed and LeafGreen.""" + display_name = "Tea" + default = 0 + + +class ExtraKeyItems(Toggle): + """Adds key items that are required to access the Rocket Hideout, Cinnabar Mansion, Safari Zone, and Power Plant. + Adds four item pickups to Rock Tunnel B1F.""" + display_name = "Extra Key Items" + default = 0 + + +class ExtraStrengthBoulders(Toggle): + """Adds Strength Boulders blocking the Route 11 gate, and in Route 13 (can be bypassed with Surf). + This potentially increases the usefulness of Strength as well as the Bicycle.""" + display_name = "Extra Strength Boulders" + default = 0 + + +class RequireItemFinder(Toggle): + """Require Item Finder to pick up hidden items.""" + display_name = "Require Item Finder" + default = 0 + + +class RandomizeHiddenItems(Choice): + """Randomize hidden items. If you choose exclude, they will be randomized but will be guaranteed junk items.""" + display_name = "Randomize Hidden Items" + option_on = 1 + option_off = 0 + alias_true = 1 + alias_false = 0 + option_exclude = 2 + default = 0 + + +class FreeFlyLocation(Toggle): + """One random fly destination will be unlocked by default.""" + display_name = "Free Fly Location" + default = 1 + + +class OaksAidRt2(Range): + """Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 2""" + display_name = "Oak's Aide Route 2" + range_start = 0 + range_end = 80 + default = 10 + + +class OaksAidRt11(Range): + """Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 11""" + display_name = "Oak's Aide Route 11" + range_start = 0 + range_end = 80 + default = 30 + + +class OaksAidRt15(Range): + """Number of Pokemon registered in the Pokedex required to receive the item from Oak's Aide on Route 15""" + display_name = "Oak's Aide Route 15" + range_start = 0 + range_end = 80 + default = 50 + + +class ExpModifier(SpecialRange): + """Modifier for EXP gained. When specifying a number, exp is multiplied by this amount and divided by 16.""" + display_name = "Exp Modifier" + range_start = 0 + range_end = 255 + default = 16 + special_range_names = { + "half": default / 2, + "normal": default, + "double": default * 2, + "triple": default * 3, + "quadruple": default * 4, + "quintuple": default * 5, + "sextuple": default * 6, + "septuple": default * 7, + "octuple": default * 8, + } + + +class RandomizeWildPokemon(Choice): + """Randomize all wild Pokemon and game corner prize Pokemon. match_types will select a Pokemon with at least one + type matching the original type of the original Pokemon. match_base_stats will prefer Pokemon with closer base stat + totals. match_types_and_base_stats will match types and will weight towards similar base stats, but there may not be + many to choose from.""" + display_name = "Randomize Wild Pokemon" + default = 0 + option_vanilla = 0 + option_match_types = 1 + option_match_base_stats = 2 + option_match_types_and_base_stats = 3 + option_completely_random = 4 + + +class RandomizeStarterPokemon(Choice): + """Randomize the starter Pokemon choices.""" + display_name = "Randomize Starter Pokemon" + default = 0 + option_vanilla = 0 + option_match_types = 1 + option_match_base_stats = 2 + option_match_types_and_base_stats = 3 + option_completely_random = 4 + + +class RandomizeStaticPokemon(Choice): + """Randomize all one-time gift and encountered Pokemon, except legendaries. + These will always be first evolution stage Pokemon.""" + display_name = "Randomize Static Pokemon" + default = 0 + option_vanilla = 0 + option_match_types = 1 + option_match_base_stats = 2 + option_match_types_and_base_stats = 3 + option_completely_random = 4 + + +class RandomizeLegendaryPokemon(Choice): + """Randomize Legendaries. Mew has been added as an encounter at the Vermilion dock truck. + Shuffle will shuffle the legendaries with each other. Static will shuffle them into other static Pokemon locations. + 'Any' will allow legendaries to appear anywhere based on wild and static randomization options, and their locations + will be randomized according to static Pokemon randomization options.""" + display_name = "Randomize Legendary Pokemon" + default = 0 + option_vanilla = 0 + option_shuffle = 1 + option_static = 2 + option_any = 3 + + +class CatchEmAll(Choice): + """Guarantee all first evolution stage Pokemon are available, or all Pokemon of all stages. + Currently only has an effect if wild Pokemon are randomized.""" + display_name = "Catch 'Em All" + default = 0 + option_off = 0 + alias_false = 0 + option_first_stage = 1 + option_all_pokemon = 2 + + +class RandomizeTrainerParties(Choice): + """Randomize enemy Pokemon encountered in trainer battles.""" + display_name = "Randomize Trainer Parties" + default = 0 + option_vanilla = 0 + option_match_types = 1 + option_match_base_stats = 2 + option_match_types_and_base_stats = 3 + option_completely_random = 4 + + +class TrainerLegendaries(Toggle): + """Allow legendary Pokemon in randomized trainer parties.""" + display_name = "Trainer Legendaries" + default = 0 + + +class BlindTrainers(Range): + """Chance each frame that you are standing on a tile in a trainer's line of sight that they will fail to initiate a + battle. If you move into and out of their line of sight without stopping, this chance will only trigger once.""" + display_name = "Blind Trainers" + range_start = 0 + range_end = 100 + default = 0 + + +class MinimumStepsBetweenEncounters(Range): + """Minimum number of steps between wild Pokemon encounters.""" + display_name = "Minimum Steps Between Encounters" + default = 3 + range_start = 0 + range_end = 255 + + +class RandomizePokemonStats(Choice): + """Randomize base stats for each Pokemon. Shuffle will shuffle the 5 base stat values amongst each other. Randomize + will completely randomize each stat, but will still add up to the same base stat total.""" + display_name = "Randomize Pokemon Stats" + default = 0 + option_vanilla = 0 + option_shuffle = 1 + option_randomize = 2 + + +class RandomizePokemonCatchRates(Toggle): + """Randomize the catch rate for each Pokemon.""" + display_name = "Randomize Catch Rates" + default = 0 + + +class MinimumCatchRate(Range): + """Minimum catch rate for each Pokemon. If randomize_catch_rates is on, this will be the minimum value that can be + chosen. Otherwise, it will raise any Pokemon's catch rate up to this value if its normal catch rate is lower.""" + display_name = "Minimum Catch Rate" + range_start = 1 + range_end = 255 + default = 3 + + +class RandomizePokemonMovesets(Choice): + """Randomize the moves learned by Pokemon. prefer_types will prefer moves that match the type of the Pokemon.""" + display_name = "Randomize Pokemon Movesets" + option_vanilla = 0 + option_prefer_types = 1 + option_completely_random = 2 + default = 0 + + +class StartWithFourMoves(Toggle): + """If movesets are randomized, this will give all Pokemon 4 starting moves.""" + display_name = "Start With Four Moves" + default = 0 + + +class TMCompatibility(Choice): + """Randomize which Pokemon can learn each TM. prefer_types: 90% chance if Pokemon's type matches the move, + 50% chance if move is Normal type and the Pokemon is not, and 25% chance otherwise. Pokemon will retain the same + TM compatibility when they evolve if the evolved form has the same type(s). Mew will always be able to learn + every TM.""" + display_name = "TM Compatibility" + default = 0 + option_vanilla = 0 + option_prefer_types = 1 + option_completely_random = 2 + option_full_compatibility = 3 + + +class HMCompatibility(Choice): + """Randomize which Pokemon can learn each HM. prefer_types: 100% chance if Pokemon's type matches the move, + 75% chance if move is Normal type and the Pokemon is not, and 25% chance otherwise. Pokemon will retain the same + HM compatibility when they evolve if the evolved form has the same type(s). Mew will always be able to learn + every HM.""" + display_name = "HM Compatibility" + default = 0 + option_vanilla = 0 + option_prefer_types = 1 + option_completely_random = 2 + option_full_compatibility = 3 + + +class RandomizePokemonTypes(Choice): + """Randomize the types of each Pokemon. Follow Evolutions will ensure Pokemon's types remain the same when evolving + (except possibly gaining a type).""" + display_name = "Pokemon Types" + option_vanilla = 0 + option_follow_evolutions = 1 + option_randomize = 2 + default = 0 + + +class SecondaryTypeChance(SpecialRange): + """If randomize_pokemon_types is on, this is the chance each Pokemon will have a secondary type. If follow_evolutions + is selected, it is the chance a second type will be added at each evolution stage. vanilla will give secondary types + to Pokemon that normally have a secondary type.""" + display_name = "Secondary Type Chance" + range_start = -1 + range_end = 100 + default = -1 + special_range_names = { + "vanilla": -1 + } + + +class RandomizeTypeChartTypes(Choice): + """The game's type chart consists of 3 columns: attacking type, defending type, and type effectiveness. + Matchups that have regular type effectiveness are not in the chart. Shuffle will shuffle the attacking types + across the attacking type column and the defending types across the defending type column (so for example Normal + type will still have exactly 2 types that it receives non-regular damage from, and 2 types it deals non-regular + damage to). Randomize will randomize each type in both columns to any random type.""" + display_name = "Randomize Type Chart Types" + option_vanilla = 0 + option_shuffle = 1 + option_randomize = 2 + default = 0 + + +class RandomizeTypeChartTypeEffectiveness(Choice): + """The game's type chart consists of 3 columns: attacking type, defending type, and type effectiveness. + Matchups that have regular type effectiveness are not in the chart. Shuffle will shuffle the type effectiveness + across the type effectiveness column (so for example there will always be 6 type immunities). Randomize will + randomize each entry in the table to no effect, not very effective, or super effective; with no effect occurring + at a low chance. Chaos will randomize the values to anywhere between 0% and 200% damage, in 10% increments.""" + display_name = "Randomize Type Chart Type Effectiveness" + option_vanilla = 0 + option_shuffle = 1 + option_randomize = 2 + option_chaos = 3 + default = 0 + + +class SafariZoneNormalBattles(Toggle): + """Change the Safari Zone to have standard wild pokemon battles.""" + display_name = "Safari Zone Normal Battles" + default = 0 + + +class NormalizeEncounterChances(Toggle): + """Each wild encounter table has 10 slots for Pokemon. Normally the chance for each being chosen ranges from + 19.9% to 1.2%. Turn this on to normalize them all to 10% each.""" + display_name = "Normalize Encounter Chances" + default = 0 + + +class ReusableTMs(Toggle): + """Makes TMs reusable, so they will not be consumed upon use.""" + display_name = "Reusable TMs" + default = 0 + + +class StartingMoney(Range): + """The amount of money you start with.""" + display_name = "Starting Money" + default = 3000 + range_start = 0 + range_end = 999999 + + +pokemon_rb_options = { + "game_version": GameVersion, + "trainer_name": TrainerName, + "rival_name": RivalName, + #"goal": Goal, + "elite_four_condition": EliteFourCondition, + "victory_road_condition": VictoryRoadCondition, + "viridian_gym_condition": ViridianGymCondition, + "cerulean_cave_condition": CeruleanCaveCondition, + "second_fossil_check_condition": SecondFossilCheckCondition, + "badgesanity": BadgeSanity, + "old_man": OldMan, + "tea": Tea, + "extra_key_items": ExtraKeyItems, + "extra_strength_boulders": ExtraStrengthBoulders, + "require_item_finder": RequireItemFinder, + "randomize_hidden_items": RandomizeHiddenItems, + "badges_needed_for_hm_moves": BadgesNeededForHMMoves, + "free_fly_location": FreeFlyLocation, + "oaks_aide_rt_2": OaksAidRt2, + "oaks_aide_rt_11": OaksAidRt11, + "oaks_aide_rt_15": OaksAidRt15, + "blind_trainers": BlindTrainers, + "minimum_steps_between_encounters": MinimumStepsBetweenEncounters, + "exp_modifier": ExpModifier, + "randomize_wild_pokemon": RandomizeWildPokemon, + "randomize_starter_pokemon": RandomizeStarterPokemon, + "randomize_static_pokemon": RandomizeStaticPokemon, + "randomize_legendary_pokemon": RandomizeLegendaryPokemon, + "catch_em_all": CatchEmAll, + "randomize_pokemon_stats": RandomizePokemonStats, + "randomize_pokemon_catch_rates": RandomizePokemonCatchRates, + "minimum_catch_rate": MinimumCatchRate, + "randomize_trainer_parties": RandomizeTrainerParties, + "trainer_legendaries": TrainerLegendaries, + "randomize_pokemon_movesets": RandomizePokemonMovesets, + "start_with_four_moves": StartWithFourMoves, + "tm_compatibility": TMCompatibility, + "hm_compatibility": HMCompatibility, + "randomize_pokemon_types": RandomizePokemonTypes, + "secondary_type_chance": SecondaryTypeChance, + "randomize_type_matchup_types": RandomizeTypeChartTypes, + "randomize_type_matchup_type_effectiveness": RandomizeTypeChartTypeEffectiveness, + "safari_zone_normal_battles": SafariZoneNormalBattles, + "normalize_encounter_chances": NormalizeEncounterChances, + "reusable_tms": ReusableTMs, + "starting_money": StartingMoney, +} \ No newline at end of file diff --git a/worlds/pokemon_rb/poke_data.py b/worlds/pokemon_rb/poke_data.py new file mode 100644 index 00000000..75222d57 --- /dev/null +++ b/worlds/pokemon_rb/poke_data.py @@ -0,0 +1,1212 @@ +id_to_mon = {1: 'Rhydon', 2: 'Kangaskhan', 3: 'Nidoran M', 4: 'Clefairy', 5: 'Spearow', 6: 'Voltorb', 7: 'Nidoking', + 8: 'Slowbro', 9: 'Ivysaur', 10: 'Exeggutor', 11: 'Lickitung', 12: 'Exeggcute', 13: 'Grimer', 14: 'Gengar', + 15: 'Nidoran F', 16: 'Nidoqueen', 17: 'Cubone', 18: 'Rhyhorn', 19: 'Lapras', 20: 'Arcanine', 21: 'Mew', + 22: 'Gyarados', 23: 'Shellder', 24: 'Tentacool', 25: 'Gastly', 26: 'Scyther', 27: 'Staryu', + 28: 'Blastoise', 29: 'Pinsir', 30: 'Tangela', 33: 'Growlithe', 34: 'Onix', 35: 'Fearow', 36: 'Pidgey', + 37: 'Slowpoke', 38: 'Kadabra', 39: 'Graveler', 40: 'Chansey', 41: 'Machoke', 42: 'Mr Mime', + 43: 'Hitmonlee', 44: 'Hitmonchan', 45: 'Arbok', 46: 'Parasect', 47: 'Psyduck', 48: 'Drowzee', 49: 'Golem', + 51: 'Magmar', 53: 'Electabuzz', 54: 'Magneton', 55: 'Koffing', 57: 'Mankey', 58: 'Seel', 59: 'Diglett', + 60: 'Tauros', 64: 'Farfetchd', 65: 'Venonat', 66: 'Dragonite', 70: 'Doduo', 71: 'Poliwag', 72: 'Jynx', + 73: 'Moltres', 74: 'Articuno', 75: 'Zapdos', 76: 'Ditto', 77: 'Meowth', 78: 'Krabby', 82: 'Vulpix', + 83: 'Ninetales', 84: 'Pikachu', 85: 'Raichu', 88: 'Dratini', 89: 'Dragonair', 90: 'Kabuto', 91: 'Kabutops', + 92: 'Horsea', 93: 'Seadra', 96: 'Sandshrew', 97: 'Sandslash', 98: 'Omanyte', 99: 'Omastar', + 100: 'Jigglypuff', 101: 'Wigglytuff', 102: 'Eevee', 103: 'Flareon', 104: 'Jolteon', 105: 'Vaporeon', + 106: 'Machop', 107: 'Zubat', 108: 'Ekans', 109: 'Paras', 110: 'Poliwhirl', 111: 'Poliwrath', 112: 'Weedle', + 113: 'Kakuna', 114: 'Beedrill', 116: 'Dodrio', 117: 'Primeape', 118: 'Dugtrio', 119: 'Venomoth', + 120: 'Dewgong', 123: 'Caterpie', 124: 'Metapod', 125: 'Butterfree', 126: 'Machamp', 128: 'Golduck', + 129: 'Hypno', 130: 'Golbat', 131: 'Mewtwo', 132: 'Snorlax', 133: 'Magikarp', 136: 'Muk', 138: 'Kingler', + 139: 'Cloyster', 141: 'Electrode', 142: 'Clefable', 143: 'Weezing', 144: 'Persian', 145: 'Marowak', + 147: 'Haunter', 148: 'Abra', 149: 'Alakazam', 150: 'Pidgeotto', 151: 'Pidgeot', 152: 'Starmie', + 153: 'Bulbasaur', 154: 'Venusaur', 155: 'Tentacruel', 157: 'Goldeen', 158: 'Seaking', 163: 'Ponyta', + 164: 'Rapidash', 165: 'Rattata', 166: 'Raticate', 167: 'Nidorino', 168: 'Nidorina', 169: 'Geodude', + 170: 'Porygon', 171: 'Aerodactyl', 173: 'Magnemite', 176: 'Charmander', 177: 'Squirtle', 178: 'Charmeleon', + 179: 'Wartortle', 180: 'Charizard', 185: 'Oddish', 186: 'Gloom', 187: 'Vileplume', 188: 'Bellsprout', + 189: 'Weepinbell', 190: 'Victreebel'} + + +pokemon_dex = { + 'Bulbasaur': 1, 'Ivysaur': 2, 'Venusaur': 3, 'Charmander': 4, 'Charmeleon': 5, 'Charizard': 6, 'Squirtle': 7, + 'Wartortle': 8, 'Blastoise': 9, 'Caterpie': 10, 'Metapod': 11, 'Butterfree': 12, 'Weedle': 13, 'Kakuna': 14, + 'Beedrill': 15, 'Pidgey': 16, 'Pidgeotto': 17, 'Pidgeot': 18, 'Rattata': 19, 'Raticate': 20, 'Spearow': 21, + 'Fearow': 22, 'Ekans': 23, 'Arbok': 24, 'Pikachu': 25, 'Raichu': 26, 'Sandshrew': 27, 'Sandslash': 28, + 'Nidoran F': 29, 'Nidorina': 30, 'Nidoqueen': 31, 'Nidoran M': 32, 'Nidorino': 33, 'Nidoking': 34, 'Clefairy': 35, + 'Clefable': 36, 'Vulpix': 37, 'Ninetales': 38, 'Jigglypuff': 39, 'Wigglytuff': 40, 'Zubat': 41, 'Golbat': 42, + 'Oddish': 43, 'Gloom': 44, 'Vileplume': 45, 'Paras': 46, 'Parasect': 47, 'Venonat': 48, 'Venomoth': 49, + 'Diglett': 50, 'Dugtrio': 51, 'Meowth': 52, 'Persian': 53, 'Psyduck': 54, 'Golduck': 55, 'Mankey': 56, + 'Primeape': 57, 'Growlithe': 58, 'Arcanine': 59, 'Poliwag': 60, 'Poliwhirl': 61, 'Poliwrath': 62, 'Abra': 63, + 'Kadabra': 64, 'Alakazam': 65, 'Machop': 66, 'Machoke': 67, 'Machamp': 68, 'Bellsprout': 69, 'Weepinbell': 70, + 'Victreebel': 71, 'Tentacool': 72, 'Tentacruel': 73, 'Geodude': 74, 'Graveler': 75, 'Golem': 76, 'Ponyta': 77, + 'Rapidash': 78, 'Slowpoke': 79, 'Slowbro': 80, 'Magnemite': 81, 'Magneton': 82, 'Farfetchd': 83, 'Doduo': 84, + 'Dodrio': 85, 'Seel': 86, 'Dewgong': 87, 'Grimer': 88, 'Muk': 89, 'Shellder': 90, 'Cloyster': 91, 'Gastly': 92, + 'Haunter': 93, 'Gengar': 94, 'Onix': 95, 'Drowzee': 96, 'Hypno': 97, 'Krabby': 98, 'Kingler': 99, 'Voltorb': 100, + 'Electrode': 101, 'Exeggcute': 102, 'Exeggutor': 103, 'Cubone': 104, 'Marowak': 105, 'Hitmonlee': 106, + 'Hitmonchan': 107, 'Lickitung': 108, 'Koffing': 109, 'Weezing': 110, 'Rhyhorn': 111, 'Rhydon': 112, 'Chansey': 113, + 'Tangela': 114, 'Kangaskhan': 115, 'Horsea': 116, 'Seadra': 117, 'Goldeen': 118, 'Seaking': 119, 'Staryu': 120, + 'Starmie': 121, 'Mr Mime': 122, 'Scyther': 123, 'Jynx': 124, 'Electabuzz': 125, 'Magmar': 126, 'Pinsir': 127, + 'Tauros': 128, 'Magikarp': 129, 'Gyarados': 130, 'Lapras': 131, 'Ditto': 132, 'Eevee': 133, 'Vaporeon': 134, + 'Jolteon': 135, 'Flareon': 136, 'Porygon': 137, 'Omanyte': 138, 'Omastar': 139, 'Kabuto': 140, 'Kabutops': 141, + 'Aerodactyl': 142, 'Snorlax': 143, 'Articuno': 144, 'Zapdos': 145, 'Moltres': 146, 'Dratini': 147, 'Dragonair': 148, + 'Dragonite': 149, 'Mewtwo': 150, 'Mew': 151 +} + + +type_ids = { + "Normal": 0x0, + "Fighting": 0x1, + "Flying": 0x2, + "Poison": 0x3, + "Ground": 0x4, + "Rock": 0x5, + "Bug": 0x7, + "Ghost": 0x8, + "Fire": 0x14, + "Water": 0x15, + "Grass": 0x16, + "Electric": 0x17, + "Psychic": 0x18, + "Ice": 0x19, + "Dragon": 0x1A +} +type_names = { + 0x0: "Normal", + 0x1: "Fighting", + 0x2: "Flying", + 0x3: "Poison", + 0x4: "Ground", + 0x5: "Rock", + 0x7: "Bug", + 0x8: "Ghost", + 0x14: "Fire", + 0x15: "Water", + 0x16: "Grass", + 0x17: "Electric", + 0x18: "Psychic", + 0x19: "Ice", + 0x1a: "Dragon" +} + +type_chart = [ + ["Water", "Fire", 20], + ["Fire", "Grass", 20], + ["Fire", "Ice", 20], + ["Grass", "Water", 20], + ["Electric", "Water", 20], + ["Water", "Rock", 20], + ["Ground", "Flying", 0], + ["Water", "Water", 5], + ["Fire", "Fire", 5], + ["Electric", "Electric", 5], + ["Ice", "Ice", 5], + ["Grass", "Grass", 5], + ["Psychic", "Psychic", 5], + ["Fire", "Water", 5], + ["Grass", "Fire", 5], + ["Water", "Grass", 5], + ["Electric", "Grass", 5], + ["Normal", "Rock", 5], + ["Normal", "Ghost", 0], + ["Ghost", "Ghost", 20], + ["Fire", "Bug", 20], + ["Fire", "Rock", 5], + ["Water", "Ground", 20], + ["Electric", "Ground", 0], + ["Electric", "Flying", 20], + ["Grass", "Ground", 20], + ["Grass", "Bug", 5], + ["Grass", "Poison", 5], + ["Grass", "Rock", 20], + ["Grass", "Flying", 5], + ["Ice", "Water", 5], + ["Ice", "Grass", 20], + ["Ice", "Ground", 20], + ["Ice", "Flying", 20], + ["Fighting", "Normal", 20], + ["Fighting", "Poison", 5], + ["Fighting", "Flying", 5], + ["Fighting", "Psychic", 5], + ["Fighting", "Bug", 5], + ["Fighting", "Rock", 20], + ["Fighting", "Ice", 20], + ["Fighting", "Ghost", 0], + ["Poison", "Grass", 20], + ["Poison", "Poison", 5], + ["Poison", "Ground", 5], + ["Poison", "Bug", 20], + ["Poison", "Rock", 5], + ["Poison", "Ghost", 5], + ["Ground", "Fire", 20], + ["Ground", "Electric", 20], + ["Ground", "Grass", 5], + ["Ground", "Bug", 5], + ["Ground", "Rock", 20], + ["Ground", "Poison", 20], + ["Flying", "Electric", 5], + ["Flying", "Fighting", 20], + ["Flying", "Bug", 20], + ["Flying", "Grass", 20], + ["Flying", "Rock", 5], + ["Psychic", "Fighting", 20], + ["Psychic", "Poison", 20], + ["Bug", "Fire", 5], + ["Bug", "Grass", 20], + ["Bug", "Fighting", 5], + ["Bug", "Flying", 5], + ["Bug", "Psychic", 20], + ["Bug", "Ghost", 5], + ["Bug", "Poison", 20], + ["Rock", "Fire", 20], + ["Rock", "Fighting", 5], + ["Rock", "Ground", 5], + ["Rock", "Flying", 20], + ["Rock", "Bug", 20], + ["Rock", "Ice", 20], + ["Ghost", "Normal", 0], + ["Ghost", "Psychic", 0], + ["Fire", "Dragon", 5], + ["Water", "Dragon", 5], + ["Electric", "Dragon", 5], + ["Grass", "Dragon", 5], + ["Ice", "Dragon", 20], + ["Dragon", "Dragon", 20] +] + +pokemon_data = { + 'Bulbasaur': {'id': 153, 'dex': 1, 'hp': 45, 'atk': 49, 'def': 49, 'spd': 45, 'spc': 65, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 64, 'start move 1': 'Tackle', + 'start move 2': 'Growl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa4\x038\xc0\x03\x08\x06')}, + 'Ivysaur': {'id': 9, 'dex': 2, 'hp': 60, 'atk': 62, 'def': 63, 'spd': 60, 'spc': 80, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 141, 'start move 1': 'Tackle', 'start move 2': 'Growl', + 'start move 3': 'Leech Seed', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa4\x038\xc0\x03\x08\x06')}, + 'Venusaur': {'id': 154, 'dex': 3, 'hp': 80, 'atk': 82, 'def': 83, 'spd': 80, 'spc': 100, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 208, 'start move 1': 'Tackle', + 'start move 2': 'Growl', 'start move 3': 'Leech Seed', 'start move 4': 'Vine Whip', 'growth rate': 3, + 'tms': bytearray(b'\xa4C8\xc0\x03\x08\x06')}, + 'Charmander': {'id': 176, 'dex': 4, 'hp': 39, 'atk': 52, 'def': 43, 'spd': 65, 'spc': 50, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 45, 'base exp': 65, 'start move 1': 'Scratch', + 'start move 2': 'Growl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb5\x03O\xc8\xe3\x08&')}, + 'Charmeleon': {'id': 178, 'dex': 5, 'hp': 58, 'atk': 64, 'def': 58, 'spd': 80, 'spc': 65, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 45, 'base exp': 142, 'start move 1': 'Scratch', + 'start move 2': 'Growl', 'start move 3': 'Ember', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb5\x03O\xc8\xe3\x08&')}, + 'Charizard': {'id': 180, 'dex': 6, 'hp': 78, 'atk': 84, 'def': 78, 'spd': 100, 'spc': 85, 'type1': 'Fire', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 209, 'start move 1': 'Scratch', + 'start move 2': 'Growl', 'start move 3': 'Ember', 'start move 4': 'Leer', 'growth rate': 3, + 'tms': bytearray(b'\xb5CO\xce\xe3\x08&')}, + 'Squirtle': {'id': 177, 'dex': 7, 'hp': 44, 'atk': 48, 'def': 65, 'spd': 43, 'spc': 50, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 45, 'base exp': 66, 'start move 1': 'Tackle', + 'start move 2': 'Tail Whip', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1?\x0f\xc8\x83\x082')}, + 'Wartortle': {'id': 179, 'dex': 8, 'hp': 59, 'atk': 63, 'def': 80, 'spd': 58, 'spc': 65, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 45, 'base exp': 143, 'start move 1': 'Tackle', + 'start move 2': 'Tail Whip', 'start move 3': 'Bubble', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1?\x0f\xc8\x83\x082')}, + 'Blastoise': {'id': 28, 'dex': 9, 'hp': 79, 'atk': 83, 'def': 100, 'spd': 78, 'spc': 85, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 45, 'base exp': 210, 'start move 1': 'Tackle', + 'start move 2': 'Tail Whip', 'start move 3': 'Bubble', 'start move 4': 'Water Gun', 'growth rate': 3, + 'tms': bytearray(b'\xb1\x7f\x0f\xce\x83\x082')}, + 'Caterpie': {'id': 123, 'dex': 10, 'hp': 45, 'atk': 30, 'def': 35, 'spd': 45, 'spc': 20, 'type1': 'Bug', + 'type2': 'Bug', 'catch rate': 255, 'base exp': 53, 'start move 1': 'Tackle', + 'start move 2': 'String Shot', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Metapod': {'id': 124, 'dex': 11, 'hp': 50, 'atk': 20, 'def': 55, 'spd': 30, 'spc': 25, 'type1': 'Bug', + 'type2': 'Bug', 'catch rate': 120, 'base exp': 72, 'start move 1': 'Harden', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Butterfree': {'id': 125, 'dex': 12, 'hp': 60, 'atk': 45, 'def': 50, 'spd': 70, 'spc': 80, 'type1': 'Bug', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 160, 'start move 1': 'Confusion', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'*C8\xf0C(\x02')}, + 'Weedle': {'id': 112, 'dex': 13, 'hp': 40, 'atk': 35, 'def': 30, 'spd': 50, 'spc': 20, 'type1': 'Bug', + 'type2': 'Poison', 'catch rate': 255, 'base exp': 52, 'start move 1': 'Poison Sting', + 'start move 2': 'String Shot', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Kakuna': {'id': 113, 'dex': 14, 'hp': 45, 'atk': 25, 'def': 50, 'spd': 35, 'spc': 25, 'type1': 'Bug', + 'type2': 'Poison', 'catch rate': 120, 'base exp': 71, 'start move 1': 'Harden', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Beedrill': {'id': 114, 'dex': 15, 'hp': 65, 'atk': 80, 'def': 40, 'spd': 75, 'spc': 45, 'type1': 'Bug', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 159, 'start move 1': 'Fury Attack', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'$C\x18\xc0\xc3\x08\x06')}, + 'Pidgey': {'id': 36, 'dex': 16, 'hp': 40, 'atk': 45, 'def': 40, 'spd': 56, 'spc': 35, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 255, 'base exp': 55, 'start move 1': 'Gust', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'*\x03\x08\xc0C\x0c\n')}, + 'Pidgeotto': {'id': 150, 'dex': 17, 'hp': 63, 'atk': 60, 'def': 55, 'spd': 71, 'spc': 50, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 120, 'base exp': 113, 'start move 1': 'Gust', + 'start move 2': 'Sand Attack', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'*\x03\x08\xc0C\x0c\n')}, + 'Pidgeot': {'id': 151, 'dex': 18, 'hp': 83, 'atk': 80, 'def': 75, 'spd': 91, 'spc': 70, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 172, 'start move 1': 'Gust', + 'start move 2': 'Sand Attack', 'start move 3': 'Quick Attack', 'start move 4': 'No Move', + 'growth rate': 3, 'tms': bytearray(b'*C\x08\xc0C\x0c\n')}, + 'Rattata': {'id': 165, 'dex': 19, 'hp': 30, 'atk': 56, 'def': 35, 'spd': 72, 'spc': 25, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 255, 'base exp': 57, 'start move 1': 'Tackle', + 'start move 2': 'Tail Whip', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0/\x88\xc9\xc2\x08\x02')}, + 'Raticate': {'id': 166, 'dex': 20, 'hp': 55, 'atk': 81, 'def': 60, 'spd': 97, 'spc': 50, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 90, 'base exp': 116, 'start move 1': 'Tackle', + 'start move 2': 'Tail Whip', 'start move 3': 'Quick Attack', 'start move 4': 'No Move', + 'growth rate': 0, 'tms': bytearray(b'\xa0\x7f\x88\xc9\xc2\x08\x02')}, + 'Spearow': {'id': 5, 'dex': 21, 'hp': 40, 'atk': 60, 'def': 30, 'spd': 70, 'spc': 31, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 255, 'base exp': 58, 'start move 1': 'Peck', 'start move 2': 'Growl', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'*\x03\x08\xc0B\x0c\n')}, + 'Fearow': {'id': 35, 'dex': 22, 'hp': 65, 'atk': 90, 'def': 65, 'spd': 100, 'spc': 61, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 90, 'base exp': 162, 'start move 1': 'Peck', 'start move 2': 'Growl', + 'start move 3': 'Leer', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'*C\x08\xc0B\x0c\n')}, + 'Ekans': {'id': 108, 'dex': 23, 'hp': 35, 'atk': 60, 'def': 44, 'spd': 55, 'spc': 40, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 255, 'base exp': 62, 'start move 1': 'Wrap', 'start move 2': 'Leer', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x03\x18\xce\x82\x88"')}, + 'Arbok': {'id': 45, 'dex': 24, 'hp': 60, 'atk': 85, 'def': 69, 'spd': 80, 'spc': 65, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 90, 'base exp': 147, 'start move 1': 'Wrap', 'start move 2': 'Leer', + 'start move 3': 'Poison Sting', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0C\x18\xce\x82\x88"')}, + 'Pikachu': {'id': 84, 'dex': 25, 'hp': 35, 'atk': 55, 'def': 30, 'spd': 90, 'spc': 50, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 190, 'base exp': 82, 'start move 1': 'Thundershock', + 'start move 2': 'Growl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x83\x8d\xc1\xc3\x18B')}, + 'Raichu': {'id': 85, 'dex': 26, 'hp': 60, 'atk': 90, 'def': 55, 'spd': 100, 'spc': 90, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 75, 'base exp': 122, 'start move 1': 'Thundershock', + 'start move 2': 'Growl', 'start move 3': 'Thunder Wave', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\xc3\x8d\xc1\xc3\x18B')}, + 'Sandshrew': {'id': 96, 'dex': 27, 'hp': 50, 'atk': 75, 'def': 85, 'spd': 40, 'spc': 30, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 255, 'base exp': 93, 'start move 1': 'Scratch', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4\x03\r\xce\xc2\x88&')}, + 'Sandslash': {'id': 97, 'dex': 28, 'hp': 75, 'atk': 100, 'def': 110, 'spd': 65, 'spc': 55, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 90, 'base exp': 163, 'start move 1': 'Scratch', + 'start move 2': 'Sand Attack', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4C\r\xce\xc2\x88&')}, + 'Nidoran F': {'id': 15, 'dex': 29, 'hp': 55, 'atk': 47, 'def': 52, 'spd': 41, 'spc': 40, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 235, 'base exp': 59, 'start move 1': 'Growl', + 'start move 2': 'Tackle', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa0#\x88\xc1\x83\x08\x02')}, + 'Nidorina': {'id': 168, 'dex': 30, 'hp': 70, 'atk': 62, 'def': 67, 'spd': 56, 'spc': 55, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 120, 'base exp': 117, 'start move 1': 'Growl', + 'start move 2': 'Tackle', 'start move 3': 'Scratch', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xe0?\x88\xc1\x83\x08\x02')}, + 'Nidoqueen': {'id': 16, 'dex': 31, 'hp': 90, 'atk': 82, 'def': 87, 'spd': 76, 'spc': 75, 'type1': 'Poison', + 'type2': 'Ground', 'catch rate': 45, 'base exp': 194, 'start move 1': 'Tackle', + 'start move 2': 'Scratch', 'start move 3': 'Tail Whip', 'start move 4': 'Body Slam', 'growth rate': 3, + 'tms': bytearray(b'\xf1\xff\x8f\xc7\xa3\x882')}, + 'Nidoran M': {'id': 3, 'dex': 32, 'hp': 46, 'atk': 57, 'def': 40, 'spd': 50, 'spc': 40, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 235, 'base exp': 60, 'start move 1': 'Leer', + 'start move 2': 'Tackle', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xe0#\x88\xc1\x83\x08\x02')}, + 'Nidorino': {'id': 167, 'dex': 33, 'hp': 61, 'atk': 72, 'def': 57, 'spd': 65, 'spc': 55, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 120, 'base exp': 118, 'start move 1': 'Leer', + 'start move 2': 'Tackle', 'start move 3': 'Horn Attack', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xe0?\x88\xc1\x83\x08\x02')}, + 'Nidoking': {'id': 7, 'dex': 34, 'hp': 81, 'atk': 92, 'def': 77, 'spd': 85, 'spc': 75, 'type1': 'Poison', + 'type2': 'Ground', 'catch rate': 45, 'base exp': 195, 'start move 1': 'Tackle', + 'start move 2': 'Horn Attack', 'start move 3': 'Poison Sting', 'start move 4': 'Thrash', + 'growth rate': 3, 'tms': bytearray(b'\xf1\xff\x8f\xc7\xa3\x882')}, + 'Clefairy': {'id': 4, 'dex': 35, 'hp': 70, 'atk': 45, 'def': 48, 'spd': 35, 'spc': 60, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 150, 'base exp': 68, 'start move 1': 'Pound', 'start move 2': 'Growl', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 4, + 'tms': bytearray(b'\xb1?\xaf\xf1\xa78c')}, + 'Clefable': {'id': 142, 'dex': 36, 'hp': 95, 'atk': 70, 'def': 73, 'spd': 60, 'spc': 85, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 25, 'base exp': 129, 'start move 1': 'Sing', + 'start move 2': 'Doubleslap', 'start move 3': 'Minimize', 'start move 4': 'Metronome', + 'growth rate': 4, 'tms': bytearray(b'\xb1\x7f\xaf\xf1\xa78c')}, + 'Vulpix': {'id': 82, 'dex': 37, 'hp': 38, 'atk': 41, 'def': 40, 'spd': 65, 'spc': 65, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 190, 'base exp': 63, 'start move 1': 'Ember', 'start move 2': 'Tail Whip', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x03\x08\xc8\xe3\x08\x02')}, + 'Ninetales': {'id': 83, 'dex': 38, 'hp': 73, 'atk': 76, 'def': 75, 'spd': 100, 'spc': 100, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 75, 'base exp': 178, 'start move 1': 'Ember', + 'start move 2': 'Tail Whip', 'start move 3': 'Quick Attack', 'start move 4': 'Roar', 'growth rate': 0, + 'tms': bytearray(b'\xa0C\x08\xc8\xe3\x08\x02')}, + 'Jigglypuff': {'id': 100, 'dex': 39, 'hp': 115, 'atk': 45, 'def': 20, 'spd': 20, 'spc': 25, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 170, 'base exp': 76, 'start move 1': 'Sing', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 4, + 'tms': bytearray(b'\xb1?\xaf\xf1\xa38c')}, + 'Wigglytuff': {'id': 101, 'dex': 40, 'hp': 140, 'atk': 70, 'def': 45, 'spd': 45, 'spc': 50, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 50, 'base exp': 109, 'start move 1': 'Sing', + 'start move 2': 'Disable', 'start move 3': 'Defense Curl', 'start move 4': 'Doubleslap', + 'growth rate': 4, 'tms': bytearray(b'\xb1\x7f\xaf\xf1\xa38c')}, + 'Zubat': {'id': 107, 'dex': 41, 'hp': 40, 'atk': 45, 'def': 35, 'spd': 55, 'spc': 40, 'type1': 'Poison', + 'type2': 'Flying', 'catch rate': 255, 'base exp': 54, 'start move 1': 'Leech Life', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'*\x03\x18\xc0B\x08\x02')}, + 'Golbat': {'id': 130, 'dex': 42, 'hp': 75, 'atk': 80, 'def': 70, 'spd': 90, 'spc': 75, 'type1': 'Poison', + 'type2': 'Flying', 'catch rate': 90, 'base exp': 171, 'start move 1': 'Leech Life', + 'start move 2': 'Screech', 'start move 3': 'Bite', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'*C\x18\xc0B\x08\x02')}, + 'Oddish': {'id': 185, 'dex': 43, 'hp': 45, 'atk': 50, 'def': 55, 'spd': 30, 'spc': 75, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 255, 'base exp': 78, 'start move 1': 'Absorb', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'$\x038\xc0\x03\x08\x06')}, + 'Gloom': {'id': 186, 'dex': 44, 'hp': 60, 'atk': 65, 'def': 70, 'spd': 40, 'spc': 85, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 120, 'base exp': 132, 'start move 1': 'Absorb', + 'start move 2': 'Poisonpowder', 'start move 3': 'Stun Spore', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'$\x038\xc0\x03\x08\x06')}, + 'Vileplume': {'id': 187, 'dex': 45, 'hp': 75, 'atk': 80, 'def': 85, 'spd': 50, 'spc': 100, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 184, 'start move 1': 'Stun Spore', + 'start move 2': 'Sleep Powder', 'start move 3': 'Acid', 'start move 4': 'Petal Dance', + 'growth rate': 3, 'tms': bytearray(b'\xa4C8\xc0\x03\x08\x06')}, + 'Paras': {'id': 109, 'dex': 46, 'hp': 35, 'atk': 70, 'def': 55, 'spd': 25, 'spc': 55, 'type1': 'Bug', + 'type2': 'Grass', 'catch rate': 190, 'base exp': 70, 'start move 1': 'Scratch', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4\x038\xc8\x83\x08\x06')}, + 'Parasect': {'id': 46, 'dex': 47, 'hp': 60, 'atk': 95, 'def': 80, 'spd': 30, 'spc': 80, 'type1': 'Bug', + 'type2': 'Grass', 'catch rate': 75, 'base exp': 128, 'start move 1': 'Scratch', + 'start move 2': 'Stun Spore', 'start move 3': 'Leech Life', 'start move 4': 'No Move', + 'growth rate': 0, 'tms': bytearray(b'\xa4C8\xc8\x83\x08\x06')}, + 'Venonat': {'id': 65, 'dex': 48, 'hp': 60, 'atk': 55, 'def': 50, 'spd': 45, 'spc': 40, 'type1': 'Bug', + 'type2': 'Poison', 'catch rate': 190, 'base exp': 75, 'start move 1': 'Tackle', + 'start move 2': 'Disable', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' \x038\xd0\x03(\x02')}, + 'Venomoth': {'id': 119, 'dex': 49, 'hp': 70, 'atk': 65, 'def': 60, 'spd': 90, 'spc': 90, 'type1': 'Bug', + 'type2': 'Poison', 'catch rate': 75, 'base exp': 138, 'start move 1': 'Tackle', + 'start move 2': 'Disable', 'start move 3': 'Poisonpowder', 'start move 4': 'Leech Life', + 'growth rate': 0, 'tms': bytearray(b'*C8\xf0C(\x02')}, + 'Diglett': {'id': 59, 'dex': 50, 'hp': 10, 'atk': 55, 'def': 25, 'spd': 95, 'spc': 45, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 255, 'base exp': 81, 'start move 1': 'Scratch', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x03\x08\xce\x02\x88\x02')}, + 'Dugtrio': {'id': 118, 'dex': 51, 'hp': 35, 'atk': 80, 'def': 50, 'spd': 120, 'spc': 70, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 50, 'base exp': 153, 'start move 1': 'Scratch', + 'start move 2': 'Growl', 'start move 3': 'Dig', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0C\x08\xce\x02\x88\x02')}, + 'Meowth': {'id': 77, 'dex': 52, 'hp': 40, 'atk': 45, 'def': 35, 'spd': 90, 'spc': 40, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 255, 'base exp': 69, 'start move 1': 'Scratch', 'start move 2': 'Growl', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x8f\x88\xc1\xc2\x08\x02')}, + 'Persian': {'id': 144, 'dex': 53, 'hp': 65, 'atk': 70, 'def': 60, 'spd': 115, 'spc': 65, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 90, 'base exp': 148, 'start move 1': 'Scratch', + 'start move 2': 'Growl', 'start move 3': 'Bite', 'start move 4': 'Screech', 'growth rate': 0, + 'tms': bytearray(b'\xa0\xcf\x88\xc1\xc2\x08\x02')}, + 'Psyduck': {'id': 47, 'dex': 54, 'hp': 50, 'atk': 52, 'def': 48, 'spd': 55, 'spc': 50, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 190, 'base exp': 80, 'start move 1': 'Scratch', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\xbf\x0f\xc8\xc2\x082')}, + 'Golduck': {'id': 128, 'dex': 55, 'hp': 80, 'atk': 82, 'def': 78, 'spd': 85, 'spc': 80, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 75, 'base exp': 174, 'start move 1': 'Scratch', + 'start move 2': 'Tail Whip', 'start move 3': 'Disable', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\xff\x0f\xc8\xc2\x082')}, + 'Mankey': {'id': 57, 'dex': 56, 'hp': 40, 'atk': 80, 'def': 35, 'spd': 70, 'spc': 35, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 190, 'base exp': 74, 'start move 1': 'Scratch', + 'start move 2': 'Leer', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x83\x8f\xc9\xc6\x88"')}, + 'Primeape': {'id': 117, 'dex': 57, 'hp': 65, 'atk': 105, 'def': 60, 'spd': 95, 'spc': 60, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 75, 'base exp': 149, 'start move 1': 'Scratch', + 'start move 2': 'Leer', 'start move 3': 'Karate Chop', 'start move 4': 'Fury Swipes', 'growth rate': 0, + 'tms': bytearray(b'\xb1\xc3\x8f\xc9\xc6\x88"')}, + 'Growlithe': {'id': 33, 'dex': 58, 'hp': 55, 'atk': 70, 'def': 45, 'spd': 60, 'spc': 50, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 190, 'base exp': 91, 'start move 1': 'Bite', 'start move 2': 'Roar', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xa0\x03H\xc8\xe3\x08\x02')}, + 'Arcanine': {'id': 20, 'dex': 59, 'hp': 90, 'atk': 110, 'def': 80, 'spd': 95, 'spc': 80, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 75, 'base exp': 213, 'start move 1': 'Roar', 'start move 2': 'Ember', + 'start move 3': 'Leer', 'start move 4': 'Take Down', 'growth rate': 5, + 'tms': bytearray(b'\xa0CH\xe8\xe3\x08\x02')}, + 'Poliwag': {'id': 71, 'dex': 60, 'hp': 40, 'atk': 50, 'def': 40, 'spd': 90, 'spc': 40, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 255, 'base exp': 77, 'start move 1': 'Bubble', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa0?\x08\xd0\x82(\x12')}, + 'Poliwhirl': {'id': 110, 'dex': 61, 'hp': 65, 'atk': 65, 'def': 65, 'spd': 90, 'spc': 50, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 120, 'base exp': 131, 'start move 1': 'Bubble', + 'start move 2': 'Hypnosis', 'start move 3': 'Water Gun', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1?\x0f\xd6\x86(2')}, + 'Poliwrath': {'id': 111, 'dex': 62, 'hp': 90, 'atk': 85, 'def': 95, 'spd': 70, 'spc': 70, 'type1': 'Water', + 'type2': 'Fighting', 'catch rate': 45, 'base exp': 185, 'start move 1': 'Hypnosis', + 'start move 2': 'Water Gun', 'start move 3': 'Doubleslap', 'start move 4': 'Body Slam', + 'growth rate': 3, 'tms': bytearray(b'\xb1\x7f\x0f\xd6\x86(2')}, + 'Abra': {'id': 148, 'dex': 63, 'hp': 25, 'atk': 20, 'def': 15, 'spd': 90, 'spc': 105, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 200, 'base exp': 73, 'start move 1': 'Teleport', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1\x03\x0f\xf0\x878C')}, + 'Kadabra': {'id': 38, 'dex': 64, 'hp': 40, 'atk': 35, 'def': 30, 'spd': 105, 'spc': 120, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 100, 'base exp': 145, 'start move 1': 'Teleport', + 'start move 2': 'Confusion', 'start move 3': 'Disable', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1\x03\x0f\xf8\x878C')}, + 'Alakazam': {'id': 149, 'dex': 65, 'hp': 55, 'atk': 50, 'def': 45, 'spd': 120, 'spc': 135, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 50, 'base exp': 186, 'start move 1': 'Teleport', + 'start move 2': 'Confusion', 'start move 3': 'Disable', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1C\x0f\xf8\x878C')}, + 'Machop': {'id': 106, 'dex': 66, 'hp': 70, 'atk': 80, 'def': 50, 'spd': 35, 'spc': 35, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 180, 'base exp': 88, 'start move 1': 'Karate Chop', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1\x03\x0f\xce\xa6\x88"')}, + 'Machoke': {'id': 41, 'dex': 67, 'hp': 80, 'atk': 100, 'def': 70, 'spd': 45, 'spc': 50, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 90, 'base exp': 146, 'start move 1': 'Karate Chop', + 'start move 2': 'Low Kick', 'start move 3': 'Leer', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1\x03\x0f\xce\xa6\x88"')}, + 'Machamp': {'id': 126, 'dex': 68, 'hp': 90, 'atk': 130, 'def': 80, 'spd': 55, 'spc': 65, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 45, 'base exp': 193, 'start move 1': 'Karate Chop', + 'start move 2': 'Low Kick', 'start move 3': 'Leer', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1C\x0f\xce\xa6\x88"')}, + 'Bellsprout': {'id': 188, 'dex': 69, 'hp': 50, 'atk': 75, 'def': 35, 'spd': 40, 'spc': 70, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 255, 'base exp': 84, 'start move 1': 'Vine Whip', + 'start move 2': 'Growth', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'$\x038\xc0\x03\x08\x06')}, + 'Weepinbell': {'id': 189, 'dex': 70, 'hp': 65, 'atk': 90, 'def': 50, 'spd': 55, 'spc': 85, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 120, 'base exp': 151, 'start move 1': 'Vine Whip', + 'start move 2': 'Growth', 'start move 3': 'Wrap', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'$\x038\xc0\x03\x08\x06')}, + 'Victreebel': {'id': 190, 'dex': 71, 'hp': 80, 'atk': 105, 'def': 65, 'spd': 70, 'spc': 100, 'type1': 'Grass', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 191, 'start move 1': 'Sleep Powder', + 'start move 2': 'Stun Spore', 'start move 3': 'Acid', 'start move 4': 'Razor Leaf', 'growth rate': 3, + 'tms': bytearray(b'\xa4C8\xc0\x03\x08\x06')}, + 'Tentacool': {'id': 24, 'dex': 72, 'hp': 40, 'atk': 40, 'def': 35, 'spd': 70, 'spc': 100, 'type1': 'Water', + 'type2': 'Poison', 'catch rate': 190, 'base exp': 105, 'start move 1': 'Acid', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'$?\x18\xc0\x83\x08\x16')}, + 'Tentacruel': {'id': 155, 'dex': 73, 'hp': 80, 'atk': 70, 'def': 65, 'spd': 100, 'spc': 120, 'type1': 'Water', + 'type2': 'Poison', 'catch rate': 60, 'base exp': 205, 'start move 1': 'Acid', + 'start move 2': 'Supersonic', 'start move 3': 'Wrap', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'$\x7f\x18\xc0\x83\x08\x16')}, + 'Geodude': {'id': 169, 'dex': 74, 'hp': 40, 'atk': 80, 'def': 100, 'spd': 20, 'spc': 30, 'type1': 'Rock', + 'type2': 'Ground', 'catch rate': 255, 'base exp': 86, 'start move 1': 'Tackle', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa1\x03\x0f\xce.\xc8"')}, + 'Graveler': {'id': 39, 'dex': 75, 'hp': 55, 'atk': 95, 'def': 115, 'spd': 35, 'spc': 45, 'type1': 'Rock', + 'type2': 'Ground', 'catch rate': 120, 'base exp': 134, 'start move 1': 'Tackle', + 'start move 2': 'Defense Curl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xa1\x03\x0f\xce.\xc8"')}, + 'Golem': {'id': 49, 'dex': 76, 'hp': 80, 'atk': 110, 'def': 130, 'spd': 45, 'spc': 55, 'type1': 'Rock', + 'type2': 'Ground', 'catch rate': 45, 'base exp': 177, 'start move 1': 'Tackle', + 'start move 2': 'Defense Curl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xb1C\x0f\xce.\xc8"')}, + 'Ponyta': {'id': 163, 'dex': 77, 'hp': 50, 'atk': 85, 'def': 55, 'spd': 90, 'spc': 65, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 190, 'base exp': 152, 'start move 1': 'Ember', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xe0\x03\x08\xc0\xe3\x08\x02')}, + 'Rapidash': {'id': 164, 'dex': 78, 'hp': 65, 'atk': 100, 'def': 70, 'spd': 105, 'spc': 80, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 60, 'base exp': 192, 'start move 1': 'Ember', + 'start move 2': 'Tail Whip', 'start move 3': 'Stomp', 'start move 4': 'Growl', 'growth rate': 0, + 'tms': bytearray(b'\xe0C\x08\xc0\xe3\x08\x02')}, + 'Slowpoke': {'id': 37, 'dex': 79, 'hp': 90, 'atk': 65, 'def': 65, 'spd': 15, 'spc': 40, 'type1': 'Water', + 'type2': 'Psychic', 'catch rate': 190, 'base exp': 99, 'start move 1': 'Confusion', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\xbf\x08\xfe\xe38s')}, + 'Slowbro': {'id': 8, 'dex': 80, 'hp': 95, 'atk': 75, 'def': 110, 'spd': 30, 'spc': 80, 'type1': 'Water', + 'type2': 'Psychic', 'catch rate': 75, 'base exp': 164, 'start move 1': 'Confusion', + 'start move 2': 'Disable', 'start move 3': 'Headbutt', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\xff\x0f\xfe\xe38s')}, + 'Magnemite': {'id': 173, 'dex': 81, 'hp': 25, 'atk': 35, 'def': 70, 'spd': 45, 'spc': 95, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 190, 'base exp': 89, 'start move 1': 'Tackle', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' \x03\x88\xe1C\x18B')}, + 'Magneton': {'id': 54, 'dex': 82, 'hp': 50, 'atk': 60, 'def': 95, 'spd': 70, 'spc': 120, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 60, 'base exp': 161, 'start move 1': 'Tackle', + 'start move 2': 'Sonicboom', 'start move 3': 'Thundershock', 'start move 4': 'No Move', + 'growth rate': 0, 'tms': bytearray(b' C\x88\xe1C\x18B')}, + 'Farfetchd': {'id': 64, 'dex': 83, 'hp': 52, 'atk': 65, 'def': 55, 'spd': 60, 'spc': 58, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 94, 'start move 1': 'Peck', + 'start move 2': 'Sand Attack', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xae\x03\x08\xc0\xc3\x08\x0e')}, + 'Doduo': {'id': 70, 'dex': 84, 'hp': 35, 'atk': 85, 'def': 45, 'spd': 75, 'spc': 35, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 190, 'base exp': 96, 'start move 1': 'Peck', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa8\x03\x08\xc0\x83\x0c\x0b')}, + 'Dodrio': {'id': 116, 'dex': 85, 'hp': 60, 'atk': 110, 'def': 70, 'spd': 100, 'spc': 60, 'type1': 'Normal', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 158, 'start move 1': 'Peck', 'start move 2': 'Growl', + 'start move 3': 'Fury Attack', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa8C\x08\xc0\x83\x0c\x0b')}, + 'Seel': {'id': 58, 'dex': 86, 'hp': 65, 'atk': 45, 'def': 55, 'spd': 45, 'spc': 70, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 190, 'base exp': 100, 'start move 1': 'Headbutt', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xe0\xbf\x08\xc0\x82\x082')}, + 'Dewgong': {'id': 120, 'dex': 87, 'hp': 90, 'atk': 70, 'def': 80, 'spd': 70, 'spc': 95, 'type1': 'Water', + 'type2': 'Ice', 'catch rate': 75, 'base exp': 176, 'start move 1': 'Headbutt', 'start move 2': 'Growl', + 'start move 3': 'Aurora Beam', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xe0\xff\x08\xc0\x82\x082')}, + 'Grimer': {'id': 13, 'dex': 88, 'hp': 80, 'atk': 80, 'def': 50, 'spd': 25, 'spc': 40, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 190, 'base exp': 90, 'start move 1': 'Pound', 'start move 2': 'Disable', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x00\x98\xc1*H\x02')}, + 'Muk': {'id': 136, 'dex': 89, 'hp': 105, 'atk': 105, 'def': 75, 'spd': 50, 'spc': 65, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 75, 'base exp': 157, 'start move 1': 'Pound', 'start move 2': 'Disable', + 'start move 3': 'Poison Gas', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0@\x98\xc1*H\x02')}, + 'Shellder': {'id': 23, 'dex': 90, 'hp': 30, 'atk': 65, 'def': 100, 'spd': 40, 'spc': 45, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 190, 'base exp': 97, 'start move 1': 'Tackle', + 'start move 2': 'Withdraw', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b' ?\x08\xe0KH\x13')}, + 'Cloyster': {'id': 139, 'dex': 91, 'hp': 50, 'atk': 95, 'def': 180, 'spd': 70, 'spc': 85, 'type1': 'Water', + 'type2': 'Ice', 'catch rate': 60, 'base exp': 203, 'start move 1': 'Withdraw', + 'start move 2': 'Supersonic', 'start move 3': 'Clamp', 'start move 4': 'Aurora Beam', 'growth rate': 5, + 'tms': bytearray(b' \x7f\x08\xe0KH\x13')}, + 'Gastly': {'id': 25, 'dex': 92, 'hp': 30, 'atk': 35, 'def': 30, 'spd': 80, 'spc': 100, 'type1': 'Ghost', + 'type2': 'Poison', 'catch rate': 190, 'base exp': 95, 'start move 1': 'Lick', + 'start move 2': 'Confuse Ray', 'start move 3': 'Night Shade', 'start move 4': 'No Move', + 'growth rate': 3, 'tms': bytearray(b' \x00\x98\xd1\nj\x02')}, + 'Haunter': {'id': 147, 'dex': 93, 'hp': 45, 'atk': 50, 'def': 45, 'spd': 95, 'spc': 115, 'type1': 'Ghost', + 'type2': 'Poison', 'catch rate': 90, 'base exp': 126, 'start move 1': 'Lick', + 'start move 2': 'Confuse Ray', 'start move 3': 'Night Shade', 'start move 4': 'No Move', + 'growth rate': 3, 'tms': bytearray(b' \x00\x98\xd1\nj\x02')}, + 'Gengar': {'id': 14, 'dex': 94, 'hp': 60, 'atk': 65, 'def': 60, 'spd': 110, 'spc': 130, 'type1': 'Ghost', + 'type2': 'Poison', 'catch rate': 45, 'base exp': 190, 'start move 1': 'Lick', + 'start move 2': 'Confuse Ray', 'start move 3': 'Night Shade', 'start move 4': 'No Move', + 'growth rate': 3, 'tms': bytearray(b'\xb1C\x9f\xd1\x8ej"')}, + 'Onix': {'id': 34, 'dex': 95, 'hp': 35, 'atk': 45, 'def': 160, 'spd': 70, 'spc': 30, 'type1': 'Rock', + 'type2': 'Ground', 'catch rate': 45, 'base exp': 108, 'start move 1': 'Tackle', 'start move 2': 'Screech', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x03\x08\xce\x8a\xc8"')}, + 'Drowzee': {'id': 48, 'dex': 96, 'hp': 60, 'atk': 48, 'def': 45, 'spd': 42, 'spc': 90, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 190, 'base exp': 102, 'start move 1': 'Pound', + 'start move 2': 'Hypnosis', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x03\x0f\xf0\x87:C')}, + 'Hypno': {'id': 129, 'dex': 97, 'hp': 85, 'atk': 73, 'def': 70, 'spd': 67, 'spc': 115, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 75, 'base exp': 165, 'start move 1': 'Pound', + 'start move 2': 'Hypnosis', 'start move 3': 'Disable', 'start move 4': 'Confusion', 'growth rate': 0, + 'tms': bytearray(b'\xb1C\x0f\xf0\x87:C')}, + 'Krabby': {'id': 78, 'dex': 98, 'hp': 30, 'atk': 105, 'def': 90, 'spd': 50, 'spc': 25, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 225, 'base exp': 115, 'start move 1': 'Bubble', 'start move 2': 'Leer', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4?\x08\xc0\x02\x086')}, + 'Kingler': {'id': 138, 'dex': 99, 'hp': 55, 'atk': 130, 'def': 115, 'spd': 75, 'spc': 50, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 60, 'base exp': 206, 'start move 1': 'Bubble', 'start move 2': 'Leer', + 'start move 3': 'Vicegrip', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4\x7f\x08\xc0\x02\x086')}, + 'Voltorb': {'id': 6, 'dex': 100, 'hp': 40, 'atk': 30, 'def': 50, 'spd': 100, 'spc': 55, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 190, 'base exp': 103, 'start move 1': 'Tackle', + 'start move 2': 'Screech', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' \x01\x88\xe1KXB')}, + 'Electrode': {'id': 141, 'dex': 101, 'hp': 60, 'atk': 50, 'def': 70, 'spd': 140, 'spc': 80, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 60, 'base exp': 150, 'start move 1': 'Tackle', + 'start move 2': 'Screech', 'start move 3': 'Sonicboom', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' A\x88\xe1\xcbXB')}, + 'Exeggcute': {'id': 12, 'dex': 102, 'hp': 60, 'atk': 40, 'def': 80, 'spd': 40, 'spc': 60, 'type1': 'Grass', + 'type2': 'Psychic', 'catch rate': 90, 'base exp': 98, 'start move 1': 'Barrage', + 'start move 2': 'Hypnosis', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b' \x03\x08\xf0\x1bh\x02')}, + 'Exeggutor': {'id': 10, 'dex': 103, 'hp': 95, 'atk': 95, 'def': 85, 'spd': 55, 'spc': 125, 'type1': 'Grass', + 'type2': 'Psychic', 'catch rate': 45, 'base exp': 212, 'start move 1': 'Barrage', + 'start move 2': 'Hypnosis', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b' C8\xf0\x1bh"')}, + 'Cubone': {'id': 17, 'dex': 104, 'hp': 50, 'atk': 50, 'def': 95, 'spd': 35, 'spc': 40, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 190, 'base exp': 87, 'start move 1': 'Bone Club', + 'start move 2': 'Growl', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1?\x0f\xce\xa2\x08"')}, + 'Marowak': {'id': 145, 'dex': 105, 'hp': 60, 'atk': 80, 'def': 110, 'spd': 45, 'spc': 50, 'type1': 'Ground', + 'type2': 'Ground', 'catch rate': 75, 'base exp': 124, 'start move 1': 'Bone Club', + 'start move 2': 'Growl', 'start move 3': 'Leer', 'start move 4': 'Focus Energy', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x7f\x0f\xce\xa2\x08"')}, + 'Hitmonlee': {'id': 43, 'dex': 106, 'hp': 50, 'atk': 120, 'def': 53, 'spd': 87, 'spc': 35, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 45, 'base exp': 139, 'start move 1': 'Double Kick', + 'start move 2': 'Meditate', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x03\x0f\xc0\xc6\x08"')}, + 'Hitmonchan': {'id': 44, 'dex': 107, 'hp': 50, 'atk': 105, 'def': 79, 'spd': 76, 'spc': 35, 'type1': 'Fighting', + 'type2': 'Fighting', 'catch rate': 45, 'base exp': 140, 'start move 1': 'Comet Punch', + 'start move 2': 'Agility', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x03\x0f\xc0\xc6\x08"')}, + 'Lickitung': {'id': 11, 'dex': 108, 'hp': 90, 'atk': 55, 'def': 75, 'spd': 30, 'spc': 60, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 45, 'base exp': 127, 'start move 1': 'Wrap', + 'start move 2': 'Supersonic', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb5\x7f\x8f\xc7\xa2\x086')}, + 'Koffing': {'id': 55, 'dex': 109, 'hp': 40, 'atk': 65, 'def': 95, 'spd': 35, 'spc': 60, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 190, 'base exp': 114, 'start move 1': 'Tackle', 'start move 2': 'Smog', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' \x00\x88\xc1*H\x02')}, + 'Weezing': {'id': 143, 'dex': 110, 'hp': 65, 'atk': 90, 'def': 120, 'spd': 60, 'spc': 85, 'type1': 'Poison', + 'type2': 'Poison', 'catch rate': 60, 'base exp': 173, 'start move 1': 'Tackle', 'start move 2': 'Smog', + 'start move 3': 'Sludge', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' @\x88\xc1*H\x02')}, + 'Rhyhorn': {'id': 18, 'dex': 111, 'hp': 80, 'atk': 85, 'def': 95, 'spd': 25, 'spc': 30, 'type1': 'Ground', + 'type2': 'Rock', 'catch rate': 120, 'base exp': 135, 'start move 1': 'Horn Attack', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xe0\x03\x88\xcf\xa2\x88"')}, + 'Rhydon': {'id': 1, 'dex': 112, 'hp': 105, 'atk': 130, 'def': 120, 'spd': 40, 'spc': 45, 'type1': 'Ground', + 'type2': 'Rock', 'catch rate': 60, 'base exp': 204, 'start move 1': 'Horn Attack', + 'start move 2': 'Stomp', 'start move 3': 'Tail Whip', 'start move 4': 'Fury Attack', 'growth rate': 5, + 'tms': bytearray(b'\xf1\xff\x8f\xcf\xa2\x882')}, + 'Chansey': {'id': 40, 'dex': 113, 'hp': 250, 'atk': 5, 'def': 5, 'spd': 50, 'spc': 105, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 30, 'base exp': 255, 'start move 1': 'Pound', + 'start move 2': 'Doubleslap', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 4, + 'tms': bytearray(b'\xb1\x7f\xaf\xf1\xb79c')}, + 'Tangela': {'id': 30, 'dex': 114, 'hp': 65, 'atk': 55, 'def': 115, 'spd': 60, 'spc': 100, 'type1': 'Grass', + 'type2': 'Grass', 'catch rate': 45, 'base exp': 166, 'start move 1': 'Constrict', + 'start move 2': 'Bind', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa4C8\xc0\x82\x08\x06')}, + 'Kangaskhan': {'id': 2, 'dex': 115, 'hp': 105, 'atk': 95, 'def': 80, 'spd': 90, 'spc': 40, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 45, 'base exp': 175, 'start move 1': 'Comet Punch', + 'start move 2': 'Rage', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x7f\x8f\xc7\xa2\x882')}, + 'Horsea': {'id': 92, 'dex': 116, 'hp': 30, 'atk': 40, 'def': 70, 'spd': 60, 'spc': 70, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 225, 'base exp': 83, 'start move 1': 'Bubble', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' ?\x08\xc0\xc2\x08\x12')}, + 'Seadra': {'id': 93, 'dex': 117, 'hp': 55, 'atk': 65, 'def': 95, 'spd': 85, 'spc': 95, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 75, 'base exp': 155, 'start move 1': 'Bubble', + 'start move 2': 'Smokescreen', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' \x7f\x08\xc0\xc2\x08\x12')}, + 'Goldeen': {'id': 157, 'dex': 118, 'hp': 45, 'atk': 67, 'def': 60, 'spd': 63, 'spc': 50, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 225, 'base exp': 111, 'start move 1': 'Peck', + 'start move 2': 'Tail Whip', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'`?\x08\xc0\xc2\x08\x12')}, + 'Seaking': {'id': 158, 'dex': 119, 'hp': 80, 'atk': 92, 'def': 65, 'spd': 68, 'spc': 80, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 60, 'base exp': 170, 'start move 1': 'Peck', + 'start move 2': 'Tail Whip', 'start move 3': 'Supersonic', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'`\x7f\x08\xc0\xc2\x08\x12')}, + 'Staryu': {'id': 27, 'dex': 120, 'hp': 30, 'atk': 45, 'def': 55, 'spd': 85, 'spc': 70, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 225, 'base exp': 106, 'start move 1': 'Tackle', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b' ?\x88\xf1\xc38S')}, + 'Starmie': {'id': 152, 'dex': 121, 'hp': 60, 'atk': 75, 'def': 85, 'spd': 115, 'spc': 100, 'type1': 'Water', + 'type2': 'Psychic', 'catch rate': 60, 'base exp': 207, 'start move 1': 'Tackle', + 'start move 2': 'Water Gun', 'start move 3': 'Harden', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b' \x7f\x88\xf1\xc38S')}, + 'Mr Mime': {'id': 42, 'dex': 122, 'hp': 40, 'atk': 45, 'def': 65, 'spd': 90, 'spc': 100, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 45, 'base exp': 136, 'start move 1': 'Confusion', + 'start move 2': 'Barrier', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1C\xaf\xf1\x878B')}, + 'Scyther': {'id': 26, 'dex': 123, 'hp': 70, 'atk': 110, 'def': 80, 'spd': 105, 'spc': 55, 'type1': 'Bug', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 187, 'start move 1': 'Quick Attack', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'$C\x08\xc0\xc2\x08\x06')}, + 'Jynx': {'id': 72, 'dex': 124, 'hp': 65, 'atk': 50, 'def': 35, 'spd': 95, 'spc': 95, 'type1': 'Ice', + 'type2': 'Psychic', 'catch rate': 45, 'base exp': 137, 'start move 1': 'Pound', + 'start move 2': 'Lovely Kiss', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1\x7f\x0f\xf0\x87(\x02')}, + 'Electabuzz': {'id': 53, 'dex': 125, 'hp': 65, 'atk': 83, 'def': 57, 'spd': 105, 'spc': 85, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 45, 'base exp': 156, 'start move 1': 'Quick Attack', + 'start move 2': 'Leer', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1C\x8f\xf1\xc78b')}, + 'Magmar': {'id': 51, 'dex': 126, 'hp': 65, 'atk': 95, 'def': 57, 'spd': 93, 'spc': 85, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 45, 'base exp': 167, 'start move 1': 'Ember', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb1C\x0f\xf0\xa6("')}, + 'Pinsir': {'id': 29, 'dex': 127, 'hp': 65, 'atk': 125, 'def': 100, 'spd': 85, 'spc': 55, 'type1': 'Bug', + 'type2': 'Bug', 'catch rate': 45, 'base exp': 200, 'start move 1': 'Vicegrip', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xa4C\r\xc0\x02\x08&')}, + 'Tauros': {'id': 60, 'dex': 128, 'hp': 75, 'atk': 100, 'def': 95, 'spd': 110, 'spc': 70, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 45, 'base exp': 211, 'start move 1': 'Tackle', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xe0s\x88\xc7\xa2\x08"')}, + 'Magikarp': {'id': 133, 'dex': 129, 'hp': 20, 'atk': 10, 'def': 55, 'spd': 80, 'spc': 20, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 255, 'base exp': 20, 'start move 1': 'Splash', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Gyarados': {'id': 22, 'dex': 130, 'hp': 95, 'atk': 125, 'def': 79, 'spd': 81, 'spc': 100, 'type1': 'Water', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 214, 'start move 1': 'Bite', + 'start move 2': 'Dragon Rage', 'start move 3': 'Leer', 'start move 4': 'Hydro Pump', 'growth rate': 5, + 'tms': bytearray(b'\xa0\x7f\xc8\xc1\xa3\x082')}, + 'Lapras': {'id': 19, 'dex': 131, 'hp': 130, 'atk': 85, 'def': 80, 'spd': 60, 'spc': 95, 'type1': 'Water', + 'type2': 'Ice', 'catch rate': 45, 'base exp': 219, 'start move 1': 'Water Gun', 'start move 2': 'Growl', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xe0\x7f\xe8\xd1\x83(2')}, + 'Ditto': {'id': 76, 'dex': 132, 'hp': 48, 'atk': 48, 'def': 48, 'spd': 48, 'spc': 48, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 35, 'base exp': 61, 'start move 1': 'Transform', + 'start move 2': 'No Move', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\x00\x00\x00\x00\x00\x00\x00')}, + 'Eevee': {'id': 102, 'dex': 133, 'hp': 55, 'atk': 55, 'def': 50, 'spd': 55, 'spc': 65, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 45, 'base exp': 92, 'start move 1': 'Tackle', + 'start move 2': 'Sand Attack', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0\x03\x08\xc0\xc3\x08\x02')}, + 'Vaporeon': {'id': 105, 'dex': 134, 'hp': 130, 'atk': 65, 'def': 60, 'spd': 65, 'spc': 110, 'type1': 'Water', + 'type2': 'Water', 'catch rate': 45, 'base exp': 196, 'start move 1': 'Tackle', + 'start move 2': 'Sand Attack', 'start move 3': 'Quick Attack', 'start move 4': 'Water Gun', + 'growth rate': 0, 'tms': bytearray(b'\xa0\x7f\x08\xc0\xc3\x08\x12')}, + 'Jolteon': {'id': 104, 'dex': 135, 'hp': 65, 'atk': 65, 'def': 60, 'spd': 130, 'spc': 110, 'type1': 'Electric', + 'type2': 'Electric', 'catch rate': 45, 'base exp': 197, 'start move 1': 'Tackle', + 'start move 2': 'Sand Attack', 'start move 3': 'Quick Attack', 'start move 4': 'Thundershock', + 'growth rate': 0, 'tms': bytearray(b'\xa0C\x88\xc1\xc3\x18B')}, + 'Flareon': {'id': 103, 'dex': 136, 'hp': 65, 'atk': 130, 'def': 60, 'spd': 65, 'spc': 110, 'type1': 'Fire', + 'type2': 'Fire', 'catch rate': 45, 'base exp': 198, 'start move 1': 'Tackle', + 'start move 2': 'Sand Attack', 'start move 3': 'Quick Attack', 'start move 4': 'Ember', + 'growth rate': 0, 'tms': bytearray(b'\xa0C\x08\xc0\xe3\x08\x02')}, + 'Porygon': {'id': 170, 'dex': 137, 'hp': 65, 'atk': 60, 'def': 70, 'spd': 40, 'spc': 75, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 45, 'base exp': 130, 'start move 1': 'Tackle', + 'start move 2': 'Sharpen', 'start move 3': 'Conversion', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b' s\x88\xf1\xc38C')}, + 'Omanyte': {'id': 98, 'dex': 138, 'hp': 35, 'atk': 40, 'def': 100, 'spd': 35, 'spc': 90, 'type1': 'Rock', + 'type2': 'Water', 'catch rate': 45, 'base exp': 120, 'start move 1': 'Water Gun', + 'start move 2': 'Withdraw', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0?\x08\xc0\x03\x08\x12')}, + 'Omastar': {'id': 99, 'dex': 139, 'hp': 70, 'atk': 60, 'def': 125, 'spd': 55, 'spc': 115, 'type1': 'Rock', + 'type2': 'Water', 'catch rate': 45, 'base exp': 199, 'start move 1': 'Water Gun', + 'start move 2': 'Withdraw', 'start move 3': 'Horn Attack', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xe0\x7f\r\xc0\x83\x08\x12')}, + 'Kabuto': {'id': 90, 'dex': 140, 'hp': 30, 'atk': 80, 'def': 90, 'spd': 55, 'spc': 45, 'type1': 'Rock', + 'type2': 'Water', 'catch rate': 45, 'base exp': 119, 'start move 1': 'Scratch', 'start move 2': 'Harden', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xa0?\x08\xc0\x03\x08\x12')}, + 'Kabutops': {'id': 91, 'dex': 141, 'hp': 60, 'atk': 115, 'def': 105, 'spd': 80, 'spc': 70, 'type1': 'Rock', + 'type2': 'Water', 'catch rate': 45, 'base exp': 201, 'start move 1': 'Scratch', + 'start move 2': 'Harden', 'start move 3': 'Absorb', 'start move 4': 'No Move', 'growth rate': 0, + 'tms': bytearray(b'\xb6\x7f\r\xc0\x83\x08\x12')}, + 'Aerodactyl': {'id': 171, 'dex': 142, 'hp': 80, 'atk': 105, 'def': 65, 'spd': 130, 'spc': 60, 'type1': 'Rock', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 202, 'start move 1': 'Wing Attack', + 'start move 2': 'Agility', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'*CH\xc0c\x0c\n')}, + 'Snorlax': {'id': 132, 'dex': 143, 'hp': 160, 'atk': 110, 'def': 65, 'spd': 30, 'spc': 65, 'type1': 'Normal', + 'type2': 'Normal', 'catch rate': 25, 'base exp': 154, 'start move 1': 'Headbutt', + 'start move 2': 'Amnesia', 'start move 3': 'Rest', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xb1\xff\xaf\xd7\xaf\xa82')}, + 'Articuno': {'id': 74, 'dex': 144, 'hp': 90, 'atk': 85, 'def': 100, 'spd': 85, 'spc': 125, 'type1': 'Ice', + 'type2': 'Flying', 'catch rate': 3, 'base exp': 215, 'start move 1': 'Peck', + 'start move 2': 'Ice Beam', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'*\x7f\x08\xc0C\x0c\n')}, + 'Zapdos': {'id': 75, 'dex': 145, 'hp': 90, 'atk': 90, 'def': 85, 'spd': 100, 'spc': 125, 'type1': 'Electric', + 'type2': 'Flying', 'catch rate': 3, 'base exp': 216, 'start move 1': 'Thundershock', + 'start move 2': 'Drill Peck', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'*C\x88\xc1C\x1cJ')}, + 'Moltres': {'id': 73, 'dex': 146, 'hp': 90, 'atk': 100, 'def': 90, 'spd': 90, 'spc': 125, 'type1': 'Fire', + 'type2': 'Flying', 'catch rate': 3, 'base exp': 217, 'start move 1': 'Peck', + 'start move 2': 'Fire Spin', 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'*C\x08\xc0c\x0c\n')}, + 'Dratini': {'id': 88, 'dex': 147, 'hp': 41, 'atk': 64, 'def': 45, 'spd': 50, 'spc': 50, 'type1': 'Dragon', + 'type2': 'Dragon', 'catch rate': 45, 'base exp': 67, 'start move 1': 'Wrap', 'start move 2': 'Leer', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xa0?\xc8\xc1\xe3\x18\x12')}, + 'Dragonair': {'id': 89, 'dex': 148, 'hp': 61, 'atk': 84, 'def': 65, 'spd': 70, 'spc': 70, 'type1': 'Dragon', + 'type2': 'Dragon', 'catch rate': 45, 'base exp': 144, 'start move 1': 'Wrap', 'start move 2': 'Leer', + 'start move 3': 'Thunder Wave', 'start move 4': 'No Move', 'growth rate': 5, + 'tms': bytearray(b'\xe0?\xc8\xc1\xe3\x18\x12')}, + 'Dragonite': {'id': 66, 'dex': 149, 'hp': 91, 'atk': 134, 'def': 95, 'spd': 80, 'spc': 100, 'type1': 'Dragon', + 'type2': 'Flying', 'catch rate': 45, 'base exp': 218, 'start move 1': 'Wrap', 'start move 2': 'Leer', + 'start move 3': 'Thunder Wave', 'start move 4': 'Agility', 'growth rate': 5, + 'tms': bytearray(b'\xe2\x7f\xc8\xc1\xe3\x182')}, + 'Mewtwo': {'id': 131, 'dex': 150, 'hp': 106, 'atk': 110, 'def': 90, 'spd': 130, 'spc': 154, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 3, 'base exp': 220, 'start move 1': 'Confusion', + 'start move 2': 'Disable', 'start move 3': 'Swift', 'start move 4': 'Psychic', 'growth rate': 5, + 'tms': bytearray(b'\xb1\xff\xaf\xf1\xaf8c')}, + 'Mew': {'id': 21, 'dex': 151, 'hp': 100, 'atk': 100, 'def': 100, 'spd': 100, 'spc': 100, 'type1': 'Psychic', + 'type2': 'Psychic', 'catch rate': 45, 'base exp': 64, 'start move 1': 'Pound', 'start move 2': 'No Move', + 'start move 3': 'No Move', 'start move 4': 'No Move', 'growth rate': 3, + 'tms': bytearray(b'\xff\xff\xff\xff\xff\xff\xff')}} + + + +evolves_from = { + "Ivysaur": "Bulbasaur", + "Venusaur": "Ivysaur", + "Charmeleon": "Charmander", + "Charizard": "Charmeleon", + "Wartortle": "Squirtle", + "Blastoise": "Wartortle", + "Metapod": "Caterpie", + "Butterfree": "Metapod", + "Kakuna": "Weedle", + "Beedrill": "Kakuna", + "Pidgeotto": "Pidgey", + "Pidgeot": "Pidgeotto", + "Raticate": "Rattata", + "Fearow": "Spearow", + "Arbok": "Ekans", + "Raichu": "Pikachu", + "Sandslash": "Sandshrew", + "Nidorina": "Nidoran F", + "Nidoqueen": "Nidorina", + "Nidorino": "Nidoran M", + "Nidoking": "Nidorino", + "Clefable": "Clefairy", + "Ninetales": "Vulpix", + "Wigglytuff": "Jigglypuff", + "Golbat": "Zubat", + "Gloom": "Oddish", + "Vileplume": "Gloom", + "Parasect": "Paras", + "Venomoth": "Venonat", + "Dugtrio": "Diglett", + "Persian": "Meowth", + "Golduck": "Psyduck", + "Primeape": "Mankey", + "Arcanine": "Growlithe", + "Poliwhirl": "Poliwag", + "Poliwrath": "Poliwhirl", + "Kadabra": "Abra", + "Alakazam": "Kadabra", + "Machoke": "Machop", + "Machamp": "Machoke", + "Weepinbell": "Bellsprout", + "Victreebel": "Weepinbell", + "Tentacruel": "Tentacool", + "Graveler": "Geodude", + "Golem": "Graveler", + "Rapidash": "Ponyta", + "Slowbro": "Slowpoke", + "Magneton": "Magnemite", + "Dodrio": "Doduo", + "Dewgong": "Seel", + "Muk": "Grimer", + "Cloyster": "Shellder", + "Haunter": "Gastly", + "Gengar": "Haunter", + "Hypno": "Drowzee", + "Kingler": "Krabby", + "Electrode": "Voltorb", + "Exeggutor": "Exeggcute", + "Marowak": "Cubone", + "Weezing": "Koffing", + "Rhydon": "Rhyhorn", + "Seadra": "Horsea", + "Seaking": "Goldeen", + "Starmie": "Staryu", + "Gyarados": "Magikarp", + "Vaporeon": "Eevee", + "Jolteon": "Eevee", + "Flareon": "Eevee", + "Omastar": "Omanyte", + "Kabutops": "Kabuto", + "Dragonair": "Dratini", + "Dragonite": "Dragonair" +} + +evolves_to = {} +for from_mon, to_mon in zip(evolves_from.values(), evolves_from.keys()): + if from_mon != "Eevee": + evolves_to[from_mon] = to_mon + +# basic_three_stage_pokemon = [] +# for mon in evolves_to.keys(): +# if evolves_to[mon] in evolves_to.keys(): +# basic_three_stage_pokemon.append(mon) +# print(basic_three_stage_pokemon) + +learnsets = { + 'Rhydon': ['Stomp', 'Tail Whip', 'Fury Attack', 'Horn Drill', 'Leer', 'Take Down'], + 'Kangaskhan': ['Bite', 'Tail Whip', 'Mega Punch', 'Leer', 'Dizzy Punch'], + 'Nidoran M': ['Horn Attack', 'Poison Sting', 'Focus Energy', 'Fury Attack', 'Horn Drill', 'Double Kick'], + 'Clefairy': ['Sing', 'Doubleslap', 'Minimize', 'Metronome', 'Defense Curl', 'Light Screen'], + 'Spearow': ['Leer', 'Fury Attack', 'Mirror Move', 'Drill Peck', 'Agility'], + 'Voltorb': ['Sonicboom', 'Selfdestruct', 'Light Screen', 'Swift', 'Explosion'], + 'Nidoking': ['Horn Attack', 'Poison Sting', 'Thrash'], + 'Slowbro': ['Disable', 'Headbutt', 'Growl', 'Water Gun', 'Withdraw', 'Amnesia', 'Psychic'], + 'Ivysaur': ['Leech Seed', 'Vine Whip', 'Poisonpowder', 'Razor Leaf', 'Growth', 'Sleep Powder', 'Solarbeam'], + 'Exeggutor': ['Stomp'], 'Lickitung': ['Stomp', 'Disable', 'Defense Curl', 'Slam', 'Screech'], + 'Exeggcute': ['Reflect', 'Leech Seed', 'Stun Spore', 'Poisonpowder', 'Solarbeam', 'Sleep Powder'], + 'Grimer': ['Poison Gas', 'Minimize', 'Sludge', 'Harden', 'Screech', 'Acid Armor'], + 'Gengar': ['Hypnosis', 'Dream Eater'], + 'Nidoran F': ['Scratch', 'Poison Sting', 'Tail Whip', 'Bite', 'Fury Swipes', 'Double Kick'], + 'Nidoqueen': ['Scratch', 'Poison Sting', 'Body Slam'], + 'Cubone': ['Leer', 'Focus Energy', 'Thrash', 'Bonemerang', 'Rage'], + 'Rhyhorn': ['Stomp', 'Tail Whip', 'Fury Attack', 'Horn Drill', 'Leer', 'Take Down'], + 'Lapras': ['Sing', 'Mist', 'Body Slam', 'Confuse Ray', 'Ice Beam', 'Hydro Pump'], + 'Mew': ['Transform', 'Mega Punch', 'Metronome', 'Psychic'], + 'Gyarados': ['Bite', 'Dragon Rage', 'Leer', 'Hydro Pump', 'Hyper Beam'], + 'Shellder': ['Supersonic', 'Clamp', 'Aurora Beam', 'Leer', 'Ice Beam'], + 'Tentacool': ['Supersonic', 'Wrap', 'Poison Sting', 'Water Gun', 'Constrict', 'Barrier', 'Screech', 'Hydro Pump'], + 'Gastly': ['Hypnosis', 'Dream Eater'], + 'Scyther': ['Leer', 'Focus Energy', 'Double Team', 'Slash', 'Swords Dance', 'Agility'], + 'Staryu': ['Water Gun', 'Harden', 'Recover', 'Swift', 'Minimize', 'Light Screen', 'Hydro Pump'], + 'Blastoise': ['Bubble', 'Water Gun', 'Bite', 'Withdraw', 'Skull Bash', 'Hydro Pump'], + 'Pinsir': ['Seismic Toss', 'Guillotine', 'Focus Energy', 'Harden', 'Slash', 'Swords Dance'], + 'Tangela': ['Absorb', 'Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Slam', 'Growth'], + 'Growlithe': ['Ember', 'Leer', 'Take Down', 'Agility', 'Flamethrower'], + 'Onix': ['Bind', 'Rock Throw', 'Rage', 'Slam', 'Harden'], + 'Fearow': ['Leer', 'Fury Attack', 'Mirror Move', 'Drill Peck', 'Agility'], + 'Pidgey': ['Sand Attack', 'Quick Attack', 'Whirlwind', 'Wing Attack', 'Agility', 'Mirror Move'], + 'Slowpoke': ['Disable', 'Headbutt', 'Growl', 'Water Gun', 'Amnesia', 'Psychic'], + 'Kadabra': ['Confusion', 'Disable', 'Psybeam', 'Recover', 'Psychic', 'Reflect'], + 'Graveler': ['Defense Curl', 'Rock Throw', 'Selfdestruct', 'Harden', 'Earthquake', 'Explosion'], + 'Chansey': ['Sing', 'Growl', 'Minimize', 'Defense Curl', 'Light Screen', 'Double Edge'], + 'Machoke': ['Low Kick', 'Leer', 'Focus Energy', 'Seismic Toss', 'Submission'], + 'Mr Mime': ['Confusion', 'Light Screen', 'Doubleslap', 'Meditate', 'Substitute'], + 'Hitmonlee': ['Rolling Kick', 'Jump Kick', 'Focus Energy', 'Hi Jump Kick', 'Mega Kick'], + 'Hitmonchan': ['Fire Punch', 'Ice Punch', 'Thunderpunch', 'Mega Punch', 'Counter'], + 'Arbok': ['Poison Sting', 'Bite', 'Glare', 'Screech', 'Acid'], + 'Parasect': ['Stun Spore', 'Leech Life', 'Spore', 'Slash', 'Growth'], + 'Psyduck': ['Tail Whip', 'Disable', 'Confusion', 'Fury Swipes', 'Hydro Pump'], + 'Drowzee': ['Disable', 'Confusion', 'Headbutt', 'Poison Gas', 'Psychic', 'Meditate'], + 'Golem': ['Defense Curl', 'Rock Throw', 'Selfdestruct', 'Harden', 'Earthquake', 'Explosion'], + 'Magmar': ['Leer', 'Confuse Ray', 'Fire Punch', 'Smokescreen', 'Smog', 'Flamethrower'], + 'Electabuzz': ['Thundershock', 'Screech', 'Thunderpunch', 'Light Screen', 'Thunder'], + 'Magneton': ['Sonicboom', 'Thundershock', 'Supersonic', 'Thunder Wave', 'Swift', 'Screech'], + 'Koffing': ['Sludge', 'Smokescreen', 'Selfdestruct', 'Haze', 'Explosion'], + 'Mankey': ['Karate Chop', 'Fury Swipes', 'Focus Energy', 'Seismic Toss', 'Thrash'], + 'Seel': ['Growl', 'Aurora Beam', 'Rest', 'Take Down', 'Ice Beam'], + 'Diglett': ['Growl', 'Dig', 'Sand Attack', 'Slash', 'Earthquake'], + 'Tauros': ['Stomp', 'Tail Whip', 'Leer', 'Rage', 'Take Down'], + 'Farfetchd': ['Leer', 'Fury Attack', 'Swords Dance', 'Agility', 'Slash'], + 'Venonat': ['Poisonpowder', 'Leech Life', 'Stun Spore', 'Psybeam', 'Sleep Powder', 'Psychic'], + 'Dragonite': ['Thunder Wave', 'Agility', 'Slam', 'Dragon Rage', 'Hyper Beam'], + 'Doduo': ['Growl', 'Fury Attack', 'Drill Peck', 'Rage', 'Tri Attack', 'Agility'], + 'Poliwag': ['Hypnosis', 'Water Gun', 'Doubleslap', 'Body Slam', 'Amnesia', 'Hydro Pump'], + 'Jynx': ['Lick', 'Doubleslap', 'Ice Punch', 'Body Slam', 'Thrash', 'Blizzard'], + 'Moltres': ['Leer', 'Agility', 'Sky Attack'], + 'Articuno': ['Blizzard', 'Agility', 'Mist'], + 'Zapdos': ['Thunder', 'Agility', 'Light Screen'], + 'Meowth': ['Bite', 'Pay Day', 'Screech', 'Fury Swipes', 'Slash'], + 'Krabby': ['Vicegrip', 'Guillotine', 'Stomp', 'Crabhammer', 'Harden'], + 'Vulpix': ['Quick Attack', 'Roar', 'Confuse Ray', 'Flamethrower', 'Fire Spin'], + 'Pikachu': ['Thunder Wave', 'Quick Attack', 'Swift', 'Agility', 'Thunder'], + 'Dratini': ['Thunder Wave', 'Agility', 'Slam', 'Dragon Rage', 'Hyper Beam'], + 'Dragonair': ['Thunder Wave', 'Agility', 'Slam', 'Dragon Rage', 'Hyper Beam'], + 'Kabuto': ['Absorb', 'Slash', 'Leer', 'Hydro Pump'], + 'Kabutops': ['Absorb', 'Slash', 'Leer', 'Hydro Pump'], + 'Horsea': ['Smokescreen', 'Leer', 'Water Gun', 'Agility', 'Hydro Pump'], + 'Seadra': ['Smokescreen', 'Leer', 'Water Gun', 'Agility', 'Hydro Pump'], + 'Sandshrew': ['Sand Attack', 'Slash', 'Poison Sting', 'Swift', 'Fury Swipes'], + 'Sandslash': ['Sand Attack', 'Slash', 'Poison Sting', 'Swift', 'Fury Swipes'], + 'Omanyte': ['Horn Attack', 'Leer', 'Spike Cannon', 'Hydro Pump'], + 'Omastar': ['Horn Attack', 'Leer', 'Spike Cannon', 'Hydro Pump'], + 'Jigglypuff': ['Pound', 'Disable', 'Defense Curl', 'Doubleslap', 'Rest', 'Body Slam', 'Double Edge'], + 'Eevee': ['Quick Attack', 'Tail Whip', 'Bite', 'Take Down'], + 'Flareon': ['Quick Attack', 'Ember', 'Tail Whip', 'Bite', 'Leer', 'Fire Spin', 'Rage', 'Flamethrower'], + 'Jolteon': ['Quick Attack', 'Thundershock', 'Tail Whip', 'Thunder Wave', 'Double Kick', 'Agility', 'Pin Missile', 'Thunder'], + 'Vaporeon': ['Quick Attack', 'Water Gun', 'Tail Whip', 'Bite', 'Acid Armor', 'Haze', 'Mist', 'Hydro Pump'], + 'Machop': ['Low Kick', 'Leer', 'Focus Energy', 'Seismic Toss', 'Submission'], + 'Zubat': ['Supersonic', 'Bite', 'Confuse Ray', 'Wing Attack', 'Haze'], + 'Ekans': ['Poison Sting', 'Bite', 'Glare', 'Screech', 'Acid'], + 'Paras': ['Stun Spore', 'Leech Life', 'Spore', 'Slash', 'Growth'], + 'Poliwhirl': ['Hypnosis', 'Water Gun', 'Doubleslap', 'Body Slam', 'Amnesia', 'Hydro Pump'], + 'Poliwrath': ['Hypnosis', 'Water Gun'], + 'Beedrill': ['Fury Attack', 'Focus Energy', 'Twineedle', 'Rage', 'Pin Missile', 'Agility'], + 'Dodrio': ['Growl', 'Fury Attack', 'Drill Peck', 'Rage', 'Tri Attack', 'Agility'], + 'Primeape': ['Karate Chop', 'Fury Swipes', 'Focus Energy', 'Seismic Toss', 'Thrash'], + 'Dugtrio': ['Growl', 'Dig', 'Sand Attack', 'Slash', 'Earthquake'], + 'Venomoth': ['Poisonpowder', 'Leech Life', 'Stun Spore', 'Psybeam', 'Sleep Powder', 'Psychic'], + 'Dewgong': ['Growl', 'Aurora Beam', 'Rest', 'Take Down', 'Ice Beam'], + 'Butterfree': ['Confusion', 'Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Supersonic', 'Whirlwind', 'Psybeam'], + 'Machamp': ['Low Kick', 'Leer', 'Focus Energy', 'Seismic Toss', 'Submission'], + 'Golduck': ['Tail Whip', 'Disable', 'Confusion', 'Fury Swipes', 'Hydro Pump'], + 'Hypno': ['Disable', 'Confusion', 'Headbutt', 'Poison Gas', 'Psychic', 'Meditate'], + 'Golbat': ['Supersonic', 'Bite', 'Confuse Ray', 'Wing Attack', 'Haze'], + 'Mewtwo': ['Barrier', 'Psychic', 'Recover', 'Mist', 'Amnesia'], + 'Snorlax': ['Body Slam', 'Harden', 'Double Edge', 'Hyper Beam'], + 'Magikarp': ['Tackle'], + 'Muk': ['Poison Gas', 'Minimize', 'Sludge', 'Harden', 'Screech', 'Acid Armor'], + 'Kingler': ['Vicegrip', 'Guillotine', 'Stomp', 'Crabhammer', 'Harden'], + 'Cloyster': ['Spike Cannon'], + 'Electrode': ['Sonicboom', 'Selfdestruct', 'Light Screen', 'Swift', 'Explosion'], + 'Weezing': ['Sludge', 'Smokescreen', 'Selfdestruct', 'Haze', 'Explosion'], + 'Persian': ['Bite', 'Pay Day', 'Screech', 'Fury Swipes', 'Slash'], + 'Marowak': ['Leer', 'Focus Energy', 'Thrash', 'Bonemerang', 'Rage'], + 'Haunter': ['Hypnosis', 'Dream Eater'], + 'Alakazam': ['Confusion', 'Disable', 'Psybeam', 'Recover', 'Psychic', 'Reflect'], + 'Pidgeotto': ['Sand Attack', 'Quick Attack', 'Whirlwind', 'Wing Attack', 'Agility', 'Mirror Move'], + 'Pidgeot': ['Sand Attack', 'Quick Attack', 'Whirlwind', 'Wing Attack', 'Agility', 'Mirror Move'], + 'Bulbasaur': ['Leech Seed', 'Vine Whip', 'Poisonpowder', 'Razor Leaf', 'Growth', 'Sleep Powder', 'Solarbeam'], + 'Venusaur': ['Leech Seed', 'Vine Whip', 'Poisonpowder', 'Razor Leaf', 'Growth', 'Sleep Powder', 'Solarbeam'], + 'Tentacruel': ['Supersonic', 'Wrap', 'Poison Sting', 'Water Gun', 'Constrict', 'Barrier', 'Screech', 'Hydro Pump'], + 'Goldeen': ['Supersonic', 'Horn Attack', 'Fury Attack', 'Waterfall', 'Horn Drill', 'Agility'], + 'Seaking': ['Supersonic', 'Horn Attack', 'Fury Attack', 'Waterfall', 'Horn Drill', 'Agility'], + 'Ponyta': ['Tail Whip', 'Stomp', 'Growl', 'Fire Spin', 'Take Down', 'Agility'], + 'Rapidash': ['Tail Whip', 'Stomp', 'Growl', 'Fire Spin', 'Take Down', 'Agility'], + 'Rattata': ['Quick Attack', 'Hyper Fang', 'Focus Energy', 'Super Fang'], + 'Raticate': ['Quick Attack', 'Hyper Fang', 'Focus Energy', 'Super Fang'], + 'Nidorino': ['Horn Attack', 'Poison Sting', 'Focus Energy', 'Fury Attack', 'Horn Drill', 'Double Kick'], + 'Nidorina': ['Scratch', 'Poison Sting', 'Tail Whip', 'Bite', 'Fury Swipes', 'Double Kick'], + 'Geodude': ['Defense Curl', 'Rock Throw', 'Selfdestruct', 'Harden', 'Earthquake', 'Explosion'], + 'Porygon': ['Psybeam', 'Recover', 'Agility', 'Tri Attack'], + 'Aerodactyl': ['Supersonic', 'Bite', 'Take Down', 'Hyper Beam'], + 'Magnemite': ['Sonicboom', 'Thundershock', 'Supersonic', 'Thunder Wave', 'Swift', 'Screech'], + 'Charmander': ['Ember', 'Leer', 'Rage', 'Slash', 'Flamethrower', 'Fire Spin'], + 'Squirtle': ['Bubble', 'Water Gun', 'Bite', 'Withdraw', 'Skull Bash', 'Hydro Pump'], + 'Charmeleon': ['Ember', 'Leer', 'Rage', 'Slash', 'Flamethrower', 'Fire Spin'], + 'Wartortle': ['Bubble', 'Water Gun', 'Bite', 'Withdraw', 'Skull Bash', 'Hydro Pump'], + 'Charizard': ['Ember', 'Leer', 'Rage', 'Slash', 'Flamethrower', 'Fire Spin'], + 'Oddish': ['Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Acid', 'Petal Dance', 'Solarbeam'], + 'Gloom': ['Poisonpowder', 'Stun Spore', 'Sleep Powder', 'Acid', 'Petal Dance', 'Solarbeam'], + 'Vileplume': ['Poisonpowder', 'Stun Spore', 'Sleep Powder'], + 'Bellsprout': ['Wrap', 'Poisonpowder', 'Sleep Powder', 'Stun Spore', 'Acid', 'Razor Leaf', 'Slam'], + 'Weepinbell': ['Wrap', 'Poisonpowder', 'Sleep Powder', 'Stun Spore', 'Acid', 'Razor Leaf', 'Slam'], + 'Victreebel': ['Wrap', 'Poisonpowder', 'Sleep Powder'] +} + +moves = { + 'No Move': {'id': 0, 'power': 0, 'type': 'Typeless', 'accuracy': 0, 'pp': 0}, + 'Pound': {'id': 1, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35}, + 'Karate Chop': {'id': 2, 'power': 50, 'type': 'Normal', 'accuracy': 100, 'pp': 25}, + 'Doubleslap': {'id': 3, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 10}, + 'Comet Punch': {'id': 4, 'power': 18, 'type': 'Normal', 'accuracy': 85, 'pp': 15}, + 'Mega Punch': {'id': 5, 'power': 80, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Pay Day': {'id': 6, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Fire Punch': {'id': 7, 'power': 75, 'type': 'Fire', 'accuracy': 100, 'pp': 15}, + 'Ice Punch': {'id': 8, 'power': 75, 'type': 'Ice', 'accuracy': 100, 'pp': 15}, + 'Thunderpunch': {'id': 9, 'power': 75, 'type': 'Electric', 'accuracy': 100, 'pp': 15}, + 'Scratch': {'id': 10, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35}, + 'Vicegrip': {'id': 11, 'power': 55, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Guillotine': {'id': 12, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5}, + 'Razor Wind': {'id': 13, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 10}, + 'Swords Dance': {'id': 14, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Cut': {'id': 15, 'power': 50, 'type': 'Normal', 'accuracy': 95, 'pp': 30}, + 'Gust': {'id': 16, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 35}, + 'Wing Attack': {'id': 17, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35}, + 'Whirlwind': {'id': 18, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Fly': {'id': 19, 'power': 70, 'type': 'Flying', 'accuracy': 95, 'pp': 15}, + 'Bind': {'id': 20, 'power': 15, 'type': 'Normal', 'accuracy': 75, 'pp': 20}, + 'Slam': {'id': 21, 'power': 80, 'type': 'Normal', 'accuracy': 75, 'pp': 20}, + 'Vine Whip': {'id': 22, 'power': 35, 'type': 'Grass', 'accuracy': 100, 'pp': 10}, + 'Stomp': {'id': 23, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Double Kick': {'id': 24, 'power': 30, 'type': 'Fighting', 'accuracy': 100, 'pp': 30}, + 'Mega Kick': {'id': 25, 'power': 120, 'type': 'Normal', 'accuracy': 75, 'pp': 5}, + 'Jump Kick': {'id': 26, 'power': 70, 'type': 'Fighting', 'accuracy': 95, 'pp': 25}, + 'Rolling Kick': {'id': 27, 'power': 60, 'type': 'Fighting', 'accuracy': 85, 'pp': 15}, + 'Sand Attack': {'id': 28, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Headbutt': {'id': 29, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Horn Attack': {'id': 30, 'power': 65, 'type': 'Normal', 'accuracy': 100, 'pp': 25}, + 'Fury Attack': {'id': 31, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Horn Drill': {'id': 32, 'power': 1, 'type': 'Normal', 'accuracy': 30, 'pp': 5}, + 'Tackle': {'id': 33, 'power': 35, 'type': 'Normal', 'accuracy': 95, 'pp': 35}, + 'Body Slam': {'id': 34, 'power': 85, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Wrap': {'id': 35, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Take Down': {'id': 36, 'power': 90, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Thrash': {'id': 37, 'power': 90, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Double Edge': {'id': 38, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Tail Whip': {'id': 39, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Poison Sting': {'id': 40, 'power': 15, 'type': 'Poison', 'accuracy': 100, 'pp': 35}, + 'Twineedle': {'id': 41, 'power': 25, 'type': 'Bug', 'accuracy': 100, 'pp': 20}, + 'Pin Missile': {'id': 42, 'power': 14, 'type': 'Bug', 'accuracy': 85, 'pp': 20}, + 'Leer': {'id': 43, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Bite': {'id': 44, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 25}, + 'Growl': {'id': 45, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40}, + 'Roar': {'id': 46, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Sing': {'id': 47, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 15}, + 'Supersonic': {'id': 48, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20}, + 'Sonicboom': {'id': 49, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 20}, + 'Disable': {'id': 50, 'power': 0, 'type': 'Normal', 'accuracy': 55, 'pp': 20}, + 'Acid': {'id': 51, 'power': 40, 'type': 'Poison', 'accuracy': 100, 'pp': 30}, + 'Ember': {'id': 52, 'power': 40, 'type': 'Fire', 'accuracy': 100, 'pp': 25}, + 'Flamethrower': {'id': 53, 'power': 95, 'type': 'Fire', 'accuracy': 100, 'pp': 15}, + 'Mist': {'id': 54, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30}, + 'Water Gun': {'id': 55, 'power': 40, 'type': 'Water', 'accuracy': 100, 'pp': 25}, + 'Hydro Pump': {'id': 56, 'power': 120, 'type': 'Water', 'accuracy': 80, 'pp': 5}, + 'Surf': {'id': 57, 'power': 95, 'type': 'Water', 'accuracy': 100, 'pp': 15}, + 'Ice Beam': {'id': 58, 'power': 95, 'type': 'Ice', 'accuracy': 100, 'pp': 10}, + 'Blizzard': {'id': 59, 'power': 120, 'type': 'Ice', 'accuracy': 90, 'pp': 5}, + 'Psybeam': {'id': 60, 'power': 65, 'type': 'Psychic', 'accuracy': 100, 'pp': 20}, + 'Bubblebeam': {'id': 61, 'power': 65, 'type': 'Water', 'accuracy': 100, 'pp': 20}, + 'Aurora Beam': {'id': 62, 'power': 65, 'type': 'Ice', 'accuracy': 100, 'pp': 20}, + 'Hyper Beam': {'id': 63, 'power': 150, 'type': 'Normal', 'accuracy': 90, 'pp': 5}, + 'Peck': {'id': 64, 'power': 35, 'type': 'Flying', 'accuracy': 100, 'pp': 35}, + 'Drill Peck': {'id': 65, 'power': 80, 'type': 'Flying', 'accuracy': 100, 'pp': 20}, + 'Submission': {'id': 66, 'power': 80, 'type': 'Fighting', 'accuracy': 80, 'pp': 25}, + 'Low Kick': {'id': 67, 'power': 50, 'type': 'Fighting', 'accuracy': 90, 'pp': 20}, + 'Counter': {'id': 68, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20}, + 'Seismic Toss': {'id': 69, 'power': 1, 'type': 'Fighting', 'accuracy': 100, 'pp': 20}, + 'Strength': {'id': 70, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Absorb': {'id': 71, 'power': 20, 'type': 'Grass', 'accuracy': 100, 'pp': 20}, + 'Mega Drain': {'id': 72, 'power': 40, 'type': 'Grass', 'accuracy': 100, 'pp': 10}, + 'Leech Seed': {'id': 73, 'power': 0, 'type': 'Grass', 'accuracy': 90, 'pp': 10}, + 'Growth': {'id': 74, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40}, + 'Razor Leaf': {'id': 75, 'power': 55, 'type': 'Grass', 'accuracy': 95, 'pp': 25}, + 'Solarbeam': {'id': 76, 'power': 120, 'type': 'Grass', 'accuracy': 100, 'pp': 10}, + 'Poisonpowder': {'id': 77, 'power': 0, 'type': 'Poison', 'accuracy': 75, 'pp': 35}, + 'Stun Spore': {'id': 78, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 30}, + 'Sleep Powder': {'id': 79, 'power': 0, 'type': 'Grass', 'accuracy': 75, 'pp': 15}, + 'Petal Dance': {'id': 80, 'power': 70, 'type': 'Grass', 'accuracy': 100, 'pp': 20}, + 'String Shot': {'id': 81, 'power': 0, 'type': 'Bug', 'accuracy': 95, 'pp': 40}, + 'Dragon Rage': {'id': 82, 'power': 1, 'type': 'Dragon', 'accuracy': 100, 'pp': 10}, + 'Fire Spin': {'id': 83, 'power': 15, 'type': 'Fire', 'accuracy': 70, 'pp': 15}, + 'Thundershock': {'id': 84, 'power': 40, 'type': 'Electric', 'accuracy': 100, 'pp': 30}, + 'Thunderbolt': {'id': 85, 'power': 95, 'type': 'Electric', 'accuracy': 100, 'pp': 15}, + 'Thunder Wave': {'id': 86, 'power': 0, 'type': 'Electric', 'accuracy': 100, 'pp': 20}, + 'Thunder': {'id': 87, 'power': 120, 'type': 'Electric', 'accuracy': 70, 'pp': 10}, + 'Rock Throw': {'id': 88, 'power': 50, 'type': 'Rock', 'accuracy': 65, 'pp': 15}, + 'Earthquake': {'id': 89, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10}, + 'Fissure': {'id': 90, 'power': 1, 'type': 'Ground', 'accuracy': 30, 'pp': 5}, + 'Dig': {'id': 91, 'power': 100, 'type': 'Ground', 'accuracy': 100, 'pp': 10}, + 'Toxic': {'id': 92, 'power': 0, 'type': 'Poison', 'accuracy': 85, 'pp': 10}, + 'Confusion': {'id': 93, 'power': 50, 'type': 'Psychic', 'accuracy': 100, 'pp': 25}, + 'Psychic': {'id': 94, 'power': 90, 'type': 'Psychic', 'accuracy': 100, 'pp': 10}, + 'Hypnosis': {'id': 95, 'power': 0, 'type': 'Psychic', 'accuracy': 60, 'pp': 20}, + 'Meditate': {'id': 96, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 40}, + 'Agility': {'id': 97, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30}, + 'Quick Attack': {'id': 98, 'power': 40, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Rage': {'id': 99, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Teleport': {'id': 100, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20}, + 'Night Shade': {'id': 101, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 15}, + 'Mimic': {'id': 102, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Screech': {'id': 103, 'power': 0, 'type': 'Normal', 'accuracy': 85, 'pp': 40}, + 'Double Team': {'id': 104, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Recover': {'id': 105, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Harden': {'id': 106, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Minimize': {'id': 107, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Smokescreen': {'id': 108, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Confuse Ray': {'id': 109, 'power': 0, 'type': 'Ghost', 'accuracy': 100, 'pp': 10}, + 'Withdraw': {'id': 110, 'power': 0, 'type': 'Water', 'accuracy': 100, 'pp': 40}, + 'Defense Curl': {'id': 111, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40}, + 'Barrier': {'id': 112, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30}, + 'Light Screen': {'id': 113, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 30}, + 'Haze': {'id': 114, 'power': 0, 'type': 'Ice', 'accuracy': 100, 'pp': 30}, + 'Reflect': {'id': 115, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20}, + 'Focus Energy': {'id': 116, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Bide': {'id': 117, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Metronome': {'id': 118, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Mirror Move': {'id': 119, 'power': 0, 'type': 'Flying', 'accuracy': 100, 'pp': 20}, + 'Selfdestruct': {'id': 120, 'power': 130, 'type': 'Normal', 'accuracy': 100, 'pp': 5}, + 'Egg Bomb': {'id': 121, 'power': 100, 'type': 'Normal', 'accuracy': 75, 'pp': 10}, + 'Lick': {'id': 122, 'power': 20, 'type': 'Ghost', 'accuracy': 100, 'pp': 30}, + 'Smog': {'id': 123, 'power': 20, 'type': 'Poison', 'accuracy': 70, 'pp': 20}, + 'Sludge': {'id': 124, 'power': 65, 'type': 'Poison', 'accuracy': 100, 'pp': 20}, + 'Bone Club': {'id': 125, 'power': 65, 'type': 'Ground', 'accuracy': 85, 'pp': 20}, + 'Fire Blast': {'id': 126, 'power': 120, 'type': 'Fire', 'accuracy': 85, 'pp': 5}, + 'Waterfall': {'id': 127, 'power': 80, 'type': 'Water', 'accuracy': 100, 'pp': 15}, + 'Clamp': {'id': 128, 'power': 35, 'type': 'Water', 'accuracy': 75, 'pp': 10}, + 'Swift': {'id': 129, 'power': 60, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Skull Bash': {'id': 130, 'power': 100, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Spike Cannon': {'id': 131, 'power': 20, 'type': 'Normal', 'accuracy': 100, 'pp': 15}, + 'Constrict': {'id': 132, 'power': 10, 'type': 'Normal', 'accuracy': 100, 'pp': 35}, + 'Amnesia': {'id': 133, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 20}, + 'Kinesis': {'id': 134, 'power': 0, 'type': 'Psychic', 'accuracy': 80, 'pp': 15}, + 'Softboiled': {'id': 135, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Hi Jump Kick': {'id': 136, 'power': 85, 'type': 'Fighting', 'accuracy': 90, 'pp': 20}, + 'Glare': {'id': 137, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 30}, + 'Dream Eater': {'id': 138, 'power': 100, 'type': 'Psychic', 'accuracy': 100, 'pp': 15}, + 'Poison Gas': {'id': 139, 'power': 0, 'type': 'Poison', 'accuracy': 55, 'pp': 40}, + 'Barrage': {'id': 140, 'power': 15, 'type': 'Normal', 'accuracy': 85, 'pp': 20}, + 'Leech Life': {'id': 141, 'power': 20, 'type': 'Bug', 'accuracy': 100, 'pp': 15}, + 'Lovely Kiss': {'id': 142, 'power': 0, 'type': 'Normal', 'accuracy': 75, 'pp': 10}, + 'Sky Attack': {'id': 143, 'power': 140, 'type': 'Flying', 'accuracy': 90, 'pp': 5}, + 'Transform': {'id': 144, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Bubble': {'id': 145, 'power': 20, 'type': 'Water', 'accuracy': 100, 'pp': 30}, + 'Dizzy Punch': {'id': 146, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Spore': {'id': 147, 'power': 0, 'type': 'Grass', 'accuracy': 100, 'pp': 15}, + 'Flash': {'id': 148, 'power': 0, 'type': 'Normal', 'accuracy': 70, 'pp': 20}, + 'Psywave': {'id': 149, 'power': 1, 'type': 'Psychic', 'accuracy': 80, 'pp': 15}, + 'Splash': {'id': 150, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 40}, + 'Acid Armor': {'id': 151, 'power': 0, 'type': 'Poison', 'accuracy': 100, 'pp': 40}, + 'Crabhammer': {'id': 152, 'power': 90, 'type': 'Water', 'accuracy': 85, 'pp': 10}, + 'Explosion': {'id': 153, 'power': 170, 'type': 'Normal', 'accuracy': 100, 'pp': 5}, + 'Fury Swipes': {'id': 154, 'power': 18, 'type': 'Normal', 'accuracy': 80, 'pp': 15}, + 'Bonemerang': {'id': 155, 'power': 50, 'type': 'Ground', 'accuracy': 90, 'pp': 10}, + 'Rest': {'id': 156, 'power': 0, 'type': 'Psychic', 'accuracy': 100, 'pp': 10}, + 'Rock Slide': {'id': 157, 'power': 75, 'type': 'Rock', 'accuracy': 90, 'pp': 10}, + 'Hyper Fang': {'id': 158, 'power': 80, 'type': 'Normal', 'accuracy': 90, 'pp': 15}, + 'Sharpen': {'id': 159, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Conversion': {'id': 160, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 30}, + 'Tri Attack': {'id': 161, 'power': 80, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + 'Super Fang': {'id': 162, 'power': 1, 'type': 'Normal', 'accuracy': 90, 'pp': 10}, + 'Slash': {'id': 163, 'power': 70, 'type': 'Normal', 'accuracy': 100, 'pp': 20}, + 'Substitute': {'id': 164, 'power': 0, 'type': 'Normal', 'accuracy': 100, 'pp': 10}, + #'Struggle': {'id': 165, 'power': 50, 'type': 'Struggle_Type', 'accuracy': 100, 'pp': 10} +} + +encounter_tables = {'Wild_Super_Rod_A': 2, 'Wild_Super_Rod_B': 2, 'Wild_Super_Rod_C': 3, 'Wild_Super_Rod_D': 2, + 'Wild_Super_Rod_E': 2, 'Wild_Super_Rod_F': 4, 'Wild_Super_Rod_G': 4, 'Wild_Super_Rod_H': 4, + 'Wild_Super_Rod_I': 4, 'Wild_Super_Rod_J': 4, 'Wild_Route1': 10, 'Wild_Route2': 10, + 'Wild_Route22': 10, 'Wild_ViridianForest': 10, 'Wild_Route3': 10, 'Wild_MtMoon1F': 10, + 'Wild_MtMoonB1F': 10, 'Wild_MtMoonB2F': 10, 'Wild_Route4': 10, 'Wild_Route24': 10, + 'Wild_Route25': 10, 'Wild_Route9': 10, 'Wild_Route5': 10, 'Wild_Route6': 10, + 'Wild_Route11': 10, 'Wild_RockTunnel1F': 10, 'Wild_RockTunnelB1F': 10, 'Wild_Route10': 10, + 'Wild_Route12': 10, 'Wild_Route8': 10, 'Wild_Route7': 10, 'Wild_PokemonTower3F': 10, + 'Wild_PokemonTower4F': 10, 'Wild_PokemonTower5F': 10, 'Wild_PokemonTower6F': 10, + 'Wild_PokemonTower7F': 10, 'Wild_Route13': 10, 'Wild_Route14': 10, 'Wild_Route15': 10, + 'Wild_Route16': 10, 'Wild_Route17': 10, 'Wild_Route18': 10, 'Wild_SafariZoneCenter': 10, + 'Wild_SafariZoneEast': 10, 'Wild_SafariZoneNorth': 10, 'Wild_SafariZoneWest': 10, + 'Wild_SeaRoutes': 10, 'Wild_SeafoamIslands1F': 10, 'Wild_SeafoamIslandsB1F': 10, + 'Wild_SeafoamIslandsB2F': 10, 'Wild_SeafoamIslandsB3F': 10, 'Wild_SeafoamIslandsB4F': 10, + 'Wild_PokemonMansion1F': 10, 'Wild_PokemonMansion2F': 10, 'Wild_PokemonMansion3F': 10, + 'Wild_PokemonMansionB1F': 10, 'Wild_Route21': 10, 'Wild_CeruleanCave1F': 10, + 'Wild_CeruleanCave2F': 10, 'Wild_CeruleanCaveB1F': 10, 'Wild_PowerPlant': 10, + 'Wild_Route23': 10, 'Wild_VictoryRoad2F': 10, 'Wild_VictoryRoad3F': 10, + 'Wild_VictoryRoad1F': 10, 'Wild_DiglettsCave': 10, 'Wild_Good_Rod': 2} + +hm_moves = ["Cut", "Fly", "Surf", "Strength", "Flash"] + +tm_moves = [ + 'Mega Punch', 'Razor Wind', 'Swords Dance', 'Whirlwind', 'Mega Kick', 'Toxic', 'Horn Drill', 'Body Slam', + 'Take Down', 'Double Edge', 'Bubblebeam', 'Water Gun', 'Ice Beam', 'Blizzard', 'Hyper Beam', 'Pay Day', + 'Submission', 'Counter', 'Seismic Toss', 'Rage', 'Mega Drain', 'Solarbeam', 'Dragon Rage', 'Thunderbolt', 'Thunder', + 'Earthquake', 'Fissure', 'Dig', 'Psychic', 'Teleport', 'Mimic', 'Double Team', 'Reflect', 'Bide', 'Metronome', + 'Selfdestruct', 'Egg Bomb', 'Fire Blast', 'Swift', 'Skull Bash', 'Softboiled', 'Dream Eater', 'Sky Attack', 'Rest', + 'Thunder Wave', 'Psywave', 'Explosion', 'Rock Slide', 'Tri Attack', 'Substitute' +] + + + +first_stage_pokemon = [pokemon for pokemon in pokemon_data.keys() if pokemon not in evolves_from] +legendary_pokemon = ["Articuno", "Zapdos", "Moltres", "Mewtwo", "Mew"] + diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py new file mode 100644 index 00000000..1650e640 --- /dev/null +++ b/worlds/pokemon_rb/regions.py @@ -0,0 +1,305 @@ + +from BaseClasses import MultiWorld, Region, Entrance, RegionType, LocationProgressType +from worlds.generic.Rules import add_item_rule +from .locations import location_data, PokemonRBLocation + + +def create_region(world: MultiWorld, player: int, name: str, locations_per_region=None, exits=None): + ret = Region(name, RegionType.Generic, name, player, world) + for location in locations_per_region.get(name, []): + if (world.randomize_hidden_items[player].value or "Hidden" not in location.name) and \ + (world.extra_key_items[player].value or name != "Rock Tunnel B1F" or "Item" not in location.name) and \ + (world.tea[player].value or location.name != "Celadon City - Mansion Lady"): + location.parent_region = ret + ret.locations.append(location) + if world.randomize_hidden_items[player].value == 2 and "Hidden" in location.name: + location.progress_type = LocationProgressType.EXCLUDED + add_item_rule(location, lambda i: not (i.advancement or i.useful)) + if exits: + for exit in exits: + ret.exits.append(Entrance(player, exit, ret)) + locations_per_region[name] = [] + return ret + + +def create_regions(world: MultiWorld, player: int): + locations_per_region = {} + for location in location_data: + locations_per_region.setdefault(location.region, []) + locations_per_region[location.region].append(PokemonRBLocation(player, location.name, location.address, + location.rom_address)) + regions = [ + create_region(world, player, "Menu", locations_per_region), + create_region(world, player, "Anywhere", locations_per_region), + create_region(world, player, "Fossil", locations_per_region), + create_region(world, player, "Pallet Town", locations_per_region), + create_region(world, player, "Route 1", locations_per_region), + create_region(world, player, "Viridian City", locations_per_region), + create_region(world, player, "Viridian City North", locations_per_region), + create_region(world, player, "Viridian Gym", locations_per_region), + create_region(world, player, "Route 2", locations_per_region), + create_region(world, player, "Route 2 East", locations_per_region), + create_region(world, player, "Diglett's Cave", locations_per_region), + create_region(world, player, "Route 22", locations_per_region), + create_region(world, player, "Route 23 South", locations_per_region), + create_region(world, player, "Route 23 North", locations_per_region), + create_region(world, player, "Viridian Forest", locations_per_region), + create_region(world, player, "Pewter City", locations_per_region), + create_region(world, player, "Pewter Gym", locations_per_region), + create_region(world, player, "Route 3", locations_per_region), + create_region(world, player, "Mt Moon 1F", locations_per_region), + create_region(world, player, "Mt Moon B1F", locations_per_region), + create_region(world, player, "Mt Moon B2F", locations_per_region), + create_region(world, player, "Route 4", locations_per_region), + create_region(world, player, "Cerulean City", locations_per_region), + create_region(world, player, "Cerulean Gym", locations_per_region), + create_region(world, player, "Route 24", locations_per_region), + create_region(world, player, "Route 25", locations_per_region), + create_region(world, player, "Route 9", locations_per_region), + create_region(world, player, "Route 10 North", locations_per_region), + create_region(world, player, "Rock Tunnel 1F", locations_per_region), + create_region(world, player, "Rock Tunnel B1F", locations_per_region), + create_region(world, player, "Power Plant", locations_per_region), + create_region(world, player, "Route 10 South", locations_per_region), + create_region(world, player, "Lavender Town", locations_per_region), + create_region(world, player, "Pokemon Tower 1F", locations_per_region), + create_region(world, player, "Pokemon Tower 2F", locations_per_region), + create_region(world, player, "Pokemon Tower 3F", locations_per_region), + create_region(world, player, "Pokemon Tower 4F", locations_per_region), + create_region(world, player, "Pokemon Tower 5F", locations_per_region), + create_region(world, player, "Pokemon Tower 6F", locations_per_region), + create_region(world, player, "Pokemon Tower 7F", locations_per_region), + create_region(world, player, "Route 5", locations_per_region), + create_region(world, player, "Saffron City", locations_per_region), + create_region(world, player, "Saffron Gym", locations_per_region), + create_region(world, player, "Copycat's House", locations_per_region), + create_region(world, player, "Underground Tunnel North-South", locations_per_region), + create_region(world, player, "Route 6", locations_per_region), + create_region(world, player, "Vermilion City", locations_per_region), + create_region(world, player, "Vermilion Gym", locations_per_region), + create_region(world, player, "S.S. Anne 1F", locations_per_region), + create_region(world, player, "S.S. Anne B1F", locations_per_region), + create_region(world, player, "S.S. Anne 2F", locations_per_region), + create_region(world, player, "Route 11", locations_per_region), + create_region(world, player, "Route 11 East", locations_per_region), + create_region(world, player, "Route 12 North", locations_per_region), + create_region(world, player, "Route 12 South", locations_per_region), + create_region(world, player, "Route 12 Grass", locations_per_region), + create_region(world, player, "Route 12 West", locations_per_region), + create_region(world, player, "Route 7", locations_per_region), + create_region(world, player, "Underground Tunnel West-East", locations_per_region), + create_region(world, player, "Route 8", locations_per_region), + create_region(world, player, "Route 8 Grass", locations_per_region), + create_region(world, player, "Celadon City", locations_per_region), + create_region(world, player, "Celadon Prize Corner", locations_per_region), + create_region(world, player, "Celadon Gym", locations_per_region), + create_region(world, player, "Route 16", locations_per_region), + create_region(world, player, "Route 16 North", locations_per_region), + create_region(world, player, "Route 17", locations_per_region), + create_region(world, player, "Route 18", locations_per_region), + create_region(world, player, "Fuchsia City", locations_per_region), + create_region(world, player, "Fuchsia Gym", locations_per_region), + create_region(world, player, "Safari Zone Gate", locations_per_region), + create_region(world, player, "Safari Zone Center", locations_per_region), + create_region(world, player, "Safari Zone East", locations_per_region), + create_region(world, player, "Safari Zone North", locations_per_region), + create_region(world, player, "Safari Zone West", locations_per_region), + create_region(world, player, "Route 15", locations_per_region), + create_region(world, player, "Route 14", locations_per_region), + create_region(world, player, "Route 13", locations_per_region), + create_region(world, player, "Route 19", locations_per_region), + create_region(world, player, "Route 20 East", locations_per_region), + create_region(world, player, "Route 20 West", locations_per_region), + create_region(world, player, "Seafoam Islands 1F", locations_per_region), + create_region(world, player, "Seafoam Islands B1F", locations_per_region), + create_region(world, player, "Seafoam Islands B2F", locations_per_region), + create_region(world, player, "Seafoam Islands B3F", locations_per_region), + create_region(world, player, "Seafoam Islands B4F", locations_per_region), + create_region(world, player, "Cinnabar Island", locations_per_region), + create_region(world, player, "Cinnabar Gym", locations_per_region), + create_region(world, player, "Route 21", locations_per_region), + create_region(world, player, "Silph Co 1F", locations_per_region), + create_region(world, player, "Silph Co 2F", locations_per_region), + create_region(world, player, "Silph Co 3F", locations_per_region), + create_region(world, player, "Silph Co 4F", locations_per_region), + create_region(world, player, "Silph Co 5F", locations_per_region), + create_region(world, player, "Silph Co 6F", locations_per_region), + create_region(world, player, "Silph Co 7F", locations_per_region), + create_region(world, player, "Silph Co 8F", locations_per_region), + create_region(world, player, "Silph Co 9F", locations_per_region), + create_region(world, player, "Silph Co 10F", locations_per_region), + create_region(world, player, "Silph Co 11F", locations_per_region), + create_region(world, player, "Rocket Hideout B1F", locations_per_region), + create_region(world, player, "Rocket Hideout B2F", locations_per_region), + create_region(world, player, "Rocket Hideout B3F", locations_per_region), + create_region(world, player, "Rocket Hideout B4F", locations_per_region), + create_region(world, player, "Pokemon Mansion 1F", locations_per_region), + create_region(world, player, "Pokemon Mansion 2F", locations_per_region), + create_region(world, player, "Pokemon Mansion 3F", locations_per_region), + create_region(world, player, "Pokemon Mansion B1F", locations_per_region), + create_region(world, player, "Victory Road 1F", locations_per_region), + create_region(world, player, "Victory Road 2F", locations_per_region), + create_region(world, player, "Victory Road 3F", locations_per_region), + create_region(world, player, "Indigo Plateau", locations_per_region), + create_region(world, player, "Cerulean Cave 1F", locations_per_region), + create_region(world, player, "Cerulean Cave 2F", locations_per_region), + create_region(world, player, "Cerulean Cave B1F", locations_per_region), + create_region(world, player, "Evolution", locations_per_region), + ] + world.regions += regions + connect(world, player, "Menu", "Anywhere", one_way=True) + connect(world, player, "Menu", "Pallet Town", one_way=True) + connect(world, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks( + state.world.second_fossil_check_condition[player].value, player), one_way=True) + connect(world, player, "Pallet Town", "Route 1") + connect(world, player, "Route 1", "Viridian City") + connect(world, player, "Viridian City", "Route 22") + connect(world, player, "Route 22", "Route 23 South", + lambda state: state.pokemon_rb_has_badges(state.world.victory_road_condition[player].value, player)) + connect(world, player, "Route 23 South", "Route 23 North", lambda state: state.pokemon_rb_can_surf(player)) + connect(world, player, "Viridian City North", "Viridian Gym", lambda state: + state.pokemon_rb_has_badges(state.world.viridian_gym_condition[player].value, player), one_way=True) + connect(world, player, "Route 2", "Route 2 East", lambda state: state.pokemon_rb_can_cut(player)) + connect(world, player, "Route 2 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player)) + connect(world, player, "Route 2", "Viridian City North") + connect(world, player, "Route 2", "Viridian Forest") + connect(world, player, "Route 2", "Pewter City") + connect(world, player, "Pewter City", "Pewter Gym", one_way=True) + connect(world, player, "Pewter City", "Route 3") + connect(world, player, "Route 4", "Route 3", one_way=True) + connect(world, player, "Mt Moon 1F", "Mt Moon B1F", one_way=True) + connect(world, player, "Mt Moon B1F", "Mt Moon B2F", one_way=True) + connect(world, player, "Mt Moon B1F", "Route 4", one_way=True) + connect(world, player, "Route 4", "Cerulean City") + connect(world, player, "Cerulean City", "Cerulean Gym", one_way=True) + connect(world, player, "Cerulean City", "Route 24", one_way=True) + connect(world, player, "Route 24", "Route 25", one_way=True) + connect(world, player, "Cerulean City", "Route 9", lambda state: state.pokemon_rb_can_cut(player)) + connect(world, player, "Route 9", "Route 10 North") + connect(world, player, "Route 10 North", "Rock Tunnel 1F", lambda state: state.pokemon_rb_can_flash(player)) + connect(world, player, "Route 10 North", "Power Plant", lambda state: state.pokemon_rb_can_surf(player) and + (state.has("Plant Key", player) or not state.world.extra_key_items[player].value), one_way=True) + connect(world, player, "Rock Tunnel 1F", "Route 10 South", lambda state: state.pokemon_rb_can_flash(player)) + connect(world, player, "Rock Tunnel 1F", "Rock Tunnel B1F") + connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) + connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) + connect(world, player, "Pokemon Tower 1F", "Pokemon Tower 2F", one_way=True) + connect(world, player, "Pokemon Tower 2F", "Pokemon Tower 3F", one_way=True) + connect(world, player, "Pokemon Tower 3F", "Pokemon Tower 4F", one_way=True) + connect(world, player, "Pokemon Tower 4F", "Pokemon Tower 5F", one_way=True) + connect(world, player, "Pokemon Tower 5F", "Pokemon Tower 6F", one_way=True) + connect(world, player, "Pokemon Tower 6F", "Pokemon Tower 7F", lambda state: state.has("Silph Scope", player)) + connect(world, player, "Cerulean City", "Route 5") + connect(world, player, "Route 5", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(world, player, "Route 5", "Underground Tunnel North-South") + connect(world, player, "Route 6", "Underground Tunnel North-South") + connect(world, player, "Route 6", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(world, player, "Route 7", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(world, player, "Route 8", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(world, player, "Saffron City", "Copycat's House", lambda state: state.has("Silph Co Liberated", player), one_way=True) + connect(world, player, "Saffron City", "Saffron Gym", lambda state: state.has("Silph Co Liberated", player), one_way=True) + connect(world, player, "Route 6", "Vermilion City") + connect(world, player, "Vermilion City", "Vermilion Gym", lambda state: state.pokemon_rb_can_surf(player) or state.pokemon_rb_can_cut(player), one_way=True) + connect(world, player, "Vermilion City", "S.S. Anne 1F", lambda state: state.has("S.S. Ticket", player), one_way=True) + connect(world, player, "S.S. Anne 1F", "S.S. Anne 2F", one_way=True) + connect(world, player, "S.S. Anne 1F", "S.S. Anne B1F", one_way=True) + connect(world, player, "Vermilion City", "Route 11") + connect(world, player, "Vermilion City", "Diglett's Cave") + connect(world, player, "Route 12 West", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player) or not state.world.extra_strength_boulders[player].value) + connect(world, player, "Route 12 North", "Route 12 South", lambda state: state.has("Poke Flute", player) or state.pokemon_rb_can_surf( player)) + connect(world, player, "Route 12 West", "Route 12 North", lambda state: state.has("Poke Flute", player)) + connect(world, player, "Route 12 West", "Route 12 South", lambda state: state.has("Poke Flute", player)) + connect(world, player, "Route 12 South", "Route 12 Grass", lambda state: state.pokemon_rb_can_cut(player)) + connect(world, player, "Route 12 North", "Lavender Town") + connect(world, player, "Route 7", "Lavender Town") + connect(world, player, "Route 10 South", "Lavender Town") + connect(world, player, "Route 7", "Underground Tunnel West-East") + connect(world, player, "Route 8", "Underground Tunnel West-East") + connect(world, player, "Route 8", "Celadon City") + connect(world, player, "Route 8", "Route 8 Grass", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(world, player, "Route 7", "Celadon City") + connect(world, player, "Celadon City", "Celadon Gym", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(world, player, "Celadon City", "Celadon Prize Corner") + connect(world, player, "Celadon City", "Route 16") + connect(world, player, "Route 16", "Route 16 North", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(world, player, "Route 16", "Route 17", lambda state: state.has("Poke Flute", player) and state.has("Bicycle", player)) + connect(world, player, "Route 17", "Route 18", lambda state: state.has("Bicycle", player)) + connect(world, player, "Fuchsia City", "Fuchsia Gym", one_way=True) + connect(world, player, "Fuchsia City", "Route 18") + connect(world, player, "Fuchsia City", "Safari Zone Gate", one_way=True) + connect(world, player, "Safari Zone Gate", "Safari Zone Center", lambda state: state.has("Safari Pass", player) or not state.world.extra_key_items[player].value, one_way=True) + connect(world, player, "Safari Zone Center", "Safari Zone East", one_way=True) + connect(world, player, "Safari Zone Center", "Safari Zone West", one_way=True) + connect(world, player, "Safari Zone Center", "Safari Zone North", one_way=True) + connect(world, player, "Fuchsia City", "Route 15") + connect(world, player, "Route 15", "Route 14") + connect(world, player, "Route 14", "Route 13") + connect(world, player, "Route 13", "Route 12 South", lambda state: state.pokemon_rb_can_strength(player) or state.pokemon_rb_can_surf(player) or not state.world.extra_strength_boulders[player].value) + connect(world, player, "Fuchsia City", "Route 19", lambda state: state.pokemon_rb_can_surf(player)) + connect(world, player, "Route 20 East", "Route 19") + connect(world, player, "Route 20 West", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) + connect(world, player, "Route 20 West", "Seafoam Islands 1F") + connect(world, player, "Route 20 East", "Seafoam Islands 1F", one_way=True) + connect(world, player, "Seafoam Islands 1F", "Route 20 East", lambda state: state.pokemon_rb_can_strength(player), one_way=True) + connect(world, player, "Viridian City", "Viridian City North", lambda state: state.has("Oak's Parcel", player) or state.world.old_man[player].value == 2 or state.pokemon_rb_can_cut(player)) + connect(world, player, "Route 3", "Mt Moon 1F", one_way=True) + connect(world, player, "Route 11", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player)) + connect(world, player, "Cinnabar Island", "Cinnabar Gym", lambda state: state.has("Secret Key", player), one_way=True) + connect(world, player, "Cinnabar Island", "Pokemon Mansion 1F", lambda state: state.has("Mansion Key", player) or not state.world.extra_key_items[player].value, one_way=True) + connect(world, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True) + connect(world, player, "Seafoam Islands B1F", "Seafoam Islands B2F", one_way=True) + connect(world, player, "Seafoam Islands B2F", "Seafoam Islands B3F", one_way=True) + connect(world, player, "Seafoam Islands B3F", "Seafoam Islands B4F", one_way=True) + connect(world, player, "Route 21", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) + connect(world, player, "Pallet Town", "Route 21", lambda state: state.pokemon_rb_can_surf(player)) + connect(world, player, "Saffron City", "Silph Co 1F", lambda state: state.has("Fuji Saved", player), one_way=True) + connect(world, player, "Silph Co 1F", "Silph Co 2F", one_way=True) + connect(world, player, "Silph Co 2F", "Silph Co 3F", one_way=True) + connect(world, player, "Silph Co 3F", "Silph Co 4F", one_way=True) + connect(world, player, "Silph Co 4F", "Silph Co 5F", one_way=True) + connect(world, player, "Silph Co 5F", "Silph Co 6F", one_way=True) + connect(world, player, "Silph Co 6F", "Silph Co 7F", one_way=True) + connect(world, player, "Silph Co 7F", "Silph Co 8F", one_way=True) + connect(world, player, "Silph Co 8F", "Silph Co 9F", one_way=True) + connect(world, player, "Silph Co 9F", "Silph Co 10F", one_way=True) + connect(world, player, "Silph Co 10F", "Silph Co 11F", one_way=True) + connect(world, player, "Celadon City", "Rocket Hideout B1F", lambda state: state.has("Hideout Key", player) or not state.world.extra_key_items[player].value, one_way=True) + connect(world, player, "Rocket Hideout B1F", "Rocket Hideout B2F", one_way=True) + connect(world, player, "Rocket Hideout B2F", "Rocket Hideout B3F", one_way=True) + connect(world, player, "Rocket Hideout B3F", "Rocket Hideout B4F", one_way=True) + connect(world, player, "Pokemon Mansion 1F", "Pokemon Mansion 2F", one_way=True) + connect(world, player, "Pokemon Mansion 2F", "Pokemon Mansion 3F", one_way=True) + connect(world, player, "Pokemon Mansion 1F", "Pokemon Mansion B1F", one_way=True) + connect(world, player, "Route 23 North", "Victory Road 1F", lambda state: state.pokemon_rb_can_strength(player), one_way=True) + connect(world, player, "Victory Road 1F", "Victory Road 2F", one_way=True) + connect(world, player, "Victory Road 2F", "Victory Road 3F", one_way=True) + connect(world, player, "Victory Road 2F", "Indigo Plateau", lambda state: state.pokemon_rb_has_badges(state.world.elite_four_condition[player], player), one_way=True) + connect(world, player, "Cerulean City", "Cerulean Cave 1F", lambda state: + state.pokemon_rb_cerulean_cave(state.world.cerulean_cave_condition[player].value + (state.world.extra_key_items[player].value * 4), player) and + state.pokemon_rb_can_surf(player), one_way=True) + connect(world, player, "Cerulean Cave 1F", "Cerulean Cave 2F", one_way=True) + connect(world, player, "Cerulean Cave 1F", "Cerulean Cave B1F", lambda state: state.pokemon_rb_can_surf(player), one_way=True) + if world.worlds[player].fly_map != "Pallet Town": + connect(world, player, "Menu", world.worlds[player].fly_map, lambda state: state.pokemon_rb_can_fly(player), one_way=True, + name="Fly to " + world.worlds[player].fly_map) + + +def connect(world: MultiWorld, player: int, source: str, target: str, rule: callable = lambda state: True, one_way=False, name=None): + source_region = world.get_region(source, player) + target_region = world.get_region(target, player) + + if name is None: + name = source + " to " + target + + connection = Entrance( + player, + name, + source_region + ) + + connection.access_rule = rule + + source_region.exits.append(connection) + connection.connect(target_region) + if not one_way: + connect(world, player, target, source, rule, True) diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py new file mode 100644 index 00000000..370bd6af --- /dev/null +++ b/worlds/pokemon_rb/rom.py @@ -0,0 +1,614 @@ +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 +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): + encounter_slots = [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.world.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): + 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(self.world.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(self.world.random.triangular(0, 50, 0))] + elif randomize_type == 4: + mon = self.world.random.choice(mons_list) + return mon + + +def process_trainer_data(self, data): + mons_list = [pokemon for pokemon in poke_data.pokemon_data.keys() if pokemon not in poke_data.legendary_pokemon + or self.world.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.world.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.world.randomize_trainer_parties[self.player].value: + mon = poke_data.id_to_mon[data[address]] + mon = randomize_pokemon(self, mon, mons_list, self.world.randomize_trainer_parties[self.player].value) + if mon is not None: + data[address] = poke_data.pokemon_data[mon]["id"] + + +def process_static_pokemon(self): + starter_slots = [location for location in location_data if location.type == "Starter Pokemon"] + legendary_slots = [location for location in location_data if location.type == "Legendary Pokemon"] + static_slots = [location for location in location_data if location.type in + ["Static Pokemon", "Missable Pokemon"]] + legendary_mons = [slot.original_item for slot in legendary_slots] + + mons_list = [pokemon for pokemon in poke_data.first_stage_pokemon if pokemon not in poke_data.legendary_pokemon + or self.world.randomize_legendary_pokemon[self.player].value == 3] + if self.world.randomize_legendary_pokemon[self.player].value == 0: + for slot in legendary_slots: + location = self.world.get_location(slot.name, self.player) + location.place_locked_item(self.create_item("Missable " + slot.original_item)) + elif self.world.randomize_legendary_pokemon[self.player].value == 1: + self.world.random.shuffle(legendary_mons) + for slot in legendary_slots: + location = self.world.get_location(slot.name, self.player) + location.place_locked_item(self.create_item("Missable " + legendary_mons.pop())) + elif self.world.randomize_legendary_pokemon[self.player].value == 2: + static_slots = static_slots + legendary_slots + self.world.random.shuffle(static_slots) + 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.world.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.world.randomize_legendary_pokemon[self.player].value == 3: + static_slots = static_slots + legendary_slots + + for slot in static_slots: + location = self.world.get_location(slot.name, self.player) + randomize_type = self.world.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: + location.place_locked_item(self.create_item(slot_type + " " + + randomize_pokemon(self, slot.original_item, mons_list, randomize_type))) + + for slot in starter_slots: + location = self.world.get_location(slot.name, self.player) + randomize_type = self.world.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: + location.place_locked_item(self.create_item(slot_type + " " + + randomize_pokemon(self, slot.original_item, mons_list, randomize_type))) + +def process_wild_pokemon(self): + + encounter_slots = get_encounter_slots(self) + + placed_mons = {pokemon: 0 for pokemon in poke_data.pokemon_data.keys()} + if self.world.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.world.randomize_legendary_pokemon[self.player].value == 3] + self.world.random.shuffle(encounter_slots) + locations = [] + for slot in encounter_slots: + mon = randomize_pokemon(self, slot.original_item, mons_list, self.world.randomize_wild_pokemon[self.player].value) + placed_mons[mon] += 1 + location = self.world.get_location(slot.name, self.player) + 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 + (pokemon not in poke_data.legendary_pokemon or self.world.randomize_legendary_pokemon[self.player].value == 3)] + if self.world.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.world.randomize_legendary_pokemon[self.player].value == 3)] + elif self.world.catch_em_all[self.player].value == 2: + mons_to_add = remaining_pokemon.copy() + logic_needed_mons = max(self.world.oaks_aide_rt_2[self.player].value, + self.world.oaks_aide_rt_11[self.player].value, + self.world.oaks_aide_rt_15[self.player].value) + if self.world.accessibility[self.player] == "minimal": + logic_needed_mons = 0 + + self.world.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.world.randomize_wild_pokemon[self.player].value in [1, 3]: + 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: + candidate_locations = location_data + candidate_locations = [self.world.get_location(location.name, self.player) for location in candidate_locations] + 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: + location = self.world.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_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(): + if self.world.randomize_pokemon_stats[self.player].value == 1: + stats = [mon_data["hp"], mon_data["atk"], mon_data["def"], mon_data["spd"], mon_data["spc"]] + self.world.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.world.randomize_pokemon_stats[self.player].value == 2: + 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: + stat = self.world.random.randint(0, 4) + 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] + if self.world.randomize_pokemon_types[self.player].value: + if self.world.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.world.secondary_type_chance[self.player].value == -1: + if mon_data["type1"] != mon_data["type2"]: + while type2 == type1: + type2 = self.world.random.choice(list(poke_data.type_names.values())) + elif self.world.random.randint(1, 100) <= self.world.secondary_type_chance[self.player].value: + type2 = self.world.random.choice(list(poke_data.type_names.values())) + else: + type1 = self.world.random.choice(list(poke_data.type_names.values())) + type2 = type1 + if ((self.world.secondary_type_chance[self.player].value == -1 and mon_data["type1"] + != mon_data["type2"]) or self.world.random.randint(1, 100) + <= self.world.secondary_type_chance[self.player].value): + while type2 == type1: + type2 = self.world.random.choice(list(poke_data.type_names.values())) + + mon_data["type1"] = type1 + mon_data["type2"] = type2 + if self.world.randomize_pokemon_movesets[self.player].value: + if self.world.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 = set(poke_data.moves.keys()) + moves -= set(["No Move"] + poke_data.hm_moves) + mon_data["start move 1"] = get_move(moves, chances, self.world.random, True) + for i in range(2, 5): + if mon_data[f"start move {i}"] != "No Move" or self.world.start_with_four_moves[ + self.player].value == 1: + mon_data[f"start move {i}"] = get_move(moves, chances, self.world.random) + if mon in learnsets: + for move_num in range(0, len(learnsets[mon])): + learnsets[mon][move_num] = get_move(moves, chances, self.world.random) + if self.world.randomize_pokemon_catch_rates[self.player].value: + mon_data["catch rate"] = self.world.random.randint(self.world.minimum_catch_rate[self.player], 255) + else: + mon_data["catch rate"] = max(self.world.minimum_catch_rate[self.player], mon_data["catch rate"]) + + 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"]: + mon_data["tms"] = local_poke_data[poke_data.evolves_from[mon]]["tms"] + elif mon != "Mew": + tms_hms = poke_data.tm_moves + poke_data.hm_moves + for flag, tm_move in enumerate(tms_hms): + if (flag < 50 and self.world.tm_compatibility[self.player].value == 1) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 1): + type_match = poke_data.moves[tm_move]["type"] in [mon_data["type1"], mon_data["type2"]] + bit = int(self.world.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.world.tm_compatibility[self.player].value == 2) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 2): + bit = [0, 1][self.world.random.randint(0, 1)] + elif (flag < 50 and self.world.tm_compatibility[self.player].value == 3) or (flag >= 50 and self.world.hm_compatibility[self.player].value == 3): + 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): + random = self.world.slot_seeds[self.player] + game_version = self.world.game_version[self.player].current_key + data = bytearray(get_base_rom_bytes(game_version)) + + basemd5 = hashlib.md5() + basemd5.update(data) + + for location in self.world.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: + 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 + + if self.world.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 + + if self.world.extra_key_items[self.player].value: + data[rom_addresses['Options']] |= 4 + data[rom_addresses["Option_Blind_Trainers"]] = round(self.world.blind_trainers[self.player].value * 2.55) + data[rom_addresses['Option_Cerulean_Cave_Condition']] = self.world.cerulean_cave_condition[self.player].value + data[rom_addresses['Option_Encounter_Minimum_Steps']] = self.world.minimum_steps_between_encounters[self.player].value + data[rom_addresses['Option_Victory_Road_Badges']] = self.world.victory_road_condition[self.player].value + data[rom_addresses['Option_Pokemon_League_Badges']] = self.world.elite_four_condition[self.player].value + data[rom_addresses['Option_Viridian_Gym_Badges']] = self.world.viridian_gym_condition[self.player].value + data[rom_addresses['Option_EXP_Modifier']] = self.world.exp_modifier[self.player].value + if not self.world.require_item_finder[self.player].value: + data[rom_addresses['Option_Itemfinder']] = 0 + if self.world.extra_strength_boulders[self.player].value: + for i in range(0, 3): + data[rom_addresses['Option_Boulders'] + (i * 3)] = 0x15 + if self.world.extra_key_items[self.player].value: + for i in range(0, 4): + data[rom_addresses['Option_Rock_Tunnel_Extra_Items'] + (i * 3)] = 0x15 + if self.world.old_man[self.player].value == 2: + data[rom_addresses['Option_Old_Man']] = 0x11 + data[rom_addresses['Option_Old_Man_Lying']] = 0x15 + money = str(self.world.starting_money[self.player].value) + while len(money) < 6: + money = "0" + money + 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) + data[rom_addresses["Text_Badges_Needed"]] = encode_text( + str(max(self.world.victory_road_condition[self.player].value, + self.world.elite_four_condition[self.player].value)))[0] + if self.world.badges_needed_for_hm_moves[self.player].value == 0: + 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(" ", "_")]) + + chart = deepcopy(poke_data.type_chart) + if self.world.randomize_type_matchup_types[self.player].value == 1: + attacking_types = [] + defending_types = [] + for matchup in chart: + attacking_types.append(matchup[0]) + defending_types.append(matchup[1]) + random.shuffle(attacking_types) + random.shuffle(defending_types) + matchups = [] + while len(attacking_types) > 0: + if [attacking_types[0], defending_types[0]] not in matchups: + matchups.append([attacking_types.pop(0), defending_types.pop(0)]) + else: + matchup = matchups.pop(0) + attacking_types.append(matchup[0]) + defending_types.append(matchup[1]) + random.shuffle(attacking_types) + random.shuffle(defending_types) + for matchup, chart_row in zip(matchups, chart): + chart_row[0] = matchup[0] + chart_row[1] = matchup[1] + elif self.world.randomize_type_matchup_types[self.player].value == 2: + used_matchups = [] + for matchup in chart: + matchup[0] = random.choice(list(poke_data.type_names.values())) + matchup[1] = random.choice(list(poke_data.type_names.values())) + while [matchup[0], matchup[1]] in used_matchups: + matchup[0] = random.choice(list(poke_data.type_names.values())) + matchup[1] = random.choice(list(poke_data.type_names.values())) + used_matchups.append([matchup[0], matchup[1]]) + if self.world.randomize_type_matchup_type_effectiveness[self.player].value == 1: + effectiveness_list = [] + for matchup in chart: + effectiveness_list.append(matchup[2]) + random.shuffle(effectiveness_list) + for (matchup, effectiveness) in zip(chart, effectiveness_list): + matchup[2] = effectiveness + elif self.world.randomize_type_matchup_type_effectiveness[self.player].value == 2: + for matchup in chart: + matchup[2] = random.choice([0] + ([5, 20] * 5)) + elif self.world.randomize_type_matchup_type_effectiveness[self.player].value == 3: + for matchup in chart: + matchup[2] = self.world.random.choice([i for i in range(0, 21) if i != 10]) + type_loc = rom_addresses["Type_Chart"] + for matchup in chart: + 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 + # sort so that super-effective matchups occur first, to prevent dual "not very effective" / "super effective" + # matchups from leading to damage being ultimately divided by 2 and then multiplied by 2, which can lead to + # damage being reduced by 1 which leads to a "not very effective" message appearing due to my changes + # to the way effectiveness messages are generated. + self.type_chart = sorted(chart, key=lambda matchup: 0 - matchup[2]) + + if self.world.normalize_encounter_chances[self.player].value: + 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"] + + data[rom_addresses["Option_Aide_Rt2"]] = self.world.oaks_aide_rt_2[self.player] + data[rom_addresses["Option_Aide_Rt11"]] = self.world.oaks_aide_rt_11[self.player] + data[rom_addresses["Option_Aide_Rt15"]] = self.world.oaks_aide_rt_15[self.player] + + if self.world.safari_zone_normal_battles[self.player].value == 1: + data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255 + + if self.world.reusable_tms[self.player].value: + data[rom_addresses["Option_Reusable_TMs"]] = 0xC9 + + process_trainer_data(self, data) + + 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() + if self.world.game_version[self.player].value: + mons.sort(key=lambda mon: 0 if mon == self.world.get_location("Pallet Town - Starter 1", self.player).item.name + else 1 if mon == self.world.get_location("Pallet Town - Starter 2", self.player).item.name else + 2 if mon == self.world.get_location("Pallet Town - Starter 3", self.player).item.name else 3) + else: + mons.sort(key=lambda mon: 0 if mon == self.world.get_location("Pallet Town - Starter 2", self.player).item.name + else 1 if mon == self.world.get_location("Pallet Town - Starter 1", self.player).item.name else + 2 if mon == self.world.get_location("Pallet Town - Starter 3", self.player).item.name else 3) + write_bytes(data, encode_text(self.world.seed_name, 20, True), rom_addresses['Title_Seed']) + + slot_name = self.world.player_name[self.player] + slot_name.replace("@", " ") + slot_name.replace("<", " ") + slot_name.replace(">", " ") + write_bytes(data, encode_text(slot_name, 16, True, True), rom_addresses['Title_Slot_Name']) + + write_bytes(data, self.trainer_name, rom_addresses['Player_Name']) + write_bytes(data, self.rival_name, rom_addresses['Rival_Name']) + + write_bytes(data, basemd5.digest(), 0xFFCC) + write_bytes(data, self.world.seed_name.encode(), 0xFFDC) + write_bytes(data, self.world.player_name[self.player].encode(), 0xFFF0) + + + + outfilepname = f'_P{self.player}' + outfilepname += f"_{self.world.get_file_safe_player_name(self.player).replace(' ', '_')}" \ + if self.world.player_name[self.player] != 'Player%d' % self.player else '' + rompath = os.path.join(output_directory, f'AP_{self.world.seed_name}{outfilepname}.gb') + with open(rompath, 'wb') as outfile: + outfile.write(data) + if self.world.game_version[self.player].current_key == "red": + patch = RedDeltaPatch(os.path.splitext(rompath)[0] + RedDeltaPatch.patch_file_ending, player=self.player, + player_name=self.world.player_name[self.player], patched_path=rompath) + else: + patch = BlueDeltaPatch(os.path.splitext(rompath)[0] + BlueDeltaPatch.patch_file_ending, player=self.player, + player_name=self.world.player_name[self.player], patched_path=rompath) + + 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(): + raise Exception('Supplied Base Rom does not match known MD5 for US(1.0) release. ' + 'Get the correct game and version, then dump it') + with open(os.path.join(os.path.dirname(__file__), f'basepatch_{game_version}.bsdiff4'), 'rb') as stream: + base_patch = bytes(stream.read()) + base_rom_bytes = bsdiff4.patch(base_rom_bytes, base_patch) + 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) diff --git a/worlds/pokemon_rb/rom_addresses.py b/worlds/pokemon_rb/rom_addresses.py new file mode 100644 index 00000000..c04a75bc --- /dev/null +++ b/worlds/pokemon_rb/rom_addresses.py @@ -0,0 +1,588 @@ +rom_addresses = { + "Option_Encounter_Minimum_Steps": 0x3c3, + "Option_Blind_Trainers": 0x317e, + "Base_Stats_Mew": 0x425b, + "Title_Mon_First": 0x436e, + "Title_Mons": 0x4547, + "Player_Name": 0x4569, + "Rival_Name": 0x4571, + "Title_Seed": 0x5dfe, + "Title_Slot_Name": 0x5e1e, + "PC_Item": 0x61ec, + "PC_Item_Quantity": 0x61f1, + "Options": 0x61f9, + "Fly_Location": 0x61fe, + "Option_Old_Man": 0xcaef, + "Option_Old_Man_Lying": 0xcaf2, + "Option_Boulders": 0xcd98, + "Option_Rock_Tunnel_Extra_Items": 0xcda1, + "Wild_Route1": 0xd0fb, + "Wild_Route2": 0xd111, + "Wild_Route22": 0xd127, + "Wild_ViridianForest": 0xd13d, + "Wild_Route3": 0xd153, + "Wild_MtMoon1F": 0xd169, + "Wild_MtMoonB1F": 0xd17f, + "Wild_MtMoonB2F": 0xd195, + "Wild_Route4": 0xd1ab, + "Wild_Route24": 0xd1c1, + "Wild_Route25": 0xd1d7, + "Wild_Route9": 0xd1ed, + "Wild_Route5": 0xd203, + "Wild_Route6": 0xd219, + "Wild_Route11": 0xd22f, + "Wild_RockTunnel1F": 0xd245, + "Wild_RockTunnelB1F": 0xd25b, + "Wild_Route10": 0xd271, + "Wild_Route12": 0xd287, + "Wild_Route8": 0xd29d, + "Wild_Route7": 0xd2b3, + "Wild_PokemonTower3F": 0xd2cd, + "Wild_PokemonTower4F": 0xd2e3, + "Wild_PokemonTower5F": 0xd2f9, + "Wild_PokemonTower6F": 0xd30f, + "Wild_PokemonTower7F": 0xd325, + "Wild_Route13": 0xd33b, + "Wild_Route14": 0xd351, + "Wild_Route15": 0xd367, + "Wild_Route16": 0xd37d, + "Wild_Route17": 0xd393, + "Wild_Route18": 0xd3a9, + "Wild_SafariZoneCenter": 0xd3bf, + "Wild_SafariZoneEast": 0xd3d5, + "Wild_SafariZoneNorth": 0xd3eb, + "Wild_SafariZoneWest": 0xd401, + "Wild_SeaRoutes": 0xd418, + "Wild_SeafoamIslands1F": 0xd42d, + "Wild_SeafoamIslandsB1F": 0xd443, + "Wild_SeafoamIslandsB2F": 0xd459, + "Wild_SeafoamIslandsB3F": 0xd46f, + "Wild_SeafoamIslandsB4F": 0xd485, + "Wild_PokemonMansion1F": 0xd49b, + "Wild_PokemonMansion2F": 0xd4b1, + "Wild_PokemonMansion3F": 0xd4c7, + "Wild_PokemonMansionB1F": 0xd4dd, + "Wild_Route21": 0xd4f3, + "Wild_Surf_Route21": 0xd508, + "Wild_CeruleanCave1F": 0xd51d, + "Wild_CeruleanCave2F": 0xd533, + "Wild_CeruleanCaveB1F": 0xd549, + "Wild_PowerPlant": 0xd55f, + "Wild_Route23": 0xd575, + "Wild_VictoryRoad2F": 0xd58b, + "Wild_VictoryRoad3F": 0xd5a1, + "Wild_VictoryRoad1F": 0xd5b7, + "Wild_DiglettsCave": 0xd5cd, + "Ghost_Battle5": 0xd723, + "HM_Surf_Badge_a": 0xda11, + "HM_Surf_Badge_b": 0xda16, + "Wild_Old_Rod": 0xe313, + "Wild_Good_Rod": 0xe340, + "Option_Reusable_TMs": 0xe60c, + "Wild_Super_Rod_A": 0xea40, + "Wild_Super_Rod_B": 0xea45, + "Wild_Super_Rod_C": 0xea4a, + "Wild_Super_Rod_D": 0xea51, + "Wild_Super_Rod_E": 0xea56, + "Wild_Super_Rod_F": 0xea5b, + "Wild_Super_Rod_G": 0xea64, + "Wild_Super_Rod_H": 0xea6d, + "Wild_Super_Rod_I": 0xea76, + "Wild_Super_Rod_J": 0xea7f, + "Starting_Money_High": 0xf949, + "Starting_Money_Middle": 0xf94c, + "Starting_Money_Low": 0xf94f, + "HM_Fly_Badge_a": 0x1318e, + "HM_Fly_Badge_b": 0x13193, + "HM_Cut_Badge_a": 0x131c4, + "HM_Cut_Badge_b": 0x131c9, + "HM_Strength_Badge_a": 0x131f4, + "HM_Strength_Badge_b": 0x131f9, + "HM_Flash_Badge_a": 0x13208, + "HM_Flash_Badge_b": 0x1320d, + "Encounter_Chances": 0x13911, + "Option_Viridian_Gym_Badges": 0x1901d, + "Event_Sleepy_Guy": 0x191bc, + "Starter2_K": 0x195a8, + "Starter3_K": 0x195b0, + "Event_Rocket_Thief": 0x196cc, + "Option_Cerulean_Cave_Condition": 0x1986c, + "Event_Stranded_Man": 0x19b2b, + "Event_Rivals_Sister": 0x19cf9, + "Option_Pokemon_League_Badges": 0x19e16, + "Missable_Silph_Co_4F_Item_1": 0x1a0d7, + "Missable_Silph_Co_4F_Item_2": 0x1a0de, + "Missable_Silph_Co_4F_Item_3": 0x1a0e5, + "Missable_Silph_Co_5F_Item_1": 0x1a337, + "Missable_Silph_Co_5F_Item_2": 0x1a33e, + "Missable_Silph_Co_5F_Item_3": 0x1a345, + "Missable_Silph_Co_6F_Item_1": 0x1a5ad, + "Missable_Silph_Co_6F_Item_2": 0x1a5b4, + "Event_Free_Sample": 0x1cade, + "Starter1_F": 0x1cca5, + "Starter2_F": 0x1cca9, + "Starter2_G": 0x1cde2, + "Starter3_G": 0x1cdea, + "Starter2_H": 0x1d0e5, + "Starter1_H": 0x1d0ef, + "Starter3_I": 0x1d0f6, + "Starter2_I": 0x1d100, + "Starter1_D": 0x1d107, + "Starter3_D": 0x1d111, + "Starter2_E": 0x1d2eb, + "Starter3_E": 0x1d2f3, + "Event_Oaks_Gift": 0x1d373, + "Event_Pokemart_Quest": 0x1d566, + "Event_Bicycle_Shop": 0x1d805, + "Text_Bicycle": 0x1d898, + "Event_Fuji": 0x1d9cd, + "Static_Encounter_Mew": 0x1dc4e, + "Gift_Eevee": 0x1dcc7, + "Event_Mr_Psychic": 0x1ddcf, + "Static_Encounter_Voltorb_A": 0x1e397, + "Static_Encounter_Voltorb_B": 0x1e39f, + "Static_Encounter_Voltorb_C": 0x1e3a7, + "Static_Encounter_Electrode_A": 0x1e3af, + "Static_Encounter_Voltorb_D": 0x1e3b7, + "Static_Encounter_Voltorb_E": 0x1e3bf, + "Static_Encounter_Electrode_B": 0x1e3c7, + "Static_Encounter_Voltorb_F": 0x1e3cf, + "Static_Encounter_Zapdos": 0x1e3d7, + "Missable_Power_Plant_Item_1": 0x1e3df, + "Missable_Power_Plant_Item_2": 0x1e3e6, + "Missable_Power_Plant_Item_3": 0x1e3ed, + "Missable_Power_Plant_Item_4": 0x1e3f4, + "Missable_Power_Plant_Item_5": 0x1e3fb, + "Event_Rt16_House_Woman": 0x1e5d4, + "Option_Victory_Road_Badges": 0x1e6a5, + "Event_Bill": 0x1e8d6, + "Starter1_O": 0x372b0, + "Starter2_O": 0x372b4, + "Starter3_O": 0x372b8, + "Base_Stats": 0x383de, + "Starter3_C": 0x39cf2, + "Starter1_C": 0x39cf8, + "Trainer_Data": 0x39d99, + "Rival_Starter2_A": 0x3a1e5, + "Rival_Starter3_A": 0x3a1e8, + "Rival_Starter1_A": 0x3a1eb, + "Rival_Starter2_B": 0x3a1f1, + "Rival_Starter3_B": 0x3a1f7, + "Rival_Starter1_B": 0x3a1fd, + "Rival_Starter2_C": 0x3a207, + "Rival_Starter3_C": 0x3a211, + "Rival_Starter1_C": 0x3a21b, + "Rival_Starter2_D": 0x3a409, + "Rival_Starter3_D": 0x3a413, + "Rival_Starter1_D": 0x3a41d, + "Rival_Starter2_E": 0x3a429, + "Rival_Starter3_E": 0x3a435, + "Rival_Starter1_E": 0x3a441, + "Rival_Starter2_F": 0x3a44d, + "Rival_Starter3_F": 0x3a459, + "Rival_Starter1_F": 0x3a465, + "Rival_Starter2_G": 0x3a473, + "Rival_Starter3_G": 0x3a481, + "Rival_Starter1_G": 0x3a48f, + "Rival_Starter2_H": 0x3a49d, + "Rival_Starter3_H": 0x3a4ab, + "Rival_Starter1_H": 0x3a4b9, + "Trainer_Data_End": 0x3a52e, + "Learnset_Rhydon": 0x3b1d9, + "Learnset_Kangaskhan": 0x3b1e7, + "Learnset_NidoranM": 0x3b1f6, + "Learnset_Clefairy": 0x3b208, + "Learnset_Spearow": 0x3b219, + "Learnset_Voltorb": 0x3b228, + "Learnset_Nidoking": 0x3b234, + "Learnset_Slowbro": 0x3b23c, + "Learnset_Ivysaur": 0x3b24f, + "Learnset_Exeggutor": 0x3b25f, + "Learnset_Lickitung": 0x3b263, + "Learnset_Exeggcute": 0x3b273, + "Learnset_Grimer": 0x3b284, + "Learnset_Gengar": 0x3b292, + "Learnset_NidoranF": 0x3b29b, + "Learnset_Nidoqueen": 0x3b2a9, + "Learnset_Cubone": 0x3b2b4, + "Learnset_Rhyhorn": 0x3b2c3, + "Learnset_Lapras": 0x3b2d1, + "Learnset_Mew": 0x3b2e1, + "Learnset_Gyarados": 0x3b2eb, + "Learnset_Shellder": 0x3b2fb, + "Learnset_Tentacool": 0x3b30a, + "Learnset_Gastly": 0x3b31f, + "Learnset_Scyther": 0x3b325, + "Learnset_Staryu": 0x3b337, + "Learnset_Blastoise": 0x3b347, + "Learnset_Pinsir": 0x3b355, + "Learnset_Tangela": 0x3b363, + "Learnset_Growlithe": 0x3b379, + "Learnset_Onix": 0x3b385, + "Learnset_Fearow": 0x3b391, + "Learnset_Pidgey": 0x3b3a0, + "Learnset_Slowpoke": 0x3b3b1, + "Learnset_Kadabra": 0x3b3c9, + "Learnset_Graveler": 0x3b3e1, + "Learnset_Chansey": 0x3b3ef, + "Learnset_Machoke": 0x3b407, + "Learnset_MrMime": 0x3b413, + "Learnset_Hitmonlee": 0x3b41f, + "Learnset_Hitmonchan": 0x3b42b, + "Learnset_Arbok": 0x3b437, + "Learnset_Parasect": 0x3b443, + "Learnset_Psyduck": 0x3b452, + "Learnset_Drowzee": 0x3b461, + "Learnset_Golem": 0x3b46f, + "Learnset_Magmar": 0x3b47f, + "Learnset_Electabuzz": 0x3b48f, + "Learnset_Magneton": 0x3b49b, + "Learnset_Koffing": 0x3b4ac, + "Learnset_Mankey": 0x3b4bd, + "Learnset_Seel": 0x3b4cc, + "Learnset_Diglett": 0x3b4db, + "Learnset_Tauros": 0x3b4e7, + "Learnset_Farfetchd": 0x3b4f9, + "Learnset_Venonat": 0x3b508, + "Learnset_Dragonite": 0x3b516, + "Learnset_Doduo": 0x3b52b, + "Learnset_Poliwag": 0x3b53c, + "Learnset_Jynx": 0x3b54a, + "Learnset_Moltres": 0x3b558, + "Learnset_Articuno": 0x3b560, + "Learnset_Zapdos": 0x3b568, + "Learnset_Meowth": 0x3b575, + "Learnset_Krabby": 0x3b584, + "Learnset_Vulpix": 0x3b59a, + "Learnset_Pikachu": 0x3b5ac, + "Learnset_Dratini": 0x3b5c1, + "Learnset_Dragonair": 0x3b5d0, + "Learnset_Kabuto": 0x3b5df, + "Learnset_Kabutops": 0x3b5e9, + "Learnset_Horsea": 0x3b5f6, + "Learnset_Seadra": 0x3b602, + "Learnset_Sandshrew": 0x3b615, + "Learnset_Sandslash": 0x3b621, + "Learnset_Omanyte": 0x3b630, + "Learnset_Omastar": 0x3b63a, + "Learnset_Jigglypuff": 0x3b648, + "Learnset_Eevee": 0x3b666, + "Learnset_Flareon": 0x3b670, + "Learnset_Jolteon": 0x3b682, + "Learnset_Vaporeon": 0x3b694, + "Learnset_Machop": 0x3b6a9, + "Learnset_Zubat": 0x3b6b8, + "Learnset_Ekans": 0x3b6c7, + "Learnset_Paras": 0x3b6d6, + "Learnset_Poliwhirl": 0x3b6e6, + "Learnset_Poliwrath": 0x3b6f4, + "Learnset_Beedrill": 0x3b704, + "Learnset_Dodrio": 0x3b714, + "Learnset_Primeape": 0x3b722, + "Learnset_Dugtrio": 0x3b72e, + "Learnset_Venomoth": 0x3b73a, + "Learnset_Dewgong": 0x3b748, + "Learnset_Butterfree": 0x3b762, + "Learnset_Machamp": 0x3b772, + "Learnset_Golduck": 0x3b780, + "Learnset_Hypno": 0x3b78c, + "Learnset_Golbat": 0x3b79a, + "Learnset_Mewtwo": 0x3b7a6, + "Learnset_Snorlax": 0x3b7b2, + "Learnset_Magikarp": 0x3b7bf, + "Learnset_Muk": 0x3b7c7, + "Learnset_Kingler": 0x3b7d7, + "Learnset_Cloyster": 0x3b7e3, + "Learnset_Electrode": 0x3b7e9, + "Learnset_Weezing": 0x3b7f7, + "Learnset_Persian": 0x3b803, + "Learnset_Marowak": 0x3b80f, + "Learnset_Haunter": 0x3b827, + "Learnset_Alakazam": 0x3b832, + "Learnset_Pidgeotto": 0x3b843, + "Learnset_Pidgeot": 0x3b851, + "Learnset_Bulbasaur": 0x3b864, + "Learnset_Venusaur": 0x3b874, + "Learnset_Tentacruel": 0x3b884, + "Learnset_Goldeen": 0x3b89b, + "Learnset_Seaking": 0x3b8a9, + "Learnset_Ponyta": 0x3b8c2, + "Learnset_Rapidash": 0x3b8d0, + "Learnset_Rattata": 0x3b8e1, + "Learnset_Raticate": 0x3b8eb, + "Learnset_Nidorino": 0x3b8f9, + "Learnset_Nidorina": 0x3b90b, + "Learnset_Geodude": 0x3b91c, + "Learnset_Porygon": 0x3b92a, + "Learnset_Aerodactyl": 0x3b934, + "Learnset_Magnemite": 0x3b942, + "Learnset_Charmander": 0x3b957, + "Learnset_Squirtle": 0x3b968, + "Learnset_Charmeleon": 0x3b979, + "Learnset_Wartortle": 0x3b98a, + "Learnset_Charizard": 0x3b998, + "Learnset_Oddish": 0x3b9b1, + "Learnset_Gloom": 0x3b9c3, + "Learnset_Vileplume": 0x3b9d1, + "Learnset_Bellsprout": 0x3b9dc, + "Learnset_Weepinbell": 0x3b9f0, + "Learnset_Victreebel": 0x3ba00, + "Type_Chart": 0x3e4b6, + "Type_Chart_Divider": 0x3e5ac, + "Ghost_Battle3": 0x3efd9, + "Missable_Pokemon_Mansion_1F_Item_1": 0x443d6, + "Missable_Pokemon_Mansion_1F_Item_2": 0x443dd, + "Map_Rock_TunnelF": 0x44676, + "Missable_Victory_Road_3F_Item_1": 0x44b07, + "Missable_Victory_Road_3F_Item_2": 0x44b0e, + "Missable_Rocket_Hideout_B1F_Item_1": 0x44d2d, + "Missable_Rocket_Hideout_B1F_Item_2": 0x44d34, + "Missable_Rocket_Hideout_B2F_Item_1": 0x4511d, + "Missable_Rocket_Hideout_B2F_Item_2": 0x45124, + "Missable_Rocket_Hideout_B2F_Item_3": 0x4512b, + "Missable_Rocket_Hideout_B2F_Item_4": 0x45132, + "Missable_Rocket_Hideout_B3F_Item_1": 0x4536f, + "Missable_Rocket_Hideout_B3F_Item_2": 0x45376, + "Missable_Rocket_Hideout_B4F_Item_1": 0x45627, + "Missable_Rocket_Hideout_B4F_Item_2": 0x4562e, + "Missable_Rocket_Hideout_B4F_Item_3": 0x45635, + "Missable_Rocket_Hideout_B4F_Item_4": 0x4563c, + "Missable_Rocket_Hideout_B4F_Item_5": 0x45643, + "Missable_Safari_Zone_East_Item_1": 0x458b2, + "Missable_Safari_Zone_East_Item_2": 0x458b9, + "Missable_Safari_Zone_East_Item_3": 0x458c0, + "Missable_Safari_Zone_East_Item_4": 0x458c7, + "Missable_Safari_Zone_North_Item_1": 0x45a12, + "Missable_Safari_Zone_North_Item_2": 0x45a19, + "Missable_Safari_Zone_Center_Item": 0x45bf9, + "Missable_Cerulean_Cave_2F_Item_1": 0x45e36, + "Missable_Cerulean_Cave_2F_Item_2": 0x45e3d, + "Missable_Cerulean_Cave_2F_Item_3": 0x45e44, + "Static_Encounter_Mewtwo": 0x45f44, + "Missable_Cerulean_Cave_B1F_Item_1": 0x45f4c, + "Missable_Cerulean_Cave_B1F_Item_2": 0x45f53, + "Missable_Rock_Tunnel_B1F_Item_1": 0x4619f, + "Missable_Rock_Tunnel_B1F_Item_2": 0x461a6, + "Missable_Rock_Tunnel_B1F_Item_3": 0x461ad, + "Missable_Rock_Tunnel_B1F_Item_4": 0x461b4, + "Static_Encounter_Articuno": 0x4690c, + "Hidden_Item_Viridian_Forest_1": 0x46e6d, + "Hidden_Item_Viridian_Forest_2": 0x46e73, + "Hidden_Item_MtMoonB2F_1": 0x46e7a, + "Hidden_Item_MtMoonB2F_2": 0x46e80, + "Hidden_Item_Route_25_1": 0x46e94, + "Hidden_Item_Route_25_2": 0x46e9a, + "Hidden_Item_Route_9": 0x46ea1, + "Hidden_Item_SS_Anne_Kitchen": 0x46eb4, + "Hidden_Item_SS_Anne_B1F": 0x46ebb, + "Hidden_Item_Route_10_1": 0x46ec2, + "Hidden_Item_Route_10_2": 0x46ec8, + "Hidden_Item_Rocket_Hideout_B1F": 0x46ecf, + "Hidden_Item_Rocket_Hideout_B3F": 0x46ed6, + "Hidden_Item_Rocket_Hideout_B4F": 0x46edd, + "Hidden_Item_Pokemon_Tower_5F": 0x46ef1, + "Hidden_Item_Route_13_1": 0x46ef8, + "Hidden_Item_Route_13_2": 0x46efe, + "Hidden_Item_Safari_Zone_West": 0x46f0c, + "Hidden_Item_Silph_Co_5F": 0x46f13, + "Hidden_Item_Silph_Co_9F": 0x46f1a, + "Hidden_Item_Copycats_House": 0x46f21, + "Hidden_Item_Cerulean_Cave_1F": 0x46f28, + "Hidden_Item_Cerulean_Cave_B1F": 0x46f2f, + "Hidden_Item_Power_Plant_1": 0x46f36, + "Hidden_Item_Power_Plant_2": 0x46f3c, + "Hidden_Item_Seafoam_Islands_B2F": 0x46f43, + "Hidden_Item_Seafoam_Islands_B4F": 0x46f4a, + "Hidden_Item_Pokemon_Mansion_1F": 0x46f51, + "Hidden_Item_Pokemon_Mansion_3F": 0x46f65, + "Hidden_Item_Pokemon_Mansion_B1F": 0x46f72, + "Hidden_Item_Route_23_1": 0x46f85, + "Hidden_Item_Route_23_2": 0x46f8b, + "Hidden_Item_Route_23_3": 0x46f91, + "Hidden_Item_Victory_Road_2F_1": 0x46f98, + "Hidden_Item_Victory_Road_2F_2": 0x46f9e, + "Hidden_Item_Unused_6F": 0x46fa5, + "Hidden_Item_Viridian_City": 0x46fb3, + "Hidden_Item_Route_11": 0x47060, + "Hidden_Item_Route_12": 0x47067, + "Hidden_Item_Route_17_1": 0x47075, + "Hidden_Item_Route_17_2": 0x4707b, + "Hidden_Item_Route_17_3": 0x47081, + "Hidden_Item_Route_17_4": 0x47087, + "Hidden_Item_Route_17_5": 0x4708d, + "Hidden_Item_Underground_Path_NS_1": 0x47094, + "Hidden_Item_Underground_Path_NS_2": 0x4709a, + "Hidden_Item_Underground_Path_WE_1": 0x470a1, + "Hidden_Item_Underground_Path_WE_2": 0x470a7, + "Hidden_Item_Celadon_City": 0x470ae, + "Hidden_Item_Seafoam_Islands_B3F": 0x470b5, + "Hidden_Item_Vermilion_City": 0x470bc, + "Hidden_Item_Cerulean_City": 0x470c3, + "Hidden_Item_Route_4": 0x470ca, + "Event_Counter": 0x482d3, + "Event_Thirsty_Girl_Lemonade": 0x484f9, + "Event_Thirsty_Girl_Soda": 0x4851d, + "Event_Thirsty_Girl_Water": 0x48541, + "Option_Tea": 0x4871d, + "Event_Mansion_Lady": 0x4872a, + "Badge_Celadon_Gym": 0x48a1b, + "Event_Celadon_Gym": 0x48a2f, + "Event_Gambling_Addict": 0x49293, + "Gift_Magikarp": 0x49430, + "Option_Aide_Rt11": 0x4958d, + "Event_Rt11_Oaks_Aide": 0x49591, + "Event_Mourning_Girl": 0x4968b, + "Option_Aide_Rt15": 0x49776, + "Event_Rt_15_Oaks_Aide": 0x4977a, + "Missable_Mt_Moon_1F_Item_1": 0x49c75, + "Missable_Mt_Moon_1F_Item_2": 0x49c7c, + "Missable_Mt_Moon_1F_Item_3": 0x49c83, + "Missable_Mt_Moon_1F_Item_4": 0x49c8a, + "Missable_Mt_Moon_1F_Item_5": 0x49c91, + "Missable_Mt_Moon_1F_Item_6": 0x49c98, + "Dome_Fossil_Text": 0x4a001, + "Event_Dome_Fossil": 0x4a021, + "Helix_Fossil_Text": 0x4a05d, + "Event_Helix_Fossil": 0x4a07d, + "Missable_Mt_Moon_B2F_Item_1": 0x4a166, + "Missable_Mt_Moon_B2F_Item_2": 0x4a16d, + "Missable_Safari_Zone_West_Item_1": 0x4a34f, + "Missable_Safari_Zone_West_Item_2": 0x4a356, + "Missable_Safari_Zone_West_Item_3": 0x4a35d, + "Missable_Safari_Zone_West_Item_4": 0x4a364, + "Event_Safari_Zone_Secret_House": 0x4a469, + "Missable_Route_24_Item": 0x506e6, + "Missable_Route_25_Item": 0x5080b, + "Starter2_B": 0x50fce, + "Starter3_B": 0x50fd0, + "Starter1_B": 0x50fd2, + "Starter2_A": 0x510f1, + "Starter3_A": 0x510f3, + "Starter1_A": 0x510f5, + "Option_Badge_Goal": 0x51317, + "Event_Nugget_Bridge": 0x5148f, + "Static_Encounter_Moltres": 0x51939, + "Missable_Victory_Road_2F_Item_1": 0x51941, + "Missable_Victory_Road_2F_Item_2": 0x51948, + "Missable_Victory_Road_2F_Item_3": 0x5194f, + "Missable_Victory_Road_2F_Item_4": 0x51956, + "Starter2_L": 0x51c85, + "Starter3_L": 0x51c8d, + "Gift_Lapras": 0x51d83, + "Missable_Silph_Co_7F_Item_1": 0x51f0d, + "Missable_Silph_Co_7F_Item_2": 0x51f14, + "Missable_Pokemon_Mansion_2F_Item": 0x520c9, + "Missable_Pokemon_Mansion_3F_Item_1": 0x522e2, + "Missable_Pokemon_Mansion_3F_Item_2": 0x522e9, + "Missable_Pokemon_Mansion_B1F_Item_1": 0x5248c, + "Missable_Pokemon_Mansion_B1F_Item_2": 0x52493, + "Missable_Pokemon_Mansion_B1F_Item_3": 0x5249a, + "Missable_Pokemon_Mansion_B1F_Item_4": 0x524a1, + "Missable_Pokemon_Mansion_B1F_Item_5": 0x524ae, + "Option_Safari_Zone_Battle_Type": 0x525c3, + "Prize_Mon_A2": 0x5282f, + "Prize_Mon_B2": 0x52830, + "Prize_Mon_C2": 0x52831, + "Prize_Mon_D2": 0x5283a, + "Prize_Mon_E2": 0x5283b, + "Prize_Mon_F2": 0x5283c, + "Prize_Mon_A": 0x52960, + "Prize_Mon_B": 0x52962, + "Prize_Mon_C": 0x52964, + "Prize_Mon_D": 0x52966, + "Prize_Mon_E": 0x52968, + "Prize_Mon_F": 0x5296a, + "Missable_Route_2_Item_1": 0x5404a, + "Missable_Route_2_Item_2": 0x54051, + "Missable_Route_4_Item": 0x543df, + "Missable_Route_9_Item": 0x546fd, + "Option_EXP_Modifier": 0x552c5, + "Rod_Vermilion_City_Fishing_Guru": 0x560df, + "Rod_Fuchsia_City_Fishing_Brother": 0x561eb, + "Rod_Route12_Fishing_Brother": 0x564ee, + "Missable_Route_12_Item_1": 0x58704, + "Missable_Route_12_Item_2": 0x5870b, + "Missable_Route_15_Item": 0x589c7, + "Ghost_Battle6": 0x58df0, + "Static_Encounter_Snorlax_A": 0x5969b, + "Static_Encounter_Snorlax_B": 0x599db, + "Event_Pokemon_Fan_Club": 0x59c8b, + "Event_Scared_Woman": 0x59e1f, + "Missable_Silph_Co_3F_Item": 0x5a0cb, + "Missable_Silph_Co_10F_Item_1": 0x5a281, + "Missable_Silph_Co_10F_Item_2": 0x5a288, + "Missable_Silph_Co_10F_Item_3": 0x5a28f, + "Guard_Drink_List": 0x5a600, + "Event_Museum": 0x5c266, + "Badge_Pewter_Gym": 0x5c3ed, + "Event_Pewter_Gym": 0x5c401, + "Badge_Cerulean_Gym": 0x5c716, + "Event_Cerulean_Gym": 0x5c72a, + "Badge_Vermilion_Gym": 0x5caba, + "Event_Vermillion_Gym": 0x5cace, + "Event_Copycat": 0x5cca9, + "Gift_Hitmonlee": 0x5cf1a, + "Gift_Hitmonchan": 0x5cf62, + "Badge_Saffron_Gym": 0x5d079, + "Event_Saffron_Gym": 0x5d08d, + "Option_Aide_Rt2": 0x5d5f2, + "Event_Route_2_Oaks_Aide": 0x5d5f6, + "Missable_Victory_Road_1F_Item_1": 0x5dae6, + "Missable_Victory_Road_1F_Item_2": 0x5daed, + "Starter2_J": 0x6060e, + "Starter3_J": 0x60616, + "Missable_Pokemon_Tower_3F_Item": 0x60787, + "Missable_Pokemon_Tower_4F_Item_1": 0x608b5, + "Missable_Pokemon_Tower_4F_Item_2": 0x608bc, + "Missable_Pokemon_Tower_4F_Item_3": 0x608c3, + "Missable_Pokemon_Tower_5F_Item": 0x60a80, + "Ghost_Battle1": 0x60b33, + "Ghost_Battle2": 0x60c0a, + "Missable_Pokemon_Tower_6F_Item_1": 0x60c85, + "Missable_Pokemon_Tower_6F_Item_2": 0x60c8c, + "Gift_Aerodactyl": 0x61064, + "Gift_Omanyte": 0x61068, + "Gift_Kabuto": 0x6106c, + "Missable_Viridian_Forest_Item_1": 0x6122c, + "Missable_Viridian_Forest_Item_2": 0x61233, + "Missable_Viridian_Forest_Item_3": 0x6123a, + "Starter2_M": 0x61450, + "Starter3_M": 0x61458, + "Event_SS_Anne_Captain": 0x618c3, + "Missable_SS_Anne_1F_Item": 0x61ac0, + "Missable_SS_Anne_2F_Item_1": 0x61ced, + "Missable_SS_Anne_2F_Item_2": 0x61d00, + "Missable_SS_Anne_B1F_Item_1": 0x61ee3, + "Missable_SS_Anne_B1F_Item_2": 0x61eea, + "Missable_SS_Anne_B1F_Item_3": 0x61ef1, + "Event_Silph_Co_President": 0x622ed, + "Ghost_Battle4": 0x708e1, + "Badge_Viridian_Gym": 0x749ca, + "Event_Viridian_Gym": 0x749de, + "Missable_Viridian_Gym_Item": 0x74c63, + "Missable_Cerulean_Cave_1F_Item_1": 0x74d68, + "Missable_Cerulean_Cave_1F_Item_2": 0x74d6f, + "Missable_Cerulean_Cave_1F_Item_3": 0x74d76, + "Event_Warden": 0x7512a, + "Missable_Wardens_House_Item": 0x751b7, + "Badge_Fuchsia_Gym": 0x755cd, + "Event_Fuschia_Gym": 0x755e1, + "Badge_Cinnabar_Gym": 0x75995, + "Event_Cinnabar_Gym": 0x759a9, + "Event_Lab_Scientist": 0x75dd6, + "Fossils_Needed_For_Second_Item": 0x75ea3, + "Event_Dome_Fossil_B": 0x75f20, + "Event_Helix_Fossil_B": 0x75f40, + "Starter2_N": 0x76169, + "Starter3_N": 0x76171, + "Option_Itemfinder": 0x76864, + "Text_Badges_Needed": 0x92304, + "Badge_Text_Boulder_Badge": 0x990b3, + "Badge_Text_Cascade_Badge": 0x990cb, + "Badge_Text_Thunder_Badge": 0x99111, + "Badge_Text_Rainbow_Badge": 0x9912e, + "Badge_Text_Soul_Badge": 0x99177, + "Badge_Text_Marsh_Badge": 0x9918c, + "Badge_Text_Volcano_Badge": 0x991d6, + "Badge_Text_Earth_Badge": 0x991f3, +} diff --git a/worlds/pokemon_rb/rules.py b/worlds/pokemon_rb/rules.py new file mode 100644 index 00000000..0e97b705 --- /dev/null +++ b/worlds/pokemon_rb/rules.py @@ -0,0 +1,165 @@ +from ..generic.Rules import add_item_rule, add_rule + +def set_rules(world, player): + + add_item_rule(world.get_location("Pallet Town - Player's PC", player), + lambda i: i.player == player and "Badge" not in i.name) + + access_rules = { + + "Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player), + "Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player), + "Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player), + "Route 2 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.world.oaks_aide_rt_2[player].value + 5, player), + "Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player), + "Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player), + "Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player), + "Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), + "Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), + "Route 11 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.world.oaks_aide_rt_11[player].value + 5, player), + "Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player), + "Silph Co 11F - Silph Co President": lambda state: state.has("Card Key", player), + "Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player), + "Route 12 - Island Item": lambda state: state.pokemon_rb_can_surf(player), + "Route 12 - Item Behind Cuttable Tree": lambda state: state.pokemon_rb_can_cut(player), + "Route 15 - Item": lambda state: state.pokemon_rb_can_cut(player), + "Route 25 - Item": lambda state: state.pokemon_rb_can_cut(player), + "Fuchsia City - Warden's House Item": lambda state: state.pokemon_rb_can_strength(player), + "Rocket Hideout B4F - Southwest Item (Lift Key)": lambda state: state.has("Lift Key", player), + "Rocket Hideout B4F - Giovanni Item (Lift Key)": lambda state: state.has("Lift Key", player), + "Silph Co 3F - Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 4F - Left Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 4F - Middle Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 4F - Right Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 5F - Northwest Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 6F - West Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 6F - Southwest Item (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 7F - East Item (Card Key)": lambda state: state.has("Card Key", player), + "Safari Zone Center - Island Item": lambda state: state.pokemon_rb_can_surf(player), + + "Silph Co 11F - Silph Co Liberated": lambda state: state.has("Card Key", player), + + "Pallet Town - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Pallet Town - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 22 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 22 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 24 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 24 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 24 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Route 6 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 6 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 10 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 10 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Safari Zone Center - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Safari Zone Center - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Safari Zone Center - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Safari Zone Center - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player), + "Route 12 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 12 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 12 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Route 12 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player), + "Route 19 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 19 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 19 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Route 19 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player), + "Route 23 - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Route 23 - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Route 23 - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Route 23 - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player), + "Fuchsia City - Super Rod Pokemon - 1": lambda state: state.has("Super Rod", player), + "Fuchsia City - Super Rod Pokemon - 2": lambda state: state.has("Super Rod", player), + "Fuchsia City - Super Rod Pokemon - 3": lambda state: state.has("Super Rod", player), + "Fuchsia City - Super Rod Pokemon - 4": lambda state: state.has("Super Rod", player), + "Anywhere - Good Rod Pokemon - 1": lambda state: state.has("Good Rod", player), + "Anywhere - Good Rod Pokemon - 2": lambda state: state.has("Good Rod", player), + "Anywhere - Old Rod Pokemon": lambda state: state.has("Old Rod", player), + "Celadon Prize Corner - Pokemon Prize - 1": lambda state: state.has("Coin Case", player), + "Celadon Prize Corner - Pokemon Prize - 2": lambda state: state.has("Coin Case", player), + "Celadon Prize Corner - Pokemon Prize - 3": lambda state: state.has("Coin Case", player), + "Celadon Prize Corner - Pokemon Prize - 4": lambda state: state.has("Coin Case", player), + "Celadon Prize Corner - Pokemon Prize - 5": lambda state: state.has("Coin Case", player), + "Celadon Prize Corner - Pokemon Prize - 6": lambda state: state.has("Coin Case", player), + "Cinnabar Island - Old Amber Pokemon": lambda state: state.has("Old Amber", player), + "Cinnabar Island - Helix Fossil Pokemon": lambda state: state.has("Helix Fossil", player), + "Cinnabar Island - Dome Fossil Pokemon": lambda state: state.has("Dome Fossil", player), + "Route 12 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player), + "Route 16 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player), + "Seafoam Islands B4F - Legendary Pokemon": lambda state: state.pokemon_rb_can_strength(player), + "Vermilion City - Legendary Pokemon": lambda state: state.pokemon_rb_can_surf(player) and state.has("S.S. Ticket", player) + } + + hidden_item_access_rules = { + "Viridian Forest - Hidden Item Northwest by Trainer": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Viridian Forest - Hidden Item Entrance Tree": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Mt Moon B2F - Hidden Item Dead End Before Fossils": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 25 - Hidden Item Fence Outside Bill's House": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 9 - Hidden Item Rock By Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "S.S. Anne 1F - Hidden Item Kitchen Trash": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "S.S. Anne B1F - Hidden Item Under Pillow": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 10 - Hidden Item Behind Rock Tunnel Entrance Tree": lambda + state: state.pokemon_rb_can_get_hidden_items(player), + "Route 10 - Hidden Item Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Rocket Hideout B1F - Hidden Item Pot Plant": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Rocket Hideout B3F - Hidden Item Near East Item": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Rocket Hideout B4F - Hidden Item Behind Giovanni": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Pokemon Tower 5F - Hidden Item Near West Staircase": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 13 - Hidden Item Dead End Boulder": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 13 - Hidden Item Dead End By Water Corner": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Pokemon Mansion B1F - Hidden Item Secret Key Room Corner": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Safari Zone West - Hidden Item Secret House Statue": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Silph Co 5F - Hidden Item Pot Plant": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Silph Co 9F - Hidden Item Nurse Bed": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Copycat's House - Hidden Item Desk": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Cerulean Cave 1F - Hidden Item Center Rocks": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Cerulean Cave B1F - Hidden Item Northeast Rocks": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Power Plant - Hidden Item Central Dead End": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Power Plant - Hidden Item Before Zapdos": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Seafoam Islands B2F - Hidden Item Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Seafoam Islands B4F - Hidden Item Corner Island": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Pokemon Mansion 1F - Hidden Item Block Near Entrance Carpet": lambda + state: state.pokemon_rb_can_get_hidden_items(player), + "Pokemon Mansion 3F - Hidden Item Behind Burglar": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 23 - Hidden Item Rocks Before Final Guard": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 23 - Hidden Item East Tree After Water": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 23 - Hidden Item On Island": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Victory Road 2F - Hidden Item Rock Before Moltres": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Victory Road 2F - Hidden Item Rock In Final Room": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Viridian City - Hidden Item Cuttable Tree": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 11 - Hidden Item Isolated Tree Near Gate": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 12 - Hidden Item Tree Near Gate": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 17 - Hidden Item In Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 17 - Hidden Item Near Northernmost Sign": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 17 - Hidden Item East Center": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 17 - Hidden Item West Center": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Route 17 - Hidden Item Before Final Bridge": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Underground Tunnel North-South - Hidden Item Near Northern Stairs": lambda + state: state.pokemon_rb_can_get_hidden_items(player), + "Underground Tunnel North-South - Hidden Item Near Southern Stairs": lambda + state: state.pokemon_rb_can_get_hidden_items(player), + "Underground Tunnel West-East - Hidden Item West": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Underground Tunnel West-East - Hidden Item East": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Celadon City - Hidden Item Dead End Near Cuttable Tree": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 25 - Hidden Item Northeast Of Grass": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Mt Moon B2F - Hidden Item Lone Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Seafoam Islands B3F - Hidden Item Rock": lambda state: state.pokemon_rb_can_get_hidden_items(player), + "Vermilion City - Hidden Item In Water Near Fan Club": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Cerulean City - Hidden Item Gym Badge Guy's Backyard": lambda state: state.pokemon_rb_can_get_hidden_items( + player), + "Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: state.pokemon_rb_can_get_hidden_items(player), + } + for loc, rule in access_rules.items(): + add_rule(world.get_location(loc, player), rule) + if world.randomize_hidden_items[player].value != 0: + for loc, rule in hidden_item_access_rules.items(): + add_rule(world.get_location(loc, player), rule) diff --git a/worlds/pokemon_rb/text.py b/worlds/pokemon_rb/text.py new file mode 100644 index 00000000..e15623d4 --- /dev/null +++ b/worlds/pokemon_rb/text.py @@ -0,0 +1,147 @@ +special_chars = { + "PKMN": 0x4A, + "'d": 0xBB, + "'l": 0xBC, + "'t": 0xBE, + "'v": 0xBF, + "PK": 0xE1, + "MN": 0xE2, + "'r": 0xE4, + "'m": 0xE5, + "MALE": 0xEF, + "FEMALE": 0xF5, +} + +char_map = { + "@": 0x50, # String terminator + "#": 0x54, # Poké + "‘": 0x70, + "’": 0x71, + "“": 0x72, + "”": 0x73, + "·": 0x74, + "…": 0x75, + " ": 0x7F, + "A": 0x80, + "B": 0x81, + "C": 0x82, + "D": 0x83, + "E": 0x84, + "F": 0x85, + "G": 0x86, + "H": 0x87, + "I": 0x88, + "J": 0x89, + "K": 0x8A, + "L": 0x8B, + "M": 0x8C, + "N": 0x8D, + "O": 0x8E, + "P": 0x8F, + "Q": 0x90, + "R": 0x91, + "S": 0x92, + "T": 0x93, + "U": 0x94, + "V": 0x95, + "W": 0x96, + "X": 0x97, + "Y": 0x98, + "Z": 0x99, + "(": 0x9A, + ")": 0x9B, + ":": 0x9C, + ";": 0x9D, + "[": 0x9E, + "]": 0x9F, + "a": 0xA0, + "b": 0xA1, + "c": 0xA2, + "d": 0xA3, + "e": 0xA4, + "f": 0xA5, + "g": 0xA6, + "h": 0xA7, + "i": 0xA8, + "j": 0xA9, + "k": 0xAA, + "l": 0xAB, + "m": 0xAC, + "n": 0xAD, + "o": 0xAE, + "p": 0xAF, + "q": 0xB0, + "r": 0xB1, + "s": 0xB2, + "t": 0xB3, + "u": 0xB4, + "v": 0xB5, + "w": 0xB6, + "x": 0xB7, + "y": 0xB8, + "z": 0xB9, + "é": 0xBA, + "'": 0xE0, + "-": 0xE3, + "?": 0xE6, + "!": 0xE7, + ".": 0xE8, + "♂": 0xEF, + "¥": 0xF0, + "$": 0xF0, + "×": 0xF1, + "/": 0xF3, + ",": 0xF4, + "♀": 0xF5, + "0": 0xF6, + "1": 0xF7, + "2": 0xF8, + "3": 0xF9, + "4": 0xFA, + "5": 0xFB, + "6": 0xFC, + "7": 0xFD, + "8": 0xFE, + "9": 0xFF, +} + +unsafe_chars = ["@", "#", "PKMN"] + + +def encode_text(text: str, length: int=0, whitespace=False, force=False, safety=False): + encoded_text = bytearray() + spec_char = "" + special = False + for char in text: + if char == ">": + if spec_char in unsafe_chars and safety: + raise KeyError(f"Disallowed Pokemon text special character '<{spec_char}>'") + try: + encoded_text.append(special_chars[spec_char]) + except KeyError: + if force: + encoded_text.append(char_map[" "]) + else: + raise KeyError(f"Invalid Pokemon text special character '<{spec_char}>'") + spec_char = "" + special = False + elif char == "<": + spec_char = "" + special = True + elif special is True: + spec_char += char + else: + if char in unsafe_chars and safety: + raise KeyError(f"Disallowed Pokemon text character '{char}'") + try: + encoded_text.append(char_map[char]) + except KeyError: + if force: + encoded_text.append(char_map[" "]) + else: + raise KeyError(f"Invalid Pokemon text character '{char}'") + if length > 0: + encoded_text = encoded_text[:length] + while whitespace and len(encoded_text) < length: + encoded_text.append(char_map[" " if whitespace is True else whitespace]) + return encoded_text