Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -19,13 +19,14 @@ from RoomData import Room
|
||||
|
||||
class World(object):
|
||||
|
||||
def __init__(self, players, owShuffle, owSwap, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
|
||||
def __init__(self, players, owShuffle, owCrossed, owMixed, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
|
||||
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
||||
self.players = players
|
||||
self.teams = 1
|
||||
self.owShuffle = owShuffle.copy()
|
||||
self.owSwap = owSwap.copy()
|
||||
self.owCrossed = owCrossed.copy()
|
||||
self.owKeepSimilar = {}
|
||||
self.owMixed = owMixed.copy()
|
||||
self.owFluteShuffle = {}
|
||||
self.shuffle = shuffle.copy()
|
||||
self.doorShuffle = doorShuffle.copy()
|
||||
@@ -2308,8 +2309,9 @@ class Spoiler(object):
|
||||
'weapons': self.world.swords,
|
||||
'goal': self.world.goal,
|
||||
'ow_shuffle': self.world.owShuffle,
|
||||
'ow_swap': self.world.owSwap,
|
||||
'ow_crossed': self.world.owCrossed,
|
||||
'ow_keepsimilar': self.world.owKeepSimilar,
|
||||
'ow_mixed': self.world.owMixed,
|
||||
'ow_fluteshuffle': self.world.owFluteShuffle,
|
||||
'shuffle': self.world.shuffle,
|
||||
'door_shuffle': self.world.doorShuffle,
|
||||
@@ -2390,9 +2392,10 @@ class Spoiler(object):
|
||||
outfile.write('Difficulty:'.ljust(line_width) + '%s\n' % self.metadata['item_pool'][player])
|
||||
outfile.write('Item Functionality:'.ljust(line_width) + '%s\n' % self.metadata['item_functionality'][player])
|
||||
outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player])
|
||||
outfile.write('Overworld Tile Swap:'.ljust(line_width) + '%s\n' % self.metadata['ow_swap'][player])
|
||||
if self.metadata['ow_shuffle'][player] != 'vanilla':
|
||||
outfile.write('Crossed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_crossed'][player] else 'No'))
|
||||
outfile.write('Keep Similar OW Edges Together:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_keepsimilar'][player] else 'No'))
|
||||
outfile.write('Mixed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No'))
|
||||
outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player])
|
||||
outfile.write('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player])
|
||||
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
|
||||
@@ -2553,11 +2556,10 @@ class Pot(object):
|
||||
self.flags = flags
|
||||
|
||||
|
||||
# byte 0: DDOO EEEE (DR, OR, ER)
|
||||
# byte 0: DDOO OEEE (DR, OR, ER)
|
||||
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0}
|
||||
or_mode = {"parallel": 1, "full": 2, "vanilla": 0}
|
||||
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, "restricted_legacy": 8,
|
||||
"full_legacy": 9, "madness_legacy": 10, "insanity_legacy": 11, "dungeonsfull": 7, "dungeonssimple": 6}
|
||||
or_mode = {"vanilla": 0, "parallel": 1, "full": 1}
|
||||
er_mode = {"vanilla": 0, "simple": 1, "restricted": 3, "full": 3, "crossed": 4, "insanity": 5, "dungeonsfull": 7, "dungeonssimple": 7}
|
||||
|
||||
# byte 1: LLLW WSSR (logic, mode, sword, retro)
|
||||
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
|
||||
@@ -2592,7 +2594,7 @@ class Settings(object):
|
||||
@staticmethod
|
||||
def make_code(w, p):
|
||||
code = bytes([
|
||||
(dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 4) | er_mode[w.shuffle[p]],
|
||||
(dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 5) | (0x10 if w.owCrossed[p] else 0) | (0x08 if w.owMixed[p] else 0) | er_mode[w.shuffle[p]],
|
||||
|
||||
(logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3)
|
||||
| (sword_mode[w.swords[p]] << 1) | (1 if w.retro[p] else 0),
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
### 0.1.8.0
|
||||
- Moved Crossed to its own checkbox option
|
||||
- Removed Legacy ER shuffles
|
||||
- Added OW Shuffle support for Plando module (needs user testing)
|
||||
- Fixed issue with Sanc start at TR as bunny when it is LW
|
||||
- Fixed issue with Pyramid Hole not getting shuffled
|
||||
- ~~Merged DR v0.5.0.3 - Minor DR fixes~~
|
||||
|
||||
### 0.1.7.4
|
||||
- Fixed issue with Mixed OW failing to generate when HC/Pyramid is swapped
|
||||
- Various fixes to improve generation rates for Mixed OW Shuffle
|
||||
|
||||
5
CLI.py
5
CLI.py
@@ -94,7 +94,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
|
||||
|
||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
||||
'ow_shuffle', 'ow_swap', 'ow_keepsimilar', 'ow_fluteshuffle',
|
||||
'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_fluteshuffle',
|
||||
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
||||
'bombbag',
|
||||
@@ -146,8 +146,9 @@ def parse_settings():
|
||||
"openpyramid": False,
|
||||
"shuffleganon": True,
|
||||
"ow_shuffle": "vanilla",
|
||||
"ow_swap": "vanilla",
|
||||
"ow_crossed": False,
|
||||
"ow_keepsimilar": False,
|
||||
"ow_mixed": False,
|
||||
"ow_fluteshuffle": "vanilla",
|
||||
"shuffle": "vanilla",
|
||||
"shufflelinks": False,
|
||||
|
||||
@@ -214,7 +214,7 @@ def vanilla_key_logic(world, player):
|
||||
analyze_dungeon(key_layout, world, player)
|
||||
world.key_logic[player][builder.name] = key_layout.key_logic
|
||||
log_key_logic(builder.name, key_layout.key_logic)
|
||||
if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and world.owSwap[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]:
|
||||
if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and not world.owCrossed[player] and world.owMixed[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]:
|
||||
validate_vanilla_key_logic(world, player)
|
||||
|
||||
|
||||
@@ -1071,6 +1071,7 @@ def assign_cross_keys(dungeon_builders, world, player):
|
||||
# Step 3: Initial valid combination find - reduce flex if needed
|
||||
for name, builder in dungeon_builders.items():
|
||||
suggested = builder.key_doors_num - builder.key_drop_cnt
|
||||
builder.total_keys = builder.key_doors_num
|
||||
find_valid_combination(builder, start_regions_map[name], world, player)
|
||||
actual_chest_keys = builder.key_doors_num - builder.key_drop_cnt
|
||||
if actual_chest_keys < suggested:
|
||||
@@ -1087,6 +1088,7 @@ def assign_cross_keys(dungeon_builders, world, player):
|
||||
name = builder.name
|
||||
logger.debug('Cross Dungeon: Increasing key count by 1 for %s', name)
|
||||
builder.key_doors_num += 1
|
||||
builder.total_keys = builder.key_doors_num
|
||||
result = find_valid_combination(builder, start_regions_map[name], world, player, drop_keys=False)
|
||||
if result:
|
||||
remaining -= 1
|
||||
@@ -1380,7 +1382,7 @@ def shuffle_key_doors(builder, world, player):
|
||||
skips.append(world.get_door(dp.door_a, player))
|
||||
break
|
||||
num_key_doors += 1
|
||||
builder.key_doors_num = num_key_doors
|
||||
builder.key_doors_num = builder.total_keys = num_key_doors
|
||||
find_small_key_door_candidates(builder, start_regions, world, player)
|
||||
find_valid_combination(builder, start_regions, world, player)
|
||||
reassign_key_doors(builder, world, player)
|
||||
|
||||
@@ -1175,6 +1175,7 @@ class DungeonBuilder(object):
|
||||
self.pre_open_stonewalls = set() # used by stonewall system
|
||||
|
||||
self.candidates = None
|
||||
self.total_keys = None
|
||||
self.key_doors_num = None
|
||||
self.combo_size = None
|
||||
self.flex = 0
|
||||
|
||||
@@ -375,6 +375,13 @@ flexible_starts = {
|
||||
'Skull Woods': ['Skull Left Drop', 'Skull Pinball']
|
||||
}
|
||||
|
||||
default_key_counts = {
|
||||
'Hyrule Castle': 1, 'Eastern Palace': 0, 'Desert Palace': 1,
|
||||
'Tower of Hera': 1, 'Agahnims Tower': 2, 'Palace of Darkness': 6,
|
||||
'Swamp Palace': 1, 'Skull Woods': 3, 'Thieves Town': 1,
|
||||
'Ice Palace': 2, 'Misery Mire': 3, 'Turtle Rock': 4, 'Ganons Tower': 4
|
||||
}
|
||||
|
||||
dungeon_keys = {
|
||||
'Hyrule Castle': 'Small Key (Escape)',
|
||||
'Eastern Palace': 'Small Key (Eastern Palace)',
|
||||
|
||||
@@ -14,7 +14,7 @@ In the vanilla, dungeonssimple, and dungeonsfull shuffles, the following ratios
|
||||
8 hints for valuable items.
|
||||
7 junk hints.
|
||||
|
||||
In the simple, restricted, and restricted legacy shuffles, these are the ratios:
|
||||
In the simple, restricted shuffles, these are the ratios:
|
||||
|
||||
2 hints for inconvenient entrances.
|
||||
1 hint for an inconvenient dungeon entrance.
|
||||
|
||||
@@ -50,18 +50,18 @@ def link_entrances(world, player):
|
||||
|
||||
# inverted entrance mods
|
||||
for owid in swapped_connections.keys():
|
||||
if invFlag != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if invFlag != (owid in world.owswaps[player][0] and world.owMixed[player]):
|
||||
for (entrancename, exitname) in swapped_connections[owid]:
|
||||
try:
|
||||
connect_two_way(world, entrancename, exitname, player)
|
||||
except RuntimeError:
|
||||
connect_entrance(world, entrancename, exitname, player)
|
||||
|
||||
if invFlag != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \
|
||||
invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if invFlag != (0x03 in world.owswaps[player][0] and world.owMixed[player]) and \
|
||||
invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
|
||||
connect_entrance(world, 'Death Mountain Return Cave (West)', 'Dark Death Mountain Healer Fairy', player)
|
||||
elif invFlag != (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \
|
||||
invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
elif invFlag != (0x0a in world.owswaps[player][0] and world.owMixed[player]) and \
|
||||
invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player)
|
||||
|
||||
# dungeon entrance shuffle
|
||||
@@ -225,7 +225,7 @@ def link_entrances(world, player):
|
||||
random.shuffle(remaining_entrances)
|
||||
old_man_entrance = remaining_entrances.pop()
|
||||
|
||||
connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
|
||||
connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]) else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
|
||||
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
|
||||
if invFlag and old_man_exit == 'Spike Cave':
|
||||
bomb_shop_doors.remove('Spike Cave')
|
||||
@@ -374,61 +374,6 @@ def link_entrances(world, player):
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, doors, door_targets, player)
|
||||
elif not invFlag and world.shuffle[player] == 'restricted_legacy':
|
||||
simple_shuffle_dungeons(world, player)
|
||||
|
||||
lw_entrances = list(LW_Entrances)
|
||||
dw_entrances = list(DW_Entrances)
|
||||
dw_must_exits = list(DW_Entrances_Must_Exit)
|
||||
old_man_entrances = list(Old_Man_Entrances)
|
||||
caves = list(Cave_Exits)
|
||||
three_exit_caves = list(Cave_Three_Exits)
|
||||
single_doors = list(Single_Cave_Doors)
|
||||
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
|
||||
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
|
||||
door_targets = list(Single_Cave_Targets)
|
||||
|
||||
# only use two exit caves to do mandatory dw connections
|
||||
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
|
||||
# add three exit doors to pool for remainder
|
||||
caves.extend(three_exit_caves)
|
||||
|
||||
# place old man, has limited options
|
||||
# exit has to come from specific set of doors, the entrance is free to move about
|
||||
random.shuffle(old_man_entrances)
|
||||
old_man_exit = old_man_entrances.pop()
|
||||
lw_entrances.extend(old_man_entrances)
|
||||
random.shuffle(lw_entrances)
|
||||
old_man_entrance = lw_entrances.pop()
|
||||
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
|
||||
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
|
||||
|
||||
# place Old Man House in Light World
|
||||
connect_caves(world, lw_entrances, [], Old_Man_House, player)
|
||||
|
||||
# connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves
|
||||
connect_caves(world, lw_entrances, dw_entrances, caves, player)
|
||||
|
||||
# scramble holes
|
||||
scramble_holes(world, player)
|
||||
|
||||
# place blacksmith, has limited options
|
||||
random.shuffle(blacksmith_doors)
|
||||
blacksmith_hut = blacksmith_doors.pop()
|
||||
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
|
||||
bomb_shop_doors.extend(blacksmith_doors)
|
||||
|
||||
# place dam and pyramid fairy, have limited options
|
||||
random.shuffle(bomb_shop_doors)
|
||||
bomb_shop = bomb_shop_doors.pop()
|
||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||
single_doors.extend(bomb_shop_doors)
|
||||
|
||||
# tavern back door cannot be shuffled yet
|
||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, single_doors, door_targets, player)
|
||||
elif world.shuffle[player] == 'full':
|
||||
skull_woods_shuffle(world, player)
|
||||
|
||||
@@ -769,324 +714,6 @@ def link_entrances(world, player):
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, entrances, door_targets, player)
|
||||
elif not invFlag and world.shuffle[player] == 'full_legacy':
|
||||
skull_woods_shuffle(world, player)
|
||||
|
||||
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances)
|
||||
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
|
||||
dw_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
|
||||
lw_must_exits = list(LW_Dungeon_Entrances_Must_Exit)
|
||||
old_man_entrances = list(Old_Man_Entrances + ['Tower of Hera'])
|
||||
caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) # don't need to consider three exit caves, have one exit caves to avoid parity issues
|
||||
single_doors = list(Single_Cave_Doors)
|
||||
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
|
||||
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
|
||||
door_targets = list(Single_Cave_Targets)
|
||||
|
||||
if world.mode[player] == 'standard':
|
||||
# must connect front of hyrule castle to do escape
|
||||
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
||||
else:
|
||||
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
|
||||
lw_entrances.append('Hyrule Castle Entrance (South)')
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
|
||||
else:
|
||||
dw_entrances.append('Ganons Tower')
|
||||
caves.append('Ganons Tower Exit')
|
||||
|
||||
# we randomize which world requirements we fulfill first so we get better dungeon distribution
|
||||
if random.randint(0, 1) == 0:
|
||||
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
|
||||
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
|
||||
else:
|
||||
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
|
||||
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
|
||||
if world.mode[player] == 'standard':
|
||||
# rest of hyrule castle must be in light world
|
||||
connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player)
|
||||
|
||||
# place old man, has limited options
|
||||
# exit has to come from specific set of doors, the entrance is free to move about
|
||||
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
|
||||
random.shuffle(old_man_entrances)
|
||||
old_man_exit = old_man_entrances.pop()
|
||||
lw_entrances.remove(old_man_exit)
|
||||
|
||||
random.shuffle(lw_entrances)
|
||||
old_man_entrance = lw_entrances.pop()
|
||||
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
|
||||
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
|
||||
|
||||
# place Old Man House in Light World
|
||||
connect_caves(world, lw_entrances, [], list(Old_Man_House), player) #need this to avoid badness with multiple seeds
|
||||
|
||||
# now scramble the rest
|
||||
connect_caves(world, lw_entrances, dw_entrances, caves, player)
|
||||
|
||||
# scramble holes
|
||||
scramble_holes(world, player)
|
||||
|
||||
# place blacksmith, has limited options
|
||||
random.shuffle(blacksmith_doors)
|
||||
blacksmith_hut = blacksmith_doors.pop()
|
||||
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
|
||||
bomb_shop_doors.extend(blacksmith_doors)
|
||||
|
||||
# place bomb shop, has limited options
|
||||
random.shuffle(bomb_shop_doors)
|
||||
bomb_shop = bomb_shop_doors.pop()
|
||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||
single_doors.extend(bomb_shop_doors)
|
||||
|
||||
# tavern back door cannot be shuffled yet
|
||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, single_doors, door_targets, player)
|
||||
elif not invFlag and world.shuffle[player] == 'madness_legacy':
|
||||
# here lie dragons, connections are no longer two way
|
||||
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances)
|
||||
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
|
||||
dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
|
||||
|
||||
lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Cave'] + list(Old_Man_Entrances)
|
||||
dw_doors = list(DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)']
|
||||
|
||||
random.shuffle(lw_doors)
|
||||
random.shuffle(dw_doors)
|
||||
|
||||
dw_entrances_must_exits.append('Skull Woods Second Section Door (West)')
|
||||
dw_entrances.append('Skull Woods Second Section Door (East)')
|
||||
dw_entrances.append('Skull Woods First Section Door')
|
||||
|
||||
lw_entrances.extend(['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'])
|
||||
|
||||
lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit)
|
||||
|
||||
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
|
||||
|
||||
mandatory_light_world = ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)']
|
||||
mandatory_dark_world = []
|
||||
caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits)
|
||||
|
||||
# shuffle up holes
|
||||
|
||||
lw_hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave']
|
||||
dw_hole_entrances = ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
|
||||
|
||||
hole_targets = [('Kakariko Well Exit', 'Kakariko Well (top)'),
|
||||
('Bat Cave Exit', 'Bat Cave (right)'),
|
||||
('North Fairy Cave Exit', 'North Fairy Cave'),
|
||||
('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'),
|
||||
('Lumberjack Tree Exit', 'Lumberjack Tree (top)'),
|
||||
(('Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'), 'Skull Back Drop')]
|
||||
|
||||
if world.mode[player] == 'standard':
|
||||
# cannot move uncle cave
|
||||
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
|
||||
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
|
||||
connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
|
||||
else:
|
||||
lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop')
|
||||
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
|
||||
lw_doors.append('Hyrule Castle Secret Entrance Stairs')
|
||||
lw_entrances.append('Hyrule Castle Secret Entrance Stairs')
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
|
||||
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
dw_entrances.append('Ganons Tower')
|
||||
caves.append('Ganons Tower Exit')
|
||||
dw_hole_entrances.append('Pyramid Hole')
|
||||
hole_targets.append(('Pyramid Exit', 'Pyramid'))
|
||||
dw_entrances_must_exits.append('Pyramid Entrance')
|
||||
dw_doors.extend(['Ganons Tower', 'Pyramid Entrance'])
|
||||
|
||||
random.shuffle(lw_hole_entrances)
|
||||
random.shuffle(dw_hole_entrances)
|
||||
random.shuffle(hole_targets)
|
||||
|
||||
# decide if skull woods first section should be in light or dark world
|
||||
sw_light = random.randint(0, 1) == 0
|
||||
if sw_light:
|
||||
sw_hole_pool = lw_hole_entrances
|
||||
mandatory_light_world.append('Skull Woods First Section Exit')
|
||||
else:
|
||||
sw_hole_pool = dw_hole_entrances
|
||||
mandatory_dark_world.append('Skull Woods First Section Exit')
|
||||
for target in ['Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle']:
|
||||
connect_entrance(world, sw_hole_pool.pop(), target, player)
|
||||
|
||||
# sanctuary has to be in light world
|
||||
connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop', player)
|
||||
mandatory_light_world.append('Sanctuary Exit')
|
||||
|
||||
# fill up remaining holes
|
||||
for hole in dw_hole_entrances:
|
||||
exits, target = hole_targets.pop()
|
||||
mandatory_dark_world.append(exits)
|
||||
connect_entrance(world, hole, target, player)
|
||||
|
||||
for hole in lw_hole_entrances:
|
||||
exits, target = hole_targets.pop()
|
||||
mandatory_light_world.append(exits)
|
||||
connect_entrance(world, hole, target, player)
|
||||
|
||||
# hyrule castle handling
|
||||
if world.mode[player] == 'standard':
|
||||
# must connect front of hyrule castle to do escape
|
||||
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
||||
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
|
||||
mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
else:
|
||||
lw_doors.append('Hyrule Castle Entrance (South)')
|
||||
lw_entrances.append('Hyrule Castle Entrance (South)')
|
||||
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
|
||||
# now let's deal with mandatory reachable stuff
|
||||
def extract_reachable_exit(cavelist):
|
||||
random.shuffle(cavelist)
|
||||
candidate = None
|
||||
for cave in cavelist:
|
||||
if isinstance(cave, tuple) and len(cave) > 1:
|
||||
# special handling: TRock and Spectracle Rock cave have two entries that we should consider entrance only
|
||||
# ToDo this should be handled in a more sensible manner
|
||||
if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2:
|
||||
continue
|
||||
candidate = cave
|
||||
break
|
||||
if candidate is None:
|
||||
raise RuntimeError('No suitable cave.')
|
||||
cavelist.remove(candidate)
|
||||
return candidate
|
||||
|
||||
def connect_reachable_exit(entrance, general, worldspecific, worldoors):
|
||||
# select which one is the primary option
|
||||
if random.randint(0, 1) == 0:
|
||||
primary = general
|
||||
secondary = worldspecific
|
||||
else:
|
||||
primary = worldspecific
|
||||
secondary = general
|
||||
|
||||
try:
|
||||
cave = extract_reachable_exit(primary)
|
||||
except RuntimeError:
|
||||
cave = extract_reachable_exit(secondary)
|
||||
|
||||
exit = cave[-1]
|
||||
cave = cave[:-1]
|
||||
connect_exit(world, exit, entrance, player)
|
||||
connect_entrance(world, worldoors.pop(), exit, player)
|
||||
# rest of cave now is forced to be in this world
|
||||
worldspecific.append(cave)
|
||||
|
||||
# we randomize which world requirements we fulfill first so we get better dungeon distribution
|
||||
if random.randint(0, 1) == 0:
|
||||
for entrance in lw_entrances_must_exits:
|
||||
connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors)
|
||||
for entrance in dw_entrances_must_exits:
|
||||
connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors)
|
||||
else:
|
||||
for entrance in dw_entrances_must_exits:
|
||||
connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors)
|
||||
for entrance in lw_entrances_must_exits:
|
||||
connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors)
|
||||
|
||||
# place old man, has limited options
|
||||
# exit has to come from specific set of doors, the entrance is free to move about
|
||||
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in lw_entrances]
|
||||
random.shuffle(old_man_entrances)
|
||||
old_man_exit = old_man_entrances.pop()
|
||||
lw_entrances.remove(old_man_exit)
|
||||
|
||||
connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player)
|
||||
connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)', player)
|
||||
mandatory_light_world.append('Old Man Cave Exit (West)')
|
||||
|
||||
# we connect up the mandatory associations we have found
|
||||
for mandatory in mandatory_light_world:
|
||||
if not isinstance(mandatory, tuple):
|
||||
mandatory = (mandatory,)
|
||||
for exit in mandatory:
|
||||
# point out somewhere
|
||||
connect_exit(world, exit, lw_entrances.pop(), player)
|
||||
# point in from somewhere
|
||||
connect_entrance(world, lw_doors.pop(), exit, player)
|
||||
|
||||
for mandatory in mandatory_dark_world:
|
||||
if not isinstance(mandatory, tuple):
|
||||
mandatory = (mandatory,)
|
||||
for exit in mandatory:
|
||||
# point out somewhere
|
||||
connect_exit(world, exit, dw_entrances.pop(), player)
|
||||
# point in from somewhere
|
||||
connect_entrance(world, dw_doors.pop(), exit, player)
|
||||
|
||||
# handle remaining caves
|
||||
while caves:
|
||||
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
|
||||
cave_candidate = (None, 0)
|
||||
for i, cave in enumerate(caves):
|
||||
if isinstance(cave, str):
|
||||
cave = (cave,)
|
||||
if len(cave) > cave_candidate[1]:
|
||||
cave_candidate = (i, len(cave))
|
||||
cave = caves.pop(cave_candidate[0])
|
||||
|
||||
place_lightworld = random.randint(0, 1) == 0
|
||||
if place_lightworld:
|
||||
target_doors = lw_doors
|
||||
target_entrances = lw_entrances
|
||||
else:
|
||||
target_doors = dw_doors
|
||||
target_entrances = dw_entrances
|
||||
|
||||
if isinstance(cave, str):
|
||||
cave = (cave,)
|
||||
|
||||
# check if we can still fit the cave into our target group
|
||||
if len(target_doors) < len(cave):
|
||||
if not place_lightworld:
|
||||
target_doors = lw_doors
|
||||
target_entrances = lw_entrances
|
||||
else:
|
||||
target_doors = dw_doors
|
||||
target_entrances = dw_entrances
|
||||
|
||||
for exit in cave:
|
||||
connect_exit(world, exit, target_entrances.pop(), player)
|
||||
connect_entrance(world, target_doors.pop(), exit, player)
|
||||
|
||||
# handle simple doors
|
||||
|
||||
single_doors = list(Single_Cave_Doors)
|
||||
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
|
||||
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
|
||||
door_targets = list(Single_Cave_Targets)
|
||||
|
||||
# place blacksmith, has limited options
|
||||
random.shuffle(blacksmith_doors)
|
||||
blacksmith_hut = blacksmith_doors.pop()
|
||||
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
|
||||
bomb_shop_doors.extend(blacksmith_doors)
|
||||
|
||||
# place dam and pyramid fairy, have limited options
|
||||
random.shuffle(bomb_shop_doors)
|
||||
bomb_shop = bomb_shop_doors.pop()
|
||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||
single_doors.extend(bomb_shop_doors)
|
||||
|
||||
# tavern back door cannot be shuffled yet
|
||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, single_doors, door_targets, player)
|
||||
elif world.shuffle[player] == 'insanity':
|
||||
# beware ye who enter here
|
||||
|
||||
@@ -1164,8 +791,8 @@ def link_entrances(world, 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, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
connect_two_way(world, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
|
||||
hole_targets.append('Pyramid')
|
||||
@@ -1177,7 +804,7 @@ def link_entrances(world, player):
|
||||
exit_pool.extend(['Agahnims Tower'])
|
||||
doors.extend(['Agahnims Tower'])
|
||||
|
||||
if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
|
||||
exit_pool.extend(['Pyramid Entrance'])
|
||||
hole_entrances.append('Pyramid Hole')
|
||||
entrances_must_exits.append('Pyramid Entrance')
|
||||
@@ -1304,205 +931,6 @@ def link_entrances(world, player):
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, doors, door_targets, player)
|
||||
elif world.shuffle[player] == 'insanity_legacy':
|
||||
world.fix_fake_world[player] = False
|
||||
# beware ye who enter here
|
||||
|
||||
if not invFlag:
|
||||
entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)']
|
||||
|
||||
doors = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave']
|
||||
else:
|
||||
entrances_must_exits = Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit
|
||||
|
||||
doors = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + Inverted_Old_Man_Entrances +\
|
||||
Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] +\
|
||||
Inverted_LW_Single_Cave_Doors + Inverted_DW_Single_Cave_Doors + ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)']
|
||||
|
||||
exit_pool = list(doors)
|
||||
|
||||
# randomize which desert ledge door is a must-exit
|
||||
if random.randint(0, 1) == 0:
|
||||
entrances_must_exits.append('Desert Palace Entrance (North)')
|
||||
else:
|
||||
entrances_must_exits.append('Desert Palace Entrance (West)')
|
||||
|
||||
doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\
|
||||
DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)']
|
||||
|
||||
random.shuffle(doors)
|
||||
|
||||
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
|
||||
|
||||
caves = Cave_Exits + Dungeon_Exits + Cave_Three_Exits + ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)',
|
||||
'Kakariko Well Exit', 'Bat Cave Exit', 'North Fairy Cave Exit', 'Lost Woods Hideout Exit', 'Lumberjack Tree Exit', 'Sanctuary Exit']
|
||||
|
||||
# shuffle up holes
|
||||
|
||||
hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave',
|
||||
'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
|
||||
|
||||
hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Back Drop',
|
||||
'Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle']
|
||||
|
||||
# tavern back door cannot be shuffled yet
|
||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||
|
||||
if world.mode[player] == 'standard':
|
||||
# cannot move uncle cave
|
||||
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
|
||||
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
|
||||
connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
|
||||
else:
|
||||
hole_entrances.append('Hyrule Castle Secret Entrance Drop')
|
||||
hole_targets.append('Hyrule Castle Secret Entrance')
|
||||
doors.append('Hyrule Castle Secret Entrance Stairs')
|
||||
caves.append('Hyrule Castle Secret Entrance Exit')
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
|
||||
if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
|
||||
hole_targets.append('Pyramid')
|
||||
|
||||
if not invFlag:
|
||||
doors.extend(['Ganons Tower'])
|
||||
exit_pool.extend(['Ganons Tower'])
|
||||
else:
|
||||
doors.extend(['Agahnims Tower'])
|
||||
exit_pool.extend(['Agahnims Tower'])
|
||||
|
||||
if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
hole_entrances.append('Pyramid Hole')
|
||||
doors.extend(['Pyramid Entrance'])
|
||||
exit_pool.extend(['Pyramid Entrance'])
|
||||
else:
|
||||
hole_entrances.append('Inverted Pyramid Hole')
|
||||
doors.extend(['Inverted Pyramid Entrance'])
|
||||
exit_pool.extend(['Inverted Pyramid Entrance'])
|
||||
|
||||
random.shuffle(hole_entrances)
|
||||
random.shuffle(hole_targets)
|
||||
random.shuffle(exit_pool)
|
||||
|
||||
# fill up holes
|
||||
for hole in hole_entrances:
|
||||
connect_entrance(world, hole, hole_targets.pop(), player)
|
||||
|
||||
# hyrule castle handling
|
||||
if world.mode[player] == 'standard':
|
||||
# must connect front of hyrule castle to do escape
|
||||
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
||||
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
|
||||
caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
else:
|
||||
doors.append('Hyrule Castle Entrance (South)')
|
||||
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
|
||||
if not world.shufflelinks[player]:
|
||||
if not invFlag:
|
||||
links_house = 'Links House'
|
||||
else:
|
||||
links_house = 'Big Shop Shop'
|
||||
else:
|
||||
if not invFlag:
|
||||
links_house_doors = [i for i in doors if i not in Sanctuary_Doors + Isolated_LH_Doors]
|
||||
else:
|
||||
links_house_doors = [i for i in doors if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(links_house_doors)
|
||||
connect_two_way(world, links_house, 'Links House Exit', player)
|
||||
connect_exit(world, 'Chris Houlihan Room Exit', links_house, player) # should always match link's house, except for plandos
|
||||
doors.remove(links_house)
|
||||
exit_pool.remove(links_house)
|
||||
|
||||
if not invFlag:
|
||||
sanc_doors = [door for door in exit_pool] #[door for door in Sanctuary_Doors if door in exit_pool]
|
||||
else:
|
||||
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in exit_pool]
|
||||
sanc_door = random.choice(sanc_doors)
|
||||
exit_pool.remove(sanc_door)
|
||||
doors.remove(sanc_door)
|
||||
connect_entrance(world, sanc_door, 'Dark Sanctuary Hint', player)
|
||||
world.get_entrance('Dark Sanctuary Hint Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||
|
||||
# now let's deal with mandatory reachable stuff
|
||||
def extract_reachable_exit(cavelist):
|
||||
random.shuffle(cavelist)
|
||||
candidate = None
|
||||
for cave in cavelist:
|
||||
if isinstance(cave, tuple) and len(cave) > 1:
|
||||
# special handling: TRock has two entries that we should consider entrance only
|
||||
# ToDo this should be handled in a more sensible manner
|
||||
if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2:
|
||||
continue
|
||||
candidate = cave
|
||||
break
|
||||
if candidate is None:
|
||||
raise RuntimeError('No suitable cave.')
|
||||
cavelist.remove(candidate)
|
||||
return candidate
|
||||
|
||||
def connect_reachable_exit(entrance, caves, doors, exit_pool):
|
||||
cave = extract_reachable_exit(caves)
|
||||
|
||||
exit = cave[-1]
|
||||
cave = cave[:-1]
|
||||
connect_exit(world, exit, entrance, player)
|
||||
connect_entrance(world, doors.pop(), exit, player)
|
||||
# rest of cave now is forced to be in this world
|
||||
exit_pool.remove(entrance)
|
||||
caves.append(cave)
|
||||
|
||||
# connect mandatory exits
|
||||
for entrance in entrances_must_exits:
|
||||
connect_reachable_exit(entrance, caves, doors, exit_pool)
|
||||
|
||||
# place old man, has limited options
|
||||
# exit has to come from specific set of doors, the entrance is free to move about
|
||||
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in exit_pool]
|
||||
random.shuffle(old_man_entrances)
|
||||
old_man_exit = old_man_entrances.pop()
|
||||
exit_pool.remove(old_man_exit)
|
||||
|
||||
connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player)
|
||||
connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player)
|
||||
caves.append('Old Man Cave Exit (West)')
|
||||
|
||||
# handle simple doors
|
||||
single_doors = list(Single_Cave_Doors)
|
||||
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
|
||||
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
|
||||
door_targets = list(Single_Cave_Targets)
|
||||
|
||||
# place blacksmith, has limited options
|
||||
random.shuffle(blacksmith_doors)
|
||||
blacksmith_hut = blacksmith_doors.pop()
|
||||
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
|
||||
doors.remove(blacksmith_hut)
|
||||
|
||||
# place dam and pyramid fairy, have limited options
|
||||
random.shuffle(bomb_shop_doors)
|
||||
bomb_shop = bomb_shop_doors.pop()
|
||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||
doors.remove(bomb_shop)
|
||||
|
||||
# handle remaining caves
|
||||
for cave in caves:
|
||||
if isinstance(cave, str):
|
||||
cave = (cave,)
|
||||
|
||||
for exit in cave:
|
||||
connect_exit(world, exit, exit_pool.pop(), player)
|
||||
connect_entrance(world, doors.pop(), exit, player)
|
||||
|
||||
# place remaining doors
|
||||
connect_doors(world, single_doors, door_targets, player)
|
||||
else:
|
||||
raise NotImplementedError('Shuffling not supported yet')
|
||||
|
||||
@@ -1515,7 +943,7 @@ def link_entrances(world, player):
|
||||
world.powder_patch_required[player] = True
|
||||
|
||||
# check for ganon location
|
||||
if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
|
||||
if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
|
||||
world.ganon_at_pyramid[player] = False
|
||||
|
||||
# check for Ganon's Tower location
|
||||
@@ -1601,16 +1029,6 @@ def scramble_holes(world, player):
|
||||
('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'),
|
||||
('Lumberjack Tree Exit', 'Lumberjack Tree (top)')]
|
||||
|
||||
if not world.shuffle_ganon:
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
hole_targets.append(('Pyramid Exit', 'Pyramid'))
|
||||
|
||||
if world.mode[player] == 'standard':
|
||||
# cannot move uncle cave
|
||||
connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
|
||||
@@ -1622,18 +1040,31 @@ def scramble_holes(world, player):
|
||||
# do not shuffle sanctuary into pyramid hole unless shuffle is crossed
|
||||
if world.shuffle[player] == 'crossed':
|
||||
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
|
||||
if world.shuffle_ganon:
|
||||
random.shuffle(hole_targets)
|
||||
exit, target = hole_targets.pop()
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
|
||||
# determine pyramid hole
|
||||
if not world.shuffle_ganon:
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
|
||||
connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player)
|
||||
else:
|
||||
hole_targets.append(('Pyramid Exit', 'Pyramid'))
|
||||
|
||||
random.shuffle(hole_targets)
|
||||
exit, target = hole_targets.pop()
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
connect_two_way(world, 'Pyramid Entrance', exit, player)
|
||||
connect_entrance(world, 'Pyramid Hole', target, player)
|
||||
else:
|
||||
connect_two_way(world, 'Inverted Pyramid Entrance', exit, player)
|
||||
connect_entrance(world, 'Inverted Pyramid Hole', target, player)
|
||||
|
||||
if world.shuffle[player] != 'crossed':
|
||||
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
|
||||
|
||||
# shuffle the rest
|
||||
random.shuffle(hole_targets)
|
||||
for entrance, drop in hole_entrances:
|
||||
exit, target = hole_targets.pop()
|
||||
|
||||
33
ItemList.py
33
ItemList.py
@@ -258,7 +258,7 @@ def generate_itempool(world, player):
|
||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray)
|
||||
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
|
||||
else:
|
||||
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.owShuffle[player], world.owSwap[player], world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player])
|
||||
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player])
|
||||
|
||||
if player in world.pool_adjustment.keys():
|
||||
amt = world.pool_adjustment[player]
|
||||
@@ -727,7 +727,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)'
|
||||
'Rupees (100)': 100, 'Rupees (300)': 300}
|
||||
|
||||
|
||||
def get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic):
|
||||
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic):
|
||||
pool = []
|
||||
placed_items = {}
|
||||
precollected_items = []
|
||||
@@ -986,25 +986,24 @@ def test():
|
||||
for mode in ['open', 'standard', 'inverted', 'retro']:
|
||||
for swords in ['random', 'assured', 'swordless', 'vanilla']:
|
||||
for progressive in ['on', 'off']:
|
||||
for shuffle in ['full', 'insanity_legacy']:
|
||||
for shuffle in ['vanilla', 'full', 'crossed', 'insanity']:
|
||||
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
|
||||
for retro in [True, False]:
|
||||
for door_shuffle in ['basic', 'crossed', 'vanilla']:
|
||||
for owShuffle in ['full', 'vanilla']:
|
||||
for owSwap in ['mixed', 'vanilla']:
|
||||
out = get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
|
||||
count = len(out[0]) + len(out[1])
|
||||
for bombbag in [True, False]:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
|
||||
count = len(out[0]) + len(out[1])
|
||||
|
||||
correct_count = total_items_to_place
|
||||
if goal == 'pedestal' and swords != 'vanilla':
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
try:
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag))
|
||||
except AssertionError as e:
|
||||
print(e)
|
||||
correct_count = total_items_to_place
|
||||
if goal == 'pedestal' and swords != 'vanilla':
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
try:
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag))
|
||||
except AssertionError as e:
|
||||
print(e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
||||
@@ -4,7 +4,7 @@ from collections import defaultdict, deque
|
||||
|
||||
from BaseClasses import DoorType
|
||||
from Regions import dungeon_events
|
||||
from Dungeons import dungeon_keys, dungeon_bigs
|
||||
from Dungeons import dungeon_keys, dungeon_bigs, default_key_counts
|
||||
from DungeonGenerator import ExplorationState, special_big_key_doors
|
||||
|
||||
|
||||
@@ -1384,9 +1384,10 @@ def create_key_counters(key_layout, world, player):
|
||||
flat_proposal = key_layout.flat_prop
|
||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||
if world.doorShuffle[player] == 'vanilla':
|
||||
state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||
state.key_locations = default_key_counts[key_layout.sector.name]
|
||||
else:
|
||||
state.key_locations = world.dungeon_layouts[player][key_layout.sector.name].key_doors_num
|
||||
builder = world.dungeon_layouts[player][key_layout.sector.name]
|
||||
state.key_locations = builder.total_keys - builder.key_drop_cnt
|
||||
state.big_key_special, special_region = False, None
|
||||
for region in key_layout.sector.regions:
|
||||
for location in region.locations:
|
||||
|
||||
8
Main.py
8
Main.py
@@ -29,7 +29,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
||||
from Utils import output_path, parse_player_names
|
||||
|
||||
__version__ = '0.5.0.2-u'
|
||||
__version__ = '0.5.0.3-u'
|
||||
|
||||
from source.classes.BabelFish import BabelFish
|
||||
|
||||
@@ -61,7 +61,7 @@ def main(args, seed=None, fish=None):
|
||||
for player, code in args.code.items():
|
||||
if code:
|
||||
Settings.adjust_args_from_code(code, player, args)
|
||||
world = World(args.multi, args.ow_shuffle, args.ow_swap, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
|
||||
world = World(args.multi, args.ow_shuffle, args.ow_crossed, args.ow_mixed, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
|
||||
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
|
||||
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
|
||||
logger = logging.getLogger('')
|
||||
@@ -263,7 +263,7 @@ def main(args, seed=None, fish=None):
|
||||
customize_shops(world, player)
|
||||
balance_money_progression(world)
|
||||
|
||||
if world.owShuffle[1] != 'vanilla' or world.owSwap[1] != 'vanilla' or str(world.seed).startswith('M'):
|
||||
if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] or world.owMixed[1] or str(world.seed).startswith('M'):
|
||||
outfilebase = f'OR_{args.outputname if args.outputname else world.seed}'
|
||||
else:
|
||||
outfilebase = f'DR_{args.outputname if args.outputname else world.seed}'
|
||||
@@ -369,7 +369,7 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
def copy_world(world):
|
||||
# ToDo: Not good yet
|
||||
ret = World(world.players, world.owShuffle, world.owSwap, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
|
||||
ret = World(world.players, world.owShuffle, world.owCrossed, world.owMixed, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
|
||||
world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
|
||||
world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
||||
ret.teams = world.teams
|
||||
|
||||
@@ -846,7 +846,7 @@ async def track_locations(ctx : Context, roomid, roomdata):
|
||||
new_locations.append(Regions.lookup_name_to_id[location])
|
||||
|
||||
try:
|
||||
if roomid in location_shop_ids:
|
||||
if ctx.shop_mode or ctx.retro_mode:
|
||||
misc_data = await snes_read(ctx, SHOP_ADDR, SHOP_SRAM_LEN)
|
||||
for cnt, b in enumerate(misc_data):
|
||||
my_check = Regions.shop_table_by_location_id[0x400000 + cnt]
|
||||
|
||||
@@ -133,9 +133,9 @@ def roll_settings(weights):
|
||||
|
||||
overworld_shuffle = get_choice('overworld_shuffle')
|
||||
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
|
||||
overworld_swap = get_choice('overworld_swap')
|
||||
ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla'
|
||||
ret.ow_crossed = get_choice('overworld_crossed')
|
||||
ret.ow_keepsimilar = get_choice('overworld_keepsimilar')
|
||||
ret.ow_mixed = get_choice('overworld_mixed')
|
||||
overworld_flute = get_choice('flute_shuffle')
|
||||
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
|
||||
entrance_shuffle = get_choice('entrance_shuffle')
|
||||
|
||||
@@ -697,7 +697,6 @@ OWTileRegions = bidict({
|
||||
'Spectacle Rock Ledge': 0x03,
|
||||
'West Death Mountain (Bottom)': 0x03,
|
||||
|
||||
'Death Mountain Floating Island': 0x05,
|
||||
'East Death Mountain (Top West)': 0x05,
|
||||
'East Death Mountain (Top East)': 0x05,
|
||||
'Spiral Cave Ledge': 0x05,
|
||||
@@ -706,6 +705,7 @@ OWTileRegions = bidict({
|
||||
'Fairy Ascension Plateau': 0x05,
|
||||
'East Death Mountain (Bottom Left)': 0x05,
|
||||
'East Death Mountain (Bottom)': 0x05,
|
||||
'Death Mountain Floating Island': 0x05,
|
||||
|
||||
'Death Mountain TR Pegs': 0x07,
|
||||
'Death Mountain TR Pegs Ledge': 0x07,
|
||||
@@ -846,6 +846,9 @@ OWTileRegions = bidict({
|
||||
'East Dark Death Mountain (Top)': 0x45,
|
||||
'East Dark Death Mountain (Bottom Left)': 0x45,
|
||||
'East Dark Death Mountain (Bottom)': 0x45,
|
||||
'Dark Death Mountain Ledge': 0x45,
|
||||
'Dark Death Mountain Isolated Ledge': 0x45,
|
||||
'Dark Death Mountain Floating Island': 0x45,
|
||||
|
||||
'Turtle Rock Area': 0x47,
|
||||
'Turtle Rock Ledge': 0x47,
|
||||
|
||||
@@ -2,7 +2,7 @@ import RaceRandom as random, logging, copy
|
||||
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
|
||||
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
|
||||
|
||||
__version__ = '0.1.7.4-u'
|
||||
__version__ = '0.1.8.0-u'
|
||||
|
||||
def link_overworld(world, player):
|
||||
# setup mandatory connections
|
||||
@@ -11,9 +11,25 @@ def link_overworld(world, player):
|
||||
for exitname, destname in temporary_mandatory_connections:
|
||||
connect_two_way(world, exitname, destname, player)
|
||||
|
||||
# tile shuffle
|
||||
trimmed_groups = copy.deepcopy(OWEdgeGroups)
|
||||
if world.owSwap[player] != 'vanilla':
|
||||
|
||||
# adjust Frog/Dig Game swap manually due to NP/P relationship with LW
|
||||
if world.owShuffle[player] == 'parallel' and not world.owKeepSimilar[player]:
|
||||
for group in trimmed_groups.keys():
|
||||
(std, region, axis, terrain, _, _) = group
|
||||
(forward_edges, back_edges) = trimmed_groups[group]
|
||||
if ['Dig Game EC', 'Dig Game ES'] in forward_edges:
|
||||
forward_edges = list(filter((['Dig Game EC', 'Dig Game ES']).__ne__, forward_edges))
|
||||
trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][0].append(['Dig Game ES'])
|
||||
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][0].append(['Dig Game EC'])
|
||||
if ['Frog WC', 'Frog WS'] in back_edges:
|
||||
back_edges = list(filter((['Frog WC', 'Frog WS']).__ne__, back_edges))
|
||||
trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][1].append(['Frog WS'])
|
||||
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][1].append(['Frog WC'])
|
||||
trimmed_groups[group] = (forward_edges, back_edges)
|
||||
|
||||
# tile shuffle
|
||||
if world.owMixed[player]:
|
||||
tile_groups = {}
|
||||
for (name, groupType) in OWTileGroups.keys():
|
||||
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']:
|
||||
@@ -44,7 +60,7 @@ def link_overworld(world, player):
|
||||
exist_dw_regions.extend(OWTileRegions.inverse[owid])
|
||||
tile_groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions)
|
||||
|
||||
#tile shuffle happens here, the groups that remain in the list are the tiles that get swapped
|
||||
# tile shuffle happens here, the groups that remain in the list are the tiles that get swapped
|
||||
removed = list()
|
||||
for group in tile_groups.keys():
|
||||
if random.randint(0, 1):
|
||||
@@ -52,7 +68,7 @@ def link_overworld(world, player):
|
||||
for group in removed:
|
||||
tile_groups.pop(group, None)
|
||||
|
||||
#save shuffled tiles to world object
|
||||
# save shuffled tiles to world object
|
||||
for group in tile_groups.keys():
|
||||
(owids, lw_regions, dw_regions) = tile_groups[group]
|
||||
(exist_owids, exist_lw_regions, exist_dw_regions) = world.owswaps[player]
|
||||
@@ -61,7 +77,7 @@ def link_overworld(world, player):
|
||||
exist_dw_regions.extend(dw_regions)
|
||||
world.owswaps[player] = [exist_owids, exist_lw_regions, exist_dw_regions]
|
||||
|
||||
#replace LW edges with DW
|
||||
# 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:
|
||||
@@ -108,7 +124,7 @@ def link_overworld(world, player):
|
||||
#TODO: Figure out a way to handle index changes on the fly when removing items
|
||||
logging.getLogger('').warning('OW Tile Swap encountered minor IndexError... retrying')
|
||||
|
||||
if 0x28 in world.owswaps[player][0]: #handle Frog/Dig Game swap manually due to NP/P relationship with LW
|
||||
if world.owShuffle[player] != 'parallel' and 0x28 in world.owswaps[player][0]: # handle Frog/Dig Game swap manually due to NP/P relationship with LW
|
||||
trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].append(['Maze Race ES'])
|
||||
trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][1].append(['Kakariko Suburb WS'])
|
||||
trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].remove(['Maze Race ES'])
|
||||
@@ -139,7 +155,7 @@ def link_overworld(world, player):
|
||||
|
||||
# make new connections
|
||||
for owid in ow_connections.keys():
|
||||
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owMixed[player]):
|
||||
for (exitname, regionname) in ow_connections[owid][0]:
|
||||
connect_simple(world, exitname, regionname, player)
|
||||
else:
|
||||
@@ -148,70 +164,102 @@ def link_overworld(world, player):
|
||||
|
||||
connected_edges = []
|
||||
|
||||
connect_custom(world, connected_edges, player)
|
||||
|
||||
# layout shuffle
|
||||
if world.owShuffle[player] == 'vanilla':
|
||||
for grouping in (trimmed_groups,):
|
||||
groups = list(trimmed_groups.values())
|
||||
if world.owShuffle[player] == 'vanilla' and not world.owCrossed[player]:
|
||||
# vanilla transitions
|
||||
groups = list(trimmed_groups.values())
|
||||
for (forward_edge_sets, back_edge_sets) in groups:
|
||||
assert len(forward_edge_sets) == len(back_edge_sets)
|
||||
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
|
||||
assert len(forward_set) == len(back_set)
|
||||
for (forward_edge, back_edge) in zip(forward_set, back_set):
|
||||
connect_two_way(world, forward_edge, back_edge, player)
|
||||
connected_edges.append(forward_edge)
|
||||
connected_edges.append(back_edge)
|
||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||
|
||||
assert len(connected_edges) == len(default_connections) * 2, connected_edges
|
||||
else:
|
||||
if world.owKeepSimilar[player] and world.owShuffle[player] == 'parallel':
|
||||
for exitname, destname in parallelsimilar_connections:
|
||||
connect_two_way(world, exitname, destname, player)
|
||||
connected_edges.append(exitname)
|
||||
connected_edges.append(destname)
|
||||
connect_two_way(world, exitname, destname, player, connected_edges)
|
||||
|
||||
if world.owShuffle[player] == 'vanilla' and world.owCrossed[player]:
|
||||
if world.mode[player] == 'standard':
|
||||
# connect vanilla std
|
||||
for group in trimmed_groups.keys():
|
||||
(std, _, _, _, _, _) = group
|
||||
if std == OpenStd.Standard:
|
||||
(forward_set, back_set) = trimmed_groups[group]
|
||||
for (forward_edges, back_edges) in zip(forward_set, back_set):
|
||||
for (forward_edge, back_edge) in zip(forward_edges, back_edges):
|
||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||
|
||||
# connect non-parallel edges
|
||||
for group in trimmed_groups.keys():
|
||||
(_, _, _, _, parallel, _) = group
|
||||
if parallel == IsParallel.No:
|
||||
(forward_set, back_set) = trimmed_groups[group]
|
||||
for (forward_edges, back_edges) in zip(forward_set, back_set):
|
||||
for (forward_edge, back_edge) in zip(forward_edges, back_edges):
|
||||
if forward_edge not in connected_edges and back_edge not in connected_edges:
|
||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||
|
||||
#TODO: Remove, just for testing
|
||||
for exitname, destname in test_connections:
|
||||
connect_two_way(world, exitname, destname, player)
|
||||
connected_edges.append(exitname)
|
||||
connected_edges.append(destname)
|
||||
connect_two_way(world, exitname, destname, player, connected_edges)
|
||||
|
||||
connect_custom(world, connected_edges, player)
|
||||
|
||||
trimmed_groups = remove_reserved(world, trimmed_groups, connected_edges, player)
|
||||
|
||||
groups = reorganize_groups(world, trimmed_groups, player)
|
||||
|
||||
#all shuffling occurs here
|
||||
random.shuffle(groups)
|
||||
for (forward_edge_sets, back_edge_sets) in groups:
|
||||
assert len(forward_edge_sets) == len(back_edge_sets)
|
||||
random.shuffle(back_edge_sets)
|
||||
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
|
||||
assert len(forward_set) == len(back_set)
|
||||
for (forward_edge, back_edge) in zip(forward_set, back_set):
|
||||
connect_two_way(world, forward_edge, back_edge, player)
|
||||
connected_edges.append(forward_edge)
|
||||
connected_edges.append(back_edge)
|
||||
if world.owShuffle[player] == 'parallel':
|
||||
if forward_edge in parallel_links.keys() or forward_edge in parallel_links.inverse.keys():
|
||||
try:
|
||||
parallel_forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links.keys() else parallel_links.inverse[forward_edge][0]
|
||||
parallel_back_edge = parallel_links[back_edge] if back_edge in parallel_links.keys() else parallel_links.inverse[back_edge][0]
|
||||
connect_two_way(world, parallel_forward_edge, parallel_back_edge, player)
|
||||
connected_edges.append(parallel_forward_edge)
|
||||
connected_edges.append(parallel_back_edge)
|
||||
except KeyError:
|
||||
# TODO: Figure out why non-parallel edges are getting into parallel groups
|
||||
raise KeyError('No parallel edge for edge %d' % back_edge)
|
||||
# all layout shuffling occurs here
|
||||
if world.owShuffle[player] != 'vanilla':
|
||||
# layout shuffle
|
||||
random.shuffle(groups)
|
||||
for (forward_edge_sets, back_edge_sets) in groups:
|
||||
assert len(forward_edge_sets) == len(back_edge_sets)
|
||||
random.shuffle(forward_edge_sets)
|
||||
random.shuffle(back_edge_sets)
|
||||
if len(forward_edge_sets) > 0:
|
||||
f = 0
|
||||
b = 0
|
||||
while f < len(forward_edge_sets) and b < len(back_edge_sets):
|
||||
forward_set = forward_edge_sets[f]
|
||||
back_set = back_edge_sets[b]
|
||||
while forward_set[0] in connected_edges:
|
||||
f += 1
|
||||
if f < len(forward_edge_sets):
|
||||
forward_set = forward_edge_sets[f]
|
||||
f += 1
|
||||
while back_set[0] in connected_edges:
|
||||
b += 1
|
||||
if b < len(back_edge_sets):
|
||||
back_set = back_edge_sets[b]
|
||||
b += 1
|
||||
assert len(forward_set) == len(back_set)
|
||||
for (forward_edge, back_edge) in zip(forward_set, back_set):
|
||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||
else:
|
||||
# vanilla/crossed shuffle
|
||||
for (forward_edge_sets, back_edge_sets) in groups:
|
||||
assert len(forward_edge_sets) == len(back_edge_sets)
|
||||
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
|
||||
assert len(forward_set) == len(back_set)
|
||||
swapped = random.randint(0, 1)
|
||||
for (forward_edge, back_edge) in zip(forward_set, back_set):
|
||||
if forward_edge not in connected_edges and back_edge not in connected_edges:
|
||||
if swapped:
|
||||
forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links else parallel_links.inverse[forward_edge][0]
|
||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||
|
||||
assert len(connected_edges) == len(default_connections) * 2, connected_edges
|
||||
|
||||
|
||||
# flute shuffle
|
||||
def connect_flutes(flute_destinations):
|
||||
for o in range(0, len(flute_destinations)):
|
||||
owslot = flute_destinations[o]
|
||||
regions = flute_data[owslot][0]
|
||||
if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owMixed[player]):
|
||||
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[0], player)
|
||||
else:
|
||||
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[1], player)
|
||||
@@ -233,7 +281,7 @@ def link_overworld(world, player):
|
||||
new_ignored.add(exit.connected_region.name)
|
||||
getIgnored(exit.connected_region.name, base_owid, OWTileRegions[exit.connected_region.name])
|
||||
|
||||
if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owMixed[player]):
|
||||
new_region = flute_data[owid][0][0]
|
||||
else:
|
||||
new_region = flute_data[owid][0][1]
|
||||
@@ -270,18 +318,23 @@ def link_overworld(world, player):
|
||||
world.owflutespots[player] = new_spots
|
||||
connect_flutes(new_spots)
|
||||
|
||||
|
||||
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]:
|
||||
connect_two_way(world, edgename1, edgename2, player)
|
||||
connected_edges.append(edgename1)
|
||||
connected_edges.append(edgename2)
|
||||
if edgename1 in connected_edges or edgename2 in connected_edges:
|
||||
owedge1 = world.check_for_owedge(edgename1, player)
|
||||
owedge2 = world.check_for_owedge(edgename2, player)
|
||||
if owedge1.dest is not None and owedge1.dest.name == owedge2.name:
|
||||
continue # if attempting to connect a pair that was already connected earlier, allow it to continue
|
||||
raise RuntimeError('Invalid plando connection: rule violation based on current settings')
|
||||
connect_two_way(world, edgename1, edgename2, player, connected_edges)
|
||||
if world.owKeepSimilar[player]: #TODO: If connecting an edge that belongs to a similar pair, the remaining edges need to get connected automatically
|
||||
continue
|
||||
|
||||
def connect_simple(world, exitname, regionname, player):
|
||||
world.get_entrance(exitname, player).connect(world.get_region(regionname, player))
|
||||
|
||||
def connect_two_way(world, edgename1, edgename2, player):
|
||||
def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
|
||||
edge1 = world.get_entrance(edgename1, player)
|
||||
edge2 = world.get_entrance(edgename2, player)
|
||||
|
||||
@@ -303,23 +356,41 @@ def connect_two_way(world, edgename1, edgename2, player):
|
||||
x.dest = y
|
||||
y.dest = x
|
||||
|
||||
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
|
||||
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]:
|
||||
world.spoiler.set_overworld(edgename2, edgename1, 'both', player)
|
||||
|
||||
if connected_edges is not None:
|
||||
connected_edges.append(edgename1)
|
||||
connected_edges.append(edgename2)
|
||||
|
||||
# connecting parallel connections
|
||||
if world.owShuffle[player] == 'parallel' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player]):
|
||||
if (edgename1 in parallel_links.keys() or edgename1 in parallel_links.inverse.keys()):
|
||||
try:
|
||||
parallel_forward_edge = parallel_links[edgename1] if edgename1 in parallel_links.keys() else parallel_links.inverse[edgename1][0]
|
||||
parallel_back_edge = parallel_links[edgename2] if edgename2 in parallel_links.keys() else parallel_links.inverse[edgename2][0]
|
||||
if not (parallel_forward_edge in connected_edges) and not (parallel_back_edge in connected_edges):
|
||||
connect_two_way(world, parallel_forward_edge, parallel_back_edge, player, connected_edges)
|
||||
except KeyError:
|
||||
# TODO: Figure out why non-parallel edges are getting into parallel groups
|
||||
raise KeyError('No parallel edge for edge %s' % edgename2)
|
||||
|
||||
def remove_reserved(world, groupedlist, connected_edges, player):
|
||||
new_grouping = {}
|
||||
for group in groupedlist.keys():
|
||||
new_grouping[group] = ([], [])
|
||||
|
||||
for group in groupedlist.keys():
|
||||
(std, region, axis, terrain, parallel, count) = group
|
||||
(_, region, _, _, _, _) = group
|
||||
(forward_edges, back_edges) = groupedlist[group]
|
||||
|
||||
# remove edges already connected (thru plando and other forced connections)
|
||||
for edge in connected_edges:
|
||||
forward_edges = list(list(filter((edge).__ne__, i)) for i in forward_edges)
|
||||
back_edges = list(list(filter((edge).__ne__, i)) for i in back_edges)
|
||||
|
||||
if world.owShuffle[player] == 'parallel' and region == WorldType.Dark:
|
||||
|
||||
# remove parallel edges from pool, since they get added during shuffle
|
||||
if (not world.owCrossed[player] and world.owShuffle[player] == 'parallel') and region == WorldType.Dark:
|
||||
for edge in parallel_links:
|
||||
forward_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in forward_edges)
|
||||
back_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in back_edges)
|
||||
@@ -330,8 +401,6 @@ def remove_reserved(world, groupedlist, connected_edges, player):
|
||||
forward_edges = list(filter(([]).__ne__, forward_edges))
|
||||
back_edges = list(filter(([]).__ne__, back_edges))
|
||||
|
||||
#TODO: Remove edges set in connect_custom. The lists above can be left with invalid counts if connect_custom removes entries, they need to get put into their appropriate group
|
||||
|
||||
(exist_forward_edges, exist_back_edges) = new_grouping[group]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
@@ -341,11 +410,324 @@ def remove_reserved(world, groupedlist, connected_edges, player):
|
||||
return new_grouping
|
||||
|
||||
def reorganize_groups(world, groups, player):
|
||||
# predefined shuffle groups get reorganized here
|
||||
# this restructures the candidate pool based on the chosen settings
|
||||
if world.owShuffle[player] == 'full':
|
||||
#predefined shuffle groups get reorganized here
|
||||
if world.owCrossed[player]:
|
||||
if world.owKeepSimilar[player]:
|
||||
if world.mode[player] == 'standard':
|
||||
# tuple goes to (A,_,C,D,_,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, _, count) = group
|
||||
new_grouping[(std, axis, terrain, count)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, _, count) = group
|
||||
(forward_edges, back_edges) = grouping[group]
|
||||
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, count)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(std, axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
# tuple goes to (_,_,C,D,_,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, _, count) = group
|
||||
new_grouping[(axis, terrain, count)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, _, count) = group
|
||||
(forward_edges, back_edges) = grouping[group]
|
||||
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, count)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
if world.mode[player] == 'standard':
|
||||
# tuple goes to (A,_,C,D,_,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, _, _) = group
|
||||
new_grouping[(std, axis, terrain)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, 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, axis, terrain)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(std, axis, terrain)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
# tuple goes to (_,_,C,D,_,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, _, _) = group
|
||||
new_grouping[(axis, terrain)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, 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[(axis, terrain)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(axis, terrain)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
if world.owKeepSimilar[player]:
|
||||
if world.mode[player] == 'standard':
|
||||
# tuple goes to (A,B,C,D,_,F)
|
||||
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)
|
||||
|
||||
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.owCrossed[player]:
|
||||
if world.owKeepSimilar[player]:
|
||||
if world.mode[player] == 'standard':
|
||||
# tuple goes to (A,_,C,D,E,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, parallel, count) = group
|
||||
new_grouping[(std, axis, terrain, parallel, count)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, parallel, count) = group
|
||||
(forward_edges, back_edges) = grouping[group]
|
||||
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel, count)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(std, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
# tuple goes to (_,_,C,D,E,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, parallel, count) = group
|
||||
new_grouping[(axis, terrain, parallel, count)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, parallel, count) = group
|
||||
(forward_edges, back_edges) = grouping[group]
|
||||
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel, count)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(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,_,C,D,E,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, axis, terrain, parallel, _) = group
|
||||
new_grouping[(std, axis, terrain, parallel)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(std, _, 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, axis, terrain, parallel)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(std, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
# tuple goes to (_,_,C,D,E,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, axis, terrain, parallel, _) = group
|
||||
new_grouping[(axis, terrain, parallel)] = ([], [])
|
||||
|
||||
for group in grouping.keys():
|
||||
(_, _, 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[(axis, terrain, parallel)]
|
||||
exist_forward_edges.extend(forward_edges)
|
||||
exist_back_edges.extend(back_edges)
|
||||
new_grouping[(axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
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())
|
||||
elif world.owShuffle[player] == 'vanilla' and world.owCrossed[player]:
|
||||
if world.owKeepSimilar[player]:
|
||||
if world.mode[player] == 'standard':
|
||||
#tuple stays (A,B,C,D,_,F)
|
||||
# tuple goes to (A,B,C,D,_,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
@@ -363,7 +745,7 @@ def reorganize_groups(world, groups, player):
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
#tuple goes to (_,B,C,D,_,F)
|
||||
# tuple goes to (_,B,C,D,_,F)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
@@ -382,7 +764,7 @@ def reorganize_groups(world, groups, player):
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
if world.mode[player] == 'standard':
|
||||
#tuple stays (A,B,C,D,_,_)
|
||||
# tuple goes to (A,B,C,D,_,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
@@ -403,7 +785,7 @@ def reorganize_groups(world, groups, player):
|
||||
|
||||
return list(new_grouping.values())
|
||||
else:
|
||||
#tuple goes to (_,B,C,D,_,_)
|
||||
# tuple goes to (_,B,C,D,_,_)
|
||||
for grouping in (groups,):
|
||||
new_grouping = {}
|
||||
|
||||
@@ -422,82 +804,14 @@ def reorganize_groups(world, groups, player):
|
||||
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':
|
||||
#predefined shuffle groups get reorganized here
|
||||
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 stays (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')
|
||||
|
||||
def create_flute_exits(world, player):
|
||||
for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']):
|
||||
if (world.owSwap[player] != 'mixed' and region.type == RegionType.LightWorld) \
|
||||
or (world.owSwap[player] == 'mixed' and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
|
||||
if (not world.owMixed[player] and region.type == RegionType.LightWorld) \
|
||||
or (world.owMixed[player] and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
|
||||
and (region.name not in world.owswaps[player][1] or region.name in world.owswaps[player][2])):
|
||||
exitname = 'Flute From ' + region.name
|
||||
exit = Entrance(region.player, exitname, region)
|
||||
@@ -507,7 +821,7 @@ def create_flute_exits(world, player):
|
||||
world.initialize_regions()
|
||||
|
||||
def update_world_regions(world, player):
|
||||
if world.owSwap[player] == 'mixed':
|
||||
if world.owMixed[player]:
|
||||
for name in world.owswaps[player][1]:
|
||||
world.get_region(name, player).type = RegionType.DarkWorld
|
||||
for name in world.owswaps[player][2]:
|
||||
@@ -1111,9 +1425,8 @@ ow_connections = {
|
||||
}
|
||||
|
||||
parallelsimilar_connections = [('Maze Race ES', 'Kakariko Suburb WS'),
|
||||
('Dig Game EC', 'Frog WC'),
|
||||
('Dig Game ES', 'Frog WS')
|
||||
]
|
||||
('Dig Game EC', 'Frog WC')
|
||||
]
|
||||
|
||||
# non shuffled overworld
|
||||
default_connections = [#('Lost Woods NW', 'Master Sword Meadow SC'),
|
||||
|
||||
175
Plando.py
175
Plando.py
@@ -9,6 +9,7 @@ import sys
|
||||
|
||||
from BaseClasses import World
|
||||
from Regions import create_regions
|
||||
from OverworldShuffle import link_overworld
|
||||
from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit
|
||||
from Rom import patch_rom, LocalRom, write_string_to_rom, apply_rom_settings, get_sprite_from_name
|
||||
from Rules import set_rules
|
||||
@@ -42,6 +43,11 @@ def main(args):
|
||||
create_regions(world, 1)
|
||||
create_dungeons(world, 1)
|
||||
|
||||
text_patches = []
|
||||
prefill_world(world, args.plando, text_patches)
|
||||
|
||||
link_overworld(world, 1)
|
||||
|
||||
link_entrances(world, 1)
|
||||
|
||||
logger.info('Calculating Access Rules.')
|
||||
@@ -50,9 +56,7 @@ def main(args):
|
||||
|
||||
logger.info('Fill the world.')
|
||||
|
||||
text_patches = []
|
||||
|
||||
fill_world(world, args.plando, text_patches)
|
||||
fill_world(world, args.plando)
|
||||
|
||||
if world.get_entrance('Dam', 1).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', 1).connected_region.name != 'Swamp Palace (Entrance)':
|
||||
world.swamp_patch_required[1] = True
|
||||
@@ -92,75 +96,14 @@ def main(args):
|
||||
return world
|
||||
|
||||
|
||||
def fill_world(world, plando, text_patches):
|
||||
mm_medallion = 'Ether'
|
||||
tr_medallion = 'Quake'
|
||||
def fill_world(world, plando):
|
||||
logger = logging.getLogger('')
|
||||
with open(plando, 'r') as plandofile:
|
||||
for line in plandofile.readlines():
|
||||
line = line.lstrip()
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
if ':' in line:
|
||||
line = line.lstrip()
|
||||
|
||||
if line.startswith('!'):
|
||||
if line.startswith('!mm_medallion'):
|
||||
_, medallionstr = line.split(':', 1)
|
||||
mm_medallion = medallionstr.strip()
|
||||
elif line.startswith('!tr_medallion'):
|
||||
_, medallionstr = line.split(':', 1)
|
||||
tr_medallion = medallionstr.strip()
|
||||
elif line.startswith('!mode'):
|
||||
_, modestr = line.split(':', 1)
|
||||
world.mode = {1: modestr.strip()}
|
||||
elif line.startswith('!logic'):
|
||||
_, logicstr = line.split(':', 1)
|
||||
world.logic = {1: logicstr.strip()}
|
||||
elif line.startswith('!goal'):
|
||||
_, goalstr = line.split(':', 1)
|
||||
world.goal = {1: goalstr.strip()}
|
||||
elif line.startswith('!light_cone_sewers'):
|
||||
_, sewerstr = line.split(':', 1)
|
||||
world.sewer_light_cone = {1: sewerstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!light_cone_lw'):
|
||||
_, lwconestr = line.split(':', 1)
|
||||
world.light_world_light_cone = lwconestr.strip().lower() == 'true'
|
||||
elif line.startswith('!light_cone_dw'):
|
||||
_, dwconestr = line.split(':', 1)
|
||||
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
|
||||
elif line.startswith('!fix_trock_doors'):
|
||||
_, trdstr = line.split(':', 1)
|
||||
world.fix_trock_doors = {1: trdstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_trock_exit'):
|
||||
_, trfstr = line.split(':', 1)
|
||||
world.fix_trock_exit = {1: trfstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_gtower_exit'):
|
||||
_, gtfstr = line.split(':', 1)
|
||||
world.fix_gtower_exit = gtfstr.strip().lower() == 'true'
|
||||
elif line.startswith('!fix_pod_exit'):
|
||||
_, podestr = line.split(':', 1)
|
||||
world.fix_palaceofdarkness_exit = {1: podestr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_skullwoods_exit'):
|
||||
_, swestr = line.split(':', 1)
|
||||
world.fix_skullwoods_exit = {1: swestr.strip().lower() == 'true'}
|
||||
elif line.startswith('!check_beatable_only'):
|
||||
_, chkbtstr = line.split(':', 1)
|
||||
world.check_beatable_only = chkbtstr.strip().lower() == 'true'
|
||||
elif line.startswith('!ganon_death_pyramid_respawn'):
|
||||
_, gnpstr = line.split(':', 1)
|
||||
world.ganon_at_pyramid = gnpstr.strip().lower() == 'true'
|
||||
elif line.startswith('!save_quit_boss'):
|
||||
_, sqbstr = line.split(':', 1)
|
||||
world.save_and_quite_from_boss = sqbstr.strip().lower() == 'true'
|
||||
elif line.startswith('!text_'):
|
||||
textname, text = line.split(':', 1)
|
||||
text_patches.append([textname.lstrip('!text_').strip(), 'text', text.strip()])
|
||||
#temporarilly removed. New credits system not ready to handle this.
|
||||
#elif line.startswith('!credits_'):
|
||||
# textname, text = line.split(':', 1)
|
||||
# text_patches.append([textname.lstrip('!credits_').strip(), 'credits', text.strip()])
|
||||
continue
|
||||
|
||||
elif ':' in line:
|
||||
locationstr, itemstr = line.split(':', 1)
|
||||
location = world.get_location(locationstr.strip(), 1)
|
||||
if location is None:
|
||||
@@ -172,6 +115,7 @@ def fill_world(world, plando, text_patches):
|
||||
world.push_item(location, item)
|
||||
if item.smallkey or item.bigkey:
|
||||
location.event = True
|
||||
#TODO: move entrance stuff to prefill_world to work like OW stuff
|
||||
elif '<=>' in line:
|
||||
entrance, exit = line.split('<=>', 1)
|
||||
connect_two_way(world, entrance.strip(), exit.strip(), 1)
|
||||
@@ -182,14 +126,107 @@ def fill_world(world, plando, text_patches):
|
||||
entrance, exit = line.split('<=', 1)
|
||||
connect_exit(world, exit.strip(), entrance.strip(), 1)
|
||||
|
||||
world.required_medallions[1] = (mm_medallion, tr_medallion)
|
||||
|
||||
# set up Agahnim Events
|
||||
world.get_location('Agahnim 1', 1).event = True
|
||||
world.get_location('Agahnim 1', 1).item = ItemFactory('Beat Agahnim 1', 1)
|
||||
world.get_location('Agahnim 2', 1).event = True
|
||||
world.get_location('Agahnim 2', 1).item = ItemFactory('Beat Agahnim 2', 1)
|
||||
|
||||
def prefill_world(world, plando, text_patches):
|
||||
mm_medallion = 'Ether'
|
||||
tr_medallion = 'Quake'
|
||||
logger = logging.getLogger('')
|
||||
with open(plando, 'r') as plandofile:
|
||||
for line in plandofile.readlines():
|
||||
line = line.lstrip()
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
elif line.startswith('!'):
|
||||
if line.startswith('!mm_medallion'):
|
||||
_, medallionstr = line.split(':', 1)
|
||||
mm_medallion = medallionstr.strip()
|
||||
elif line.startswith('!tr_medallion'):
|
||||
_, medallionstr = line.split(':', 1)
|
||||
tr_medallion = medallionstr.strip()
|
||||
elif line.startswith('!mode'):
|
||||
_, modestr = line.split(':', 1)
|
||||
world.mode = {1: modestr.strip()}
|
||||
elif line.startswith('!logic'):
|
||||
_, logicstr = line.split(':', 1)
|
||||
world.logic = {1: logicstr.strip()}
|
||||
elif line.startswith('!goal'):
|
||||
_, goalstr = line.split(':', 1)
|
||||
world.goal = {1: goalstr.strip()}
|
||||
elif line.startswith('!owShuffle'):
|
||||
_, modestr = line.split(':', 1)
|
||||
world.owShuffle = {1: modestr.strip()}
|
||||
elif line.startswith('!owCrossed'):
|
||||
_, modestr = line.split(':', 1)
|
||||
modestr = modestr.strip().lower()
|
||||
world.owCrossed = {1: True if modestr in ('true', 'yes', 'on', 'enabled') else False}
|
||||
elif line.startswith('!owKeepSimilar'):
|
||||
_, modestr = line.split(':', 1)
|
||||
modestr = modestr.strip().lower()
|
||||
world.owKeepSimilar = {1: True if modestr in ('true', 'yes', 'on', 'enabled') else False}
|
||||
elif line.startswith('!light_cone_sewers'):
|
||||
_, sewerstr = line.split(':', 1)
|
||||
world.sewer_light_cone = {1: sewerstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!light_cone_lw'):
|
||||
_, lwconestr = line.split(':', 1)
|
||||
world.light_world_light_cone = lwconestr.strip().lower() == 'true'
|
||||
elif line.startswith('!light_cone_dw'):
|
||||
_, dwconestr = line.split(':', 1)
|
||||
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
|
||||
elif line.startswith('!fix_trock_doors'):
|
||||
_, trdstr = line.split(':', 1)
|
||||
world.fix_trock_doors = {1: trdstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_trock_exit'):
|
||||
_, trfstr = line.split(':', 1)
|
||||
world.fix_trock_exit = {1: trfstr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_gtower_exit'):
|
||||
_, gtfstr = line.split(':', 1)
|
||||
world.fix_gtower_exit = gtfstr.strip().lower() == 'true'
|
||||
elif line.startswith('!fix_pod_exit'):
|
||||
_, podestr = line.split(':', 1)
|
||||
world.fix_palaceofdarkness_exit = {1: podestr.strip().lower() == 'true'}
|
||||
elif line.startswith('!fix_skullwoods_exit'):
|
||||
_, swestr = line.split(':', 1)
|
||||
world.fix_skullwoods_exit = {1: swestr.strip().lower() == 'true'}
|
||||
elif line.startswith('!check_beatable_only'):
|
||||
_, chkbtstr = line.split(':', 1)
|
||||
world.check_beatable_only = chkbtstr.strip().lower() == 'true'
|
||||
elif line.startswith('!ganon_death_pyramid_respawn'):
|
||||
_, gnpstr = line.split(':', 1)
|
||||
world.ganon_at_pyramid = gnpstr.strip().lower() == 'true'
|
||||
elif line.startswith('!save_quit_boss'):
|
||||
_, sqbstr = line.split(':', 1)
|
||||
world.save_and_quite_from_boss = sqbstr.strip().lower() == 'true'
|
||||
elif line.startswith('!text_'):
|
||||
textname, text = line.split(':', 1)
|
||||
text_patches.append([textname.lstrip('!text_').strip(), 'text', text.strip()])
|
||||
#temporarilly removed. New credits system not ready to handle this.
|
||||
#elif line.startswith('!credits_'):
|
||||
# textname, text = line.split(':', 1)
|
||||
# text_patches.append([textname.lstrip('!credits_').strip(), 'credits', text.strip()])
|
||||
continue
|
||||
elif line.startswith('$'):
|
||||
edge1, edge2 = line.split('=', 1)
|
||||
if world.custom_overworld is None:
|
||||
world.custom_overworld = {1: []}
|
||||
world.custom_overworld[1].append(edge1.strip(), edge2.strip())
|
||||
#TODO: Do entrances similar to OW
|
||||
elif '<=>' in line:
|
||||
entrance, exit = line.split('<=>', 1)
|
||||
#connect_two_way(world, entrance.strip(), exit.strip(), 1)
|
||||
elif '=>' in line:
|
||||
entrance, exit = line.split('=>', 1)
|
||||
#connect_entrance(world, entrance.strip(), exit.strip(), 1)
|
||||
elif '<=' in line:
|
||||
entrance, exit = line.split('<=', 1)
|
||||
#connect_exit(world, exit.strip(), entrance.strip(), 1)
|
||||
|
||||
world.required_medallions[1] = (mm_medallion, tr_medallion)
|
||||
|
||||
|
||||
def start():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
@@ -242,3 +242,10 @@ Ganons Tower - Mini Helmasaur Room - Right: Nothing
|
||||
Ganons Tower - Pre-Moldorm Chest: Small Key (Ganons Tower)
|
||||
Ganons Tower - Validation Chest: Nothing
|
||||
Ganon: Triforce
|
||||
|
||||
# set Overworld connections (lines starting with $, separate edges with =)
|
||||
!owShuffle: parallel
|
||||
#!owMixed: true # Mixed OW not supported yet
|
||||
!owCrossed: true
|
||||
!owKeepSimilar: true
|
||||
$Links House NE = Kakariko Village SE
|
||||
|
||||
28
README.md
28
README.md
@@ -66,19 +66,9 @@ OW Transitions are shuffled, but both worlds will have a matching layout.
|
||||
|
||||
OW Transitions are shuffled within each world separately.
|
||||
|
||||
## Overworld Tile Swap (--ow_swap)
|
||||
## Crossed (--ow_crossed)
|
||||
|
||||
### Vanilla
|
||||
|
||||
OW tiles remain in their original worlds.
|
||||
|
||||
### Mixed
|
||||
|
||||
OW tiles are randomly chosen to become a part of the opposite world
|
||||
|
||||
### Crossed
|
||||
|
||||
OW tiles remain in their original world, but transitions can now be travel cross-world.
|
||||
This allows OW connections to be shuffled cross-world.
|
||||
|
||||
## Visual Representation of Main OW Shuffle Settings
|
||||
|
||||
@@ -88,6 +78,10 @@ OW tiles remain in their original world, but transitions can now be travel cross
|
||||
|
||||
This keeps similar edge transitions together. ie. The 2 west edges of Potion Shop will be paired to another set of two similar edges
|
||||
|
||||
## Mixed Overworld (--ow_mixed)
|
||||
|
||||
OW tiles are randomly chosen to become a part of the opposite world
|
||||
|
||||
## Flute Shuffle (--ow_fluteshuffle)
|
||||
|
||||
When enabled, new flute spots are generated and gives the player the option to cancel out of the flute menu by pressing X.
|
||||
@@ -120,10 +114,10 @@ Show the help message and exit.
|
||||
For specifying the overworld layout shuffle you want as above. (default: vanilla)
|
||||
|
||||
```
|
||||
--ow_swap <mode>
|
||||
--ow_crossed
|
||||
```
|
||||
|
||||
For specifying the overworld tile swap you want as above. (default: vanilla)
|
||||
This allows cross-world connections on the overworld
|
||||
|
||||
```
|
||||
--ow_keepsimilar
|
||||
@@ -131,6 +125,12 @@ For specifying the overworld tile swap you want as above. (default: vanilla)
|
||||
|
||||
This keeps similar edge transitions paired together with other pairs of transitions
|
||||
|
||||
```
|
||||
--ow_mixed
|
||||
```
|
||||
|
||||
This gives each OW tile a random chance to be swapped to the opposite world
|
||||
|
||||
```
|
||||
--ow_fluteshuffle <mode>
|
||||
```
|
||||
|
||||
@@ -15,6 +15,10 @@ CLI: ```--bombbag```
|
||||
|
||||
# Bug Fixes and Notes.
|
||||
|
||||
* 0.5.0.3
|
||||
* Fixed a bug in retro+vanilla and big key placement
|
||||
* Fixed a problem with shops not registering in the Multiclient until you visit one
|
||||
* Fixed a bug in the Mystery code with sfx
|
||||
* 0.5.0.2
|
||||
* --shuffle_sfx option added
|
||||
* 0.5.0.1
|
||||
|
||||
139
Rom.py
139
Rom.py
@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = 'cc8fc59caa0bbe6d26ac64b9d2893709'
|
||||
RANDOMIZERBASEHASH = '99f3f57ab2c9449172cade4927a462d6'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -612,71 +612,20 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
if world.pot_contents[player]:
|
||||
write_pots_to_rom(rom, world.pot_contents[player])
|
||||
|
||||
# patch overworld edges
|
||||
inverted_buffer = [0] * 0x82
|
||||
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
|
||||
owMode = 0
|
||||
if world.owShuffle[player] == 'parallel':
|
||||
owMode = 1
|
||||
elif world.owShuffle[player] == 'full':
|
||||
owMode = 2
|
||||
|
||||
if world.owSwap[player] == 'mixed':
|
||||
owMode |= 0x100
|
||||
world.fix_fake_world[player] = True
|
||||
elif world.owSwap[player] == 'crossed':
|
||||
owMode |= 0x200
|
||||
world.fix_fake_world[player] = True
|
||||
|
||||
write_int16(rom, 0x150002, owMode)
|
||||
|
||||
owFlags = 0
|
||||
if world.owKeepSimilar[player]:
|
||||
owFlags |= 0x1
|
||||
if world.owFluteShuffle[player] != 'vanilla':
|
||||
owFlags |= 0x100
|
||||
|
||||
write_int16(rom, 0x150004, owFlags)
|
||||
|
||||
rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves
|
||||
|
||||
# patches map data specific for OW Shuffle
|
||||
#inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM
|
||||
inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain
|
||||
#inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM
|
||||
inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain
|
||||
|
||||
if world.owSwap[player] == 'mixed':
|
||||
for b in world.owswaps[player][0]:
|
||||
# load inverted maps
|
||||
inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2)
|
||||
|
||||
# set world flag
|
||||
rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40)
|
||||
|
||||
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())
|
||||
|
||||
# patch flute spots
|
||||
owFlags = 0
|
||||
if world.owFluteShuffle[player] == 'vanilla':
|
||||
flute_spots = default_flute_connections
|
||||
else:
|
||||
flute_spots = world.owflutespots[player]
|
||||
owFlags |= 0x100
|
||||
|
||||
for o in range(0, len(flute_spots)):
|
||||
owslot = flute_spots[o]
|
||||
offset = 0
|
||||
data = flute_data[owslot]
|
||||
|
||||
if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owMixed[player]):
|
||||
offset = 0x40
|
||||
|
||||
write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), data[1] + offset) # owid
|
||||
@@ -695,7 +644,55 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
||||
rom.write_byte(snes_to_pc(0x0AB78B + o), data[12] // 0x100) # X high byte
|
||||
rom.write_byte(snes_to_pc(0x0AB793 + o), data[11] & 0xff) # Y low byte
|
||||
rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte
|
||||
|
||||
# patch overworld edges
|
||||
inverted_buffer = [0] * 0x82
|
||||
if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] or world.owMixed[player]:
|
||||
owMode = 0
|
||||
if world.owShuffle[player] == 'parallel':
|
||||
owMode = 1
|
||||
elif world.owShuffle[player] == 'full':
|
||||
owMode = 2
|
||||
|
||||
if world.owKeepSimilar[player] and (world.owShuffle[player] != 'vanilla' or world.owCrossed[player]):
|
||||
owMode |= 0x100
|
||||
if world.owCrossed[player]:
|
||||
owMode |= 0x200
|
||||
world.fix_fake_world[player] = True
|
||||
if world.owMixed[player]:
|
||||
owMode |= 0x400
|
||||
|
||||
write_int16(rom, 0x150002, owMode)
|
||||
|
||||
write_int16(rom, 0x150004, owFlags)
|
||||
|
||||
rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves
|
||||
|
||||
# patches map data specific for OW Shuffle
|
||||
#inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM
|
||||
inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain
|
||||
#inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM
|
||||
inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock
|
||||
inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain
|
||||
|
||||
if world.owMixed[player]:
|
||||
for b in world.owswaps[player][0]:
|
||||
# load inverted maps
|
||||
inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2)
|
||||
|
||||
# set world flag
|
||||
rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40)
|
||||
|
||||
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())
|
||||
|
||||
|
||||
# patch entrance/exits/holes
|
||||
for region in world.regions:
|
||||
@@ -2177,10 +2174,10 @@ def write_strings(rom, world, player, team):
|
||||
if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']:
|
||||
entrances_to_hint.update(InsanityEntrances)
|
||||
if world.shuffle_ganon:
|
||||
if world.mode[player] == 'inverted':
|
||||
if world.mode[player] == 'inverted' != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
|
||||
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
|
||||
else:
|
||||
entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'})
|
||||
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
|
||||
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 0
|
||||
for entrance in all_entrances:
|
||||
if entrance.name in entrances_to_hint:
|
||||
@@ -2247,7 +2244,7 @@ def write_strings(rom, world, player, team):
|
||||
tt[hint_locations.pop(0)] = this_hint
|
||||
|
||||
# Adding a guaranteed hint for the Flute in overworld shuffle.
|
||||
if world.owShuffle[player] in ['parallel','full']:
|
||||
if world.owShuffle[player] != 'vanilla' or world.owMixed[player]:
|
||||
this_location = world.find_items_not_key_only('Ocarina', player)
|
||||
if this_location:
|
||||
this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.'
|
||||
@@ -2255,7 +2252,7 @@ def write_strings(rom, world, player, team):
|
||||
|
||||
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
|
||||
items_to_hint = RelevantItems.copy()
|
||||
if world.owShuffle[player] in ['parallel','full']:
|
||||
if world.owShuffle[player] != 'vanilla' or world.owMixed[player]:
|
||||
items_to_hint.remove('Ocarina')
|
||||
if world.keyshuffle[player]:
|
||||
items_to_hint.extend(SmallKeys)
|
||||
@@ -2264,7 +2261,7 @@ def write_strings(rom, world, player, team):
|
||||
random.shuffle(items_to_hint)
|
||||
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
|
||||
hint_count += 2 if world.doorShuffle[player] == 'crossed' else 0
|
||||
hint_count += 1 if world.owShuffle[player] in ['parallel', 'full'] else 0
|
||||
hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owMixed[player] else 0
|
||||
while hint_count > 0:
|
||||
this_item = items_to_hint.pop(0)
|
||||
this_location = world.find_items_not_key_only(this_item, player)
|
||||
@@ -2475,7 +2472,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
|
||||
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.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']:
|
||||
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW
|
||||
|
||||
@@ -2498,18 +2495,18 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
|
||||
rom.write_byte(snes_to_pc(0x02D9B8), 0x12)
|
||||
|
||||
rom.write_bytes(0x180247, [0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00]) #indicates the overworld door being used for the single entrance spawn point
|
||||
if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock
|
||||
if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC387), [0xDD, 0xD1]) # add warps under rocks
|
||||
rom.write_bytes(snes_to_pc(0x1BD1DD), [0xA4, 0x06, 0x82, 0x9E, 0x06, 0x82, 0xFF, 0xFF]) # add warps under rocks
|
||||
rom.write_byte(0x180089, 0x01) # open TR after exit
|
||||
rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail
|
||||
if world.shuffle[player] in ['vanilla']:
|
||||
world.fix_trock_doors[player] = True
|
||||
if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock
|
||||
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owMixed[player]):
|
||||
write_int16(rom, 0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn
|
||||
rom.write_byte(0x15B8C + 0x06, 0x1B)
|
||||
write_int16(rom, 0x15BDB + 2 * 0x06, 0x00AE)
|
||||
@@ -2597,21 +2594,21 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
|
||||
|
||||
write_int16(rom, 0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door
|
||||
write_int16(rom, 0xDBA71 + 2 * 0x35, 0x011C)
|
||||
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact
|
||||
if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owMixed[player]):
|
||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||
rom.write_byte(0x15B8C, 0x6C) # exit links at bomb shop area
|
||||
rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house
|
||||
rom.write_byte(0xDBB73 + 0x52, 0x01)
|
||||
if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock
|
||||
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock
|
||||
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) # add warp under rock
|
||||
rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock
|
||||
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock
|
||||
|
||||
# apply inverted map changes
|
||||
|
||||
82
Rules.py
82
Rules.py
@@ -844,7 +844,7 @@ def ow_rules(world, player):
|
||||
else:
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Lost Woods Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Lost Woods Pedestal Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -859,12 +859,12 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Skull Woods Forgotten (Middle) Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Skull Woods Front Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('West Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Spectacle Rock Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
@@ -872,7 +872,7 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Bubble Boy Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('West Dark Death Mountain (Bottom) Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('East Death Mountain (Top West) Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('East Death Mountain (Top East) Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Mimic Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -894,7 +894,7 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Dark Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: state.can_lift_heavy_rocks(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('TR Pegs Area Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('TR Pegs Teleporter', player), lambda state: state.has('Hammer', player))
|
||||
else:
|
||||
@@ -902,7 +902,7 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Turtle Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Mountain Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Mountain Entry Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Mountain Entry Entrance Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -911,12 +911,12 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Bumper Cave Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Zora Waterfall Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Catfish Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Lost Woods Pass West Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Lost Woods Pass East Top Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Lost Woods Pass East Bottom Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -929,24 +929,24 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('West Dark World Teleporter (Hammer)', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('West Dark World Teleporter (Rock)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) # bunny cannot lift bushes
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Kakariko Fortune Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Outcast Fortune Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Kakariko Pond Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Outcast Pond Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Sanctuary Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Bonk Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Chapel Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Chapel Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
|
||||
else:
|
||||
@@ -956,28 +956,28 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Dark Graveyard Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Graveyard Grave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('River Bend Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('River Bend East Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Qirn Jump Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Qirn Jump East Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Potion Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Potion Shop Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Witch Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Witch Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Zora Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Zora Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Catfish Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Catfish Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Kakariko Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Kakariko Grass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
@@ -985,13 +985,13 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Village of Outcasts Southwest Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Hammer House Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Forgotton Forest Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Forgotton Forest Fence Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Shield Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: False)
|
||||
set_rule(world.get_entrance('Inverted Pyramid Entrance', player), lambda state: False)
|
||||
set_rule(world.get_entrance('Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player])
|
||||
@@ -1017,7 +1017,7 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Pyramid Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Post Aga Inverted Teleporter', player), lambda state: state.has('Beat Agahnim 1', player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Wooden Bridge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Wooden Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Wooden Bridge West Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -1026,12 +1026,12 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Broken Bridge East Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Broken Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Eastern Palace Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Palace of Darkness Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Blacksmith Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Blacksmith Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Bat Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -1039,19 +1039,19 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Hammer Pegs Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Hammer Pegs Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Sand Dunes Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Dunes Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Maze Race Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dig Game Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dig Game Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Kakariko Suburb Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Kakariko Suburb South Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
@@ -1059,24 +1059,24 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Frog Prison Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Archery Game Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Flute Boy Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Flute Boy Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Stumpy Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Stumpy Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Central Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Links House Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Big Bomb Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Stone Bridge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Stone Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player))
|
||||
@@ -1085,19 +1085,19 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Hammer Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Eastern Nook Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
|
||||
else:
|
||||
set_rule(world.get_entrance('Darkness Nook Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Checkerboard Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Desert Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Desert Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -1112,14 +1112,14 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Misery Mire Main Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Misery Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Flute Boy Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Stumpy Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Stumpy Bush Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('C Whirlpool Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('C Whirlpool Outer Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
|
||||
@@ -1130,12 +1130,12 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('South Teleporter Cliff Ledge Drop', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Statues Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Hype Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Lake Hylia Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Lake Hylia Northeast Mirror Spot', player), lambda state: state.has_Mirror(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))
|
||||
@@ -1152,12 +1152,12 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Ice Palace Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Ice Palace Teleporter', player), lambda state: state.can_lift_heavy_rocks(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Ice Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Shopping Mall Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Desert Pass Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Desert Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
@@ -1165,17 +1165,17 @@ def ow_rules(world, player):
|
||||
set_rule(world.get_entrance('Swamp Nook Southeast Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Swamp Nook Pegs Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Dam Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Swamp Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('South Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Dark South Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
|
||||
if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
|
||||
if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owMixed[player]):
|
||||
set_rule(world.get_entrance('Octoballoon Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Bomber Corner Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -2050,7 +2050,7 @@ def set_inverted_big_bomb_rules(world, player):
|
||||
else:
|
||||
raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name)
|
||||
|
||||
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
|
||||
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]:
|
||||
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) #temp disable progression until routing to Pyramid get be guaranteed
|
||||
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ OWNewDestination:
|
||||
sep #$30 : lda OWOppSlotOffset,y : !add $04 : asl : and #$7f : sta $700
|
||||
|
||||
; crossed OW shuffle
|
||||
lda.l OWMode+1 : and #$ff : cmp #$02 : bne .return
|
||||
lda.l OWMode+1 : and.b #$02 : beq .return
|
||||
ldx $05 : lda.l OWTileWorldAssoc,x : sta.l $7ef3ca ; change world
|
||||
|
||||
; toggle bunny mode
|
||||
|
||||
Binary file not shown.
@@ -3,13 +3,15 @@
|
||||
vanilla: 0
|
||||
parallel: 2
|
||||
full: 2
|
||||
overworld_crossed:
|
||||
on: 1
|
||||
off: 1
|
||||
overworld_keepsimilar:
|
||||
on: 1
|
||||
off: 1
|
||||
overworld_swap:
|
||||
vanilla: 0
|
||||
mixed: 2
|
||||
crossed: 2
|
||||
overworld_mixed:
|
||||
on: 1
|
||||
off: 1
|
||||
flute_shuffle:
|
||||
vanilla: 0
|
||||
balanced: 1
|
||||
|
||||
@@ -116,17 +116,18 @@
|
||||
"full"
|
||||
]
|
||||
},
|
||||
"ow_swap": {
|
||||
"choices": [
|
||||
"vanilla",
|
||||
"mixed",
|
||||
"crossed"
|
||||
]
|
||||
"ow_crossed": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"ow_keepsimilar": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"ow_mixed": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"ow_fluteshuffle": {
|
||||
"choices": [
|
||||
"vanilla",
|
||||
@@ -142,10 +143,6 @@
|
||||
"full",
|
||||
"crossed",
|
||||
"insanity",
|
||||
"restricted_legacy",
|
||||
"full_legacy",
|
||||
"madness_legacy",
|
||||
"insanity_legacy",
|
||||
"dungeonsfull",
|
||||
"dungeonssimple"
|
||||
]
|
||||
|
||||
@@ -189,10 +189,8 @@
|
||||
" which they are entered.",
|
||||
"Vanilla: All entrances are in the same locations they were",
|
||||
" in the base game.",
|
||||
"Legacy shuffles preserve behavior from older versions of the",
|
||||
"entrance randomizer including significant technical limitations.",
|
||||
"The dungeon variants only mix up dungeons and keep the rest of",
|
||||
"the overworld vanilla."
|
||||
"the entrances vanilla."
|
||||
],
|
||||
"ow_shuffle": [
|
||||
"This shuffles the layout of the overworld.",
|
||||
@@ -203,22 +201,19 @@
|
||||
"Full: Overworld transitions are shuffled, but both worlds",
|
||||
" will have an independent map shape."
|
||||
],
|
||||
"ow_swap": [
|
||||
"This swaps the tiles of the overworld from one world to the other.",
|
||||
"Vanilla: All overworld tiles remain in their original world as",
|
||||
" they were in the base game.",
|
||||
"Mixed: Overworld tiles are randomly chosen to become part of",
|
||||
" the opposite world.",
|
||||
"Crossed: Overworld tiles remain in their original world, but",
|
||||
" the transitions are shuffled cross world."
|
||||
],
|
||||
"ow_crossed": [
|
||||
"This allows cross-world connections to occur on the overworld." ],
|
||||
"ow_keepsimilar": [
|
||||
"This keeps similar edge transitions together. ie. the two west edges on",
|
||||
"Potion Shop will be paired with another similar pair." ],
|
||||
"ow_mixed": [
|
||||
"Overworld tiles are randomly chosen to become part of the opposite world."
|
||||
],
|
||||
"ow_fluteshuffle": [
|
||||
"This randomizes the flute spot destinations.",
|
||||
"Vanilla: All flute spots remain unchanged.",
|
||||
"Balanced: New flute spots will be generated but prevents flute spots from being on any adjacent screen.",
|
||||
"Balanced: New flute spots will be generated but prevents flute",
|
||||
" spots from being on any adjacent screen.",
|
||||
"Random: New flute spots will be generated with minimal bias."
|
||||
],
|
||||
"door_shuffle": [
|
||||
|
||||
@@ -117,12 +117,9 @@
|
||||
"randomizer.overworld.overworldshuffle.parallel": "Parallel",
|
||||
"randomizer.overworld.overworldshuffle.full": "Full",
|
||||
|
||||
"randomizer.overworld.overworldswap": "Tile Swap",
|
||||
"randomizer.overworld.overworldswap.vanilla": "Vanilla",
|
||||
"randomizer.overworld.overworldswap.mixed": "Mixed",
|
||||
"randomizer.overworld.overworldswap.crossed": "Crossed",
|
||||
|
||||
"randomizer.overworld.crossed": "Crossed",
|
||||
"randomizer.overworld.keepsimilar": "Keep Similar Edges Together",
|
||||
"randomizer.overworld.mixed": "Mixed",
|
||||
|
||||
"randomizer.overworld.overworldflute": "Flute Shuffle",
|
||||
"randomizer.overworld.overworldflute.vanilla": "Vanilla",
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
"full",
|
||||
"crossed",
|
||||
"insanity",
|
||||
"restricted_legacy",
|
||||
"full_legacy",
|
||||
"madness_legacy",
|
||||
"insanity_legacy",
|
||||
"dungeonsfull",
|
||||
"dungeonssimple"
|
||||
]
|
||||
|
||||
@@ -9,15 +9,6 @@
|
||||
"full"
|
||||
]
|
||||
},
|
||||
"overworldswap": {
|
||||
"type": "selectbox",
|
||||
"default": "mixed",
|
||||
"options": [
|
||||
"vanilla",
|
||||
"mixed",
|
||||
"crossed"
|
||||
]
|
||||
},
|
||||
"overworldflute": {
|
||||
"type": "selectbox",
|
||||
"default": "vanilla",
|
||||
@@ -29,6 +20,14 @@
|
||||
}
|
||||
},
|
||||
"rightOverworldFrame": {
|
||||
"crossed": {
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
},
|
||||
"mixed": {
|
||||
"type": "checkbox",
|
||||
"default": true
|
||||
},
|
||||
"keepsimilar": {
|
||||
"type": "checkbox",
|
||||
"default": true
|
||||
|
||||
@@ -76,8 +76,9 @@ SETTINGSTOPROCESS = {
|
||||
},
|
||||
"overworld": {
|
||||
"overworldshuffle": "ow_shuffle",
|
||||
"overworldswap": "ow_swap",
|
||||
"crossed": "ow_crossed",
|
||||
"keepsimilar": "ow_keepsimilar",
|
||||
"mixed": "ow_mixed",
|
||||
"overworldflute": "ow_fluteshuffle"
|
||||
},
|
||||
"entrance": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from tkinter import ttk, Frame, Label, W, E, NW, LEFT, RIGHT, X, TOP
|
||||
from tkinter import ttk, Frame, Label, W, E, NW, LEFT, RIGHT, X, Y, TOP
|
||||
import source.gui.widgets as widgets
|
||||
import json
|
||||
import os
|
||||
@@ -16,12 +16,10 @@ def overworld_page(parent):
|
||||
# Load Overworld Shuffle option widgets as defined by JSON file
|
||||
# Defns include frame name, widget type, widget options, widget placement attributes
|
||||
# These get split left & right
|
||||
self.frames["widgets"] = Frame(self)
|
||||
self.frames["leftOverworldFrame"] = Frame(self.frames["widgets"])
|
||||
self.frames["rightOverworldFrame"] = Frame(self.frames["widgets"])
|
||||
self.frames["widgets"].pack(fill=X)
|
||||
self.frames["leftOverworldFrame"].pack(side=LEFT)
|
||||
self.frames["rightOverworldFrame"].pack(side=LEFT, anchor=NW)
|
||||
self.frames["leftOverworldFrame"] = Frame(self)
|
||||
self.frames["rightOverworldFrame"] = Frame(self)
|
||||
self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW)
|
||||
self.frames["rightOverworldFrame"].pack(anchor=NW)
|
||||
|
||||
with open(os.path.join("resources","app","gui","randomize","overworld","widgets.json")) as overworldWidgets:
|
||||
myDict = json.load(overworldWidgets)
|
||||
@@ -29,8 +27,8 @@ def overworld_page(parent):
|
||||
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
|
||||
for key in dictWidgets:
|
||||
self.widgets[key] = dictWidgets[key]
|
||||
if key == "rightOverworldFrame":
|
||||
self.widgets[key].pack(anchor=E)
|
||||
if framename == "rightOverworldFrame":
|
||||
self.widgets[key].pack(side=LEFT)
|
||||
else:
|
||||
self.widgets[key].pack(anchor=E)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user