Some fixes for Custom Goals

- Changed spoiler meta output to include goaltext
- Fixed GT Cutscene crash
- Allow multiple %d to resolve in goaltext
This commit is contained in:
codemann8
2025-11-08 10:37:16 -06:00
parent 2ff0aa7b67
commit 600865b659
5 changed files with 32 additions and 21 deletions

View File

@@ -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)

14
Main.py
View File

@@ -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}')

15
Rom.py
View File

@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'a1c8a1c9b4a626f25a240d5b35b17ffe'
RANDOMIZERBASEHASH = '39c6d90d9aa4711fe3c95d85b1a9b16e'
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'])
@@ -2588,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']:

Binary file not shown.

View File

@@ -76,7 +76,7 @@ This must be defined by player. Each player number should be listed with the app
* `gtentry` (Ganon's Tower entrance)
* `ganongoal` (Ganon vulnerability)
* `pedpull` (Master Sword Pedestal activation)
* `pedgoal` (Master Sword Pedestal activation)
* `murahgoal` (Murahdahla requirement, if given requirements, Murahdahla appears always and acts as an alternative way to beat the game)
These four custom goals use the following identical structure to define them. These goals have four primary subsections: `cutscene_gfx`, `goaltext`, `requirements`, and `logic`