better vba automation methods

This commit is contained in:
Bryan Bishop
2013-09-07 23:37:05 -05:00
parent 63bd0e4d6f
commit 92a4be1776

189
vba.py
View File

@@ -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
else:
nstep(step_size)
press("a", holdsteps=50, aftersteps=1)
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:
stack = get_stack()
# 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")