From b82d647bf9229ea5c4ac887f9a97ada2b3914f9b Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 6 Nov 2021 15:51:00 -0500 Subject: [PATCH 01/33] Changed Big Bomb logic to BFS method --- BaseClasses.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 9c5f9e9c..e5e41912 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1618,7 +1618,7 @@ class Entrance(object): def can_reach_thru(self, state, start_region, ignore_underworld=False, ignore_ledges=False, allow_save_quit=False): def explore_region(region, path = []): nonlocal found - if region not in explored_regions or len(explored_regions[region]) > len(path): + if region not in explored_regions: explored_regions[region] = path for exit in region.exits: if exit.connected_region and (not ignore_ledges or exit.spot_type != 'Ledge') \ @@ -1628,20 +1628,28 @@ class Entrance(object): found = True explored_regions[self.parent_region] = path + [exit] elif not ignore_underworld or region.type == exit.connected_region.type or exit.connected_region.type not in [RegionType.Cave, RegionType.Dungeon]: - explore_region(exit.connected_region, path + [exit]) + exits_to_traverse.append(tuple((exit, path))) - found = False - explored_regions = {} - explore_region(start_region.entrances[0].parent_region) - if found: - self.temp_path = explored_regions[self.parent_region] - elif allow_save_quit: - world = self.parent_region.world if self.parent_region else None - exit = world.get_entrance('Links House S&Q', self.player) - explore_region(exit.connected_region, [exit]) + def traverse_paths(region, start_path=[]): + explore_region(region, start_path) + while not found and len(exits_to_traverse): + exit, path = exits_to_traverse.pop(0) + explore_region(exit.connected_region, path + [exit]) if found: self.temp_path = explored_regions[self.parent_region] + found = False + explored_regions = {} + exits_to_traverse = list() + traverse_paths(start_region.entrances[0].parent_region) + + if not found and allow_save_quit: + explored_regions = {} + exits_to_traverse = list() + world = self.parent_region.world if self.parent_region else None + exit = world.get_entrance('Links House S&Q', self.player) + traverse_paths(exit.connected_region, [exit]) + #TODO: Implement residual mirror portal placing for the previous leg, to be used for the final destination return found From 61f94ad9a5b355c8aa0a327e95492b9673016b3d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 7 Nov 2021 15:00:09 -0600 Subject: [PATCH 02/33] Minor change for OWG edge classification --- OWEdges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OWEdges.py b/OWEdges.py index 14849137..f13e3b32 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -1478,7 +1478,6 @@ OWExitTypes = { 'Dark Bonk Rocks Cliff Ledge Drop', 'Bomb Shop Cliff Ledge Drop', 'Hammer Bridge South Cliff Ledge Drop', - 'Ice Lake Northeast Pier Hop', 'Ice Lake Moat Bomb Jump', 'Ice Lake Area Cliff Ledge Drop', 'Ice Palace Island FAWT Ledge Drop', @@ -1623,6 +1622,7 @@ OWExitTypes = { 'Hype Cave Landing', 'Ice Lake Northeast Water Drop', 'Ice Lake Northeast Pier', + 'Ice Lake Northeast Pier Hop', 'Ice Lake Moat Water Entry', 'Ice Palace Approach', 'Ice Palace Leave', From 36a75b7c9922d90221c3f4a5c0c6078f56a97903 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 7 Nov 2021 16:25:23 -0600 Subject: [PATCH 03/33] Allow normal Link speed with Old Man if not in his cave or on WDM --- Rom.py | 2 +- asm/owrando.asm | 20 ++++++++++++++++++++ data/base2current.bps | Bin 141292 -> 141326 bytes 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index f4dd88af..05d20cdd 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'e3373be98af9d6de1cb1ab12176ecb0e' +RANDOMIZERBASEHASH = '9df10796c8a8fe07d81fc0012700934a' class JsonRom(object): diff --git a/asm/owrando.asm b/asm/owrando.asm index 283c358f..f244cb79 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -37,6 +37,10 @@ db #$b0 ; BCS to replace BEQ org $06907f ; < 3107f - sprite_prep.asm:2170 (LDA $7EF3CA) lda $8a : and.b #$40 +; override Link speed with Old Man following +org $09a32e ; < bank_09.asm:7457 (LDA.b #$0C : STA.b $5E) +jsl OWOldManSpeed + ; Dark Bonk Rocks Rain Sequence Guards (allowing Tile Swap on Dark Bonk Rocks) ;org $09c957 ; <- 4c957 ;dw #$cb5f ; matches value on Central Bonk Rocks screen @@ -162,6 +166,22 @@ OWSmithAccept: clc : rtl + sec : rtl } +OWOldManSpeed: +{ + lda $1b : beq .outdoors + lda $a0 : and #$fe : cmp #$f0 : beq .vanilla ; if in cave where you find Old Man + bra .normalspeed + .outdoors + lda $8a : cmp #$03 : beq .vanilla ; if on WDM screen + + .normalspeed + lda $5e : cmp #$0c : rtl + stz $5e : rtl + + .vanilla + lda #$0c : sta $5e ; what we wrote over + rtl +} org $aa9000 OWEdgeTransition: diff --git a/data/base2current.bps b/data/base2current.bps index be48a7a27ce7c08c10dbc3ac6211e0f580d09da1..a73808cb31a95e88023255877f8b0605f6da30ed 100644 GIT binary patch delta 257 zcmV+c0sj8%&j^mt2(U>31SizkE3-`j+zJU4wv8gQh^kzRlQ9e(lRpdwvrY^@IS7_M zPm7j%1=W+4MMDBwg0tI2mQoI@6J_R+H~=UR?z0v*oRfZ-8v_+iinE-Upb`<5ejK2k z=px>Yr10}#mu41>lTE8d13X*Ovz4n6SS6Mh3W@db3Zkw05P$}yipc};2&G=h z3~OXwYpD!{UNg9ttOEfa11~I{kIU0&eBE9asUyP6Vr644;?grU8EhC@BMgmuRN}F#}p;gtwfh0Z4!d31RvetBC|~a+zJ8&!;?V_6q8R329r+=2D4HOLOBSRVM5xL zL{{OGoJB(eE=Jn3;YF5GE2=dLpO0mof$p;l4#~GLPRWmbr-cIir$C#R0jH+`r{OrWS?## From 5c2592cfe07cbac942b4773c815c67d191ced302 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 8 Nov 2021 04:47:07 -0600 Subject: [PATCH 04/33] Fixed flute exits to be LW tiles only --- OverworldShuffle.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index cad95825..778601dd 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -723,9 +723,7 @@ def reorganize_groups(world, groups, player): def create_flute_exits(world, player): for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']): - if (not world.owMixed[player] and region.type == RegionType.LightWorld) \ - or (world.owMixed[player] and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \ - and (region.name not in world.owswaps[player][1] or region.name in world.owswaps[player][2])): + if region.type == (RegionType.LightWorld if world.mode != 'inverted' else RegionType.DarkWorld): exitname = 'Flute From ' + region.name exit = Entrance(region.player, exitname, region) exit.spot_type = 'Flute' From af675cf6f7585bd227676466436c01d7146d740c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 11:11:29 -0600 Subject: [PATCH 05/33] Improvements to hints Removed irrelevant hints in Lite/Lean ER Added Links House hint in ER --- Rom.py | 101 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/Rom.py b/Rom.py index 05d20cdd..42d990ed 100644 --- a/Rom.py +++ b/Rom.py @@ -2161,14 +2161,14 @@ def write_strings(rom, world, player, team): else: hint_count = 4 for entrance in all_entrances: - if entrance.name in entrances_to_hint: - if hint_count > 0: + if hint_count > 0: + if entrance.name in entrances_to_hint: this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(entrance.connected_region) + '.' tt[hint_locations.pop(0)] = this_hint entrances_to_hint.pop(entrance.name) hint_count -= 1 - else: - break + else: + break #Next we handle hints for randomly selected other entrances, curating the selection intelligently based on shuffle. if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: @@ -2180,9 +2180,15 @@ def write_strings(rom, world, player, team): entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) elif world.shuffle[player] == 'restricted': entrances_to_hint.update(ConnectorEntrances) - entrances_to_hint.update(OtherEntrances) + entrances_to_hint.update(ItemEntrances) + if world.shuffle[player] not in ['lite', 'lean']: + entrances_to_hint.update(ShopEntrances) + entrances_to_hint.update(OtherEntrances) + elif world.shopsanity[player]: + entrances_to_hint.update(ShopEntrances) + if world.shufflelinks[player] and world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']: + entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'}) - entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']: entrances_to_hint.update(InsanityEntrances) if world.shuffle_ganon: @@ -2254,17 +2260,15 @@ def write_strings(rom, world, player, team): else: this_hint = location + ' contains ' + hint_text(world.get_location(location, player).item) + '.' tt[hint_locations.pop(0)] = this_hint - - # Adding a guaranteed hint for the Flute in overworld shuffle. + + # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. + items_to_hint = RelevantItems.copy() if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: + # Adding a guaranteed hint for the Flute in overworld shuffle. this_location = world.find_items_not_key_only('Ocarina', player) if this_location: this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.' tt[hint_locations.pop(0)] = this_hint - - # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. - items_to_hint = RelevantItems.copy() - if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: items_to_hint.remove('Ocarina') if world.keyshuffle[player]: items_to_hint.extend(SmallKeys) @@ -2273,7 +2277,7 @@ def write_strings(rom, world, player, team): random.shuffle(items_to_hint) hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8 hint_count += 2 if world.doorShuffle[player] == 'crossed' else 0 - hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owMixed[player] else 0 + hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] != 'none' or world.owMixed[player] else 0 while hint_count > 0: this_item = items_to_hint.pop(0) this_location = world.find_items_not_key_only(this_item, player) @@ -2734,16 +2738,46 @@ DungeonEntrances = {'Eastern Palace': 'Eastern Palace', 'Desert Palace Entrance (North)': 'The northmost cave in the desert' } -OtherEntrances = {'Blinds Hideout': 'Blind\'s old house', - 'Lake Hylia Fairy': 'A cave NE of Lake Hylia', +ItemEntrances = {'Blinds Hideout': 'Blind\'s old house', + 'Chicken House': 'The chicken lady\'s house', + 'Aginahs Cave': 'The open desert cave', + 'Sahasrahlas Hut': 'The house near armos', + 'Blacksmiths Hut': 'The old smithery', + 'Sick Kids House': 'The central house in Kakariko', + 'Mini Moldorm Cave': 'The cave south of Lake Hylia', + 'Ice Rod Cave': 'The sealed cave SE Lake Hylia', + 'Library': 'The old library', + 'Potion Shop': 'The witch\'s building', + 'Dam': 'The old dam', + 'Waterfall of Wishing': 'Going behind the waterfall', + 'Bonk Rock Cave': 'The rock pile near Sanctuary', + 'Graveyard Cave': 'The graveyard ledge', + 'Checkerboard Cave': 'The NE desert ledge', + 'Cave 45': 'The ledge south of haunted grove', + 'Kings Grave': 'The northeastmost grave', + 'C-Shaped House': 'The NE house in Village of Outcasts', + 'Mire Shed': 'The western hut in the mire', + 'Spike Cave': 'The ledge cave on west dark DM', + 'Hype Cave': 'The cave south of the old bomb shop', + 'Brewery': 'The Village of Outcasts building with no door', + 'Chest Game': 'The westmost building in the Village of Outcasts', + 'Big Bomb Shop': 'The old bomb shop' + } + +ShopEntrances = {'Cave Shop (Lake Hylia)': 'The cave NW Lake Hylia', + 'Kakariko Shop': 'The old Kakariko shop', + 'Capacity Upgrade': 'The cave on the island', + 'Dark Lake Hylia Shop': 'The building NW dark Lake Hylia', + 'Dark World Shop': 'The hammer sealed building', + 'Red Shield Shop': 'The fenced in building', + 'Cave Shop (Dark Death Mountain)': 'The base of east dark DM', + 'Dark World Potion Shop': 'The building near the catfish', + 'Dark World Lumberjack Shop': 'The northmost Dark World building' + } + +OtherEntrances = {'Lake Hylia Fairy': 'A cave NE of Lake Hylia', 'Light Hype Fairy': 'The cave south of your house', 'Desert Fairy': 'The cave near the desert', - 'Chicken House': 'The chicken lady\'s house', - 'Aginahs Cave': 'The open desert cave', - 'Sahasrahlas Hut': 'The house near armos', - 'Cave Shop (Lake Hylia)': 'The cave NW Lake Hylia', - 'Blacksmiths Hut': 'The old smithery', - 'Sick Kids House': 'The central house in Kakariko', 'Lost Woods Gamble': 'A tree trunk door', 'Fortune Teller (Light)': 'A building NE of Kakariko', 'Snitch Lady (East)': 'A house guarded by a snitch', @@ -2751,49 +2785,24 @@ OtherEntrances = {'Blinds Hideout': 'Blind\'s old house', 'Bush Covered House': 'A house with an uncut lawn', 'Tavern (Front)': 'A building with a backdoor', 'Light World Bomb Hut': 'A Kakariko building with no door', - 'Kakariko Shop': 'The old Kakariko shop', - 'Mini Moldorm Cave': 'The cave south of Lake Hylia', 'Long Fairy Cave': 'The eastmost portal cave', 'Good Bee Cave': 'The open cave SE Lake Hylia', '20 Rupee Cave': 'The rock SE Lake Hylia', '50 Rupee Cave': 'The rock near the desert', - 'Ice Rod Cave': 'The sealed cave SE Lake Hylia', - 'Library': 'The old library', - 'Potion Shop': 'The witch\'s building', - 'Dam': 'The old dam', 'Lumberjack House': 'The lumberjack house', 'Lake Hylia Fortune Teller': 'The building NW Lake Hylia', 'Kakariko Gamble Game': 'The old Kakariko gambling den', - 'Waterfall of Wishing': 'Going behind the waterfall', - 'Capacity Upgrade': 'The cave on the island', - 'Bonk Rock Cave': 'The rock pile near Sanctuary', - 'Graveyard Cave': 'The graveyard ledge', - 'Checkerboard Cave': 'The NE desert ledge', - 'Cave 45': 'The ledge south of haunted grove', - 'Kings Grave': 'The northeastmost grave', 'Bonk Fairy (Light)': 'The rock pile near your home', 'Hookshot Fairy': 'The left paired cave on east DM', 'Bonk Fairy (Dark)': 'The rock pile near the old bomb shop', 'Dark Lake Hylia Fairy': 'The cave NE dark Lake Hylia', - 'C-Shaped House': 'The NE house in Village of Outcasts', 'Dark Death Mountain Fairy': 'The SW cave on dark DM', - 'Dark Lake Hylia Shop': 'The building NW dark Lake Hylia', - 'Dark World Shop': 'The hammer sealed building', - 'Red Shield Shop': 'The fenced in building', - 'Mire Shed': 'The western hut in the mire', 'East Dark World Hint': 'The dark cave near the eastmost portal', 'Dark Desert Hint': 'The cave east of the mire', - 'Spike Cave': 'The ledge cave on west dark DM', 'Palace of Darkness Hint': 'The building south of Kiki', 'Dark Lake Hylia Ledge Spike Cave': 'The rock SE dark Lake Hylia', - 'Cave Shop (Dark Death Mountain)': 'The base of east dark DM', - 'Dark World Potion Shop': 'The building near the catfish', 'Archery Game': 'The old archery game', - 'Dark World Lumberjack Shop': 'The northmost Dark World building', - 'Hype Cave': 'The cave south of the old bomb shop', - 'Brewery': 'The Village of Outcasts building with no door', 'Dark Lake Hylia Ledge Hint': 'The open cave SE dark Lake Hylia', - 'Chest Game': 'The westmost building in the Village of Outcasts', 'Dark Desert Fairy': 'The eastern hut in the mire', 'Dark Lake Hylia Ledge Fairy': 'The sealed cave SE dark Lake Hylia', 'Fortune Teller (Dark)': 'The building NE the Village of Outcasts' From 9714604da8e0d0449efa92275d6519eed1ab98a2 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 14:08:22 -0600 Subject: [PATCH 06/33] Fixed issue with Lite ER not placing some caves appropriately --- EntranceShuffle.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 34733fc2..5b3b821d 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -428,13 +428,21 @@ def link_entrances(world, player): caves = Cave_Base + (dw_dungeons if not invFlag else lw_dungeons) connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (dw_entrances if not invFlag else lw_entrances)] connect_inaccessible_regions(world, [], connector_entrances, caves, player) + if invFlag: + lw_dungeons = list(set(lw_dungeons) & set(caves)) + else: + dw_dungeons = list(set(dw_dungeons) & set(caves)) caves = list(set(Cave_Base) & set(caves)) + (lw_dungeons if not invFlag else dw_dungeons) connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (lw_entrances if not invFlag else dw_entrances)] connect_inaccessible_regions(world, connector_entrances, [], caves, player) + if not invFlag: + lw_dungeons = list(set(lw_dungeons) & set(caves)) + else: + dw_dungeons = list(set(dw_dungeons) & set(caves)) - lw_dungeons = list(set(lw_dungeons) & set(caves)) + (Old_Man_House if not invFlag else []) - dw_dungeons = list(set(dw_dungeons) & set(caves)) + ([] if not invFlag else Old_Man_House) + lw_dungeons = lw_dungeons + (Old_Man_House if not invFlag else []) + dw_dungeons = dw_dungeons + ([] if not invFlag else Old_Man_House) caves = list(set(Cave_Base) & set(caves)) + DW_Mid_Dungeon_Exits # place old man, has limited options From 3f59e93bfd82778d810aaeff676c6b1edbbaa42e Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 14:41:22 -0600 Subject: [PATCH 07/33] Added OW Tile Swaps to spoiler log --- BaseClasses.py | 27 ++++++++++++++++++++++++++- OverworldShuffle.py | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index e5e41912..157dab9a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2918,8 +2918,33 @@ class Spoiler(object): outfile.write(f'{fairy}: {bottle}\n') if self.overworlds: - # overworlds: overworld transitions; outfile.write('\n\nOverworld:\n\n') + # overworld tile swaps + swap_output = False + for player in range(1, self.world.players + 1): + if self.world.owMixed[player]: + from OverworldShuffle import tile_swap_spoiler_table + if not swap_output: + swap_output = True + outfile.write('OW Tile Swaps:\n') + outfile.write(('' if self.world.players == 1 else str('(Player ' + str(player) + ')')).ljust(11)) # player name + s = list(map(lambda x: ' ' if x not in self.world.owswaps[player][0] else 'S', [i for i in range(0x40)])) + outfile.write((tile_swap_spoiler_table + '\n\n') % ( s[0x02], s[0x07], \ + s[0x00], s[0x03], s[0x05], \ + s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], \ + s[0x0a], s[0x0f], + s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], + s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], \ + s[0x22], s[0x25], s[0x1a], s[0x1d], \ + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], \ + s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], \ + s[0x3a],s[0x3b],s[0x3c], s[0x3f], \ + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], \ + s[0x32],s[0x33],s[0x34], s[0x37], \ + s[0x30], s[0x35], + s[0x3a],s[0x3b],s[0x3c], s[0x3f])) + + # overworld transitions outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","overworlds",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","overworlds",entry['exit'])) for entry in self.overworlds.values()])) if self.entrances: diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 778601dd..a8acfa32 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1587,3 +1587,23 @@ flute_data = { 0x3c: (['South Pass Area', 'Dark South Pass Area'], 0x3c, 0x0584, 0x0ed0, 0x081e, 0x0f38, 0x0898, 0x0f45, 0x08a3, 0xfffe, 0x0002, 0x0f38, 0x0898), 0x3f: (['Octoballoon Area', 'Bomber Corner Area'], 0x3f, 0x0810, 0x0f05, 0x0e75, 0x0f67, 0x0ef3, 0x0f72, 0x0efa, 0xfffb, 0x000b, 0x0f80, 0x0ef0) } + +tile_swap_spoiler_table = \ +""" 0 1 2 3 4 5 6 7 + +---+-+---+---+-+ + 01234567 A(00)| |%s| | |%s| + +--------+ | %s +-+ %s | %s +-+ +A(00)|%s %s%s %s %s| B(08)| |%s| | |%s| +B(08)| %s %s| +-+-+-+-+-+-+-+-+ +C(10)|%s%s%s%s%s%s%s%s| C(10)|%s|%s|%s|%s|%s|%s|%s|%s| +D(18)|%s %s%s %s%s | +-+-+-+-+-+-+-+-+ +E(20)| %s %s | D(18)| |%s| |%s| | +F(28)|%s%s%s%s%s%s%s%s| | %s +-+ %s +-+ %s | +G(30)|%s %s%s%s%s %s| E(20)| |%s| |%s| | +H(38)| %s%s%s %s| +-+-+-+-+-+-+-+-+ + +--------+ F(28)|%s|%s|%s|%s|%s|%s|%s|%s| + +-+-+-+-+-+-+-+-+ + G(30)| |%s|%s|%s| |%s| + | %s +-+-+-+ %s +-+ + H(38)| |%s|%s|%s| |%s| + +---+-+-+-+---+-+""" From 1df52df5a90f239f49acde75e4ff376a85e9000f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 14:56:11 -0600 Subject: [PATCH 08/33] Fixed issue with Whirlpool Shuffle not following Polar rules --- OverworldShuffle.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index a8acfa32..31ce2905 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -197,16 +197,17 @@ def link_overworld(world, player): else: whirlpool_candidates = [[],[]] for (from_owid, from_whirlpool, from_region), (to_owid, to_whirlpool, to_region) in default_whirlpool_connections: - if world.owCrossed[player] != 'none': - whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region))) - whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region))) + if world.owCrossed[player] == 'polar' and world.owMixed[player] and from_owid == 0x55: + # connect the 2 DW whirlpools in Polar Mixed + connect_simple(world, from_whirlpool, to_region, player) + connect_simple(world, to_whirlpool, from_region, player) else: - if world.get_region(from_region, player).type == RegionType.LightWorld: + if world.owCrossed[player] != 'none' or world.get_region(from_region, player).type == RegionType.LightWorld: whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region))) else: whirlpool_candidates[1].append(tuple((from_owid, from_whirlpool, from_region))) - if world.get_region(to_region, player).type == RegionType.LightWorld: + if world.owCrossed[player] != 'none' or world.get_region(to_region, player).type == RegionType.LightWorld: whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region))) else: whirlpool_candidates[1].append(tuple((to_owid, to_whirlpool, to_region))) From 067580892f805c130303d01d6dadca3dd86aaf4c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 14:57:19 -0600 Subject: [PATCH 09/33] Version bump 0.2.2.1 --- CHANGELOG.md | 8 ++++++++ OverworldShuffle.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d13649..866c1d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +### 0.2.2.1 +- Allow normal Link speed with Old Man following if not in his cave or WDM +- Fixed issue with Flute exits not getting placed on the correct tiles +- Hints in Lite/Lean ER no longer refer to entrances that are guaranteed vanilla +- Added Links House entrance to hint candidate list in ER when it is shuffled +- Added Tile Swaps ASCII map to Spoiler Log when Tile Swap is enabled +- Fixed issue with Whirlpool Shuffle not abiding by Polar rules + ### 0.2.2.0 - Delivering Big Red Bomb is now in logic - Smith/Purple Chest have proper dynamic pathing to fix logical issues diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 31ce2905..dfce3ccb 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.2.0-u' +__version__ = '0.2.2.1-u' def link_overworld(world, player): # setup mandatory connections From b1bbf92c6d9a1f6586eba6370f4fb64a5efac0b1 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 16:13:01 -0600 Subject: [PATCH 10/33] Better approach for Links House/Bomb Shop ER hint --- Rom.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Rom.py b/Rom.py index 42d990ed..a5b95c56 100644 --- a/Rom.py +++ b/Rom.py @@ -2186,8 +2186,15 @@ def write_strings(rom, world, player, team): entrances_to_hint.update(OtherEntrances) elif world.shopsanity[player]: entrances_to_hint.update(ShopEntrances) - if world.shufflelinks[player] and world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']: - entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) + if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']: + if world.mode[player] == 'inverted' != (0x2c in world.owswaps[player][0] and world.owMixed[player]): + entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) + if world.shufflelinks[player]: + entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) + else: + entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) + if world.shufflelinks[player]: + entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'}) if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']: entrances_to_hint.update(InsanityEntrances) @@ -2760,8 +2767,7 @@ ItemEntrances = {'Blinds Hideout': 'Blind\'s old house', 'Spike Cave': 'The ledge cave on west dark DM', 'Hype Cave': 'The cave south of the old bomb shop', 'Brewery': 'The Village of Outcasts building with no door', - 'Chest Game': 'The westmost building in the Village of Outcasts', - 'Big Bomb Shop': 'The old bomb shop' + 'Chest Game': 'The westmost building in the Village of Outcasts' } ShopEntrances = {'Cave Shop (Lake Hylia)': 'The cave NW Lake Hylia', From 8c05a3bd35094548c00d3278694b9c6ce2181ac6 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 17:30:16 -0600 Subject: [PATCH 11/33] Fixed issue with Whirlpool Shuffle with Grouped OW Crossed --- OverworldShuffle.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index dfce3ccb..a25cbeff 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -153,7 +153,8 @@ def link_overworld(world, player): logging.getLogger('').debug('Crossing overworld edges') if world.owCrossed[player] in ['grouped', 'limited', 'chaos']: if world.owCrossed[player] == 'grouped': - crossed_edges = shuffle_tiles(world, tile_groups, [[],[],[]], player) + ow_crossed_tiles = [[],[],[]] + crossed_edges = shuffle_tiles(world, tile_groups, ow_crossed_tiles, player) elif world.owCrossed[player] in ['limited', 'chaos']: crossed_edges = list() crossed_candidates = list() @@ -202,12 +203,16 @@ def link_overworld(world, player): connect_simple(world, from_whirlpool, to_region, player) connect_simple(world, to_whirlpool, from_region, player) else: - if world.owCrossed[player] != 'none' or world.get_region(from_region, player).type == RegionType.LightWorld: + if (world.owCrossed[player] == 'none' and (world.get_region(from_region, player).type == RegionType.LightWorld)) \ + or (world.owCrossed[player] == 'grouped' and ((from_owid < 0x40) == (from_owid not in ow_crossed_tiles[0]))) \ + or world.owCrossed[player] not in ['none', 'grouped']: whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region))) else: whirlpool_candidates[1].append(tuple((from_owid, from_whirlpool, from_region))) - if world.owCrossed[player] != 'none' or world.get_region(to_region, player).type == RegionType.LightWorld: + if (world.owCrossed[player] == 'none' and (world.get_region(to_region, player).type == RegionType.LightWorld)) \ + or (world.owCrossed[player] == 'grouped' and ((to_owid < 0x40) == (to_owid not in ow_crossed_tiles[0]))) \ + or world.owCrossed[player] not in ['none', 'grouped']: whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region))) else: whirlpool_candidates[1].append(tuple((to_owid, to_whirlpool, to_region))) @@ -447,7 +452,7 @@ def shuffle_tiles(world, groups, result_list, player): exist_dw_regions.extend(dw_regions) # check whirlpool parity - valid_whirlpool_parity = world.owCrossed[player] != 'none' or len(set(new_results[0]) & set({0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f})) % 2 == 0 + valid_whirlpool_parity = world.owCrossed[player] not in ['none', 'grouped'] or len(set(new_results[0]) & set({0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f})) % 2 == 0 (exist_owids, exist_lw_regions, exist_dw_regions) = result_list exist_owids.extend(new_results[0]) From e31f41e7a9194f6b1b9cb66e046d8bdb64c012ff Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 17:40:31 -0600 Subject: [PATCH 12/33] Fixed issue with Whirlpool Shuffle with Grouped OW Crossed --- OverworldShuffle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index a25cbeff..a422b45a 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -204,15 +204,15 @@ def link_overworld(world, player): connect_simple(world, to_whirlpool, from_region, player) else: if (world.owCrossed[player] == 'none' and (world.get_region(from_region, player).type == RegionType.LightWorld)) \ - or (world.owCrossed[player] == 'grouped' and ((from_owid < 0x40) == (from_owid not in ow_crossed_tiles[0]))) \ - or world.owCrossed[player] not in ['none', 'grouped']: + or world.owCrossed[player] not in ['none', 'grouped'] \ + or (world.owCrossed[player] == 'grouped' and ((from_owid < 0x40) == (from_owid not in ow_crossed_tiles[0]))): whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region))) else: whirlpool_candidates[1].append(tuple((from_owid, from_whirlpool, from_region))) if (world.owCrossed[player] == 'none' and (world.get_region(to_region, player).type == RegionType.LightWorld)) \ - or (world.owCrossed[player] == 'grouped' and ((to_owid < 0x40) == (to_owid not in ow_crossed_tiles[0]))) \ - or world.owCrossed[player] not in ['none', 'grouped']: + or world.owCrossed[player] not in ['none', 'grouped'] \ + or (world.owCrossed[player] == 'grouped' and ((to_owid < 0x40) == (to_owid not in ow_crossed_tiles[0]))): whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region))) else: whirlpool_candidates[1].append(tuple((to_owid, to_whirlpool, to_region))) From 796171ab4dc882ff640a374d6d44d358bcc5c92c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 18:46:06 -0600 Subject: [PATCH 13/33] Fixed issue with Whirlpool Shuffle with Grouped OW Crossed --- OverworldShuffle.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index a422b45a..0f3b2a5e 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -197,28 +197,31 @@ def link_overworld(world, player): connect_simple(world, to_whirlpool, from_region, player) else: whirlpool_candidates = [[],[]] + world.owwhirlpools[player] = [None] * 8 for (from_owid, from_whirlpool, from_region), (to_owid, to_whirlpool, to_region) in default_whirlpool_connections: if world.owCrossed[player] == 'polar' and world.owMixed[player] and from_owid == 0x55: # connect the 2 DW whirlpools in Polar Mixed connect_simple(world, from_whirlpool, to_region, player) connect_simple(world, to_whirlpool, from_region, player) + world.owwhirlpools[player][7] = from_owid + world.owwhirlpools[player][6] = to_owid + world.spoiler.set_overworld(from_whirlpool, to_whirlpool, 'both', player) else: - if (world.owCrossed[player] == 'none' and (world.get_region(from_region, player).type == RegionType.LightWorld)) \ - or world.owCrossed[player] not in ['none', 'grouped'] \ + if ((world.owCrossed[player] == 'none' or (world.owCrossed[player] == 'polar' and not world.owMixed[player])) and (world.get_region(from_region, player).type == RegionType.LightWorld)) \ + or world.owCrossed[player] not in ['none', 'polar', 'grouped'] \ or (world.owCrossed[player] == 'grouped' and ((from_owid < 0x40) == (from_owid not in ow_crossed_tiles[0]))): whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region))) else: whirlpool_candidates[1].append(tuple((from_owid, from_whirlpool, from_region))) - if (world.owCrossed[player] == 'none' and (world.get_region(to_region, player).type == RegionType.LightWorld)) \ - or world.owCrossed[player] not in ['none', 'grouped'] \ + if ((world.owCrossed[player] == 'none' or (world.owCrossed[player] == 'polar' and not world.owMixed[player])) and (world.get_region(to_region, player).type == RegionType.LightWorld)) \ + or world.owCrossed[player] not in ['none', 'polar', 'grouped'] \ or (world.owCrossed[player] == 'grouped' and ((to_owid < 0x40) == (to_owid not in ow_crossed_tiles[0]))): whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region))) else: whirlpool_candidates[1].append(tuple((to_owid, to_whirlpool, to_region))) # shuffle happens here - world.owwhirlpools[player] = [None] * 8 whirlpool_map = [ 0x35, 0x0f, 0x15, 0x33, 0x12, 0x3f, 0x55, 0x7f ] for whirlpools in whirlpool_candidates: random.shuffle(whirlpools) From 9db079fbbe8597b232548ad5a412f27285a51b2f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 9 Nov 2021 18:57:45 -0600 Subject: [PATCH 14/33] Fixed Mystery filename to not spoil OWR --- Main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index 0b0443f0..f0b8be7c 100644 --- a/Main.py +++ b/Main.py @@ -130,8 +130,8 @@ def main(args, seed=None, fish=None): for player, name in enumerate(team, 1): world.player_names[player].append(name) logger.info('') - - if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] not in ['none', 'polar'] or world.owMixed[1] or str(world.seed).startswith('M'): + + if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] not in ['none', 'polar'] or world.owMixed[1] or str(args.outputname).startswith('M'): outfilebase = f'OR_{args.outputname if args.outputname else world.seed}' else: outfilebase = f'DR_{args.outputname if args.outputname else world.seed}' From d92f5382bddc08a639d8900d7892ba3a9f2cc242 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 11 Nov 2021 17:15:41 -0600 Subject: [PATCH 15/33] Fixed Triforce Hunt goal --- Rules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 688e63da..aebc5fd7 100644 --- a/Rules.py +++ b/Rules.py @@ -56,7 +56,8 @@ def set_rules(world, player): # require aga2 to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) elif world.goal[player] == 'triforcehunt': - add_rule(world.get_location('Murahdahla', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= int(state.world.treasure_hunt_count[player])) + if ('Murahdahla', player) in world._location_cache: + add_rule(world.get_location('Murahdahla', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= int(state.world.treasure_hunt_count[player])) # if swamp and dam have not been moved we require mirror for swamp palace if not world.swamp_patch_required[player]: From 48699036ed3fdf1f13b8298bf2968f5d73dc75a4 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 11 Nov 2021 17:33:20 -0600 Subject: [PATCH 16/33] Version bump 0.2.2.2 --- CHANGELOG.md | 5 +++++ OverworldShuffle.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 866c1d9e..d47f7c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 0.2.2.2 +- Fixed Whirlpool Shuffle with Grouped Crossed OW +- Made filename not spoil OWR in Mystery +- Fixed Triforce Hunt goal + ### 0.2.2.1 - Allow normal Link speed with Old Man following if not in his cave or WDM - Fixed issue with Flute exits not getting placed on the correct tiles diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 0f3b2a5e..65236379 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.2.1-u' +__version__ = '0.2.2.2-u' def link_overworld(world, player): # setup mandatory connections From 64bc9189c5a58543fe2b9d29a040a4481877d224 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 14 Nov 2021 17:08:50 -0600 Subject: [PATCH 17/33] Fixed Inverted Double-Swap issue --- Rom.py | 22 +++++++++++----------- data/base2current.bps | Bin 141326 -> 141325 bytes 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Rom.py b/Rom.py index a5b95c56..d17fc0c4 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '9df10796c8a8fe07d81fc0012700934a' +RANDOMIZERBASEHASH = '9dfff0f3d093eb9adce053e9773f523e' class JsonRom(object): @@ -668,20 +668,20 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # patches map data specific for OW Shuffle #inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM - inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain + inverted_buffer[0x1A] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x1B] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x22] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x3F] |= 0x2 # added C to terrain #inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM - inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain + inverted_buffer[0x5A] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x5B] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x62] |= 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x7F] |= 0x2 # added C to terrain if world.owMixed[player]: for b in world.owswaps[player][0]: # load inverted maps - inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2) + inverted_buffer[b] ^= 0x1 # set world flag rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40) @@ -2458,7 +2458,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): if world.mode[player] == 'inverted': # load inverted maps for b in range(0x00, len(inverted_buffer)): - inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2) + inverted_buffer[b] ^= 0x1 rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals rom.write_byte(snes_to_pc(0x02B34D), 0xF0) diff --git a/data/base2current.bps b/data/base2current.bps index a73808cb31a95e88023255877f8b0605f6da30ed..53c32fda396138439e597846c3178e30e8c92eb7 100644 GIT binary patch delta 2940 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 delta 2920 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 Date: Sun, 14 Nov 2021 17:12:42 -0600 Subject: [PATCH 18/33] Version bump 0.2.2.3 --- CHANGELOG.md | 4 ++++ OverworldShuffle.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d47f7c1e..f4eb0a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 0.2.2.3 +- Fixed GT entrance not being opened when Inverted and WDM is Tile Swapped +- The Goal sign is moved to the area where the hole will be (always in opposite world as starting) + ### 0.2.2.2 - Fixed Whirlpool Shuffle with Grouped Crossed OW - Made filename not spoil OWR in Mystery diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 65236379..67b2dee7 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.2.2-u' +__version__ = '0.2.2.3-u' def link_overworld(world, player): # setup mandatory connections From cf614e4dffc1ca50620e3124fdac924f40673472 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 20 Nov 2021 00:35:37 -0600 Subject: [PATCH 19/33] Fixed mirror portals getting erased when changing worlds --- Rom.py | 2 +- asm/owrando.asm | 15 +++++++++++++++ data/base2current.bps | Bin 141325 -> 141366 bytes 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index d17fc0c4..b0b92b22 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '9dfff0f3d093eb9adce053e9773f523e' +RANDOMIZERBASEHASH = 'f51b9d3d5d995d55c111af82570d8f04' class JsonRom(object): diff --git a/asm/owrando.asm b/asm/owrando.asm index f244cb79..b781b81b 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -391,6 +391,21 @@ OWWorldUpdate: ; x = owid of destination screen { lda.l OWTileWorldAssoc,x : cmp.l $7ef3ca : beq .return sta.l $7ef3ca ; change world + + ; activate mirror portal sprite + phx : cmp #0 : beq + : lda #1 + + cmp.l InvertedMode : bne + + lda #$09 : pha : bra .setPortalSpriteState + + lda #$07 : pha ; some state that allows the portal to persist without it getting overwritten + + .setPortalSpriteState + ldx #$0f + - lda $0e20,x : cmp #$6C : bne + + pla : sta $0dd0,x : plx : bra .playSfx + + dex : bpl - + pla : plx + + .playSfx lda #$38 : sta $012f ; play sfx - #$3b is an alternative ; toggle bunny mode diff --git a/data/base2current.bps b/data/base2current.bps index 53c32fda396138439e597846c3178e30e8c92eb7..d4ddf56e4c0c0a29fbfd6a8a749dc80a2867b1d2 100644 GIT binary patch delta 178 zcmV;j08Rgm&31i|}01cOZhw@m>7f&~H%fS1q)0Th38kIM6Y+Q|U$0;vJd zN`Nqq#g#~a1E~i{mO4k8{cO+&Xr0gv`hW$>5btREsW^=<0k2o{e$XwiN`NrX2$yGJ zrGbE(LJy{$NkGX!&=aLyk_errF?pt)fDNTx$rsQ9p9_F*-gJ72wk9i`pA0~f*LiXi gfU8nel$&!|grA2y2LZP`2Lj3<2$31aS_I@q7f&~Hfm(d0R6h%po%JY7yIE^m>k|na4 zEw4&|Fwh8>XJMs*fSYa(rmaap$w1H(rCrGc@C2)0c&43z4W(Vl7tjKq3xF5kAbN?m ZTtvBtQU?LIQU?OuAPAmp{iQKgVjp Date: Mon, 22 Nov 2021 09:08:16 -0600 Subject: [PATCH 20/33] Fix mirror portals getting erased when changing worlds --- Rom.py | 2 +- data/base2current.bps | Bin 141366 -> 141347 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index b0b92b22..a86cde36 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f51b9d3d5d995d55c111af82570d8f04' +RANDOMIZERBASEHASH = '1a9145bd70bf9b28f7a3fe7ba38fc7ab' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index d4ddf56e4c0c0a29fbfd6a8a749dc80a2867b1d2..e22c046f7e4cbaa92de854c693494b074c8fad6c 100644 GIT binary patch delta 302 zcmV+}0nz@p&31c?N8`?E~}^8*VA(v>1#h^p%9bb+%%1*if6xU=gALIeTG zlP(Om8I*{s>OhE{BA%F_B5=o~mK{f$>lId#iFC9644o|lBMP#Uo;o`Navbur+&XW_ z0qvJf0|6XRn*eBvy#Z^l%JY8E0c&Wf-bib&1(Tb#1tOj40&A}ZP=Cn*@C1+8nW+Ge z22g)%mOu!Iq0j_ey|)VP00NhprSJ@;^2tEZ2B`s)s;?tPm#YH-Bm!Tzm(c?OLIF^> z6a)dg0s-}x(FOq&MM;m!^M0u~jV}R`C9;_31i|}01hY*6^8*4H)w4?lr~(0_v-Af-1O>B*s$7ecK@1&} zPYec=UJQ@{f0OSF7Xe?h2MwJq0~rjAlczd6185%1v*kK($Y5=N0Ba&_>H@C>fT}40 z&uUGSa&@Hb@fH2SqmuF$6fqPR6Qx~} y2%V=fd8VC!4W(Vl7tjKq3xIFlbb5&dwk9i`hd&1aw?790+#m>;;-!mI0wL1bfsw!f From 62d6512e9fef560adfdde4c49f032c5546ad64d0 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 22 Nov 2021 09:41:02 -0600 Subject: [PATCH 21/33] Fix mirror portals getting erased when changing worlds --- asm/owrando.asm | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/asm/owrando.asm b/asm/owrando.asm index b781b81b..0fc9716a 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -14,6 +14,9 @@ jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA ;org $02e238 ;LDX #$9E : - DEX : DEX : CMP $DAEE,X : BNE - ;jsl OWSpecialTransition : nop #5 +org $05af75 +jsl OWPreserveMirrorSprite : nop #2 ; LDA $7EF3CA : BNE $05AFDF + ; whirlpool shuffle cross world change org $02b3bd jsl OWWhirlpoolUpdate ;JSL $02EA6C @@ -141,6 +144,19 @@ OWWhirlpoolUpdate: rtl } +OWPreserveMirrorSprite: +{ + lda.l OWMode+1 : and.b #!FLAG_OW_CROSSED : beq .vanilla + rtl ; if OW Crossed, skip world check and continue + .vanilla + lda $7ef3ca : bne .deleteMirror + rtl + + .deleteMirror + pla : lda #$de : pha ; in vanilla, if in dark world, jump to $05afdf + rtl +} + OWFluteCancel: { lda.l OWFlags+1 : and #$01 : bne + @@ -391,25 +407,10 @@ OWWorldUpdate: ; x = owid of destination screen { lda.l OWTileWorldAssoc,x : cmp.l $7ef3ca : beq .return sta.l $7ef3ca ; change world - - ; activate mirror portal sprite - phx : cmp #0 : beq + : lda #1 - + cmp.l InvertedMode : bne + - lda #$09 : pha : bra .setPortalSpriteState - + lda #$07 : pha ; some state that allows the portal to persist without it getting overwritten - - .setPortalSpriteState - ldx #$0f - - lda $0e20,x : cmp #$6C : bne + - pla : sta $0dd0,x : plx : bra .playSfx - + dex : bpl - - pla : plx - - .playSfx lda #$38 : sta $012f ; play sfx - #$3b is an alternative ; toggle bunny mode - + lda $7ef357 : bne .nobunny + lda $7ef357 : bne .nobunny lda.l InvertedMode : bne .inverted lda $7ef3ca : and.b #$40 : bra + .inverted lda $7ef3ca : and.b #$40 : eor #$40 From a563b930cc917c3739a0ffdd46ef6911bacf273c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 23 Nov 2021 22:32:17 -0600 Subject: [PATCH 22/33] Modeled mirror portal re-entry logic with followers --- BaseClasses.py | 154 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 126 insertions(+), 28 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 157dab9a..3bb4f8b3 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1584,16 +1584,16 @@ class Entrance(object): self.temp_path = [] def can_reach(self, state): - # Destination Pickup OW Only No Ledges Can S&Q - multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False), - 'Missing Smith': ('Frog', True, False, True), - 'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True) } + # 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) } if self.name in multi_step_locations: if self not in state.path: - world = self.parent_region.world if self.parent_region else None + world = self.parent_region.world step_location = world.get_location(multi_step_locations[self.name][0], self.player) - if step_location.can_reach(state) and self.can_reach_thru(state, step_location.parent_region, multi_step_locations[self.name][1], multi_step_locations[self.name][2], multi_step_locations[self.name][3]) and self.access_rule(state): + if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_locations[self.name][1], multi_step_locations[self.name][2], multi_step_locations[self.name][3], multi_step_locations[self.name][4]) and self.access_rule(state): if not self in state.path: path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None)) item_name = step_location.item.name if step_location.item else 'Pick Up Item' @@ -1615,8 +1615,8 @@ class Entrance(object): return False - def can_reach_thru(self, state, start_region, ignore_underworld=False, ignore_ledges=False, allow_save_quit=False): - def explore_region(region, path = []): + def can_reach_thru(self, state, step_location, ignore_underworld=False, ignore_ledges=False, allow_save_quit=False, allow_mirror_reentry=False): + def explore_region(region, destination, path = []): nonlocal found if region not in explored_regions: explored_regions[region] = path @@ -1624,34 +1624,132 @@ class Entrance(object): if exit.connected_region and (not ignore_ledges or exit.spot_type != 'Ledge') \ and exit.connected_region.name not in ['Dig Game Area'] \ and exit.access_rule(state): - if exit.connected_region == self.parent_region: + if exit.connected_region == destination: found = True - explored_regions[self.parent_region] = path + [exit] + explored_regions[destination] = path + [exit] elif not ignore_underworld or region.type == exit.connected_region.type or exit.connected_region.type not in [RegionType.Cave, RegionType.Dungeon]: exits_to_traverse.append(tuple((exit, path))) - def traverse_paths(region, start_path=[]): - explore_region(region, start_path) - while not found and len(exits_to_traverse): - exit, path = exits_to_traverse.pop(0) - explore_region(exit.connected_region, path + [exit]) - if found: - self.temp_path = explored_regions[self.parent_region] - - found = False - explored_regions = {} - exits_to_traverse = list() - traverse_paths(start_region.entrances[0].parent_region) - - if not found and allow_save_quit: + def traverse_paths(region, destination, start_path=[]): + nonlocal explored_regions, exits_to_traverse explored_regions = {} exits_to_traverse = list() - world = self.parent_region.world if self.parent_region else None - exit = world.get_entrance('Links House S&Q', self.player) - traverse_paths(exit.connected_region, [exit]) + explore_region(region, destination, start_path) + while not found and len(exits_to_traverse): + exit, path = exits_to_traverse.pop(0) + explore_region(exit.connected_region, destination, path + [exit]) + if found: + self.temp_path = explored_regions[destination] - #TODO: Implement residual mirror portal placing for the previous leg, to be used for the final destination + start_region = step_location.parent_region + explored_regions = {} + exits_to_traverse = list() + found = False + + if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player): + # check for path using mirror portal re-entry at location of the follower pickup + # this is checked first as this often the shortest path + follower_region = start_region + if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: + follower_region = start_region.entrances[0].parent_region + if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): + from OWEdges import OWTileRegions + from OverworldShuffle import ow_connections + owid = OWTileRegions[follower_region.name] + (mirror_map, other_world) = ow_connections[owid % 0x40] + mirror_map.extend(other_world) + mirror_exit = None + while len(mirror_map): + if mirror_map[0][1] == follower_region.name: + mirror_exit = mirror_map[0][0] + break + mirror_map.pop(0) + if mirror_exit: + mirror_region = follower_region.world.get_entrance(mirror_exit, self.player).parent_region + if mirror_region.can_reach(state): + traverse_paths(mirror_region, self.parent_region) + if found: + path = state.path.get(mirror_region, (mirror_region.name, None)) + path = (follower_region.name, (mirror_exit, path)) + item_name = step_location.item.name if step_location.item else 'Pick Up Item' + if start_region.name != follower_region.name: + path = (start_region.name, (start_region.entrances[0].name, path)) + path = (f'{step_location.parent_region.name} Exit', ('Leave Item Area', (item_name, path))) + else: + path = (item_name, path) + path = ('Use Mirror Portal', (follower_region.name, path)) + while len(self.temp_path): + exit = self.temp_path.pop(0) + path = (exit.name, (exit.parent_region.name, path)) + item_name = self.connected_region.locations[0].item.name if self.connected_region.locations[0].item else 'Deliver Item' + path = (self.parent_region.name, path) + state.path[self] = (self.name, path) + + if not found: + # check normal paths + traverse_paths(start_region.entrances[0].parent_region, self.parent_region) + if not found and allow_save_quit: + # check paths involving save and quit + exit = self.parent_region.world.get_entrance('Links House S&Q', self.player) + traverse_paths(exit.connected_region, self.parent_region, [exit]) + + if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player): + # check for paths using mirror portal re-entry at location of final destination + # this is checked last as this is the most complicated/exhaustive check + follower_region = start_region + if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: + follower_region = start_region.entrances[0].parent_region + if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): + dest_region = self.parent_region + if dest_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: + dest_region = start_region.entrances[0].parent_region + if (dest_region.world.mode[self.player] != 'inverted') != (dest_region.type == RegionType.LightWorld): + from OWEdges import OWTileRegions + from OverworldShuffle import ow_connections + owid = OWTileRegions[dest_region.name] + (mirror_map, other_world) = ow_connections[owid % 0x40] + mirror_map.extend(other_world) + mirror_map = [(x, d) for (x, d) in mirror_map if x in [e.name for e in dest_region.exits]] + # loop thru potential places to leave a mirror portal + while len(mirror_map) and not found: + mirror_exit = dest_region.world.get_entrance(mirror_map[0][0], self.player) + if mirror_exit.connected_region.type != dest_region.type: + # find path from placed mirror portal to the follower pickup + from Items import ItemFactory + mirror_item = ItemFactory('Magic Mirror', self.player) + while state.prog_items['Magic Mirror', self.player]: + state.remove(mirror_item) + temp_ignore_ledges = ignore_ledges + ignore_ledges = False + traverse_paths(mirror_exit.connected_region, start_region) + ignore_ledges = temp_ignore_ledges + state.collect(mirror_item, True) + if found: + path_to_pickup = self.temp_path + # find path from follower pickup to placed mirror portal + found = False + state.remove(mirror_item) + traverse_paths(follower_region, mirror_exit.connected_region) + state.collect(mirror_item, True) + mirror_map.pop(0) + if found: + path = state.path.get(self.parent_region, (self.parent_region.name, None)) + path = (mirror_exit.name, path) + + while len(path_to_pickup): + exit = path_to_pickup.pop(0) + path = (exit.name, (exit.parent_region.name, path)) + item_name = step_location.item.name if step_location.item else 'Pick Up Item' + path = (f'{step_location.parent_region.name} Exit', (item_name, path)) + + while len(self.temp_path): + exit = self.temp_path.pop(0) + path = (exit.name, (exit.parent_region.name, path)) + path = ('Use Mirror Portal', (mirror_exit.connected_region.name, path)) + path = (self.parent_region.name, path) + state.path[self] = (self.name, path) + return found def connect(self, region, addresses=None, target=None, vanilla=None): From b8855efe76c31cf37a8f50970fe797e1a2964b86 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 23 Nov 2021 22:33:39 -0600 Subject: [PATCH 23/33] Fixed incorrect mirror rule --- Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index aebc5fd7..535eb6ec 100644 --- a/Rules.py +++ b/Rules.py @@ -717,7 +717,7 @@ def default_rules(world, player): # Underworld Logic set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up - set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Magic Mirror', player)) # can erase block set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling From 6506f6d0777c46512bd8f14443e8d8e02eb980e4 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 00:47:46 -0600 Subject: [PATCH 24/33] Minor tweak to Big Bomb Shop showing up in spoiler --- Main.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Main.py b/Main.py index f0b8be7c..40e73b57 100644 --- a/Main.py +++ b/Main.py @@ -567,7 +567,7 @@ def create_playthrough(world): # get locations containing progress items prog_locations = [location for location in world.get_filled_locations() if location.item.advancement] - optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Big Bomb'] + optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop'] state_cache = [None] collection_spheres = [] state = CollectionState(world) @@ -674,9 +674,6 @@ def create_playthrough(world): old_world.spoiler.paths = dict() for player in range(1, world.players + 1): old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player}) - for path in dict(old_world.spoiler.paths).values(): - if any(exit == 'Pyramid Fairy' for (_, exit) in path): - old_world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player)) # we can finally output our playthrough old_world.spoiler.playthrough = {"0": [str(item) for item in world.precollected_items if item.advancement]} From df6ea16f3abb0545e43ae0faaeee767f9a9b930c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 02:52:18 -0600 Subject: [PATCH 25/33] Minor tweak to Big Bomb Shop showing up in spoiler --- BaseClasses.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 3bb4f8b3..3f567ba0 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2734,6 +2734,8 @@ class Spoiler(object): self.shops = [] self.bosses = OrderedDict() + self.suppress_spoiler_locations = ['Big Bomb', 'Dark Blacksmith Ruins', 'Frog', 'Middle Aged Man'] + def set_overworld(self, entrance, exit, direction, player): if self.world.players == 1: self.overworlds[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]) @@ -2921,7 +2923,7 @@ class Spoiler(object): if self.shops: out['Shops'] = self.shops out['playthrough'] = self.playthrough - out['paths'] = self.paths + out['paths'] = {l:p for (l, p) in self.paths if l not in self.suppress_spoiler_locations} out['Bosses'] = self.bosses out['meta'] = self.metadata @@ -3107,13 +3109,14 @@ class Spoiler(object): outfile.write('\n\nPaths:\n\n') path_listings = [] for location, path in sorted(self.paths.items()): - path_lines = [] - for region, exit in path: - if exit is not None: - path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit))) - else: - path_lines.append(self.world.fish.translate("meta","rooms",region)) - path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines))) + if location not in self.suppress_spoiler_locations: + path_lines = [] + for region, exit in path: + if exit is not None: + path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit))) + else: + path_lines.append(self.world.fish.translate("meta","rooms",region)) + path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines))) outfile.write('\n'.join(path_listings)) From f06cee8927dc6ec00a96afd01bb298ba8a041ad8 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 02:54:16 -0600 Subject: [PATCH 26/33] Restructuring spoiler output to support future types of map text output --- BaseClasses.py | 39 +++++++++++++++--------------------- OverworldShuffle.py | 48 +++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 3f567ba0..27f37d9a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2719,6 +2719,7 @@ class Spoiler(object): self.world = world self.hashes = {} self.overworlds = {} + self.maps = {} self.entrances = {} self.doors = {} self.doorTypes = {} @@ -2742,6 +2743,14 @@ class Spoiler(object): else: self.overworlds[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)]) + def set_map(self, type, text, data, player): + if type not in self.maps: + self.maps[type] = {} + if self.world.players == 1: + self.maps[type][player] = OrderedDict([('text', text), ('data', data)]) + else: + self.maps[type][player] = OrderedDict([('player', player), ('text', text), ('data', data)]) + def set_entrance(self, entrance, exit, direction, player): if self.world.players == 1: self.entrances[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]) @@ -2910,6 +2919,7 @@ class Spoiler(object): self.parse_data() out = OrderedDict() out['Overworld'] = list(self.overworlds.values()) + out['Maps'] = list(self.maps.values()) out['Entrances'] = list(self.entrances.values()) out['Doors'] = list(self.doors.values()) out['Lobbies'] = list(self.lobbies.values()) @@ -3020,29 +3030,12 @@ class Spoiler(object): if self.overworlds: outfile.write('\n\nOverworld:\n\n') # overworld tile swaps - swap_output = False - for player in range(1, self.world.players + 1): - if self.world.owMixed[player]: - from OverworldShuffle import tile_swap_spoiler_table - if not swap_output: - swap_output = True - outfile.write('OW Tile Swaps:\n') - outfile.write(('' if self.world.players == 1 else str('(Player ' + str(player) + ')')).ljust(11)) # player name - s = list(map(lambda x: ' ' if x not in self.world.owswaps[player][0] else 'S', [i for i in range(0x40)])) - outfile.write((tile_swap_spoiler_table + '\n\n') % ( s[0x02], s[0x07], \ - s[0x00], s[0x03], s[0x05], \ - s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], \ - s[0x0a], s[0x0f], - s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], - s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], \ - s[0x22], s[0x25], s[0x1a], s[0x1d], \ - s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], \ - s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], \ - s[0x3a],s[0x3b],s[0x3c], s[0x3f], \ - s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], \ - s[0x32],s[0x33],s[0x34], s[0x37], \ - s[0x30], s[0x35], - s[0x3a],s[0x3b],s[0x3c], s[0x3f])) + if self.maps['swaps']: + outfile.write('OW Tile Swaps:\n') + for player in self.maps['swaps']: + if self.world.players > 1: + outfile.write(str('(Player ' + str(player) + ')\n')) # player name + outfile.write(self.maps['swaps'][player]['text'] + '\n\n') # overworld transitions outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","overworlds",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","overworlds",entry['exit'])) for entry in self.overworlds.values()])) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 67b2dee7..371686c5 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -137,6 +137,24 @@ def link_overworld(world, player): assert len(swapped_edges) == 0, 'Not all edges were swapped successfully: ' + ', '.join(swapped_edges ) update_world_regions(world, player) + + # update spoiler + s = list(map(lambda x: ' ' if x not in world.owswaps[player][0] else 'S', [i for i in range(0x40)])) + text_output = tile_swap_spoiler_table.replace('s', '%s') % ( s[0x02], s[0x07], + s[0x00], s[0x03], s[0x05], + s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], + s[0x0a], s[0x0f], + s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], + s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], + s[0x22], s[0x25], s[0x1a], s[0x1d], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], + s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], + s[0x3a],s[0x3b],s[0x3c], s[0x3f], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], + s[0x32],s[0x33],s[0x34], s[0x37], + s[0x30], s[0x35], + s[0x3a],s[0x3b],s[0x3c], s[0x3f]) + world.spoiler.set_map('swaps', text_output, world.owswaps[player][0], player) # apply tile logical connections for owid in ow_connections.keys(): @@ -1598,21 +1616,21 @@ flute_data = { } tile_swap_spoiler_table = \ -""" 0 1 2 3 4 5 6 7 +""" 0 1 2 3 4 5 6 7 +---+-+---+---+-+ - 01234567 A(00)| |%s| | |%s| - +--------+ | %s +-+ %s | %s +-+ -A(00)|%s %s%s %s %s| B(08)| |%s| | |%s| -B(08)| %s %s| +-+-+-+-+-+-+-+-+ -C(10)|%s%s%s%s%s%s%s%s| C(10)|%s|%s|%s|%s|%s|%s|%s|%s| -D(18)|%s %s%s %s%s | +-+-+-+-+-+-+-+-+ -E(20)| %s %s | D(18)| |%s| |%s| | -F(28)|%s%s%s%s%s%s%s%s| | %s +-+ %s +-+ %s | -G(30)|%s %s%s%s%s %s| E(20)| |%s| |%s| | -H(38)| %s%s%s %s| +-+-+-+-+-+-+-+-+ - +--------+ F(28)|%s|%s|%s|%s|%s|%s|%s|%s| + 01234567 A(00)| |s| | |s| + +--------+ | s +-+ s | s +-+ +A(00)|s ss s s| B(08)| |s| | |s| +B(08)| s s| +-+-+-+-+-+-+-+-+ +C(10)|ssssssss| C(10)|s|s|s|s|s|s|s|s| +D(18)|s ss ss | +-+-+-+-+-+-+-+-+ +E(20)| s s | D(18)| |s| |s| | +F(28)|ssssssss| | s +-+ s +-+ s | +G(30)|s ssss s| E(20)| |s| |s| | +H(38)| sss s| +-+-+-+-+-+-+-+-+ + +--------+ F(28)|s|s|s|s|s|s|s|s| +-+-+-+-+-+-+-+-+ - G(30)| |%s|%s|%s| |%s| - | %s +-+-+-+ %s +-+ - H(38)| |%s|%s|%s| |%s| + G(30)| |s|s|s| |s| + | s +-+-+-+ s +-+ + H(38)| |s|s|s| |s| +---+-+-+-+---+-+""" From 043a108577aee52c7015e861fc0b2b3b44d2aafd Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 03:14:23 -0600 Subject: [PATCH 27/33] Restructuring spoiler output to support future types of map text output --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 27f37d9a..dc447748 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3030,7 +3030,7 @@ class Spoiler(object): if self.overworlds: outfile.write('\n\nOverworld:\n\n') # overworld tile swaps - if self.maps['swaps']: + if 'swaps' in self.maps: outfile.write('OW Tile Swaps:\n') for player in self.maps['swaps']: if self.world.players > 1: From ae5e1cc5114d03663c233b68a1d98d280474aff1 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 03:33:10 -0600 Subject: [PATCH 28/33] Omit displaying identical paths in spoiler log --- BaseClasses.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BaseClasses.py b/BaseClasses.py index dc447748..7d994704 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3101,7 +3101,13 @@ class Spoiler(object): # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name outfile.write('\n\nPaths:\n\n') path_listings = [] + displayed_regions = [] for location, path in sorted(self.paths.items()): + if self.world.players == 1: + region = self.world.get_location(location, 1).parent_region + if region.name in displayed_regions: + continue + displayed_regions.append(region.name) if location not in self.suppress_spoiler_locations: path_lines = [] for region, exit in path: From 0795eb50c1e70838eda9910442f201c23b7e86eb Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 04:23:23 -0600 Subject: [PATCH 29/33] Version bump 0.2.3.0 --- CHANGELOG.md | 6 ++++++ OverworldShuffle.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4eb0a11..65011c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +### 0.2.3.0 +- Fixed issue in Crossed OW where mirror portal sprites would disappear when changing worlds +- Added Big Red Bomb logic to support using residual mirror portals for later re-entry +- Suppressed irrelevant paths in spoiler playthru +- Suppressed identical paths in spoiler playthru if multiple locations within the same region contain progression + ### 0.2.2.3 - Fixed GT entrance not being opened when Inverted and WDM is Tile Swapped - The Goal sign is moved to the area where the hole will be (always in opposite world as starting) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 371686c5..3d7142e6 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.2.3-u' +__version__ = '0.2.3.0-u' def link_overworld(world, player): # setup mandatory connections From 3ef1f4bb52b13b19328ae6639516b5b2a2501b19 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 12:31:58 -0600 Subject: [PATCH 30/33] Omit displaying identical paths in spoiler log --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 7d994704..ad51f12d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3104,7 +3104,7 @@ class Spoiler(object): displayed_regions = [] for location, path in sorted(self.paths.items()): if self.world.players == 1: - region = self.world.get_location(location, 1).parent_region + region = self.world.get_location(location.split(' @', 1)[0], 1).parent_region if region.name in displayed_regions: continue displayed_regions.append(region.name) From 1ca4905089f37cc0e9e0e8fe7e40ae8829eda6dd Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 12:38:35 -0600 Subject: [PATCH 31/33] Version bump 0.2.3.1 --- CHANGELOG.md | 2 +- OverworldShuffle.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65011c9a..99c25c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -### 0.2.3.0 +### 0.2.3.0/1 - Fixed issue in Crossed OW where mirror portal sprites would disappear when changing worlds - Added Big Red Bomb logic to support using residual mirror portals for later re-entry - Suppressed irrelevant paths in spoiler playthru diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3d7142e6..25a49111 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.3.0-u' +__version__ = '0.2.3.1-u' def link_overworld(world, player): # setup mandatory connections From 7ba0f97b23851ad4eedaeb5cab3e1b02ef35d3e7 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 15:56:20 -0600 Subject: [PATCH 32/33] Fix mirror portals getting erased when changing worlds --- Rom.py | 9 ++++----- data/base2current.bps | Bin 141347 -> 141365 bytes 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Rom.py b/Rom.py index a86cde36..30729de4 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '1a9145bd70bf9b28f7a3fe7ba38fc7ab' +RANDOMIZERBASEHASH = '383fbac5f195f82d2b74b62113229fac' class JsonRom(object): @@ -1432,10 +1432,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier - rom.write_byte(0x2AF79, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) - rom.write_byte(0x3A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) - rom.write_byte(0x3A96D, 0xF0 if world.mode[player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) - rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) + rom.write_byte(0x03A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) + rom.write_byte(0x03A96D, 0xF0 if world.mode[player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) + rom.write_byte(0x03A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) diff --git a/data/base2current.bps b/data/base2current.bps index e22c046f7e4cbaa92de854c693494b074c8fad6c..db61bf4dc810344f4da2cd6d315433687ca0d576 100644 GIT binary patch delta 205 zcmV;;05boh&31Ze%20<%p4rUn7Uv-Af-1O?TIs$7ecK@1&}PYec=UJQ@{ zp_A_n7Xg8j2Ms9^E0~FOu%O_LB6YB!bby;uo3lU-HTGPA0hdXAqnQV<%JY8k2$Kz9 zfRAvg4S=cMNNcYJfT}40&;=r$=>lu722g*=0q_JOo#_Ip0FMSxe`}UN2#BH31YEth z3hn>`ml!mqrSJ@;^2tEZ2B`s)s;@nlzykp#0V|j00|7$;YKJNW0k31c?N8`?E~}rUn7Hv-Af-1O>>5s$7ecK@1&}PYec=UJQ@{ zl#}la7XfsW2Ms9@6IzguIiuW<3%7KDn`}9evq23t_Dj71Yp=@le$WAHXsO;vYp(^9 zo3#ZZo#_H=uLe+m$pP>LkJy>10FMSxe`}UN2#BH31YEth3hn>`mzkyT45jkPK+p!M p0h6k)BbU+x0VM%nm-qt#Ljh2SJOlx^JOl#qBM2s~iL&bp&qLw#OfUcd From c883690ea37ea0b616b6cde51f6dd4da1b12f75c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 24 Nov 2021 15:57:25 -0600 Subject: [PATCH 33/33] Version bump 0.2.3.2 --- CHANGELOG.md | 2 +- OverworldShuffle.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99c25c1e..93f9b2f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -### 0.2.3.0/1 +### 0.2.3.0/1/2 - Fixed issue in Crossed OW where mirror portal sprites would disappear when changing worlds - Added Big Red Bomb logic to support using residual mirror portals for later re-entry - Suppressed irrelevant paths in spoiler playthru diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 25a49111..f5729ab3 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.3.1-u' +__version__ = '0.2.3.2-u' def link_overworld(world, player): # setup mandatory connections