mirror of https://github.com/pret/pokecrystal.git
465 lines
10 KiB
Python
465 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
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 to elm and do whatever he wants
|
|
handle_elm("totodile")
|
|
|
|
new_bark_level_grind(10, skip=False)
|
|
|
|
def skippable(func):
|
|
"""
|
|
Makes a function skippable by saving 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):
|
|
skip = True
|
|
|
|
if "skip" in kwargs.keys():
|
|
skip = kwargs["skip"]
|
|
del kwargs["skip"]
|
|
|
|
# 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)):
|
|
skip = False
|
|
|
|
return_value = None
|
|
|
|
if not skip:
|
|
vba.save_state(func.__name__ + "-start", override=True)
|
|
return_value = func(*args, **kwargs)
|
|
vba.save_state(func.__name__ + "-end", override=True)
|
|
elif skip:
|
|
vba.set_state(vba.load_state(func.__name__ + "-end"))
|
|
|
|
return return_value
|
|
return wrapped_function
|
|
|
|
@skippable
|
|
def skip_intro():
|
|
"""
|
|
Skip the game boot intro sequence.
|
|
"""
|
|
# copyright sequence
|
|
vba.nstep(400)
|
|
|
|
# skip the ditto sequence
|
|
vba.press("a")
|
|
vba.nstep(100)
|
|
|
|
# skip the start screen
|
|
vba.press("start")
|
|
vba.nstep(100)
|
|
|
|
# click "new game"
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# Are you a boy? Or are you a girl?
|
|
vba.nstep(145)
|
|
|
|
# pick "boy"
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# ....
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
|
|
# What time is it?
|
|
vba.crystal.text_wait()
|
|
|
|
# DAY 10 o'clock
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# WHAT? DAY 10 o'clock? YES/NO.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# How many minutes? 0 min.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# Whoa! 0 min.? YES/NO.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# People call me
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# tures that we call
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
|
|
# everything about pokemon yet.
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# Now, what did you say your name was?
|
|
vba.crystal.text_wait()
|
|
|
|
# move down to "CHRIS"
|
|
vba.press("d")
|
|
vba.nstep(50)
|
|
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# wait until playable
|
|
# could be 150, but it sometimes takes longer??
|
|
vba.nstep(200)
|
|
|
|
return
|
|
|
|
@skippable
|
|
def handle_mom():
|
|
"""
|
|
Walk to mom. Handle her speech and questions.
|
|
"""
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("u"); vba.nstep(50)
|
|
vba.press("u"); vba.nstep(50)
|
|
vba.press("u"); vba.nstep(50)
|
|
vba.press("u"); vba.nstep(50)
|
|
|
|
# wait for next map to load
|
|
vba.nstep(50)
|
|
|
|
vba.press("d"); vba.nstep(50)
|
|
vba.press("d"); vba.nstep(50)
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
# walk into mom's line of sight
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
vba.nstep(50)
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# What day is it? SUNDAY.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# SUNDAY, is it? YES/NO
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
vba.nstep(200)
|
|
|
|
# is it DST now? YES/NO.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# 10:06 AM DST, is that OK? YES/NO.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# know how to use the PHONE? YES/NO.
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.press("a", holdsteps=50, aftersteps=1)
|
|
|
|
# have to wait for her to move back :(
|
|
vba.nstep(50)
|
|
|
|
# face down
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
return
|
|
|
|
@skippable
|
|
def handle_elm(starter_choice):
|
|
"""
|
|
Walk to Elm's Lab and get a starter.
|
|
"""
|
|
|
|
# walk down
|
|
vba.press("d"); vba.nstep(50)
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
# face left
|
|
vba.press("l"); vba.nstep(50)
|
|
|
|
# walk left
|
|
vba.press("l"); vba.nstep(50)
|
|
vba.press("l"); vba.nstep(50)
|
|
|
|
# face down
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
# walk down
|
|
vba.press("d"); vba.nstep(50)
|
|
|
|
# walk down to warp to outside
|
|
vba.press("d"); vba.nstep(50)
|
|
vba.nstep(10)
|
|
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
|
|
vba.press("u", holdsteps=10, aftersteps=50)
|
|
vba.press("u", holdsteps=10, aftersteps=50)
|
|
|
|
# warp into elm's lab (bottom left warp)
|
|
vba.press("u", holdsteps=5, aftersteps=50)
|
|
|
|
# let the script play
|
|
vba.nstep(200)
|
|
|
|
vba.crystal.text_wait()
|
|
# I needed to ask you a fa
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.nstep(50)
|
|
|
|
# YES/NO.
|
|
vba.press("a")
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.press("a")
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.press("a")
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.crystal.text_wait()
|
|
|
|
for x in range(0, 8): # was 15
|
|
vba.crystal.text_wait()
|
|
|
|
vba.nstep(50)
|
|
vba.press("a")
|
|
vba.nstep(100)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# Go on! Pick one.
|
|
vba.nstep(100)
|
|
vba.press("a"); vba.nstep(50)
|
|
|
|
vba.press("r"); vba.nstep(50)
|
|
vba.press("r"); vba.nstep(50)
|
|
|
|
right = 0
|
|
if starter_choice in [1, "cyndaquil"]:
|
|
right = 0
|
|
elif starter_choice in [2, "totodile"]:
|
|
right = 1
|
|
elif starter_choice in [3, "chikorita"]:
|
|
right = 2
|
|
else:
|
|
raise Exception("bad starter")
|
|
|
|
for each in range(0, right):
|
|
vba.press("r"); vba.nstep(50)
|
|
|
|
# look up
|
|
vba.press("u", holdsteps=5, aftersteps=50)
|
|
|
|
# get menu
|
|
vba.press("a", holdsteps=5, aftersteps=50)
|
|
|
|
# let the image show
|
|
vba.nstep(100)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# YES/NO.
|
|
vba.press("a")
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# received.. music is playing.
|
|
vba.press("a")
|
|
vba.crystal.text_wait()
|
|
|
|
# YES/NO (nickname)
|
|
vba.crystal.text_wait()
|
|
|
|
vba.press("b")
|
|
|
|
# get back to elm
|
|
vba.nstep(100)
|
|
vba.nstep(100)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# phone number..
|
|
vba.press("a")
|
|
vba.crystal.text_wait()
|
|
vba.nstep(100)
|
|
vba.press("a")
|
|
vba.nstep(300)
|
|
vba.press("a")
|
|
vba.nstep(100)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
# I'm counting on you!
|
|
vba.nstep(200)
|
|
vba.press("a")
|
|
vba.nstep(50)
|
|
|
|
# look down
|
|
vba.press("d", holdsteps=5, aftersteps=50)
|
|
|
|
# walk down
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.press("a")
|
|
vba.nstep(50)
|
|
vba.press("a")
|
|
|
|
vba.crystal.text_wait()
|
|
vba.crystal.text_wait()
|
|
|
|
vba.press("a")
|
|
vba.nstep(50)
|
|
|
|
vba.crystal.text_wait()
|
|
vba.press("a")
|
|
|
|
vba.nstep(100)
|
|
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
|
|
# step outside
|
|
vba.nstep(40)
|
|
|
|
@skippable
|
|
def new_bark_level_grind(level):
|
|
"""
|
|
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)
|
|
|
|
# TODO: walk around in grass, handle battles
|
|
# TODO: heal at the lab, then repeat entire function
|
|
|
|
@skippable
|
|
def new_bark_level_grind_travel_to_grass():
|
|
"""
|
|
Move to just above the grass.
|
|
"""
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
vba.press("l", holdsteps=10, aftersteps=50)
|
|
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
vba.press("d", holdsteps=10, aftersteps=50)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|