1204 lines
53 KiB
Python
1204 lines
53 KiB
Python
from itertools import chain
|
|
from enum import IntEnum
|
|
from .ItemPool import IGNORE_LOCATION
|
|
|
|
class Scenes(IntEnum):
|
|
# Dungeons
|
|
DEKU_TREE = 0x00
|
|
DODONGOS_CAVERN = 0x01
|
|
KING_DODONGO_LOBBY = 0x12
|
|
JABU_JABU = 0x02
|
|
FOREST_TEMPLE = 0x03
|
|
FIRE_TEMPLE = 0x04
|
|
WATER_TEMPLE = 0x05
|
|
SPIRIT_TEMPLE = 0x06
|
|
SHADOW_TEMPLE = 0x07
|
|
# Bean patch scenes
|
|
GRAVEYARD = 0x53
|
|
ZORAS_RIVER = 0x54
|
|
KOKIRI_FOREST = 0x55
|
|
LAKE_HYLIA = 0x57
|
|
GERUDO_VALLEY = 0x5A
|
|
LOST_WOODS = 0x5B
|
|
DESERT_COLOSSUS = 0x5C
|
|
DEATH_MOUNTAIN_TRAIL = 0x60
|
|
DEATH_MOUNTAIN_CRATER = 0x61
|
|
|
|
class FlagType(IntEnum):
|
|
CHEST = 0x00
|
|
SWITCH = 0x01
|
|
CLEAR = 0x02
|
|
COLLECT = 0x03
|
|
# 0x04 unused
|
|
VISITED_ROOM = 0x05
|
|
VISITED_FLOOR = 0x06
|
|
|
|
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 write_permanent_flag(self, scene, type, byte_offset, bit_values):
|
|
# Save format is described here: https://wiki.cloudmodding.com/oot/Save_Format
|
|
# Permanent flags start at offset 0x00D4. Each scene has 7 types of flags, one
|
|
# of which is unused. Each flag type is 4 bytes wide per-scene, thus each scene
|
|
# takes 28 (0x1C) bytes.
|
|
# Scenes and FlagType enums are defined for increased readability when using
|
|
# this function.
|
|
self.write_bits(0x00D4 + scene * 0x1C + type * 0x04 + byte_offset, bit_values)
|
|
|
|
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) * 0x10
|
|
|
|
|
|
def give_item(self, world, item, count=1):
|
|
if item.endswith(')'):
|
|
item_base, implicit_count = item[:-1].split(' (', 1)
|
|
if implicit_count.isdigit():
|
|
item = item_base
|
|
count *= int(implicit_count)
|
|
|
|
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(world)
|
|
elif item == IGNORE_LOCATION:
|
|
pass # used to disable some skipped and inaccessible locations
|
|
elif item in SaveContext.save_writes_table:
|
|
if item.startswith('Small Key Ring ('):
|
|
dungeon = item[:-1].split(' (', 1)[1]
|
|
save_writes = {
|
|
"Forest Temple" : {
|
|
'keys.forest': 6 if world.dungeon_mq[dungeon] else 5,
|
|
'total_keys.forest': 6 if world.dungeon_mq[dungeon] else 5,
|
|
},
|
|
"Fire Temple" : {
|
|
'keys.fire': 5 if world.dungeon_mq[dungeon] else 8,
|
|
'total_keys.fire': 5 if world.dungeon_mq[dungeon] else 8,
|
|
},
|
|
"Water Temple" : {
|
|
'keys.water': 2 if world.dungeon_mq[dungeon] else 6,
|
|
'total_keys.water': 2 if world.dungeon_mq[dungeon] else 6,
|
|
},
|
|
"Spirit Temple" : {
|
|
'keys.spirit': 7 if world.dungeon_mq[dungeon] else 5,
|
|
'total_keys.spirit': 7 if world.dungeon_mq[dungeon] else 5,
|
|
},
|
|
"Shadow Temple" : {
|
|
'keys.shadow': 6 if world.dungeon_mq[dungeon] else 5,
|
|
'total_keys.shadow': 6 if world.dungeon_mq[dungeon] else 5,
|
|
},
|
|
"Bottom of the Well" : {
|
|
'keys.botw': 2 if world.dungeon_mq[dungeon] else 3,
|
|
'total_keys.botw': 2 if world.dungeon_mq[dungeon] else 3,
|
|
},
|
|
"Gerudo Training Ground" : {
|
|
'keys.gtg': 3 if world.dungeon_mq[dungeon] else 9,
|
|
'total_keys.gtg': 3 if world.dungeon_mq[dungeon] else 9,
|
|
},
|
|
"Thieves Hideout" : {
|
|
'keys.fortress': 4,
|
|
'total_keys.fortress': 4,
|
|
},
|
|
"Ganons Castle" : {
|
|
'keys.gc': 3 if world.dungeon_mq[dungeon] else 2,
|
|
'total_keys.gc': 3 if world.dungeon_mq[dungeon] else 2,
|
|
},
|
|
}[dungeon]
|
|
else:
|
|
save_writes = SaveContext.save_writes_table[item]
|
|
for address, value in save_writes.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, world):
|
|
self.give_item(world, "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_giants_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),
|
|
'total_keys' : { # Upper half of unused word in own scene
|
|
'deku' : Address(0xD4 + 0x1C * 0x00 + 0x10, size=2),
|
|
'dodongo' : Address(0xD4 + 0x1C * 0x01 + 0x10, size=2),
|
|
'jabu' : Address(0xD4 + 0x1C * 0x02 + 0x10, size=2),
|
|
'forest' : Address(0xD4 + 0x1C * 0x03 + 0x10, size=2),
|
|
'fire' : Address(0xD4 + 0x1C * 0x04 + 0x10, size=2),
|
|
'water' : Address(0xD4 + 0x1C * 0x05 + 0x10, size=2),
|
|
'spirit' : Address(0xD4 + 0x1C * 0x06 + 0x10, size=2),
|
|
'shadow' : Address(0xD4 + 0x1C * 0x07 + 0x10, size=2),
|
|
'botw' : Address(0xD4 + 0x1C * 0x08 + 0x10, size=2),
|
|
'ice' : Address(0xD4 + 0x1C * 0x09 + 0x10, size=2),
|
|
'gt' : Address(0xD4 + 0x1C * 0x0A + 0x10, size=2),
|
|
'gtg' : Address(0xD4 + 0x1C * 0x0B + 0x10, size=2),
|
|
'fortress' : Address(0xD4 + 0x1C * 0x0C + 0x10, size=2),
|
|
'gc' : Address(0xD4 + 0x1C * 0x0D + 0x10, size=2),
|
|
},
|
|
'triforce_pieces' : Address(0xD4 + 0x1C * 0x48 + 0x10, size=4), # Unused word in scene x48
|
|
'pending_freezes' : Address(0xD4 + 0x1C * 0x49 + 0x10, size=4), # Unused word in scene x49
|
|
}
|
|
|
|
|
|
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_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 Stick": {
|
|
'item_slot.stick' : 'stick',
|
|
'upgrades.stick_upgrade' : 1,
|
|
'ammo.stick' : None,
|
|
},
|
|
"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_sword'},
|
|
"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},
|
|
"Rupee (Treasure Chest Game)" : {'rupees' : None},
|
|
"Rupees" : {'rupees' : None},
|
|
"Magic Bean Pack" : {
|
|
'item_slot.beans' : 'beans',
|
|
'ammo.beans' : 10
|
|
},
|
|
"Ice Trap" : {'pending_freezes': None},
|
|
"Triforce Piece" : {'triforce_pieces': None},
|
|
"Boss Key (Forest Temple)" : {'dungeon_items.forest.boss_key': True},
|
|
"Boss Key (Fire Temple)" : {'dungeon_items.fire.boss_key': True},
|
|
"Boss Key (Water Temple)" : {'dungeon_items.water.boss_key': True},
|
|
"Boss Key (Spirit Temple)" : {'dungeon_items.spirit.boss_key': True},
|
|
"Boss Key (Shadow Temple)" : {'dungeon_items.shadow.boss_key': True},
|
|
"Boss Key (Ganons Castle)" : {'dungeon_items.gt.boss_key': True},
|
|
"Compass (Deku Tree)" : {'dungeon_items.deku.compass': True},
|
|
"Compass (Dodongos Cavern)" : {'dungeon_items.dodongo.compass': True},
|
|
"Compass (Jabu Jabus Belly)" : {'dungeon_items.jabu.compass': True},
|
|
"Compass (Forest Temple)" : {'dungeon_items.forest.compass': True},
|
|
"Compass (Fire Temple)" : {'dungeon_items.fire.compass': True},
|
|
"Compass (Water Temple)" : {'dungeon_items.water.compass': True},
|
|
"Compass (Spirit Temple)" : {'dungeon_items.spirit.compass': True},
|
|
"Compass (Shadow Temple)" : {'dungeon_items.shadow.compass': True},
|
|
"Compass (Bottom of the Well)" : {'dungeon_items.botw.compass': True},
|
|
"Compass (Ice Cavern)" : {'dungeon_items.ice.compass': True},
|
|
"Map (Deku Tree)" : {'dungeon_items.deku.map': True},
|
|
"Map (Dodongos Cavern)" : {'dungeon_items.dodongo.map': True},
|
|
"Map (Jabu Jabus Belly)" : {'dungeon_items.jabu.map': True},
|
|
"Map (Forest Temple)" : {'dungeon_items.forest.map': True},
|
|
"Map (Fire Temple)" : {'dungeon_items.fire.map': True},
|
|
"Map (Water Temple)" : {'dungeon_items.water.map': True},
|
|
"Map (Spirit Temple)" : {'dungeon_items.spirit.map': True},
|
|
"Map (Shadow Temple)" : {'dungeon_items.shadow.map': True},
|
|
"Map (Bottom of the Well)" : {'dungeon_items.botw.map': True},
|
|
"Map (Ice Cavern)" : {'dungeon_items.ice.map': True},
|
|
"Small Key (Forest Temple)" : {
|
|
'keys.forest': None,
|
|
'total_keys.forest': None,
|
|
},
|
|
"Small Key (Fire Temple)" : {
|
|
'keys.fire': None,
|
|
'total_keys.fire': None,
|
|
},
|
|
"Small Key (Water Temple)" : {
|
|
'keys.water': None,
|
|
'total_keys.water': None,
|
|
},
|
|
"Small Key (Spirit Temple)" : {
|
|
'keys.spirit': None,
|
|
'total_keys.spirit': None,
|
|
},
|
|
"Small Key (Shadow Temple)" : {
|
|
'keys.shadow': None,
|
|
'total_keys.shadow': None,
|
|
},
|
|
"Small Key (Bottom of the Well)" : {
|
|
'keys.botw': None,
|
|
'total_keys.botw': None,
|
|
},
|
|
"Small Key (Gerudo Training Ground)" : {
|
|
'keys.gtg': None,
|
|
'total_keys.gtg': None,
|
|
},
|
|
"Small Key (Thieves Hideout)" : {
|
|
'keys.fortress': None,
|
|
'total_keys.fortress': None,
|
|
},
|
|
"Small Key (Ganons Castle)" : {
|
|
'keys.gc': None,
|
|
'total_keys.gc': None,
|
|
},
|
|
#HACK: these counts aren't used since exact counts based on whether the dungeon is MQ are defined above,
|
|
# but the entries need to be there for key rings to be valid starting items
|
|
"Small Key Ring (Forest Temple)" : {
|
|
'keys.forest': 6,
|
|
'total_keys.forest': 6,
|
|
},
|
|
"Small Key Ring (Fire Temple)" : {
|
|
'keys.fire': 8,
|
|
'total_keys.fire': 8,
|
|
},
|
|
"Small Key Ring (Water Temple)" : {
|
|
'keys.water': 6,
|
|
'total_keys.water': 6,
|
|
},
|
|
"Small Key Ring (Spirit Temple)" : {
|
|
'keys.spirit': 7,
|
|
'total_keys.spirit': 7,
|
|
},
|
|
"Small Key Ring (Shadow Temple)" : {
|
|
'keys.shadow': 6,
|
|
'total_keys.shadow': 6,
|
|
},
|
|
"Small Key Ring (Bottom of the Well)" : {
|
|
'keys.botw': 3,
|
|
'total_keys.botw': 3,
|
|
},
|
|
"Small Key Ring (Gerudo Training Ground)" : {
|
|
'keys.gtg': 9,
|
|
'total_keys.gtg': 9,
|
|
},
|
|
"Small Key Ring (Thieves Hideout)" : {
|
|
'keys.fortress': 4,
|
|
'total_keys.fortress': 4,
|
|
},
|
|
"Small Key Ring (Ganons Castle)" : {
|
|
'keys.gc': 3,
|
|
'total_keys.gc': 3,
|
|
},
|
|
}
|
|
|
|
|
|
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',
|
|
'bottle_1',
|
|
'bottle_2',
|
|
'bottle_3',
|
|
'bottle_4',
|
|
],
|
|
'sword' : [
|
|
'kokiri_sword',
|
|
],
|
|
'shield' : [
|
|
'deku_shield',
|
|
'hylian_shield',
|
|
],
|
|
'tunic' : [
|
|
'kokiri_tunic',
|
|
],
|
|
'boots' : [
|
|
'kokiri_boots',
|
|
],
|
|
}
|
|
}
|