From 51ff03e95298fbb42f860e1fb896ea881a71a0d2 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:42:54 -0500 Subject: fix up some import lines --- pokemontools/vba/vba.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index d7fdf1d..9569bc2 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -12,9 +12,13 @@ from copy import copy import unittest # for converting bytes to readable text -from pokemontools.chars import chars +from pokemontools.chars import ( + chars, +) -from pokemontools.map_names import map_names +from pokemontools.map_names import ( + map_names, +) import keyboard -- cgit v1.2.3 From ef1709a8bed631e1b01cffb65fb651d8f60938c5 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:47:39 -0500 Subject: remove the custom press() implementation This is now handled in vba_wrapper. --- pokemontools/vba/vba.py | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 9569bc2..dbbe3c3 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -46,27 +46,6 @@ def translate_chars(charz): result += chars[each] return result -def press(buttons, holdsteps=1, aftersteps=1): - """ - Press a button. - - Use steplimit to say for how many steps you want to press - the button (try leaving it at the default, 1). - """ - if hasattr(buttons, "__len__"): - number = button_combiner(buttons) - elif isinstance(buttons, int): - number = buttons - else: - number = buttons - for step_counter in range(0, holdsteps): - Gb.step(number) - - # clear the button press - if aftersteps > 0: - for step_counter in range(0, aftersteps): - Gb.step(0) - def call(bank, address): """ Jumps into a function at a certain address. @@ -148,7 +127,7 @@ class crystal: # set CurSFX set_memory_at(0xc2bf, 0) - press("a", holdsteps=10, aftersteps=1) + vba.press("a", hold=10, after=1) # check if CurSFX is SFX_READ_TEXT_2 if get_memory_at(0xc2bf) == 0x8: @@ -415,9 +394,9 @@ class crystal: Applies a sequence of buttons to the on-screen keyboard. """ for buttons in button_sequence: - press(buttons) + vba.press(buttons) nstep(2) - press([]) + vba.press([]) @staticmethod def write(something="TrAiNeR"): @@ -493,8 +472,8 @@ class crystal: """ Attempt to move the player. """ - press(cmd, holdsteps=10, aftersteps=0) - press([]) + vba.press(cmd, hold=10, after=0) + vba.press([]) memory = get_memory() #while memory[0xd4e1] == 2 and memory[0xd042] != 0x3e: -- cgit v1.2.3 From 115ce6781e2750d3b326e6a994677c80d0613029 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:52:44 -0500 Subject: use vba.step() instead of step() --- pokemontools/vba/vba.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index dbbe3c3..b2c164d 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -162,7 +162,7 @@ class crystal: break else: - nstep(step_size) + vba.step(count=step_size) # if there is a callback, then call the callback and exit when the # callback returns True. This is especially useful during the @@ -223,7 +223,7 @@ class crystal: for step_counter in range(0, steplimit): crystal.walk_through_walls() #call(0x1, 0x1078) - step() + vba.step() @staticmethod def disable_triggers(): @@ -395,7 +395,7 @@ class crystal: """ for buttons in button_sequence: vba.press(buttons) - nstep(2) + vba.step(count=2) vba.press([]) @staticmethod @@ -479,7 +479,7 @@ class crystal: #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) + vba.step(count=10) memory = get_memory() class TestEmulator(unittest.TestCase): @@ -492,7 +492,7 @@ class TestEmulator(unittest.TestCase): # what text to read from registers["de"] = 0x1276 - nstep(10) + vba.step(count=10) text = crystal.get_text() -- cgit v1.2.3 From 48896b14568aac35955baee6d5f47a5701f7825b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:53:34 -0500 Subject: replace one more step() with vba.step() --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index b2c164d..814c853 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -459,7 +459,7 @@ class crystal: print "script is done executing" return else: - step() + vba.step() if debug: limit = limit - 1 -- cgit v1.2.3 From 645cce62c8e7ea3784c7f493f7ad6413bcd64ba8 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:55:37 -0500 Subject: set_memory_at -> vba.write_memory_at The set_memory_at function was moved into vba_wrapper. There's no reason for that one to be defined in pokemontools. --- pokemontools/vba/vba.py | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 814c853..75b3aad 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -64,8 +64,8 @@ def call(bank, address): for value in push: registers.sp -= 2 - set_memory_at(registers.sp + 1, value >> 8) - set_memory_at(registers.sp, value & 0xFF) + vba.write_memory_at(registers.sp + 1, value >> 8) + vba.write_memory_at(registers.sp, value & 0xFF) if get_memory_range(registers.sp, 2) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) print "actual memory values: " + str(get_memory_range(registers.sp , 2)) @@ -125,7 +125,7 @@ class crystal: print "pressing, then breaking.. address is: " + str(hex(address)) # set CurSFX - set_memory_at(0xc2bf, 0) + vba.write_memory_at(0xc2bf, 0) vba.press("a", hold=10, after=1) @@ -204,14 +204,14 @@ class crystal: to be executed each step/tick if continuous walk-through-walls is desired. """ - set_memory_at(0xC2FA, 0) - set_memory_at(0xC2FB, 0) - set_memory_at(0xC2FC, 0) - set_memory_at(0xC2FD, 0) + vba.write_memory_at(0xC2FA, 0) + vba.write_memory_at(0xC2FB, 0) + vba.write_memory_at(0xC2FC, 0) + vba.write_memory_at(0xC2FD, 0) #@staticmethod #def set_enemy_level(level): - # set_memory_at(0xd213, level) + # vba.write_memory_at(0xd213, level) @staticmethod def nstep(steplimit=500): @@ -227,13 +227,13 @@ class crystal: @staticmethod def disable_triggers(): - set_memory_at(0x23c4, 0xAF) - set_memory_at(0x23d0, 0xAF); + vba.write_memory_at(0x23c4, 0xAF) + vba.write_memory_at(0x23d0, 0xAF); @staticmethod def disable_callbacks(): - set_memory_at(0x23f2, 0xAF) - set_memory_at(0x23fe, 0xAF) + vba.write_memory_at(0x23f2, 0xAF) + vba.write_memory_at(0x23fe, 0xAF) @staticmethod def get_map_group_id(): @@ -279,7 +279,7 @@ class crystal: This probably works on other menus. """ - set_memory_at(0xcfa9, id) + vba.write_memory_at(0xcfa9, id) @staticmethod def is_in_battle(): @@ -300,10 +300,10 @@ class crystal: Note: this might start at 0xDCA4 (minus one on all addresses), but not sure. """ - set_memory_at(0xDCA5, 0xFF) - set_memory_at(0xDCA6, 0xFF) - set_memory_at(0xDCA7, 0xFF) - set_memory_at(0xDCA8, 0xFF) + vba.write_memory_at(0xDCA5, 0xFF) + vba.write_memory_at(0xDCA6, 0xFF) + vba.write_memory_at(0xDCA7, 0xFF) + vba.write_memory_at(0xDCA8, 0xFF) @staticmethod def get_gender(): @@ -329,14 +329,14 @@ class crystal: @staticmethod def warp(map_group_id, map_id, x, y): - set_memory_at(0xdcb5, map_group_id) - set_memory_at(0xdcb6, map_id) - set_memory_at(0xdcb7, y) - set_memory_at(0xdcb8, x) - set_memory_at(0xd001, 0xFF) - set_memory_at(0xff9f, 0xF1) - set_memory_at(0xd432, 1) - set_memory_at(0xd434, 0 & 251) + vba.write_memory_at(0xdcb5, map_group_id) + vba.write_memory_at(0xdcb6, map_id) + vba.write_memory_at(0xdcb7, y) + vba.write_memory_at(0xdcb8, x) + vba.write_memory_at(0xd001, 0xFF) + vba.write_memory_at(0xff9f, 0xF1) + vba.write_memory_at(0xd432, 1) + vba.write_memory_at(0xd434, 0 & 251) @staticmethod def warp_pokecenter(): @@ -346,16 +346,16 @@ class crystal: @staticmethod def masterballs(): # masterball - set_memory_at(0xd8d8, 1) - set_memory_at(0xd8d9, 99) + vba.write_memory_at(0xd8d8, 1) + vba.write_memory_at(0xd8d9, 99) # ultraball - set_memory_at(0xd8da, 2) - set_memory_at(0xd8db, 99) + vba.write_memory_at(0xd8da, 2) + vba.write_memory_at(0xd8db, 99) # pokeballs - set_memory_at(0xd8dc, 5) - set_memory_at(0xd8dd, 99) + vba.write_memory_at(0xd8dc, 5) + vba.write_memory_at(0xd8dd, 99) @staticmethod def get_text(): -- cgit v1.2.3 From fecf601fd9fa912bcd9c374eb7db8901f9e6e791 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 15:57:24 -0500 Subject: get_memory_at -> vba.read_memory_at --- pokemontools/vba/vba.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 75b3aad..e0ebe61 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -69,7 +69,7 @@ def call(bank, address): if get_memory_range(registers.sp, 2) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) print "actual memory values: " + str(get_memory_range(registers.sp , 2)) - print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(get_memory_at(registers.sp)) + print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) if bank != 0: registers["af"] = (bank << 8) | (registers["af"] & 0xFF) @@ -87,8 +87,8 @@ def get_stack(): for x in range(0, 11): sp = sp - (2 * x) - hi = get_memory_at(sp + 1) - lo = get_memory_at(sp) + hi = vba.read_memory_at(sp + 1) + lo = vba.read_memory_at(sp) address = ((hi << 8) | lo) addresses.append(address) @@ -117,8 +117,8 @@ class crystal: :param max_wait: number of wait loops to perform """ while max_wait > 0: - hi = get_memory_at(registers.sp + 1) - lo = get_memory_at(registers.sp) + hi = vba.read_memory_at(registers.sp + 1) + lo = vba.read_memory_at(registers.sp) address = ((hi << 8) | lo) if address in range(0xa1b, 0xa46) + range(0xaaf, 0xaf5): # 0xaef: @@ -130,7 +130,7 @@ class crystal: vba.press("a", hold=10, after=1) # check if CurSFX is SFX_READ_TEXT_2 - if get_memory_at(0xc2bf) == 0x8: + if vba.read_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: @@ -169,7 +169,7 @@ class crystal: # 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". + # "vba.read_memory_at(0xcfb1) != 0". if callback != None: result = callback() if result == True: @@ -240,14 +240,14 @@ class crystal: """ Returns the current map group. """ - return get_memory_at(0xdcb5) + return vba.read_memory_at(0xdcb5) @staticmethod def get_map_id(): """ Returns the map number of the current map. """ - return get_memory_at(0xdcb6) + return vba.read_memory_at(0xdcb6) @staticmethod def get_map_name(): @@ -265,8 +265,8 @@ class crystal: Relative to top-left corner of map. """ - x = get_memory_at(0xdcb8) - y = get_memory_at(0xdcb7) + x = vba.read_memory_at(0xdcb8) + y = vba.read_memory_at(0xdcb7) return (x, y) @staticmethod @@ -286,11 +286,11 @@ class crystal: """ Checks whether or not we're in a battle. """ - return (get_memory_at(0xd22d) != 0) or crystal.is_in_link_battle() + return (vba.read_memory_at(0xd22d) != 0) or crystal.is_in_link_battle() @staticmethod def is_in_link_battle(): - return get_memory_at(0xc2dc) != 0 + return vba.read_memory_at(0xc2dc) != 0 @staticmethod def unlock_flypoints(): @@ -310,7 +310,7 @@ class crystal: """ Returns 'male' or 'female'. """ - gender = get_memory_at(0xD472) + gender = vba.read_memory_at(0xD472) if gender == 0: return "male" elif gender == 1: @@ -455,7 +455,7 @@ class crystal: Wait until ScriptRunning isn't -1. """ while limit > 0: - if get_memory_at(0xd438) != 255: + if vba.read_memory_at(0xd438) != 255: print "script is done executing" return else: -- cgit v1.2.3 From d29f193037cad147fe99aca6216a2e35346f5780 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 16:02:10 -0500 Subject: placeholder for get_memory_range But really, the old calls to get_memory_range should just be replaced with code that uses vba.memory[:] directly. --- pokemontools/vba/vba.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index e0ebe61..e107d90 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -40,6 +40,13 @@ registers = vba_wrapper.core.registers.Registers(vba) button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine +def get_memory_range(address, length): + """ + This is just a lame way to avoid converting some of the old + get_memory_range calls to use the vba.memory property. + """ + return list(vba.memory[address:address+length]) + def translate_chars(charz): result = "" for each in charz: -- cgit v1.2.3 From 55a436a1445027f7bf512cbf2b54392bbc0f99e0 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 16:07:51 -0500 Subject: fix some memory manipulation to use vba_wrapper --- pokemontools/vba/vba.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index e107d90..8fdff08 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -194,12 +194,12 @@ class crystal: @staticmethod def walk_through_walls_slow(): - memory = get_memory() + memory = vba.memory memory[0xC2FA] = 0 memory[0xC2FB] = 0 memory[0xC2FC] = 0 memory[0xC2FD] = 0 - set_memory(memory) + vba.memory = memory @staticmethod def walk_through_walls(): @@ -420,7 +420,7 @@ class crystal: """ This causes corruption, so it's not working yet. """ - memory = get_memory() + memory = vba.memory memory[0xdcd7] = 2 memory[0xdcd9] = 0x7 @@ -454,7 +454,7 @@ class crystal: memory[0xdd33] = 0x10 memory[0xdd34] = 0x40 - set_memory(memory) + vba.memory = memory @staticmethod def wait_for_script_running(debug=False, limit=1000): @@ -482,12 +482,12 @@ class crystal: vba.press(cmd, hold=10, after=0) vba.press([]) - memory = get_memory() + memory = vba.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: vba.step(count=10) - memory = get_memory() + memory = vba.memory class TestEmulator(unittest.TestCase): def test_PlaceString(self): -- cgit v1.2.3 From 7f5151989b504d9561f041aaacff9916a9c68568 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:29:52 -0500 Subject: pass vba into get_memory_range --- pokemontools/vba/vba.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 8fdff08..b403236 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -40,7 +40,7 @@ registers = vba_wrapper.core.registers.Registers(vba) button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine -def get_memory_range(address, length): +def get_memory_range(vba, address, length): """ This is just a lame way to avoid converting some of the old get_memory_range calls to use the vba.memory property. @@ -73,9 +73,9 @@ def call(bank, address): registers.sp -= 2 vba.write_memory_at(registers.sp + 1, value >> 8) vba.write_memory_at(registers.sp, value & 0xFF) - if get_memory_range(registers.sp, 2) != [value & 0xFF, value >> 8]: + if get_memory_range(vba, registers.sp, 2) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) - print "actual memory values: " + str(get_memory_range(registers.sp , 2)) + print "actual memory values: " + str(get_memory_range(vba, registers.sp, 2)) print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) if bank != 0: @@ -330,7 +330,7 @@ class crystal: """ Returns the 7 characters making up the player's name. """ - bytez = get_memory_range(0xD47D, 7) + bytez = get_memory_range(vba, 0xD47D, 7) name = translate_chars(bytez) return name @@ -372,7 +372,7 @@ class crystal: Other characters will not be shown. """ output = "" - tiles = get_memory_range(0xc4a0, 1000) + tiles = get_memory_range(vba, 0xc4a0, 1000) for each in tiles: if each in chars.keys(): thing = chars[each] -- cgit v1.2.3 From 975eb021754c3a1ad6a5aa447358212564382351 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:31:44 -0500 Subject: pass vba and registers into call() --- pokemontools/vba/vba.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index b403236..f6a0f7b 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -53,7 +53,7 @@ def translate_chars(charz): result += chars[each] return result -def call(bank, address): +def call(bank, address, vba=vba, registers=registers): """ Jumps into a function at a certain address. @@ -491,7 +491,7 @@ class crystal: class TestEmulator(unittest.TestCase): def test_PlaceString(self): - call(0, 0x1078) + call(vba, 0, 0x1078) # where to draw the text registers["hl"] = 0xc4a0 -- cgit v1.2.3 From fc3b2ded897e9bb01180a2f5ea91b6f3ca037c09 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:32:15 -0500 Subject: pass vba and registers into get_stack --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index f6a0f7b..eafbaa1 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -85,7 +85,7 @@ def call(bank, address, vba=vba, registers=registers): else: registers["pc"] = address -def get_stack(): +def get_stack(vba=vba, registers=registers): """ Return a list of functions on the stack. """ -- cgit v1.2.3 From 2f9df7133f39b286afe222f635be32c0699c87bf Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:34:46 -0500 Subject: move get_stack into vba.crystal --- pokemontools/vba/vba.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index eafbaa1..a4c7e27 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -85,26 +85,27 @@ def call(bank, address, vba=vba, registers=registers): else: registers["pc"] = address -def get_stack(vba=vba, registers=registers): +class crystal: """ - Return a list of functions on the stack. + Just a simple namespace to store a bunch of functions for Pokémon Crystal. """ - addresses = [] - sp = registers.sp - for x in range(0, 11): - sp = sp - (2 * x) - hi = vba.read_memory_at(sp + 1) - lo = vba.read_memory_at(sp) - address = ((hi << 8) | lo) - addresses.append(address) + @staticmethod + def get_stack(vba=vba, registers=registers): + """ + Return a list of functions on the stack. + """ + addresses = [] + sp = registers.sp - return addresses + for x in range(0, 11): + sp = sp - (2 * x) + hi = vba.read_memory_at(sp + 1) + lo = vba.read_memory_at(sp) + address = ((hi << 8) | lo) + addresses.append(address) -class crystal: - """ - Just a simple namespace to store a bunch of functions for Pokémon Crystal. - """ + return addresses @staticmethod def text_wait(step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None): @@ -150,7 +151,7 @@ class crystal: break else: - stack = get_stack() + stack = crystal.get_stack() # yes/no box or the name selection box if address in range(0xa46, 0xaaf): -- cgit v1.2.3 From b1c34e88d51a0d1f45659c75920a58e997346e54 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:41:41 -0500 Subject: move call into vba.crystal --- pokemontools/vba/vba.py | 67 +++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 33 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index a4c7e27..e206d8a 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -53,43 +53,44 @@ def translate_chars(charz): result += chars[each] return result -def call(bank, address, vba=vba, registers=registers): - """ - Jumps into a function at a certain address. - - Go into the start menu, pause the game and try call(1, 0x1078) to see a - string printed to the screen. - """ - push = [ - registers.pc, - registers.hl, - registers.de, - registers.bc, - registers.af, - 0x3bb7, - ] - - for value in push: - registers.sp -= 2 - vba.write_memory_at(registers.sp + 1, value >> 8) - vba.write_memory_at(registers.sp, value & 0xFF) - if get_memory_range(vba, registers.sp, 2) != [value & 0xFF, value >> 8]: - print "desired memory values: " + str([value & 0xFF, value >> 8] ) - print "actual memory values: " + str(get_memory_range(vba, registers.sp, 2)) - print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) - - if bank != 0: - registers["af"] = (bank << 8) | (registers["af"] & 0xFF) - registers["hl"] = address - registers["pc"] = 0x2d63 # FarJump - else: - registers["pc"] = address - class crystal: """ Just a simple namespace to store a bunch of functions for Pokémon Crystal. """ + @staticmethod + def call(bank, address, vba=vba, registers=registers): + """ + Jumps into a function at a certain address. + + Go into the start menu, pause the game and try call(1, 0x1078) to see a + string printed to the screen. + """ + push = [ + registers.pc, + registers.hl, + registers.de, + registers.bc, + registers.af, + 0x3bb7, + ] + + for value in push: + registers.sp -= 2 + vba.write_memory_at(registers.sp + 1, value >> 8) + vba.write_memory_at(registers.sp, value & 0xFF) + if get_memory_range(vba, registers.sp, 2) != [value & 0xFF, value >> 8]: + print "desired memory values: " + str([value & 0xFF, value >> 8] ) + print "actual memory values: " + str(get_memory_range(vba, registers.sp, 2)) + print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) + + if bank != 0: + registers["af"] = (bank << 8) | (registers["af"] & 0xFF) + registers["hl"] = address + registers["pc"] = 0x2d63 # FarJump + else: + registers["pc"] = address + @staticmethod def get_stack(vba=vba, registers=registers): """ @@ -492,7 +493,7 @@ class crystal: class TestEmulator(unittest.TestCase): def test_PlaceString(self): - call(vba, 0, 0x1078) + crystal.call(0, 0x1078) # where to draw the text registers["hl"] = 0xc4a0 -- cgit v1.2.3 From 8ca4bcf37e5183455c9cc23e385597b92b4e6d29 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 19:45:35 -0500 Subject: remove get_memory_range --- pokemontools/vba/vba.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index e206d8a..7d6a588 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -40,13 +40,6 @@ registers = vba_wrapper.core.registers.Registers(vba) button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine -def get_memory_range(vba, address, length): - """ - This is just a lame way to avoid converting some of the old - get_memory_range calls to use the vba.memory property. - """ - return list(vba.memory[address:address+length]) - def translate_chars(charz): result = "" for each in charz: @@ -79,9 +72,9 @@ class crystal: registers.sp -= 2 vba.write_memory_at(registers.sp + 1, value >> 8) vba.write_memory_at(registers.sp, value & 0xFF) - if get_memory_range(vba, registers.sp, 2) != [value & 0xFF, value >> 8]: + if list(vba.memory[registers.sp : registers.sp + 2]) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) - print "actual memory values: " + str(get_memory_range(vba, registers.sp, 2)) + print "actual memory values: " + str(list(vba.memory[registers.sp : registers.sp + 2])) print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) if bank != 0: @@ -332,7 +325,7 @@ class crystal: """ Returns the 7 characters making up the player's name. """ - bytez = get_memory_range(vba, 0xD47D, 7) + bytez = vba.memory[0xD47D:0xD47D + 7] name = translate_chars(bytez) return name @@ -374,7 +367,7 @@ class crystal: Other characters will not be shown. """ output = "" - tiles = get_memory_range(vba, 0xc4a0, 1000) + tiles = vba.memory[0xc4a0:0xc4a0 + 1000] for each in tiles: if each in chars.keys(): thing = chars[each] -- cgit v1.2.3 From 7fdf53f162dc8027619803b36d4447cdc2f294bd Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 21:24:38 -0500 Subject: switch vba.crystal to have instance methods --- pokemontools/vba/vba.py | 279 ++++++++++++++++++++++-------------------------- 1 file changed, 128 insertions(+), 151 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 7d6a588..7e3f0a7 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -23,20 +23,10 @@ from pokemontools.map_names import ( import keyboard # just use a default config for now until the globals are removed completely -import pokemontools.config as conf -config = conf.Config() -project_path = config.path -save_state_path = config.save_state_path -rom_path = config.rom_path - -if not os.path.exists(rom_path): - raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) +import pokemontools.configuration as configuration import vba_wrapper -vba = vba_wrapper.VBA(rom_path) -registers = vba_wrapper.core.registers.Registers(vba) - button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine @@ -46,13 +36,26 @@ def translate_chars(charz): result += chars[each] return result -class crystal: +class crystal(object): """ Just a simple namespace to store a bunch of functions for Pokémon Crystal. + There can only be one running instance of the emulator per process because + it's a poorly written shared library. """ - @staticmethod - def call(bank, address, vba=vba, registers=registers): + def __init__(self): + """ + Launch the VBA controller. + """ + self.config = configuration.Config() + + self.vba = vba_wrapper.VBA(self.config.rom_path) + self.registers = vba_wrapper.core.registers.Registers(self.vba) + + if not os.path.exists(rom_path): + raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) + + def call(self, bank, address): """ Jumps into a function at a certain address. @@ -60,49 +63,47 @@ class crystal: string printed to the screen. """ push = [ - registers.pc, - registers.hl, - registers.de, - registers.bc, - registers.af, + self.registers.pc, + self.registers.hl, + self.registers.de, + self.registers.bc, + self.registers.af, 0x3bb7, ] for value in push: - registers.sp -= 2 - vba.write_memory_at(registers.sp + 1, value >> 8) - vba.write_memory_at(registers.sp, value & 0xFF) - if list(vba.memory[registers.sp : registers.sp + 2]) != [value & 0xFF, value >> 8]: + self.registers.sp -= 2 + self.vba.write_memory_at(registers.sp + 1, value >> 8) + self.vba.write_memory_at(registers.sp, value & 0xFF) + if list(self.vba.memory[self.registers.sp : self.registers.sp + 2]) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) - print "actual memory values: " + str(list(vba.memory[registers.sp : registers.sp + 2])) - print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(vba.read_memory_at(registers.sp)) + print "actual memory values: " + str(list(self.vba.memory[self.registers.sp : self.registers.sp + 2])) + print "wrong value at " + hex(self.registers.sp) + " expected " + hex(value) + " but got " + hex(self.vba.read_memory_at(self.registers.sp)) if bank != 0: - registers["af"] = (bank << 8) | (registers["af"] & 0xFF) - registers["hl"] = address - registers["pc"] = 0x2d63 # FarJump + self.registers["af"] = (bank << 8) | (self.registers["af"] & 0xFF) + self.registers["hl"] = address + self.registers["pc"] = 0x2d63 # FarJump else: - registers["pc"] = address + self.registers["pc"] = address - @staticmethod - def get_stack(vba=vba, registers=registers): + def get_stack(self): """ Return a list of functions on the stack. """ addresses = [] - sp = registers.sp + sp = self.registers.sp for x in range(0, 11): sp = sp - (2 * x) - hi = vba.read_memory_at(sp + 1) - lo = vba.read_memory_at(sp) + hi = self.vba.read_memory_at(sp + 1) + lo = self.vba.read_memory_at(sp) address = ((hi << 8) | lo) addresses.append(address) return addresses - @staticmethod - def text_wait(step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None): + def text_wait(self, 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. @@ -119,22 +120,22 @@ class crystal: :param max_wait: number of wait loops to perform """ while max_wait > 0: - hi = vba.read_memory_at(registers.sp + 1) - lo = vba.read_memory_at(registers.sp) + hi = self.vba.read_memory_at(registers.sp + 1) + lo = self.vba.read_memory_at(registers.sp) address = ((hi << 8) | lo) if address in range(0xa1b, 0xa46) + range(0xaaf, 0xaf5): # 0xaef: print "pressing, then breaking.. address is: " + str(hex(address)) # set CurSFX - vba.write_memory_at(0xc2bf, 0) + self.vba.write_memory_at(0xc2bf, 0) - vba.press("a", hold=10, after=1) + self.vba.press("a", hold=10, after=1) # check if CurSFX is SFX_READ_TEXT_2 - if vba.read_memory_at(0xc2bf) == 0x8: + if self.vba.read_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) + return self.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 @@ -145,7 +146,7 @@ class crystal: break else: - stack = crystal.get_stack() + stack = self.get_stack() # yes/no box or the name selection box if address in range(0xa46, 0xaaf): @@ -164,7 +165,7 @@ class crystal: break else: - vba.step(count=step_size) + self.vba.step(count=step_size) # if there is a callback, then call the callback and exit when the # callback returns True. This is especially useful during the @@ -187,17 +188,15 @@ class crystal: if max_wait == 0: print "max_wait was hit" - @staticmethod - def walk_through_walls_slow(): - memory = vba.memory + def walk_through_walls_slow(self): + memory = self.vba.memory memory[0xC2FA] = 0 memory[0xC2FB] = 0 memory[0xC2FC] = 0 memory[0xC2FD] = 0 - vba.memory = memory + self.vba.memory = memory - @staticmethod - def walk_through_walls(): + def walk_through_walls(self): """ Lets the player walk all over the map. @@ -206,73 +205,65 @@ class crystal: to be executed each step/tick if continuous walk-through-walls is desired. """ - vba.write_memory_at(0xC2FA, 0) - vba.write_memory_at(0xC2FB, 0) - vba.write_memory_at(0xC2FC, 0) - vba.write_memory_at(0xC2FD, 0) + self.vba.write_memory_at(0xC2FA, 0) + self.vba.write_memory_at(0xC2FB, 0) + self.vba.write_memory_at(0xC2FC, 0) + self.vba.write_memory_at(0xC2FD, 0) #@staticmethod #def set_enemy_level(level): # vba.write_memory_at(0xd213, level) - @staticmethod - def nstep(steplimit=500): + def nstep(self, steplimit=500): """ Steps the CPU forward and calls some functions in between each step. (For example, to manipulate memory.) This is pretty slow. """ for step_counter in range(0, steplimit): - crystal.walk_through_walls() + self.walk_through_walls() #call(0x1, 0x1078) - vba.step() + self.vba.step() - @staticmethod - def disable_triggers(): - vba.write_memory_at(0x23c4, 0xAF) - vba.write_memory_at(0x23d0, 0xAF); + def disable_triggers(self): + self.vba.write_memory_at(0x23c4, 0xAF) + self.vba.write_memory_at(0x23d0, 0xAF); - @staticmethod - def disable_callbacks(): - vba.write_memory_at(0x23f2, 0xAF) - vba.write_memory_at(0x23fe, 0xAF) + def disable_callbacks(self): + self.vba.write_memory_at(0x23f2, 0xAF) + self.vba.write_memory_at(0x23fe, 0xAF) - @staticmethod - def get_map_group_id(): + def get_map_group_id(self): """ Returns the current map group. """ - return vba.read_memory_at(0xdcb5) + return self.vba.read_memory_at(0xdcb5) - @staticmethod - def get_map_id(): + def get_map_id(self): """ Returns the map number of the current map. """ - return vba.read_memory_at(0xdcb6) + return self.vba.read_memory_at(0xdcb6) - @staticmethod - def get_map_name(): + def get_map_name(self, map_names=map_names): """ Figures out the current map name. """ - map_group_id = crystal.get_map_group_id() - map_id = crystal.get_map_id() + map_group_id = self.get_map_group_id() + map_id = self.get_map_id() return map_names[map_group_id][map_id]["name"] - @staticmethod - def get_xy(): + def get_xy(self): """ (x, y) coordinates of player on map. Relative to top-left corner of map. """ - x = vba.read_memory_at(0xdcb8) - y = vba.read_memory_at(0xdcb7) + x = self.vba.read_memory_at(0xdcb8) + y = self.vba.read_memory_at(0xdcb7) return (x, y) - @staticmethod - def menu_select(id=1): + def menu_select(self, id=1): """ Sets the cursor to the given pokemon in the player's party. @@ -281,38 +272,34 @@ class crystal: This probably works on other menus. """ - vba.write_memory_at(0xcfa9, id) + self.vba.write_memory_at(0xcfa9, id) - @staticmethod - def is_in_battle(): + def is_in_battle(self): """ Checks whether or not we're in a battle. """ - return (vba.read_memory_at(0xd22d) != 0) or crystal.is_in_link_battle() + return (self.vba.read_memory_at(0xd22d) != 0) or self.is_in_link_battle() - @staticmethod - def is_in_link_battle(): - return vba.read_memory_at(0xc2dc) != 0 + def is_in_link_battle(self): + return self.vba.read_memory_at(0xc2dc) != 0 - @staticmethod - def unlock_flypoints(): + def unlock_flypoints(self): """ Unlocks different destinations for flying. Note: this might start at 0xDCA4 (minus one on all addresses), but not sure. """ - vba.write_memory_at(0xDCA5, 0xFF) - vba.write_memory_at(0xDCA6, 0xFF) - vba.write_memory_at(0xDCA7, 0xFF) - vba.write_memory_at(0xDCA8, 0xFF) + self.vba.write_memory_at(0xDCA5, 0xFF) + self.vba.write_memory_at(0xDCA6, 0xFF) + self.vba.write_memory_at(0xDCA7, 0xFF) + self.vba.write_memory_at(0xDCA8, 0xFF) - @staticmethod - def get_gender(): + def get_gender(self): """ Returns 'male' or 'female'. """ - gender = vba.read_memory_at(0xD472) + gender = self.vba.read_memory_at(0xD472) if gender == 0: return "male" elif gender == 1: @@ -320,54 +307,49 @@ class crystal: else: return gender - @staticmethod - def get_player_name(): + def get_player_name(self): """ Returns the 7 characters making up the player's name. """ - bytez = vba.memory[0xD47D:0xD47D + 7] + bytez = self.vba.memory[0xD47D:0xD47D + 7] name = translate_chars(bytez) return name - @staticmethod - def warp(map_group_id, map_id, x, y): - vba.write_memory_at(0xdcb5, map_group_id) - vba.write_memory_at(0xdcb6, map_id) - vba.write_memory_at(0xdcb7, y) - vba.write_memory_at(0xdcb8, x) - vba.write_memory_at(0xd001, 0xFF) - vba.write_memory_at(0xff9f, 0xF1) - vba.write_memory_at(0xd432, 1) - vba.write_memory_at(0xd434, 0 & 251) - - @staticmethod - def warp_pokecenter(): - crystal.warp(1, 1, 3, 3) - crystal.nstep(200) - - @staticmethod - def masterballs(): + def warp(self, map_group_id, map_id, x, y): + self.vba.write_memory_at(0xdcb5, map_group_id) + self.vba.write_memory_at(0xdcb6, map_id) + self.vba.write_memory_at(0xdcb7, y) + self.vba.write_memory_at(0xdcb8, x) + self.vba.write_memory_at(0xd001, 0xFF) + self.vba.write_memory_at(0xff9f, 0xF1) + self.vba.write_memory_at(0xd432, 1) + self.vba.write_memory_at(0xd434, 0 & 251) + + def warp_pokecenter(self): + self.warp(1, 1, 3, 3) + self.nstep(200) + + def masterballs(self): # masterball - vba.write_memory_at(0xd8d8, 1) - vba.write_memory_at(0xd8d9, 99) + self.vba.write_memory_at(0xd8d8, 1) + self.vba.write_memory_at(0xd8d9, 99) # ultraball - vba.write_memory_at(0xd8da, 2) - vba.write_memory_at(0xd8db, 99) + self.vba.write_memory_at(0xd8da, 2) + self.vba.write_memory_at(0xd8db, 99) # pokeballs - vba.write_memory_at(0xd8dc, 5) - vba.write_memory_at(0xd8dd, 99) + self.vba.write_memory_at(0xd8dc, 5) + self.vba.write_memory_at(0xd8dd, 99) - @staticmethod - def get_text(): + def get_text(self, chars=chars): """ Returns alphanumeric text on the screen. Other characters will not be shown. """ output = "" - tiles = vba.memory[0xc4a0:0xc4a0 + 1000] + tiles = self.vba.memory[0xc4a0:0xc4a0 + 1000] for each in tiles: if each in chars.keys(): thing = chars[each] @@ -390,32 +372,29 @@ class crystal: return output - @staticmethod - def keyboard_apply(button_sequence): + def keyboard_apply(self, button_sequence): """ Applies a sequence of buttons to the on-screen keyboard. """ for buttons in button_sequence: - vba.press(buttons) - vba.step(count=2) - vba.press([]) + self.vba.press(buttons) + self.vba.step(count=2) + self.vba.press([]) - @staticmethod - def write(something="TrAiNeR"): + def write(self, something="TrAiNeR"): """ Types out a word. Uses a planning algorithm to do this in the most efficient way possible. """ button_sequence = keyboard.plan_typing(something) - crystal.keyboard_apply([[x] for x in button_sequence]) + self.keyboard_apply([[x] for x in button_sequence]) - @staticmethod - def set_partymon2(): + def set_partymon2(self): """ This causes corruption, so it's not working yet. """ - memory = vba.memory + memory = self.vba.memory memory[0xdcd7] = 2 memory[0xdcd9] = 0x7 @@ -449,19 +428,18 @@ class crystal: memory[0xdd33] = 0x10 memory[0xdd34] = 0x40 - vba.memory = memory + self.vba.memory = memory - @staticmethod - def wait_for_script_running(debug=False, limit=1000): + def wait_for_script_running(self, debug=False, limit=1000): """ Wait until ScriptRunning isn't -1. """ while limit > 0: - if vba.read_memory_at(0xd438) != 255: + if self.vba.read_memory_at(0xd438) != 255: print "script is done executing" return else: - vba.step() + self.vba.step() if debug: limit = limit - 1 @@ -469,20 +447,19 @@ class crystal: if limit == 0: print "limit ran out" - @staticmethod - def move(cmd): + def move(self, cmd): """ Attempt to move the player. """ - vba.press(cmd, hold=10, after=0) - vba.press([]) + self.vba.press(cmd, hold=10, after=0) + self.vba.press([]) - memory = vba.memory + memory = self.vba.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: - vba.step(count=10) - memory = vba.memory + self.vba.step(count=10) + memory = self.vba.memory class TestEmulator(unittest.TestCase): def test_PlaceString(self): -- cgit v1.2.3 From e3a596e7d8c9de64162da2e0a05d5b17942d4694 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 22:12:37 -0500 Subject: fix a vba.py test (test_PlaceString) --- pokemontools/vba/vba.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 7e3f0a7..b775970 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -462,18 +462,21 @@ class crystal(object): memory = self.vba.memory class TestEmulator(unittest.TestCase): + def setUp(self): + self.cry = crystal() + def test_PlaceString(self): - crystal.call(0, 0x1078) + self.cry.call(0, 0x1078) # where to draw the text - registers["hl"] = 0xc4a0 + self.cry.registers["hl"] = 0xc4a0 # what text to read from - registers["de"] = 0x1276 + self.cry.registers["de"] = 0x1276 - vba.step(count=10) + self.cry.vba.step(count=10) - text = crystal.get_text() + text = self.cry.get_text() self.assertTrue("TRAINER" in text) -- cgit v1.2.3 From 2a439694d6af7416732b76fe37e8ea9fe0e9faff Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 23:12:38 -0500 Subject: combine some vba tests --- pokemontools/vba/vba.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index b775970..a308ea8 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -52,7 +52,7 @@ class crystal(object): self.vba = vba_wrapper.VBA(self.config.rom_path) self.registers = vba_wrapper.core.registers.Registers(self.vba) - if not os.path.exists(rom_path): + if not os.path.exists(self.config.rom_path): raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) def call(self, bank, address): @@ -73,8 +73,8 @@ class crystal(object): for value in push: self.registers.sp -= 2 - self.vba.write_memory_at(registers.sp + 1, value >> 8) - self.vba.write_memory_at(registers.sp, value & 0xFF) + self.vba.write_memory_at(self.registers.sp + 1, value >> 8) + self.vba.write_memory_at(self.registers.sp, value & 0xFF) if list(self.vba.memory[self.registers.sp : self.registers.sp + 2]) != [value & 0xFF, value >> 8]: print "desired memory values: " + str([value & 0xFF, value >> 8] ) print "actual memory values: " + str(list(self.vba.memory[self.registers.sp : self.registers.sp + 2])) @@ -462,8 +462,12 @@ class crystal(object): memory = self.vba.memory class TestEmulator(unittest.TestCase): - def setUp(self): - self.cry = crystal() + @classmethod + def setUpClass(cls): + cls.cry = crystal() + + # advance it forward past the intro sequences + cls.cry.vba.step(count=3500) def test_PlaceString(self): self.cry.call(0, 0x1078) @@ -480,8 +484,7 @@ class TestEmulator(unittest.TestCase): self.assertTrue("TRAINER" in text) -class TestWriter(unittest.TestCase): - def test_very_basic(self): + def test_keyboard_planner(self): button_sequence = keyboard.plan_typing("an") expected_result = ["select", "a", "d", "r", "r", "r", "r", "a"] -- cgit v1.2.3 From f0e75972a119812ec37ec27fcdcd00afc45edf98 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 21 Sep 2013 23:14:58 -0500 Subject: move tests into test_vba.py They didn't belong in pokemontools/vba/vba.py in the first place. --- pokemontools/vba/vba.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index a308ea8..863e16c 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -9,8 +9,6 @@ import re import string from copy import copy -import unittest - # for converting bytes to readable text from pokemontools.chars import ( chars, @@ -460,36 +458,3 @@ class crystal(object): #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e: self.vba.step(count=10) memory = self.vba.memory - -class TestEmulator(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.cry = crystal() - - # advance it forward past the intro sequences - cls.cry.vba.step(count=3500) - - def test_PlaceString(self): - self.cry.call(0, 0x1078) - - # where to draw the text - self.cry.registers["hl"] = 0xc4a0 - - # what text to read from - self.cry.registers["de"] = 0x1276 - - self.cry.vba.step(count=10) - - text = self.cry.get_text() - - self.assertTrue("TRAINER" in text) - - def test_keyboard_planner(self): - button_sequence = keyboard.plan_typing("an") - expected_result = ["select", "a", "d", "r", "r", "r", "r", "a"] - - self.assertEqual(len(expected_result), len(button_sequence)) - self.assertEqual(expected_result, button_sequence) - -if __name__ == "__main__": - unittest.main() -- cgit v1.2.3 From 6b56969ccf00262187071e95568cee89f637692b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 00:44:35 -0500 Subject: make vba.crystal accept config --- pokemontools/vba/vba.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 863e16c..d821999 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -41,11 +41,14 @@ class crystal(object): it's a poorly written shared library. """ - def __init__(self): + def __init__(self, config=None): """ Launch the VBA controller. """ - self.config = configuration.Config() + if not config: + config = configuration.Config() + + self.config = config self.vba = vba_wrapper.VBA(self.config.rom_path) self.registers = vba_wrapper.core.registers.Registers(self.vba) -- cgit v1.2.3 From dc63d8d51dd451b13fb5e1386edfc7bac0874ee6 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 00:49:44 -0500 Subject: re-implement save_state This can be used to dump state to a file based on the current configuration of the running instance. --- pokemontools/vba/vba.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index d821999..d8ceebb 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -56,6 +56,27 @@ class crystal(object): if not os.path.exists(self.config.rom_path): raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) + def save_state(self, name, state=None, override=False): + """ + Saves the given state to save_state_path. + + The file format must be ".sav", and this will be appended to your + string if necessary. + """ + if state == None: + state = self.vba.state + + if len(name) < 4 or name[-4:] != ".sav": + name += ".sav" + + save_path = os.path.join(self.config.save_state_path, name) + + if not override and os.path.exists(save_path): + raise Exception("oops, save state path already exists: {0}".format(save_path)) + + with open(save_path, "wb") as file_handler: + file_handler.write(state) + def call(self, bank, address): """ Jumps into a function at a certain address. -- cgit v1.2.3 From 8e0a4f922906a118bc0a7bcc2e8680c6ba9060df Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 01:01:03 -0500 Subject: use self.registers in text_wait --- pokemontools/vba/vba.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index d8ceebb..0818b93 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -142,8 +142,8 @@ class crystal(object): :param max_wait: number of wait loops to perform """ while max_wait > 0: - hi = self.vba.read_memory_at(registers.sp + 1) - lo = self.vba.read_memory_at(registers.sp) + hi = self.vba.read_memory_at(self.registers.sp + 1) + lo = self.vba.read_memory_at(self.registers.sp) address = ((hi << 8) | lo) if address in range(0xa1b, 0xa46) + range(0xaaf, 0xaf5): # 0xaef: -- cgit v1.2.3 From 7bfbadc5687a1eb297db0ff5956478a8bd38178f Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 01:10:46 -0500 Subject: implement a vba helper func for state loading This re-implements the load_state method that previously existed. I forget why it was removed, but basically a similar function is needed again, and it doesn't entirely belong in the emulator or in the emulator wrapper because these save states are game-specific. --- pokemontools/vba/vba.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 0818b93..7dc0d8f 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -77,6 +77,30 @@ class crystal(object): with open(save_path, "wb") as file_handler: file_handler.write(state) + def load_state(self, name, loadit=True): + """ + Read a state from file based on the name of the state. + + Looks in save_state_path for a file with this name (".sav" is + optional). + + @param loadit: whether or not to set the emulator to this state + """ + save_path = os.path.join(self.config.save_state_path, name) + + if not os.path.exists(save_path): + if len(name) < 4 or name[-4:] != ".sav": + name += ".sav" + save_path = os.path.join(save_state_path, name) + + with open(save_path, "rb") as file_handler: + state = file_handler.read() + + if loadit: + self.vba.state = state + + return state + def call(self, bank, address): """ Jumps into a function at a certain address. -- cgit v1.2.3 From bf20b9982b19535044a7d40848bd46a04731694e Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 01:12:55 -0500 Subject: save_state_path is only on self.config --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 7dc0d8f..1b33ba7 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -91,7 +91,7 @@ class crystal(object): if not os.path.exists(save_path): if len(name) < 4 or name[-4:] != ".sav": name += ".sav" - save_path = os.path.join(save_state_path, name) + save_path = os.path.join(self.config.save_state_path, name) with open(save_path, "rb") as file_handler: state = file_handler.read() -- cgit v1.2.3 From a3bab14b2657582675b2a94c99e55ae3148e93eb Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 01:28:06 -0500 Subject: weird, why was there no shutdown command? --- pokemontools/vba/vba.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 1b33ba7..3c8c084 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -56,6 +56,12 @@ class crystal(object): if not os.path.exists(self.config.rom_path): raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) + def shutdown(self): + """ + Reset the emulator. + """ + self.vba.shutdown() + def save_state(self, name, state=None, override=False): """ Saves the given state to save_state_path. -- cgit v1.2.3 From 6e4c7d5a0f1e5d416b204e83348df3a619631d8f Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 12:14:49 -0500 Subject: pause a few frames before typing on a keyboard For names that weren't starting with a capletter, the "select" button to switch to downcase was happening too soon. So add in a small delay to get the keyboard writing to work. --- pokemontools/vba/vba.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 3c8c084..654292e 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -430,7 +430,12 @@ class crystal(object): """ for buttons in button_sequence: self.vba.press(buttons) - self.vba.step(count=2) + + if buttons == "select": + self.vba.step(count=5) + else: + self.vba.step(count=2) + self.vba.press([]) def write(self, something="TrAiNeR"): @@ -440,7 +445,9 @@ class crystal(object): Uses a planning algorithm to do this in the most efficient way possible. """ button_sequence = keyboard.plan_typing(something) + self.vba.step(count=10) self.keyboard_apply([[x] for x in button_sequence]) + return button_sequence def set_partymon2(self): """ -- cgit v1.2.3 From 3c4207d777a31914eeb76fb19e9ddd4ac34575f0 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 22 Sep 2013 17:26:56 -0500 Subject: basic level-up stats screen detection --- pokemontools/vba/vba.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 654292e..ce4bef2 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -424,6 +424,39 @@ class crystal(object): return output + def is_showing_stats_screen(self): + """ + This is meant to detect whether or not the stats screen is showing. + This is the menu that pops up after leveling up. + """ + # These words must be on the screen if the stats screen is currently + # displayed. + parts = [ + "ATTACK", + "DEFENSE", + "SPCL.ATK", + "SPCL.DEF", + "SPEED", + ] + + # get the current text on the screen + text = self.get_text() + + if all([part in text for part in parts]): + return True + else: + return False + + def handle_stats_screen(self, force=False): + """ + Attempts to bypass a stats screen. Set force=True if you want to make + the attempt regardless of whether or not the system thinks a stats + screen is showing. + """ + if self.is_showing_stats_screen() or force: + self.vba.press("a") + self.vba.step(count=20) + def keyboard_apply(self, button_sequence): """ Applies a sequence of buttons to the on-screen keyboard. -- cgit v1.2.3 From c2d13dab14153c405657471d44e49d83f36e00e6 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 12 Oct 2013 16:17:57 -0500 Subject: write get_enemy_hp to calculate current hp --- pokemontools/vba/vba.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index ce4bef2..433df40 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -552,3 +552,10 @@ class crystal(object): #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e: self.vba.step(count=10) memory = self.vba.memory + + def get_enemy_hp(self): + """ + Returns the HP of the current enemy. + """ + hp = ((self.cry.vba.memory[0xd218] << 8) | self.cry.vba.memory[0xd217]) + return hp -- cgit v1.2.3 From de5a585e5e1f1829ee26bca6e5b7d8aa9dceee25 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 12 Oct 2013 16:18:52 -0500 Subject: oops, made a mistake in get_enemy_hp --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 433df40..007c132 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -557,5 +557,5 @@ class crystal(object): """ Returns the HP of the current enemy. """ - hp = ((self.cry.vba.memory[0xd218] << 8) | self.cry.vba.memory[0xd217]) + hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217]) return hp -- cgit v1.2.3 From 49c51e03d059bb5a93b4dcd494bd145f5f96428a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 12 Oct 2013 16:31:05 -0500 Subject: even more docstrings --- pokemontools/vba/vba.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 007c132..e30616f 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -368,6 +368,9 @@ class crystal(object): return name def warp(self, map_group_id, map_id, x, y): + """ + Warp into another map. + """ self.vba.write_memory_at(0xdcb5, map_group_id) self.vba.write_memory_at(0xdcb6, map_id) self.vba.write_memory_at(0xdcb7, y) @@ -378,10 +381,17 @@ class crystal(object): self.vba.write_memory_at(0xd434, 0 & 251) def warp_pokecenter(self): + """ + Warp straight into a pokecenter. + """ self.warp(1, 1, 3, 3) self.nstep(200) def masterballs(self): + """ + Deposit some pokeballs into the first few slots of the pack. This + overrides whatever items were previously there. + """ # masterball self.vba.write_memory_at(0xd8d8, 1) self.vba.write_memory_at(0xd8d9, 99) -- cgit v1.2.3 From e13cdeab1104260e2dcfd04aa9dbaa5688f357d0 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 12 Oct 2013 16:33:52 -0500 Subject: another minor docstring --- pokemontools/vba/vba.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index e30616f..81b27a3 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -29,6 +29,10 @@ button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine def translate_chars(charz): + """ + Translate a string from the in-game format to readable form. This is + accomplished through the same lookup table that the preprocessors use. + """ result = "" for each in charz: result += chars[each] -- cgit v1.2.3 From 5fdd27030e0268b6fa494b9219d3a1ac8c2a35cb Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 12 Oct 2013 16:41:50 -0500 Subject: move() can now take a list of movements to make --- pokemontools/vba/vba.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 81b27a3..81ffe4e 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -557,15 +557,19 @@ class crystal(object): """ Attempt to move the player. """ - self.vba.press(cmd, hold=10, after=0) - self.vba.press([]) + if isinstance(cmd, list): + for command in cmd: + self.move(cmd) + else: + self.vba.press(cmd, hold=10, after=0) + self.vba.press([]) - memory = self.vba.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: - self.vba.step(count=10) memory = self.vba.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: + self.vba.step(count=10) + memory = self.vba.memory def get_enemy_hp(self): """ -- cgit v1.2.3 From 211da4dcb3ac2d8ca370d6b840e2368f59b5114d Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 2 Nov 2013 14:41:48 -0500 Subject: attempt to start a trainer battle --- pokemontools/vba/vba.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 81ffe4e..1af2364 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -577,3 +577,17 @@ class crystal(object): """ hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217]) return hp + + def start_trainer_battle(self, trainer_group, trainer_id): + memory = self.vba.memory + + # setup the battle + memory[0xd459] = 0x81 + memory[0xd22f] = trainer_group + memory[0xd231] = trainer_id + + self.vba.memory = memory + + Script_startbattle_address = 0x97436 + + self.call(0x25, Script_startbattle_address) -- cgit v1.2.3 From 0d4f9340b08766ae3a445b1428cbfe4b4a33031a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 2 Nov 2013 20:56:40 -0500 Subject: a naive implementation of start_trainer_battle This is a really dumb way to start a battle, but the other methods aren't working yet. --- pokemontools/vba/vba.py | 91 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 15 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 1af2364..bb468b8 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -127,14 +127,7 @@ class crystal(object): 0x3bb7, ] - for value in push: - self.registers.sp -= 2 - self.vba.write_memory_at(self.registers.sp + 1, value >> 8) - self.vba.write_memory_at(self.registers.sp, value & 0xFF) - if list(self.vba.memory[self.registers.sp : self.registers.sp + 2]) != [value & 0xFF, value >> 8]: - print "desired memory values: " + str([value & 0xFF, value >> 8] ) - print "actual memory values: " + str(list(self.vba.memory[self.registers.sp : self.registers.sp + 2])) - print "wrong value at " + hex(self.registers.sp) + " expected " + hex(value) + " but got " + hex(self.vba.read_memory_at(self.registers.sp)) + self.push_stack(push) if bank != 0: self.registers["af"] = (bank << 8) | (self.registers["af"] & 0xFF) @@ -143,6 +136,16 @@ class crystal(object): else: self.registers["pc"] = address + def push_stack(self, push): + for value in push: + self.registers["sp"] -= 2 + self.vba.write_memory_at(self.registers.sp + 1, value >> 8) + self.vba.write_memory_at(self.registers.sp, value & 0xFF) + if list(self.vba.memory[self.registers.sp : self.registers.sp + 2]) != [value & 0xFF, value >> 8]: + print "desired memory values: " + str([value & 0xFF, value >> 8] ) + print "actual memory values: " + str(list(self.vba.memory[self.registers.sp : self.registers.sp + 2])) + print "wrong value at " + hex(self.registers.sp) + " expected " + hex(value) + " but got " + hex(self.vba.read_memory_at(self.registers.sp)) + def get_stack(self): """ Return a list of functions on the stack. @@ -578,16 +581,74 @@ class crystal(object): hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217]) return hp - def start_trainer_battle(self, trainer_group, trainer_id): + def start_trainer_battle(self, map_group=0x1, map_id=0xc, x=6, y=8, direction="l", loop_limit=10): + """ + Starts a trainer battle by warping into a map at the designated + coordinates, pressing the direction button for a full walk step (which + ideally should be blocked, this is mainly to establish direction), and + then pressing "a" to initiate the trainer battle. + """ + self.warp(map_group, map_id, x, y) + + # finish loading the map, might not be necessary? + self.nstep(100) + + # face towards the trainer (or whatever direction was specified). If + # this direction is blocked, then this will only change which direction + # the character is facing. However, if this direction is not blocked by + # the map or by an npc, then this will cause an entire step to be + # taken. + self.vba.press([direction]) + + # talk to the trainer, don't assume line of sight will be triggered + self.vba.press(["a"]) + self.vba.press([]) + + # trainer might talk, skip any text until the player can choose moves + while not self.is_in_battle() and loop_limit > 0: + self.text_wait() + loop_limit -= 1 + + def broken_start_battle(self): + # go to a map with wildmons + self.warp(0x1, 0xc, 6, 8) + self.nstep(200) + memory = self.vba.memory + Script_startbattle_address = 0x97436 + CallScript_address = 0x261f + RockSmashBattleScript_address = 0x97cf9 + RockSmashEncounter_address = 0x97cc0 + StartBattle_address = 0x3f4c1 + ScriptRunning = 0xd438 + ScriptBank = 0xd439 + ScriptPos = 0xd43a + start_wild_battle = 0x3f4dd + script = 0x1a1dc6 + # setup the battle - memory[0xd459] = 0x81 - memory[0xd22f] = trainer_group - memory[0xd231] = trainer_id + #memory[0xd459] = 0x81 + #memory[0xd22f] = trainer_group + #memory[0xd231] = trainer_id - self.vba.memory = memory + #self.vba.memory = memory - Script_startbattle_address = 0x97436 + #self.call(0x25, Script_startbattle_address % 0x4000) + + #self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) + #self.vba.registers["hl"] = RockSmashBattleScript_address % 0x4000 + #self.call(0x0, CallScript_address) + + #self.call(StartBattle_address / 0x4000, StartBattle_address % 0x4000) + #self.call(RockSmashEncounter_address / 0x4000, RockSmashEncounter_address % 0x4000) + + #self.push_stack([self.registers.pc]) + #memory[ScriptBank] = script / 0x4000 + #memory[ScriptPos] = ((script % 0x4000) & 0xff00) >> 8 + #memory[ScriptPos+1] = ((script % 0x4000) & 0xff) + #memory[ScriptRunning] = 0xff + + #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) - self.call(0x25, Script_startbattle_address) + #self.vba.memory = memory -- cgit v1.2.3 From a9a9568d142b81d07a6aa37721fe29ebbbb898ee Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 2 Nov 2013 23:12:14 -0500 Subject: a broken attempt at starting random battles --- pokemontools/vba/vba.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index bb468b8..a64a75a 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -609,6 +609,11 @@ class crystal(object): self.text_wait() loop_limit -= 1 + def broken_start_random_battle(self): + self.push_stack([self.registers.pc]) + self.registers["pc"] = address % 0x4000 + self.call(address / 0x4000, address % 0x4000) + def broken_start_battle(self): # go to a map with wildmons self.warp(0x1, 0xc, 6, 8) -- cgit v1.2.3 From a4179b5337ef767d215185d65a0f7af0f4cd4dbd Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 3 Nov 2013 01:41:07 -0500 Subject: attempting a few more trainer battles --- pokemontools/vba/vba.py | 63 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 19 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index a64a75a..05d4fad 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -581,7 +581,7 @@ class crystal(object): hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217]) return hp - def start_trainer_battle(self, map_group=0x1, map_id=0xc, x=6, y=8, direction="l", loop_limit=10): + def start_trainer_battle_lamely(self, map_group=0x1, map_id=0xc, x=6, y=8, direction="l", loop_limit=10): """ Starts a trainer battle by warping into a map at the designated coordinates, pressing the direction button for a full walk step (which @@ -614,36 +614,61 @@ class crystal(object): self.registers["pc"] = address % 0x4000 self.call(address / 0x4000, address % 0x4000) - def broken_start_battle(self): - # go to a map with wildmons - self.warp(0x1, 0xc, 6, 8) - self.nstep(200) + def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): + """ + This will fail after the first mon is defeated. + """ + Script_startbattle_address = 0x97436 + # setup the battle memory = self.vba.memory + memory[0xd459] = 0x81 + memory[0xd22f] = trainer_group + memory[0xd231] = trainer_id + self.vba.memory = memory - Script_startbattle_address = 0x97436 + self.call(0x25, (Script_startbattle_address % 0x4000) + 0x4000) + + def broken_start_random_battle_by_rocksmash_battle_script(self): + """ + This doesn't start a battle. + """ CallScript_address = 0x261f RockSmashBattleScript_address = 0x97cf9 - RockSmashEncounter_address = 0x97cc0 - StartBattle_address = 0x3f4c1 ScriptRunning = 0xd438 ScriptBank = 0xd439 ScriptPos = 0xd43a - start_wild_battle = 0x3f4dd - script = 0x1a1dc6 - # setup the battle - #memory[0xd459] = 0x81 - #memory[0xd22f] = trainer_group - #memory[0xd231] = trainer_id + memory = self.vba.memory + memory[ScriptBank] = RockSmashBattleScript_address / 0x4000 + memory[ScriptPos] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff00) >> 8 + memory[ScriptPos+1] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff) + memory[ScriptRunning] = 0xff + self.vba.memory = memory - #self.vba.memory = memory + self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) + self.vba.registers["hl"] = (RockSmashBattleScript_address % 0x4000) + 0x4000 + self.call(0x0, CallScript_address) - #self.call(0x25, Script_startbattle_address % 0x4000) + #def attempt_start_battle_by_startbattle(self): + # StartBattle_address = 0x3f4c1 + # self.call(StartBattle_address / 0x4000, (StartBattle_address % 0x4000) + 0x4000) - #self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) - #self.vba.registers["hl"] = RockSmashBattleScript_address % 0x4000 - #self.call(0x0, CallScript_address) + #def attempt_start_random_battle_by_wild_battle(self): + # start_wild_battle = 0x3f4dd + # #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) + # #self.vba.registers["pc"] = ... + + def old_crap(self): + CallScript_address = 0x261f + RockSmashBattleScript_address = 0x97cf9 + RockSmashEncounter_address = 0x97cc0 + StartBattle_address = 0x3f4c1 + ScriptRunning = 0xd438 + ScriptBank = 0xd439 + ScriptPos = 0xd43a + start_wild_battle = 0x3f4dd + script = 0x1a1dc6 #self.call(StartBattle_address / 0x4000, StartBattle_address % 0x4000) #self.call(RockSmashEncounter_address / 0x4000, RockSmashEncounter_address % 0x4000) -- cgit v1.2.3 From fd61c8f460d6e7453a79f945768f5952599ba724 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sat, 9 Nov 2013 15:09:21 -0600 Subject: fix move() for lists of commands --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 05d4fad..3367dee 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -562,7 +562,7 @@ class crystal(object): """ if isinstance(cmd, list): for command in cmd: - self.move(cmd) + self.move(command) else: self.vba.press(cmd, hold=10, after=0) self.vba.press([]) -- cgit v1.2.3 From a6118071a0b0f3c0a43754f3336693f198d2a6da Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 13:53:24 -0600 Subject: broken attempt at calling givepoke --- pokemontools/vba/vba.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index cb0070d..515ac8d 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -636,6 +636,36 @@ class crystal(object): self.call(0x25, (Script_startbattle_address % 0x4000) + 0x4000) + def set_script(self, address): + """ + Sets the current script in wram to whatever address. + """ + ScriptBank = 0xd439 + ScriptPos = 0xd43a + + memory = self.vba.memory + memory[ScriptBank] = address / 0x4000 + memory[ScriptPos] = (((address % 0x4000) + 0x4000) & 0xff00) >> 8 + memory[ScriptPos] = ((address % 0x4000) + 0x4000) & 0xff + + # TODO: determine if this is necessary + #memory[ScriptRunning] = 0xff + + self.vba.memory = memory + + def attempt_call_givepoke(self): + """ + An attempt at calling the givepoke command directly. + """ + givepoke_address = 0x97932 + + # 0, 50, 0, 0 + givepoke_data_address = 0x6ca5 + + self.set_script(givepoke_data_address) + + self.call(givepoke_address / 0x4000, (givepoke_address % 0x4000) + 0x4000) + def broken_start_random_battle_by_rocksmash_battle_script(self): """ This doesn't start a battle. -- cgit v1.2.3 From 271cce531902e381f1207122060f7fef898a2afc Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 13:56:29 -0600 Subject: make call() calculate bank addresses --- pokemontools/vba/vba.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 515ac8d..3249ce3 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -118,13 +118,16 @@ class crystal(object): return state - def call(self, bank, address): + def call(self, address): """ Jumps into a function at a certain address. Go into the start menu, pause the game and try call(1, 0x1078) to see a string printed to the screen. """ + bank = address / 0x4000 + address = (address % 0x4000) + 0x4000 + push = [ self.registers.pc, self.registers.hl, @@ -288,7 +291,7 @@ class crystal(object): """ for step_counter in range(0, steplimit): self.walk_through_walls() - #call(0x1, 0x1078) + #call(0x1078) self.vba.step() def disable_triggers(self): @@ -617,9 +620,10 @@ class crystal(object): loop_limit -= 1 def broken_start_random_battle(self): + address = None self.push_stack([self.registers.pc]) self.registers["pc"] = address % 0x4000 - self.call(address / 0x4000, address % 0x4000) + self.call(address) def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): """ @@ -634,7 +638,7 @@ class crystal(object): memory[0xd231] = trainer_id self.vba.memory = memory - self.call(0x25, (Script_startbattle_address % 0x4000) + 0x4000) + self.call(Script_startbattle_address) def set_script(self, address): """ @@ -664,7 +668,7 @@ class crystal(object): self.set_script(givepoke_data_address) - self.call(givepoke_address / 0x4000, (givepoke_address % 0x4000) + 0x4000) + self.call(givepoke_address) def broken_start_random_battle_by_rocksmash_battle_script(self): """ @@ -685,15 +689,15 @@ class crystal(object): self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) self.vba.registers["hl"] = (RockSmashBattleScript_address % 0x4000) + 0x4000 - self.call(0x0, CallScript_address) + self.call(CallScript_address) #def attempt_start_battle_by_startbattle(self): # StartBattle_address = 0x3f4c1 - # self.call(StartBattle_address / 0x4000, (StartBattle_address % 0x4000) + 0x4000) + # self.call(StartBattle_address) #def attempt_start_random_battle_by_wild_battle(self): # start_wild_battle = 0x3f4dd - # #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) + # #self.call(start_wild_battle) # #self.vba.registers["pc"] = ... def old_crap(self): @@ -707,8 +711,8 @@ class crystal(object): start_wild_battle = 0x3f4dd script = 0x1a1dc6 - #self.call(StartBattle_address / 0x4000, StartBattle_address % 0x4000) - #self.call(RockSmashEncounter_address / 0x4000, RockSmashEncounter_address % 0x4000) + #self.call(StartBattle_address) + #self.call(RockSmashEncounter_address) #self.push_stack([self.registers.pc]) #memory[ScriptBank] = script / 0x4000 @@ -716,6 +720,6 @@ class crystal(object): #memory[ScriptPos+1] = ((script % 0x4000) & 0xff) #memory[ScriptRunning] = 0xff - #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) + #self.call(start_wild_battle) #self.vba.memory = memory -- cgit v1.2.3 From b36e42494df301452594534a87bdebc2296d4cc0 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 14:13:20 -0600 Subject: stop writing 0x4000 everywhere --- pokemontools/vba/vba.py | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 3249ce3..b3224bb 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -35,6 +35,21 @@ import vba_wrapper button_masks = vba_wrapper.core.VBA.button_masks button_combiner = vba_wrapper.core.VBA.button_combine +def calculate_bank(address): + """ + Which bank does this address exist in? + """ + return address / 0x4000 + +def calculate_address(address): + """ + Gives the relative address once the bank is loaded. + + This is not the same as the calculate_pointer in the + pokemontools.crystal.pointers module. + """ + return (address % 0x4000) + 0x4000 + def translate_chars(charz): """ Translate a string from the in-game format to readable form. This is @@ -118,15 +133,15 @@ class crystal(object): return state - def call(self, address): + def call(self, address, bank=None): """ Jumps into a function at a certain address. Go into the start menu, pause the game and try call(1, 0x1078) to see a string printed to the screen. """ - bank = address / 0x4000 - address = (address % 0x4000) + 0x4000 + if not bank: + bank = calculate_bank(address) push = [ self.registers.pc, @@ -619,12 +634,6 @@ class crystal(object): self.text_wait() loop_limit -= 1 - def broken_start_random_battle(self): - address = None - self.push_stack([self.registers.pc]) - self.registers["pc"] = address % 0x4000 - self.call(address) - def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): """ This will fail after the first mon is defeated. @@ -648,9 +657,10 @@ class crystal(object): ScriptPos = 0xd43a memory = self.vba.memory - memory[ScriptBank] = address / 0x4000 - memory[ScriptPos] = (((address % 0x4000) + 0x4000) & 0xff00) >> 8 - memory[ScriptPos] = ((address % 0x4000) + 0x4000) & 0xff + memory[ScriptBank] = calculate_bank(address) + pointer = calculate_address(address) + memory[ScriptPos] = (calculate_address(address) & 0xff00) >> 8 + memory[ScriptPos] = calculate_address(address) & 0xff # TODO: determine if this is necessary #memory[ScriptRunning] = 0xff @@ -681,14 +691,14 @@ class crystal(object): ScriptPos = 0xd43a memory = self.vba.memory - memory[ScriptBank] = RockSmashBattleScript_address / 0x4000 - memory[ScriptPos] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff00) >> 8 - memory[ScriptPos+1] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff) + memory[ScriptBank] = calculate_bank(RockSmashBattleScript_address) + memory[ScriptPos] = (calculate_address(RockSmashBattleScript_address) & 0xff00) >> 8 + memory[ScriptPos+1] = calculate_address(RockSmashBattleScript_address) & 0xff memory[ScriptRunning] = 0xff self.vba.memory = memory - self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) - self.vba.registers["hl"] = (RockSmashBattleScript_address % 0x4000) + 0x4000 + self.vba.registers["af"] = (calculate_bank(RockSmashBattleScript_address) << 8) | (self.vba.registers.af & 0xff) + self.vba.registers["hl"] = calculate_address(RockSmashBattleScript_address) self.call(CallScript_address) #def attempt_start_battle_by_startbattle(self): @@ -715,9 +725,7 @@ class crystal(object): #self.call(RockSmashEncounter_address) #self.push_stack([self.registers.pc]) - #memory[ScriptBank] = script / 0x4000 - #memory[ScriptPos] = ((script % 0x4000) & 0xff00) >> 8 - #memory[ScriptPos+1] = ((script % 0x4000) & 0xff) + #self.set_script(script) #memory[ScriptRunning] = 0xff #self.call(start_wild_battle) -- cgit v1.2.3 From 699ed8c6c1fa58a7d3b3edf58542541e2e6f34af Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 14:18:54 -0600 Subject: fix a few call() calls --- pokemontools/vba/vba.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index b3224bb..c32e8bd 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -647,7 +647,7 @@ class crystal(object): memory[0xd231] = trainer_id self.vba.memory = memory - self.call(Script_startbattle_address) + self.call(calculate_address(Script_startbattle_address), bank=calculate_bank(Script_startbattle_address,)) def set_script(self, address): """ @@ -678,7 +678,7 @@ class crystal(object): self.set_script(givepoke_data_address) - self.call(givepoke_address) + self.call(calculate_address(givepoke_address), bank=calculate_bank(givepoke_address)) def broken_start_random_battle_by_rocksmash_battle_script(self): """ @@ -699,11 +699,11 @@ class crystal(object): self.vba.registers["af"] = (calculate_bank(RockSmashBattleScript_address) << 8) | (self.vba.registers.af & 0xff) self.vba.registers["hl"] = calculate_address(RockSmashBattleScript_address) - self.call(CallScript_address) + self.call(calculate_address(CallScript_address), bank=calculate_bank(CallScript_address)) #def attempt_start_battle_by_startbattle(self): # StartBattle_address = 0x3f4c1 - # self.call(StartBattle_address) + # self.call(calculate_address(StartBattle_address), bank=calculate_bank(StartBattle_address)) #def attempt_start_random_battle_by_wild_battle(self): # start_wild_battle = 0x3f4dd -- cgit v1.2.3 From 8b637503a560c7028ac066d50b7aa3923e49082a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 14:19:26 -0600 Subject: allow bank=0 in call() --- pokemontools/vba/vba.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index c32e8bd..2a2c4ca 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -140,7 +140,7 @@ class crystal(object): Go into the start menu, pause the game and try call(1, 0x1078) to see a string printed to the screen. """ - if not bank: + if bank is None: bank = calculate_bank(address) push = [ -- cgit v1.2.3 From cab54cc723ee66f962d36d2e7c6d844a3df9b3b0 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 15:04:31 -0600 Subject: crude attempt at injecting asm into wram --- pokemontools/vba/vba.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 2a2c4ca..bd737da 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -187,6 +187,73 @@ class crystal(object): return addresses + def inject_asm(self, asm, address=0xc6d4): + """ + Writes asm to memory. Makes the emulator run the asm. + + This function will append "ret" to the list of bytes. Before returning, + it updates the value at the first byte to indicate that the function + has executed. + + The first byte at the given address is reserved for whether the asm has + finished executing. + """ + memory = self.vba.memory + + # the first byte is reserved for whether the script has finished + has_finished = address + memory[has_finished] = 0 + + # the second byte is where the script will be stored + script_address = address + 1 + + # TODO: error checking; make sure the last byte doesn't already return. + # Use some functions from gbz80disasm to perform this check. + + # set a value to indicate that the script has executed + set_has_finished = [ + # push af + 0xf5, + + # ld a, 1 + 0xfa, 1, + + # ld [has_finished], a + 0x77, has_finished & 0xff, has_finished >> 8, + + # pop af + 0xf1, + + # ret + 0xc9, + ] + + # append the last opcodes to the script + asm.extend(set_has_finished) + + memory[script_address : script_address + len(asm)] = asm + self.vba.memory = memory + + # make the emulator call the script + self.call(script_address, bank=0) + + # make the emulator step forward + self.vba.step(count=1) + + # check if the script has executed + # TODO: should this raise an exception if the script didn't finish? + if self.vba.memory[has_finished] == 0: + return False + elif self.vba.memory[has_finished] == 1: + return True + else: + raise Exception( + "has_finished at {has_finished} was overwritten with an unexpected value {value}".format( + has_finished=hex(has_finished), + value=self.vba.memory[has_finished], + ) + ) + def text_wait(self, 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. -- cgit v1.2.3 From e700c37844142b5017feddce6f25dbbba5e89f04 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 15:19:15 -0600 Subject: a working inject_asm implementation --- pokemontools/vba/vba.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index bd737da..605a69d 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -187,7 +187,7 @@ class crystal(object): return addresses - def inject_asm(self, asm, address=0xc6d4): + def inject_asm(self, asm=[], address=0xdfcf): """ Writes asm to memory. Makes the emulator run the asm. @@ -198,7 +198,7 @@ class crystal(object): The first byte at the given address is reserved for whether the asm has finished executing. """ - memory = self.vba.memory + memory = list(self.vba.memory) # the first byte is reserved for whether the script has finished has_finished = address @@ -216,10 +216,10 @@ class crystal(object): 0xf5, # ld a, 1 - 0xfa, 1, + 0x3e, 1, # ld [has_finished], a - 0x77, has_finished & 0xff, has_finished >> 8, + 0xea, has_finished & 0xff, has_finished >> 8, # pop af 0xf1, @@ -229,7 +229,7 @@ class crystal(object): ] # append the last opcodes to the script - asm.extend(set_has_finished) + asm = bytearray(asm) + bytearray(set_has_finished) memory[script_address : script_address + len(asm)] = asm self.vba.memory = memory @@ -238,7 +238,7 @@ class crystal(object): self.call(script_address, bank=0) # make the emulator step forward - self.vba.step(count=1) + self.vba.step(count=50) # check if the script has executed # TODO: should this raise an exception if the script didn't finish? -- cgit v1.2.3 From b71fbf3852fb1274b13b548e13b27303e155658c Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 15:53:12 -0600 Subject: write inject_asm_into_rom This method injects asm straight into the ROM loaded in the emulator. It does not overwrite the ROM on the file system. This method is much slower than the wram version because it involves copying memory multiples and copying the entire ROM into python and then sending it back to the emulator. --- pokemontools/vba/vba.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 605a69d..f69595b 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -187,7 +187,93 @@ class crystal(object): return addresses - def inject_asm(self, asm=[], address=0xdfcf): + def inject_asm_into_rom(self, asm=[], address=0x75 * 0x4000, has_finished_address=0xdb75): + """ + Writes asm to the loaded ROM. Calls the asm. + + :param address: ROM address for where to store the injected asm script. + The default value is an address in pokecrystal that isn't used for + anything. + + :param has_finished_address: address for where to store whether the + script executed or not. This value is restored when the script has been + confirmed to work. It's conceivable that some injected asm might need + to change that address if the asm needs to access the original wram + value itself. + """ + if len(asm) > 0x4000: + raise Exception("too much asm") + + # temporarily use wram + cached_wram_value = self.vba.memory[has_finished_address] + + # set the value at has_finished_address to 0 + reset_wram_mem = list(self.vba.memory) + reset_wram_mem[has_finished_address] = 0 + self.vba.memory = reset_wram_mem + + # set a value to indicate that the script has executed + set_has_finished = [ + # push af + 0xf5, + + # ld a, 1 + 0x3e, 1, + + # ld [has_finished], a + 0xea, has_finished_address & 0xff, has_finished_address >> 8, + + # pop af + 0xf1, + + # ret + 0xc9, + ] + + # TODO: check if asm ends with a byte that causes a return or call or + # other "ender". Raise an exception if it already returns on its own. + + # combine the given asm with the setter bytes + total_asm = asm + set_has_finished + + # get a copy of the current rom + rom = list(self.vba.rom) + + # inject the asm + rom[address : address + len(total_asm)] = total_asm + + # set the rom with the injected asm + self.vba.rom = rom + + # call the injected asm + self.call(calculate_address(address), bank=calculate_bank(address)) + + # make the emulator step forward + self.vba.step(count=20) + + # check if the script has executed (see below) + current_mem = self.vba.memory + + # reset the wram value to its original value + another_mem = list(self.vba.memory) + another_mem[has_finished_address] = cached_wram_value + self.vba.memory = another_mem + + # check if the script has actually executed + # TODO: should this raise an exception if the script didn't finish? + if current_mem[has_finished_address] == 0: + return False + elif current_mem[has_finished_address] == 1: + return True + else: + raise Exception( + "has_finished_address at {has_finished_address} was overwritten with an unexpected value {value}".format( + has_finished_address=hex(has_finished_address), + value=current_mem[has_finished_address], + ) + ) + + def inject_asm_into_wram(self, asm=[], address=0xdfcf): """ Writes asm to memory. Makes the emulator run the asm. -- cgit v1.2.3 From 6b0d8ee855af252b48709c88a46c748c956af740 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 16:29:23 -0600 Subject: implement call_script to call CallScript This is the entry point for calling in-game scripts. --- pokemontools/vba/vba.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index f69595b..42f5e5b 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -340,6 +340,54 @@ class crystal(object): ) ) + def call_script(self, address, bank=None, wram=False, force=False): + """ + Sets wram values so that the engine plays a script. + + :param address: address of the map script + :param bank: override for bank calculation (based on address) + :param wram: force bank to 0 + :param force: override an already-running script + """ + + ScriptFlags = 0xd434 + ScriptMode = 0xd437 + ScriptRunning = 0xd438 + ScriptBank = 0xd439 + ScriptPos = 0xd43a + NumScriptParents = 0xd43c + ScriptParents = 0xd43d + + num_possible_parents = 4 + len_parent = 3 + + mem = list(self.vba.memory) + + if mem[ScriptRunning] == 0xff: + if force: + # wipe the parent routine array + mem[NumScriptParents] = 0 + for i in xrange(num_possible_parents * len_parent): + mem[ScriptParents + i] = 0 + else: + raise Exception("a script is already running, use force=True") + + if wram: + bank = 0 + elif not bank: + bank = calculate_bank(address) + address = address % 0x4000 + 0x4000 * bool(bank) + + mem[ScriptFlags] |= 4 + mem[ScriptMode] = 1 + mem[ScriptRunning] = 0xff + + mem[ScriptBank] = bank + mem[ScriptPos] = address % 0x100 + mem[ScriptPos+1] = address / 0x100 + + self.vba.memory = mem + def text_wait(self, 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. -- cgit v1.2.3 From c76156f3f0d48a6eed324c7f99e20a365f2da416 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 16:29:55 -0600 Subject: method to lower enemy hp during battle --- pokemontools/vba/vba.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 42f5e5b..a3c8b7b 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -495,9 +495,12 @@ class crystal(object): self.vba.write_memory_at(0xC2FC, 0) self.vba.write_memory_at(0xC2FD, 0) - #@staticmethod - #def set_enemy_level(level): - # vba.write_memory_at(0xd213, level) + def lower_enemy_hp(self): + """ + Dramatically lower the enemy's HP. + """ + self.vba.write_memory_at(0xd216, 0) + self.vba.write_memory_at(0xd217, 1) def nstep(self, steplimit=500): """ -- cgit v1.2.3 From 4fab1088d9718ef94bb72caa3463a0648aff848a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 16:32:16 -0600 Subject: function to start a battle by rocksmash 01:04 < padz> u cunt --- pokemontools/vba/vba.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index a3c8b7b..60cd0ab 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -884,26 +884,13 @@ class crystal(object): self.call(calculate_address(givepoke_address), bank=calculate_bank(givepoke_address)) - def broken_start_random_battle_by_rocksmash_battle_script(self): + def start_random_battle_by_rocksmash_battle_script(self): """ - This doesn't start a battle. + Initiates a wild battle using the same function that using rocksmash + would call. """ - CallScript_address = 0x261f RockSmashBattleScript_address = 0x97cf9 - ScriptRunning = 0xd438 - ScriptBank = 0xd439 - ScriptPos = 0xd43a - - memory = self.vba.memory - memory[ScriptBank] = calculate_bank(RockSmashBattleScript_address) - memory[ScriptPos] = (calculate_address(RockSmashBattleScript_address) & 0xff00) >> 8 - memory[ScriptPos+1] = calculate_address(RockSmashBattleScript_address) & 0xff - memory[ScriptRunning] = 0xff - self.vba.memory = memory - - self.vba.registers["af"] = (calculate_bank(RockSmashBattleScript_address) << 8) | (self.vba.registers.af & 0xff) - self.vba.registers["hl"] = calculate_address(RockSmashBattleScript_address) - self.call(calculate_address(CallScript_address), bank=calculate_bank(CallScript_address)) + self.call_script(RockSmashBattleScript_address) #def attempt_start_battle_by_startbattle(self): # StartBattle_address = 0x3f4c1 -- cgit v1.2.3 From bc30cd1e9a95342cbb77c1a05a2656f2505e817a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 17:38:55 -0600 Subject: an attempt at givepoke --- pokemontools/vba/vba.py | 66 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 7 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 60cd0ab..62c16da 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -871,18 +871,70 @@ class crystal(object): self.vba.memory = memory - def attempt_call_givepoke(self): + def givepoke(self, pokemon_id, level, nickname): """ - An attempt at calling the givepoke command directly. + Give the player a pokemon. """ - givepoke_address = 0x97932 + if isinstance(nickname, str): + if len(nickname) == 0: + raise Exception("invalid nickname") + elif len(nickname) > 11: + raise Exception("nickname too long") + else: + if not nickname: + nickname = False + else: + raise Exception("nickname needs to be a string, False or None") + + # script to inject into wram + script = [ + 0x47, # loadfont + #0x55, # keeptextopen + + # givepoke pokemon_id, level, 0, 0 + 0x2d, pokemon_id, level, 0, 0, + + #0x54, # closetext + 0x49, # loadmovesprites + 0x91, # end + ] + + #address = 0xd073 + #address = 0xc000 + address = 0xd8f1 + + mem = list(self.vba.memory) + backup_wram = mem[address : address + len(script)] + mem[address : address + len(script)] = script + self.vba.memory = mem + + self.call_script(address, wram=True) + + # "would you like to give it a nickname?" + self.text_wait() - # 0, 50, 0, 0 - givepoke_data_address = 0x6ca5 + if nickname: + # yes + self.vba.press("a", hold=10) + + # wait for the keyboard to appear + # TODO: this wait should be moved into write() + self.vba.step(count=20) - self.set_script(givepoke_data_address) + # type the requested nicknameb + self.write(nickname) - self.call(calculate_address(givepoke_address), bank=calculate_bank(givepoke_address)) + self.vba.press("start", hold=5, after=10) + self.vba.press("a", hold=5, after=50) + else: + # no nickname + self.vba.press("d", hold=10, after=20) + self.vba.press("a", hold=5, after=30) + + # reset whatever was in wram before this script was called + mem = list(self.vba.memory) + mem[address : address + len(script)] = backup_wram + self.vba.memory = mem def start_random_battle_by_rocksmash_battle_script(self): """ -- cgit v1.2.3 From d1da18652d96212c05be10a9dfb22bc02071f84e Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 17:53:06 -0600 Subject: make givepoke work (h/t padz) --- pokemontools/vba/vba.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 62c16da..9d2be23 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -871,7 +871,7 @@ class crystal(object): self.vba.memory = memory - def givepoke(self, pokemon_id, level, nickname): + def givepoke(self, pokemon_id, level, nickname=None): """ Give the player a pokemon. """ @@ -899,9 +899,12 @@ class crystal(object): 0x91, # end ] + # picked this region of wram because it looks like it's probably unused + # in situations where givepoke will work. #address = 0xd073 #address = 0xc000 - address = 0xd8f1 + #address = 0xd8f1 + address = 0xd280 mem = list(self.vba.memory) backup_wram = mem[address : address + len(script)] @@ -928,8 +931,11 @@ class crystal(object): self.vba.press("a", hold=5, after=50) else: # no nickname - self.vba.press("d", hold=10, after=20) - self.vba.press("a", hold=5, after=30) + self.vba.press("b", hold=10, after=20) + + # Wait for the script to end in the engine before copying the original + # wram values back in. + self.vba.step(count=100) # reset whatever was in wram before this script was called mem = list(self.vba.memory) -- cgit v1.2.3 From 9310699565243442a8c363012b8bb09f000ddf29 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 19:49:39 -0600 Subject: write script to ROM and execute This handles all of the usual tasks that will be required for injecting and running custom scripts. --- pokemontools/vba/vba.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 9d2be23..0d678da 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -838,7 +838,7 @@ class crystal(object): self.text_wait() loop_limit -= 1 - def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): + def broken_start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): """ This will fail after the first mon is defeated. """ @@ -871,6 +871,86 @@ class crystal(object): self.vba.memory = memory + def inject_script_into_rom(self, asm=[0x91], rom_address=0x75 * 0x4000, wram_address=0xd280, limit=50): + """ + Writes a script to the ROM in a blank location. Calls call_script to + make the game engine aware of the script. Then executes the script and + looks for confirmation thta the script has started to run. + + The script must end itself. + + :param asm: scripting command bytes + :param rom_address: rom location to write asm to + :param wram_address: temporary storage for indicating if the script has + started yet + :param limit: number of frames to emulate before giving up on the start + script + """ + execution_pending = 0 + execution_started = 1 + valid_execution_states = (execution_pending, execution_started) + + # location for byte for whether script has started executing + execution_indicator_address = wram_address + + # backup whatever exists at the current wram location + backup_wram = self.vba.read_memory_at(execution_indicator_address) + + # .. and set it to "pending" + self.vba.write_memory_at(execution_indicator_address, execution_pending) + + # initial script that runs first to tell python that execution started + execution_indicator_script = [ + # writebyte to say that the script has executed + 0x15, execution_started, + + # copyvartobyte + 0x1a, execution_indicator_address & 0xff, execution_indicator_address >> 8, + ] + + # make the indicator script run before the user script + full_script = execution_indicator_script + asm + + # inject the asm + rom = list(self.vba.rom) + rom[rom_address : rom_address + len(full_script)] = full_script + + # set the rom with the injected bytes + self.vba.rom = rom + + # setup the script for execution + self.call_script(rom_address) + + status = execution_pending + while status != execution_started and limit > 0: + # emulator time travel + self.vba.step(count=1) + + # get latest wram + status = self.vba.read_memory_at(execution_indicator_address) + if status not in valid_execution_states: + raise Exception( + "The execution indicator at {addr} has invalid state {value}".format( + addr=hex(execution_indicator_address), + value=status, + ) + ) + elif status == execution_started: + break # hooray + + limit -= 1 + + if status == execution_pending and limit == 0: + raise Exception( + "Emulation timeout while waiting for script to start." + ) + + # The script has started so it's okay to reset wram back to whatever it + # was. + self.vba.write_memory_at(execution_indicator_address, backup_wram) + + return True + def givepoke(self, pokemon_id, level, nickname=None): """ Give the player a pokemon. -- cgit v1.2.3 From 9bf214f963113a40b2a6e3037e06c71abae2e69a Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 19:56:03 -0600 Subject: simplify givepoke by using inject_script_into_rom --- pokemontools/vba/vba.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 0d678da..9376c6a 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -951,7 +951,7 @@ class crystal(object): return True - def givepoke(self, pokemon_id, level, nickname=None): + def givepoke(self, pokemon_id, level, nickname=None, wram=False): """ Give the player a pokemon. """ @@ -986,12 +986,15 @@ class crystal(object): #address = 0xd8f1 address = 0xd280 - mem = list(self.vba.memory) - backup_wram = mem[address : address + len(script)] - mem[address : address + len(script)] = script - self.vba.memory = mem + if not wram: + self.inject_script_into_rom(asm=script, wram_address=address) + else: + mem = list(self.vba.memory) + backup_wram = mem[address : address + len(script)] + mem[address : address + len(script)] = script + self.vba.memory = mem - self.call_script(address, wram=True) + self.call_script(address, wram=True) # "would you like to give it a nickname?" self.text_wait() @@ -1013,14 +1016,15 @@ class crystal(object): # no nickname self.vba.press("b", hold=10, after=20) - # Wait for the script to end in the engine before copying the original - # wram values back in. - self.vba.step(count=100) - - # reset whatever was in wram before this script was called - mem = list(self.vba.memory) - mem[address : address + len(script)] = backup_wram - self.vba.memory = mem + if wram: + # Wait for the script to end in the engine before copying the original + # wram values back in. + self.vba.step(count=100) + + # reset whatever was in wram before this script was called + mem = list(self.vba.memory) + mem[address : address + len(script)] = backup_wram + self.vba.memory = mem def start_random_battle_by_rocksmash_battle_script(self): """ -- cgit v1.2.3 From 409eab17163b2a85f07d4fe3ea58ed7e4a39b00b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 22:43:48 -0600 Subject: start_trainer_battle spawns a battle --- pokemontools/vba/vba.py | 102 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 5 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 9376c6a..17472f0 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -60,6 +60,22 @@ def translate_chars(charz): result += chars[each] return result +def translate_text(text, chars=chars): + """ + Converts text to the in-game byte coding. + """ + output = [] + for given_char in text: + for (byte, char) in chars.iteritems(): + if char == given_char: + output.append(byte) + break + else: + raise Exception( + "no match for {0}".format(given_char) + ) + return output + class crystal(object): """ Just a simple namespace to store a bunch of functions for Pokémon Crystal. @@ -853,6 +869,85 @@ class crystal(object): self.call(calculate_address(Script_startbattle_address), bank=calculate_bank(Script_startbattle_address,)) + def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1, text_win="YOU WIN", text_address=0xdb90): + """ + Start a trainer battle with the trainer located by trainer_group and + trainer_id. + + :param trainer_group: trainer group id + :param trainer_id: trainer id within the group + :param text_win: text to show if player wins + :param text_address: where to store text_win in wram + """ + # where the script will be written + rom_address = 0x75 * 0x4000 + + # battle win message + translated_text = translate_text(text_win) + + # also include the first and last bytes needed for text + translated_text = [0] + translated_text + [0x57] + + mem = self.vba.memory + + # create a backup of the current data + wram_backup = mem[text_address : text_address + len(translated_text)] + + # manipulate the memory + mem[text_address : text_address + len(translated_text)] = translated_text + self.vba.memory = mem + + text_pointer_hi = text_address / 0x100 + text_pointer_lo = text_address % 0x100 + + script = [ + # loadfont + #0x47, + + # winlosstext address, address + 0x64, text_pointer_lo, text_pointer_hi, 0, 0, + + # loadtrainer group, id + 0x5e, trainer_group, trainer_id, + + # startbattle + 0x5f, + + # returnafterbattle + 0x60, + + # reloadmapmusic + 0x83, + + # reloadmap + 0x7B, + ] + + # Now make the script restore wram at the end (after the text has been + # used). The assumption here is that this particular subset of wram + # data would not be needed during the bulk of the script. + address = text_address + for byte in wram_backup: + address_hi = address / 0x100 + address_lo = address % 0x100 + + script += [ + # loadvar + 0x1b, address_lo, address_hi, byte, + ] + + address += 1 + + script += [ + # end + 0x91, + ] + + # Use a different wram address because the default is something related + # to trainers. + # use a higher loop limit because otherwise it doesn't start fast enough? + self.inject_script_into_rom(asm=script, rom_address=rom_address, wram_address=0xdb75, limit=1000) + def set_script(self, address): """ Sets the current script in wram to whatever address. @@ -901,11 +996,8 @@ class crystal(object): # initial script that runs first to tell python that execution started execution_indicator_script = [ - # writebyte to say that the script has executed - 0x15, execution_started, - - # copyvartobyte - 0x1a, execution_indicator_address & 0xff, execution_indicator_address >> 8, + # loadvar address, value + 0x1b, execution_indicator_address & 0xff, execution_indicator_address >> 8, execution_started, ] # make the indicator script run before the user script -- cgit v1.2.3 From 0729ed3554f3d79bd59b626c82ccedb88b063860 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 22:45:12 -0600 Subject: remove a dead function --- pokemontools/vba/vba.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 17472f0..a564ac2 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -832,6 +832,8 @@ class crystal(object): coordinates, pressing the direction button for a full walk step (which ideally should be blocked, this is mainly to establish direction), and then pressing "a" to initiate the trainer battle. + + Consider using start_trainer_battle instead. """ self.warp(map_group, map_id, x, y) @@ -854,21 +856,6 @@ class crystal(object): self.text_wait() loop_limit -= 1 - def broken_start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): - """ - This will fail after the first mon is defeated. - """ - Script_startbattle_address = 0x97436 - - # setup the battle - memory = self.vba.memory - memory[0xd459] = 0x81 - memory[0xd22f] = trainer_group - memory[0xd231] = trainer_id - self.vba.memory = memory - - self.call(calculate_address(Script_startbattle_address), bank=calculate_bank(Script_startbattle_address,)) - def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1, text_win="YOU WIN", text_address=0xdb90): """ Start a trainer battle with the trainer located by trainer_group and -- cgit v1.2.3 From 44ef6852f4d24f075ae58373d329e681151ccb62 Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 22:49:19 -0600 Subject: remove old functions --- pokemontools/vba/vba.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index a564ac2..3e3d66d 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -1113,15 +1113,12 @@ class crystal(object): RockSmashBattleScript_address = 0x97cf9 self.call_script(RockSmashBattleScript_address) - #def attempt_start_battle_by_startbattle(self): - # StartBattle_address = 0x3f4c1 - # self.call(calculate_address(StartBattle_address), bank=calculate_bank(StartBattle_address)) - #def attempt_start_random_battle_by_wild_battle(self): # start_wild_battle = 0x3f4dd # #self.call(start_wild_battle) # #self.vba.registers["pc"] = ... + # why is this here? def old_crap(self): CallScript_address = 0x261f RockSmashBattleScript_address = 0x97cf9 @@ -1132,14 +1129,3 @@ class crystal(object): ScriptPos = 0xd43a start_wild_battle = 0x3f4dd script = 0x1a1dc6 - - #self.call(StartBattle_address) - #self.call(RockSmashEncounter_address) - - #self.push_stack([self.registers.pc]) - #self.set_script(script) - #memory[ScriptRunning] = 0xff - - #self.call(start_wild_battle) - - #self.vba.memory = memory -- cgit v1.2.3 From b94aaa38829dc96804ca35b01d1db92fe77f914b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Sun, 10 Nov 2013 23:05:31 -0600 Subject: remove more dead code --- pokemontools/vba/vba.py | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 3e3d66d..0dac63f 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -1112,20 +1112,3 @@ class crystal(object): """ RockSmashBattleScript_address = 0x97cf9 self.call_script(RockSmashBattleScript_address) - - #def attempt_start_random_battle_by_wild_battle(self): - # start_wild_battle = 0x3f4dd - # #self.call(start_wild_battle) - # #self.vba.registers["pc"] = ... - - # why is this here? - def old_crap(self): - CallScript_address = 0x261f - RockSmashBattleScript_address = 0x97cf9 - RockSmashEncounter_address = 0x97cc0 - StartBattle_address = 0x3f4c1 - ScriptRunning = 0xd438 - ScriptBank = 0xd439 - ScriptPos = 0xd43a - start_wild_battle = 0x3f4dd - script = 0x1a1dc6 -- cgit v1.2.3 From 966985411f01b799fa71f4823da7a8cd6d9cc47b Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Mon, 11 Nov 2013 00:55:54 -0600 Subject: detect the "mandatory switch" menu This requires a slightly slower text_wait function. There is probably a way to refactor that function in a way that doesn't cause cancer. --- pokemontools/vba/vba.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'pokemontools/vba/vba.py') diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 0dac63f..10513c6 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -431,12 +431,17 @@ class crystal(object): # set CurSFX self.vba.write_memory_at(0xc2bf, 0) - self.vba.press("a", hold=10, after=1) + self.vba.press("a", hold=10, after=50) # check if CurSFX is SFX_READ_TEXT_2 if self.vba.read_memory_at(0xc2bf) == 0x8: - print "cursfx is set to SFX_READ_TEXT_2, looping.." - return self.text_wait(step_size=step_size, max_wait=max_wait, debug=debug, callback=callback, sfx_limit=sfx_limit) + if "CANCEL Which" in self.get_text(): + print "probably the 'switch pokemon' menu" + return + else: + print "cursfx is set to SFX_READ_TEXT_2, looping.." + print self.get_text() + return self.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 -- cgit v1.2.3