LttP: Allow DeathLink to be adjusted post-gen

This commit is contained in:
Fabian Dill 2021-11-08 16:34:54 +01:00
parent e8639988ce
commit b9941e40c1
4 changed files with 52 additions and 30 deletions

View File

@ -15,7 +15,7 @@ from argparse import Namespace
from concurrent.futures import as_completed, ThreadPoolExecutor from concurrent.futures import as_completed, ThreadPoolExecutor
from glob import glob from glob import glob
from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, LEFT, X, TOP, LabelFrame, \ from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, LEFT, X, TOP, LabelFrame, \
IntVar, Checkbutton, E, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage IntVar, Checkbutton, E, W, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage
from urllib.parse import urlparse from urllib.parse import urlparse
from urllib.request import urlopen from urllib.request import urlopen
@ -51,6 +51,7 @@ def main():
(default: %(default)s) (default: %(default)s)
''') ''')
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
parser.add_argument('--deathlink', help='Enable DeathLink system.', action='store_true')
parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true')
parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?', parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?',
choices=['normal', 'hide_goal', 'hide_required', 'hide_both'], choices=['normal', 'hide_goal', 'hide_required', 'hide_both'],
@ -152,7 +153,8 @@ def adjust(args):
world = getattr(args, "world") world = getattr(args, "world")
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.menuspeed, args.music, apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.menuspeed, args.music,
args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom, world=world) args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom, world=world,
deathlink=args.deathlink)
path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc') path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc')
rom.write_to_file(path) rom.write_to_file(path)
@ -205,6 +207,7 @@ def adjustGUI():
guiargs.quickswap = bool(rom_vars.quickSwapVar.get()) guiargs.quickswap = bool(rom_vars.quickSwapVar.get())
guiargs.music = bool(rom_vars.MusicVar.get()) guiargs.music = bool(rom_vars.MusicVar.get())
guiargs.reduceflashing = bool(rom_vars.disableFlashingVar.get()) guiargs.reduceflashing = bool(rom_vars.disableFlashingVar.get())
guiargs.deathlink = bool(rom_vars.DeathLinkVar.get())
guiargs.rom = romVar2.get() guiargs.rom = romVar2.get()
guiargs.baserom = romVar.get() guiargs.baserom = romVar.get()
guiargs.sprite = rom_vars.sprite guiargs.sprite = rom_vars.sprite
@ -272,17 +275,18 @@ def update_sprites(task, on_finish=None):
current_sprites = [os.path.basename(file) for file in glob(sprite_dir + '/*')] current_sprites = [os.path.basename(file) for file in glob(sprite_dir + '/*')]
alttpr_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path)) alttpr_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path))
for sprite in sprites_arr if sprite["author"] != "Nintendo"] for sprite in sprites_arr if sprite["author"] != "Nintendo"]
needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in alttpr_sprites if filename not in current_sprites] needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in alttpr_sprites if
filename not in current_sprites]
alttpr_filenames = [filename for (_, filename) in alttpr_sprites] alttpr_filenames = [filename for (_, filename) in alttpr_sprites]
obsolete_sprites = [sprite for sprite in current_sprites if sprite not in alttpr_filenames] obsolete_sprites = [sprite for sprite in current_sprites if sprite not in alttpr_filenames]
except Exception as e: except Exception as e:
resultmessage = "Error Determining which sprites to update. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e) resultmessage = "Error Determining which sprites to update. Sprites not updated.\n\n%s: %s" % (
type(e).__name__, e)
successful = False successful = False
task.queue_event(finished) task.queue_event(finished)
return return
def dl(sprite_url, filename): def dl(sprite_url, filename):
target = os.path.join(sprite_dir, filename) target = os.path.join(sprite_dir, filename)
with urlopen(sprite_url) as response, open(target, 'wb') as out: with urlopen(sprite_url) as response, open(target, 'wb') as out:
@ -291,7 +295,6 @@ def update_sprites(task, on_finish=None):
def rem(sprite): def rem(sprite):
os.remove(os.path.join(sprite_dir, sprite)) os.remove(os.path.join(sprite_dir, sprite))
with ThreadPoolExecutor() as pool: with ThreadPoolExecutor() as pool:
dl_tasks = [] dl_tasks = []
rem_tasks = [] rem_tasks = []
@ -420,6 +423,7 @@ def get_rom_frame(parent=None):
romVar.set(rom) romVar.set(rom)
romSelectButton['state'] = "disabled" romSelectButton['state'] = "disabled"
romSelectButton["text"] = "ROM verified" romSelectButton["text"] = "ROM verified"
romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect) romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect)
baseRomLabel.pack(side=LEFT) baseRomLabel.pack(side=LEFT)
@ -444,17 +448,21 @@ def get_rom_options_frame(parent=None):
MusicCheckbutton.grid(row=0, column=0, sticky=E) MusicCheckbutton.grid(row=0, column=0, sticky=E)
vars.disableFlashingVar = IntVar(value=1) vars.disableFlashingVar = IntVar(value=1)
disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", variable=vars.disableFlashingVar) disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)",
disableFlashingCheckbutton.grid(row=6, column=0, sticky=E) variable=vars.disableFlashingVar)
disableFlashingCheckbutton.grid(row=6, column=0, sticky=W)
vars.DeathLinkVar = IntVar(value=0)
DeathLinkCheckbutton = Checkbutton(romOptionsFrame, text="DeathLink (Team Deaths)", variable=vars.DeathLinkVar)
DeathLinkCheckbutton.grid(row=7, column=0, sticky=W)
spriteDialogFrame = Frame(romOptionsFrame) spriteDialogFrame = Frame(romOptionsFrame)
spriteDialogFrame.grid(row=0, column=1) spriteDialogFrame.grid(row=0, column=1)
baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:')
vars.spriteNameVar = StringVar() vars.spriteNameVar = StringVar()
vars.sprite = None vars.sprite = None
def set_sprite(sprite_param): def set_sprite(sprite_param):
nonlocal vars nonlocal vars
if isinstance(sprite_param, str): if isinstance(sprite_param, str):
@ -491,7 +499,8 @@ def get_rom_options_frame(parent=None):
menuspeedLabel.pack(side=LEFT) menuspeedLabel.pack(side=LEFT)
vars.menuspeedVar = StringVar() vars.menuspeedVar = StringVar()
vars.menuspeedVar.set('normal') vars.menuspeedVar.set('normal')
menuspeedOptionMenu = OptionMenu(menuspeedFrame, vars.menuspeedVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') menuspeedOptionMenu = OptionMenu(menuspeedFrame, vars.menuspeedVar, 'normal', 'instant', 'double', 'triple',
'quadruple', 'half')
menuspeedOptionMenu.pack(side=LEFT) menuspeedOptionMenu.pack(side=LEFT)
heartcolorFrame = Frame(romOptionsFrame) heartcolorFrame = Frame(romOptionsFrame)
@ -518,7 +527,8 @@ def get_rom_options_frame(parent=None):
owPalettesLabel.pack(side=LEFT) owPalettesLabel.pack(side=LEFT)
vars.owPalettesVar = StringVar() vars.owPalettesVar = StringVar()
vars.owPalettesVar.set('default') vars.owPalettesVar.set('default')
owPalettesOptionMenu = OptionMenu(owPalettesFrame, vars.owPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') owPalettesOptionMenu = OptionMenu(owPalettesFrame, vars.owPalettesVar, 'default', 'good', 'blackout', 'grayscale',
'negative', 'classic', 'dizzy', 'sick', 'puke')
owPalettesOptionMenu.pack(side=LEFT) owPalettesOptionMenu.pack(side=LEFT)
uwPalettesFrame = Frame(romOptionsFrame) uwPalettesFrame = Frame(romOptionsFrame)
@ -527,7 +537,8 @@ def get_rom_options_frame(parent=None):
uwPalettesLabel.pack(side=LEFT) uwPalettesLabel.pack(side=LEFT)
vars.uwPalettesVar = StringVar() vars.uwPalettesVar = StringVar()
vars.uwPalettesVar.set('default') vars.uwPalettesVar.set('default')
uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, vars.uwPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, vars.uwPalettesVar, 'default', 'good', 'blackout', 'grayscale',
'negative', 'classic', 'dizzy', 'sick', 'puke')
uwPalettesOptionMenu.pack(side=LEFT) uwPalettesOptionMenu.pack(side=LEFT)
hudPalettesFrame = Frame(romOptionsFrame) hudPalettesFrame = Frame(romOptionsFrame)
@ -536,7 +547,8 @@ def get_rom_options_frame(parent=None):
hudPalettesLabel.pack(side=LEFT) hudPalettesLabel.pack(side=LEFT)
vars.hudPalettesVar = StringVar() vars.hudPalettesVar = StringVar()
vars.hudPalettesVar.set('default') vars.hudPalettesVar.set('default')
hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, vars.hudPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, vars.hudPalettesVar, 'default', 'good', 'blackout',
'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke')
hudPalettesOptionMenu.pack(side=LEFT) hudPalettesOptionMenu.pack(side=LEFT)
swordPalettesFrame = Frame(romOptionsFrame) swordPalettesFrame = Frame(romOptionsFrame)
@ -545,7 +557,8 @@ def get_rom_options_frame(parent=None):
swordPalettesLabel.pack(side=LEFT) swordPalettesLabel.pack(side=LEFT)
vars.swordPalettesVar = StringVar() vars.swordPalettesVar = StringVar()
vars.swordPalettesVar.set('default') vars.swordPalettesVar.set('default')
swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, vars.swordPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, vars.swordPalettesVar, 'default', 'good', 'blackout',
'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke')
swordPalettesOptionMenu.pack(side=LEFT) swordPalettesOptionMenu.pack(side=LEFT)
shieldPalettesFrame = Frame(romOptionsFrame) shieldPalettesFrame = Frame(romOptionsFrame)
@ -554,7 +567,8 @@ def get_rom_options_frame(parent=None):
shieldPalettesLabel.pack(side=LEFT) shieldPalettesLabel.pack(side=LEFT)
vars.shieldPalettesVar = StringVar() vars.shieldPalettesVar = StringVar()
vars.shieldPalettesVar.set('default') vars.shieldPalettesVar.set('default')
shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'good', 'blackout',
'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke')
shieldPalettesOptionMenu.pack(side=LEFT) shieldPalettesOptionMenu.pack(side=LEFT)
spritePoolFrame = Frame(romOptionsFrame) spritePoolFrame = Frame(romOptionsFrame)
@ -563,6 +577,7 @@ def get_rom_options_frame(parent=None):
vars.spritePoolCountVar = StringVar() vars.spritePoolCountVar = StringVar()
vars.sprite_pool = [] vars.sprite_pool = []
def set_sprite_pool(sprite_param): def set_sprite_pool(sprite_param):
nonlocal vars nonlocal vars
operation = "add" operation = "add"
@ -632,8 +647,10 @@ class SpriteSelector():
title_link.pack(side=LEFT) title_link.pack(side=LEFT)
title_link.bind("<Button-1>", open_custom_sprite_dir) title_link.bind("<Button-1>", open_custom_sprite_dir)
self.icon_section(alttpr_frametitle, self.alttpr_sprite_dir, 'ALTTPR sprites not found. Click "Update alttpr sprites" to download them.') self.icon_section(alttpr_frametitle, self.alttpr_sprite_dir,
self.icon_section(custom_frametitle, self.custom_sprite_dir, 'Put sprites in the custom sprites folder (see open link above) to have them appear here.') 'ALTTPR sprites not found. Click "Update alttpr sprites" to download them.')
self.icon_section(custom_frametitle, self.custom_sprite_dir,
'Put sprites in the custom sprites folder (see open link above) to have them appear here.')
if not randomOnEvent: if not randomOnEvent:
self.sprite_pool_section(spritePool) self.sprite_pool_section(spritePool)
@ -683,7 +700,8 @@ class SpriteSelector():
button = Checkbutton(frame, text="Bonk", command=self.update_random_button, variable=self.randomOnBonkVar) button = Checkbutton(frame, text="Bonk", command=self.update_random_button, variable=self.randomOnBonkVar)
button.pack(side=LEFT, padx=(0, 5)) button.pack(side=LEFT, padx=(0, 5))
button = Checkbutton(frame, text="Random", command=self.update_random_button, variable=self.randomOnRandomVar) button = Checkbutton(frame, text="Random", command=self.update_random_button,
variable=self.randomOnRandomVar)
button.pack(side=LEFT, padx=(0, 5)) button.pack(side=LEFT, padx=(0, 5))
if adjuster: if adjuster:
@ -805,7 +823,6 @@ class SpriteSelector():
BackgroundTaskProgress(self.parent, update_sprites, "Updating Sprites", on_finish) BackgroundTaskProgress(self.parent, update_sprites, "Updating Sprites", on_finish)
def browse_for_sprite(self): def browse_for_sprite(self):
sprite = filedialog.askopenfilename( sprite = filedialog.askopenfilename(
filetypes=[("All Sprite Sources", (".zspr", ".spr", ".sfc", ".smc")), filetypes=[("All Sprite Sources", (".zspr", ".spr", ".sfc", ".smc")),
@ -819,7 +836,6 @@ class SpriteSelector():
self.callback(None) self.callback(None)
self.window.destroy() self.window.destroy()
def use_default_sprite(self): def use_default_sprite(self):
self.callback(None) self.callback(None)
self.window.destroy() self.window.destroy()
@ -923,7 +939,8 @@ def get_image_for_sprite(sprite, gif_only: bool = False):
gif_lsd = bytearray(7) gif_lsd = bytearray(7)
gif_lsd[0] = width gif_lsd[0] = width
gif_lsd[2] = height gif_lsd[2] = height
gif_lsd[4] = 0xF4 # 32 color palette follows. transparant + 15 for sprite + 1 for shadow=17 which rounds up to 32 as nearest power of 2 gif_lsd[
4] = 0xF4 # 32 color palette follows. transparant + 15 for sprite + 1 for shadow=17 which rounds up to 32 as nearest power of 2
gif_lsd[5] = 0 # background color is zero gif_lsd[5] = 0 # background color is zero
gif_lsd[6] = 0 # aspect raio not specified gif_lsd[6] = 0 # aspect raio not specified
gif_gct = bytearray(3 * 32) gif_gct = bytearray(3 * 32)
@ -943,7 +960,8 @@ def get_image_for_sprite(sprite, gif_only: bool = False):
gif_id[7] = height gif_id[7] = height
gif_id[9] = 0 # no local color table gif_id[9] = 0 # no local color table
gif_img_minimum_code_size = bytes([7]) # we choose 7 bits, so that each pixel is represented by a byte, for conviennce. gif_img_minimum_code_size = bytes(
[7]) # we choose 7 bits, so that each pixel is represented by a byte, for conviennce.
clear = 0x80 clear = 0x80
stop = 0x81 stop = 0x81
@ -1100,5 +1118,6 @@ class ToolTips(object):
widget.after_cancel(cls.after_id) widget.after_cancel(cls.after_id)
cls.after_id = None cls.after_id = None
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1283,6 +1283,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
else: else:
await ctx.send_msgs(client, [{"cmd": "DataPackage", await ctx.send_msgs(client, [{"cmd": "DataPackage",
"data": network_data_package}]) "data": network_data_package}])
elif client.auth: elif client.auth:
if cmd == "ConnectUpdate": if cmd == "ConnectUpdate":
if not args: if not args:

View File

@ -1645,7 +1645,6 @@ def patch_rom(world, rom, player, enemized):
# remote items flag, does not currently work # remote items flag, does not currently work
rom.write_byte(0x18637C, int(world.worlds[player].remote_items)) rom.write_byte(0x18637C, int(world.worlds[player].remote_items))
rom.write_byte(0x18008D, int(world.death_link[player]))
# set rom name # set rom name
# 21 bytes # 21 bytes
from Main import __version__ from Main import __version__
@ -1768,7 +1767,7 @@ def hud_format_text(text):
def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options, def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options,
world=None, player=1, allow_random_on_event=False, reduceflashing=False, world=None, player=1, allow_random_on_event=False, reduceflashing=False,
triforcehud: str = None): triforcehud: str = None, deathlink: bool = False):
local_random = random if not world else world.slot_seeds[player] local_random = random if not world else world.slot_seeds[player]
disable_music: bool = not music disable_music: bool = not music
# enable instant item menu # enable instant item menu
@ -1902,6 +1901,8 @@ def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, spri
elif palettes_options['dungeon'] == 'random': elif palettes_options['dungeon'] == 'random':
randomize_uw_palettes(rom, local_random) randomize_uw_palettes(rom, local_random)
rom.write_byte(0x18008D, int(deathlink))
apply_random_sprite_on_event(rom, sprite, local_random, allow_random_on_event, apply_random_sprite_on_event(rom, sprite, local_random, allow_random_on_event,
world.sprite_pool[player] if world else []) world.sprite_pool[player] if world else [])
if isinstance(rom, LocalRom): if isinstance(rom, LocalRom):

View File

@ -294,7 +294,8 @@ class ALTTPWorld(World):
world.sprite[player], world.sprite[player],
palettes_options, world, player, True, palettes_options, world, player, True,
reduceflashing=world.reduceflashing[player] or world.is_race, reduceflashing=world.reduceflashing[player] or world.is_race,
triforcehud=world.triforcehud[player].current_key) triforcehud=world.triforcehud[player].current_key,
deathlink=world.death_link[player])
outfilepname = f'_P{player}' outfilepname = f'_P{player}'
outfilepname += f"_{world.player_name[player].replace(' ', '_')}" \ outfilepname += f"_{world.player_name[player].replace(' ', '_')}" \