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

View File

@@ -22,7 +22,7 @@ from source.dungeon.RoomObject import RoomObject
class World(object):
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.teams = 1
self.shuffle = shuffle.copy()
@@ -64,7 +64,6 @@ class World(object):
self.fix_trock_exit = {}
self.shuffle_ganon = shuffle_ganon
self.fix_gtower_exit = self.shuffle_ganon
self.retro = retro.copy()
self.custom = custom
self.customitemarray = customitemarray
self.can_take_damage = True
@@ -91,10 +90,6 @@ class World(object):
self.pot_contents = {}
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):
self.__dict__.setdefault(attr, {})[player] = val
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('mapshuffle', False)
set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', False)
set_player_attr('keyshuffle', 'standard')
set_player_attr('bigkeyshuffle', False)
set_player_attr('restrict_boss_items', 'none')
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('boss_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_gt_orig', {})
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_count', 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('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):
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):
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
self.precollected_items.append(item)
self.state.collect(item, True)
@@ -587,7 +591,7 @@ class CollectionState(object):
if key_logic.sm_doors[door]:
self.reached_doors[player].add(key_logic.sm_doors[door].name)
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[connection.name] = (connection, crystal_state)
elif door.name not in self.opened_doors[player]:
@@ -759,7 +763,7 @@ class CollectionState(object):
return None
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']
else:
self.dungeon_limits = [dungeon_name]
@@ -935,7 +939,7 @@ class CollectionState(object):
checked_locations = 0
while new_locations:
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)]
reachable_events = self._do_not_flood_the_keys(reachable_events)
for event in reachable_events:
@@ -995,7 +999,7 @@ class CollectionState(object):
return self.prog_items[item, player] >= count
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)':
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)
@@ -1091,7 +1095,7 @@ class CollectionState(object):
or self.has('Cane of Somaria', 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
return self.has('Bow', player) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player))
return self.has('Bow', player)
@@ -1373,7 +1377,7 @@ class Region(object):
return False
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.map and not self.world.mapshuffle[item.player])
or (item.compass and not self.world.compassshuffle[item.player]))
@@ -2239,7 +2243,7 @@ class Item(object):
return item_dungeon
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.compass and not world.compassshuffle[self.player])
or (self.map and not world.mapshuffle[self.player]))
@@ -2371,15 +2375,16 @@ class Spoiler(object):
self.metadata = {'version': ERVersion,
'logic': self.world.logic,
'mode': self.world.mode,
'retro': self.world.retro,
'bombbag': self.world.bombbag,
'weapons': self.world.swords,
'flute_mode': self.world.flute_mode,
'bow_mode': self.world.bow_mode,
'goal': self.world.goal,
'shuffle': self.world.shuffle,
'shuffleganon': self.world.shuffle_ganon,
'shufflelinks': self.world.shufflelinks,
'shuffletavern': self.world.shuffletavern,
'take_any': self.world.take_any,
'overworld_map': self.world.overworld_map,
'door_shuffle': self.world.doorShuffle,
'intensity': self.world.intensity,
@@ -2561,7 +2566,6 @@ class Spoiler(object):
outfile.write(f'Settings Code: {self.metadata["code"][player]}\n')
outfile.write('Logic: %s\n' % self.metadata['logic'][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('Goal: %s\n' % self.metadata['goal'][player])
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('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
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"Bombbag: {yn(self.metadata['bombbag'][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"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\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':
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])
@@ -2597,7 +2604,7 @@ class Spoiler(object):
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('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('Boss shuffle: %s\n' % self.metadata['boss_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,
'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}
world_mode = {"open": 0, "standard": 1, "inverted": 2}
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
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}
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 11: F???, ???? (flute_mode)
# byte 11: FBBB, TTSS (flute_mode, bow_mode, take_any, small_key_mode)
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
# psuedoboots does not effect 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)
settings_version = 0
settings_version = 1
class Settings(object):
@@ -2897,7 +2907,7 @@ class Settings(object):
(dr_mode[w.doorShuffle[p]] << 5) | er_mode[w.shuffle[p]],
(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)
| (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)
| (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)
| (door_type_mode[w.door_type_mode[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
@@ -2926,14 +2936,16 @@ class Settings(object):
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()
@staticmethod
def adjust_args_from_code(code, player, args):
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')
if settings[10] != settings_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.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5]
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.shopsanity[p] = True if settings[3] & 0x80 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.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.compassshuffle[p] = True if settings[7] & 0x10 else False
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)]
if len(settings) > 11:
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):

25
CLI.py
View File

@@ -103,19 +103,28 @@ def parse_cli(argv, no_defaults=False):
ret = parser.parse_args(argv)
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:
ret.dropshuffle = True
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:
defaults = copy.deepcopy(ret)
for player in range(1, player_num + 1):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
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',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items',
@@ -158,6 +167,7 @@ def parse_settings():
"crystals_ganon": "7",
"swords": "random",
'flute_mode': 'normal',
'bow_mode': 'progressive',
"difficulty": "normal",
"item_functionality": "normal",
"timer": "none",
@@ -175,6 +185,7 @@ def parse_settings():
"shufflelinks": False,
"shuffletavern": False,
"overworld_map": "default",
'take_any': 'none',
"pseudoboots": False,
"shuffleenemies": "none",
@@ -189,11 +200,11 @@ def parse_settings():
'pottery': 'none',
'colorizepots': False,
'shufflepots': False,
"mapshuffle": False,
"compassshuffle": False,
"keyshuffle": False,
"bigkeyshuffle": False,
"keysanity": False,
'mapshuffle': False,
'compassshuffle': False,
'keyshuffle': 'none',
'bigkeyshuffle': False,
'keysanity': False,
"door_shuffle": "basic",
"intensity": 2,
'door_type_mode': 'original',

View File

@@ -470,7 +470,7 @@ def choose_portals(world, player):
for dungeon, info in shuffled_info:
outstanding_portals = list(dungeon_portals[dungeon])
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:
sanc = world.get_portal('Sanctuary', player)
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))
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
else:
target_items += 29 # small keys in chests
@@ -1248,7 +1248,7 @@ def cross_dungeon(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))
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
else:
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):
logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors"))
start = time.process_time()
if world.retro[player]:
if world.keyshuffle[player] == 'universal':
remaining = 29
if world.dropshuffle[player]:
remaining += 13
@@ -1424,7 +1424,7 @@ def assign_cross_keys(dungeon_builders, world, player):
# Last Step: Adjust Small Key Dungeon Pool
for name, builder in dungeon_builders.items():
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])
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
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
reassign_key_doors(small_map, world, player)
for dungeon_name in pool:
if not world.retro[player]:
if world.keyshuffle[player] != 'universal':
builder = world.dungeon_layouts[player][dungeon_name]
log_key_logic(builder.name, world.key_logic[player][builder.name])
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
elif d.type is DoorType.Normal:
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]:
dest_room = world.get_room(d.dest.roomIndex, player)
if stateful_door(d.dest, dest_room.kind(d.dest)):

View File

@@ -1355,7 +1355,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge
sanc_builder = random.choice(lw_builders)
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 = {}
crystal_switches = {}
crystal_barriers = {}

View File

@@ -5,7 +5,7 @@ from Items import ItemFactory
def create_dungeons(world, player):
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.boss = BossFactory(default_boss, player)
for region in dungeon.regions:

22
Fill.py
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
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
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
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)
unplaced_smalls = list(smalls)
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)
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):
return False
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
dungeon = location.parent_region.dungeon
if dungeon:
@@ -215,7 +216,7 @@ def track_dungeon_items(item, location, 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.compass and not world.compassshuffle[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
# 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]
# 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:
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player)
# 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)
# don't write out all pots to spoiler
if write_skips:
@@ -663,7 +665,7 @@ def balance_multiworld_progression(world):
candidate_items = collections.defaultdict(set)
while True:
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)
player = location.item.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)
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)
checked_locations |= sphere_locations
@@ -812,7 +814,9 @@ def balance_money_progression(world):
return True
if item.name in ['Progressive Armor', 'Blue Mail', 'Red Mail']:
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
if location.name in pay_for_locations:
return True

View File

@@ -73,7 +73,7 @@ class InitialSram:
if startingstate.has('Bow', player):
equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1
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
if startingstate.has('Silver Arrows', player):
equip[0x38E] |= 0x40
@@ -188,7 +188,7 @@ class InitialSram:
elif item.name in bombs:
starting_bombs += bombs[item.name]
elif item.name in arrows:
if world.retro[player]:
if world.bow_mode[player].startswith('retro'):
equip[0x38E] |= 0x80
starting_arrows = 1
else:

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)
skip_pool_adjustments = True
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)
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:
amt = world.pool_adjustment[player]
@@ -295,7 +295,8 @@ def generate_itempool(world, player):
if not found_sword and world.swords[player] != 'swordless':
found_sword = True
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
possible_weapons.append(item)
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.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.map and world.mapshuffle[player])
or (item.compass and world.compassshuffle[player]))])
@@ -409,8 +410,9 @@ def generate_itempool(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)
if world.keyshuffle[player] == 'universal':
if world.dropshuffle[player] and not skip_pool_adjustments:
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13
if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments:
@@ -432,6 +434,10 @@ take_any_locations = [
'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']
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):
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:
take_any_locations.remove('Archery Game')
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)
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]
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)
world.regions.append(old_man_take_any)
@@ -539,38 +548,47 @@ def fill_prizes(world, attempts=15):
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]:
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(red_pots)
removals.extend(random.sample(shields_n_hearts, 5))
removals = []
if retro_bow:
removals = [next(item for item in world.itempool if item.name == 'Arrows (10)' and item.player == player)]
removals.extend([item for item in world.itempool if item.name == 'Arrow Upgrade (+5)' and item.player == player])
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))
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:
world.itempool.remove(remove)
for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield
arrow_item = ItemFactory('Single Arrow', player)
arrow_item.advancement = True
world.itempool.append(arrow_item)
for i in range(5): # replace the red potions
world.itempool.append(ItemFactory('Small Key (Universal)', player))
world.itempool.append(ItemFactory('Rupees (50)', player)) # replaces the arrow upgrade
if retro_bow:
for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield
arrow_item = ItemFactory('Single Arrow', player)
arrow_item.advancement = True
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
world.itempool.append(ItemFactory('Small Key (Universal)', player))
# TODO: move hard+ mode changes for shields here, utilizing the new shops
else:
rss = world.get_region('Red Shield Shop', player).shop
if not rss.locked:
rss.custom = True
rss.add_inventory(2, 'Single Arrow', 80)
if retro_bow:
rss = world.get_region('Red Shield Shop', player).shop
if not rss.locked:
rss.custom = True
rss.add_inventory(2, 'Single Arrow', 80)
rss.locked = True
cap_shop = world.get_region('Capacity Upgrade', player).shop
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
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
cap_shop = world.get_region('Capacity Upgrade', player).shop
cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro
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.shopsanity[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):
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 = []
shops_to_customize = shop_to_location_table.copy()
if world.retro[player]:
if world.take_any[player] != 'none':
shops_to_customize.update(retro_shops)
for shop_name, loc_list in shops_to_customize.items():
shop = world.get_region(shop_name, player).shop
@@ -609,7 +628,7 @@ def customize_shops(world, player):
price = 0
else:
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
# randomize price
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:
choices = []
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))
if len(choices) > 0:
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):
retro_bow = world.bow_mode[player].startswith('retro')
for super_tile, pot_list in vanilla_pots.items():
for pot in pot_list:
if pot.item not in [PotItem.Hole, PotItem.Key, PotItem.Switch]:
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])
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):
pool = []
placed_items = {}
@@ -862,7 +882,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
else:
pool.extend(diff.basicarmor)
if want_progressives():
if 'silvers' not in world.bow_mode[player]:
pool.extend(['Progressive Bow'] * 2)
elif swords != 'swordless':
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':
place_item('Master Sword Pedestal', 'Triforce')
if retro:
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('Arrow Upgrade (+5)','Rupees (5)') for item in pool]
pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool]
if world.bow_mode[player].startswith('retro'):
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('Arrow Upgrade (+5)', '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)
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)'
indices = [i for i, x in enumerate(pool) if x == replace]
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)
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:
customitemarray = customitemarray[1]
pool = []
@@ -1014,7 +1035,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
itemtotal = itemtotal + 1
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'])
place_item(key_location, 'Small Key (Universal)')
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(['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
if itemtotal < total_items_to_place:
nothings = total_items_to_place - itemtotal
@@ -1066,7 +1087,7 @@ def make_customizer_pool(world, player):
pool.append(random.choice(diff.bottles))
elif item_name.startswith('Small Key') and item_name != 'Small Key (Universal)':
d_item = ItemFactory(item_name, player)
if not world.keyshuffle[player]:
if world.keyshuffle[player] == 'none':
d_name = d_item.dungeon
dungeon = world.get_dungeon(d_name, player)
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']
if world.shopsanity[player]:
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])
if world.retro[player]:
if world.keyshuffle[player] == 'universal':
guaranteed_items.append('Small Key (Universal)')
for item in guaranteed_items:
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.
def test():
for difficulty in ['normal', 'hard', 'expert']:
for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']:
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
for mode in ['open', 'standard', 'inverted', 'retro']:
for swords in ['random', 'assured', 'swordless', 'vanilla']:
for progressive in ['on', 'off']:
for shuffle in ['full']:
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
for retro in [True, False]:
for bombbag in [True, False]:
for door_shuffle in ['basic', 'crossed', 'vanilla']:
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
count = len(out[0]) + len(out[1])
correct_count = total_items_to_place
if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
# pedestal goals generate one extra item
correct_count += 1
if retro:
correct_count += 28
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))
except AssertionError as e:
print(e)
if __name__ == '__main__':
test()
# def test():
# for difficulty in ['normal', 'hard', 'expert']:
# for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']:
# for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
# for mode in ['open', 'standard', 'inverted', 'retro']:
# for swords in ['random', 'assured', 'swordless', 'vanilla']:
# for progressive in ['on', 'off']:
# for shuffle in ['full']:
# for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
# for retro in [True, False]:
# for bombbag in [True, False]:
# for door_shuffle in ['basic', 'crossed', 'vanilla']:
# out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
# count = len(out[0]) + len(out[1])
#
# correct_count = total_items_to_place
# if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
# # pedestal goals generate one extra item
# correct_count += 1
# if retro:
# correct_count += 28
# 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))
# except AssertionError as e:
# print(e)
#
# if __name__ == '__main__':
# test()
def fill_specific_items(world):
@@ -1270,7 +1291,7 @@ def fill_specific_items(world):
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('Compass') and not world.compassshuffle[player])
or (item.startswith('Map') and not world.mapshuffle[player]))

View File

@@ -158,7 +158,7 @@ class PlacementRule(object):
left -= rule_needed
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 == 'BigBomb':
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_key_locked_locations(key_layout.all_chest_locations))
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
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):
if not world.keyshuffle[player] and not world.retro[player]:
if world.keyshuffle[player] == 'none':
cnt = 0
for loc in key_counter.free_locations:
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):
if not world.keyshuffle[player] and not world.retro[player]:
if world.keyshuffle[player] == 'none':
cnt = 0
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):
@@ -1446,8 +1446,9 @@ def validate_bk_layout(proposal, builder, start_regions, world, player):
# Soft lock stuff
def validate_key_layout(key_layout, world, player):
# retro 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':
# universal key is all good - except for hyrule castle in standard mode
if world.logic[player] == 'nologic' or (world.keyshuffle[player] == 'universal' and
(world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')):
return True
flat_proposal = key_layout.flat_prop
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):
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 # done, doesn't matter what
flat_proposal = key_layout.flat_prop
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):
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
avail_chest_keys = min(free_locations - bk_adj, state.key_locations - key_only)
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):
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
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)
@@ -2059,14 +2060,14 @@ def val_rule(rule, skn, allow=False, loc=None, askn=None, setCheck=None):
# Soft lock stuff
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.
max_counter = find_max_counter(key_layout)
keys_outside = 0
big_key_outside = False
smallkey_name = dungeon_keys[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)
if world.bigkeyshuffle[player]:
max_counter = find_max_counter(key_layout)

View File

@@ -75,7 +75,7 @@ def main(args, seed=None, fish=None):
customized.adjust_args(args)
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.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
logger = logging.getLogger('')
if seed is None:
@@ -96,6 +96,7 @@ def main(args, seed=None, fish=None):
world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.bombbag = args.bombbag.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_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()
@@ -124,11 +125,13 @@ def main(args, seed=None, fish=None):
world.shuffletavern = args.shuffletavern.copy()
world.pseudoboots = args.pseudoboots.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.collection_rate = args.collection_rate.copy()
world.colorizepots = args.colorizepots.copy()
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
world.finish_init()
logger.info(
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):
if world.shopsanity[player]:
sell_potions(world, player)
if world.retro[player]:
if world.keyshuffle[player] == 'universal':
sell_keys(world, player)
else:
lock_shop_locations(world, player)
@@ -425,7 +428,7 @@ def copy_world(world):
# ToDo: Not good yet
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.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.player_names = copy.deepcopy(world.player_names)
ret.remote_items = world.remote_items.copy()

View File

@@ -879,7 +879,8 @@ def shuffle_pots(world, player):
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))
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)
else:
available_pots = new_pots
@@ -890,7 +891,7 @@ def shuffle_pots(world, player):
new_pot = random.choice(available_pots)
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
if new_pot.item == PotItem.Key:
@@ -938,7 +939,7 @@ def shuffle_pot_switches(world, player):
new_pot = random.choice(available_pots)
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
if new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange):

View File

@@ -1029,7 +1029,7 @@ def adjust_locations(world, player):
loc.event = False
item_dungeon = key_item.dungeon
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)
elif key_item.bigkey:
dungeon.big_key = key_item

36
Rom.py
View File

@@ -797,7 +797,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x138002, 2)
for name, layout in world.key_layout[player].items():
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)
else:
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
multiClientFlags = ((0x1 if world.dropshuffle[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)
| (0x10 if is_mystery else 0))
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]
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
0xE2: 0xDB} #10 Arrows -> Red Rupee
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
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
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
0x17, difficulty.heart_piece_limit, 0x47, 0xff, # piece of heart -> green 20
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
else:
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
if world.clock_mode == 'none':
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
# c - enabled for inside compasses
# 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)
| (0x04 if world.mapshuffle[player] else 0x00)
| (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
#
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)
| (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00)
| (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, 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(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost
rom.write_byte(0x301FC, 0xDA if world.retro[player] else 0xE1) # rupees replace arrows under pots
rom.write_byte(0x180172, 0x01 if world.keyshuffle[player] == 'universal' else 0x00) # universal keys
rom.write_byte(0x180175, 0x01 if world.bow_mode[player].startswith('retro') else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.bow_mode[player].startswith('retro') else 0x00) # wood arrow cost
rom.write_byte(0x180178, 0x32 if world.bow_mode[player].startswith('retro') else 0x00) # silver arrow cost
rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) # rupees replace arrows under pots
if enemized:
rom.write_byte(0x1B152e, 0xDA if world.retro[player] else 0xE1)
rom.write_byte(0x30052, 0xDB if world.retro[player] 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(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] 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_byte(0x1B152e, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1)
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.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows
rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows
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
rom.write_byte(0x180020, 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
# of how many exist. This supports many settings well.
items_to_hint = RelevantItems.copy()
if world.keyshuffle[player]:
if world.keyshuffle[player] == 'wild':
items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]:
items_to_hint.extend(BigKeys)

View File

@@ -2072,7 +2072,7 @@ def add_key_logic_rules(world, 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:
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 door in layout.flat_prop:
if world.mode[player] != 'standard' or not retro_in_hc(door.entrance):

View File

@@ -48,6 +48,11 @@
flute_mode:
normal: 3
active: 1
bow_mode:
progressive: 5
silvers: 5
retro: 1
retro_silvers: 1
entrance_shuffle:
none: 15
dungeonssimple: 3
@@ -66,9 +71,10 @@
open: 1
inverted: 1
retro: 0
retro:
on: 1
off: 4
take_any:
random: 1
fixed: 3
none: 16
goals:
ganon: 2
fast_ganon: 2
@@ -86,6 +92,12 @@
mc: 3
mcs: 2
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:
on: 5
off: 0

View File

@@ -50,7 +50,6 @@ world_state:
standard: 1
open: 1
inverted: 1
retro: 0
retro:
on: 1
off: 1

View File

@@ -48,6 +48,14 @@
"active"
]
},
"bow_mode": {
"choices": [
"progressive",
"silvers",
"retro",
"retro_silvers"
]
},
"goal": {
"choices": [
"ganon",
@@ -266,8 +274,11 @@
"type": "bool"
},
"keyshuffle": {
"action": "store_true",
"type": "bool"
"choices": [
"none",
"wild",
"universal"
]
},
"bigkeyshuffle": {
"action": "store_true",
@@ -286,6 +297,13 @@
"action": "store_true",
"type": "bool"
},
"take_any": {
"choices": [
"none",
"random",
"fixed"
]
},
"startinventory": {},
"usestartinventory": {
"type": "bool"

View File

@@ -122,10 +122,17 @@
"Expert: Greatly reduced functionality."
],
"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.",
"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": [
"Select game timer setting. Affects available itempool. (default: %(default)s)",
"None: No timer.",
@@ -300,6 +307,12 @@
"Keys are universal, shooting arrows costs rupees,",
"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"],
"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)" ],

View File

@@ -53,6 +53,9 @@
"randomizer.dungeon.mapshuffle": "Maps",
"randomizer.dungeon.compassshuffle": "Compasses",
"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.keydropshuffle": "Key Drop Shuffle (Legacy)",
"randomizer.dungeon.decoupledoors": "Decouple Doors",
@@ -155,7 +158,10 @@
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
"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.quickswap": "L/R Quickswapping",
@@ -219,7 +225,7 @@
"randomizer.generation.rom.dialog.allfiles": "All Files",
"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.bombbag": "Bombbag",
@@ -289,6 +295,12 @@
"randomizer.item.flute_mode.normal": "Normal",
"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.none": "No Timer",
"randomizer.item.timer.display": "Stopwatch",

View File

@@ -2,7 +2,6 @@
"keysanity": {
"mapshuffle": { "type": "checkbox" },
"compassshuffle": { "type": "checkbox" },
"smallkeyshuffle": { "type": "checkbox" },
"bigkeyshuffle": { "type": "checkbox" }
}
}

View File

@@ -1,5 +1,13 @@
{
"widgets": {
"keyshuffle": {
"type": "selectbox",
"options": [
"standard",
"wild",
"universal"
]
},
"dungeondoorshuffle": {
"type": "selectbox",
"default": "basic",

View File

@@ -29,6 +29,14 @@
"config": {
"width": 45
}
},
"take_any": {
"type": "selectbox",
"options": [
"none",
"random",
"fixed"
]
}
}
}

View File

@@ -85,6 +85,15 @@
"active"
]
},
"bow_mode": {
"type": "selectbox",
"options": [
"progressive",
"silvers",
"retro",
"retro_silvers"
]
},
"timer": {
"type": "selectbox",
"options": [

View File

@@ -69,11 +69,13 @@ class CustomSettings(object):
args.mode[p] = get_setting(settings['mode'], args.mode[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.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.goal[p] = get_setting(settings['goal'], args.goal[p])
args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p])
args.accessibility[p] = get_setting(settings['accessibility'], args.accessibility[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.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[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]['swords'] = world.swords[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]['goal'] = world.goal[p]
settings_dict[p]['accessibility'] = world.accessibility[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]['shopsanity'] = world.shopsanity[p]
settings_dict[p]['dropshuffle'] = world.dropshuffle[p]

View File

@@ -68,6 +68,7 @@ SETTINGSTOPROCESS = {
"weapons": "swords",
"itempool": "difficulty",
"flute_mode": "flute_mode",
"bow_mode": "bow_mode",
"timer": "timer",
"accessibility": "accessibility",
"sortingalgo": "algorithm",
@@ -81,6 +82,7 @@ SETTINGSTOPROCESS = {
"shuffletavern": "shuffletavern",
"entranceshuffle": "shuffle",
"overworld_map": "overworld_map",
"take_any": "take_any",
},
"enemizer": {
"enemyshuffle": "shuffleenemies",

View File

@@ -285,4 +285,12 @@ def create_guiargs(parent):
guiargs.dropshuffle = 1
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

View File

@@ -82,10 +82,11 @@ def create_item_pool_config(world):
if world.shopsanity[player]:
for item, locs in shop_vanilla_mapping.items():
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():
config.static_placement[player][item].extend(locs)
# universal keys
if world.keyshuffle[player] == 'universal':
universal_key_locations = []
for item, locs in vanilla_mapping.items():
if 'Small Key' in item:
@@ -98,12 +99,13 @@ def create_item_pool_config(world):
for item, locs in potkeys_vanilla_mapping.items():
universal_key_locations.extend(locs)
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['Blue Shield'])
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] = [
LocationGroup('Major').locs(mode_grouping['Overworld Major'] + mode_grouping['Big Chests'] + mode_grouping['Heart Containers']),
LocationGroup('bkhp').locs(mode_grouping['Heart Pieces']),
@@ -124,7 +126,7 @@ def create_item_pool_config(world):
groups.locations.extend(mode_grouping['Big Keys'])
if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Key Drops'])
if world.keyshuffle[player]:
if world.keyshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Small Keys'])
if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Key Drops'])
@@ -137,7 +139,7 @@ def create_item_pool_config(world):
if world.shopsanity[player]:
groups.locations.append('Capacity Upgrade - Left')
groups.locations.append('Capacity Upgrade - Right')
if world.retro[player]:
if world.take_any[player] != 'none':
if world.shopsanity[player]:
groups.locations.extend(retro_vanilla_mapping['Heart Container'])
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]
or not world.mapshuffle[player]
or not world.bigkeyshuffle[player]
or not (world.keyshuffle[player] or world.retro[player])):
or world.keyshuffle[player] == 'standard'):
return True
return False
@@ -335,7 +337,7 @@ def determine_major_items(world, player):
pass # now what?
if world.bigkeyshuffle[player]:
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'})
if world.compassshuffle[player]:
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]:
major_item_set.add('Bomb 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')
if world.keyshuffle[player] == 'universal':
major_item_set.add('Small Key (Universal)')
if world.goal == 'triforcehunt':
major_item_set.add('Triforce Piece')

View File

@@ -64,7 +64,13 @@ def roll_settings(weights):
dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items
ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else 'm' in dungeon_items
ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items
ret.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.accessibility = get_choice('accessibility')
@@ -130,6 +136,7 @@ def roll_settings(weights):
ret.mode = 'open'
ret.retro = True
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'
@@ -145,6 +152,7 @@ def roll_settings(weights):
ret.difficulty = get_choice('item_pool')
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')