Merge branch 'main' into Archipelago_Main
# Conflicts: # BaseClasses.py # LttPAdjuster.py # Main.py # Mystery.py # Utils.py # WebHostLib/generate.py # playerSettings.yaml
This commit is contained in:
commit
d24ee45462
|
@ -30,6 +30,7 @@ class MultiWorld():
|
||||||
self.random = random.Random() # world-local random state is saved for multiple generations running concurrently
|
self.random = random.Random() # world-local random state is saved for multiple generations running concurrently
|
||||||
self.players = players
|
self.players = players
|
||||||
self.teams = 1
|
self.teams = 1
|
||||||
|
self.glitch_triforce = False
|
||||||
self.algorithm = 'balanced'
|
self.algorithm = 'balanced'
|
||||||
self.dungeons = []
|
self.dungeons = []
|
||||||
self.regions = []
|
self.regions = []
|
||||||
|
|
155
Gui.py
155
Gui.py
|
@ -7,6 +7,7 @@ import random
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
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 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
|
||||||
|
import tkinter.font as font
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
@ -501,6 +502,9 @@ def guiMain(args=None):
|
||||||
guiargs.rom = romVar.get()
|
guiargs.rom = romVar.get()
|
||||||
guiargs.create_diff = patchesVar.get()
|
guiargs.create_diff = patchesVar.get()
|
||||||
guiargs.sprite = rom_vars.sprite
|
guiargs.sprite = rom_vars.sprite
|
||||||
|
if rom_vars.sprite_pool:
|
||||||
|
guiargs.sprite_pool = rom_vars.sprite_pool
|
||||||
|
messagebox.showinfo(title="Sprite Pool", message=", ".join(guiargs.sprite_pool))
|
||||||
# get default values for missing parameters
|
# get default values for missing parameters
|
||||||
for k,v in vars(parse_arguments(['--multi', str(guiargs.multi)])).items():
|
for k,v in vars(parse_arguments(['--multi', str(guiargs.multi)])).items():
|
||||||
if k not in vars(guiargs):
|
if k not in vars(guiargs):
|
||||||
|
@ -1308,7 +1312,8 @@ def get_rom_options_frame(parent=None):
|
||||||
spriteEntry = Label(spriteDialogFrame, textvariable=vars.spriteNameVar)
|
spriteEntry = Label(spriteDialogFrame, textvariable=vars.spriteNameVar)
|
||||||
|
|
||||||
def SpriteSelect():
|
def SpriteSelect():
|
||||||
SpriteSelector(parent, set_sprite)
|
nonlocal vars
|
||||||
|
SpriteSelector(parent, set_sprite, spritePool=vars.sprite_pool)
|
||||||
|
|
||||||
spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect)
|
spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect)
|
||||||
|
|
||||||
|
@ -1392,21 +1397,68 @@ def get_rom_options_frame(parent=None):
|
||||||
shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke')
|
shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'random', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke')
|
||||||
shieldPalettesOptionMenu.pack(side=LEFT)
|
shieldPalettesOptionMenu.pack(side=LEFT)
|
||||||
|
|
||||||
|
spritePoolFrame = Frame(romOptionsFrame)
|
||||||
|
spritePoolFrame.grid(row=5, column=1)
|
||||||
|
baseSpritePoolLabel = Label(spritePoolFrame, text='Sprite Pool:')
|
||||||
|
|
||||||
|
vars.spritePoolCountVar = StringVar()
|
||||||
|
vars.sprite_pool = []
|
||||||
|
def set_sprite_pool(sprite_param):
|
||||||
|
nonlocal vars
|
||||||
|
operation = "add"
|
||||||
|
if isinstance(sprite_param, tuple):
|
||||||
|
operation, sprite_param = sprite_param
|
||||||
|
if isinstance(sprite_param, Sprite) and sprite_param.valid:
|
||||||
|
sprite_param = sprite_param.name
|
||||||
|
if isinstance(sprite_param, str):
|
||||||
|
if operation == "add":
|
||||||
|
vars.sprite_pool.append(sprite_param)
|
||||||
|
elif operation == "remove":
|
||||||
|
vars.sprite_pool.remove(sprite_param)
|
||||||
|
elif operation == "clear":
|
||||||
|
vars.sprite_pool.clear()
|
||||||
|
vars.spritePoolCountVar.set(str(len(vars.sprite_pool)))
|
||||||
|
|
||||||
|
set_sprite_pool(None)
|
||||||
|
vars.spritePoolCountVar.set('0')
|
||||||
|
spritePoolEntry = Label(spritePoolFrame, textvariable=vars.spritePoolCountVar)
|
||||||
|
|
||||||
|
def SpritePoolSelect():
|
||||||
|
nonlocal vars
|
||||||
|
SpriteSelector(parent, set_sprite_pool, randomOnEvent=False, spritePool=vars.sprite_pool)
|
||||||
|
|
||||||
|
def SpritePoolClear():
|
||||||
|
nonlocal vars
|
||||||
|
vars.sprite_pool.clear()
|
||||||
|
vars.spritePoolCountVar.set('0')
|
||||||
|
|
||||||
|
spritePoolSelectButton = Button(spritePoolFrame, text='...', command=SpritePoolSelect)
|
||||||
|
spritePoolClearButton = Button(spritePoolFrame, text='Clear', command=SpritePoolClear)
|
||||||
|
|
||||||
|
baseSpritePoolLabel.pack(side=LEFT)
|
||||||
|
spritePoolEntry.pack(side=LEFT)
|
||||||
|
spritePoolSelectButton.pack(side=LEFT)
|
||||||
|
spritePoolClearButton.pack(side=LEFT)
|
||||||
|
|
||||||
return romOptionsFrame, vars, set_sprite
|
return romOptionsFrame, vars, set_sprite
|
||||||
|
|
||||||
|
|
||||||
class SpriteSelector():
|
class SpriteSelector():
|
||||||
def __init__(self, parent, callback, adjuster=False):
|
def __init__(self, parent, callback, adjuster=False, randomOnEvent=True, spritePool=None):
|
||||||
self.deploy_icons()
|
self.deploy_icons()
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.window = Toplevel(parent)
|
self.window = Toplevel(parent)
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.adjuster = adjuster
|
self.adjuster = adjuster
|
||||||
|
self.randomOnEvent = randomOnEvent
|
||||||
|
self.spritePoolButtons = None
|
||||||
|
|
||||||
self.window.wm_title("TAKE ANY ONE YOU WANT")
|
self.window.wm_title("TAKE ANY ONE YOU WANT")
|
||||||
self.window['padx'] = 5
|
self.window['padx'] = 5
|
||||||
self.window['pady'] = 5
|
self.window['pady'] = 5
|
||||||
|
self.spritesPerRow = 32
|
||||||
self.all_sprites = []
|
self.all_sprites = []
|
||||||
|
self.sprite_pool = spritePool
|
||||||
|
|
||||||
def open_custom_sprite_dir(_evt):
|
def open_custom_sprite_dir(_evt):
|
||||||
open_file(self.custom_sprite_dir)
|
open_file(self.custom_sprite_dir)
|
||||||
|
@ -1422,10 +1474,13 @@ class SpriteSelector():
|
||||||
|
|
||||||
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, '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.')
|
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:
|
||||||
|
self.sprite_pool_section(spritePool)
|
||||||
|
|
||||||
frame = Frame(self.window)
|
frame = Frame(self.window)
|
||||||
frame.pack(side=BOTTOM, fill=X, pady=5)
|
frame.pack(side=BOTTOM, fill=X, pady=5)
|
||||||
|
|
||||||
|
if self.randomOnEvent:
|
||||||
button = Button(frame, text="Browse for file...", command=self.browse_for_sprite)
|
button = Button(frame, text="Browse for file...", command=self.browse_for_sprite)
|
||||||
button.pack(side=RIGHT, padx=(5, 0))
|
button.pack(side=RIGHT, padx=(5, 0))
|
||||||
|
|
||||||
|
@ -1447,7 +1502,9 @@ class SpriteSelector():
|
||||||
self.randomOnSlashVar = IntVar()
|
self.randomOnSlashVar = IntVar()
|
||||||
self.randomOnItemVar = IntVar()
|
self.randomOnItemVar = IntVar()
|
||||||
self.randomOnBonkVar = IntVar()
|
self.randomOnBonkVar = IntVar()
|
||||||
|
self.randomOnRandomVar = IntVar()
|
||||||
|
|
||||||
|
if self.randomOnEvent:
|
||||||
button = Checkbutton(frame, text="Hit", command=self.update_random_button, variable=self.randomOnHitVar)
|
button = Checkbutton(frame, text="Hit", command=self.update_random_button, variable=self.randomOnHitVar)
|
||||||
button.pack(side=LEFT, padx=(0, 5))
|
button.pack(side=LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
@ -1466,6 +1523,9 @@ 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.pack(side=LEFT, padx=(0, 5))
|
||||||
|
|
||||||
if adjuster:
|
if adjuster:
|
||||||
button = Button(frame, text="Current sprite from rom", command=self.use_default_sprite)
|
button = Button(frame, text="Current sprite from rom", command=self.use_default_sprite)
|
||||||
button.pack(side=LEFT, padx=(0, 5))
|
button.pack(side=LEFT, padx=(0, 5))
|
||||||
|
@ -1473,6 +1533,62 @@ class SpriteSelector():
|
||||||
set_icon(self.window)
|
set_icon(self.window)
|
||||||
self.window.focus()
|
self.window.focus()
|
||||||
|
|
||||||
|
def remove_from_sprite_pool(self, button, spritename):
|
||||||
|
self.callback(("remove", spritename))
|
||||||
|
self.spritePoolButtons.buttons.remove(button)
|
||||||
|
button.destroy()
|
||||||
|
|
||||||
|
def add_to_sprite_pool(self, spritename):
|
||||||
|
if isinstance(spritename, str):
|
||||||
|
if spritename == "random":
|
||||||
|
button = Button(self.spritePoolButtons, text="?")
|
||||||
|
button['font'] = font.Font(size=19)
|
||||||
|
button.configure(command=lambda spr="random": self.remove_from_sprite_pool(button, spr))
|
||||||
|
ToolTips.register(button, "Random")
|
||||||
|
self.spritePoolButtons.buttons.append(button)
|
||||||
|
else:
|
||||||
|
spritename = Sprite.get_sprite_from_name(spritename)
|
||||||
|
if isinstance(spritename, Sprite) and spritename.valid:
|
||||||
|
image = get_image_for_sprite(spritename)
|
||||||
|
if image is None:
|
||||||
|
return
|
||||||
|
button = Button(self.spritePoolButtons, image=image)
|
||||||
|
button.configure(command=lambda spr=spritename: self.remove_from_sprite_pool(button, spr.name))
|
||||||
|
ToolTips.register(button, spritename.name +
|
||||||
|
f"\nBy: {spritename.author_name if spritename.author_name else ''}")
|
||||||
|
button.image = image
|
||||||
|
|
||||||
|
self.spritePoolButtons.buttons.append(button)
|
||||||
|
self.grid_fill_sprites(self.spritePoolButtons)
|
||||||
|
|
||||||
|
def sprite_pool_section(self, spritePool):
|
||||||
|
def clear_sprite_pool(_evt):
|
||||||
|
self.callback(("clear", "Clear"))
|
||||||
|
for button in self.spritePoolButtons.buttons:
|
||||||
|
button.destroy()
|
||||||
|
self.spritePoolButtons.buttons.clear()
|
||||||
|
|
||||||
|
frametitle = Frame(self.window)
|
||||||
|
title_text = Label(frametitle, text="Sprite Pool")
|
||||||
|
title_link = Label(frametitle, text="(clear)", fg="blue", cursor="hand2")
|
||||||
|
title_text.pack(side=LEFT)
|
||||||
|
title_link.pack(side=LEFT)
|
||||||
|
title_link.bind("<Button-1>", clear_sprite_pool)
|
||||||
|
|
||||||
|
self.spritePoolButtons = LabelFrame(self.window, labelwidget=frametitle, padx=5, pady=5)
|
||||||
|
self.spritePoolButtons.pack(side=TOP, fill=X)
|
||||||
|
self.spritePoolButtons.buttons = []
|
||||||
|
|
||||||
|
def update_sprites(event):
|
||||||
|
self.spritesPerRow = (event.width - 10) // 38
|
||||||
|
self.grid_fill_sprites(self.spritePoolButtons)
|
||||||
|
|
||||||
|
self.grid_fill_sprites(self.spritePoolButtons)
|
||||||
|
self.spritePoolButtons.bind("<Configure>", update_sprites)
|
||||||
|
|
||||||
|
if spritePool:
|
||||||
|
for sprite in spritePool:
|
||||||
|
self.add_to_sprite_pool(sprite)
|
||||||
|
|
||||||
def icon_section(self, frame_label, path, no_results_label):
|
def icon_section(self, frame_label, path, no_results_label):
|
||||||
frame = LabelFrame(self.window, labelwidget=frame_label, padx=5, pady=5)
|
frame = LabelFrame(self.window, labelwidget=frame_label, padx=5, pady=5)
|
||||||
|
@ -1503,16 +1619,16 @@ class SpriteSelector():
|
||||||
label.pack()
|
label.pack()
|
||||||
|
|
||||||
def update_sprites(event):
|
def update_sprites(event):
|
||||||
sprites_per_row = (event.width - 10) // 38
|
self.spritesPerRow = (event.width - 10) // 38
|
||||||
self.grid_fill_sprites(frame, sprites_per_row)
|
self.grid_fill_sprites(frame)
|
||||||
|
|
||||||
self.grid_fill_sprites(frame, 32)
|
self.grid_fill_sprites(frame)
|
||||||
|
|
||||||
frame.bind("<Configure>", update_sprites)
|
frame.bind("<Configure>", update_sprites)
|
||||||
|
|
||||||
def grid_fill_sprites(self, frame, sprites_per_row):
|
def grid_fill_sprites(self, frame):
|
||||||
for i, button in enumerate(frame.buttons):
|
for i, button in enumerate(frame.buttons):
|
||||||
button.grid(row=i // sprites_per_row, column=i % sprites_per_row)
|
button.grid(row=i // self.spritesPerRow, column=i % self.spritesPerRow)
|
||||||
|
|
||||||
def update_alttpr_sprites(self):
|
def update_alttpr_sprites(self):
|
||||||
# need to wrap in try catch. We don't want errors getting the json or downloading the files to break us.
|
# need to wrap in try catch. We don't want errors getting the json or downloading the files to break us.
|
||||||
|
@ -1549,27 +1665,48 @@ class SpriteSelector():
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
|
||||||
def use_default_link_sprite(self):
|
def use_default_link_sprite(self):
|
||||||
|
if self.randomOnEvent:
|
||||||
self.callback(Sprite.default_link_sprite())
|
self.callback(Sprite.default_link_sprite())
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
else:
|
||||||
|
self.callback("link")
|
||||||
|
self.add_to_sprite_pool("link")
|
||||||
|
|
||||||
def update_random_button(self):
|
def update_random_button(self):
|
||||||
|
if self.randomOnRandomVar.get():
|
||||||
|
randomon = "random"
|
||||||
|
else:
|
||||||
randomon = "-hit" if self.randomOnHitVar.get() else ""
|
randomon = "-hit" if self.randomOnHitVar.get() else ""
|
||||||
randomon += "-enter" if self.randomOnEnterVar.get() else ""
|
randomon += "-enter" if self.randomOnEnterVar.get() else ""
|
||||||
randomon += "-exit" if self.randomOnExitVar.get() else ""
|
randomon += "-exit" if self.randomOnExitVar.get() else ""
|
||||||
randomon += "-slash" if self.randomOnSlashVar.get() else ""
|
randomon += "-slash" if self.randomOnSlashVar.get() else ""
|
||||||
randomon += "-item" if self.randomOnItemVar.get() else ""
|
randomon += "-item" if self.randomOnItemVar.get() else ""
|
||||||
randomon += "-bonk" if self.randomOnBonkVar.get() else ""
|
randomon += "-bonk" if self.randomOnBonkVar.get() else ""
|
||||||
|
|
||||||
self.randomOnEventText.set(f"randomon{randomon}" if randomon else None)
|
self.randomOnEventText.set(f"randomon{randomon}" if randomon else None)
|
||||||
self.randomButtonText.set("Random On Event" if randomon else "Random")
|
self.randomButtonText.set("Random On Event" if randomon else "Random")
|
||||||
|
|
||||||
def use_random_sprite(self):
|
def use_random_sprite(self):
|
||||||
randomon = self.randomOnEventText.get()
|
if not self.randomOnEvent:
|
||||||
self.callback(randomon if randomon else (random.choice(self.all_sprites) if self.all_sprites else None))
|
self.callback("random")
|
||||||
|
self.add_to_sprite_pool("random")
|
||||||
|
return
|
||||||
|
elif self.randomOnEventText.get():
|
||||||
|
self.callback(self.randomOnEventText.get())
|
||||||
|
elif self.sprite_pool:
|
||||||
|
self.callback(random.choice(self.sprite_pool))
|
||||||
|
elif self.all_sprites:
|
||||||
|
self.callback(random.choice(self.all_sprites))
|
||||||
|
else:
|
||||||
|
self.callback(None)
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
|
||||||
def select_sprite(self, spritename):
|
def select_sprite(self, spritename):
|
||||||
self.callback(spritename)
|
self.callback(spritename)
|
||||||
|
if self.randomOnEvent:
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
else:
|
||||||
|
self.add_to_sprite_pool(spritename)
|
||||||
|
|
||||||
def deploy_icons(self):
|
def deploy_icons(self):
|
||||||
if not os.path.exists(self.custom_sprite_dir):
|
if not os.path.exists(self.custom_sprite_dir):
|
||||||
|
|
|
@ -9,6 +9,13 @@ import time
|
||||||
from worlds.alttp.Rom import Sprite, LocalRom, apply_rom_settings
|
from worlds.alttp.Rom import Sprite, LocalRom, apply_rom_settings
|
||||||
from Utils import output_path
|
from Utils import output_path
|
||||||
|
|
||||||
|
|
||||||
|
class AdjusterWorld(object):
|
||||||
|
def __init__(self, sprite_pool):
|
||||||
|
import random
|
||||||
|
self.sprite_pool = {1: sprite_pool}
|
||||||
|
self.rom_seeds = {1: random}
|
||||||
|
|
||||||
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
||||||
|
|
||||||
def _get_help_string(self, action):
|
def _get_help_string(self, action):
|
||||||
|
@ -100,10 +107,14 @@ def adjust(args):
|
||||||
palettes_options['sword']=args.sword_palettes
|
palettes_options['sword']=args.sword_palettes
|
||||||
palettes_options['shield']=args.shield_palettes
|
palettes_options['shield']=args.shield_palettes
|
||||||
# palettes_options['link']=args.link_palettesvera
|
# palettes_options['link']=args.link_palettesvera
|
||||||
|
|
||||||
racerom = rom.read_byte(0x180213) > 0
|
racerom = rom.read_byte(0x180213) > 0
|
||||||
|
world = None
|
||||||
|
if hasattr(args, "world"):
|
||||||
|
world = getattr(args, "world")
|
||||||
|
|
||||||
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic,
|
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic,
|
||||||
args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom)
|
args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom, world=world)
|
||||||
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)
|
||||||
|
|
||||||
|
@ -159,8 +170,14 @@ def adjustGUI():
|
||||||
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
|
||||||
|
if rom_vars.sprite_pool:
|
||||||
|
guiargs.world = AdjusterWorld(rom_vars.sprite_pool)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
guiargs, path = adjust(args=guiargs)
|
guiargs, path = adjust(args=guiargs)
|
||||||
|
if rom_vars.sprite_pool:
|
||||||
|
guiargs.sprite_pool = rom_vars.sprite_pool
|
||||||
|
delattr(guiargs, "world")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
messagebox.showerror(title="Error while adjusting Rom", message=str(e))
|
messagebox.showerror(title="Error while adjusting Rom", message=str(e))
|
||||||
|
|
10
Main.py
10
Main.py
|
@ -135,6 +135,7 @@ def main(args, seed=None):
|
||||||
import Options
|
import Options
|
||||||
for hk_option in Options.hollow_knight_options:
|
for hk_option in Options.hollow_knight_options:
|
||||||
setattr(world, hk_option, getattr(args, hk_option, {}))
|
setattr(world, hk_option, getattr(args, hk_option, {}))
|
||||||
|
world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option.
|
||||||
|
|
||||||
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)}
|
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)}
|
||||||
|
|
||||||
|
@ -146,13 +147,10 @@ def main(args, seed=None):
|
||||||
world.shuffle[player] = shuffle
|
world.shuffle[player] = shuffle
|
||||||
if shuffle == "vanilla":
|
if shuffle == "vanilla":
|
||||||
world.er_seeds[player] = "vanilla"
|
world.er_seeds[player] = "vanilla"
|
||||||
elif seed.startswith("group-"): # renamed from team to group to not confuse with existing team name use
|
elif seed.startswith("group-") or args.race:
|
||||||
|
# renamed from team to group to not confuse with existing team name use
|
||||||
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
||||||
elif not args.race:
|
else: # not a race or group seed, use set seed as is.
|
||||||
world.er_seeds[player] = seed
|
|
||||||
elif seed: # race but with a set seed, ignore set seed and use group logic instead
|
|
||||||
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
|
||||||
else: # race but without a set seed
|
|
||||||
world.er_seeds[player] = seed
|
world.er_seeds[player] = seed
|
||||||
elif world.shuffle[player] == "vanilla":
|
elif world.shuffle[player] == "vanilla":
|
||||||
world.er_seeds[player] = "vanilla"
|
world.er_seeds[player] = "vanilla"
|
||||||
|
|
|
@ -34,6 +34,7 @@ if __name__ == "__main__":
|
||||||
enemizer_path = multi_mystery_options["enemizer_path"]
|
enemizer_path = multi_mystery_options["enemizer_path"]
|
||||||
player_files_path = multi_mystery_options["player_files_path"]
|
player_files_path = multi_mystery_options["player_files_path"]
|
||||||
target_player_count = multi_mystery_options["players"]
|
target_player_count = multi_mystery_options["players"]
|
||||||
|
glitch_triforce = multi_mystery_options["glitch_triforce_room"]
|
||||||
race = multi_mystery_options["race"]
|
race = multi_mystery_options["race"]
|
||||||
plando_options = multi_mystery_options["plando_options"]
|
plando_options = multi_mystery_options["plando_options"]
|
||||||
create_spoiler = multi_mystery_options["create_spoiler"]
|
create_spoiler = multi_mystery_options["create_spoiler"]
|
||||||
|
@ -99,6 +100,8 @@ if __name__ == "__main__":
|
||||||
command += " --skip_playthrough"
|
command += " --skip_playthrough"
|
||||||
if zip_diffs:
|
if zip_diffs:
|
||||||
command += " --create_diff"
|
command += " --create_diff"
|
||||||
|
if glitch_triforce:
|
||||||
|
command += " --glitch_triforce"
|
||||||
if race:
|
if race:
|
||||||
command += " --race"
|
command += " --race"
|
||||||
if os.path.exists(os.path.join(player_files_path, meta_file_path)):
|
if os.path.exists(os.path.join(player_files_path, meta_file_path)):
|
||||||
|
|
22
Mystery.py
22
Mystery.py
|
@ -6,6 +6,7 @@ import urllib.parse
|
||||||
import typing
|
import typing
|
||||||
import os
|
import os
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
import string
|
||||||
|
|
||||||
import ModuleUpdate
|
import ModuleUpdate
|
||||||
from worlds.generic import PlandoItem, PlandoConnection
|
from worlds.generic import PlandoItem, PlandoConnection
|
||||||
|
@ -43,6 +44,7 @@ def mystery_argparse():
|
||||||
parser.add_argument('--rom')
|
parser.add_argument('--rom')
|
||||||
parser.add_argument('--enemizercli')
|
parser.add_argument('--enemizercli')
|
||||||
parser.add_argument('--outputpath')
|
parser.add_argument('--outputpath')
|
||||||
|
parser.add_argument('--glitch_triforce', action='store_true')
|
||||||
parser.add_argument('--race', action='store_true')
|
parser.add_argument('--race', action='store_true')
|
||||||
parser.add_argument('--meta', default=None)
|
parser.add_argument('--meta', default=None)
|
||||||
parser.add_argument('--log_output_path', help='Path to store output log')
|
parser.add_argument('--log_output_path', help='Path to store output log')
|
||||||
|
@ -107,6 +109,7 @@ def main(args=None, callback=ERmain):
|
||||||
erargs.name = {x: "" for x in range(1, args.multi + 1)} # only so it can be overwrittin in mystery
|
erargs.name = {x: "" for x in range(1, args.multi + 1)} # only so it can be overwrittin in mystery
|
||||||
erargs.create_spoiler = args.create_spoiler
|
erargs.create_spoiler = args.create_spoiler
|
||||||
erargs.create_diff = args.create_diff
|
erargs.create_diff = args.create_diff
|
||||||
|
erargs.glitch_triforce = args.glitch_triforce
|
||||||
erargs.race = args.race
|
erargs.race = args.race
|
||||||
erargs.skip_playthrough = args.skip_playthrough
|
erargs.skip_playthrough = args.skip_playthrough
|
||||||
erargs.outputname = seedname
|
erargs.outputname = seedname
|
||||||
|
@ -215,9 +218,7 @@ def main(args=None, callback=ERmain):
|
||||||
erargs.name[player] = f"Player{player}"
|
erargs.name[player] = f"Player{player}"
|
||||||
elif not erargs.name[player]: # if name was not specified, generate it from filename
|
elif not erargs.name[player]: # if name was not specified, generate it from filename
|
||||||
erargs.name[player] = os.path.splitext(os.path.split(path)[-1])[0]
|
erargs.name[player] = os.path.splitext(os.path.split(path)[-1])[0]
|
||||||
name_counter[erargs.name[player]] += 1
|
erargs.name[player] = handle_name(erargs.name[player], player, name_counter)
|
||||||
erargs.name[player]= handle_name(erargs.name[player].format(number=name_counter[erargs.name[player]],
|
|
||||||
player=player))
|
|
||||||
|
|
||||||
erargs.names = ",".join(erargs.name[i] for i in range(1, args.multi + 1))
|
erargs.names = ",".join(erargs.name[i] for i in range(1, args.multi + 1))
|
||||||
del (erargs.name)
|
del (erargs.name)
|
||||||
|
@ -284,8 +285,19 @@ def get_choice(option, root, value=None) -> typing.Any:
|
||||||
raise RuntimeError(f"All options specified in \"{option}\" are weighted as zero.")
|
raise RuntimeError(f"All options specified in \"{option}\" are weighted as zero.")
|
||||||
|
|
||||||
|
|
||||||
def handle_name(name: str):
|
class SafeDict(dict):
|
||||||
return name.strip().replace(' ', '_')[:16]
|
def __missing__(self, key):
|
||||||
|
return '{' + key + '}'
|
||||||
|
|
||||||
|
|
||||||
|
def handle_name(name: str, player: int, name_counter: Counter):
|
||||||
|
name_counter[name] += 1
|
||||||
|
new_name = "%".join([x.replace("%number%", "{number}").replace("%player%", "{player}") for x in name.split("%%")])
|
||||||
|
new_name = string.Formatter().vformat(new_name, (), SafeDict(number=name_counter[name],
|
||||||
|
NUMBER=(name_counter[name] if name_counter[name] > 1 else ''),
|
||||||
|
player=player,
|
||||||
|
PLAYER=(player if player > 1 else '')))
|
||||||
|
return new_name.strip().replace(' ', '_')[:16]
|
||||||
|
|
||||||
|
|
||||||
def prefer_int(input_data: str) -> typing.Union[str, int]:
|
def prefer_int(input_data: str) -> typing.Union[str, int]:
|
||||||
|
|
19
Utils.py
19
Utils.py
|
@ -206,6 +206,7 @@ def get_default_options() -> dict:
|
||||||
"zip_spoiler": 0,
|
"zip_spoiler": 0,
|
||||||
"zip_multidata": 1,
|
"zip_multidata": 1,
|
||||||
"zip_format": 1,
|
"zip_format": 1,
|
||||||
|
"glitch_triforce_room": 1,
|
||||||
"race": 0,
|
"race": 0,
|
||||||
"cpu_threads": 0,
|
"cpu_threads": 0,
|
||||||
"max_attempts": 0,
|
"max_attempts": 0,
|
||||||
|
@ -315,9 +316,20 @@ def get_adjuster_settings(romfile: str) -> typing.Tuple[str, bool]:
|
||||||
import Patch
|
import Patch
|
||||||
adjuster_settings.rom = romfile
|
adjuster_settings.rom = romfile
|
||||||
adjuster_settings.baserom = Patch.get_base_rom_path()
|
adjuster_settings.baserom = Patch.get_base_rom_path()
|
||||||
|
adjuster_settings.world = None
|
||||||
whitelist = {"disablemusic", "fastmenu", "heartbeep", "heartcolor", "ow_palettes", "quickswap",
|
whitelist = {"disablemusic", "fastmenu", "heartbeep", "heartcolor", "ow_palettes", "quickswap",
|
||||||
"uw_palettes", "sprite"}
|
"uw_palettes", "sprite"}
|
||||||
printed_options = {name: value for name, value in vars(adjuster_settings).items() if name in whitelist}
|
printed_options = {name: value for name, value in vars(adjuster_settings).items() if name in whitelist}
|
||||||
|
if hasattr(adjuster_settings, "sprite_pool"):
|
||||||
|
sprite_pool = {}
|
||||||
|
for sprite in getattr(adjuster_settings, "sprite_pool"):
|
||||||
|
if sprite in sprite_pool:
|
||||||
|
sprite_pool[sprite] += 1
|
||||||
|
else:
|
||||||
|
sprite_pool[sprite] = 1
|
||||||
|
if sprite_pool:
|
||||||
|
printed_options["sprite_pool"] = sprite_pool
|
||||||
|
|
||||||
|
|
||||||
if hasattr(get_adjuster_settings, "adjust_wanted"):
|
if hasattr(get_adjuster_settings, "adjust_wanted"):
|
||||||
adjust_wanted = getattr(get_adjuster_settings, "adjust_wanted")
|
adjust_wanted = getattr(get_adjuster_settings, "adjust_wanted")
|
||||||
|
@ -328,9 +340,16 @@ def get_adjuster_settings(romfile: str) -> typing.Tuple[str, bool]:
|
||||||
f"{pprint.pformat(printed_options)}\n"
|
f"{pprint.pformat(printed_options)}\n"
|
||||||
f"Enter yes, no or never: ")
|
f"Enter yes, no or never: ")
|
||||||
if adjust_wanted and adjust_wanted.startswith("y"):
|
if adjust_wanted and adjust_wanted.startswith("y"):
|
||||||
|
if hasattr(adjuster_settings, "sprite_pool"):
|
||||||
|
from Adjuster import AdjusterWorld
|
||||||
|
adjuster_settings.world = AdjusterWorld(getattr(adjuster_settings, "sprite_pool"))
|
||||||
|
|
||||||
adjusted = True
|
adjusted = True
|
||||||
import Adjuster
|
import Adjuster
|
||||||
_, romfile = Adjuster.adjust(adjuster_settings)
|
_, romfile = Adjuster.adjust(adjuster_settings)
|
||||||
|
|
||||||
|
if hasattr(adjuster_settings, "world"):
|
||||||
|
delattr(adjuster_settings, "world")
|
||||||
elif adjust_wanted and "never" in adjust_wanted:
|
elif adjust_wanted and "never" in adjust_wanted:
|
||||||
persistent_store("adjuster", "never_adjust", True)
|
persistent_store("adjuster", "never_adjust", True)
|
||||||
return romfile, False
|
return romfile, False
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import random
|
import random
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
from flask import request, flash, redirect, url_for, session, render_template
|
from flask import request, flash, redirect, url_for, session, render_template
|
||||||
|
|
||||||
from worlds.alttp.EntranceRandomizer import parse_arguments
|
from worlds.alttp.EntranceRandomizer import parse_arguments
|
||||||
from Main import main as ERmain
|
from Main import main as ERmain
|
||||||
from Main import get_seed, seeddigits
|
from Main import get_seed, seeddigits
|
||||||
|
from Mystery import handle_name
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
|
@ -80,13 +82,15 @@ def gen_game(gen_options, race=False, owner=None, sid=None):
|
||||||
erargs.progression_balancing = {}
|
erargs.progression_balancing = {}
|
||||||
erargs.create_diff = True
|
erargs.create_diff = True
|
||||||
|
|
||||||
|
name_counter = Counter()
|
||||||
for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
|
for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
|
||||||
for k, v in settings.items():
|
for k, v in settings.items():
|
||||||
if v is not None:
|
if v is not None:
|
||||||
getattr(erargs, k)[player] = v
|
getattr(erargs, k)[player] = v
|
||||||
|
|
||||||
if not erargs.name[player]:
|
if not erargs.name[player]:
|
||||||
erargs.name[player] = os.path.split(playerfile)[-1].split(".")[0]
|
erargs.name[player] = os.path.splitext(os.path.split(playerfile)[-1])[0]
|
||||||
|
erargs.name[player] = handle_name(erargs.name[player], player, name_counter)
|
||||||
|
|
||||||
erargs.names = ",".join(erargs.name[i] for i in range(1, playercount + 1))
|
erargs.names = ",".join(erargs.name[i] for i in range(1, playercount + 1))
|
||||||
del (erargs.name)
|
del (erargs.name)
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -4,38 +4,39 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.jsx",
|
"main": "index.jsx",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"dev": "webpack --mode development --config webpack.dev.js --watch"
|
"dev": "webpack --config webpack.dev.js"
|
||||||
},
|
},
|
||||||
"author": "LegendaryLinux",
|
"author": "LegendaryLinux",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.34",
|
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"css-loader": "^5.0.1",
|
"css-loader": "^5.1.3",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.21",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-devtools-extension": "^2.13.8",
|
"redux-devtools-extension": "^2.13.9",
|
||||||
"sass-loader": "^10.1.1",
|
"sass-loader": "^10.1.1",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"webpack-cli": "^4.5.0"
|
"webpack-cli": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.13",
|
"@babel/core": "^7.13.10",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||||
"@babel/preset-env": "^7.12.13",
|
"@babel/preset-env": "^7.13.10",
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"eslint": "^7.19.0",
|
"buffer": "^6.0.3",
|
||||||
|
"eslint": "^7.22.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
"webpack": "^5.20.0"
|
"stream-browserify": "^3.0.0",
|
||||||
|
"webpack": "^5.27.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,15 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
mode: 'production',
|
||||||
|
watch: false,
|
||||||
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
crypto: require.resolve('crypto-browserify'),
|
||||||
|
buffer: require.resolve('buffer/'),
|
||||||
|
stream: require.resolve('stream-browserify'),
|
||||||
|
},
|
||||||
|
},
|
||||||
entry: {
|
entry: {
|
||||||
index: './src/js/index.js',
|
index: './src/js/index.js',
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
mode: 'development',
|
||||||
|
watch: true,
|
||||||
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
crypto: require.resolve('crypto-browserify'),
|
||||||
|
},
|
||||||
|
},
|
||||||
entry: {
|
entry: {
|
||||||
index: './src/js/index.js',
|
index: './src/js/index.js',
|
||||||
},
|
},
|
||||||
|
|
|
@ -91,6 +91,12 @@ multi_mystery_options:
|
||||||
# 2 -> 7z is recommended for roms. All of them get the job done.
|
# 2 -> 7z is recommended for roms. All of them get the job done.
|
||||||
# 3 -> bz2
|
# 3 -> bz2
|
||||||
zip_format: 1
|
zip_format: 1
|
||||||
|
# Glitch to Triforce room from Ganon
|
||||||
|
# When disabled, you have to have a weapon that can hurt ganon (master sword or swordless/easy item functionality + hammer)
|
||||||
|
# and have completed the goal required for killing ganon to be able to access the triforce room.
|
||||||
|
# 1 -> Enabled.
|
||||||
|
# 0 -> Disabled (except in no-logic)
|
||||||
|
glitch_triforce_room: 1
|
||||||
# Create encrypted race roms
|
# Create encrypted race roms
|
||||||
race: 0
|
race: 0
|
||||||
# List of options that can be plando'd. Can be combined, for example "bosses, items"
|
# List of options that can be plando'd. Can be combined, for example "bosses, items"
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
description: Template Name # Used to describe your yaml. Useful if you have multiple files
|
description: Template Name # Used to describe your yaml. Useful if you have multiple files
|
||||||
name: YourName{number} # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
|
name: YourName{number} # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
|
||||||
#{player} will be replaced with the player's slot number.
|
#{player} will be replaced with the player's slot number.
|
||||||
|
#{PLAYER} will be replaced with the player's slot number if that slot number is greater than 1.
|
||||||
#{number} will be replaced with the counter value of the name.
|
#{number} will be replaced with the counter value of the name.
|
||||||
|
#{NUMBER} will be replaced with the counter value of the name if the counter value is greater than 1.
|
||||||
game:
|
game:
|
||||||
A Link to the Past: 1
|
A Link to the Past: 1
|
||||||
Hollow Knight: 1
|
Hollow Knight: 1
|
||||||
|
|
|
@ -228,10 +228,10 @@ def place_bosses(world, player: int):
|
||||||
level = loc[-1]
|
level = loc[-1]
|
||||||
loc = " ".join(loc[:-1])
|
loc = " ".join(loc[:-1])
|
||||||
loc = loc.title().replace("Of", "of")
|
loc = loc.title().replace("Of", "of")
|
||||||
if can_place_boss(boss, loc, level) and [loc, level] in boss_locations:
|
if can_place_boss(boss, loc, level) and (loc, level) in boss_locations:
|
||||||
place_boss(world, player, boss, loc, level)
|
place_boss(world, player, boss, loc, level)
|
||||||
already_placed_bosses.append(boss)
|
already_placed_bosses.append(boss)
|
||||||
boss_locations.remove([loc, level])
|
boss_locations.remove((loc, level))
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Cannot place {boss} at {format_boss_location(loc, level)} for player {player}.")
|
raise Exception(f"Cannot place {boss} at {format_boss_location(loc, level)} for player {player}.")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -31,6 +31,7 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
No Logic: Distribute items without regard for
|
No Logic: Distribute items without regard for
|
||||||
item requirements.
|
item requirements.
|
||||||
''')
|
''')
|
||||||
|
parser.add_argument('--glitch_triforce', help='Allow glitching to Triforce from Ganon\'s room', action='store_true')
|
||||||
parser.add_argument('--mode', default=defval('open'), const='open', nargs='?', choices=['standard', 'open', 'inverted'],
|
parser.add_argument('--mode', default=defval('open'), const='open', nargs='?', choices=['standard', 'open', 'inverted'],
|
||||||
help='''\
|
help='''\
|
||||||
Select game mode. (default: %(default)s)
|
Select game mode. (default: %(default)s)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '4f63251fb1f769e1a6b017346b2e51dc'
|
RANDOMIZERBASEHASH = '13a75c5dd28055fbcf8f69bd8161871d'
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
@ -226,6 +226,9 @@ def apply_random_sprite_on_event(rom: LocalRom, sprite, local_random, allow_rand
|
||||||
elif sprite == 'randomonnone':
|
elif sprite == 'randomonnone':
|
||||||
# Allows for opting into random on events on race rom seeds, without actually enabling any of the events initially.
|
# Allows for opting into random on events on race rom seeds, without actually enabling any of the events initially.
|
||||||
onevent = 0x0000
|
onevent = 0x0000
|
||||||
|
elif sprite == 'randomonrandom':
|
||||||
|
# Allows random to take the wheel on which events apply. (at least one event will be applied.)
|
||||||
|
onevent = local_random.randint(0x0001, 0x003F)
|
||||||
elif userandomsprites:
|
elif userandomsprites:
|
||||||
onevent = 0x01 if 'hit' in sprite else 0x00
|
onevent = 0x01 if 'hit' in sprite else 0x00
|
||||||
onevent += 0x02 if 'enter' in sprite else 0x00
|
onevent += 0x02 if 'enter' in sprite else 0x00
|
||||||
|
@ -536,6 +539,8 @@ class Sprite(object):
|
||||||
self.valid = False
|
self.valid = False
|
||||||
return
|
return
|
||||||
(sprite, palette, self.name, self.author_name) = result
|
(sprite, palette, self.name, self.author_name) = result
|
||||||
|
if self.name == "":
|
||||||
|
self.name = os.path.split(filename)[1].split(".")[0]
|
||||||
if len(sprite) != 0x7000:
|
if len(sprite) != 0x7000:
|
||||||
self.valid = False
|
self.valid = False
|
||||||
return
|
return
|
||||||
|
@ -1499,6 +1504,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||||
rom.write_byte(0xEFD95, digging_game_rng)
|
rom.write_byte(0xEFD95, digging_game_rng)
|
||||||
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
||||||
rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix
|
rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix
|
||||||
|
rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.logic[player] == 'nologic' else 0x00) # disable glitching to Triforce from Ganons Room
|
||||||
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
|
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
|
||||||
|
|
||||||
# remove shield from uncle
|
# remove shield from uncle
|
||||||
|
|
|
@ -194,20 +194,21 @@ def ShopSlotFill(world):
|
||||||
|
|
||||||
del locations_per_sphere
|
del locations_per_sphere
|
||||||
|
|
||||||
total_spheres = len(candidates_per_sphere)
|
|
||||||
|
|
||||||
for i, current_shop_slots in enumerate(shops_per_sphere):
|
for i, current_shop_slots in enumerate(shops_per_sphere):
|
||||||
if current_shop_slots:
|
if current_shop_slots:
|
||||||
candidate_sphere_ids = list(range(i, total_spheres))
|
# getting all candidates and shuffling them feels cpu expensive, there may be a better method
|
||||||
|
candidates = [(location, i) for i, candidates in enumerate(candidates_per_sphere[i:], start=i)
|
||||||
|
for location in candidates]
|
||||||
|
world.random.shuffle(candidates)
|
||||||
for location in current_shop_slots:
|
for location in current_shop_slots:
|
||||||
shop: Shop = location.parent_region.shop
|
shop: Shop = location.parent_region.shop
|
||||||
swapping_sphere_id = world.random.choices(candidate_sphere_ids,
|
for index, (c, swapping_sphere_id) in enumerate(candidates): # chosen item locations
|
||||||
cum_weights=cumu_weights[i:])[0]
|
|
||||||
swapping_sphere: list = candidates_per_sphere[swapping_sphere_id]
|
|
||||||
for c in swapping_sphere: # chosen item locations
|
|
||||||
if c.item_rule(location.item) and location.item_rule(c.item):
|
if c.item_rule(location.item) and location.item_rule(c.item):
|
||||||
swap_location_item(c, location, check_locked=False)
|
swap_location_item(c, location, check_locked=False)
|
||||||
logger.debug(f'Swapping {c} into {location}:: {location.item}')
|
logger.debug(f'Swapping {c} into {location}:: {location.item}')
|
||||||
|
# remove candidate
|
||||||
|
candidates_per_sphere[swapping_sphere_id].remove(c)
|
||||||
|
candidates.pop(index)
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -216,10 +217,6 @@ def ShopSlotFill(world):
|
||||||
location.shop_slot_disabled = True
|
location.shop_slot_disabled = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# remove candidate
|
|
||||||
swapping_sphere.remove(c)
|
|
||||||
cumu_weights[swapping_sphere_id] -= 1
|
|
||||||
|
|
||||||
item_name = location.item.name
|
item_name = location.item.name
|
||||||
if any(x in item_name for x in ['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
|
if any(x in item_name for x in ['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
|
||||||
price = world.random.randrange(1, 7)
|
price = world.random.randrange(1, 7)
|
||||||
|
|
Loading…
Reference in New Issue