Merge branch 'DoorDev' of https://github.com/aerinon/ALttPDoorRandomizer into aerinon-DoorDev

This commit is contained in:
Mike A. Trethewey
2020-03-03 07:05:23 -08:00
9 changed files with 232 additions and 189 deletions

View File

@@ -769,6 +769,16 @@ class CollectionState(object):
else:
self.prog_items.add(('Bow', item.player))
changed = True
elif 'Armor' in item.name:
if self.has('Red Mail', item.player):
pass
elif self.has('Blue Mail', item.player):
self.prog_items.add(('Red Mail', item.player))
changed = True
else:
self.prog_items.add(('Blue Mail', item.player))
changed = True
elif item.name.startswith('Bottle'):
if self.bottle_count(item.player) < self.world.difficulty_requirements[item.player].progressive_bottle_limit:
self.prog_items.add((item.name, item.player))

View File

@@ -183,6 +183,10 @@ def place_bosses(world, player):
raise FillError('Could not place boss for location %s' % loc_text)
bosses.remove(boss)
# GT Bosses can move dungeon - find the real dungeon to place them in
if level:
loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0]
loc_text = loc + ' (' + level + ')'
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)
elif world.boss_shuffle[player] == "chaos": #all bosses chosen at random
@@ -193,5 +197,9 @@ def place_bosses(world, player):
except IndexError:
raise FillError('Could not place boss for location %s' % loc_text)
# GT Bosses can move dungeon - find the real dungeon to place them in
if level:
loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0]
loc_text = loc + ' (' + level + ')'
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)

344
CLI.py
View File

@@ -9,7 +9,6 @@ import shlex
import sys
from Main import main
from Rom import get_sprite_from_name
from Utils import is_bundled, close_console
from Fill import FillError
@@ -212,7 +211,7 @@ def parse_arguments(argv, no_defaults=False):
parser.add_argument('--rom', default=defval(settings["rom"]), help='Path to an ALttP JAP(1.0) rom to use as a base.')
parser.add_argument('--loglevel', default=defval('info'), const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help='Define seed number to generate.', type=int)
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help='''\
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else None), 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
@@ -264,7 +263,7 @@ def parse_arguments(argv, no_defaults=False):
help='Select the color of Link\'s heart meter. (default: %(default)s)')
parser.add_argument('--ow_palettes', default=defval(settings["ow_palettes"]), choices=['default', 'random', 'blackout'])
parser.add_argument('--uw_palettes', default=defval(settings["uw_palettes"]), choices=['default', 'random', 'blackout'])
parser.add_argument('--sprite', help='''\
parser.add_argument('--sprite', default=defval(settings["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,
or 0x7078 (28792) bytes including palette data.
@@ -291,6 +290,7 @@ def parse_arguments(argv, no_defaults=False):
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
parser.add_argument('--outputpath', default=defval(settings["outputpath"]))
parser.add_argument('--race', default=defval(settings["race"] != 0), action='store_true')
parser.add_argument('--saveonexit', default=defval(settings["saveonexit"]), choices=['never', 'ask', 'always'])
parser.add_argument('--outputname')
if multiargs.multi:
@@ -322,185 +322,187 @@ def parse_arguments(argv, no_defaults=False):
return ret
def get_settings():
# set default settings
settings = {
"retro": False,
"mode": "open",
"logic": "noglitches",
"goal": "ganon",
"crystals_gt": "7",
"crystals_ganon": "7",
"swords": "random",
"difficulty": "normal",
"item_functionality": "normal",
"timer": "none",
"progressive": "on",
"accessibility": "items",
"algorithm": "balanced",
# set default settings
settings = {
"retro": False,
"mode": "open",
"logic": "noglitches",
"goal": "ganon",
"crystals_gt": "7",
"crystals_ganon": "7",
"swords": "random",
"difficulty": "normal",
"item_functionality": "normal",
"timer": "none",
"progressive": "on",
"accessibility": "items",
"algorithm": "balanced",
"openpyramid": False,
"shuffleganon": False,
"shuffle": "vanilla",
"openpyramid": False,
"shuffleganon": False,
"shuffle": "vanilla",
"shufflepots": False,
"shuffleenemies": "none",
"shufflebosses": "none",
"enemy_damage": "default",
"enemy_health": "default",
"enemizercli": os.path.join(".","EnemizerCLI","EnemizerCLI.Core"),
"shufflepots": False,
"shuffleenemies": "none",
"shufflebosses": "none",
"enemy_damage": "default",
"enemy_health": "default",
"enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"),
"mapshuffle": False,
"compassshuffle": False,
"keyshuffle": False,
"bigkeyshuffle": False,
"keysanity": False,
"door_shuffle": "basic",
"experimental": 0,
"dungeon_counters": "off",
"mapshuffle": False,
"compassshuffle": False,
"keyshuffle": False,
"bigkeyshuffle": False,
"keysanity": False,
"door_shuffle": "basic",
"experimental": 0,
"dungeon_counters": "off",
"multi": 1,
"names": "",
"multi": 1,
"names": "",
"hints": True,
"disablemusic": False,
"quickswap": False,
"heartcolor": "red",
"heartbeep": "normal",
"sprite": None,
"fastmenu": "normal",
"ow_palettes": "default",
"uw_palettes": "default",
"hints": True,
"disablemusic": False,
"quickswap": False,
"heartcolor": "red",
"heartbeep": "normal",
"sprite": None,
"fastmenu": "normal",
"ow_palettes": "default",
"uw_palettes": "default",
"create_spoiler": False,
"skip_playthrough": True,
"suppress_rom": False,
"usestartinventory": False,
"custom": False,
"rom": os.path.join(".","Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"),
"create_spoiler": False,
"skip_playthrough": False,
"suppress_rom": False,
"usestartinventory": False,
"custom": False,
"rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"),
"seed": None,
"count": 1,
"startinventory": "",
"beemizer": 0,
"remote_items": False,
"race": False,
"customitemarray": {
"bow": 0,
"progressivebow": 2,
"boomerang": 1,
"redmerang": 1,
"hookshot": 1,
"mushroom": 1,
"powder": 1,
"firerod": 1,
"icerod": 1,
"bombos": 1,
"ether": 1,
"quake": 1,
"lamp": 1,
"hammer": 1,
"shovel": 1,
"flute": 1,
"bugnet": 1,
"book": 1,
"bottle": 4,
"somaria": 1,
"byrna": 1,
"cape": 1,
"mirror": 1,
"boots": 1,
"powerglove": 0,
"titansmitt": 0,
"progressiveglove": 2,
"flippers": 1,
"pearl": 1,
"heartpiece": 24,
"heartcontainer": 10,
"sancheart": 1,
"sword1": 0,
"sword2": 0,
"sword3": 0,
"sword4": 0,
"progressivesword": 4,
"shield1": 0,
"shield2": 0,
"shield3": 0,
"progressiveshield": 3,
"mail2": 0,
"mail3": 0,
"progressivemail": 2,
"halfmagic": 1,
"quartermagic": 0,
"bombsplus5": 0,
"bombsplus10": 0,
"arrowsplus5": 0,
"arrowsplus10": 0,
"arrow1": 1,
"arrow10": 12,
"bomb1": 0,
"bomb3": 16,
"bomb10": 1,
"rupee1": 2,
"rupee5": 4,
"rupee20": 28,
"rupee50": 7,
"rupee100": 1,
"rupee300": 5,
"blueclock": 0,
"greenclock": 0,
"redclock": 0,
"silversupgrade": 0,
"generickeys": 0,
"triforcepieces": 0,
"triforcepiecesgoal": 0,
"triforce": 0,
"rupoor": 0,
"rupoorcost": 10
},
"randomSprite": False,
"outputpath": os.path.join(".")
}
settings["startinventoryarray"] = {}
if sys.platform.lower().find("windows"):
settings["enemizercli"] += ".exe"
"seed": None,
"count": None,
"startinventory": "",
"beemizer": 0,
"remote_items": False,
"race": False,
"customitemarray": {
"bow": 0,
"progressivebow": 2,
"boomerang": 1,
"redmerang": 1,
"hookshot": 1,
"mushroom": 1,
"powder": 1,
"firerod": 1,
"icerod": 1,
"bombos": 1,
"ether": 1,
"quake": 1,
"lamp": 1,
"hammer": 1,
"shovel": 1,
"flute": 1,
"bugnet": 1,
"book": 1,
"bottle": 4,
"somaria": 1,
"byrna": 1,
"cape": 1,
"mirror": 1,
"boots": 1,
"powerglove": 0,
"titansmitt": 0,
"progressiveglove": 2,
"flippers": 1,
"pearl": 1,
"heartpiece": 24,
"heartcontainer": 10,
"sancheart": 1,
"sword1": 0,
"sword2": 0,
"sword3": 0,
"sword4": 0,
"progressivesword": 4,
"shield1": 0,
"shield2": 0,
"shield3": 0,
"progressiveshield": 3,
"mail2": 0,
"mail3": 0,
"progressivemail": 2,
"halfmagic": 1,
"quartermagic": 0,
"bombsplus5": 0,
"bombsplus10": 0,
"arrowsplus5": 0,
"arrowsplus10": 0,
"arrow1": 1,
"arrow10": 12,
"bomb1": 0,
"bomb3": 16,
"bomb10": 1,
"rupee1": 2,
"rupee5": 4,
"rupee20": 28,
"rupee50": 7,
"rupee100": 1,
"rupee300": 5,
"blueclock": 0,
"greenclock": 0,
"redclock": 0,
"silversupgrade": 0,
"generickeys": 0,
"triforcepieces": 0,
"triforcepiecesgoal": 0,
"triforce": 0,
"rupoor": 0,
"rupoorcost": 10
},
"randomSprite": False,
"outputpath": os.path.join("."),
"saveonexit": "ask",
"startinventoryarray": {}
}
if sys.platform.lower().find("windows"):
settings["enemizercli"] += ".exe"
# read saved settings file if it exists and set these
settings_path = os.path.join(".", "resources", "user", "settings.json")
if os.path.exists(settings_path):
with(open(settings_path)) as json_file:
data = json.load(json_file)
for k, v in data.items():
settings[k] = v
return settings
# read saved settings file if it exists and set these
settings_path = os.path.join(".", "resources", "user", "settings.json")
if os.path.exists(settings_path):
with(open(settings_path)) as json_file:
data = json.load(json_file)
if 'sprite' in data.keys() and data['sprite']:
data['sprite'] = get_sprite_from_name(data['sprite'])
for k,v in data.items():
settings[k] = v
return settings
def get_args_priority(settings_args, gui_args, cli_args):
args = {}
args["settings"] = get_settings() if settings_args is None else settings_args
args["gui"] = {} if gui_args is None else gui_args
args["cli"] = cli_args
args = {}
args["settings"] = get_settings() if settings_args is None else settings_args
args["gui"] = {} if gui_args is None else gui_args
args["cli"] = cli_args
args["load"] = args["settings"]
if args["gui"] is not None:
for k in args["gui"]:
if k not in args["load"] or args["load"][k] != args["gui"]:
args["load"][k] = args["gui"][k]
args["load"] = args["settings"]
if args["gui"] is not None:
for k in args["gui"]:
if k not in args["load"] or args["load"][k] != args["gui"]:
args["load"][k] = args["gui"][k]
if args["cli"] is None:
args["cli"] = {}
cli = vars(parse_arguments(None))
for k,v in cli.items():
if isinstance(v,dict) and 1 in v:
args["cli"][k] = v[1]
else:
args["cli"][k] = v
load_doesnt_have_key = k not in args["load"]
different_val = (k in args["load"] and k in args["cli"]) and (args["load"][k] != args["cli"][k])
cli_has_empty_dict = k in args["cli"] and isinstance(args["cli"][k],dict) and len(args["cli"][k]) == 0
if load_doesnt_have_key or different_val:
if not cli_has_empty_dict:
args["load"][k] = args["cli"][k]
if args["cli"] is None:
args["cli"] = {}
cli = vars(parse_arguments(None))
for k, v in cli.items():
if isinstance(v, dict) and 1 in v:
args["cli"][k] = v[1]
else:
args["cli"][k] = v
load_doesnt_have_key = k not in args["load"]
different_val = (k in args["load"] and k in args["cli"]) and (args["load"][k] != args["cli"][k])
cli_has_empty_dict = k in args["cli"] and isinstance(args["cli"][k], dict) and len(args["cli"][k]) == 0
if load_doesnt_have_key or different_val:
if not cli_has_empty_dict:
args["load"][k] = args["cli"][k]
return args
return args

View File

@@ -2298,8 +2298,6 @@ Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)',
'Death Mountain Return Cave (East)',
'Death Mountain Return Cave (West)',
'Spectacle Rock Cave Peak',
'Spectacle Rock Cave',
'Spectacle Rock Cave (Bottom)',
'Paradox Cave (Bottom)',
'Paradox Cave (Middle)',
'Paradox Cave (Top)',

23
Gui.py
View File

@@ -21,7 +21,6 @@ from source.gui.randomize.generation import generation_page
from source.gui.bottom import bottom_frame, create_guiargs
from GuiUtils import set_icon
from Main import __version__ as ESVersion
from Rom import get_sprite_from_name
def guiMain(args=None):
@@ -36,21 +35,29 @@ def guiMain(args=None):
f.write(json.dumps(args, indent=2))
os.chmod(os.path.join(settings_path, "settings.json"),0o755)
def save_settings_from_gui():
def save_settings_from_gui(confirm):
gui_args = vars(create_guiargs(self))
if self.randomSprite.get():
gui_args['sprite'] = 'random'
elif gui_args['sprite']:
gui_args['sprite'] = gui_args['sprite'].name
save_settings(gui_args)
messagebox.showinfo("Door Shuffle " + ESVersion,"Settings saved from GUI.")
if confirm:
messagebox.showinfo("Door Shuffle " + ESVersion, "Settings saved from GUI.")
# routine for exiting the app
def guiExit():
dosave = messagebox.askyesno("Door Shuffle " + ESVersion, "Save settings before exit?")
if dosave:
save_settings_from_gui()
sys.exit(0)
skip_exit = False
if self.settings['saveonexit'] == 'ask':
dosave = messagebox.askyesnocancel("Door Shuffle " + ESVersion, "Save settings before exit?")
if dosave:
save_settings_from_gui(True)
if dosave is None:
skip_exit = True
elif self.settings['saveonexit'] == 'always':
save_settings_from_gui(False)
if not skip_exit:
sys.exit(0)
# make main window
# add program title & version number
@@ -137,7 +144,7 @@ def guiMain(args=None):
# bottom of window: Open Output Directory, Open Documentation (if exists)
self.frames["bottom"] = bottom_frame(self, self, None)
## Save Settings Button
savesettingsButton = Button(self.frames["bottom"], text='Save Settings to File', command=save_settings_from_gui)
savesettingsButton = Button(self.frames["bottom"], text='Save Settings to File', command=lambda: save_settings_from_gui(True))
savesettingsButton.pack(side=RIGHT)
# set bottom frame to main window

View File

@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties, fill_prizes
from Utils import output_path, parse_player_names
__version__ = '0.0.17.2p'
__version__ = '0.0.18dev'
def main(args, seed=None):

4
Rom.py
View File

@@ -981,8 +981,8 @@ def patch_rom(world, rom, player, team, enemized):
startingstate = CollectionState(world)
if startingstate.has('Bow', player):
equip[0x340] = 1
equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases
equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1
equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases
if not world.retro[player]:
equip[0x38E] |= 0x80
if startingstate.has('Silver Arrows', player):

View File

@@ -1185,14 +1185,13 @@ def set_inverted_big_bomb_rules(world, player):
'Hyrule Castle Entrance (East)',
'Inverted Ganons Tower',
'Cave 45',
'Checkerboard Cave']
'Checkerboard Cave',
'Inverted Big Bomb Shop']
LW_DM_entrances = ['Old Man Cave (East)',
'Old Man House (Bottom)',
'Old Man House (Top)',
'Death Mountain Return Cave (East)',
'Spectacle Rock Cave Peak',
'Spectacle Rock Cave',
'Spectacle Rock Cave (Bottom)',
'Tower of Hera',
'Death Mountain Return Cave (West)',
'Paradox Cave (Top)',
@@ -1212,7 +1211,7 @@ def set_inverted_big_bomb_rules(world, player):
'Chest Game',
'Dark World Hammer Peg Cave',
'Red Shield Shop',
'Dark Sanctuary Hint',
'Inverted Dark Sanctuary',
'Fortune Teller (Dark)',
'Dark World Shop',
'Dark World Lumberjack Shop',
@@ -1222,7 +1221,7 @@ def set_inverted_big_bomb_rules(world, player):
Southern_DW_entrances = ['Hype Cave',
'Bonk Fairy (Dark)',
'Archery Game',
'Inverted Big Bomb Shop',
'Inverted Links House',
'Dark Lake Hylia Shop',
'Swamp Palace']
Isolated_DW_entrances = ['Spike Cave',

View File

@@ -22,5 +22,24 @@
"label": {
"text": "Use custom item pool"
}
},
"saveonexit": {
"type": "selectbox",
"label": {
"text": "Save Settings on Exit"
},
"managerAttrs": {
"label": {
"side": "left"
},
"selectbox": {
"side": "right"
}
},
"options": {
"Ask Me": "ask",
"Always": "always",
"Never": "never"
}
}
}