2018-01-01 18:11:11 +00:00
#!/usr/bin/env python3
2017-07-14 12:34:33 +00:00
import argparse
import os
import logging
import random
2017-07-14 16:24:23 +00:00
import textwrap
2017-11-19 21:00:26 +00:00
import sys
2017-07-14 12:34:33 +00:00
from Gui import guiMain
2017-12-17 05:25:46 +00:00
from Main import main
2017-12-02 14:21:04 +00:00
from Utils import is_bundled , close_console
2017-07-14 12:34:33 +00:00
2017-07-14 16:24:23 +00:00
class ArgumentDefaultsHelpFormatter ( argparse . RawTextHelpFormatter ) :
def _get_help_string ( self , action ) :
return textwrap . dedent ( action . help )
2017-11-04 18:23:57 +00:00
2017-12-17 05:25:46 +00:00
def start ( ) :
2017-07-14 16:24:23 +00:00
parser = argparse . ArgumentParser ( formatter_class = ArgumentDefaultsHelpFormatter )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --create_spoiler ' , help = ' Output a Spoiler File ' , action = ' store_true ' )
parser . add_argument ( ' --logic ' , default = ' noglitches ' , const = ' noglitches ' , nargs = ' ? ' , choices = [ ' noglitches ' , ' minorglitches ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select Enforcement of Item Requirements . ( default : % ( default ) s )
2017-11-19 01:43:37 +00:00
No Glitches :
2017-07-14 16:24:23 +00:00
Minor Glitches : May require Fake Flippers , Bunny Revival
2017-11-19 01:43:37 +00:00
and Dark Room Navigation .
2017-07-14 16:24:23 +00:00
''' )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --mode ' , default = ' open ' , const = ' open ' , nargs = ' ? ' , choices = [ ' standard ' , ' open ' , ' swordless ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select game mode . ( default : % ( default ) s )
2017-07-17 20:28:29 +00:00
Open : World starts with Zelda rescued .
2017-07-14 16:24:23 +00:00
Standard : Fixes Hyrule Castle Secret Entrance and Front Door
but may lead to weird rain state issues if you exit
through the Hyrule Castle side exits before rescuing
Zelda in a full shuffle .
Swordless : Like Open , but with no swords . Curtains in
Skull Woods and Agahnims Tower are removed ,
Agahnim \' s Tower barrier can be destroyed with
hammer . Misery Mire and Turtle Rock can be opened
without a sword . Hammer damages Ganon . Ether and
2017-11-19 01:43:37 +00:00
Bombos Tablet can be activated with Hammer ( and Book ) .
2017-07-14 16:24:23 +00:00
''' )
2017-08-01 17:07:44 +00:00
parser . add_argument ( ' --goal ' , default = ' ganon ' , const = ' ganon ' , nargs = ' ? ' , choices = [ ' ganon ' , ' pedestal ' , ' dungeons ' , ' triforcehunt ' , ' crystals ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select completion goal . ( default : % ( default ) s )
2017-11-19 01:43:37 +00:00
Ganon : Collect all crystals , beat Agahnim 2 then
2017-07-17 20:28:29 +00:00
defeat Ganon .
2017-08-01 17:07:44 +00:00
Crystals : Collect all crystals then defeat Ganon .
2017-07-14 16:24:23 +00:00
Pedestal : Places the Triforce at the Master Sword Pedestal .
2017-07-17 20:28:29 +00:00
All Dungeons : Collect all crystals , pendants , beat both
Agahnim fights and then defeat Ganon .
2017-11-19 01:43:37 +00:00
Triforce Hunt : Places 30 Triforce Pieces in the world , collect
20 of them to beat the game .
2017-07-14 16:24:23 +00:00
''' )
2017-11-10 10:18:09 +00:00
parser . add_argument ( ' --difficulty ' , default = ' normal ' , const = ' normal ' , nargs = ' ? ' , choices = [ ' easy ' , ' normal ' , ' hard ' , ' expert ' , ' insane ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select game difficulty . Affects available itempool . ( default : % ( default ) s )
2017-11-10 10:18:09 +00:00
Easy : An easy setting with extra equipment .
2017-07-14 16:24:23 +00:00
Normal : Normal difficulty .
2017-11-10 10:18:09 +00:00
Hard : A harder setting with less equipment and reduced health .
Expert : A harder yet setting with minimum equipment and health .
2017-11-19 01:43:37 +00:00
Insane : A setting with the absolute minimum in equipment and no extra health .
2017-11-10 10:18:09 +00:00
''' )
2017-11-19 01:36:42 +00:00
parser . add_argument ( ' --timer ' , default = ' none ' , const = ' normal ' , nargs = ' ? ' , choices = [ ' none ' , ' display ' , ' timed ' , ' timed-ohko ' , ' ohko ' , ' timed-countdown ' ] ,
2017-11-10 10:18:09 +00:00
help = ''' \
Select game timer setting . Affects available itempool . ( default : % ( default ) s )
None : No timer .
2017-11-12 00:08:41 +00:00
Display : Displays a timer but does not affect
the itempool .
2017-07-14 16:24:23 +00:00
Timed : Starts with clock at zero . Green Clocks
subtract 4 minutes ( Total : 20 ) , Blue Clocks
subtract 2 minutes ( Total : 10 ) , Red Clocks add
2 minutes ( Total : 10 ) . Winner is player with
lowest time at the end .
Timed OHKO : Starts clock at 10 minutes . Green Clocks add
5 minutes ( Total : 25 ) . As long as clock is at 0 ,
Link will die in one hit .
2017-11-19 01:36:42 +00:00
OHKO : Like Timed OHKO , but no clock items are present
and the clock is permenantly at zero .
2017-07-14 16:24:23 +00:00
Timed Countdown : Starts with clock at 40 minutes . Same clocks as
Timed mode . If time runs out , you lose ( but can
2017-11-19 01:43:37 +00:00
still keep playing ) .
2017-07-14 16:24:23 +00:00
''' )
2017-11-10 10:18:09 +00:00
parser . add_argument ( ' --progressive ' , default = ' on ' , const = ' normal ' , nargs = ' ? ' , choices = [ ' on ' , ' off ' , ' random ' ] ,
help = ''' \
Select progressive equipment setting . Affects available itempool . ( default : % ( default ) s )
On : Swords , Shields , Armor , and Gloves will
all be progressive equipment . Each subsequent
item of the same type the player finds will
upgrade that piece of equipment by one stage .
Off : Swords , Shields , Armor , and Gloves will not
be progressive equipment . Higher level items may
be found at any time . Downgrades are not possible .
Random : Swords , Shields , Armor , and Gloves will , per
category , be randomly progressive or not .
2017-11-19 01:43:37 +00:00
Link will die in one hit .
2017-11-10 10:18:09 +00:00
''' )
2017-11-11 21:28:34 +00:00
parser . add_argument ( ' --algorithm ' , default = ' balanced ' , const = ' balanced ' , nargs = ' ? ' , choices = [ ' freshness ' , ' flood ' , ' vt21 ' , ' vt22 ' , ' vt25 ' , ' vt26 ' , ' balanced ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
2017-10-15 17:52:42 +00:00
Select item filling algorithm . ( default : % ( default ) s
2017-11-11 21:28:34 +00:00
balanced : vt26 derivitive that aims to strike a balance between
the overworld heavy vt25 and the dungeon heavy vt26
algorithm .
2017-10-15 17:52:42 +00:00
vt26 : Shuffle items and place them in a random location
that it is not impossible to be in . This includes
2017-11-11 21:28:34 +00:00
dungeon keys and items .
2017-08-01 17:07:44 +00:00
vt25 : Shuffle items and place them in a random location
2017-07-14 16:24:23 +00:00
that it is not impossible to be in .
vt21 : Unbiased in its selection , but has tendency to put
Ice Rod in Turtle Rock .
vt22 : Drops off stale locations after 1 / 3 of progress
items were placed to try to circumvent vt21 \' s
shortcomings .
Freshness : Keep track of stale locations ( ones that cannot be
reached yet ) and decrease likeliness of selecting
them the more often they were found unreachable .
Flood : Push out items starting from Link \' s House and
slightly biased to placing progression items with
2017-11-19 01:43:37 +00:00
less restrictions .
2017-07-14 16:24:23 +00:00
''' )
2018-02-20 20:34:39 +00:00
parser . add_argument ( ' --shuffle ' , default = ' full ' , const = ' full ' , nargs = ' ? ' , choices = [ ' vanilla ' , ' simple ' , ' restricted ' , ' full ' , ' crossed ' , ' insanity ' , ' restricted_legacy ' , ' full_legacy ' , ' madness_legacy ' , ' insanity_legacy ' , ' dungeonsfull ' , ' dungeonssimple ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select Entrance Shuffling Algorithm . ( default : % ( default ) s )
2018-02-20 20:34:39 +00:00
Full : Mix cave and dungeon entrances freely while limiting
multi - entrance caves to one world .
2017-07-14 16:24:23 +00:00
Simple : Shuffle Dungeon Entrances / Exits between each other
and keep all 4 - entrance dungeons confined to one
location . All caves outside of death mountain are
2018-02-20 20:34:39 +00:00
shuffled in pairs and matched by original type .
2017-07-14 16:24:23 +00:00
Restricted : Use Dungeons shuffling from Simple but freely
connect remaining entrances .
2018-02-20 20:34:39 +00:00
Crossed : Mix cave and dungeon entrances freely while allowing
caves to cross between worlds .
Insanity : Decouple entrances and exits from each other and
shuffle them freely . Caves that used to be single
entrance will still exit to the same location from
which they are entered .
Vanilla : All entrances are in the same locations they were
in the base game .
Legacy shuffles preserve behavior from older versions of the
entrance randomizer including significant technical limitations .
2017-07-14 16:24:23 +00:00
The dungeon variants only mix up dungeons and keep the rest of
2017-11-19 01:43:37 +00:00
the overworld vanilla .
2017-07-14 16:24:23 +00:00
''' )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --rom ' , default = ' Zelda no Densetsu - Kamigami no Triforce (Japan).sfc ' , help = ' Path to an ALttP JAP(1.0) rom to use as a base. ' )
parser . add_argument ( ' --loglevel ' , default = ' info ' , const = ' info ' , nargs = ' ? ' , choices = [ ' error ' , ' info ' , ' warning ' , ' debug ' ] , help = ' Select level of logging for output. ' )
parser . add_argument ( ' --seed ' , help = ' Define seed number to generate. ' , type = int )
2017-07-14 16:24:23 +00:00
parser . add_argument ( ' --count ' , help = ''' \
Use to batch generate multiple seeds with same settings .
If - - seed is provided , it will be used for the first seed , then
used to derive the next seed ( i . e . generating 10 seeds with
- - seed given will produce the same 10 ( different ) roms each
2017-11-19 01:43:37 +00:00
time ) .
2017-07-14 16:24:23 +00:00
''' , type=int)
2018-01-05 22:53:29 +00:00
parser . add_argument ( ' --fastmenu ' , default = ' normal ' , const = ' normal ' , nargs = ' ? ' , choices = [ ' normal ' , ' instant ' , ' double ' , ' triple ' , ' quadruple ' , ' half ' ] ,
help = ''' \
Select the rate at which the menu opens and closes .
( default : % ( default ) s )
''' )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --quickswap ' , help = ' Enable quick item swapping with L and R. ' , action = ' store_true ' )
2017-11-28 10:54:14 +00:00
parser . add_argument ( ' --disablemusic ' , help = ' Disables game music. ' , action = ' store_true ' )
2017-10-28 22:34:37 +00:00
parser . add_argument ( ' --keysanity ' , help = ''' \
Keys ( and other dungeon items ) are no longer restricted to
their dungeons , but can be anywhere
''' , action= ' store_true ' )
2018-01-22 02:43:44 +00:00
parser . add_argument ( ' --custom ' , default = False , help = ' Not supported. ' )
parser . add_argument ( ' --customitemarray ' , default = False , help = ' Not supported. ' )
2017-07-14 16:24:23 +00:00
parser . add_argument ( ' --nodungeonitems ' , help = ''' \
Remove Maps and Compasses from Itempool , replacing them by
2017-11-19 01:43:37 +00:00
empty slots .
2017-07-14 16:24:23 +00:00
''' , action= ' store_true ' )
parser . add_argument ( ' --beatableonly ' , help = ''' \
Only check if the game is beatable with placement . Do not
ensure all locations are reachable . This only has an effect
2017-11-19 01:43:37 +00:00
on the restrictive algorithm currently .
2017-07-14 16:24:23 +00:00
''' , action= ' store_true ' )
2018-01-27 22:11:53 +00:00
# included for backwards compatibility
parser . add_argument ( ' --shuffleganon ' , help = argparse . SUPPRESS , action = ' store_true ' , default = True )
parser . add_argument ( ' --no-shuffleganon ' , help = ''' \
If set , the Pyramid Hole and Ganon ' s Tower are not
included entrance shuffle pool .
''' , action= ' store_false ' , dest= ' shuffleganon ' )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --heartbeep ' , default = ' normal ' , const = ' normal ' , nargs = ' ? ' , choices = [ ' normal ' , ' half ' , ' quarter ' , ' off ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select the rate at which the heart beep sound is played at
2017-11-19 01:43:37 +00:00
low health . ( default : % ( default ) s )
2017-07-14 16:24:23 +00:00
''' )
parser . add_argument ( ' --sprite ' , help = ''' \
Path to a sprite sheet to use for Link . Needs to be in
2017-11-19 01:43:37 +00:00
binary format and have a length of 0x7000 ( 28672 ) bytes ,
2017-07-14 16:24:23 +00:00
or 0x7078 ( 28792 ) bytes including palette data .
Alternatively , can be a ALttP Rom patched with a Link
2017-11-19 01:43:37 +00:00
sprite that will be extracted .
2017-07-14 16:24:23 +00:00
''' )
2017-07-14 12:34:33 +00:00
parser . add_argument ( ' --suppress_rom ' , help = ' Do not create an output rom file. ' , action = ' store_true ' )
parser . add_argument ( ' --gui ' , help = ' Launch the GUI ' , action = ' store_true ' )
2017-07-16 21:20:54 +00:00
parser . add_argument ( ' --jsonout ' , action = ' store_true ' , help = ''' \
2017-11-19 01:43:37 +00:00
Output . json patch to stdout instead of a patched rom . Used
2017-07-14 16:24:23 +00:00
for VT site integration , do not use otherwise .
''' )
2017-07-14 12:34:33 +00:00
args = parser . parse_args ( )
2017-12-17 05:25:46 +00:00
if is_bundled ( ) and len ( sys . argv ) == 1 :
2017-11-28 14:36:32 +00:00
# for the bundled builds, if we have no arguments, the user
# probably wants the gui. Users of the bundled build who want the command line
2017-11-16 03:06:53 +00:00
# interface shouuld specify at least one option, possibly setting a value to a
# default if they like all the defaults
2017-12-02 14:21:04 +00:00
close_console ( )
2017-11-16 03:06:53 +00:00
guiMain ( )
sys . exit ( 0 )
2017-07-14 12:34:33 +00:00
# ToDo: Validate files further than mere existance
if not args . jsonout and not os . path . isfile ( args . rom ) :
input ( ' Could not find valid base rom for patching at expected path %s . Please run with -h to see help for further information. \n Press Enter to exit. ' % args . rom )
2017-11-19 21:00:26 +00:00
sys . exit ( 1 )
2017-07-16 21:20:54 +00:00
if args . sprite is not None and not os . path . isfile ( args . sprite ) :
if not args . jsonout :
input ( ' Could not find link sprite sheet at given location. \n Press Enter to exit. ' % args . sprite )
2017-11-19 21:00:26 +00:00
sys . exit ( 1 )
2017-07-16 21:20:54 +00:00
else :
raise IOError ( ' Cannot find sprite file at %s ' % args . sprite )
2017-07-14 12:34:33 +00:00
# set up logger
loglevel = { ' error ' : logging . ERROR , ' info ' : logging . INFO , ' warning ' : logging . WARNING , ' debug ' : logging . DEBUG } [ args . loglevel ]
logging . basicConfig ( format = ' %(message)s ' , level = loglevel )
if args . gui :
guiMain ( args )
elif args . count is not None :
seed = args . seed
2017-12-17 05:25:46 +00:00
for _ in range ( args . count ) :
2017-07-14 12:34:33 +00:00
main ( seed = seed , args = args )
seed = random . randint ( 0 , 999999999 )
else :
main ( seed = args . seed , args = args )
2017-12-17 05:25:46 +00:00
if __name__ == ' __main__ ' :
start ( )