Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
+111
-71
@@ -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,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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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'),
|
||||
|
||||
@@ -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
@@ -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
@@ -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'),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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():
|
||||
|
||||
@@ -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
@@ -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 = ''
|
||||
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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?')
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -4,7 +4,7 @@ meta:
|
||||
settings:
|
||||
1:
|
||||
door_shuffle: vanilla
|
||||
dropshuffle: true
|
||||
dropshuffle: keys
|
||||
experimental: true
|
||||
goal: ganon
|
||||
hints: false
|
||||
|
||||
@@ -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
|
||||
+1
-1
@@ -15,7 +15,7 @@ settings:
|
||||
intensity: 3
|
||||
door_type_mode: big
|
||||
pottery: keys
|
||||
dropshuffle: true
|
||||
dropshuffle: keys
|
||||
experimental: true
|
||||
dungeon_counters: 'on'
|
||||
hints: true
|
||||
+1
-1
@@ -14,7 +14,7 @@ settings:
|
||||
intensity: 3
|
||||
door_type_mode: big
|
||||
pottery: keys
|
||||
dropshuffle: true
|
||||
dropshuffle: keys
|
||||
experimental: true
|
||||
dungeon_counters: 'on'
|
||||
hints: true
|
||||
+1
-1
@@ -14,7 +14,7 @@ settings:
|
||||
intensity: 3
|
||||
door_type_mode: big
|
||||
pottery: lottery
|
||||
dropshuffle: true
|
||||
dropshuffle: keys
|
||||
experimental: true
|
||||
dungeon_counters: 'on'
|
||||
hints: true
|
||||
+1
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# do nothing, just exist to make "source" package
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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'])
|
||||
|
||||
@@ -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
|
||||
@@ -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')
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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]\)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# do nothing, just exist to make "source" package
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -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"]]
|
||||
|
||||
@@ -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%
|
||||
@@ -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
@@ -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())
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
@@ -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)',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# do nothing, just exist to make "source" package
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user