diff options
author | Bryan Bishop <kanzure@gmail.com> | 2013-09-07 23:37:05 -0500 |
---|---|---|
committer | Bryan Bishop <kanzure@gmail.com> | 2013-09-07 23:37:05 -0500 |
commit | 92a4be1776c89bafff0a39c58dbad7b3f7dc77e2 (patch) | |
tree | 454909a76b0cff87a7c6718eea1e09b329c9990a | |
parent | 63bd0e4d6fcb77766dc12fd94788deff586972cd (diff) |
better vba automation methods
-rw-r--r-- | vba.py | 187 |
1 files changed, 179 insertions, 8 deletions
@@ -115,7 +115,7 @@ except ImportError: print "Not loading the keyboard module (which uses networkx)." if not os.path.exists(rom_path): - raise Exception("rom_path is not configured properly; edit vba_config.py?") + raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) def _check_java_library_path(): """ @@ -736,30 +736,142 @@ class cheats: """ Gb.cheatAddGameshark(code, description) -class crystal: +def get_stack(): + """ + Return a list of functions on the stack. + """ + addresses = [] + sp = registers.sp + + for x in range(0, 11): + sp = sp - (2 * x) + hi = get_memory_at(sp + 1) + lo = get_memory_at(sp) + address = ((hi << 8) | lo) + addresses.append(address) + return addresses + +class crystal: """ Just a simple namespace to store a bunch of functions for Pokémon Crystal. """ @staticmethod - def text_wait(step_size=10, max_wait=500): + def better_text_wait(limit=500): + """ + Try to get out of text. + """ + done = False + + while not done: + if limit == 0: + raise Exception("limit reached") + + press("a", holdsteps=10, aftersteps=1) + + stack = get_stack() + + # PrintLetterDelay + pld = range(0x313d, 0x318c) + + delayframes = range(0x468, 0x46f) + + if any([address in stack for address in [0xaef] + pld + delayframes + range(0xa35, 0xa46)]): + press("a", holdsteps=10, aftersteps=1) + else: + print str([hex(x) for x in stack]) + break + + limit = limit - 1 + + print "limit is: " + str(limit) + + @staticmethod + def text_wait(step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None): """ Presses the "A" button when text is done being drawn to screen. + The `debug` parameter is only useful when debugging this function. It + enables the `max_wait` feature, which causes the function to exit + instead of hanging around. + + The `sfx_limit` parameter is useful for when the player is given an + item during the text. Set it to 1 to not treat the sound as the end of + text. The next loop around it will return to the normal behavior of the + function. + :param step_size: number of steps per wait loop :param max_wait: number of wait loops to perform """ - for x in range(0, max_wait): + while max_wait > 0: hi = get_memory_at(registers.sp + 1) lo = get_memory_at(registers.sp) address = ((hi << 8) | lo) - if address == 0xaef: - break + + if address in range(0xa1b, 0xa46) + range(0xaaf, 0xaf5): # 0xaef: + print "pressing, then breaking.. address is: " + str(hex(address)) + + # set CurSFX + set_memory_at(0xc2bf, 0) + + press("a", holdsteps=10, aftersteps=1) + + # check if CurSFX is SFX_READ_TEXT_2 + if get_memory_at(0xc2bf) == 0x8: + print "cursfx is set to SFX_READ_TEXT_2, looping.." + return crystal.text_wait(step_size=step_size, max_wait=max_wait, debug=debug, callback=callback, sfx_limit=sfx_limit) + else: + if sfx_limit > 0: + sfx_limit = sfx_limit - 1 + print "decreasing sfx_limit" + else: + # probably the last textbox in a sequence + print "cursfx is not set to SFX_READ_TEXT_2, so: breaking" + + break else: - nstep(step_size) + stack = get_stack() - press("a", holdsteps=50, aftersteps=1) + # yes/no box or the name selection box + if address in range(0xa46, 0xaaf): + print "probably at a yes/no box.. exiting." + break + + # date/time box (day choice) + # 0x47ab is the one from the intro, 0x49ab is the one from mom. + elif 0x47ab in stack or 0x49ab in stack: # was any([x in stack for x in range(0x46EE, 0x47AB)]) + print "probably at a date/time box ? exiting." + break + + # "How many minutes?" selection box + elif 0x4826 in stack: + print "probably at a \"How many minutes?\" box ? exiting." + break + + else: + nstep(step_size) + + # if there is a callback, then call the callback and exit when the + # callback returns True. This is especially useful during the + # OakSpeech intro where textboxes are running constantly, and then + # suddenly the player can move around. One way to detect that is to + # set callback to a function that returns + # "vba.get_memory_at(0xcfb1) != 0". + if callback != None: + result = callback() + if result == True: + print "callback returned True, exiting" + return + + # only useful when debugging. When this is left on, text that + # takes a while to print to screen will cause this function to + # exit. + if debug == True: + max_wait = max_wait - 1 + + if max_wait == 0: + print "max_wait was hit" @staticmethod def walk_through_walls_slow(): @@ -1025,6 +1137,65 @@ class crystal: set_memory(memory) + @staticmethod + def wait_for_script_running(debug=False, limit=1000): + """ + Wait until ScriptRunning isn't -1. + """ + while limit > 0: + if get_memory_at(0xd438) != 255: + print "script is done executing" + return + else: + step() + + if debug: + limit = limit - 1 + + if limit == 0: + print "limit ran out" + + @staticmethod + def move(cmd): + """ + Attempt to move the player. + """ + press(cmd, holdsteps=10, aftersteps=0) + press([]) + + memory = get_memory() + #while memory[0xd4e1] == 2 and memory[0xd042] != 0x3e: + while memory[0xd043] in [0, 1, 2, 3]: + #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e: + nstep(10) + memory = get_memory() + + @staticmethod + def bad_move_d042(cmd, limit=1000): + """ + Move until not moving. + """ + press(cmd, holdsteps=10, aftersteps=0) + press([]) + + # still standing? probably at a wall or something. + if get_memory()[0xd042] == 0x3e: + return + + counter = 0 + + # this doesn't work because sometimes 0xd042 happens more/less times + while counter <= 6: + # check MovementAnimation + if get_memory()[0xd042] != 0x3e: + print "0xd042 is not 0x3e" + counter += 1 + + # make the emulator state advance one frame or w/e + step() + + print "counter: " + str(counter) + class TestEmulator(unittest.TestCase): try: state = load_state("cheating-12") |