Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2024-08-29 03:16:17 -05:00
39 changed files with 729 additions and 380 deletions
+35 -34
View File
@@ -2,24 +2,17 @@
name: ⏱️Test/🔨Build/🚀Deploy name: ⏱️Test/🔨Build/🚀Deploy
# fire on # fire on
on: [ on:
push, push:
pull_request branches:
] # - DoorDevUnstable
# - DoorDev
# on: - OverworldShuffle
# push: pull_request:
# branches: branches:
# - DoorDevUnstable # - DoorDevUnstable
# - DoorDev # - DoorDev
# - OverworldShuffleDev - OverworldShuffle
# - OverworldShuffle
# pull_request:
# branches:
# - DoorDevUnstable
# - DoorDev
# - OverworldShuffleDev
# - OverworldShuffle
# stuff to do # stuff to do
jobs: jobs:
@@ -43,7 +36,6 @@ jobs:
os-name: [ os-name: [
# ubuntu-latest, # ubuntu-22.04 # ubuntu-latest, # ubuntu-22.04
ubuntu-22.04, ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12 macos-latest, # macos-12
windows-latest # windows-2022 windows-latest # windows-2022
] ]
@@ -102,7 +94,6 @@ jobs:
os-name: [ os-name: [
# ubuntu-latest, # ubuntu-22.04 # ubuntu-latest, # ubuntu-22.04
ubuntu-22.04, ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12 macos-latest, # macos-12
windows-latest # windows-2022 windows-latest # windows-2022
] ]
@@ -178,7 +169,6 @@ jobs:
os-name: [ os-name: [
# ubuntu-latest, # ubuntu-22.04 # ubuntu-latest, # ubuntu-22.04
ubuntu-22.04, ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12 macos-latest, # macos-12
windows-latest # windows-2022 windows-latest # windows-2022
] ]
@@ -220,7 +210,6 @@ jobs:
os-name: [ os-name: [
# ubuntu-latest, # ubuntu-22.04 # ubuntu-latest, # ubuntu-22.04
ubuntu-22.04, ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12 macos-latest, # macos-12
windows-latest # windows-2022 windows-latest # windows-2022
] ]
@@ -358,8 +347,10 @@ jobs:
echo "MacOS Asset: ${{ steps.identify-macos-asset.outputs.asset_macos }}" echo "MacOS Asset: ${{ steps.identify-macos-asset.outputs.asset_macos }}"
echo "Windows Asset: ${{ steps.identify-windows-asset.outputs.asset_windows }}" echo "Windows Asset: ${{ steps.identify-windows-asset.outputs.asset_windows }}"
# create a release (MASTER) # create a release (OverworldShuffle)
- name: 📀->🚀Create a Release (MASTER) # if: contains(github.ref, 'OverworldShuffle') # branch or tag name
# if: contains(github.event.head_commit.message, 'Version bump') # commit message
- name: 📀->🚀Create a Release (OverworldShuffle)
id: create_release id: create_release
uses: actions/create-release@v1.1.4 uses: actions/create-release@v1.1.4
env: env:
@@ -369,11 +360,11 @@ jobs:
release_name: ${{ steps.debug_info.outputs.release_name }} release_name: ${{ steps.debug_info.outputs.release_name }}
body_path: CHANGELOG.md body_path: CHANGELOG.md
# draft: true # draft: true
if: contains(github.ref, 'OverworldShuffle') && contains(github.event.head_commit.message, 'Version bump') # branch/tag name and commit message if: contains(github.event.head_commit.message, "Merge 'OverworldShuffleDev' into OverworldShuffle") # branch/tag name and commit message
# upload linux archive asset (MASTER) # upload linux archive asset (OverworldShuffle)
#TODO: Make sure we're firing on the proper branches #TODO: Make sure we're firing on the proper branches
- name: 🔼Upload Linux Archive Asset (MASTER) - name: 🔼Upload Linux Archive Asset (OverworldShuffle)
id: upload-linux-asset id: upload-linux-asset
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
@@ -383,11 +374,11 @@ jobs:
asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/linux/${{ steps.identify-linux-asset.outputs.asset_linux }} asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/linux/${{ steps.identify-linux-asset.outputs.asset_linux }}
asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-linux-focal.tar.gz asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-linux-focal.tar.gz
asset_content_type: application/gzip asset_content_type: application/gzip
if: contains(github.ref, 'OverworldShuffle') && contains(github.event.head_commit.message, 'Version bump') # branch/tag name and commit message if: contains(github.event.head_commit.message, "Merge 'OverworldShuffleDev' into OverworldShuffle") # branch/tag name and commit message
# upload macos archive asset (MASTER) # upload macos archive asset (OverworldShuffle)
#TODO: Make sure we're firing on the proper branches #TODO: Make sure we're firing on the proper branches
- name: 🔼Upload MacOS Archive Asset (MASTER) - name: 🔼Upload MacOS Archive Asset (OverworldShuffle)
id: upload-macos-asset id: upload-macos-asset
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
@@ -397,11 +388,11 @@ jobs:
asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/macos/${{ steps.identify-macos-asset.outputs.asset_macos }} asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/macos/${{ steps.identify-macos-asset.outputs.asset_macos }}
asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-osx.tar.gz asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-osx.tar.gz
asset_content_type: application/gzip asset_content_type: application/gzip
if: contains(github.ref, 'OverworldShuffle') && contains(github.event.head_commit.message, 'Version bump') # branch/tag name and commit message if: contains(github.event.head_commit.message, "Merge 'OverworldShuffleDev' into OverworldShuffle") # branch/tag name and commit message
# upload windows archive asset (MASTER) # upload windows archive asset (OverworldShuffle)
#TODO: Make sure we're firing on the proper branches #TODO: Make sure we're firing on the proper branches
- name: 🔼Upload Windows Archive Asset (MASTER) - name: 🔼Upload Windows Archive Asset (OverworldShuffle)
id: upload-windows-asset id: upload-windows-asset
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
@@ -411,4 +402,14 @@ jobs:
asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/windows/${{ steps.identify-windows-asset.outputs.asset_windows }} asset_path: ${{ steps.parentDir.outputs.parentDir }}/deploy/windows/${{ steps.identify-windows-asset.outputs.asset_windows }}
asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-windows.zip asset_name: ${{ steps.debug_info.outputs.asset_prefix }}-windows.zip
asset_content_type: application/zip asset_content_type: application/zip
if: contains(github.ref, 'OverworldShuffle') && contains(github.event.head_commit.message, 'Version bump') # branch/tag name and commit message if: contains(github.event.head_commit.message, "Merge 'OverworldShuffleDev' into OverworldShuffle") # branch/tag name and commit message
- name: 🖳Tag Baserom
uses: ./.github/actions/tag-repo
env:
FINE_PAT: ${{ secrets.ALTTPER_TAGGER }}
with:
repository: ${{ github.repository_owner }}/z3randomizer
ref-name: heads/OWMain
github-tag: ${{ github.event.release.tag_name }}
if: contains(github.event.head_commit.message, "Merge 'OverworldShuffleDev' into OverworldShuffle")
+21 -6
View File
@@ -642,7 +642,7 @@ class CollectionState(object):
queue = deque(old_state.blocked_connections[player].items()) queue = deque(old_state.blocked_connections[player].items())
old_state.traverse_world(queue, rrp, bc, player) old_state.traverse_world(queue, rrp, bc, player)
if old_state.world.key_logic_algorithm[player] == 'default': if old_state.world.key_logic_algorithm[player] == 'dangerous':
unresolved_events = [x for y in old_state.reachable_regions[player] for x in y.locations unresolved_events = [x for y in old_state.reachable_regions[player] for x in y.locations
if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement) if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement)
and x not in old_state.locations_checked and x.can_reach(old_state)] and x not in old_state.locations_checked and x.can_reach(old_state)]
@@ -670,7 +670,7 @@ class CollectionState(object):
queue = deque(self.blocked_connections[player].items()) queue = deque(self.blocked_connections[player].items())
self.traverse_world(queue, rrp, bc, player) self.traverse_world(queue, rrp, bc, player)
if self.world.key_logic_algorithm[player] == 'default': if self.world.key_logic_algorithm[player] == 'dangerous':
unresolved_events = [x for y in self.reachable_regions[player] for x in y.locations unresolved_events = [x for y in self.reachable_regions[player] for x in y.locations
if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement) if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement)
and x not in self.locations_checked and x.can_reach(self)] and x not in self.locations_checked and x.can_reach(self)]
@@ -2651,7 +2651,7 @@ class Location(object):
self.recursion_count = 0 self.recursion_count = 0
self.staleness_count = 0 self.staleness_count = 0
self.locked = False self.locked = False
self.real = not prize self.real = True
self.always_allow = lambda item, state: False self.always_allow = lambda item, state: False
self.access_rule = lambda state: True self.access_rule = lambda state: True
self.verbose_rule = None self.verbose_rule = None
@@ -2789,7 +2789,7 @@ class Item(object):
def explore_region(region): def explore_region(region):
explored_regions.append(region.name) explored_regions.append(region.name)
for ent in region.entrances: for ent in region.entrances:
if ent.parent_region is not None: if ent.parent_region is not None and ent.spot_type != 'OWG':
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]: if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
return ent return ent
elif ent.parent_region.name not in explored_regions: elif ent.parent_region.name not in explored_regions:
@@ -2989,6 +2989,8 @@ class Spoiler(object):
'shuffleganon': self.world.shuffle_ganon, 'shuffleganon': self.world.shuffle_ganon,
'shufflelinks': self.world.shufflelinks, 'shufflelinks': self.world.shufflelinks,
'shuffletavern': self.world.shuffletavern, 'shuffletavern': self.world.shuffletavern,
'skullwoods': self.world.skullwoods,
'linked_drops': self.world.linked_drops,
'take_any': self.world.take_any, 'take_any': self.world.take_any,
'overworld_map': self.world.overworld_map, 'overworld_map': self.world.overworld_map,
'door_shuffle': self.world.doorShuffle, 'door_shuffle': self.world.doorShuffle,
@@ -3242,6 +3244,9 @@ class Spoiler(object):
if self.metadata['shuffle'][player] != 'vanilla': if self.metadata['shuffle'][player] != 'vanilla':
outfile.write('Shuffle Link\'s House:'.ljust(line_width) + '%s\n' % yn(self.metadata['shufflelinks'][player])) outfile.write('Shuffle Link\'s House:'.ljust(line_width) + '%s\n' % yn(self.metadata['shufflelinks'][player]))
outfile.write('Shuffle Back of Tavern:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffletavern'][player])) outfile.write('Shuffle Back of Tavern:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffletavern'][player]))
outfile.write('Skull Woods Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['skullwoods'][player])
if self.metadata['linked_drops'] != "unset":
outfile.write('Linked Drops Override:'.ljust(line_width) + '%s\n' % self.metadata['linked_drops'][player])
outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffleganon'][player])) outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffleganon'][player]))
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player]) 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('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player])
@@ -3641,7 +3646,11 @@ keyshuffle_mode = {'none': 0, 'off': 0, 'wild': 1, 'on': 1, 'universal': 2}
# byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo) # byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo)
overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2} overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3} trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3}
key_logic_algo = {'default': 0, 'partial': 1, 'strict': 2} key_logic_algo = {'dangerous': 0, 'partial': 1, 'strict': 2}
# byte 15: SSDD ???? (skullwoods, linked_drops, 4 free bytes)
skullwoods_mode = {'original': 0, 'restricted': 1, 'loose': 2, 'followlinked': 3}
linked_drops_mode = {'unset': 0, 'linked': 1, 'independent': 2}
# sfx_shuffle and other adjust items does not affect settings code # sfx_shuffle and other adjust items does not affect settings code
@@ -3698,6 +3707,8 @@ class Settings(object):
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 ((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]), | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4),
]) ])
return base64.b64encode(code, "+-".encode()).decode() return base64.b64encode(code, "+-".encode()).decode()
@@ -3705,7 +3716,7 @@ class Settings(object):
def adjust_args_from_code(code, player, args): def adjust_args_from_code(code, player, args):
settings, p = base64.b64decode(code.encode(), "+-".encode()), player settings, p = base64.b64decode(code.encode(), "+-".encode()), player
if len(settings) < 14: if len(settings) < 15:
raise Exception('Provided code is incompatible with this version') raise Exception('Provided code is incompatible with this version')
if settings[10] != settings_version: if settings[10] != settings_version:
raise Exception('Provided code is incompatible with this version') raise Exception('Provided code is incompatible with this version')
@@ -3788,6 +3799,10 @@ class Settings(object):
args.trap_door_mode[p] = r(trap_door_mode)[(settings[14] & 0x18) >> 3] args.trap_door_mode[p] = r(trap_door_mode)[(settings[14] & 0x18) >> 3]
args.key_logic_algorithm[p] = r(key_logic_algo)[settings[14] & 0x07] args.key_logic_algorithm[p] = r(key_logic_algo)[settings[14] & 0x07]
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]
class KeyRuleType(FastEnum): class KeyRuleType(FastEnum):
WorstCase = 0 WorstCase = 0
+10
View File
@@ -1,5 +1,15 @@
# Changelog # Changelog
## 0.5.0.2
- \~Merged in DR v1.4.5~
- ER - SW Shuffle Options
- ER - Linked Drops Options
- Many GFX fixes
- Fixes to MSU/music
- No longer delete smith with prize shuffle
- Fixed screen scroll issue in Zora's Domain
- Fixed HUD when drawing A items on DR menu
## 0.5.0.1 ## 0.5.0.1
- Fixed HP refill interrupting boss cutscenes - Fixed HP refill interrupting boss cutscenes
- Fixed incorrect compass counts in Prize Shuffle - Fixed incorrect compass counts in Prize Shuffle
+3
View File
@@ -139,6 +139,7 @@ def parse_cli(argv, no_defaults=False):
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', 'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
'skullwoods', 'linked_drops',
'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor',
@@ -203,6 +204,8 @@ def parse_settings():
"shuffle": "vanilla", "shuffle": "vanilla",
"shufflelinks": False, "shufflelinks": False,
"shuffletavern": True, "shuffletavern": True,
'skullwoods': 'original',
'linked_drops': 'unset',
"overworld_map": "default", "overworld_map": "default",
"take_any": "none", "take_any": "none",
"pseudoboots": False, "pseudoboots": False,
+1 -1
View File
@@ -2088,7 +2088,7 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, all_
if actual_chest_keys == 0: if actual_chest_keys == 0:
dungeon.small_keys = [] dungeon.small_keys = []
else: else:
dungeon.small_keys = [ItemFactory(dungeon_keys[dungeon_name], player)] * actual_chest_keys dungeon.small_keys = [ItemFactory(dungeon_keys[dungeon_name], player) for _ in range(actual_chest_keys)]
for name, small_list in small_map.items(): for name, small_list in small_map.items():
used_doors.update(flatten_pair_list(small_list)) used_doors.update(flatten_pair_list(small_list))
+3 -5
View File
@@ -1850,10 +1850,8 @@ def ensure_crystal_switches_reachable(dungeon_map, crystal_switches, polarized_s
sector.equations = calc_sector_equations(sector) sector.equations = calc_sector_equations(sector)
if sector.is_entrance_sector() and not sector.destination_entrance: if sector.is_entrance_sector() and not sector.destination_entrance:
need_switch = True need_switch = True
for region in sector.get_start_regions(): if sector.c_switch: # this relies on the fact that Mire Fishbone SE cannot be a portal
if region.crystal_switch: need_switch = False
need_switch = False
break
any_benefit = False any_benefit = False
for eq in sector.equations: for eq in sector.equations:
if len(eq.benefit) > 0: if len(eq.benefit) > 0:
@@ -4035,7 +4033,7 @@ def calc_door_equation(door, sector, look_for_entrance, sewers_flag=None):
eq = DoorEquation(door) eq = DoorEquation(door)
eq.benefit[hook_from_door(door)].append(door) eq.benefit[hook_from_door(door)].append(door)
eq.required = True eq.required = True
eq.c_switch = door.crystal == CrystalBarrier.Either eq.c_switch = sector.c_switch # Big change - not true for mire fishbone, need to verify for others
# exceptions for long entrances ??? # exceptions for long entrances ???
# if door.name in ['PoD Dark Alley']: # if door.name in ['PoD Dark Alley']:
eq.entrance_flag = True eq.entrance_flag = True
+1 -1
View File
@@ -95,7 +95,7 @@ def fill_dungeons_restrictive(world, shuffled_locations):
continue continue
break break
else: else:
raise FillError(f'Unable to place dungeon prizes {", ".join(list(map(lambda d: d.hint_text, prize_locs)))}') raise FillError(f'Unable to place dungeon prizes: {", ".join(list(map(lambda d: d.name, prizes)))}')
random.shuffle(shuffled_locations) random.shuffle(shuffled_locations)
fill(all_state_base, others, shuffled_locations) fill(all_state_base, others, shuffled_locations)
+10 -2
View File
@@ -69,6 +69,7 @@ class InitialSram:
starting_arrow_cap_upgrades = 30 starting_arrow_cap_upgrades = 30
starting_bombs = 0 starting_bombs = 0
starting_arrows = 0 starting_arrows = 0
starting_magic = 0
startingstate = CollectionState(world) startingstate = CollectionState(world)
@@ -108,10 +109,10 @@ class InitialSram:
if startingstate.has('Magic Upgrade (1/4)', player): if startingstate.has('Magic Upgrade (1/4)', player):
equip[0x37B] = 2 equip[0x37B] = 2
equip[0x36E] = 0x80 starting_magic = 0x80
elif startingstate.has('Magic Upgrade (1/2)', player): elif startingstate.has('Magic Upgrade (1/2)', player):
equip[0x37B] = 1 equip[0x37B] = 1
equip[0x36E] = 0x80 starting_magic = 0x80
if startingstate.has('Return Old Man', player): if startingstate.has('Return Old Man', player):
self._initial_sram_bytes[0x410] |= 0x01 self._initial_sram_bytes[0x410] |= 0x01
@@ -176,6 +177,7 @@ class InitialSram:
arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10} arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10}
bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10} bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10}
arrows = {'Single Arrow': 1, 'Arrows (10)': 10} arrows = {'Single Arrow': 1, 'Arrows (10)': 10}
magic = {'Big Magic': 0x80, 'Small Magic': 0x10}
if item.name in set_table: if item.name in set_table:
equip[set_table[item.name][0]] = set_table[item.name][1] equip[set_table[item.name][0]] = set_table[item.name][1]
@@ -212,14 +214,20 @@ class InitialSram:
if item.name != 'Piece of Heart' or equip[0x36B] == 0: if item.name != 'Piece of Heart' or equip[0x36B] == 0:
equip[0x36C] = min(equip[0x36C] + 0x08, 0xA0) equip[0x36C] = min(equip[0x36C] + 0x08, 0xA0)
equip[0x36D] = min(equip[0x36D] + 0x08, 0xA0) equip[0x36D] = min(equip[0x36D] + 0x08, 0xA0)
elif item.name in magic:
starting_magic += magic[item.name]
else: else:
raise RuntimeError(f'Unsupported item in starting equipment: {item.name}') raise RuntimeError(f'Unsupported item in starting equipment: {item.name}')
equip[0x36E] = min(starting_magic, 0x80)
equip[0x370] = min(starting_bomb_cap_upgrades, 50) equip[0x370] = min(starting_bomb_cap_upgrades, 50)
equip[0x371] = min(starting_arrow_cap_upgrades, 70) equip[0x371] = min(starting_arrow_cap_upgrades, 70)
equip[0x343] = min(starting_bombs, equip[0x370]) equip[0x343] = min(starting_bombs, equip[0x370])
equip[0x377] = min(starting_arrows, equip[0x371]) equip[0x377] = min(starting_arrows, equip[0x371])
if not startingstate.has('Magic Mirror', player) and world.doorShuffle[player] != 'vanilla':
equip[0x353] = 1
# Assertion and copy equip to initial_sram_bytes # Assertion and copy equip to initial_sram_bytes
assert equip[:0x340] == [0] * 0x340 assert equip[:0x340] == [0] * 0x340
self._initial_sram_bytes[0x340:0x38F] = equip[0x340:0x38F] self._initial_sram_bytes[0x340:0x38F] = equip[0x340:0x38F]
+23 -1
View File
@@ -11,7 +11,7 @@ from Tables import bonk_prize_lookup
from Items import ItemFactory from Items import ItemFactory
from source.dungeon.EnemyList import add_drop_contents from source.dungeon.EnemyList import add_drop_contents
from source.overworld.EntranceShuffle2 import connect_entrance from source.overworld.EntranceShuffle2 import exit_ids, door_addresses
from source.item.FillUtil import trash_items, pot_items from source.item.FillUtil import trash_items, pot_items
import source.classes.constants as CONST import source.classes.constants as CONST
@@ -546,6 +546,27 @@ def set_up_take_anys(world, player, skip_adjustments=False):
world.initialize_regions() world.initialize_regions()
def connect_entrance(world, entrancename, exitname, player):
entrance = world.get_entrance(entrancename, player)
# check if we got an entrance or a region to connect to
try:
region = world.get_region(exitname, player)
exit = None
except RuntimeError:
exit = world.get_entrance(exitname, player)
region = exit.parent_region
# if this was already connected somewhere, remove the backreference
if entrance.connected_region is not None:
entrance.connected_region.entrances.remove(entrance)
target = exit_ids[exit.name][0] if exit is not None else exit_ids.get(region.name, None)
addresses = door_addresses[entrance.name][0]
entrance.connect(region, addresses, target)
world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player)
def create_dynamic_shop_locations(world, player): def create_dynamic_shop_locations(world, player):
for shop in world.shops[player]: for shop in world.shops[player]:
if shop.region.player == player: if shop.region.player == player:
@@ -1462,6 +1483,7 @@ def make_customizer_pool(world, player):
bow_found = next((i for i in pool if i in {'Bow', 'Progressive Bow'}), None) bow_found = next((i for i in pool if i in {'Bow', 'Progressive Bow'}), None)
if not bow_found: if not bow_found:
missing_items.append('Progressive Bow') missing_items.append('Progressive Bow')
missing_items = [i for i in missing_items if all(i != start.name or player != start.player for start in world.precollected_items)]
if missing_items: if missing_items:
logging.getLogger('').warning(f'The following items are not in the custom item pool {", ".join(missing_items)}') logging.getLogger('').warning(f'The following items are not in the custom item pool {", ".join(missing_items)}')
+140 -140
View File
@@ -20,151 +20,151 @@ def ItemFactory(items, player):
# Format: Name: (Advancement, Priority, Type, ItemCode, BasePrice, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text) # Format: Name: (Advancement, Priority, Type, ItemCode, BasePrice, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text)
item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class!', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'),
'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow': (True, False, None, 0x64, 150, 'Bow!\nJoin the archer class!', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'),
'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'Bow!\nJoin the archer class!', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'),
'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'),
'Hammer': (True, False, None, 0x09, 250, 'Stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'), 'Hammer': (True, False, None, 0x09, 250, 'Stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'),
'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), 'Hookshot': (True, False, None, 0x0A, 250, 'Hookshot!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'),
'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), 'Magic Mirror': (True, False, None, 0x1A, 250, 'Mirror!\nTake some time to\nreflect on this moment!', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'),
'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), 'Ocarina': (True, False, None, 0x14, 250, 'Ocarina!\nA Flute by another name', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'),
'Ocarina (Activated)': (True, False, None, 0x4A, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), 'Ocarina (Activated)': (True, False, None, 0x4A, 250, 'Ocarina!\nA Flute by another name', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'),
'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Pegasus Boots!\nGotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'),
'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'), 'Power Glove': (True, False, None, 0x1B, 100, 'Glove!\nNow you can\nlift weak stuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'),
'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the Cape'), 'Cape': (True, False, None, 0x19, 50, 'Cape!\nInvisbility cloak activate!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the Cape'),
'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'), 'Mushroom': (True, False, None, 0x29, 50, 'Mushroom!\nDon\'t eat it. Find a witch.', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'),
'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'), 'Shovel': (True, False, None, 0x13, 50, 'Shovel!\nCan you dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'),
'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'), 'Lamp': (True, False, None, 0x12, 150, 'Lamp!\nYou can see in the dark,\nand light torches.', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'),
'Magic Powder': (True, False, None, 0x0D, 50, 'You can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'), 'Magic Powder': (True, False, None, 0x0D, 50, 'Powder!\nSprinkle it on a dancing pickle!', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'),
'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'), 'Moon Pearl': (True, False, None, 0x1F, 200, 'Moon Pearl!\nRabbit Be Gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'),
'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the Red Cane'), 'Cane of Somaria': (True, False, None, 0x15, 250, 'Cane of Somaria!\nMake blocks, throw blocks', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the Red Cane'),
'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'), 'Fire Rod': (True, False, None, 0x07, 250, 'Fire Rod!\nI\'m burning for you!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'),
'Flippers': (True, False, None, 0x1E, 250, 'Fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'), 'Flippers': (True, False, None, 0x1E, 250, 'Flippers!\nFancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'),
'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'), 'Ice Rod': (True, False, None, 0x08, 250, 'Ice Rod!\nActivate Freeze Ray!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'),
'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'), 'Titans Mitts': (True, False, None, 0x1C, 200, 'Mitts!\nLift ALL the rocks!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'),
'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), 'Bombos': (True, False, None, 0x0F, 100, 'Bombos!\nExplosions! Fire!\nBurn it all!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'),
'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), 'Ether': (True, False, None, 0x10, 100, 'Ether!\nLet\'s cool things down!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'),
'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), 'Quake': (True, False, None, 0x11, 100, 'Quake!\nLet\'s shake the ground!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'),
'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a bottle'), 'Bottle': (True, False, None, 0x16, 50, 'Bottle!\nStore all manner of things!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a bottle'),
'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a bottle'), 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Red Potion!\nFill your hearts!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a bottle'),
'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a bottle'), 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Green Potion!\nRestore your magic!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a bottle'),
'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a bottle'), 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Blue Potion!\nHeal and restore!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a bottle'),
'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a bottle'), 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Fairy Bottle!\nI\'ll save your life!', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a bottle'),
'Bottle (Bee)': (True, False, None, 0x3C, 50, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bottle'), 'Bottle (Bee)': (True, False, None, 0x3C, 50, 'Bee Bottle!\nI\'ll sting your enemies a bit!', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bottle'),
'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a bottle'), 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'Good Bee Bottle!\nI\'ll sting your enemies\na whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a bottle'),
'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), 'Master Sword': (True, False, 'Sword', 0x50, 100, 'Master Sword!\nEvil\'s bane!', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'),
'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'Tempered Sword!\nMore slashy!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'),
'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'), 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'Fighter Sword!\nStarter level slashy!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'),
'Sword and Shield': (True, False, 'Sword', 0x00, 'An uncle\nsword rests\nhere!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'), 'Sword and Shield': (True, False, 'Sword', 0x00, 'Sword and Shield!\nUncle sword ahoy!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'),
'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'Golden Sword!\nBest slashy!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'),
'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'), 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'Sword!\nA better sword for your time!', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'),
'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'), 'Progressive Glove': (True, False, None, 0x61, 150, 'Glove!\nLift more than you can now!', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'),
'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Silver Arrows!\nDefeat Ganon faster!', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'),
'Green Pendant': (True, False, 'Prize', 0x37, 250, 'A pendant that\nsome old guy\nwill never see', 'and the green pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'), 'Green Pendant': (True, False, 'Prize', 0x37, 250, 'Green Pendant! Too\nbad Sahasrahla\nwill never see it!', 'and the green pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Blue Pendant': (True, False, 'Prize', 0x39, 150, 'A pendant that you\'ll never get', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'), 'Blue Pendant': (True, False, 'Prize', 0x39, 150, 'Blue Pendant!\nBut how?!', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Red Pendant': (True, False, 'Prize', 0x38, 150, 'A pendant that you\'ll never get', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'), 'Red Pendant': (True, False, 'Prize', 0x38, 150, 'Red Pendant!\nBut how?!', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Triforce': (True, False, None, 0x6A, 777, 'Triforce!\nGrab me to win!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'),
'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Power Star': (True, False, None, 0x6B, 100, 'Power Star!\nA small step\ntowards victory!', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'),
'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'Triforce Piece!\nA small step\ntowards victory!', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'),
'Crystal 1': (True, False, 'Prize', 0xB1, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 1': (True, False, 'Prize', 0xB1, 200, 'Crystal 1! It\'s very shiny!', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 2': (True, False, 'Prize', 0xB4, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 2': (True, False, 'Prize', 0xB4, 200, 'Crystal 2! It\'s very shiny!', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 3': (True, False, 'Prize', 0xB6, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 3': (True, False, 'Prize', 0xB6, 200, 'Crystal 3! It\'s very shiny!', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 4': (True, False, 'Prize', 0xB5, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 4': (True, False, 'Prize', 0xB5, 200, 'Crystal 4! It\'s very shiny!', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 5': (True, False, 'Prize', 0xB2, 250, 'A crystal that\nunlocks a bomb', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 5': (True, False, 'Prize', 0xB2, 250, 'Crystal 5! It unlocks a bomb!', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 6': (True, False, 'Prize', 0xB0, 250, 'A crystal that\nunlocks a bomb', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 6': (True, False, 'Prize', 0xB0, 250, 'Crystal 6! It unlocks a bomb!', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 7': (True, False, 'Prize', 0xB3, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'), 'Crystal 7': (True, False, 'Prize', 0xB3, 200, 'Crystal 7! It\'s very shiny!', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Single Arrow': (False, False, None, 0x43, 3, 'Single Arrow!\nDestiny awaits!', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), 'Arrows (10)': (False, False, None, 0x44, 30, '10 Arrows!\nAn archer\'s bailout!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'),
'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, '10 Arrow Upgrade!\nGain 10 more arrow capacity!', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, '5 Arrow Upgrade!\nGain 5 more arrow capacity!', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Single Bomb': (False, False, None, 0x27, 5, '1 Bomb!\nBoom boom pow!', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'),
'Arrows (5)': (False, False, None, 0xD5, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Arrows (5)': (False, False, None, 0xD5, 15, '5 Arrows!\nAn archer\'s bailout!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'),
'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), 'Small Magic': (False, False, None, 0x45, 5, 'Magic Decanter!\nJust a little one', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'),
'Big Magic': (False, False, None, 0xD4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), 'Big Magic': (False, False, None, 0xD4, 40, 'Magic Decanter!\nFull refill', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0xD3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Chicken': (False, False, None, 0xD3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (3)': (False, False, None, 0x28, 15, '3 Bombs!\nExplosions!\nIn a handy 3-pack', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, '10 Bombs!\nExplosions!\nIn a thrifty 10-pack', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, '10 Bomb Upgrade!\nGain 10 more bomb capacity!', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, '5 Bomb Upgrade!\nGain 5 more bomb capacity!', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'), 'Blue Mail': (False, True, None, 0x22, 50, 'Blue Mail!\nLess damage activate!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'),
'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'), 'Red Mail': (False, True, None, 0x23, 100, 'Red Mail!\nEven less damage!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'),
'Progressive Armor': (False, True, None, 0x60, 50, 'Time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), 'Progressive Armor': (False, True, None, 0x60, 50, 'Upgrade Mail!\nNot the kind you read!', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'),
'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the Blue Boomerang'), 'Blue Boomerang': (True, False, None, 0x0C, 50, 'Blue Boomerang!\nAlways comes back!', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the Blue Boomerang'),
'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the Red Boomerang'), 'Red Boomerang': (True, False, None, 0x2A, 50, 'Red Boomerang!\nAlways comes back!', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the Red Boomerang'),
'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a Blue Shield'), 'Blue Shield': (False, True, None, 0x04, 50, 'Blue Shield!\nBlock rocks!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a blue shield'),
'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'a Red Shield'), 'Red Shield': (False, True, None, 0x05, 500, 'Red Shield!\nBlock fireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the Red Shield'),
'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'), 'Mirror Shield': (True, False, None, 0x06, 200, 'Mirror Shield!\nBlock lasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'),
'Progressive Shield': (True, False, None, 0x5F, 50, 'Have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), 'Progressive Shield': (True, False, None, 0x5F, 50, 'Shield!\nA better shield for your time!', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'),
'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the Bug Net'), 'Bug Catching Net': (True, False, None, 0x21, 50, 'Bug Net!\nCatch all manner\nof things!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the Bug Net'),
'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the Blue Cane'), 'Cane of Byrna': (True, False, None, 0x18, 50, 'Cane of Byrna!\nSwirly protection!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the Blue Cane'),
'Boss Heart Container': (False, True, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Boss Heart Container': (False, True, None, 0x3E, 40, 'Heart Container!\nHealth Increased!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'),
'Sanctuary Heart Container': (False, True, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Sanctuary Heart Container': (False, True, None, 0x3F, 50, 'Heart Container!\nHealth Increased!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'),
'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), 'Piece of Heart': (False, False, None, 0x17, 10, 'Heart Piece!\nOne step closer\nto more health!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'),
'Rupee (1)': (False, False, None, 0x34, 0, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a green rupee'), 'Rupee (1)': (False, False, None, 0x34, 0, 'Rupees!\nJust pocket\nchange.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a green rupee'),
'Rupees (5)': (False, False, None, 0x35, 2, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a blue rupee'), 'Rupees (5)': (False, False, None, 0x35, 2, 'Rupees!\nJust pocket\nchange.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a blue rupee'),
'Rupees (20)': (False, False, None, 0x36, 10, 'Just couch\ncash. Move\nright along.', 'and the couch cash', 'the piggy-bank kid', 'life lesson for sale', 'the witch buying drugs', 'destitute boy has lunch again', 'a red rupee'), 'Rupees (20)': (False, False, None, 0x36, 10, 'Rupees!\nJust couch\ncash.', 'and the couch cash', 'the piggy-bank kid', 'life lesson for sale', 'the witch buying drugs', 'destitute boy has lunch again', 'a red rupee'),
'Rupees (50)': (False, False, None, 0x41, 25, 'A rupee pile!\nOkay?', 'and the rupee pile', 'the well-off kid', 'life lesson for sale', 'buying okay drugs', 'destitute boy has dinner again', 'fifty rupees'), 'Rupees (50)': (False, False, None, 0x41, 25, 'Rupees!\nA big pile!', 'and the rupee pile', 'the well-off kid', 'life lesson for sale', 'buying okay drugs', 'destitute boy has dinner again', 'fifty rupees'),
'Rupees (100)': (False, False, None, 0x40, 50, 'A rupee stash!\nHell yeah!', 'and the rupee stash', 'the kind-of-rich kid', 'life lesson for sale', 'buying good drugs', 'affluent boy goes drinking again', 'one hundred rupees'), 'Rupees (100)': (False, False, None, 0x40, 50, 'Rupees!\nA big stash!', 'and the rupee stash', 'the kind-of-rich kid', 'life lesson for sale', 'buying good drugs', 'affluent boy goes drinking again', 'one hundred rupees'),
'Rupees (300)': (False, False, None, 0x46, 150, 'A rupee hoard!\nHell yeah!', 'and the rupee hoard', 'the really-rich kid', 'life lesson for sale', 'buying the best drugs', 'fat-cat boy is rich again', 'three hundred rupees'), 'Rupees (300)': (False, False, None, 0x46, 150, 'Rupees!\nA big hoard!', 'and the rupee hoard', 'the really-rich kid', 'life lesson for sale', 'buying the best drugs', 'fat-cat boy is rich again', 'three hundred rupees'),
'Rupoor': (False, False, None, 0x59, 0, 'A debt collector', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'), 'Rupoor': (False, False, None, 0x59, 0, 'Rupoor!\nI\'ll take your rupees!', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'),
'Red Clock': (False, True, None, 0x5B, 0, 'A waste of time', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'), 'Red Clock': (False, True, None, 0x5B, 0, 'Red Clock!\nA waste of time.', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'),
'Blue Clock': (False, True, None, 0x5C, 50, 'A bit of time', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'), 'Blue Clock': (False, True, None, 0x5C, 50, 'Blue Clock!\nA bit of time!', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'),
'Green Clock': (False, True, None, 0x5D, 200, 'A lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), 'Green Clock': (False, True, None, 0x5D, 200, 'Green Clock!\nTons of time!', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'),
'Single RNG': (False, True, None, 0x62, 300, 'Something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), 'Single RNG': (False, True, None, 0x62, 300, 'Something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'),
'Multi RNG': (False, True, None, 0x63, 100, 'Something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), 'Multi RNG': (False, True, None, 0x63, 100, 'Something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'),
'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Half Magic!\nDouble your magic power!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half Magic'), # can be required to beat mothula in an open seed in very very rare circumstance
'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Quarter Magic!\nMagic is basically free now!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter Magic'), # can be required to beat mothula in an open seed in very very rare circumstance
'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Eastern Palace'), 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key for\nEastern Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'),
'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 60, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Eastern Palace'), 'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 60, 'A big key for\nEastern Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'),
'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'Now you can find the Armos Knights!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Eastern Palace'), 'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'A compass for\nEastern Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Eastern Palace'),
'Map (Eastern Palace)': (False, True, 'Map', 0x7D, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Eastern Palace'), 'Map (Eastern Palace)': (False, True, 'Map', 0x7D, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Eastern Palace'),
'Small Key (Desert Palace)': (False, False, 'SmallKey', 0xA3, 40, 'A small key to the desert', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Desert Palace'), 'Small Key (Desert Palace)': (False, False, 'SmallKey', 0xA3, 40, 'A small key for\nDesert Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Desert Palace'),
'Big Key (Desert Palace)': (False, False, 'BigKey', 0x9C, 60, 'A big key to the desert', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Desert Palace'), 'Big Key (Desert Palace)': (False, False, 'BigKey', 0x9C, 60, 'A big key for\nDesert Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Desert Palace'),
'Compass (Desert Palace)': (False, True, 'Compass', 0x8C, 10, 'Now you can find Lanmolas!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Desert Palace'), 'Compass (Desert Palace)': (False, True, 'Compass', 0x8C, 10, 'A compass for\nDesert Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Desert Palace'),
'Map (Desert Palace)': (False, True, 'Map', 0x7C, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Desert Palace'), 'Map (Desert Palace)': (False, True, 'Map', 0x7C, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Desert Palace'),
'Small Key (Tower of Hera)': (False, False, 'SmallKey', 0xAA, 40, 'A small key to Hera', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Tower of Hera'), 'Small Key (Tower of Hera)': (False, False, 'SmallKey', 0xAA, 40, 'A small key for\nTower of Hera', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Tower of Hera'),
'Big Key (Tower of Hera)': (False, False, 'BigKey', 0x95, 60, 'A big key to Hera', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Tower of Hera'), 'Big Key (Tower of Hera)': (False, False, 'BigKey', 0x95, 60, 'A big key for\nTower of Hera', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Tower of Hera'),
'Compass (Tower of Hera)': (False, True, 'Compass', 0x85, 10, 'Now you can find Moldorm!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Tower of Hera'), 'Compass (Tower of Hera)': (False, True, 'Compass', 0x85, 10, 'A compass for\nTower of Hera', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Tower of Hera'),
'Map (Tower of Hera)': (False, True, 'Map', 0x75, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Tower of Hera'), 'Map (Tower of Hera)': (False, True, 'Map', 0x75, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Tower of Hera'),
'Small Key (Escape)': (False, False, 'SmallKey', 0xA0, 40, 'A small key to the castle', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Hyrule Castle'), 'Small Key (Escape)': (False, False, 'SmallKey', 0xA0, 40, 'A small key for\nHyrule Castle', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Hyrule Castle'),
'Big Key (Escape)': (False, False, 'BigKey', 0x9F, 60, 'A big key to the castle', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Hyrule Castle'), 'Big Key (Escape)': (False, False, 'BigKey', 0x9F, 60, 'A big key for\nHyrule Castle', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Hyrule Castle'),
'Compass (Escape)': (False, True, 'Compass', 0x8F, 10, 'Now you can find no boss!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a Compass to Hyrule Castle'), 'Compass (Escape)': (False, True, 'Compass', 0x8F, 10, 'A compass for\nHyrule Castle', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Hyrule Castle'),
'Map (Escape)': (False, True, 'Map', 0x7F, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Hyrule Castle'), 'Map (Escape)': (False, True, 'Map', 0x7F, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Hyrule Castle'),
'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 40, 'A small key to Agahnim', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Castle Tower'), 'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 40, 'A small key for\nAgahnim\'s Tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Castle Tower'),
'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 60, 'A big key to Agahnim', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Castle Tower'), 'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 60, 'A big key for\nAgahnim\'s Tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'),
'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 10, 'Now you can find Aga1!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a Compass to Castle Tower'), 'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 10, 'A compass for\nAgahnim\'s Tower', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Castle Tower'),
'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Castle Tower'), 'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'),
'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 40, 'A small key to darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Palace of Darkness'), 'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 40, 'A small key for\nDark Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'),
'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 60, 'A big key to darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Palace of Darkness'), 'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 60, 'A big key for\nDark Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'),
'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 10, 'Now you can find Helmasaur King!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Palace of Darkness'), 'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 10, 'A compass for\nDark Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'),
'Map (Palace of Darkness)': (False, True, 'Map', 0x79, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Palace of Darkness'), 'Map (Palace of Darkness)': (False, True, 'Map', 0x79, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Palace of Darkness'),
'Small Key (Thieves Town)': (False, False, 'SmallKey', 0xAB, 40, 'A small key to thievery', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Thieves\' Town'), 'Small Key (Thieves Town)': (False, False, 'SmallKey', 0xAB, 40, 'A small key for\nThieves Town', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Thieves\' Town'),
'Big Key (Thieves Town)': (False, False, 'BigKey', 0x94, 60, 'A big key to thievery', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Thieves\' Town'), 'Big Key (Thieves Town)': (False, False, 'BigKey', 0x94, 60, 'A big key for\nThieves Town', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Thieves\' Town'),
'Compass (Thieves Town)': (False, True, 'Compass', 0x84, 10, 'Now you can find Blind!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Thieves\' Town'), 'Compass (Thieves Town)': (False, True, 'Compass', 0x84, 10, 'A compass for\nThieves Town', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Thieves\' Town'),
'Map (Thieves Town)': (False, True, 'Map', 0x74, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Thieves\' Town'), 'Map (Thieves Town)': (False, True, 'Map', 0x74, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Thieves\' Town'),
'Small Key (Skull Woods)': (False, False, 'SmallKey', 0xA8, 40, 'A small key to the woods', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Skull Woods'), 'Small Key (Skull Woods)': (False, False, 'SmallKey', 0xA8, 40, 'A small key for\nSkull Woods', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Skull Woods'),
'Big Key (Skull Woods)': (False, False, 'BigKey', 0x97, 60, 'A big key to the woods', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Skull Woods'), 'Big Key (Skull Woods)': (False, False, 'BigKey', 0x97, 60, 'A big key for\nSkull Woods', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Skull Woods'),
'Compass (Skull Woods)': (False, True, 'Compass', 0x87, 10, 'Now you can find Mothula!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Skull Woods'), 'Compass (Skull Woods)': (False, True, 'Compass', 0x87, 10, 'A compass for\nSkull Woods', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Skull Woods'),
'Map (Skull Woods)': (False, True, 'Map', 0x77, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Skull Woods'), 'Map (Skull Woods)': (False, True, 'Map', 0x77, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Skull Woods'),
'Small Key (Swamp Palace)': (False, False, 'SmallKey', 0xA5, 40, 'A small key to the swamp', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Swamp Palace'), 'Small Key (Swamp Palace)': (False, False, 'SmallKey', 0xA5, 40, 'A small key for\nSwamp Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Swamp Palace'),
'Big Key (Swamp Palace)': (False, False, 'BigKey', 0x9A, 60, 'A big key to the swamp', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Swamp Palace'), 'Big Key (Swamp Palace)': (False, False, 'BigKey', 0x9A, 60, 'A big key for\nSwamp Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Swamp Palace'),
'Compass (Swamp Palace)': (False, True, 'Compass', 0x8A, 10, 'Now you can find Arrghus!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Swamp Palace'), 'Compass (Swamp Palace)': (False, True, 'Compass', 0x8A, 10, 'A compass for\nSwamp Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Swamp Palace'),
'Map (Swamp Palace)': (False, True, 'Map', 0x7A, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Swamp Palace'), 'Map (Swamp Palace)': (False, True, 'Map', 0x7A, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Swamp Palace'),
'Small Key (Ice Palace)': (False, False, 'SmallKey', 0xA9, 40, 'A small key to the iceberg', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Ice Palace'), 'Small Key (Ice Palace)': (False, False, 'SmallKey', 0xA9, 40, 'A small key for\nIce Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ice Palace'),
'Big Key (Ice Palace)': (False, False, 'BigKey', 0x96, 60, 'A big key to the iceberg', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Ice Palace'), 'Big Key (Ice Palace)': (False, False, 'BigKey', 0x96, 60, 'A big key for\nIce Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ice Palace'),
'Compass (Ice Palace)': (False, True, 'Compass', 0x86, 10, 'Now you can find Kholdstare!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Ice Palace'), 'Compass (Ice Palace)': (False, True, 'Compass', 0x86, 10, 'A compass for\nIce Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Ice Palace'),
'Map (Ice Palace)': (False, True, 'Map', 0x76, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Ice Palace'), 'Map (Ice Palace)': (False, True, 'Map', 0x76, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ice Palace'),
'Small Key (Misery Mire)': (False, False, 'SmallKey', 0xA7, 40, 'A small key to the mire', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Misery Mire'), 'Small Key (Misery Mire)': (False, False, 'SmallKey', 0xA7, 40, 'A small key for\nMisery Mire', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Misery Mire'),
'Big Key (Misery Mire)': (False, False, 'BigKey', 0x98, 60, 'A big key to the mire', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Misery Mire'), 'Big Key (Misery Mire)': (False, False, 'BigKey', 0x98, 60, 'A big key for\nMisery Mire', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Misery Mire'),
'Compass (Misery Mire)': (False, True, 'Compass', 0x88, 10, 'Now you can find Vitreous!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Misery Mire'), 'Compass (Misery Mire)': (False, True, 'Compass', 0x88, 10, 'A compass for\nMisery Mire', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Misery Mire'),
'Map (Misery Mire)': (False, True, 'Map', 0x78, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Misery Mire'), 'Map (Misery Mire)': (False, True, 'Map', 0x78, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Misery Mire'),
'Small Key (Turtle Rock)': (False, False, 'SmallKey', 0xAC, 40, 'A small key to the pipe maze', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Turtle Rock'), 'Small Key (Turtle Rock)': (False, False, 'SmallKey', 0xAC, 40, 'A small key for\nTurtle Rock', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Turtle Rock'),
'Big Key (Turtle Rock)': (False, False, 'BigKey', 0x93, 60, 'A big key to the pipe maze', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Turtle Rock'), 'Big Key (Turtle Rock)': (False, False, 'BigKey', 0x93, 60, 'A big key for\nTurtle Rock', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Turtle Rock'),
'Compass (Turtle Rock)': (False, True, 'Compass', 0x83, 10, 'Now you can find Trinexx!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Turtle Rock'), 'Compass (Turtle Rock)': (False, True, 'Compass', 0x83, 10, 'A compass for\nTurtle Rock', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Turtle Rock'),
'Map (Turtle Rock)': (False, True, 'Map', 0x73, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Turtle Rock'), 'Map (Turtle Rock)': (False, True, 'Map', 0x73, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Turtle Rock'),
'Small Key (Ganons Tower)': (False, False, 'SmallKey', 0xAD, 40, 'A small key to the evil tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Ganon\'s Tower'), 'Small Key (Ganons Tower)': (False, False, 'SmallKey', 0xAD, 40, 'A small key for\nGanon\'s Tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ganon\'s Tower'),
'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 60, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a Big Key to Ganon\'s Tower'), 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 60, 'A big key for\nGanon\'s Tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ganon\'s Tower'),
'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 10, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a Compass to Ganon\'s Tower'), 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 10, 'A compass for\nGanon\'s Tower', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Ganon\'s Tower'),
'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Ganon\'s Tower'), 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a Map to Ganon\'s Tower'),
'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key'), 'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key'),
'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'), 'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'),
+3 -3
View File
@@ -2137,11 +2137,11 @@ def validate_key_placement(key_layout, world, player):
if key_layout.prize_relevant: if key_layout.prize_relevant:
found_prize = any(x for x in counter.important_locations if x.prize) found_prize = any(x for x in counter.important_locations if x.prize)
if not found_prize and dungeon_table[key_layout.sector.name].prize: if not found_prize and dungeon_table[key_layout.sector.name].prize:
prize_loc = dungeon_table[key_layout.sector.name].prize.location prize_dungeon = [d for d in world.dungeons if d.name == key_layout.sector.name][0]
if key_layout.prize_relevant == 'BigBomb': if key_layout.prize_relevant == 'BigBomb':
found_prize = prize_loc.item.name not in ['Crystal 5', 'Crystal 6'] found_prize = prize_dungeon.prize.name not in ['Crystal 5', 'Crystal 6']
elif key_layout.prize_relevant == 'GT': elif key_layout.prize_relevant == 'GT':
found_prize = 'Crystal' not in prize_loc.item.name or world.crystals_needed_for_gt[player] < 7 found_prize = 'Crystal' not in prize_dungeon.prize.name or world.crystals_needed_for_gt[player] < 7
else: else:
found_prize = False found_prize = False
can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \ can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \
+17 -4
View File
@@ -40,7 +40,7 @@ from source.enemizer.DamageTables import DamageTable
from source.enemizer.Enemizer import randomize_enemies from source.enemizer.Enemizer import randomize_enemies
from source.rom.DataTables import init_data_tables from source.rom.DataTables import init_data_tables
version_number = '1.4.1.12' version_number = '1.4.5'
version_branch = '-u' version_branch = '-u'
__version__ = f'{version_number}{version_branch}' __version__ = f'{version_number}{version_branch}'
@@ -161,7 +161,7 @@ def main(args, seed=None, fish=None):
if world.spoiler_mode != 'none' and not args.jsonout: if world.spoiler_mode != 'none' and not args.jsonout:
logger.info(world.fish.translate("cli", "cli", "create.meta")) logger.info(world.fish.translate("cli", "cli", "create.meta"))
world.spoiler.meta_to_file(output_path(f'{outfilebase}_Spoiler.txt')) world.spoiler.meta_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
if args.mystery and not (args.suppress_meta or args.create_spoiler): if args.mystery and not (args.suppress_meta or args.spoiler != 'none'):
world.spoiler.mystery_meta_to_file(output_path(f'{outfilebase}_meta.txt')) world.spoiler.mystery_meta_to_file(output_path(f'{outfilebase}_meta.txt'))
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
@@ -362,7 +362,7 @@ def main(args, seed=None, fish=None):
with open(output_path('%s_multidata' % outfilebase), 'wb') as f: with open(output_path('%s_multidata' % outfilebase), 'wb') as f:
f.write(multidata) f.write(multidata)
if args.mystery and not (args.suppress_meta or args.create_spoiler): if args.mystery and not (args.suppress_meta or args.spoiler not in ['full']):
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_meta.txt')) world.spoiler.hashes_to_file(output_path(f'{outfilebase}_meta.txt'))
elif world.spoiler_mode != 'none' and not args.jsonout: elif world.spoiler_mode != 'none' and not args.jsonout:
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_Spoiler.txt')) world.spoiler.hashes_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
@@ -495,6 +495,8 @@ def init_world(args, fish):
world.standardize_palettes = args.standardize_palettes.copy() world.standardize_palettes = args.standardize_palettes.copy()
world.shufflelinks = args.shufflelinks.copy() world.shufflelinks = args.shufflelinks.copy()
world.shuffletavern = args.shuffletavern.copy() world.shuffletavern = args.shuffletavern.copy()
world.skullwoods = args.skullwoods.copy()
world.linked_drops = args.linked_drops.copy()
world.pseudoboots = args.pseudoboots.copy() world.pseudoboots = args.pseudoboots.copy()
world.overworld_map = args.overworld_map.copy() world.overworld_map = args.overworld_map.copy()
world.take_any = args.take_any.copy() world.take_any = args.take_any.copy()
@@ -522,7 +524,18 @@ def set_starting_inventory(world, args):
if inv_list: if inv_list:
for inv_item in inv_list: for inv_item in inv_list:
name = inv_item.strip() name = inv_item.strip()
name = name if name != 'Ocarina' or world.flute_mode[player] != 'active' else 'Ocarina (Activated)' if inv_item == 'RandomWeapon':
name = random.choice(['Progressive Bow', 'Hammer', 'Progressive Sword', 'Cane of Somaria', 'Cane of Byrna', 'Fire Rod'])
extra = []
if name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']:
extra.append('Big Magic')
if name == 'Progressive Bow':
extra.extend(['Arrows (10)'] * 3)
for e in extra:
item = ItemFactory(e, p)
if item:
world.push_precollected(item)
name = name if name != 'Ocarina' or world.flute_mode[p] != 'active' else 'Ocarina (Activated)'
item = ItemFactory(name, p) item = ItemFactory(name, p)
if item: if item:
world.push_precollected(item) world.push_precollected(item)
+2 -2
View File
@@ -344,7 +344,7 @@ location_table_boss = {'Eastern Palace - Prize': 0x2000,
'Desert Palace - Prize': 0x1000, 'Desert Palace - Prize': 0x1000,
'Tower of Hera - Prize': 0x0020, 'Tower of Hera - Prize': 0x0020,
'Palace of Darkness - Prize': 0x0200, 'Palace of Darkness - Prize': 0x0200,
'Thieves Town - Prize': 0x0010, 'Thieves\' Town - Prize': 0x0010,
'Skull Woods - Prize': 0x0080, 'Skull Woods - Prize': 0x0080,
'Swamp Palace - Prize': 0x0400, 'Swamp Palace - Prize': 0x0400,
'Ice Palace - Prize': 0x0040, 'Ice Palace - Prize': 0x0040,
@@ -1097,7 +1097,7 @@ async def game_watcher(ctx : Context):
async def main(): async def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--snes', default='localhost:8080', help='Address of the QUsb2snes server.') parser.add_argument('--snes', default='localhost:23074', help='Address of the QUsb2snes/SNI server.')
parser.add_argument('--connect', default=None, help='Address of the multiworld host.') parser.add_argument('--connect', default=None, help='Address of the multiworld host.')
parser.add_argument('--password', default=None, help='Password of the multiworld host.') parser.add_argument('--password', default=None, help='Password of the multiworld host.')
parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical'])
+3 -3
View File
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
from OverworldGlitchRules import create_owg_connections from OverworldGlitchRules import create_owg_connections
from Utils import bidict from Utils import bidict
version_number = '0.5.0.1' version_number = '0.5.0.2'
# branch indicator is intentionally different across branches # branch indicator is intentionally different across branches
version_branch = '' version_branch = ''
@@ -1151,7 +1151,7 @@ def define_tile_groups(world, do_grouped, player):
merge_groups([[0x13, 0x14, 0x1b]]) merge_groups([[0x13, 0x14, 0x1b]])
# sanctuary and grave connector # sanctuary and grave connector
if world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite']: if world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite', 'district']:
merge_groups([[0x13, 0x14]]) merge_groups([[0x13, 0x14]])
# cross-screen connector # cross-screen connector
@@ -1499,7 +1499,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F
elif exit.connected_region.name not in explored_regions \ elif exit.connected_region.name not in explored_regions \
and (exit.connected_region.type == region.type and (exit.connected_region.type == region.type
or exit.name in OWExitTypes['OWEdge'] or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \ or exit.name in OWExitTypes['OWEdge'] or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \
and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.name not in OWExitTypes['Ledge', 'OWG']): and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.name not in (OWExitTypes['Ledge'] + OWExitTypes['OWG'])):
explore_region(exit.connected_region.name, exit.connected_region) explore_region(exit.connected_region.name, exit.connected_region)
if build_copy_world: if build_copy_world:
+3 -3
View File
@@ -87,7 +87,7 @@ def main(args):
outfilebase = 'Plando_%s_%s' % (os.path.splitext(os.path.basename(args.plando))[0], world.seed) outfilebase = 'Plando_%s_%s' % (os.path.splitext(os.path.basename(args.plando))[0], world.seed)
rom.write_to_file('%s.sfc' % outfilebase) rom.write_to_file('%s.sfc' % outfilebase)
if args.create_spoiler: if args.spoiler != 'none':
world.spoiler.to_file('%s_Spoiler.txt' % outfilebase) world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
logger.info('Done. Enjoy.') logger.info('Done. Enjoy.')
@@ -178,7 +178,7 @@ def prefill_world(world, plando, text_patches):
world.fix_trock_exit = {1: trfstr.strip().lower() == 'true'} world.fix_trock_exit = {1: trfstr.strip().lower() == 'true'}
elif line.startswith('!fix_gtower_exit'): elif line.startswith('!fix_gtower_exit'):
_, gtfstr = line.split(':', 1) _, gtfstr = line.split(':', 1)
world.fix_gtower_exit = gtfstr.strip().lower() == 'true' world.fix_gtower_exit = {1: gtfstr.strip().lower() == 'true'}
elif line.startswith('!fix_pod_exit'): elif line.startswith('!fix_pod_exit'):
_, podestr = line.split(':', 1) _, podestr = line.split(':', 1)
world.fix_palaceofdarkness_exit = {1: podestr.strip().lower() == 'true'} world.fix_palaceofdarkness_exit = {1: podestr.strip().lower() == 'true'}
@@ -223,7 +223,7 @@ def prefill_world(world, plando, text_patches):
def start(): def start():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true') parser.add_argument('--spoiler', default='none', help='Output a Spoiler File')
parser.add_argument('--ignore_unsolvable', help='Do not abort if seed is deemed unsolvable.', action='store_true') parser.add_argument('--ignore_unsolvable', help='Do not abort if seed is deemed unsolvable.', action='store_true')
parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.') parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.')
parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.') parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
+6 -5
View File
@@ -1240,11 +1240,12 @@ def adjust_locations(world, player):
index += 1 index += 1
setup_enemy_locations(world, player) setup_enemy_locations(world, player)
# disable forced prize locations # disable forced prize locations
if world.prizeshuffle[player] != 'none': prize_on_boss = world.prizeshuffle[player] == 'none'
for l in [name for name, data in location_table.items() if data[2]]: for l in [name for name, data in location_table.items() if data[2]]:
location = world.get_location_unsafe(l, player) location = world.get_location_unsafe(l, player)
if location: if location:
location.prize = False location.prize = prize_on_boss
location.real = not prize_on_boss
# unreal events: # unreal events:
for l in ['Ganon', 'Zelda Pickup', 'Zelda Drop Off'] + list(location_events): for l in ['Ganon', 'Zelda Pickup', 'Zelda Drop Off'] + list(location_events):
location = world.get_location_unsafe(l, player) location = world.get_location_unsafe(l, player)
+10 -3
View File
@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '87be9d9bd56b6ad8e4b9697ecfc31841' RANDOMIZERBASEHASH = 'c0c4b8166fbe9b637a0aaa82e3f4cb8d'
class JsonRom(object): class JsonRom(object):
@@ -492,6 +492,9 @@ def patch_rom(world, rom, player, team, is_mystery=False):
if world.mapshuffle[player]: if world.mapshuffle[player]:
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
if world.doorShuffle[player] != 'vanilla':
rom.write_bytes(snes_to_pc(0x0293FE), [0x80, 0x15]) # skip pre-Aga music change
# fix for swamp drains if necessary # fix for swamp drains if necessary
swamp1location = world.get_location('Swamp Palace - Trench 1 Pot Key', player) swamp1location = world.get_location('Swamp Palace - Trench 1 Pot Key', player)
if not swamp1location.pot.indicator: if not swamp1location.pot.indicator:
@@ -664,7 +667,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
write_int16(rom, 0x15DB5 + 2 * offset, 0x0640) write_int16(rom, 0x15DB5 + 2 * offset, 0x0640)
elif room_id == 0x00d6 and world.fix_trock_exit[player]: elif room_id == 0x00d6 and world.fix_trock_exit[player]:
write_int16(rom, 0x15DB5 + 2 * offset, 0x0134) write_int16(rom, 0x15DB5 + 2 * offset, 0x0134)
elif room_id == 0x000c and world.fix_gtower_exit: # fix ganons tower exit point elif room_id == 0x000c and world.fix_gtower_exit[player]: # fix ganons tower exit point
write_int16(rom, 0x15DB5 + 2 * offset, 0x00A4) write_int16(rom, 0x15DB5 + 2 * offset, 0x00A4)
else: else:
write_int16(rom, 0x15DB5 + 2 * offset, link_y) write_int16(rom, 0x15DB5 + 2 * offset, link_y)
@@ -889,7 +892,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
rom.write_byte(cr_pc+0x1e, 0xEE) # slash rom.write_byte(cr_pc+0x1e, 0xEE) # slash
rom.write_byte(cr_pc+0x1f, thousands_bot) rom.write_byte(cr_pc+0x1f, thousands_bot)
# modify stat config # modify stat config
stat_address = 0x23978C stat_address = 0x23983E
stat_pc = snes_to_pc(stat_address) stat_pc = snes_to_pc(stat_address)
rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1) rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1)
rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0) rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0)
@@ -2704,6 +2707,8 @@ def patch_shuffled_dark_sanc(world, rom, player):
dark_sanc = world.get_region('Dark Sanctuary Hint', player) dark_sanc = world.get_region('Dark Sanctuary Hint', player)
dark_sanc_entrance = str([i for i in dark_sanc.entrances if i.parent_region.name != 'Menu'][0].name) dark_sanc_entrance = str([i for i in dark_sanc.entrances if i.parent_region.name != 'Menu'][0].name)
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1] room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1]
if dark_sanc_entrance == 'Tavern North':
link_y -= 0x10 # rom code assumes south-facing doors and adds $10 to the y-coordinate
door_index = door_addresses[str(dark_sanc_entrance)][0] door_index = door_addresses[str(dark_sanc_entrance)][0]
rom.initial_sram.pre_set_overworld_flag(ow_area, door_addresses[dark_sanc_entrance][2]) rom.initial_sram.pre_set_overworld_flag(ow_area, door_addresses[dark_sanc_entrance][2])
@@ -2719,6 +2724,8 @@ def patch_shuffled_bomb_shop(world, rom, player):
bomb_shop = world.get_region('Big Bomb Shop', player) bomb_shop = world.get_region('Big Bomb Shop', player)
bomb_shop_entrance = str([i for i in bomb_shop.entrances if i.parent_region.name != 'Menu'][0].name) bomb_shop_entrance = str([i for i in bomb_shop.entrances if i.parent_region.name != 'Menu'][0].name)
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[bomb_shop_entrance][1] room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[bomb_shop_entrance][1]
if bomb_shop_entrance == 'Tavern North':
link_y -= 0x10 # rom code assumes south-facing doors and adds $10 to the y-coordinate
door_index = door_addresses[str(bomb_shop_entrance)][0] door_index = door_addresses[str(bomb_shop_entrance)][0]
rom.initial_sram.pre_set_overworld_flag(ow_area, door_addresses[bomb_shop_entrance][2]) rom.initial_sram.pre_set_overworld_flag(ow_area, door_addresses[bomb_shop_entrance][2])
+11 -10
View File
@@ -557,11 +557,12 @@ def global_rules(world, player):
set_rule(world.get_entrance('Mire Post-Gap Gap', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Post-Gap Gap', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player))
set_rule(world.get_entrance('Mire Falling Bridge Hook Path', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Falling Bridge Hook Path', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player))
set_rule(world.get_entrance('Mire Falling Bridge Hook Only Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Falling Bridge Hook Only Path', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Mire 2 NE', player), lambda state: state.has_sword(player) or # Note: new enemy logic doesn't account for Fire Rod + Bombs or Ice Rod + Bombs yet
(state.has('Fire Rod', player) and (state.can_use_bombs(player) or state.can_extend_magic(player, 9))) or # 9 fr shots or 8 with some bombs # set_rule(world.get_entrance('Mire 2 NE', player), lambda state: state.has_sword(player) or
(state.has('Ice Rod', player) and state.can_use_bombs(player)) or # freeze popo and throw, bomb to finish # (state.has('Fire Rod', player) and (state.can_use_bombs(player) or state.can_extend_magic(player, 9))) or # 9 fr shots or 8 with some bombs
state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player)) # need to defeat wizzrobes, bombs don't work ... # (state.has('Ice Rod', player) and state.can_use_bombs(player)) or # freeze popo and throw, bomb to finish
# byrna could work with sufficient magic # state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player)) # need to defeat wizzrobes, bombs don't work ...
# byrna could work with sufficient magic
set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player))
loc = world.get_location('Misery Mire - Spikes Pot Key', player) loc = world.get_location('Misery Mire - Spikes Pot Key', player)
if loc.pot: if loc.pot:
@@ -641,10 +642,6 @@ def global_rules(world, player):
set_rule(world.get_entrance('GT Ice Armos NE', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state)) set_rule(world.get_entrance('GT Ice Armos NE', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
set_rule(world.get_entrance('GT Ice Armos WS', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state)) set_rule(world.get_entrance('GT Ice Armos WS', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
set_rule(world.get_entrance('GT Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('GT Mimics 1 ES', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('GT Mimics 2 WS', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('GT Mimics 2 NE', player), lambda state: state.can_shoot_arrows(player))
# consider access to refill room - interior doors would need a change # consider access to refill room - interior doors would need a change
set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(player)) set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
@@ -1531,6 +1528,10 @@ std_kill_rooms = {
(['GT Petting Zoo SE'], [], 0x7d, [4, 5, 6, 7, 8, 10]), (['GT Petting Zoo SE'], [], 0x7d, [4, 5, 6, 7, 8, 10]),
'GT DMs Room': # Four red stalfos 'GT DMs Room': # Four red stalfos
(['GT DMs Room SW'], [], 0x7b, [2, 3, 4, 5, 8, 9, 10]), (['GT DMs Room SW'], [], 0x7b, [2, 3, 4, 5, 8, 9, 10]),
'GT Mimics 1': # two red mimics
(['GT Mimics 1 NW', 'GT Mimics 1 ES'], [], 0x006b, [5, 6, 7, 8, 9]),
'GT Mimics 2': # two red mimics
(['GT Mimics 2 NE', 'GT Mimics 2 WS'], [], 0x006b, [10, 11, 12, 13]),
'GT Gauntlet 1': # Stalfos/zazaks 'GT Gauntlet 1': # Stalfos/zazaks
(['GT Gauntlet 1 WN'], [], 0x5d, [3, 4, 5, 6]), (['GT Gauntlet 1 WN'], [], 0x5d, [3, 4, 5, 6]),
'GT Gauntlet 2': # Red stalfos 'GT Gauntlet 2': # Red stalfos
@@ -2017,7 +2018,7 @@ def add_key_logic_rules(world, player):
eval_func = eval_small_key_door 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] == 'wild':
eval_func = eval_small_key_door_strict eval_func = eval_small_key_door_strict
elif world.key_logic_algorithm[player] != 'default': elif world.key_logic_algorithm[player] != 'dangerous':
eval_func = eval_small_key_door_partial eval_func = eval_small_key_door_partial
for d_name, d_logic in key_logic.items(): for d_name, d_logic in key_logic.items():
for door_name, rule in d_logic.door_rules.items(): for door_name, rule in d_logic.door_rules.items():
+1 -1
View File
@@ -29,7 +29,7 @@ def main(args=None):
['Std ', ' --mode standard'], ['Std ', ' --mode standard'],
['Inv ', ' --mode inverted']]: ['Inv ', ' --mode inverted']]:
basecommand = f"python3.8 DungeonRandomizer.py --door_shuffle {args.dr} --intensity {args.tense} --suppress_rom --suppress_spoiler" basecommand = f"python DungeonRandomizer.py --door_shuffle {args.dr} --intensity {args.tense} --suppress_rom --spoiler none"
def gen_seed(): def gen_seed():
taskcommand = basecommand + " " + command + mode[1] taskcommand = basecommand + " " + command + mode[1]
+1 -1
View File
@@ -25,7 +25,7 @@ def main(args=None):
alive = 0 alive = 0
basecommand = f'py Mystery.py --suppress_rom --suppress_meta --create_spoiler --outputpath L:/_Work/Zelda/ROMs/Bug/Automate/{datetime.now().strftime("%y%m%d")} --weights L:/_Work/Zelda/ROMs/Bug/Automate/_test.yml' basecommand = f'py Mystery.py --suppress_rom --suppress_meta --spoiler full --outputpath L:/_Work/Zelda/ROMs/Bug/Automate/{datetime.now().strftime("%y%m%d")} --weights L:/_Work/Zelda/ROMs/Bug/Automate/_test.yml'
def gen_seed(): def gen_seed():
return subprocess.run(basecommand, capture_output=True, shell=True, text=True) return subprocess.run(basecommand, capture_output=True, shell=True, text=True)
+30 -30
View File
@@ -1668,50 +1668,50 @@ class TextTable(object):
text['potion_shop_no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.") text['potion_shop_no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.")
text['item_get_lamp'] = CompressedTextMapper.convert("Lamp! You can see in the dark, and light torches.") text['item_get_lamp'] = CompressedTextMapper.convert("Lamp! You can see in the dark, and light torches.")
text['item_get_boomerang'] = CompressedTextMapper.convert("Boomerang! Press START to select it.") text['item_get_boomerang'] = CompressedTextMapper.convert("Boomerang! Press START to select it.")
text['item_get_bow'] = CompressedTextMapper.convert("You're in bow mode now!") text['item_get_bow'] = CompressedTextMapper.convert("Bow! Join the archer class!")
text['item_get_shovel'] = CompressedTextMapper.convert("This is my new mop. My friend George, he gave me this mop. It's a pretty good mop. It's not as good as my old mop. I miss my old mop. But it's still a good mop.") text['item_get_shovel'] = CompressedTextMapper.convert("Shovel! Can you dig it?")
text['item_get_magic_cape'] = CompressedTextMapper.convert("Finally! we get to play Invisble Man!") text['item_get_magic_cape'] = CompressedTextMapper.convert("Cape! Invisbility cloak activate!")
text['item_get_powder'] = CompressedTextMapper.convert("It's the powder. Let's cause some mischief!") text['item_get_powder'] = CompressedTextMapper.convert("Powder! Sprinkle it on a dancing pickle!")
text['item_get_flippers'] = CompressedTextMapper.convert("Splish! Splash! Let's go take a bath!") text['item_get_flippers'] = CompressedTextMapper.convert("Flippers! Time to swim!")
text['item_get_power_gloves'] = CompressedTextMapper.convert("Feel the power! You can now lift light rocks! Rock on!") text['item_get_power_gloves'] = CompressedTextMapper.convert("Gloves! Lift up those rocks!")
text['item_get_pendant_courage'] = CompressedTextMapper.convert("We have the Pendant of Courage! How brave!") text['item_get_pendant_courage'] = CompressedTextMapper.convert("We have the Pendant of Courage! How brave!")
text['item_get_pendant_power'] = CompressedTextMapper.convert("We have the Pendant of Power! How robust!") text['item_get_pendant_power'] = CompressedTextMapper.convert("We have the Pendant of Power! How robust!")
text['item_get_pendant_wisdom'] = CompressedTextMapper.convert("We have the Pendant of Wisdom! How astute!") text['item_get_pendant_wisdom'] = CompressedTextMapper.convert("We have the Pendant of Wisdom! How astute!")
text['item_get_mushroom'] = CompressedTextMapper.convert("A Mushroom! Don't eat it. Find a witch.") text['item_get_mushroom'] = CompressedTextMapper.convert("Mushroom! Don't eat it. Find a witch.")
text['item_get_book'] = CompressedTextMapper.convert("It book! U R now litterit!") text['item_get_book'] = CompressedTextMapper.convert("Book! Are you well read?")
text['item_get_moonpearl'] = CompressedTextMapper.convert("I found a shiny marble! No more hops!") text['item_get_moonpearl'] = CompressedTextMapper.convert("Moon Pearl! Rabbit Be Gone!")
text['item_get_compass'] = CompressedTextMapper.convert("A compass! I can now find the boss.") text['item_get_compass'] = CompressedTextMapper.convert("A compass! I can now find the boss.")
# 60 # 60
text['item_get_map'] = CompressedTextMapper.convert("Yo! You found a MAP! Press X to see it.") text['item_get_map'] = CompressedTextMapper.convert("Yo! You found a MAP! Press X to see it.")
text['item_get_ice_rod'] = CompressedTextMapper.convert("It's the Ice Rod! Freeze Ray time.") text['item_get_ice_rod'] = CompressedTextMapper.convert("Ice Rod! Time to chill out!")
text['item_get_fire_rod'] = CompressedTextMapper.convert("A Rod that shoots fire? Let's burn all the things!") text['item_get_fire_rod'] = CompressedTextMapper.convert("Fire Rod! I'm burning for you!")
text['item_get_ether'] = CompressedTextMapper.convert("We can chill out with this!") text['item_get_ether'] = CompressedTextMapper.convert("Ether! Let's cool things down!")
text['item_get_bombos'] = CompressedTextMapper.convert("Let's set everything on fire, and melt things!") text['item_get_bombos'] = CompressedTextMapper.convert("Bombos! Explosions, fire, burn it all!")
text['item_get_quake'] = CompressedTextMapper.convert("Time to make the earth shake, rattle, and roll!") text['item_get_quake'] = CompressedTextMapper.convert("Quake! Let's shake the ground!")
text['item_get_hammer'] = CompressedTextMapper.convert("STOP!\n\nHammer Time!") # 66 text['item_get_hammer'] = CompressedTextMapper.convert("STOP!\n\nHammer Time!") # 66
text['item_get_ocarina'] = CompressedTextMapper.convert("Finally! We can play the Song of Time!") text['item_get_ocarina'] = CompressedTextMapper.convert("Ocarina! A Flute by another name")
text['item_get_cane_of_somaria'] = CompressedTextMapper.convert("Make blocks!\nThrow blocks!\nsplode Blocks!") text['item_get_cane_of_somaria'] = CompressedTextMapper.convert("Somaria! Make blocks, throw blocks")
text['item_get_hookshot'] = CompressedTextMapper.convert("BOING!!!\nBOING!!!\nSay no more…") text['item_get_hookshot'] = CompressedTextMapper.convert("Hookshot! Grab all the things!")
text['item_get_bombs'] = CompressedTextMapper.convert("BOMBS! Use A to pick 'em up, throw 'em, get hurt!") text['item_get_bombs'] = CompressedTextMapper.convert("BOMBS! Use A to pick 'em up, throw 'em, get hurt!")
text['item_get_bottle'] = CompressedTextMapper.convert("It's a terrarium. I hope we find a lizard!") text['item_get_bottle'] = CompressedTextMapper.convert("Bottle! Store all manner of things")
text['item_get_big_key'] = CompressedTextMapper.convert("Yo! You got a Big Key!") text['item_get_big_key'] = CompressedTextMapper.convert("Yo! You got a Big Key!")
text['item_get_titans_mitts'] = CompressedTextMapper.convert("So, like, you can now lift anything.\nANYTHING!") text['item_get_titans_mitts'] = CompressedTextMapper.convert("Mitts! Lift ALL the rocks!")
text['item_get_magic_mirror'] = CompressedTextMapper.convert("We could stare at this all day or, you know, beat Ganon…") text['item_get_magic_mirror'] = CompressedTextMapper.convert("Mirror! Take some time to reflect on this moment!")
text['item_get_fake_mastersword'] = CompressedTextMapper.convert("It's the Master Sword! …or not…\n\n FOOL!") text['item_get_fake_mastersword'] = CompressedTextMapper.convert("It's the Master Sword! …or not…\n\n FOOL!")
# 70 # 70
text['post_item_get_mastersword'] = CompressedTextMapper.convert("{NOBORDER}\n{SPEED6}\n@, you got the sword!\n{CHANGEMUSIC}\nNow let's go beat up Agahnim!") text['post_item_get_mastersword'] = CompressedTextMapper.convert("{NOBORDER}\n{SPEED6}\n@, you got the sword!\n{CHANGEMUSIC}\nNow let's go beat up Agahnim!")
text['item_get_red_potion'] = CompressedTextMapper.convert("Red goo to go! Nice!") text['item_get_red_potion'] = CompressedTextMapper.convert("Red Potion! Heal yourself")
text['item_get_green_potion'] = CompressedTextMapper.convert("Green goo to go! Nice!") text['item_get_green_potion'] = CompressedTextMapper.convert("Green Potion! Magic refill!")
text['item_get_blue_potion'] = CompressedTextMapper.convert("Blue goo to go! Nice!") text['item_get_blue_potion'] = CompressedTextMapper.convert("Blue Potion! Heal and restore!")
text['item_get_bug_net'] = CompressedTextMapper.convert("Surprise Net! Let's catch stuff!") text['item_get_bug_net'] = CompressedTextMapper.convert("Bug Net! Let's catch stuff!")
text['item_get_blue_mail'] = CompressedTextMapper.convert("Blue threads? Less damage activated!") text['item_get_blue_mail'] = CompressedTextMapper.convert("Blue Mail! Less damage activated!")
text['item_get_red_mail'] = CompressedTextMapper.convert("You feel the power of the eggplant on your head.") text['item_get_red_mail'] = CompressedTextMapper.convert("Red Mail! Even less damage!")
text['item_get_temperedsword'] = CompressedTextMapper.convert("Nice… I now have a craving for Cheetos.") text['item_get_temperedsword'] = CompressedTextMapper.convert("Tempered Sword! Even more slashy!")
text['item_get_mirror_shield'] = CompressedTextMapper.convert("Pit would be proud!") text['item_get_mirror_shield'] = CompressedTextMapper.convert("Mirror Shield! Time to reflect")
text['item_get_cane_of_byrna'] = CompressedTextMapper.convert("It's the Blue Cane. You can now protect yourself with lag!") text['item_get_cane_of_byrna'] = CompressedTextMapper.convert("Byrna! Swirly protection!")
text['missing_big_key'] = CompressedTextMapper.convert("Something is missing…\nThe Big Key?") text['missing_big_key'] = CompressedTextMapper.convert("Something is missing…\nThe Big Key?")
text['missing_magic'] = CompressedTextMapper.convert("Something is missing…\nMagic meter?") text['missing_magic'] = CompressedTextMapper.convert("Something is missing…\nMagic meter?")
text['item_get_pegasus_boots'] = CompressedTextMapper.convert("Finally, it's bonking time!\nHold A to dash") text['item_get_pegasus_boots'] = CompressedTextMapper.convert("Pegasus Boots! Finally, it's bonking time!\nHold A to dash")
text['talking_tree_info_start'] = CompressedTextMapper.convert("Whoa! I can talk again!") text['talking_tree_info_start'] = CompressedTextMapper.convert("Whoa! I can talk again!")
text['talking_tree_info_1'] = CompressedTextMapper.convert("Yank on the pitchfork in the center of town, ya heard it here.") text['talking_tree_info_1'] = CompressedTextMapper.convert("Yank on the pitchfork in the center of town, ya heard it here.")
text['talking_tree_info_2'] = CompressedTextMapper.convert("Ganon is such a dingus, no one likes him, ya heard it here.") text['talking_tree_info_2'] = CompressedTextMapper.convert("Ganon is such a dingus, no one likes him, ya heard it here.")
Binary file not shown.
+2 -2
View File
@@ -47,8 +47,8 @@
boss: 0 boss: 0
oneway: 0 oneway: 0
key_logic_algorithm: key_logic_algorithm:
default: 1 dangerous: 0
partial: 0 partial: 1
strict: 0 strict: 0
decoupledoors: off decoupledoors: off
door_self_loops: door_self_loops:
+10 -1
View File
@@ -25,7 +25,7 @@ trap_door_mode:
boss: 1 boss: 1
oneway: 1 oneway: 1
key_logic_algorithm: key_logic_algorithm:
default: 1 dangerous: 1
partial: 0 partial: 0
strict: 0 strict: 0
decoupledoors: decoupledoors:
@@ -71,6 +71,15 @@ shufflelinks:
shuffletavern: shuffletavern:
on: 1 on: 1
off: 1 off: 1
skullwoods:
original: 1
restricted: 1
loose: 1
followlinked: 1
linked_drops:
unset: 1
linked: 1
independent: 1
world_state: world_state:
standard: 1 standard: 1
open: 1 open: 1
+16 -1
View File
@@ -254,7 +254,7 @@
}, },
"key_logic_algorithm": { "key_logic_algorithm": {
"choices": [ "choices": [
"default", "dangerous",
"partial", "partial",
"strict" "strict"
] ]
@@ -516,6 +516,21 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"skullwoods": {
"choices": [
"original",
"restricted",
"loose",
"followlinked"
]
},
"linked_drops": {
"choices": [
"unset",
"linked",
"independent"
]
},
"overworld_map": { "overworld_map": {
"choices": [ "choices": [
"default", "default",
+15 -1
View File
@@ -305,7 +305,7 @@
], ],
"key_logic_algorithm": [ "key_logic_algorithm": [
"Key Logic Algorithm (default: %(default)s)", "Key Logic Algorithm (default: %(default)s)",
"default: Balance between safety and randomization", "dangerous: Key usage must follow logic for safety",
"partial: Partial protection when using certain minor glitches", "partial: Partial protection when using certain minor glitches",
"strict: Ensure small keys are available" "strict: Ensure small keys are available"
], ],
@@ -431,6 +431,20 @@
"shuffletavern": [ "shuffletavern": [
"Include the back of the tavern in the entrance shuffle pool. (default: %(default)s)" "Include the back of the tavern in the entrance shuffle pool. (default: %(default)s)"
], ],
"skullwoods": [
"Select how to shuffle skull woods (default: %(default)s)",
"Original: Skull Woods is shuffled amongst itself",
"Restricted: Drops are vanilla. Entrances stay in skull woods",
"Loose: Drops are vanilla. Entrances go in the main pool",
"Followlinked: If drops are linked, then pinball/left side will be vanilla",
" with other drops paired with an entrance. Otherwise, all go into the main pool"
],
"linked_drops": [
"Select how drops are treated in entrance shuffle. (default: %(default)s)",
"Unset: The shuffle mode determines the setting.",
"Linked: Dropdowns will be linked with entrance caves",
"Independent: Dropdowns will not be linked"
],
"overworld_map": [ "overworld_map": [
"Control if and how the overworld map indicators show the locations of dungeons (default: %(default)s)" "Control if and how the overworld map indicators show the locations of dungeons (default: %(default)s)"
], ],
+12 -1
View File
@@ -92,7 +92,7 @@
"randomizer.dungeon.trap_door_mode.oneway": "Remove All Annoying Traps", "randomizer.dungeon.trap_door_mode.oneway": "Remove All Annoying Traps",
"randomizer.dungeon.key_logic_algorithm": "Key Logic Algorithm", "randomizer.dungeon.key_logic_algorithm": "Key Logic Algorithm",
"randomizer.dungeon.key_logic_algorithm.default": "Default", "randomizer.dungeon.key_logic_algorithm.dangerous": "Dangerous",
"randomizer.dungeon.key_logic_algorithm.partial": "Partial Protection", "randomizer.dungeon.key_logic_algorithm.partial": "Partial Protection",
"randomizer.dungeon.key_logic_algorithm.strict": "Strict", "randomizer.dungeon.key_logic_algorithm.strict": "Strict",
@@ -195,6 +195,17 @@
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
"randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple", "randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple",
"randomizer.entrance.skullwoods": "Skull Woods Shuffle",
"randomizer.entrance.skullwoods.original": "Original",
"randomizer.entrance.skullwoods.restricted": "Vanilla Drops, Entrances Restricted",
"randomizer.entrance.skullwoods.loose": "Vanilla Drops, Entrances use Shuffle",
"randomizer.entrance.skullwoods.followlinked": "Follow Linked Drops Setting",
"randomizer.entrance.linked_drops": "Linked Drops Override",
"randomizer.entrance.linked_drops.unset": "Determined by Shuffle",
"randomizer.entrance.linked_drops.linked": "Always Linked",
"randomizer.entrance.linked_drops.independent": "Independent",
"randomizer.gameoptions.nobgm": "Disable Music & MSU-1", "randomizer.gameoptions.nobgm": "Disable Music & MSU-1",
"randomizer.gameoptions.quickswap": "L/R Quickswapping", "randomizer.gameoptions.quickswap": "L/R Quickswapping",
"randomizer.gameoptions.reduce_flashing": "Reduce Flashing", "randomizer.gameoptions.reduce_flashing": "Reduce Flashing",
@@ -14,11 +14,11 @@
}, },
"key_logic_algorithm": { "key_logic_algorithm": {
"type": "selectbox", "type": "selectbox",
"default": "default", "default": "partial",
"options": [ "options": [
"default",
"partial", "partial",
"strict" "strict",
"dangerous"
], ],
"config": { "config": {
"padx": [20,0], "padx": [20,0],
@@ -35,6 +35,26 @@
"padx": [20,0] "padx": [20,0]
} }
}, },
"skullwoods": {
"type": "selectbox",
"options": [
"original",
"restricted",
"loose",
"followlinked"
],
"config": {
"width": 30
}
},
"linked_drops": {
"type": "selectbox",
"options": [
"unset",
"linked",
"independent"
]
},
"openpyramid": { "openpyramid": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
+1 -1
View File
@@ -19,7 +19,7 @@ global FILESIZE_CHECK
# windows: 2022, 2019 # windows: 2022, 2019
# macos: 14, 13, 12, 11 # macos: 14, 13, 12, 11
DEFAULT_EVENT = "event" DEFAULT_EVENT = "event"
DEFAULT_REPO_SLUG = "miketrethewey/ALttPDoorRandomizer" DEFAULT_REPO_SLUG = "codemann8/ALttPDoorRandomizer"
FILENAME_CHECKS = [ FILENAME_CHECKS = [
"DungeonRandomizer", "DungeonRandomizer",
"Gui", "Gui",
+4 -4
View File
@@ -71,7 +71,7 @@ if len(BUILD_FILENAMES) > 0:
# clean the git slate # clean the git slate
git_clean() git_clean()
# mv dirs from source code # mv dirs from source code
dirs = [ dirs = [
os.path.join(".",".git"), os.path.join(".",".git"),
os.path.join(".",".github"), os.path.join(".",".github"),
@@ -98,8 +98,8 @@ if len(BUILD_FILENAMES) > 0:
if "linux" in env["OS_NAME"] or "ubuntu" in env["OS_NAME"] or "mac" in env["OS_NAME"] or "osx" in env["OS_NAME"]: if "linux" in env["OS_NAME"] or "ubuntu" in env["OS_NAME"] or "mac" in env["OS_NAME"] or "osx" in env["OS_NAME"]:
os.chmod(os.path.join(".",BUILD_FILENAME),0o755) os.chmod(os.path.join(".",BUILD_FILENAME),0o755)
# .zip if windows # .zip if windows
# .tar.gz otherwise # .tar.gz otherwise
if len(BUILD_FILENAMES) > 1: if len(BUILD_FILENAMES) > 1:
# ZIP_FILENAME = os.path.join("..","deploy",env["REPO_NAME"]) # ZIP_FILENAME = os.path.join("..","deploy",env["REPO_NAME"])
ZIP_FILENAME = os.path.join("..","deploy","ALttPOverworldRandomizer") ZIP_FILENAME = os.path.join("..","deploy","ALttPOverworldRandomizer")
@@ -112,7 +112,7 @@ if len(BUILD_FILENAMES) > 0:
make_archive(ZIP_FILENAME,"gztar") make_archive(ZIP_FILENAME,"gztar")
ZIP_FILENAME += ".tar.gz" ZIP_FILENAME += ".tar.gz"
# mv dirs back # mv dirs back
for dir in dirs: for dir in dirs:
if os.path.isdir(os.path.join("..","build",dir)): if os.path.isdir(os.path.join("..","build",dir)):
move( move(
+4
View File
@@ -160,6 +160,8 @@ class CustomSettings(object):
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p]) args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p]) args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
args.shuffletavern[p] = get_setting(settings['shuffletavern'], args.shuffletavern[p]) args.shuffletavern[p] = get_setting(settings['shuffletavern'], args.shuffletavern[p])
args.skullwoods[p] = get_setting(settings['skullwoods'], args.skullwoods[p])
args.linked_drops[p] = get_setting(settings['linked_drops'], args.linked_drops[p])
args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p]) args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p])
args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p]) args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p])
args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p]) args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p])
@@ -335,6 +337,8 @@ class CustomSettings(object):
settings_dict[p]['bombbag'] = world.bombbag[p] settings_dict[p]['bombbag'] = world.bombbag[p]
settings_dict[p]['shufflelinks'] = world.shufflelinks[p] settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
settings_dict[p]['shuffletavern'] = world.shuffletavern[p] settings_dict[p]['shuffletavern'] = world.shuffletavern[p]
settings_dict[p]['skullwoods'] = world.skullwoods[p]
settings_dict[p]['linked_drops'] = world.linked_drops[p]
settings_dict[p]['overworld_map'] = world.overworld_map[p] settings_dict[p]['overworld_map'] = world.overworld_map[p]
settings_dict[p]['pseudoboots'] = world.pseudoboots[p] settings_dict[p]['pseudoboots'] = world.pseudoboots[p]
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p] settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
+2
View File
@@ -103,6 +103,8 @@ SETTINGSTOPROCESS = {
"shuffleganon": "shuffleganon", "shuffleganon": "shuffleganon",
"shufflelinks": "shufflelinks", "shufflelinks": "shufflelinks",
"shuffletavern": "shuffletavern", "shuffletavern": "shuffletavern",
"skullwoods": "skullwoods",
"linked_drops": "linked_drops",
"openpyramid": "openpyramid", "openpyramid": "openpyramid",
"overworld_map": "overworld_map", "overworld_map": "overworld_map",
}, },
+1 -2
View File
@@ -201,8 +201,7 @@ def find_possible_rules(vln_list, used_resources, world, player):
flag = flag if flag < 0 else (hits + used_resources['Magic'] * 7 / 8) flag = flag if flag < 0 else (hits + used_resources['Magic'] * 7 / 8)
optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources)) optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources))
elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160: elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values()) flag = hits + used_resources['Magic']
flag = flag if flag < 0 else (hits + used_resources['Magic'])
optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources)) optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources))
elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160: elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values()) flag = min(vln[damage_type] for vln in vln_list.values())
+18 -9
View File
@@ -34,8 +34,9 @@ UwGeneralDeny:
- [ 0x001e, 4, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Red Bari 4" - [ 0x001e, 4, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Red Bari 4"
- [ 0x001e, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 1" - [ 0x001e, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 1"
- [ 0x001e, 6, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 2" - [ 0x001e, 6, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 2"
- [ 0x001f, 0, [ "RollerHorizontalRight" ] ] #"Ice Palace - Big Key View - Pengator 1" - [0x001f, 0, ["RollerHorizontalRight", "RollerHorizontalLeft"]] #"Ice Palace - Big Key View - Pengator 1"
- [ 0x001f, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x001f, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [0x001f, 4, ["RollerVerticalDown", "RollerVerticalUp"]] # too large, hits the door
- [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2" - [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2"
- [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3" - [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3"
- [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
@@ -54,10 +55,12 @@ UwGeneralDeny:
- [0x0028, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] - [0x0028, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] - [0x0028, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]]
- [0x0028, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalUp"]] #"Swamp Palace - Entrance Ledge - Spike Trap" - [0x0028, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalUp"]] #"Swamp Palace - Entrance Ledge - Spike Trap"
- [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1" - [0x002a, 1, ["RollerVerticalUp", "RollerVerticalDown"]]
- [ 0x002a, 3, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 2" - [0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1"
- [0x002a, 3, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Chainchomp"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 2"
- [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]] - [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]]
- [ 0x002a, 6, [ "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 5" - [ 0x002a, 6, [ "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 5"
- [0x002a, 7, ["RollerVerticalUp", "RollerVerticalDown", "Chainchomp"]]
- [ 0x002b, 5, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Fairies - Red Bari 2" - [ 0x002b, 5, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Fairies - Red Bari 2"
- [ 0x002e, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 1" - [ 0x002e, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 1"
- [ 0x002e, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 2" - [ 0x002e, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 2"
@@ -80,6 +83,7 @@ UwGeneralDeny:
- [0x0039, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Bumper"]] #"Skull Woods - Play Pen - Hardhat Beetle" - [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" - [ 0x0039, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 2"
- [ 0x003b, 1, [ "Bumper" ]] - [ 0x003b, 1, [ "Bumper" ]]
- [ 0x003b, 4, ["RollerVerticalUp", "RollerVerticalDown"]]
- [ 0x003c, 0, ["BigSpike"]] - [ 0x003c, 0, ["BigSpike"]]
- [ 0x003c, 1, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Hookshot Cave - Blue Bari 1" - [ 0x003c, 1, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Hookshot Cave - Blue Bari 1"
- [ 0x003c, 2, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hookshot Cave - Blue Bari 2" - [ 0x003c, 2, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hookshot Cave - Blue Bari 2"
@@ -118,7 +122,7 @@ UwGeneralDeny:
- [ 0x0049, 5, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 2" - [ 0x0049, 5, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 2"
- [ 0x0049, 7, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 4" - [ 0x0049, 7, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 4"
- [ 0x0049, 8, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Skull Woods - Bari Pits - Gibdo 5" - [ 0x0049, 8, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Skull Woods - Bari Pits - Gibdo 5"
- [ 0x004b, 0, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Palace of Darkness - Mimics 1 - Red Eyegore" - [0x004b, 0, ["Beamos", "AntiFairyCircle", "Bumper", "BigSpike"]] #"Palace of Darkness - Mimics 1 - Red Eyegore"
- [ 0x004b, 1, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Warp Hint - Antifairy 1" - [ 0x004b, 1, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Warp Hint - Antifairy 1"
- [ 0x004b, 5, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 1" - [ 0x004b, 5, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 1"
- [ 0x004b, 6, [ "AntiFairyCircle", "BigSpike" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 2" - [ 0x004b, 6, [ "AntiFairyCircle", "BigSpike" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 2"
@@ -129,6 +133,7 @@ UwGeneralDeny:
- [ 0x0050, 0, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Guard" - [ 0x0050, 0, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Guard"
- [ 0x0050, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 1" - [ 0x0050, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 1"
- [ 0x0050, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 2" - [ 0x0050, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 2"
- [0x0051, 2, ["Zoro"]] # Zoro clips off and doesn't return
- [ 0x0052, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Guard" - [ 0x0052, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Guard"
- [ 0x0052, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 1" - [ 0x0052, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 1"
- [ 0x0052, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 2" - [ 0x0052, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 2"
@@ -151,7 +156,7 @@ UwGeneralDeny:
- [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 1" - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 1"
- [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 2" - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 2"
- [ 0x0058, 0, ["Statue"]] - [ 0x0058, 0, ["Statue"]]
- [ 0x0058, 1, ["Statue"]] - [0x0058, 1, ["Statue", "RollerHorizontalRight", "RollerHorizontalLeft"]]
- [ 0x0058, 2, ["Statue"]] - [ 0x0058, 2, ["Statue"]]
- [ 0x0058, 3, ["Statue"]] - [ 0x0058, 3, ["Statue"]]
- [ 0x0058, 4, ["Statue"]] - [ 0x0058, 4, ["Statue"]]
@@ -205,7 +210,7 @@ UwGeneralDeny:
- [ 0x007b, 7, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - DMs Room - Hardhat Beetle" - [ 0x007b, 7, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - DMs Room - Hardhat Beetle"
- [ 0x007c, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Counterclockwise)" - [ 0x007c, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Counterclockwise)"
- [ 0x007c, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Spike Trap" - [ 0x007c, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Spike Trap"
- [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)" - [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue", "SpikeBlock"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)"
- [ 0x007c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Hardhat Beetle" - [ 0x007c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Hardhat Beetle"
- [ 0x007d, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 1" - [ 0x007d, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 1"
- [ 0x007d, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 2" - [ 0x007d, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 2"
@@ -267,6 +272,7 @@ UwGeneralDeny:
- [ 0x009c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 3" - [ 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, 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, 5, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 5"
- [0x009d, 2, ["AntiFairyCircle"]]
- [ 0x009d, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Compass Room - Gibdo 2" - [ 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" - [ 0x009d, 6, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - Compass Room - Blue Bari 1"
- [ 0x009d, 7, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Compass Room - Blue Bari 2" - [ 0x009d, 7, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Compass Room - Blue Bari 2"
@@ -288,7 +294,7 @@ UwGeneralDeny:
- [ 0x00ab, 7, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Thieves' Town - Spike Dodge - Spike Trap 6" - [ 0x00ab, 7, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Thieves' Town - Spike Dodge - Spike Trap 6"
- [ 0x00ae, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 1" - [ 0x00ae, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 1"
- [ 0x00ae, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 2" - [ 0x00ae, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 2"
- [ 0x00af, 0, [ "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Ice Clock - Fire Bar (Clockwise)" - [0x00af, 0, ["RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Lynel"]] #"Ice Palace - Ice Clock - Fire Bar (Clockwise)"
- [0x00b0, 7, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay - [0x00b0, 7, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
- [0x00b0, 8, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay - [0x00b0, 8, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
- [ 0x00b1, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 1" - [ 0x00b1, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 1"
@@ -332,6 +338,7 @@ UwGeneralDeny:
- [ 0x00cb, 9, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] - [ 0x00cb, 9, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ]
- [ 0x00cb, 10, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] - [ 0x00cb, 10, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ]
- [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [0x00cc, 6, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper"]] # prevent access around
- [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?)
- [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?)
- [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper", "Chainchomp"]] #"Ice Palace - Over Boss - top - Red Bari 1" - [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper", "Chainchomp"]] #"Ice Palace - Over Boss - top - Red Bari 1"
@@ -389,7 +396,7 @@ UwGeneralDeny:
- [ 0x00e7, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 6" - [ 0x00e7, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 6"
- [ 0x00e7, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 7" - [ 0x00e7, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 7"
- [ 0x00e8, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 1" - [ 0x00e8, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 1"
- [ 0x00e8, 1, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 2" - [ 0x00e8, 2, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 2"
- [ 0x00ee, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 1" - [ 0x00ee, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 1"
- [ 0x00ee, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 2" - [ 0x00ee, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 2"
- [ 0x00ee, 2, [ "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 3" - [ 0x00ee, 2, [ "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 3"
@@ -402,7 +409,7 @@ UwGeneralDeny:
- [ 0x00f1, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 4" - [ 0x00f1, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 4"
- [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5" - [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5"
- [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6" - [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6"
- [ 0x00fd, 0, [ "Bumper" ] ] - [0x00fd, 0, ["Bumper", "AntiFairyCircle"]]
- [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x010b, 6, ["RollerHorizontalRight"]] - [0x010b, 6, ["RollerHorizontalRight"]]
@@ -449,6 +456,8 @@ OwGeneralDeny:
- [0x5e, 18, ["Gibo"]] # kiki eating Gibo - [0x5e, 18, ["Gibo"]] # kiki eating Gibo
- [0x5e, 19, ["Gibo"]] # kiki eating Gibo - [0x5e, 19, ["Gibo"]] # kiki eating Gibo
- [0x5e, 20, ["Gibo"]] # kiki eating Gibo - [0x5e, 20, ["Gibo"]] # kiki eating Gibo
- [0x62, 1, ["RollerVerticalUp", "RollerVerticalDown"]] # hard to avoid roller around hammer pegs
- [0x62, 3, ["RollerVerticalUp", "RollerVerticalDown"]] # hard to avoid roller around hammer pegs
- [0x77, 1, ["Bumper"]] # soft-lock potential near ladder - [0x77, 1, ["Bumper"]] # soft-lock potential near ladder
- [0x7f, 1, ["Bumper"]] # soft-lock potential near ladder - [0x7f, 1, ["Bumper"]] # soft-lock potential near ladder
UwEnemyDrop: UwEnemyDrop:
+283 -98
View File
@@ -26,6 +26,7 @@ class EntrancePool(object):
self.decoupled_exits = [] self.decoupled_exits = []
self.original_entrances = set() self.original_entrances = set()
self.original_exits = set() self.original_exits = set()
self.same_world_restricted = {}
self.world = world self.world = world
self.player = player self.player = player
@@ -59,7 +60,6 @@ def link_entrances_new(world, player):
avail_pool.entrances = set(i_drop_map.keys()).union(i_entrance_map.keys()).union(i_single_ent_map.keys()) avail_pool.entrances = set(i_drop_map.keys()).union(i_entrance_map.keys()).union(i_single_ent_map.keys())
avail_pool.exits = set(i_entrance_map.values()).union(i_drop_map.values()).union(i_single_ent_map.values()) avail_pool.exits = set(i_entrance_map.values()).union(i_drop_map.values()).union(i_single_ent_map.values())
avail_pool.inverted = world.mode[player] == 'inverted' avail_pool.inverted = world.mode[player] == 'inverted'
avail_pool.assumed_loose_caves = world.shuffle[player] == 'district'
inverted_substitution(avail_pool, avail_pool.entrances, True, True) inverted_substitution(avail_pool, avail_pool.entrances, True, True)
inverted_substitution(avail_pool, avail_pool.exits, False, True) inverted_substitution(avail_pool, avail_pool.exits, False, True)
avail_pool.original_entrances.update(avail_pool.entrances) avail_pool.original_entrances.update(avail_pool.entrances)
@@ -99,8 +99,13 @@ def link_entrances_new(world, player):
if mode not in modes: if mode not in modes:
raise RuntimeError(f'Shuffle mode {mode} is not yet supported') raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
mode_cfg = copy.deepcopy(modes[mode]) mode_cfg = copy.deepcopy(modes[mode])
if world.linked_drops[player] != 'unset':
mode_cfg['keep_drops_together'] = 'on' if world.linked_drops[player] == 'linked' else 'off'
avail_pool.swapped = mode_cfg['undefined'] == 'swap' avail_pool.swapped = mode_cfg['undefined'] == 'swap'
avail_pool.keep_drops_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True avail_pool.keep_drops_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
avail_pool.assumed_loose_caves = not avail_pool.keep_drops_together and world.shuffle[player] == 'district'
avail_pool.coupled = mode_cfg['decoupled'] != 'on' if 'decoupled' in mode_cfg else True avail_pool.coupled = mode_cfg['decoupled'] != 'on' if 'decoupled' in mode_cfg else True
if avail_pool.is_standard(): if avail_pool.is_standard():
do_standard_connections(avail_pool) do_standard_connections(avail_pool)
@@ -108,11 +113,7 @@ def link_entrances_new(world, player):
for pool_name, pool in pool_list.items(): for pool_name, pool in pool_list.items():
special_shuffle = pool['special'] if 'special' in pool else None special_shuffle = pool['special'] if 'special' in pool else None
if special_shuffle == 'drops': if special_shuffle == 'drops':
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances']) handle_skull_woods_drops(avail_pool, pool['entrances'], mode_cfg)
if avail_pool.swapped:
connect_swapped(holes, targets, avail_pool)
else:
connect_random(holes, targets, avail_pool)
elif special_shuffle == 'normal_drops': elif special_shuffle == 'normal_drops':
cross_world = mode_cfg['cross_world'] == 'on' if 'cross_world' in mode_cfg else False cross_world = mode_cfg['cross_world'] == 'on' if 'cross_world' in mode_cfg else False
do_holes_and_linked_drops(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, cross_world) do_holes_and_linked_drops(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, cross_world)
@@ -143,20 +144,7 @@ def link_entrances_new(world, player):
entrances, exits = find_entrances_and_exits(avail_pool, entrances+drops) entrances, exits = find_entrances_and_exits(avail_pool, entrances+drops)
do_main_shuffle(entrances, exits, avail_pool, mode_cfg) do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
elif special_shuffle == 'skull': elif special_shuffle == 'skull':
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) handle_skull_woods_entrances(avail_pool, pool['entrances'])
rem_ent = None
if avail_pool.world.shuffle[avail_pool.player] in ['dungeonssimple', 'simple', 'restricted'] \
and not avail_pool.world.is_tile_swapped(0x00, avail_pool.player):
rem_ent = random.choice(['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)'])
entrances.remove(rem_ent)
exits.remove('Skull Woods First Section Exit')
connect_random(entrances, exits, avail_pool, True)
entrances, exits = [rem_ent], ['Skull Woods First Section Exit']
if avail_pool.swapped:
connect_swapped(entrances, exits, avail_pool, True)
else:
connect_random(entrances, exits, avail_pool, True)
avail_pool.skull_handled = True
else: else:
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
do_main_shuffle(entrances, exits, avail_pool, mode_cfg) do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
@@ -253,12 +241,21 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
# mandatory exits # mandatory exits
rem_entrances, rem_exits = set(), set() rem_entrances, rem_exits = set(), set()
if not cross_world: if not cross_world:
determine_dungeon_restrictions(avail)
mand_exits = figure_out_must_exits_same_world(entrances, exits, avail) mand_exits = figure_out_must_exits_same_world(entrances, exits, avail)
must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves = mand_exits must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves = mand_exits
def do_world_mandatory(world_entrances, world_must_exits, restriction):
nonlocal multi_exit_caves
candidates = filter_restricted_caves(multi_exit_caves, restriction, avail)
other_candidates = [x for x in multi_exit_caves if x not in candidates] # remember those not passed in
do_mandatory_connections(avail, world_entrances, candidates, world_must_exits)
multi_exit_caves = (other_candidates + candidates) if other_candidates else candidates # rebuild list from the candidates and those not passed
if not avail.inverted: if not avail.inverted:
do_mandatory_connections(avail, lw_entrances, multi_exit_caves, must_exit_lw) do_world_mandatory(lw_entrances, must_exit_lw, 'LightWorld')
else: else:
do_mandatory_connections(avail, dw_entrances, multi_exit_caves, must_exit_dw) do_world_mandatory(dw_entrances, must_exit_dw, 'DarkWorld')
new_mec = [] new_mec = []
for cave_option in multi_exit_caves: for cave_option in multi_exit_caves:
@@ -272,9 +269,9 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
multi_exit_caves = new_mec multi_exit_caves = new_mec
if not avail.inverted: if not avail.inverted:
do_mandatory_connections(avail, dw_entrances, multi_exit_caves, must_exit_dw) do_world_mandatory(dw_entrances, must_exit_dw, 'DarkWorld')
else: else:
do_mandatory_connections(avail, lw_entrances, multi_exit_caves, must_exit_lw) do_world_mandatory(lw_entrances, must_exit_lw, 'LightWorld')
rem_entrances.update(lw_entrances) rem_entrances.update(lw_entrances)
rem_entrances.update(dw_entrances) rem_entrances.update(dw_entrances)
else: else:
@@ -367,6 +364,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
if bonk_fairy_exception(x): if bonk_fairy_exception(x):
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x) lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail) do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail)
if avail.world.doorShuffle[avail.player] != 'vanilla':
determine_dungeon_restrictions(avail)
possibles = figure_out_possible_exits(rem_exits)
do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail)
unused_entrances.update(lw_entrances) unused_entrances.update(lw_entrances)
unused_entrances.update(dw_entrances) unused_entrances.update(dw_entrances)
else: else:
@@ -519,12 +520,16 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
if not avail.keep_drops_together: if not avail.keep_drops_together:
targets = [avail.one_way_map[x] for x in holes_to_shuffle] targets = [avail.one_way_map[x] for x in holes_to_shuffle]
connect_random(holes_to_shuffle, targets, avail) if avail.swapped:
connect_swapped(holes_to_shuffle, targets, avail)
else:
connect_random(holes_to_shuffle, targets, avail)
remove_from_list(entrances, holes_to_shuffle) remove_from_list(entrances, holes_to_shuffle)
remove_from_list(exits, targets) remove_from_list(exits, targets)
return # we're done here return # we're done here
hole_entrances, hole_targets = [], [] hole_entrances, hole_targets = [], []
leftover_hole_entrances, leftover_hole_targets = [], []
for hole in drop_map: for hole in drop_map:
if hole in avail.original_entrances and hole in linked_drop_map: if hole in avail.original_entrances and hole in linked_drop_map:
linked_entrance = linked_drop_map[hole] linked_entrance = linked_drop_map[hole]
@@ -534,39 +539,44 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
target_drop = avail.one_way_map[hole] target_drop = avail.one_way_map[hole]
if target_exit in exits and target_drop in exits: if target_exit in exits and target_drop in exits:
hole_targets.append((target_exit, target_drop)) hole_targets.append((target_exit, target_drop))
else:
if hole in avail.original_entrances and hole in entrances:
leftover_hole_entrances.append(hole)
if drop_map[hole] in exits:
leftover_hole_targets.append(drop_map[hole])
random.shuffle(hole_entrances) random.shuffle(hole_entrances)
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle: if not cross_world:
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player) if 'Sanctuary Grave' in holes_to_shuffle:
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted' hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
if hc.connected_region: is_hc_in_opp_world = avail.inverted
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld if hc.connected_region:
chosen_entrance = None opp_world = RegionType.LightWorld if avail.inverted else RegionType.DarkWorld
if is_hc_in_dw: is_hc_in_opp_world = hc.connected_region.type == opp_world
if avail.swapped: start_world_entrances = DW_Entrances if avail.inverted else LW_Entrances
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary') opp_world_entrances = LW_Entrances if avail.inverted else DW_Entrances
chosen_entrance = None
if is_hc_in_opp_world:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in opp_world_entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next((e for e in hole_entrances if e[0] in opp_world_entrances), None)
if not chosen_entrance: if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances) if avail.swapped:
if not chosen_entrance: chosen_entrance = next(e for e in hole_entrances if e[0] in start_world_entrances and e[0] != 'Sanctuary')
if avail.swapped: if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary') chosen_entrance = next(e for e in hole_entrances if e[0] in start_world_entrances)
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances) if chosen_entrance:
if chosen_entrance: connect_hole_via_interior(chosen_entrance, 'Sanctuary Exit', hole_entrances, hole_targets, entrances, exits, avail)
hole_entrances.remove(chosen_entrance)
sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') sw_world_entrances = DW_Entrances if not avail.world.is_tile_swapped(0x00, avail.player) else LW_Entrances
hole_targets.remove(sanc_interior) if 'Skull Woods First Section Hole (North)' in holes_to_shuffle:
connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit chosen_entrance = next(e for e in hole_entrances if e[0] in sw_world_entrances)
connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole connect_hole_via_interior(chosen_entrance, 'Skull Woods First Section Exit', hole_entrances, hole_targets, entrances, exits, avail)
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]]) if 'Skull Woods Second Section Hole' in holes_to_shuffle:
remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) chosen_entrance = next(e for e in hole_entrances if e[0] in sw_world_entrances)
if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]: connect_hole_via_interior(chosen_entrance, 'Skull Woods Second Section Exit (East)', hole_entrances, hole_targets, entrances, exits, avail)
swap_ent, swap_ext = connect_swap(chosen_entrance[0], sanc_interior[0], avail)
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], sanc_interior[1], avail)
hole_entrances.remove((swap_ent, swap_drop))
hole_targets.remove((swap_ext, swap_tgt))
remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt])
random.shuffle(hole_targets) random.shuffle(hole_targets)
while len(hole_entrances): while len(hole_entrances):
@@ -588,6 +598,31 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
remove_from_list(entrances, [swap_ent, swap_drop]) remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt]) remove_from_list(exits, [swap_ext, swap_tgt])
if leftover_hole_entrances and leftover_hole_targets:
remove_from_list(entrances, leftover_hole_entrances)
remove_from_list(exits, leftover_hole_targets)
if avail.swapped:
connect_swapped(leftover_hole_entrances, leftover_hole_targets, avail)
else:
connect_random(leftover_hole_entrances, leftover_hole_targets, avail)
def connect_hole_via_interior(chosen_entrance, interior, hole_entrances, hole_targets, entrances, exits, avail):
hole_entrances.remove(chosen_entrance)
interior = next(target for target in hole_targets if target[0] == interior)
hole_targets.remove(interior)
connect_two_way(chosen_entrance[0], interior[0], avail)
connect_entrance(chosen_entrance[1], interior[1], avail)
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
remove_from_list(exits, [interior[0], interior[1]])
if avail.swapped and drop_map[chosen_entrance[1]] != interior[1]:
swap_ent, swap_ext = connect_swap(chosen_entrance[0], interior[0], avail)
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], interior[1], avail)
hole_entrances.remove((swap_ent, swap_drop))
hole_targets.remove((swap_ext, swap_tgt))
remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt])
def do_dark_sanc(entrances, exits, avail): def do_dark_sanc(entrances, exits, avail):
if avail.world.is_dark_chapel_start(avail.player): if avail.world.is_dark_chapel_start(avail.player):
@@ -867,14 +902,15 @@ def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_wo
return found_entrances return found_entrances
def figure_out_connectors(exits, avail): def figure_out_connectors(exits, avail, cross_world=True):
multi_exit_caves = [] multi_exit_caves = []
cave_list = list(Connector_List) cave_list = list(Connector_List)
if avail.assumed_loose_caves: if avail.assumed_loose_caves or (not avail.skull_handled and (cross_world or not avail.world.is_tile_swapped(0x00, avail.player))):
sw_list = ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'] skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
random.shuffle(sw_list) cave_list.extend(skull_connector)
cave_list.extend([sw_list]) if avail.assumed_loose_caves or not avail.keep_drops_together:
cave_list.extend([[entrance_map[e]] for e in linked_drop_map.values() if 'Inverted ' not in e]) cave_list.extend([[entrance_map[e]] for e in linked_drop_map.values() if 'Inverted ' not in e and 'Skull Woods ' not in e])
for item in cave_list: for item in cave_list:
if all(x in exits for x in item): if all(x in exits for x in item):
remove_from_list(exits, item) remove_from_list(exits, item)
@@ -1055,12 +1091,49 @@ def must_exits_helper(avail):
return must_exit_lw, must_exit_dw return must_exit_lw, must_exit_dw
def figure_out_possible_exits(exits):
possible_multi_exit_caves = []
for item in doors_possible_connectors:
if item in exits:
remove_from_list(exits, item)
possible_multi_exit_caves.append(item)
return possible_multi_exit_caves
def determine_dungeon_restrictions(avail):
check_for_hc = (avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla')
for check in dungeon_restriction_checks:
dungeon_exits, drop_regions = check
if check_for_hc and any('Hyrule Castle' in x for x in dungeon_exits):
avail.same_world_restricted.update({x: 'LightWorld' for x in dungeon_exits})
else:
restriction = None
for x in dungeon_exits:
ent = avail.world.get_entrance(x, avail.player)
if ent.connected_region:
if ent.connected_region.type == RegionType.LightWorld:
restriction = 'LightWorld'
elif ent.connected_region.type == RegionType.DarkWorld:
restriction = 'DarkWorld'
# Holes only restrict
for x in drop_regions:
region = avail.world.get_region(x, avail.player)
ent = next((ent for ent in region.entrances if ent.parent_region and ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]), None)
if ent:
if ent.parent_region.type == RegionType.LightWorld and not avail.inverted:
restriction = 'LightWorld'
elif ent.parent_region.type == RegionType.DarkWorld and avail.inverted:
restriction = 'DarkWorld'
if restriction:
avail.same_world_restricted.update({x: restriction for x in dungeon_exits})
def figure_out_must_exits_same_world(entrances, exits, avail): def figure_out_must_exits_same_world(entrances, exits, avail):
lw_entrances, dw_entrances = [], [] lw_entrances, dw_entrances = [], []
for x in entrances: for x in entrances:
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x) lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
multi_exit_caves = figure_out_connectors(exits, avail) multi_exit_caves = figure_out_connectors(exits, avail, False)
must_exit_lw, must_exit_dw = must_exits_helper(avail) must_exit_lw, must_exit_dw = must_exits_helper(avail)
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances) must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
@@ -1069,8 +1142,26 @@ def figure_out_must_exits_same_world(entrances, exits, avail):
return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves
def filter_restricted_caves(multi_exit_caves, restriction, avail):
candidates = []
for cave in multi_exit_caves:
if all(x not in avail.same_world_restricted or avail.same_world_restricted[x] == restriction for x in cave):
candidates.append(cave)
return candidates
def flatten(list_to_flatten):
ret = []
for item in list_to_flatten:
if isinstance(item, tuple):
ret.extend(item)
else:
ret.append(item)
return ret
def figure_out_must_exits_cross_world(entrances, exits, avail): def figure_out_must_exits_cross_world(entrances, exits, avail):
multi_exit_caves = figure_out_connectors(exits, avail) multi_exit_caves = figure_out_connectors(exits, avail, True)
must_exit_lw, must_exit_dw = must_exits_helper(avail) must_exit_lw, must_exit_dw = must_exits_helper(avail)
must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances) must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances)
@@ -1083,7 +1174,7 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
random.shuffle(dw_entrances) random.shuffle(dw_entrances)
random.shuffle(caves) random.shuffle(caves)
while caves: while caves:
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill # connect highest-exit-count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
cave_candidate = (None, 0) cave_candidate = (None, 0)
for i, cave in enumerate(caves): for i, cave in enumerate(caves):
if isinstance(cave, str): if isinstance(cave, str):
@@ -1092,12 +1183,19 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
cave_candidate = (i, len(cave)) cave_candidate = (i, len(cave))
cave = caves.pop(cave_candidate[0]) cave = caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
if isinstance(cave, str): if isinstance(cave, str):
cave = (cave,) cave = (cave,)
target, restriction = None, None
if any(x in avail.same_world_restricted for x in cave):
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
# check if we can still fit the cave into our target group # check if we can still fit the cave into our target group
if len(target) < len(cave): if len(target) < len(cave):
if restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (main)')
# need to use other set # need to use other set
target = lw_entrances if target is dw_entrances else dw_entrances target = lw_entrances if target is dw_entrances else dw_entrances
@@ -1111,6 +1209,18 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
connect_two_way(target.pop(), ext, avail) connect_two_way(target.pop(), ext, avail)
def do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail):
random.shuffle(possibles)
while possibles:
possible = possibles.pop()
target = None
if possible in avail.same_world_restricted:
target = lw_entrances if avail.same_world_restricted[possible] == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
connect_two_way(target.pop(), possible, avail)
determine_dungeon_restrictions(avail)
def do_cross_world_connectors(entrances, caves, avail): def do_cross_world_connectors(entrances, caves, avail):
random.shuffle(entrances) random.shuffle(entrances)
random.shuffle(caves) random.shuffle(caves)
@@ -1158,6 +1268,44 @@ def do_cross_world_connectors(entrances, caves, avail):
break break
def handle_skull_woods_drops(avail, pool, mode_cfg):
skull_woods = avail.world.skullwoods[avail.player]
if skull_woods in ['restricted', 'loose']:
for drop in pool:
target = drop_map[drop]
connect_entrance(drop, target, avail)
elif skull_woods == 'original':
holes, targets = find_entrances_and_targets_drops(avail, pool)
if avail.swapped:
connect_swapped(holes, targets, avail)
else:
connect_random(holes, targets, avail)
elif skull_woods == 'followlinked':
keep_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
if keep_together:
for drop in ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)']:
target = drop_map[drop]
connect_entrance(drop, target, avail)
def handle_skull_woods_entrances(avail, pool):
skull_woods = avail.world.skullwoods[avail.player]
if skull_woods in ['restricted', 'original']:
entrances, exits = find_entrances_and_exits(avail, pool)
if avail.world.shuffle[avail.player] in ['dungeonssimple', 'simple', 'restricted'] \
and not avail.world.is_tile_swapped(0x00, avail.player):
rem_ent = random.choice(['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)'])
entrances.remove(rem_ent)
exits.remove('Skull Woods First Section Exit')
connect_random(entrances, exits, avail, True)
entrances, exits = [rem_ent], ['Skull Woods First Section Exit']
if avail.swapped:
connect_swapped(entrances, exits, avail, True)
else:
connect_random(entrances, exits, avail, True)
avail.skull_handled = True
def do_fixed_shuffle(avail, entrance_list): def do_fixed_shuffle(avail, entrance_list):
max_size = 0 max_size = 0
options = {} options = {}
@@ -1214,7 +1362,6 @@ def do_same_world_shuffle(avail, pool_def):
multi_exit = pool_def['connectors'] multi_exit = pool_def['connectors']
# complete_entrance_set = set() # complete_entrance_set = set()
lw_entrances, dw_entrances, multi_exits_caves, other_exits = [], [], [], [] lw_entrances, dw_entrances, multi_exits_caves, other_exits = [], [], [], []
hyrule_forced = None
single_entrances, single_exits = find_entrances_and_exits(avail, single_exit) single_entrances, single_exits = find_entrances_and_exits(avail, single_exit)
other_exits.extend(single_exits) other_exits.extend(single_exits)
@@ -1224,11 +1371,8 @@ def do_same_world_shuffle(avail, pool_def):
for option in multi_exit: for option in multi_exit:
multi_entrances, multi_exits = find_entrances_and_exits(avail, option) multi_entrances, multi_exits = find_entrances_and_exits(avail, option)
# complete_entrance_set.update(multi_entrances) # complete_entrance_set.update(multi_entrances)
if avail.is_sanc_forced_in_hc() and any(x in multi_entrances for x in ['Hyrule Castle Entrance (South)', if multi_exits:
'Hyrule Castle Entrance (East)', multi_exits_caves.append(multi_exits)
'Hyrule Castle Entrance (West)']):
hyrule_forced = [multi_exits]
multi_exits_caves.append(multi_exits)
for x in multi_entrances: for x in multi_entrances:
(dw_entrances, lw_entrances)[x in LW_Entrances].append(x) (dw_entrances, lw_entrances)[x in LW_Entrances].append(x)
@@ -1236,13 +1380,16 @@ def do_same_world_shuffle(avail, pool_def):
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances) must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances) must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances)
do_mandatory_connections(avail, lw_entrances, multi_exits_caves, must_exit_lw) def do_world_mandatory(world_entrances, world_must_exits, restriction):
if hyrule_forced and hyrule_forced[0] in multi_exits_caves: nonlocal multi_exits_caves
remove_from_list(multi_exits_caves, hyrule_forced) candidates = filter_restricted_caves(multi_exits_caves, restriction, avail)
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw) other_candidates = [x for x in multi_exits_caves if x not in candidates] # remember those not passed in
multi_exits_caves.append(hyrule_forced[0]) do_mandatory_connections(avail, world_entrances, candidates, world_must_exits)
else: multi_exits_caves = (other_candidates + candidates) if other_candidates else candidates # rebuild list from the candidates and those not passed
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw)
determine_dungeon_restrictions(avail)
do_world_mandatory(lw_entrances, must_exit_lw, 'LightWorld')
do_world_mandatory(dw_entrances, must_exit_dw, 'DarkWorld')
# connect caves # connect caves
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
@@ -1250,19 +1397,20 @@ def do_same_world_shuffle(avail, pool_def):
random.shuffle(multi_exits_caves) random.shuffle(multi_exits_caves)
while multi_exits_caves: while multi_exits_caves:
cave_candidate = (None, 0) cave_candidate = (None, 0)
if hyrule_forced and hyrule_forced[0] in multi_exits_caves: for i, cave in enumerate(multi_exits_caves):
multi_exits_caves.remove(hyrule_forced[0]) if len(cave) > cave_candidate[1]:
cave = hyrule_forced[0] cave_candidate = (i, len(cave))
hyrule_forced = None cave = multi_exits_caves.pop(cave_candidate[0])
target = lw_entrances
else:
for i, cave in enumerate(multi_exits_caves):
if len(cave) > cave_candidate[1]:
cave_candidate = (i, len(cave))
cave = multi_exits_caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
target, restriction = None, None
if any(x in avail.same_world_restricted for x in cave):
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
if len(target) < len(cave): # swap because we ran out of entrances in that world if len(target) < len(cave): # swap because we ran out of entrances in that world
if restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (dungeonsfull)')
target = lw_entrances if target is dw_entrances else dw_entrances target = lw_entrances if target is dw_entrances else dw_entrances
for ext in cave: for ext in cave:
@@ -1670,7 +1818,7 @@ def connect_swap(entrance, exit, avail):
swap_entrance = next(e for e, x in avail.combine_map.items() if x == exit) swap_entrance = next(e for e, x in avail.combine_map.items() if x == exit)
if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.world.is_tile_swapped(0x1b, avail.player): if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.world.is_tile_swapped(0x1b, avail.player):
swap_entrance = 'Inverted ' + swap_entrance swap_entrance = 'Inverted ' + swap_entrance
if entrance in entrance_map: if swap_exit in entrance_map.values():
connect_two_way(swap_entrance, swap_exit, avail) connect_two_way(swap_entrance, swap_exit, avail)
else: else:
connect_entrance(swap_entrance, swap_exit, avail) connect_entrance(swap_entrance, swap_exit, avail)
@@ -1816,6 +1964,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)'] 'Skull Woods Second Section Door (West)']
}, },
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': { 'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower'] 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -1849,13 +2003,15 @@ modes = {
'sanc_flag': 'light_world', # always light world flag 'sanc_flag': 'light_world', # always light world flag
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Agahnims Tower', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Agahnims Tower', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace',
'Ganons Tower'], 'Ganons Tower', 'Desert Palace Entrance (North)', 'Dark Death Mountain Ledge (East)'],
'connectors': [['Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (East)', 'connectors': [['Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (East)',
'Hyrule Castle Entrance (West)'], 'Hyrule Castle Entrance (West)'],
['Desert Palace Entrance (South)', 'Desert Palace Entrance (East)', ['Desert Palace Entrance (South)', 'Desert Palace Entrance (East)',
'Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'], 'Desert Palace Entrance (West)'],
['Turtle Rock', 'Turtle Rock Isolated Ledge Entrance', ['Turtle Rock', 'Turtle Rock Isolated Ledge Entrance',
'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)']] 'Dark Death Mountain Ledge (West)'],
['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)',
'Skull Woods First Section Door']]
}, },
} }
}, },
@@ -1878,7 +2034,8 @@ modes = {
'special': 'normal_drops', 'special': 'normal_drops',
'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop', 'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop',
'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree',
'Sanctuary Grave', 'Pyramid Hole'] 'Sanctuary Grave', 'Pyramid Hole',
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
}, },
'fixed_non_items': { 'fixed_non_items': {
'special': 'vanilla', 'special': 'vanilla',
@@ -1990,7 +2147,8 @@ modes = {
'special': 'normal_drops', 'special': 'normal_drops',
'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop', 'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop',
'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree',
'Sanctuary Grave', 'Pyramid Hole'] 'Sanctuary Grave', 'Pyramid Hole',
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
}, },
'fixed_non_items': { 'fixed_non_items': {
'special': 'vanilla', 'special': 'vanilla',
@@ -2081,6 +2239,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)'] 'Skull Woods Second Section Door (West)']
}, },
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': { 'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower'] 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -2156,6 +2320,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)'] 'Skull Woods Second Section Door (West)']
}, },
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': { 'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower'] 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -2408,11 +2578,14 @@ linked_drop_map = {
'Lumberjack Tree Tree': 'Lumberjack Tree Cave', 'Lumberjack Tree Tree': 'Lumberjack Tree Cave',
'Sanctuary Grave': 'Sanctuary', 'Sanctuary Grave': 'Sanctuary',
'Pyramid Hole': 'Pyramid Entrance', 'Pyramid Hole': 'Pyramid Entrance',
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance' 'Inverted Pyramid Hole': 'Inverted Pyramid Entrance',
'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door',
'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (East)',
} }
sw_linked_drop_map = { sw_linked_drop_map = {
'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (West)', 'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (East)',
'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door', 'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door',
'Skull Woods First Section Hole (West)': 'Skull Woods First Section Door', 'Skull Woods First Section Hole (West)': 'Skull Woods First Section Door',
'Skull Woods First Section Hole (East)': 'Skull Woods First Section Door' 'Skull Woods First Section Hole (East)': 'Skull Woods First Section Door'
@@ -2490,7 +2663,6 @@ entrance_map = {
'Paradox Cave (Top)': 'Paradox Cave Exit (Top)' 'Paradox Cave (Top)': 'Paradox Cave Exit (Top)'
} }
single_entrance_map = { single_entrance_map = {
'Mimic Cave': 'Mimic Cave', 'Dark Death Mountain Fairy': 'Dark Death Mountain Healer Fairy', 'Mimic Cave': 'Mimic Cave', 'Dark Death Mountain Fairy': 'Dark Death Mountain Healer Fairy',
'Dark Death Mountain Shop': 'Dark Death Mountain Shop', 'Spike Cave': 'Spike Cave', 'Dark Death Mountain Shop': 'Dark Death Mountain Shop', 'Spike Cave': 'Spike Cave',
@@ -2601,6 +2773,19 @@ Dungeon_Exit_Set = {
'Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)' 'Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)'
} }
dungeon_restriction_checks = [
(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Sanctuary Exit'], ['Sewer Drop']),
(['Desert Palace Exit (South)', 'Desert Palace Exit (East)', 'Desert Palace Exit (West)', 'Desert Palace Exit (North)'], []),
(['Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)', 'Turtle Rock Ledge Exit (East)'], []),
(['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'],
['Skull Pinball', 'Skull Left Drop', 'Skull Pot Circle', 'Skull Back Drop'])
]
doors_possible_connectors = [
'Sanctuary Exit', 'Desert Palace Exit (North)', 'Skull Woods First Section Exit',
'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'
]
# Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups # Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups
Must_Exit_Invalid_Connections = defaultdict(set) Must_Exit_Invalid_Connections = defaultdict(set)
+2
View File
@@ -173,6 +173,8 @@ def roll_settings(weights):
ret.shuffleganon = get_choice_bool('shuffleganon') ret.shuffleganon = get_choice_bool('shuffleganon')
ret.shufflelinks = get_choice_bool('shufflelinks') ret.shufflelinks = get_choice_bool('shufflelinks')
ret.shuffletavern = get_choice_bool('shuffletavern') ret.shuffletavern = get_choice_bool('shuffletavern')
ret.skullwoods = get_choice('skullwoods')
ret.linked_drops = get_choice('linked_drops')
ret.crystals_gt = get_choice('tower_open') ret.crystals_gt = get_choice('tower_open')
ret.crystals_ganon = get_choice('ganon_open') ret.crystals_ganon = get_choice('ganon_open')
+1 -1
View File
@@ -136,7 +136,7 @@ if __name__ == "__main__":
error[2] = error[2].split("\n") error[2] = error[2].split("\n")
results["errors"].append(error) results["errors"].append(error)
with open("new-test-suite-success.txt", "w") as stream: with open(os.path.join(LOGPATH, "new-test-suite-success.txt"), 'w') as stream:
stream.write(str.join("\n", successes)) stream.write(str.join("\n", successes))
results["success"] = successes results["success"] = successes
+1 -1
View File
@@ -6,7 +6,7 @@ meta:
players: 1 players: 1
settings: settings:
1: 1:
key_logic_algorithm: default key_logic_algorithm: dangerous
keysanity: True keysanity: True
crystals_needed_for_gt: 0 # to skip trash fill crystals_needed_for_gt: 0 # to skip trash fill
placements: placements: