From cf614e4dffc1ca50620e3124fdac924f40673472 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 20 Nov 2021 00:35:37 -0600 Subject: [PATCH 01/11] 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 02/11] 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 03/11] 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 04/11] 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 05/11] 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 06/11] 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 07/11] 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 08/11] 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 09/11] 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 10/11] 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 11/11] 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