Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2024-03-25 22:06:59 -05:00
84 changed files with 13023 additions and 1736 deletions
+111 -71
View File
@@ -10,7 +10,6 @@ try:
except ImportError:
from enum import IntFlag as FastEnum
from source.classes.BabelFish import BabelFish
from Utils import int16_as_bytes
from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup
@@ -60,8 +59,6 @@ class World(object):
self._location_cache = {}
self.required_locations = []
self.shuffle_bonk_drops = {}
self.light_world_light_cone = False
self.dark_world_light_cone = False
self.clock_mode = 'none'
self.rupoor_cost = 10
self.lock_aga_door_in_escape = False
@@ -106,11 +103,13 @@ class World(object):
self._portal_cache = {}
self.sanc_portal = {}
self.fish = BabelFish()
self.pot_contents = {}
self.data_tables = {}
self.damage_table = {}
for player in range(1, players + 1):
def set_player_attr(attr, val):
self.__dict__.setdefault(attr, {})[player] = val
set_player_attr('_region_cache', {})
set_player_attr('player_names', [])
set_player_attr('owswaps', [[],[],[]])
@@ -148,6 +147,7 @@ class World(object):
set_player_attr('enemy_shuffle', 'none')
set_player_attr('enemy_health', 'default')
set_player_attr('enemy_damage', 'default')
set_player_attr('any_enemy_logic', 'allow_all')
set_player_attr('beemizer', 0)
set_player_attr('escape_assist', [])
set_player_attr('crystals_needed_for_ganon', 7)
@@ -180,6 +180,7 @@ class World(object):
set_player_attr('exp_cache', defaultdict(dict))
set_player_attr('enabled_entrances', {})
set_player_attr('data_tables', None)
def finish_init(self):
for player in range(1, self.players + 1):
@@ -282,6 +283,9 @@ class World(object):
return dungeon
raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player))
def get_dungeons(self, player):
return [d for d in self.dungeons if d.player == player]
def get_door(self, doorname, player):
door = self.check_for_door(doorname, player)
if door is None:
@@ -461,7 +465,7 @@ class World(object):
def push_precollected(self, item):
item.world = self
if ((item.smallkey and self.keyshuffle[item.player] != 'none')
or (item.bigkey and self.bigkeyshuffle[item.player])):
or (item.bigkey and self.bigkeyshuffle[item.player])):
item.advancement = True
self.precollected_items.append(item)
self.state.collect(item, True)
@@ -475,7 +479,7 @@ class World(object):
item.location = location
item.world = self
if location.player != item.player and location.type == LocationType.Pot:
self.pot_contents[location.player].multiworld_count += 1
self.data_tables[location.player].pot_secret_table.multiworld_count += 1
if collect:
self.state.collect(item, location.event, location)
@@ -842,7 +846,7 @@ class CollectionState(object):
bc_ = self.blocked_connections[player]
for block, crystal in bc_.items():
if (block, crystal) not in terminal_queue and self.possibly_connected_to_dungeon(block.connected_region, player):
terminal_queue.append((block, crystal))
terminal_queue.append((block, crystal))
self.traverse_world(terminal_queue, rrp_, bc_, player)
self.dungeon_limits = None
@@ -865,7 +869,7 @@ class CollectionState(object):
missing_bc = {}
for blocked, crystal in common_bc.items():
if (blocked not in bc and blocked.parent_region in rrp
and self.should_visit(blocked.connected_region, rrp, crystal, player)):
and self.should_visit(blocked.connected_region, rrp, crystal, player)):
missing_bc[blocked] = crystal
for k in missing_bc:
bc[k] = missing_bc[k]
@@ -921,8 +925,8 @@ class CollectionState(object):
rule = key_logic.door_rules[door.name]
key = KeyRuleType.AllowSmall
if (key in rule.new_rules and key_total >= rule.new_rules[key] and door.name not in skip
and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player]
and rule.small_location.item is None):
and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player]
and rule.small_location.item is None):
if paired:
door_candidates.append((door.name, paired.name))
skip.add(paired.name)
@@ -1046,8 +1050,8 @@ class CollectionState(object):
'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Ocarina (Activated)',
'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)',
'Magic Upgrade (1/4)']
or item_name.startswith(('Bottle', 'Small Key', 'Big Key'))
or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass'))))
or item_name.startswith(('Bottle', 'Small Key', 'Big Key'))
or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass'))))
def can_reach(self, spot, resolution_hint=None, player=None):
try:
@@ -1094,7 +1098,6 @@ class CollectionState(object):
self.collect(event.item, True, event)
new_locations = True
def can_reach_blue(self, region, player):
return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either]
@@ -1107,7 +1110,7 @@ class CollectionState(object):
if event.name in flooded_keys.keys():
flood_location = self.world.get_location(flooded_keys[event.name], event.player)
if (flood_location.item and flood_location not in self.locations_checked
and self.location_can_be_flooded(flood_location)):
and self.location_can_be_flooded(flood_location)):
adjusted_checks.remove(event)
if len(adjusted_checks) < len(reachable_events):
return adjusted_checks
@@ -1225,16 +1228,16 @@ class CollectionState(object):
# Warning: This only considers items that are marked as advancement items
diff = self.world.difficulty_requirements[player]
return (
min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit)
+ self.item_count('Sanctuary Heart Container', player)
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4
+ 3 # starting hearts
min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit)
+ self.item_count('Sanctuary Heart Container', player)
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4
+ 3 # starting hearts
)
def can_lift_heavy_rocks(self, player):
return self.has('Titans Mitts', player)
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
if self.has('Magic Upgrade (1/4)', player):
basemagic = 32
@@ -1285,16 +1288,16 @@ class CollectionState(object):
def can_hit_crystal_through_barrier(self, player):
return (self.can_use_bombs(player)
or self.can_shoot_arrows(player)
or self.has('Blue Boomerang', player)
or self.has('Red Boomerang', player)
or self.has('Fire Rod', player)
or self.has('Ice Rod', player)
or self.has('Cane of Somaria', player))
or self.can_shoot_arrows(player)
or self.has('Blue Boomerang', player)
or self.has('Red Boomerang', player)
or self.has('Fire Rod', player)
or self.has('Ice Rod', player)
or self.has('Cane of Somaria', player))
def can_shoot_arrows(self, player):
if self.world.bow_mode[player] in ['retro', 'retro_silvers']:
#todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe
# todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe
return self.has('Bow', player) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player))
return self.has('Bow', player)
@@ -1342,9 +1345,8 @@ class CollectionState(object):
return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player))
def can_avoid_lasers(self, player):
return (self.has('Mirror Shield', player) or
self.has('Cape', player) or
(self.has('Cane of Byrna', player) and self.world.difficulty_adjustments[player] not in ['hard', 'expert']))
return (self.has('Mirror Shield', player) or self.has('Cape', player)
or (self.has('Cane of Byrna', player) and self.world.difficulty_adjustments[player] not in ['hard', 'expert']))
def is_not_bunny(self, region, player):
return self.has_Pearl(player) or not region.can_cause_bunny(player)
@@ -1526,13 +1528,20 @@ class CollectionState(object):
def __getattr__(self, item):
if item.startswith('can_reach_'):
return self.can_reach(item[10])
#elif item.startswith('has_'):
# elif item.startswith('has_'):
# return self.has(item[4])
if item == '__len__':
return
raise RuntimeError('Cannot parse %s.' % item)
@unique
class Terrain(Enum):
Land = 0
Water = 1
@unique
class RegionType(Enum):
Menu = 0
@@ -1558,7 +1567,7 @@ class Region(object):
self.dungeon = None
self.shop = None
self.world = None
self.is_light_world = False # will be set aftermaking connections.
self.is_light_world = False # will be set aftermaking connections.
self.is_dark_world = False
self.spot_type = 'Region'
self.terrain = None
@@ -1624,6 +1633,7 @@ class Entrance(object):
self.recursion_count = 0
self.vanilla = None
self.access_rule = lambda state: True
self.verbose_rule = None
self.player = player
self.door = None
self.hide_path = False
@@ -2049,7 +2059,7 @@ class Door(object):
self.edge_id = None
self.edge_width = None
#portal items
# portal items
self.portalAble = False
self.roomLayout = 0x22 # free scroll- both directions
self.entranceFlag = False
@@ -2501,17 +2511,17 @@ class Portal(object):
return self.door.roomIndex
def relative_coords(self):
y_rel = (self.door.roomIndex & 0xf0) >> 3 #todo: fix the shift!!!!
y_rel = (self.door.roomIndex & 0xf0) >> 3 # todo: fix the shift!!!!
x_rel = (self.door.roomIndex & 0x0f) * 2
quad = self.door.quadrant
if quad == 0:
return [y_rel, y_rel, y_rel, y_rel+1, x_rel, x_rel, x_rel, x_rel+1]
return [y_rel, y_rel, y_rel, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1]
elif quad == 1:
return [y_rel, y_rel, y_rel, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1]
return [y_rel, y_rel, y_rel, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1]
elif quad == 2:
return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel, x_rel, x_rel, x_rel+1]
return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1]
else:
return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1]
return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1]
def scroll_x(self):
x_rel = (self.door.roomIndex & 0x0f) * 2
@@ -2520,7 +2530,7 @@ class Portal(object):
elif self.door.doorIndex == 1:
return [0x80, x_rel]
else:
return [0x00, x_rel+1]
return [0x00, x_rel + 1]
def scroll_y(self):
y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1
@@ -2540,7 +2550,7 @@ class Portal(object):
elif self.door.doorIndex == 1:
return [0xf8, x_rel]
else:
return [0x78, x_rel+1]
return [0x78, x_rel + 1]
# def camera_y(self):
# return [0x87, 0x01]
@@ -2599,20 +2609,22 @@ class Boss(object):
def can_defeat(self, state):
return self.defeat_rule(state, self.player)
class Location(object):
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, player_address=None):
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None,
player_address=None, note=None):
self.name = name
self.parent_region = parent
if forced_item is not None:
from Items import ItemFactory
self.forced_item = ItemFactory([forced_item], player)[0]
self.item = self.forced_item
self.item.location = self
self.event = True
from Items import ItemFactory
self.forced_item = ItemFactory([forced_item], player)[0]
self.item = self.forced_item
self.item.location = self
self.event = True
else:
self.forced_item = None
self.item = None
self.event = False
self.forced_item = None
self.item = None
self.event = False
self.crystal = crystal
self.address = address
self.player_address = player_address
@@ -2624,11 +2636,14 @@ class Location(object):
self.real = not crystal
self.always_allow = lambda item, state: False
self.access_rule = lambda state: True
self.verbose_rule = None
self.item_rule = lambda item: True
self.player = player
self.skip = False
self.type = LocationType.Normal if not crystal else LocationType.Prize
self.pot = None
self.drop = None
self.note = note
def can_fill(self, state, item, check_access=True):
if not self.valid_multiworld(state, item):
@@ -2637,7 +2652,7 @@ class Location(object):
def valid_multiworld(self, state, item):
if self.type == LocationType.Pot and self.player != item.player:
return state.world.pot_contents[self.player].multiworld_count < 256
return state.world.data_tables[self.player].pot_secret_table.multiworld_count < 256
return True
def can_reach(self, state):
@@ -2659,6 +2674,8 @@ class Location(object):
name += f' @ {self.parent_region.dungeon.name}'
if world and world.players > 1:
name += f' ({world.get_player_names(self.player)})'
if self.note:
name += f' ({self.note})'
return name
def can_cause_bunny(self, player):
@@ -2761,12 +2778,14 @@ class Item(object):
class Crystal(Item):
pass
@unique
class ShopType(Enum):
Shop = 0
TakeAny = 1
UpgradeShop = 2
class Shop(object):
def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address):
self.region = region
@@ -2791,7 +2810,7 @@ class Shop(object):
config = self.item_count
from EntranceShuffle import door_addresses
if len(entrances) == 1 and entrances[0].name in door_addresses:
door_id = door_addresses[entrances[0].name][0]+1
door_id = door_addresses[entrances[0].name][0] + 1
else:
door_id = 0
config |= 0x40 # ignore door id
@@ -2799,7 +2818,7 @@ class Shop(object):
config |= 0x80
if self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM
return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00]
return [0x00] + int16_as_bytes(self.room_id) + [door_id, 0x00, config, self.shopkeeper_config, 0x00]
def has_unlimited(self, item):
for inv in self.inventory:
@@ -2947,6 +2966,7 @@ class Spoiler(object):
'enemy_shuffle': self.world.enemy_shuffle,
'enemy_health': self.world.enemy_health,
'enemy_damage': self.world.enemy_damage,
'any_enemy_logic': self.world.any_enemy_logic,
'players': self.world.players,
'teams': self.world.teams,
'experimental': self.world.experimental,
@@ -3077,7 +3097,7 @@ class Spoiler(object):
out['Special'] = self.medallions
out['Bottles'] = self.bottles
if self.hashes:
out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()}
out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team + 1})": hash for (player, team), hash in self.hashes.items()}
if self.shops:
out['Shops'] = self.shops
out['playthrough'] = self.playthrough
@@ -3142,7 +3162,7 @@ class Spoiler(object):
outfile.write('Bonk Drops:'.ljust(line_width) + '%s\n' % yn(self.metadata['bonk_drops'][player]))
outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player])
outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player]))
outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['dropshuffle'][player]))
outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['dropshuffle'][player])
outfile.write('Take Any Caves:'.ljust(line_width) + '%s\n' % self.metadata['take_any'][player])
outfile.write('\n')
outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player])
@@ -3182,6 +3202,8 @@ class Spoiler(object):
outfile.write('\n')
outfile.write('Boss Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['boss_shuffle'][player])
outfile.write('Enemy Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['enemy_shuffle'][player])
if self.metadata['enemy_shuffle'][player] != 'none':
outfile.write('Enemy Logic:'.ljust(line_width) + '%s\n' % self.metadata['any_enemy_logic'][player])
outfile.write('Enemy Health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player])
outfile.write('Enemy Damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player])
outfile.write('\n')
@@ -3191,7 +3213,7 @@ class Spoiler(object):
if self.startinventory:
outfile.write('Starting Inventory:'.ljust(line_width))
outfile.write('\n'.ljust(line_width+1).join(self.startinventory) + '\n')
outfile.write('\n'.ljust(line_width + 1).join(self.startinventory) + '\n')
def hashes_to_file(self, filename):
with open(filename, 'r') as infile:
@@ -3211,7 +3233,7 @@ class Spoiler(object):
if len(self.hashes) > 0:
for team in range(self.world.teams):
player_name = self.world.player_names[player][team]
label = f"Hash - {player_name} (Team {team+1}): " if self.world.teams > 1 else 'Hash: '
label = f"Hash - {player_name} (Team {team + 1}): " if self.world.teams > 1 else 'Hash: '
idx = insert(contents, idx, f'{label}{self.hashes[player, team]}\n')
if self.world.players > 1:
insert(contents, idx, '\n') # return value ignored here, if you want to add more lines
@@ -3285,16 +3307,16 @@ class Spoiler(object):
if self.entrances:
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
outfile.write('\n\nEntrances:\n\n')
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","entrances",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","entrances",entry['exit'])) for entry in self.entrances.values()]))
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta", "entrances", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta", "entrances", entry['exit'])) for entry in self.entrances.values()]))
if self.doors:
outfile.write('\n\nDoors:\n\n')
outfile.write('\n'.join(
['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '',
self.world.fish.translate("meta","doors",entry['entrance']),
self.world.fish.translate("meta", "doors", entry['entrance']),
'<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>',
self.world.fish.translate("meta","doors",entry['exit']),
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for
self.world.fish.translate("meta", "doors", entry['exit']),
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for
entry in self.doors.values()]))
if self.lobbies:
outfile.write('\n\nDungeon Lobbies:\n\n')
@@ -3306,8 +3328,8 @@ class Spoiler(object):
# doorNames: For some reason these come in combined, somehow need to split on the thing to translate
# doorTypes: Small Key, Bombable, Bonkable
outfile.write('\n\nDoor Types:\n\n')
outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta","doors",entry['doorNames']), self.world.fish.translate("meta","doorTypes",entry['type'])) for entry in self.doorTypes.values()]))
outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta", "doors", entry['doorNames']), self.world.fish.translate("meta", "doorTypes", entry['type'])) for entry in self.doorTypes.values()]))
# 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
outfile.write('\n\nLocations:\n\n')
@@ -3316,7 +3338,7 @@ 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
# items: Item names
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':
@@ -3324,12 +3346,28 @@ class Spoiler(object):
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']]))
def extras(self, filename):
# todo: conditional on enemy shuffle mode
with open(filename, 'a') as outfile:
outfile.write('\n\nOverworld Enemies:\n\n')
for player in range(1, self.world.players + 1):
player_tag = ' ' + self.world.get_player_names(player) if self.world.players > 1 else ''
for area, sprite_list in self.world.data_tables[player].ow_enemy_table.items():
for idx, sprite in enumerate(sprite_list):
outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n')
outfile.write('\n\nUnderworld Enemies:\n\n')
for player in range(1, self.world.players + 1):
player_tag = ' ' + self.world.get_player_names(player) if self.world.players > 1 else ''
for area, sprite_list in self.world.data_tables[player].uw_enemy_table.room_map.items():
for idx, sprite in enumerate(sprite_list):
outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n')
def playthrough_to_file(self, filename):
with open(filename, 'a') as outfile:
# 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
outfile.write('\n\nPlaythrough:\n\n')
outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()]))
outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()]))
if self.unreachables:
# 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
@@ -3354,10 +3392,10 @@ class Spoiler(object):
path_lines = []
for region, exit in path:
if exit is not None:
path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit)))
path_lines.append("{} -> {}".format(self.world.fish.translate("meta", "rooms", region), self.world.fish.translate("meta", "entrances", exit)))
else:
path_lines.append(self.world.fish.translate("meta","rooms",region))
path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines)))
path_lines.append(self.world.fish.translate("meta", "rooms", region))
path_listings.append("{}\n {}".format(self.world.fish.translate("meta", "locations", location), "\n => ".join(path_lines)))
outfile.write('\n'.join(path_listings))
@@ -3388,6 +3426,7 @@ dungeon_keys = {
'Universal': 'Small Key (Universal)'
}
class PotItem(FastEnum):
Nothing = 0x0
OneRupee = 0x1
@@ -3469,7 +3508,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4,
# byte 1: LLLW WSS? (logic, mode, sword)
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
world_mode = {"open": 0, "standard": 1, "inverted": 2}
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
# byte 2: GGGD DFFH (goal, diff, item_func, hints)
goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5,
@@ -3484,6 +3523,7 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2}
# new byte 4: TDDD PPPP (tavern shuffle, drop, pottery)
# dropshuffle reserves 2 bits, pottery needs 4)
drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2}
pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7,
'clustered': 8, 'nonempty': 9}
@@ -3553,7 +3593,7 @@ class Settings(object):
| (0x8 if w.standardize_palettes[p] == "original" else 0)
| (0 if w.intensity[p] == "random" else w.intensity[p]),
(0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]),
(0x80 if w.shuffletavern[p] else 0) | (drop_shuffle_mode[w.dropshuffle[p]] << 4) | (pottery_mode[w.pottery[p]]),
(0x80 if w.door_self_loops[p] else 0)
| ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3)
@@ -3584,7 +3624,7 @@ class Settings(object):
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
])
])
return base64.b64encode(code, "+-".encode()).decode()
@staticmethod
@@ -3618,7 +3658,7 @@ class Settings(object):
args.intensity[p] = "random" if intensity == 0 else intensity
args.shuffletavern[p] = True if settings[4] & 0x80 else False
args.dropshuffle[p] = True if settings[4] & 0x10 else False
args.dropshuffle[p] = r(drop_shuffle_mode)[(settings[4] & 0x70) >> 4]
args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F]
args.door_self_loops[p] = True if settings[5] & 0x80 else False
+2
View File
@@ -2,6 +2,7 @@ import logging
import RaceRandom as random
from BaseClasses import Boss, FillError
from source.enemizer.Bossmizer import boss_adjust
def BossFactory(boss, player, on_ice=False):
@@ -309,6 +310,7 @@ def place_bosses(world, player):
raise FillError('Could not place boss for location %s' % loc_text)
place_boss(boss, level, loc, loc_text, world, player)
boss_adjust(world, player)
def place_boss(boss, level, loc, loc_text, world, player):
+5
View File
@@ -1,5 +1,10 @@
# Changelog
## 0.4.0.0
- Fully merged big base ROM changes from upstream, including FastROM
- \~Merged up to date with DR v1.4.1.8~
- Various GFX re-implementation including new GFX for bee traps, bomb bags, etc.
## 0.3.4.2
- Added Shuffle SFX Instruments as post-gen option
- Fixed some issues with Swapped ER failing to place Old Man Cave
+4 -7
View File
@@ -110,7 +110,7 @@ def parse_cli(argv, no_defaults=False):
ret.keyshuffle = 'wild'
if ret.keydropshuffle:
ret.dropshuffle = True
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery
if ret.retro or ret.mode == 'retro':
@@ -145,7 +145,7 @@ def parse_cli(argv, no_defaults=False):
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'shuffle_sfxinstruments',
'shuffle_songinstruments', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'aga_randomness']:
'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic', 'aga_randomness']:
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
if player == 1:
setattr(ret, name, {1: value})
@@ -211,11 +211,11 @@ def parse_settings():
"shufflebosses": "none",
"enemy_damage": "default",
"enemy_health": "default",
"enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"),
'any_enemy_logic': 'allow_all',
"shopsanity": False,
"keydropshuffle": False,
"dropshuffle": False,
"dropshuffle": "none",
"pottery": "none",
"colorizepots": True,
"shufflepots": False,
@@ -366,9 +366,6 @@ def parse_settings():
"notes": ""
}
if sys.platform.lower().find("windows"):
settings["enemizercli"] += ".exe"
# read saved settings file if it exists and set these
settings_path = os.path.join(".", "resources", "user", "settings.json")
settings = apply_settings_file(settings, settings_path)
+25 -12
View File
@@ -216,6 +216,7 @@ def valid_connection(region, std_flag, world, player):
return region and (region.type == RegionType.Dungeon or region.name in world.inaccessible_regions[player] or
(std_flag and region.name == 'Hyrule Castle Ledge'))
def vanilla_key_logic(world, player):
builders = []
world.dungeon_layouts[player] = {}
@@ -263,7 +264,7 @@ def vanilla_key_logic(world, player):
log_key_logic(builder.name, key_layout.key_logic)
# special adjustments for vanilla
if world.keyshuffle[player] != 'universal':
if world.mode[player] != 'standard' and not world.dropshuffle[player]:
if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none' :
# adjust hc doors
def adjust_hc_door(door_rule):
if door_rule.new_rules[KeyRuleType.WorstCase] == 3:
@@ -903,10 +904,10 @@ def main_dungeon_pool(dungeon_pool, world, player):
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items))
target_items = 34
if world.keyshuffle[player] == 'universal':
target_items += 1 if world.dropshuffle[player] else 0 # the hc big key
target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key
else:
target_items += 29 # small keys in chests
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
target_items += 14 # 13 dropped smalls + 1 big
if world.pottery[player] not in ['none', 'cave']:
target_items += 19 # 19 pot keys
@@ -1314,10 +1315,10 @@ def cross_dungeon(world, player):
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items))
target_items = 34
if world.keyshuffle[player] == 'universal':
target_items += 1 if world.dropshuffle[player] else 0 # the hc big key
target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key
else:
target_items += 29 # small keys in chests
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
target_items += 14 # 13 dropped smalls + 1 big
if world.pottery[player] not in ['none', 'cave']:
target_items += 19 # 19 pot keys
@@ -1403,7 +1404,7 @@ def assign_cross_keys(dungeon_builders, world, player):
start = time.process_time()
if world.keyshuffle[player] == 'universal':
remaining = 29
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
remaining += 13
if world.pottery[player] not in ['none', 'cave']:
remaining += 19
@@ -2437,20 +2438,25 @@ def change_door_to_trap(d, world, player):
if d.entrance.connected_region is not None and d.blocked:
d.entrance.connected_region.entrances.remove(d.entrance)
d.entrance.connected_region = None
if d.dependents:
for dep in d.dependents:
if dep.entrance.connected_region is not None:
dep.entrance.connected_region.remove(dep.entrance)
dep.entrance.connected_region = None
trap_door_exceptions = {
'PoD Mimics 2 SW', 'TR Twin Pokeys NW', 'Thieves Blocked Entry SW', 'Hyrule Dungeon Armory Interior Key Door N',
'Desert Compass Key Door WN', 'TR Tile Room SE', 'Mire Cross SW', 'Tower Circle of Pots ES',
'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S',
'PoD Mimics 1 SW', 'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S',
'Skull 2 East Lobby WS', 'GT Hope Room WN', 'Eastern Courtyard Ledge S', 'Ice Lobby SE', 'GT Speed Torch WN',
'Ice Switch Room ES', 'Ice Switch Room NE', 'Skull Torch Room WS', 'GT Speed Torch NE', 'GT Speed Torch WS',
'GT Torch Cross WN', 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Torches WN', 'PoD Lobby N', 'PoD Middle Cage S',
'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'GT Conveyor Cross EN', 'Eastern Pot Switch WN',
'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Eastern Pot Switch WN',
'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Eastern Dark Square Key Door WN', 'Eastern Lobby NW',
'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Desert Back Lobby S', 'Desert West S',
'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Ice Cross Right ES', 'Desert Back Lobby S', 'Desert West S',
'Desert West Lobby ES', 'Mire Hidden Shooters SE', 'Mire Hidden Shooters ES', 'Mire Hidden Shooters WS',
'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS',
'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', 'GT Conveyor Cross EN',
}
@@ -3594,6 +3600,7 @@ class DROptions(Flag):
Hide_Total = 0x100
DarkWorld_Spawns = 0x200
BigKeyDoor_Shuffle = 0x400
EnemyDropIndicator = 0x800 # if on, enemy drop indicator show, else it doesn't
# DATA GOES DOWN HERE
@@ -3666,8 +3673,10 @@ logical_connections = [
('PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony - Ranged Crystal'),
('PoD Map Balcony Ranged Crystal Exit', 'PoD Map Balcony'),
('PoD Basement Ledge Drop Down', 'PoD Stalfos Basement'),
('PoD Falling Bridge Path N', 'PoD Falling Bridge Ledge'),
('PoD Falling Bridge Path S', 'PoD Falling Bridge'),
('PoD Falling Bridge Path N', 'PoD Falling Bridge Mid'),
('PoD Falling Bridge Path S', 'PoD Falling Bridge Mid'),
('PoD Falling Bridge Mid Path S', 'PoD Falling Bridge'),
('PoD Falling Bridge Mid Path N', 'PoD Falling Bridge Ledge'),
('PoD Bow Statue Left to Right Barrier - Orange', 'PoD Bow Statue Right'),
('PoD Bow Statue Left to Right Bypass', 'PoD Bow Statue Right'),
('PoD Bow Statue Left to Crystal', 'PoD Bow Statue Left - Crystal'),
@@ -3755,11 +3764,14 @@ logical_connections = [
('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'),
('Ice Cross Right Push Block Top', 'Ice Bomb Drop'),
('Ice Bomb Drop Path', 'Ice Bomb Drop - Top'),
('Ice Conveyor to Crystal', 'Ice Conveyor - Crystal'),
('Ice Conveyor Crystal Exit', 'Ice Conveyor'),
('Ice Big Key Push Block', 'Ice Dead End'),
('Ice Bomb Jump Ledge Orange Barrier', 'Ice Bomb Jump Catwalk'),
('Ice Bomb Jump Catwalk Orange Barrier', 'Ice Bomb Jump Ledge'),
('Ice Right H Path', 'Ice Hammer Block'),
('Ice Hammer Block Path', 'Ice Right H'),
('Ice Hookshot Ledge Path', 'Ice Hookshot Balcony'),
('Ice Hookshot Balcony Path', 'Ice Hookshot Ledge'),
('Ice Crystal Right Orange Barrier', 'Ice Crystal Left'),
@@ -4130,6 +4142,7 @@ interior_doors = [
('PoD Lobby NE', 'PoD Middle Cage SE'),
('PoD Warp Hint SE', 'PoD Jelly Hall NE'),
('PoD Jelly Hall NW', 'PoD Mimics 1 SW'),
('PoD Map Balcony ES', 'PoD Fairy Pool WS'),
('PoD Falling Bridge EN', 'PoD Compass Room WN'),
('PoD Compass Room SE', 'PoD Harmless Hellway NE'),
('PoD Mimics 2 NW', 'PoD Bow Statue SW'),
+13 -6
View File
@@ -250,7 +250,7 @@ def create_doors(world, player):
create_door(player, 'Desert Tiles 2 NE', Intr).dir(No, 0x43, Right, High).small_key().pos(1),
create_door(player, 'Desert Wall Slide SE', Intr).dir(So, 0x43, Right, High).small_key().pos(1),
create_door(player, 'Desert Wall Slide NW', Nrml).dir(No, 0x43, Left, High).big_key().pos(0).no_entrance(),
create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), problem with enemizer
create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00),
# Hera
create_door(player, 'Hera Lobby S', Nrml).dir(So, 0x77, Mid, Low).pos(0).portal(Z, 0x22, 1),
@@ -410,6 +410,8 @@ def create_doors(world, player):
create_door(player, 'PoD Map Balcony Drop Down', Lgcl),
create_door(player, 'PoD Map Balcony to Ranged Crystal', Lgcl),
create_door(player, 'PoD Map Balcony Ranged Crystal Exit', Lgcl),
create_door(player, 'PoD Map Balcony ES', Intr).dir(Ea, 0x2b, Bot, High).pos(0),
create_door(player, 'PoD Fairy Pool WS', Intr).dir(We, 0x2b, Bot, High).pos(0),
create_door(player, 'PoD Map Balcony WS', Nrml).dir(We, 0x2b, Bot, High).pos(1),
create_door(player, 'PoD Map Balcony South Stairs', StrS).dir(So, 0x2b, Left, High),
create_door(player, 'PoD Conveyor North Stairs', StrS).dir(No, 0x3b, Left, High),
@@ -425,6 +427,8 @@ def create_doors(world, player):
create_door(player, 'PoD Falling Bridge EN', Intr).dir(Ea, 0x1a, Top, High).pos(4),
create_door(player, 'PoD Falling Bridge Path N', Lgcl),
create_door(player, 'PoD Falling Bridge Path S', Lgcl),
create_door(player, 'PoD Falling Bridge Mid Path S', Lgcl),
create_door(player, 'PoD Falling Bridge Mid Path N', Lgcl),
create_door(player, 'PoD Big Chest Balcony W', Nrml).dir(We, 0x1a, Mid, High).pos(2),
create_door(player, 'PoD Dark Maze EN', Nrml).dir(Ea, 0x19, Top, High).small_key().pos(1),
create_door(player, 'PoD Dark Maze E', Nrml).dir(Ea, 0x19, Mid, High).pos(0),
@@ -736,6 +740,7 @@ def create_doors(world, player):
create_door(player, 'Ice Cross Top Push Block Right', Lgcl), # dynamic
create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3).portal(X, 0x00),
create_door(player, 'Ice Cross Right ES', Nrml).dir(Ea, 0x1e, Bot, High).trap(0x4).pos(0),
create_door(player, 'Ice Bomb Drop Path', Lgcl),
create_door(player, 'Ice Bomb Drop Hole', Hole),
create_door(player, 'Ice Compass Room NE', Nrml).dir(No, 0x2e, Right, High).pos(0),
create_door(player, 'Ice Pengator Switch WS', Nrml).dir(We, 0x1f, Bot, High).trap(0x4).pos(0),
@@ -767,6 +772,8 @@ def create_doors(world, player):
create_door(player, 'Ice Spike Room WS', Nrml).dir(We, 0x5f, Bot, High).small_key().pos(0),
create_door(player, 'Ice Spike Room Down Stairs', Sprl).dir(Dn, 0x5f, 3, HTH).ss(Z, 0x11, 0x48, True, True),
create_door(player, 'Ice Spike Room Up Stairs', Sprl).dir(Up, 0x5f, 4, HTH).ss(Z, 0x1a, 0xa4, True, True),
create_door(player, 'Ice Right H Path', Lgcl),
create_door(player, 'Ice Hammer Block Path', Lgcl),
create_door(player, 'Ice Hammer Block Down Stairs', Sprl).dir(Dn, 0x3f, 0, HTH).ss(Z, 0x11, 0xb8, True, True).kill(),
create_door(player, 'Ice Hammer Block ES', Intr).dir(Ea, 0x3f, Bot, High).pos(0),
create_door(player, 'Ice Tongue Pull WS', Intr).dir(We, 0x3f, Bot, High).pos(0),
@@ -1067,7 +1074,7 @@ def create_doors(world, player):
create_door(player, 'TR Final Abyss Balcony Path', Lgcl),
create_door(player, 'TR Final Abyss Ledge Path', Lgcl),
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), # .portal(Z, 0x00), -enemizer doesn't work
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00),
create_door(player, 'GT Lobby S', Nrml).dir(So, 0x0c, Mid, High).pos(0).portal(Z, 0x22),
create_door(player, 'GT Lobby Left Down Stairs', Sprl).dir(Dn, 0x0c, 1, HTL).ss(A, 0x0f, 0x80),
@@ -1505,8 +1512,8 @@ def create_doors(world, player):
world.get_door('TR Big Chest Entrance SE', player).passage = False
world.get_door('Sewers Secret Room Key Door S', player).dungeonLink = 'Hyrule Castle'
world.get_door('Desert Cannonball S', player).dead_end()
# world.get_door('Desert Boss SW', player).dead_end()
# world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace'
world.get_door('Desert Boss SW', player).dead_end()
world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace'
world.get_door('Skull 1 Lobby S', player).dungeonLink = 'Skull Woods'
world.get_door('Skull Map Room SE', player).dungeonLink = 'Skull Woods'
world.get_door('Skull Spike Corner SW', player).dungeonLink = 'Skull Woods'
@@ -1516,8 +1523,8 @@ def create_doors(world, player):
world.get_door('Mire Right Bridge SE', player).dead_end(allowPassage=True)
world.get_door('TR Roller Room SW', player).dead_end()
world.get_door('TR Tile Room SE', player).dead_end()
# world.get_door('TR Boss SW', player).dead_end()
# world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock'
world.get_door('TR Boss SW', player).dead_end()
world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock'
world.get_door('GT Petting Zoo SE', player).dead_end()
world.get_door('GT DMs Room SW', player).dead_end()
world.get_door("GT Bob\'s Room SE", player).passage = False
+7 -5
View File
@@ -98,10 +98,11 @@ pod_regions = [
'PoD Lobby', 'PoD Left Cage', 'PoD Middle Cage', 'PoD Shooter Room', 'PoD Pit Room', 'PoD Pit Room Blocked',
'PoD Arena Main', 'PoD Arena Main - Ranged Crystal', 'PoD Arena North', 'PoD Arena Bridge', 'PoD Arena Bridge - Ranged Crystal',
'PoD Arena Landing', 'PoD Arena Right', 'PoD Arena Right - Ranged Crystal', 'PoD Arena Ledge', 'PoD Arena Ledge - Ranged Crystal', 'PoD Sexy Statue',
'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Conveyor', 'PoD Mimics 1', 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room',
'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge',
'PoD Falling Bridge Ledge', 'PoD Dark Maze', 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement',
'PoD Harmless Hellway', 'PoD Mimics 2', 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal',
'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Fairy Pool', 'PoD Conveyor', 'PoD Mimics 1',
'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', 'PoD Stalfos Basement', 'PoD Basement Ledge',
'PoD Big Key Landing', 'PoD Falling Bridge', 'PoD Falling Bridge Mid', 'PoD Falling Bridge Ledge', 'PoD Dark Maze',
'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', 'PoD Harmless Hellway', 'PoD Mimics 2',
'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal',
'PoD Dark Pegs Landing', 'PoD Dark Pegs Right', 'PoD Dark Pegs Middle', 'PoD Dark Pegs Left', 'PoD Dark Pegs Landing - Ranged Crystal',
'PoD Dark Pegs Middle - Ranged Crystal', 'PoD Dark Pegs Left - Ranged Crystal', 'PoD Lonely Turtle', 'PoD Turtle Party',
'PoD Dark Alley', 'PoD Callback', 'PoD Boss', 'Palace of Darkness Portal'
@@ -144,7 +145,8 @@ thieves_regions = [
ice_regions = [
'Ice Lobby', 'Ice Jelly Key', 'Ice Floor Switch', 'Ice Cross Left', 'Ice Cross Bottom', 'Ice Cross Right',
'Ice Cross Top', 'Ice Compass Room', 'Ice Pengator Switch', 'Ice Dead End', 'Ice Big Key', 'Ice Bomb Drop',
'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk', 'Ice Narrow Corridor',
'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk',
'Ice Narrow Corridor', 'Ice Right H', 'Ice Bomb Drop - Top',
'Ice Pengator Trap', 'Ice Spike Cross', 'Ice Firebar', 'Ice Falling Square', 'Ice Spike Room', 'Ice Hammer Block',
'Ice Tongue Pull', 'Ice Freezors', 'Ice Freezors Ledge', 'Ice Tall Hint', 'Ice Hookshot Ledge',
'Ice Hookshot Balcony', 'Ice Spikeball', 'Ice Lonely Freezor', 'Iced T', 'Ice Catwalk', 'Ice Many Pots',
+61 -47
View File
@@ -1511,7 +1511,7 @@ def connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, playe
}
for region_name in world.inaccessible_regions[player]:
if (world.logic[player] in ['noglitches', 'minorglitches'] and region_name in glitch_regions) \
or (region_name == 'Pyramid Exit Ledge' and (world.shuffle[player] != 'insanity' or world.is_tile_swapped(0x1b, player))) \
or (region_name == 'Pyramid Exit Ledge' and (world.shuffle[player] not in ['district', 'insanity'] or world.is_tile_swapped(0x1b, player))) \
or (region_name == 'Spiral Mimic Ledge Extend' and not world.is_tile_swapped(0x05, player)):
# removing irrelevant and resolved regions
inaccessible_regions.remove(region_name)
@@ -2155,20 +2155,20 @@ Exit_Pool_Base = ['Links House Exit',
# these are connections that cannot be shuffled and always exist.
# They link together underworld regions
mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'),
mandatory_connections = [
('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'),
('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'),
('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'),
('Kakariko Well (top to back)', 'Kakariko Well (back)'),
('Blinds Hideout N', 'Blinds Hideout (Top)'),
('Bat Cave Door', 'Bat Cave (left)'),
('Sewer Drop', 'Sewers Rat Path'),
('Old Man Cave Dropdown', 'Old Man Cave'),
('Old Man House Front to Back', 'Old Man House Back'),
('Old Man House Back to Front', 'Old Man House'),
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'),
('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'),
('Old Man Cave Dropdown', 'Old Man Cave (West)'),
('Old Man Cave W', 'Old Man Cave (West)'),
('Old Man Cave E', 'Old Man Cave (East)'),
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'),
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'),
('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'),
('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'),
('Old Man House Front to Back', 'Old Man House Back'),
('Old Man House Back to Front', 'Old Man House'),
('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'),
('Paradox Shop', 'Paradox Shop'),
('Paradox Cave Push Block Reverse', 'Paradox Cave Chest Area'),
@@ -2179,89 +2179,102 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide
('Fairy Ascension Cave Climb', 'Fairy Ascension Cave (Top)'),
('Fairy Ascension Cave Pots', 'Fairy Ascension Cave (Bottom)'),
('Fairy Ascension Cave Drop', 'Fairy Ascension Cave (Drop)'),
('Missing Smith', 'Missing Smith'),
('Bumper Cave Bottom to Top', 'Bumper Cave (top)'),
('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'),
('Superbunny Cave Climb', 'Superbunny Cave (Top)'),
('Sewer Drop', 'Sewers Rat Path'),
('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'),
('Kakariko Well (top to back)', 'Kakariko Well (back)'),
('Blinds Hideout N', 'Blinds Hideout (Top)'),
('Bat Cave Door', 'Bat Cave (left)'),
('Good Bee Cave Front to Back', 'Good Bee Cave (back)'),
('Good Bee Cave Back to Front', 'Good Bee Cave'),
('Capacity Upgrade East', 'Capacity Fairy Pool'),
('Capacity Fairy Pool West', 'Capacity Upgrade'),
('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'),
('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'),
('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'),
('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'),
('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'),
('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'),
('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'),
('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'),
('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'),
('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'),
('Superbunny Cave Climb', 'Superbunny Cave (Top)'),
('Bumper Cave Bottom to Top', 'Bumper Cave (top)'),
('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'),
('Missing Smith', 'Missing Smith'),
('Ganon Drop', 'Bottom of Pyramid')
]
# non-shuffled entrance links
default_connections = [('Bonk Fairy (Light)', 'Bonk Fairy (Light)'),
('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'),
('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'),
('Lost Woods Gamble', 'Lost Woods Gamble'),
default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'),
('Fortune Teller (Light)', 'Fortune Teller (Light)'),
('Bush Covered House', 'Bush Covered House'),
('Long Fairy Cave', 'Long Fairy Cave'), # near East Light World Teleporter
('Good Bee Cave', 'Good Bee Cave'),
('Kakariko Gamble Game', 'Kakariko Gamble Game'),
('Bonk Fairy (Light)', 'Bonk Fairy (Light)'),
('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'),
('Long Fairy Cave', 'Long Fairy Cave'),
('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'),
('Good Bee Cave', 'Good Bee Cave'),
('East Dark World Hint', 'East Dark World Hint'),
('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'),
('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint'),
('Dark Sanctuary Hint', 'Dark Sanctuary Hint'),
('Fortune Teller (Dark)', 'Fortune Teller (Dark)'),
('Dark Sanctuary Hint', 'Dark Sanctuary Hint'),
('Archery Game', 'Archery Game'),
('Mire Fairy', 'Mire Healer Fairy')
('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'),
('East Dark World Hint', 'East Dark World Hint'),
('Mire Fairy', 'Mire Healer Fairy'),
('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint')
]
default_takeany_connections = [('Light Hype Fairy', 'Light Hype Fairy'),
('Desert Fairy', 'Desert Healer Fairy'),
('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'),
('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy'),
('Bonk Fairy (Dark)', 'Bonk Fairy (Dark)'),
('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy')
('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy')
]
default_pot_connections = [('Lumberjack House', 'Lumberjack House'),
('Hookshot Fairy', 'Hookshot Fairy'),
('Snitch Lady (East)', 'Snitch Lady (East)'),
('Snitch Lady (West)', 'Snitch Lady (West)'),
('Tavern (Front)', 'Tavern (Front)'),
('Light World Bomb Hut', 'Light World Bomb Hut'),
('Tavern (Front)', 'Tavern (Front)'),
('20 Rupee Cave', '20 Rupee Cave'),
('50 Rupee Cave', '50 Rupee Cave'),
('Hookshot Fairy', 'Hookshot Fairy'),
('Palace of Darkness Hint', 'Palace of Darkness Hint'),
('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave'),
('Mire Hint', 'Mire Hint')
('Mire Hint', 'Mire Hint'),
('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave')
]
default_connector_connections = [('Old Man Cave (West)', 'Old Man Cave Exit (West)'),
('Old Man Cave (East)', 'Old Man Cave Exit (East)'),
('Old Man House (Bottom)', 'Old Man House Exit (Bottom)'),
('Old Man House (Top)', 'Old Man House Exit (Top)'),
default_connector_connections = [('Death Mountain Return Cave (West)', 'Death Mountain Return Cave Exit (West)'),
('Death Mountain Return Cave (East)', 'Death Mountain Return Cave Exit (East)'),
('Death Mountain Return Cave (West)', 'Death Mountain Return Cave Exit (West)'),
('Spectacle Rock Cave Peak', 'Spectacle Rock Cave Exit (Peak)'),
('Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave Exit'),
('Spectacle Rock Cave', 'Spectacle Rock Cave Exit (Top)'),
('Old Man Cave (East)', 'Old Man Cave Exit (East)'),
('Old Man Cave (West)', 'Old Man Cave Exit (West)'),
('Old Man House (Bottom)', 'Old Man House Exit (Bottom)'),
('Old Man House (Top)', 'Old Man House Exit (Top)'),
('Spiral Cave', 'Spiral Cave Exit (Top)'),
('Spiral Cave (Bottom)', 'Spiral Cave Exit'),
('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave Exit (Bottom)'),
('Fairy Ascension Cave (Top)', 'Fairy Ascension Cave Exit (Top)'),
('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave Exit (Bottom)'),
('Paradox Cave (Bottom)', 'Paradox Cave Exit (Bottom)'),
('Paradox Cave (Middle)', 'Paradox Cave Exit (Middle)'),
('Paradox Cave (Top)', 'Paradox Cave Exit (Top)'),
('Elder House (East)', 'Elder House Exit (East)'),
('Elder House (West)', 'Elder House Exit (West)'),
('Two Brothers House (East)', 'Two Brothers House Exit (East)'),
('Elder House (East)', 'Elder House Exit (East)'),
('Two Brothers House (West)', 'Two Brothers House Exit (West)'),
('Bumper Cave (Top)', 'Bumper Cave Exit (Top)'),
('Bumper Cave (Bottom)', 'Bumper Cave Exit (Bottom)'),
('Two Brothers House (East)', 'Two Brothers House Exit (East)'),
('Hookshot Cave Back Entrance', 'Hookshot Cave Back Exit'),
('Hookshot Cave', 'Hookshot Cave Front Exit'),
('Superbunny Cave (Top)', 'Superbunny Cave Exit (Top)'),
('Superbunny Cave (Bottom)', 'Superbunny Cave Exit (Bottom)'),
('Hookshot Cave', 'Hookshot Cave Front Exit'),
('Hookshot Cave Back Entrance', 'Hookshot Cave Back Exit')
('Bumper Cave (Bottom)', 'Bumper Cave Exit (Bottom)'),
('Bumper Cave (Top)', 'Bumper Cave Exit (Top)')
]
default_item_connections = [('Links House', 'Links House Exit'),
('Mimic Cave', 'Mimic Cave'),
default_item_connections = [('Mimic Cave', 'Mimic Cave'),
('Waterfall of Wishing', 'Waterfall of Wishing'),
('Bonk Rock Cave', 'Bonk Rock Cave'),
('Graveyard Cave', 'Graveyard Cave'),
@@ -2273,6 +2286,7 @@ default_item_connections = [('Links House', 'Links House Exit'),
('Sahasrahlas Hut', 'Sahasrahlas Hut'),
('Blacksmiths Hut', 'Blacksmiths Hut'),
('Library', 'Library'),
('Links House', 'Links House Exit'),
('Checkerboard Cave', 'Checkerboard Cave'),
('Aginahs Cave', 'Aginahs Cave'),
('Cave 45', 'Cave 45'),
+20 -28
View File
@@ -10,7 +10,7 @@ from BaseClasses import CollectionState, FillError, LocationType
from Items import ItemFactory
from Regions import shop_to_location_table, retro_shops
from source.item.FillUtil import filter_locations, classify_major_items, replace_trash_item, vanilla_fallback
from source.item.FillUtil import filter_pot_locations, valid_pot_items
from source.item.FillUtil import filter_special_locations, valid_pot_items
def get_dungeon_item_pool(world):
@@ -284,9 +284,10 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp
return spot_to_fill
return None
# explicitly fail these cases
elif world.algorithm in ['dungeon_only', 'major_only']:
elif world.algorithm in ['dungeon_only', 'major_only', 'district']:
raise FillError(f'Rare placement for {world.algorithm} detected. {item_to_place} unable to be placed.'
f' Try a different seed')
# I don't think any algorithm uses fallback placement anymore, vanilla is special. Others simply fail.
else:
other_locations = [x for x in locations if x not in attempted]
for location in other_locations:
@@ -386,28 +387,28 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# get items to distribute
classify_major_items(world)
# handle pot shuffle
pots_used = False
pot_item_pool = collections.defaultdict(list)
# handle special case fast fill
locations_used = False
location_item_pool = collections.defaultdict(list)
# guarantee one big magic in a bonk location
for player in range(1, world.players + 1):
if world.shuffle_bonk_drops[player]:
for item in world.itempool:
if item.name in ['Big Magic'] and item.player == player:
pot_item_pool[player].append(item)
location_item_pool[player].append(item)
break
from Regions import bonk_prize_table
for player, magic_pool in pot_item_pool.items():
for player, magic_pool in location_item_pool.items():
if len(magic_pool) > 0:
world.itempool.remove(magic_pool[0])
pot_locations = [location for location in fill_locations if location.player == player
bonk_locations = [location for location in fill_locations if location.player == player
and location.name in [n for n, (_, _, aga, _, _, _) in bonk_prize_table.items() if not aga]]
pot_locations = filter_pot_locations(pot_locations, world)
fast_fill_helper(world, magic_pool, pot_locations)
pots_used = True
bonk_locations = filter_special_locations(bonk_locations, world, lambda l: l.name == 'Kakariko Portal Tree')
fast_fill_helper(world, magic_pool, bonk_locations)
locations_used = True
if pots_used:
if locations_used:
fill_locations = world.get_unfilled_locations()
random.shuffle(fill_locations)
@@ -498,6 +499,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
if world.players > 1:
fast_fill_pot_for_multiworld(world, restitempool, fill_locations)
# todo: fast fill drops?
if world.algorithm == 'vanilla_fill':
fast_vanilla_fill(world, restitempool, fill_locations)
else:
@@ -507,7 +509,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
unfilled = [location.name for location in fill_locations]
if unplaced or unfilled:
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
ensure_good_pots(world)
ensure_good_items(world)
def config_sort(world):
@@ -537,36 +539,26 @@ def calc_trash_locations(world, player):
return gt_count, total_count
def ensure_good_pots(world, write_skips=False):
def ensure_good_items(world, write_skips=False):
for loc in world.get_locations():
if loc.item is None:
loc.item = ItemFactory('Nothing', loc.player)
# convert Arrows 5 and Nothing when necessary
if (loc.item.name in {'Nothing'}
if (loc.item.name in {'Arrows (5)', 'Nothing'}
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player)
if (loc.item.name in {'Arrows (5)'}
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player)
# # can be placed here by multiworld balancing or shop balancing
# # change it to something normal for the player it got swapped to
# elif (loc.item.name in {'Chicken', 'Big Magic'}
# and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
# if loc.type == LocationType.Pot:
# loc.item.player = loc.player
# else:
# loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player)
# do the arrow retro check
if world.bow_mode[loc.item.player].startswith('retro') and loc.item.name in {'Arrows (5)', 'Arrows (10)'}:
loc.item = ItemFactory('Rupees (5)', loc.item.player)
# don't write out all pots to spoiler
# todo: skip uninteresting enemy drops
if write_skips:
if loc.type in [LocationType.Pot, LocationType.Bonk] and loc.item.name in valid_pot_items:
loc.skip = True
invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)',
'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'}
'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic', 'Fairy': 'Small Heart'}
def fast_fill_helper(world, item_pool, fill_locations):
@@ -601,7 +593,7 @@ def fast_fill_pot_for_multiworld(world, item_pool, fill_locations):
if loc.type == LocationType.Pot:
pot_fill_locations[loc.player].append(loc)
for player in range(1, world.players+1):
flex = 256 - world.pot_contents[player].multiworld_count
flex = 256 - world.data_tables[player].pot_secret_table.multiworld_count
fill_count = len(pot_fill_locations[player]) - flex
if fill_count > 0:
fill_spots = random.sample(pot_fill_locations[player], fill_count)
+1 -1
View File
@@ -46,7 +46,7 @@ def save_settings(gui, args, filename):
output_args = {}
settings = ["create_rom", "suppress_rom", "bps", "create_spoiler", "suppress_spoiler",
"calc_playthrough", "skip_playthrough", "print_custom_yaml",
"settingsonload", "rom", "enemizercli", "outputpath"]
"settingsonload", "rom", "outputpath"]
if filename == "settings.json":
for s in settings:
output_args[s] = args[s]
+8 -9
View File
@@ -56,18 +56,17 @@ class InitialSram:
def pre_set_overworld_flag(self, owid, bitmask):
self._or_value(OVERWORLD_DATA+owid, bitmask)
def pre_open_tr_bomb_doors(self):
self._or_value(ROOM_DATA+0x47, 0x80)
self._or_value(ROOM_DATA+0x01AB, 0x80)
def set_starting_equipment(self, world: object, player: int):
equip = [0] * (0x340 + 0x4F)
equip[0x36C] = 0x18
equip[0x36D] = 0x18
equip[0x379] = 0x68
if world.bombbag[player]:
starting_max_bombs = 0
else:
starting_max_bombs = 10
starting_max_arrows = 30
starting_bomb_cap_upgrades = 0
starting_arrow_cap_upgrades = 0
starting_bomb_cap_upgrades = 10 if not world.bombbag[player] else 0
starting_arrow_cap_upgrades = 30
starting_bombs = 0
starting_arrows = 0
@@ -218,8 +217,8 @@ class InitialSram:
equip[0x370] = min(starting_bomb_cap_upgrades, 50)
equip[0x371] = min(starting_arrow_cap_upgrades, 70)
equip[0x343] = min(starting_bombs, (equip[0x370] + starting_max_bombs))
equip[0x377] = min(starting_arrows, (equip[0x371] + starting_max_arrows))
equip[0x343] = min(starting_bombs, equip[0x370])
equip[0x377] = min(starting_arrows, equip[0x371])
# Assertion and copy equip to initial_sram_bytes
assert equip[:0x340] == [0] * 0x340
+36 -25
View File
@@ -11,6 +11,7 @@ from PotShuffle import vanilla_pots
from Tables import bonk_prize_lookup
from Items import ItemFactory
from source.dungeon.EnemyList import add_drop_contents
from source.item.FillUtil import trash_items, pot_items
import source.classes.constants as CONST
@@ -199,8 +200,7 @@ def generate_itempool(world, player):
world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
if world.goal[player] in ['triforcehunt', 'trinity']:
region = world.get_region('Hyrule Castle Courtyard',player)
region = world.get_region('Hyrule Castle Courtyard', player)
loc = Location(player, "Murahdahla", parent=region)
region.locations.append(loc)
world.dynamic_locations.append(loc)
@@ -305,7 +305,7 @@ def generate_itempool(world, player):
# set up item pool
skip_pool_adjustments = False
if world.customizer and world.customizer.get_item_pool():
if world.customizer and world.customizer.get_item_pool() and player in world.customizer.get_item_pool():
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = make_customizer_pool(world, player)
skip_pool_adjustments = True
elif world.custom and player in world.customitemarray:
@@ -327,13 +327,6 @@ def generate_itempool(world, player):
for _ in range(0, amt):
pool.append('Rupees (20)')
if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']:
# In HMG force swamp smalls in pots to allow getting out of swamp palace
placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)'
placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)'
pool.remove('Small Key (Swamp Palace)')
pool.remove('Small Key (Swamp Palace)')
start_inventory = list(world.precollected_items)
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
@@ -410,6 +403,19 @@ def generate_itempool(world, player):
or (item.map and world.mapshuffle[player])
or (item.compass and world.compassshuffle[player]))])
if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']:
# In HMG force swamp smalls in pots to allow getting out of swamp palace
loc = world.get_location('Swamp Palace - Trench 1 Pot Key', player)
world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False)
loc.event = True
loc.locked = True
loc = world.get_location('Swamp Palace - Pot Row Pot Key', player)
world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False)
loc.event = True
loc.locked = True
world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player))
world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player))
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
@@ -468,7 +474,7 @@ def generate_itempool(world, player):
if world.take_any[player] != 'none':
set_up_take_anys(world, player, skip_pool_adjustments)
if world.keyshuffle[player] == 'universal':
if world.dropshuffle[player] and not skip_pool_adjustments:
if world.dropshuffle[player] != 'none' and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13
if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19
@@ -482,6 +488,9 @@ def generate_itempool(world, player):
create_dynamic_bonkdrop_locations(world, player)
add_bonkdrop_contents(world, player)
if world.dropshuffle[player] == 'underworld' and not skip_pool_adjustments:
add_drop_contents(world, player)
# modfiy based on start inventory, if any
modify_pool_for_start_inventory(start_inventory, world, player)
@@ -513,6 +522,8 @@ def generate_itempool(world, player):
world.itempool.append(ItemFactory(item_name, player))
take_any_locations = [
'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut',
'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)',
@@ -815,6 +826,7 @@ def set_up_shops(world, player):
else:
cap_shop = world.get_region('Capacity Upgrade', player).shop
cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbag
cap_shop.inventory[1] = None
def customize_shops(world, player):
@@ -1369,20 +1381,19 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
pool.extend(['Nothing'] * nothings)
start_inventory = [x for x in world.precollected_items if x.player == player]
if not start_inventory:
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory):
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
if world.swords[player] == 'assured' and all(' Sword' not in x.name for x in start_inventory):
precollected_items.append('Progressive Sword')
if 'Progressive Sword' in pool:
pool.remove('Progressive Sword')
pool.append('Rupees (50)')
elif 'Fighter Sword' in pool:
pool.remove('Fighter Sword')
pool.append('Rupees (50)')
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory):
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
if world.swords[player] == 'assured' and all(' Sword' not in x.name for x in start_inventory):
precollected_items.append('Progressive Sword')
if 'Progressive Sword' in pool:
pool.remove('Progressive Sword')
pool.append('Rupees (50)')
elif 'Fighter Sword' in pool:
pool.remove('Fighter Sword')
pool.append('Rupees (50)')
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms)
+17 -19
View File
@@ -1,5 +1,3 @@
import logging
from BaseClasses import Item
@@ -62,28 +60,28 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'),
'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'),
'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'),
'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x08], 999, None, None, None, None, None, None, None),
'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x09], 999, None, None, None, None, None, None, None),
'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x0a], 999, None, None, None, None, None, None, None),
'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x37, 0x08], 999, None, None, None, None, None, None, None),
'Red Pendant': (True, False, 'Crystal', [0x02, 0x32, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None),
'Blue Pendant': (True, False, 'Crystal', [0x01, 0x34, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None),
'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'),
'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'),
'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'),
'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x01], 999, None, None, None, None, None, None, None),
'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x02], 999, None, None, None, None, None, None, None),
'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x03], 999, None, None, None, None, None, None, None),
'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x04], 999, None, None, None, None, None, None, None),
'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x05], 999, None, None, None, None, None, None, None),
'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x06], 999, None, None, None, None, None, None, None),
'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x07], 999, None, None, None, None, None, None, None),
'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x20, 0x01], 999, None, None, None, None, None, None, None),
'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x20, 0x02], 999, None, None, None, None, None, None, None),
'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x20, 0x03], 999, None, None, None, None, None, None, None),
'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x20, 0x04], 999, None, None, None, None, None, None, None),
'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x20, 0x05], 999, None, None, None, None, None, None, None),
'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x20, 0x06], 999, None, None, None, None, None, None, None),
'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x20, 0x07], 999, None, None, None, None, None, None, None),
'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'),
'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'),
'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'),
'Arrows (5)': (False, False, None, 0xD5, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'),
'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'),
'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0xB3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Big Magic': (False, False, None, 0xD4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0xD3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
@@ -170,15 +168,15 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Ganon\'s Tower'),
'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key'),
'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'),
'Bee Trap': (False, False, None, 0xB0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'),
'Bee Trap': (False, False, None, 0xD0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'),
'Red Potion': (False, False, None, 0x2E, 150, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a red potion'),
'Green Potion': (False, False, None, 0x2F, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a green potion'),
'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'),
'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'),
'Good Bee': (False, False, None, 0xB5, 10, 'I will sting your foes a lot', 'and the cold buddy', 'the beekeeper kid', 'cold insect for sale', 'shroom pollenation', 'bottle boy has cold bee again', 'a good bee'),
'Good Bee': (False, False, None, 0xD6, 10, 'I will sting your foes a lot', 'and the cold buddy', 'the beekeeper kid', 'cold insect for sale', 'shroom pollenation', 'bottle boy has cold bee again', 'a good bee'),
'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'),
'Apples': (False, False, None, 0xB1, 30, 'Just a few pieces of fruit!', 'and the juicy fruit', 'the fruity kid', 'the fruit stand', 'expired fruit', 'bottle boy has fruit again', 'an apple hoard'),
'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'),
'Apples': (False, False, None, 0xD1, 30, 'Just a few pieces of fruit!', 'and the juicy fruit', 'the fruity kid', 'the fruit stand', 'expired fruit', 'bottle boy has fruit again', 'an apple hoard'),
'Fairy': (False, False, None, 0xD2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'),
'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
+1 -1
View File
@@ -1430,7 +1430,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance, is_atgt_swappe
def validate_bk_layout(proposal, builder, start_regions, world, player):
bk_special = check_bk_special(builder.master_sector.regions, world, player)
if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special):
if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special):
return True
flat_proposal = flatten_pair_list(proposal)
state = ExplorationState(dungeon=builder.name)
+37 -41
View File
@@ -18,13 +18,13 @@ from Regions import create_regions, create_shops, mark_light_dark_world_regions,
from OWEdges import create_owedges
from OverworldShuffle import link_overworld, update_world_regions, create_dynamic_exits
from EntranceShuffle import link_entrances
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string
from Rom import patch_rom, patch_race_rom, apply_rom_settings, LocalRom, JsonRom, get_hash_string
from Doors import create_doors
from DoorShuffle import link_doors, connect_portal, link_doors_prep
from RoomData import create_rooms
from Rules import set_rules
from Dungeons import create_dungeons
from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots
from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_items
from Fill import dungeon_tracking
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations
@@ -36,8 +36,11 @@ from source.item.FillUtil import create_item_pool_config, massage_item_pool, dis
from source.overworld.EntranceShuffle2 import link_entrances_new
from source.tools.BPS import create_bps_from_data
from source.classes.CustomSettings import CustomSettings
from source.enemizer.DamageTables import DamageTable
from source.enemizer.Enemizer import randomize_enemies
from source.rom.DataTables import init_data_tables
version_number = '1.2.0.23'
version_number = '1.4.1.8'
version_branch = '-u'
__version__ = f'{version_number}{version_branch}'
@@ -170,10 +173,13 @@ def main(args, seed=None, fish=None):
create_doors(world, player)
create_rooms(world, player)
create_dungeons(world, player)
adjust_locations(world, player)
world.damage_table[player] = DamageTable()
world.data_tables[player] = init_data_tables(world, player)
place_bosses(world, player)
randomize_enemies(world, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(world, player)
adjust_locations(world, player)
if any(world.potshuffle.values()):
logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
@@ -287,7 +293,7 @@ def main(args, seed=None, fish=None):
customize_shops(world, player)
if args.algorithm in ['balanced', 'equitable']:
balance_money_progression(world)
ensure_good_pots(world, True)
ensure_good_items(world, True)
if args.print_custom_yaml:
world.settings.record_info(world)
@@ -300,34 +306,13 @@ def main(args, seed=None, fish=None):
rom_names = []
jsonout = {}
enemized = False
if not args.suppress_rom or args.bps:
logger.info(world.fish.translate("cli","cli","patching.rom"))
for team in range(world.teams):
for player in range(1, world.players + 1):
sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit'
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none'
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
or sprite_random_on_hit)
rom = JsonRom() if args.jsonout else LocalRom(args.rom)
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
if use_enemizer and (args.enemizercli or not args.jsonout):
local_rom = LocalRom(args.rom) # update base2current.json (side effect)
if args.rom and not(os.path.isfile(args.rom)):
raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom)
if os.path.exists(args.enemizercli):
patch_enemizer(world, player, rom, local_rom, args.enemizercli, sprite_random_on_hit)
enemized = True
if not args.jsonout:
rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000)
else:
enemizerMsg = world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli + "\n"
enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied")
logging.warning(enemizerMsg)
raise EnemizerError(enemizerMsg)
patch_rom(world, rom, player, team, enemized, bool(args.mystery))
patch_rom(world, rom, player, team, bool(args.mystery))
if args.race:
patch_race_rom(rom)
@@ -380,6 +365,8 @@ def main(args, seed=None, fish=None):
if args.create_spoiler and not args.jsonout:
logger.info(world.fish.translate("cli", "cli", "patching.spoiler"))
world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt'))
if args.loglevel == 'debug':
world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt'))
if not args.skip_playthrough:
logger.info(world.fish.translate("cli","cli","calc.playthrough"))
@@ -403,7 +390,6 @@ def main(args, seed=None, fish=None):
logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO))
logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO))
logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO))
logger.info(world.fish.translate("cli","cli","used.enemizer") % (YES if enemized else NO))
logger.info(world.fish.translate("cli","cli","seed") + ": %s", world.seed)
logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start)
@@ -483,6 +469,7 @@ def init_world(args, fish):
world.enemy_shuffle = args.shuffleenemies.copy()
world.enemy_health = args.enemy_health.copy()
world.enemy_damage = args.enemy_damage.copy()
world.any_enemy_logic = args.any_enemy_logic.copy()
world.beemizer = args.beemizer.copy()
world.intensity = {player: 'random' if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
world.door_type_mode = args.door_type_mode.copy()
@@ -525,10 +512,11 @@ def set_starting_inventory(world, args):
if world.customizer and world.customizer.get_start_inventory():
for p, inv_list in world.customizer.get_start_inventory().items():
for inv_item in inv_list:
item = ItemFactory(inv_item.strip(), p)
if item:
world.push_precollected(item)
if inv_list:
for inv_item in inv_list:
item = ItemFactory(inv_item.strip(), p)
if item:
world.push_precollected(item)
def copy_world(world):
@@ -548,8 +536,6 @@ def copy_world(world):
ret.treasure_hunt_count = world.treasure_hunt_count.copy()
ret.treasure_hunt_icon = world.treasure_hunt_icon.copy()
ret.sewer_light_cone = world.sewer_light_cone.copy()
ret.light_world_light_cone = world.light_world_light_cone
ret.dark_world_light_cone = world.dark_world_light_cone
ret.seed = world.seed
ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge.copy()
ret.can_access_trock_front = world.can_access_trock_front.copy()
@@ -565,6 +551,7 @@ def copy_world(world):
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy()
ret.bow_mode = world.bow_mode.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
@@ -577,10 +564,12 @@ def copy_world(world):
ret.open_pyramid = world.open_pyramid.copy()
ret.shufflelinks = world.shufflelinks.copy()
ret.shuffle_ganon = world.shuffle_ganon.copy()
ret.take_any = world.take_any.copy()
ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy()
ret.enemy_health = world.enemy_health.copy()
ret.enemy_damage = world.enemy_damage.copy()
ret.any_enemy_logic = world.any_enemy_logic.copy()
ret.beemizer = world.beemizer.copy()
ret.intensity = world.intensity.copy()
ret.decoupledoors = world.decoupledoors.copy()
@@ -601,16 +590,18 @@ def copy_world(world):
ret.prizes = world.prizes.copy()
ret.restrict_boss_items = world.restrict_boss_items.copy()
ret.inaccessible_regions = world.inaccessible_regions.copy()
ret.damage_table = world.damage_table
ret.data_tables = world.data_tables # can be changed...
for player in range(1, world.players + 1):
create_regions(ret, player)
update_world_regions(ret, player)
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
create_owg_connections(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_dynamic_exits(ret, player)
create_dungeon_regions(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_owedges(ret, player)
create_shops(ret, player)
#create_doors(ret, player)
@@ -740,8 +731,6 @@ def copy_world_premature(world, player):
ret.treasure_hunt_count = world.treasure_hunt_count.copy()
ret.treasure_hunt_icon = world.treasure_hunt_icon.copy()
ret.sewer_light_cone = world.sewer_light_cone.copy()
ret.light_world_light_cone = world.light_world_light_cone
ret.dark_world_light_cone = world.dark_world_light_cone
ret.seed = world.seed
ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge.copy()
ret.can_access_trock_front = world.can_access_trock_front.copy()
@@ -756,6 +745,8 @@ def copy_world_premature(world, player):
ret.keyshuffle = world.keyshuffle.copy()
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy()
ret.bow_mode = world.bow_mode.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
@@ -768,10 +759,12 @@ def copy_world_premature(world, player):
ret.open_pyramid = world.open_pyramid.copy()
ret.shufflelinks = world.shufflelinks.copy()
ret.shuffle_ganon = world.shuffle_ganon.copy()
ret.take_any = world.take_any.copy()
ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy()
ret.enemy_health = world.enemy_health.copy()
ret.enemy_damage = world.enemy_damage.copy()
ret.any_enemy_logic = world.any_enemy_logic.copy()
ret.beemizer = world.beemizer.copy()
ret.intensity = world.intensity.copy()
ret.decoupledoors = world.decoupledoors.copy()
@@ -791,6 +784,9 @@ def copy_world_premature(world, player):
ret.owflutespots = world.owflutespots.copy()
ret.prizes = world.prizes.copy()
ret.restrict_boss_items = world.restrict_boss_items.copy()
ret.inaccessible_regions = world.inaccessible_regions.copy()
ret.damage_table = world.damage_table
ret.data_tables = world.data_tables # can be changed...
ret.key_logic = world.key_logic.copy()
ret.is_copied_world = True
@@ -799,10 +795,10 @@ def copy_world_premature(world, player):
update_world_regions(ret, player)
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
create_owg_connections(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_dynamic_exits(ret, player)
create_dungeon_regions(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
create_owedges(ret, player)
create_shops(ret, player)
create_doors(ret, player)
+33 -11
View File
@@ -8,10 +8,12 @@ import shlex
import urllib.parse
import websockets
from BaseClasses import PotItem, PotFlags
from BaseClasses import PotItem, PotFlags, LocationType
import Items
import Regions
import PotShuffle
import source.dungeon.EnemyList as EnemyList
import source.rom.DataTables as DataTables
class ReceivedItem:
@@ -67,6 +69,7 @@ class Context:
self.lookup_id_to_name = {}
self.pottery_locations_enabled = None
self.uw_sprite_locations_enabled = None
def color_code(*args):
codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34,
@@ -99,6 +102,7 @@ ITEM_SRAM_SIZE = 0x250
SHOP_SRAM_LEN = 0x29 # 41 tracked items
POT_LOCATION_TABLE = 0x142A60
UW_SPRITE_LOCATION_TABLE = 0x142CB0
RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes
RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte
@@ -836,9 +840,11 @@ def filter_location(ctx, location):
tracking_data = ctx.pottery_locations_enabled
tile_pots = tracking_data[tile_idx] | (tracking_data[tile_idx+1] << 8)
return (mask & tile_pots) == 0
if (not ctx.key_drop_mode and location in PotShuffle.key_drop_data
and PotShuffle.key_drop_data[location][0] == 'Drop'):
return True
if location in location_table_sprite_items:
tile_idx, mask = location_table_sprite_items[location]
tracking_data = ctx.uw_sprite_locations_enabled
tile_sprites = tracking_data[tile_idx] | (tracking_data[tile_idx+1] << 8)
return (mask & tile_sprites) == 0
if not ctx.shop_mode and location in Regions.flat_normal_shops:
return True
if not ctx.retro_mode and location in Regions.flat_retro_shops:
@@ -849,13 +855,6 @@ def filter_location(ctx, location):
def init_lookups(ctx):
ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()}
ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()}
for location, datum in PotShuffle.key_drop_data.items():
type = datum[0]
if type == 'Drop':
location_id, super_tile, sprite_index = datum[1]
location_table_sprite_items[location] = (2 * super_tile, 0x8000 >> sprite_index)
ctx.lookup_name_to_id[location] = location_id
ctx.lookup_id_to_name[location_id] = location
for super_tile, pot_list in PotShuffle.vanilla_pots.items():
for pot_index, pot in enumerate(pot_list):
if pot.item != PotItem.Hole:
@@ -869,6 +868,25 @@ def init_lookups(ctx):
location_id = Regions.pot_address(pot_index, super_tile)
ctx.lookup_name_to_id[loc_name] = location_id
ctx.lookup_id_to_name[location_id] = loc_name
uw_table = DataTables.get_uw_enemy_table()
key_drop_data = {(v[1][1], v[1][2]): k for k, v in PotShuffle.key_drop_data.items() if v[0] == 'Drop'}
for super_tile, enemy_list in uw_table.room_map.items():
index_adj = 0
for index, sprite in enumerate(enemy_list):
if sprite.sub_type == 0x07: # overlord
index_adj += 1
continue
if (super_tile, index) in key_drop_data:
loc_name = key_drop_data[(super_tile, index)]
location_id = PotShuffle.key_drop_data[loc_name][1][0]
else:
loc_name = f'{sprite.region} Enemy #{index+1}'
location_id = EnemyList.drop_address(index, super_tile)
if index < index_adj:
logging.info(f'Problem at {hex(super_tile)} {loc_name}')
location_table_sprite_items[loc_name] = (2 * super_tile, 0x8000 >> (index-index_adj))
ctx.lookup_name_to_id[loc_name] = location_id
ctx.lookup_id_to_name[location_id] = loc_name
async def track_locations(ctx : Context, roomid, roomdata):
@@ -999,6 +1017,7 @@ async def game_watcher(ctx : Context):
if not ctx.rom:
rom = await snes_read(ctx, ROMNAME_START, ROMNAME_SIZE)
logging.info(f'Rom name: {rom} @ {hex(ROMNAME_START)}')
if rom is None or rom == bytes([0] * ROMNAME_SIZE):
continue
@@ -1015,6 +1034,9 @@ async def game_watcher(ctx : Context):
if ctx.pottery_locations_enabled is None:
ctx.pottery_locations_enabled = await snes_read(ctx, POT_LOCATION_TABLE, 0x250)
if ctx.uw_sprite_locations_enabled is None:
ctx.uw_sprite_locations_enabled = await snes_read(ctx, UW_SPRITE_LOCATION_TABLE, 0x250)
gamemode = await snes_read(ctx, WRAM_START + 0x10, 1)
if gamemode is None or gamemode[0] not in INGAME_MODES:
continue
+14 -6
View File
@@ -16,6 +16,8 @@ import Items
import Regions
import PotShuffle
from MultiClient import ReceivedItem, get_item_name_from_id, get_location_name_from_address
import source.dungeon.EnemyList as EnemyList
import source.rom.DataTables as DataTables
class Client:
def __init__(self, socket):
@@ -355,12 +357,6 @@ async def console(ctx : Context):
def init_lookups(ctx):
ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()}
ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()}
for location, datum in PotShuffle.key_drop_data.items():
type = datum[0]
if type == 'Drop':
location_id = datum[1][0]
ctx.lookup_name_to_id[location] = location_id
ctx.lookup_id_to_name[location_id] = location
for super_tile, pot_list in PotShuffle.vanilla_pots.items():
for pot_index, pot in enumerate(pot_list):
if pot.item != PotItem.Hole:
@@ -373,6 +369,18 @@ def init_lookups(ctx):
location_id = Regions.pot_address(pot_index, super_tile)
ctx.lookup_name_to_id[loc_name] = location_id
ctx.lookup_id_to_name[location_id] = loc_name
uw_table = DataTables.get_uw_enemy_table()
key_drop_data = {(v[1][1], v[1][2]): k for k, v in PotShuffle.key_drop_data.items() if v[0] == 'Drop'}
for super_tile, enemy_list in uw_table.room_map.items():
for index, sprite in enumerate(enemy_list):
if (super_tile, index) in key_drop_data:
loc_name = key_drop_data[(super_tile, index)]
location_id = PotShuffle.key_drop_data[loc_name][1][0]
else:
loc_name = f'{sprite.region} Enemy #{index+1}'
location_id = EnemyList.drop_address(index, super_tile)
ctx.lookup_name_to_id[loc_name] = location_id
ctx.lookup_id_to_name[location_id] = loc_name
async def main():
-3
View File
@@ -34,7 +34,6 @@ def main():
parser.add_argument('--suppress_meta', action='store_true')
parser.add_argument('--bps', action='store_true')
parser.add_argument('--rom')
parser.add_argument('--enemizercli')
parser.add_argument('--outputpath')
parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical'])
for player in range(1, multiargs.multi + 1):
@@ -78,8 +77,6 @@ def main():
if args.rom:
erargs.rom = args.rom
if args.enemizercli:
erargs.enemizercli = args.enemizercli
mw_settings = {'algorithm': False}
+1 -1
View File
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
from OverworldGlitchRules import create_owg_connections
from Utils import bidict
version_number = '0.3.4.2'
version_number = '0.4.0.0'
# branch indicator is intentionally different across branches
version_branch = ''
-6
View File
@@ -170,12 +170,6 @@ def prefill_world(world, plando, text_patches):
elif line.startswith('!light_cone_sewers'):
_, sewerstr = line.split(':', 1)
world.sewer_light_cone = {1: sewerstr.strip().lower() == 'true'}
elif line.startswith('!light_cone_lw'):
_, lwconestr = line.split(':', 1)
world.light_world_light_cone = lwconestr.strip().lower() == 'true'
elif line.startswith('!light_cone_dw'):
_, dwconestr = line.split(':', 1)
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
elif line.startswith('!fix_trock_doors'):
_, trdstr = line.split(':', 1)
world.fix_trock_doors = {1: trdstr.strip().lower() == 'true'}
+21 -14
View File
@@ -750,7 +750,7 @@ vanilla_pots = {
Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADED, [0xB3, 0x73, 0xFA])),
Pot(92, 14, PotItem.Heart, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF0, [0xBB, 0x73, 0xFA])),
Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF3, [0xC3, 0x73, 0xFA]))],
0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))],
0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave (East)', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))],
0xF3: [Pot(0x28, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA76F, [0x53, 0xA3, 0xFA])),
Pot(0x2C, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA772, [0x5B, 0xA3, 0xFA])),
Pot(0x30, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA775, [0x63, 0xA3, 0xFA]))],
@@ -919,14 +919,14 @@ def shuffle_pots(world, player):
new_pot_contents.room_map[super_tile] = new_pots
world.pot_contents[player] = new_pot_contents
world.data_tables[player].pot_secret_table = new_pot_contents
def shuffle_pot_switches(world, player):
import RaceRandom as random
for super_tile in vanilla_pots:
new_pots = world.pot_contents[player].room_map[super_tile]
new_pots = world.data_tables[player].pot_secret_table.room_map[super_tile]
# sort in the order Hole, Switch, Key, Other, Nothing
sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0}
old_pots = sorted(new_pots, key=lambda pot: sort_order.get(pot.item, 1), reverse=True)
@@ -1016,16 +1016,16 @@ key_drop_data = {
'Swamp Palace - Trench 2 Pot Key': ['Pot', 0x35, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'],
'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'],
'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'],
'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'],
'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 2), 'dropped near Mothula', 'Small Key (Skull Woods)'],
"Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves Town", 'Small Key (Thieves Town)'],
"Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves Town", 'Small Key (Thieves Town)'],
'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 9), 'dropped in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Many Pots Pot Key': ['Pot', 0x9F, 'in a pot in Ice Palace', 'Small Key (Ice Palace)'],
'Misery Mire - Spikes Pot Key': ['Pot', 0xB3, 'in a pot in Misery Mire', 'Small Key (Misery Mire)'],
'Misery Mire - Fishbone Pot Key': ['Pot', 0xA1, 'in a pot in forgotten Mire', 'Small Key (Misery Mire)'],
'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 9), 'dropped in Misery Mire', 'Small Key (Misery Mire)'],
'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 10), 'dropped in Misery Mire', 'Small Key (Misery Mire)'],
'Turtle Rock - Pokey 1 Key Drop': ['Drop', (0x09E70D, 0xB6, 5), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'],
'Turtle Rock - Pokey 2 Key Drop': ['Drop', (0x09DA5D, 0x13, 6), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'],
'Ganons Tower - Conveyor Cross Pot Key': ['Pot', 0x8B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'],
@@ -1034,18 +1034,21 @@ key_drop_data = {
'Ganons Tower - Mini Helmasaur Key Drop': ['Drop', (0x09DDC4, 0x3D, 2), "dropped atop Ganon's Tower", 'Small Key (Ganons Tower)']
}
key_drop_special = {(data[1][1], data[1][2]): name for name, data in key_drop_data.items() if data[0] == 'Drop'}
class PotSecretTable(object):
def __init__(self):
self.room_map = defaultdict(list)
self.multiworld_count = 0
def write_pot_data_to_rom(self, rom, colorize):
pointer_address = 0x140000 # pots currently in bank 28
pointer_offset = 0x128 * 2
empty_address = (pointer_address + pointer_offset)
def write_pot_data_to_rom(self, rom, colorize, data_tables):
pointer_address = snes_to_pc(0x09D87E) # pots currently in bank 9
data_bank_address = snes_to_pc(0x09DACE)
# pointer_offset = 0x128 * 2
empty_address = data_bank_address
empty_pointer = pc_to_snes(empty_address) & 0xFFFF
data_pointer = pointer_address + pointer_offset + 2
data_pointer = data_bank_address + 2
for room in range(0, 0x128):
if room in self.room_map:
list_idx = 0
@@ -1056,9 +1059,13 @@ class PotSecretTable(object):
rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data())
if pot.location is not None and not pot.location.forced_item:
collection_rate_mask |= 1 << (15 - list_idx)
if colorize and pot.obj_ref:
pot.obj_ref.change_type(Shuffled_Pot)
pot.obj_ref.write_to_rom(rom)
if colorize:
if room in data_tables.room_list:
room_object = data_tables.room_list[room]
room_object.find_all_pots()[list_idx].change_type(Shuffled_Pot)
elif pot.obj_ref:
pot.obj_ref.change_type(Shuffled_Pot)
pot.obj_ref.write_to_rom(rom)
list_idx += 1
rom.write_bytes(data_pointer + list_idx * 3, [0xFF, 0xFF])
rom.write_bytes(snes_to_pc(0x28AA60) + room * 2, int16_as_bytes(collection_rate_mask))
+17 -11
View File
@@ -13,16 +13,8 @@ This is a very new mode of LTTPR so the tools and info is very limited.
- CodeTracker, an [EmoTracker](https://emotracker.net) package for LTTPR
- There is an [OW OWG Reference Sheet](https://zelda.codemann8.com/images/shared/ow-owg-reference-sheet.png) that shows all the in-logic places where boots/mirror clips and fake flippers are expected from the player.
# Known Issues
(Updated 2021-11-06)
### If you want to playtest this, know these things:
- If you fake flipper, beware of transitioning south. You could end up at the top of the waterfall in the southeast of either world. If you mistakenly drop down, it is important to know that altho the game may appear as frozen for a bit of time, the game is simply scrolling Link back to the original point of water entry. Upon "landing", you'll be able to S+Q properly. Falling from the waterfall is avoidable but it is super easy to do as it is super close to the transition.
- In Crossed OW, there are some interesting bunny swimming situations that can occur, these ARE in logic currently, just be careful and avoid taking a hit from an enemy.
### Known bugs:
- Screens that loop on itself and also have free-standing items, the sprites are duplicated and can cause item duplication
- When OWG are performed to enter mega-tile screens (large OW screens), there is a small chance that an incorrect VRAM reference value causes the map graphics to offset in increments of 16 pixels
# Feedback and Bug Reports
@@ -137,7 +129,7 @@ Note: These changes do impact the logic. If you use `CodeTracker`, these Inverte
Only settings specifically added by this Overworld Shuffle fork are found here. All door and entrance randomizer settings are supported. See their [readme](https://github.com/Aerinon/ALttPDoorRandomizer/blob/master/README.md)
## Overworld Layout Shuffle (--ow_shuffle)
OW Edge Transitions are shuffled to create new world layouts. A brief visual representation of this can be viewed [here](https://media.discordapp.net/attachments/783989090017738753/857299555183362078/ow-modes.gif). (This graphic also includes combinations of Crossed and Tile Flip)
OW Edge Transitions are shuffled to create new world layouts. A brief visual representation of this can be viewed [here](https://cdn.discordapp.com/attachments/783989090017738753/857299555183362078/ow-modes.gif?ex=66044c6d&is=65f1d76d&hm=b43ebd1b22e0a86c7caf3153c217ba14174bb92a2b168c15fe8e75364b5c8c15&). (This graphic also includes combinations of Crossed and Tile Flip)
### Vanilla
@@ -197,7 +189,7 @@ Being that this uses concepts from Inverted, it will be important to review the
During gameplay:
- When on the OW, there will be an L or D in the upper left corner, indicating which world you are currently in. Mirroring still works the same, you must be in the DW to mirror to the LW.
- When doing a map check (pressing X while on the OW), the tiles shown will reflect the flipped tiles. This means that dungeon prizes will show the prizes for the dungeons that are now part of that world, beware of Desert/Mire and Eastern/PoD. Here is an image showing the difference of appearance when tiles are flipped on the [map check](https://media.discordapp.net/attachments/783989090017738753/970646558049714196/lttp-lw-mapcheck.gif) screen.
- When doing a map check (pressing X while on the OW), the tiles shown will reflect the flipped tiles. This means that dungeon prizes will show the prizes for the dungeons that are now part of that world, beware of Desert/Mire and Eastern/PoD. Here is an image showing the difference of appearance when tiles are flipped on the [map check](https://cdn.discordapp.com/attachments/783989090017738753/970646558049714196/lttp-lw-mapcheck.gif?ex=66015e8d&is=65eee98d&hm=b4d97c52d6aed593f0e6ec54924696ba969ce11109ce5ba1291b50a8a3e2dac8&) screen.
Note: Tiles are put into Tile Groups (see `Terminology`) that must be shuffled together when certain settings are enabled. For instance, if ER is disabled, then any tiles that have a connector cave that leads to a different tile, then those tiles must flip together.
@@ -256,7 +248,7 @@ This adds 42 new item locations to the game. These bonk locations are limited to
- Some screens are coded to change the "alternate tree color", some of them are strange (just how the vanilla game does it)
- Rocks and statues are unable to be made to have a different color
Here is a map that shows all the [Bonk Locations](https://cdn.discordapp.com/attachments/1105770688649895968/1105770806769877072/bonkdrops.png?width=1399&height=702). FYI, the numbers indicate how many bonk items there. The stars with a green square are all Bonk Locations that are unlocked after you kill Aga 1.
Here is a map that shows all the [Bonk Locations](https://cdn.discordapp.com/attachments/1105770688649895968/1105770806769877072/bonkdrops.png?ex=6603d650&is=65f16150&hm=3576367abd636ba7723ef30e87a4bc407c5e1eb9a8be325e90b1e22c04c58401&). FYI, the numbers indicate how many bonk items there. The stars with a green square are all Bonk Locations that are unlocked after you kill Aga 1.
As far as map trackers, Bonk Locations are supported on `CodeTracker` when the Bonk Drops option is enabled.
@@ -323,6 +315,20 @@ This mode groups entrances into types and shuffles them freely within those grou
Both Lite and Lean ER modes are supported in `CodeTracker`, showing only the entrances that are shuffled.
### Swapped
This mostly follows the rules of Crossed ER, but instead of entrances being randomly placed individually, random pairs of two entrances are chosen to swap with each other. This creates a unique experience where checking one entrance gives you information about another entrance, which can influence your future routing.
If there is an odd amount of entrances being shuffled in the pool, the last remaining entrance will be vanilla. All other entrances are guaranteed to be non-vanilla.
Here is a [Swapped ER Reference Sheet](http://zelda.codemann8.com/images/shared/swapped-reference-sheet.png) that shows some of the caves/houses that are less familiar to most people.
### District
This is an entrance shuffle that only shuffles entrances within their respective `Districts` (See Below). Also, dropdowns and the entrance that a dropdown normally exits are NOT kept together; this also means that the Skull Woods entrances are no longer guaranteed to lead to Skull Woods. Also, since there is no district that can span multiple worlds, this is NOT a cross-world entrance mode.
Districts are a concept originally conceived by Aerinon in the Door Randomizer, where parts of the OW are split into areas and given a name. Here is a [District Map](https://cdn.discordapp.com/attachments/783989090017738753/1194615705027477534/districts.png?ex=66040e12&is=65f19912&hm=9ef382f004f7013e018f0b04d0bc98727f87b8b5da6499f34dbcf6e14bb0ac90&) showing how they are split up.
# Command Line Options
```
+181 -3
View File
@@ -1,5 +1,37 @@
# New Features
FastROM changes have been included now.
## Enemizer Features
Please see this document for extensive details. [Enemizer in DR](https://docs.google.com/document/d/1iwY7Gy50DR3SsdXVaLFIbx4xRBqo9a-e1_jAl5LMCX8/edit?usp=sharing)
Key points:
* Enemizer no longer uses a third party program. It is now built-in.
* New option under Enemy Drops: Underworld. Any underworld enemy can drop items.
* New option under Enemizer tab: Enemy Logic
Please read the entire document above for extensive details about enemizer and enemy drop shuffle systems.
Enemizer main changes:
* Several sprites added to the pool. Most notable is how enemies behave on shallow water. They work now.
* Clearing rooms, spawnable chests, and enemy keys drops can now have enemies with specific logic in the room. This logic is controlled by the new Enemy Logic option
* New system for banning enemies that cause issue is place. If you see an enemy in a place that would cause issue, please report it and it can be banned to never happen again. Initial bans can be found [in the code](source/enemizer/enemy_deny.yaml) for the curious
* Thieves are always unkillable, but banned from the entire underworld. We can selectively ban them from problematic places in the overworld, and if someone wants to figure out where they could be safe in the underworld, I'll allow them there once the major problems have been banned.
* THe old "random" and "legacy" options have been discarded for enemy shuffle. Tile room patterns are currently shuffled with enemies.
Underworld drops:
* A flashing blue square added to help locate enemies that have remaining drops on the supertile. (Dungeons and caves without a compass get this for free.)
* Flying enemies, spawned enemies, and enemies with special death routines will not drop items.
* Pikits do not drop their item if they have eaten a shield.
* Hovers in swamp waterway do no drop items due to a layer issue that's not been solved.
* Enemies that are over pits require boomerang or hookshot to collect the item
* Enemies behind rails require the boomerang (hookshot can sequence break in certain cases)
* Enemies that spawn on walls do not drop items. (Keese normally don't, but in enemizer these can be valid drops otherwise. The document has a visual guide.)
(Older notes below)
One major change with this update is that big key doors and certain trap doors are no longer guaranteed to be vanilla in Dungeon Door Shuffle modes even if you choose not to shuffle those types. A newer algorithm for putting dungeons together has been written and it will remove big key doors and trap doors when necessary to ensure progress can be made.
Please note that retro features are now independently customizable as referenced below. Selecting Retro mode or World State: Retro will change Bow Mode to Retro (Progressive). Take Anys to Random, and Small Keys to Universal.
@@ -107,20 +139,166 @@ These are now independent of retro mode and have three options: None, Random, an
* Dark Lake Hylia Ledge Healer Fairy (aka Shopping Mall Bomb)
* Bonk Fairy (Dark)
# Bug Fixes and Notes
# Patch Notes
* 1.4.1.8u
* HUD: New dungeon indicators based on common abbreviations
* OWG+HMG: EG is allowed to be armed
* Drop Shuffle: Fixed an issue with minor drops counting for the wrong dungeon
* Enemizer: Trinexx ice breath should be properly disabled if Trinexx is located outside of Turtle Rock
* Enemizer: Enemy bans
* 1.4.1.7u
* Some bugs around Triforce Pieces smoothed out
* Enemizer: No exception for mimics/eyegores in vanilla rooms if enemy logic is turned to off
* Enemizer: Various enemy bans
* 1.4.1.6u
* Difficulty: Fixed some issues around item caps not being respected
* Enemezier: Tutorial guards remove from South Kakariko
* Map: Pendant colors fixed
* Minor rom code cleanup
* Enemizer: Hovers added to problematic pool near pits. Some other bans
* 1.4.1.5u
* Major Fix: Problem with Ganon's Room sprites
* HMG: Remove extra Swamp Smalls in the pool due to pottery settings
* Enemizer: Couple enemy bans
* 1.4.1.4u
* Logic: Fixed logic bugs surrounding dynammic doors missing logic from big keys and other door types
* Inverted: Castle warp should not appear after defeating Aga 1
* Enemzier: Fixed a crash with cached sprites Zora/Swamola
* 1.4.1.3v
* Enemizer: Raven/Murderdactyls using the correct damage table
* Enemzier: Boss drops only center when boss shuffle is on
* 1.4.1.2v
* Expert/Hard Item Pool: Capacity fairy no longer gives out free crystal
* Vanilla door + Universal Keys: Generation fixed
* Boss Shuffle: Generation fixed (thanks Codemann for easy solution)
* Vanilla ER: No need for ability to check prizes on keysanity menu
* Swapped ER: Possible generation issue fixed (thanks Codemann)
* Enemizer: Roller ban
* Performance: Faster text boxes. Thanks Kan!
* 1.4.1.1v
* Logic: Moon pearl logic respects blocked doors
* 1.4.1.0v
* World Model Refactor: The overworld has been split up by screen, brings OR and DR a bit closer together in the model sense. A few OWG clips have been rewritten to fit into this new logic better.
* Logic: New logic for some bosses on ice
* Helmasaur on Ice: Bombs for mask, sword or arrows for 2nd phase
* Blind on Ice: Beam sword, Somaria, or Byrna plus magic extension for damage. Red shield or Byrna for protection.
* Kholdstare on Ice: Three options (after cracking the shell)
* Beam sword
* Fire Rod with 1.5 magic extensions
* Fire Rod & Bombos & any Sword & 1 Magic Extension
* Vitreous on Ice: Arrows and Bombs or a Beam Sword
* Trinexx on Ice: Boots always required for dodging. Damage options:
* Gold sword
* Tempered sword with magic extension
* Hammer or Master sword with 3 magic extensions (Rod spam for elemental heads, non-ideal weapon for last phase)
* Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO)
* Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert
* Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert
* Enemizer Damage Rework:
* Shuffled: Actually shuffles the damage groups in the table instead of picking random numbers and reducing for mails from there. Enemies will still be assigned to a damage group randomly.
* There will always be at least one group which does no damage. The thief will always be in that group. Ganon always has his own group.
* Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes
* Generation: Trinexx and Lanmolas room allowed as lobbies in intensity 3 (works with enemizer now)
* Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior?
* 1.4.0.1v
* Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door
* Generation: Fix a broken tile pattern
* Inverted: Castle warp should not appear after defeating Aga 1
* Murahdahla: Should not disappear after Aga 1. May fix other subtle issues.
* Shopsanity: Buying multiple of an item in the potion shop should no longer increase item count.
* 1.4.0.0v
* Initial support for HMG (Thanks Muffins!)
* Generation: fix for bunny walk logic taking up too much memory
* Key Logic: Partial is now the new default
* Enemizer: enemy bans
* 1.3.0.9v
* ER: New Swapped ER mode borrowed from OWR
* ER: fixed a generation error where TR chooses all "must-exits"
* Ganonhunt: playthrough no longer collects crystals
* Vanilla Fill: Uncle weapon is always a sword, medallions for Mire/TR will be vanilla
* Customizer: support shufflebosses/shuffleenemies as well as boss_shuffle/enemy_shuffle
* Enemizer: enemy bans
* 1.3.0.8v
* Enemizer: Red Mimics correctly banned from challenge rooms in appropriate logic setting
* No Logic Standard ER: Rain doors aren't blocked if no logic is enabled.
* Trinexx: attempt to fix early start
* MW Progression Balancing: Change to be percentage based instead of raw count. (80% threshold)
* Take anys: Good Bee cave chosen as take any should no longer prevent generation
* Money balancing: Fixed generation issue
1.2.0.23u
* Money balancing: Fixed generation issue
* Enemizer: various enemy bans
* 1.3.0.7v
* Fix for Mimic Cave enemy drops
* Fix for Spectacle Rock Cave enemy drops (the mini-moldorms)
* Fix for multiworld lamps with incorrect graphics
* No longer shuffles fairy bonks (from trees) as part of Enemizer
* 1.3.0.6v
* Flute can't be activated in rain state (except glitched modes) (Thanks codemann!)
* Enemizer
* Arrghus at Lanmo 2 no longer prevents pot pickups
* Trinexx at Lanmo 2 requires the Cape go backwards to face him
* Lift-able Blocks require a sprite slot (should help reduce problems)
* Fixed logic issues:
* Self-locking key not allowed in Sanctuary in standard (typo fixed)
* More advanced bunny-walking logic in dungeons (multiple paths considered)
* ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used)
* MSU: GTBK song fix for DR (Thanks codemann!)
* District Algorithm: Fails if no available location outside chosen districts
* Various enemy bans
* More Gibos near kiki and Old Man
* Bumper/AntiFairy obstacles
* Damaging roller
* Statue + Pots don't mix
* Statues on Skull Big Key Chest tile
* Toppo in challenge rooms
* Misc others
* 1.3.0.5v
* Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud.
* Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile.
* Enemizer: Statues banned offscreen for pull switches
* Enemizer: Several sprite producing enemies have been limited on crowded tiles. Offenders: Hinox, Sluggula, Bomb Guard, Beamos, Gibo, Wall Cannons, Probe using Guards. Others do not spam as many projectiles.
* Enemizer: More enemy bans (mostly Wizzrobes near walls where they won't spawn, couple missed firebar spots)
* 1.3.0.4v
* Enemizer: The bunny beam near Lanmo 2 and the 4 fairies near Ice Armos are not shuffled anymore. This is due to how bosses shuffle works and since it cannot be guaranteed to work within the current system, they are vanilla. (Vitreous still overwrites the fairies and Arrghus only lets two spawn, etc.)
* Dropshuffle: Pokey 1 has been fixed to drop his item
* Mystery/Customizer: true/false and on/off in yaml files should behave the same.
* More enemy bans as have been reported
* 1.3.0.3v
* Faeries now part of the enemy shuffle pool. Take note, this will increase enemy drop locations to include fairy pools both in dungeons and in caves.
* Enemy drop indicator (blue square) now works in caves based on entrance used
* Fixes:
* Collection rate counter is properly hidden in mystery seeds
* Sprite limit lowered where possible to allow for lifting of pots
* Hovers in Swamp Waterway properly do not drop items anymore
* Lots more bans (thanks to jsd in particular but also thanks to all the reports)
* Minor issue with customizer/mystery files not allowing "true" for booleans
* 1.3.0.2v
* Fix for multiworld received keys not counting correctly
* Fix for multiworld lamps incorrect graphics
* Fix for collection rate decreasing on item "pickup"
* Fix for pendants as prizes counting as items
* Fix for castle barrier gfx in rain state
* Enemizer fixes and bans:
* Fixed a generation issue where ChainChomp placement would cause a failure. (Invincible enemies banned in Sprial Cave for early game traversal for now)
* Skull Pot Prison should not be blocked by "impassable" enemies
* Bumpers banned in Ice Hookshot room
* Fixed issue in GT Spike Crystal room
* Fixed blockage issues in TT Ambush and Compass rooms
* Forbid Bumper in Fairy Ascension cave; needed to clip into wall weirdly to pass.
* Enemy Drop bans
* Forbid Stals in many places where they cannot be woken up. Behind rails and on top of blocks, for example.
* A couple minor wizzrobes bans because of despawns.
* Enemies over pits and on conveyors near pits have been issued standard bans for falling enemies. Mimics join the ranks here as they don't work well on pits or on conveyors.
* Mimics banned where conveyors touch walls and could clip out unintentionally
* 1.3.0.1v
* Fixed bugs with item duping and disappearing drops
* Fixed multiworld crash
* Fixed assured sword missing when using start inventory (via GUI/CLI)
* Forbid extra statues in Swamp Push Statue room
* Forbid bumpers on OW water
* Forbid Stal on pits
* Text fix on sprite author (thanks Synack)
* 1.2.0.23u
* Generation: fix for bunny walk logic taking up too much memory
* Key Logic: Partial is now the new default
* 1.2.0.22u
+249 -234
View File
@@ -3,6 +3,8 @@ from Items import ItemFactory
from BaseClasses import Region, Location, Entrance, RegionType, Terrain, Shop, ShopType, LocationType, PotItem, PotFlags
from PotShuffle import key_drop_data, vanilla_pots, choose_pots, PotSecretTable
from source.dungeon.EnemyList import setup_enemy_locations, enemy_names
def create_regions(world, player):
world.regions += [
@@ -244,21 +246,23 @@ def create_regions(world, player):
create_dw_region(player, 'Bomber Corner Area', None, ['Bomber Corner Water Drop', 'Bomber Corner WS', 'Bomber Corner NE']),
create_dw_region(player, 'Bomber Corner Water', None, ['Bomber Corner Pier', 'Bomber Corner Whirlpool', 'Bomber Corner WC'], 'Dark World', Terrain.Water),
create_dw_region(player, 'Bomber Corner Water Ledge', None, ['Bomber Corner Waterfall Water Drop', 'Bomber Corner NW'], 'Dark World', Terrain.Water),
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_cave_region(player, 'Lumberjack House', 'a boring house'),
create_cave_region(player, 'Old Man Cave', 'a connector', ['Lost Old Man'], ['Old Man Cave Exit (East)']),
create_cave_region(player, 'Old Man Cave (West)', 'a connector', ['Lost Old Man'], ['Old Man Cave E']),
create_cave_region(player, 'Old Man Cave (East)', 'a connector', None, ['Old Man Cave Exit (East)', 'Old Man Cave W']),
create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']),
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_cave_region(player, 'Death Mountain Return Cave (left)', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave E']),
create_cave_region(player, 'Death Mountain Return Cave (right)', 'a connector', None, ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']),
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit', 'Spectacle Rock Cave East Edge']),
create_cave_region(player, 'Spectacle Rock Cave Pool', 'a connector', None, ['Spectacle Rock Cave West Edge']),
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
@@ -308,7 +312,8 @@ def create_regions(world, player):
create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
create_cave_region(player, 'Library', 'the library', ['Library']),
create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain', None, ['Bonk Fairy (Light) Pool']),
create_cave_region(player, 'Bonk Fairy Pool', 'a fairy fountain'),
create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
@@ -319,11 +324,13 @@ def create_regions(world, player):
create_cave_region(player, 'Light Hype Fairy', 'a fairy fountain'),
create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region(player, 'Lake Hylia Shop', 'a common shop', ['Lake Hylia Shop - Left', 'Lake Hylia Shop - Middle', 'Lake Hylia Shop - Right']),
create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']),
create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right'], ['Capacity Upgrade East']),
create_cave_region(player, 'Capacity Fairy Pool', 'near the queen of fairies', None, ['Capacity Fairy Pool West']),
create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left',
'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region(player, 'Good Bee Cave', 'a cold bee'),
create_cave_region(player, 'Good Bee Cave', 'a cold bee', None, ['Good Bee Cave Front to Back']),
create_cave_region(player, 'Good Bee Cave (back)', 'a cold bee', None, ['Good Bee Cave Back to Front']),
create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'),
@@ -337,7 +344,8 @@ def create_regions(world, player):
create_cave_region(player, 'Hookshot Cave (Bonk Islands)', 'a connector', ['Hookshot Cave - Bottom Right']),
create_cave_region(player, 'Hookshot Cave (Hook Islands)', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Left']),
create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']),
create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']),
create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Back Exit']),
create_cave_region(player, 'Hookshot Cave (Fairy Pool)', 'a connector', None, ['Hookshot Cave Fairy to Back']),
create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
create_cave_region(player, 'Dark Death Mountain Shop', 'a common shop', ['Dark Death Mountain Shop - Left', 'Dark Death Mountain Shop - Middle', 'Dark Death Mountain Shop - Right']),
@@ -357,7 +365,7 @@ def create_regions(world, player):
create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(player, 'Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region(player, 'Archery Game', 'a game of skill'),
create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain', None, ['Bonk Fairy (Dark) Pool']),
create_cave_region(player, 'Big Bomb Shop', 'the bomb shop', ['Big Bomb'], ['Big Bomb Shop Exit']),
create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
@@ -578,8 +586,9 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'PoD Arena Ledge', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge'], ['PoD Arena Ledge ES', 'PoD Arena Ledge to Ranged Crystal']),
create_dungeon_region(player, 'PoD Arena Ledge - Ranged Crystal', 'Palace of Darkness', None, ['PoD Arena Ledge Ranged Crystal Exit']),
create_dungeon_region(player, 'PoD Sexy Statue', 'Palace of Darkness', None, ['PoD Sexy Statue W', 'PoD Sexy Statue NW']),
create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down']),
create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down', 'PoD Map Balcony ES']),
create_dungeon_region(player, 'PoD Map Balcony - Ranged Crystal', 'Palace of Darkness', None, ['PoD Map Balcony Ranged Crystal Exit']),
create_dungeon_region(player, 'PoD Fairy Pool', 'Palace of Darkness', None, ['PoD Fairy Pool WS']),
create_dungeon_region(player, 'PoD Conveyor', 'Palace of Darkness', None, ['PoD Conveyor North Stairs', 'PoD Conveyor SW']),
create_dungeon_region(player, 'PoD Mimics 1', 'Palace of Darkness', None, ['PoD Mimics 1 NW', 'PoD Mimics 1 SW']),
create_dungeon_region(player, 'PoD Jelly Hall', 'Palace of Darkness', None, ['PoD Jelly Hall NW', 'PoD Jelly Hall NE']),
@@ -589,6 +598,7 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'PoD Basement Ledge', 'Palace of Darkness', None, ['PoD Basement Ledge Drop Down', 'PoD Basement Ledge Up Stairs']),
create_dungeon_region(player, 'PoD Big Key Landing', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest'], ['PoD Big Key Landing Down Stairs', 'PoD Big Key Landing Hole']),
create_dungeon_region(player, 'PoD Falling Bridge Ledge', 'Palace of Darkness', None, ['PoD Falling Bridge WN', 'PoD Falling Bridge EN', 'PoD Falling Bridge Path S']),
create_dungeon_region(player, 'PoD Falling Bridge Mid', 'Palace of Darkness', None, ['PoD Falling Bridge Mid Path S', 'PoD Falling Bridge Mid Path N']),
create_dungeon_region(player, 'PoD Falling Bridge', 'Palace of Darkness', None, ['PoD Falling Bridge SW', 'PoD Falling Bridge Path N']),
create_dungeon_region(player, 'PoD Dark Maze', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom'], ['PoD Dark Maze EN', 'PoD Dark Maze E']),
create_dungeon_region(player, 'PoD Big Chest Balcony', 'Palace of Darkness', ['Palace of Darkness - Big Chest'], ['PoD Big Chest Balcony W']),
@@ -743,7 +753,8 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'Ice Pengator Switch', 'Ice Palace', None, ['Ice Pengator Switch WS', 'Ice Pengator Switch ES']),
create_dungeon_region(player, 'Ice Dead End', 'Ice Palace', None, ['Ice Dead End WS']),
create_dungeon_region(player, 'Ice Big Key', 'Ice Palace', ['Ice Palace - Big Key Chest'], ['Ice Big Key Push Block', 'Ice Big Key Down Ladder']),
create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Hole']),
create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Path']),
create_dungeon_region(player, 'Ice Bomb Drop - Top', 'Ice Palace', None, ['Ice Bomb Drop Hole']),
create_dungeon_region(player, 'Ice Stalfos Hint', 'Ice Palace', None, ['Ice Stalfos Hint SE']),
create_dungeon_region(player, 'Ice Conveyor', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Conveyor NE', 'Ice Conveyor to Crystal', 'Ice Conveyor SW']),
create_dungeon_region(player, 'Ice Conveyor - Crystal', 'Ice Palace', None, ['Ice Conveyor Crystal Exit']),
@@ -755,7 +766,8 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'Ice Firebar', 'Ice Palace', None, ['Ice Firebar ES', 'Ice Firebar Down Ladder']),
create_dungeon_region(player, 'Ice Falling Square', 'Ice Palace', None, ['Ice Falling Square SE', 'Ice Falling Square Hole']),
create_dungeon_region(player, 'Ice Spike Room', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Spike Room WS', 'Ice Spike Room Up Stairs', 'Ice Spike Room Down Stairs']),
create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES']),
create_dungeon_region(player, 'Ice Right H', 'Ice Palace', None, ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES', 'Ice Right H Path']),
create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Path']),
create_dungeon_region(player, 'Ice Tongue Pull', 'Ice Palace', None, ['Ice Tongue Pull Up Ladder', 'Ice Tongue Pull WS']),
create_dungeon_region(player, 'Ice Freezors', 'Ice Palace', ['Ice Palace - Freezor Chest'], ['Ice Freezors Up Ladder', 'Ice Freezors Hole', 'Ice Freezors Bomb Hole']),
create_dungeon_region(player, 'Ice Freezors Ledge', 'Ice Palace', None, ['Ice Freezors Ledge ES', 'Ice Freezors Ledge Hole']),
@@ -1163,7 +1175,7 @@ def create_shops(world, player):
def adjust_locations(world, player):
# handle pots
world.pot_contents[player] = PotSecretTable()
world.data_tables[player].pot_secret_table = PotSecretTable()
for location, datum in key_drop_data.items():
loc = world.get_location(location, player)
drop_location = 'Drop' == datum[0]
@@ -1171,6 +1183,10 @@ def adjust_locations(world, player):
loc.type = LocationType.Drop
snes_address, room, sprite_idx = datum[1]
loc.address = snes_address
sprite = world.data_tables[player].uw_enemy_table.room_map[room][sprite_idx]
sprite.location = loc
if world.enemy_shuffle[player] != 'none':
loc.note = enemy_names[sprite.kind]
else:
loc.type = LocationType.Pot
pot, pot_index = next((p, i) for i, p in enumerate(vanilla_pots[datum[1]]) if p.item == PotItem.Key)
@@ -1178,13 +1194,12 @@ def adjust_locations(world, player):
loc.address = pot_address(pot_index, datum[1])
loc.pot = pot
pot.location = loc
if (not world.dropshuffle[player] and drop_location)\
or (not drop_location and world.pottery[player] in ['none', 'cave']):
if ((world.dropshuffle[player] == 'none' and drop_location)
or (not drop_location and world.pottery[player] in ['none', 'cave'])):
loc.skip = True
else:
key_item = loc.item
key_item.location = None
loc.forced_item = None
loc.item = None
loc.event = False
@@ -1202,7 +1217,7 @@ def adjust_locations(world, player):
pot = world.get_location(loc, player).pot
else:
pot = pot_orig.copy()
world.pot_contents[player].room_map[super_tile].append(pot)
world.data_tables[player].pot_secret_table.room_map[super_tile].append(pot)
if valid_pot_location(pot, world.pot_pool[player], world, player):
create_pot_location(pot, pot_index, super_tile, world, player)
@@ -1215,6 +1230,7 @@ def adjust_locations(world, player):
loc.type = LocationType.Shop
# player address? it is in the shop table
index += 1
setup_enemy_locations(world, player)
# unreal events:
for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Frog', 'Missing Smith', 'Dark Blacksmith Ruins', 'Middle Aged Man',
'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Turtle Medallion Pad',
@@ -1312,7 +1328,7 @@ bonk_prize_table = {
'Dark Tree Line Tree 3': (0x26, 0x08, False, '', 'Dark Tree Line Area', 'in a tree'),
'Dark Tree Line Tree 4': (0x27, 0x04, False, '', 'Dark Tree Line Area', 'in a tree'),
'Hype Cave Statue': (0x28, 0x10, False, '', 'Hype Cave Area', 'encased in stone'),
'Cold Fairy Statue': (0x29, 0x02, False, '', 'Good Bee Cave', 'encased in stone')
'Cold Fairy Statue': (0x29, 0x02, False, '', 'Good Bee Cave (back)', 'encased in stone')
}
bonk_table_by_location_id = {0x2ABB00+(data[0]*6)+3: name for name, data in bonk_prize_table.items()}
@@ -1388,222 +1404,222 @@ flooded_keys_reverse = {
}
location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Bottle Merchant': (0x2eb18, 0x186339, False, 'with a merchant'),
'Flute Spot': (0x18014a, 0x18633d, False, 'underground'),
'Sunken Treasure': (0x180145, 0x186354, False, 'underwater'),
'Purple Chest': (0x33d68, 0x186359, False, 'from a box'),
"Blind's Hideout - Top": (0xeb0f, 0x1862e3, False, 'in a basement'),
"Blind's Hideout - Left": (0xeb12, 0x1862e6, False, 'in a basement'),
"Blind's Hideout - Right": (0xeb15, 0x1862e9, False, 'in a basement'),
"Blind's Hideout - Far Left": (0xeb18, 0x1862ec, False, 'in a basement'),
"Blind's Hideout - Far Right": (0xeb1b, 0x1862ef, False, 'in a basement'),
"Link's Uncle": (0x2df45, 0x18635f, False, 'with your uncle'),
'Secret Passage': (0xe971, 0x186145, False, 'near your uncle'),
'King Zora': (0xee1c3, 0x186360, False, 'at a high price'),
"Zora's Ledge": (0x180149, 0x186358, False, 'near Zora'),
'Waterfall Fairy - Left': (0xe9b0, 0x186184, False, 'near a fairy'),
'Waterfall Fairy - Right': (0xe9d1, 0x1861a5, False, 'near a fairy'),
"King's Tomb": (0xe97a, 0x18614e, False, 'alone in a cave'),
'Floodgate Chest': (0xe98c, 0x186160, False, 'in the dam'),
"Link's House": (0xe9bc, 0x186190, False, 'in your home'),
'Kakariko Tavern': (0xe9ce, 0x1861a2, False, 'in the bar'),
'Chicken House': (0xe9e9, 0x1861bd, False, 'near poultry'),
"Aginah's Cave": (0xe9f2, 0x1861c6, False, 'with Aginah'),
"Sahasrahla's Hut - Left": (0xea82, 0x186256, False, 'near the elder'),
"Sahasrahla's Hut - Middle": (0xea85, 0x186259, False, 'near the elder'),
"Sahasrahla's Hut - Right": (0xea88, 0x18625c, False, 'near the elder'),
'Sahasrahla': (0x2f1fc, 0x186365, False, 'with the elder'),
'Kakariko Well - Top': (0xea8e, 0x186262, False, 'in a well'),
'Kakariko Well - Left': (0xea91, 0x186265, False, 'in a well'),
'Kakariko Well - Middle': (0xea94, 0x186268, False, 'in a well'),
'Kakariko Well - Right': (0xea97, 0x18626b, False, 'in a well'),
'Kakariko Well - Bottom': (0xea9a, 0x18626e, False, 'in a well'),
'Blacksmith': (0x18002a, 0x186366, False, 'with the Smith'),
'Magic Bat': (0x180015, 0x18635e, False, 'with the Bat'),
'Sick Kid': (0x339cf, 0x186367, False, 'with the sick'),
'Hobo': (0x33e7d, 0x186368, False, 'with the Hobo'),
'Lost Woods Hideout': (0x180000, 0x186348, False, 'near a thief'),
'Lumberjack Tree': (0x180001, 0x186349, False, 'in a hole'),
'Cave 45': (0x180003, 0x18634b, False, 'alone in a cave'),
'Graveyard Cave': (0x180004, 0x18634c, False, 'alone in a cave'),
'Checkerboard Cave': (0x180005, 0x18634d, False, 'alone in a cave'),
'Mini Moldorm Cave - Far Left': (0xeb42, 0x186316, False, 'near Moldorms'),
'Mini Moldorm Cave - Left': (0xeb45, 0x186319, False, 'near Moldorms'),
'Mini Moldorm Cave - Right': (0xeb48, 0x18631c, False, 'near Moldorms'),
'Mini Moldorm Cave - Far Right': (0xeb4b, 0x18631f, False, 'near Moldorms'),
'Mini Moldorm Cave - Generous Guy': (0x180010, 0x18635a, False, 'near Moldorms'),
'Ice Rod Cave': (0xeb4e, 0x186322, False, 'in a frozen cave'),
'Bonk Rock Cave': (0xeb3f, 0x186313, False, 'alone in a cave'),
'Library': (0x180012, 0x18635c, False, 'near books'),
'Potion Shop': (0x180014, 0x18635d, False, 'near potions'),
'Lake Hylia Island': (0x180144, 0x186353, False, 'on an island'),
'Maze Race': (0x180142, 0x186351, False, 'at the race'),
'Desert Ledge': (0x180143, 0x186352, False, 'in the desert'),
'Desert Palace - Big Chest': (0xe98f, 0x186163, False, 'in Desert Palace'),
'Desert Palace - Torch': (0x180160, 0x186362, False, 'in Desert Palace'),
'Desert Palace - Map Chest': (0xe9b6, 0x18618a, False, 'in Desert Palace'),
'Desert Palace - Compass Chest': (0xe9cb, 0x18619f, False, 'in Desert Palace'),
'Desert Palace - Big Key Chest': (0xe9c2, 0x186196, False, 'in Desert Palace'),
'Desert Palace - Boss': (0x180151, 0x18633f, False, 'with Lanmolas'),
'Eastern Palace - Compass Chest': (0xe977, 0x18614b, False, 'in Eastern Palace'),
'Eastern Palace - Big Chest': (0xe97d, 0x186151, False, 'in Eastern Palace'),
'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186187, False, 'in Eastern Palace'),
'Eastern Palace - Big Key Chest': (0xe9b9, 0x18618d, False, 'in Eastern Palace'),
'Eastern Palace - Map Chest': (0xe9f5, 0x1861c9, False, 'in Eastern Palace'),
'Eastern Palace - Boss': (0x180150, 0x18633e, False, 'with the Armos'),
'Master Sword Pedestal': (0x289b0, 0x186369, False, 'at the Pedestal'),
'Hyrule Castle - Boomerang Chest': (0xe974, 0x186148, False, 'in Hyrule Castle'),
'Hyrule Castle - Map Chest': (0xeb0c, 0x1862e0, False, 'in Hyrule Castle'),
"Hyrule Castle - Zelda's Chest": (0xeb09, 0x1862dd, False, 'in Hyrule Castle'),
'Sewers - Dark Cross': (0xe96e, 0x186142, False, 'in the sewers'),
'Sewers - Secret Room - Left': (0xeb5d, 0x186331, False, 'in the sewers'),
'Sewers - Secret Room - Middle': (0xeb60, 0x186334, False, 'in the sewers'),
'Sewers - Secret Room - Right': (0xeb63, 0x186337, False, 'in the sewers'),
'Sanctuary': (0xea79, 0x18624d, False, 'in Sanctuary'),
'Castle Tower - Room 03': (0xeab5, 0x186289, False, 'in Castle Tower'),
'Castle Tower - Dark Maze': (0xeab2, 0x186286, False, 'in Castle Tower'),
'Old Man': (0xf69fa, 0x186364, False, 'with the Old Man'),
'Spectacle Rock Cave': (0x180002, 0x18634a, False, 'alone in a cave'),
'Paradox Cave Lower - Far Left': (0xeb2a, 0x1862fe, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Left': (0xeb2d, 0x186301, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Right': (0xeb30, 0x186304, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Far Right': (0xeb33, 0x186307, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'),
'Spiral Cave': (0xe9bf, 0x186193, False, 'in Spiral Cave'),
'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'),
'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'),
'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'),
'Tower of Hera - Map Chest': (0xe9ad, 0x186181, False, 'in Tower of Hera'),
'Tower of Hera - Big Key Chest': (0xe9e6, 0x1861ba, False, 'in Tower of Hera'),
'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'),
'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'),
'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'),
'Pyramid': (0x180147, 0x186356, False, 'on the Pyramid'),
'Catfish': (0xee185, 0x186361, False, 'with a catfish'),
'Stumpy': (0x330c7, 0x18636a, False, 'with Tree Boy'),
'Digging Game': (0x180148, 0x186357, False, 'underground'),
'Bombos Tablet': (0x180017, 0x18633c, False, 'at a monolith'),
'Hype Cave - Top': (0xeb1e, 0x1862f2, False, 'near a bat-like man'),
'Hype Cave - Middle Right': (0xeb21, 0x1862f5, False, 'near a bat-like man'),
'Hype Cave - Middle Left': (0xeb24, 0x1862f8, False, 'near a bat-like man'),
'Hype Cave - Bottom': (0xeb27, 0x1862fb, False, 'near a bat-like man'),
'Hype Cave - Generous Guy': (0x180011, 0x18635b, False, 'with a bat-like man'),
'Peg Cave': (0x180006, 0x18634e, False, 'alone in a cave'),
'Pyramid Fairy - Left': (0xe980, 0x186154, False, 'near a fairy'),
'Pyramid Fairy - Right': (0xe983, 0x186157, False, 'near a fairy'),
'Brewery': (0xe9ec, 0x1861c0, False, 'alone in a home'),
'C-Shaped House': (0xe9ef, 0x1861c3, False, 'alone in a home'),
'Chest Game': (0xeda8, 0x18636b, False, 'as a prize'),
'Bumper Cave Ledge': (0x180146, 0x186355, False, 'on a ledge'),
'Mire Shed - Left': (0xea73, 0x186247, False, 'near sparks'),
'Mire Shed - Right': (0xea76, 0x18624a, False, 'near sparks'),
'Superbunny Cave - Top': (0xea7c, 0x186250, False, 'in a connection'),
'Superbunny Cave - Bottom': (0xea7f, 0x186253, False, 'in a connection'),
'Spike Cave': (0xea8b, 0x18625f, False, 'beyond spikes'),
'Hookshot Cave - Top Right': (0xeb51, 0x186325, False, 'across pits'),
'Hookshot Cave - Top Left': (0xeb54, 0x186328, False, 'across pits'),
'Hookshot Cave - Bottom Right': (0xeb5a, 0x18632e, False, 'across pits'),
'Hookshot Cave - Bottom Left': (0xeb57, 0x18632b, False, 'across pits'),
'Floating Island': (0x180141, 0x186350, False, 'on an island'),
'Mimic Cave': (0xe9c5, 0x186199, False, 'in a cave of mimicry'),
'Swamp Palace - Entrance': (0xea9d, 0x186271, False, 'in Swamp Palace'),
'Swamp Palace - Map Chest': (0xe986, 0x18615a, False, 'in Swamp Palace'),
'Swamp Palace - Big Chest': (0xe989, 0x18615d, False, 'in Swamp Palace'),
'Swamp Palace - Compass Chest': (0xeaa0, 0x186274, False, 'in Swamp Palace'),
'Swamp Palace - Big Key Chest': (0xeaa6, 0x18627a, False, 'in Swamp Palace'),
'Swamp Palace - West Chest': (0xeaa3, 0x186277, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x18627d, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'),
'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'),
'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'),
"Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves Town"),
"Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves Town"),
"Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves Town"),
"Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves Town"),
"Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves Town"),
"Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves Town"),
"Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves Town"),
"Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'),
'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'),
'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'),
'Skull Woods - Big Chest': (0xe998, 0x18616c, False, 'in Skull Woods'),
'Skull Woods - Pot Prison': (0xe9a1, 0x186175, False, 'in Skull Woods'),
'Skull Woods - Pinball Room': (0xe9c8, 0x18619c, False, 'in Skull Woods'),
'Skull Woods - Big Key Chest': (0xe99e, 0x186172, False, 'in Skull Woods'),
'Skull Woods - Bridge Room': (0xe9fe, 0x1861d2, False, 'near Mothula'),
'Skull Woods - Boss': (0x180155, 0x186343, False, 'with Mothula'),
'Ice Palace - Compass Chest': (0xe9d4, 0x1861a8, False, 'in Ice Palace'),
'Ice Palace - Freezor Chest': (0xe995, 0x186169, False, 'in Ice Palace'),
'Ice Palace - Big Chest': (0xe9aa, 0x18617e, False, 'in Ice Palace'),
'Ice Palace - Iced T Room': (0xe9e3, 0x1861b7, False, 'in Ice Palace'),
'Ice Palace - Spike Room': (0xe9e0, 0x1861b4, False, 'in Ice Palace'),
'Ice Palace - Big Key Chest': (0xe9a4, 0x186178, False, 'in Ice Palace'),
'Ice Palace - Map Chest': (0xe9dd, 0x1861b1, False, 'in Ice Palace'),
'Ice Palace - Boss': (0x180157, 0x186345, False, 'with Kholdstare'),
'Misery Mire - Big Chest': (0xea67, 0x18623b, False, 'in Misery Mire'),
'Misery Mire - Map Chest': (0xea6a, 0x18623e, False, 'in Misery Mire'),
'Misery Mire - Main Lobby': (0xea5e, 0x186232, False, 'in Misery Mire'),
'Misery Mire - Bridge Chest': (0xea61, 0x186235, False, 'in Misery Mire'),
'Misery Mire - Spike Chest': (0xe9da, 0x1861ae, False, 'in Misery Mire'),
'Misery Mire - Compass Chest': (0xea64, 0x186238, False, 'in Misery Mire'),
'Misery Mire - Big Key Chest': (0xea6d, 0x186241, False, 'in Misery Mire'),
'Misery Mire - Boss': (0x180158, 0x186346, False, 'with Vitreous'),
'Turtle Rock - Compass Chest': (0xea22, 0x1861f6, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Left': (0xea1c, 0x1861f0, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Right': (0xea1f, 0x1861f3, False, 'in Turtle Rock'),
'Turtle Rock - Chain Chomps': (0xea16, 0x1861ea, False, 'in Turtle Rock'),
'Turtle Rock - Big Key Chest': (0xea25, 0x1861f9, False, 'in Turtle Rock'),
'Turtle Rock - Big Chest': (0xea19, 0x1861ed, False, 'in Turtle Rock'),
'Turtle Rock - Crystaroller Room': (0xea34, 0x186208, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186205, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186202, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x1861ff, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x1861fc, False, 'in Turtle Rock'),
'Turtle Rock - Boss': (0x180159, 0x186347, False, 'with Trinexx'),
'Palace of Darkness - Shooter Room': (0xea5b, 0x18622f, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186211, False, 'in Palace of Darkness'),
'Palace of Darkness - Stalfos Basement': (0xea49, 0x18621d, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Key Chest': (0xea37, 0x18620b, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x18620e, False, 'in Palace of Darkness'),
'Palace of Darkness - Map Chest': (0xea52, 0x186226, False, 'in Palace of Darkness'),
'Palace of Darkness - Compass Chest': (0xea43, 0x186217, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186220, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186223, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186229, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x18622c, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Chest': (0xea40, 0x186214, False, 'in Palace of Darkness'),
'Palace of Darkness - Harmless Hellway': (0xea46, 0x18621a, False, 'in Palace of Darkness'),
'Palace of Darkness - Boss': (0x180153, 0x186341, False, 'with Helmasaur King'),
"Ganons Tower - Bob's Torch": (0x180161, 0x186363, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Left': (0xead9, 0x1862ad, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Right': (0xeadc, 0x1862b0, False, "in Ganon's Tower"),
'Ganons Tower - Tile Room': (0xeae2, 0x1862b6, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x1862b9, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x1862bc, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x1862bf, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x1862c2, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x18628c, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x18628f, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186292, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186295, False, "in Ganon's Tower"),
'Ganons Tower - Map Chest': (0xead3, 0x1862a7, False, "in Ganon's Tower"),
'Ganons Tower - Firesnake Room': (0xead0, 0x1862a4, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186298, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x18629b, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x18629e, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x1862a1, False, "in Ganon's Tower"),
"Ganons Tower - Bob's Chest": (0xeadf, 0x1862b3, False, "in Ganon's Tower"),
'Ganons Tower - Big Chest': (0xead6, 0x1862aa, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x1862c8, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x1862cb, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Chest': (0xeaf1, 0x1862c5, False, "in Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x1862d1, False, "atop Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x1862d4, False, "atop Ganon's Tower"),
'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x1862d7, False, "atop Ganon's Tower"),
'Ganons Tower - Validation Chest': (0xeb06, 0x1862da, False, "atop Ganon's Tower"),
location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'),
'Bottle Merchant': (0x2eb18, 0x186df9, False, 'with a merchant'),
'Flute Spot': (0x18014a, 0x186dfd, False, 'underground'),
'Sunken Treasure': (0x180145, 0x186e14, False, 'underwater'),
'Purple Chest': (0x33d68, 0x186e19, False, 'from a box'),
"Blind's Hideout - Top": (0xeb0f, 0x186da3, False, 'in a basement'),
"Blind's Hideout - Left": (0xeb12, 0x186da6, False, 'in a basement'),
"Blind's Hideout - Right": (0xeb15, 0x186da9, False, 'in a basement'),
"Blind's Hideout - Far Left": (0xeb18, 0x186dac, False, 'in a basement'),
"Blind's Hideout - Far Right": (0xeb1b, 0x186daf, False, 'in a basement'),
"Link's Uncle": (0x2df45, 0x186e1f, False, 'with your uncle'),
'Secret Passage': (0xe971, 0x186c05, False, 'near your uncle'),
'King Zora': (0xee1c3, 0x186e20, False, 'at a high price'),
"Zora's Ledge": (0x180149, 0x186e18, False, 'near Zora'),
'Waterfall Fairy - Left': (0xe9b0, 0x186c44, False, 'near a fairy'),
'Waterfall Fairy - Right': (0xe9d1, 0x186c65, False, 'near a fairy'),
"King's Tomb": (0xe97a, 0x186c0e, False, 'alone in a cave'),
'Floodgate Chest': (0xe98c, 0x186c20, False, 'in the dam'),
"Link's House": (0xe9bc, 0x186c50, False, 'in your home'),
'Kakariko Tavern': (0xe9ce, 0x186c62, False, 'in the bar'),
'Chicken House': (0xe9e9, 0x186c7d, False, 'near poultry'),
"Aginah's Cave": (0xe9f2, 0x186c86, False, 'with Aginah'),
"Sahasrahla's Hut - Left": (0xea82, 0x186d16, False, 'near the elder'),
"Sahasrahla's Hut - Middle": (0xea85, 0x186d19, False, 'near the elder'),
"Sahasrahla's Hut - Right": (0xea88, 0x186d1c, False, 'near the elder'),
'Sahasrahla': (0x2f1fc, 0x186e25, False, 'with the elder'),
'Kakariko Well - Top': (0xea8e, 0x186d22, False, 'in a well'),
'Kakariko Well - Left': (0xea91, 0x186d25, False, 'in a well'),
'Kakariko Well - Middle': (0xea94, 0x186d28, False, 'in a well'),
'Kakariko Well - Right': (0xea97, 0x186d2b, False, 'in a well'),
'Kakariko Well - Bottom': (0xea9a, 0x186d2e, False, 'in a well'),
'Blacksmith': (0x18002a, 0x186e26, False, 'with the smith'),
'Magic Bat': (0x180015, 0x186e1e, False, 'with the bat'),
'Sick Kid': (0x339cf, 0x186e27, False, 'with the sick'),
'Hobo': (0x33e7d, 0x186e28, False, 'with the hobo'),
'Lost Woods Hideout': (0x180000, 0x186e08, False, 'near a thief'),
'Lumberjack Tree': (0x180001, 0x186e09, False, 'in a hole'),
'Cave 45': (0x180003, 0x186e0b, False, 'alone in a cave'),
'Graveyard Cave': (0x180004, 0x186e0c, False, 'alone in a cave'),
'Checkerboard Cave': (0x180005, 0x186e0d, False, 'alone in a cave'),
'Mini Moldorm Cave - Far Left': (0xeb42, 0x186dd6, False, 'near Moldorms'),
'Mini Moldorm Cave - Left': (0xeb45, 0x186dd9, False, 'near Moldorms'),
'Mini Moldorm Cave - Right': (0xeb48, 0x186ddc, False, 'near Moldorms'),
'Mini Moldorm Cave - Far Right': (0xeb4b, 0x186ddf, False, 'near Moldorms'),
'Mini Moldorm Cave - Generous Guy': (0x180010, 0x186e1a, False, 'near Moldorms'),
'Ice Rod Cave': (0xeb4e, 0x186de2, False, 'in a frozen cave'),
'Bonk Rock Cave': (0xeb3f, 0x186dd3, False, 'alone in a cave'),
'Library': (0x180012, 0x186e1c, False, 'near books'),
'Potion Shop': (0x180014, 0x186e1d, False, 'near potions'),
'Lake Hylia Island': (0x180144, 0x186e13, False, 'on an island'),
'Maze Race': (0x180142, 0x186e11, False, 'at the race'),
'Desert Ledge': (0x180143, 0x186e12, False, 'in the desert'),
'Desert Palace - Big Chest': (0xe98f, 0x186c23, False, 'in Desert Palace'),
'Desert Palace - Torch': (0x180160, 0x186e22, False, 'in Desert Palace'),
'Desert Palace - Map Chest': (0xe9b6, 0x186c4a, False, 'in Desert Palace'),
'Desert Palace - Compass Chest': (0xe9cb, 0x186c5f, False, 'in Desert Palace'),
'Desert Palace - Big Key Chest': (0xe9c2, 0x186c56, False, 'in Desert Palace'),
'Desert Palace - Boss': (0x180151, 0x186dff, False, 'with Lanmolas'),
'Eastern Palace - Compass Chest': (0xe977, 0x186c0b, False, 'in Eastern Palace'),
'Eastern Palace - Big Chest': (0xe97d, 0x186c11, False, 'in Eastern Palace'),
'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186c47, False, 'in Eastern Palace'),
'Eastern Palace - Big Key Chest': (0xe9b9, 0x186c4d, False, 'in Eastern Palace'),
'Eastern Palace - Map Chest': (0xe9f5, 0x186c89, False, 'in Eastern Palace'),
'Eastern Palace - Boss': (0x180150, 0x186dfe, False, 'with the Armos'),
'Master Sword Pedestal': (0x289b0, 0x186e29, False, 'at the pedestal'),
'Hyrule Castle - Boomerang Chest': (0xe974, 0x186c08, False, 'in Hyrule Castle'),
'Hyrule Castle - Map Chest': (0xeb0c, 0x186da0, False, 'in Hyrule Castle'),
"Hyrule Castle - Zelda's Chest": (0xeb09, 0x186d9d, False, 'in Hyrule Castle'),
'Sewers - Dark Cross': (0xe96e, 0x186c02, False, 'in the sewers'),
'Sewers - Secret Room - Left': (0xeb5d, 0x186df1, False, 'in the sewers'),
'Sewers - Secret Room - Middle': (0xeb60, 0x186df4, False, 'in the sewers'),
'Sewers - Secret Room - Right': (0xeb63, 0x186df7, False, 'in the sewers'),
'Sanctuary': (0xea79, 0x186d0d, False, 'in Sanctuary'),
'Castle Tower - Room 03': (0xeab5, 0x186d49, False, 'in Castle Tower'),
'Castle Tower - Dark Maze': (0xeab2, 0x186d46, False, 'in Castle Tower'),
'Old Man': (0xf69fa, 0x186e24, False, 'with the old man'),
'Spectacle Rock Cave': (0x180002, 0x186e0a, False, 'alone in a cave'),
'Paradox Cave Lower - Far Left': (0xeb2a, 0x186dbe, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Left': (0xeb2d, 0x186dc1, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Right': (0xeb30, 0x186dc4, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Far Right': (0xeb33, 0x186dc7, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Middle': (0xeb36, 0x186dca, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Left': (0xeb39, 0x186dcd, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Right': (0xeb3c, 0x186dd0, False, 'in a cave with seven chests'),
'Spiral Cave': (0xe9bf, 0x186c53, False, 'in Spiral Cave'),
'Ether Tablet': (0x180016, 0x186dfb, False, 'at a monolith'),
'Spectacle Rock': (0x180140, 0x186e0f, False, 'atop a rock'),
'Tower of Hera - Basement Cage': (0x180162, 0x186dfa, False, 'in Tower of Hera'),
'Tower of Hera - Map Chest': (0xe9ad, 0x186c41, False, 'in Tower of Hera'),
'Tower of Hera - Big Key Chest': (0xe9e6, 0x186c7a, False, 'in Tower of Hera'),
'Tower of Hera - Compass Chest': (0xe9fb, 0x186c8f, False, 'in Tower of Hera'),
'Tower of Hera - Big Chest': (0xe9f8, 0x186c8c, False, 'in Tower of Hera'),
'Tower of Hera - Boss': (0x180152, 0x186e00, False, 'with Moldorm'),
'Pyramid': (0x180147, 0x186e16, False, 'on the Pyramid'),
'Catfish': (0xee185, 0x186e21, False, 'with a catfish'),
'Stumpy': (0x330c7, 0x186e2a, False, 'with tree boy'),
'Digging Game': (0x180148, 0x186e17, False, 'underground'),
'Bombos Tablet': (0x180017, 0x186dfc, False, 'at a monolith'),
'Hype Cave - Top': (0xeb1e, 0x186db2, False, 'near a bat-like man'),
'Hype Cave - Middle Right': (0xeb21, 0x186db5, False, 'near a bat-like man'),
'Hype Cave - Middle Left': (0xeb24, 0x186db8, False, 'near a bat-like man'),
'Hype Cave - Bottom': (0xeb27, 0x186dbb, False, 'near a bat-like man'),
'Hype Cave - Generous Guy': (0x180011, 0x186e1b, False, 'with a bat-like man'),
'Peg Cave': (0x180006, 0x186e0e, False, 'alone in a cave'),
'Pyramid Fairy - Left': (0xe980, 0x186c14, False, 'near a fairy'),
'Pyramid Fairy - Right': (0xe983, 0x186c17, False, 'near a fairy'),
'Brewery': (0xe9ec, 0x186c80, False, 'alone in a home'),
'C-Shaped House': (0xe9ef, 0x186c83, False, 'alone in a home'),
'Chest Game': (0xeda8, 0x186e2b, False, 'as a prize'),
'Bumper Cave Ledge': (0x180146, 0x186e15, False, 'on a ledge'),
'Mire Shed - Left': (0xea73, 0x186d07, False, 'near sparks'),
'Mire Shed - Right': (0xea76, 0x186d0a, False, 'near sparks'),
'Superbunny Cave - Top': (0xea7c, 0x186d10, False, 'in a connection'),
'Superbunny Cave - Bottom': (0xea7f, 0x186d13, False, 'in a connection'),
'Spike Cave': (0xea8b, 0x186d1f, False, 'beyond spikes'),
'Hookshot Cave - Top Right': (0xeb51, 0x186de5, False, 'across pits'),
'Hookshot Cave - Top Left': (0xeb54, 0x186de8, False, 'across pits'),
'Hookshot Cave - Bottom Right': (0xeb5a, 0x186dee, False, 'across pits'),
'Hookshot Cave - Bottom Left': (0xeb57, 0x186deb, False, 'across pits'),
'Floating Island': (0x180141, 0x186e10, False, 'on an island'),
'Mimic Cave': (0xe9c5, 0x186c59, False, 'in a cave of mimicry'),
'Swamp Palace - Entrance': (0xea9d, 0x186d31, False, 'in Swamp Palace'),
'Swamp Palace - Map Chest': (0xe986, 0x186c1a, False, 'in Swamp Palace'),
'Swamp Palace - Big Chest': (0xe989, 0x186c1d, False, 'in Swamp Palace'),
'Swamp Palace - Compass Chest': (0xeaa0, 0x186d34, False, 'in Swamp Palace'),
'Swamp Palace - Big Key Chest': (0xeaa6, 0x186d3a, False, 'in Swamp Palace'),
'Swamp Palace - West Chest': (0xeaa3, 0x186d37, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x186d3d, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186d40, False, 'in Swamp Palace'),
'Swamp Palace - Waterfall Room': (0xeaaf, 0x186d43, False, 'in Swamp Palace'),
'Swamp Palace - Boss': (0x180154, 0x186e02, False, 'with Arrghus'),
"Thieves' Town - Big Key Chest": (0xea04, 0x186c98, False, "in Thieves Town"),
"Thieves' Town - Map Chest": (0xea01, 0x186c95, False, "in Thieves Town"),
"Thieves' Town - Compass Chest": (0xea07, 0x186c9b, False, "in Thieves Town"),
"Thieves' Town - Ambush Chest": (0xea0a, 0x186c9e, False, "in Thieves Town"),
"Thieves' Town - Attic": (0xea0d, 0x186ca1, False, "in Thieves Town"),
"Thieves' Town - Big Chest": (0xea10, 0x186ca4, False, "in Thieves Town"),
"Thieves' Town - Blind's Cell": (0xea13, 0x186ca7, False, "in Thieves Town"),
"Thieves' Town - Boss": (0x180156, 0x186e04, False, 'with Blind'),
'Skull Woods - Compass Chest': (0xe992, 0x186c26, False, 'in Skull Woods'),
'Skull Woods - Map Chest': (0xe99b, 0x186c2f, False, 'in Skull Woods'),
'Skull Woods - Big Chest': (0xe998, 0x186c2c, False, 'in Skull Woods'),
'Skull Woods - Pot Prison': (0xe9a1, 0x186c35, False, 'in Skull Woods'),
'Skull Woods - Pinball Room': (0xe9c8, 0x186c5c, False, 'in Skull Woods'),
'Skull Woods - Big Key Chest': (0xe99e, 0x186c32, False, 'in Skull Woods'),
'Skull Woods - Bridge Room': (0xe9fe, 0x186c92, False, 'near Mothula'),
'Skull Woods - Boss': (0x180155, 0x186e03, False, 'with Mothula'),
'Ice Palace - Compass Chest': (0xe9d4, 0x186c68, False, 'in Ice Palace'),
'Ice Palace - Freezor Chest': (0xe995, 0x186c29, False, 'in Ice Palace'),
'Ice Palace - Big Chest': (0xe9aa, 0x186c3e, False, 'in Ice Palace'),
'Ice Palace - Iced T Room': (0xe9e3, 0x186c77, False, 'in Ice Palace'),
'Ice Palace - Spike Room': (0xe9e0, 0x186c74, False, 'in Ice Palace'),
'Ice Palace - Big Key Chest': (0xe9a4, 0x186c38, False, 'in Ice Palace'),
'Ice Palace - Map Chest': (0xe9dd, 0x186c71, False, 'in Ice Palace'),
'Ice Palace - Boss': (0x180157, 0x186e05, False, 'with Kholdstare'),
'Misery Mire - Big Chest': (0xea67, 0x186cfb, False, 'in Misery Mire'),
'Misery Mire - Map Chest': (0xea6a, 0x186cfe, False, 'in Misery Mire'),
'Misery Mire - Main Lobby': (0xea5e, 0x186cf2, False, 'in Misery Mire'),
'Misery Mire - Bridge Chest': (0xea61, 0x186cf5, False, 'in Misery Mire'),
'Misery Mire - Spike Chest': (0xe9da, 0x186c6e, False, 'in Misery Mire'),
'Misery Mire - Compass Chest': (0xea64, 0x186cf8, False, 'in Misery Mire'),
'Misery Mire - Big Key Chest': (0xea6d, 0x186d01, False, 'in Misery Mire'),
'Misery Mire - Boss': (0x180158, 0x186e06, False, 'with Vitreous'),
'Turtle Rock - Compass Chest': (0xea22, 0x186cb6, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Left': (0xea1c, 0x186cb0, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Right': (0xea1f, 0x186cb3, False, 'in Turtle Rock'),
'Turtle Rock - Chain Chomps': (0xea16, 0x186caa, False, 'in Turtle Rock'),
'Turtle Rock - Big Key Chest': (0xea25, 0x186cb9, False, 'in Turtle Rock'),
'Turtle Rock - Big Chest': (0xea19, 0x186cad, False, 'in Turtle Rock'),
'Turtle Rock - Crystaroller Room': (0xea34, 0x186cc8, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186cc5, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186cc2, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x186cbf, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x186cbc, False, 'in Turtle Rock'),
'Turtle Rock - Boss': (0x180159, 0x186e07, False, 'with Trinexx'),
'Palace of Darkness - Shooter Room': (0xea5b, 0x186cef, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186cd1, False, 'in Palace of Darkness'),
'Palace of Darkness - Stalfos Basement': (0xea49, 0x186cdd, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Key Chest': (0xea37, 0x186ccb, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x186cce, False, 'in Palace of Darkness'),
'Palace of Darkness - Map Chest': (0xea52, 0x186ce6, False, 'in Palace of Darkness'),
'Palace of Darkness - Compass Chest': (0xea43, 0x186cd7, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186ce0, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186ce3, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186ce9, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x186cec, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Chest': (0xea40, 0x186cd4, False, 'in Palace of Darkness'),
'Palace of Darkness - Harmless Hellway': (0xea46, 0x186cda, False, 'in Palace of Darkness'),
'Palace of Darkness - Boss': (0x180153, 0x186e01, False, 'with Helmasaur King'),
"Ganons Tower - Bob's Torch": (0x180161, 0x186e23, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Left': (0xead9, 0x186d6d, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Right': (0xeadc, 0x186d70, False, "in Ganon's Tower"),
'Ganons Tower - Tile Room': (0xeae2, 0x186d76, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x186d79, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x186d7c, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x186d7f, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x186d82, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x186d4c, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x186d4f, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186d52, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186d55, False, "in Ganon's Tower"),
'Ganons Tower - Map Chest': (0xead3, 0x186d67, False, "in Ganon's Tower"),
'Ganons Tower - Firesnake Room': (0xead0, 0x186d64, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186d58, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x186d5b, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x186d5e, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x186d61, False, "in Ganon's Tower"),
"Ganons Tower - Bob's Chest": (0xeadf, 0x186d73, False, "in Ganon's Tower"),
'Ganons Tower - Big Chest': (0xead6, 0x186d6a, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x186d88, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x186d8b, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Chest': (0xeaf1, 0x186d85, False, "in Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x186d91, False, "atop Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x186d94, False, "atop Ganon's Tower"),
'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x186d97, False, "atop Ganon's Tower"),
'Ganons Tower - Validation Chest': (0xeb06, 0x186d9a, False, "atop Ganon's Tower"),
'Ganon': (None, None, False, 'from me'),
'Agahnim 1': (None, None, False, 'from Ganon\'s wizardry form'),
'Agahnim 2': (None, None, False, 'from Ganon\'s wizardry form'),
@@ -1670,7 +1686,6 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Potion Shop - Middle': (None, None, False, 'for sale near potions'),
'Potion Shop - Right': (None, None, False, 'for sale near potions'),
}
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
lookup_id_to_name.update(shop_table_by_location_id)
lookup_id_to_name.update(bonk_table_by_location_id)
+102 -280
View File
@@ -5,6 +5,8 @@ import json
import hashlib
import logging
import os
import Items
import RaceRandom as random
import struct
import sys
@@ -34,11 +36,14 @@ from InitialSram import InitialSram
from source.classes.SFX import randomize_sfx, randomize_sfxinstruments, randomize_songinstruments
from source.item.FillUtil import valid_pot_items
from source.dungeon.RoomList import Room0127
from source.dungeon.EnemyList import EnemySprite, setup_enemy_dungeon_tables
from source.dungeon.RoomObject import DoorObject
from source.enemizer.Bossmizer import boss_writes
from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '3f08ebba5a2c79b373dbd0c9151ade9b'
RANDOMIZERBASEHASH = '168c7d6e330a134e7565baedab79aa15'
class JsonRom(object):
@@ -219,182 +224,6 @@ def read_rom(stream):
has_smc_header = True
return buffer, has_smc_header
def patch_enemizer(world, player, rom, local_rom, enemizercli, random_sprite_on_hit):
baserom_path = os.path.abspath(local_rom.file)
unheadered_path = None
if local_rom.has_smc_header:
headered_path = baserom_path
unheadered_path = baserom_path = os.path.abspath(output_path('unheadered_rom.sfc'))
with open(headered_path, 'rb') as headered:
with open(baserom_path, 'wb') as unheadered:
unheadered.write(headered.read()[0x200:])
basepatch_path = os.path.abspath(local_path(os.path.join("data","base2current.json")))
enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json")
randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json'))
options_path = os.path.abspath(output_path('enemizer_options.json'))
enemizer_output_path = os.path.abspath(output_path('enemizer_output.json'))
# write options file for enemizer
options = {
'RandomizeEnemies': world.enemy_shuffle[player] != 'none',
'RandomizeEnemiesType': 3,
'RandomizeBushEnemyChance': world.enemy_shuffle[player] in ['random', 'legacy'],
'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default',
'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]],
'OHKO': False,
'RandomizeEnemyDamage': world.enemy_damage[player] != 'default',
'AllowEnemyZeroDamage': True,
'ShuffleEnemyDamageGroups': world.enemy_damage[player] != 'default',
'EnemyDamageChaosMode': world.enemy_damage[player] == 'random',
'EasyModeEscape': False,
'EnemiesAbsorbable': False,
'AbsorbableSpawnRate': 10,
'AbsorbableTypes': {
'FullMagic': True, 'SmallMagic': True, 'Bomb_1': True, 'BlueRupee': True, 'Heart': True, 'BigKey': True, 'Key': True,
'Fairy': True, 'Arrow_10': True, 'Arrow_5': True, 'Bomb_8': True, 'Bomb_4': True, 'GreenRupee': True, 'RedRupee': True
},
'BossMadness': False,
'RandomizeBosses': True,
'RandomizeBossesType': 0,
'RandomizeBossHealth': False,
'RandomizeBossHealthMinAmount': 0,
'RandomizeBossHealthMaxAmount': 300,
'RandomizeBossDamage': False,
'RandomizeBossDamageMinAmount': 0,
'RandomizeBossDamageMaxAmount': 200,
'RandomizeBossBehavior': False,
'RandomizeDungeonPalettes': False,
'SetBlackoutMode': False,
'RandomizeOverworldPalettes': False,
'RandomizeSpritePalettes': False,
'SetAdvancedSpritePalettes': False,
'PukeMode': False,
'NegativeMode': False,
'GrayscaleMode': False,
'GenerateSpoilers': False,
'RandomizeLinkSpritePalette': False,
'RandomizePots': False,
'ShuffleMusic': False,
'BootlegMagic': True,
'CustomBosses': False,
'AndyMode': False,
'HeartBeepSpeed': 0,
'AlternateGfx': False,
'ShieldGraphics': "shield_gfx/normal.gfx",
'SwordGraphics': "sword_gfx/normal.gfx",
'BeeMizer': False,
'BeesLevel': 0,
'RandomizeTileTrapPattern': world.enemy_shuffle[player] in ['random', 'legacy'],
'RandomizeTileTrapFloorTile': False,
'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'legacy' else world.enemy_shuffle[player] != 'none',
'RandomizeSpriteOnHit': random_sprite_on_hit,
'DebugMode': False,
'DebugForceEnemy': False,
'DebugForceEnemyId': 0,
'DebugForceBoss': False,
'DebugForceBossId': 0,
'DebugOpenShutterDoors': False,
'DebugForceEnemyDamageZero': False,
'DebugShowRoomIdInRupeeCounter': False,
'UseManualBosses': True,
'ManualBosses': {
'EasternPalace': world.get_dungeon("Eastern Palace", player).boss.enemizer_name,
'DesertPalace': world.get_dungeon("Desert Palace", player).boss.enemizer_name,
'TowerOfHera': world.get_dungeon("Tower of Hera", player).boss.enemizer_name,
'AgahnimsTower': 'Agahnim',
'PalaceOfDarkness': world.get_dungeon("Palace of Darkness", player).boss.enemizer_name,
'SwampPalace': world.get_dungeon("Swamp Palace", player).boss.enemizer_name,
'SkullWoods': world.get_dungeon("Skull Woods", player).boss.enemizer_name,
'ThievesTown': world.get_dungeon("Thieves Town", player).boss.enemizer_name,
'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name,
'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name,
'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name,
'GanonsTower1': [x for x in world.dungeons if x.player == player and 'bottom' in x.bosses.keys()][0].bosses['bottom'].enemizer_name,
'GanonsTower2': [x for x in world.dungeons if x.player == player and 'middle' in x.bosses.keys()][0].bosses['middle'].enemizer_name,
'GanonsTower3': [x for x in world.dungeons if x.player == player and 'top' in x.bosses.keys()][0].bosses['top'].enemizer_name,
'GanonsTower4': 'Agahnim2',
'Ganon': 'Ganon',
}
}
rom.write_to_file(randopatch_path)
with open(options_path, 'w') as f:
json.dump(options, f)
try:
subprocess.run([os.path.abspath(enemizercli),
'--rom', baserom_path,
'--seed', str(world.rom_seeds[player]),
'--base', basepatch_path,
'--randomizer', randopatch_path,
'--enemizer', options_path,
'--output', enemizer_output_path],
cwd=os.path.dirname(enemizercli),
check=True,
capture_output=True)
except subprocess.CalledProcessError as e:
from Main import EnemizerError
enemizerMsg = world.fish.translate("cli","cli","Enemizer returned exit code: ") + str(e.returncode) + "\n"
enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied")
logging.error(f'Enemizer error output: {e.stderr.decode("utf-8")}\n')
raise EnemizerError(enemizerMsg)
with open(enemizer_basepatch_path, 'r') as f:
for patch in json.load(f):
rom.write_bytes(patch["address"], patch["patchData"])
with open(enemizer_output_path, 'r') as f:
for patch in json.load(f):
rom.write_bytes(patch["address"], patch["patchData"])
if world.get_dungeon("Thieves Town", player).boss.enemizer_name == "Blind":
rom.write_byte(0x04DE81, 0x6) # maiden spawn
# restore blind spawn code - necessary because the old enemizer clobbers this stuff
# this line could be commented out if ijwu's enemizer is used exclusively
# if keeping this line, note the jump to the dr_baserom's enemizer section
rom.write_bytes(0xEA081, [0x5c, 0x00, 0x80, 0xb7, 0xc9, 0x6, 0xf0, 0x24,
0xad, 0x3, 0x4, 0x29, 0x20, 0xf0, 0x1d])
rom.write_byte(0x200101, 0) # Do not close boss room door on entry.
rom.write_byte(0x1B0101, 0) # Do not close boss room door on entry. (for Ijwu's enemizer)
else:
rom.write_byte(0x04DE83, 0xB3) # maiden is now something else
if random_sprite_on_hit:
_populate_sprite_table()
sprites = list(_sprite_table.values())
if sprites:
while len(sprites) < 32:
sprites.extend(sprites)
random.shuffle(sprites)
for i, path in enumerate(sprites[:32]):
sprite = Sprite(path)
rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite)
rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette)
rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette)
if local_rom.has_smc_header:
try:
os.remove(unheadered_path)
except OSError:
pass
try:
os.remove(randopatch_path)
except OSError:
pass
try:
os.remove(options_path)
except OSError:
pass
try:
os.remove(enemizer_output_path)
except OSError:
pass
_sprite_table = {}
def _populate_sprite_table():
@@ -589,7 +418,7 @@ def handle_native_dungeon(location, itemid):
return itemid
def patch_rom(world, rom, player, team, enemized, is_mystery=False):
def patch_rom(world, rom, player, team, is_mystery=False):
random.seed(world.rom_seeds[player])
# progressive bow silver arrow hint hack
@@ -620,18 +449,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
location.pot.indicator = standing_item_flag
location.pot.standing_item_code = code
continue
elif location.type == LocationType.Drop:
if location.item.player != player:
code = 0xF9
else:
code = 0xF8
sprite_pointer = snes_to_pc(location.address)
rom.write_byte(sprite_pointer, handle_native_dungeon(location, itemid))
if code == 0xF9:
rom.write_byte(sprite_pointer+1, location.item.player)
else:
rom.write_byte(sprite_pointer+1, 0)
rom.write_byte(sprite_pointer+2, code)
elif location.type == LocationType.Drop: # handled in the sprite table routine
continue
elif location.type == LocationType.Bonk:
address = snes_to_pc(location.address)
@@ -800,7 +618,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
owFlags |= 0x0200
# setting spriteID to D9, a placeholder sprite we use to inform ROM to spawn a dynamic item
#for address in bonk_addresses:
for address in [b for b in bonk_addresses if b != 0x4D0AE]: # temp fix for screen 1A murahdahla sprite replacement
rom.write_byte(address, 0xD9)
# temporary fix for screen 1A
@@ -878,6 +695,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if ((world.collection_rate[player] or world.goal[player] == 'completionist')
and world.goal[player] not in ['triforcehunt', 'trinity', 'ganonhunt']):
dr_flags |= DROptions.Debug
rom.write_byte(snes_to_pc(0x308039), 1)
if world.doorShuffle[player] not in ['vanilla', 'basic'] and world.logic[player] != 'nologic'\
and world.mixed_travel[player] == 'prevent':
# PoD Falling Bridge or Hammjump
@@ -897,10 +715,12 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
dr_flags |= DROptions.OriginalPalettes
if world.experimental[player]:
dr_flags |= DROptions.DarkWorld_Spawns
if world.logic[player] != 'nologic':
if world.logic[player] not in ['owglitches', 'hybridglitches', 'nologic']:
dr_flags |= DROptions.Fix_EG
if world.door_type_mode[player] in ['big', 'all', 'chaos']:
dr_flags |= DROptions.BigKeyDoor_Shuffle
if world.dropshuffle[player] in ['underworld']:
dr_flags |= DROptions.EnemyDropIndicator
my_locations = world.get_filled_locations(player)
valid_locations = [l for l in my_locations if ((l.type == LocationType.Pot and not l.forced_item)
@@ -911,7 +731,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
valid_loc_by_dungeon = valid_dungeon_locations(valid_locations)
# fix hc big key problems (map and compass too)
if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player]
if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] != 'none'
or world.pottery[player] not in ['none', 'cave']):
rom.write_byte(0x151f1, 2)
rom.write_byte(0x15270, 2)
@@ -950,17 +770,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
for name, layout in world.key_layout[player].items():
offset = compass_data[name][4]//2
if world.keyshuffle[player] == 'universal':
rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops)
rom.write_byte(0x187010+offset, layout.max_chests + layout.max_drops)
else:
rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used
rom.write_byte(0x13f030+offset, layout.max_chests)
rom.write_byte(0x187010+offset, layout.max_chests)
builder = world.dungeon_layouts[player][name]
valid_cnt = len(valid_loc_by_dungeon[name])
if valid_cnt > 256:
logging.getLogger('').warning(f'{name} exceeds 256 in locations ({valid_cnt})')
rom.write_byte(0x13f080+offset, valid_cnt % 10)
rom.write_byte(0x13f090+offset, valid_cnt // 10)
rom.write_byte(0x13f0a0+offset, valid_cnt)
bk_status = 1 if builder.bk_required else 0
bk_status = 2 if builder.bk_provided else bk_status
rom.write_byte(0x13f040+offset*2, bk_status)
@@ -1046,9 +860,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
credits_total = len(valid_locations)
if world.dropshuffle[player] or world.pottery[player] != 'none':
if world.dropshuffle[player] != 'none' or world.pottery[player] != 'none':
rom.write_byte(0x142A50, 1) # StandingItemsOn
multiClientFlags = ((0x1 if world.dropshuffle[player] else 0)
multiClientFlags = ((0x1 if world.dropshuffle[player] != 'none' else 0)
| (0x2 if world.shopsanity[player] else 0)
| (0x4 if world.take_any[player] != 'none' else 0)
| (0x8 if world.pottery[player] != 'none' else 0)
@@ -1056,13 +870,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x142A51, multiClientFlags)
# StandingItemCounterMask
rom.write_byte(0x142A55, ((0x1 if world.pottery[player] not in ['none', 'cave'] else 0)
| (0x2 if world.dropshuffle[player] else 0)))
| (0x2 if world.dropshuffle[player] != 'none' else 0)))
if world.pottery[player] not in ['none', 'keys']:
# Cuccos should not prevent kill rooms from opening
rom.write_byte(snes_to_pc(0x0DB457), 0x40)
rom.write_byte(snes_to_pc(0x28AA56), 0 if world.pottery[player] == 'none' else 1)
write_int16(rom, 0x187010, credits_total) # dynamic credits
write_int16(rom, 0x180196, credits_total) # dynamic credits
if credits_total != 216:
# collection rate address (hi):
cr_address = 0x238055
@@ -1072,14 +886,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
last_top, last_bot = credits_digit(credits_total % 10)
if credits_total >= 1000:
thousands_top, thousands_bot = credits_digit((credits_total // 1000) % 10)
rom.write_byte(cr_pc, 0xa2) # slash
rom.write_byte(cr_pc, 0xDB) # slash
rom.write_byte(cr_pc+1, thousands_top)
rom.write_byte(cr_pc+0x1e, 0xc2) # slash
rom.write_byte(cr_pc+0x1e, 0xEE) # slash
rom.write_byte(cr_pc+0x1f, thousands_bot)
# modify stat config
stat_address = 0x23B969
owr_difference = 0x26 # can't remember why there is a difference between DR fork
stat_pc = snes_to_pc(stat_address - owr_difference)
stat_address = 0x23978C
stat_pc = snes_to_pc(stat_address)
rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1)
rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0)
rom.write_byte(stat_pc+3, 0x80) # change to four digits (from 60)
@@ -1139,8 +952,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# set light cones
rom.write_byte(0x180038, 0x01 if world.sewer_light_cone[player] else 0x00)
rom.write_byte(0x180039, 0x01 if world.light_world_light_cone else 0x00)
rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
GREEN_TWENTY_RUPEES = 0x47
TRIFORCE_PIECE = ItemFactory('Triforce Piece', player).code
@@ -1269,6 +1080,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
])
# item GFX changes
if world.bombbag[player]:
rom.write_byte(snes_to_pc(0x22C8A4), 0xE0) # use new bomb bag gfx
rom.write_byte(snes_to_pc(0x22BD52), 0x02)
# set Fountain bottle exchange items
rom.write_byte(0x348FF, ItemFactory(world.bottle_refills[player][0], player).code)
rom.write_byte(0x3493B, ItemFactory(world.bottle_refills[player][1], player).code)
@@ -1357,11 +1173,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
gametype = 0x04 # item
if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
or world.pottery[player] != 'none'):
if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla'
or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'):
gametype |= 0x02 # entrance/door
if enemized:
gametype |= 0x01 # enemizer
rom.write_byte(0x180211, gametype) # Game type
warningflags = 0x00 # none
@@ -1401,11 +1215,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if world.pseudoboots[player]:
rom.write_byte(0x18008E, 0x01)
rom.initial_sram.set_starting_equipment(world, player)
rom.write_byte(0x180034, 10 if not world.bombbag[player] else 0) # starting max bombs
rom.write_byte(0x180035, 30) # starting max arrows
rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode
#rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier
rom.write_byte(0x180043, 0x00) # Hammer always breaks barrier
rom.write_byte(0x02AF79, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom.write_byte(0x03A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom.write_byte(0x03A96D, 0xF0 if world.mode[player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
@@ -1418,20 +1230,21 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
(0x04 if 'magic' in world.escape_assist[player] else 0x00))) # Escape assist
if world.goal[player] in ['pedestal', 'triforcehunt']:
rom.write_byte(0x18003E, 0x01) # make ganon invincible
rom.write_byte(0x1801A8, 0x01) # make ganon invincible
elif world.goal[player] in ['dungeons']:
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
rom.write_byte(0x1801A8, 0x02) # make ganon invincible until all dungeons are beat
elif world.goal[player] in ['crystals', 'trinity']:
rom.write_byte(0x18003E, 0x04) # make ganon invincible until all crystals
rom.write_byte(0x1801A8, 0x04) # make ganon invincible until all crystals
elif world.goal[player] in ['ganonhunt']:
rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected
rom.write_byte(0x1801A8, 0x05) # make ganon invincible until all triforce pieces collected
elif world.goal[player] in ['completionist']:
rom.write_byte(0x18003E, 0x0a) # make ganon invincible until everything is collected
rom.write_byte(0x1801A8, 0x0B) # make ganon invincible until everything is collected
else:
rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected
rom.write_byte(0x1801A8, 0x03) # make ganon invincible until all crystals and aga 2 are collected
rom.write_byte(0x18005E, world.crystals_needed_for_gt[player])
rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player])
rom.write_byte(0x18019A, world.crystals_needed_for_gt[player])
rom.write_byte(0x1801A6, world.crystals_needed_for_ganon[player])
rom.write_byte(0x1801A2, 0x00) # ped requirement is vanilla, set to 0x1 for special requirements
# block HC upstairs doors in rain state in standard mode
prevent_rain = world.mode[player] == 'standard' and world.shuffle[player] != 'vanilla' and world.logic[player] != 'nologic'
@@ -1471,7 +1284,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x18003C, 0x00)
elif world.dungeon_counters[player] == 'on':
compass_mode = 0x02 # always on
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']):
compass_mode = 0x01 # show on pickup
if (world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default') or world.owMixed[player]:
@@ -1543,7 +1356,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# Bitfield - enable free items to show up in menu
#
# ----dcba
# ---edcba
# e - Bosses
# d - Compass
# c - Map
# b - Big Key
@@ -1553,7 +1367,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
| (0x02 if world.bigkeyshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00)
| (0x08 if world.compassshuffle[player] else 0x00))) # free roaming items in menu
| (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu
| (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon
# Map reveals
reveal_bytes = {
@@ -1585,10 +1400,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x180175, 0x01 if world.bow_mode[player].startswith('retro') else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.bow_mode[player].startswith('retro') else 0x00) # wood arrow cost
rom.write_byte(0x180178, 0x32 if world.bow_mode[player].startswith('retro') else 0x00) # silver arrow cost
rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) # rupees replace arrows under pots
if enemized:
rom.write_byte(0x1B152e, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1)
if world.bow_mode[player].startswith('retro'):
# rupees replace arrows under pots for original and enemizer code
rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1)
rom.write_byte(snes_to_pc(0x36837D), 0xDA if world.bow_mode[player].startswith('retro') else 0xE1)
if world.bow_mode[player].startswith('retro') or world.keyshuffle[player] == 'universal':
rom.write_byte(0x30052, 0xE4 if world.keyshuffle[player] == 'universal' else 0xDB) # replace arrows in fish prize from bottle merchant
rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows
rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows
@@ -1596,10 +1411,12 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
digging_game_rng = random.randint(1, 30) # set rng for digging game
rom.write_byte(0x180020, digging_game_rng)
rom.write_byte(0xEFD95, digging_game_rng)
glitches_enabled = world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix
rom.write_byte(0x1800A4, 0x01 if not glitches_enabled else 0x00) # enable POD EG fix
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
rom.write_byte(0x180358, 0x01 if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']) else 0x00)
rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00)
rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00)
# remove shield from uncle
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
@@ -1678,10 +1495,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if item_dungeon == 'Escape':
item_dungeon = 'Hyrule Castle'
is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon
# hera small key is 11th in list, 10th sprite because of overlord
if is_small_key_this_dungeon:
rom.write_byte(0x4E3BB, 0xE4)
world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.SmallKey
else:
rom.write_byte(0x4E3BB, 0xEB)
world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.HeartPiece
# fix trock doors for reverse entrances
if world.fix_trock_doors[player]:
@@ -1689,27 +1507,32 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
world.get_room(0x23, player).change(0, DoorKind.CaveEntrance)
if world.get_door('TR Eye Bridge SW', player).entranceFlag:
world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance)
# do this unconditionally - gets overwritten by RoomData in doorShufflemodes
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
# do this conditionally - don't mess with doors
if world.doorShuffle[player] == 'vanilla':
rom.initial_sram.pre_open_tr_bomb_doors() # preopen bombable exits
if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player]
if world.boss_shuffle[player] != 'none' or world.doorShuffle[player] != 'vanilla':
rom.write_byte(snes_to_pc(0x30835A), 1) # fix Prize On The Eyes
if world.boss_shuffle[player] != 'none':
boss_writes(world, player, rom)
write_enemy_shuffle_settings(world, player, rom)
if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.pottery[player] != 'none'):
for room in world.rooms:
if room.player == player and room.modified:
rom.write_bytes(room.address(), room.rom_data())
if room.index in world.data_tables[player].room_list:
t = [DoorObject(x[0], x[1]) for x in room.doorList]
world.data_tables[player].room_list[room.index].doors = t
else:
rom.write_bytes(room.address(), room.rom_data())
if world.pottery[player] not in ['none']:
rom.write_bytes(snes_to_pc(0x1F8375), int32_as_bytes(0x2B8000))
# make hammer pegs use different tiles
Room0127.write_to_rom(snes_to_pc(0x2B8000), rom)
if world.pot_contents[player]:
colorize_pots = (world.pottery[player] != 'vanilla'
if world.data_tables[player]:
colorize_pots = (world.pottery[player] != 'vanilla', 'lottery'
and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered']))
if world.pot_contents[player].size() > 0x2800:
raise Exception('Pot table is too big for current area')
world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots)
setup_enemy_dungeon_tables(world, player)
world.data_tables[player].write_to_rom(rom, colorize_pots, world.enemy_shuffle[player] == 'random')
write_enemizer_tweaks(rom, world, player)
write_strings(rom, world, player, team)
@@ -1717,15 +1540,14 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# write initial sram
rom.write_initial_sram()
rom.write_byte(0x18636C, 1 if world.remote_items[player] else 0)
rom.write_byte(0x187E30, 1 if world.remote_items[player] else 0)
# set rom name
# 21 bytes
from Main import __version__
from OverworldShuffle import __version__ as ORVersion
seedstring = f'{world.seed:09}' if isinstance(world.seed, int) else world.seed
# todo: change to DR when Enemizer is okay with DR
rom.name = bytearray(f'ER{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}O\0', 'utf8')[:21]
rom.name = bytearray(f'OR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x7FC0, rom.name)
@@ -1779,7 +1601,7 @@ def write_custom_shops(rom, world, player):
if item is None:
break
if world.shopsanity[player] or shop.type == ShopType.TakeAny:
rom.write_byte(0x186560 + shop.sram_address + index, 1)
rom.write_byte(0x186E40 + shop.sram_address + index, 1)
if world.shopsanity[player] and shop.region.name in shop_to_location_table:
loc_item = world.get_location(shop_to_location_table[shop.region.name][index], player).item
elif world.shopsanity[player] and shop.region.name in retro_shops:
@@ -1788,8 +1610,8 @@ def write_custom_shops(rom, world, player):
loc_item = ItemFactory(item['item'], player)
if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade'
and world.difficulty[player] != 'normal'):
# really should be 5A instead of B0 -- surprise!!!
item_id, price, replace, replace_price, item_max = 0xB0, [0, 0], 0xFF, [0, 0], 1
# it's a BeeTrap -- surprise!!!
item_id, price, replace, replace_price, item_max = Items.item_table['Bee Trap'][3], [0, 0], 0xFF, [0, 0], 1
else:
item_id = loc_item.code
price = int16_as_bytes(item['price'])
@@ -1811,6 +1633,7 @@ def write_enemizer_tweaks(rom, world, player):
rom.write_byte(snes_to_pc(0x1DF6D8), 0) # lets enemies walk on water instead of clipping into infinity?
rom.write_byte(snes_to_pc(0x0DB6B3), 0x82) # hovers don't need water necessarily?
def hud_format_text(text):
output = bytes()
for char in text.lower():
@@ -1882,17 +1705,7 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
# set heart color
if color == 'random':
color = random.choice(['red', 'blue', 'green', 'yellow'])
rom.write_byte(0x6FA1E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA20, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA22, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA24, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA26, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA28, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2A, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2C, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA2E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color])
rom.write_byte(0x187020, {'red': 0, 'blue': 1, 'green': 2, 'yellow': 3}[color])
# write link sprite if required
if sprite is not None:
@@ -1912,8 +1725,8 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
"J": (0x66, 0x8C), "K": (0x67, 0x8D), "L": (0x68, 0x8E), "M": (0x69, 0x8F), "N": (0x6A, 0x90),
"O": (0x6B, 0x91), "P": (0x6C, 0x92), "Q": (0x6D, 0x93), "R": (0x6E, 0x94), "S": (0x6F, 0x95),
"T": (0x70, 0x96), "U": (0x71, 0x97), "V": (0x72, 0x98), "W": (0x73, 0x99), "X": (0x74, 0x9A),
"Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0x77, 0x9d), ".": (0xA0, 0xC0), "/": (0xA2, 0xC2),
":": (0xA3, 0xC3), "_": (0xA6, 0xC6)}
"Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0xD9, 0xEC), ".": (0xDC, 0xEF), "/": (0xDB, 0xEE),
":": (0xDD, 0xF0), "_": (0xDE, 0xF1)}
return char_map[char] if char in char_map else (0x9F, 0x9F)
character_bytes = map(convert_char_to_credits, padded_author)
@@ -2143,6 +1956,16 @@ def write_string_to_rom(rom, target, string):
def write_strings(rom, world, player, team):
tt = TextTable()
tt.removeUnwantedText()
if world.shuffle[player] != 'vanilla':
tt['houlihan_room'] = CompressedTextMapper.convert(
" Crosskeys\n"
" Tournament\n"
" Winners\n{HARP}\n"
" ~~~2022~~~\n Schulzer\n\n"
" ~~~2021~~~\n Goomba\n\n"
" ~~~2020~~~\n Linlinlin\n\n"
" ~~~2019~~~\n Kohrek\n"
)
# Let's keep this guy's text accurate to the shuffle setting.
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'lite', 'lean']:
@@ -2672,7 +2495,6 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
write_int16(rom, 0x15AEE + 2*0x38, 0x00E0)
write_int16(rom, 0x15AEE + 2*0x25, 0x000C)
if world.is_tile_swapped(0x05, player):
rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC428), 0x00) # remove secret portal
@@ -2767,9 +2589,6 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
write_int16(rom, snes_to_pc(0x1af584), 0x14AE)
write_int16(rom, snes_to_pc(0x1af58c), 0x54AE)
rom.write_byte(0xF6E58, 0x80) # no whirlpool under castle gate
rom.write_byte(snes_to_pc(0x09D436), 0xF3) # replace whirlpool with harmless sprite
write_int16(rom, 0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door
write_int16(rom, 0xDBA71 + 2 * 0x35, 0x011C)
@@ -2789,6 +2608,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
rom.write_byte(0x1607C + 0x37, 0x0A)
write_int16(rom, 0x160CB + 2 * 0x37, 0x0000)
write_int16(rom, 0x16169 + 2 * 0x37, 0x811C)
del world.data_tables[player].ow_enemy_table[0xab][5] # remove castle gate warp
if world.is_tile_swapped(0x29, player):
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact
if world.is_tile_swapped(0x2c, player):
@@ -2875,9 +2695,11 @@ def update_compasses(rom, dungeon_locations, world, player):
for name, builder in layouts.items():
dungeon_id = compass_data[name][4]
dungeon_count = len(dungeon_locations[name])
if dungeon_count > 255:
logging.getLogger('').warning(f'{name} has more locations than 255. Need 16-bit compass counts')
rom.write_byte(0x187000 + dungeon_id//2, dungeon_count % 256)
rom.write_bytes(0x187040 + dungeon_id, int16_as_bytes(dungeon_count))
# total tiles
rom.write_bytes(0x187060 + dungeon_id, int16_as_bytes(((dungeon_count // 100) % 10) + 0x2490))
rom.write_bytes(0x187080 + dungeon_id, int16_as_bytes(((dungeon_count // 10) % 10) + 0x2490))
rom.write_bytes(0x1870A0 + dungeon_id, int16_as_bytes((dungeon_count % 10) + 0x2490))
if builder.bk_provided:
if provided_dungeon:
logging.getLogger('').warning('Multiple dungeons have forced BKs! Compass code might need updating?')
+209 -170
View File
@@ -11,9 +11,13 @@ from OWEdges import OWExitTypes
from OverworldGlitchRules import overworld_glitches_rules
from UnderworldGlitchRules import underworld_glitches_rules
from source.logic.Rule import RuleFactory
from source.dungeon.EnemyList import EnemySprite, Sprite
from source.enemizer.EnemyLogic import special_rules_check, special_rules_for_region, defeat_rule_single
from source.enemizer.EnemyLogic import defeat_rule_multiple, and_rule as and_rule_new, or_rule as or_rule_new
def set_rules(world, player):
if world.logic[player] == 'nologic':
logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!')
world.get_region('Menu', player).can_reach_private = lambda state: True
@@ -54,13 +58,17 @@ def set_rules(world, player):
bomb_rules(world, player)
pot_rules(world, player)
drop_rules(world, player)
challenge_room_rules(world, player)
if world.goal[player] == 'dungeons':
# require all dungeons to beat ganon
add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has_beaten_aga(player) and state.has('Beat Agahnim 2', player) and state.has_crystals(7, player))
elif world.goal[player] == 'ganon':
# require aga2 to beat ganon
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
elif world.goal[player] in ['crystals', 'ganon']:
add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
if world.goal[player] == 'ganon':
# require aga2 to beat ganon
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
elif world.goal[player] in ['triforcehunt', 'trinity']:
for location in world.get_region('Hyrule Castle Courtyard', player).locations:
if location.name == 'Murahdahla':
@@ -70,6 +78,7 @@ def set_rules(world, player):
elif world.goal[player] == 'completionist':
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
if (world.flute_mode[player] != 'active' and not world.is_tile_swapped(0x18, player)
and 'Ocarina (Activated)' not in list(map(str, [i for i in world.precollected_items if i.player == player]))):
if not world.is_copied_world:
@@ -146,6 +155,14 @@ def set_always_allow(spot, rule):
spot.always_allow = rule
def add_rule_new(spot, rule, combine='and'):
if combine == 'and':
spot.verbose_rule = and_rule_new(*[spot.verbose_rule, rule])
else:
spot.verbose_rule = or_rule_new(*[spot.verbose_rule, rule])
add_rule(spot, rule.rule_lambda, combine)
def add_rule(spot, rule, combine='and'):
old_rule = spot.access_rule
if combine == 'or':
@@ -269,6 +286,7 @@ def global_rules(world, player):
# underworld rules
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has_Mirror(player)) # can erase block - overridden in noglitches
set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up
set_rule(world.get_entrance('Hookshot Cave Bonk Path', player), lambda state: state.has('Hookshot', player) or state.has('Pegasus Boots', player))
set_rule(world.get_entrance('Hookshot Cave Hook Path', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Bumper Cave Bottom to Top', player), lambda state: state.has('Cape', player))
@@ -325,7 +343,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Skull Woods Rock (West)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Skull Woods Rock (East)', player), lambda state: state.can_lift_rocks(player))
# this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off?
set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: (state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player)))
set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))
set_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player))
@@ -399,13 +417,9 @@ def global_rules(world, player):
# Eastern Palace
# Eyegore room needs a bow
set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player))
# set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player))
# set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('Eastern Map Balcony Hook Path', player), lambda state: state.has('Hookshot', player))
if is_trapped('Eastern Single Eyegore ES'):
set_rule(world.get_entrance('Eastern Single Eyegore ES', player), lambda state: state.can_shoot_arrows(player))
if is_trapped('Eastern Duo Eyegores SE'):
set_rule(world.get_entrance('Eastern Duo Eyegores SE', player), lambda state: state.can_shoot_arrows(player))
# Boss rules. Same as below but no BK or arrow requirement.
set_defeat_dungeon_boss_rule(world.get_location('Eastern Palace - Prize', player))
@@ -424,24 +438,12 @@ def global_rules(world, 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 Circle of Pots NW', player), lambda state: state.can_kill_most_things(player))
if is_trapped('Tower Circle of Pots ES'):
set_rule(world.get_entrance('Tower Circle of Pots ES', 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_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player))
set_rule(world.get_entrance('PoD Arena Landing Bonk Path', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player))
if is_trapped('PoD Mimics 2 SW'):
set_rule(world.get_entrance('PoD Mimics 2 SW', player), lambda state: state.can_shoot_arrows(player))
# set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
# set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('PoD Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('PoD Map Balcony Drop Down', player), lambda state: state.has('Hammer', player))
set_rule(world.get_entrance('PoD Dark Pegs Landing to Right', player), lambda state: state.has('Hammer', player))
@@ -532,6 +534,7 @@ def global_rules(world, player):
if is_trapped('Ice Lobby SE'):
set_rule(world.get_entrance('Ice Lobby SE', player), lambda state: state.can_melt_things(player))
set_rule(world.get_entrance('Ice Hammer Block ES', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
set_rule(world.get_entrance('Ice Right H Path', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
set_rule(world.get_location('Ice Palace - Hammer Block Key Drop', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
set_rule(world.get_entrance('Ice Antechamber Hole', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
@@ -577,9 +580,10 @@ def global_rules(world, player):
set_rule(world.get_entrance('Mire Tile Room ES', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('Mire Attic Hint Hole', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('Mire Dark Shooters SW', player), lambda state: state.has('Cane of Somaria', player))
if is_trapped('Mire Dark Shooters SE'):
set_rule(world.get_entrance('Mire Dark Shooters SE', player),
lambda state: state.has('Cane of Somaria', player))
# Not: somaria doesn't work here, so this cannot be opened if trapped
# if is_trapped('Mire Dark Shooters SE'):
# set_rule(world.get_entrance('Mire Dark Shooters SE', player),
# lambda state: state.has('Cane of Somaria', player))
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player))
@@ -649,21 +653,12 @@ def global_rules(world, player):
set_rule(world.get_entrance('GT Mimics 2 NE', player), lambda state: state.can_shoot_arrows(player))
# consider access to refill room - interior doors would need a change
set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('GT Gauntlet 1 WN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 2 EN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 2 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 3 NW', player), lambda state: state.can_kill_most_things(player))
if not world.get_door('GT Gauntlet 3 SW', player).entranceFlag:
set_rule(world.get_entrance('GT Gauntlet 3 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 4 NW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 4 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 5 NW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Gauntlet 5 WS', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 1 SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 2 SE', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Wizzrobes 2 NE', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
# Need cape to safely get past trinexx backwards in this room, makes magic usage tighter
# Could not guarantee safety with byrna, not sure why
if world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].name == 'Trinexx':
add_rule(world.get_entrance('GT Quad Pot SW', player), lambda state: state.has('Cape', player))
set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player))
if is_trapped('GT Torch Cross WN'):
set_rule(world.get_entrance('GT Torch Cross WN', player), lambda state: state.has_fire_source(player))
@@ -763,6 +758,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Ice Bomb Jump Ledge Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Ledge', player), player))
set_rule(world.get_entrance('Ice Bomb Jump Catwalk Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Catwalk', player), player))
set_rule(world.get_entrance('Ice Bomb Drop Path', player), lambda state: state.can_hit_crystal(player))
set_rule(world.get_entrance('Ice Conveyor to Crystal', player), lambda state: state.can_hit_crystal(player))
set_rule(world.get_entrance('Ice Refill to Crystal', player), lambda state: state.can_hit_crystal(player) or state.can_reach_blue(world.get_region('Ice Refill', player), player))
@@ -850,7 +846,7 @@ def global_rules(world, player):
add_key_logic_rules(world, player)
if world.logic[player] == 'hybridglitches':
if world.logic[player] == 'hybridglitches' and not world.is_copied_world:
add_hmg_key_logic_rules(world, player)
# End of door rando rules.
@@ -873,9 +869,6 @@ def global_rules(world, player):
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player)
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
if world.goal[player] != 'ganonhunt':
add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
@@ -884,7 +877,9 @@ def bomb_rules(world, player):
bonkable_doors = ['Two Brothers House Exit (West)', 'Two Brothers House Exit (East)'] # Technically this is incorrectly defined, but functionally the same as what is intended.
bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Paradox Shop', 'Mini Moldorm Cave',
'Hookshot Cave Back to Middle', 'Hookshot Cave Front to Middle', 'Hookshot Cave Middle to Front',
'Hookshot Cave Middle to Back', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery',
'Hookshot Cave Middle to Back', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Fairy to Back',
'Good Bee Cave Front to Back', 'Good Bee Cave Back to Front', 'Capacity Upgrade East',
'Capacity Fairy Pool West', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery',
'Paradox Cave Chest Area NE', 'Blinds Hideout N', 'Kakariko Well (top to back)',
'Light Hype Fairy']
for entrance in bonkable_doors:
@@ -904,81 +899,10 @@ def bomb_rules(world, player):
add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player))
add_bunny_rule(world.get_location(location, player), player)
cave_kill_locations = ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy', 'Spiral Cave']
for location in cave_kill_locations:
add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player))
add_bunny_rule(world.get_location(location, player), player)
add_rule(world.get_entrance('Spiral Cave (top to bottom)', player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player))
add_bunny_rule(world.get_entrance('Spiral Cave (top to bottom)', player), player)
paradox_switch_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right', 'Paradox Cave Lower - Middle']
for location in paradox_switch_chests:
add_rule(world.get_location(location, player), lambda state: state.can_hit_crystal_through_barrier(player))
add_bunny_rule(world.get_location(location, player), player)
# Dungeon bomb logic
easy_kill_rooms = [ # Door, bool-bombable
('Hyrule Dungeon Armory S', True), # One green guard
('Hyrule Dungeon Armory ES', True), # One green guard
('Hyrule Dungeon Armory Boomerang WS', True), # One blue guard
('Desert Compass NE', True), # Three popos
('Desert Four Statues NW', True), # Four popos
('Desert Four Statues ES', True), # Four popos
('Hera Beetles WS', False), # Three blue beetles and only two pots, and bombs don't work.
('Thieves Basement Block WN', True), # One blue and one red zazak and one Stalfos. Two pots. Need to kill the third enemy somehow.
('Ice Pengator Trap NE', False), # Five pengators. Bomb-doable?
('TR Twin Pokeys EN', False), # Two pokeys
('TR Twin Pokeys SW', False), # Two pokeys
('GT Petting Zoo SE', False), # Dont make anyone do this room with bombs and/or pots.
('GT DMs Room SW', False) # Four red stalfos
]
conditional_kill_traps = [
('Hyrule Dungeon Armory Interior Key Door N', True),
('Desert Compass Key Door WN', True),
('Thieves Blocked Entry SW', True),
('TR Tongue Pull WS', True),
('TR Twin Pokeys NW', False),
]
for killdoor, bombable in easy_kill_rooms:
if bombable:
add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player)))
else:
add_rule(world.get_entrance(killdoor, player), lambda state: state.can_kill_most_things(player))
for kill_door, bombable in conditional_kill_traps:
if world.get_entrance(kill_door, player).door.trapped:
if bombable:
add_rule(world.get_entrance(kill_door, player),
lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player)))
else:
add_rule(world.get_entrance(kill_door, player), lambda state: state.can_kill_most_things(player))
add_rule(world.get_entrance('Ice Stalfos Hint SE', player), lambda state: state.can_use_bombs(player)) # Need bombs for big stalfos knights
add_rule(world.get_entrance('Mire Cross ES', player), lambda state: state.can_kill_most_things(player)) # 4 Sluggulas. Bombs don't work // or (state.can_use_bombs(player) and state.has('Magic Powder'), player)
if world.get_entrance('Mire Cross SW', player).door.trapped:
add_rule(world.get_entrance('Mire Cross SW', player), lambda state: state.can_kill_most_things(player))
enemy_kill_drops = [ # Location, bool-bombable
('Hyrule Castle - Map Guard Key Drop', True),
('Hyrule Castle - Boomerang Guard Key Drop', True),
('Hyrule Castle - Key Rat Key Drop', True),
# ('Hyrule Castle - Big Key Drop', True), # Pots are available
# ('Eastern Palace - Dark Eyegore Key Drop', True), # Pots are available
('Castle Tower - Dark Archer Key Drop', True),
# ('Castle Tower - Circle of Pots Key Drop', True), # Pots are available
# ('Skull Woods - Spike Corner Key Drop', True), # Pots are available
('Ice Palace - Jelly Key Drop', True),
('Ice Palace - Conveyor Key Drop', True),
('Misery Mire - Conveyor Crystal Key Drop', True),
('Turtle Rock - Pokey 1 Key Drop', True),
('Turtle Rock - Pokey 2 Key Drop', True),
# ('Ganons Tower - Mini Helmasaur Key Drop', True) # Pots are available
('Castle Tower - Room 03', True), # Two spring soliders
('Ice Palace - Compass Chest', True) # Pengators
]
for location, bombable in enemy_kill_drops:
if bombable:
add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.can_kill_most_things(player))
else:
add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player))
add_rule(world.get_location('Attic Cracked Floor', player), lambda state: state.can_use_bombs(player))
bombable_floors = ['PoD Pit Room Bomb Hole', 'Ice Bomb Drop Hole', 'Ice Freezors Bomb Hole', 'GT Bob\'s Room Hole']
@@ -1012,8 +936,57 @@ def bomb_rules(world, player):
for door in doors_to_bomb_check:
if door.kind(world) in [DoorKind.Dashable]:
add_rule(door.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player))
if door.dependents:
for dep in door.dependents:
add_rule(dep.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player))
elif door.kind(world) in [DoorKind.Bombable]:
add_rule(door.entrance, lambda state: state.can_use_bombs(player))
if door.dependents:
for dep in door.dependents:
add_rule(dep.entrance, lambda state: state.can_use_bombs(player))
def challenge_room_rules(world, player):
room_map = world.data_tables[player].uw_enemy_table.room_map
stats = world.data_tables[player].enemy_stats
for region, data in std_kill_rooms.items():
entrances, trap_ables, room_id, enemy_list = data
rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region)
for ent in entrances:
entrance = world.get_entrance(ent, player)
if not entrance.door or not entrance.door.entranceFlag:
add_rule_new(entrance, rule)
for ent in trap_ables:
entrance = world.get_entrance(ent, player)
if entrance.door.trapped and not entrance.door.entranceFlag:
add_rule_new(entrance, rule)
for region, data in kill_chests.items():
locations, room_id, enemy_list = data
rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region)
for loc in locations:
add_rule_new(world.get_location(loc, player), rule)
def get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region):
sprite_list = room_map[room_id]
sprite_region_pairs = []
for idx, sprite in enumerate(sprite_list):
if idx in enemy_list:
if not stats[sprite.kind].ignore_for_kill_room:
sprite_region_pairs.append((sprite, world.get_region(sprite.region, player)))
if region == 'Eastern Stalfos Spawn':
stalfos_spawn_exception(sprite_region_pairs, stats, world, player)
if sprite_region_pairs:
return defeat_rule_multiple(world, player, sprite_region_pairs)
return RuleFactory.static_rule(True)
def stalfos_spawn_exception(sprite_region_pairs, stats, world, player):
if stats[EnemySprite.Stalfos].health * 4 > 40:
for x in range(0, 4):
sprite_region_pairs.append((Sprite(0x00a8, EnemySprite.Stalfos, 0, 0, 0, 0, 'Eastern Stalfos Spawn'),
world.get_region('Eastern Stalfos Spawn', player)))
return sprite_region_pairs
def pot_rules(world, player):
@@ -1061,6 +1034,20 @@ def pot_rules(world, player):
add_rule(l, lambda state: state.can_hit_crystal(player))
def drop_rules(world, player):
data_tables = world.data_tables[player]
for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items():
for enemy in enemy_list:
if enemy.location:
rule = defeat_rule_single(world, player, enemy, enemy.location.parent_region)
if enemy.location.parent_region.name in special_rules_check:
rule = special_rules_for_region(world, player, enemy.location.parent_region.name,
enemy.location, rule)
if rule.rule_lambda is None:
raise Exception(f'Bad rule for enemy drop. Need to inspect this case: {hex(enemy.kind)}')
add_rule_new(enemy.location, rule)
def ow_inverted_rules(world, player):
if world.is_atgt_swapped(player):
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
@@ -1304,6 +1291,7 @@ def ow_bunny_rules(world, player):
add_bunny_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Iceberg Water Entry', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), player)
add_bunny_rule(world.get_entrance('Shopping Mall Water Drop', player), player)
add_bunny_rule(world.get_entrance('Bomber Corner Water Drop', player), player)
add_bunny_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), player)
@@ -1440,7 +1428,8 @@ def add_conditional_lamps(world, player):
'Sewers Water': {'sewer': True, 'entrances': ['Sewers Water S', 'Sewers Water W'], 'locations': []},
'Sewers Dark Aquabats': {'sewer': True, 'entrances': ['Sewers Dark Aquabats N', 'Sewers Dark Aquabats ES'], 'locations': []},
'Sewers Key Rat': {'sewer': True, 'entrances': ['Sewers Key Rat S', 'Sewers Key Rat NE'], 'locations': ['Hyrule Castle - Key Rat Key Drop']},
'Old Man Cave': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)']},
'Old Man Cave (East)': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)', 'Old Man Cave W']},
'Old Man Cave (West)': {'sewer': False, 'entrances': ['Old Man Cave E']},
'Old Man House Back': {'sewer': False, 'entrances': ['Old Man House Back to Front', 'Old Man House Exit (Top)']},
'Death Mountain Return Cave (left)': {'sewer': False, 'entrances': ['Death Mountain Return Cave E', 'Death Mountain Return Cave Exit (West)']},
'Death Mountain Return Cave (right)': {'sewer': False, 'entrances': ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']},
@@ -1485,7 +1474,7 @@ def swordless_rules(world, player):
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player))
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player))
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
@@ -1494,50 +1483,93 @@ def swordless_rules(world, player):
if not world.is_atgt_swapped(player):
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player)) # barrier gets removed after killing agahnim, rule for that added later
# todo: new traps
std_kill_rooms = {
'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], # One green guard
'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'], # One blue guard
'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], # Can use pots
'Desert Compass Room': ['Desert Compass NE'], # Three popos
'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'], # Four popos
'Hera Beetles': ['Hera Beetles WS'], # Three blue beetles and only two pots, and bombs don't work.
'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'], # Two ball and chain
'Tower Dark Archers': ['Tower Dark Archers WN'], # Not a kill room
'Tower Red Spears': ['Tower Red Spears WN'], # Two spear soldiers
'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'], # Two usain bolts
'Tower Circle of Pots': ['Tower Circle of Pots NW'], # Two spear soldiers. Plenty of pots.
'PoD Turtle Party': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], # Lots of turtles.
'Thieves Basement Block': ['Thieves Basement Block WN'], # One blue and one red zazak and one Stalfos. Two pots. Need weapon.
'Ice Stalfos Hint': ['Ice Stalfos Hint SE'], # Need bombs for big stalfos knights
'Ice Pengator Trap': ['Ice Pengator Trap NE'], # Five pengators. Bomb-doable?
'Mire 2': ['Mire 2 NE'], # Wizzrobes. Bombs dont work.
'Mire Cross': ['Mire Cross ES'], # 4 Sluggulas. Bombs don't work
'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], # Two pokeys
'GT Petting Zoo': ['GT Petting Zoo SE'], # Dont make anyone do this room with bombs.
'GT DMs Room': ['GT DMs Room SW'], # Four red stalfos
'GT Gauntlet 1': ['GT Gauntlet 1 WN'], # Stalfos/zazaks
'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], # Red stalfos
'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], # Blue zazaks
'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], # Red zazaks
'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], # Stalfos and zazak
'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'], # Wizzrobes. Bombs don't work
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work
} # all trap rooms?
'Hyrule Dungeon Armory Main': # One green guard
(['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], ['Hyrule Dungeon Armory Interior Key Door N'],
0x71, [0]),
'Hyrule Dungeon Armory Boomerang': # One blue guard
(['Hyrule Dungeon Armory Boomerang WS'], [], 0x71, [1]),
'Eastern Stalfos Spawn': # Can use pots up to a point see stalfos_spawn_exception
(['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], [], 0xa8, []),
'Eastern Single Eyegore':
(['Eastern Single Eyegore NE'], ['Eastern Single Eyegore ES'], 0xd8, [8, 9, 10]),
'Eastern Duo Eyegores':
(['Eastern Duo Eyegores NE'], ['Eastern Duo Eyegores SE'], 0xd8, [0, 1, 2, 3, 4, 5, 6, 7]),
'Desert Compass Room': # Three popos (beamos)
(['Desert Compass NE'], ['Desert Compass Key Door WN'], 0x085, [2, 3, 4, 5]),
'Desert Four Statues': # Four popos (beamos)
(['Desert Four Statues NW', 'Desert Four Statues ES'], [], 0x53, [5, 6, 8, 9, 10]),
'Hera Beetles': # Three blue beetles and only two pots, and bombs don't work.
(['Hera Beetles WS'], [], 0x31, [7, 8, 10]),
'Tower Gold Knights': # Two ball and chain
(['Tower Gold Knights SW', 'Tower Gold Knights EN'], [], 0xe0, [0, 1]),
'Tower Dark Archers': # Backwards kill room
(['Tower Dark Archers WN'], [], 0xc0, [0, 1, 3]),
'Tower Red Spears': # Two spear soldiers
(['Tower Red Spears WN'], [], 0xb0, [1, 2, 3, 4]),
'Tower Red Guards': # Two usain bolts
(['Tower Red Guards EN', 'Tower Red Guards SW'], [], 0xb0, [0, 5]),
'Tower Circle of Pots': # Two spear soldiers. Plenty of pots.
(['Tower Circle of Pots NW'], ['Tower Circle of Pots ES'], 0xb0, [7, 8, 9, 10]),
'PoD Mimics 1':
(['PoD Mimics 1 NW'], ['PoD Mimics 1 SW'], 0x4b, [0, 3, 4]),
'PoD Mimics 2':
(['PoD Mimics 2 NW'], ['PoD Mimics 2 SW'], 0x1b, [3, 4, 5]),
'PoD Turtle Party': # Lots of turtles.
(['PoD Turtle Party ES', 'PoD Turtle Party NW'], [], 0x0b, [4, 5, 6, 7, 8, 9]),
'Thieves Basement Block': # One blue and one red zazak and one Stalfos. Two pots. Need weapon.
(['Thieves Basement Block WN'], ['Thieves Blocked Entry SW'], 0x45, [1, 2, 3]),
'Ice Jelly Key':
(['Ice Jelly Key ES'], [], 0x0e, [1, 2, 3]),
'Ice Stalfos Hint': # Need bombs for big stalfos knights
(['Ice Stalfos Hint SE'], [], 0x3e, [1, 2]),
'Ice Pengator Trap': # Five pengators. Bomb-doable?
(['Ice Pengator Trap NE'], [], 0x6e, [0, 1, 2, 3, 4]),
'Mire 2': # Wizzrobes. Bombs dont work.
(['Mire 2 NE'], [], 0xd2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
'Mire Cross': # 4 Sluggulas. Bombs don't work
(['Mire Cross ES'], ['Mire Cross SW'], 0xb2, [5, 6, 7, 10, 11]),
'TR Twin Pokeys': # Two pokeys
(['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], ['TR Twin Pokeys NW'], 0x24, [3, 4, 5, 6]),
'TR Tongue Pull': # Kill zols for money
(['TR Tongue Pull NE'], ['TR Tongue Pull WS'], 0x04, [9, 13, 14]),
'GT Petting Zoo': # Don't make anyone do this room with bombs.
(['GT Petting Zoo SE'], [], 0x7d, [4, 5, 6, 7, 8, 10]),
'GT DMs Room': # Four red stalfos
(['GT DMs Room SW'], [], 0x7b, [2, 3, 4, 5, 8, 9, 10]),
'GT Gauntlet 1': # Stalfos/zazaks
(['GT Gauntlet 1 WN'], [], 0x5d, [3, 4, 5, 6]),
'GT Gauntlet 2': # Red stalfos
(['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], [], 0x5d, [0, 1, 2, 7]),
'GT Gauntlet 3': # Blue zazaks
(['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], [], 0x5d, [8, 9, 10, 11, 12]),
'GT Gauntlet 4': # Red zazaks
(['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], [], 0x6d, [0, 1, 2, 3]),
'GT Gauntlet 5': # Stalfos and zazak
(['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], [], 0x6d, [4, 5, 6, 7, 8]),
'GT Wizzrobes 1': # Wizzrobes. Bombs don't work
(['GT Wizzrobes 1 SW'], [], 0xa5, [2, 3, 7]),
'GT Wizzrobes 2': # Wizzrobes. Bombs don't work
(['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'], [], 0xa5, [0, 1, 4, 5, 6]),
'Spiral Cave (Top)': # for traversal in enemizer at low health
(['Spiral Cave (top to bottom)'], [], 0xEE, [0, 1, 2, 3, 4]),
} # all trap rooms? (Desert Trap Room, Thieves Trap Room currently subtile only)
std_kill_doors_if_trapped = {
'Hyrule Dungeon Armory Main': 'Hyrule Dungeon Armory Interior Key Door N',
# 'Eastern Single Eyegore ES', # arrow rule is sufficient
# 'Eastern Duo Eyegores S', # arrow rule is sufficient
'TR Twin Pokeys': 'TR Twin Pokeys NW',
'Thieves Basement Block': 'Thieves Blocked Entry SW',
'Desert Compass Room': 'Desert Compass Key Door WN',
'Mire Cross': 'Mire Cross SW',
'Tower Circle of Pots': 'Tower Circle of Pots ES',
# 'Ice Lobby S' # can melt rule is sufficient
kill_chests = {
'Tower Room 03': (['Castle Tower - Room 03'], 0xe0, [2, 3]),
'Ice Compass Room': (['Ice Palace - Compass Chest'], 0x2e, [0, 1, 2, 3, 4, 5]),
'Swamp Entrance': (['Swamp Palace - Entrance'], 0x28, [0, 1, 2, 3, 4]),
'GT Tile Room': (['Ganons Tower - Tile Room'], 0x8d, [1, 2, 3, 4]),
'Mini Moldorm Cave':
(['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left',
'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy'], 0x123, [0, 1, 2, 3]),
'Mimic Cave':
(['Mimic Cave'], 0x10c, [4, 5, 6, 7]),
}
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)
@@ -1589,12 +1621,12 @@ def standard_rules(world, player):
for loc in region.locations:
add_rule(loc, lambda state: standard_escape_rule(state))
if region.name in std_kill_rooms:
for ent in std_kill_rooms[region.name]:
for ent in std_kill_rooms[region.name][0]:
add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state))
if region.name in std_kill_doors_if_trapped:
ent = world.get_entrance(std_kill_doors_if_trapped[region.name], player)
if ent.door.trapped:
add_rule(ent, lambda state: standard_escape_rule(state))
for ent in std_kill_rooms[region.name][1]:
entrance = world.get_entrance(ent, player)
if entrance.door.trapped:
add_rule(entrance, 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_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player))
@@ -1930,8 +1962,9 @@ bunny_impassible_doors = {
'Thieves Hellway Blue Barrier', 'Thieves Hellway Crystal Blue Barrier', 'Thieves Attic ES',
'Thieves Basement Block Path', 'Thieves Blocked Entry Path', 'Thieves Conveyor Bridge Block Path',
'Thieves Conveyor Block Path', 'Ice Lobby WS', 'Ice Cross Left Push Block', 'Ice Cross Bottom Push Block Left',
'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block', 'Ice Stalfos Hint SE', 'Ice Bomb Jump EN',
'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS',
'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block',
'Ice Stalfos Hint SE', 'Ice Bomb Jump EN', 'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Right H Path',
'Ice Bomb Drop Path', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS',
'Ice Hookshot Ledge Path', 'Ice Hookshot Balcony Path', 'Ice Many Pots SW', 'Ice Many Pots WS',
'Ice Crystal Right Blue Hole', 'Ice Crystal Left Blue Barrier', 'Ice Big Chest Landing Push Blocks',
'Ice Backwards Room Hole', 'Ice Switch Room SE', 'Ice Antechamber NE', 'Ice Antechamber Hole', 'Mire Lobby Gap',
@@ -1963,13 +1996,16 @@ bunny_impassible_doors = {
'GT Validation Block Path'
}
# these should generally match trap_door_exceptions unless the switch is in the open/push block
bunny_impassible_if_trapped = {
'Hyrule Dungeon Armory Interior Key Door N', 'Eastern Pot Switch WN', 'Eastern Lobby NW',
'Eastern Lobby NE', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', 'PoD Mimics 2 SW',
'PoD Middle Cage S', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'Skull Torch Room WS',
'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW',
'Ice Tall Hint EN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW',
'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN',
'Eastern Lobby NE', 'Eastern Courtyard Ledge S', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES',
'PoD Mimics 1 SW', 'PoD Mimics 2 SW', 'PoD Middle Cage S', 'PoD Lobby N', 'Swamp Push Statue S',
'Skull 2 East Lobby WS', 'Skull Torch Room WS', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW',
'Thieves Blocked Entry SW', 'Ice Bomb Jump NW',
'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW',
'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Tongue Pull WS', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN',
'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'GT Hidden Spikes SE', 'GT Conveyor Cross EN',
'GT Speed Torch WN', 'Ice Lobby SE'
}
@@ -2001,6 +2037,9 @@ def add_key_logic_rules(world, player):
forbid_item(location, d_logic.small_key_name, player)
for door in d_logic.bk_doors:
add_rule(world.get_entrance(door.name, player), create_rule(d_logic.bk_name, player))
if door.dependents:
for dep in door.dependents:
add_rule(dep.entrance, create_rule(d_logic.bk_name, player))
for chest in d_logic.bk_chests:
big_chest = world.get_location(chest.name, player)
add_rule(big_chest, create_rule(d_logic.bk_name, player))
+89 -72
View File
@@ -106,7 +106,7 @@ Triforce_texts = [
" Whelp…\n that just\n happened",
" Oh hey…\n it's you",
"\n Wheeeeee!!",
" Time for\n another one?",
" Time for\n another one?",
" And\n\n scene",
"\n GOT EM!!",
"\n THE VALUUUE!!!",
@@ -479,7 +479,7 @@ class Credits(object):
],
'pedestal': [
SceneSmallCreditLine(19, 'and the master sword'),
SceneSmallAltCreditLine(21, 'sleeps again...'),
SceneSmallAltCreditLine(21, 'sleeps again···'),
SceneLargeCreditLine(23, 'Forever!'),
],
}
@@ -827,6 +827,7 @@ class CharTextMapper(object):
class RawMBTextMapper(CharTextMapper):
char_map = {' ': 0xFF,
'': 0x99, # Cursor
'': 0xC4,
'': 0xC5,
'?': 0xC6,
@@ -835,22 +836,15 @@ class RawMBTextMapper(CharTextMapper):
'-': 0xC9,
"🡄": 0xCA,
"🡆": 0xCB,
'': 0xCC,
'': 0x9F,
'.': 0xCD,
'~': 0xCE,
'': 0xCE,
':': 0xEA,
'@': [0x6A], # Links name (only works if compressed)
'>': [0x00, 0xD2, 0x00, 0xD3], # Link's face
"'": 0xD8,
'>': [0x00, 0x9B, 0x00, 0x9C], # Link's face
"'": 0x9D,
'': 0xD8,
'%': 0xDD, # Hylian Bird
'^': 0xDE, # Hylian Ankh
'=': 0xDF, # Hylian Wavy Lines
'': 0xE0,
'': 0xE1,
'': 0xE2,
'': 0xE3,
'': 0xE4, # Cursor
'¼': [0x00, 0xE5, 0x00, 0xE7], # ¼ heart
'½': [0x00, 0xE6, 0x00, 0xE7], # ½ heart
'¾': [0x00, 0xE8, 0x00, 0xE9], # ¾ heart
@@ -1035,22 +1029,29 @@ class RawMBTextMapper(CharTextMapper):
"": 0x0D,
"": 0x0E,
"": 0x0F,
"": 0x10,
"": 0x11,
"": 0x12,
"": 0x13,
"": 0x14,
"": 0x15,
"": 0x16,
"": 0x17,
"": 0x18,
"": 0x19,
"": 0x1A,
"": 0x1B,
"": 0x1C,
"": 0x1D,
"": 0x1E,
"": 0x1F,
"%": 0x10, # Hylian Bird
"^": 0x11, # Hylian Ankh
"=": 0x12, # Hylian Wavy Lines
"": 0x13,
"": 0x14,
"": 0x15,
"": 0x16,
#"様": 0x10,
#"子": 0x11,
#"湖": 0x12,
#"達": 0x13,
#"彼": 0x14,
#"女": 0x15,
#"言": 0x16,
#"祭": 0x17,
#"早": 0x18,
#"雨": 0x19,
#"剣": 0x1A,
#"盾": 0x1B,
#"解": 0x1C,
#"抜": 0x1D,
#"者": 0x1E,
#"味": 0x1F,
"": 0x20,
"": 0x21,
"": 0x22,
@@ -1276,7 +1277,7 @@ class RawMBTextMapper(CharTextMapper):
"": 0xFE,
"": 0xFF}
alpha_offset = 0x49
alpha_lower_offset = -0x31
alpha_lower_offset = 0x6F
number_offset = 0x70
@classmethod
@@ -1299,7 +1300,7 @@ class RawMBTextMapper(CharTextMapper):
class GoldCreditMapper(CharTextMapper):
char_map = {' ': 0x9F,
',': 0x34,
',': 0x34, # apostrophe/comma top
"'": 0x35,
'-': 0x36,
'.': 0x37,}
@@ -1321,21 +1322,23 @@ class RedCreditMapper(CharTextMapper):
class LargeCreditTopMapper(CharTextMapper):
char_map = {' ': 0x9F,
"'": 0x77,
'!': 0x78,
'.': 0xA0,
'#': 0xA1,
'/': 0xA2,
':': 0xA3,
',': 0xA4,
'?': 0xA5,
'=': 0xA6,
'"': 0xA7,
'-': 0xA8,
'·': 0xA9,
'': 0xA9,
'': 0xAA,
'': 0xAB,}
"'": 0xD9,
'"': 0xDA,
'/': 0xDB,
'.': 0xDC,
':': 0xDD,
'_': 0xDE,
'·': 0xDF,
'': 0xDF,
'': 0xE0,
'#': 0xE1,
'@': 0xE2,
'>': 0xE3,
'?': 0xE4,
'!': 0xE5,
'~': 0xE6,
',': 0xE7,
'-': 0xE8,}
alpha_offset = -0x04
alpha_lower_offset = -0x04
number_offset = 0x23
@@ -1343,21 +1346,23 @@ class LargeCreditTopMapper(CharTextMapper):
class LargeCreditBottomMapper(CharTextMapper):
char_map = {' ': 0x9F,
"'": 0x9D,
'!': 0x9E,
'.': 0xC0,
'#': 0xC1,
'/': 0xC2,
':': 0xC3,
',': 0xC4,
'?': 0xC5,
'=': 0xC6,
'"': 0xC7,
'-': 0xC8,
'·': 0xC9,
'': 0xC9,
'': 0xCA,
'': 0xCB,}
"'": 0xEC,
'"': 0xED,
'/': 0xEE,
'.': 0xEF,
':': 0xF0,
'_': 0xF1,
'·': 0xF2,
'': 0xF2,
'': 0xF3,
'#': 0xF4,
'@': 0xF5,
'>': 0xF6,
'?': 0xF7,
'!': 0xF8,
'~': 0xF9,
',': 0xFA,
'-': 0xFB,}
alpha_offset = 0x22
alpha_lower_offset = 0x22
number_offset = 0x49
@@ -1570,16 +1575,16 @@ class TextTable(object):
def setDefaultText(self):
text = self._text
text['set_cursor'] = bytearray([0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xE4, 0xFE, 0x68])
text['set_cursor2'] = bytearray([0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xE4, 0xFE, 0x68])
text['set_cursor'] = bytearray([0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x99, 0xFE, 0x68])
text['set_cursor2'] = bytearray([0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0x99, 0xFE, 0x68])
text['game_over_menu'] = CompressedTextMapper.convert("{SPEED0}\nSave-Continue\nSave-Quit\nContinue", False)
text['var_test'] = CompressedTextMapper.convert("0= ᚋ, 1= ᚌ\n2= ᚍ, 3= ᚎ", False)
text['follower_no_enter'] = CompressedTextMapper.convert("Can't you take me some place nice.")
text['choice_1_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71])
text['choice_2_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xF9, 0xFF, 0xFE, 0x71])
text['choice_3_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xE4, 0xFE, 0x71])
text['choice_1_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xFE, 0x72])
text['choice_2_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xFE, 0x72])
text['choice_1_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0x99, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71])
text['choice_2_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0x99, 0xF9, 0xFF, 0xFE, 0x71])
text['choice_3_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0x99, 0xFE, 0x71])
text['choice_1_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0x99, 0xF8, 0xFF, 0xFE, 0x72])
text['choice_2_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0x99, 0xFE, 0x72])
text['uncle_leaving_text'] = CompressedTextMapper.convert("I'm just going out for a pack of smokes.")
text['uncle_dying_sewer'] = CompressedTextMapper.convert("I've fallen and I can't get up, take this.")
text['tutorial_guard_1'] = CompressedTextMapper.convert("Only adults should travel at night.")
@@ -1642,7 +1647,7 @@ class TextTable(object):
text['sign_outside_magic_shop'] = CompressedTextMapper.convert("Welcome to the Magic Shoppe")
# 40
text['sign_death_mountain_cave_back'] = CompressedTextMapper.convert("Cave away from sky cabbages")
text['sign_east_of_links_house'] = CompressedTextMapper.convert(" Lake Hylia\n\n Also, a shop")
text['sign_east_of_links_house'] = CompressedTextMapper.convert("↓Lake Hylia\n\n Also, a shop")
text['sign_south_of_lumberjacks'] = CompressedTextMapper.convert("← Kakariko\n Village")
text['sign_east_of_desert'] = CompressedTextMapper.convert("← Desert\n\n It's hot.")
text['sign_east_of_sanctuary'] = CompressedTextMapper.convert("↑→ Potions!\n\nWish waterfall")
@@ -1774,7 +1779,10 @@ class TextTable(object):
text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!")
text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^")
text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.")
text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n ~~~2022~~~\nAndy\n\n ~~~2021~~~\nprdwong")
text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n"
" ~~~2023~~~\nEriror\n\n"
" ~~~2022~~~\nAndy\n\n"
" ~~~2021~~~\nprdwong")
text['telepathic_tile_palace_of_darkness'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a funny looking Enemizer")
# C0
text['telepathic_tile_desert_bonk_torch_room'] = CompressedTextMapper.convert("{NOBORDER}\nThings can be knocked down, if you fancy yourself a dashing dude.")
@@ -1784,7 +1792,13 @@ class TextTable(object):
text['telepathic_tile_ice_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.")
text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.")
text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.")
text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2021~~~\nDaaanty\n\n ~~~2019~~~\nJet082\n\n ~~~2018~~~\nAndy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174")
text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n"
" ~~~2023~~~\nnGanonsGoneWild\n\n"
" ~~~2022~~~\nObscure\n\n"
" ~~~2021~~~\nDaaanty\n\n"
" ~~~2019~~~\nJet082\n\n"
" ~~~2018~~~\nAndy\n\n"
" ~~~2017~~~\nA: ajneb174\nS: ajneb174")
text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\n ≥ Keep\n Release\n{CHOICE}")
text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\n ≥ Keep\n Release\n{CHOICE}")
text['no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.")
@@ -2012,7 +2026,10 @@ class TextTable(object):
text['ganon_fall_in_alt'] = CompressedTextMapper.convert("You think you are ready to face me?\n\nI will not die unless you complete your goals. Dingus!")
text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in your ears? I cannot die!")
# 190
text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n~~~HMG 2021~~~\nKrithel\n\n~~~OWG 2019~~~\nGlan\n\n~~~OWG 2018~~~\nChristosOwen\nthe numpty")
text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n"
"~~~HMG 2021~~~\nKrithel\n\n"
"~~~OWG 2019~~~\nGlan\n\n"
"~~~OWG 2018~~~\nChristosOwen\nthe numpty")
text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.")
text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.")
text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.")
+1
View File
@@ -66,6 +66,7 @@ def create_hybridmajor_connectors(world, player):
specrock_connectors,
]:
new_connectors = [(connector[0], connector[1], world.get_entrance(connector[2], player).connected_region) for connector in connectors]
new_connectors = [c for c in new_connectors if c[2] is not None]
create_no_logic_connections(player, world, new_connectors)
+49 -1
View File
@@ -7,6 +7,12 @@ import xml.etree.ElementTree as ET
from collections import defaultdict
from math import factorial
from itertools import count
import fileinput
import urllib.request
import urllib.parse
import yaml
from pathlib import Path
def int16_as_bytes(value):
@@ -14,6 +20,11 @@ def int16_as_bytes(value):
return [value & 0xFF, (value >> 8) & 0xFF]
def int24_as_bytes(value):
value = value & 0xFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF]
def int32_as_bytes(value):
value = value & 0xFFFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
@@ -716,6 +727,42 @@ def stack_size3a(size=2):
return size - 1
def find_and_replace():
for data_line in fileinput.input('scratch.txt', inplace=True):
if '=' in data_line:
one, two = data_line.split(' = ')
number = int(two.strip())
print(data_line.replace(two, hex(number)))
def load_yaml(path_list):
path = os.path.join(*path_list)
if os.path.exists(Path(path)):
with open(path, "r", encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.SafeLoader)
elif urllib.parse.urlparse(path).scheme in ['http', 'https']:
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
yaml_cache = {}
def load_cached_yaml(path_list):
path = os.path.join(*path_list)
if path in yaml_cache:
return yaml_cache[path]
else:
if os.path.exists(Path(path)):
with open(path, "r", encoding="utf-8") as f:
data = yaml.load(f, Loader=yaml.SafeLoader)
yaml_cache[path] = data
return data
elif urllib.parse.urlparse(path).scheme in ['http', 'https']:
data = yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
yaml_cache[path] = data
return data
class bidict(dict):
def __init__(self, *args, **kwargs):
super(bidict, self).__init__(*args, **kwargs)
@@ -750,4 +797,5 @@ if __name__ == '__main__':
# room_palette_data(old_rom=sys.argv[1])
# extract_data_from_us_rom(sys.argv[1])
# extract_data_from_jp_rom(sys.argv[1])
check_pots()
# check_pots()
find_and_replace()
+358 -363
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+1 -1
View File
@@ -4,7 +4,7 @@ meta:
settings:
1:
door_shuffle: vanilla
dropshuffle: true
dropshuffle: keys
experimental: true
goal: ganon
hints: false
+1 -1
View File
@@ -7,7 +7,7 @@ meta:
settings:
1:
door_shuffle: basic
dropshuffle: true
dropshuffle: keys
experimental: true
goal: ganon
hints: true
@@ -0,0 +1,70 @@
meta:
algorithm: balanced
players: 1
race: true
user_notes: ''
placements:
1:
Desert Palace - Boss: Triforce Piece
Eastern Palace - Boss: Triforce Piece
Ice Palace - Boss: Triforce Piece
Misery Mire - Boss: Triforce Piece
Palace of Darkness - Boss: Triforce Piece
Skull Woods - Boss: Triforce Piece
Swamp Palace - Boss: Triforce Piece
Thieves' Town - Boss: Triforce Piece
Tower of Hera - Boss: Triforce Piece
Turtle Rock - Boss: Triforce Piece
settings:
1:
accessibility: locations
aga_randomness: true
any_enemy_logic: allow_all
beemizer: 0
bigkeyshuffle: true
bombbag: false
boss_shuffle: none
bow_mode: progressive
collection_rate: true
compassshuffle: true
crystals_ganon: '7'
crystals_gt: random
decoupledoors: false
difficulty: normal
door_self_loops: false
door_shuffle: crossed
door_type_mode: big
dropshuffle: keys
dungeon_counters: 'on'
enemy_damage: default
enemy_health: default
enemy_shuffle: none
experimental: true
flute_mode: normal
goal: ganonhunt
hints: true
intensity: 3
item_functionality: normal
key_logic_algorithm: partial
keyshuffle: wild
logic: noglitches
mapshuffle: true
mixed_travel: prevent
mode: open
openpyramid: auto
overworld_map: default
pottery: keys
pseudoboots: true
shopsanity: true
shuffle: crossed
shufflelinks: true
shufflepots: false
shuffletavern: true
standardize_palettes: standardize
swords: random
take_any: none
trap_door_mode: boss
triforce_goal: 8
triforce_pool: 10
start_inventory:
1: []
@@ -0,0 +1,31 @@
meta:
players: 1
race: true
settings:
1:
shopsanity: true
pottery: keys
dropshuffle: keys
keysanity: true
accessibility: locations
goal: crystals
crystals_gt: random
shuffle: crossed
shufflelinks: true
shuffletavern: true
door_shuffle: crossed
intensity: 3
door_type_mode: big
key_logic_algorithm: partial
experimental: true
dungeon_counters: 'on'
pseudoboots: true
hints: true
msu_resume: true
collection_rate: true
quickswap: true
@@ -0,0 +1,66 @@
start_inventory:
1:
- Compass (Eastern Palace)
- Compass (Desert Palace)
- Compass (Tower of Hera)
- Compass (Agahnims Tower)
- Compass (Palace of Darkness)
- Compass (Swamp Palace)
- Compass (Skull Woods)
- Compass (Thieves Town)
- Compass (Ice Palace)
- Compass (Misery Mire)
- Compass (Turtle Rock)
- Compass (Ganons Tower)
- Compass (Escape)
settings:
1:
accessibility: locations
aga_randomness: true
any_enemy_logic: allow_all
beemizer: '0'
bigkeyshuffle: 1
bombbag: 0
boss_shuffle: none
bow_mode: progressive
collection_rate: 1
compassshuffle: 1
crystals_ganon: '7'
crystals_gt: random
decoupledoors: 0
difficulty: normal
door_self_loops: 0
door_shuffle: crossed
door_type_mode: big
dropshuffle: underworld
dungeon_counters: 'on'
enemy_damage: default
enemy_health: default
enemy_shuffle: none
experimental: 1
flute_mode: normal
goal: crystals
hints: 1
intensity: 3
item_functionality: normal
key_logic_algorithm: partial
keyshuffle: wild
logic: noglitches
mapshuffle: 1
mixed_travel: prevent
mode: open
openpyramid: auto
overworld_map: default
pottery: keys
pseudoboots: 1
shopsanity: 1
shuffle: crossed
shufflelinks: 1
shufflepots: 0
shuffletavern: 1
standardize_palettes: standardize
swords: assured
take_any: none
trap_door_mode: boss
triforce_goal: 0
triforce_pool: 0
@@ -15,7 +15,7 @@ settings:
intensity: 3
door_type_mode: big
pottery: keys
dropshuffle: true
dropshuffle: keys
experimental: true
dungeon_counters: 'on'
hints: true
@@ -14,7 +14,7 @@ settings:
intensity: 3
door_type_mode: big
pottery: keys
dropshuffle: true
dropshuffle: keys
experimental: true
dungeon_counters: 'on'
hints: true
@@ -14,7 +14,7 @@ settings:
intensity: 3
door_type_mode: big
pottery: lottery
dropshuffle: true
dropshuffle: keys
experimental: true
dungeon_counters: 'on'
hints: true
@@ -16,7 +16,7 @@ settings:
intensity: 3
door_type_mode: big
pottery: keys
dropshuffle: true
dropshuffle: keys
experimental: true
dungeon_counters: 'on'
hints: true
+7 -2
View File
@@ -55,8 +55,9 @@
on: 1
off: 1
dropshuffle:
on: 1
off: 1
none: 4
keys: 1
underworld: 1
pottery:
none: 8
keys: 1
@@ -212,6 +213,10 @@
none: 3
shuffled: 2
random: 1
any_enemy_logic:
none: 1
allow_drops: 2
allow_all: 3
hints:
on: 1
off: 1
+4 -2
View File
@@ -35,8 +35,9 @@ door_self_loops:
on: 1
off: 1
dropshuffle:
on: 1
off: 1
none: 10 # fewer locations
keys: 1
underworld: 1
pottery:
none: 10 # fewer locations
keys: 1
@@ -152,6 +153,7 @@ enemy_shuffle: # shouldn't affect generation
shuffled: 1
random: 1
legacy: 0
any_enemy_logic: allow_all # more interesting logic
hints:
on: 1
off: 1
+13 -8
View File
@@ -96,8 +96,11 @@
"type": "bool"
},
"dropshuffle" : {
"action": "store_true",
"type": "bool"
"choices" : [
"none",
"keys",
"underworld"
]
},
"pottery" : {
"choices" : [
@@ -535,9 +538,6 @@
"bps": {
"action": "store_true"
},
"enemizercli": {
"setting": "enemizercli"
},
"shufflebosses": {
"choices": [
"none",
@@ -550,9 +550,7 @@
"shuffleenemies": {
"choices": [
"none",
"shuffled",
"random",
"legacy"
"shuffled"
]
},
"enemy_health": {
@@ -571,6 +569,13 @@
"random"
]
},
"any_enemy_logic": {
"choices": [
"none",
"allow_drops",
"allow_all"
]
},
"remote_items": {
"action": "store_true",
"type": "bool"
+11 -1
View File
@@ -343,7 +343,11 @@
"keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"],
"dropshuffle": [ "Keys dropped by enemies are shuffled and other items can take their place. (default: %(default)s)"],
"dropshuffle": [ "Controls how enemies drop items (default: %(default)s)",
"None: Enemies drops prize packs or keys as normal",
"Keys: Enemies that drop keys can drop other items and the keys are shuffled into the pool",
"Underworld: All killable enemies in the underworld can drop items"
],
"pottery": [ "Controls how items under pots are shuffled and if other items can take their place:",
"None: No pots are changed",
"Keys: Key pots are included in the location pool and other items can take their place",
@@ -382,6 +386,12 @@
"collection_rate": [ "Display collection rate (default: %(default)s)" ],
"pseudoboots": [ " Start with pseudo boots that allow dashing but no item checks (default: %(default)s)"],
"bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
"any_enemy_logic": [
"How to handle potential traversal between dungeon in Crossed door shuffle",
"None: Enemies that required unusual weaponry are not allowed to logical protect doors, chests, or key drops.",
"Allow_Drops: Drops are okay for those enemies, and a correct weapon is logically required",
"Allow_All: All enemies allowed anywhere. (Logic will be adapted to the placement)"
],
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
"usestartinventory": [ "Toggle usage of Starting Inventory." ],
"customizer": ["Path to a customizer file."],
+8 -3
View File
@@ -134,8 +134,10 @@
"randomizer.enemizer.enemyhealth.hard": "Hard",
"randomizer.enemizer.enemyhealth.expert": "Expert",
"randomizer.enemizer.enemizercli": "EnemizerCLI path: ",
"randomizer.enemizer.enemizercli.online": "(get online)",
"randomizer.enemizer.enemylogic": "Enemy Logic",
"randomizer.enemizer.enemylogic.none": "Forbid special enemies",
"randomizer.enemizer.enemylogic.allow_drops": "Item drops may have special enemies",
"randomizer.enemizer.enemylogic.allow_all": "Allow special enemies anywhere",
"randomizer.overworld.overworldshuffle": "Layout Shuffle",
@@ -355,7 +357,10 @@
"randomizer.item.colorizepots": "Colorize Randomized Pots",
"randomizer.item.potshuffle": "Pot Shuffle (Legacy)",
"randomizer.item.dropshuffle": "Shuffle Enemy Key Drops",
"randomizer.item.dropshuffle": "Enemy Drops",
"randomizer.item.dropshuffle.none": "None",
"randomizer.item.dropshuffle.keys": "Small Key Enemies",
"randomizer.item.dropshuffle.underworld": "Underworld Enemies",
"randomizer.item.keydropshuffle": "Enable Key Drop Shuffle (Legacy)",
"randomizer.item.take_any": "Take Any Caves",
@@ -4,9 +4,7 @@
"type": "selectbox",
"options": [
"none",
"shuffled",
"random",
"legacy"
"shuffled"
]
},
"bossshuffle": {
@@ -39,5 +37,19 @@
"expert"
]
}
},
"bottomEnemizerFrame": {
"enemylogic": {
"type": "selectbox",
"default": "allow_all",
"options": [
"none",
"allow_drops",
"allow_all"
],
"config": {
"width": 32
}
}
}
}
@@ -158,12 +158,17 @@
}
},
"dropshuffle": {
"type": "checkbox"
}
"type": "selectbox",
"default": "none",
"options": [
"none",
"keys",
"underworld"
]}
},
"leftPoolFrame2": {
"keydropshuffle": {
"type": "button",
"type": "checkbox",
"config": {
"command": "keydropshuffle"
}
+9 -1
View File
@@ -110,7 +110,8 @@ class CustomSettings(object):
args.pottery[p] = get_setting(settings['pottery'], args.pottery[p])
if get_setting(settings['keydropshuffle'], args.keydropshuffle[p]):
args.dropshuffle[p] = True
if args.dropshuffle[p] == 'none':
args.dropshuffle[p] = 'keys'
if args.pottery[p] == 'none':
args.pottery[p] = 'keys'
@@ -153,6 +154,7 @@ class CustomSettings(object):
args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], get_setting(settings['shuffleenemies'], args.shuffleenemies[p]))
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])
args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p])
args.any_enemy_logic[p] = get_setting(settings['any_enemy_logic'], args.any_enemy_logic[p])
args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p])
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
@@ -260,6 +262,11 @@ class CustomSettings(object):
return self.file_source['drops']
return None
def get_enemies(self):
if 'enemies' in self.file_source:
return self.file_source['enemies']
return None
def create_from_world(self, world, settings):
self.player_range = range(1, world.players + 1)
settings_dict, meta_dict = {}, {}
@@ -319,6 +326,7 @@ class CustomSettings(object):
settings_dict[p]['enemy_shuffle'] = world.enemy_shuffle[p]
settings_dict[p]['enemy_health'] = world.enemy_health[p]
settings_dict[p]['enemy_damage'] = world.enemy_damage[p]
settings_dict[p]['any_enemy_logic'] = world.any_enemy_logic[p]
settings_dict[p]['shufflepots'] = world.potshuffle[p]
settings_dict[p]['bombbag'] = world.bombbag[p]
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
+4 -4
View File
@@ -472,9 +472,9 @@ sfx_instrument_changes = [
SFXInstrumentChange(0x02, 0x17, 0x10, [0x1A96F5], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]),
SFXInstrumentChange(0x02, 0x18, 0x10, [0x1A9707], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]),
SFXInstrumentChange(0x02, 0x19, 0x10, [0x1A971F], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]),
SFXInstrumentChange(0x02, 0x1A, 0x01, [0x1A96C7], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1B, 0x11, [0x1A96B8], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1C, 0x11, [0x1A96B2], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1A, 0x01, [0x1A96C7], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1B, 0x11, [0x1A96B8], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1C, 0x11, [0x1A96B2], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]),
SFXInstrumentChange(0x02, 0x1D, 0x16, [0x1A966C], ban=[0x09, 0x10, 0x11, 0x13, 0x15, 0x18]),
SFXInstrumentChange(0x02, 0x1E, 0x01, [0x1A9928], type=Lg, ban=[0x06]),
SFXInstrumentChange(0x02, 0x1F, 0x02, [0x1A969A], type=Me|Be|Hd, ban=[0x09]),
@@ -492,7 +492,7 @@ sfx_instrument_changes = [
SFXInstrumentChange(0x02, 0x28, 0x11, [0x1A9638], type=[0x10, 0x11, 0x12]),
SFXInstrumentChange(0x02, 0x29, 0x01, [0x1A97F0], type=Sf), # accompanied $29+3B
SFXInstrumentChange(0x02, 0x2A, 0x0C, [0x1A93E4], type=Be, inc=[0x08, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x16]),
SFXInstrumentChange(0x02, 0x2B, 0x0E, [0x1A93A3], type=Me, ban=[0x08, 0x09, 0x0D], inc=[0x03, 0x11, 0x17]),
SFXInstrumentChange(0x02, 0x2B, 0x0E, [0x1A93A3], type=Me, ban=[0x08, 0x09, 0x0D], inc=[0x03, 0x17]),
SFXInstrumentChange(0x02, 0x2C, 0x00, [0x1A938A, 0x1A9398], type=Sh, ban=[0x13]), # chained $2C->3A
SFXInstrumentChange(0x02, 0x2C, 0x17, [0x1A9393, 0x1A939D], type=Me|Sh, ban=[0x0E], inc=[0x03, 0x11, 0x17]), # chained $2C->3A
SFXInstrumentChange(0x02, 0x2D, 0x0F, [0x1A9457], type=Me, ban=[0x09], inc=[0x03, 0x17]),
+2 -1
View File
@@ -127,7 +127,8 @@ SETTINGSTOPROCESS = {
"enemyshuffle": "shuffleenemies",
"bossshuffle": "shufflebosses",
"enemydamage": "enemy_damage",
"enemyhealth": "enemy_health"
"enemyhealth": "enemy_health",
"enemylogic": "any_enemy_logic"
},
"gameoptions": {
"nobgm": "disablemusic",
File diff suppressed because it is too large Load Diff
+266
View File
@@ -0,0 +1,266 @@
Ganon = 0x0
HC_NorthCorridor = 0x1
HC_SwitchRoom = 0x2
HoulihanRoom = 0x3
TR_CrystalRollerRoom = 0x4
Swamp_Arrghus = 0x6
Hera_Moldorm = 0x7
Cave_HealingFairy = 0x8
PalaceofDarkness0x09 = 0x9
PoD_StalfosTrapRoom = 0xa
PoD_TurtleRoom = 0xb
GT_EntranceRoom = 0xc
GT_Agahnim2 = 0xd
Ice_EntranceRoom = 0xe
GanonEvacuationRoute = 0x10
HC_BombableStockRoom = 0x11
Sanctuary = 0x12
TR_Hokku_BokkuKeyRoom2 = 0x13
TR_BigKeyRoom = 0x14
TurtleRock0x15 = 0x15
Swamp_SwimmingTreadmill = 0x16
Hera_MoldormFallRoom = 0x17
Cave0x18_BigFairyDropEntrance = 0x18
PoD_DarkMaze = 0x19
PoD_BigChestRoom = 0x1a
PoD_Mimics_MovingWallRoom = 0x1b
GT_IceArmos = 0x1c
GT_FinalHallway = 0x1d
Ice_BombFloor_BariRoom = 0x1e
Ice_Pengator_BigKeyRoom = 0x1f
Tower_Agahnim = 0x20
HC_KeyRatRoom = 0x21
HC_SewerTextTriggerRoom = 0x22
TR_WestExittoBalcony = 0x23
TR_DoubleHokku_Bokku_BigchestRoom = 0x24
Swamp_StatueRoom = 0x26
Hera_BigChest = 0x27
Swamp_EntranceRoom = 0x28
Skull_Mothula = 0x29
PoD_BigHubRoom = 0x2a
PoD_MapChest_FairyRoom = 0x2b
Cave0x2C_HookshotCaveBackdoor = 0x2c
Ice_CompassRoom = 0x2e
Cave_KakarikoWellHP = 0x2f
Tower_MaidenSacrificeChamber = 0x30
Hera_HardhatBeetlesRoom = 0x31
HC_SewerKeyChestRoom = 0x32
Desert_Lanmolas = 0x33
Swamp_PushBlockPuzzle_Pre_BigKeyRoom = 0x34
Swamp_BigKey_BSRoom = 0x35
Swamp_BigChestRoom = 0x36
Swamp_MapChest_WaterFillRoom = 0x37
Swamp_KeyPotRoom = 0x38
Skull_GibdoKey_MothulaHoleRoom = 0x39
PoD_BombableFloorRoom = 0x3a
PoD_SpikeBlock_ConveyorRoom = 0x3b
Cave0x3C_HookshotCave = 0x3c
GT_TorchRoom2 = 0x3d
Ice_StalfosKnights_ConveyorHellway = 0x3e
Ice_MapChestRoom = 0x3f
Tower_FinalBridgeRoom = 0x40
HC_FirstDarkRoom = 0x41
HC_6RopesRoom = 0x42
Desert_TorchPuzzle_MovingWallRoom = 0x43
TT_BigChestRoom = 0x44
TT_JailCellsRoom = 0x45
Swamp_CompassChestRoom = 0x46
Skull_GibdoTorchPuzzleRoom = 0x49
PoD_EntranceRoom = 0x4a
PoD_Warps_SouthMimicsRoom = 0x4b
GT_Mini_HelmasaurConveyorRoom = 0x4c
GT_MoldormRoom = 0x4d
Ice_Bomb_JumpRoom = 0x4e
IcePalaceCloneRoom_FairyRoom = 0x4f
HC_WestCorridor = 0x50
HC_ThroneRoom = 0x51
HC_EastCorridor = 0x52
Desert_Popos2_BeamosHellwayRoom = 0x53
Swamp_UpstairsPitsRoom = 0x54
CastleSecretEntrance_UncleDeathRoom = 0x55
Skull_KeyPot_TrapRoom = 0x56
Skull_BigKeyRoom = 0x57
Skull_BigChestRoom = 0x58
Skull_FinalSectionEntranceRoom = 0x59
PoD_HelmasaurKing = 0x5a
GT_SpikePitRoom = 0x5b
GT_Ganon_BallZ = 0x5c
GT_Gauntlet1_2_3 = 0x5d
Ice_LonelyFirebar = 0x5e
Ice_HiddenChest_SpikeFloorRoom = 0x5f
HC_WestEntranceRoom = 0x60
HC_MainEntranceRoom = 0x61
HC_EastEntranceRoom = 0x62
Desert_FinalSectionEntranceRoom = 0x63
TT_WestAtticRoom = 0x64
TT_EastAtticRoom = 0x65
Swamp_HiddenChest_HiddenDoorRoom = 0x66
Skull_CompassChestRoom = 0x67
Skull_KeyChest_TrapRoom = 0x68
PoD_RupeeRoom = 0x6a
GT_MimicsRooms = 0x6b
GT_LanmolasRoom = 0x6c
GT_Gauntlet4_5 = 0x6d
Ice_PengatorsRoom = 0x6e
HC_SmallCorridortoJailCells = 0x70
HC_BoomerangChestRoom = 0x71
HC_MapChestRoom = 0x72
Desert_BigChestRoom = 0x73
Desert_MapChestRoom = 0x74
Desert_BigKeyChestRoom = 0x75
Swamp_WaterDrainRoom = 0x76
Hera_EntranceRoom = 0x77
GanonsTower = 0x7b
GT_EastSideCollapsingBridge_ExplodingWallRoom = 0x7c
GT_Winder_WarpMazeRoom = 0x7d
Ice_HiddenChest_BombableFloorRoom = 0x7e
Ice_BigSpikeTrapsRoom = 0x7f
HC_JailCellRoom = 0x80
HC_NextToChasmRoom = 0x81
HC_BasementChasmRoom = 0x82
Desert_WestEntranceRoom = 0x83
Desert_MainEntranceRoom = 0x84
Desert_EastEntranceRoom = 0x85
Hera_TileRoom = 0x87
Eastern_FairyRoom = 0x89
GT_BlockPuzzle_SpikeSkip_MapChestRoom = 0x8b
GT_EastandWestDownstairs_BigChestRoom = 0x8c
GT_Tile_TorchPuzzleRoom = 0x8d
IcePalace0x8E = 0x8e
Mire_Vitreous = 0x90
Mire_FinalSwitchRoom = 0x91
Mire_DarkBombWall_SwitchesRoom = 0x92
Mire_DarkCaneFloorSwitchPuzzleRoom = 0x93
GT_FinalCollapsingBridgeRoom = 0x95
GT_Torches1Room = 0x96
Mire_TorchPuzzle_MovingWallRoom = 0x97
Mire_EntranceRoom = 0x98
Eastern_EyegoreKeyRoom = 0x99
GT_ManySpikes_WarpMazeRoom = 0x9b
GT_InvisibleFloorMazeRoom = 0x9c
GT_CompassChest_InvisibleFloorRoom = 0x9d
Ice_BigChestRoom = 0x9e
IcePalace0x9F = 0x9f
Mire_Pre_VitreousRoom = 0xa0
Mire_FishRoom = 0xa1
Mire_BridgeKeyChestRoom = 0xa2
MiseryMire0xA3 = 0xa3
TR_Trinexx = 0xa4
GT_WizzrobesRooms = 0xa5
GT_MoldormFallRoom = 0xa6
Hera_FairyRoom = 0xa7
Eastern_StalfosSpawnRoom = 0xa8
Eastern_BigChestRoom = 0xa9
Eastern_MapChestRoom = 0xaa
TT_MovingSpikes_KeyPotRoom = 0xab
TT_BlindTheThief = 0xac
IcePalace0xAE = 0xae
Ice_IceBridgeRoom = 0xaf
Tower_CircleofPots = 0xb0
Mire_HourglassRoom = 0xb1
Mire_SlugRoom = 0xb2
Mire_SpikeKeyChestRoom = 0xb3
TR_Pre_TrinexxRoom = 0xb4
TR_DarkMaze = 0xb5
TR_ChainChompsRoom = 0xb6
TR_MapChest_KeyChest_RollerRoom = 0xb7
Eastern_BigKeyRoom = 0xb8
Eastern_LobbyCannonballsRoom = 0xb9
Eastern_DarkAntifairy_KeyPotRoom = 0xba
TT_Hellway = 0xbb
TT_ConveyorToilet = 0xbc
Ice_BlockPuzzleRoom = 0xbe
IcePalaceCloneRoom_SwitchRoom = 0xbf
Tower_DarkBridgeRoom = 0xc0
Mire_CompassChest_TileRoom = 0xc1
Mire_BigHubRoom = 0xc2
Mire_BigChestRoom = 0xc3
TR_FinalCrystalSwitchPuzzleRoom = 0xc4
TR_LaserBridge = 0xc5
TurtleRock0xC6 = 0xc6
TR_TorchPuzzle = 0xc7
Eastern_ArmosKnights = 0xc8
Eastern_EntranceRoom = 0xc9
UnknownRoom = 0xca
TT_NorthWestEntranceRoom = 0xcb
TT_NorthEastEntranceRoom = 0xcc
Ice_HoletoKholdstareRoom = 0xce
Tower_DarkMaze = 0xd0
Mire_ConveyorSlug_BigKeyRoom = 0xd1
Mire_Mire02_WizzrobesRoom = 0xd2
TR_LaserKeyRoom = 0xd5
TR_EntranceRoom = 0xd6
Eastern_PreArmosKnightsRoom = 0xd8
Eastern_CanonballRoom = 0xd9
EasternPalace = 0xda
TT_Main_SouthWestEntranceRoom = 0xdb
TT_SouthEastEntranceRoom = 0xdc
Ice_Kholdstare = 0xde
Cave_BackwardsDeathMountainTopFloor = 0xdf
Tower_EntranceRoom = 0xe0
Cave_LostWoodsHP = 0xe1
Cave_LumberjacksTreeHP = 0xe2
Cave_HalfMagic = 0xe3
Cave_LostOldManFinalCave = 0xe4
Cave_LostOldManFinalCave2 = 0xe5
Cave0xE6 = 0xe6
Cave0xE7 = 0xe7
Cave0xE8 = 0xe8
Cave_SpectacleRockHP = 0xea
Cave0xEB = 0xeb
Cave0xED = 0xed
Cave_SpiralCave = 0xee
Cave_CrystalSwitch_5ChestsRoom = 0xef
Cave_LostOldManStartingCave = 0xf0
Cave_LostOldManStartingCave2 = 0xf1
House = 0xf2
House_OldWoman = 0xf3
House_AngryBrothers = 0xf4
House_AngryBrothers2 = 0xf5
Cave0xF8 = 0xf8
Cave0xF9 = 0xf9
Cave0xFA = 0xfa
Cave0xFB = 0xfb
Cave0xFD = 0xfd
Cave0xFE = 0xfe
Cave0xFF = 0xff
ShopInLostWoods0x100 = 0x100
ScaredLadyHouses = 0x101
SickKid = 0x102
Inn_BushHouse = 0x103
LinksHouse = 0x104
ShabadooHouse = 0x105
ChestGame_BombHouse = 0x106
Library_BombFarmRoom = 0x107
ChickenHouse = 0x108
WitchHut = 0x109
Aginah = 0x10a
SwampFloodwayRoom = 0x10b
MimicCave = 0x10c
CaveOutsideMiseryMire = 0x10d
Cave0x10E = 0x10e
Shop0x10F = 0x10f
Shop0x110 = 0x110
ArcherGame = 0x111
CaveShop0x112 = 0x112
KingsTomb = 0x113
WishingWell_Cave0x114 = 0x114
WishingWell_BigFairy = 0x115
FatFairy = 0x116
SpikeCave = 0x117
Shop0x118 = 0x118
BlindsHouse = 0x119
Mutant = 0x11a
MirrorCaveGroveAndTomb = 0x11b
BombShop = 0x11c
BlindsBasement = 0x11d
HypeCave = 0x11e
Shop0x11F = 0x11f
IceRodCave = 0x120
SmithHouse = 0x121
FortuneTellers = 0x122
MiniMoldormCave = 0x123
UnknownCave_BonkCave = 0x124
Cave0x125 = 0x125
CheckerBoardCave = 0x126
HammerPegCave = 0x127
+331
View File
@@ -0,0 +1,331 @@
vanilla_headers = {
0x0000: [0x41, 0x21, 0x13, 0x22, 0x07, 0x3D, 0x00, 0x00, 0x00, 0x10, 0xC0, 0x00, 0x00, 0x04],
0x0001: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x50, 0x52],
0x0002: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x18, 0x0D],
0x0003: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00],
0x0004: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x26, 0x14, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x08, 0x08],
0x0005: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00],
0x0006: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00],
0x0007: [0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00, 0x00, 0x00, 0x17, 0x17, 0xC0, 0x07, 0x06],
0x0008: [0xC0, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00],
0x0009: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x4B, 0x4A, 0x4A, 0x00, 0x0F],
0x000A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x09, 0x3A, 0x01, 0x0F, 0x07],
0x000B: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6A, 0x1B, 0xC0, 0x28, 0x0E],
0x000C: [0xC0, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x8C, 0x8C, 0x40],
0x000D: [0x40, 0x1B, 0x0E, 0x18, 0x05, 0x38, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00],
0x000E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x13],
0x000F: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00],
0x0010: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00],
0x0011: [0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0xC0, 0x1D, 0x04],
0x0012: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00],
0x0013: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00],
0x0014: [0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00],
0x0015: [0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x90, 0x08, 0x08],
0x0016: [0x90, 0x08, 0x08, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x20, 0x06, 0x05],
0x0017: [0x20, 0x06, 0x05, 0x19, 0x00, 0x35, 0x00, 0x00, 0x00, 0x27, 0x07, 0x27, 0x01, 0x0F],
0x0018: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00],
0x0019: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00],
0x001A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x6A, 0x68, 0x0F],
0x001B: [0x68, 0x0F, 0x07, 0x08, 0x00, 0x03, 0x1C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x1A, 0x0E],
0x001C: [0x00, 0x1A, 0x0E, 0x09, 0x00, 0x04, 0x3F, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x1B, 0x0E],
0x001D: [0x00, 0x1B, 0x0E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x20, 0x13, 0x0B],
0x001E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3E, 0x0E, 0x00, 0x13, 0x0B],
0x001F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x20, 0x0C, 0x02],
0x0020: [0x20, 0x0C, 0x02, 0x12, 0x00, 0x15, 0x25, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00],
0x0021: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00],
0x0022: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00],
0x0023: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00],
0x0024: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00],
0x0025: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20],
0x0026: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20],
0x0027: [0x20, 0x06, 0x05, 0x19, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x17, 0x31, 0x80, 0x0A],
0x0028: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x32, 0x1B, 0x00, 0x00, 0x00, 0x38, 0xCC, 0x0E, 0x09],
0x0029: [0xCC, 0x0E, 0x09, 0x1A, 0x02, 0x25, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00],
0x002A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00],
0x002B: [0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x13, 0x0B],
0x002C: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00],
0x002D: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00],
0x002E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00],
0x002F: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00],
0x0030: [0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x06, 0x05],
0x0031: [0x20, 0x06, 0x05, 0x19, 0x00, 0x37, 0x04, 0x22, 0x00, 0x77, 0x27, 0x77, 0x01, 0x01],
0x0032: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x04, 0x05],
0x0033: [0x00, 0x04, 0x05, 0x0B, 0x00, 0x15, 0x25, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00],
0x0034: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x80, 0x0A, 0x08],
0x0035: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00],
0x0036: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00],
0x0037: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00],
0x0038: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x20, 0x0D, 0x09],
0x0039: [0x20, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x20, 0x0F, 0x07, 0x19],
0x003A: [0x20, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x0F, 0x07],
0x003B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x07, 0x06],
0x003C: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00],
0x003D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00, 0x00, 0x00, 0x96, 0x96, 0xCC, 0x13, 0x0B],
0x003E: [0xCC, 0x13, 0x0B, 0x29, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x13, 0x0B],
0x003F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x27, 0x14, 0x00, 0x00, 0x00, 0x1F, 0x5F, 0xC0, 0x00],
0x0040: [0xC0, 0x00, 0x02, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB0, 0x01, 0x00],
0x0041: [0x01, 0x00, 0x00, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x42, 0x01, 0x01, 0x01],
0x0042: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x32, 0x68, 0x04],
0x0043: [0x68, 0x04, 0x05, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00],
0x0044: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00],
0x0045: [0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x0A, 0x08],
0x0046: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34],
0x0047: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00],
0x0048: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00],
0x0049: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00],
0x004A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x0F],
0x004B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x1A, 0x0E, 0x0C],
0x004C: [0x00, 0x1A, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x1A, 0x0E],
0x004D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x32, 0x3F, 0x00, 0x00, 0xA6, 0xA6, 0x00, 0x13, 0x0B],
0x004E: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x13, 0x0B],
0x004F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xC0, 0x00, 0x00, 0x04],
0x0050: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01],
0x0051: [0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xC0, 0x00, 0x00],
0x0052: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01],
0x0053: [0xC0, 0x04, 0x05, 0x0A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x63, 0x20, 0x0A, 0x08],
0x0054: [0x20, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x34, 0x01, 0x01, 0x10],
0x0055: [0x01, 0x01, 0x10, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00],
0x0056: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00],
0x0057: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28],
0x0058: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28, 0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00],
0x0059: [0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00],
0x005A: [0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00, 0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00],
0x005B: [0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00],
0x005C: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x24, 0x0E],
0x005D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x20, 0x13, 0x0B],
0x005E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x13, 0x0B],
0x005F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0xC0, 0x00],
0x0060: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00],
0x0061: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x51, 0x00, 0x09, 0x05],
0x0062: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00],
0x0063: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x53, 0xE0, 0x23, 0x0A],
0x0064: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xE0, 0x23, 0x0A],
0x0065: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xC0, 0x0A, 0x08, 0x11],
0x0066: [0xC0, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x0D, 0x09],
0x0067: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x22, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00],
0x0068: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00],
0x0069: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B],
0x006A: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B],
0x006B: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x08, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x24, 0x0E],
0x006C: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x03, 0x3F, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x24, 0x0E],
0x006D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x05, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00],
0x006E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x01, 0x01],
0x006F: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01],
0x0070: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01],
0x0071: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01],
0x0072: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x09, 0x05],
0x0073: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00],
0x0074: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00],
0x0075: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18],
0x0076: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26, 0xC0],
0x0077: [0xC0, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0x31, 0x87, 0x87, 0x00],
0x0078: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13],
0x0079: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13],
0x007A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13],
0x007B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13],
0x007C: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x20, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C],
0x007D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x9B, 0x20, 0x13, 0x0B, 0x1C],
0x007E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x2B, 0x17, 0x00, 0x00, 0x9E, 0x5E, 0x00, 0x13, 0x0B],
0x007F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x60, 0x01, 0x01],
0x0080: [0x60, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01],
0x0081: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00],
0x0082: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00],
0x0083: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00],
0x0084: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00],
0x0085: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01],
0x0086: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B],
0x0087: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B],
0x0088: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13],
0x0089: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13],
0x008A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00],
0x008B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00],
0x008C: [0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00, 0x28, 0x00, 0x1C, 0x0C, 0x0C, 0x1C, 0x00],
0x008D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x33, 0x29, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00],
0x008E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x80, 0x12, 0x0C],
0x008F: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00],
0x0090: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00],
0x0091: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x11, 0x0C],
0x0092: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00],
0x0093: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0xA2, 0x00, 0x25, 0x0E],
0x0094: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00],
0x0095: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00],
0x0096: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x68, 0x11, 0x0C],
0x0097: [0x68, 0x11, 0x0C, 0x1D, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x11, 0x0C],
0x0098: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0x01, 0x0B, 0x05],
0x0099: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x28, 0x0E],
0x009A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13],
0x009B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13],
0x009C: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B],
0x009D: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B, 0x00, 0x00, 0x7B, 0x20, 0x13, 0x0B, 0x1C],
0x009E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xBE, 0x00, 0x13, 0x0B],
0x009F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00],
0x00A0: [0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x11, 0x0C],
0x00A1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00],
0x00A2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x60, 0x19, 0x0D],
0x00A3: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00],
0x00A4: [0x60, 0x19, 0x0D, 0x17, 0x04, 0x25, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00],
0x00A5: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x25, 0x0E],
0x00A6: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x06, 0x05],
0x00A7: [0x00, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xC0, 0x0B, 0x05, 0x08],
0x00A8: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x03, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00A9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x89, 0xC0, 0x0B, 0x05, 0x08],
0x00AA: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00],
0x00AB: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xE0, 0x17, 0x0A],
0x00AC: [0xE0, 0x17, 0x0A, 0x20, 0x00, 0x25, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00],
0x00AD: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B],
0x00AE: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B],
0x00AF: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02],
0x00B0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02, 0x08, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x11],
0x00B1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB2, 0xC0, 0x11, 0x0C, 0x1D],
0x00B2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x03, 0x0E, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00],
0x00B3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00],
0x00B4: [0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x18, 0x0D],
0x00B5: [0x01, 0x18, 0x0D, 0x25, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x18, 0x0D],
0x00B6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x0B, 0x05],
0x00B7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00],
0x00B8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x27, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00],
0x00B9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00BA: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00],
0x00BB: [0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00],
0x00BC: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x13, 0x0B],
0x00BD: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B],
0x00BE: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B],
0x00BF: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F],
0x00C0: [0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0xB0, 0xD0, 0x00, 0x11],
0x00C1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x33, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00],
0x00C2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00],
0x00C3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00],
0x00C4: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x18, 0x0D],
0x00C5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00],
0x00C6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00],
0x00C7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00, 0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25],
0x00C8: [0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00C9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00],
0x00CA: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00],
0x00CB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00],
0x00CC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00],
0x00CD: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21],
0x00CE: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21],
0x00CF: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11],
0x00D0: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11],
0x00D1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x97, 0x00, 0x11, 0x0C],
0x00D2: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x0B, 0x05],
0x00D3: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00D4: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00D5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00],
0x00D6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00],
0x00D7: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00D8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00D9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00],
0x00DA: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x99, 0xE0, 0x14, 0x0B],
0x00DB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00],
0x00DC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00],
0x00DD: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00DE: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00DF: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x26, 0x02],
0x00E0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x01, 0x2A, 0x00, 0x00, 0x00, 0xD0, 0xC0, 0x07, 0x06],
0x00E1: [0xC0, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00E2: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00],
0x00E3: [0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00, 0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00],
0x00E4: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00],
0x00E5: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00],
0x00E6: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00E7: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00E8: [0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8],
0x00E9: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06],
0x00EA: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06],
0x00EB: [0x20, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xFB, 0x20, 0x20, 0x06],
0x00EC: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20],
0x00ED: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20],
0x00EE: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x20, 0x20, 0x06, 0x13],
0x00EF: [0x20, 0x20, 0x06, 0x13, 0x00, 0x02, 0x00, 0x08, 0x00, 0xFF, 0xDF, 0xFF, 0x00, 0x02],
0x00F0: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00F1: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00F2: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07],
0x00F3: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07],
0x00F4: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00F5: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00F6: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8],
0x00F7: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8],
0x00F8: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8],
0x00F9: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00FA: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x07, 0x06],
0x00FB: [0x00, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x20, 0x06],
0x00FC: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07],
0x00FD: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07],
0x00FE: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00],
0x00FF: [0x00, 0x07, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x05, 0x03],
0x0100: [0x00, 0x05, 0x03, 0x28, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00],
0x0101: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00],
0x0102: [0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00],
0x0103: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00],
0x0104: [0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00],
0x0105: [0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00],
0x0106: [0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00],
0x0107: [0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00],
0x0108: [0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00],
0x0109: [0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00],
0x010A: [0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A],
0x010B: [0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A, 0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00],
0x010C: [0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00],
0x010D: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00],
0x010E: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00],
0x010F: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00],
0x0110: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00],
0x0111: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00],
0x0112: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00],
0x0113: [0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00],
0x0114: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00],
0x0115: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00],
0x0116: [0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00],
0x0117: [0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00, 0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00],
0x0118: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00],
0x0119: [0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x1C, 0x0F],
0x011A: [0x00, 0x1C, 0x0F, 0x05, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00],
0x011B: [0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00],
0x011C: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00],
0x011D: [0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x20, 0x06],
0x011E: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00],
0x011F: [0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00],
0x0120: [0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00],
0x0121: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00],
0x0122: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00],
0x0123: [0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00],
0x0124: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x0125: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x0126: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x0127: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00],
0x0128: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x0129: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x012A: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x012B: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x012C: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x012D: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
0x012E: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
}
class RoomHeader:
def __init__(self, room_id, byte_array):
self.room_id = room_id
# todo: the rest of the header
self.byte_0 = byte_array[0] # bg2, collision, lights out
self.sprite_sheet = byte_array[3] # sprite gfx #
self.effect = byte_array[4]
def write_to_rom(self, rom, base_address):
room_offest = self.room_id*14
rom.write_byte(base_address + room_offest + 0, self.byte_0)
rom.write_byte(base_address + room_offest + 3, self.sprite_sheet)
rom.write_byte(base_address + room_offest + 4, self.effect)
def init_room_headers():
header_table = {}
for room_id, header_bytes in vanilla_headers.items():
header_table[room_id] = RoomHeader(room_id, header_bytes)
return header_table
+509 -18
View File
@@ -1,34 +1,505 @@
try:
from fast_enum import FastEnum
except ImportError:
from enum import IntFlag as FastEnum
from RoomData import DoorKind, Position
from source.dungeon.RoomObject import RoomObject, DoorObject
class Room:
def __init__(self, layout, layer1, layer2, doors):
def __init__(self, layout, layer1, layer2, doors, layer3=None):
self.layout = layout
self.layer1 = layer1
self.layer2 = layer2
self.layer3 = layer3
self.doors = doors
def write_to_rom(self, address, rom):
offset = 0
rom.write_bytes(address, self.layout)
address += 2
offset += 2
for obj in self.layer1:
rom.write_bytes(address, obj.data)
address += 3
rom.write_bytes(address, [0xFF, 0xFF])
address += 2
rom.write_bytes(address + offset, obj.data)
offset += 3
rom.write_bytes(address + offset, [0xFF, 0xFF])
offset += 2
for obj in self.layer2:
rom.write_bytes(address, obj.data)
address += 3
rom.write_bytes(address, [0xFF, 0xFF, 0xF0, 0xFF])
address += 4
rom.write_bytes(address + offset, obj.data)
offset += 3
rom.write_bytes(address + offset, [0xFF, 0xFF])
offset += 2
if self.layer3:
for obj in self.layer3:
rom.write_bytes(address + offset, obj.data)
offset += 3
rom.write_bytes(address + offset, [0xF0, 0xFF])
offset += 2
door_start = offset
for door in self.doors:
rom.write_bytes(address, door.get_bytes())
address += 2
rom.write_bytes(address, [0xFF, 0xFF])
return address + 2 # where the data ended
rom.write_bytes(address + offset, door.get_bytes())
offset += 2
rom.write_bytes(address + offset, [0xFF, 0xFF])
return door_start, offset + 2 # how many bytes were written
def find_all_pots(self):
pots = []
pots.extend([x for x in self.layer1 if x.data[2] in {0xFA, 0xFB} and not x.dummy])
pots.extend([x for x in self.layer2 if x.data[2] in {0xFA, 0xFB} and not x.dummy])
if self.layer3:
pots.extend([x for x in self.layer3 if x.data[2] in {0xFA, 0xFB} and not x.dummy])
return pots
Room0006 = Room([0xE1, 0x00],
[RoomObject(0x1FA15C, [0x1B, 0xA3, 0xC8]),
RoomObject(0x1FA15F, [0x58, 0xA3, 0xC8]),
RoomObject(0x1FA162, [0x1B, 0xD8, 0xC8]),
RoomObject(0x1FA165, [0x58, 0xD8, 0xC8]),
RoomObject(0x1FA168, [0x17, 0x9F, 0x3F]),
RoomObject(0x1FA16B, [0x54, 0x9F, 0x3F]),
RoomObject(0x1FA16E, [0x17, 0xA3, 0x79]),
RoomObject(0x1FA171, [0x14, 0xE1, 0x79]),
RoomObject(0x1FA174, [0x17, 0xEB, 0x40]),
RoomObject(0x1FA177, [0x54, 0xEB, 0x40]),
RoomObject(0x1FA17A, [0x6B, 0xA3, 0x7A]),
RoomObject(0x1FA17D, [0x68, 0xE1, 0x7A]),
RoomObject(0x1FA180, [0x21, 0x90, 0xF8]),
RoomObject(0x1FA183, [0x51, 0x90, 0xF8]),
RoomObject(0x1FA186, [0x0C, 0xA5, 0x7F]),
RoomObject(0x1FA189, [0x6C, 0xA5, 0x80])],
[], [DoorObject(Position.SouthW, DoorKind.Trap)])
Room0007 = Room([0x81, 0x1C],
[RoomObject(0x1FCAF0, [0x0A, 0x4E, 0x0D]),
RoomObject(0x1FCAF3, [0x0A, 0xAA, 0x0E]),
RoomObject(0x1FCAF6, [0x0B, 0x51, 0x61]),
RoomObject(0x1FCAF9, [0xC0, 0x2C, 0xA2]),
RoomObject(0x1FCAFC, [0xB0, 0x20, 0x0F]),
RoomObject(0x1FCAFF, [0xB0, 0x22, 0x62]),
RoomObject(0x1FCB02, [0xFE, 0xC1, 0x02]),
RoomObject(0x1FCB05, [0xC9, 0x38, 0x01]),
RoomObject(0x1FCB08, [0xFF, 0xA3, 0x82]),
RoomObject(0x1FCB0B, [0xBA, 0xE6, 0x10]),
RoomObject(0x1FCB0E, [0xE8, 0xAA, 0x62]),
RoomObject(0x1FCB11, [0xFF, 0x43, 0xB9]),
RoomObject(0x1FCB14, [0x53, 0x53, 0xE0]),
RoomObject(0x1FCB17, [0x91, 0x53, 0xE0]),
RoomObject(0x1FCB1A, [0x53, 0x91, 0xE0]),
RoomObject(0x1FCB1D, [0x91, 0x91, 0xE0]),
RoomObject(0x1FCB20, [0x3C, 0x6B, 0xC2]),
RoomObject(0x1FCB23, [0x3D, 0x9B, 0xC3]),
RoomObject(0x1FCB26, [0x54, 0xA6, 0xC3]),
RoomObject(0x1FCB29, [0x5C, 0xAA, 0xC3]),
RoomObject(0x1FCB2C, [0x68, 0xB1, 0xC3]),
RoomObject(0x1FCB2F, [0x75, 0xB0, 0xC3]),
RoomObject(0x1FCB32, [0x8F, 0xB1, 0xC3]),
RoomObject(0x1FCB35, [0x9B, 0xAA, 0xC3]),
RoomObject(0x1FCB38, [0xA6, 0xA0, 0xC3]),
RoomObject(0x1FCB3B, [0xAD, 0x98, 0xC3]),
RoomObject(0x1FCB3E, [0xB4, 0x6A, 0xC2]),
RoomObject(0x1FCB41, [0x51, 0x3D, 0xC3]),
RoomObject(0x1FCB44, [0x45, 0x49, 0xC3]),
RoomObject(0x1FCB47, [0x3D, 0x51, 0xC3]),
RoomObject(0x1FCB4A, [0x9C, 0x39, 0xC2]),
RoomObject(0x1FCB4D, [0xA1, 0x49, 0xC3]),
RoomObject(0x1FCB50, [0xAD, 0x51, 0xC3]),
RoomObject(0x1FCB53, [0x3A, 0x50, 0x8A]),
RoomObject(0x1FCB56, [0x38, 0x50, 0x22]),
RoomObject(0x1FCB59, [0x44, 0x44, 0x69]),
RoomObject(0x1FCB5C, [0x44, 0x44, 0x22]),
RoomObject(0x1FCB5F, [0x58, 0x13, 0x05]),
RoomObject(0x1FCB62, [0x60, 0x15, 0x55]),
RoomObject(0x1FCB65, [0x78, 0x10, 0x3A]),
RoomObject(0x1FCB68, [0x08, 0x5B, 0x65]),
RoomObject(0x1FCB6B, [0x0C, 0x61, 0x7F]),
RoomObject(0x1FCB6E, [0xC8, 0x39, 0x05]),
RoomObject(0x1FCB71, [0xE8, 0x5B, 0x66]),
RoomObject(0x1FCB74, [0xEC, 0x4A, 0x80]),
RoomObject(0x1FCB77, [0x58, 0xEB, 0x06]),
RoomObject(0x1FCB7A, [0x60, 0xED, 0x56]),
RoomObject(0x1FCB7D, [0x78, 0xEC, 0x3B]),
RoomObject(0x1FCB80, [0x50, 0x38, 0x69]),
RoomObject(0x1FCB83, [0x50, 0x38, 0x5F]),
RoomObject(0x1FCB86, [0xA8, 0x38, 0x69]),
RoomObject(0x1FCB89, [0xA8, 0x44, 0x22]),
RoomObject(0x1FCB8C, [0xB4, 0x44, 0x69]),
RoomObject(0x1FCB8F, [0xB4, 0x51, 0x22]),
RoomObject(0x1FCB92, [0xC6, 0x50, 0x8A]),
RoomObject(0x1FCB95, [0x3B, 0xC8, 0x22]),
RoomObject(0x1FCB98, [0x8B, 0xC8, 0x22]),
RoomObject(0x1FCB9B, [0x74, 0xBC, 0x69]),
RoomObject(0x1FCB9E, [0x88, 0xBC, 0x69]),
RoomObject(0x1FCBA1, [0x63, 0x3C, 0xC2]),
RoomObject(0x1FCBA4, [0x66, 0x4F, 0x29]),
RoomObject(0x1FCBA7, [0x64, 0x50, 0x6B]),
RoomObject(0x1FCBAA, [0x5C, 0x54, 0x2B]),
RoomObject(0x1FCBAD, [0x5C, 0x58, 0x6B]),
RoomObject(0x1FCBB0, [0x54, 0x5C, 0x2B]),
RoomObject(0x1FCBB3, [0x54, 0x60, 0x6B]),
RoomObject(0x1FCBB6, [0x4C, 0x64, 0x2B]),
RoomObject(0x1FCBB9, [0x4E, 0x6B, 0x6B]),
RoomObject(0x1FCBBC, [0x4C, 0x98, 0x2D]),
RoomObject(0x1FCBBF, [0x54, 0x9C, 0x6B]),
RoomObject(0x1FCBC2, [0x54, 0xA0, 0x2D]),
RoomObject(0x1FCBC5, [0x5C, 0xA4, 0x6B]),
RoomObject(0x1FCBC8, [0x5C, 0xA8, 0x2D]),
RoomObject(0x1FCBCB, [0x64, 0xAC, 0x6B]),
RoomObject(0x1FCBCE, [0x66, 0xB3, 0x2A]),
RoomObject(0x1FCBD1, [0x98, 0xAC, 0x6A]),
RoomObject(0x1FCBD4, [0x98, 0xA8, 0x2E]),
RoomObject(0x1FCBD7, [0xA0, 0xA4, 0x6A]),
RoomObject(0x1FCBDA, [0xA0, 0xA0, 0x2E]),
RoomObject(0x1FCBDD, [0xA8, 0x9C, 0x6A]),
RoomObject(0x1FCBE0, [0xA8, 0x98, 0x2E]),
RoomObject(0x1FCBE3, [0xB2, 0x6B, 0x6A]),
RoomObject(0x1FCBE6, [0xA8, 0x64, 0x2C]),
RoomObject(0x1FCBE9, [0xA8, 0x60, 0x6A]),
RoomObject(0x1FCBEC, [0xA0, 0x5C, 0x2C]),
RoomObject(0x1FCBEF, [0xA0, 0x58, 0x6A]),
RoomObject(0x1FCBF2, [0x98, 0x54, 0x2C]),
RoomObject(0x1FCBF5, [0x98, 0x50, 0x6A]),
RoomObject(0x1FCBF8, [0x68, 0x74, 0xC2]),
RoomObject(0x1FCBFB, [0x68, 0x71, 0x27]),
RoomObject(0x1FCBFE, [0x68, 0x77, 0x6A]),
RoomObject(0x1FCC01, [0x74, 0x77, 0x6B]),
RoomObject(0x1FCC04, [0x68, 0x85, 0x28]),
RoomObject(0x1FCC07, [0xFC, 0x31, 0x72]),
RoomObject(0x1FCC0A, [0x74, 0xAE, 0x04]),
RoomObject(0x1FCC0D, [0x71, 0xA0, 0xE0]),
RoomObject(0x1FCC10, [0x0A, 0x13, 0xA0]),
RoomObject(0x1FCC13, [0x0A, 0xBF, 0xA1]),
RoomObject(0x1FCC16, [0xBE, 0xF7, 0xA3]),
RoomObject(0x1FCC19, [0xC3, 0x11, 0xC0]),
RoomObject(0x1FCC1C, [0xD1, 0x31, 0x00])],
[], [])
Room001C = Room([0xE1, 0x00],
[RoomObject(0x1FF74B, [0x2D, 0x32, 0xA4]),
RoomObject(0x1FF74E, [0xA9, 0x1E, 0xDC]),
RoomObject(0x1FF751, [0xA8, 0x91, 0x3A]),
RoomObject(0x1FF754, [0x88, 0xAD, 0x76]),
RoomObject(0x1FF757, [0xEC, 0xAD, 0x77]),
RoomObject(0x1FF75A, [0xA8, 0x50, 0x3D]),
RoomObject(0x1FF75D, [0xD0, 0x50, 0x3D]),
RoomObject(0x1FF760, [0x30, 0xA9, 0x3D]),
RoomObject(0x1FF763, [0x30, 0xC1, 0x3D]),
RoomObject(0x1FF766, [0xFC, 0x69, 0x38]),
RoomObject(0x1FF769, [0x97, 0x9F, 0xD1]),
RoomObject(0x1FF76C, [0xCD, 0x9F, 0xD1]),
RoomObject(0x1FF76F, [0x97, 0xDC, 0xD1]),
RoomObject(0x1FF772, [0xCD, 0xDC, 0xD1]),
RoomObject(0x1FF775, [0xBD, 0x32, 0xF9]),
RoomObject(0x1FF778, [0xB1, 0x22, 0xF9]),
RoomObject(0x1FF77B, [0xC9, 0x22, 0xF9]),], [],
[DoorObject(Position.InteriorE, DoorKind.TrapTriggerable),
DoorObject(Position.InteriorS, DoorKind.Trap), DoorObject(Position.InteriorW, DoorKind.Dashable)])
Room0029 = Room([0xE5, 0x00],
[RoomObject(0x1FC188, [0x97, 0x9C, 0xDE]),
RoomObject(0x1FC18B, [0xB7, 0x9C, 0xDE]),
RoomObject(0x1FC18E, [0xD6, 0x9C, 0xDE]),
RoomObject(0x1FC191, [0x97, 0xE4, 0xDE]),
RoomObject(0x1FC194, [0xB7, 0xE4, 0xDE]),
RoomObject(0x1FC197, [0xD6, 0xE4, 0xDE]),
RoomObject(0x1FC19A, [0x94, 0xA7, 0xDE]),
RoomObject(0x1FC19D, [0x94, 0xC7, 0xDE]),
RoomObject(0x1FC1A0, [0xE4, 0xA7, 0xDE]),
RoomObject(0x1FC1A3, [0xE4, 0xC7, 0xDE])],
[RoomObject(0x1FC1A8, [0x03, 0x03, 0xCA]),
RoomObject(0x1FC1AB, [0x43, 0x03, 0xCA]),
RoomObject(0x1FC1AE, [0x83, 0x03, 0xCA]),
RoomObject(0x1FC1B1, [0xC3, 0x03, 0xCA]),
RoomObject(0x1FC1B4, [0x03, 0x43, 0xCA]),
RoomObject(0x1FC1B7, [0x43, 0x43, 0xCA]),
RoomObject(0x1FC1BA, [0x83, 0x43, 0xCA]),
RoomObject(0x1FC1BD, [0xC3, 0x43, 0xCA]),
RoomObject(0x1FC1C0, [0x03, 0x83, 0xCA]),
RoomObject(0x1FC1C3, [0x43, 0x83, 0xCA]),
RoomObject(0x1FC1C6, [0x83, 0x83, 0xCA]),
RoomObject(0x1FC1C9, [0xC3, 0x83, 0xCA]),
RoomObject(0x1FC1CC, [0x03, 0xC3, 0xCA]),
RoomObject(0x1FC1CF, [0x43, 0xC3, 0xCA]),
RoomObject(0x1FC1D2, [0x83, 0xC3, 0xCA]),
RoomObject(0x1FC1D5, [0xC3, 0xC3, 0xCA])],
[], layer3=[RoomObject(0x1FC1DA, [0x9F, 0xA7, 0xC6]),
RoomObject(0x1FC1DD, [0xD4, 0xA7, 0xC6]),
RoomObject(0x1FC1E0, [0xFE, 0xF9, 0xF4]),
RoomObject(0x1FC1E3, [0xFF, 0x1E, 0x74]),
RoomObject(0x1FC1E6, [0xFE, 0x5C, 0x74]),
RoomObject(0x1FC1E9, [0xFF, 0x9C, 0x74])])
Room0033 = Room([0xE9, 0x00], [], [], [DoorObject(Position.SouthW, DoorKind.Trap)])
Room004D = Room([0x82, 0x1C],
[RoomObject(0x1FFD43, [0x09, 0x34, 0x0D]),
RoomObject(0x1FFD46, [0x08, 0x3A, 0x61]),
RoomObject(0x1FFD49, [0x09, 0xC0, 0x0E]),
RoomObject(0x1FFD4C, [0x08, 0xC2, 0x61]),
RoomObject(0x1FFD4F, [0xD1, 0x10, 0x0F]),
RoomObject(0x1FFD52, [0xE8, 0x3A, 0x62]),
RoomObject(0x1FFD55, [0x5E, 0x1C, 0x03]),
RoomObject(0x1FFD58, [0x17, 0x49, 0x63]),
RoomObject(0x1FFD5B, [0xDF, 0x4B, 0x64]),
RoomObject(0x1FFD5E, [0xDC, 0xCA, 0x64]),
RoomObject(0x1FFD61, [0xFF, 0x7D, 0xCB]),
RoomObject(0x1FFD64, [0x9D, 0xDF, 0x04]),
RoomObject(0x1FFD67, [0x3B, 0x5B, 0xE0]),
RoomObject(0x1FFD6A, [0x7B, 0x5B, 0xE0]),
RoomObject(0x1FFD6D, [0xB8, 0x5B, 0xE0]),
RoomObject(0x1FFD70, [0x6A, 0xB1, 0xE0]),
RoomObject(0x1FFD73, [0x78, 0x54, 0xC2]),
RoomObject(0x1FFD76, [0x5B, 0x2A, 0xC2]),
RoomObject(0x1FFD79, [0x98, 0x2A, 0xC2]),
RoomObject(0x1FFD7C, [0x21, 0x4B, 0xC3]),
RoomObject(0x1FFD7F, [0x21, 0x7B, 0xC3]),
RoomObject(0x1FFD82, [0x21, 0xA1, 0xC3]),
RoomObject(0x1FFD85, [0x38, 0x7B, 0xC2]),
RoomObject(0x1FFD88, [0x48, 0x8A, 0xC2]),
RoomObject(0x1FFD8B, [0x3A, 0xAA, 0xC2]),
RoomObject(0x1FFD8E, [0x5B, 0x9C, 0xC2]),
RoomObject(0x1FFD91, [0xC9, 0x4B, 0xC3]),
RoomObject(0x1FFD94, [0xC9, 0x7B, 0xC3]),
RoomObject(0x1FFD97, [0xB8, 0x79, 0xC2]),
RoomObject(0x1FFD9A, [0xA8, 0x88, 0xC2]),
RoomObject(0x1FFD9D, [0x9B, 0x9B, 0xC2]),
RoomObject(0x1FFDA0, [0x9B, 0xD0, 0xC2]),
RoomObject(0x1FFDA3, [0xD0, 0xA3, 0xC2]),
RoomObject(0x1FFDA6, [0x78, 0x8C, 0xC2]),
RoomObject(0x1FFDA9, [0x15, 0x45, 0x22]),
RoomObject(0x1FFDAC, [0x59, 0x1F, 0x69]),
RoomObject(0x1FFDAF, [0xA5, 0x1F, 0x69]),
RoomObject(0x1FFDB2, [0xC9, 0x45, 0x22]),
RoomObject(0x1FFDB5, [0x68, 0xE4, 0x5E]),
RoomObject(0x1FFDB8, [0x15, 0xB9, 0x22]),
RoomObject(0x1FFDBB, [0x35, 0xB9, 0x69]),
RoomObject(0x1FFDBE, [0x37, 0xD9, 0x22]),
RoomObject(0x1FFDC1, [0x88, 0xD9, 0x22]),
RoomObject(0x1FFDC4, [0x98, 0xD9, 0x69]),
RoomObject(0x1FFDC7, [0x66, 0xCB, 0x2A]),
RoomObject(0x1FFDCA, [0x69, 0xC9, 0x04]),
RoomObject(0x1FFDCD, [0x79, 0xCB, 0xF9]),
RoomObject(0x1FFDD0, [0x8D, 0xBA, 0xF9]),
RoomObject(0x1FFDD3, [0x37, 0x57, 0x29]),
RoomObject(0x1FFDD6, [0x87, 0x57, 0x29]),
RoomObject(0x1FFDD9, [0x78, 0x5A, 0x6A]),
RoomObject(0x1FFDDC, [0x84, 0x5A, 0x6B]),
RoomObject(0x1FFDDF, [0x78, 0x65, 0x28]),
RoomObject(0x1FFDE2, [0x35, 0x5B, 0x6B]),
RoomObject(0x1FFDE5, [0x34, 0x7A, 0x2D]),
RoomObject(0x1FFDE8, [0x44, 0x7E, 0x6B]),
RoomObject(0x1FFDEB, [0x44, 0x8A, 0x2D]),
RoomObject(0x1FFDEE, [0x54, 0x8E, 0x6B]),
RoomObject(0x1FFDF1, [0x55, 0x9B, 0x2A]),
RoomObject(0x1FFDF4, [0x78, 0x8E, 0x6A]),
RoomObject(0x1FFDF7, [0x78, 0x89, 0x27]),
RoomObject(0x1FFDFA, [0x84, 0x8E, 0x6B]),
RoomObject(0x1FFDFD, [0x85, 0x9B, 0x2A]),
RoomObject(0x1FFE00, [0xA8, 0x8E, 0x6A]),
RoomObject(0x1FFE03, [0xA8, 0x8A, 0x2E]),
RoomObject(0x1FFE06, [0xB8, 0x7E, 0x6A]),
RoomObject(0x1FFE09, [0xB8, 0x7A, 0x2E]),
RoomObject(0x1FFE0C, [0xC9, 0x5B, 0x6A]),
RoomObject(0x1FFE0F, [0x66, 0xAF, 0x29]),
RoomObject(0x1FFE12, [0x65, 0xB1, 0x6B]),
RoomObject(0x1FFE15, [0x99, 0xB1, 0x6A]),
RoomObject(0x1FFE18, [0x38, 0x4B, 0x03]),
RoomObject(0x1FFE1B, [0xA8, 0x4B, 0x03]),
RoomObject(0x1FFE1E, [0x48, 0x13, 0x3A]),
RoomObject(0x1FFE21, [0x0C, 0x4A, 0x7F]),
RoomObject(0x1FFE24, [0xEC, 0x4A, 0x80]),
RoomObject(0x1FFE27, [0xFE, 0xE1, 0x39]),
RoomObject(0x1FFE2A, [0x09, 0x11, 0xA0]),
RoomObject(0x1FFE2D, [0xD5, 0x11, 0xA2]),
RoomObject(0x1FFE30, [0x09, 0xD5, 0xA1])],
[RoomObject(0x1FFE35, [0x5B, 0x19, 0xDB]),
RoomObject(0x1FFE38, [0x98, 0x19, 0xDB]),
RoomObject(0x1FFE3B, [0x11, 0x4B, 0xDB]),
RoomObject(0x1FFE3E, [0x11, 0x8A, 0xDB]),
RoomObject(0x1FFE41, [0x39, 0x48, 0xDB]),
RoomObject(0x1FFE44, [0xA9, 0x48, 0xDB]),
RoomObject(0x1FFE47, [0xD9, 0x4B, 0xDB]),
RoomObject(0x1FFE4A, [0xD9, 0x8B, 0xDB]),
RoomObject(0x1FFE4D, [0x6A, 0xC8, 0xDB]),
RoomObject(0x1FFE50, [0x9B, 0xCA, 0xDB]),
RoomObject(0x1FFE53, [0xD9, 0xCA, 0xDB])],
[DoorObject(Position.NorthW, DoorKind.SmallKey), DoorObject(Position.WestS, DoorKind.Normal)])
Room005A = Room([0xE9, 0x00],
[RoomObject(0x1FA7CD, [0xA8, 0xA8, 0xDE]),
RoomObject(0x1FA7D0, [0xB0, 0xA0, 0xDE]),
RoomObject(0x1FA7D3, [0xB8, 0xA8, 0xDE]),
RoomObject(0x1FA7D6, [0xC0, 0xA0, 0xDE]),
RoomObject(0x1FA7D9, [0xC8, 0xA8, 0xDE]),
RoomObject(0x1FA7DC, [0xD0, 0xA0, 0xDE])],
[], [DoorObject(Position.SouthE, DoorKind.Trap)])
Room006C = Room([0xE2, 0x00],
[RoomObject(0x1FFA58, [0x17, 0x9F, 0xE8]),
RoomObject(0x1FFA5B, [0x4D, 0x9F, 0xE8]),
RoomObject(0x1FFA5E, [0x17, 0xDC, 0xE8]),
RoomObject(0x1FFA61, [0x4D, 0xDC, 0xE8]),
RoomObject(0x1FFA64, [0x18, 0xE1, 0xFE]),
RoomObject(0x1FFA67, [0x88, 0xAD, 0x76]),
RoomObject(0x1FFA6A, [0x99, 0xBC, 0x33]),
RoomObject(0x1FFA6D, [0x9B, 0xBB, 0x34]),
RoomObject(0x1FFA70, [0x9B, 0xCF, 0x34]),
RoomObject(0x1FFA73, [0xD8, 0xB8, 0x34]),
RoomObject(0x1FFA76, [0xD8, 0xCC, 0x34]),
RoomObject(0x1FFA79, [0xAF, 0xAA, 0xFE]),
RoomObject(0x1FFA7C, [0xC7, 0xAA, 0xFE]),
RoomObject(0x1FFA7F, [0xAF, 0xD2, 0xFE]),
RoomObject(0x1FFA82, [0xC7, 0xD2, 0xFE]),
RoomObject(0x1FFA85, [0x28, 0x11, 0x3A]),
RoomObject(0x1FFA88, [0x28, 0x91, 0x3A]),
RoomObject(0x1FFA8B, [0xFC, 0xE1, 0x38]),
RoomObject(0x1FFA8E, [0x2B, 0x33, 0xFA]),
RoomObject(0x1FFA91, [0x53, 0x33, 0xFA]),
RoomObject(0x1FFA94, [0x2B, 0x53, 0xFA]),
RoomObject(0x1FFA97, [0x53, 0x53, 0xFA])], [],
[DoorObject(Position.InteriorS, DoorKind.Trap2), DoorObject(Position.InteriorW, DoorKind.Trap),
DoorObject(Position.EastS, DoorKind.Normal)])
Room0090 = Room([0xE1, 0x00],
[RoomObject(0x1FBAA0, [0x28, 0xEC, 0x56]),
RoomObject(0x1FBAA3, [0x48, 0xEC, 0x56]),
RoomObject(0x1FBAA6, [0x1B, 0xA2, 0xFF])],
[RoomObject(0x1FBAAB, [0x16, 0x9C, 0xFE])], [DoorObject(Position.SouthW, DoorKind.Trap)])
Room00A4 = Room([0xE1, 0x00],
[RoomObject(0x1FE702, [0xFC, 0x08, 0x00]),
RoomObject(0x1FE705, [0x13, 0x80, 0x01]),
RoomObject(0x1FE708, [0xFD, 0xC8, 0x02]),
RoomObject(0x1FE70B, [0x02, 0x93, 0x61]),
RoomObject(0x1FE70E, [0xFC, 0x0E, 0x81]),
RoomObject(0x1FE711, [0x13, 0xE8, 0x02]),
RoomObject(0x1FE714, [0xFD, 0xCE, 0x83]),
RoomObject(0x1FE717, [0x72, 0x93, 0x62]),
RoomObject(0x1FE71A, [0x13, 0x93, 0xC4]),
RoomObject(0x1FE71D, [0x51, 0x93, 0xC4]),
RoomObject(0x1FE720, [0x51, 0xC9, 0xC4]),
RoomObject(0x1FE723, [0x10, 0xC9, 0xC4]),
RoomObject(0x1FE726, [0x0E, 0x8D, 0xDE]),
RoomObject(0x1FE729, [0x0D, 0x9C, 0xDE]),
RoomObject(0x1FE72C, [0x0C, 0xA5, 0xDE]),
RoomObject(0x1FE72F, [0x5E, 0x8C, 0xDE]),
RoomObject(0x1FE732, [0x65, 0x94, 0xDE]),
RoomObject(0x1FE735, [0x6C, 0x9C, 0xDE])],
[RoomObject(0x1FE73A, [0x2E, 0x98, 0xFF])], [DoorObject(Position.SouthW, DoorKind.Trap)])
Room00AC = Room([0xE9, 0x00],
[RoomObject(0x1FD9B1, [0x88, 0xA4, 0x0D]),
RoomObject(0x1FD9B4, [0x88, 0xD0, 0x0E]),
RoomObject(0x1FD9B7, [0xE0, 0x90, 0x0F]),
RoomObject(0x1FD9BA, [0xE0, 0xE4, 0x10]),
RoomObject(0x1FD9BD, [0x89, 0xAB, 0x61]),
RoomObject(0x1FD9C0, [0xE9, 0xAB, 0x62]),
RoomObject(0x1FD9C3, [0x88, 0x91, 0xA0]),
RoomObject(0x1FD9C6, [0x88, 0xE5, 0xA1]),
RoomObject(0x1FD9C9, [0xE4, 0x91, 0xA2]),
RoomObject(0x1FD9CC, [0xE4, 0xF5, 0xA3])],
[RoomObject(0x1FD9D1, [0xB1, 0xA8, 0xFF])], [DoorObject(Position.SouthE, DoorKind.Trap)])
Room00C8 = Room([0xE1, 0x00],
[RoomObject(0x0A9587, [0x98, 0x92, 0x3A]),
RoomObject(0x0A958A, [0x88, 0xAA, 0x65]),
RoomObject(0x0A958D, [0xE8, 0xAA, 0x66])], [], [DoorObject(Position.SouthE, DoorKind.Trap)])
Room00DE = Room([0xE4, 0x00], [], [RoomObject(0x1FCAE5, [0xAD, 0x21, 0xF9])], [])
Room010C = Room([0xE0, 0x08],
[RoomObject(0x03F25D, [0xFC, 0x62, 0x00]),
RoomObject(0x03F260, [0x19, 0x33, 0x61]),
RoomObject(0x03F263, [0xFC, 0x66, 0x81]),
RoomObject(0x03F266, [0x29, 0x22, 0x01]),
RoomObject(0x03F269, [0xFD, 0x62, 0x02]),
RoomObject(0x03F26C, [0x59, 0x33, 0x62]),
RoomObject(0x03F26F, [0xFD, 0x66, 0x83]),
RoomObject(0x03F272, [0xFC, 0x89, 0x00]),
RoomObject(0x03F275, [0x22, 0xA1, 0x61]),
RoomObject(0x03F278, [0xFC, 0x8E, 0x81]),
RoomObject(0x03F27B, [0x90, 0xCA, 0x0E]),
RoomObject(0x03F27E, [0xD0, 0xE6, 0x10]),
RoomObject(0x03F281, [0xFE, 0x41, 0x00]),
RoomObject(0x03F284, [0x93, 0x23, 0x61]),
RoomObject(0x03F287, [0x92, 0x98, 0x61]),
RoomObject(0x03F28A, [0xFF, 0x81, 0x02]),
RoomObject(0x03F28D, [0xE3, 0x23, 0x62]),
RoomObject(0x03F290, [0xE2, 0x98, 0x62]),
RoomObject(0x03F293, [0xFE, 0x71, 0xC8]),
RoomObject(0x03F296, [0xAD, 0x1C, 0x03]),
RoomObject(0x03F299, [0xFF, 0x51, 0xCA]),
RoomObject(0x03F29C, [0x9D, 0x2D, 0x63]),
RoomObject(0x03F29F, [0xFE, 0x75, 0x89]),
RoomObject(0x03F2A2, [0xAD, 0x58, 0x04]),
RoomObject(0x03F2A5, [0xD5, 0x2D, 0x64]),
RoomObject(0x03F2A8, [0xFF, 0x55, 0x8B]),
RoomObject(0x03F2AB, [0xFE, 0x78, 0x08]),
RoomObject(0x03F2AE, [0xAD, 0x80, 0x03]),
RoomObject(0x03F2B1, [0xFF, 0x58, 0x0A]),
RoomObject(0x03F2B4, [0x9C, 0x91, 0x63]),
RoomObject(0x03F2B7, [0xFE, 0x7A, 0x09]),
RoomObject(0x03F2BA, [0xAD, 0xA0, 0x04]),
RoomObject(0x03F2BD, [0xFF, 0x5A, 0x0B]),
RoomObject(0x03F2C0, [0xD4, 0x91, 0x64]),
RoomObject(0x03F2C3, [0x2C, 0x2E, 0xDC]),
RoomObject(0x03F2C6, [0x3D, 0x3E, 0xF9]),
RoomObject(0x03F2C9, [0x32, 0xA9, 0xF9]),
RoomObject(0x03F2CC, [0x3A, 0xA9, 0xF9]),
RoomObject(0x03F2CF, [0x42, 0xA9, 0xF9]),
RoomObject(0x03F2D2, [0x4A, 0xA9, 0xF9]),
RoomObject(0x03F2D5, [0x57, 0x9E, 0x69]),
RoomObject(0x03F2D8, [0x54, 0xDC, 0x69]),
RoomObject(0x03F2DB, [0x38, 0xC8, 0x89]),
RoomObject(0x03F2DE, [0x58, 0x9C, 0x89]),
RoomObject(0x03F2E1, [0x58, 0xA8, 0x89]),
RoomObject(0x03F2E4, [0x58, 0xB4, 0x89]),
RoomObject(0x03F2E7, [0x58, 0xC0, 0x89]),
RoomObject(0x03F2EA, [0x58, 0xCC, 0x89]),
RoomObject(0x03F2ED, [0x58, 0xD8, 0x89]),
RoomObject(0x03F2F0, [0x58, 0xE4, 0x89]),
RoomObject(0x03F2F3, [0xAA, 0x2E, 0xC8]),
RoomObject(0x03F2F6, [0xAA, 0x29, 0x3F]),
RoomObject(0x03F2F9, [0xAA, 0x2E, 0x79]),
RoomObject(0x03F2FC, [0xAA, 0x59, 0x40]),
RoomObject(0x03F2FF, [0xD6, 0x2E, 0x7A]),
RoomObject(0x03F302, [0xAA, 0x90, 0xC8]),
RoomObject(0x03F305, [0xAA, 0x8D, 0x3F]),
RoomObject(0x03F308, [0xA8, 0x93, 0x79]),
RoomObject(0x03F30B, [0xAA, 0xA1, 0x40]),
RoomObject(0x03F30E, [0xD4, 0x93, 0x7A]),
RoomObject(0x03F311, [0xB9, 0x5B, 0xF9]),
RoomObject(0x03F314, [0xB9, 0xA3, 0xF9]),
RoomObject(0x03F317, [0x9C, 0x68, 0x22]),
RoomObject(0x03F31A, [0x9C, 0x6A, 0x69]),
RoomObject(0x03F31D, [0x9C, 0x7C, 0x22]),
RoomObject(0x03F320, [0xD4, 0x7C, 0x22]),
RoomObject(0x03F323, [0x9C, 0xB0, 0x22]),
RoomObject(0x03F326, [0xD4, 0xB0, 0x22]),
RoomObject(0x03F329, [0xB3, 0x73, 0xFA]),
RoomObject(0x03F32C, [0xCD, 0x68, 0xDD]),
RoomObject(0x03F32F, [0xB8, 0xC0, 0xDD]),
RoomObject(0x03F332, [0x08, 0x00, 0x60]),
RoomObject(0x03F335, [0x10, 0x00, 0x60]),
RoomObject(0x03F338, [0x1B, 0x10, 0xC0]),
RoomObject(0x03F33B, [0x59, 0x10, 0xC0]),
RoomObject(0x03F33E, [0x68, 0x23, 0xC0]),
RoomObject(0x03F341, [0x68, 0x61, 0xC0]),
RoomObject(0x03F344, [0x1B, 0x91, 0x60]),
RoomObject(0x03F347, [0x88, 0x10, 0x60]),
RoomObject(0x03F34A, [0xF0, 0x10, 0x60]),
RoomObject(0x03F34D, [0x90, 0xDF, 0xA1]),
RoomObject(0x03F350, [0xD4, 0xF7, 0xA3])], [],
[DoorObject(Position.InteriorW, DoorKind.TrapTriggerable),
DoorObject(Position.SouthW, DoorKind.CaveEntrance), DoorObject(Position.SouthE, DoorKind.CaveEntrance)]
)
Room0127 = Room([0xE1, 0x00],
[RoomObject(0x0AB600, [0xFE, 0x89, 0x00]),
@@ -44,15 +515,35 @@ Room0127 = Room([0xE1, 0x00],
RoomObject(0x0AB61E, [0x43, 0xCB, 0xFA]),
RoomObject(0x0AB621, [0x4B, 0xCB, 0xFA]),
RoomObject(0x0AB624, [0xBF, 0x94, 0xF9]),
RoomObject(0x0AB627, [0xB3, 0xB3, 0xFA]),
RoomObject(0x0AB62A, [0xCB, 0xB3, 0xFA]),
RoomObject(0x0AB627, [0xB3, 0xB3, 0xFA], True),
RoomObject(0x0AB62A, [0xCB, 0xB3, 0xFA], True),
RoomObject(0x0AB62D, [0xAD, 0xC8, 0xDF]),
RoomObject(0x0AB630, [0xC4, 0xC8, 0xDF]),
RoomObject(0x0AB633, [0xB3, 0xE3, 0xFA]),
RoomObject(0x0AB636, [0xCB, 0xE3, 0xFA]),
RoomObject(0x0AB633, [0xB3, 0xE3, 0xFA], True),
RoomObject(0x0AB636, [0xCB, 0xE3, 0xFA], True),
RoomObject(0x0AB639, [0x81, 0x93, 0xC0]),
RoomObject(0x0AB63C, [0x81, 0xD2, 0xC0]),
RoomObject(0x0AB63F, [0xE1, 0x93, 0xC0]),
RoomObject(0x0AB642, [0xE1, 0xD2, 0xC0])],
[], [DoorObject(Position.SouthW, DoorKind.CaveEntrance),
DoorObject(Position.SouthE, DoorKind.CaveEntrance)])
# boss room id, room data, shell x, shell y, clear layer 2
boss_rooms = {
'Eastern Palace': (0xc8, Room00C8, 0x2B, 0x28, False),
'Desert Palace': (0x33, Room0033, 0x0B, 0x28, False),
'Tower of Hera': (7, Room0007, 0x18, 0x16, False),
'Palace of Darkness': (0x5A, Room005A, 0x2B, 0x28, False),
'Swamp Palace': (6, Room0006, 0x0B, 0x28, False),
'Skull Woods': (0x29, Room0029, 0x2B, 0x28, False),
'Thieves Town': (0xac, Room00AC, 0x2B, 0x28, True),
'Ice Palace': (0xde, Room00DE, 0x2B, 0x08, True),
'Misery Mire': (0x90, Room0090, 0x0B, 0x28, True),
'Turtle Rock': (0xa4, Room00A4, 0x0B, 0x28, True),
}
gt_boss_room = {
'bottom': (0x1C, Room001C, 0x2B, 0x28, False),
'middle': (0x6C, Room006C, 0x0B, 0x28, False),
'top': (0x4D, Room004D, 0x18, 0x16, False),
}
+13 -1
View File
@@ -8,9 +8,10 @@ Shuffled_Pot = (0xFB, 0, 0) # formerly weird pot, or black diagonal thing
class RoomObject:
def __init__(self, address, data):
def __init__(self, address, data, dummy=False):
self.address = address
self.data = data
self.dummy = dummy # some room objects are dummies, unreachable
def change_type(self, new_type):
type_id, datum_a, datum_b = new_type
@@ -22,6 +23,17 @@ class RoomObject:
def write_to_rom(self, rom):
rom.write_bytes(snes_to_pc(self.address), self.data)
# subtype 3 only?
def matches_oid(self, oid):
my_oid = (self.data[2] << 4) | ((self.data[1] & 3) << 2) | (self.data[0] & 3)
return my_oid == oid
@staticmethod
def subtype3_factory(x, y, type_id):
return RoomObject(None, [((x << 2) & 0xFC) | (type_id & 0x3),
((y << 2) & 0xFC) | ((type_id >> 2) & 0x3),
0xF0 | ((type_id >> 4) & 0xF)])
class DoorObject:
+1
View File
@@ -0,0 +1 @@
# do nothing, just exist to make "source" package
+222
View File
@@ -0,0 +1,222 @@
import RaceRandom as random
from Utils import snes_to_pc
from source.dungeon.EnemyList import EnemySprite, SpriteType, Sprite
from source.dungeon.RoomList import boss_rooms, gt_boss_room, Room0006
from source.dungeon.RoomObject import RoomObject
from source.enemizer.SpriteSheets import required_boss_sheets
def get_dungeon_boss_room(dungeon_name, level):
if level is None:
return boss_rooms[dungeon_name]
return gt_boss_room[level]
def get_dungeon_boss_default(dungeon_name, level):
if level is None:
return boss_defaults[dungeon_name]
return gt_boss_defaults[level]
def add_shell_to_boss_room(data_tables, dungeon_name, level, shell_id):
room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level)
if room_id in data_tables.room_list:
room = data_tables.room_list[room_id]
else:
data_tables.room_list[room_id] = room
room.layout[0] = 0xF0
if clear_layer_2:
room.layer2.clear()
y_offset = 0 if shell_id == 0xF95 else -2
room.layer2.append(RoomObject.subtype3_factory(shell_x, shell_y + y_offset, shell_id))
def remove_shell_from_boss_room(data_tables, dungeon_name, level, shell_id):
room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level)
if room_id in data_tables.room_list:
room = data_tables.room_list[room_id]
else:
data_tables.room_list[room_id] = room
room.layer2[:] = [obj for obj in room.layer2 if not obj.matches_oid(shell_id)]
def remove_water_tiles(data_tables):
room = Room0006
if 0x6 in data_tables.room_list:
room = data_tables.room_list[0x6]
else:
data_tables.room_list[0x6] = room
room.layer1.clear()
def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y):
return Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, None, False, None)
def add_armos_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05))
sprite_list.insert(1, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05))
sprite_list.insert(2, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05))
sprite_list.insert(3, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08))
sprite_list.insert(4, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08))
sprite_list.insert(5, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08))
sprite_list.insert(6, create_sprite(room_id, 0x19, SpriteType.Overlord, 0, 0x07, 0x08))
def add_lanmolas_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07))
sprite_list.insert(1, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07))
sprite_list.insert(2, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09))
def add_moldorm_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.Moldorm, 0x00, 0, 0x09, 0x09))
def add_helmasaur_king_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.HelmasaurKing, 0x00, 0, 0x07, 0x06))
def add_arrghus_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x07))
sprite_list.insert(1, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(2, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(3, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(4, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(5, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(6, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(7, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(8, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(9, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(10, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(11, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(12, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
sprite_list.insert(13, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07))
def add_mothula_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06))
def add_blind_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.Blind, 0x00, 0, 0x09, 0x05))
def add_kholdstare_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.KholdstareShell, 0x00, 0, 0x07, 0x05))
sprite_list.insert(1, create_sprite(room_id, EnemySprite.FallingIce, 0x00, 0, 0x07, 0x05))
sprite_list.insert(2, create_sprite(room_id, EnemySprite.Kholdstare, 0x00, 0, 0x07, 0x05))
def add_vitreous_to_list(sprite_list, room_id):
sprite_list.clear() # vitreous does not play nice which other sprites on the tile, just kill them
sprite_list.append(create_sprite(room_id, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05))
def add_trinexx_to_list(sprite_list, room_id):
sprite_list.insert(0, create_sprite(room_id, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05))
sprite_list.insert(1, create_sprite(room_id, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05))
sprite_list.insert(2, create_sprite(room_id, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05))
def boss_adjust(world, player):
data_tables = world.data_tables[player]
for dungeon in world.get_dungeons(player):
for level, boss in dungeon.bosses.items():
if not boss or boss.name in ['Agahnim', 'Agahnim2']:
continue
default_boss = get_dungeon_boss_default(dungeon.name, level)
room_data = get_dungeon_boss_room(dungeon.name, level)
room_id = room_data[0]
if default_boss != boss.name:
sprite_list = data_tables.uw_enemy_table.room_map[room_id]
data = boss_room_remove_data[room_id]
del sprite_list[:data]
add_func, sprite_type = boss_addition_table[boss.name]
add_func(sprite_list, room_id)
if len(sprite_list) > 15:
del sprite_list[15:]
data_tables.room_headers[room_id].sprite_sheet = required_boss_sheets[sprite_type]
def boss_writes(world, player, rom):
rom.write_byte(snes_to_pc(0x368107), 1) # centralize drops
eye_number = random.randint(0, 8) # randomize moldorm eyes (var + 1)
rom.write_byte(snes_to_pc(0x368102), eye_number) # enemizer flag
rom.write_byte(snes_to_pc(0x1DDBB3), eye_number) # loop variable
data_tables = world.data_tables[player]
arrghus_can_swim = True
water_tiles_on = True
for dungeon in world.get_dungeons(player):
for level, boss in dungeon.bosses.items():
if not boss or boss.name in ['Agahnim', 'Agahnim2']:
continue
room_data = get_dungeon_boss_room(dungeon.name, level)
room_id = room_data[0]
# room changes
if boss.name == 'Arrghus' and (dungeon.name != 'Swamp Palace' or level is not None):
rom.write_byte(snes_to_pc(0x0DB6BE), 0) # arrghus can stand on ground
arrghus_can_swim = False
if boss.name != 'Arrghus' and dungeon.name == 'Swamp Palace' and level is None:
remove_water_tiles(data_tables)
water_tiles_on = False
if boss.name == 'Trinexx' and (dungeon.name != 'Turtle Rock' or level is not None):
add_shell_to_boss_room(data_tables, dungeon.name, level, 0xFF2)
data_tables.room_headers[room_id].byte_0 = 0x60
data_tables.room_headers[room_id].effect = 4
# $2E, $98, $FF (original shell)
# disable trinexx ice breath with No-ops if there's a trinexx anywhere outside TR
rom.write_bytes(snes_to_pc(0x09B37E), [0xEA, 0xEA, 0xEA, 0xEA])
if boss.name == 'Kholdstare' and (dungeon.name != 'Ice Palace' or level is not None):
add_shell_to_boss_room(data_tables, dungeon.name, level, 0xF95)
data_tables.room_headers[room_id].byte_0 = 0xE0
data_tables.room_headers[room_id].effect = 1
if boss.name != 'Trinexx' and dungeon.name == 'Turtle Rock' and level is None:
remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xFF2)
if boss.name != 'Kholdstare' and dungeon.name == 'Ice Palace' and level is None:
remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xF95)
if boss.name != 'Blind' and dungeon.name == 'Thieves Town' and level is None:
rom.write_byte(snes_to_pc(0x368101), 1) # set blind boss door flag
# maiden is deleted
del data_tables.uw_enemy_table.room_map[0x45][0]
if not arrghus_can_swim and water_tiles_on:
remove_water_tiles(data_tables)
boss_defaults = {
'Eastern Palace': 'Armos Knights',
'Desert Palace': 'Lanmolas',
'Tower of Hera': 'Moldorm',
'Palace of Darkness': 'Helmasaur King',
'Swamp Palace': 'Arrghus',
'Skull Woods': 'Mothula',
'Thieves Town': 'Blind',
'Ice Palace': 'Kholdstare',
'Misery Mire': 'Vitreous',
'Turtle Rock': 'Trinexx',
}
gt_boss_defaults = {
'bottom': 'Armos Knights',
'middle': 'Lanmolas',
'top': 'Moldorm',
}
boss_room_remove_data = {
6: 14, 7: 1, 0x1c: 7, 0x29: 1, 0x33: 3, 0x4d: 1, 0x5a: 1,
0x6c: 3, 0x90: 1, 0xa4: 2, 0xac: 1, 0xc8: 7, 0xde: 3
}
boss_addition_table = {
'Armos Knights': (add_armos_to_list, EnemySprite.ArmosKnight),
'Lanmolas': (add_lanmolas_to_list, EnemySprite.Lanmolas),
'Moldorm': (add_moldorm_to_list, EnemySprite.Moldorm),
'Helmasaur King': (add_helmasaur_king_to_list, EnemySprite.HelmasaurKing),
'Arrghus': (add_arrghus_to_list, EnemySprite.Arrghus),
'Mothula': (add_mothula_to_list, EnemySprite.Mothula),
'Blind': (add_blind_to_list, EnemySprite.Blind),
'Kholdstare': (add_kholdstare_to_list, EnemySprite.Kholdstare),
'Vitreous': (add_vitreous_to_list, EnemySprite.Vitreous),
'Trinexx': (add_trinexx_to_list, EnemySprite.TrinexxRockHead)
}
+8
View File
@@ -0,0 +1,8 @@
from Utils import load_cached_yaml
class DamageTable:
def __init__(self):
self.damage_table = load_cached_yaml(['source', 'enemizer', 'damage_table.yaml'])
self.enemy_damage = load_cached_yaml(['source', 'enemizer', 'enemy_damage_table.yaml'])
+536
View File
@@ -0,0 +1,536 @@
import RaceRandom as random
from Utils import snes_to_pc
from source.dungeon.EnemyList import SpriteType, EnemySprite, sprite_translation
from source.dungeon.RoomList import Room010C
from source.enemizer.SpriteSheets import sub_group_choices
from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets
from source.enemizer.TilePattern import tile_patterns
shutter_sprites = {
0xb8: {0, 1, 2, 3, 4, 5}, 0xb: {4, 5, 6, 7, 8, 9}, 0x1b: {3, 4, 5}, 0x4b: {0, 3, 4}, 0x4: {9, 13, 14},
0x24: {3, 4, 5, 6}, # not sure about 6 - bunny beam under pot
0x28: {0, 1, 2, 3, 4}, 0xe: {0, 1, 2, 3}, 0x2e: {0, 1, 2, 3, 4, 5}, 0x3e: {1, 2}, 0x6e: {0, 1, 2, 3, 4},
0x31: {7, 8, 10}, 0x44: {2, 3, 5}, 0x45: {1, 2, 3}, 0x53: {5, 6, 8, 9, 10}, 0x75: {0, 2, 3, 4, 5},
0x85: {2, 3, 4, 5}, 0x5d: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 0x6b: {5, 6, 7, 8, 9, 10, 11, 12, 13},
0x6d: {0, 1, 2, 3, 4, 5, 6, 7, 8}, 0x7b: {2, 3, 4, 5, 8, 9, 10}, 0x7d: {4, 5, 6, 7, 8, 10}, 0x8d: {0, 1, 2, 3, 4},
0xa5: {0, 1, 2, 3, 4, 5, 6, 7}, 0x71: {0, 1}, 0xd8: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
0xb0: {0, 1, 2, 3, 4, 5, 7, 8, 9, 10}, 0xc0: {0, 1, 2}, 0xe0: {0, 1, 2, 3}, 0xb2: {5, 6, 7, 10, 11},
0xd2: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 0xef: {0, 1, 2}, 0x10c: {4, 5, 6, 7}, 0x123: {0, 1, 2, 3},
0xee: {0, 1, 2, 3, 4} # low health traversal
}
def setup_specific_requirements(data_tables):
requirements = data_tables.sprite_requirements
water_groups = set()
water_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
killable_groups = set()
killable_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
key_groups = set()
key_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
for sid, requirement in requirements.items():
if isinstance(requirement, dict):
continue
if requirement.good_for_uw_water():
water_groups.update(requirement.groups)
for i in range(0, 4):
limited = [x for x in requirement.sub_groups[i] if x in sub_group_choices[i]]
water_sub_groups[i].update(limited)
if requirement.good_for_shutter([]):
killable_groups.update(requirement.groups)
for i in range(0, 4):
killable_sub_groups[i].update(requirement.sub_groups[i])
if requirement.can_drop:
key_groups.update(requirement.groups)
for i in range(0, 4):
key_sub_groups[i].update(requirement.sub_groups[i])
return water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups
def get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets):
# forced sprites for room
requirements = data_tables.sprite_requirements
water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups = specific
# forced_req = set()
key_needed = False
killable_needed = room_id in shutter_sprites
for sheet in all_sheets:
if room_id in sheet.room_set:
return [sheet]
match_all_room_groups = set()
match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
# match_all_sub_groups = {0: set(uw_sub_group_choices[0] + [70, 72]), 1: set(uw_sub_group_choices[1] + [13, 73]),
# 2: set(uw_sub_group_choices[2] + [19]), 3: set(uw_sub_group_choices[3] + [25, 68])}
for sprite in data_tables.uw_enemy_table.room_map[room_id]:
sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type
key = (sprite.kind, sprite_secondary)
if key not in requirements:
continue
req = requirements[key]
if isinstance(req, dict):
req = req[room_id]
if req.static or not req.can_randomize or sprite.static:
if req.groups:
match_all_room_groups.intersection_update(req.groups)
if not match_all_room_groups:
match_all_room_groups = set(req.groups)
for i in range(0, 4):
if req.sub_groups[i]:
match_all_sub_groups[i].intersection_update(req.sub_groups[i])
if not match_all_sub_groups[i]:
match_all_sub_groups[i] = set(req.sub_groups[i])
# forced_req.add(req)
if sprite.drops_item:
key_needed = True
match_any_room_groups = set()
match_any_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
exclude_all_groups = set()
exclude_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
if room_id in data_tables.room_requirements:
required_groups = data_tables.room_requirements[room_id]
for idx, grp in enumerate(required_groups):
if grp is not None:
if isinstance(grp, tuple):
match_any_sub_groups[idx].update(grp)
else:
match_all_sub_groups[idx] = {grp}
if key_needed:
if key_groups:
match_any_room_groups.update(key_groups)
for i in range(0, 4):
if key_sub_groups[i]:
match_any_sub_groups[i].update(key_sub_groups[i])
elif killable_needed:
if killable_groups:
match_any_room_groups.update(killable_groups)
for i in range(0, 4):
if killable_sub_groups[i]:
match_any_sub_groups[i].update(killable_sub_groups[i])
possible_sheets = []
for sheet in uw_sheets:
if match_all_room_groups and sheet.id not in match_all_room_groups:
continue
if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)):
continue
if exclude_all_groups and sheet.id in exclude_all_groups:
continue
if any(exclude_all_sub_groups[i] and sheet.sub_groups[i] in exclude_all_sub_groups[i] for i in range(0, 4)):
continue
if match_any_room_groups and sheet.id not in match_any_sub_groups:
continue
test_subs = [i for i in range(0, 4) if match_any_sub_groups[i]]
if test_subs and all(sheet.sub_groups[i] not in match_any_sub_groups[i] for i in test_subs):
continue
possible_sheets.append(sheet)
return possible_sheets
def get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables):
requirements = data_tables.sprite_requirements
for sheet in all_sheets:
if area_id in sheet.room_set:
return [sheet]
match_all_room_groups = set()
match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
for sprite in data_tables.ow_enemy_table[area_id]:
sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type
key = (sprite.kind, sprite_secondary)
if key not in requirements:
continue
req = requirements[key]
if isinstance(req, dict):
req = req[area_id]
if req.static or not req.can_randomize:
if req.groups:
match_all_room_groups.intersection_update(req.groups)
if not match_all_room_groups:
match_all_room_groups = set(req.groups)
for i in range(0, 4):
if req.sub_groups[i]:
match_all_sub_groups[i].intersection_update(req.sub_groups[i])
if not match_all_sub_groups[i]:
match_all_sub_groups[i] = set(req.sub_groups[i])
possible_sheets = []
for sheet in ow_sheets:
if match_all_room_groups and sheet.id not in match_all_room_groups:
continue
if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)):
continue
possible_sheets.append(sheet)
return possible_sheets
ignore_sheets_uw = {65, 69, 71, 78, 79, 82, 88, 98}
ignore_sheets_ow = {6}
def find_candidate_sprites(data_tables, sheet_range, uw=True):
requirements = data_tables.sprite_requirements
sprite_candidates = []
sheet_candidates = []
all_sheets = []
candidate_groups = set()
candidate_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()}
for k, r in requirements.items():
if isinstance(r, dict):
continue
valid_flag = (uw and r.uw_valid) or (not uw and r.ow_valid)
if not r.static and valid_flag and not r.dont_use:
candidate_groups.update(r.groups)
for i in range(0, 4):
candidate_sub_groups[i].update(r.sub_groups[i])
sprite_candidates.append(k)
for num in sheet_range:
sheet = data_tables.sprite_sheets[num]
all_sheets.append(sheet)
if (uw and num in ignore_sheets_uw) or (not uw and num in ignore_sheets_ow):
continue
if candidate_groups and sheet not in candidate_groups:
continue
test_subs = [i for i in range(0, 4) if candidate_sub_groups[i]]
if test_subs and all(sheet.sub_groups[i] not in candidate_sub_groups[i] for i in test_subs):
continue
sheet_candidates.append(sheet)
return sprite_candidates, sheet_candidates, all_sheets
def get_possible_enemy_sprites(room_id, sheet, uw_sprites, data_tables):
ret = []
for sprite in uw_sprites:
requirement = data_tables.sprite_requirements[sprite]
if isinstance(requirement, dict):
requirement = requirement[room_id]
if sheet.valid_sprite(requirement) and requirement.can_spawn_in_room(room_id):
ret.append(requirement)
return ret
def get_possible_enemy_sprites_ow(sheet, sprites, data_tables):
ret = []
for sprite in sprites:
requirement = data_tables.sprite_requirements[sprite]
if isinstance(requirement, dict):
continue
if sheet.valid_sprite(requirement) and requirement.ow_valid:
ret.append(requirement)
return ret
def get_randomize_able_sprites(room_id, data_tables):
sprite_table = {}
for idx, sprite in enumerate(data_tables.uw_enemy_table.room_map[room_id]):
sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type
key = (sprite.kind, sprite_secondary)
if key not in data_tables.sprite_requirements:
continue
req = data_tables.sprite_requirements[key]
if isinstance(req, dict):
continue
if not req.static and req.can_randomize and not sprite.static:
sprite_table[idx] = sprite
return sprite_table
def get_randomize_able_sprites_ow(area_id, data_tables):
sprite_table = {}
for idx, sprite in enumerate(data_tables.ow_enemy_table[area_id]):
sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type
key = (sprite.kind, sprite_secondary)
if key not in data_tables.sprite_requirements:
continue
req = data_tables.sprite_requirements[key]
if isinstance(req, dict):
continue
if not req.static and req.can_randomize:
sprite_table[idx] = sprite
return sprite_table
sprite_limiter = {
EnemySprite.Debirando: 2,
EnemySprite.DebirandoPit: 2,
EnemySprite.Hinox: 2,
EnemySprite.Sluggula: 2,
EnemySprite.BombGuard: 2,
EnemySprite.Beamos: 2,
EnemySprite.Gibo: 2,
# EnemySprite.CannonTrooper: 2, ??
EnemySprite.WallCannonHorzTop: 2,
EnemySprite.WallCannonHorzBottom: 2,
EnemySprite.WallCannonVertLeft: 2,
EnemySprite.WallCannonVertRight: 2,
EnemySprite.BlueArcher: 2,
EnemySprite.BlueGuard: 2,
EnemySprite.GreenGuard: 2,
EnemySprite.RedSpearGuard: 2,
EnemySprite.RedJavelinGuard: 2,
EnemySprite.AntiFairyCircle: 4
}
def exceeds_sprite_limit(limit, sprite):
return sprite_limiter[sprite.sprite]-1+limit > 15 if sprite.sprite in sprite_limiter else False
def randomize_underworld_rooms(data_tables, world, player, custom_uw):
any_enemy_logic = world.any_enemy_logic[player]
enemy_drops_active = world.dropshuffle[player] in ['underworld']
specific = setup_specific_requirements(data_tables)
uw_candidates, uw_sheets, all_sheets = find_candidate_sprites(data_tables, range(65, 124))
for room_id in range(0, 0x128):
if room_id in {0, 1, 3, 6, 7, 0xd, 0x14, 0x20, 0x29, 0x30, 0x33,
0x4d, 0x5a, 0x90, 0xa4, 0xac, 0xc8, 0xde}:
continue
current_sprites = data_tables.uw_enemy_table.room_map[room_id]
sprite_limit = sum(sprite_limiter[x.kind] if x.kind in sprite_limiter else 1 for x in current_sprites)
if room_id in {0x3f, 0x44, 0x45, 0x93, 0xce, 0x117}:
sprite_limit += 1 # for liftable blocks see PotFlags.Block in PotShuffle
randomizeable_sprites = get_randomize_able_sprites(room_id, data_tables)
if not randomizeable_sprites:
candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets)
chosen_sheet = random.choice(candidate_sheets)
data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40
if randomizeable_sprites:
candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets)
done = False
while not done:
chosen_sheet = random.choice(candidate_sheets)
data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40
candidate_sprites = get_possible_enemy_sprites(room_id, chosen_sheet, uw_candidates, data_tables)
randomized = True
# wallmaster in hera basement throws off hera basement key code
wallmaster_chosen = room_id in {0x0039, 0x0049, 0x0056, 0x0057, 0x0068, 0x0087, 0x008d}
for i, sprite in randomizeable_sprites.items():
if room_id in custom_uw and i in custom_uw[room_id]:
sprite.kind = sprite_translation[custom_uw[room_id][i]]
else:
# filter out water if necessary
candidate_sprites = [x for x in candidate_sprites if not x.water_only or sprite.water]
# filter out wallmaster if already on tile
if wallmaster_chosen:
candidate_sprites = [x for x in candidate_sprites if x.sprite != EnemySprite.Wallmaster]
candidate_sprites = [x for x in candidate_sprites if not exceeds_sprite_limit(sprite_limit, x)]
if sprite.drops_item:
forbidden = determine_forbidden(any_enemy_logic == 'none', room_id, True)
choice_list = [x for x in candidate_sprites if x.good_for_key_drop(forbidden)]
# terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore
elif room_id in shutter_sprites and i in shutter_sprites[room_id]:
forbidden = determine_forbidden(any_enemy_logic != 'allow_all', room_id)
choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden)]
else:
choice_list = [x for x in candidate_sprites if not x.water_only]
choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_denials)
if enemy_drops_active:
choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_drop_denials)
if len(choice_list) == 0:
randomized = False
break
weight = [data_tables.uw_weights[r.sprite] for r in choice_list]
chosen = random.choices(choice_list, weight, k=1)[0]
sprite.kind = chosen.sprite
if sprite.kind in sprite_limiter:
sprite_limit += sprite_limiter[sprite.kind]-1
if sprite.kind == EnemySprite.Wallmaster:
wallmaster_chosen = True
sprite.kind = 0x09
sprite.sub_type = SpriteType.Overlord
done = randomized
# done with sprites
# done with rooms
def determine_forbidden(forbid, room_id, drop_flag=False):
forbidden_set = set()
if forbid:
forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob,
EnemySprite.Lynel, EnemySprite.RedEyegoreMimic, EnemySprite.RedMimic})
if drop_flag:
forbidden_set.add(EnemySprite.RedBari) # requires FireRod to Drop
# else: Not yet able to protect triggers, would change default GT tile room behavior
# forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway
return forbidden_set
def filter_choices(options, room_id, sprite_idx, denials):
key = room_id, sprite_idx
return [x for x in options if key not in denials or x.sprite not in denials[key]]
def filter_water_phobic(options, sprite):
return [x for x in options if not x.water_phobic or not sprite.water]
def randomize_overworld_enemies(data_tables, custom_ow):
ow_candidates, ow_sheets, all_sheets = find_candidate_sprites(data_tables, range(1, 64), False)
areas_to_randomize = [0, 2, 3, 5, 7, 0xA, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x1a, 0x1b, 0x1d, 0x1e, 0x22, 0x25, 0x28, 0x29, 0x2A, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x32, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3b, 0x3c, 0x3f]
area_list = areas_to_randomize + [x + 0x40 for x in areas_to_randomize] # light world + dark world
area_list += [0x80, 0x81] + [x + 0x90 for x in areas_to_randomize] # specials + post aga LW
for area_id in area_list:
randomizeable_sprites = get_randomize_able_sprites_ow(area_id, data_tables)
if not randomizeable_sprites:
candidate_sheets = get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables)
chosen_sheet = random.choice(candidate_sheets)
data_tables.overworld_sprite_sheets[area_id] = chosen_sheet
candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables)
else:
candidate_sheets = get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables)
chosen_sheet = random.choice(candidate_sheets)
data_tables.overworld_sprite_sheets[area_id] = chosen_sheet
candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables)
for i, sprite in randomizeable_sprites.items():
if area_id in custom_ow and i in custom_ow[area_id]:
sprite.kind = sprite_translation[custom_ow[area_id][i]]
else:
candidate_sprites = filter_choices(candidate_sprites, area_id, i, data_tables.ow_enemy_denials)
candidate_sprites = filter_water_phobic(candidate_sprites, sprite)
weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites]
chosen = random.choices(candidate_sprites, weight, k=1)[0]
sprite.kind = chosen.sprite
# randomize the bush sprite per area
weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites]
bush_sprite_choice = random.choices(candidate_sprites, weight, k=1)[0]
data_tables.bush_sprite_table[area_id] = bush_sprite_choice
# damage and health tables only go to F2
skip_sprites = {
EnemySprite.ArmosKnight, EnemySprite.Lanmolas, EnemySprite.Moldorm, EnemySprite.Mothula, EnemySprite.Arrghus,
EnemySprite.HelmasaurKing, EnemySprite.Vitreous, EnemySprite.TrinexxRockHead, EnemySprite.TrinexxFireHead,
EnemySprite.TrinexxIceHead, EnemySprite.Blind, EnemySprite.Kholdstare, EnemySprite.KholdstareShell,
EnemySprite.FallingIce, EnemySprite.Arrghi, EnemySprite.Agahnim, EnemySprite.Ganon,
EnemySprite.PositionTarget, EnemySprite.Boulders
}
def randomize_enemies(world, player):
if world.enemy_shuffle[player] != 'none':
data_tables = world.data_tables[player]
custom_uw, custom_ow = {}, {}
enemy_map = world.customizer.get_enemies() if world.customizer else None
if enemy_map and player in enemy_map:
if 'Underworld' in enemy_map[player]:
custom_uw = enemy_map[player]['Underworld']
if 'Overworld' in enemy_map[player]:
custom_ow = enemy_map[player]['Overworld']
randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_uw)
randomize_underworld_rooms(data_tables, world, player, custom_uw)
randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_ow)
randomize_overworld_enemies(data_tables, custom_ow)
# fix thief stats
# subclass_table = world.damage_table[player].damage_table['SubClassTable']
# subclass_table[EnemySprite.Thief] = subclass_table[EnemySprite.GreenEyegoreMimic]
# data_tables.enemy_stats[EnemySprite.Thief].health = 4
# could turn droppable on here if we wanted for killable theives
# health shuffle
if world.enemy_health[player] != 'default':
stats = world.data_tables[player].enemy_stats
min_health = {'easy': 1, 'normal': 2, 'hard': 2, 'expert': 4}
max_health = {'easy': 4, 'normal': 15, 'hard': 25, 'expert': 50}
min_h = min_health[world.enemy_health[player]]
max_h = max_health[world.enemy_health[player]]
for sprite, stat in stats.items():
if sprite == EnemySprite.Octorok4Way:
stat.health = stats[EnemySprite.Octorok].health # these guys share data
elif sprite == EnemySprite.GreenMimic:
stat.health = stats[EnemySprite.GreenEyegoreMimic].health # these share data
elif sprite == EnemySprite.RedMimic:
stat.health = stats[EnemySprite.RedEyegoreMimic].health # these share data
elif sprite not in skip_sprites:
if isinstance(stat.health, tuple):
stat.health = random.randint(min_h, max_h), random.randint(min_h, max_h)
else:
stat.health = random.randint(min_h, max_h)
if world.enemy_damage[player] != 'default':
stats = world.data_tables[player].enemy_stats
# randomize damage groupings
for sprite, stat in stats.items():
if sprite == EnemySprite.Octorok4Way:
stat.damage = stats[EnemySprite.Octorok].damage # these guys share data
elif sprite == EnemySprite.GreenMimic:
stat.damage = stats[EnemySprite.GreenEyegoreMimic].damage # these share data
elif sprite == EnemySprite.RedMimic:
stat.damage = stats[EnemySprite.RedEyegoreMimic].damage # these share data
elif sprite == EnemySprite.Thief: # always group 0 for 0 damage
stat.damage = 0
elif sprite not in skip_sprites:
if isinstance(stat.damage, tuple):
stat.damage = random.randint(0, 8), random.randint(0, 8)
else:
stat.damage = random.randint(0, 8)
# randomize bump table
original_table = [
(0x02, 0x01, 0x01),
(0x04, 0x04, 0x04),
(0x00, 0x00, 0x00),
(0x08, 0x04, 0x02),
(0x08, 0x08, 0x08),
(0x10, 0x08, 0x04),
(0x20, 0x10, 0x08),
(0x20, 0x18, 0x10),
(0x18, 0x10, 0x08),
(0x40, 0x30, 0x18)]
for i in range(0, 10):
if i == 0: # group 0 will always be 0 for thieves
green_mail, blue_mail, red_mail = 0, 0, 0
del original_table[2]
else:
if world.enemy_damage[player] == 'random':
green_mail = random.randint(0, 64)
if world.enemy_damage[player] == 'random':
blue_mail = random.randint(0, 64)
red_mail = random.randint(0, 64)
else:
idx = random.randint(0, len(original_table)-1)
green_mail, blue_mail, red_mail = original_table[idx]
del original_table[idx]
world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail]
def write_enemy_shuffle_settings(world, player, rom):
if world.dropshuffle[player] in ['underworld']:
rom.write_byte(snes_to_pc(0x368109), 0x01)
if world.enemy_shuffle[player] != 'none':
# enable new mimics
rom.write_byte(snes_to_pc(0x368105), 0x01)
# killable thief
# rom.write_byte(snes_to_pc(0x368108), 0xc4)
# rom.write_byte(snes_to_pc(0x0DB237), 4) # health value - randomize it if killable, maybe
# mimic room barriers
data_tables = world.data_tables[player]
mimic_room = data_tables.room_list[0x10c] = Room010C
mimic_room.layer1[40].data[0] = 0x54 # rail adjust
mimic_room.layer1[40].data[1] = 0x9C
mimic_room.layer1[45].data[1] = 0xB0 # block adjust 1
mimic_room.layer1[47].data[1] = 0xD0 # block adjust 2
# random tile pattern
pattern_name, tile_pattern = random.choice(tile_patterns)
rom.write_byte(snes_to_pc(0x9BA1D), len(tile_pattern))
for idx, pair in enumerate(tile_pattern):
rom.write_byte(snes_to_pc(0x09BA2A + idx), (pair[0] + 3) * 16)
rom.write_byte(snes_to_pc(0x09BA40 + idx), (pair[1] + 4) * 16)
if world.enemy_shuffle[player] == 'random':
rom.write_byte(snes_to_pc(0x368100), 1) # randomize bushes
+185
View File
@@ -0,0 +1,185 @@
from types import SimpleNamespace
from collections import Counter, defaultdict
from source.dungeon.EnemyList import enemy_names, SpriteType
from source.enemizer.Enemizer import randomize_underworld_rooms, randomize_overworld_enemies
from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets
from source.rom.DataTables import init_data_tables
from source.enemizer.DamageTables import DamageTable
import RaceRandom as random
def calculate_odds():
ctr_uw = Counter()
ctr_ow = Counter()
for trial in range(0, 100):
world = SimpleNamespace(pottery={1: 'none'}, damage_table={1: DamageTable()})
data_tables = init_data_tables(world, 1)
randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables)
randomize_overworld_sprite_sheets(data_tables.sprite_sheets)
for num in range(65, 124):
sheet = data_tables.sprite_sheets[num]
ret = []
for req in data_tables.sprite_requirements.values():
if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static:
ret.append(enemy_names[req.sprite])
for x in ret:
ctr_uw[x] += 1
for num in range(1, 64):
sheet = data_tables.sprite_sheets[num]
ret = []
for req in data_tables.sprite_requirements.values():
if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static:
ret.append(enemy_names[req.sprite])
for x in ret:
ctr_ow[x] += 1
ttl = sum(ctr_uw.values())
print(f'UW: # Total {ttl}')
for k, v in ctr_uw.items():
weight = round(.01 * ttl * 100 / v)
print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}')
ttl = sum(ctr_ow.values())
print(f'OW: # Total {ttl}')
for k, v in ctr_ow.items():
weight = round(.01 * ttl * 100 / v)
print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}')
if __name__ == '__main__':
# calculate_odds()
random.seed(42)
#
frequency = Counter()
sheet_ctr = Counter()
ctr_uw = Counter()
ctr_ow = Counter()
sheet_thing = Counter()
sheet_sub_groups = defaultdict(set)
for trial in range(0, 100):
world = SimpleNamespace(pottery={1: 'none'}, damage_table={1: DamageTable()}, any_enemy_logic={1: 'allow_all'},
dropshuffle={1: 'none'})
data_tables = init_data_tables(world, 1)
stats = data_tables.enemy_stats
def randomize_able_sprite(sprite, stats):
if sprite.static:
return False
if sprite.sub_type == 0x7 and sprite.kind != 0x9:
return False
if sprite.sub_type == 0x7 and sprite.kind == 0x9:
return True
return sprite.kind not in stats or not stats[sprite.kind].static
if trial == 0:
print(f' Underworld:')
for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items():
print(f' {hex(room_id)}:')
for i, sprite in enumerate(enemy_list):
if randomize_able_sprite(sprite, stats):
print(f' {i}: {str(sprite)}')
print(f' Overworld:')
for area, enemy_list in data_tables.ow_enemy_table.items():
print(f' {hex(area)}:')
for i, sprite in enumerate(enemy_list):
if randomize_able_sprite(sprite, stats):
print(f' {i}: {str(sprite)}')
randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, {})
randomize_underworld_rooms(data_tables, world, 1)
randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, {})
randomize_overworld_enemies(data_tables)
for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items():
# print(f'Room {hex(room_id)}:')
for i, sprite in enumerate(enemy_list):
if randomize_able_sprite(sprite, stats):
result = str(sprite)
frequency[result] += 1
for area, enemy_list in data_tables.ow_enemy_table.items():
for i, sprite in enumerate(enemy_list):
if randomize_able_sprite(sprite, stats):
result = str(sprite)
frequency[result] += 1
for num in range(65, 124):
if num in {65, 69, 71, 78, 79, 82, 88, 98}:
continue
sheet = data_tables.sprite_sheets[num]
ret = []
for req in data_tables.sprite_requirements.values():
if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static:
ret.append(enemy_names[req.sprite])
for x in ret:
ctr_uw[x] += 1
sheet_ctr[x] += 1
key = tuple(sorted(ret))
sheet_thing[key] += 1
sheet_sub_groups[key].add(tuple(sheet.sub_groups))
for num in range(1, 64):
if num == 6:
continue
sheet = data_tables.sprite_sheets[num]
ret = []
for req in data_tables.sprite_requirements.values():
if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static:
ret.append(enemy_names[req.sprite])
for x in ret:
ctr_ow[x] += 1
sheet_ctr[x] += 1
key = tuple(sorted(ret))
sheet_thing[key] += 1
sheet_sub_groups[key].add(tuple(sheet.sub_groups))
total_sheets = sum(sheet_thing.values())
ttl = sum(ctr_uw.values())
print(f'UW: # Total {ttl}')
for k in sorted(list(ctr_uw.keys())):
v = ctr_uw[k]
weight = round(.01 * ttl * 100 / v)
print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%')
ttl = sum(ctr_ow.values())
print(f'OW: # Total {ttl}')
for k in sorted(list(ctr_ow.keys())):
v = ctr_ow[k]
weight = round(.01 * ttl * 100 / v)
print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%')
ttl = sum(sheet_ctr.values())
print(f'Sheet: # Total {ttl}')
for k, v in sheet_ctr.items():
print(f' {k} {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%')
ttl = sum(frequency.values())
print(f'Total: {ttl}')
for enemy, freq in frequency.items():
print(f'{enemy} {freq*100/ttl:.5f}% raw:{freq}')
ttl = sum(sheet_thing.values())
print(f'Total Sheets?: {ttl}')
def rejoin(list_of_things):
return f'[{",".join([str(i) for i in list_of_things])}]'
for items, cnt in sheet_thing.items():
print(f'{",".join(items)} {cnt} {cnt*100/ttl:.5f}% {",".join([rejoin(x) for x in sheet_sub_groups[items]])}')
# if result not in column_headers:
# column_headers[result] = None
# stats[(room_id, i)][result] += 1
# with open('result.csv', 'w') as result_file:
# result_file.write('room_id,slot,')
# result_file.write(','.join(column_headers.keys()))
# result_file.write('\n')
#
# for key, counter in stats.items():
# rid, slot = key
# result_file.write(f'{rid},{slot}')
# for result_item in column_headers.keys():
# result_file.write(f',{counter[result_item]}')
# result_file.write('\n')
+525
View File
@@ -0,0 +1,525 @@
import math
from collections import defaultdict
import RaceRandom as random
from source.logic.Rule import RuleFactory
from source.dungeon.EnemyList import EnemySprite
# these are for drops only
def defeat_rule_single(world, player, enemy_sprite, region):
if enemy_sprite.kind == EnemySprite.Terrorpin:
# must be flipped
return has('Hammer', player)
elif enemy_sprite.kind == EnemySprite.RedBari:
# must be burned to drop
return or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player)))
vln = enemy_vulnerability(world, player, enemy_sprite, region)
rules = []
if vln['Blunt'] != 0:
rules.append(has_blunt_weapon(player))
if vln['Stun'] != 0:
rules.append(buzzblob_rule(player))
if vln['Somaria'] != 0:
rules.append(somaria_rule(world, player, vln['Somaria']))
if vln['Byrna'] != 0:
rules.append(byrna_rule(world, player, vln['Byrna']))
if vln['Master'] != 0:
rules.append(has_class_2_weapon(player))
if vln['Bow'] != 0:
rules.append(bow_rule(world, player, vln['Bow']))
if vln['Silvers'] != 0:
rules.append(silvers_rule(world, player, vln['Silvers']))
if vln['Bomb'] != 0:
rules.append(bombs_rule(world, player, vln['Bomb']))
if vln['Hookshot'] != 0:
rules.append(has('Hookshot', player))
if vln['IceRod'] != 0:
rules.append(ice_rod_rule(world, player, vln['IceRod']))
if vln['FireRod'] != 0:
rules.append(fire_rod_rule(world, player, vln['FireRod']))
if vln['Boomerang'] != 0:
rules.append(has_boomerang(player))
if vln['Powder'] not in [0, -3]: # fairy doesn't make it drop
rules.append(magic_powder_rule(world, player, vln['Powder']))
# skip medallions if vln to Blunt?
if vln['Bombos'] != 0 and vln['Blunt'] == 0:
rules.append(medallion_rule(world, player, 'Bombos', vln['Bombos']))
if vln['Ether'] != 0 and vln['Blunt'] == 0:
rules.append(medallion_rule(world, player, 'Ether', vln['Ether']))
if vln['Quake'] != 0 and vln['Blunt'] == 0:
rules.append(medallion_rule(world, player, 'Quake', vln['Quake']))
if enemy_sprite.kind == EnemySprite.StalfosKnight:
# must be bombed once made vulnerable
return and_rule(can_use_bombs(world, player), or_rule(*rules))
return or_rule(*rules)
damage_cost = {
'Bomb': 1, 'Bow': 1, 'Silvers': 1,
'Powder': .5, 'Somaria': .5, 'Byrna': 1.125,
'FireRod': 1, 'IceRod': 1,
'Bombos': 2, 'Ether': 2, 'Quake': 2
}
# damage_set = ['Blunt', 'Stun', 'Master', 'Tempered', 'Boomerang', 'Hookshot', 'Bomb', 'Silvers', 'Bow',
# 'Somaria', 'Powder', 'FireRod', 'IceRod', 'Byrna', 'Bombos', 'Ether', 'Quake']
# these are for "challenge" rooms
def defeat_rule_multiple(world, player, enemy_sprite_region_pairs):
vln_list = {}
for sprite, region in enemy_sprite_region_pairs:
vln_list[(sprite, region)] = enemy_vulnerability(world, player, sprite, region)
# damage_accounting = {x: list(y) for x, y in damage_types.items()}
used_resources = {'Bomb': 0, 'Arrow': 0, 'Magic': 0}
required_rules = []
picky_enemies = []
hammer_required = False
bombs_required = False
for key, vln in vln_list.items():
if key[0].kind == EnemySprite.Terrorpin:
if not hammer_required:
required_rules.append(has('Hammer', player))
hammer_required = True
picky_enemies.append(key)
continue
if key[0].kind == EnemySprite.StalfosKnight:
if not bombs_required:
required_rules.append(bombs_rule(world, player, 1))
bombs_required = True
used_resources['Bomb'] += 1
picky_enemies.append(key)
continue
vln_types = [k for k in vln.keys() if vln[k] != 0]
if len(vln_types) == 1:
d_type = vln_types[0]
required_rules.append(defeat_rule_single(world, player, key[0], key[1]))
picky_enemies.append(key)
if d_type in damage_cost:
cost = damage_cost[d_type]
if d_type == 'Bomb':
used_resources['Bomb'] += cost
elif d_type in ['Bow', 'Silvers']:
used_resources['Arrow'] += cost
else:
used_resources['Magic'] += cost
vln_list = {k: v for k, v in vln_list.items() if k not in picky_enemies}
while len(vln_list) > 0:
optional_clears = find_possible_rules(vln_list, used_resources, world, player)
if len(optional_clears) == 0:
raise Exception('Kill rules seems to be insufficient for this enemy set, please report:'
+ ', '.join([str(x) for x, y in enemy_sprite_region_pairs]))
# find rules which kill the most
# idea: this could be multiple criteria: most-constrained then which method kills the most
best_rules = {}
best_size = 0
for vln_option in optional_clears.keys():
if len(vln_option) > best_size:
best_size = len(vln_option)
best_rules.clear()
best_rules[vln_option] = optional_clears[vln_option]
elif len(vln_option) == best_size: # assumes vln_option is different from prior options
best_rules[vln_option] = optional_clears[vln_option]
if len(best_rules) == 1:
vln_option, rule_pair_list = next(iter(best_rules.items()))
else:
vln_option, rule_pair_list = random.choice(list(best_rules.items()))
if best_size == 0:
raise Exception('Invulnerable enemy? rules seems to be insufficient for this enemy set, please report:'
+ ', '.join([str(x) for x, y in enemy_sprite_region_pairs]))
new_vln_list = {vln_kv[0]: vln_kv[1] for idx, vln_kv in enumerate(vln_list.items()) if idx not in vln_option}
rules_to_add = [rule for rule, resources in rule_pair_list]
resources_to_use = [resources for rule, resources in rule_pair_list]
required_rules.append(or_rule(*rules_to_add))
for r in resources_to_use:
for k, v in r.items():
used_resources[k] += v
vln_list = new_vln_list
return and_rule(*required_rules)
def find_possible_rules(vln_list, used_resources, world, player):
optional_clears = defaultdict(list)
blunt_marker = defaultdict(bool)
for damage_type in ['Blunt', 'Stun', 'Master', 'Boomerang', 'Hookshot']:
# all_vln = all(vln[damage_type] != 0 for vln in vln_list.values())
vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0})
if vln_sub_list:
if damage_type == 'Blunt':
optional_clears[vln_sub_list].append((has_blunt_weapon(player), {}))
blunt_marker[vln_sub_list] = True
if damage_type == 'Stun':
optional_clears[vln_sub_list].append((buzzblob_rule(player), {}))
if damage_type == 'Master' and not blunt_marker[vln_sub_list]:
optional_clears[vln_sub_list].append((has_class_2_weapon(player), {}))
if damage_type == 'Boomerang':
optional_clears[vln_sub_list].append((has('Hookshot', player), {}))
elif damage_type == 'Hookshot':
optional_clears[vln_sub_list].append((has_boomerang(player), {}))
damage_type = 'Bomb'
vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0})
if vln_sub_list:
hits = needed_resources(damage_type, vln_list)
if hits + used_resources['Bomb'] <= 8:
optional_clears[vln_sub_list].append(
(bombs_rule(world, player, hits + used_resources['Bomb']), {'Bomb': hits}))
for damage_type in ['Bow', 'Silvers']:
vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0})
if vln_sub_list:
hits = needed_resources(damage_type, vln_list)
resources = {'Arrow': hits}
if damage_type == 'Bow' and hits + used_resources['Arrow'] <= 25:
optional_clears[vln_sub_list].append(
(bow_rule(world, player, hits + used_resources['Arrow']), resources))
if damage_type == 'Silvers' and hits + used_resources['Arrow'] <= 25:
optional_clears[vln_sub_list].append(
(silvers_rule(world, player, hits + used_resources['Arrow']), resources))
for damage_type in ['Powder', 'Somaria', 'Byrna', 'FireRod', 'IceRod', 'Bombos', 'Ether', 'Quake']:
vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0})
if vln_sub_list:
hits = needed_resources(damage_type, vln_list)
resources = {'Magic': damage_cost[damage_type] * hits}
if damage_type == 'Powder' and math.ceil(hits / 16) * 8 + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'] * 2)
optional_clears[vln_sub_list].append((magic_powder_rule(world, player, flag), resources))
elif damage_type == 'Somaria' and math.ceil(hits / 64) * 8 + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'] * 8)
optional_clears[vln_sub_list].append((somaria_rule(world, player, flag), resources))
elif damage_type == 'Byrna' and math.ceil(hits / 7) * 8 + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'] * 7 / 8)
optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources))
elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'])
optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources))
elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'])
optional_clears[vln_sub_list].append((ice_rod_rule(world, player, flag), resources))
elif hits * 2 + used_resources['Magic'] <= 160 and not blunt_marker[vln_sub_list]:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'] / 2)
optional_clears[vln_sub_list].append((medallion_rule(world, player, damage_type, flag), resources))
return optional_clears
def needed_resources(damage_type, vln_list):
return sum(vln[damage_type] if vln[damage_type] >= 0 else 1 for vln in vln_list.values() if vln[damage_type] != 0)
special_rules_check = {
'Swamp Waterway': None,
'Hera Back': [5, 6],
'GT Petting Zoo': [5, 8, 9, 11],
'Mimic Cave': [4, 5, 6, 7],
'Ice Hookshot Ledge': None,
'TR Hub Ledges': [3, 4, 5, 6, 7],
'TR Dark Ride': [1, 2, 3],
'GT Speed Torch': [11, 13],
'Old Man Cave (West)': None,
'Old Man Cave (East)': None,
'Old Man House': [1, 3],
'Old Man House Back': [4, 5, 6],
'Death Mountain Return Cave (left)': None,
'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7], # 2, 5, 6 are considered embedded
'Hookshot Fairy': [0, 1, 2, 3]
}
def special_rules_for_region(world, player, region_name, location, original_rule):
if region_name == 'Swamp Waterway':
return or_rule(medallion_rule(world, player, 'Quake', 1),
medallion_rule(world, player, 'Ether', 1),
medallion_rule(world, player, 'Bombos', 1))
elif region_name in ['Hera Back', 'GT Petting Zoo', 'Mimic Cave']:
enemy_number = int(location.name.split('#')[1])
if region_name == 'Mimic Cave':
if enemy_number in [4, 5]: # these are behind hammer blocks potentially
return and_rule(original_rule, has('Hammer', player))
elif enemy_number in special_rules_check[region_name]: # these are behind rails
return and_rule(original_rule, has_boomerang(player))
if enemy_number in special_rules_check[region_name]:
return and_rule(original_rule, has_boomerang(player))
else:
return original_rule
elif region_name in ['TR Hub Ledges', 'Ice Hookshot Ledge', 'TR Dark Ride']:
enemy_number = int(location.name.split('#')[1])
if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]:
return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player)))
else:
return original_rule
elif region_name in ['Old Man Cave (West)', 'Old Man Cave (East)', 'Old Man House Back', 'Old Man House',
'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)',
'Hookshot Fairy']:
enemy_number = int(location.name.split('#')[1])
if region_name == 'Death Mountain Return Cave (left)':
if enemy_number in [1, 5]:
return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player)))
else:
return and_rule(original_rule, has('Hookshot', player))
if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]:
return and_rule(original_rule, has('Hookshot', player))
else:
return original_rule
return original_rule
def has_blunt_weapon(player):
return or_rule(has_sword(player), has('Hammer', player))
# Bombs, Arrows, Bombos, FireRod, Somaria, Byrna should be handled by the damage table logic
# Powder doesn't work (also handled by damage table)
def buzzblob_rule(player):
return or_rule(has('Golden Sword', player),
and_rule(has_blunt_weapon(player),
or_rule(has_boomerang(player), has('Hookshot', player), has('Ice Rod', player))),
and_rule(has('Ether', player), has_sword(player)),
and_rule(has('Quake', player), has_sword(player)))
def has_class_2_weapon(player):
return or_rule(has_beam_sword(player), has('Hammer', player))
def somaria_rule(world, player, somaria_hits):
if somaria_hits == -1:
return has('Cane of Somaria', player) # insta-kill somaria? - not in vanilla
else:
magic_needed = math.ceil(somaria_hits / 64) * 8 # 64 hits per magic bar - 80 max?
if magic_needed > 8:
return and_rule(has('Cane of Somaria', player), can_extend_magic(world, player, magic_needed))
else:
return has('Cane of Somaria', player)
def byrna_rule(world, player, byrna_hits):
if byrna_hits == -1:
return has('Cane of Byrna', player) # insta-kill byrna? - not in vanilla
else:
magic_needed = math.ceil(byrna_hits / 7) * 8 # 7 hits per magic bar - generous?
if magic_needed > 8:
return and_rule(has('Cane of Byrna', player), can_extend_magic(world, player, magic_needed))
else:
return has('Cane of Byrna', player)
def bow_rule(world, player, arrows):
if arrows == -1 or 0 < arrows <= 25:
return can_shoot_normal_arrows(world, player)
return RuleFactory.static_rule(False)
def silvers_rule(world, player, arrows):
if arrows == -1 or 0 < arrows <= 25:
return can_shoot_silver_arrows(world, player)
return RuleFactory.static_rule(False)
def bombs_rule(world, player, bombs):
if bombs == -1 or 0 < bombs <= 8:
return can_use_bombs(world, player)
return RuleFactory.static_rule(False)
def ice_rod_rule(world, player, shots):
if shots == -1:
return has('Ice Rod', player)
if shots > 8:
return and_rule(has('Ice Rod', player), can_extend_magic(world, player, shots))
else:
return has('Ice Rod', player)
def fire_rod_rule(world, player, shots):
if shots == -1:
return has('Fire Rod', player)
if shots > 8:
return and_rule(has('Fire Rod', player), can_extend_magic(world, player, shots))
else:
return has('Fire Rod', player)
def magic_powder_rule(world, player, shots):
if shots == -1:
return has('Magic Powder', player)
if shots == -2:
# todo: other resources possible I guess - harder to keep track of though
return and_rule(has('Magic Powder', player), or_rule(has_blunt_weapon(player), has('Hookshot', player)))
magic_needed = math.ceil(shots / 16) * 8 # 16 tries per magic bar, that could be tight...
if magic_needed > 8:
return and_rule(has('Magic Powder', player), can_extend_magic(world, player, shots))
else:
return has('Magic Powder', player)
def medallion_rule(world, player, medallion, shots):
if shots == -1:
return and_rule(has(medallion, player), has_sword(player))
if shots == -2:
return and_rule(has(medallion, player), has_sword(player))
magic_needed = shots * 2
if magic_needed > 8:
return and_rule(has(medallion, player), has_sword(player), can_extend_magic(world, player, shots))
else:
return and_rule(has(medallion, player), has_sword(player))
def or_rule(*rules):
return RuleFactory.disj(rules)
def and_rule(*rules):
return RuleFactory.conj(rules)
def has(item, player, count=1):
return RuleFactory.item(item, player, count)
def has_sword(player):
return or_rule(
has('Fighter Sword', player), has('Master Sword', player),
has('Tempered Sword', player), has('Golden Sword', player)
)
def has_beam_sword(player):
return or_rule(
has('Master Sword', player), has('Tempered Sword', player), has('Golden Sword', player)
)
def has_class_3_sword(player):
return or_rule(
has('Tempered Sword', player), has('Golden Sword', player)
)
def can_extend_magic(world, player, magic, flag_t=False):
potion_shops = (find_shops_that_sell('Blue Potion', world, player) |
find_shops_that_sell('Green Potion', world, player))
return RuleFactory.extend_magic(player, magic, world.difficulty_adjustments[player], potion_shops, flag_t)
# class 0 damage (subtypes 1 and 2)
def has_boomerang(player):
return or_rule(has('Blue Boomerang', player), has('Red_Boomerang', player))
def find_shops_that_sell(item, world, player):
return {shop.region for shop in world.shops[player] if shop.has_unlimited(item) and shop.region.player == player}
def can_shoot_normal_arrows(world, player):
if world.bow_mode[player].startswith('retro'):
shops = find_shops_that_sell('Single Arrow', world, player)
# retro+shopsanity, shops may not sell the Single Arrow at all
if world.bow_mode[player] == 'retro_silvers':
# non-progressive silvers grant wooden arrows, so shop may not be needed
return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops),
has('Single Arrow', player), has('Silver Arrows', player)))
else:
return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops),
has('Single Arrow', player)))
return has('Bow', player)
def can_shoot_silver_arrows(world, player):
# retro_silver requires the silver arrows item which is sufficient for the quiver
if world.bow_mode[player] == 'retro':
shops = find_shops_that_sell('Single Arrow', world, player)
# retro+shopsanity, shops may not sell the Single Arrow at all
return and_rule(has('Silver Arrows', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops),
has('Single Arrow', player)))
return and_rule(has('Bow', player), has('Silver Arrows', player))
def can_use_bombs(world, player):
return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player))
def enemy_vulnerability(world, player, enemy_sprite, region):
damage_table = world.damage_table[player].damage_table
stats = world.data_tables[player].enemy_stats
damage_src = damage_table['DamageSource']
sub_class_table = damage_table['SubClassTable']
enemy_sub_class = sub_class_table[enemy_sprite.kind]
vulnerability = defaultdict(int)
c1 = number_of_hits('Sword1', damage_src, enemy_sub_class, stats, enemy_sprite, region)
if c1 != 0:
if enemy_sprite.kind == EnemySprite.Buzzblob:
vulnerability['Stun'] = -1
else:
vulnerability['Blunt'] = -1
vulnerability['Master'] = -1
vulnerability['Somaria'] = c1
vulnerability['Byrna'] = c1
else:
c2 = number_of_hits('Sword3', damage_src, enemy_sub_class, stats, enemy_sprite, region)
if c2 != 0:
vulnerability['Master'] = -1 # currently Lynels are only vulnerable to only master spins or above
hits = number_of_hits('Arrow', damage_src, enemy_sub_class, stats, enemy_sprite, region)
if hits != 0:
vulnerability['Bow'] = hits
hits = number_of_hits('SilverArrow', damage_src, enemy_sub_class, stats, enemy_sprite, region)
if hits != 0:
vulnerability['Silvers'] = hits
for method in ['Bomb', 'Hookshot', 'FireRod', 'IceRod', 'Boomerang', 'Powder', 'Bombos', 'Ether', 'Quake']:
hits = number_of_hits(method, damage_src, enemy_sub_class, stats, enemy_sprite, region)
if hits == -3:
if enemy_sprite.kind != EnemySprite.Buzzblob: # buzzblobs are special and don't die
vulnerability[method] = -1
elif hits != 0:
vulnerability[method] = hits
return vulnerability
def number_of_hits(source_name, damage_src, enemy_sub_class, stats, enemy_sprite, region):
damage_class = damage_src[source_name]['class']
sub_class = enemy_sub_class[damage_class]
damage_amount = damage_src[source_name]['subclass'][sub_class]
if damage_amount == 0:
return 0
elif damage_amount <= 0x64:
health = stats[enemy_sprite.kind].health
if isinstance(health, tuple):
if enemy_sprite.kind in [EnemySprite.Tektite, EnemySprite.HardhatBeetle]:
idx = enemy_sprite.tile_x & 0x1
health = health[idx]
elif region.is_light_world and region.is_dark_world:
health = min(health)
elif region.is_light_world:
health = health[0]
elif region.is_dark_world:
health = health[1]
else:
health = max(health)
return math.ceil(health / damage_amount)
elif damage_amount in [0xF9, 0xFA, 0xFD]:
# -1 incinerated; -2 blobbed, -3 fairy-ed (depends on enemy if "killed" or not)
# F9: fairy, defeated, but doesn't drop anything: -3
# FA: blobbed - can you kill a blob? = -2
# FD: incinerated
return special_classes[damage_amount]
else:
return 0
special_classes = {0xF9: -3, 0xFA: -2, 0xFD: -1}
File diff suppressed because it is too large Load Diff
+788
View File
@@ -0,0 +1,788 @@
import logging
from collections import defaultdict
import RaceRandom as random
from source.dungeon.EnemyList import EnemySprite, SpriteType, enemy_names, sprite_translation, overlord_names
from source.dungeon.RoomConstants import *
class SpriteRequirement:
def __init__(self, sprite, overlord=0):
self.sprite = sprite
self.overlord = overlord
self.boss = False
self.static = False # npcs and do not randomize
self.killable = True
self.can_drop = True
self.water_only = False
self.dont_use = False
self.ow_valid = True
self.uw_valid = True
self.can_randomize = True
self.water_phobic = False
self.groups = []
self.sub_groups = defaultdict(list)
self.excluded_rooms = set()
self.allowed_rooms = set()
def can_spawn_in_room(self, room_id):
return room_id not in self.excluded_rooms and (self.sprite != EnemySprite.Wallmaster or room_id < 0x100)
def no_drop(self):
self.can_drop = False
return self
def sub_group(self, key, subs):
if isinstance(subs, list):
self.sub_groups[key].extend(subs)
else:
self.sub_groups[key].append(subs)
return self
def group(self, group_id):
self.groups.append(group_id)
return self
def exclude(self, exclusions):
self.excluded_rooms.update(exclusions)
return self
def allow(self, allowed):
self.allowed_rooms.update(allowed)
return self
def affix(self):
self.static = True
self.killable = False
self.can_drop = False
return self
def stasis(self):
self.can_randomize = False
return self
def exalt(self):
self.boss = True
self.static = True # not randomized by sprite sheet
return self
def immune(self):
self.killable = False
self.can_drop = False
return self
def immerse(self):
self.water_only = True
return self
def aquaphobia(self):
self.water_phobic = True
return self
def skip(self):
self.dont_use = True
return self
def ow_skip(self):
self.ow_valid = False
return self
def uw_skip(self):
self.uw_valid = False
return self
def good_for_uw_water(self):
return self.water_only and not self.static and not self.dont_use and self.uw_valid
def good_for_shutter(self, forbidden):
if self.sprite in forbidden:
return False
return self.killable and not self.static and not self.dont_use and self.uw_valid
def good_for_key_drop(self, forbidden):
return self.good_for_shutter(forbidden) and self.can_drop
def __str__(self):
return f'Req for {enemy_names[self.sprite] if self.overlord != 0x7 else overlord_names[self.sprite]}'
NoFlyingRooms = {0xd2, 0x10c} # Mire 2, Mimic Cave
NoBeamosOrTrapRooms = {0xb, 0x16, 0x19, 0x1e, 0x26, 0x27, 0x36, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4b, 0x4e, 0x55, 0x57,
0x5f, 0x65, 0x6a, 0x74, 0x76, 0x7d, 0x7f, 0x83, 0x84, 0x85, 0x8c, 0x8d, 0x92, 0x95, 0x98, 0x9b,
0x9c, 0x9d, 0x9e, 0xa0, 0xaa, 0xaf, 0xb3, 0xba, 0xbb, 0xbc, 0xc6, 0xcb, 0xce, 0xd0, 0xd2, 0xd5,
0xd8, 0xdc, 0xdf, 0xe4, 0xe7, 0xee, 0xf9, 0xfd, 0x10c}
LenientTrapsForTesting = {0x16, 0x26, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4e, 0x57,
0x65, 0x6a, 0x74, 0x76, 0x7d, 0x98,
0x9e, 0xaf, 0xba, 0xc6, 0xcb, 0xce, 0xd2, 0xd5,
0xd8, 0xdf, 0xe4, 0xe7, 0xee, 0xfd, 0x10c}
# this will have to be dynamic if cave rooms are allowed in dungeons
WallmasterValidRooms = {
HC_NorthCorridor, HC_SwitchRoom, HoulihanRoom, TR_CrystalRollerRoom,
PalaceofDarkness0x09, PoD_StalfosTrapRoom, PoD_TurtleRoom, GT_EntranceRoom, Ice_EntranceRoom,
GanonEvacuationRoute, HC_BombableStockRoom, Sanctuary, TR_Hokku_BokkuKeyRoom2, TR_BigKeyRoom, TurtleRock0x15,
Swamp_SwimmingTreadmill, Hera_MoldormFallRoom, PoD_DarkMaze, PoD_BigChestRoom, PoD_Mimics_MovingWallRoom,
GT_IceArmos, GT_FinalHallway, Ice_BombFloor_BariRoom, Ice_Pengator_BigKeyRoom, Tower_Agahnim, HC_KeyRatRoom,
HC_SewerTextTriggerRoom, TR_WestExittoBalcony, TR_DoubleHokku_Bokku_BigchestRoom, Swamp_StatueRoom, Hera_BigChest,
Swamp_EntranceRoom, Skull_Mothula, PoD_BigHubRoom, PoD_MapChest_FairyRoom, Ice_CompassRoom, Hera_HardhatBeetlesRoom,
HC_SewerKeyChestRoom, Desert_Lanmolas, Swamp_PushBlockPuzzle_Pre_BigKeyRoom, Swamp_BigKey_BSRoom,
Swamp_BigChestRoom, Swamp_MapChest_WaterFillRoom, Swamp_KeyPotRoom, Skull_GibdoKey_MothulaHoleRoom,
PoD_BombableFloorRoom, PoD_SpikeBlock_ConveyorRoom, GT_TorchRoom2, Ice_StalfosKnights_ConveyorHellway,
Ice_MapChestRoom, Tower_FinalBridgeRoom, HC_FirstDarkRoom, HC_6RopesRoom, Desert_TorchPuzzle_MovingWallRoom,
TT_BigChestRoom, TT_JailCellsRoom, Swamp_CompassChestRoom, Skull_GibdoTorchPuzzleRoom, PoD_EntranceRoom,
PoD_Warps_SouthMimicsRoom, GT_Mini_HelmasaurConveyorRoom, GT_MoldormRoom, Ice_Bomb_JumpRoom,
IcePalaceCloneRoom_FairyRoom, HC_WestCorridor, HC_ThroneRoom, HC_EastCorridor, Desert_Popos2_BeamosHellwayRoom,
Swamp_UpstairsPitsRoom, CastleSecretEntrance_UncleDeathRoom, Skull_KeyPot_TrapRoom, Skull_BigKeyRoom,
Skull_BigChestRoom, Skull_FinalSectionEntranceRoom, PoD_HelmasaurKing, GT_SpikePitRoom, GT_Ganon_BallZ,
GT_Gauntlet1_2_3, Ice_LonelyFirebar, Ice_HiddenChest_SpikeFloorRoom, HC_WestEntranceRoom, HC_MainEntranceRoom,
HC_EastEntranceRoom, Desert_FinalSectionEntranceRoom, TT_WestAtticRoom, TT_EastAtticRoom,
Swamp_HiddenChest_HiddenDoorRoom, Skull_CompassChestRoom, Skull_KeyChest_TrapRoom, PoD_RupeeRoom, GT_MimicsRooms,
GT_LanmolasRoom, GT_Gauntlet4_5, Ice_PengatorsRoom, HC_SmallCorridortoJailCells, HC_BoomerangChestRoom,
HC_MapChestRoom, Desert_BigChestRoom, Desert_MapChestRoom, Desert_BigKeyChestRoom, Swamp_WaterDrainRoom,
Hera_EntranceRoom, GanonsTower, GT_EastSideCollapsingBridge_ExplodingWallRoom, GT_Winder_WarpMazeRoom,
Ice_HiddenChest_BombableFloorRoom, Ice_BigSpikeTrapsRoom, HC_JailCellRoom, HC_NextToChasmRoom, HC_BasementChasmRoom,
Desert_WestEntranceRoom, Desert_MainEntranceRoom, Desert_EastEntranceRoom, Hera_TileRoom, Eastern_FairyRoom,
GT_BlockPuzzle_SpikeSkip_MapChestRoom, GT_EastandWestDownstairs_BigChestRoom, GT_Tile_TorchPuzzleRoom,
IcePalace0x8E, Mire_Vitreous, Mire_FinalSwitchRoom, Mire_DarkBombWall_SwitchesRoom,
Mire_DarkCaneFloorSwitchPuzzleRoom, GT_FinalCollapsingBridgeRoom, GT_Torches1Room, Mire_TorchPuzzle_MovingWallRoom,
Mire_EntranceRoom, Eastern_EyegoreKeyRoom, GT_ManySpikes_WarpMazeRoom, GT_InvisibleFloorMazeRoom,
GT_CompassChest_InvisibleFloorRoom, Ice_BigChestRoom, IcePalace0x9F, Mire_Pre_VitreousRoom, Mire_FishRoom,
Mire_BridgeKeyChestRoom, MiseryMire0xA3, TR_Trinexx, GT_WizzrobesRooms, GT_MoldormFallRoom, Hera_FairyRoom,
Eastern_StalfosSpawnRoom, Eastern_BigChestRoom, Eastern_MapChestRoom, TT_MovingSpikes_KeyPotRoom, TT_BlindTheThief,
IcePalace0xAE, Ice_IceBridgeRoom, Tower_CircleofPots, Mire_HourglassRoom, Mire_SlugRoom, Mire_SpikeKeyChestRoom,
TR_Pre_TrinexxRoom, TR_DarkMaze, TR_ChainChompsRoom, TR_MapChest_KeyChest_RollerRoom, Eastern_BigKeyRoom,
Eastern_LobbyCannonballsRoom, Eastern_DarkAntifairy_KeyPotRoom, TT_Hellway, TT_ConveyorToilet, Ice_BlockPuzzleRoom,
IcePalaceCloneRoom_SwitchRoom, Tower_DarkBridgeRoom, Mire_CompassChest_TileRoom, Mire_BigHubRoom, Mire_BigChestRoom,
TR_FinalCrystalSwitchPuzzleRoom, TR_LaserBridge, TurtleRock0xC6, TR_TorchPuzzle,
Eastern_EntranceRoom, UnknownRoom, TT_NorthWestEntranceRoom, TT_NorthEastEntranceRoom, Ice_HoletoKholdstareRoom,
Tower_DarkMaze, Mire_ConveyorSlug_BigKeyRoom, Mire_Mire02_WizzrobesRoom, TR_LaserKeyRoom, TR_EntranceRoom,
Eastern_PreArmosKnightsRoom, Eastern_CanonballRoom, EasternPalace, TT_Main_SouthWestEntranceRoom,
TT_SouthEastEntranceRoom, Tower_EntranceRoom
}
def init_sprite_requirements():
reqs = [
SpriteRequirement(EnemySprite.Raven).no_drop().sub_group(3, [0x11, 0x19]).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.Vulture).no_drop().sub_group(2, 0x12).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.CorrectPullSwitch).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.WrongPullSwitch).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.Octorok).sub_group(2, [0xc, 0x18]),
SpriteRequirement(EnemySprite.Moldorm).exalt().sub_group(2, 0x30),
SpriteRequirement(EnemySprite.Octorok4Way).sub_group(2, 0xc),
SpriteRequirement(EnemySprite.Cucco).immune().sub_group(3, [0x15, 0x50]).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.Buzzblob).sub_group(3, 0x11),
SpriteRequirement(EnemySprite.Snapdragon).sub_group(0, 0x16).sub_group(2, 0x17),
SpriteRequirement(EnemySprite.Octoballoon).no_drop().sub_group(2, 0xc).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.Hinox).sub_group(0, 0x16),
SpriteRequirement(EnemySprite.Moblin).sub_group(2, 0x17),
SpriteRequirement(EnemySprite.MiniHelmasaur).sub_group(1, 0x1e),
SpriteRequirement(EnemySprite.AntiFairy).no_drop().sub_group(3, [0x52, 0x53])
.exclude(NoFlyingRooms).exclude({0x40}), # no anti-fairies in aga tower bridge room
SpriteRequirement(EnemySprite.Wiseman).affix().sub_group(2, 0x4c),
SpriteRequirement(EnemySprite.Hoarder).sub_group(3, 0x11).exclude({0x10c}),
SpriteRequirement(EnemySprite.MiniMoldorm).sub_group(1, 0x1e),
SpriteRequirement(EnemySprite.Poe).no_drop().sub_group(3, 0x15).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.Smithy).affix().sub_group(1, 0x1d).sub_group(3, 0x15),
SpriteRequirement(EnemySprite.Statue).stasis().immune().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.CrystalSwitch).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.SickKid).affix().sub_group(0, 0x51),
SpriteRequirement(EnemySprite.Sluggula).sub_group(2, 0x25),
SpriteRequirement(EnemySprite.WaterSwitch).affix().sub_group(3, 0x53),
SpriteRequirement(EnemySprite.Ropa).sub_group(0, 0x16),
SpriteRequirement(EnemySprite.RedBari).sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.BlueBari).sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.TalkingTree).affix().sub_group(3, [0x15, 0x1B]),
SpriteRequirement(EnemySprite.HardhatBeetle).sub_group(1, 0x1e),
SpriteRequirement(EnemySprite.Deadrock).sub_group(3, 0x10).exclude({0x7f, 0x10c}),
SpriteRequirement(EnemySprite.DarkWorldHintNpc).affix(), # no groups?
SpriteRequirement(EnemySprite.AdultNpc).affix().sub_group(0, [0xe, 0x4f]),
SpriteRequirement(EnemySprite.SweepingLady).affix().group(6), # no sub groups?
SpriteRequirement(EnemySprite.Lumberjacks).affix().sub_group(2, 0x4a),
SpriteRequirement(EnemySprite.RaceGameLady).affix().group(6),
SpriteRequirement(EnemySprite.FortuneTeller).affix().sub_group(0, 0x4b),
SpriteRequirement(EnemySprite.ArgueBros).affix().sub_group(0, 0x4f),
SpriteRequirement(EnemySprite.RupeePull).affix(),
SpriteRequirement(EnemySprite.YoungSnitch).affix().group(6),
SpriteRequirement(EnemySprite.Innkeeper).affix(), # no groups?
SpriteRequirement(EnemySprite.Witch).affix().sub_group(2, 0x7c),
SpriteRequirement(EnemySprite.Waterfall).affix(),
SpriteRequirement(EnemySprite.EyeStatue).affix(),
SpriteRequirement(EnemySprite.Locksmith).affix().sub_group(3, 0x11),
SpriteRequirement(EnemySprite.MagicBat).affix().sub_group(3, 0x1d),
SpriteRequirement(EnemySprite.KidInKak).affix().group(6),
SpriteRequirement(EnemySprite.OldSnitch).affix().group(6),
SpriteRequirement(EnemySprite.Hoarder2).sub_group(3, 0x11).exclude({0x10c}),
SpriteRequirement(EnemySprite.TutorialGuard).affix(),
SpriteRequirement(EnemySprite.LightningGate).affix().sub_group(3, 0x3f),
SpriteRequirement(EnemySprite.BlueGuard).sub_group(1, [0xd, 0x49]),
SpriteRequirement(EnemySprite.GreenGuard).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.RedSpearGuard).sub_group(1, [0xd, 0x49]),
SpriteRequirement(EnemySprite.BluesainBolt).sub_group(0, 0x46).sub_group(1, [0xd, 0x49]),
SpriteRequirement(EnemySprite.UsainBolt).sub_group(1, [0xd, 0x49]),
SpriteRequirement(EnemySprite.BlueArcher).sub_group(0, 0x48).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.GreenBushGuard).sub_group(0, 0x48).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.RedJavelinGuard).sub_group(0, 0x46).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.RedBushGuard).sub_group(0, 0x46).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.BombGuard).sub_group(0, 0x46).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.GreenKnifeGuard).sub_group(1, 0x49).sub_group(2, 0x13),
SpriteRequirement(EnemySprite.Geldman).sub_group(2, 0x12).exclude({0x10c}),
SpriteRequirement(EnemySprite.Toppo).immune().sub_group(3, 0x11),
SpriteRequirement(EnemySprite.Popo).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.Popo2).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.ArmosStatue).sub_group(3, 0x10).exclude({0x10c}),
SpriteRequirement(EnemySprite.KingZora).affix().sub_group(3, 0x44),
SpriteRequirement(EnemySprite.ArmosKnight).exalt().sub_group(3, 0x1d),
SpriteRequirement(EnemySprite.Lanmolas).exalt().sub_group(3, 0x31),
SpriteRequirement(EnemySprite.FireballZora).immerse().no_drop().sub_group(2, [0xc, 0x18]), # .uw_skip() test
SpriteRequirement(EnemySprite.Zora).sub_group(2, 0xc).sub_group(3, 0x44), # .uw_skip() test
SpriteRequirement(EnemySprite.DesertStatue).affix().sub_group(2, 0x12),
SpriteRequirement(EnemySprite.Crab).sub_group(2, 0xc),
SpriteRequirement(EnemySprite.LostWoodsBird).affix().sub_group(2, 0x37).sub_group(3, 0x36),
SpriteRequirement(EnemySprite.LostWoodsSquirrel).affix().sub_group(2, 0x37).sub_group(3, 0x36),
SpriteRequirement(EnemySprite.SparkCW).immune().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.SparkCCW).immune().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.RollerVerticalUp).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms),
SpriteRequirement(EnemySprite.RollerVerticalDown).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms),
SpriteRequirement(EnemySprite.RollerHorizontalLeft).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms),
SpriteRequirement(EnemySprite.RollerHorizontalRight).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms),
SpriteRequirement(EnemySprite.Beamos).no_drop().sub_group(1, 0x2c).exclude(NoBeamosOrTrapRooms),
SpriteRequirement(EnemySprite.MasterSword).affix().sub_group(2, 0x37).sub_group(3, 0x36),
SpriteRequirement(EnemySprite.DebirandoPit).sub_group(0, 0x2f), # skip
SpriteRequirement(EnemySprite.Debirando).sub_group(0, 0x2f), # skip
SpriteRequirement(EnemySprite.ArcheryNpc).affix().sub_group(0, 0x4b),
SpriteRequirement(EnemySprite.WallCannonVertLeft).affix().sub_group(0, 0x2f),
SpriteRequirement(EnemySprite.WallCannonVertRight).affix().sub_group(0, 0x2f),
SpriteRequirement(EnemySprite.WallCannonHorzTop).affix().sub_group(0, 0x2f),
SpriteRequirement(EnemySprite.WallCannonHorzBottom).affix().sub_group(0, 0x2f),
SpriteRequirement(EnemySprite.BallNChain).sub_group(0, 0x46).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.CannonTrooper).sub_group(0, 0x46).sub_group(1, 0x49),
SpriteRequirement(EnemySprite.CricketRat).sub_group(2, [0x1c, 0x24]),
SpriteRequirement(EnemySprite.Snake).sub_group(2, [0x1c, 0x24]),
SpriteRequirement(EnemySprite.Keese).no_drop().sub_group(2, [0x1c, 0x24]),
SpriteRequirement(EnemySprite.Leever).sub_group(0, 0x2f),
SpriteRequirement(EnemySprite.FairyPondTrigger).affix().sub_group(3, 0x36),
SpriteRequirement(EnemySprite.UnclePriest).affix().sub_group(0, [0x47, 0x51]),
SpriteRequirement(EnemySprite.RunningNpc).affix().group(6),
SpriteRequirement(EnemySprite.BottleMerchant).affix().group(6),
SpriteRequirement(EnemySprite.Zelda).affix(),
SpriteRequirement(EnemySprite.Grandma).affix().sub_group(0, 0x4b).sub_group(1, 0x4d).sub_group(2, 0x4a),
SpriteRequirement(EnemySprite.Agahnim).exalt().sub_group(0, 0x55).sub_group(1, [0x1a, 0x3d]).sub_group(2, 0x42)
.sub_group(3, 0x43),
SpriteRequirement(EnemySprite.FloatingSkull).sub_group(0, 0x1f).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.BigSpike).sub_group(3, [0x52, 0x53]).no_drop(),
SpriteRequirement(EnemySprite.FirebarCW).immune().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.FirebarCCW).immune().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.Firesnake).no_drop().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.Hover).sub_group(2, 0x22), # .exclude(NoFlyingRooms), might be okay now
SpriteRequirement(EnemySprite.AntiFairyCircle).no_drop().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.GreenEyegoreMimic).sub_group(2, 0x2e),
SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e),
SpriteRequirement(EnemySprite.Kodongo).sub_group(2, 0x2a),
# SpriteRequirement(EnemySprite.YellowStalfos).sub_group(0, 0x1f), # doesn't spawn
SpriteRequirement(EnemySprite.Mothula).exalt().sub_group(2, 0x38).sub_group(3, 0x52),
SpriteRequirement(EnemySprite.SpikeBlock).immune().sub_group(3, [0x52, 0x53]).exclude(NoBeamosOrTrapRooms)
.exclude({0x28}), # why exclude sp entrance?
SpriteRequirement(EnemySprite.Gibdo).sub_group(2, 0x23),
SpriteRequirement(EnemySprite.Arrghus).exalt().sub_group(2, 0x39),
SpriteRequirement(EnemySprite.Arrghi).exalt().sub_group(2, 0x39),
SpriteRequirement(EnemySprite.Terrorpin).sub_group(2, 0x2a).exclude({0x10c}), # probably fine in mimic now
SpriteRequirement(EnemySprite.Blob).sub_group(1, 0x20),
SpriteRequirement(EnemySprite.Wallmaster).immune().ow_skip().sub_group(2, 0x23)
.allow(WallmasterValidRooms),
SpriteRequirement(EnemySprite.StalfosKnight).sub_group(1, 0x20).exclude({0x10c}),
SpriteRequirement(EnemySprite.HelmasaurKing).exalt().sub_group(2, 0x3a).sub_group(3, 0x3e),
SpriteRequirement(EnemySprite.Bumper).immune().aquaphobia().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.LaserEyeLeft).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.LaserEyeRight).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.LaserEyeTop).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.LaserEyeBottom).affix().sub_group(3, [0x52, 0x53]),
SpriteRequirement(EnemySprite.Pengator).sub_group(2, 0x26),
SpriteRequirement(EnemySprite.Kyameron).no_drop().immerse().sub_group(2, 0x22),
SpriteRequirement(EnemySprite.Wizzrobe).sub_group(2, [0x25, 0x29]),
SpriteRequirement(EnemySprite.Zoro).sub_group(1, 0x20),
SpriteRequirement(EnemySprite.Babasu).sub_group(1, 0x20),
SpriteRequirement(EnemySprite.GroveOstritch).affix().sub_group(2, 0x4e),
SpriteRequirement(EnemySprite.GroveRabbit).affix(),
SpriteRequirement(EnemySprite.GroveBird).affix().sub_group(2, 0x4e),
SpriteRequirement(EnemySprite.Freezor).stasis().skip().sub_group(2, 0x26),
SpriteRequirement(EnemySprite.Kholdstare).exalt().sub_group(2, 0x3c),
SpriteRequirement(EnemySprite.KholdstareShell).exalt(),
SpriteRequirement(EnemySprite.FallingIce).exalt().sub_group(2, 0x3c),
SpriteRequirement(EnemySprite.BlueZazak).sub_group(2, 0x28),
SpriteRequirement(EnemySprite.RedZazak).sub_group(2, 0x28),
SpriteRequirement(EnemySprite.Stalfos).sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.GreenZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.BlueZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms),
SpriteRequirement(EnemySprite.Pikit).sub_group(3, 0x1b),
SpriteRequirement(EnemySprite.CrystalMaiden).affix(),
SpriteRequirement(EnemySprite.OldMan).affix().sub_group(0, 0x46).sub_group(1, 0x49).sub_group(2, 0x1c),
SpriteRequirement(EnemySprite.PipeDown).affix(),
SpriteRequirement(EnemySprite.PipeUp).affix(),
SpriteRequirement(EnemySprite.PipeRight).affix(),
SpriteRequirement(EnemySprite.PipeLeft).affix(),
SpriteRequirement(EnemySprite.GoodBee).affix().sub_group(0, 0x1f),
SpriteRequirement(EnemySprite.PurpleChest).affix().sub_group(3, 0x15),
SpriteRequirement(EnemySprite.BombShopGuy).affix().sub_group(1, 0x4d),
SpriteRequirement(EnemySprite.Kiki).affix().sub_group(3, 0x19),
SpriteRequirement(EnemySprite.BlindMaiden).affix(),
# dialog tester.sub_group(1, 0x2c)
SpriteRequirement(EnemySprite.BullyPinkBall).affix().sub_group(3, 0x14),
# shop keepers in complex thing below
SpriteRequirement(EnemySprite.Drunkard).affix().sub_group(0, 0x4f).sub_group(1, 0x4d).sub_group(2, 0x4a).
sub_group(3, 0x50),
SpriteRequirement(EnemySprite.Vitreous).exalt().sub_group(3, 0x3d),
SpriteRequirement(EnemySprite.Catfish).affix().sub_group(2, 0x18),
SpriteRequirement(EnemySprite.CutsceneAgahnim).affix().sub_group(0, 0x55).sub_group(1, 0x3d)
.sub_group(2, 0x42).sub_group(3, 0x43),
SpriteRequirement(EnemySprite.Boulder).affix().sub_group(3, 0x10),
SpriteRequirement(EnemySprite.Gibo).sub_group(2, 0x28),
SpriteRequirement(EnemySprite.Thief).immune().uw_skip().sub_group(0, [0xe, 0x15]),
SpriteRequirement(EnemySprite.Medusa).affix(),
SpriteRequirement(EnemySprite.FourWayShooter).affix(),
SpriteRequirement(EnemySprite.Pokey).sub_group(2, 0x27),
SpriteRequirement(EnemySprite.BigFairy).affix().sub_group(2, 0x39).sub_group(3, 0x36),
SpriteRequirement(EnemySprite.Tektite).sub_group(3, 0x10),
SpriteRequirement(EnemySprite.Chainchomp).immune().sub_group(2, 0x27),
SpriteRequirement(EnemySprite.TrinexxRockHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f),
SpriteRequirement(EnemySprite.TrinexxFireHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f),
SpriteRequirement(EnemySprite.TrinexxIceHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f),
SpriteRequirement(EnemySprite.Blind).exalt().sub_group(1, 0x2c).sub_group(2, 0x3b),
SpriteRequirement(EnemySprite.Swamola).no_drop().sub_group(3, 0x19),
SpriteRequirement(EnemySprite.Lynel).sub_group(3, 0x14),
SpriteRequirement(EnemySprite.BunnyBeam).no_drop().ow_skip(),
SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(),
SpriteRequirement(EnemySprite.Stal),
SpriteRequirement(EnemySprite.Landmine).skip(),
SpriteRequirement(EnemySprite.DiggingGameNPC).affix().sub_group(1, 0x2a),
SpriteRequirement(EnemySprite.Ganon).exalt().sub_group(0, 0x21).sub_group(1, 0x41)
.sub_group(2, 0x45).sub_group(3, 0x33),
SpriteRequirement(EnemySprite.Faerie).immune(),
SpriteRequirement(EnemySprite.FakeMasterSword).immune().sub_group(3, 0x11),
SpriteRequirement(EnemySprite.MagicShopAssistant).affix().sub_group(0, 0x4b).sub_group(3, 0x5a),
SpriteRequirement(EnemySprite.SomariaPlatform).affix().sub_group(2, 0x27),
SpriteRequirement(EnemySprite.CastleMantle).affix().sub_group(0, 0x5d),
SpriteRequirement(EnemySprite.GreenMimic).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.RedMimic).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.MedallionTablet).affix().sub_group(2, 0x12),
# overlord requirements - encapsulated mostly in the required sheets
SpriteRequirement(2, 7).affix().sub_group(2, 46),
SpriteRequirement(3, 7).affix().sub_group(2, 46),
SpriteRequirement(5, 7).affix().sub_group(0, 31),
SpriteRequirement(6, 7).affix().sub_group(2, [28, 36]),
SpriteRequirement(7, 7).affix(),
SpriteRequirement(8, 7).affix().sub_group(1, 32),
SpriteRequirement(9, 7).affix().sub_group(2, 35),
SpriteRequirement(0xa, 7).affix().sub_group(3, 82),
SpriteRequirement(0xb, 7).affix().sub_group(3, 82),
SpriteRequirement(0x10, 7).affix().sub_group(2, 34),
SpriteRequirement(0x11, 7).affix().sub_group(2, 34),
SpriteRequirement(0x12, 7).affix().sub_group(2, 34),
SpriteRequirement(0x13, 7).affix().sub_group(2, 34),
SpriteRequirement(0x14, 7).affix(),
SpriteRequirement(0x15, 7).affix().sub_group(2, [37, 41]),
SpriteRequirement(0x16, 7).affix().sub_group(1, 32),
SpriteRequirement(0x17, 7).affix().sub_group(0, 31),
SpriteRequirement(0x18, 7).affix().sub_group(0, 31),
SpriteRequirement(0x19, 7).affix(),
SpriteRequirement(0x1a, 7).affix(),
]
simple = {(r.sprite, r.overlord): r for r in reqs}
shopkeeper = [
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(2, 74).sub_group(3, 90)
.allow({0xff, 0x112, 0x11f}),
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(1, 77).sub_group(2, 74)
.sub_group(3, 90).allow({0x10f, 0x110, 0x11f}),
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 79).sub_group(2, 74).sub_group(3, 90)
.allow({0x118}),
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 90)
.allow({0x123, 0x124}),
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 80)
.allow({0x125, 0x100}),
SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 21).allow({0x11e}),
]
complex_r = {}
for req in shopkeeper:
for r in req.allowed_rooms:
complex_r[r] = req
simple[(EnemySprite.Shopkeeper, 0)] = complex_r
return simple
# sheet 1 and 1c have group 4 modified from vanilla for murahdahla
vanilla_sheets = [
(0x00, 0x49, 0x00, 0x00), (0x46, 0x49, 0x0C, 0x3F), (0x48, 0x49, 0x13, 0x3F), (0x46, 0x49, 0x13, 0x0E),
(0x48, 0x49, 0x0C, 0x11), (0x48, 0x49, 0x0C, 0x10), (0x4F, 0x49, 0x4A, 0x50), (0x0E, 0x49, 0x4A, 0x11),
(0x46, 0x49, 0x12, 0x00), (0x00, 0x49, 0x00, 0x50), (0x00, 0x49, 0x00, 0x11), (0x48, 0x49, 0x0C, 0x00),
(0x00, 0x00, 0x37, 0x36), (0x48, 0x49, 0x4C, 0x11), (0x5D, 0x2C, 0x0C, 0x44), (0x00, 0x00, 0x4E, 0x00),
(0x0F, 0x00, 0x12, 0x10), (0x00, 0x00, 0x00, 0x4C), (0x00, 0x0D, 0x17, 0x00), (0x16, 0x0D, 0x17, 0x1B),
(0x16, 0x0D, 0x17, 0x14), (0x15, 0x0D, 0x17, 0x15), (0x16, 0x0D, 0x18, 0x19), (0x16, 0x0D, 0x17, 0x19),
(0x16, 0x0D, 0x00, 0x00), (0x16, 0x0D, 0x18, 0x1B), (0x0F, 0x49, 0x4A, 0x11), (0x4B, 0x2A, 0x5C, 0x15),
(0x16, 0x49, 0x17, 0x3F), (0x00, 0x00, 0x00, 0x15), (0x16, 0x0D, 0x17, 0x10), (0x16, 0x49, 0x12, 0x00),
(0x16, 0x49, 0x0C, 0x11), (0x00, 0x00, 0x12, 0x10), (0x16, 0x0D, 0x00, 0x11), (0x16, 0x49, 0x0C, 0x00),
(0x16, 0x0D, 0x4C, 0x11), (0x0E, 0x0D, 0x4A, 0x11), (0x16, 0x1A, 0x17, 0x1B), (0x4F, 0x34, 0x4A, 0x50),
(0x35, 0x4D, 0x65, 0x36), (0x4A, 0x34, 0x4E, 0x00), (0x0E, 0x34, 0x4A, 0x11), (0x51, 0x34, 0x5D, 0x59),
(0x4B, 0x49, 0x4C, 0x11), (0x2D, 0x00, 0x00, 0x00), (0x5D, 0x00, 0x12, 0x59), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x47, 0x49, 0x2B, 0x2D), (0x46, 0x49, 0x1C, 0x52), (0x00, 0x49, 0x1C, 0x52), (0x5D, 0x49, 0x00, 0x52),
(0x46, 0x49, 0x13, 0x52), (0x4B, 0x4D, 0x4A, 0x5A), (0x47, 0x49, 0x1C, 0x52), (0x4B, 0x4D, 0x39, 0x36),
(0x1F, 0x2C, 0x2E, 0x52), (0x1F, 0x2C, 0x2E, 0x1D), (0x2F, 0x2C, 0x2E, 0x52), (0x2F, 0x2C, 0x2E, 0x31),
(0x1F, 0x1E, 0x30, 0x52), (0x51, 0x49, 0x13, 0x00), (0x4F, 0x49, 0x13, 0x50), (0x4F, 0x4D, 0x4A, 0x50),
(0x4B, 0x49, 0x4C, 0x2B), (0x1F, 0x20, 0x22, 0x53), (0x55, 0x3D, 0x42, 0x43), (0x1F, 0x1E, 0x23, 0x52),
(0x1F, 0x1E, 0x39, 0x3A), (0x1F, 0x1E, 0x3A, 0x3E), (0x1F, 0x1E, 0x3C, 0x3D), (0x40, 0x1E, 0x27, 0x3F),
(0x55, 0x1A, 0x42, 0x43), (0x1F, 0x1E, 0x2A, 0x52), (0x1F, 0x1E, 0x38, 0x52), (0x1F, 0x20, 0x28, 0x52),
(0x1F, 0x20, 0x26, 0x52), (0x1F, 0x2C, 0x25, 0x52), (0x1F, 0x20, 0x27, 0x52), (0x1F, 0x1E, 0x29, 0x52),
(0x1F, 0x2C, 0x3B, 0x52), (0x46, 0x49, 0x24, 0x52), (0x21, 0x41, 0x45, 0x33), (0x1F, 0x2C, 0x28, 0x31),
(0x1F, 0x0D, 0x29, 0x52), (0x1F, 0x1E, 0x27, 0x52), (0x1F, 0x20, 0x27, 0x53), (0x48, 0x49, 0x13, 0x52),
(0x0E, 0x1E, 0x4A, 0x50), (0x1F, 0x20, 0x26, 0x53), (0x15, 0x00, 0x00, 0x00), (0x1F, 0x00, 0x2A, 0x52),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x08), (0x5D, 0x49, 0x00, 0x52), (0x55, 0x49, 0x42, 0x43),
(0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50),
(0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50),
(0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x56, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50),
(0x61, 0x56, 0x33, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50)
]
required_boss_sheets = {EnemySprite.ArmosKnight: 9, EnemySprite.Lanmolas: 11, EnemySprite.Moldorm: 12,
EnemySprite.HelmasaurKing: 21, EnemySprite.Arrghus: 20, EnemySprite.Mothula: 26,
EnemySprite.Blind: 32, EnemySprite.Kholdstare: 22, EnemySprite.Vitreous: 22,
EnemySprite.TrinexxRockHead: 23}
class SpriteSheet:
def __init__(self, id, default_sub_groups):
self.id = id
self.sub_groups = list(default_sub_groups)
self.locked = [False] * 4
self.room_set = set()
def dungeon_id(self):
return self.id + 0x40
def valid_sprite(self, requirement):
if requirement.groups and self.id not in requirement.groups:
return False
for idx, sub in enumerate(self.sub_groups):
if requirement.sub_groups[idx] and sub not in requirement.sub_groups[idx]:
return False
return True
def lock_sprite_in(self, sprite):
for i, options in sprite.sub_groups.items():
self.locked[i] = len(options) > 0
def add_sprite_to_sheet(self, groups, rooms=None):
for idx, g in enumerate(groups):
if g is not None:
self.sub_groups[idx] = g
self.locked[idx] = True
if rooms is not None:
self.room_set.update(rooms)
def write_to_rom(self, rom, base_address):
rom.write_bytes(base_address + self.id * 4, self.sub_groups)
def __str__(self):
return f'{self.id} => [{", ".join([str(x) for x in self.sub_groups])}]'
# convert to dungeon id
def did(n):
return n + 0x40
def init_sprite_sheets(requirements):
sheets = {id: SpriteSheet(id, def_sheet) for id, def_sheet in enumerate(vanilla_sheets)}
# wait until bosses are randomized to determine which are randomized
for sprite, sheet_num in required_boss_sheets.items():
sheet = sheets[did(sheet_num)] # convert to dungeon sheet id
boss_sprite = requirements[(sprite, 0)]
sheet.lock_sprite_in(boss_sprite)
return sheets
def setup_required_dungeon_groups(sheets, data_tables):
sheets[did(1)].add_sprite_to_sheet([70, 73, 28, 82], {0xe4, 0xf0}) # old man
# various npcs
sheets[did(5)].add_sprite_to_sheet([75, 77, 74, 90], {0xf3, 0x109, 0x10e, 0x10f, 0x110, 0x111, 0x112,
0x11a, 0x11c, 0x11f, 0x122})
sheets[did(7)].add_sprite_to_sheet([75, 77, 57, 54], {0x8, 0x2c, 0x114, 0x115, 0x116}) # big fairies
sheets[did(13)].add_sprite_to_sheet([81, None, None, None], {0x55, 0x102, 0x104}) # uncle, sick kid
sheets[did(14)].add_sprite_to_sheet([71, 73, 76, 80], {0x12, 0x105, 0x10a}) # wisemen
sheets[did(15)].add_sprite_to_sheet([79, 77, 74, 80], {0xf4, 0xf5, 0x101, 0x103, 0x106, 0x118, 0x119}) # more npcs
sheets[did(18)].add_sprite_to_sheet([85, 61, 66, 67], {0x20, 0x30}) # aga alter, aga1
sheets[did(24)].add_sprite_to_sheet([85, 26, 66, 67], {0xd}) # aga2
sheets[did(34)].add_sprite_to_sheet([33, 65, 69, 51], {0}) # ganon
sheets[did(40)].add_sprite_to_sheet([14, None, 74, 80], {0x124, 0x125, 0x126}) # fairy + rupee npcs
sheets[did(9)].add_sprite_to_sheet([None, None, None, 29], {0xe3}) # magic bat
sheets[did(28)].add_sprite_to_sheet([None, None, 38, 82], {0xe, 0x7e, 0x8e, 0x9e}) # freezors
sheets[did(3)].add_sprite_to_sheet([93, None, None, None], {0x51}) # mantle
sheets[did(42)].add_sprite_to_sheet([21, None, None, None], {0x11e}) # hype cave
sheets[did(10)].add_sprite_to_sheet([47, None, 46, None], {0x5c, 0x75, 0xb9, 0xd9}) # cannonballs
sheets[did(37)].add_sprite_to_sheet([31, None, 39, 82], {0x24, 0xb4, 0xb5, 0xc6, 0xc7, 0xd6}) # somaria platforms
# not sure 31 is needed above
free_sheet_reqs = [
([75, None, None, None], [0xff, 0x11f]), # shopkeepers
([None, 77, None, 21], [0x121]), # smithy
([None, None, None, 80], [0x108]), # chicken house
([14, 30, None, None], [0x123]), # mini moldorm (shutter door)
([None, None, 34, None], [0x36, 0x46, 0x66, 0x76]), # pirogusu spawners
([None, 32, None, None], [0x9f]), # babasu spawners
([31, None, None, None], [0x7f]), # force baris
([None, None, 35, None], [0x39, 0x49]), # wallmasters
# bumpers - why the split - because of other requirements -
([None, None, None, (82, 83)], [0x17, 0x2a, 0x4c, 0x59, 0x67, 0x7e, 0x8b, 0xeb, 0xfb]),
# crystal switches - split for some reason
([None, None, None, (82, 83)], [0xb, 0x13, 0x1b, 0x1e, 0x2a, 0x2b, 0x31, 0x5b, 0x6b, 0x77, 0x8b,
0x91, 0x92, 0x9b, 0x9d, 0xa1, 0xab, 0xbf, 0xc4, 0xef]),
# laser eyes - split for some reason
([None, None, None, (82, 83)], [0x13, 0x23, 0x96, 0xa5, 0xc5, 0xd5]),
# statues - split for some reason
([None, None, None, (82, 83)], [0x26, 0x2b, 0x40, 0x4a, 0x6b, 0x7b]),
([None, None, None, 83], [0x43, 0x63, 0x87]), # tile rooms
# non-optional
([None, None, None, 82], [0x58, 0x8c, 0x10b]), # pull switches
([None, None, (28, 36), 82], [0x2, 0x64]), # pull switches (snakes)
([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x5e, 0x7c, 0x95, 0xc3]), # collapsing bridges
([None, None, None, 83], [0x3f, 0xce]), # pull tongue
([None, None, None, 83], [0x35, 0x37, 0x76]), # swamp drains
([None, None, 34, None], [0x28]), # tektike forced? - spawn chest
([None, None, 37, None], [0x97]), # wizzrobe spawner - in middle of room...
# combined
([None, 32, None, (82, 83)], [0x3e]), # babasu spawners + crystal switch
([None, 32, None, 83], [0x4]), # zoro spawners + crystal switch + pull switch
([None, None, 35, 82], [0x56]), # wallmaster + collasping bridge
([None, None, 35, (82, 83)], [0x57, 0x68]), # wallmaster + statue and wallmaster + bumpers
([None, None, 34, 83], [0x76]), # swamp drain + pirogusu spawners
([None, None, 35, 83], [0x8d]), # wallmaster + tile room
([None, None, None, 83], [0xb6, 0xc1]), # tile room + crystal switch
# allow some sprites / increase odds:
([72, 73, None, None], []), # allow for blue archer + greenbush
([None, 73, 19, None], []), # allow for green knife guard
([None, None, 12, 68], []), # increase odds for zora
([22, None, 23, None], []), # increase odds for snapdragon
]
data_tables.room_requirements = {}
# find home for the free_sheet_reqs
for pair in free_sheet_reqs:
groups, room_list = pair
for room in room_list:
data_tables.room_requirements[room] = groups
find_matching_sheet(groups, sheets, range(65, 124), room_list)
# RandomizeRooms(optionFlags);
# roomCollection.LoadRooms()
# roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags);
# more stuff
sub_group_choices = {
0: [22, 31, 47, 14],
1: [44, 30, 32], # 73, 13
2: [12, 18, 23, 24, 28, 46, 34, 35, 39, 40, 38, 41, 36, 37, 42],
3: [17, 16, 27, 20, 82, 83, 25] # 25 for Swamola
}
# 70, 72 for guards
# 0: 72 specifically for BlueArcher/GreenBush (but needs combination)
# 0: 70 for guards but needs combination
# 2: 19 for green knife guard, but needs combination
# 3: 68 for Zora, but needs combination
def combine_req(sub_groups, requirement):
for i in range(0, 4):
if requirement.sub_groups[i]:
if len(sub_groups[i]) == 0:
sub_groups[i].update(requirement.sub_groups[i])
else:
if len(sub_groups[i].intersection(requirement.sub_groups[i])) == 0:
raise IncompatibleEnemyException
sub_groups[i].intersection_update(requirement.sub_groups[i])
def setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, sheet_range, uw=True):
requirements = data_tables.sprite_requirements
for room_id, enemy_map in custom_enemies.items():
if uw:
original_list = data_tables.uw_enemy_table.room_map[room_id]
else:
original_list = data_tables.ow_enemy_table[room_id]
sub_groups_choices = [set(), set(), set(), set()]
for idx, sprite in enumerate(original_list):
if idx in enemy_map:
key = (sprite_translation[enemy_map[idx]], 0)
if key not in requirements:
continue
req = requirements[key]
try:
combine_req(sub_groups_choices, req)
except IncompatibleEnemyException:
logging.getLogger('').warning(f'Incompatible enemy: {hex(room_id)}:{idx} {enemy_map[idx]}')
else:
sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type
key = (sprite.kind, sprite_secondary)
if key not in requirements:
continue
req = requirements[key]
if isinstance(req, dict):
req = req[room_id]
if req.static or not req.can_randomize:
try:
combine_req(sub_groups_choices, req)
except IncompatibleEnemyException:
raise IncompatibleEnemyException(f'Incompatible enemy: {hex(room_id)}:{idx} {str(req)}')
sheet_req = [None if not x else tuple(x) for x in sub_groups_choices]
find_matching_sheet(sheet_req, sheets, sheet_range, [room_id], True)
def randomize_underworld_sprite_sheets(sheets, data_tables, custom_enemies):
setup_required_dungeon_groups(sheets, data_tables)
setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, range(65, 124), True)
for num in range(65, 124): # sheets 0x41 to 0x7B inclusive
sheet = sheets[num]
# if not sheet.locked[1] and num in [65, 66, 67, 68]: # guard stuff, kind of
# sheet.locked[1] = True
# sheet.sub_groups[1] = random.choice([13, 73])
free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]]
while free_slots:
choices = [c for c in data_tables.sheet_choices if all(slot in free_slots for slot in c.slots)]
weights = [c.weight for c in choices]
choice = random.choices(choices, weights, k=1)[0]
for idx, val in choice.assignments.items():
v = random.choice(val) if isinstance(val, list) else val
sheet.sub_groups[idx] = v
sheet.locked[idx] = True
free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]]
def setup_required_overworld_groups(sheets):
sheets[7].add_sprite_to_sheet([None, None, 74, None], {0x2}) # lumberjacks
sheets[16].add_sprite_to_sheet([None, None, 18, 16], {0x3, 0x93}) # WDM (pre/post-Aga)
sheets[7].add_sprite_to_sheet([None, None, None, 17], {0xA, 0x9A}) # DM Foothills? (pre/post-Aga)
sheets[4].add_sprite_to_sheet([None, None, None, None], {0xF, 0x9F}) # Waterfall of wishing (pre/post-Aga)
sheets[3].add_sprite_to_sheet([None, None, None, 14], {0x14, 0xA4}) # Graveyard (pre/post-Aga)
sheets[1].add_sprite_to_sheet([None, None, 76, 0x3F], {0x1B, 0xAB}) # Hyrule Castle (pre/post-Aga)
sheets[2].add_sprite_to_sheet([None, None, None, 0x3F], {}) # Hyrule Castle - rain state
# Smithy/Race/Kak (pre/post-Aga)
sheets[6].add_sprite_to_sheet([0x4F, 0x49, 0x4A, 0x50], {0x18, 0x22, 0x28, 0xA8, 0xB2, 0xB8})
sheets[8].add_sprite_to_sheet([None, None, 18, None], {0x30, 0xC0}) # Desert (pre/post-Aga)
sheets[10].add_sprite_to_sheet([None, None, None, 17], {0x3A, 0xCA}) # M-rock (pre/post-Aga)
sheets[22].add_sprite_to_sheet([None, None, 24, None], {0x4F, 0xDF}) # Catfish (pre/post-Aga)
sheets[21].add_sprite_to_sheet([21, None, None, 21], {0x62, 0xF2}) # Smith DW (pre/post-Aga)
sheets[27].add_sprite_to_sheet([None, 42, None, None], {0x68, 0xF8}) # Dig Game (pre/post-Aga)
sheets[13].add_sprite_to_sheet([None, None, 76, None], {0x16, 0xA6}) # Witch hut (pre/post-Aga)
sheets[29].add_sprite_to_sheet([None, 77, None, 21], {0x69, 0xF9}) # VoO South (pre/post-Aga)
sheets[15].add_sprite_to_sheet([None, None, 78, None], {0x2A, 0xBA}) # Haunted Grove (pre/post-Aga)
sheets[17].add_sprite_to_sheet([None, None, None, 76], {0x6A, 0xFA}) # Stumpy (pre/post-Aga)
sheets[12].add_sprite_to_sheet([None, None, 55, 54], {0x80, 0x110}) # Specials (pre/post-Aga)
sheets[14].add_sprite_to_sheet([None, None, 12, 68], {0x81, 0x111}) # Zora's Domain (pre/post-Aga)
sheets[26].add_sprite_to_sheet([15, None, None, None], {0x92}) # Lumberjacks post-Aga
sheets[23].add_sprite_to_sheet([None, None, None, 25], {0x5E, 0xEE}) # PoD pre/post-Aga
free_sheet_reqs = [
[None, None, None, 0x14], # bully+pink ball needs this
[72, 73, None, None], # allow for blue archer + green bush
[None, 73, 19, None], # allow for green knife guard
[22, None, 23, None], # increase odds for snapdragon
[70, 73, None, None], # guards group (ballnchain, redbush, redjav, cannon, bomb, bluesain
[None, None, None, 0x15], # an option for talking trees
[None, None, None, 0x1B], # an option for talking trees
]
for group in free_sheet_reqs:
find_matching_sheet(group, sheets, range(1, 64))
class NoMatchingSheetException(Exception):
pass
class IncompatibleEnemyException(Exception):
pass
def find_matching_sheet(groups, sheets, search_sheets, room_list=None, lock_match=False):
possible_sheets = []
found_match = False
for num in search_sheets:
if num in {6, 65, 69, 71, 78, 79, 82, 88, 98}: # these are not useful sheets for randomization
continue
sheet = sheets[num]
valid = True
match = True
for idx, value in enumerate(groups):
if value is not None and sheet.locked[idx]:
valid = False
if (sheet.sub_groups[idx] not in value if isinstance(value, tuple)
else value != sheet.sub_groups[idx]):
match = False
elif value is not None:
match = False
if match:
found_match = True
if lock_match and room_list is not None:
sheet.room_set.update(room_list)
break
if valid:
possible_sheets.append(sheet)
if not found_match:
if len(possible_sheets) == 0:
raise NoMatchingSheetException
chosen_sheet = random.choice(possible_sheets)
chosen_groups = [(random.choice(g) if isinstance(g, tuple) else g) for g in groups]
chosen_sheet.add_sprite_to_sheet(chosen_groups, room_list)
def randomize_overworld_sprite_sheets(sheets, data_tables, custom_enemies):
setup_required_overworld_groups(sheets)
setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, range(1, 64), False)
for num in range(1, 64): # sheets 0x1 to 0x3F inclusive
sheet = sheets[num]
if num == 6: # skip this group - it is locked for kakariko
continue
free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]]
while free_slots:
choices = [c for c in data_tables.sheet_choices if all(slot in free_slots for slot in c.slots)]
weights = [c.weight for c in choices]
choice = random.choices(choices, weights, k=1)[0]
for idx, val in choice.assignments.items():
v = random.choice(val) if isinstance(val, list) else val
sheet.sub_groups[idx] = v
sheet.locked[idx] = True
free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]]
class SheetChoice:
def __init__(self, slots, assignments, weight):
self.slots = slots
self.assignments = assignments
self.weight = weight
+102
View File
@@ -0,0 +1,102 @@
import os
import json
import codecs
if __name__ == '__main__':
directory = './EnemizerCLI.Core/tiles'
for filename in os.listdir(directory):
with codecs.open(directory+'/'+filename, 'r', 'utf-8-sig') as fin:
pattern = json.load(fin)
pairs = [f'({x["x"]}, {x["y"]})' for x in pattern["Items"]]
print(f'(\'{filename}\', [{", ".join(pairs)}]),')
tile_patterns = [
('heart soft', [(3, 1), (5, 2), (4, 7), (2, 5), (7, 1), (7, 5), (8, 4), (1, 2), (2, 1), (1, 4), (5, 7), (6, 1),
(6, 6), (4, 2), (8, 3), (1, 3), (3, 6), (8, 2)]),
('metroid', [(2, 7), (7, 7), (1, 3), (3, 1), (8, 3), (1, 6), (4, 5), (5, 3), (1, 4), (6, 1), (6, 4), (8, 6), (3, 4),
(7, 5), (4, 1), (5, 5), (2, 2), (2, 5), (7, 2), (5, 1), (4, 3), (8, 4)]),
# ('moldorm vertical', [(5, 1), (6, 0), (7, 2), (5, 4), (4, 4), (4, 1), (3, 5), (5, 6), (3, 2), (6, 6), (7, 5),
# (6, 1), (4, 8), (3, 3), (5, 7), (3, 8), (2, 7), (6, 4), (4, 0), (3, 6), (7, 3), (4, 6)]),
# ('scream emoji', [(2, 2), (7, 2), (2, 3), (7, 3), (1, 7), (8, 7), (3, 2), (6, 2), (2, 6), (7, 6), (3, 3), (6, 3),
# (2, 7), (7, 7), (4, 5), (5, 7), (5, 5), (4, 7), (4, 6), (5, 6), (2, 5), (7, 5)]),
('mario mushroom', [(3, 7), (4, 7), (5, 7), (3, 4), (4, 4), (5, 4), (3, 1), (4, 1), (5, 1), (2, 2), (6, 6), (6, 2),
(2, 6), (1, 3), (7, 5), (7, 3), (1, 5), (1, 4), (6, 5), (7, 4), (2, 5)]),
('moldorm', [(1, 3), (2, 5), (3, 6), (5, 5), (7, 5), (7, 2), (5, 3), (3, 2), (2, 4), (1, 5), (5, 4), (6, 2), (7, 4),
(8, 1), (8, 4), (4, 2), (9, 2), (2, 3), (4, 6), (9, 3), (7, 3), (6, 6)]),
# ('thinking emoji', [(5, 6), (6, 4), (4, 3), (3, 1), (2, 6), (5, 3), (6, 6), (6, 1), (3, 0), (4, 7), (2, 4),
# (3, 8), (6, 0), (3, 7), (3, 3)]),
('triangle', [(1, 5), (7, 5), (4, 2), (3, 5), (5, 5), (4, 3), (2, 4), (6, 4), (5, 3), (2, 5), (6, 5), (3, 3),
(4, 5), (5, 4), (3, 4), (4, 4)]),
('heart', [(8, 3), (2, 3), (5, 7), (2, 4), (8, 4), (4, 6), (6, 6), (8, 2), (2, 2), (5, 2), (7, 1), (3, 1), (3, 5),
(6, 1), (7, 5), (4, 1)]),
('arrghus', [(4, 1), (6, 2), (2, 3), (4, 3), (3, 4), (4, 5), (5, 6), (3, 7), (1, 4), (2, 5), (5, 1), (6, 3), (2, 2),
(7, 4), (5, 4), (4, 4), (6, 5), (3, 6), (5, 7), (3, 1), (3, 5), (5, 5)]),
# ('cowboy smile', [(1, 2), (3, 3), (4, 1), (3, 5), (5, 8), (6, 7), (5, 2), (7, 2), (1, 3), (2, 7), (5, 5), (5, 3),
# (3, 2), (4, 3), (7, 3), (2, 3), (4, 2), (3, 8), (4, 8), (6, 3)]),
('clown face happy', [(2, 2), (7, 6), (7, 2), (2, 6), (3, 3), (6, 7), (6, 3), (3, 7), (2, 3), (5, 6), (7, 3),
(4, 6), (2, 5), (6, 6), (7, 5), (3, 6), (4, 5), (5, 7), (5, 5), (4, 7), (3, 2), (6, 2)]),
('generic happy face', [(2, 1), (6, 3), (6, 5), (4, 6), (2, 2), (6, 6), (2, 3), (3, 5), (3, 2), (3, 3), (6, 2),
(5, 5), (1, 2), (3, 6), (7, 5), (7, 1), (4, 5), (8, 2), (5, 6), (2, 5), (7, 3), (7, 2)]),
# ('YMCA', [(1, 2), (2, 3), (5, 2), (7, 2), (6, 3), (7, 4), (2, 4), (5, 4), (6, 2), (3, 2), (5, 3), (7, 3), (7, 6),
# (4, 8), (2, 7), (6, 7), (4, 6), (8, 8), (8, 7), (3, 8), (3, 6), (6, 8)]),
# ('ze', [(5, 7), (6, 7), (7, 7), (1, 3), (2, 3), (3, 3), (5, 5), (6, 5), (7, 5), (1, 7), (2, 7), (3, 7), (5, 3),
# (6, 3), (7, 3), (3, 4), (2, 5), (1, 6), (5, 4), (5, 6)]),
# ('space invader metroid', [(4, 1), (2, 3), (3, 5), (5, 6), (7, 6), (7, 3), (2, 8), (3, 2), (1, 7), (1, 4), (6, 2),
# (8, 5), (7, 8), (4, 6), (8, 7), (5, 1), (2, 6), (8, 4), (1, 5), (6, 5)]),
('screw attack', [(2, 7), (7, 4), (6, 1), (3, 7), (2, 4), (5, 6), (4, 2), (5, 4), (3, 6), (7, 1), (3, 3), (6, 4),
(4, 6), (4, 3), (6, 2), (3, 4), (6, 5), (5, 2), (4, 5), (4, 4), (5, 3), (5, 5)]),
('vanilla wrong order', [(7, 2), (7, 4), (7, 5), (7, 7), (6, 3), (6, 5), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6),
(4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (3, 3), (3, 5), (2, 2), (2, 4), (2, 5), (2, 7)]),
('tile shaped tiles', [(2, 7), (2, 6), (2, 5), (2, 4), (2, 3), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2),
(7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (6, 7), (5, 7), (4, 7), (3, 7), (4, 5), (5, 4)]),
# ('panda shocked emoji', [(7, 3), (7, 4), (3, 3), (3, 4), (5, 5), (5, 6), (5, 7), (4, 6), (4, 7), (6, 7), (6, 6),
# (8, 6), (8, 7), (8, 8), (2, 8), (2, 7), (2, 6), (2, 1), (3, 1), (7, 1), (8, 1), (5, 8)]),
('JK', [(1, 5), (3, 4), (5, 3), (7, 3), (3, 2), (7, 5), (6, 4), (2, 6), (5, 6), (5, 5), (8, 2), (3, 5), (8, 6),
(5, 2), (3, 3), (5, 4)]),
('dollar sign', [(6, 2), (5, 1), (4, 1), (3, 1), (2, 2), (2, 6), (3, 7), (4, 7), (5, 7), (6, 6), (2, 3), (6, 5),
(3, 4), (5, 4), (4, 4), (4, 0), (4, 2), (4, 3), (4, 5), (4, 6), (4, 8)]),
# ('rupee diagonal', [(1, 4), (1, 5), (1, 6), (1, 7), (2, 7), (3, 7), (4, 7), (5, 6), (6, 5), (7, 4), (7, 3),
# (7, 2), (7, 1), (6, 1), (5, 1), (4, 1), (3, 2), (2, 3), (3, 5), (4, 4), (5, 3)]),
# ('sword', [(1, 8), (8, 1), (8, 2), (1, 4), (7, 1), (5, 8), (7, 3), (2, 4), (6, 2), (5, 7), (6, 4), (2, 5), (5, 3),
# (4, 7), (5, 5), (3, 6), (4, 4), (2, 7), (4, 6), (3, 5)]),
('z1 dungeon1', [(4, 6), (6, 4), (2, 4), (3, 7), (4, 3), (5, 7), (3, 2), (5, 4), (3, 5), (5, 5), (4, 2), (6, 3),
(4, 7), (3, 4), (7, 3), (4, 5), (4, 4)]),
# ('z1 dungeon 4', [(4, 8), (3, 6), (4, 4), (5, 2), (3, 1), (4, 3), (6, 2), (4, 1), (3, 3), (5, 4), (3, 5), (4, 7),
# (6, 1), (3, 8), (5, 7), (3, 2), (4, 6), (3, 4), (5, 1)]),
# ('LTTP', [(3, 4), (2, 3), (6, 2), (6, 4), (2, 2), (5, 2), (6, 3), (2, 4), (7, 2), (3, 6), (4, 8), (8, 7), (7, 6),
# (7, 8), (5, 6), (7, 7), (4, 6), (4, 7), (8, 6)]),
('triple triforce', [(4, 2), (3, 3), (4, 3), (5, 3), (6, 5), (5, 6), (6, 6), (7, 6), (3, 6), (2, 6), (2, 5),
(1, 6)]),
# ('TILE', [(2, 2), (5, 4), (3, 2), (2, 4), (5, 2), (2, 3), (1, 2), (5, 3), (3, 6), (6, 6), (7, 8), (8, 6), (6, 7),
# (3, 8), (4, 8), (3, 7), (6, 8), (7, 6), (7, 7), (8, 8)]),
# ('panda thinking emoji', [(2, 1), (3, 1), (6, 2), (7, 2), (6, 3), (3, 2), (3, 3), (3, 5), (4, 5), (5, 5), (2, 6),
# (2, 7), (1, 7), (2, 8), (1, 8), (3, 7), (4, 7), (3, 8)]),
# ('pokata key', [(3, 1), (4, 2), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5),
# (3, 2), (4, 3), (5, 4), (5, 6), (4, 7), (5, 8), (3, 4), (5, 2), (4, 1)]),
('tile shaped tiles randomish', [(4, 2), (2, 2), (7, 5), (5, 4), (3, 2), (4, 5), (4, 7), (7, 7), (2, 7), (2, 4),
(7, 2), (2, 5), (5, 7), (7, 4), (5, 2), (6, 2), (3, 7), (2, 3), (7, 6), (6, 7),
(7, 3), (2, 6)]),
('NO', [(1, 5), (4, 4), (3, 4), (6, 2), (8, 4), (1, 2), (8, 5), (7, 5), (1, 3), (6, 5), (6, 4), (1, 4), (4, 5),
(4, 2), (2, 3), (7, 2), (8, 2), (8, 3), (4, 3), (6, 3)]),
('bomb', [(3, 3), (5, 7), (6, 4), (2, 6), (5, 3), (3, 7), (6, 6), (2, 4), (7, 2), (2, 5), (4, 7), (5, 1), (4, 2),
(6, 5), (6, 1), (4, 3), (8, 2)]),
# ('boot', [(6, 7), (8, 6), (5, 5), (5, 3), (8, 2), (3, 6), (4, 8), (7, 8), (8, 4), (6, 2), (2, 7), (3, 8), (4, 5),
# (5, 4), (7, 2), (8, 7), (8, 8), (8, 3), (5, 8), (2, 8), (5, 2), (8, 5)]),
# ('javalogo', [(5, 8), (4, 8), (3, 8), (2, 8), (1, 7), (5, 6), (4, 6), (3, 6), (2, 6), (1, 5), (7, 7), (8, 6),
# (7, 5), (3, 4), (3, 3), (2, 2), (3, 1), (5, 4), (5, 3), (6, 2)]),
# ('pokata and ender key', [(3, 1), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5),
# (3, 2), (4, 7), (4, 1), (5, 8), (5, 2), (3, 4), (5, 6), (5, 4)]),
('kitty', [(3, 1), (6, 4), (7, 6), (8, 3), (1, 4), (3, 7), (3, 4), (2, 2), (5, 2), (6, 1), (6, 5), (6, 7), (2, 6),
(7, 2), (8, 5), (1, 3), (1, 5), (4, 2), (3, 5), (4, 6), (8, 4), (5, 6)]),
('creeper face', [(3, 7), (4, 5), (5, 4), (5, 6), (3, 3), (2, 2), (3, 2), (7, 3), (6, 7), (4, 6), (6, 3), (7, 2),
(2, 3), (4, 4), (3, 6), (6, 2), (6, 6), (5, 5)]),
('fast', [(7, 7)])
]
banned_tiles = {
# these are pots in tower of hera, only ban if pottery is on?
(1, 8), (1, 7), (3, 8), (5, 8), (7, 8),
# these are crystal barrier tiles
# (8, 3), (8, 4), (8, 5) # should I ban these too? bypasses barrier logic? regex \(8, [345]\)
}
+1
View File
@@ -0,0 +1 @@
# do nothing, just exist to make "source" package
+304
View File
@@ -0,0 +1,304 @@
# From: https://spannerisms.github.io/damage
# There are 16 damage classes, each with 8 subclasses.
# These subclasses determine the damage or effect inflicted on a sprite.
# Subclass data is stored as a table in ROM at $0D:B8F1
# DamageSource and SubClassTable
#_0DB8F1: db $00, $01, $20, $FF, $FC, $FB, $00, $00 ; 0x00 - Boomerang
#_0DB8F9: db $00, $02, $40, $04, $00, $00, $00, $00 ; 0x01 - Sword 1
#_0DB901: db $00, $04, $40, $02, $03, $00, $00, $00 ; 0x02 - Sword 2
#_0DB909: db $00, $08, $40, $04, $00, $00, $00, $00 ; 0x03 - Sword 3
#_0DB911: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x04 - Sword 4
#_0DB919: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x05 - Sword 5
#_0DB921: db $00, $04, $40, $10, $00, $00, $00, $00 ; 0x06 - Arrow
#_0DB929: db $00, $FF, $40, $FF, $FC, $FB, $00, $00 ; 0x07 - Hookshot
#_0DB931: db $00, $04, $40, $FF, $FC, $FB, $20, $00 ; 0x08 - Bomb
#_0DB939: db $00, $64, $18, $64, $00, $00, $00, $00 ; 0x09 - Silver arrow
#_0DB941: db $00, $F9, $FA, $FF, $64, $00, $00, $00 ; 0x0A - Powder
#_0DB949: db $00, $08, $40, $FD, $04, $10, $00, $00 ; 0x0B - Fire rod
#_0DB951: db $00, $08, $40, $FE, $04, $00, $00, $00 ; 0x0C - Ice rod
#_0DB959: db $00, $10, $40, $FD, $00, $00, $00, $00 ; 0x0D - Bombos
#_0DB961: db $00, $FE, $40, $10, $00, $00, $00, $00 ; 0x0E - Ether
#_0DB969: db $00, $20, $40, $FF, $00, $00, $00, $FA ; 0x0F - Quake
# Special Values:
# $F9 Target becomes a faerie
# $FA Target becomes a blob
# $FB Target stunned for 32 frames
# $FC Target stunned for 128 frames
# $FD Target incinerated
# $FE Target becomes frozen
# $FF Target stunned for 255 frames
DamageSource:
Boomerang:
class: 0x00
subclass: [0x00, 0x01, 0x20, 0xFF, 0xFC, 0xFB, 0x00, 0x00]
Sword1:
class: 0x01
subclass: [0x00, 0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00]
Sword2:
class: 0x02
subclass: [0x00, 0x04, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00]
Sword3:
class: 0x03
subclass: [0x00, 0x08, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00]
Sword4:
class: 0x04
subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00]
Sword5:
class: 0x05
subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00]
Arrow:
class: 0x06
subclass: [0x00, 0x04, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00]
Hookshot:
class: 0x07
subclass: [0x00, 0xFF, 0x40, 0xFF, 0xFC, 0xFB, 0x00, 0x00]
Bomb:
class: 0x08
subclass: [0x00, 0x04, 0x40, 0xFF, 0xFC, 0xFB, 0x20, 0x00]
SilverArrow:
class: 0x09
subclass: [0x00, 0x64, 0x18, 0x64, 0x00, 0x00, 0x00, 0x00]
Powder:
class: 0x0A
subclass: [0x00, 0xF9, 0xFA, 0xFF, 0x64, 0x00, 0x00, 0x00]
FireRod:
class: 0x0B
subclass: [0x00, 0x08, 0x40, 0xFD, 0x04, 0x10, 0x00, 0x00]
IceRod:
class: 0x0C
subclass: [0x00, 0x08, 0x40, 0xFE, 0x04, 0x00, 0x00, 0x00]
Bombos:
class: 0x0D
subclass: [0x00, 0x10, 0x40, 0xFD, 0x00, 0x00, 0x00, 0x00]
Ether:
class: 0x0E
subclass: [0x00, 0xFE, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00]
Quake:
class: 0x0F
subclass: [0x00, 0x20, 0x40, 0xFF, 0x00, 0x00, 0x00, 0xFA]
# The subclass that each sprite should look for in each damage class is in a table in WRAM at $7F:6000
# Rando migrated it to $31:C800
SubClassTable:
0x0: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1]
0x1: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1]
0x2: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x8: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7]
0x9: [0, 1, 3, 3, 3, 3, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0]
0xA: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7]
0xB: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 0, 0, 0]
0xC: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1]
0xD: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3]
0xE: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7]
0xF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 3, 2]
0x10: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 3, 1]
0x11: [4, 1, 1, 1, 1, 2, 1, 0, 2, 1, 0, 1, 3, 3, 1, 7]
0x12: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 3, 1, 7]
0x13: [0, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 0, 0, 3, 2, 7]
0x14: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x15: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
0x16: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x17: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 7]
0x18: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 1, 3, 3, 7]
0x19: [1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, 3, 2, 3]
0x1A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x1B: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 3, 2]
0x1C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x1D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x1E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x1F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x20: [3, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 3, 3, 1, 7]
0x21: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x22: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7]
0x23: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3]
0x24: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3]
0x25: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x26: [0, 1, 1, 1, 1, 1, 0, 3, 3, 1, 0, 0, 0, 3, 1, 3]
0x27: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7]
0x28: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x29: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x2A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
0x2B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x2C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x2D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x2E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x2F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x30: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x31: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x32: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x33: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x34: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x35: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x36: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x37: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x38: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x39: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x3E: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 7]
0x3F: [0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]
0x40: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x41: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 7]
0x42: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7]
0x43: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7]
0x44: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7]
0x45: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7]
0x46: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7]
0x47: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 7]
0x48: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7]
0x49: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 2, 7]
0x4A: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7]
0x4B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7]
0x4C: [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 3, 3, 3]
0x4D: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 2, 3, 2, 3]
0x4E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7]
0x4F: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7]
0x50: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x51: [3, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 3, 3, 3, 3]
0x52: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x53: [1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 1, 1, 0, 0, 0]
0x54: [0, 1, 3, 3, 3, 3, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0]
0x55: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1]
0x56: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1]
0x57: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x58: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 3, 3, 1, 7]
0x59: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x5A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x5B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3]
0x5C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3]
0x5D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x5E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x5F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x60: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x61: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x62: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x63: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 3, 3, 3] # the pit spawns the 0x64
0x64: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 3, 3, 3]
0x65: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x66: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x67: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x68: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x69: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x6A: [5, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7]
0x6B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]
0x6C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x6D: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7]
0x6E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7]
0x6F: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 3]
0x70: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x71: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 0, 3, 3, 3, 1, 3]
0x72: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x73: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x74: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x75: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x76: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x77: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x78: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x79: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1]
0x7A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x7B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x7C: [0, 1, 1, 1, 1, 1, 1, 0, 2, 1, 0, 3, 3, 3, 3, 3]
0x7D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x7E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x7F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x80: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x81: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 2, 3, 2]
0x82: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x83: [0, 1, 1, 2, 2, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0]
0x84: [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
0x85: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 2, 3]
0x86: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 3, 3, 1, 7]
0x87: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x88: [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0] # mothula
0x89: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x8A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x8B: [3, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 3, 3, 3, 2, 3]
0x8C: [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0]
0x8D: [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0]
0x8E: [1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 1, 3, 2, 2, 3]
0x8F: [3, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 3, 3, 3, 1, 2]
0x90: [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 3, 3, 2]
0x91: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x92: [0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0]
0x93: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x94: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 2, 3]
0x95: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x96: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x97: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x98: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x99: [1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1, 0, 3, 1, 2]
0x9A: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 2, 3, 2, 1, 1]
0x9B: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 2, 3, 2]
0x9C: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2]
0x9D: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2]
0x9E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0x9F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xA0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xA1: [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0]
0xA2: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0]
0xA3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0]
0xA4: [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 3, 0, 1, 3, 1]
0xA5: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3]
0xA6: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3]
0xA7: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 2, 7]
0xA8: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1]
0xA9: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1]
0xAA: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 3]
0xAB: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0]
0xAC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xAD: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xAE: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xAF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB2: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1]
0xB3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xB9: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xBA: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xBB: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xBC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xBD: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0]
0xBE: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0]
0xBF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC3: [0, 1, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0]
0xC4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1]
0xC6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1]
0xC7: [0, 1, 1, 1, 1, 1, 1, 0, 1, 2, 0, 3, 1, 3, 1, 3]
0xC8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xC9: [5, 1, 1, 1, 1, 1, 3, 0, 2, 1, 0, 3, 3, 1, 3, 1]
0xCA: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xCB: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xCC: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]
0xCD: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0]
0xCE: [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xCF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 2, 1]
0xD0: [0, 0, 0, 1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0]
0xD1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2]
0xD2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7]
0xD3: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 7]
0xD4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xD5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xD6: [0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
0xD7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0]
0xEF: [0, 1, 1, 2, 2, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0]
0xF0: [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
+10
View File
@@ -0,0 +1,10 @@
0x00: [0x02, 0x01, 0x01]
0x01: [0x04, 0x04, 0x04]
0x02: [0x00, 0x00, 0x00]
0x03: [0x08, 0x04, 0x02]
0x04: [0x08, 0x08, 0x08]
0x05: [0x10, 0x08, 0x04]
0x06: [0x20, 0x10, 0x08]
0x07: [0x20, 0x18, 0x10]
0x08: [0x18, 0x10, 0x08] # Roller class damage
0x09: [0x40, 0x30, 0x18] # Ganon class damage - max 8 hearts
+88 -60
View File
@@ -17,9 +17,9 @@ UwGeneralDeny:
- [ 0x000e, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Entrance - Freezor"
- [ 0x000e, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Top Bari"
- [ 0x000e, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Middle Bari"
- [ 0x0016, 0, [ "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 1"
- [ 0x0016, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 2"
- [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Blue Bari"
- [ 0x0016, 0, [ "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit"] ] #"Swamp Palace - Pool - Zol 1"
- [ 0x0016, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Zol 2"
- [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Blue Bari"
- [ 0x0016, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 3"
- [ 0x0017, 5, [ "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Bumper Room - Fire Bar (Clockwise)"
- [ 0x0019, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Dark Maze - Kodongo 1"
@@ -49,10 +49,14 @@ UwGeneralDeny:
- [ 0x0027, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 3"
- [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]]
- [ 0x0027, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Kodongo 1"
- [ 0x0028, 4, [ "RollerVerticalUp" ] ] #"Swamp Palace - Entrance Ledge - Spike Trap"
- [0x0028, 0, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 1, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalUp"]] #"Swamp Palace - Entrance Ledge - Spike Trap"
- [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1"
- [ 0x002a, 3, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 2"
- [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ]]
- [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]]
- [ 0x002a, 6, [ "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 5"
- [ 0x002b, 5, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Fairies - Red Bari 2"
- [ 0x002e, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 1"
@@ -67,6 +71,7 @@ UwGeneralDeny:
- [ 0x0034, 4, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - West Wing - Zol"
- [ 0x0035, 6, [ "RollerHorizontalRight" ] ] #"Swamp Palace - West Lever - Stalfos 2"
- [ 0x0035, 9, [ "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Swamp Palace - West Lever - Blue Bari"
- [0x0036, 5, ["AntiFairyCircle", "Bumper"]]
- [ 0x0036, 7, [ "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Lobby - Hover 3"
- [ 0x0037, 7, [ "RollerHorizontalRight" ] ] #"Swamp Palace - Water 1 - Blue Bari"
- [ 0x0038, 4, [ "RollerHorizontalRight" ] ] #"Swamp Palace - Long Hall - Kyameron 2"
@@ -82,6 +87,10 @@ UwGeneralDeny:
- [ 0x003d, 10, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Spark (Clockwise) 1"
- [ 0x003d, 12, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Bunny Beam"
- [ 0x003d, 13, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Antifairy"
- [0x003e, 8, ["Wizzrobe"]]
- [0x003e, 9, ["Wizzrobe"]]
- [0x003e, 10, ["Wizzrobe"]]
- [0x003e, 11, ["Wizzrobe"]]
- [ 0x003f, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 1"
- [ 0x003f, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 2"
- [ 0x003f, 4, [ "Wizzrobe", "Statue", "Bumper", "BigSpike", "AntiFairyCircle"]] # Wizzrobes can't spawn on pots
@@ -90,12 +99,12 @@ UwGeneralDeny:
- [ 0x0041, 0, [ "RollerHorizontalLeft" ] ] #"Sewers - Dark Cactus - Rat 1"
- [ 0x0041, 1, [ "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Sewers - Dark Cactus - Rat 2"
- [ 0x0041, 2, [ "RollerVerticalUp" ] ] #"Sewers - Dark Cactus - Rat 3"
- [ 0x0042, 0, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 1"
- [ 0x0042, 1, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 2"
- [ 0x0042, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 3"
- [ 0x0042, 3, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 4"
- [ 0x0042, 4, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 5"
- [ 0x0042, 5, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 6"
- [ 0x0042, 0, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 1"
- [ 0x0042, 1, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 2"
- [ 0x0042, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 3"
- [ 0x0042, 3, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 4"
- [ 0x0042, 4, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 5"
- [ 0x0042, 5, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 6"
- [ 0x0044, 4, [ "RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Joke Room - Zol"
- [ 0x0044, 6, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "BigSpike" ] ] #"Thieves' Town - Joke Room - Red Bari"
- [ 0x0044, 8, [ "Statue", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Joke Room - Blue Bari 4"
@@ -125,7 +134,7 @@ UwGeneralDeny:
- [ 0x0052, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 2"
- [ 0x0053, 1, [ "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - Bridge - Beamos 1"
- [ 0x0053, 5, [ "RollerVerticalDown" ] ] #"Desert Palace - Popo Genocide - Popo TL"
- [ 0x0053, 7, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - Bridge - Popo 5"
- [ 0x0053, 7, ["Beamos", "AntiFairyCircle", "Bumper", "RollerVerticalUp", "RollerVerticalDown"]] #"Desert Palace - Bridge - Popo 5"
- [ 0x0055, 1, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Secret Passage Exit - Green Knife Guard 1"
- [ 0x0057, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [ 0x0057, 1, ["Statue"]] # Statue switch issues
@@ -139,8 +148,8 @@ UwGeneralDeny:
- [ 0x0057, 10, ["Statue"]] # Statue switch issues
- [ 0x0057, 11, ["Statue"]] # Statue switch issues
- [ 0x0057, 12, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock", "Statue"]] #"Skull Woods - Big Key Room - Gibdo 6"
- [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 1"
- [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 2"
- [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 1"
- [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 2"
- [ 0x0058, 0, ["Statue"]]
- [ 0x0058, 1, ["Statue"]]
- [ 0x0058, 2, ["Statue"]]
@@ -151,12 +160,13 @@ UwGeneralDeny:
- [ 0x0058, 8, ["Statue"]]
- [ 0x0059, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Mini Moldorm 1"
- [ 0x0059, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Mini Moldorm 2"
- [0x0059, 5, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]]
- [ 0x0059, 9, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Gibdo 1"
- [ 0x005e, 3, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Pit Trap - Big Spike Trap"
- [ 0x005e, 4, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ice Palace - Pit Trap - Fire Bar (Clockwise)"
- [ 0x005f, 0, [ "RollerVerticalDown", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari University - Blue Bari 1"
- [ 0x005f, 1, [ "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Ice Palace - Bari University - Blue Bari 2"
- [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Hyrule Castle - West - Blue Guard"
- [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Beamos" ] ] #"Hyrule Castle - West - Blue Guard"
- [ 0x0062, 0, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hyrule Castle - East - Blue Guard"
- [ 0x0064, 2, [ "Bumper" , "Beamos" ] ] #"Thieves' Town - Attic Hall Left - Keese 2"
- [ 0x0064, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
@@ -170,7 +180,7 @@ UwGeneralDeny:
- [ 0x0067, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2"
- [ 0x0067, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 1"
- [ 0x0067, 4, [ "AntiFairyCircle", "Bumper" ]]
- [ 0x0067, 5, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 3"
- [ 0x0067, 5, ["RollerVerticalDown", "Beamos"]] #"Skull Woods - Firebar Pits - Hardhat Beetle 3"
- [ 0x0067, 6, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 4"
- [ 0x0067, 7, [ "Beamos", "AntiFairyCircle", "Bumper", "BunnyBeam" ] ] #"Skull Woods - Firebar Pits - Fire Bar (Clockwise)"
- [ 0x006a, 0, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 1"
@@ -195,7 +205,7 @@ UwGeneralDeny:
- [ 0x007b, 7, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - DMs Room - Hardhat Beetle"
- [ 0x007c, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Counterclockwise)"
- [ 0x007c, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Spike Trap"
- [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)"
- [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)"
- [ 0x007c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Hardhat Beetle"
- [ 0x007d, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 1"
- [ 0x007d, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 2"
@@ -230,7 +240,7 @@ UwGeneralDeny:
- [ 0x008d, 9, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Tile Room - Fire Bar (Clockwise)"
- [ 0x008d, 10, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ganon's Tower - Tile Room - Blue Bari 1"
- [ 0x008d, 12, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ganon's Tower - Tile Room - Blue Bari 2"
- [ 0x008e, 2, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots
- [0x008e, 2, ["Wizzrobe", "Statue", "FirebarCW", "FirebarCCW"]] # Wizzrobes can't spawn on pots
- [ 0x0092, 8, [ "RollerVerticalUp", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Misery Mire - Dark Weave - Spike Trap"
- [ 0x0092, 9, [ "RollerHorizontalRight" ] ] #"Misery Mire - Dark Weave - Antifairy 3"
- [ 0x0092, 10, [ "RollerHorizontalLeft" ] ] #"Misery Mire - Dark Weave - Stalfos"
@@ -244,6 +254,7 @@ UwGeneralDeny:
- [ 0x0098, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 3"
- [ 0x0098, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 4"
- [ 0x0098, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 5"
- [0x0099, 8, ["RollerHorizontalLeft", "RollerHorizontalRight"]]
- [ 0x009b, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 1"
- [ 0x009b, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 2"
- [ 0x009b, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ]
@@ -288,7 +299,8 @@ UwGeneralDeny:
- [ 0x00b2, 9, [ "RollerVerticalUp" ] ] #"Misery Mire - Sluggula Cross - Sluggula BL"
- [ 0x00b3, 0, [ "RollerVerticalUp", "RollerHorizontalRight", "BigSpike", "SpikeBlock" ] ] #"Misery Mire - Spike Room - Stalfos 1"
- [ 0x00b3, 2, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "Bumper" ] ] #"Misery Mire - Spike Room - Beamos"
- [ 0x00b3, 3, [ "AntiFairyCircle", "Bumper" ] ] #"Misery Mire - Spike Room - Yomo Medusa"
- [ 0x00b3, 3, ["AntiFairyCircle", "Bumper" ]] #"Misery Mire - Spike Room - Yomo Medusa"
- [ 0x00b3, 4, ["AntiFairyCircle", "Bumper"]]
- [ 0x00b6, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Tile Room - Zol 1"
- [ 0x00b6, 8, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Tile Room - Zol 2"
- [ 0x00ba, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Dark Stalfos - Antifairy 1"
@@ -319,13 +331,13 @@ UwGeneralDeny:
- [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?)
- [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?)
- [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 1"
- [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper", "Chainchomp"]] #"Ice Palace - Over Boss - top - Red Bari 1"
- [ 0x00ce, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 2"
- [ 0x00ce, 3, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Over Boss - top - Statue"
- [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]]
- [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]]
- [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]]
- [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]]
- [ 0x00ce, 3, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Chainchomp"]] #"Ice Palace - Over Boss - top - Statue"
- [0x00ce, 4, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]]
- [0x00ce, 5, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]]
- [0x00ce, 6, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]]
- [0x00ce, 7, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]]
- [ 0x00d0, 0, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]]
- [ 0x00d0, 1, [ "AntiFairyCircle", "BigSpike", "Bumper"]]
- [ 0x00d0, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]]
@@ -334,7 +346,16 @@ UwGeneralDeny:
- [ 0x00d0, 7, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]]
- [ 0x00d0, 9, [ "AntiFairyCircle", "BigSpike", "Bumper"]]
- [ 0x00d0, 6, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] # Agahnims Tower - Dark Maze - Blue Guard 2
- [ 0x00d2, 8, [ "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Mire 2 - Popo BL"
- [0x00d2, 0, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 1, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 5, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 6, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 7, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x00d2, 8, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Mire 2 - Popo BL"
- [0x00d2, 9, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [ 0x00d5, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Eye Bridge - Hardhat Beetle"
- [ 0x00d8, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Red Eyegore L"
- [ 0x00d8, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Red Eyegore R"
@@ -347,6 +368,7 @@ UwGeneralDeny:
- [ 0x00d8, 8, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Kill Room 1 - Red Eyegore"
- [ 0x00d9, 1, [ "RollerHorizontalRight" ] ] #"Eastern Palace - Dodgeball - Green Eyegore 1"
- [ 0x00db, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [ 0x00db, 3, [ "Bumper" ] ] # Okay in vanilla
- [ 0x00dc, 2, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ]
- [ 0x00dc, 9, [ "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Thieves' Town - Grand Room SE - Fire Snake 2"
- [ 0x00df, 0, [ "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Paradox Cave - Top - Mini Moldorm 1"
@@ -381,6 +403,8 @@ UwGeneralDeny:
- [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x010b, 6, ["RollerHorizontalRight"]]
- [0x010c, 4, ["AntiFairyCircle"]]
- [0x010c, 5, ["AntiFairyCircle"]]
- [0x010c, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]]
- [0x010c, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]]
OwGeneralDeny:
@@ -397,11 +421,13 @@ OwGeneralDeny:
- [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
- [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
- [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
- [0x55, 6, ["BigSpike"]]
- [0x57, 5, ["RollerVerticalUp", "RollerVerticalDown"]]
- [0x5e, 0, ["Gibo"]] # kiki eating Gibo
- [0x5e, 1, ["Gibo"]] # kiki eating Gibo
- [0x5e, 1, ["Gibo", "RollerVerticalUp", "RollerVerticalDown"]] # kiki eating Gibo
- [0x5e, 2, ["Gibo"]] # kiki eating Gibo
- [0x5e, 3, ["Gibo"]] # kiki eating Gibo
- [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo
- [0x5e, 4, ["RollerVerticalUp", "RollerVerticalDown", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo
- [0x5e, 5, ["Gibo"]] # kiki eating Gibo
- [0x5e, 6, ["Gibo"]] # kiki eating Gibo
- [0x5e, 7, ["Gibo"]] # kiki eating Gibo
@@ -435,94 +461,97 @@ UwEnemyDrop:
- [0x00b0, 8, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
# the following are not allowed at certain pits (or on conveyors near pits)
# because they despawned or clipped away or immediately fell, etc
- [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x0044, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0044, 8, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0049, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x007b, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x007b, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
- [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x0095, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
- [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x0095, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]]
- [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x00af, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]]
- [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
- [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard",
- [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover",
"BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard",
"BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]]
# wizzrobe despawn issues - on pots/blocks - too close to some object
@@ -577,7 +606,6 @@ UwEnemyDrop:
- [0x009f, 5, ["Wizzrobe", "Stal"]]
- [0x00a1, 1, ["Wizzrobe"]]
- [0x00aa, 5, ["Wizzrobe"]]
- [0x00af, 0, ["Wizzrobe", "Stal"]]
- [0x00b0, 1, ["Wizzrobe"]]
- [0x00b0, 2, ["Wizzrobe"]]
- [0x00b2, 4, ["Wizzrobe"]]
@@ -608,7 +636,7 @@ UwEnemyDrop:
- [0x010c, 5, ["Wizzrobe"]]
# other mimic cave spots are in the rail section
# enemies that have problems with conveyors
# enemies that have problems with conveyors - add wizzrobes here - for collision?
- [0x003b, 0, ["GreenMimic", "RedMimic"]]
- [0x003b, 1, ["GreenMimic", "RedMimic"]]
- [0x003b, 2, ["GreenMimic", "RedMimic"]]
+206
View File
@@ -0,0 +1,206 @@
UW: # Total 94431
AntiFairy: 40 # 1.82885% raw:1727 15.28319%
AntiFairyCircle: 40 # 1.82885% raw:1727 15.28319%
ArmosStatue: 186 # 0.53796% raw:508 4.49558%
Babasu: 80 # 1.15428% raw:1090 9.64602%
BallNChain: 163 # 0.61526% raw:581 5.14159%
Beamos: 155 # 0.64703% raw:611 5.40708%
BigSpike: 40 # 1.82885% raw:1727 15.28319%
Blob: 87 # 1.15428% raw:1090 9.64602%
BlueArcher: 325 # 0.30816% raw:291 2.57522%
BlueBari: 49 # 2.02688% raw:1914 16.93805%
BlueGuard: 44 # 2.28527% raw:2158 19.09735%
BlueZazak: 294 # 0.33993% raw:321 2.84071%
BlueZirro: 180 # 0.55702% raw:526 4.65487%
BluesainBolt: 150 # 0.66821% raw:631 5.58407%
BombGuard: 163 # 0.61526% raw:581 5.14159%
Bumper: 55 # 1.82885% raw:1727 15.28319%
BunnyBeam: 19 # 5.40077% raw:5100 45.13274%
Buzzblob: 123 # 0.81223% raw:767 6.78761%
CannonTrooper: 163 # 0.61526% raw:581 5.14159%
Chainchomp: 238 # 0.42041% raw:397 3.51327%
Crab: 204 # 0.48925% raw:462 4.08850%
CricketRat: 212 # 0.47230% raw:446 3.94690%
Cucco: 120 # 0.54325% raw:513 4.53982%
Deadrock: 186 # 0.53796% raw:508 4.49558%
Debirando: 154 # 0.65127% raw:615 5.44248%
DebirandoPit: 154 # 0.65127% raw:615 5.44248%
Faerie: 19
FakeMasterSword: 120 # 0.81223% raw:767 6.78761%
FireballZora: 165 # 0.60679% raw:573 5.07080%
FirebarCCW: 49 # 2.02688% raw:1914 16.93805%
FirebarCW: 49 # 2.02688% raw:1914 16.93805%
Firesnake: 49 # 2.02688% raw:1914 16.93805%
FloatingSkull: 49 # 2.02688% raw:1914 16.93805%
FloppingFish: 30 # 5.40077% raw:5100 45.13274%
Freezor: 454 # 0.22027% raw:208 1.84071%
Geldman: 445 # 0.22450% raw:212 1.87611%
Gibdo: 150 # 0.40453% raw:382 3.38053%
Gibo: 294 # 0.33993% raw:321 2.84071%
GreenBushGuard: 325 # 0.30816% raw:291 2.57522%
GreenEyegoreMimic: 175 # 0.57291% raw:541 4.78761%
GreenMimic: 175 # 0.57291% raw:541 4.78761%
GreenGuard: 55 # 1.80237% raw:1702 15.06195%
GreenKnifeGuard: 621 # 0.16096% raw:152 1.34513%
GreenZirro: 180 # 0.55702% raw:526 4.65487%
HardhatBeetle: 109 # 0.91495% raw:864 7.64602%
Hinox: 140 # 0.71269% raw:673 5.95575%
Hoarder: 123 # 0.81223% raw:767 6.78761%
Hoarder2: 123 # 0.81223% raw:767 6.78761%
Hover: 150 # 0.42147% raw:398 3.52212%
Keese: 212 # 0.47230% raw:446 3.94690%
Kodongo: 463 # 0.21603% raw:204 1.80531%
Kyameron: 237 # 0.42147% raw:398 3.52212%
Landmine: 19 # 5.40077% raw:5100 45.13274%
Leever: 154 # 0.65127% raw:615 5.44248%
Lynel: 400 # 0.17897% raw:169 1.49558%
MiniHelmasaur: 109 # 0.91495% raw:864 7.64602%
MiniMoldorm: 109 # 0.91495% raw:864 7.64602%
Moblin: 367 # 0.27216% raw:257 2.27434%
Octoballoon: 204 # 0.48925% raw:462 4.08850%
Octorok: 165 # 0.60679% raw:573 5.07080%
Octorok4Way: 204 # 0.48925% raw:462 4.08850%
Pengator: 300 # 0.22027% raw:208 1.84071%
Pikit: 180 # 0.55702% raw:526 4.65487%
Poe: 284 # 0.35264% raw:333 2.94690%
Pokey: 238 # 0.42041% raw:397 3.51327%
Popo: 155 # 0.64703% raw:611 5.40708%
Popo2: 155 # 0.64703% raw:611 5.40708%
Raven: 94 # 1.06109% raw:1002 8.86726%
RedBari: 49 # 2.02688% raw:1914 16.93805%
RedBushGuard: 163 # 0.61526% raw:581 5.14159%
RedEyegoreMimic: 175 # 0.57291% raw:541 4.78761%
RedMimic: 175 # 0.57291% raw:541 4.78761%
RedJavelinGuard: 163 # 0.61526% raw:581 5.14159%
RedSpearGuard: 44 # 2.28527% raw:2158 19.09735%
RedZazak: 294 # 0.33993% raw:321 2.84071%
RollerHorizontalLeft: 238 # 0.42041% raw:397 3.51327%
RollerHorizontalRight: 238 # 0.42041% raw:397 3.51327%
RollerVerticalDown: 238 # 0.42041% raw:397 3.51327%
RollerVerticalUp: 238 # 0.42041% raw:397 3.51327%
Ropa: 140 # 0.71269% raw:673 5.95575%
Sluggula: 360 # 0.27745% raw:262 2.31858%
Snake: 212 # 0.47230% raw:446 3.94690%
Snapdragon: 562 # 0.17791% raw:168 1.48673%
SparkCCW: 49 # 2.02688% raw:1914 16.93805%
SparkCW: 49 # 2.02688% raw:1914 16.93805%
SpikeBlock: 55 # 1.82885% raw:1727 15.28319%
Stal: 19 # 5.40077% raw:5100 45.13274%
Stalfos: 49 # 2.02688% raw:1914 16.93805%
StalfosKnight: 87 # 1.15428% raw:1090 9.64602%
Statue: 30 # 1.82885% raw:1727 15.28319%
Swamola: 402 # 0.24886% raw:235 2.07965%
Tektite: 186 # 0.53796% raw:508 4.49558%
Terrorpin: 463 # 0.21603% raw:204 1.80531%
Thief: 100 # 0.60997% raw:576 5.09735%
Toppo: 123 # 0.81223% raw:767 6.78761%
UsainBolt: 44 # 2.28527% raw:2158 19.09735%
Vulture: 445 # 0.22450% raw:212 1.87611%
Wallmaster: 247 # 0.40453% raw:382 3.38053%
Wizzrobe: 306 # 0.32722% raw:309 2.73451%
# YellowStalfos: 49
Zora: 609 # 0.16414% raw:155 1.37168%
Zoro: 80 # 1.15428% raw:1090 9.64602%
OW: # Total 117724
AntiFairy: 60 # 0.97346% raw:1146 10.14159%
AntiFairyCircle: 60 # 0.97346% raw:1146 10.14159%
ArmosStatue: 144 # 0.69570% raw:819 7.24779%
Babasu: 100 # 0.84605% raw:996 8.81416%
BallNChain: 121 # 0.82651% raw:973 8.61062%
Beamos: 184 # 0.54364% raw:640 5.66372%
BigSpike: 70 # 0.97346% raw:1146 10.14159%
Blob: 118 # 0.84605% raw:996 8.81416%
BlueArcher: 291 # 0.34403% raw:405 3.58407%
BlueBari: 47 # 2.11767% raw:2493 22.06195%
BlueGuard: 39 # 2.56617% raw:3021 26.73451%
BlueZazak: 247 # 0.40519% raw:477 4.22124%
BlueZirro: 158 # 0.63369% raw:746 6.60177%
BluesainBolt: 111 # 0.90381% raw:1064 9.41593%
BombGuard: 121 # 0.82651% raw:973 8.61062%
Bumper: 103 # 0.97346% raw:1146 10.14159%
BunnyBeam: 19 # 5.26656% raw:6200 54.86726%
Buzzblob: 86 # 1.15609% raw:1361 12.04425%
CannonTrooper: 121 # 0.82651% raw:973 8.61062%
Chainchomp: 226 # 0.44341% raw:522 4.61947%
Crab: 163 # 0.61500% raw:724 6.40708%
CricketRat: 236 # 0.42387% raw:499 4.41593%
Cucco: 140 # 0.60396% raw:711 6.29204%
Deadrock: 144 # 0.69570% raw:819 7.24779%
Debirando: 164 # 0.60905% raw:717 6.34513%
DebirandoPit: 164 # 0.60905% raw:717 6.34513%
Faerie: 19
FakeMasterSword: 120 # 1.15609% raw:1361 12.04425%
FireballZora: 119 # 0.84350% raw:993 8.78761%
FirebarCCW: 47 # 2.11767% raw:2493 22.06195%
FirebarCW: 47 # 2.11767% raw:2493 22.06195%
Firesnake: 47 # 2.11767% raw:2493 22.06195%
FloatingSkull: 47 # 2.11767% raw:2493 22.06195%
FloppingFish: 30 # 5.26656% raw:6200 54.86726%
Freezor: 692 # 0.14441% raw:170 1.50442%
Geldman: 229 # 0.43746% raw:515 4.55752%
Gibdo: 300 # 0.15545% raw:183 1.61947%
Gibo: 247 # 0.40519% raw:477 4.22124%
GreenBushGuard: 291 # 0.34403% raw:405 3.58407%
GreenEyegoreMimic: 171 # 0.58527% raw:689 6.09735%
GreenMimic: 171 # 0.58527% raw:689 6.09735%
GreenGuard: 48 # 2.06330% raw:2429 21.49558%
GreenKnifeGuard: 589 # 0.16989% raw:200 1.76991%
GreenZirro: 158 # 0.63369% raw:746 6.60177%
HardhatBeetle: 113 # 0.88682% raw:1044 9.23894%
Hinox: 123 # 0.81462% raw:959 8.48673%
Hoarder: 86 # 1.15609% raw:1361 12.04425%
Hoarder2: 86 # 1.15609% raw:1361 12.04425%
Hover: 200 # 0.32449% raw:382 3.38053%
Keese: 236 # 0.42387% raw:499 4.41593%
Kodongo: 346 # 0.28881% raw:340 3.00885%
Kyameron: 308 # 0.32449% raw:382 3.38053%
Landmine: 19 # 5.26656% raw:6200 54.86726%
Leever: 164 # 0.60905% raw:717 6.34513%
Lynel: 325 # 0.30750% raw:362 3.20354%
MiniHelmasaur: 113 # 0.88682% raw:1044 9.23894%
MiniMoldorm: 113 # 0.88682% raw:1044 9.23894%
Moblin: 303 # 0.33043% raw:389 3.44248%
Octoballoon: 163 # 0.61500% raw:724 6.40708%
Octorok: 119 # 0.84350% raw:993 8.78761%
Octorok4Way: 163 # 0.61500% raw:724 6.40708%
Pengator: 400 # 0.14441% raw:170 1.50442%
Pikit: 158 # 0.63369% raw:746 6.60177%
Poe: 202 # 0.49608% raw:584 5.16814%
Pokey: 226 # 0.44341% raw:522 4.61947%
Popo: 184 # 0.54364% raw:640 5.66372%
Popo2: 184 # 0.54364% raw:640 5.66372%
Raven: 65 # 1.53325% raw:1805 15.97345%
RedBari: 47 # 2.11767% raw:2493 22.06195%
RedBushGuard: 121 # 0.82651% raw:973 8.61062%
RedEyegoreMimic: 171 # 0.58527% raw:689 6.09735%
RedMimic: 171 # 0.58527% raw:689 6.09735%
RedJavelinGuard: 121 # 0.82651% raw:973 8.61062%
RedSpearGuard: 39 # 2.56617% raw:3021 26.73451%
RedZazak: 247 # 0.40519% raw:477 4.22124%
RollerHorizontalLeft: 226 # 0.44341% raw:522 4.61947%
RollerHorizontalRight: 226 # 0.44341% raw:522 4.61947%
RollerVerticalDown: 226 # 0.44341% raw:522 4.61947%
RollerVerticalUp: 226 # 0.44341% raw:522 4.61947%
Ropa: 123 # 0.81462% raw:959 8.48673%
Sluggula: 443 # 0.22595% raw:266 2.35398%
Snake: 236 # 0.42387% raw:499 4.41593%
Snapdragon: 526 # 0.19028% raw:224 1.98230%
SparkCCW: 47 # 2.11767% raw:2493 22.06195%
SparkCW: 47 # 2.11767% raw:2493 22.06195%
SpikeBlock: 103 # 0.97346% raw:1146 10.14159%
Stal: 19 # 5.26656% raw:6200 54.86726%
Stalfos: 47 # 2.11767% raw:2493 22.06195%
StalfosKnight: 118 # 0.84605% raw:996 8.81416%
Statue: 60 # 0.97346% raw:1146 10.14159%
Swamola: 265 # 0.37715% raw:444 3.92920%
Tektite: 144 # 0.69570% raw:819 7.24779%
Terrorpin: 346 # 0.28881% raw:340 3.00885%
Thief: 100 # 0.39244% raw:462 4.08850%
Toppo: 86 # 1.15609% raw:1361 12.04425%
UsainBolt: 39 # 2.56617% raw:3021 26.73451%
Vulture: 229 # 0.43746% raw:515 4.55752%
Wallmaster: 643 # 0.15545% raw:183 1.61947%
Wizzrobe: 345 # 0.28966% raw:341 3.01770%
# YellowStalfos: 47
Zora: 558 # 0.17923% raw:211 1.86726%
Zoro: 100 # 0.84605% raw:996 8.81416%
+168
View File
@@ -0,0 +1,168 @@
SheetChoices:
# Complex
- slots: [0, 1]
assignments:
0: 0x46
1: 0xd
weight: .5 # BluesainBolt(1/2) - 2 types
- slots: [0, 1]
assignments:
0: 0x46
1: 0x49
weight: 5.5 # CannonTrooper, BallNChain, RedBushGuard, RedJavelinGuard, BombGuard, BluesainBolt(1/2)
- slots: [0, 1]
assignments:
0: 0x48
1: 0x49
weight: 2 # GreenBushGuard, BlueArcher
- slots: [1, 2]
assignments:
1: 0x49
2: 0x13
weight: 1 # GreenKnifeGuard
- slots: [0, 2]
assignments:
0: 0x16
2: 0x17
weight: 1 # Snapdragon
- slots: [2, 3]
assignments:
2: 0xc
3: 0x44
weight: 1 # Zora (walking)
# Slot 0 (21 enemy types require slot 0)
- slots: [0]
assignments:
0: [0xe, 0x15]
weight: 1 # Thief
- slots: [0]
assignments:
0: 0x16
weight: 2 # Ropa, Hinox
- slots: [0]
assignments:
0: 0x1f
weight: 7 # Sparks, Firebars, FloatingSkull, RedBari, BlueBari, Firesnake, Stalfos, ~~YellowStalfos~~
- slots: [0]
assignments:
0: 0x2f
weight: 2 # Debirandos, Leever
# Slot 1 (24 enemy types require slot 1)
- slots: [1]
assignments:
1: 0x1e
weight: 3 # MiniMoldorm, MiniHelmasaur, Hardhat
- slots: [1]
assignments:
1: 0x20
weight: 3 # StalfosKnight, Blob, Babasus
- slots: [1]
assignments:
1: 0x23
weight: 1 # Wallmaster
- slots: [1]
assignments:
1: 0x2c
weight: 4 # Beamos, Popos, Mimics (2)
- slots: [1]
assignments:
1: 0x49
weight: 2.5 # GreenGuard, RedSpearGuard, BlueGuard, UsainBolt (1/2)
- slots: [1]
assignments:
1: 0xd
weight: 1.5 # RedSpearGuard, BlueGuard, UsainBolt (1/2)
# Slot 2 (29 enemy types require slot 2)
- slots: [2]
assignments:
2: 0xc
weight: 3 # Crab, Octoballon, FireballZora(1/2), Octorocks(1/2) (4Way is 0xC only?)
- slots: [2]
assignments:
2: 0x12
weight: 2 # Vulture, Geldman
- slots: [2]
assignments:
2: 0x17
weight: 1 # Moblin
- slots: [2]
assignments:
2: 0x18
weight: 1 # FireballZora(1/2) (note: immersible only), Octoroks(1/2)
- slots: [2]
assignments:
2: [0x1c, 0x24]
weight: 3 # Keese, CricketRat, Snake
- slots: [2]
assignments:
2: 0x22
weight: 2 # Kyameron (note: immersible only), Hover
- slots: [2]
assignments:
2: 0x23
weight: 1 # Gibdo
- slots: [2]
assignments:
2: 0x25
weight: 1.5 # Sluggula, Wizzrobe (half)
- slots: [2]
assignments:
2: 0x26
weight: 1 # Pengator
- slots: [2]
assignments:
2: 0x27
weight: 3 # Rollers4, Chainchomp, Pokey
- slots: [2]
assignments:
2: 0x28
weight: 3 # Gibo, BlueZazak, RedZazak
- slots: [2]
assignments:
2: 0x29
weight: .5 # Wizzrobe (half)
- slots: [2]
assignments:
2: 0x2a
weight: 2 # Kodongo, Terrorpin
- slots: [2]
assignments:
2: 0x2e
weight: 2 # GreenEyeGoreMimic, RedEyeGoreMimic (Eyegores only)
# Slot 3 (21 enemy types require slot 3)
- slots: [3]
assignments:
3: 0x10
weight: 3 # Deadrock, Tektite, ArmosStatue
- slots: [3]
assignments:
3: 0x11
weight: 4.5 # FakeMasterSword, Hoarder, Buzzblob, Toppo, Raven(1/2)
- slots: [3]
assignments:
3: 0x14
weight: 1 # Lynel
- slots: [3]
assignments:
3: 0x15
weight: 1.5 # Poe, Cucco(1/2)
- slots: [3]
assignments:
3: 0x19
weight: 1.5 # Swamola, Raven(1/2)
- slots: [3]
assignments:
3: 0x1b
weight: 3 # GreenZirro, BlueZirro, Pikit
- slots: [3]
assignments:
3: [0x50]
weight: .5 # Cucco(1/2)
- slots: [3]
assignments:
3: [0x52, 0x53]
weight: 5 # SpikeBlock, Bumper, BigSpike, AntiFairy, Statue (AntiFairyCircle?)
+59 -59
View File
@@ -1,4 +1,4 @@
from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT, X
from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, LEFT, RIGHT, X
from argparse import Namespace
import logging
import os
@@ -79,65 +79,68 @@ def bottom_frame(self, parent, args=None):
guiargs = create_guiargs(parent)
# get default values for missing parameters
for k,v in vars(parse_cli(['--multi', str(guiargs.multi)])).items():
cliargs = ['--multi', str(guiargs.multi)]
if hasattr(guiargs, 'customizer'):
cliargs.extend(['--customizer', str(guiargs.customizer)])
for k,v in vars(parse_cli(cliargs)).items():
if k not in vars(guiargs):
setattr(guiargs, k, v)
elif type(v) is dict: # use same settings for every player
setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)})
elif type(v) is dict: # use same settings for every player
players = guiargs.multi if len(v) == 0 else len(v)
setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, players + 1)})
argsDump = vars(guiargs)
hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"])
needEnemizer = False
if hasEnemizer:
falsey = ["none", "default", False, 0]
for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]:
if enemizerOption in argsDump:
if isinstance(argsDump[enemizerOption], dict):
for playerID,playerSetting in argsDump[enemizerOption].items():
if not playerSetting in falsey:
needEnemizer = True
elif not argsDump[enemizerOption] in falsey:
needEnemizer = True
seeds = []
if not needEnemizer or (needEnemizer and hasEnemizer):
try:
if guiargs.count is not None and guiargs.seed:
seed = guiargs.seed
for _ in range(guiargs.count):
seeds.append(seed)
main(seed=seed, args=guiargs, fish=parent.fish)
seed = random.randint(0, 999999999)
else:
if guiargs.seed:
seeds.append(guiargs.seed)
else:
random.seed(None)
guiargs.seed = random.randint(0, 999999999)
seeds.append(guiargs.seed)
main(seed=guiargs.seed, args=guiargs, fish=parent.fish)
except (FillError, EnemizerError, Exception, RuntimeError) as e:
logging.exception(e)
messagebox.showerror(title="Error while creating seed", message=str(e))
else:
YES = parent.fish.translate("cli","cli","yes")
NO = parent.fish.translate("cli","cli","no")
successMsg = ""
made = {}
for k in [ "rom", "playthrough", "spoiler" ]:
made[k] = parent.fish.translate("cli","cli","made." + k)
made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer")
for k in made:
v = made[k]
pattern = "([\w]+)(:)([\s]+)(.*)"
m = re.search(pattern,made[k])
made[k] = m.group(1) + m.group(2) + ' ' + m.group(4)
successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n"
successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n"
successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n"
successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n"
# FIXME: English
successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds)))
messagebox.showinfo(title="Success", message=successMsg)
needEnemizer = False
falsey = ["none", "default", False, 0]
for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]:
if enemizerOption in argsDump:
if isinstance(argsDump[enemizerOption], dict):
for playerID,playerSetting in argsDump[enemizerOption].items():
if not playerSetting in falsey:
needEnemizer = True
elif not argsDump[enemizerOption] in falsey:
needEnemizer = True
seeds = []
try:
if guiargs.count is not None and guiargs.seed:
seed = guiargs.seed
for _ in range(guiargs.count):
seeds.append(seed)
main(seed=seed, args=guiargs, fish=parent.fish)
seed = random.randint(0, 999999999)
else:
if guiargs.seed:
seeds.append(guiargs.seed)
else:
random.seed(None)
guiargs.seed = random.randint(0, 999999999)
seeds.append(guiargs.seed)
main(seed=guiargs.seed, args=guiargs, fish=parent.fish)
except (FillError, EnemizerError, Exception, RuntimeError) as e:
logging.exception(e)
messagebox.showerror(title="Error while creating seed", message=str(e))
else:
YES = parent.fish.translate("cli","cli","yes")
NO = parent.fish.translate("cli","cli","no")
successMsg = ""
made = {}
for k in [ "rom", "playthrough", "spoiler" ]:
made[k] = parent.fish.translate("cli","cli","made." + k)
made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer")
for k in made:
v = made[k]
pattern = "([\w]+)(:)([\s]+)(.*)"
m = re.search(pattern,made[k])
made[k] = m.group(1) + m.group(2) + ' ' + m.group(4)
successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n"
successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n"
successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n"
successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n"
# FIXME: English
successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds)))
messagebox.showinfo(title="Success", message=successMsg)
## Generate Button
# widget ID
@@ -273,9 +276,6 @@ def create_guiargs(parent):
if hasattr(pagewidgets[widget], 'storageVar'):
setattr(guiargs, arg, pagewidgets[widget].storageVar.get())
# Get EnemizerCLI setting
guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get()
# Get Multiworld Worlds count
guiargs.multi = int(parent.pages["bottom"].pages["content"].widgets["worlds"].storageVar.get())
-14
View File
@@ -80,20 +80,6 @@ def loadcliargs(gui, args, settings=None):
# If we've got a Game Options val and we don't have an Adjust val, use the Game Options val
gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg])
# Get EnemizerCLI setting
mainpage = "randomizer"
subpage = "enemizer"
widget = "enemizercli"
setting = "enemizercli"
# set storagevar
gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting])
# set textbox/frame label
label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget)
gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label)
# set get from web label
label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".online")
gui.pages[mainpage].pages[subpage].widgets[widget].pieces["online"].label.configure(text=label)
# Get baserom path
mainpage = "randomizer"
subpage = "generation"
+5 -47
View File
@@ -6,10 +6,7 @@ import webbrowser
from source.classes.Empty import Empty
def enemizer_page(parent,settings):
def open_enemizer_download(_evt):
webbrowser.open("https://github.com/Bonta0/Enemizer/releases")
# Enemizer
# Enemizer
self = ttk.Frame(parent)
# Enemizer options
@@ -29,7 +26,7 @@ def enemizer_page(parent,settings):
self.frames["selectOptionsFrame"].pack(fill=X)
self.frames["leftEnemizerFrame"].pack(side=LEFT)
self.frames["rightEnemizerFrame"].pack(side=RIGHT)
self.frames["bottomEnemizerFrame"].pack(fill=X)
self.frames["bottomEnemizerFrame"].pack(fill=X, padx=(12, 0))
# Load Enemizer option widgets as defined by JSON file
# Defns include frame name, widget type, widget options, widget placement attributes
@@ -43,47 +40,8 @@ def enemizer_page(parent,settings):
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
if framename == 'bottomEnemizerFrame':
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs)
## Enemizer CLI Path
# This one's more-complicated, build it and stuff it
# widget ID
widget = "enemizercli"
# Empty object
self.widgets[widget] = Empty()
# pieces
self.widgets[widget].pieces = {}
# frame
self.widgets[widget].pieces["frame"] = Frame(self.frames["bottomEnemizerFrame"])
# frame: label
self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="EnemizerCLI path: ")
self.widgets[widget].pieces["frame"].label.pack(side=LEFT)
# get app online
self.widgets[widget].pieces["online"] = Empty()
# get app online: label
self.widgets[widget].pieces["online"].label = Label(self.widgets[widget].pieces["frame"], text="(get online)", fg="blue", cursor="hand2")
self.widgets[widget].pieces["online"].label.pack(side=LEFT)
# get app online: open browser
self.widgets[widget].pieces["online"].label.bind("<Button-1>", open_enemizer_download)
# storage var
self.widgets[widget].storageVar = StringVar(value=settings["enemizercli"])
# textbox
self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar)
self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True)
def EnemizerSelectPath():
path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join("."))
if path:
self.widgets[widget].storageVar.set(path)
settings["enemizercli"] = path
# dialog button
self.widgets[widget].pieces["opendialog"] = Button(self.widgets[widget].pieces["frame"], text='...', command=EnemizerSelectPath)
self.widgets[widget].pieces["opendialog"].pack(side=LEFT)
# frame: pack
self.widgets[widget].pieces["frame"].pack(fill=X)
return self,settings
return self, settings
+3 -3
View File
@@ -287,9 +287,9 @@ def widget_command(widget, command=""):
temp_widget.storageVar.set('keys')
temp_widget = root.pages["randomizer"].pages["item"].widgets["dropshuffle"]
text_output += f'\n {temp_widget.checkbox.cget("text")}'
if temp_widget.storageVar.get() == 0:
temp_widget.storageVar.set(1)
text_output += f'\n {temp_widget.label.cget("text")}'
if temp_widget.storageVar.get() == 'none':
temp_widget.storageVar.set('keys')
if text_output:
messagebox.showinfo('', f'The following settings were changed:{text_output}')
+9 -9
View File
@@ -70,7 +70,7 @@ def create_item_pool_config(world):
for player in range(1, world.players + 1):
config.static_placement[player] = defaultdict(list)
config.static_placement[player].update(vanilla_mapping)
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
for item, locs in keydrop_vanilla_mapping.items():
config.static_placement[player][item].extend(locs)
if world.pottery[player] not in ['none', 'cave']:
@@ -96,7 +96,7 @@ def create_item_pool_config(world):
for item, locs in vanilla_mapping.items():
if 'Small Key' in item:
universal_key_locations.extend(locs)
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
for item, locs in keydrop_vanilla_mapping.items():
if 'Small Key' in item:
universal_key_locations.extend(locs)
@@ -129,11 +129,11 @@ def create_item_pool_config(world):
groups = LocationGroup('Major').locs(init_set)
if world.bigkeyshuffle[player]:
groups.locations.extend(mode_grouping['Big Keys'])
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Key Drops'])
if world.keyshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Small Keys'])
if world.dropshuffle[player]:
if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Key Drops'])
if world.pottery[player] not in ['none', 'cave']:
groups.locations.extend(mode_grouping['Pot Keys'])
@@ -357,7 +357,7 @@ def determine_major_items(world, player):
major_item_set.add('Single Arrow')
if world.keyshuffle[player] == 'universal':
major_item_set.add('Small Key (Universal)')
if world.goal[player] in ['triforcehunt', 'trinity', 'ganonhunt']:
if world.goal[player] in {'triforcehunt', 'ganonhunt', 'trinity'}:
major_item_set.add('Triforce Piece')
if world.bombbag[player]:
major_item_set.add('Bomb Upgrade (+10)')
@@ -416,11 +416,11 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion
return filtered
if world.algorithm == 'district':
config = world.item_pool_config
if ((isinstance(item_to_place,str) and item_to_place == 'Placeholder')
if ((isinstance(item_to_place, str) and item_to_place == 'Placeholder')
or item_to_place.name in config.item_pool[item_to_place.player]):
restricted = config.location_groups[0].locations
filtered = [l for l in locations if l.name in restricted and l.player in restricted[l.name]]
return filtered if len(filtered) > 0 else locations
return filtered
elif potion:
restricted = config.location_groups[0].locations
filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]]
@@ -439,14 +439,14 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion
return locations
def filter_pot_locations(locations, world):
def filter_special_locations(locations, world, vanilla_matcher):
if world.algorithm == 'district':
config = world.item_pool_config
restricted = config.location_groups[0].locations
filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]]
return filtered if len(filtered) > 0 else locations
if world.algorithm == 'vanilla_fill':
filtered = [l for l in locations if l.pot and l.pot.item in [PotItem.Chicken, PotItem.BigMagic]]
filtered = [l for l in locations if vanilla_matcher(l)]
return filtered if len(filtered) > 0 else locations
return locations
+576
View File
@@ -0,0 +1,576 @@
import itertools
from collections import OrderedDict
try:
from fast_enum import FastEnum
except ImportError:
from enum import IntFlag as FastEnum
from BaseClasses import CrystalBarrier, KeyRuleType
from Dungeons import dungeon_keys
class RuleType(FastEnum):
Conjunction = 0
Disjunction = 1
Item = 2
Glitch = 3
Reachability = 4
Static = 5
Bottle = 6
Crystal = 7
Barrier = 8
Hearts = 9
Unlimited = 10
ExtendMagic = 11
Boss = 12
Negate = 13
LocationCheck = 14
SmallKeyDoor = 15
class Rule(object):
def __init__(self, rule_type):
self.rule_type = rule_type
self.sub_rules = []
self.principal = None
self.player = 0
self.resolution_hint = None
self.barrier = None
self.flag = None
self.locations = []
self.count = 1
self.std_req = None
self.rule_lambda = lambda state: True
def eval(self, state):
return self.rule_lambda(state)
def get_requirements(self, progressive_flag=True):
if not self.std_req:
reqs = rule_requirements[self.rule_type](self, progressive_flag)
self.std_req = standardize_requirements(reqs, progressive_flag)
return self.std_req
def __str__(self):
return str(self.__unicode__())
def __unicode__(self):
return rule_prints[self.rule_type](self)
rule_prints = {
RuleType.Conjunction: lambda self: f'({" and ".join([str(x) for x in self.sub_rules])})',
RuleType.Disjunction: lambda self: f'({" or ".join([str(x) for x in self.sub_rules])})',
RuleType.Item: lambda self: f'has {self.principal}' if self.count == 1 else f'has {self.count} {self.principal}(s)',
RuleType.Reachability: lambda self: f'canReach {self.principal}',
RuleType.Static: lambda self: f'{self.principal}',
RuleType.Crystal: lambda self: f'has {self.principal} crystals',
RuleType.Barrier: lambda self: f'{self.barrier} @ {self.principal}',
RuleType.Hearts: lambda self: f'has {self.principal} hearts',
RuleType.Unlimited: lambda self: f'canBuyUnlimited {self.principal}',
RuleType.ExtendMagic: lambda self: f'magicNeeded {self.principal}',
RuleType.Boss: lambda self: f'canDefeat({self.principal.defeat_rule})',
RuleType.Negate: lambda self: f'not ({self.sub_rules[0]})',
RuleType.LocationCheck: lambda self: f'{self.principal} in [{", ".join(self.locations)}]',
RuleType.SmallKeyDoor: lambda self: f'doorOpen {self.principal[0]}:{self.principal[1]}'
}
def or_rule(rule1, rule2):
return lambda state: rule1(state) or rule2(state)
def and_rule(rule1, rule2):
return lambda state: rule1(state) and rule2(state)
class RuleFactory(object):
@staticmethod
def static_rule(boolean):
rule = Rule(RuleType.Static)
rule.principal = boolean
rule.rule_lambda = lambda state: boolean
return rule
@staticmethod
def conj(rules):
if len(rules) == 1:
return rules[0]
rule = Rule(RuleType.Conjunction)
rule_lambda = None
for r in rules:
if r is None:
continue
if r.rule_type == RuleType.Conjunction:
rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc
elif r.rule_type == RuleType.Static and r.principal: # remove static flag if unnecessary
continue
elif r.rule_type == RuleType.Static and not r.principal: # always evaluates to false
return r
else:
rule.sub_rules.append(r)
if not rule_lambda:
rule_lambda = r.rule_lambda
else:
rule_lambda = and_rule(rule_lambda, r.rule_lambda)
rule.rule_lambda = rule_lambda if rule_lambda else lambda state: True
return rule
@staticmethod
def disj(rules):
if len(rules) == 1:
return rules[0]
rule = Rule(RuleType.Disjunction)
rule_lambda = None
for r in rules:
if r is None:
continue
if r.rule_type == RuleType.Disjunction:
rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc
elif r.rule_type == RuleType.Static and not r.principal: # remove static flag if unnecessary
continue
elif r.rule_type == RuleType.Static and r.principal: # always evaluates to true
return r
else:
rule.sub_rules.append(r)
if not rule_lambda:
rule_lambda = r.rule_lambda
else:
rule_lambda = or_rule(rule_lambda, r.rule_lambda)
rule.rule_lambda = rule_lambda if rule_lambda else lambda state: True
return rule
@staticmethod
def item(item, player, count=1):
rule = Rule(RuleType.Item)
rule.principal = item
rule.player = player
rule.count = count
rule.rule_lambda = lambda state: state.has(item, player, count)
return rule
@staticmethod
def bottle(player):
rule = Rule(RuleType.Bottle)
rule.player = player
rule.rule_lambda = lambda state: state.has_bottle(player)
return rule
@staticmethod
def crystals(number, player):
rule = Rule(RuleType.Crystal)
rule.principal = number
rule.player = player
rule.rule_lambda = lambda state: state.has_crystals(number, player)
return rule
@staticmethod
def barrier(region, player, barrier):
rule = Rule(RuleType.Barrier)
rule.principal = region
rule.player = player
rule.barrier = barrier
rule.rule_lambda = lambda state: state.can_cross_barrier(region, player, barrier)
return rule
@staticmethod
def hearts(number, player):
rule = Rule(RuleType.Hearts)
rule.principal = number
rule.player = player
rule.rule_lambda = lambda state: state.has_hearts(number, player)
return rule
@staticmethod
def unlimited(item, player, shop_regions):
rule = Rule(RuleType.Unlimited)
rule.principal = item
rule.player = player
rule.locations = shop_regions # list of regions where said item can be bought
rule.rule_lambda = lambda state: state.can_buy_unlimited(item, player)
return rule
@staticmethod
def extend_magic(player, magic, difficulty, magic_potion_regions, flag):
rule = Rule(RuleType.ExtendMagic)
rule.principal = magic
rule.player = player
rule.resolution_hint = difficulty # world difficulty setting
rule.locations = magic_potion_regions # list of regions where blue/green can be bought
rule.flag = flag
rule.rule_lambda = lambda state: state.can_extend_magic(player, magic, flag)
return rule
@staticmethod
def boss(boss):
rule = Rule(RuleType.Boss)
rule.principal = boss
rule.rule_lambda = lambda state: boss.defeat_rule.eval(state)
return rule
@staticmethod
def neg(orig):
rule = Rule(RuleType.Negate)
rule.sub_rules.append(orig)
rule.rule_lambda = lambda state: not orig.rule_lambda(state)
return rule
@staticmethod
def check_location(item, location, player):
rule = Rule(RuleType.LocationCheck)
rule.principal = item
rule.location = location
rule.player = player
rule.rule_lambda = eval_location(item, location, player)
return rule
@staticmethod
def small_key_door(door_name, dungeon, player, door_rules):
rule = Rule(RuleType.SmallKeyDoor)
rule.principal = (door_name, dungeon)
rule.player = player
rule.resolution_hint = door_rules # door_rule object from KeyDoorShuffle
rule.rule_lambda = eval_small_key_door(door_name, dungeon, player)
return rule
def eval_location(item, location, player):
return lambda state: eval_location_main(item, location, player, state)
def eval_location_main(item, location, player, state):
location = state.world.get_location(location, player)
return location.item and location.item.name == item and location.player == player
def eval_small_key_door_main(state, door_name, dungeon, player):
if state.is_door_open(door_name, player):
return True
key_logic = state.world.key_logic[player][dungeon]
door_rule = key_logic.door_rules[door_name]
door_openable = False
for ruleType, number in door_rule.new_rules.items():
if door_openable:
return True
if ruleType == KeyRuleType.WorstCase:
door_openable |= state.has_sm_key(key_logic.small_key_name, player, number)
elif ruleType == KeyRuleType.AllowSmall:
if (door_rule.small_location.item and door_rule.small_location.item.name == key_logic.small_key_name
and door_rule.small_location.item.player == player):
return True # always okay if allow small is on
elif isinstance(ruleType, tuple):
lock, lock_item = ruleType
# this doesn't track logical locks yet, i.e. hammer locks the item and hammer is there, but the item isn't
for loc in door_rule.alternate_big_key_loc:
spot = state.world.get_location(loc, player)
if spot.item and spot.item.name == lock_item:
door_openable |= state.has_sm_key(key_logic.small_key_name, player, number)
break
return door_openable
def eval_small_key_door(door_name, dungeon, player):
return lambda state: eval_small_key_door_main(state, door_name, dungeon, player)
def conjunction_requirements(rule, f):
combined = [ReqSet()]
for r in rule.sub_rules:
result = r.get_requirements(f)
combined = merge_requirements(combined, result)
return combined
def disjunction_requirements(rule, f):
results = []
for r in rule.sub_rules:
result = r.get_requirements(f)
results.extend(result)
return results
rule_requirements = {
RuleType.Conjunction: conjunction_requirements,
RuleType.Disjunction: disjunction_requirements,
RuleType.Item: lambda rule, f: [ReqSet([Requirement(ReqType.Item, rule.principal, rule.player, rule, rule.count)])],
RuleType.Reachability: lambda rule, f: [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule)])],
RuleType.Static: lambda rule, f: static_req(rule),
RuleType.Crystal: lambda rule, f: crystal_requirements(rule),
RuleType.Bottle: lambda rule, f: [ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, 1)])],
RuleType.Barrier: lambda rule, f: barrier_req(rule),
RuleType.Hearts: lambda rule, f: empty_req(), # todo: the one heart container
RuleType.Unlimited: lambda rule, f: unlimited_buys(rule),
RuleType.ExtendMagic: lambda rule, f: magic_requirements(rule),
RuleType.Boss: lambda rule, f: rule.principal.defeat_rule.get_requirements(f),
RuleType.Negate: lambda rule, f: empty_req(), # ignore these and just don't flood the key too early
RuleType.LocationCheck: lambda rule, f: location_check(rule),
RuleType.SmallKeyDoor: lambda rule, f: small_key_reqs(rule)
}
avail_crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
def crystal_requirements(rule):
crystal_rules = map(lambda c: Requirement(ReqType.Item, c, rule.player, rule), avail_crystals)
combinations = itertools.combinations(crystal_rules, rule.principal)
counter_list = []
for combo in combinations:
counter_list.append(ReqSet(combo))
return counter_list
# todo: 1/4 magic
def magic_requirements(rule):
if rule.principal <= 8:
return [set()]
bottle_val = 1.0
if rule.resolution_hint == 'expert' and not rule.flag:
bottle_val = 0.25
elif rule.resolution_hint == 'hard' and not rule.flag:
bottle_val = 0.5
base, min_bot, reqs = 8, None, []
for i in range(1, 5):
if base + bottle_val*base*i >= rule.principal:
min_bot = i
break
if min_bot:
for region in rule.locations:
reqs.append(ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot),
Requirement(ReqType.Reachable, region, rule.player, rule)]))
if rule.principal <= 16:
reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1)]))
return reqs
else:
base, min_bot = 16, 4
for i in range(1, 5):
if base + bottle_val*base*i >= rule.principal:
min_bot = i
break
if min_bot:
for region in rule.locations:
reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1),
Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot),
Requirement(ReqType.Reachable, region, rule.player, rule)]))
return reqs
def static_req(rule):
return [ReqSet()] if rule.principal else [ReqSet([Requirement(ReqType.Item, 'Impossible', rule.player, rule)])]
def barrier_req(rule):
return [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule, crystal=rule.barrier)])]
def empty_req():
return [ReqSet()]
def location_check(rule):
return [ReqSet([Requirement(ReqType.Placement, rule.principal, rule.player, rule, locations=rule.locations)])]
def unlimited_buys(rule):
requirements = []
for region in rule.locations:
requirements.append(ReqSet([Requirement(ReqType.Reachable, region, rule.player, rule)]))
return requirements
def small_key_reqs(rule):
requirements = []
door_name, dungeon = rule.principal
key_name = dungeon_keys[dungeon]
for rule_type, number in rule.resolution_hint.new_rules.items():
if rule_type == KeyRuleType.WorstCase:
requirements.append(ReqSet([Requirement(ReqType.Item, key_name, rule.player, rule, number)]))
elif rule_type == KeyRuleType.AllowSmall:
small_loc = rule.resolution_hint.small_location.name
requirements.append(ReqSet([
Requirement(ReqType.Placement, key_name, rule.player, rule, locations=[small_loc]),
Requirement(ReqType.Item, key_name, rule.player, rule, number)]))
elif isinstance(rule_type, tuple):
lock, lock_item = rule_type
locs = [x.name for x in rule.resolution_hint.alternate_big_key_loc]
requirements.append(ReqSet([
Requirement(ReqType.Placement, lock_item, rule.player, rule, locations=locs),
Requirement(ReqType.Item, key_name, rule.player, rule, number)]))
return requirements
class ReqType(FastEnum):
Item = 0
Placement = 2
class ReqSet(object):
def __init__(self, requirements=None):
if requirements is None:
requirements = []
self.keyed = OrderedDict()
for r in requirements:
self.keyed[r.simple_key()] = r
def append(self, req):
self.keyed[req.simple_key()] = req
def get_values(self):
return self.keyed.values()
def merge(self, other):
new_set = ReqSet(self.get_values())
for r in other.get_values():
key = r.simple_key()
if key in new_set.keyed:
new_set.keyed[key] = max(r, new_set.keyed[key], key=lambda r: r.amount)
else:
new_set.keyed[key] = r
return new_set
def redundant(self, other):
for k, req in other.keyed.items():
if k not in self.keyed:
return False
elif self.keyed[k].amount < req.amount:
return False
return True
def different(self, other):
for key in self.keyed.keys():
if key not in other.keyed:
return True
if key in other.keyed and self.keyed[key].amount > other.keyed[key].amount:
return True
return False
def find_item(self, item_name):
for key, req in self.keyed.items():
if req.req_type == ReqType.Item and req.item == item_name:
return req
return None
def __eq__(self, other):
for key, req in self.keyed.items():
if key not in other.keyed:
return False
if req.amount != other.keyed[key].amount:
return False
for key in other.keyed:
if key not in self.keyed:
return False
return True
def __str__(self):
return str(self.__unicode__())
def __unicode__(self):
return " and ".join([str(x) for x in self.keyed.values()])
class Requirement(object):
def __init__(self, req_type, item, player, rule, amount=1, crystal=CrystalBarrier.Null, locations=()):
self.req_type = req_type
self.item = item
self.player = player
self.rule = rule
self.amount = amount
self.crystal = crystal
self.locations = tuple(locations)
def simple_key(self):
return self.req_type, self.item, self.player, self.crystal, self.locations
def key(self):
return self.req_type, self.item, self.player, self.amount, self.crystal, self.locations
def __eq__(self, other):
if isinstance(other, Requirement):
return self.key() == other.key()
return NotImplemented
def __hash__(self):
return hash(self.key())
def __str__(self):
return str(self.__unicode__())
def __unicode__(self):
if self.req_type == ReqType.Item:
return f'has {self.item}' if self.amount == 1 else f'has {self.amount} {self.item}(s)'
elif self.req_type == ReqType.Placement:
return f'{self.item} located @ {",".join(self.locations)}'
# requirement utility methods
def merge_requirements(starting_requirements, new_requirements):
merge = []
for req in starting_requirements:
for new_r in new_requirements:
merge.append(req.merge(new_r))
return reduce_requirements(merge)
only_one = {'Moon Pearl', 'Hammer', 'Blue Boomerang', 'Red Boomerang', 'Hookshot', 'Mushroom', 'Powder',
'Fire Rod', 'Ice Rod', 'Bombos', 'Ether', 'Quake', 'Lamp', 'Shovel', 'Ocarina', 'Bug Catching Net',
'Book of Mudora', 'Magic Mirror', 'Cape', 'Cane of Somaria', 'Cane of Byrna', 'Flippers', 'Pegasus Boots'}
def standardize_requirements(requirements, progressive_flag):
assert isinstance(requirements, list)
for req in requirements:
for thing in req.get_values():
if thing.item in only_one and thing.amount > 1:
thing.amount = 1
if progressive_flag:
substitute_progressive(req)
return reduce_requirements(requirements)
def reduce_requirements(requirements):
removals = []
reduced = list(requirements)
# subset manip
ttl = len(reduced)
for i in range(0, ttl - 1):
for j in range(i + 1, ttl):
req, other_req = reduced[i], reduced[j]
if req.redundant(other_req):
removals.append(req)
elif other_req.redundant(req):
removals.append(other_req)
for removal in removals:
if removal in reduced:
reduced.remove(removal)
assert len(reduced) != 0
return reduced
progress_sub = {
'Fighter Sword': ('Progressive Sword', 1),
'Master Sword': ('Progressive Sword', 2),
'Tempered Sword': ('Progressive Sword', 3),
'Golden Sword': ('Progressive Sword', 4),
'Power Glove': ('Progressive Glove', 1),
'Titans Mitts': ('Progressive Glove', 2),
'Bow': ('Progressive Bow', 1),
'Silver Arrows': ('Progressive Bow', 2),
'Blue Mail': ('Progressive Armor', 1),
'Red Mail': ('Progressive Armor', 2),
'Blue Shield': ('Progressive Shield', 1),
'Red Shield': ('Progressive Shield', 2),
'Mirror Shield': ('Progressive Shield', 3),
}
def substitute_progressive(req):
for item in req.get_values():
if item.item in progress_sub.keys():
item.item, item.amount = progress_sub[item.item]
+17 -5
View File
@@ -910,7 +910,7 @@ def must_exits_helper(avail):
}
for region_name in avail.world.inaccessible_regions[avail.player]:
if (avail.world.logic[avail.player] in ['noglitches', 'minorglitches'] and region_name in glitch_regions) \
or (region_name == 'Pyramid Exit Ledge' and (avail.world.shuffle[avail.player] != 'insanity' or avail.world.is_tile_swapped(0x1b, avail.player))) \
or (region_name == 'Pyramid Exit Ledge' and (avail.keep_drops_together or avail.world.is_tile_swapped(0x1b, avail.player))) \
or (region_name == 'Spiral Mimic Ledge Extend' and not avail.world.is_tile_swapped(0x05, avail.player)):
# removing irrelevant and resolved regions
inaccessible_regions.remove(region_name)
@@ -2561,9 +2561,13 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide
('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'),
('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'),
('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'),
('Old Man Cave Dropdown', 'Old Man Cave'),
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
('Old Man Cave Dropdown', 'Old Man Cave (East)'),
('Old Man Cave W', 'Old Man Cave (West)'),
('Old Man Cave E', 'Old Man Cave (East)'),
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'),
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'),
('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'),
('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'),
('Old Man House Front to Back', 'Old Man House Back'),
('Old Man House Back to Front', 'Old Man House'),
('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'),
@@ -2582,11 +2586,19 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide
('Sewer Drop', 'Sewers Rat Path'),
('Missing Smith', 'Missing Smith'),
('Bat Cave Door', 'Bat Cave (left)'),
('Good Bee Cave Front to Back', 'Good Bee Cave (back)'),
('Good Bee Cave Back to Front', 'Good Bee Cave'),
('Capacity Upgrade East', 'Capacity Fairy Pool'),
('Capacity Fairy Pool West', 'Capacity Upgrade'),
('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'),
('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'),
('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'),
('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'),
('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'),
('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'),
('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'),
('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'),
('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'),
('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'),
('Superbunny Cave Climb', 'Superbunny Cave (Top)'),
@@ -2606,7 +2618,7 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble',
'Lumberjack Tree Exit': 'Lumberjack Area',
'Death Mountain Return Cave (East)': 'Death Mountain Return Cave (right)',
'Death Mountain Return Cave Exit (East)': 'West Death Mountain (Bottom)',
'Old Man Cave (East)': 'Old Man Cave',
'Old Man Cave (East)': 'Old Man Cave (East)',
'Old Man Cave Exit (East)': 'West Death Mountain (Bottom)',
'Spectacle Rock Cave': 'Spectacle Rock Cave (Top)',
'Spectacle Rock Cave Exit (Top)': 'West Death Mountain (Bottom)',
+228
View File
@@ -0,0 +1,228 @@
from collections import defaultdict
from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_cached_yaml, pc_to_snes
from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats, EnemySprite
from source.dungeon.EnemyList import sprite_translation
from source.dungeon.RoomHeader import init_room_headers
from source.dungeon.RoomList import Room0127
from source.enemizer.OwEnemyList import init_vanilla_sprites_ow, vanilla_sprites_ow
from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements, SheetChoice
def convert_area_id_to_offset(area_id):
if area_id < 0x40:
return area_id
if 0x40 <= area_id < 0x80:
return area_id + 0x40
if 0x90 <= area_id <= 0xCF:
return area_id - 0x50
raise Exception(f'{hex(area_id)} is not a valid area id for offset math')
class DataTables:
def __init__(self):
self.room_headers = None
self.room_list = None
self.sprite_sheets = None
self.uw_enemy_table = None
self.ow_enemy_table = None
self.pot_secret_table = None
self.overworld_sprite_sheets = None
# associated data
self.sprite_requirements = None
self.room_requirements = None
self.enemy_stats = None
self.enemy_damage = None
self.bush_sprite_table = {}
# enemizer conditions
self.uw_enemy_denials = {}
self.ow_enemy_denials = {}
self.uw_enemy_drop_denials = {}
self.sheet_choices = []
denial_data = load_cached_yaml(['source', 'enemizer', 'enemy_deny.yaml'])
for denial in denial_data['UwGeneralDeny']:
self.uw_enemy_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]}
for denial in denial_data['OwGeneralDeny']:
self.ow_enemy_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]}
for denial in denial_data['UwEnemyDrop']:
self.uw_enemy_drop_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]}
weights = load_cached_yaml(['source', 'enemizer', 'enemy_weight.yaml'])
self.uw_weights = {sprite_translation[k]: v for k, v in weights['UW'].items()}
self.ow_weights = {sprite_translation[k]: v for k, v in weights['OW'].items()}
sheet_weights = load_cached_yaml(['source', 'enemizer', 'sheet_weight.yaml'])
for item in sheet_weights['SheetChoices']:
choice = SheetChoice(tuple(item['slots']), item['assignments'], item['weight'])
self.sheet_choices.append(choice)
def write_to_rom(self, rom, colorize_pots=False, increase_bush_sprite_chance=False):
if self.pot_secret_table.size() > 0x11c0:
raise Exception('Pot table is too big for current area')
self.pot_secret_table.write_pot_data_to_rom(rom, colorize_pots, self)
for room_id, header in self.room_headers.items():
data_location = (0x30DA00 + room_id * 14) & 0xFFFF
rom.write_bytes(snes_to_pc(0x04F1E2) + room_id * 2, int16_as_bytes(data_location))
header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm
room_start_address = 0x378000
for room_id, room in self.room_list.items():
rom.write_bytes(snes_to_pc(0x1F8000 + room_id * 3), int24_as_bytes(room_start_address))
door_start, bytes_written = room.write_to_rom(snes_to_pc(room_start_address), rom)
rom.write_bytes(snes_to_pc(0x1F83C0 + room_id * 3), int24_as_bytes(room_start_address + door_start))
room_start_address += bytes_written
if room_start_address > 0x380000:
raise Exception('Room list exceeded bank size')
# size notes: bank 03 uses 140E bytes
# bank 0A uses 372A bytes
# bank 1F uses 77CE bytes: total is about a bank and a half
# probably should reuse bank 1F if writing all the rooms out
for sheet in self.sprite_sheets.values():
sheet.write_to_rom(rom, snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3
if self.uw_enemy_table.size() > 0x2800:
raise Exception('Sprite table is too big for current area')
self.uw_enemy_table.write_sprite_data_to_rom(rom)
self.uw_enemy_table.check_special_bitmasks_size()
self.uw_enemy_table.write_special_bitmask_table(rom)
for area_id, sheet in self.overworld_sprite_sheets.items():
if area_id in [0x80, 0x81]:
offset = area_id - 0x80 # 02E575 for special areas?
rom.write_byte(snes_to_pc(0x02E576+offset), sheet.id)
else:
offset = convert_area_id_to_offset(area_id)
rom.write_byte(snes_to_pc(0x00FA81+offset), sheet.id)
# _00FA81 is LW normal
# _00FAC1 is LW post-aga
# _00FB01 is DW
self.write_ow_sprite_data_to_rom(rom)
for sprite, stats in self.enemy_stats.items():
# write health to rom
if stats.health is not None:
if isinstance(stats.health, tuple):
if sprite == EnemySprite.Octorok4Way: # skip this one
continue
if sprite in special_health_table:
a1, a2 = special_health_table[sprite]
rom.write_byte(snes_to_pc(a1), stats.health[0])
rom.write_byte(snes_to_pc(a2), stats.health[1])
else:
rom.write_byte(snes_to_pc(0x0DB173+int(sprite)), stats.health)
# write damage class to rom
if stats.damage is not None:
if isinstance(stats.damage, tuple):
if sprite == EnemySprite.Octorok4Way: # skip this one
continue
if sprite in special_damage_table:
a1, a2 = special_damage_table[sprite]
rom.write_byte(snes_to_pc(a1), stats.dmask | stats.damage[0])
rom.write_byte(snes_to_pc(a2), stats.dmask | stats.damage[1])
else:
rom.write_byte(snes_to_pc(0x0DB266+int(sprite)), stats.dmask | stats.damage)
# write damage table to rom
for idx, damage_list in self.enemy_damage.items():
rom.write_bytes(snes_to_pc(0x06F42D + idx * 3), damage_list)
# write bush spawns to rom:
for area_id, bush_sprite in self.bush_sprite_table.items():
rom.write_byte(snes_to_pc(0x368120 + area_id), bush_sprite.sprite)
if increase_bush_sprite_chance:
rom.write_bytes(snes_to_pc(0x1AFBBB), [
0x01, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x12,
0x0F, 0x01, 0x0F, 0x0F, 0x11, 0x0F, 0x0F, 0x03
])
def write_ow_sprite_data_to_rom(self, rom):
# calculate how big this table is going to be?
# bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1
# ending_byte = 0x09CB3B + bytes
max_per_state = {0: 0x40, 1: 0x90, 2: 0x8D} # dropped max on state 2 to steal space for a couple extra sprites (Murahdahla)
pointer_address = snes_to_pc(0x09C881)
data_pointer = snes_to_pc(0x09CB3B) # was originally 0x09CB41 - stealing space for a couple extra sprites (Murahdahla)
empty_pointer = pc_to_snes(data_pointer) & 0xFFFF
rom.write_byte(data_pointer, 0xff)
cached_dark_world = {}
data_pointer += 1
for state in range(0, 3):
if state > 0: # move pointer to next section
pointer_address += max_per_state[state-1] * 2
for screen in range(0, max_per_state[state]):
internal_screen_id = screen
if state == 0:
internal_screen_id += 0x200
if state == 2 and screen < 0x40:
internal_screen_id += 0x90
# has no sprites
if internal_screen_id not in self.ow_enemy_table or len(self.ow_enemy_table[internal_screen_id]) == 0:
rom.write_bytes(pointer_address + screen * 2, int16_as_bytes(empty_pointer))
else:
if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40
rom.write_bytes(pointer_address + screen * 2, cached_dark_world[screen])
# the sprites are already written out
elif len(self.ow_enemy_table[internal_screen_id]) > 0:
data_address = pc_to_snes(data_pointer) & 0xFFFF
ref = int16_as_bytes(data_address)
if screen >= 40:
cached_dark_world[screen] = ref
rom.write_bytes(pointer_address + screen * 2, ref)
for sprite in self.ow_enemy_table[internal_screen_id]:
data = sprite.sprite_data()
rom.write_bytes(data_pointer, data)
data_pointer += len(data)
rom.write_byte(data_pointer, 0xff)
data_pointer += 1
special_health_table = {
EnemySprite.Octorok: (0x068F76, 0x068F77),
EnemySprite.HardhatBeetle: (0x06911F, 0x069120),
EnemySprite.Tektite: (0x068D97, 0x068D98),
EnemySprite.CricketRat: (0x068876, 0x068877),
EnemySprite.Keese: (0x06888A, 0x06888B),
EnemySprite.Snake: (0x0688A6, 0x0688A7),
EnemySprite.Raven: (0x068965, 0x068966)
}
special_damage_table = {
EnemySprite.Octorok: (0x068F74, 0x068F75),
EnemySprite.Tektite: (0x068D99, 0x068D9A),
EnemySprite.CricketRat: (0x068874, 0x068875),
EnemySprite.Keese: (0x068888, 0x068889),
EnemySprite.Snake: (0x0688A4, 0x0688A5),
EnemySprite.Raven: (0x068963, 0x068964)
}
def init_data_tables(world, player):
data_tables = DataTables()
data_tables.room_headers = init_room_headers()
data_tables.room_list = {}
if world.pottery[player] not in ['none']:
data_tables.room_list[0x0127] = Room0127
data_tables.sprite_requirements = init_sprite_requirements()
data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements)
init_vanilla_sprites()
data_tables.enemy_stats = init_enemy_stats()
uw_table = data_tables.uw_enemy_table = EnemyTable()
for room, sprite_list in vanilla_sprites.items():
for sprite in sprite_list:
uw_table.room_map[room].append(sprite.copy())
data_tables.overworld_sprite_sheets = {}
data_tables.ow_enemy_table = defaultdict(list)
init_vanilla_sprites_ow()
for area, sprite_list in vanilla_sprites_ow.items():
for sprite in sprite_list:
if sprite.bonk and world.shuffle_bonk_drops[player]:
sprite.kind = EnemySprite.GreenRupee
data_tables.ow_enemy_table[area].append(sprite.copy())
data_tables.enemy_damage = {k: list(v) for k, v in world.damage_table[player].enemy_damage.items()}
# todo: more denials based on enemy drops
return data_tables
def get_uw_enemy_table():
init_vanilla_sprites()
uw_table = EnemyTable()
for room, sprite_list in vanilla_sprites.items():
for sprite in sprite_list:
uw_table.room_map[room].append(sprite.copy())
return uw_table
+1
View File
@@ -0,0 +1 @@
# do nothing, just exist to make "source" package
+73 -38
View File
@@ -32,6 +32,39 @@ def roll_settings(weights):
return default
return choice
def get_choice_bool(option, root=weights):
choice = get_choice(option, root)
if choice is True or choice == 'on':
return True
if choice is False or choice == 'off':
return False
if choice is None:
return choice
raise Exception("This fields needs to be true/false or off/on")
def get_choice_non_bool(option, root=weights):
choice = get_choice(option, root)
if choice is True or choice == 'on':
return 'on'
if choice is False or choice == 'off':
return 'off'
return choice
def get_choice_yn(option, root=weights):
choice = get_choice(option, root)
if choice is True or choice == 'yes':
return 'yes'
if choice is False or choice == 'no':
return 'no'
return choice
def get_choice_bool_default(option, root=weights, default=None):
choice = get_choice_bool(option, root)
if choice is None and default is not None:
return default
return choice
while True:
subweights = weights.get('subweights', {})
if len(subweights) == 0:
@@ -46,9 +79,10 @@ def roll_settings(weights):
ret.algorithm = get_choice('algorithm')
glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches', 'no_logic': 'nologic',
glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches',
'hmg': 'hybridglitches', 'hybridglitches': 'hybridglitches',
'owg': 'owglitches', 'owglitches': 'owglitches'}
'owg': 'owglitches', 'owglitches': 'owglitches',
'no_logic': 'nologic'}
glitches_required = get_choice('glitches_required')
if glitches_required is not None:
if glitches_required not in glitch_map.keys():
@@ -62,8 +96,8 @@ def roll_settings(weights):
dungeon_items = get_choice('dungeon_items')
dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items
dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items
ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else 'm' in dungeon_items
ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items
ret.mapshuffle = get_choice_bool('map_shuffle') if 'map_shuffle' in weights else 'm' in dungeon_items
ret.compassshuffle = get_choice_bool('compass_shuffle') if 'compass_shuffle' in weights else 'c' in dungeon_items
if 'smallkey_shuffle' in weights:
ret.keyshuffle = get_choice('smallkey_shuffle')
else:
@@ -71,23 +105,23 @@ def roll_settings(weights):
ret.keyshuffle = 'wild'
if 'u' in dungeon_items:
ret.keyshuffle = 'universal'
ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else 'b' in dungeon_items
ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items
ret.accessibility = get_choice('accessibility')
ret.restrict_boss_items = get_choice('restrict_boss_items')
overworld_shuffle = get_choice('overworld_shuffle')
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
ret.ow_terrain = get_choice('overworld_terrain') == 'on'
ret.ow_terrain = get_choice_bool('overworld_terrain')
valid_options = {'none': 'none', 'polar': 'polar', 'grouped': 'polar', 'chaos': 'unrestricted', 'unrestricted': 'unrestricted'}
ret.ow_crossed = get_choice('overworld_crossed')
ret.ow_crossed = valid_options[ret.ow_crossed] if ret.ow_crossed in valid_options else 'none'
ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on'
ret.ow_mixed = get_choice('overworld_swap') == 'on'
ret.ow_whirlpool = get_choice('whirlpool_shuffle') == 'on'
ret.ow_keepsimilar = get_choice_bool('overworld_keepsimilar')
ret.ow_mixed = get_choice_bool('overworld_swap')
ret.ow_whirlpool = get_choice_bool('whirlpool_shuffle')
overworld_flute = get_choice('flute_shuffle')
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
ret.bonk_drops = get_choice('bonk_drops') == 'on'
ret.bonk_drops = get_choice_bool('bonk_drops')
entrance_shuffle = get_choice('entrance_shuffle')
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
overworld_map = get_choice('overworld_map')
@@ -98,29 +132,30 @@ def roll_settings(weights):
ret.door_type_mode = get_choice('door_type_mode')
ret.trap_door_mode = get_choice('trap_door_mode')
ret.key_logic_algorithm = get_choice('key_logic_algorithm')
ret.decoupledoors = get_choice('decoupledoors') == 'on'
ret.door_self_loops = get_choice('door_self_loops') == 'on'
ret.experimental = get_choice('experimental') == 'on'
ret.collection_rate = get_choice('collection_rate') == 'on'
ret.decoupledoors = get_choice_bool('decoupledoors')
ret.door_self_loops = get_choice_bool('door_self_loops')
ret.experimental = get_choice_bool('experimental')
ret.collection_rate = get_choice_bool('collection_rate')
ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default'
ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default'
if ret.dungeon_counters == 'default':
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
ret.pseudoboots = get_choice('pseudoboots') == 'on'
ret.shopsanity = get_choice('shopsanity') == 'on'
keydropshuffle = get_choice('keydropshuffle') == 'on'
ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle
ret.pseudoboots = get_choice_bool('pseudoboots')
ret.shopsanity = get_choice_bool('shopsanity')
keydropshuffle = get_choice_bool('keydropshuffle')
ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none'
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle
ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none'
ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery
ret.colorizepots = get_choice_default('colorizepots', default='on') == 'on'
ret.shufflepots = get_choice('pot_shuffle') == 'on'
ret.aga_randomness = get_choice('aga_randomness') == 'on'
ret.colorizepots = get_choice_bool_default('colorizepots', default=True)
ret.shufflepots = get_choice_bool('pot_shuffle')
ret.aga_randomness = get_choice_bool('aga_randomness')
ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent'
ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights
else 'standardize')
goal = get_choice('goals')
goal = get_choice_default('goals', default='ganon')
if goal is not None:
ret.goal = {'ganon': 'ganon',
'fast_ganon': 'crystals',
@@ -134,9 +169,9 @@ def roll_settings(weights):
ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto'
ret.shuffleganon = get_choice('shuffleganon') == 'on'
ret.shufflelinks = get_choice('shufflelinks') == 'on'
ret.shuffletavern = get_choice('shuffletavern') == 'on'
ret.shuffleganon = get_choice_bool('shuffleganon')
ret.shufflelinks = get_choice_bool('shufflelinks')
ret.shuffletavern = get_choice_bool('shuffletavern')
ret.crystals_gt = get_choice('tower_open')
ret.crystals_ganon = get_choice('ganon_open')
@@ -154,12 +189,12 @@ def roll_settings(weights):
if ret.mode == 'retro':
ret.mode = 'open'
ret.retro = True
ret.retro = get_choice('retro') == 'on' # this overrides world_state if used
ret.retro = get_choice_bool('retro') # this overrides world_state if used
ret.take_any = get_choice_default('take_any', default='none')
ret.bombbag = get_choice('bombbag') == 'on'
ret.bombbag = get_choice_bool('bombbag')
ret.hints = get_choice('hints') == 'on'
ret.hints = get_choice_bool('hints')
swords = get_choice('weapons')
if swords is not None:
@@ -196,6 +231,7 @@ def roll_settings(weights):
ret.enemy_damage = damage_choice
ret.enemy_health = get_choice('enemy_health')
ret.any_enemy_logic = get_choice('any_enemy_logic')
ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0'
@@ -211,18 +247,17 @@ def roll_settings(weights):
if 'rom' in weights:
romweights = weights['rom']
ret.sprite = get_choice('sprite', romweights)
ret.disablemusic = get_choice('disablemusic', romweights) == 'on'
ret.quickswap = get_choice('quickswap', romweights) == 'on'
ret.reduce_flashing = get_choice('reduce_flashing', romweights) == 'on'
ret.msu_resume = get_choice('msu_resume', romweights) == 'on'
ret.disablemusic = get_choice_bool('disablemusic', romweights)
ret.quickswap = get_choice_bool('quickswap', romweights)
ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights)
ret.fastmenu = get_choice('menuspeed', romweights)
ret.heartcolor = get_choice('heartcolor', romweights)
ret.heartbeep = get_choice('heartbeep', romweights)
ret.heartbeep = get_choice_non_bool('heartbeep', romweights)
ret.ow_palettes = get_choice('ow_palettes', romweights)
ret.uw_palettes = get_choice('uw_palettes', romweights)
ret.shuffle_sfx = get_choice('shuffle_sfx', romweights) == 'on'
ret.shuffle_sfxinstruments = get_choice('shuffle_sfxinstruments', romweights) == 'on'
ret.shuffle_songinstruments = get_choice('shuffle_songinstruments', romweights) == 'on'
ret.msu_resume = get_choice('msu_resume', romweights) == 'on'
ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights)
ret.shuffle_sfxinstruments = get_choice_bool('shuffle_sfxinstruments', romweights)
ret.shuffle_songinstruments = get_choice_bool('shuffle_songinstruments', romweights)
ret.msu_resume = get_choice_bool('msu_resume', romweights)
return ret
+1 -1
View File
@@ -28,7 +28,7 @@ def main(args=None):
def test(test_name: str, command: str, test_file: str):
tests[test_name] = [command]
base_command = f"python3.8 DungeonRandomizer.py --suppress_rom --suppress_spoiler"
base_command = f"python3 DungeonRandomizer.py --suppress_rom --suppress_spoiler"
def gen_seed():
task_command = base_command + " " + command
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
meta:
seed: 394
settings:
1:
# mode: standard
# boss_shuffle: random
# dropshuffle: underworld
# enemy_shuffle: shuffled
door_shuffle: partitioned
intensity: 3
shuffle: lite
experimental: on
shopsanity: on
# dungeon_counters: 'on'
#entrances:
# 1:
# entrances:
# Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top)
# two-way:
# Hyrule Castle Entrance (South): Links House Exit
doors:
1:
# lobbies:
# Hyrule Castle South: GT Lobby S
doors:
TR Eye Bridge SW: Ice Compass Room NE
TR Lazy Eyes SE: Mire Conveyor Barrier NW
Hera Basement Cage Up Stairs: Hera Lobby Down Stairs
Hera Lobby Key Stairs:
dest: Hera Boss Down Stairs
type: Key Door
Hera Tile Room Up Stairs: Hera 5F Down Stairs
#bosses:
# 1:
# Ganons Tower (middle): Trinexx
placements:
1:
Tower of Hera - Basement Cage: Small Key (Tower of Hera)
# Link's House: Lamp
#placements:
# 1:
# 'Hera Basement Cage Enemy #4': Small Key (Palace of Darkness)
# 'Hera Basement Cage Enemy #9': Small Key (Tower of Hera)
# 'Hera Basement Cage Enemy #11': Fire Rod
# Tower of Hera - Basement Cage: Small Key (Turtle Rock)
#start_inventory:
# 1:
# - Pegasus Boots
# - Progressive Sword
# - Ocarina (Activated)
# - Hookshot
+29
View File
@@ -0,0 +1,29 @@
meta:
players: 1
settings:
1:
mode: open
enemy_shuffle: shuffled
dropshuffle: underworld
enemies:
1:
Underworld:
0x55:
1: Lynel
placements: # all the ways to kill a Lynel are Flipper locked
1:
Waterfall Fairy - Left: Progressive Sword
Waterfall Fairy - Right: Progressive Sword
Hobo: Progressive Sword
Swamp Palace - Map Chest: Progressive Bow
Swamp Palace - Compass Chest: Progressive Bow
Zora's Ledge: Hammer
advanced_placements:
1:
- type: Verification
item: Flippers
locations:
'Hyrule Castle Secret Entrance Enemy #2': False # can't kill the lynel
Link's Uncle: True