Merge remote-tracking branch 'remotes/door_rando/DoorDev' into Dev
This commit is contained in:
+67
-5
@@ -2,7 +2,9 @@ import copy
|
||||
from enum import Enum, unique
|
||||
import logging
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from collections import OrderedDict, deque
|
||||
|
||||
from EntranceShuffle import door_addresses
|
||||
from _vendor.collections_extended import bag
|
||||
from EntranceShuffle import door_addresses
|
||||
from Utils import int16_as_bytes
|
||||
@@ -373,6 +375,7 @@ class CollectionState(object):
|
||||
self.prog_items = bag()
|
||||
self.world = parent
|
||||
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
|
||||
self.colored_regions = {player: {} for player in range(1, parent.players + 1)}
|
||||
self.events = []
|
||||
self.path = {}
|
||||
self.locations_checked = set()
|
||||
@@ -384,6 +387,7 @@ class CollectionState(object):
|
||||
player_regions = self.world.get_regions(player)
|
||||
self.stale[player] = False
|
||||
rrp = self.reachable_regions[player]
|
||||
ccr = self.colored_regions[player]
|
||||
new_regions = True
|
||||
reachable_regions_count = len(rrp)
|
||||
while new_regions:
|
||||
@@ -391,13 +395,53 @@ class CollectionState(object):
|
||||
for candidate in player_regions:
|
||||
if candidate.can_reach_private(self):
|
||||
rrp.add(candidate)
|
||||
if candidate.type == RegionType.Dungeon:
|
||||
c_switch_present = False
|
||||
for ext in candidate.exits:
|
||||
door = self.world.check_for_door(ext.name, player)
|
||||
if door is not None and door.crystal == CrystalBarrier.Either:
|
||||
c_switch_present = True
|
||||
if c_switch_present:
|
||||
ccr[candidate] = CrystalBarrier.Either
|
||||
self.spread_crystal_access(candidate, CrystalBarrier.Either, rrp, ccr, player)
|
||||
else:
|
||||
for entrance in candidate.entrances:
|
||||
door = self.world.check_for_door(entrance.name, player)
|
||||
if door is None or entrance.parent_region.type != RegionType.Dungeon:
|
||||
ccr[candidate] = CrystalBarrier.Orange
|
||||
if entrance.parent_region in ccr.keys():
|
||||
color_type = ccr[entrance.parent_region]
|
||||
current_type = ccr[candidate] if candidate in ccr.keys() else None
|
||||
ccr[candidate] = color_type if current_type is None or color_type == current_type else CrystalBarrier.Either
|
||||
new_regions = len(rrp) > reachable_regions_count
|
||||
reachable_regions_count = len(rrp)
|
||||
|
||||
def spread_crystal_access(self, region, crystal, rrp, ccr, player):
|
||||
queue = deque([(region, crystal)])
|
||||
visited = set()
|
||||
while len(queue) > 0:
|
||||
region, crystal = queue.popleft()
|
||||
visited.add(region)
|
||||
for ext in region.exits:
|
||||
connect = ext.connected_region
|
||||
if connect not in visited and connect is not None and connect.type == RegionType.Dungeon:
|
||||
if connect in rrp and ext.can_reach(self) and connect:
|
||||
door = self.world.check_for_door(ext.name, player)
|
||||
current_crystal = ccr[connect]
|
||||
if door is not None and not door.blocked and current_crystal != crystal and current_crystal != CrystalBarrier.Either:
|
||||
if door.crystal in [CrystalBarrier.Either, CrystalBarrier.Null]:
|
||||
ccr[connect] = crystal
|
||||
queue.append((connect, crystal))
|
||||
else:
|
||||
queue.append((connect, door.crystal))
|
||||
if door.crystal != current_crystal:
|
||||
ccr[connect] = CrystalBarrier.Either
|
||||
|
||||
def copy(self):
|
||||
ret = CollectionState(self.world)
|
||||
ret.prog_items = self.prog_items.copy()
|
||||
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)}
|
||||
ret.colored_regions = {player: copy.copy(self.colored_regions[player]) for player in range(1, self.world.players + 1)}
|
||||
ret.events = copy.copy(self.events)
|
||||
ret.path = copy.copy(self.path)
|
||||
ret.locations_checked = copy.copy(self.locations_checked)
|
||||
@@ -418,6 +462,15 @@ class CollectionState(object):
|
||||
|
||||
return spot.can_reach(self)
|
||||
|
||||
def sweep_for_crystal_access(self):
|
||||
for player, rrp in self.reachable_regions.items():
|
||||
dungeon_regions = [x for x in rrp if x.type == RegionType.Dungeon]
|
||||
ccr = self.colored_regions[player]
|
||||
for region in dungeon_regions:
|
||||
if region in ccr.keys():
|
||||
self.spread_crystal_access(region, ccr[region], rrp, ccr, player)
|
||||
self.stale[player] = True
|
||||
|
||||
def sweep_for_events(self, key_only=False, locations=None):
|
||||
# this may need improvement
|
||||
if locations is None:
|
||||
@@ -435,6 +488,18 @@ class CollectionState(object):
|
||||
self.collect(event.item, True, event)
|
||||
new_locations = len(reachable_events) > checked_locations
|
||||
checked_locations = len(reachable_events)
|
||||
if new_locations:
|
||||
self.sweep_for_crystal_access()
|
||||
|
||||
def can_reach_blue(self, region, player):
|
||||
if region not in self.colored_regions[player].keys():
|
||||
return False
|
||||
return self.colored_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either]
|
||||
|
||||
def can_reach_orange(self, region, player):
|
||||
if region not in self.colored_regions[player].keys():
|
||||
return False
|
||||
return self.colored_regions[player][region] in [CrystalBarrier.Orange, CrystalBarrier.Either]
|
||||
|
||||
def _do_not_flood_the_keys(self, reachable_events):
|
||||
adjusted_checks = list(reachable_events)
|
||||
@@ -721,7 +786,7 @@ class CollectionState(object):
|
||||
class RegionType(Enum):
|
||||
LightWorld = 1
|
||||
DarkWorld = 2
|
||||
Cave = 3 # Also includes Houses
|
||||
Cave = 3 # Also includes Houses
|
||||
Dungeon = 4
|
||||
|
||||
@property
|
||||
@@ -1277,7 +1342,6 @@ class Shop(object):
|
||||
def __init__(self, region, room_id, type, shopkeeper_config, custom, locked):
|
||||
self.region = region
|
||||
self.room_id = room_id
|
||||
self.default_door_id = default_door_id
|
||||
self.type = type
|
||||
self.inventory = [None, None, None]
|
||||
self.shopkeeper_config = shopkeeper_config
|
||||
@@ -1297,8 +1361,6 @@ class Shop(object):
|
||||
config = self.item_count
|
||||
if len(entrances) == 1 and entrances[0].name in door_addresses:
|
||||
door_id = door_addresses[entrances[0].name][0]+1
|
||||
elif self.default_door_id is not None:
|
||||
door_id = self.default_door_id
|
||||
else:
|
||||
door_id = 0
|
||||
config |= 0x40 # ignore door id
|
||||
|
||||
+10
-5
@@ -734,6 +734,7 @@ def reassign_boss(boss_region, boss_key, builder, gt, world, player):
|
||||
|
||||
|
||||
def experiment(world, player):
|
||||
# print_wiki_doors(dungeon_regions, world, player)
|
||||
cross_dungeon(world, player)
|
||||
|
||||
|
||||
@@ -750,8 +751,12 @@ def convert_to_sectors(region_names, world, player):
|
||||
matching_sectors = []
|
||||
while len(exits) > 0:
|
||||
ext = exits.pop()
|
||||
if ext.connected_region is not None:
|
||||
connect_region = ext.connected_region
|
||||
door = world.check_for_door(ext.name, player)
|
||||
if ext.connected_region is not None or door is not None and door.controller is not None:
|
||||
if door is not None and door.controller is not None:
|
||||
connect_region = world.get_entrance(door.controller.name, player).parent_region
|
||||
else:
|
||||
connect_region = ext.connected_region
|
||||
if connect_region not in region_chunk and connect_region in region_list:
|
||||
region_list.remove(connect_region)
|
||||
region_chunk.append(connect_region)
|
||||
@@ -763,7 +768,6 @@ def convert_to_sectors(region_names, world, player):
|
||||
if existing not in matching_sectors:
|
||||
matching_sectors.append(existing)
|
||||
else:
|
||||
door = world.check_for_door(ext.name, player)
|
||||
if door is not None and door.controller is None and door.dest is None:
|
||||
outstanding_doors.append(door)
|
||||
sector = Sector()
|
||||
@@ -1034,7 +1038,7 @@ def reassign_key_doors(builder, proposal, world, player):
|
||||
room.delete(d.doorListPos)
|
||||
else:
|
||||
if len(room.doorList) > 1:
|
||||
room.mirror(d.doorListPos)
|
||||
room.mirror(d.doorListPos) # todo: I don't think this works for crossed - maybe it will
|
||||
else:
|
||||
room.delete(d.doorListPos)
|
||||
d.smallKey = False
|
||||
@@ -1350,7 +1354,6 @@ logical_connections = [
|
||||
('Thieves Conveyor Block Path', 'Thieves Conveyor Bridge'),
|
||||
('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'),
|
||||
('Ice Cross Right Push Block Top', 'Ice Bomb Drop'),
|
||||
('Ice Cross Top Push Block Left', 'Ice Floor Switch'),
|
||||
('Ice Big Key Push Block', 'Ice Dead End'),
|
||||
('Ice Bomb Jump Ledge Orange Barrier', 'Ice Bomb Jump Catwalk'),
|
||||
('Ice Bomb Jump Catwalk Orange Barrier', 'Ice Bomb Jump Ledge'),
|
||||
@@ -1536,6 +1539,8 @@ falldown_pits = [
|
||||
('Ice Backwards Room Hole', 'Ice Fairy'),
|
||||
('Ice Antechamber Hole', 'Ice Boss'),
|
||||
('Mire Attic Hint Hole', 'Mire BK Chest Ledge'),
|
||||
('Mire Torches Top Holes', 'Mire Conveyor Barrier'),
|
||||
('Mire Torches Bottom Holes', 'Mire Warping Pool'),
|
||||
('GT Bob\'s Room Hole', 'GT Ice Armos'),
|
||||
('GT Falling Torches Hole', 'GT Staredown'),
|
||||
('GT Moldorm Hole', 'GT Moldorm Pit')
|
||||
|
||||
@@ -616,7 +616,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Cross Bottom Push Block Right', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Right Push Block Top', Lgcl),
|
||||
create_door(player, 'Ice Cross Right Push Block Bottom', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Top Push Block Left', Lgcl),
|
||||
create_door(player, 'Ice Cross Top Push Block Bottom', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Top Push Block Right', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3),
|
||||
create_door(player, 'Ice Cross Right ES', Nrml).dir(Ea, 0x1e, Bot, High).trap(0x4).pos(0),
|
||||
@@ -789,8 +789,10 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Warping Pool Warp', Warp),
|
||||
create_door(player, 'Mire Torches Top Down Stairs', Sprl).dir(Dn, 0x97, 0, HTH).ss(A, 0x11, 0xb0, True).kill(),
|
||||
create_door(player, 'Mire Torches Top SW', Intr).dir(So, 0x97, Left, High).pos(1),
|
||||
create_door(player, 'Mire Torches Bottom Holes', Hole),
|
||||
create_door(player, 'Mire Torches Bottom NW', Intr).dir(No, 0x97, Left, High).pos(1),
|
||||
create_door(player, 'Mire Torches Bottom WS', Intr).dir(We, 0x97, Bot, High).pos(0),
|
||||
create_door(player, 'Mire Torches Top Holes', Hole),
|
||||
create_door(player, 'Mire Attic Hint ES', Intr).dir(Ea, 0x97, Bot, High).pos(0),
|
||||
create_door(player, 'Mire Attic Hint Hole', Hole),
|
||||
create_door(player, 'Mire Dark Shooters Up Stairs', Sprl).dir(Up, 0x93, 0, LTH).ss(A, 0x32, 0xec),
|
||||
@@ -900,7 +902,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Torch EN', Intr).dir(Ea, 0x8c, Top, High).small_key().pos(2),
|
||||
create_door(player, 'GT Hope Room WN', Intr).dir(We, 0x8c, Top, High).small_key().pos(2),
|
||||
create_door(player, 'GT Torch SW', Intr).dir(So, 0x8c, Left, High).no_exit().pos(1),
|
||||
create_door(player, 'GT Big Chest NW', Intr).dir(So, 0x8c, Left, High).pos(1),
|
||||
create_door(player, 'GT Big Chest NW', Intr).dir(No, 0x8c, Left, High).pos(1),
|
||||
create_door(player, 'GT Blocked Stairs Down Stairs', Sprl).dir(Dn, 0x8c, 3, HTH).ss(Z, 0x12, 0x40, True, True).kill(),
|
||||
create_door(player, 'GT Blocked Stairs Block Path', Lgcl),
|
||||
create_door(player, 'GT Big Chest SW', Nrml).dir(So, 0x8c, Left, High).pos(4),
|
||||
@@ -1004,11 +1006,11 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Gauntlet 2 EN', Intr).dir(Ea, 0x5d, Top, High).pos(2),
|
||||
create_door(player, 'GT Gauntlet 2 SW', Intr).dir(So, 0x5d, Left, High).pos(0),
|
||||
create_door(player, 'GT Gauntlet 3 NW', Intr).dir(No, 0x5d, Left, High).pos(0),
|
||||
create_door(player, 'GT Gauntlet 3 SW', Nrml).dir(So, 0x5d, Left, High).pos(1),
|
||||
create_door(player, 'GT Gauntlet 4 NW', Nrml).dir(No, 0x6d, Left, High).pos(0),
|
||||
create_door(player, 'GT Gauntlet 3 SW', Nrml).dir(So, 0x5d, Left, High).trap(0x2).pos(1),
|
||||
create_door(player, 'GT Gauntlet 4 NW', Nrml).dir(No, 0x6d, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT Gauntlet 4 SW', Intr).dir(So, 0x6d, Left, High).pos(1),
|
||||
create_door(player, 'GT Gauntlet 5 NW', Intr).dir(No, 0x6d, Left, High).pos(1),
|
||||
create_door(player, 'GT Gauntlet 5 WS', Nrml).dir(We, 0x6d, Bot, High).pos(2),
|
||||
create_door(player, 'GT Gauntlet 5 WS', Nrml).dir(We, 0x6d, Bot, High).trap(0x1).pos(2),
|
||||
create_door(player, 'GT Beam Dash ES', Nrml).dir(Ea, 0x6c, Bot, High).pos(2).kill(),
|
||||
create_door(player, 'GT Beam Dash WS', Intr).dir(We, 0x6c, Bot, High).pos(0),
|
||||
create_door(player, 'GT Lanmolas 2 ES', Intr).dir(Ea, 0x6c, Bot, High).pos(0),
|
||||
@@ -1084,11 +1086,12 @@ def create_doors(world, player):
|
||||
world.get_door('Hera Startile Wide Holes', player).c_switch()
|
||||
|
||||
world.get_door('PoD Arena Main SW', player).c_switch()
|
||||
world.get_door('PoD Arena Bonk Path', player).c_switch()
|
||||
world.get_door('PoD Arena Bridge SE', player).c_switch()
|
||||
world.get_door('PoD Arena Bridge Drop Down', player).c_switch()
|
||||
world.get_door('PoD Arena Main Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||
# maybe you can cross this way with blue up??
|
||||
world.get_door('PoD Arena Main Crystal Path', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('PoD Arena Crystals E', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('PoD Arena Crystal Path', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('PoD Sexy Statue W', player).c_switch()
|
||||
world.get_door('PoD Sexy Statue NW', player).c_switch()
|
||||
@@ -1185,6 +1188,7 @@ def create_doors(world, player):
|
||||
world.get_door('GT Hookshot Entry Blue Barrier', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('GT Double Switch Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Orange Barrier 2', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Orange Path', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Key Orange Path', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Key Blue Path', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('GT Double Switch Blue Barrier', player).barrier(CrystalBarrier.Blue)
|
||||
@@ -1195,6 +1199,7 @@ def create_doors(world, player):
|
||||
east_controller = world.get_door('Ice Cross Right ES', player)
|
||||
controller_door(south_controller, world.get_door('Ice Cross Left Push Block', player))
|
||||
controller_door(south_controller, world.get_door('Ice Cross Right Push Block Bottom', player))
|
||||
controller_door(south_controller, world.get_door('Ice Cross Top Push Block Bottom', player))
|
||||
controller_door(east_controller, world.get_door('Ice Cross Bottom Push Block Right', player))
|
||||
controller_door(east_controller, world.get_door('Ice Cross Top Push Block Right', player))
|
||||
|
||||
|
||||
+2
-2
@@ -1128,7 +1128,7 @@ def assign_crystal_switch_sectors(dungeon_map, crystal_switches, assign_one=Fals
|
||||
assign_sector(random.choice(list(crystal_switches)), builder, crystal_switches)
|
||||
return crystal_switches
|
||||
sector_list = list(crystal_switches)
|
||||
choices = random.choices(sector_list, k=len(population))
|
||||
choices = random.sample(sector_list, k=len(population))
|
||||
for i, choice in enumerate(choices):
|
||||
builder = dungeon_map[population[i]]
|
||||
assign_sector(choice, builder, crystal_switches)
|
||||
@@ -1583,7 +1583,7 @@ dungeon_boss_sectors = {
|
||||
'Palace of Darkness': ['PoD Boss'],
|
||||
'Swamp Palace': ['Swamp Boss'],
|
||||
'Skull Woods': ['Skull Boss'],
|
||||
'Thieves Town': ['Thieves Attic Window', 'Thieves Blind\'s Cell', 'Thieves Boss'],
|
||||
'Thieves Town': ['Thieves Blind\'s Cell', 'Thieves Boss'],
|
||||
'Ice Palace': ['Ice Boss'],
|
||||
'Misery Mire': ['Mire Boss'],
|
||||
'Turtle Rock': ['TR Boss'],
|
||||
|
||||
@@ -303,7 +303,7 @@ def guiMain(args=None):
|
||||
|
||||
shuffleFrame = Frame(drowDownFrame)
|
||||
shuffleVar = StringVar()
|
||||
shuffleVar.set('full')
|
||||
shuffleVar.set('vanilla')
|
||||
shuffleOptionMenu = OptionMenu(shuffleFrame, shuffleVar, 'vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple')
|
||||
shuffleOptionMenu.pack(side=RIGHT)
|
||||
shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm')
|
||||
@@ -311,8 +311,8 @@ def guiMain(args=None):
|
||||
|
||||
doorShuffleFrame = Frame(drowDownFrame)
|
||||
doorShuffleVar = StringVar()
|
||||
doorShuffleVar.set('vanilla')
|
||||
doorShuffleOptionMenu = OptionMenu(doorShuffleFrame, doorShuffleVar, 'vanilla', 'basic', 'crosssed', 'experimental')
|
||||
doorShuffleVar.set('basic')
|
||||
doorShuffleOptionMenu = OptionMenu(doorShuffleFrame, doorShuffleVar, 'vanilla', 'basic', 'crossed', 'experimental')
|
||||
doorShuffleOptionMenu.pack(side=RIGHT)
|
||||
doorShuffleLabel = Label(doorShuffleFrame, text='Door shuffle algorithm')
|
||||
doorShuffleLabel.pack(side=LEFT)
|
||||
|
||||
+17
-13
@@ -118,9 +118,9 @@ def analyze_dungeon(key_layout, world, player):
|
||||
raw_avail = chest_keys + len(key_counter.key_only_locations)
|
||||
available = raw_avail - key_counter.used_keys
|
||||
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
||||
avail_bigs = count_unique_big_doors(key_counter)
|
||||
avail_bigs = exist_relevant_big_doors(key_counter, key_layout)
|
||||
if not key_counter.big_key_opened:
|
||||
if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls and avail_bigs == 0:
|
||||
if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls and not avail_bigs:
|
||||
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
|
||||
if not key_counter.big_key_opened and big_chest_in_locations(key_counter.free_locations):
|
||||
key_logic.sm_restricted.update(find_big_chest_locations(key_counter.free_locations))
|
||||
@@ -318,6 +318,7 @@ def create_rule(key_counter, prev_counter, key_layout, world, player):
|
||||
available = raw_avail - key_counter.used_keys
|
||||
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
||||
required_keys = min(available, possible_smalls) + key_counter.used_keys
|
||||
# required_keys = key_counter.used_keys + 1 # this sometimes makes more sense
|
||||
# if prev_avail < required_keys:
|
||||
# required_keys = prev_avail + prev_counter.used_keys
|
||||
# return DoorRules(required_keys)
|
||||
@@ -487,15 +488,16 @@ def count_unique_small_doors(key_counter, proposal):
|
||||
return cnt
|
||||
|
||||
|
||||
def count_unique_big_doors(key_counter):
|
||||
cnt = 0
|
||||
counted = set()
|
||||
for door in key_counter.child_doors:
|
||||
if door.bigKey and door not in counted:
|
||||
cnt += 1
|
||||
counted.add(door)
|
||||
counted.add(door.dest)
|
||||
return cnt
|
||||
def exist_relevant_big_doors(key_counter, key_layout):
|
||||
bk_counter = find_counter(key_counter.open_doors, True, key_layout, False)
|
||||
if bk_counter is not None:
|
||||
diff = dict_difference(bk_counter.free_locations, key_counter.free_locations)
|
||||
if len(diff) > 0:
|
||||
return True
|
||||
diff = dict_difference(bk_counter.key_only_locations, key_counter.key_only_locations)
|
||||
if len(diff) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def count_locations_big_optional(locations, bk=False):
|
||||
@@ -788,7 +790,7 @@ def state_id(state, flat_proposal):
|
||||
return s_id
|
||||
|
||||
|
||||
def find_counter(opened_doors, bk_hint, key_layout):
|
||||
def find_counter(opened_doors, bk_hint, key_layout, raise_on_error=True):
|
||||
counter = find_counter_hint(opened_doors, bk_hint, key_layout)
|
||||
if counter is not None:
|
||||
return counter
|
||||
@@ -801,7 +803,9 @@ def find_counter(opened_doors, bk_hint, key_layout):
|
||||
counter = find_counter_hint(dict.fromkeys(more_doors), bk_hint, key_layout)
|
||||
if counter is not None:
|
||||
return counter
|
||||
raise Exception('Unable to find door permutation. Init CID: %s' % counter_id(opened_doors, bk_hint, key_layout.flat_prop))
|
||||
if raise_on_error:
|
||||
raise Exception('Unable to find door permutation. Init CID: %s' % counter_id(opened_doors, bk_hint, key_layout.flat_prop))
|
||||
return None
|
||||
|
||||
|
||||
def find_counter_hint(opened_doors, bk_hint, key_layout):
|
||||
|
||||
@@ -361,6 +361,7 @@ def create_playthrough(world):
|
||||
logging.getLogger('').debug('Building up collection spheres.')
|
||||
while sphere_candidates:
|
||||
state.sweep_for_events(key_only=True)
|
||||
state.sweep_for_crystal_access()
|
||||
|
||||
sphere = []
|
||||
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
|
||||
@@ -422,6 +423,7 @@ def create_playthrough(world):
|
||||
collection_spheres = []
|
||||
while required_locations:
|
||||
state.sweep_for_events(key_only=True)
|
||||
state.sweep_for_crystal_access()
|
||||
|
||||
sphere = list(filter(lambda loc: state.can_reach(loc) and state.not_flooding_a_key(world, loc), required_locations))
|
||||
|
||||
|
||||
+3
-3
@@ -481,7 +481,7 @@ def create_regions(world, player):
|
||||
create_dungeon_region(player, 'Ice Cross Left', 'Ice Palace', None, ['Ice Cross Left WS', 'Ice Cross Left Push Block']),
|
||||
create_dungeon_region(player, 'Ice Cross Bottom', 'Ice Palace', None, ['Ice Cross Bottom SE', 'Ice Cross Bottom Push Block Left', 'Ice Cross Bottom Push Block Right']),
|
||||
create_dungeon_region(player, 'Ice Cross Right', 'Ice Palace', None, ['Ice Cross Right ES', 'Ice Cross Right Push Block Top', 'Ice Cross Right Push Block Bottom']),
|
||||
create_dungeon_region(player, 'Ice Cross Top', 'Ice Palace', None, ['Ice Cross Top NE', 'Ice Cross Top Push Block Left', 'Ice Cross Top Push Block Right']),
|
||||
create_dungeon_region(player, 'Ice Cross Top', 'Ice Palace', None, ['Ice Cross Top NE', 'Ice Cross Top Push Block Bottom', 'Ice Cross Top Push Block Right']),
|
||||
create_dungeon_region(player, 'Ice Compass Room', 'Ice Palace', ['Ice Palace - Compass Chest'], ['Ice Compass Room NE']),
|
||||
create_dungeon_region(player, 'Ice Pengator Switch', 'Ice Palace', None, ['Ice Pengator Switch WS', 'Ice Pengator Switch ES']),
|
||||
create_dungeon_region(player, 'Ice Dead End', 'Ice Palace', None, ['Ice Dead End WS']),
|
||||
@@ -560,8 +560,8 @@ def create_regions(world, player):
|
||||
create_dungeon_region(player, 'Mire Conveyor Barrier', 'Misery Mire', None, ['Mire Conveyor Barrier NW', 'Mire Conveyor Barrier Up Stairs']),
|
||||
create_dungeon_region(player, 'Mire BK Chest Ledge', 'Misery Mire', ['Misery Mire - Big Key Chest'], ['Mire BK Chest Ledge WS']),
|
||||
create_dungeon_region(player, 'Mire Warping Pool', 'Misery Mire', None, ['Mire Warping Pool ES', 'Mire Warping Pool Warp']),
|
||||
create_dungeon_region(player, 'Mire Torches Top', 'Misery Mire', None, ['Mire Torches Top Down Stairs', 'Mire Torches Top SW']),
|
||||
create_dungeon_region(player, 'Mire Torches Bottom', 'Misery Mire', None, ['Mire Torches Bottom NW', 'Mire Torches Bottom WS']),
|
||||
create_dungeon_region(player, 'Mire Torches Top', 'Misery Mire', None, ['Mire Torches Top Down Stairs', 'Mire Torches Top SW', 'Mire Torches Top Holes']),
|
||||
create_dungeon_region(player, 'Mire Torches Bottom', 'Misery Mire', None, ['Mire Torches Bottom NW', 'Mire Torches Bottom WS', 'Mire Torches Bottom Holes']),
|
||||
create_dungeon_region(player, 'Mire Attic Hint', 'Misery Mire', None, ['Mire Attic Hint ES', 'Mire Attic Hint Hole']),
|
||||
create_dungeon_region(player, 'Mire Dark Shooters', 'Misery Mire', None, ['Mire Dark Shooters Up Stairs', 'Mire Dark Shooters SW', 'Mire Dark Shooters SE']),
|
||||
create_dungeon_region(player, 'Mire Key Rupees', 'Misery Mire', None, ['Mire Key Rupees NE']),
|
||||
|
||||
@@ -1254,8 +1254,11 @@ def patch_rom(world, player, rom, enemized):
|
||||
# set rom name
|
||||
# 21 bytes
|
||||
from Main import __version__
|
||||
rom.name = bytearray('ER{0}_{1}_{2:09}\0'.format(__version__.split('-')[0].replace('.','')[0:3], player, world.seed), 'utf8')
|
||||
rom.write_bytes(0x7FC0, rom.name[0:21])
|
||||
# todo: change to DR when Enemizer is okay with DR
|
||||
rom.name = bytearray('ER_{0}_{1:09}\0'.format(__version__[0:7], world.seed), 'utf8')
|
||||
assert len(rom.name) <= 21
|
||||
rom.write_bytes(0x7FC0, rom.name)
|
||||
rom.name[0] = ord('D')
|
||||
|
||||
# Write title screen Code
|
||||
hashint = int(rom.get_hash(), 16)
|
||||
|
||||
@@ -233,6 +233,7 @@ def create_rooms(world, player):
|
||||
# Room(player, 0xff, 0x52c9a).door(Position.InteriorW, DoorKind.Bombable).door(Position.InteriorE, DoorKind.Bombable).door(Position.SouthE, DoorKind.CaveEntrance),
|
||||
]
|
||||
# fix some wonky things
|
||||
world.get_room(0x51, player).change(1, DoorKind.Normal) # fix the dungeon changer
|
||||
world.get_room(0x60, player).swap(2, 4) # puts the exit at pos 2 - enables pos 3
|
||||
world.get_room(0x61, player).swap(1, 6) # puts the WN door at pos 1 - enables it
|
||||
world.get_room(0x61, player).swap(5, 6) # puts the Incognito Entrance at the end, so it can be deleted
|
||||
|
||||
@@ -311,6 +311,55 @@ def global_rules(world, player):
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||
|
||||
add_key_logic_rules(world, player)
|
||||
|
||||
# crystal switch rules
|
||||
set_rule(world.get_entrance('PoD Arena Crystal Path', player), lambda state: state.can_reach_blue(world.get_region('PoD Arena Crystal', player), player))
|
||||
set_rule(world.get_entrance('Swamp Trench 2 Pots Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Trench 2 Pots', player), player))
|
||||
set_rule(world.get_entrance('Swamp Shortcut Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Shortcut', player), player))
|
||||
set_rule(world.get_entrance('Thieves Attic ES', player), lambda state: state.can_reach_blue(world.get_region('Thieves Attic', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Crystal Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway N Crystal', player), player))
|
||||
set_rule(world.get_entrance('Thieves Triple Bypass SE', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player))
|
||||
set_rule(world.get_entrance('Thieves Triple Bypass WN', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player))
|
||||
set_rule(world.get_entrance('Thieves Triple Bypass EN', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player))
|
||||
set_rule(world.get_entrance('Ice Crystal Right Blue Hole', player), lambda state: state.can_reach_blue(world.get_region('Ice Crystal Right', player), player))
|
||||
set_rule(world.get_entrance('Ice Crystal Left Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Ice Crystal Left', player), player))
|
||||
set_rule(world.get_entrance('Ice Backwards Room Hole', player), lambda state: state.can_reach_blue(world.get_region('Ice Backwards Room', player), player))
|
||||
set_rule(world.get_entrance('Mire Hub Upper Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub', player), player))
|
||||
set_rule(world.get_entrance('Mire Hub Lower Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub', player), player))
|
||||
set_rule(world.get_entrance('Mire Hub Right Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub Right', player), player))
|
||||
set_rule(world.get_entrance('Mire Hub Top Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub Top', player), player))
|
||||
set_rule(world.get_entrance('Mire Map Spike Side Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Map Spike Side', player), player))
|
||||
set_rule(world.get_entrance('Mire Map Spot Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Map Spot', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Dead End Left Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Dead End', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Dead End Right Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Dead End', player), player))
|
||||
set_rule(world.get_entrance('Mire South Fish Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire South Fish', player), player))
|
||||
set_rule(world.get_entrance('Mire Compass Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Compass Room', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Mid Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Mid', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Left Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Left', player), player))
|
||||
set_rule(world.get_entrance('TR Crystal Maze Blue Path', player), lambda state: state.can_reach_blue(world.get_region('TR Crystal Maze End', player), player))
|
||||
set_rule(world.get_entrance('GT Hookshot Entry Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Entry', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Key Blue Path', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Key Spot', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Switches', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Transition Blue', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Transition', player), player))
|
||||
|
||||
set_rule(world.get_entrance('Swamp Barrier Ledge - Orange', player), lambda state: state.can_reach_orange(world.get_region('Swamp Barrier Ledge', player), player))
|
||||
set_rule(world.get_entrance('Swamp Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('Swamp Barrier', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Thieves Hellway', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Crystal Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Thieves Hellway S Crystal', player), player))
|
||||
set_rule(world.get_entrance('Ice Bomb Jump Ledge Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Ledge', player), player))
|
||||
set_rule(world.get_entrance('Ice Bomb Jump Catwalk Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Catwalk', player), player))
|
||||
set_rule(world.get_entrance('Ice Crystal Right Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Crystal Right', player), player))
|
||||
set_rule(world.get_entrance('Ice Crystal Left Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Crystal Left', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Right Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Crystal Right', player), player))
|
||||
set_rule(world.get_entrance('Mire Crystal Mid Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Crystal Mid', player), player))
|
||||
set_rule(world.get_entrance('Mire Firesnake Skip Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Firesnake Skip', player), player))
|
||||
set_rule(world.get_entrance('Mire Antechamber Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Antechamber', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Entry', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Orange Barrier 2', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Entry', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Switches', player), player))
|
||||
set_rule(world.get_entrance('GT Double Switch Key Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Key Spot', player), player))
|
||||
|
||||
# End of door rando rules.
|
||||
|
||||
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
||||
|
||||
@@ -188,5 +188,44 @@ def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan)
|
||||
print("%s: %s" % (dp, bytes))
|
||||
|
||||
|
||||
def print_wiki_doors(d_regions, world, player):
|
||||
|
||||
for d, region_list in d_regions.items():
|
||||
tile_map = {}
|
||||
for region in region_list:
|
||||
tile = None
|
||||
r = world.get_region(region, player)
|
||||
for ext in r.exits:
|
||||
door = world.check_for_door(ext.name, player)
|
||||
if door is not None and door.roomIndex != -1:
|
||||
tile = door.roomIndex
|
||||
break
|
||||
if tile is not None:
|
||||
if tile not in tile_map:
|
||||
tile_map[tile] = []
|
||||
tile_map[tile].append(r)
|
||||
print(d)
|
||||
print('{| class="wikitable"')
|
||||
print('|-')
|
||||
print('! Room')
|
||||
print('! Supertile')
|
||||
print('! Doors')
|
||||
for tile, region_list in tile_map.items():
|
||||
tile_done = False
|
||||
for region in region_list:
|
||||
print('|-')
|
||||
print('| '+region.name)
|
||||
if not tile_done:
|
||||
listlen = len(region_list)
|
||||
link = '| {{UnderworldMapLink|'+str(tile)+'}}'
|
||||
print(link if listlen < 2 else '| rowspan = '+str(listlen)+' '+link)
|
||||
tile_done = True
|
||||
strs_to_print = []
|
||||
for ext in region.exits:
|
||||
strs_to_print.append(ext.name)
|
||||
print('| '+' <br /> '.join(strs_to_print))
|
||||
print('|}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
read_entrance_data(old_rom='C:\\Users\\Randall\\Documents\\kwyn\\orig\\z3.sfc')
|
||||
+2
-2
@@ -43,8 +43,8 @@ org $00d6ae ;(PC: 56ae)
|
||||
LoadTransAuxGfx:
|
||||
org $00df5a ;(PC: 5f5a)
|
||||
PrepTransAuxGfx:
|
||||
;org $0ffd65 ;(PC: 07fd65)
|
||||
;Dungeon_LoadCustomTileAttr:
|
||||
org $0ffd65 ;(PC: 07fd65)
|
||||
Dungeon_LoadCustomTileAttr:
|
||||
;org $01fec1
|
||||
;Dungeon_ApproachFixedColor_variable:
|
||||
;org $a0f972 ; Rando version
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ GfxFixer:
|
||||
jsl LoadRoomHook ; this is the rando version - let's only call this guy once - may fix star tiles and slower loads
|
||||
jsl Dungeon_InitStarTileCh
|
||||
jsl LoadTransAuxGfx
|
||||
;jsl Dungeon_LoadCustomTileAttr
|
||||
jsl Dungeon_LoadCustomTileAttr
|
||||
jsl PrepTransAuxGfx
|
||||
lda #$09 : sta $17 : sta $0710
|
||||
jsl Palette_SpriteAux3
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user