Crossed dungeon palette refinement
Fixed some entrances that require reset otherwise Fixed TR lobbies that need to be bombed Fixed animated tiles in lobbies Fixed wallmaster+lamp problem Fixed some key rules that caused item requirements to be ignored Fixed Old Man cave to be properly one-way in the graph Fixed some odd key logic issues
This commit is contained in:
@@ -129,6 +129,7 @@ class World(object):
|
|||||||
|
|
||||||
set_player_attr('keydropshuffle', False)
|
set_player_attr('keydropshuffle', False)
|
||||||
set_player_attr('mixed_travel', 'prevent')
|
set_player_attr('mixed_travel', 'prevent')
|
||||||
|
set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False});
|
||||||
|
|
||||||
def get_name_string_for_object(self, obj):
|
def get_name_string_for_object(self, obj):
|
||||||
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
|
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
|
||||||
|
|||||||
158
DoorShuffle.py
158
DoorShuffle.py
@@ -398,6 +398,14 @@ def choose_portals(world, player):
|
|||||||
if not hera_door.entranceFlag:
|
if not hera_door.entranceFlag:
|
||||||
world.get_room(0x77, player).change(0, DoorKind.NormalLow2)
|
world.get_room(0x77, player).change(0, DoorKind.NormalLow2)
|
||||||
|
|
||||||
|
# tr rock bomb entrances
|
||||||
|
for portal in world.dungeon_portals[player]:
|
||||||
|
if not portal.destination and not portal.deadEnd:
|
||||||
|
if portal.door.name == 'TR Lazy Eyes SE':
|
||||||
|
world.get_room(0x23, player).change(0, DoorKind.DungeonEntrance)
|
||||||
|
if portal.door.name == 'TR Eye Bridge SW':
|
||||||
|
world.get_room(0xd5, player).change(0, DoorKind.DungeonEntrance)
|
||||||
|
|
||||||
if not world.swamp_patch_required[player]:
|
if not world.swamp_patch_required[player]:
|
||||||
swamp_region = world.get_entrance('Swamp Palace', player).connected_region
|
swamp_region = world.get_entrance('Swamp Palace', player).connected_region
|
||||||
if swamp_region.name != 'Swamp Lobby':
|
if swamp_region.name != 'Swamp Lobby':
|
||||||
@@ -905,27 +913,13 @@ def cross_dungeon(world, player):
|
|||||||
state.visit_region(start_area)
|
state.visit_region(start_area)
|
||||||
state.add_all_doors_check_unattached(start_area, world, player)
|
state.add_all_doors_check_unattached(start_area, world, player)
|
||||||
explore_state(state, world, player)
|
explore_state(state, world, player)
|
||||||
if state.visited(sanctuary):
|
if state.visited_at_all(sanctuary):
|
||||||
reachable_portals.append(portal)
|
reachable_portals.append(portal)
|
||||||
world.sanc_portal[player] = random.choice(reachable_portals)
|
world.sanc_portal[player] = random.choice(reachable_portals)
|
||||||
|
|
||||||
for portal in world.dungeon_portals[player]:
|
check_entrance_fixes(world, player)
|
||||||
if portal.door.roomIndex >= 0:
|
|
||||||
room = world.get_room(portal.door.roomIndex, player)
|
|
||||||
if room.palette is None:
|
|
||||||
name = portal.door.entrance.parent_region.dungeon.name
|
|
||||||
room.palette = palette_map[name]
|
|
||||||
|
|
||||||
for name, builder in dungeon_builders.items():
|
palette_assignment(world, player)
|
||||||
for region in builder.master_sector.regions:
|
|
||||||
for ext in region.exits:
|
|
||||||
if ext.door and ext.door.roomIndex >= 0:
|
|
||||||
room = world.get_room(ext.door.roomIndex, player)
|
|
||||||
if room.palette is None:
|
|
||||||
room.palette = palette_map[name]
|
|
||||||
eastfairies = world.get_room(0x89, player)
|
|
||||||
eastfairies.palette = palette_map[world.get_region('Eastern Courtyard', player).dungeon.name]
|
|
||||||
# other ones that could use programmatic treatment: Skull Boss x29, Hera Fairies xa7, Ice Boss xde
|
|
||||||
|
|
||||||
refine_hints(dungeon_builders)
|
refine_hints(dungeon_builders)
|
||||||
|
|
||||||
@@ -1026,6 +1020,91 @@ def reassign_boss(boss_region, boss_key, builder, gt, world, player):
|
|||||||
new_dungeon.bosses[boss_key] = gt_boss
|
new_dungeon.bosses[boss_key] = gt_boss
|
||||||
|
|
||||||
|
|
||||||
|
def check_entrance_fixes(world, player):
|
||||||
|
# I believe these modes will be fine
|
||||||
|
if world.shuffle[player] not in ['insanity', 'insanity_legacy', 'madness_legacy']:
|
||||||
|
checks = {
|
||||||
|
'Palace of Darkness': 'pod',
|
||||||
|
'Skull Woods Final Section': 'sw',
|
||||||
|
'Turtle Rock': 'tr',
|
||||||
|
'Ganons Tower': 'gt',
|
||||||
|
}
|
||||||
|
if world.mode[player] == 'inverted':
|
||||||
|
del checks['Ganons Tower']
|
||||||
|
for ent_name, key in checks.items():
|
||||||
|
entrance = world.get_entrance(ent_name, player)
|
||||||
|
if entrance.connected_region.dungeon:
|
||||||
|
layout = world.dungeon_layouts[player][entrance.connected_region.dungeon.name]
|
||||||
|
if 'Sanctuary' in layout.master_sector.region_set():
|
||||||
|
world.force_fix[player][key] = True
|
||||||
|
|
||||||
|
|
||||||
|
def palette_assignment(world, player):
|
||||||
|
for portal in world.dungeon_portals[player]:
|
||||||
|
if portal.door.roomIndex >= 0:
|
||||||
|
room = world.get_room(portal.door.roomIndex, player)
|
||||||
|
if room.palette is None:
|
||||||
|
name = portal.door.entrance.parent_region.dungeon.name
|
||||||
|
room.palette = palette_map[name][0]
|
||||||
|
|
||||||
|
for name, builder in world.dungeon_layouts[player].items():
|
||||||
|
for region in builder.master_sector.regions:
|
||||||
|
for ext in region.exits:
|
||||||
|
if ext.door and ext.door.roomIndex >= 0 and ext.door.name not in palette_non_influencers:
|
||||||
|
room = world.get_room(ext.door.roomIndex, player)
|
||||||
|
if room.palette is None:
|
||||||
|
room.palette = palette_map[name][0]
|
||||||
|
|
||||||
|
for name, tuple in palette_map.items():
|
||||||
|
if tuple[1] is not None:
|
||||||
|
door_name = boss_indicator[name][1]
|
||||||
|
door = world.get_door(door_name, player)
|
||||||
|
room = world.get_room(door.roomIndex, player)
|
||||||
|
room.palette = tuple[1]
|
||||||
|
if tuple[2]:
|
||||||
|
leading_door = world.get_door(tuple[2], player)
|
||||||
|
ent = next(iter(leading_door.entrance.parent_region.entrances))
|
||||||
|
if ent.door and door.roomIndex:
|
||||||
|
room = world.get_room(door.roomIndex, player)
|
||||||
|
room.palette = tuple[1]
|
||||||
|
|
||||||
|
|
||||||
|
rat_path = world.get_region('Sewers Rat Path', player)
|
||||||
|
visited_rooms = set()
|
||||||
|
visited_regions = {rat_path}
|
||||||
|
queue = deque([(rat_path, 0)])
|
||||||
|
while len(queue) > 0:
|
||||||
|
region, dist = queue.popleft()
|
||||||
|
if dist > 5:
|
||||||
|
continue
|
||||||
|
for ext in region.exits:
|
||||||
|
if ext.door and ext.door.roomIndex >= 0 and ext.door.name not in palette_non_influencers:
|
||||||
|
room_idx = ext.door.roomIndex
|
||||||
|
if room_idx not in visited_rooms:
|
||||||
|
room = world.get_room(room_idx, player)
|
||||||
|
room.palette = 0x1
|
||||||
|
visited_rooms.add(room_idx)
|
||||||
|
if ext.door and ext.door.type in [DoorType.SpiralStairs, DoorType.Ladder]:
|
||||||
|
if ext.door.dest and ext.door.dest.roomIndex:
|
||||||
|
visited_rooms.add(ext.door.dest.roomIndex)
|
||||||
|
if ext.connected_region:
|
||||||
|
visited_regions.add(ext.connected_region)
|
||||||
|
elif ext.connected_region and ext.connected_region.type == RegionType.Dungeon and ext.connected_region not in visited_regions:
|
||||||
|
queue.append((ext.connected_region, dist+1))
|
||||||
|
visited_regions.add(ext.connected_region)
|
||||||
|
|
||||||
|
for connection in ['Sanctuary S', 'Sanctuary N']:
|
||||||
|
adjacent = world.get_entrance(connection, player)
|
||||||
|
if adjacent.door.dest and adjacent.door.dest.entrance.parent_region.type == RegionType.Dungeon:
|
||||||
|
if adjacent.door and adjacent.door.dest and adjacent.door.dest.roomIndex >= 0:
|
||||||
|
room = world.get_room(adjacent.door.dest.roomIndex, player)
|
||||||
|
room.palette = 0x1d
|
||||||
|
|
||||||
|
eastfairies = world.get_room(0x89, player)
|
||||||
|
eastfairies.palette = palette_map[world.get_region('Eastern Courtyard', player).dungeon.name][0]
|
||||||
|
# other ones that could use programmatic treatment: Skull Boss x29, Hera Fairies xa7, Ice Boss xde (Ice Fairies!)
|
||||||
|
|
||||||
|
|
||||||
def refine_hints(dungeon_builders):
|
def refine_hints(dungeon_builders):
|
||||||
for name, builder in dungeon_builders.items():
|
for name, builder in dungeon_builders.items():
|
||||||
for region in builder.master_sector.regions:
|
for region in builder.master_sector.regions:
|
||||||
@@ -2525,14 +2604,49 @@ boss_indicator = {
|
|||||||
'Ganons Tower': (0x1a, 'GT Agahnim 2 SW')
|
'Ganons Tower': (0x1a, 'GT Agahnim 2 SW')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# tuples: (non-boss, boss)
|
||||||
|
# see Utils for other notes
|
||||||
palette_map = {
|
palette_map = {
|
||||||
'Hyrule Castle': 0x0, 'Eastern Palace': 0xb, 'Desert Palace': 0x4, 'Agahnims Tower': 0xc,
|
'Hyrule Castle': (0x0, None),
|
||||||
'Swamp Palace': 0x8, 'Palace of Darkness': 0x10, 'Misery Mire': 0x11, 'Skull Woods': 0xe,
|
'Eastern Palace': (0xb, None),
|
||||||
'Ice Palace': 0x14, 'Tower of Hera': 0x6, 'Thieves Town': 0x17, 'Turtle Rock': 0x19,
|
'Desert Palace': (0x9, 0x4, 'Desert Boss SW'),
|
||||||
'Ganons Tower': 0x1b
|
'Agahnims Tower': (0x0, 0xc, 'Tower Agahnim 1 SW'), # ancillary 0x26 for F1, F4
|
||||||
|
'Swamp Palace': (0xa, 0x8, 'Swamp Boss SW'),
|
||||||
|
'Palace of Darkness': (0xf, 0x10, 'PoD Boss SE'),
|
||||||
|
'Misery Mire': (0x11, 0x12, 'Mire Boss SW'),
|
||||||
|
'Skull Woods': (0xd, 0xe, 'Skull Spike Corner SW'),
|
||||||
|
'Ice Palace': (0x13, 0x14, 'Ice Antechamber NE'),
|
||||||
|
'Tower of Hera': (0x6, None),
|
||||||
|
'Thieves Town': (0x17, None), # the attic uses 0x23
|
||||||
|
'Turtle Rock': (0x18, 0x19, 'TR Boss SW'),
|
||||||
|
'Ganons Tower': (0x28, 0x1b, 'GT Agahnim 2 SW'), # other palettes: 0x1a (other) 0x24 (Gauntlet - Lanmo) 0x25 (conveyor-torch-wizzrode moldorm pit f5?)
|
||||||
}
|
}
|
||||||
|
|
||||||
# todo: inverted
|
# implications:
|
||||||
|
# pipe room -> where lava chest is
|
||||||
|
# dark alley -> where pod basement is
|
||||||
|
# conveyor star or hidden star -> where DMs room is
|
||||||
|
# falling bridge -> where Rando room is
|
||||||
|
# petting zoo -> where firesnake is
|
||||||
|
# basement cage -> where tile room is
|
||||||
|
# bob's room -> where big chest/hope/torch are
|
||||||
|
# invis bridges -> compass room
|
||||||
|
|
||||||
|
palette_non_influencers = {
|
||||||
|
'PoD Shooter Room Up Stairs', 'TR Lava Dual Pipes EN', 'TR Lava Dual Pipes WN', 'TR Lava Dual Pipes SW',
|
||||||
|
'TR Lava Escape SE', 'TR Lava Escape NW', 'PoD Arena Ledge ES', 'Swamp Big Key Ledge WN', 'Swamp Hub Dead Ledge EN',
|
||||||
|
'Swamp Map Ledge EN', 'Skull Pot Prison ES', 'Skull Pot Prison SE', 'PoD Dark Alley NE', 'GT Conveyor Star Pits EN',
|
||||||
|
'GT Hidden Star ES', 'GT Falling Bridge WN', 'GT Falling Bridge WS', 'GT Petting Zoo SE',
|
||||||
|
'Hera Basement Cage Up Stairs', "GT Bob's Room SE", 'GT Warp Maze (Pits) ES', 'GT Invisible Bridges WS',
|
||||||
|
'Mire Over Bridge E', 'Mire Over Bridge W', 'Eastern Courtyard Ledge S', 'Eastern Courtyard Ledge W',
|
||||||
|
'Eastern Courtyard Ledge E', 'Eastern Map Valley WN', 'Eastern Map Valley SW', 'Mire BK Door Room EN',
|
||||||
|
'Mire BK Door Room N', 'TR Tile Room SE', 'TR Tile Room NE', 'TR Refill SE', 'Eastern Cannonball Ledge WN',
|
||||||
|
'Eastern Cannonball Ledge Key Door EN', 'Mire Neglected Room SE', 'Mire Neglected Room NE', 'Mire Chest View NE',
|
||||||
|
'TR Compass Room NW', 'Desert Dead End Edge', 'Hyrule Dungeon South Abyss Catwalk North Edge',
|
||||||
|
'Hyrule Dungeon South Abyss Catwalk West Edge'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
portal_map = {
|
portal_map = {
|
||||||
'Sanctuary': ('Sanctuary', 'Sanctuary Exit'),
|
'Sanctuary': ('Sanctuary', 'Sanctuary Exit'),
|
||||||
'Hyrule Castle West': ('Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)'),
|
'Hyrule Castle West': ('Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)'),
|
||||||
|
|||||||
@@ -1067,7 +1067,7 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
|||||||
explorable_door = local_state.next_avail_door()
|
explorable_door = local_state.next_avail_door()
|
||||||
if explorable_door.door.bigKey:
|
if explorable_door.door.bigKey:
|
||||||
if bk_flag:
|
if bk_flag:
|
||||||
big_not_found = not special_big_key_found(local_state, world, player) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
big_not_found = not special_big_key_found(local_state) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
||||||
if big_not_found:
|
if big_not_found:
|
||||||
continue # we can't open this door
|
continue # we can't open this door
|
||||||
if explorable_door.door in proposed_map:
|
if explorable_door.door in proposed_map:
|
||||||
@@ -1083,9 +1083,11 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
|||||||
return local_state
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
def special_big_key_found(state, world, player):
|
def special_big_key_found(state):
|
||||||
cellblock = world.get_region('Hyrule Dungeon Cellblock', player)
|
for location in state.found_locations:
|
||||||
return state.visited(cellblock)
|
if location.forced_item and location.forced_item.bigkey:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def valid_region_to_explore_in_regions(region, all_regions, world, player):
|
def valid_region_to_explore_in_regions(region, all_regions, world, player):
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ def link_entrances(world, player):
|
|||||||
for exitname, regionname in mandatory_connections:
|
for exitname, regionname in mandatory_connections:
|
||||||
connect_simple(world, exitname, regionname, player)
|
connect_simple(world, exitname, regionname, player)
|
||||||
|
|
||||||
|
connect_custom(world, player)
|
||||||
|
|
||||||
# if we do not shuffle, set default connections
|
# if we do not shuffle, set default connections
|
||||||
if world.shuffle[player] == 'vanilla':
|
if world.shuffle[player] == 'vanilla':
|
||||||
for exitname, regionname in default_connections:
|
for exitname, regionname in default_connections:
|
||||||
@@ -1780,6 +1782,15 @@ def link_inverted_entrances(world, player):
|
|||||||
if world.get_entrance('Inverted Ganons Tower', player).connected_region.name != 'GT Lobby':
|
if world.get_entrance('Inverted Ganons Tower', player).connected_region.name != 'GT Lobby':
|
||||||
world.ganonstower_vanilla[player] = False
|
world.ganonstower_vanilla[player] = False
|
||||||
|
|
||||||
|
|
||||||
|
def connect_custom(world, player):
|
||||||
|
if hasattr(world, 'custom_entrances') and world.custom_entrances[player]:
|
||||||
|
for exit_name, region_name in world.custom_entrances[player]:
|
||||||
|
# doesn't actually change addresses
|
||||||
|
connect_simple(world, exit_name, region_name, player)
|
||||||
|
# this needs to remove custom connections from the pool
|
||||||
|
|
||||||
|
|
||||||
def connect_simple(world, exitname, regionname, player):
|
def connect_simple(world, exitname, regionname, player):
|
||||||
world.get_entrance(exitname, player).connect(world.get_region(regionname, player))
|
world.get_entrance(exitname, player).connect(world.get_region(regionname, player))
|
||||||
|
|
||||||
@@ -2863,6 +2874,7 @@ mandatory_connections = [('Links House S&Q', 'Links House'),
|
|||||||
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||||
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||||
('Death Mountain Return Ledge Drop', 'Light World'),
|
('Death Mountain Return Ledge Drop', 'Light World'),
|
||||||
|
('Old Man Cave Dropdown', 'Old Man Cave'),
|
||||||
('Old Man House Front to Back', 'Old Man House Back'),
|
('Old Man House Front to Back', 'Old Man House Back'),
|
||||||
('Old Man House Back to Front', 'Old Man House'),
|
('Old Man House Back to Front', 'Old Man House'),
|
||||||
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
||||||
@@ -2972,6 +2984,7 @@ inverted_mandatory_connections = [('Links House S&Q', 'Inverted Links House'),
|
|||||||
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||||
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||||
('Death Mountain Return Ledge Drop', 'Light World'),
|
('Death Mountain Return Ledge Drop', 'Light World'),
|
||||||
|
('Old Man Cave Dropdown', 'Old Man Cave'),
|
||||||
('Old Man House Front to Back', 'Old Man House Back'),
|
('Old Man House Front to Back', 'Old Man House Back'),
|
||||||
('Old Man House Back to Front', 'Old Man House'),
|
('Old Man House Back to Front', 'Old Man House'),
|
||||||
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ def create_inverted_regions(world, player):
|
|||||||
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||||
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
|
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
|
||||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
|
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
|
||||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']),
|
||||||
|
create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']),
|
||||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
||||||
|
|||||||
@@ -1389,7 +1389,7 @@ def create_key_counters(key_layout, world, player):
|
|||||||
if door.bigKey or door.name in special_big_key_doors:
|
if door.bigKey or door.name in special_big_key_doors:
|
||||||
key_layout.key_logic.bk_doors.add(door)
|
key_layout.key_logic.bk_doors.add(door)
|
||||||
# open the door, if possible
|
# open the door, if possible
|
||||||
if not door.bigKey or not child_state.big_key_special or child_state.visited(special_region):
|
if not door.bigKey or not child_state.big_key_special or child_state.visited_at_all(special_region):
|
||||||
open_a_door(door, child_state, flat_proposal)
|
open_a_door(door, child_state, flat_proposal)
|
||||||
expand_key_state(child_state, flat_proposal, world, player)
|
expand_key_state(child_state, flat_proposal, world, player)
|
||||||
code = state_id(child_state, key_layout.flat_prop)
|
code = state_id(child_state, key_layout.flat_prop)
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.2.0.9-u'
|
__version__ = '0.2.0.10u'
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -2,21 +2,22 @@
|
|||||||
|
|
||||||
* Lobby shuffle added as Intensity level 3
|
* Lobby shuffle added as Intensity level 3
|
||||||
* Can now be found in the spoiler
|
* Can now be found in the spoiler
|
||||||
|
* Palette changes:
|
||||||
|
* Certain doors/transition no longer have an effect on the palette choice (dead ends mostly or just bridges)
|
||||||
|
* Sanctuary palette back to the adjacent rooms to Sanctuary (sanctuary stays the dungeon color for now)
|
||||||
|
* Sewer palette comes back for part of Hyrule Castle for areas "near" the sewer dropdown
|
||||||
* Known issues:
|
* Known issues:
|
||||||
* If a dungeon is vanilla in ER and Sanc is in that dungeon and the dungeon has an entrance that needs to let link out: is broken.
|
* Palettes aren't perfect
|
||||||
* e.g. PoD, GT, TR
|
May add a way to turn off palette "fixing"
|
||||||
* Some TR lobbies that need a bomb aren't pre-opened.
|
|
||||||
* Palettes aren't perfect - may add Sanctuary and Sewer palette back. May add a way to turn off palette "fixing"
|
|
||||||
* Some ugly colors
|
* Some ugly colors
|
||||||
* Invisible floors can be see in many palettes
|
* Invisible floors can be see in many palettes
|
||||||
* Animated tiles aren't loaded correctly in lobbies
|
|
||||||
* If a wallmaster grabs you and the lobby is dark, the lamp doesn't turn on
|
|
||||||
* --keydropshuffle added (coming to the GUI soon). This add 33 new locations to the game where keys are found under pots
|
* --keydropshuffle added (coming to the GUI soon). This add 33 new locations to the game where keys are found under pots
|
||||||
and where enemies drop keys. This includes 32 small key location and the ball and chain guard who normally drop the HC
|
and where enemies drop keys. This includes 32 small key location and the ball and chain guard who normally drop the HC
|
||||||
Big Key.
|
Big Key.
|
||||||
* Overall location count updated
|
* Overall location count updated
|
||||||
* Setting mentioned in spoiler
|
* Setting mentioned in spoiler
|
||||||
* GT Big Key count / total location count needs to be updated
|
* Known issue:
|
||||||
|
* GT Big Key count needs to be updated
|
||||||
* --mixed_travel setting added
|
* --mixed_travel setting added
|
||||||
* Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
* Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
||||||
otherwise unconnected logically can be reach using these glitches. To prevent the player from unintentionally
|
otherwise unconnected logically can be reach using these glitches. To prevent the player from unintentionally
|
||||||
@@ -35,6 +36,16 @@ otherwise unconnected logically can be reach using these glitches. To prevent th
|
|||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
|
|
||||||
|
* 2.0.10u
|
||||||
|
* Fix POD, TR, GT and SKULL 3 entrance if sanc ends up in that dungeon in crossed ER+
|
||||||
|
* TR Lobbies that need a bomb and can be entered before bombing should be pre-opened
|
||||||
|
* Animated tiles are loaded correctly in lobbies
|
||||||
|
* If a wallmaster grabs you and the lobby is dark, the lamp turns on now
|
||||||
|
* Certain key rules no longer override item requirements (e.g. Somaria behind TR Hub)
|
||||||
|
* Old Man Cave is correctly one way in the graph
|
||||||
|
* Some key logic fixes
|
||||||
|
* 2.0.9-u
|
||||||
|
* /missing command in MultiClient fixed
|
||||||
* 2.0.8-u
|
* 2.0.8-u
|
||||||
* Player sprite disappears after picking up a key drop in keydropshuffle
|
* Player sprite disappears after picking up a key drop in keydropshuffle
|
||||||
* Sewers and Hyrule Castle compass problems
|
* Sewers and Hyrule Castle compass problems
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ def create_regions(world, player):
|
|||||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
|
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
|
||||||
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||||
|
|
||||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']),
|
||||||
|
create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']),
|
||||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
|
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
|
||||||
|
|||||||
17
Rom.py
17
Rom.py
@@ -24,7 +24,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '16fab813f3cbef58c66a47595a3858ee'
|
RANDOMIZERBASEHASH = 'e16cc8659527baecc02e3b49b83fa49b'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -621,8 +621,9 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
|
|
||||||
# setup dr option flags based on experimental, etc.
|
# setup dr option flags based on experimental, etc.
|
||||||
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
||||||
if world.experimental[player]:
|
if world.doorShuffle[player] == 'crossed':
|
||||||
dr_flags |= DROptions.Map_Info
|
dr_flags |= DROptions.Map_Info
|
||||||
|
if world.experimental[player]:
|
||||||
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':
|
||||||
@@ -703,9 +704,17 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
if portal.boss_exit_idx > -1:
|
if portal.boss_exit_idx > -1:
|
||||||
rom.write_byte(0x7939 + portal.boss_exit_idx, portal.current_room())
|
rom.write_byte(0x7939 + portal.boss_exit_idx, portal.current_room())
|
||||||
|
|
||||||
# fix skull woods exit, if not fixed during exit patching
|
world.force_fix[player]['sw'] |= world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla'
|
||||||
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
|
|
||||||
|
# fix exits, if not fixed during exit patching
|
||||||
|
if world.force_fix[player]['sw']:
|
||||||
write_int16(rom, 0x15DB5 + 2 * exit_ids['Skull Woods Final Section Exit'][1], 0x00F8)
|
write_int16(rom, 0x15DB5 + 2 * exit_ids['Skull Woods Final Section Exit'][1], 0x00F8)
|
||||||
|
if world.force_fix[player]['pod']:
|
||||||
|
write_int16(rom, 0x15DB5 + 2 * exit_ids['Palace of Darkness Exit'][1], 0x0640)
|
||||||
|
if world.force_fix[player]['tr']:
|
||||||
|
write_int16(rom, 0x15DB5 + 2 * exit_ids['Turtle Rock Exit (Front)'][1], 0x0134)
|
||||||
|
if world.force_fix[player]['gt']:
|
||||||
|
write_int16(rom, 0x15DB5 + 2 * exit_ids['Ganons Tower Exit'][1], 0x00A4)
|
||||||
|
|
||||||
write_custom_shops(rom, world, player)
|
write_custom_shops(rom, world, player)
|
||||||
|
|
||||||
|
|||||||
9
Rules.py
9
Rules.py
@@ -74,6 +74,10 @@ def add_rule(spot, rule, combine='and'):
|
|||||||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||||
|
|
||||||
|
|
||||||
|
def or_rule(rule1, rule2):
|
||||||
|
return lambda state: rule1(state) or rule2(state)
|
||||||
|
|
||||||
|
|
||||||
def add_lamp_requirement(spot, player):
|
def add_lamp_requirement(spot, player):
|
||||||
add_rule(spot, lambda state: state.has('Lamp', player, state.world.lamps_needed_for_dark_rooms))
|
add_rule(spot, lambda state: state.has('Lamp', player, state.world.lamps_needed_for_dark_rooms))
|
||||||
|
|
||||||
@@ -1563,9 +1567,10 @@ def add_key_logic_rules(world, player):
|
|||||||
for door_name, keys in d_logic.door_rules.items():
|
for door_name, keys in d_logic.door_rules.items():
|
||||||
spot = world.get_entrance(door_name, player)
|
spot = world.get_entrance(door_name, player)
|
||||||
if not world.retro[player] or world.mode[player] != 'standard' or not retro_in_hc(spot):
|
if not world.retro[player] or world.mode[player] != 'standard' or not retro_in_hc(spot):
|
||||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys))
|
rule = create_advanced_key_rule(d_logic, player, keys)
|
||||||
if keys.opposite:
|
if keys.opposite:
|
||||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys.opposite), 'or')
|
rule = or_rule(rule, create_advanced_key_rule(d_logic, player, keys.opposite))
|
||||||
|
add_rule(spot, rule)
|
||||||
for location in d_logic.bk_restricted:
|
for location in d_logic.bk_restricted:
|
||||||
if not location.forced_item:
|
if not location.forced_item:
|
||||||
forbid_item(location, d_logic.bk_name, player)
|
forbid_item(location, d_logic.bk_name, player)
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ nop #5
|
|||||||
org $01b618 ; Bank01.asm : 7963 Dungeon_LoadHeader (REP #$20 : INY : LDA [$0D], Y)
|
org $01b618 ; Bank01.asm : 7963 Dungeon_LoadHeader (REP #$20 : INY : LDA [$0D], Y)
|
||||||
nop : jsl OverridePaletteHeader
|
nop : jsl OverridePaletteHeader
|
||||||
|
|
||||||
|
org $02817e ; Bank02.asm : 414 (LDA $02811E, X)
|
||||||
|
jsl FixAnimatedTiles
|
||||||
|
|
||||||
|
org $028a06 ; Bank02.asm : 1941 Dungeon_ResetTorchBackgroundAndPlayer
|
||||||
|
JSL FixWallmasterLamp
|
||||||
|
|
||||||
org $00d377 ;Bank 00 line 3185
|
org $00d377 ;Bank 00 line 3185
|
||||||
DecompDungAnimatedTiles:
|
DecompDungAnimatedTiles:
|
||||||
org $00fda4 ;Bank 00 line 8882
|
org $00fda4 ;Bank 00 line 8882
|
||||||
|
|||||||
16
asm/gfx.asm
16
asm/gfx.asm
@@ -33,6 +33,22 @@ GfxFixer:
|
|||||||
rtl
|
rtl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FixAnimatedTiles:
|
||||||
|
LDA.L DRMode : cmp #$02 : bne +
|
||||||
|
PHX
|
||||||
|
LDX $A0 : LDA.l TilesetTable, x
|
||||||
|
CMP $0AA1 : beq ++
|
||||||
|
TAX : PLA : BRA +
|
||||||
|
++
|
||||||
|
PLX
|
||||||
|
+ LDA $02802E, X ; what we wrote over
|
||||||
|
RTL
|
||||||
|
|
||||||
|
FixWallmasterLamp:
|
||||||
|
ORA $0458
|
||||||
|
STY $1C : STA $1D : RTL ; what we wrote over
|
||||||
|
|
||||||
|
|
||||||
CgramAuxToMain: ; ripped this from bank02 because it ended with rts
|
CgramAuxToMain: ; ripped this from bank02 because it ended with rts
|
||||||
{
|
{
|
||||||
rep #$20
|
rep #$20
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user