From 4db69b877933f157ce0aad21eab8583ac95e93d8 Mon Sep 17 00:00:00 2001 From: Catobat <69204835+Catobat@users.noreply.github.com> Date: Sun, 1 May 2022 01:25:32 +0200 Subject: [PATCH 01/14] Randomize Special Transitions --- BaseClasses.py | 22 ++-- OWEdges.py | 56 ++++++----- OverworldShuffle.py | 22 +--- Rom.py | 11 +- asm/owrando.asm | 227 +++++++++++++++++++++++++++++++----------- data/base2current.bps | Bin 92184 -> 92460 bytes 6 files changed, 227 insertions(+), 111 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 8a3da107..9b4fe235 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2225,7 +2225,6 @@ class Door(object): class WorldType(IntEnum): Light = 0 Dark = 1 - Special = 2 @unique @@ -2241,6 +2240,8 @@ class OWEdge(object): self.type = DoorType.Open self.direction = direction self.terrain = terrain + self.specialEntrance = False + self.specialExit = False self.deadEnd = False # rom properties @@ -2254,6 +2255,7 @@ class OWEdge(object): self.zeroHzCam = False self.zeroVtCam = False self.edge_id = edge_id + self.specialID = 0x0 self.midpoint = 0x0 self.linkOpp = 0x0 @@ -2265,12 +2267,10 @@ class OWEdge(object): self.unknownX = 0x0 self.unknownY = 0x0 - if self.owIndex < 0x40: + if self.owIndex < 0x40 or self.owIndex >= 0x80: self.worldType = WorldType.Light - elif self.owIndex < 0x80: - self.worldType = WorldType.Dark else: - self.worldType = WorldType.Special + self.worldType = WorldType.Dark # logical properties # self.connected = False # combine with Dest? @@ -2288,7 +2288,7 @@ class OWEdge(object): return base_address[self.direction] + (self.edge_id * 16) def getTarget(self): - return self.dest.edge_id + return self.dest.specialID if self.dest.specialExit else self.dest.edge_id def dead_end(self): self.deadEnd = True @@ -2298,6 +2298,16 @@ class OWEdge(object): self.vramLoc = vram_loc return self + def special_entrance(self, special_id): + self.specialEntrance = True + self.specialID = special_id + return self + + def special_exit(self, special_id): + self.specialExit = True + self.specialID = special_id + return self + def __eq__(self, other): return isinstance(other, self.__class__) and self.name == other.name diff --git a/OWEdges.py b/OWEdges.py index 95c546ad..a076f4e7 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -38,7 +38,7 @@ NP = IsParallel.No def create_owedges(world, player): edges = [ # name, owID,dir,type,edge_id,(owSlot) vram - create_owedge(player, 'Lost Woods NW', 0x00, No, Ld, 0x00) .coordInfo(0x00a0, 0x0284), + create_owedge(player, 'Lost Woods NW', 0x00, No, Ld, 0x00) .coordInfo(0x00a0, 0x0284).special_entrance(0x80), create_owedge(player, 'Lost Woods SW', 0x00, So, Ld, 0x01, 0x08).coordInfo(0x0058, 0x2000), create_owedge(player, 'Lost Woods SC', 0x00, So, Ld, 0x02, 0x08).coordInfo(0x0178, 0x2020), create_owedge(player, 'Lost Woods SE', 0x00, So, Ld, 0x03, 0x09).coordInfo(0x0388, 0x2060), @@ -53,7 +53,7 @@ def create_owedges(world, player): create_owedge(player, 'Death Mountain TR Pegs WN', 0x07, We, Ld, 0x02) .coordInfo(0x0078, 0x00e0), create_owedge(player, 'Mountain Entry NW', 0x0a, No, Ld, 0x01) .coordInfo(0x04cc, 0x180a), create_owedge(player, 'Mountain Entry SE', 0x0a, So, Ld, 0x04) .coordInfo(0x0518, 0x1012), - create_owedge(player, 'Zora Waterfall NE', 0x0f, No, Ld, 0x02) .coordInfo(0x0f80, 0x009a), + create_owedge(player, 'Zora Waterfall NE', 0x0f, No, Ld, 0x02) .coordInfo(0x0f80, 0x009a).special_entrance(0x82), create_owedge(player, 'Zora Waterfall SE', 0x0f, So, Ld, 0x05) .coordInfo(0x0f80, 0x1020), create_owedge(player, 'Lost Woods Pass NW', 0x10, No, Ld, 0x03) .coordInfo(0x0058, 0x1800), create_owedge(player, 'Lost Woods Pass NE', 0x10, No, Ld, 0x04) .coordInfo(0x0178, 0x181e), @@ -132,7 +132,7 @@ def create_owedges(world, player): create_owedge(player, 'Links House ES', 0x2c, Ea, Ld, 0x17) .coordInfo(0x0b80, 0x08c0), create_owedge(player, 'Stone Bridge NC', 0x2d, No, Ld, 0x14) .coordInfo(0x0af0, 0x180e), create_owedge(player, 'Stone Bridge SC', 0x2d, So, Ld, 0x19) .coordInfo(0x0ae0, 0x100c), - create_owedge(player, 'Stone Bridge WC', 0x2d, We, Wr, 0x17) .coordInfo(0x0b1c, 0x061c), + create_owedge(player, 'Stone Bridge WC', 0x2d, We, Wr, 0x17) .coordInfo(0x0b1c, 0x061c).special_entrance(0x81), create_owedge(player, 'Stone Bridge WS', 0x2d, We, Ld, 0x18) .coordInfo(0x0b80, 0x08e0), create_owedge(player, 'Stone Bridge EN', 0x2d, Ea, Ld, 0x18) .coordInfo(0x0a90, 0x01c0), create_owedge(player, 'Stone Bridge EC', 0x2d, Ea, Wr, 0x19) .coordInfo(0x0b3c, 0x0640), @@ -317,9 +317,9 @@ def create_owedges(world, player): create_owedge(player, 'Bomber Corner NE', 0x7f, No, Ld, 0x41) .coordInfo(0x0f50, 0x181c), create_owedge(player, 'Bomber Corner WC', 0x7f, We, Wr, 0x49) .coordInfo(0x0f30, 0x05e0), create_owedge(player, 'Bomber Corner WS', 0x7f, We, Ld, 0x4a) .coordInfo(0x0f94, 0x0860), - create_owedge(player, 'Master Sword Meadow SC', 0x80, So, Ld, 0x40) .coordInfo(0x0080, 0x0000), - create_owedge(player, 'Hobo EC', 0x80, Ea, Wr, 0x4a) .coordInfo(0x008c, 0x0020), - create_owedge(player, 'Zoras Domain SW', 0x81, So, Ld, 0x41, 0x89).coordInfo(0x02a4, 0x1782) + create_owedge(player, 'Master Sword Meadow SC', 0x80, So, Ld, 0x40) .coordInfo(0x0080, 0x0000).special_exit(0x80), + create_owedge(player, 'Hobo EC', 0x80, Ea, Wr, 0x4a) .coordInfo(0x008c, 0x0020).special_exit(0x81), + create_owedge(player, 'Zoras Domain SW', 0x81, So, Ld, 0x41, 0x89).coordInfo(0x02a4, 0x1782).special_exit(0x82) ] world.owedges += edges @@ -439,16 +439,16 @@ OWEdgeGroups = { ['Octoballoon NE'] ] ), - # (Op, LW, Vt, Ld, NP, 1): ( - # [ - # ['Master Sword Meadow SC'], - # ['Zoras Domain SW'] - # ], - # [ - # ['Lost Woods NW'], - # ['Zora Waterfall NE'] - # ] - # ), + (Op, LW, Vt, Ld, NP, 1): ( + [ + ['Master Sword Meadow SC'], + ['Zoras Domain SW'] + ], + [ + ['Lost Woods NW'], + ['Zora Waterfall NE'] + ] + ), (Op, LW, Hz, Ld, PL, 2): ( [ ['Kakariko Fortune EN', 'Kakariko Fortune ES'], @@ -505,14 +505,14 @@ OWEdgeGroups = { ['Statues WC'] ] ), - # (Op, LW, Hz, Wr, NP, 1): ( - # [ - # ['Hobo EC'] - # ], - # [ - # ['Stone Bridge WC'] - # ] - # ), + (Op, LW, Hz, Wr, NP, 1): ( + [ + ['Hobo EC'] + ], + [ + ['Stone Bridge WC'] + ] + ), (Op, LW, Vt, Wr, PL, 1): ( [ ['Tree Line SC'], @@ -627,6 +627,10 @@ OWEdgeGroups = { ['Hype Cave WN', 'Hype Cave WS'] ] ), + (Op, DW, Vt, Ld, NP, 1): ( + [ ], + [ ] + ), (Op, DW, Hz, Ld, NP, 2): ( [ ['Dig Game EC', 'Dig Game ES'] @@ -675,6 +679,10 @@ OWEdgeGroups = { ['Hype Cave WC'] ] ), + (Op, DW, Hz, Wr, NP, 1): ( + [ ], + [ ] + ), (Op, DW, Vt, Wr, PL, 1): ( [ ['Dark Tree Line SC'], diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 410ee7f8..f014769b 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -11,8 +11,6 @@ def link_overworld(world, player): # setup mandatory connections for exitname, regionname in mandatory_connections: connect_simple(world, exitname, regionname, player) - for exitname, destname in temporary_mandatory_connections: - connect_two_way(world, exitname, destname, player) def performSwap(groups, swaps): def getParallel(edgename): @@ -539,18 +537,13 @@ def shuffle_tiles(world, groups, result_list, player): exist_dw_regions.extend(new_results[2]) # replace LW edges with DW - ignore_list = list() #TODO: Remove ignore_list when special OW areas are included in pool - for edgeset in temporary_mandatory_connections: - for edge in edgeset: - ignore_list.append(edge) - if world.owCrossed[player] != 'polar': # in polar, the actual edge connections remain vanilla def getSwappedEdges(world, lst, player): for regionname in lst: region = world.get_region(regionname, player) for exit in region.exits: - if exit.spot_type == 'OWEdge' and exit.name not in ignore_list: + if exit.spot_type == 'OWEdge': swapped_edges.append(exit.name) getSwappedEdges(world, result_list[1], player) @@ -1140,13 +1133,6 @@ test_connections = [ #('Links House NE', 'Lost Woods Pass SW') ] -temporary_mandatory_connections = [ - # Special OW Areas - ('Lost Woods NW', 'Master Sword Meadow SC'), - ('Zora Waterfall NE', 'Zoras Domain SW'), - ('Stone Bridge WC', 'Hobo EC'), - ] - # these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions mandatory_connections = [# Intra-tile OW Connections ('Lost Woods Bush (West)', 'Lost Woods East Area'), #pearl @@ -1750,14 +1736,14 @@ parallelsimilar_connections = [('Maze Race ES', 'Kakariko Suburb WS'), ] # non shuffled overworld -default_connections = [#('Lost Woods NW', 'Master Sword Meadow SC'), +default_connections = [('Lost Woods NW', 'Master Sword Meadow SC'), ('Lost Woods SW', 'Lost Woods Pass NW'), ('Lost Woods SC', 'Lost Woods Pass NE'), ('Lost Woods SE', 'Kakariko Fortune NE'), ('Lost Woods EN', 'Lumberjack WN'), ('Lumberjack SW', 'Mountain Entry NW'), ('Mountain Entry SE', 'Kakariko Pond NE'), - #('Zora Waterfall NE', 'Zoras Domain SW'), + ('Zora Waterfall NE', 'Zoras Domain SW'), ('Lost Woods Pass SW', 'Kakariko NW'), ('Lost Woods Pass SE', 'Kakariko NC'), ('Kakariko Fortune SC', 'Kakariko NE'), @@ -1806,7 +1792,7 @@ default_connections = [#('Lost Woods NW', 'Master Sword Meadow SC'), ('Stone Bridge SC', 'Lake Hylia NW'), ('Stone Bridge EN', 'Tree Line WN'), ('Stone Bridge EC', 'Tree Line WC'), - #('Stone Bridge WC', 'Hobo EC'), + ('Stone Bridge WC', 'Hobo EC'), ('Tree Line SC', 'Lake Hylia NC'), ('Tree Line SE', 'Lake Hylia NE'), ('Desert EC', 'Desert Pass WC'), diff --git a/Rom.py b/Rom.py index d2bd5930..b8dceed3 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '4a1dfc4fa793b8659a95d579f6a5a925' +RANDOMIZERBASEHASH = '32da3b3eb8f7a3e43f7c7351aeeddf11' class JsonRom(object): @@ -698,17 +698,18 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): inverted_buffer[b] ^= 0x1 # set world flag - world_flag = 0x00 if b >= 0x40 else 0x40 + world_flag = 0x00 if b >= 0x40 and b < 0x80 else 0x40 rom.write_byte(0x153A00 + b, world_flag) - if b % 0x40 in megatiles: + if b & 0xBF in megatiles: rom.write_byte(0x153A00 + b + 1, world_flag) rom.write_byte(0x153A00 + b + 8, world_flag) rom.write_byte(0x153A00 + b + 9, world_flag) - + for edge in world.owedges: if edge.dest is not None and isinstance(edge.dest, OWEdge) and edge.player == player: write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc) - write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget()) + if not edge.specialExit: + rom.write_byte(0x1539e0 + (edge.specialID - 0x80) * 2 if edge.specialEntrance else edge.getAddress() + 0x0e, edge.getTarget()) write_int16(rom, 0x150002, owMode) write_int16(rom, 0x150004, owFlags) diff --git a/asm/owrando.asm b/asm/owrando.asm index 717890bd..61097756 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -9,10 +9,28 @@ OWReserved: dw 0 ;Hooks +org $02a929 +OWDetectTransitionReturn: + +org $02a939 +JSL OWDetectEdgeTransition +BCS OWDetectTransitionReturn + +org $04e8ae +JSL OWDetectSpecialTransition +RTL : NOP + org $02a999 jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA -;org $02e238 ;LDX #$9E : - DEX : DEX : CMP $DAEE,X : BNE - -;jsl OWSpecialTransition : nop #5 + +org $02e809 +JSL OWSpecialExit + +org $02bfe8 +JSL OWAdjustExitPosition + +org $02c1a9 +JSL OWEndScrollTransition org $05af75 jsl OWPreserveMirrorSprite : nop #2 ; LDA $7EF3CA : BNE $05AFDF @@ -107,10 +125,14 @@ org $1bed95 ; < ? - palettes.asm:748 () jsl.l OWWorldCheck16 : nop org $02b16e ; AND #$3F : ORA 7EF3CA -and #$7f : eor #$40 : nop #2 ; something to do with mirroring and simply toggling world to opposite one: TODO: better comment +and #$7f : eor #$40 : nop #2 ;Code org $aa8800 +OWTransitionDirection: +dw 3, 2, 1, 0 ; $02 after $02A932 +OWEdgeDataOffset: +dw OWSouthEdges, OWEastEdges, OWSouthEdges OWCoordIndex: ; Horizontal 1st db 2, 2, 0, 0 ; Coordinate Index $20-$23 OWOppCoordIndex: ; Horizontal 1st @@ -304,22 +326,88 @@ LoadMapDarkOrMixed: dw $0C00-$01F0 ; top right dw 0,0,0,0,0,0 dw $0800+$01F0 ; bottom left - dw $0400+$0210 ; bottom + dw $0400+$0210 ; bottom right } org $aa9000 +OWDetectEdgeTransition: +{ + STZ.w $06FC + LDA.l OWMode : ORA.l OWMode+1 : BEQ .normal + JSR OWShuffle + LDA.w $06FA : BMI .special + .normal + REP #$31 : LDX.b $02 : LDA.b $84 ; what we wrote over + RTL + .special + REP #$30 + AND.w #$0003 : TAY : ASL : TAX + LDA.w #$007F : STA.w $06FA + JSR OWLoadSpecialArea + SEC + RTL +} +OWDetectSpecialTransition: +{ + STZ.w $06FC + LDA.l OWMode : BEQ .normal + LDA.l OWSpecialDestIndex,X : BIT.w #$0080 : BNE .special + STA.w $06FA + LDA.l OWEdgeDataOffset,X : STA.w $06F8 + PLA : SEP #$30 : PLA ; delete 3 bytes from stack + JSL $07F413 : BCS .return ; Link_CheckForEdgeScreenTransition + LDA.l $04E879,X : STA.b $00 : CMP.b #$08 : BNE .hobo + LSR : STA.b $20 : STZ.b $E8 ; move Link and camera to edge + LDA.b #$06 : STA.b $02 + STZ.w $0418 + BRA .continue + .hobo + STA.b $02 : STA.w $0418 + ASL : STA.b $22 : STZ.b $E2 ; move Link and camera to edge + LDA.b #$0A : STA.b $23 : STA.b $E3 + .continue + STZ.b $03 + ; copied from DeleteCertainAncillaeStopDashing at $028A0E + JSL $09AC57 ; Ancilla_TerminateSelectInteractives + LDA.w $0372 : BEQ .not_dashing + STZ.b $4D : STZ.b $46 + LDA.b #$FF : STA.b $29 : STA.b $C7 + STZ.b $3D : STZ.b $5E : STZ.w $032B : STZ.w $0372 : STZ.b $5D + .not_dashing + PLA : REP #$31 : PLA ; delete 3 bytes from stack + LDX.b $02 + LDA.b $84 + JML $02A93F + .special + AND.w #$0003 : TAY : ASL : TAX + .normal + JSR OWLoadSpecialArea + .return + RTL +} OWEdgeTransition: { - php : phy - lda.l OWMode : ora.l OWMode+1 : beq + - jsl OWShuffle : bra .return - + jsl OWVanilla - .return - ply : plp : rtl + LDA.l OWMode : ORA.l OWMode+1 : BEQ .normal + LDY.w $06FA : CPY.b #$7F + BEQ .normal + REP #$10 + LDX.w $06F8 + PHB : PHK : PLB + JSR OWNewDestination + PLB + SEP #$30 + RTL + .normal + LDA.l $02A4E3,X : ORA.l $7EF3CA ; what we wrote over + RTL } -OWVanilla: +OWSpecialExit: { - lda $02a4e3,X : ora $7ef3ca : rtl + LDA.l OWMode+1 : AND.b #!FLAG_OW_CROSSED : BEQ .return + JSR OWWorldUpdate + .return + LDA.l $7EFD40,X ; what we wrote over + RTL } OWShuffle: { @@ -330,21 +418,13 @@ OWShuffle: ;down X = $34 ;compares X to determine direction of edge transition - phx : lsr $700 : cpx $700 : !blt .upOrLeft - dex : cpx $700 : bne .downEdge - lda #$3 : sta $418 : bra .setOWID ;right - .downEdge - lda #$1 : sta $418 : bra .setOWID ;down - .upOrLeft - inx : cpx $700 : bne .upEdge - lda #$2 : sta $418 : bra .setOWID ;left - .upEdge - lda #$0 : sta $418 ;up + phx : lsr.w $0700 + tyx : lda.l OWTransitionDirection,X : sta.w $0418 .setOWID ;look up transitions in current area in table OWEdgeOffsets ;offset is (8bytes * OW Slot ID) + (2bytes * direction) - asl : rep #$20 : pha : sep #$20 ;2 bytes per direction + asl : rep #$20 : and.w #$00ff : pha : sep #$20 ;2 bytes per direction lda $8a : and #$40 : !add $700 : rep #$30 : and #$00ff : asl #3 adc 1,S : tax asl $700 : pla @@ -368,25 +448,25 @@ OWShuffle: pla : dec : bne .nextTransition : bra .noTransition .newDestination - pla : sep #$30 : plx : lda $8a : bra .return + pla : sep #$30 : plx : rts .noTransition - sep #$30 : plx : jsl OWVanilla + sep #$30 : plx + lda.b #$7f : sta.w $06fa .return - rtl + rts } OWSearchTransition: { ;A-16 XY-16 lda $418 : bne + ;north lda.l OWNorthEdges,x : dec - cmp $22 : !bge .nomatch - lda.l OWNorthEdges+2,x : cmp $22 : !blt .nomatch + cmp $22 : !bge .exitloop + lda.l OWNorthEdges+2,x : cmp $22 : !blt .exitloop ;MATCH lda.l OWNorthEdges+14,x : tay ;y = record id of dest - sep #$20 : lda #OWSouthEdges>>16 : phb : pha : plb - ldx #OWSouthEdges : jsr OWNewDestination : plb ;x = address of table + ldx.w #OWSouthEdges ;x = address of table bra .matchfound + dec : bne + ;south lda.l OWSouthEdges,x : dec @@ -394,29 +474,25 @@ OWSearchTransition: lda.l OWSouthEdges+2,x : cmp $22 : !blt .exitloop ;MATCH lda.l OWSouthEdges+14,x : tay ;y = record id of dest - sep #$20 : lda #OWNorthEdges>>16 : phb : pha : plb : phx - ldx #OWNorthEdges : jsr OWNewDestination : plx : plb ;x = address of table + ldx.w #OWNorthEdges ;x = address of table bra .matchfound - .nomatch - bra .exitloop + dec : bne + ; west lda.l OWWestEdges,x : dec cmp $20 : !bge .exitloop lda.l OWWestEdges+2,x : cmp $20 : !blt .exitloop ;MATCH lda.l OWWestEdges+14,x : tay ;y = record id of dest - sep #$20 : lda #OWEastEdges>>16 : phb : pha : plb - ldx #OWEastEdges : jsr OWNewDestination : plb ;x = address of table + ldx.w #OWEastEdges ;x = address of table bra .matchfound + lda.l OWEastEdges,x : dec ;east cmp $20 : !bge .exitloop lda.l OWEastEdges+2,x : cmp $20 : !blt .exitloop ;MATCH lda.l OWEastEdges+14,x : tay ;y = record id of dest - sep #$20 : lda #OWWestEdges>>16 : phb : pha : plb - ldx #OWWestEdges : jsr OWNewDestination : plb ;x = address of table + ldx.w #OWWestEdges ;x = address of table .matchfound + stx $06f8 : sty $06fa : sec : rts plx : pla : pea $0001 : phx sec : rts @@ -449,10 +525,11 @@ OWNewDestination: ++ lda $84 : !add 1,s : sta $84 : pla : pla .adjustMainAxis - ;LDA $84 : SEC : SBC #$0400 : AND #$0F80 : ASL : XBA : STA $88 ; vram - LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 + LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 ; vram LDA $84 : SEC : SBC #$0010 : AND #$003E : LSR : STA $86 + LDA.w $000F,X : AND.w #$00FF : STA.w $06FC ; position to walk to after transition (if non-zero) + pla : pla : sep #$10 : ldy $418 ldx OWCoordIndex,y : lda $20,x : and #$fe00 : pha lda $20,x : and #$01ff : pha ;s1 = relative cur, s3 = ow cur @@ -496,7 +573,7 @@ OWNewDestination: ldx OWOppBGIndex,y : lda $e0,x : !add 1,s : sta $e0,x lda $418 : asl : tax : lda $610,x : !add 1,s : sta $610,x : pla - sep #$30 : lda OWOppSlotOffset,y : !add $04 : asl : and #$7f : sta $700 + sep #$30 : lda $04 : and #$3f : !add OWOppSlotOffset,y : asl : sta $700 ; crossed OW shuffle lda.l OWMode+1 : and.b #!FLAG_OW_CROSSED : beq .return @@ -504,13 +581,18 @@ OWNewDestination: .return lda $05 : sta $8a - ;bra + - ; nop #8 - ; jsl $02EA41 - ; nop #8 - ;+ rep #$30 : rts } +OWLoadSpecialArea: +{ + LDA.l $04E881,X : STA.b $A0 + JSL $04E8B4 + LDA.l OWMode+1 : AND.b #!FLAG_OW_CROSSED : BEQ .return + TYX : LDA.l OWSpecialDestSlot,X : TAX + JSR OWWorldUpdate + .return + RTS +} OWWorldUpdate: ; x = owid of destination screen { lda.l OWTileWorldAssoc,x : cmp.l $7ef3ca : beq .return @@ -545,10 +627,29 @@ OWWorldUpdate: ; x = owid of destination screen .return rts } -OWSpecialTransition: +OWAdjustExitPosition: { - LDX #$9E - - DEX : DEX : CMP $DAEE,X : BNE - + LDA.w $06FC : CMP.b #$60 : BEQ .stone_bridge + CMP.b #$B0 : BNE .normal + LDA.b #$80 : STA.b $20 : STZ.b $21 + BRA .normal + .stone_bridge + LDA.b #$A0 : STA.b $E2 + LDA.b #$3D : STA.w $061C + LDA.b #$3B : STA.w $061E + INC.b $23 : INC.w $061D : INC.w $061F + .normal + INC.b $11 : STZ.b $B0 ; what we wrote over + RTL +} +OWEndScrollTransition: +{ + LDY.w $06FC : BEQ .normal + CMP.w $06FC + RTL + .normal + CMP.l $02C176,X ; what we wrote over + RTL } ;Data @@ -556,7 +657,7 @@ org $aaa000 OWEdgeOffsets: ;2 bytes per each direction per each OW Slot, order is NSWE per value at $0418 ;AABB, A = offset to the transition table, B = number of transitions -dw $0001, $0000, $0000, $0000 ;OW Slot 00, OWID 0x00 Lost Woods +dw $0000, $0000, $0000, $0000 ;OW Slot 00, OWID 0x00 Lost Woods dw $0000, $0000, $0000, $0001 ;OW Slot 01, OWID 0x00 dw $0000, $0001, $0001, $0000 ;OW Slot 02, OWID 0x02 Lumberjack dw $0000, $0000, $0000, $0000 @@ -572,7 +673,7 @@ dw $0000, $0000, $0000, $0000 dw $0000, $0000, $0000, $0301 dw $0000, $0000, $0301, $0000 dw $0000, $0000, $0000, $0000 -dw $0201, $0501, $0000, $0000 ;Zora +dw $0000, $0501, $0000, $0000 ;Zora dw $0302, $0602, $0000, $0000 dw $0501, $0801, $0000, $0402 @@ -606,7 +707,7 @@ dw $1101, $0000, $1201, $1301 dw $0000, $1502, $1301, $0000 dw $1201, $1701, $0000, $1403 dw $1301, $1801, $1403, $1701 ;Links -dw $1401, $1901, $1702, $1802 ;Hobo +dw $1401, $1901, $1801, $1802 ;Hobo dw $1501, $1a02, $1902, $0000 dw $1601, $0000, $0000, $0000 @@ -704,12 +805,15 @@ dw $0000, $4001, $0000, $0000 dw $0000, $0000, $0000, $4a01 dw $0000, $4101, $0000, $0000 +OWSpecialDestSlot: +db $80, $80, $81 + org $aaa800 ;PC 152800 OWNorthEdges: ; Min Max Width Mid OW Slot/OWID VRAM *FREE* Dest Index -dw $00a0, $00a0, $0000, $00a0, $0000, $0000, $0000, $0040 ;Lost Woods +dw $00a0, $00a0, $0000, $00a0, $0000, $0000, $0000, $B040 ;Lost Woods (exit only) dw $0458, $0540, $00e8, $04cc, $0a0a, $0000, $0000, $0000 -dw $0f38, $0f60, $0028, $0f4c, $0f0f, $0000, $0000, $0041 +dw $0f38, $0f60, $0028, $0f4c, $0f0f, $0000, $0000, $2041 ;Waterfall (exit only) dw $0058, $0058, $0000, $0058, $1010, $0000, $0000, $0001 dw $0178, $0178, $0000, $0178, $1010, $0000, $0000, $0002 dw $0388, $0388, $0000, $0388, $1111, $0000, $0000, $0003 @@ -838,8 +942,8 @@ dw $06a0, $07b0, $0110, $0728, $7373, $0000, $0000, $003e dw $0830, $09b0, $0180, $08f0, $7474, $0000, $0000, $003f dw $0e78, $0e88, $0010, $0e80, $7777, $0000, $0000, $0040 dw $0ee0, $0fc0, $00e0, $0f50, $7777, $0000, $0000, $0041 -dw $0080, $0080, $0000, $0080, $8080, $0000, $0000, $0000 ;Pedestal -dw $0288, $02c0, $0038, $02a4, $8189, $0000, $0000, $0002 ;Zora +dw $0080, $0080, $0000, $0080, $8080, $0000, $0000, $0000 ;Pedestal (unused) +dw $0288, $02c0, $0038, $02a4, $8189, $0000, $0000, $0002 ;Zora (unused) OWWestEdges: dw $0070, $00a0, $0030, $0088, $0202, $0000, $0000, $0000 dw $0068, $0078, $0010, $0070, $0505, $0000, $0000, $0001 @@ -864,7 +968,7 @@ dw $0b60, $0ba0, $0040, $0b80, $2a2a, $0000, $0000, $0013 dw $0ab0, $0ad0, $0020, $0ac0, $2c2c, $0000, $0000, $0014 dw $0af0, $0b40, $0050, $0b18, $2c2c, $0000, $0000, $0015 dw $0b78, $0ba0, $0028, $0b8c, $2c2c, $0000, $0000, $0016 -dw $0b10, $0b28, $0018, $0b1c, $2d2d, $0000, $0000, $004a +dw $0b10, $0b28, $0018, $0b1c, $2d2d, $0000, $0000, $604a ;Stone Bridge (exit only) dw $0b68, $0b98, $0030, $0b80, $2d2d, $0000, $0000, $0017 dw $0a68, $0ab8, $0050, $0a90, $2e2e, $0000, $0000, $0018 dw $0b00, $0b78, $0078, $0b3c, $2e2e, $0000, $0000, $0019 @@ -991,7 +1095,11 @@ dw $0e28, $0fb8, $0190, $0ef0, $7b7b, $0000, $0000, $0047 dw $0f78, $0fb8, $0040, $0f98, $7c7c, $0000, $0000, $0048 dw $0f20, $0f40, $0020, $0f30, $757e, $0000, $0000, $0049 dw $0f70, $0fb8, $0048, $0f94, $757e, $0000, $0000, $004a -dw $0058, $00c0, $0068, $008c, $8080, $0000, $0000, $0017 ;Hobo +dw $0058, $00c0, $0068, $008c, $8080, $0000, $0000, $0017 ;Hobo (unused) + +org $aab9e0 ;PC 1539e0 +OWSpecialDestIndex: +dw $0080, $0081, $0082 org $aaba00 ;PC 153a00 OWTileWorldAssoc: @@ -1011,6 +1119,7 @@ db $40, $40, $40, $40, $40, $40, $40, $40 db $40, $40, $40, $40, $40, $40, $40, $40 db $40, $40, $40, $40, $40, $40, $40, $40 db $40, $40, $40, $40, $40, $40, $40, $40 +db $00, $00 org $aabb00 ;PC 153b00 OWTileMapAlt: @@ -1031,3 +1140,5 @@ db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 + +db 0, 0 diff --git a/data/base2current.bps b/data/base2current.bps index 277b30a01b4d930ee08b07b49620b63f5645d325..3a185824a730cb3e8812df710e5ddd3bda7347ee 100644 GIT binary patch delta 1764 zcmW-gdsGuw8o=+JOos3dGBEL$RNJ+*e#Rw${^bvUWDr(Nx` zTDXHm6L1VMkRe122tj5D5*KK+R$}S#^x*N4eYoAar>zZb4<~MWqHA|OEY=H5mv~YAu zYo5N9qpx*}bFMe=XA?8ea`cxSBItAVbD@y0}BM#F$ps1H3k=(I&=X zzh^nRbi79H$6)ceOz|;PmfFN_u9`bD2oP889lxH)VX(}#D>Nt(OjU_tEss zpgXIw=k8P6PuTC;#A?(w3 zw0V5n5@Xde-`a}*xD;DvogiCI+;zXW%r!o^qpw>&`SmlGF_$UciWRqy--v&fQsmy< zbkunn7{TR_R;@knZdi5RnFf+TTF+Y>Jzd#=2Iu-*rD$Y%2U54l}xO#-^5Ld2p`WKl_%Dk)9tM+3OetjkG1@%M6=sclMa5q-? zu3M({IvE&F5`|{K%@dTW(_rzw{MykKZ2S-5I~PPd0+9v@gcUZCdq^-*JwvM>Bm`d% zH_qW!Fr$bltE^Q0oIqHylO&E*R*WO@Br9DX6_Ttgo2|3;GT(ce9K>pFwoKO7Fw?(a zhA@9?b(w(izmv|}kFl14TVH4b^Tg%mc_b9&Yw8>4NK|h*^$Z$#)7xHy z)MUB61teSy11T2C$uy;V3`|kV-=;H?dqIM?4z&LgCDlPu2@;qn6$X9HqV93L$TTNy zMGh+$RGh4wd17}~Q*NSOa|ToR#tIVcJl>C0FR+ z`(bQYyh3;E3oD1k;nP$A{kBA+RNx7HHB1K<>DF*jK%!t6ZCIpL#-N0$;Kp!yP`pBQ zJRRN=lyH0kEf2>ho**iGe_|1l;h!fYWDFjENF19d4CZ;}0R*=VX42g4qZ?N7UnRrU zFqg+u;5xXT-;e^co42Ym5eq(R;O?aYsTSMbTK<(>blb zP+%L$%H!or;F{)=w>#NXN6SxSoh+&nZ=VLYr&Y}SyeX=hS|)|N=z^Nk@{Me}3bFK= zsDnYieaKOc_@`Q2joY%&7nf9oq}8ih>_b1UDP1T=Aw7cml2HR1T_9%6@sychBwIjI*RMbtq0br>e#K=H!bUEe z__u>cMQq8IP;DuCvf74a#ChAm9=$Z;% zf^usP@O=>CmO&kq*BOvRQ@O2q9hqUSNh{Q{QFN+@};e9#g;h`twT@G7d`X00c z2zJ1Nee5fF|NVPIg_ws7b%UWb8L&sKbjz#%)nh;L+?zw*yxhZ~zZSuimB}kkKZ`J} Y;Cr$Dd?>aR=4upodiX;MV`J0*0gH0eXaE2J delta 1447 zcmW+!drVVT96smv_6dCm1HS7Ukis8Ku-_EL}VcQEZm zDv7&aRBdF!;nSp^Eu?APDUEAx6Ho3BHu-;X_)&rd8ei5?u|0t9eU3Nn;eLM zy#1r9j_XSR0YrkHRbr3o>%VLo4$;)0my>lJs=?F*wZ9tI-KoUp>{pBkkb%V?- zV9%yiIrfSKga0Z9ck*KP-#UAQk9zp!lP&7yre0VTrn7`FcU7U0sV0twF%OOy6$}bI z9G0;Nx35w8IaTdAT-8WIx@vvjWxi+f`DYOUb?!IINw>-Uv(~A9o3;vJJlsw93t?(h zX>2NTbKIbsP}BE?Py^S}F(UXOd`O=b!Lqttp4_wl{wmnx!4M;5i$r(?VPz0bBwCr; z?CQ?Kwa$O4XSM?VQ^2>%Cs_>g-5j$5M~QB@KryqlWbXwMvskCNZosaH#+2HS($%Qa zD2@TX<3wsxAre(WMgsWykTL+e7}e`YzIJwDX0F3Z+%hw#L_elfy#`;#muQtER&O+2 zQ>s7}q`oK^K>~d!Z{F32_$)1)s(M&~2Qvd;fGHX$yxIUZz>E`CO+Yfh41|4 zqa0G|Y=fw_D3}va4!sec4G=>M!s7w;Q18L2bRuCx{hwtD+1L*yFOU``LPG;3ZOcy`TVG$#jMUF$a*G+v7ex##sSAebr8DRgbl zLb@p$X24o{BpR+flx<`@VNy%YP37_QF91Lsy-4e7P*g%ggI@30CP_gg_$IOGzFr@a z4Jq(ZV7#tWfmd1okL4sBnIe!MN?v8oOkkNuFF3Zvo>E>hK* zfpc@tVl=|-jxTodP%4(?+N5VLQnYMjP67!R-ma-jeK3>QEi zCd6<$M3+jT1;Y~j(^43K1?EkxroV9aa8&2}TNqOp=(6YWO-_Ei1YO28+*$BF-nlC7 zK}Ug9C(Q<%r8E{lMi%#^`KK4b+Yl#Hd+YpkEL1^mU9)pdntxml_i-Tbrz_y+?Do68 zskA2%KI4{H=wge%FA3H|N#%K7+V2mJlrmW;M}N`3CIz;~O6cSytHac?{LHtG1E-q& kf2@GfOQVXrS0gOR?c7p&)BkD*%#0VbI_B!dbF;et0luv~>Hq)$ From ee07ac59a78239a848bed86428fbd37dba80f2e3 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 3 May 2022 22:08:44 -0500 Subject: [PATCH 02/14] Fixed minor issue in ER with glitched regions that aren't in any OWTileGroup --- EntranceShuffle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index ed6653b4..54143e0c 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1668,7 +1668,7 @@ def get_starting_entrances(world, player, force_starting_world=True): # get entrances from list of regions entrances = list() for region_name in regions: - if world.shuffle[player] == 'simple' and region_name in OWTileRegions and OWTileRegions[region_name] in [0x03, 0x05, 0x07]: + if world.shuffle[player] == 'simple' and region_name in OWTileRegions.keys() and OWTileRegions[region_name] in [0x03, 0x05, 0x07]: continue region = world.get_region(region_name, player) if not force_starting_world or region.type == (RegionType.LightWorld if not invFlag else RegionType.DarkWorld): @@ -1710,7 +1710,7 @@ def get_distant_entrances(world, start_entrance, player): # get entrances from remaining regions candidates = list() for region_name in [r for r in regions if r not in explored_regions]: - if OWTileRegions[region_name] in [0x03, 0x05, 0x07]: + if region_name in OWTileRegions.keys() and OWTileRegions[region_name] in [0x03, 0x05, 0x07]: continue region = world.get_region(region_name, player) for exit in region.exits: From 1c2b27933b305f83cbc374ada432b8dc8e395366 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 3 May 2022 23:40:02 -0500 Subject: [PATCH 03/14] Simplified OWR edge group reorganization --- OverworldShuffle.py | 175 +++++++------------------------------------- 1 file changed, 27 insertions(+), 148 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index bd855436..132c8974 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -661,158 +661,37 @@ def remove_reserved(world, groupedlist, connected_edges, player): return new_grouping def reorganize_groups(world, groups, player): + def get_group_key(group): + #(std, region, axis, terrain, parallel, count) = group + new_group = list(group) + if world.mode[player] != "standard": + new_group[0] = None + if world.owShuffle[player] != 'parallel': + new_group[4] = None + if not world.owKeepSimilar[player]: + new_group[5] = None + return tuple(new_group) + # predefined shuffle groups get reorganized here # this restructures the candidate pool based on the chosen settings - if world.owShuffle[player] == 'full': - if world.owKeepSimilar[player]: - if world.mode[player] == 'standard': - # tuple goes to (A,B,C,D,_,F) - for grouping in (groups,): - new_grouping = {} + for grouping in (groups,): + new_grouping = {} - for group in grouping.keys(): - (std, region, axis, terrain, _, count) = group - new_grouping[(std, region, axis, terrain, count)] = ([], []) - - for group in grouping.keys(): - (std, region, axis, terrain, _, count) = group - (forward_edges, back_edges) = grouping[group] - (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, count)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(std, region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges) + for group in grouping.keys(): + new_grouping[get_group_key(group)] = ([], []) + + for group in grouping.keys(): + new_group = get_group_key(group) + (forward_edges, back_edges) = grouping[group] + if not world.owKeepSimilar[player]: + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + (exist_forward_edges, exist_back_edges) = new_grouping[new_group] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[new_group] = (exist_forward_edges, exist_back_edges) - return list(new_grouping.values()) - else: - # tuple goes to (_,B,C,D,_,F) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, _, count) = group - new_grouping[(region, axis, terrain, count)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, _, count) = group - (forward_edges, back_edges) = grouping[group] - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, count)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - if world.mode[player] == 'standard': - # tuple goes to (A,B,C,D,_,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (std, region, axis, terrain, _, _) = group - new_grouping[(std, region, axis, terrain)] = ([], []) - - for group in grouping.keys(): - (std, region, axis, terrain, _, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(std, region, axis, terrain)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - # tuple goes to (_,B,C,D,_,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, _, _) = group - new_grouping[(region, axis, terrain)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, _, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - elif world.owShuffle[player] == 'parallel': - if world.owKeepSimilar[player]: - if world.mode[player] == 'standard': - # tuple stays (A,B,C,D,E,F) - for grouping in (groups,): - return list(grouping.values()) - else: - # tuple goes to (_,B,C,D,E,F) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, count) = group - new_grouping[(region, axis, terrain, parallel, count)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, count) = group - (forward_edges, back_edges) = grouping[group] - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel, count)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - if world.mode[player] == 'standard': - # tuple goes to (A,B,C,D,E,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (std, region, axis, terrain, parallel, _) = group - new_grouping[(std, region, axis, terrain, parallel)] = ([], []) - - for group in grouping.keys(): - (std, region, axis, terrain, parallel, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, parallel)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(std, region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - # tuple goes to (_,B,C,D,E,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, _) = group - new_grouping[(region, axis, terrain, parallel)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - raise NotImplementedError('Shuffling not supported yet') + return list(new_grouping.values()) 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']): From 56b2e0e5e45f517f5260d5bd58621f02ebeacb90 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 4 May 2022 11:36:28 -0500 Subject: [PATCH 04/14] Adding an Auto default for Pre-open Pyramid option --- BaseClasses.py | 19 ++++++++++++++++--- CLI.py | 2 +- Mystery.py | 6 +++--- Rom.py | 2 +- Rules.py | 2 +- mystery_example.yml | 4 ++++ resources/app/cli/args.json | 7 +++++-- resources/app/gui/lang/en.json | 3 +++ .../app/gui/randomize/entrando/widgets.json | 12 +++++++++++- source/gui/randomize/entrando.py | 2 +- 10 files changed, 46 insertions(+), 13 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 8a3da107..4fa982db 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -147,7 +147,7 @@ class World(object): set_player_attr('crystals_needed_for_gt', 7) set_player_attr('crystals_ganon_orig', {}) set_player_attr('crystals_gt_orig', {}) - set_player_attr('open_pyramid', False) + set_player_attr('open_pyramid', 'auto') set_player_attr('treasure_hunt_icon', 'Triforce Piece') set_player_attr('treasure_hunt_count', 0) set_player_attr('treasure_hunt_total', 0) @@ -317,6 +317,19 @@ class World(object): def is_bombshop_start(self, player): return self.is_tile_swapped(0x2c, player) and (self.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not self.shufflelinks[player]) + def is_pyramid_open(self, player): + if self.open_pyramid[player] == 'yes': + return True + elif self.open_pyramid[player] == 'no': + return False + else: + if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']: + return False + elif self.goal[player] in ['crystals', 'trinity']: + return True + else: + return False + def check_for_door(self, doorname, player): if isinstance(doorname, Door): return doorname @@ -3045,7 +3058,7 @@ class Spoiler(object): if self.metadata['shuffle'][player] != 'vanilla' or self.metadata['ow_mixed'][player]: outfile.write('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player]) if self.metadata['goal'][player] != 'trinity': - outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % yn(self.metadata['open_pyramid'][player])) + outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) if self.metadata['door_shuffle'][player] != 'vanilla': outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player]) @@ -3338,7 +3351,7 @@ class Settings(object): | (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0), ((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3) - | (0x4 if w.open_pyramid[p] else 0) | access_mode[w.accessibility[p]], + | (0x4 if w.is_pyramid_open(p) else 0) | access_mode[w.accessibility[p]], (0x80 if w.bigkeyshuffle[p] else 0) | (0x40 if w.keyshuffle[p] else 0) | (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0) diff --git a/CLI.py b/CLI.py index 264e1f06..e8ba78de 100644 --- a/CLI.py +++ b/CLI.py @@ -144,7 +144,7 @@ def parse_settings(): "restrict_boss_items": "none", # Shuffle Ganon defaults to TRUE - "openpyramid": False, + "openpyramid": "auto", "shuffleganon": True, "ow_shuffle": "vanilla", "ow_crossed": "none", diff --git a/Mystery.py b/Mystery.py index f378ccea..faf5d16a 100644 --- a/Mystery.py +++ b/Mystery.py @@ -184,7 +184,6 @@ def roll_settings(weights): if ret.dungeon_counters == 'default': ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off' - ret.shufflelinks = get_choice('shufflelinks') == 'on' ret.pseudoboots = get_choice('pseudoboots') == 'on' ret.shopsanity = get_choice('shopsanity') == 'on' ret.keydropshuffle = get_choice('keydropshuffle') == 'on' @@ -200,12 +199,13 @@ def roll_settings(weights): 'triforce-hunt': 'triforcehunt', 'trinity': 'trinity' }[goal] - ret.openpyramid = goal in ['fast_ganon', 'trinity'] if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False + + ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto' ret.shuffleganon = get_choice('shuffleganon') == 'on' + ret.shufflelinks = get_choice('shufflelinks') == 'on' ret.crystals_gt = get_choice('tower_open') - ret.crystals_ganon = get_choice('ganon_open') goal_min = get_choice_default('triforce_goal_min', default=20) diff --git a/Rom.py b/Rom.py index d2bd5930..3eba1055 100644 --- a/Rom.py +++ b/Rom.py @@ -1287,7 +1287,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest rom.write_byte(0x50599, 0x00) # disable below ganon chest rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest - rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole + rom.write_byte(0x18008B, 0x01 if world.is_pyramid_open(player) else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped(player) else 0x00) # AT/GT swapped rom.write_byte(0xF5D73, 0xF0) # bees are catchable diff --git a/Rules.py b/Rules.py index 3810a1cb..f934f2ca 100644 --- a/Rules.py +++ b/Rules.py @@ -1046,7 +1046,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Top of Pyramid', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('Top of Pyramid (Inner)', player), lambda state: state.has('Beat Agahnim 1', player)) else: - set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: world.open_pyramid[player] or world.goal[player] == 'trinity' or state.has('Beat Agahnim 2', player)) + set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or state.has('Beat Agahnim 2', player)) set_rule(world.get_entrance('Pyramid Hole', player), lambda state: False) set_rule(world.get_entrance('Pyramid Entrance', player), lambda state: False) diff --git a/mystery_example.yml b/mystery_example.yml index a3663361..d7fe4580 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -53,6 +53,10 @@ lean: 2 crossed: 3 insanity: 1 + open_pyramid: + auto: 1 + yes: 0 + no: 0 shufflelinks: on: 1 off: 1 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index b62fe115..7c0aa769 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -218,8 +218,11 @@ ] }, "openpyramid": { - "action": "store_true", - "type": "bool" + "choices": [ + "auto", + "yes", + "no" + ] }, "rom": {}, "loglevel": { diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 229d044e..b66c4def 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -138,6 +138,9 @@ "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", + "randomizer.entrance.openpyramid.auto": "Auto", + "randomizer.entrance.openpyramid.yes": "Yes", + "randomizer.entrance.openpyramid.no": "No", "randomizer.entrance.shuffleganon": "Include Ganon's Tower and Pyramid Hole in shuffle pool", "randomizer.entrance.shufflelinks": "Include Link's House in the shuffle pool", "randomizer.entrance.overworld_map": "Overworld Map", diff --git a/resources/app/gui/randomize/entrando/widgets.json b/resources/app/gui/randomize/entrando/widgets.json index d4cf3afe..9c3102f6 100644 --- a/resources/app/gui/randomize/entrando/widgets.json +++ b/resources/app/gui/randomize/entrando/widgets.json @@ -1,6 +1,16 @@ { "widgets": { - "openpyramid": { "type": "checkbox" }, + "openpyramid": { + "type": "selectbox", + "options": [ + "auto", + "yes", + "no" + ], + "config": { + "width": 10 + } + }, "shuffleganon": { "type": "checkbox" }, "shufflelinks": { "type": "checkbox" }, "overworld_map": { diff --git a/source/gui/randomize/entrando.py b/source/gui/randomize/entrando.py index 0759218c..476e6ff2 100644 --- a/source/gui/randomize/entrando.py +++ b/source/gui/randomize/entrando.py @@ -27,7 +27,7 @@ def entrando_page(parent): for key in dictWidgets: self.widgets[key] = dictWidgets[key] packAttrs = {"anchor":E} - if self.widgets[key].type == "checkbox": + if self.widgets[key].type == "checkbox" or key == "openpyramid": packAttrs["anchor"] = W self.widgets[key].pack(packAttrs) From 54c8efe464640947abe2772c46f729b43c51d869 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 4 May 2022 12:10:54 -0500 Subject: [PATCH 05/14] Simplified OWR tile group reorganization --- OverworldShuffle.py | 80 +++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 132c8974..a30dfba1 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -561,7 +561,17 @@ def shuffle_tiles(world, groups, result_list, player): return swapped_edges def reorganize_tile_groups(world, player): - def can_shuffle_group(name, groupType): + def get_group_key(group): + #(name, groupType, whirlpoolGroup) = group + new_group = list(group) + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']: + new_group[1] = None + if not world.owWhirlpoolShuffle[player] and world.owCrossed[player] == 'none': + new_group[2] = None + return tuple(new_group) + + def can_shuffle_group(group): + (name, groupType, whirlpoolGroup) = group return name not in ['Castle', 'Links', 'Central Bonk Rocks'] \ or (world.mode[player] != 'standard' and (name != 'Castle' \ or world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] \ @@ -570,60 +580,22 @@ def reorganize_tile_groups(world, player): or (world.mode[player] == 'standard' and world.shuffle[player] in ['lean', 'crossed', 'insanity'] and name == 'Castle' and groupType == 'Entrance') groups = {} - for (name, groupType, whirlpoolGroup) in OWTileGroups.keys(): - if can_shuffle_group(name, groupType): - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']: - if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none': - groups[(name, whirlpoolGroup)] = ([], [], []) - else: - groups[(name,)] = ([], [], []) - else: - if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none': - groups[(name, groupType, whirlpoolGroup)] = ([], [], []) - else: - groups[(name, groupType)] = ([], [], []) + for group in OWTileGroups.keys(): + if can_shuffle_group(group): + groups[get_group_key(group)] = ([], [], []) - for (name, groupType, whirlpoolGroup) in OWTileGroups.keys(): - if can_shuffle_group(name, groupType): - (lw_owids, dw_owids) = OWTileGroups[(name, groupType, whirlpoolGroup)] - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']: - if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none': - (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, whirlpoolGroup)] - exist_owids.extend(lw_owids) - exist_owids.extend(dw_owids) - for owid in lw_owids: - exist_lw_regions.extend(OWTileRegions.inverse[owid]) - for owid in dw_owids: - exist_dw_regions.extend(OWTileRegions.inverse[owid]) - groups[(name, whirlpoolGroup)] = (exist_owids, exist_lw_regions, exist_dw_regions) - else: - (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name,)] - exist_owids.extend(lw_owids) - exist_owids.extend(dw_owids) - for owid in lw_owids: - exist_lw_regions.extend(OWTileRegions.inverse[owid]) - for owid in dw_owids: - exist_dw_regions.extend(OWTileRegions.inverse[owid]) - groups[(name,)] = (exist_owids, exist_lw_regions, exist_dw_regions) - else: - if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none': - (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, groupType, whirlpoolGroup)] - exist_owids.extend(lw_owids) - exist_owids.extend(dw_owids) - for owid in lw_owids: - exist_lw_regions.extend(OWTileRegions.inverse[owid]) - for owid in dw_owids: - exist_dw_regions.extend(OWTileRegions.inverse[owid]) - groups[(name, groupType, whirlpoolGroup)] = (exist_owids, exist_lw_regions, exist_dw_regions) - else: - (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, groupType)] - exist_owids.extend(lw_owids) - exist_owids.extend(dw_owids) - for owid in lw_owids: - exist_lw_regions.extend(OWTileRegions.inverse[owid]) - for owid in dw_owids: - exist_dw_regions.extend(OWTileRegions.inverse[owid]) - groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions) + for group in OWTileGroups.keys(): + if can_shuffle_group(group): + (lw_owids, dw_owids) = OWTileGroups[group] + (exist_owids, exist_lw_regions, exist_dw_regions) = groups[get_group_key(group)] + exist_owids.extend(lw_owids) + exist_owids.extend(dw_owids) + for owid in lw_owids: + exist_lw_regions.extend(OWTileRegions.inverse[owid]) + for owid in dw_owids: + exist_dw_regions.extend(OWTileRegions.inverse[owid]) + groups[get_group_key(group)] = (exist_owids, exist_lw_regions, exist_dw_regions) + return groups def remove_reserved(world, groupedlist, connected_edges, player): From 1eefa5dbde47543ed467914450ab9094f910c348 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 4 May 2022 16:48:55 -0500 Subject: [PATCH 06/14] Randomize Special Transitions --- asm/owrando.asm | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/asm/owrando.asm b/asm/owrando.asm index 61097756..04a6d576 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -13,16 +13,17 @@ org $02a929 OWDetectTransitionReturn: org $02a939 +OverworldHandleTransitions_SpecialTrigger: JSL OWDetectEdgeTransition BCS OWDetectTransitionReturn +org $02a999 +jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA + org $04e8ae JSL OWDetectSpecialTransition RTL : NOP -org $02a999 -jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA - org $02e809 JSL OWSpecialExit @@ -32,6 +33,12 @@ JSL OWAdjustExitPosition org $02c1a9 JSL OWEndScrollTransition +org $04E881 +Overworld_LoadSpecialOverworld_RoomId: +org $04E8B4 +Overworld_LoadSpecialOverworld: + + org $05af75 jsl OWPreserveMirrorSprite : nop #2 ; LDA $7EF3CA : BNE $05AFDF @@ -355,7 +362,7 @@ OWDetectSpecialTransition: STA.w $06FA LDA.l OWEdgeDataOffset,X : STA.w $06F8 PLA : SEP #$30 : PLA ; delete 3 bytes from stack - JSL $07F413 : BCS .return ; Link_CheckForEdgeScreenTransition + JSL Link_CheckForEdgeScreenTransition : BCS .return ; Link_CheckForEdgeScreenTransition LDA.l $04E879,X : STA.b $00 : CMP.b #$08 : BNE .hobo LSR : STA.b $20 : STZ.b $E8 ; move Link and camera to edge LDA.b #$06 : STA.b $02 @@ -368,7 +375,7 @@ OWDetectSpecialTransition: .continue STZ.b $03 ; copied from DeleteCertainAncillaeStopDashing at $028A0E - JSL $09AC57 ; Ancilla_TerminateSelectInteractives + JSL Ancilla_TerminateSelectInteractives LDA.w $0372 : BEQ .not_dashing STZ.b $4D : STZ.b $46 LDA.b #$FF : STA.b $29 : STA.b $C7 @@ -377,7 +384,7 @@ OWDetectSpecialTransition: PLA : REP #$31 : PLA ; delete 3 bytes from stack LDX.b $02 LDA.b $84 - JML $02A93F + JML OverworldHandleTransitions_SpecialTrigger+6 .special AND.w #$0003 : TAY : ASL : TAX .normal @@ -411,13 +418,7 @@ OWSpecialExit: } OWShuffle: { - ;Assume you're at links house = $2c - ;transitioning right will result in X = $2d - ;transitioning left will result in X = $2b - ;up X = $24 - ;down X = $34 - - ;compares X to determine direction of edge transition + ;determine direction of edge transition phx : lsr.w $0700 tyx : lda.l OWTransitionDirection,X : sta.w $0418 @@ -585,8 +586,8 @@ OWNewDestination: } OWLoadSpecialArea: { - LDA.l $04E881,X : STA.b $A0 - JSL $04E8B4 + LDA.l Overworld_LoadSpecialOverworld_RoomId,X : STA.b $A0 + JSL Overworld_LoadSpecialOverworld LDA.l OWMode+1 : AND.b #!FLAG_OW_CROSSED : BEQ .return TYX : LDA.l OWSpecialDestSlot,X : TAX JSR OWWorldUpdate From 82aa0b5d791f378a508552c952c651d54a5f9708 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 5 May 2022 07:50:08 -0500 Subject: [PATCH 07/14] Fixed issue where some water regions are unreachable because they weren't ever checked --- OverworldShuffle.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 68e8dc48..79eb4080 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -288,7 +288,7 @@ def link_overworld(world, player): trimmed_groups = remove_reserved(world, trimmed_groups, connected_edges, player) groups = reorganize_groups(world, trimmed_groups, player) - tries = 10 + tries = 20 valid_layout = False connected_edge_cache = connected_edges.copy() while not valid_layout and tries > 0: @@ -939,12 +939,10 @@ def validate_layout(world, player): while unreachable_count != len(unreachable_regions): # find unreachable regions unreachable_regions = {} - flat_sectors = [[r for l in s for r in l] for s in world.owsectors[player]] - for sector in flat_sectors: - for region_name in sector: - if region_name not in explored_regions and region_name not in isolated_regions: - region = base_world.get_region(region_name, player) - unreachable_regions[region_name] = region + for region_name in list(OWTileRegions.copy().keys()): + if region_name not in explored_regions and region_name not in isolated_regions: + region = base_world.get_region(region_name, player) + unreachable_regions[region_name] = region # loop thru unreachable regions to check if some can be excluded unreachable_count = len(unreachable_regions) From 6f1fb6a38cd567162004587f316a8b263959d4ce Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 5 May 2022 07:52:42 -0500 Subject: [PATCH 08/14] Fixed broken graph issue with Lake Hylia Water D --- OWEdges.py | 3 ++- OverworldShuffle.py | 3 ++- Regions.py | 4 ++-- Rules.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OWEdges.py b/OWEdges.py index a076f4e7..e3f7d20b 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -1593,7 +1593,8 @@ OWExitTypes = { 'Lake Hylia West Pier', 'Lake Hylia Northeast Water Drop', 'Lake Hylia East Pier', - 'Lake Hylia Water D Entry', + 'Lake Hylia Water D Approach', + 'Lake Hylia Water D Leave', 'Desert Pass Ladder (South)', 'Desert Pass Rocks (North)', 'Desert Pass Rocks (South)', diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 79eb4080..8573f9d2 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1080,7 +1080,8 @@ mandatory_connections = [# Intra-tile OW Connections ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), ('Lake Hylia West Pier', 'Lake Hylia Area'), ('Lake Hylia East Pier', 'Lake Hylia Northeast Bank'), - ('Lake Hylia Water D Entry', 'Lake Hylia Water'), #flippers + ('Lake Hylia Water D Approach', 'Lake Hylia Water D'), + ('Lake Hylia Water D Leave', 'Lake Hylia Water'), #flippers ('Desert Pass Ledge Drop', 'Desert Pass Area'), ('Desert Pass Rocks (North)', 'Desert Pass Southeast'), #glove ('Desert Pass Rocks (South)', 'Desert Pass Area'), #glove diff --git a/Regions.py b/Regions.py index c83659b7..42bfd467 100644 --- a/Regions.py +++ b/Regions.py @@ -111,8 +111,8 @@ def create_regions(world, player): create_lw_region(player, 'Lake Hylia Northeast Bank', None, ['Lake Hylia Northeast Water Drop', 'Ice Lake Northeast Mirror Spot', 'Lake Hylia NE']), create_lw_region(player, 'Lake Hylia Central Island', None, ['Lake Hylia Central Water Drop', 'Capacity Upgrade', 'Ice Palace Mirror Spot', 'Lake Hylia Teleporter']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island'], ['Lake Hylia Island Water Drop']), - create_lw_region(player, 'Lake Hylia Water', None, ['Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia West Pier', 'Lake Hylia East Pier', 'Lake Hylia NC', 'Lake Hylia EC', 'Lake Hylia Whirlpool'], 'Light World', Terrain.Water), - create_lw_region(player, 'Lake Hylia Water D', None, ['Lake Hylia Water D Entry', 'Ice Lake Moat Mirror Spot'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Water', None, ['Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia West Pier', 'Lake Hylia East Pier', 'Lake Hylia Water D Approach', 'Lake Hylia NC', 'Lake Hylia EC', 'Lake Hylia Whirlpool'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Water D', None, ['Lake Hylia Water D Leave', 'Ice Lake Moat Mirror Spot'], 'Light World', Terrain.Water), create_lw_region(player, 'Ice Cave Area', None, ['Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Shopping Mall Mirror Spot', 'Ice Cave SE', 'Ice Cave SW']), create_lw_region(player, 'Desert Pass Area', ['Middle Aged Man'], ['Desert Pass Ladder (South)', 'Middle Aged Man', 'Desert Fairy', '50 Rupee Cave', 'Swamp Nook Mirror Spot', 'Desert Pass WS', 'Desert Pass EC', 'Desert Pass Rocks (North)']), create_lw_region(player, 'Middle Aged Man', ['Purple Chest'], None), diff --git a/Rules.py b/Rules.py index f934f2ca..6cef5f87 100644 --- a/Rules.py +++ b/Rules.py @@ -844,7 +844,7 @@ def default_rules(world, player): set_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Lake Hylia Central Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Lake Hylia Island Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Lake Hylia Water D Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Water D Leave', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Ice Cave SW', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) From 701b72462f4a3197da4ff2be382b056a0e328b51 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 5 May 2022 18:26:27 -0500 Subject: [PATCH 09/14] Unscramble lowercase letters for new font --- Rom.py | 2 +- data/base2current.bps | Bin 92460 -> 92682 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index e802a6cd..bad91344 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '32da3b3eb8f7a3e43f7c7351aeeddf11' +RANDOMIZERBASEHASH = 'c3f4161e3727103b69e5acbd3be75efa' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 3a185824a730cb3e8812df710e5ddd3bda7347ee..6631e3d4e9c93a5d904611f0db89f8ebcbf61aaa 100644 GIT binary patch delta 333 zcmZ2;iM8tr>jqaw<|!YuH+wM7=eOZ$n3lfg$t3%N))$i*U0YvF>UuG$;l)JHGhW_4 zXC|cu9-mhpci_ZCeo--T*%y6E9u1A^FDB~h>HGSHbWMIKpv_~f+R$;0zq@C9R@%DF z@`4_W^?&XeOciOkcSfXPoooBNj1HBCb)p^fB!Kj?waXmafs{-8vcfi?@ZWg>4ak&7 z6OdQhI1fz!ou}9cO4u~{+CFz?m(rb^751~N;`n&^{K=IWO{@So C!J7mC delta 134 zcmV;10D1q4)CH{41+YZ{19tI=vq%A-4+312lOqu;3vY*r)(?w}w{#$=vsw{I0SPqQ zfh$z`t9uobO%pN#Kr^#}69cR-pC3-#k2jo;P=}8dyOT1;=>?$$hykPpr3Huqs0FD7 ohyknxtp$hyum!ONpIwrZw7*9KGFj!b{=WpO2;iERd Date: Thu, 5 May 2022 23:25:09 -0500 Subject: [PATCH 10/14] Add lowercase letters to credits end screen --- Rom.py | 2 +- data/base2current.bps | Bin 92682 -> 91774 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index bad91344..80d6ee95 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'c3f4161e3727103b69e5acbd3be75efa' +RANDOMIZERBASEHASH = '538bf256ff03bb7576991114395eeccc' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 6631e3d4e9c93a5d904611f0db89f8ebcbf61aaa..a080ac84b84262bc669872057fafe51ef781590c 100644 GIT binary patch delta 2838 zcmWkw3s6&68a_9MBx)3VwLxPM42nvXQA8>+CW;nRkgBw(SOQ{HjEIN`ZbN-h^}>nP$j(nTX!aLvB?xvL&~jlB`8U6qqb4&(^?D&)N-wDkzl-Nyr^1qT{Uvr zz8Vz1=UTn74&LoJHhYLN0bDkFi2FyU5-4N}nE|Ay7R%6F@ymt6X^?v z?2THiMQ)@gT`R@)m)311Z5D&`;^aA>)6Yftq%!-pX zw+=H~Z2WeU*$Y{@1-DYr7&vb;8}70Zvxog9@?qz7Rki4qi0)(0)4N=3p zN0aRlE5GRZMOs_+2Do*MzP5_d-i1HQ% zCdgmXfP&s`bMQJHkI?>_!8S377RAj@Ps><=K;@wKC9qZr-l+QVTJ;Dj&l_PMpra6_ z*P8G_D>G3#+&fEqiK(18%;rExHShOG<|?Wo(HyJOr@g6sC1R^#`{Awiw!-&ThiAge z-lA~w2A*`h_m%!fe}=}J%980S06>_R>14x+ed{l*e-hqGwk?&;lNHPEB=K>WPBxT( z`isdNf{IhjfmOFCLgkCt z*T5uKoT6q4@xo=jbPerzApxUC|NKG*y54`sM_V0-qSUK@I3rh)XnZY2npn`leB%m= z7B#RjrOz2N>X_h+1ji$G#3L!iq%^y8iF#=4C?aX;Np?~_B?c&F%P=cQ9fqs@0w0P! z+fY7&)@=ymO&h{A#qO(h>`?4f?DEfF9Cr7EwPZG|M(dAvhm+U1=sll7y(rzY2kf$ujc#M8o_M;5sJC*p1YNfum%%OvqTRq z^x2+7m3oS3n!wbdMy&8*jSs6Eu-Z-~Mqy!tzvh%@+1lsSKLHkOZEaU(LC3ZPp3bGz zv<21a!)$(QKw-x%%*SW0EN5e{@MgqaLWOk!ZJkphJ|2-jmzAFIO2oG#GEQtJaid)# zejR~MVWq_}Ga^-d$RA`@coH&fZ-JnrJSj}eyl`g6<~c=YAndM`+(}CK_D4_2rf@3&f;Y#l z$E~k%xr+inGkt&5NBtZnc=MKj;khJPQY`x};fsulI=pt?P;k9)$P0iH6wA_6(e@K4 zryZJZc;@@4$D*JY5hNXZ!WkzolCM!BB7gA0D1b(+Z5{(0X8kRkziAL+&ked2U89ys z?-ArcD_qlEMB3i~vth)ICQ0s~)Q`gKwjQM6Gwe%F@)Z}`jSESePPT>-^|;c6BzyF8 zYOWkrlQ_b9xk=xoH!*r>JR+SceUF)6PrVWagftgxrPFJ1MTd<~_YQFvuIQq!s{&q> zYi6@9v-&G;vti6ZXFb2`8KBU0Xl9n0=>=9KvtV@)$6mRo?nE=X0u`4i-jL{wL6>Y? zJ<6Cohzue2?{>v+Dl^jbQa6?K zVR9h32dhbr=t5R%Lvla~;b@1rj?II#!VGoH)E?A=a(h_9jP}eNX4P|t9cr$iCFhLT zjER(=Q2IXHHWokS%`sxNhr>9ZDex`M>Bv!?L+Z}fd9p2uV-VEGEa}8KmvIh7&kj0^ zWt)=jC830XGp8k|CF((Y%@6d9Z|J;Bl;D3zYlX`glaFoleDdzFXUl|PJU4rg`HyeO zyBD|0n(WQmeT`^e&5sVcOy~aZnd2WoCJE1wbK1EC>6)2#*7g*<43~48$>me)P;&MO z1e{D6{v{`MX*;~vQF{8yK85Y-f#^E75$sU>RRKM_)^RYBso6+UQzj$HwA+E$PE_6M z%}mKmIXHEvqGO@YQ#ypJ69=3ELPQA1;p*w;hO!yVl@`@vddlewadI7>=y#Ip%9@!& zefJe+95ZWxo)}3&T}S!CK}sTa?MS-B#C{a)j%_UvO>t}cH;w~QlhNMk<}5fmI?6cM zwNn%5rU7HXm}6QX08e+}yht{W9G`ov3&wd?EwKkJLL&MT(|l4W z>im$@)S4{7@`$h?O`nNW_lM*fCgaN@6(zMH<*-Ow)iZP7ZM+C#uKL1(cAlxq}TqdR42pv zb#w;RdRK`JoY8A^`j0-wE58kV&`2NkunSsH@^_E&YeMXDAs4I=a+D0T%P*qC>(1HA zrP=<OR)r`dWJDqSfR2Xl zrvPYisRJ#8PSth5$CXo<{nMDsI^`N*1c0AA#*1Eg5}6AI0C z5~qLGwN@|SbvMFN2Tr_3D@gW8HiwpO;wXb9cxwr%u~J&h-hwiZCOBLagh;)cm^3{y zit~TEnR9xR`Kd+bn)FdKvZ2D=gj`VJMW)@vr<0(JDZFM+;_(5O^q_z9B5~v{E}%~1 zP6mLI~@Y(Ng{U3Ex@?!u1 delta 3725 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 Vyr Date: Thu, 5 May 2022 23:27:23 -0500 Subject: [PATCH 11/14] Adding mirror connection for Lake Hylia Water D --- OWEdges.py | 1 + OverworldShuffle.py | 1 + Regions.py | 2 +- Rules.py | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/OWEdges.py b/OWEdges.py index e3f7d20b..b79445a7 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -1863,6 +1863,7 @@ OWExitTypes = { 'South Shore East Mirror Spot', 'Lake Hylia Island Mirror Spot', 'Lake Hylia Water Mirror Spot', + 'Lake Hylia Water D Mirror Spot', 'Lake Hylia Central Island Mirror Spot', 'Ice Cave Mirror Spot', 'Desert Pass Ledge Mirror Spot', diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 8573f9d2..e6dddae4 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1540,6 +1540,7 @@ ow_connections = { ('Lake Hylia Island Mirror Spot', 'Lake Hylia Island'), ('Lake Hylia Central Island Mirror Spot', 'Lake Hylia Central Island'), ('Lake Hylia Water Mirror Spot', 'Lake Hylia Water'), + ('Lake Hylia Water D Mirror Spot', 'Lake Hylia Water D'), ('Lake Hylia Teleporter', 'Ice Palace Area') ], [ ('Lake Hylia Island Pier', 'Lake Hylia Island'), diff --git a/Regions.py b/Regions.py index 42bfd467..fe52b5f7 100644 --- a/Regions.py +++ b/Regions.py @@ -213,7 +213,7 @@ def create_regions(world, player): create_dw_region(player, 'Ice Lake Ledge (West)', None, ['Ice Lake Southwest Water Drop', 'South Shore Mirror Spot', 'Ice Lake WS']), create_dw_region(player, 'Ice Lake Ledge (East)', None, ['Ice Lake Southeast Water Drop', 'South Shore East Mirror Spot', 'Ice Lake ES']), create_dw_region(player, 'Ice Lake Water', None, ['Ice Lake Northeast Pier', 'Ice Lake Moat Bomb Jump', 'Lake Hylia Island Mirror Spot', 'Ice Lake NC', 'Ice Lake EC'], 'Dark World', Terrain.Water), - create_dw_region(player, 'Ice Lake Moat', None, ['Ice Lake Moat Water Entry', 'Ice Lake Northeast Pier Hop', 'Lake Hylia Water Mirror Spot']), + create_dw_region(player, 'Ice Lake Moat', None, ['Ice Lake Moat Water Entry', 'Ice Lake Northeast Pier Hop', 'Lake Hylia Water Mirror Spot', 'Lake Hylia Water D Mirror Spot']), create_dw_region(player, 'Ice Palace Area', None, ['Ice Palace', 'Ice Palace Teleporter', 'Lake Hylia Central Island Mirror Spot']), create_dw_region(player, 'Shopping Mall Area', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Ice Cave Mirror Spot', 'Shopping Mall SW', 'Shopping Mall SE']), create_dw_region(player, 'Swamp Nook Area', None, ['Desert Pass Ledge Mirror Spot', 'Desert Pass Mirror Spot', 'Swamp Nook EC', 'Swamp Nook ES']), diff --git a/Rules.py b/Rules.py index 6cef5f87..fe8b1396 100644 --- a/Rules.py +++ b/Rules.py @@ -1186,6 +1186,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player)) set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lake Hylia Water Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Lake Hylia Water D Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('South Shore Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('South Shore East Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lake Hylia Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) From 165fc26e333f82749697d5678d0fc0dcef55a4b3 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 10 May 2022 08:03:07 -0500 Subject: [PATCH 12/14] Add temporary new rule for Bomb Bag +5 upgrades --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index b9f6eed1..d8e34b6e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1278,7 +1278,7 @@ class CollectionState(object): # In the future, this can be used to check if the player starts without bombs def can_use_bombs(self, player): - return (not self.world.bombbag[player] or self.has('Bomb Upgrade (+10)', player)) and ((hasattr(self.world,"override_bomb_check") and self.world.override_bomb_check) or self.can_farm_bombs(player)) + return (not self.world.bombbag[player] or self.has('Bomb Upgrade (+10)', player) or self.has('Bomb Upgrade (+5)', player, 2)) and ((hasattr(self.world,"override_bomb_check") and self.world.override_bomb_check) or self.can_farm_bombs(player)) def can_hit_crystal(self, player): return (self.can_use_bombs(player) From 47d2aad9bb027b8eb2fdc45493da8813f511aa05 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 10 May 2022 08:04:42 -0500 Subject: [PATCH 13/14] Reformatting TF Room texts --- Text.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Text.py b/Text.py index 895babca..67c61df8 100644 --- a/Text.py +++ b/Text.py @@ -94,44 +94,44 @@ Triforce_texts = [ 'This was meant to be a trapezoid', # these ones are from web randomizer "\n G G", - "All your base\nare belong\nto us.", - "You have ended\nthe domination\nof Dr. Wily", + " All your base\n are belong\n to us.", + " You have ended\n the domination\n of Dr. Wily", " Thanks for\n playing!!!", "\n You Win!", " Thank you!\n Your quest\n is over.", " A winner\n is you!", "\n WINNER!!", - "\n I'm sorry\n\n but your\n princess is in\n another castle", + "\n I'm sorry\n\nbut our princess is\n in another castle", "\n Success!", " Whelp…\n that just\n happened", " Oh hey…\n it's you", "\n Wheeeeee!!", " Time for\n another one?", - "and\n\n scene", + " And\n\n scene", "\n GOT EM!!", - "\nTHE VALUUUE!!!", - "Cool seed,\n\nright?", + "\n THE VALUUUE!!!", + " Cool seed,\n\n right?", "\n We did it!", " Spam those\n emotes in\n wilds chat", "\n O M G", - " Hello. Will\n you be my\n friend?", + " Hello. Will you\n you be my friend?", " Beetorp\n was\n here!", - "The Wind Fish\nwill wake\nsoon. Hoot!", - "Meow Meow Meow\nMeow Meow Meow\n Oh My God!", - "Ahhhhhhhhh\nYa ya yaaaah\nYa ya yaaah", - ".done\n\n.comment lol", - "You get to\ndrink from\nthe firehose", - "Do you prefer\n bacon, pork,\n or ham?", - "You get one\nwish. Choose\nwisely, hero!", - "Can you please\nbreak us three\nup? Thanks.", + " The Wind Fish\n will wake soon.\n Hoot!", + " Meow Meow Meow\n Meow Meow Meow\n Oh My God!", + " Ahhhhhhhhh\n Ya ya yaaaah\n Ya ya yaaah", + " .done\n\n .comment lol", + " You get to\n drink from\n the firehose", + " Do you prefer\n bacon, pork,\n or ham?", + " You get one\n wish. Choose\n wisely, hero!", + " Can you please\n break us three\n up? Thanks.", " Pick us up\n before we\n get dizzy!", - "Thank you,\nMikey. You’re\n2 minutes late", - "This was a\n7000 series\ntrain.", + " Thank you,\n Mikey. You’re\n 2 minutes late", + " This was a\n 7000 series\n train.", " I'd buy\n that for\n a rupee!", " Did you like\n that bow\n placement?", - "I promise the\nnext seed will\nbe better.", - "\n Honk.", - "Breakfast\nis served!", + " I promise the\n next seed will\n be better.", + "\n Honk.", + " Breakfast\n is served!", ] BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!'] Sahasrahla2_texts = ['You already got my item, idiot.', 'Why are you still talking to me?', 'This text won\'t change.', 'Have you met my brother, Hasarahshla?'] From 9aff3d0b90ef245c8f841d738127a9cd284472d9 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 10 May 2022 08:09:38 -0500 Subject: [PATCH 14/14] Version bump 0.2.7.2 --- CHANGELOG.md | 5 +++++ OverworldShuffle.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4acbd0e7..ae14e2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 0.2.7.2 +- Special OW Area are now shuffled in Layout Shuffle (Zora/Hobo/Pedestal) +- Fixed some broken water region graph modelling, fixed some reachability logic +- Some minor code simplifications + ### 0.2.7.1 - Map checks in Mixed OWR now will show the proper tile images when screens are swapped (ie. Pyramid shows in the LW if that screen is swapped) - Added mystery seed number to spoiler log, so it is easier to match a spoiler log to a rom filename diff --git a/OverworldShuffle.py b/OverworldShuffle.py index e6dddae4..4f391dc6 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -5,7 +5,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_number = '0.2.7.1' +version_number = '0.2.7.2' version_branch = '-u' __version__ = '%s%s' % (version_number, version_branch)