Skip to content

Commit

Permalink
Merge 'Handle Gerudo Fortress heart piece in logic' (OoTRandomizer#2179)
Browse files Browse the repository at this point in the history
  • Loading branch information
fenhl committed Dec 12, 2024
2 parents e180707 + a0f3423 commit 16c48c4
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 31 deletions.
1 change: 1 addition & 0 deletions ItemList.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ class GetItemId(IntEnum):
'Gerudo Membership Card': ('Item', True, GetItemId.GI_GERUDOS_CARD, None),
'Heart Container': ('Item', True, GetItemId.GI_HEART_CONTAINER, {'alias': ('Piece of Heart', 4), 'progressive': float('Inf')}),
'Piece of Heart': ('Item', True, GetItemId.GI_HEART_PIECE, {'progressive': float('Inf')}),
'Piece of Heart (Out of Logic)': ('Item', None, GetItemId.GI_HEART_PIECE, None),
'Boss Key': ('BossKey', True, GetItemId.GI_BOSS_KEY, None),
'Compass': ('Compass', None, GetItemId.GI_COMPASS, None),
'Map': ('Map', None, GetItemId.GI_DUNGEON_MAP, None),
Expand Down
39 changes: 21 additions & 18 deletions ItemPool.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from World import World


plentiful_items: list[str] = ([
plentiful_items: list[str] = [
'Biggoron Sword',
'Boomerang',
'Lens of Truth',
Expand All @@ -36,14 +36,8 @@
'Bow',
'Slingshot',
'Bomb Bag',
'Double Defense'] +
['Heart Container'] * 8
)

# Ludicrous replaces all health upgrades with heart containers
# as done in plentiful. The item list is used separately to
# dynamically replace all junk with even levels of each item.
ludicrous_health: list[str] = ['Heart Container'] * 8
'Double Defense',
]

# List of items that can be multiplied in ludicrous mode.
# Used to filter the pre-plando pool for candidates instead
Expand Down Expand Up @@ -219,12 +213,8 @@
)

item_difficulty_max: dict[str, dict[str, int]] = {
'ludicrous': {
'Piece of Heart': 3,
},
'plentiful': {
'Piece of Heart': 3,
},
'ludicrous': {},
'plentiful': {},
'balanced': {},
'scarce': {
'Bombchus (5)': 1,
Expand Down Expand Up @@ -544,9 +534,6 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
if world.settings.shuffle_individual_ocarina_notes:
pending_junk_pool.extend(['Ocarina A Button', 'Ocarina C up Button', 'Ocarina C left Button', 'Ocarina C down Button', 'Ocarina C right Button'])

if world.settings.item_pool_value == 'ludicrous':
pending_junk_pool.extend(ludicrous_health)

if world.settings.triforce_hunt:
pending_junk_pool.extend(['Triforce Piece'] * world.settings.triforce_count_per_world)
if world.settings.shuffle_individual_ocarina_notes:
Expand Down Expand Up @@ -713,6 +700,15 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
else:
shuffle_item = False

# Gerudo Fortress Freestanding Heart Piece
elif location.vanilla_item == 'Piece of Heart (Out of Logic)':
shuffle_item = world.settings.shuffle_gerudo_fortress_heart_piece == 'shuffle'
if world.settings.shuffle_hideout_entrances or world.settings.logic_rules == 'glitched':
if world.settings.shuffle_hideout_entrances and world.settings.shuffle_gerudo_fortress_heart_piece == 'remove':
item = IGNORE_LOCATION
else:
item = 'Piece of Heart'

# Thieves' Hideout
elif location.vanilla_item == 'Small Key (Thieves Hideout)':
shuffle_item = world.settings.shuffle_hideoutkeys != 'vanilla'
Expand Down Expand Up @@ -961,6 +957,13 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:

for item, maximum in item_difficulty_max[world.settings.item_pool_value].items():
replace_max_item(pool, item, maximum)
# Dynamically condense regular heart pieces into heart containers depending on how many are in the pool
# (which varies based on the Shuffle Gerudo Fortress Heart Piece setting)
if world.settings.item_pool_value in ('plentiful', 'ludicrous'):
indices = [items_idx for items_idx, val in enumerate(pool) if val == 'Piece of Heart']
num_full_hearts = (len(indices) // 4) * 4
for hearts_idx, items_idx in enumerate(indices[:num_full_hearts]):
pool[items_idx] = 'Heart Container' if hearts_idx % 4 == 0 else get_junk_item()[0]

world.distribution.alter_pool(world, pool)

Expand Down
1 change: 1 addition & 0 deletions LocationList.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ def shop_address(shop_id: int, shelf_id: int) -> int:

# Gerudo's Fortress
("GF Chest", ("Chest", 0x5D, 0x00, None, 'Piece of Heart', ("Gerudo's Fortress", "Chests",))),
("GF Freestanding PoH", ("Collectable", 0x5D, 0x01, None, 'Piece of Heart (Out of Logic)', ("Gerudo's Fortress", "Freestandings",))),
("GF HBA 1000 Points", ("NPC", 0x5D, 0x3E, None, 'Piece of Heart', ("Gerudo's Fortress", "Minigames",))),
("GF HBA 1500 Points", ("NPC", 0x5D, 0x30, None, 'Bow', ("Gerudo's Fortress", "Minigames",))),
("GF GS Top Floor", ("GS Token", 0x14, 0x02, None, 'Gold Skulltula Token', ("Gerudo's Fortress", "Gold Skulltulas",))),
Expand Down
6 changes: 4 additions & 2 deletions Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ def copy(addr, size):
# Add the extended texture data to the DMA table.
rom.update_dmadata_record_by_key(None, extended_textures_start, end_address)

save_context = SaveContext()

# Create an option so that recovery hearts no longer drop by changing the code which checks Link's health when an item is spawned.
if world.settings.no_collectible_hearts:
symbol = rom.sym('NO_COLLECTIBLE_HEARTS')
Expand Down Expand Up @@ -792,6 +794,8 @@ def set_entrance_updates(entrances: Iterable[Entrance]) -> None:

if world.settings.shuffle_hideout_entrances:
rom.write_byte(rom.sym('HIDEOUT_SHUFFLED'), 1)
if world.settings.shuffle_gerudo_fortress_heart_piece == 'remove':
save_context.write_permanent_flag(Scenes.GERUDO_FORTRESS, FlagType.COLLECT, 0x3, 0x02)

if world.shuffle_dungeon_entrances:
rom.write_byte(rom.sym('DUNGEONS_SHUFFLED'), 1)
Expand Down Expand Up @@ -839,8 +843,6 @@ def set_entrance_updates(entrances: Iterable[Entrance]) -> None:
rom.write_bytes(rom.sym('CFG_FILE_SELECT_HASH'), spoiler.file_hash)
rom.write_bytes(rom.sym('PASSWORD'), spoiler.password)

save_context = SaveContext()

# Initial Save Data
if not world.settings.useful_cutscenes and 'Forest Temple' not in world.settings.dungeon_shortcuts:
save_context.write_bits(0x00D4 + 0x03 * 0x1C + 0x04 + 0x0, 0x08) # Forest Temple switch flag (Poe Sisters cutscene)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ issue. You should always Hard Reset to avoid this issue entirely.
* New `Require Lens of Truth for Treasure Chest Game` setting.
* New option `Market Big Poes` for the `Misc. Hints` setting.
* New setting `Shuffle Ganon's Tower Entrance` to allow shuffling the boss entrance to Ganon himself.
* New setting to control the behavior of the child-only heart piece in Gerudo Fortress when Thieves' Hideout entrances are shuffled.

#### Bug fixes
* Ocarina buttons required to play the Song of Time are now part of the `path of time` goal.
Expand Down
3 changes: 2 additions & 1 deletion SaveContext.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ class Scenes(IntEnum):
WATER_TEMPLE = 0x05
SPIRIT_TEMPLE = 0x06
SHADOW_TEMPLE = 0x07
# Bean patch scenes
# Various overworld scenes
GRAVEYARD = 0x53
ZORAS_RIVER = 0x54
KOKIRI_FOREST = 0x55
LAKE_HYLIA = 0x57
GERUDO_VALLEY = 0x5A
LOST_WOODS = 0x5B
DESERT_COLOSSUS = 0x5C
GERUDO_FORTRESS = 0x5D
DEATH_MOUNTAIN_TRAIL = 0x60
DEATH_MOUNTAIN_CRATER = 0x61
GORON_CITY = 0x62
Expand Down
48 changes: 41 additions & 7 deletions SettingsList.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ class SettingInfos:
'settings': [
'open_forest', 'open_kakariko', 'open_door_of_time', 'zora_fountain', 'gerudo_fortress', 'dungeon_shortcuts_choice',
'dungeon_shortcuts', 'trials_random', 'trials',
'starting_age', 'shuffle_interior_entrances', 'shuffle_hideout_entrances',
'starting_age', 'shuffle_interior_entrances', 'shuffle_hideout_entrances', 'shuffle_gerudo_fortress_heart_piece',
'shuffle_grotto_entrances', 'shuffle_dungeon_entrances',
'shuffle_bosses', 'shuffle_overworld_entrances', 'shuffle_gerudo_valley_river_exit', 'owl_drops', 'warp_songs', 'spawn_positions',
'triforce_hunt', 'triforce_count_per_world', 'triforce_goal_per_world', 'free_bombchu_drops', 'one_item_per_dungeon',
Expand Down Expand Up @@ -664,7 +664,7 @@ class SettingInfos:
''',
disable = {
'glitchless': {'settings': ['tricks_list_msg']},
'glitched': {'settings': ['allowed_tricks', 'shuffle_interior_entrances', 'shuffle_hideout_entrances', 'shuffle_grotto_entrances',
'glitched': {'settings': ['allowed_tricks', 'shuffle_interior_entrances', 'shuffle_hideout_entrances', 'shuffle_gerudo_fortress_heart_piece', 'shuffle_grotto_entrances',
'shuffle_dungeon_entrances', 'shuffle_overworld_entrances', 'shuffle_gerudo_valley_river_exit', 'owl_drops',
'warp_songs', 'spawn_positions', 'mq_dungeons_mode', 'mq_dungeons_specific',
'mq_dungeons_count', 'shuffle_bosses', 'shuffle_ganon_tower', 'dungeon_shortcuts', 'deadly_bonks',
Expand Down Expand Up @@ -1667,7 +1667,7 @@ class SettingInfos:
''',
shared = True,
disable = {
'off': {'settings': ['shuffle_hideout_entrances']},
'off': {'settings': ['shuffle_hideout_entrances', 'shuffle_gerudo_fortress_heart_piece']},
},
gui_params = {
'randomize_key': 'randomize_settings',
Expand All @@ -1687,18 +1687,52 @@ class SettingInfos:
Note that savewarping in any room of Thieves' Hideout
always takes you to the first room (with 1 torch).
There is an extra heart piece on the balcony above the jail in
Gerudo's Fortress if accessed as child. This is not shuffled
and not considered in logic.
''',
default = False,
shared = True,
disable = {
False: {'settings': ['shuffle_gerudo_fortress_heart_piece']},
},
gui_params = {
'randomize_key': 'randomize_settings',
},
)

shuffle_gerudo_fortress_heart_piece = Combobox(
gui_text = 'Shuffle Gerudo Fortress Heart Piece',
default = 'remove',
choices = {
'remove': 'Remove',
'vanilla': 'Vanilla',
'shuffle': 'Shuffle',
},
gui_tooltip = '''\
There is an extra heart piece on the balcony above the jail in
Gerudo's Fortress if accessed as child. Normally, this is not
reachable without glitches, so it's not shuffled and not
considered in logic. With the Thieves' Hideout entrances
shuffled, it becomes reachable, so use this setting to decide
how to handle that.
'Remove':
The heart piece is completely removed from the game. There is
no freestanding item to collect above the jail.
'Vanilla':
The heart piece exists as an unshuffled heart piece. It is
considered in logic for the purpose of heart win conditions.
'Shuffle':
There is a shuffled item above the jail and the extra heart
piece is shuffled into the item pool. Both are considered in
logic.
''',
shared = True,
gui_params = {
"hide_when_disabled": True,
},
)

shuffle_grotto_entrances = Checkbutton(
gui_text = 'Shuffle Grotto Entrances',
gui_tooltip = '''\
Expand Down
1 change: 1 addition & 0 deletions data/Glitched World/Overworld.json
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@
"GF Chest": "(is_child and can_mega) or
(is_adult and can_use(Hover_Boots) or can_use(Scarecrow) or can_use(Longshot) or can_mega)",
#// known softlock if child opens this chest, so only put it in logic for adult
"GF Freestanding PoH": "is_child and can_hover",
"GF HBA 1000 Points": "
Gerudo_Membership_Card and can_ride_epona and Bow and is_adult",
"GF HBA 1500 Points": "
Expand Down
15 changes: 13 additions & 2 deletions data/World/Overworld.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"exits": {
"Root Exits": "is_starting_age or Time_Travel",
"HC Garden Skippable Locations": "skip_child_zelda",
"Beyond Door of Time Skippable Locations": "skip_reward_from_rauru"
"Beyond Door of Time Skippable Locations": "skip_reward_from_rauru",
# Hack to make ALR work if hideout entrances are unshuffled
"GF Above Jail Child Locations": "shuffle_gerudo_fortress_heart_piece == 'remove' or not shuffle_hideout_entrances"
}
},
{
Expand Down Expand Up @@ -854,7 +856,16 @@
"Hideout Hall to Balcony": "True",
"Gerudo Fortress": "True",
"GF Chest Roof": "can_use(Longshot)",
"GF Break Room Entrance": "damage_multiplier != 'ohko' or can_use(Nayrus_Love)"
"GF Break Room Entrance": "damage_multiplier != 'ohko' or can_use(Nayrus_Love)",
"GF Above Jail Child Locations": "is_child"
}
},
{
"region_name": "GF Above Jail Child Locations",
"scene": "Gerudo Fortress",
"hint": "GERUDO_FORTRESS",
"locations": {
"GF Freestanding PoH": "True"
}
},
{
Expand Down
11 changes: 11 additions & 0 deletions data/presets_default.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -247,6 +248,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -452,6 +454,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -664,6 +667,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -879,6 +883,7 @@
"empty_dungeons_count": 3,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -1090,6 +1095,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -1301,6 +1307,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "all",
"shuffle_hideout_entrances": true,
"shuffle_gerudo_fortress_heart_piece": "shuffle",
"shuffle_grotto_entrances": true,
"shuffle_dungeon_entrances": "all",
"shuffle_bosses": "full",
Expand Down Expand Up @@ -1686,6 +1693,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -1882,6 +1890,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "off",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -2113,6 +2122,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "remove",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "simple",
"shuffle_bosses": "off",
Expand Down Expand Up @@ -2324,6 +2334,7 @@
"empty_dungeons_count": 2,
"shuffle_interior_entrances": "off",
"shuffle_hideout_entrances": false,
"shuffle_gerudo_fortress_heart_piece": "vanilla",
"shuffle_grotto_entrances": false,
"shuffle_dungeon_entrances": "all",
"shuffle_bosses": "off",
Expand Down
1 change: 1 addition & 0 deletions data/settings_mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"empty_dungeons_count",
"shuffle_interior_entrances",
"shuffle_hideout_entrances",
"shuffle_gerudo_fortress_heart_piece",
"shuffle_grotto_entrances",
"shuffle_dungeon_entrances",
"shuffle_bosses",
Expand Down
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '8.2.38'
__version__ = '8.2.39'

# This is a supplemental version number for branches based off of main dev.
supplementary_version = 0
Expand Down

0 comments on commit 16c48c4

Please sign in to comment.