OoT bug fixes (#955)
* OoT: fix shop patching crash due to Item changes * OoT: more informative failure in triforce piece replacement * OoT: in triforce hunt, remove ganon BK from pool and lock the door * OoT: no longer store trap information on the item
This commit is contained in:
parent
2a7babce68
commit
a753905ee4
|
@ -1388,6 +1388,10 @@ def get_pool_core(world):
|
||||||
remove_junk_pool = list(remove_junk_pool) + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)', 'Ice Trap']
|
remove_junk_pool = list(remove_junk_pool) + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)', 'Ice Trap']
|
||||||
|
|
||||||
junk_candidates = [item for item in pool if item in remove_junk_pool]
|
junk_candidates = [item for item in pool if item in remove_junk_pool]
|
||||||
|
if len(pending_junk_pool) > len(junk_candidates):
|
||||||
|
excess = len(pending_junk_pool) - len(junk_candidates)
|
||||||
|
if world.triforce_hunt:
|
||||||
|
raise RuntimeError(f"Items in the pool for player {world.player} exceed locations. Add {excess} location(s) or remove {excess} triforce piece(s).")
|
||||||
while pending_junk_pool:
|
while pending_junk_pool:
|
||||||
pending_item = pending_junk_pool.pop()
|
pending_item = pending_junk_pool.pop()
|
||||||
if not junk_candidates:
|
if not junk_candidates:
|
||||||
|
|
|
@ -49,7 +49,6 @@ class OOTItem(Item):
|
||||||
self.type = type
|
self.type = type
|
||||||
self.index = index
|
self.index = index
|
||||||
self.special = special or {}
|
self.special = special or {}
|
||||||
self.looks_like_item = None
|
|
||||||
self.price = special.get('price', None) if special else None
|
self.price = special.get('price', None) if special else None
|
||||||
self.internal = False
|
self.internal = False
|
||||||
|
|
||||||
|
|
|
@ -158,12 +158,12 @@ class TriforceGoal(Range):
|
||||||
"""Number of Triforce pieces required to complete the game."""
|
"""Number of Triforce pieces required to complete the game."""
|
||||||
display_name = "Required Triforce Pieces"
|
display_name = "Required Triforce Pieces"
|
||||||
range_start = 1
|
range_start = 1
|
||||||
range_end = 100
|
range_end = 80
|
||||||
default = 20
|
default = 20
|
||||||
|
|
||||||
|
|
||||||
class ExtraTriforces(Range):
|
class ExtraTriforces(Range):
|
||||||
"""Percentage of additional Triforce pieces in the pool, separate from the item pool setting."""
|
"""Percentage of additional Triforce pieces in the pool. With high numbers, you may need to randomize additional locations to have enough items."""
|
||||||
display_name = "Percentage of Extra Triforce Pieces"
|
display_name = "Percentage of Extra Triforce Pieces"
|
||||||
range_start = 0
|
range_start = 0
|
||||||
range_end = 100
|
range_end = 100
|
||||||
|
|
|
@ -1844,7 +1844,7 @@ def write_rom_item(rom, item_id, item):
|
||||||
|
|
||||||
|
|
||||||
def get_override_table(world):
|
def get_override_table(world):
|
||||||
return list(filter(lambda val: val != None, map(partial(get_override_entry, world.player), world.world.get_filled_locations(world.player))))
|
return list(filter(lambda val: val != None, map(partial(get_override_entry, world), world.world.get_filled_locations(world.player))))
|
||||||
|
|
||||||
|
|
||||||
override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c
|
override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c
|
||||||
|
@ -1852,10 +1852,10 @@ def get_override_table_bytes(override_table):
|
||||||
return b''.join(sorted(itertools.starmap(override_struct.pack, override_table)))
|
return b''.join(sorted(itertools.starmap(override_struct.pack, override_table)))
|
||||||
|
|
||||||
|
|
||||||
def get_override_entry(player_id, location):
|
def get_override_entry(ootworld, location):
|
||||||
scene = location.scene
|
scene = location.scene
|
||||||
default = location.default
|
default = location.default
|
||||||
player_id = 0 if player_id == location.item.player else min(location.item.player, 255)
|
player_id = 0 if ootworld.player == location.item.player else min(location.item.player, 255)
|
||||||
if location.item.game != 'Ocarina of Time':
|
if location.item.game != 'Ocarina of Time':
|
||||||
# This is an AP sendable. It's guaranteed to not be None.
|
# This is an AP sendable. It's guaranteed to not be None.
|
||||||
if location.item.advancement:
|
if location.item.advancement:
|
||||||
|
@ -1869,7 +1869,7 @@ def get_override_entry(player_id, location):
|
||||||
|
|
||||||
if location.item.trap:
|
if location.item.trap:
|
||||||
item_id = 0x7C # Ice Trap ID, to get "X is a fool" message
|
item_id = 0x7C # Ice Trap ID, to get "X is a fool" message
|
||||||
looks_like_item_id = location.item.looks_like_item.index
|
looks_like_item_id = ootworld.trap_appearances[location.address].index
|
||||||
else:
|
else:
|
||||||
looks_like_item_id = 0
|
looks_like_item_id = 0
|
||||||
|
|
||||||
|
@ -2091,7 +2091,8 @@ def get_locked_doors(rom, world):
|
||||||
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
||||||
|
|
||||||
# If boss door, set the door's unlock flag
|
# If boss door, set the door's unlock flag
|
||||||
if (world.shuffle_bosskeys == 'remove' and scene != 0x0A) or (world.shuffle_ganon_bosskey == 'remove' and scene == 0x0A):
|
if (world.shuffle_bosskeys == 'remove' and scene != 0x0A) or (
|
||||||
|
world.shuffle_ganon_bosskey == 'remove' and scene == 0x0A and not world.triforce_hunt):
|
||||||
if actor_id == 0x002E and actor_type == 0x05:
|
if actor_id == 0x002E and actor_type == 0x05:
|
||||||
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
||||||
|
|
||||||
|
@ -2109,23 +2110,20 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
|
||||||
rom.write_int16(location.address1, location.item.index)
|
rom.write_int16(location.address1, location.item.index)
|
||||||
else:
|
else:
|
||||||
if location.item.trap:
|
if location.item.trap:
|
||||||
item_display = location.item.looks_like_item
|
item_display = world.trap_appearances[location.address]
|
||||||
elif location.item.game != "Ocarina of Time":
|
|
||||||
item_display = location.item
|
|
||||||
if location.item.advancement:
|
|
||||||
item_display.index = 0xCB
|
|
||||||
else:
|
|
||||||
item_display.index = 0xCC
|
|
||||||
item_display.special = {}
|
|
||||||
else:
|
else:
|
||||||
item_display = location.item
|
item_display = location.item
|
||||||
|
|
||||||
# bottles in shops should look like empty bottles
|
# bottles in shops should look like empty bottles
|
||||||
# so that that are different than normal shop refils
|
# so that that are different than normal shop refils
|
||||||
|
if location.item.trap or location.item.game == "Ocarina of Time":
|
||||||
if 'shop_object' in item_display.special:
|
if 'shop_object' in item_display.special:
|
||||||
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
||||||
else:
|
else:
|
||||||
rom_item = read_rom_item(rom, item_display.index)
|
rom_item = read_rom_item(rom, item_display.index)
|
||||||
|
else:
|
||||||
|
display_index = 0xCB if location.item.advancement else 0xCC
|
||||||
|
rom_item = read_rom_item(rom, display_index)
|
||||||
|
|
||||||
shop_objs.add(rom_item['object_id'])
|
shop_objs.add(rom_item['object_id'])
|
||||||
shop_id = world.current_shop_id
|
shop_id = world.current_shop_id
|
||||||
|
|
|
@ -178,6 +178,10 @@ class OOTWorld(World):
|
||||||
if self.skip_child_zelda:
|
if self.skip_child_zelda:
|
||||||
self.shuffle_weird_egg = False
|
self.shuffle_weird_egg = False
|
||||||
|
|
||||||
|
# Ganon boss key should not be in itempool in triforce hunt
|
||||||
|
if self.triforce_hunt:
|
||||||
|
self.shuffle_ganon_bosskey = 'remove'
|
||||||
|
|
||||||
# Determine skipped trials in GT
|
# Determine skipped trials in GT
|
||||||
# This needs to be done before the logic rules in GT are parsed
|
# This needs to be done before the logic rules in GT are parsed
|
||||||
trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light']
|
trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light']
|
||||||
|
@ -803,9 +807,10 @@ class OOTWorld(World):
|
||||||
|
|
||||||
with i_o_limiter:
|
with i_o_limiter:
|
||||||
# Make traps appear as other random items
|
# Make traps appear as other random items
|
||||||
ice_traps = [loc.item for loc in self.get_locations() if loc.item.trap]
|
trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap]
|
||||||
for trap in ice_traps:
|
self.trap_appearances = {}
|
||||||
trap.looks_like_item = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name)
|
for loc_id in trap_location_ids:
|
||||||
|
self.trap_appearances[loc_id] = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name)
|
||||||
|
|
||||||
# Seed hint RNG, used for ganon text lines also
|
# Seed hint RNG, used for ganon text lines also
|
||||||
self.hint_rng = self.world.slot_seeds[self.player]
|
self.hint_rng = self.world.slot_seeds[self.player]
|
||||||
|
|
Loading…
Reference in New Issue