Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2024-12-25 23:11:08 -06:00
32 changed files with 459 additions and 240 deletions
+55 -36
View File
@@ -71,7 +71,6 @@ class World(object):
self.fix_palaceofdarkness_exit = {}
self.fix_trock_exit = {}
self.shuffle_ganon = shuffle_ganon
self.fix_gtower_exit = self.shuffle_ganon
self.custom = custom
self.customitemarray = customitemarray
self.can_take_damage = True
@@ -133,16 +132,17 @@ class World(object):
set_player_attr('fix_skullwoods_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] or self.doorShuffle[player] not in ['vanilla'])
set_player_attr('fix_palaceofdarkness_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
set_player_attr('fix_trock_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
set_player_attr('fix_gtower_exit', self.shuffle_ganon[player] > 0)
set_player_attr('can_access_trock_eyebridge', None)
set_player_attr('can_access_trock_front', None)
set_player_attr('can_access_trock_big_chest', None)
set_player_attr('can_access_trock_middle', None)
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'hybridglitches', 'nologic']
or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity'])
set_player_attr('mapshuffle', False)
set_player_attr('compassshuffle', False)
set_player_attr('mapshuffle', 'none')
set_player_attr('compassshuffle', 'none')
set_player_attr('keyshuffle', 'none')
set_player_attr('bigkeyshuffle', False)
set_player_attr('bigkeyshuffle', 'none')
set_player_attr('prizeshuffle', 'none')
set_player_attr('restrict_boss_items', 'none')
set_player_attr('bombbag', False)
@@ -154,7 +154,7 @@ class World(object):
set_player_attr('enemy_health', 'default')
set_player_attr('enemy_damage', 'default')
set_player_attr('any_enemy_logic', 'allow_all')
set_player_attr('beemizer', 0)
set_player_attr('beemizer', '0')
set_player_attr('escape_assist', [])
set_player_attr('crystals_needed_for_ganon', 7)
set_player_attr('crystals_needed_for_gt', 7)
@@ -477,7 +477,7 @@ class World(object):
item.world = self
if ((item.prize and self.prizeshuffle[item.player] != 'none')
or (item.smallkey and self.keyshuffle[item.player] != 'none')
or (item.bigkey and self.bigkeyshuffle[item.player])):
or (item.bigkey and self.bigkeyshuffle[item.player] != 'none')):
item.advancement = True
self.precollected_items.append(item)
self.state.collect(item, True)
@@ -1100,7 +1100,7 @@ class CollectionState(object):
new_locations = True
while new_locations:
reachable_events = [location for location in locations if location.event and
(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))
(not key_only or (self.world.keyshuffle[location.item.player] in ['none', 'district'] and location.item.smallkey) or (self.world.bigkeyshuffle[location.item.player] in ['none', 'district'] and location.item.bigkey))
and location.can_reach(self)]
reachable_events = self._do_not_flood_the_keys(reachable_events)
new_locations = False
@@ -1577,6 +1577,7 @@ class Region(object):
self.exits = []
self.locations = []
self.dungeon = None
self.districts = []
self.shop = None
self.world = None
self.is_light_world = False # will be set aftermaking connections.
@@ -1607,10 +1608,16 @@ class Region(object):
return False
def can_fill(self, item):
if item.is_near_dungeon_item(self.world):
item_dungeon = self.world.get_dungeon(item.dungeon, self.player) if item.dungeon else item.dungeon_object
ret = (self.dungeon and self.dungeon.is_dungeon_item(item))
ret = ret or (len(self.districts) and item_dungeon and len([d for d in self.districts if d in item_dungeon.districts]))
return ret and item.player == self.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])
or (item.bigkey and self.world.bigkeyshuffle[item.player] == 'none')
or (item.map and self.world.mapshuffle[item.player] == 'none')
or (item.compass and self.world.compassshuffle[item.player] == 'none')
or (item.prize and self.world.prizeshuffle[item.player] == 'dungeon'))
# not all small keys to escape must be in escape
# sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
@@ -1859,6 +1866,7 @@ class Dungeon(object):
def __init__(self, name, regions, big_key, small_keys, dungeon_items, player, dungeon_id):
self.name = name
self.regions = regions
self.districts = []
self.prize = None
self.big_key = big_key
self.small_keys = small_keys
@@ -1887,8 +1895,8 @@ class Dungeon(object):
return self.dungeon_items + self.keys + ([self.prize] if self.prize else [])
def is_dungeon_item(self, item):
if item.prize:
return item.player == self.player and self.prize is None and self.name not in ['Hyrule Castle', 'Agahnims Tower', 'Ganons Tower']
if item.prize and item.dungeon is None:
return item.player == self.player and self.name not in ['Hyrule Castle', 'Agahnims Tower', 'Ganons Tower']
else:
return item.player == self.player and item.name in [dungeon_item.name for dungeon_item in self.all_items]
@@ -2757,6 +2765,7 @@ class Item(object):
self.code = code
self.price = price
self.location = None
self.dungeon_object = None
self.world = None
self.player = player
@@ -2792,9 +2801,16 @@ class Item(object):
def is_inside_dungeon_item(self, world):
return ((self.prize and world.prizeshuffle[self.player] in ['none', 'dungeon'])
or (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]))
or (self.bigkey and world.bigkeyshuffle[self.player] == 'none')
or (self.compass and world.compassshuffle[self.player] == 'none')
or (self.map and world.mapshuffle[self.player] == 'none'))
def is_near_dungeon_item(self, world):
return ((self.prize and world.prizeshuffle[self.player] == 'district')
or (self.smallkey and world.keyshuffle[self.player] == 'district')
or (self.bigkey and world.bigkeyshuffle[self.player] == 'district')
or (self.compass and world.compassshuffle[self.player] == 'district')
or (self.map and world.mapshuffle[self.player] == 'district'))
def get_map_location(self):
if self.location:
@@ -2826,7 +2842,7 @@ class Item(object):
def __eq__(self, other):
if type(other) is str:
return self.name == other
return self.name == other.name and self.player == other.player
return other is not None and self.name == other.name and self.player == other.player
# have 6 address that need to be filled
@@ -3266,10 +3282,10 @@ class Spoiler(object):
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player])
outfile.write('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player])
outfile.write('\n')
outfile.write('Map Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['mapshuffle'][player]))
outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player]))
outfile.write('Map Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['mapshuffle'][player])
outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['compassshuffle'][player])
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['bigkeyshuffle'][player]))
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['bigkeyshuffle'][player])
outfile.write('Prize Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['prizeshuffle'][player])
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
outfile.write('\n')
@@ -3627,9 +3643,11 @@ counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3}
# byte 6: LCCC CPAA (shuffle links, crystals ganon, pyramid, access
access_mode = {"items": 0, "locations": 1, "none": 2}
# byte 7: B?MC DDPP (big, ?, maps, compass, door_type, prize shuffle)
door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3}
prizeshuffle_mode = {'none': 0, 'dungeon': 1, 'wild': 3}
# byte 7: MMCC SSBB (maps, compass, small, big)
mapshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3}
compassshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3}
keyshuffle_mode = {'none': 0, 'off': 0, 'universal': 1, 'district': 2, 'wild': 3, 'on': 3}
bigkeyshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3}
# byte 8: HHHD DPEE (enemy_health, enemy_dmg, potshuffle, enemies)
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
@@ -3651,11 +3669,11 @@ orcrossed_mode = {"none": 0, "polar": 1, "grouped": 2, "unrestricted": 4}
# byte 12: KMB? FF?? (keep similar, mixed/tile flip, bonk drops, flute spots)
flutespot_mode = {"vanilla": 0, "balanced": 1, "random": 2}
# byte 13: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode)
# byte 13: FBBB TTPP (flute_mode, bow_mode, take_any, prize shuffle)
flute_mode = {'normal': 0, 'active': 1}
bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3} # reserved 8 modes?
take_any_mode = {'none': 0, 'random': 1, 'fixed': 2}
keyshuffle_mode = {'none': 0, 'off': 0, 'wild': 1, 'on': 1, 'universal': 2}
prizeshuffle_mode = {'none': 0, 'dungeon': 1, 'district': 2, 'wild': 3}
# additions
# byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo)
@@ -3663,9 +3681,10 @@ overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3}
key_logic_algo = {'dangerous': 0, 'partial': 1, 'strict': 2}
# byte 15: SSDD ???? (skullwoods, linked_drops, 4 free bytes)
# byte 15: SSLL ??DD (skullwoods, linked_drops, 2 free bytes, door_type)
skullwoods_mode = {'original': 0, 'restricted': 1, 'loose': 2, 'followlinked': 3}
linked_drops_mode = {'unset': 0, 'linked': 1, 'independent': 2}
door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3}
# sfx_shuffle and other adjust items does not affect settings code
@@ -3700,9 +3719,8 @@ class Settings(object):
(0x80 if w.shufflelinks[p] else 0) | ((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3)
| (0x4 if w.is_pyramid_open(p) else 0) | access_mode[w.accessibility[p]],
(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) | prizeshuffle_mode[w.prizeshuffle[p]],
(mapshuffle_mode[w.mapshuffle[p]] << 6) | (compassshuffle_mode[w.compassshuffle[p]] << 4)
| (keyshuffle_mode[w.keyshuffle[p]] << 2) | (bigkeyshuffle_mode[w.bigkeyshuffle[p]]),
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3)
| (0x4 if w.potshuffle[p] else 0) | (enemy_mode[w.enemy_shuffle[p]]),
@@ -3718,12 +3736,13 @@ class Settings(object):
| (0x20 if w.shuffle_bonk_drops[p] else 0) | (flutespot_mode[w.owFluteShuffle[p]] << 4),
(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]]),
| take_any_mode[w.take_any[p]] << 2 | prizeshuffle_mode[w.prizeshuffle[p]]),
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4),
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4
| door_type_mode[w.door_type_mode[p]]),
])
return base64.b64encode(code, "+-".encode()).decode()
@@ -3776,11 +3795,10 @@ class Settings(object):
args.crystals_ganon[p] = "random" if cgan == 8 else cgan
args.openpyramid[p] = True if settings[6] & 0x4 else False
args.bigkeyshuffle[p] = True if settings[7] & 0x80 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]
args.prizeshuffle[p] = r(prizeshuffle_mode)[settings[7] & 0x3]
args.mapshuffle[p] = r(mapshuffle_mode)[(settings[7] & 0xC0) >> 6]
args.compassshuffle[p] = r(compassshuffle_mode)[(settings[7] & 0x30) >> 4]
args.keyshuffle[p] = r(keyshuffle_mode)[(settings[7] & 0xC) >> 2]
args.bigkeyshuffle[p] = r(bigkeyshuffle_mode)[settings[7] & 0x3]
args.enemy_health[p] = r(e_health)[(settings[8] & 0xE0) >> 5]
args.enemy_damage[p] = r(e_dmg)[(settings[8] & 0x18) >> 3]
@@ -3806,7 +3824,7 @@ class Settings(object):
args.flute_mode[p] = r(flute_mode)[(settings[13] & 0x80) >> 7]
args.bow_mode[p] = r(bow_mode)[(settings[13] & 0x70) >> 4]
args.take_any[p] = r(take_any_mode)[(settings[13] & 0xC) >> 2]
args.keyshuffle[p] = r(keyshuffle_mode)[settings[13] & 0x3]
args.prizeshuffle[p] = r(prizeshuffle_mode)[settings[13] & 0x3]
if len(settings) > 14:
args.pseudoboots[p] = True if settings[14] & 0x80 else False
@@ -3817,6 +3835,7 @@ class Settings(object):
if len(settings) > 15:
args.skullwoods[p] = r(skullwoods_mode)[(settings[15] & 0xc0) >> 6]
args.linked_drops[p] = r(linked_drops_mode)[(settings[15] & 0x30) >> 4]
args.door_type_mode[p] = r(door_type_mode)[(settings[15] & 0x3)]
class KeyRuleType(FastEnum):
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## 0.5.1.0
- New "Nearby" Dungeon Item Shuffle option
- Fixed issue with smith follower getting deleted incorrectly on s+q
- Various generation and logic fixes for HMG/NL
- \~Merged in DR v1.4.6.2~
## 0.5.0.6
- New improved Pseudoboots behavior
- Fixed issue with missing Blue Potion in Dark Lake Shop in Inverted
+6 -7
View File
@@ -106,8 +106,7 @@ def parse_cli(argv, no_defaults=False):
ret = parser.parse_args(argv)
if ret.keysanity:
ret.mapshuffle, ret.compassshuffle, ret.bigkeyshuffle = [True] * 3
ret.keyshuffle = 'wild'
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = 'wild' * 4
if ret.keydropshuffle:
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
@@ -137,7 +136,7 @@ def parse_cli(argv, no_defaults=False):
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'prizeshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference',
'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
'skullwoods', 'linked_drops',
'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
@@ -222,10 +221,10 @@ def parse_settings():
"pottery": "none",
"colorizepots": True,
"shufflepots": False,
"mapshuffle": False,
"compassshuffle": False,
"mapshuffle": "none",
"compassshuffle": "none",
"keyshuffle": "none",
"bigkeyshuffle": False,
"bigkeyshuffle": "none",
"prizeshuffle": "none",
"keysanity": False,
"door_shuffle": "vanilla",
@@ -285,7 +284,7 @@ def parse_settings():
"seed": "",
"count": 1,
"startinventory": "",
"beemizer": 0,
'beemizer': '0',
"remote_items": False,
"race": False,
"customitemarray": {
+5 -5
View File
@@ -477,7 +477,7 @@ def choose_portals(world, player):
allowed = {name: set(group[0]) for group in world.dungeon_pool[player] for name in group[0]}
# key drops allow the big key in the right place in Desert Tiles 2
bk_shuffle = world.bigkeyshuffle[player] or world.pottery[player] not in ['none', 'cave']
bk_shuffle = world.bigkeyshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']
std_flag = world.mode[player] == 'standard'
# roast incognito doors
world.get_room(0x60, player).delete(5)
@@ -2322,7 +2322,7 @@ def validate_trap_layout(proposal, builder, start_regions, paths, world, player)
if bk_special_loc:
if not state.found_forced_bk():
return False
if world.accessibility[player] != 'beatable':
if world.accessibility[player] != 'none':
all_locations = [l for r in builder.master_sector.region_set() for l in world.get_region(r, player).locations]
if any(l not in state.found_locations for l in all_locations):
return False
@@ -2689,12 +2689,12 @@ def calc_used_dungeon_items(builder, world, player):
basic_flag = world.doorShuffle[player] == 'basic'
base = 0 if basic_flag else 2 # at least 2 items per dungeon, except in basic
base = max(count_reserved_locations(world, player, builder.location_set), base)
if not world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] == 'none':
if builder.bk_required and not builder.bk_provided:
base += 1
if not world.compassshuffle[player] and (builder.name not in ['Hyrule Castle', 'Agahnims Tower'] or not basic_flag):
if world.compassshuffle[player] == 'none' and (builder.name not in ['Hyrule Castle', 'Agahnims Tower'] or not basic_flag):
base += 1
if not world.mapshuffle[player] and (builder.name != 'Agahnims Tower' or not basic_flag):
if world.mapshuffle[player] == 'none' and (builder.name != 'Agahnims Tower' or not basic_flag):
base += 1
if world.prizeshuffle[player] == 'dungeon' and builder.name not in ['Hyrule Castle', 'Agahnims Tower', 'Ganons Tower']:
base += 1
+7 -7
View File
@@ -231,7 +231,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
start = ExplorationState(dungeon=name)
start.big_key_special = bk_special
group_flags, door_map = find_bk_groups(name, available_sectors, proposed_map, bk_special)
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
bk_flag = False if world.bigkeyshuffle[player] != 'none' and not bk_special else bk_needed
def exception(d):
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
@@ -436,7 +436,7 @@ def check_valid(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, a
if len(dungeon.keys()) <= 1 and len(proposed_map.keys()) < len(doors_to_connect):
return False
# origin has no more hooks, but not all doors have been proposed
if not world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] == 'none':
possible_bks = len(dungeon['Origin'].possible_bk_locations)
if bk_special and check_for_special(dungeon['Origin'].visited_regions):
possible_bks = 1
@@ -470,7 +470,7 @@ def check_valid(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, a
if len(outstanding_doors[key]) > 0 and len(hangers[key]) == 0 and len(hooks[opp_key]) == 0:
return False
all_visited = set()
bk_possible = not bk_needed or (world.bigkeyshuffle[player] and not bk_special)
bk_possible = not bk_needed or (world.bigkeyshuffle[player] != 'none' and not bk_special)
for piece in dungeon.values():
all_visited.update(piece.visited_regions)
if ((not bk_possible and len(piece.possible_bk_locations) > 0) or
@@ -544,7 +544,7 @@ def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_re
start = ExplorationState(dungeon=name)
start.big_key_special = bk_special
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
bk_flag = False if world.bigkeyshuffle[player] != 'none' and not bk_special else bk_needed
def exception(d):
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
@@ -1775,11 +1775,11 @@ def requested_dungeon_items(world, player):
num = 0
if world.prizeshuffle[player] == 'dungeon':
num += 1
if not world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] == 'none':
num += 1
if not world.compassshuffle[player]:
if world.compassshuffle[player] == 'none':
num += 1
if not world.mapshuffle[player]:
if world.mapshuffle[player] == 'none':
num += 1
return num
+42 -13
View File
@@ -45,12 +45,12 @@ def fill_dungeons_restrictive(world, shuffled_locations):
for item in world.get_items():
if ((item.prize and world.prizeshuffle[item.player] != 'none')
or (item.smallkey and world.keyshuffle[item.player] != 'none')
or (item.bigkey and world.bigkeyshuffle[item.player])):
or (item.bigkey and world.bigkeyshuffle[item.player] != 'none')):
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] not in ['none', 'district']) or (item.compass and world.compassshuffle[item.player] not in ['none', 'district']):
item.priority = True
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world) or item.is_near_dungeon_item(world)]
bigs, smalls, prizes, others = [], [], [], []
for i in dungeon_items:
(bigs if i.bigkey else smalls if i.smallkey else prizes if i.prize else others).append(i)
@@ -76,6 +76,19 @@ def fill_dungeons_restrictive(world, shuffled_locations):
prizes_copy = prizes.copy()
for attempt in range(15):
try:
for player in range(1, world.players + 1):
if world.prizeshuffle[player] == 'district':
dungeon_pool = []
for dungeon in world.dungeons:
from Dungeons import dungeon_table
if dungeon.player == player and dungeon_table[dungeon.name].prize:
dungeon_pool.append(dungeon)
random.shuffle(dungeon_pool)
for item in prizes:
if item.player == player:
dungeon = dungeon_pool.pop()
dungeon.prize = item
item.dungeon_object = dungeon
random.shuffle(prizes)
random.shuffle(shuffled_locations)
prize_state_base = all_state_base.copy()
@@ -86,7 +99,6 @@ def fill_dungeons_restrictive(world, shuffled_locations):
logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, 14 - attempt)
prizes = prizes_copy.copy()
for dungeon in world.dungeons:
if world.prizeshuffle[dungeon.player] == 'dungeon':
dungeon.prize = None
for prize in prizes:
if prize.location:
@@ -186,7 +198,8 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
test_state.sweep_for_events()
if location.can_fill(test_state, item_to_place, perform_access_check):
if valid_key_placement(item_to_place, location, key_pool, test_state, world):
if item_to_place.prize or valid_dungeon_placement(item_to_place, location, world):
if (item_to_place.prize and world.prizeshuffle[item_to_place.player] == 'none') \
or valid_dungeon_placement(item_to_place, location, world):
return location
if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize:
location.item = None
@@ -203,6 +216,10 @@ def valid_key_placement(item, location, key_pool, collection_state, world):
or world.keyshuffle[item.player] == 'universal' or world.logic[item.player] == 'nologic'):
return True
dungeon = location.parent_region.dungeon
if not dungeon and item.is_near_dungeon_item(world):
check_dungeon = world.get_dungeon(item.dungeon, item.player) if item.dungeon else item.dungeon_object
if len([d for d in location.parent_region.districts if d in check_dungeon.districts]):
dungeon = check_dungeon
if dungeon:
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
return True
@@ -236,13 +253,22 @@ def valid_reserved_placement(item, location, world):
def valid_dungeon_placement(item, location, world):
if location.parent_region.dungeon:
layout = world.dungeon_layouts[location.player][location.parent_region.dungeon.name]
dungeon = location.parent_region.dungeon
if not dungeon and item.is_near_dungeon_item(world):
check_dungeon = world.get_dungeon(item.dungeon, item.player) if item.dungeon else item.dungeon_object
if len([d for d in location.parent_region.districts if d in check_dungeon.districts]):
dungeon = check_dungeon
if dungeon:
layout = world.dungeon_layouts[location.player][dungeon.name]
if not is_dungeon_item(item, world) or item.player != location.player:
if item.prize and item.is_near_dungeon_item(world):
return item.dungeon_object == dungeon and layout.free_items > 0
return layout.free_items > 0
elif item.prize:
return not dungeon.prize and layout.dungeon_items > 0
else:
# the second half probably doesn't matter much - should always return true
return item.dungeon == location.parent_region.dungeon.name and layout.dungeon_items > 0
return item.dungeon == dungeon.name and layout.dungeon_items > 0
return not is_dungeon_item(item, world)
@@ -267,14 +293,15 @@ def track_dungeon_items(item, location, world):
layout.free_items -= 1
if item.prize:
location.parent_region.dungeon.prize = item
item.dungeon_object = location.parent_region.dungeon
def is_dungeon_item(item, world):
return ((item.prize and world.prizeshuffle[item.player] in ['none', 'dungeon'])
or (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]))
or (item.bigkey and world.bigkeyshuffle[item.player] == 'none')
or (item.compass and world.compassshuffle[item.player] == 'none')
or (item.map and world.mapshuffle[item.player] == 'none'))
def recovery_placement(item_to_place, locations, world, state, base_state, itempool, perform_access_check, attempted,
@@ -816,7 +843,8 @@ 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] != 'none' 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] != 'none' 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:
@@ -895,7 +923,8 @@ def balance_multiworld_progression(world):
sphere_locations.add(location)
for location in sphere_locations:
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):
if location.event and (world.keyshuffle[location.item.player] != 'none' or not location.item.smallkey) \
and (world.bigkeyshuffle[location.item.player] != 'none' or not location.item.bigkey):
state.collect(location.item, True, location)
checked_locations |= sphere_locations
+15 -14
View File
@@ -348,11 +348,11 @@ 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.prize and world.prizeshuffle[player] == 'wild')
or (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]))])
and ((item.prize and world.prizeshuffle[player] not in ['none', 'dungeon', 'district'])
or (item.smallkey and world.keyshuffle[player] not in ['none', 'district'])
or (item.bigkey and world.bigkeyshuffle[player] not in ['none', 'district'])
or (item.map and world.mapshuffle[player] not in ['none', 'district'])
or (item.compass and world.compassshuffle[player] not in ['none', 'district']))])
if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']:
keys_to_remove = 2
@@ -462,7 +462,7 @@ def generate_itempool(world, player):
'3': {'trap': 50, 'bee': 50},
'4': {'trap': 100}}
def beemizer(item):
if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type:
if world.beemizer[item.player] != '0' and not item.advancement and not item.priority and not item.type:
choice = random.choices(list(beeweights[world.beemizer[item.player]].keys()), weights=list(beeweights[world.beemizer[item.player]].values()))[0]
return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player)
return item
@@ -753,6 +753,7 @@ def fill_prizes(world, attempts=15):
fill_restrictive(world, all_state, prize_locs, prizepool, single_player_placement=True)
for prize_loc in crystal_locations:
prize_loc.parent_region.dungeon.prize = prize_loc.item
prize_loc.item.dungeon_object = prize_loc.parent_region.dungeon
except FillError as e:
logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, attempts - attempt - 1)
for location in empty_crystal_locations:
@@ -1434,12 +1435,12 @@ def make_customizer_pool(world, player):
or item_name.startswith('Crystal') or item_name.endswith('Pendant')):
d_item = ItemFactory(item_name, player)
if ((d_item.prize and world.prizeshuffle[player] in ['none', 'dungeon'])
or (d_item.bigkey and not world.bigkeyshuffle[player])
or (d_item.compass and not world.compassshuffle[player])
or (d_item.map and not world.mapshuffle[player])):
or (d_item.bigkey and world.bigkeyshuffle[player] == 'none')
or (d_item.compass and world.compassshuffle[player] == 'none')
or (d_item.map and world.mapshuffle[player] == 'none')):
d_name = d_item.dungeon
dungeon = world.get_dungeon(d_name, player)
current_amount = 1 if d_item == dungeon.big_key or d_item in dungeon.dungeon_items else 0
current_amount = 1 if dungeon.big_key and (d_item == dungeon.big_key or d_item in dungeon.dungeon_items) else 0
additional_amount = amount - current_amount
possible_fit = min(additional_amount, len(dungeon_locations[d_name])-dungeon_count[d_name])
if possible_fit > 0:
@@ -1450,7 +1451,7 @@ def make_customizer_pool(world, player):
pool.extend([item_name] * amount)
else:
dungeon = world.get_dungeon(d_item.dungeon, player)
current_amount = 1 if d_item == dungeon.big_key or d_item in dungeon.dungeon_items else 0
current_amount = 1 if dungeon.big_key and (d_item == dungeon.big_key or d_item in dungeon.dungeon_items) else 0
additional_amount = amount - current_amount
dungeon.dungeon_items.extend([d_item] * additional_amount)
else:
@@ -1694,7 +1695,7 @@ def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_
def is_dungeon_item(item, world, player):
return (((item.startswith('Crystal') or item.endswith('Pendant')) and world.prizeshuffle[player] in ['none', 'dungeon'])
or (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]))
or (item.startswith('Big Key') and world.bigkeyshuffle[player] == 'none')
or (item.startswith('Compass') and world.compassshuffle[player] == 'none')
or (item.startswith('Map') and world.mapshuffle[player] == 'none'))
+9 -9
View File
@@ -1303,7 +1303,7 @@ def check_rules_deep(original_counter, key_layout, world, player):
big_avail = counter.big_key_opened or bk_drop
big_maybe_not_found = not counter.big_key_opened and not bk_drop # better named as big_missing?
if not key_layout.big_key_special and not big_avail:
if world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] != 'none':
big_avail = True
else:
for location in counter.free_locations:
@@ -1430,7 +1430,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance, is_atgt_swappe
def validate_bk_layout(proposal, builder, start_regions, world, player):
bk_special = check_bk_special(builder.master_sector.regions, world, player)
if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special):
if world.bigkeyshuffle[player] != 'none' and (world.dropshuffle[player] != 'none' or not bk_special):
return True
flat_proposal = flatten_pair_list(proposal)
state = ExplorationState(dungeon=builder.name)
@@ -1438,7 +1438,7 @@ def validate_bk_layout(proposal, builder, start_regions, world, player):
for region in start_regions:
dungeon_entrance, portal_door = find_outside_connection(region)
prize_relevant_flag = prize_relevance_sig2(start_regions, builder.name, dungeon_entrance, world.is_atgt_swapped(player))
if prize_relevant_flag and world.prizeshuffle[player] == 'none':
if prize_relevant_flag and world.prizeshuffle[player] in ['none', 'dungeon']:
state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance
# key_layout.prize_relevant = prize_relevant_flag
@@ -1469,7 +1469,7 @@ def validate_key_layout(key_layout, world, player):
for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region)
prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance, world.is_atgt_swapped(player))
if prize_relevant_flag and world.prizeshuffle[player] == 'none':
if prize_relevant_flag and world.prizeshuffle[player] in ['none', 'dungeon']:
state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = prize_relevant_flag
@@ -1606,7 +1606,7 @@ def determine_prize_lock(key_layout, world, player):
for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region)
prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance, world.is_atgt_swapped(player))
if prize_relevant_flag and world.prizeshuffle[player] == 'none':
if prize_relevant_flag and world.prizeshuffle[player] in ['none', 'dungeon']:
state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = prize_relevant_flag
@@ -1650,13 +1650,13 @@ def cnt_avail_small_locations_by_ctr(free_locations, counter, layout, world, pla
def cnt_avail_big_locations(ttl_locations, state, world, player):
if not world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] == 'none':
return max(0, ttl_locations - state.used_locations) if not state.big_key_special else 0
return 1 if not state.big_key_special else 0
def cnt_avail_big_locations_by_ctr(ttl_locations, counter, layout, world, player):
if not world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] == 'none':
bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0
used_locations = max(0, counter.used_keys - len(counter.key_only_locations)) + bk_adj
return max(0, ttl_locations - used_locations) if not layout.big_key_special else 0
@@ -1683,7 +1683,7 @@ def create_key_counters(key_layout, world, player):
for region in key_layout.start_regions:
dungeon_entrance, portal_door = find_outside_connection(region)
prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance, world.is_atgt_swapped(player))
if prize_relevant_flag and world.prizeshuffle[player] == 'none':
if prize_relevant_flag and world.prizeshuffle[player] in ['none', 'dungeon']:
state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = prize_relevant_flag
@@ -2096,7 +2096,7 @@ def validate_key_placement(key_layout, world, player):
bigkey_name = dungeon_bigs[key_layout.sector.name]
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]:
if world.bigkeyshuffle[player] != 'none':
max_counter = find_max_counter(key_layout)
big_key_outside = bigkey_name not in (l.item.name for l in max_counter.free_locations if l.item)
for i in world.precollected_items:
+13 -14
View File
@@ -28,7 +28,7 @@ from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dunge
from Fill import dungeon_tracking
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations
from UnderworldGlitchRules import create_hybridmajor_connections, get_hybridmajor_connection_entrances
from UnderworldGlitchRules import connect_hmg_entrances_regions, create_hmg_entrances_regions
from Utils import output_path, parse_player_names
from source.item.District import init_districts
@@ -40,7 +40,7 @@ from source.enemizer.DamageTables import DamageTable
from source.enemizer.Enemizer import randomize_enemies
from source.rom.DataTables import init_data_tables
version_number = '1.4.6'
version_number = '1.4.7.2'
version_branch = '-u'
__version__ = f'{version_number}{version_branch}'
@@ -224,7 +224,8 @@ def main(args, seed=None, fish=None):
for player in range(1, world.players + 1):
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(world, player)
create_hmg_entrances_regions(world, player)
connect_hmg_entrances_regions(world, player)
generate_itempool(world, player)
verify_item_pool_config(world)
@@ -479,7 +480,7 @@ def init_world(args, fish):
world.enemy_health = args.enemy_health.copy()
world.enemy_damage = args.enemy_damage.copy()
world.any_enemy_logic = args.any_enemy_logic.copy()
world.beemizer = args.beemizer.copy()
world.beemizer = {player: str(args.beemizer[player]) for player in range(1, world.players + 1)}
world.intensity = {player: 'random' if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
world.door_type_mode = args.door_type_mode.copy()
world.trap_door_mode = args.trap_door_mode.copy()
@@ -643,6 +644,10 @@ def copy_world(world):
connection = Entrance(player, 'Uncle S&Q', parent)
parent.exits.append(connection)
connection.connect(target)
# This makes the regions for HMG only
# we'll connect them later after all other connections are made (OW <=> UW, Portals)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hmg_entrances_regions(ret, player)
# copy bosses
for dungeon in world.dungeons:
@@ -659,7 +664,6 @@ def copy_world(world):
# We have to skip these for now. They require both the rest of the entrances _and_ the dungeon portals to be copied first
# We will connect them later
hmg_entrances = get_hybridmajor_connection_entrances()
for region in world.regions:
copied_region = ret.get_region(region.name, region.player)
@@ -670,8 +674,6 @@ def copy_world(world):
for location in copied_region.locations:
location.parent_region = copied_region
for entrance in region.entrances:
if entrance.name in hmg_entrances:
continue
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
for exit in region.exits:
if exit.connected_region:
@@ -739,14 +741,14 @@ def copy_world(world):
categorize_world_regions(ret, player)
create_farm_locations(ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
connect_hmg_entrances_regions(ret, player)
for region in world.regions:
copied_region = ret.get_region(region.name, region.player)
for entrance in region.entrances:
if entrance.name not in hmg_entrances:
continue
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
ent = ret.get_entrance(entrance.name, entrance.player)
if (ent.connected_region != copied_region):
ent.connect(copied_region)
for player in range(1, world.players + 1):
set_rules(ret, player)
@@ -899,9 +901,6 @@ def copy_world_premature(world, player):
for portal in world.dungeon_portals[player]:
connect_portal(portal, ret, player)
if world.logic[player] in ('nologic', 'hybridglitches'):
create_hybridmajor_connections(ret, player)
set_rules(ret, player)
return ret
+4 -1
View File
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
from OverworldGlitchRules import create_owg_connections
from Utils import bidict
version_number = '0.5.0.6'
version_number = '0.5.1.0'
# branch indicator is intentionally different across branches
version_branch = ''
@@ -1404,6 +1404,9 @@ def can_reach_smith(world, player):
if world.logic[player] in ['noglitches', 'minorglitches'] and not world.is_tile_swapped(0x29, player):
blank_state.collect(ItemFactory('Titans Mitts', player), True)
blank_state.collect(ItemFactory('Moon Pearl', player), True)
if not world.bombbag[player]:
blank_state.collect(ItemFactory('Farmable Bombs', player), True)
blank_state.collect(ItemFactory('Farmable Rupees', player), True)
found = False
explored_regions = list()
+8
View File
@@ -268,6 +268,10 @@ As far as map trackers, Bonk Locations are supported on `CodeTracker` when the B
- 1 8x Bomb Pack
- 1 Good Bee
## Nearby Dungeon Items
This is a new option in addition to the traditional wild vs non-wild (keysanity/non-keysanity) options for all the dungeon item types (maps, compasses, small keys, big keys, prizes). This new option shuffles dungeon items into locations somewhere either within the dungeon that it is assigned to or within the surrounding district of that dungeon.
## Prize Shuffle
A new option has been added to shuffle the 10 dungeon prizes in ways that they haven't been shuffled before. This means that dungeon prizes can be found in other item locations, such as chests or free-standing item locations. This also means that bosses are able to drop a 2nd item in place of the shuffled prize.
@@ -280,6 +284,10 @@ This is the normal prize behavior that has been a part of rando up until now. Th
This option shuffles the prize into a location somewhere within the dungeon that it is assigned to.
### Nearby
This option shuffles the prize into a location somewhere either within the dungeon that it is assigned to or within the surrounding district of that dungeon.
### Randomized
This option freely shuffles the prizes throughout the world. While the dungeon prizes can end up anywhere, they still are assigned to a specific dungeon. When you defeat the boss of a certain dungeon, checking the map on the overworld will reveal the location WHERE you can find the prize, an example shown [here](https://zelda.codemann8.com/images/shared/prizemap-all.gif). Finding the map will still reveal WHAT the prize is. If you defeated a boss but haven't collected the map for that dungeon, the prize will be indicated by a red X, example shown [here](https://zelda.codemann8.com/images/shared/prizemap-boss.gif). If you collected a map but haven't defeated the boss yet, the icon indicator on the map will be shown on the top edge (for LW dungeons) or the bottom edge (for DW dungeons), but it will show you WHAT the prize is for that dungeon, an example of that is shown [here](https://zelda.codemann8.com/images/shared/prizemap-map.gif).
+17 -9
View File
@@ -141,12 +141,20 @@ These are now independent of retro mode and have three options: None, Random, an
# Patch Notes
1.4.6
- Restores original Sanc & Quit behavior, if Aga1 is not dead, then no quick pyramid warp
- Fixed problem with Lite/Lean shuffling some fairy caves unnecessarily
- The 300 Rupees item is now marked as priority, so it will not be used as GT junk fill (this matches a change in the VT randomizer)
- Murahdahla is now logically accessible in inverted mode as a bunny. May affect some beatable-only TFH seeds by allowing Moon Pearl to be inaccessible.
- Fixed an issue around PreferredLocationGroups in customizer which would not work with pre-activated Ocraina or priority items.
- Minor fix on triforce text
- Enemy bans for poor placements
* 1.4.7.2
- Fixed an issue with shuffle_ganon/fix_gtower_exit causing a generation failure
- More HMG fixes by Muffins
* 1.4.7.1
- Fixed an issue with the repaired "beemizer" setting not being backwards compatible
* 1.4.7
- Fixed generation error with Big Key in starting inventory (thanks Cody!)
- HMG/NL logic fixes by Muffins
- Enemizer: Disabled Walking Zora in the UW due to crash with Swamola (they ignore a lot of collison anyway)
- Enemizer: Fixed an issue with enemizer bush sprites
- Enemizer: Banned new Mimics from being the randomized bush sprite due to crash
- "Beatable" or "accessibility: none" can now use randomized trap doors to seal off entire parts of dungeons (was intended, bug prevented the logic skip)
- Logic error with enemizer and standard should use new enemy logic rules
- Fixed a bug with the inconsistent treatment of the beemizer setting
- Fixed an issue with returning Blacksmith in Simple shuffle (when blacksmith is at Link's House)
- Fixed an issue with dark sanctuary spawn at tavern north door (thanks Codemann!)
- Various enemy bans for the last few months
+34 -28
View File
@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'a51679f0e31ee29f59247545d705992a'
RANDOMIZERBASEHASH = '1fde4fa24bc9d3efe450c3bc30e4cf2c'
class JsonRom(object):
@@ -480,14 +480,14 @@ def patch_rom(world, rom, player, team, is_mystery=False):
rom.write_byte(address, value)
# patch music
if world.mapshuffle[player]:
if world.mapshuffle[player] != 'none':
music = random.choice([0x11, 0x16])
else:
music = 0x11 if 'Pendant' in dungeon.prize.name else 0x16
for music_address in dungeon_music_addresses[dungeon.name]:
rom.write_byte(music_address, music)
if world.mapshuffle[player]:
if world.mapshuffle[player] != 'none':
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
if world.doorShuffle[player] != 'vanilla':
@@ -1131,7 +1131,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
ERtimeincrease = 10
else:
ERtimeincrease = 20
if world.keyshuffle[player] != 'none' or world.bigkeyshuffle[player] or world.mapshuffle[player]:
if world.keyshuffle[player] != 'none' or world.bigkeyshuffle[player] != 'none' or world.mapshuffle[player] != 'none':
ERtimeincrease = ERtimeincrease + 15
if world.clock_mode == 'none':
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
@@ -1268,34 +1268,36 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# Bitfield - enable text box to show with free roaming items
#
# --po bmcs
# -tpo bmcs
# t - suppress "this dungeon" textboxes (temporary fix)
# p - enabled for non-prize crystals
# o - enabled for outside dungeon items
# o - enabled for outside dungeon items (unused currently?)
# b - enabled for inside big keys
# m - enabled for inside maps
# c - enabled for inside compasses
# s - enabled for inside small keys
rom.write_byte(0x18016A, 0x10 | ((0x20 if world.prizeshuffle[player] == 'wild' else 0x00)
| (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
rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] else 0x00) # maps showing crystals on overworld
free_item_text = 0x40 if 'district' in [world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]] else 0x00
rom.write_byte(0x18016A, free_item_text | 0x10 | ((0x20 if world.prizeshuffle[player] not in ['none', 'dungeon'] else 0x00)
| (0x01 if world.keyshuffle[player] not in ['none', 'universal'] else 0x00)
| (0x02 if world.compassshuffle[player] != 'none' else 0x00)
| (0x04 if world.mapshuffle[player] != 'none' else 0x00)
| (0x08 if world.bigkeyshuffle[player] != 'none' else 0x00))) # free roaming item text boxes
rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] not in ['none', 'district'] else 0x00) # maps showing crystals on overworld
# compasses showing dungeon count
compass_mode = 0x80 if world.compassshuffle[player] else 0x00
compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'district'] else 0x00
if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off':
pass
elif world.dungeon_counters[player] == 'on':
compass_mode |= 0x02 # always on
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
elif (world.compassshuffle[player] not in ['none', 'district'] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']):
compass_mode |= 0x01 # show on pickup
if world.overworld_map[player] == 'map':
compass_mode |= 0x10 # show icon if map is collected
elif world.overworld_map[player] == 'compass':
compass_mode |= 0x20 # show icon if compass is collected
if world.prizeshuffle[player] == 'wild':
if world.prizeshuffle[player] not in ['none', 'dungeon', 'district']:
compass_mode |= 0x40 # show icon if boss is defeated, hide if collected
rom.write_byte(0x18003C, compass_mode)
@@ -1331,7 +1333,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
map_index = max(0, dungeon_index - 2)
# write out dislocated coords
if map_index >= 0x02 and map_index < 0x18 and (world.overworld_map[player] != 'default' or world.prizeshuffle[player] == 'wild'):
if map_index >= 0x02 and map_index < 0x18 and (world.overworld_map[player] != 'default' or world.prizeshuffle[player] not in ['none', 'dungeon', 'district']):
owid_map = [0x1E, 0x30, 0xFF, 0x7B, 0x5E, 0x70, 0x40, 0x75, 0x03, 0x58, 0x47]
x_map_position_generic = [0x03c0, 0x0740, 0xff00, 0x03c0, 0x01c0, 0x0bc0, 0x05c0, 0x09c0, 0x0ac0, 0x07c0, 0x0dc0]
y_map_position_generic = [0xff00, 0xff00, 0xff00, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0xff00, 0x0fc0, 0x0fc0]
@@ -1345,7 +1347,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
write_int16(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+6, y_map_position_generic[idx])
# write out icon coord data
if world.prizeshuffle[player] == 'wild' and dungeon_table[dungeon].prize:
if world.prizeshuffle[player] not in ['none', 'dungeon', 'district'] and dungeon_table[dungeon].prize:
dungeon_obj = world.get_dungeon(dungeon, player)
entrance = dungeon_obj.prize.get_map_location()
coords = get_entrance_coords(entrance)
@@ -1385,7 +1387,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# figure out compass entrances and what world (light/dark)
write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6), coords)
if world.prizeshuffle[player] != 'wild' and dungeon_table[dungeon].prize:
if world.prizeshuffle[player] in ['none', 'dungeon', 'district'] and dungeon_table[dungeon].prize:
# prize location
write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+8, coords)
@@ -1424,11 +1426,11 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# b - Big Key
# a - Small Key
#
enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] == 'wild'
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
enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] not in ['none', 'dungeon', 'district']
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] not in ['none', 'universal'] else 0x00)
| (0x02 if world.bigkeyshuffle[player] != 'none' else 0x00)
| (0x04 if world.mapshuffle[player] != 'none' or enable_menu_map_check else 0x00)
| (0x08 if world.compassshuffle[player] != 'none' else 0x00) # free roaming items in menu
| (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon
def get_reveal_bytes(itemName):
@@ -1528,7 +1530,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# rom.write_byte(snes_to_pc(0x0DB730), 0x08) # allows chickens to travel across water
# allow smith into multi-entrance caves in appropriate shuffles
if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
if world.shuffle[player] in ['restricted', 'simple', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity']:
rom.write_byte(0x18004C, 0x01)
# set correct flag for hera basement item
@@ -2203,11 +2205,11 @@ def write_strings(rom, world, player, team):
this_hint = this_hint[0].upper() + this_hint[1:]
tt[hint_locations.pop(0)] = this_hint
items_to_hint.remove(flute_item)
if world.keyshuffle[player] == 'wild':
if world.keyshuffle[player] not in ['none', 'universal']:
items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] != 'none':
items_to_hint.extend(BigKeys)
if world.prizeshuffle[player] == 'wild':
if world.prizeshuffle[player] not in ['none', 'dungeon']:
items_to_hint.extend(Prizes)
random.shuffle(items_to_hint)
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 8
@@ -2323,10 +2325,14 @@ def write_strings(rom, world, player, team):
crystal5 = world.find_items('Crystal 5', player)[0]
crystal6 = world.find_items('Crystal 6', player)[0]
greenpendant = world.find_items('Green Pendant', player)[0]
if world.prizeshuffle[player] == 'none':
if world.prizeshuffle[player] in ['none', 'dungeon']:
(crystal5, crystal6, greenpendant) = tuple([x.parent_region.dungeon.name for x in [crystal5, crystal6, greenpendant]])
tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5, crystal6)
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant
elif world.prizeshuffle[player] == 'district':
(crystal5, crystal6, greenpendant) = tuple([x.item.dungeon_object.name for x in [crystal5, crystal6, greenpendant]])
tt['bomb_shop'] = 'Big Bomb?\nThe crystals can be found near %s and %s.' % (crystal5, crystal6)
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom near %s' % greenpendant
else:
tt['bomb_shop'] = 'Big Bomb?\nThe crystals can be found %s and %s.' % (crystal5.hint_text, crystal6.hint_text)
tt['sahasrahla_bring_courage'] = 'My family heirloom can be found %s' % greenpendant.hint_text
+20 -8
View File
@@ -1614,16 +1614,28 @@ def standard_rules(world, player):
add_rule(world.get_location('Secret Passage', player), lambda state: standard_escape_rule(state))
escape_builder = world.dungeon_layouts[player]['Hyrule Castle']
room_map = world.data_tables[player].uw_enemy_table.room_map
stats = world.data_tables[player].enemy_stats
for region in escape_builder.master_sector.regions:
for loc in region.locations:
add_rule(loc, lambda state: standard_escape_rule(state))
if region.name in std_kill_rooms:
for ent in std_kill_rooms[region.name][0]:
add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state))
for ent in std_kill_rooms[region.name][1]:
entrances, trap_ables, room_id, enemy_list = std_kill_rooms[region.name]
rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region)
for ent in entrances:
entrance = world.get_entrance(ent, player)
if entrance.door.trapped:
add_rule(entrance, lambda state: standard_escape_rule(state))
if not entrance.door or not entrance.door.entranceFlag:
add_rule_new(entrance, rule)
for ent in trap_ables:
entrance = world.get_entrance(ent, player)
if entrance.door.trapped and not entrance.door.entranceFlag:
add_rule_new(entrance, rule)
else:
for loc in region.locations:
if loc.name in kill_chests:
locations, room_id, enemy_list = kill_chests[loc.name]
rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region)
add_rule_new(world.get_location(loc, player), rule)
else:
add_rule(loc, lambda state: standard_escape_rule(state))
set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player))
set_rule(world.get_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player))
@@ -2017,7 +2029,7 @@ def add_hmg_key_logic_rules(world, player):
def add_key_logic_rules(world, player):
key_logic = world.key_logic[player]
eval_func = eval_small_key_door
if world.key_logic_algorithm[player] == 'strict' and world.keyshuffle[player] == 'wild':
if world.key_logic_algorithm[player] == 'strict' and world.keyshuffle[player] not in ['none', 'universal']:
eval_func = eval_small_key_door_strict
elif world.key_logic_algorithm[player] != 'dangerous':
eval_func = eval_small_key_door_partial
+33 -30
View File
@@ -1,9 +1,7 @@
import functools
from BaseClasses import Entrance, DoorType
from BaseClasses import Entrance, DoorType, Door
from DoorShuffle import connect_simple_door
import Rules
from OverworldGlitchRules import create_no_logic_connections
from Doors import create_door
kikiskip_spots = [
("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal")
@@ -13,7 +11,7 @@ mirehera_spots = [("Mire to Hera Clip", "Mire Torches Top", "Hera Portal")]
heraswamp_spots = [("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal")]
icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop - Top")]
icepalace_spots = [("Ice Lobby Clip", "Ice Lobby", "Ice Bomb Drop - Top")]
thievesdesert_spots = [
("Thieves to Desert West Clip", "Thieves Attic", "Desert West Portal"),
@@ -29,10 +27,7 @@ paradox_spots = [
("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")
]
# Create connections between dungeons/locations
def create_hybridmajor_connections(world, player):
fix_fake_worlds = world.fix_fake_world[player]
def create_hmg_entrances_regions(world, player):
for spots in [
kikiskip_spots,
mirehera_spots,
@@ -42,35 +37,43 @@ def create_hybridmajor_connections(world, player):
specrock_spots,
paradox_spots,
]:
create_no_logic_connections(player, world, spots, connect_external=fix_fake_worlds)
for entrance, parent_region, _, *_ in spots:
parent = world.get_region(parent_region, player)
connection = Entrance(player, entrance, parent)
connection.spot_type = 'HMG'
if connection not in parent.exits:
parent.exits.append(connection)
ip_bomb_top_reg = world.get_region("Ice Bomb Drop - Top", player)
ip_clip_entrance = Entrance(player, "Ice Bomb Drop Clip", ip_bomb_top_reg)
ip_bomb_top_reg.exits.append(ip_clip_entrance)
def connect_hmg_entrances_regions(world, player):
for spots in [
kikiskip_spots,
mirehera_spots,
heraswamp_spots,
icepalace_spots,
thievesdesert_spots,
specrock_spots,
paradox_spots,
]:
for entrance, _, target_region, *_ in spots:
connection = world.get_entrance(entrance, player)
if world.fix_fake_world[player] and target_region.endswith(" Portal"):
target = world.get_portal(target_region[:-7], player).find_portal_entrance().parent_region
else:
target = world.get_region(target_region, player)
connection.connect(target)
# Add the new Ice path (back of bomb drop to front) to the world and model it properly
clip_door = create_door(player, "Ice Bomb Drop Clip", DoorType.Logical)
ip_clip_entrance = world.get_entrance('Ice Bomb Drop Clip', 1)
clip_door = Door(player, "Ice Bomb Drop Clip", DoorType.Logical, ip_clip_entrance)
world.doors += [clip_door]
world.initialize_doors([clip_door])
ice_bomb_top_reg = world.get_region("Ice Bomb Drop - Top", player)
ice_bomb_top_reg.exits.append(
Entrance(player, "Ice Bomb Drop Clip", ice_bomb_top_reg)
)
connect_simple_door(world, "Ice Bomb Drop Clip", "Ice Bomb Drop", player)
def get_hybridmajor_connection_entrances():
connections = []
for connector in (
kikiskip_spots
+ mirehera_spots
+ heraswamp_spots
+ icepalace_spots
+ thievesdesert_spots
+ specrock_spots
+ paradox_spots
):
connections.append(connector[0])
connections.append('Ice Bomb Drop Clip')
return set(connections)
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
# This creates a copy of the input state that has Moon Pearl.
def fake_pearl_state(state, player):
Binary file not shown.
+24
View File
@@ -0,0 +1,24 @@
meta:
bps: on
user_notes: An attempt to replicate the Avianart Pots and Bones settings
settings:
1:
goal: triforcehunt
mode: standard
swords: assured
hints: on
shuffle: crossed
keysanity: on
pottery: lottery
dropshuffle: underworld
triforce_goal: 216
triforce_pool: 469
beemizer: 0
start_inventory:
1:
- Pegasus Boots
- Lamp
- Red Boomerang
+11 -6
View File
@@ -151,6 +151,7 @@
prize_shuffle:
none: 1
dungeon: 1
district: 1
wild: 1
dungeon_items:
standard: 10
@@ -160,18 +161,22 @@
mcu: 1 # map, compass, universal smalls
# for use when you aren't using the dungeon_items above
# map_shuffle:
# on: 1
# off: 1
# none: 1
# district: 1
# wild: 1
# compass_shuffle:
# on: 1
# off: 1
# none: 1
# district: 1
# wild: 1
# smallkey_shuffle:
# none: 5
# district: 1
# wild: 1
# universal: 1
# bigkey_shuffle:
# on: 1
# off: 1
# none: 1
# district: 1
# wild: 1
dungeon_counters:
on: 5
off: 0
+16 -6
View File
@@ -369,12 +369,18 @@
"type": "bool"
},
"mapshuffle": {
"action": "store_true",
"type": "bool"
"choices": [
"none",
"district",
"wild"
]
},
"compassshuffle": {
"action": "store_true",
"type": "bool"
"choices": [
"none",
"district",
"wild"
]
},
"keyshuffle": {
"choices": [
@@ -384,13 +390,17 @@
]
},
"bigkeyshuffle": {
"action": "store_true",
"type": "bool"
"choices": [
"none",
"district",
"wild"
]
},
"prizeshuffle": {
"choices": [
"none",
"dungeon",
"district",
"wild"
]
},
+11
View File
@@ -53,15 +53,26 @@
"randomizer.dungeon.keysanity": "Shuffle: ",
"randomizer.dungeon.mapshuffle": "Maps",
"randomizer.dungeon.mapshuffle.none": "In Dungeon",
"randomizer.dungeon.mapshuffle.district": "Nearby",
"randomizer.dungeon.mapshuffle.wild": "Randomized",
"randomizer.dungeon.compassshuffle": "Compasses",
"randomizer.dungeon.compassshuffle.none": "In Dungeon",
"randomizer.dungeon.compassshuffle.district": "Nearby",
"randomizer.dungeon.compassshuffle.wild": "Randomized",
"randomizer.dungeon.smallkeyshuffle": "Small Keys",
"randomizer.dungeon.smallkeyshuffle.none": "In Dungeon",
"randomizer.dungeon.smallkeyshuffle.district": "Nearby",
"randomizer.dungeon.smallkeyshuffle.wild": "Randomized",
"randomizer.dungeon.smallkeyshuffle.universal": "Universal",
"randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.bigkeyshuffle.none": "In Dungeon",
"randomizer.dungeon.bigkeyshuffle.district": "Nearby",
"randomizer.dungeon.bigkeyshuffle.wild": "Randomized",
"randomizer.dungeon.prizeshuffle": "Prizes",
"randomizer.dungeon.prizeshuffle.none": "On Boss",
"randomizer.dungeon.prizeshuffle.dungeon": "In Dungeon",
"randomizer.dungeon.prizeshuffle.district": "Nearby",
"randomizer.dungeon.prizeshuffle.wild": "Randomized",
"randomizer.dungeon.decoupledoors": "Decouple Doors",
"randomizer.dungeon.door_self_loops": "Allow Self-Looping Spiral Stairs",
@@ -1,9 +1,32 @@
{
"keysanity": {
"mapshuffle": {
"type": "selectbox",
"options": [
"none",
"district",
"wild"
],
"config": {
"padx": [20,0]
}
},
"compassshuffle": {
"type": "selectbox",
"options": [
"none",
"district",
"wild"
],
"config": {
"padx": [20,0]
}
},
"smallkeyshuffle": {
"type": "selectbox",
"options": [
"none",
"district",
"wild",
"universal"
],
@@ -11,8 +34,16 @@
"padx": [20,0]
}
},
"mapshuffle": { "type": "checkbox" },
"compassshuffle": { "type": "checkbox" },
"bigkeyshuffle": { "type": "checkbox" }
"bigkeyshuffle": {
"type": "selectbox",
"options": [
"none",
"district",
"wild"
],
"config": {
"padx": [20,0]
}
}
}
}
@@ -6,6 +6,7 @@
"options": [
"none",
"dungeon",
"district",
"wild"
],
"config": {
+8 -3
View File
@@ -65,6 +65,8 @@ class CustomSettings(object):
args.outputname = get_setting(meta['name'], args.outputname)
args.bps = get_setting(meta['bps'], args.bps)
args.suppress_rom = get_setting(meta['suppress_rom'], args.suppress_rom)
args.skip_playthrough = get_setting(meta['skip_playthrough'], args.skip_playthrough)
args.spoiler = get_setting(meta['spoiler'], args.spoiler)
args.names = get_setting(meta['names'], args.names)
args.race = get_setting(meta['race'], args.race)
args.notes = get_setting(meta['user_notes'], args.notes)
@@ -145,11 +147,14 @@ class CustomSettings(object):
args.compassshuffle[p] = get_setting(settings['compassshuffle'], args.compassshuffle[p])
if get_setting(settings['keysanity'], args.keysanity):
args.bigkeyshuffle[p] = True
if args.bigkeyshuffle[p] == 'none':
args.bigkeyshuffle[p] = 'wild'
if args.keyshuffle[p] == 'none':
args.keyshuffle[p] = 'wild'
args.mapshuffle[p] = True
args.compassshuffle[p] = True
if args.mapshuffle[p] == 'none':
args.mapshuffle[p] = 'wild'
if args.compassshuffle[p] == 'none':
args.compassshuffle[p] = 'wild'
args.shufflebosses[p] = get_setting(settings['boss_shuffle'], get_setting(settings['shufflebosses'], args.shufflebosses[p]))
args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], get_setting(settings['shuffleenemies'], args.shuffleenemies[p]))
+2 -2
View File
@@ -233,7 +233,7 @@ def modify_proposal(proposed_map, explored_state, doors_to_connect, hash_code_se
def explore_proposal(name, entrance_regions, all_regions, proposed_map, valid_doors, bk_special, world, player):
start = ExplorationState(dungeon=name)
bk_relevant = (world.door_type_mode[player] == 'original' and not world.bigkeyshuffle[player]) or bk_special
bk_relevant = (world.door_type_mode[player] == 'original' and world.bigkeyshuffle[player] == 'none') or bk_special
start.big_key_special = bk_special
original_state = extend_reachable_state_lenient(entrance_regions, start, proposed_map,
all_regions, valid_doors, bk_relevant, world, player)
@@ -302,7 +302,7 @@ def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_re
target_regions.add(region)
start = ExplorationState(dungeon=name)
bk_relevant = (world.door_type_mode[player] == 'original' and not world.bigkeyshuffle[player]) or bk_special
bk_relevant = (world.door_type_mode[player] == 'original' and world.bigkeyshuffle[player] == 'none') or bk_special
start.big_key_special = bk_special
original_state = extend_reachable_state_lenient(starting_regions, start, proposed_map, all_regions,
valid_doors, bk_relevant, world, player)
+4 -3
View File
@@ -408,8 +408,9 @@ def randomize_overworld_enemies(data_tables, custom_ow):
chosen = random.choices(candidate_sprites, weight, k=1)[0]
sprite.kind = chosen.sprite
# randomize the bush sprite per area
weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites]
bush_sprite_choice = random.choices(candidate_sprites, weight, k=1)[0]
bush_candidates = [x for x in candidate_sprites if x.bush_valid]
weight = [data_tables.ow_weights[r.sprite] for r in bush_candidates]
bush_sprite_choice = random.choices(bush_candidates, weight, k=1)[0]
data_tables.bush_sprite_table[area_id] = bush_sprite_choice
@@ -532,5 +533,5 @@ def write_enemy_shuffle_settings(world, player, rom):
for idx, pair in enumerate(tile_pattern):
rom.write_byte(snes_to_pc(0x09BA2A + idx), (pair[0] + 3) * 16)
rom.write_byte(snes_to_pc(0x09BA40 + idx), (pair[1] + 4) * 16)
if world.enemy_shuffle[player] == 'random':
if world.enemy_shuffle[player] == 'shuffled':
rom.write_byte(snes_to_pc(0x368100), 1) # randomize bushes
+8 -3
View File
@@ -21,6 +21,7 @@ class SpriteRequirement:
self.uw_valid = True
self.can_randomize = True
self.water_phobic = False
self.bush_valid = True
self.groups = []
self.sub_groups = defaultdict(list)
@@ -94,6 +95,10 @@ class SpriteRequirement:
self.uw_valid = False
return self
def no_bush(self):
self.bush_valid = False
return self
def good_for_uw_water(self):
return self.water_only and not self.static and not self.dont_use and self.uw_valid
@@ -237,7 +242,7 @@ def init_sprite_requirements():
SpriteRequirement(EnemySprite.ArmosKnight).exalt().sub_group(3, 0x1d),
SpriteRequirement(EnemySprite.Lanmolas).exalt().sub_group(3, 0x31),
SpriteRequirement(EnemySprite.FireballZora).immerse().no_drop().sub_group(2, [0xc, 0x18]), # .uw_skip() test
SpriteRequirement(EnemySprite.Zora).sub_group(2, 0xc).sub_group(3, 0x44), # .uw_skip() test
SpriteRequirement(EnemySprite.Zora).sub_group(2, 0xc).sub_group(3, 0x44).uw_skip(),
SpriteRequirement(EnemySprite.DesertStatue).affix().sub_group(2, 0x12),
SpriteRequirement(EnemySprite.Crab).sub_group(2, 0xc),
SpriteRequirement(EnemySprite.LostWoodsBird).affix().sub_group(2, 0x37).sub_group(3, 0x36),
@@ -366,8 +371,8 @@ def init_sprite_requirements():
SpriteRequirement(EnemySprite.MagicShopAssistant).affix().sub_group(0, 0x4b).sub_group(3, 0x5a),
SpriteRequirement(EnemySprite.SomariaPlatform).affix().sub_group(2, 0x27),
SpriteRequirement(EnemySprite.CastleMantle).affix().sub_group(0, 0x5d),
SpriteRequirement(EnemySprite.GreenMimic).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.RedMimic).sub_group(1, 0x2c),
SpriteRequirement(EnemySprite.GreenMimic).sub_group(1, 0x2c).no_bush(),
SpriteRequirement(EnemySprite.RedMimic).sub_group(1, 0x2c).no_bush(),
SpriteRequirement(EnemySprite.MedallionTablet).affix().sub_group(2, 0x12),
# overlord requirements - encapsulated mostly in the required sheets
+19 -10
View File
@@ -17,9 +17,9 @@ UwGeneralDeny:
- [ 0x000e, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Entrance - Freezor"
- [ 0x000e, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Top Bari"
- [ 0x000e, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Middle Bari"
- [ 0x0016, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit"] ] #"Swamp Palace - Pool - Zol 1"
- [ 0x0016, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Zol 2"
- [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Blue Bari"
- [ 0x0016, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit", "Deadrock"] ] #"Swamp Palace - Pool - Zol 1"
- [ 0x0016, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit", "Deadrock" ] ] #"Swamp Palace - Pool - Zol 2"
- [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit", "Deadrock" ] ] #"Swamp Palace - Pool - Blue Bari"
- [ 0x0016, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 3"
- [ 0x0017, 5, [ "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower of Hera - Bumper Room - Fire Bar (Clockwise)"
- [ 0x0019, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Dark Maze - Kodongo 1"
@@ -43,7 +43,7 @@ UwGeneralDeny:
- [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [ 0x0026, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 1"
- [ 0x0026, 8, [ "AntiFairyCircle", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 3"
- [ 0x0026, 9, [ "RollerHorizontalRight", "Statue" ] ] #"Swamp Palace - Big Spoon - Kyameron"
- [0x0026, 9, ["RollerHorizontalRight", "Statue", "BigSpike"]] #"Swamp Palace - Big Spoon - Kyameron"
- [ 0x0026, 10, [ "Statue" ] ] # multiple push statues in this room can cause issues
- [ 0x0026, 11, [ "Statue" ] ] # multiple push statues in this room can cause issues
- [ 0x0027, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "FirebarCW" ] ] #"Tower of Hera - Petting Zoo - Mini Moldorm 1"
@@ -84,6 +84,7 @@ UwGeneralDeny:
- [ 0x0039, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 1"
- [0x0039, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Bumper"]] #"Skull Woods - Play Pen - Hardhat Beetle"
- [ 0x0039, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 2"
- [0x003a, 1, ["RollerVerticalUp"]]
- [ 0x003b, 1, [ "Bumper" ]]
- [ 0x003b, 4, ["RollerVerticalUp", "RollerVerticalDown"]]
- [ 0x003c, 0, ["BigSpike"]]
@@ -92,7 +93,7 @@ UwGeneralDeny:
- [ 0x003d, 9, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Spark (Counterclockwise)"
- [ 0x003d, 10, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Spark (Clockwise) 1"
- [ 0x003d, 12, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Bunny Beam"
- [ 0x003d, 13, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Antifairy"
- [0x003d, 13, ["AntiFairyCircle", "Bumper", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Ganon's Tower - Torches 2 - Antifairy"
- [0x003e, 8, ["Wizzrobe"]]
- [0x003e, 9, ["Wizzrobe", "GreenMimic", "RedMimic", "RedEyegoreMimic", "GreenEyegoreMimic"]] # drops a key by default
- [0x003e, 10, ["Wizzrobe"]]
@@ -171,7 +172,7 @@ UwGeneralDeny:
- [0x0059, 5, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]]
- [ 0x0059, 9, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Gibdo 1"
- [ 0x005e, 3, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Pit Trap - Big Spike Trap"
- [ 0x005e, 4, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ice Palace - Pit Trap - Fire Bar (Clockwise)"
- [0x005e, 4, ["RollerVerticalUp", "RollerVerticalDown", "SpikeBlock"]] #"Ice Palace - Pit Trap - Fire Bar (Clockwise)"
- [ 0x005f, 0, [ "RollerVerticalDown", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari University - Blue Bari 1"
- [ 0x005f, 1, [ "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Ice Palace - Bari University - Blue Bari 2"
- [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Beamos", "SpikeBlock" ] ] #"Hyrule Castle - West - Blue Guard"
@@ -198,6 +199,7 @@ UwGeneralDeny:
- [ 0x006a, 5, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 4"
- [ 0x006b, 7, [ "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Mimics 1 - Spike Trap 1"
- [ 0x0071, 0, [ "RollerHorizontalLeft" ] ] #"Hyrule Castle - Basement Trap - Green Guard"
- [0x0072, 1, ["RollerHorizontalLeft", "RollerHorizontalRight"]]
- [ 0x0074, 0, [ "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - North Hallway - Red Devalant 1"
- [ 0x0074, 1, [ "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - North Hallway - Red Devalant 2"
- [ 0x0074, 4, [ "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - North Hallway - Leever 1"
@@ -275,6 +277,7 @@ UwGeneralDeny:
- [ 0x009c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 3"
- [ 0x009c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 4"
- [ 0x009c, 5, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 5"
- [0x009c, 6, ["AntiFairyCircle", "Bumper"]]
- [0x009d, 2, ["AntiFairyCircle"]]
- [ 0x009d, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Compass Room - Gibdo 2"
- [ 0x009d, 6, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - Compass Room - Blue Bari 1"
@@ -414,13 +417,17 @@ UwGeneralDeny:
- [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5"
- [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6"
- [0x00fd, 0, ["Bumper", "AntiFairyCircle"]]
- [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x010b, 6, ["RollerHorizontalRight"]]
- [0x010c, 4, ["AntiFairyCircle"]]
- [0x010c, 5, ["AntiFairyCircle"]]
- [0x010c, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]]
- [0x010c, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]]
- [0x011e, 0, ["RollerVerticalDown"]]
- [0x011e, 1, ["RollerVerticalDown"]]
- [0x011e, 2, ["RollerVerticalDown"]]
- [0x011e, 3, ["RollerVerticalDown"]]
OwGeneralDeny:
- [0x03, 2, ["Gibo"]] # OldMan eating Gibo
- [0x03, 4, ["Gibo"]] # OldMan eating Gibo
@@ -428,12 +435,14 @@ OwGeneralDeny:
- [0x03, 6, ["Gibo"]] # OldMan eating Gibo
- [0x03, 8, ["Gibo"]] # OldMan eating Gibo
- [0x03, 9, ["Gibo"]] # OldMan eating Gibo
- [0x03, 10, ["Gibo"]] # OldMan eating Gibo
- [0x03, 10, ["Gibo", "Beamos"]] # OldMan eating Gibo
- [0x05, 10, ["Bumper", "AntiFairyCircle"]] # Blocks path
- [0x05, 11, ["Bumper", "AntiFairyCircle"]] # Blocks path to portal
- [0x07, 3, ["Bumper", "AntiFairyCircle"]] # Blocks path to ladder
- [0x07, 3, ["Bumper", "AntiFairyCircle", "RollerHorizontalRight", "RollerHorizontalLeft"]] # Blocks path to ladder
- [0x07, 4, ["RollerHorizontalLeft"]] # Blocks path to ladder
- [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here
- [0x35, 8, ["RollerVerticalUp", "RollerVerticalDown"]] # blocks the dock
- [0x37, 5, ["RollerVerticalUp"]] # combines with a roller above to make the way impassable
- [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
- [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
- [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]]
+11 -3
View File
@@ -14,6 +14,8 @@ def dungeon_page(parent):
self.frames = {}
self.frames["keysanity"] = Frame(self)
self.frames["keysanity"].pack(anchor=W)
self.frames["keysanity2"] = Frame(self)
self.frames["keysanity2"].pack(anchor=W)
## Dungeon Item Shuffle
mscbLabel = Label(self.frames["keysanity"], text="Dungeon Items: ")
@@ -23,9 +25,15 @@ def dungeon_page(parent):
# Defns include frame name, widget type, widget options, widget placement attributes
# This first set goes in the Keysanity frame
with open(os.path.join("resources","app","gui","randomize","dungeon","keysanity.json")) as keysanityItems:
myDict = json.load(keysanityItems)
myDict = myDict["keysanity"]
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"])
myDictFile = json.load(keysanityItems)
myDict = myDictFile["keysanity"]
myDict1, myDict2 = dict(), dict()
count = 2
for key in myDict.keys():
(myDict1 if count > 0 else myDict2)[key] = myDict[key]
count -= 1
dictWidgets = {**widgets.make_widgets_from_dict(self, myDict1, self.frames["keysanity"]), \
**widgets.make_widgets_from_dict(self, myDict2, self.frames["keysanity2"])}
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
packAttrs = {"side":LEFT}
+6
View File
@@ -96,12 +96,15 @@ def resolve_districts(world):
for name, district in world.districts[player].items():
if district.dungeon:
dungeon = world.get_dungeon(district.dungeon, player)
dungeon.districts = [district] + dungeon.districts
layout = world.dungeon_layouts[player][district.dungeon]
district.locations.update([l.name for r in layout.master_sector.regions
for l in r.locations if not l.item and l.real])
else:
for region_name in district.regions:
region = world.get_region(region_name, player)
region.districts.append(district)
for location in region.locations:
if not location.item and location.real:
district.locations.add(location.name)
@@ -115,6 +118,7 @@ def resolve_districts(world):
RuntimeError(f'No region connected to entrance: {ent.name} Likely a missing entry in OWExitTypes')
visited.add(region)
if region.type == RegionType.Cave:
region.districts.append(district)
for location in region.locations:
if not location.item and location.real:
district.locations.add(location.name)
@@ -123,6 +127,8 @@ def resolve_districts(world):
queue.appendleft(ext.connected_region)
elif region.type == RegionType.Dungeon and region.dungeon:
district.dungeons.add(region.dungeon.name)
if district not in region.dungeon.districts:
region.dungeon.districts.append(district)
elif region.name in inaccessible:
district.access_points.add(region)
+12 -11
View File
@@ -134,7 +134,7 @@ def create_item_pool_config(world):
groups = LocationGroup('Major').locs(init_set)
if world.prizeshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Prizes'])
if world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Keys'])
if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Key Drops'])
@@ -144,9 +144,9 @@ def create_item_pool_config(world):
groups.locations.extend(mode_grouping['Key Drops'])
if world.pottery[player] not in ['none', 'cave']:
groups.locations.extend(mode_grouping['Pot Keys'])
if world.compassshuffle[player]:
if world.compassshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Compasses'])
if world.mapshuffle[player]:
if world.mapshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Maps'])
if world.shopsanity[player]:
groups.locations.append('Capacity Upgrade - Left')
@@ -259,12 +259,12 @@ def location_prefilled(location, world, player):
def previously_reserved(location, world, player):
if '- Boss' in location.name or '- Prize' in location.name:
if world.restrict_boss_items[player] == 'mapcompass' and (not world.compassshuffle[player]
or not world.mapshuffle[player]):
if world.restrict_boss_items[player] == 'mapcompass' and (world.compassshuffle[player] == 'none'
or world.mapshuffle[player] == 'none'):
return True
if world.restrict_boss_items[player] == 'dungeon' and (not world.compassshuffle[player]
or not world.mapshuffle[player]
or not world.bigkeyshuffle[player]
if world.restrict_boss_items[player] == 'dungeon' and (world.compassshuffle[player] == 'none'
or world.mapshuffle[player] == 'none'
or world.bigkeyshuffle[player] == 'none'
or world.keyshuffle[player] == 'none'
or world.prizeshuffle[player] in ['none', 'dungeon']):
return True
@@ -303,6 +303,7 @@ def massage_item_pool(world):
if item.prize:
dungeon = dungeon_pool[item.player].pop()
dungeon.prize = item
item.dungeon_object = dungeon
player_pool[item.player].append(item)
for dungeon in world.dungeons:
for item in dungeon.all_items:
@@ -381,13 +382,13 @@ def determine_major_items(world, player):
pass # now what?
if world.prizeshuffle[player] not in ['none', 'dungeon']:
major_item_set.update({x for x, y in item_table.items() if y[2] == 'Prize'})
if world.bigkeyshuffle[player]:
if world.bigkeyshuffle[player] != 'none':
major_item_set.update({x for x, y in item_table.items() if y[2] == 'BigKey'})
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]:
if world.compassshuffle[player] != 'none':
major_item_set.update({x for x, y in item_table.items() if y[2] == 'Compass'})
if world.mapshuffle[player]:
if world.mapshuffle[player] != 'none':
major_item_set.update({x for x, y in item_table.items() if y[2] == 'Map'})
if world.shopsanity[player]:
major_item_set.add('Bomb Upgrade (+5)')
+1 -1
View File
@@ -504,7 +504,7 @@ def remove_from_list(t_list, removals):
def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
holes_to_shuffle = [x for x in entrances if x in drop_map]
if not avail.world.shuffle_ganon:
if not avail.world.shuffle_ganon[avail.player]:
if avail.world.is_tile_swapped(0x1b, avail.player) and 'Inverted Pyramid Hole' in holes_to_shuffle:
connect_entrance('Inverted Pyramid Hole', 'Pyramid', avail)
connect_two_way('Pyramid Entrance', 'Pyramid Exit', avail)
+13 -4
View File
@@ -96,8 +96,14 @@ def roll_settings(weights):
dungeon_items = get_choice('dungeon_items')
dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items
dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items
ret.mapshuffle = get_choice_bool('map_shuffle') if 'map_shuffle' in weights else 'm' in dungeon_items
ret.compassshuffle = get_choice_bool('compass_shuffle') if 'compass_shuffle' in weights else 'c' in dungeon_items
if 'map_shuffle' in weights:
ret.mapshuffle = get_choice('map_shuffle')
elif 'm' in dungeon_items:
ret.mapshuffle = 'wild'
if 'compass_shuffle' in weights:
ret.compassshuffle = get_choice('compass_shuffle')
elif 'c' in dungeon_items:
ret.compassshuffle = 'wild'
if 'smallkey_shuffle' in weights:
ret.keyshuffle = get_choice('smallkey_shuffle')
else:
@@ -105,7 +111,10 @@ def roll_settings(weights):
ret.keyshuffle = 'wild'
if 'u' in dungeon_items:
ret.keyshuffle = 'universal'
ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items
if 'bigkey_shuffle' in weights:
ret.bigkeyshuffle = get_choice('bigkey_shuffle')
elif 'b' in dungeon_items:
ret.bigkeyshuffle = 'wild'
ret.prizeshuffle = get_choice('prize_shuffle')
ret.accessibility = get_choice('accessibility')
@@ -140,7 +149,7 @@ def roll_settings(weights):
ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default'
if ret.dungeon_counters == 'default':
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle != 'none' else 'off'
ret.pseudoboots = get_choice_bool('pseudoboots')
ret.shopsanity = get_choice_bool('shopsanity')