Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -3274,16 +3274,20 @@ class Spoiler(object):
|
||||
custom = self.metadata['custom_goals'][player]
|
||||
if custom['gtentry'] and 'requirements' in custom['gtentry']:
|
||||
outfile.write('GT Entry Requirement:'.ljust(line_width) + 'custom\n')
|
||||
outfile.write(' %s\n' % custom['gtentry']['goaltext'])
|
||||
else:
|
||||
outfile.write('GT Entry Requirement:'.ljust(line_width) + '%s crystals\n' % str(self.world.crystals_gt_orig[player]))
|
||||
if custom['ganongoal'] and 'requirements' in custom['ganongoal']:
|
||||
outfile.write('Ganon Requirement:'.ljust(line_width) + 'custom\n')
|
||||
outfile.write(' %s\n' % custom['ganongoal']['goaltext'])
|
||||
else:
|
||||
outfile.write('Ganon Requirement:'.ljust(line_width) + '%s crystals\n' % str(self.world.crystals_ganon_orig[player]))
|
||||
if custom['pedgoal'] and 'requirements' in custom['pedgoal']:
|
||||
outfile.write('Pedestal Requirement:'.ljust(line_width) + 'custom\n')
|
||||
outfile.write(' %s\n' % custom['pedgoal']['goaltext'])
|
||||
if custom['murahgoal'] and 'requirements' in custom['murahgoal']:
|
||||
outfile.write('Murahdahla Requirement:'.ljust(line_width) + 'custom\n')
|
||||
outfile.write(' %s\n' % custom['murahgoal']['goaltext'])
|
||||
outfile.write('Swords:'.ljust(line_width) + '%s\n' % self.metadata['weapons'][player])
|
||||
outfile.write('\n')
|
||||
outfile.write('Accessibility:'.ljust(line_width) + '%s\n' % self.metadata['accessibility'][player])
|
||||
@@ -3396,20 +3400,22 @@ class Spoiler(object):
|
||||
player_name = '' if self.world.players == 1 else str(' (Player ' + str(player) + ')')
|
||||
goal = self.world.custom_goals[player]['gtentry']
|
||||
if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
outfile.write(str('GT Entry Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
pass
|
||||
# outfile.write(str('GT Entry Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
elif self.world.crystals_gt_orig[player] == 'random':
|
||||
outfile.write(str('Crystals Required for GT' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['gt_crystals'][player])))
|
||||
goal = self.world.custom_goals[player]['ganongoal']
|
||||
if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
outfile.write(str('Ganon Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
pass
|
||||
# outfile.write(str('Ganon Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
elif self.world.crystals_ganon_orig[player] == 'random':
|
||||
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
|
||||
goal = self.world.custom_goals[player]['pedgoal']
|
||||
if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
outfile.write(str('Pedestal Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
goal = self.world.custom_goals[player]['murahgoal']
|
||||
if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
outfile.write(str('Murahdahla Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
# goal = self.world.custom_goals[player]['pedgoal']
|
||||
# if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
# outfile.write(str('Pedestal Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
# goal = self.world.custom_goals[player]['murahgoal']
|
||||
# if goal and 'requirements' in goal and goal['requirements'][0]['condition'] != 0x00:
|
||||
# outfile.write(str('Murahdahla Sign Text' + player_name + ':').ljust(line_width) + '%s\n' % goal['goaltext'])
|
||||
outfile.write('\n\nPrizes:\n\n')
|
||||
for dungeon, prize in self.prizes.items():
|
||||
outfile.write(str(dungeon + ':').ljust(line_width) + '%s\n' % prize)
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.1.2
|
||||
- Various fixes for Custom Goal Framework
|
||||
- Added custom gfx for Pedestal and Murahdahla
|
||||
- Re-fixed purple chest follower dupe
|
||||
- Updated tournament winners texts
|
||||
|
||||
## 0.6.1.1
|
||||
- Fixed issue with Bosses goals in Custom Goal Framework
|
||||
- Fixed error when using Custom Goals with no extra values
|
||||
|
||||
14
Main.py
14
Main.py
@@ -645,23 +645,23 @@ def resolve_random_settings(world, args):
|
||||
else:
|
||||
raise Exception(f'Invalid {list(r.keys())[0]} requirement target for {goal_type}')
|
||||
if req['condition'] & 0x7F == req_table['Pendants']:
|
||||
goal['logic']['pendants'] = req['target'] or 3
|
||||
goal['logic']['pendants'] = req['target'] = req.get('target', 3)
|
||||
elif req['condition'] & 0x7F == req_table['Crystals']:
|
||||
goal['logic']['crystals'] = req['target'] or 7
|
||||
goal['logic']['crystals'] = req['target'] = req.get('target', 7)
|
||||
elif req['condition'] & 0x7F == req_table['PendantBosses']:
|
||||
goal['logic']['pendant_bosses'] = req['target'] or 3
|
||||
goal['logic']['pendant_bosses'] = req['target'] = req.get('target', 3)
|
||||
elif req['condition'] & 0x7F == req_table['CrystalBosses']:
|
||||
goal['logic']['crystal_bosses'] = req['target'] or 7
|
||||
goal['logic']['crystal_bosses'] = req['target'] = req.get('target', 7)
|
||||
elif req['condition'] & 0x7F == req_table['PrizeBosses']:
|
||||
goal['logic']['bosses'] = req['target'] or 10
|
||||
goal['logic']['bosses'] = req['target'] = req.get('target', 10)
|
||||
elif req['condition'] & 0x7F == req_table['Aga1']:
|
||||
goal['logic']['aga1'] = True
|
||||
elif req['condition'] & 0x7F == req_table['Aga2']:
|
||||
goal['logic']['aga2'] = True
|
||||
elif req['condition'] & 0x7F == req_table['TriforcePieces']:
|
||||
goal['logic']['goal_items'] = req['target'] or None
|
||||
goal['logic']['goal_items'] = req['target'] = req.get('target', None)
|
||||
elif req['condition'] & 0x7F == req_table['CollectionRate']:
|
||||
goal['logic']['collection'] = req['target'] or None
|
||||
goal['logic']['collection'] = req['target'] = req.get('target', None)
|
||||
goal['requirements'].append(req)
|
||||
except KeyError:
|
||||
raise KeyError(f'Invalid {goal_type} requirement: {r}')
|
||||
|
||||
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
|
||||
from OverworldGlitchRules import create_owg_connections
|
||||
from Utils import bidict
|
||||
|
||||
version_number = '0.6.1.1'
|
||||
version_number = '0.6.1.2'
|
||||
# branch indicator is intentionally different across branches
|
||||
version_branch = ''
|
||||
|
||||
|
||||
36
Rom.py
36
Rom.py
@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = '72c4b2d00057d1faced32871d8081f3a'
|
||||
RANDOMIZERBASEHASH = '2039c11b935d3b81f78810d9f4be19d6'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -1270,8 +1270,11 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
goal_bytes += [req['target']]
|
||||
else:
|
||||
goal_bytes += int16_as_bytes(req['target'])
|
||||
elif 'target' in req:
|
||||
if req['condition'] & 0x7F < 0x08:
|
||||
elif req['condition'] & 0x80 == 0:
|
||||
if req['condition'] & 0x7F == 0x06 or req['condition'] & 0x7F == 0x07:
|
||||
# agahnims have no target value
|
||||
pass
|
||||
elif req['condition'] & 0x7F < 0x08:
|
||||
goal_bytes += [req['target']]
|
||||
else:
|
||||
goal_bytes += int16_as_bytes(req['target'])
|
||||
@@ -1338,12 +1341,19 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
if start_address > 0x81D8:
|
||||
raise Exception("Custom Goal data too long to fit in allocated space, try reducing the amount of requirements.")
|
||||
|
||||
# gt entry
|
||||
gtentry = world.custom_goals[player]['gtentry']
|
||||
if gtentry and 'cutscene_gfx' in gtentry:
|
||||
gfx = gtentry['cutscene_gfx']
|
||||
write_int16(rom, snes_to_pc(0x3081D8), gfx[0])
|
||||
rom.write_byte(snes_to_pc(0x3081E6), gfx[1])
|
||||
# goal cutscene gfx
|
||||
goals = {
|
||||
#goal: gfx addr, palette addr
|
||||
'gtentry': (0x3081D8, 0x3081E6),
|
||||
'pedgoal': (0x3081ED, 0x3081F3),
|
||||
'murahgoal': (0x3081F6, 0x3081FC),
|
||||
}
|
||||
for goal_type, gfx_addr in goals.items():
|
||||
goal = world.custom_goals[player][goal_type]
|
||||
if goal and 'cutscene_gfx' in goal:
|
||||
gfx = goal['cutscene_gfx']
|
||||
write_int16(rom, snes_to_pc(gfx_addr[0]), gfx[0])
|
||||
rom.write_byte(snes_to_pc(gfx_addr[1]), gfx[1])
|
||||
|
||||
# block HC upstairs doors in rain state in standard mode
|
||||
prevent_rain = world.mode[player] == 'standard' and world.shuffle[player] != 'vanilla' and world.logic[player] != 'nologic'
|
||||
@@ -1751,7 +1761,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
gen, seedstring = rom_header.split('|', 1)
|
||||
gen = f'{gen:<3}'
|
||||
seedstring = f'{int(seedstring):09}' if seedstring.isdigit() else seedstring[:9]
|
||||
rom.name = bytearray(f'{gen}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
||||
rom.name = bytearray(f'OR{gen}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
||||
elif len(rom_header) <= 9:
|
||||
seedstring = f'{int(rom_header):09}' if rom_header.isdigit() else rom_header
|
||||
rom.name = bytearray(f'OR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
||||
@@ -2581,8 +2591,10 @@ def write_strings(rom, world, player, team):
|
||||
|
||||
def get_custom_goal_text(type):
|
||||
goal_text = world.custom_goals[player][type]['goaltext']
|
||||
if '%d' in goal_text:
|
||||
return goal_text % world.custom_goals[player][type]['requirements'][0]['target']
|
||||
placeholder_count = goal_text.count('%d')
|
||||
if placeholder_count > 0:
|
||||
targets = [req['target'] for req in world.custom_goals[player][type]['requirements'] if 'target' in req][:placeholder_count]
|
||||
return goal_text % tuple(targets)
|
||||
return goal_text
|
||||
|
||||
if world.custom_goals[player]['gtentry'] and 'goaltext' in world.custom_goals[player]['gtentry']:
|
||||
|
||||
3
Rules.py
3
Rules.py
@@ -1805,6 +1805,9 @@ def standard_rules(world, player):
|
||||
add_rule(world.get_location('Hyrule Castle Tree', player), lambda state: state.has('Zelda Delivered', player))
|
||||
add_rule(world.get_location('Central Bonk Rocks Tree', player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
if not world.is_copied_world:
|
||||
add_rule(world.get_location('Hyrule Castle Courtyard Tree Pull', player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# don't allow bombs to get past here before zelda is rescued
|
||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
||||
|
||||
|
||||
6
Text.py
6
Text.py
@@ -1782,6 +1782,8 @@ class TextTable(object):
|
||||
text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^")
|
||||
text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.")
|
||||
text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n"
|
||||
" ~~~2025~~~\nSchulzer\n\n"
|
||||
" ~~~2024~~~\nhumbugh\n\n"
|
||||
" ~~~2023~~~\nEriror\n\n"
|
||||
" ~~~2022~~~\nAndy\n\n"
|
||||
" ~~~2021~~~\nprdwong")
|
||||
@@ -1795,7 +1797,8 @@ class TextTable(object):
|
||||
text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.")
|
||||
text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.")
|
||||
text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n"
|
||||
" ~~~2023~~~\nnGanonsGoneWild\n\n"
|
||||
" ~~~2024~~~\nGammachuu\n\n"
|
||||
" ~~~2023~~~\nGanonsGoneWild\n\n"
|
||||
" ~~~2022~~~\nObscure\n\n"
|
||||
" ~~~2021~~~\nDaaanty\n\n"
|
||||
" ~~~2019~~~\nJet082\n\n"
|
||||
@@ -2029,6 +2032,7 @@ class TextTable(object):
|
||||
text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in your ears? I cannot die!")
|
||||
# 190
|
||||
text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n"
|
||||
"~~~HMG 2025~~~\nSkele\n"
|
||||
"~~~No Logic 2024~~~\ntam\n\n"
|
||||
"~~~HMG 2023~~~\ntam\n\n"
|
||||
"~~~No Logic 2022~~~\nChexhuman\n\n"
|
||||
|
||||
Binary file not shown.
@@ -577,9 +577,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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user