diff options
Diffstat (limited to 'pokemontools/vba/autoplayer.py')
-rw-r--r-- | pokemontools/vba/autoplayer.py | 795 |
1 files changed, 427 insertions, 368 deletions
diff --git a/pokemontools/vba/autoplayer.py b/pokemontools/vba/autoplayer.py index 9aa8f4a..6aa9494 100644 --- a/pokemontools/vba/autoplayer.py +++ b/pokemontools/vba/autoplayer.py @@ -4,492 +4,551 @@ 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") +import pokemontools.configuration as configuration - new_bark_level_grind(10, skip=False) +# bring in the emulator and basic tools +import vba as _vba def skippable(func): """ Makes a function skippable. - Saves the state before and after the function runs. - Pass "skip=True" to the function to load the previous save - state from when the function finished. + Saves the state before and after the function runs. Pass "skip=True" to the + function to load the previous save state from when the function finished. """ def wrapped_function(*args, **kwargs): + self = args[0] skip = True + override = True if "skip" in kwargs.keys(): skip = kwargs["skip"] del kwargs["skip"] + if "override" in kwargs.keys(): + override = kwargs["override"] + del kwargs["override"] + # override skip if there's no save if skip: full_name = func.__name__ + "-end.sav" - if not os.path.exists(os.path.join(vba.save_state_path, full_name)): + if not os.path.exists(os.path.join(self.config.save_state_path, full_name)): skip = False return_value = None if not skip: - vba.save_state(func.__name__ + "-start", override=True) + if override: + self.cry.save_state(func.__name__ + "-start", override=override) + return_value = func(*args, **kwargs) - vba.save_state(func.__name__ + "-end", override=True) + + if override: + self.cry.save_state(func.__name__ + "-end", override=override) elif skip: - vba.set_state(vba.load_state(func.__name__ + "-end")) + self.cry.vba.state = self.cry.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 __init__(self, cry=None, config=None): + self.cry = cry - # skip the ditto sequence - vba.press("a") - vba.nstep(100) + if not config: + config = configuration.Config() - # skip the start screen - vba.press("start") - vba.nstep(100) + self.config = config - # click "new game" - vba.press("a", holdsteps=50, aftersteps=1) + def setup(self): + if not self.cry: + self.cry = _vba.crystal(config=self.config) - # skip text up to "Are you a boy? Or are you a girl?" - vba.crystal.text_wait() + def main(self): + """ + Start the game. + """ + # get past the opening sequence + self.skip_intro(skip=True) - # select "Boy" - vba.press("a", holdsteps=50, aftersteps=1) + # walk to mom and handle her text + self.handle_mom(skip=True) - # text until "What time is it?" - vba.crystal.text_wait() + # walk outside into new bark town + self.walk_into_new_bark_town(skip=True) - # select 10 o'clock - vba.press("a", holdsteps=50, aftersteps=1) + # walk to elm and do whatever he wants + self.handle_elm("totodile", skip=True) - # yes i mean it - vba.press("a", holdsteps=50, aftersteps=1) + self.new_bark_level_grind(17, skip=False) - # "How many minutes?" 0 min. - vba.press("a", holdsteps=50, aftersteps=1) + @skippable + def skip_intro(self, stop_at_name_selection=False): + """ + Skip the game boot intro sequence. + """ - # "Who! 0 min.?" yes/no select yes - vba.press("a", holdsteps=50, aftersteps=1) + # copyright sequence + self.cry.nstep(400) - # read text until name selection - vba.crystal.text_wait() + # skip the ditto sequence + self.cry.vba.press("a") + self.cry.nstep(100) - # select "Chris" - vba.press("d", holdsteps=10, aftersteps=1) - vba.press("a", holdsteps=50, aftersteps=1) + # skip the start screen + self.cry.vba.press("start") + self.cry.nstep(100) - def overworldcheck(): - """ - A basic check for when the game starts. - """ - return vba.get_memory_at(0xcfb1) != 0 + # click "new game" + self.cry.vba.press("a", hold=50, after=1) - # go until the introduction is done - vba.crystal.text_wait(callback=overworldcheck) + # skip text up to "Are you a boy? Or are you a girl?" + self.cry.text_wait() - return + # select "Boy" + self.cry.vba.press("a", hold=50, after=1) -@skippable -def handle_mom(): - """ - Walk to mom. Handle her speech and questions. - """ + # text until "What time is it?" + self.cry.text_wait() - vba.crystal.move("r") - vba.crystal.move("r") - vba.crystal.move("r") - vba.crystal.move("r") + # select 10 o'clock + self.cry.vba.press("a", hold=50, after=1) - vba.crystal.move("u") - vba.crystal.move("u") - vba.crystal.move("u") + # yes i mean it + self.cry.vba.press("a", hold=50, after=1) - vba.crystal.move("d") - vba.crystal.move("d") + # "How many minutes?" 0 min. + self.cry.vba.press("a", hold=50, after=1) - # move into mom's line of sight - vba.crystal.move("d") + # "Who! 0 min.?" yes/no select yes + self.cry.vba.press("a", hold=50, after=1) - # let mom talk until "What day is it?" - vba.crystal.text_wait() + # read text until name selection + self.cry.text_wait() - # "What day is it?" Sunday - vba.press("a", holdsteps=10) # Sunday + if stop_at_name_selection: + return - vba.crystal.text_wait() + # select "Chris" + self.cry.vba.press("d", hold=10, after=1) + self.cry.vba.press("a", hold=50, after=1) - # "SUNDAY, is it?" yes/no - vba.press("a", holdsteps=10) # yes + def overworldcheck(): + """ + A basic check for when the game starts. + """ + return self.cry.vba.memory[0xcfb1] != 0 - vba.crystal.text_wait() + # go until the introduction is done + self.cry.text_wait(callback=overworldcheck) - # "Is it Daylight Saving Time now?" yes/no - vba.press("a", holdsteps=10) # yes + return - vba.crystal.text_wait() + @skippable + def handle_mom(self): + """ + Walk to mom. Handle her speech and questions. + """ - # "AM DST, is that OK?" yes/no - vba.press("a", holdsteps=10) # yes + self.cry.move("r") + self.cry.move("r") + self.cry.move("r") + self.cry.move("r") - # text until "know how to use the PHONE?" yes/no - vba.crystal.text_wait() + self.cry.move("u") + self.cry.move("u") + self.cry.move("u") - # press yes - vba.press("a", holdsteps=10) + self.cry.move("d") + self.cry.move("d") - # wait until mom is done talking - vba.crystal.text_wait() + # move into mom's line of sight + self.cry.move("d") - # wait until the script is done running - vba.crystal.wait_for_script_running() + # let mom talk until "What day is it?" + self.cry.text_wait() - return + # "What day is it?" Sunday + self.cry.vba.press("a", hold=10) # Sunday -@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") + # "SUNDAY, is it?" yes/no + self.cry.vba.press("a", hold=10) # yes - # walk outside - vba.crystal.move("d") + self.cry.text_wait() -@skippable -def handle_elm(starter_choice): - """ - Walk to Elm's Lab and get a starter. - """ + # "Is it Daylight Saving Time now?" yes/no + self.cry.vba.press("a", hold=10) # yes - # 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") + self.cry.text_wait() - # walk into the lab - vba.crystal.move("u") + # "AM DST, is that OK?" yes/no + self.cry.vba.press("a", hold=10) # yes - # talk to elm - vba.crystal.text_wait() + # text until "know how to use the PHONE?" yes/no + self.cry.text_wait() - # "that I recently caught." yes/no - vba.press("a", holdsteps=10) # yes + # press yes + self.cry.vba.press("a", hold=10) - # talk to elm some more - vba.crystal.text_wait() + # wait until mom is done talking + self.cry.text_wait() - # talking isn't done yet.. - vba.crystal.text_wait() - vba.crystal.text_wait() - vba.crystal.text_wait() + # wait until the script is done running + self.cry.wait_for_script_running() - # wait until the script is done running - vba.crystal.wait_for_script_running() + return - # move toward the pokeballs - vba.crystal.move("r") + @skippable + def walk_into_new_bark_town(self): + """ + Walk outside after talking with mom. + """ - # move to cyndaquil - vba.crystal.move("r") + self.cry.move("d") + self.cry.move("d") + self.cry.move("d") + self.cry.move("l") + self.cry.move("l") - moves = 0 + # walk outside + self.cry.move("d") + + @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", hold=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() + + # wait until the script is done running + self.cry.wait_for_script_running() + + # move toward the pokeballs + self.cry.move("r") + + # 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 + elif 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", hold=10, after=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", hold=10, after=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", hold=10, after=0) # move to "no" + self.cry.vba.press("a", hold=10, after=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() + # wait until the script is done running + self.cry.wait_for_script_running() - # move down - vba.crystal.move("d") - vba.crystal.move("d") - vba.crystal.move("d") - vba.crystal.move("d") + # move down + self.cry.move("d") + self.cry.move("d") + self.cry.move("d") + self.cry.move("d") - # move into the researcher's line of sight - vba.crystal.move("d") + # move into the researcher's line of sight + self.cry.move("d") - # get the potion from the person - vba.crystal.text_wait() - vba.crystal.text_wait() + # get the potion from the person + self.cry.text_wait() + self.cry.text_wait() - # wait for the script to end - vba.crystal.wait_for_script_running() + # wait for the script to end + self.cry.wait_for_script_running() - vba.crystal.move("d") - vba.crystal.move("d") - vba.crystal.move("d") + self.cry.move("d") + self.cry.move("d") + self.cry.move("d") - # go outside - vba.crystal.move("d") + # go outside + self.cry.move("d") - return + return -@skippable -def new_bark_level_grind(level): - """ - Do level grinding in New Bark. + @skippable + def new_bark_level_grind(self, level, walk_to_grass=True): + """ + Do level grinding in New Bark. - Starting just outside of Elm's Lab, do some level grinding until the first - partymon level is equal to the given value.. - """ + Starting just outside of Elm's Lab, do some level grinding until the + first partymon level is equal to the given value.. + """ - # walk to the grass area - new_bark_level_grind_walk_to_grass(skip=False) + # walk to the grass area + if walk_to_grass: + self.new_bark_level_grind_walk_to_grass(skip=False) - # TODO: walk around in grass, handle battles - walk = ["d", "d", "u", "d", "u", "d"] - for direction in walk: - vba.crystal.move(direction) + last_direction = "u" - # wait for wild battle to completely start - vba.crystal.text_wait() + # walk around in the grass until a battle happens + while self.cry.vba.memory[0xd22d] == 0: + if last_direction == "u": + direction = "d" + else: + direction = "u" - attacks = 5 + self.cry.move(direction) - while attacks > 0: - # FIGHT - vba.press("a", holdsteps=10, aftersteps=1) + last_direction = direction - # wait to select a move - vba.crystal.text_wait() + # wait for wild battle to completely start + self.cry.text_wait() - # SCRATCH - vba.press("a", holdsteps=10, aftersteps=1) + attacks = 5 - # wait for the move to be over - vba.crystal.text_wait() + while attacks > 0: + # FIGHT + self.cry.vba.press("a", hold=10, after=1) - hp = ((vba.get_memory_at(0xd218) << 8) | vba.get_memory_at(0xd217)) - print "enemy hp is: " + str(hp) + # wait to select a move + self.cry.text_wait() - if hp == 0: - print "enemy hp is zero, exiting" - break - else: + # SCRATCH + self.cry.vba.press("a", hold=10, after=1) + + # wait for the move to be over + self.cry.text_wait() + + hp = ((self.cry.vba.memory[0xd218] << 8) | self.cry.vba.memory[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.memory[0xd22d] != 0: + self.cry.vba.press("a", hold=10, after=1) + + # wait for the map to finish loading + self.cry.vba.step(count=50) + + # This is used to handle any additional textbox that might be up on the + # screen. The debug parameter is set to True so that max_wait is + # enabled. This might be a textbox that is still waiting around because + # of some faint during the battle. I am not completely sure why this + # happens. + self.cry.text_wait(max_wait=30, debug=True) + + print "okay, back in the overworld" + + cur_hp = ((self.cry.vba.memory[0xdd01] << 8) | self.cry.vba.memory[0xdd02]) + move_pp = self.cry.vba.memory[0xdcf6] # move 1 pp + + # if pokemon health is >20, just continue + # if move 1 PP is 0, just continue + if cur_hp > 20 and move_pp > 5 and self.cry.vba.memory[0xdcfe] < level: + self.cry.move("u") + return self.new_bark_level_grind(level, walk_to_grass=False, skip=False) + + # 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", hold=10, after=1) + + # wait for yes/no box + self.cry.text_wait() + + # press yes + self.cry.vba.press("a", hold=10, after=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.step(count=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.memory[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") + + self.cry.move("l") + self.cry.move("l") + + 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") + + # 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() |