Merge in DoorDev

This commit is contained in:
aerinon
2020-03-11 14:33:53 -06:00
20 changed files with 713 additions and 135 deletions

View File

@@ -7,7 +7,7 @@ from collections import OrderedDict, deque, defaultdict
from EntranceShuffle import door_addresses
from _vendor.collections_extended import bag
from Utils import int16_as_bytes
from Tables import normal_offset_table, spiral_offset_table
from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup
from RoomData import Room
class World(object):
@@ -41,7 +41,7 @@ class World(object):
self.shuffle_bonk_prizes = False
self.light_world_light_cone = False
self.dark_world_light_cone = False
self.clock_mode = 'off'
self.clock_mode = 'none'
self.rupoor_cost = 10
self.aga_randomness = True
self.lock_aga_door_in_escape = False
@@ -1041,12 +1041,21 @@ hook_dir_map = {
Direction.East: Hook.East,
}
edge_map = {
Direction.North: Hook.NEdge,
Direction.South: Hook.SEdge,
Direction.West: Hook.WEdge,
Direction.East: Hook.EEdge,
}
def hook_from_door(door):
if door.type == DoorType.SpiralStairs:
return Hook.Stairs
if door.type == DoorType.Normal:
return hook_dir_map[door.direction]
if door.type == DoorType.Open:
return edge_map[door.direction]
return None
@@ -1158,6 +1167,8 @@ class Door(object):
self.zeroHzCam = False
self.zeroVtCam = False
self.doorListPos = -1
self.edge_id = None
self.edge_width = None
# logical properties
# self.connected = False # combine with Dest?
@@ -1182,10 +1193,18 @@ class Door(object):
return 0x13A000 + normal_offset_table[self.roomIndex] * 24 + (self.doorIndex + self.direction.value * 3) * 2
elif self.type == DoorType.SpiralStairs:
return 0x13B000 + (spiral_offset_table[self.roomIndex] + self.doorIndex) * 4
elif self.type == DoorType.Open:
base_address = {
Direction.North: 0x13C500,
Direction.South: 0x13C533,
Direction.West: 0x13C566,
Direction.East: 0x13C581,
}
return base_address[self.direction] + self.edge_id * 3
def getTarget(self, toggle):
def getTarget(self, src):
if self.type == DoorType.Normal:
bitmask = 4 * (self.layer ^ 1 if toggle else self.layer)
bitmask = 4 * (self.layer ^ 1 if src.toggle else self.layer)
bitmask += 0x08 * int(self.trapFlag)
return [self.roomIndex, bitmask + self.doorIndex]
if self.type == DoorType.SpiralStairs:
@@ -1194,6 +1213,17 @@ class Door(object):
bitmask += 0x20 * int(self.zeroVtCam)
bitmask += 0x80 if self.direction == Direction.Up else 0
return [self.roomIndex, bitmask + self.quadrant, self.shiftX, self.shiftY]
if self.type == DoorType.Open:
bitmask = self.edge_id
bitmask += 0x10 * self.layer
bitmask += 0x20 * self.quadrant
bitmask += 0x80
if src.type == DoorType.Open:
fraction = 0x10 * multiply_lookup[src.edge_width][self.edge_width]
fraction += divisor_lookup[src.edge_width][self.edge_width]
return [self.roomIndex, bitmask, fraction]
else:
return [self.roomIndex, bitmask]
def dir(self, direction, room, doorIndex, layer):
self.direction = direction
@@ -1210,6 +1240,12 @@ class Door(object):
self.zeroVtCam = zero_vt_cam
return self
def edge(self, edge_id, quadrant, width):
self.edge_id = edge_id
self.quadrant = quadrant
self.edge_width = width
return self
def small_key(self):
self.smallKey = True
return self

6
CLI.py
View File

@@ -188,7 +188,7 @@ def parse_arguments(argv, no_defaults=False):
base game.
''')
parser.add_argument('--experimental', default=defval(settings["experimental"] != 0), help='Enable experimental features', action='store_true')
parser.add_argument('--dungeon_counters', default=defval(settings["dungeon_counters"]), help='Enable dungeon chest counters', const='off', nargs='?', choices=['off', 'on', 'pickup'])
parser.add_argument('--dungeon_counters', default=defval(settings["dungeon_counters"]), help='Enable dungeon chest counters', const='off', nargs='?', choices=['off', 'on', 'pickup', 'default'])
parser.add_argument('--crystals_ganon', default=defval(settings["crystals_ganon"]), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'],
help='''\
How many crystals are needed to defeat ganon. Any other
@@ -357,8 +357,8 @@ def get_settings():
"bigkeyshuffle": False,
"keysanity": False,
"door_shuffle": "basic",
"experimental": 0,
"dungeon_counters": "off",
"experimental": False,
"dungeon_counters": "default",
"multi": 1,
"names": "",

View File

@@ -30,8 +30,6 @@ def link_doors(world, player):
# These connections are here because they are currently unable to be shuffled
for entrance, ext in straight_staircases:
connect_two_way(world, entrance, ext, player)
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
for exitName, regionName in falldown_pits:
connect_simple_door(world, exitName, regionName, player)
for exitName, regionName in dungeon_warps:
@@ -40,6 +38,8 @@ def link_doors(world, player):
connect_two_way(world, ent, ext, player)
if world.doorShuffle[player] == 'vanilla':
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
for exitName, regionName in vanilla_logical_connections:
connect_simple_door(world, exitName, regionName, player)
for entrance, ext in spiral_staircases:
@@ -50,8 +50,13 @@ def link_doors(world, player):
connect_one_way(world, ent, ext, player)
vanilla_key_logic(world, player)
elif world.doorShuffle[player] == 'basic':
# if not world.experimental[player]:
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
within_dungeon(world, player)
elif world.doorShuffle[player] == 'crossed':
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
cross_dungeon(world, player)
else:
logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player])
@@ -134,7 +139,7 @@ def vanilla_key_logic(world, player):
enabled_entrances = {}
sector_queue = deque(builders)
last_key = None
last_key, loops = None, 0
while len(sector_queue) > 0:
builder = sector_queue.popleft()
@@ -142,12 +147,14 @@ def vanilla_key_logic(world, player):
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, builder.name)
origin_list_sans_drops = remove_drop_origins(origin_list)
if len(origin_list_sans_drops) <= 0:
if last_key == builder.name:
raise Exception('Infinte loop detected %s' % builder.name)
if last_key == builder.name or loops > 1000:
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name
raise Exception('Infinite loop detected for "%s" located at %s' % builder.name, origin_name)
sector_queue.append(builder)
last_key = builder.name
loops += 1
else:
find_new_entrances(builder.master_sector, connections, potentials, enabled_entrances, world, player)
find_new_entrances(builder.master_sector, entrances_map, connections, potentials, enabled_entrances, world, player)
start_regions = convert_regions(origin_list, world, player)
doors = convert_key_doors(default_small_key_doors[builder.name], world, player)
key_layout = build_key_layout(builder, start_regions, doors, world, player)
@@ -160,7 +167,7 @@ def vanilla_key_logic(world, player):
world.key_logic[player][builder.name] = key_layout.key_logic
log_key_logic(builder.name, key_layout.key_logic)
last_key = None
if world.shuffle[player] == 'vanilla' and world.accessibility[player] == 'items':
if world.shuffle[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player]:
validate_vanilla_key_logic(world, player)
@@ -345,7 +352,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
entrances_map, potentials, connections = connections_tuple
enabled_entrances = {}
sector_queue = deque(dungeon_builders.values())
last_key = None
last_key, loops = None, 0
while len(sector_queue) > 0:
builder = sector_queue.popleft()
split_dungeon = builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
@@ -356,14 +363,16 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, name)
origin_list_sans_drops = remove_drop_origins(origin_list)
if len(origin_list_sans_drops) <= 0 or name == "Turtle Rock" and not validate_tr(builder, origin_list_sans_drops, world, player):
if last_key == builder.name:
raise Exception('Infinte loop detected %s' % builder.name)
if last_key == builder.name or loops > 1000:
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name
raise Exception('Infinite loop detected for "%s" located at %s' % builder.name, origin_name)
sector_queue.append(builder)
last_key = builder.name
loops += 1
else:
logging.getLogger('').info('Generating dungeon: %s', builder.name)
ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player)
find_new_entrances(ds, connections, potentials, enabled_entrances, world, player)
find_new_entrances(ds, entrances_map, connections, potentials, enabled_entrances, world, player)
ds.name = name
builder.master_sector = ds
builder.layout_starts = origin_list if len(builder.entrance_list) <= 0 else builder.entrance_list
@@ -420,9 +429,14 @@ def remove_drop_origins(entrance_list):
return [x for x in entrance_list if x not in drop_entrances]
def find_new_entrances(sector, connections, potentials, enabled, world, player):
def find_new_entrances(sector, entrances_map, connections, potentials, enabled, world, player):
for region in sector.regions:
if region.name in connections.keys() and (connections[region.name] in potentials.keys() or connections[region.name].name in world.inaccessible_regions[player]):
enable_new_entrances(region, connections, potentials, enabled, world, player)
inverted_aga_check(entrances_map, connections, potentials, enabled, world, player)
def enable_new_entrances(region, connections, potentials, enabled, world, player):
new_region = connections[region.name]
if new_region in potentials.keys():
for potential in potentials.pop(new_region):
@@ -443,6 +457,24 @@ def find_new_entrances(sector, connections, potentials, enabled, world, player):
queue.append(new_exit)
def inverted_aga_check(entrances_map, connections, potentials, enabled, world, player):
if world.mode[player] == 'inverted':
if 'Agahnims Tower' in entrances_map.keys() or aga_tower_enabled(enabled):
for region in list(potentials.keys()):
if region.name == 'Hyrule Castle Ledge':
for r_name in potentials[region]:
new_region = world.get_region(r_name, player)
enable_new_entrances(new_region, connections, potentials, enabled, world, player)
def aga_tower_enabled(enabled):
for region_name, enabled_tuple in enabled.items():
entrance, dungeon = enabled_tuple
if dungeon.name == 'Agahnims Tower':
return True
return False
def within_dungeon_legacy(world, player):
# TODO: The "starts" regions need access logic
# Aerinon's note: I think this is handled already by ER Rules - may need to check correct requirements
@@ -812,7 +844,7 @@ def convert_to_sectors(region_names, world, player):
matching_sectors = []
while len(exits) > 0:
ext = exits.pop()
door = world.check_for_door(ext.name, player)
door = ext.door
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
@@ -1685,7 +1717,7 @@ open_edges = [
('Thieves Ambush EN Edge', 'Thieves BK Corner WN Edge'),
('Thieves BK Corner S Edge', 'Thieves Compass Room N Edge'),
('Thieves BK Corner SW Edge', 'Thieves Compass Room NW Edge'),
('Thieves Compass Room WS Edge', 'Thieves Big Chest Nook WS Edge'),
('Thieves Compass Room WS Edge', 'Thieves Big Chest Nook ES Edge'),
('Thieves Cricket Hall Left Edge', 'Thieves Cricket Hall Right Edge')
]
@@ -1935,7 +1967,7 @@ interior_doors = [
key_doors = [
('Sewers Key Rat Key Door N', 'Sewers Secret Room Key Door S'),
('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S'),
('Sewers Dark Cross Key Door N', 'Sewers Water S'),
('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN'),
('Eastern Darkness Up Stairs', 'Eastern Attic Start Down Stairs'),
('Eastern Big Key NE', 'Eastern Hint Tile Blocked Path SE'),
@@ -1955,7 +1987,7 @@ key_doors = [
default_small_key_doors = {
'Hyrule Castle': [
('Sewers Key Rat Key Door N', 'Sewers Secret Room Key Door S'),
('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S'),
('Sewers Dark Cross Key Door N', 'Sewers Water S'),
('Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon North Abyss Key Door N'),
('Hyrule Dungeon Armory Interior Key Door N', 'Hyrule Dungeon Armory Interior Key Door S')
],
@@ -2053,7 +2085,7 @@ default_door_connections = [
('Hyrule Castle West Hall E', 'Hyrule Castle Back Hall W'),
('Hyrule Castle Throne Room N', 'Sewers Behind Tapestry S'),
('Hyrule Dungeon Guardroom N', 'Hyrule Dungeon Armory S'),
('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S'),
('Sewers Dark Cross Key Door N', 'Sewers Water S'),
('Sewers Water W', 'Sewers Key Rat E'),
('Sewers Key Rat Key Door N', 'Sewers Secret Room Key Door S'),
('Eastern Lobby Bridge N', 'Eastern Cannonball S'),

View File

@@ -68,15 +68,15 @@ def create_doors(world, player):
create_door(player, 'Hyrule Dungeon Map Room Up Stairs', Sprl).dir(Up, 0x72, 0, LTH).ss(A, 0x4b, 0xec),
create_door(player, 'Hyrule Dungeon Map Room Key Door S', Intr).dir(So, 0x72, Mid, High).small_key().pos(0),
create_door(player, 'Hyrule Dungeon North Abyss Key Door N', Intr).dir(No, 0x72, Mid, High).small_key().pos(0),
create_door(player, 'Hyrule Dungeon North Abyss South Edge', Open).dir(So, 0x72, None, Low),
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', Open).dir(So, 0x72, None, High),
create_door(player, 'Hyrule Dungeon North Abyss South Edge', Open).dir(So, 0x72, None, Low).edge(0, Z, 0x10),
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', Open).dir(So, 0x72, None, High).edge(1, Z, 0x08),
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Dropdown', Lgcl),
create_door(player, 'Hyrule Dungeon South Abyss North Edge', Open).dir(No, 0x82, None, Low),
create_door(player, 'Hyrule Dungeon South Abyss West Edge', Open).dir(We, 0x82, None, Low),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', Open).dir(No, 0x82, None, High),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', Open).dir(We, 0x82, None, High),
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', Open).dir(Ea, 0x81, None, High),
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', Open).dir(We, 0x81, None, High),
create_door(player, 'Hyrule Dungeon South Abyss North Edge', Open).dir(No, 0x82, None, Low).edge(0, A, 0x10),
create_door(player, 'Hyrule Dungeon South Abyss West Edge', Open).dir(We, 0x82, None, Low).edge(3, Z, 0x10),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', Open).dir(No, 0x82, None, High).edge(1, A, 0x08),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', Open).dir(We, 0x82, None, High).edge(4, A, 0x18),
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', Open).dir(Ea, 0x81, None, High).edge(3, S, 0x10),
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', Open).dir(Ea, 0x81, None, High).edge(4, X, 0x18),
create_door(player, 'Hyrule Dungeon Guardroom N', Nrml).dir(No, 0x81, Left, Low).pos(0),
create_door(player, 'Hyrule Dungeon Armory S', Nrml).dir(So, 0x71, Left, Low).trap(0x2).pos(1),
create_door(player, 'Hyrule Dungeon Armory ES', Intr).dir(Ea, 0x71, Left, Low).pos(2),
@@ -95,7 +95,7 @@ def create_doors(world, player):
create_door(player, 'Sewers Rope Room North Stairs', StrS).dir(No, 0x42, Mid, High),
create_door(player, 'Sewers Dark Cross South Stairs', StrS).dir(So, 0x32, Mid, High),
create_door(player, 'Sewers Dark Cross Key Door N', Nrml).dir(No, 0x32, Mid, High).small_key().pos(0),
create_door(player, 'Sewers Dark Cross Key Door S', Nrml).dir(So, 0x22, Mid, High).small_key().pos(0),
create_door(player, 'Sewers Water S', Nrml).dir(So, 0x22, Mid, High).small_key().pos(0),
create_door(player, 'Sewers Water W', Nrml).dir(We, 0x22, Bot, High).pos(1),
create_door(player, 'Sewers Key Rat E', Nrml).dir(Ea, 0x21, Bot, High).pos(1),
create_door(player, 'Sewers Key Rat Key Door N', Nrml).dir(No, 0x21, Right, High).small_key().pos(0),
@@ -178,44 +178,44 @@ def create_doors(world, player):
create_door(player, 'Eastern Boss SE', Nrml).dir(So, 0xc8, Right, High).no_exit().trap(0x4).pos(0),
# Desert Palace
create_door(player, 'Desert Main Lobby NW Edge', Open).dir(No, 0x84, None, High),
create_door(player, 'Desert Main Lobby N Edge', Open).dir(No, 0x84, None, High),
create_door(player, 'Desert Main Lobby NE Edge', Open).dir(No, 0x84, None, High),
create_door(player, 'Desert Main Lobby E Edge', Open).dir(Ea, 0x84, None, High),
create_door(player, 'Desert Main Lobby NW Edge', Open).dir(No, 0x84, None, High).edge(3, A, 0x20),
create_door(player, 'Desert Main Lobby N Edge', Open).dir(No, 0x84, None, High).edge(4, A, 0xa0),
create_door(player, 'Desert Main Lobby NE Edge', Open).dir(No, 0x84, None, High).edge(5, S, 0x20),
create_door(player, 'Desert Main Lobby E Edge', Open).dir(Ea, 0x84, None, High).edge(5, S, 0xa0),
create_door(player, 'Desert Main Lobby Left Path', Lgcl),
create_door(player, 'Desert Main Lobby Right Path', Lgcl),
create_door(player, 'Desert Left Alcove Path', Lgcl),
create_door(player, 'Desert Right Alcove Path', Lgcl),
create_door(player, 'Desert Dead End Edge', Open).dir(So, 0x74, None, High),
create_door(player, 'Desert East Wing W Edge', Open).dir(We, 0x85, None, High),
create_door(player, 'Desert East Wing N Edge', Open).dir(No, 0x85, None, High),
create_door(player, 'Desert Dead End Edge', Open).dir(So, 0x74, None, High).edge(4, Z, 0xa0),
create_door(player, 'Desert East Wing W Edge', Open).dir(We, 0x85, None, High).edge(5, A, 0xa0),
create_door(player, 'Desert East Wing N Edge', Open).dir(No, 0x85, None, High).edge(6, A, 0x20),
create_door(player, 'Desert East Lobby WS', Intr).dir(We, 0x85, Bot, High).pos(3),
create_door(player, 'Desert East Wing ES', Intr).dir(Ea, 0x85, Bot, High).pos(3),
create_door(player, 'Desert East Wing Key Door EN', Intr).dir(Ea, 0x85, Top, High).small_key().pos(1),
create_door(player, 'Desert Compass Key Door WN', Intr).dir(We, 0x85, Top, High).small_key().pos(1),
create_door(player, 'Desert Compass NW', Nrml).dir(No, 0x85, Right, High).trap(0x4).pos(0),
create_door(player, 'Desert Cannonball S', Nrml).dir(So, 0x75, Right, High).pos(1),
create_door(player, 'Desert Arrow Pot Corner S Edge', Open).dir(So, 0x75, None, High),
create_door(player, 'Desert Arrow Pot Corner W Edge', Open).dir(We, 0x75, None, High),
create_door(player, 'Desert Arrow Pot Corner S Edge', Open).dir(So, 0x75, None, High).edge(6, Z, 0x20),
create_door(player, 'Desert Arrow Pot Corner W Edge', Open).dir(We, 0x75, None, High).edge(2, Z, 0x20),
create_door(player, 'Desert Arrow Pot Corner NW', Intr).dir(No, 0x75, Left, High).pos(0),
create_door(player, 'Desert Trap Room SW', Intr).dir(So, 0x75, Left, High).pos(0),
create_door(player, 'Desert North Hall SE Edge', Open).dir(So, 0x74, None, High),
create_door(player, 'Desert North Hall SW Edge', Open).dir(So, 0x74, None, High),
create_door(player, 'Desert North Hall W Edge', Open).dir(We, 0x74, None, High),
create_door(player, 'Desert North Hall E Edge', Open).dir(Ea, 0x74, None, High),
create_door(player, 'Desert North Hall SE Edge', Open).dir(So, 0x74, None, High).edge(5, X, 0x20),
create_door(player, 'Desert North Hall SW Edge', Open).dir(So, 0x74, None, High).edge(3, Z, 0x20),
create_door(player, 'Desert North Hall W Edge', Open).dir(We, 0x74, None, High).edge(2, Z, 0x20),
create_door(player, 'Desert North Hall E Edge', Open).dir(Ea, 0x74, None, High).edge(1, X, 0x20),
create_door(player, 'Desert North Hall NW', Intr).dir(No, 0x74, Left, High).pos(1),
create_door(player, 'Desert Map SW', Intr).dir(So, 0x74, Left, High).pos(1),
create_door(player, 'Desert North Hall NE', Intr).dir(No, 0x74, Right, High).pos(0),
create_door(player, 'Desert Map SE', Intr).dir(So, 0x74, Right, High).pos(0),
create_door(player, 'Desert Sandworm Corner S Edge', Open).dir(So, 0x73, None, High),
create_door(player, 'Desert Sandworm Corner E Edge', Open).dir(Ea, 0x73, None, High),
create_door(player, 'Desert Sandworm Corner S Edge', Open).dir(So, 0x73, None, High).edge(2, X, 0x20),
create_door(player, 'Desert Sandworm Corner E Edge', Open).dir(Ea, 0x73, None, High).edge(1, X, 0x20),
create_door(player, 'Desert Sandworm Corner NE', Intr).dir(No, 0x73, Right, High).pos(2),
create_door(player, 'Desert Bonk Torch SE', Intr).dir(So, 0x73, Right, High).pos(2),
create_door(player, 'Desert Sandworm Corner WS', Intr).dir(We, 0x73, Bot, High).pos(1),
create_door(player, 'Desert Circle of Pots ES', Intr).dir(Ea, 0x73, Bot, High).pos(1),
create_door(player, 'Desert Circle of Pots NW', Intr).dir(No, 0x73, Left, High).pos(0),
create_door(player, 'Desert Big Chest SW', Intr).dir(So, 0x73, Left, High).pos(0),
create_door(player, 'Desert West Wing N Edge', Open).dir(No, 0x83, None, High),
create_door(player, 'Desert West Wing N Edge', Open).dir(No, 0x83, None, High).edge(2, S, 0x20),
create_door(player, 'Desert West Wing WS', Intr).dir(We, 0x83, Bot, High).pos(2),
create_door(player, 'Desert West Lobby ES', Intr).dir(Ea, 0x83, Bot, High).pos(2),
create_door(player, 'Desert West Lobby NW', Intr).dir(No, 0x83, Left, High).pos(0),
@@ -537,26 +537,26 @@ def create_doors(world, player):
create_door(player, 'Skull Final Drop WS', Intr).dir(We, 0x39, Bot, High).small_key().pos(1),
create_door(player, 'Skull Final Drop Hole', Hole),
create_door(player, 'Thieves Lobby N Edge', Open).dir(No, 0xdb, None, Low),
create_door(player, 'Thieves Lobby NE Edge', Open).dir(No, 0xdb, None, Low),
create_door(player, 'Thieves Lobby N Edge', Open).dir(No, 0xdb, None, Low).edge(7, A, 0x10),
create_door(player, 'Thieves Lobby NE Edge', Open).dir(No, 0xdb, None, Low).edge(8, S, 0x18),
create_door(player, 'Thieves Lobby E', Nrml).dir(Ea, 0xdb, Mid, High).no_exit().trap(0x4).pos(0),
create_door(player, 'Thieves Big Chest Nook WS Edge', Open).dir(We, 0xdb, None, Low),
create_door(player, 'Thieves Ambush S Edge', Open).dir(So, 0xcb, None, Low),
create_door(player, 'Thieves Ambush SE Edge', Open).dir(So, 0xcb, None, Low),
create_door(player, 'Thieves Ambush ES Edge', Open).dir(Ea, 0xcb, None, Low),
create_door(player, 'Thieves Ambush EN Edge', Open).dir(Ea, 0xcb, None, Low),
create_door(player, 'Thieves Big Chest Nook ES Edge', Open).dir(Ea, 0xdb, None, Low).edge(8, X, 0x50),
create_door(player, 'Thieves Ambush S Edge', Open).dir(So, 0xcb, None, Low).edge(7, Z, 0x10),
create_door(player, 'Thieves Ambush SE Edge', Open).dir(So, 0xcb, None, Low).edge(8, X, 0x18),
create_door(player, 'Thieves Ambush ES Edge', Open).dir(Ea, 0xcb, None, Low).edge(6, X, 0x50),
create_door(player, 'Thieves Ambush EN Edge', Open).dir(Ea, 0xcb, None, Low).edge(7, S, 0x50),
create_door(player, 'Thieves Ambush E', Nrml).dir(Ea, 0xcb, Mid, High).pos(0),
create_door(player, 'Thieves BK Corner WN Edge', Open).dir(We, 0xcc, None, Low),
create_door(player, 'Thieves BK Corner WS Edge', Open).dir(We, 0xcc, None, Low),
create_door(player, 'Thieves BK Corner S Edge', Open).dir(So, 0xcc, None, Low),
create_door(player, 'Thieves BK Corner SW Edge', Open).dir(So, 0xcc, None, Low),
create_door(player, 'Thieves BK Corner WN Edge', Open).dir(We, 0xcc, None, Low).edge(7, A, 0x50),
create_door(player, 'Thieves BK Corner WS Edge', Open).dir(We, 0xcc, None, Low).edge(6, Z, 0x50),
create_door(player, 'Thieves BK Corner S Edge', Open).dir(So, 0xcc, None, Low).edge(10, Z, 0x10),
create_door(player, 'Thieves BK Corner SW Edge', Open).dir(So, 0xcc, None, Low).edge(9, Z, 0x18),
create_door(player, 'Thieves Rail Ledge Drop Down', Lgcl),
create_door(player, 'Thieves Rail Ledge W', Nrml).dir(We, 0xcc, Mid, High).pos(2),
create_door(player, 'Thieves Rail Ledge NW', Nrml).dir(No, 0xcc, Left, High).pos(1),
create_door(player, 'Thieves BK Corner NE', Nrml).dir(No, 0xcc, Right, High).big_key().pos(0),
create_door(player, 'Thieves Compass Room NW Edge', Open).dir(No, 0xdc, None, Low),
create_door(player, 'Thieves Compass Room N Edge', Open).dir(No, 0xdc, None, Low),
create_door(player, 'Thieves Compass Room WS Edge', Open).dir(We, 0xdc, None, Low),
create_door(player, 'Thieves Compass Room NW Edge', Open).dir(No, 0xdc, None, Low).edge(9, A, 0x18),
create_door(player, 'Thieves Compass Room N Edge', Open).dir(No, 0xdc, None, Low).edge(10, A, 0x10),
create_door(player, 'Thieves Compass Room WS Edge', Open).dir(We, 0xdc, None, Low).edge(8, Z, 0x50),
create_door(player, 'Thieves Compass Room W', Nrml).dir(We, 0xdc, Mid, High).pos(0),
create_door(player, 'Thieves Hallway SE', Nrml).dir(So, 0xbc, Right, High).small_key().pos(1),
create_door(player, 'Thieves Hallway NE', Nrml).dir(No, 0xbc, Right, High).pos(7),
@@ -589,8 +589,8 @@ def create_doors(world, player):
create_door(player, 'Thieves Attic Down Stairs', Sprl).dir(Dn, 0x64, 0, HTH).ss(Z, 0x11, 0x80, True, True),
create_door(player, 'Thieves Attic ES', Intr).dir(Ea, 0x64, Bot, High).pos(0),
create_door(player, 'Thieves Cricket Hall Left WS', Intr).dir(We, 0x64, Bot, High).pos(0),
create_door(player, 'Thieves Cricket Hall Left Edge', Open).dir(Ea, 0x64, None, High),
create_door(player, 'Thieves Cricket Hall Right Edge', Open).dir(We, 0x65, None, High),
create_door(player, 'Thieves Cricket Hall Left Edge', Open).dir(Ea, 0x64, None, High).edge(0, X, 0x30),
create_door(player, 'Thieves Cricket Hall Right Edge', Open).dir(We, 0x65, None, High).edge(0, Z, 0x30),
create_door(player, 'Thieves Cricket Hall Right ES', Intr).dir(Ea, 0x65, Bot, High).pos(0),
create_door(player, 'Thieves Attic Window WS', Intr).dir(We, 0x65, Bot, High).pos(0),
create_door(player, 'Thieves Basement Block Up Stairs', Sprl).dir(Up, 0x45, 0, HTH).ss(A, 0x1a, 0x6c, True, True),
@@ -1231,7 +1231,7 @@ def create_paired_doors(world, player):
PairedDoor('PoD Falling Bridge WN', 'PoD Dark Maze EN', True), # Pod Dark maze door
PairedDoor('PoD Dark Maze E', 'PoD Big Chest Balcony W', True), # PoD Bombable by Big Chest
PairedDoor('PoD Arena Main NW', 'PoD Falling Bridge SW', True), # Pod key door by bridge
PairedDoor('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S', True),
PairedDoor('Sewers Dark Cross Key Door N', 'Sewers Water S', True),
PairedDoor('Swamp Hub WN', 'Swamp Crystal Switch EN', True), # Swamp key door crystal switch
PairedDoor('Swamp Hub North Ledge N', 'Swamp Push Statue S', True), # Swamp key door above big chest
PairedDoor('PoD Map Balcony WS', 'PoD Arena Ledge ES', True), # Pod bombable by arena

View File

@@ -520,6 +520,10 @@ type_map = {
Hook.South: Hook.North,
Hook.West: Hook.East,
Hook.East: Hook.West,
Hook.NEdge: Hook.SEdge,
Hook.SEdge: Hook.NEdge,
Hook.EEdge: Hook.WEdge,
Hook.WEdge: Hook.EEdge,
}
@@ -535,21 +539,31 @@ hang_dir_map = {
}
edge_map_back = {
Direction.North: Hook.SEdge,
Direction.South: Hook.NEdge,
Direction.West: Hook.EEdge,
Direction.East: Hook.WEdge,
}
def hanger_from_door(door):
if door.type == DoorType.SpiralStairs:
return Hook.Stairs
if door.type == DoorType.Normal:
return hang_dir_map[door.direction]
if door.type == DoorType.Open:
return edge_map_back[door.direction]
return None
def connect_doors(a, b):
# Return on unsupported types.
if a.type in [DoorType.Open, DoorType.StraightStairs, DoorType.Hole, DoorType.Warp, DoorType.Ladder,
if a.type in [DoorType.StraightStairs, DoorType.Hole, DoorType.Warp, DoorType.Ladder,
DoorType.Interior, DoorType.Logical]:
return
# Connect supported types
if a.type == DoorType.Normal or a.type == DoorType.SpiralStairs:
if a.type == DoorType.Normal or a.type == DoorType.SpiralStairs or a.type == DoorType.Open:
if a.blocked:
connect_one_way(b.entrance, a.entrance)
elif b.blocked:
@@ -798,6 +812,8 @@ class ExplorationState(object):
def add_all_doors_check_keys(self, region, key_door_proposal, world, player):
for door in get_doors(world, region, player):
if self.can_traverse(door):
if door.controller:
door = door.controller
if door in key_door_proposal and door not in self.opened_doors:
if not self.in_door_list(door, self.small_doors):
self.append_door_to_list(door, self.small_doors)

View File

@@ -132,7 +132,6 @@ def fill_dungeons_restrictive(world, shuffled_locations):
# with shuffled dungeon items they are distributed as part of the normal item pool
for item in world.get_items():
if (item.smallkey and world.keyshuffle[item.player]) or (item.bigkey and world.bigkeyshuffle[item.player]):
all_state_base.collect(item, True)
item.advancement = True
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
item.priority = True
@@ -146,7 +145,8 @@ def fill_dungeons_restrictive(world, shuffled_locations):
sort_order = {"BigKey": 3, "SmallKey": 2}
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items, True)
fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items,
keys_in_itempool={player: not world.keyshuffle[player] for player in range(1, world.players+1)}, single_player_placement=True)
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],

View File

@@ -161,7 +161,7 @@ def distribute_items_staleness(world):
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s', [item.name for item in itempool], [location.name for location in fill_locations])
def fill_restrictive(world, base_state, locations, itempool, single_player_placement = False):
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool = None, single_player_placement = False):
def sweep_from_pool():
new_state = base_state.copy()
for item in itempool:
@@ -202,7 +202,7 @@ def fill_restrictive(world, base_state, locations, itempool, single_player_place
test_state = maximum_exploration_state
if (not single_player_placement or location.player == item_to_place.player)\
and location.can_fill(test_state, item_to_place, perform_access_check)\
and valid_key_placement(item_to_place, location, itempool, world):
and valid_key_placement(item_to_place, location, itempool if (keys_in_itempool and keys_in_itempool[item_to_place.player]) else world.itempool, world):
spot_to_fill = location
break
elif item_to_place.smallkey or item_to_place.bigkey:
@@ -290,7 +290,8 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# todo: crossed
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
fill_restrictive(world, world.state, fill_locations, progitempool)
fill_restrictive(world, world.state, fill_locations, progitempool,
keys_in_itempool={player: world.keyshuffle[player] for player in range(1, world.players+1)})
random.shuffle(fill_locations)

View File

@@ -443,7 +443,7 @@ def fill_prizes(world, attempts=15):
prize_locs = list(empty_crystal_locations)
random.shuffle(prizepool)
random.shuffle(prize_locs)
fill_restrictive(world, all_state, prize_locs, prizepool, True)
fill_restrictive(world, all_state, prize_locs, prizepool, single_player_placement=True)
except FillError as e:
logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, attempts - attempt - 1)
for location in empty_crystal_locations:

View File

@@ -232,7 +232,7 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'Sewers Behind Tapestry', 'Hyrule Castle', None, ['Sewers Behind Tapestry S', 'Sewers Behind Tapestry Down Stairs']),
create_dungeon_region(player, 'Sewers Rope Room', 'Hyrule Castle', None, ['Sewers Rope Room Up Stairs', 'Sewers Rope Room North Stairs']),
create_dungeon_region(player, 'Sewers Dark Cross', 'Hyrule Castle', ['Sewers - Dark Cross'], ['Sewers Dark Cross Key Door N', 'Sewers Dark Cross South Stairs']),
create_dungeon_region(player, 'Sewers Water', 'Hyrule Castle', None, ['Sewers Dark Cross Key Door S', 'Sewers Water W']),
create_dungeon_region(player, 'Sewers Water', 'Hyrule Castle', None, ['Sewers Water S', 'Sewers Water W']),
create_dungeon_region(player, 'Sewers Key Rat', 'Hyrule Castle', ['Hyrule Castle - Key Rat Key Drop'], ['Sewers Key Rat E', 'Sewers Key Rat Key Door N']),
create_dungeon_region(player, 'Sewers Secret Room Blocked Path', 'Hyrule Castle', None, ['Sewers Secret Room Up Stairs']),
create_dungeon_region(player, 'Sewers Rat Path', 'Hyrule Castle', None, ['Sewers Secret Room Key Door S', 'Sewers Secret Room Push Block', 'Sewers Rat Path WS', 'Sewers Rat Path WN']),
@@ -460,7 +460,7 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'Thieves Rail Ledge', 'Thieves\' Town', None, ['Thieves Rail Ledge NW', 'Thieves Rail Ledge W', 'Thieves Rail Ledge Drop Down']),
create_dungeon_region(player, 'Thieves BK Corner', 'Thieves\' Town', None, ['Thieves BK Corner WN Edge', 'Thieves BK Corner WS Edge', 'Thieves BK Corner S Edge', 'Thieves BK Corner SW Edge', 'Thieves BK Corner NE']),
create_dungeon_region(player, 'Thieves Compass Room', 'Thieves\' Town', ['Thieves\' Town - Compass Chest'], ['Thieves Compass Room NW Edge', 'Thieves Compass Room N Edge', 'Thieves Compass Room WS Edge', 'Thieves Compass Room W']),
create_dungeon_region(player, 'Thieves Big Chest Nook', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest'], ['Thieves Big Chest Nook WS Edge']),
create_dungeon_region(player, 'Thieves Big Chest Nook', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest'], ['Thieves Big Chest Nook ES Edge']),
create_dungeon_region(player, 'Thieves Hallway', 'Thieves\' Town', ['Thieves\' Town - Hallway Pot Key'], ['Thieves Hallway SE', 'Thieves Hallway NE', 'Thieves Hallway WN', 'Thieves Hallway WS']),
create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', ['Revealing Light', 'Thieves\' Town - Boss', 'Thieves\' Town - Prize'], ['Thieves Boss SE']),
create_dungeon_region(player, 'Thieves Pot Alcove Mid', 'Thieves\' Town', None, ['Thieves Pot Alcove Mid ES', 'Thieves Pot Alcove Mid WS']),

6
Rom.py
View File

@@ -605,7 +605,7 @@ def patch_rom(world, rom, player, team, enemized):
rom.write_byte(0x139004, 1)
for door in world.doors:
if door.dest is not None and door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs]:
rom.write_bytes(door.getAddress(), door.dest.getTarget(door.toggle))
rom.write_bytes(door.getAddress(), door.dest.getTarget(door))
for room in world.rooms:
if room.player == player and room.modified:
rom.write_bytes(room.address(), room.rom_data())
@@ -899,7 +899,7 @@ def patch_rom(world, rom, player, team, enemized):
ERtimeincrease = 20
if world.keyshuffle[player] or world.bigkeyshuffle[player] or world.mapshuffle[player]:
ERtimeincrease = ERtimeincrease + 15
if world.clock_mode == 'off':
if world.clock_mode == 'none':
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32)
@@ -1157,7 +1157,7 @@ def patch_rom(world, rom, player, team, enemized):
rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] else 0x00) # maps showing crystals on overworld
# compasses showing dungeon count
if world.clock_mode != 'off' or world.dungeon_counters[player] == 'off':
if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off':
rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location
elif world.dungeon_counters[player] == 'on':
rom.write_byte(0x18003C, 0x02) # always on

View File

@@ -732,7 +732,7 @@ def no_glitches_rules(world, player):
'Sewers Dark Cross': {'sewer': True, 'entrances': ['Sewers Dark Cross Key Door N', 'Sewers Dark Cross South Stairs'], 'locations': ['Sewers - Dark Cross']},
'Sewers Behind Tapestry': {'sewer': True, 'entrances': ['Sewers Behind Tapestry S', 'Sewers Behind Tapestry Down Stairs'], 'locations': []},
'Sewers Rope Room': {'sewer': True, 'entrances': ['Sewers Rope Room Up Stairs', 'Sewers Rope Room North Stairs'], 'locations': []},
'Sewers Water': {'sewer': True, 'entrances': ['Sewers Dark Cross Key Door S', 'Sewers Water W'], 'locations': []},
'Sewers Water': {'sewer': True, 'entrances': ['Sewers Water S', 'Sewers Water W'], 'locations': []},
'Sewers Key Rat': {'sewer': True, 'entrances': ['Sewers Key Rat E', 'Sewers Key Rat Key Door N'], 'locations': ['Hyrule Castle - Key Rat Key Drop']},
}

View File

@@ -60,3 +60,24 @@ door_pair_offset_table = {
0xc9: 0x01e7, 0xcb: 0x01ec, 0xcc: 0x01ed, 0xce: 0x01f0, 0xd0: 0x01f1, 0xd1: 0x01f3, 0xd2: 0x01f7, 0xd5: 0x01f8,
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x020
}
multiply_lookup = {
0x08: {0x8: 1, 0x10: 2, 0x18: 3, 0x20: 4, 0x30: 6, 0x50: 0xa, 0xa0: 0x14},
0x10: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 2, 0x30: 3, 0x50: 0x4, 0xa0: 0xa},
0x18: {0x8: 1, 0x10: 2, 0x18: 1, 0x20: 4, 0x30: 2, 0x50: 0xa, 0xa0: 0x14},
0x20: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 1, 0x30: 3, 0x50: 5, 0xa0: 5},
0x30: {0x8: 1, 0x10: 1, 0x18: 1, 0x20: 2, 0x30: 1, 0x50: 5, 0xa0: 0xa},
0x50: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 2, 0x30: 3, 0x50: 1, 0xa0: 2},
0xa0: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 1, 0x30: 3, 0x50: 1, 0xa0: 1},
}
divisor_lookup = {
0x08: {0x8: 1, 0x10: 1, 0x18: 1, 0x20: 1, 0x30: 1, 0x50: 1, 0xa0: 1},
0x10: {0x8: 2, 0x10: 1, 0x18: 2, 0x20: 1, 0x30: 1, 0x50: 1, 0xa0: 1},
0x18: {0x8: 3, 0x10: 3, 0x18: 1, 0x20: 3, 0x30: 1, 0x50: 3, 0xa0: 3},
0x20: {0x8: 4, 0x10: 2, 0x18: 4, 0x20: 1, 0x30: 2, 0x50: 2, 0xa0: 1},
0x30: {0x8: 6, 0x10: 3, 0x18: 2, 0x20: 3, 0x30: 1, 0x50: 3, 0xa0: 3},
0x50: {0x8: 0xa, 0x10: 4, 0x18: 0xa, 0x20: 5, 0x30: 5, 0x50: 1, 0xa0: 1},
0xa0: {0x8: 0x14, 0x10: 0xa, 0x18: 0x14, 0x20: 5, 0x30: 0xa, 0x50: 2, 0xa0: 1},
}

View File

@@ -3,6 +3,7 @@ import os
import re
import subprocess
import sys
import xml.etree.ElementTree as ET
def int16_as_bytes(value):
value = value & 0xFFFF
@@ -239,6 +240,46 @@ def print_wiki_doors(d_regions, world, player):
print('|}')
def print_xml_doors(d_regions, world, player):
root = ET.Element('root')
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)
dungeon = ET.SubElement(root, 'dungeon', {'name': d})
for tile, r_list in tile_map.items():
supertile = ET.SubElement(dungeon, 'supertile', {'id': str(tile)})
for region in r_list:
room = ET.SubElement(supertile, 'room', {'name': region.name})
for ext in region.exits:
ET.SubElement(room, 'door', {'name': ext.name})
ET.dump(root)
def print_graph(world):
root = ET.Element('root')
for region in world.regions:
r = ET.SubElement(root, 'region', {'name': region.name})
for ext in region.exits:
attribs = {'name': ext.name}
if ext.connected_region:
attribs['connected_region'] = ext.connected_region.name
if ext.door and ext.door.dest:
attribs['dest'] = ext.door.dest.name
ET.SubElement(r, 'exit', attribs)
ET.dump(root)
if __name__ == '__main__':
pass
# make_new_base2current()

View File

@@ -1,9 +1,11 @@
!add = "clc : adc"
!sub = "sec : sbc"
!bge = "bcs"
!blt = "bcc"
; Free RAM notes
; Normal doors use $FE for scrolling indicator
; Normal doors use $AB to store the trap door indicator
; Normal doors use $AB-AC for scrolling indicator
; Normal doors use $FE to store the trap door indicator
; Spiral doors use $045e to store stair type
; Gfx uses $b1 to for sub-sub-sub-module thing
@@ -17,6 +19,8 @@ incsrc spiral.asm
incsrc gfx.asm
incsrc keydoors.asm
incsrc overrides.asm
;incsrc edges.asm
;incsrc math.asm
warnpc $279000
; Data Section

View File

@@ -489,3 +489,55 @@ dw $0000,$0000
dw $0000
dw $0000,$0000,$0000,$0000
dw $ffff ; indicates the end - we can drop this
; Edge Transition Table
org $27C500 ;ends around 27C5F0
NorthOpenEdge:
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
SouthOpenEdge:
db $83,$a2,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
WestOpenEdge:
db $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
EastOpenEdge:
db $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
db $00,$80,$11, $00,$80,$11, $00,$80,$11
; Edge Info Table (Midpoint, Width, Min Coord)
NorthEdgeInfo:
db $a8,$10,$a0, $2c,$08,$28 ;HC
db $b8,$20,$a8 ; DP West Wing
db $38,$20,$28, $f8,$a0,$a8, $b8,$20,$a8 ; DP Main
db $78,$20,$68 ; DP East Wing
db $f8,$10,$f0, $7c,$18,$70 ; TT Lobby
db $74,$18,$68, $f8,$10,$f0 ; TT Compass
SouthEdgeInfo:
db $a8,$10,$a0, $2c,$08,$28 ; HC
db $b8,$20,$a8 ; DP Sandworm
db $38,$20,$28, $f8,$a0,$a8, $b8,$20,$a8 ; DP North Hall & Dead End
db $78,$20,$68 ; DP Arrow Pot
db $f8,$10,$f0, $7c,$18,$70 ; TT Ambush
db $74,$18,$68, $f8,$10,$f0 ; TT BK Corner
WestEdgeInfo:
db $78,$30,$60 ; TT Attic
db $40,$20,$30 ; DP North Hall
db $40,$20,$30 ; DP Arrow Pot
db $84,$18,$78, $68,$10,$60 ; HC South
db $a0,$a0,$50 ; DP East Wing
db $58,$50,$30, $98,$50,$70 ; TT BK Corner
db $58,$50,$30 ; TT Compass
EastEdgeInfo:
db $78,$30,$60 ; TT Attic
db $40,$20,$30 ; DP Sandworm
db $40,$20,$30 ; DP North Hall
db $68,$10,$60, $84,$18,$78 ; HC Guards
db $a0,$a0,$50 ; DP Main Lobby
db $58,$50,$30, $98,$50,$70 ; TT Ambush
db $58,$50,$30 ; TT Nook
MultDivInfo: ; (1placeholder, 1, 2, 3, 4, 5, 6, 10, 20)
db $01, $01, $02, $03, $04, $05, $06, $0a, $14

291
asm/edges.asm Normal file
View File

@@ -0,0 +1,291 @@
HorzEdge:
cpy #$ff : beq +
jsr DetectWestEdge : bra ++
+ jsr DetectEastEdge
++ cmp #$ff : beq +
sta $00 : asl : !add $00 : tax
cpy #$ff : beq ++
jsr LoadWestData : bra .main
++ jsr LoadEastData
.main
jsr LoadEdgeRoomHorz
sec : rts
+ clc : rts
VertEdge:
cpy #$ff : beq +
jsr DetectNorthEdge : bra ++
+ jsr DetectSouthEdge
++ cmp #$ff : beq +
sta $00 : asl : !add $00 : tax
cpy #$ff : beq ++
jsr LoadNorthData : bra .main
++ jsr LoadSouthData
.main
jsr LoadEdgeRoomVert
sec : rts
+ clc : rts
LoadEdgeRoomHorz:
lda $03 : sta $a0
sty $09
and.b #$0f : asl a : !sub $23 : !add $09 : sta $02
ldy #$00 : jsr ShiftVariablesMainDir
lda $a0 : and.b #$F0 : lsr #3 : sta $0603 : inc : sta $0607
lda $aa : asl : tax ; current quad as 0/4
lda $04 : and #$40 : bne +
lda $603 : sta $00 : stz $01 : bra ++
+ lda $607 : sta $00 : lda #$02 : sta $01
++ ; $01 now contains 0 or 2
lda $00 : sta $21 : sta $0601 : sta $0605
lda $01 : sta $aa : lsr : sta $01 : stz $00
lda $0a : sta $20
stz $0e
rep #$30
lda $e8 : and #$01ff : sta $02
lda $0a : and #$00ff : !add $00 : sta $00
cmp #$006c : !bge +
lda #$0077 : bra ++
+ cmp #$017c : !blt +
lda #$0187 : bra ++
+ !add #$000b
++ sta $0618 : inc #2 : sta $061a
lda $00 : cmp #$0078 : !bge +
lda #$0000 : bra ++
+ cmp #$0178 : !blt +
lda #$0100 : bra ++
+ !sub #$0078
++ sta $00
; figures out scroll amt
cmp $02 : bne +
lda #$0000 : bra .done
+ !blt +
!sub $02 : inc $0e : bra .done
+ lda $02 : !sub $00
.done sta $ab : sep #$30
lda $0e : asl : ora $ac : sta $ac
lda $0603, x : sta $e9
lda $04 : and #$80 : lsr #4 : sta $ee ; layer stuff
rts
LoadEdgeRoomVert:
lda $03 : sta $a0
sty $09
and.b #$f0 : lsr #3 : !sub $21 : !add $09 : sta $02
ldy #$01 : jsr ShiftVariablesMainDir
lda $a0 : and.b #$0f : asl : sta $060b : inc : sta $060f
lda $a9 : asl #2 : tax ; current quad as 0/4
lda $04 : and #$20 : bne +
lda $60b : sta $00 : stz $01 : bra ++
+ lda $60f : sta $00 : lda #$01 : sta $01
++ ; $01 now contains 0 or 1
lda $00 : sta $23 : sta $0609 : sta $060d
lda $01 : sta $a9 : stz $00 ; setup for 16 bit ops
lda $0a : sta $22
stz $0e ; pos/neg indicator
rep #$30
lda $e2 : and #$01ff : sta $02
lda $0a : and #$00ff : !add $00 : sta $00
cmp #$0078 : !bge +
lda #$007f : bra ++
+ cmp #$0178 : !blt +
lda #$017f : bra ++
+ !add #$0007
++ sta $061c : inc #2 : sta $061e
lda $00 : cmp #$0078 : !bge +
lda #$0000 : bra ++
+ cmp #$0178 : !blt +
lda #$0100 : bra ++
+ !sub #$0078
++ sta $00
; figures out scroll amt
cmp $02 : bne +
lda #$0000 : bra .done
+ !blt +
!sub $02 : inc $0e : bra .done
+ lda $02 : !sub $00
.done sta $ab : sep #$30
lda $0e : asl : ora $ac : sta $ac
lda $060b, x : sta $e3
lda $04 : and #$10 : lsr #4 : sta $ee ; layer stuff
rts
LoadNorthData:
lda NorthEdgeInfo, x : sta $06 ; not needed I think
lda NorthOpenEdge, x : sta $03 : inx
lda NorthEdgeInfo, x : sta $07 ;probably needed for maths - unsure
lda NorthOpenEdge, x : sta $04 : inx
lda NorthEdgeInfo, x : sta $08 ; needed for maths
lda NorthOpenEdge, x : sta $05
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
lda SouthEdgeInfo, x : sta $0a : inx ; needed now, and for nrml transition
lda SouthEdgeInfo, x : sta $0b : inx ; probably not needed - unsure
lda SouthEdgeInfo, x : sta $0c ; needed for maths
rts
LoadSouthData:
lda SouthEdgeInfo, x : sta $06
lda SouthOpenEdge, x : sta $03 : inx
lda SouthEdgeInfo, x : sta $07
lda SouthOpenEdge, x : sta $04 : inx
lda SouthEdgeInfo, x : sta $08
lda SouthOpenEdge, x : sta $05
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
lda NorthEdgeInfo, x : sta $0a : inx
lda NorthEdgeInfo, x : sta $0b : inx
lda NorthEdgeInfo, x : sta $0c
rts
LoadWestData:
lda WestEdgeInfo, x : sta $06
lda WestOpenEdge, x : sta $03 : inx
lda WestEdgeInfo, x : sta $07
lda WestOpenEdge, x : sta $04 : inx
lda WestEdgeInfo, x : sta $08
lda WestOpenEdge, x : sta $05
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
lda EastEdgeInfo, x : sta $0a : inx
lda EastEdgeInfo, x : sta $0b : inx
lda EastEdgeInfo, x : sta $0c
rts
LoadEastData:
lda EastEdgeInfo, x : sta $06
lda EastOpenEdge, x : sta $03 : inx
lda EastEdgeInfo, x : sta $07
lda EastOpenEdge, x : sta $04 : inx
lda EastEdgeInfo, x : sta $08
lda EastOpenEdge, x : sta $05
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
lda WestEdgeInfo, x : sta $0a : inx
lda WestEdgeInfo, x : sta $0b : inx
lda WestEdgeInfo, x : sta $0c
rts
DetectNorthEdge:
ldx #$ff
lda $a2
cmp #$82 : bne +
lda $22 : cmp #$50 : bcs ++
ldx #$01 : bra .end
++ ldx #$00 : bra .end
+ cmp #$83 : bne +
ldx #$02 : bra .end
+ cmp #$84 : bne +
lda $a9 : beq ++
lda $22 : cmp #$78 : bcs +++
ldx #$04 : bra .end
+++ ldx #$05 : bra .end
++ lda $22 : cmp #$78 : bcs ++
ldx #$03 : bra .end
++ ldx #$04 : bra .end
+ cmp #$85 : bne +
ldx #$06 : bra .end
+ cmp #$db : bne +
lda $a9 : beq ++
lda $22 : beq ++
ldx #$08 : bra .end
++ ldx #$07 : bra .end
+ cmp #$dc : bne .end
lda $a9 : bne ++
lda $22 : cmp #$b0 : bcs ++
ldx #$09 : bra .end
++ ldx #$0a
.end txa : rts
DetectSouthEdge:
ldx #$ff
lda $a2
cmp #$72 : bne +
lda $22 : cmp #$50 : bcs ++
ldx #$01 : bra .end
++ ldx #$00 : bra .end
+ cmp #$73 : bne +
ldx #$02 : bra .end
+ cmp #$74 : bne +
lda $a9 : beq ++
lda $22 : cmp #$78 : bcs +++
ldx #$04 : bra .end
+++ ldx #$05 : bra .end
++ lda $22 : cmp #$78 : bcs ++
ldx #$03 : bra .end
++ ldx #$04 : bra .end
+ cmp #$75 : bne +
ldx #$06 : bra .end
+ cmp #$cb : bne +
lda $a9 : beq ++
lda $22 : beq ++
ldx #$08 : bra .end
++ ldx #$07 : bra .end
+ cmp #$cc : bne .end
lda $a9 : bne ++
lda $22 : cmp #$b0 : bcs ++
ldx #$09 : bra .end
++ ldx #$0a
.end txa : rts
DetectWestEdge:
ldx #$ff
lda $a2
cmp #$65 : bne +
ldx #$00 : bra .end
+ cmp #$74 : bne +
ldx #$01 : bra .end
+ cmp #$75 : bne +
ldx #$02 : bra .end
+ cmp #$82 : bne +
lda $aa : beq ++
ldx #$03 : bra .end
++ ldx #$04 : bra .end
+ cmp #$85 : bne +
ldx #$05 : bra .end
+ cmp #$cc : bne +
lda $aa : beq ++
ldx #$07 : bra .end
++ ldx #$06 : bra .end
+ cmp #$dc : bne .end
ldx #$08
.end txa : rts
DetectEastEdge:
ldx #$ff
lda $a2
cmp #$64 : bne +
ldx #$00 : bra .end
+ cmp #$73 : bne +
ldx #$01 : bra .end
+ cmp #$74 : bne +
ldx #$02 : bra .end
+ cmp #$81 : bne +
lda $aa : beq ++
ldx #$04 : bra .end
++ ldx #$03 : bra .end
+ cmp #$84 : bne +
ldx #$05 : bra .end
+ cmp #$cb : bne +
lda $aa : beq ++
ldx #$07 : bra .end
++ ldx #$06 : bra .end
+ cmp #$db : bne .end
ldx #$08
.end txa : rts

61
asm/math.asm Normal file
View File

@@ -0,0 +1,61 @@
;divide by 2 example
; 0 1 2 3 4 5 6 7 8 9 a b c d e f 10--Offset Ruler
;v 00 01 01 02 02 03 03 04 04 04 05 05 06 06 07 07 08
;divide by 3 example
; 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18
;00 00 01 01 01 02 02 02 03 03 03 04 04 04 05 05 05 06 06 06 07 07 07 08 08
MultiplyByY:
.loop cpy #$0001 : beq .done
cpy #$0003 : bne ++
jsr MultiBy3 : bra .done
++ cpy #$0005 : bne ++
jsr MultiBy5 : bra .done
++ asl : sta $00 : tya : lsr : tay : lda $00 : bra .loop
.done rts
;todo -- width in X?
DivideByY:
.loop cpy #$0001 : beq .done
cpy #$0003 : bne ++
jsr DivideBy3 : bra .done
++ cpy #$0005 : bne ++
jsr DivideBy5 : bra .done
; todo -- alter - width
++ tyx : jsr DivideBy2 : sta $00 : tya : lsr : tay : lda $00 : bra .loop
.done rts
MultiBy3:
sta $00 : asl : !add $00
rts
MultiBy5:
sta $00 : asl #2 : !add $00
rts
;width of divison in x
DivideBy2:
sta $00
lsr : bcc .done
sta $02 : txa : lsr : cmp $00 : !bge .done
lda $02 : inc
.done rts
DivideBy3:
sta $00
ldx #$0000
lda #$0002
.loop cmp $00 : !bge .store
inx : !add #$0003 : bra .loop
.store txa
rts
DivideBy5:
sta $00
ldx #$0000
lda #$0003
.loop cmp $00 : !bge .store
inx : !add #$0005 : bra .loop
.store txa
rts

View File

@@ -1,4 +1,5 @@
WarpLeft:
lda DRMode : beq .end
lda $040c : cmp.b #$ff : beq .end
lda $20 : ldx $aa
jsr CalcIndex
@@ -9,6 +10,7 @@ WarpLeft:
rtl
WarpRight:
lda DRMode : beq .end
lda $040c : cmp.b #$ff : beq .end
lda $20 : ldx $aa
jsr CalcIndex
@@ -19,6 +21,7 @@ WarpRight:
rtl
WarpUp:
lda DRMode : beq .end
lda $040c : cmp.b #$ff : beq .end
lda $22 : ldx $a9
jsr CalcIndex
@@ -29,6 +32,7 @@ WarpUp:
rtl
WarpDown:
lda DRMode : beq .end
lda $040c : cmp.b #$ff : beq .end
lda $22 : ldx $a9
jsr CalcIndex
@@ -39,11 +43,11 @@ WarpDown:
rtl
TrapDoorFixer:
lda $ab : and #$0038 : beq .end
lda $fe : and #$0038 : beq .end
xba : asl #2 : sta $00
stz $0468 : lda $068c : ora $00 : sta $068c
.end
stz $ab ; clear our ab here because we don't need it anymore
stz $fe ; clear our ab here because we don't need it anymore
rts
Cleanup:
@@ -72,7 +76,9 @@ LoadRoomHorz:
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00
lda $01 : and.b #$80 : cmp #$80 : bne .gtg
pla : sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
; jsr HorzEdge : pla : bcs .end
pla
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
.gtg ;Good to Go!
pla ; Throw away normal room (don't fill up the stack)
@@ -84,7 +90,8 @@ LoadRoomHorz:
jsr ShiftQuad
jsr ShiftCameraBounds
ldy #$01 : jsr ShiftVariablesSubDir ; flip direction
lda $01 : sta $ab : and #$04 : lsr #2
jsr SetupScrollIndicator
lda $01 : sta $fe : and #$04 : lsr #2
sta $ee
lda $01 : and #$10 : beq .end : stz $0468
.end
@@ -100,8 +107,9 @@ LoadRoomVert:
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00
lda $01 : and.b #$80 : cmp #$80 : bne .gtg
pla : sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
; jsr VertEdge : pla : bcs .end
pla
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
.gtg ;Good to Go!
pla ; Throw away normal room (don't fill up the stack)
lda $a0 : and.b #$F0 : lsr #3 : !sub $21 : !add $06 : sta $02
@@ -111,13 +119,20 @@ LoadRoomVert:
jsr ShiftQuad
jsr ShiftCameraBounds
ldy #$00 : jsr ShiftVariablesSubDir ; flip direction
lda $01 : sta $ab : and #$04 : lsr #2
jsr SetupScrollIndicator
lda $01 : sta $fe : and #$04 : lsr #2
sta $ee
.end
plb ; restore db register
rts
}
SetupScrollIndicator:
lda $ab : and #$01 : asl : sta $ac
lda $ab : and #$40 : clc : rol #3 : ora $ac : sta $ac
lda $ab : and #$20 : asl #2 : sta $ab
rts
LookupNewRoom: ; expects data offset to be in A
{
rep #$30 : and #$00FF ;sanitize A reg (who knows what is in the high byte)
@@ -132,7 +147,7 @@ LookupNewRoom: ; expects data offset to be in A
rts
}
; INPUTS-- X: Direction Index , $02: Shift Value
; INPUTS-- Y: Direction Index , $02: Shift Value
; Sets high bytes of various registers
ShiftVariablesMainDir:
{
@@ -152,7 +167,7 @@ ShiftLowCoord:
{
lda $01 : and.b #$03 ; high byte index
jsr CalcOpposingShift
lda $fe : and.b #$f0 : cmp.b #$20 : bne .lowDone
lda $ab : and.b #$f0 : cmp.b #$20 : bne .lowDone
lda OppCoordIndex,y : tax
lda #$80 : !add $20,x : sta $20,x
.lowDone
@@ -160,13 +175,13 @@ ShiftLowCoord:
}
; expects A to be (0,1,2) (dest number) and (0,1,2) (src door number) to be stored in $04
; $0127 will be set to a bitmask aaaa qxxf
; $ab will be set to a bitmask aaaa qxxf
; a - amount of adjust
; f - flag, if set, then amount is pos, otherwise neg.
; q - quadrant, if set, then quadrant needs to be modified
CalcOpposingShift:
{
stz $fe ; set up (can you zero out 127 alone?)
stz $ab : stz $ac ; set up
cmp.b $04 : beq .noOffset ; (equal, no shifts to do)
phy : tay ; reserve these
lda $04 : tax : tya : !sub $04 : sta $04 : cmp.b #$00 : bpl .shiftPos
@@ -174,8 +189,8 @@ CalcOpposingShift:
cpx.b #$01 : beq .skipNegQuad
ora #$08
.skipNegQuad
sta $fe : lda $04 : cmp.b #$FE : beq .done ;already set $0127
lda $fe : eor #$60
sta $ab : lda $04 : cmp.b #$FE : beq .done ;already set $ab
lda $ab : eor #$60
bra .setDone
.shiftPos
@@ -183,10 +198,10 @@ CalcOpposingShift:
cpy.b #$01 : beq .skipPosQuad
ora #$08
.skipPosQuad
sta $fe : lda $04 : cmp.b #$02 : bcs .done ;already set $0127
lda $fe : eor #$60
sta $ab : lda $04 : cmp.b #$02 : bcs .done ;already set $ab
lda $ab : eor #$60
.setDone sta $fe
.setDone sta $ab
.done ply
.noOffset rts
}
@@ -194,9 +209,9 @@ CalcOpposingShift:
ShiftQuad:
{
lda $fe : and #$08 : cmp.b #$00 : beq .quadDone
lda $ab : and #$08 : beq .quadDone
lda ShiftQuadIndex,y : tax ; X should be set to either 1 (vertical) or 2 (horizontal) (for a9,aa quadrant)
lda $fe : and #$01 : cmp.b #$00 : beq .decQuad
lda $ab : and #$01 : beq .decQuad
inc $02
txa : sta $a8, x ; alter a9/aa
bra .quadDone
@@ -224,8 +239,8 @@ ShiftCameraBounds:
{
lda CamBoundIndex,y : tax ; should be 0 for horz travel (vert bounds) or 4 for vert travel (horz bounds)
rep #$30
lda $fe : and #$00f0 : asl #2 : sta $06
lda $fe : and #$0001 : cmp #$0000 : beq .subIt
lda $ab : and #$00f0 : asl #2 : sta $06
lda $ab : and #$0001 : cmp #$0000 : beq .subIt
lda $0618, x : !add $06 : sta $0618, x
lda $061A, x : !add $06 : sta $061A, x
sep #$30
@@ -239,19 +254,26 @@ ShiftCameraBounds:
AdjustTransition:
{
lda $fe : and #$00f0 : lsr
sep #$20 : cmp $0126 : bcc .reset
rep #$20
lda $ab : and #$01ff : beq .reset
phy : ldy #$06 ; operating on vertical registers during horizontal trans
cpx.b #$02 : bcs .horizontalScrolling
ldy #$00 ; operate on horizontal regs during vert trans
.horizontalScrolling
lda $fe : and #$0001 : asl : tax
lda.l OffsetTable,x : adc $00E2,y : and.w #$FFFE : sta $00E2,y : sta $00E0,y
cmp #$0008 : bcs +
pha : lda $ab : and #$0200 : beq ++
pla : bra .add
++ pla : eor #$ffff : inc ; convert to negative
.add jsr AdjustCamAdd : ply : bra .reset
+ lda $ab : and #$0200 : xba : tax
lda.l OffsetTable,x : jsr AdjustCamAdd
lda $ab : !sub #$0008 : sta $ab
ply : bra .done
.reset ; clear the 0127 variable so to not disturb intra-tile doors
stz $fe
.reset ; clear the $ab variable so to not disturb intra-tile doors
stz $ab
.done
rep #$20 : lda $00 : and #$01fc
lda $00 : and #$01fc
rtl
}
AdjustCamAdd:
!add $00E2,y : sta $00E2,y : sta $00E0,y : rts

View File

@@ -49,7 +49,7 @@ def custom_page(top, parent):
for key in dictWidgets:
self.customWidgets[key] = dictWidgets[key]
for i, key in enumerate(CONST.CUSTOMITEMS):
self.customWidgets[key].storageVar.set(top.settings["customitemarray"][i])
for key in CONST.CUSTOMITEMS:
self.customWidgets[key].storageVar.set(top.settings["customitemarray"][key])
return self

View File

@@ -37,9 +37,10 @@
"selectbox": {
"side": "right"
},
"default": "Off"
"default": "Auto"
},
"options": {
"Auto": "default",
"Off": "off",
"On": "on",
"On Compass Pickup": "pickup"