From 129d2ec0374c1067653aeb398e4d7ea28a65d496 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 13:50:41 +0100 Subject: [PATCH 01/14] remove unnecessary ternaries in multiclient --- MultiClient.py | 8 ++++---- WebUI.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MultiClient.py b/MultiClient.py index ea0dfcbb..0e3fac44 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -940,7 +940,7 @@ async def process_server_cmd(ctx: Context, cmd, args): ctx.ui_node.notify_item_sent(ctx.player_names[player_sent], ctx.player_names[player_recvd], get_item_name_from_id(item), get_location_name_from_address(location), player_sent == ctx.slot, player_recvd == ctx.slot, - True if get_item_name_from_id(item) in Items.progression_items else False) + get_item_name_from_id(item) in Items.progression_items) item = color_item(item, player_sent == ctx.slot) player_sent = color(ctx.player_names[player_sent], 'yellow' if player_sent != ctx.slot else 'magenta') player_recvd = color(ctx.player_names[player_recvd], 'yellow' if player_recvd != ctx.slot else 'magenta') @@ -952,7 +952,7 @@ async def process_server_cmd(ctx: Context, cmd, args): found = ReceivedItem(*args) ctx.ui_node.notify_item_found(ctx.player_names[found.player], get_item_name_from_id(found.item), get_location_name_from_address(found.location), found.player == ctx.slot, - True if get_item_name_from_id(found.item) in Items.progression_items else False) + get_item_name_from_id(found.item) in Items.progression_items) item = color_item(found.item, found.player == ctx.slot) player_sent = color(ctx.player_names[found.player], 'yellow' if found.player != ctx.slot else 'magenta') logging.info('%s found %s (%s)' % (player_sent, item, color(get_location_name_from_address(found.location), @@ -1089,7 +1089,7 @@ class ClientCommandProcessor(CommandProcessor): self.ctx.ui_node.notify_item_received(self.ctx.player_names[item.player], get_item_name_from_id(item.item), get_location_name_from_address(item.location), index, len(self.ctx.items_received), - True if get_item_name_from_id(item.item) in Items.progression_items else False) + get_item_name_from_id(item.item) in Items.progression_items) logging.info('%s from %s (%s) (%d/%d in list)' % ( color(get_item_name_from_id(item.item), 'red', 'bold'), color(self.ctx.player_names[item.player], 'yellow'), @@ -1348,7 +1348,7 @@ async def game_watcher(ctx : Context): ctx.ui_node.notify_item_received(ctx.player_names[item.player], get_item_name_from_id(item.item), get_location_name_from_address(item.location), recv_index + 1, len(ctx.items_received), - True if get_item_name_from_id(item.item) in Items.progression_items else False) + get_item_name_from_id(item.item) in Items.progression_items) logging.info('Received %s from %s (%s) (%d/%d in list)' % ( color(get_item_name_from_id(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), get_location_name_from_address(item.location), recv_index + 1, len(ctx.items_received))) diff --git a/WebUI.py b/WebUI.py index 59f5a3d5..157fa0d6 100644 --- a/WebUI.py +++ b/WebUI.py @@ -58,9 +58,9 @@ class WebUiClient(Node, logging.Handler): 'recipient': recipient, 'item': item, 'location': location, - 'iAmFinder': 1 if i_am_finder else 0, - 'iAmRecipient': 1 if i_am_recipient else 0, - 'itemIsUnique': 1 if item_is_unique else 0, + 'iAmFinder': int(i_am_finder), + 'iAmRecipient': int(i_am_recipient), + 'itemIsUnique': int(item_is_unique), })) def notify_item_found(self, finder: str, item: str, location: str, i_am_finder: bool, item_is_unique: bool = False): @@ -68,8 +68,8 @@ class WebUiClient(Node, logging.Handler): 'finder': finder, 'item': item, 'location': location, - 'iAmFinder': 1 if i_am_finder else 0, - 'itemIsUnique': 1 if item_is_unique else 0, + 'iAmFinder': int(i_am_finder), + 'itemIsUnique': int(item_is_unique), })) def notify_item_received(self, finder: str, item: str, location: str, item_index: int, queue_length: int, @@ -80,7 +80,7 @@ class WebUiClient(Node, logging.Handler): 'location': location, 'itemIndex': item_index, 'queueLength': queue_length, - 'itemIsUnique': 1 if item_is_unique else 0, + 'itemIsUnique': int(item_is_unique), })) def send_hint(self, finder, recipient, item, location, found, i_am_finder: bool, i_am_recipient: bool, From 9b6a695551b737a6e702a1621e54701be86a6ab3 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 15:18:26 +0100 Subject: [PATCH 02/14] default sort hints table so that it pushes found hints to the bottom --- WebHostLib/static/assets/tracker.js | 2 +- WebHostLib/templates/tracker.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WebHostLib/static/assets/tracker.js b/WebHostLib/static/assets/tracker.js index 50cb518c..e88b2ee6 100644 --- a/WebHostLib/static/assets/tracker.js +++ b/WebHostLib/static/assets/tracker.js @@ -55,7 +55,7 @@ window.addEventListener('load', () => { window.addEventListener('resize', () => { adjustTableHeight(); - tables.draw() + tables.draw(); }); $(".table-wrapper").scrollsync({ diff --git a/WebHostLib/templates/tracker.html b/WebHostLib/templates/tracker.html index 3a528c16..e814ca47 100644 --- a/WebHostLib/templates/tracker.html +++ b/WebHostLib/templates/tracker.html @@ -1,7 +1,7 @@ {% extends 'tablepage.html' %} {% block head %} {{ super() }} - Multiworld Tracker for Room {{ room.id }} + Multiworld Tracker @@ -151,7 +151,7 @@ {% endfor %} {% for team, hints in hints.items() %}
- +
From 68c639d7988ca5942db5958418bf74354f03aa6c Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 15:23:18 +0100 Subject: [PATCH 03/14] empty text comes before text; right. --- WebHostLib/templates/tracker.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/templates/tracker.html b/WebHostLib/templates/tracker.html index e814ca47..1ba56337 100644 --- a/WebHostLib/templates/tracker.html +++ b/WebHostLib/templates/tracker.html @@ -151,7 +151,7 @@ {% endfor %} {% for team, hints in hints.items() %}
-
Finder
+
From fca64f117736acdd77b4becf5f0dc95fac3cd3b4 Mon Sep 17 00:00:00 2001 From: pepperpow <45885625+pepperpow@users.noreply.github.com> Date: Fri, 19 Feb 2021 10:45:54 -0600 Subject: [PATCH 04/14] Removes Flashing instances in game (#168) * Added reduced flashing, triforce hud and cutscene options * Corrected parameters and replacement order * Mixed up rom byte * Removed triforce hud, smoothed cutscene speed and reset tables * Removed triforcehud line and added bird cutscene speedup * Added options to yaml * Added check for race rom generation (is not internal asm) * Added options to GUI (check sprite adjust crash) * Fixed inconsistency in setting weight * A "slow" setting for the cutscenespeed (#1) * Slow wall setting * Slow wall setting * Slow wall setting * Slow wall setting * Slow wall setting * Slow wall setting * Update playerSettings.yaml * Remove instances of cutscene speed modification * Changed command to remove to mitigate frame advantage * Antiepilepsy enabled for default/race roms, param change, RTL byte * Found a frame independent antiflashing patch for real * Further ASM patching style * Reduce these changes to just two bytes * Added patches for Dark Mountain and Ether Flashing palette reveal Co-authored-by: StructuralMike <66819228+StructuralMike@users.noreply.github.com> --- Adjuster.py | 1 + AdjusterMain.py | 3 ++- EntranceRandomizer.py | 3 ++- Gui.py | 11 +++++++++- Main.py | 2 +- Mystery.py | 1 + Rom.py | 19 +++++++++++++++++- .../static/static/weightedSettings.json | 20 +++++++++++++++++++ playerSettings.yaml | 3 +++ 9 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Adjuster.py b/Adjuster.py index 661381a9..f98f38fa 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -27,6 +27,7 @@ def main(): ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') + parser.add_argument('--enableflashing', help='Reenable flashing animations (unfriendly to epilepsy, always disabled in race roms)', action='store_false', dest="reduceflashing") parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'], help='''\ Select the rate at which the heart beep sound is played at diff --git a/AdjusterMain.py b/AdjusterMain.py index f26c669b..fa770b51 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -28,9 +28,10 @@ def adjust(args): palettes_options['sword']=args.sword_palettes palettes_options['shield']=args.shield_palettes # palettes_options['link']=args.link_palettesvera + racerom = rom.read_byte(0x180213) > 0 apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, - args.sprite, palettes_options) + args.sprite, palettes_options, reduceflashing=args.reduceflashing if not racerom else True) path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc') rom.write_to_file(path) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index f8404fe7..022bd96d 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -251,6 +251,7 @@ def parse_arguments(argv, no_defaults=False): ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') + parser.add_argument('--enableflashing', help='Reenable flashing animations (unfriendly to epilepsy, always disabled in race roms)', action='store_false', dest="reduceflashing") parser.add_argument('--mapshuffle', default=defval(False), help='Maps are no longer restricted to their dungeons, but can be anywhere', action='store_true') @@ -410,7 +411,7 @@ def parse_arguments(argv, no_defaults=False): "plando_items", "plando_texts", "plando_connections", 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', - 'restrict_dungeon_item_on_boss', + 'restrict_dungeon_item_on_boss', 'reduceflashing', 'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: diff --git a/Gui.py b/Gui.py index 19c62a85..86a71db7 100755 --- a/Gui.py +++ b/Gui.py @@ -130,6 +130,10 @@ def guiMain(args=None): disableMusicCheckbutton = Checkbutton(romOptionsFrame, text="Disable music", variable=disableMusicVar) disableMusicCheckbutton.grid(row=0, column=0, sticky=E) + disableFlashingVar = IntVar(value=1) + disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", variable=disableFlashingVar) + disableFlashingCheckbutton.grid(row=6, column=0, sticky=E) + spriteDialogFrame = Frame(romOptionsFrame) spriteDialogFrame.grid(row=0, column=1) baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') @@ -241,7 +245,7 @@ def guiMain(args=None): romDialogFrame = Frame(romOptionsFrame) - romDialogFrame.grid(row=6, column=0, columnspan=2, sticky=W+E) + romDialogFrame.grid(row=7, column=0, columnspan=2, sticky=W+E) baseRomLabel = Label(romDialogFrame, text='Base Rom: ') romVar = StringVar(value="Zelda no Densetsu - Kamigami no Triforce (Japan).sfc") @@ -577,6 +581,7 @@ def guiMain(args=None): guiargs.retro = bool(retroVar.get()) guiargs.quickswap = bool(quickSwapVar.get()) guiargs.disablemusic = bool(disableMusicVar.get()) + guiargs.reduceflashing = bool(disableFlashingVar.get()) guiargs.ow_palettes = owPalettesVar.get() guiargs.uw_palettes = uwPalettesVar.get() guiargs.hud_palettes = hudPalettesVar.get() @@ -697,9 +702,11 @@ def guiMain(args=None): quickSwapCheckbutton2 = Checkbutton(checkBoxFrame2, text="L/R Item quickswapping", variable=quickSwapVar) disableMusicCheckbutton2 = Checkbutton(checkBoxFrame2, text="Disable game music", variable=disableMusicVar) + disableFlashingCheckbutton2 = Checkbutton(checkBoxFrame2, text="Disable flashing (anti-epilepsy)", variable=disableFlashingVar) quickSwapCheckbutton2.pack(expand=True, anchor=W) disableMusicCheckbutton2.pack(expand=True, anchor=W) + disableFlashingCheckbutton2.pack(expand=True, anchor=W) fileDialogFrame2 = Frame(rightHalfFrame2) @@ -808,6 +815,7 @@ def guiMain(args=None): guiargs.shield_palettes = shieldPalettesVar.get() guiargs.quickswap = bool(quickSwapVar.get()) guiargs.disablemusic = bool(disableMusicVar.get()) + guiargs.reduceflashing = bool(disableFlashingVar.get()) guiargs.rom = romVar2.get() guiargs.baserom = romVar.get() guiargs.sprite = sprite @@ -1492,6 +1500,7 @@ def guiMain(args=None): retroVar.set(args.retro) quickSwapVar.set(int(args.quickswap)) disableMusicVar.set(int(args.disablemusic)) + disableFlashingVar.set(int(args.reduceflashing)) if args.count: countVar.set(str(args.count)) if args.seed: diff --git a/Main.py b/Main.py index fcd21c9a..2d22db67 100644 --- a/Main.py +++ b/Main.py @@ -259,7 +259,7 @@ def main(args, seed=None): apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], - palettes_options, world, player, True) + palettes_options, world, player, True, reduceflashing=args.reduceflashing[player] if not args.race else True) mcsb_name = '' if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], diff --git a/Mystery.py b/Mystery.py index 2041294b..92ab4182 100644 --- a/Mystery.py +++ b/Mystery.py @@ -691,6 +691,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b ret.disablemusic = get_choice('disablemusic', romweights, False) ret.quickswap = get_choice('quickswap', romweights, True) ret.fastmenu = get_choice('menuspeed', romweights, "normal") + ret.reduceflashing = get_choice('reduceflashing', romweights, False) ret.heartcolor = get_choice('heartcolor', romweights, "red") ret.heartbeep = convert_to_on_off(get_choice('heartbeep', romweights, "normal")) ret.ow_palettes = get_choice('ow_palettes', romweights, "default") diff --git a/Rom.py b/Rom.py index 285b7e52..e7c664df 100644 --- a/Rom.py +++ b/Rom.py @@ -1672,7 +1672,7 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite: str, palettes_options, - world=None, player=1, allow_random_on_event=False): + world=None, player=1, allow_random_on_event=False, reduceflashing=False): local_random = random if not world else world.rom_seeds[player] # enable instant item menu @@ -1697,6 +1697,23 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr else: rom.write_byte(0x180048, 0x08) + + # Reduce flashing by nopping out instructions + if reduceflashing: + rom.write_bytes(0x17E07, [0x06]) # reduce amount of colors changed, add this branch if we need to reduce more ""+ [0x80] + [(0x81-0x08)]"" + rom.write_bytes(0x17EAB, [0xD0, 0x03, 0xA9, 0x40, 0x29, 0x60]) # nullifies aga lightning, cutscene, vitreous, bat, ether + # ONLY write to black values with this low pale blue to indicate flashing, that's IT. ""BNE + : LDA #$2940 : + : RTS"" + rom.write_bytes(0x123FE, [0x72]) # set lightning flash in misery mire (and standard) to brightness 0x72 + rom.write_bytes(0x3FA7B, [0x80, 0xac-0x7b]) # branch from palette writing lightning on death mountain + rom.write_byte(0x10817F, 0x01) # internal rom option + else: + rom.write_bytes(0x17E07, [0x00]) + rom.write_bytes(0x17EAB, [0x85, 0x00, 0x29, 0x1F, 0x00, 0x18]) + rom.write_bytes(0x123FE, [0x32]) # original weather flash value + rom.write_bytes(0x3FA7B, [0xc2, 0x20]) # rep #$20 + rom.write_byte(0x10817F, 0x00) # internal rom option + + rom.write_byte(0x18004B, 0x01 if quickswap else 0x00) rom.write_byte(0x0CFE18, 0x00 if disable_music else rom.orig_buffer[0x0CFE18] if rom.orig_buffer else 0x70) diff --git a/WebHostLib/static/static/weightedSettings.json b/WebHostLib/static/static/weightedSettings.json index b6e1b7cb..d118c4bd 100644 --- a/WebHostLib/static/static/weightedSettings.json +++ b/WebHostLib/static/static/weightedSettings.json @@ -1602,6 +1602,26 @@ } } }, + "reduceflashing": { + "keyString": "rom.reduceflashing", + "friendlyName": "Reduce Flashing", + "description": "Disable the amount of flashing effects in-game", + "inputType": "range", + "subOptions": { + "on": { + "keyString": "rom.reduceflashing.on", + "friendlyName": "Disabled", + "description": "Disables flashing.", + "defaultValue": 50 + }, + "off": { + "keyString": "rom.reduceflashing.off", + "friendlyName": "Enabled", + "description": "Enables flashing.", + "defaultValue": 0 + } + } + }, "quickswap": { "keyString": "rom.quickswap", "friendlyName": "Item Quick-Swap", diff --git a/playerSettings.yaml b/playerSettings.yaml index f911f766..f212f49c 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -401,6 +401,9 @@ rom: quickswap: # Enable switching items by pressing the L+R shoulder buttons on: 50 off: 0 + reduceflashing: # Reduces instances of flashing such as lightning attacks, weather, ether and more. + on: 50 + off: 0 menuspeed: # Controls how fast the item menu opens and closes normal: 50 instant: 0 From 12222d5a4ca52d4ef93a7186f184537aaabc4e3e Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 19:08:11 +0100 Subject: [PATCH 05/14] Split adjuster into own program --- Adjuster.py | 140 ++++++++++++-- AdjusterMain.py | 41 ---- Gui.py | 499 +++++++++++++++++------------------------------- GuiUtils.py | 2 +- Utils.py | 4 +- setup.py | 7 +- 6 files changed, 307 insertions(+), 386 deletions(-) delete mode 100644 AdjusterMain.py diff --git a/Adjuster.py b/Adjuster.py index f98f38fa..4f3465fb 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -4,9 +4,11 @@ import os import logging import textwrap import sys +import time + +from Rom import Sprite, LocalRom, apply_rom_settings +from Utils import output_path -from AdjusterMain import adjust -from Rom import Sprite class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): @@ -51,25 +53,127 @@ def main(): parser.add_argument('--names', default='', type=str) args = parser.parse_args() - # ToDo: Validate files further than mere existance - if not os.path.isfile(args.rom): - input( - 'Could not find valid rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom) - sys.exit(1) - if args.sprite is not None and not os.path.isfile(args.sprite) and not Sprite.get_sprite_from_name(args.sprite): - input('Could not find link sprite sheet at given location. \nPress Enter to exit.') - sys.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) - args, path = adjust(args=args) - from Utils import persistent_store - from Rom import Sprite - if isinstance(args.sprite, Sprite): - args.sprite = args.sprite.name - persistent_store("adjuster", "last_settings_3", args) + + if not os.path.isfile(args.rom): + adjustGUI() + else: + if args.sprite is not None and not os.path.isfile(args.sprite) and not Sprite.get_sprite_from_name(args.sprite): + input('Could not find link sprite sheet at given location. \nPress Enter to exit.') + sys.exit(1) + + args, path = adjust(args=args) + from Utils import persistent_store + if isinstance(args.sprite, Sprite): + args.sprite = args.sprite.name + persistent_store("adjuster", "last_settings_3", args) + + +def adjust(args): + start = time.perf_counter() + logger = logging.getLogger('Adjuster') + logger.info('Patching ROM.') + vanillaRom = args.baserom + if os.path.splitext(args.rom)[-1].lower() == '.bmbp': + import Patch + meta, args.rom = Patch.create_rom_file(args.rom) + + if os.stat(args.rom).st_size in (0x200000, 0x400000) and os.path.splitext(args.rom)[-1].lower() == '.sfc': + rom = LocalRom(args.rom, patch=False, vanillaRom=vanillaRom) + else: + raise RuntimeError( + 'Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') + palettes_options={} + palettes_options['dungeon']=args.uw_palettes + + palettes_options['overworld']=args.ow_palettes + palettes_options['hud']=args.hud_palettes + palettes_options['sword']=args.sword_palettes + palettes_options['shield']=args.shield_palettes + # palettes_options['link']=args.link_palettesvera + racerom = rom.read_byte(0x180213) > 0 + + apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, + args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom) + path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc') + rom.write_to_file(path) + + logger.info('Done. Enjoy.') + logger.debug('Total Time: %s', time.perf_counter() - start) + + return args, path + +def adjustGUI(): + from tkinter import Checkbutton, OptionMenu, Toplevel, LabelFrame, PhotoImage, Tk, LEFT, RIGHT, BOTTOM, TOP, \ + StringVar, IntVar, Frame, Label, W, E, X, BOTH, Entry, Spinbox, Button, filedialog, messagebox, ttk + from Gui import get_rom_options_frame, get_rom_frame + from GuiUtils import set_icon + from argparse import Namespace + from Main import __version__ as MWVersion + adjustWindow = Tk() + adjustWindow.wm_title("Berserker's Multiworld %s LttP Adjuster" % MWVersion) + set_icon(adjustWindow) + + rom_options_frame, rom_vars, set_sprite = get_rom_options_frame(adjustWindow) + + bottomFrame2 = Frame(adjustWindow) + + romFrame, romVar = get_rom_frame(adjustWindow) + + romDialogFrame = Frame(adjustWindow) + baseRomLabel2 = Label(romDialogFrame, text='Rom to adjust') + romVar2 = StringVar() + romEntry2 = Entry(romDialogFrame, textvariable=romVar2) + + def RomSelect2(): + rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc", ".bmbp")), ("All Files", "*")]) + romVar2.set(rom) + romSelectButton2 = Button(romDialogFrame, text='Select Rom', command=RomSelect2) + romDialogFrame.pack(side=TOP, expand=True, fill=X) + baseRomLabel2.pack(side=LEFT) + romEntry2.pack(side=LEFT, expand=True, fill=X) + romSelectButton2.pack(side=LEFT) + + def adjustRom(): + guiargs = Namespace() + guiargs.heartbeep = rom_vars.heartbeepVar.get() + guiargs.heartcolor = rom_vars.heartcolorVar.get() + guiargs.fastmenu = rom_vars.fastMenuVar.get() + guiargs.ow_palettes = rom_vars.owPalettesVar.get() + guiargs.uw_palettes = rom_vars.uwPalettesVar.get() + guiargs.hud_palettes = rom_vars.hudPalettesVar.get() + guiargs.sword_palettes = rom_vars.swordPalettesVar.get() + guiargs.shield_palettes = rom_vars.shieldPalettesVar.get() + guiargs.quickswap = bool(rom_vars.quickSwapVar.get()) + guiargs.disablemusic = bool(rom_vars.disableMusicVar.get()) + guiargs.reduceflashing = bool(rom_vars.disableFlashingVar.get()) + guiargs.rom = romVar2.get() + guiargs.baserom = romVar.get() + guiargs.sprite = rom_vars.sprite + try: + guiargs, path = adjust(args=guiargs) + except Exception as e: + logging.exception(e) + messagebox.showerror(title="Error while adjusting Rom", message=str(e)) + else: + messagebox.showinfo(title="Success", message="Rom patched successfully") + from Utils import persistent_store + from Rom import Sprite + if isinstance(guiargs.sprite, Sprite): + guiargs.sprite = guiargs.sprite.name + persistent_store("adjuster", "last_settings_3", guiargs) + + adjustButton = Button(bottomFrame2, text='Adjust Rom', command=adjustRom) + rom_options_frame.pack(side=TOP) + adjustButton.pack(side=BOTTOM, padx=(5, 5)) + + bottomFrame2.pack(side=BOTTOM, pady=(5, 5)) + + adjustWindow.mainloop() + if __name__ == '__main__': - main() + main() \ No newline at end of file diff --git a/AdjusterMain.py b/AdjusterMain.py deleted file mode 100644 index fa770b51..00000000 --- a/AdjusterMain.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -import time -import logging - -from Utils import output_path -from Rom import LocalRom, apply_rom_settings - - -def adjust(args): - start = time.perf_counter() - logger = logging.getLogger('Adjuster') - logger.info('Patching ROM.') - vanillaRom = args.baserom - if os.path.splitext(args.rom)[-1].lower() == '.bmbp': - import Patch - meta, args.rom = Patch.create_rom_file(args.rom) - - if os.stat(args.rom).st_size in (0x200000, 0x400000) and os.path.splitext(args.rom)[-1].lower() == '.sfc': - rom = LocalRom(args.rom, patch=False, vanillaRom=vanillaRom) - else: - raise RuntimeError( - 'Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') - palettes_options={} - palettes_options['dungeon']=args.uw_palettes - - palettes_options['overworld']=args.ow_palettes - palettes_options['hud']=args.hud_palettes - palettes_options['sword']=args.sword_palettes - palettes_options['shield']=args.shield_palettes - # palettes_options['link']=args.link_palettesvera - racerom = rom.read_byte(0x180213) > 0 - - apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, - args.sprite, palettes_options, reduceflashing=args.reduceflashing if not racerom else True) - path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc') - rom.write_to_file(path) - - logger.info('Done. Enjoy.') - logger.debug('Total Time: %s', time.perf_counter() - start) - - return args, path diff --git a/Gui.py b/Gui.py index 86a71db7..296d7bae 100755 --- a/Gui.py +++ b/Gui.py @@ -14,12 +14,11 @@ from concurrent.futures import ThreadPoolExecutor, as_completed import ModuleUpdate ModuleUpdate.update() -from AdjusterMain import adjust from EntranceRandomizer import parse_arguments from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress from Main import main, get_seed, __version__ as MWVersion from Rom import Sprite -from Utils import is_bundled, local_path, output_path, open_file +from Utils import local_path, output_path, open_file def guiMain(args=None): @@ -30,10 +29,8 @@ def guiMain(args=None): notebook = ttk.Notebook(mainWindow) randomizerWindow = ttk.Frame(notebook) - adjustWindow = ttk.Frame(notebook) customWindow = ttk.Frame(notebook) notebook.add(randomizerWindow, text='Randomize') - notebook.add(adjustWindow, text='Adjust') notebook.add(customWindow, text='Custom Items') notebook.pack() @@ -57,6 +54,8 @@ def guiMain(args=None): # randomizer controls topFrame = Frame(randomizerWindow) + romFrame, romVar = get_rom_frame(topFrame) + rightHalfFrame = Frame(topFrame) checkBoxFrame = Frame(rightHalfFrame) @@ -119,158 +118,9 @@ def guiMain(args=None): hintsCheckbutton.pack(expand=True, anchor=W) tileShuffleButton.pack(expand=True, anchor=W) - - romOptionsFrame = LabelFrame(rightHalfFrame, text="Rom options") - romOptionsFrame.columnconfigure(0, weight=1) - romOptionsFrame.columnconfigure(1, weight=1) - for i in range(5): - romOptionsFrame.rowconfigure(i, weight=1) - - disableMusicVar = IntVar() - disableMusicCheckbutton = Checkbutton(romOptionsFrame, text="Disable music", variable=disableMusicVar) - disableMusicCheckbutton.grid(row=0, column=0, sticky=E) - - disableFlashingVar = IntVar(value=1) - disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", variable=disableFlashingVar) - disableFlashingCheckbutton.grid(row=6, column=0, sticky=E) - - spriteDialogFrame = Frame(romOptionsFrame) - spriteDialogFrame.grid(row=0, column=1) - baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') - - spriteNameVar = StringVar() - sprite = None - def set_sprite(sprite_param): - nonlocal sprite - if isinstance(sprite_param, str): - sprite = sprite_param - spriteNameVar.set(sprite_param) - elif sprite_param is None or not sprite_param.valid: - sprite = None - spriteNameVar.set('(unchanged)') - else: - sprite = sprite_param - spriteNameVar.set(sprite.name) - - set_sprite(None) - spriteNameVar.set('(unchanged)') - spriteEntry = Label(spriteDialogFrame, textvariable=spriteNameVar) - - def SpriteSelect(): - SpriteSelector(mainWindow, set_sprite) - - spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect) - - baseSpriteLabel.pack(side=LEFT) - spriteEntry.pack(side=LEFT) - spriteSelectButton.pack(side=LEFT) - - quickSwapVar = IntVar(value=1) - quickSwapCheckbutton = Checkbutton(romOptionsFrame, text="L/R Quickswapping", variable=quickSwapVar) - quickSwapCheckbutton.grid(row=1, column=0, sticky=E) - - fastMenuFrame = Frame(romOptionsFrame) - fastMenuFrame.grid(row=1, column=1, sticky=E) - fastMenuLabel = Label(fastMenuFrame, text='Menu speed') - fastMenuLabel.pack(side=LEFT) - fastMenuVar = StringVar() - fastMenuVar.set('normal') - fastMenuOptionMenu = OptionMenu(fastMenuFrame, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') - fastMenuOptionMenu.pack(side=LEFT) - - heartcolorFrame = Frame(romOptionsFrame) - heartcolorFrame.grid(row=2, column=0, sticky=E) - heartcolorLabel = Label(heartcolorFrame, text='Heart color') - heartcolorLabel.pack(side=LEFT) - heartcolorVar = StringVar() - heartcolorVar.set('red') - heartcolorOptionMenu = OptionMenu(heartcolorFrame, heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') - heartcolorOptionMenu.pack(side=LEFT) - - heartbeepFrame = Frame(romOptionsFrame) - heartbeepFrame.grid(row=2, column=1, sticky=E) - heartbeepLabel = Label(heartbeepFrame, text='Heartbeep') - heartbeepLabel.pack(side=LEFT) - heartbeepVar = StringVar() - heartbeepVar.set('normal') - heartbeepOptionMenu = OptionMenu(heartbeepFrame, heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') - heartbeepOptionMenu.pack(side=LEFT) - - owPalettesFrame = Frame(romOptionsFrame) - owPalettesFrame.grid(row=3, column=0, sticky=E) - owPalettesLabel = Label(owPalettesFrame, text='Overworld palettes') - owPalettesLabel.pack(side=LEFT) - owPalettesVar = StringVar() - owPalettesVar.set('default') - owPalettesOptionMenu = OptionMenu(owPalettesFrame, owPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - owPalettesOptionMenu.pack(side=LEFT) - - uwPalettesFrame = Frame(romOptionsFrame) - uwPalettesFrame.grid(row=3, column=1, sticky=E) - uwPalettesLabel = Label(uwPalettesFrame, text='Dungeon palettes') - uwPalettesLabel.pack(side=LEFT) - uwPalettesVar = StringVar() - uwPalettesVar.set('default') - uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, uwPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - uwPalettesOptionMenu.pack(side=LEFT) - - hudPalettesFrame = Frame(romOptionsFrame) - hudPalettesFrame.grid(row=4, column=0, sticky=E) - hudPalettesLabel = Label(hudPalettesFrame, text='HUD palettes') - hudPalettesLabel.pack(side=LEFT) - hudPalettesVar = StringVar() - hudPalettesVar.set('default') - hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, hudPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - hudPalettesOptionMenu.pack(side=LEFT) - - swordPalettesFrame = Frame(romOptionsFrame) - swordPalettesFrame.grid(row=4, column=1, sticky=E) - swordPalettesLabel = Label(swordPalettesFrame, text='Sword palettes') - swordPalettesLabel.pack(side=LEFT) - swordPalettesVar = StringVar() - swordPalettesVar.set('default') - swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, swordPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - swordPalettesOptionMenu.pack(side=LEFT) - - shieldPalettesFrame = Frame(romOptionsFrame) - shieldPalettesFrame.grid(row=5, column=0, sticky=E) - shieldPalettesLabel = Label(shieldPalettesFrame, text='Shield palettes') - shieldPalettesLabel.pack(side=LEFT) - shieldPalettesVar = StringVar() - shieldPalettesVar.set('default') - shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, shieldPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - shieldPalettesOptionMenu.pack(side=LEFT) - - - - - romDialogFrame = Frame(romOptionsFrame) - romDialogFrame.grid(row=7, column=0, columnspan=2, sticky=W+E) - - baseRomLabel = Label(romDialogFrame, text='Base Rom: ') - romVar = StringVar(value="Zelda no Densetsu - Kamigami no Triforce (Japan).sfc") - romEntry = Entry(romDialogFrame, textvariable=romVar) - - def RomSelect(): - rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) - import Patch - try: - Patch.get_base_rom_bytes(rom) # throws error on checksum fail - except Exception as e: - logging.exception(e) - messagebox.showerror(title="Error while reading ROM", message=str(e)) - else: - romVar.set(rom) - romSelectButton['state'] = "disabled" - romSelectButton["text"] = "ROM verified" - romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect) - - baseRomLabel.pack(side=LEFT) - romEntry.pack(side=LEFT, expand=True, fill=X) - romSelectButton.pack(side=LEFT) - checkBoxFrame.pack(side=TOP, anchor=W, padx=5, pady=10) + romOptionsFrame, rom_vars, set_sprite = get_rom_options_frame(rightHalfFrame) romOptionsFrame.pack(expand=True, fill=BOTH, padx=3) drowDownFrame = Frame(topFrame) @@ -567,9 +417,9 @@ def guiMain(args=None): guiargs.accessibility = accessibilityVar.get() guiargs.algorithm = algorithmVar.get() guiargs.shuffle = shuffleVar.get() - guiargs.heartbeep = heartbeepVar.get() - guiargs.heartcolor = heartcolorVar.get() - guiargs.fastmenu = fastMenuVar.get() + guiargs.heartbeep = rom_vars.heartbeepVar.get() + guiargs.heartcolor = rom_vars.heartcolorVar.get() + guiargs.fastmenu = rom_vars.fastMenuVar.get() guiargs.create_spoiler = bool(createSpoilerVar.get()) guiargs.skip_playthrough = not bool(createSpoilerVar.get()) guiargs.suppress_rom = bool(suppressRomVar.get()) @@ -579,14 +429,14 @@ def guiMain(args=None): guiargs.keyshuffle = {"on": True, "universal": "universal", "off": False}[keyshuffleVar.get()] guiargs.bigkeyshuffle = bool(bigkeyshuffleVar.get()) guiargs.retro = bool(retroVar.get()) - guiargs.quickswap = bool(quickSwapVar.get()) - guiargs.disablemusic = bool(disableMusicVar.get()) - guiargs.reduceflashing = bool(disableFlashingVar.get()) - guiargs.ow_palettes = owPalettesVar.get() - guiargs.uw_palettes = uwPalettesVar.get() - guiargs.hud_palettes = hudPalettesVar.get() - guiargs.sword_palettes = swordPalettesVar.get() - guiargs.shield_palettes = shieldPalettesVar.get() + guiargs.quickswap = bool(rom_vars.quickSwapVar.get()) + guiargs.disablemusic = bool(rom_vars.disableMusicVar.get()) + guiargs.reduceflashing = bool(rom_vars.disableFlashingVar.get()) + guiargs.ow_palettes = rom_vars.owPalettesVar.get() + guiargs.uw_palettes = rom_vars.uwPalettesVar.get() + guiargs.hud_palettes = rom_vars.hudPalettesVar.get() + guiargs.sword_palettes = rom_vars.swordPalettesVar.get() + guiargs.shield_palettes = rom_vars.shieldPalettesVar.get() guiargs.shuffleganon = bool(shuffleGanonVar.get()) guiargs.hints = bool(hintsVar.get()) guiargs.enemizercli = enemizerCLIpathVar.get() @@ -646,7 +496,7 @@ def guiMain(args=None): int(rupoorcostVar.get()), int(triforceVar.get())] guiargs.rom = romVar.get() guiargs.create_diff = patchesVar.get() - guiargs.sprite = sprite + guiargs.sprite = rom_vars.sprite # get default values for missing parameters for k,v in vars(parse_arguments(['--multi', str(guiargs.multi)])).items(): if k not in vars(guiargs): @@ -694,153 +544,6 @@ def guiMain(args=None): enemizerFrame.pack(side=BOTTOM, fill=BOTH) shopframe.pack(side=BOTTOM, expand=True, fill=X) - # Adjuster Controls - - topFrame2 = Frame(adjustWindow) - rightHalfFrame2 = Frame(topFrame2) - checkBoxFrame2 = Frame(rightHalfFrame2) - - quickSwapCheckbutton2 = Checkbutton(checkBoxFrame2, text="L/R Item quickswapping", variable=quickSwapVar) - disableMusicCheckbutton2 = Checkbutton(checkBoxFrame2, text="Disable game music", variable=disableMusicVar) - disableFlashingCheckbutton2 = Checkbutton(checkBoxFrame2, text="Disable flashing (anti-epilepsy)", variable=disableFlashingVar) - - quickSwapCheckbutton2.pack(expand=True, anchor=W) - disableMusicCheckbutton2.pack(expand=True, anchor=W) - disableFlashingCheckbutton2.pack(expand=True, anchor=W) - - fileDialogFrame2 = Frame(rightHalfFrame2) - - romDialogFrame2 = Frame(fileDialogFrame2) - baseRomLabel2 = Label(romDialogFrame2, text='Rom to adjust') - romVar2 = StringVar() - romEntry2 = Entry(romDialogFrame2, textvariable=romVar2) - - def RomSelect2(): - rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc", ".bmbp")), ("All Files", "*")]) - romVar2.set(rom) - romSelectButton2 = Button(romDialogFrame2, text='Select Rom', command=RomSelect2) - - baseRomLabel2.pack(side=LEFT) - romEntry2.pack(side=LEFT) - romSelectButton2.pack(side=LEFT) - - spriteDialogFrame2 = Frame(fileDialogFrame2) - baseSpriteLabel2 = Label(spriteDialogFrame2, text='Link Sprite') - spriteEntry2 = Label(spriteDialogFrame2, textvariable=spriteNameVar) - - def SpriteSelectAdjuster(): - SpriteSelector(mainWindow, set_sprite, adjuster=True) - - spriteSelectButton2 = Button(spriteDialogFrame2, text='Select Sprite', command=SpriteSelectAdjuster) - - baseSpriteLabel2.pack(side=LEFT) - spriteEntry2.pack(side=LEFT) - spriteSelectButton2.pack(side=LEFT) - - romDialogFrame2.pack() - spriteDialogFrame2.pack() - - checkBoxFrame2.pack() - fileDialogFrame2.pack() - - drowDownFrame2 = Frame(topFrame2) - heartbeepFrame2 = Frame(drowDownFrame2) - heartbeepOptionMenu2 = OptionMenu(heartbeepFrame2, heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') - heartbeepOptionMenu2.pack(side=RIGHT) - heartbeepLabel2 = Label(heartbeepFrame2, text='Heartbeep sound rate') - heartbeepLabel2.pack(side=LEFT) - - heartcolorFrame2 = Frame(drowDownFrame2) - heartcolorOptionMenu2 = OptionMenu(heartcolorFrame2, heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') - heartcolorOptionMenu2.pack(side=RIGHT) - heartcolorLabel2 = Label(heartcolorFrame2, text='Heart color') - heartcolorLabel2.pack(side=LEFT) - - fastMenuFrame2 = Frame(drowDownFrame2) - fastMenuOptionMenu2 = OptionMenu(fastMenuFrame2, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') - fastMenuOptionMenu2.pack(side=RIGHT) - fastMenuLabel2 = Label(fastMenuFrame2, text='Menu speed') - fastMenuLabel2.pack(side=LEFT) - - owPalettesFrame2 = Frame(drowDownFrame2) - owPalettesOptionMenu2 = OptionMenu(owPalettesFrame2, owPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - owPalettesOptionMenu2.pack(side=RIGHT) - owPalettesLabel2 = Label(owPalettesFrame2, text='Overworld palettes') - owPalettesLabel2.pack(side=LEFT) - - uwPalettesFrame2 = Frame(drowDownFrame2) - uwPalettesOptionMenu2 = OptionMenu(uwPalettesFrame2, uwPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - uwPalettesOptionMenu2.pack(side=RIGHT) - uwPalettesLabel2 = Label(uwPalettesFrame2, text='Dungeon palettes') - uwPalettesLabel2.pack(side=LEFT) - - hudPalettesFrame2 = Frame(drowDownFrame2) - hudPalettesOptionMenu2 = OptionMenu(hudPalettesFrame2, hudPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - hudPalettesOptionMenu2.pack(side=RIGHT) - hudPalettesLabel2 = Label(hudPalettesFrame2, text='HUD palettes') - hudPalettesLabel2.pack(side=LEFT) - - swordPalettesFrame2 = Frame(drowDownFrame2) - swordPalettesOptionMenu2 = OptionMenu(swordPalettesFrame2, swordPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - swordPalettesOptionMenu2.pack(side=RIGHT) - swordPalettesLabel2 = Label(swordPalettesFrame2, text='Sword palettes') - swordPalettesLabel2.pack(side=LEFT) - - shieldPalettesFrame2 = Frame(drowDownFrame2) - shieldPalettesOptionMenu2 = OptionMenu(shieldPalettesFrame2, shieldPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') - shieldPalettesOptionMenu2.pack(side=RIGHT) - shieldPalettesLabel2 = Label(shieldPalettesFrame2, text='Shield palettes') - shieldPalettesLabel2.pack(side=LEFT) - - heartbeepFrame2.pack(expand=True, anchor=E) - heartcolorFrame2.pack(expand=True, anchor=E) - fastMenuFrame2.pack(expand=True, anchor=E) - owPalettesFrame2.pack(expand=True, anchor=E) - uwPalettesFrame2.pack(expand=True, anchor=E) - hudPalettesFrame2.pack(expand=True, anchor=E) - swordPalettesFrame2.pack(expand=True, anchor=E) - shieldPalettesFrame2.pack(expand=True, anchor=E) - - bottomFrame2 = Frame(topFrame2) - - def adjustRom(): - guiargs = Namespace() - guiargs.heartbeep = heartbeepVar.get() - guiargs.heartcolor = heartcolorVar.get() - guiargs.fastmenu = fastMenuVar.get() - guiargs.ow_palettes = owPalettesVar.get() - guiargs.uw_palettes = uwPalettesVar.get() - guiargs.hud_palettes = hudPalettesVar.get() - guiargs.sword_palettes = swordPalettesVar.get() - guiargs.shield_palettes = shieldPalettesVar.get() - guiargs.quickswap = bool(quickSwapVar.get()) - guiargs.disablemusic = bool(disableMusicVar.get()) - guiargs.reduceflashing = bool(disableFlashingVar.get()) - guiargs.rom = romVar2.get() - guiargs.baserom = romVar.get() - guiargs.sprite = sprite - try: - guiargs, path = adjust(args=guiargs) - except Exception as e: - logging.exception(e) - messagebox.showerror(title="Error while adjusting Rom", message=str(e)) - else: - messagebox.showinfo(title="Success", message="Rom patched successfully") - from Utils import persistent_store - from Rom import Sprite - if isinstance(guiargs.sprite, Sprite): - guiargs.sprite = guiargs.sprite.name - persistent_store("adjuster", "last_settings_3", guiargs) - - adjustButton = Button(bottomFrame2, text='Adjust Rom', command=adjustRom) - - adjustButton.pack(side=LEFT, padx=(5, 0)) - - drowDownFrame2.pack(side=LEFT, pady=(0, 40)) - rightHalfFrame2.pack(side=RIGHT) - topFrame2.pack(side=TOP, pady=70) - bottomFrame2.pack(side=BOTTOM, pady=(180, 0)) - # Custom Controls topFrame3 = Frame(customWindow) @@ -1498,9 +1201,9 @@ def guiMain(args=None): keyshuffleVar.set(args.keyshuffle) bigkeyshuffleVar.set(args.bigkeyshuffle) retroVar.set(args.retro) - quickSwapVar.set(int(args.quickswap)) - disableMusicVar.set(int(args.disablemusic)) - disableFlashingVar.set(int(args.reduceflashing)) + rom_vars.quickSwapVar.set(int(args.quickswap)) + rom_vars.disableMusicVar.set(int(args.disablemusic)) + rom_vars.disableFlashingVar.set(int(args.reduceflashing)) if args.count: countVar.set(str(args.count)) if args.seed: @@ -1521,10 +1224,10 @@ def guiMain(args=None): crystalsGanonVar.set(args.crystals_ganon) algorithmVar.set(args.algorithm) shuffleVar.set(args.shuffle) - heartbeepVar.set(args.heartbeep) - fastMenuVar.set(args.fastmenu) + rom_vars.heartbeepVar.set(args.heartbeep) + rom_vars.fastMenuVar.set(args.fastmenu) logicVar.set(args.logic) - romVar.set(args.rom) + rom_vars.romVar.set(args.rom) shuffleGanonVar.set(args.shuffleganon) hintsVar.set(args.hints) if args.sprite is not None: @@ -1532,11 +1235,165 @@ def guiMain(args=None): mainWindow.mainloop() +def get_rom_frame(parent=None): + romFrame = Frame(parent) + baseRomLabel = Label(romFrame, text='LttP Base Rom: ') + romVar = StringVar(value="Zelda no Densetsu - Kamigami no Triforce (Japan).sfc") + romEntry = Entry(romFrame, textvariable=romVar) + + def RomSelect(): + rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) + import Patch + try: + Patch.get_base_rom_bytes(rom) # throws error on checksum fail + except Exception as e: + logging.exception(e) + messagebox.showerror(title="Error while reading ROM", message=str(e)) + else: + romVar.set(rom) + romSelectButton['state'] = "disabled" + romSelectButton["text"] = "ROM verified" + romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect) + + baseRomLabel.pack(side=LEFT) + romEntry.pack(side=LEFT, expand=True, fill=X) + romSelectButton.pack(side=LEFT) + romFrame.pack(side=TOP, expand=True, fill=X) + + return romFrame, romVar + + +def get_rom_options_frame(parent=None): + romOptionsFrame = LabelFrame(parent, text="Rom options") + romOptionsFrame.columnconfigure(0, weight=1) + romOptionsFrame.columnconfigure(1, weight=1) + for i in range(5): + romOptionsFrame.rowconfigure(i, weight=1) + vars = Namespace() + + vars.disableMusicVar = IntVar() + disableMusicCheckbutton = Checkbutton(romOptionsFrame, text="Disable music", variable=vars.disableMusicVar) + disableMusicCheckbutton.grid(row=0, column=0, sticky=E) + + vars.disableFlashingVar = IntVar(value=1) + disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", variable=vars.disableFlashingVar) + disableFlashingCheckbutton.grid(row=6, column=0, sticky=E) + + spriteDialogFrame = Frame(romOptionsFrame) + spriteDialogFrame.grid(row=0, column=1) + baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') + + + + vars.spriteNameVar = StringVar() + vars.sprite = None + def set_sprite(sprite_param): + nonlocal vars + if isinstance(sprite_param, str): + vars.sprite = sprite_param + vars.spriteNameVar.set(sprite_param) + elif sprite_param is None or not sprite_param.valid: + vars.sprite = None + vars.spriteNameVar.set('(unchanged)') + else: + vars.sprite = sprite_param + vars.spriteNameVar.set(vars.sprite.name) + + set_sprite(None) + vars.spriteNameVar.set('(unchanged)') + spriteEntry = Label(spriteDialogFrame, textvariable=vars.spriteNameVar) + + def SpriteSelect(): + SpriteSelector(parent, set_sprite) + + spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect) + + baseSpriteLabel.pack(side=LEFT) + spriteEntry.pack(side=LEFT) + spriteSelectButton.pack(side=LEFT) + + vars.quickSwapVar = IntVar(value=1) + quickSwapCheckbutton = Checkbutton(romOptionsFrame, text="L/R Quickswapping", variable=vars.quickSwapVar) + quickSwapCheckbutton.grid(row=1, column=0, sticky=E) + + fastMenuFrame = Frame(romOptionsFrame) + fastMenuFrame.grid(row=1, column=1, sticky=E) + fastMenuLabel = Label(fastMenuFrame, text='Menu speed') + fastMenuLabel.pack(side=LEFT) + vars.fastMenuVar = StringVar() + vars.fastMenuVar.set('normal') + fastMenuOptionMenu = OptionMenu(fastMenuFrame, vars.fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') + fastMenuOptionMenu.pack(side=LEFT) + + heartcolorFrame = Frame(romOptionsFrame) + heartcolorFrame.grid(row=2, column=0, sticky=E) + heartcolorLabel = Label(heartcolorFrame, text='Heart color') + heartcolorLabel.pack(side=LEFT) + vars.heartcolorVar = StringVar() + vars.heartcolorVar.set('red') + heartcolorOptionMenu = OptionMenu(heartcolorFrame, vars.heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') + heartcolorOptionMenu.pack(side=LEFT) + + heartbeepFrame = Frame(romOptionsFrame) + heartbeepFrame.grid(row=2, column=1, sticky=E) + heartbeepLabel = Label(heartbeepFrame, text='Heartbeep') + heartbeepLabel.pack(side=LEFT) + vars.heartbeepVar = StringVar() + vars.heartbeepVar.set('normal') + heartbeepOptionMenu = OptionMenu(heartbeepFrame, vars.heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') + heartbeepOptionMenu.pack(side=LEFT) + + owPalettesFrame = Frame(romOptionsFrame) + owPalettesFrame.grid(row=3, column=0, sticky=E) + owPalettesLabel = Label(owPalettesFrame, text='Overworld palettes') + owPalettesLabel.pack(side=LEFT) + vars.owPalettesVar = StringVar() + vars.owPalettesVar.set('default') + owPalettesOptionMenu = OptionMenu(owPalettesFrame, vars.owPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + owPalettesOptionMenu.pack(side=LEFT) + + uwPalettesFrame = Frame(romOptionsFrame) + uwPalettesFrame.grid(row=3, column=1, sticky=E) + uwPalettesLabel = Label(uwPalettesFrame, text='Dungeon palettes') + uwPalettesLabel.pack(side=LEFT) + vars.uwPalettesVar = StringVar() + vars.uwPalettesVar.set('default') + uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, vars.uwPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + uwPalettesOptionMenu.pack(side=LEFT) + + hudPalettesFrame = Frame(romOptionsFrame) + hudPalettesFrame.grid(row=4, column=0, sticky=E) + hudPalettesLabel = Label(hudPalettesFrame, text='HUD palettes') + hudPalettesLabel.pack(side=LEFT) + vars.hudPalettesVar = StringVar() + vars.hudPalettesVar.set('default') + hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, vars.hudPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + hudPalettesOptionMenu.pack(side=LEFT) + + swordPalettesFrame = Frame(romOptionsFrame) + swordPalettesFrame.grid(row=4, column=1, sticky=E) + swordPalettesLabel = Label(swordPalettesFrame, text='Sword palettes') + swordPalettesLabel.pack(side=LEFT) + vars.swordPalettesVar = StringVar() + vars.swordPalettesVar.set('default') + swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, vars.swordPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + swordPalettesOptionMenu.pack(side=LEFT) + + shieldPalettesFrame = Frame(romOptionsFrame) + shieldPalettesFrame.grid(row=5, column=0, sticky=E) + shieldPalettesLabel = Label(shieldPalettesFrame, text='Shield palettes') + shieldPalettesLabel.pack(side=LEFT) + vars.shieldPalettesVar = StringVar() + vars.shieldPalettesVar.set('default') + shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + shieldPalettesOptionMenu.pack(side=LEFT) + + return romOptionsFrame, vars, set_sprite + class SpriteSelector(): def __init__(self, parent, callback, adjuster=False): - if is_bundled(): - self.deploy_icons() + self.deploy_icons() self.parent = parent self.window = Toplevel(parent) self.callback = callback diff --git a/GuiUtils.py b/GuiUtils.py index c0542076..9e9e33e9 100644 --- a/GuiUtils.py +++ b/GuiUtils.py @@ -25,7 +25,7 @@ class BackgroundTask(object): def stop(self): self.running = False - #safe to call from worker + # safe to call from worker def queue_event(self, event): self.queue.put(event) diff --git a/Utils.py b/Utils.py index 602b7751..758f0058 100644 --- a/Utils.py +++ b/Utils.py @@ -343,8 +343,8 @@ def get_adjuster_settings(romfile: str) -> typing.Tuple[str, bool]: f"Enter yes, no or never: ") if adjust_wanted and adjust_wanted.startswith("y"): adjusted = True - import AdjusterMain - _, romfile = AdjusterMain.adjust(adjuster_settings) + import Adjuster + _, romfile = Adjuster.adjust(adjuster_settings) elif adjust_wanted and "never" in adjust_wanted: persistent_store("adjuster", "never_adjust", True) return romfile, False diff --git a/setup.py b/setup.py index 48872d7b..73e2e9c0 100644 --- a/setup.py +++ b/setup.py @@ -48,8 +48,8 @@ def manifest_creation(): path = os.path.join(dirpath, filename) hashes[os.path.relpath(path, start=buildfolder)] = pool.submit(_threaded_hash, path) import json - manifest = {"buildtime": buildtime.isoformat(sep=" ", timespec="seconds")} - manifest["hashes"] = {path: hash.result() for path, hash in hashes.items()} + manifest = {"buildtime": buildtime.isoformat(sep=" ", timespec="seconds"), + "hashes": {path: hash.result() for path, hash in hashes.items()}} json.dump(manifest, open(manifestpath, "wt"), indent=4) print("Created Manifest") @@ -58,7 +58,8 @@ scripts = {"MultiClient.py": "BerserkerMultiClient", "MultiMystery.py": "BerserkerMultiMystery", "MultiServer.py": "BerserkerMultiServer", "gui.py": "BerserkerMultiCreator", - "Mystery.py": "BerserkerMystery"} + "Mystery.py": "BerserkerMystery", + "Adjuster.py": "BerserkerLttPAdjuster"} exes = [] From 596552037c019fed6dbd95ceb7746b5c6d754b64 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 19:10:01 +0100 Subject: [PATCH 06/14] mention where a rom is patched to --- Adjuster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adjuster.py b/Adjuster.py index 4f3465fb..781b513f 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -159,7 +159,7 @@ def adjustGUI(): logging.exception(e) messagebox.showerror(title="Error while adjusting Rom", message=str(e)) else: - messagebox.showinfo(title="Success", message="Rom patched successfully") + messagebox.showinfo(title="Success", message=f"Rom patched successfully to {path}") from Utils import persistent_store from Rom import Sprite if isinstance(guiargs.sprite, Sprite): From cb02977c1ca86457f2d51054c88eca2a01dec852 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 19 Feb 2021 19:44:25 +0100 Subject: [PATCH 07/14] update ingame text to match license file --- Text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Text.py b/Text.py index 460d78e5..0f314f4b 100644 --- a/Text.py +++ b/Text.py @@ -1884,7 +1884,7 @@ class TextTable(object): text['item_get_whole_heart'] = CompressedTextMapper.convert("You got a whole ♥!!\nGo you!") text['item_get_sanc_heart'] = CompressedTextMapper.convert("You got a whole ♥!\nGo you!") text['fairy_fountain_refill'] = CompressedTextMapper.convert("Well done, lettuce have a cup of tea…") - text['death_mountain_bullied_no_pearl'] = CompressedTextMapper.convert("The following license applies to the base patch for the randomizer.\n\nCopyright (c) 2017 LLCoolDave\n\nCopyright (c) 2020 Berserker66\n\nCopyright (c) 2020 CaitSith2\n\nCopyright 2016, 2017 Equilateral IT\n\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.") + text['death_mountain_bullied_no_pearl'] = CompressedTextMapper.convert("The following license applies to the base patch for the randomizer.\n\nCopyright (c) 2017 LLCoolDave\n\nCopyright (c) 2021 Berserker66\n\nCopyright (c) 2021 CaitSith2\n\nCopyright 2016, 2017 Equilateral IT\n\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.") text['death_mountain_bullied_with_pearl'] = CompressedTextMapper.convert("The software is provided \"as is\", without warranty of any kind, express or implied, including but not limited to the warranties of\nmerchantability,\nfitness for a particular purpose and\nnoninfringement.\nIn no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the software.") text['death_mountain_bully_no_pearl'] = CompressedTextMapper.convert("Add garlic, ginger and apple and cook for 2 minutes. Add carrots, potatoes, garam masala and curry powder and stir well. Add tomato paste, stir well and slowly add red wine and bring to a boil. Add sugar, soy sauce and water, stir and bring to a boil again.") text['death_mountain_bully_with_pearl'] = CompressedTextMapper.convert("I think I forgot how to smile…") From 5e8b4ac3ceabdf4265181d88767d2931145533aa Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 19 Feb 2021 17:50:49 -0500 Subject: [PATCH 08/14] Weighted Settings v4.0.1 rev1 - Fix a bug (it was a typo) causing the "puke" palettes to never be updated on weighted settings files generated from the website. - Also added the "reduceflashing" option to weighted settings --- WebHostLib/static/static/weightedSettings.json | 14 +++++++------- WebHostLib/static/static/weightedSettings.yaml | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/WebHostLib/static/static/weightedSettings.json b/WebHostLib/static/static/weightedSettings.json index d118c4bd..cd0bbfbf 100644 --- a/WebHostLib/static/static/weightedSettings.json +++ b/WebHostLib/static/static/weightedSettings.json @@ -1604,8 +1604,8 @@ }, "reduceflashing": { "keyString": "rom.reduceflashing", - "friendlyName": "Reduce Flashing", - "description": "Disable the amount of flashing effects in-game", + "friendlyName": "Full-Screen Flashing Effects", + "description": "Enable or disable full-screen flashing effects in game.", "inputType": "range", "subOptions": { "on": { @@ -1817,7 +1817,7 @@ "defaultValue": 0 }, "puke": { - "keyString": "rom.ow_palettes.Puke", + "keyString": "rom.ow_palettes.puke", "friendlyName": "Puke", "description": "No logic at all.", "defaultValue": 0 @@ -1879,7 +1879,7 @@ "defaultValue": 0 }, "puke": { - "keyString": "rom.uw_palettes.Puke", + "keyString": "rom.uw_palettes.puke", "friendlyName": "Puke", "description": "No logic at all.", "defaultValue": 0 @@ -1941,7 +1941,7 @@ "defaultValue": 0 }, "puke": { - "keyString": "rom.hud_palettes.Puke", + "keyString": "rom.hud_palettes.puke", "friendlyName": "Puke", "description": "No logic at all.", "defaultValue": 0 @@ -2003,7 +2003,7 @@ "defaultValue": 0 }, "puke": { - "keyString": "rom.shield_palettes.Puke", + "keyString": "rom.shield_palettes.puke", "friendlyName": "Puke", "description": "No logic at all.", "defaultValue": 0 @@ -2065,7 +2065,7 @@ "defaultValue": 0 }, "puke": { - "keyString": "rom.sword_palettes.Puke", + "keyString": "rom.sword_palettes.puke", "friendlyName": "Puke", "description": "No logic at all.", "defaultValue": 0 diff --git a/WebHostLib/static/static/weightedSettings.yaml b/WebHostLib/static/static/weightedSettings.yaml index 6fe041c3..657c32f6 100644 --- a/WebHostLib/static/static/weightedSettings.yaml +++ b/WebHostLib/static/static/weightedSettings.yaml @@ -20,7 +20,7 @@ # For use with the weighted-settings page on the website. Changing this value will cause all users to be prompted # to update their settings. The version number should match the current released version number, and the revision # should be updated manually by whoever edits this file. -ws_version: 4.0.1 rev0 +ws_version: 4.0.1 rev1 description: Template Name # Used to describe your yaml. Useful if you have multiple files name: YourName # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit @@ -363,6 +363,9 @@ rom: quickswap: # Enable switching items by pressing the L+R shoulder buttons on: 50 off: 0 + reduceflashing: # Reduces instances of flashing such as lightning attacks, weather, ether and more. + on: 50 + off: 0 menuspeed: # Controls how fast the item menu opens and closes normal: 50 instant: 0 From a7cbb440d1b2acb8732a1ba94959d47e2c1dd924 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 20 Feb 2021 01:48:24 +0100 Subject: [PATCH 09/14] speed up connecting single-doors --- EntranceShuffle.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index f4c68d1c..605ede2c 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2054,10 +2054,11 @@ def connect_doors(world, doors, targets, player): """This works inplace""" world.random.shuffle(doors) world.random.shuffle(targets) - while doors: - door = doors.pop() - target = targets.pop() + placing = min(len(doors), len(targets)) + for door, target in zip(doors, targets): connect_entrance(world, door, target, player) + doors[:] = doors[placing:] + targets[:] = targets[placing:] def skull_woods_shuffle(world, player): From c55cf28229fa28e2af3370f48b1b4ae01173c05c Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 20 Feb 2021 02:30:55 +0100 Subject: [PATCH 10/14] allow ER coop --- BaseClasses.py | 5 +++++ Fill.py | 2 +- Gui.py | 6 +++++- Main.py | 10 ++++++++++ playerSettings.yaml | 3 +++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 14b48db3..4d054e17 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -4,6 +4,7 @@ import copy from enum import Enum, unique import logging import json +import functools from collections import OrderedDict, Counter, deque from typing import Union, Optional, List, Dict, NamedTuple, Iterable import secrets @@ -160,6 +161,10 @@ class World(object): region.world = self self._region_cache[region.player][region.name] = region + @functools.cached_property + def world_name_lookup(self): + return {self.player_names[player_id][0]: player_id for player_id in self.player_ids} + def _recache(self): """Rebuild world cache""" for region in self.regions: diff --git a/Fill.py b/Fill.py index 6a7219c9..0e797be1 100644 --- a/Fill.py +++ b/Fill.py @@ -359,7 +359,7 @@ def swap_location_item(location_1: Location, location_2: Location, check_locked= def distribute_planned(world): - world_name_lookup = {world.player_names[player_id][0]: player_id for player_id in world.player_ids} + world_name_lookup = world.world_name_lookup for player in world.player_ids: placement: PlandoItem diff --git a/Gui.py b/Gui.py index 296d7bae..d04f7352 100755 --- a/Gui.py +++ b/Gui.py @@ -236,7 +236,8 @@ def guiMain(args=None): shuffleVar.set('vanilla') shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', - 'dungeonsfull', 'dungeonssimple') + 'dungeonsfull', 'dungeonssimple', "same simple", "same restricted", "same full", + "same crossed", "same insanity", "same dungeonsfull", "same dungeonssimple") shuffleOptionMenu.pack(side=RIGHT) shuffleLabel = Label(shuffleFrame, text='Entrance shuffle') shuffleLabel.pack(side=LEFT) @@ -417,6 +418,9 @@ def guiMain(args=None): guiargs.accessibility = accessibilityVar.get() guiargs.algorithm = algorithmVar.get() guiargs.shuffle = shuffleVar.get() + if "same " in guiargs.shuffle: + guiargs.shuffle = guiargs.shuffle[5:] + "-" + str(seedVar.get() if seedVar.get() else + random.randint(0, 2**64)) guiargs.heartbeep = rom_vars.heartbeepVar.get() guiargs.heartcolor = rom_vars.heartcolorVar.get() guiargs.fastmenu = rom_vars.fastMenuVar.get() diff --git a/Main.py b/Main.py index 2d22db67..1f1f0c2a 100644 --- a/Main.py +++ b/Main.py @@ -170,12 +170,22 @@ def main(args, seed=None): {"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}: world.fix_fake_world[player] = False + old_random = world.random + + # seeded entrance shuffle + if "-" in world.shuffle[player]: + shuffle, seed = world.shuffle[player].split("-") + world.random = random.Random(int(seed)) + world.shuffle[player] = shuffle + if world.mode[player] != 'inverted': link_entrances(world, player) mark_light_world_regions(world, player) else: link_inverted_entrances(world, player) mark_dark_world_regions(world, player) + + world.random = old_random plando_connect(world, player) logger.info('Generating Item Pool.') diff --git a/playerSettings.yaml b/playerSettings.yaml index f212f49c..0a524049 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -87,6 +87,9 @@ entrance_shuffle: # Documentation: https://alttpr.com/en/options#entrance_shuffl full: 0 # Less strict than restricted crossed: 0 # Less strict than full insanity: 0 # Very few grouping rules. Good luck + # you can also define entrance shuffle seed, like so: + crossed-1000: 0 # using this method, you can have the same layout as another player and share entrance information + # however, many other settings like logic, world state, retro etc. may affect the shuffle result as well. goals: ganon: 50 # Climb GT, defeat Agahnim 2, and then kill Ganon fast_ganon: 0 # Only killing Ganon is required. However, items may still be placed in GT From 8c020db07d939f207c2156661b68bc1b044b5af7 Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Fri, 19 Feb 2021 23:21:18 -0800 Subject: [PATCH 11/14] Add an identifier for triggers, to know which branch the seed is rolled on. --- Mystery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Mystery.py b/Mystery.py index 92ab4182..873893f6 100644 --- a/Mystery.py +++ b/Mystery.py @@ -325,6 +325,7 @@ def roll_linked_options(weights: dict) -> dict: def roll_triggers(weights: dict) -> dict: weights = weights.copy() # make sure we don't write back to other weights sets in same_settings + weights["_Generator_Version"] = "Main" # Some means for triggers to know if the seed is on main or doors. for option_set in weights["triggers"]: try: key = get_choice("option_name", option_set) From 8dc2a5748cd5dfb9fea46f218476aeab5f2dbd02 Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Sat, 20 Feb 2021 12:01:38 -0800 Subject: [PATCH 12/14] Allow for possible reuse of er layout This is done by assigning a unique seed to each player who doesn't have one set. er layout seed is output in spoiler log. --- BaseClasses.py | 6 +++++- EntranceRandomizer.py | 3 ++- Main.py | 17 +++++++++++------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4d054e17..e226c453 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -25,6 +25,7 @@ class World(object): plando_texts: List[Dict[str, str]] plando_items: List[PlandoItem] plando_connections: List[PlandoConnection] + er_seeds: Dict[int, str] def __init__(self, players: int, shuffle, logic, mode, swords, difficulty, item_functionality, timer, progressive, @@ -1338,7 +1339,8 @@ class Spoiler(object): 'shop_shuffle_slots': self.world.shop_shuffle_slots, 'shuffle_prizes': self.world.shuffle_prizes, 'sprite_pool': self.world.sprite_pool, - 'restrict_dungeon_item_on_boss': self.world.restrict_dungeon_item_on_boss + 'restrict_dungeon_item_on_boss': self.world.restrict_dungeon_item_on_boss, + 'er_seeds': self.world.er_seeds } def to_json(self): @@ -1402,6 +1404,8 @@ class Spoiler(object): outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player]) outfile.write('Item Progression: %s\n' % self.metadata['progressive'][player]) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player]) + if self.metadata['shuffle'][player] != "vanilla": + outfile.write('Entrance Shuffle Seed %s\n' % self.metadata['er_seeds'][player]) outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player]) outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player]) outfile.write('Pyramid hole pre-opened: %s\n' % ( diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 022bd96d..c894d0a0 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -378,6 +378,7 @@ def parse_arguments(argv, no_defaults=False): ret.plando_items = [] ret.plando_texts = {} ret.plando_connections = [] + ret.er_seeds = {} ret.glitch_boots = not ret.disable_glitch_boots if ret.timer == "none": @@ -408,7 +409,7 @@ def parse_arguments(argv, no_defaults=False): 'heartbeep', "skip_progression_balancing", "triforce_pieces_available", "triforce_pieces_required", "shop_shuffle", "shop_shuffle_slots", "required_medallions", - "plando_items", "plando_texts", "plando_connections", + "plando_items", "plando_texts", "plando_connections", "er_seeds", 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'restrict_dungeon_item_on_boss', 'reduceflashing', diff --git a/Main.py b/Main.py index 1f1f0c2a..284367f9 100644 --- a/Main.py +++ b/Main.py @@ -92,11 +92,20 @@ def main(args, seed=None): world.plando_items = args.plando_items.copy() world.plando_texts = args.plando_texts.copy() world.plando_connections = args.plando_connections.copy() + world.er_seeds = args.er_seeds.copy() world.restrict_dungeon_item_on_boss = args.restrict_dungeon_item_on_boss.copy() world.required_medallions = args.required_medallions.copy() world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)} + for player in range(1, world.players+1): + world.er_seeds[player] = str(world.random.randint(0, 2 ** 64)) + + if "-" in world.shuffle[player]: + shuffle, seed = world.shuffle[player].split("-") + world.shuffle[player] = shuffle + world.er_seeds[player] = seed + logger.info('ALttP Berserker\'s Multiworld Version %s - Seed: %s\n', __version__, world.seed) parsed_names = parse_player_names(args.names, world.players, args.teams) @@ -170,13 +179,9 @@ def main(args, seed=None): {"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}: world.fix_fake_world[player] = False - old_random = world.random - # seeded entrance shuffle - if "-" in world.shuffle[player]: - shuffle, seed = world.shuffle[player].split("-") - world.random = random.Random(int(seed)) - world.shuffle[player] = shuffle + old_random = world.random + world.random = random.Random(world.er_seeds[player]) if world.mode[player] != 'inverted': link_entrances(world, player) From c0cd79f0d77bd2d109659f3a248eae4b9c1a1889 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 21 Feb 2021 11:07:02 +0100 Subject: [PATCH 13/14] Garbage Collect Autohost subprocesses to accumulate less open file descriptors which may crash the webhost service. --- WebHostLib/autolauncher.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/WebHostLib/autolauncher.py b/WebHostLib/autolauncher.py index 392e8feb..025ba325 100644 --- a/WebHostLib/autolauncher.py +++ b/WebHostLib/autolauncher.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging import multiprocessing from datetime import timedelta, datetime +import concurrent.futures import sys import typing import time @@ -136,6 +137,7 @@ def autohost(config: dict): multiworlds = {} +guardians = concurrent.futures.ThreadPoolExecutor(2, thread_name_prefix="Guardian") class MultiworldInstance(): def __init__(self, room: Room, config: dict): @@ -153,12 +155,18 @@ class MultiworldInstance(): args=(self.room_id, self.ponyconfig), name="MultiHost") self.process.start() + self.guardian = guardians.submit(self._collect) def stop(self): if self.process: self.process.terminate() self.process = None + def _collect(self): + self.process.join() # wait for process to finish + self.process = None + self.guardian = None + from .models import Room, Generation, STATE_QUEUED, STATE_STARTED, STATE_ERROR, db, Seed from .customserver import run_server_process From a8bace554ef7ff553d27b57ddd76317401525c64 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 21 Feb 2021 13:57:00 +0100 Subject: [PATCH 14/14] update prompt_toolkit --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1a479447..4b8f6ca5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ websockets>=8.1 PyYAML>=5.4.1 fuzzywuzzy>=0.18.0 bsdiff4>=1.2.0 -prompt_toolkit>=3.0.14 +prompt_toolkit>=3.0.16 appdirs>=1.4.4 maseya-z3pr>=1.0.0rc1 xxtea>=2.0.0.post0
Finder