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']
|
||||
|
||||
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:
|
||||
pending_item = pending_junk_pool.pop()
|
||||
if not junk_candidates:
|
||||
|
|
|
@ -49,7 +49,6 @@ class OOTItem(Item):
|
|||
self.type = type
|
||||
self.index = index
|
||||
self.special = special or {}
|
||||
self.looks_like_item = None
|
||||
self.price = special.get('price', None) if special else None
|
||||
self.internal = False
|
||||
|
||||
|
|
|
@ -158,12 +158,12 @@ class TriforceGoal(Range):
|
|||
"""Number of Triforce pieces required to complete the game."""
|
||||
display_name = "Required Triforce Pieces"
|
||||
range_start = 1
|
||||
range_end = 100
|
||||
range_end = 80
|
||||
default = 20
|
||||
|
||||
|
||||
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"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
|
|
|
@ -1844,7 +1844,7 @@ def write_rom_item(rom, item_id, item):
|
|||
|
||||
|
||||
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
|
||||
|
@ -1852,10 +1852,10 @@ def get_override_table_bytes(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
|
||||
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':
|
||||
# This is an AP sendable. It's guaranteed to not be None.
|
||||
if location.item.advancement:
|
||||
|
@ -1869,7 +1869,7 @@ def get_override_entry(player_id, location):
|
|||
|
||||
if location.item.trap:
|
||||
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:
|
||||
looks_like_item_id = 0
|
||||
|
||||
|
@ -2091,7 +2091,8 @@ def get_locked_doors(rom, world):
|
|||
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
||||
|
||||
# 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:
|
||||
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)
|
||||
else:
|
||||
if location.item.trap:
|
||||
item_display = location.item.looks_like_item
|
||||
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 = {}
|
||||
item_display = world.trap_appearances[location.address]
|
||||
else:
|
||||
item_display = location.item
|
||||
|
||||
# bottles in shops should look like empty bottles
|
||||
# so that that are different than normal shop refils
|
||||
if 'shop_object' in item_display.special:
|
||||
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
||||
if location.item.trap or location.item.game == "Ocarina of Time":
|
||||
if 'shop_object' in item_display.special:
|
||||
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
||||
else:
|
||||
rom_item = read_rom_item(rom, item_display.index)
|
||||
else:
|
||||
rom_item = read_rom_item(rom, item_display.index)
|
||||
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_id = world.current_shop_id
|
||||
|
|
|
@ -178,6 +178,10 @@ class OOTWorld(World):
|
|||
if self.skip_child_zelda:
|
||||
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
|
||||
# This needs to be done before the logic rules in GT are parsed
|
||||
trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light']
|
||||
|
@ -803,9 +807,10 @@ class OOTWorld(World):
|
|||
|
||||
with i_o_limiter:
|
||||
# Make traps appear as other random items
|
||||
ice_traps = [loc.item for loc in self.get_locations() if loc.item.trap]
|
||||
for trap in ice_traps:
|
||||
trap.looks_like_item = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name)
|
||||
trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap]
|
||||
self.trap_appearances = {}
|
||||
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
|
||||
self.hint_rng = self.world.slot_seeds[self.player]
|
||||
|
|
Loading…
Reference in New Issue