From 2771040cb32ae9dbade05234e9c499e01f87e37d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 7 Nov 2025 22:06:07 -0600 Subject: [PATCH 1/9] Added custom gfx for Ped and Murahdahla --- Rom.py | 21 ++++++++++++++------- data/base2current.bps | Bin 137065 -> 137148 bytes 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Rom.py b/Rom.py index 43c3006e..b471867c 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '72c4b2d00057d1faced32871d8081f3a' +RANDOMIZERBASEHASH = 'a1c8a1c9b4a626f25a240d5b35b17ffe' class JsonRom(object): @@ -1338,12 +1338,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' diff --git a/data/base2current.bps b/data/base2current.bps index 224e1fb6c7a305d7eaf1f730f18852f88bc3c6fd..9560fa8a184ce289aed573bdfbe895330229f819 100644 GIT binary patch delta 1993 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 From a3dd82f4670db378da4f07c2c97ce26ee16632df Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 7 Nov 2025 22:08:18 -0600 Subject: [PATCH 2/9] Initial custom header implementation --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index b471867c..a04a4aab 100644 --- a/Rom.py +++ b/Rom.py @@ -1758,7 +1758,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] From 7cfa8f4b4d31e6a09db2dd877be3b692124a6dcb Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 02:36:28 -0600 Subject: [PATCH 3/9] Yaml errors no longer fail silently --- source/gui/bottom.py | 55 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 7faf3402..e25c2b2f 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -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): From 7016e5afbf0d72f3e13b5675a7cf8852ce984d72 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 02:36:28 -0600 Subject: [PATCH 4/9] Yaml errors no longer fail silently --- source/classes/CustomSettings.py | 14 +++++--- source/gui/bottom.py | 55 +++++++++++++++++--------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 80b23e0d..af34f007 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -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 diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 7faf3402..e25c2b2f 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -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): From 600865b6590b6c4214691619890ebf89ba520ade Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 10:37:16 -0600 Subject: [PATCH 5/9] Some fixes for Custom Goals - Changed spoiler meta output to include goaltext - Fixed GT Cutscene crash - Allow multiple %d to resolve in goaltext --- BaseClasses.py | 22 ++++++++++++++-------- Main.py | 14 +++++++------- Rom.py | 15 ++++++++++----- data/base2current.bps | Bin 137148 -> 137199 bytes docs/Customizer.md | 2 +- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index f41301eb..3abcbdd4 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -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) diff --git a/Main.py b/Main.py index ee5a2d63..a8d2265f 100644 --- a/Main.py +++ b/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}') diff --git a/Rom.py b/Rom.py index a04a4aab..8e69b142 100644 --- a/Rom.py +++ b/Rom.py @@ -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']: diff --git a/data/base2current.bps b/data/base2current.bps index 9560fa8a184ce289aed573bdfbe895330229f819..2d325e9098601881c2aac9356f06c1ebc87e3f39 100644 GIT binary patch delta 6597 zcmW+430PA{^UX^L;Z6V*l_Ne738124@j?*~kWxJG78Ml{8m)?2&%%3w00BaX4_Glk z0=$TU(txQ(&`JfW#MTU%P2u- zAxCWO(u9P-hnmGBV6A4WKYXJprX1R?bJT$5#6&*7nz_|B-~~{_{sTXF(?uM0PA@;s z$g3H;jG?PlR2KUi8O_%{NcA_5+9c1g_;uZt!zPDazGQpRnQKgYdO^*x0o&N;iNvM?-P6e(+Ov zNRz&Xnx$?`T-h^NQkP$#PYG1972^Wn6nk=9DE!IZAD8W*pnSA3=Ctu6z_BZz!@GWm zRYXjNQ1(njA(XIlC!8DEk@qgpN(o&<{dq-C{UztR<06i6^x!x~GtyKEHE*f4A_eU|-b=kY=fo#JXRce#rDR7FJ; zalK4QwUW|RHq2RO=ws;XraW)#;-RB>a|Z{HD5wTB#o?G@aIjC4atRBMF!T|$DN(aS zuA-{h%o(bXbJw^lZbJ2!E#D9ca0x<*!{7<0HI$^38$4SPD;gLW9JE(Z$65dA;lutp z%3ZuwQpl79(F}c8Md8V(Ra6L@6um~$tmQ-uQ_{+m{H&qmT5gcQ%UY}DBJf$(*QK0f ztqaczM%-sQ(*}Zeiz{(2e?H-y~bS2EOI4G6ua8#f5c%@^a(0y$LA zDiS6`iup{!+KD)EhJPX;VJR!g(ZHAH6FIqJ2sgi{5jc~cD)UTjf;Vh8D@|1->GQ%I zbRyFkI?SbKC7$Gvezx`7G*Gfbt{f$Kb>_A!XLx)xznEorT+1V;s?9I1X@Cr0Z!Wwc zae`#l_~SIv?>qCuAGIP^cFZ;*tx$=Me(-)PTlBlxlUdJBQC2w-w1ViDSYoyaX>*dj zMd0G`BbQ2j@D3H51uvU{bWxejua@!Lzx{!eajsKj)(>rbYCAjhpH4ULA2`~XT7og3 zDl_;0Glu6-Ud>T|pL7QozV`u_OKqMiPNv@TXLbvZXB&Z5-nES7!)>wTQ5DxEqIwV~ z$ww)?Cv=wxGhLE;cNz7zilfF_#tem49`sxKhQm?F`9m-;aNoV`c~L^*sl0lk^9a+b z%_F)*{gS3mfU4yhlW+&?;(;wz}HKSl#V`A!R{UzTMp_v^!f#yNgv|cch(- zjsvupkvBlQ_YTlqAp`We0c@gmV71RCIB=Y9wR+pdt??lPWy1f&1N5U_Izb!qpOR82 z92gjo^6bLife1EORyx z>iW zci3WuuuxrLoLrc=ppYO)0V!bLq8$O?t|P5N+=i!OO|^7sy)Vez!7~2|&2E7i1S>%}IXj%}=C8?E&4uZuwe^i)C zc{`*rggw9%b2zTL-R@Gqpj>}fB*|6LA@!?t*BQUYMrO9|2;-zJuc%g;N_acae$Tvi zn@j!9a{c}P6epNU5aF4pbg9qO4W$`BwgtzamDzy(z_U(PtLpcc>wgxRVtG3xm(goR z#P%sG^iOp8IqW&@LYfIt_o-G@=m)fg=#|#M@YIuO%~d=@j080LQG1W!tIukz<@yv) zRD`(VZFH3q55%T`{ zOVEBflZ-~;%h(F)H!7&mZ)I0Ab#C>q@t^TDC9_<+{+Y;>f*0C{!h#`SZDxi3mHMfY zo;|H0El-7`I!Rr~?8K27t(G(UF&%~}^Da^ij}@lXx;hOG%|5ZZh&_$I*WF|^x;~{- zeU(-t<~scvSTYC(nm5Id1(Z=y&AJEoXf ztQ}WeQ}2Ye#HQs)T2wU(D|nh#APin7N* z#E|_|o^BtNrwR2Mp$bPuY@m+{XG8ZGh;)crcu$vZ`DYAV16IPyF^Gbe&nYr^+Ge& zAonns4jf7dgP+}_9dnt9Kgl}qxx~sYMhg)g4ir2> z4dF0>^!G>Khr=l6_dS15>cd5d*oj~)1b_yO8Vml^s^xh#grZkw=(P>)mUt*M?nXZC z%$ixh9eGMD*CwG2W1-H6)&ANnYw>gukwzD*YZ+-IRGuy}-8M909DFv)q9BY~1sFL6 zdCF~L8zal1=PWvL?TIGw^XlSwG%XfA9tYE42?`nyt6kjF6q=_O+--x}WTYIAx49C1 zF&+XYSJvu$tl8~HYIP$pIa;e*iAi;>ZnZW0KxK&YBSY`~;bqo-iDY#xTDrMZz3CG2 zdLM}+z~7^3?A|BJHyNq5*-P4VLJCDkzzTAD0;-IFb+K)4q+sJE?$mch)W`5E=k94Y zYU|JPjnmwzPrJ0H@Tn?g{S1fhD$O|uV;NspUb*#zBGIJ&t=7q`-L|`~(RB}+JOP4X z8A_P|zQV2PCCs?L#Yu>s0DfyjVpZ%GKLM?whZsG`3vs;4?zwnqa4;1-%a$1>gsjm; zUS&_dEi8V&8TlPjGfk|mhF`QGrm4slg@pEP`67<{ifZ-i^n6FlhID#kDrt4=9Lk}; zC%`@ThIX!U==KM7Lg%JVk5)7=5hh674D{XWhVWzmGb}jPXlQixSGUC(k__7oWrpL1 zHp6T*brOsc$2rr6Mq)JYD_)nxHtp|VXC%X7Fd@|>7zP)SVG;xjloG#VY8u%l!OWb> ztc7gdmPUe5V2DCvyo*dxY2Dh;gWoa-&1#12);7n@b)eDmus27SI2X5!Ya$5uPfxTE zL`O^Q(p${!hBTj|1~0EGHJ=g0A`C0h%zfQW)Lfic+O5Njm_yh~dAIiFGH(aEt=)9q zMBtAV2&bT-3@9lPm-w!?4TJIazX>LEY0Nw>1QzvzW{cOvee{ zw%pGq_@8OBo$fpRZuWhQ2fqGJVLv?-fuY8Q_LR3uOoq3#A|V=y3>Ckj`e-=dy*=rn zF$jVnhN0=u;AdH%J^F19+=e@fw8q_7KB|U*(sg;b1eWvQ^ zSa;#(7Y$;Xx%iTzPxf?FUF_)y?ftvAr(>s8ok=UmOX@V5`G&pyOQGV?H*cl%RC!NF zs)>GQjcaXfmsu_hyC&p=hwG3&hB=ccGGW_(n9@uDb3U8NJ?V0 zi^6_kmoo~}ROB$>7v{P4y>aNXC7^&F6qyVkj2u5DzYdoe-Bl%N^QoM-rb@SM_dTUk zOB&jZs*_sKCDC(Tz*Gfe zL&39;tfKMkCoAAR$UuQB@l*E#db$#3j6tI#^d}$R>E{v-Vss{MX$~`#}d05(n+rmcGa~I-2P!A0E2m zPNO)Yn|aj`FG1_pK@@JLE7#%o#ZF{j2fImrI!a#;YpA^`rNtL&T9pm_CjEypv|(?4 zi_JMBBjfpeWy+}{g?Zv~^wWA+kaOcfnP9|-5z>F(Dti0zdD`>fcwU5L3&s;M;$u{?0j7&~|Accg!6$Ppy(fdtLhI4}4KO^+KTSxZqZPIcV>BJ3HLAMl zKjP6|$HVP*UIzVr27U5fA`5MSxUWJ%8-a!0==4UI9Xk4ekgiPrSJ5T9n6^(yHyCPz zHrH$23r?m~%H<}KZnTF{`;?7HnuP*4!A3IpBUG>nGN2#XH$k=dU^1*if2YAd5_eR4Hp5cbk1lNnzjwpOn;|qjGs9j((heE@68v-wmD$yS zjPkytO}pS&i;{1s%DTv*IW~GCKboE3V(qS0>UjaIh4xH=j$K3JGmybCG zuk!^8-2x*;zrBjlGu=ZUG^`B7mhx|=2zhLd&d&ZCmDxkIm1VC1RGIgCM+6DEWE?)WpkA@!8DJY ze@9b56cWl4r)s~fjEU+vyy4&%?2?eX8X-7=sCk8@Efea6J{fZ~=YQAx5~gdGa0)EP2K~wrz7mb3nSI+Z1Ugu~Wrda#*J{iIuMI$#@zEVOj z;DcXU4CB0pP4S?W%ZN3V;R}in@9$+cYBSK4Vn`vyGtt-*cp^+nd&H2)Z4(-$0w1T3 z-S0BP8=`zJTVhp^MS3h;DyB7UF$8hqPU0dlZ8@)o&AbsAdz@*;19@C-I4H@-ImIrP ztJHzdv$g3chJ}eR)Uu0(WQTxQ!iiSNs;gZN&wP(QX?MZ3C?ms|AOAe7Uxpk;S{@#O zW(ef_m({e`9i<+Ga})9y``jq#v~(P&`JLSz$F=Em`x|_>AoEd(Aj87Z!=vzxd*s0< zHEssI)e&s-wLz9nEgS%HPZ*kBgWonA(7GCUO-4kaRkh%c6LeoKuIvXYbufdBone`& zg9i?-!JE~Zc-jx2^u*DYFau6F_jQZaHE#w|-=r!WgpCr*qDJ6g;__|z6~P1n5~xjb z&ugrxj9hV}K46OxedtrJ0$kD56KL&qbd{kRbDM|wl(ViV*a#ayj0%kqN2dFtt47%7 zvdX_)lYq_4M#+{1O<;8vEEr!gh8bt^ISHS72zb%P-_sXbF7?1%J}FB@_?aC@&iDvT zw!%Zv>`BFj#c71&Tih5zvUaE?^b$-9bQqg$c^PiW{0)XVdvtEw+ETHPh>wVm&}A;T zZ7F>TUpl%*ol;>ze8cD%C)%?5Ekrpu@H{(McS|LobRr!+%SZI0vwaJT3jNv)&Pt4ka3_cgC=ymcBB3p_D~ zgs>3<(xA~s@j#)i#?~6^ZLJ5bO3_xTerm0)-}29I=gqP6=J($0%)EJdVG*3R2&xGF zzETF;sMUmqLY!uyA573}904XxIl(Kt&JgD{M03_ULNcurT!mZK&CWOu8|W>rUqUneo9i9;%6iuA3GO?qjLA_@+|w#zy2EfzLoC|Y z+&BDuEs;l`@!T?~kzq(IhNOy4|L>_{wXCs18!z$Z1Ll2SU)2v3d4eh_RX%yZoYoLd z3a08X#j7|8;%gOVCw3rCKVY5==+oZo6-7q1>RjXMPHG2X1I zfzs`xbk`|W45=dS8Lf{3mT}=cJtb%=T&K4O(azzd(s-Y#C?93@3eM(umsK!?Q|Z@z zeSMCgzCI~C>95zcCUmn~r;^!IM3s2UbwnxcCRqSpbgCo_s_7ERdbmaZDVggww}i39 zZ_!lM6F(Qzvqs;5eCyCKXK>r3WcrR#N@1af_>2w@?*qm191X#chQ7n} z;1+|9n5k|~TJ}v}MPqS^LH417t{ocyYWn!t(eMNPaBMzbN&IC*vDS6t{J=?D%&?#R z7+oYC4;;EfS_&C-T*TR7bkTsPm10syyt-l_-pHBmxl(G9yjIp|9Ul1|IHUN%HKkf< zEJHtwT8QUW&@-cFfG?dNy#m(LccMc^oGWAYsmwUmS3{8uRl#UT;z$`&MOAEL-65s@ z$7|sNeR8}3R$5m~Fz|+w70f+)BG%kAbO(8w{^H9+r5aN9<|kS-ZBYEddgic-a4usm zQWXc3#Ll{=xFx3B6nWiJJ3*xs82`;-5=2SknXwURkRgcv8 zadwf0_;8i3jt?6HkC=Uwd>_q6Y{05=it34|M@-U%?%R|+hEfsAM~p#5kdGJ{%ZTZ| z`1@WFzcWITs{Dbf6jHj1E-6t>-<`7_zw?%kog0ond`sud9X3Y)j_I`+E}r`H_KRO+ zMJn06cg#+?{+n8bhwSP*<}_7V^S>v&OeOpGEq!7xf%m?p@6GLlan=h7rvZPVrxl6g zVZ8NJ;>t)iaeVjkaTr633p7w~Jz7vG0-kk(hVOzutFX?{CVE4HRcWcm@xNYMgN|mo z0Bx;2E%w6yd_=eZm=0^{!B_U<_YBq}S59%bbt4qjbk4OR*3z468o;BMSd}-#&Ja$w z+@FHKxoUlKUn_K}j*4wpsD%52OKH^$tLVt;uE2d6m@`HyupY5(eW5j_zm4zO`wNps zgxqI`bgRqTR=|I&u-e`&;dnIv!eldUld|m_99$xS_WRK3{>MFrbRoiiVMs2KWqtBt z6ok{^e zTRJhU-TtwE_h^A#*dA=>I-EkQJPl6d20QNX46WiCIOOWy0i{Fa($L;^=p`@Pz1syW ziTC!B=N&PjPcS;U@QzX-=|wFYdDos&EsAW)6p~P=>6-VS&3vVS1?)JKV`g@qGpI@P{mC_hP2M zKMidkhe&@|4#DWIKTP#V{QNB~Nv)uX)C&F9C^T{e=y)MS5j_%pI|63lE7l=i03<^- z`YZr~XYMUg&mk#9%c)~5Jfe}=qcn*q#W0+Y^J!PIEvidw*_vE1Md;fjR?F-CuIwk< zt8f%Zl(brD40Qy+O8(wSm1yH+BKc1 zL;Svv=@Guq%qDi-F%WMrGek&DGl7wvL_je^EJS~cAuILNV@6%0T{l8?T&NhHO|Yz) zrx0c*G)3i-<>xxjD=Y!6v6mH=1=@v|=?O1s8BwXk`}t^1auK~gxkw59qtH5*_9~=? z(QheIE!R$;`9iU|P#=WO1j1YxhIjOJP^Ef) zfa#%QY&HUhK?>jdnf4%U)gGlgwP(w}r!xX?+mmFfq+FHcBj(^6t7} z)Wy_(y__<#(89t|7CO{&g|SOrRz2rm9_2_CZ$LpoFcV+C1Z4!lY<%%j)EWc}3og}K zDmgil7_2|Q5>q|*z{!4BBT-|xClt$6WT-Jxf1UDgZl?V7`zU8^O>M5qQo+eVVO~Wi z9j?a28pFf?DMnZ-P^73x>1vGC4ET=_1QqR9U~P#*itpB(Rjc#;{Rjna5IP?3P59 zM3$;%*DSJp!czNd480=Dd{miIf3(K%Tx40m3OiVfUY2B*>S7IgSr)R?CDx#qB?aZl zsYNJ|J&5g~ey_UA8mCuKjqb*uSjx+?h-OY2UJ5NTc8^EUXCV- zoh`O1GD=-aC9+NlR?Ddr77u0diUB7z{jRXA&^Kz<)*5z-)MfMubW?we+MvI!bT)3$ zY7~}K{TbCR)?70~$@QmMX&L${1fmLdS5H9}>bxfDO@|Hk~THs7#mMs$UF+9gTsg-{b-^{GkTDpDjb#3ONOf0^!RfWMDwy1Jka~t zMuoyPU<*C^Ab}{n|0;?Y4Kae?Pk*ES)=n9V@S`1Z;fkxPf#*T#{5P;&x!dHP+3e2okfSFO?wl&E|sqB1m z^O_?+J=Y|u4wNULl-bC8983W&`g9zuaNWK}q51PW4@aMthmMVdDAMtHPF z*C^j-rZxDE?9k`9qAV#a#W&4ET~b&zi+Yb@HkZ^Ix}?S!Zr$wMlV+{)G}k=EgE-u! zwRj~IDUH*3-SwKYJaZLSUsJd7s3NIU{YQhdRU5Fav3bTev^D}lzzHcMV7Oq!`U-07 zKXLO>TLkzIUNK8W`}*@q4LR8CiGMejQ`fzHIC>BPzS2*3tEm>+q2rd#i)kryOhmIk zB{voS-o$)MwEK5H8^BeAJO5y@xZS<;MH3no2@gE>oMh^TY<|=zaB1o6wIJ^(h!8(v z$a`%j-ocwD;vi{i#%EL>5t~Mt5=>bp%2a0ZLm5#JD9UqY-(kU=Z#i9J$CQ7TIU~f1 zeM?QJ$a$orN1J96NJz}VVOv%Oo+dV+t5FcbQ;6Tth3MZXm{G7_TyRs@jA5%-5TfOS z%38;=(Uqj#A<413yI-iBdxn|{RXV#?YSw1G=D zg-IUkSfS)A42wWPo_AY41clUm?Jmz^Y6)v)Q@8e}U{ULlYRi5L#{P`#*isibNzUy| z&HB-HHX4!u-yUjno51=U?=hq3i7*as+tMe(Dtv?}vEQrce-6&@oWDZ+BS9O(xnWM2Pj@b)W zpPibY=1F?uN5fDkdNmE^7U=W*pEdTjW0=Pg7P^SZw1PPSdw$6mDILkhr z1#6v$+W?Y-UC3!DB^IW67;Mc4Omg{t&VS?F$*<6XSnz>8=y)uI`Hkka(Y!QceYUv> zjA9o$`I3qy3}})+dLIix1-g^1UDE7^4qf;9pA>>q&5i$e=ajCa<+S%1T}S-cA)UI8 z9V{fA^F6QYC_Mki`3tpbl`8C;58tSjiuK;TRg10>Q%)9X--EH?DnHyGZh1OX8usE5v6UJ;Gh@#k2BC4Aq9rj5|U#m5Ld)%@vx`#XOzM+8BLTvrCw_@$Dju(pa31p zSOoKjt(jQd=-?#nysE?j!%lNPeHnV8sVi-QO`Z<%x z{+z20sk!7GiKlZ_OB#FHA^l?TbvkynOPtLvzjHB^xuz1ut>TgCFeQJNJlsD_LE7#E4-&1sE@fZPEYptW-4T3_*{V!0V)`oChb{uQL~~ zGf%HGy*HTuZZNiCbxOAPuiJC%c(&N-R{F(sJHehu4p-Wzuy(LG=aTU6mVi3*j$5WT z5@A0_+_EXwVPuSK>r)-Ke~`)UljYpqLp@Fi%r_d9q73XuHk<@MCVrmJvDfzJ9&pIyWFoR|D@IIq)YY`ciktR zzHSiX5A?}1m^plUheX0Ea~TFk+hq7*R(4DsaL^7w#&Y-ioK+aM1iYe z8$KZeq1BK|NMx1e-|5oSEkeF^eTgLwsCJ*wjS z`T0ry`>43s!^YEsM~8}};uv;}$i7?VlO@Jl4463i(fc$5|6~vLK+s(of^{ZzR|@eb z*!qDQieX~bh?nH!N1^Myk01S9Tn5H87nd~Fj88_7qG!OWPe!kzH{0abBc$_-bP|lc z4`xd^YIH9R;=ziDHBb<|_c7BJ4t7Dm$l@n&cL*%I&*t0zWAkO(uV3Nfhl|GabCSBy zcWYp(aQ?4smL~dSjUjV0$t_5PL~Fq}EP0)PMEh$Unda$ajMl8`CjUx65~u2u{hUnl zW+r)jAhiXKk!X?xEm;dR%tybkg;}GQ<_XBUMgJ+fgcpYF6p&4(hM)|i)}!ROtWGYs z;AC@u7_n2?j8eZui`T(geAy;+VjX0{DHNCv2izV!s#?rm<_gZDy#dIc4&mU2-lW5L zh(zHTuoJ)g7#+-j#gKyj$pHTW)^9zG4j-1;ufs`RW{=nmNVcJ0J(5yBbaH6t9c(jW zB$TQ5p3SdxWnUrM7v-!6@vs@9lC}pkZ@2gJJK2$ZrOi>6xQ9~86XitYbi zoKtoZyT9MD(uuT9lcB?zkSJM{_T!x{>D>f-FQU*Q@!- z16FaKc>^?(tL+@z>;0(f6XIoDe7zI9lahmfl48t+Y zwb%J#V`RJc5(Cr=d zH=_j1;s5@AWbW7hC+m7s**PBlv=wH;kH~8ql!zl1RIDytHJnnA{lA88AcvNWomJZC zZ~^im3kKwmPHux%eA^1NJ{KDBI5))K4pM&5F3l;gjkamqp$o^it*Xm|{eFpCOBy+* zKeIcDZi>yjttA?dOov;7Rt&add63T`yw_I!egPeyVG2XKlZicdCp|Vh zMB3c>w#9ov&h@`m&Jd#>87y3~S%UYwSr%!7d@59nUgfhrxU20I1vV}&n}ZG)LnKak zqld*XT9mh?)O2ni*`HXJ*kaj&cCWJyD;Zea3>Wl334`(ZX|^{?xCjt}I?G|~pm`HL zN#znOwJv;Kd3D}JYOU6@Y0ec<>*ly6g7n9a6T1zT`;_qcM+kr)g+`m@W-a@%V)WN`faa$QKu6M0^48Y3=d zbBtanSE)z7&e!^&tu#bJy6qqhi+G%E1p@S8ANb)D{cJw<&`H@3eIsBDtO29f!2*XZJKMSdX6U2PwWX4Ebo`2aol;p6lFA2D=kD=4iLrUTR?% z;Bn#T3mtp^8H6-C_#0n40aY}>2yjKM4Q!ncM^Sp1h6|_LzSP4bp4+l?wI+e|XQSFL z#J0)=Lvat`LbdLFZ`%7b1y9iYg{`m|7>E>XDy|K|Fc3q-0*|8R+PdhaH;e(Fo6#iS z8WpewP8~sNucNCJ(OlTN&ZmZ6ftH(L4Xj3|%rF;^_D9?n*yK7YphlC(nwf=owmmIi zci|6}){dg~*giQ9hducVr<-3yC)@u024-{d$!k!Y9Y*3Vn^C469t%CA%S{W@F{h7g z2MLR`>9&=ZV9H3|?tI(ZUA6;%z)%;DZs({D4VEBHkm^6>{AlZX3+J5NT(@?pSV=ejc^S4}y>Tu_@RFBoZ1|)#?Ee7-?K4*Z diff --git a/docs/Customizer.md b/docs/Customizer.md index 6c7063e0..b1c85f19 100644 --- a/docs/Customizer.md +++ b/docs/Customizer.md @@ -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` From ace8f3144ee599e9f520c9d88d93477536806c94 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 10:38:10 -0600 Subject: [PATCH 6/9] Adding missing rule for Hyrule Castle Tree Pull --- Rules.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Rules.py b/Rules.py index 5368b2f5..ee69a481 100644 --- a/Rules.py +++ b/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)) From d7af3a4a7d3d91283b1c94c1b335239ba3f81967 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 4 Nov 2025 08:17:23 -0700 Subject: [PATCH 7/9] Merge pull request #170 from theclearmouse/patch-3 update tournament winners in telepathy tiles --- Text.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Text.py b/Text.py index 07fb2e4d..9eb6027f 100644 --- a/Text.py +++ b/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" From 411f0bc7bc6752e209fd208f32f18b3235733f75 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 11:12:27 -0600 Subject: [PATCH 8/9] Re-fixed purple chest follower dupe --- Rom.py | 2 +- data/base2current.bps | Bin 137199 -> 137207 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 8e69b142..c0335ec8 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '39c6d90d9aa4711fe3c95d85b1a9b16e' +RANDOMIZERBASEHASH = '2039c11b935d3b81f78810d9f4be19d6' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 2d325e9098601881c2aac9356f06c1ebc87e3f39..d052e425fa889e07974906a91a5ceaa7ae87a070 100644 GIT binary patch delta 116 zcmV-)0E_?cuL$?A2(T&x1o_?uA+s(6S<5M(IC|vSy)X{R4Db@k0MHSy%!07V4Dbdb z?c|`6J4Ar)Z$y)GDS!lnlc>(?312FJPASSalSH$e&kno=2z`K;Z?~_D0TLDgVwb|9 W0X6{yxA~y~?sW)?q8dZ9pzI7P1vB#i delta 108 zcmV-y0F(dsuL$q22(T&x1fStx9cWugrq5$qeuYk3@iyJ4Ar) zZ$y&|Du8-~ld#U~33n=hLMh50lTfpy&kno=e0+eHW4E`90TLDgR+q}50X6{;w*jI7 O?sW*Wos!Ymp2(|MA27NA From f66ec985a99600e2a16c09a0de35257214bb162f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 8 Nov 2025 11:18:34 -0600 Subject: [PATCH 9/9] Version bump 0.6.1.2 --- CHANGELOG.md | 6 ++++++ OverworldShuffle.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61fc68fd..b7435482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/OverworldShuffle.py b/OverworldShuffle.py index ebd067bb..028f1659 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -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 = '-u'