Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -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])
|
||||||
|
|||||||
@@ -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~~
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
27
Fill.py
@@ -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):
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
8
Main.py
8
Main.py
@@ -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)
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
23
Rom.py
@@ -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
459
Rules.py
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user