summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pokemontools/vba/autoplayer.py742
1 files changed, 378 insertions, 364 deletions
diff --git a/pokemontools/vba/autoplayer.py b/pokemontools/vba/autoplayer.py
index 9aa8f4a..a63caa8 100644
--- a/pokemontools/vba/autoplayer.py
+++ b/pokemontools/vba/autoplayer.py
@@ -5,27 +5,7 @@ Programmatic speedrun of Pokémon Crystal
import os
# bring in the emulator and basic tools
-import vba
-
-def main():
- """
- Start the game.
- """
- vba.load_rom()
-
- # get past the opening sequence
- skip_intro()
-
- # walk to mom and handle her text
- handle_mom()
-
- # walk outside into new bark town
- walk_into_new_bark_town()
-
- # walk to elm and do whatever he wants
- handle_elm("totodile")
-
- new_bark_level_grind(10, skip=False)
+import vba as _vba
def skippable(func):
"""
@@ -51,445 +31,479 @@ def skippable(func):
return_value = None
if not skip:
- vba.save_state(func.__name__ + "-start", override=True)
+ _vba.save_state(func.__name__ + "-start", override=True)
return_value = func(*args, **kwargs)
- vba.save_state(func.__name__ + "-end", override=True)
+ _vba.save_state(func.__name__ + "-end", override=True)
elif skip:
- vba.set_state(vba.load_state(func.__name__ + "-end"))
+ _vba.set_state(vba.load_state(func.__name__ + "-end"))
return return_value
return wrapped_function
-@skippable
-def skip_intro():
+class Runner(object):
"""
- Skip the game boot intro sequence.
+ ``Runner`` is used to represent a set of functions that control an instance
+ of the emulator. This allows for automated runs of games.
"""
+ pass
- # copyright sequence
- vba.nstep(400)
+class SpeedRunner(Runner):
+ def setup(self):
+ self.cry = _vba.crystal()
- # skip the ditto sequence
- vba.press("a")
- vba.nstep(100)
+ def main(self):
+ """
+ Start the game.
+ """
+ # get past the opening sequence
+ self.skip_intro()
- # skip the start screen
- vba.press("start")
- vba.nstep(100)
+ # walk to mom and handle her text
+ self.handle_mom()
- # click "new game"
- vba.press("a", holdsteps=50, aftersteps=1)
+ # walk outside into new bark town
+ self.walk_into_new_bark_town()
- # skip text up to "Are you a boy? Or are you a girl?"
- vba.crystal.text_wait()
+ # walk to elm and do whatever he wants
+ self.handle_elm("totodile")
- # select "Boy"
- vba.press("a", holdsteps=50, aftersteps=1)
+ self.new_bark_level_grind(10, skip=False)
- # text until "What time is it?"
- vba.crystal.text_wait()
+ @skippable
+ def skip_intro(self):
+ """
+ Skip the game boot intro sequence.
+ """
- # select 10 o'clock
- vba.press("a", holdsteps=50, aftersteps=1)
+ # copyright sequence
+ self.cry.nstep(400)
- # yes i mean it
- vba.press("a", holdsteps=50, aftersteps=1)
+ # skip the ditto sequence
+ self.cry.press("a")
+ self.cry.nstep(100)
- # "How many minutes?" 0 min.
- vba.press("a", holdsteps=50, aftersteps=1)
+ # skip the start screen
+ self.cry.press("start")
+ self.cry.nstep(100)
- # "Who! 0 min.?" yes/no select yes
- vba.press("a", holdsteps=50, aftersteps=1)
+ # click "new game"
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- # read text until name selection
- vba.crystal.text_wait()
+ # skip text up to "Are you a boy? Or are you a girl?"
+ self.cry.text_wait()
- # select "Chris"
- vba.press("d", holdsteps=10, aftersteps=1)
- vba.press("a", holdsteps=50, aftersteps=1)
+ # select "Boy"
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- def overworldcheck():
- """
- A basic check for when the game starts.
- """
- return vba.get_memory_at(0xcfb1) != 0
+ # text until "What time is it?"
+ self.cry.text_wait()
- # go until the introduction is done
- vba.crystal.text_wait(callback=overworldcheck)
+ # select 10 o'clock
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- return
+ # yes i mean it
+ self.cry.press("a", holdsteps=50, aftersteps=1)
-@skippable
-def handle_mom():
- """
- Walk to mom. Handle her speech and questions.
- """
+ # "How many minutes?" 0 min.
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
+ # "Who! 0 min.?" yes/no select yes
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
+ # read text until name selection
+ self.cry.text_wait()
- vba.crystal.move("d")
- vba.crystal.move("d")
+ # select "Chris"
+ self.cry.press("d", holdsteps=10, aftersteps=1)
+ self.cry.press("a", holdsteps=50, aftersteps=1)
- # move into mom's line of sight
- vba.crystal.move("d")
+ def overworldcheck():
+ """
+ A basic check for when the game starts.
+ """
+ return self.cry.vba.memory[0xcfb1] != 0
- # let mom talk until "What day is it?"
- vba.crystal.text_wait()
+ # go until the introduction is done
+ self.cry.text_wait(callback=overworldcheck)
- # "What day is it?" Sunday
- vba.press("a", holdsteps=10) # Sunday
+ return
- vba.crystal.text_wait()
+ @skippable
+ def handle_mom(self):
+ """
+ Walk to mom. Handle her speech and questions.
+ """
- # "SUNDAY, is it?" yes/no
- vba.press("a", holdsteps=10) # yes
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
- vba.crystal.text_wait()
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
- # "Is it Daylight Saving Time now?" yes/no
- vba.press("a", holdsteps=10) # yes
+ self.cry.move("d")
+ self.cry.move("d")
- vba.crystal.text_wait()
+ # move into mom's line of sight
+ self.cry.move("d")
- # "AM DST, is that OK?" yes/no
- vba.press("a", holdsteps=10) # yes
+ # let mom talk until "What day is it?"
+ self.cry.text_wait()
- # text until "know how to use the PHONE?" yes/no
- vba.crystal.text_wait()
+ # "What day is it?" Sunday
+ self.cry.vba.press("a", holdsteps=10) # Sunday
- # press yes
- vba.press("a", holdsteps=10)
+ self.cry.text_wait()
- # wait until mom is done talking
- vba.crystal.text_wait()
+ # "SUNDAY, is it?" yes/no
+ self.cry.vba.press("a", holdsteps=10) # yes
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ self.cry.text_wait()
- return
+ # "Is it Daylight Saving Time now?" yes/no
+ self.cry.vba.press("a", holdsteps=10) # yes
-@skippable
-def walk_into_new_bark_town():
- """
- Walk outside after talking with mom.
- """
+ self.cry.text_wait()
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("l")
- vba.crystal.move("l")
+ # "AM DST, is that OK?" yes/no
+ self.cry.vba.press("a", holdsteps=10) # yes
- # walk outside
- vba.crystal.move("d")
+ # text until "know how to use the PHONE?" yes/no
+ self.cry.text_wait()
-@skippable
-def handle_elm(starter_choice):
- """
- Walk to Elm's Lab and get a starter.
- """
+ # press yes
+ self.cry.vba.press("a", holdsteps=10)
- # walk to the lab
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("u")
- vba.crystal.move("u")
+ # wait until mom is done talking
+ self.cry.text_wait()
- # walk into the lab
- vba.crystal.move("u")
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
- # talk to elm
- vba.crystal.text_wait()
+ return
- # "that I recently caught." yes/no
- vba.press("a", holdsteps=10) # yes
+ @skippable
+ def walk_into_new_bark_town(self):
+ """
+ Walk outside after talking with mom.
+ """
- # talk to elm some more
- vba.crystal.text_wait()
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("l")
+ self.cry.move("l")
- # talking isn't done yet..
- vba.crystal.text_wait()
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # walk outside
+ self.cry.move("d")
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ @skippable
+ def handle_elm(self, starter_choice):
+ """
+ Walk to Elm's Lab and get a starter.
+ """
+
+ # walk to the lab
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # walk into the lab
+ self.cry.move("u")
+
+ # talk to elm
+ self.cry.text_wait()
+
+ # "that I recently caught." yes/no
+ self.cry.vba.press("a", holdsteps=10) # yes
+
+ # talk to elm some more
+ self.cry.text_wait()
+
+ # talking isn't done yet..
+ self.cry.text_wait()
+ self.cry.text_wait()
+ self.cry.text_wait()
- # move toward the pokeballs
- vba.crystal.move("r")
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
- # move to cyndaquil
- vba.crystal.move("r")
+ # move toward the pokeballs
+ self.cry.move("r")
- moves = 0
+ # move to cyndaquil
+ self.cry.move("r")
- if starter_choice.lower() == "cyndaquil":
moves = 0
- if starter_choice.lower() == "totodile":
- moves = 1
- else:
- moves = 2
- for each in range(0, moves):
- vba.crystal.move("r")
+ if starter_choice.lower() == "cyndaquil":
+ moves = 0
+ if starter_choice.lower() == "totodile":
+ moves = 1
+ else:
+ moves = 2
- # face the pokeball
- vba.crystal.move("u")
+ for each in range(0, moves):
+ self.cry.move("r")
- # select it
- vba.press("a", holdsteps=10, aftersteps=0)
+ # face the pokeball
+ self.cry.move("u")
- # wait for the image to pop up
- vba.crystal.text_wait()
+ # select it
+ self.cry.vba.press("a", holdsteps=10, aftersteps=0)
- # wait for the image to close
- vba.crystal.text_wait()
+ # wait for the image to pop up
+ self.cry.text_wait()
- # wait for the yes/no box
- vba.crystal.text_wait()
+ # wait for the image to close
+ self.cry.text_wait()
- # press yes
- vba.press("a", holdsteps=10, aftersteps=0)
+ # wait for the yes/no box
+ self.cry.text_wait()
- # wait for elm to talk a bit
- vba.crystal.text_wait()
+ # press yes
+ self.cry.vba.press("a", holdsteps=10, aftersteps=0)
- # TODO: why didn't that finish his talking?
- vba.crystal.text_wait()
+ # wait for elm to talk a bit
+ self.cry.text_wait()
- # give a nickname? yes/no
- vba.press("d", holdsteps=10, aftersteps=0) # move to "no"
- vba.press("a", holdsteps=10, aftersteps=0) # no
+ # TODO: why didn't that finish his talking?
+ self.cry.text_wait()
- # TODO: why didn't this wait until he was completely done?
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # give a nickname? yes/no
+ self.cry.vba.press("d", holdsteps=10, aftersteps=0) # move to "no"
+ self.cry.vba.press("a", holdsteps=10, aftersteps=0) # no
- # get the phone number
- vba.crystal.text_wait()
+ # TODO: why didn't this wait until he was completely done?
+ self.cry.text_wait()
+ self.cry.text_wait()
- # talk with elm a bit more
- vba.crystal.text_wait()
+ # get the phone number
+ self.cry.text_wait()
- # TODO: and again.. wtf?
- vba.crystal.text_wait()
+ # talk with elm a bit more
+ self.cry.text_wait()
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ # TODO: and again.. wtf?
+ self.cry.text_wait()
- # move down
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
- # move into the researcher's line of sight
- vba.crystal.move("d")
+ # move down
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
- # get the potion from the person
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # move into the researcher's line of sight
+ self.cry.move("d")
- # wait for the script to end
- vba.crystal.wait_for_script_running()
+ # get the potion from the person
+ self.cry.text_wait()
+ self.cry.text_wait()
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ # wait for the script to end
+ self.cry.wait_for_script_running()
- # go outside
- vba.crystal.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
- return
+ # go outside
+ self.cry.move("d")
-@skippable
-def new_bark_level_grind(level):
- """
- Do level grinding in New Bark.
+ return
- Starting just outside of Elm's Lab, do some level grinding until the first
- partymon level is equal to the given value..
- """
+ @skippable
+ def new_bark_level_grind(self, level):
+ """
+ Do level grinding in New Bark.
- # walk to the grass area
- new_bark_level_grind_walk_to_grass(skip=False)
+ Starting just outside of Elm's Lab, do some level grinding until the
+ first partymon level is equal to the given value..
+ """
- # TODO: walk around in grass, handle battles
- walk = ["d", "d", "u", "d", "u", "d"]
- for direction in walk:
- vba.crystal.move(direction)
+ # walk to the grass area
+ self.new_bark_level_grind_walk_to_grass(skip=False)
- # wait for wild battle to completely start
- vba.crystal.text_wait()
+ # TODO: walk around in grass, handle battles
+ walk = ["d", "d", "u", "d", "u", "d"]
+ for direction in walk:
+ self.cry.move(direction)
- attacks = 5
+ # wait for wild battle to completely start
+ self.cry.text_wait()
- while attacks > 0:
- # FIGHT
- vba.press("a", holdsteps=10, aftersteps=1)
+ attacks = 5
- # wait to select a move
- vba.crystal.text_wait()
+ while attacks > 0:
+ # FIGHT
+ self.cry.vba.press("a", holdsteps=10, aftersteps=1)
- # SCRATCH
- vba.press("a", holdsteps=10, aftersteps=1)
+ # wait to select a move
+ self.cry.text_wait()
- # wait for the move to be over
- vba.crystal.text_wait()
+ # SCRATCH
+ self.cry.vba.press("a", holdsteps=10, aftersteps=1)
- hp = ((vba.get_memory_at(0xd218) << 8) | vba.get_memory_at(0xd217))
- print "enemy hp is: " + str(hp)
+ # wait for the move to be over
+ self.cry.text_wait()
- if hp == 0:
- print "enemy hp is zero, exiting"
- break
- else:
+ hp = ((self.cry.vba.get_memory_at(0xd218) << 8) | self.cry.vba.get_memory_at(0xd217))
print "enemy hp is: " + str(hp)
- attacks = attacks - 1
-
- while vba.get_memory_at(0xd22d) != 0:
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # wait for the map to finish loading
- vba.nstep(50)
-
- print "okay, back in the overworld"
-
- # move up
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
-
- # move into new bark town
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # move up
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
-
- # move to the door
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # walk in
- vba.crystal.move("u")
-
- # move up to the healing thing
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # face it
- vba.crystal.move("u")
-
- # interact
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # wait for yes/no box
- vba.crystal.text_wait()
-
- # press yes
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # TODO: when is healing done?
-
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
-
- # wait for it to be really really done
- vba.nstep(50)
-
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # move to the door
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- # walk out
- vba.crystal.move("d")
-
- # check partymon1 level
- if vba.get_memory_at(0xdcfe) < level:
- new_bark_level_grind(level, skip=False)
- else:
- return
+ if hp == 0:
+ print "enemy hp is zero, exiting"
+ break
+ else:
+ print "enemy hp is: " + str(hp)
+
+ attacks = attacks - 1
+
+ while self.cry.vba.get_memory_at(0xd22d) != 0:
+ self.cry.vba.press("a", holdsteps=10, aftersteps=1)
+
+ # wait for the map to finish loading
+ self.cry.vba.nstep(50)
+
+ print "okay, back in the overworld"
+
+ # move up
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # move into new bark town
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # move up
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # move to the door
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # walk in
+ self.cry.move("u")
+
+ # move up to the healing thing
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # face it
+ self.cry.move("u")
+
+ # interact
+ self.cry.vba.press("a", holdsteps=10, aftersteps=1)
+
+ # wait for yes/no box
+ self.cry.text_wait()
+
+ # press yes
+ self.cry.vba.press("a", holdsteps=10, aftersteps=1)
+
+ # TODO: when is healing done?
+
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
+
+ # wait for it to be really really done
+ self.cry.vba.nstep(50)
+
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # move to the door
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+
+ # walk out
+ self.cry.move("d")
+
+ # check partymon1 level
+ if self.cry.vba.get_memory_at(0xdcfe) < level:
+ self.new_bark_level_grind(level, skip=False)
+ else:
+ return
-@skippable
-def new_bark_level_grind_walk_to_grass():
- """
- Move to just above the grass from outside Elm's lab.
- """
+ @skippable
+ def new_bark_level_grind_walk_to_grass(self):
+ """
+ Move to just above the grass from outside Elm's lab.
+ """
+
+ self.cry.move("d")
+ self.cry.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # move to route 29 past the trees
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # move to just above the grass
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ self.cry.move("d")
+ self.cry.move("d")
+
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # move to route 29 past the trees
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # move to just above the grass
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+
+def main():
+ runner = SpeedRunner()
+ runner.setup()
+ return runner.main()
if __name__ == "__main__":
main()