Whole ton of things:

-Experimental Flag
--Mirror Scroll
--Mortal GT Minibosses
--Random door kinds
-Crossed Mode
--Standard logic
--Nothing Items
--GT Trash fill skip
--Too many keys in retro
--Hint work
--Spoiler clarification
--Aga 1 logic
-Misc
--Retro nothing item
--Bombable/Dashable matching
--ER+Inverted Logic fix
--Logic for GT Gauntlet/Wizzrobes
--Logic for PoD Sexy Statue switch
This commit is contained in:
aerinon
2020-02-11 14:40:58 -07:00
parent ce1b28e3bd
commit 1f7c27009e
19 changed files with 476 additions and 228 deletions
+14 -5
View File
@@ -69,6 +69,7 @@ class World(object):
self.dungeon_layouts = {}
self.inaccessible_regions = {}
self.key_logic = {}
self.pool_adjustment = {}
for player in range(1, players + 1):
def set_player_attr(attr, val):
@@ -1515,11 +1516,11 @@ class Spoiler(object):
else:
self.entrances[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)])
def set_door(self, entrance, exit, direction, player):
def set_door(self, entrance, exit, direction, player, d_name):
if self.world.players == 1:
self.doors[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)])
self.doors[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction), ('dname', d_name)])
else:
self.doors[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)])
self.doors[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction), ('dname', d_name)])
def set_door_type(self, doorNames, type, player):
if self.world.players == 1:
@@ -1625,7 +1626,8 @@ class Spoiler(object):
'enemy_health': self.world.enemy_health,
'enemy_damage': self.world.enemy_damage,
'players': self.world.players,
'teams': self.world.teams
'teams': self.world.teams,
'experimental' : self.world.experimental
}
def to_json(self):
@@ -1683,9 +1685,16 @@ class Spoiler(object):
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
outfile.write('Hints: %s\n' % ('Yes' if self.metadata['hints'][player] else 'No'))
outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No'))
if self.doors:
outfile.write('\n\nDoors:\n\n')
outfile.write('\n'.join(['%s%s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.doors.values()]))
outfile.write('\n'.join(
['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '',
entry['entrance'],
'<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>',
entry['exit'],
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for
entry in self.doors.values()]))
if self.doorTypes:
outfile.write('\n\nDoor Types:\n\n')
outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', entry['doorNames'], entry['type']) for entry in self.doorTypes.values()]))
+196 -58
View File
@@ -1,6 +1,5 @@
import random
import collections
from collections import defaultdict
from collections import defaultdict, deque
import logging
import operator as op
import time
@@ -11,7 +10,7 @@ from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBa
from Regions import key_only_locations
from Dungeons import hyrule_castle_regions, eastern_regions, desert_regions, hera_regions, tower_regions, pod_regions
from Dungeons import dungeon_regions, region_starts, split_region_starts, flexible_starts
from Dungeons import drop_entrances, dungeon_bigs, dungeon_keys
from Dungeons import drop_entrances, dungeon_bigs, dungeon_keys, dungeon_hints
from Items import ItemFactory
from RoomData import DoorKind, PairedDoor
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, validate_tr
@@ -54,8 +53,6 @@ def link_doors(world, player):
within_dungeon(world, player)
elif world.doorShuffle[player] == 'crossed':
cross_dungeon(world, player)
elif world.doorShuffle[player] == 'experimental':
experiment(world, player)
else:
logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player])
raise Exception('Invalid door shuffle setting: %s' % world.doorShuffle[player])
@@ -69,7 +66,7 @@ def mark_regions(world, player):
# traverse dungeons and make sure dungeon property is assigned
player_dungeons = [dungeon for dungeon in world.dungeons if dungeon.player == player]
for dungeon in player_dungeons:
queue = collections.deque(dungeon.regions)
queue = deque(dungeon.regions)
while len(queue) > 0:
region = world.get_region(queue.popleft(), player)
if region.name not in dungeon.regions:
@@ -87,31 +84,42 @@ def mark_regions(world, player):
def create_door_spoiler(world, player):
logger = logging.getLogger('')
queue = collections.deque((door for door in world.doors if door.player == player))
queue = deque(world.dungeon_layouts[player].values())
while len(queue) > 0:
door_a = queue.popleft()
if door_a.type in [DoorType.Normal, DoorType.SpiralStairs]:
door_b = door_a.dest
if door_b is not None:
logger.debug('spoiler: %s connected to %s', door_a.name, door_b.name)
if not door_a.blocked and not door_b.blocked:
world.spoiler.set_door(door_a.name, door_b.name, 'both', player)
elif door_a.blocked:
world.spoiler.set_door(door_b.name, door_a.name, 'entrance', player)
elif door_b.blocked:
world.spoiler.set_door(door_a.name, door_b.name, 'entrance', player)
else:
logger.warning('This is a bug')
if door_b in queue:
queue.remove(door_b)
else:
logger.debug('Door not found in queue: %s connected to %s', door_b.name, door_a.name)
else:
logger.warning('Door not connected: %s', door_a.name)
builder = queue.popleft()
done = set()
start_regions = set(convert_regions(builder.layout_starts, world, player)) # todo: set all_entrances for basic
reg_queue = deque(start_regions)
visited = set(start_regions)
while len(reg_queue) > 0:
next = reg_queue.pop()
for ext in next.exits:
door_a = ext.door
connect = ext.connected_region
if door_a and door_a.type in [DoorType.Normal, DoorType.SpiralStairs] and door_a not in done:
done.add(door_a)
door_b = door_a.dest
if door_b:
done.add(door_b)
if not door_a.blocked and not door_b.blocked:
world.spoiler.set_door(door_a.name, door_b.name, 'both', player, builder.name)
elif door_a.blocked:
world.spoiler.set_door(door_b.name, door_a.name, 'entrance', player, builder.name)
elif door_b.blocked:
world.spoiler.set_door(door_a.name, door_b.name, 'entrance', player, builder.name)
else:
logger.warning('This is a bug during door spoiler')
else:
logger.warning('Door not connected: %s', door_a.name)
if connect and connect.type == RegionType.Dungeon and connect not in visited:
visited.add(connect)
reg_queue.append(connect)
def vanilla_key_logic(world, player):
builders = []
world.dungeon_layouts[player] = {}
for dungeon in [dungeon for dungeon in world.dungeons if dungeon.player == player]:
sector = Sector()
sector.name = dungeon.name
@@ -119,12 +127,13 @@ def vanilla_key_logic(world, player):
builder = simple_dungeon_builder(sector.name, [sector])
builder.master_sector = sector
builders.append(builder)
world.dungeon_layouts[player][builder.name] = builder
overworld_prep(world, player)
entrances_map, potentials, connections = determine_entrance_list(world, player)
enabled_entrances = {}
sector_queue = collections.deque(builders)
sector_queue = deque(builders)
last_key = None
while len(sector_queue) > 0:
builder = sector_queue.popleft()
@@ -313,6 +322,7 @@ def within_dungeon(world, player):
for builder in world.dungeon_layouts[player].values():
shuffle_key_doors(builder, world, player)
logging.getLogger('').info('Key door shuffle time: %s', time.process_time()-start)
smooth_door_pairs(world, player)
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map):
@@ -334,7 +344,7 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map)
def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player):
entrances_map, potentials, connections = connections_tuple
enabled_entrances = {}
sector_queue = collections.deque(dungeon_builders.values())
sector_queue = deque(dungeon_builders.values())
last_key = None
while len(sector_queue) > 0:
builder = sector_queue.popleft()
@@ -425,7 +435,7 @@ def find_new_entrances(sector, connections, potentials, enabled, world, player):
for potential in potentials.pop(new_region):
enabled[potential] = (region.name, region.dungeon)
# see if this unexplored region connects elsewhere
queue = collections.deque(new_region.exits)
queue = deque(new_region.exits)
visited = set()
while len(queue) > 0:
ext = queue.popleft()
@@ -671,6 +681,33 @@ def cross_dungeon(world, player):
gt = world.get_dungeon('Ganons Tower', player)
del gt.dungeon_items[0] # removes map
assign_cross_keys(dungeon_builders, world, player)
all_dungeon_items = [y for x in world.dungeons if x.player == player for y in x.all_items]
target_items = 34 if world.retro[player] else 63
d_items = target_items - len(all_dungeon_items)
if d_items > 0:
if d_items >= 1: # restore HC map
world.get_dungeon('Hyrule Castle', player).dungeon_items.append(ItemFactory('Map (Escape)', player))
if d_items >= 2: # restore GT map
world.get_dungeon('Ganons Tower', player).dungeon_items.append(ItemFactory('Map (Ganons Tower)', player))
if d_items > 2:
world.pool_adjustment[player] = d_items - 2
elif d_items < 0:
world.pool_adjustment[player] = d_items
smooth_door_pairs(world, player)
# Re-assign dungeon bosses
gt = world.get_dungeon('Ganons Tower', player)
for name, builder in dungeon_builders.items():
reassign_boss('GT Ice Armos', 'bottom', builder, gt, world, player)
reassign_boss('GT Lanmolas 2', 'middle', builder, gt, world, player)
reassign_boss('GT Moldorm', 'top', builder, gt, world, player)
if world.hints[player]:
refine_hints(dungeon_builders)
def assign_cross_keys(dungeon_builders, world, player):
start = time.process_time()
total_keys = remaining = 29
total_candidates = 0
@@ -688,6 +725,7 @@ def cross_dungeon(world, player):
total_candidates += builder.key_doors_num
start_regions_map[name] = start_regions
# Step 2: Initial Key Number Assignment & Calculate Flexibility
for name, builder in dungeon_builders.items():
calculated = int(round(builder.key_doors_num*total_keys/total_candidates))
@@ -717,7 +755,7 @@ def cross_dungeon(world, player):
# Step 4: Try to assign remaining keys
builder_order = [x for x in dungeon_builders.values() if x.flex > 0]
builder_order.sort(key=lambda b: b.combo_size)
queue = collections.deque(builder_order)
queue = deque(builder_order)
logger = logging.getLogger('')
while len(queue) > 0 and remaining > 0:
builder = queue.popleft()
@@ -731,7 +769,7 @@ def cross_dungeon(world, player):
if builder.flex > 0:
builder.combo_size = ncr(len(builder.candidates), builder.key_doors_num)
queue.append(builder)
queue = collections.deque(sorted(queue, key=lambda b: b.combo_size))
queue = deque(sorted(queue, key=lambda b: b.combo_size))
else:
logger.info('Cross Dungeon: Increase failed for %s', name)
builder.key_doors_num -= 1
@@ -739,22 +777,15 @@ def cross_dungeon(world, player):
logger.info('Cross Dungeon: Keys unable to assign in pool %s', remaining)
# Last Step: Adjust Small Key Dungeon Pool
for name, builder in dungeon_builders.items():
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
dungeon = world.get_dungeon(name, player)
if actual_chest_keys == 0:
dungeon.small_keys = []
else:
dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys
if not world.retro[player]:
for name, builder in dungeon_builders.items():
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
dungeon = world.get_dungeon(name, player)
if actual_chest_keys == 0:
dungeon.small_keys = []
else:
dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys
logging.getLogger('').info('Cross Dungeon: Key door shuffle time: %s', time.process_time()-start)
# todo: pair down paired doors - excessive rom writes ATM
# Re-assign dungeon bosses
gt = world.get_dungeon('Ganons Tower', player)
for name, builder in dungeon_builders.items():
reassign_boss('GT Ice Armos', 'bottom', builder, gt, world, player)
reassign_boss('GT Lanmolas 2', 'middle', builder, gt, world, player)
reassign_boss('GT Moldorm', 'top', builder, gt, world, player)
def reassign_boss(boss_region, boss_key, builder, gt, world, player):
@@ -765,9 +796,12 @@ def reassign_boss(boss_region, boss_key, builder, gt, world, player):
new_dungeon.bosses[boss_key] = gt_boss
def experiment(world, player):
# print_wiki_doors(dungeon_regions, world, player)
cross_dungeon(world, player)
def refine_hints(dungeon_builders):
for name, builder in dungeon_builders.items():
for region in builder.master_sector.regions:
for location in region.locations:
if not location.event and '- Boss' not in location.name and '- Prize' not in location.name and location.name != 'Sanctuary':
location.hint_text = dungeon_hints[name]
def convert_to_sectors(region_names, world, player):
@@ -817,7 +851,7 @@ def convert_to_sectors(region_names, world, player):
# those with split region starts like Desert/Skull combine for key layouts
def combine_layouts(recombinant_builders, dungeon_builders, entrances_map):
for recombine in recombinant_builders.values():
queue = collections.deque(dungeon_builders.values())
queue = deque(dungeon_builders.values())
while len(queue) > 0:
builder = queue.pop()
if builder.name.startswith(recombine.name):
@@ -865,8 +899,8 @@ def find_current_key_doors(builder, world, player):
current_doors = []
for region in builder.master_sector.regions:
for ext in region.exits:
d = world.check_for_door(ext.name, player)
if d is not None and d.smallKey:
d = ext.door
if d and d.smallKey:
current_doors.append(d)
return current_doors
@@ -976,7 +1010,7 @@ def log_key_logic(d_name, key_logic):
def build_pair_list(flat_list):
paired_list = []
queue = collections.deque(flat_list)
queue = deque(flat_list)
while len(queue) > 0:
d = queue.pop()
if d.dest in queue and d.type != DoorType.SpiralStairs:
@@ -1002,7 +1036,7 @@ def find_key_door_candidates(region, checked, world, player):
dungeon = region.dungeon
candidates = []
checked_doors = list(checked)
queue = collections.deque([(region, None, None)])
queue = deque([(region, None, None)])
while len(queue) > 0:
current, last_door, last_region = queue.pop()
for ext in current.exits:
@@ -1061,7 +1095,7 @@ def ncr(n, r):
def reassign_key_doors(builder, proposal, world, player):
logger = logging.getLogger('')
flat_proposal = flatten_pair_list(proposal)
queue = collections.deque(find_current_key_doors(builder, world, player))
queue = deque(find_current_key_doors(builder, world, player))
while len(queue) > 0:
d = queue.pop()
if d.type is DoorType.SpiralStairs and d not in proposal:
@@ -1070,7 +1104,7 @@ def reassign_key_doors(builder, proposal, world, player):
room.delete(d.doorListPos)
else:
if len(room.doorList) > 1:
room.mirror(d.doorListPos) # todo: I don't think this works for crossed - maybe it will
room.mirror(d.doorListPos) # I think this works for crossed now
else:
room.delete(d.doorListPos)
d.smallKey = False
@@ -1129,6 +1163,104 @@ def change_door_to_small_key(d, world, player):
room.change(d.doorListPos, DoorKind.SmallKey)
def smooth_door_pairs(world, player):
all_doors = [x for x in world.doors if x.player == player]
skip = set()
for door in all_doors:
if door.type in [DoorType.Normal, DoorType.Interior] and door not in skip:
partner = door.dest
skip.add(partner)
room_a = world.get_room(door.roomIndex, player)
room_b = world.get_room(partner.roomIndex, player)
type_a = room_a.kind(door)
type_b = room_b.kind(partner)
valid_pair = stateful_door(door, type_a) and stateful_door(partner, type_b)
if door.type == DoorType.Normal:
if type_a == DoorKind.SmallKey or type_b == DoorKind.SmallKey:
if valid_pair:
if type_a != DoorKind.SmallKey:
room_a.change(door.doorListPos, DoorKind.SmallKey)
if type_b != DoorKind.SmallKey:
room_b.change(partner.doorListPos, DoorKind.SmallKey)
add_pair(door, partner, world, player)
else:
if type_a == DoorKind.SmallKey:
remove_pair(door, world, player)
if type_b == DoorKind.SmallKey:
remove_pair(door, world, player)
elif type_a in [DoorKind.Bombable, DoorKind.Dashable] or type_b in [DoorKind.Bombable, DoorKind.Dashable]:
if valid_pair:
if type_a == type_b:
add_pair(door, partner, world, player)
spoiler_type = 'Bomb Door' if type_a == DoorKind.Bombable else 'Dash Door'
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
else:
new_type = DoorKind.Dashable if type_a == DoorKind.Dashable or type_b == DoorKind.Dashable else DoorKind.Bombable
if type_a != new_type:
room_a.change(door.doorListPos, new_type)
if type_b != new_type:
room_b.change(partner.doorListPos, new_type)
add_pair(door, partner, world, player)
spoiler_type = 'Bomb Door' if new_type == DoorKind.Bombable else 'Dash Door'
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
else:
if type_a in [DoorKind.Bombable, DoorKind.Dashable]:
room_a.change(door.doorListPos, DoorKind.Normal)
remove_pair(door, world, player)
elif type_b in [DoorKind.Bombable, DoorKind.Dashable]:
room_b.change(partner.doorListPos, DoorKind.Normal)
remove_pair(partner, world, player)
elif world.experimental[player] and valid_pair and type_a != DoorKind.SmallKey and type_b != DoorKind.SmallKey:
random_door_type(door, partner, world, player, type_a, type_b, room_a, room_b)
world.paired_doors[player] = [x for x in world.paired_doors[player] if x.pair or x.original]
def add_pair(door_a, door_b, world, player):
pair_a, pair_b = None, None
for paired_door in world.paired_doors[player]:
if paired_door.door_a == door_a.name and paired_door.door_b == door_b.name:
paired_door.pair = True
return
if paired_door.door_a == door_b.name and paired_door.door_b == door_a.name:
paired_door.pair = True
return
if paired_door.door_a == door_a.name or paired_door.door_b == door_a.name:
pair_a = paired_door
if paired_door.door_a == door_b.name or paired_door.door_b == door_b.name:
pair_b = paired_door
if pair_a:
pair_a.pair = False
if pair_b:
pair_b.pair = False
world.paired_doors[player].append(PairedDoor(door_a, door_b))
def remove_pair(door, world, player):
for paired_door in world.paired_doors[player]:
if paired_door.door_a == door.name or paired_door.door_b == door.name:
paired_door.pair = False
break
def stateful_door(door, kind):
if 0 <= door.doorListPos < 4:
return kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable] #, DoorKind.BigKey]
return False
def random_door_type(door, partner, world, player, type_a, type_b, room_a, room_b):
r_kind = random.choices([DoorKind.Normal, DoorKind.Bombable, DoorKind.Dashable], [5, 2, 3], k=1)[0]
if r_kind != DoorKind.Normal:
if door.type == DoorType.Normal:
add_pair(door, partner, world, player)
if type_a != r_kind:
room_a.change(door.doorListPos, r_kind)
if type_b != r_kind:
room_b.change(partner.doorListPos, r_kind)
spoiler_type = 'Bomb Door' if r_kind == DoorKind.Bombable else 'Dash Door'
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
def determine_required_paths(world, player):
paths = {
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby'],
@@ -1168,13 +1300,18 @@ def find_inaccessible_regions(world, player):
regs = convert_regions(start_regions, world, player)
all_regions = set([r for r in world.regions if r.player == player and r.type is not RegionType.Dungeon])
visited_regions = set()
queue = collections.deque(regs)
queue = deque(regs)
while len(queue) > 0:
next_region = queue.popleft()
visited_regions.add(next_region)
if next_region.name == 'Inverted Dark Sanctuary': # special spawn point in cave
for ent in next_region.entrances:
parent = ent.parent_region
if parent and parent.type is not RegionType.Dungeon and parent not in queue and parent not in visited_regions:
queue.append(parent)
for ext in next_region.exits:
connect = ext.connected_region
if connect is not None and connect.type is not RegionType.Dungeon and connect not in queue and connect not in visited_regions:
if connect and connect.type is not RegionType.Dungeon and connect not in queue and connect not in visited_regions:
queue.append(connect)
world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)])
if world.mode[player] == 'standard':
@@ -1302,6 +1439,7 @@ def check_for_pinball_fix(state, bad_region, world, player):
@unique
class DROptions(Flag):
Eternal_Mini_Bosses = 0x01 # If on, GT minibosses marked as defeated when they try to spawn a heart
Town_Portal = 0x02 # If on, Players will start with mirror scroll
Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required
# DATA GOES DOWN HERE
+40 -38
View File
@@ -1098,6 +1098,8 @@ def create_doors(world, player):
world.get_door('PoD Arena Crystal Path', player).barrier(CrystalBarrier.Blue)
world.get_door('PoD Sexy Statue W', player).c_switch()
world.get_door('PoD Sexy Statue NW', player).c_switch()
world.get_door('PoD Map Balcony WS', player).c_switch()
world.get_door('PoD Map Balcony South Stairs', player).c_switch()
world.get_door('PoD Bow Statue SW', player).c_switch()
world.get_door('PoD Bow Statue Down Ladder', player).c_switch()
world.get_door('PoD Dark Pegs Up Ladder', player).c_switch()
@@ -1211,44 +1213,44 @@ def create_doors(world, player):
def create_paired_doors(world, player):
world.paired_doors[player] = [
PairedDoor('Sewers Secret Room Key Door S', 'Sewers Key Rat Key Door N'),
PairedDoor('TR Pokey 2 ES', 'TR Lava Island WS'), # TR Pokey Key
PairedDoor('TR Dodgers NE', 'TR Lava Escape SE'), # TR Big key door by pipes
PairedDoor('PoD Falling Bridge WN', 'PoD Dark Maze EN'), # Pod Dark maze door
PairedDoor('PoD Dark Maze E', 'PoD Big Chest Balcony W'), # PoD Bombable by Big Chest
PairedDoor('PoD Arena Main NW', 'PoD Falling Bridge SW'), # Pod key door by bridge
PairedDoor('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S'),
PairedDoor('Swamp Hub WN', 'Swamp Crystal Switch EN'), # Swamp key door crystal switch
PairedDoor('Swamp Hub North Ledge N', 'Swamp Push Statue S'), # Swamp key door above big chest
PairedDoor('PoD Map Balcony WS', 'PoD Arena Ledge ES'), # Pod bombable by arena
PairedDoor('Swamp Hub Dead Ledge EN', 'Swamp Hammer Switch WN'), # Swamp bombable to random pots
PairedDoor('Swamp Pot Row WN', 'Swamp Map Ledge EN'), # Swamp bombable to map chest
PairedDoor('Swamp Pot Row WS', 'Swamp Trench 1 Approach ES'), # Swamp key door early room $38
PairedDoor('PoD Middle Cage N', 'PoD Pit Room S'),
PairedDoor('GT Crystal Circles SW', 'GT Left Moldorm Ledge NW'), # GT moldorm key door
PairedDoor('Ice Conveyor SW', 'Ice Bomb Jump NW'), # Ice BJ key door
PairedDoor('Desert Tiles 2 SE', 'Desert Beamos Hall NE'),
PairedDoor('Skull 3 Lobby NW', 'Skull Star Pits SW'), # Skull 3 key door
PairedDoor('Skull 1 Lobby WS', 'Skull Pot Prison ES'), # Skull 1 key door - pot prison to big chest
PairedDoor('Skull Map Room SE', 'Skull Pinball NE'), # Skull 1 - pinball key door
PairedDoor('GT Dash Hall NE', 'GT Hidden Spikes SE'), # gt main big key door
PairedDoor('Ice Spike Cross ES', 'Ice Spike Room WS'), # ice door to spike chest
PairedDoor('GT Conveyor Star Pits EN', 'GT Falling Bridge WN'), # gt right side key door to cape bridge
PairedDoor('GT Warp Maze (Rails) WS', 'GT Randomizer Room ES'), # gt bombable to rando room
PairedDoor('Ice Tall Hint SE', 'Ice Lonely Freezor NE'), # ice's big icy room key door to lonely freezor
PairedDoor('Eastern Courtyard N', 'Eastern Darkness S'),
PairedDoor('Mire Fishbone SE', 'Mire Spike Barrier NE'), # mire fishbone key door
PairedDoor('Mire BK Door Room N', 'Mire Left Bridge S'), # mire big key door to bridges
PairedDoor('Eastern Big Key NE', 'Eastern Hint Tile Blocked Path SE'),
PairedDoor('TR Hub NW', 'TR Pokey 1 SW'), # TR somaria hub to pokey
PairedDoor('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN'),
PairedDoor('Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW'), # TT random bomb to pots
PairedDoor('Thieves BK Corner NE', 'Thieves Hallway SE'), # TT big key door
PairedDoor('Ice Switch Room ES', 'Ice Refill WS'), # Ice last key door to crystal switch
PairedDoor('Mire Hub WS', 'Mire Conveyor Crystal ES'), # mire hub key door to attic
PairedDoor('Mire Hub Right EN', 'Mire Map Spot WN'), # mire hub key door to map
PairedDoor('TR Dash Bridge WS', 'TR Crystal Maze ES'), # tr last key door to switch maze
PairedDoor('Thieves Ambush E', 'Thieves Rail Ledge W') # TT dashable above
PairedDoor('Sewers Secret Room Key Door S', 'Sewers Key Rat Key Door N', True),
PairedDoor('TR Pokey 2 ES', 'TR Lava Island WS', True), # TR Pokey Key
PairedDoor('TR Dodgers NE', 'TR Lava Escape SE', True), # TR Big key door by pipes
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('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
PairedDoor('Swamp Hub Dead Ledge EN', 'Swamp Hammer Switch WN', True), # Swamp bombable to random pots
PairedDoor('Swamp Pot Row WN', 'Swamp Map Ledge EN', True), # Swamp bombable to map chest
PairedDoor('Swamp Pot Row WS', 'Swamp Trench 1 Approach ES', True), # Swamp key door early room $38
PairedDoor('PoD Middle Cage N', 'PoD Pit Room S', True),
PairedDoor('GT Crystal Circles SW', 'GT Left Moldorm Ledge NW', True), # GT moldorm key door
PairedDoor('Ice Conveyor SW', 'Ice Bomb Jump NW', True), # Ice BJ key door
PairedDoor('Desert Tiles 2 SE', 'Desert Beamos Hall NE', True),
PairedDoor('Skull 3 Lobby NW', 'Skull Star Pits SW', True), # Skull 3 key door
PairedDoor('Skull 1 Lobby WS', 'Skull Pot Prison ES', True), # Skull 1 key door - pot prison to big chest
PairedDoor('Skull Map Room SE', 'Skull Pinball NE', True), # Skull 1 - pinball key door
PairedDoor('GT Dash Hall NE', 'GT Hidden Spikes SE', True), # gt main big key door
PairedDoor('Ice Spike Cross ES', 'Ice Spike Room WS', True), # ice door to spike chest
PairedDoor('GT Conveyor Star Pits EN', 'GT Falling Bridge WN', True), # gt right side key door to cape bridge
PairedDoor('GT Warp Maze (Rails) WS', 'GT Randomizer Room ES', True), # gt bombable to rando room
PairedDoor('Ice Tall Hint SE', 'Ice Lonely Freezor NE', True), # ice's big icy room key door to lonely freezor
PairedDoor('Eastern Courtyard N', 'Eastern Darkness S', True),
PairedDoor('Mire Fishbone SE', 'Mire Spike Barrier NE', True), # mire fishbone key door
PairedDoor('Mire BK Door Room N', 'Mire Left Bridge S', True), # mire big key door to bridges
PairedDoor('Eastern Big Key NE', 'Eastern Hint Tile Blocked Path SE', True),
PairedDoor('TR Hub NW', 'TR Pokey 1 SW', True), # TR somaria hub to pokey
PairedDoor('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN', True),
PairedDoor('Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW', True), # TT random bomb to pots
PairedDoor('Thieves BK Corner NE', 'Thieves Hallway SE', True), # TT big key door
PairedDoor('Ice Switch Room ES', 'Ice Refill WS', True), # Ice last key door to crystal switch
PairedDoor('Mire Hub WS', 'Mire Conveyor Crystal ES', True), # mire hub key door to attic
PairedDoor('Mire Hub Right EN', 'Mire Map Spot WN', True), # mire hub key door to map
PairedDoor('TR Dash Bridge WS', 'TR Crystal Maze ES', True), # tr last key door to switch maze
PairedDoor('Thieves Ambush E', 'Thieves Rail Ledge W', True) # TT dashable above
]
+3 -3
View File
@@ -173,7 +173,7 @@ def parse_arguments(argv, no_defaults=False):
The dungeon variants only mix up dungeons and keep the rest of
the overworld vanilla.
''')
parser.add_argument('--door_shuffle', default=defval('basic'), const='vanilla', nargs='?', choices=['vanilla', 'basic', 'crossed', 'experimental'],
parser.add_argument('--door_shuffle', default=defval('basic'), const='vanilla', nargs='?', choices=['vanilla', 'basic', 'crossed'],
help='''\
Select Door Shuffling Algorithm. (default: %(default)s)
Basic: Doors are mixed within a single dungeon.
@@ -182,8 +182,8 @@ def parse_arguments(argv, no_defaults=False):
(Not yet implemented)
Vanilla: All doors are connected the same way they were in the
base game.
Experimental: Experimental mixes live here. Use at your own risk.
''')
parser.add_argument('--experimental', default=defval(False), help='Enable experimental features', action='store_true')
parser.add_argument('--crystals_ganon', default=defval('7'), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'],
help='''\
How many crystals are needed to defeat ganon. Any other
@@ -302,7 +302,7 @@ def parse_arguments(argv, no_defaults=False):
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'retro', 'accessibility', 'hints', 'beemizer',
'retro', 'accessibility', 'hints', 'beemizer', 'experimental',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
'remote_items']:
+16
View File
@@ -380,3 +380,19 @@ dungeon_bigs = {
'Ganons Tower': 'Big Key (Ganons Tower)'
}
dungeon_hints = {
'Hyrule Castle': 'in Hyrule Castle',
'Eastern Palace': 'in Eastern Palace',
'Desert Palace': 'in Desert Palace',
'Tower of Hera': 'in Tower of Hera',
'Agahnims Tower': 'in Castle Tower',
'Palace of Darkness': 'in Palace of Darkness',
'Swamp Palace': 'in Swamp Palace)',
'Skull Woods': 'in Skull Woods',
'Thieves Town': 'in Thieves\' Town)',
'Ice Palace': 'in Ice Palace',
'Misery Mire': 'in Misery Mire',
'Turtle Rock': 'in Turtle Rock',
'Ganons Tower': 'in Ganon\'s Tower'
}
+1 -1
View File
@@ -236,7 +236,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# fill in gtower locations with trash first
for player in range(1, world.players + 1):
if not gftower_trash or not world.ganonstower_vanilla[player]:
if not gftower_trash or not world.ganonstower_vanilla[player] or world.doorShuffle[player] == 'crossed':
continue
gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, 15))
+1 -1
View File
@@ -315,7 +315,7 @@ def guiMain(args=None):
doorShuffleFrame = Frame(drowDownFrame)
doorShuffleVar = StringVar()
doorShuffleVar.set('basic')
doorShuffleOptionMenu = OptionMenu(doorShuffleFrame, doorShuffleVar, 'vanilla', 'basic', 'crossed', 'experimental')
doorShuffleOptionMenu = OptionMenu(doorShuffleFrame, doorShuffleVar, 'vanilla', 'basic', 'crossed')
doorShuffleOptionMenu.pack(side=RIGHT)
doorShuffleLabel = Label(doorShuffleFrame, text='Door shuffle algorithm')
doorShuffleLabel.pack(side=LEFT)
+19 -7
View File
@@ -58,7 +58,7 @@ difficulties = {
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 17 + ['Rupees (20)'] * 10,
retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 4,
progressive_shield_limit = 3,
@@ -85,7 +85,7 @@ difficulties = {
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 3,
progressive_shield_limit = 2,
@@ -112,7 +112,7 @@ difficulties = {
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 2,
progressive_shield_limit = 1,
@@ -206,7 +206,16 @@ def generate_itempool(world, player):
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray)
world.rupoor_cost = min(world.customitemarray[69], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player])
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player])
if player in world.pool_adjustment.keys():
amt = world.pool_adjustment[player]
if amt < 0:
for i in range(0, amt):
pool.remove('Rupees (20)')
elif amt > 0:
for i in range(0, amt):
pool.append('Rupees (20)')
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
@@ -406,7 +415,7 @@ def set_up_shops(world, player):
rss.locked = True
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro):
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, door_shuffle):
pool = []
placed_items = {}
precollected_items = []
@@ -525,8 +534,11 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r
pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool]
pool.extend(diff.retro)
if mode == 'standard':
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
place_item(key_location, 'Small Key (Universal)')
if door_shuffle == 'vanilla':
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
place_item(key_location, 'Small Key (Universal)')
else:
pool.extend(['Small Key (Universal)'])
else:
pool.extend(['Small Key (Universal)'])
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
+3 -2
View File
@@ -806,7 +806,8 @@ def reduce_rules(small_rules, collected, collected_alt):
# Soft lock stuff
def validate_key_layout(key_layout, world, player):
if world.retro[player]: # retro is all good - don't care how the doors are laid out
# retro is all good - except for hyrule castle in standard mode
if world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle'):
return True
flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name)
@@ -815,7 +816,7 @@ def validate_key_layout(key_layout, world, player):
for region in key_layout.start_regions:
state.visit_region(region, key_checks=True)
state.add_all_doors_check_keys(region, flat_proposal, world, player)
return validate_key_layout_sub_loop(key_layout, state, {}, flat_proposal, None, None, world, player)
return validate_key_layout_sub_loop(key_layout, state, {}, flat_proposal, None, 0, world, player)
def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposal, prev_state, prev_avail, world, player):
+2 -1
View File
@@ -23,7 +23,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties, fill_prizes
from Utils import output_path, parse_player_names
__version__ = '0.0.12pre'
__version__ = '0.0.13pre'
def main(args, seed=None):
@@ -56,6 +56,7 @@ def main(args, seed=None):
world.enemy_health = args.enemy_health.copy()
world.enemy_damage = args.enemy_damage.copy()
world.beemizer = args.beemizer.copy()
world.experimental = args.experimental.copy()
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
+15 -14
View File
@@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses, exit_ids
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'c06e14396839bc443a6918e736f1e5a7'
RANDOMIZERBASEHASH = '818b2c659a610cb4112804cf612ffd37'
class JsonRom(object):
@@ -591,7 +591,7 @@ def patch_rom(world, rom, player, team, enemized):
patch_shuffled_dark_sanc(world, rom, player)
# patch doors
dr_flags = DROptions.Eternal_Mini_Bosses
dr_flags = DROptions.Eternal_Mini_Bosses if not world.experimental[player] else DROptions.Town_Portal
if world.doorShuffle[player] == 'crossed':
rom.write_byte(0x139004, 2)
rom.write_byte(0x151f1, 2)
@@ -1676,10 +1676,13 @@ def write_strings(rom, world, player, team):
# Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable.
locations_to_hint = InconvenientLocations.copy()
if world.doorShuffle[player] != 'crossed':
locations_to_hint.extend(InconvenientDungeonLocations)
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
locations_to_hint.extend(InconvenientVanillaLocations)
random.shuffle(locations_to_hint)
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5
hint_count -= 2 if world.doorShuffle[player] == 'crossed' else 0
del locations_to_hint[hint_count:]
for location in locations_to_hint:
if location == 'Swamp Left':
@@ -1733,20 +1736,17 @@ def write_strings(rom, world, player, team):
items_to_hint.extend(BigKeys)
random.shuffle(items_to_hint)
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
hint_count += 2 if world.doorShuffle[player] == 'crossed' else 0
while hint_count > 0:
this_item = items_to_hint.pop(0)
this_location = world.find_items_not_key_only(this_item, player)
random.shuffle(this_location)
#This looks dumb but prevents hints for Skull Woods Pinball Room's key safely with any item pool.
if this_location:
if this_location[0].name == 'Skull Woods - Pinball Room':
this_location.pop(0)
if this_location:
this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.'
tt[hint_locations.pop(0)] = this_hint
hint_count -= 1
# Adding a hint for the Thieves' Town Attic location in Crossed Doorshufle.
# Adding a hint for the Thieves' Town Attic location in Crossed door shuffle.
if world.doorShuffle[player] in ['crossed']:
attic_hint = world.get_location("Thieves' Town - Attic", player).parent_region.dungeon.name
this_hint = 'A cracked floor can be found in ' + attic_hint + '.'
@@ -2300,7 +2300,7 @@ HintLocations = ['telepathic_tile_eastern_palace',
'telepathic_tile_castle_tower',
'telepathic_tile_ice_large_room',
'telepathic_tile_turtle_rock',
'telepathic_tile_ice_entrace',
'telepathic_tile_ice_entrance',
'telepathic_tile_ice_stalfos_knights_room',
'telepathic_tile_tower_of_hera_entrance',
'telepathic_tile_south_east_darkworld_cave',
@@ -2313,15 +2313,16 @@ HintLocations = ['telepathic_tile_eastern_palace',
InconvenientLocations = ['Spike Cave',
'Sahasrahla',
'Purple Chest',
'Swamp Left',
'Mire Left',
'Tower of Hera - Big Key Chest',
'Eastern Palace - Big Key Chest',
'Thieves\' Town - Big Chest',
'Ice Palace - Big Chest',
'Ganons Tower - Big Chest',
'Magic Bat']
InconvenientDungeonLocations = ['Swamp Left',
'Mire Left',
'Eastern Palace - Big Key Chest',
'Thieves\' Town - Big Chest',
'Ice Palace - Big Chest',
'Ganons Tower - Big Chest']
InconvenientVanillaLocations = ['Graveyard Cave',
'Mimic Cave']
+5 -1
View File
@@ -251,6 +251,9 @@ class Room(object):
self.doorList = []
self.modified = False
def kind(self, door):
return self.doorList[door.doorListPos][1]
def door(self, pos, kind):
self.doorList.append((pos, kind))
return self
@@ -299,10 +302,11 @@ class Room(object):
class PairedDoor(object):
def __init__(self, door_a, door_b):
def __init__(self, door_a, door_b, original=False):
self.door_a = door_a
self.door_b = door_b
self.pair = True
self.original = original
def address_a(self, world, player):
d = world.check_for_door(self.door_a, player)
+141 -89
View File
@@ -1,7 +1,7 @@
import collections
import logging
from BaseClasses import CollectionState
from BaseClasses import CollectionState, RegionType
from Regions import key_only_locations
from collections import deque
def set_rules(world, player):
@@ -168,6 +168,7 @@ def global_rules(world, player):
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player))
set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player))
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player))
set_rule(world.get_entrance('PoD Arena Bonk Path', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
@@ -302,7 +303,18 @@ def global_rules(world, player):
set_rule(world.get_entrance('GT Mimics 2 WS', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('GT Mimics 2 NE', player), lambda state: state.can_shoot_arrows(player))
# consider access to refill room
# consider can_kill_most_things to gauntlet
set_rule(world.get_entrance('GT Gauntlet 1 WN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 2 EN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 2 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 3 NW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 3 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 4 NW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 4 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 5 NW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 5 WS', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 1 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 2 SE', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 2 NE', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player))
@@ -688,63 +700,59 @@ def no_glitches_rules(world, player):
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
add_lamp_requirement(spot, player)
add_conditional_lamp('TR Dark Ride Up Stairs', 'TR Dark Ride', 'Entrance')
add_conditional_lamp('TR Dark Ride SW', 'TR Dark Ride', 'Entrance')
add_conditional_lamp('Mire Dark Shooters Up Stairs', 'Mire Dark Shooters', 'Entrance')
add_conditional_lamp('Mire Dark Shooters SW', 'Mire Dark Shooters', 'Entrance')
add_conditional_lamp('Mire Dark Shooters SE', 'Mire Dark Shooters', 'Entrance')
add_conditional_lamp('Mire Key Rupees NE', 'Mire Key Rupees', 'Entrance')
add_conditional_lamp('Mire Block X NW', 'Mire Block X', 'Entrance')
add_conditional_lamp('Mire Block X WS', 'Mire Block X', 'Entrance')
add_conditional_lamp('Mire Tall Dark and Roomy ES', 'Mire Tall Dark and Roomy', 'Entrance')
add_conditional_lamp('Mire Tall Dark and Roomy WS', 'Mire Tall Dark and Roomy', 'Entrance')
add_conditional_lamp('Mire Tall Dark and Roomy WN', 'Mire Tall Dark and Roomy', 'Entrance')
add_conditional_lamp('Mire Crystal Right ES', 'Mire Crystal Right', 'Entrance')
add_conditional_lamp('Mire Crystal Mid NW', 'Mire Crystal Mid', 'Entrance')
add_conditional_lamp('Mire Crystal Left WS', 'Mire Crystal Left', 'Entrance')
add_conditional_lamp('Mire Crystal Top SW', 'Mire Crystal Top', 'Entrance')
add_conditional_lamp('Mire Shooter Rupees EN', 'Mire Shooter Rupees', 'Entrance')
add_conditional_lamp('PoD Dark Alley NE', 'PoD Dark Alley', 'Entrance')
add_conditional_lamp('PoD Callback WS', 'PoD Callback', 'Entrance')
add_conditional_lamp('PoD Callback Warp', 'PoD Callback', 'Entrance')
add_conditional_lamp('PoD Turtle Party ES', 'PoD Turtle Party', 'Entrance')
add_conditional_lamp('PoD Turtle Party NW', 'PoD Turtle Party', 'Entrance')
add_conditional_lamp('PoD Lonely Turtle SW', 'PoD Lonely Turtle', 'Entrance')
add_conditional_lamp('PoD Lonely Turtle EN', 'PoD Lonely Turtle', 'Entrance')
add_conditional_lamp('PoD Dark Pegs Up Ladder', 'PoD Dark Pegs', 'Entrance')
add_conditional_lamp('PoD Dark Pegs WN', 'PoD Dark Pegs', 'Entrance')
add_conditional_lamp('PoD Dark Basement W Up Stairs', 'PoD Dark Basement', 'Entrance')
add_conditional_lamp('PoD Dark Basement E Up Stairs', 'PoD Dark Basement', 'Entrance')
add_conditional_lamp('PoD Dark Maze EN', 'PoD Dark Maze', 'Entrance')
add_conditional_lamp('PoD Dark Maze E', 'PoD Dark Maze', 'Entrance')
add_conditional_lamp('Palace of Darkness - Dark Basement - Left', 'PoD Dark Basement', 'Location')
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'PoD Dark Basement', 'Location')
add_conditional_lamp('Palace of Darkness - Dark Maze - Top', 'PoD Dark Maze', 'Location')
add_conditional_lamp('Palace of Darkness - Dark Maze - Bottom', 'PoD Dark Maze', 'Location')
add_conditional_lamp('Eastern Dark Square NW', 'Eastern Dark Square', 'Entrance')
add_conditional_lamp('Eastern Dark Square Key Door WN', 'Eastern Dark Square', 'Entrance')
add_conditional_lamp('Eastern Dark Square EN', 'Eastern Dark Square', 'Entrance')
add_conditional_lamp('Eastern Dark Pots WN', 'Eastern Dark Pots', 'Entrance')
add_conditional_lamp('Eastern Darkness S', 'Eastern Darkness', 'Entrance')
add_conditional_lamp('Eastern Darkness Up Stairs', 'Eastern Darkness', 'Entrance')
add_conditional_lamp('Eastern Darkness NE', 'Eastern Darkness', 'Entrance')
add_conditional_lamp('Eastern Rupees SE', 'Eastern Rupees', 'Entrance')
add_conditional_lamp('Eastern Palace - Dark Square Pot Key', 'Eastern Dark Square', 'Location')
add_conditional_lamp('Eastern Palace - Dark Eyegore Key Drop', 'Eastern Darkness', 'Location')
add_conditional_lamp('Tower Lone Statue Down Stairs', 'Tower Lone Statue', 'Entrance')
add_conditional_lamp('Tower Lone Statue WN', 'Tower Lone Statue', 'Entrance')
add_conditional_lamp('Tower Dark Maze EN', 'Tower Dark Maze', 'Entrance')
add_conditional_lamp('Tower Dark Maze ES', 'Tower Dark Maze', 'Entrance')
add_conditional_lamp('Tower Dark Chargers WS', 'Tower Dark Chargers', 'Entrance')
add_conditional_lamp('Tower Dark Chargers Up Stairs', 'Tower Dark Chargers', 'Entrance')
add_conditional_lamp('Tower Dual Statues Down Stairs', 'Tower Dual Statues', 'Entrance')
add_conditional_lamp('Tower Dual Statues WS', 'Tower Dual Statues', 'Entrance')
add_conditional_lamp('Tower Dark Pits ES', 'Tower Dark Pits', 'Entrance')
add_conditional_lamp('Tower Dark Pits EN', 'Tower Dark Pits', 'Entrance')
add_conditional_lamp('Tower Dark Archers WN', 'Tower Dark Archers', 'Entrance')
add_conditional_lamp('Tower Dark Archers Up Stairs', 'Tower Dark Archers', 'Entrance')
add_conditional_lamp('Castle Tower - Dark Maze', 'Tower Dark Maze', 'Location')
add_conditional_lamp('Castle Tower - Dark Archer Key Drop', 'Tower Dark Archers', 'Location')
dark_rooms = {
'TR Dark Ride': {'sewer': False, 'entrances': ['TR Dark Ride Up Stairs', 'TR Dark Ride SW'], 'locations': []},
'Mire Dark Shooters': {'sewer': False, 'entrances': ['Mire Dark Shooters Up Stairs', 'Mire Dark Shooters SW', 'Mire Dark Shooters SE'], 'locations': []},
'Mire Key Rupees': {'sewer': False, 'entrances': ['Mire Key Rupees NE'], 'locations': []},
'Mire Block X': {'sewer': False, 'entrances': ['Mire Block X NW', 'Mire Block X WS'], 'locations': []},
'Mire Tall Dark and Roomy': {'sewer': False, 'entrances': ['Mire Tall Dark and Roomy ES', 'Mire Tall Dark and Roomy WS', 'Mire Tall Dark and Roomy WN'], 'locations': []},
'Mire Crystal Right': {'sewer': False, 'entrances': ['Mire Crystal Right ES'], 'locations': []},
'Mire Crystal Mid': {'sewer': False, 'entrances': ['Mire Crystal Mid NW'], 'locations': []},
'Mire Crystal Left': {'sewer': False, 'entrances': ['Mire Crystal Left WS'], 'locations': []},
'Mire Crystal Top': {'sewer': False, 'entrances': ['Mire Crystal Top SW'], 'locations': []},
'Mire Shooter Rupees': {'sewer': False, 'entrances': ['Mire Shooter Rupees EN'], 'locations': []},
'PoD Dark Alley': {'sewer': False, 'entrances': ['PoD Dark Alley NE'], 'locations': []},
'PoD Callback': {'sewer': False, 'entrances': ['PoD Callback WS', 'PoD Callback Warp'], 'locations': []},
'PoD Turtle Party': {'sewer': False, 'entrances': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], 'locations': []},
'PoD Lonely Turtle': {'sewer': False, 'entrances': ['PoD Lonely Turtle SW', 'PoD Lonely Turtle EN'], 'locations': []},
'PoD Dark Pegs': {'sewer': False, 'entrances': ['PoD Dark Pegs Up Ladder', 'PoD Dark Pegs WN'], 'locations': []},
'PoD Dark Basement': {'sewer': False, 'entrances': ['PoD Dark Basement W Up Stairs', 'PoD Dark Basement E Up Stairs'], 'locations': ['Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right']},
'PoD Dark Maze': {'sewer': False, 'entrances': ['PoD Dark Maze EN', 'PoD Dark Maze E'], 'locations': ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom']},
'Eastern Dark Square': {'sewer': False, 'entrances': ['Eastern Dark Square NW', 'Eastern Dark Square Key Door WN', 'Eastern Dark Square EN'], 'locations': []},
'Eastern Dark Pots': {'sewer': False, 'entrances': ['Eastern Dark Pots WN'], 'locations': ['Eastern Palace - Dark Square Pot Key']},
'Eastern Darkness': {'sewer': False, 'entrances': ['Eastern Darkness S', 'Eastern Darkness Up Stairs', 'Eastern Darkness NE'], 'locations': ['Eastern Palace - Dark Eyegore Key Drop']},
'Eastern Rupees': {'sewer': False, 'entrances': ['Eastern Rupees SE'], 'locations': []},
'Tower Lone Statue': {'sewer': False, 'entrances': ['Tower Lone Statue Down Stairs', 'Tower Lone Statue WN'], 'locations': []},
'Tower Dark Maze': {'sewer': False, 'entrances': ['Tower Dark Maze EN', 'Tower Dark Maze ES'], 'locations': ['Castle Tower - Dark Maze']},
'Tower Dark Chargers': {'sewer': False, 'entrances': ['Tower Dark Chargers WS', 'Tower Dark Chargers Up Stairs'], 'locations': []},
'Tower Dual Statues': {'sewer': False, 'entrances': ['Tower Dual Statues Down Stairs', 'Tower Dual Statues WS'], 'locations': []},
'Tower Dark Pits': {'sewer': False, 'entrances': ['Tower Dark Pits ES', 'Tower Dark Pits EN'], 'locations': []},
'Tower Dark Archers': {'sewer': False, 'entrances': ['Tower Dark Archers WN', 'Tower Dark Archers Up Stairs'], 'locations': ['Castle Tower - Dark Archer Key Drop']},
'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 Key Rat': {'sewer': True, 'entrances': ['Sewers Key Rat E', 'Sewers Key Rat Key Door N'], 'locations': ['Hyrule Castle - Key Rat Key Drop']},
}
dark_debug_set = set()
for region, info in dark_rooms.items():
is_dark = False
if not world.sewer_light_cone[player]:
is_dark = True
elif world.doorShuffle[player] != 'crossed' and not info['sewer']:
is_dark = True
elif world.doorShuffle[player] == 'crossed':
sewer_builder = world.dungeon_layouts[player]['Hyrule Castle']
is_dark = region not in sewer_builder.master_sector.region_set()
if is_dark:
dark_debug_set.add(region)
for ent in info['entrances']:
add_conditional_lamp(ent, region, 'Entrance')
for loc in info['locations']:
add_conditional_lamp(loc, region, 'Location')
logging.getLogger('').debug('Non Dark Regions: ' + ', '.join(set(dark_rooms.keys()).difference(dark_debug_set)))
add_conditional_lamp('Old Man', 'Old Man Cave', 'Location')
add_conditional_lamp('Old Man Cave Exit (East)', 'Old Man Cave', 'Entrance')
add_conditional_lamp('Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave', 'Entrance')
@@ -752,19 +760,6 @@ def no_glitches_rules(world, player):
add_conditional_lamp('Old Man House Front to Back', 'Old Man House', 'Entrance')
add_conditional_lamp('Old Man House Back to Front', 'Old Man House', 'Entrance')
if not world.sewer_light_cone[player]:
add_lamp_requirement(world.get_location('Sewers - Dark Cross', player), player)
add_lamp_requirement(world.get_entrance('Sewers Behind Tapestry S', player), player)
add_lamp_requirement(world.get_entrance('Sewers Behind Tapestry Down Stairs', player), player)
add_lamp_requirement(world.get_entrance('Sewers Rope Room Up Stairs', player), player)
add_lamp_requirement(world.get_entrance('Sewers Rope Room North Stairs', player), player)
add_lamp_requirement(world.get_entrance('Sewers Dark Cross South Stairs', player), player)
add_lamp_requirement(world.get_entrance('Sewers Dark Cross Key Door N', player), player)
add_lamp_requirement(world.get_entrance('Sewers Dark Cross Key Door S', player), player)
add_lamp_requirement(world.get_entrance('Sewers Water W', player), player)
add_lamp_requirement(world.get_entrance('Sewers Key Rat E', player), player)
add_lamp_requirement(world.get_entrance('Sewers Key Rat Key Door N', player), player)
def open_rules(world, player):
# softlock protection as you can reach the sewers small key door with a guard drop key
@@ -795,10 +790,42 @@ def swordless_rules(world, player):
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
std_kill_rooms = {
'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S'],
'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'],
'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'],
'Desert Compass Room': ['Desert Compass NW'],
'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'],
'Hera Beetles': ['Hera Beetles WS'],
'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'],
'Tower Dark Archers': ['Tower Dark Archers WN'],
'Tower Red Spears': ['Tower Red Spears WN'],
'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'],
'Tower Circle of Pots': ['Tower Circle of Pots NW'],
'PoD Turtle Party': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], # todo: hammer req. in main rules
'Thieves Basement Block': ['Thieves Basement Block WN'],
'Ice Stalfos Hint': ['Ice Stalfos Hint SE'],
'Ice Pengator Trap': ['Ice Pengator Trap NE'],
'Mire 2': ['Mire 2 NE'],
'Mire Cross': ['Mire Cross ES'],
'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'],
'GT Petting Zoo': ['GT Petting Zoo SE'],
'GT DMs Room': ['GT DMs Room SW'],
'GT Gauntlet 1': ['GT Gauntlet 1 WN'],
'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'],
'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'],
'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'],
'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'],
'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'],
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE']
} # all trap rooms?
def standard_rules(world, 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))
if world.shuffle[player] != 'vanilla':
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):
@@ -815,21 +842,21 @@ def standard_rules(world, player):
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))
escape_builder = world.dungeon_layouts[player]['Hyrule Castle']
for region in escape_builder.master_sector.regions:
for loc in region.locations:
add_rule(loc, lambda state: state.can_kill_most_things(player))
if region.name in std_kill_rooms:
for ent in std_kill_rooms[region.name]:
add_rule(world.get_entrance(ent, player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_location('Hyrule Castle - Map Guard Key Drop', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), lambda state: state.can_kill_most_things(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))
def check_rule_list(state, r_list):
return True if len(r_list) <= 0 else r_list[0](state) and check_rule_list(state, r_list[1:])
rule_list, debug_path = find_rules_for_zelda_delivery(world, player)
set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list))
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))
@@ -853,6 +880,31 @@ def standard_rules(world, player):
add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player))
def find_rules_for_zelda_delivery(world, player):
# path rules for backtracking
start_region = world.get_region('Hyrule Dungeon Cellblock', player)
queue = deque([(start_region, [], [])])
visited = {start_region}
blank_state = CollectionState(world)
while len(queue) > 0:
region, path_rules, path = queue.popleft()
for ext in region.exits:
connect = ext.connected_region
if connect and connect.type == RegionType.Dungeon and connect not in visited:
rule = ext.access_rule
rule_list = list(path_rules)
next_path = list(path)
if not rule(blank_state):
rule_list.append(rule)
next_path.append(ext.name)
if connect.name == 'Sanctuary':
return rule_list, next_path
else:
visited.add(connect)
queue.append((connect, rule_list, next_path))
raise Exception('No path to Sanctuary found')
def set_big_bomb_rules(world, player):
# this is a mess
bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0]
@@ -1291,7 +1343,7 @@ def set_bunny_rules(world, player):
# a) being able to reach it, and
# b) being able to access all entrances from there to `region`
seen = set([region])
queue = collections.deque([(region, [])])
queue = deque([(region, [])])
while queue:
(current, path) = queue.popleft()
for entrance in current.entrances:
@@ -1367,7 +1419,7 @@ def set_inverted_bunny_rules(world, player):
# a) being able to reach it, and
# b) being able to access all entrances from there to `region`
seen = set([region])
queue = collections.deque([(region, [])])
queue = deque([(region, [])])
while queue:
(current, path) = queue.popleft()
for entrance in current.entrances:
+1 -1
View File
@@ -1651,7 +1651,7 @@ class TextTable(object):
text['telepathic_tile_castle_tower'] = CompressedTextMapper.convert("{NOBORDER}\nYou can reflect Agahnim's energy with Sword, Bug-net or Hammer.")
text['telepathic_tile_ice_large_room'] = CompressedTextMapper.convert("{NOBORDER}\nAll right stop collaborate and listen\nIce is back with my brand new invention")
text['telepathic_tile_turtle_rock'] = CompressedTextMapper.convert("{NOBORDER}\nYou shall not pass… without the red cane")
text['telepathic_tile_ice_entrace'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.")
text['telepathic_tile_ice_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.")
text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.")
text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.")
text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2018~~~\nS: Andy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174")
+4 -4
View File
@@ -188,16 +188,16 @@ def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan)
for ent, offset in entrance_offsets.items():
# print(ent)
str = ent
string = ent
for dp, data in entrance_data.items():
byte_array = []
address, size = data
for i in range(0, size):
byte_array.append(old_rom_data[address+(offset*size)+i])
bytes = ', '.join('0x{:02x}'.format(x) for x in byte_array)
str += '\t'+bytes
some_bytes = ', '.join('0x{:02x}'.format(x) for x in byte_array)
string += '\t'+some_bytes
# print("%s: %s" % (dp, bytes))
print(str)
print(string)
def print_wiki_doors(d_regions, world, player):
+2
View File
@@ -63,6 +63,8 @@ org $0DFA53
jsl.l LampCheckOverride
org $028046 ; <- 10046 - Bank02.asm : 217 (JSL EnableForceBlank) (Start of Module_LoadFile)
jsl.l OnFileLoadOverride
org $07A93F ; < 3A93F - Bank07.asm 6548 (LDA $8A : AND.b #$40 - Mirror checks)
jsl.l MirrorCheckOverride
org $05ef47
Sprite_HeartContainer_Override: ;sprite_heart_upgrades.asm : 96-100 (LDA $040C : CMP.b #$1A : BNE .not_in_ganons_tower)
+11
View File
@@ -37,4 +37,15 @@ OnFileLoadOverride:
jsl OnFileLoad ; what I wrote over
lda DRFlags : and #$80 : beq + ;flag is off
lda $7ef086 : ora #$80 : sta $7ef086
+ lda DRFlags : and #$02 : beq +
lda $7ef353 : bne +
lda #$01 : sta $7ef353
+ rtl
MirrorCheckOverride:
lda $8A : and #$40 ; what I wrote over
beq +
lda DRFlags : and #$02 : beq ++
lda $7ef353 : cmp #$01 : beq +
++ lda #$01
+ rtl
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long