fix(key logic): typo
This commit is contained in:
133
BaseClasses.py
133
BaseClasses.py
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user