2018-01-01 18:11:11 +00:00
#!/usr/bin/env python3
2017-07-14 12:34:33 +00:00
import argparse
2019-12-16 11:52:30 +00:00
import copy
2017-07-14 16:24:23 +00:00
import textwrap
2019-12-16 11:52:30 +00:00
import shlex
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 )
2019-12-15 17:10:01 +00:00
def parse_arguments ( argv , no_defaults = False ) :
def defval ( value ) :
return value if not no_defaults else None
2017-11-04 18:23:57 +00:00
2019-12-16 11:52:30 +00:00
# we need to know how many players we have first
2019-12-30 19:43:43 +00:00
parser = argparse . ArgumentParser ( add_help = False )
2019-12-16 11:52:30 +00:00
parser . add_argument ( ' --multi ' , default = defval ( 1 ) , type = lambda value : min ( max ( int ( value ) , 1 ) , 255 ) )
multiargs , _ = parser . parse_known_args ( argv )
2017-07-14 16:24:23 +00:00
parser = argparse . ArgumentParser ( formatter_class = ArgumentDefaultsHelpFormatter )
2021-06-15 03:10:26 +00:00
parser . add_argument ( ' --logic ' , default = defval ( ' noglitches ' ) , const = ' noglitches ' , nargs = ' ? ' , choices = [ ' noglitches ' , ' minorglitches ' , ' owglitches ' , ' hybridglitches ' , ' nologic ' ] ,
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 .
2020-04-14 05:02:43 +00:00
Overworld Glitches : May require overworld glitches .
2021-06-15 03:10:26 +00:00
Hybrid Major Glitches : May require both overworld and underworld clipping .
2018-03-25 22:21:36 +00:00
No Logic : Distribute items without regard for
2020-04-14 05:02:43 +00:00
item requirements .
2017-07-14 16:24:23 +00:00
''' )
2021-03-22 20:14:19 +00:00
parser . add_argument ( ' --glitch_triforce ' , help = ' Allow glitching to Triforce from Ganon \' s room ' , action = ' store_true ' )
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --mode ' , default = defval ( ' open ' ) , const = ' open ' , nargs = ' ? ' , choices = [ ' standard ' , ' open ' , ' inverted ' ] ,
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 .
2019-07-27 13:13:13 +00:00
Inverted : Starting locations are Dark Sanctuary in West Dark
World or at Link ' s House, which is shuffled freely.
Requires the moon pearl to be Link in the Light World
instead of a bunny .
2017-07-14 16:24:23 +00:00
''' )
2020-06-03 00:19:16 +00:00
parser . add_argument ( ' --goal ' , default = defval ( ' ganon ' ) , const = ' ganon ' , nargs = ' ? ' ,
2021-03-14 07:38:02 +00:00
choices = [ ' ganon ' , ' pedestal ' , ' bosses ' , ' triforcehunt ' , ' localtriforcehunt ' , ' ganontriforcehunt ' , ' localganontriforcehunt ' , ' crystals ' , ' ganonpedestal ' ] ,
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 .
2020-10-15 22:41:03 +00:00
Ganon Pedestal : Pull the Master Sword Pedestal , then defeat Ganon .
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 .
2020-06-03 00:19:16 +00:00
Local Triforce Hunt : Places 30 Triforce Pieces in your world , collect
2020-06-26 14:18:53 +00:00
20 of them to beat the game .
Ganon Triforce Hunt : Places 30 Triforce Pieces in the world , collect
20 of them , then defeat Ganon .
Local Ganon Triforce Hunt : Places 30 Triforce Pieces in your world ,
collect 20 of them , then defeat Ganon .
2017-07-14 16:24:23 +00:00
''' )
2020-06-17 08:02:54 +00:00
parser . add_argument ( ' --triforce_pieces_available ' , default = defval ( 30 ) ,
2020-06-18 15:48:33 +00:00
type = lambda value : min ( max ( int ( value ) , 1 ) , 90 ) ,
2020-06-17 08:02:54 +00:00
help = ''' Set Triforce Pieces available in item pool. ''' )
2020-06-07 13:22:24 +00:00
parser . add_argument ( ' --triforce_pieces_required ' , default = defval ( 20 ) ,
2020-06-18 15:48:33 +00:00
type = lambda value : min ( max ( int ( value ) , 1 ) , 90 ) ,
2020-06-07 13:22:24 +00:00
help = ''' Set Triforce Pieces required to win a Triforce Hunt ''' )
parser . add_argument ( ' --difficulty ' , default = defval ( ' normal ' ) , const = ' normal ' , nargs = ' ? ' ,
2020-08-01 14:49:46 +00:00
choices = [ ' easy ' , ' normal ' , ' hard ' , ' expert ' ] ,
2017-07-14 16:24:23 +00:00
help = ''' \
Select game difficulty . Affects available itempool . ( default : % ( default ) s )
2020-08-01 14:49:46 +00:00
Easy : An easier setting with some equipment duplicated and increased health .
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 .
''' )
2020-06-07 13:22:24 +00:00
parser . add_argument ( ' --item_functionality ' , default = defval ( ' normal ' ) , const = ' normal ' , nargs = ' ? ' ,
2020-09-17 05:00:27 +00:00
choices = [ ' easy ' , ' normal ' , ' hard ' , ' expert ' ] ,
2020-06-07 13:22:24 +00:00
help = ''' \
2019-08-10 23:37:26 +00:00
Select limits on item functionality to increase difficulty . ( default : % ( default ) s )
2020-09-17 05:00:27 +00:00
Easy : Easy functionality . ( Medallions usable without sword )
2019-08-10 23:37:26 +00:00
Normal : Normal functionality .
Hard : Reduced functionality .
Expert : Greatly reduced functionality .
''' )
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --timer ' , default = defval ( ' 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
''' )
2020-10-28 23:20:59 +00:00
parser . add_argument ( ' --countdown_start_time ' , default = defval ( 10 ) , type = int ,
help = ''' Set amount of time, in minutes, to start with in Timed Countdown and Timed OHKO modes ''' )
parser . add_argument ( ' --red_clock_time ' , default = defval ( - 2 ) , type = int ,
help = ''' Set amount of time, in minutes, to add from picking up red clocks; negative removes time instead ''' )
parser . add_argument ( ' --blue_clock_time ' , default = defval ( 2 ) , type = int ,
help = ''' Set amount of time, in minutes, to add from picking up blue clocks; negative removes time instead ''' )
parser . add_argument ( ' --green_clock_time ' , default = defval ( 4 ) , type = int ,
help = ''' Set amount of time, in minutes, to add from picking up green clocks; negative removes time instead ''' )
2020-04-12 22:46:32 +00:00
parser . add_argument ( ' --dungeon_counters ' , default = defval ( ' default ' ) , const = ' default ' , nargs = ' ? ' , choices = [ ' default ' , ' on ' , ' pickup ' , ' off ' ] ,
help = ''' \
Select dungeon counter display settings . ( default : % ( default ) s )
( Note , since timer takes up the same space on the hud as dungeon
counters , timer settings override dungeon counter settings . )
Default : Dungeon counters only show when the compass is
picked up , or otherwise sent , only when compass
shuffle is turned on .
On : Dungeon counters are always displayed .
Pickup : Dungeon counters are shown when the compass is
picked up , even when compass shuffle is turned
off .
Off : Dungeon counters are never shown .
''' )
2021-08-09 07:15:41 +00:00
2020-08-25 15:44:03 +00:00
parser . add_argument ( ' --algorithm ' , default = defval ( ' balanced ' ) , const = ' balanced ' , nargs = ' ? ' ,
choices = [ ' freshness ' , ' flood ' , ' 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 .
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
''' )
2021-05-08 10:04:03 +00:00
parser . add_argument ( ' --shuffle ' , default = defval ( ' vanilla ' ) , const = ' vanilla ' , nargs = ' ? ' , choices = [ ' vanilla ' , ' simple ' , ' restricted ' , ' full ' , ' crossed ' , ' insanity ' , ' restricted_legacy ' , ' full_legacy ' , ' madness_legacy ' , ' insanity_legacy ' , ' dungeonsfull ' , ' dungeonssimple ' , ' dungeonscrossed ' ] ,
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
''' )
2021-01-29 21:27:42 +00:00
parser . add_argument ( ' --open_pyramid ' , default = defval ( ' auto ' ) , help = ''' \
Pre - opens the pyramid hole , this removes the Agahnim 2 requirement for it .
Depending on goal , you might still need to beat Agahnim 2 in order to beat ganon .
fast ganon goals are crystals , ganontriforcehunt , localganontriforcehunt , pedestalganon
auto - Only opens pyramid hole if the goal specifies a fast ganon , and entrance shuffle
is vanilla , dungeonssimple or dungeonsfull .
goal - Opens pyramid hole if the goal specifies a fast ganon .
yes - Always opens the pyramid hole .
no - Never opens the pyramid hole .
''' , choices=[ ' auto ' , ' goal ' , ' yes ' , ' no ' ])
2021-09-18 04:56:19 +00:00
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --loglevel ' , default = defval ( ' info ' ) , const = ' info ' , nargs = ' ? ' , choices = [ ' error ' , ' info ' , ' warning ' , ' debug ' ] , help = ' Select level of logging for output. ' )
2017-07-14 12:34:33 +00:00
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)
2021-08-09 07:15:41 +00:00
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --custom ' , default = defval ( False ) , help = ' Not supported. ' )
parser . add_argument ( ' --customitemarray ' , default = defval ( False ) , help = ' Not supported. ' )
2018-01-27 22:11:53 +00:00
# included for backwards compatibility
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --shuffleganon ' , help = argparse . SUPPRESS , action = ' store_true ' , default = defval ( True ) )
2018-01-27 22:11:53 +00:00
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 ' )
2021-01-03 12:13:59 +00:00
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 ( ' --gui ' , help = ' Launch the GUI ' , action = ' store_true ' )
2021-09-16 22:17:54 +00:00
2020-01-06 17:39:18 +00:00
parser . add_argument ( ' --enemizercli ' , default = defval ( ' EnemizerCLI/EnemizerCLI.Core ' ) )
2020-07-30 22:07:55 +00:00
parser . add_argument ( ' --shufflebosses ' , default = defval ( ' none ' ) , choices = [ ' none ' , ' basic ' , ' normal ' , ' chaos ' ,
2020-08-19 19:10:02 +00:00
" singularity " ] )
2021-09-12 23:32:32 +00:00
2020-07-05 01:12:28 +00:00
parser . add_argument ( ' --enemy_health ' , default = defval ( ' default ' ) ,
choices = [ ' default ' , ' easy ' , ' normal ' , ' hard ' , ' expert ' ] )
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --enemy_damage ' , default = defval ( ' default ' ) , choices = [ ' default ' , ' shuffled ' , ' chaos ' ] )
2019-12-30 02:03:53 +00:00
parser . add_argument ( ' --beemizer ' , default = defval ( 0 ) , type = lambda value : min ( max ( int ( value ) , 0 ) , 4 ) )
2020-08-23 19:38:21 +00:00
parser . add_argument ( ' --shop_shuffle ' , default = ' ' , help = ''' \
combine letters for options :
2020-11-24 01:37:02 +00:00
g : generate default inventories for light and dark world shops , and unique shops
f : generate default inventories for each shop individually
i : shuffle the default inventories of the shops around
2020-08-23 19:38:21 +00:00
p : randomize the prices of the items in shop inventories
u : shuffle capacity upgrades into the item pool
2021-01-09 17:04:14 +00:00
w : consider witch ' s hut like any other shop and shuffle/randomize it too
2020-08-23 19:38:21 +00:00
''' )
2020-09-20 02:35:45 +00:00
parser . add_argument ( ' --shuffle_prizes ' , default = defval ( ' g ' ) , choices = [ ' ' , ' g ' , ' b ' , ' gb ' ] )
2020-10-06 20:22:03 +00:00
parser . add_argument ( ' --sprite_pool ' , help = ''' \
Specifies a colon separated list of sprites used for random / randomonevent . If not specified , the full sprite pool is used . ''' )
2020-10-07 17:51:46 +00:00
parser . add_argument ( ' --dark_room_logic ' , default = ( ' Lamp ' ) , choices = [ " lamp " , " torches " , " none " ] , help = ''' \
For unlit dark rooms , require the Lamp to be considered in logic by default .
Torches means additionally easily accessible Torches that can be lit with Fire Rod are considered doable .
None means full traversal through dark rooms without tools is considered doable . ''' )
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --multi ' , default = defval ( 1 ) , type = lambda value : min ( max ( int ( value ) , 1 ) , 255 ) )
parser . add_argument ( ' --names ' , default = defval ( ' ' ) )
2019-04-18 09:23:24 +00:00
parser . add_argument ( ' --outputpath ' )
2021-02-21 19:17:24 +00:00
parser . add_argument ( ' --game ' , default = " A Link to the Past " )
2019-12-15 17:10:01 +00:00
parser . add_argument ( ' --race ' , default = defval ( False ) , action = ' store_true ' )
2019-12-15 16:29:17 +00:00
parser . add_argument ( ' --outputname ' )
2021-05-13 19:57:11 +00:00
parser . add_argument ( ' --start_hints ' )
2019-12-16 11:52:30 +00:00
if multiargs . multi :
for player in range ( 1 , multiargs . multi + 1 ) :
parser . add_argument ( f ' --p { player } ' , default = defval ( ' ' ) , help = argparse . SUPPRESS )
ret = parser . parse_args ( argv )
2021-01-02 11:49:43 +00:00
2021-01-02 22:00:14 +00:00
# shuffle medallions
ret . required_medallions = ( " random " , " random " )
2021-01-02 11:49:43 +00:00
# cannot be set through CLI currently
2021-01-02 15:44:58 +00:00
ret . plando_items = [ ]
ret . plando_texts = { }
2021-01-02 21:41:03 +00:00
ret . plando_connections = [ ]
2021-02-20 20:01:38 +00:00
ret . er_seeds = { }
2021-01-02 11:49:43 +00:00
2020-03-04 12:55:03 +00:00
if ret . timer == " none " :
ret . timer = False
2020-04-12 22:46:32 +00:00
if ret . dungeon_counters == ' on ' :
ret . dungeon_counters = True
elif ret . dungeon_counters == ' off ' :
ret . dungeon_counters = False
2019-12-16 11:52:30 +00:00
if multiargs . multi :
defaults = copy . deepcopy ( ret )
for player in range ( 1 , multiargs . multi + 1 ) :
2020-03-04 12:55:03 +00:00
playerargs = parse_arguments ( shlex . split ( getattr ( ret , f " p { player } " ) ) , True )
2019-12-16 11:52:30 +00:00
2021-09-12 23:32:32 +00:00
for name in [ ' logic ' , ' mode ' , ' goal ' , ' difficulty ' , ' item_functionality ' ,
2021-06-08 19:58:11 +00:00
' shuffle ' , ' open_pyramid ' , ' timer ' ,
2020-10-28 23:20:59 +00:00
' countdown_start_time ' , ' red_clock_time ' , ' blue_clock_time ' , ' green_clock_time ' ,
2021-09-16 22:17:54 +00:00
' beemizer ' ,
2021-09-12 23:32:32 +00:00
' shufflebosses ' , ' enemy_health ' , ' enemy_damage ' ,
2021-08-09 07:15:41 +00:00
' sprite ' ,
2021-09-16 22:17:54 +00:00
" triforce_pieces_available " ,
2021-06-08 19:58:11 +00:00
" triforce_pieces_required " , " shop_shuffle " ,
2021-05-13 19:57:11 +00:00
" required_medallions " , " start_hints " ,
2021-02-20 20:01:38 +00:00
" plando_items " , " plando_texts " , " plando_connections " , " er_seeds " ,
2021-09-12 23:32:32 +00:00
' dungeon_counters ' ,
' shuffle_prizes ' , ' sprite_pool ' , ' dark_room_logic ' ,
' game ' ] :
2019-12-16 11:52:30 +00:00
value = getattr ( defaults , name ) if getattr ( playerargs , name ) is None else getattr ( playerargs , name )
if player == 1 :
setattr ( ret , name , { 1 : value } )
else :
getattr ( ret , name ) [ player ] = value
2020-10-24 03:38:56 +00:00
return ret