Merge remote-tracking branch 'origin/DoorDev' into DoorDev

# Conflicts:
#	asm/spiral.asm
This commit is contained in:
aerinon
2019-09-06 17:36:33 -06:00
12 changed files with 101 additions and 62 deletions

View File

@@ -284,7 +284,7 @@ class World(object):
def has_beaten_game(self, state, player=None):
if player:
return state.has('Triforce', player) or (self.goal in ['triforcehunt'] and (state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > self.treasure_hunt_count))
return state.has('Triforce', player)
else:
return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1)))
@@ -860,7 +860,7 @@ class Door(object):
layer = 4 * (self.layer ^ 1 if toggle else self.layer)
return [self.roomIndex, layer + self.doorIndex]
if self.type == DoorType.SpiralStairs:
bitmask = 0x04 * int(self.layer)
bitmask = int(self.layer) << 2
bitmask += 0x10 * int(self.zeroHzCam)
bitmask += 0x20 * int(self.zeroVtCam)
return [self.roomIndex, bitmask + self.quadrant, self.shiftX, self.shiftY]

View File

@@ -119,11 +119,11 @@ def can_place_boss(world, boss, dungeon_name, level=None):
if world.swords in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace':
return False
if dungeon_name == 'Ganons Tower' and level == 'top':
if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'top':
if boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]:
return False
if dungeon_name == 'Ganons Tower' and level == 'middle':
if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'middle':
if boss in ["Blind"]:
return False

View File

@@ -16,6 +16,12 @@ A = 0
S = 1
Z = 2
X = 3
# Layer transitions
HTH = 0 # High to High 00
HTL = 1 # High to Low 01
LTH = 2 # Low to High 10
LTL = 3 # Low to Low 11
def create_doors(world, player):
@@ -38,12 +44,12 @@ def create_doors(world, player):
create_dir_door(player, 'Hyrule Castle West Hall S', DoorType.Normal, Direction.South, 0x50, Right, Low),
create_dir_door(player, 'Hyrule Castle Back Hall W', DoorType.Normal, Direction.West, 0x01, Top, Low),
create_dir_door(player, 'Hyrule Castle Back Hall E', DoorType.Normal, Direction.East, 0x01, Top, Low),
create_spiral_stairs(player, 'Hyrule Castle Back Hall Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x01, 0, Low, A, 0x37, 0xf8),
create_spiral_stairs(player, 'Hyrule Castle Back Hall Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x01, 0, HTL, A, 0x2a, 0x00),
create_dir_door(player, 'Hyrule Castle Throne Room N', DoorType.Normal, Direction.North, 0x51, Mid, High),
create_dir_door(player, 'Hyrule Castle Throne Room South Stairs', DoorType.StraightStairs, Direction.South, 0x51, Mid, Low),
# hyrule dungeon level
create_spiral_stairs(player, 'Hyrule Dungeon Map Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x72, 0, High, A, 0x4f, 0xf8),
create_spiral_stairs(player, 'Hyrule Dungeon Map Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x72, 0, LTH, A, 0x4b, 0xec),
create_dir_door(player, 'Hyrule Dungeon North Abyss South Edge', DoorType.Open, Direction.South, 0x72, None, Low),
create_dir_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', DoorType.Open, Direction.South, 0x72, None, High),
create_dir_door(player, 'Hyrule Dungeon South Abyss North Edge', DoorType.Open, Direction.North, 0x82, None, Low),
@@ -54,15 +60,15 @@ def create_doors(world, player):
create_dir_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', DoorType.Open, Direction.West, 0x81, None, High),
create_dir_door(player, 'Hyrule Dungeon Guardroom N', DoorType.Normal, Direction.North, 0x81, Left, Low), # todo: is this a toggle door?
create_dir_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South, 0x71, Left, Low), # not sure what the layer should be here
create_spiral_stairs(player, 'Hyrule Dungeon Armory Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x71, 0, Low, A, 0x1e, 0xa0, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x70, 2, High, A, 0x36, 0xa0, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x70, 1, High, A, 0x1f, 0x50),
create_spiral_stairs(player, 'Hyrule Dungeon Cellblock Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x80, 0, High, A, 0x1f, 0x50),
create_spiral_stairs(player, 'Hyrule Dungeon Armory Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x71, 0, HTL, A, 0x11, 0xa8, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x70, 2, LTH, A, 0x32, 0x94, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x70, 1, HTH, A, 0x11, 0x58),
create_spiral_stairs(player, 'Hyrule Dungeon Cellblock Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x80, 0, HTH, A, 0x1a, 0x44),
# sewers
create_blocked_door(player, 'Sewers Behind Tapestry S', DoorType.Normal, Direction.South, 0x41, Mid, High),
create_spiral_stairs(player, 'Sewers Behind Tapestry Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x41, 0, High, S, 0x1f, 0xa8),
create_spiral_stairs(player, 'Sewers Rope Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x42, 0, High, S, 0x1f, 0xa8),
create_spiral_stairs(player, 'Sewers Behind Tapestry Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x41, 0, HTH, S, 0x12, 0xb0),
create_spiral_stairs(player, 'Sewers Rope Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x42, 0, HTH, S, 0x1b, 0x9c),
create_dir_door(player, 'Sewers Rope Room North Stairs', DoorType.StraightStairs, Direction.North, 0x42, Mid, High),
create_dir_door(player, 'Sewers Dark Cross South Stairs', DoorType.StraightStairs, Direction.South, 0x32, Mid, High),
create_dir_door(player, 'Sewers Dark Cross Key Door N', DoorType.Normal, Direction.North, 0x32, Mid, High),
@@ -71,8 +77,8 @@ def create_doors(world, player):
create_dir_door(player, 'Sewers Key Rat E', DoorType.Normal, Direction.East, 0x21, Bot, High),
create_small_key_door(player, 'Sewers Key Rat Key Door N', DoorType.Normal, Direction.North, 0x21, Right, High),
create_small_key_door(player, 'Sewers Secret Room Key Door S', DoorType.Normal, Direction.South, 0x11, Right, High),
create_spiral_stairs(player, 'Sewers Secret Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x11, 0, High, S, 0x37, 0x78),
create_spiral_stairs(player, 'Sewers Pull Switch Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x02, 0, High, S, 0x1f, 0x78),
create_spiral_stairs(player, 'Sewers Secret Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x11, 0, LTH, S, 0x33, 0x6c, True),
create_spiral_stairs(player, 'Sewers Pull Switch Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x02, 0, HTL, S, 0x12, 0x80),
create_toggle_door(player, 'Sewers Pull Switch S', DoorType.Normal, Direction.South, 0x02, Mid, Low),
# logically one way the sanc, but should be linked - also toggle
create_blocked_door(player, 'Sanctuary N', DoorType.Normal, Direction.North, 0x12, Mid, 0, True),
@@ -104,8 +110,8 @@ def create_doors(world, player):
create_small_key_door(player, 'Eastern Big Key EN', DoorType.Normal, Direction.East, 0xb8, Top, High),
create_dir_door(player, 'Eastern Big Key NE', DoorType.Normal, Direction.North, 0xb8, Right, High),
create_small_key_door(player, 'Eastern Darkness S', DoorType.Normal, Direction.South, 0x99, Mid, High),
create_spiral_stairs(player, 'Eastern Darkness Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x99, 0, High, Z, 0x1e, 0x78, False, True),
create_spiral_stairs(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs, Direction.Down, 0xda, 0, High, Z, 0x1f, 0x78, False, True),
create_spiral_stairs(player, 'Eastern Darkness Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x99, 0, HTH, Z, 0x1a, 0x6c, False, True),
create_spiral_stairs(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs, Direction.Down, 0xda, 0, HTH, Z, 0x11, 0x80, False, True),
create_dir_door(player, 'Eastern Attic Start WS', DoorType.Normal, Direction.West, 0xda, Bot, High),
create_dir_door(player, 'Eastern Attic Switches ES', DoorType.Normal, Direction.East, 0xd9, Bot, High),
create_dir_door(player, 'Eastern Attic Switches WS', DoorType.Normal, Direction.West, 0xd9, Bot, High),

View File

@@ -3507,8 +3507,8 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'
('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'),
('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'),
('Bumper Cave Exit (Bottom)', 'Light World'),
('Death Mountain Return Cave (East)', 'Bumper Cave'),
('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'),
('Death Mountain Return Cave (West)', 'Bumper Cave'),
('Death Mountain Return Cave (East)', 'Death Mountain Return Cave'),
('Death Mountain Return Cave Exit (West)', 'Death Mountain'),
('Death Mountain Return Cave Exit (East)', 'Death Mountain'),
('Hookshot Cave Exit (South)', 'Dark Death Mountain'),

View File

@@ -137,6 +137,23 @@ def generate_itempool(world, player):
else:
world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
if world.goal in ['triforcehunt']:
if world.mode == 'inverted':
region = world.get_region('Light World',player)
else:
region = world.get_region('Hyrule Castle Courtyard', player)
loc = Location(player, "Murahdahla", parent=region)
loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > state.world.treasure_hunt_count
region.locations.append(loc)
world.dynamic_locations.append(loc)
world.clear_location_cache()
world.push_item(loc, ItemFactory('Triforce', player), False)
loc.event = True
loc.locked = True
world.get_location('Ganon', player).event = True
world.get_location('Ganon', player).locked = True
world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False)

View File

@@ -285,6 +285,11 @@ def copy_dynamic_regions_and_locations(world, ret):
for location in world.dynamic_locations:
new_reg = ret.get_region(location.parent_region.name, location.parent_region.player)
new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, new_reg)
# todo: this is potentially dangerous. later refactor so we
# can apply dynamic region rules on top of copied world like other rules
new_loc.access_rule = location.access_rule
new_loc.always_allow = location.always_allow
new_loc.item_rule = location.item_rule
new_reg.locations.append(new_loc)
ret.clear_location_cache()

11
Rom.py
View File

@@ -18,7 +18,7 @@ from EntranceShuffle import door_addresses
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '6b3a06053a9751e976a228ded2b0439d'
RANDOMIZERBASEHASH = 'e04929f8b0c2393c169af62544f58a7c'
class JsonRom(object):
@@ -842,6 +842,7 @@ def patch_rom(world, player, rom):
# set up goals for treasure hunt
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28])
rom.write_byte(0x180167, world.treasure_hunt_count % 256)
rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
# TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc)
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
@@ -1282,17 +1283,18 @@ def write_strings(rom, world, player):
random.shuffle(silverarrows)
silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
prog_bow_locs = world.find_items('Progressive Bow', player)
distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None)
if distinguished_prog_bow_loc:
prog_bow_locs.remove(distinguished_prog_bow_loc)
silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my'))
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
if any(prog_bow_locs):
silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my'))
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
@@ -1323,6 +1325,7 @@ def write_strings(rom, world, player):
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!'
tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count
elif world.goal in ['pedestal']:
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
@@ -1406,8 +1409,6 @@ def set_inverted_mode(world, rom):
rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute
rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8)
rom.write_int16(snes_to_pc(0x02E8F7), 0x01F8)
rom.write_byte(0x7A943, 0xF0)
rom.write_byte(0x7A96D, 0xD0)
rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof
# the following bytes should only be written in vanilla
# or they'll overwrite the randomizer's shuffles

View File

@@ -1890,5 +1890,6 @@ class TextTable(object):
text['ganon_phase_3_no_silvers_alt'] = CompressedTextMapper.convert("You can't best me without silver arrows!")
text['ganon_phase_3_no_silvers'] = CompressedTextMapper.convert("You can't best me without silver arrows!")
text['ganon_phase_3_silvers'] = CompressedTextMapper.convert("Oh no! Silver! My one true weakness!")
text['murahdahla'] = CompressedTextMapper.convert("Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n{PAUSE3}\n… … …\nWait! you can see me? I knew I should have\nhidden in a hollow tree.")
text['end_pad_data'] = bytearray([0xfb])
text['terminator'] = bytearray([0xFF, 0xFF])

View File

@@ -27,9 +27,10 @@ NotLinkDoor2:
; Staircase routine
;org $02a1e7 ;(PC: 121e7)
org $01c3d4 ;(PC: c3d4)
jsl SpiralWarp : nop #4
jsl RecordStairType : nop
org $02a1e7 ;(PC: 121e7)
jsl SpiralWarp
; Graphics fix

View File

@@ -1,12 +1,18 @@
RecordStairType: {
lda $0e : sta $045e
lda $a0 : lda $063d, x
rtl
}
SpiralWarp: {
pha : lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
cmp #$06 : bcs .abort ; abort if not supported yet -- todo: this needs to be altered/removed as more dungeons are implemented
lda $0e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
cmp #$5f : beq .gtg
.abort pla : sta $a0 : bra .end
.abort
lda $a2 : and #$0f : rtl ; run highjack code and get out
.gtg
pla
phb : phk : plb : phx : phy ; push stuff
jsr LookupSpiralOffset
rep #$30 : and #$00FF : asl #2 : tax
@@ -28,9 +34,15 @@ SpiralWarp: {
.skipYQuad
lda $01 : and #$04 : lsr : sta $048a ;fix layer calc 0->0 2->1
lda $01 : and #$08 : lsr #2 : sta $0492 ;fix from layer calc 0->0 2->1
; shift lower coordinates
lda $02 : sta $22
lda $03 : sta $20
lda $02 : sta $22 : bne .adjY : inc $23
.adjY lda $03 : sta $20 : bne .set53 : inc $21
.set53 ldx #$08
lda $0462 : and #$04 : beq .upStairs
ldx #$fd
.upStairs
txa : !add $22 : sta $53
lda $01 : and #$10 : sta $07 ; zeroHzCam check
ldy #$00 : jsr SetCamera
@@ -38,12 +50,8 @@ SpiralWarp: {
ldy #$01 : jsr SetCamera
ply : plx : plb ; pull the stuff we pushed
.end rtl
; this is the code we are hijacking
; lda $a0 - we overwrote all this behavior
; lda $063d, x
; sta $048a
lda $a2 : and #$0f ; this is the code we are hijacking
rtl
}
;Sets the offset in A
@@ -86,7 +94,9 @@ LookupSpiralOffset: {
ShiftQuadSimple: {
lda CoordIndex,y : tax
lda $20,x : beq .skip
lda $21,x : !add $06 : sta $21,x ; coordinate update
.skip
lda CamQuadIndex,y : tax
lda $0601,x : !add $06 : sta $0601,x
lda $0605,x : !add $06 : sta $0605,x ; high bytes of these guys
@@ -94,8 +104,7 @@ ShiftQuadSimple: {
}
SetCamera: {
stz $04 : sty $05
stz $04
tyx : lda $a9,x : bne .nonZeroHalf
lda CamQuadIndex,y : tax : lda $607,x : pha
lda CameraIndex,y : tax : pla : cmp $e3, x : bne .noQuadAdj
@@ -104,27 +113,30 @@ SetCamera: {
.noQuadAdj
lda $07 : bne .adj0
lda CoordIndex,y : tax
lda $20,x : cmp #$79 : bcc .adj0
lda $20,x : beq .oddQuad
cmp #$79 : bcc .adj0
!sub #$78 : sta $04
tya : asl : add #$04 : tax : jsr AdjCamBounds : bra .done
tya : asl : !add #$04 : tax : jsr AdjCamBounds : bra .done
.oddQuad
lda #$80 : sta $04 : bra .adj1 ; this is such a weird case - quad cross boundary
.adj0
tya : asl : tax : jsr AdjCamBounds : bra .done
.nonZeroHalf ;meaning either right half or bottom half
lda $07 : bne .setQuad
lda CoordIndex,y : tax
lda $20,x : cmp #$78 : bcs .setQuad
lda $07 : bne .adj1
!add #$78 : sta $04
tya : asl : add #$08 : tax : jsr AdjCamBounds : bra .done
.adj1
tya : asl : !add #$08 : tax : jsr AdjCamBounds : bra .done
.setQuad
lda CamQuadIndex,y : tax : lda $0607, x : pha
lda CameraIndex,y : tax : pla : sta $e3, x
.adj1
tya : asl : add #$0c : tax : jsr AdjCamBounds : bra .done
tya : asl : !add #$0c : tax : jsr AdjCamBounds : bra .done
.done
lda CameraIndex,y : tax : lda $e2, x : phx
lda CameraIndex,y : tax
lda $04 : sta $e2, x
rts
}
@@ -141,18 +153,4 @@ AdjCamBounds: {
lda $05 : sta $0618, x
inc #2 : sta $061A, x : sep #$20
rts
}
; todo - should I delete this, yet?
;input : A should be loaded with current camera low byte
; $04 should be loaded with what we want the low byte to become (00-ff)
AdjCamBoundsOld:
{
stz $05 : rep #$30 : and #$00FF ; sanitize input for 16 bit
!sub $04 : sta $04
lda OppCamBoundIndex,y : tax
lda $0618, x : !sub $04 : sta $0618, x
lda $061A, x : !sub $04 : sta $061A, x
sep #$30
rts
}

File diff suppressed because one or more lines are too long

View File

@@ -13,4 +13,14 @@ Notes 2:
(Fixed partially )Guardroom N Door is set to $8000, I think it was supposed to be Eastern Compass Area SW $02a8 that how this get teleported back to HC. Wrong room index. Still might be a toggle door
(Fixed) Courtyard N is set to $02D8 - typo
(Fixed) Sanc N is $0650 but should be $0250 which I think is right. typo again on the wrong layer
(Fixed) Eastern boss had wrong room
(Fixed) Eastern boss had wrong room
Pairing Notes
30-34 4f-37 #$18,-24
30-34 37-1f #$18,-24
30-34 1f-1f???
30-35 1f-1f
30-34 36-1e #$18,-24
30-34 1e-1f
inconsistencies are caused by $0492 being set wrong