 51c38fc628
			
		
	
	51c38fc628
	
	
	
		
			
			* first commit (not including OoT data files yet) * added some basic options * rule parser works now at least * make sure to commit everything this time * temporary change to BaseClasses for oot * overworld location graph builds mostly correctly * adding oot data files * commenting out world options until later since they only existed to make the RuleParser work * conversion functions between AP ids and OOT ids * world graph outputs * set scrub prices * itempool generates, entrances connected, way too many options added * fixed set_rules and set_shop_rules * temp baseclasses changes * Reaches the fill step now, old event-based system retained in case the new way breaks * Song placements and misc fixes everywhere * temporary changes to make oot work * changed root exits for AP fill framework * prevent infinite recursion due to OoT sharing usage of the address field * age reachability works hopefully, songs are broken again * working spoiler log generation on beatable-only * Logic tricks implemented * need this for logic tricks * fixed map/compass being placed on Serenade location * kill unreachable events before filling the world * add a bunch of utility functions to prepare for rom patching * move OptionList into generic options * fixed some silly bugs with OptionList * properly seed all random behavior (so far) * ROM generation working * fix hints trying to get alttp dungeon hint texts * continue fixing hints * add oot to network data package * change item and location IDs to 66000 and 67000 range respectively * push removed items to precollected items * fixed various issues with cross-contamination with multiple world generation * reenable glitched logic (hopefully) * glitched world files age-check fix * cleaned up some get_locations calls * added token shuffle and scrub shuffle, modified some options slightly to make the parsing work * reenable MQ dungeons * fix forest mq exception * made targeting style an option for now, will be cosmetic later * reminder to move targeting to cosmetics * some oot option maintenance * enabled starting time of day * fixed issue breaking shop slots in multiworld generation * added "off" option for text shuffle and hints * shopsanity functionality restored * change patch file extension * remove unnecessary utility functions + imports * update MIT license * change option to "patch_uncompressed_rom" instead of "compress_rom" * compliance with new AutoWorld systems * Kill only internal events, remove non-internal big poe event in code * re-add the big poe event and handle it correctly * remove extra method in Range option * fix typo * Starting items, starting with consumables option * do not remove nonexistent item * move set_shop_rules to after shop items are placed * some cleanup * add retries for song placement * flagged Skull Mask and Mask of Truth as advancement items * update OoT to use LogicMixin * Fixed trying to assign starting items from the wrong players * fixed song retry step * improved option handling, comments, and starting item replacements * DefaultOnToggle writes Yes or No to spoiler * enable compression of output if Compress executable is present * clean up compression * check whether (de)compressor exists before running the process * allow specification of rom path in host.yaml * check if decompressed file already exists before decompressing again * fix triforce hunt generation * rename all the oot state functions with prefix * OoT: mark triforce pieces as completion goal for triforce hunt * added overworld and any-dungeon shuffle for dungeon items * Hide most unshuffled locations and events from the list of locations in spoiler * build oot option ranges with a generic function instead of defining each separately * move oot output-type control to host.yaml instead of individual yamls * implement dungeon song shuffle * minor improvements to overworld dungeon item shuffle * remove random ice trap names in shops, mostly to avoid maintaining a massive censor list * always output patch file to folder, remove option to generate ROM in preparation for removal * re-add the fix for infinite recursion due to not being light or dark world * change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently * oot: remove item_names and location_names * oot: minor fixes * oot: comment out ROM patching * oot: only add CollectionState objects on creation if actually needed * main entrance shuffle method and entrances-based rules * fix entrances based rules * disable master quest and big poe count options for client compatibility * use get_player_name instead of get_player_names * fix OptionList * fix oot options for new option system * new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES * fill AP player name in oot rom with 0 instead of 0xDF * encode player name with ASCII for fixed-width * revert oot player name array to 8 bytes per name * remove Pierre location if fast scarecrow is on * check player name length * "free_scarecrow" not "fast_scarecrow" * OoT locations now properly store the AP ID instead of the oot internal ID * oot __version__ updates in lockstep with AP version * pull in unmodified oot cosmetic files * also grab JSONDump since it's needed apparently * gather extra needed methods, modify imports * delete cosmetics log, replace all instances of SettingsList with OOTWorld * cosmetic options working, except for sound effects (due to ear-safe issues) * SFX, Music, and Fanfare randomization reenabled * move OoT data files into the worlds folder * move Compress and Decompress into oot data folder * Replace get_all_state with custom method to avoid the cache * OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues * set data_version to 0 * make Kokiri Sword shuffle off by default * reenable "Random Choice" for various cosmetic options * kill Ruto's Letter turnin if open fountain also fix for shopsanity * place Buy Goron/Zora Tunic first in shop shuffle * make ice traps appear as other items instead of breaking generation * managed to break ice traps on non-major-only * only handle ice traps if they are on * fix shopsanity for non-oot games, and write player name instead of player number * light arrows hint uses player name instead of player number * Reenable "skip child zelda" option * fix entrances_based_rules * fix ganondorf hint if starting with light arrows * fix dungeonitem shuffle and shopsanity interaction * remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group * force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any * keep bosses and bombchu bowling chus out of data package * revert workaround for infinite recursion and fix it properly * fix shared shop id caches during patching process * fix shop text box overflows, as much as possible * add default oot host.yaml option * add .apz5, .n64, .z64 to gitignore * Properly document and name all (functioning) OOT options * clean up some imports * remove unnecessary files from oot's data * fix typo in gitignore * readd the Compress and Decompress utilities, since they are needed for generation * cleanup of imports and some minor optimizations * increase shop offset for item IDs to 0xCB * remove shop item AP ids entirely * prevent triforce pieces for other players from being received by yourself * add "excluded" property to Location * Hint system adapted and reenabled; hints still unseeded * make hints deterministic with lists instead of sets * do not allow hints to point to Light Arrows on non-vanilla bridge * foreign locations hint as their full name in OoT rather than their region * checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated * consolidate versioning in Utils * ice traps appear as major items rather than any progression item * set prescription and claim check as defaults for adult trade item settings * add oot options to playerSettings * allow case-insensitive logic tricks in yaml * fix oot shopsanity option formatting * Write OoT override info even if local item, enabling local checks to show up immediately in the client * implement CollectionState.can_live_dmg for oot glitched logic * filter item names for invalid characters when patching shops * make ice traps appear according to the settings of the world they are shuffled into, rather than the original world * set hidden-spoiler items and locations with Shop items to events * make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start * Fix oot Glitched and No Logic generation * fix indenting * Greatly reduce displayed cosmetic options * Change oot data version to 1 * add apz5 distribution to webhost * print player name if an ALttP dungeon contains a good item for OoT world * delete unneeded commented code * remove OcarinaSongs import to satisfy lint
		
			
				
	
	
		
			1001 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1001 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from itertools import chain
 | |
| 
 | |
| class Address():
 | |
|     prev_address = None
 | |
| 
 | |
|     def __init__(self, address=None, size=4, mask=0xFFFFFFFF, max=None, choices=None, value=None):
 | |
|         if address is None:
 | |
|             self.address = Address.prev_address
 | |
|         else:
 | |
|             self.address = address           
 | |
|         self.value = value
 | |
|         self.size = size
 | |
|         self.choices = choices
 | |
|         self.mask = mask
 | |
| 
 | |
|         Address.prev_address = self.address + self.size
 | |
| 
 | |
|         self.bit_offset = 0
 | |
|         while mask & 1 == 0:
 | |
|             mask = mask >> 1
 | |
|             self.bit_offset += 1
 | |
| 
 | |
|         if max is None:
 | |
|             self.max = mask
 | |
|         else:
 | |
|             self.max = max
 | |
| 
 | |
| 
 | |
|     def get_value(self, default=0):
 | |
|         if self.value is None:
 | |
|             return default
 | |
|         return self.value
 | |
| 
 | |
| 
 | |
|     def get_value_raw(self):
 | |
|         if self.value is None:
 | |
|             return None
 | |
| 
 | |
|         value = self.value
 | |
|         if self.choices is not None:
 | |
|             value = self.choices[value]
 | |
|         if not isinstance(value, int):
 | |
|             raise ValueError("Invalid value type '%s'" % str(value))
 | |
| 
 | |
|         if isinstance(value, bool):
 | |
|             value = 1 if value else 0
 | |
|         if value > self.max:
 | |
|             value = self.max
 | |
| 
 | |
|         value = (value << self.bit_offset) & self.mask
 | |
|         return value
 | |
| 
 | |
| 
 | |
|     def set_value_raw(self, value):
 | |
|         if value is None:
 | |
|             self.value = None
 | |
|             return
 | |
| 
 | |
|         if not isinstance(value, int):
 | |
|             raise ValueError("Invalid value type '%s'" % str(value))
 | |
| 
 | |
|         value = (value & self.mask) >> self.bit_offset
 | |
|         if value > self.max:
 | |
|             value = self.max
 | |
|         
 | |
|         if self.choices is not None:
 | |
|             for choice_name, choice_value in self.choices.items():
 | |
|                 if choice_value == value:
 | |
|                     value = choice_name
 | |
|                     break
 | |
| 
 | |
|         self.value = value
 | |
| 
 | |
| 
 | |
|     def get_writes(self, save_context):
 | |
|         if self.value is None:
 | |
|             return
 | |
| 
 | |
|         value = self.get_value_raw()
 | |
|         if value is None:
 | |
|             return
 | |
| 
 | |
|         values = zip(Address.to_bytes(value, self.size), 
 | |
|                      Address.to_bytes(self.mask, self.size))
 | |
| 
 | |
|         for i, (byte, mask) in enumerate(values):
 | |
|             if mask == 0:
 | |
|                 continue
 | |
|             if mask == 0xFF:
 | |
|                 save_context.write_byte(self.address + i, byte)
 | |
|             else:
 | |
|                 save_context.write_bits(self.address + i, byte, mask=mask)
 | |
| 
 | |
| 
 | |
|     def to_bytes(value, size):
 | |
|         ret = []
 | |
|         for _ in range(size):
 | |
|             ret.insert(0, value & 0xFF)
 | |
|             value = value >> 8
 | |
|         return ret
 | |
| 
 | |
| 
 | |
| class SaveContext():
 | |
|     def __init__(self):
 | |
|         self.save_bits = {}
 | |
|         self.save_bytes = {}
 | |
|         self.addresses = self.get_save_context_addresses()
 | |
| 
 | |
| 
 | |
|     # will set the bits of value to the offset in the save (or'ing them with what is already there)
 | |
|     def write_bits(self, address, value, mask=None, predicate=None):
 | |
|         if predicate and not predicate(value):
 | |
|             return
 | |
| 
 | |
|         if mask is not None:
 | |
|             value = value & mask
 | |
| 
 | |
|         if address in self.save_bytes:
 | |
|             old_val = self.save_bytes[address]
 | |
|             if mask is not None:
 | |
|                 old_val &= ~mask
 | |
|             value = old_val | value
 | |
|             self.write_byte(address, value, predicate)
 | |
|         elif address in self.save_bits:
 | |
|             if mask is not None:
 | |
|                 self.save_bits[address] &= ~mask
 | |
|             self.save_bits[address] |= value
 | |
|         else:
 | |
|             self.save_bits[address] = value
 | |
| 
 | |
| 
 | |
|     # will overwrite the byte at offset with the given value
 | |
|     def write_byte(self, address, value, predicate=None):
 | |
|         if predicate and not predicate(value):
 | |
|             return
 | |
| 
 | |
|         if address in self.save_bits:
 | |
|             del self.save_bits[address]
 | |
| 
 | |
|         self.save_bytes[address] = value
 | |
| 
 | |
| 
 | |
|     # will overwrite the byte at offset with the given value
 | |
|     def write_bytes(self, address, bytes, predicate=None):
 | |
|         for i, value in enumerate(bytes):
 | |
|             self.write_byte(address + i, value, predicate)
 | |
| 
 | |
| 
 | |
|     def write_save_entry(self, address):
 | |
|         if isinstance(address, dict):
 | |
|             for name, sub_address in address.items():
 | |
|                 self.write_save_entry(sub_address)
 | |
|         elif isinstance(address, list):
 | |
|             for sub_address in address:
 | |
|                 self.write_save_entry(sub_address)
 | |
|         else:
 | |
|             address.get_writes(self)
 | |
| 
 | |
| 
 | |
|     def set_ammo_max(self):
 | |
|         ammo_maxes = {
 | |
|             'stick'     : ('stick_upgrade', [10,  10,  20,  30]),
 | |
|             'nut'       : ('nut_upgrade',   [20,  20,  30,  40]),
 | |
|             'bomb'      : ('bomb_bag',      [00,  20,  30,  40]),
 | |
|             'bow'       : ('quiver',        [00,  30,  40,  50]),
 | |
|             'slingshot' : ('bullet_bag',    [00,  30,  40,  50]),
 | |
|             'rupees'    : ('wallet',        [99, 200, 500, 999]),
 | |
|         }
 | |
| 
 | |
|         for ammo, (upgrade, maxes) in ammo_maxes.items():
 | |
|             upgrade_count = self.addresses['upgrades'][upgrade].get_value()
 | |
|             try:
 | |
|                 ammo_max = maxes[upgrade_count]
 | |
|             except IndexError:
 | |
|                 ammo_max = maxes[-1]
 | |
|             if ammo == 'rupees':
 | |
|                 self.addresses[ammo].max = ammo_max
 | |
|             else:
 | |
|                 self.addresses['ammo'][ammo].max = ammo_max
 | |
| 
 | |
| 
 | |
|     # will overwrite the byte at offset with the given value
 | |
|     def write_save_table(self, rom):
 | |
|         self.set_ammo_max()
 | |
|         for name, address in self.addresses.items():
 | |
|             self.write_save_entry(address)
 | |
| 
 | |
|         save_table = []
 | |
|         for address, value in self.save_bits.items():
 | |
|             if value != 0:
 | |
|                 save_table += [(address & 0xFF00) >> 8, address & 0xFF, 0x00, value]
 | |
|         for address, value in self.save_bytes.items():
 | |
|             save_table += [(address & 0xFF00) >> 8, address & 0xFF, 0x01, value]
 | |
|         save_table += [0x00,0x00,0x00,0x00]
 | |
| 
 | |
|         table_len = len(save_table)
 | |
|         if table_len > 0x400:
 | |
|             raise Exception("The Initial Save Table has exceeded its maximum capacity: 0x%03X/0x400" % table_len)
 | |
|         rom.write_bytes(rom.sym('INITIAL_SAVE_DATA'), save_table)
 | |
| 
 | |
| 
 | |
|     def give_bottle(self, item, count):
 | |
|         for bottle_id in range(4):
 | |
|             item_slot = 'bottle_%d' % (bottle_id + 1)
 | |
|             if self.addresses['item_slot'][item_slot].get_value(0xFF) != 0xFF:
 | |
|                 continue
 | |
| 
 | |
|             self.addresses['item_slot'][item_slot].value = SaveContext.bottle_types[item]
 | |
|             count -= 1
 | |
| 
 | |
|             if count == 0:
 | |
|                 return
 | |
| 
 | |
| 
 | |
|     def give_health(self, health):
 | |
|         health += self.addresses['health_capacity'].get_value(0x30) / 0x10
 | |
|         health += self.addresses['quest']['heart_pieces'].get_value() / 4
 | |
| 
 | |
|         self.addresses['health_capacity'].value       = int(health) * 0x10
 | |
|         self.addresses['health'].value                = int(health) * 0x10
 | |
|         self.addresses['quest']['heart_pieces'].value = int((health % 1) * 4)
 | |
| 
 | |
| 
 | |
|     def give_raw_item(self, item):
 | |
|         if item.endswith(')'):
 | |
|             item_base, count = item[:-1].split(' (', 1)
 | |
|             if count.isdigit():
 | |
|                 return self.give_item(item_base, count=int(count))
 | |
|         return self.give_item(item)
 | |
| 
 | |
| 
 | |
|     def give_item(self, item, count=1):
 | |
|         if item in SaveContext.bottle_types:
 | |
|             self.give_bottle(item, count)
 | |
|         elif item in ["Piece of Heart", "Piece of Heart (Treasure Chest Game)"]:
 | |
|             self.give_health(count / 4)
 | |
|         elif item == "Heart Container":
 | |
|             self.give_health(count)
 | |
|         elif item == "Bombchu Item":
 | |
|             self.give_bombchu_item()
 | |
|         elif item in SaveContext.save_writes_table:
 | |
|             for address, value in SaveContext.save_writes_table[item].items():
 | |
|                 if value is None:
 | |
|                     value = count
 | |
|                 elif isinstance(value, list):
 | |
|                     value = value[min(len(value), count) - 1]
 | |
|                 elif isinstance(value, bool):
 | |
|                     value = 1 if value else 0
 | |
| 
 | |
|                 address_value = self.addresses
 | |
|                 prev_sub_address = 'Save Context'
 | |
|                 for sub_address in address.split('.'):
 | |
|                     if sub_address not in address_value:
 | |
|                         raise ValueError('Unknown key %s in %s of SaveContext' % (sub_address, prev_sub_address))
 | |
| 
 | |
|                     if isinstance(address_value, list):
 | |
|                         sub_address =  int(sub_address)
 | |
| 
 | |
|                     address_value = address_value[sub_address]
 | |
|                     prev_sub_address = sub_address
 | |
|                 if not isinstance(address_value, Address):
 | |
|                     raise ValueError('%s does not resolve to an Address in SaveContext' % (sub_address))
 | |
| 
 | |
|                 if isinstance(value, int) and value < address_value.get_value():
 | |
|                     continue
 | |
| 
 | |
|                 address_value.value = value
 | |
|         else:
 | |
|             raise ValueError("Cannot give unknown starting item %s" % item)
 | |
| 
 | |
| 
 | |
|     def give_bombchu_item(self):
 | |
|         self.give_item("Bombchus", 0)
 | |
| 
 | |
| 
 | |
|     def equip_default_items(self, age):
 | |
|         self.equip_items(age, 'equips_' + age)
 | |
| 
 | |
| 
 | |
|     def equip_current_items(self, age):
 | |
|         self.equip_items(age, 'equips')
 | |
| 
 | |
| 
 | |
|     def equip_items(self, age, equip_type):
 | |
|         if age not in ['child', 'adult']:
 | |
|             raise ValueError("Age must be 'adult' or 'child', not %s" % age)
 | |
| 
 | |
|         if equip_type not in ['equips', 'equips_child', 'equips_adult']:
 | |
|             raise ValueError("Equip type must be 'equips', 'equips_child' or 'equips_adult', not %s" % equip_type)
 | |
| 
 | |
|         age = 'equips_' + age
 | |
|         c_buttons = list(self.addresses[age]['button_slots'].keys())
 | |
|         for item_slot in SaveContext.equipable_items[age]['items']:
 | |
|             item = self.addresses['item_slot'][item_slot].get_value('none')
 | |
|             if item != 'none':
 | |
|                 c_button = c_buttons.pop()
 | |
|                 self.addresses[equip_type]['button_slots'][c_button].value = item_slot
 | |
|                 self.addresses[equip_type]['button_items'][c_button].value = item
 | |
|                 if not c_buttons:
 | |
|                     break
 | |
| 
 | |
|         for equip_item, equip_addresses in self.addresses[age]['equips'].items():
 | |
|             for item in SaveContext.equipable_items[age][equip_item]:
 | |
|                 if self.addresses['equip_items'][item].get_value():
 | |
|                     item_value = self.addresses['equip_items'][item].get_value_raw()
 | |
|                     self.addresses[equip_type]['equips'][equip_item].set_value_raw(item_value)
 | |
|                     if equip_item == 'tunic':
 | |
|                         self.addresses[equip_type]['equips'][equip_item].value = 1
 | |
|                     if equip_item == 'sword':
 | |
|                         self.addresses[equip_type]['button_items']['b'].value = item
 | |
|                     break
 | |
| 
 | |
| 
 | |
|     def get_save_context_addresses(self):
 | |
|         return {
 | |
|             'entrance_index'             : Address(0x0000, size=4),
 | |
|             'link_age'                   : Address(size=4, max=1),
 | |
|             'unk_00'                     : Address(size=2),
 | |
|             'cutscene_index'             : Address(size=2),
 | |
|             'time_of_day'                : Address(size=2),
 | |
|             'unk_01'                     : Address(size=2),
 | |
|             'night_flag'                 : Address(size=4, max=1),
 | |
|             'unk_02'                     : Address(size=8),
 | |
|             'id'                         : Address(size=6),
 | |
|             'deaths'                     : Address(size=2),
 | |
|             'file_name'                  : Address(size=8),
 | |
|             'n64dd_flag'                 : Address(size=2),
 | |
|             'health_capacity'            : Address(size=2, max=0x140),
 | |
|             'health'                     : Address(size=2, max=0x140),
 | |
|             'magic_level'                : Address(size=1, max=2),
 | |
|             'magic'                      : Address(size=1, max=0x60),
 | |
|             'rupees'                     : Address(size=2),
 | |
|             'bgs_hits_left'              : Address(size=2),
 | |
|             'navi_timer'                 : Address(size=2),
 | |
|             'magic_acquired'             : Address(size=1, max=1),
 | |
|             'unk_03'                     : Address(size=1),
 | |
|             'double_magic'               : Address(size=1, max=1),
 | |
|             'double_defense'             : Address(size=1, max=1),
 | |
|             'bgs_flag'                   : Address(size=1, max=1),
 | |
|             'unk_05'                     : Address(size=1),
 | |
| 
 | |
|             # Equiped Items
 | |
|             'equips_child' : {
 | |
|                 'button_items' : {
 | |
|                     'b'                  : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'left'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 },
 | |
|                 'button_slots' : {
 | |
|                     'left'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                 },
 | |
|                 'equips' : {
 | |
|                     'sword'              : Address(0x0048, size=2, mask=0x000F),
 | |
|                     'shield'             : Address(0x0048, size=2, mask=0x00F0),
 | |
|                     'tunic'              : Address(0x0048, size=2, mask=0x0F00),
 | |
|                     'boots'              : Address(0x0048, size=2, mask=0xF000),                
 | |
|                 },
 | |
|             },
 | |
|             'equips_adult' : {
 | |
|                 'button_items' : {
 | |
|                     'b'                  : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'left'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 },
 | |
|                 'button_slots' : {
 | |
|                     'left'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                 },
 | |
|                 'equips' : {
 | |
|                     'sword'              : Address(0x0052, size=2, mask=0x000F),
 | |
|                     'shield'             : Address(0x0052, size=2, mask=0x00F0),
 | |
|                     'tunic'              : Address(0x0052, size=2, mask=0x0F00),
 | |
|                     'boots'              : Address(0x0052, size=2, mask=0xF000),                
 | |
|                 },
 | |
|             },
 | |
|             'unk_06'                     : Address(size=0x12),
 | |
|             'scene_index'                : Address(size=2),
 | |
| 
 | |
|             'equips' : {
 | |
|                 'button_items' : {
 | |
|                     'b'                  : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'left'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 },
 | |
|                 'button_slots' : {
 | |
|                     'left'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'down'               : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                     'right'              : Address(size=1, choices=SaveContext.slot_id_map),
 | |
|                 },
 | |
|                 'equips' : {
 | |
|                     'sword'              : Address(0x0070, size=2, mask=0x000F, max=3),
 | |
|                     'shield'             : Address(0x0070, size=2, mask=0x00F0, max=3),
 | |
|                     'tunic'              : Address(0x0070, size=2, mask=0x0F00, max=3),
 | |
|                     'boots'              : Address(0x0070, size=2, mask=0xF000, max=3),                
 | |
|                 },
 | |
|             },
 | |
|             'unk_07'                     : Address(size=2),
 | |
| 
 | |
|             # Item Slots
 | |
|             'item_slot'                  : {
 | |
|                 'stick'                  : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'nut'                    : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bomb'                   : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bow'                    : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'fire_arrow'             : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'dins_fire'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'slingshot'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'ocarina'                : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bombchu'                : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'hookshot'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'ice_arrow'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'farores_wind'           : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'boomerang'              : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'lens'                   : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'beans'                  : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'hammer'                 : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'light_arrow'            : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'nayrus_love'            : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bottle_1'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bottle_2'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bottle_3'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'bottle_4'               : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'adult_trade'            : Address(size=1, choices=SaveContext.item_id_map),
 | |
|                 'child_trade'            : Address(size=1, choices=SaveContext.item_id_map),
 | |
|             },
 | |
| 
 | |
|             # Item Ammo
 | |
|             'ammo' : {
 | |
|                 'stick'                  : Address(size=1),
 | |
|                 'nut'                    : Address(size=1),
 | |
|                 'bomb'                   : Address(size=1),
 | |
|                 'bow'                    : Address(size=1),
 | |
|                 'fire_arrow'             : Address(size=1, max=0),
 | |
|                 'dins_fire'              : Address(size=1, max=0),
 | |
|                 'slingshot'              : Address(size=1),
 | |
|                 'ocarina'                : Address(size=1, max=0),
 | |
|                 'bombchu'                : Address(size=1, max=50),
 | |
|                 'hookshot'               : Address(size=1, max=0),
 | |
|                 'ice_arrow'              : Address(size=1, max=0),
 | |
|                 'farores_wind'           : Address(size=1, max=0),
 | |
|                 'boomerang'              : Address(size=1, max=0),
 | |
|                 'lens'                   : Address(size=1, max=0),
 | |
|                 'beans'                  : Address(size=1, max=10),
 | |
|             },
 | |
|             'magic_beans_sold'           : Address(size=1, max=10),
 | |
| 
 | |
|             # Equipment
 | |
|             'equip_items' : {
 | |
|                 'kokiri_sword'           : Address(0x009C, size=2, mask=0x0001),
 | |
|                 'master_sword'           : Address(0x009C, size=2, mask=0x0002),
 | |
|                 'biggoron_sword'         : Address(0x009C, size=2, mask=0x0004),
 | |
|                 'broken_knife'           : Address(0x009C, size=2, mask=0x0008),
 | |
|                 'deku_shield'            : Address(0x009C, size=2, mask=0x0010),
 | |
|                 'hylian_shield'          : Address(0x009C, size=2, mask=0x0020),
 | |
|                 'mirror_shield'          : Address(0x009C, size=2, mask=0x0040),
 | |
|                 'kokiri_tunic'           : Address(0x009C, size=2, mask=0x0100),
 | |
|                 'goron_tunic'            : Address(0x009C, size=2, mask=0x0200),
 | |
|                 'zora_tunic'             : Address(0x009C, size=2, mask=0x0400),
 | |
|                 'kokiri_boots'           : Address(0x009C, size=2, mask=0x1000),
 | |
|                 'iron_boots'             : Address(0x009C, size=2, mask=0x2000),
 | |
|                 'hover_boots'            : Address(0x009C, size=2, mask=0x4000),
 | |
|             },
 | |
| 
 | |
|             'unk_08'                     : Address(size=2),
 | |
| 
 | |
|             # Upgrades
 | |
|             'upgrades' : {
 | |
|                 'quiver'                 : Address(0x00A0, mask=0x00000007, max=3),
 | |
|                 'bomb_bag'               : Address(0x00A0, mask=0x00000038, max=3),
 | |
|                 'strength_upgrade'       : Address(0x00A0, mask=0x000001C0, max=3),
 | |
|                 'diving_upgrade'         : Address(0x00A0, mask=0x00000E00, max=2),
 | |
|                 'wallet'                 : Address(0x00A0, mask=0x00003000, max=3),
 | |
|                 'bullet_bag'             : Address(0x00A0, mask=0x0001C000, max=3),
 | |
|                 'stick_upgrade'          : Address(0x00A0, mask=0x000E0000, max=3),
 | |
|                 'nut_upgrade'            : Address(0x00A0, mask=0x00700000, max=3),
 | |
|             },
 | |
| 
 | |
|             # Medallions
 | |
|             'quest' : {
 | |
|                 'medallions' : {
 | |
|                     'forest'             : Address(0x00A4, mask=0x00000001),
 | |
|                     'fire'               : Address(0x00A4, mask=0x00000002),
 | |
|                     'water'              : Address(0x00A4, mask=0x00000004),
 | |
|                     'spirit'             : Address(0x00A4, mask=0x00000008),
 | |
|                     'shadow'             : Address(0x00A4, mask=0x00000010),
 | |
|                     'light'              : Address(0x00A4, mask=0x00000020),
 | |
| 
 | |
|                 },
 | |
|                 'songs' : {
 | |
|                     'minuet_of_forest'   : Address(0x00A4, mask=0x00000040),
 | |
|                     'bolero_of_fire'     : Address(0x00A4, mask=0x00000080),
 | |
|                     'serenade_of_water'  : Address(0x00A4, mask=0x00000100),
 | |
|                     'requiem_of_spirit'  : Address(0x00A4, mask=0x00000200),
 | |
|                     'nocturne_of_shadow' : Address(0x00A4, mask=0x00000400),
 | |
|                     'prelude_of_light'   : Address(0x00A4, mask=0x00000800),
 | |
|                     'zeldas_lullaby'     : Address(0x00A4, mask=0x00001000),
 | |
|                     'eponas_song'        : Address(0x00A4, mask=0x00002000),
 | |
|                     'sarias_song'        : Address(0x00A4, mask=0x00004000),
 | |
|                     'suns_song'          : Address(0x00A4, mask=0x00008000),
 | |
|                     'song_of_time'       : Address(0x00A4, mask=0x00010000),
 | |
|                     'song_of_storms'     : Address(0x00A4, mask=0x00020000),
 | |
|                 },
 | |
|                 'stones' : {
 | |
|                     'kokiris_emerald'    : Address(0x00A4, mask=0x00040000),
 | |
|                     'gorons_ruby'        : Address(0x00A4, mask=0x00080000),
 | |
|                     'zoras_sapphire'     : Address(0x00A4, mask=0x00100000),
 | |
|                 },
 | |
|                 'stone_of_agony'         : Address(0x00A4, mask=0x00200000),
 | |
|                 'gerudos_card'           : Address(0x00A4, mask=0x00400000),
 | |
|                 'gold_skulltula'         : Address(0x00A4, mask=0x00800000),
 | |
|                 'heart_pieces'           : Address(0x00A4, mask=0xFF000000),
 | |
|             },
 | |
| 
 | |
|             # Dungeon Items
 | |
|             'dungeon_items' : {
 | |
|                 'deku' : {
 | |
|                      'boss_key'          : Address(0x00A8, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00A8, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00A8, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'dodongo' : {
 | |
|                      'boss_key'          : Address(0x00A9, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00A9, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00A9, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'jabu' : {
 | |
|                      'boss_key'          : Address(0x00AA, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AA, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AA, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'forest' : {
 | |
|                      'boss_key'          : Address(0x00AB, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AB, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AB, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'fire' : {
 | |
|                      'boss_key'          : Address(0x00AC, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AC, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AC, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'water' : {
 | |
|                      'boss_key'          : Address(0x00AD, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AD, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AD, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'spirit' : {
 | |
|                      'boss_key'          : Address(0x00AE, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AE, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AE, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'shadow' : {
 | |
|                      'boss_key'          : Address(0x00AF, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00AF, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00AF, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'botw' : {
 | |
|                      'boss_key'          : Address(0x00B0, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B0, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B0, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'ice' : {
 | |
|                      'boss_key'          : Address(0x00B1, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B1, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B1, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'gt' : {
 | |
|                      'boss_key'          : Address(0x00B2, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B2, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B2, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'gtg' : {
 | |
|                      'boss_key'          : Address(0x00B3, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B3, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B3, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'fortress' : {
 | |
|                      'boss_key'          : Address(0x00B4, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B4, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B4, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'gc' : {
 | |
|                      'boss_key'          : Address(0x00B5, size=1, mask=0x01),
 | |
|                      'compass'           : Address(0x00B5, size=1, mask=0x02),
 | |
|                      'map'               : Address(0x00B5, size=1, mask=0x04),
 | |
|                 },
 | |
|                 'unused'                 : Address(size=6),
 | |
|             },
 | |
|             'keys' : {
 | |
|                 'deku'                   : Address(size=1),
 | |
|                 'dodongo'                : Address(size=1),
 | |
|                 'jabu'                   : Address(size=1),
 | |
|                 'forest'                 : Address(size=1),
 | |
|                 'fire'                   : Address(size=1),
 | |
|                 'water'                  : Address(size=1),
 | |
|                 'spirit'                 : Address(size=1),
 | |
|                 'shadow'                 : Address(size=1),
 | |
|                 'botw'                   : Address(size=1),
 | |
|                 'ice'                    : Address(size=1),
 | |
|                 'gt'                     : Address(size=1),
 | |
|                 'gtg'                    : Address(size=1),
 | |
|                 'fortress'               : Address(size=1),
 | |
|                 'gc'                     : Address(size=1),
 | |
|                 'unused'                 : Address(size=5),
 | |
|             },
 | |
|             'defense_hearts'             : Address(size=1, max=20),
 | |
|             'gs_tokens'                  : Address(size=2, max=100),
 | |
|             'triforce_pieces'            : Address(0xD4 + 0x1C * 0x48 + 0x10, size=4), # Unused word in scene x48
 | |
|         }
 | |
| 
 | |
| 
 | |
|     item_id_map = {
 | |
|         'none'                : 0xFF,
 | |
|         'stick'               : 0x00,
 | |
|         'nut'                 : 0x01,
 | |
|         'bomb'                : 0x02,
 | |
|         'bow'                 : 0x03,
 | |
|         'fire_arrow'          : 0x04,
 | |
|         'dins_fire'           : 0x05,
 | |
|         'slingshot'           : 0x06,
 | |
|         'fairy_ocarina'       : 0x07,
 | |
|         'ocarina_of_time'     : 0x08,
 | |
|         'bombchu'             : 0x09,
 | |
|         'hookshot'            : 0x0A,
 | |
|         'longshot'            : 0x0B,
 | |
|         'ice_arrow'           : 0x0C,
 | |
|         'farores_wind'        : 0x0D,
 | |
|         'boomerang'           : 0x0E,
 | |
|         'lens'                : 0x0F,
 | |
|         'beans'               : 0x10,
 | |
|         'hammer'              : 0x11,
 | |
|         'light_arrow'         : 0x12,
 | |
|         'nayrus_love'         : 0x13,
 | |
|         'bottle'              : 0x14,
 | |
|         'red_potion'          : 0x15,
 | |
|         'green_potion'        : 0x16,
 | |
|         'blue_potion'         : 0x17,
 | |
|         'fairy'               : 0x18,
 | |
|         'fish'                : 0x19,
 | |
|         'milk'                : 0x1A,
 | |
|         'letter'              : 0x1B,
 | |
|         'blue_fire'           : 0x1C,
 | |
|         'bug'                 : 0x1D,
 | |
|         'big_poe'             : 0x1E,
 | |
|         'half_milk'           : 0x1F,
 | |
|         'poe'                 : 0x20,
 | |
|         'weird_egg'           : 0x21,
 | |
|         'chicken'             : 0x22,
 | |
|         'zeldas_letter'       : 0x23,
 | |
|         'keaton_mask'         : 0x24,
 | |
|         'skull_mask'          : 0x25,
 | |
|         'spooky_mask'         : 0x26,
 | |
|         'bunny_hood'          : 0x27,
 | |
|         'goron_mask'          : 0x28,
 | |
|         'zora_mask'           : 0x29,
 | |
|         'gerudo_mask'         : 0x2A,
 | |
|         'mask_of_truth'       : 0x2B,
 | |
|         'sold_out'            : 0x2C,
 | |
|         'pocket_egg'          : 0x2D,
 | |
|         'pocket_cucco'        : 0x2E,
 | |
|         'cojiro'              : 0x2F,
 | |
|         'odd_mushroom'        : 0x30,
 | |
|         'odd_potion'          : 0x31,
 | |
|         'poachers_saw'        : 0x32,
 | |
|         'broken_gorons_sword' : 0x33,
 | |
|         'prescription'        : 0x34,
 | |
|         'eyeball_frog'        : 0x35,
 | |
|         'eye_drops'           : 0x36,
 | |
|         'claim_check'         : 0x37,
 | |
|         'bow_fire_arrow'      : 0x38,
 | |
|         'bow_ice_arrow'       : 0x39,
 | |
|         'bow_light_arrow'     : 0x3A,
 | |
|         'kokiri_sword'        : 0x3B,
 | |
|         'master_sword'        : 0x3C,
 | |
|         'biggoron_sword'      : 0x3D,
 | |
|         'deku_shield'         : 0x3E,
 | |
|         'hylian_shield'       : 0x3F,
 | |
|         'mirror_shield'       : 0x40,
 | |
|         'kokiri_tunic'        : 0x41,
 | |
|         'goron_tunic'         : 0x42,
 | |
|         'zora_tunic'          : 0x43,
 | |
|         'kokiri_boots'        : 0x44,
 | |
|         'iron_boots'          : 0x45,
 | |
|         'hover_boots'         : 0x46,
 | |
|         'bullet_bag_30'       : 0x47,
 | |
|         'bullet_bag_40'       : 0x48,
 | |
|         'bullet_bag_50'       : 0x49,
 | |
|         'quiver_30'           : 0x4A,
 | |
|         'quiver_40'           : 0x4B,
 | |
|         'quiver_50'           : 0x4C,
 | |
|         'bomb_bag_20'         : 0x4D,
 | |
|         'bomb_bag_30'         : 0x4E,
 | |
|         'bomb_bag_40'         : 0x4F,
 | |
|         'gorons_bracelet'     : 0x40,
 | |
|         'silver_gauntlets'    : 0x41,
 | |
|         'golden_gauntlets'    : 0x42,
 | |
|         'silver_scale'        : 0x43,
 | |
|         'golden_scale'        : 0x44,
 | |
|         'broken_giants_knife' : 0x45,
 | |
|         'adults_wallet'       : 0x46,
 | |
|         'giants_wallet'       : 0x47,
 | |
|         'deku_seeds'          : 0x48,
 | |
|         'fishing_pole'        : 0x49,
 | |
|         'minuet'              : 0x4A,
 | |
|         'bolero'              : 0x4B,
 | |
|         'serenade'            : 0x4C,
 | |
|         'requiem'             : 0x4D,
 | |
|         'nocturne'            : 0x4E,
 | |
|         'prelude'             : 0x4F,
 | |
|         'zeldas_lullaby'      : 0x50,
 | |
|         'eponas_song'         : 0x51,
 | |
|         'sarias_song'         : 0x52,
 | |
|         'suns_song'           : 0x53,
 | |
|         'song_of_time'        : 0x54,
 | |
|         'song_of_storms'      : 0x55,
 | |
|         'forest_medallion'    : 0x56,
 | |
|         'fire_medallion'      : 0x57,
 | |
|         'water_medallion'     : 0x58,
 | |
|         'spirit_medallion'    : 0x59,
 | |
|         'shadow_medallion'    : 0x5A,
 | |
|         'light_medallion'     : 0x5B,
 | |
|         'kokiris_emerald'     : 0x5C,
 | |
|         'gorons_ruby'         : 0x5D,
 | |
|         'zoras_sapphire'      : 0x5E,
 | |
|         'stone_of_agony'      : 0x5F,
 | |
|         'gerudos_card'        : 0x60,
 | |
|         'gold_skulltula'      : 0x61,
 | |
|         'heart_container'     : 0x62,
 | |
|         'piece_of_heart'      : 0x63,
 | |
|         'boss_key'            : 0x64,
 | |
|         'compass'             : 0x65,
 | |
|         'dungeon_map'         : 0x66,
 | |
|         'small_key'           : 0x67,
 | |
|     }
 | |
| 
 | |
| 
 | |
|     slot_id_map = {
 | |
|         'stick'               : 0x00,
 | |
|         'nut'                 : 0x01,
 | |
|         'bomb'                : 0x02,
 | |
|         'bow'                 : 0x03,
 | |
|         'fire_arrow'          : 0x04,
 | |
|         'dins_fire'           : 0x05,
 | |
|         'slingshot'           : 0x06,
 | |
|         'ocarina'             : 0x07,
 | |
|         'bombchu'             : 0x08,
 | |
|         'hookshot'            : 0x09,
 | |
|         'ice_arrow'           : 0x0A,
 | |
|         'farores_wind'        : 0x0B,
 | |
|         'boomerang'           : 0x0C,
 | |
|         'lens'                : 0x0D,
 | |
|         'beans'               : 0x0E,
 | |
|         'hammer'              : 0x0F,
 | |
|         'light_arrow'         : 0x10,
 | |
|         'nayrus_love'         : 0x11,
 | |
|         'bottle_1'            : 0x12,
 | |
|         'bottle_2'            : 0x13,
 | |
|         'bottle_3'            : 0x14,
 | |
|         'bottle_4'            : 0x15,
 | |
|         'adult_trade'         : 0x16,
 | |
|         'child_trade'         : 0x17,
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bottle_types = {
 | |
|         "Bottle"                   : 'bottle',
 | |
|         "Bottle with Red Potion"   : 'red_potion',
 | |
|         "Bottle with Green Potion" : 'green_potion',
 | |
|         "Bottle with Blue Potion"  : 'blue_potion',
 | |
|         "Bottle with Fairy"        : 'fairy',
 | |
|         "Bottle with Fish"         : 'fish',
 | |
|         "Bottle with Milk"         : 'milk',
 | |
|         "Rutos Letter"             : 'letter',
 | |
|         "Bottle with Blue Fire"    : 'blue_fire',
 | |
|         "Bottle with Bugs"         : 'bug',
 | |
|         "Bottle with Big Poe"      : 'big_poe',
 | |
|         "Bottle with Milk (Half)"  : 'half_milk',
 | |
|         "Bottle with Poe"          : 'poe',    
 | |
|     }
 | |
| 
 | |
| 
 | |
|     save_writes_table = {
 | |
|         "Deku Stick Capacity": {
 | |
|             'item_slot.stick'            : 'stick',
 | |
|             'upgrades.stick_upgrade'     : [2,3],
 | |
|         },
 | |
|         "Deku Sticks": {
 | |
|             'item_slot.stick'            : 'stick',
 | |
|             'upgrades.stick_upgrade'     : 1,
 | |
|             'ammo.stick'                 : None,
 | |
|         },
 | |
|         "Deku Nut Capacity": {
 | |
|             'item_slot.nut'              : 'nut',
 | |
|             'upgrades.nut_upgrade'       : [2,3],
 | |
|         },
 | |
|         "Deku Nuts": {
 | |
|             'item_slot.nut'              : 'nut',
 | |
|             'upgrades.nut_upgrade'       : 1,
 | |
|             'ammo.nut'                   : None,
 | |
|         },
 | |
|         "Bomb Bag": {
 | |
|             'item_slot.bomb'             : 'bomb',
 | |
|             'upgrades.bomb_bag'          : None,
 | |
|         },
 | |
|         "Bombs" : {
 | |
|             'ammo.bomb'                  : None,
 | |
|         },
 | |
|         "Bombchus" : {
 | |
|             'item_slot.bombchu'          : 'bombchu',
 | |
|             'ammo.bombchu'               : None,
 | |
|         },
 | |
|         "Bow" : {
 | |
|             'item_slot.bow'              : 'bow',
 | |
|             'upgrades.quiver'            : None,
 | |
|         },
 | |
|         "Arrows" : {
 | |
|             'ammo.bow'                   : None,
 | |
|         },
 | |
|         "Slingshot"    : {
 | |
|             'item_slot.slingshot'        : 'slingshot',
 | |
|             'upgrades.bullet_bag'        : None,
 | |
|         },
 | |
|         "Deku Seeds" : {
 | |
|             'ammo.slingshot'             : None,
 | |
|         },
 | |
|         "Magic Bean" : {
 | |
|             'item_slot.beans'            : 'beans',
 | |
|             'ammo.beans'                 : None,
 | |
|             'magic_beans_sold'           : None,
 | |
|         },
 | |
|         "Fire Arrows"    : {'item_slot.fire_arrow'      : 'fire_arrow'},
 | |
|         "Ice Arrows"     : {'item_slot.ice_arrow'       : 'ice_arrow'},
 | |
|         "Light Arrows"   : {'item_slot.light_arrow'     : 'light_arrow'},
 | |
|         "Dins Fire"      : {'item_slot.dins_fire'       : 'dins_fire'},
 | |
|         "Farores Wind"   : {'item_slot.farores_wind'    : 'farores_wind'},
 | |
|         "Nayrus Love"    : {'item_slot.nayrus_love'     : 'nayrus_love'},
 | |
|         "Ocarina"        : {'item_slot.ocarina'         : ['fairy_ocarina', 'ocarina_of_time']},
 | |
|         "Progressive Hookshot" : {'item_slot.hookshot'  : ['hookshot', 'longshot']},
 | |
|         "Boomerang"      : {'item_slot.boomerang'       : 'boomerang'},
 | |
|         "Lens of Truth"  : {'item_slot.lens'            : 'lens'},
 | |
|         "Megaton Hammer"         : {'item_slot.hammer'          : 'hammer'},
 | |
|         "Pocket Egg"     : {'item_slot.adult_trade'     : 'pocket_egg'},
 | |
|         "Pocket Cucco"   : {'item_slot.adult_trade'     : 'pocket_cucco'},
 | |
|         "Cojiro"         : {'item_slot.adult_trade'     : 'cojiro'},
 | |
|         "Odd Mushroom"   : {'item_slot.adult_trade'     : 'odd_mushroom'},
 | |
|         "Poachers Saw"   : {'item_slot.adult_trade'     : 'poachers_saw'},
 | |
|         "Broken Sword"   : {'item_slot.adult_trade'     : 'broken_knife'},
 | |
|         "Prescription"   : {'item_slot.adult_trade'     : 'prescription'},
 | |
|         "Eyeball Frog"   : {'item_slot.adult_trade'     : 'eyeball_frog'},
 | |
|         "Eyedrops"       : {'item_slot.adult_trade'     : 'eye_drops'},
 | |
|         "Claim Check"    : {'item_slot.adult_trade'     : 'claim_check'},
 | |
|         "Weird Egg"      : {'item_slot.child_trade'     : 'weird_egg'},
 | |
|         "Chicken"        : {'item_slot.child_trade'     : 'chicken'},
 | |
|         "Zeldas Letter"  : {'item_slot.child_trade'     : 'zeldas_letter'},
 | |
|         "Goron Tunic"    : {'equip_items.goron_tunic'   : True},
 | |
|         "Zora Tunic"     : {'equip_items.zora_tunic'    : True},
 | |
|         "Iron Boots"     : {'equip_items.iron_boots'    : True},
 | |
|         "Hover Boots"    : {'equip_items.hover_boots'   : True},
 | |
|         "Deku Shield"    : {'equip_items.deku_shield'   : True},
 | |
|         "Hylian Shield"  : {'equip_items.hylian_shield' : True},
 | |
|         "Mirror Shield"  : {'equip_items.mirror_shield' : True},
 | |
|         "Kokiri Sword"   : {'equip_items.kokiri_sword'  : True},
 | |
|         "Master Sword"   : {'equip_items.master_sword'  : True},
 | |
|         "Giants Knife" : {
 | |
|             'equip_items.biggoron_sword' : True,
 | |
|             'bgs_hits_left'              : 8,
 | |
|         },
 | |
|         "Biggoron Sword" : {
 | |
|             'equip_items.biggoron_sword' : True,
 | |
|             'bgs_flag'                   : True,
 | |
|             'bgs_hits_left'              : 1,
 | |
|         },
 | |
|         "Gerudo Membership Card" : {'quest.gerudos_card'             : True},
 | |
|         "Stone of Agony"         : {'quest.stone_of_agony'           : True},
 | |
|         "Zeldas Lullaby"         : {'quest.songs.zeldas_lullaby'     : True},
 | |
|         "Eponas Song"            : {'quest.songs.eponas_song'        : True},
 | |
|         "Sarias Song"            : {'quest.songs.sarias_song'        : True},
 | |
|         "Suns Song"              : {'quest.songs.suns_song'          : True},
 | |
|         "Song of Time"           : {'quest.songs.song_of_time'       : True},
 | |
|         "Song of Storms"         : {'quest.songs.song_of_storms'     : True},
 | |
|         "Minuet of Forest"       : {'quest.songs.minuet_of_forest'   : True},
 | |
|         "Bolero of Fire"         : {'quest.songs.bolero_of_fire'     : True},
 | |
|         "Serenade of Water"      : {'quest.songs.serenade_of_water'  : True},
 | |
|         "Requiem of Spirit"      : {'quest.songs.requiem_of_spirit'  : True},
 | |
|         "Nocturne of Shadow"     : {'quest.songs.nocturne_of_shadow' : True},
 | |
|         "Prelude of Light"       : {'quest.songs.prelude_of_light'   : True},
 | |
|         "Kokiri Emerald"         : {'quest.stones.kokiris_emerald'   : True},
 | |
|         "Goron Ruby"             : {'quest.stones.gorons_ruby'       : True},
 | |
|         "Zora Sapphire"          : {'quest.stones.zoras_sapphire'    : True},
 | |
|         "Light Medallion"        : {'quest.medallions.light'         : True},
 | |
|         "Forest Medallion"       : {'quest.medallions.forest'        : True},
 | |
|         "Fire Medallion"         : {'quest.medallions.fire'          : True},
 | |
|         "Water Medallion"        : {'quest.medallions.water'         : True},
 | |
|         "Spirit Medallion"       : {'quest.medallions.spirit'        : True},
 | |
|         "Shadow Medallion"       : {'quest.medallions.shadow'        : True},
 | |
|         "Progressive Strength Upgrade" : {'upgrades.strength_upgrade' : None},
 | |
|         "Progressive Scale"            : {'upgrades.diving_upgrade'   : None},
 | |
|         "Progressive Wallet"           : {'upgrades.wallet'           : None},
 | |
|         "Gold Skulltula Token" : {
 | |
|             'quest.gold_skulltula'  : True,
 | |
|             'gs_tokens'             : None,
 | |
|         },
 | |
|         "Double Defense" : {
 | |
|             'double_defense'        : True,
 | |
|             'defense_hearts'        : 20,
 | |
|         },
 | |
|         "Magic Meter" : {
 | |
|             'magic_acquired'        : True,
 | |
|             'magic'                 : [0x30, 0x60],
 | |
|             'magic_level'           : None,
 | |
|             'double_magic'          : [False, True],
 | |
|         },
 | |
|         "Rupee"                     : {'rupees' : None},
 | |
|         "Rupees"                    : {'rupees' : None},
 | |
|         "Magic Bean Pack" : {
 | |
|             'item_slot.beans'       : 'beans',
 | |
|             'ammo.beans'            : 10
 | |
|         },
 | |
|         "Triforce Piece"            : {'triforce_pieces': None},
 | |
|     }
 | |
| 
 | |
|     giveable_items = set(chain(save_writes_table.keys(), bottle_types.keys(),
 | |
|         ["Piece of Heart", "Piece of Heart (Treasure Chest Game)", "Heart Container", "Rupee (1)"]))
 | |
| 
 | |
| 
 | |
|     equipable_items = {
 | |
|         'equips_adult' : {
 | |
|             'items': [
 | |
|                 'hookshot',
 | |
|                 'hammer',
 | |
|                 'bomb',
 | |
|                 'bow',
 | |
|                 'nut',
 | |
|                 'lens',
 | |
|                 'farores_wind',
 | |
|                 'dins_fire',
 | |
|                 'bombchu',
 | |
|                 'nayrus_love',
 | |
|                 'adult_trade',
 | |
|                 'bottle_1',
 | |
|                 'bottle_2',
 | |
|                 'bottle_3',
 | |
|                 'bottle_4',
 | |
|             ],
 | |
|             'sword' : [
 | |
|                 'biggoron_sword',
 | |
|                 'master_sword',
 | |
|             ],
 | |
|             'shield' : [
 | |
|                 'mirror_shield',
 | |
|                 'hylian_shield',
 | |
|             ],
 | |
|             'tunic' : [
 | |
|                 'goron_tunic',
 | |
|                 'zora_tunic',
 | |
|                 'kokiri_tunic',
 | |
|             ],
 | |
|             'boots' : [
 | |
|                 'kokiri_boots'
 | |
|             ],
 | |
|         },
 | |
|         'equips_child' : {
 | |
|             'items': [
 | |
|                 'bomb',
 | |
|                 'boomerang',
 | |
|                 'slingshot',
 | |
|                 'stick',
 | |
|                 'nut',
 | |
|                 'lens',
 | |
|                 'farores_wind',
 | |
|                 'dins_fire',
 | |
|                 'bombchu',
 | |
|                 'nayrus_love',
 | |
|                 'beans',
 | |
|                 'child_trade',
 | |
|                 'bottle_1',
 | |
|                 'bottle_2',
 | |
|                 'bottle_3',
 | |
|                 'bottle_4',
 | |
|             ],
 | |
|             'sword' : [
 | |
|                 'kokiri_sword',
 | |
|             ],
 | |
|             'shield' : [
 | |
|                 'deku_shield',
 | |
|                 'hylian_shield',
 | |
|             ],
 | |
|             'tunic' : [
 | |
|                 'kokiri_tunic',
 | |
|             ],
 | |
|             'boots' : [
 | |
|                 'kokiri_boots',
 | |
|             ],
 | |
|         }
 | |
|     }
 |