From a1ca99700adebe3ff001aa8bd993f65566c78c1c Mon Sep 17 00:00:00 2001 From: cassidy Date: Thu, 21 Oct 2021 22:36:50 -0400 Subject: [PATCH 01/10] Fix typo in inverted LW portal removal my bad --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 5680714e..2ea49059 100644 --- a/Rom.py +++ b/Rom.py @@ -2307,7 +2307,7 @@ def set_inverted_mode(world, player, rom): write_int16(rom, snes_to_pc(0x02E8F7), 0x01F8) rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof rom.write_byte(snes_to_pc(0x1BC428), 0x00) # remove diggable light world portals - rom.write_byte(snes_to_pc(0x1BC42A), 0x00) + rom.write_byte(snes_to_pc(0x1BC43A), 0x00) rom.write_byte(snes_to_pc(0x1BC590), 0x00) rom.write_byte(snes_to_pc(0x1BC5A1), 0x00) rom.write_byte(snes_to_pc(0x1BC5B1), 0x00) From 62c348fe7c1c5c0242b7c4197fd397e0dbae2b14 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 25 Oct 2021 16:31:36 -0600 Subject: [PATCH 02/10] Capacity upgrade fairy now sells nothing useful (nor could downgrade your sword) --- Rom.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Rom.py b/Rom.py index 2ea49059..ccfa4c43 100644 --- a/Rom.py +++ b/Rom.py @@ -1619,13 +1619,16 @@ def write_custom_shops(rom, world, player): loc_item = ItemFactory(item['item'], player) if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade' and world.difficulty[player] != 'normal'): - continue # skip cap upgrades except in normal/shopsanity - item_id = loc_item.code - price = int16_as_bytes(item['price']) - replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF - replace_price = int16_as_bytes(item['replacement_price']) + # really should be 5A instead of B0 -- surprise!!! + item_id, price, replace, replace_price, item_max = 0xB0, [0, 0], 0xFF, [0, 0], 1 + else: + item_id = loc_item.code + price = int16_as_bytes(item['price']) + replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF + replace_price = int16_as_bytes(item['replacement_price']) + item_max = item['max'] item_player = 0 if item['player'] == player else item['player'] - item_data = [shop_id, item_id] + price + [item['max'], replace] + replace_price + [item_player] + item_data = [shop_id, item_id] + price + [item_max, replace] + replace_price + [item_player] items_data.extend(item_data) rom.write_bytes(0x184800, shop_data) From 3b3fe7f8ca756b412c47da3fd4be1e91f5c0c2bf Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 27 Oct 2021 13:29:07 -0600 Subject: [PATCH 03/10] Bonk fairy (light) standard er restriction --- EntranceShuffle.py | 40 ++++++++++++++++++++++++++++++++++++++-- RELEASENOTES.md | 3 +++ Rules.py | 2 +- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 7e5dcdd3..390cf83d 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -215,6 +215,13 @@ def link_entrances(world, player): if bomb_shop in dw_entrances: dw_entrances.remove(bomb_shop) + # standard mode cannot have Bonk Fairy Light be a connector in case of starting boots + # or boots are in links house, etc. + removed = False + if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in lw_entrances: + lw_entrances.remove('Bonk Fairy (Light)') + removed = True + # place the old man cave's entrance somewhere in the light world random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() @@ -226,6 +233,8 @@ def link_entrances(world, player): # now scramble the rest connect_caves(world, lw_entrances, dw_entrances, caves, player) + if removed: + lw_entrances.append('Bonk Fairy (Light)') # scramble holes scramble_holes(world, player) @@ -395,14 +404,23 @@ def link_entrances(world, player): if bomb_shop in dw_entrances: dw_entrances.remove(bomb_shop) + # standard mode cannot have Bonk Fairy Light be a connector in case of + # starting boots or boots are in links house, etc. + removed = False + if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in lw_entrances: + lw_entrances.remove('Bonk Fairy (Light)') + removed = True + # place the old man cave's entrance somewhere in the light world old_man_entrance = lw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) - # now scramble the rest connect_caves(world, lw_entrances, dw_entrances, caves, player) + if removed: + lw_entrances.append('Bonk Fairy (Light)') + # scramble holes scramble_holes(world, player) @@ -487,16 +505,24 @@ def link_entrances(world, player): connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) entrances.remove(bomb_shop) + # standard mode cannot have Bonk Fairy Light be a connector in case of + # starting boots or boots are in links house, etc. + removed = False + if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in entrances: + entrances.remove('Bonk Fairy (Light)') + removed = True # place the old man cave's entrance somewhere random.shuffle(entrances) old_man_entrance = entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) - # now scramble the rest connect_caves(world, entrances, [], caves, player) + if removed: + entrances.append('Bonk Fairy (Light)') + # scramble holes scramble_holes(world, player) @@ -971,6 +997,13 @@ def link_entrances(world, player): connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) doors.remove(bomb_shop) + # standard mode cannot have Bonk Fairy Light be a connector in case of + # starting boots or boots are in links house, etc. + removed = False + if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in doors: + doors.remove('Bonk Fairy (Light)') + removed = True + # handle remaining caves for cave in caves: if isinstance(cave, str): @@ -980,6 +1013,9 @@ def link_entrances(world, player): connect_exit(world, exit, exit_pool.pop(), player) connect_entrance(world, doors.pop(), exit, player) + if removed: + doors.append('Bonk Fairy (Light)') + # place remaining doors connect_doors(world, doors, door_targets, player) elif world.shuffle[player] == 'insanity_legacy': diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7098107e..2574015a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,9 @@ CLI: ```--bombbag``` # Bug Fixes and Notes. +* 0.5.1.5 + * Fix for hard pool capacity upgrades missing + * Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable * 0.5.1.4 * Revert quadrant glitch fix for baserom * Fix for inverted diff --git a/Rules.py b/Rules.py index 0d657844..cf19fbb1 100644 --- a/Rules.py +++ b/Rules.py @@ -1212,7 +1212,7 @@ def standard_rules(world, player): 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)', 'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', - 'Waterfall of Wishing', 'Hyrule Castle Main Gate', '50 Rupee Cave', + 'Waterfall of Wishing', 'Hyrule Castle Main Gate', '50 Rupee Cave', 'Bonk Fairy (Light)', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']: add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player)) From 4b0c2afa987e3a40ed0c9d52cf068dcbc70ef3b0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 27 Oct 2021 14:30:25 -0600 Subject: [PATCH 04/10] Retro + enemizer fix for arrows under pots --- RELEASENOTES.md | 1 + Rom.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2574015a..54da4dfc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,6 +18,7 @@ CLI: ```--bombbag``` * 0.5.1.5 * Fix for hard pool capacity upgrades missing * Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable + * Bug fix for retro + enemizer and arrows appearing under pots * 0.5.1.4 * Revert quadrant glitch fix for baserom * Fix for inverted diff --git a/Rom.py b/Rom.py index ccfa4c43..eaa78bfe 100644 --- a/Rom.py +++ b/Rom.py @@ -1454,6 +1454,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost rom.write_byte(0x301FC, 0xDA if world.retro[player] else 0xE1) # rupees replace arrows under pots + if enemized: + rom.write_byte(0x1B152e, 0xDA if world.retro[player] else 0xE1) rom.write_byte(0x30052, 0xDB if world.retro[player] else 0xE2) # replace arrows in fish prize from bottle merchant rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows From 3a413140036a9a29d7f6c0ae5e85aeb3ad32c2b0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 29 Oct 2021 11:50:16 -0600 Subject: [PATCH 05/10] Shufflelinks and bombbag added to settings code --- BaseClasses.py | 9 +++++++-- RELEASENOTES.md | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 58530558..0ca14e0b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2396,6 +2396,7 @@ class Spoiler(object): 'weapons': self.world.swords, 'goal': self.world.goal, 'shuffle': self.world.shuffle, + 'shufflelinks': self.world.shufflelinks, 'door_shuffle': self.world.doorShuffle, 'intensity': self.world.intensity, 'item_pool': self.world.difficulty, @@ -2470,6 +2471,7 @@ class Spoiler(object): outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player]) outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player]) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player]) + outfile.write(f"Link's House Shuffled: {'Yes' if self.metadata['shufflelinks'][player] else 'No'}\n") outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player]) outfile.write('Intensity: %s\n' % self.metadata['intensity'][player]) addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else '' @@ -2669,7 +2671,7 @@ access_mode = {"items": 0, "locations": 1, "none": 2} boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3} enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3} -# byte 7: HHHD DP?? (enemy_health, enemy_dmg, potshuffle, ?) +# byte 7: HHHD DPBS (enemy_health, enemy_dmg, potshuffle, bomb logic, shuffle links) e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4} e_dmg = {"default": 0, "shuffled": 1, "random": 2} @@ -2700,7 +2702,8 @@ class Settings(object): | (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0) | (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]), - (e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0)]) + (e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0) + | (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0)]) return base64.b64encode(code, "+-".encode()).decode() @staticmethod @@ -2744,6 +2747,8 @@ class Settings(object): args.enemy_health[p] = r(e_health)[(settings[7] & 0xE0) >> 5] args.enemy_damage[p] = r(e_dmg)[(settings[7] & 0x18) >> 3] args.shufflepots[p] = True if settings[7] & 0x4 else False + args.bombbag[p] = True if settings[7] & 0x2 else False + args.shufflelinks[p] = True if settings[7] & 0x1 else False class KeyRuleType(FastEnum): diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 54da4dfc..75c18e0d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,6 +19,7 @@ CLI: ```--bombbag``` * Fix for hard pool capacity upgrades missing * Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable * Bug fix for retro + enemizer and arrows appearing under pots + * Add bombbag and shufflelinks to settings code * 0.5.1.4 * Revert quadrant glitch fix for baserom * Fix for inverted From 01e4c2bff7eb317b739f12690374a30b8b79b53b Mon Sep 17 00:00:00 2001 From: Catobat <69204835+Catobat@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:58:01 +0100 Subject: [PATCH 06/10] Include bottle refills in spoiler --- BaseClasses.py | 15 +++++++++++++++ ItemList.py | 9 +++++++++ Main.py | 1 + Rom.py | 8 ++------ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 58530558..d7625a33 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -97,6 +97,7 @@ class World(object): set_player_attr('player_names', []) set_player_attr('remote_items', False) set_player_attr('required_medallions', ['Ether', 'Quake']) + set_player_attr('bottle_refills', ['Bottle (Green Potion)', 'Bottle (Green Potion)']) set_player_attr('swamp_patch_required', False) set_player_attr('powder_patch_required', False) set_player_attr('ganon_at_pyramid', True) @@ -2272,6 +2273,7 @@ class Spoiler(object): self.doorTypes = {} self.lobbies = {} self.medallions = {} + self.bottles = {} self.playthrough = {} self.unreachables = [] self.startinventory = [] @@ -2315,6 +2317,15 @@ class Spoiler(object): self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0] self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1] + self.bottles = OrderedDict() + if self.world.players == 1: + self.bottles['Waterfall Bottle'] = self.world.bottle_refills[1][0] + self.bottles['Pyramid Bottle'] = self.world.bottle_refills[1][1] + else: + for player in range(1, self.world.players + 1): + self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0] + self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1] + self.startinventory = list(map(str, self.world.precollected_items)) self.locations = OrderedDict() @@ -2434,6 +2445,7 @@ class Spoiler(object): out.update(self.locations) out['Starting Inventory'] = self.startinventory out['Special'] = self.medallions + out['Bottles'] = self.bottles if self.hashes: out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()} if self.shops: @@ -2519,6 +2531,9 @@ class Spoiler(object): outfile.write('\n\nMedallions:\n') for dungeon, medallion in self.medallions.items(): outfile.write(f'\n{dungeon}: {medallion} Medallion') + outfile.write('\n\nBottle Refills:\n') + for fairy, bottle in self.bottles.items(): + outfile.write(f'\n{fairy}: {bottle}') if self.startinventory: outfile.write('\n\nStarting Inventory:\n\n') outfile.write('\n'.join(self.startinventory)) diff --git a/ItemList.py b/ItemList.py index c0f2f86c..9e0b0411 100644 --- a/ItemList.py +++ b/ItemList.py @@ -370,6 +370,15 @@ def generate_itempool(world, player): tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] world.required_medallions[player] = (mm_medallion, tr_medallion) + # shuffle bottle refills + if world.difficulty[player] in ['hard', 'expert']: + waterfall_bottle = hardbottles[random.randint(0, 5)] + pyramid_bottle = hardbottles[random.randint(0, 5)] + else: + waterfall_bottle = normalbottles[random.randint(0, 6)] + pyramid_bottle = normalbottles[random.randint(0, 6)] + world.bottle_refills[player] = (waterfall_bottle, pyramid_bottle) + set_up_shops(world, player) if world.retro[player]: diff --git a/Main.py b/Main.py index 75fe9242..e13eda0d 100644 --- a/Main.py +++ b/Main.py @@ -364,6 +364,7 @@ def copy_world(world): ret.player_names = copy.deepcopy(world.player_names) ret.remote_items = world.remote_items.copy() ret.required_medallions = world.required_medallions.copy() + ret.bottle_refills = world.bottle_refills.copy() ret.swamp_patch_required = world.swamp_patch_required.copy() ret.ganon_at_pyramid = world.ganon_at_pyramid.copy() ret.powder_patch_required = world.powder_patch_required.copy() diff --git a/Rom.py b/Rom.py index 2ea49059..fad83d84 100644 --- a/Rom.py +++ b/Rom.py @@ -1066,12 +1066,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): ]) # set Fountain bottle exchange items - if world.difficulty[player] in ['hard', 'expert']: - rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)]) - rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)]) - else: - rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)]) - rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)]) + rom.write_byte(0x348FF, ItemFactory(world.bottle_refills[player][0], player).code) + rom.write_byte(0x3493B, ItemFactory(world.bottle_refills[player][1], player).code) #enable Fat Fairy Chests rom.write_bytes(0x1FC16, [0xB1, 0xC6, 0xF9, 0xC9, 0xC6, 0xF9]) From 5bac1ba5cc10126a496db1a597b6a2e578f4903c Mon Sep 17 00:00:00 2001 From: Catobat <69204835+Catobat@users.noreply.github.com> Date: Mon, 1 Nov 2021 21:04:54 +0100 Subject: [PATCH 07/10] Add support for Subweights --- Mystery.py | 13 +++++++- mystery_example_subweights.yml | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 mystery_example_subweights.yml diff --git a/Mystery.py b/Mystery.py index 73644500..d3d05fa0 100644 --- a/Mystery.py +++ b/Mystery.py @@ -99,7 +99,8 @@ def get_weights(path): raise Exception(f'Failed to read weights file: {e}') def roll_settings(weights): - def get_choice(option, root=weights): + def get_choice(option, root=None): + root = weights if root is None else root if option not in root: return None if type(root[option]) is not dict: @@ -114,6 +115,16 @@ def roll_settings(weights): return default return choice + while True: + subweights = weights.get('subweights', {}) + if len(subweights) == 0: + break + chances = ({k: int(v['chance']) for (k, v) in subweights.items()}) + subweight_name = random.choices(list(chances.keys()), weights=list(chances.values()))[0] + subweights = weights.get('subweights', {}).get(subweight_name, {}).get('weights', {}) + subweights['subweights'] = subweights.get('subweights', {}) + weights = {**weights, **subweights} + ret = argparse.Namespace() glitches_required = get_choice('glitches_required') diff --git a/mystery_example_subweights.yml b/mystery_example_subweights.yml new file mode 100644 index 00000000..1ec43cac --- /dev/null +++ b/mystery_example_subweights.yml @@ -0,0 +1,57 @@ + description: Example for subweights + glitches_required: none + world_state: open + goals: ganon + weapons: randomized + entrance_shuffle: none + intensity: 3 + subweights: + vanilla: + chance: 25 + weights: + door_shuffle: vanilla + keydropshuffle: + on: 40 + off: 60 + basic: + chance: 25 + weights: + door_shuffle: basic + keydropshuffle: + on: 70 + off: 30 + crossed: + chance: 25 + weights: + door_shuffle: crossed + keydropshuffle: + on: 90 + off: 10 + chaos: + chance: 25 + weights: + door_shuffle: crossed + entrance_shuffle: + none: 30 + crossed: 70 + keydropshuffle: + on: 90 + off: 10 + shopsanity: + on: 50 + off: 50 + bombbag: + on: 25 + off: 75 + subweights: + normal: + chance: 40 + weights: {} + swordless: + chance: 20 + weights: + weapons: swordless + keysanity: + chance: 40 + weights: + dungeon_items: full From 59dd62e3ce2aae97e8c017991e293fb9e85c1dde Mon Sep 17 00:00:00 2001 From: Catobat <69204835+Catobat@users.noreply.github.com> Date: Mon, 1 Nov 2021 21:24:54 +0100 Subject: [PATCH 08/10] Fix some settings being mandatory in weight files --- Mystery.py | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/Mystery.py b/Mystery.py index d3d05fa0..9cf0e729 100644 --- a/Mystery.py +++ b/Mystery.py @@ -128,10 +128,11 @@ def roll_settings(weights): ret = argparse.Namespace() glitches_required = get_choice('glitches_required') - if glitches_required not in ['none', 'no_logic']: - print("Only NMG and No Logic supported") - glitches_required = 'none' - ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required] + if glitches_required is not None: + if glitches_required not in ['none', 'no_logic']: + print("Only NMG and No Logic supported") + glitches_required = 'none' + ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required] item_placement = get_choice('item_placement') # not supported in ER @@ -163,26 +164,27 @@ def roll_settings(weights): ret.standardize_palettes = get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize' goal = get_choice('goals') - ret.goal = {'ganon': 'ganon', - 'fast_ganon': 'crystals', - 'dungeons': 'dungeons', - 'pedestal': 'pedestal', - 'triforce-hunt': 'triforcehunt' - }[goal] + if goal is not None: + ret.goal = {'ganon': 'ganon', + 'fast_ganon': 'crystals', + 'dungeons': 'dungeons', + 'pedestal': 'pedestal', + 'triforce-hunt': 'triforcehunt' + }[goal] ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False ret.crystals_gt = get_choice('tower_open') ret.crystals_ganon = get_choice('ganon_open') - - if ret.goal == 'triforcehunt': - goal_min = get_choice_default('triforce_goal_min', default=20) - goal_max = get_choice_default('triforce_goal_max', default=20) - pool_min = get_choice_default('triforce_pool_min', default=30) - pool_max = get_choice_default('triforce_pool_max', default=30) - ret.triforce_goal = random.randint(int(goal_min), int(goal_max)) - min_diff = get_choice_default('triforce_min_difference', default=10) - ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max)) + + goal_min = get_choice_default('triforce_goal_min', default=20) + goal_max = get_choice_default('triforce_goal_max', default=20) + pool_min = get_choice_default('triforce_pool_min', default=30) + pool_max = get_choice_default('triforce_pool_max', default=30) + ret.triforce_goal = random.randint(int(goal_min), int(goal_max)) + min_diff = get_choice_default('triforce_min_difference', default=10) + ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max)) + ret.mode = get_choice('world_state') if ret.mode == 'retro': ret.mode = 'open' @@ -193,11 +195,13 @@ def roll_settings(weights): ret.hints = get_choice('hints') == 'on' - ret.swords = {'randomized': 'random', - 'assured': 'assured', - 'vanilla': 'vanilla', - 'swordless': 'swordless' - }[get_choice('weapons')] + swords = get_choice('weapons') + if swords is not None: + ret.swords = {'randomized': 'random', + 'assured': 'assured', + 'vanilla': 'vanilla', + 'swordless': 'swordless' + }[swords] ret.difficulty = get_choice('item_pool') From 1b1292f6b8f5c05ce0a3511dbcad42a872adb50d Mon Sep 17 00:00:00 2001 From: Catobat <69204835+Catobat@users.noreply.github.com> Date: Mon, 1 Nov 2021 23:29:06 +0100 Subject: [PATCH 09/10] ROM fixes --- Rom.py | 2 +- asm/doorrando.asm | 1 + asm/doortables.asm | 2 ++ asm/drhooks.asm | 3 +++ asm/gfx.asm | 10 ++++++++++ asm/normal.asm | 15 ++++++++++----- asm/scroll.asm | 6 +++++- data/base2current.bps | Bin 136114 -> 136201 bytes 8 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Rom.py b/Rom.py index fad83d84..1153da24 100644 --- a/Rom.py +++ b/Rom.py @@ -32,7 +32,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '11daec4f3e1afc96cd044585dfba9df8' +RANDOMIZERBASEHASH = 'bcce69ecfff6c169371afc84193c0402' class JsonRom(object): diff --git a/asm/doorrando.asm b/asm/doorrando.asm index 807095c7..8cceeba0 100644 --- a/asm/doorrando.asm +++ b/asm/doorrando.asm @@ -9,6 +9,7 @@ ; Normal doors use $FE to store the trap door indicator ; Normal doors use $045e to store Y coordinate when transitioning to in-room stairs ; Normal doors use $045f to determine the order in which supertile quadrants are drawn +; Straight stairs use $046d to store X coordinate on animation start ; Spiral doors use $045e to store stair type ; Gfx uses $b1 to for sub-sub-sub-module thing diff --git a/asm/doortables.asm b/asm/doortables.asm index eae16430..90678ca3 100644 --- a/asm/doortables.asm +++ b/asm/doortables.asm @@ -681,6 +681,8 @@ db $00,$07,$20,$20,$07,$07,$07,$07,$07,$20,$20,$07,$20,$20,$20,$20 db $07,$07,$02,$02,$02,$02,$07,$07,$07,$20,$20,$07,$20,$20,$20,$07 ;27f300 +DungeonTilesets: +db $04,$04,$05,$12,$04,$08,$07,$0C,$09,$0B,$05,$0A,$0D,$0E,$06,$06 ; ;org $27ff00 diff --git a/asm/drhooks.asm b/asm/drhooks.asm index 1d3b485b..f067dad4 100644 --- a/asm/drhooks.asm +++ b/asm/drhooks.asm @@ -76,6 +76,9 @@ nop : jsl OverridePaletteHeader org $02817e ; Bank02.asm : 414 (LDA $02811E, X) jsl FixAnimatedTiles +org $0aef43 ; UnderworldMap_RecoverGFX +jsl FixCloseDungeonMap + org $028a06 ; Bank02.asm : 1941 Dungeon_ResetTorchBackgroundAndPlayer JSL FixWallmasterLamp diff --git a/asm/gfx.asm b/asm/gfx.asm index b22fba62..94cb8848 100644 --- a/asm/gfx.asm +++ b/asm/gfx.asm @@ -45,6 +45,16 @@ FixAnimatedTiles: + LDA $02802E, X ; what we wrote over RTL +FixCloseDungeonMap: + LDA.l DRMode : CMP #$02 : BNE .vanilla + LDA $040C : BMI .vanilla + LSR : TAX + LDA.l DungeonTilesets,x + RTL + .vanilla + LDA $7EC20E + RTL + FixWallmasterLamp: ORA $0458 STY $1C : STA $1D : RTL ; what we wrote over diff --git a/asm/normal.asm b/asm/normal.asm index 3bdf9622..13323d88 100644 --- a/asm/normal.asm +++ b/asm/normal.asm @@ -150,15 +150,14 @@ LoadRoomVert: .notEdge lda $01 : and #$03 : cmp #$03 : bne .normal jsr ScrollToInroomStairs + stz $046d bra .end .normal ldy #$01 : jsr ShiftVariablesMainDir jsr PrepScrollToNormal .scroll - lda $01 : and #$40 : pha + lda $01 : and #$40 : sta $046d jsr ScrollX - pla : beq .end - ldy #$00 : jsr ApplyScroll .end plb ; restore db register rts @@ -291,6 +290,11 @@ StraightStairsAdj: stx $0464 : sty $012e ; what we wrote over lda.l DRMode : beq + lda $045e : bne .toInroom + lda $046d : beq .noScroll + sta $22 + ldy #$00 : jsr ApplyScroll + stz $046d + .noScroll jsr GetTileAttribute : tax lda $11 : cmp #$12 : beq .goingNorth lda $a2 : cmp #$51 : bne ++ @@ -338,9 +342,10 @@ db $d0, $f6, $10, $1a, $f0, $00 StraightStairsFix: { + pha lda.l DRMode : bne + - !add $20 : sta $20 ;what we wrote over - + rtl + pla : !add $20 : sta $20 : rtl ;what we wrote over + + pla : rtl } StraightStairLayerFix: diff --git a/asm/scroll.asm b/asm/scroll.asm index f66918c8..435b5c98 100644 --- a/asm/scroll.asm +++ b/asm/scroll.asm @@ -168,7 +168,11 @@ ScrollX: ;change the X offset variables pla : sta $00 sep #$30 - lda $04 : sta $22 + lda $04 : ldx $046d : bne .straight + sta $22 : bra + + .straight + sta $046d ; set X position later + + lda $00 : sta $23 : sta $0609 : sta $060d lda $01 : sta $a9 lda $0e : asl : ora $ac : sta $ac diff --git a/data/base2current.bps b/data/base2current.bps index a0d85faa0dc758ea3e0d3f04179760dc1e401269..7d88566a4996c6ae95d21586ca043435130550ef 100644 GIT binary patch delta 2460 zcmW+$dsGuw9-cdyJO~Mdpcr0t7}RNeW~mRXtfkaNMAZ7MMWpE#TiWQctMnnYa3^8F z2q78nh5=GccuX}=1dLm?iv?}Fg`Tp;-PInqwY9QPiLX&X)YIw?{bzoU@80izXYTKQ z#}2FPM^s%IU`bEq3q?i!Zt89|9SWI~p_{6zrXfJhY0pD0L>V0P*7AC4xr6QkX48=U z{8z0cyzhsV5LRYnugLFJum{n8yNmIM`{6^y-tZ7phaheFiuYP!JYpiZ7^ z)xh6jB1Bj@>Tx9-P1C^aSZin7>-(l zG_p+aZ?sWXF=JIVy~58?tz~rR2YX8eZE|x|v{d}cQ46GiNy?r@k7WzMlE8doF8~(; z&GLmf_!@nuya+C#qpG|JT{ZosKs`J8tEk!40YljH0E`53)h_|CD{w4)9thJh4(do1 zjXeom)qEoZFQX1!I%p2u(%pC(Yz%zAbTa^E^oM0Rz#J%9_VHi9jlj%~hf7M&GXgds z3#!R-XJOY$)(2lG6nuy1f4&M8Ebha|-xvWHp*XEo(f34D3V^h7P%eej92sweGIEJc z!qcKmR4-bELO*Ufsx%1?#kd&?zd zJ1YdAF>)X>s4lotaMeGZ{h1vyOI#*C9A%BH#ODLVjbwu~^ux#+b5gDzq8aP-u)DA$ zp9mIo>>@-pOQD73m!vbv%eFQlYCtb02+afQgSxY%-{95Cf7QZCr1!RshOt+$pjz-r zDB)iW!&`6lVIsKJJ8Gk|_^kfz=!0M4OMkJA&4qc1XK*uiKh6-78oaNI6S1sL9MVB$ zA&Q+~Eroo_oL`${!YxZL^#!n?Y=>KnC=6<{Jju{OuxkrzR)#~j<*1}lR@3LVe3DOy zVS|&E^Iz5bpd)AsxM{!WG5Wpy&^WWN;Qr3?(F$&WnPc;@MH~{Lx)RkYFh&lg2~9*knhT z!l8B6qIID+Rcbq*#AWjvj`Vs?$I`9G35;vkNC_YJsh55*g>&W5FbfCIGJsfP#vq32$nhl{2M8y zfLvNh-aSzKs2y40@JIQ?cj8;tBj!fNVl~H7 z{xKLXy`oGh{YR32f+bsW<@F4X_SB%T*Kugi`TIeSE(eE>oP$OMO84@+(Zd@lAOp?4 z83&f3l{b?$dk)kKc?o1ij=ZZ-keV--j)P^^SHu5|g zy-6vwEsV^QhoWw!l*XDL49Z#gsUERSkT)6>adC}timRlV$1@(Veoz~^Z z**wOGFjzCw$618#;QE%#QawDhR$y~~LNhyWZ)$D`=R03c)I(>Jn;fVb>9%-gv~mUA zBV88n+k_tSGF>!eqyBq%Cmsz2qOA~uN4@h+5cj@Y^|Pi!LztA=ZN1n%+R_ECpU9akK@Q6oT+Am`S;ehldbfc-n7KJ z!OJcwkL5_cYZ4Y@tglS*#ne-$2_p=K2?Prye}r0r@a`{nliOU9OL{kT2UeYIoLpd# z79dR%E2_PVe3Tm+Bm*>~(e^DTFTCXvWRm%r$$c(?ByBEZIz|qn>N|-U3rsU5dkA}5Q_^+sgN@1I*pOokyD-DTff8(kFM$f`bBrYBh`1 zWlK`wWpf{fiBCIuCYiT7&LSAgAE^Hh!APEuG0@CFB);V*3WW z=VP?#MSDF4f^_Hdd+jFvr)h7ZHDfTUk#{!pMZ4gc)~`!Wn?$k^{W#`|_Sd5QjG)AV zvK)V{KkDhu^l-$CPbZVyw4wBmZu&^CS)}V%#Ps+3$7e{fMY~vRQjzbYzVVrQMXKmo zW3xy0?yt?-`H4e8cA|;#jObt7o_BN&oJ3i~qYTJs__tBc{keF29V)(`2!>JJ{k5~{ z&%48=VeT3$iStUIN4w!mCc8b?hjkueu*09$7n@n1qnhAT%t$veH{unAhdpbWrVV<3%J;$>hW@}qs#7P{2!--AQ}Jw delta 2460 zcmX9;dsGuw8lRa=9)!dY&=@f4I8eu^RF)P=wXI0CrHGAS0`q{=g7`d0z+gYVRNPFqVxIZNiimoSs==PHUn zC&+4_*<;-R>>rj>xRoVqpbOI#6qivrV9C7CTR!n%z3s9yJHb7q&?&go5=g$E^=6m=UMWRjk?(Ubi*jT zsG?ou3s5b28SR1_B;`rE8frz*MIJ4qM*eHJms2So7by`pzq!cSVn9S<^WlVK5sHTv z;d@cEDby@|7DJE1Z{%O0Ct$lGZ^|{6`h+LtlQ%_`tdM5-qj}o~qAQeNUi#51x8B~vI!12W>Z^0x10 zfyPotVaFCG05&M#n*`eHxGY;!5fomsx@S z|Ddd-j@fy{QAQ&$-pU;)NB~}hZ))vE{9Ms)nq@G30apyaAIMGIVddrku@~0YsBPh} zCZf#0k$*EdoB4scWfqC0a7MU(@D(go3XctDqnYr>!PW0ln_~e?%8dmBZ=BhNM>Y~y zZLPdE94i?3rtrE*%q1eI_vaq|RShPQo?A8w#NR+7NcyKX@Zn@u!23O z_DA2I2EvC?r!*LY0^BtGHex1L9tk5tQdlk+!lnLbVWrWN zR{Eyj+NduVxhyJoYB@LDTYO6bWN^udLAO0w3;dhsYXML42$RT0$yl}HC_FH-Td}`_ zQ?t!`SvWMZPz~)cqX8D^y>?=v0?xVfT&V(`0UiYq;+IOkHJg!%8o{zn12RF)$dvkm zR;D$8I?g**Lx!z%9356awd_NFhm3!oO9fA^ap*@?~~IJnHQAl z3kZe?-q9D33<>mb?-6@!@Cj!+mJFu;Srd;WR_Y7j_jgpf*;V2xf{}}ApIt?KQ&IG! z4eq4fcMYf%&b;fuQg*?!cQY4%?PSQ{D2NuPEzZS||C$pVXNYFA)JtQ~wSj zbCGq;v8A!#_!^$sd>y9wALP61qq+8{X2t?Xqlc)i9z199CAGNn&kc51{LkaDfXmgy z0Y>}nNfQeuly12dD;P#QqdX}f?w!62HYdO4Xi^*(0$OWS zTHrpI47_IJ1Zo;UxR)-}JoA7%{MSEz68el2$SEEn;vs|aa&aSO;@cTD$f+(LuAOMW zh!-G(@G5SlVcd(G1xXM=xy3uu#;!8>%@lJt(#J@Kx)1g-^2rp3MP0PQVLc<|apG&| zP`9^_iH1$A%52p5-kbvqACfZDoaumxgU*M9atmX-|zybpm=$sl#vB2#e9+>0Q>JO@W7a#q#>K|ELCZGk{T6u}vztBfB* zZmTD#Km<#B;%EtRh8sVy&h9$_-_5mq5i~-zzxZ=o3U~c4h0rkyv~O{aCT`z5;NzB0 zOS)48;w?x#a=x~|78cXI9EnJB_SXir^V&Bn>$`$6^H`;hovLGJ>Db#khhGYkrZ-_o z5JB{%_m5KgQl*DF)ngVY?+RUifACR~7@Mgyp;1AsgxJ_qu|&GyTWzyzdWvha-agIB zh+_EMSZ3ULk53a*?-GBuoP(KwR&h#LGqw;L;^4)xndnFG+p#tCKj=Oeo!#2gFnE+dZb2;1LYvT*29FqE&ODPAACG4TOg6W~^@nm^y zHdsn@9=N^bu(wma;!bVehv!4z{_*})b*b%=j^4V9-lYO-c6V-%L*t&7N9A?WD?Tmk lJhU8jPs8H&a_UW}0b9DJbHOI`tBZ;+eo07s)1UnQ{{Ucq8Pxy) From 90f7016f497ca0d74a8937bc201c19d228fb082f Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 2 Nov 2021 15:25:34 -0600 Subject: [PATCH 10/10] Update release notes and version Remove tileset fix (I like the other more visually) --- Main.py | 2 +- RELEASENOTES.md | 8 +++++++- Rom.py | 2 +- asm/drhooks.asm | 3 --- asm/gfx.asm | 10 ---------- data/base2current.bps | Bin 136201 -> 136415 bytes 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Main.py b/Main.py index e13eda0d..453e33e8 100644 --- a/Main.py +++ b/Main.py @@ -29,7 +29,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.5.1.4-u' +__version__ = '0.5.1.5-u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 75c18e0d..c1a7a62c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,7 +19,13 @@ CLI: ```--bombbag``` * Fix for hard pool capacity upgrades missing * Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable * Bug fix for retro + enemizer and arrows appearing under pots - * Add bombbag and shufflelinks to settings code + * Added bombbag and shufflelinks to settings code + * Catobat fixes: + * Fairy refills in spoiler + * Subweights support in mystery + * More defaults for mystery weights + * Less camera jank for straight stair transitions + * Bug with Straight stairs with vanilla doors where Link's walking animation stopped early is fixed * 0.5.1.4 * Revert quadrant glitch fix for baserom * Fix for inverted diff --git a/Rom.py b/Rom.py index f0484b02..f3b3cb68 100644 --- a/Rom.py +++ b/Rom.py @@ -32,7 +32,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'bcce69ecfff6c169371afc84193c0402' +RANDOMIZERBASEHASH = '2cfa164d4b66a15406f53ca4750ef59a' class JsonRom(object): diff --git a/asm/drhooks.asm b/asm/drhooks.asm index f067dad4..1d3b485b 100644 --- a/asm/drhooks.asm +++ b/asm/drhooks.asm @@ -76,9 +76,6 @@ nop : jsl OverridePaletteHeader org $02817e ; Bank02.asm : 414 (LDA $02811E, X) jsl FixAnimatedTiles -org $0aef43 ; UnderworldMap_RecoverGFX -jsl FixCloseDungeonMap - org $028a06 ; Bank02.asm : 1941 Dungeon_ResetTorchBackgroundAndPlayer JSL FixWallmasterLamp diff --git a/asm/gfx.asm b/asm/gfx.asm index 94cb8848..b22fba62 100644 --- a/asm/gfx.asm +++ b/asm/gfx.asm @@ -45,16 +45,6 @@ FixAnimatedTiles: + LDA $02802E, X ; what we wrote over RTL -FixCloseDungeonMap: - LDA.l DRMode : CMP #$02 : BNE .vanilla - LDA $040C : BMI .vanilla - LSR : TAX - LDA.l DungeonTilesets,x - RTL - .vanilla - LDA $7EC20E - RTL - FixWallmasterLamp: ORA $0458 STY $1C : STA $1D : RTL ; what we wrote over diff --git a/data/base2current.bps b/data/base2current.bps index 7d88566a4996c6ae95d21586ca043435130550ef..328d0f8196c9f52c4e8bb095e2ff83a9157dc2ea 100644 GIT binary patch delta 9649 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

$LjCK)|i1Hr6i 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 QYsU+Z_FC}AysV!81Gzb&;{X5v delta 9515 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