Merge pull request #63 from aerinon/DoorDevUnstable
Door dev unstable moving to stable
This commit is contained in:
@@ -220,7 +220,7 @@ jobs:
|
|||||||
body: ${{ steps.release_notes.outputs.body }}
|
body: ${{ steps.release_notes.outputs.body }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorDev')
|
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||||
# upload linux archive asset
|
# upload linux archive asset
|
||||||
- name: Upload Linux Archive Asset
|
- name: Upload Linux Archive Asset
|
||||||
id: upload-linux-asset
|
id: upload-linux-asset
|
||||||
@@ -232,7 +232,7 @@ jobs:
|
|||||||
asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz
|
asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz
|
||||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz
|
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz
|
||||||
asset_content_type: application/gzip
|
asset_content_type: application/gzip
|
||||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorDev')
|
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||||
# upload macos archive asset
|
# upload macos archive asset
|
||||||
- name: Upload MacOS Archive Asset
|
- name: Upload MacOS Archive Asset
|
||||||
id: upload-macos-asset
|
id: upload-macos-asset
|
||||||
@@ -244,7 +244,7 @@ jobs:
|
|||||||
asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz
|
asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz
|
||||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz
|
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz
|
||||||
asset_content_type: application/gzip
|
asset_content_type: application/gzip
|
||||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorDev')
|
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||||
# upload windows archive asset
|
# upload windows archive asset
|
||||||
- name: Upload Windows Archive Asset
|
- name: Upload Windows Archive Asset
|
||||||
id: upload-windows-asset
|
id: upload-windows-asset
|
||||||
@@ -256,4 +256,4 @@ jobs:
|
|||||||
asset_path: ../deploy/windows/ALttPDoorRandomizer.zip
|
asset_path: ../deploy/windows/ALttPDoorRandomizer.zip
|
||||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip
|
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorDev')
|
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||||
|
|||||||
+221
-160
@@ -1,23 +1,31 @@
|
|||||||
import copy
|
import copy
|
||||||
from enum import Enum, unique, Flag
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
from collections import OrderedDict, deque, defaultdict
|
import logging
|
||||||
|
from collections import OrderedDict, Counter, deque, defaultdict
|
||||||
|
from enum import Enum, unique
|
||||||
|
|
||||||
|
try:
|
||||||
|
from fast_enum import FastEnum
|
||||||
|
except ImportError:
|
||||||
|
from enum import Flag
|
||||||
|
FastEnum = Flag
|
||||||
|
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
from EntranceShuffle import door_addresses
|
from EntranceShuffle import door_addresses, indirect_connections
|
||||||
from _vendor.collections_extended import bag
|
|
||||||
from Utils import int16_as_bytes
|
from Utils import int16_as_bytes
|
||||||
from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup
|
from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup
|
||||||
from RoomData import Room
|
from RoomData import Room
|
||||||
|
|
||||||
class World(object):
|
class World(object):
|
||||||
|
|
||||||
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
|
||||||
|
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
||||||
self.players = players
|
self.players = players
|
||||||
self.teams = 1
|
self.teams = 1
|
||||||
self.shuffle = shuffle.copy()
|
self.shuffle = shuffle.copy()
|
||||||
self.doorShuffle = doorShuffle.copy()
|
self.doorShuffle = doorShuffle.copy()
|
||||||
|
self.intensity = {}
|
||||||
self.logic = logic.copy()
|
self.logic = logic.copy()
|
||||||
self.mode = mode.copy()
|
self.mode = mode.copy()
|
||||||
self.swords = swords.copy()
|
self.swords = swords.copy()
|
||||||
@@ -126,6 +134,12 @@ class World(object):
|
|||||||
for region in regions if regions else self.regions:
|
for region in regions if regions else self.regions:
|
||||||
region.world = self
|
region.world = self
|
||||||
self._region_cache[region.player][region.name] = region
|
self._region_cache[region.player][region.name] = region
|
||||||
|
for exit in region.exits:
|
||||||
|
self._entrance_cache[(exit.name, exit.player)] = exit
|
||||||
|
|
||||||
|
def initialize_doors(self, doors):
|
||||||
|
for door in doors:
|
||||||
|
self._door_cache[(door.name, door.player)] = door
|
||||||
|
|
||||||
def get_regions(self, player=None):
|
def get_regions(self, player=None):
|
||||||
return self.regions if player is None else self._region_cache[player].values()
|
return self.regions if player is None else self._region_cache[player].values()
|
||||||
@@ -235,41 +249,41 @@ class World(object):
|
|||||||
if ret.has('Golden Sword', item.player):
|
if ret.has('Golden Sword', item.player):
|
||||||
pass
|
pass
|
||||||
elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 4:
|
elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 4:
|
||||||
ret.prog_items.add(('Golden Sword', item.player))
|
ret.prog_items['Golden Sword', item.player] += 1
|
||||||
elif ret.has('Master Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 3:
|
elif ret.has('Master Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 3:
|
||||||
ret.prog_items.add(('Tempered Sword', item.player))
|
ret.prog_items['Tempered Sword', item.player] += 1
|
||||||
elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 2:
|
elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 2:
|
||||||
ret.prog_items.add(('Master Sword', item.player))
|
ret.prog_items['Master Sword', item.player] += 1
|
||||||
elif self.difficulty_requirements[item.player].progressive_sword_limit >= 1:
|
elif self.difficulty_requirements[item.player].progressive_sword_limit >= 1:
|
||||||
ret.prog_items.add(('Fighter Sword', item.player))
|
ret.prog_items['Fighter Sword', item.player] += 1
|
||||||
elif 'Glove' in item.name:
|
elif 'Glove' in item.name:
|
||||||
if ret.has('Titans Mitts', item.player):
|
if ret.has('Titans Mitts', item.player):
|
||||||
pass
|
pass
|
||||||
elif ret.has('Power Glove', item.player):
|
elif ret.has('Power Glove', item.player):
|
||||||
ret.prog_items.add(('Titans Mitts', item.player))
|
ret.prog_items['Titans Mitts', item.player] += 1
|
||||||
else:
|
else:
|
||||||
ret.prog_items.add(('Power Glove', item.player))
|
ret.prog_items['Power Glove', item.player] += 1
|
||||||
elif 'Shield' in item.name:
|
elif 'Shield' in item.name:
|
||||||
if ret.has('Mirror Shield', item.player):
|
if ret.has('Mirror Shield', item.player):
|
||||||
pass
|
pass
|
||||||
elif ret.has('Red Shield', item.player) and self.difficulty_requirements[item.player].progressive_shield_limit >= 3:
|
elif ret.has('Red Shield', item.player) and self.difficulty_requirements[item.player].progressive_shield_limit >= 3:
|
||||||
ret.prog_items.add(('Mirror Shield', item.player))
|
ret.prog_items['Mirror Shield', item.player] += 1
|
||||||
elif ret.has('Blue Shield', item.player) and self.difficulty_requirements[item.player].progressive_shield_limit >= 2:
|
elif ret.has('Blue Shield', item.player) and self.difficulty_requirements[item.player].progressive_shield_limit >= 2:
|
||||||
ret.prog_items.add(('Red Shield', item.player))
|
ret.prog_items['Red Shield', item.player] += 1
|
||||||
elif self.difficulty_requirements[item.player].progressive_shield_limit >= 1:
|
elif self.difficulty_requirements[item.player].progressive_shield_limit >= 1:
|
||||||
ret.prog_items.add(('Blue Shield', item.player))
|
ret.prog_items['Blue Shield', item.player] += 1
|
||||||
elif 'Bow' in item.name:
|
elif 'Bow' in item.name:
|
||||||
if ret.has('Silver Arrows', item.player):
|
if ret.has('Silver Arrows', item.player):
|
||||||
pass
|
pass
|
||||||
elif ret.has('Bow', item.player) and self.difficulty_requirements[item.player].progressive_bow_limit >= 2:
|
elif ret.has('Bow', item.player) and self.difficulty_requirements[item.player].progressive_bow_limit >= 2:
|
||||||
ret.prog_items.add(('Silver Arrows', item.player))
|
ret.prog_items['Silver Arrows', item.player] += 1
|
||||||
elif self.difficulty_requirements[item.player].progressive_bow_limit >= 1:
|
elif self.difficulty_requirements[item.player].progressive_bow_limit >= 1:
|
||||||
ret.prog_items.add(('Bow', item.player))
|
ret.prog_items['Bow', item.player] += 1
|
||||||
elif item.name.startswith('Bottle'):
|
elif item.name.startswith('Bottle'):
|
||||||
if ret.bottle_count(item.player) < self.difficulty_requirements[item.player].progressive_bottle_limit:
|
if ret.bottle_count(item.player) < self.difficulty_requirements[item.player].progressive_bottle_limit:
|
||||||
ret.prog_items.add((item.name, item.player))
|
ret.prog_items[item.name, item.player] += 1
|
||||||
elif item.advancement or item.smallkey or item.bigkey:
|
elif item.advancement or item.smallkey or item.bigkey:
|
||||||
ret.prog_items.add((item.name, item.player))
|
ret.prog_items[item.name, item.player] += 1
|
||||||
|
|
||||||
for item in self.itempool:
|
for item in self.itempool:
|
||||||
soft_collect(item)
|
soft_collect(item)
|
||||||
@@ -384,7 +398,6 @@ class World(object):
|
|||||||
prog_locations = [location for location in self.get_locations() if location.item is not None and (location.item.advancement or location.event) and location not in state.locations_checked]
|
prog_locations = [location for location in self.get_locations() if location.item is not None and (location.item.advancement or location.event) and location not in state.locations_checked]
|
||||||
|
|
||||||
while prog_locations:
|
while prog_locations:
|
||||||
state.sweep_for_crystal_access()
|
|
||||||
sphere = []
|
sphere = []
|
||||||
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
|
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
|
||||||
for location in prog_locations:
|
for location in prog_locations:
|
||||||
@@ -408,11 +421,10 @@ class World(object):
|
|||||||
class CollectionState(object):
|
class CollectionState(object):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.prog_items = bag()
|
self.prog_items = Counter()
|
||||||
self.world = parent
|
self.world = parent
|
||||||
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
|
self.reachable_regions = {player: dict() for player in range(1, parent.players + 1)}
|
||||||
self.colored_regions = {player: {} for player in range(1, parent.players + 1)}
|
self.blocked_connections = {player: dict() for player in range(1, parent.players + 1)}
|
||||||
self.blocked_color_regions = {player: set() for player in range(1, parent.players + 1)}
|
|
||||||
self.events = []
|
self.events = []
|
||||||
self.path = {}
|
self.path = {}
|
||||||
self.locations_checked = set()
|
self.locations_checked = set()
|
||||||
@@ -421,88 +433,71 @@ class CollectionState(object):
|
|||||||
self.collect(item, True)
|
self.collect(item, True)
|
||||||
|
|
||||||
def update_reachable_regions(self, player):
|
def update_reachable_regions(self, player):
|
||||||
player_regions = self.world.get_regions(player)
|
|
||||||
self.stale[player] = False
|
self.stale[player] = False
|
||||||
rrp = self.reachable_regions[player]
|
rrp = self.reachable_regions[player]
|
||||||
ccr = self.colored_regions[player]
|
bc = self.blocked_connections[player]
|
||||||
blocked = self.blocked_color_regions[player]
|
|
||||||
new_regions = True
|
|
||||||
reachable_regions_count = len(rrp)
|
|
||||||
while new_regions:
|
|
||||||
player_regions = [region for region in player_regions if region not in rrp]
|
|
||||||
for candidate in player_regions:
|
|
||||||
if candidate.can_reach_private(self):
|
|
||||||
rrp.add(candidate)
|
|
||||||
if candidate.type == RegionType.Dungeon:
|
|
||||||
c_switch_present = False
|
|
||||||
for ext in candidate.exits:
|
|
||||||
door = self.world.check_for_door(ext.name, player)
|
|
||||||
if door is not None and door.crystal == CrystalBarrier.Either:
|
|
||||||
c_switch_present = True
|
|
||||||
break
|
|
||||||
if c_switch_present:
|
|
||||||
ccr[candidate] = CrystalBarrier.Either
|
|
||||||
self.spread_crystal_access(candidate, CrystalBarrier.Either, rrp, ccr, player)
|
|
||||||
for ext in candidate.exits:
|
|
||||||
connect = ext.connected_region
|
|
||||||
if connect in rrp and not ext.can_reach(self):
|
|
||||||
blocked.add(candidate)
|
|
||||||
else:
|
|
||||||
color_type = CrystalBarrier.Null
|
|
||||||
for entrance in candidate.entrances:
|
|
||||||
if entrance.parent_region in rrp:
|
|
||||||
if entrance.can_reach(self):
|
|
||||||
door = self.world.check_for_door(entrance.name, player)
|
|
||||||
if door is None or entrance.parent_region.type != RegionType.Dungeon:
|
|
||||||
color_type |= CrystalBarrier.Orange
|
|
||||||
elif entrance.parent_region in ccr.keys():
|
|
||||||
color_type |= (ccr[entrance.parent_region] & (door.crystal or CrystalBarrier.Either))
|
|
||||||
else:
|
|
||||||
blocked.add(entrance.parent_region)
|
|
||||||
if color_type:
|
|
||||||
ccr[candidate] = color_type
|
|
||||||
for ext in candidate.exits:
|
|
||||||
connect = ext.connected_region
|
|
||||||
if connect in rrp and connect in ccr:
|
|
||||||
door = self.world.check_for_door(ext.name, player)
|
|
||||||
if door is not None and not door.blocked:
|
|
||||||
if ext.can_reach(self):
|
|
||||||
new_color = ccr[connect] | (ccr[candidate] & (door.crystal or CrystalBarrier.Either))
|
|
||||||
if new_color != ccr[connect]:
|
|
||||||
self.spread_crystal_access(candidate, new_color, rrp, ccr, player)
|
|
||||||
else:
|
|
||||||
blocked.add(candidate)
|
|
||||||
new_regions = len(rrp) > reachable_regions_count
|
|
||||||
reachable_regions_count = len(rrp)
|
|
||||||
|
|
||||||
def spread_crystal_access(self, region, crystal, rrp, ccr, player):
|
# init on first call - this can't be done on construction since the regions don't exist yet
|
||||||
queue = deque([(region, crystal)])
|
start = self.world.get_region('Menu', player)
|
||||||
visited = set()
|
if not start in rrp:
|
||||||
updated = False
|
rrp[start] = CrystalBarrier.Orange
|
||||||
while len(queue) > 0:
|
for exit in start.exits:
|
||||||
region, crystal = queue.popleft()
|
bc[exit] = CrystalBarrier.Orange
|
||||||
visited.add(region)
|
|
||||||
for ext in region.exits:
|
queue = deque(self.blocked_connections[player].items())
|
||||||
connect = ext.connected_region
|
|
||||||
if connect is not None and connect.type == RegionType.Dungeon:
|
# run BFS on all connections, and keep track of those blocked by missing items
|
||||||
if connect not in visited and connect in rrp and connect in ccr:
|
while True:
|
||||||
if ext.can_reach(self):
|
try:
|
||||||
door = self.world.check_for_door(ext.name, player)
|
connection, crystal_state = queue.popleft()
|
||||||
|
new_region = connection.connected_region
|
||||||
|
if new_region is None or new_region in rrp and (new_region.type != RegionType.Dungeon or (rrp[new_region] & crystal_state) == crystal_state):
|
||||||
|
bc.pop(connection, None)
|
||||||
|
elif connection.can_reach(self):
|
||||||
|
if new_region.type == RegionType.Dungeon:
|
||||||
|
new_crystal_state = crystal_state
|
||||||
|
for exit in new_region.exits:
|
||||||
|
door = exit.door
|
||||||
|
if door is not None and door.crystal == CrystalBarrier.Either:
|
||||||
|
new_crystal_state = CrystalBarrier.Either
|
||||||
|
break
|
||||||
|
if new_region in rrp:
|
||||||
|
new_crystal_state |= rrp[new_region]
|
||||||
|
|
||||||
|
rrp[new_region] = new_crystal_state
|
||||||
|
|
||||||
|
for exit in new_region.exits:
|
||||||
|
door = exit.door
|
||||||
if door is not None and not door.blocked:
|
if door is not None and not door.blocked:
|
||||||
current_crystal = ccr[connect]
|
door_crystal_state = new_crystal_state & (door.crystal or CrystalBarrier.Either)
|
||||||
new_crystal = current_crystal | (crystal & (door.crystal or CrystalBarrier.Either))
|
bc[exit] = door_crystal_state
|
||||||
if current_crystal != new_crystal:
|
queue.append((exit, door_crystal_state))
|
||||||
updated = True
|
elif door is None:
|
||||||
ccr[connect] = new_crystal
|
queue.append((exit, new_crystal_state))
|
||||||
queue.append((connect, new_crystal))
|
else:
|
||||||
return updated
|
new_crystal_state = CrystalBarrier.Orange
|
||||||
|
rrp[new_region] = new_crystal_state
|
||||||
|
bc.pop(connection, None)
|
||||||
|
for exit in new_region.exits:
|
||||||
|
bc[exit] = new_crystal_state
|
||||||
|
queue.append((exit, new_crystal_state))
|
||||||
|
|
||||||
|
self.path[new_region] = (new_region.name, self.path.get(connection, None))
|
||||||
|
|
||||||
|
# Retry connections if the new region can unblock them
|
||||||
|
if new_region.name in indirect_connections:
|
||||||
|
new_entrance = self.world.get_entrance(indirect_connections[new_region.name], player)
|
||||||
|
if new_entrance in bc and new_entrance not in queue and new_entrance.parent_region in rrp:
|
||||||
|
queue.append((new_entrance, rrp[new_entrance.parent_region]))
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
ret = CollectionState(self.world)
|
ret = CollectionState(self.world)
|
||||||
ret.prog_items = self.prog_items.copy()
|
ret.prog_items = self.prog_items.copy()
|
||||||
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)}
|
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)}
|
||||||
ret.colored_regions = {player: copy.copy(self.colored_regions[player]) for player in range(1, self.world.players + 1)}
|
ret.blocked_connections = {player: copy.copy(self.blocked_connections[player]) for player in range(1, self.world.players + 1)}
|
||||||
ret.blocked_color_regions = {player: copy.copy(self.blocked_color_regions[player]) for player in range(1, self.world.players + 1)}
|
|
||||||
ret.events = copy.copy(self.events)
|
ret.events = copy.copy(self.events)
|
||||||
ret.path = copy.copy(self.path)
|
ret.path = copy.copy(self.path)
|
||||||
ret.locations_checked = copy.copy(self.locations_checked)
|
ret.locations_checked = copy.copy(self.locations_checked)
|
||||||
@@ -523,19 +518,6 @@ class CollectionState(object):
|
|||||||
|
|
||||||
return spot.can_reach(self)
|
return spot.can_reach(self)
|
||||||
|
|
||||||
def sweep_for_crystal_access(self):
|
|
||||||
for player, rrp in self.reachable_regions.items():
|
|
||||||
updated = True
|
|
||||||
while updated:
|
|
||||||
if self.stale[player]:
|
|
||||||
self.update_reachable_regions(player)
|
|
||||||
updated = False
|
|
||||||
dungeon_regions = self.blocked_color_regions[player]
|
|
||||||
ccr = self.colored_regions[player]
|
|
||||||
for region in dungeon_regions.copy():
|
|
||||||
if region in ccr.keys():
|
|
||||||
updated |= self.spread_crystal_access(region, ccr[region], rrp, ccr, player)
|
|
||||||
self.stale[player] = updated
|
|
||||||
|
|
||||||
def sweep_for_events(self, key_only=False, locations=None):
|
def sweep_for_events(self, key_only=False, locations=None):
|
||||||
# this may need improvement
|
# this may need improvement
|
||||||
@@ -554,18 +536,13 @@ class CollectionState(object):
|
|||||||
self.collect(event.item, True, event)
|
self.collect(event.item, True, event)
|
||||||
new_locations = len(reachable_events) > checked_locations
|
new_locations = len(reachable_events) > checked_locations
|
||||||
checked_locations = len(reachable_events)
|
checked_locations = len(reachable_events)
|
||||||
if new_locations:
|
|
||||||
self.sweep_for_crystal_access()
|
|
||||||
|
|
||||||
def can_reach_blue(self, region, player):
|
def can_reach_blue(self, region, player):
|
||||||
if region not in self.colored_regions[player].keys():
|
return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either]
|
||||||
return False
|
|
||||||
return self.colored_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either]
|
|
||||||
|
|
||||||
def can_reach_orange(self, region, player):
|
def can_reach_orange(self, region, player):
|
||||||
if region not in self.colored_regions[player].keys():
|
return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Orange, CrystalBarrier.Either]
|
||||||
return False
|
|
||||||
return self.colored_regions[player][region] in [CrystalBarrier.Orange, CrystalBarrier.Either]
|
|
||||||
|
|
||||||
def _do_not_flood_the_keys(self, reachable_events):
|
def _do_not_flood_the_keys(self, reachable_events):
|
||||||
adjusted_checks = list(reachable_events)
|
adjusted_checks = list(reachable_events)
|
||||||
@@ -584,14 +561,14 @@ class CollectionState(object):
|
|||||||
def has(self, item, player, count=1):
|
def has(self, item, player, count=1):
|
||||||
if count == 1:
|
if count == 1:
|
||||||
return (item, player) in self.prog_items
|
return (item, player) in self.prog_items
|
||||||
return self.prog_items.count((item, player)) >= count
|
return self.prog_items[item, player] >= count
|
||||||
|
|
||||||
def has_key(self, item, player, count=1):
|
def has_key(self, item, player, count=1):
|
||||||
if self.world.retro[player]:
|
if self.world.retro[player]:
|
||||||
return self.can_buy_unlimited('Small Key (Universal)', player)
|
return self.can_buy_unlimited('Small Key (Universal)', player)
|
||||||
if count == 1:
|
if count == 1:
|
||||||
return (item, player) in self.prog_items
|
return (item, player) in self.prog_items
|
||||||
return self.prog_items.count((item, player)) >= count
|
return self.prog_items[item, player] >= count
|
||||||
|
|
||||||
def can_buy_unlimited(self, item, player):
|
def can_buy_unlimited(self, item, player):
|
||||||
for shop in self.world.shops:
|
for shop in self.world.shops:
|
||||||
@@ -600,7 +577,7 @@ class CollectionState(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def item_count(self, item, player):
|
def item_count(self, item, player):
|
||||||
return self.prog_items.count((item, player))
|
return self.prog_items[item, player]
|
||||||
|
|
||||||
def has_crystals(self, count, player):
|
def has_crystals(self, count, player):
|
||||||
crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
|
crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
|
||||||
@@ -634,9 +611,9 @@ class CollectionState(object):
|
|||||||
|
|
||||||
def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
|
def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
|
||||||
basemagic = 8
|
basemagic = 8
|
||||||
if self.has('Quarter Magic', player):
|
if self.has('Magic Upgrade (1/4)', player):
|
||||||
basemagic = 32
|
basemagic = 32
|
||||||
elif self.has('Half Magic', player):
|
elif self.has('Magic Upgrade (1/2)', player):
|
||||||
basemagic = 16
|
basemagic = 16
|
||||||
if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player):
|
if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player):
|
||||||
if self.world.difficulty_adjustments[player] == 'hard' and not fullrefill:
|
if self.world.difficulty_adjustments[player] == 'hard' and not fullrefill:
|
||||||
@@ -657,9 +634,8 @@ class CollectionState(object):
|
|||||||
|
|
||||||
def can_shoot_arrows(self, player):
|
def can_shoot_arrows(self, player):
|
||||||
if self.world.retro[player]:
|
if self.world.retro[player]:
|
||||||
#TODO: need to decide how we want to handle wooden arrows longer-term (a can-buy-a check, or via dynamic shop location)
|
#todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe
|
||||||
#FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe
|
return self.has('Bow', player) and self.can_buy_unlimited('Single Arrow', player)
|
||||||
return self.has('Bow', player) and (self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player))
|
|
||||||
return self.has('Bow', player)
|
return self.has('Bow', player)
|
||||||
|
|
||||||
def can_get_good_bee(self, player):
|
def can_get_good_bee(self, player):
|
||||||
@@ -734,63 +710,63 @@ class CollectionState(object):
|
|||||||
if self.has('Golden Sword', item.player):
|
if self.has('Golden Sword', item.player):
|
||||||
pass
|
pass
|
||||||
elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 4:
|
elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 4:
|
||||||
self.prog_items.add(('Golden Sword', item.player))
|
self.prog_items['Golden Sword', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif self.has('Master Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 3:
|
elif self.has('Master Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 3:
|
||||||
self.prog_items.add(('Tempered Sword', item.player))
|
self.prog_items['Tempered Sword', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif self.has('Fighter Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 2:
|
elif self.has('Fighter Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 2:
|
||||||
self.prog_items.add(('Master Sword', item.player))
|
self.prog_items['Master Sword', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif self.world.difficulty_requirements[item.player].progressive_sword_limit >= 1:
|
elif self.world.difficulty_requirements[item.player].progressive_sword_limit >= 1:
|
||||||
self.prog_items.add(('Fighter Sword', item.player))
|
self.prog_items['Fighter Sword', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif 'Glove' in item.name:
|
elif 'Glove' in item.name:
|
||||||
if self.has('Titans Mitts', item.player):
|
if self.has('Titans Mitts', item.player):
|
||||||
pass
|
pass
|
||||||
elif self.has('Power Glove', item.player):
|
elif self.has('Power Glove', item.player):
|
||||||
self.prog_items.add(('Titans Mitts', item.player))
|
self.prog_items['Titans Mitts', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
self.prog_items.add(('Power Glove', item.player))
|
self.prog_items['Power Glove', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif 'Shield' in item.name:
|
elif 'Shield' in item.name:
|
||||||
if self.has('Mirror Shield', item.player):
|
if self.has('Mirror Shield', item.player):
|
||||||
pass
|
pass
|
||||||
elif self.has('Red Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 3:
|
elif self.has('Red Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 3:
|
||||||
self.prog_items.add(('Mirror Shield', item.player))
|
self.prog_items['Mirror Shield', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif self.has('Blue Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 2:
|
elif self.has('Blue Shield', item.player) and self.world.difficulty_requirements[item.player].progressive_shield_limit >= 2:
|
||||||
self.prog_items.add(('Red Shield', item.player))
|
self.prog_items['Red Shield', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif self.world.difficulty_requirements[item.player].progressive_shield_limit >= 1:
|
elif self.world.difficulty_requirements[item.player].progressive_shield_limit >= 1:
|
||||||
self.prog_items.add(('Blue Shield', item.player))
|
self.prog_items['Blue Shield', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif 'Bow' in item.name:
|
elif 'Bow' in item.name:
|
||||||
if self.has('Silver Arrows', item.player):
|
if self.has('Silver Arrows', item.player):
|
||||||
pass
|
pass
|
||||||
elif self.has('Bow', item.player):
|
elif self.has('Bow', item.player):
|
||||||
self.prog_items.add(('Silver Arrows', item.player))
|
self.prog_items['Silver Arrows', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
self.prog_items.add(('Bow', item.player))
|
self.prog_items['Bow', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif 'Armor' in item.name:
|
elif 'Armor' in item.name:
|
||||||
if self.has('Red Mail', item.player):
|
if self.has('Red Mail', item.player):
|
||||||
pass
|
pass
|
||||||
elif self.has('Blue Mail', item.player):
|
elif self.has('Blue Mail', item.player):
|
||||||
self.prog_items.add(('Red Mail', item.player))
|
self.prog_items['Red Mail', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
self.prog_items.add(('Blue Mail', item.player))
|
self.prog_items['Blue Mail', item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
elif item.name.startswith('Bottle'):
|
elif item.name.startswith('Bottle'):
|
||||||
if self.bottle_count(item.player) < self.world.difficulty_requirements[item.player].progressive_bottle_limit:
|
if self.bottle_count(item.player) < self.world.difficulty_requirements[item.player].progressive_bottle_limit:
|
||||||
self.prog_items.add((item.name, item.player))
|
self.prog_items[item.name, item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
elif event or item.advancement:
|
elif event or item.advancement:
|
||||||
self.prog_items.add((item.name, item.player))
|
self.prog_items[item.name, item.player] += 1
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
self.stale[item.player] = True
|
self.stale[item.player] = True
|
||||||
@@ -839,13 +815,13 @@ class CollectionState(object):
|
|||||||
to_remove = None
|
to_remove = None
|
||||||
|
|
||||||
if to_remove is not None:
|
if to_remove is not None:
|
||||||
try:
|
|
||||||
self.prog_items.remove((to_remove, item.player))
|
|
||||||
except ValueError:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
self.prog_items[to_remove, item.player] -= 1
|
||||||
|
if self.prog_items[to_remove, item.player] < 1:
|
||||||
|
del (self.prog_items[to_remove, item.player])
|
||||||
# invalidate caches, nothing can be trusted anymore now
|
# invalidate caches, nothing can be trusted anymore now
|
||||||
self.reachable_regions[item.player] = set()
|
self.reachable_regions[item.player] = dict()
|
||||||
|
self.blocked_connections[item.player] = dict()
|
||||||
self.stale[item.player] = True
|
self.stale[item.player] = True
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
@@ -935,10 +911,11 @@ class Entrance(object):
|
|||||||
self.access_rule = lambda state: True
|
self.access_rule = lambda state: True
|
||||||
self.player = player
|
self.player = player
|
||||||
self.door = None
|
self.door = None
|
||||||
|
self.hide_path = False
|
||||||
|
|
||||||
def can_reach(self, state):
|
def can_reach(self, state):
|
||||||
if self.parent_region.can_reach(state) and self.access_rule(state):
|
if self.parent_region.can_reach(state) and self.access_rule(state):
|
||||||
if not self in state.path:
|
if not self.hide_path and not self in state.path:
|
||||||
state.path[self] = (self.name, state.path.get(self.parent_region, (self.parent_region.name, None)))
|
state.path[self] = (self.name, state.path.get(self.parent_region, (self.parent_region.name, None)))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1031,6 +1008,31 @@ class Direction(Enum):
|
|||||||
Down = 5
|
Down = 5
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class Hook(Enum):
|
||||||
|
North = 0
|
||||||
|
West = 1
|
||||||
|
South = 2
|
||||||
|
East = 3
|
||||||
|
Stairs = 4
|
||||||
|
|
||||||
|
|
||||||
|
hook_dir_map = {
|
||||||
|
Direction.North: Hook.North,
|
||||||
|
Direction.South: Hook.South,
|
||||||
|
Direction.West: Hook.West,
|
||||||
|
Direction.East: Hook.East,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hook_from_door(door):
|
||||||
|
if door.type == DoorType.SpiralStairs:
|
||||||
|
return Hook.Stairs
|
||||||
|
if door.type in [DoorType.Normal, DoorType.Open, DoorType.StraightStairs]:
|
||||||
|
return hook_dir_map[door.direction]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Polarity:
|
class Polarity:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vector = [0, 0, 0]
|
self.vector = [0, 0, 0]
|
||||||
@@ -1058,6 +1060,16 @@ class Polarity:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
h = 17
|
||||||
|
spot = self.vector[0]
|
||||||
|
h *= 31 + (spot if spot >= 0 else spot + 100)
|
||||||
|
spot = self.vector[1]
|
||||||
|
h *= 43 + (spot if spot >= 0 else spot + 100)
|
||||||
|
spot = self.vector[2]
|
||||||
|
h *= 73 + (spot if spot >= 0 else spot + 100)
|
||||||
|
return h
|
||||||
|
|
||||||
def is_neutral(self):
|
def is_neutral(self):
|
||||||
for i in range(len(self.vector)):
|
for i in range(len(self.vector)):
|
||||||
if self.vector[i] != 0:
|
if self.vector[i] != 0:
|
||||||
@@ -1076,6 +1088,12 @@ class Polarity:
|
|||||||
result += abs(self.vector[i])
|
result += abs(self.vector[i])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.__unicode__())
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return f'{self.vector}'
|
||||||
|
|
||||||
|
|
||||||
pol_idx = {
|
pol_idx = {
|
||||||
Direction.North: (0, 'Pos'),
|
Direction.North: (0, 'Pos'),
|
||||||
@@ -1104,14 +1122,15 @@ pol_comp = {
|
|||||||
'Mod': lambda x: 0 if x == 0 else 1
|
'Mod': lambda x: 0 if x == 0 else 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class PolSlot(Enum):
|
class PolSlot(Enum):
|
||||||
NorthSouth = 0
|
NorthSouth = 0
|
||||||
EastWest = 1
|
EastWest = 1
|
||||||
Stairs = 2
|
Stairs = 2
|
||||||
|
|
||||||
@unique
|
|
||||||
class CrystalBarrier(Flag):
|
class CrystalBarrier(FastEnum):
|
||||||
Null = 0 # no special requirement
|
Null = 0 # no special requirement
|
||||||
Blue = 1 # blue must be down and explore state set to Blue
|
Blue = 1 # blue must be down and explore state set to Blue
|
||||||
Orange = 2 # orange must be down and explore state set to Orange
|
Orange = 2 # orange must be down and explore state set to Orange
|
||||||
@@ -1161,23 +1180,25 @@ class Door(object):
|
|||||||
entrance.door = self
|
entrance.door = self
|
||||||
|
|
||||||
def getAddress(self):
|
def getAddress(self):
|
||||||
if self.type == DoorType.Normal:
|
if self.type in [DoorType.Normal, DoorType.StraightStairs]:
|
||||||
return 0x13A000 + normal_offset_table[self.roomIndex] * 24 + (self.doorIndex + self.direction.value * 3) * 2
|
return 0x13A000 + normal_offset_table[self.roomIndex] * 24 + (self.doorIndex + self.direction.value * 3) * 2
|
||||||
elif self.type == DoorType.SpiralStairs:
|
elif self.type == DoorType.SpiralStairs:
|
||||||
return 0x13B000 + (spiral_offset_table[self.roomIndex] + self.doorIndex) * 4
|
return 0x13B000 + (spiral_offset_table[self.roomIndex] + self.doorIndex) * 4
|
||||||
elif self.type == DoorType.Open:
|
elif self.type == DoorType.Open:
|
||||||
base_address = {
|
base_address = {
|
||||||
Direction.North: 0x13C500,
|
Direction.North: 0x13C500,
|
||||||
Direction.South: 0x13C533,
|
Direction.South: 0x13C521,
|
||||||
Direction.West: 0x13C566,
|
Direction.West: 0x13C542,
|
||||||
Direction.East: 0x13C581,
|
Direction.East: 0x13C55D,
|
||||||
}
|
}
|
||||||
return base_address[self.direction] + self.edge_id * 3
|
return base_address[self.direction] + self.edge_id * 3
|
||||||
|
|
||||||
def getTarget(self, src):
|
def getTarget(self, src):
|
||||||
if self.type == DoorType.Normal:
|
if self.type in [DoorType.Normal, DoorType.StraightStairs]:
|
||||||
bitmask = 4 * (self.layer ^ 1 if src.toggle else self.layer)
|
bitmask = 4 * (self.layer ^ 1 if src.toggle else self.layer)
|
||||||
bitmask += 0x08 * int(self.trapFlag)
|
bitmask += 0x08 * int(self.trapFlag)
|
||||||
|
if src.type == DoorType.StraightStairs:
|
||||||
|
bitmask += 0x40
|
||||||
return [self.roomIndex, bitmask + self.doorIndex]
|
return [self.roomIndex, bitmask + self.doorIndex]
|
||||||
if self.type == DoorType.SpiralStairs:
|
if self.type == DoorType.SpiralStairs:
|
||||||
bitmask = int(self.layer) << 2
|
bitmask = int(self.layer) << 2
|
||||||
@@ -1187,16 +1208,26 @@ class Door(object):
|
|||||||
return [self.roomIndex, bitmask + self.quadrant, self.shiftX, self.shiftY]
|
return [self.roomIndex, bitmask + self.quadrant, self.shiftX, self.shiftY]
|
||||||
if self.type == DoorType.Open:
|
if self.type == DoorType.Open:
|
||||||
bitmask = self.edge_id
|
bitmask = self.edge_id
|
||||||
bitmask += 0x10 * self.layer
|
bitmask += 0x10 * (self.layer ^ 1 if src.toggle else self.layer)
|
||||||
bitmask += 0x20 * self.quadrant
|
|
||||||
bitmask += 0x80
|
bitmask += 0x80
|
||||||
|
if src.type == DoorType.StraightStairs:
|
||||||
|
bitmask += 0x40
|
||||||
if src.type == DoorType.Open:
|
if src.type == DoorType.Open:
|
||||||
|
bitmask += 0x20 * self.quadrant
|
||||||
fraction = 0x10 * multiply_lookup[src.edge_width][self.edge_width]
|
fraction = 0x10 * multiply_lookup[src.edge_width][self.edge_width]
|
||||||
fraction += divisor_lookup[src.edge_width][self.edge_width]
|
fraction += divisor_lookup[src.edge_width][self.edge_width]
|
||||||
return [self.roomIndex, bitmask, fraction]
|
return [self.roomIndex, bitmask, fraction]
|
||||||
else:
|
else:
|
||||||
|
bitmask += 0x20 * self.quad_indicator()
|
||||||
return [self.roomIndex, bitmask]
|
return [self.roomIndex, bitmask]
|
||||||
|
|
||||||
|
def quad_indicator(self):
|
||||||
|
if self.direction in [Direction.North, Direction.South]:
|
||||||
|
return self.quadrant & 0x1
|
||||||
|
elif self.direction in [Direction.East, Direction.West]:
|
||||||
|
return (self.quadrant & 0x2) >> 1
|
||||||
|
return 0
|
||||||
|
|
||||||
def dir(self, direction, room, doorIndex, layer):
|
def dir(self, direction, room, doorIndex, layer):
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
self.roomIndex = room
|
self.roomIndex = room
|
||||||
@@ -1294,6 +1325,7 @@ class Sector(object):
|
|||||||
self.branch_factor = None
|
self.branch_factor = None
|
||||||
self.dead_end_cnt = None
|
self.dead_end_cnt = None
|
||||||
self.entrance_sector = None
|
self.entrance_sector = None
|
||||||
|
self.destination_entrance = False
|
||||||
self.equations = None
|
self.equations = None
|
||||||
|
|
||||||
def region_set(self):
|
def region_set(self):
|
||||||
@@ -1315,6 +1347,13 @@ class Sector(object):
|
|||||||
magnitude[idx] = magnitude[idx] + 1
|
magnitude[idx] = magnitude[idx] + 1
|
||||||
return magnitude
|
return magnitude
|
||||||
|
|
||||||
|
def hook_magnitude(self):
|
||||||
|
magnitude = [0] * len(Hook)
|
||||||
|
for door in self.outstanding_doors:
|
||||||
|
idx = hook_from_door(door).value
|
||||||
|
magnitude[idx] = magnitude[idx] + 1
|
||||||
|
return magnitude
|
||||||
|
|
||||||
def outflow(self):
|
def outflow(self):
|
||||||
outflow = 0
|
outflow = 0
|
||||||
for door in self.outstanding_doors:
|
for door in self.outstanding_doors:
|
||||||
@@ -1337,7 +1376,7 @@ class Sector(object):
|
|||||||
self.branch_factor -= cnt_dead - 1
|
self.branch_factor -= cnt_dead - 1
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
for ent in region.entrances:
|
for ent in region.entrances:
|
||||||
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
|
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or ent.parent_region.name == 'Sewer Drop':
|
||||||
# same sector as another entrance
|
# same sector as another entrance
|
||||||
if region.name not in ['Skull Pot Circle', 'Skull Back Drop', 'Desert East Lobby', 'Desert West Lobby']:
|
if region.name not in ['Skull Pot Circle', 'Skull Back Drop', 'Desert East Lobby', 'Desert West Lobby']:
|
||||||
self.branch_factor += 1
|
self.branch_factor += 1
|
||||||
@@ -1364,11 +1403,23 @@ class Sector(object):
|
|||||||
self.entrance_sector = True
|
self.entrance_sector = True
|
||||||
return self.entrance_sector
|
return self.entrance_sector
|
||||||
|
|
||||||
|
def get_start_regions(self):
|
||||||
|
if self.is_entrance_sector():
|
||||||
|
starts = []
|
||||||
|
for region in self.regions:
|
||||||
|
for ent in region.entrances:
|
||||||
|
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or ent.parent_region.name == 'Sewer Drop':
|
||||||
|
starts.append(region)
|
||||||
|
return starts
|
||||||
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return '%s' % next(iter(self.region_set()))
|
if len(self.regions) > 0:
|
||||||
|
return f'{self.regions[0].name}'
|
||||||
|
return f'{next(iter(self.region_set()))}'
|
||||||
|
|
||||||
|
|
||||||
class Boss(object):
|
class Boss(object):
|
||||||
@@ -1619,7 +1670,7 @@ class Spoiler(object):
|
|||||||
for index, item in enumerate(shop.inventory):
|
for index, item in enumerate(shop.inventory):
|
||||||
if item is None:
|
if item is None:
|
||||||
continue
|
continue
|
||||||
shopdata['item_{}'.format(index)] = "{} — {}".format(item['item'], item['price']) if item['price'] else item['item']
|
shopdata['item_{}'.format(index)] = "{} - {}".format(item['item'], item['price']) if item['price'] else item['item']
|
||||||
self.shops.append(shopdata)
|
self.shops.append(shopdata)
|
||||||
|
|
||||||
for player in range(1, self.world.players + 1):
|
for player in range(1, self.world.players + 1):
|
||||||
@@ -1654,6 +1705,7 @@ class Spoiler(object):
|
|||||||
'goal': self.world.goal,
|
'goal': self.world.goal,
|
||||||
'shuffle': self.world.shuffle,
|
'shuffle': self.world.shuffle,
|
||||||
'door_shuffle': self.world.doorShuffle,
|
'door_shuffle': self.world.doorShuffle,
|
||||||
|
'intensity': self.world.intensity,
|
||||||
'item_pool': self.world.difficulty,
|
'item_pool': self.world.difficulty,
|
||||||
'item_functionality': self.world.difficulty_adjustments,
|
'item_functionality': self.world.difficulty_adjustments,
|
||||||
'gt_crystals': self.world.crystals_needed_for_gt,
|
'gt_crystals': self.world.crystals_needed_for_gt,
|
||||||
@@ -1716,6 +1768,7 @@ class Spoiler(object):
|
|||||||
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
||||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
||||||
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
||||||
|
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
||||||
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
|
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
|
||||||
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
|
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
|
||||||
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
||||||
@@ -1765,6 +1818,12 @@ class Spoiler(object):
|
|||||||
outfile.write('\n\nShops:\n\n')
|
outfile.write('\n\nShops:\n\n')
|
||||||
outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops))
|
outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops))
|
||||||
|
|
||||||
|
for player in range(1, self.world.players + 1):
|
||||||
|
if self.world.boss_shuffle[player] != 'none':
|
||||||
|
bossmap = self.bosses[player] if self.world.players > 1 else self.bosses
|
||||||
|
outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n')
|
||||||
|
outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']]))
|
||||||
|
|
||||||
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
||||||
# items: Item names
|
# items: Item names
|
||||||
outfile.write('\n\nPlaythrough:\n\n')
|
outfile.write('\n\nPlaythrough:\n\n')
|
||||||
@@ -1773,7 +1832,9 @@ class Spoiler(object):
|
|||||||
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
||||||
# items: Item names
|
# items: Item names
|
||||||
outfile.write('\n\nUnreachable Items:\n\n')
|
outfile.write('\n\nUnreachable Items:\n\n')
|
||||||
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta","items",unreachable.item), self.world.fish.translate("meta","locations",unreachable)) for unreachable in self.unreachables]))
|
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta", "items", unreachable.item.name),
|
||||||
|
self.world.fish.translate("meta", "locations", unreachable.name))
|
||||||
|
for unreachable in self.unreachables]))
|
||||||
|
|
||||||
# rooms: Change up room names; only if it's got no locations in it
|
# rooms: Change up room names; only if it's got no locations in it
|
||||||
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
|
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ def ArmosKnightsDefeatRule(state, player):
|
|||||||
# Magic amounts are probably a bit overkill
|
# Magic amounts are probably a bit overkill
|
||||||
return (
|
return (
|
||||||
state.has_blunt_weapon(player) or
|
state.has_blunt_weapon(player) or
|
||||||
|
state.can_shoot_arrows(player) or
|
||||||
(state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or
|
(state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or
|
||||||
(state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or
|
(state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or
|
||||||
(state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or
|
(state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or
|
||||||
@@ -26,18 +27,19 @@ def ArmosKnightsDefeatRule(state, player):
|
|||||||
state.has('Red Boomerang', player))
|
state.has('Red Boomerang', player))
|
||||||
|
|
||||||
def LanmolasDefeatRule(state, player):
|
def LanmolasDefeatRule(state, player):
|
||||||
# TODO: Allow the canes here?
|
|
||||||
return (
|
return (
|
||||||
state.has_blunt_weapon(player) or
|
state.has_blunt_weapon(player) or
|
||||||
state.has('Fire Rod', player) or
|
state.has('Fire Rod', player) or
|
||||||
state.has('Ice Rod', player) or
|
state.has('Ice Rod', player) or
|
||||||
|
state.has('Cane of Somaria', player) or
|
||||||
|
state.has('Cane of Byrna', player) or
|
||||||
state.can_shoot_arrows(player))
|
state.can_shoot_arrows(player))
|
||||||
|
|
||||||
def MoldormDefeatRule(state, player):
|
def MoldormDefeatRule(state, player):
|
||||||
return state.has_blunt_weapon(player)
|
return state.has_blunt_weapon(player)
|
||||||
|
|
||||||
def HelmasaurKingDefeatRule(state, player):
|
def HelmasaurKingDefeatRule(state, player):
|
||||||
return state.has_blunt_weapon(player) or state.can_shoot_arrows(player)
|
return state.has_sword(player) or state.can_shoot_arrows(player)
|
||||||
|
|
||||||
def ArrghusDefeatRule(state, player):
|
def ArrghusDefeatRule(state, player):
|
||||||
if not state.has('Hookshot', player):
|
if not state.has('Hookshot', player):
|
||||||
@@ -95,7 +97,11 @@ def VitreousDefeatRule(state, player):
|
|||||||
def TrinexxDefeatRule(state, player):
|
def TrinexxDefeatRule(state, player):
|
||||||
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
|
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
|
||||||
return False
|
return False
|
||||||
return state.has('Hammer', player) or state.has_beam_sword(player) or (state.has_sword(player) and state.can_extend_magic(player, 32))
|
return (state.has('Hammer', player) or
|
||||||
|
state.has('Golden Sword', player) or
|
||||||
|
state.has('Tempered Sword', player) or
|
||||||
|
(state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or
|
||||||
|
(state.has_sword(player) and state.can_extend_magic(player, 32)))
|
||||||
|
|
||||||
def AgahnimDefeatRule(state, player):
|
def AgahnimDefeatRule(state, player):
|
||||||
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
||||||
@@ -160,7 +166,7 @@ def place_bosses(world, player):
|
|||||||
all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons
|
all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons
|
||||||
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
||||||
|
|
||||||
if world.boss_shuffle[player] in ["basic", "normal"]:
|
if world.boss_shuffle[player] in ["simple", "full"]:
|
||||||
# temporary hack for swordless kholdstare:
|
# temporary hack for swordless kholdstare:
|
||||||
if world.swords[player] == 'swordless':
|
if world.swords[player] == 'swordless':
|
||||||
world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player)
|
world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player)
|
||||||
@@ -189,7 +195,7 @@ def place_bosses(world, player):
|
|||||||
loc_text = loc + ' (' + level + ')'
|
loc_text = loc + ' (' + level + ')'
|
||||||
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
|
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
|
||||||
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)
|
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)
|
||||||
elif world.boss_shuffle[player] == "chaos": #all bosses chosen at random
|
elif world.boss_shuffle[player] == "random": #all bosses chosen at random
|
||||||
for [loc, level] in boss_locations:
|
for [loc, level] in boss_locations:
|
||||||
loc_text = loc + (' ('+level+')' if level else '')
|
loc_text = loc + (' ('+level+')' if level else '')
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
playerargs = parse_cli(shlex.split(getattr(ret,f"p{player}")), True)
|
playerargs = parse_cli(shlex.split(getattr(ret,f"p{player}")), True)
|
||||||
|
|
||||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
||||||
'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
||||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
||||||
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
||||||
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||||
@@ -141,6 +141,7 @@ def parse_settings():
|
|||||||
"bigkeyshuffle": False,
|
"bigkeyshuffle": False,
|
||||||
"keysanity": False,
|
"keysanity": False,
|
||||||
"door_shuffle": "basic",
|
"door_shuffle": "basic",
|
||||||
|
"intensity": 2,
|
||||||
"experimental": False,
|
"experimental": False,
|
||||||
"dungeon_counters": "default",
|
"dungeon_counters": "default",
|
||||||
|
|
||||||
|
|||||||
+117
-311
@@ -8,13 +8,12 @@ from enum import unique, Flag
|
|||||||
from functools import reduce
|
from functools import reduce
|
||||||
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier
|
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier
|
||||||
from Regions import key_only_locations
|
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, standard_starts, split_region_starts, flexible_starts
|
||||||
from Dungeons import dungeon_regions, region_starts, split_region_starts, flexible_starts
|
from Dungeons import dungeon_bigs, dungeon_keys, dungeon_hints
|
||||||
from Dungeons import drop_entrances, dungeon_bigs, dungeon_keys, dungeon_hints
|
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
from RoomData import DoorKind, PairedDoor
|
from RoomData import DoorKind, PairedDoor
|
||||||
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, validate_tr
|
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths
|
||||||
from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder
|
from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances
|
||||||
from KeyDoorShuffle import analyze_dungeon, validate_vanilla_key_logic, build_key_layout, validate_key_layout
|
from KeyDoorShuffle import analyze_dungeon, validate_vanilla_key_logic, build_key_layout, validate_key_layout
|
||||||
|
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@ def link_doors(world, player):
|
|||||||
connect_interior_doors(edge_a, edge_b, world, player)
|
connect_interior_doors(edge_a, edge_b, world, player)
|
||||||
|
|
||||||
# These connections are here because they are currently unable to be shuffled
|
# These connections are here because they are currently unable to be shuffled
|
||||||
for entrance, ext in straight_staircases:
|
|
||||||
connect_two_way(world, entrance, ext, player)
|
|
||||||
for exitName, regionName in falldown_pits:
|
for exitName, regionName in falldown_pits:
|
||||||
connect_simple_door(world, exitName, regionName, player)
|
connect_simple_door(world, exitName, regionName, player)
|
||||||
for exitName, regionName in dungeon_warps:
|
for exitName, regionName in dungeon_warps:
|
||||||
@@ -37,9 +34,17 @@ def link_doors(world, player):
|
|||||||
for ent, ext in ladders:
|
for ent, ext in ladders:
|
||||||
connect_two_way(world, ent, ext, player)
|
connect_two_way(world, ent, ext, player)
|
||||||
|
|
||||||
|
if world.intensity[player] < 2:
|
||||||
|
for entrance, ext in open_edges:
|
||||||
|
connect_two_way(world, entrance, ext, player)
|
||||||
|
for entrance, ext in straight_staircases:
|
||||||
|
connect_two_way(world, entrance, ext, player)
|
||||||
|
|
||||||
if world.doorShuffle[player] == 'vanilla':
|
if world.doorShuffle[player] == 'vanilla':
|
||||||
for entrance, ext in open_edges:
|
for entrance, ext in open_edges:
|
||||||
connect_two_way(world, entrance, ext, player)
|
connect_two_way(world, entrance, ext, player)
|
||||||
|
for entrance, ext in straight_staircases:
|
||||||
|
connect_two_way(world, entrance, ext, player)
|
||||||
for exitName, regionName in vanilla_logical_connections:
|
for exitName, regionName in vanilla_logical_connections:
|
||||||
connect_simple_door(world, exitName, regionName, player)
|
connect_simple_door(world, exitName, regionName, player)
|
||||||
for entrance, ext in spiral_staircases:
|
for entrance, ext in spiral_staircases:
|
||||||
@@ -50,13 +55,8 @@ def link_doors(world, player):
|
|||||||
connect_one_way(world, ent, ext, player)
|
connect_one_way(world, ent, ext, player)
|
||||||
vanilla_key_logic(world, player)
|
vanilla_key_logic(world, player)
|
||||||
elif world.doorShuffle[player] == 'basic':
|
elif world.doorShuffle[player] == 'basic':
|
||||||
# if not world.experimental[player]:
|
|
||||||
for entrance, ext in open_edges:
|
|
||||||
connect_two_way(world, entrance, ext, player)
|
|
||||||
within_dungeon(world, player)
|
within_dungeon(world, player)
|
||||||
elif world.doorShuffle[player] == 'crossed':
|
elif world.doorShuffle[player] == 'crossed':
|
||||||
for entrance, ext in open_edges:
|
|
||||||
connect_two_way(world, entrance, ext, player)
|
|
||||||
cross_dungeon(world, player)
|
cross_dungeon(world, player)
|
||||||
else:
|
else:
|
||||||
logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player])
|
logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player])
|
||||||
@@ -102,7 +102,8 @@ def create_door_spoiler(world, player):
|
|||||||
for ext in next.exits:
|
for ext in next.exits:
|
||||||
door_a = ext.door
|
door_a = ext.door
|
||||||
connect = ext.connected_region
|
connect = ext.connected_region
|
||||||
if door_a and door_a.type in [DoorType.Normal, DoorType.SpiralStairs] and door_a not in done:
|
if door_a and door_a.type in [DoorType.Normal, DoorType.SpiralStairs, DoorType.Open,
|
||||||
|
DoorType.StraightStairs] and door_a not in done:
|
||||||
done.add(door_a)
|
done.add(door_a)
|
||||||
door_b = door_a.dest
|
door_b = door_a.dest
|
||||||
if door_b:
|
if door_b:
|
||||||
@@ -143,13 +144,13 @@ def vanilla_key_logic(world, player):
|
|||||||
while len(sector_queue) > 0:
|
while len(sector_queue) > 0:
|
||||||
builder = sector_queue.popleft()
|
builder = sector_queue.popleft()
|
||||||
|
|
||||||
|
split_dungeon = builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
|
||||||
origin_list = list(entrances_map[builder.name])
|
origin_list = list(entrances_map[builder.name])
|
||||||
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, builder.name)
|
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, builder.name)
|
||||||
origin_list_sans_drops = remove_drop_origins(origin_list)
|
if len(origin_list) <= 0 or not pre_validate(builder, origin_list, split_dungeon, world, player):
|
||||||
if len(origin_list_sans_drops) <= 0:
|
|
||||||
if last_key == builder.name or loops > 1000:
|
if last_key == builder.name or loops > 1000:
|
||||||
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name
|
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name if len(origin_list) > 0 else 'no origin'
|
||||||
raise Exception('Infinite loop detected for "%s" located at %s' % builder.name, origin_name)
|
raise Exception('Infinite loop detected for "%s" located at %s' % (builder.name, origin_name))
|
||||||
sector_queue.append(builder)
|
sector_queue.append(builder)
|
||||||
last_key = builder.name
|
last_key = builder.name
|
||||||
loops += 1
|
loops += 1
|
||||||
@@ -161,6 +162,7 @@ def vanilla_key_logic(world, player):
|
|||||||
valid = validate_key_layout(key_layout, world, player)
|
valid = validate_key_layout(key_layout, world, player)
|
||||||
if not valid:
|
if not valid:
|
||||||
logging.getLogger('').warning('Vanilla key layout not valid %s', builder.name)
|
logging.getLogger('').warning('Vanilla key layout not valid %s', builder.name)
|
||||||
|
builder.key_door_proposal = doors
|
||||||
if player not in world.key_logic.keys():
|
if player not in world.key_logic.keys():
|
||||||
world.key_logic[player] = {}
|
world.key_logic[player] = {}
|
||||||
analyze_dungeon(key_layout, world, player)
|
analyze_dungeon(key_layout, world, player)
|
||||||
@@ -318,25 +320,35 @@ def within_dungeon(world, player):
|
|||||||
dungeon_builders[key] = simple_dungeon_builder(key, sector_list)
|
dungeon_builders[key] = simple_dungeon_builder(key, sector_list)
|
||||||
dungeon_builders[key].entrance_list = list(entrances_map[key])
|
dungeon_builders[key].entrance_list = list(entrances_map[key])
|
||||||
recombinant_builders = {}
|
recombinant_builders = {}
|
||||||
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, world.fish)
|
builder_info = None, None, world, player
|
||||||
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
paths = determine_required_paths(world, player)
|
paths = determine_required_paths(world, player)
|
||||||
check_required_paths(paths, world, player)
|
check_required_paths(paths, world, player)
|
||||||
|
|
||||||
# shuffle_key_doors for dungeons
|
# shuffle_key_doors for dungeons
|
||||||
|
logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors"))
|
||||||
start = time.process_time()
|
start = time.process_time()
|
||||||
for builder in world.dungeon_layouts[player].values():
|
for builder in world.dungeon_layouts[player].values():
|
||||||
shuffle_key_doors(builder, world, player)
|
shuffle_key_doors(builder, world, player)
|
||||||
logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","keydoor.shuffle.time"), time.process_time()-start)
|
logging.getLogger('').info('%s: %s', world.fish.translate("cli", "cli", "keydoor.shuffle.time"), time.process_time()-start)
|
||||||
smooth_door_pairs(world, player)
|
smooth_door_pairs(world, player)
|
||||||
|
|
||||||
|
|
||||||
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, fish):
|
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info):
|
||||||
for name, split_list in split_region_starts.items():
|
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
||||||
|
if dungeon_entrances is None:
|
||||||
|
dungeon_entrances = default_dungeon_entrances
|
||||||
|
if split_dungeon_entrances is None:
|
||||||
|
split_dungeon_entrances = split_region_starts
|
||||||
|
builder_info = dungeon_entrances, split_region_starts, world, player
|
||||||
|
|
||||||
|
for name, split_list in split_dungeon_entrances.items():
|
||||||
builder = dungeon_builders.pop(name)
|
builder = dungeon_builders.pop(name)
|
||||||
recombinant_builders[name] = builder
|
recombinant_builders[name] = builder
|
||||||
split_builders = split_dungeon_builder(builder, split_list, fish)
|
|
||||||
|
split_builders = split_dungeon_builder(builder, split_list, builder_info)
|
||||||
dungeon_builders.update(split_builders)
|
dungeon_builders.update(split_builders)
|
||||||
for sub_name, split_entrances in split_list.items():
|
for sub_name, split_entrances in split_list.items():
|
||||||
sub_builder = dungeon_builders[name+' '+sub_name]
|
sub_builder = dungeon_builders[name+' '+sub_name]
|
||||||
@@ -353,6 +365,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
|
|||||||
enabled_entrances = {}
|
enabled_entrances = {}
|
||||||
sector_queue = deque(dungeon_builders.values())
|
sector_queue = deque(dungeon_builders.values())
|
||||||
last_key, loops = None, 0
|
last_key, loops = None, 0
|
||||||
|
logging.getLogger('').info(world.fish.translate("cli", "cli", "generating.dungeon"))
|
||||||
while len(sector_queue) > 0:
|
while len(sector_queue) > 0:
|
||||||
builder = sector_queue.popleft()
|
builder = sector_queue.popleft()
|
||||||
split_dungeon = builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
|
split_dungeon = builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
|
||||||
@@ -361,17 +374,15 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
|
|||||||
name = ' '.join(builder.name.split(' ')[:-1])
|
name = ' '.join(builder.name.split(' ')[:-1])
|
||||||
origin_list = list(builder.entrance_list)
|
origin_list = list(builder.entrance_list)
|
||||||
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, name)
|
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, name)
|
||||||
origin_list_sans_drops = remove_drop_origins(origin_list)
|
if len(origin_list) <= 0 or not pre_validate(builder, origin_list, split_dungeon, world, player):
|
||||||
if len(origin_list_sans_drops) <= 0 or name == "Turtle Rock" and not validate_tr(builder, origin_list_sans_drops, world, player):
|
|
||||||
if last_key == builder.name or loops > 1000:
|
if last_key == builder.name or loops > 1000:
|
||||||
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name
|
origin_name = world.get_region(origin_list[0], player).entrances[0].parent_region.name if len(origin_list) > 0 else 'no origin'
|
||||||
raise Exception('Infinite loop detected for "%s" located at %s' % builder.name, origin_name)
|
raise Exception('Infinite loop detected for "%s" located at %s' % (builder.name, origin_name))
|
||||||
sector_queue.append(builder)
|
sector_queue.append(builder)
|
||||||
last_key = builder.name
|
last_key = builder.name
|
||||||
loops += 1
|
loops += 1
|
||||||
else:
|
else:
|
||||||
logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","generating.dungeon"), builder.name)
|
ds = generate_dungeon(builder, origin_list, split_dungeon, world, player)
|
||||||
ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player)
|
|
||||||
find_new_entrances(ds, entrances_map, connections, potentials, enabled_entrances, world, player)
|
find_new_entrances(ds, entrances_map, connections, potentials, enabled_entrances, world, player)
|
||||||
ds.name = name
|
ds.name = name
|
||||||
builder.master_sector = ds
|
builder.master_sector = ds
|
||||||
@@ -391,11 +402,13 @@ def determine_entrance_list(world, player):
|
|||||||
connections = {}
|
connections = {}
|
||||||
for key, r_names in region_starts.items():
|
for key, r_names in region_starts.items():
|
||||||
entrance_map[key] = []
|
entrance_map[key] = []
|
||||||
|
if world.mode[player] == 'standard' and key in standard_starts.keys():
|
||||||
|
r_names = standard_starts[key]
|
||||||
for region_name in r_names:
|
for region_name in r_names:
|
||||||
region = world.get_region(region_name, player)
|
region = world.get_region(region_name, player)
|
||||||
for ent in region.entrances:
|
for ent in region.entrances:
|
||||||
parent = ent.parent_region
|
parent = ent.parent_region
|
||||||
if parent.type != RegionType.Dungeon or parent.name == 'Sewer Drop':
|
if (parent.type != RegionType.Dungeon and parent.name != 'Menu') or parent.name == 'Sewer Drop':
|
||||||
if parent.name not in world.inaccessible_regions[player]:
|
if parent.name not in world.inaccessible_regions[player]:
|
||||||
entrance_map[key].append(region_name)
|
entrance_map[key].append(region_name)
|
||||||
else:
|
else:
|
||||||
@@ -406,11 +419,6 @@ def determine_entrance_list(world, player):
|
|||||||
return entrance_map, potential_entrances, connections
|
return entrance_map, potential_entrances, connections
|
||||||
|
|
||||||
|
|
||||||
# todo: kill drop exceptions
|
|
||||||
def drop_exception(name):
|
|
||||||
return name in ['Skull Pot Circle', 'Skull Back Drop']
|
|
||||||
|
|
||||||
|
|
||||||
def add_shuffled_entrances(sectors, region_list, entrance_list):
|
def add_shuffled_entrances(sectors, region_list, entrance_list):
|
||||||
for sector in sectors:
|
for sector in sectors:
|
||||||
for region in sector.regions:
|
for region in sector.regions:
|
||||||
@@ -428,26 +436,20 @@ def find_enabled_origins(sectors, enabled, entrance_list, entrance_map, key):
|
|||||||
if key not in entrance_map.keys():
|
if key not in entrance_map.keys():
|
||||||
key = ' '.join(key.split(' ')[:-1])
|
key = ' '.join(key.split(' ')[:-1])
|
||||||
entrance_map[key].append(region.name)
|
entrance_map[key].append(region.name)
|
||||||
if drop_exception(region.name): # only because they have unique regions
|
|
||||||
entrance_list.append(region.name)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_drop_origins(entrance_list):
|
|
||||||
return [x for x in entrance_list if x not in drop_entrances]
|
|
||||||
|
|
||||||
|
|
||||||
def find_new_entrances(sector, entrances_map, connections, potentials, enabled, world, player):
|
def find_new_entrances(sector, entrances_map, connections, potentials, enabled, world, player):
|
||||||
for region in sector.regions:
|
for region in sector.regions:
|
||||||
if region.name in connections.keys() and (connections[region.name] in potentials.keys() or connections[region.name].name in world.inaccessible_regions[player]):
|
if region.name in connections.keys() and (connections[region.name] in potentials.keys() or connections[region.name].name in world.inaccessible_regions[player]):
|
||||||
enable_new_entrances(region, connections, potentials, enabled, world, player)
|
enable_new_entrances(region, connections, potentials, enabled, world, player, region)
|
||||||
inverted_aga_check(entrances_map, connections, potentials, enabled, world, player)
|
inverted_aga_check(entrances_map, connections, potentials, enabled, world, player)
|
||||||
|
|
||||||
|
|
||||||
def enable_new_entrances(region, connections, potentials, enabled, world, player):
|
def enable_new_entrances(region, connections, potentials, enabled, world, player, region_enabler):
|
||||||
new_region = connections[region.name]
|
new_region = connections[region.name]
|
||||||
if new_region in potentials.keys():
|
if new_region in potentials.keys():
|
||||||
for potential in potentials.pop(new_region):
|
for potential in potentials.pop(new_region):
|
||||||
enabled[potential] = (region.name, region.dungeon)
|
enabled[potential] = (region_enabler.name, region_enabler.dungeon)
|
||||||
# see if this unexplored region connects elsewhere
|
# see if this unexplored region connects elsewhere
|
||||||
queue = deque(new_region.exits)
|
queue = deque(new_region.exits)
|
||||||
visited = set()
|
visited = set()
|
||||||
@@ -469,9 +471,10 @@ def inverted_aga_check(entrances_map, connections, potentials, enabled, world, p
|
|||||||
if 'Agahnims Tower' in entrances_map.keys() or aga_tower_enabled(enabled):
|
if 'Agahnims Tower' in entrances_map.keys() or aga_tower_enabled(enabled):
|
||||||
for region in list(potentials.keys()):
|
for region in list(potentials.keys()):
|
||||||
if region.name == 'Hyrule Castle Ledge':
|
if region.name == 'Hyrule Castle Ledge':
|
||||||
|
enabler = world.get_region('Tower Agahnim 1', player)
|
||||||
for r_name in potentials[region]:
|
for r_name in potentials[region]:
|
||||||
new_region = world.get_region(r_name, player)
|
new_region = world.get_region(r_name, player)
|
||||||
enable_new_entrances(new_region, connections, potentials, enabled, world, player)
|
enable_new_entrances(new_region, connections, potentials, enabled, world, player, enabler)
|
||||||
|
|
||||||
|
|
||||||
def aga_tower_enabled(enabled):
|
def aga_tower_enabled(enabled):
|
||||||
@@ -482,191 +485,6 @@ def aga_tower_enabled(enabled):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def within_dungeon_legacy(world, player):
|
|
||||||
# TODO: The "starts" regions need access logic
|
|
||||||
# Aerinon's note: I think this is handled already by ER Rules - may need to check correct requirements
|
|
||||||
dungeon_region_starts_es = ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room']
|
|
||||||
dungeon_region_starts_ep = ['Eastern Lobby']
|
|
||||||
dungeon_region_starts_dp = ['Desert Back Lobby', 'Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby']
|
|
||||||
dungeon_region_starts_th = ['Hera Lobby']
|
|
||||||
dungeon_region_starts_at = ['Tower Lobby']
|
|
||||||
dungeon_region_starts_pd = ['PoD Lobby']
|
|
||||||
dungeon_region_lists = [
|
|
||||||
(dungeon_region_starts_es, hyrule_castle_regions),
|
|
||||||
(dungeon_region_starts_ep, eastern_regions),
|
|
||||||
(dungeon_region_starts_dp, desert_regions),
|
|
||||||
(dungeon_region_starts_th, hera_regions),
|
|
||||||
(dungeon_region_starts_at, tower_regions),
|
|
||||||
(dungeon_region_starts_pd, pod_regions),
|
|
||||||
]
|
|
||||||
for start_list, region_list in dungeon_region_lists:
|
|
||||||
shuffle_dungeon(world, player, start_list, region_list)
|
|
||||||
|
|
||||||
world.dungeon_layouts[player] = {}
|
|
||||||
for key in dungeon_regions.keys():
|
|
||||||
world.dungeon_layouts[player][key] = (key, region_starts[key])
|
|
||||||
|
|
||||||
|
|
||||||
def shuffle_dungeon(world, player, start_region_names, dungeon_region_names):
|
|
||||||
logger = logging.getLogger('')
|
|
||||||
# Part one - generate a random layout
|
|
||||||
available_regions = []
|
|
||||||
for name in [r for r in dungeon_region_names if r not in start_region_names]:
|
|
||||||
available_regions.append(world.get_region(name, player))
|
|
||||||
random.shuffle(available_regions)
|
|
||||||
|
|
||||||
# "Ugly" doors are doors that we don't want to see from the front, because of some
|
|
||||||
# sort of unsupported key door. To handle them, make a map of "ugly regions" and
|
|
||||||
# never link across them.
|
|
||||||
ugly_regions = {}
|
|
||||||
next_ugly_region = 1
|
|
||||||
|
|
||||||
# Add all start regions to the open set.
|
|
||||||
available_doors = []
|
|
||||||
for name in start_region_names:
|
|
||||||
logger.info("Starting in %s", name)
|
|
||||||
for door in get_doors(world, world.get_region(name, player), player):
|
|
||||||
ugly_regions[door.name] = 0
|
|
||||||
available_doors.append(door)
|
|
||||||
|
|
||||||
# Loop until all available doors are used
|
|
||||||
while len(available_doors) > 0:
|
|
||||||
# Pick a random available door to connect, prioritizing ones that aren't blocked.
|
|
||||||
# This makes them either get picked up through another door (so they head deeper
|
|
||||||
# into the dungeon), or puts them late in the dungeon (so they probably are part
|
|
||||||
# of a loop). Panic if neither of these happens.
|
|
||||||
random.shuffle(available_doors)
|
|
||||||
available_doors.sort(key=lambda door: 1 if door.blocked else 0 if door.ugly else 2)
|
|
||||||
door = available_doors.pop()
|
|
||||||
logger.info('Linking %s', door.name)
|
|
||||||
# Find an available region that has a compatible door
|
|
||||||
connect_region, connect_door = find_compatible_door_in_regions(world, door, available_regions, player)
|
|
||||||
# Also ignore compatible doors if they're blocked; these should only be used to
|
|
||||||
# create loops.
|
|
||||||
if connect_region is not None and not door.blocked:
|
|
||||||
logger.info(' Found new region %s via %s', connect_region.name, connect_door.name)
|
|
||||||
# Apply connection and add the new region's doors to the available list
|
|
||||||
maybe_connect_two_way(world, door, connect_door, player)
|
|
||||||
# Figure out the new room's ugliness region
|
|
||||||
new_room_ugly_region = ugly_regions[door.name]
|
|
||||||
if connect_door.ugly:
|
|
||||||
next_ugly_region += 1
|
|
||||||
new_room_ugly_region = next_ugly_region
|
|
||||||
is_new_region = connect_region in available_regions
|
|
||||||
# Add the doors
|
|
||||||
for door in get_doors(world, connect_region, player):
|
|
||||||
ugly_regions[door.name] = new_room_ugly_region
|
|
||||||
if is_new_region:
|
|
||||||
available_doors.append(door)
|
|
||||||
# If an ugly door is anything but the connect door, panic and die
|
|
||||||
if door != connect_door and door.ugly:
|
|
||||||
logger.info('Failed because of ugly door, trying again.')
|
|
||||||
shuffle_dungeon(world, player, start_region_names, dungeon_region_names)
|
|
||||||
return
|
|
||||||
|
|
||||||
# We've used this region and door, so don't use them again
|
|
||||||
if is_new_region:
|
|
||||||
available_regions.remove(connect_region)
|
|
||||||
if connect_door in available_doors:
|
|
||||||
available_doors.remove(connect_door)
|
|
||||||
else:
|
|
||||||
# If there's no available region with a door, use an internal connection
|
|
||||||
connect_door = find_compatible_door_in_list(ugly_regions, world, door, available_doors, player)
|
|
||||||
if connect_door is not None:
|
|
||||||
logger.info(' Adding loop via %s', connect_door.name)
|
|
||||||
maybe_connect_two_way(world, door, connect_door, player)
|
|
||||||
if connect_door in available_doors:
|
|
||||||
available_doors.remove(connect_door)
|
|
||||||
# Check that we used everything, and retry if we failed
|
|
||||||
if len(available_regions) > 0 or len(available_doors) > 0:
|
|
||||||
logger.info('Failed to add all regions to dungeon, trying again.')
|
|
||||||
shuffle_dungeon(world, player, start_region_names, dungeon_region_names)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
# Connects a and b. Or don't if they're an unsupported connection type.
|
|
||||||
# TODO: This is gross, don't do it this way
|
|
||||||
def maybe_connect_two_way(world, a, b, player):
|
|
||||||
# Return on unsupported types.
|
|
||||||
if a.type in [DoorType.Open, DoorType.StraightStairs, DoorType.Hole, DoorType.Warp, DoorType.Ladder,
|
|
||||||
DoorType.Interior, DoorType.Logical]:
|
|
||||||
return
|
|
||||||
# Connect supported types
|
|
||||||
if a.type == DoorType.Normal or a.type == DoorType.SpiralStairs:
|
|
||||||
if a.blocked:
|
|
||||||
connect_one_way(world, b.name, a.name, player)
|
|
||||||
elif b.blocked:
|
|
||||||
connect_one_way(world, a.name, b.name, player)
|
|
||||||
else:
|
|
||||||
connect_two_way(world, a.name, b.name, player)
|
|
||||||
return
|
|
||||||
# If we failed to account for a type, panic
|
|
||||||
raise RuntimeError('Unknown door type ' + a.type.name)
|
|
||||||
|
|
||||||
|
|
||||||
# Finds a compatible door in regions, returns the region and door
|
|
||||||
def find_compatible_door_in_regions(world, door, regions, player):
|
|
||||||
if door.type in [DoorType.Hole, DoorType.Warp, DoorType.Logical]:
|
|
||||||
return door.dest, door
|
|
||||||
for region in regions:
|
|
||||||
for proposed_door in get_doors(world, region, player):
|
|
||||||
if doors_compatible(door, proposed_door):
|
|
||||||
return region, proposed_door
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
|
|
||||||
def find_compatible_door_in_list(ugly_regions, world, door, doors, player):
|
|
||||||
if door.type in [DoorType.Hole, DoorType.Warp, DoorType.Logical]:
|
|
||||||
return door
|
|
||||||
for proposed_door in doors:
|
|
||||||
if ugly_regions[door.name] != ugly_regions[proposed_door.name]:
|
|
||||||
continue
|
|
||||||
if doors_compatible(door, proposed_door):
|
|
||||||
return proposed_door
|
|
||||||
|
|
||||||
|
|
||||||
def get_doors(world, region, player):
|
|
||||||
res = []
|
|
||||||
for exit in region.exits:
|
|
||||||
door = world.check_for_door(exit.name, player)
|
|
||||||
if door is not None:
|
|
||||||
res.append(door)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def get_entrance_doors(world, region, player):
|
|
||||||
res = []
|
|
||||||
for exit in region.entrances:
|
|
||||||
door = world.check_for_door(exit.name, player)
|
|
||||||
if door is not None:
|
|
||||||
res.append(door)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def doors_compatible(a, b):
|
|
||||||
if a.type != b.type:
|
|
||||||
return False
|
|
||||||
if a.type == DoorType.Open:
|
|
||||||
return doors_fit_mandatory_pair(open_edges, a, b)
|
|
||||||
if a.type == DoorType.StraightStairs:
|
|
||||||
return doors_fit_mandatory_pair(straight_staircases, a, b)
|
|
||||||
if a.type == DoorType.Interior:
|
|
||||||
return doors_fit_mandatory_pair(interior_doors, a, b)
|
|
||||||
if a.type == DoorType.Ladder:
|
|
||||||
return doors_fit_mandatory_pair(ladders, a, b)
|
|
||||||
if a.type == DoorType.Normal and (a.smallKey or b.smallKey or a.bigKey or b.bigKey):
|
|
||||||
return doors_fit_mandatory_pair(key_doors, a, b)
|
|
||||||
if a.type in [DoorType.Hole, DoorType.Warp, DoorType.Logical]:
|
|
||||||
return False # these aren't compatible with anything
|
|
||||||
return a.direction == switch_dir(b.direction)
|
|
||||||
|
|
||||||
|
|
||||||
def doors_fit_mandatory_pair(pair_list, a, b):
|
|
||||||
for pair_a, pair_b in pair_list:
|
|
||||||
if (a.name == pair_a and b.name == pair_b) or (a.name == pair_b and b.name == pair_a):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# goals:
|
# goals:
|
||||||
# 1. have enough chests to be interesting (2 more than dungeon items)
|
# 1. have enough chests to be interesting (2 more than dungeon items)
|
||||||
# 2. have a balanced amount of regions added (check)
|
# 2. have a balanced amount of regions added (check)
|
||||||
@@ -683,10 +501,11 @@ def cross_dungeon(world, player):
|
|||||||
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
||||||
connections_tuple = (entrances_map, potentials, connections)
|
connections_tuple = (entrances_map, potentials, connections)
|
||||||
|
|
||||||
all_sectors = []
|
all_sectors, all_regions = [], []
|
||||||
for key in dungeon_regions.keys():
|
for key in dungeon_regions.keys():
|
||||||
all_sectors.extend(convert_to_sectors(dungeon_regions[key], world, player))
|
all_regions += dungeon_regions[key]
|
||||||
dungeon_builders = create_dungeon_builders(all_sectors, world, player)
|
all_sectors.extend(convert_to_sectors(all_regions, world, player))
|
||||||
|
dungeon_builders = create_dungeon_builders(all_sectors, connections_tuple, world, player)
|
||||||
for builder in dungeon_builders.values():
|
for builder in dungeon_builders.values():
|
||||||
builder.entrance_list = list(entrances_map[builder.name])
|
builder.entrance_list = list(entrances_map[builder.name])
|
||||||
dungeon_obj = world.get_dungeon(builder.name, player)
|
dungeon_obj = world.get_dungeon(builder.name, player)
|
||||||
@@ -698,7 +517,8 @@ def cross_dungeon(world, player):
|
|||||||
key_name = dungeon_keys[builder.name] if loc.name != 'Hyrule Castle - Big Key Drop' else dungeon_bigs[builder.name]
|
key_name = dungeon_keys[builder.name] if loc.name != 'Hyrule Castle - Big Key Drop' else dungeon_bigs[builder.name]
|
||||||
loc.forced_item = loc.item = ItemFactory(key_name, player)
|
loc.forced_item = loc.item = ItemFactory(key_name, player)
|
||||||
recombinant_builders = {}
|
recombinant_builders = {}
|
||||||
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, world.fish)
|
builder_info = None, None, world, player
|
||||||
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
||||||
|
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
@@ -730,6 +550,7 @@ def cross_dungeon(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def assign_cross_keys(dungeon_builders, world, player):
|
def assign_cross_keys(dungeon_builders, world, player):
|
||||||
|
logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors"))
|
||||||
start = time.process_time()
|
start = time.process_time()
|
||||||
total_keys = remaining = 29
|
total_keys = remaining = 29
|
||||||
total_candidates = 0
|
total_candidates = 0
|
||||||
@@ -782,7 +603,7 @@ def assign_cross_keys(dungeon_builders, world, player):
|
|||||||
while len(queue) > 0 and remaining > 0:
|
while len(queue) > 0 and remaining > 0:
|
||||||
builder = queue.popleft()
|
builder = queue.popleft()
|
||||||
name = builder.name
|
name = builder.name
|
||||||
logger.info('Cross Dungeon: Increasing key count by 1 for %s', name)
|
logger.debug('Cross Dungeon: Increasing key count by 1 for %s', name)
|
||||||
builder.key_doors_num += 1
|
builder.key_doors_num += 1
|
||||||
result = find_valid_combination(builder, start_regions_map[name], world, player, drop_keys=False)
|
result = find_valid_combination(builder, start_regions_map[name], world, player, drop_keys=False)
|
||||||
if result:
|
if result:
|
||||||
@@ -793,10 +614,10 @@ def assign_cross_keys(dungeon_builders, world, player):
|
|||||||
queue.append(builder)
|
queue.append(builder)
|
||||||
queue = deque(sorted(queue, key=lambda b: b.combo_size))
|
queue = deque(sorted(queue, key=lambda b: b.combo_size))
|
||||||
else:
|
else:
|
||||||
logger.info('Cross Dungeon: Increase failed for %s', name)
|
logger.debug('Cross Dungeon: Increase failed for %s', name)
|
||||||
builder.key_doors_num -= 1
|
builder.key_doors_num -= 1
|
||||||
builder.flex = 0
|
builder.flex = 0
|
||||||
logger.info('Cross Dungeon: Keys unable to assign in pool %s', remaining)
|
logger.debug('Cross Dungeon: Keys unable to assign in pool %s', remaining)
|
||||||
|
|
||||||
# Last Step: Adjust Small Key Dungeon Pool
|
# Last Step: Adjust Small Key Dungeon Pool
|
||||||
if not world.retro[player]:
|
if not world.retro[player]:
|
||||||
@@ -809,7 +630,7 @@ def assign_cross_keys(dungeon_builders, world, player):
|
|||||||
dungeon.small_keys = []
|
dungeon.small_keys = []
|
||||||
else:
|
else:
|
||||||
dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys
|
dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys
|
||||||
logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","keydoor.shuffle.time.crossed"), time.process_time()-start)
|
logger.info('%s: %s', world.fish.translate("cli", "cli", "keydoor.shuffle.time.crossed"), time.process_time()-start)
|
||||||
|
|
||||||
|
|
||||||
def reassign_boss(boss_region, boss_key, builder, gt, world, player):
|
def reassign_boss(boss_region, boss_key, builder, gt, world, player):
|
||||||
@@ -940,7 +761,7 @@ def find_small_key_door_candidates(builder, start_regions, world, player):
|
|||||||
checked_doors = set()
|
checked_doors = set()
|
||||||
for region in start_regions:
|
for region in start_regions:
|
||||||
possible, checked = find_key_door_candidates(region, checked_doors, world, player)
|
possible, checked = find_key_door_candidates(region, checked_doors, world, player)
|
||||||
candidates.extend(possible)
|
candidates.extend([x for x in possible if x not in candidates])
|
||||||
checked_doors.update(checked)
|
checked_doors.update(checked)
|
||||||
flat_candidates = []
|
flat_candidates = []
|
||||||
for candidate in candidates:
|
for candidate in candidates:
|
||||||
@@ -966,7 +787,6 @@ def calc_used_dungeon_items(builder):
|
|||||||
|
|
||||||
def find_valid_combination(builder, start_regions, world, player, drop_keys=True):
|
def find_valid_combination(builder, start_regions, world, player, drop_keys=True):
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.info('%s %s', world.fish.translate("cli","cli","shuffling.keydoors"), builder.name)
|
|
||||||
# find valid combination of candidates
|
# find valid combination of candidates
|
||||||
if len(builder.candidates) < builder.key_doors_num:
|
if len(builder.candidates) < builder.key_doors_num:
|
||||||
if not drop_keys:
|
if not drop_keys:
|
||||||
@@ -1069,6 +889,9 @@ def flatten_pair_list(paired_list):
|
|||||||
return flat_list
|
return flat_list
|
||||||
|
|
||||||
|
|
||||||
|
okay_normals = [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable, DoorKind.DungeonChanger]
|
||||||
|
|
||||||
|
|
||||||
def find_key_door_candidates(region, checked, world, player):
|
def find_key_door_candidates(region, checked, world, player):
|
||||||
dungeon = region.dungeon
|
dungeon = region.dungeon
|
||||||
candidates = []
|
candidates = []
|
||||||
@@ -1093,11 +916,12 @@ def find_key_door_candidates(region, checked, world, player):
|
|||||||
elif d.type == DoorType.Normal:
|
elif d.type == DoorType.Normal:
|
||||||
d2 = d.dest
|
d2 = d.dest
|
||||||
if d2 not in candidates:
|
if d2 not in candidates:
|
||||||
room_b = world.get_room(d2.roomIndex, player)
|
if d2.type == DoorType.Normal:
|
||||||
pos_b, kind_b = room_b.doorList[d2.doorListPos]
|
room_b = world.get_room(d2.roomIndex, player)
|
||||||
okay_normals = [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable,
|
pos_b, kind_b = room_b.doorList[d2.doorListPos]
|
||||||
DoorKind.Dashable, DoorKind.DungeonChanger]
|
valid = kind in okay_normals and kind_b in okay_normals
|
||||||
valid = kind in okay_normals and kind_b in okay_normals
|
else:
|
||||||
|
valid = kind in okay_normals
|
||||||
if valid and 0 <= d2.doorListPos < 4:
|
if valid and 0 <= d2.doorListPos < 4:
|
||||||
candidates.append(d2)
|
candidates.append(d2)
|
||||||
else:
|
else:
|
||||||
@@ -1182,8 +1006,8 @@ def reassign_key_doors(builder, world, player):
|
|||||||
dp.pair = False
|
dp.pair = False
|
||||||
if not found:
|
if not found:
|
||||||
world.paired_doors[player].append(PairedDoor(d1.name, d2.name))
|
world.paired_doors[player].append(PairedDoor(d1.name, d2.name))
|
||||||
change_door_to_small_key(d1, world, player)
|
change_door_to_small_key(d1, world, player)
|
||||||
change_door_to_small_key(d2, world, player)
|
change_door_to_small_key(d2, world, player)
|
||||||
world.spoiler.set_door_type(d1.name+' <-> '+d2.name, 'Key Door', player)
|
world.spoiler.set_door_type(d1.name+' <-> '+d2.name, 'Key Door', player)
|
||||||
logger.debug('Key Door: %s', d1.name+' <-> '+d2.name)
|
logger.debug('Key Door: %s', d1.name+' <-> '+d2.name)
|
||||||
else:
|
else:
|
||||||
@@ -1214,10 +1038,13 @@ def smooth_door_pairs(world, player):
|
|||||||
partner = door.dest
|
partner = door.dest
|
||||||
skip.add(partner)
|
skip.add(partner)
|
||||||
room_a = world.get_room(door.roomIndex, player)
|
room_a = world.get_room(door.roomIndex, player)
|
||||||
room_b = world.get_room(partner.roomIndex, player)
|
|
||||||
type_a = room_a.kind(door)
|
type_a = room_a.kind(door)
|
||||||
type_b = room_b.kind(partner)
|
if partner.type in [DoorType.Normal, DoorType.Interior]:
|
||||||
valid_pair = stateful_door(door, type_a) and stateful_door(partner, type_b)
|
room_b = world.get_room(partner.roomIndex, player)
|
||||||
|
type_b = room_b.kind(partner)
|
||||||
|
valid_pair = stateful_door(door, type_a) and stateful_door(partner, type_b)
|
||||||
|
else:
|
||||||
|
valid_pair, room_b, type_b = False, None, None
|
||||||
if door.type == DoorType.Normal:
|
if door.type == DoorType.Normal:
|
||||||
if type_a == DoorKind.SmallKey or type_b == DoorKind.SmallKey:
|
if type_a == DoorKind.SmallKey or type_b == DoorKind.SmallKey:
|
||||||
if valid_pair:
|
if valid_pair:
|
||||||
@@ -1304,31 +1131,6 @@ def random_door_type(door, partner, world, player, type_a, type_b, room_a, room_
|
|||||||
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
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'],
|
|
||||||
'Eastern Palace': ['Eastern Boss'],
|
|
||||||
'Desert Palace': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby', 'Desert Boss'],
|
|
||||||
'Tower of Hera': ['Hera Boss'],
|
|
||||||
'Agahnims Tower': ['Tower Agahnim 1'],
|
|
||||||
'Palace of Darkness': ['PoD Boss'],
|
|
||||||
'Swamp Palace': ['Swamp Boss'],
|
|
||||||
'Skull Woods': ['Skull 1 Lobby', 'Skull 2 East Lobby', 'Skull 2 West Lobby', 'Skull Boss'],
|
|
||||||
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
|
||||||
'Ice Palace': ['Ice Boss'],
|
|
||||||
'Misery Mire': ['Mire Boss'],
|
|
||||||
'Turtle Rock': ['TR Main Lobby', 'TR Lazy Eyes', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Boss'],
|
|
||||||
'Ganons Tower': ['GT Agahnim 2']
|
|
||||||
}
|
|
||||||
if world.mode[player] == 'standard':
|
|
||||||
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
|
||||||
# noinspection PyTypeChecker
|
|
||||||
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
|
||||||
if world.doorShuffle[player] in ['basic']:
|
|
||||||
paths['Thieves Town'].append('Thieves Attic Window')
|
|
||||||
return paths
|
|
||||||
|
|
||||||
|
|
||||||
def overworld_prep(world, player):
|
def overworld_prep(world, player):
|
||||||
find_inaccessible_regions(world, player)
|
find_inaccessible_regions(world, player)
|
||||||
add_inaccessible_doors(world, player)
|
add_inaccessible_doors(world, player)
|
||||||
@@ -1357,9 +1159,6 @@ def find_inaccessible_regions(world, player):
|
|||||||
if connect 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)
|
queue.append(connect)
|
||||||
world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)])
|
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':
|
|
||||||
world.inaccessible_regions[player].append('Hyrule Castle Ledge')
|
|
||||||
world.inaccessible_regions[player].append('Sewer Drop')
|
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.debug('Inaccessible Regions:')
|
logger.debug('Inaccessible Regions:')
|
||||||
for r in world.inaccessible_regions[player]:
|
for r in world.inaccessible_regions[player]:
|
||||||
@@ -1393,37 +1192,38 @@ def create_door(world, player, entName, region_name):
|
|||||||
|
|
||||||
def check_required_paths(paths, world, player):
|
def check_required_paths(paths, world, player):
|
||||||
for dungeon_name in paths.keys():
|
for dungeon_name in paths.keys():
|
||||||
builder = world.dungeon_layouts[player][dungeon_name]
|
if dungeon_name in world.dungeon_layouts[player].keys():
|
||||||
if len(paths[dungeon_name]) > 0:
|
builder = world.dungeon_layouts[player][dungeon_name]
|
||||||
states_to_explore = defaultdict(list)
|
if len(paths[dungeon_name]) > 0:
|
||||||
for path in paths[dungeon_name]:
|
states_to_explore = defaultdict(list)
|
||||||
if type(path) is tuple:
|
for path in paths[dungeon_name]:
|
||||||
states_to_explore[tuple([path[0]])].append(path[1])
|
if type(path) is tuple:
|
||||||
else:
|
states_to_explore[tuple([path[0]])].append(path[1])
|
||||||
states_to_explore[tuple(builder.path_entrances)].append(path)
|
else:
|
||||||
cached_initial_state = None
|
states_to_explore[tuple(builder.path_entrances)].append(path)
|
||||||
for start_regs, dest_regs in states_to_explore.items():
|
cached_initial_state = None
|
||||||
check_paths = convert_regions(dest_regs, world, player)
|
for start_regs, dest_regs in states_to_explore.items():
|
||||||
start_regions = convert_regions(start_regs, world, player)
|
check_paths = convert_regions(dest_regs, world, player)
|
||||||
initial = start_regs == tuple(builder.path_entrances)
|
start_regions = convert_regions(start_regs, world, player)
|
||||||
if not initial or cached_initial_state is None:
|
initial = start_regs == tuple(builder.path_entrances)
|
||||||
init = determine_init_crystal(initial, cached_initial_state, start_regions)
|
if not initial or cached_initial_state is None:
|
||||||
state = ExplorationState(init, dungeon_name)
|
init = determine_init_crystal(initial, cached_initial_state, start_regions)
|
||||||
for region in start_regions:
|
state = ExplorationState(init, dungeon_name)
|
||||||
state.visit_region(region)
|
for region in start_regions:
|
||||||
state.add_all_doors_check_unattached(region, world, player)
|
state.visit_region(region)
|
||||||
explore_state(state, world, player)
|
state.add_all_doors_check_unattached(region, world, player)
|
||||||
if initial and cached_initial_state is None:
|
|
||||||
cached_initial_state = state
|
|
||||||
else:
|
|
||||||
state = cached_initial_state
|
|
||||||
valid, bad_region = check_if_regions_visited(state, check_paths)
|
|
||||||
if not valid:
|
|
||||||
if check_for_pinball_fix(state, bad_region, world, player):
|
|
||||||
explore_state(state, world, player)
|
explore_state(state, world, player)
|
||||||
valid, bad_region = check_if_regions_visited(state, check_paths)
|
if initial and cached_initial_state is None:
|
||||||
if not valid:
|
cached_initial_state = state
|
||||||
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
else:
|
||||||
|
state = cached_initial_state
|
||||||
|
valid, bad_region = check_if_regions_visited(state, check_paths)
|
||||||
|
if not valid:
|
||||||
|
if check_for_pinball_fix(state, bad_region, world, player):
|
||||||
|
explore_state(state, world, player)
|
||||||
|
valid, bad_region = check_if_regions_visited(state, check_paths)
|
||||||
|
if not valid:
|
||||||
|
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
||||||
|
|
||||||
|
|
||||||
def determine_init_crystal(initial, state, start_regions):
|
def determine_init_crystal(initial, state, start_regions):
|
||||||
@@ -1466,7 +1266,7 @@ def check_if_regions_visited(state, check_paths):
|
|||||||
|
|
||||||
def check_for_pinball_fix(state, bad_region, world, player):
|
def check_for_pinball_fix(state, bad_region, world, player):
|
||||||
pinball_region = world.get_region('Skull Pinball', player)
|
pinball_region = world.get_region('Skull Pinball', player)
|
||||||
if bad_region.name == 'Skull 2 West Lobby' and state.visited_at_all(pinball_region): #revisit this for entrance shuffle
|
if bad_region.name == 'Skull 2 West Lobby' and state.visited_at_all(pinball_region): # revisit this for entrance shuffle
|
||||||
door = world.get_door('Skull Pinball WS', player)
|
door = world.get_door('Skull Pinball WS', player)
|
||||||
room = world.get_room(door.roomIndex, player)
|
room = world.get_room(door.roomIndex, player)
|
||||||
if room.doorList[door.doorListPos][1] == DoorKind.Trap:
|
if room.doorList[door.doorListPos][1] == DoorKind.Trap:
|
||||||
@@ -1485,12 +1285,15 @@ class DROptions(Flag):
|
|||||||
Eternal_Mini_Bosses = 0x01 # If on, GT minibosses marked as defeated when they try to spawn a heart
|
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
|
Town_Portal = 0x02 # If on, Players will start with mirror scroll
|
||||||
Map_Info = 0x04
|
Map_Info = 0x04
|
||||||
|
Debug = 0x08
|
||||||
Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required
|
Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required
|
||||||
|
|
||||||
# DATA GOES DOWN HERE
|
|
||||||
|
|
||||||
|
# DATA GOES DOWN HERE
|
||||||
logical_connections = [
|
logical_connections = [
|
||||||
('Hyrule Dungeon North Abyss Catwalk Dropdown', 'Hyrule Dungeon North Abyss'),
|
('Hyrule Dungeon North Abyss Catwalk Dropdown', 'Hyrule Dungeon North Abyss'),
|
||||||
|
('Hyrule Castle Throne Room Tapestry', 'Hyrule Castle Behind Tapestry'),
|
||||||
|
('Hyrule Castle Tapestry Backwards', 'Hyrule Castle Throne Room'),
|
||||||
('Sewers Secret Room Push Block', 'Sewers Secret Room Blocked Path'),
|
('Sewers Secret Room Push Block', 'Sewers Secret Room Blocked Path'),
|
||||||
('Eastern Hint Tile Push Block', 'Eastern Hint Tile'),
|
('Eastern Hint Tile Push Block', 'Eastern Hint Tile'),
|
||||||
('Eastern Map Balcony Hook Path', 'Eastern Map Room'),
|
('Eastern Map Balcony Hook Path', 'Eastern Map Room'),
|
||||||
@@ -1499,6 +1302,7 @@ logical_connections = [
|
|||||||
('Desert Main Lobby Right Path', 'Desert Right Alcove'),
|
('Desert Main Lobby Right Path', 'Desert Right Alcove'),
|
||||||
('Desert Left Alcove Path', 'Desert Main Lobby'),
|
('Desert Left Alcove Path', 'Desert Main Lobby'),
|
||||||
('Desert Right Alcove Path', 'Desert Main Lobby'),
|
('Desert Right Alcove Path', 'Desert Main Lobby'),
|
||||||
|
('Hera Big Chest Hook Path', 'Hera Big Chest Landing'),
|
||||||
('Hera Big Chest Landing Exit', 'Hera 4F'),
|
('Hera Big Chest Landing Exit', 'Hera 4F'),
|
||||||
('PoD Pit Room Block Path N', 'PoD Pit Room Blocked'),
|
('PoD Pit Room Block Path N', 'PoD Pit Room Blocked'),
|
||||||
('PoD Pit Room Block Path S', 'PoD Pit Room'),
|
('PoD Pit Room Block Path S', 'PoD Pit Room'),
|
||||||
@@ -1553,6 +1357,8 @@ logical_connections = [
|
|||||||
('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'),
|
('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'),
|
||||||
('Thieves Hellway Blue Barrier', 'Thieves Hellway N Crystal'),
|
('Thieves Hellway Blue Barrier', 'Thieves Hellway N Crystal'),
|
||||||
('Thieves Hellway Crystal Blue Barrier', 'Thieves Hellway'),
|
('Thieves Hellway Crystal Blue Barrier', 'Thieves Hellway'),
|
||||||
|
('Thieves Attic Orange Barrier', 'Thieves Attic Hint'),
|
||||||
|
('Thieves Attic Hint Orange Barrier', 'Thieves Attic'),
|
||||||
('Thieves Basement Block Path', 'Thieves Blocked Entry'),
|
('Thieves Basement Block Path', 'Thieves Blocked Entry'),
|
||||||
('Thieves Blocked Entry Path', 'Thieves Basement Block'),
|
('Thieves Blocked Entry Path', 'Thieves Basement Block'),
|
||||||
('Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block'),
|
('Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block'),
|
||||||
@@ -1710,8 +1516,8 @@ open_edges = [
|
|||||||
('Desert Main Lobby E Edge', 'Desert East Wing W Edge'),
|
('Desert Main Lobby E Edge', 'Desert East Wing W Edge'),
|
||||||
('Desert East Wing N Edge', 'Desert Arrow Pot Corner S Edge'),
|
('Desert East Wing N Edge', 'Desert Arrow Pot Corner S Edge'),
|
||||||
('Desert Arrow Pot Corner W Edge', 'Desert North Hall E Edge'),
|
('Desert Arrow Pot Corner W Edge', 'Desert North Hall E Edge'),
|
||||||
('Desert North Hall W Edge', 'Desert Sandworm Corner S Edge'),
|
('Desert West Wing N Edge', 'Desert Sandworm Corner S Edge'),
|
||||||
('Desert Sandworm Corner E Edge', 'Desert West Wing N Edge'),
|
('Desert Sandworm Corner E Edge', 'Desert North Hall W Edge'),
|
||||||
('Thieves Lobby N Edge', 'Thieves Ambush S Edge'),
|
('Thieves Lobby N Edge', 'Thieves Ambush S Edge'),
|
||||||
('Thieves Lobby NE Edge', 'Thieves Ambush SE Edge'),
|
('Thieves Lobby NE Edge', 'Thieves Ambush SE Edge'),
|
||||||
('Thieves Ambush ES Edge', 'Thieves BK Corner WS Edge'),
|
('Thieves Ambush ES Edge', 'Thieves BK Corner WS Edge'),
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Intr = DoorType.Interior
|
|||||||
|
|
||||||
|
|
||||||
def create_doors(world, player):
|
def create_doors(world, player):
|
||||||
world.doors += [
|
doors = [
|
||||||
# hyrule castle
|
# hyrule castle
|
||||||
create_door(player, 'Hyrule Castle Lobby W', Nrml).dir(We, 0x61, Mid, High).toggler().pos(0),
|
create_door(player, 'Hyrule Castle Lobby W', Nrml).dir(We, 0x61, Mid, High).toggler().pos(0),
|
||||||
create_door(player, 'Hyrule Castle Lobby E', Nrml).dir(Ea, 0x61, Mid, High).toggler().pos(2),
|
create_door(player, 'Hyrule Castle Lobby E', Nrml).dir(Ea, 0x61, Mid, High).toggler().pos(2),
|
||||||
@@ -61,6 +61,8 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Hyrule Castle Back Hall W', Nrml).dir(We, 0x01, Top, Low).pos(0),
|
create_door(player, 'Hyrule Castle Back Hall W', Nrml).dir(We, 0x01, Top, Low).pos(0),
|
||||||
create_door(player, 'Hyrule Castle Back Hall E', Nrml).dir(Ea, 0x01, Top, Low).pos(1),
|
create_door(player, 'Hyrule Castle Back Hall E', Nrml).dir(Ea, 0x01, Top, Low).pos(1),
|
||||||
create_door(player, 'Hyrule Castle Back Hall Down Stairs', Sprl).dir(Dn, 0x01, 0, HTL).ss(A, 0x2a, 0x00),
|
create_door(player, 'Hyrule Castle Back Hall Down Stairs', Sprl).dir(Dn, 0x01, 0, HTL).ss(A, 0x2a, 0x00),
|
||||||
|
create_door(player, 'Hyrule Castle Throne Room Tapestry', Lgcl),
|
||||||
|
create_door(player, 'Hyrule Castle Tapestry Backwards', Lgcl),
|
||||||
create_door(player, 'Hyrule Castle Throne Room N', Nrml).dir(No, 0x51, Mid, High).pos(1),
|
create_door(player, 'Hyrule Castle Throne Room N', Nrml).dir(No, 0x51, Mid, High).pos(1),
|
||||||
create_door(player, 'Hyrule Castle Throne Room South Stairs', StrS).dir(So, 0x51, Mid, Low),
|
create_door(player, 'Hyrule Castle Throne Room South Stairs', StrS).dir(So, 0x51, Mid, Low),
|
||||||
|
|
||||||
@@ -72,11 +74,11 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', Open).dir(So, 0x72, None, High).edge(1, Z, 0x08),
|
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', Open).dir(So, 0x72, None, High).edge(1, Z, 0x08),
|
||||||
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Dropdown', Lgcl),
|
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Dropdown', Lgcl),
|
||||||
create_door(player, 'Hyrule Dungeon South Abyss North Edge', Open).dir(No, 0x82, None, Low).edge(0, A, 0x10),
|
create_door(player, 'Hyrule Dungeon South Abyss North Edge', Open).dir(No, 0x82, None, Low).edge(0, A, 0x10),
|
||||||
create_door(player, 'Hyrule Dungeon South Abyss West Edge', Open).dir(We, 0x82, None, Low).edge(3, Z, 0x10),
|
create_door(player, 'Hyrule Dungeon South Abyss West Edge', Open).dir(We, 0x82, None, Low).edge(3, Z, 0x18),
|
||||||
create_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', Open).dir(No, 0x82, None, High).edge(1, A, 0x08),
|
create_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', Open).dir(No, 0x82, None, High).edge(1, A, 0x08),
|
||||||
create_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', Open).dir(We, 0x82, None, High).edge(4, A, 0x18),
|
create_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', Open).dir(We, 0x82, None, High).edge(4, A, 0x10),
|
||||||
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', Open).dir(Ea, 0x81, None, High).edge(3, S, 0x10),
|
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', Open).dir(Ea, 0x81, None, High).edge(3, S, 0x10),
|
||||||
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', Open).dir(Ea, 0x81, None, High).edge(4, X, 0x18),
|
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', Open).dir(Ea, 0x81, None, Low).edge(4, X, 0x18),
|
||||||
create_door(player, 'Hyrule Dungeon Guardroom N', Nrml).dir(No, 0x81, Left, Low).pos(0),
|
create_door(player, 'Hyrule Dungeon Guardroom N', Nrml).dir(No, 0x81, Left, Low).pos(0),
|
||||||
create_door(player, 'Hyrule Dungeon Armory S', Nrml).dir(So, 0x71, Left, Low).trap(0x2).pos(1),
|
create_door(player, 'Hyrule Dungeon Armory S', Nrml).dir(So, 0x71, Left, Low).trap(0x2).pos(1),
|
||||||
create_door(player, 'Hyrule Dungeon Armory ES', Intr).dir(Ea, 0x71, Left, Low).pos(2),
|
create_door(player, 'Hyrule Dungeon Armory ES', Intr).dir(Ea, 0x71, Left, Low).pos(2),
|
||||||
@@ -201,8 +203,8 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Desert Trap Room SW', Intr).dir(So, 0x75, Left, High).pos(0),
|
create_door(player, 'Desert Trap Room SW', Intr).dir(So, 0x75, Left, High).pos(0),
|
||||||
create_door(player, 'Desert North Hall SE Edge', Open).dir(So, 0x74, None, High).edge(5, X, 0x20),
|
create_door(player, 'Desert North Hall SE Edge', Open).dir(So, 0x74, None, High).edge(5, X, 0x20),
|
||||||
create_door(player, 'Desert North Hall SW Edge', Open).dir(So, 0x74, None, High).edge(3, Z, 0x20),
|
create_door(player, 'Desert North Hall SW Edge', Open).dir(So, 0x74, None, High).edge(3, Z, 0x20),
|
||||||
create_door(player, 'Desert North Hall W Edge', Open).dir(We, 0x74, None, High).edge(2, Z, 0x20),
|
create_door(player, 'Desert North Hall W Edge', Open).dir(We, 0x74, None, High).edge(1, Z, 0x20),
|
||||||
create_door(player, 'Desert North Hall E Edge', Open).dir(Ea, 0x74, None, High).edge(1, X, 0x20),
|
create_door(player, 'Desert North Hall E Edge', Open).dir(Ea, 0x74, None, High).edge(2, X, 0x20),
|
||||||
create_door(player, 'Desert North Hall NW', Intr).dir(No, 0x74, Left, High).pos(1),
|
create_door(player, 'Desert North Hall NW', Intr).dir(No, 0x74, Left, High).pos(1),
|
||||||
create_door(player, 'Desert Map SW', Intr).dir(So, 0x74, Left, High).pos(1),
|
create_door(player, 'Desert Map SW', Intr).dir(So, 0x74, Left, High).pos(1),
|
||||||
create_door(player, 'Desert North Hall NE', Intr).dir(No, 0x74, Right, High).pos(0),
|
create_door(player, 'Desert North Hall NE', Intr).dir(No, 0x74, Right, High).pos(0),
|
||||||
@@ -259,6 +261,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Hera 4F Down Stairs', Sprl).dir(Dn, 0x27, 0, HTH).ss(S, 0x62, 0xc0),
|
create_door(player, 'Hera 4F Down Stairs', Sprl).dir(Dn, 0x27, 0, HTH).ss(S, 0x62, 0xc0),
|
||||||
create_door(player, 'Hera 4F Up Stairs', Sprl).dir(Up, 0x27, 1, HTH).ss(A, 0x6b, 0x2c),
|
create_door(player, 'Hera 4F Up Stairs', Sprl).dir(Up, 0x27, 1, HTH).ss(A, 0x6b, 0x2c),
|
||||||
create_door(player, 'Hera 4F Holes', Hole),
|
create_door(player, 'Hera 4F Holes', Hole),
|
||||||
|
create_door(player, 'Hera Big Chest Hook Path', Lgcl),
|
||||||
create_door(player, 'Hera Big Chest Landing Exit', Lgcl),
|
create_door(player, 'Hera Big Chest Landing Exit', Lgcl),
|
||||||
create_door(player, 'Hera Big Chest Landing Holes', Hole),
|
create_door(player, 'Hera Big Chest Landing Holes', Hole),
|
||||||
create_door(player, 'Hera 5F Down Stairs', Sprl).dir(Dn, 0x17, 1, HTH).ss(A, 0x62, 0x40),
|
create_door(player, 'Hera 5F Down Stairs', Sprl).dir(Dn, 0x17, 1, HTH).ss(A, 0x62, 0x40),
|
||||||
@@ -333,7 +336,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'PoD Basement Ledge Drop Down', Lgcl),
|
create_door(player, 'PoD Basement Ledge Drop Down', Lgcl),
|
||||||
create_door(player, 'PoD Stalfos Basement Warp', Warp),
|
create_door(player, 'PoD Stalfos Basement Warp', Warp),
|
||||||
create_door(player, 'PoD Arena Main SW', Nrml).dir(So, 0x2a, Left, High).pos(4),
|
create_door(player, 'PoD Arena Main SW', Nrml).dir(So, 0x2a, Left, High).pos(4),
|
||||||
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5).kill(),
|
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5),
|
||||||
create_door(player, 'PoD Arena Main NW', Nrml).dir(No, 0x2a, Left, High).small_key().pos(1),
|
create_door(player, 'PoD Arena Main NW', Nrml).dir(No, 0x2a, Left, High).small_key().pos(1),
|
||||||
create_door(player, 'PoD Arena Main NE', Nrml).dir(No, 0x2a, Right, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'PoD Arena Main NE', Nrml).dir(No, 0x2a, Right, High).no_exit().trap(0x4).pos(0),
|
||||||
create_door(player, 'PoD Arena Main Crystal Path', Lgcl),
|
create_door(player, 'PoD Arena Main Crystal Path', Lgcl),
|
||||||
@@ -588,6 +591,8 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Thieves Spike Switch Up Stairs', Sprl).dir(Up, 0xab, 0, HTH).ss(Z, 0x1a, 0x6c, True, True).small_key().pos(0),
|
create_door(player, 'Thieves Spike Switch Up Stairs', Sprl).dir(Up, 0xab, 0, HTH).ss(Z, 0x1a, 0x6c, True, True).small_key().pos(0),
|
||||||
create_door(player, 'Thieves Attic Down Stairs', Sprl).dir(Dn, 0x64, 0, HTH).ss(Z, 0x11, 0x80, True, True),
|
create_door(player, 'Thieves Attic Down Stairs', Sprl).dir(Dn, 0x64, 0, HTH).ss(Z, 0x11, 0x80, True, True),
|
||||||
create_door(player, 'Thieves Attic ES', Intr).dir(Ea, 0x64, Bot, High).pos(0),
|
create_door(player, 'Thieves Attic ES', Intr).dir(Ea, 0x64, Bot, High).pos(0),
|
||||||
|
create_door(player, 'Thieves Attic Orange Barrier', Lgcl),
|
||||||
|
create_door(player, 'Thieves Attic Hint Orange Barrier', Lgcl),
|
||||||
create_door(player, 'Thieves Cricket Hall Left WS', Intr).dir(We, 0x64, Bot, High).pos(0),
|
create_door(player, 'Thieves Cricket Hall Left WS', Intr).dir(We, 0x64, Bot, High).pos(0),
|
||||||
create_door(player, 'Thieves Cricket Hall Left Edge', Open).dir(Ea, 0x64, None, High).edge(0, X, 0x30),
|
create_door(player, 'Thieves Cricket Hall Left Edge', Open).dir(Ea, 0x64, None, High).edge(0, X, 0x30),
|
||||||
create_door(player, 'Thieves Cricket Hall Right Edge', Open).dir(We, 0x65, None, High).edge(0, Z, 0x30),
|
create_door(player, 'Thieves Cricket Hall Right Edge', Open).dir(We, 0x65, None, High).edge(0, Z, 0x30),
|
||||||
@@ -900,7 +905,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'TR Crystal Maze Blue Path', Lgcl),
|
create_door(player, 'TR Crystal Maze Blue Path', Lgcl),
|
||||||
create_door(player, 'TR Crystal Maze Cane Path', Lgcl),
|
create_door(player, 'TR Crystal Maze Cane Path', Lgcl),
|
||||||
create_door(player, 'TR Crystal Maze North Stairs', StrS).dir(No, 0xc4, Mid, High),
|
create_door(player, 'TR Crystal Maze North Stairs', StrS).dir(No, 0xc4, Mid, High),
|
||||||
create_door(player, 'TR Final Abyss South Stairs', StrS).dir(No, 0xb4, Right, High),
|
create_door(player, 'TR Final Abyss South Stairs', StrS).dir(So, 0xb4, Mid, High),
|
||||||
create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0),
|
create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0),
|
||||||
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
|
|
||||||
@@ -1067,6 +1072,10 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Brightly Lit Hall NW', Nrml).dir(No, 0x1d, Left, High).big_key().pos(0),
|
create_door(player, 'GT Brightly Lit Hall NW', Nrml).dir(No, 0x1d, Left, High).big_key().pos(0),
|
||||||
create_door(player, 'GT Agahnim 2 SW', Nrml).dir(So, 0x0d, Left, High).no_exit().trap(0x4).pos(0)
|
create_door(player, 'GT Agahnim 2 SW', Nrml).dir(So, 0x0d, Left, High).no_exit().trap(0x4).pos(0)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
world.doors += doors
|
||||||
|
world.initialize_doors(doors)
|
||||||
|
|
||||||
create_paired_doors(world, player)
|
create_paired_doors(world, player)
|
||||||
|
|
||||||
# swamp events
|
# swamp events
|
||||||
@@ -1084,7 +1093,8 @@ def create_doors(world, player):
|
|||||||
world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain')
|
world.get_door('Swamp Flooded Room Ladder', player).event('Swamp Drain')
|
||||||
|
|
||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
world.get_door('Hyrule Castle Throne Room N', player).event('Zelda Pickup')
|
world.get_door('Hyrule Castle Throne Room Tapestry', player).event('Zelda Pickup')
|
||||||
|
world.get_door('Hyrule Castle Tapestry Backwards', player).event('Zelda Pickup')
|
||||||
|
|
||||||
# crystal switches and barriers
|
# crystal switches and barriers
|
||||||
world.get_door('Hera Lobby Down Stairs', player).c_switch()
|
world.get_door('Hera Lobby Down Stairs', player).c_switch()
|
||||||
@@ -1136,6 +1146,9 @@ def create_doors(world, player):
|
|||||||
world.get_door('Thieves Triple Bypass EN', player).barrier(CrystalBarrier.Blue)
|
world.get_door('Thieves Triple Bypass EN', player).barrier(CrystalBarrier.Blue)
|
||||||
world.get_door('Thieves Hellway Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
world.get_door('Thieves Hellway Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
world.get_door('Thieves Hellway Crystal Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
world.get_door('Thieves Hellway Crystal Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
|
world.get_door('Thieves Hellway Crystal Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
|
world.get_door('Thieves Attic Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
|
world.get_door('Thieves Attic Hint Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
|
|
||||||
world.get_door('Ice Bomb Drop SE', player).c_switch()
|
world.get_door('Ice Bomb Drop SE', player).c_switch()
|
||||||
world.get_door('Ice Conveyor SW', player).c_switch()
|
world.get_door('Ice Conveyor SW', player).c_switch()
|
||||||
|
|||||||
+1898
-458
File diff suppressed because it is too large
Load Diff
@@ -75,7 +75,7 @@ def start():
|
|||||||
logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err)
|
logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err)
|
||||||
seed = random.randint(0, 999999999)
|
seed = random.randint(0, 999999999)
|
||||||
for fail in failures:
|
for fail in failures:
|
||||||
logger.info('%s seed failed with: %s', fail[1], fail[0])
|
logger.info('%s\tseed failed with: %s', fail[1], fail[0])
|
||||||
fail_rate = 100 * len(failures) / args.count
|
fail_rate = 100 * len(failures) / args.count
|
||||||
success_rate = 100 * (args.count - len(failures)) / args.count
|
success_rate = 100 * (args.count - len(failures)) / args.count
|
||||||
fail_rate = str(fail_rate).split('.')
|
fail_rate = str(fail_rate).split('.')
|
||||||
|
|||||||
+12
-12
@@ -162,13 +162,13 @@ dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
|||||||
|
|
||||||
hyrule_castle_regions = [
|
hyrule_castle_regions = [
|
||||||
'Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall',
|
'Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall',
|
||||||
'Hyrule Castle West Hall', 'Hyrule Castle Back Hall', 'Hyrule Castle Throne Room', 'Hyrule Dungeon Map Room',
|
'Hyrule Castle West Hall', 'Hyrule Castle Back Hall', 'Hyrule Castle Throne Room', 'Hyrule Castle Behind Tapestry',
|
||||||
'Hyrule Dungeon North Abyss', 'Hyrule Dungeon North Abyss Catwalk', 'Hyrule Dungeon South Abyss',
|
'Hyrule Dungeon Map Room', 'Hyrule Dungeon North Abyss', 'Hyrule Dungeon North Abyss Catwalk',
|
||||||
'Hyrule Dungeon South Abyss Catwalk', 'Hyrule Dungeon Guardroom', 'Hyrule Dungeon Armory Main',
|
'Hyrule Dungeon South Abyss', 'Hyrule Dungeon South Abyss Catwalk', 'Hyrule Dungeon Guardroom',
|
||||||
'Hyrule Dungeon Armory Boomerang', 'Hyrule Dungeon Armory North Branch', 'Hyrule Dungeon Staircase',
|
'Hyrule Dungeon Armory Main', 'Hyrule Dungeon Armory Boomerang', 'Hyrule Dungeon Armory North Branch',
|
||||||
'Hyrule Dungeon Cellblock', 'Sewers Behind Tapestry', 'Sewers Rope Room', 'Sewers Dark Cross', 'Sewers Water',
|
'Hyrule Dungeon Staircase', 'Hyrule Dungeon Cellblock', 'Sewers Behind Tapestry', 'Sewers Rope Room',
|
||||||
'Sewers Key Rat', 'Sewers Rat Path', 'Sewers Secret Room Blocked Path', 'Sewers Secret Room',
|
'Sewers Dark Cross', 'Sewers Water', 'Sewers Key Rat', 'Sewers Rat Path', 'Sewers Secret Room Blocked Path',
|
||||||
'Sewers Yet More Rats', 'Sewers Pull Switch', 'Sanctuary'
|
'Sewers Secret Room', 'Sewers Yet More Rats', 'Sewers Pull Switch', 'Sanctuary'
|
||||||
]
|
]
|
||||||
|
|
||||||
eastern_regions = [
|
eastern_regions = [
|
||||||
@@ -238,7 +238,7 @@ thieves_regions = [
|
|||||||
'Thieves Big Chest Nook', 'Thieves Hallway', 'Thieves Boss', 'Thieves Pot Alcove Mid', 'Thieves Pot Alcove Bottom',
|
'Thieves Big Chest Nook', 'Thieves Hallway', 'Thieves Boss', 'Thieves Pot Alcove Mid', 'Thieves Pot Alcove Bottom',
|
||||||
'Thieves Pot Alcove Top', 'Thieves Conveyor Maze', 'Thieves Spike Track', 'Thieves Hellway',
|
'Thieves Pot Alcove Top', 'Thieves Conveyor Maze', 'Thieves Spike Track', 'Thieves Hellway',
|
||||||
'Thieves Hellway N Crystal', 'Thieves Hellway S Crystal', 'Thieves Triple Bypass', 'Thieves Spike Switch',
|
'Thieves Hellway N Crystal', 'Thieves Hellway S Crystal', 'Thieves Triple Bypass', 'Thieves Spike Switch',
|
||||||
'Thieves Attic', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', 'Thieves Attic Window',
|
'Thieves Attic', 'Thieves Attic Hint', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', 'Thieves Attic Window',
|
||||||
'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', 'Thieves Blind\'s Cell',
|
'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', 'Thieves Blind\'s Cell',
|
||||||
'Thieves Conveyor Bridge', 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap'
|
'Thieves Conveyor Bridge', 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap'
|
||||||
]
|
]
|
||||||
@@ -331,6 +331,10 @@ region_starts = {
|
|||||||
'Ganons Tower': ['GT Lobby']
|
'Ganons Tower': ['GT Lobby']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
standard_starts = {
|
||||||
|
'Hyrule Castle': ['Hyrule Castle Lobby']
|
||||||
|
}
|
||||||
|
|
||||||
split_region_starts = {
|
split_region_starts = {
|
||||||
'Desert Palace': {
|
'Desert Palace': {
|
||||||
'Back': ['Desert Back Lobby'],
|
'Back': ['Desert Back Lobby'],
|
||||||
@@ -347,10 +351,6 @@ flexible_starts = {
|
|||||||
'Skull Woods': ['Skull Left Drop', 'Skull Pinball']
|
'Skull Woods': ['Skull Left Drop', 'Skull Pinball']
|
||||||
}
|
}
|
||||||
|
|
||||||
drop_entrances = [
|
|
||||||
'Sewers Rat Path', 'Skull Pinball', 'Skull Left Drop', 'Sanctuary' # Pot circle, Back drop have unique access
|
|
||||||
]
|
|
||||||
|
|
||||||
dungeon_keys = {
|
dungeon_keys = {
|
||||||
'Hyrule Castle': 'Small Key (Escape)',
|
'Hyrule Castle': 'Small Key (Escape)',
|
||||||
'Eastern Palace': 'Small Key (Eastern Palace)',
|
'Eastern Palace': 'Small Key (Eastern Palace)',
|
||||||
|
|||||||
+50
-9
@@ -42,6 +42,8 @@ def link_entrances(world, player):
|
|||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
# must connect front of hyrule castle to do escape
|
# must connect front of hyrule castle to do escape
|
||||||
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
|
||||||
|
elif world.doorShuffle[player] != 'vanilla':
|
||||||
|
lw_entrances.append('Hyrule Castle Entrance (South)')
|
||||||
else:
|
else:
|
||||||
dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||||
lw_entrances.append('Hyrule Castle Entrance (South)')
|
lw_entrances.append('Hyrule Castle Entrance (South)')
|
||||||
@@ -55,6 +57,11 @@ def link_entrances(world, player):
|
|||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
# rest of hyrule castle must be in light world, so it has to be the one connected to east exit of desert
|
# rest of hyrule castle must be in light world, so it has to be the one connected to east exit of desert
|
||||||
connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit), player)
|
connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit), player)
|
||||||
|
elif world.doorShuffle[player] != 'vanilla':
|
||||||
|
# sanc is in light world, so must all of HC if door shuffle is on
|
||||||
|
connect_mandatory_exits(world, lw_entrances,
|
||||||
|
[('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')],
|
||||||
|
list(LW_Dungeon_Entrances_Must_Exit), player)
|
||||||
else:
|
else:
|
||||||
connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player)
|
connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player)
|
||||||
connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player)
|
connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player)
|
||||||
@@ -1204,7 +1211,9 @@ def link_inverted_entrances(world, player):
|
|||||||
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in bomb_shop_doors]
|
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in bomb_shop_doors]
|
||||||
sanc_door = random.choice(sanc_doors)
|
sanc_door = random.choice(sanc_doors)
|
||||||
bomb_shop_doors.remove(sanc_door)
|
bomb_shop_doors.remove(sanc_door)
|
||||||
connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player)
|
|
||||||
|
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
|
||||||
|
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||||
|
|
||||||
lw_dm_entrances = ['Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Old Man House (Bottom)',
|
lw_dm_entrances = ['Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Old Man House (Bottom)',
|
||||||
'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Spiral Cave (Bottom)', 'Old Man Cave (East)',
|
'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Spiral Cave (Bottom)', 'Old Man Cave (East)',
|
||||||
@@ -1279,7 +1288,8 @@ def link_inverted_entrances(world, player):
|
|||||||
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
|
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
|
||||||
sanc_door = random.choice(sanc_doors)
|
sanc_door = random.choice(sanc_doors)
|
||||||
dw_entrances.remove(sanc_door)
|
dw_entrances.remove(sanc_door)
|
||||||
connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player)
|
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
|
||||||
|
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||||
|
|
||||||
# tavern back door cannot be shuffled yet
|
# tavern back door cannot be shuffled yet
|
||||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||||
@@ -1410,7 +1420,8 @@ def link_inverted_entrances(world, player):
|
|||||||
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
|
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
|
||||||
sanc_door = random.choice(sanc_doors)
|
sanc_door = random.choice(sanc_doors)
|
||||||
dw_entrances.remove(sanc_door)
|
dw_entrances.remove(sanc_door)
|
||||||
connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player)
|
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
|
||||||
|
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||||
|
|
||||||
# place old man house
|
# place old man house
|
||||||
# no dw must exits in inverted, but we randomize whether cave is in light or dark world
|
# no dw must exits in inverted, but we randomize whether cave is in light or dark world
|
||||||
@@ -1547,7 +1558,8 @@ def link_inverted_entrances(world, player):
|
|||||||
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances]
|
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances]
|
||||||
sanc_door = random.choice(sanc_doors)
|
sanc_door = random.choice(sanc_doors)
|
||||||
entrances.remove(sanc_door)
|
entrances.remove(sanc_door)
|
||||||
connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player)
|
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
|
||||||
|
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||||
|
|
||||||
# tavern back door cannot be shuffled yet
|
# tavern back door cannot be shuffled yet
|
||||||
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
connect_doors(world, ['Tavern North'], ['Tavern'], player)
|
||||||
@@ -1680,7 +1692,8 @@ def link_inverted_entrances(world, player):
|
|||||||
sanc_door = random.choice(sanc_doors)
|
sanc_door = random.choice(sanc_doors)
|
||||||
entrances.remove(sanc_door)
|
entrances.remove(sanc_door)
|
||||||
doors.remove(sanc_door)
|
doors.remove(sanc_door)
|
||||||
connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player)
|
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
|
||||||
|
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
|
||||||
|
|
||||||
# now let's deal with mandatory reachable stuff
|
# now let's deal with mandatory reachable stuff
|
||||||
def extract_reachable_exit(cavelist):
|
def extract_reachable_exit(cavelist):
|
||||||
@@ -2041,6 +2054,14 @@ def simple_shuffle_dungeons(world, player):
|
|||||||
else:
|
else:
|
||||||
hc_target = multi_dungeons[2]
|
hc_target = multi_dungeons[2]
|
||||||
|
|
||||||
|
# door shuffle should restrict hyrule castle to the light world due to sanc being limited to the LW
|
||||||
|
if world.doorShuffle[player] != 'vanilla' and hc_target == 'Turtle Rock':
|
||||||
|
swap_w_dp = random.choice([True, False])
|
||||||
|
if swap_w_dp:
|
||||||
|
hc_target, dp_target = dp_target, hc_target
|
||||||
|
else:
|
||||||
|
hc_target, tr_target = tr_target, hc_target
|
||||||
|
|
||||||
# ToDo improve this?
|
# ToDo improve this?
|
||||||
|
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
@@ -2298,6 +2319,8 @@ Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)',
|
|||||||
'Death Mountain Return Cave (East)',
|
'Death Mountain Return Cave (East)',
|
||||||
'Death Mountain Return Cave (West)',
|
'Death Mountain Return Cave (West)',
|
||||||
'Spectacle Rock Cave Peak',
|
'Spectacle Rock Cave Peak',
|
||||||
|
'Spectacle Rock Cave',
|
||||||
|
'Spectacle Rock Cave (Bottom)',
|
||||||
'Paradox Cave (Bottom)',
|
'Paradox Cave (Bottom)',
|
||||||
'Paradox Cave (Middle)',
|
'Paradox Cave (Middle)',
|
||||||
'Paradox Cave (Top)',
|
'Paradox Cave (Top)',
|
||||||
@@ -2638,8 +2661,6 @@ Inverted_Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)',
|
|||||||
'Death Mountain Return Cave (East)',
|
'Death Mountain Return Cave (East)',
|
||||||
'Death Mountain Return Cave (West)',
|
'Death Mountain Return Cave (West)',
|
||||||
'Spectacle Rock Cave Peak',
|
'Spectacle Rock Cave Peak',
|
||||||
'Spectacle Rock Cave',
|
|
||||||
'Spectacle Rock Cave (Bottom)',
|
|
||||||
'Paradox Cave (Bottom)',
|
'Paradox Cave (Bottom)',
|
||||||
'Paradox Cave (Middle)',
|
'Paradox Cave (Middle)',
|
||||||
'Paradox Cave (Top)',
|
'Paradox Cave (Top)',
|
||||||
@@ -2814,7 +2835,10 @@ Isolated_LH_Doors = ['Kings Grave',
|
|||||||
'Turtle Rock Isolated Ledge Entrance']
|
'Turtle Rock Isolated Ledge Entrance']
|
||||||
|
|
||||||
# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions
|
# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions
|
||||||
mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
mandatory_connections = [('Links House S&Q', 'Links House'),
|
||||||
|
('Sanctuary S&Q', 'Sanctuary'),
|
||||||
|
('Old Man S&Q', 'Old Man House'),
|
||||||
|
('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
||||||
('Lake Hylia Central Island Teleporter', 'Dark Lake Hylia Central Island'),
|
('Lake Hylia Central Island Teleporter', 'Dark Lake Hylia Central Island'),
|
||||||
('Zoras River', 'Zoras River'),
|
('Zoras River', 'Zoras River'),
|
||||||
('Kings Grave Outer Rocks', 'Kings Grave Area'),
|
('Kings Grave Outer Rocks', 'Kings Grave Area'),
|
||||||
@@ -2923,7 +2947,11 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
|
|||||||
('Pyramid Drop', 'East Dark World')
|
('Pyramid Drop', 'East Dark World')
|
||||||
]
|
]
|
||||||
|
|
||||||
inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
inverted_mandatory_connections = [('Links House S&Q', 'Inverted Links House'),
|
||||||
|
('Dark Sanctuary S&Q', 'Inverted Dark Sanctuary'),
|
||||||
|
('Old Man S&Q', 'Old Man House'),
|
||||||
|
('Castle Ledge S&Q', 'Hyrule Castle Ledge'),
|
||||||
|
('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
||||||
('Lake Hylia Island', 'Lake Hylia Island'),
|
('Lake Hylia Island', 'Lake Hylia Island'),
|
||||||
('Zoras River', 'Zoras River'),
|
('Zoras River', 'Zoras River'),
|
||||||
('Kings Grave Outer Rocks', 'Kings Grave Area'),
|
('Kings Grave Outer Rocks', 'Kings Grave Area'),
|
||||||
@@ -3352,6 +3380,7 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'
|
|||||||
('Inverted Links House Exit', 'South Dark World'),
|
('Inverted Links House Exit', 'South Dark World'),
|
||||||
('Inverted Big Bomb Shop', 'Inverted Big Bomb Shop'),
|
('Inverted Big Bomb Shop', 'Inverted Big Bomb Shop'),
|
||||||
('Inverted Dark Sanctuary', 'Inverted Dark Sanctuary'),
|
('Inverted Dark Sanctuary', 'Inverted Dark Sanctuary'),
|
||||||
|
('Inverted Dark Sanctuary Exit', 'West Dark World'),
|
||||||
('Old Man Cave (West)', 'Bumper Cave'),
|
('Old Man Cave (West)', 'Bumper Cave'),
|
||||||
('Old Man Cave (East)', 'Death Mountain Return Cave'),
|
('Old Man Cave (East)', 'Death Mountain Return Cave'),
|
||||||
('Old Man Cave Exit (West)', 'West Dark World'),
|
('Old Man Cave Exit (West)', 'West Dark World'),
|
||||||
@@ -3484,12 +3513,24 @@ inverted_default_dungeon_connections = [('Desert Palace Entrance (South)', 'Dese
|
|||||||
('Ice Palace Exit', 'Dark Lake Hylia')
|
('Ice Palace Exit', 'Dark Lake Hylia')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
indirect_connections = {
|
||||||
|
'Turtle Rock (Top)': 'Turtle Rock',
|
||||||
|
'East Dark World': 'Pyramid Fairy',
|
||||||
|
'Big Bomb Shop': 'Pyramid Fairy',
|
||||||
|
'Dark Desert': 'Pyramid Fairy',
|
||||||
|
'West Dark World': 'Pyramid Fairy',
|
||||||
|
'South Dark World': 'Pyramid Fairy',
|
||||||
|
'Light World': 'Pyramid Fairy',
|
||||||
|
'Old Man Cave': 'Old Man S&Q'
|
||||||
|
}
|
||||||
# format:
|
# format:
|
||||||
# Key=Name
|
# Key=Name
|
||||||
# addr = (door_index, exitdata) # multiexit
|
# addr = (door_index, exitdata) # multiexit
|
||||||
# | ([addr], None) # holes
|
# | ([addr], None) # holes
|
||||||
# exitdata = (room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2)
|
# exitdata = (room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2)
|
||||||
|
|
||||||
|
# ToDo somehow merge this with creation of the locations
|
||||||
|
|
||||||
# ToDo somehow merge this with creation of the locations
|
# ToDo somehow merge this with creation of the locations
|
||||||
door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)),
|
door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)),
|
||||||
'Inverted Big Bomb Shop': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)),
|
'Inverted Big Bomb Shop': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)),
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
|
|||||||
world.push_item(spot_to_fill, item_to_place, True)
|
world.push_item(spot_to_fill, item_to_place, True)
|
||||||
itempool.remove(item_to_place)
|
itempool.remove(item_to_place)
|
||||||
fill_locations.remove(spot_to_fill)
|
fill_locations.remove(spot_to_fill)
|
||||||
|
unplaced = [item.name for item in itempool]
|
||||||
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s', [item.name for item in itempool], [location.name for location in fill_locations])
|
unfilled = [location.name for location in fill_locations]
|
||||||
|
if unplaced or unfilled:
|
||||||
|
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||||
|
|
||||||
|
|
||||||
def distribute_items_staleness(world):
|
def distribute_items_staleness(world):
|
||||||
@@ -158,8 +160,10 @@ def distribute_items_staleness(world):
|
|||||||
itempool.remove(item_to_place)
|
itempool.remove(item_to_place)
|
||||||
fill_locations.remove(spot_to_fill)
|
fill_locations.remove(spot_to_fill)
|
||||||
|
|
||||||
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s', [item.name for item in itempool], [location.name for location in fill_locations])
|
unplaced = [item.name for item in itempool]
|
||||||
|
unfilled = [location.name for location in fill_locations]
|
||||||
|
if unplaced or unfilled:
|
||||||
|
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||||
|
|
||||||
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool = None, single_player_placement = False):
|
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool = None, single_player_placement = False):
|
||||||
def sweep_from_pool():
|
def sweep_from_pool():
|
||||||
@@ -226,7 +230,7 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool =
|
|||||||
|
|
||||||
|
|
||||||
def valid_key_placement(item, location, itempool, world):
|
def valid_key_placement(item, location, itempool, world):
|
||||||
if (not item.smallkey and not item.bigkey) or item.player != location.player or world.retro[item.player]:
|
if (not item.smallkey and not item.bigkey) or item.player != location.player or world.retro[item.player] or world.logic[item.player] == 'nologic':
|
||||||
return True
|
return True
|
||||||
dungeon = location.parent_region.dungeon
|
dungeon = location.parent_region.dungeon
|
||||||
if dungeon:
|
if dungeon:
|
||||||
@@ -291,7 +295,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
|||||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
|
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
|
||||||
|
|
||||||
fill_restrictive(world, world.state, fill_locations, progitempool,
|
fill_restrictive(world, world.state, fill_locations, progitempool,
|
||||||
keys_in_itempool={player: world.keyshuffle[player] for player in range(1, world.players+1)})
|
keys_in_itempool={player: world.keyshuffle[player] for player in range(1, world.players + 1)})
|
||||||
|
|
||||||
random.shuffle(fill_locations)
|
random.shuffle(fill_locations)
|
||||||
|
|
||||||
@@ -299,8 +303,10 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
|||||||
|
|
||||||
fast_fill(world, restitempool, fill_locations)
|
fast_fill(world, restitempool, fill_locations)
|
||||||
|
|
||||||
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s', [item.name for item in progitempool + prioitempool + restitempool], [location.name for location in fill_locations])
|
unplaced = [item.name for item in prioitempool + restitempool]
|
||||||
|
unfilled = [location.name for location in fill_locations]
|
||||||
|
if unplaced or unfilled:
|
||||||
|
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||||
|
|
||||||
def fast_fill(world, item_pool, fill_locations):
|
def fast_fill(world, item_pool, fill_locations):
|
||||||
while item_pool and fill_locations:
|
while item_pool and fill_locations:
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@ from Regions import create_lw_region, create_dw_region, create_cave_region, crea
|
|||||||
def create_inverted_regions(world, player):
|
def create_inverted_regions(world, player):
|
||||||
|
|
||||||
world.regions += [
|
world.regions += [
|
||||||
|
create_dw_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
|
||||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'],
|
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'],
|
||||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
|
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
|
||||||
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||||
@@ -176,7 +177,7 @@ def create_inverted_regions(world, player):
|
|||||||
create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
||||||
create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
||||||
create_cave_region(player, 'Red Shield Shop', 'the rare shop'),
|
create_cave_region(player, 'Red Shield Shop', 'the rare shop'),
|
||||||
create_cave_region(player, 'Inverted Dark Sanctuary', 'a storyteller'),
|
create_cave_region(player, 'Inverted Dark Sanctuary', 'a storyteller', None, ['Inverted Dark Sanctuary Exit']),
|
||||||
create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
||||||
create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)',
|
create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)',
|
||||||
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
||||||
|
|||||||
+5
-2
@@ -265,7 +265,7 @@ def generate_itempool(world, player):
|
|||||||
if player in world.pool_adjustment.keys():
|
if player in world.pool_adjustment.keys():
|
||||||
amt = world.pool_adjustment[player]
|
amt = world.pool_adjustment[player]
|
||||||
if amt < 0:
|
if amt < 0:
|
||||||
for _ in range(0, amt):
|
for _ in range(amt, 0):
|
||||||
pool.remove('Rupees (20)')
|
pool.remove('Rupees (20)')
|
||||||
elif amt > 0:
|
elif amt > 0:
|
||||||
for _ in range(0, amt):
|
for _ in range(0, amt):
|
||||||
@@ -287,9 +287,12 @@ def generate_itempool(world, player):
|
|||||||
if item in ['Progressive Bow', 'Bow'] and not found_bow:
|
if item in ['Progressive Bow', 'Bow'] and not found_bow:
|
||||||
found_bow = True
|
found_bow = True
|
||||||
possible_weapons.append(item)
|
possible_weapons.append(item)
|
||||||
if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
|
if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
|
||||||
if item not in possible_weapons:
|
if item not in possible_weapons:
|
||||||
possible_weapons.append(item)
|
possible_weapons.append(item)
|
||||||
|
if item in ['Bombs (10)']:
|
||||||
|
if item not in possible_weapons and world.doorShuffle[player] != 'crossed':
|
||||||
|
possible_weapons.append(item)
|
||||||
starting_weapon = random.choice(possible_weapons)
|
starting_weapon = random.choice(possible_weapons)
|
||||||
placed_items["Link's Uncle"] = starting_weapon
|
placed_items["Link's Uncle"] = starting_weapon
|
||||||
pool.remove(starting_weapon)
|
pool.remove(starting_weapon)
|
||||||
|
|||||||
+117
-28
@@ -41,7 +41,8 @@ class KeyLogic(object):
|
|||||||
|
|
||||||
def __init__(self, dungeon_name):
|
def __init__(self, dungeon_name):
|
||||||
self.door_rules = {}
|
self.door_rules = {}
|
||||||
self.bk_restricted = set()
|
self.bk_restricted = set() # subset of free locations
|
||||||
|
self.bk_locked = set() # includes potentially other locations and key only locations
|
||||||
self.sm_restricted = set()
|
self.sm_restricted = set()
|
||||||
self.small_key_name = dungeon_keys[dungeon_name]
|
self.small_key_name = dungeon_keys[dungeon_name]
|
||||||
self.bk_name = dungeon_bigs[dungeon_name]
|
self.bk_name = dungeon_bigs[dungeon_name]
|
||||||
@@ -50,7 +51,9 @@ class KeyLogic(object):
|
|||||||
self.logic_min = {}
|
self.logic_min = {}
|
||||||
self.logic_max = {}
|
self.logic_max = {}
|
||||||
self.placement_rules = []
|
self.placement_rules = []
|
||||||
|
self.location_rules = {}
|
||||||
self.outside_keys = 0
|
self.outside_keys = 0
|
||||||
|
self.dungeon = dungeon_name
|
||||||
|
|
||||||
def check_placement(self, unplaced_keys, big_key_loc=None):
|
def check_placement(self, unplaced_keys, big_key_loc=None):
|
||||||
for rule in self.placement_rules:
|
for rule in self.placement_rules:
|
||||||
@@ -77,6 +80,18 @@ class DoorRules(object):
|
|||||||
self.opposite = None
|
self.opposite = None
|
||||||
|
|
||||||
|
|
||||||
|
class LocationRule(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.small_key_num = 0
|
||||||
|
self.conditional_sets = []
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionalLocationRule(object):
|
||||||
|
def __init__(self, conditional_set):
|
||||||
|
self.conditional_set = conditional_set
|
||||||
|
self.small_key_num = 0
|
||||||
|
|
||||||
|
|
||||||
class PlacementRule(object):
|
class PlacementRule(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -88,6 +103,7 @@ class PlacementRule(object):
|
|||||||
self.check_locations_w_bk = None
|
self.check_locations_w_bk = None
|
||||||
self.check_locations_wo_bk = None
|
self.check_locations_wo_bk = None
|
||||||
self.bk_relevant = True
|
self.bk_relevant = True
|
||||||
|
self.key_reduced = False
|
||||||
|
|
||||||
def contradicts(self, rule, unplaced_keys, big_key_loc):
|
def contradicts(self, rule, unplaced_keys, big_key_loc):
|
||||||
bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False
|
bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False
|
||||||
@@ -208,6 +224,7 @@ def analyze_dungeon(key_layout, world, player):
|
|||||||
|
|
||||||
find_bk_locked_sections(key_layout, world, player)
|
find_bk_locked_sections(key_layout, world, player)
|
||||||
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
|
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
|
||||||
|
key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations))
|
||||||
if world.retro[player] and world.mode[player] != 'standard':
|
if world.retro[player] and world.mode[player] != 'standard':
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -297,7 +314,9 @@ def create_exhaustive_placement_rules(key_layout, world, player):
|
|||||||
rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc))
|
rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc))
|
||||||
if valid_rule:
|
if valid_rule:
|
||||||
key_logic.placement_rules.append(rule)
|
key_logic.placement_rules.append(rule)
|
||||||
|
adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr)
|
||||||
refine_placement_rules(key_layout, max_ctr)
|
refine_placement_rules(key_layout, max_ctr)
|
||||||
|
refine_location_rules(key_layout)
|
||||||
|
|
||||||
|
|
||||||
def placement_self_lock_adjustment(rule, max_ctr, blocked_loc, ctr, world, player):
|
def placement_self_lock_adjustment(rule, max_ctr, blocked_loc, ctr, world, player):
|
||||||
@@ -319,6 +338,37 @@ def check_sm_restriction_needed(key_layout, max_ctr, rule, blocked):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr):
|
||||||
|
if rule.bk_conditional_set:
|
||||||
|
test_set = (rule.bk_conditional_set - key_logic.bk_locked) - set(max_ctr.key_only_locations.keys())
|
||||||
|
needed = rule.needed_keys_wo_bk if test_set else 0
|
||||||
|
else:
|
||||||
|
test_set = None
|
||||||
|
needed = rule.needed_keys_w_bk
|
||||||
|
if needed > 0:
|
||||||
|
accessible_loc.update(key_counter.other_locations)
|
||||||
|
blocked_loc = key_layout.all_locations-accessible_loc
|
||||||
|
for location in blocked_loc:
|
||||||
|
if location not in key_logic.location_rules.keys():
|
||||||
|
loc_rule = LocationRule()
|
||||||
|
key_logic.location_rules[location] = loc_rule
|
||||||
|
else:
|
||||||
|
loc_rule = key_logic.location_rules[location]
|
||||||
|
if test_set:
|
||||||
|
if location not in key_logic.bk_locked:
|
||||||
|
cond_rule = None
|
||||||
|
for other in loc_rule.conditional_sets:
|
||||||
|
if other.conditional_set == test_set:
|
||||||
|
cond_rule = other
|
||||||
|
break
|
||||||
|
if not cond_rule:
|
||||||
|
cond_rule = ConditionalLocationRule(test_set)
|
||||||
|
loc_rule.conditional_sets.append(cond_rule)
|
||||||
|
cond_rule.small_key_num = max(needed, cond_rule.small_key_num)
|
||||||
|
else:
|
||||||
|
loc_rule.small_key_num = max(needed, loc_rule.small_key_num)
|
||||||
|
|
||||||
|
|
||||||
def refine_placement_rules(key_layout, max_ctr):
|
def refine_placement_rules(key_layout, max_ctr):
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
changed = True
|
changed = True
|
||||||
@@ -407,6 +457,20 @@ def refine_placement_rules(key_layout, max_ctr):
|
|||||||
removed_rules[r2] = r1
|
removed_rules[r2] = r1
|
||||||
|
|
||||||
|
|
||||||
|
def refine_location_rules(key_layout):
|
||||||
|
locs_to_remove = []
|
||||||
|
for loc, rule in key_layout.key_logic.location_rules.items():
|
||||||
|
conditions_to_remove = []
|
||||||
|
for cond_rule in rule.conditional_sets:
|
||||||
|
if cond_rule.small_key_num <= rule.small_key_num:
|
||||||
|
conditions_to_remove.append(cond_rule)
|
||||||
|
rule.conditional_sets = [x for x in rule.conditional_sets if x not in conditions_to_remove]
|
||||||
|
if rule.small_key_num == 0 and len(rule.conditional_sets) == 0:
|
||||||
|
locs_to_remove.append(loc)
|
||||||
|
for loc in locs_to_remove:
|
||||||
|
del key_layout.key_logic.location_rules[loc]
|
||||||
|
|
||||||
|
|
||||||
def create_inclusive_rule(key_layout, max_ctr, code, key_counter, blocked_loc, accessible_loc, min_keys, world, player):
|
def create_inclusive_rule(key_layout, max_ctr, code, key_counter, blocked_loc, accessible_loc, min_keys, world, player):
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
rule = PlacementRule()
|
rule = PlacementRule()
|
||||||
@@ -421,6 +485,7 @@ def create_inclusive_rule(key_layout, max_ctr, code, key_counter, blocked_loc, a
|
|||||||
rule.check_locations_w_bk = accessible_loc
|
rule.check_locations_w_bk = accessible_loc
|
||||||
check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
||||||
key_logic.placement_rules.append(rule)
|
key_logic.placement_rules.append(rule)
|
||||||
|
adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr)
|
||||||
|
|
||||||
|
|
||||||
def queue_sorter(queue_item):
|
def queue_sorter(queue_item):
|
||||||
@@ -438,12 +503,10 @@ def queue_sorter_2(queue_item):
|
|||||||
|
|
||||||
|
|
||||||
def find_bk_locked_sections(key_layout, world, player):
|
def find_bk_locked_sections(key_layout, world, player):
|
||||||
if key_layout.big_key_special:
|
|
||||||
return
|
|
||||||
key_counters = key_layout.key_counters
|
key_counters = key_layout.key_counters
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
|
|
||||||
bk_key_not_required = set()
|
bk_not_required = set()
|
||||||
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
||||||
for counter in key_counters.values():
|
for counter in key_counters.values():
|
||||||
key_layout.all_chest_locations.update(counter.free_locations)
|
key_layout.all_chest_locations.update(counter.free_locations)
|
||||||
@@ -452,10 +515,19 @@ def find_bk_locked_sections(key_layout, world, player):
|
|||||||
if counter.big_key_opened and counter.important_location:
|
if counter.big_key_opened and counter.important_location:
|
||||||
big_chest_allowed_big_key = False
|
big_chest_allowed_big_key = False
|
||||||
if not counter.big_key_opened:
|
if not counter.big_key_opened:
|
||||||
bk_key_not_required.update(counter.free_locations)
|
bk_not_required.update(counter.free_locations)
|
||||||
key_logic.bk_restricted.update(dict.fromkeys(set(key_layout.all_chest_locations).difference(bk_key_not_required)))
|
bk_not_required.update(counter.key_only_locations)
|
||||||
|
bk_not_required.update(counter.other_locations)
|
||||||
|
# todo?: handle bk special differently in cross dungeon
|
||||||
|
# notably: things behind bk doors - relying on the bk door logic atm
|
||||||
|
if not key_layout.big_key_special:
|
||||||
|
key_logic.bk_restricted.update(dict.fromkeys(set(key_layout.all_chest_locations).difference(bk_not_required)))
|
||||||
|
key_logic.bk_locked.update(dict.fromkeys(set(key_layout.all_locations) - bk_not_required))
|
||||||
if not big_chest_allowed_big_key:
|
if not big_chest_allowed_big_key:
|
||||||
key_logic.bk_restricted.update(find_big_chest_locations(key_layout.all_chest_locations))
|
bk_required_locations = find_big_chest_locations(key_layout.all_chest_locations)
|
||||||
|
bk_required_locations += find_big_key_locked_locations(key_layout.all_chest_locations)
|
||||||
|
key_logic.bk_restricted.update(bk_required_locations)
|
||||||
|
key_logic.bk_locked.update(bk_required_locations)
|
||||||
|
|
||||||
|
|
||||||
def empty_counter(counter):
|
def empty_counter(counter):
|
||||||
@@ -903,23 +975,26 @@ def filter_big_chest(locations):
|
|||||||
def count_free_locations(state):
|
def count_free_locations(state):
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for loc in state.found_locations:
|
for loc in state.found_locations:
|
||||||
if '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations and loc.name not in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
if '- Prize' not in loc.name and loc.name not in dungeon_events and not loc.forced_item:
|
||||||
cnt += 1
|
if loc.name not in ['Agahnim 1', 'Agahnim 2']:
|
||||||
|
cnt += 1
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
|
|
||||||
def count_locations_exclude_big_chest(state):
|
def count_locations_exclude_big_chest(state):
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for loc in state.found_locations:
|
for loc in state.found_locations:
|
||||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations and loc.name not in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events:
|
||||||
cnt += 1
|
if not loc.forced_item and loc.name not in ['Agahnim 1', 'Agahnim 2', "Hyrule Castle - Zelda's Chest",
|
||||||
|
"Thieves' Town - Blind's Cell"]:
|
||||||
|
cnt += 1
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
|
|
||||||
def count_key_only_locations(state):
|
def count_small_key_only_locations(state):
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for loc in state.found_locations:
|
for loc in state.found_locations:
|
||||||
if loc.name in key_only_locations:
|
if loc.forced_item and loc.item.smallkey:
|
||||||
cnt += 1
|
cnt += 1
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
@@ -936,6 +1011,14 @@ def find_big_chest_locations(locations):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def find_big_key_locked_locations(locations):
|
||||||
|
ret = []
|
||||||
|
for loc in locations:
|
||||||
|
if loc.name in ["Thieves' Town - Blind's Cell", "Hyrule Castle - Zelda's Chest"]:
|
||||||
|
ret.append(loc)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def expand_key_state(state, flat_proposal, world, player):
|
def expand_key_state(state, flat_proposal, world, player):
|
||||||
while len(state.avail_doors) > 0:
|
while len(state.avail_doors) > 0:
|
||||||
exp_door = state.next_avail_door()
|
exp_door = state.next_avail_door()
|
||||||
@@ -1149,7 +1232,7 @@ def set_paired_rules(key_logic, world, player):
|
|||||||
# Soft lock stuff
|
# Soft lock stuff
|
||||||
def validate_key_layout(key_layout, world, player):
|
def validate_key_layout(key_layout, world, player):
|
||||||
# retro is all good - except for hyrule castle in standard mode
|
# 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'):
|
if (world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) or world.logic[player] == 'nologic':
|
||||||
return True
|
return True
|
||||||
flat_proposal = key_layout.flat_prop
|
flat_proposal = key_layout.flat_prop
|
||||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||||
@@ -1169,8 +1252,8 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
|||||||
return True # I think that's the end
|
return True # I think that's the end
|
||||||
# todo: fix state to separate out these types
|
# todo: fix state to separate out these types
|
||||||
ttl_locations = count_free_locations(state) if state.big_key_opened else count_locations_exclude_big_chest(state)
|
ttl_locations = count_free_locations(state) if state.big_key_opened else count_locations_exclude_big_chest(state)
|
||||||
ttl_key_only = count_key_only_locations(state)
|
ttl_small_key_only = count_small_key_only_locations(state)
|
||||||
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_key_only, state, world, player)
|
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player)
|
||||||
available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player)
|
available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player)
|
||||||
if invalid_self_locking_key(key_layout, state, prev_state, prev_avail, world, player):
|
if invalid_self_locking_key(key_layout, state, prev_state, prev_avail, world, player):
|
||||||
return False
|
return False
|
||||||
@@ -1184,7 +1267,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
|||||||
state_copy = state.copy()
|
state_copy = state.copy()
|
||||||
open_a_door(exp_door.door, state_copy, flat_proposal)
|
open_a_door(exp_door.door, state_copy, flat_proposal)
|
||||||
state_copy.used_smalls += 1
|
state_copy.used_smalls += 1
|
||||||
if state_copy.used_smalls > ttl_key_only:
|
if state_copy.used_smalls > ttl_small_key_only:
|
||||||
state_copy.used_locations += 1
|
state_copy.used_locations += 1
|
||||||
code = state_id(state_copy, flat_proposal)
|
code = state_id(state_copy, flat_proposal)
|
||||||
if code not in checked_states.keys():
|
if code not in checked_states.keys():
|
||||||
@@ -1260,7 +1343,10 @@ def create_key_counters(key_layout, world, player):
|
|||||||
key_counters = {}
|
key_counters = {}
|
||||||
flat_proposal = key_layout.flat_prop
|
flat_proposal = key_layout.flat_prop
|
||||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||||
state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
if world.doorShuffle[player] == 'vanilla':
|
||||||
|
state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||||
|
else:
|
||||||
|
state.key_locations = world.dungeon_layouts[player][key_layout.sector.name].key_doors_num
|
||||||
state.big_key_special = world.get_region('Hyrule Dungeon Cellblock', player) in key_layout.sector.regions
|
state.big_key_special = world.get_region('Hyrule Dungeon Cellblock', player) in key_layout.sector.regions
|
||||||
for region in key_layout.start_regions:
|
for region in key_layout.start_regions:
|
||||||
state.visit_region(region, key_checks=True)
|
state.visit_region(region, key_checks=True)
|
||||||
@@ -1294,8 +1380,10 @@ def create_key_counter(state, key_layout, world, player):
|
|||||||
if important_location(loc, world, player):
|
if important_location(loc, world, player):
|
||||||
key_counter.important_location = True
|
key_counter.important_location = True
|
||||||
key_counter.other_locations[loc] = None
|
key_counter.other_locations[loc] = None
|
||||||
elif loc.event and 'Small Key' in loc.item.name:
|
elif loc.forced_item and loc.item.name == key_layout.key_logic.small_key_name:
|
||||||
key_counter.key_only_locations[loc] = None
|
key_counter.key_only_locations[loc] = None
|
||||||
|
elif loc.forced_item and loc.item.name == key_layout.key_logic.bk_name:
|
||||||
|
key_counter.other_locations[loc] = None
|
||||||
elif loc.name not in dungeon_events:
|
elif loc.name not in dungeon_events:
|
||||||
key_counter.free_locations[loc] = None
|
key_counter.free_locations[loc] = None
|
||||||
else:
|
else:
|
||||||
@@ -1306,11 +1394,6 @@ def create_key_counter(state, key_layout, world, player):
|
|||||||
key_counter.big_key_opened = state.visited(world.get_region('Hyrule Dungeon Cellblock', player))
|
key_counter.big_key_opened = state.visited(world.get_region('Hyrule Dungeon Cellblock', player))
|
||||||
else:
|
else:
|
||||||
key_counter.big_key_opened = state.big_key_opened
|
key_counter.big_key_opened = state.big_key_opened
|
||||||
# if soft_lock_check:
|
|
||||||
# avail_chests = available_chest_small_keys(key_counter, key_counter.big_key_opened, world)
|
|
||||||
# avail_keys = avail_chests + len(key_counter.key_only_locations)
|
|
||||||
# if avail_keys <= key_counter.used_keys and avail_keys < key_layout.max_chests + key_layout.max_drops:
|
|
||||||
# raise SoftLockException()
|
|
||||||
return key_counter
|
return key_counter
|
||||||
|
|
||||||
|
|
||||||
@@ -1322,13 +1405,14 @@ def imp_locations_factory(world, player):
|
|||||||
if imp_locations:
|
if imp_locations:
|
||||||
return imp_locations
|
return imp_locations
|
||||||
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
||||||
if world.mode[player] == 'standard' or world.doorShuffle[player] == 'crossed':
|
if world.mode[player] == 'standard':
|
||||||
imp_locations.append('Hyrule Dungeon Cellblock')
|
imp_locations.append('Zelda Pickup')
|
||||||
|
imp_locations.append('Zelda Dropoff')
|
||||||
return imp_locations
|
return imp_locations
|
||||||
|
|
||||||
|
|
||||||
def important_location(loc, world, player):
|
def important_location(loc, world, player):
|
||||||
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player)
|
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player) or (loc.forced_item is not None and loc.item.bigkey)
|
||||||
|
|
||||||
|
|
||||||
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||||
@@ -1390,6 +1474,8 @@ def find_counter_hint(opened_doors, bk_hint, key_layout):
|
|||||||
|
|
||||||
def find_max_counter(key_layout):
|
def find_max_counter(key_layout):
|
||||||
max_counter = find_counter_hint(dict.fromkeys(key_layout.flat_prop), False, key_layout)
|
max_counter = find_counter_hint(dict.fromkeys(key_layout.flat_prop), False, key_layout)
|
||||||
|
if max_counter is None:
|
||||||
|
raise Exception("Max Counter is none - something is amiss")
|
||||||
if len(max_counter.child_doors) > 0:
|
if len(max_counter.child_doors) > 0:
|
||||||
max_counter = find_counter_hint(dict.fromkeys(key_layout.flat_prop), True, key_layout)
|
max_counter = find_counter_hint(dict.fromkeys(key_layout.flat_prop), True, key_layout)
|
||||||
return max_counter
|
return max_counter
|
||||||
@@ -1604,7 +1690,10 @@ def validate_key_placement(key_layout, world, player):
|
|||||||
for code, counter in key_layout.key_counters.items():
|
for code, counter in key_layout.key_counters.items():
|
||||||
if len(counter.child_doors) == 0:
|
if len(counter.child_doors) == 0:
|
||||||
continue
|
continue
|
||||||
big_found = any(i.item == dungeon.big_key for i in counter.free_locations if "- Big Chest" not in i.name) or big_key_outside
|
if key_layout.big_key_special:
|
||||||
|
big_found = any(i.forced_item is not None and i.item.bigkey for i in counter.other_locations) or big_key_outside
|
||||||
|
else:
|
||||||
|
big_found = any(i.item is not None and i.item == dungeon.big_key for i in counter.free_locations if "- Big Chest" not in i.name) or big_key_outside
|
||||||
if counter.big_key_opened and not big_found:
|
if counter.big_key_opened and not big_found:
|
||||||
continue # Can't get to this state
|
continue # Can't get to this state
|
||||||
found_locations = set(i for i in counter.free_locations if big_found or "- Big Chest" not in i.name)
|
found_locations = set(i for i in counter.free_locations if big_found or "- Big Chest" not in i.name)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import random
|
|||||||
import time
|
import time
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
from BaseClasses import World, CollectionState, Item, Region, Location, Shop
|
from BaseClasses import World, CollectionState, Item, Region, Location, Shop, Entrance
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
from KeyDoorShuffle import validate_key_placement
|
from KeyDoorShuffle import validate_key_placement
|
||||||
from Regions import create_regions, create_shops, mark_light_world_regions, create_dungeon_regions
|
from Regions import create_regions, create_shops, mark_light_world_regions, create_dungeon_regions
|
||||||
@@ -24,8 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.0.21dev'
|
__version__ = '0.1.0-dev'
|
||||||
|
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
@@ -39,7 +38,9 @@ def main(args, seed=None, fish=None):
|
|||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
|
|
||||||
# initialize the world
|
# initialize the world
|
||||||
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
|
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
|
||||||
|
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
|
||||||
|
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
if seed is None:
|
if seed is None:
|
||||||
random.seed(None)
|
random.seed(None)
|
||||||
@@ -61,6 +62,7 @@ def main(args, seed=None, fish=None):
|
|||||||
world.enemy_health = args.enemy_health.copy()
|
world.enemy_health = args.enemy_health.copy()
|
||||||
world.enemy_damage = args.enemy_damage.copy()
|
world.enemy_damage = args.enemy_damage.copy()
|
||||||
world.beemizer = args.beemizer.copy()
|
world.beemizer = args.beemizer.copy()
|
||||||
|
world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
|
||||||
world.experimental = args.experimental.copy()
|
world.experimental = args.experimental.copy()
|
||||||
world.dungeon_counters = args.dungeon_counters.copy()
|
world.dungeon_counters = args.dungeon_counters.copy()
|
||||||
world.fish = fish
|
world.fish = fish
|
||||||
@@ -146,17 +148,18 @@ def main(args, seed=None, fish=None):
|
|||||||
fill_dungeons(world)
|
fill_dungeons(world)
|
||||||
|
|
||||||
for player in range(1, world.players+1):
|
for player in range(1, world.players+1):
|
||||||
for key_layout in world.key_layout[player].values():
|
if world.logic[player] != 'nologic':
|
||||||
if not validate_key_placement(key_layout, world, player):
|
for key_layout in world.key_layout[player].values():
|
||||||
raise RuntimeError(
|
if not validate_key_placement(key_layout, world, player):
|
||||||
"%s: %s (%s %d)" %
|
raise RuntimeError(
|
||||||
(
|
"%s: %s (%s %d)" %
|
||||||
world.fish.translate("cli","cli","keylock.detected"),
|
(
|
||||||
key_layout.sector.name,
|
world.fish.translate("cli", "cli", "keylock.detected"),
|
||||||
world.fish.translate("cli","cli","player"),
|
key_layout.sector.name,
|
||||||
player
|
world.fish.translate("cli", "cli", "player"),
|
||||||
)
|
player
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(world.fish.translate("cli","cli","fill.world"))
|
logger.info(world.fish.translate("cli","cli","fill.world"))
|
||||||
|
|
||||||
@@ -287,7 +290,8 @@ def main(args, seed=None, fish=None):
|
|||||||
"roms": rom_names,
|
"roms": rom_names,
|
||||||
"remote_items": [player for player in range(1, world.players + 1) if world.remote_items[player]],
|
"remote_items": [player for player in range(1, world.players + 1) if world.remote_items[player]],
|
||||||
"locations": [((location.address, location.player), (location.item.code, location.item.player))
|
"locations": [((location.address, location.player), (location.item.code, location.item.player))
|
||||||
for location in world.get_filled_locations() if type(location.address) is int]
|
for location in world.get_filled_locations() if type(location.address) is int],
|
||||||
|
"tags" : ["DR"]
|
||||||
}).encode("utf-8"))
|
}).encode("utf-8"))
|
||||||
if args.jsonout:
|
if args.jsonout:
|
||||||
jsonout["multidata"] = list(multidata)
|
jsonout["multidata"] = list(multidata)
|
||||||
@@ -328,7 +332,9 @@ def main(args, seed=None, fish=None):
|
|||||||
|
|
||||||
def copy_world(world):
|
def copy_world(world):
|
||||||
# ToDo: Not good yet
|
# ToDo: Not good yet
|
||||||
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
|
||||||
|
world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
|
||||||
|
world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
||||||
ret.teams = world.teams
|
ret.teams = world.teams
|
||||||
ret.player_names = copy.deepcopy(world.player_names)
|
ret.player_names = copy.deepcopy(world.player_names)
|
||||||
ret.remote_items = world.remote_items.copy()
|
ret.remote_items = world.remote_items.copy()
|
||||||
@@ -363,6 +369,8 @@ def copy_world(world):
|
|||||||
ret.enemy_health = world.enemy_health.copy()
|
ret.enemy_health = world.enemy_health.copy()
|
||||||
ret.enemy_damage = world.enemy_damage.copy()
|
ret.enemy_damage = world.enemy_damage.copy()
|
||||||
ret.beemizer = world.beemizer.copy()
|
ret.beemizer = world.beemizer.copy()
|
||||||
|
ret.intensity = world.intensity.copy()
|
||||||
|
ret.experimental = world.experimental.copy()
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
@@ -371,11 +379,17 @@ def copy_world(world):
|
|||||||
create_inverted_regions(ret, player)
|
create_inverted_regions(ret, player)
|
||||||
create_dungeon_regions(ret, player)
|
create_dungeon_regions(ret, player)
|
||||||
create_shops(ret, player)
|
create_shops(ret, player)
|
||||||
create_doors(ret, player)
|
|
||||||
create_rooms(ret, player)
|
create_rooms(ret, player)
|
||||||
create_dungeons(ret, player)
|
create_dungeons(ret, player)
|
||||||
|
|
||||||
copy_dynamic_regions_and_locations(world, ret)
|
copy_dynamic_regions_and_locations(world, ret)
|
||||||
|
for player in range(1, world.players + 1):
|
||||||
|
if world.mode[player] == 'standard':
|
||||||
|
parent = ret.get_region('Menu', player)
|
||||||
|
target = ret.get_region('Hyrule Castle Secret Entrance', player)
|
||||||
|
connection = Entrance(player, 'Uncle S&Q', parent)
|
||||||
|
parent.exits.append(connection)
|
||||||
|
connection.connect(target)
|
||||||
|
|
||||||
# copy bosses
|
# copy bosses
|
||||||
for dungeon in world.dungeons:
|
for dungeon in world.dungeons:
|
||||||
@@ -418,6 +432,10 @@ def copy_world(world):
|
|||||||
ret.state.stale = {player: True for player in range(1, world.players + 1)}
|
ret.state.stale = {player: True for player in range(1, world.players + 1)}
|
||||||
|
|
||||||
ret.doors = world.doors
|
ret.doors = world.doors
|
||||||
|
for door in ret.doors:
|
||||||
|
entrance = ret.check_for_entrance(door.name, door.player)
|
||||||
|
if entrance is not None:
|
||||||
|
entrance.door = door
|
||||||
ret.paired_doors = world.paired_doors
|
ret.paired_doors = world.paired_doors
|
||||||
ret.rooms = world.rooms
|
ret.rooms = world.rooms
|
||||||
ret.inaccessible_regions = world.inaccessible_regions
|
ret.inaccessible_regions = world.inaccessible_regions
|
||||||
@@ -469,7 +487,6 @@ def create_playthrough(world):
|
|||||||
logging.getLogger('').debug(world.fish.translate("cli","cli","building.collection.spheres"))
|
logging.getLogger('').debug(world.fish.translate("cli","cli","building.collection.spheres"))
|
||||||
while sphere_candidates:
|
while sphere_candidates:
|
||||||
state.sweep_for_events(key_only=True)
|
state.sweep_for_events(key_only=True)
|
||||||
state.sweep_for_crystal_access()
|
|
||||||
|
|
||||||
sphere = []
|
sphere = []
|
||||||
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
|
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
|
||||||
@@ -487,7 +504,7 @@ def create_playthrough(world):
|
|||||||
|
|
||||||
logging.getLogger('').debug(world.fish.translate("cli","cli","building.calculating.spheres"), len(collection_spheres), len(sphere), len(prog_locations))
|
logging.getLogger('').debug(world.fish.translate("cli","cli","building.calculating.spheres"), len(collection_spheres), len(sphere), len(prog_locations))
|
||||||
if not sphere:
|
if not sphere:
|
||||||
logging.getLogger('').debug(world.fish.translate("cli","cli","cannot.reach.items"), [world.fish.translate("cli","cli","cannot.reach.item") % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates])
|
logging.getLogger('').error(world.fish.translate("cli","cli","cannot.reach.items"), [world.fish.translate("cli","cli","cannot.reach.item") % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates])
|
||||||
if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]):
|
if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]):
|
||||||
raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.progression"))
|
raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.progression"))
|
||||||
else:
|
else:
|
||||||
@@ -531,7 +548,6 @@ def create_playthrough(world):
|
|||||||
collection_spheres = []
|
collection_spheres = []
|
||||||
while required_locations:
|
while required_locations:
|
||||||
state.sweep_for_events(key_only=True)
|
state.sweep_for_events(key_only=True)
|
||||||
state.sweep_for_crystal_access()
|
|
||||||
|
|
||||||
sphere = list(filter(lambda loc: state.can_reach(loc) and state.not_flooding_a_key(world, loc), required_locations))
|
sphere = list(filter(lambda loc: state.can_reach(loc) and state.not_flooding_a_key(world, loc), required_locations))
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -149,9 +149,10 @@ def roll_settings(weights):
|
|||||||
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
|
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
|
||||||
door_shuffle = get_choice('door_shuffle')
|
door_shuffle = get_choice('door_shuffle')
|
||||||
ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
|
ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
|
||||||
|
ret.intensity = get_choice('intensity')
|
||||||
ret.experimental = get_choice('experimental') == 'on'
|
ret.experimental = get_choice('experimental') == 'on'
|
||||||
|
|
||||||
ret.dungeon_counters = get_choice('dungeon_counters')
|
ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default'
|
||||||
if ret.dungeon_counters == 'default':
|
if ret.dungeon_counters == 'default':
|
||||||
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
|
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ def roll_settings(weights):
|
|||||||
|
|
||||||
ret.crystals_gt = get_choice('tower_open')
|
ret.crystals_gt = get_choice('tower_open')
|
||||||
|
|
||||||
ret.crystals_ganon = get_choice('ganon_open')
|
ret.crystals_ganon = get_choice('ganon_open')
|
||||||
|
|
||||||
ret.mode = get_choice('world_state')
|
ret.mode = get_choice('world_state')
|
||||||
if ret.mode == 'retro':
|
if ret.mode == 'retro':
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ Doors are shuffled between dungeons as well.
|
|||||||
|
|
||||||
Doors are not shuffled.
|
Doors are not shuffled.
|
||||||
|
|
||||||
|
## Intensity
|
||||||
|
|
||||||
|
#### Level 1
|
||||||
|
Normal door and spiral staircases are shuffled
|
||||||
|
#### Level 2
|
||||||
|
Same as Level 1 plus open edges and straight staircases are shuffled.
|
||||||
|
#### Level 3 (Coming soon)
|
||||||
|
Same as Level 2 plus Dungeon Lobbies are shuffled
|
||||||
|
|
||||||
|
|
||||||
## Map/Compass/Small Key/Big Key shuffle (aka Keysanity)
|
## Map/Compass/Small Key/Big Key shuffle (aka Keysanity)
|
||||||
|
|
||||||
These settings allow dungeon specific items to be distributed anywhere in the world and not just in their native dungeon.
|
These settings allow dungeon specific items to be distributed anywhere in the world and not just in their native dungeon.
|
||||||
@@ -75,3 +85,9 @@ Show the help message and exit.
|
|||||||
```
|
```
|
||||||
|
|
||||||
For specifying the door shuffle you want as above. (default: basic)
|
For specifying the door shuffle you want as above. (default: basic)
|
||||||
|
|
||||||
|
```
|
||||||
|
--intensity
|
||||||
|
```
|
||||||
|
|
||||||
|
For specifying the door shuffle intensity level you want as above. (default: 2)
|
||||||
|
|||||||
+29
-22
@@ -1,31 +1,38 @@
|
|||||||
# New Features
|
# New Features
|
||||||
|
|
||||||
* Mirror Scroll no longer erases blocks, the real mirror still will. (Sorry!)
|
* Crossed Dungeon generation improvements
|
||||||
* Standard+Crossed Dungeon now gives you a little magic, a few bombs, and a few arrows if you die or S&Q after meeting your uncle (also works with mirror/scroll)
|
* Standard mode generation improvements
|
||||||
* Dungeon reminder added to hud for Crossed dungeons
|
* Spoiler lists bosses (multiworld compatible)
|
||||||
* Blinking red square added to hud and it indicates a boss room is close by. Only appears if you have the compass. (Basic & Crossed)
|
* Bombs escape not valid for Crossed Dungeon
|
||||||
* Agahnims dungeon items can be started with now
|
* Graph algorithm speed improvement for placements and playthrough
|
||||||
* GUI updates courtesy of Mike T
|
* TT Attic Hint tile should have a crystal switch accessible now
|
||||||
|
* Updated to v.31.0.5
|
||||||
|
|
||||||
## Map Features (Crossed only + Experimental)
|
### Experimental features
|
||||||
|
|
||||||
* Key counters added to hud. Indicates number of keys in chests (found/total). In small key shuffle, this count indicates how many smalls for that dungeon could be outside it.
|
* Moved BK information and total chest keys per dungeon to keysanity menu. The info there requires compass for all info.
|
||||||
* Total key indicator added to hud if you have found the map. Counts down from the total number of keys in dungeon to 0 as you collect them.
|
* Map still required for on-hud key counter.
|
||||||
* Big Key indicator added to hud. Indicates if BK is not in the dungeon, or if BnC guard has it (Probably will move away from hud if kept)
|
* Added total counter to keysanity the compass/map screen when you have the compass for the dungeon.
|
||||||
|
* Open "Edge" transitions can now be linked with normal doors
|
||||||
|
* "Straight" staircases (the ones similar to normal doors) can be linked with both normal doors and edges
|
||||||
|
|
||||||
Note: Only one of the key indicator will probably become core at most.
|
#### Couple of temporary debug features added:
|
||||||
|
|
||||||
## Experimental changes
|
* Total item count displays where TFH's goal usually does
|
||||||
|
* A red square appears in the upper right corner of the hud if the castle gate is closed
|
||||||
* Mirror scroll is now core for non-vanilla Door Shuffle (no longer experimental)
|
|
||||||
* GT Bosses stay dead in non-vanilla Door Shuffle (no longer experimental)
|
|
||||||
* Map features listed above are now experimental
|
|
||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
|
|
||||||
* Splashing at hobo no longer prevents you from buying bomb capacity upgrades
|
* Fix for Animated Tiles in crossed dungeon
|
||||||
* Small vitreous eyeballs will not drop items (DR basic and crossed only)
|
* Stonewall hardlock no longer reachable from certain drops (Sewer Drop, some Skull Woods drops) that were previously possible
|
||||||
* In Vanilla doors the HC back hallway area was broken - should be better now - also Trap Doors
|
* No logic uses less key door logic
|
||||||
* Firebar speed should now be consistent. Ice palace rooms have slow firebars even if shuffled to other dungeons. Others should have normal speed firebars.
|
* Spoiler log encoding
|
||||||
* Red/Blue pendant swap fixed (originally ER bug)
|
* Enemizer settings made consistent with website
|
||||||
* Compass shuffle vs map shuffle item menu fix (originally ER bug)
|
* Swamp flooded ladders in the basement now requires Flippers
|
||||||
|
* PoD EG Glitch gets killed on transitions (Only when DR is on)
|
||||||
|
* Problem with standard logic fixed wanting you to pass through the tapestry backwards to rescue Zelda
|
||||||
|
* Fixed SRAM corruption issues
|
||||||
|
* Problem with the dungeons requiring you to take Blind through her attic fixed. (Maiden no longer despawns)
|
||||||
|
* Hyrule Castle will not be your DW access in various Entrance Shuffles: simple, restricted, dungeonssimple, dungeonsfull
|
||||||
|
(Also prevents getting stuck in TR opening)
|
||||||
|
* Beatable only (accessibility: none) no longer fails when there are unplaced items
|
||||||
+10
-6
@@ -4,6 +4,7 @@ from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType
|
|||||||
|
|
||||||
def create_regions(world, player):
|
def create_regions(world, player):
|
||||||
world.regions += [
|
world.regions += [
|
||||||
|
create_lw_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
|
||||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'],
|
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',
|
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
|
||||||
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||||
@@ -177,7 +178,7 @@ def create_regions(world, player):
|
|||||||
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
||||||
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
||||||
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
|
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
|
||||||
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
||||||
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
||||||
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
||||||
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
|
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
|
||||||
@@ -211,7 +212,8 @@ def create_dungeon_regions(world, player):
|
|||||||
'Hyrule Castle East Hall SW']),
|
'Hyrule Castle East Hall SW']),
|
||||||
create_dungeon_region(player, 'Hyrule Castle West Hall', 'Hyrule Castle', None, ['Hyrule Castle West Hall E', 'Hyrule Castle West Hall S']),
|
create_dungeon_region(player, 'Hyrule Castle West Hall', 'Hyrule Castle', None, ['Hyrule Castle West Hall E', 'Hyrule Castle West Hall S']),
|
||||||
create_dungeon_region(player, 'Hyrule Castle Back Hall', 'Hyrule Castle', None, ['Hyrule Castle Back Hall E', 'Hyrule Castle Back Hall W', 'Hyrule Castle Back Hall Down Stairs']),
|
create_dungeon_region(player, 'Hyrule Castle Back Hall', 'Hyrule Castle', None, ['Hyrule Castle Back Hall E', 'Hyrule Castle Back Hall W', 'Hyrule Castle Back Hall Down Stairs']),
|
||||||
create_dungeon_region(player, 'Hyrule Castle Throne Room', 'Hyrule Castle', None, ['Hyrule Castle Throne Room N', 'Hyrule Castle Throne Room South Stairs']),
|
create_dungeon_region(player, 'Hyrule Castle Throne Room', 'Hyrule Castle', None, ['Hyrule Castle Throne Room Tapestry', 'Hyrule Castle Throne Room South Stairs']),
|
||||||
|
create_dungeon_region(player, 'Hyrule Castle Behind Tapestry', 'Hyrule Castle', None, ['Hyrule Castle Throne Room N', 'Hyrule Castle Tapestry Backwards']),
|
||||||
|
|
||||||
create_dungeon_region(player, 'Hyrule Dungeon Map Room', 'Hyrule Castle', ['Hyrule Castle - Map Chest', 'Hyrule Castle - Map Guard Key Drop'], ['Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon Map Room Up Stairs']),
|
create_dungeon_region(player, 'Hyrule Dungeon Map Room', 'Hyrule Castle', ['Hyrule Castle - Map Chest', 'Hyrule Castle - Map Guard Key Drop'], ['Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon Map Room Up Stairs']),
|
||||||
create_dungeon_region(player, 'Hyrule Dungeon North Abyss', 'Hyrule Castle', None, ['Hyrule Dungeon North Abyss South Edge', 'Hyrule Dungeon North Abyss Key Door N']),
|
create_dungeon_region(player, 'Hyrule Dungeon North Abyss', 'Hyrule Castle', None, ['Hyrule Dungeon North Abyss South Edge', 'Hyrule Dungeon North Abyss Key Door N']),
|
||||||
@@ -314,7 +316,7 @@ def create_dungeon_regions(world, player):
|
|||||||
create_dungeon_region(player, 'Hera Beetles', 'Tower of Hera', None, ['Hera Beetles Down Stairs', 'Hera Beetles WS', 'Hera Beetles Holes']),
|
create_dungeon_region(player, 'Hera Beetles', 'Tower of Hera', None, ['Hera Beetles Down Stairs', 'Hera Beetles WS', 'Hera Beetles Holes']),
|
||||||
create_dungeon_region(player, 'Hera Startile Corner', 'Tower of Hera', None, ['Hera Startile Corner ES', 'Hera Startile Corner NW', 'Hera Startile Corner Holes']),
|
create_dungeon_region(player, 'Hera Startile Corner', 'Tower of Hera', None, ['Hera Startile Corner ES', 'Hera Startile Corner NW', 'Hera Startile Corner Holes']),
|
||||||
create_dungeon_region(player, 'Hera Startile Wide', 'Tower of Hera', None, ['Hera Startile Wide SW', 'Hera Startile Wide Up Stairs', 'Hera Startile Wide Holes']),
|
create_dungeon_region(player, 'Hera Startile Wide', 'Tower of Hera', None, ['Hera Startile Wide SW', 'Hera Startile Wide Up Stairs', 'Hera Startile Wide Holes']),
|
||||||
create_dungeon_region(player, 'Hera 4F', 'Tower of Hera', ['Tower of Hera - Compass Chest'], ['Hera 4F Down Stairs', 'Hera 4F Up Stairs', 'Hera 4F Holes']),
|
create_dungeon_region(player, 'Hera 4F', 'Tower of Hera', ['Tower of Hera - Compass Chest'], ['Hera 4F Down Stairs', 'Hera 4F Up Stairs', 'Hera Big Chest Hook Path', 'Hera 4F Holes']),
|
||||||
create_dungeon_region(player, 'Hera Big Chest Landing', 'Tower of Hera', ['Tower of Hera - Big Chest'], ['Hera Big Chest Landing Exit', 'Hera Big Chest Landing Holes']),
|
create_dungeon_region(player, 'Hera Big Chest Landing', 'Tower of Hera', ['Tower of Hera - Big Chest'], ['Hera Big Chest Landing Exit', 'Hera Big Chest Landing Holes']),
|
||||||
create_dungeon_region(player, 'Hera 5F', 'Tower of Hera', None, ['Hera 5F Down Stairs', 'Hera 5F Up Stairs', 'Hera 5F Star Hole', 'Hera 5F Pothole Chain', 'Hera 5F Normal Holes']),
|
create_dungeon_region(player, 'Hera 5F', 'Tower of Hera', None, ['Hera 5F Down Stairs', 'Hera 5F Up Stairs', 'Hera 5F Star Hole', 'Hera 5F Pothole Chain', 'Hera 5F Normal Holes']),
|
||||||
create_dungeon_region(player, 'Hera Fairies', 'Tower of Hera', None, ['Hera Fairies\' Warp']),
|
create_dungeon_region(player, 'Hera Fairies', 'Tower of Hera', None, ['Hera Fairies\' Warp']),
|
||||||
@@ -473,7 +475,8 @@ def create_dungeon_regions(world, player):
|
|||||||
create_dungeon_region(player, 'Thieves Hellway S Crystal', 'Thieves\' Town', None, ['Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway Crystal ES']),
|
create_dungeon_region(player, 'Thieves Hellway S Crystal', 'Thieves\' Town', None, ['Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway Crystal ES']),
|
||||||
create_dungeon_region(player, 'Thieves Triple Bypass', 'Thieves\' Town', None, ['Thieves Triple Bypass WN', 'Thieves Triple Bypass EN', 'Thieves Triple Bypass SE']),
|
create_dungeon_region(player, 'Thieves Triple Bypass', 'Thieves\' Town', None, ['Thieves Triple Bypass WN', 'Thieves Triple Bypass EN', 'Thieves Triple Bypass SE']),
|
||||||
create_dungeon_region(player, 'Thieves Spike Switch', 'Thieves\' Town', ['Thieves\' Town - Spike Switch Pot Key'], ['Thieves Spike Switch SW', 'Thieves Spike Switch Up Stairs']),
|
create_dungeon_region(player, 'Thieves Spike Switch', 'Thieves\' Town', ['Thieves\' Town - Spike Switch Pot Key'], ['Thieves Spike Switch SW', 'Thieves Spike Switch Up Stairs']),
|
||||||
create_dungeon_region(player, 'Thieves Attic', 'Thieves\' Town', None, ['Thieves Attic Down Stairs', 'Thieves Attic ES']),
|
create_dungeon_region(player, 'Thieves Attic', 'Thieves\' Town', None, ['Thieves Attic Down Stairs', 'Thieves Attic ES', 'Thieves Attic Orange Barrier']),
|
||||||
|
create_dungeon_region(player, 'Thieves Attic Hint', 'Thieves\' Town', None, ['Thieves Attic Hint Orange Barrier']),
|
||||||
create_dungeon_region(player, 'Thieves Cricket Hall Left', 'Thieves\' Town', None, ['Thieves Cricket Hall Left WS', 'Thieves Cricket Hall Left Edge']),
|
create_dungeon_region(player, 'Thieves Cricket Hall Left', 'Thieves\' Town', None, ['Thieves Cricket Hall Left WS', 'Thieves Cricket Hall Left Edge']),
|
||||||
create_dungeon_region(player, 'Thieves Cricket Hall Right', 'Thieves\' Town', None, ['Thieves Cricket Hall Right Edge', 'Thieves Cricket Hall Right ES']),
|
create_dungeon_region(player, 'Thieves Cricket Hall Right', 'Thieves\' Town', None, ['Thieves Cricket Hall Right Edge', 'Thieves Cricket Hall Right ES']),
|
||||||
create_dungeon_region(player, 'Thieves Attic Window', 'Thieves\' Town', ['Thieves\' Town - Attic', 'Attic Cracked Floor'], ['Thieves Attic Window WS']),
|
create_dungeon_region(player, 'Thieves Attic Window', 'Thieves\' Town', ['Thieves\' Town - Attic', 'Attic Cracked Floor'], ['Thieves Attic Window WS']),
|
||||||
@@ -727,7 +730,7 @@ def create_dungeon_regions(world, player):
|
|||||||
world.get_region('Hera Tridorm', player).crystal_switch = True
|
world.get_region('Hera Tridorm', player).crystal_switch = True
|
||||||
world.get_region('Hera Startile Wide', player).crystal_switch = True
|
world.get_region('Hera Startile Wide', player).crystal_switch = True
|
||||||
world.get_region('PoD Arena Main', player).crystal_switch = True
|
world.get_region('PoD Arena Main', player).crystal_switch = True
|
||||||
world.get_region('PoD Arena Bridge', player).crystal_switch = True
|
world.get_region('PoD Arena Bridge', player).crystal_switch = True # RANGED Weapon Required
|
||||||
world.get_region('PoD Sexy Statue', player).crystal_switch = True
|
world.get_region('PoD Sexy Statue', player).crystal_switch = True
|
||||||
world.get_region('PoD Bow Statue', player).crystal_switch = True # LADDER not accessible (maybe with cane)
|
world.get_region('PoD Bow Statue', player).crystal_switch = True # LADDER not accessible (maybe with cane)
|
||||||
world.get_region('PoD Dark Pegs', player).crystal_switch = True
|
world.get_region('PoD Dark Pegs', player).crystal_switch = True
|
||||||
@@ -747,7 +750,8 @@ def create_dungeon_regions(world, player):
|
|||||||
world.get_region('TR Crystal Maze', player).crystal_switch = True
|
world.get_region('TR Crystal Maze', player).crystal_switch = True
|
||||||
world.get_region('GT Crystal Conveyor', player).crystal_switch = True # INTERIOR not accessible
|
world.get_region('GT Crystal Conveyor', player).crystal_switch = True # INTERIOR not accessible
|
||||||
world.get_region('GT Hookshot South Platform', player).crystal_switch = True
|
world.get_region('GT Hookshot South Platform', player).crystal_switch = True
|
||||||
# world.get_region('GT Double Switch Switches', player).crystal_switch = True # this is not very relevant
|
# Relevant to indicate north door can access c_switch
|
||||||
|
world.get_region('GT Double Switch Switches', player).crystal_switch = True
|
||||||
world.get_region('GT Spike Crystals', player).crystal_switch = True
|
world.get_region('GT Spike Crystals', player).crystal_switch = True
|
||||||
world.get_region('GT Crystal Paths', player).crystal_switch = True
|
world.get_region('GT Crystal Paths', player).crystal_switch = True
|
||||||
world.get_region('GT Hidden Spikes', player).crystal_switch = True
|
world.get_region('GT Hidden Spikes', player).crystal_switch = True
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '9ed3ae2d129faa1cd3858e2f72b11b62'
|
RANDOMIZERBASEHASH = 'b9e578ef0af231041070bd9049a55646'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -172,14 +172,14 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, r
|
|||||||
options = {
|
options = {
|
||||||
'RandomizeEnemies': world.enemy_shuffle[player] != 'none',
|
'RandomizeEnemies': world.enemy_shuffle[player] != 'none',
|
||||||
'RandomizeEnemiesType': 3,
|
'RandomizeEnemiesType': 3,
|
||||||
'RandomizeBushEnemyChance': world.enemy_shuffle[player] == 'chaos',
|
'RandomizeBushEnemyChance': world.enemy_shuffle[player] == 'random',
|
||||||
'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default',
|
'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default',
|
||||||
'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]],
|
'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]],
|
||||||
'OHKO': False,
|
'OHKO': False,
|
||||||
'RandomizeEnemyDamage': world.enemy_damage[player] != 'default',
|
'RandomizeEnemyDamage': world.enemy_damage[player] != 'default',
|
||||||
'AllowEnemyZeroDamage': True,
|
'AllowEnemyZeroDamage': True,
|
||||||
'ShuffleEnemyDamageGroups': world.enemy_damage[player] != 'default',
|
'ShuffleEnemyDamageGroups': world.enemy_damage[player] != 'default',
|
||||||
'EnemyDamageChaosMode': world.enemy_damage[player] == 'chaos',
|
'EnemyDamageChaosMode': world.enemy_damage[player] == 'random',
|
||||||
'EasyModeEscape': False,
|
'EasyModeEscape': False,
|
||||||
'EnemiesAbsorbable': False,
|
'EnemiesAbsorbable': False,
|
||||||
'AbsorbableSpawnRate': 10,
|
'AbsorbableSpawnRate': 10,
|
||||||
@@ -218,9 +218,9 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, r
|
|||||||
'SwordGraphics': "sword_gfx/normal.gfx",
|
'SwordGraphics': "sword_gfx/normal.gfx",
|
||||||
'BeeMizer': False,
|
'BeeMizer': False,
|
||||||
'BeesLevel': 0,
|
'BeesLevel': 0,
|
||||||
'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'chaos',
|
'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'random',
|
||||||
'RandomizeTileTrapFloorTile': False,
|
'RandomizeTileTrapFloorTile': False,
|
||||||
'AllowKillableThief': bool(random.randint(0,1)) if world.enemy_shuffle[player] == 'chaos' else world.enemy_shuffle[player] != 'none',
|
'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'random' else world.enemy_shuffle[player] != 'none',
|
||||||
'RandomizeSpriteOnHit': random_sprite_on_hit,
|
'RandomizeSpriteOnHit': random_sprite_on_hit,
|
||||||
'DebugMode': False,
|
'DebugMode': False,
|
||||||
'DebugForceEnemy': False,
|
'DebugForceEnemy': False,
|
||||||
@@ -319,6 +319,8 @@ def get_sprite_from_name(name):
|
|||||||
name = name.lower()
|
name = name.lower()
|
||||||
if name in ['random', 'randomonhit']:
|
if name in ['random', 'randomonhit']:
|
||||||
return Sprite(random.choice(list(_sprite_table.values())))
|
return Sprite(random.choice(list(_sprite_table.values())))
|
||||||
|
if name == ('(default link)'):
|
||||||
|
name = 'link'
|
||||||
return Sprite(_sprite_table[name]) if name in _sprite_table else None
|
return Sprite(_sprite_table[name]) if name in _sprite_table else None
|
||||||
|
|
||||||
class Sprite(object):
|
class Sprite(object):
|
||||||
@@ -593,18 +595,21 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
patch_shuffled_dark_sanc(world, rom, player)
|
patch_shuffled_dark_sanc(world, rom, player)
|
||||||
|
|
||||||
# setup dr option flags based on experimental, etc.
|
# setup dr option flags based on experimental, etc.
|
||||||
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
||||||
if world.experimental[player]:
|
if world.experimental[player]:
|
||||||
dr_flags |= DROptions.Map_Info
|
dr_flags |= DROptions.Map_Info
|
||||||
|
dr_flags |= DROptions.Debug
|
||||||
|
|
||||||
# patch doors
|
# patch doors
|
||||||
if world.doorShuffle[player] == 'crossed':
|
if world.doorShuffle[player] == 'crossed':
|
||||||
rom.write_byte(0x139004, 2)
|
rom.write_byte(0x138002, 2)
|
||||||
for name, layout in world.key_layout[player].items():
|
for name, layout in world.key_layout[player].items():
|
||||||
offset = compass_data[name][4]//2
|
offset = compass_data[name][4]//2
|
||||||
rom.write_byte(0x13f01c+offset, layout.max_chests + layout.max_drops)
|
rom.write_byte(0x13f01c+offset, layout.max_chests + layout.max_drops)
|
||||||
rom.write_byte(0x13f02a+offset, layout.max_chests)
|
rom.write_byte(0x13f02a+offset, layout.max_chests)
|
||||||
builder = world.dungeon_layouts[player][name]
|
builder = world.dungeon_layouts[player][name]
|
||||||
|
rom.write_byte(0x13f070+offset, builder.location_cnt % 10)
|
||||||
|
rom.write_byte(0x13f07e+offset, builder.location_cnt // 10)
|
||||||
bk_status = 1 if builder.bk_required else 0
|
bk_status = 1 if builder.bk_required else 0
|
||||||
bk_status = 2 if builder.bk_provided else bk_status
|
bk_status = 2 if builder.bk_provided else bk_status
|
||||||
rom.write_byte(0x13f038+offset*2, bk_status)
|
rom.write_byte(0x13f038+offset*2, bk_status)
|
||||||
@@ -616,9 +621,10 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
else:
|
else:
|
||||||
logging.getLogger('').warning('Randomizer rom update! Compasses in crossed are borken')
|
logging.getLogger('').warning('Randomizer rom update! Compasses in crossed are borken')
|
||||||
if world.doorShuffle[player] == 'basic':
|
if world.doorShuffle[player] == 'basic':
|
||||||
rom.write_byte(0x139004, 1)
|
rom.write_byte(0x138002, 1)
|
||||||
for door in world.doors:
|
for door in world.doors:
|
||||||
if door.dest is not None and door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs]:
|
if door.dest is not None and door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs,
|
||||||
|
DoorType.Open, DoorType.StraightStairs]:
|
||||||
rom.write_bytes(door.getAddress(), door.dest.getTarget(door))
|
rom.write_bytes(door.getAddress(), door.dest.getTarget(door))
|
||||||
for room in world.rooms:
|
for room in world.rooms:
|
||||||
if room.player == player and room.modified:
|
if room.player == player and room.modified:
|
||||||
@@ -638,9 +644,11 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
dungeon_name = opposite_door.entrance.parent_region.dungeon.name
|
dungeon_name = opposite_door.entrance.parent_region.dungeon.name
|
||||||
dungeon_id = boss_indicator[dungeon_name][0]
|
dungeon_id = boss_indicator[dungeon_name][0]
|
||||||
rom.write_byte(0x13f000+dungeon_id, opposite_door.roomIndex)
|
rom.write_byte(0x13f000+dungeon_id, opposite_door.roomIndex)
|
||||||
rom.write_byte(0x139006, dr_flags.value)
|
elif not opposite_door:
|
||||||
|
rom.write_byte(0x13f000+dungeon_id, 0) # no supertile preceeding boss
|
||||||
|
rom.write_byte(0x138004, dr_flags.value)
|
||||||
if dr_flags & DROptions.Town_Portal and world.mode[player] == 'inverted':
|
if dr_flags & DROptions.Town_Portal and world.mode[player] == 'inverted':
|
||||||
rom.write_byte(0x139008, 1)
|
rom.write_byte(0x138006, 1)
|
||||||
|
|
||||||
# fix skull woods exit, if not fixed during exit patching
|
# fix skull woods exit, if not fixed during exit patching
|
||||||
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
|
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
|
||||||
@@ -1318,8 +1326,8 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
rom.write_bytes(0x7FC0, rom.name)
|
rom.write_bytes(0x7FC0, rom.name)
|
||||||
|
|
||||||
# set player names
|
# set player names
|
||||||
for p in range(1, min(world.players, 64) + 1):
|
for p in range(1, min(world.players, 255) + 1):
|
||||||
rom.write_bytes(0x186380 + ((p - 1) * 32), hud_format_text(world.player_names[p][team]))
|
rom.write_bytes(0x195FFC + ((p - 1) * 32), hud_format_text(world.player_names[p][team]))
|
||||||
|
|
||||||
# Write title screen Code
|
# Write title screen Code
|
||||||
hashint = int(rom.get_hash(), 16)
|
hashint = int(rom.get_hash(), 16)
|
||||||
@@ -2118,7 +2126,8 @@ def set_inverted_mode(world, player, rom):
|
|||||||
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05])
|
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05])
|
||||||
|
|
||||||
def patch_shuffled_dark_sanc(world, rom, player):
|
def patch_shuffled_dark_sanc(world, rom, player):
|
||||||
dark_sanc_entrance = str(world.get_region('Inverted Dark Sanctuary', player).entrances[0].name)
|
dark_sanc = world.get_region('Inverted Dark Sanctuary', player)
|
||||||
|
dark_sanc_entrance = str([i for i in dark_sanc.entrances if i.parent_region.name != 'Menu'][0].name)
|
||||||
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1]
|
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1]
|
||||||
door_index = door_addresses[str(dark_sanc_entrance)][0]
|
door_index = door_addresses[str(dark_sanc_entrance)][0]
|
||||||
|
|
||||||
@@ -2130,9 +2139,9 @@ def patch_shuffled_dark_sanc(world, rom, player):
|
|||||||
rom.write_bytes(0x180262, [unknown_1, unknown_2, 0x00])
|
rom.write_bytes(0x180262, [unknown_1, unknown_2, 0x00])
|
||||||
|
|
||||||
|
|
||||||
# 24B116 and 20BAD8
|
# 24B118 and 20BB32
|
||||||
compass_r_addr = 0x123116 # a9 90 24 8f 9a c7 7e
|
compass_r_addr = 0x123118 # a9 90 24 8f 9a c7 7e
|
||||||
compass_w_addr = 0x103ad8 # e2 20 ad 0c 04 c9 00 d0
|
compass_w_addr = 0x103b32 # e2 20 ad 0c 04 c9 00 d0
|
||||||
|
|
||||||
|
|
||||||
def compass_code_good(rom):
|
def compass_code_good(rom):
|
||||||
|
|||||||
@@ -1,28 +1,19 @@
|
|||||||
import logging
|
import logging
|
||||||
from BaseClasses import CollectionState, RegionType, DoorType
|
from collections import deque
|
||||||
|
|
||||||
|
from BaseClasses import CollectionState, RegionType, DoorType, Entrance
|
||||||
from Regions import key_only_locations
|
from Regions import key_only_locations
|
||||||
from RoomData import DoorKind
|
from RoomData import DoorKind
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
|
|
||||||
def set_rules(world, player):
|
def set_rules(world, player):
|
||||||
|
|
||||||
if world.logic[player] == 'nologic':
|
if world.logic[player] == 'nologic':
|
||||||
logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!')
|
logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!')
|
||||||
if world.mode[player] != 'inverted':
|
world.get_region('Menu', player).can_reach_private = lambda state: True
|
||||||
world.get_region('Links House', player).can_reach_private = lambda state: True
|
for exit in world.get_region('Menu', player).exits:
|
||||||
world.get_region('Sanctuary', player).can_reach_private = lambda state: True
|
exit.hide_path = True
|
||||||
old_rule = world.get_region('Old Man House', player).can_reach
|
return
|
||||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
world.get_region('Inverted Links House', player).can_reach_private = lambda state: True
|
|
||||||
world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True
|
|
||||||
if world.shuffle[player] != 'vanilla':
|
|
||||||
old_rule = world.get_region('Old Man House', player).can_reach
|
|
||||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
|
||||||
world.get_region('Hyrule Castle Ledge', player).can_reach_private = lambda state: True
|
|
||||||
return
|
|
||||||
|
|
||||||
global_rules(world, player)
|
global_rules(world, player)
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
@@ -113,8 +104,11 @@ def global_rules(world, player):
|
|||||||
add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player)
|
add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player)
|
||||||
|
|
||||||
# we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled!
|
# we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled!
|
||||||
old_rule = world.get_region('Old Man House', player).can_reach_private
|
world.get_region('Menu', player).can_reach_private = lambda state: True
|
||||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
for exit in world.get_region('Menu', player).exits:
|
||||||
|
exit.hide_path = True
|
||||||
|
|
||||||
|
set_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.can_reach('Old Man', 'Location', player))
|
||||||
|
|
||||||
set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
||||||
set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player))
|
set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player))
|
||||||
@@ -165,9 +159,17 @@ def global_rules(world, player):
|
|||||||
|
|
||||||
# Tower of Hera
|
# Tower of Hera
|
||||||
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player))
|
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player))
|
||||||
|
set_rule(world.get_entrance('Hera Big Chest Hook Path', player), lambda state: state.has('Hookshot', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player))
|
||||||
|
|
||||||
|
# Castle Tower
|
||||||
|
set_rule(world.get_entrance('Tower Gold Knights SW', player), lambda state: state.can_kill_most_things(player))
|
||||||
|
set_rule(world.get_entrance('Tower Gold Knights EN', player), lambda state: state.can_kill_most_things(player))
|
||||||
|
set_rule(world.get_entrance('Tower Dark Archers WN', player), lambda state: state.can_kill_most_things(player))
|
||||||
|
set_rule(world.get_entrance('Tower Red Spears WN', player), lambda state: state.can_kill_most_things(player))
|
||||||
|
set_rule(world.get_entrance('Tower Red Guards EN', player), lambda state: state.can_kill_most_things(player))
|
||||||
|
set_rule(world.get_entrance('Tower Red Guards SW', player), lambda state: state.can_kill_most_things(player))
|
||||||
set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(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_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player))
|
||||||
|
|
||||||
@@ -205,6 +207,8 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Swamp Flooded Room Ladder', player), lambda state: state.has('Drained Swamp', player))
|
set_rule(world.get_entrance('Swamp Flooded Room Ladder', player), lambda state: state.has('Drained Swamp', player))
|
||||||
set_rule(world.get_location('Swamp Palace - Flooded Room - Left', player), lambda state: state.has('Drained Swamp', player))
|
set_rule(world.get_location('Swamp Palace - Flooded Room - Left', player), lambda state: state.has('Drained Swamp', player))
|
||||||
set_rule(world.get_location('Swamp Palace - Flooded Room - Right', player), lambda state: state.has('Drained Swamp', player))
|
set_rule(world.get_location('Swamp Palace - Flooded Room - Right', player), lambda state: state.has('Drained Swamp', player))
|
||||||
|
set_rule(world.get_entrance('Swamp Flooded Spot Ladder', player), lambda state: state.has('Flippers', player) or state.has('Drained Swamp', player))
|
||||||
|
set_rule(world.get_entrance('Swamp Drain Left Up Stairs', player), lambda state: state.has('Flippers', player) or state.has('Drained Swamp', player))
|
||||||
set_rule(world.get_entrance('Swamp Waterway NW', player), lambda state: state.has('Flippers', player))
|
set_rule(world.get_entrance('Swamp Waterway NW', player), lambda state: state.has('Flippers', player))
|
||||||
set_rule(world.get_entrance('Swamp Waterway N', player), lambda state: state.has('Flippers', player))
|
set_rule(world.get_entrance('Swamp Waterway N', player), lambda state: state.has('Flippers', player))
|
||||||
set_rule(world.get_entrance('Swamp Waterway NE', player), lambda state: state.has('Flippers', player))
|
set_rule(world.get_entrance('Swamp Waterway NE', player), lambda state: state.has('Flippers', player))
|
||||||
@@ -270,7 +274,7 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
||||||
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has_Boots(player))
|
||||||
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('TR Crystal Maze Cane Path', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Crystal Maze Cane Path', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
@@ -323,8 +327,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('GT Moldorm Gap', player), lambda state: state.has('Hookshot', player) and world.get_region('GT Moldorm', player).dungeon.bosses['top'].can_defeat(state))
|
set_rule(world.get_entrance('GT Moldorm Gap', player), lambda state: state.has('Hookshot', player) and world.get_region('GT Moldorm', player).dungeon.bosses['top'].can_defeat(state))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||||
|
|
||||||
add_key_logic_rules(world, player)
|
|
||||||
|
|
||||||
# crystal switch rules
|
# crystal switch rules
|
||||||
set_rule(world.get_entrance('PoD Arena Crystal Path', player), lambda state: state.can_reach_blue(world.get_region('PoD Arena Crystal', player), player))
|
set_rule(world.get_entrance('PoD Arena Crystal Path', player), lambda state: state.can_reach_blue(world.get_region('PoD Arena Crystal', player), player))
|
||||||
set_rule(world.get_entrance('Swamp Trench 2 Pots Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Trench 2 Pots', player), player))
|
set_rule(world.get_entrance('Swamp Trench 2 Pots Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Trench 2 Pots', player), player))
|
||||||
@@ -375,6 +377,8 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('GT Double Switch Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Switches', player), player))
|
set_rule(world.get_entrance('GT Double Switch Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Switches', player), player))
|
||||||
set_rule(world.get_entrance('GT Double Switch Key Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Key Spot', player), player))
|
set_rule(world.get_entrance('GT Double Switch Key Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Key Spot', player), player))
|
||||||
|
|
||||||
|
add_key_logic_rules(world, player)
|
||||||
|
|
||||||
# End of door rando rules.
|
# End of door rando rules.
|
||||||
|
|
||||||
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
||||||
@@ -384,16 +388,6 @@ def global_rules(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def default_rules(world, player):
|
def default_rules(world, player):
|
||||||
if world.mode[player] == 'standard':
|
|
||||||
# Links house requires reaching Sanc so skipping that chest isn't a softlock.
|
|
||||||
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.has('Zelda Delivered', player) or old_rule(state)
|
|
||||||
else:
|
|
||||||
# these are default save&quit points and always accessible
|
|
||||||
world.get_region('Links House', player).can_reach_private = lambda state: True
|
|
||||||
world.get_region('Sanctuary', player).can_reach_private = lambda state: True
|
|
||||||
|
|
||||||
# overworld requirements
|
# overworld requirements
|
||||||
set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player))
|
set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player))
|
||||||
set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player))
|
set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player))
|
||||||
@@ -506,12 +500,7 @@ def default_rules(world, player):
|
|||||||
|
|
||||||
def inverted_rules(world, player):
|
def inverted_rules(world, player):
|
||||||
# s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached
|
# s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached
|
||||||
world.get_region('Inverted Links House', player).can_reach_private = lambda state: True
|
set_rule(world.get_entrance('Castle Ledge S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player))
|
||||||
world.get_region('Inverted Links House', player).entrances[0].can_reach = lambda state: True
|
|
||||||
world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True
|
|
||||||
|
|
||||||
old_rule = world.get_region('Hyrule Castle Ledge', player).can_reach_private
|
|
||||||
world.get_region('Hyrule Castle Ledge', player).can_reach_private = lambda state: (state.has_Mirror(player) and state.has('Beat Agahnim 1', player) and state.can_reach_light_world(player)) or old_rule(state)
|
|
||||||
|
|
||||||
# overworld requirements
|
# overworld requirements
|
||||||
set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player))
|
set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player))
|
||||||
@@ -824,7 +813,19 @@ std_kill_rooms = {
|
|||||||
} # all trap rooms?
|
} # all trap rooms?
|
||||||
|
|
||||||
|
|
||||||
|
def add_connection(parent_name, target_name, entrance_name, world, player):
|
||||||
|
parent = world.get_region(parent_name, player)
|
||||||
|
target = world.get_region(target_name, player)
|
||||||
|
connection = Entrance(player, entrance_name, parent)
|
||||||
|
parent.exits.append(connection)
|
||||||
|
connection.connect(target)
|
||||||
|
|
||||||
|
|
||||||
def standard_rules(world, player):
|
def standard_rules(world, player):
|
||||||
|
add_connection('Menu', 'Hyrule Castle Secret Entrance', 'Uncle S&Q', world, player)
|
||||||
|
world.get_entrance('Uncle S&Q', player).hide_path = True
|
||||||
|
set_rule(world.get_entrance('Links House S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||||
|
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||||
# these are because of rails
|
# these are because of rails
|
||||||
if world.shuffle[player] != 'vanilla':
|
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 (East)', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
@@ -837,24 +838,32 @@ def standard_rules(world, player):
|
|||||||
copy_state.sweep_for_events()
|
copy_state.sweep_for_events()
|
||||||
return copy_state.has('Zelda Delivered', player)
|
return copy_state.has('Zelda Delivered', player)
|
||||||
|
|
||||||
|
def bomb_escape_rule():
|
||||||
|
loc = world.get_location("Link's Uncle", player)
|
||||||
|
return loc.item and loc.item.name == 'Bombs (10)'
|
||||||
|
|
||||||
|
def standard_escape_rule(state):
|
||||||
|
return state.can_kill_most_things(player) or bomb_escape_rule()
|
||||||
|
|
||||||
add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule)
|
add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule)
|
||||||
|
|
||||||
# ensures the required weapon for escape lands on uncle (unless player has it pre-equipped)
|
# 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',
|
for location in ['Link\'s House', 'Sanctuary', 'Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||||
'Sewers - Secret Room - Right']:
|
'Sewers - Secret Room - Right']:
|
||||||
add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player))
|
add_rule(world.get_location(location, player), lambda state: standard_escape_rule(state))
|
||||||
add_rule(world.get_location('Secret Passage', player), lambda state: state.can_kill_most_things(player))
|
add_rule(world.get_location('Secret Passage', player), lambda state: standard_escape_rule(state))
|
||||||
|
|
||||||
escape_builder = world.dungeon_layouts[player]['Hyrule Castle']
|
escape_builder = world.dungeon_layouts[player]['Hyrule Castle']
|
||||||
for region in escape_builder.master_sector.regions:
|
for region in escape_builder.master_sector.regions:
|
||||||
for loc in region.locations:
|
for loc in region.locations:
|
||||||
add_rule(loc, lambda state: state.can_kill_most_things(player))
|
add_rule(loc, lambda state: standard_escape_rule(state))
|
||||||
if region.name in std_kill_rooms:
|
if region.name in std_kill_rooms:
|
||||||
for ent in std_kill_rooms[region.name]:
|
for ent in std_kill_rooms[region.name]:
|
||||||
add_rule(world.get_entrance(ent, player), lambda state: state.can_kill_most_things(player))
|
add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state))
|
||||||
|
|
||||||
set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', 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_entrance('Hyrule Castle Throne Room Tapestry', player), lambda state: state.has('Zelda Herself', player))
|
||||||
|
set_rule(world.get_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player))
|
||||||
|
|
||||||
def check_rule_list(state, r_list):
|
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:])
|
return True if len(r_list) <= 0 else r_list[0](state) and check_rule_list(state, r_list[1:])
|
||||||
@@ -1039,7 +1048,7 @@ def set_big_bomb_rules(world, player):
|
|||||||
# the basic routes assume you can reach eastern light world with the bomb.
|
# the basic routes assume you can reach eastern light world with the bomb.
|
||||||
# you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp
|
# you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp
|
||||||
def basic_routes(state):
|
def basic_routes(state):
|
||||||
return southern_teleporter(state) or state.can_reach('Top of Pyramid', 'Entrance', player)
|
return southern_teleporter(state) or state.has('Beat Agahnim 1', player)
|
||||||
|
|
||||||
# Key for below abbreviations:
|
# Key for below abbreviations:
|
||||||
# P = pearl
|
# P = pearl
|
||||||
@@ -1072,7 +1081,7 @@ def set_big_bomb_rules(world, player):
|
|||||||
#1. Mirror and enter via gate: Need mirror and Aga1
|
#1. Mirror and enter via gate: Need mirror and Aga1
|
||||||
#2. cross peg bridge: Need hammer and moon pearl
|
#2. cross peg bridge: Need hammer and moon pearl
|
||||||
# -> CPB or (M and A)
|
# -> CPB or (M and A)
|
||||||
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.can_reach('Top of Pyramid', 'Entrance', player)))
|
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player)))
|
||||||
elif bombshop_entrance.name in Isolated_DW_entrances:
|
elif bombshop_entrance.name in Isolated_DW_entrances:
|
||||||
# 1. mirror then flute then basic routes
|
# 1. mirror then flute then basic routes
|
||||||
# -> M and Flute and BR
|
# -> M and Flute and BR
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ normal_offset_table = {
|
|||||||
0xb7: 0x79, 0xb8: 0x7A, 0xb9: 0x7B, 0xba: 0x7C, 0xbb: 0x7D, 0xbc: 0x7E, 0xbe: 0x7F, 0xbf: 0x80,
|
0xb7: 0x79, 0xb8: 0x7A, 0xb9: 0x7B, 0xba: 0x7C, 0xbb: 0x7D, 0xbc: 0x7E, 0xbe: 0x7F, 0xbf: 0x80,
|
||||||
0xc1: 0x81, 0xc2: 0x82, 0xc3: 0x83, 0xc4: 0x84, 0xc5: 0x85, 0xc6: 0x86, 0xc7: 0x87, 0xc8: 0x88,
|
0xc1: 0x81, 0xc2: 0x82, 0xc3: 0x83, 0xc4: 0x84, 0xc5: 0x85, 0xc6: 0x86, 0xc7: 0x87, 0xc8: 0x88,
|
||||||
0xc9: 0x89, 0xcb: 0x8A, 0xcc: 0x8B, 0xce: 0x8C, 0xd1: 0x8D, 0xd2: 0x8E, 0xd5: 0x8F, 0xd6: 0x90,
|
0xc9: 0x89, 0xcb: 0x8A, 0xcc: 0x8B, 0xce: 0x8C, 0xd1: 0x8D, 0xd2: 0x8E, 0xd5: 0x8F, 0xd6: 0x90,
|
||||||
0xd8: 0x91, 0xd9: 0x92, 0xda: 0x93, 0xdb: 0x94, 0xdc: 0x95
|
0xd8: 0x91, 0xd9: 0x92, 0xda: 0x93, 0xdb: 0x94, 0xdc: 0x95,
|
||||||
|
0x40: 0x96, 0x42: 0x97 # newcomers for str stairs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -61,23 +62,25 @@ door_pair_offset_table = {
|
|||||||
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x020
|
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x020
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Note: 0-7 correspond to 1,2,3,4,5,6,a,14 respectively, see doortables.asm : MultDivInfo
|
||||||
|
|
||||||
multiply_lookup = {
|
multiply_lookup = {
|
||||||
0x08: {0x8: 1, 0x10: 2, 0x18: 3, 0x20: 4, 0x30: 6, 0x50: 0xa, 0xa0: 0x14},
|
0x08: {0x8: 0, 0x10: 1, 0x18: 2, 0x20: 3, 0x30: 5, 0x50: 6, 0xa0: 7},
|
||||||
0x10: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 2, 0x30: 3, 0x50: 0x4, 0xa0: 0xa},
|
0x10: {0x8: 0, 0x10: 0, 0x18: 2, 0x20: 1, 0x30: 2, 0x50: 3, 0xa0: 6},
|
||||||
0x18: {0x8: 1, 0x10: 2, 0x18: 1, 0x20: 4, 0x30: 2, 0x50: 0xa, 0xa0: 0x14},
|
0x18: {0x8: 0, 0x10: 1, 0x18: 0, 0x20: 3, 0x30: 1, 0x50: 6, 0xa0: 7},
|
||||||
0x20: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 1, 0x30: 3, 0x50: 5, 0xa0: 5},
|
0x20: {0x8: 0, 0x10: 0, 0x18: 2, 0x20: 0, 0x30: 2, 0x50: 4, 0xa0: 4},
|
||||||
0x30: {0x8: 1, 0x10: 1, 0x18: 1, 0x20: 2, 0x30: 1, 0x50: 5, 0xa0: 0xa},
|
0x30: {0x8: 0, 0x10: 0, 0x18: 0, 0x20: 1, 0x30: 0, 0x50: 4, 0xa0: 6},
|
||||||
0x50: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 2, 0x30: 3, 0x50: 1, 0xa0: 2},
|
0x50: {0x8: 0, 0x10: 0, 0x18: 2, 0x20: 1, 0x30: 2, 0x50: 0, 0xa0: 1},
|
||||||
0xa0: {0x8: 1, 0x10: 1, 0x18: 3, 0x20: 1, 0x30: 3, 0x50: 1, 0xa0: 1},
|
0xa0: {0x8: 0, 0x10: 0, 0x18: 2, 0x20: 0, 0x30: 2, 0x50: 0, 0xa0: 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
divisor_lookup = {
|
divisor_lookup = {
|
||||||
0x08: {0x8: 1, 0x10: 1, 0x18: 1, 0x20: 1, 0x30: 1, 0x50: 1, 0xa0: 1},
|
0x08: {0x8: 0, 0x10: 0, 0x18: 0, 0x20: 0, 0x30: 0, 0x50: 0, 0xa0: 0},
|
||||||
0x10: {0x8: 2, 0x10: 1, 0x18: 2, 0x20: 1, 0x30: 1, 0x50: 1, 0xa0: 1},
|
0x10: {0x8: 1, 0x10: 0, 0x18: 1, 0x20: 0, 0x30: 0, 0x50: 0, 0xa0: 0},
|
||||||
0x18: {0x8: 3, 0x10: 3, 0x18: 1, 0x20: 3, 0x30: 1, 0x50: 3, 0xa0: 3},
|
0x18: {0x8: 2, 0x10: 2, 0x18: 0, 0x20: 2, 0x30: 0, 0x50: 2, 0xa0: 2},
|
||||||
0x20: {0x8: 4, 0x10: 2, 0x18: 4, 0x20: 1, 0x30: 2, 0x50: 2, 0xa0: 1},
|
0x20: {0x8: 3, 0x10: 1, 0x18: 3, 0x20: 0, 0x30: 1, 0x50: 1, 0xa0: 0},
|
||||||
0x30: {0x8: 6, 0x10: 3, 0x18: 2, 0x20: 3, 0x30: 1, 0x50: 3, 0xa0: 3},
|
0x30: {0x8: 5, 0x10: 2, 0x18: 1, 0x20: 2, 0x30: 0, 0x50: 2, 0xa0: 2},
|
||||||
0x50: {0x8: 0xa, 0x10: 4, 0x18: 0xa, 0x20: 5, 0x30: 5, 0x50: 1, 0xa0: 1},
|
0x50: {0x8: 6, 0x10: 3, 0x18: 6, 0x20: 4, 0x30: 4, 0x50: 0, 0xa0: 0},
|
||||||
0xa0: {0x8: 0x14, 0x10: 0xa, 0x18: 0x14, 0x20: 5, 0x30: 0xa, 0x50: 2, 0xa0: 1},
|
0xa0: {0x8: 7, 0x10: 6, 0x18: 7, 0x20: 4, 0x30: 6, 0x50: 1, 0xa0: 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,26 @@ entrance_data = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def read_layout_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc'):
|
||||||
|
with open(old_rom, 'rb') as stream:
|
||||||
|
old_rom_data = bytearray(stream.read())
|
||||||
|
|
||||||
|
string = ''
|
||||||
|
for room in range(0, 0xff+1):
|
||||||
|
# print(ent)
|
||||||
|
pointer_start = 0xf8000+room*3
|
||||||
|
highbyte = old_rom_data[pointer_start+2]
|
||||||
|
midbyte = old_rom_data[pointer_start+1]
|
||||||
|
midbyte = midbyte - 0x80 if highbyte % 2 == 0 else midbyte
|
||||||
|
pointer = highbyte // 2 * 0x10000
|
||||||
|
pointer += midbyte * 0x100
|
||||||
|
pointer += old_rom_data[pointer_start]
|
||||||
|
layout_byte = old_rom_data[pointer+1]
|
||||||
|
layout = (layout_byte & 0x1c) >> 2
|
||||||
|
string += hex(room) + ':' + str(layout) + '\n'
|
||||||
|
print(string)
|
||||||
|
|
||||||
|
|
||||||
def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc'):
|
def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc'):
|
||||||
with open(old_rom, 'rb') as stream:
|
with open(old_rom, 'rb') as stream:
|
||||||
old_rom_data = bytearray(stream.read())
|
old_rom_data = bytearray(stream.read())
|
||||||
@@ -396,6 +416,6 @@ def print_graph(world):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pass
|
|
||||||
# make_new_base2current()
|
# make_new_base2current()
|
||||||
read_entrance_data(old_rom='C:\\Users\\Randall\\Documents\\kwyn\\orig\\z3.sfc')
|
# read_entrance_data(old_rom=sys.argv[1])
|
||||||
|
read_layout_data(old_rom=sys.argv[1])
|
||||||
|
|||||||
@@ -184,4 +184,19 @@ $a2 - MM 162 idx 0
|
|||||||
$a8 - EP 168 idx 2
|
$a8 - EP 168 idx 2
|
||||||
$bc - TT 188 idx 1
|
$bc - TT 188 idx 1
|
||||||
|
|
||||||
|
;SRAM corruption investigation
|
||||||
|
;call stack
|
||||||
|
; 110c7 7.e.7 (Dungeon_SpiralStaircase_7)
|
||||||
|
; 112b1
|
||||||
|
|
||||||
|
; dw $8CE2 ; = $10CE2*
|
||||||
|
; dw $8E0F ; = $10E0F*
|
||||||
|
; dw $8E1D ; = $10E1D*
|
||||||
|
; dw $8D10 ; = $10D10*
|
||||||
|
; dw $90C7 ; = $110C7*
|
||||||
|
|
||||||
|
;bank2 line 3323 - x is d422 - comes from 048c
|
||||||
|
|
||||||
|
; called by 10CE2, (Dungeon_SpiralStaircase_3)
|
||||||
|
;122f0
|
||||||
|
|
||||||
|
|||||||
+13
-35
@@ -14,47 +14,25 @@ incsrc drhooks.asm
|
|||||||
|
|
||||||
;Main Code
|
;Main Code
|
||||||
org $278000 ;138000
|
org $278000 ;138000
|
||||||
incsrc normal.asm
|
db $44, $52 ;DR
|
||||||
incsrc spiral.asm
|
|
||||||
incsrc gfx.asm
|
|
||||||
incsrc keydoors.asm
|
|
||||||
incsrc overrides.asm
|
|
||||||
;incsrc edges.asm
|
|
||||||
;incsrc math.asm
|
|
||||||
incsrc hudadditions.asm
|
|
||||||
warnpc $279000
|
|
||||||
|
|
||||||
; Data Section
|
|
||||||
org $279000
|
|
||||||
OffsetTable:
|
|
||||||
dw -8, 8
|
|
||||||
DRMode:
|
DRMode:
|
||||||
dw 0
|
dw 0
|
||||||
DRFlags:
|
DRFlags:
|
||||||
dw 0
|
dw 0
|
||||||
DRScroll:
|
DRScroll:
|
||||||
db 0
|
db 0
|
||||||
|
OffsetTable:
|
||||||
|
dw -8, 8
|
||||||
|
|
||||||
; Vert 0,6,0 Horz 2,0,8
|
incsrc normal.asm
|
||||||
org $279010
|
incsrc scroll.asm
|
||||||
CoordIndex: ; Horizontal 1st
|
incsrc spiral.asm
|
||||||
db 2, 0 ; Coordinate Index $20-$23
|
incsrc gfx.asm
|
||||||
OppCoordIndex:
|
incsrc keydoors.asm
|
||||||
db 0, 2 ; Swapped coordinate Index $20-$23 (minor optimization)
|
incsrc overrides.asm
|
||||||
CameraIndex: ; Horizontal 1st
|
incsrc edges.asm
|
||||||
db 0, 6 ; Camera Index $e2-$ea
|
incsrc math.asm
|
||||||
CamQuadIndex: ; Horizontal 1st
|
incsrc hudadditions.asm
|
||||||
db 8, 0 ; Camera quadrants $600-$60f
|
warnpc $279700
|
||||||
ShiftQuadIndex:
|
|
||||||
db 2, 1 ; see ShiftQuad func (relates to $a9,$aa)
|
|
||||||
CamBoundIndex: ; Horizontal 1st
|
|
||||||
db 0, 4 ; Camera Bounds $0618-$61f
|
|
||||||
OppCamBoundIndex: ; Horizontal 1st
|
|
||||||
db 4, 0 ; Camera Bounds $0618-$61f
|
|
||||||
CamBoundBaseLine: ; X camera stuff is 1st column todo Y camera needs more testing
|
|
||||||
dw $007f, $0077 ; Left/Top camera bounds when at edge or layout frozen
|
|
||||||
dw $0007, $000b ; Left/Top camera bounds when not frozen + appropriate low byte $22/$20 (preadj. by #$78/#$6c)
|
|
||||||
dw $00ff, $010b ; Right/Bot camera bounds when not frozen + appropriate low byte $20/$22
|
|
||||||
dw $017f, $0187 ; Right/Bot camera bound when at edge or layout frozen
|
|
||||||
|
|
||||||
incsrc doortables.asm
|
incsrc doortables.asm
|
||||||
|
|||||||
+213
-160
@@ -43,7 +43,7 @@ db $00,$01,$02,$00,$03,$00,$04,$00,$00,$00,$00,$00,$00,$05,$00,$00
|
|||||||
db $00,$06,$07,$08,$09,$0A,$0B,$00,$00,$0C,$0D,$0E,$00,$0F,$10,$11
|
db $00,$06,$07,$08,$09,$0A,$0B,$00,$00,$0C,$0D,$0E,$00,$0F,$10,$11
|
||||||
db $12,$13,$14,$15,$16,$00,$17,$00,$00,$00,$18,$19,$00,$00,$1A,$00
|
db $12,$13,$14,$15,$16,$00,$17,$00,$00,$00,$18,$19,$00,$00,$1A,$00
|
||||||
db $1B,$00,$1C,$1D,$1E,$1F,$20,$21,$22,$23,$24,$25,$00,$26,$27,$00
|
db $1B,$00,$1C,$1D,$1E,$1F,$20,$21,$22,$23,$24,$25,$00,$26,$27,$00
|
||||||
db $00,$28,$00,$29,$2A,$2B,$2C,$00,$00,$2D,$2E,$2F,$30,$31,$32,$00
|
db $96,$28,$97,$29,$2A,$2B,$2C,$00,$00,$2D,$2E,$2F,$30,$31,$32,$00
|
||||||
db $33,$34,$35,$36,$00,$00,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$3F,$40
|
db $33,$34,$35,$36,$00,$00,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$3F,$40
|
||||||
db $41,$42,$43,$00,$00,$00,$44,$45,$46,$00,$47,$48,$49,$4A,$4B,$00
|
db $41,$42,$43,$00,$00,$00,$44,$45,$46,$00,$47,$48,$49,$4A,$4B,$00
|
||||||
db $00,$4C,$00,$00,$00,$4D,$4E,$00,$00,$00,$00,$4F,$50,$51,$52,$53
|
db $00,$4C,$00,$00,$00,$4D,$4E,$00,$00,$00,$00,$4F,$50,$51,$52,$53
|
||||||
@@ -58,164 +58,166 @@ db $00
|
|||||||
org $27A000
|
org $27A000
|
||||||
DoorTable:
|
DoorTable:
|
||||||
;; NW 00 N 01 N 02 WN 00 W 01 WS 02 SW 00 S 01 SE 02 EN 00 E 01 ES 02 - Door ruler
|
;; NW 00 N 01 N 02 WN 00 W 01 WS 02 SW 00 S 01 SE 02 EN 00 E 01 ES 02 - Door ruler
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Default/Garbage row
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Default/Garbage row
|
||||||
dw $8000, $8000, $8000, $0450, $8000, $8000, $8000, $8000, $8000, $0452, $8000, $8000 ; HC Back Hall (x01)
|
dw $0003, $0003, $0003, $0450, $0003, $0003, $0003, $0003, $0003, $0452, $0003, $0003 ; HC Back Hall (x01)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sewer Switches (x02)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewer Switches (x02)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Crystaroller
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Crystaroller
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Arghus
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Arghus
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Aga 2
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Aga 2
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sewer Secret Room
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewer Secret Room
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sanc
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sanc
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Pokey
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Pokey
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Lava Pipe
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Lava Pipe
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Pipes n Ledge
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Pipes n Ledge
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swap Canal
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swap Canal
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod dark Maze
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod dark Maze
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod Bridge
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod Bridge
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod Eye Statue
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod Eye Statue
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Pre Aga
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Pre Aga
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Cross
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Cross
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice BK
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice BK
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x20 Aga1
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x20 Aga1
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sewer Key Rat
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewer Key Rat
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sewer Waters
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewer Waters
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Eye Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Eye Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Chest Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Chest Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp Statue
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Statue
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; PoD Arena (x2a)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; PoD Arena (x2a)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; PoD Statue (x2b)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; PoD Statue (x2b)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Compass
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Compass
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x30 Aga's Altar
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x30 Aga's Altar
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Dark Cross
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Dark Cross
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Lanmolas
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Lanmolas
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp West Wing
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp West Wing
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Flooded Key
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Flooded Key
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp Main Hub (x36)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Main Hub (x36)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp Hammer Time
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Hammer Time
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp First Basement
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp First Basement
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Drop to the Moth
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Drop to the Moth
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod 3 Catwalks
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod 3 Catwalks
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod Conveyor
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod Conveyor
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Minihelma
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Minihelma
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Conveyor
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Conveyor
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Sewers
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewers
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Desert Torches
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert Torches
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Big Chest
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Big Chest
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Cellblock
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Cellblock
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp Compass Loop
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Compass Loop
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Skull3 Torches
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Skull3 Torches
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Pod Mimics 1
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Pod Mimics 1
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Conveyor Ice
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Conveyor Ice
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Moldorm
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Moldorm
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; IPBJ
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; IPBJ
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $0401, $8000, $8000 ; HC West Hall (x50)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0401, $0003, $0003 ; HC West Hall (x50)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC Throne Room (x51)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC Throne Room (x51)
|
||||||
dw $8000, $8000, $8000, $0401, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC East Hall (x52)
|
dw $0003, $0003, $0003, $0401, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC East Hall (x52)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Desert Tiles 1
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert Tiles 1
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Skull 2 Left Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Skull 2 Left Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Skull 2 Right Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Skull 2 Right Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Skull 1 Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Skull 1 Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Skull 3 Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Skull 3 Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Helmasaur
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Helmasaur
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Spike Switch
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Spike Switch
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Cannonball
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Cannonball
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Gauntlet 1
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Gauntlet 1
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Choice Cross
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Choice Cross
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Iced U
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Iced U
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC West Lobby (x60)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC West Lobby (x60)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC Main Lobby (x61)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC Main Lobby (x61)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC East Lobby (x62)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC East Lobby (x62)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x66 Swamp Waterfall
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x66 Swamp Waterfall
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x67 Skull 1 Left Drop
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x67 Skull 1 Left Drop
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x68 Skull 1 Pinball
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x68 Skull 1 Pinball
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x6a Pod Rupees
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x6a Pod Rupees
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x6b GT Mimics
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x6b GT Mimics
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x6c GT Lanmolas
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x6c GT Lanmolas
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x6d Gauntlet 2
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x6d Gauntlet 2
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; x6e Ice Gators
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; x6e Ice Gators
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC Armory
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC Armory
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Desert BK Chest
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert BK Chest
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Swamp Flooded Chests
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Flooded Chests
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT DM's Tile
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT DM's Tile
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Randoroom
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Randoroom
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Warp Maze
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Warp Maze
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Freezors
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Freezors
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Hookpit
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Hookpit
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; HC Catawalk
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; HC Catawalk
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Desert Right Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert Right Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Left
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Left
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Hopeful Torch
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Hopeful Torch
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Right
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Right
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Lonely Freezor
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Lonely Freezor
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Vitreous (x90)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Vitreous (x90)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Rain
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Rain
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Dark Crystals
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Dark Crystals
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Blockswitch
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Blockswitch
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Fallbridge
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Fallbridge
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Torch Cross
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Torch Cross
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Darkness
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Darkness
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Warp Maze 2
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Warp Maze 2
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Invis Bridge
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Invis Bridge
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Compass Room
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Compass Room
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Big Chests
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Big Chests
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Icy Pots
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Icy Pots
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Pre-Vitreous (xa0)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Pre-Vitreous (xa0)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Fishbone
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Fishbone
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Bridges
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Bridges
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Corner
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Corner
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Trinexx (xa4)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Trinexx (xa4)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; GT Wizzrobes
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Wizzrobes
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Compass (xa8)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Compass (xa8)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Courtyard (xa9)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Courtyard (xa9)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Map (xaa)
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Map (xaa)
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Switch
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Switch
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Blind
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Blind
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Iced T
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Iced T
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Slipway
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Slipway
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Warpzone
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Warpzone
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire ????
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire ????
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Spikes
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Spikes
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Refill
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Refill
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Dark Maze
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Dark Maze
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Chainchomp
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Chainchomp
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Rollers
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Rollers
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Big Key
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Big Key
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Easter Cannonball
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Easter Cannonball
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Dark Circle
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Dark Circle
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Hellway
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Hellway
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Bossway
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Bossway
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Blockswitch
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Blockswitch
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Backtracker
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Backtracker
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Tiles
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Tiles
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Main Hub
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Main Hub
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire Big Chest
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Big Chest
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Switch Maze
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Switch Maze
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Narrow
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Narrow
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Early Hub
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Early Hub
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Floating Torches
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Floating Torches
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Armos
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Armos
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT NW Quad
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT NW Quad
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT NE Quad
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT NE Quad
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Ice Boss Drop
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Boss Drop
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire BK
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire BK
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Mire 2
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire 2
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Laser Bridge
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Laser Bridge
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TR Main Entrance
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TR Main Entrance
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Eyegores
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Eyegores
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Attic Switches
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Attic Switches
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; Eastern Attic Start
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Eastern Attic Start
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT Entrance Quad
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT Entrance Quad
|
||||||
dw $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000, $8000 ; TT SE Quad
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT SE Quad
|
||||||
; this should end at 27AE10 about
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Aga 6F
|
||||||
; some values you can hardcode
|
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewers Rope
|
||||||
|
; this should end at 27AE40 about (152 * 24 bytes = 3648 or E40)
|
||||||
|
; some values you can hardcode for spirals
|
||||||
;dw $0070, $36a0 ; ->HC Stairwell
|
;dw $0070, $36a0 ; ->HC Stairwell
|
||||||
;dw $0072, $4ff8 ; ->HC Map Room
|
;dw $0072, $4ff8 ; ->HC Map Room
|
||||||
;dw $0080, $1f50 ; ->zelda's cellblock
|
;dw $0080, $1f50 ; ->zelda's cellblock
|
||||||
|
|
||||||
org $27B000
|
org $27B000
|
||||||
SpiralTable:
|
SpiralTable: ;113 4 byte entries - should end at 27B44C
|
||||||
dw $0203, $8080 ;null row
|
dw $0203, $8080 ;null row
|
||||||
dw $0203, $8080 ;HC Backhallway
|
dw $0203, $8080 ;HC Backhallway
|
||||||
dw $0203, $8080 ;Sewer Pull
|
dw $0203, $8080 ;Sewer Pull
|
||||||
@@ -490,14 +492,15 @@ dw $0000
|
|||||||
dw $0000,$0000,$0000,$0000
|
dw $0000,$0000,$0000,$0000
|
||||||
dw $ffff ; indicates the end - we can drop this
|
dw $ffff ; indicates the end - we can drop this
|
||||||
|
|
||||||
; Edge Transition Table
|
; Edge Transition Table (Target Room, Flags, MultiDiv ratio for edges)
|
||||||
org $27C500 ;ends around 27C5F0
|
org $27C500 ;ends around 27C5F(9) 4 bytes would be 27C649
|
||||||
|
;I kind of want to split the 3rd byte into two
|
||||||
NorthOpenEdge:
|
NorthOpenEdge:
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
SouthOpenEdge:
|
SouthOpenEdge:
|
||||||
db $83,$a2,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
WestOpenEdge:
|
WestOpenEdge:
|
||||||
@@ -509,6 +512,7 @@ db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
|||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
db $00,$80,$11, $00,$80,$11, $00,$80,$11
|
||||||
; Edge Info Table (Midpoint, Width, Min Coord)
|
; Edge Info Table (Midpoint, Width, Min Coord)
|
||||||
|
; I kind of want to add a fourth byte to help indicate quadrant info on min coord
|
||||||
NorthEdgeInfo:
|
NorthEdgeInfo:
|
||||||
db $a8,$10,$a0, $2c,$08,$28 ;HC
|
db $a8,$10,$a0, $2c,$08,$28 ;HC
|
||||||
db $b8,$20,$a8 ; DP West Wing
|
db $b8,$20,$a8 ; DP West Wing
|
||||||
@@ -539,8 +543,9 @@ db $68,$10,$60, $84,$18,$78 ; HC Guards
|
|||||||
db $a0,$a0,$50 ; DP Main Lobby
|
db $a0,$a0,$50 ; DP Main Lobby
|
||||||
db $58,$50,$30, $98,$50,$70 ; TT Ambush
|
db $58,$50,$30, $98,$50,$70 ; TT Ambush
|
||||||
db $58,$50,$30 ; TT Nook
|
db $58,$50,$30 ; TT Nook
|
||||||
MultDivInfo: ; (1placeholder, 1, 2, 3, 4, 5, 6, 10, 20)
|
MultDivInfo: ; (1, 2, 3, 4, 5, 6, 10, 20)
|
||||||
db $01, $01, $02, $03, $04, $05, $06, $0a, $14
|
db $01, $02, $03, $04, $05, $06, $0a, $14
|
||||||
|
; indices: 0-7
|
||||||
|
|
||||||
|
|
||||||
; dungeon tables
|
; dungeon tables
|
||||||
@@ -556,5 +561,53 @@ BigKeyStatus: ;27f038 (status 2 indicate BnC guard)
|
|||||||
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001
|
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001
|
||||||
DungeonReminderTable: ;27f054
|
DungeonReminderTable: ;27f054
|
||||||
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C
|
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C
|
||||||
;27f070
|
TotalLocationsLow: ;27f070
|
||||||
|
db $08, $08, $06, $06, $02, $00, $04, $08, $08, $08, $06, $08, $02, $07
|
||||||
|
TotalLocationsHigh: ;27f07e
|
||||||
|
db $00, $00, $00, $00, $00, $01, $01, $00, $00, $00, $00, $00, $01, $02
|
||||||
|
;27F08C
|
||||||
|
|
||||||
|
; Vert 0,6,0 Horz 2,0,8
|
||||||
|
org $27f090
|
||||||
|
CoordIndex: ; Horizontal 1st
|
||||||
|
db 2, 0 ; Coordinate Index $20-$23
|
||||||
|
OppCoordIndex:
|
||||||
|
db 0, 2 ; Swapped coordinate Index $20-$23 (minor optimization)
|
||||||
|
CameraIndex: ; Horizontal 1st
|
||||||
|
db 0, 6 ; Camera Index $e2-$ea
|
||||||
|
CamQuadIndex: ; Horizontal 1st
|
||||||
|
db 8, 0 ; Camera quadrants $600-$60f
|
||||||
|
ShiftQuadIndex:
|
||||||
|
db 2, 1 ; see ShiftQuad func (relates to $a9,$aa)
|
||||||
|
CamBoundIndex: ; Horizontal 1st
|
||||||
|
db 0, 4 ; Camera Bounds $0618-$61f
|
||||||
|
OppCamBoundIndex: ; Horizontal 1st
|
||||||
|
db 4, 0 ; Camera Bounds $0618-$61f
|
||||||
|
CamBoundBaseLine: ; X camera stuff is 1st column todo Y camera needs more testing
|
||||||
|
dw $007f, $0077 ; Left/Top camera bounds when at edge or layout frozen
|
||||||
|
dw $0007, $000b ; Left/Top camera bounds when not frozen + appropriate low byte $22/$20 (preadj. by #$78/#$6c)
|
||||||
|
dw $00ff, $010b ; Right/Bot camera bounds when not frozen + appropriate low byte $20/$22
|
||||||
|
dw $017f, $0187 ; Right/Bot camera bound when at edge or layout frozen
|
||||||
|
;27f0ae next free byte
|
||||||
|
|
||||||
|
org $27f100
|
||||||
|
TilesetTable:
|
||||||
|
; 0 1 2 3 4 5 6 7 8 9 a b c d e f --Offset Ruler
|
||||||
|
db $13,$04,$04,$06,$0d,$ff,$08,$05,$06,$07,$07,$07,$0e,$0e,$0b,$ff
|
||||||
|
db $13,$04,$04,$0d,$0d,$0d,$08,$05,$06,$07,$07,$07,$0e,$0e,$0b,$0b
|
||||||
|
db $04,$04,$04,$0d,$0d,$ff,$08,$05,$08,$09,$07,$07,$06,$ff,$0b,$06
|
||||||
|
db $04,$05,$04,$12,$08,$08,$08,$08,$08,$09,$07,$07,$06,$0e,$0b,$0b
|
||||||
|
db $04,$04,$04,$12,$0a,$0a,$08,$ff,$ff,$09,$07,$07,$0e,$0e,$0b,$0b
|
||||||
|
db $04,$04,$04,$12,$08,$01,$09,$09,$09,$09,$07,$0e,$0e,$0e,$0b,$0b
|
||||||
|
db $04,$04,$04,$12,$0a,$0a,$08,$09,$09,$ff,$07,$0e,$0e,$0e,$0b,$ff
|
||||||
|
db $04,$04,$04,$12,$12,$12,$08,$05,$ff,$ff,$ff,$0e,$0e,$0e,$0b,$0b
|
||||||
|
db $04,$04,$04,$12,$12,$12,$ff,$05,$ff,$05,$ff,$0e,$0e,$0e,$0b,$ff
|
||||||
|
db $0c,$0c,$0c,$0c,$ff,$0e,$0e,$0c,$0c,$05,$ff,$0e,$0e,$0e,$0b,$0b
|
||||||
|
db $0c,$0c,$0c,$0c,$0d,$0e,$0e,$05,$05,$05,$05,$0a,$0a,$ff,$0b,$0b
|
||||||
|
db $04,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$05,$05,$05,$0a,$0a,$ff,$0b,$0b
|
||||||
|
db $04,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$05,$05,$ff,$0a,$0a,$ff,$0b,$ff
|
||||||
|
db $04,$0c,$0c,$ff,$ff,$0d,$0d,$ff,$05,$05,$05,$0a,$0a,$ff,$0b,$06
|
||||||
|
db $04,$06,$06,$06,$06,$06,$06,$06,$06,$ff,$06,$06,$ff,$06,$06,$06
|
||||||
|
db $06,$06,$03,$03,$03,$03,$ff,$ff,$06,$06,$06,$06,$ff,$06,$06,$06
|
||||||
|
;27f200
|
||||||
|
|
||||||
|
|||||||
+38
-6
@@ -24,14 +24,34 @@ NotLinkDoor2:
|
|||||||
|
|
||||||
|
|
||||||
; Staircase routine
|
; Staircase routine
|
||||||
org $01c3d4 ;(PC: c3d4)
|
org $01c3d4 ; <- c3d4 - Bank01.asm : 9762-4 (Dungeon_DetectStaircase-> STA $A0 : LDA $063D, X)
|
||||||
jsl RecordStairType : nop
|
jsl RecordStairType : nop
|
||||||
org $02a1e7 ;(PC: 121e7)
|
org $02a1e7 ;(PC: 121e7)
|
||||||
jsl SpiralWarp
|
jsl SpiralWarp
|
||||||
|
|
||||||
|
org $0291b3 ; <- Bank02.asm : 3303 (LDA $0462 : AND.b #$04)
|
||||||
|
jsl SpiralPriorityHack : nop
|
||||||
|
org $0290f9 ; <- Bank02.asm : 3188 (LDA $0462 : AND.b #$04)
|
||||||
|
jsl SpiralPriorityHack : nop
|
||||||
|
|
||||||
|
org $029369 ; <- 11369 - Bank02.asm : 3610 (STX $0464 : STY $012E)
|
||||||
|
jsl StraightStairsAdj : nop #2
|
||||||
|
org $029383 ; <- 11384 - Bank02.asm : 3629 (.walkingDownStaircase-> ADD $20 : STA $20)
|
||||||
|
jsl StraightStairsFix : nop
|
||||||
|
org $0293aa ; <- 113aa - Bank02.asm : 3653 (ADD $20 : STA $20)
|
||||||
|
jsl StraightStairsFix : nop
|
||||||
|
org $0293d1 ; <- 113d1 - Bank02.asm : 3683 (ADD $20 : STA $20 BRANCH_IOTA)
|
||||||
|
jsl StraightStairsFix : nop
|
||||||
|
org $029396 ; <- 11396 - Bank02.asm : 3641 (LDA $01C322, X)
|
||||||
|
jsl StraightStairLayerFix
|
||||||
|
org $02c06d ; <- Bank02.asm : 9874 (LDX $0418, CMP.b #$02)
|
||||||
|
jsl DoorToStraight : nop
|
||||||
|
org $02941a ; <- Bank02.asm : 3748 module 7.12.11 (LDA $0464 : BNE BRANCH_$11513 : INC $B0 : RTS)
|
||||||
|
jsl StraightStairsTrapDoor : rts
|
||||||
|
|
||||||
|
|
||||||
; Graphics fix
|
; Graphics fix
|
||||||
org $02895d
|
org $02895d ; Bank 02 line 1812 (JSL Dungeon_LoadRoom : JSL Dungeon_InitStarTileChr : JSL $00D6F9 : INC $B0)
|
||||||
Splicer:
|
Splicer:
|
||||||
jsl GfxFixer
|
jsl GfxFixer
|
||||||
lda $b1 : beq .done
|
lda $b1 : beq .done
|
||||||
@@ -39,14 +59,20 @@ rts
|
|||||||
nop #5
|
nop #5
|
||||||
.done
|
.done
|
||||||
|
|
||||||
org $00fda4
|
org $00d377 ;Bank 00 line 3185
|
||||||
|
DecompDungAnimatedTiles:
|
||||||
|
org $00fda4 ;Bank 00 line 8882
|
||||||
Dungeon_InitStarTileCh:
|
Dungeon_InitStarTileCh:
|
||||||
org $00d6ae ;(PC: 56ae)
|
org $00d6ae ;(PC: 56ae)
|
||||||
LoadTransAuxGfx:
|
LoadTransAuxGfx:
|
||||||
|
org $00d739 ;
|
||||||
|
LoadTransAuxGfx_Alt:
|
||||||
org $00df5a ;(PC: 5f5a)
|
org $00df5a ;(PC: 5f5a)
|
||||||
PrepTransAuxGfx:
|
PrepTransAuxGfx:
|
||||||
org $0ffd65 ;(PC: 07fd65)
|
org $0ffd65 ;(PC: 07fd65)
|
||||||
Dungeon_LoadCustomTileAttr:
|
Dungeon_LoadCustomTileAttr:
|
||||||
|
org $01feb0
|
||||||
|
Dungeon_ApproachFixedColor:
|
||||||
;org $01fec1
|
;org $01fec1
|
||||||
;Dungeon_ApproachFixedColor_variable:
|
;Dungeon_ApproachFixedColor_variable:
|
||||||
;org $a0f972 ; Rando version
|
;org $a0f972 ; Rando version
|
||||||
@@ -75,9 +101,9 @@ nop : stz $0dd0, X : rts
|
|||||||
.not_in_ganons_tower
|
.not_in_ganons_tower
|
||||||
|
|
||||||
|
|
||||||
org $2081f2
|
org $208206
|
||||||
jsl MirrorCheckOverride2
|
jsl MirrorCheckOverride2
|
||||||
org $20825c
|
org $208270
|
||||||
jsl MirrorCheckOverride2
|
jsl MirrorCheckOverride2
|
||||||
org $07a955 ; <- Bank07.asm : around 6564 (JP is a bit different) (STZ $05FC : STZ $05FD)
|
org $07a955 ; <- Bank07.asm : around 6564 (JP is a bit different) (STZ $05FC : STZ $05FD)
|
||||||
jsl BlockEraseFix
|
jsl BlockEraseFix
|
||||||
@@ -94,9 +120,15 @@ jsl GuruguruFix : bra .next
|
|||||||
nop #3
|
nop #3
|
||||||
.next
|
.next
|
||||||
|
|
||||||
|
org $028fc9
|
||||||
|
nop #2 : jsl BlindAtticFix
|
||||||
|
|
||||||
; also rando's hooks.asm line 1360
|
; also rando's hooks.asm line 1360
|
||||||
org $a0ee11 ; <- 6FC4C - headsup_display.asm : 836 (LDA $7EF36E : AND.w #$00FF : ADD.w #$0007 : AND.w #$FFF8 : TAX)
|
; 106e4e -> goes to a0ee4e
|
||||||
|
org $a0ee8a ; <- 6FC4C - headsup_display.asm : 836 (LDA $7EF36E : AND.w #$00FF : ADD.w #$0007 : AND.w #$FFF8 : TAX)
|
||||||
jsl DrHudOverride
|
jsl DrHudOverride
|
||||||
|
org $0ded04 ; <- rando's hooks.asm line 2192 - 6ED04 - equipment.asm : 1963 (REP #$30)
|
||||||
|
jsl DrHudDungeonItemsAdditions
|
||||||
org $098638 ; rando's hooks.asm line 2192
|
org $098638 ; rando's hooks.asm line 2192
|
||||||
jsl CountChestKeys
|
jsl CountChestKeys
|
||||||
org $06D192 ; rando's hooks.asm line 457
|
org $06D192 ; rando's hooks.asm line 457
|
||||||
|
|||||||
+105
-103
@@ -1,6 +1,6 @@
|
|||||||
HorzEdge:
|
HorzEdge:
|
||||||
cpy #$ff : beq +
|
cpy #$ff : beq +
|
||||||
jsr DetectWestEdge : bra ++
|
jsr DetectWestEdge : ldy #$02 : bra ++
|
||||||
+ jsr DetectEastEdge
|
+ jsr DetectEastEdge
|
||||||
++ cmp #$ff : beq +
|
++ cmp #$ff : beq +
|
||||||
sta $00 : asl : !add $00 : tax
|
sta $00 : asl : !add $00 : tax
|
||||||
@@ -28,154 +28,156 @@ VertEdge:
|
|||||||
|
|
||||||
LoadEdgeRoomHorz:
|
LoadEdgeRoomHorz:
|
||||||
lda $03 : sta $a0
|
lda $03 : sta $a0
|
||||||
sty $09
|
sty $06
|
||||||
and.b #$0f : asl a : !sub $23 : !add $09 : sta $02
|
and.b #$0f : asl a : !sub $23 : !add $06 : sta $02
|
||||||
ldy #$00 : jsr ShiftVariablesMainDir
|
ldy #$00 : jsr ShiftVariablesMainDir
|
||||||
lda $a0 : and.b #$F0 : lsr #3 : sta $0603 : inc : sta $0607
|
|
||||||
|
|
||||||
|
lda $04 : and #$80 : bne .edge
|
||||||
|
lda $04 : sta $01 ; load up flags in $01
|
||||||
|
jsr PrepScrollToNormal
|
||||||
|
bra .scroll
|
||||||
|
|
||||||
lda $aa : asl : tax ; current quad as 0/4
|
.edge
|
||||||
lda $04 : and #$40 : bne +
|
lda $04 : and #$10 : beq +
|
||||||
lda $603 : sta $00 : stz $01 : bra ++
|
lda #$01
|
||||||
+ lda $607 : sta $00 : lda #$02 : sta $01
|
+ sta $ee ; layer stuff
|
||||||
++ ; $01 now contains 0 or 2
|
|
||||||
lda $00 : sta $21 : sta $0601 : sta $0605
|
|
||||||
lda $01 : sta $aa : lsr : sta $01 : stz $00
|
|
||||||
lda $0a : sta $20
|
|
||||||
|
|
||||||
stz $0e
|
jsr MathHorz
|
||||||
rep #$30
|
|
||||||
lda $e8 : and #$01ff : sta $02
|
|
||||||
lda $0a : and #$00ff : !add $00 : sta $00
|
|
||||||
|
|
||||||
cmp #$006c : !bge +
|
.scroll
|
||||||
lda #$0077 : bra ++
|
jsr ScrollY
|
||||||
+ cmp #$017c : !blt +
|
|
||||||
lda #$0187 : bra ++
|
|
||||||
+ !add #$000b
|
|
||||||
++ sta $0618 : inc #2 : sta $061a
|
|
||||||
|
|
||||||
lda $00 : cmp #$0078 : !bge +
|
|
||||||
lda #$0000 : bra ++
|
|
||||||
+ cmp #$0178 : !blt +
|
|
||||||
lda #$0100 : bra ++
|
|
||||||
+ !sub #$0078
|
|
||||||
++ sta $00
|
|
||||||
|
|
||||||
; figures out scroll amt
|
|
||||||
cmp $02 : bne +
|
|
||||||
lda #$0000 : bra .done
|
|
||||||
+ !blt +
|
|
||||||
!sub $02 : inc $0e : bra .done
|
|
||||||
+ lda $02 : !sub $00
|
|
||||||
|
|
||||||
.done sta $ab : sep #$30
|
|
||||||
lda $0e : asl : ora $ac : sta $ac
|
|
||||||
lda $0603, x : sta $e9
|
|
||||||
|
|
||||||
lda $04 : and #$80 : lsr #4 : sta $ee ; layer stuff
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
LoadEdgeRoomVert:
|
LoadEdgeRoomVert:
|
||||||
lda $03 : sta $a0
|
lda $03 : sta $a0
|
||||||
sty $09
|
sty $06
|
||||||
and.b #$f0 : lsr #3 : !sub $21 : !add $09 : sta $02
|
and.b #$f0 : lsr #3 : !sub $21 : !add $06 : sta $02
|
||||||
ldy #$01 : jsr ShiftVariablesMainDir
|
ldy #$01 : jsr ShiftVariablesMainDir
|
||||||
lda $a0 : and.b #$0f : asl : sta $060b : inc : sta $060f
|
|
||||||
|
|
||||||
lda $a9 : asl #2 : tax ; current quad as 0/4
|
lda $04 : and #$80 : bne .edge
|
||||||
lda $04 : and #$20 : bne +
|
lda $04 : sta $01 ; load up flags in $01
|
||||||
lda $60b : sta $00 : stz $01 : bra ++
|
jsr PrepScrollToNormal
|
||||||
+ lda $60f : sta $00 : lda #$01 : sta $01
|
bra .scroll
|
||||||
++ ; $01 now contains 0 or 1
|
|
||||||
lda $00 : sta $23 : sta $0609 : sta $060d
|
|
||||||
lda $01 : sta $a9 : stz $00 ; setup for 16 bit ops
|
|
||||||
lda $0a : sta $22
|
|
||||||
|
|
||||||
stz $0e ; pos/neg indicator
|
.edge
|
||||||
rep #$30
|
lda $04 : and #$10 : beq +
|
||||||
lda $e2 : and #$01ff : sta $02
|
lda #$01
|
||||||
lda $0a : and #$00ff : !add $00 : sta $00
|
+ sta $ee ; layer stuff
|
||||||
|
|
||||||
cmp #$0078 : !bge +
|
jsr MathVert
|
||||||
lda #$007f : bra ++
|
lda $03
|
||||||
+ cmp #$0178 : !blt +
|
|
||||||
lda #$017f : bra ++
|
|
||||||
+ !add #$0007
|
|
||||||
++ sta $061c : inc #2 : sta $061e
|
|
||||||
|
|
||||||
lda $00 : cmp #$0078 : !bge +
|
.scroll
|
||||||
lda #$0000 : bra ++
|
jsr ScrollX
|
||||||
+ cmp #$0178 : !blt +
|
|
||||||
lda #$0100 : bra ++
|
|
||||||
+ !sub #$0078
|
|
||||||
++ sta $00
|
|
||||||
|
|
||||||
; figures out scroll amt
|
|
||||||
cmp $02 : bne +
|
|
||||||
lda #$0000 : bra .done
|
|
||||||
+ !blt +
|
|
||||||
!sub $02 : inc $0e : bra .done
|
|
||||||
+ lda $02 : !sub $00
|
|
||||||
|
|
||||||
.done sta $ab : sep #$30
|
|
||||||
lda $0e : asl : ora $ac : sta $ac
|
|
||||||
lda $060b, x : sta $e3
|
|
||||||
|
|
||||||
lda $04 : and #$10 : lsr #4 : sta $ee ; layer stuff
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
|
||||||
|
MathHorz:
|
||||||
|
jsr MathStart : lda $20
|
||||||
|
jsr MathMid : and #$0040
|
||||||
|
jsr MathEnd
|
||||||
|
rts
|
||||||
|
|
||||||
|
MathVert:
|
||||||
|
jsr MathStart : lda $22
|
||||||
|
jsr MathMid : and #$0020
|
||||||
|
jsr MathEnd
|
||||||
|
rts
|
||||||
|
|
||||||
|
MathStart:
|
||||||
|
rep #$30
|
||||||
|
lda $08 : and #$00ff : sta $00
|
||||||
|
rts
|
||||||
|
|
||||||
|
MathMid:
|
||||||
|
and #$01ff : !sub $00 : and #$00ff : sta $00
|
||||||
|
; nothing should be bigger than $a0 at this point
|
||||||
|
|
||||||
|
lda $05 : and #$00f0 : lsr #4 : tax
|
||||||
|
lda MultDivInfo, x : and #$00ff : tay
|
||||||
|
lda $00 : jsr MultiplyByY : sta $02
|
||||||
|
|
||||||
|
lda $07 : and #$00ff : jsr MultiplyByY : tax
|
||||||
|
|
||||||
|
lda $05 : and #$000f : tay
|
||||||
|
lda MultDivInfo, y : and #$00ff : tay
|
||||||
|
lda $02 : jsr DivideByY : sta $00
|
||||||
|
lda $0c : and #$00ff : sta $02
|
||||||
|
lda $04
|
||||||
|
rts
|
||||||
|
|
||||||
|
MathEnd:
|
||||||
|
beq +
|
||||||
|
lda #$0100
|
||||||
|
+ !add $02 : !add $00
|
||||||
|
sta $04
|
||||||
|
sep #$30
|
||||||
|
rts
|
||||||
|
|
||||||
|
; don't need midpoint of edge Link is leaving (formerly in $06 - used by dir indicator)
|
||||||
|
; don't need width of edge Link is going to (currently in $0b)
|
||||||
LoadNorthData:
|
LoadNorthData:
|
||||||
lda NorthEdgeInfo, x : sta $06 ; not needed I think
|
lda NorthOpenEdge, x : sta $03 : inx ; target room
|
||||||
lda NorthOpenEdge, x : sta $03 : inx
|
lda NorthEdgeInfo, x : sta $07 ; needed for maths - (divide by 2 anyway)
|
||||||
lda NorthEdgeInfo, x : sta $07 ;probably needed for maths - unsure
|
lda NorthOpenEdge, x : sta $04 : inx ; bit field
|
||||||
lda NorthOpenEdge, x : sta $04 : inx
|
|
||||||
lda NorthEdgeInfo, x : sta $08 ; needed for maths
|
lda NorthEdgeInfo, x : sta $08 ; needed for maths
|
||||||
lda NorthOpenEdge, x : sta $05
|
lda NorthOpenEdge, x : sta $05 ; ratio
|
||||||
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
|
lda $04 : jsr LoadSouthMidpoint : inx ; needed now, and for nrml transition
|
||||||
lda SouthEdgeInfo, x : sta $0a : inx ; needed now, and for nrml transition
|
lda SouthEdgeInfo, x : sta $0b : inx ; probably not needed todo: remove
|
||||||
lda SouthEdgeInfo, x : sta $0b : inx ; probably not needed - unsure
|
|
||||||
lda SouthEdgeInfo, x : sta $0c ; needed for maths
|
lda SouthEdgeInfo, x : sta $0c ; needed for maths
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
LoadSouthMidpoint:
|
||||||
|
and #$0f : sta $00 : asl : !add $00 : tax
|
||||||
|
lda SouthEdgeInfo, x : sta $0a ; needed now, and for nrml transition
|
||||||
|
rts
|
||||||
|
|
||||||
LoadSouthData:
|
LoadSouthData:
|
||||||
lda SouthEdgeInfo, x : sta $06
|
|
||||||
lda SouthOpenEdge, x : sta $03 : inx
|
lda SouthOpenEdge, x : sta $03 : inx
|
||||||
lda SouthEdgeInfo, x : sta $07
|
lda SouthEdgeInfo, x : sta $07
|
||||||
lda SouthOpenEdge, x : sta $04 : inx
|
lda SouthOpenEdge, x : sta $04 : inx
|
||||||
lda SouthEdgeInfo, x : sta $08
|
lda SouthEdgeInfo, x : sta $08
|
||||||
lda SouthOpenEdge, x : sta $05
|
lda SouthOpenEdge, x : sta $05
|
||||||
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
|
lda $04 : jsr LoadNorthMidpoint : inx
|
||||||
lda NorthEdgeInfo, x : sta $0a : inx
|
|
||||||
lda NorthEdgeInfo, x : sta $0b : inx
|
lda NorthEdgeInfo, x : sta $0b : inx
|
||||||
lda NorthEdgeInfo, x : sta $0c
|
lda NorthEdgeInfo, x : sta $0c
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
LoadNorthMidpoint:
|
||||||
|
and #$0f : sta $00 : asl : !add $00 : tax
|
||||||
|
lda NorthEdgeInfo, x : sta $0a ; needed now, and for nrml transition
|
||||||
|
rts
|
||||||
|
|
||||||
LoadWestData:
|
LoadWestData:
|
||||||
lda WestEdgeInfo, x : sta $06
|
|
||||||
lda WestOpenEdge, x : sta $03 : inx
|
lda WestOpenEdge, x : sta $03 : inx
|
||||||
lda WestEdgeInfo, x : sta $07
|
lda WestEdgeInfo, x : sta $07
|
||||||
lda WestOpenEdge, x : sta $04 : inx
|
lda WestOpenEdge, x : sta $04 : inx
|
||||||
lda WestEdgeInfo, x : sta $08
|
lda WestEdgeInfo, x : sta $08
|
||||||
lda WestOpenEdge, x : sta $05
|
lda WestOpenEdge, x : sta $05
|
||||||
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
|
lda $04 : jsr LoadEastMidpoint : inx
|
||||||
lda EastEdgeInfo, x : sta $0a : inx
|
|
||||||
lda EastEdgeInfo, x : sta $0b : inx
|
lda EastEdgeInfo, x : sta $0b : inx
|
||||||
lda EastEdgeInfo, x : sta $0c
|
lda EastEdgeInfo, x : sta $0c
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
LoadEastMidpoint:
|
||||||
|
and #$0f : sta $00 : asl : !add $00 : tax
|
||||||
|
lda EastEdgeInfo, x : sta $0a ; needed now, and for nrml transition
|
||||||
|
rts
|
||||||
|
|
||||||
LoadEastData:
|
LoadEastData:
|
||||||
lda EastEdgeInfo, x : sta $06
|
|
||||||
lda EastOpenEdge, x : sta $03 : inx
|
lda EastOpenEdge, x : sta $03 : inx
|
||||||
lda EastEdgeInfo, x : sta $07
|
lda EastEdgeInfo, x : sta $07
|
||||||
lda EastOpenEdge, x : sta $04 : inx
|
lda EastOpenEdge, x : sta $04 : inx
|
||||||
lda EastEdgeInfo, x : sta $08
|
lda EastEdgeInfo, x : sta $08
|
||||||
lda EastOpenEdge, x : sta $05
|
lda EastOpenEdge, x : sta $05
|
||||||
lda $04 : and #$0f : sta $00 : asl : !add $00 : tax
|
lda $04 : jsr LoadWestMidpoint : inx
|
||||||
lda WestEdgeInfo, x : sta $0a : inx
|
|
||||||
lda WestEdgeInfo, x : sta $0b : inx
|
lda WestEdgeInfo, x : sta $0b : inx
|
||||||
lda WestEdgeInfo, x : sta $0c
|
lda WestEdgeInfo, x : sta $0c
|
||||||
|
|
||||||
|
|
||||||
|
LoadWestMidpoint:
|
||||||
|
and #$0f : sta $00 : asl : !add $00 : tax
|
||||||
|
lda WestEdgeInfo, x : sta $0a ; needed now, and for nrml transition
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
|
||||||
@@ -258,8 +260,8 @@ DetectWestEdge:
|
|||||||
ldx #$05 : bra .end
|
ldx #$05 : bra .end
|
||||||
+ cmp #$cc : bne +
|
+ cmp #$cc : bne +
|
||||||
lda $aa : beq ++
|
lda $aa : beq ++
|
||||||
ldx #$07 : bra .end
|
ldx #$06 : bra .end
|
||||||
++ ldx #$06 : bra .end
|
++ ldx #$07 : bra .end
|
||||||
+ cmp #$dc : bne .end
|
+ cmp #$dc : bne .end
|
||||||
ldx #$08
|
ldx #$08
|
||||||
.end txa : rts
|
.end txa : rts
|
||||||
@@ -281,8 +283,8 @@ DetectEastEdge:
|
|||||||
ldx #$05 : bra .end
|
ldx #$05 : bra .end
|
||||||
+ cmp #$cb : bne +
|
+ cmp #$cb : bne +
|
||||||
lda $aa : beq ++
|
lda $aa : beq ++
|
||||||
ldx #$07 : bra .end
|
ldx #$06 : bra .end
|
||||||
++ ldx #$06 : bra .end
|
++ ldx #$07 : bra .end
|
||||||
+ cmp #$db : bne .end
|
+ cmp #$db : bne .end
|
||||||
ldx #$08
|
ldx #$08
|
||||||
.end txa : rts
|
.end txa : rts
|
||||||
|
|||||||
+14
-1
@@ -1,11 +1,24 @@
|
|||||||
GfxFixer:
|
GfxFixer:
|
||||||
{
|
{
|
||||||
lda $b1 : bne .stage2
|
lda.l DRMode : bne +
|
||||||
|
jsl LoadRoomHook ;this is the code we overwrote
|
||||||
|
jsl Dungeon_InitStarTileCh
|
||||||
|
jsl LoadTransAuxGfx_Alt
|
||||||
|
inc $b0
|
||||||
|
rtl
|
||||||
|
+ lda $b1 : bne .stage2
|
||||||
jsl LoadRoomHook ; this is the rando version - let's only call this guy once - may fix star tiles and slower loads
|
jsl LoadRoomHook ; this is the rando version - let's only call this guy once - may fix star tiles and slower loads
|
||||||
jsl Dungeon_InitStarTileCh
|
jsl Dungeon_InitStarTileCh
|
||||||
jsl LoadTransAuxGfx
|
jsl LoadTransAuxGfx
|
||||||
jsl Dungeon_LoadCustomTileAttr
|
jsl Dungeon_LoadCustomTileAttr
|
||||||
jsl PrepTransAuxGfx
|
jsl PrepTransAuxGfx
|
||||||
|
lda.l DRMode : cmp #$02 : bne + ; only do this in crossed mode
|
||||||
|
ldx $a0 : lda.l TilesetTable, x
|
||||||
|
cmp $0aa1 : beq + ; already eq no need to decomp
|
||||||
|
sta $0aa1
|
||||||
|
tax : lda $02802e, x : tay
|
||||||
|
jsl DecompDungAnimatedTiles
|
||||||
|
+
|
||||||
lda #$09 : sta $17 : sta $0710
|
lda #$09 : sta $17 : sta $0710
|
||||||
jsl Palette_SpriteAux3
|
jsl Palette_SpriteAux3
|
||||||
jsl Palette_SpriteAux2
|
jsl Palette_SpriteAux2
|
||||||
|
|||||||
+129
-23
@@ -7,51 +7,124 @@ DrHudOverride:
|
|||||||
|
|
||||||
HudAdditions:
|
HudAdditions:
|
||||||
{
|
{
|
||||||
|
lda.l DRFlags : and #$0008 : beq ++
|
||||||
|
lda $7EF423 : and #$00ff
|
||||||
|
jsr HudHexToDec4DigitCopy
|
||||||
|
LDX.b $05 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+10 ; draw 100's digit
|
||||||
|
LDX.b $06 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+12 ; draw 10's digit
|
||||||
|
LDX.b $07 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+14 ; draw 1's digit
|
||||||
|
|
||||||
|
lda $7ef29b : and #$0020 : beq +
|
||||||
|
lda #$207f : bra .drawthing
|
||||||
|
+ lda #$345e
|
||||||
|
.drawthing STA !GOAL_DRAW_ADDRESS+16 ; castle gate indicator
|
||||||
|
++
|
||||||
|
|
||||||
ldx $040c : cpx #$ff : bne + : rts : +
|
ldx $040c : cpx #$ff : bne + : rts : +
|
||||||
lda DRMode : bne + : rts : +
|
lda.l DRMode : bne + : rts : +
|
||||||
phb : phk : plb
|
phb : phk : plb
|
||||||
lda $7ef364 : and.l $0098c0, x : beq +
|
lda $7ef364 : and.l $0098c0, x : beq +
|
||||||
lda CompassBossIndicator, x : and #$00ff : cmp $a0 : bne +
|
lda.w CompassBossIndicator, x : and #$00ff : cmp $a0 : bne +
|
||||||
lda $1a : and #$0010 : beq +
|
lda $1a : and #$0010 : beq +
|
||||||
lda #$345e : sta $7ec790 : bra .next
|
lda #$345e : sta $7ec790 : bra .next
|
||||||
+ lda #$207f : sta $7ec790
|
+ lda #$207f : sta $7ec790
|
||||||
.next lda DRMode : and #$0002 : bne + : plb : rts : +
|
.next lda.w DRMode : and #$0002 : bne + : plb : rts : +
|
||||||
lda $7ef36d : and #$00ff : beq +
|
lda $7ef36d : and #$00ff : beq +
|
||||||
lda DungeonReminderTable, x : bra .reminder
|
lda.w DungeonReminderTable, x : bra .reminder
|
||||||
+ lda #$207f
|
+ lda #$207f
|
||||||
.reminder sta $7ec702
|
.reminder sta $7ec702
|
||||||
+ lda DRFlags : and #$0004 : beq .restore
|
+ lda.w DRFlags : and #$0004 : beq .restore
|
||||||
lda $7ef368 : and.l $0098c0, x : beq .restore
|
lda $7ef368 : and.l $0098c0, x : beq .restore
|
||||||
|
|
||||||
lda #$2811 : sta $7ec740
|
; lda #$2811 : sta $7ec740
|
||||||
lda $7ef366 : and.l $0098c0, x : bne .check
|
; lda $7ef366 : and.l $0098c0, x : bne .check
|
||||||
lda BigKeyStatus, x : bne + ; change this, if bk status changes to one byte
|
; lda.w BigKeyStatus, x : bne + ; change this, if bk status changes to one byte
|
||||||
lda #$2574 : bra ++
|
; lda #$2574 : bra ++
|
||||||
+ cmp #$0002 : bne +
|
; + cmp #$0002 : bne +
|
||||||
lda #$2420 : bra ++
|
; lda #$2420 : bra ++
|
||||||
+ lda #$207f : bra ++
|
; + lda #$207f : bra ++
|
||||||
.check lda #$2826
|
; .check lda #$2826
|
||||||
++ sta $7ec742
|
; ++ sta $7ec742
|
||||||
txa : lsr : tax
|
txa : lsr : tax
|
||||||
|
|
||||||
lda $7ef4e0, x : jsr ConvertToDisplay : sta $7ec7a2
|
lda $7ef4e0, x : jsr ConvertToDisplay : sta $7ec7a2
|
||||||
lda #$2830 : sta $7ec7a4
|
lda #$2830 : sta $7ec7a4
|
||||||
lda ChestKeys, x : jsr ConvertToDisplay : sta $7ec7a6
|
lda.w ChestKeys, x : jsr ConvertToDisplay : sta $7ec7a6
|
||||||
|
|
||||||
lda #$2871 : sta $7ec780
|
; lda #$2871 : sta $7ec780
|
||||||
lda TotalKeys, x
|
; lda.w TotalKeys, x
|
||||||
sep #$20 : !sub $7ef4b0, x : rep #$20
|
; sep #$20 : !sub $7ef4b0, x : rep #$20 ; todo 4b0 no longer in use
|
||||||
jsr ConvertToDisplay : sta $7ec782
|
; jsr ConvertToDisplay : sta $7ec782
|
||||||
|
|
||||||
.restore
|
.restore
|
||||||
plb : rts
|
plb : rts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HudOffsets:
|
||||||
|
; none hc east desert aga swamp pod mire skull ice hera tt tr gt
|
||||||
|
dw $fffe, $0000, $0006, $0008, $0002, $0010, $000e, $0018, $0012, $0016, $000a, $0014, $001a, $001e
|
||||||
|
|
||||||
|
DrHudDungeonItemsAdditions:
|
||||||
|
{
|
||||||
|
jsl DrawHUDDungeonItems
|
||||||
|
lda.l HUDDungeonItems : and #$ff : bne + : rtl : +
|
||||||
|
lda.l DRMode : cmp #$02 : beq + : rtl : +
|
||||||
|
|
||||||
|
phx : phy : php
|
||||||
|
rep #$30
|
||||||
|
|
||||||
|
lda !HUD_FLAG : and.w #$0020 : beq + : bra ++ : +
|
||||||
|
lda HUDDungeonItems : and.w #$0003 : bne + : bra ++ : +
|
||||||
|
lda.w #$2810 : sta $1684 ; small keys icon
|
||||||
|
lda.w #$2811 : sta $16c4 ; big key icon
|
||||||
|
lda.w #$2810 : sta $1704 ; small keys icon
|
||||||
|
ldx #$0002
|
||||||
|
- lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||||
|
lda.l HudOffsets, x : tay
|
||||||
|
jsr BkStatus : sta $16C6, y ; big key status
|
||||||
|
phx
|
||||||
|
txa : lsr : tax
|
||||||
|
lda.l ChestKeys, x : jsr ConvertToDisplay2 : sta $1706, y ; small key totals
|
||||||
|
plx
|
||||||
|
+ inx #2 : cpx #$001b : bcc -
|
||||||
|
++
|
||||||
|
lda !HUD_FLAG : and.w #$0020 : bne + : bra ++ : +
|
||||||
|
lda HUDDungeonItems : and.w #$000c : bne + : bra ++ : +
|
||||||
|
lda.w #$24f5 : sta $1704 ; blank
|
||||||
|
ldx #$0002
|
||||||
|
- lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||||
|
lda.l HudOffsets, x : tay
|
||||||
|
phx ; total chest counts
|
||||||
|
txa : lsr : tax
|
||||||
|
lda.l TotalLocationsLow, x : jsr ConvertToDisplay2 : sta $1706, y
|
||||||
|
lda.l TotalLocationsHigh, x : jsr ConvertToDisplay2 : sta $16c6, y
|
||||||
|
plx
|
||||||
|
+
|
||||||
|
+ inx #2 : cpx #$001b : bcc -
|
||||||
|
++
|
||||||
|
plp : ply : plx : rtl
|
||||||
|
}
|
||||||
|
|
||||||
|
BkStatus:
|
||||||
|
lda $7ef366 : and.l $0098c0, x : bne +++ ; has the bk already
|
||||||
|
lda.l BigKeyStatus, x : bne ++
|
||||||
|
lda #$2482 : rts ; 0/O for no BK
|
||||||
|
++ cmp #$0002 : bne +
|
||||||
|
lda #$2420 : rts ; symbol for BnC
|
||||||
|
+ lda #$24f5 : rts ; black otherwise
|
||||||
|
+++ lda #$2826 : rts ; check mark
|
||||||
|
|
||||||
ConvertToDisplay:
|
ConvertToDisplay:
|
||||||
and #$00ff : cmp #$000a : !blt +
|
and.w #$00ff : cmp #$000a : !blt +
|
||||||
!add #$2553 : rts
|
!add #$2553 : rts
|
||||||
+ !add #$2490 : rts
|
+ !add #$2490 : rts
|
||||||
|
|
||||||
|
ConvertToDisplay2:
|
||||||
|
and.w #$00ff : beq ++
|
||||||
|
cmp #$000a : !blt +
|
||||||
|
!add #$2553 : rts
|
||||||
|
+ !add #$2816 : rts
|
||||||
|
++ lda #$2483 : rts ; 0/O for 0 or placeholder digit
|
||||||
|
|
||||||
CountChestKeys:
|
CountChestKeys:
|
||||||
jsl ItemDowngradeFix
|
jsl ItemDowngradeFix
|
||||||
@@ -92,6 +165,39 @@ CountBonkItem:
|
|||||||
lda.l BonkKey_GTower
|
lda.l BonkKey_GTower
|
||||||
bra ++
|
bra ++
|
||||||
+ lda.b #$24 ; default to small key
|
+ lda.b #$24 ; default to small key
|
||||||
++
|
++ cmp #$24 : bne +
|
||||||
phy : tay : jsr CountChest : ply
|
phy : tay : jsr CountChest : ply
|
||||||
rtl
|
+ rtl
|
||||||
|
|
||||||
|
;================================================================================
|
||||||
|
; 16-bit A, 8-bit X
|
||||||
|
; in: A(b) - Byte to Convert
|
||||||
|
; out: $04 - $07 (high - low)
|
||||||
|
;================================================================================
|
||||||
|
HudHexToDec4DigitCopy:
|
||||||
|
LDY.b #$90
|
||||||
|
-
|
||||||
|
CMP.w #1000 : !BLT +
|
||||||
|
INY
|
||||||
|
SBC.w #1000 : BRA -
|
||||||
|
+
|
||||||
|
STY $04 : LDY #$90 ; Store 1000s digit & reset Y
|
||||||
|
-
|
||||||
|
CMP.w #100 : !BLT +
|
||||||
|
INY
|
||||||
|
SBC.w #100 : BRA -
|
||||||
|
+
|
||||||
|
STY $05 : LDY #$90 ; Store 100s digit & reset Y
|
||||||
|
-
|
||||||
|
CMP.w #10 : !BLT +
|
||||||
|
INY
|
||||||
|
SBC.w #10 : BRA -
|
||||||
|
+
|
||||||
|
STY $06 : LDY #$90 ; Store 10s digit & reset Y
|
||||||
|
CMP.w #1 : !BLT +
|
||||||
|
-
|
||||||
|
INY
|
||||||
|
DEC : BNE -
|
||||||
|
+
|
||||||
|
STY $07 ; Store 1s digit
|
||||||
|
RTS
|
||||||
+3
-3
@@ -12,7 +12,7 @@
|
|||||||
CheckIfDoorsOpen: {
|
CheckIfDoorsOpen: {
|
||||||
jsr TrapDoorFixer ; see normal.asm
|
jsr TrapDoorFixer ; see normal.asm
|
||||||
; note we are 16bit mode right now
|
; note we are 16bit mode right now
|
||||||
lda DRMode : beq +
|
lda.l DRMode : beq +
|
||||||
lda $040c : cmp #$00ff : bne .gtg
|
lda $040c : cmp #$00ff : bne .gtg
|
||||||
+ lda $a0 : dec : tax : and #$000f ; hijacked code
|
+ lda $a0 : dec : tax : and #$000f ; hijacked code
|
||||||
sec : rtl ; set carry to indicate normal behavior
|
sec : rtl ; set carry to indicate normal behavior
|
||||||
@@ -22,9 +22,9 @@ CheckIfDoorsOpen: {
|
|||||||
stx $00 : ldy #$0000
|
stx $00 : ldy #$0000
|
||||||
.nextDoor
|
.nextDoor
|
||||||
lda $a0 : asl : tax
|
lda $a0 : asl : tax
|
||||||
lda KeyDoorOffset, x : beq .skipDoor
|
lda.w KeyDoorOffset, x : beq .skipDoor
|
||||||
asl : sty $05 : !add $05 : tax
|
asl : sty $05 : !add $05 : tax
|
||||||
lda PairedDoorTable, x : beq .skipDoor
|
lda.w PairedDoorTable, x : beq .skipDoor
|
||||||
sta $02 : and #$00ff : asl a : tax
|
sta $02 : and #$00ff : asl a : tax
|
||||||
lda $02 : and #$ff00 : sta $03
|
lda $02 : and #$ff00 : sta $03
|
||||||
lda $7ef000, x : and #$f000 : and $03 : beq .skipDoor
|
lda $7ef000, x : and #$f000 : and $03 : beq .skipDoor
|
||||||
|
|||||||
+12
-7
@@ -15,15 +15,19 @@ cpy #$0003 : bne ++
|
|||||||
++ asl : sta $00 : tya : lsr : tay : lda $00 : bra .loop
|
++ asl : sta $00 : tya : lsr : tay : lda $00 : bra .loop
|
||||||
.done rts
|
.done rts
|
||||||
|
|
||||||
;todo -- width in X?
|
;Divisor in Y. Width of division is in X for rounding toward middle
|
||||||
DivideByY:
|
DivideByY:
|
||||||
.loop cpy #$0001 : beq .done
|
.loop
|
||||||
|
cpy #$0000 : beq .done
|
||||||
|
cpy #$0001 : beq .done
|
||||||
cpy #$0003 : bne ++
|
cpy #$0003 : bne ++
|
||||||
jsr DivideBy3 : bra .done
|
jsr DivideBy3 : bra .done
|
||||||
++ cpy #$0005 : bne ++
|
++ cpy #$0005 : bne ++
|
||||||
jsr DivideBy5 : bra .done
|
jsr DivideBy5 : bra .done
|
||||||
; todo -- alter - width
|
++ jsr DivideBy2 : sta $00
|
||||||
++ tyx : jsr DivideBy2 : sta $00 : tya : lsr : tay : lda $00 : bra .loop
|
tya : lsr : tay
|
||||||
|
txa : lsr : tax
|
||||||
|
lda $00 : bra .loop
|
||||||
.done rts
|
.done rts
|
||||||
|
|
||||||
MultiBy3:
|
MultiBy3:
|
||||||
@@ -34,12 +38,13 @@ MultiBy5:
|
|||||||
sta $00 : asl #2 : !add $00
|
sta $00 : asl #2 : !add $00
|
||||||
rts
|
rts
|
||||||
|
|
||||||
;width of divison in x
|
;width of divison in x: rounds toward X/2
|
||||||
DivideBy2:
|
DivideBy2:
|
||||||
sta $00
|
sta $00
|
||||||
lsr : bcc .done
|
lsr : bcc .done
|
||||||
sta $02 : txa : lsr : cmp $00 : !bge .done
|
sta $02 : txa : lsr : cmp $00 : !blt +
|
||||||
lda $02 : inc
|
lda $02 : inc : bra .done
|
||||||
|
+ lda $02
|
||||||
.done rts
|
.done rts
|
||||||
|
|
||||||
DivideBy3:
|
DivideBy3:
|
||||||
|
|||||||
+151
-143
@@ -1,5 +1,5 @@
|
|||||||
WarpLeft:
|
WarpLeft:
|
||||||
lda DRMode : beq .end
|
lda.l DRMode : beq .end
|
||||||
lda $040c : cmp.b #$ff : beq .end
|
lda $040c : cmp.b #$ff : beq .end
|
||||||
lda $20 : ldx $aa
|
lda $20 : ldx $aa
|
||||||
jsr CalcIndex
|
jsr CalcIndex
|
||||||
@@ -10,7 +10,7 @@ WarpLeft:
|
|||||||
rtl
|
rtl
|
||||||
|
|
||||||
WarpRight:
|
WarpRight:
|
||||||
lda DRMode : beq .end
|
lda.l DRMode : beq .end
|
||||||
lda $040c : cmp.b #$ff : beq .end
|
lda $040c : cmp.b #$ff : beq .end
|
||||||
lda $20 : ldx $aa
|
lda $20 : ldx $aa
|
||||||
jsr CalcIndex
|
jsr CalcIndex
|
||||||
@@ -21,7 +21,7 @@ WarpRight:
|
|||||||
rtl
|
rtl
|
||||||
|
|
||||||
WarpUp:
|
WarpUp:
|
||||||
lda DRMode : beq .end
|
lda.l DRMode : beq .end
|
||||||
lda $040c : cmp.b #$ff : beq .end
|
lda $040c : cmp.b #$ff : beq .end
|
||||||
lda $22 : ldx $a9
|
lda $22 : ldx $a9
|
||||||
jsr CalcIndex
|
jsr CalcIndex
|
||||||
@@ -32,7 +32,7 @@ WarpUp:
|
|||||||
rtl
|
rtl
|
||||||
|
|
||||||
WarpDown:
|
WarpDown:
|
||||||
lda DRMode : beq .end
|
lda.l DRMode : beq .end
|
||||||
lda $040c : cmp.b #$ff : beq .end
|
lda $040c : cmp.b #$ff : beq .end
|
||||||
lda $22 : ldx $a9
|
lda $22 : ldx $a9
|
||||||
jsr CalcIndex
|
jsr CalcIndex
|
||||||
@@ -45,13 +45,13 @@ WarpDown:
|
|||||||
; carry set = use link door like normal
|
; carry set = use link door like normal
|
||||||
; carry clear = we are in dr mode, never use linking doors
|
; carry clear = we are in dr mode, never use linking doors
|
||||||
CheckLinkDoorR:
|
CheckLinkDoorR:
|
||||||
lda DRMode : bne +
|
lda.l DRMode : bne +
|
||||||
lda $7ec004 : sta $a0 ; what we wrote over
|
lda $7ec004 : sta $a0 ; what we wrote over
|
||||||
sec : rtl
|
sec : rtl
|
||||||
+ clc : rtl
|
+ clc : rtl
|
||||||
|
|
||||||
CheckLinkDoorL:
|
CheckLinkDoorL:
|
||||||
lda DRMode : bne +
|
lda.l DRMode : bne +
|
||||||
lda $7ec003 : sta $a0 ; what we wrote over
|
lda $7ec003 : sta $a0 ; what we wrote over
|
||||||
sec : rtl
|
sec : rtl
|
||||||
+ clc : rtl
|
+ clc : rtl
|
||||||
@@ -61,10 +61,11 @@ TrapDoorFixer:
|
|||||||
xba : asl #2 : sta $00
|
xba : asl #2 : sta $00
|
||||||
stz $0468 : lda $068c : ora $00 : sta $068c
|
stz $0468 : lda $068c : ora $00 : sta $068c
|
||||||
.end
|
.end
|
||||||
stz $fe ; clear our ab here because we don't need it anymore
|
stz $fe ; clear our fe here because we don't need it anymore
|
||||||
rts
|
rts
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
|
stz $047a
|
||||||
inc $11
|
inc $11
|
||||||
lda $ef
|
lda $ef
|
||||||
rts
|
rts
|
||||||
@@ -88,29 +89,32 @@ LoadRoomHorz:
|
|||||||
{
|
{
|
||||||
phb : phk : plb
|
phb : phk : plb
|
||||||
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
|
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
|
||||||
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00
|
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00-$01
|
||||||
lda $01 : and.b #$80 : cmp #$80 : bne .gtg
|
lda $00 : cmp #$03 : bne .gtg
|
||||||
; jsr HorzEdge : pla : bcs .end
|
jsr HorzEdge : pla : bcs .end
|
||||||
pla
|
|
||||||
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
|
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
|
||||||
|
|
||||||
.gtg ;Good to Go!
|
.gtg ;Good to Go!
|
||||||
pla ; Throw away normal room (don't fill up the stack)
|
pla ; Throw away normal room (don't fill up the stack)
|
||||||
lda $a0 : and.b #$0F : asl a : !sub $23 : !add $06 : sta $02
|
lda $a0 : and.b #$0F : asl a : !sub $23 : !add $06 : sta $02
|
||||||
ldy #$00 : jsr ShiftVariablesMainDir
|
ldy #$00 : jsr ShiftVariablesMainDir
|
||||||
lda $aa : lsr : sta $07
|
|
||||||
lda $a0 : and.b #$F0 : lsr #3 : !add $07 : !sub $21 : sta $02 : sta $03
|
lda $01 : and #$80 : beq .normal
|
||||||
jsr ShiftLowCoord
|
ldy $06 : cpy #$ff : beq +
|
||||||
jsr ShiftQuad
|
lda $01 : jsr LoadEastMidpoint : bra ++
|
||||||
jsr ShiftCameraBounds
|
+ lda $01 : jsr LoadWestMidpoint
|
||||||
ldy #$01 : jsr ShiftVariablesSubDir ; flip direction
|
++ jsr PrepScrollToEdge : bra .scroll
|
||||||
jsr SetupScrollIndicator
|
|
||||||
lda $01 : sta $fe : and #$04 : lsr #2
|
.normal
|
||||||
sta $ee
|
jsr PrepScrollToNormal
|
||||||
lda $01 : and #$10 : beq .end : stz $0468
|
.scroll
|
||||||
|
lda $01 : and #$40 : pha
|
||||||
|
jsr ScrollY
|
||||||
|
pla : beq .end
|
||||||
|
ldy #$06 : jsr ApplyScroll
|
||||||
.end
|
.end
|
||||||
plb ; restore db register
|
plb ; restore db register
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
|
|
||||||
; Y is an adjustment for main direction of travel (stored at $06)
|
; Y is an adjustment for main direction of travel (stored at $06)
|
||||||
@@ -119,43 +123,42 @@ LoadRoomVert:
|
|||||||
{
|
{
|
||||||
phb : phk : plb
|
phb : phk : plb
|
||||||
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
|
sty $06 : sta $07 : lda $a0 : pha ; Store normal room on stack
|
||||||
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00
|
lda $07 : jsr LookupNewRoom ; New room is in A, Room Data is in $00-$01
|
||||||
lda $01 : and.b #$80 : cmp #$80 : bne .gtg
|
lda $00 : cmp #$03 : bne .gtg
|
||||||
; jsr VertEdge : pla : bcs .end
|
jsr VertEdge : pla : bcs .end
|
||||||
pla
|
|
||||||
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
|
sta $a0 : bra .end ; Restore normal room, abort (straight staircases and open edges can get in this routine)
|
||||||
.gtg ;Good to Go!
|
.gtg ;Good to Go!
|
||||||
pla ; Throw away normal room (don't fill up the stack)
|
pla ; Throw away normal room (don't fill up the stack)
|
||||||
lda $a0 : and.b #$F0 : lsr #3 : !sub $21 : !add $06 : sta $02
|
lda $a0 : and.b #$F0 : lsr #3 : !sub $21 : !add $06 : sta $02
|
||||||
ldy #$01 : jsr ShiftVariablesMainDir
|
ldy #$01 : jsr ShiftVariablesMainDir
|
||||||
lda $a0 : and.b #$0F : asl a : !add $a9 : !sub $23 : sta $02 : sta $03
|
|
||||||
jsr ShiftLowCoord
|
|
||||||
jsr ShiftQuad
|
|
||||||
jsr ShiftCameraBounds
|
|
||||||
ldy #$00 : jsr ShiftVariablesSubDir ; flip direction
|
|
||||||
jsr SetupScrollIndicator
|
|
||||||
lda $01 : sta $fe : and #$04 : lsr #2
|
|
||||||
sta $ee
|
|
||||||
.end
|
|
||||||
plb ; restore db register
|
|
||||||
rts
|
|
||||||
}
|
|
||||||
|
|
||||||
SetupScrollIndicator:
|
lda $01 : and #$80 : beq .normal
|
||||||
lda $ab : and #$01 : asl : sta $ac
|
ldy $06 : cpy #$ff : beq +
|
||||||
lda $ab : and #$40 : clc : rol #3 : ora $ac : sta $ac
|
lda $01 : jsr LoadSouthMidpoint : bra ++
|
||||||
lda $ab : and #$20 : asl #2 : sta $ab
|
+ lda $01 : jsr LoadNorthMidpoint
|
||||||
|
++ jsr PrepScrollToEdge : bra .scroll
|
||||||
|
|
||||||
|
.normal
|
||||||
|
jsr PrepScrollToNormal
|
||||||
|
.scroll
|
||||||
|
lda $01 : and #$40 : pha
|
||||||
|
jsr ScrollX
|
||||||
|
pla : beq .end
|
||||||
|
ldy #$00 : jsr ApplyScroll
|
||||||
|
.end
|
||||||
|
plb ; restore db register
|
||||||
rts
|
rts
|
||||||
|
}
|
||||||
|
|
||||||
LookupNewRoom: ; expects data offset to be in A
|
LookupNewRoom: ; expects data offset to be in A
|
||||||
{
|
{
|
||||||
rep #$30 : and #$00FF ;sanitize A reg (who knows what is in the high byte)
|
rep #$30 : and #$00FF ;sanitize A reg (who knows what is in the high byte)
|
||||||
sta $00 ; offset in 00
|
sta $00 ; offset in 00
|
||||||
lda $a2 : tax ; probably okay loading $a3 in the high byte
|
lda $a2 : tax ; probably okay loading $a3 in the high byte
|
||||||
lda DoorOffset,x : and #$00FF ;we only want the low byte
|
lda.w DoorOffset,x : and #$00FF ;we only want the low byte
|
||||||
asl #3 : sta $02 : !add $02 : !add $02 ;multiply by 24 (data size)
|
asl #3 : sta $02 : !add $02 : !add $02 ;multiply by 24 (data size)
|
||||||
!add $00 ; should now have the offset of the address I want to load
|
!add $00 ; should now have the offset of the address I want to load
|
||||||
tax : lda DoorTable,x : sta $00
|
tax : lda.w DoorTable,x : sta $00
|
||||||
and #$00FF : sta $a0 ; assign new room
|
and #$00FF : sta $a0 ; assign new room
|
||||||
sep #$30
|
sep #$30
|
||||||
rts
|
rts
|
||||||
@@ -165,11 +168,11 @@ LookupNewRoom: ; expects data offset to be in A
|
|||||||
; Sets high bytes of various registers
|
; Sets high bytes of various registers
|
||||||
ShiftVariablesMainDir:
|
ShiftVariablesMainDir:
|
||||||
{
|
{
|
||||||
lda CoordIndex,y : tax
|
lda.w CoordIndex,y : tax
|
||||||
lda $21,x : !add $02 : sta $21,x ; coordinate update
|
lda $21,x : !add $02 : sta $21,x ; coordinate update
|
||||||
lda CameraIndex,y : tax
|
lda.w CameraIndex,y : tax
|
||||||
lda $e3,x : !add $02 : sta $e3,x ; scroll register high byte
|
lda $e3,x : !add $02 : sta $e3,x ; scroll register high byte
|
||||||
lda CamQuadIndex,y : tax
|
lda.w CamQuadIndex,y : tax
|
||||||
lda $0605,x : !add $02 : sta $0605,x ; high bytes of these guys
|
lda $0605,x : !add $02 : sta $0605,x ; high bytes of these guys
|
||||||
lda $0607,x : !add $02 : sta $0607,x
|
lda $0607,x : !add $02 : sta $0607,x
|
||||||
lda $0601,x : !add $02 : sta $0601,x
|
lda $0601,x : !add $02 : sta $0601,x
|
||||||
@@ -177,117 +180,122 @@ ShiftVariablesMainDir:
|
|||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
|
|
||||||
ShiftLowCoord:
|
|
||||||
|
; Target pixel should be in A, other info in $01
|
||||||
|
; Sets $04 $05 and $ee
|
||||||
|
PrepScrollToEdge:
|
||||||
{
|
{
|
||||||
lda $01 : and.b #$03 ; high byte index
|
sta $04 : lda $01 : and #$20 : beq +
|
||||||
jsr CalcOpposingShift
|
lda #01
|
||||||
lda $ab : and.b #$f0 : cmp.b #$20 : bne .lowDone
|
+ sta $05
|
||||||
lda OppCoordIndex,y : tax
|
lda $01 : and #$10 : beq +
|
||||||
lda #$80 : !add $20,x : sta $20,x
|
lda #01
|
||||||
.lowDone
|
+ sta $ee
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
|
|
||||||
; expects A to be (0,1,2) (dest number) and (0,1,2) (src door number) to be stored in $04
|
; Normal Flags should be in $01
|
||||||
; $ab will be set to a bitmask aaaa qxxf
|
; Sets $04 $05 and $ee, and $fe
|
||||||
; a - amount of adjust
|
PrepScrollToNormal:
|
||||||
; f - flag, if set, then amount is pos, otherwise neg.
|
|
||||||
; q - quadrant, if set, then quadrant needs to be modified
|
|
||||||
CalcOpposingShift:
|
|
||||||
{
|
{
|
||||||
stz $ab : stz $ac ; set up
|
lda $01 : sta $fe : and #$04 : lsr #2 : sta $ee ; trap door and layer
|
||||||
cmp.b $04 : beq .noOffset ; (equal, no shifts to do)
|
stz $05 : lda #$78 : sta $04
|
||||||
phy : tay ; reserve these
|
lda $01 : and #$03 : beq .end
|
||||||
lda $04 : tax : tya : !sub $04 : sta $04 : cmp.b #$00 : bpl .shiftPos
|
cmp #$02 : !bge +
|
||||||
lda #$40
|
lda #$f8 : sta $04 : bra .end
|
||||||
cpx.b #$01 : beq .skipNegQuad
|
+ inc $05
|
||||||
ora #$08
|
.end rts
|
||||||
.skipNegQuad
|
|
||||||
sta $ab : lda $04 : cmp.b #$FE : beq .done ;already set $ab
|
|
||||||
lda $ab : eor #$60
|
|
||||||
bra .setDone
|
|
||||||
|
|
||||||
.shiftPos
|
|
||||||
lda #$41
|
|
||||||
cpy.b #$01 : beq .skipPosQuad
|
|
||||||
ora #$08
|
|
||||||
.skipPosQuad
|
|
||||||
sta $ab : lda $04 : cmp.b #$02 : bcs .done ;already set $ab
|
|
||||||
lda $ab : eor #$60
|
|
||||||
|
|
||||||
.setDone sta $ab
|
|
||||||
.done ply
|
|
||||||
.noOffset rts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StraightStairsAdj:
|
||||||
ShiftQuad:
|
|
||||||
{
|
{
|
||||||
lda $ab : and #$08 : beq .quadDone
|
stx $0464 : sty $012e ; what we wrote over
|
||||||
lda ShiftQuadIndex,y : tax ; X should be set to either 1 (vertical) or 2 (horizontal) (for a9,aa quadrant)
|
lda.l DRMode : beq +
|
||||||
lda $ab : and #$01 : beq .decQuad
|
jsr GetTileAttribute : tax
|
||||||
inc $02
|
lda $11 : cmp #$12 : beq .goingNorth
|
||||||
txa : sta $a8, x ; alter a9/aa
|
lda $a2 : cmp #$51 : bne ++
|
||||||
bra .quadDone
|
rep #$20 : lda #$0018 : !add $20 : sta $20 : sep #$20 ; special fix for throne room
|
||||||
.decQuad
|
jsr GetTileAttribute : tax
|
||||||
dec $02
|
++ lda.l StepAdjustmentDown, X : bra .end
|
||||||
lda #$00 : sta $a8, x ; alter a9/aa
|
; lda $ee : beq .end
|
||||||
.quadDone rts
|
; rep #$20 : lda #$ffe0 : !add $20 : sta $20 : sep #$20
|
||||||
|
.goingNorth
|
||||||
|
cpx #$00 : bne ++
|
||||||
|
lda $a0 : cmp #$51 : bne ++
|
||||||
|
lda #$36 : bra .end ; special fix for throne room
|
||||||
|
++ ldy $ee : cpy #$00 : beq ++
|
||||||
|
inx
|
||||||
|
++ lda.l StepAdjustmentUp, X
|
||||||
|
.end
|
||||||
|
pha : lda $0462 : and #$04 : bne ++
|
||||||
|
pla : !add #$f6 : pha
|
||||||
|
++ pla : !add $0464 : sta $0464
|
||||||
|
+ rtl
|
||||||
}
|
}
|
||||||
|
|
||||||
ShiftVariablesSubDir:
|
GetTileAttribute:
|
||||||
{
|
{
|
||||||
lda CoordIndex,y : tax
|
phk : pea.w .jslrtsreturn-1
|
||||||
lda $21,x : !add $02 : sta $21,x ; coordinate update
|
pea.w $02802c
|
||||||
lda CameraIndex,y : tax
|
jml $02c11d ; mucks with x/y sets a to Tile Attribute, I think
|
||||||
lda $e3,x : !add $03 : sta $e3,x ; scroll register high byte
|
.jslrtsreturn
|
||||||
lda CamQuadIndex,y : tax
|
rts
|
||||||
lda $0601,x : !add $02 : sta $0601,x
|
|
||||||
lda $0605,x : !add $02 : sta $0605,x ; high bytes of these guys
|
|
||||||
lda $0603,x : !add $03 : sta $0603,x
|
|
||||||
lda $0607,x : !add $03 : sta $0607,x
|
|
||||||
rts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShiftCameraBounds:
|
; 0 open edge
|
||||||
|
; 1 nrm door high
|
||||||
|
; 2 straight str
|
||||||
|
; 3 nrm door low
|
||||||
|
; 4 trap door high
|
||||||
|
; 5 trap door low (none of these exist on North direction)
|
||||||
|
StepAdjustmentUp: ; really North Stairs
|
||||||
|
db $00, $f6, $1a, $18, $16, $38
|
||||||
|
StepAdjustmentDown: ; really South Stairs
|
||||||
|
db $d0, $f6, $10, $1a, $f0, $00
|
||||||
|
|
||||||
|
StraightStairsFix:
|
||||||
{
|
{
|
||||||
lda CamBoundIndex,y : tax ; should be 0 for horz travel (vert bounds) or 4 for vert travel (horz bounds)
|
lda.l DRMode : bne +
|
||||||
rep #$30
|
!add $20 : sta $20 ;what we wrote over
|
||||||
lda $ab : and #$00f0 : asl #2 : sta $06
|
+ rtl
|
||||||
lda $ab : and #$0001 : cmp #$0000 : beq .subIt
|
|
||||||
lda $0618, x : !add $06 : sta $0618, x
|
|
||||||
lda $061A, x : !add $06 : sta $061A, x
|
|
||||||
sep #$30
|
|
||||||
rts
|
|
||||||
.subIt
|
|
||||||
lda $0618, x : !sub $06 : sta $0618, x
|
|
||||||
lda $061A, x : !sub $06 : sta $061A, x
|
|
||||||
sep #$30
|
|
||||||
rts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustTransition:
|
StraightStairLayerFix:
|
||||||
{
|
{
|
||||||
lda $ab : and #$01ff : beq .reset
|
lda.l DRMode : beq +
|
||||||
phy : ldy #$06 ; operating on vertical registers during horizontal trans
|
lda $ee : rtl
|
||||||
cpx.b #$02 : bcs .horizontalScrolling
|
+ lda $01c322, x : rtl ; what we wrote over
|
||||||
ldy #$00 ; operate on horizontal regs during vert trans
|
|
||||||
.horizontalScrolling
|
|
||||||
cmp #$0008 : bcs +
|
|
||||||
pha : lda $ab : and #$0200 : beq ++
|
|
||||||
pla : bra .add
|
|
||||||
++ pla : eor #$ffff : inc ; convert to negative
|
|
||||||
.add jsr AdjustCamAdd : ply : bra .reset
|
|
||||||
+ lda $ab : and #$0200 : xba : tax
|
|
||||||
lda.l OffsetTable,x : jsr AdjustCamAdd
|
|
||||||
lda $ab : !sub #$0008 : sta $ab
|
|
||||||
ply : bra .done
|
|
||||||
.reset ; clear the $ab variable so to not disturb intra-tile doors
|
|
||||||
stz $ab
|
|
||||||
.done
|
|
||||||
lda $00 : and #$01fc
|
|
||||||
rtl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustCamAdd:
|
DoorToStraight:
|
||||||
!add $00E2,y : sta $00E2,y : sta $00E0,y : rts
|
{
|
||||||
|
pha
|
||||||
|
lda.l DRMode : beq .skip
|
||||||
|
pla : bne .end
|
||||||
|
pha
|
||||||
|
lda $a0 : cmp #$51 : bne .skip
|
||||||
|
lda #$04 : sta $4e
|
||||||
|
.skip pla
|
||||||
|
.end ldx $0418 : cmp #$02 ;what we wrote over
|
||||||
|
rtl
|
||||||
|
}
|
||||||
|
|
||||||
|
StraightStairsTrapDoor:
|
||||||
|
{
|
||||||
|
lda $0464 : bne +
|
||||||
|
; reset function
|
||||||
|
phk : pea.w .jslrtsreturn-1
|
||||||
|
pea.w $02802c
|
||||||
|
jml $028c73 ; $10D71 .reset label of Bank02
|
||||||
|
.jslrtsreturn
|
||||||
|
lda $0468 : bne ++
|
||||||
|
lda $a0 : cmp.b #$ac : bne .animateTraps
|
||||||
|
lda $0403 : and.b #$20 : bne .animateTraps
|
||||||
|
lda $0403 : and.b #$10 : beq ++
|
||||||
|
.animateTraps
|
||||||
|
lda #$05 : sta $11
|
||||||
|
inc $0468 : stz $068e : stz $0690
|
||||||
|
++ rtl
|
||||||
|
+ jsl Dungeon_ApproachFixedColor ; what we wrote over
|
||||||
|
.end rtl
|
||||||
|
}
|
||||||
+13
-7
@@ -26,7 +26,7 @@ GtBossHeartCheckOverride:
|
|||||||
lda $a0 : cmp #$1c : beq ++
|
lda $a0 : cmp #$1c : beq ++
|
||||||
cmp #$6c : beq ++
|
cmp #$6c : beq ++
|
||||||
cmp #$4d : bne +
|
cmp #$4d : bne +
|
||||||
++ lda DRFlags : and #$01 : bne ++ ;skip if flag on
|
++ lda.l DRFlags : and #$01 : bne ++ ;skip if flag on
|
||||||
lda $403 : ora #$80 : sta $403
|
lda $403 : ora #$80 : sta $403
|
||||||
++ clc
|
++ clc
|
||||||
rtl
|
rtl
|
||||||
@@ -35,19 +35,19 @@ rtl
|
|||||||
|
|
||||||
OnFileLoadOverride:
|
OnFileLoadOverride:
|
||||||
jsl OnFileLoad ; what I wrote over
|
jsl OnFileLoad ; what I wrote over
|
||||||
lda DRFlags : and #$80 : beq + ;flag is off
|
lda.l DRFlags : and #$80 : beq + ;flag is off
|
||||||
lda $7ef086 : ora #$80 : sta $7ef086
|
lda $7ef086 : ora #$80 : sta $7ef086
|
||||||
+ lda DRFlags : and #$02 : beq +
|
+ lda.l DRFlags : and #$02 : beq +
|
||||||
lda $7ef353 : bne +
|
lda $7ef353 : bne +
|
||||||
lda #$01 : sta $7ef353
|
lda #$01 : sta $7ef353
|
||||||
+ rtl
|
+ rtl
|
||||||
|
|
||||||
MirrorCheckOverride:
|
MirrorCheckOverride:
|
||||||
lda DRFlags : and #$02 : beq ++
|
lda.l DRFlags : and #$02 : beq ++
|
||||||
lda $7ef353 : cmp #$01 : beq +
|
lda $7ef353 : cmp #$01 : beq +
|
||||||
++ lda $8A : and #$40 ; what I wrote over
|
++ lda $8A : and #$40 ; what I wrote over
|
||||||
rtl
|
rtl
|
||||||
+ lda DRScroll : rtl
|
+ lda.l DRScroll : rtl
|
||||||
|
|
||||||
MirrorCheckOverride2:
|
MirrorCheckOverride2:
|
||||||
lda $7ef353 : and #$02 : rtl
|
lda $7ef353 : and #$02 : rtl
|
||||||
@@ -64,7 +64,7 @@ FixShopCode:
|
|||||||
+ rtl
|
+ rtl
|
||||||
|
|
||||||
VitreousKeyReset:
|
VitreousKeyReset:
|
||||||
lda DRMode : beq +
|
lda.l DRMode : beq +
|
||||||
stz $0cba, x
|
stz $0cba, x
|
||||||
+ jsl $0db818 ;restore old code
|
+ jsl $0db818 ;restore old code
|
||||||
rtl
|
rtl
|
||||||
@@ -73,4 +73,10 @@ GuruguruFix:
|
|||||||
lda $a0 : cmp #$df : !bge +
|
lda $a0 : cmp #$df : !bge +
|
||||||
and #$0f : cmp #$0e : !blt +
|
and #$0f : cmp #$0e : !blt +
|
||||||
iny #2
|
iny #2
|
||||||
+ rtl
|
+ rtl
|
||||||
|
|
||||||
|
BlindAtticFix:
|
||||||
|
lda.l DRMode : beq +
|
||||||
|
lda #$01 : rtl
|
||||||
|
+ lda $7EF3CC : cmp.b #$06
|
||||||
|
rtl
|
||||||
+206
@@ -0,0 +1,206 @@
|
|||||||
|
AdjustTransition:
|
||||||
|
{
|
||||||
|
lda $ab : and #$01ff : beq .reset
|
||||||
|
phy : ldy #$06 ; operating on vertical registers during horizontal trans
|
||||||
|
cpx.b #$02 : bcs .horizontalScrolling
|
||||||
|
ldy #$00 ; operate on horizontal regs during vert trans
|
||||||
|
.horizontalScrolling
|
||||||
|
cmp #$0008 : bcs +
|
||||||
|
pha : lda $ab : and #$0200 : beq ++
|
||||||
|
pla : bra .add
|
||||||
|
++ pla : eor #$ffff : inc ; convert to negative
|
||||||
|
.add jsr AdjustCamAdd : ply : bra .reset
|
||||||
|
+ lda $ab : and #$0200 : xba : tax
|
||||||
|
lda.l OffsetTable,x : jsr AdjustCamAdd
|
||||||
|
lda $ab : !sub #$0008 : sta $ab
|
||||||
|
ply : bra .done
|
||||||
|
.reset ; clear the $ab variable so to not disturb intra-tile doors
|
||||||
|
stz $ab
|
||||||
|
.done
|
||||||
|
lda $00 : and #$01fc
|
||||||
|
rtl
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustCamAdd:
|
||||||
|
!add $00E2,y : pha
|
||||||
|
and #$01ff : cmp #$0111 : !blt +
|
||||||
|
cmp #$01f8 : !bge ++
|
||||||
|
pla : and #$ff10 : pha : bra +
|
||||||
|
++ pla : and #$ff00 : !add #$0100 : pha
|
||||||
|
+ pla : sta $00E2,y : sta $00E0,y : rts
|
||||||
|
|
||||||
|
; expects target quad in $05 (either 0 or 1) and target pixel in $04, target room should be in $a0
|
||||||
|
; $06 is either $ff or $01/02
|
||||||
|
; uses $00-$03 and $0e for calculation
|
||||||
|
; also set up $ac
|
||||||
|
ScrollY: ;change the Y offset variables
|
||||||
|
lda $a0 : and.b #$f0 : lsr #3 : sta $0603 : inc : sta $0607
|
||||||
|
|
||||||
|
lda $05 : bne +
|
||||||
|
lda $603 : sta $00 : stz $01 : bra ++
|
||||||
|
+ lda $607 : sta $00 : lda #$02 : sta $01
|
||||||
|
++ ; $01 now contains 0 or 2 and $00 contains the correct lat
|
||||||
|
|
||||||
|
stz $0e
|
||||||
|
rep #$30
|
||||||
|
lda $00 : pha
|
||||||
|
|
||||||
|
lda $e8 : and #$01ff : sta $02
|
||||||
|
lda $04 : jsr LimitYCamera : sta $00
|
||||||
|
jsr CheckRoomLayoutY : bcc +
|
||||||
|
lda $00 : cmp #$0080 : !bge ++
|
||||||
|
cmp #$0010 : !blt .cmpSrll
|
||||||
|
lda #$0010 : bra .cmpSrll
|
||||||
|
++ cmp #$0100 : !bge .cmpSrll
|
||||||
|
lda #$0100
|
||||||
|
.cmpSrll sta $00
|
||||||
|
|
||||||
|
; figures out scroll amt
|
||||||
|
+ lda $00 : cmp $02 : bne +
|
||||||
|
lda #$0000 : bra .next
|
||||||
|
+ !blt +
|
||||||
|
!sub $02 : inc $0e : bra .next
|
||||||
|
+ lda $02 : !sub $00
|
||||||
|
|
||||||
|
.next
|
||||||
|
sta $ab
|
||||||
|
jsr AdjustCameraBoundsY
|
||||||
|
|
||||||
|
pla : sta $00
|
||||||
|
sep #$30
|
||||||
|
lda $04 : sta $20
|
||||||
|
lda $00 : sta $21 : sta $0601 : sta $0605
|
||||||
|
lda $01 : sta $aa
|
||||||
|
lda $0e : asl : ora $ac : sta $ac
|
||||||
|
lda $e9 : and #$01 : asl #2 : tax : lda $0603, x : sta $e9
|
||||||
|
rts
|
||||||
|
|
||||||
|
LimitYCamera:
|
||||||
|
cmp #$006c : !bge +
|
||||||
|
lda #$0000 : bra .end
|
||||||
|
+ cmp #$017d : !blt +
|
||||||
|
lda #$0110 : bra .end
|
||||||
|
+ !sub #$006c
|
||||||
|
.end rts
|
||||||
|
|
||||||
|
CheckRoomLayoutY:
|
||||||
|
jsr LoadRoomLayout ;switches to 8-bit
|
||||||
|
cmp #$00 : beq .lock
|
||||||
|
cmp #$07 : beq .free
|
||||||
|
cmp #$01 : beq .free
|
||||||
|
cmp #$04 : !bge .lock
|
||||||
|
cmp #$02 : bne +
|
||||||
|
lda $06 : cmp #$ff : beq .lock
|
||||||
|
+ cmp #$03 : bne .free
|
||||||
|
lda $06 : cmp #$ff : bne .lock
|
||||||
|
.free rep #$30 : clc : rts
|
||||||
|
.lock rep #$30 : sec : rts
|
||||||
|
|
||||||
|
AdjustCameraBoundsY:
|
||||||
|
jsr CheckRoomLayoutY : bcc .free
|
||||||
|
|
||||||
|
; layouts that are camera locked (quads only)
|
||||||
|
lda $04 : and #$00ff : cmp #$007d : !blt +
|
||||||
|
lda #$0088 : bra ++
|
||||||
|
+ cmp #$006d : !bge +
|
||||||
|
lda #$0078 : bra ++
|
||||||
|
+ !add #$000b
|
||||||
|
|
||||||
|
; I think we no longer need the $02 variable
|
||||||
|
++ sta $02 : lda $04 : and #$0100 : !add $02 : bra .setBounds
|
||||||
|
|
||||||
|
; layouts where the camera is free
|
||||||
|
.free lda $04 : cmp #$006c : !bge +
|
||||||
|
lda #$0077 : bra .setBounds
|
||||||
|
+ cmp #$017c : !blt +
|
||||||
|
lda #$0187 : bra .setBounds
|
||||||
|
+ !add #$000b
|
||||||
|
.setBounds sta $0618 : inc #2 : sta $061a
|
||||||
|
rts
|
||||||
|
|
||||||
|
LoadRoomLayout:
|
||||||
|
lda $a0 : asl : !add $a0 : tax
|
||||||
|
lda $1f8001, x : sta $b8
|
||||||
|
lda $1f8000, x : sta $b7
|
||||||
|
sep #$30
|
||||||
|
ldy #$01 : lda [$b7], y : and #$1c : lsr #2
|
||||||
|
rts
|
||||||
|
|
||||||
|
; expects target quad in $05 (either 0 or 1) and target pixel in $04, target room should be in $a0
|
||||||
|
; uses $00-$03 and $0e for calculation
|
||||||
|
; also set up $ac
|
||||||
|
ScrollX: ;change the X offset variables
|
||||||
|
lda $a0 : and.b #$0f : asl : sta $060b : inc : sta $060f
|
||||||
|
|
||||||
|
lda $05 : bne +
|
||||||
|
lda $60b : sta $00 : stz $01 : bra ++
|
||||||
|
+ lda $60f : sta $00 : lda #$01 : sta $01
|
||||||
|
++ ; $01 now contains 0 or 1 and $00 contains the correct long
|
||||||
|
|
||||||
|
stz $0e ; pos/neg indicator
|
||||||
|
rep #$30
|
||||||
|
lda $00 : pha
|
||||||
|
|
||||||
|
lda $e2 : and #$01ff : sta $02
|
||||||
|
lda $04 : jsr LimitXCamera : sta $00
|
||||||
|
jsr CheckRoomLayoutX : bcc +
|
||||||
|
lda $00 : cmp #$0080 : !bge ++
|
||||||
|
lda #$0000 : bra .cmpSrll
|
||||||
|
++ lda #$0100
|
||||||
|
.cmpSrll sta $00
|
||||||
|
|
||||||
|
;figures out scroll amt
|
||||||
|
+ lda $00 : cmp $02 : bne +
|
||||||
|
lda #$0000 : bra .next
|
||||||
|
+ !blt +
|
||||||
|
!sub $02 : inc $0e : bra .next
|
||||||
|
+ lda $02 : !sub $00
|
||||||
|
|
||||||
|
.next
|
||||||
|
sta $ab : lda $04
|
||||||
|
|
||||||
|
cmp #$0078 : !bge +
|
||||||
|
lda #$007f : bra ++
|
||||||
|
+ cmp #$0178 : !blt +
|
||||||
|
lda #$017f : bra ++
|
||||||
|
+ !add #$0007
|
||||||
|
++ sta $061c : inc #2 : sta $061e
|
||||||
|
|
||||||
|
pla : sta $00
|
||||||
|
sep #$30
|
||||||
|
lda $04 : sta $22
|
||||||
|
lda $00 : sta $23 : sta $0609 : sta $060d
|
||||||
|
lda $01 : sta $a9
|
||||||
|
lda $0e : asl : ora $ac : sta $ac
|
||||||
|
lda $e3 : and #$01 : asl #2 : tax : lda $060b, x : sta $e3
|
||||||
|
|
||||||
|
rts
|
||||||
|
|
||||||
|
LimitXCamera:
|
||||||
|
cmp #$0080 : !bge +
|
||||||
|
lda #$0000 : bra .end
|
||||||
|
+ cmp #$0181 : !blt +
|
||||||
|
lda #$0180
|
||||||
|
+ !sub #$0080
|
||||||
|
.end rts
|
||||||
|
|
||||||
|
CheckRoomLayoutX:
|
||||||
|
jsr LoadRoomLayout ;switches to 8-bit
|
||||||
|
cmp #$04 : !blt .lock
|
||||||
|
cmp #$05 : bne +
|
||||||
|
lda $06 : cmp #$ff : beq .lock
|
||||||
|
+ cmp #$06 : bne .free
|
||||||
|
lda $06 : cmp #$ff : bne .lock
|
||||||
|
.free rep #$30 : clc : rts
|
||||||
|
.lock rep #$30 : sec : rts
|
||||||
|
|
||||||
|
ApplyScroll:
|
||||||
|
rep #$30
|
||||||
|
lda $ab : and #$01ff : sta $00
|
||||||
|
lda $ab : and #$0200 : beq +
|
||||||
|
lda $00e2, y : !add $00 : bra .end
|
||||||
|
+ lda $00e2, y : !sub $00
|
||||||
|
.end
|
||||||
|
sta $00e2, y
|
||||||
|
sta $00e0, y
|
||||||
|
stz $ab : sep #$30 : rts
|
||||||
+43
-20
@@ -1,11 +1,17 @@
|
|||||||
RecordStairType: {
|
RecordStairType: {
|
||||||
sta $a0
|
pha
|
||||||
lda $0e : sta $045e
|
lda.l DRMode : beq .norm
|
||||||
lda $063d, x
|
lda $040c : cmp #$ff : beq .norm
|
||||||
|
lda $0e : sta $045e
|
||||||
|
cmp #$26 : beq .norm ; skipping in-floor staircases
|
||||||
|
pla : bra +
|
||||||
|
.norm pla : sta $a0
|
||||||
|
+ lda $063d, x
|
||||||
rtl
|
rtl
|
||||||
}
|
}
|
||||||
|
|
||||||
SpiralWarp: {
|
SpiralWarp: {
|
||||||
|
lda.l DRMode : beq .abort ; abort if not DR
|
||||||
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
|
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
|
||||||
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
|
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
|
||||||
cmp #$5f : beq .gtg
|
cmp #$5f : beq .gtg
|
||||||
@@ -16,8 +22,8 @@ SpiralWarp: {
|
|||||||
phb : phk : plb : phx : phy ; push stuff
|
phb : phk : plb : phx : phy ; push stuff
|
||||||
jsr LookupSpiralOffset
|
jsr LookupSpiralOffset
|
||||||
rep #$30 : and #$00FF : asl #2 : tax
|
rep #$30 : and #$00FF : asl #2 : tax
|
||||||
lda SpiralTable, x : sta $00
|
lda.w SpiralTable, x : sta $00
|
||||||
lda SpiralTable+2, x : sta $02
|
lda.w SpiralTable+2, x : sta $02
|
||||||
sep #$30
|
sep #$30
|
||||||
lda $00 : sta $a0
|
lda $00 : sta $a0
|
||||||
; shift quadrant if necessary
|
; shift quadrant if necessary
|
||||||
@@ -65,6 +71,16 @@ SpiralWarp: {
|
|||||||
ldy #$01 : jsr SetCamera
|
ldy #$01 : jsr SetCamera
|
||||||
|
|
||||||
stz $045e ; clear the staircase flag
|
stz $045e ; clear the staircase flag
|
||||||
|
|
||||||
|
; animated tiles fix
|
||||||
|
lda.l DRMode : cmp #$02 : bne + ; only do this in crossed mode
|
||||||
|
ldx $a0 : lda.l TilesetTable, x
|
||||||
|
cmp $0aa1 : beq + ; already eq no need to decomp
|
||||||
|
sta $0aa1
|
||||||
|
tax : lda $02802e, x : tay
|
||||||
|
jsl DecompDungAnimatedTiles
|
||||||
|
+
|
||||||
|
stz $047a
|
||||||
ply : plx : plb ; pull the stuff we pushed
|
ply : plx : plb ; pull the stuff we pushed
|
||||||
lda $a2 : and #$0f ; this is the code we are hijacking
|
lda $a2 : and #$0f ; this is the code we are hijacking
|
||||||
rtl
|
rtl
|
||||||
@@ -117,17 +133,17 @@ LookupSpiralOffset: {
|
|||||||
lda #$02 : sta $01 ; always 2
|
lda #$02 : sta $01 ; always 2
|
||||||
|
|
||||||
.done
|
.done
|
||||||
lda $a2 : tax : lda SpiralOffset,x
|
lda $a2 : tax : lda.w SpiralOffset,x
|
||||||
!add $01 ;add a thing (0 in easy case)
|
!add $01 ;add a thing (0 in easy case)
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
|
|
||||||
ShiftQuadSimple: {
|
ShiftQuadSimple: {
|
||||||
lda CoordIndex,y : tax
|
lda.w CoordIndex,y : tax
|
||||||
lda $20,x : beq .skip
|
lda $20,x : beq .skip
|
||||||
lda $21,x : !add $06 : sta $21,x ; coordinate update
|
lda $21,x : !add $06 : sta $21,x ; coordinate update
|
||||||
.skip
|
.skip
|
||||||
lda CamQuadIndex,y : tax
|
lda.w CamQuadIndex,y : tax
|
||||||
lda $0601,x : !add $06 : sta $0601,x
|
lda $0601,x : !add $06 : sta $0601,x
|
||||||
lda $0605,x : !add $06 : sta $0605,x ; high bytes of these guys
|
lda $0605,x : !add $06 : sta $0605,x ; high bytes of these guys
|
||||||
rts
|
rts
|
||||||
@@ -136,13 +152,13 @@ ShiftQuadSimple: {
|
|||||||
SetCamera: {
|
SetCamera: {
|
||||||
stz $04
|
stz $04
|
||||||
tyx : lda $a9,x : bne .nonZeroHalf
|
tyx : lda $a9,x : bne .nonZeroHalf
|
||||||
lda CamQuadIndex,y : tax : lda $607,x : pha
|
lda.w CamQuadIndex,y : tax : lda $607,x : pha
|
||||||
lda CameraIndex,y : tax : pla : cmp $e3, x : bne .noQuadAdj
|
lda.w CameraIndex,y : tax : pla : cmp $e3, x : bne .noQuadAdj
|
||||||
dec $e3,x
|
dec $e3,x
|
||||||
|
|
||||||
.noQuadAdj
|
.noQuadAdj
|
||||||
lda $07 : bne .adj0
|
lda $07 : bne .adj0
|
||||||
lda CoordIndex,y : tax
|
lda.w CoordIndex,y : tax
|
||||||
lda $20,x : beq .oddQuad
|
lda $20,x : beq .oddQuad
|
||||||
cmp #$79 : bcc .adj0
|
cmp #$79 : bcc .adj0
|
||||||
!sub #$78 : sta $04
|
!sub #$78 : sta $04
|
||||||
@@ -154,21 +170,21 @@ SetCamera: {
|
|||||||
|
|
||||||
.nonZeroHalf ;meaning either right half or bottom half
|
.nonZeroHalf ;meaning either right half or bottom half
|
||||||
lda $07 : bne .setQuad
|
lda $07 : bne .setQuad
|
||||||
lda CoordIndex,y : tax
|
lda.w CoordIndex,y : tax
|
||||||
lda $20,x : cmp #$78 : bcs .setQuad
|
lda $20,x : cmp #$78 : bcs .setQuad
|
||||||
!add #$78 : sta $04
|
!add #$78 : sta $04
|
||||||
lda CamQuadIndex,y : tax : lda $0603, x : pha
|
lda.w CamQuadIndex,y : tax : lda $0603, x : pha
|
||||||
lda CameraIndex,y : tax : pla : sta $e3, x
|
lda.w CameraIndex,y : tax : pla : sta $e3, x
|
||||||
.adj1
|
.adj1
|
||||||
tya : asl : !add #$08 : tax : jsr AdjCamBounds : bra .done
|
tya : asl : !add #$08 : tax : jsr AdjCamBounds : bra .done
|
||||||
|
|
||||||
.setQuad
|
.setQuad
|
||||||
lda CamQuadIndex,y : tax : lda $0607, x : pha
|
lda.w CamQuadIndex,y : tax : lda $0607, x : pha
|
||||||
lda CameraIndex,y : tax : pla : sta $e3, x
|
lda.w CameraIndex,y : tax : pla : sta $e3, x
|
||||||
tya : asl : !add #$0c : tax : jsr AdjCamBounds : bra .done
|
tya : asl : !add #$0c : tax : jsr AdjCamBounds : bra .done
|
||||||
|
|
||||||
.done
|
.done
|
||||||
lda CameraIndex,y : tax
|
lda.w CameraIndex,y : tax
|
||||||
lda $04 : sta $e2, x
|
lda $04 : sta $e2, x
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
@@ -176,13 +192,20 @@ SetCamera: {
|
|||||||
; input, expects X to be an appropriate offset into the CamBoundBaseLine table
|
; input, expects X to be an appropriate offset into the CamBoundBaseLine table
|
||||||
; when $04 is 0 no coordinate are added
|
; when $04 is 0 no coordinate are added
|
||||||
AdjCamBounds: {
|
AdjCamBounds: {
|
||||||
rep #$20 : lda CamBoundBaseLine, x : sta $05
|
rep #$20 : lda.w CamBoundBaseLine, x : sta $05
|
||||||
lda $04 : and #$00ff : beq .common
|
lda $04 : and #$00ff : beq .common
|
||||||
lda CoordIndex,y : tax
|
lda.w CoordIndex,y : tax
|
||||||
lda $20, x : and #$00ff : !add $05 : sta $05
|
lda $20, x : and #$00ff : !add $05 : sta $05
|
||||||
.common
|
.common
|
||||||
lda OppCamBoundIndex,y : tax
|
lda.w OppCamBoundIndex,y : tax
|
||||||
lda $05 : sta $0618, x
|
lda $05 : sta $0618, x
|
||||||
inc #2 : sta $061A, x : sep #$20
|
inc #2 : sta $061A, x : sep #$20
|
||||||
rts
|
rts
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiralPriorityHack: {
|
||||||
|
lda.l DRMode : beq +
|
||||||
|
lda #$01 : rtl ; always skip the priority code - until I figure out how to fix it
|
||||||
|
+ lda $0462 : and #$04 ; what we wrote over
|
||||||
|
rtl
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -110,6 +110,11 @@
|
|||||||
"vanilla"
|
"vanilla"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"intensity": {
|
||||||
|
"choices":[
|
||||||
|
"3", "2", "1", "random"
|
||||||
|
]
|
||||||
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
@@ -124,12 +129,12 @@
|
|||||||
},
|
},
|
||||||
"crystals_ganon": {
|
"crystals_ganon": {
|
||||||
"choices": [
|
"choices": [
|
||||||
7, 6, 5, 4, 3, 2, 1, 0, "random"
|
"7", "6", "5", "4", "3", "2", "1", "0", "random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"crystals_gt": {
|
"crystals_gt": {
|
||||||
"choices": [
|
"choices": [
|
||||||
7, 6, 5, 4, 3, 2, 1, 0, "random"
|
"7", "6", "5", "4", "3", "2", "1", "0", "random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"openpyramid": {
|
"openpyramid": {
|
||||||
@@ -284,16 +289,16 @@
|
|||||||
"shufflebosses": {
|
"shufflebosses": {
|
||||||
"choices": [
|
"choices": [
|
||||||
"none",
|
"none",
|
||||||
"basic",
|
"simple",
|
||||||
"normal",
|
"full",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"shuffleenemies": {
|
"shuffleenemies": {
|
||||||
"choices": [
|
"choices": [
|
||||||
"none",
|
"none",
|
||||||
"shuffled",
|
"shuffled",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enemy_health": {
|
"enemy_health": {
|
||||||
@@ -309,7 +314,7 @@
|
|||||||
"choices": [
|
"choices": [
|
||||||
"default",
|
"default",
|
||||||
"shuffled",
|
"shuffled",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"shufflepots": {
|
"shufflepots": {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
"shuffling.world": "Shuffling the World about",
|
"shuffling.world": "Shuffling the World about",
|
||||||
"shuffling.dungeons": "Shuffling dungeons",
|
"shuffling.dungeons": "Shuffling dungeons",
|
||||||
"basic.traversal": "--Basic Traversal",
|
"basic.traversal": "--Basic Traversal",
|
||||||
"generating.dungeon": "Generating dungeon",
|
"generating.dungeon": "Generating dungeons",
|
||||||
"shuffling.keydoors": "Shuffling Key doors for",
|
"shuffling.keydoors": "Shuffling Key doors",
|
||||||
"lowering.keys.candidates": "Lowering key door count because not enough candidates",
|
"lowering.keys.candidates": "Lowering key door count because not enough candidates",
|
||||||
"lowering.keys.layouts": "Lowering key door count because no valid layouts",
|
"lowering.keys.layouts": "Lowering key door count because no valid layouts",
|
||||||
"keydoor.shuffle.time": "Key door shuffle time",
|
"keydoor.shuffle.time": "Key door shuffle time",
|
||||||
@@ -198,6 +198,13 @@
|
|||||||
"Vanilla: All doors are connected the same way they were in the",
|
"Vanilla: All doors are connected the same way they were in the",
|
||||||
" base game."
|
" base game."
|
||||||
],
|
],
|
||||||
|
"intensity" : [
|
||||||
|
"Door Shuffle Intensity Level (default: %(default)s)",
|
||||||
|
"1: Shuffles normal doors and spiral staircases",
|
||||||
|
"2: And shuffles open edges and straight staircases",
|
||||||
|
"3: (Coming soon) And shuffles dungeon lobbies",
|
||||||
|
"random: Picks one of those at random"
|
||||||
|
],
|
||||||
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
|
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
|
||||||
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
|
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
|
||||||
"crystals_ganon": [
|
"crystals_ganon": [
|
||||||
|
|||||||
@@ -58,6 +58,12 @@
|
|||||||
"randomizer.dungeon.dungeondoorshuffle.basic": "Basic",
|
"randomizer.dungeon.dungeondoorshuffle.basic": "Basic",
|
||||||
"randomizer.dungeon.dungeondoorshuffle.crossed": "Crossed",
|
"randomizer.dungeon.dungeondoorshuffle.crossed": "Crossed",
|
||||||
|
|
||||||
|
"randomizer.dungeon.dungeonintensity": "Intensity Level",
|
||||||
|
"randomizer.dungeon.dungeonintensity.1": "1: Normal Supertile and Spiral Stairs",
|
||||||
|
"randomizer.dungeon.dungeonintensity.2": "2: Open Edges and Straight Stairs",
|
||||||
|
"randomizer.dungeon.dungeonintensity.3": "3: (Coming soon) Dungeon Lobbies",
|
||||||
|
"randomizer.dungeon.dungeonintensity.random": "Random",
|
||||||
|
|
||||||
"randomizer.dungeon.experimental": "Enable Experimental Features",
|
"randomizer.dungeon.experimental": "Enable Experimental Features",
|
||||||
|
|
||||||
"randomizer.dungeon.dungeon_counters": "Dungeon Chest Counters",
|
"randomizer.dungeon.dungeon_counters": "Dungeon Chest Counters",
|
||||||
@@ -70,25 +76,25 @@
|
|||||||
"randomizer.enemizer.potshuffle": "Pot Shuffle",
|
"randomizer.enemizer.potshuffle": "Pot Shuffle",
|
||||||
|
|
||||||
"randomizer.enemizer.enemyshuffle": "Enemy Shuffle",
|
"randomizer.enemizer.enemyshuffle": "Enemy Shuffle",
|
||||||
"randomizer.enemizer.enemyshuffle.none": "Vanilla",
|
"randomizer.enemizer.enemyshuffle.none": "None",
|
||||||
"randomizer.enemizer.enemyshuffle.shuffled": "Shuffled",
|
"randomizer.enemizer.enemyshuffle.shuffled": "Shuffled",
|
||||||
"randomizer.enemizer.enemyshuffle.chaos": "Chaos",
|
"randomizer.enemizer.enemyshuffle.random": "Random",
|
||||||
|
|
||||||
"randomizer.enemizer.bossshuffle": "Boss Shuffle",
|
"randomizer.enemizer.bossshuffle": "Boss Shuffle",
|
||||||
"randomizer.enemizer.bossshuffle.none": "Vanilla",
|
"randomizer.enemizer.bossshuffle.none": "None",
|
||||||
"randomizer.enemizer.bossshuffle.basic": "Basic",
|
"randomizer.enemizer.bossshuffle.simple": "Simple",
|
||||||
"randomizer.enemizer.bossshuffle.shuffled": "Shuffled",
|
"randomizer.enemizer.bossshuffle.full": "Full",
|
||||||
"randomizer.enemizer.bossshuffle.chaos": "Chaos",
|
"randomizer.enemizer.bossshuffle.random": "Random",
|
||||||
|
|
||||||
"randomizer.enemizer.enemydamage": "Enemy Damage",
|
"randomizer.enemizer.enemydamage": "Enemy Damage",
|
||||||
"randomizer.enemizer.enemydamage.default": "Vanilla",
|
"randomizer.enemizer.enemydamage.default": "Default",
|
||||||
"randomizer.enemizer.enemydamage.shuffled": "Shuffled",
|
"randomizer.enemizer.enemydamage.shuffled": "Shuffled",
|
||||||
"randomizer.enemizer.enemydamage.chaos": "Chaos",
|
"randomizer.enemizer.enemydamage.random": "Random",
|
||||||
|
|
||||||
"randomizer.enemizer.enemyhealth": "Enemy Health",
|
"randomizer.enemizer.enemyhealth": "Enemy Health",
|
||||||
"randomizer.enemizer.enemyhealth.default": "Vanilla",
|
"randomizer.enemizer.enemyhealth.default": "Default",
|
||||||
"randomizer.enemizer.enemyhealth.easy": "Easy",
|
"randomizer.enemizer.enemyhealth.easy": "Easy",
|
||||||
"randomizer.enemizer.enemyhealth.normal": "Normal",
|
"randomizer.enemizer.enemyhealth.normal": "Medium",
|
||||||
"randomizer.enemizer.enemyhealth.hard": "Hard",
|
"randomizer.enemizer.enemyhealth.hard": "Hard",
|
||||||
"randomizer.enemizer.enemyhealth.expert": "Expert",
|
"randomizer.enemizer.enemyhealth.expert": "Expert",
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,19 @@
|
|||||||
"crossed"
|
"crossed"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"dungeonintensity": {
|
||||||
|
"type": "selectbox",
|
||||||
|
"default": "2",
|
||||||
|
"options": [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"random"
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"width": 40
|
||||||
|
}
|
||||||
|
},
|
||||||
"experimental": { "type": "checkbox" },
|
"experimental": { "type": "checkbox" },
|
||||||
"dungeon_counters": {
|
"dungeon_counters": {
|
||||||
"type": "selectbox",
|
"type": "selectbox",
|
||||||
|
|||||||
@@ -8,16 +8,16 @@
|
|||||||
"options": [
|
"options": [
|
||||||
"none",
|
"none",
|
||||||
"shuffled",
|
"shuffled",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"bossshuffle": {
|
"bossshuffle": {
|
||||||
"type": "selectbox",
|
"type": "selectbox",
|
||||||
"options": [
|
"options": [
|
||||||
"none",
|
"none",
|
||||||
"basic",
|
"simple",
|
||||||
"shuffled",
|
"full",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"options": [
|
"options": [
|
||||||
"default",
|
"default",
|
||||||
"shuffled",
|
"shuffled",
|
||||||
"chaos"
|
"random"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enemyhealth": {
|
"enemyhealth": {
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
aenum
|
aenum
|
||||||
|
fast-enum
|
||||||
@@ -87,6 +87,7 @@ SETTINGSTOPROCESS = {
|
|||||||
"smallkeyshuffle": "keyshuffle",
|
"smallkeyshuffle": "keyshuffle",
|
||||||
"bigkeyshuffle": "bigkeyshuffle",
|
"bigkeyshuffle": "bigkeyshuffle",
|
||||||
"dungeondoorshuffle": "door_shuffle",
|
"dungeondoorshuffle": "door_shuffle",
|
||||||
|
"dungeonintensity": "intensity",
|
||||||
"experimental": "experimental",
|
"experimental": "experimental",
|
||||||
"dungeon_counters": "dungeon_counters"
|
"dungeon_counters": "dungeon_counters"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ def bottom_frame(self, parent, args=None):
|
|||||||
argsDump = vars(guiargs)
|
argsDump = vars(guiargs)
|
||||||
hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"])
|
hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"])
|
||||||
needEnemizer = False
|
needEnemizer = False
|
||||||
if not hasEnemizer:
|
if hasEnemizer:
|
||||||
falsey = [ "none", "default", "vanilla", False, 0 ]
|
falsey = ["none", "default", False, 0]
|
||||||
for enemizerOption in [ "shufflepots", "shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health" ]:
|
for enemizerOption in [ "shufflepots", "shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health" ]:
|
||||||
if enemizerOption in argsDump:
|
if enemizerOption in argsDump:
|
||||||
if isinstance(argsDump[enemizerOption], dict):
|
if isinstance(argsDump[enemizerOption], dict):
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
# Make an OptionMenu with a label and pretty option labels
|
# Make an OptionMenu with a label and pretty option labels
|
||||||
def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs):
|
def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs, config=None):
|
||||||
self = Frame(parent)
|
self = Frame(parent)
|
||||||
|
|
||||||
labels = options
|
labels = options
|
||||||
@@ -96,7 +96,7 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt
|
|||||||
else:
|
else:
|
||||||
self.label.pack(side=LEFT)
|
self.label.pack(side=LEFT)
|
||||||
|
|
||||||
self.selectbox.config(width=20)
|
self.selectbox.config(width=config['width'] if config and config['width'] else 20)
|
||||||
idx = 0
|
idx = 0
|
||||||
default = self.selectbox.options["values"][idx]
|
default = self.selectbox.options["values"][idx]
|
||||||
if managerAttrs is not None and "default" in managerAttrs:
|
if managerAttrs is not None and "default" in managerAttrs:
|
||||||
@@ -166,7 +166,8 @@ def make_textbox(self, parent, label, storageVar, manager, managerAttrs):
|
|||||||
return widget
|
return widget
|
||||||
|
|
||||||
# Make a generic widget
|
# Make a generic widget
|
||||||
def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(), options=None):
|
def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(),
|
||||||
|
options=None, config=None):
|
||||||
widget = None
|
widget = None
|
||||||
if manager is None:
|
if manager is None:
|
||||||
manager = "pack"
|
manager = "pack"
|
||||||
@@ -184,7 +185,7 @@ def make_widget(self, type, parent, label, storageVar=None, manager=None, manage
|
|||||||
elif type == "selectbox":
|
elif type == "selectbox":
|
||||||
if thisStorageVar is None:
|
if thisStorageVar is None:
|
||||||
thisStorageVar = StringVar()
|
thisStorageVar = StringVar()
|
||||||
widget = make_selectbox(self, parent, label, options, thisStorageVar, manager, managerAttrs)
|
widget = make_selectbox(self, parent, label, options, thisStorageVar, manager, managerAttrs, config)
|
||||||
elif type == "spinbox":
|
elif type == "spinbox":
|
||||||
if thisStorageVar is None:
|
if thisStorageVar is None:
|
||||||
thisStorageVar = StringVar()
|
thisStorageVar = StringVar()
|
||||||
@@ -203,13 +204,14 @@ def make_widget_from_dict(self, defn, parent):
|
|||||||
manager = defn["manager"] if "manager" in defn else None
|
manager = defn["manager"] if "manager" in defn else None
|
||||||
managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None
|
managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None
|
||||||
options = defn["options"] if "options" in defn else None
|
options = defn["options"] if "options" in defn else None
|
||||||
|
config = defn["config"] if "config" in defn else None
|
||||||
|
|
||||||
if managerAttrs is None and "default" in defn:
|
if managerAttrs is None and "default" in defn:
|
||||||
managerAttrs = {}
|
managerAttrs = {}
|
||||||
if "default" in defn:
|
if "default" in defn:
|
||||||
managerAttrs["default"] = defn["default"]
|
managerAttrs["default"] = defn["default"]
|
||||||
|
|
||||||
widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options)
|
widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options, config)
|
||||||
widget.type = type
|
widget.type = type
|
||||||
return widget
|
return widget
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user