Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2021-10-02 14:09:34 -05:00
17 changed files with 980 additions and 642 deletions

View File

@@ -12,7 +12,6 @@ except ImportError:
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish
from EntranceShuffle import door_addresses, indirect_connections
from Utils import int16_as_bytes from Utils import int16_as_bytes
from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup
from RoomData import Room from RoomData import Room
@@ -598,6 +597,7 @@ class CollectionState(object):
self.path[new_region] = (new_region.name, self.path.get(connection, None)) self.path[new_region] = (new_region.name, self.path.get(connection, None))
# Retry connections if the new region can unblock them # Retry connections if the new region can unblock them
from EntranceShuffle import indirect_connections
if new_region.name in indirect_connections: if new_region.name in indirect_connections:
new_entrance = self.world.get_entrance(indirect_connections[new_region.name], player) new_entrance = self.world.get_entrance(indirect_connections[new_region.name], player)
if new_entrance in bc and new_entrance.parent_region in rrp: if new_entrance in bc and new_entrance.parent_region in rrp:
@@ -1355,6 +1355,8 @@ class CollectionState(object):
def collect(self, item, event=False, location=None): def collect(self, item, event=False, location=None):
if location: if location:
self.locations_checked.add(location) self.locations_checked.add(location)
if not item:
return
changed = False changed = False
if item.name.startswith('Progressive '): if item.name.startswith('Progressive '):
if 'Sword' in item.name: if 'Sword' in item.name:
@@ -2499,6 +2501,7 @@ class Shop(object):
# [id][roomID-low][roomID-high][doorID][zero][shop_config][shopkeeper_config][sram_index] # [id][roomID-low][roomID-high][doorID][zero][shop_config][shopkeeper_config][sram_index]
entrances = self.region.entrances entrances = self.region.entrances
config = self.item_count config = self.item_count
from EntranceShuffle import door_addresses
if len(entrances) == 1 and entrances[0].name in door_addresses: if len(entrances) == 1 and entrances[0].name in door_addresses:
door_id = door_addresses[entrances[0].name][0]+1 door_id = door_addresses[entrances[0].name][0]+1
else: else:
@@ -2764,8 +2767,8 @@ class Spoiler(object):
outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player]) outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player])
if self.metadata['ow_shuffle'][player] != 'vanilla': if self.metadata['ow_shuffle'][player] != 'vanilla':
outfile.write('Keep Similar OW Edges Together:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_keepsimilar'][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('Crossed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_crossed'][player] else 'No')) outfile.write('Crossed OW:'.ljust(line_width) + '%s\n' % self.metadata['ow_crossed'][player])
outfile.write('Mixed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No')) outfile.write('Swapped OW (Mixed):'.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('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('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]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])

View File

@@ -1,5 +1,10 @@
# Changelog # Changelog
### 0.1.9.2
- Fixed spoiler log and mystery for new Crossed/Mixed structure
- Minor preparations and tweaks to ER framework (added global Entrance/Exit pool)
- ~~Merged DR v0.5.1.2 - Blind Prison shuffled outside TT/Keylogic Improvements~~
### 0.1.9.1 ### 0.1.9.1
- Fixed logic issue with leaving IP entrance not requiring flippers - Fixed logic issue with leaving IP entrance not requiring flippers
- ~~Merged DR v0.5.1.1 - Map Indicator Fix/Boss Shuffle Bias/Shop Hints~~ - ~~Merged DR v0.5.1.1 - Map Indicator Fix/Boss Shuffle Bias/Shop Hints~~

View File

@@ -14,7 +14,7 @@ from RoomData import DoorKind, PairedDoor, reset_rooms
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances
from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances
from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException
from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock
from Utils import ncr, kth_combination from Utils import ncr, kth_combination
@@ -1358,10 +1358,8 @@ def combine_layouts(recombinant_builders, dungeon_builders, entrances_map):
if recombine.master_sector is None: if recombine.master_sector is None:
recombine.master_sector = builder.master_sector recombine.master_sector = builder.master_sector
recombine.master_sector.name = recombine.name recombine.master_sector.name = recombine.name
recombine.pre_open_stonewalls = builder.pre_open_stonewalls
else: else:
recombine.master_sector.regions.extend(builder.master_sector.regions) recombine.master_sector.regions.extend(builder.master_sector.regions)
recombine.pre_open_stonewalls.update(builder.pre_open_stonewalls)
recombine.layout_starts = list(entrances_map[recombine.name]) recombine.layout_starts = list(entrances_map[recombine.name])
dungeon_builders[recombine.name] = recombine dungeon_builders[recombine.name] = recombine
@@ -1465,6 +1463,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
start_regions = [x for x in start_regions if x not in excluded.keys()] start_regions = [x for x in start_regions if x not in excluded.keys()]
key_layout = build_key_layout(builder, start_regions, proposal, world, player) key_layout = build_key_layout(builder, start_regions, proposal, world, player)
determine_prize_lock(key_layout, world, player)
while not validate_key_layout(key_layout, world, player): while not validate_key_layout(key_layout, world, player):
itr += 1 itr += 1
stop_early = False stop_early = False
@@ -1582,7 +1581,7 @@ def find_key_door_candidates(region, checked, world, player):
if d2.type == DoorType.Normal: if d2.type == DoorType.Normal:
room_b = world.get_room(d2.roomIndex, player) room_b = world.get_room(d2.roomIndex, player)
pos_b, kind_b = room_b.doorList[d2.doorListPos] pos_b, kind_b = room_b.doorList[d2.doorListPos]
valid = kind in okay_normals and kind_b in okay_normals valid = kind in okay_normals and kind_b in okay_normals and valid_key_door_pair(d, d2)
else: else:
valid = kind in okay_normals valid = kind in okay_normals
if valid and 0 <= d2.doorListPos < 4: if valid and 0 <= d2.doorListPos < 4:
@@ -1599,6 +1598,12 @@ def find_key_door_candidates(region, checked, world, player):
return candidates, checked_doors return candidates, checked_doors
def valid_key_door_pair(door1, door2):
if door1.roomIndex != door2.roomIndex:
return True
return len(door1.entrance.parent_region.exits) <= 1 or len(door2.entrance.parent_region.exits) <= 1
def reassign_key_doors(builder, world, player): def reassign_key_doors(builder, world, player):
logger = logging.getLogger('') logger = logging.getLogger('')
logger.debug('Key doors for %s', builder.name) logger.debug('Key doors for %s', builder.name)
@@ -2042,10 +2047,10 @@ class DROptions(Flag):
Town_Portal = 0x02 # If on, Players will start with mirror scroll Town_Portal = 0x02 # If on, Players will start with mirror scroll
Map_Info = 0x04 Map_Info = 0x04
Debug = 0x08 Debug = 0x08
Rails = 0x10 # If on, draws rails # Rails = 0x10 # Unused bit now
OriginalPalettes = 0x20 OriginalPalettes = 0x20
Open_PoD_Wall = 0x40 # If on, pre opens the PoD wall, no bow required # Open_PoD_Wall = 0x40 # No longer pre-opening pod wall - unused
Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required # Open_Desert_Wall = 0x80 # No longer pre-opening desert wall - unused
Hide_Total = 0x100 Hide_Total = 0x100
DarkWorld_Spawns = 0x200 DarkWorld_Spawns = 0x200

View File

@@ -56,24 +56,6 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player): def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player):
stonewalls = check_for_stonewalls(builder)
sector = generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player)
for stonewall in stonewalls:
if not stonewall_valid(stonewall):
builder.pre_open_stonewalls.add(stonewall)
return sector
def check_for_stonewalls(builder):
stonewalls = set()
for sector in builder.sectors:
for door in sector.outstanding_doors:
if door.stonewall:
stonewalls.add(door)
return stonewalls
def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player):
if builder.valid_proposal: # we made this earlier in gen, just use it if builder.valid_proposal: # we made this earlier in gen, just use it
proposed_map = builder.valid_proposal proposed_map = builder.valid_proposal
else: else:
@@ -112,6 +94,15 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
if (access_region.name in world.inaccessible_regions[player] and if (access_region.name in world.inaccessible_regions[player] and
region.name not in world.enabled_entrances[player]): region.name not in world.enabled_entrances[player]):
excluded[region] = None excluded[region] = None
elif len(region.entrances) == 1: # for holes
access_region = next(x.parent_region for x in region.entrances
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]
or x.parent_region.name == 'Sewer Drop')
if access_region.name == 'Sewer Drop':
access_region = next(x.parent_region for x in access_region.entrances)
if (access_region.name in world.inaccessible_regions[player] and
region.name not in world.enabled_entrances[player]):
excluded[region] = None
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()] entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
doors_to_connect = {} doors_to_connect = {}
all_regions = set() all_regions = set()
@@ -585,7 +576,8 @@ def determine_paths_for_dungeon(world, player, all_regions, name):
paths.append(boss) paths.append(boss)
if 'Thieves Boss' in all_r_names: if 'Thieves Boss' in all_r_names:
paths.append('Thieves Boss') paths.append('Thieves Boss')
paths.append(('Thieves Blind\'s Cell', 'Thieves Boss')) if world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind':
paths.append(('Thieves Blind\'s Cell', 'Thieves Boss'))
for drop_check in drop_path_checks: for drop_check in drop_path_checks:
if drop_check in all_r_names: if drop_check in all_r_names:
paths.append((drop_check, non_hole_portals)) paths.append((drop_check, non_hole_portals))
@@ -611,35 +603,6 @@ def winnow_hangers(hangers, hooks):
hangers[hanger].remove(door) hangers[hanger].remove(door)
def stonewall_valid(stonewall):
bad_door = stonewall.dest
if bad_door.blocked:
return True # great we're done with this one
loop_region = stonewall.entrance.parent_region
start_regions = [bad_door.entrance.parent_region]
if bad_door.dependents:
for dep in bad_door.dependents:
start_regions.append(dep.entrance.parent_region)
queue = deque(start_regions)
visited = set(start_regions)
while len(queue) > 0:
region = queue.popleft()
if region == loop_region:
return False # guaranteed loop
possible_entrances = list(region.entrances)
for entrance in possible_entrances:
parent = entrance.parent_region
if parent.type != RegionType.Dungeon:
return False # you can get stuck from an entrance
else:
door = entrance.door
if (door is None or (door != stonewall and not door.blocked)) and parent not in visited:
visited.add(parent)
queue.append(parent)
# we didn't find anything bad
return True
def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception): def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception):
# todo: info about dungeon events - not sure about that # todo: info about dungeon events - not sure about that
graph_piece = GraphPiece() graph_piece = GraphPiece()
@@ -1197,8 +1160,6 @@ class DungeonBuilder(object):
self.path_entrances = None # used for pathing/key doors, I think self.path_entrances = None # used for pathing/key doors, I think
self.split_flag = False self.split_flag = False
self.pre_open_stonewalls = set() # used by stonewall system
self.candidates = None self.candidates = None
self.total_keys = None self.total_keys = None
self.key_doors_num = None self.key_doors_num = None
@@ -1275,6 +1236,9 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary']: # need to deliver zelda for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary']: # need to deliver zelda
assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, assign_sector(find_sector(r_name, candidate_sectors), current_dungeon,
candidate_sectors, global_pole) candidate_sectors, global_pole)
if key == 'Thieves Town' and world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind':
assign_sector(find_sector("Thieves Blind's Cell", candidate_sectors), current_dungeon,
candidate_sectors, global_pole)
entrances_map, potentials, connections = connections_tuple entrances_map, potentials, connections = connections_tuple
accessible_sectors, reverse_d_map = set(), {} accessible_sectors, reverse_d_map = set(), {}
for key in dungeon_entrances.keys(): for key in dungeon_entrances.keys():
@@ -3898,7 +3862,7 @@ dungeon_boss_sectors = {
'Palace of Darkness': ['PoD Boss'], 'Palace of Darkness': ['PoD Boss'],
'Swamp Palace': ['Swamp Boss'], 'Swamp Palace': ['Swamp Boss'],
'Skull Woods': ['Skull Boss'], 'Skull Woods': ['Skull Boss'],
'Thieves Town': ['Thieves Blind\'s Cell', 'Thieves Boss'], 'Thieves Town': ['Thieves Boss'],
'Ice Palace': ['Ice Boss'], 'Ice Palace': ['Ice Boss'],
'Misery Mire': ['Mire Boss'], 'Misery Mire': ['Mire Boss'],
'Turtle Rock': ['TR Boss'], 'Turtle Rock': ['TR Boss'],

File diff suppressed because it is too large Load Diff

27
Fill.py
View File

@@ -245,7 +245,11 @@ def valid_key_placement(item, location, itempool, world):
return True return True
key_logic = world.key_logic[item.player][dungeon.name] key_logic = world.key_logic[item.player][dungeon.name]
unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name and x.player == item.player]) unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name and x.player == item.player])
return key_logic.check_placement(unplaced_keys, location if item.bigkey else None) prize_loc = None
if key_logic.prize_location:
prize_loc = world.get_location(key_logic.prize_location, location.player)
cr_count = world.crystals_needed_for_gt[location.player]
return key_logic.check_placement(unplaced_keys, location if item.bigkey else None, prize_loc, cr_count)
else: else:
inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player]) inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player])
or (item.bigkey and not world.bigkeyshuffle[item.player])) or (item.bigkey and not world.bigkeyshuffle[item.player]))
@@ -644,7 +648,7 @@ def balance_money_progression(world):
base_value = sum(rupee_rooms.values()) base_value = sum(rupee_rooms.values())
available_money = {player: base_value for player in range(1, world.players+1)} available_money = {player: base_value for player in range(1, world.players+1)}
for loc in world.get_locations(): for loc in world.get_locations():
if loc.item.name in rupee_chart: if loc.item and loc.item.name in rupee_chart:
available_money[loc.item.player] += rupee_chart[loc.item.name] available_money[loc.item.player] += rupee_chart[loc.item.name]
total_price = {player: 0 for player in range(1, world.players+1)} total_price = {player: 0 for player in range(1, world.players+1)}
@@ -709,7 +713,7 @@ def balance_money_progression(world):
slot = shop_to_location_table[location.parent_region.name].index(location.name) slot = shop_to_location_table[location.parent_region.name].index(location.name)
shop = location.parent_region.shop shop = location.parent_region.shop
shop_item = shop.inventory[slot] shop_item = shop.inventory[slot]
if interesting_item(location, location.item, world, location.item.player): if location.item and interesting_item(location, location.item, world, location.item.player):
if location.item.name.startswith('Rupee') and loc_player == location.item.player: if location.item.name.startswith('Rupee') and loc_player == location.item.player:
if shop_item['price'] < rupee_chart[location.item.name]: if shop_item['price'] < rupee_chart[location.item.name]:
wallet[loc_player] -= shop_item['price'] # will get picked up in the location_free block wallet[loc_player] -= shop_item['price'] # will get picked up in the location_free block
@@ -729,14 +733,15 @@ def balance_money_progression(world):
if location_free and location.item: if location_free and location.item:
state.collect(location.item, True, location) state.collect(location.item, True, location)
unchecked_locations.remove(location) unchecked_locations.remove(location)
if location.item.name.startswith('Rupee'): if location.item:
wallet[location.item.player] += rupee_chart[location.item.name] if location.item.name.startswith('Rupee'):
if location.item.name != 'Rupees (300)': wallet[location.item.player] += rupee_chart[location.item.name]
if location.item.name != 'Rupees (300)':
balance_locations[location.item.player].add(location)
if interesting_item(location, location.item, world, location.item.player):
checked_locations.append(location)
elif location.item.name in acceptable_balancers:
balance_locations[location.item.player].add(location) balance_locations[location.item.player].add(location)
if interesting_item(location, location.item, world, location.item.player):
checked_locations.append(location)
elif location.item.name in acceptable_balancers:
balance_locations[location.item.player].add(location)
for room, income in rupee_rooms.items(): for room, income in rupee_rooms.items():
for player in range(1, world.players+1): for player in range(1, world.players+1):
if room not in rooms_visited[player] and world.get_region(room, player) in state.reachable_regions[player]: if room not in rooms_visited[player] and world.get_region(room, player) in state.reachable_regions[player]:
@@ -801,7 +806,7 @@ def balance_money_progression(world):
else: else:
state.collect(location.item, True, location) state.collect(location.item, True, location)
unchecked_locations.remove(location) unchecked_locations.remove(location)
if location.item.name.startswith('Rupee'): if location.item and location.item.name.startswith('Rupee'):
wallet[location.item.player] += rupee_chart[location.item.name] wallet[location.item.player] += rupee_chart[location.item.name]
def set_prize_drops(world, player): def set_prize_drops(world, player):

View File

@@ -4,7 +4,6 @@ import math
import RaceRandom as random import RaceRandom as random
from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState
from Bosses import place_bosses
from Dungeons import get_dungeon_item_pool from Dungeons import get_dungeon_item_pool
from EntranceShuffle import connect_entrance from EntranceShuffle import connect_entrance
from Regions import shop_to_location_table, retro_shops, shop_table_by_location from Regions import shop_to_location_table, retro_shops, shop_table_by_location
@@ -372,7 +371,6 @@ def generate_itempool(world, player):
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
world.required_medallions[player] = (mm_medallion, tr_medallion) world.required_medallions[player] = (mm_medallion, tr_medallion)
place_bosses(world, player)
set_up_shops(world, player) set_up_shops(world, player)
if world.retro[player]: if world.retro[player]:

View File

@@ -26,7 +26,8 @@ class KeyLayout(object):
self.item_locations = set() self.item_locations = set()
self.found_doors = set() self.found_doors = set()
self.prize_relevant = False self.prize_relevant = None
self.prize_can_lock = None # if true, then you may need to beat the bo
# bk special? # bk special?
# bk required? True if big chests or big doors exists # bk required? True if big chests or big doors exists
@@ -37,7 +38,7 @@ class KeyLayout(object):
self.max_chests = calc_max_chests(builder, self, world, player) self.max_chests = calc_max_chests(builder, self, world, player)
self.all_locations = set() self.all_locations = set()
self.item_locations = set() self.item_locations = set()
self.prize_relevant = False self.prize_relevant = None
class KeyLogic(object): class KeyLogic(object):
@@ -58,10 +59,11 @@ class KeyLogic(object):
self.outside_keys = 0 self.outside_keys = 0
self.dungeon = dungeon_name self.dungeon = dungeon_name
self.sm_doors = {} self.sm_doors = {}
self.prize_location = None
def check_placement(self, unplaced_keys, big_key_loc=None): def check_placement(self, unplaced_keys, big_key_loc=None, prize_loc=None, cr_count=7):
for rule in self.placement_rules: for rule in self.placement_rules:
if not rule.is_satisfiable(self.outside_keys, unplaced_keys, big_key_loc): if not rule.is_satisfiable(self.outside_keys, unplaced_keys, big_key_loc, prize_loc, cr_count):
return False return False
if big_key_loc: if big_key_loc:
for rule_a, rule_b in itertools.combinations(self.placement_rules, 2): for rule_a, rule_b in itertools.combinations(self.placement_rules, 2):
@@ -120,6 +122,7 @@ class PlacementRule(object):
self.check_locations_wo_bk = None self.check_locations_wo_bk = None
self.bk_relevant = True self.bk_relevant = True
self.key_reduced = False self.key_reduced = False
self.prize_relevance = None
def contradicts(self, rule, unplaced_keys, big_key_loc): def contradicts(self, rule, unplaced_keys, big_key_loc):
bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False
@@ -154,7 +157,14 @@ class PlacementRule(object):
left -= rule_needed left -= rule_needed
return False return False
def is_satisfiable(self, outside_keys, unplaced_keys, big_key_loc): def is_satisfiable(self, outside_keys, unplaced_keys, big_key_loc, prize_location, cr_count):
if self.prize_relevance and prize_location:
if self.prize_relevance == 'BigBomb':
if prize_location.item.name not in ['Crystal 5', 'Crystal 6']:
return True
elif self.prize_relevance == 'GT':
if 'Crystal' not in prize_location.item.name or cr_count < 7:
return True
bk_blocked = False bk_blocked = False
if self.bk_conditional_set: if self.bk_conditional_set:
for loc in self.bk_conditional_set: for loc in self.bk_conditional_set:
@@ -258,6 +268,7 @@ def analyze_dungeon(key_layout, world, player):
find_bk_locked_sections(key_layout, world, player) find_bk_locked_sections(key_layout, world, player)
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations)) key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations)) key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations))
key_logic.prize_location = dungeon_table[key_layout.sector.name].prize
if world.retro[player] and world.mode[player] != 'standard': if world.retro[player] and world.mode[player] != 'standard':
return return
@@ -284,7 +295,7 @@ def analyze_dungeon(key_layout, world, player):
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations)) key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
# note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop # note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop
# I don't like this exception here or there # I don't like this exception here or there
elif available <= possible_smalls and avail_bigs and non_big_locs > 0: elif available < possible_smalls and avail_bigs and non_big_locs > 0:
max_ctr = find_max_counter(key_layout) max_ctr = find_max_counter(key_layout)
bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations] bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations]
key_logic.bk_restricted.update(filter_big_chest(bk_lockdown)) key_logic.bk_restricted.update(filter_big_chest(bk_lockdown))
@@ -361,6 +372,7 @@ def create_exhaustive_placement_rules(key_layout, world, player):
rule.bk_conditional_set = blocked_loc rule.bk_conditional_set = blocked_loc
rule.needed_keys_wo_bk = min_keys rule.needed_keys_wo_bk = min_keys
rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc)) rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc))
rule.prize_relevance = key_layout.prize_relevant if rule_prize_relevant(key_counter) else None
if valid_rule: if valid_rule:
key_logic.placement_rules.append(rule) key_logic.placement_rules.append(rule)
adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr) adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr)
@@ -368,6 +380,10 @@ def create_exhaustive_placement_rules(key_layout, world, player):
refine_location_rules(key_layout) refine_location_rules(key_layout)
def rule_prize_relevant(key_counter):
return not key_counter.prize_doors_opened and not key_counter.prize_received
def skip_key_counter_due_to_prize(key_layout, key_counter): def skip_key_counter_due_to_prize(key_layout, key_counter):
return key_layout.prize_relevant and key_counter.prize_received and not key_counter.prize_doors_opened return key_layout.prize_relevant and key_counter.prize_received and not key_counter.prize_doors_opened
@@ -467,7 +483,7 @@ def refine_placement_rules(key_layout, max_ctr):
if rule.needed_keys_wo_bk == 0: if rule.needed_keys_wo_bk == 0:
rules_to_remove.append(rule) rules_to_remove.append(rule)
if len(rule.check_locations_wo_bk) < rule.needed_keys_wo_bk or rule.needed_keys_wo_bk > key_layout.max_chests: if len(rule.check_locations_wo_bk) < rule.needed_keys_wo_bk or rule.needed_keys_wo_bk > key_layout.max_chests:
if len(rule.bk_conditional_set) > 0: if not rule.prize_relevance and len(rule.bk_conditional_set) > 0:
key_logic.bk_restricted.update(rule.bk_conditional_set) key_logic.bk_restricted.update(rule.bk_conditional_set)
rules_to_remove.append(rule) rules_to_remove.append(rule)
changed = True # impossible for bk to be here, I think changed = True # impossible for bk to be here, I think
@@ -1380,6 +1396,15 @@ def forced_big_key_avail(locations):
return None return None
def prize_relevance(key_layout, dungeon_entrance):
if len(key_layout.start_regions) > 1 and dungeon_entrance and dungeon_table[key_layout.key_logic.dungeon].prize:
if dungeon_entrance.name in ['Ganons Tower', 'Inverted Ganons Tower']:
return 'GT'
elif dungeon_entrance.name == 'Pyramid Fairy':
return 'BigBomb'
return None
# Soft lock stuff # Soft lock stuff
def validate_key_layout(key_layout, world, player): def validate_key_layout(key_layout, world, player):
# retro is all good - except for hyrule castle in standard mode # retro is all good - except for hyrule castle in standard mode
@@ -1391,12 +1416,11 @@ def validate_key_layout(key_layout, world, player):
state.big_key_special = check_bk_special(key_layout.sector.regions, world, player) state.big_key_special = check_bk_special(key_layout.sector.regions, world, player)
for region in key_layout.start_regions: for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region) dungeon_entrance, portal_door = find_outside_connection(region)
if (len(key_layout.start_regions) > 1 and dungeon_entrance and prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance)
dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy'] if prize_relevant_flag:
and dungeon_table[key_layout.key_logic.dungeon].prize):
state.append_door_to_list(portal_door, state.prize_doors) state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = True key_layout.prize_relevant = prize_relevant_flag
else: else:
state.visit_region(region, key_checks=True) state.visit_region(region, key_checks=True)
state.add_all_doors_check_keys(region, flat_proposal, world, player) state.add_all_doors_check_keys(region, flat_proposal, world, player)
@@ -1424,7 +1448,11 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
found_forced_bk = state.found_forced_bk() found_forced_bk = state.found_forced_bk()
smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations) smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations)
bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk) bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk)
if smalls_done and bk_done: # prize door should not be opened if the boss is reachable - but not reached yet
allow_for_prize_lock = (key_layout.prize_can_lock and
not any(x for x in state.found_locations if '- Prize' in x.name))
prize_done = not key_layout.prize_relevant or state.prize_doors_opened or allow_for_prize_lock
if smalls_done and bk_done and prize_done:
return False return False
else: else:
# todo: pretty sure you should OR these paths together, maybe when there's one location and it can # todo: pretty sure you should OR these paths together, maybe when there's one location and it can
@@ -1460,6 +1488,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
if not valid: if not valid:
return False return False
# todo: feel like you only open these if the boss is available??? # todo: feel like you only open these if the boss is available???
# todo: or if a crystal isn't valid placement on this boss
if not state.prize_doors_opened and key_layout.prize_relevant: if not state.prize_doors_opened and key_layout.prize_relevant:
state_copy = state.copy() state_copy = state.copy()
open_a_door(next(iter(state_copy.prize_door_set)), state_copy, flat_proposal, world, player) open_a_door(next(iter(state_copy.prize_door_set)), state_copy, flat_proposal, world, player)
@@ -1506,6 +1535,39 @@ def enough_small_locations(state, avail_small_loc):
return avail_small_loc >= len(unique_d_set) return avail_small_loc >= len(unique_d_set)
def determine_prize_lock(key_layout, world, player):
if ((world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle'))
or world.logic[player] == 'nologic'):
return # done, doesn't matter what
flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name)
state.key_locations = key_layout.max_chests
state.big_key_special = check_bk_special(key_layout.sector.regions, world, player)
prize_lock_possible = False
for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region)
prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance)
if prize_relevant_flag:
state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = prize_relevant_flag
prize_lock_possible = True
else:
state.visit_region(region, key_checks=True)
state.add_all_doors_check_keys(region, flat_proposal, world, player)
if not prize_lock_possible:
return # done, no prize entrances to worry about
expand_key_state(state, flat_proposal, world, player)
while len(state.small_doors) > 0 or len(state.big_doors) > 0:
if len(state.big_doors) > 0:
open_a_door(state.big_doors[0].door, state, flat_proposal, world, player)
elif len(state.small_doors) > 0:
open_a_door(state.small_doors[0].door, state, flat_proposal, world, player)
expand_key_state(state, flat_proposal, world, player)
if any(x for x in state.found_locations if '- Prize' in x.name):
key_layout.prize_can_lock = True
def cnt_avail_small_locations(free_locations, key_only, state, world, player): def cnt_avail_small_locations(free_locations, key_only, state, world, player):
if not world.keyshuffle[player] and not world.retro[player]: if not world.keyshuffle[player] and not world.retro[player]:
bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0 bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0
@@ -1554,12 +1616,11 @@ def create_key_counters(key_layout, world, player):
state.big_key_special = True state.big_key_special = True
for region in key_layout.start_regions: for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region) dungeon_entrance, portal_door = find_outside_connection(region)
if (len(key_layout.start_regions) > 1 and dungeon_entrance and prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance)
dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy'] if prize_relevant_flag:
and dungeon_table[key_layout.key_logic.dungeon].prize):
state.append_door_to_list(portal_door, state.prize_doors) state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = True key_layout.prize_relevant = prize_relevant_flag
else: else:
state.visit_region(region, key_checks=True) state.visit_region(region, key_checks=True)
state.add_all_doors_check_keys(region, flat_proposal, world, player) state.add_all_doors_check_keys(region, flat_proposal, world, player)
@@ -1988,8 +2049,10 @@ def validate_key_placement(key_layout, world, player):
found_prize = any(x for x in counter.important_locations if '- Prize' in x.name) found_prize = any(x for x in counter.important_locations if '- Prize' in x.name)
if not found_prize and dungeon_table[key_layout.sector.name].prize: if not found_prize and dungeon_table[key_layout.sector.name].prize:
prize_loc = world.get_location(dungeon_table[key_layout.sector.name].prize, player) prize_loc = world.get_location(dungeon_table[key_layout.sector.name].prize, player)
# todo: pyramid fairy only care about crystals 5 & 6 if key_layout.prize_relevant == 'BigBomb':
found_prize = 'Crystal' not in prize_loc.item.name found_prize = prize_loc.item.name not in ['Crystal 5', 'Crystal 6']
elif key_layout.prize_relevant == 'GT':
found_prize = 'Crystal' not in prize_loc.item.name or world.crystals_needed_for_gt[player] < 7
else: else:
found_prize = False found_prize = False
can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \ can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \

View File

@@ -10,6 +10,7 @@ import time
import zlib import zlib
from BaseClasses import World, CollectionState, Item, Region, Location, Shop, Entrance, Settings from BaseClasses import World, CollectionState, Item, Region, Location, Shop, Entrance, Settings
from Bosses import place_bosses
from Items import ItemFactory from Items import ItemFactory
from KeyDoorShuffle import validate_key_placement from KeyDoorShuffle import validate_key_placement
from OverworldGlitchRules import create_owg_connections from OverworldGlitchRules import create_owg_connections
@@ -29,7 +30,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
from Utils import output_path, parse_player_names from Utils import output_path, parse_player_names
__version__ = '0.5.1.1-u' __version__ = '0.5.1.2-u'
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish
@@ -151,6 +152,7 @@ def main(args, seed=None, fish=None):
create_rooms(world, player) create_rooms(world, player)
create_dungeons(world, player) create_dungeons(world, player)
adjust_locations(world, player) adjust_locations(world, player)
place_bosses(world, player)
if any(world.potshuffle.values()): if any(world.potshuffle.values()):
logger.info(world.fish.translate("cli", "cli", "shuffling.pots")) logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
@@ -495,10 +497,6 @@ def copy_world(world):
ret.state.stale = {player: True for player in range(1, world.players + 1)} ret.state.stale = {player: True for player in range(1, world.players + 1)}
ret.owedges = world.owedges ret.owedges = world.owedges
for edge in ret.owedges:
transition = ret.check_for_owedge(edge.name, edge.player)
if transition is not None:
transition.dest = edge
ret.doors = world.doors ret.doors = world.doors
for door in ret.doors: for door in ret.doors:
entrance = ret.check_for_entrance(door.name, door.player) entrance = ret.check_for_entrance(door.name, door.player)

View File

@@ -137,7 +137,7 @@ def roll_settings(weights):
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla' ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
ret.ow_crossed = get_choice('overworld_crossed') ret.ow_crossed = get_choice('overworld_crossed')
ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on' ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on'
ret.ow_mixed = get_choice('overworld_mixed') == 'on' ret.ow_mixed = get_choice('overworld_swap') == 'on'
overworld_flute = get_choice('flute_shuffle') overworld_flute = get_choice('flute_shuffle')
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla' ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
entrance_shuffle = get_choice('entrance_shuffle') entrance_shuffle = get_choice('entrance_shuffle')

View File

@@ -2,7 +2,7 @@ import RaceRandom as random, logging, copy
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
__version__ = '0.1.9.1-u' __version__ = '0.1.9.2-u'
def link_overworld(world, player): def link_overworld(world, player):
# setup mandatory connections # setup mandatory connections

View File

@@ -15,6 +15,14 @@ CLI: ```--bombbag```
# Bug Fixes and Notes. # Bug Fixes and Notes.
* 0.5.1.2
* Allowed Blind's Cell to be shuffled anywhere if Blind is not the boss of Thieves Town
* Remove unique annotation from a FastEnum that was causing problems
* Updated prevent mixed_travel setting to prevent more mixed travel
* Prevent key door loops on the same supertile where you could have spent 2 keys on one logical door
* Promoted dynamic soft-lock prevention on "stonewalls" from experimental to be the primary prevention (Stonewalls are now never pre-opened)
* Fix to money balancing algorithm with small item_pool, thanks Catobat
* Many fixes and refinements to key logic and generation
* 0.5.1.1 * 0.5.1.1
* Shop hints in ER are now more generic instead of using "near X" because they aren't near that anymore * Shop hints in ER are now more generic instead of using "near X" because they aren't near that anymore
* Added memory location for mutliworld scripts to read what item was just obtain (longer than one frame) * Added memory location for mutliworld scripts to read what item was just obtain (longer than one frame)

23
Rom.py
View File

@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '9e8b765fca0f00b54e5be78bb6eb62a3' RANDOMIZERBASEHASH = 'c81153d8bb571e41fe472d36274f47b3'
class JsonRom(object): class JsonRom(object):
@@ -754,7 +754,19 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
dr_flags |= DROptions.Debug dr_flags |= DROptions.Debug
if world.doorShuffle[player] == 'crossed' and world.logic[player] != 'nologic'\ if world.doorShuffle[player] == 'crossed' and world.logic[player] != 'nologic'\
and world.mixed_travel[player] == 'prevent': and world.mixed_travel[player] == 'prevent':
dr_flags |= DROptions.Rails # PoD Falling Bridge or Hammjump
# 1FA607: db $2D, $79, $69 ; 0x0069: Vertical Rail ↕ | { 0B, 1E } | Size: 05
# 1FA60A: db $14, $99, $5D ; 0x005D: Large Horizontal Rail ↔ | { 05, 26 } | Size: 01
rom.write_bytes(0xfa607, [0x2d, 0x79, 0x69, 0x14, 0x99, 0x5d])
# PoD Arena
# 1FA573: db $D4, $B2, $22 ; 0x0022: Horizontal Rail ↔ | { 35, 2C } | Size: 02
# 1FA576: db $D4, $CE, $22 ; 0x0022: Horizontal Rail ↔ | { 35, 33 } | Size: 01
# 1FA579: db $D9, $AE, $69 ; 0x0069: Vertical Rail ↕ | { 36, 2B } | Size: 06
rom.write_bytes(0xfa573, [0xd4, 0xb2, 0x22, 0xd4, 0xce, 0x22, 0xd9, 0xae, 0x69])
# Mire BK Pond
# 1FB1FC: db $C8, $9D, $69 ; 0x0069: Vertical Rail ↕ | { 32, 27 } | Size: 01
# 1FB1FF: db $B4, $AC, $5D ; 0x005D: Large Horizontal Rail ↔ | { 2D, 2B } | Size: 00
rom.write_bytes(0xfb1fc, [0xc8, 0x9d, 0x69, 0xb4, 0xac, 0x5d])
if world.standardize_palettes[player] == 'original': if world.standardize_palettes[player] == 'original':
dr_flags |= DROptions.OriginalPalettes dr_flags |= DROptions.OriginalPalettes
if world.experimental[player]: if world.experimental[player]:
@@ -825,13 +837,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(paired_door.address_a(world, player), paired_door.rom_data_a(world, player)) rom.write_bytes(paired_door.address_a(world, player), paired_door.rom_data_a(world, player))
rom.write_bytes(paired_door.address_b(world, player), paired_door.rom_data_b(world, player)) rom.write_bytes(paired_door.address_b(world, player), paired_door.rom_data_b(world, player))
if world.doorShuffle[player] != 'vanilla': if world.doorShuffle[player] != 'vanilla':
if not world.experimental[player]:
for builder in world.dungeon_layouts[player].values():
for stonewall in builder.pre_open_stonewalls:
if stonewall.name == 'Desert Wall Slide NW':
dr_flags |= DROptions.Open_Desert_Wall
elif stonewall.name == 'PoD Bow Statue Down Ladder':
dr_flags |= DROptions.Open_PoD_Wall
for name, pair in boss_indicator.items(): for name, pair in boss_indicator.items():
dungeon_id, boss_door = pair dungeon_id, boss_door = pair
opposite_door = world.get_door(boss_door, player).dest opposite_door = world.get_door(boss_door, player).dest

459
Rules.py
View File

@@ -1629,239 +1629,240 @@ def find_rules_for_zelda_delivery(world, player):
def set_big_bomb_rules(world, player): def set_big_bomb_rules(world, player):
# this is a mess # this is a mess
bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0] if len(world.get_region('Big Bomb Shop', player).entrances) > 0:
Normal_LW_entrances = ['Blinds Hideout', bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0]
'Bonk Fairy (Light)', Normal_LW_entrances = ['Blinds Hideout',
'Lake Hylia Fairy', 'Bonk Fairy (Light)',
'Light Hype Fairy', 'Lake Hylia Fairy',
'Desert Fairy', 'Light Hype Fairy',
'Chicken House', 'Desert Fairy',
'Aginahs Cave', 'Chicken House',
'Sahasrahlas Hut', 'Aginahs Cave',
'Cave Shop (Lake Hylia)', 'Sahasrahlas Hut',
'Blacksmiths Hut', 'Cave Shop (Lake Hylia)',
'Sick Kids House', 'Blacksmiths Hut',
'Lost Woods Gamble', 'Sick Kids House',
'Fortune Teller (Light)', 'Lost Woods Gamble',
'Snitch Lady (East)', 'Fortune Teller (Light)',
'Snitch Lady (West)', 'Snitch Lady (East)',
'Bush Covered House', 'Snitch Lady (West)',
'Tavern (Front)', 'Bush Covered House',
'Light World Bomb Hut', 'Tavern (Front)',
'Kakariko Shop', 'Light World Bomb Hut',
'Mini Moldorm Cave', 'Kakariko Shop',
'Long Fairy Cave', 'Mini Moldorm Cave',
'Good Bee Cave', 'Long Fairy Cave',
'20 Rupee Cave', 'Good Bee Cave',
'50 Rupee Cave', '20 Rupee Cave',
'Ice Rod Cave', '50 Rupee Cave',
'Bonk Rock Cave', 'Ice Rod Cave',
'Library', 'Bonk Rock Cave',
'Potion Shop', 'Library',
'Dam', 'Potion Shop',
'Lumberjack House', 'Dam',
'Lake Hylia Fortune Teller', 'Lumberjack House',
'Eastern Palace', 'Lake Hylia Fortune Teller',
'Kakariko Gamble Game', 'Eastern Palace',
'Kakariko Well Cave', 'Kakariko Gamble Game',
'Bat Cave Cave', 'Kakariko Well Cave',
'Elder House (East)', 'Bat Cave Cave',
'Elder House (West)', 'Elder House (East)',
'North Fairy Cave', 'Elder House (West)',
'Lost Woods Hideout Stump', 'North Fairy Cave',
'Lumberjack Tree Cave', 'Lost Woods Hideout Stump',
'Two Brothers House (East)', 'Lumberjack Tree Cave',
'Sanctuary', 'Two Brothers House (East)',
'Hyrule Castle Entrance (South)', 'Sanctuary',
'Hyrule Castle Secret Entrance Stairs'] 'Hyrule Castle Entrance (South)',
LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy', 'Hyrule Castle Secret Entrance Stairs']
'Dark Lake Hylia Ledge Spike Cave', LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy',
'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave',
'Mire Shed', 'Dark Lake Hylia Ledge Hint',
'Dark Desert Hint', 'Mire Shed',
'Dark Desert Fairy', 'Dark Desert Hint',
'Misery Mire'] 'Dark Desert Fairy',
Northern_DW_entrances = ['Brewery', 'Misery Mire']
'C-Shaped House', Northern_DW_entrances = ['Brewery',
'Chest Game', 'C-Shaped House',
'Dark World Hammer Peg Cave', 'Chest Game',
'Red Shield Shop', 'Dark World Hammer Peg Cave',
'Dark Sanctuary Hint', 'Red Shield Shop',
'Fortune Teller (Dark)', 'Dark Sanctuary Hint',
'Dark World Shop', 'Fortune Teller (Dark)',
'Dark World Lumberjack Shop', 'Dark World Shop',
'Thieves Town', 'Dark World Lumberjack Shop',
'Skull Woods First Section Door', 'Thieves Town',
'Skull Woods Second Section Door (East)'] 'Skull Woods First Section Door',
Southern_DW_entrances = ['Hype Cave', 'Skull Woods Second Section Door (East)']
'Bonk Fairy (Dark)', Southern_DW_entrances = ['Hype Cave',
'Archery Game', 'Bonk Fairy (Dark)',
'Big Bomb Shop', 'Archery Game',
'Dark Lake Hylia Shop', 'Big Bomb Shop',
'Swamp Palace'] 'Dark Lake Hylia Shop',
Isolated_DW_entrances = ['Spike Cave', 'Swamp Palace']
'Cave Shop (Dark Death Mountain)', Isolated_DW_entrances = ['Spike Cave',
'Dark Death Mountain Fairy', 'Cave Shop (Dark Death Mountain)',
'Mimic Cave', 'Dark Death Mountain Fairy',
'Skull Woods Second Section Door (West)', 'Mimic Cave',
'Skull Woods Final Section', 'Skull Woods Second Section Door (West)',
'Ice Palace', 'Skull Woods Final Section',
'Turtle Rock', 'Ice Palace',
'Dark Death Mountain Ledge (West)', 'Turtle Rock',
'Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)',
'Bumper Cave (Top)', 'Dark Death Mountain Ledge (East)',
'Superbunny Cave (Top)', 'Bumper Cave (Top)',
'Superbunny Cave (Bottom)', 'Superbunny Cave (Top)',
'Hookshot Cave', 'Superbunny Cave (Bottom)',
'Ganons Tower', 'Hookshot Cave',
'Turtle Rock Isolated Ledge Entrance', 'Ganons Tower',
'Hookshot Cave Back Entrance'] 'Turtle Rock Isolated Ledge Entrance',
Isolated_LW_entrances = ['Capacity Upgrade', 'Hookshot Cave Back Entrance']
'Tower of Hera', Isolated_LW_entrances = ['Capacity Upgrade',
'Death Mountain Return Cave (West)', 'Tower of Hera',
'Paradox Cave (Top)', 'Death Mountain Return Cave (West)',
'Fairy Ascension Cave (Top)', 'Paradox Cave (Top)',
'Spiral Cave', 'Fairy Ascension Cave (Top)',
'Desert Palace Entrance (East)'] 'Spiral Cave',
West_LW_DM_entrances = ['Old Man Cave (East)', 'Desert Palace Entrance (East)']
'Old Man House (Bottom)', West_LW_DM_entrances = ['Old Man Cave (East)',
'Old Man House (Top)', 'Old Man House (Bottom)',
'Death Mountain Return Cave (East)', 'Old Man House (Top)',
'Spectacle Rock Cave Peak', 'Death Mountain Return Cave (East)',
'Spectacle Rock Cave', 'Spectacle Rock Cave Peak',
'Spectacle Rock Cave (Bottom)'] 'Spectacle Rock Cave',
East_LW_DM_entrances = ['Paradox Cave (Bottom)', 'Spectacle Rock Cave (Bottom)']
'Paradox Cave (Middle)', East_LW_DM_entrances = ['Paradox Cave (Bottom)',
'Hookshot Fairy', 'Paradox Cave (Middle)',
'Spiral Cave (Bottom)'] 'Hookshot Fairy',
Mirror_from_SDW_entrances = ['Two Brothers House (West)', 'Spiral Cave (Bottom)']
'Cave 45'] Mirror_from_SDW_entrances = ['Two Brothers House (West)',
Castle_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Cave 45']
'Hyrule Castle Entrance (East)', Castle_ledge_entrances = ['Hyrule Castle Entrance (West)',
'Agahnims Tower'] 'Hyrule Castle Entrance (East)',
Desert_mirrorable_ledge_entrances = ['Desert Palace Entrance (West)', 'Agahnims Tower']
'Desert Palace Entrance (North)', Desert_mirrorable_ledge_entrances = ['Desert Palace Entrance (West)',
'Desert Palace Entrance (South)', 'Desert Palace Entrance (North)',
'Checkerboard Cave'] 'Desert Palace Entrance (South)',
'Checkerboard Cave']
set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_reach('Pyramid Area', 'Region', player) or state.can_reach('East Dark World', 'Region', player)) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_reach('Pyramid Area', 'Region', player) or state.can_reach('East Dark World', 'Region', player)) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player))
#crossing peg bridge starting from the southern dark world #crossing peg bridge starting from the southern dark world
def cross_peg_bridge(state): def cross_peg_bridge(state):
return state.has('Hammer', player) and state.has_Pearl(player) return state.has('Hammer', player) and state.has_Pearl(player)
# returning via the eastern and southern teleporters needs the same items, so we use the southern teleporter for out routing. # returning via the eastern and southern teleporters needs the same items, so we use the southern teleporter for out routing.
# crossing preg bridge already requires hammer so we just add the gloves to the requirement # crossing preg bridge already requires hammer so we just add the gloves to the requirement
def southern_teleporter(state): def southern_teleporter(state):
return state.can_lift_rocks(player) and cross_peg_bridge(state) return state.can_lift_rocks(player) and cross_peg_bridge(state)
# the basic routes assume you can reach eastern light world with the bomb. # the basic routes assume you can reach eastern light world with the bomb.
# you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp # you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp
def basic_routes(state): def basic_routes(state):
return southern_teleporter(state) or state.has('Beat Agahnim 1', player) return southern_teleporter(state) or state.has('Beat Agahnim 1', player)
# Key for below abbreviations: # Key for below abbreviations:
# P = pearl # P = pearl
# A = Aga1 # A = Aga1
# H = hammer # H = hammer
# M = Mirror # M = Mirror
# G = Glove # G = Glove
if bombshop_entrance.name in Normal_LW_entrances: if bombshop_entrance.name in Normal_LW_entrances:
#1. basic routes #1. basic routes
#2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror #2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror
# -> M or BR # -> M or BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: basic_routes(state) or state.has_Mirror(player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: basic_routes(state) or state.has_Mirror(player))
elif bombshop_entrance.name in LW_walkable_entrances: elif bombshop_entrance.name in LW_walkable_entrances:
#1. Mirror then basic routes #1. Mirror then basic routes
# -> M and BR # -> M and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and basic_routes(state))
elif bombshop_entrance.name in Northern_DW_entrances: elif bombshop_entrance.name in Northern_DW_entrances:
#1. Mirror and basic routes #1. Mirror and basic routes
#2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl #2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl
# -> (Mitts and CPB) or (M and BR) # -> (Mitts and CPB) or (M and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and basic_routes(state)))
elif bombshop_entrance.name == 'Bumper Cave (Bottom)': elif bombshop_entrance.name == 'Bumper Cave (Bottom)':
#1. Mirror and Lift rock and basic_routes #1. Mirror and Lift rock and basic_routes
#2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) #2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case)
#3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl
# -> (Mitts and CPB) or (((G or Flute) and M) and BR)) # -> (Mitts and CPB) or (((G or Flute) and M) and BR))
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.can_flute(player)) and state.has_Mirror(player)) and basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.can_flute(player)) and state.has_Mirror(player)) and basic_routes(state)))
elif bombshop_entrance.name in Southern_DW_entrances: elif bombshop_entrance.name in Southern_DW_entrances:
#1. Mirror and enter via gate: Need mirror and Aga1 #1. Mirror and enter via gate: Need mirror and Aga1
#2. cross peg bridge: Need hammer and moon pearl #2. cross peg bridge: Need hammer and moon pearl
# -> CPB or (M and A) # -> CPB or (M and A)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player)))
elif bombshop_entrance.name in Isolated_DW_entrances: elif bombshop_entrance.name in Isolated_DW_entrances:
# 1. mirror then flute then basic routes # 1. mirror then flute then basic routes
# -> M and Flute and BR # -> M and Flute and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.can_flute(player) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.can_flute(player) and basic_routes(state))
elif bombshop_entrance.name in Isolated_LW_entrances: elif bombshop_entrance.name in Isolated_LW_entrances:
# 1. flute then basic routes # 1. flute then basic routes
# Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations. # Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations.
# -> Flute and BR # -> Flute and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and basic_routes(state))
elif bombshop_entrance.name in West_LW_DM_entrances: elif bombshop_entrance.name in West_LW_DM_entrances:
# 1. flute then basic routes or mirror # 1. flute then basic routes or mirror
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly. # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly.
# -> Flute and (M or BR) # -> Flute and (M or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('West Death Mountain (Bottom)', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('West Death Mountain (Bottom)', 'Region', player))
elif bombshop_entrance.name in East_LW_DM_entrances: elif bombshop_entrance.name in East_LW_DM_entrances:
# 1. flute then basic routes or mirror and hookshot # 1. flute then basic routes or mirror and hookshot
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot
# -> Flute and ((M and Hookshot) or BR) # -> Flute and ((M and Hookshot) or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Death Mountain (Bottom)', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Death Mountain (Bottom)', 'Region', player))
elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)': elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)':
# Same as East_LW_DM_entrances except navigation without BR requires Mitts # Same as East_LW_DM_entrances except navigation without BR requires Mitts
# -> Flute and ((M and Hookshot and Mitts) or BR) # -> Flute and ((M and Hookshot and Mitts) or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Death Mountain (Bottom)', 'Region', player) and state.can_lift_heavy_rocks(player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Death Mountain (Bottom)', 'Region', player) and state.can_lift_heavy_rocks(player))
elif bombshop_entrance.name in Castle_ledge_entrances: elif bombshop_entrance.name in Castle_ledge_entrances:
# 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror # 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror
# 2. flute then basic routes # 2. flute then basic routes
# -> M or (Flute and BR) # -> M or (Flute and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Hyrule Castle Ledge', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Hyrule Castle Ledge', 'Region', player))
elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances: elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances:
# Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and: # Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and:
# 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes # 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes
# 2. flute then basic routes # 2. flute then basic routes
# -> (Mire access and M) or Flute) and BR # -> (Mire access and M) or Flute) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Desert Ledge', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Desert Ledge', 'Region', player))
elif bombshop_entrance.name == 'Old Man Cave (West)': elif bombshop_entrance.name == 'Old Man Cave (West)':
# 1. Lift rock then basic_routes # 1. Lift rock then basic_routes
# 2. flute then basic_routes # 2. flute then basic_routes
# -> (Flute or G) and BR # -> (Flute or G) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Death Mountain Entrance', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Death Mountain Entrance', 'Region', player))
elif bombshop_entrance.name == 'Graveyard Cave': elif bombshop_entrance.name == 'Graveyard Cave':
# 1. flute then basic routes # 1. flute then basic routes
# 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge
# -> (Flute or (M and P and West Dark World access) and BR # -> (Flute or (M and P and West Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Dark Graveyard Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Dark Graveyard Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state))
elif bombshop_entrance.name in Mirror_from_SDW_entrances: elif bombshop_entrance.name in Mirror_from_SDW_entrances:
# 1. flute then basic routes # 1. flute then basic routes
# 2. (has South dark world access) use existing mirror spot, mirror again off ledge # 2. (has South dark world access) use existing mirror spot, mirror again off ledge
# -> (Flute or (M and South Dark World access) and BR # -> (Flute or (M and South Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Pyramid Area', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Pyramid Area', 'Region', player) and state.has_Mirror(player))) and basic_routes(state))
elif bombshop_entrance.name == 'Dark World Potion Shop': elif bombshop_entrance.name == 'Dark World Potion Shop':
# 1. walk down by lifting rock: needs gloves and pearl` # 1. walk down by lifting rock: needs gloves and pearl`
# 2. walk down by hammering peg: needs hammer and pearl # 2. walk down by hammering peg: needs hammer and pearl
# 3. mirror and basic routes # 3. mirror and basic routes
# -> (P and (H or Gloves)) or (M and BR) # -> (P and (H or Gloves)) or (M and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Dark Witch Area', 'Region', player)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Dark Witch Area', 'Region', player))
elif bombshop_entrance.name == 'Kings Grave': elif bombshop_entrance.name == 'Kings Grave':
# same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts # same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts
# (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot) # (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot)
# to account for insanity, must consider a way to escape without a cave for basic_routes # to account for insanity, must consider a way to escape without a cave for basic_routes
# -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR) # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) or state.can_flute(player) or (state.can_reach('Dark Graveyard Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player)))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) or state.can_flute(player) or (state.can_reach('Dark Graveyard Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))))
elif bombshop_entrance.name == 'Waterfall of Wishing': elif bombshop_entrance.name == 'Waterfall of Wishing':
# same as the Normal_LW_entrances case except in insanity it's possible you could be here without Flippers which # same as the Normal_LW_entrances case except in insanity it's possible you could be here without Flippers which
# means you need an escape route of either Flippers or Flute # means you need an escape route of either Flippers or Flute
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.can_flute(player))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.can_flute(player)))
#TODO: Fix red bomb rules, artifically adding a bunch of rules to help reduce unbeatable seeds in OW shuffle #TODO: Fix red bomb rules, artifically adding a bunch of rules to help reduce unbeatable seeds in OW shuffle
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False)
#add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Pyramid Area', 'Region', player)) #add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Pyramid Area', 'Region', player))
#add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has('Flippers', player) and state.can_flute(player) and state.has('Hammer', player) and state.has('Hookshot', player) and state.has_Pearl(player) and state.has_Mirror(player))) #add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has('Flippers', player) and state.can_flute(player) and state.has('Hammer', player) and state.has('Hookshot', player) and state.has_Pearl(player) and state.has_Mirror(player)))
def set_inverted_big_bomb_rules(world, player): def set_inverted_big_bomb_rules(world, player):
bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0] bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0]
@@ -2344,7 +2345,7 @@ def add_key_logic_rules(world, player):
big_chest = world.get_location(chest.name, player) big_chest = world.get_location(chest.name, player)
add_rule(big_chest, create_rule(d_logic.bk_name, player)) add_rule(big_chest, create_rule(d_logic.bk_name, player))
if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1: if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1:
set_always_allow(big_chest, lambda state, item: item.name == d_logic.bk_name and item.player == player) set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player))
if world.retro[player]: if world.retro[player]:
for d_name, layout in world.key_layout[player].items(): for d_name, layout in world.key_layout[player].items():
for door in layout.flat_prop: for door in layout.flat_prop:
@@ -2382,6 +2383,10 @@ def eval_small_key_door(door_name, dungeon, player):
return lambda state: eval_small_key_door_main(state, door_name, dungeon, player) return lambda state: eval_small_key_door_main(state, door_name, dungeon, player)
def allow_big_key_in_big_chest(bk_name, player):
return lambda state, item: item.name == bk_name and item.player == player
def retro_in_hc(spot): def retro_in_hc(spot):
return spot.parent_region.dungeon.name == 'Hyrule Castle' if spot.parent_region.dungeon else False return spot.parent_region.dungeon.name == 'Hyrule Castle' if spot.parent_region.dungeon else False

View File

@@ -35,11 +35,7 @@ rtl
OnFileLoadOverride: OnFileLoadOverride:
jsl OnFileLoad ; what I wrote over jsl OnFileLoad ; what I wrote over
lda.l DRFlags : and #$80 : beq + ;flag is off + lda.l DRFlags : and #$02 : beq + ; Mirror Scroll
lda $7ef086 : ora #$80 : sta $7ef086
+ lda.l DRFlags : and #$40 : beq + ;flag is off
lda $7ef036 : ora #$80 : sta $7ef036
+ lda.l DRFlags : and #$02 : beq +
lda $7ef353 : bne + lda $7ef353 : bne +
lda #$01 : sta $7ef353 lda #$01 : sta $7ef353
+ rtl + rtl

Binary file not shown.

View File

@@ -4,14 +4,18 @@
parallel: 2 parallel: 2
full: 2 full: 2
overworld_crossed: overworld_crossed:
on: 1 none: 1
off: 1 polar: 1
grouped: 1
limited: 1
chaos: 1
overworld_keepsimilar: overworld_keepsimilar:
on: 1 on: 1
off: 1 off: 1
overworld_mixed: overworld_swap:
on: 1 vanilla: 1
off: 1 mixed: 2
crossed: 2
flute_shuffle: flute_shuffle:
vanilla: 0 vanilla: 0
balanced: 1 balanced: 1