From fcaaab30a4a3c7569920ab9ea6a334224ab49186 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 30 Apr 2025 06:41:43 -0500 Subject: [PATCH] Initial Follower Shuffle Implementation --- BaseClasses.py | 25 +++-- CLI.py | 3 +- Doors.py | 2 +- DungeonGenerator.py | 6 +- InitialSram.py | 23 +++- ItemList.py | 104 +++++++++++++++--- Items.py | 3 + Main.py | 14 ++- OWEdges.py | 2 + OverworldShuffle.py | 1 + README.md | 27 +++++ Regions.py | 16 ++- Rom.py | 47 ++++++-- Rules.py | 8 +- data/base2current.bps | Bin 133998 -> 136345 bytes docs/Customizer.md | 10 ++ docs/customizer_example.yaml | 1 + resources/app/cli/args.json | 4 + resources/app/cli/lang/en.json | 3 + resources/app/gui/lang/en.json | 5 +- resources/app/gui/randomize/item/widgets.json | 4 + source/classes/CustomSettings.py | 2 + source/classes/constants.py | 1 + source/dungeon/DungeonStitcher.py | 3 +- source/dungeon/RoomHeader.py | 1 + source/enemizer/Bossmizer.py | 5 +- source/enemizer/Enemizer.py | 17 ++- source/enemizer/SpriteSheets.py | 28 +++++ source/tools/MysteryUtils.py | 1 + 29 files changed, 316 insertions(+), 50 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4691ef4b..a82f3093 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1662,18 +1662,21 @@ class Entrance(object): self.temp_path = [] def can_reach(self, state): - # Destination Pickup OW Only No Ledges Can S&Q Allow Mirror - multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False, True), - 'Missing Smith': ('Frog', True, False, True, True), - 'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True), - 'Old Man Drop Off': ('Lost Old Man', True, False, False, False), - 'Revealing Light': ('Suspicious Maiden', False, False, False, False) + # Destination Pickup OW Only No Ledges Can S&Q Allow Mirror + multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False, True), + 'Missing Smith': ('Frog', True, False, True, True), + 'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True), + 'Dark Palace Button':('Kiki', True, False, False, False), + 'Old Man Drop Off': ('Lost Old Man', True, False, False, False), + 'Revealing Light': ('Suspicious Maiden', False, False, False, False) } if self.name in multi_step_locations: if self not in state.path: world = self.parent_region.world multi_step_loc = multi_step_locations[self.name] + if world.shuffle_followers[self.player]: + multi_step_loc = (multi_step_loc[0], self.name == 'Pyramid Crack', multi_step_loc[2], True, True) step_location = world.get_location(multi_step_loc[0], self.player) if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_loc[1], multi_step_loc[2], multi_step_loc[3], multi_step_loc[4]) and self.access_rule(state): if not self in state.path: @@ -2952,7 +2955,7 @@ class Spoiler(object): self.settings = {} - self.suppress_spoiler_locations = ['Big Bomb', 'Frog', 'Dark Blacksmith Ruins', 'Middle Aged Man', 'Lost Old Man', 'Old Man Drop Off'] + self.suppress_spoiler_locations = ['Lost Old Man', 'Big Bomb', 'Frog', 'Dark Blacksmith Ruins', 'Middle Aged Man', 'Kiki'] def set_overworld(self, entrance, exit, direction, player): if self.world.players == 1: @@ -3018,6 +3021,7 @@ class Spoiler(object): 'ow_whirlpool': self.world.owWhirlpoolShuffle, 'ow_fluteshuffle': self.world.owFluteShuffle, 'bonk_drops': self.world.shuffle_bonk_drops, + 'shuffle_followers': self.world.shuffle_followers, 'shuffle': self.world.shuffle, 'shuffleganon': self.world.shuffle_ganon, 'shufflelinks': self.world.shufflelinks, @@ -3259,6 +3263,7 @@ class Spoiler(object): outfile.write('\n') outfile.write('Shopsanity:'.ljust(line_width) + '%s\n' % yn(self.metadata['shopsanity'][player])) outfile.write('Bonk Drops:'.ljust(line_width) + '%s\n' % yn(self.metadata['bonk_drops'][player])) + outfile.write('Followers:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffle_followers'][player])) outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player]) outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player])) outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['dropshuffle'][player]) @@ -3670,7 +3675,7 @@ boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique or_mode = {"vanilla": 0, "parallel": 1, "full": 2} orcrossed_mode = {"none": 0, "polar": 1, "grouped": 2, "unrestricted": 4} -# byte 12: KMB? FF?? (keep similar, mixed/tile flip, bonk drops, flute spots) +# byte 12: KMBQ FF?? (keep similar, mixed/tile flip, bonk drops, follower quests, flute spots) flutespot_mode = {"vanilla": 0, "balanced": 1, "random": 2} # byte 13: FBBB TTPP (flute_mode, bow_mode, take_any, prize shuffle) @@ -3737,7 +3742,8 @@ class Settings(object): | (0x08 if w.owWhirlpoolShuffle[p] else 0) | orcrossed_mode[w.owCrossed[p]], (0x80 if w.owKeepSimilar[p] else 0) | (0x40 if w.owMixed[p] else 0) - | (0x20 if w.shuffle_bonk_drops[p] else 0) | (flutespot_mode[w.owFluteShuffle[p]] << 4), + | (0x20 if w.shuffle_bonk_drops[p] else 0) | (0x10 if w.shuffle_followers[p] else 0) + | (flutespot_mode[w.owFluteShuffle[p]] << 4), (flute_mode[w.flute_mode[p]] << 7 | bow_mode[w.bow_mode[p]] << 4 | take_any_mode[w.take_any[p]] << 2 | prizeshuffle_mode[w.prizeshuffle[p]]), @@ -3822,6 +3828,7 @@ class Settings(object): args.ow_keepsimilar[p] = True if settings[12] & 0x80 else False args.ow_mixed[p] = True if settings[12] & 0x40 else False args.bonk_drops[p] = True if settings[12] & 0x20 else False + args.shuffle_followers[p] = True if settings[12] & 0x10 else False args.ow_fluteshuffle[p] = r(flutespot_mode)[(settings[12] & 0x0C) >> 2] if len(settings) > 13: diff --git a/CLI.py b/CLI.py index 8fc7263e..c6ae7d9f 100644 --- a/CLI.py +++ b/CLI.py @@ -132,7 +132,7 @@ def parse_cli(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle', 'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle', - 'flute_mode', 'bow_mode', 'take_any', 'boots_hint', + 'flute_mode', 'bow_mode', 'take_any', 'boots_hint', 'shuffle_followers', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'prizeshuffle', 'startinventory', 'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', @@ -200,6 +200,7 @@ def parse_settings(): "ow_mixed": False, "ow_whirlpool": False, "ow_fluteshuffle": "vanilla", + "shuffle_followers": False, "bonk_drops": False, "shuffle": "vanilla", "shufflelinks": False, diff --git a/Doors.py b/Doors.py index af83f6fc..aa55cf0c 100644 --- a/Doors.py +++ b/Doors.py @@ -1301,7 +1301,7 @@ def create_doors(world, player): world.get_door('Swamp Drain Right Switch', player).event('Swamp Drain') world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain') - if world.mode[player] == 'standard': + if world.mode[player] == 'standard' and 'Zelda Herself' not in [i.name for i in world.precollected_items if i.player == player]: world.get_door('Hyrule Castle Throne Room Tapestry', player).event('Zelda Pickup') world.get_door('Hyrule Castle Tapestry Backwards', player).event('Zelda Pickup') diff --git a/DungeonGenerator.py b/DungeonGenerator.py index f8297aff..d5c65621 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -602,7 +602,8 @@ def determine_paths_for_dungeon(world, player, all_regions, name): paths.append(boss) if 'Thieves Boss' in all_r_names: paths.append('Thieves Boss') - if world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind': + if world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind' \ + and not world.shuffle_followers[player]: paths.append(('Thieves Blind\'s Cell', 'Thieves Boss')) for drop_check in drop_path_checks: if drop_check in all_r_names: @@ -1324,7 +1325,8 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary', 'Hyrule Castle Throne Room']: # need to deliver zelda assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole) - if key == 'Thieves Town' and world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind': + if key == 'Thieves Town' and (world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind' + and not world.shuffle_followers[player]): assign_sector(find_sector("Thieves Blind's Cell", candidate_sectors), current_dungeon, candidate_sectors, global_pole) entrances_map, potentials, connections = connections_tuple diff --git a/InitialSram.py b/InitialSram.py index 0aa25bc2..f360236a 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -128,6 +128,25 @@ class InitialSram: else: self.set_progress_indicator(0x03) + if startingstate.has('Zelda Herself', player): + self._initial_sram_bytes[0x3CC] = 0x01 + elif startingstate.has('Escort Old Man', player): + self._initial_sram_bytes[0x3CC] = 0x04 + elif startingstate.has('Maiden Rescued', player): + self._initial_sram_bytes[0x3CC] = 0x06 + elif startingstate.has('Get Frog', player): + self._initial_sram_bytes[0x3CC] = 0x07 + elif startingstate.has('Sign Vandalized', player): + self._initial_sram_bytes[0x3CC] = 0x09 + elif startingstate.has('Pick Up Kiki', player): + self._initial_sram_bytes[0x3CC] = 0x0A + elif startingstate.has('Pick Up Purple Chest', player): + self._initial_sram_bytes[0x3CC] = 0x0C + elif startingstate.has('Pick Up Big Bomb', player): + self._initial_sram_bytes[0x3CC] = 0x0D + if self._initial_sram_bytes[0x3CC] > 0x01 and world.mode[player] == 'standard': + self._initial_sram_bytes[0x3D3] = 0x80 + for item in world.precollected_items: if item.player != player: continue @@ -138,7 +157,9 @@ class InitialSram: 'Mirror Shield', 'Red Shield', 'Blue Shield', 'Progressive Shield', 'Red Mail', 'Blue Mail', 'Progressive Armor', 'Magic Upgrade (1/4)', 'Magic Upgrade (1/2)', - 'Return Old Man', 'Beat Agahnim 1']: + 'Return Old Man', 'Beat Agahnim 1', 'Zelda Herself', 'Escort Old Man', + 'Maiden Rescued', 'Get Frog', 'Sign Vandalized', 'Pick Up Kiki', + 'Pick Up Purple Chest', 'Pick Up Big Bomb']: continue set_table = {'Book of Mudora': (0x34E, 1), 'Hammer': (0x34B, 1), 'Bug Catching Net': (0x34D, 1), 'Hookshot': (0x342, 1), 'Magic Mirror': (0x353, 2), diff --git a/ItemList.py b/ItemList.py index 45c1df2d..e690d83a 100644 --- a/ItemList.py +++ b/ItemList.py @@ -129,6 +129,40 @@ difficulties = { ), } + +follower_quests = { + 'Zelda Pickup': ['Zelda Herself', 'Zelda Drop Off', 'Zelda Delivered'], + 'Lost Old Man': ['Escort Old Man', 'Old Man Drop Off', 'Return Old Man'], + 'Locksmith': ['Sign Vandalized', None, None], + 'Kiki': ['Pick Up Kiki', 'Kiki Assistance', 'Dark Palace Opened'], + 'Suspicious Maiden': ['Maiden Rescued', 'Revealing Light', 'Maiden Unmasked'], + 'Frog': ['Get Frog', 'Missing Smith', 'Return Smith'], + 'Dark Blacksmith Ruins': ['Pick Up Purple Chest', 'Middle Aged Man', 'Deliver Purple Chest'], + 'Big Bomb': ['Pick Up Big Bomb', 'Pyramid Crack', 'Detonate Big Bomb'], +} + +follower_locations = { + 'Zelda Pickup': 0x1802C0, + 'Lost Old Man': 0x1802C3, + 'Suspicious Maiden': 0x1802C6, + 'Frog': 0x1802C9, + 'Locksmith': 0x1802CC, + 'Kiki': 0x1802CF, + 'Dark Blacksmith Ruins': 0x1802D2, + 'Big Bomb': 0x1802D5, +} + +follower_pickups = { + 'Zelda Herself': 0x01, + 'Escort Old Man': 0x04, + 'Maiden Rescued': 0x06, + 'Get Frog': 0x07, + 'Sign Vandalized': 0x09, + 'Pick Up Kiki': 0x0A, + 'Pick Up Purple Chest': 0x0C, + 'Pick Up Big Bomb': 0x0D, +} + # Translate between Mike's label array and YAML/JSON keys def get_custom_array_key(item): label_switcher = { @@ -194,17 +228,10 @@ def generate_itempool(world, player): if world.timer in ['ohko', 'timed-ohko']: world.can_take_damage = False - def set_event_item(location_name, item_name=None): - location = world.get_location(location_name, player) - if item_name: - world.push_item(location, ItemFactory(item_name, player), False) - location.event = True - location.locked = True - if world.goal[player] in ['pedestal', 'triforcehunt']: - set_event_item('Ganon', 'Nothing') + set_event_item(world, player, 'Ganon', 'Nothing') else: - set_event_item('Ganon', 'Triforce') + set_event_item(world, player, 'Ganon', 'Triforce') if world.goal[player] in ['triforcehunt', 'trinity']: region = world.get_region('Hyrule Castle Courtyard', player) @@ -241,12 +268,17 @@ def generate_itempool(world, player): old_man.skip = True for loc, item in location_events.items(): - if item: - set_event_item(loc, item) - + if loc in follower_quests and world.shuffle_followers[player]: + item = None + set_event_item(world, player, loc, item) + + zelda_pickup, zelda_dropoff = None, None if world.mode[player] == 'standard': - set_event_item('Zelda Pickup', 'Zelda Herself') - set_event_item('Zelda Drop Off', 'Zelda Delivered') + if not world.shuffle_followers[player]: + zelda_pickup = 'Zelda Herself' + zelda_dropoff = 'Zelda Delivered' + set_event_item(world, player, 'Zelda Pickup', zelda_pickup) + set_event_item(world, player, 'Zelda Drop Off', zelda_dropoff) # set up item pool skip_pool_adjustments = False @@ -1642,6 +1674,50 @@ def fill_specific_items(world): world.item_pool_config.verify_target += len(placement['locations']) +def set_event_item(world, player, location_name, item_name=None): + location = world.get_location(location_name, player) + if item_name: + world.push_item(location, ItemFactory(item_name, player), False) + location.event = True + if location_name not in follower_quests or not world.shuffle_followers[player]: + location.locked = True + + +def shuffle_event_items(world, player): + if (world.shuffle_followers[player]): + available_quests = follower_quests.copy() + available_pickups = [quests[0] for quests in available_quests.values()] + + for loc_name in follower_quests.keys(): + loc = world.get_location(loc_name, player) + if loc.item: + set_event_item(world, player, loc_name) + available_quests.pop(loc_name) + available_pickups.remove(loc.item.name) + + + if world.mode[player] == 'standard': + if 'Zelda Herself' in available_pickups: + zelda_pickup = available_quests.pop('Zelda Pickup')[0] + available_pickups.remove(zelda_pickup) + set_event_item(world, player, 'Zelda Pickup', zelda_pickup) + + random.shuffle(available_pickups) + + restricted_pickups = { 'Get Frog': 'Dark Blacksmith Ruins'} + for pickup in restricted_pickups: + restricted_quests = [q for q in available_quests.keys() if q not in restricted_pickups[pickup]] + random.shuffle(restricted_quests) + quest = restricted_quests.pop() + available_quests.pop(quest) + available_pickups.remove(pickup) + set_event_item(world, player, quest, pickup) + + for pickup in available_pickups: + quest, _ = available_quests.popitem() + set_event_item(world, player, quest, pickup) + + def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool): item_parts = item.split('#') item_player = player if len(item_parts) < 2 else int(item_parts[1]) diff --git a/Items.py b/Items.py index 5b24b17a..ec6a18b8 100644 --- a/Items.py +++ b/Items.py @@ -180,6 +180,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class 'Beat Boss': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), + 'Pick Up Kiki': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), + 'Dark Palace Opened': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Return Smith': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Pick Up Purple Chest': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), @@ -198,6 +200,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class 'Hidden Pits': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Zelda Herself': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), + 'Sign Vandalized': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Escort Old Man': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Return Old Man': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Farmable Bombs': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/Main.py b/Main.py index f668e853..6029ee30 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,7 @@ from Dungeons import create_dungeons from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_items from Fill import dungeon_tracking from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops -from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations +from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations, shuffle_event_items, follower_pickups from UnderworldGlitchRules import connect_hmg_entrances_regions, create_hmg_entrances_regions from Utils import output_path, parse_player_names @@ -35,6 +35,7 @@ from source.item.District import init_districts from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config, verify_item_pool_config from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data +from source.tools.GraphExporter import GephiStreamer from source.classes.CustomSettings import CustomSettings from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies @@ -244,6 +245,7 @@ def main(args, seed=None, fish=None): sell_keys(world, player) else: lock_shop_locations(world, player) + shuffle_event_items(world, player) massage_item_pool(world) logger.info(world.fish.translate("cli", "cli", "placing.dungeon.prizes")) @@ -476,6 +478,7 @@ def init_world(args, fish): world.owKeepSimilar = args.ow_keepsimilar.copy() world.owWhirlpoolShuffle = args.ow_whirlpool.copy() world.owFluteShuffle = args.ow_fluteshuffle.copy() + world.shuffle_followers = args.shuffle_followers.copy() world.shuffle_bonk_drops = args.bonk_drops.copy() world.open_pyramid = args.openpyramid.copy() world.boss_shuffle = args.shufflebosses.copy() @@ -530,6 +533,7 @@ def set_starting_inventory(world, args): if world.customizer and world.customizer.get_start_inventory(): for p, inv_list in world.customizer.get_start_inventory().items(): if inv_list: + follower_added = False for inv_item in inv_list: name = inv_item.strip() if inv_item == 'RandomWeapon': @@ -543,7 +547,13 @@ def set_starting_inventory(world, args): item = ItemFactory(e, p) if item: world.push_precollected(item) + elif inv_item == 'RandomFollower': + name = random.choice([f for f in follower_pickups if f != 'Zelda Herself' or world.mode[p] == 'standard']) name = name if name != 'Ocarina' or world.flute_mode[p] != 'active' else 'Ocarina (Activated)' + if name in follower_pickups: + if not world.shuffle_followers[p] or follower_added: + continue + follower_added = True item = ItemFactory(name, p) if item: world.push_precollected(item) @@ -591,6 +601,7 @@ def copy_world(world): ret.owKeepSimilar = world.owKeepSimilar.copy() ret.owWhirlpoolShuffle = world.owWhirlpoolShuffle.copy() ret.owFluteShuffle = world.owFluteShuffle.copy() + ret.shuffle_followers = world.shuffle_followers.copy() ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy() ret.open_pyramid = world.open_pyramid.copy() ret.shufflelinks = world.shufflelinks.copy() @@ -811,6 +822,7 @@ def copy_world_premature(world, player): ret.owKeepSimilar = world.owKeepSimilar.copy() ret.owWhirlpoolShuffle = world.owWhirlpoolShuffle.copy() ret.owFluteShuffle = world.owFluteShuffle.copy() + ret.shuffle_followers = world.shuffle_followers.copy() ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy() ret.open_pyramid = world.open_pyramid.copy() ret.shufflelinks = world.shufflelinks.copy() diff --git a/OWEdges.py b/OWEdges.py index ef9c9182..645e05ad 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -1241,6 +1241,7 @@ OWTileRegions = bidict({ 'Broken Bridge Water': 0x5d, 'Palace of Darkness Area': 0x5e, + 'Dark Palace Button': 0x5e, 'Hammer Pegs Area': 0x62, 'Hammer Pegs Entry': 0x62, @@ -1580,6 +1581,7 @@ OWExitTypes = { 'Middle Aged Man', 'Desert Pass Ladder (South)', 'Desert Pass Ladder (North)', + 'Kiki Assistance', 'GT Approach', 'GT Leave', ], diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 6934829e..6118b731 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1833,6 +1833,7 @@ mandatory_connections = [ ('Broken Bridge Water Drop', 'Broken Bridge Water'), #flippers ('Broken Bridge Northeast Water Drop', 'Broken Bridge Water'), #flippers ('Broken Bridge West Water Drop', 'Broken Bridge Water'), #flippers + ('Kiki Assistance', 'Dark Palace Button'), ('Peg Area Rocks (West)', 'Hammer Pegs Area'), #mitts ('Peg Area Rocks (East)', 'Hammer Pegs Entry'), #mitts ('Dig Game To Ledge Drop', 'Dig Game Ledge'), #mitts diff --git a/README.md b/README.md index 707b7b75..f5c6572a 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,27 @@ New flute spots are chosen at random, with restrictions that limit the promixity New flute spots are chosen at random with minimum bias. +## Follower Shuffle (--shuffle_followers) + +This shuffles the follower companions throughout the world. Here is a full list of followers in the game: + +- Princess Zelda +- Old Man +- Blind Maiden +- Frog/Blacksmith +- Locksmith (Guy who unlocks Purple Chest) +- Kiki the Monkey +- Purple Chest +- Super Bomb + +When followers are shuffled, you must still fulfill the original requirements of the follower location. For example, if the Super Bomb Shop now contains the Frog, you must have crystals 5 and 6 and 100 rupees to unlock the Frog. It is also important to note that Purple Chest still needs to be delivered to the usual spot. Also, since it isn't useful normally, many people might not know that the Locksmith (the Purple Chest unlocking guy) can follow you, you must remove his sign to get him to follow. + +Most of the limitations of followers in the vanilla game have been lifted for this shuffle to work. For instance, you are able to mirror, flute, die, collect a crystal, and save/quit in most situations and still retain the follower. You are also able to enter caves/dungeons and complete dig game while you have a follower. + +In the scenario where you are forced down a narrow path and a follower is in your way and you already have a different follower. Running into that follower will switch the followers rather than overwriting, this gives you the opportunity to proceed with either one of the followers. Note that if you leave the screen, you lose this option to switch and you will need to go back to the original place you found the first follower. + +Optionally, thru the customizer, you can add a follower to your starting inventory, and you will be given that follower and it will stay with you until you complete their quest. + ## Bonk Drop Shuffle (--bonk_drops) This adds 42 new item locations to the game. These bonk locations are limited to the ones that drop a static item in the vanilla game. @@ -403,6 +424,12 @@ This gives each OW tile a random chance to be flipped to the opposite world For randomizing the flute spots around the overworld +``` +--shuffle_followers +``` + +This shuffles the follower companion locations, ie. Purple Chest, Old Man, etc. + ``` --bonk_drops ``` diff --git a/Regions.py b/Regions.py index 7068f0f6..d539cb86 100644 --- a/Regions.py +++ b/Regions.py @@ -133,7 +133,7 @@ def create_regions(world, player): create_lw_region(player, 'Ice Cave Area', None, ['Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Cave Water Drop', 'Ice Cave SE']), create_lw_region(player, 'Ice Cave Water', None, ['Ice Cave Pier', 'Ice Cave SW'], 'Light World', Terrain.Water), create_lw_region(player, 'Desert Pass Area', ['Middle Aged Man'], ['Desert Fairy', '50 Rupee Cave', 'Middle Aged Man', 'Desert Pass Ladder (South)', 'Desert Pass Rocks (North)', 'Desert Pass WS', 'Desert Pass EC']), - create_lw_region(player, 'Middle Aged Man', ['Purple Chest'], None), + create_lw_region(player, 'Middle Aged Man', ['Purple Chest', 'Locksmith'], None), create_lw_region(player, 'Desert Pass Southeast', None, ['Desert Pass Rocks (South)', 'Desert Pass ES']), create_lw_region(player, 'Desert Pass Ledge', None, ['Desert Pass Ladder (North)', 'Desert Pass Ledge Drop', 'Desert Pass WC']), create_lw_region(player, 'Dam Area', ['Sunken Treasure'], ['Dam', 'Dam WC', 'Dam WS', 'Dam NC', 'Dam EC']), @@ -197,7 +197,8 @@ def create_regions(world, player): create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']), create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']), create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water), - create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']), + create_dw_region(player, 'Palace of Darkness Area', ['Kiki'], ['Palace of Darkness Hint', 'Palace of Darkness', 'Kiki Assistance', 'Palace of Darkness SW', 'Palace of Darkness SE']), + create_dw_region(player, 'Dark Palace Button', ['Kiki Assistance'], None), create_dw_region(player, 'Darkness Cliff', None, ['Dark Dunes Cliff Ledge Drop', 'Hammer Bridge North Cliff Ledge Drop', 'Dark Tree Line Cliff Ledge Drop', 'Palace of Darkness Cliff Ledge Drop']), create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']), create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']), @@ -436,7 +437,6 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Hyrule Dungeon Staircase', 'Hyrule Castle', None, ['Hyrule Dungeon Staircase Up Stairs', 'Hyrule Dungeon Staircase Down Stairs']), create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'Hyrule Castle', ['Hyrule Castle - Big Key Drop'], ['Hyrule Dungeon Cellblock Up Stairs', 'Hyrule Dungeon Cellblock Door']), create_dungeon_region(player, 'Hyrule Dungeon Cell', 'Hyrule Castle', - ["Hyrule Castle - Zelda's Chest"] if not std_flag else ["Hyrule Castle - Zelda's Chest", 'Zelda Pickup'], ['Hyrule Dungeon Cell Exit']), @@ -1255,7 +1255,9 @@ def adjust_locations(world, player): location.type = LocationType.Logical location.real = False if l not in ['Ganon', 'Agahnim 1', 'Agahnim 2']: - location.skip = True + from ItemList import follower_quests + if not world.shuffle_followers[player] or l not in follower_quests: + location.skip = True def valid_pot_location(pot, pot_set, world, player): @@ -1413,9 +1415,12 @@ location_events = { 'Ice Palace - Boss Kill': 'Beat Boss', 'Misery Mire - Boss Kill': 'Beat Boss', 'Turtle Rock - Boss Kill': 'Beat Boss', + 'Locksmith': 'Sign Vandalized', 'Lost Old Man': 'Escort Old Man', 'Old Man Drop Off': 'Return Old Man', 'Floodgate': 'Open Floodgate', + 'Kiki': 'Pick Up Kiki', + 'Kiki Assistance': 'Dark Palace Opened', 'Big Bomb': 'Pick Up Big Bomb', 'Pyramid Crack': 'Detonate Big Bomb', 'Frog': 'Get Frog', @@ -1671,9 +1676,12 @@ location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'), 'Ice Palace - Boss Kill': (None, None, False, None), 'Misery Mire - Boss Kill': (None, None, False, None), 'Turtle Rock - Boss Kill': (None, None, False, None), + 'Locksmith': (None, None, False, None), 'Lost Old Man': (None, None, False, None), 'Old Man Drop Off': (None, None, False, None), 'Floodgate': (None, None, False, None), + 'Kiki': (None, None, False, None), + 'Kiki Assistance': (None, None, False, None), 'Frog': (None, None, False, None), 'Missing Smith': (None, None, False, None), 'Dark Blacksmith Ruins': (None, None, False, None), diff --git a/Rom.py b/Rom.py index abea18f4..c102dfec 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '80e0a4f8bd5cc6f83ac9f7f46c01bf4f' +RANDOMIZERBASEHASH = '7ce0f9fc9db08644ff77fb41993d9e34' class JsonRom(object): @@ -638,10 +638,6 @@ def patch_rom(world, rom, player, team, is_mystery=False): write_int16(rom, 0x150002, owMode) write_int16(rom, 0x150004, owFlags) - from OverworldShuffle import can_reach_smith - if not can_reach_smith(world, player): - rom.write_byte(0x180043, 0x01) # patch for deleting smith on S+Q - # patch entrance/exits/holes for region in world.regions: for exit in region.exits: @@ -1545,9 +1541,44 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(snes_to_pc(0x0DB810), 0x8A) # allows heart pieces to travel across water # rom.write_byte(snes_to_pc(0x0DB730), 0x08) # allows chickens to travel across water - # allow smith into multi-entrance caves in appropriate shuffles - if world.shuffle[player] in ['restricted', 'simple', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity']: - rom.write_byte(0x18004C, 0x01) + + if world.shuffle_followers[player]: + from ItemList import follower_locations, follower_pickups + + for loc_name, address in follower_locations.items(): + loc = world.get_location(loc_name, player) + rom.write_byte(address, follower_pickups[loc.item.name]) + + rom.write_byte(0x18004C, 0x02) # enable follower shuffle + rom.write_byte(snes_to_pc(0x1BBD3A), 0x80) # allow all followers thru entrances + rom.write_bytes(snes_to_pc(0x1DFC70), [0xEA, 0xEA]) # allow followers at dig game + rom.write_byte(snes_to_pc(0x02823B), 0x80) # allow super bomb indoors + rom.write_byte(snes_to_pc(0x0283FB), 0x80) # allow maiden to go outside + rom.write_bytes(snes_to_pc(0x02D6F2), [0xEA, 0xEA]) # disable old man checkpoint + rom.write_byte(snes_to_pc(0x05DEFA), 0xAF) # no follower despawn at uncle + rom.write_byte(snes_to_pc(0x05DF3C), 0xAF) # no follower despawn at uncle + rom.write_bytes(snes_to_pc(0x079448), [0xEA, 0xEA]) # dont draw super bomb while falling into holes + rom.write_byte(snes_to_pc(0x079595), 0x80) # allow super bomb to follow into OW holes + rom.write_bytes(snes_to_pc(0x07A132), [0xEA, 0xEA]) # allow bomb use with super bomb + rom.write_byte(snes_to_pc(0x07A4B4), 0x80) # allow ether use with super bomb + rom.write_byte(snes_to_pc(0x07A589), 0x80) # allow bombos use with super bomb + rom.write_byte(snes_to_pc(0x07A66B), 0x80) # allow quake use with super bomb + rom.write_byte(snes_to_pc(0x07A919), 0x80) # disable kiki dialogue during mirror + rom.write_byte(snes_to_pc(0x07AAC5), 0xAF) # keep all followers after mirroring + rom.write_byte(snes_to_pc(0x08DED6), 0x80) # allow locksmith to follow with flute + rom.write_bytes(snes_to_pc(0x09A045), [0xEA, 0xEA]) # allow super bomb to follow into UW holes + rom.write_byte(snes_to_pc(0x09ACDF), 0x6B) # allow kiki/locksmith to follow after screen transition + + if world.enemy_shuffle[player] != 'none': + # informs zelda and maiden to draw over gfx slots that are guaranteed unused + rom.write_bytes(0x1802C1, world.data_tables[player].room_headers[0x80].free_gfx[0:2]) + rom.write_bytes(0x1802C7, world.data_tables[player].room_headers[0x45].free_gfx[0:2]) + else: + from OverworldShuffle import can_reach_smith + if not can_reach_smith(world, player): + rom.write_byte(0x180043, 0x01) # patch for deleting smith on S+Q + if world.shuffle[player] in ['restricted', 'simple', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity']: + rom.write_byte(0x18004C, 0x01) # allow smith into multi-entrance caves in appropriate shuffles # set correct flag for hera basement item hera_basement = world.get_location('Tower of Hera - Basement Cage', player) diff --git a/Rules.py b/Rules.py index 29c95a9b..68dfda95 100644 --- a/Rules.py +++ b/Rules.py @@ -252,6 +252,7 @@ def global_rules(world, player): set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_location('Kiki Assistance', player), lambda state: state.has('Pick Up Kiki', player)) # Can S&Q with chest set_rule(world.get_location('Middle Aged Man', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Deliver Purple Chest', player)) # Can S&Q with chest set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) @@ -406,6 +407,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has_Boots(player)) set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Palace of Darkness', player), lambda state: state.has('Dark Palace Opened', player)) set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has('Turtle Opened', player)) @@ -1128,6 +1130,8 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_location('Maze Race', player), player) add_bunny_rule(world.get_location('Flute Spot', player), player) add_bunny_rule(world.get_location('Catfish', player), player) + add_bunny_rule(world.get_location('Kiki', player), player) + add_bunny_rule(world.get_location('Locksmith', player), player) # entrances add_bunny_rule(world.get_entrance('Lost Woods Hideout Drop', player), player) @@ -1148,7 +1152,6 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_entrance('Skull Woods Final Section', player), player) # bunny cannot use fire rod add_bunny_rule(world.get_entrance('Hookshot Cave', player), player) add_bunny_rule(world.get_entrance('Thieves Town', player), player) # bunny cannot pull - add_bunny_rule(world.get_entrance('Palace of Darkness', player), player) # kiki needs pearl add_bunny_rule(world.get_entrance('Hammer Peg Cave', player), player) add_bunny_rule(world.get_entrance('Bonk Fairy (Dark)', player), player) add_bunny_rule(world.get_entrance('Misery Mire', player), player) @@ -1639,7 +1642,6 @@ def standard_rules(world, player): else: add_rule(loc, lambda state: standard_escape_rule(state)) - set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player)) set_rule(world.get_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player)) def check_rule_list(state, r_list): @@ -1702,7 +1704,7 @@ def set_bunny_rules(world, player, inverted): bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Old Man', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins', - 'Spectacle Rock', 'Bombos Tablet', 'Ether Tablet', 'Purple Chest', 'Blacksmith', + 'Spectacle Rock', 'Bombos Tablet', 'Ether Tablet', 'Kiki Assistance', 'Purple Chest', 'Blacksmith', 'Missing Smith', 'Master Sword Pedestal', 'Bottle Merchant', 'Sunken Treasure', 'Desert Ledge', 'Pyramid Crack', 'Big Bomb', 'Stumpy', 'Lost Old Man', 'Old Man Drop Off', 'Murahdahla', 'Kakariko Shop - Left', 'Kakariko Shop - Middle', 'Kakariko Shop - Right', diff --git a/data/base2current.bps b/data/base2current.bps index db46c22a6aaf24c4317e0d06e25a5acb3590b983..84b14dd60ee8f4971aa260ec8bde9ec35c33c7ce 100644 GIT binary patch delta 18728 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 PD88n1`7