Move cmd entry point to EntranceRandomizer.py. Add very barebones tkinter Gui.
This commit is contained in:
parent
1066063ffe
commit
c3854f4d2d
|
@ -0,0 +1,77 @@
|
|||
import argparse
|
||||
import os
|
||||
import logging
|
||||
import random
|
||||
|
||||
from Main import main
|
||||
from Gui import guiMain
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
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'],
|
||||
help='Select Enforcement of Item Requirements. Minor Glitches may require Fake Flippers, Bunny Revival and Dark Room Navigation.')
|
||||
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'],
|
||||
help='Select game mode. 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.\n'
|
||||
'Swordless mode is like open, but with no swords. Curtains in Skull Woods and Agahnims Tower are removed, Agahnims Tower barrier can be destroyed with hammer. Misery Mire and Turtle Rock can be opened without a sword.\n'
|
||||
'Hammer damages Ganon. Ether and Bombos Tablet are unreachable but contain trash items always.')
|
||||
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'],
|
||||
help='Select completion goal. Pedestal places the Triforce at the Master Sword Pedestal. All dungeons is not enforced ingame but considered in the playthrough. \n'
|
||||
'Star Hunt places 15 Power Stars pieces in the world, collect 10 of them to beat the game.\n'
|
||||
'Triforce Hunt places 3 Triforce Pieces in the world, collect them all to beat the game.')
|
||||
parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['normal', 'timed', 'timed-ohko', 'timed-countdown'],
|
||||
help='Select game difficulty. Affects available itempool.\n'
|
||||
'Timed modes replace low value items with clocks, the overall rupee count in the pool stays roughly the same.\n'
|
||||
'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.\n'
|
||||
'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.\n'
|
||||
'Timed Countdown starts with clock at 40 minutes. Same clocks as Timed mode. If time runs out, you lose (but can still keep playing).')
|
||||
parser.add_argument('--algorithm', default='restrictive', const='restrictive', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'restrictive'],
|
||||
help='Select item filling algorithm. vt21 is unbiased in its selection, but has tendency to put Ice Rod in Turtle Rock.\n'
|
||||
'vt22 drops off stale locations after 1/3 of progress items were placed to try to circumvent vt21\'s shortcomings.\n'
|
||||
'freshness keeps track of stale locations (ones that cannot be reached yet) and decreases likeliness of selecting them the more often they were found unreachable.\n'
|
||||
'flood pushes out items starting from Link\'s House and is slightly biased to placing progression items with less restrictions.\n'
|
||||
'restrictive shuffles items and then places them in a random location that it is not impossible to be in.')
|
||||
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
||||
help='Select Entrance Shuffling Algorithm.\n'
|
||||
'Simple shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. All caves outside of death mountain are shuffled in pairs.\n'
|
||||
'Restricted uses Dungeons shuffling from Simple but freely connects remaining entrances.\n'
|
||||
'Full mixes cave and dungeon entrances freely.\n'
|
||||
'Madness decouples entrances and exits from each other and shuffles them freely, only ensuring that no fake Light/Dark World happens and all locations are reachable.\n'
|
||||
'Insanity is Madness without the world restrictions. Mirror and Pearl are provided early to ensure Filling algorithm works properly. Deal with Fake LW/DW at your discretion. Experimental.\n'
|
||||
'The dungeon variants only mix up dungeons and keep the rest of the overworld vanilla.')
|
||||
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)
|
||||
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 time).', type=int)
|
||||
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
||||
parser.add_argument('--nodungeonitems', help='Remove Maps and Compasses from Itempool, replacing them by empty slots.', 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 on the restrictive algorithm currently.', action='store_true')
|
||||
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
|
||||
help='Select the rate at which the heart beep sound is played at low health.')
|
||||
parser.add_argument('--sprite', help='Path to a sprite sheet to use for Link. Needs to be in binary format and have a length of 0x7000 (28672) bytes.')
|
||||
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')
|
||||
parser.add_argument('--jsonout', help='Output .json patch file instead of a patched rom. Used for VT site integration, do not use otherwise.')
|
||||
args = parser.parse_args()
|
||||
|
||||
# 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. \nPress Enter to exit.' % args.rom)
|
||||
exit(1)
|
||||
if args.sprite is not None and not os.path.isfile(args.rom):
|
||||
input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite)
|
||||
exit(1)
|
||||
|
||||
# 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
|
||||
for i in range(args.count):
|
||||
main(seed=seed, args=args)
|
||||
seed = random.randint(0, 999999999)
|
||||
else:
|
||||
main(seed=args.seed, args=args)
|
|
@ -0,0 +1,215 @@
|
|||
from Main import main, __version__ as ESVersion
|
||||
from argparse import Namespace
|
||||
import random
|
||||
|
||||
from tkinter import Checkbutton, OptionMenu, Tk, LEFT, RIGHT, BOTTOM, TOP, StringVar, IntVar, Frame, Label, W, E, Entry, Spinbox, Button, filedialog, messagebox
|
||||
|
||||
|
||||
def guiMain(args=None):
|
||||
mainWindow = Tk()
|
||||
mainWindow.wm_title("Entrance Shuffle %s" % ESVersion)
|
||||
|
||||
topFrame = Frame(mainWindow)
|
||||
rightHalfFrame = Frame(topFrame)
|
||||
checkBoxFrame = Frame(rightHalfFrame)
|
||||
|
||||
createSpoilerVar = IntVar()
|
||||
createSpoilerCheckbutton = Checkbutton(checkBoxFrame, text="Create Spoiler Log", variable=createSpoilerVar)
|
||||
suppressRomVar = IntVar()
|
||||
suppressRomCheckbutton = Checkbutton(checkBoxFrame, text="Do not create patched Rom", variable=suppressRomVar)
|
||||
quickSwapVar = IntVar()
|
||||
quickSwapCheckbutton = Checkbutton(checkBoxFrame, text="Enabled L/R Item quickswapping", variable=quickSwapVar)
|
||||
dungeonItemsVar = IntVar()
|
||||
dungeonItemsCheckbutton = Checkbutton(checkBoxFrame, text="Place Dungeon Items (Compasses/Maps)", onvalue=0, offvalue=1, variable=dungeonItemsVar)
|
||||
beatableOnlyVar = IntVar()
|
||||
beatableOnlyCheckbutton = Checkbutton(checkBoxFrame, text="Only ensure seed is beatable, not all items must be reachable", variable=beatableOnlyVar)
|
||||
|
||||
createSpoilerCheckbutton.pack(expand=True, anchor=W)
|
||||
suppressRomCheckbutton.pack(expand=True, anchor=W)
|
||||
quickSwapCheckbutton.pack(expand=True, anchor=W)
|
||||
dungeonItemsCheckbutton.pack(expand=True, anchor=W)
|
||||
beatableOnlyCheckbutton.pack(expand=True, anchor=W)
|
||||
|
||||
fileDialogFrame = Frame(rightHalfFrame)
|
||||
|
||||
romDialogFrame = Frame(fileDialogFrame)
|
||||
baseRomLabel = Label(romDialogFrame, text='Base Rom')
|
||||
romVar = StringVar()
|
||||
romEntry = Entry(romDialogFrame, textvariable=romVar)
|
||||
def RomSelect():
|
||||
rom = filedialog.askopenfilename()
|
||||
romVar.set(rom)
|
||||
romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect)
|
||||
|
||||
baseRomLabel.pack(side=LEFT)
|
||||
romEntry.pack(side=LEFT)
|
||||
romSelectButton.pack(side=LEFT)
|
||||
|
||||
spriteDialogFrame = Frame(fileDialogFrame)
|
||||
baseSpriteLabel = Label(spriteDialogFrame, text='Link Sprite')
|
||||
spriteVar = StringVar()
|
||||
spriteEntry = Entry(spriteDialogFrame, textvariable=spriteVar)
|
||||
|
||||
def SpriteSelect():
|
||||
sprite = filedialog.askopenfilename()
|
||||
spriteVar.set(sprite)
|
||||
|
||||
spriteSelectButton = Button(spriteDialogFrame, text='Select Sprite', command=SpriteSelect)
|
||||
|
||||
baseSpriteLabel.pack(side=LEFT)
|
||||
spriteEntry.pack(side=LEFT)
|
||||
spriteSelectButton.pack(side=LEFT)
|
||||
|
||||
romDialogFrame.pack()
|
||||
spriteDialogFrame.pack()
|
||||
|
||||
checkBoxFrame.pack()
|
||||
fileDialogFrame.pack()
|
||||
|
||||
drowDownFrame = Frame(topFrame)
|
||||
|
||||
modeFrame = Frame(drowDownFrame)
|
||||
modeVar = StringVar()
|
||||
modeVar.set('standard')
|
||||
modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'swordless')
|
||||
modeOptionMenu.pack(side=RIGHT)
|
||||
modeLabel = Label(modeFrame, text='Game Mode')
|
||||
modeLabel.pack(side=LEFT)
|
||||
|
||||
logicFrame = Frame(drowDownFrame)
|
||||
logicVar = StringVar()
|
||||
logicVar.set('noglitches')
|
||||
logicOptionMenu = OptionMenu(logicFrame, logicVar, 'noglitches', 'minorglitches')
|
||||
logicOptionMenu.pack(side=RIGHT)
|
||||
logicLabel = Label(logicFrame, text='Game logic')
|
||||
logicLabel.pack(side=LEFT)
|
||||
|
||||
goalFrame = Frame(drowDownFrame)
|
||||
goalVar = StringVar()
|
||||
goalVar.set('ganon')
|
||||
goalOptionMenu = OptionMenu(goalFrame, goalVar, 'ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt')
|
||||
goalOptionMenu.pack(side=RIGHT)
|
||||
goalLabel = Label(goalFrame, text='Game goal')
|
||||
goalLabel.pack(side=LEFT)
|
||||
|
||||
difficultyFrame = Frame(drowDownFrame)
|
||||
difficultyVar = StringVar()
|
||||
difficultyVar.set('normal')
|
||||
difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'normal', 'timed', 'timed-ohko', 'timed-countdown')
|
||||
difficultyOptionMenu.pack(side=RIGHT)
|
||||
difficultyLabel = Label(difficultyFrame, text='Game difficulty')
|
||||
difficultyLabel.pack(side=LEFT)
|
||||
|
||||
algorithmFrame = Frame(drowDownFrame)
|
||||
algorithmVar = StringVar()
|
||||
algorithmVar.set('restrictive')
|
||||
algorithmOptionMenu = OptionMenu(algorithmFrame, algorithmVar, 'freshness', 'flood', 'vt21', 'vt22', 'restrictive')
|
||||
algorithmOptionMenu.pack(side=RIGHT)
|
||||
algorithmLabel = Label(algorithmFrame, text='Item distribution algorithm')
|
||||
algorithmLabel.pack(side=LEFT)
|
||||
|
||||
shuffleFrame = Frame(drowDownFrame)
|
||||
shuffleVar = StringVar()
|
||||
shuffleVar.set('full')
|
||||
shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple')
|
||||
shuffleOptionMenu.pack(side=RIGHT)
|
||||
shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm')
|
||||
shuffleLabel.pack(side=LEFT)
|
||||
|
||||
heartbeepFrame = Frame(drowDownFrame)
|
||||
heartbeepVar = StringVar()
|
||||
heartbeepVar.set('normal')
|
||||
heartbeepOptionMenu = OptionMenu(heartbeepFrame, heartbeepVar, 'normal', 'half', 'quarter', 'off')
|
||||
heartbeepOptionMenu.pack(side=RIGHT)
|
||||
heartbeepLabel = Label(heartbeepFrame, text='Heartbeep sound rate')
|
||||
heartbeepLabel.pack(side=LEFT)
|
||||
|
||||
modeFrame.pack(expand=True, anchor=E)
|
||||
logicFrame.pack(expand=True, anchor=E)
|
||||
goalFrame.pack(expand=True, anchor=E)
|
||||
difficultyFrame.pack(expand=True, anchor=E)
|
||||
algorithmFrame.pack(expand=True, anchor=E)
|
||||
shuffleFrame.pack(expand=True, anchor=E)
|
||||
heartbeepFrame.pack(expand=True, anchor=E)
|
||||
|
||||
bottomFrame = Frame(mainWindow)
|
||||
|
||||
seedLabel = Label(bottomFrame, text='Seed #')
|
||||
seedVar = StringVar()
|
||||
seedEntry = Entry(bottomFrame, textvariable=seedVar)
|
||||
countLabel = Label(bottomFrame, text='Count')
|
||||
countVar = StringVar()
|
||||
countSpinbox = Spinbox(bottomFrame, from_=1, to=100, textvariable=countVar)
|
||||
|
||||
def generateRom():
|
||||
guiargs = Namespace
|
||||
guiargs.seed = int(seedVar.get()) if seedVar.get() else None
|
||||
guiargs.count = int(countVar.get()) if countVar.get() != '1' else None
|
||||
guiargs.mode = modeVar.get()
|
||||
guiargs.logic = logicVar.get()
|
||||
guiargs.goal = goalVar.get()
|
||||
guiargs.difficulty = difficultyVar.get()
|
||||
guiargs.algorithm = algorithmVar.get()
|
||||
guiargs.shuffle = shuffleVar.get()
|
||||
guiargs.heartbeep = heartbeepVar.get()
|
||||
guiargs.create_spoiler = bool(createSpoilerVar.get())
|
||||
guiargs.suppress_rom = bool(suppressRomVar.get())
|
||||
guiargs.nodungeonitems = bool(dungeonItemsVar.get())
|
||||
guiargs.beatableonly = bool(beatableOnlyVar.get())
|
||||
guiargs.quickswap = bool(quickSwapVar.get())
|
||||
guiargs.rom = romVar.get()
|
||||
guiargs.sprite = spriteVar.get() if spriteVar.get() else None
|
||||
try:
|
||||
if guiargs.count is not None:
|
||||
seed = guiargs.seed
|
||||
for i in range(guiargs.count):
|
||||
main(seed=seed, args=guiargs)
|
||||
seed = random.randint(0, 999999999)
|
||||
else:
|
||||
main(seed=guiargs.seed, args=guiargs)
|
||||
except Exception as e:
|
||||
messagebox.showerror(title="Error while creating seed", message=str(e))
|
||||
else:
|
||||
messagebox.showinfo(title="Success", message="Rom patched successfully")
|
||||
|
||||
generateButton = Button(bottomFrame, text='Generate Patched Rom', command=generateRom)
|
||||
|
||||
seedLabel.pack(side=LEFT)
|
||||
seedEntry.pack(side=LEFT)
|
||||
countLabel.pack(side=LEFT)
|
||||
countSpinbox.pack(side=LEFT)
|
||||
generateButton.pack(side=LEFT)
|
||||
|
||||
drowDownFrame.pack(side=LEFT)
|
||||
rightHalfFrame.pack(side=RIGHT)
|
||||
topFrame.pack(side=TOP)
|
||||
bottomFrame.pack(side=BOTTOM)
|
||||
|
||||
if args is not None:
|
||||
# load values from commandline args
|
||||
createSpoilerVar.set(int(args.create_spoiler))
|
||||
suppressRomVar.set(int(args.suppress_rom))
|
||||
if args.nodungeonitems:
|
||||
dungeonItemsVar.set(int(not args.nodungeonitems))
|
||||
beatableOnlyVar.set(int(args.beatableonly))
|
||||
quickSwapVar.set(int(args.quickswap))
|
||||
if args.count:
|
||||
countVar.set(str(args.count))
|
||||
if args.seed:
|
||||
seedVar.set(str(args.seed))
|
||||
modeVar.set(args.mode)
|
||||
difficultyVar.set(args.difficulty)
|
||||
goalVar.set(args.goal)
|
||||
algorithmVar.set(args.algorithm)
|
||||
shuffleVar.set(args.shuffle)
|
||||
heartbeepVar.set(args.heartbeep)
|
||||
logicVar.set(args.logic)
|
||||
romVar.set(args.rom)
|
||||
if args.sprite is not None:
|
||||
spriteVar.set(args.sprite)
|
||||
|
||||
mainWindow.mainloop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
guiMain()
|
69
Main.py
69
Main.py
|
@ -8,8 +8,6 @@ from Items import ItemFactory
|
|||
import random
|
||||
import time
|
||||
import logging
|
||||
import argparse
|
||||
import os
|
||||
|
||||
__version__ = '0.4.1-dev'
|
||||
|
||||
|
@ -612,70 +610,3 @@ def create_playthrough(world):
|
|||
|
||||
def print_location_spoiler(world):
|
||||
return 'Locations:\n\n' + '\n'.join(['%s: %s' % (location, location.item if location.item is not None else 'Nothing') for location in world.get_locations()]) + '\n\n'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
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'],
|
||||
help='Select Enforcement of Item Requirements. Minor Glitches may require Fake Flippers, Bunny Revival and Dark Room Navigation.')
|
||||
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'],
|
||||
help='Select game mode. 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.\n'
|
||||
'Swordless mode is like open, but with no swords. Curtains in Skull Woods and Agahnims Tower are removed, Agahnims Tower barrier can be destroyed with hammer. Misery Mire and Turtle Rock can be opened without a sword.\n'
|
||||
'Hammer damages Ganon. Ether and Bombos Tablet are unreachable but contain trash items always.')
|
||||
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'],
|
||||
help='Select completion goal. Pedestal places the Triforce at the Master Sword Pedestal. All dungeons is not enforced ingame but considered in the playthrough. \n'
|
||||
'Star Hunt places 15 Power Stars pieces in the world, collect 10 of them to beat the game.\n'
|
||||
'Triforce Hunt places 3 Triforce Pieces in the world, collect them all to beat the game.')
|
||||
parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['normal', 'timed', 'timed-ohko', 'timed-countdown'],
|
||||
help='Select game difficulty. Affects available itempool.\n'
|
||||
'Timed modes replace low value items with clocks, the overall rupee count in the pool stays roughly the same.\n'
|
||||
'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.\n'
|
||||
'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.\n'
|
||||
'Timed Countdown starts with clock at 40 minutes. Same clocks as Timed mode. If time runs out, you lose (but can still keep playing).')
|
||||
parser.add_argument('--algorithm', default='restrictive', const='restrictive', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'restrictive'],
|
||||
help='Select item filling algorithm. vt21 is unbiased in its selection, but has tendency to put Ice Rod in Turtle Rock.\n'
|
||||
'vt22 drops off stale locations after 1/3 of progress items were placed to try to circumvent vt21\'s shortcomings.\n'
|
||||
'freshness keeps track of stale locations (ones that cannot be reached yet) and decreases likeliness of selecting them the more often they were found unreachable.\n'
|
||||
'flood pushes out items starting from Link\'s House and is slightly biased to placing progression items with less restrictions.\n'
|
||||
'restrictive shuffles items and then places them in a random location that it is not impossible to be in.')
|
||||
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
||||
help='Select Entrance Shuffling Algorithm.\n'
|
||||
'Simple shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. All caves outside of death mountain are shuffled in pairs.\n'
|
||||
'Restricted uses Dungeons shuffling from Simple but freely connects remaining entrances.\n'
|
||||
'Full mixes cave and dungeon entrances freely.\n'
|
||||
'Madness decouples entrances and exits from each other and shuffles them freely, only ensuring that no fake Light/Dark World happens and all locations are reachable.\n'
|
||||
'Insanity is Madness without the world restrictions. Mirror and Pearl are provided early to ensure Filling algorithm works properly. Deal with Fake LW/DW at your discretion. Experimental.\n'
|
||||
'The dungeon variants only mix up dungeons and keep the rest of the overworld vanilla.')
|
||||
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)
|
||||
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 time).', type=int)
|
||||
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
||||
parser.add_argument('--nodungeonitems', help='Remove Maps and Compasses from Itempool, replacing them by empty slots.', 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 on the restrictive algorithm currently.', action='store_true')
|
||||
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
|
||||
help='Select the rate at which the heart beep sound is played at low health.')
|
||||
parser.add_argument('--sprite', help='Path to a sprite sheet to use for Link. Needs to be in binary format and have a length of 0x7000 (28672) bytes.')
|
||||
parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
# ToDo: Validate files further than mere existance
|
||||
if 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. \nPress Enter to exit.' % args.rom)
|
||||
exit(1)
|
||||
if args.sprite is not None and not os.path.isfile(args.rom):
|
||||
input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite)
|
||||
exit(1)
|
||||
|
||||
# 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.count is not None:
|
||||
seed = args.seed
|
||||
for i in range(args.count):
|
||||
main(seed=seed, args=args)
|
||||
seed = random.randint(0, 999999999)
|
||||
else:
|
||||
main(seed=args.seed, args=args)
|
||||
|
|
Loading…
Reference in New Issue