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
# fire on
on: [
push,
pull_request
]
# on:
# push:
# branches:
# - DoorDevUnstable
# - DoorDev
# - OverworldShuffleDev
# - OverworldShuffle
# pull_request:
# branches:
# - DoorDevUnstable
# - DoorDev
# - OverworldShuffleDev
# - OverworldShuffle
on:
push:
branches:
# - DoorDevUnstable
# - DoorDev
- OverworldShuffle
pull_request:
branches:
# - DoorDevUnstable
# - DoorDev
- OverworldShuffle
# stuff to do
jobs:
@@ -43,7 +36,6 @@ jobs:
os-name: [
# ubuntu-latest, # ubuntu-22.04
ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12
windows-latest # windows-2022
]
@@ -102,7 +94,6 @@ jobs:
os-name: [
# ubuntu-latest, # ubuntu-22.04
ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12
windows-latest # windows-2022
]
@@ -178,7 +169,6 @@ jobs:
os-name: [
# ubuntu-latest, # ubuntu-22.04
ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12
windows-latest # windows-2022
]
@@ -220,7 +210,6 @@ jobs:
os-name: [
# ubuntu-latest, # ubuntu-22.04
ubuntu-22.04,
ubuntu-20.04,
macos-latest, # macos-12
windows-latest # windows-2022
]
@@ -358,8 +347,10 @@ jobs:
echo "MacOS Asset: ${{ steps.identify-macos-asset.outputs.asset_macos }}"
echo "Windows Asset: ${{ steps.identify-windows-asset.outputs.asset_windows }}"
# create a release (MASTER)
- name: 📀->🚀Create a Release (MASTER)
# create a release (OverworldShuffle)
# 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
uses: actions/create-release@v1.1.4
env:
@@ -369,11 +360,11 @@ jobs:
release_name: ${{ steps.debug_info.outputs.release_name }}
body_path: CHANGELOG.md
# 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
- name: 🔼Upload Linux Archive Asset (MASTER)
- name: 🔼Upload Linux Archive Asset (OverworldShuffle)
id: upload-linux-asset
uses: actions/upload-release-asset@v1.0.2
env:
@@ -383,11 +374,11 @@ jobs:
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_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
- name: 🔼Upload MacOS Archive Asset (MASTER)
- name: 🔼Upload MacOS Archive Asset (OverworldShuffle)
id: upload-macos-asset
uses: actions/upload-release-asset@v1.0.2
env:
@@ -397,11 +388,11 @@ jobs:
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_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
- name: 🔼Upload Windows Archive Asset (MASTER)
- name: 🔼Upload Windows Archive Asset (OverworldShuffle)
id: upload-windows-asset
uses: actions/upload-release-asset@v1.0.2
env:
@@ -411,4 +402,14 @@ jobs:
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_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())
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
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)]
@@ -670,7 +670,7 @@ class CollectionState(object):
queue = deque(self.blocked_connections[player].items())
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
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)]
@@ -2651,7 +2651,7 @@ class Location(object):
self.recursion_count = 0
self.staleness_count = 0
self.locked = False
self.real = not prize
self.real = True
self.always_allow = lambda item, state: False
self.access_rule = lambda state: True
self.verbose_rule = None
@@ -2789,7 +2789,7 @@ class Item(object):
def explore_region(region):
explored_regions.append(region.name)
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]:
return ent
elif ent.parent_region.name not in explored_regions:
@@ -2989,6 +2989,8 @@ class Spoiler(object):
'shuffleganon': self.world.shuffle_ganon,
'shufflelinks': self.world.shufflelinks,
'shuffletavern': self.world.shuffletavern,
'skullwoods': self.world.skullwoods,
'linked_drops': self.world.linked_drops,
'take_any': self.world.take_any,
'overworld_map': self.world.overworld_map,
'door_shuffle': self.world.doorShuffle,
@@ -3242,6 +3244,9 @@ class Spoiler(object):
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 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('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])
@@ -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)
overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
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
@@ -3698,6 +3707,8 @@ class Settings(object):
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4),
])
return base64.b64encode(code, "+-".encode()).decode()
@@ -3705,7 +3716,7 @@ class Settings(object):
def adjust_args_from_code(code, player, args):
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')
if settings[10] != settings_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.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):
WorstCase = 0
+10
View File
@@ -1,5 +1,15 @@
# 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
- Fixed HP refill interrupting boss cutscenes
- 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',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
'skullwoods', 'linked_drops',
'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor',
@@ -203,6 +204,8 @@ def parse_settings():
"shuffle": "vanilla",
"shufflelinks": False,
"shuffletavern": True,
'skullwoods': 'original',
'linked_drops': 'unset',
"overworld_map": "default",
"take_any": "none",
"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:
dungeon.small_keys = []
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():
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)
if sector.is_entrance_sector() and not sector.destination_entrance:
need_switch = True
for region in sector.get_start_regions():
if region.crystal_switch:
need_switch = False
break
if sector.c_switch: # this relies on the fact that Mire Fishbone SE cannot be a portal
need_switch = False
any_benefit = False
for eq in sector.equations:
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.benefit[hook_from_door(door)].append(door)
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 ???
# if door.name in ['PoD Dark Alley']:
eq.entrance_flag = True
+1 -1
View File
@@ -95,7 +95,7 @@ def fill_dungeons_restrictive(world, shuffled_locations):
continue
break
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)
fill(all_state_base, others, shuffled_locations)
+10 -2
View File
@@ -69,6 +69,7 @@ class InitialSram:
starting_arrow_cap_upgrades = 30
starting_bombs = 0
starting_arrows = 0
starting_magic = 0
startingstate = CollectionState(world)
@@ -108,10 +109,10 @@ class InitialSram:
if startingstate.has('Magic Upgrade (1/4)', player):
equip[0x37B] = 2
equip[0x36E] = 0x80
starting_magic = 0x80
elif startingstate.has('Magic Upgrade (1/2)', player):
equip[0x37B] = 1
equip[0x36E] = 0x80
starting_magic = 0x80
if startingstate.has('Return Old Man', player):
self._initial_sram_bytes[0x410] |= 0x01
@@ -176,6 +177,7 @@ class InitialSram:
arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10}
bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10}
arrows = {'Single Arrow': 1, 'Arrows (10)': 10}
magic = {'Big Magic': 0x80, 'Small Magic': 0x10}
if item.name in set_table:
equip[set_table[item.name][0]] = set_table[item.name][1]
@@ -212,13 +214,19 @@ class InitialSram:
if item.name != 'Piece of Heart' or equip[0x36B] == 0:
equip[0x36C] = min(equip[0x36C] + 0x08, 0xA0)
equip[0x36D] = min(equip[0x36D] + 0x08, 0xA0)
elif item.name in magic:
starting_magic += magic[item.name]
else:
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[0x371] = min(starting_arrow_cap_upgrades, 70)
equip[0x343] = min(starting_bombs, equip[0x370])
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
assert equip[:0x340] == [0] * 0x340
+23 -1
View File
@@ -11,7 +11,7 @@ from Tables import bonk_prize_lookup
from Items import ItemFactory
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
import source.classes.constants as CONST
@@ -546,6 +546,27 @@ def set_up_take_anys(world, player, skip_adjustments=False):
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):
for shop in world.shops[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)
if not bow_found:
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:
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)
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'),
'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 (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'),
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, '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, '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'),
'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'),
'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'),
'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 (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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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 (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 (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 (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 (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 (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 (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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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 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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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 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 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 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 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 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 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'),
'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'),
'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'),
'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 (+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'),
'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'),
'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'),
'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'),
'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'),
'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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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'),
'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 (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'),
'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 (+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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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 (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 (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 (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 (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'),
'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'),
'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'),
'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'),
'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'),
'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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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, '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'),
'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/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
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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'),
'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, '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 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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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 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, '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'),
'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'),
+3 -3
View File
@@ -2137,11 +2137,11 @@ def validate_key_placement(key_layout, world, player):
if key_layout.prize_relevant:
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:
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':
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':
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:
found_prize = False
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.rom.DataTables import init_data_tables
version_number = '1.4.1.12'
version_number = '1.4.5'
version_branch = '-u'
__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:
logger.info(world.fish.translate("cli", "cli", "create.meta"))
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'))
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:
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'))
elif world.spoiler_mode != 'none' and not args.jsonout:
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.shufflelinks = args.shufflelinks.copy()
world.shuffletavern = args.shuffletavern.copy()
world.skullwoods = args.skullwoods.copy()
world.linked_drops = args.linked_drops.copy()
world.pseudoboots = args.pseudoboots.copy()
world.overworld_map = args.overworld_map.copy()
world.take_any = args.take_any.copy()
@@ -522,7 +524,18 @@ def set_starting_inventory(world, args):
if inv_list:
for inv_item in inv_list:
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)
if item:
world.push_precollected(item)
+2 -2
View File
@@ -344,7 +344,7 @@ location_table_boss = {'Eastern Palace - Prize': 0x2000,
'Desert Palace - Prize': 0x1000,
'Tower of Hera - Prize': 0x0020,
'Palace of Darkness - Prize': 0x0200,
'Thieves Town - Prize': 0x0010,
'Thieves\' Town - Prize': 0x0010,
'Skull Woods - Prize': 0x0080,
'Swamp Palace - Prize': 0x0400,
'Ice Palace - Prize': 0x0040,
@@ -1097,7 +1097,7 @@ async def game_watcher(ctx : Context):
async def main():
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('--password', default=None, help='Password of the multiworld host.')
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 Utils import bidict
version_number = '0.5.0.1'
version_number = '0.5.0.2'
# branch indicator is intentionally different across branches
version_branch = ''
@@ -1151,7 +1151,7 @@ def define_tile_groups(world, do_grouped, player):
merge_groups([[0x13, 0x14, 0x1b]])
# 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]])
# 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 \
and (exit.connected_region.type == region.type
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)
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)
rom.write_to_file('%s.sfc' % outfilebase)
if args.create_spoiler:
if args.spoiler != 'none':
world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
logger.info('Done. Enjoy.')
@@ -178,7 +178,7 @@ def prefill_world(world, plando, text_patches):
world.fix_trock_exit = {1: trfstr.strip().lower() == 'true'}
elif line.startswith('!fix_gtower_exit'):
_, 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'):
_, podestr = line.split(':', 1)
world.fix_palaceofdarkness_exit = {1: podestr.strip().lower() == 'true'}
@@ -223,7 +223,7 @@ def prefill_world(world, plando, text_patches):
def start():
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('--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.')
+6 -5
View File
@@ -1240,11 +1240,12 @@ def adjust_locations(world, player):
index += 1
setup_enemy_locations(world, player)
# disable forced prize locations
if world.prizeshuffle[player] != 'none':
for l in [name for name, data in location_table.items() if data[2]]:
location = world.get_location_unsafe(l, player)
if location:
location.prize = False
prize_on_boss = world.prizeshuffle[player] == 'none'
for l in [name for name, data in location_table.items() if data[2]]:
location = world.get_location_unsafe(l, player)
if location:
location.prize = prize_on_boss
location.real = not prize_on_boss
# unreal events:
for l in ['Ganon', 'Zelda Pickup', 'Zelda Drop Off'] + list(location_events):
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'
RANDOMIZERBASEHASH = '87be9d9bd56b6ad8e4b9697ecfc31841'
RANDOMIZERBASEHASH = 'c0c4b8166fbe9b637a0aaa82e3f4cb8d'
class JsonRom(object):
@@ -492,6 +492,9 @@ def patch_rom(world, rom, player, team, is_mystery=False):
if world.mapshuffle[player]:
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
swamp1location = world.get_location('Swamp Palace - Trench 1 Pot Key', player)
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)
elif room_id == 0x00d6 and world.fix_trock_exit[player]:
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)
else:
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+0x1f, thousands_bot)
# modify stat config
stat_address = 0x23978C
stat_address = 0x23983E
stat_pc = snes_to_pc(stat_address)
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)
@@ -2704,6 +2707,8 @@ def patch_shuffled_dark_sanc(world, rom, 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)
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]
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_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]
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]
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 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 2 NE', player), lambda state: state.has_sword(player) or
(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('Ice Rod', player) and state.can_use_bombs(player)) or # freeze popo and throw, bomb to finish
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
# Note: new enemy logic doesn't account for Fire Rod + Bombs or Ice Rod + Bombs yet
# set_rule(world.get_entrance('Mire 2 NE', player), lambda state: state.has_sword(player) or
# (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('Ice Rod', player) and state.can_use_bombs(player)) or # freeze popo and throw, bomb to finish
# 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))
loc = world.get_location('Misery Mire - Spikes Pot Key', player)
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 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
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))
@@ -1531,6 +1528,10 @@ std_kill_rooms = {
(['GT Petting Zoo SE'], [], 0x7d, [4, 5, 6, 7, 8, 10]),
'GT DMs Room': # Four red stalfos
(['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 WN'], [], 0x5d, [3, 4, 5, 6]),
'GT Gauntlet 2': # Red stalfos
@@ -2017,7 +2018,7 @@ def add_key_logic_rules(world, player):
eval_func = eval_small_key_door
if world.key_logic_algorithm[player] == 'strict' and world.keyshuffle[player] == 'wild':
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
for d_name, d_logic in key_logic.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'],
['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():
taskcommand = basecommand + " " + command + mode[1]
+1 -1
View File
@@ -25,7 +25,7 @@ def main(args=None):
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():
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['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_bow'] = CompressedTextMapper.convert("You're in bow mode now!")
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_magic_cape'] = CompressedTextMapper.convert("Finally! we get to play Invisble Man!")
text['item_get_powder'] = CompressedTextMapper.convert("It's the powder. Let's cause some mischief!")
text['item_get_flippers'] = CompressedTextMapper.convert("Splish! Splash! Let's go take a bath!")
text['item_get_power_gloves'] = CompressedTextMapper.convert("Feel the power! You can now lift light rocks! Rock on!")
text['item_get_bow'] = CompressedTextMapper.convert("Bow! Join the archer class!")
text['item_get_shovel'] = CompressedTextMapper.convert("Shovel! Can you dig it?")
text['item_get_magic_cape'] = CompressedTextMapper.convert("Cape! Invisbility cloak activate!")
text['item_get_powder'] = CompressedTextMapper.convert("Powder! Sprinkle it on a dancing pickle!")
text['item_get_flippers'] = CompressedTextMapper.convert("Flippers! Time to swim!")
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_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_mushroom'] = CompressedTextMapper.convert("A Mushroom! Don't eat it. Find a witch.")
text['item_get_book'] = CompressedTextMapper.convert("It book! U R now litterit!")
text['item_get_moonpearl'] = CompressedTextMapper.convert("I found a shiny marble! No more hops!")
text['item_get_mushroom'] = CompressedTextMapper.convert("Mushroom! Don't eat it. Find a witch.")
text['item_get_book'] = CompressedTextMapper.convert("Book! Are you well read?")
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.")
# 60
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_fire_rod'] = CompressedTextMapper.convert("A Rod that shoots fire? Let's burn all the things!")
text['item_get_ether'] = CompressedTextMapper.convert("We can chill out with this!")
text['item_get_bombos'] = CompressedTextMapper.convert("Let's set everything on fire, and melt things!")
text['item_get_quake'] = CompressedTextMapper.convert("Time to make the earth shake, rattle, and roll!")
text['item_get_ice_rod'] = CompressedTextMapper.convert("Ice Rod! Time to chill out!")
text['item_get_fire_rod'] = CompressedTextMapper.convert("Fire Rod! I'm burning for you!")
text['item_get_ether'] = CompressedTextMapper.convert("Ether! Let's cool things down!")
text['item_get_bombos'] = CompressedTextMapper.convert("Bombos! Explosions, fire, burn it all!")
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_ocarina'] = CompressedTextMapper.convert("Finally! We can play the Song of Time!")
text['item_get_cane_of_somaria'] = CompressedTextMapper.convert("Make blocks!\nThrow blocks!\nsplode Blocks!")
text['item_get_hookshot'] = CompressedTextMapper.convert("BOING!!!\nBOING!!!\nSay no more…")
text['item_get_ocarina'] = CompressedTextMapper.convert("Ocarina! A Flute by another name")
text['item_get_cane_of_somaria'] = CompressedTextMapper.convert("Somaria! Make blocks, throw blocks")
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_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_titans_mitts'] = CompressedTextMapper.convert("So, like, you can now lift anything.\nANYTHING!")
text['item_get_magic_mirror'] = CompressedTextMapper.convert("We could stare at this all day or, you know, beat Ganon…")
text['item_get_titans_mitts'] = CompressedTextMapper.convert("Mitts! Lift ALL the rocks!")
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!")
# 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['item_get_red_potion'] = CompressedTextMapper.convert("Red goo to go! Nice!")
text['item_get_green_potion'] = CompressedTextMapper.convert("Green goo to go! Nice!")
text['item_get_blue_potion'] = CompressedTextMapper.convert("Blue goo to go! Nice!")
text['item_get_bug_net'] = CompressedTextMapper.convert("Surprise Net! Let's catch stuff!")
text['item_get_blue_mail'] = CompressedTextMapper.convert("Blue threads? Less damage activated!")
text['item_get_red_mail'] = CompressedTextMapper.convert("You feel the power of the eggplant on your head.")
text['item_get_temperedsword'] = CompressedTextMapper.convert("Nice… I now have a craving for Cheetos.")
text['item_get_mirror_shield'] = CompressedTextMapper.convert("Pit would be proud!")
text['item_get_cane_of_byrna'] = CompressedTextMapper.convert("It's the Blue Cane. You can now protect yourself with lag!")
text['item_get_red_potion'] = CompressedTextMapper.convert("Red Potion! Heal yourself")
text['item_get_green_potion'] = CompressedTextMapper.convert("Green Potion! Magic refill!")
text['item_get_blue_potion'] = CompressedTextMapper.convert("Blue Potion! Heal and restore!")
text['item_get_bug_net'] = CompressedTextMapper.convert("Bug Net! Let's catch stuff!")
text['item_get_blue_mail'] = CompressedTextMapper.convert("Blue Mail! Less damage activated!")
text['item_get_red_mail'] = CompressedTextMapper.convert("Red Mail! Even less damage!")
text['item_get_temperedsword'] = CompressedTextMapper.convert("Tempered Sword! Even more slashy!")
text['item_get_mirror_shield'] = CompressedTextMapper.convert("Mirror Shield! Time to reflect")
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_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_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.")
Binary file not shown.
+2 -2
View File
@@ -47,8 +47,8 @@
boss: 0
oneway: 0
key_logic_algorithm:
default: 1
partial: 0
dangerous: 0
partial: 1
strict: 0
decoupledoors: off
door_self_loops:
+10 -1
View File
@@ -25,7 +25,7 @@ trap_door_mode:
boss: 1
oneway: 1
key_logic_algorithm:
default: 1
dangerous: 1
partial: 0
strict: 0
decoupledoors:
@@ -71,6 +71,15 @@ shufflelinks:
shuffletavern:
on: 1
off: 1
skullwoods:
original: 1
restricted: 1
loose: 1
followlinked: 1
linked_drops:
unset: 1
linked: 1
independent: 1
world_state:
standard: 1
open: 1
+16 -1
View File
@@ -254,7 +254,7 @@
},
"key_logic_algorithm": {
"choices": [
"default",
"dangerous",
"partial",
"strict"
]
@@ -516,6 +516,21 @@
"action": "store_true",
"type": "bool"
},
"skullwoods": {
"choices": [
"original",
"restricted",
"loose",
"followlinked"
]
},
"linked_drops": {
"choices": [
"unset",
"linked",
"independent"
]
},
"overworld_map": {
"choices": [
"default",
+15 -1
View File
@@ -305,7 +305,7 @@
],
"key_logic_algorithm": [
"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",
"strict: Ensure small keys are available"
],
@@ -431,6 +431,20 @@
"shuffletavern": [
"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": [
"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.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.strict": "Strict",
@@ -195,6 +195,17 @@
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
"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.quickswap": "L/R Quickswapping",
"randomizer.gameoptions.reduce_flashing": "Reduce Flashing",
@@ -14,11 +14,11 @@
},
"key_logic_algorithm": {
"type": "selectbox",
"default": "default",
"default": "partial",
"options": [
"default",
"partial",
"strict"
"strict",
"dangerous"
],
"config": {
"padx": [20,0],
@@ -35,6 +35,26 @@
"padx": [20,0]
}
},
"skullwoods": {
"type": "selectbox",
"options": [
"original",
"restricted",
"loose",
"followlinked"
],
"config": {
"width": 30
}
},
"linked_drops": {
"type": "selectbox",
"options": [
"unset",
"linked",
"independent"
]
},
"openpyramid": {
"type": "selectbox",
"options": [
+1 -1
View File
@@ -19,7 +19,7 @@ global FILESIZE_CHECK
# windows: 2022, 2019
# macos: 14, 13, 12, 11
DEFAULT_EVENT = "event"
DEFAULT_REPO_SLUG = "miketrethewey/ALttPDoorRandomizer"
DEFAULT_REPO_SLUG = "codemann8/ALttPDoorRandomizer"
FILENAME_CHECKS = [
"DungeonRandomizer",
"Gui",
+4 -4
View File
@@ -71,7 +71,7 @@ if len(BUILD_FILENAMES) > 0:
# clean the git slate
git_clean()
# mv dirs from source code
# mv dirs from source code
dirs = [
os.path.join(".",".git"),
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"]:
os.chmod(os.path.join(".",BUILD_FILENAME),0o755)
# .zip if windows
# .tar.gz otherwise
# .zip if windows
# .tar.gz otherwise
if len(BUILD_FILENAMES) > 1:
# ZIP_FILENAME = os.path.join("..","deploy",env["REPO_NAME"])
ZIP_FILENAME = os.path.join("..","deploy","ALttPOverworldRandomizer")
@@ -112,7 +112,7 @@ if len(BUILD_FILENAMES) > 0:
make_archive(ZIP_FILENAME,"gztar")
ZIP_FILENAME += ".tar.gz"
# mv dirs back
# mv dirs back
for dir in dirs:
if os.path.isdir(os.path.join("..","build",dir)):
move(
+4
View File
@@ -160,6 +160,8 @@ class CustomSettings(object):
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[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.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[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]['shufflelinks'] = world.shufflelinks[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]['pseudoboots'] = world.pseudoboots[p]
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
+2
View File
@@ -103,6 +103,8 @@ SETTINGSTOPROCESS = {
"shuffleganon": "shuffleganon",
"shufflelinks": "shufflelinks",
"shuffletavern": "shuffletavern",
"skullwoods": "skullwoods",
"linked_drops": "linked_drops",
"openpyramid": "openpyramid",
"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)
optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources))
elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160:
flag = min(vln[damage_type] for vln in vln_list.values())
flag = flag if flag < 0 else (hits + used_resources['Magic'])
flag = hits + used_resources['Magic']
optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources))
elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160:
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, 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"
- [ 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, 4, ["RollerVerticalDown", "RollerVerticalUp"]] # too large, hits the door
- [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2"
- [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3"
- [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
@@ -54,10 +55,12 @@ UwGeneralDeny:
- [0x0028, 2, ["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"
- [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1"
- [ 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, 1, ["RollerVerticalUp", "RollerVerticalDown"]]
- [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, 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"
- [ 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"
@@ -80,6 +83,7 @@ UwGeneralDeny:
- [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"
- [ 0x003b, 1, [ "Bumper" ]]
- [ 0x003b, 4, ["RollerVerticalUp", "RollerVerticalDown"]]
- [ 0x003c, 0, ["BigSpike"]]
- [ 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"
@@ -118,7 +122,7 @@ UwGeneralDeny:
- [ 0x0049, 5, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 2"
- [ 0x0049, 7, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 4"
- [ 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, 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"
@@ -129,6 +133,7 @@ UwGeneralDeny:
- [ 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, 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, 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"
@@ -151,7 +156,7 @@ UwGeneralDeny:
- [ 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"
- [ 0x0058, 0, ["Statue"]]
- [ 0x0058, 1, ["Statue"]]
- [0x0058, 1, ["Statue", "RollerHorizontalRight", "RollerHorizontalLeft"]]
- [ 0x0058, 2, ["Statue"]]
- [ 0x0058, 3, ["Statue"]]
- [ 0x0058, 4, ["Statue"]]
@@ -205,7 +210,7 @@ UwGeneralDeny:
- [ 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, 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"
- [ 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"
@@ -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, 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"
- [0x009d, 2, ["AntiFairyCircle"]]
- [ 0x009d, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Compass Room - Gibdo 2"
- [ 0x009d, 6, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - Compass Room - Blue Bari 1"
- [ 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"
- [ 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"
- [ 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, 8, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
- [ 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, 10, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ]
- [ 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, 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"
@@ -389,7 +396,7 @@ UwGeneralDeny:
- [ 0x00e7, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 6"
- [ 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, 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, 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"
@@ -402,7 +409,7 @@ UwGeneralDeny:
- [ 0x00f1, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 4"
- [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5"
- [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6"
- [ 0x00fd, 0, [ "Bumper" ] ]
- [0x00fd, 0, ["Bumper", "AntiFairyCircle"]]
- [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]]
- [0x010b, 6, ["RollerHorizontalRight"]]
@@ -449,6 +456,8 @@ OwGeneralDeny:
- [0x5e, 18, ["Gibo"]] # kiki eating Gibo
- [0x5e, 19, ["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
- [0x7f, 1, ["Bumper"]] # soft-lock potential near ladder
UwEnemyDrop:
+283 -98
View File
@@ -26,6 +26,7 @@ class EntrancePool(object):
self.decoupled_exits = []
self.original_entrances = set()
self.original_exits = set()
self.same_world_restricted = {}
self.world = world
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.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.assumed_loose_caves = world.shuffle[player] == 'district'
inverted_substitution(avail_pool, avail_pool.entrances, True, True)
inverted_substitution(avail_pool, avail_pool.exits, False, True)
avail_pool.original_entrances.update(avail_pool.entrances)
@@ -99,8 +99,13 @@ def link_entrances_new(world, player):
if mode not in modes:
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
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.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
if avail_pool.is_standard():
do_standard_connections(avail_pool)
@@ -108,11 +113,7 @@ def link_entrances_new(world, player):
for pool_name, pool in pool_list.items():
special_shuffle = pool['special'] if 'special' in pool else None
if special_shuffle == 'drops':
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances'])
if avail_pool.swapped:
connect_swapped(holes, targets, avail_pool)
else:
connect_random(holes, targets, avail_pool)
handle_skull_woods_drops(avail_pool, pool['entrances'], mode_cfg)
elif special_shuffle == 'normal_drops':
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)
@@ -143,20 +144,7 @@ def link_entrances_new(world, player):
entrances, exits = find_entrances_and_exits(avail_pool, entrances+drops)
do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
elif special_shuffle == 'skull':
entrances, exits = find_entrances_and_exits(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
handle_skull_woods_entrances(avail_pool, pool['entrances'])
else:
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
@@ -253,12 +241,21 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
# mandatory exits
rem_entrances, rem_exits = set(), set()
if not cross_world:
determine_dungeon_restrictions(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
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:
do_mandatory_connections(avail, lw_entrances, multi_exit_caves, must_exit_lw)
do_world_mandatory(lw_entrances, must_exit_lw, 'LightWorld')
else:
do_mandatory_connections(avail, dw_entrances, multi_exit_caves, must_exit_dw)
do_world_mandatory(dw_entrances, must_exit_dw, 'DarkWorld')
new_mec = []
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
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:
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(dw_entrances)
else:
@@ -367,6 +364,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
if bonk_fairy_exception(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)
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(dw_entrances)
else:
@@ -519,12 +520,16 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
if not avail.keep_drops_together:
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(exits, targets)
return # we're done here
hole_entrances, hole_targets = [], []
leftover_hole_entrances, leftover_hole_targets = [], []
for hole in drop_map:
if hole in avail.original_entrances and hole in linked_drop_map:
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]
if target_exit in exits and target_drop in exits:
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)
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle:
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
if hc.connected_region:
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
chosen_entrance = None
if is_hc_in_dw:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary')
if not cross_world:
if 'Sanctuary Grave' in holes_to_shuffle:
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
is_hc_in_opp_world = avail.inverted
if hc.connected_region:
opp_world = RegionType.LightWorld if avail.inverted else RegionType.DarkWorld
is_hc_in_opp_world = hc.connected_region.type == opp_world
start_world_entrances = DW_Entrances if avail.inverted else LW_Entrances
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:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
if not chosen_entrance:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances)
if chosen_entrance:
hole_entrances.remove(chosen_entrance)
sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit')
hole_targets.remove(sanc_interior)
connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit
connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
remove_from_list(exits, [sanc_interior[0], sanc_interior[1]])
if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]:
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])
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in start_world_entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in start_world_entrances)
if chosen_entrance:
connect_hole_via_interior(chosen_entrance, 'Sanctuary Exit', hole_entrances, hole_targets, entrances, exits, avail)
sw_world_entrances = DW_Entrances if not avail.world.is_tile_swapped(0x00, avail.player) else LW_Entrances
if 'Skull Woods First Section Hole (North)' in holes_to_shuffle:
chosen_entrance = next(e for e in hole_entrances if e[0] in sw_world_entrances)
connect_hole_via_interior(chosen_entrance, 'Skull Woods First Section Exit', hole_entrances, hole_targets, entrances, exits, avail)
if 'Skull Woods Second Section Hole' in holes_to_shuffle:
chosen_entrance = next(e for e in hole_entrances if e[0] in sw_world_entrances)
connect_hole_via_interior(chosen_entrance, 'Skull Woods Second Section Exit (East)', hole_entrances, hole_targets, entrances, exits, avail)
random.shuffle(hole_targets)
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(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):
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
def figure_out_connectors(exits, avail):
def figure_out_connectors(exits, avail, cross_world=True):
multi_exit_caves = []
cave_list = list(Connector_List)
if avail.assumed_loose_caves:
sw_list = ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']
random.shuffle(sw_list)
cave_list.extend([sw_list])
cave_list.extend([[entrance_map[e]] for e in linked_drop_map.values() if 'Inverted ' not in e])
if avail.assumed_loose_caves or (not avail.skull_handled and (cross_world or not avail.world.is_tile_swapped(0x00, avail.player))):
skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
cave_list.extend(skull_connector)
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 and 'Skull Woods ' not in e])
for item in cave_list:
if all(x in exits for x in item):
remove_from_list(exits, item)
@@ -1055,12 +1091,49 @@ def must_exits_helper(avail):
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):
lw_entrances, dw_entrances = [], []
for x in entrances:
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_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
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):
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 = 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(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)
for i, cave in enumerate(caves):
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 = caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
if isinstance(cave, str):
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
if len(target) < len(cave):
if restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (main)')
# need to use other set
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)
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):
random.shuffle(entrances)
random.shuffle(caves)
@@ -1158,6 +1268,44 @@ def do_cross_world_connectors(entrances, caves, avail):
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):
max_size = 0
options = {}
@@ -1214,7 +1362,6 @@ def do_same_world_shuffle(avail, pool_def):
multi_exit = pool_def['connectors']
# complete_entrance_set = set()
lw_entrances, dw_entrances, multi_exits_caves, other_exits = [], [], [], []
hyrule_forced = None
single_entrances, single_exits = find_entrances_and_exits(avail, single_exit)
other_exits.extend(single_exits)
@@ -1224,11 +1371,8 @@ def do_same_world_shuffle(avail, pool_def):
for option in multi_exit:
multi_entrances, multi_exits = find_entrances_and_exits(avail, option)
# 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)',
'Hyrule Castle Entrance (East)',
'Hyrule Castle Entrance (West)']):
hyrule_forced = [multi_exits]
multi_exits_caves.append(multi_exits)
if multi_exits:
multi_exits_caves.append(multi_exits)
for x in multi_entrances:
(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_dw = must_exit_filter(avail, must_exit_dw, dw_entrances)
do_mandatory_connections(avail, lw_entrances, multi_exits_caves, must_exit_lw)
if hyrule_forced and hyrule_forced[0] in multi_exits_caves:
remove_from_list(multi_exits_caves, hyrule_forced)
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw)
multi_exits_caves.append(hyrule_forced[0])
else:
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw)
def do_world_mandatory(world_entrances, world_must_exits, restriction):
nonlocal multi_exits_caves
candidates = filter_restricted_caves(multi_exits_caves, restriction, avail)
other_candidates = [x for x in multi_exits_caves if x not in candidates] # remember those not passed in
do_mandatory_connections(avail, world_entrances, candidates, world_must_exits)
multi_exits_caves = (other_candidates + candidates) if other_candidates else candidates # rebuild list from the candidates and those not passed
determine_dungeon_restrictions(avail)
do_world_mandatory(lw_entrances, must_exit_lw, 'LightWorld')
do_world_mandatory(dw_entrances, must_exit_dw, 'DarkWorld')
# connect caves
random.shuffle(lw_entrances)
@@ -1250,19 +1397,20 @@ def do_same_world_shuffle(avail, pool_def):
random.shuffle(multi_exits_caves)
while multi_exits_caves:
cave_candidate = (None, 0)
if hyrule_forced and hyrule_forced[0] in multi_exits_caves:
multi_exits_caves.remove(hyrule_forced[0])
cave = hyrule_forced[0]
hyrule_forced = None
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
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, 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 restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (dungeonsfull)')
target = lw_entrances if target is dw_entrances else dw_entrances
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)
if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.world.is_tile_swapped(0x1b, avail.player):
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)
else:
connect_entrance(swap_entrance, swap_exit, avail)
@@ -1816,6 +1964,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'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': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -1849,13 +2003,15 @@ modes = {
'sanc_flag': 'light_world', # always light world flag
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'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)',
'Hyrule Castle Entrance (West)'],
['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',
'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',
'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop',
'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': {
'special': 'vanilla',
@@ -1990,7 +2147,8 @@ modes = {
'special': 'normal_drops',
'entrances': ['Hyrule Castle Secret Entrance Drop', 'Kakariko Well Drop', 'Bat Cave Drop',
'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': {
'special': 'vanilla',
@@ -2081,6 +2239,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'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': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'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)',
'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': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -2408,11 +2578,14 @@ linked_drop_map = {
'Lumberjack Tree Tree': 'Lumberjack Tree Cave',
'Sanctuary Grave': 'Sanctuary',
'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 = {
'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 (West)': '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)'
}
single_entrance_map = {
'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',
@@ -2601,6 +2773,19 @@ Dungeon_Exit_Set = {
'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
Must_Exit_Invalid_Connections = defaultdict(set)
+2
View File
@@ -173,6 +173,8 @@ def roll_settings(weights):
ret.shuffleganon = get_choice_bool('shuffleganon')
ret.shufflelinks = get_choice_bool('shufflelinks')
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_ganon = get_choice('ganon_open')
+1 -1
View File
@@ -136,7 +136,7 @@ if __name__ == "__main__":
error[2] = error[2].split("\n")
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))
results["success"] = successes
+1 -1
View File
@@ -6,7 +6,7 @@ meta:
players: 1
settings:
1:
key_logic_algorithm: default
key_logic_algorithm: dangerous
keysanity: True
crystals_needed_for_gt: 0 # to skip trash fill
placements: