Initial work for fixing standard (basic only so far)
This commit is contained in:
+19
-1
@@ -169,6 +169,19 @@ class World(object):
|
||||
return door
|
||||
return None
|
||||
|
||||
def check_for_entrance(self, entrance, player):
|
||||
if isinstance(entrance, Entrance):
|
||||
return entrance
|
||||
try:
|
||||
return self._entrance_cache[(entrance, player)]
|
||||
except KeyError:
|
||||
for region in self.regions:
|
||||
for ext in region.exits:
|
||||
if ext.name == entrance and ext.player == player:
|
||||
self._entrance_cache[(entrance, player)] = ext
|
||||
return ext
|
||||
return None
|
||||
|
||||
def get_room(self, room_idx, player):
|
||||
if isinstance(room_idx, Room):
|
||||
return room_idx
|
||||
@@ -835,6 +848,7 @@ class Entrance(object):
|
||||
self.vanilla = None
|
||||
self.access_rule = lambda state: True
|
||||
self.player = player
|
||||
self.door = None
|
||||
|
||||
def can_reach(self, state):
|
||||
if self.parent_region.can_reach(state) and self.access_rule(state):
|
||||
@@ -1020,7 +1034,7 @@ class CrystalBarrier(Enum):
|
||||
|
||||
|
||||
class Door(object):
|
||||
def __init__(self, player, name, type):
|
||||
def __init__(self, player, name, type, entrance=None):
|
||||
self.player = player
|
||||
self.name = name
|
||||
self.type = type
|
||||
@@ -1055,6 +1069,10 @@ class Door(object):
|
||||
self.dependents = []
|
||||
self.dead = False
|
||||
|
||||
self.entrance = entrance
|
||||
if entrance is not None:
|
||||
entrance.door = self
|
||||
|
||||
def getAddress(self):
|
||||
if self.type == DoorType.Normal:
|
||||
return 0x13A000 + normal_offset_table[self.roomIndex] * 24 + (self.doorIndex + self.direction.value * 3) * 2
|
||||
|
||||
+7
-6
@@ -1194,19 +1194,20 @@ def add_inaccessible_doors(world, player):
|
||||
create_door(world, player, 'Death Mountain Return Cave (West)', 'Death Mountain Return Ledge')
|
||||
if 'Desert Palace Lone Stairs' in world.inaccessible_regions[player]:
|
||||
create_door(world, player, 'Desert Palace Entrance (East)', 'Desert Palace Lone Stairs')
|
||||
if world.mode == 'standard' and 'Hyrule Castle Ledge' in world.inaccessible_regions[player]:
|
||||
create_door(world, player, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Ledge')
|
||||
create_door(world, player, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Ledge')
|
||||
# if world.mode == 'standard' and 'Hyrule Castle Ledge' in world.inaccessible_regions[player]:
|
||||
# create_door(world, player, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Ledge')
|
||||
# create_door(world, player, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Ledge')
|
||||
|
||||
|
||||
def create_door(world, player, entName, region_name):
|
||||
connect = world.get_entrance(entName, player).connected_region
|
||||
entrance = world.get_entrance(entName, player)
|
||||
connect = entrance.connected_region
|
||||
for ext in connect.exits:
|
||||
if ext.connected_region is not None and ext.connected_region.name == region_name:
|
||||
d = Door(player, ext.name, DoorType.Logical),
|
||||
d = Door(player, ext.name, DoorType.Logical, ext),
|
||||
world.doors += d
|
||||
connect_door_only(world, ext.name, ext.connected_region, player)
|
||||
d = Door(player, entName, DoorType.Logical),
|
||||
d = Door(player, entName, DoorType.Logical, entrance),
|
||||
world.doors += d
|
||||
connect_door_only(world, entName, connect, player)
|
||||
|
||||
|
||||
@@ -1069,6 +1069,10 @@ def create_doors(world, player):
|
||||
world.get_door('Swamp Drain Right Switch', player).event('Swamp Drain')
|
||||
world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain')
|
||||
|
||||
# if world.mode[player] == 'standard': # todo: multi
|
||||
if world.mode == 'standard':
|
||||
world.get_door('Hyrule Castle Throne Room N', player).event('Zelda Pickup')
|
||||
|
||||
# crystal switches and barriers
|
||||
world.get_door('Hera Lobby Down Stairs', player).c_switch()
|
||||
world.get_door('Hera Lobby Key Stairs', player).c_switch()
|
||||
@@ -1203,6 +1207,8 @@ def create_doors(world, 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))
|
||||
|
||||
assign_entrances(world, player)
|
||||
|
||||
|
||||
def create_paired_doors(world, player):
|
||||
world.paired_doors[player] = [
|
||||
@@ -1247,6 +1253,15 @@ def create_paired_doors(world, player):
|
||||
]
|
||||
|
||||
|
||||
def assign_entrances(world, player):
|
||||
for door in world.doors:
|
||||
if door.player == player:
|
||||
entrance = world.check_for_entrance(door.name, player)
|
||||
if entrance is not None:
|
||||
door.entrance = entrance
|
||||
entrance.door = door
|
||||
|
||||
|
||||
def controller_door(controller, dependent):
|
||||
dependent.controller = controller
|
||||
controller.dependents.append(dependent)
|
||||
|
||||
+64
-33
@@ -1,6 +1,6 @@
|
||||
import random
|
||||
import collections
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, deque
|
||||
from enum import Enum, unique
|
||||
import logging
|
||||
from functools import reduce
|
||||
@@ -34,13 +34,13 @@ class GraphPiece:
|
||||
def generate_dungeon(name, available_sectors, entrance_region_names, split_dungeon, world, player):
|
||||
logger = logging.getLogger('')
|
||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||
doors_to_connect = set()
|
||||
doors_to_connect = {}
|
||||
all_regions = set()
|
||||
bk_needed = False
|
||||
bk_special = False
|
||||
for sector in available_sectors:
|
||||
for door in sector.outstanding_doors:
|
||||
doors_to_connect.add(door)
|
||||
doors_to_connect[door.name] = door
|
||||
all_regions.update(sector.regions)
|
||||
bk_needed = bk_needed or determine_if_bk_needed(sector, split_dungeon, world, player)
|
||||
bk_special = bk_special or check_for_special(sector)
|
||||
@@ -51,7 +51,9 @@ def generate_dungeon(name, available_sectors, entrance_region_names, split_dunge
|
||||
backtrack = False
|
||||
itr = 0
|
||||
finished = False
|
||||
# last_choice = None
|
||||
# flag if standard and this is hyrule castle
|
||||
# std_flag = world.mode[player] == 'standard' and bk_special # todo: multi
|
||||
std_flag = world.mode == 'standard' and bk_special
|
||||
while not finished:
|
||||
# what are my choices?
|
||||
itr += 1
|
||||
@@ -61,7 +63,7 @@ def generate_dungeon(name, available_sectors, entrance_region_names, split_dunge
|
||||
dungeon, hangers, hooks = gen_dungeon_info(name, available_sectors, entrance_regions, proposed_map,
|
||||
doors_to_connect, bk_needed, bk_special, world, player)
|
||||
dungeon_cache[depth] = dungeon, hangers, hooks
|
||||
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed)
|
||||
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed, std_flag)
|
||||
else:
|
||||
dungeon, hangers, hooks = dungeon_cache[depth]
|
||||
valid = True
|
||||
@@ -98,7 +100,7 @@ def generate_dungeon(name, available_sectors, entrance_region_names, split_dunge
|
||||
queue = collections.deque(proposed_map.items())
|
||||
while len(queue) > 0:
|
||||
a, b = queue.pop()
|
||||
connect_doors(a, b, world, player)
|
||||
connect_doors(a, b)
|
||||
queue.remove((b, a))
|
||||
master_sector = available_sectors.pop()
|
||||
for sub_sector in available_sectors:
|
||||
@@ -136,7 +138,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, proposed_map, va
|
||||
for door in sector.outstanding_doors:
|
||||
if not door.stonewall and door not in proposed_map.keys():
|
||||
hanger_set.add(door)
|
||||
parent = parent_region(door, world, player).parent_region
|
||||
parent = door.entrance.parent_region
|
||||
init_state = ExplorationState(dungeon=name)
|
||||
init_state.big_key_special = start.big_key_special
|
||||
o_state = extend_reachable_state_improved([parent], init_state, proposed_map,
|
||||
@@ -198,7 +200,7 @@ def check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_do
|
||||
|
||||
|
||||
def explore_blue_state(door, dungeon, o_state, proposed_map, valid_doors, world, player):
|
||||
parent = parent_region(door, world, player).parent_region
|
||||
parent = door.entrance.parent_region
|
||||
blue_start = ExplorationState(CrystalBarrier.Blue, o_state.dungeon)
|
||||
blue_start.big_key_special = o_state.big_key_special
|
||||
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, valid_doors, False, world, player)
|
||||
@@ -269,7 +271,7 @@ def filter_choices(next_hanger, door, orig_hang, prev_choices, hook_candidates):
|
||||
return next_hanger != door and orig_hang != next_hanger and door not in hook_candidates
|
||||
|
||||
|
||||
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed):
|
||||
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed, std_flag):
|
||||
# evaluate if everything is still plausible
|
||||
|
||||
# only origin is left in the dungeon and not everything is connected
|
||||
@@ -300,7 +302,7 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
if len(must_hang[key]) > len(hooks[key]):
|
||||
return False
|
||||
outstanding_doors = defaultdict(list)
|
||||
for d in doors_to_connect:
|
||||
for d in doors_to_connect.values():
|
||||
if d not in proposed_map.keys():
|
||||
outstanding_doors[hook_from_door(d)].append(d)
|
||||
for key in outstanding_doors.keys():
|
||||
@@ -317,6 +319,8 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
return False
|
||||
if not bk_possible:
|
||||
return False
|
||||
if std_flag and not cellblock_valid(doors_to_connect, all_regions, proposed_map):
|
||||
return False
|
||||
new_hangers_found = True
|
||||
accessible_hook_types = []
|
||||
hanger_matching = set()
|
||||
@@ -344,6 +348,41 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
return len(all_hangers.difference(hanger_matching)) == 0
|
||||
|
||||
|
||||
def cellblock_valid(valid_doors, all_regions, proposed_map):
|
||||
cellblock = None
|
||||
for region in all_regions:
|
||||
if 'Hyrule Dungeon Cellblock' == region.name:
|
||||
cellblock = region
|
||||
break
|
||||
queue = deque([cellblock])
|
||||
visited = {cellblock}
|
||||
while len(queue) > 0:
|
||||
region = queue.popleft()
|
||||
if region.name == 'Sanctuary':
|
||||
return True
|
||||
for ext in region.exits:
|
||||
connect = ext.connected_region
|
||||
if connect is None and ext.name in valid_doors:
|
||||
door = valid_doors[ext.name]
|
||||
if not door.blocked:
|
||||
if door in proposed_map:
|
||||
new_region = proposed_map[door].entrance.parent_region
|
||||
if new_region not in visited:
|
||||
visited.add(new_region)
|
||||
queue.append(new_region)
|
||||
else:
|
||||
return True # outstanding connection possible
|
||||
elif connect is not None:
|
||||
door = ext.door
|
||||
if door is not None and not door.blocked and connect not in visited:
|
||||
visited.add(connect)
|
||||
queue.append(connect)
|
||||
return False # couldn't find an outstanding door or the sanctuary
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def winnow_hangers(hangers, hooks):
|
||||
removal_info = []
|
||||
for hanger, door_set in hangers.items():
|
||||
@@ -406,10 +445,6 @@ def filter_for_potential_bk_locations(locations):
|
||||
and x.name not in key_only_locations.keys() and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
||||
|
||||
|
||||
def parent_region(door, world, player):
|
||||
return world.get_entrance(door.name, player)
|
||||
|
||||
|
||||
def opposite_h_type(h_type):
|
||||
type_map = {
|
||||
Hook.Stairs: Hook.Stairs,
|
||||
@@ -450,7 +485,7 @@ def hanger_from_door(door):
|
||||
return None
|
||||
|
||||
|
||||
def connect_doors(a, b, world, player):
|
||||
def connect_doors(a, b):
|
||||
# Return on unsupported types.
|
||||
if a.type in [DoorType.Open, DoorType.StraightStairs, DoorType.Hole, DoorType.Warp, DoorType.Ladder,
|
||||
DoorType.Interior, DoorType.Logical]:
|
||||
@@ -458,28 +493,26 @@ def connect_doors(a, b, world, player):
|
||||
# Connect supported types
|
||||
if a.type == DoorType.Normal or a.type == DoorType.SpiralStairs:
|
||||
if a.blocked:
|
||||
connect_one_way(world, b.name, a.name, player)
|
||||
connect_one_way(b.entrance, a.entrance)
|
||||
elif b.blocked:
|
||||
connect_one_way(world, a.name, b.name, player)
|
||||
connect_one_way(a.entrance, b.entrance)
|
||||
else:
|
||||
connect_two_way(world, a.name, b.name, player)
|
||||
connect_two_way(a.entrance, b.entrance)
|
||||
dep_doors, target = [], None
|
||||
if len(a.dependents) > 0:
|
||||
dep_doors, target = a.dependents, b
|
||||
elif len(b.dependents) > 0:
|
||||
dep_doors, target = b.dependents, a
|
||||
if target is not None:
|
||||
target_region = world.get_entrance(target.name, player).parent_region
|
||||
target_region = target.entrance.parent_region
|
||||
for dep in dep_doors:
|
||||
connect_simple_door(dep, target_region, world, player)
|
||||
connect_simple_door(dep, target_region)
|
||||
return
|
||||
# If we failed to account for a type, panic
|
||||
raise RuntimeError('Unknown door type ' + a.type.name)
|
||||
|
||||
|
||||
def connect_two_way(world, entrancename, exitname, player):
|
||||
entrance = world.get_entrance(entrancename, player)
|
||||
ext = world.get_entrance(exitname, player)
|
||||
def connect_two_way(entrance, ext):
|
||||
|
||||
# if these were already connected somewhere, remove the backreference
|
||||
if entrance.connected_region is not None:
|
||||
@@ -491,17 +524,15 @@ def connect_two_way(world, entrancename, exitname, player):
|
||||
ext.connect(entrance.parent_region)
|
||||
if entrance.parent_region.dungeon:
|
||||
ext.parent_region.dungeon = entrance.parent_region.dungeon
|
||||
x = world.check_for_door(entrancename, player)
|
||||
y = world.check_for_door(exitname, player)
|
||||
x = entrance.door
|
||||
y = ext.door
|
||||
if x is not None:
|
||||
x.dest = y
|
||||
if y is not None:
|
||||
y.dest = x
|
||||
|
||||
|
||||
def connect_one_way(world, entrancename, exitname, player):
|
||||
entrance = world.get_entrance(entrancename, player)
|
||||
ext = world.get_entrance(exitname, player)
|
||||
def connect_one_way(entrance, ext):
|
||||
|
||||
# if these were already connected somewhere, remove the backreference
|
||||
if entrance.connected_region is not None:
|
||||
@@ -512,16 +543,16 @@ def connect_one_way(world, entrancename, exitname, player):
|
||||
entrance.connect(ext.parent_region)
|
||||
if entrance.parent_region.dungeon:
|
||||
ext.parent_region.dungeon = entrance.parent_region.dungeon
|
||||
x = world.check_for_door(entrancename, player)
|
||||
y = world.check_for_door(exitname, player)
|
||||
x = entrance.door
|
||||
y = ext.door
|
||||
if x is not None:
|
||||
x.dest = y
|
||||
if y is not None:
|
||||
y.dest = x
|
||||
|
||||
|
||||
def connect_simple_door(exit_door, region, world, player):
|
||||
world.get_entrance(exit_door.name, player).connect(region)
|
||||
def connect_simple_door(exit_door, region):
|
||||
exit_door.entrance.connect(region)
|
||||
exit_door.dest = region
|
||||
|
||||
|
||||
@@ -679,7 +710,7 @@ class ExplorationState(object):
|
||||
if self.can_traverse(door):
|
||||
if door.controller is not None:
|
||||
door = door.controller
|
||||
if door.dest is None and door not in proposed_map.keys() and door in valid_doors:
|
||||
if door.dest is None and door not in proposed_map.keys() and door.name in valid_doors.keys():
|
||||
if not self.in_door_list_ic(door, self.unattached_doors):
|
||||
self.append_door_to_list(door, self.unattached_doors, flag)
|
||||
else:
|
||||
|
||||
@@ -244,6 +244,7 @@ def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=No
|
||||
fill_locations.reverse()
|
||||
|
||||
# Make sure the escape small key is placed first in standard keysanity to prevent running out of spots
|
||||
# todo: crossed
|
||||
if world.keysanity and world.mode == 'standard':
|
||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' else 0)
|
||||
|
||||
|
||||
@@ -195,6 +195,15 @@ def generate_itempool(world, player):
|
||||
world.push_item(world.get_location('Ice Block Drop', player), ItemFactory('Convenient Block', player), False)
|
||||
world.get_location('Ice Block Drop', player).event = True
|
||||
world.get_location('Ice Block Drop', player).locked = True
|
||||
# if world.mode[player] == 'standard': todo: multi
|
||||
if world.mode == 'standard':
|
||||
world.push_item(world.get_location('Zelda Pickup', player), ItemFactory('Zelda Herself', player), False)
|
||||
world.get_location('Zelda Pickup', player).event = True
|
||||
world.get_location('Zelda Pickup', player).locked = True
|
||||
world.push_item(world.get_location('Zelda Drop Off', player), ItemFactory('Zelda Delivered', player), False)
|
||||
world.get_location('Zelda Drop Off', player).event = True
|
||||
world.get_location('Zelda Drop Off', player).locked = True
|
||||
|
||||
|
||||
# set up item pool
|
||||
if world.custom:
|
||||
|
||||
@@ -181,4 +181,6 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
|
||||
'Maiden Rescued': (True, False, 'Event', None, None, None, None, None, None, None, None),
|
||||
'Maiden Unmasked': (True, False, 'Event', None, None, None, None, None, None, None, None),
|
||||
'Convenient Block': (True, False, 'Event', None, None, None, None, None, None, None, None),
|
||||
'Zelda Herself': (True, False, 'Event', None, None, None, None, None, None, None, None),
|
||||
'Zelda Delivered': (True, False, 'Event', None, None, None, None, None, None, None, None),
|
||||
}
|
||||
|
||||
+11
-3
@@ -3,7 +3,8 @@ from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType
|
||||
|
||||
|
||||
def create_regions(world, player):
|
||||
|
||||
# std_flag = world.mode[player] == 'standard' # todo: multi
|
||||
std_flag = world.mode == 'standard'
|
||||
world.regions += [
|
||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
|
||||
@@ -219,7 +220,10 @@ def create_regions(world, player):
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory Boomerang', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Boomerang Guard Key Drop'], ['Hyrule Dungeon Armory Boomerang WS']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory North Branch', 'Hyrule Castle', None, ['Hyrule Dungeon Armory Interior Key Door S', 'Hyrule Dungeon Armory Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Staircase', 'Hyrule Castle', None, ['Hyrule Dungeon Staircase Up Stairs', 'Hyrule Dungeon Staircase Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'Hyrule Castle', ['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest'], ['Hyrule Dungeon Cellblock Up Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'Hyrule Castle',
|
||||
['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest'] if not std_flag else
|
||||
['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest', 'Zelda Pickup'],
|
||||
['Hyrule Dungeon Cellblock Up Stairs']),
|
||||
|
||||
|
||||
create_dungeon_region(player, 'Sewers Behind Tapestry', 'Hyrule Castle', None, ['Sewers Behind Tapestry S', 'Sewers Behind Tapestry Down Stairs']),
|
||||
@@ -233,7 +237,9 @@ def create_regions(world, player):
|
||||
['Sewers Secret Room ES', 'Sewers Secret Room EN']),
|
||||
create_dungeon_region(player, 'Sewers Yet More Rats', 'Hyrule Castle', None, ['Sewers Pull Switch Down Stairs', 'Sewers Yet More Rats S']),
|
||||
create_dungeon_region(player, 'Sewers Pull Switch', 'Hyrule Castle', None, ['Sewers Pull Switch N', 'Sewers Pull Switch S']),
|
||||
create_dungeon_region(player, 'Sanctuary', 'Hyrule Castle', ['Sanctuary'], ['Sanctuary Exit', 'Sanctuary N']),
|
||||
create_dungeon_region(player, 'Sanctuary', 'Hyrule Castle',
|
||||
['Sanctuary'] if not std_flag else ['Sanctuary', 'Zelda Drop Off'],
|
||||
['Sanctuary Exit', 'Sanctuary N']),
|
||||
|
||||
# Eastern Palace
|
||||
create_dungeon_region(player, 'Eastern Lobby', 'Eastern Palace', None, ['Eastern Lobby N', 'Eastern Palace Exit', 'Eastern Lobby NW', 'Eastern Lobby NE']),
|
||||
@@ -1094,6 +1100,8 @@ location_table = {'Mushroom': (0x180013, False, 'in the woods'),
|
||||
'Suspicious Maiden': (None, False, None),
|
||||
'Revealing Light': (None, False, None),
|
||||
'Ice Block Drop': (None, False, None),
|
||||
'Zelda Pickup': (None, False, None),
|
||||
'Zelda Drop Off': (None, False, None),
|
||||
'Eastern Palace - Prize': ([0x1209D, 0x53EF8, 0x53EF9, 0x180052, 0x18007C, 0xC6FE], True, 'Eastern Palace'),
|
||||
'Desert Palace - Prize': ([0x1209E, 0x53F1C, 0x53F1D, 0x180053, 0x180078, 0xC6FF], True, 'Desert Palace'),
|
||||
'Tower of Hera - Prize': ([0x120A5, 0x53F0A, 0x53F0B, 0x18005A, 0x18007A, 0xC706], True, 'Tower of Hera'),
|
||||
|
||||
@@ -19,7 +19,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = 'f4c6e0ad6aa3e36d2a16f4340d6e0c36'
|
||||
RANDOMIZERBASEHASH = '4e291b6f6227de32db2735423889a780'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
|
||||
@@ -116,8 +116,7 @@ def global_rules(world, player):
|
||||
|
||||
if world.mode == 'standard':
|
||||
world.get_region('Hyrule Castle Secret Entrance', player).can_reach_private = lambda state: True
|
||||
old_rule = world.get_region('Links House', player).can_reach_private
|
||||
world.get_region('Links House', player).can_reach_private = lambda state: state.can_reach('Sanctuary', 'Region', player) or old_rule(state)
|
||||
world.get_region('Links House', player).can_reach_private = lambda state: True
|
||||
else:
|
||||
# these are default save&quit points and always accessible
|
||||
world.get_region('Links House', player).can_reach_private = lambda state: True
|
||||
@@ -877,19 +876,19 @@ def inverted_rules(world, player):
|
||||
|
||||
def no_glitches_rules(world, player):
|
||||
if world.mode != 'inverted':
|
||||
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player))
|
||||
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to
|
||||
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player))
|
||||
add_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to
|
||||
add_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player)))
|
||||
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
|
||||
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
|
||||
add_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player))
|
||||
|
||||
# todo: move some dungeon rules to no glictes logic - see these for examples
|
||||
# add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
|
||||
@@ -1032,27 +1031,30 @@ def swordless_rules(world, player):
|
||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||
|
||||
|
||||
def standard_rules(world, player):
|
||||
# add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||
|
||||
# ensures the required weapon for escape lands on uncle (unless player has it pre-equipped)
|
||||
add_rule(world.get_location('Secret Passage', player), lambda state: state.can_kill_most_things(player))
|
||||
add_rule(world.get_location('Hyrule Castle - Map Chest', player), lambda state: state.can_kill_most_things(player))
|
||||
# these are because of rails
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.has('Zelda Delivered', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# too restrictive for crossed?
|
||||
def uncle_item_rule(item):
|
||||
copy_state = CollectionState(world)
|
||||
copy_state.collect(item)
|
||||
copy_state.sweep_for_events()
|
||||
return copy_state.can_reach('Sanctuary', 'Region', player)
|
||||
return copy_state.has('Zelda Delivered', player)
|
||||
|
||||
add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule)
|
||||
|
||||
# easiest way to enforce key placement not relevant for open
|
||||
set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.can_kill_most_things(player))
|
||||
# ensures the required weapon for escape lands on uncle (unless player has it pre-equipped)
|
||||
for location in ['Link\'s House', 'Sanctuary', 'Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||
'Sewers - Secret Room - Right']:
|
||||
add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player))
|
||||
add_rule(world.get_location('Secret Passage', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
# todo: in crossed these chest/key drops are not necessarily present
|
||||
add_rule(world.get_location('Hyrule Castle - Map Chest', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
@@ -1061,6 +1063,31 @@ def standard_rules(world, player):
|
||||
set_rule(world.get_location('Hyrule Castle - Key Rat Key Drop', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_entrance('Hyrule Dungeon Armory S', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
set_rule(world.get_location('Hyrule Castle - Big Key Drop', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Throne Room N', player), lambda state: state.has('Zelda Herself', player))
|
||||
set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player))
|
||||
|
||||
for location in ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest']:
|
||||
add_rule(world.get_location(location, player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# Bonk Fairy (Light) is a notable omission in ER shuffles/Retro
|
||||
for entrance in ['Blinds Hideout', 'Zoras River', 'Kings Grave Outer Rocks', 'Dam', 'Tavern North', 'Chicken House',
|
||||
'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blacksmiths Hut',
|
||||
'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge',
|
||||
'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lumberjack Tree Tree',
|
||||
'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier',
|
||||
'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Palace Stairs',
|
||||
'Eastern Palace', 'Master Sword Meadow', 'Sanctuary', 'Sanctuary Grave',
|
||||
'Death Mountain Entrance Rock', 'Flute Spot 1', 'Dark Desert Teleporter', 'East Hyrule Teleporter',
|
||||
'South Hyrule Teleporter', 'Kakariko Teleporter', 'Elder House (East)', 'Elder House (West)',
|
||||
'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)',
|
||||
'Snitch Lady (West)', 'Tavern (Front)', 'Bush Covered House', 'Light World Bomb Hut',
|
||||
'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)',
|
||||
'Waterfall of Wishing', 'Hyrule Castle Main Gate', '50 Rupee Cave',
|
||||
'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy',
|
||||
'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']:
|
||||
add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
def set_trock_key_rules(world, player):
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ incsrc normal.asm
|
||||
incsrc spiral.asm
|
||||
incsrc gfx.asm
|
||||
incsrc keydoors.asm
|
||||
incsrc overrides.asm
|
||||
warnpc $279000
|
||||
|
||||
; Data Section
|
||||
org $279000
|
||||
|
||||
@@ -59,6 +59,9 @@ org $1bece4
|
||||
Palette_SpriteAux1:
|
||||
|
||||
|
||||
org $0DFA53
|
||||
jsl.l LampCheckOverride
|
||||
|
||||
; These two, if enabled together, have implications for vanilla BK doors in IP/Hera/Mire
|
||||
; IPBJ is common enough to consider not doing this. Mire is not a concern for vanilla - maybe glitched modes
|
||||
; Hera BK door back can be seen with Pot clipping - likely useful for no logic seeds
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
;================================================================================
|
||||
; Lamp Mantle & Light Cone Fix
|
||||
;--------------------------------------------------------------------------------
|
||||
; Output: 0 for darkness, 1 for lamp cone
|
||||
;--------------------------------------------------------------------------------
|
||||
LampCheckOverride:
|
||||
LDA $7F50C4 : CMP.b #$01 : BNE + : RTL : +
|
||||
CMP.b #$FF : BNE + : INC : RTL : +
|
||||
|
||||
LDA $7EF34A : BNE .done ; skip if we already have lantern
|
||||
|
||||
LDA $7EF3CA : BNE +
|
||||
.lightWorld
|
||||
LDA $040C : CMP.b #$02 : BNE ++ ; check if we're in HC
|
||||
LDA LampConeSewers : BRA .done
|
||||
++
|
||||
LDA LampConeLightWorld : BRA .done
|
||||
+
|
||||
.darkWorld
|
||||
LDA LampConeDarkWorld
|
||||
.done
|
||||
;BNE + : STZ $1D : + ; remember to turn cone off after a torch
|
||||
RTL
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user