Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - OldNewComputers

Pages: [1]
1
As a programming project, I'm writing a Pokemon battle simulator in Python. It still lacks status conditions and most move effects (I am using the pret disassembly and some data extraction tools I wrote to get most of this info, plus some diagrams on Bulbapedia), but I wanted your guys opinion on the code. Keep in mind that, to run it, you need a Pokemon Red/Blue ROM with the name "pokeblue.gb" in the same directory as this file, and I wanted the opinion of you guys on the code. I'm not sure if I'm targeting bug compatibility with anything (including Stadium or RB/Y), but I am targeting Generation 1 functionality. Beware that it is pretty long, and liable only to get longer. Most of the code is in the "mainBattle" function, but that calls several utility functions, and several other functions serve to datamine or create monsters for use in the game. Summary printing works too.
Code: [Select]
#pokemon battle simulator - gen 1
import random
import math
file = open('pokeblue.gb','rb')
file_contents = file.read()
file.close()

typeMatchups = {0: {5: 0.5, 8: 0.0}, 1: {0: 2.0, 3: 0.5, 2: 0.5, 24: 0.5, 7: 0.5, 5: 2.0, 25: 2.0, 8: 0.0}, 2: {23: 0.5, 1: 2.0, 7: 2.0, 22: 2.0, 5: 0.5}, 3: {22: 2.0, 3: 0.5, 4: 0.5, 7: 2.0, 5: 0.5, 8: 0.5}, 4: {2: 0.0, 20: 2.0, 23: 2.0, 22: 0.5, 7: 0.5, 5: 2.0, 3: 2.0}, 5: {20: 2.0, 1: 0.5, 4: 0.5, 2: 2.0, 7: 2.0, 25: 2.0}, 6: {}, 7: {20: 0.5, 22: 2.0, 1: 0.5, 2: 0.5, 24: 2.0, 8: 0.5, 3: 2.0}, 8: {8: 2.0, 0: 0.0, 24: 0.0}, 9: {}, 10: {}, 11: {}, 12: {}, 13: {}, 14: {}, 15: {}, 16: {}, 17: {}, 18: {}, 19: {}, 20: {22: 2.0, 25: 2.0, 20: 0.5, 21: 0.5, 7: 2.0, 5: 0.5, 26: 0.5}, 21: {20: 2.0, 5: 2.0, 21: 0.5, 22: 0.5, 4: 2.0, 26: 0.5}, 22: {21: 2.0, 22: 0.5, 20: 0.5, 4: 2.0, 7: 0.5, 3: 0.5, 5: 2.0, 2: 0.5, 26: 0.5}, 23: {21: 2.0, 23: 0.5, 22: 0.5, 4: 0.0, 2: 2.0, 26: 0.5}, 24: {24: 0.5, 1: 2.0, 3: 2.0}, 25: {25: 0.5, 21: 0.5, 22: 2.0, 4: 2.0, 2: 2.0, 26: 2.0}, 26: {26: 2.0}}
typeNames = {0: 'NORMAL', 1: "FIGHTING", 2: "FLYING", 3: "POISON", 4: 'GROUND',
             5: "ROCK", 6: "BIRD", 7: "BUG", 8: "GHOST", 0x14: "FIRE",
             0x15: 'WATER', 0x16: "GRASS", 0x17: 'ELECTRIC', 0x18: "PSYCHIC",
             0x19: 'ICE', 0x1A: 'DRAGON'}

volatileStatus0 = [0]*8 #confusion, recharge, wrap, leechseed, flinch
volatileStatus1 = [0]*8

statChanges0 = [0, 0, 0, 0, 0, 0] #attack, defense, speed, special, accuracy, evasion
statChanges1 = [0, 0, 0, 0, 0, 0]


statChangeList = {-6: 0.25, -5: 0.28, -4: 1/3, -3: 0.4, -2: 0.5, -1: 2/3, 0: 1,
                  1: 1.5, 2: 2, 3: 2.5, 4: 3, 5: 3.5, 6: 4}

indexToDex = []
dexToIndex = [112, 115, 32, 35, 21, 100, 34, 80, 2, 103, 108, 102, 88, 94, 29, 31, 104, 111, 131, 59, 151, 130, 90, 72, 92, 123, 120, 9, 127, 114, 0, 0, 58, 95, 22, 16, 79, 64, 75, 113, 67, 122, 106, 107, 24, 47, 54, 96, 76, 0, 126, 0, 125, 82, 109, 0, 56, 86, 50, 128, 0, 0, 0, 83, 48, 149, 0, 0, 0, 84, 60, 124, 146, 144, 145, 132, 52, 98, 0, 0, 0, 37, 38, 25, 26, 0, 0, 147, 148, 140, 141, 116, 117, 0, 0, 27, 28, 138, 139, 39, 40, 133, 136, 135, 134, 66, 41, 23, 46, 61, 62, 13, 14, 15, 0, 85, 57, 51, 49, 87, 0, 0, 10, 11, 12, 68, 0, 55, 97, 42, 150, 143, 129, 0, 0, 89, 0, 99, 91, 0, 101, 36, 110, 53, 105, 0, 93, 63, 65, 17, 18, 121, 1, 3, 73, 0, 118, 119, 0, 0, 0, 0, 77, 78, 19, 20, 33, 30, 74, 137, 142, 0, 81, 0, 0, 4, 7, 5, 8, 6, 0, 0, 0, 0, 43, 44, 45, 69, 70, 71]###

a = [21, 54, 1, 100, 0, 24, 24, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 54, 1, 205, 0, 205, 0, 205, 0, 205, 0]
b = [126, 34, 1, 100, 0, 1, 1, 0, 1, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 34, 1, 9, 1, 165, 0, 115, 0, 135, 0]
c = [21, 147, 1, 100, 0, 24, 24, 0, 94, 1, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 35, 0, 0, 0, 147, 1, 42, 1, 42, 1, 42, 1, 42, 1]
                                                                                                    #10  35
player_old = [c,b,a]
enemy_old = ['ROCKET',[a,b]]

currentPkmn = 0
currentEMon = 0

pidgeot_gary = [151, 228, 0, 61, 0, 0, 2, 0, 17, 119, 143, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 20, 5, 20, 0, 228, 0, 159, 0, 153, 0, 172, 0, 147, 0]
alakazam_gary = [149, 188, 0, 59, 0, 24, 24, 0, 60, 94, 115, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 10, 20, 20, 0, 188, 0, 118, 0, 112, 0, 201, 0, 219, 0]
rhydon_gary = [1, 255, 0, 61, 0, 4, 5, 0, 43, 39, 31, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 20, 5, 0, 255, 0, 220, 0, 208, 0, 110, 0, 116, 0]
arcanine_gary = [20, 237, 0, 61, 0, 20, 20, 0, 46, 43, 52, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 25, 20, 0, 237, 0, 195, 0, 159, 0, 177, 0, 159, 0]
exeggutor_gary = [10, 251, 0, 63, 0, 22, 24, 0, 95, 140, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 0, 0, 251, 0, 183, 0, 170, 0, 132, 0, 221, 0]
blastoise_gary = [28, 238, 0, 65, 0, 21, 21, 0, 56, 59, 44, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 25, 40, 0, 238, 0, 173, 0, 195, 0, 166, 0, 175, 0]

test_charizard = [180, 233, 0, 64, 0, 20, 2, 0, 99, 163, 53, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 15, 15, 0, 233, 0, 172, 0, 164, 0, 192, 0, 173, 0]
test_raichu = [85, 210, 0, 64, 0, 23, 23, 0, 85, 86, 97, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 20, 30, 20, 0, 210, 0, 179, 0, 134, 0, 192, 0, 179, 0]
test_tauros = [60, 229, 0, 64, 0, 0, 0, 0, 36, 43, 99, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 20, 30, 0, 229, 0, 192, 0, 186, 0, 205, 0, 154, 0]
test_gyarados = [22, 255, 0, 64, 0, 21, 2, 0, 56, 82, 43, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 30, 5, 0, 255, 0, 224, 0, 165, 0, 168, 0, 192, 0]

player_test = [test_charizard, test_raichu, test_tauros, test_gyarados] #add mons
enemy_gary = ["BLUE",[pidgeot_gary, alakazam_gary, rhydon_gary, arcanine_gary, exeggutor_gary, blastoise_gary]] #add mons

moveNames = """POUND
KARATE CHOP
DOUBLESLAP
COMET PUNCH
MEGA PUNCH
PAY DAY
FIRE PUNCH
ICE PUNCH
THUNDERPUNCH
SCRATCH
VICEGRIP
GUILLOTINE
RAZOR WIND
SWORDS DANCE
CUT
GUST
WING ATTACK
WHIRLWIND
FLY
BIND
SLAM
VINE WHIP
STOMP
DOUBLE KICK
MEGA KICK
JUMP KICK
ROLLING KICK
SAND-ATTACK
HEADBUTT
HORN ATTACK
FURY ATTACK
HORN DRILL
TACKLE
BODY SLAM
WRAP
TAKE DOWN
THRASH
DOUBLE-EDGE
TAIL WHIP
POISON STING
TWINEEDLE
PIN MISSILE
LEER
BITE
GROWL
ROAR
SING
SUPERSONIC
SONICBOOM
DISABLE
ACID
EMBER
FLAMETHROWER
MIST
WATER GUN
HYDRO PUMP
SURF
ICE BEAM
BLIZZARD
PSYBEAM
BUBBLEBEAM
AURORA BEAM
HYPER BEAM
PECK
DRILL PECK
SUBMISSION
LOW KICK
COUNTER
SEISMIC TOSS
STRENGTH
ABSORB
MEGA DRAIN
LEECH SEED
GROWTH
RAZOR LEAF
SOLARBEAM
POISONPOWDER
STUN SPORE
SLEEP POWDER
PETAL DANCE
STRING SHOT
DRAGON RAGE
FIRE SPIN
THUNDERSHOCK
THUNDERBOLT
THUNDER WAVE
THUNDER
ROCK THROW
EARTHQUAKE
FISSURE
DIG
TOXIC
CONFUSION
PSYCHIC
HYPNOSIS
MEDITATE
AGILITY
QUICK ATTACK
RAGE
TELEPORT
NIGHT SHADE
MIMIC
SCREECH
DOUBLE TEAM
RECOVER
HARDEN
MINIMIZE
SMOKESCREEN
CONFUSE RAY
WITHDRAW
DEFENSE CURL
BARRIER
LIGHT SCREEN
HAZE
REFLECT
FOCUS ENERGY
BIDE
METRONOME
MIRROR MOVE
SELFDESTRUCT
EGG BOMB
LICK
SMOG
SLUDGE
BONE CLUB
FIRE BLAST
WATERFALL
CLAMP
SWIFT
SKULL BASH
SPIKE CANNON
CONSTRICT
AMNESIA
KINESIS
SOFTBOILED
HI JUMP KICK
GLARE
DREAM EATER
POISON GAS
BARRAGE
LEECH LIFE
LOVELY KISS
SKY ATTACK
TRANSFORM
BUBBLE
DIZZY PUNCH
SPORE
FLASH
PSYWAVE
SPLASH
ACID ARMOR
CRABHAMMER
EXPLOSION
FURY SWIPES
BONEMERANG
REST
ROCK SLIDE
HYPER FANG
SHARPEN
CONVERSION
TRI ATTACK
SUPER FANG
SLASH
SUBSTITUTE
STRUGGLE"""

pokeNames = ['RHYDON', 'KANGASKHAN', 'NIDORAN♂', 'CLEFAIRY', 'SPEAROW', 'VOLTORB', 'NIDOKING', 'SLOWBRO', 'IVYSAUR', 'EXEGGUTOR', 'LICKITUNG', 'EXEGGCUTE', 'GRIMER', 'GENGAR', 'NIDORAN♀', 'NIDOQUEEN', 'CUBONE', 'RHYHORN', 'LAPRAS', 'ARCANINE', 'MEW', 'GYARADOS', 'SHELLDER', 'TENTACOOL', 'GASTLY', 'SCYTHER', 'STARYU', 'BLASTOISE', 'PINSIR', 'TANGELA', 'MISSINGNO.', 'MISSINGNO.', 'GROWLITHE', 'ONIX', 'FEAROW', 'PIDGEY', 'SLOWPOKE', 'KADABRA', 'GRAVELER', 'CHANSEY', 'MACHOKE', 'MR.MIME', 'HITMONLEE', 'HITMONCHAN', 'ARBOK', 'PARASECT', 'PSYDUCK', 'DROWZEE', 'GOLEM', 'MISSINGNO.', 'MAGMAR', 'MISSINGNO.', 'ELECTABUZZ', 'MAGNETON', 'KOFFING', 'MISSINGNO.', 'MANKEY', 'SEEL', 'DIGLETT', 'TAUROS', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', "FARFETCH'D", 'VENONAT', 'DRAGONITE', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'DODUO', 'POLIWAG', 'JYNX', 'MOLTRES', 'ARTICUNO', 'ZAPDOS', 'DITTO', 'MEOWTH', 'KRABBY', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'VULPIX', 'NINETALES', 'PIKACHU', 'RAICHU', 'MISSINGNO.', 'MISSINGNO.', 'DRATINI', 'DRAGONAIR', 'KABUTO', 'KABUTOPS', 'HORSEA', 'SEADRA', 'MISSINGNO.', 'MISSINGNO.', 'SANDSHREW', 'SANDSLASH', 'OMANYTE', 'OMASTAR', 'JIGGLYPUFF', 'WIGGLYTUFF', 'EEVEE', 'FLAREON', 'JOLTEON', 'VAPOREON', 'MACHOP', 'ZUBAT', 'EKANS', 'PARAS', 'POLIWHIRL', 'POLIWRATH', 'WEEDLE', 'KAKUNA', 'BEEDRILL', 'MISSINGNO.', 'DODRIO', 'PRIMEAPE', 'DUGTRIO', 'VENOMOTH', 'DEWGONG', 'MISSINGNO.', 'MISSINGNO.', 'CATERPIE', 'METAPOD', 'BUTTERFREE', 'MACHAMP', 'MISSINGNO.', 'GOLDUCK', 'HYPNO', 'GOLBAT', 'MEWTWO', 'SNORLAX', 'MAGIKARP', 'MISSINGNO.', 'MISSINGNO.', 'MUK', 'MISSINGNO.', 'KINGLER', 'CLOYSTER', 'MISSINGNO.', 'ELECTRODE', 'CLEFABLE', 'WEEZING', 'PERSIAN', 'MAROWAK', 'MISSINGNO.', 'HAUNTER', 'ABRA', 'ALAKAZAM', 'PIDGEOTTO', 'PIDGEOT', 'STARMIE', 'BULBASAUR', 'VENUSAUR', 'TENTACRUEL', 'MISSINGNO.', 'GOLDEEN', 'SEAKING', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'PONYTA', 'RAPIDASH', 'RATTATA', 'RATICATE', 'NIDORINO', 'NIDORINA', 'GEODUDE', 'PORYGON', 'AERODACTYL', 'MISSINGNO.', 'MAGNEMITE', 'MISSINGNO.', 'MISSINGNO.', 'CHARMANDER', 'SQUIRTLE', 'CHARMELEON', 'WARTORTLE', 'CHARIZARD', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'ODDISH', 'GLOOM', 'VILEPLUME', 'BELLSPROUT', 'WEEPINBELL', 'VICTREEBEL']

def createReverseList(lst):
    dict = {}
    for i in range(len(pokeNames)):
        dict[pokeNames[i]] = i+1
    return dict

revPN = createReverseList(pokeNames)

def findTable():
    address = 0

    for i in file_contents:
        if file_contents[address] == 112 and file_contents[address+1] == 115 and file_contents[address+2] == 32 and file_contents[address+3] == 35: return address
        address += 1

def grabTable(address):
    table = []

    for i in range(0, 190):
        table.append(file_contents[address + i])
   
    return table

def convertNameToIndex(moveName):
    string = moveNames.split("\n")
    index = 0

    for i in string:
        if i == moveName: return index + 1
        index += 1

def physOrSpcl(type):
    if type > 16:
        return 1

    else:
        return 0

def getTE(type0, type1):
    try: return typeMatchups[type0][type1]
    except: return 1

def readBaseStats(pokedex):
    if pokedex in range(1, 151):
        offset = 0x383de + 28*(pokedex-1)
    else:
        offset = 0x425b

    species = []

    for i in range(0, 28):
        species.append(file_contents[offset + i])
       
    return species

def readMoveData(moveID, po=0):
    offset = 0x38000 + 6*(moveID-1)

    if po:
        print("Index Number: {}".format(file_contents[offset]))
        print("Effect ID: {}".format(file_contents[offset+1]))
        print('Attack Power: {}'.format(file_contents[offset+2]))
        print("Move Type: {}".format(file_contents[offset+3]))
        print("Accuracy: {}".format(file_contents[offset+4]))
        print("PP: {}".format(file_contents[offset+5]))

    return (file_contents[offset], file_contents[offset+1], file_contents[offset+2],
            file_contents[offset+3], file_contents[offset+4], file_contents[offset+5])


def indvCalc(B, E, I, L):
    return int(int((2 * B + (I*2) + E) * L / 100 + 5))

def statCalc(evs, ivs, dex, level): #evs = [0,0,0,0,0], ivs = [0,0,0,0]
    stats = [0] * 5
    #generate hp iv
    hp_dv = 0
    add = 8
    for i in ivs:
        if not (i % 2 == 0): hp_dv += add
        add /= 2
    #calculate E value in format E_HP, E_A
    E = [0] * 5
    for i in range(0, 5):
        E[i] = int(min(255, int(math.sqrt(max(0, evs[i] - 1)) + 1)) / 4)

    #calculate all stats
    for i in range(1, 5):
        stats[i] = indvCalc(readBaseStats(dex)[i+1], E[i], ivs[i-1], level)
    #calculate hp
    stats[0] = int((2 * readBaseStats(dex)[1] + (hp_dv*2) + E[0]) * level / 100 + level + 10)
   
    return stats

def monsterCreator(species):
    pokemon = [species] + ([0] * 43)
    #get base stats
    basestats = readBaseStats(dexToIndex[species - 1])
    #enter level, evs, ivs
    pokemon[3] = int(input("Enter level: "))
    evs = [int(input("Enter HP EV: ")), int(input("Enter Attack EV: ")), int(input("Enter Defense EV: ")), int(input("Enter Speed EV: ")), int(input("Enter Special EV: "))]
    ivs = [int(input("Enter Attack IV: ")), int(input("Enter Defense IV: ")), int(input("Enter Speed IV: ")), int(input("Enter Special IV: ")), ]
    #enter moves
    num = int(input('Enter number of moves: '))
   
    for i in range(num):
        move = input("Enter move name: ").upper()
        move = convertNameToIndex(move)
        pokemon[8+i] = move
        pokemon[0x1d+i] = readMoveData(move)[5]

    #calculate stats
    stats = statCalc(evs, ivs, dexToIndex[species - 1], pokemon[3])

    for i in range(0, 5):
        upper = stats[i] >> 8
        lower = stats[i] & 255

        pokemon[0x22+(2*i)] = lower
        pokemon[0x23+(2*i)] = upper
    pokemon[1] = pokemon[0x22]
    pokemon[2] = pokemon[0x23]
    #assign type 1, type 2
    pokemon[5] = basestats[6]
    pokemon[6] = basestats[7]
   
    #return value
    return pokemon

def getMoves(pokemon):
    moveList = ["-"] + moveNames.split("\n")
    list = []
    empty = []
    #print("1. {} | {} | {}/{} PP".format(moveList[pokemon[8]], typeNames[readMoveData(pokemon[8])[3]], pokemon[0x1d], readMoveData(pokemon[8])[5]))
    if pokemon[0x1d] == 0: empty.append("1")
    else: list.append("1")

    if pokemon[9] != 0:
        #print("2. {} | {} | {}/{} PP".format(moveList[pokemon[9]], typeNames[readMoveData(pokemon[9])[3]], pokemon[0x1e], readMoveData(pokemon[9])[5]))
        if pokemon[0x1e] == 0: empty.append("2")
        else: list.append("2")

    if pokemon[10] != 0:
        #print("3. {} | {} | {}/{} PP".format(moveList[pokemon[10]], typeNames[readMoveData(pokemon[10])[3]], pokemon[0x1f], readMoveData(pokemon[10])[5]))
        if pokemon[0x1f] == 0: empty.append("3")
        else: list.append("3")

    if pokemon[11] != 0:
        #("4. {} | {} | {}/{} PP".format(moveList[pokemon[11]], typeNames[readMoveData(pokemon[11])[3]], pokemon[0x20], readMoveData(pokemon[11])[5]))
        if pokemon[0x20] == 0: empty.append("4")
        else: list.append("4")
       
    return list, empty


def printMoves(pokemon):
    moveList = ["-"] + moveNames.split("\n")
    list = []
    empty = []
    print("1. {} | {} | {}/{} PP".format(moveList[pokemon[8]], typeNames[readMoveData(pokemon[8])[3]], pokemon[0x1d], readMoveData(pokemon[8])[5]))
    if pokemon[0x1d] == 0: empty.append("1")
    else: list.append("1")

    if pokemon[9] != 0:
        print("2. {} | {} | {}/{} PP".format(moveList[pokemon[9]], typeNames[readMoveData(pokemon[9])[3]], pokemon[0x1e], readMoveData(pokemon[9])[5]))
        if pokemon[0x1e] == 0: empty.append("2")
        else: list.append("2")

    if pokemon[10] != 0:
        print("3. {} | {} | {}/{} PP".format(moveList[pokemon[10]], typeNames[readMoveData(pokemon[10])[3]], pokemon[0x1f], readMoveData(pokemon[10])[5]))
        if pokemon[0x1f] == 0: empty.append("3")
        else: list.append("3")

    if pokemon[11] != 0:
        print("4. {} | {} | {}/{} PP".format(moveList[pokemon[11]], typeNames[readMoveData(pokemon[11])[3]], pokemon[0x20], readMoveData(pokemon[11])[5]))
        if pokemon[0x20] == 0: empty.append("4")
        else: list.append("4")
       
    return list, empty

def retStatus(pokemon):
    statString = ''
    status = pokemon[4]
    hp = pokemon[1] + (pokemon[2] * 256)

    if status == 0 and hp == 0:
        statString = 'FNT'
    if status == 0 and hp > 0:
        statString = 'OK'
    if status == 4:
        statString = 'SLP'
    if status == 8:
        statString = 'PSN'
    if status == 16:
        statString = 'BRN'
    if status == 32:
        statString = 'FRZ'
    if status == 64:
        statString = 'PRZ'

    return statString


def printSummary(pokemon):
    #what we need to print - stats, types, species, moves
    print("Species: {} | Level {}".format(pokeNames[pokemon[0] - 1], pokemon[3]))
    print("HP: {}/{}".format(pokemon[0x1] + (pokemon[0x2] * 256), pokemon[0x22] + (pokemon[0x23] * 256)))
    print("Attack: {}".format(pokemon[0x24] + (pokemon[0x25] * 256)))
    print("Defense: {}".format(pokemon[0x26] + (pokemon[0x27] * 256)))
    print("Speed: {}".format(pokemon[0x28] + (pokemon[0x29] * 256)))
    print("Special: {}".format(pokemon[0x2A] + (pokemon[0x2B] * 256)))

    print('Status: {}'.format(retStatus(pokemon)))

   
    if pokemon[5] != pokemon[6]: print("Type: {}/{}".format(typeNames[pokemon[5]], typeNames[pokemon[6]])) #add check for single/dual type
    else: print ('Type: {}'.format(typeNames[pokemon[5]]))
    #add attacks
    printMoves(pokemon)
   
    return

def accuracyTest(moveID):
    result = random.randint(0, 256)
    accuracy = readMoveData(moveID)[4] * statChangeList[statChanges0[4]] * statChangeList[0-(statChanges1[5])]

    if accuracy >= result: return 0
    return 1


def printParty(party,p=1):
    counter = 1
    valids = []
    fainted = []
    for i in party:
        stat = retStatus(i)
        if p: print("{}. {} | {} | Level {} | {}/{}HP".format(counter, pokeNames[i[0]-1], stat, i[3], i[0x1] + (i[0x2] * 256), i[0x22] + (i[0x23] * 256))) #add level

        if stat == "FNT":
            fainted.append(str(counter))
        else:
            valids.append(str(counter))

        counter += 1
       
    return valids,fainted


def damageCalculator(attacker, target, moveID):
    global random
    stab = 1
    #modifier = random * stab * type
    #damage = ( ( ( ( (  (2*level) / 5  ) + 2  ) * power * a/d  )/50 ) + 2 ) * modifier

    ###assign vars - attacker level, power, acting attack/defense
    level = attacker[3] #implement critical hits, doubles level, based on speed
    power = readMoveData(moveID)[2]
    result = physOrSpcl(readMoveData(moveID)[3])

    if result:
        a = (attacker[0x2a] + (attacker[0x2b] * 256)) * statChangeList[statChanges0[3]]
        d = (target[0x2a] + (target[0x2b] * 256)) * statChangeList[statChanges1[3]]
    if not result:
        if attacker[4] == 16: a = (attacker[0x24] + (attacker[0x25] * 256)) * statChangeList[statChanges0[0]] * 0.25 #* (0.25*int(attacker[4]==16))
        else: a = (attacker[0x24] + (attacker[0x25] * 256)) * statChangeList[statChanges0[0]]
        d = (target[0x26] + (target[0x27] * 256)) * statChangeList[statChanges1[1]]

    ###calculate modifier
    #calculate random
    rnd = random.randint(217,255) / 255
    #calculate stab
    if readMoveData(moveID)[3] in (attacker[5], attacker[6]):
        stab = 1.5
    #calculate type effectiveness
    if target[5] == target[6]:
        #type = typeMatchups[readMoveData(moveID)[3]][target[5]]
        type = getTE(readMoveData(moveID)[3], target[5])
    if target[5] != target[6]:
        type = getTE(readMoveData(moveID)[3], target[5]) * getTE(readMoveData(moveID)[3], target[6])
        #type = typeMatchups[readMoveData(moveID)[3]][target[5]] * typeMatchups[readMoveData(moveID)[3]][target[6]]

    if type == 0:
        print("It had no effect!")
    if type < 1 and type != 0:
        print("It's not very effective...")
    if type > 1:
        print("It's super effective!")

    modifier = rnd * stab * type
    damage = ( ( ( ( (  (2*level) / 5  ) + 2  ) * power * a/d  )/50 ) + 2 ) * modifier
    #print("Level:{},Power:{},A:{},D:{}".format(level,power,a,d))


    return int(damage)

def resolveMove(attacker, target, move, entry, who=0): #48=recoil, 29=2to5hits
    #add checks for 0 basepower, non-damaging effects
    ret_value = [0, 0] #value 0 = target fainted, value 1 = attacker fainted
    acc = accuracyTest(move)
    if not acc and readMoveData(move)[2] != 0 and readMoveData(move)[1] != 29 and readMoveData(move)[1] != 38:
        damage = damageCalculator(attacker, target, move)
        enemyHP = target[1] + (target[2] * 256)
        #print("{} damage!".format(damage)) #inflict damage, add faint check
        enemyHP -= damage
        if enemyHP < 0: enemyHP = 0
        upper = enemyHP >> 8
        lower = enemyHP & 255
        target[1] = lower
        target[2] = upper
    elif not acc and readMoveData(move)[1] == 38:
        print("It's a one-hit-KO!")
        target[1] = 0; target[2] = 0
    else:
        print("The attack missed...")

    if readMoveData(move)[1] == 48:
        playerHP = attacker[1] + (attacker[2] * 256)
        recoil = int(damage/4)
        print("{} recoil!".format(recoil))
        playerHP -= recoil
        if playerHP < 0: playerHP = 0
        upper = playerHP >> 8
        lower = playerHP & 255
        attacker[1] = lower
        attacker[2] = upper

    if readMoveData(move)[1] == 71: #special down
        down = random.randint(1,10)
        if down == 10:
            if not who:
                statChanges1[3] -= 1
                print("Enemy {}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

            if who: #enemy using attack
                statChanges0[3] -= 1
                print("{}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

            #print("{}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

    if entry != -1:
        attacker[0x1c+entry] -= 1

    if target[1] == 0 and target[2] == 0:
        ret_value[(not who)] = 1
    if attacker[1] == 0 and attacker[2] == 0:
        ret_value[(who)] = 1
    return ret_value



def mainBattle(playerParty, enemy): #list of pokes
    global currentPkmn
    global currentEMon
    global volatileStatus0
    global volatileStatus1
    global statChanges0
    global statChanges1
    moveNameList = [""] + moveNames.split("\n")
    attacking = 0 #0=not,1=attacking,2=struggle
    enemyAttacking = 0
    attack = [0,0]
    enemyAttack = [0,0]
    print("{} WANTS TO FIGHT!".format(enemy[0]))
    enemyParty = enemy[1]

    print("GO, {}!".format(pokeNames[playerParty[0][0]-1]))
    print("{} SENT OUT {}!".format(enemy[0],  pokeNames[enemy[1][0][0]-1]    ))

    while True: #mainbattle loop, add flinch, do speedtest
        enemy_maxhp = enemyParty[currentEMon][0x22] + (enemyParty[currentEMon][0x23] * 256)
        enemy_chp = enemyParty[currentEMon][0x1] + (enemyParty[currentEMon][0x2] * 256)
        enemy_php = (enemy_chp / enemy_maxhp) * 100
        enemy_php = round(enemy_php, 0)
        player_maxhp = playerParty[currentPkmn][0x22] + (playerParty[currentPkmn][0x23] * 256)
        player_chp = playerParty[currentPkmn][0x1] + (playerParty[currentPkmn][0x2] * 256)
        stat = retStatus(playerParty[currentPkmn])
        enemyStat = retStatus(enemyParty[currentEMon])
        print("{} | {} | Level {} | {}/{}HP".format(pokeNames[playerParty[currentPkmn][0]-1], stat, playerParty[currentPkmn][3], player_chp, player_maxhp)) #add hp, level
        print('Enemy: {} | {} | Level {} | {}% HP'.format(pokeNames[enemy[1][currentEMon][0]-1], enemyStat, enemyParty[currentEMon][3], enemy_php)) #add hp, level
        print("")

        if volatileStatus0[1] == 0: #add wrap check

            while True:
                attacking = 0
                attack = [0,0]
                choice = input('''Please choice an option
1.) Fight (Choose an attack)
2.) Switch Pokemon
? ''')
                if choice == "1":
                    options = getMoves(playerParty[currentPkmn])

                    if options[0] == []:
                        print("{} ran out of moves! {} used STRUGGLE!".format(pokeNames[playerParty[currentPkmn][0]-1], pokeNames[playerParty[currentPkmn][0]-1]))
                        #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1)
                        attacking = 2
                        break
                   
                    else: #add freeze, sleep, confusion checks
                        options = printMoves(playerParty[currentPkmn])
                        print("C. Cancel")
                        pkmn = input("? ")

                        while pkmn not in options[0]:
                            #print(options[0])
                            if pkmn in options[1]: print("The move is out of PP!")
                            elif pkmn == "C": break
                            else: print("Out of range. Re-enter move number to use.")
                            pkmn = input('? ').upper()

                        if pkmn in options[0]:
                            #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn))
                            attacking = 1
                            attack = (playerParty[currentPkmn][int(pkmn)+7],int(pkmn))
                            break

                        continue
                       

                if choice == "2": #add summary
                    options = printParty(playerParty)
                    print("C. Cancel")
                    pkmn = input("? ").upper()

                    while pkmn not in options[0]:
                        if pkmn in options[1]: print("There's no energy left to battle!")
                        elif pkmn == "C": break
                        else: print("Out of range. Re-enter Pokemon number to switch.")
                        pkmn = input('? ').upper()

                    if pkmn in options[0]:
                        volatileStatus0 = [0]*8
                        statChanges0 = [0]*6
                        print("Enough, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                        currentPkmn = int(pkmn)-1
                        print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                        break

                    continue

        else:
            print("{} needs to recharge!".format(pokeNames[playerParty[currentPkmn][0]-1]))







        #do enemy input
        if volatileStatus1[1] == 0: #add wrap check

            while True:
                options = printParty(enemyParty,0)
                enemyAttacking = 0
                enemyAttack = [0,0]
                if len(options[0]) > 1: choice = random.choice(["1", "2"])
                else: choice = "1"
                if choice == "1":
                    options = getMoves(enemyParty[currentEMon])

                    if options[0] == []:
                        print("{} ran out of moves! {} used STRUGGLE!".format(pokeNames[enemyParty[currentEMon][0]-1], pokeNames[enemyParty[currentEMon][0]-1]))
                        #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1)
                        enemyAttacking = 2
                        break
                   
                    else: #add freeze, sleep, confusion checks
                        #options = printMoves(playerParty[currentPkmn])
                        #print("C. Cancel")
                        ekmn = random.choice(options[0])

                        while ekmn not in options[0]:
                            #print(options[0])
                            if ekmn in options[1]: pass
                            elif ekmn == "C": break
                            else: pass#rint("Out of range. Re-enter move number to use.")
                            ekmn = random.choice(options[0])

                        if ekmn in options[0]:
                            #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn))
                            enemyAttacking = 1
                            attack = (enemyParty[currentEMon][int(ekmn)+7],int(ekmn))
                            break

                        continue
                       

                if choice == "2": #add summary
                    #print("C. Cancel")
                    ekmn = random.choice(options[0])

                    while ekmn not in options[0]:
                        if ekmn in options[1]: pass#rint("There's no energy left to battle!")
                        elif ekmn == "C": break
                        else: pass#rint("Out of range. Re-enter Pokemon number to switch.")
                        ekmn = input('? ').upper()

                    if ekmn in options[0]:
                        volatileStatus1 = [0]*8
                        statChanges1 = [0]*6
                        print("{} withdrew {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                        currentEMon = int(ekmn)-1
                        print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                        break

                    continue

        else:
            print("{} needs to recharge!".format(pokeNames[enemyParty[currentEMon][0]-1]))




        #add burn checks
        if playerParty[currentPkmn][4] == 16: #player burned
            print("{}'s hurt by the burn!")
            playerMaxHP = playerParty[currentPkmn][22] + (playerParty[currentPkmn][23] * 256)
            playerHP = playerParty[currentPkmn][1] + (playerParty[currentPkmn][2] * 256)
            playerHP -= int(playerMaxHP * (1/16))
            upper = playerHP >> 8
            lower = playerHP & 255
            playerParty[currentPkmn][1] = lower
            playerParty[currentPkmn][2] = upper
        ###faint check for burn check
           
        #input resolving time - add confusion, wrap tests
        result = [0,0]
        playerSpeed = (playerParty[currentPkmn][0x28] + (playerParty[currentPkmn][0x29] * 256)) * statChangeList[statChanges0[2]]
        enemySpeed = (enemyParty[currentEMon][0x28] + (enemyParty[currentEMon][0x29] * 256)) * statChangeList[statChanges1[2]]

        if playerSpeed == enemySpeed:
            rand = random.randint(1,2)
            if rand == 1: playerSpeed += 1
            if rand == 2: enemySpeed += 1

        if playerSpeed > enemySpeed: #player outspeeds enemy, include faint check
            if attacking == 1:
                print("{} used {}!".format(pokeNames[playerParty[currentPkmn][0]-1], moveNameList[playerParty[currentPkmn][int(pkmn)+7]]))
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn), 1)     
            if attacking == 2:
                print("{} is out of PP! {} used STRUGGLE!")
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1, 1)
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
            result = [0,0]
            #execute enemy attack
            if enemyAttacking == 1:
                print("Enemy {} used {}!".format(pokeNames[enemy[1][currentEMon][0]-1], moveNameList[enemyParty[currentEMon][int(ekmn)+7]]))
                result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], enemyParty[currentEMon][int(ekmn)+7], int(ekmn))
               
                #print(result)
            if enemyAttacking == 2: result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], 165, -1)
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))


            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            #faint check for enemy, player here

        if enemySpeed > playerSpeed: #enemy outspeeds player, include faint check
           
            if enemyAttacking == 1:
                print("Enemy {} used {}!".format(pokeNames[enemy[1][currentEMon][0]-1], moveNameList[enemyParty[currentEMon][int(ekmn)+7]]))
                result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], enemyParty[currentEMon][int(ekmn)+7], int(ekmn))
               
            if enemyAttacking == 2: result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], 165, -1)
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
            result = [0,0]
            #execute player attack
            if attacking == 1:
                print("{} used {}!".format(pokeNames[playerParty[currentPkmn][0]-1], moveNameList[playerParty[currentPkmn][int(pkmn)+7]]))
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn), 1)
               
            if attacking == 2: result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1, 1)
           
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))

            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                   
        volatileStatus0[4] = 0 #reset flinch chance
        volatileStatus1[4] = 0

        #add leech seed, burn, poison check
    return
2
Emulation & ROM Hacking / Re: Spaceworld Demo Datamining
« on: January 23, 2019, 04:57:27 pm »
As with your previous program, it doesn't provide anything new, but seems like it was a good coding exercise that could be adapted for a good few necessary scripts in the future. :)

Thanks for your input, you do have a point that the basestats data is found elsewhere and so is the general program, but what necessary scripts are you potentially referring to?
3
Emulation & ROM Hacking / Spaceworld Demo Datamining
« on: January 23, 2019, 04:16:16 pm »
Based on a small datamining tool I wrote for Generation 1, I adapted it to the Spaceworld demo based on some Gen II base stats info from Bulbapedia. I attached a CSV (well, not sure how to attach a file so I just inserted it as code) with the base stats info I've gotten out of the ROM. (No idea if this is the right place to put this topic). While I found tons of stuff on the sprites from the Gen 2 beta, I didn't find the basestats anywhere. Now, here is what I learned about the Pokemon Base Stats data structure from the Gen 2 betas. I've only attached the new Pokemon and I'll also insert the program code.

- Bulbasaur begins at 0x50f10
- The format is largely the same as Generation II
- Each Pokemon is 31 bytes long, instead of 32 bytes like the final game (not sure what is cut out yet, may be egg info or one of the TM flag bytes)

Code: [Select]
#, HP, ATK, DEF, SPD, SPATK, SPDEF, TYPE1, TYPE2, CATCHRATE, BASEEXP, GENDER
152, 55, 40, 45, 40, 75, 50, GRASS, GRASS, 255, 64, MEDIUM SLOW, 50/50 M/F
153, 50, 45, 50, 50, 45, 50, GRASS, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F
154, 70, 65, 60, 60, 55, 50, GRASS, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F
155, 50, 60, 40, 40, 50, 50, FIRE, FIRE, 255, 64, MEDIUM SLOW, 50/50 M/F
156, 60, 70, 50, 50, 60, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
157, 70, 80, 60, 60, 70, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
158, 45, 50, 50, 45, 50, 50, WATER, WATER, 255, 64, MEDIUM SLOW, 50/50 M/F
159, 55, 55, 60, 55, 55, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
160, 75, 60, 70, 65, 60, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
161, 65, 55, 40, 65, 55, 50, FLYING, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
162, 50, 50, 50, 50, 50, 50, FLYING, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
163, 50, 50, 50, 50, 50, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
164, 50, 50, 50, 50, 50, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
165, 55, 45, 45, 50, 70, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
166, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
167, 50, 50, 50, 50, 50, 50, GRASS, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F
168, 50, 50, 50, 50, 50, 50, GRASS, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F
169, 50, 50, 50, 50, 50, 50, WATER, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
170, 55, 80, 50, 45, 60, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
171, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
172, 50, 50, 50, 50, 50, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
173, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
174, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
175, 50, 45, 50, 55, 75, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
176, 45, 50, 50, 60, 50, 50, FLYING, PSYCHIC, 255, 100, MEDIUM SLOW, 50/50 M/F
177, 55, 50, 50, 80, 70, 50, FLYING, PSYCHIC, 255, 100, MEDIUM SLOW, 50/50 M/F
178, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
179, 45, 50, 55, 40, 55, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
180, 50, 50, 50, 30, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
181, 90, 110, 50, 110, 55, 50, WATER, STEEL, 255, 100, MEDIUM SLOW, 50/50 M/F
182, 60, 65, 60, 30, 80, 50, WATER, STEEL, 255, 100, MEDIUM SLOW, 50/50 M/F
183, 60, 65, 50, 85, 45, 50, POISON, FLYING, 255, 100, MEDIUM FAST, 50/50 M/F
184, 50, 50, 50, 50, 50, 50, BUG, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F
185, 50, 50, 50, 50, 50, 50, BUG, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
186, 50, 50, 50, 50, 50, 50, BUG, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
187, 50, 50, 50, 50, 50, 50, FLYING, STEEL, 255, 100, MEDIUM SLOW, 50/50 M/F
188, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
189, 50, 50, 50, 50, 50, 50, NORMAL, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
190, 50, 50, 50, 50, 50, 50, GRASS, PSYCHIC, 255, 100, MEDIUM SLOW, 50/50 M/F
191, 50, 50, 50, 50, 50, 50, GROUND, GROUND, 255, 100, MEDIUM SLOW, 50/50 M/F
192, 70, 70, 70, 50, 50, 50, GROUND, GROUND, 255, 100, MEDIUM SLOW, 50/50 M/F
193, 50, 50, 50, 50, 50, 50, DARK, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
194, 70, 50, 50, 45, 45, 50, DARK, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
195, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
196, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
197, 40, 65, 40, 70, 65, 50, DARK, DARK, 255, 100, MEDIUM SLOW, 100% Female
198, 50, 50, 50, 50, 50, 50, DARK, DARK, 255, 100, MEDIUM SLOW, 50/50 M/F
199, 90, 85, 95, 70, 70, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
200, 95, 75, 110, 30, 80, 50, WATER, PSYCHIC, 255, 100, MEDIUM FAST, 50/50 M/F
201, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
202, 50, 50, 50, 50, 50, 50, BUG, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
203, 50, 50, 50, 50, 50, 50, BUG, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
204, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
205, 50, 50, 50, 50, 50, 50, PSYCHIC, PSYCHIC, 255, 100, MEDIUM SLOW, 50/50 M/F
206, 50, 50, 50, 50, 50, 50, POISON, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
207, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
208, 50, 50, 50, 50, 50, 50, POISON, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
209, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
210, 50, 50, 50, 50, 50, 50, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
211, 50, 50, 50, 50, 50, 50, FIGHTING, FIGHTING, 255, 100, MEDIUM SLOW, 50/50 M/F
212, 50, 50, 50, 50, 50, 50, FIGHTING, FIGHTING, 255, 100, MEDIUM SLOW, 50/50 M/F
213, 50, 50, 50, 50, 50, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
214, 40, 50, 35, 50, 50, 50, GRASS, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
215, 50, 50, 45, 50, 60, 50, GRASS, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
216, 60, 50, 55, 50, 70, 50, GRASS, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
217, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
218, 50, 50, 50, 50, 50, 50, ICE, ICE, 255, 100, MEDIUM SLOW, 50/50 M/F
219, 30, 55, 45, 65, 50, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM FAST, 100% Male
220, 50, 50, 50, 50, 50, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
221, 50, 50, 50, 50, 50, 50, GRASS, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
222, 50, 50, 50, 50, 50, 50, GRASS, POISON, 255, 100, MEDIUM SLOW, 50/50 M/F
223, 110, 50, 60, 40, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
224, 50, 50, 50, 50, 50, 50, WATER, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
225, 50, 50, 50, 50, 50, 50, WATER, ICE, 255, 100, MEDIUM SLOW, 50/50 M/F
226, 55, 50, 45, 50, 50, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
227, 65, 60, 55, 60, 60, 50, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
228, 50, 50, 50, 50, 50, 50, NORMAL, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
229, 55, 40, 50, 45, 75, 50, GHOST, GHOST, 255, 100, MEDIUM SLOW, 50/50 M/F
230, 60, 50, 55, 55, 70, 50, GHOST, GHOST, 255, 100, MEDIUM SLOW, 50/50 M/F
231, 50, 50, 50, 50, 50, 50, DARK, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
232, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
233, 50, 50, 50, 50, 50, 50, BUG, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
234, 50, 50, 50, 50, 50, 50, BUG, BUG, 255, 100, MEDIUM SLOW, 50/50 M/F
235, 50, 50, 50, 50, 50, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
236, 50, 50, 50, 50, 50, 50, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
237, 50, 50, 50, 50, 50, 50, ICE, ICE, 255, 100, MEDIUM SLOW, 50/50 M/F
238, 50, 50, 50, 50, 50, 50, ICE, ICE, 255, 100, MEDIUM SLOW, 50/50 M/F
239, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
240, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
241, 50, 50, 50, 50, 50, 50, STEEL, GROUND, 255, 100, MEDIUM SLOW, 50/50 M/F
242, 50, 50, 50, 50, 50, 50, DRAGON, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
243, 90, 90, 85, 100, 125, 98, ELECTRIC, ELECTRIC, 255, 100, MEDIUM SLOW, 50/50 M/F
244, 90, 100, 90, 90, 125, 99, FIRE, FIRE, 255, 100, MEDIUM SLOW, 50/50 M/F
245, 90, 85, 100, 85, 125, 97, WATER, WATER, 255, 100, MEDIUM SLOW, 50/50 M/F
246, 45, 65, 50, 85, 40, 50, DARK, DARK, 255, 100, MEDIUM SLOW, 50/50 M/F
247, 100, 100, 100, 100, 100, 50, FLYING, FLYING, 255, 100, MEDIUM SLOW, 50/50 M/F
248, 50, 50, 50, 50, 50, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
249, 65, 70, 60, 50, 70, 50, PSYCHIC, PSYCHIC, 255, 100, MEDIUM SLOW, 100% Female
250, 55, 55, 50, 60, 60, 50, NORMAL, NORMAL, 255, 100, MEDIUM SLOW, 50/50 M/F
251, 50, 50, 50, 50, 50, 50, GRASS, GRASS, 255, 100, MEDIUM SLOW, 50/50 M/F

Code: [Select]
#constants
FILE_NAME = "pkmn_gold.sgb"
FILE = open(FILE_NAME, "rb")
FILE_CONTENTS = FILE.read()
FILE.close()

typeDict = {0: "NORMAL", 1: "FIGHTING", 2: "FLYING", 3: "POISON", 4: "GROUND",
            5: "ROCK", 6: "BIRD", 7: "BUG", 8: "GHOST", 9: "STEEL", 0x14: "FIRE",
            0x15: "WATER", 0x16: "GRASS", 0x17: "ELECTRIC", 0x18:"PSYCHIC",
            0x19: "ICE", 0x1A: "DRAGON", 0x1B: "DARK"}


growthDict = {0: "MEDIUM FAST", 3: "MEDIUM SLOW", 4: "FAST", 5: "SLOW"}


#loop 45 49
"""for i in range(len(FILE_CONTENTS)):
  if FILE_CONTENTS[i] == 1 and FILE_CONTENTS[i+1] == 45 and FILE_CONTENTS[i+2] == 49:
    print(i)"""

#functions
def findBulba():
  for i in range(len(FILE_CONTENTS)):
    if FILE_CONTENTS[i] == 1 and FILE_CONTENTS[i+1] == 45 and FILE_CONTENTS[i+2] == 49:
      return i

def findIvy():
  for i in range(len(FILE_CONTENTS)):
    if FILE_CONTENTS[i] == 2 and FILE_CONTENTS[i+1] == 60 and FILE_CONTENTS[i+2] == 62:
      return i

def offsetCalc(pokedexNum):
  return 331536 + (31 * (pokedexNum - 1))

def printPkmn(pokedexNum): #gender is 0x0d
  offset = offsetCalc(pokedexNum)
  print("Pokedex #: {}".format(FILE_CONTENTS[offset]))

  print("Base HP: {}".format(FILE_CONTENTS[offset+1]))

  print("Base Attack: {}".format(FILE_CONTENTS[offset+2]))

  print("Base Defense: {}".format(FILE_CONTENTS[offset+3]))

  print("Base Speed: {}".format(FILE_CONTENTS[offset+4]))

  print("Base SpAtk: {}".format(FILE_CONTENTS[offset+5]))

  print("Base SpDef: {}".format(FILE_CONTENTS[offset+6]))

  print("Type 1: {}".format(typeDict[FILE_CONTENTS[offset+7]]))

  print("Type 2: {}".format(typeDict[FILE_CONTENTS[offset+8]]))

  print("Catch Rate: {}".format(FILE_CONTENTS[offset+9]))

  print("Base EXP Yield: {}".format(FILE_CONTENTS[offset+10]))

  if FILE_CONTENTS[offset+0xd] == 255:
    gender = "100% Genderless"
 
  if FILE_CONTENTS[offset+0xd] == 254:
    gender = "100% Female"
 
  if FILE_CONTENTS[offset+0xd] == 191:
    gender = "25%M/75%F"
 
  if FILE_CONTENTS[offset+0xd] == 127:
    gender = "50/50 M/F"
 
  if FILE_CONTENTS[offset+0xd] == 63:
    gender = "75M/25F"
 
  if FILE_CONTENTS[offset+0xd] == 31:
    gender = "87.5M/12.5F"
 
  if FILE_CONTENTS[offset+0xd] == 0:
    gender = "100% Male"
 
  print("Growth Rate: {}".format(growthDict[FILE_CONTENTS[offset+0x16]]))

  print("Gender Ratio: {}".format(gender))

def genCSVLine(pokedexNum): #gender is 0x0d
  CSVLine = ""
  offset = offsetCalc(pokedexNum)
  #print("Pokedex #: {}".format(FILE_CONTENTS[offset]))
  CSVLine += str(FILE_CONTENTS[offset]) + ", "

  #print("Base HP: {}".format(FILE_CONTENTS[offset+1]))
  CSVLine += str(FILE_CONTENTS[offset+1]) + ", "

  #print("Base Attack: {}".format(FILE_CONTENTS[offset+2]))
  CSVLine += str(FILE_CONTENTS[offset+2]) + ", "

  #print("Base Defense: {}".format(FILE_CONTENTS[offset+3]))
  CSVLine += str(FILE_CONTENTS[offset+3]) + ", "

  #print("Base Speed: {}".format(FILE_CONTENTS[offset+4]))
  CSVLine += str(FILE_CONTENTS[offset+4]) + ", "

  #print("Base SpAtk: {}".format(FILE_CONTENTS[offset+5]))
  CSVLine += str(FILE_CONTENTS[offset+5]) + ", "

  #print("Base SpDef: {}".format(FILE_CONTENTS[offset+6]))
  CSVLine += str(FILE_CONTENTS[offset+6]) + ", "

  #print("Type 1: {}".format(typeDict[FILE_CONTENTS[offset+7]]))
  CSVLine += str(typeDict[FILE_CONTENTS[offset+7]]) + ", "

  #print("Type 2: {}".format(typeDict[FILE_CONTENTS[offset+8]]))
  CSVLine += str(typeDict[FILE_CONTENTS[offset+8]]) + ", "

  #print("Catch Rate: {}".format(FILE_CONTENTS[offset+9]))
  CSVLine += str(FILE_CONTENTS[offset+9]) + ", "

  #print("Base EXP Yield: {}".format(FILE_CONTENTS[offset+10]))
  CSVLine += str(FILE_CONTENTS[offset+10]) + ", "

  if FILE_CONTENTS[offset+0xd] == 255:
    gender = "100% Genderless"
 
  if FILE_CONTENTS[offset+0xd] == 254:
    gender = "100% Female"
 
  if FILE_CONTENTS[offset+0xd] == 191:
    gender = "25%M/75%F"
 
  if FILE_CONTENTS[offset+0xd] == 127:
    gender = "50/50 M/F"
 
  if FILE_CONTENTS[offset+0xd] == 63:
    gender = "75M/25F"
 
  if FILE_CONTENTS[offset+0xd] == 31:
    gender = "87.5M/12.5F"
 
  if FILE_CONTENTS[offset+0xd] == 0:
    gender = "100% Male"
 
  #print("Growth Rate: {}".format(growthDict[FILE_CONTENTS[offset+0x16]]))
  CSVLine += growthDict[FILE_CONTENTS[offset+0x16]] + ", "

  #print("Gender Ratio: {}".format(gender))
  CSVLine += gender
  return CSVLine

def genCSV():
  CSV = "#, HP, ATK, DEF, SPD, SPATK, SPDEF, TYPE1, TYPE2, CATCHRATE, BASEEXP, GENDER\n"
  for i in range(152, 252):
    CSV += genCSVLine(i) + "\n"
  print(CSV)
  return
4
The Dumpster Out Back / My New Pokemon ROM Hacking Tool!
« on: January 22, 2019, 06:28:13 pm »
As a test of my programming skill, I wrote a basic ROM hacking tool for Pokemon Generation 1 (well, I've only tested it with Yellow version) in Python. You can edit basic info about Pokemon such as types, base stats, basic attacks (not yet but soon), catch rate, growth rate (not yet but soon), etc., as well as sprite pointers (not yet but soon). So there are a number of features I plan, and I might even add some Gen 2 compatibility.

A few major features I want to add too but I don't have enough info for I'm listing below and I hope you can help me find the information. The first two below are my main priorities but the latter two would be nice too.

1.) Edit Moves (Move data structure gen 1? Where do the moves begin? How large is a move in bytes?)
2.) Edit Trainers (Trainer data structure gen 1? Where do the trainers begin? How large is a trainer in bytes?)
3.) Edit Map (Map data structure gen 1? What offset do the maps start at? How large is a map in bytes?)
4.) Edit Sprites (Sprite structure gen 1? Where do the Pokemon sprites begin? How large is each?)

BTW a couple bugs -- I only have a few moves programmed in -- trying to print out moves it doesn't understand as base moves causes a key error.
Code: [Select]
"""
pokereader v1
Allows you to read and edit Pokemon base stats.
"""

#constants
START_ADDRESS = 0x383DE
FILE = "DEBUGYELLOW.GBC"
typeDict = {0: "NORMAL", 1: "FIGHTING", 2: "FLYING", 3: "POISON", 4: "GROUND",
            5: "ROCK", 6: "BIRD", 7: "BUG", 8: "GHOST", 0x14: "FIRE",
            0x15: "WATER", 0x16: "GRASS", 0x17: "ELECTRIC", 0x18:"PSYCHIC",
            0x19: "ICE", 0x1A: "DRAGON"}
growthDict = {0: "MEDIUM FAST", 3: "MEDIUM SLOW", 4: "FAST", 5: "SLOW"}
attackDict = {1: "POUND", 2: "KARATE CHOP", 3: "DOUBLE SLAP",
              33: "TACKLE", 45: "GROWL"}

reverseTypeDict = {v: k for k, v in typeDict.items()}
reverseAttackDict = {v: k for k, v in attackDict.items()}
reverseGrowthDict = {v: k for k, v in growthDict.items()}

menuString = """Pokemon Editor Menu
1. Edit Pokedex Number
2. Edit Base HP
3. Edit Base Attack
4. Edit Base Defense
5. Edit Base Speed
6. Edit Base Special
7. Edit Type 1
8. Edit Type 2
9. Edit Catch Rate
A. Edit Base EXP
B. Edit Base Attack 1
C. Edit Base Attack 2
D. Edit Base Attack 3
E. Edit Base Attack 4
F. Edit TM/HM Flags
10. Edit Dimensions of Frontsprite
11. Edit Pointer to Frontsprite
12. Edit Pointer to Backsprite
13. Edit Growth Rate
14. Exit"""


#functions
def calcPkmnOffset(pokedexNum, gen=1):
    return START_ADDRESS + (28 * (pokedexNum - 1))

def printReport(pokedexNum):
    offset = calcPkmnOffset(pokedexNum)
    fil = open(FILE, "rb")
    file = fil.read()

    print("Pokedex Number: " + str(file[offset + 0]))
    print("Base HP: " + str(file[offset + 1]))
    print("Base Attack: " + str(file[offset + 2]))
    print("Base Defense: " + str(file[offset + 3]))
    print("Base Speed: " + str(file[offset + 4]))
    print("Base Special: " + str(file[offset + 5]))

    try:
        print("Type 1: " + str(typeDict[file[offset+6]]))
    except: print("Type 1: GLITCH")

    try:
        print("Type 2: " + str(typeDict[file[offset+7]]))
    except: print("Type 2: GLITCH")

    print("Catch rate: " + str(file[offset+8]))
    print("Base EXP Yield: " + str(file[offset+9]))
    ##### insert attacks known at lv1 here -- 0x0f-0x12
    if file[offset+0x0f]:
        print("Lv1 Attack #1: {}".format(attackDict[file[offset+0x0f]]))

    if file[offset+0x10]:
        print("Lv1 Attack #2: {}".format(attackDict[file[offset+0x10]]))

    if file[offset+0x11]:
        print("Lv1 Attack #3: {}".format(attackDict[file[offset+0x11]]))

    if file[offset+0x12]:
        print("Lv1 Attack #4: {}".format(attackDict[file[offset+0x12]]))

    print("Growth Rate: " + growthDict[file[offset+0x13]])

    tms = input("Show TM/HM Learnset? (Y)es or (N)o? ").upper()

    """if tms == "Y" or tms == "YES":
        for i in range(55):
            off = offset + 0x14 + int((i / 8))
            n = bool(off & (2 ** (i % 8)))
            if n: print("TM" + str(i-1)) #very buggy"""

    fil.close()
   
    return

def editStats(pokedexNum):
    offset = calcPkmnOffset(pokedexNum)

    review = input("Review old stats? (Y)es or (N)o? ").upper()

    if review == "Y" or review == "YES":
        printReport(pokedexNum)

    file_ = open(FILE, "rb")
    file_old = list(file_.read())
    file_.close()
    open(FILE,"wb").close()
    file = open(FILE, "wb")
   

    while True:
        print(menuString)
        choice = input("? ")

        if choice == "1":
            num = int(input("Enter New Pokedex Number: "))
            file_old[offset] = num

        if choice == "2":
            stat = int(input("Enter New HP Basestat: "))
            file_old[offset+1] = stat

        if choice == "3":
            stat = int(input("Enter New Attack Basestat: "))
            file_old[offset+2] = stat

        if choice == "4":
            stat = int(input("Enter New Defense Basestat: "))
            file_old[offset+3] = stat

        if choice == "5":
            stat = int(input("Enter New Speed Basestat: "))
            file_old[offset+4] = stat

        if choice == "6":
            stat = int(input("Enter New Special Basestat: "))
            file_old[offset+5] = stat

        if choice == "7":
            typ = reverseTypeDict[input("Enter New Type 1: ").upper()]
            file_old[offset+6] = typ

        if choice == "8":
            typ = reverseTypeDict[input("Enter New Type 2: ").upper()]
            file_old[offset+7] = typ

        if choice == "9":
            stat = int(input("Enter New Catchrate: "))
            file_old[offset+8] = stat

        if choice == "A":
            stat = int(input("Enter New EXP Yield: "))
            file_old[offset+9] = stat
           
        if choice == "14": break

    file_old = bytearray(file_old)
    file.write(file_old)
    file.close()

    return
Pages: [1]