Retro break up of various options

This commit is contained in:
aerinon
2022-09-02 13:09:39 -06:00
parent af4f8e5b4b
commit 87834986d4
28 changed files with 351 additions and 193 deletions
+43 -28
View File
@@ -22,7 +22,7 @@ from source.dungeon.RoomObject import RoomObject
class World(object): class World(object):
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints): timer, progressive, goal, algorithm, accessibility, shuffle_ganon, custom, customitemarray, hints):
self.players = players self.players = players
self.teams = 1 self.teams = 1
self.shuffle = shuffle.copy() self.shuffle = shuffle.copy()
@@ -64,7 +64,6 @@ class World(object):
self.fix_trock_exit = {} self.fix_trock_exit = {}
self.shuffle_ganon = shuffle_ganon self.shuffle_ganon = shuffle_ganon
self.fix_gtower_exit = self.shuffle_ganon self.fix_gtower_exit = self.shuffle_ganon
self.retro = retro.copy()
self.custom = custom self.custom = custom
self.customitemarray = customitemarray self.customitemarray = customitemarray
self.can_take_damage = True self.can_take_damage = True
@@ -91,10 +90,6 @@ class World(object):
self.pot_contents = {} self.pot_contents = {}
for player in range(1, players + 1): for player in range(1, players + 1):
# If World State is Retro, set to Open and set Retro flag
if self.mode[player] == "retro":
self.mode[player] = "open"
self.retro[player] = True
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', {})
@@ -118,10 +113,12 @@ class World(object):
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic'] or shuffle[player] in ['crossed', 'insanity']) set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic'] or shuffle[player] in ['crossed', 'insanity'])
set_player_attr('mapshuffle', False) set_player_attr('mapshuffle', False)
set_player_attr('compassshuffle', False) set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', False) set_player_attr('keyshuffle', 'standard')
set_player_attr('bigkeyshuffle', False) set_player_attr('bigkeyshuffle', False)
set_player_attr('restrict_boss_items', 'none') set_player_attr('restrict_boss_items', 'none')
set_player_attr('bombbag', False) set_player_attr('bombbag', False)
set_player_attr('flute_mode', False)
set_player_attr('bow_mode', False)
set_player_attr('difficulty_requirements', None) set_player_attr('difficulty_requirements', None)
set_player_attr('boss_shuffle', 'none') set_player_attr('boss_shuffle', 'none')
set_player_attr('enemy_shuffle', 'none') set_player_attr('enemy_shuffle', 'none')
@@ -134,6 +131,7 @@ class World(object):
set_player_attr('crystals_ganon_orig', {}) set_player_attr('crystals_ganon_orig', {})
set_player_attr('crystals_gt_orig', {}) set_player_attr('crystals_gt_orig', {})
set_player_attr('open_pyramid', False) set_player_attr('open_pyramid', False)
set_player_attr('take_any', 'none')
set_player_attr('treasure_hunt_icon', 'Triforce Piece') set_player_attr('treasure_hunt_icon', 'Triforce Piece')
set_player_attr('treasure_hunt_count', 0) set_player_attr('treasure_hunt_count', 0)
set_player_attr('treasure_hunt_total', 0) set_player_attr('treasure_hunt_total', 0)
@@ -154,6 +152,11 @@ class World(object):
set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('exp_cache', defaultdict(dict))
set_player_attr('enabled_entrances', {}) set_player_attr('enabled_entrances', {})
def finish_init(self):
for player in range(1, self.players + 1):
if self.mode[player] == 'retro':
self.mode[player] == 'open'
def get_name_string_for_object(self, obj): def get_name_string_for_object(self, obj):
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})' return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
@@ -375,7 +378,8 @@ 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]) or (item.bigkey and self.bigkeyshuffle[item.player]): if ((item.smallkey and self.keyshuffle[item.player] != 'none')
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)
@@ -587,7 +591,7 @@ class CollectionState(object):
if key_logic.sm_doors[door]: if key_logic.sm_doors[door]:
self.reached_doors[player].add(key_logic.sm_doors[door].name) self.reached_doors[player].add(key_logic.sm_doors[door].name)
if not connection.can_reach(self): if not connection.can_reach(self):
checklist_key = 'Universal' if self.world.retro[player] else dungeon_name checklist_key = 'Universal' if self.world.keyshuffle[player] == 'universal' else dungeon_name
checklist = self.dungeons_to_check[player][checklist_key] checklist = self.dungeons_to_check[player][checklist_key]
checklist[connection.name] = (connection, crystal_state) checklist[connection.name] = (connection, crystal_state)
elif door.name not in self.opened_doors[player]: elif door.name not in self.opened_doors[player]:
@@ -759,7 +763,7 @@ class CollectionState(object):
return None return None
def set_dungeon_limits(self, player, dungeon_name): def set_dungeon_limits(self, player, dungeon_name):
if self.world.retro[player] and self.world.mode[player] == 'standard': if self.world.keyshuffle[player] == 'universal' and self.world.mode[player] == 'standard':
self.dungeon_limits = ['Hyrule Castle', 'Agahnims Tower'] self.dungeon_limits = ['Hyrule Castle', 'Agahnims Tower']
else: else:
self.dungeon_limits = [dungeon_name] self.dungeon_limits = [dungeon_name]
@@ -935,7 +939,7 @@ class CollectionState(object):
checked_locations = 0 checked_locations = 0
while new_locations: while new_locations:
reachable_events = [location for location in locations if location.event and reachable_events = [location for location in locations if location.event and
(not key_only or (not self.world.keyshuffle[location.item.player] and location.item.smallkey) or (not self.world.bigkeyshuffle[location.item.player] and location.item.bigkey)) (not key_only or (self.world.keyshuffle[location.item.player] == 'none' and location.item.smallkey) or (not self.world.bigkeyshuffle[location.item.player] and location.item.bigkey))
and location.can_reach(self)] and location.can_reach(self)]
reachable_events = self._do_not_flood_the_keys(reachable_events) reachable_events = self._do_not_flood_the_keys(reachable_events)
for event in reachable_events: for event in reachable_events:
@@ -995,7 +999,7 @@ class CollectionState(object):
return self.prog_items[item, player] >= count return self.prog_items[item, player] >= count
def has_sm_key(self, item, player, count=1): def has_sm_key(self, item, player, count=1):
if self.world.retro[player]: if self.world.keyshuffle[player] == 'universal':
if self.world.mode[player] == 'standard' and self.world.doorShuffle[player] == 'vanilla' and item == 'Small Key (Escape)': if self.world.mode[player] == 'standard' and self.world.doorShuffle[player] == 'vanilla' and item == 'Small Key (Escape)':
return True # Cannot access the shop until escape is finished. This is safe because the key is manually placed in make_custom_item_pool return True # Cannot access the shop until escape is finished. This is safe because the key is manually placed in make_custom_item_pool
return self.can_buy_unlimited('Small Key (Universal)', player) return self.can_buy_unlimited('Small Key (Universal)', player)
@@ -1091,7 +1095,7 @@ class CollectionState(object):
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.retro[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) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player))
return self.has('Bow', player) return self.has('Bow', player)
@@ -1373,7 +1377,7 @@ class Region(object):
return False return False
def can_fill(self, item): def can_fill(self, item):
inside_dungeon_item = ((item.smallkey and not self.world.keyshuffle[item.player]) inside_dungeon_item = ((item.smallkey and self.world.keyshuffle[item.player] == 'none')
or (item.bigkey and not self.world.bigkeyshuffle[item.player]) or (item.bigkey and not self.world.bigkeyshuffle[item.player])
or (item.map and not self.world.mapshuffle[item.player]) or (item.map and not self.world.mapshuffle[item.player])
or (item.compass and not self.world.compassshuffle[item.player])) or (item.compass and not self.world.compassshuffle[item.player]))
@@ -2239,7 +2243,7 @@ class Item(object):
return item_dungeon return item_dungeon
def is_inside_dungeon_item(self, world): def is_inside_dungeon_item(self, world):
return ((self.smallkey and not world.keyshuffle[self.player]) return ((self.smallkey and world.keyshuffle[self.player] == 'none')
or (self.bigkey and not world.bigkeyshuffle[self.player]) or (self.bigkey and not world.bigkeyshuffle[self.player])
or (self.compass and not world.compassshuffle[self.player]) or (self.compass and not world.compassshuffle[self.player])
or (self.map and not world.mapshuffle[self.player])) or (self.map and not world.mapshuffle[self.player]))
@@ -2371,15 +2375,16 @@ class Spoiler(object):
self.metadata = {'version': ERVersion, self.metadata = {'version': ERVersion,
'logic': self.world.logic, 'logic': self.world.logic,
'mode': self.world.mode, 'mode': self.world.mode,
'retro': self.world.retro,
'bombbag': self.world.bombbag, 'bombbag': self.world.bombbag,
'weapons': self.world.swords, 'weapons': self.world.swords,
'flute_mode': self.world.flute_mode, 'flute_mode': self.world.flute_mode,
'bow_mode': self.world.bow_mode,
'goal': self.world.goal, 'goal': self.world.goal,
'shuffle': self.world.shuffle, 'shuffle': self.world.shuffle,
'shuffleganon': self.world.shuffle_ganon, 'shuffleganon': self.world.shuffle_ganon,
'shufflelinks': self.world.shufflelinks, 'shufflelinks': self.world.shufflelinks,
'shuffletavern': self.world.shuffletavern, 'shuffletavern': self.world.shuffletavern,
'take_any': self.world.take_any,
'overworld_map': self.world.overworld_map, 'overworld_map': self.world.overworld_map,
'door_shuffle': self.world.doorShuffle, 'door_shuffle': self.world.doorShuffle,
'intensity': self.world.intensity, 'intensity': self.world.intensity,
@@ -2561,7 +2566,6 @@ class Spoiler(object):
outfile.write(f'Settings Code: {self.metadata["code"][player]}\n') outfile.write(f'Settings Code: {self.metadata["code"][player]}\n')
outfile.write('Logic: %s\n' % self.metadata['logic'][player]) outfile.write('Logic: %s\n' % self.metadata['logic'][player])
outfile.write('Mode: %s\n' % self.metadata['mode'][player]) outfile.write('Mode: %s\n' % self.metadata['mode'][player])
outfile.write('Retro: %s\n' % ('Yes' if self.metadata['retro'][player] else 'No'))
outfile.write('Swords: %s\n' % self.metadata['weapons'][player]) outfile.write('Swords: %s\n' % self.metadata['weapons'][player])
outfile.write('Goal: %s\n' % self.metadata['goal'][player]) outfile.write('Goal: %s\n' % self.metadata['goal'][player])
if self.metadata['goal'][player] in ['triforcehunt', 'trinity']: if self.metadata['goal'][player] in ['triforcehunt', 'trinity']:
@@ -2574,6 +2578,7 @@ class Spoiler(object):
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player]) outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player]) outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
outfile.write(f"Flute Mode: {self.metadata['flute_mode'][player]}\n") outfile.write(f"Flute Mode: {self.metadata['flute_mode'][player]}\n")
outfile.write(f"Bow Mode: {self.metadata['bow_mode'][player]}\n")
outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n") outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n")
outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n") outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n")
outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n") outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n")
@@ -2583,6 +2588,8 @@ class Spoiler(object):
outfile.write(f"Back of Tavern Shuffled: {yn(self.metadata['shuffletavern'][player])}\n") outfile.write(f"Back of Tavern Shuffled: {yn(self.metadata['shuffletavern'][player])}\n")
outfile.write(f"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\n") outfile.write(f"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\n")
outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n") outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n")
outfile.write(f"Take Any Caves: {self.metadata['take_any'][player]}\n")
outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n")
if self.metadata['goal'][player] != 'trinity': if self.metadata['goal'][player] != 'trinity':
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No')) outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player]) outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
@@ -2597,7 +2604,7 @@ class Spoiler(object):
outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n") outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n")
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No')) outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No')) outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No'))
outfile.write('Small Key shuffle: %s\n' % ('Yes' if self.metadata['keyshuffle'][player] else 'No')) outfile.write(f"Small Key shuffle: {self.metadata['keyshuffle'][player]}\n")
outfile.write('Big Key shuffle: %s\n' % ('Yes' if self.metadata['bigkeyshuffle'][player] else 'No')) outfile.write('Big Key shuffle: %s\n' % ('Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player]) outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player]) outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
@@ -2835,7 +2842,7 @@ dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3}
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8, er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6} 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6}
# byte 1: LLLW WSSR (logic, mode, sword, retro) # 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}
@@ -2861,7 +2868,7 @@ counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3}
# byte 6: CCCC CPAA (crystals ganon, pyramid, access # byte 6: CCCC CPAA (crystals ganon, pyramid, access
access_mode = {"items": 0, "locations": 1, "none": 2} access_mode = {"items": 0, "locations": 1, "none": 2}
# byte 7: BSMC DDEE (big, small, maps, compass, door_type, enemies) # byte 7: B?MC DDEE (big, ?, maps, compass, door_type, enemies)
door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3} door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3}
enemy_mode = {"none": 0, "shuffled": 1, "chaos": 2, "random": 2, "legacy": 3} enemy_mode = {"none": 0, "shuffled": 1, "chaos": 2, "random": 2, "legacy": 3}
@@ -2878,15 +2885,18 @@ boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique
# byte 10: settings_version # byte 10: settings_version
# byte 11: F???, ???? (flute_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}
keyshuffle_mode = {'none': 0, 'wild': 1, 'universal': 2} # reserved 8 modes?
take_any_mode = {'none': 0, 'random': 1, 'fixed': 2}
bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silver': 3}
# additions # additions
# psuedoboots does not effect code # psuedoboots does not effect code
# sfx_shuffle and other adjust items does not effect settings code # sfx_shuffle and other adjust items does not effect settings code
# Bump this when making changes that are not backwards compatible (nearly all of them) # Bump this when making changes that are not backwards compatible (nearly all of them)
settings_version = 0 settings_version = 1
class Settings(object): class Settings(object):
@@ -2897,7 +2907,7 @@ class Settings(object):
(dr_mode[w.doorShuffle[p]] << 5) | er_mode[w.shuffle[p]], (dr_mode[w.doorShuffle[p]] << 5) | er_mode[w.shuffle[p]],
(logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3) (logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3)
| (sword_mode[w.swords[p]] << 1) | (1 if w.retro[p] else 0), | (sword_mode[w.swords[p]] << 1),
(goal_mode[w.goal[p]] << 5) | (diff_mode[w.difficulty[p]] << 3) (goal_mode[w.goal[p]] << 5) | (diff_mode[w.difficulty[p]] << 3)
| (func_mode[w.difficulty_adjustments[p]] << 1) | (1 if w.hints[p] else 0), | (func_mode[w.difficulty_adjustments[p]] << 1) | (1 if w.hints[p] else 0),
@@ -2915,7 +2925,7 @@ class Settings(object):
((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3) ((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3)
| (0x4 if w.open_pyramid[p] else 0) | access_mode[w.accessibility[p]], | (0x4 if w.open_pyramid[p] else 0) | access_mode[w.accessibility[p]],
(0x80 if w.bigkeyshuffle[p] else 0) | (0x40 if w.keyshuffle[p] else 0) (0x80 if w.bigkeyshuffle[p] else 0)
| (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0) | (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0)
| (door_type_mode[w.door_type_mode[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]), | (door_type_mode[w.door_type_mode[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
@@ -2926,14 +2936,16 @@ class Settings(object):
settings_version, settings_version,
flute_mode[w.flute_mode[p]] << 7]) (flute_mode[w.flute_mode[p]] << 7 | bow_mode[w.bow_mode[p]] << 4
| take_any_mode[w.take_any[p]] << 2 | keyshuffle_mode[w.keyshuffle[p]])
])
return base64.b64encode(code, "+-".encode()).decode() return base64.b64encode(code, "+-".encode()).decode()
@staticmethod @staticmethod
def adjust_args_from_code(code, player, args): def adjust_args_from_code(code, player, args):
settings, p = base64.b64decode(code.encode(), "+-".encode()), player settings, p = base64.b64decode(code.encode(), "+-".encode()), player
if len(settings) < 11: if len(settings) < 12:
raise Exception('Provided code is incompatible with this version') raise Exception('Provided code is incompatible with this version')
if settings[10] != settings_version: if settings[10] != settings_version:
raise Exception('Provided code is incompatible with this version') raise Exception('Provided code is incompatible with this version')
@@ -2950,7 +2962,7 @@ class Settings(object):
args.item_functionality[p] = r(func_mode)[(settings[2] & 0x6) >> 1] args.item_functionality[p] = r(func_mode)[(settings[2] & 0x6) >> 1]
args.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5] args.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5]
args.accessibility[p] = r(access_mode)[settings[6] & 0x3] args.accessibility[p] = r(access_mode)[settings[6] & 0x3]
args.retro[p] = True if settings[1] & 0x01 else False # args.retro[p] = True if settings[1] & 0x01 else False
args.hints[p] = True if settings[2] & 0x01 else False args.hints[p] = True if settings[2] & 0x01 else False
args.shopsanity[p] = True if settings[3] & 0x80 else False args.shopsanity[p] = True if settings[3] & 0x80 else False
args.decoupledoors[p] = True if settings[3] & 0x40 else False args.decoupledoors[p] = True if settings[3] & 0x40 else False
@@ -2973,7 +2985,7 @@ class Settings(object):
args.openpyramid[p] = True if settings[6] & 0x4 else False args.openpyramid[p] = True if settings[6] & 0x4 else False
args.bigkeyshuffle[p] = True if settings[7] & 0x80 else False args.bigkeyshuffle[p] = True if settings[7] & 0x80 else False
args.keyshuffle[p] = True if settings[7] & 0x40 else False # args.keyshuffle[p] = True if settings[7] & 0x40 else False
args.mapshuffle[p] = True if settings[7] & 0x20 else False args.mapshuffle[p] = True if settings[7] & 0x20 else False
args.compassshuffle[p] = True if settings[7] & 0x10 else False args.compassshuffle[p] = True if settings[7] & 0x10 else False
args.door_type_mode[p] = r(door_type_mode)[(settings[7] & 0xc) >> 2] args.door_type_mode[p] = r(door_type_mode)[(settings[7] & 0xc) >> 2]
@@ -2990,6 +3002,9 @@ class Settings(object):
args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)] args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)]
if len(settings) > 11: if len(settings) > 11:
args.flute_mode[p] = r(flute_mode)[(settings[11] & 0x80) >> 7] args.flute_mode[p] = r(flute_mode)[(settings[11] & 0x80) >> 7]
args.bow_mode[p] = r(bow_mode)[(settings[11] & 0x70) >> 4]
args.take_any[p] = r(take_any_mode)[(settings[11] & 0xC) >> 2]
args.keyshuffle[p] = r(keyshuffle_mode)[settings[11] & 0x3]
class KeyRuleType(FastEnum): class KeyRuleType(FastEnum):
+18 -7
View File
@@ -103,19 +103,28 @@ def parse_cli(argv, no_defaults=False):
ret = parser.parse_args(argv) ret = parser.parse_args(argv)
if ret.keysanity: if ret.keysanity:
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = [True] * 4 ret.mapshuffle, ret.compassshuffle, ret.bigkeyshuffle = [True] * 3
ret.keyshuffle = 'wild'
if ret.keydropshuffle: if ret.keydropshuffle:
ret.dropshuffle = True ret.dropshuffle = True
ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery
if ret.retro or ret.mode == 'retro':
if ret.bow_mode == 'progressive':
ret.bow_mode = 'retro'
elif ret.bow_mode == 'silvers':
ret.bow_mode = 'retro_silvers'
ret.take_any = 'random' if ret.take_any == 'none' else ret.take_any
ret.keyshuffle = 'universal'
if player_num: if player_num:
defaults = copy.deepcopy(ret) defaults = copy.deepcopy(ret)
for player in range(1, player_num + 1): for player in range(1, player_num + 1):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True) playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'flute_mode', 'flute_mode', 'bow_mode', 'take_any',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items', 'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items',
@@ -158,6 +167,7 @@ def parse_settings():
"crystals_ganon": "7", "crystals_ganon": "7",
"swords": "random", "swords": "random",
'flute_mode': 'normal', 'flute_mode': 'normal',
'bow_mode': 'progressive',
"difficulty": "normal", "difficulty": "normal",
"item_functionality": "normal", "item_functionality": "normal",
"timer": "none", "timer": "none",
@@ -175,6 +185,7 @@ def parse_settings():
"shufflelinks": False, "shufflelinks": False,
"shuffletavern": False, "shuffletavern": False,
"overworld_map": "default", "overworld_map": "default",
'take_any': 'none',
"pseudoboots": False, "pseudoboots": False,
"shuffleenemies": "none", "shuffleenemies": "none",
@@ -189,11 +200,11 @@ def parse_settings():
'pottery': 'none', 'pottery': 'none',
'colorizepots': False, 'colorizepots': False,
'shufflepots': False, 'shufflepots': False,
"mapshuffle": False, 'mapshuffle': False,
"compassshuffle": False, 'compassshuffle': False,
"keyshuffle": False, 'keyshuffle': 'none',
"bigkeyshuffle": False, 'bigkeyshuffle': False,
"keysanity": False, 'keysanity': False,
"door_shuffle": "basic", "door_shuffle": "basic",
"intensity": 2, "intensity": 2,
'door_type_mode': 'original', 'door_type_mode': 'original',
+7 -7
View File
@@ -470,7 +470,7 @@ def choose_portals(world, player):
for dungeon, info in shuffled_info: for dungeon, info in shuffled_info:
outstanding_portals = list(dungeon_portals[dungeon]) outstanding_portals = list(dungeon_portals[dungeon])
hc_flag = std_flag and dungeon == 'Hyrule Castle' hc_flag = std_flag and dungeon == 'Hyrule Castle'
rupee_bow_flag = hc_flag and world.retro[player] # rupee bow rupee_bow_flag = hc_flag and world.bow_mode[player].startswith('retro') # rupee bow
if hc_flag: if hc_flag:
sanc = world.get_portal('Sanctuary', player) sanc = world.get_portal('Sanctuary', player)
sanc.destination = True sanc.destination = True
@@ -843,7 +843,7 @@ 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)) 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 target_items = 34
if world.retro[player]: 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] else 0 # the hc big key
else: else:
target_items += 29 # small keys in chests target_items += 29 # small keys in chests
@@ -1248,7 +1248,7 @@ def cross_dungeon(world, player):
assign_cross_keys(dungeon_builders, world, player) assign_cross_keys(dungeon_builders, world, player)
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) 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 target_items = 34
if world.retro[player]: 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] else 0 # the hc big key
else: else:
target_items += 29 # small keys in chests target_items += 29 # small keys in chests
@@ -1336,7 +1336,7 @@ def filter_key_door_pool(pool, selected_custom):
def assign_cross_keys(dungeon_builders, world, player): def assign_cross_keys(dungeon_builders, world, player):
logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors")) logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors"))
start = time.process_time() start = time.process_time()
if world.retro[player]: if world.keyshuffle[player] == 'universal':
remaining = 29 remaining = 29
if world.dropshuffle[player]: if world.dropshuffle[player]:
remaining += 13 remaining += 13
@@ -1424,7 +1424,7 @@ def assign_cross_keys(dungeon_builders, world, player):
# Last Step: Adjust Small Key Dungeon Pool # Last Step: Adjust Small Key Dungeon Pool
for name, builder in dungeon_builders.items(): for name, builder in dungeon_builders.items():
reassign_key_doors(builder, world, player) reassign_key_doors(builder, world, player)
if not world.retro[player]: if world.keyshuffle[player] != 'universal':
log_key_logic(builder.name, world.key_logic[player][builder.name]) log_key_logic(builder.name, world.key_logic[player][builder.name])
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0) actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
dungeon = world.get_dungeon(name, player) dungeon = world.get_dungeon(name, player)
@@ -1963,7 +1963,7 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, worl
# time to re-assign # time to re-assign
reassign_key_doors(small_map, world, player) reassign_key_doors(small_map, world, player)
for dungeon_name in pool: for dungeon_name in pool:
if not world.retro[player]: if world.keyshuffle[player] != 'universal':
builder = world.dungeon_layouts[player][dungeon_name] builder = world.dungeon_layouts[player][dungeon_name]
log_key_logic(builder.name, world.key_logic[player][builder.name]) log_key_logic(builder.name, world.key_logic[player][builder.name])
if world.doorShuffle[player] != 'basic': if world.doorShuffle[player] != 'basic':
@@ -2470,7 +2470,7 @@ def reassign_big_key_doors(bk_map, world, player):
pass # we don't have spiral stairs candidates yet that aren't already key doors pass # we don't have spiral stairs candidates yet that aren't already key doors
elif d.type is DoorType.Normal: elif d.type is DoorType.Normal:
change_door_to_big_key(d, world, player) change_door_to_big_key(d, world, player)
if not world.decoupledoors[player] and d.dest: if not world.decoupledoors[player] and d.dest and world.door_type_mode[player] != 'original':
if d.dest.type in [DoorType.Normal]: if d.dest.type in [DoorType.Normal]:
dest_room = world.get_room(d.dest.roomIndex, player) dest_room = world.get_room(d.dest.roomIndex, player)
if stateful_door(d.dest, dest_room.kind(d.dest)): if stateful_door(d.dest, dest_room.kind(d.dest)):
+1 -1
View File
@@ -1355,7 +1355,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge
sanc_builder = random.choice(lw_builders) sanc_builder = random.choice(lw_builders)
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole) assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
bow_sectors, retro_std_flag = {}, world.retro[player] and world.mode[player] == 'standard' bow_sectors, retro_std_flag = {}, world.bow_mode[player].startswith('retro') and world.mode[player] == 'standard'
free_location_sectors = {} free_location_sectors = {}
crystal_switches = {} crystal_switches = {}
crystal_barriers = {} crystal_barriers = {}
+1 -1
View File
@@ -5,7 +5,7 @@ from Items import ItemFactory
def create_dungeons(world, player): def create_dungeons(world, player):
def make_dungeon(name, id, default_boss, dungeon_regions, big_key, small_keys, dungeon_items): def make_dungeon(name, id, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro[player] else small_keys, dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.keyshuffle[player] == 'universal' else small_keys,
dungeon_items, player, id) dungeon_items, player, id)
dungeon.boss = BossFactory(default_boss, player) dungeon.boss = BossFactory(default_boss, player)
for region in dungeon.regions: for region in dungeon.regions:
+13 -9
View File
@@ -39,7 +39,8 @@ def fill_dungeons_restrictive(world, shuffled_locations):
# with shuffled dungeon items they are distributed as part of the normal item pool # with shuffled dungeon items they are distributed as part of the normal item pool
for item in world.get_items(): for item in world.get_items():
if (item.smallkey and world.keyshuffle[item.player]) or (item.bigkey and world.bigkeyshuffle[item.player]): if ((item.smallkey and world.keyshuffle[item.player] != 'none')
or (item.bigkey and world.bigkeyshuffle[item.player])):
item.advancement = True item.advancement = True
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]): elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
item.priority = True item.priority = True
@@ -50,7 +51,7 @@ def fill_dungeons_restrictive(world, shuffled_locations):
(bigs if i.bigkey else smalls if i.smallkey else others).append(i) (bigs if i.bigkey else smalls if i.smallkey else others).append(i)
unplaced_smalls = list(smalls) unplaced_smalls = list(smalls)
for i in world.itempool: for i in world.itempool:
if i.smallkey and world.keyshuffle[i.player]: if i.smallkey and world.keyshuffle[i.player] != 'none':
unplaced_smalls.append(i) unplaced_smalls.append(i)
def fill(base_state, items, key_pool): def fill(base_state, items, key_pool):
@@ -160,7 +161,7 @@ def valid_key_placement(item, location, key_pool, world):
if not valid_reserved_placement(item, location, world): if not valid_reserved_placement(item, location, world):
return False return False
if ((not item.smallkey and not item.bigkey) or item.player != location.player if ((not item.smallkey and not item.bigkey) or item.player != location.player
or world.retro[item.player] or world.logic[item.player] == 'nologic'): or world.keyshuffle[item.player] == 'universal' or world.logic[item.player] == 'nologic'):
return True return True
dungeon = location.parent_region.dungeon dungeon = location.parent_region.dungeon
if dungeon: if dungeon:
@@ -215,7 +216,7 @@ def track_dungeon_items(item, location, world):
def is_dungeon_item(item, world): def is_dungeon_item(item, world):
return ((item.smallkey and not world.keyshuffle[item.player]) return ((item.smallkey and world.keyshuffle[item.player] == 'none')
or (item.bigkey and not world.bigkeyshuffle[item.player]) or (item.bigkey and not world.bigkeyshuffle[item.player])
or (item.compass and not world.compassshuffle[item.player]) or (item.compass and not world.compassshuffle[item.player])
or (item.map and not world.mapshuffle[item.player])) or (item.map and not world.mapshuffle[item.player]))
@@ -419,7 +420,8 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots # Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
# todo: crossed # todo: crossed
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0) progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)'
and world.keyshuffle[item.player] != 'none' and world.mode[item.player] == 'standard' else 0)
key_pool = [x for x in progitempool if x.smallkey] key_pool = [x for x in progitempool if x.smallkey]
# sort maps and compasses to the back -- this may not be viable in equitable & ambrosia # sort maps and compasses to the back -- this may not be viable in equitable & ambrosia
@@ -498,7 +500,7 @@ def ensure_good_pots(world, write_skips=False):
else: else:
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player)
# do the arrow retro check # do the arrow retro check
if world.retro[loc.item.player] and loc.item.name in {'Arrows (5)', 'Arrows (10)'}: 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) loc.item = ItemFactory('Rupees (5)', loc.item.player)
# don't write out all pots to spoiler # don't write out all pots to spoiler
if write_skips: if write_skips:
@@ -663,7 +665,7 @@ def balance_multiworld_progression(world):
candidate_items = collections.defaultdict(set) candidate_items = collections.defaultdict(set)
while True: while True:
for location in balancing_sphere: for location in balancing_sphere:
if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey): if location.event and (world.keyshuffle[location.item.player] != 'none' or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
balancing_state.collect(location.item, True, location) balancing_state.collect(location.item, True, location)
player = location.item.player player = location.item.player
if player in balancing_players and not location.locked and location.player != player: if player in balancing_players and not location.locked and location.player != player:
@@ -738,7 +740,7 @@ def balance_multiworld_progression(world):
sphere_locations.add(location) sphere_locations.add(location)
for location in sphere_locations: for location in sphere_locations:
if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey): if location.event and (world.keyshuffle[location.item.player] != 'none' or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
state.collect(location.item, True, location) state.collect(location.item, True, location)
checked_locations |= sphere_locations checked_locations |= sphere_locations
@@ -812,7 +814,9 @@ def balance_money_progression(world):
return True return True
if item.name in ['Progressive Armor', 'Blue Mail', 'Red Mail']: if item.name in ['Progressive Armor', 'Blue Mail', 'Red Mail']:
return True return True
if world.retro[player] and (item.name in ['Single Arrow', 'Small Key (Universal)']): if world.keyshuffle[player] == 'universal' and item.name == 'Small Key (Universal)':
return True
if world.bow_mode[player].startswith('retro') and item.name == 'Single Arrow':
return True return True
if location.name in pay_for_locations: if location.name in pay_for_locations:
return True return True
+2 -2
View File
@@ -73,7 +73,7 @@ class InitialSram:
if startingstate.has('Bow', player): if startingstate.has('Bow', player):
equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1 equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1
equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases
if not world.retro[player]: if not world.bow_mode[player].startswith('retro'):
equip[0x38E] |= 0x80 equip[0x38E] |= 0x80
if startingstate.has('Silver Arrows', player): if startingstate.has('Silver Arrows', player):
equip[0x38E] |= 0x40 equip[0x38E] |= 0x40
@@ -188,7 +188,7 @@ class InitialSram:
elif item.name in bombs: elif item.name in bombs:
starting_bombs += bombs[item.name] starting_bombs += bombs[item.name]
elif item.name in arrows: elif item.name in arrows:
if world.retro[player]: if world.bow_mode[player].startswith('retro'):
equip[0x38E] |= 0x80 equip[0x38E] |= 0x80
starting_arrows = 1 starting_arrows = 1
else: else:
+81 -60
View File
@@ -264,10 +264,10 @@ def generate_itempool(world, player):
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = make_customizer_pool(world, player) (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = make_customizer_pool(world, player)
skip_pool_adjustments = True skip_pool_adjustments = True
elif world.custom: elif world.custom:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray) (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world, player, world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.bombbag[player], world.customitemarray)
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
else: else:
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.flute_mode[player] == 'active') (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world, player, world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.flute_mode[player] == 'active')
if player in world.pool_adjustment.keys() and not skip_pool_adjustments: if player in world.pool_adjustment.keys() and not skip_pool_adjustments:
amt = world.pool_adjustment[player] amt = world.pool_adjustment[player]
@@ -295,7 +295,8 @@ def generate_itempool(world, player):
if not found_sword and world.swords[player] != 'swordless': if not found_sword and world.swords[player] != 'swordless':
found_sword = True found_sword = True
possible_weapons.append(item) possible_weapons.append(item)
if item in ['Progressive Bow', 'Bow'] and not found_bow and not world.retro[player]: if (item in ['Progressive Bow', 'Bow'] and not found_bow
and not world.bow_mode[player].startswith('retro')):
found_bow = True found_bow = True
possible_weapons.append(item) possible_weapons.append(item)
if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
@@ -349,7 +350,7 @@ def generate_itempool(world, player):
world.treasure_hunt_icon[player] = 'Triforce Piece' world.treasure_hunt_icon[player] = 'Triforce Piece'
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
and ((item.smallkey and world.keyshuffle[player]) and ((item.smallkey and world.keyshuffle[player] != 'none')
or (item.bigkey and world.bigkeyshuffle[player]) or (item.bigkey and world.bigkeyshuffle[player])
or (item.map and world.mapshuffle[player]) or (item.map and world.mapshuffle[player])
or (item.compass and world.compassshuffle[player]))]) or (item.compass and world.compassshuffle[player]))])
@@ -409,8 +410,9 @@ def generate_itempool(world, player):
set_up_shops(world, player) set_up_shops(world, player)
if world.retro[player]: if world.take_any[player] != 'none':
set_up_take_anys(world, player, skip_pool_adjustments) 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] and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13 world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13
if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments: if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments:
@@ -432,6 +434,10 @@ take_any_locations = [
'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint', 'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint',
'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint'] 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint']
fixed_take_anys = [
'Desert Healer Fairy', 'Swamp Healer Fairy', 'Dark Death Mountain Healer Fairy',
'Dark Lake Hylia Ledge Healer Fairy', 'Bonk Fairy (Dark)']
def set_up_take_anys(world, player, skip_adjustments=False): def set_up_take_anys(world, player, skip_adjustments=False):
if world.mode[player] == 'inverted': if world.mode[player] == 'inverted':
@@ -440,9 +446,12 @@ def set_up_take_anys(world, player, skip_adjustments=False):
if 'Archery Game' in take_any_locations: if 'Archery Game' in take_any_locations:
take_any_locations.remove('Archery Game') take_any_locations.remove('Archery Game')
if world.take_any[player] == 'random':
take_any_candidates = [x for x in take_any_locations if len(world.get_region(x, player).locations) == 0] take_any_candidates = [x for x in take_any_locations if len(world.get_region(x, player).locations) == 0]
regions = random.sample(take_any_candidates, 5) regions = random.sample(take_any_candidates, 5)
elif world.take_any[player] == 'fixed':
regions = list(fixed_take_anys)
random.shuffle(regions)
old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player)
world.regions.append(old_man_take_any) world.regions.append(old_man_take_any)
@@ -539,38 +548,47 @@ def fill_prizes(world, attempts=15):
def set_up_shops(world, player): def set_up_shops(world, player):
if world.retro[player]: retro_bow = world.bow_mode[player].startswith('retro')
universal_keys = world.keyshuffle[player] == 'universal'
if retro_bow or universal_keys:
if world.shopsanity[player]: if world.shopsanity[player]:
removals = []
if retro_bow:
removals = [next(item for item in world.itempool if item.name == 'Arrows (10)' and item.player == player)] removals = [next(item for item in world.itempool if item.name == 'Arrows (10)' and item.player == player)]
red_pots = [item for item in world.itempool if item.name == 'Red Potion' and item.player == player][:5]
shields_n_hearts = [item for item in world.itempool if item.name in ['Blue Shield', 'Small Heart'] and item.player == player]
removals.extend([item for item in world.itempool if item.name == 'Arrow Upgrade (+5)' and item.player == player]) removals.extend([item for item in world.itempool if item.name == 'Arrow Upgrade (+5)' and item.player == player])
removals.extend(red_pots) shields_n_hearts = [item for item in world.itempool if item.name in ['Blue Shield', 'Small Heart'] and item.player == player]
removals.extend(random.sample(shields_n_hearts, 5)) removals.extend(random.sample(shields_n_hearts, 5))
if universal_keys:
red_pots = [item for item in world.itempool if item.name == 'Red Potion' and item.player == player][:5]
removals.extend(red_pots)
for remove in removals: for remove in removals:
world.itempool.remove(remove) world.itempool.remove(remove)
if retro_bow:
for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield
arrow_item = ItemFactory('Single Arrow', player) arrow_item = ItemFactory('Single Arrow', player)
arrow_item.advancement = True arrow_item.advancement = True
world.itempool.append(arrow_item) world.itempool.append(arrow_item)
world.itempool.append(ItemFactory('Rupees (50)', player)) # replaces the arrow upgrade
if universal_keys:
for i in range(5): # replace the red potions for i in range(5): # replace the red potions
world.itempool.append(ItemFactory('Small Key (Universal)', player)) world.itempool.append(ItemFactory('Small Key (Universal)', player))
world.itempool.append(ItemFactory('Rupees (50)', player)) # replaces the arrow upgrade
# TODO: move hard+ mode changes for shields here, utilizing the new shops # TODO: move hard+ mode changes for shields here, utilizing the new shops
else: else:
if retro_bow:
rss = world.get_region('Red Shield Shop', player).shop rss = world.get_region('Red Shield Shop', player).shop
if not rss.locked: if not rss.locked:
rss.custom = True rss.custom = True
rss.add_inventory(2, 'Single Arrow', 80) rss.add_inventory(2, 'Single Arrow', 80)
for shop in random.sample([s for s in world.shops[player] if not s.locked and s.region.player == player], 5):
shop.custom = True
shop.locked = True
shop.add_inventory(0, 'Single Arrow', 80)
shop.add_inventory(1, 'Small Key (Universal)', 100)
shop.add_inventory(2, 'Bombs (10)', 50)
rss.locked = True rss.locked = True
cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop = world.get_region('Capacity Upgrade', player).shop
cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro
for shop in random.sample([s for s in world.shops[player] if not s.locked and s.region.player == player], 5):
shop.custom = True
shop.locked = True
if retro_bow:
shop.add_inventory(0, 'Single Arrow', 80)
if universal_keys:
shop.add_inventory(1, 'Small Key (Universal)', 100)
if world.bombbag[player]: if world.bombbag[player]:
if world.shopsanity[player]: if world.shopsanity[player]:
removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player] removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player]
@@ -583,10 +601,11 @@ def set_up_shops(world, player):
def customize_shops(world, player): def customize_shops(world, player):
found_bomb_upgrade, found_arrow_upgrade = False, world.retro[player] retro_bow = world.bow_mode[player].startswith('retro')
found_bomb_upgrade, found_arrow_upgrade = False, retro_bow
possible_replacements = [] possible_replacements = []
shops_to_customize = shop_to_location_table.copy() shops_to_customize = shop_to_location_table.copy()
if world.retro[player]: if world.take_any[player] != 'none':
shops_to_customize.update(retro_shops) shops_to_customize.update(retro_shops)
for shop_name, loc_list in shops_to_customize.items(): for shop_name, loc_list in shops_to_customize.items():
shop = world.get_region(shop_name, player).shop shop = world.get_region(shop_name, player).shop
@@ -609,7 +628,7 @@ def customize_shops(world, player):
price = 0 price = 0
else: else:
price = 120 if shop_name == 'Potion Shop' and item.name == 'Red Potion' else item.price price = 120 if shop_name == 'Potion Shop' and item.name == 'Red Potion' else item.price
if world.retro[player] and item.name == 'Single Arrow': if retro_bow and item.name == 'Single Arrow':
price = 80 price = 80
# randomize price # randomize price
shop.add_inventory(idx, item.name, randomize_price(price), max_repeat, player=item.player) shop.add_inventory(idx, item.name, randomize_price(price), max_repeat, player=item.player)
@@ -636,7 +655,7 @@ def customize_shops(world, player):
if not found_arrow_upgrade and len(possible_replacements) > 0: if not found_arrow_upgrade and len(possible_replacements) > 0:
choices = [] choices = []
for shop, idx, loc, item in possible_replacements: for shop, idx, loc, item in possible_replacements:
if item.name == 'Arrows (10)' or (item.name == 'Single Arrow' and not world.retro[player]): if item.name == 'Arrows (10)' or (item.name == 'Single Arrow' and not retro_bow):
choices.append((shop, idx, loc, item)) choices.append((shop, idx, loc, item))
if len(choices) > 0: if len(choices) > 0:
shop, idx, loc, item = random.choice(choices) shop, idx, loc, item = random.choice(choices)
@@ -780,16 +799,17 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)'
def add_pot_contents(world, player): def add_pot_contents(world, player):
retro_bow = world.bow_mode[player].startswith('retro')
for super_tile, pot_list in vanilla_pots.items(): for super_tile, pot_list in vanilla_pots.items():
for pot in pot_list: for pot in pot_list:
if pot.item not in [PotItem.Hole, PotItem.Key, PotItem.Switch]: if pot.item not in [PotItem.Hole, PotItem.Key, PotItem.Switch]:
if valid_pot_location(pot, world.pot_pool[player], world, player): if valid_pot_location(pot, world.pot_pool[player], world, player):
item = ('Rupees (5)' if world.retro[player] and pot_items[pot.item] == 'Arrows (5)' item = ('Rupees (5)' if retro_bow and pot_items[pot.item] == 'Arrows (5)'
else pot_items[pot.item]) else pot_items[pot.item])
world.itempool.append(ItemFactory(item, player)) world.itempool.append(ItemFactory(item, player))
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, bombbag,
door_shuffle, logic, flute_activated): door_shuffle, logic, flute_activated):
pool = [] pool = []
placed_items = {} placed_items = {}
@@ -862,7 +882,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
else: else:
pool.extend(diff.basicarmor) pool.extend(diff.basicarmor)
if want_progressives(): if 'silvers' not in world.bow_mode[player]:
pool.extend(['Progressive Bow'] * 2) pool.extend(['Progressive Bow'] * 2)
elif swords != 'swordless': elif swords != 'swordless':
pool.extend(diff.basicbow) pool.extend(diff.basicbow)
@@ -910,13 +930,14 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
if goal in ['pedestal', 'trinity'] and swords != 'vanilla': if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
place_item('Master Sword Pedestal', 'Triforce') place_item('Master Sword Pedestal', 'Triforce')
if retro: if world.bow_mode[player].startswith('retro'):
pool = [item.replace('Single Arrow', 'Rupees (5)') for item in pool] pool = [item.replace('Single Arrow', 'Rupees (5)') for item in pool]
pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool] pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool]
pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool] pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool]
pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool] pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool]
if world.keyshuffle[player] == 'universal':
pool.extend(diff.retro) pool.extend(diff.retro)
if door_shuffle != 'vanilla': # door shuffle needs more keys for retro if door_shuffle != 'vanilla': # door shuffle needs more keys for universal keys
replace = 'Rupees (20)' if difficulty == 'normal' else 'Rupees (5)' replace = 'Rupees (20)' if difficulty == 'normal' else 'Rupees (5)'
indices = [i for i, x in enumerate(pool) if x == replace] indices = [i for i, x in enumerate(pool) if x == replace]
for i in range(0, min(10, len(indices))): for i in range(0, min(10, len(indices))):
@@ -932,7 +953,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag, customitemarray): def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer, goal, mode, swords, bombbag, customitemarray):
if isinstance(customitemarray,dict) and 1 in customitemarray: if isinstance(customitemarray,dict) and 1 in customitemarray:
customitemarray = customitemarray[1] customitemarray = customitemarray[1]
pool = [] pool = []
@@ -1014,7 +1035,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
itemtotal = itemtotal + 1 itemtotal = itemtotal + 1
if mode == 'standard': if mode == 'standard':
if retro: if world.keyshuffle[player] == 'universal':
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
place_item(key_location, 'Small Key (Universal)') place_item(key_location, 'Small Key (Universal)')
pool.extend(['Small Key (Universal)'] * max((customitemarray["generickeys"] - 1), 0)) pool.extend(['Small Key (Universal)'] * max((customitemarray["generickeys"] - 1), 0))
@@ -1028,7 +1049,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
pool.extend(['Magic Mirror'] * customitemarray["mirror"]) pool.extend(['Magic Mirror'] * customitemarray["mirror"])
pool.extend(['Moon Pearl'] * customitemarray["pearl"]) pool.extend(['Moon Pearl'] * customitemarray["pearl"])
if retro: if world.keyshuffle[player] == 'universal':
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
if itemtotal < total_items_to_place: if itemtotal < total_items_to_place:
nothings = total_items_to_place - itemtotal nothings = total_items_to_place - itemtotal
@@ -1066,7 +1087,7 @@ def make_customizer_pool(world, player):
pool.append(random.choice(diff.bottles)) pool.append(random.choice(diff.bottles))
elif item_name.startswith('Small Key') and item_name != 'Small Key (Universal)': elif item_name.startswith('Small Key') and item_name != 'Small Key (Universal)':
d_item = ItemFactory(item_name, player) d_item = ItemFactory(item_name, player)
if not world.keyshuffle[player]: if world.keyshuffle[player] == 'none':
d_name = d_item.dungeon d_name = d_item.dungeon
dungeon = world.get_dungeon(d_name, player) dungeon = world.get_dungeon(d_name, player)
target_amount = max(amount, len(dungeon.small_keys)) target_amount = max(amount, len(dungeon.small_keys))
@@ -1121,7 +1142,7 @@ def make_customizer_pool(world, player):
guaranteed_items = alwaysitems + ['Magic Mirror', 'Moon Pearl'] guaranteed_items = alwaysitems + ['Magic Mirror', 'Moon Pearl']
if world.shopsanity[player]: if world.shopsanity[player]:
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion']) guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])
if world.retro[player]: if world.keyshuffle[player] == 'universal':
guaranteed_items.append('Small Key (Universal)') guaranteed_items.append('Small Key (Universal)')
for item in guaranteed_items: for item in guaranteed_items:
if item not in pool: if item not in pool:
@@ -1199,34 +1220,34 @@ def set_default_triforce(goal, custom_goal, custom_total):
# A quick test to ensure all combinations generate the correct amount of items. # A quick test to ensure all combinations generate the correct amount of items.
def test(): # def test():
for difficulty in ['normal', 'hard', 'expert']: # for difficulty in ['normal', 'hard', 'expert']:
for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']: # for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']:
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']: # for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
for mode in ['open', 'standard', 'inverted', 'retro']: # for mode in ['open', 'standard', 'inverted', 'retro']:
for swords in ['random', 'assured', 'swordless', 'vanilla']: # for swords in ['random', 'assured', 'swordless', 'vanilla']:
for progressive in ['on', 'off']: # for progressive in ['on', 'off']:
for shuffle in ['full']: # for shuffle in ['full']:
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']: # for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
for retro in [True, False]: # for retro in [True, False]:
for bombbag in [True, False]: # for bombbag in [True, False]:
for door_shuffle in ['basic', 'crossed', 'vanilla']: # for door_shuffle in ['basic', 'crossed', 'vanilla']:
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic) # out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
count = len(out[0]) + len(out[1]) # count = len(out[0]) + len(out[1])
#
correct_count = total_items_to_place # correct_count = total_items_to_place
if goal in ['pedestal', 'trinity'] and swords != 'vanilla': # if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
# pedestal goals generate one extra item # # pedestal goals generate one extra item
correct_count += 1 # correct_count += 1
if retro: # if retro:
correct_count += 28 # correct_count += 28
try: # try:
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag)) # assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag))
except AssertionError as e: # except AssertionError as e:
print(e) # print(e)
#
if __name__ == '__main__': # if __name__ == '__main__':
test() # test()
def fill_specific_items(world): def fill_specific_items(world):
@@ -1270,7 +1291,7 @@ def fill_specific_items(world):
def is_dungeon_item(item, world, player): def is_dungeon_item(item, world, player):
return ((item.startswith('Small Key') and not world.keyshuffle[player]) return ((item.startswith('Small Key') and world.keyshuffle[player] == 'none')
or (item.startswith('Big Key') and not world.bigkeyshuffle[player]) or (item.startswith('Big Key') and not world.bigkeyshuffle[player])
or (item.startswith('Compass') and not world.compassshuffle[player]) or (item.startswith('Compass') and not world.compassshuffle[player])
or (item.startswith('Map') and not world.mapshuffle[player])) or (item.startswith('Map') and not world.mapshuffle[player]))
+13 -12
View File
@@ -158,7 +158,7 @@ class PlacementRule(object):
left -= rule_needed left -= rule_needed
return False return False
def is_satisfiable(self, outside_keys, unplaced_keys, big_key_loc, prize_location, cr_count): def is_satisfiable(self, outside_keys, wild_keys, unplaced_keys, big_key_loc, prize_location, cr_count):
if self.prize_relevance and prize_location: if self.prize_relevance and prize_location:
if self.prize_relevance == 'BigBomb': if self.prize_relevance == 'BigBomb':
if prize_location.item.name not in ['Crystal 5', 'Crystal 6']: if prize_location.item.name not in ['Crystal 5', 'Crystal 6']:
@@ -272,7 +272,7 @@ def analyze_dungeon(key_layout, world, player):
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations)) key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations)) key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations))
key_logic.prize_location = dungeon_table[key_layout.sector.name].prize key_logic.prize_location = dungeon_table[key_layout.sector.name].prize
if world.retro[player] and world.mode[player] != 'standard': if world.keyshuffle[player] == 'universal' and world.mode[player] != 'standard':
return return
original_key_counter = find_counter({}, False, key_layout, False) original_key_counter = find_counter({}, False, key_layout, False)
@@ -923,7 +923,7 @@ def self_lock_possible(counter):
def available_chest_small_keys(key_counter, world, player): def available_chest_small_keys(key_counter, world, player):
if not world.keyshuffle[player] and not world.retro[player]: if world.keyshuffle[player] == 'none':
cnt = 0 cnt = 0
for loc in key_counter.free_locations: for loc in key_counter.free_locations:
if key_counter.big_key_opened or '- Big Chest' not in loc.name: if key_counter.big_key_opened or '- Big Chest' not in loc.name:
@@ -934,7 +934,7 @@ def available_chest_small_keys(key_counter, world, player):
def available_chest_small_keys_logic(key_counter, world, player, sm_restricted): def available_chest_small_keys_logic(key_counter, world, player, sm_restricted):
if not world.keyshuffle[player] and not world.retro[player]: if world.keyshuffle[player] == 'none':
cnt = 0 cnt = 0
for loc in key_counter.free_locations: for loc in key_counter.free_locations:
if loc not in sm_restricted and (key_counter.big_key_opened or '- Big Chest' not in loc.name): if loc not in sm_restricted and (key_counter.big_key_opened or '- Big Chest' not in loc.name):
@@ -1446,8 +1446,9 @@ def validate_bk_layout(proposal, builder, start_regions, world, player):
# Soft lock stuff # Soft lock stuff
def validate_key_layout(key_layout, world, player): def validate_key_layout(key_layout, world, player):
# retro is all good - except for hyrule castle in standard mode # universal key is all good - except for hyrule castle in standard mode
if (world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) or world.logic[player] == 'nologic': if world.logic[player] == 'nologic' or (world.keyshuffle[player] == 'universal' and
(world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')):
return True return True
flat_proposal = key_layout.flat_prop flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name) state = ExplorationState(dungeon=key_layout.sector.name)
@@ -1575,8 +1576,8 @@ def enough_small_locations(state, avail_small_loc):
def determine_prize_lock(key_layout, world, player): def determine_prize_lock(key_layout, world, player):
if ((world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) if world.logic[player] == 'nologic' or (world.keyshuffle[player] == 'universal' and
or world.logic[player] == 'nologic'): (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')):
return # done, doesn't matter what return # done, doesn't matter what
flat_proposal = key_layout.flat_prop flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name) state = ExplorationState(dungeon=key_layout.sector.name)
@@ -1608,7 +1609,7 @@ def determine_prize_lock(key_layout, world, player):
def cnt_avail_small_locations(free_locations, key_only, state, world, player): def cnt_avail_small_locations(free_locations, key_only, state, world, player):
if not world.keyshuffle[player] and not world.retro[player]: if world.keyshuffle[player] == 'none':
bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0 bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0
avail_chest_keys = min(free_locations - bk_adj, state.key_locations - key_only) avail_chest_keys = min(free_locations - bk_adj, state.key_locations - key_only)
return max(0, avail_chest_keys + key_only - state.used_smalls) return max(0, avail_chest_keys + key_only - state.used_smalls)
@@ -1616,7 +1617,7 @@ def cnt_avail_small_locations(free_locations, key_only, state, world, player):
def cnt_avail_small_locations_by_ctr(free_locations, counter, layout, world, player): def cnt_avail_small_locations_by_ctr(free_locations, counter, layout, world, player):
if not world.keyshuffle[player] and not world.retro[player]: if world.keyshuffle[player] == 'none':
bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0 bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0
avail_chest_keys = min(free_locations - bk_adj, layout.max_chests) avail_chest_keys = min(free_locations - bk_adj, layout.max_chests)
return max(0, avail_chest_keys + len(counter.key_only_locations) - counter.used_keys) return max(0, avail_chest_keys + len(counter.key_only_locations) - counter.used_keys)
@@ -2059,14 +2060,14 @@ def val_rule(rule, skn, allow=False, loc=None, askn=None, setCheck=None):
# Soft lock stuff # Soft lock stuff
def validate_key_placement(key_layout, world, player): def validate_key_placement(key_layout, world, player):
if world.retro[player] or world.accessibility[player] == 'none': if world.keyshuffle[player] == 'universal' or world.accessibility[player] == 'none':
return True # Can't keylock in retro. Expected if beatable only. return True # Can't keylock in retro. Expected if beatable only.
max_counter = find_max_counter(key_layout) max_counter = find_max_counter(key_layout)
keys_outside = 0 keys_outside = 0
big_key_outside = False big_key_outside = False
smallkey_name = dungeon_keys[key_layout.sector.name] smallkey_name = dungeon_keys[key_layout.sector.name]
bigkey_name = dungeon_bigs[key_layout.sector.name] bigkey_name = dungeon_bigs[key_layout.sector.name]
if world.keyshuffle[player]: if world.keyshuffle[player] != 'none':
keys_outside = key_layout.max_chests - sum(1 for i in max_counter.free_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player) keys_outside = key_layout.max_chests - sum(1 for i in max_counter.free_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player)
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
max_counter = find_max_counter(key_layout) max_counter = find_max_counter(key_layout)
+6 -3
View File
@@ -75,7 +75,7 @@ def main(args, seed=None, fish=None):
customized.adjust_args(args) customized.adjust_args(args)
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints)
world.customizer = customized if customized else None world.customizer = customized if customized else None
logger = logging.getLogger('') logger = logging.getLogger('')
if seed is None: if seed is None:
@@ -96,6 +96,7 @@ def main(args, seed=None, fish=None):
world.bigkeyshuffle = args.bigkeyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.bombbag = args.bombbag.copy() world.bombbag = args.bombbag.copy()
world.flute_mode = args.flute_mode.copy() world.flute_mode = args.flute_mode.copy()
world.bow_mode = args.bow_mode.copy()
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
world.crystals_ganon_orig = args.crystals_ganon.copy() world.crystals_ganon_orig = args.crystals_ganon.copy()
@@ -124,11 +125,13 @@ def main(args, seed=None, fish=None):
world.shuffletavern = args.shuffletavern.copy() world.shuffletavern = args.shuffletavern.copy()
world.pseudoboots = args.pseudoboots.copy() world.pseudoboots = args.pseudoboots.copy()
world.overworld_map = args.overworld_map.copy() world.overworld_map = args.overworld_map.copy()
world.take_any = args.take_any.copy()
world.restrict_boss_items = args.restrict_boss_items.copy() world.restrict_boss_items = args.restrict_boss_items.copy()
world.collection_rate = args.collection_rate.copy() world.collection_rate = args.collection_rate.copy()
world.colorizepots = args.colorizepots.copy() world.colorizepots = args.colorizepots.copy()
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
world.finish_init()
logger.info( logger.info(
world.fish.translate("cli","cli","app.title") + "\n", world.fish.translate("cli","cli","app.title") + "\n",
@@ -247,7 +250,7 @@ def main(args, seed=None, fish=None):
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
if world.shopsanity[player]: if world.shopsanity[player]:
sell_potions(world, player) sell_potions(world, player)
if world.retro[player]: if world.keyshuffle[player] == 'universal':
sell_keys(world, player) sell_keys(world, player)
else: else:
lock_shop_locations(world, player) lock_shop_locations(world, player)
@@ -425,7 +428,7 @@ def copy_world(world):
# ToDo: Not good yet # ToDo: Not good yet
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints) world.accessibility, world.shuffle_ganon, world.custom, world.customitemarray, world.hints)
ret.teams = world.teams ret.teams = world.teams
ret.player_names = copy.deepcopy(world.player_names) ret.player_names = copy.deepcopy(world.player_names)
ret.remote_items = world.remote_items.copy() ret.remote_items = world.remote_items.copy()
+4 -3
View File
@@ -879,7 +879,8 @@ def shuffle_pots(world, player):
elif old_pot.item == PotItem.Switch: elif old_pot.item == PotItem.Switch:
available_pots = (pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch)) available_pots = (pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch))
elif old_pot.item == PotItem.Key: elif old_pot.item == PotItem.Key:
if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and world.pottery[player] == 'none' and world.logic[player] != 'nologic': if (world.doorShuffle[player] == 'vanilla' and world.keyshuffle[player] != 'universal'
and world.pottery[player] == 'none' and world.logic[player] != 'nologic'):
available_pots = (pot for pot in new_pots if pot.room not in invalid_key_rooms) available_pots = (pot for pot in new_pots if pot.room not in invalid_key_rooms)
else: else:
available_pots = new_pots available_pots = new_pots
@@ -890,7 +891,7 @@ def shuffle_pots(world, player):
new_pot = random.choice(available_pots) new_pot = random.choice(available_pots)
new_pot.item = old_pot.item new_pot.item = old_pot.item
if world.retro[player] and new_pot.item == PotItem.FiveArrows: if world.bow_mode[player].startswith('retro') and new_pot.item == PotItem.FiveArrows:
new_pot.item = PotItem.FiveRupees new_pot.item = PotItem.FiveRupees
if new_pot.item == PotItem.Key: if new_pot.item == PotItem.Key:
@@ -938,7 +939,7 @@ def shuffle_pot_switches(world, player):
new_pot = random.choice(available_pots) new_pot = random.choice(available_pots)
new_pot.item, old_pot.item = old_pot.item, new_pot.item new_pot.item, old_pot.item = old_pot.item, new_pot.item
if world.retro[player] and new_pot.item == PotItem.FiveArrows: if world.bow_mode[player].startswith('retro') and new_pot.item == PotItem.FiveArrows:
new_pot.item = PotItem.FiveRupees new_pot.item = PotItem.FiveRupees
if new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange): if new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange):
+1 -1
View File
@@ -1029,7 +1029,7 @@ def adjust_locations(world, player):
loc.event = False loc.event = False
item_dungeon = key_item.dungeon item_dungeon = key_item.dungeon
dungeon = world.get_dungeon(item_dungeon, player) dungeon = world.get_dungeon(item_dungeon, player)
if key_item.smallkey and not world.retro[player]: if key_item.smallkey and world.keyshuffle[player] != 'universal':
dungeon.small_keys.append(key_item) dungeon.small_keys.append(key_item)
elif key_item.bigkey: elif key_item.bigkey:
dungeon.big_key = key_item dungeon.big_key = key_item
+18 -18
View File
@@ -797,7 +797,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x138002, 2) rom.write_byte(0x138002, 2)
for name, layout in world.key_layout[player].items(): for name, layout in world.key_layout[player].items():
offset = compass_data[name][4]//2 offset = compass_data[name][4]//2
if world.retro[player]: if world.keyshuffle[player] == 'universal':
rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops) rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops)
else: else:
rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used
@@ -898,7 +898,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x142A50, 1) # StandingItemsOn rom.write_byte(0x142A50, 1) # StandingItemsOn
multiClientFlags = ((0x1 if world.dropshuffle[player] else 0) multiClientFlags = ((0x1 if world.dropshuffle[player] else 0)
| (0x2 if world.shopsanity[player] else 0) | (0x2 if world.shopsanity[player] else 0)
| (0x4 if world.retro[player] else 0) | (0x4 if world.take_any[player] != 'none' else 0)
| (0x8 if world.pottery[player] != 'none' else 0) | (0x8 if world.pottery[player] != 'none' else 0)
| (0x10 if is_mystery else 0)) | (0x10 if is_mystery else 0))
rom.write_byte(0x142A51, multiClientFlags) rom.write_byte(0x142A51, multiClientFlags)
@@ -1112,7 +1112,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
prizes = [prize_replacements.get(prize, prize) for prize in prizes] prizes = [prize_replacements.get(prize, prize) for prize in prizes]
dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes] dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes]
if world.retro[player]: if world.bow_mode[player].startswith('retro'):
prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee
0xE2: 0xDB} #10 Arrows -> Red Rupee 0xE2: 0xDB} #10 Arrows -> Red Rupee
prizes = [prize_replacements.get(prize, prize) for prize in prizes] prizes = [prize_replacements.get(prize, prize) for prize in prizes]
@@ -1155,7 +1155,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
0x51, 0x00 if world.bombbag[player] else 0x06, 0x31 if world.bombbag[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bombbag -> turns into Bombs (10) 0x51, 0x00 if world.bombbag[player] else 0x06, 0x31 if world.bombbag[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bombbag -> turns into Bombs (10)
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
0x58, 0x01, 0x36 if world.retro[player] else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode) 0x58, 0x01, 0x36 if world.bow_mode[player].startswith('retro') else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode)
0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20
0x17, difficulty.heart_piece_limit, 0x47, 0xff, # piece of heart -> green 20 0x17, difficulty.heart_piece_limit, 0x47, 0xff, # piece of heart -> green 20
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
@@ -1204,7 +1204,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
ERtimeincrease = 10 ERtimeincrease = 10
else: else:
ERtimeincrease = 20 ERtimeincrease = 20
if world.keyshuffle[player] or world.bigkeyshuffle[player] or world.mapshuffle[player]: if world.keyshuffle[player] != 'none' or world.bigkeyshuffle[player] or world.mapshuffle[player]:
ERtimeincrease = ERtimeincrease + 15 ERtimeincrease = ERtimeincrease + 15
if world.clock_mode == 'none': if world.clock_mode == 'none':
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
@@ -1337,7 +1337,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# m - enabled for inside maps # m - enabled for inside maps
# c - enabled for inside compasses # c - enabled for inside compasses
# s - enabled for inside small keys # s - enabled for inside small keys
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] else 0x00) rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
| (0x02 if world.compassshuffle[player] else 0x00) | (0x02 if world.compassshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] else 0x00) | (0x04 if world.mapshuffle[player] else 0x00)
| (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes | (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes
@@ -1403,7 +1403,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# a - Small Key # a - Small Key
# #
enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'none' enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'none'
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] else 0x00) rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
| (0x02 if world.bigkeyshuffle[player] else 0x00) | (0x02 if world.bigkeyshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] or enable_menu_map_check 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
@@ -1434,17 +1434,17 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
write_int16(rom, 0x18017A, get_reveal_bytes('Green Pendant') if world.mapshuffle[player] else 0x0000) # Sahasrahla reveal write_int16(rom, 0x18017A, get_reveal_bytes('Green Pendant') if world.mapshuffle[player] else 0x0000) # Sahasrahla reveal
write_int16(rom, 0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.mapshuffle[player] else 0x0000) # Bomb Shop Reveal write_int16(rom, 0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.mapshuffle[player] else 0x0000) # Bomb Shop Reveal
rom.write_byte(0x180172, 0x01 if world.retro[player] else 0x00) # universal keys rom.write_byte(0x180172, 0x01 if world.keyshuffle[player] == 'universal' else 0x00) # universal keys
rom.write_byte(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow rom.write_byte(0x180175, 0x01 if world.bow_mode[player].startswith('retro') else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost rom.write_byte(0x180176, 0x0A if world.bow_mode[player].startswith('retro') else 0x00) # wood arrow cost
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver 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.retro[player] else 0xE1) # rupees replace arrows under pots rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) # rupees replace arrows under pots
if enemized: if enemized:
rom.write_byte(0x1B152e, 0xDA if world.retro[player] else 0xE1) rom.write_byte(0x1B152e, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1)
rom.write_byte(0x30052, 0xDB if world.retro[player] else 0xE2) # replace arrows in fish prize from bottle merchant rom.write_byte(0x30052, 0xDB if world.bow_mode[player].startswith('retro') else 0xE2) # replace arrows in fish prize from bottle merchant
rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows 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.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit 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
rom.write_bytes(0xEDA5, [0x35, 0x41] if world.retro[player] else [0x43, 0x44]) # Chest game gives rupees instead of arrows rom.write_bytes(0xEDA5, [0x35, 0x41] if world.bow_mode[player].startswith('retro') else [0x43, 0x44]) # Chest game gives rupees instead of arrows
digging_game_rng = random.randint(1, 30) # set rng for digging game digging_game_rng = random.randint(1, 30) # set rng for digging game
rom.write_byte(0x180020, digging_game_rng) rom.write_byte(0x180020, digging_game_rng)
rom.write_byte(0xEFD95, digging_game_rng) rom.write_byte(0xEFD95, digging_game_rng)
@@ -2151,7 +2151,7 @@ def write_strings(rom, world, player, team):
# It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless # It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless
# of how many exist. This supports many settings well. # of how many exist. This supports many settings well.
items_to_hint = RelevantItems.copy() items_to_hint = RelevantItems.copy()
if world.keyshuffle[player]: if world.keyshuffle[player] == 'wild':
items_to_hint.extend(SmallKeys) items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
items_to_hint.extend(BigKeys) items_to_hint.extend(BigKeys)
+1 -1
View File
@@ -2072,7 +2072,7 @@ def add_key_logic_rules(world, player):
add_rule(big_chest, create_rule(d_logic.bk_name, player)) add_rule(big_chest, create_rule(d_logic.bk_name, player))
if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1: if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1:
set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player)) set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player))
if world.retro[player]: if world.keyshuffle[player] == 'universal':
for d_name, layout in world.key_layout[player].items(): for d_name, layout in world.key_layout[player].items():
for door in layout.flat_prop: for door in layout.flat_prop:
if world.mode[player] != 'standard' or not retro_in_hc(door.entrance): if world.mode[player] != 'standard' or not retro_in_hc(door.entrance):
+15 -3
View File
@@ -48,6 +48,11 @@
flute_mode: flute_mode:
normal: 3 normal: 3
active: 1 active: 1
bow_mode:
progressive: 5
silvers: 5
retro: 1
retro_silvers: 1
entrance_shuffle: entrance_shuffle:
none: 15 none: 15
dungeonssimple: 3 dungeonssimple: 3
@@ -66,9 +71,10 @@
open: 1 open: 1
inverted: 1 inverted: 1
retro: 0 retro: 0
retro: take_any:
on: 1 random: 1
off: 4 fixed: 3
none: 16
goals: goals:
ganon: 2 ganon: 2
fast_ganon: 2 fast_ganon: 2
@@ -86,6 +92,12 @@
mc: 3 mc: 3
mcs: 2 mcs: 2
full: 5 full: 5
mcu: 1 # map, compass, universal smalls
# for use when you aren't using the dungeon_items above
# smallkey_shuffle:
# standard: 5
# wild: 1
# universal: 1
dungeon_counters: dungeon_counters:
on: 5 on: 5
off: 0 off: 0
-1
View File
@@ -50,7 +50,6 @@ world_state:
standard: 1 standard: 1
open: 1 open: 1
inverted: 1 inverted: 1
retro: 0
retro: retro:
on: 1 on: 1
off: 1 off: 1
+20 -2
View File
@@ -48,6 +48,14 @@
"active" "active"
] ]
}, },
"bow_mode": {
"choices": [
"progressive",
"silvers",
"retro",
"retro_silvers"
]
},
"goal": { "goal": {
"choices": [ "choices": [
"ganon", "ganon",
@@ -266,8 +274,11 @@
"type": "bool" "type": "bool"
}, },
"keyshuffle": { "keyshuffle": {
"action": "store_true", "choices": [
"type": "bool" "none",
"wild",
"universal"
]
}, },
"bigkeyshuffle": { "bigkeyshuffle": {
"action": "store_true", "action": "store_true",
@@ -286,6 +297,13 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"take_any": {
"choices": [
"none",
"random",
"fixed"
]
},
"startinventory": {}, "startinventory": {},
"usestartinventory": { "usestartinventory": {
"type": "bool" "type": "bool"
+14 -1
View File
@@ -122,10 +122,17 @@
"Expert: Greatly reduced functionality." "Expert: Greatly reduced functionality."
], ],
"flute_mode": [ "flute_mode": [
"Determine if you need to wake up the bird or not on flute pickup (default: %(default)s)", "Determines if you need to wake up the bird or not on flute pickup (default: %(default)s)",
"Normal: Normal functionality.", "Normal: Normal functionality.",
"Active: Flute is activated on pickup." "Active: Flute is activated on pickup."
], ],
"bow_mode": [
"Determines how the bow acts in the pool (default: %(default)s)",
"Progressive: Two progressive bows placed. First picked up is the bow. Second is silvers.",
"Silvers Separate: Bow and silvers are completely separate items.",
"Retro: Z1 Bow where arrows cost money and the Single Arrow must be bought or found to shoot",
"Retro + Silvers: Bow and silvers are completely separate items."
],
"timer": [ "timer": [
"Select game timer setting. Affects available itempool. (default: %(default)s)", "Select game timer setting. Affects available itempool. (default: %(default)s)",
"None: No timer.", "None: No timer.",
@@ -300,6 +307,12 @@
"Keys are universal, shooting arrows costs rupees,", "Keys are universal, shooting arrows costs rupees,",
"and a few other little things make this more like Zelda-1. (default: %(default)s)" "and a few other little things make this more like Zelda-1. (default: %(default)s)"
], ],
"take_any": [
"Take Any caves from Zelda 1 (default: %(default)s)",
"None: No take any caves",
"Random: Take any caves can replace a random set of un-interesting caves. See documentation for full list",
"Fixed: Take any caves will replace certain location. See documentation for full list"
],
"pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], "pseudoboots": [ " Players starts 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)" ], "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
+14 -2
View File
@@ -53,6 +53,9 @@
"randomizer.dungeon.mapshuffle": "Maps", "randomizer.dungeon.mapshuffle": "Maps",
"randomizer.dungeon.compassshuffle": "Compasses", "randomizer.dungeon.compassshuffle": "Compasses",
"randomizer.dungeon.smallkeyshuffle": "Small Keys", "randomizer.dungeon.smallkeyshuffle": "Small Keys",
"randomizer.dungeon.smallkeyshuffle.standard": "In Dungeon",
"randomizer.dungeon.smallkeyshuffle.wild": "Randomized",
"randomizer.dungeon.smallkeyshuffle.universal": "Universal",
"randomizer.dungeon.bigkeyshuffle": "Big Keys", "randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.keydropshuffle": "Key Drop Shuffle (Legacy)", "randomizer.dungeon.keydropshuffle": "Key Drop Shuffle (Legacy)",
"randomizer.dungeon.decoupledoors": "Decouple Doors", "randomizer.dungeon.decoupledoors": "Decouple Doors",
@@ -155,7 +158,10 @@
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
"randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple", "randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple",
"randomizer.entrance.take_any": "Take Any Caves",
"randomizer.entrance.take_any.none": "None",
"randomizer.entrance.take_any.random": "Random",
"randomizer.entrance.take_any.fixed": "Fixed",
"randomizer.gameoptions.nobgm": "Disable Music & MSU-1", "randomizer.gameoptions.nobgm": "Disable Music & MSU-1",
"randomizer.gameoptions.quickswap": "L/R Quickswapping", "randomizer.gameoptions.quickswap": "L/R Quickswapping",
@@ -219,7 +225,7 @@
"randomizer.generation.rom.dialog.allfiles": "All Files", "randomizer.generation.rom.dialog.allfiles": "All Files",
"randomizer.item.hints": "Include Helpful Hints", "randomizer.item.hints": "Include Helpful Hints",
"randomizer.item.retro": "Retro mode (universal keys)", "randomizer.item.retro": "Retro mode",
"randomizer.item.pseudoboots": "Start with Pseudo Boots", "randomizer.item.pseudoboots": "Start with Pseudo Boots",
"randomizer.item.bombbag": "Bombbag", "randomizer.item.bombbag": "Bombbag",
@@ -289,6 +295,12 @@
"randomizer.item.flute_mode.normal": "Normal", "randomizer.item.flute_mode.normal": "Normal",
"randomizer.item.flute_mode.active": "Pre-Activated", "randomizer.item.flute_mode.active": "Pre-Activated",
"randomizer.item.bow_mode": "Bow Mode",
"randomizer.item.bow_mode.progressive": "Progressive",
"randomizer.item.bow_mode.silvers": "Silvers Separate",
"randomizer.item.bow_mode.retro": "Retro (Progressive)",
"randomizer.item.bow_mode.retro_silvers": "Retro + Silvers",
"randomizer.item.timer": "Timer Setting", "randomizer.item.timer": "Timer Setting",
"randomizer.item.timer.none": "No Timer", "randomizer.item.timer.none": "No Timer",
"randomizer.item.timer.display": "Stopwatch", "randomizer.item.timer.display": "Stopwatch",
@@ -2,7 +2,6 @@
"keysanity": { "keysanity": {
"mapshuffle": { "type": "checkbox" }, "mapshuffle": { "type": "checkbox" },
"compassshuffle": { "type": "checkbox" }, "compassshuffle": { "type": "checkbox" },
"smallkeyshuffle": { "type": "checkbox" },
"bigkeyshuffle": { "type": "checkbox" } "bigkeyshuffle": { "type": "checkbox" }
} }
} }
@@ -1,5 +1,13 @@
{ {
"widgets": { "widgets": {
"keyshuffle": {
"type": "selectbox",
"options": [
"standard",
"wild",
"universal"
]
},
"dungeondoorshuffle": { "dungeondoorshuffle": {
"type": "selectbox", "type": "selectbox",
"default": "basic", "default": "basic",
@@ -29,6 +29,14 @@
"config": { "config": {
"width": 45 "width": 45
} }
},
"take_any": {
"type": "selectbox",
"options": [
"none",
"random",
"fixed"
]
} }
} }
} }
@@ -85,6 +85,15 @@
"active" "active"
] ]
}, },
"bow_mode": {
"type": "selectbox",
"options": [
"progressive",
"silvers",
"retro",
"retro_silvers"
]
},
"timer": { "timer": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
+4 -1
View File
@@ -69,11 +69,13 @@ class CustomSettings(object):
args.mode[p] = get_setting(settings['mode'], args.mode[p]) args.mode[p] = get_setting(settings['mode'], args.mode[p])
args.swords[p] = get_setting(settings['swords'], args.swords[p]) args.swords[p] = get_setting(settings['swords'], args.swords[p])
args.flute_mode[p] = get_setting(settings['flute_mode'], args.flute_mode[p]) args.flute_mode[p] = get_setting(settings['flute_mode'], args.flute_mode[p])
args.bow_mode[p] = get_setting(settings['bow_mode'], args.bow_mode[p])
args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p]) args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p])
args.goal[p] = get_setting(settings['goal'], args.goal[p]) args.goal[p] = get_setting(settings['goal'], args.goal[p])
args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p]) args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p])
args.accessibility[p] = get_setting(settings['accessibility'], args.accessibility[p]) args.accessibility[p] = get_setting(settings['accessibility'], args.accessibility[p])
args.retro[p] = get_setting(settings['retro'], args.retro[p]) args.retro[p] = get_setting(settings['retro'], args.retro[p])
args.take_any[p] = get_setting(settings['take_any'], args.take_any[p])
args.hints[p] = get_setting(settings['hints'], args.hints[p]) args.hints[p] = get_setting(settings['hints'], args.hints[p])
args.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[p]) args.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[p])
args.dropshuffle[p] = get_setting(settings['dropshuffle'], args.dropshuffle[p]) args.dropshuffle[p] = get_setting(settings['dropshuffle'], args.dropshuffle[p])
@@ -191,11 +193,12 @@ class CustomSettings(object):
settings_dict[p]['mode'] = world.mode[p] settings_dict[p]['mode'] = world.mode[p]
settings_dict[p]['swords'] = world.swords[p] settings_dict[p]['swords'] = world.swords[p]
settings_dict[p]['flute_mode'] = world.flute_mode[p] settings_dict[p]['flute_mode'] = world.flute_mode[p]
settings_dict[p]['bow_mode'] = world.bow_mode[p]
settings_dict[p]['difficulty'] = world.difficulty[p] settings_dict[p]['difficulty'] = world.difficulty[p]
settings_dict[p]['goal'] = world.goal[p] settings_dict[p]['goal'] = world.goal[p]
settings_dict[p]['accessibility'] = world.accessibility[p] settings_dict[p]['accessibility'] = world.accessibility[p]
settings_dict[p]['item_functionality'] = world.difficulty_adjustments[p] settings_dict[p]['item_functionality'] = world.difficulty_adjustments[p]
settings_dict[p]['retro'] = world.retro[p] settings_dict[p]['take_any'] = world.take_any[p]
settings_dict[p]['hints'] = world.hints[p] settings_dict[p]['hints'] = world.hints[p]
settings_dict[p]['shopsanity'] = world.shopsanity[p] settings_dict[p]['shopsanity'] = world.shopsanity[p]
settings_dict[p]['dropshuffle'] = world.dropshuffle[p] settings_dict[p]['dropshuffle'] = world.dropshuffle[p]
+2
View File
@@ -68,6 +68,7 @@ SETTINGSTOPROCESS = {
"weapons": "swords", "weapons": "swords",
"itempool": "difficulty", "itempool": "difficulty",
"flute_mode": "flute_mode", "flute_mode": "flute_mode",
"bow_mode": "bow_mode",
"timer": "timer", "timer": "timer",
"accessibility": "accessibility", "accessibility": "accessibility",
"sortingalgo": "algorithm", "sortingalgo": "algorithm",
@@ -81,6 +82,7 @@ SETTINGSTOPROCESS = {
"shuffletavern": "shuffletavern", "shuffletavern": "shuffletavern",
"entranceshuffle": "shuffle", "entranceshuffle": "shuffle",
"overworld_map": "overworld_map", "overworld_map": "overworld_map",
"take_any": "take_any",
}, },
"enemizer": { "enemizer": {
"enemyshuffle": "shuffleenemies", "enemyshuffle": "shuffleenemies",
+8
View File
@@ -285,4 +285,12 @@ def create_guiargs(parent):
guiargs.dropshuffle = 1 guiargs.dropshuffle = 1
guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery
if guiargs.retro or guiargs.mode == 'retro':
if guiargs.bow_mode == 'progressive':
guiargs.bow_mode = 'retro'
elif guiargs.bow_mode == 'silvers':
guiargs.bow_mode = 'retro_silvers'
guiargs.take_any = 'random' if guiargs.take_any == 'none' else guiargs.take_any
guiargs.keyshuffle = 'universal'
return guiargs return guiargs
+12 -9
View File
@@ -82,10 +82,11 @@ def create_item_pool_config(world):
if world.shopsanity[player]: if world.shopsanity[player]:
for item, locs in shop_vanilla_mapping.items(): for item, locs in shop_vanilla_mapping.items():
config.static_placement[player][item].extend(locs) config.static_placement[player][item].extend(locs)
if world.retro[player]: if world.take_any[player] != 'none':
for item, locs in retro_vanilla_mapping.items(): for item, locs in retro_vanilla_mapping.items():
config.static_placement[player][item].extend(locs) config.static_placement[player][item].extend(locs)
# universal keys # universal keys
if world.keyshuffle[player] == 'universal':
universal_key_locations = [] universal_key_locations = []
for item, locs in vanilla_mapping.items(): for item, locs in vanilla_mapping.items():
if 'Small Key' in item: if 'Small Key' in item:
@@ -98,12 +99,13 @@ def create_item_pool_config(world):
for item, locs in potkeys_vanilla_mapping.items(): for item, locs in potkeys_vanilla_mapping.items():
universal_key_locations.extend(locs) universal_key_locations.extend(locs)
if world.shopsanity[player]: if world.shopsanity[player]:
single_arrow_placement = list(shop_vanilla_mapping['Red Potion'])
single_arrow_placement.append('Red Shield Shop - Right')
config.static_placement[player]['Single Arrow'] = single_arrow_placement
universal_key_locations.extend(shop_vanilla_mapping['Small Heart']) universal_key_locations.extend(shop_vanilla_mapping['Small Heart'])
universal_key_locations.extend(shop_vanilla_mapping['Blue Shield']) universal_key_locations.extend(shop_vanilla_mapping['Blue Shield'])
config.static_placement[player]['Small Key (Universal)'] = universal_key_locations config.static_placement[player]['Small Key (Universal)'] = universal_key_locations
if world.bow_mode[player].startswith('retro') and world.shopsanity[player]:
single_arrow_placement = list(shop_vanilla_mapping['Red Potion'])
single_arrow_placement.append('Red Shield Shop - Right')
config.static_placement[player]['Single Arrow'] = single_arrow_placement
config.location_groups[player] = [ config.location_groups[player] = [
LocationGroup('Major').locs(mode_grouping['Overworld Major'] + mode_grouping['Big Chests'] + mode_grouping['Heart Containers']), LocationGroup('Major').locs(mode_grouping['Overworld Major'] + mode_grouping['Big Chests'] + mode_grouping['Heart Containers']),
LocationGroup('bkhp').locs(mode_grouping['Heart Pieces']), LocationGroup('bkhp').locs(mode_grouping['Heart Pieces']),
@@ -124,7 +126,7 @@ def create_item_pool_config(world):
groups.locations.extend(mode_grouping['Big Keys']) groups.locations.extend(mode_grouping['Big Keys'])
if world.dropshuffle[player] != 'none': if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Key Drops']) groups.locations.extend(mode_grouping['Big Key Drops'])
if world.keyshuffle[player]: if world.keyshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Small Keys']) groups.locations.extend(mode_grouping['Small Keys'])
if world.dropshuffle[player] != 'none': if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Key Drops']) groups.locations.extend(mode_grouping['Key Drops'])
@@ -137,7 +139,7 @@ def create_item_pool_config(world):
if world.shopsanity[player]: if world.shopsanity[player]:
groups.locations.append('Capacity Upgrade - Left') groups.locations.append('Capacity Upgrade - Left')
groups.locations.append('Capacity Upgrade - Right') groups.locations.append('Capacity Upgrade - Right')
if world.retro[player]: if world.take_any[player] != 'none':
if world.shopsanity[player]: if world.shopsanity[player]:
groups.locations.extend(retro_vanilla_mapping['Heart Container']) groups.locations.extend(retro_vanilla_mapping['Heart Container'])
groups.locations.append('Old Man Sword Cave Item 1') groups.locations.append('Old Man Sword Cave Item 1')
@@ -249,7 +251,7 @@ def previously_reserved(location, world, player):
if world.restrict_boss_items[player] == 'dungeon' and (not world.compassshuffle[player] if world.restrict_boss_items[player] == 'dungeon' and (not world.compassshuffle[player]
or not world.mapshuffle[player] or not world.mapshuffle[player]
or not world.bigkeyshuffle[player] or not world.bigkeyshuffle[player]
or not (world.keyshuffle[player] or world.retro[player])): or world.keyshuffle[player] == 'standard'):
return True return True
return False return False
@@ -335,7 +337,7 @@ def determine_major_items(world, player):
pass # now what? pass # now what?
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
major_item_set.update({x for x, y in item_table.items() if y[2] == 'BigKey'}) major_item_set.update({x for x, y in item_table.items() if y[2] == 'BigKey'})
if world.keyshuffle[player]: if world.keyshuffle[player] != 'none':
major_item_set.update({x for x, y in item_table.items() if y[2] == 'SmallKey'}) major_item_set.update({x for x, y in item_table.items() if y[2] == 'SmallKey'})
if world.compassshuffle[player]: if world.compassshuffle[player]:
major_item_set.update({x for x, y in item_table.items() if y[2] == 'Compass'}) major_item_set.update({x for x, y in item_table.items() if y[2] == 'Compass'})
@@ -344,8 +346,9 @@ def determine_major_items(world, player):
if world.shopsanity[player]: if world.shopsanity[player]:
major_item_set.add('Bomb Upgrade (+5)') major_item_set.add('Bomb Upgrade (+5)')
major_item_set.add('Arrow Upgrade (+5)') major_item_set.add('Arrow Upgrade (+5)')
if world.retro[player]: if world.bow_mode[player].startswith('retro'):
major_item_set.add('Single Arrow') major_item_set.add('Single Arrow')
if world.keyshuffle[player] == 'universal':
major_item_set.add('Small Key (Universal)') major_item_set.add('Small Key (Universal)')
if world.goal == 'triforcehunt': if world.goal == 'triforcehunt':
major_item_set.add('Triforce Piece') major_item_set.add('Triforce Piece')
+9 -1
View File
@@ -64,7 +64,13 @@ def roll_settings(weights):
dungeon_items = 'mcsb' if dungeon_items == 'full' 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.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.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items
ret.keyshuffle = get_choice('smallkey_shuffle') == 'on' if 'smallkey_shuffle' in weights else 's' in dungeon_items if 'smallkey_shuffle' in weights:
ret.keyshuffle = get_choice('smallkey_shuffle')
else:
if 's' in dungeon_items:
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('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else 'b' in dungeon_items
ret.accessibility = get_choice('accessibility') ret.accessibility = get_choice('accessibility')
@@ -130,6 +136,7 @@ def roll_settings(weights):
ret.mode = 'open' ret.mode = 'open'
ret.retro = True ret.retro = True
ret.retro = get_choice('retro') == 'on' # this overrides world_state if used ret.retro = get_choice('retro') == 'on' # 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('bombbag') == 'on'
@@ -145,6 +152,7 @@ def roll_settings(weights):
ret.difficulty = get_choice('item_pool') ret.difficulty = get_choice('item_pool')
ret.flute_mode = get_choice_default('flute_mode', default='normal') ret.flute_mode = get_choice_default('flute_mode', default='normal')
ret.bow_mode = get_choice_default('bow_mode', default='progressive')
ret.item_functionality = get_choice('item_functionality') ret.item_functionality = get_choice('item_functionality')