fix(key logic): typo

This commit is contained in:
aerinon
2023-11-11 20:00:31 -07:00
parent 13f9fe8f92
commit f4a702951c
2 changed files with 69 additions and 66 deletions

View File

@@ -10,7 +10,6 @@ try:
except ImportError: except ImportError:
from enum import IntFlag as FastEnum from enum import IntFlag as FastEnum
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish
from EntranceShuffle import door_addresses, indirect_connections from EntranceShuffle import door_addresses, indirect_connections
from Utils import int16_as_bytes from Utils import int16_as_bytes
@@ -94,6 +93,7 @@ class World(object):
for player in range(1, players + 1): for player in range(1, players + 1):
def set_player_attr(attr, val): def set_player_attr(attr, val):
self.__dict__.setdefault(attr, {})[player] = val self.__dict__.setdefault(attr, {})[player] = val
set_player_attr('_region_cache', {}) set_player_attr('_region_cache', {})
set_player_attr('player_names', []) set_player_attr('player_names', [])
set_player_attr('remote_items', False) set_player_attr('remote_items', False)
@@ -408,7 +408,7 @@ class World(object):
def push_precollected(self, item): def push_precollected(self, item):
item.world = self item.world = self
if ((item.smallkey and self.keyshuffle[item.player] != 'none') 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 item.advancement = True
self.precollected_items.append(item) self.precollected_items.append(item)
self.state.collect(item, True) self.state.collect(item, True)
@@ -761,7 +761,7 @@ class CollectionState(object):
bc_ = self.blocked_connections[player] bc_ = self.blocked_connections[player]
for block, crystal in bc_.items(): for block, crystal in bc_.items():
if (block, crystal) not in terminal_queue and self.possibly_connected_to_dungeon(block.connected_region, player): 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.traverse_world(terminal_queue, rrp_, bc_, player)
self.dungeon_limits = None self.dungeon_limits = None
@@ -784,7 +784,7 @@ class CollectionState(object):
missing_bc = {} missing_bc = {}
for blocked, crystal in common_bc.items(): for blocked, crystal in common_bc.items():
if (blocked not in bc and blocked.parent_region in rrp 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 missing_bc[blocked] = crystal
for k in missing_bc: for k in missing_bc:
bc[k] = missing_bc[k] bc[k] = missing_bc[k]
@@ -833,15 +833,15 @@ class CollectionState(object):
return door_candidates return door_candidates
door_candidates, skip = [], set() door_candidates, skip = [], set()
if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal' if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal'
and state.placing_item and state.placing_item.name == small_key_name): and state.placing_item and state.placing_item.name == small_key_name):
key_logic = state.world.key_logic[player][dungeon_name] key_logic = state.world.key_logic[player][dungeon_name]
for door, paired in key_logic.sm_doors.items(): for door, paired in key_logic.sm_doors.items():
if door.name in key_logic.door_rules: if door.name in key_logic.door_rules:
rule = key_logic.door_rules[door.name] rule = key_logic.door_rules[door.name]
key = KeyRuleType.AllowSmall key = KeyRuleType.AllowSmall
if (key in rule.new_rules and key_total >= rule.new_rules[key] and door.name not in skip 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 door.name in state.reached_doors[player] and door.name not in state.opened_doors[player]
and rule.small_location.item is None): and rule.small_location.item is None):
if paired: if paired:
door_candidates.append((door.name, paired.name)) door_candidates.append((door.name, paired.name))
skip.add(paired.name) skip.add(paired.name)
@@ -965,8 +965,8 @@ class CollectionState(object):
'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Ocarina (Activated)', '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)', 'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)',
'Magic Upgrade (1/4)'] 'Magic Upgrade (1/4)']
or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) or item_name.startswith(('Bottle', 'Small Key', 'Big Key'))
or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass')))) or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass'))))
def can_reach(self, spot, resolution_hint=None, player=None): def can_reach(self, spot, resolution_hint=None, player=None):
try: try:
@@ -1013,7 +1013,6 @@ class CollectionState(object):
self.collect(event.item, True, event) self.collect(event.item, True, event)
new_locations = True new_locations = True
def can_reach_blue(self, region, player): 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] return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either]
@@ -1026,7 +1025,7 @@ class CollectionState(object):
if event.name in flooded_keys.keys(): if event.name in flooded_keys.keys():
flood_location = self.world.get_location(flooded_keys[event.name], event.player) flood_location = self.world.get_location(flooded_keys[event.name], event.player)
if (flood_location.item and flood_location not in self.locations_checked 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) adjusted_checks.remove(event)
if len(adjusted_checks) < len(reachable_events): if len(adjusted_checks) < len(reachable_events):
return adjusted_checks return adjusted_checks
@@ -1115,16 +1114,16 @@ class CollectionState(object):
# Warning: This only considers items that are marked as advancement items # Warning: This only considers items that are marked as advancement items
diff = self.world.difficulty_requirements[player] diff = self.world.difficulty_requirements[player]
return ( return (
min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit)
+ self.item_count('Sanctuary Heart Container', player) + self.item_count('Sanctuary Heart Container', player)
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4
+ 3 # starting hearts + 3 # starting hearts
) )
def can_lift_heavy_rocks(self, player): def can_lift_heavy_rocks(self, player):
return self.has('Titans Mitts', 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 basemagic = 8
if self.has('Magic Upgrade (1/4)', player): if self.has('Magic Upgrade (1/4)', player):
basemagic = 32 basemagic = 32
@@ -1162,19 +1161,19 @@ class CollectionState(object):
or self.has('Ice Rod', player) or self.has('Ice Rod', player)
or self.has('Cane of Somaria', player) or self.has('Cane of Somaria', player)
or self.has('Cane of Byrna', player)) or self.has('Cane of Byrna', player))
def can_hit_crystal_through_barrier(self, player): def can_hit_crystal_through_barrier(self, player):
return (self.can_use_bombs(player) return (self.can_use_bombs(player)
or self.can_shoot_arrows(player) or self.can_shoot_arrows(player)
or self.has('Blue Boomerang', player) or self.has('Blue Boomerang', player)
or self.has('Red Boomerang', player) or self.has('Red Boomerang', player)
or self.has('Fire Rod', player) or self.has('Fire Rod', player)
or self.has('Ice Rod', player) or self.has('Ice Rod', player)
or self.has('Cane of Somaria', player)) or self.has('Cane of Somaria', player))
def can_shoot_arrows(self, player): def can_shoot_arrows(self, player):
if self.world.bow_mode[player] in ['retro', 'retro_silvers']: 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) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player))
return self.has('Bow', player) return self.has('Bow', player)
@@ -1215,7 +1214,7 @@ class CollectionState(object):
return False # can't flute in rain state return False # can't flute in rain state
lw = self.world.get_region('Light World', player) lw = self.world.get_region('Light World', player)
return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self) return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self)
and self.is_not_bunny(lw, player)) and self.is_not_bunny(lw, player))
def can_melt_things(self, player): def can_melt_things(self, player):
return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player))
@@ -1403,13 +1402,14 @@ class CollectionState(object):
def __getattr__(self, item): def __getattr__(self, item):
if item.startswith('can_reach_'): if item.startswith('can_reach_'):
return self.can_reach(item[10]) return self.can_reach(item[10])
#elif item.startswith('has_'): # elif item.startswith('has_'):
# return self.has(item[4]) # return self.has(item[4])
if item == '__len__': if item == '__len__':
return return
raise RuntimeError('Cannot parse %s.' % item) raise RuntimeError('Cannot parse %s.' % item)
@unique @unique
class RegionType(Enum): class RegionType(Enum):
Menu = 0 Menu = 0
@@ -1435,7 +1435,7 @@ class Region(object):
self.dungeon = None self.dungeon = None
self.shop = None self.shop = None
self.world = 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.is_dark_world = False
self.spot_type = 'Region' self.spot_type = 'Region'
self.hint_text = hint self.hint_text = hint
@@ -1747,7 +1747,7 @@ class Door(object):
self.edge_id = None self.edge_id = None
self.edge_width = None self.edge_width = None
#portal items # portal items
self.portalAble = False self.portalAble = False
self.roomLayout = 0x22 # free scroll- both directions self.roomLayout = 0x22 # free scroll- both directions
self.entranceFlag = False self.entranceFlag = False
@@ -2093,17 +2093,17 @@ class Portal(object):
return self.door.roomIndex return self.door.roomIndex
def relative_coords(self): 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 x_rel = (self.door.roomIndex & 0x0f) * 2
quad = self.door.quadrant quad = self.door.quadrant
if quad == 0: 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: 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: 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: 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): def scroll_x(self):
x_rel = (self.door.roomIndex & 0x0f) * 2 x_rel = (self.door.roomIndex & 0x0f) * 2
@@ -2112,7 +2112,7 @@ class Portal(object):
elif self.door.doorIndex == 1: elif self.door.doorIndex == 1:
return [0x80, x_rel] return [0x80, x_rel]
else: else:
return [0x00, x_rel+1] return [0x00, x_rel + 1]
def scroll_y(self): def scroll_y(self):
y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1 y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1
@@ -2132,7 +2132,7 @@ class Portal(object):
elif self.door.doorIndex == 1: elif self.door.doorIndex == 1:
return [0xf8, x_rel] return [0xf8, x_rel]
else: else:
return [0x78, x_rel+1] return [0x78, x_rel + 1]
# def camera_y(self): # def camera_y(self):
# return [0x87, 0x01] # return [0x87, 0x01]
@@ -2191,21 +2191,22 @@ class Boss(object):
def can_defeat(self, state): def can_defeat(self, state):
return self.defeat_rule(state, self.player) return self.defeat_rule(state, self.player)
class Location(object): class Location(object):
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None,
player_address=None, note=None): player_address=None, note=None):
self.name = name self.name = name
self.parent_region = parent self.parent_region = parent
if forced_item is not None: if forced_item is not None:
from Items import ItemFactory from Items import ItemFactory
self.forced_item = ItemFactory([forced_item], player)[0] self.forced_item = ItemFactory([forced_item], player)[0]
self.item = self.forced_item self.item = self.forced_item
self.item.location = self self.item.location = self
self.event = True self.event = True
else: else:
self.forced_item = None self.forced_item = None
self.item = None self.item = None
self.event = False self.event = False
self.crystal = crystal self.crystal = crystal
self.address = address self.address = address
self.player_address = player_address self.player_address = player_address
@@ -2353,12 +2354,14 @@ class Item(object):
class Crystal(Item): class Crystal(Item):
pass pass
@unique @unique
class ShopType(Enum): class ShopType(Enum):
Shop = 0 Shop = 0
TakeAny = 1 TakeAny = 1
UpgradeShop = 2 UpgradeShop = 2
class Shop(object): class Shop(object):
def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address): def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address):
self.region = region self.region = region
@@ -2382,7 +2385,7 @@ class Shop(object):
entrances = self.region.entrances entrances = self.region.entrances
config = self.item_count config = self.item_count
if len(entrances) == 1 and entrances[0].name in 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: else:
door_id = 0 door_id = 0
config |= 0x40 # ignore door id config |= 0x40 # ignore door id
@@ -2390,7 +2393,7 @@ class Shop(object):
config |= 0x80 config |= 0x80
if self.type == ShopType.UpgradeShop: if self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM 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): def has_unlimited(self, item):
for inv in self.inventory: for inv in self.inventory:
@@ -2632,7 +2635,7 @@ class Spoiler(object):
out['Special'] = self.medallions out['Special'] = self.medallions
out['Bottles'] = self.bottles out['Bottles'] = self.bottles
if self.hashes: 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: if self.shops:
out['Shops'] = self.shops out['Shops'] = self.shops
out['playthrough'] = self.playthrough out['playthrough'] = self.playthrough
@@ -2723,7 +2726,7 @@ class Spoiler(object):
if self.startinventory: if self.startinventory:
outfile.write('Starting Inventory:'.ljust(line_width)) 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): def hashes_to_file(self, filename):
with open(filename, 'r') as infile: with open(filename, 'r') as infile:
@@ -2743,7 +2746,7 @@ class Spoiler(object):
if len(self.hashes) > 0: if len(self.hashes) > 0:
for team in range(self.world.teams): for team in range(self.world.teams):
player_name = self.world.player_names[player][team] 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') idx = insert(contents, idx, f'{label}{self.hashes[player, team]}\n')
if self.world.players > 1: if self.world.players > 1:
insert(contents, idx, '\n') # return value ignored here, if you want to add more lines insert(contents, idx, '\n') # return value ignored here, if you want to add more lines
@@ -2774,16 +2777,16 @@ class Spoiler(object):
if self.entrances: if self.entrances:
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
outfile.write('\nEntrances:\n\n') outfile.write('\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: if self.doors:
outfile.write('\n\nDoors:\n\n') outfile.write('\n\nDoors:\n\n')
outfile.write('\n'.join( outfile.write('\n'.join(
['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', ['%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 '=>', '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>',
self.world.fish.translate("meta","doors",entry['exit']), self.world.fish.translate("meta", "doors", entry['exit']),
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for
entry in self.doors.values()])) entry in self.doors.values()]))
if self.lobbies: if self.lobbies:
outfile.write('\n\nDungeon Lobbies:\n\n') outfile.write('\n\nDungeon Lobbies:\n\n')
@@ -2795,7 +2798,7 @@ class Spoiler(object):
# doorNames: For some reason these come in combined, somehow need to split on the thing to translate # doorNames: For some reason these come in combined, somehow need to split on the thing to translate
# doorTypes: Small Key, Bombable, Bonkable # doorTypes: Small Key, Bombable, Bonkable
outfile.write('\n\nDoor Types:\n\n') 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 # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
# items: Item names # items: Item names
@@ -2805,7 +2808,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 # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
# items: Item names # items: Item names
outfile.write('\n\nShops:\n\n') outfile.write('\n\nShops:\n\n')
outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta", "locations", shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta", "items", item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops))
for player in range(1, self.world.players + 1): for player in range(1, self.world.players + 1):
if self.world.boss_shuffle[player] != 'none': if self.world.boss_shuffle[player] != 'none':
@@ -2818,23 +2821,23 @@ class Spoiler(object):
with open(filename, 'a') as outfile: with open(filename, 'a') as outfile:
outfile.write('\n\nOverworld Enemies:\n\n') outfile.write('\n\nOverworld Enemies:\n\n')
for player in range(1, self.world.players + 1): for player in range(1, self.world.players + 1):
player_tag = ' '+self.world.get_player_names(player) if self.world.players > 1 else '' 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 area, sprite_list in self.world.data_tables[player].ow_enemy_table.items():
for idx, sprite in enumerate(sprite_list): for idx, sprite in enumerate(sprite_list):
outfile.write(f'{hex(area)} Enemy #{idx+1}{player_tag}: {str(sprite)}\n') outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n')
outfile.write('\n\nUnderworld Enemies:\n\n') outfile.write('\n\nUnderworld Enemies:\n\n')
for player in range(1, self.world.players + 1): for player in range(1, self.world.players + 1):
player_tag = ' '+self.world.get_player_names(player) if self.world.players > 1 else '' 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 area, sprite_list in self.world.data_tables[player].uw_enemy_table.room_map.items():
for idx, sprite in enumerate(sprite_list): for idx, sprite in enumerate(sprite_list):
outfile.write(f'{hex(area)} Enemy #{idx+1}{player_tag}: {str(sprite)}\n') outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n')
def playthrough_to_file(self, filename): def playthrough_to_file(self, filename):
with open(filename, 'a') as outfile: 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 # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
# items: Item names # items: Item names
outfile.write('\n\nPlaythrough:\n\n') outfile.write('\n\nPlaythrough:\n\n')
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: 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 # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
# items: Item names # items: Item names
@@ -2852,10 +2855,10 @@ class Spoiler(object):
path_lines = [] path_lines = []
for region, exit in path: for region, exit in path:
if exit is not None: 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: else:
path_lines.append(self.world.fish.translate("meta","rooms",region)) 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_listings.append("{}\n {}".format(self.world.fish.translate("meta", "locations", location), "\n => ".join(path_lines)))
outfile.write('\n'.join(path_listings)) outfile.write('\n'.join(path_listings))
@@ -2887,6 +2890,7 @@ dungeon_keys = {
'Universal': 'Small Key (Universal)' 'Universal': 'Small Key (Universal)'
} }
class PotItem(FastEnum): class PotItem(FastEnum):
Nothing = 0x0 Nothing = 0x0
OneRupee = 0x1 OneRupee = 0x1
@@ -2968,7 +2972,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4,
# byte 1: LLLW WSS? (logic, mode, sword) # byte 1: LLLW WSS? (logic, mode, sword)
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4} logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
world_mode = {"open": 0, "standard": 1, "inverted": 2} 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) # byte 2: GGGD DFFH (goal, diff, item_func, hints)
goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5, goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5,
@@ -3008,7 +3012,6 @@ rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2}
algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4, 'major_only': 5} algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4, 'major_only': 5}
boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique': 4} boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique': 4}
# byte 10: settings_version # byte 10: settings_version
# byte 11: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode) # byte 11: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode)
flute_mode = {'normal': 0, 'active': 1} flute_mode = {'normal': 0, 'active': 1}
@@ -3071,7 +3074,7 @@ class Settings(object):
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 ((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]]), | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
]) ])
return base64.b64encode(code, "+-".encode()).decode() return base64.b64encode(code, "+-".encode()).decode()
@staticmethod @staticmethod

View File

@@ -1800,7 +1800,7 @@ def imp_locations_factory(world, player):
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden'] imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
if world.mode[player] == 'standard': if world.mode[player] == 'standard':
imp_locations.append('Zelda Pickup') imp_locations.append('Zelda Pickup')
imp_locations.append('Zelda Dropoff') imp_locations.append('Zelda Drop Off')
return imp_locations return imp_locations