From cb11ddb1c6d54b586081238e4c5563373bd2b06e Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 3 Sep 2025 02:41:26 -0500 Subject: [PATCH 1/7] Fixed follower plando error --- ItemList.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ItemList.py b/ItemList.py index de36b74a..4b4b3ace 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1715,10 +1715,11 @@ def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_ item_player = player if len(item_parts) < 2 else int(item_parts[1]) item_name = item_parts[0] event_flag = False - if item_name in prize_set: - item_player = player # prizes must be for that player + if item_name in prize_set or item_name in follower_pickups: + item_player = player # must be for that player item_to_place = ItemFactory(item_name, item_player) - prize_pool.remove(item_name) + if item_name in prize_set: + prize_pool.remove(item_name) event_flag = True elif is_dungeon_item(item_name, world, item_player): item_to_place = next(x for x in dungeon_pool From 95ec5c79838505ca0e918f45fe7783cbe79e2340 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 3 Sep 2025 02:51:28 -0500 Subject: [PATCH 2/7] Fixed error with placing Old Man cave in ER --- source/overworld/EntranceShuffle2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index c190c428..d02e3362 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -403,6 +403,7 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): else: region_name = 'West Dark Death Mountain (Top)' om_cave_options = list(get_accessible_entrances(region_name, avail, [], cross_world, True, True, True, True)) + om_cave_options = [e for e in om_cave_options if e in avail.entrances] if avail.swapped: om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances] assert len(om_cave_options), 'No available entrances left to place Old Man Cave' From 808daf224d8b5f4ee276d2b8b34dc2b19eb2c664 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 3 Sep 2025 02:52:55 -0500 Subject: [PATCH 3/7] Fixed EG2 spawn starts to connect logic exits more consistently --- source/overworld/EntranceShuffle2.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index d02e3362..777edaec 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -644,7 +644,6 @@ def do_dark_sanc(entrances, exits, avail): entrances.remove(choice) exits.remove('Dark Sanctuary Hint') connect_entrance(choice, 'Dark Sanctuary Hint', avail) - ext.connect(avail.world.get_entrance(choice, avail.player).parent_region) if not avail.coupled: avail.decoupled_entrances.remove(choice) if avail.swapped and choice != 'Dark Sanctuary Hint': @@ -652,8 +651,7 @@ def do_dark_sanc(entrances, exits, avail): entrances.remove(swap_ent) exits.remove(swap_ext) elif not ext.connected_region: - # default to output to vanilla area, assume vanilla connection - ext.connect(avail.world.get_region('Dark Chapel Area', avail.player)) + raise Exception('Dark Sanctuary Hint was placed earlier but its exit not properly connected') def do_links_house(entrances, exits, avail, cross_world): @@ -723,8 +721,6 @@ def do_links_house(entrances, exits, avail, cross_world): connect_two_way(links_house, lh_exit, avail) else: connect_entrance(links_house, lh_exit, avail) - ext = avail.world.get_entrance('Big Bomb Shop Exit', avail.player) - ext.connect(avail.world.get_entrance(links_house, avail.player).parent_region) entrances.remove(links_house) exits.remove(lh_exit) if not avail.coupled: @@ -1908,6 +1904,12 @@ def connect_entrance(entrancename, exit_name, avail): avail.entrances.remove(entrancename) if avail.coupled: avail.exits.remove(exit_name) + if exit_name == 'Big Bomb Shop' and avail.world.is_bombshop_start(avail.player): + ext = avail.world.get_entrance('Big Bomb Shop Exit', avail.player) + ext.connect(avail.world.get_entrance(entrancename, avail.player).parent_region) + if exit_name == 'Dark Sanctuary Hint' and avail.world.is_dark_chapel_start(avail.player): + ext = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player) + ext.connect(avail.world.get_entrance(entrancename, avail.player).parent_region) world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player) logging.getLogger('').debug(f'Connected (entr) {entrance.name} to {exit.name if exit is not None else region.name}') From 5fc3924b8ab81c3aaf16bdc9ddb15605bca8cfe0 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 3 Sep 2025 02:50:20 -0500 Subject: [PATCH 4/7] Allow Zelda escape to use TT Maiden Cell as checkpoint --- BaseClasses.py | 2 ++ Doors.py | 8 ++++++-- DungeonGenerator.py | 12 ++++++------ ItemList.py | 15 +++++++++------ KeyDoorShuffle.py | 5 ++++- Rom.py | 14 +++++++++++++- Rules.py | 2 +- data/base2current.bps | Bin 136642 -> 136664 bytes source/dungeon/DungeonStitcher.py | 8 ++++---- source/enemizer/Enemizer.py | 15 +++++++++++++++ 10 files changed, 60 insertions(+), 21 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index a82f3093..1a230d6b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -76,6 +76,7 @@ class World(object): self.can_take_damage = True self.hints = hints.copy() self.prizes = {} + self.default_zelda_region = {} self.dynamic_regions = [] self.dynamic_locations = [] self.spoiler_mode = spoiler_mode @@ -185,6 +186,7 @@ class World(object): set_player_attr('standardize_palettes', 'standardize') set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False}) set_player_attr('prizes', {'dig;': [], 'pull': [0, 0, 0], 'crab': [0, 0], 'stun': 0, 'fish': 0, 'enemies': []}) + set_player_attr('default_zelda_region', 'Hyrule Dungeon Cellblock') set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('enabled_entrances', {}) diff --git a/Doors.py b/Doors.py index aa55cf0c..a8a53a26 100644 --- a/Doors.py +++ b/Doors.py @@ -1302,8 +1302,12 @@ def create_doors(world, player): world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain') 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') + if not world.shuffle_followers[player]: + zelda_location = 'Zelda Pickup' + else: + zelda_location = 'Suspicious Maiden' + world.get_door('Hyrule Castle Throne Room Tapestry', player).event(zelda_location) + world.get_door('Hyrule Castle Tapestry Backwards', player).event(zelda_location) # crystal switches and barriers world.get_door('Hera Lobby Crystal Exit', player).c_switch() diff --git a/DungeonGenerator.py b/DungeonGenerator.py index d3e33bb8..3306b71d 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -586,13 +586,13 @@ def determine_paths_for_dungeon(world, player, all_regions, name): paths.append(portal.door.entrance.parent_region.name) if world.mode[player] == 'standard': if name == 'Hyrule Castle': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Sanctuary')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Sanctuary')) if name == 'Hyrule Castle Sewers': paths.append('Sanctuary') if name == 'Hyrule Castle Dungeon': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Hyrule Castle Throne Room')) if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town': paths.append('Thieves Attic Window') elif 'Thieves Attic Window' in all_r_names: @@ -1322,7 +1322,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge for r_name in dungeon_boss_sectors[key]: assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole) if key == 'Hyrule Castle' and world.mode[player] == 'standard': - for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary', 'Hyrule Castle Throne Room']: # need to deliver zelda + for r_name in [world.default_zelda_region[player], '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' @@ -3091,7 +3091,7 @@ def split_dungeon_builder(builder, split_list, builder_info): if builder.name == 'Hyrule Castle': assign_sector(find_sector('Hyrule Castle Throne Room', candidate_sectors), dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole) - assign_sector(find_sector('Hyrule Dungeon Cellblock', candidate_sectors), + assign_sector(find_sector(world.default_zelda_region[player], candidate_sectors), dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole) dungeon_map['Hyrule Castle Dungeon'].throne_door = world.get_door('Hyrule Castle Throne Room N', player) dungeon_map['Hyrule Castle Sewers'].sewers_access = builder.throne_door diff --git a/ItemList.py b/ItemList.py index 4b4b3ace..65d67f57 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1676,7 +1676,7 @@ def set_event_item(world, player, location_name, item_name=None): def shuffle_event_items(world, player): - if (world.shuffle_followers[player]): + if world.shuffle_followers[player]: available_quests = follower_quests.copy() available_pickups = [quests[0] for quests in available_quests.values()] @@ -1688,11 +1688,14 @@ def shuffle_event_items(world, player): 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) + if world.mode[player] == 'standard' and 'Zelda Herself' in available_pickups: + zelda_dropoff = 'Zelda Pickup' + if world.default_zelda_region[player] == 'Thieves Blind\'s Cell': + zelda_dropoff = 'Suspicious Maiden' + available_quests.pop(zelda_dropoff) + zelda_pickup = 'Zelda Herself' + available_pickups.remove(zelda_pickup) + set_event_item(world, player, zelda_dropoff, zelda_pickup) random.shuffle(available_pickups) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index d9c837b9..65ad0faf 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1805,7 +1805,10 @@ def imp_locations_factory(world, player): return imp_locations imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden'] if world.mode[player] == 'standard': - imp_locations.append('Zelda Pickup') + if world.default_zelda_region[player] == 'Thieves Blinds\' Cell': + imp_locations.append('Suspicious Maiden') + else: + imp_locations.append('Zelda Pickup') imp_locations.append('Zelda Drop Off') return imp_locations diff --git a/Rom.py b/Rom.py index faf0b6c3..42cfe59d 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '93386b05ee4b5de6b9165941b9e14e97' +RANDOMIZERBASEHASH = '20e588b832011dcf15dbd31dff6955ce' class JsonRom(object): @@ -1581,6 +1581,18 @@ def patch_rom(world, rom, player, team, is_mystery=False): 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.default_zelda_region[player] == 'Thieves Blind\'s Cell': + write_int16(rom, snes_to_pc(0x02D8D6), 0x45) # change zelda spawn point to maiden cell + rom.write_bytes(snes_to_pc(0x02D8F0), [0x08, 0x08, 0x08, 0x09, 0x0B, 0x0A, 0x0B, 0x0B]) + write_int16(rom, snes_to_pc(0x02D91C), 0x0B00) + write_int16(rom, snes_to_pc(0x02D92A), 0x0800) + write_int16(rom, snes_to_pc(0x02D938), 0x0860) + write_int16(rom, snes_to_pc(0x02D946), 0x0B90) + write_int16(rom, snes_to_pc(0x02D954), 0x0078) + write_int16(rom, snes_to_pc(0x02D962), 0x017F) + rom.write_byte(snes_to_pc(0x02D975), 0x00) + rom.write_byte(snes_to_pc(0x02D98A), 0x02) + 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]) diff --git a/Rules.py b/Rules.py index b062d917..d87eaa45 100644 --- a/Rules.py +++ b/Rules.py @@ -1669,7 +1669,7 @@ def standard_rules(world, player): def find_rules_for_zelda_delivery(world, player): # path rules for backtracking - start_region = world.get_region('Hyrule Dungeon Cellblock', player) + start_region = world.get_region(world.default_zelda_region[player], player) queue = deque([(start_region, [], [])]) visited = {start_region} blank_state = CollectionState(world) diff --git a/data/base2current.bps b/data/base2current.bps index 3ac6a7ce81674927ee8d2e3aaa19e8698204ca37..0fe2d932963a31af760b5381bf17b8627f8c443c 100644 GIT binary patch delta 1792 zcmW+#4Ny~87S0VxNHB>3L8Bmqhw`JM)YX;ZN>v1dbt=@3%d}AJ)I#LpR-JBLEW*9f z1cD7oxR6(Tggn3~Z>bAw?4}(iy9LKWB4}pW?kL+<{!P%*>@=O#u3PuTxik09J?A^; zeD^!|4EKre^@#^ILO>NmvT1>EHYzAf3J-w!3pbBvD!tZgMM3$K0&$;YUg|C zQzJ3xEL!(i2I`%=wl?es@y=T{6QZ~zKQqvEeV#E(h{JxydUE@B7UEre4d$jS_!+Z- zkOb^`Ia64gJ#`uB26~E1kQ!2lUVos_8i{r+N2}K51(+$M8%4S-*r%?(c@W}6El0dK zFu0=T2XmX=yn=We7or`jI1O9FK=0Lad+wu!o8s)Ldp6=fm_nK0E$1zCBTASw!w^Z% zx{teaKp9RRDd%=Hl2L>T>AmkgwCwQ+g1asi?4C7lcQ77PHJtxaJKzDnG#Ks4_a#pq zllT()zj|ScwXWScF+Mq?AijMHiFZEN4l8~h&LYJ#S@e-Ago7jd)7crfO73~tx6GX- z_r($!^UMGrO5KX(TtkJd)TUUjd8Bzn)Zp?_WfC#RaE0WWN9=i3?ksm`59ZE#1TQQX zsNL8)`l>8@-q7p0#3qt_u4Mh1nnO9Tafi^GMUT%g8{o&*qw z9iE!?9LQwd5o7F966r6hD7w`i7EXp)|Ll?Y#P;%PAKu|j%xIcpG`1{Swi%AIz-Er@ zgfiC5@ysR(&ZVRh%1$|xV%u%Q*b=ceoh^J=?#13@qnXH((+ib*dIQJbaSya=utGy^W{67WE61rE`cU zkI8gg$Bj7!KuHs#a zEOgsXZ5g%+_g(&C7n23siLCp&#Iw&(R!gCS0%!3EeT2&Dy*DZL?- z8lWyCfjy;lD8&us9uQ0jli;eq`jD=|1!Ji|k57yx)O)XLjbDG$@1+~NfKEgL-jnCg z>GC{38ohfu&)@ddbux7Gy8YveRggZT#WiM!IfCVpoE(}a{8}U3X|Fhsl=MNZF3)e@ zj?e^k#a8;{UwR&w$DaAhZM4)=y5}~o%z)H^Xn6Zuwx@31lz5?QXs+WazjOlI#@81`a84nX zHPENpT52l3!TILFT(*5Kz_le5pqtUS^Bi;?TJN`3d_aFIm-DzK=VmR)&6rI6;%n@N ztFP?ZWbesY_MnaU635(GpLgV~MHG)y8gUGwD&L=Dr9nX`#Z87%-K5oOAx&dH*4f`U zGiM?#29Gp|f!-nsvuxS94=%mG57(!|v-s?tM-nmviZ^&SwnjOBcej4CsGtO5Nxt*P z|CNftOH`2v#DguaY7zKL^cu3{qH!-A2(}zv4Gl0DY0Ym!WVg`{5?#Mhg27dS#93VG z`tKINX0EB0J!$zvY=cd=G&!8r{SRJWWGytXCtvGRQGYE18$g^ZPzFNDt4{yw*-&rO zWuk1F=@-DO8T_EM|1f>T6{-hZ6Ds#MQ?fHIej~U6gm2bDD#JBs0Ddue*>$-EL;$eK z)r-Ms1YatpPE#NQl({-6a0SHV*XnId@5ML!+%VFb_)!yeih83R{3K30YSSHyq*7eP zm%!hF@WX_A^a)pb2dIhwt0`;;zXk_fUOVs$MdEC@Y;>RNa|hs`6pwYE*H?)S^ZpNL C%|25A delta 1753 zcmW+!e^66b7S2sVeh>%{pahgOJdlc&$|4mCt6&j9MO$R1s}@AJ{)j%Q*4e6zNbgMz z5H%#>3a|Kx?+IebQkJZ-P1Q+PV5|fI(`q~F?o@<;30j(taRzr>-IxAz&%Nh<-+bqM z=iKd)4D?8Biv+@IRr5ppTY|&N-0M^9%*+b}H3(TUkbciJ+Y?21Bj`r3TmN*5T?~=# z?B7C>er9IIO?@gs?G$e%YVRWC_6PICLTUOm$2YYQAK>3!k4-<|Wlsl@^roYRmN%`S z%_L5Ny0b5hYoYncBM#~Rx@+DVz{S^yefUpmQmXWE*{}3OgS|_Q_=z1;RK^#EZN%^+ z{D#4pI!hP%v$V!tWw6~qm~mn9tHC6Ib=i(ZmM424%-_9-a;YuY7rIUf) z_XpG^XSRwM`jaDgZm2khW0liMC=ct9U>_FmkgJ?R;aQ*hChi6+1G+4B=|+H zOEx}~_2yoa7Eq5FfcsoE@f_FKnyoOH6@<#eIu53^Hf`u-sufQw?3}(F>F2{xC7DA8D~H0))IGkoV4hcyWUyMK)&169A0)WK6sgaIYsznU>~GjV72GN`ixN|6Im zE?s4QmT6^ml3*u?+=>CUwRc)4vhsL3Xd(oc#V@OTQXn~^7%qOkI$z|(g#okrNtlR;-r!2g{jye47}l^>h*zCA9WrH8NuoP zWG9rnfPa2am6OYRM6bmaBdMxd56iXjlaJ|ie5cDLn-?@75f!Cn6gs8}UBT=~%$EE@ zFB-bFvC!M{{Y5e`c#%1EaxbJ0YjL%y%OtS!xFri{PT+SN>9fqP&ykWY)#{ddnYR(T zOBEb$D4b_gy-Kxvz>olc@r0>L27LjytxI-N0Txy8|$FXymi6jz{gePGR${Gx>anr-E};S8FGlN+%DQN{Z`)-b;?knAJ_sZO%B z)l3?>b!D;+dyG|Py(`R*LA_ZTEU7tf6`b9_3g@T6kFja&vGmPFP?I)H;zjo3w?2DI zl(`AwPgmKcFDoR8MJWyU$lezMznEC}KTB!yZ10DHG~gC+H)@_kD+1p{CCE0PG`tIK z{^s&{sE2-WYtacrb{LrLiH?m*a5YX?H-T#%Q5nFRo=_S-*L)^>L;1USQt%a1-@q-?CC1AbdN;41uP~i9ugCPNbpNwjxKpI%?xJZEuK(V>D-okdD z+|uKO;#Sqs6I3O&w+%d#h($ diff --git a/source/dungeon/DungeonStitcher.py b/source/dungeon/DungeonStitcher.py index 29a2c32f..d7ce3c4f 100644 --- a/source/dungeon/DungeonStitcher.py +++ b/source/dungeon/DungeonStitcher.py @@ -333,13 +333,13 @@ def determine_paths_for_dungeon(world, player, all_regions, name): if portal.destination: paths.append(portal.door.entrance.parent_region.name) if world.mode[player] == 'standard' and name == 'Hyrule Castle Dungeon': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Hyrule Castle Throne Room')) entrance = next(x for x in world.dungeon_portals[player] if x.name == 'Hyrule Castle South') # todo: in non-er, we can use the other portals too - paths.append(('Hyrule Dungeon Cellblock', entrance.door.entrance.parent_region.name)) + paths.append((world.default_zelda_region[player], entrance.door.entrance.parent_region.name)) paths.append(('Hyrule Castle Throne Room', [entrance.door.entrance.parent_region.name, - 'Hyrule Dungeon Cellblock'])) + world.default_zelda_region[player]])) if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town': paths.append('Thieves Attic Window') elif 'Thieves Attic Window' in all_r_names: diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index f4e89207..cc32ba8c 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -521,6 +521,21 @@ def randomize_enemies(world, player): green_mail, blue_mail, red_mail = original_table[idx] del original_table[idx] world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail] + # determine default zelda follower location + if world.mode[player] == 'standard' and world.doorShuffle[player] == 'crossed' and world.shuffle_followers[player]: + def random_zelda(): + world.default_zelda_region[player] = random.choice(['Hyrule Dungeon Cellblock', 'Thieves Blind\'s Cell']) + if world.customizer: + placements = world.customizer.get_placements() + if placements and player in placements and 'Zelda Herself' in placements[player].values(): + location = [l for (l, item) in placements[player].items() if item == 'Zelda Herself'][0] + if location == 'Suspicious Maiden': + world.default_zelda_region[player] = 'Thieves Blind\'s Cell' + else: + random_zelda() + else: + random_zelda() + def write_enemy_shuffle_settings(world, player, rom): From a5f14530cd5c70b1ff21054dd814ceb1d4bb35d8 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 3 Sep 2025 02:50:20 -0500 Subject: [PATCH 5/7] Allow Zelda escape to use TT Maiden Cell as checkpoint --- BaseClasses.py | 2 ++ Doors.py | 8 ++++++-- DungeonGenerator.py | 12 ++++++------ ItemList.py | 15 +++++++++------ KeyDoorShuffle.py | 5 ++++- Rom.py | 14 +++++++++++++- Rules.py | 2 +- data/base2current.bps | Bin 136642 -> 136664 bytes source/dungeon/DungeonStitcher.py | 8 ++++---- source/enemizer/Enemizer.py | 15 +++++++++++++++ 10 files changed, 60 insertions(+), 21 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index a82f3093..1a230d6b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -76,6 +76,7 @@ class World(object): self.can_take_damage = True self.hints = hints.copy() self.prizes = {} + self.default_zelda_region = {} self.dynamic_regions = [] self.dynamic_locations = [] self.spoiler_mode = spoiler_mode @@ -185,6 +186,7 @@ class World(object): set_player_attr('standardize_palettes', 'standardize') set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False}) set_player_attr('prizes', {'dig;': [], 'pull': [0, 0, 0], 'crab': [0, 0], 'stun': 0, 'fish': 0, 'enemies': []}) + set_player_attr('default_zelda_region', 'Hyrule Dungeon Cellblock') set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('enabled_entrances', {}) diff --git a/Doors.py b/Doors.py index aa55cf0c..909fe40b 100644 --- a/Doors.py +++ b/Doors.py @@ -1302,8 +1302,12 @@ def create_doors(world, player): world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain') 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') + if world.default_zelda_region[player] == 'Thieves Blind\'s Cell': + zelda_location = 'Suspicious Maiden' + else: + zelda_location = 'Zelda Pickup' + world.get_door('Hyrule Castle Throne Room Tapestry', player).event(zelda_location) + world.get_door('Hyrule Castle Tapestry Backwards', player).event(zelda_location) # crystal switches and barriers world.get_door('Hera Lobby Crystal Exit', player).c_switch() diff --git a/DungeonGenerator.py b/DungeonGenerator.py index d3e33bb8..3306b71d 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -586,13 +586,13 @@ def determine_paths_for_dungeon(world, player, all_regions, name): paths.append(portal.door.entrance.parent_region.name) if world.mode[player] == 'standard': if name == 'Hyrule Castle': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Sanctuary')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Sanctuary')) if name == 'Hyrule Castle Sewers': paths.append('Sanctuary') if name == 'Hyrule Castle Dungeon': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Hyrule Castle Throne Room')) if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town': paths.append('Thieves Attic Window') elif 'Thieves Attic Window' in all_r_names: @@ -1322,7 +1322,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge for r_name in dungeon_boss_sectors[key]: assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole) if key == 'Hyrule Castle' and world.mode[player] == 'standard': - for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary', 'Hyrule Castle Throne Room']: # need to deliver zelda + for r_name in [world.default_zelda_region[player], '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' @@ -3091,7 +3091,7 @@ def split_dungeon_builder(builder, split_list, builder_info): if builder.name == 'Hyrule Castle': assign_sector(find_sector('Hyrule Castle Throne Room', candidate_sectors), dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole) - assign_sector(find_sector('Hyrule Dungeon Cellblock', candidate_sectors), + assign_sector(find_sector(world.default_zelda_region[player], candidate_sectors), dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole) dungeon_map['Hyrule Castle Dungeon'].throne_door = world.get_door('Hyrule Castle Throne Room N', player) dungeon_map['Hyrule Castle Sewers'].sewers_access = builder.throne_door diff --git a/ItemList.py b/ItemList.py index 4b4b3ace..65d67f57 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1676,7 +1676,7 @@ def set_event_item(world, player, location_name, item_name=None): def shuffle_event_items(world, player): - if (world.shuffle_followers[player]): + if world.shuffle_followers[player]: available_quests = follower_quests.copy() available_pickups = [quests[0] for quests in available_quests.values()] @@ -1688,11 +1688,14 @@ def shuffle_event_items(world, player): 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) + if world.mode[player] == 'standard' and 'Zelda Herself' in available_pickups: + zelda_dropoff = 'Zelda Pickup' + if world.default_zelda_region[player] == 'Thieves Blind\'s Cell': + zelda_dropoff = 'Suspicious Maiden' + available_quests.pop(zelda_dropoff) + zelda_pickup = 'Zelda Herself' + available_pickups.remove(zelda_pickup) + set_event_item(world, player, zelda_dropoff, zelda_pickup) random.shuffle(available_pickups) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index d9c837b9..65ad0faf 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1805,7 +1805,10 @@ def imp_locations_factory(world, player): return imp_locations imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden'] if world.mode[player] == 'standard': - imp_locations.append('Zelda Pickup') + if world.default_zelda_region[player] == 'Thieves Blinds\' Cell': + imp_locations.append('Suspicious Maiden') + else: + imp_locations.append('Zelda Pickup') imp_locations.append('Zelda Drop Off') return imp_locations diff --git a/Rom.py b/Rom.py index faf0b6c3..42cfe59d 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '93386b05ee4b5de6b9165941b9e14e97' +RANDOMIZERBASEHASH = '20e588b832011dcf15dbd31dff6955ce' class JsonRom(object): @@ -1581,6 +1581,18 @@ def patch_rom(world, rom, player, team, is_mystery=False): 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.default_zelda_region[player] == 'Thieves Blind\'s Cell': + write_int16(rom, snes_to_pc(0x02D8D6), 0x45) # change zelda spawn point to maiden cell + rom.write_bytes(snes_to_pc(0x02D8F0), [0x08, 0x08, 0x08, 0x09, 0x0B, 0x0A, 0x0B, 0x0B]) + write_int16(rom, snes_to_pc(0x02D91C), 0x0B00) + write_int16(rom, snes_to_pc(0x02D92A), 0x0800) + write_int16(rom, snes_to_pc(0x02D938), 0x0860) + write_int16(rom, snes_to_pc(0x02D946), 0x0B90) + write_int16(rom, snes_to_pc(0x02D954), 0x0078) + write_int16(rom, snes_to_pc(0x02D962), 0x017F) + rom.write_byte(snes_to_pc(0x02D975), 0x00) + rom.write_byte(snes_to_pc(0x02D98A), 0x02) + 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]) diff --git a/Rules.py b/Rules.py index b062d917..d87eaa45 100644 --- a/Rules.py +++ b/Rules.py @@ -1669,7 +1669,7 @@ def standard_rules(world, player): def find_rules_for_zelda_delivery(world, player): # path rules for backtracking - start_region = world.get_region('Hyrule Dungeon Cellblock', player) + start_region = world.get_region(world.default_zelda_region[player], player) queue = deque([(start_region, [], [])]) visited = {start_region} blank_state = CollectionState(world) diff --git a/data/base2current.bps b/data/base2current.bps index 3ac6a7ce81674927ee8d2e3aaa19e8698204ca37..0fe2d932963a31af760b5381bf17b8627f8c443c 100644 GIT binary patch delta 1792 zcmW+#4Ny~87S0VxNHB>3L8Bmqhw`JM)YX;ZN>v1dbt=@3%d}AJ)I#LpR-JBLEW*9f z1cD7oxR6(Tggn3~Z>bAw?4}(iy9LKWB4}pW?kL+<{!P%*>@=O#u3PuTxik09J?A^; zeD^!|4EKre^@#^ILO>NmvT1>EHYzAf3J-w!3pbBvD!tZgMM3$K0&$;YUg|C zQzJ3xEL!(i2I`%=wl?es@y=T{6QZ~zKQqvEeV#E(h{JxydUE@B7UEre4d$jS_!+Z- zkOb^`Ia64gJ#`uB26~E1kQ!2lUVos_8i{r+N2}K51(+$M8%4S-*r%?(c@W}6El0dK zFu0=T2XmX=yn=We7or`jI1O9FK=0Lad+wu!o8s)Ldp6=fm_nK0E$1zCBTASw!w^Z% zx{teaKp9RRDd%=Hl2L>T>AmkgwCwQ+g1asi?4C7lcQ77PHJtxaJKzDnG#Ks4_a#pq zllT()zj|ScwXWScF+Mq?AijMHiFZEN4l8~h&LYJ#S@e-Ago7jd)7crfO73~tx6GX- z_r($!^UMGrO5KX(TtkJd)TUUjd8Bzn)Zp?_WfC#RaE0WWN9=i3?ksm`59ZE#1TQQX zsNL8)`l>8@-q7p0#3qt_u4Mh1nnO9Tafi^GMUT%g8{o&*qw z9iE!?9LQwd5o7F966r6hD7w`i7EXp)|Ll?Y#P;%PAKu|j%xIcpG`1{Swi%AIz-Er@ zgfiC5@ysR(&ZVRh%1$|xV%u%Q*b=ceoh^J=?#13@qnXH((+ib*dIQJbaSya=utGy^W{67WE61rE`cU zkI8gg$Bj7!KuHs#a zEOgsXZ5g%+_g(&C7n23siLCp&#Iw&(R!gCS0%!3EeT2&Dy*DZL?- z8lWyCfjy;lD8&us9uQ0jli;eq`jD=|1!Ji|k57yx)O)XLjbDG$@1+~NfKEgL-jnCg z>GC{38ohfu&)@ddbux7Gy8YveRggZT#WiM!IfCVpoE(}a{8}U3X|Fhsl=MNZF3)e@ zj?e^k#a8;{UwR&w$DaAhZM4)=y5}~o%z)H^Xn6Zuwx@31lz5?QXs+WazjOlI#@81`a84nX zHPENpT52l3!TILFT(*5Kz_le5pqtUS^Bi;?TJN`3d_aFIm-DzK=VmR)&6rI6;%n@N ztFP?ZWbesY_MnaU635(GpLgV~MHG)y8gUGwD&L=Dr9nX`#Z87%-K5oOAx&dH*4f`U zGiM?#29Gp|f!-nsvuxS94=%mG57(!|v-s?tM-nmviZ^&SwnjOBcej4CsGtO5Nxt*P z|CNftOH`2v#DguaY7zKL^cu3{qH!-A2(}zv4Gl0DY0Ym!WVg`{5?#Mhg27dS#93VG z`tKINX0EB0J!$zvY=cd=G&!8r{SRJWWGytXCtvGRQGYE18$g^ZPzFNDt4{yw*-&rO zWuk1F=@-DO8T_EM|1f>T6{-hZ6Ds#MQ?fHIej~U6gm2bDD#JBs0Ddue*>$-EL;$eK z)r-Ms1YatpPE#NQl({-6a0SHV*XnId@5ML!+%VFb_)!yeih83R{3K30YSSHyq*7eP zm%!hF@WX_A^a)pb2dIhwt0`;;zXk_fUOVs$MdEC@Y;>RNa|hs`6pwYE*H?)S^ZpNL C%|25A delta 1753 zcmW+!e^66b7S2sVeh>%{pahgOJdlc&$|4mCt6&j9MO$R1s}@AJ{)j%Q*4e6zNbgMz z5H%#>3a|Kx?+IebQkJZ-P1Q+PV5|fI(`q~F?o@<;30j(taRzr>-IxAz&%Nh<-+bqM z=iKd)4D?8Biv+@IRr5ppTY|&N-0M^9%*+b}H3(TUkbciJ+Y?21Bj`r3TmN*5T?~=# z?B7C>er9IIO?@gs?G$e%YVRWC_6PICLTUOm$2YYQAK>3!k4-<|Wlsl@^roYRmN%`S z%_L5Ny0b5hYoYncBM#~Rx@+DVz{S^yefUpmQmXWE*{}3OgS|_Q_=z1;RK^#EZN%^+ z{D#4pI!hP%v$V!tWw6~qm~mn9tHC6Ib=i(ZmM424%-_9-a;YuY7rIUf) z_XpG^XSRwM`jaDgZm2khW0liMC=ct9U>_FmkgJ?R;aQ*hChi6+1G+4B=|+H zOEx}~_2yoa7Eq5FfcsoE@f_FKnyoOH6@<#eIu53^Hf`u-sufQw?3}(F>F2{xC7DA8D~H0))IGkoV4hcyWUyMK)&169A0)WK6sgaIYsznU>~GjV72GN`ixN|6Im zE?s4QmT6^ml3*u?+=>CUwRc)4vhsL3Xd(oc#V@OTQXn~^7%qOkI$z|(g#okrNtlR;-r!2g{jye47}l^>h*zCA9WrH8NuoP zWG9rnfPa2am6OYRM6bmaBdMxd56iXjlaJ|ie5cDLn-?@75f!Cn6gs8}UBT=~%$EE@ zFB-bFvC!M{{Y5e`c#%1EaxbJ0YjL%y%OtS!xFri{PT+SN>9fqP&ykWY)#{ddnYR(T zOBEb$D4b_gy-Kxvz>olc@r0>L27LjytxI-N0Txy8|$FXymi6jz{gePGR${Gx>anr-E};S8FGlN+%DQN{Z`)-b;?knAJ_sZO%B z)l3?>b!D;+dyG|Py(`R*LA_ZTEU7tf6`b9_3g@T6kFja&vGmPFP?I)H;zjo3w?2DI zl(`AwPgmKcFDoR8MJWyU$lezMznEC}KTB!yZ10DHG~gC+H)@_kD+1p{CCE0PG`tIK z{^s&{sE2-WYtacrb{LrLiH?m*a5YX?H-T#%Q5nFRo=_S-*L)^>L;1USQt%a1-@q-?CC1AbdN;41uP~i9ugCPNbpNwjxKpI%?xJZEuK(V>D-okdD z+|uKO;#Sqs6I3O&w+%d#h($ diff --git a/source/dungeon/DungeonStitcher.py b/source/dungeon/DungeonStitcher.py index 29a2c32f..d7ce3c4f 100644 --- a/source/dungeon/DungeonStitcher.py +++ b/source/dungeon/DungeonStitcher.py @@ -333,13 +333,13 @@ def determine_paths_for_dungeon(world, player, all_regions, name): if portal.destination: paths.append(portal.door.entrance.parent_region.name) if world.mode[player] == 'standard' and name == 'Hyrule Castle Dungeon': - paths.append('Hyrule Dungeon Cellblock') - paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room')) + paths.append(world.default_zelda_region[player]) + paths.append((world.default_zelda_region[player], 'Hyrule Castle Throne Room')) entrance = next(x for x in world.dungeon_portals[player] if x.name == 'Hyrule Castle South') # todo: in non-er, we can use the other portals too - paths.append(('Hyrule Dungeon Cellblock', entrance.door.entrance.parent_region.name)) + paths.append((world.default_zelda_region[player], entrance.door.entrance.parent_region.name)) paths.append(('Hyrule Castle Throne Room', [entrance.door.entrance.parent_region.name, - 'Hyrule Dungeon Cellblock'])) + world.default_zelda_region[player]])) if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town': paths.append('Thieves Attic Window') elif 'Thieves Attic Window' in all_r_names: diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index f4e89207..cc32ba8c 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -521,6 +521,21 @@ def randomize_enemies(world, player): green_mail, blue_mail, red_mail = original_table[idx] del original_table[idx] world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail] + # determine default zelda follower location + if world.mode[player] == 'standard' and world.doorShuffle[player] == 'crossed' and world.shuffle_followers[player]: + def random_zelda(): + world.default_zelda_region[player] = random.choice(['Hyrule Dungeon Cellblock', 'Thieves Blind\'s Cell']) + if world.customizer: + placements = world.customizer.get_placements() + if placements and player in placements and 'Zelda Herself' in placements[player].values(): + location = [l for (l, item) in placements[player].items() if item == 'Zelda Herself'][0] + if location == 'Suspicious Maiden': + world.default_zelda_region[player] = 'Thieves Blind\'s Cell' + else: + random_zelda() + else: + random_zelda() + def write_enemy_shuffle_settings(world, player, rom): From 52ed4cf7ded9b7e738b0c734c7ef94787cb4f294 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 7 Sep 2025 10:52:23 -0500 Subject: [PATCH 6/7] Re-fixed old man spawn on pyramid issue --- Rom.py | 2 +- data/base2current.bps | Bin 136664 -> 136651 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 42cfe59d..b70e983b 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '20e588b832011dcf15dbd31dff6955ce' +RANDOMIZERBASEHASH = '65fae75651987228878051028da066ad' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 0fe2d932963a31af760b5381bf17b8627f8c443c..d28bca6d34028f9c2ea3977ae252f75564d57ef4 100644 GIT binary patch delta 9554 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 H^K}0YTlch| delta 9605 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 A9{>OV From b8a40cf6c1f178d222452132f5a5de2527f3a1da Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 7 Sep 2025 10:59:34 -0500 Subject: [PATCH 7/7] Version bump 0.6.0.8 --- CHANGELOG.md | 6 ++++++ OverworldShuffle.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28128e2b..8e83b3b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.6.0.8 +- Re-fixed issue with Old Man spawning on pyramid +- Allowing Zelda to be in TT Prison for follower shuffle escape +- Fixed error with placing Old Man Cave in ER +- Fixed error when plando'ing followers at locations + ## 0.6.0.7 - Emergency fix for GT cutscene GFX diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3d28147b..bfe555cd 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.0.7' +version_number = '0.6.0.8' # branch indicator is intentionally different across branches version_branch = '-u'