Merge branch 'OverworldShuffle' into GwaaKiwi
This commit is contained in:
@@ -208,6 +208,7 @@ class CustomSettings(object):
|
||||
|
||||
# rom adjust stuff
|
||||
args.sprite[p] = get_setting(settings['sprite'], args.sprite[p])
|
||||
args.triforce_gfx[p] = get_setting(settings['triforce_gfx'], args.triforce_gfx[p])
|
||||
args.disablemusic[p] = get_setting(settings['disablemusic'], args.disablemusic[p])
|
||||
args.quickswap[p] = get_setting(settings['quickswap'], args.quickswap[p])
|
||||
args.reduce_flashing[p] = get_setting(settings['reduce_flashing'], args.reduce_flashing[p])
|
||||
@@ -221,6 +222,14 @@ class CustomSettings(object):
|
||||
args.shuffle_songinstruments[p] = get_setting(settings['shuffle_songinstruments'], args.shuffle_songinstruments[p])
|
||||
args.msu_resume[p] = get_setting(settings['msu_resume'], args.msu_resume[p])
|
||||
|
||||
def has_setting(self, player, setting):
|
||||
if 'settings' in self.file_source and player in self.file_source['settings']:
|
||||
return setting in self.file_source['settings'][player]
|
||||
return False
|
||||
|
||||
def get_setting(self, player, setting):
|
||||
return self.file_source['settings'][player][setting]
|
||||
|
||||
def get_item_pool(self):
|
||||
if 'item_pool' in self.file_source:
|
||||
return self.file_source['item_pool']
|
||||
@@ -299,9 +308,9 @@ class CustomSettings(object):
|
||||
return self.file_source['enemies']
|
||||
return None
|
||||
|
||||
def get_gtentry(self):
|
||||
if 'gt_entry' in self.file_source:
|
||||
return self.file_source['gt_entry']
|
||||
def get_goals(self):
|
||||
if 'goals' in self.file_source:
|
||||
return self.file_source['goals']
|
||||
return None
|
||||
|
||||
|
||||
@@ -577,9 +586,13 @@ class CustomSettings(object):
|
||||
|
||||
|
||||
def load_yaml(path):
|
||||
if os.path.exists(Path(path)):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
elif urllib.parse.urlparse(path).scheme in ['http', 'https']:
|
||||
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
|
||||
try:
|
||||
if os.path.exists(Path(path)):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
elif urllib.parse.urlparse(path).scheme in ['http', 'https']:
|
||||
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
|
||||
except yaml.YAMLError as e:
|
||||
error_msg = f"Error parsing YAML file '{path}':\n{str(e)}"
|
||||
raise ValueError(error_msg) from e
|
||||
|
||||
|
||||
130
source/classes/ItemGfxSelector.py
Normal file
130
source/classes/ItemGfxSelector.py
Normal file
@@ -0,0 +1,130 @@
|
||||
from tkinter import Button, Canvas, Label, LabelFrame, Frame, PhotoImage, Scrollbar, Toplevel, LEFT, BOTTOM, X, RIGHT, TOP
|
||||
import os
|
||||
from GuiUtils import ToolTips, set_icon
|
||||
from Utils import local_path
|
||||
|
||||
|
||||
class ItemGfxSelector(object):
|
||||
def __init__(self, parent, callback, valid_items=None, adjuster=False):
|
||||
self.parent = parent
|
||||
self.window = Toplevel(parent)
|
||||
self.window.geometry("800x650")
|
||||
self.callback = callback
|
||||
self.valid_items = valid_items if valid_items else []
|
||||
self.adjuster = adjuster
|
||||
|
||||
self.window.wm_title("Select Triforce Piece Graphics")
|
||||
self.window['padx'] = 5
|
||||
self.window['pady'] = 5
|
||||
|
||||
def open_itemgfx_dir(_evt):
|
||||
from Utils import open_file
|
||||
itemgfx_dir = local_path(os.path.join("data", "itemgfx"))
|
||||
if not os.path.isdir(itemgfx_dir):
|
||||
os.makedirs(itemgfx_dir)
|
||||
open_file(itemgfx_dir)
|
||||
|
||||
frametitle = Frame(self.window)
|
||||
title_text = Label(frametitle, text="Item Graphics")
|
||||
title_text.pack(side=LEFT)
|
||||
local_title_link = Label(frametitle, text="(open folder)", fg="blue", cursor="hand2")
|
||||
local_title_link.pack(side=LEFT)
|
||||
local_title_link.bind("<Button-1>", open_itemgfx_dir)
|
||||
|
||||
self.icon_section(frametitle)
|
||||
|
||||
frame = Frame(self.window)
|
||||
frame.pack(side=BOTTOM, fill=X, pady=5)
|
||||
|
||||
button = Button(frame, text="Default (Triforce)", command=self.use_default)
|
||||
button.pack(side=LEFT, padx=(0, 5))
|
||||
|
||||
if adjuster:
|
||||
button = Button(frame, text="Current triforce from rom", command=self.use_default_unchanged)
|
||||
button.pack(side=LEFT, padx=(0, 5))
|
||||
|
||||
set_icon(self.window)
|
||||
self.window.focus()
|
||||
|
||||
def icon_section(self, frame_label):
|
||||
frame = LabelFrame(self.window, labelwidget=frame_label, padx=5, pady=5)
|
||||
canvas = Canvas(frame, borderwidth=0, width=780)
|
||||
y_scrollbar = Scrollbar(frame, orient="vertical", command=canvas.yview)
|
||||
y_scrollbar.pack(side="right", fill="y")
|
||||
content_frame = Frame(canvas)
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
canvas.create_window((4, 4), window=content_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=y_scrollbar.set)
|
||||
|
||||
def onFrameConfigure(canvas):
|
||||
"""Reset the scroll region to encompass the inner frame"""
|
||||
canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
|
||||
content_frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
|
||||
frame.pack(side=TOP, fill="both", expand=True)
|
||||
|
||||
itemgfx_dir = local_path(os.path.join("data", "itemgfx"))
|
||||
|
||||
if not os.path.exists(itemgfx_dir):
|
||||
label = Label(content_frame, text='No item graphics found in data/itemgfx folder.')
|
||||
label.pack()
|
||||
return
|
||||
|
||||
# Get all GIF files (converted from PNG)
|
||||
gif_files = []
|
||||
for file in os.listdir(itemgfx_dir):
|
||||
if file.lower().endswith('.gif'):
|
||||
item_name = os.path.splitext(file)[0]
|
||||
# Only include if it's in the valid_items list
|
||||
if item_name in self.valid_items:
|
||||
gif_files.append((file, item_name))
|
||||
|
||||
# Sort by name
|
||||
gif_files.sort(key=lambda x: str.lower(x[1]))
|
||||
|
||||
if len(gif_files) == 0:
|
||||
label = Label(content_frame, text='No valid item graphics found. Items must match names from Tables.py.')
|
||||
label.pack()
|
||||
return
|
||||
|
||||
# Calculate how many columns can fit (assuming ~40px per icon with padding)
|
||||
max_columns = 18
|
||||
|
||||
i = 0
|
||||
for filename, item_name in gif_files:
|
||||
filepath = os.path.join(itemgfx_dir, filename)
|
||||
image = self.get_image_for_item(filepath)
|
||||
if image is None:
|
||||
continue
|
||||
|
||||
button = Button(content_frame, image=image, command=lambda name=item_name: self.select_item(name))
|
||||
ToolTips.register(button, item_name)
|
||||
button.image = image
|
||||
button.grid(row=i // max_columns, column=i % max_columns, padx=2, pady=2)
|
||||
i += 1
|
||||
|
||||
if i == 0:
|
||||
label = Label(content_frame, text='No valid item graphics could be loaded.')
|
||||
label.pack()
|
||||
|
||||
def get_image_for_item(self, filepath):
|
||||
"""Load and prepare an item graphic for display"""
|
||||
try:
|
||||
# Load GIF with native Tkinter PhotoImage (no PIL required)
|
||||
photo = PhotoImage(file=filepath)
|
||||
return photo
|
||||
except Exception as e:
|
||||
print(f"Error loading image {filepath}: {e}")
|
||||
return None
|
||||
|
||||
def use_default(self):
|
||||
self.callback("Triforce")
|
||||
self.window.destroy()
|
||||
|
||||
def use_default_unchanged(self):
|
||||
self.callback(None)
|
||||
self.window.destroy()
|
||||
|
||||
def select_item(self, item_name):
|
||||
self.callback(item_name)
|
||||
self.window.destroy()
|
||||
@@ -333,13 +333,13 @@ def determine_paths_for_dungeon(world, player, all_regions, name):
|
||||
if portal.destination:
|
||||
paths.append(portal.door.entrance.parent_region.name)
|
||||
if world.mode[player] == 'standard' and name == 'Hyrule Castle Dungeon':
|
||||
paths.append('Hyrule Dungeon Cellblock')
|
||||
paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room'))
|
||||
paths.append(world.default_zelda_region[player])
|
||||
paths.append((world.default_zelda_region[player], 'Hyrule Castle Throne Room'))
|
||||
entrance = next(x for x in world.dungeon_portals[player] if x.name == 'Hyrule Castle South')
|
||||
# todo: in non-er, we can use the other portals too
|
||||
paths.append(('Hyrule Dungeon Cellblock', entrance.door.entrance.parent_region.name))
|
||||
paths.append((world.default_zelda_region[player], entrance.door.entrance.parent_region.name))
|
||||
paths.append(('Hyrule Castle Throne Room', [entrance.door.entrance.parent_region.name,
|
||||
'Hyrule Dungeon Cellblock']))
|
||||
world.default_zelda_region[player]]))
|
||||
if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town':
|
||||
paths.append('Thieves Attic Window')
|
||||
elif 'Thieves Attic Window' in all_r_names:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import RaceRandom as random
|
||||
from collections import defaultdict
|
||||
from Utils import snes_to_pc
|
||||
|
||||
from source.dungeon.EnemyList import SpriteType, EnemySprite, sprite_translation
|
||||
@@ -446,13 +447,16 @@ def randomize_enemies(world, player):
|
||||
set_mimics(data_tables)
|
||||
elif world.enemy_shuffle[player] != 'none':
|
||||
data_tables = world.data_tables[player]
|
||||
custom_ow, custom_uw = {}, {}
|
||||
enemy_map = world.customizer.get_enemies() if world.customizer else None
|
||||
if enemy_map and player in enemy_map:
|
||||
if 'Underworld' in enemy_map[player]:
|
||||
custom_uw = enemy_map[player]['Underworld']
|
||||
if 'Overworld' in enemy_map[player]:
|
||||
custom_ow = enemy_map[player]['Overworld']
|
||||
if world.force_enemy[player]:
|
||||
custom_ow = {area_id: {i: world.force_enemy[player] for i, s in enumerate(sprite_list)} for area_id, sprite_list in world.data_tables[player].ow_enemy_table.items()}
|
||||
custom_uw = {room_id: {i: world.force_enemy[player] for i, s in enumerate(sprite_list)} for room_id, sprite_list in world.data_tables[player].uw_enemy_table.room_map.items()}
|
||||
else:
|
||||
enemy_map = world.customizer.get_enemies() if world.customizer else None
|
||||
if enemy_map and player in enemy_map:
|
||||
if 'Underworld' in enemy_map[player]:
|
||||
custom_uw = enemy_map[player]['Underworld']
|
||||
if 'Overworld' in enemy_map[player]:
|
||||
custom_ow = enemy_map[player]['Overworld']
|
||||
randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_uw)
|
||||
randomize_underworld_rooms(data_tables, world, player, custom_uw)
|
||||
randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_ow)
|
||||
@@ -525,6 +529,21 @@ def randomize_enemies(world, player):
|
||||
green_mail, blue_mail, red_mail = original_table[idx]
|
||||
del original_table[idx]
|
||||
world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail]
|
||||
# determine default zelda follower location
|
||||
if world.mode[player] == 'standard' and world.doorShuffle[player] == 'crossed' and world.shuffle_followers[player]:
|
||||
def random_zelda():
|
||||
world.default_zelda_region[player] = random.choice(['Hyrule Dungeon Cellblock', 'Thieves Blind\'s Cell'])
|
||||
if world.customizer:
|
||||
placements = world.customizer.get_placements()
|
||||
if placements and player in placements and 'Zelda Herself' in placements[player].values():
|
||||
location = [l for (l, item) in placements[player].items() if item == 'Zelda Herself'][0]
|
||||
if location == 'Suspicious Maiden':
|
||||
world.default_zelda_region[player] = 'Thieves Blind\'s Cell'
|
||||
else:
|
||||
random_zelda()
|
||||
else:
|
||||
random_zelda()
|
||||
|
||||
|
||||
|
||||
def write_enemy_shuffle_settings(world, player, rom):
|
||||
|
||||
@@ -363,7 +363,7 @@ def init_sprite_requirements():
|
||||
SpriteRequirement(EnemySprite.TrinexxFireHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f),
|
||||
SpriteRequirement(EnemySprite.TrinexxIceHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f),
|
||||
SpriteRequirement(EnemySprite.Blind).exalt().sub_group(1, 0x2c).sub_group(2, 0x3b),
|
||||
SpriteRequirement(EnemySprite.Swamola).no_drop().sub_group(3, 0x19),
|
||||
SpriteRequirement(EnemySprite.Swamola).skip().no_drop().sub_group(3, 0x19),
|
||||
SpriteRequirement(EnemySprite.Lynel).sub_group(3, 0x14),
|
||||
SpriteRequirement(EnemySprite.BunnyBeam).no_drop().ow_skip(),
|
||||
SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(),
|
||||
@@ -683,9 +683,11 @@ def setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, sheet_range,
|
||||
if key not in requirements:
|
||||
continue
|
||||
req = requirements[key]
|
||||
if isinstance(req, dict):
|
||||
if isinstance(req, dict) and room_id in req:
|
||||
req = req[room_id]
|
||||
if req.static or not req.can_randomize:
|
||||
else:
|
||||
req = None
|
||||
if req and (req.static or not req.can_randomize):
|
||||
try:
|
||||
combine_req(sub_groups_choices, req)
|
||||
except IncompatibleEnemyException:
|
||||
|
||||
@@ -84,7 +84,7 @@ UwGeneralDeny:
|
||||
- [ 0x0039, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 1"
|
||||
- [0x0039, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Bumper", "AntiFairyCircle"]] #"Skull Woods - Play Pen - Hardhat Beetle"
|
||||
- [ 0x0039, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 2"
|
||||
- [0x003a, 1, ["RollerVerticalUp"]]
|
||||
- [0x003a, 1, ["RollerVerticalUp", "RollerVerticalDown"]]
|
||||
- [ 0x003b, 1, [ "Bumper", "AntiFairyCircle" ]]
|
||||
- [ 0x003b, 4, ["RollerVerticalUp", "RollerVerticalDown"]]
|
||||
- [ 0x003c, 0, ["BigSpike"]]
|
||||
@@ -339,7 +339,11 @@ UwGeneralDeny:
|
||||
- [ 0x00c2, 5, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
|
||||
- [ 0x00c5, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Catwalk - Mini Helmasaur"
|
||||
- [ 0x00c5, 7, [ "Statue" ] ] #"Turtle Rock - Catwalk - Laser Eye (Left) 4"
|
||||
- [0x00c6, 2, ["Bumper", "AntiFairyCircle"]]
|
||||
- [0x00c6, 3, ["Bumper", "AntiFairyCircle"]]
|
||||
- [0x00c6, 4, ["Bumper", "AntiFairyCircle"]]
|
||||
- [0x00c6, 5, ["Bumper", "AntiFairyCircle"]]
|
||||
- [0x00c6, 6, ["Bumper", "AntiFairyCircle"]]
|
||||
- [ 0x00cb, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
|
||||
- [ 0x00cb, 3, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Grand Room NW - Zol 1"
|
||||
- [ 0x00cb, 5, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Grand Room NW - Zol 2"
|
||||
@@ -397,6 +401,7 @@ UwGeneralDeny:
|
||||
- [ 0x00e4, 2, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home - Keese 3"
|
||||
- [ 0x00e5, 4, [ "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home Circle - Keese 5"
|
||||
- [ 0x00e5, 5, [ "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home Circle - Keese 6"
|
||||
- [0x00e6, 0, ["Statue"]] # Death Mountain Descent Cave Left - Keese 1 - Statue blocking pot access
|
||||
- [ 0x00e7, 0, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 1"
|
||||
- [ 0x00e7, 1, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 2"
|
||||
- [ 0x00e7, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 3"
|
||||
@@ -441,7 +446,7 @@ OwGeneralDeny:
|
||||
- [0x05, 10, ["Bumper", "AntiFairyCircle"]] # Blocks path
|
||||
- [0x05, 11, ["Bumper", "AntiFairyCircle"]] # Blocks path to portal
|
||||
- [0x07, 3, ["Bumper", "AntiFairyCircle", "RollerHorizontalRight", "RollerHorizontalLeft"]] # Blocks path to ladder
|
||||
- [0x07, 4, ["RollerHorizontalLeft"]] # Blocks path to ladder
|
||||
- [0x07, 4, ["Bumper", "AntiFairyCircle", "RollerHorizontalLeft"]] # Blocks path to ladder
|
||||
- [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here
|
||||
- [0x35, 8, ["RollerVerticalUp", "RollerVerticalDown"]] # blocks the dock
|
||||
- [0x37, 5, ["RollerVerticalUp"]] # combines with a roller above to make the way impassable
|
||||
@@ -450,7 +455,7 @@ OwGeneralDeny:
|
||||
- [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
|
||||
- [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
|
||||
- [0x40, 16, ["RollerVerticalUp", "RollerVerticalDown"]] # Ropa near back hole is really large as a roller
|
||||
- [0x55, 6, ["BigSpike"]]
|
||||
- [0x55, 6, ["BigSpike", "Bumper", "AntiFairyCircle"]]
|
||||
- [0x57, 5, ["RollerVerticalUp", "RollerVerticalDown"]]
|
||||
- [0x5b, 0, ["AntiFairyCircle", "Bumper"]] # ropa on pyramid
|
||||
- [0x5e, 0, ["Gibo"]] # kiki eating Gibo
|
||||
@@ -480,6 +485,7 @@ OwGeneralDeny:
|
||||
- [0x77, 1, ["Bumper", "AntiFairyCircle"]] # soft-lock potential near ladder
|
||||
- [0x7f, 1, ["Bumper", "AntiFairyCircle"]] # soft-lock potential near ladder
|
||||
UwEnemyDrop:
|
||||
- [0x0015, 8, ["Zoro"]] # layer issues
|
||||
- [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return
|
||||
- [0x00cb, 3, ["Zoro"]] # layer issues
|
||||
- [0x00cb, 5, ["Zoro"]] # layer issues
|
||||
|
||||
@@ -2,6 +2,7 @@ from tkinter import ttk, filedialog, messagebox, StringVar, Button, Entry, Frame
|
||||
from AdjusterMain import adjust, patch
|
||||
from argparse import Namespace
|
||||
from source.classes.SpriteSelector import SpriteSelector
|
||||
from source.classes.ItemGfxSelector import ItemGfxSelector
|
||||
import source.gui.widgets as widgets
|
||||
import json
|
||||
import logging
|
||||
@@ -71,6 +72,30 @@ def adjust_page(top, parent, settings):
|
||||
spriteSelectButton2.pack(side=LEFT)
|
||||
spriteDialogFrame2.pack(anchor=E)
|
||||
|
||||
# Triforce Piece GFX Selection
|
||||
self.triforceGfxNameVar = StringVar()
|
||||
self.triforceGfxNameVar.set('(unchanged)')
|
||||
triforceGfxDialogFrame = Frame(self.frames["leftAdjustFrame"])
|
||||
triforceGfxLabel = Label(triforceGfxDialogFrame, text='Triforce Piece:')
|
||||
triforceGfxEntry = Label(triforceGfxDialogFrame, textvariable=self.triforceGfxNameVar)
|
||||
self.triforce_gfx = None
|
||||
|
||||
def set_triforce_gfx(item_name):
|
||||
self.triforce_gfx = item_name
|
||||
self.triforceGfxNameVar.set(item_name if item_name else '(unchanged)')
|
||||
|
||||
def TriforceGfxSelectAdjuster():
|
||||
from Tables import item_gfx_table
|
||||
valid_items = list(item_gfx_table.keys())
|
||||
ItemGfxSelector(parent, set_triforce_gfx, valid_items=valid_items, adjuster=True)
|
||||
|
||||
triforceGfxSelectButton = Button(triforceGfxDialogFrame, text='...', command=TriforceGfxSelectAdjuster)
|
||||
|
||||
triforceGfxLabel.pack(side=LEFT)
|
||||
triforceGfxEntry.pack(side=LEFT)
|
||||
triforceGfxSelectButton.pack(side=LEFT)
|
||||
triforceGfxDialogFrame.pack(anchor=E)
|
||||
|
||||
# Path to game file to Adjust
|
||||
# This one's more-complicated, build it and stuff it
|
||||
adjustRomFrame = Frame(self.frames["bottomAdjustFrame"])
|
||||
@@ -117,6 +142,7 @@ def adjust_page(top, parent, settings):
|
||||
guiargs.rom = self.romVar2.get()
|
||||
guiargs.baserom = top.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.get()
|
||||
guiargs.sprite = self.sprite
|
||||
guiargs.triforce_gfx = self.triforce_gfx
|
||||
guiargs.outputpath = os.path.dirname(guiargs.rom)
|
||||
try:
|
||||
adjust(args=guiargs)
|
||||
@@ -171,6 +197,7 @@ def adjust_page(top, parent, settings):
|
||||
guiargs.patch = self.patchVar.get()
|
||||
guiargs.baserom = top.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.get()
|
||||
guiargs.sprite = self.sprite
|
||||
guiargs.triforce_gfx = self.triforce_gfx
|
||||
guiargs.outputpath = os.path.dirname(guiargs.patch)
|
||||
try:
|
||||
patch(args=guiargs)
|
||||
|
||||
@@ -77,32 +77,37 @@ def bottom_frame(self, parent, args=None):
|
||||
argsDump['sprite'] = argsDump['sprite'].name
|
||||
save_settings(parent, argsDump, "last.json")
|
||||
|
||||
guiargs = create_guiargs(parent)
|
||||
# get default values for missing parameters
|
||||
cliargs = ['--multi', str(guiargs.multi)]
|
||||
if hasattr(guiargs, 'customizer'):
|
||||
cliargs.extend(['--customizer', str(guiargs.customizer)])
|
||||
for k,v in vars(parse_cli(cliargs)).items():
|
||||
if k not in vars(guiargs):
|
||||
setattr(guiargs, k, v)
|
||||
elif type(v) is dict: # use same settings for every player
|
||||
players = guiargs.multi if len(v) == 0 else len(v)
|
||||
setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, players + 1)})
|
||||
argsDump = vars(guiargs)
|
||||
|
||||
needEnemizer = False
|
||||
falsey = ["none", "default", False, 0]
|
||||
for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]:
|
||||
if enemizerOption in argsDump:
|
||||
if isinstance(argsDump[enemizerOption], dict):
|
||||
for playerID,playerSetting in argsDump[enemizerOption].items():
|
||||
if not playerSetting in falsey:
|
||||
needEnemizer = True
|
||||
elif not argsDump[enemizerOption] in falsey:
|
||||
needEnemizer = True
|
||||
seeds = []
|
||||
|
||||
try:
|
||||
guiargs = create_guiargs(parent)
|
||||
# get default values for missing parameters
|
||||
cliargs = ['--multi', str(guiargs.multi)]
|
||||
if hasattr(guiargs, 'customizer'):
|
||||
cliargs.extend(['--customizer', str(guiargs.customizer)])
|
||||
|
||||
for k,v in vars(parse_cli(cliargs)).items():
|
||||
if k not in vars(guiargs):
|
||||
setattr(guiargs, k, v)
|
||||
elif type(v) is dict: # use same settings for every player
|
||||
players = guiargs.multi if len(v) == 0 else len(v)
|
||||
setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, players + 1)})
|
||||
if guiargs.multi == 1 and guiargs.names.endswith("=="):
|
||||
# allow settings code thru player names entry
|
||||
guiargs.code[1] = guiargs.names
|
||||
guiargs.names = ""
|
||||
argsDump = vars(guiargs)
|
||||
|
||||
needEnemizer = False
|
||||
falsey = ["none", "default", False, 0]
|
||||
for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]:
|
||||
if enemizerOption in argsDump:
|
||||
if isinstance(argsDump[enemizerOption], dict):
|
||||
for playerID,playerSetting in argsDump[enemizerOption].items():
|
||||
if not playerSetting in falsey:
|
||||
needEnemizer = True
|
||||
elif not argsDump[enemizerOption] in falsey:
|
||||
needEnemizer = True
|
||||
seeds = []
|
||||
|
||||
if guiargs.count is not None and guiargs.seed:
|
||||
seed = guiargs.seed
|
||||
for _ in range(guiargs.count):
|
||||
@@ -340,6 +345,9 @@ def create_guiargs(parent):
|
||||
guiargs.sprite = parent.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"]
|
||||
guiargs.randomSprite = parent.randomSprite.get()
|
||||
|
||||
# Get Triforce Piece GFX Selection
|
||||
guiargs.triforce_gfx = parent.pages["randomizer"].pages["gameoptions"].widgets["triforce_gfx"]["selectedItem"]
|
||||
|
||||
# Get output path
|
||||
guiargs.outputpath = parent.settings["outputpath"]
|
||||
|
||||
|
||||
@@ -197,6 +197,13 @@ def loadcliargs(gui, args, settings=None):
|
||||
spriteNameVar=gui.pages["adjust"].content.spriteNameVar2,
|
||||
randomSpriteVar=gui.randomSprite)
|
||||
|
||||
# Figure out Triforce GFX Selection
|
||||
if "triforce_gfx" in args and args["triforce_gfx"] is not None:
|
||||
gui.pages["randomizer"].pages["gameoptions"].widgets["triforce_gfx"]["selectedItem"] = args["triforce_gfx"]
|
||||
gui.pages["randomizer"].pages["gameoptions"].widgets["triforce_gfx"]["itemNameVar"].set(args["triforce_gfx"])
|
||||
gui.pages["adjust"].content.triforce_gfx = args["triforce_gfx"]
|
||||
gui.pages["adjust"].content.triforceGfxNameVar.set(args["triforce_gfx"])
|
||||
|
||||
# Load args/settings for Adjust tab
|
||||
def loadadjustargs(gui, settings):
|
||||
options = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from tkinter import ttk, StringVar, Button, Entry, Frame, Label, NE, NW, E, W, LEFT, RIGHT
|
||||
from functools import partial
|
||||
import source.classes.SpriteSelector as spriteSelector
|
||||
import source.classes.ItemGfxSelector as itemGfxSelector
|
||||
import source.gui.widgets as widgets
|
||||
import json
|
||||
import os
|
||||
@@ -66,6 +67,34 @@ def gameoptions_page(top, parent):
|
||||
spriteSelectButton.pack(side=LEFT)
|
||||
spriteDialogFrame.pack(anchor=E)
|
||||
|
||||
## Triforce Piece graphics selection
|
||||
triforcegfxDialogFrame = Frame(self.frames["leftRomOptionsFrame"])
|
||||
triforceGfxLabel = Label(triforcegfxDialogFrame, text='Triforce Piece:')
|
||||
|
||||
self.widgets["triforce_gfx"] = {}
|
||||
self.widgets["triforce_gfx"]["selectedItem"] = None
|
||||
self.widgets["triforce_gfx"]["itemNameVar"] = StringVar()
|
||||
self.widgets["triforce_gfx"]["itemNameVar"].set('Triforce')
|
||||
|
||||
triforceGfxEntry = Label(triforcegfxDialogFrame, textvariable=self.widgets["triforce_gfx"]["itemNameVar"])
|
||||
|
||||
def triforce_gfx_setter(item_name):
|
||||
self.widgets["triforce_gfx"]["selectedItem"] = item_name
|
||||
self.widgets["triforce_gfx"]["itemNameVar"].set(item_name)
|
||||
|
||||
def triforce_gfx_select():
|
||||
# Import Tables to get valid item names
|
||||
from Tables import item_gfx_table
|
||||
valid_items = list(item_gfx_table.keys())
|
||||
itemGfxSelector.ItemGfxSelector(parent, triforce_gfx_setter, valid_items=valid_items)
|
||||
|
||||
triforceGfxSelectButton = Button(triforcegfxDialogFrame, text='...', command=triforce_gfx_select)
|
||||
|
||||
triforceGfxLabel.pack(side=LEFT)
|
||||
triforceGfxEntry.pack(side=LEFT)
|
||||
triforceGfxSelectButton.pack(side=LEFT)
|
||||
triforcegfxDialogFrame.pack(anchor=E)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
|
||||
@@ -403,6 +403,7 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world):
|
||||
else:
|
||||
region_name = 'West Dark Death Mountain (Top)'
|
||||
om_cave_options = list(get_accessible_entrances(region_name, avail, [], cross_world, True, True, True, True))
|
||||
om_cave_options = [e for e in om_cave_options if e in avail.entrances]
|
||||
if avail.swapped:
|
||||
om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances]
|
||||
assert len(om_cave_options), 'No available entrances left to place Old Man Cave'
|
||||
@@ -643,7 +644,6 @@ def do_dark_sanc(entrances, exits, avail):
|
||||
entrances.remove(choice)
|
||||
exits.remove('Dark Sanctuary Hint')
|
||||
connect_entrance(choice, 'Dark Sanctuary Hint', avail)
|
||||
ext.connect(avail.world.get_entrance(choice, avail.player).parent_region)
|
||||
if not avail.coupled:
|
||||
avail.decoupled_entrances.remove(choice)
|
||||
if avail.swapped and choice != 'Dark Sanctuary Hint':
|
||||
@@ -651,8 +651,7 @@ def do_dark_sanc(entrances, exits, avail):
|
||||
entrances.remove(swap_ent)
|
||||
exits.remove(swap_ext)
|
||||
elif not ext.connected_region:
|
||||
# default to output to vanilla area, assume vanilla connection
|
||||
ext.connect(avail.world.get_region('Dark Chapel Area', avail.player))
|
||||
raise Exception('Dark Sanctuary Hint was placed earlier but its exit not properly connected')
|
||||
|
||||
|
||||
def do_links_house(entrances, exits, avail, cross_world):
|
||||
@@ -722,8 +721,6 @@ def do_links_house(entrances, exits, avail, cross_world):
|
||||
connect_two_way(links_house, lh_exit, avail)
|
||||
else:
|
||||
connect_entrance(links_house, lh_exit, avail)
|
||||
ext = avail.world.get_entrance('Big Bomb Shop Exit', avail.player)
|
||||
ext.connect(avail.world.get_entrance(links_house, avail.player).parent_region)
|
||||
entrances.remove(links_house)
|
||||
exits.remove(lh_exit)
|
||||
if not avail.coupled:
|
||||
@@ -867,7 +864,7 @@ def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_wo
|
||||
|
||||
for p in range(1, avail.world.players + 1):
|
||||
avail.world.key_logic[p] = {}
|
||||
base_world = copy_world_premature(avail.world, avail.player)
|
||||
base_world = copy_world_premature(avail.world, avail.player, create_flute_exits=True)
|
||||
base_world.override_bomb_check = True
|
||||
|
||||
connect_simple(base_world, 'Links House S&Q', start_region, avail.player)
|
||||
@@ -1907,6 +1904,12 @@ def connect_entrance(entrancename, exit_name, avail):
|
||||
avail.entrances.remove(entrancename)
|
||||
if avail.coupled:
|
||||
avail.exits.remove(exit_name)
|
||||
if exit_name == 'Big Bomb Shop' and avail.world.is_bombshop_start(avail.player):
|
||||
ext = avail.world.get_entrance('Big Bomb Shop Exit', avail.player)
|
||||
ext.connect(avail.world.get_entrance(entrancename, avail.player).parent_region)
|
||||
if exit_name == 'Dark Sanctuary Hint' and avail.world.is_dark_chapel_start(avail.player):
|
||||
ext = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player)
|
||||
ext.connect(avail.world.get_entrance(entrancename, avail.player).parent_region)
|
||||
world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player)
|
||||
logging.getLogger('').debug(f'Connected (entr) {entrance.name} to {exit.name if exit is not None else region.name}')
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ def roll_settings(weights):
|
||||
if 'rom' in weights:
|
||||
romweights = weights['rom']
|
||||
ret.sprite = get_choice('sprite', romweights)
|
||||
ret.triforce_gfx = get_choice('triforce_gfx', romweights)
|
||||
ret.disablemusic = get_choice_bool('disablemusic', romweights)
|
||||
ret.quickswap = get_choice_bool('quickswap', romweights)
|
||||
ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights)
|
||||
|
||||
Reference in New Issue
Block a user