#!/usr/bin/env python
# vim: set expandtab tabstop=4 shiftwidth=4:

# This is an automated "solver" to get energy tank #3 in Chozodia,
# as defined by http://metroid.retropixel.net/games/metroidzm/items5.php#chozodia
# Starts off loading the most recent save state, which expects
# to be in the lower-left-hand corner, underwater, right before
# all the crazy shinespark stuff happens.
#
# Example of this running: https://www.youtube.com/watch?v=NB_lNT82DKk

import argparse
import subprocess

parser = argparse.ArgumentParser(
    description="Metroid: Zero Mission energy tank #3 in Chozodia")

parser.add_argument('-v', '--verbose',
    action='store_true',
    help='Show full xdotool command')

args = parser.parse_args()

initial_pause = 2
commands = ['xdotool']
load = 'ctrl+l'
right = 'KP_Right'
left = 'KP_Left'
up = 'KP_Up'
down = 'KP_Down'
jump = 'space'

def key(button, length):
    keydown(button)
    sleep(length)
    keyup(button)

def keydown(button):
    global commands
    commands.extend(['keydown', button])

def keyup(button):
    global commands
    commands.extend(['keyup', button])

def sleep(length):
    commands.extend(['sleep', str(length)])

def shinespark(direction, duration, do_renew=True):
    global jump
    sleep(0.1)
    keydown(jump)
    sleep(0.4)
    keydown(direction)
    sleep(duration)
    keyup(jump)
    keyup(direction)
    if do_renew:
        renew()

def renew():
    global down
    key(down, 0.1)

# Initial Delay
sleep(initial_pause)

# Load
key(load, 0.2)
sleep(0.2)

# Run right to activate shinespark
key(right, 2.75)
renew()

# Jump up to the next platform
keydown(left)
keydown(jump)
sleep(0.4)
keyup(jump)
sleep(1)
keyup(left)

# Activate again
shinespark(left, 0.3)

# Jump up again (pt1, left)
keydown(left)
keydown(jump)
sleep(0.45)
keyup(jump)
keyup(left)

# (pt2, right)
sleep(0.1)
keydown(right)
keydown(jump)
sleep(0.2)
keyup(right)
sleep(0.2)
keyup(jump)

# (pt3, right again)
sleep(0.1)
keydown(right)
keydown(jump)
sleep(0.1)
keyup(right)
keyup(jump)
sleep(0.1)

# Shinespark a tiny bit
shinespark(right, 0.2)

# And hop up to the next one, all wobbly-like (pt1)
keydown(left)
keydown(jump)
sleep(0.2)
keyup(left)
keydown(right)
sleep(0.1)
keyup(right)
keydown(left)
sleep(0.1)
keyup(left)
keyup(jump)

# (pt2)
sleep(0.1)
keydown(right)
keydown(jump)
sleep(0.2)
keyup(right)
keydown(left)
sleep(0.2)
keyup(left)
keyup(jump)

# Shinespark left
shinespark(left, 0.2)

# Now hop up to the exit corridor
keydown(right)
keydown(jump)
sleep(0.5)
keyup(jump)
sleep(0.1)
keydown(jump)
sleep(0.3)
keyup(jump)

# ... and keep running a bit
sleep(0.8)
keyup(right)

# Shinespark up the ramp towards the exit
shinespark(right, 0.5)

# Run through the door (allow for a bit more time due to
# the room-changing animation)
keydown(right)
sleep(1.1)

# still running, screw-attack through the blocks
keydown(jump)
sleep(0.3)
keyup(jump)
sleep(0.4)
keyup(right)

# While falling, hug the wall to the left
sleep(0.2)
key(left, 0.5)

# Rather tight timing here, but shinespark the instant
# we land, in a little alcove
sleep(0.5)
shinespark(left, 0.2)

# Run over to the right wall (nearly there!)
key(right, 1.2)

# Finally, shinespark over to the right once we hit
# the platform we've been aiming for all along
sleep(0.5)
shinespark(right, 0.5, do_renew=False)

# Run!
print('')
print('xdotool arg count: {}'.format(len(commands)-1))
if args.verbose:
    print('Full command:')
    print('   {}'.format(' '.join(commands)))
    print('')
print('Focus game within {} seconds!'.format(initial_pause))
subprocess.run(commands)
print('')
print('Done!')

