summaryrefslogtreecommitdiff
path: root/pokemontools/vba/vba.py
diff options
context:
space:
mode:
Diffstat (limited to 'pokemontools/vba/vba.py')
-rw-r--r--pokemontools/vba/vba.py601
1 files changed, 379 insertions, 222 deletions
diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py
index d7fdf1d..3367dee 100644
--- a/pokemontools/vba/vba.py
+++ b/pokemontools/vba/vba.py
@@ -9,115 +9,160 @@ import re
import string
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
# 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
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]
return result
-def press(buttons, holdsteps=1, aftersteps=1):
+class crystal(object):
"""
- 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):
+ 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.
"""
- 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
- set_memory_at(registers.sp + 1, value >> 8)
- set_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))
- print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(get_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
-
-def get_stack():
- """
- Return a list of functions on the stack.
- """
- addresses = []
- sp = registers.sp
+ def __init__(self, config=None):
+ """
+ Launch the VBA controller.
+ """
+ if not config:
+ config = configuration.Config()
- for x in range(0, 11):
- sp = sp - (2 * x)
- hi = get_memory_at(sp + 1)
- lo = get_memory_at(sp)
- address = ((hi << 8) | lo)
- addresses.append(address)
+ self.config = config
- return addresses
+ self.vba = vba_wrapper.VBA(self.config.rom_path)
+ self.registers = vba_wrapper.core.registers.Registers(self.vba)
-class crystal:
- """
- Just a simple namespace to store a bunch of functions for Pokémon Crystal.
- """
+ 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.
+
+ 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 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(self.config.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.
+
+ Go into the start menu, pause the game and try call(1, 0x1078) to see a
+ string printed to the screen.
+ """
+ push = [
+ self.registers.pc,
+ self.registers.hl,
+ self.registers.de,
+ self.registers.bc,
+ self.registers.af,
+ 0x3bb7,
+ ]
+
+ self.push_stack(push)
+
+ if bank != 0:
+ self.registers["af"] = (bank << 8) | (self.registers["af"] & 0xFF)
+ self.registers["hl"] = address
+ self.registers["pc"] = 0x2d63 # FarJump
+ 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))
- @staticmethod
- def text_wait(step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None):
+ def get_stack(self):
+ """
+ Return a list of functions on the stack.
+ """
+ addresses = []
+ sp = self.registers.sp
+
+ for x in range(0, 11):
+ sp = sp - (2 * x)
+ hi = self.vba.read_memory_at(sp + 1)
+ lo = self.vba.read_memory_at(sp)
+ address = ((hi << 8) | lo)
+ addresses.append(address)
+
+ return addresses
+
+ 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.
@@ -134,22 +179,22 @@ 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 = 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:
print "pressing, then breaking.. address is: " + str(hex(address))
# set CurSFX
- set_memory_at(0xc2bf, 0)
+ self.vba.write_memory_at(0xc2bf, 0)
- press("a", holdsteps=10, aftersteps=1)
+ self.vba.press("a", hold=10, after=1)
# check if CurSFX is SFX_READ_TEXT_2
- if get_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
@@ -160,7 +205,7 @@ class crystal:
break
else:
- stack = get_stack()
+ stack = self.get_stack()
# yes/no box or the name selection box
if address in range(0xa46, 0xaaf):
@@ -179,14 +224,14 @@ class crystal:
break
else:
- nstep(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
# 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:
@@ -202,17 +247,15 @@ class crystal:
if max_wait == 0:
print "max_wait was hit"
- @staticmethod
- def walk_through_walls_slow():
- memory = get_memory()
+ def walk_through_walls_slow(self):
+ memory = self.vba.memory
memory[0xC2FA] = 0
memory[0xC2FB] = 0
memory[0xC2FC] = 0
memory[0xC2FD] = 0
- set_memory(memory)
+ self.vba.memory = memory
- @staticmethod
- def walk_through_walls():
+ def walk_through_walls(self):
"""
Lets the player walk all over the map.
@@ -221,73 +264,65 @@ 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)
+ 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):
- # set_memory_at(0xd213, 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)
- step()
+ self.vba.step()
- @staticmethod
- def disable_triggers():
- set_memory_at(0x23c4, 0xAF)
- set_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():
- set_memory_at(0x23f2, 0xAF)
- set_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 get_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 get_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 = get_memory_at(0xdcb8)
- y = get_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.
@@ -296,38 +331,34 @@ class crystal:
This probably works on other menus.
"""
- set_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 (get_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 get_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.
"""
- set_memory_at(0xDCA5, 0xFF)
- set_memory_at(0xDCA6, 0xFF)
- set_memory_at(0xDCA7, 0xFF)
- set_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 = get_memory_at(0xD472)
+ gender = self.vba.read_memory_at(0xD472)
if gender == 0:
return "male"
elif gender == 1:
@@ -335,54 +366,59 @@ 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 = get_memory_range(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):
- 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)
-
- @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):
+ """
+ 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)
+ 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):
+ """
+ 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
- set_memory_at(0xd8d8, 1)
- set_memory_at(0xd8d9, 99)
+ self.vba.write_memory_at(0xd8d8, 1)
+ self.vba.write_memory_at(0xd8d9, 99)
# ultraball
- set_memory_at(0xd8da, 2)
- set_memory_at(0xd8db, 99)
+ self.vba.write_memory_at(0xd8da, 2)
+ self.vba.write_memory_at(0xd8db, 99)
# pokeballs
- set_memory_at(0xd8dc, 5)
- set_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 = get_memory_range(0xc4a0, 1000)
+ tiles = self.vba.memory[0xc4a0:0xc4a0 + 1000]
for each in tiles:
if each in chars.keys():
thing = chars[each]
@@ -405,32 +441,69 @@ class crystal:
return output
- @staticmethod
- def keyboard_apply(button_sequence):
+ 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.
"""
for buttons in button_sequence:
- press(buttons)
- nstep(2)
- press([])
+ self.vba.press(buttons)
+
+ if buttons == "select":
+ self.vba.step(count=5)
+ else:
+ self.vba.step(count=2)
- @staticmethod
- def write(something="TrAiNeR"):
+ self.vba.press([])
+
+ 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.vba.step(count=10)
+ self.keyboard_apply([[x] for x in button_sequence])
+ return button_sequence
- @staticmethod
- def set_partymon2():
+ def set_partymon2(self):
"""
This causes corruption, so it's not working yet.
"""
- memory = get_memory()
+ memory = self.vba.memory
memory[0xdcd7] = 2
memory[0xdcd9] = 0x7
@@ -464,19 +537,18 @@ class crystal:
memory[0xdd33] = 0x10
memory[0xdd34] = 0x40
- set_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 get_memory_at(0xd438) != 255:
+ if self.vba.read_memory_at(0xd438) != 255:
print "script is done executing"
return
else:
- step()
+ self.vba.step()
if debug:
limit = limit - 1
@@ -484,44 +556,129 @@ class crystal:
if limit == 0:
print "limit ran out"
- @staticmethod
- def move(cmd):
+ def move(self, cmd):
"""
Attempt to move the player.
"""
- press(cmd, holdsteps=10, aftersteps=0)
- press([])
+ if isinstance(cmd, list):
+ for command in cmd:
+ self.move(command)
+ 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
+
+ def get_enemy_hp(self):
+ """
+ Returns the HP of the current enemy.
+ """
+ hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217])
+ return hp
+
+ 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
+ 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)
- memory = get_memory()
- #while memory[0xd4e1] == 2 and memory[0xd042] != 0x3e:
- while memory[0xd043] in [0, 1, 2, 3]:
- #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e:
- nstep(10)
- memory = get_memory()
+ # 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])
-class TestEmulator(unittest.TestCase):
- def test_PlaceString(self):
- call(0, 0x1078)
+ # 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_random_battle(self):
+ self.push_stack([self.registers.pc])
+ self.registers["pc"] = address % 0x4000
+ self.call(address / 0x4000, address % 0x4000)
+
+ 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
+
+ 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
+ ScriptRunning = 0xd438
+ ScriptBank = 0xd439
+ ScriptPos = 0xd43a
- # where to draw the text
- registers["hl"] = 0xc4a0
+ 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
- # what text to read from
- registers["de"] = 0x1276
+ 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)
- nstep(10)
+ #def attempt_start_battle_by_startbattle(self):
+ # StartBattle_address = 0x3f4c1
+ # self.call(StartBattle_address / 0x4000, (StartBattle_address % 0x4000) + 0x4000)
- text = crystal.get_text()
+ #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"] = ...
- self.assertTrue("TRAINER" in text)
+ 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
-class TestWriter(unittest.TestCase):
- def test_very_basic(self):
- button_sequence = keyboard.plan_typing("an")
- expected_result = ["select", "a", "d", "r", "r", "r", "r", "a"]
+ #self.call(StartBattle_address / 0x4000, StartBattle_address % 0x4000)
+ #self.call(RockSmashEncounter_address / 0x4000, RockSmashEncounter_address % 0x4000)
- self.assertEqual(len(expected_result), len(button_sequence))
- self.assertEqual(expected_result, button_sequence)
+ #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)
-if __name__ == "__main__":
- unittest.main()
+ #self.vba.memory = memory