diff --git a/pyboy/plugins/game_wrapper_pokemon_red/__init__.py b/pyboy/plugins/game_wrapper_pokemon_red/__init__.py new file mode 100644 index 000000000..483ff23f9 --- /dev/null +++ b/pyboy/plugins/game_wrapper_pokemon_red/__init__.py @@ -0,0 +1 @@ +from .wrapper import GameWrapperPokemonRed diff --git a/pyboy/plugins/game_wrapper_pokemon_red/constants.py b/pyboy/plugins/game_wrapper_pokemon_red/constants.py new file mode 100644 index 000000000..4e857a32f --- /dev/null +++ b/pyboy/plugins/game_wrapper_pokemon_red/constants.py @@ -0,0 +1,1399 @@ +STRING_TERMINATOR = 80 +ASCII_DELTA = 63 + +# Moves +MOVE_POUND = 0x01 +MOVE_KARATE_CHOP = 0x02 +MOVE_DOUBLESLAP = 0x03 +MOVE_COMET_PUNCH = 0x04 +MOVE_MEGA_PUNCH = 0x05 +MOVE_PAY_DAY = 0x06 +MOVE_FIRE_PUNCH = 0x07 +MOVE_ICE_PUNCH = 0x08 +MOVE_THUNDERPUNCH = 0x09 +MOVE_SCRATCH = 0x0a +MOVE_VICEGRIP = 0x0b +MOVE_GUILLOTINE = 0x0c +MOVE_RAZOR_WIND = 0x0d +MOVE_SWORDS_DANCE = 0x0e +MOVE_CUT = 0x0f +MOVE_GUST = 0x10 +MOVE_WING_ATTACK = 0x11 +MOVE_WHIRLWIND = 0x12 +MOVE_FLY = 0x13 +MOVE_BIND = 0x14 +MOVE_SLAM = 0x15 +MOVE_VINE_WHIP = 0x16 +MOVE_STOMP = 0x17 +MOVE_DOUBLE_KICK = 0x18 +MOVE_MEGA_KICK = 0x19 +MOVE_JUMP_KICK = 0x1a +MOVE_ROLLING_KICK = 0x1b +MOVE_SAND_ATTACK = 0x1c +MOVE_HEADBUTT = 0x1d +MOVE_HORN_ATTACK = 0x1e +MOVE_FURY_ATTACK = 0x1f +MOVE_HORN_DRILL = 0x20 +MOVE_TACKLE = 0x21 +MOVE_BODY_SLAM = 0x22 +MOVE_WRAP = 0x23 +MOVE_TAKE_DOWN = 0x24 +MOVE_THRASH = 0x25 +MOVE_DOUBLE_EDGE = 0x26 +MOVE_TAIL_WHIP = 0x27 +MOVE_POISON_STING = 0x28 +MOVE_TWINEEDLE = 0x29 +MOVE_PIN_MISSILE = 0x2a +MOVE_LEER = 0x2b +MOVE_BITE = 0x2c +MOVE_GROWL = 0x2d +MOVE_ROAR = 0x2e +MOVE_SING = 0x2f +MOVE_SUPERSONIC = 0x30 +MOVE_SONICBOOM = 0x31 +MOVE_DISABLE = 0x32 +MOVE_ACID = 0x33 +MOVE_EMBER = 0x34 +MOVE_FLAMETHROWER = 0x35 +MOVE_MIST = 0x36 +MOVE_WATER_GUN = 0x37 +MOVE_HYDRO_PUMP = 0x38 +MOVE_SURF = 0x39 +MOVE_ICE_BEAM = 0x3a +MOVE_BLIZZARD = 0x3b +MOVE_PSYBEAM = 0x3c +MOVE_BUBBLEBEAM = 0x3d +MOVE_AURORA_BEAM = 0x3e +MOVE_HYPER_BEAM = 0x3f +MOVE_PECK = 0x40 +MOVE_DRILL_PECK = 0x41 +MOVE_SUBMISSION = 0x42 +MOVE_LOW_KICK = 0x43 +MOVE_COUNTER = 0x44 +MOVE_SEISMIC_TOSS = 0x45 +MOVE_STRENGTH = 0x46 +MOVE_ABSORB = 0x47 +MOVE_MEGA_DRAIN = 0x48 +MOVE_LEECH_SEED = 0x49 +MOVE_GROWTH = 0x4a +MOVE_RAZOR_LEAF = 0x4b +MOVE_SOLARBEAM = 0x4c +MOVE_POISONPOWDER = 0x4d +MOVE_STUN_SPORE = 0x4e +MOVE_SLEEP_POWDER = 0x4f +MOVE_PETAL_DANCE = 0x50 +MOVE_STRING_SHOT = 0x51 +MOVE_DRAGON_RAGE = 0x52 +MOVE_FIRE_SPIN = 0x53 +MOVE_THUNDERSHOCK = 0x54 +MOVE_THUNDERBOLT = 0x55 +MOVE_THUNDER_WAVE = 0x56 +MOVE_THUNDER = 0x57 +MOVE_ROCK_THROW = 0x58 +MOVE_EARTHQUAKE = 0x59 +MOVE_FISSURE = 0x5a +MOVE_DIG = 0x5b +MOVE_TOXIC = 0x5c +MOVE_CONFUSION = 0x5d +MOVE_PSYCHIC_M = 0x5e +MOVE_HYPNOSIS = 0x5f +MOVE_MEDITATE = 0x60 +MOVE_AGILITY = 0x61 +MOVE_QUICK_ATTACK = 0x62 +MOVE_RAGE = 0x63 +MOVE_TELEPORT = 0x64 +MOVE_NIGHT_SHADE = 0x65 +MOVE_MIMIC = 0x66 +MOVE_SCREECH = 0x67 +MOVE_DOUBLE_TEAM = 0x68 +MOVE_RECOVER = 0x69 +MOVE_HARDEN = 0x6a +MOVE_MINIMIZE = 0x6b +MOVE_SMOKESCREEN = 0x6c +MOVE_CONFUSE_RAY = 0x6d +MOVE_WITHDRAW = 0x6e +MOVE_DEFENSE_CURL = 0x6f +MOVE_BARRIER = 0x70 +MOVE_LIGHT_SCREEN = 0x71 +MOVE_HAZE = 0x72 +MOVE_REFLECT = 0x73 +MOVE_FOCUS_ENERGY = 0x74 +MOVE_BIDE = 0x75 +MOVE_METRONOME = 0x76 +MOVE_MIRROR_MOVE = 0x77 +MOVE_SELFDESTRUCT = 0x78 +MOVE_EGG_BOMB = 0x79 +MOVE_LICK = 0x7a +MOVE_SMOG = 0x7b +MOVE_SLUDGE = 0x7c +MOVE_BONE_CLUB = 0x7d +MOVE_FIRE_BLAST = 0x7e +MOVE_WATERFALL = 0x7f +MOVE_CLAMP = 0x80 +MOVE_SWIFT = 0x81 +MOVE_SKULL_BASH = 0x82 +MOVE_SPIKE_CANNON = 0x83 +MOVE_CONSTRICT = 0x84 +MOVE_AMNESIA = 0x85 +MOVE_KINESIS = 0x86 +MOVE_SOFTBOILED = 0x87 +MOVE_HI_JUMP_KICK = 0x88 +MOVE_GLARE = 0x89 +MOVE_DREAM_EATER = 0x8a +MOVE_POISON_GAS = 0x8b +MOVE_BARRAGE = 0x8c +MOVE_LEECH_LIFE = 0x8d +MOVE_LOVELY_KISS = 0x8e +MOVE_SKY_ATTACK = 0x8f +MOVE_TRANSFORM = 0x90 +MOVE_BUBBLE = 0x91 +MOVE_DIZZY_PUNCH = 0x92 +MOVE_SPORE = 0x93 +MOVE_FLASH = 0x94 +MOVE_PSYWAVE = 0x95 +MOVE_SPLASH = 0x96 +MOVE_ACID_ARMOR = 0x97 +MOVE_CRABHAMMER = 0x98 +MOVE_EXPLOSION = 0x99 +MOVE_FURY_SWIPES = 0x9a +MOVE_BONEMERANG = 0x9b +MOVE_REST = 0x9c +MOVE_ROCK_SLIDE = 0x9d +MOVE_HYPER_FANG = 0x9e +MOVE_SHARPEN = 0x9f +MOVE_CONVERSION = 0xa0 +MOVE_TRI_ATTACK = 0xa1 +MOVE_SUPER_FANG = 0xa2 +MOVE_SLASH = 0xa3 +MOVE_SUBSTITUTE = 0xa4 + +MONSTER_RHYDON = 0x01 +MONSTER_KANGASKHAN = 0x02 +MONSTER_NIDORAN_M = 0x03 +MONSTER_CLEFAIRY = 0x04 +MONSTER_SPEAROW = 0x05 +MONSTER_VOLTORB = 0x06 +MONSTER_NIDOKING = 0x07 +MONSTER_SLOWBRO = 0x08 +MONSTER_IVYSAUR = 0x09 +MONSTER_EXEGGUTOR = 0x0A +MONSTER_LICKITUNG = 0x0B +MONSTER_EXEGGCUTE = 0x0C +MONSTER_GRIMER = 0x0D +MONSTER_GENGAR = 0x0E +MONSTER_NIDORAN_F = 0x0F +MONSTER_NIDOQUEEN = 0x10 +MONSTER_CUBONE = 0x11 +MONSTER_RHYHORN = 0x12 +MONSTER_LAPRAS = 0x13 +MONSTER_ARCANINE = 0x14 +MONSTER_MEW = 0x15 +MONSTER_GYARADOS = 0x16 +MONSTER_SHELLDER = 0x17 +MONSTER_TENTACOOL = 0x18 +MONSTER_GASTLY = 0x19 +MONSTER_SCYTHER = 0x1A +MONSTER_STARYU = 0x1B +MONSTER_BLASTOISE = 0x1C +MONSTER_PINSIR = 0x1D +MONSTER_TANGELA = 0x1E +MONSTER_MISSINGNO_1F = 0x1F +MONSTER_MISSINGNO_20 = 0x20 +MONSTER_GROWLITHE = 0x21 +MONSTER_ONIX = 0x22 +MONSTER_FEAROW = 0x23 +MONSTER_PIDGEY = 0x24 +MONSTER_SLOWPOKE = 0x25 +MONSTER_KADABRA = 0x26 +MONSTER_GRAVELER = 0x27 +MONSTER_CHANSEY = 0x28 +MONSTER_MACHOKE = 0x29 +MONSTER_MR_MIME = 0x2A +MONSTER_HITMONLEE = 0x2B +MONSTER_HITMONCHAN = 0x2C +MONSTER_ARBOK = 0x2D +MONSTER_PARASECT = 0x2E +MONSTER_PSYDUCK = 0x2F +MONSTER_DROWZEE = 0x30 +MONSTER_GOLEM = 0x31 +MONSTER_MISSINGNO_32 = 0x32 +MONSTER_MAGMAR = 0x33 +MONSTER_MISSINGNO_34 = 0x34 +MONSTER_ELECTABUZZ = 0x35 +MONSTER_MAGNETON = 0x36 +MONSTER_KOFFING = 0x37 +MONSTER_MISSINGNO_38 = 0x38 +MONSTER_MANKEY = 0x39 +MONSTER_SEEL = 0x3A +MONSTER_DIGLETT = 0x3B +MONSTER_TAUROS = 0x3C +MONSTER_MISSINGNO_3D = 0x3D +MONSTER_MISSINGNO_3E = 0x3E +MONSTER_MISSINGNO_3F = 0x3F +MONSTER_FARFETCHD = 0x40 +MONSTER_VENONAT = 0x41 +MONSTER_DRAGONITE = 0x42 +MONSTER_MISSINGNO_43 = 0x43 +MONSTER_MISSINGNO_44 = 0x44 +MONSTER_MISSINGNO_45 = 0x45 +MONSTER_DODUO = 0x46 +MONSTER_POLIWAG = 0x47 +MONSTER_JYNX = 0x48 +MONSTER_MOLTRES = 0x49 +MONSTER_ARTICUNO = 0x4A +MONSTER_ZAPDOS = 0x4B +MONSTER_DITTO = 0x4C +MONSTER_MEOWTH = 0x4D +MONSTER_KRABBY = 0x4E +MONSTER_MISSINGNO_4F = 0x4F +MONSTER_MISSINGNO_50 = 0x50 +MONSTER_MISSINGNO_51 = 0x51 +MONSTER_VULPIX = 0x52 +MONSTER_NINETALES = 0x53 +MONSTER_PIKACHU = 0x54 +MONSTER_RAICHU = 0x55 +MONSTER_MISSINGNO_56 = 0x56 +MONSTER_MISSINGNO_57 = 0x57 +MONSTER_DRATINI = 0x58 +MONSTER_DRAGONAIR = 0x59 +MONSTER_KABUTO = 0x5A +MONSTER_KABUTOPS = 0x5B +MONSTER_HORSEA = 0x5C +MONSTER_SEADRA = 0x5D +MONSTER_MISSINGNO_5E = 0x5E +MONSTER_MISSINGNO_5F = 0x5F +MONSTER_SANDSHREW = 0x60 +MONSTER_SANDSLASH = 0x61 +MONSTER_OMANYTE = 0x62 +MONSTER_OMASTAR = 0x63 +MONSTER_JIGGLYPUFF = 0x64 +MONSTER_WIGGLYTUFF = 0x65 +MONSTER_EEVEE = 0x66 +MONSTER_FLAREON = 0x67 +MONSTER_JOLTEON = 0x68 +MONSTER_VAPOREON = 0x69 +MONSTER_MACHOP = 0x6A +MONSTER_ZUBAT = 0x6B +MONSTER_EKANS = 0x6C +MONSTER_PARAS = 0x6D +MONSTER_POLIWHIRL = 0x6E +MONSTER_POLIWRATH = 0x6F +MONSTER_WEEDLE = 0x70 +MONSTER_KAKUNA = 0x71 +MONSTER_BEEDRILL = 0x72 +MONSTER_MISSINGNO_73 = 0x73 +MONSTER_DODRIO = 0x74 +MONSTER_PRIMEAPE = 0x75 +MONSTER_DUGTRIO = 0x76 +MONSTER_VENOMOTH = 0x77 +MONSTER_DEWGONG = 0x78 +MONSTER_MISSINGNO_79 = 0x79 +MONSTER_MISSINGNO_7A = 0x7A +MONSTER_CATERPIE = 0x7B +MONSTER_METAPOD = 0x7C +MONSTER_BUTTERFREE = 0x7D +MONSTER_MACHAMP = 0x7E +MONSTER_MISSINGNO_7F = 0x7F +MONSTER_GOLDUCK = 0x80 +MONSTER_HYPNO = 0x81 +MONSTER_GOLBAT = 0x82 +MONSTER_MEWTWO = 0x83 +MONSTER_SNORLAX = 0x84 +MONSTER_MAGIKARP = 0x85 +MONSTER_MISSINGNO_86 = 0x86 +MONSTER_MISSINGNO_87 = 0x87 +MONSTER_MUK = 0x88 +MONSTER_MISSINGNO_89 = 0x89 +MONSTER_KINGLER = 0x8A +MONSTER_CLOYSTER = 0x8B +MONSTER_MISSINGNO_8C = 0x8C +MONSTER_ELECTRODE = 0x8D +MONSTER_CLEFABLE = 0x8E +MONSTER_WEEZING = 0x8F +MONSTER_PERSIAN = 0x90 +MONSTER_MAROWAK = 0x91 +MONSTER_MISSINGNO_92 = 0x92 +MONSTER_HAUNTER = 0x93 +MONSTER_ABRA = 0x94 +MONSTER_ALAKAZAM = 0x95 +MONSTER_PIDGEOTTO = 0x96 +MONSTER_PIDGEOT = 0x97 +MONSTER_STARMIE = 0x98 +MONSTER_BULBASAUR = 0x99 +MONSTER_VENUSAUR = 0x9A +MONSTER_TENTACRUEL = 0x9B +MONSTER_MISSINGNO_9C = 0x9C +MONSTER_GOLDEEN = 0x9D +MONSTER_SEAKING = 0x9E +MONSTER_MISSINGNO_9F = 0x9F +MONSTER_MISSINGNO_A0 = 0xA0 +MONSTER_MISSINGNO_A1 = 0xA1 +MONSTER_MISSINGNO_A2 = 0xA2 +MONSTER_PONYTA = 0xA3 +MONSTER_RAPIDASH = 0xA4 +MONSTER_RATTATA = 0xA5 +MONSTER_RATICATE = 0xA6 +MONSTER_NIDORINO = 0xA7 +MONSTER_NIDORINA = 0xA8 +MONSTER_GEODUDE = 0xA9 +MONSTER_PORYGON = 0xAA +MONSTER_AERODACTYL = 0xAB +MONSTER_MISSINGNO_AC = 0xAC +MONSTER_MAGNEMITE = 0xAD +MONSTER_MISSINGNO_AE = 0xAE +MONSTER_MISSINGNO_AF = 0xAF +MONSTER_CHARMANDER = 0xB0 +MONSTER_SQUIRTLE = 0xB1 +MONSTER_CHARMELEON = 0xB2 +MONSTER_WARTORTLE = 0xB3 +MONSTER_CHARIZARD = 0xB4 +MONSTER_MISSINGNO_B5 = 0xB5 +MONSTER_FOSSIL_KABUTOPS = 0xB6 +MONSTER_FOSSIL_AERODACTYL = 0xB7 +MONSTER_MON_GHOST = 0xB8 +MONSTER_ODDISH = 0xB9 +MONSTER_GLOOM = 0xBA +MONSTER_VILEPLUME = 0xBB +MONSTER_BELLSPROUT = 0xBC +MONSTER_WEEPINBELL = 0xBD +MONSTER_VICTREEBEL = 0xBE + +ITEM_MASTER_BALL = 0x01 +ITEM_ULTRA_BALL = 0x02 +ITEM_GREAT_BALL = 0x03 +ITEM_POKE_BALL = 0x04 +ITEM_TOWN_MAP = 0x05 +ITEM_BICYCLE = 0x06 +ITEM_SURFBOARD = 0x07 +ITEM_SAFARI_BALL = 0x08 +ITEM_POKEDEX = 0x09 +ITEM_MOON_STONE = 0x0A +ITEM_ANTIDOTE = 0x0B +ITEM_BURN_HEAL = 0x0C +ITEM_ICE_HEAL = 0x0D +ITEM_AWAKENING = 0x0E +ITEM_PARLYZ_HEAL = 0x0F +ITEM_FULL_RESTORE = 0x10 +ITEM_MAX_POTION = 0x11 +ITEM_HYPER_POTION = 0x12 +ITEM_SUPER_POTION = 0x13 +ITEM_POTION = 0x14 +ITEM_BOULDERBADGE = 0x15 +ITEM_CASCADEBADGE = 0x16 +ITEM_THUNDERBADGE = 0x17 +ITEM_RAINBOWBADGE = 0x18 +ITEM_SOULBADGE = 0x19 +ITEM_MARSHBADGE = 0x1A +ITEM_VOLCANOBADGE = 0x1B +ITEM_EARTHBADGE = 0x1C +ITEM_ESCAPE_ROPE = 0x1D +ITEM_REPEL = 0x1E +ITEM_OLD_AMBER = 0x1F +ITEM_FIRE_STONE = 0x20 +ITEM_THUNDER_STONE = 0x21 +ITEM_WATER_STONE = 0x22 +ITEM_HP_UP = 0x23 +ITEM_PROTEIN = 0x24 +ITEM_IRON = 0x25 +ITEM_CARBOS = 0x26 +ITEM_CALCIUM = 0x27 +ITEM_RARE_CANDY = 0x28 +ITEM_DOME_FOSSIL = 0x29 +ITEM_HELIX_FOSSIL = 0x2A +ITEM_SECRET_KEY = 0x2B +ITEM_UNUSED_ITEM = 0x2C +ITEM_BIKE_VOUCHER = 0x2D +ITEM_X_ACCURACY = 0x2E +ITEM_LEAF_STONE = 0x2F +ITEM_CARD_KEY = 0x30 +ITEM_NUGGET = 0x31 +ITEM_PP_UP_2 = 0x32 +ITEM_POKE_DOLL = 0x33 +ITEM_FULL_HEAL = 0x34 +ITEM_REVIVE = 0x35 +ITEM_MAX_REVIVE = 0x36 +ITEM_GUARD_SPEC = 0x37 +ITEM_SUPER_REPEL = 0x38 +ITEM_MAX_REPEL = 0x39 +ITEM_DIRE_HIT = 0x3A +ITEM_COIN = 0x3B +ITEM_FRESH_WATER = 0x3C +ITEM_SODA_POP = 0x3D +ITEM_LEMONADE = 0x3E +ITEM_S_S_TICKET = 0x3F +ITEM_GOLD_TEETH = 0x40 +ITEM_X_ATTACK = 0x41 +ITEM_X_DEFEND = 0x42 +ITEM_X_SPEED = 0x43 +ITEM_X_SPECIAL = 0x44 +ITEM_COIN_CASE = 0x45 +ITEM_OAKS_PARCEL = 0x46 +ITEM_ITEMFINDER = 0x47 +ITEM_SILPH_SCOPE = 0x48 +ITEM_POKE_FLUTE = 0x49 +ITEM_LIFT_KEY = 0x4A +ITEM_EXP_ALL = 0x4B +ITEM_OLD_ROD = 0x4C +ITEM_GOOD_ROD = 0x4D +ITEM_SUPER_ROD = 0x4E +ITEM_PP_UP = 0x4F +ITEM_ETHER = 0x50 +ITEM_MAX_ETHER = 0x51 +ITEM_ELIXER = 0x52 +ITEM_MAX_ELIXER = 0x53 +ITEM_FLOOR_B2F = 0x54 +ITEM_FLOOR_B1F = 0x55 +ITEM_FLOOR_1F = 0x56 +ITEM_FLOOR_2F = 0x57 +ITEM_FLOOR_3F = 0x58 +ITEM_FLOOR_4F = 0x59 +ITEM_FLOOR_5F = 0x5A +ITEM_FLOOR_6F = 0x5B +ITEM_FLOOR_7F = 0x5C +ITEM_FLOOR_8F = 0x5D +ITEM_FLOOR_9F = 0x5E +ITEM_FLOOR_10F = 0x5F +ITEM_FLOOR_11F = 0x60 +ITEM_FLOOR_B4F = 0x61 +ITEM_HM_01 = 0xC4 +ITEM_HM_02 = 0xC5 +ITEM_HM_03 = 0xC6 +ITEM_HM_04 = 0xC7 +ITEM_HM_05 = 0xC8 +ITEM_TM_01 = 0xC9 +ITEM_TM_02 = 0xCA +ITEM_TM_03 = 0xCB +ITEM_TM_04 = 0xCC +ITEM_TM_05 = 0xCD +ITEM_TM_06 = 0xCE +ITEM_TM_07 = 0xCF +ITEM_TM_08 = 0xD0 +ITEM_TM_09 = 0xD1 +ITEM_TM_10 = 0xD2 +ITEM_TM_11 = 0xD3 +ITEM_TM_12 = 0xD4 +ITEM_TM_13 = 0xD5 +ITEM_TM_14 = 0xD6 +ITEM_TM_15 = 0xD7 +ITEM_TM_16 = 0xD8 +ITEM_TM_17 = 0xD9 +ITEM_TM_18 = 0xDA +ITEM_TM_19 = 0xDB +ITEM_TM_20 = 0xDC +ITEM_TM_21 = 0xDD +ITEM_TM_22 = 0xDE +ITEM_TM_23 = 0xDF +ITEM_TM_24 = 0xE0 +ITEM_TM_25 = 0xE1 +ITEM_TM_26 = 0xE2 +ITEM_TM_27 = 0xE3 +ITEM_TM_28 = 0xE4 +ITEM_TM_29 = 0xE5 +ITEM_TM_30 = 0xE6 +ITEM_TM_31 = 0xE7 +ITEM_TM_32 = 0xE8 +ITEM_TM_33 = 0xE9 +ITEM_TM_34 = 0xEA +ITEM_TM_35 = 0xEB +ITEM_TM_36 = 0xEC +ITEM_TM_37 = 0xED +ITEM_TM_38 = 0xEE +ITEM_TM_39 = 0xEF +ITEM_TM_40 = 0xF0 +ITEM_TM_41 = 0xF1 +ITEM_TM_42 = 0xF2 +ITEM_TM_43 = 0xF3 +ITEM_TM_44 = 0xF4 +ITEM_TM_45 = 0xF5 +ITEM_TM_46 = 0xF6 +ITEM_TM_47 = 0xF7 +ITEM_TM_48 = 0xF8 +ITEM_TM_49 = 0xF9 +ITEM_TM_50 = 0xFA + +SPRITE_RED = 0x01 +SPRITE_BLUE = 0x02 +SPRITE_OAK = 0x03 +SPRITE_BUG_CATCHER = 0x04 +SPRITE_SLOWBRO = 0x05 +SPRITE_LASS = 0x06 +SPRITE_BLACK_HAIR_BOY_1 = 0x07 +SPRITE_LITTLE_GIRL = 0x08 +SPRITE_BIRD = 0x09 +SPRITE_FAT_BALD_GUY = 0x0a +SPRITE_GAMBLER = 0x0b +SPRITE_BLACK_HAIR_BOY_2 = 0x0c +SPRITE_GIRL = 0x0d +SPRITE_HIKER = 0x0e +SPRITE_FOULARD_WOMAN = 0x0f +SPRITE_GENTLEMAN = 0x10 +SPRITE_DAISY = 0x11 +SPRITE_BIKER = 0x12 +SPRITE_SAILOR = 0x13 +SPRITE_COOK = 0x14 +SPRITE_BIKE_SHOP_GUY = 0x15 +SPRITE_MR_FUJI = 0x16 +SPRITE_GIOVANNI = 0x17 +SPRITE_ROCKET = 0x18 +SPRITE_MEDIUM = 0x19 +SPRITE_WAITER = 0x1a +SPRITE_ERIKA = 0x1b +SPRITE_MOM_GEISHA = 0x1c +SPRITE_BRUNETTE_GIRL = 0x1d +SPRITE_LANCE = 0x1e +SPRITE_OAK_SCIENTIST_AIDE = 0x1f +SPRITE_OAK_AIDE = 0x20 +SPRITE_ROCKER = 0x21 +SPRITE_SWIMMER = 0x22 +SPRITE_WHITE_PLAYER = 0x23 +SPRITE_GYM_HELPER = 0x24 +SPRITE_OLD_PERSON = 0x25 +SPRITE_MART_GUY = 0x26 +SPRITE_FISHER = 0x27 +SPRITE_OLD_MEDIUM_WOMAN = 0x28 +SPRITE_NURSE = 0x29 +SPRITE_CABLE_CLUB_WOMAN = 0x2a +SPRITE_MR_MASTERBALL = 0x2b +SPRITE_LAPRAS_GIVER = 0x2c +SPRITE_WARDEN = 0x2d +SPRITE_SS_CAPTAIN = 0x2e +SPRITE_FISHER2 = 0x2f +SPRITE_BLACKBELT = 0x30 +SPRITE_GUARD = 0x31 +SPRITE_COP_GUARD = 0x32 +SPRITE_MOM = 0x33 +SPRITE_BALDING_GUY = 0x34 +SPRITE_YOUNG_BOY = 0x35 +SPRITE_GAMEBOY_KID = 0x36 +SPRITE_GAMEBOY_KID_COPY = 0x37 +SPRITE_CLEFAIRY = 0x38 +SPRITE_AGATHA = 0x39 +SPRITE_BRUNO = 0x3a +SPRITE_LORELEI = 0x3b +SPRITE_SEEL = 0x3c +SPRITE_BALL = 0x3d +SPRITE_OMANYTE = 0x3e +SPRITE_BOULDER = 0x3f +SPRITE_PAPER_SHEET = 0x40 +SPRITE_BOOK_MAP_DEX = 0x41 +SPRITE_CLIPBOARD = 0x42 +SPRITE_SNORLAX = 0x43 +SPRITE_OLD_AMBER_COPY = 0x44 +SPRITE_OLD_AMBER = 0x45 +SPRITE_LYING_OLD_MAN_UNUSED_1 = 0x46 +SPRITE_LYING_OLD_MAN_UNUSED_2 = 0x47 +SPRITE_LYING_OLD_MAN = 0x48 + +MAP_PALLET_TOWN = 0x00 +MAP_VIRIDIAN_CITY = 0x01 +MAP_PEWTER_CITY = 0x02 +MAP_CERULEAN_CITY = 0x03 +MAP_LAVENDER_TOWN = 0x04 +MAP_VERMILION_CITY = 0x05 +MAP_CELADON_CITY = 0x06 +MAP_FUCHSIA_CITY = 0x07 +MAP_CINNABAR_ISLAND = 0x08 +MAP_INDIGO_PLATEAU = 0x09 +MAP_SAFFRON_CITY = 0x0A +MAP_UNUSED_MAP_0B = 0x0B +MAP_ROUTE_1 = 0x0C +MAP_ROUTE_2 = 0x0D +MAP_ROUTE_3 = 0x0E +MAP_ROUTE_4 = 0x0F +MAP_ROUTE_5 = 0x10 +MAP_ROUTE_6 = 0x11 +MAP_ROUTE_7 = 0x12 +MAP_ROUTE_8 = 0x13 +MAP_ROUTE_9 = 0x14 +MAP_ROUTE_10 = 0x15 +MAP_ROUTE_11 = 0x16 +MAP_ROUTE_12 = 0x17 +MAP_ROUTE_13 = 0x18 +MAP_ROUTE_14 = 0x19 +MAP_ROUTE_15 = 0x1A +MAP_ROUTE_16 = 0x1B +MAP_ROUTE_17 = 0x1C +MAP_ROUTE_18 = 0x1D +MAP_ROUTE_19 = 0x1E +MAP_ROUTE_20 = 0x1F +MAP_ROUTE_21 = 0x20 +MAP_ROUTE_22 = 0x21 +MAP_ROUTE_23 = 0x22 +MAP_ROUTE_24 = 0x23 +MAP_ROUTE_25 = 0x24 +MAP_REDS_HOUSE_1F = 0x25 +MAP_REDS_HOUSE_2F = 0x26 +MAP_BLUES_HOUSE = 0x27 +MAP_OAKS_LAB = 0x28 +MAP_VIRIDIAN_POKECENTER = 0x29 +MAP_VIRIDIAN_MART = 0x2A +MAP_VIRIDIAN_SCHOOL_HOUSE = 0x2B +MAP_VIRIDIAN_NICKNAME_HOUSE = 0x2C +MAP_VIRIDIAN_GYM = 0x2D +MAP_DIGLETTS_CAVE_ROUTE_2 = 0x2E +MAP_VIRIDIAN_FOREST_NORTH_GATE = 0x2F +MAP_ROUTE_2_TRADE_HOUSE = 0x30 +MAP_ROUTE_2_GATE = 0x31 +MAP_VIRIDIAN_FOREST_SOUTH_GATE = 0x32 +MAP_VIRIDIAN_FOREST = 0x33 +MAP_MUSEUM_1F = 0x34 +MAP_MUSEUM_2F = 0x35 +MAP_PEWTER_GYM = 0x36 +MAP_PEWTER_NIDORAN_HOUSE = 0x37 +MAP_PEWTER_MART = 0x38 +MAP_PEWTER_SPEECH_HOUSE = 0x39 +MAP_PEWTER_POKECENTER = 0x3A +MAP_MT_MOON_1F = 0x3B +MAP_MT_MOON_B1F = 0x3C +MAP_MT_MOON_B2F = 0x3D +MAP_CERULEAN_TRASHED_HOUSE = 0x3E +MAP_CERULEAN_TRADE_HOUSE = 0x3F +MAP_CERULEAN_POKECENTER = 0x40 +MAP_CERULEAN_GYM = 0x41 +MAP_BIKE_SHOP = 0x42 +MAP_CERULEAN_MART = 0x43 +MAP_MT_MOON_POKECENTER = 0x44 +MAP_CERULEAN_TRASHED_HOUSE_COPY = 0x45 +MAP_ROUTE_5_GATE = 0x46 +MAP_UNDERGROUND_PATH_ROUTE_5 = 0x47 +MAP_DAYCARE = 0x48 +MAP_ROUTE_6_GATE = 0x49 +MAP_UNDERGROUND_PATH_ROUTE_6 = 0x4A +MAP_UNDERGROUND_PATH_ROUTE_6_COPY = 0x4B +MAP_ROUTE_7_GATE = 0x4C +MAP_UNDERGROUND_PATH_ROUTE_7 = 0x4D +MAP_UNDERGROUND_PATH_ROUTE_7_COPY = 0x4E +MAP_ROUTE_8_GATE = 0x4F +MAP_UNDERGROUND_PATH_ROUTE_8 = 0x50 +MAP_ROCK_TUNNEL_POKECENTER = 0x51 +MAP_ROCK_TUNNEL_1F = 0x52 +MAP_POWER_PLANT = 0x53 +MAP_ROUTE_11_GATE_1F = 0x54 +MAP_DIGLETTS_CAVE_ROUTE_11 = 0x55 +MAP_ROUTE_11_GATE_2F = 0x56 +MAP_ROUTE_12_GATE_1F = 0x57 +MAP_BILLS_HOUSE = 0x58 +MAP_VERMILION_POKECENTER = 0x59 +MAP_POKEMON_FAN_CLUB = 0x5A +MAP_VERMILION_MART = 0x5B +MAP_VERMILION_GYM = 0x5C +MAP_VERMILION_PIDGEY_HOUSE = 0x5D +MAP_VERMILION_DOCK = 0x5E +MAP_SS_ANNE_1F = 0x5F +MAP_SS_ANNE_2F = 0x60 +MAP_SS_ANNE_3F = 0x61 +MAP_SS_ANNE_B1F = 0x62 +MAP_SS_ANNE_BOW = 0x63 +MAP_SS_ANNE_KITCHEN = 0x64 +MAP_SS_ANNE_CAPTAINS_ROOM = 0x65 +MAP_SS_ANNE_1F_ROOMS = 0x66 +MAP_SS_ANNE_2F_ROOMS = 0x67 +MAP_SS_ANNE_B1F_ROOMS = 0x68 +MAP_UNUSED_MAP_69 = 0x69 +MAP_UNUSED_MAP_6A = 0x6A +MAP_UNUSED_MAP_6B = 0x6B +MAP_VICTORY_ROAD_1F = 0x6C +MAP_UNUSED_MAP_6D = 0x6D +MAP_UNUSED_MAP_6E = 0x6E +MAP_UNUSED_MAP_6F = 0x6F +MAP_UNUSED_MAP_70 = 0x70 +MAP_LANCES_ROOM = 0x71 +MAP_UNUSED_MAP_72 = 0x72 +MAP_UNUSED_MAP_73 = 0x73 +MAP_UNUSED_MAP_74 = 0x74 +MAP_UNUSED_MAP_75 = 0x75 +MAP_HALL_OF_FAME = 0x76 +MAP_UNDERGROUND_PATH_NORTH_SOUTH = 0x77 +MAP_CHAMPIONS_ROOM = 0x78 +MAP_UNDERGROUND_PATH_WEST_EAST = 0x79 +MAP_CELADON_MART_1F = 0x7A +MAP_CELADON_MART_2F = 0x7B +MAP_CELADON_MART_3F = 0x7C +MAP_CELADON_MART_4F = 0x7D +MAP_CELADON_MART_ROOF = 0x7E +MAP_CELADON_MART_ELEVATOR = 0x7F +MAP_CELADON_MANSION_1F = 0x80 +MAP_CELADON_MANSION_2F = 0x81 +MAP_CELADON_MANSION_3F = 0x82 +MAP_CELADON_MANSION_ROOF = 0x83 +MAP_CELADON_MANSION_ROOF_HOUSE = 0x84 +MAP_CELADON_POKECENTER = 0x85 +MAP_CELADON_GYM = 0x86 +MAP_GAME_CORNER = 0x87 +MAP_CELADON_MART_5F = 0x88 +MAP_GAME_CORNER_PRIZE_ROOM = 0x89 +MAP_CELADON_DINER = 0x8A +MAP_CELADON_CHIEF_HOUSE = 0x8B +MAP_CELADON_HOTEL = 0x8C +MAP_LAVENDER_POKECENTER = 0x8D +MAP_POKEMON_TOWER_1F = 0x8E +MAP_POKEMON_TOWER_2F = 0x8F +MAP_POKEMON_TOWER_3F = 0x90 +MAP_POKEMON_TOWER_4F = 0x91 +MAP_POKEMON_TOWER_5F = 0x92 +MAP_POKEMON_TOWER_6F = 0x93 +MAP_POKEMON_TOWER_7F = 0x94 +MAP_MR_FUJIS_HOUSE = 0x95 +MAP_LAVENDER_MART = 0x96 +MAP_LAVENDER_CUBONE_HOUSE = 0x97 +MAP_FUCHSIA_MART = 0x98 +MAP_FUCHSIA_BILLS_GRANDPAS_HOUSE = 0x99 +MAP_FUCHSIA_POKECENTER = 0x9A +MAP_WARDENS_HOUSE = 0x9B +MAP_SAFARI_ZONE_GATE = 0x9C +MAP_FUCHSIA_GYM = 0x9D +MAP_FUCHSIA_MEETING_ROOM = 0x9E +MAP_SEAFOAM_ISLANDS_B1F = 0x9F +MAP_SEAFOAM_ISLANDS_B2F = 0xA0 +MAP_SEAFOAM_ISLANDS_B3F = 0xA1 +MAP_SEAFOAM_ISLANDS_B4F = 0xA2 +MAP_VERMILION_OLD_ROD_HOUSE = 0xA3 +MAP_FUCHSIA_GOOD_ROD_HOUSE = 0xA4 +MAP_POKEMON_MANSION_1F = 0xA5 +MAP_CINNABAR_GYM = 0xA6 +MAP_CINNABAR_LAB = 0xA7 +MAP_CINNABAR_LAB_TRADE_ROOM = 0xA8 +MAP_CINNABAR_LAB_METRONOME_ROOM = 0xA9 +MAP_CINNABAR_LAB_FOSSIL_ROOM = 0xAA +MAP_CINNABAR_POKECENTER = 0xAB +MAP_CINNABAR_MART = 0xAC +MAP_CINNABAR_MART_COPY = 0xAD +MAP_INDIGO_PLATEAU_LOBBY = 0xAE +MAP_COPYCATS_HOUSE_1F = 0xAF +MAP_COPYCATS_HOUSE_2F = 0xB0 +MAP_FIGHTING_DOJO = 0xB1 +MAP_SAFFRON_GYM = 0xB2 +MAP_SAFFRON_PIDGEY_HOUSE = 0xB3 +MAP_SAFFRON_MART = 0xB4 +MAP_SILPH_CO_1F = 0xB5 +MAP_SAFFRON_POKECENTER = 0xB6 +MAP_MR_PSYCHICS_HOUSE = 0xB7 +MAP_ROUTE_15_GATE_1F = 0xB8 +MAP_ROUTE_15_GATE_2F = 0xB9 +MAP_ROUTE_16_GATE_1F = 0xBA +MAP_ROUTE_16_GATE_2F = 0xBB +MAP_ROUTE_16_FLY_HOUSE = 0xBC +MAP_ROUTE_12_SUPER_ROD_HOUSE = 0xBD +MAP_ROUTE_18_GATE_1F = 0xBE +MAP_ROUTE_18_GATE_2F = 0xBF +MAP_SEAFOAM_ISLANDS_1F = 0xC0 +MAP_ROUTE_22_GATE = 0xC1 +MAP_VICTORY_ROAD_2F = 0xC2 +MAP_ROUTE_12_GATE_2F = 0xC3 +MAP_VERMILION_TRADE_HOUSE = 0xC4 +MAP_DIGLETTS_CAVE = 0xC5 +MAP_VICTORY_ROAD_3F = 0xC6 +MAP_ROCKET_HIDEOUT_B1F = 0xC7 +MAP_ROCKET_HIDEOUT_B2F = 0xC8 +MAP_ROCKET_HIDEOUT_B3F = 0xC9 +MAP_ROCKET_HIDEOUT_B4F = 0xCA +MAP_ROCKET_HIDEOUT_ELEVATOR = 0xCB +MAP_UNUSED_MAP_CC = 0xCC +MAP_UNUSED_MAP_CD = 0xCD +MAP_UNUSED_MAP_CE = 0xCE +MAP_SILPH_CO_2F = 0xCF +MAP_SILPH_CO_3F = 0xD0 +MAP_SILPH_CO_4F = 0xD1 +MAP_SILPH_CO_5F = 0xD2 +MAP_SILPH_CO_6F = 0xD3 +MAP_SILPH_CO_7F = 0xD4 +MAP_SILPH_CO_8F = 0xD5 +MAP_POKEMON_MANSION_2F = 0xD6 +MAP_POKEMON_MANSION_3F = 0xD7 +MAP_POKEMON_MANSION_B1F = 0xD8 +MAP_SAFARI_ZONE_EAST = 0xD9 +MAP_SAFARI_ZONE_NORTH = 0xDA +MAP_SAFARI_ZONE_WEST = 0xDB +MAP_SAFARI_ZONE_CENTER = 0xDC +MAP_SAFARI_ZONE_CENTER_REST_HOUSE = 0xDD +MAP_SAFARI_ZONE_SECRET_HOUSE = 0xDE +MAP_SAFARI_ZONE_WEST_REST_HOUSE = 0xDF +MAP_SAFARI_ZONE_EAST_REST_HOUSE = 0xE0 +MAP_SAFARI_ZONE_NORTH_REST_HOUSE = 0xE1 +MAP_CERULEAN_CAVE_2F = 0xE2 +MAP_CERULEAN_CAVE_B1F = 0xE3 +MAP_CERULEAN_CAVE_1F = 0xE4 +MAP_NAME_RATERS_HOUSE = 0xE5 +MAP_CERULEAN_BADGE_HOUSE = 0xE6 +MAP_UNUSED_MAP_E7 = 0xE7 +MAP_ROCK_TUNNEL_B1F = 0xE8 +MAP_SILPH_CO_9F = 0xE9 +MAP_SILPH_CO_10F = 0xEA +MAP_SILPH_CO_11F = 0xEB +MAP_SILPH_CO_ELEVATOR = 0xEC +MAP_UNUSED_MAP_ED = 0xED +MAP_UNUSED_MAP_EE = 0xEE +MAP_TRADE_CENTER = 0xEF +MAP_COLOSSEUM = 0xF0 +MAP_UNUSED_MAP_F1 = 0xF1 +MAP_UNUSED_MAP_F2 = 0xF2 +MAP_UNUSED_MAP_F3 = 0xF3 +MAP_UNUSED_MAP_F4 = 0xF4 +MAP_LORELEIS_ROOM = 0xF5 +MAP_BRUNOS_ROOM = 0xF6 +MAP_AGATHAS_ROOM = 0xF7 + +MAP_NAMES = [ +'PALLET TOWN', +'VIRIDIAN CITY', +'PEWTER CITY', +'CERULEAN CITY', +'LAVENDER TOWN', +'VERMILION CITY', +'CELADON CITY', +'FUCHSIA CITY', +'CINNABAR ISLAND', +'INDIGO PLATEAU', +'SAFFRON CITY', +'UNUSED MAP 0B', +'ROUTE 1', +'ROUTE 2', +'ROUTE 3', +'ROUTE 4', +'ROUTE 5', +'ROUTE 6', +'ROUTE 7', +'ROUTE 8', +'ROUTE 9', +'ROUTE 10', +'ROUTE 11', +'ROUTE 12', +'ROUTE 13', +'ROUTE 14', +'ROUTE 15', +'ROUTE 16', +'ROUTE 17', +'ROUTE 18', +'ROUTE 19', +'ROUTE 20', +'ROUTE 21', +'ROUTE 22', +'ROUTE 23', +'ROUTE 24', +'ROUTE 25', +'RED\'S HOUSE 1F', +'RED\'S HOUSE 2F', +'BLUE\'S HOUSE', +'OAK\'S LAB', +'VIRIDIAN POKECENTER', +'VIRIDIAN MART', +'VIRIDIAN SCHOOL HOUSE', +'VIRIDIAN NICKNAME HOUSE', +'VIRIDIAN GYM', +'DIGLETTS CAVE ROUTE 2', +'VIRIDIAN FOREST NORTH GATE', +'ROUTE 2 TRADE HOUSE', +'ROUTE 2 GATE', +'VIRIDIAN FOREST SOUTH GATE', +'VIRIDIAN FOREST', +'MUSEUM 1F', +'MUSEUM 2F', +'PEWTER GYM', +'PEWTER NIDORAN HOUSE', +'PEWTER MART', +'PEWTER SPEECH HOUSE', +'PEWTER POKECENTER', +'MT MOON 1F', +'MT MOON B1F', +'MT MOON B2F', +'CERULEAN TRASHED HOUSE', +'CERULEAN TRADE HOUSE', +'CERULEAN POKECENTER', +'CERULEAN GYM', +'BIKE SHOP', +'CERULEAN MART', +'MT MOON POKECENTER', +'CERULEAN TRASHED HOUSE COPY', +'ROUTE 5 GATE', +'UNDERGROUND PATH ROUTE 5', +'DAYCARE', +'ROUTE 6 GATE', +'UNDERGROUND PATH ROUTE 6', +'UNDERGROUND PATH ROUTE 6 COPY', +'ROUTE 7 GATE', +'UNDERGROUND PATH ROUTE 7', +'UNDERGROUND PATH ROUTE 7 COPY', +'ROUTE 8 GATE', +'UNDERGROUND PATH ROUTE 8', +'ROCK TUNNEL POKECENTER', +'ROCK TUNNEL 1F', +'POWER PLANT', +'ROUTE 11 GATE 1F', +'DIGLETT\'S CAVE ROUTE 11', +'ROUTE 11 GATE 2F', +'ROUTE 12 GATE 1F', +'BILLS HOUSE', +'VERMILION POKECENTER', +'POKEMON FAN CLUB', +'VERMILION MART', +'VERMILION GYM', +'VERMILION PIDGEY HOUSE', +'VERMILION DOCK', +'SS ANNE 1F', +'SS ANNE 2F', +'SS ANNE 3F', +'SS ANNE B1F', +'SS ANNE BOW', +'SS ANNE KITCHEN', +'SS ANNE CAPTAIN\'S ROOM', +'SS ANNE 1F ROOMS', +'SS ANNE 2F ROOMS', +'SS ANNE B1F ROOMS', +'UNUSED MAP 69', +'UNUSED MAP 6A', +'UNUSED MAP 6B', +'VICTORY ROAD 1F', +'UNUSED MAP 6D', +'UNUSED MAP 6E', +'UNUSED MAP 6F', +'UNUSED MAP 70', +'LANCES ROOM', +'UNUSED MAP 72', +'UNUSED MAP 73', +'UNUSED MAP 74', +'UNUSED MAP 75', +'HALL OF FAME', +'UNDERGROUND PATH NORTH SOUTH', +'CHAMPION\'S ROOM', +'UNDERGROUND PATH WEST EAST', +'CELADON MART 1F', +'CELADON MART 2F', +'CELADON MART 3F', +'CELADON MART 4F', +'CELADON MART ROOF', +'CELADON MART ELEVATOR', +'CELADON MANSION 1F', +'CELADON MANSION 2F', +'CELADON MANSION 3F', +'CELADON MANSION ROOF', +'CELADON MANSION ROOF HOUSE', +'CELADON POKECENTER', +'CELADON GYM', +'GAME CORNER', +'CELADON MART 5F', +'GAME CORNER PRIZE ROOM', +'CELADON DINER', +'CELADON CHIEF HOUSE', +'CELADON HOTEL', +'LAVENDER POKECENTER', +'POKEMON TOWER 1F', +'POKEMON TOWER 2F', +'POKEMON TOWER 3F', +'POKEMON TOWER 4F', +'POKEMON TOWER 5F', +'POKEMON TOWER 6F', +'POKEMON TOWER 7F', +'MR FUJIS HOUSE', +'LAVENDER MART', +'LAVENDER CUBONE HOUSE', +'FUCHSIA MART', +'FUCHSIA BILL\'S GRANDPAS HOUSE', +'FUCHSIA POKECENTER', +'WARDENS HOUSE', +'SAFARI ZONE GATE', +'FUCHSIA GYM', +'FUCHSIA MEETING ROOM', +'SEAFOAM ISLANDS B1F', +'SEAFOAM ISLANDS B2F', +'SEAFOAM ISLANDS B3F', +'SEAFOAM ISLANDS B4F', +'VERMILION OLD ROD HOUSE', +'FUCHSIA GOOD ROD HOUSE', +'POKEMON MANSION 1F', +'CINNABAR GYM', +'CINNABAR LAB', +'CINNABAR LAB TRADE ROOM', +'CINNABAR LAB METRONOME ROOM', +'CINNABAR LAB FOSSIL ROOM', +'CINNABAR POKECENTER', +'CINNABAR MART', +'CINNABAR MART COPY', +'INDIGO PLATEAU LOBBY', +'COPYCATS HOUSE 1F', +'COPYCATS HOUSE 2F', +'FIGHTING DOJO', +'SAFFRON GYM', +'SAFFRON PIDGEY HOUSE', +'SAFFRON MART', +'SILPH CO 1F', +'SAFFRON POKECENTER', +'MR PSYCHICS HOUSE', +'ROUTE 15 GATE 1F', +'ROUTE 15 GATE 2F', +'ROUTE 16 GATE 1F', +'ROUTE 16 GATE 2F', +'ROUTE 16 FLY HOUSE', +'ROUTE 12 SUPER ROD HOUSE', +'ROUTE 18 GATE 1F', +'ROUTE 18 GATE 2F', +'SEAFOAM ISLANDS 1F', +'ROUTE 22 GATE', +'VICTORY ROAD 2F', +'ROUTE 12 GATE 2F', +'VERMILION TRADE HOUSE', +'DIGLETTS CAVE', +'VICTORY ROAD 3F', +'ROCKET HIDEOUT B1F', +'ROCKET HIDEOUT B2F', +'ROCKET HIDEOUT B3F', +'ROCKET HIDEOUT B4F', +'ROCKET HIDEOUT ELEVATOR', +'UNUSED MAP CC', +'UNUSED MAP CD', +'UNUSED MAP CE', +'SILPH CO 2F', +'SILPH CO 3F', +'SILPH CO 4F', +'SILPH CO 5F', +'SILPH CO 6F', +'SILPH CO 7F', +'SILPH CO 8F', +'POKEMON MANSION 2F', +'POKEMON MANSION 3F', +'POKEMON MANSION B1F', +'SAFARI ZONE EAST', +'SAFARI ZONE NORTH', +'SAFARI ZONE WEST', +'SAFARI ZONE CENTER', +'SAFARI ZONE CENTER REST HOUSE', +'SAFARI ZONE SECRET HOUSE', +'SAFARI ZONE WEST REST HOUSE', +'SAFARI ZONE EAST REST HOUSE', +'SAFARI ZONE NORTH REST HOUSE', +'CERULEAN CAVE 2F', +'CERULEAN CAVE B1F', +'CERULEAN CAVE 1F', +'NAME RATER\'S HOUSE', +'CERULEAN BADGE HOUSE', +'UNUSED MAP E7', +'ROCK TUNNEL B1F', +'SILPH CO 9F', +'SILPH CO 10F', +'SILPH CO 11F', +'SILPH CO ELEVATOR', +'UNUSED MAP ED', +'UNUSED MAP EE', +'TRADE CENTER', +'COLOSSEUM', +'UNUSED MAP F1', +'UNUSED MAP F2', +'UNUSED MAP F3', +'UNUSED MAP F4', +'LORELEI\'S ROOM', +'BRUNO\'S ROOM', +'AGATHA\'S ROOM', +] + +STATUS_ASLEEP = 4 +STATUS_POISONED = 8 +STATUS_BURNED = 16 +STATUS_FROZEN = 32 +STATUS_PARALYZED = 64 + +MONSTER_NAMES = ( + "Bulbasaur", + "Ivysaur", + "Venusaur", + "Charmander", + "Charmeleon", + "Charizard", + "Squirtle", + "Wartortle", + "Blastoise", + "Caterpie", + "Metapod", + "Butterfree", + "Weedle", + "Kakuna", + "Beedrill", + "Pidgey", + "Pidgeotto", + "Pidgeot", + "Rattata", + "Raticate", + "Spearow", + "Fearow", + "Ekans", + "Arbok", + "Pikachu", + "Raichu", + "Sandshrew", + "Sandslash", + "Nidoran♀", + "Nidorina", + "Nidoqueen", + "Nidoran♂", + "Nidorino", + "Nidoking", + "Clefairy", + "Clefable", + "Vulpix", + "Ninetales", + "Jigglypuff", + "Wigglytuff", + "Zubat", + "Golbat", + "Oddish", + "Gloom", + "Vileplume", + "Paras", + "Parasect", + "Venonat", + "Venomoth", + "Diglett", + "Dugtrio", + "Meowth", + "Persian", + "Psyduck", + "Golduck", + "Mankey", + "Primeape", + "Growlithe", + "Arcanine", + "Poliwag", + "Poliwhirl", + "Poliwrath", + "Abra", + "Kadabra", + "Alakazam", + "Machop", + "Machoke", + "Machamp", + "Bellsprout", + "Weepinbell", + "Victreebel", + "Tentacool", + "Tentacruel", + "Geodude", + "Graveler", + "Golem", + "Ponyta", + "Rapidash", + "Slowpoke", + "Slowbro", + "Magnemite", + "Magneton", + "Farfetch’d", + "Doduo", + "Dodrio", + "Seel", + "Dewgong", + "Grimer", + "Muk", + "Shellder", + "Cloyster", + "Gastly", + "Haunter", + "Gengar", + "Onix", + "Drowzee", + "Hypno", + "Krabby", + "Kingler", + "Voltorb", + "Electrode", + "Exeggcute", + "Exeggutor", + "Cubone", + "Marowak", + "Hitmonlee", + "Hitmonchan", + "Lickitung", + "Koffing", + "Weezing", + "Rhyhorn", + "Rhydon", + "Chansey", + "Tangela", + "Kangaskhan", + "Horsea", + "Seadra", + "Goldeen", + "Seaking", + "Staryu", + "Starmie", + "Mr. Mime", + "Scyther", + "Jynx", + "Electabuzz", + "Magmar", + "Pinsir", + "Tauros", + "Magikarp", + "Gyarados", + "Lapras", + "Ditto", + "Eevee", + "Vaporeon", + "Jolteon", + "Flareon", + "Porygon", + "Omanyte", + "Omastar", + "Kabuto", + "Kabutops", + "Aerodactyl", + "Snorlax", + "Articuno", + "Zapdos", + "Moltres", + "Dratini", + "Dragonair", + "Dragonite", + "Mewtwo", + "Mew", +) + +POKEDEX_TO_INDEX_MAP = ( + (112, 0x01), + (115, 0x02), + (32, 0x03), + (35, 0x04), + (21, 0x05), + (100, 0x06), + (34, 0x07), + (80, 0x08), + (2, 0x09), + (103, 0x0A), + (108, 0x0B), + (102, 0x0C), + (88, 0x0D), + (94, 0x0E), + (29, 0x0F), + (31, 0x10), + (104, 0x11), + (111, 0x12), + (131, 0x13), + (59, 0x14), + (151, 0x15), + (130, 0x16), + (90, 0x17), + (72, 0x18), + (92, 0x19), + (123, 0x1A), + (120, 0x1B), + (9, 0x1C), + (127, 0x1D), + (114, 0x1E), + (58, 0x21), + (95, 0x22), + (22, 0x23), + (16, 0x24), + (79, 0x25), + (64, 0x26), + (75, 0x27), + (113, 0x28), + (67, 0x29), + (122, 0x2A), + (106, 0x2B), + (107, 0x2C), + (24, 0x2D), + (47, 0x2E), + (54, 0x2F), + (96, 0x30), + (76, 0x31), + (126, 0x33), + (125, 0x35), + (82, 0x36), + (109, 0x37), + (56, 0x39), + (86, 0x3A), + (50, 0x3B), + (128, 0x3C), + (83, 0x40), + (48, 0x41), + (149, 0x42), + (84, 0x46), + (60, 0x47), + (124, 0x48), + (146, 0x49), + (144, 0x4A), + (145, 0x4B), + (132, 0x4C), + (52, 0x4D), + (98, 0x4E), + (37, 0x52), + (38, 0x53), + (25, 0x54), + (26, 0x55), + (147, 0x58), + (148, 0x59), + (140, 0x5A), + (141, 0x5B), + (116, 0x5C), + (117, 0x5D), + (27, 0x60), + (28, 0x61), + (138, 0x62), + (139, 0x63), + (39, 0x64), + (40, 0x65), + (133, 0x66), + (136, 0x67), + (135, 0x68), + (134, 0x69), + (66, 0x6A), + (41, 0x6B), + (23, 0x6C), + (46, 0x6D), + (61, 0x6E), + (62, 0x6F), + (13, 0x70), + (14, 0x71), + (15, 0x72), + (85, 0x74), + (57, 0x75), + (51, 0x76), + (49, 0x77), + (87, 0x78), + (10, 0x7B), + (11, 0x7C), + (12, 0x7D), + (68, 0x7E), + (55, 0x80), + (97, 0x81), + (42, 0x82), + (150, 0x83), + (143, 0x84), + (129, 0x85), + (89, 0x88), + (99, 0x8A), + (91, 0x8B), + (101, 0x8D), + (36, 0x8E), + (110, 0x8F), + (53, 0x90), + (105, 0x91), + (93, 0x93), + (63, 0x94), + (65, 0x95), + (17, 0x96), + (18, 0x97), + (121, 0x98), + (1, 0x99), + (2, 0x9A), + (73, 0x9B), + (118, 0x9D), + (119, 0x9E), + (77, 0xA3), + (78, 0xA4), + (19, 0xA5), + (20, 0xA6), + (33, 0xA7), + (30, 0xA8), + (74, 0xA9), + (137, 0xAA), + (142, 0xAB), + (81, 0xAD), + (4, 0xB0), + (7, 0xB1), + (5, 0xB2), + (8, 0xB3), + (6, 0xB4), + (43, 0xB9), + (44, 0xBA), + (45, 0xBB), + (69, 0xBC), + (70, 0xBD), + (71, 0xBE), +) diff --git a/pyboy/plugins/game_wrapper_pokemon_red/utils.py b/pyboy/plugins/game_wrapper_pokemon_red/utils.py new file mode 100644 index 000000000..77d52d432 --- /dev/null +++ b/pyboy/plugins/game_wrapper_pokemon_red/utils.py @@ -0,0 +1,17 @@ +from .constants import ASCII_DELTA + +def get_character_index(character): + if character == ' ': + return 0x7F + if character == '?': + return 0xE6 + if character == '!': + return 0xE7 + if character == 'é': + return 0xBA + + index = ord(character) + if index > 47 and index < 58: + # number + return index + 197 + return index + ASCII_DELTA diff --git a/pyboy/plugins/game_wrapper_pokemon_red/wrapper.py b/pyboy/plugins/game_wrapper_pokemon_red/wrapper.py new file mode 100644 index 000000000..b363c5455 --- /dev/null +++ b/pyboy/plugins/game_wrapper_pokemon_red/wrapper.py @@ -0,0 +1,385 @@ +# +# License: See LICENSE.md file +# GitHub: https://github.com/Baekalfen/PyBoy +# + +__pdoc__ = { + "GameWrapperPokemonRed.cartridge_title": False, + "GameWrapperPokemonRed.post_tick": False, +} + +from pyboy.utils import WindowEvent +from pyboy.logger import logger +from ..base_plugin import PyBoyGameWrapper +from . import constants +from .utils import get_character_index + +PKMN_SIZE = 0x2C +BYTE_ORDER = 'big' + + +try: + from cython import compiled + cythonmode = compiled +except ImportError: + cythonmode = False + + +class GameWrapperPokemonRed(PyBoyGameWrapper): + """ + This class wraps Pokemon Red, and provides easy access to score and a "fitness" score for AIs. + + If you call `print` on an instance of this object, it will show an overview of everything this object provides. + """ + cartridge_title = "POKEMON RED" + + def __init__(self, *args, **kwargs): + # TODO: Determine proper size + self.shape = (20, 16) + """The shape of the game area""" + super().__init__(*args, game_area_section=(0, 0) + self.shape, game_area_wrap_around=True, **kwargs) + + def start_game(self, timer_div=None): + """ + Call this function right after initializing PyBoy. This will navigate through menus to start the game at the + first playable state. + + The state of the emulator is saved, and using `reset_game`, you can get back to this point of the game + instantly. + + Kwargs: + timer_div (int): Replace timer's DIV register with this value. Use `None` to randomize. + """ + while self.get_player_name() != 'NINTEN': # Player is NINTEN on start screen + self.pyboy.tick() + self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) + self.pyboy.tick() + PyBoyGameWrapper.start_game(self, timer_div=timer_div) + + def reset_game(self, timer_div=None): + """ + After calling `start_game`, you can call this method at any time to reset the game. + + Kwargs: + timer_div (int): Replace timer's DIV register with this value. Use `None` to randomize. + """ + PyBoyGameWrapper.reset_game(self, timer_div=timer_div) + + def get_int_at_address(self, address, size=2): + """Returns an integer from a given address of a specified size.""" + bytes = [] + for i in range(size): + bytes.append(self.pyboy.get_memory_value(address + i)) + return int.from_bytes(bytes, byteorder=BYTE_ORDER) + + def set_int_at_address(self, address, value, size=2): + """Sets an integer at a given address of a specified size.""" + bytes = value.to_bytes(2, byteorder=BYTE_ORDER) + i = 0 + for byte in bytes: + self.pyboy.set_memory_value(address + i, byte) + i += 1 + + def set_text(self, text, address): + """Sets text at address. + + Will always add a string terminator (80) at the end. + """ + i = 0 + for character in text: + try: + self.pyboy.set_memory_value(address + i, get_character_index(character)) + i += 1 + except: + pass + self.pyboy.set_memory_value(address + i, constants.STRING_TERMINATOR) + + def set_rom_text(self, text, bank, address): + i = 0 + for character in text: + try: + self.pyboy.override_memory_value(bank, address + i, get_character_index(character)) + i += 1 + except: + pass + + def set_player_name(self, name): + """ + Sets the player name. + + Args: + name (string): Name to be set (will be trimmed at 8 characters). + """ + self.set_text(name[:8], 0xD158) + + def get_player_name(self): + """ + Returns player name. + """ + return self.get_text(0xD158) + + def set_rival_name(self, name): + """ + Sets the rival name. + + Args: + name (string): Name to be set (will be trimmed at 8 characters). + """ + self.set_text(name[:8], 0xD34A) + + def get_rival_name(self): + """ + Returns rival name. + """ + return self.get_text(0xD34A) + + def get_text(self, address, cap = 16): + """ + Retrieves a string from a given address. + + Args: + address (int): Address from where to retrieve text from. + cap (int): Maximum expected length of string (default: 16). + """ + i = 0 + text = '' + while i < cap: + value = self.pyboy.get_memory_value(address + i) + try: + text += chr(value - constants.ASCII_DELTA) + except: + pass + if value == constants.STRING_TERMINATOR: + break + i += 1 + return text + + def set_palette_to_darkness(self): + self.pyboy.set_memory_value(0xD35D, 6) + + def set_player_monster(self, index, monster): + """ + Sets player monster at a given index. + + Args: + index (int): Which monster to change (0-5). + monster (int): Which type of monster to set. + """ + self.pyboy.set_memory_value(0xD16B + index, monster) + self.pyboy.set_memory_value(0xD164 + (index * PKMN_SIZE), monster) + + def get_player_monster(self, index): + """ + Returns player monster at a given index. + + Args: + index (int): Which monster to change (0-5). + """ + return self.pyboy.get_memory_value(0xD16B + index) + + def set_player_monster_nickname(self, index, text): + """ + Sets player monster's nickname at a given index. + + Args: + index (int): Which monster to change (0-5). + text (string): Nickname to set (will be trimmed at 10 characters). + """ + self.set_text(text[:10], 0xD2B5 + (index * PKMN_SIZE)) + + def get_player_monster_nickname(self, index): + """ + Returns player monster's nickname at a given index. + + Args: + index (int): Which monster (0-5). + """ + return self.get_text(0xD2B5 + (index * PKMN_SIZE)) + + def set_player_monster_move(self, index, move_index, move): + """ + Sets player monster's move at a given index. + + Args: + index (int): Which monster to change (0-5). + move_index (int): Which move to change (0-3). + move (int): Which type of move to set. + """ + self.pyboy.set_memory_value(0xD01C + (index * PKMN_SIZE) + move_index, move) + + def get_player_monster_move(self, index, move_index): + """ + Returns player monster's move at a given index. + + Args: + index (int): Which monster (0-5). + move_index (int): Which move to change (0-3). + """ + return self.pyboy.get_memory_value(0xD01C + (index * PKMN_SIZE) + move_index) + + def get_player_monster_current_hp(self, index): + """ + Returns player monster's current HP at a given index. + + Args: + index (int): Which monster (0-5). + """ + return self.get_int_at_address(0xD16C + (index * PKMN_SIZE)) + + def get_active_player_monster_current_hp(self): + return self.get_int_at_address(0xD015) + + def set_player_monster_current_hp(self, index, hp): + """ + Sets the current HP of a given player monster. + + Args: + index (int): Which monster to change (0-5). + hp (int): Health points. + """ + self.set_int_at_address(0xD16C + (index * PKMN_SIZE), hp) + + def set_active_player_monster_current_hp(self, hp): + self.set_int_at_address(0xD015, hp) + + def get_player_monster_max_hp(self, index): + """ + Returns player monster's max HP at a given index. + + Args: + index (int): Which monster (0-5). + """ + return self.get_int_at_address(0xD18D + (index * PKMN_SIZE)) + + def get_active_player_monster_max_hp(self): + return self.get_int_at_address(0xD023) + + def set_player_monster_max_hp(self, index, hp): + """ + Sets the max HP of a given player monster. + + Args: + index (int): Which monster to change (0-5). + hp (int): Health points. + """ + self.set_int_at_address(0xD18D + (index * PKMN_SIZE), hp) + + def set_active_player_monster_max_hp(self, hp): + return self.set_int_at_address(0xD023, hp) + + def set_player_monster_status(self, index, status): + """ + Sets the ailment status of a given player monster. + + Args: + index (int): Which monster to change (0-5). + status (int): Status to be set. + """ + assert status >= 0 and status < 0xFF + assert index < 6 + self.pyboy.set_memory_value(0xD16F + (index * PKMN_SIZE), status) + + def set_player_monster_asleep(self, index): + self.set_player_monster_status(index, constants.STATUS_ASLEEP) + + def set_player_monster_poisoned(self, index): + self.set_player_monster_status(index, constants.STATUS_POISONED) + + def set_player_monster_burned(self, index): + self.set_player_monster_status(index, constants.STATUS_BURNED) + + def set_player_monster_frozen(self, index): + self.set_player_monster_status(index, constants.STATUS_FROZEN) + + def set_player_monster_paralyzed(self, index): + self.set_player_monster_status(index, constants.STATUS_PARALYZED) + + def set_player_monster_no_ailment(self, index): + self.set_player_monster_status(index, 0) + + def get_player_money(self): + return self.get_int_at_address(0xD347, size=3) + + def set_active_player_monster_status(self, status): + self.pyboy.set_memory_value(0xD018, status) + + def set_active_player_monster_asleep(self): + self.set_active_player_monster_status(constants.STATUS_ASLEEP) + + def set_active_player_monster_poisoned(self): + self.set_active_player_monster_status(constants.STATUS_POISONED) + + def set_active_player_monster_burned(self): + self.set_active_player_monster_status(constants.STATUS_BURNED) + + def set_active_player_monster_frozen(self): + self.set_active_player_monster_status(constants.STATUS_FROZEN) + + def set_active_player_monster_paralyzed(self): + self.set_active_player_monster_status(constants.STATUS_PARALYZED) + + def set_active_player_monster_no_ailment(self): + self.set_active_player_monster_status(0) + + def set_active_player_monster_move(self, index, move): + self.pyboy.set_memory_value(0xD01C + index, move) + + def set_active_opponent_monster_move(self, index, move): + self.pyboy.set_memory_value(0xCFED + index, move) + + def set_active_player_monster_nickname(self, nickname): + self.set_text(nickname[:10], 0xD009) + + def set_active_opponent_monster_nickname(self, nickname): + self.set_text(nickname[:10], 0xCFDA) + + def set_active_opponent_monster_status(self, status): + self.pyboy.set_memory_value(0xCFE9, status) + + def set_active_opponent_monster_asleep(self): + self.set_active_opponent_monster_status(constants.STATUS_ASLEEP) + + def set_active_opponent_monster_poisoned(self): + self.set_active_opponent_monster_status(constants.STATUS_POISONED) + + def set_active_opponent_monster_burned(self): + self.set_active_opponent_monster_status(constants.STATUS_BURNED) + + def set_active_opponent_monster_frozen(self): + self.set_active_opponent_monster_status(constants.STATUS_FROZEN) + + def set_active_opponent_monster_paralyzed(self): + self.set_active_opponent_monster_status(constants.STATUS_PARALYZED) + + def set_active_opponent_monster_no_ailment(self): + self.set_active_opponent_monster_status(0) + + def heal_player_monster(self, index): + """ + Fully heals a player monster. + + Args: + index (int): Which monster to change (0-5). + """ + self.set_player_monster_no_ailment(index) + max_hp = self.get_player_monster_max_hp(index) + self.set_player_monster_current_hp(index, max_hp) + + def heal_active_player_monster(self): + self.set_active_player_monster_no_ailment() + max_hp = self.get_active_player_monster_max_hp() + self.set_active_player_monster_current_hp(max_hp) + + def is_battle_happening(self): + return self.pyboy.get_memory_value(0xD057) != 0 + + def get_current_map(self): + return self.pyboy.get_memory_value(0xD35E) + + def get_current_map_name(self): + return constants.MAP_NAMES[self.get_current_map()] + + def set_wild_encounter_rate(self, value): + self.pyboy.set_memory_value(0xD887, value) + + # TODO: Add player position functions