Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -304,6 +304,9 @@ class World(object):
|
||||
def is_tile_swapped(self, owid, player):
|
||||
return (self.mode[player] == 'inverted') != (owid in self.owswaps[player][0] and self.owMixed[player])
|
||||
|
||||
def is_atgt_swapped(self, player):
|
||||
return (0x03 in self.owswaps[player][0]) == (0x1b in self.owswaps[player][0]) == (self.mode[player] != 'inverted')
|
||||
|
||||
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])
|
||||
|
||||
@@ -3053,6 +3056,18 @@ class Spoiler(object):
|
||||
|
||||
if self.overworlds:
|
||||
outfile.write('\n\nOverworld:\n\n')
|
||||
|
||||
# flute shuffle
|
||||
for player in range(1, self.world.players + 1):
|
||||
if ('flute', player) in self.maps:
|
||||
outfile.write('Flute Spots:\n')
|
||||
break
|
||||
for player in range(1, self.world.players + 1):
|
||||
if ('flute', player) in self.maps:
|
||||
if self.world.players > 1:
|
||||
outfile.write(str('(Player ' + str(player) + ')\n')) # player name
|
||||
outfile.write(self.maps[('flute', player)]['text'] + '\n\n')
|
||||
|
||||
# overworld tile swaps
|
||||
for player in range(1, self.world.players + 1):
|
||||
if ('swaps', player) in self.maps:
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
### 0.2.5.3
|
||||
- Changed AT/GT Swap to favor vanilla, only swapping if GT entrance is the only choice in starting world
|
||||
- Fixed issue with Links House not swapping in OW Mixed
|
||||
- Added Flute Spots to spoiler log
|
||||
- Fixed issue with Light Hype Fairy excluded from bombable door list
|
||||
|
||||
### 0.2.5.1
|
||||
- Fixed missing rule for Inverted VoO Portal access
|
||||
|
||||
|
||||
@@ -90,11 +90,11 @@ def link_entrances(world, player):
|
||||
for entrancename, exitname in default_skulldrop_connections:
|
||||
connect_logical(world, entrancename, exitname, player, False)
|
||||
|
||||
if not invFlag:
|
||||
for entrancename, exitname in open_default_dungeon_connections:
|
||||
if world.is_atgt_swapped(player):
|
||||
for entrancename, exitname in inverted_default_dungeon_connections:
|
||||
connect_logical(world, entrancename, exitname, player, True)
|
||||
else:
|
||||
for entrancename, exitname in inverted_default_dungeon_connections:
|
||||
for entrancename, exitname in open_default_dungeon_connections:
|
||||
connect_logical(world, entrancename, exitname, player, True)
|
||||
elif world.shuffle[player] == 'dungeonssimple':
|
||||
suppress_spoiler = False
|
||||
@@ -288,7 +288,7 @@ def link_entrances(world, player):
|
||||
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
caves.append('Ganons Tower Exit')
|
||||
|
||||
@@ -361,7 +361,7 @@ def link_entrances(world, player):
|
||||
lw_dungeons.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')))
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
dw_dungeons.append('Ganons Tower Exit')
|
||||
|
||||
@@ -449,7 +449,7 @@ def link_entrances(world, player):
|
||||
Dungeon_Exits.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')))
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
Dungeon_Exits.append('Ganons Tower Exit')
|
||||
|
||||
@@ -499,7 +499,7 @@ def link_entrances(world, player):
|
||||
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
caves.append('Ganons Tower Exit')
|
||||
|
||||
@@ -567,7 +567,7 @@ def link_entrances(world, player):
|
||||
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
@@ -662,7 +662,7 @@ def link_entrances(world, player):
|
||||
world.ganon_at_pyramid[player] = False
|
||||
|
||||
# check for Ganon's Tower location
|
||||
if world.get_entrance('Ganons Tower' if not invFlag else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby':
|
||||
if world.get_entrance('Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby':
|
||||
world.ganonstower_vanilla[player] = False
|
||||
|
||||
|
||||
@@ -1235,7 +1235,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
|
||||
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
dungeon_exits.append('Ganons Tower Exit')
|
||||
|
||||
|
||||
@@ -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__ = '0.2.5.2-u'
|
||||
__version__ = '0.2.5.3-u'
|
||||
|
||||
def link_overworld(world, player):
|
||||
# setup mandatory connections
|
||||
@@ -67,8 +67,12 @@ def link_overworld(world, player):
|
||||
# move both sets
|
||||
if parallel == IsParallel.Yes and not (all(edge in orig_swaps for edge in map(getParallel, forward_set)) and all(edge in orig_swaps for edge in map(getParallel, back_set))):
|
||||
raise Exception('Cannot move a parallel edge without the other')
|
||||
new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set)
|
||||
new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set)
|
||||
new_mode = OpenStd.Open
|
||||
if tuple((OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)) not in new_groups.keys():
|
||||
# when Links House tile is swapped, the DW edges need to get put into existing Standard group
|
||||
new_mode = OpenStd.Standard
|
||||
new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set)
|
||||
new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set)
|
||||
for edge in forward_set:
|
||||
swaps.remove(edge)
|
||||
for edge in back_set:
|
||||
@@ -416,6 +420,24 @@ def link_overworld(world, player):
|
||||
world.owflutespots[player] = new_spots
|
||||
connect_flutes(new_spots)
|
||||
|
||||
# update spoiler
|
||||
s = list(map(lambda x: ' ' if x not in new_spots else 'F', [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('flute', text_output, new_spots, player)
|
||||
|
||||
def connect_custom(world, connected_edges, player):
|
||||
if hasattr(world, 'custom_overworld') and world.custom_overworld[player]:
|
||||
for edgename1, edgename2 in world.custom_overworld[player]:
|
||||
|
||||
26
Rom.py
26
Rom.py
@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = 'b300438a7242825e33300f9d155814ef'
|
||||
RANDOMIZERBASEHASH = '9ff49ef63fdddeb32de09646bd459bf8'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -877,7 +877,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
rom.write_byte(0x138006, 1)
|
||||
|
||||
# swap in non-ER Lobby Shuffle Inverted - but only then
|
||||
if world.mode[player] == 'inverted' and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla':
|
||||
if world.is_atgt_swapped(player) and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla':
|
||||
aga_portal = world.get_portal('Agahnims Tower', player)
|
||||
gt_portal = world.get_portal('Ganons Tower', player)
|
||||
aga_portal.exit_offset, gt_portal.exit_offset = gt_portal.exit_offset, aga_portal.exit_offset
|
||||
@@ -1274,7 +1274,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
# assorted fixes
|
||||
rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1
|
||||
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
|
||||
if world.mode[player] == 'inverted':
|
||||
if world.is_atgt_swapped(player):
|
||||
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
|
||||
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death
|
||||
rom.write_byte(0x180173, 0x01) # Bob is enabled
|
||||
@@ -1286,6 +1286,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
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(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
|
||||
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
|
||||
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
|
||||
@@ -2168,7 +2169,7 @@ def write_strings(rom, world, player, team):
|
||||
entrances_to_hint = {}
|
||||
entrances_to_hint.update(InconvenientDungeonEntrances)
|
||||
if world.shuffle_ganon:
|
||||
if world.mode[player] == 'inverted':
|
||||
if world.is_atgt_swapped(player):
|
||||
entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'})
|
||||
else:
|
||||
entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'})
|
||||
@@ -2201,7 +2202,7 @@ def write_strings(rom, world, player, team):
|
||||
if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']:
|
||||
entrances_to_hint.update(ConnectorEntrances)
|
||||
entrances_to_hint.update(DungeonEntrances)
|
||||
if world.mode[player] == 'inverted':
|
||||
if world.is_atgt_swapped(player):
|
||||
entrances_to_hint.update({'Ganons Tower': 'The dark mountain tower'})
|
||||
else:
|
||||
entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'})
|
||||
@@ -2520,13 +2521,14 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
|
||||
write_int16(rom, snes_to_pc(0x02D998), 0x0000)
|
||||
write_int16(rom, snes_to_pc(0x02D9A6), 0x005A)
|
||||
rom.write_byte(snes_to_pc(0x02D9B3), 0x12)
|
||||
|
||||
if world.shuffle[player] == 'vanilla':
|
||||
rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT
|
||||
rom.write_byte(0xDBB73 + 0x36, 0x24)
|
||||
if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3:
|
||||
write_int16(rom, 0x15AEE + 2*0x38, 0x00E0)
|
||||
write_int16(rom, 0x15AEE + 2*0x25, 0x000C)
|
||||
|
||||
# switch AT and GT
|
||||
if world.shuffle[player] == 'vanilla' and world.is_atgt_swapped(player):
|
||||
rom.write_byte(0xDBB73 + 0x23, 0x37)
|
||||
rom.write_byte(0xDBB73 + 0x36, 0x24)
|
||||
if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3:
|
||||
write_int16(rom, 0x15AEE + 2*0x38, 0x00E0)
|
||||
write_int16(rom, 0x15AEE + 2*0x25, 0x000C)
|
||||
|
||||
|
||||
if world.is_tile_swapped(0x05, player):
|
||||
|
||||
8
Rules.py
8
Rules.py
@@ -854,12 +854,12 @@ def default_rules(world, player):
|
||||
|
||||
|
||||
def ow_rules(world, player):
|
||||
if world.mode[player] != 'inverted':
|
||||
if world.is_atgt_swapped(player):
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||
set_rule(world.get_entrance('GT Entry Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||
set_rule(world.get_entrance('GT Entry Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'crossed', 'insanity'))
|
||||
else:
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||
|
||||
if not world.is_tile_swapped(0x00, player):
|
||||
set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -1524,7 +1524,7 @@ def swordless_rules(world, player):
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
|
||||
|
||||
if world.mode[player] != 'inverted':
|
||||
if not world.is_atgt_swapped(player):
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||
|
||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user