Skip to content

Commit

Permalink
OoS: Added support for self-locking keys (thanks to Ishigh study)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinopony committed Mar 20, 2024
1 parent b786a06 commit f857b9d
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 39 deletions.
39 changes: 36 additions & 3 deletions worlds/tloz_oos/Logic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from BaseClasses import MultiWorld
from worlds.tloz_oos.data.logic.DungeonsLogic import (make_d0_logic, make_d1_logic, make_d2_logic, make_d3_logic,
make_d4_logic, make_d5_logic, make_d6_logic, make_d7_logic,
make_d8_logic)
from worlds.tloz_oos import LOCATIONS_DATA
from worlds.tloz_oos.data.logic.DungeonsLogic import *
from worlds.tloz_oos.data.logic.OverworldLogic import make_holodrum_logic
from worlds.tloz_oos.data.logic.SubrosiaLogic import make_subrosia_logic

Expand Down Expand Up @@ -42,3 +41,37 @@ def create_connections(multiworld: MultiWorld, player: int):
region_1.connect(region_2, None, rule)
if is_two_way:
region_2.connect(region_1, None, rule)


def apply_self_locking_rules(multiworld: MultiWorld, player: int):
if multiworld.worlds[player].options.accessibility == Accessibility.option_locations:
return

MINIMAL_REQUIRED_KEYS_TO_REACH_KEYDOOR = {
"Hero's Cave: Final Chest": 0,
"Gnarled Root Dungeon: Item in Basement": 1,
"Snake's Remains: Chest on Terrace": 2,
"Poison Moth's Lair (1F): Chest in Mimics Room": 1,
"Dancing Dragon Dungeon (1F): Crumbling Room Chest": 2,
"Dancing Dragon Dungeon (1F): Eye Diving Spot Item": 2,
"Unicorn's Cave: Magnet Gloves Chest": 1,
"Unicorn's Cave: Treadmills Basement Item": 3,
"Explorer's Crypt (B1F): Chest in Jumping Stalfos Room": 4, # Not counting poe skip
"Explorer's Crypt (1F): Chest Right of Entrance": 1
}

for location_name, key_count in MINIMAL_REQUIRED_KEYS_TO_REACH_KEYDOOR.items():
location_data = LOCATIONS_DATA[location_name]
dungeon = location_data["dungeon"]
small_key_item_name = f"Small Key ({DUNGEON_NAMES[dungeon]})"
location = multiworld.get_location(location_name, player)
location.always_allow = make_always_allow_lambda(player, small_key_item_name, key_count)


def make_always_allow_lambda(player: int, item_name: str, key_count: int):
if key_count == 0:
return lambda state, item: (item.player == player and item.name == item_name)

return lambda state, item: (item.player == player
and item.name == item_name
and state.has(item_name, player, key_count))
2 changes: 2 additions & 0 deletions worlds/tloz_oos/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ class OracleOfSeasonsCombatDifficulty(Choice):
option_harder = 3
option_insane = 4

default = 2


class OracleOfSeasonsQuickFlute(DefaultOnToggle):
"""
Expand Down
3 changes: 2 additions & 1 deletion worlds/tloz_oos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from worlds.AutoWorld import WebWorld, World
from .Data import *
from worlds.tloz_oos.data.Items import *
from .Logic import create_connections
from .Logic import create_connections, apply_self_locking_rules
from .Options import *
from .data import LOCATIONS_DATA
from .data.Constants import *
Expand Down Expand Up @@ -339,6 +339,7 @@ def exclude_problematic_locations(self):

def set_rules(self):
create_connections(self.multiworld, self.player)
apply_self_locking_rules(self.multiworld, self.player)
self.multiworld.completion_condition[self.player] = lambda state: state.has("_beaten_game", self.player)

def create_item(self, name: str) -> Item:
Expand Down
1 change: 1 addition & 0 deletions worlds/tloz_oos/data/Regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@
"d5 gibdo/zol chest",
"d5 stalfos room",
"d5 magnet ball chest",
"d5 syger lobby",
"d5 post syger",
"d5 basement",
"d5 boss",
Expand Down
103 changes: 68 additions & 35 deletions worlds/tloz_oos/data/logic/DungeonsLogic.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ def make_d0_logic(player: int):
])],

# 1 key
["enter d0", "d0 sword chest", False, lambda state: oos_has_small_keys(state, player, 0, 1)],
["enter d0", "d0 sword chest", False, lambda state: any([
oos_has_small_keys(state, player, 0, 1),
region_holds_small_key(state, player, "d0 sword chest", 0)
])],
]


Expand Down Expand Up @@ -75,7 +78,10 @@ def make_d1_logic(player: int):
# 2 keys
["d1 railway chest", "d1 basement", False, lambda state: all([
oos_has_bombs(state, player),
oos_has_small_keys(state, player, 1, 2),
any([
oos_has_small_keys(state, player, 1, 2),
region_holds_small_key(state, player, "d1 basement", 1)
]),
oos_can_kill_armored_enemy(state, player)
])],
]
Expand Down Expand Up @@ -123,7 +129,10 @@ def make_d2_logic(player: int):
oos_can_kill_d2_far_moblin(state, player)
])
])],
["d2 spinner", "d2 terrace chest", False, lambda state: oos_has_small_keys(state, player, 2, 3)],
["d2 spinner", "d2 terrace chest", False, lambda state: any([
oos_has_small_keys(state, player, 2, 3),
region_holds_small_key(state, player, "d2 terrace chest", 2)
])],
]


Expand Down Expand Up @@ -161,7 +170,10 @@ def make_d3_logic(player: int):

# 2 keys
["d3 water room", "d3 mimic chest", False, lambda state: all([
oos_has_small_keys(state, player, 3, 2),
any([
oos_has_small_keys(state, player, 3, 2),
region_holds_small_key(state, player, "d3 mimic chest", 3)
]),
oos_can_kill_normal_enemy(state, player)
])],
["d3 mimic stairs", "d3 omuai stairs", False, lambda state: all([
Expand Down Expand Up @@ -215,7 +227,7 @@ def make_d4_logic(player: int):
]),
all([ # pushing enemies in the water
oos_has_rod(state, player),
oos_has_boomerang(state, player) # TODO: oos_can_push_enemy?
oos_has_boomerang(state, player)
])
])
])],
Expand Down Expand Up @@ -278,21 +290,30 @@ def make_d4_logic(player: int):
])],

# 5 keys
["d4 final minecart", "d4 cracked floor room", False, lambda state: oos_has_small_keys(state, player, 4, 5)],
["d4 final minecart", "d4 cracked floor room", False, lambda state: any([
oos_has_small_keys(state, player, 4, 5),
region_holds_small_key(state, player, "d4 cracked floor room", 4)
])],
["d4 final minecart", "d4 dive spot", False, lambda state: all([
any([ # hit distant levers
oos_has_magic_boomerang(state, player),
oos_has_slingshot(state, player)
]),
oos_can_jump_2_wide_pit(state, player),
oos_has_small_keys(state, player, 4, 5),
any([
oos_has_small_keys(state, player, 4, 5),
region_holds_small_key(state, player, "d4 dive spot", 4)
]),
oos_has_flippers(state, player)
])],

["d4 cracked floor room", "d4 basement stairs", False, lambda state: any([
oos_has_boomerang(state, player),
oos_has_slingshot(state, player),
oos_option_hard_logic(state, player)
["d4 final minecart", "d4 basement stairs", False, lambda state: all([
oos_has_small_keys(state, player, 4, 5),
any([
oos_has_boomerang(state, player),
oos_has_slingshot(state, player),
oos_option_hard_logic(state, player)
])
])],

["d4 basement stairs", "gohma owl", False, lambda state: oos_can_use_mystery_seeds(state, player)],
Expand Down Expand Up @@ -364,11 +385,6 @@ def make_d5_logic(player: int):
oos_has_magnet_gloves(state, player),
oos_has_cape(state, player),
oos_can_jump_4_wide_pit(state, player),
all([
oos_option_medium_logic(state, player),
oos_can_jump_3_wide_liquid(state, player),
# Not a liquid, but technically equivalent
])
])],

["enter d5", "d5 spiral chest", False, lambda state: any([
Expand All @@ -393,12 +409,6 @@ def make_d5_logic(player: int):

["d5 cart bay", "d5 cart chest", False, lambda state: oos_can_trigger_lever_from_minecart(state, player)],

["enter d5", "d5 pot room", False, lambda state: all([
oos_has_magnet_gloves(state, player),
oos_has_bombs(state, player),
oos_has_feather(state, player)
])],

["d5 cart bay", "d5 spinner chest", False, lambda state: any([
oos_has_magnet_gloves(state, player),
oos_can_jump_5_wide_pit(state, player)
Expand All @@ -415,6 +425,12 @@ def make_d5_logic(player: int):
])
])],

["enter d5", "d5 pot room", False, lambda state: all([
oos_has_magnet_gloves(state, player),
oos_has_bombs(state, player),
oos_has_feather(state, player)
])],

["d5 cart bay", "d5 pot room", False, lambda state: any([
oos_has_feather(state, player),
all([
Expand All @@ -425,17 +441,23 @@ def make_d5_logic(player: int):

["d5 pot room", "d5 gibdo/zol chest", False, lambda state: oos_can_kill_normal_enemy(state, player)],

["d5 cart bay", "d5 stalfos room", False, lambda state: any([
["d5 cart bay", "d5 syger lobby", False, lambda state: any([
oos_has_magnet_gloves(state, player),
oos_has_cape(state, player),
])],

["d5 pot room", "d5 stalfos room", False, lambda state: any([
["d5 pot room", "d5 syger lobby", False, lambda state: any([
oos_has_magnet_gloves(state, player),
oos_has_cape(state, player),
])],

["d5 syger lobby", "d5 stalfos room", False, None],

# 5 keys
["d5 syger lobby", "d5 post syger", False, lambda state: all([
oos_has_small_keys(state, player, 5, 3),
oos_can_kill_armored_enemy(state, player)
])],

["d5 pot room", "d5 magnet ball chest", False, lambda state: all([
any([
oos_has_flippers(state, player),
Expand All @@ -445,16 +467,17 @@ def make_d5_logic(player: int):
oos_can_jump_3_wide_liquid(state, player),
])
]),
oos_has_small_keys(state, player, 5, 5),
])],

["d5 pot room", "d5 post syger", False, lambda state: all([
oos_has_small_keys(state, player, 5, 5),
oos_can_kill_armored_enemy(state, player)
any([
oos_has_small_keys(state, player, 5, 5),
region_holds_small_key(state, player, "d5 magnet ball chest", 5)
])
])],

["d5 post syger", "d5 basement", False, lambda state: all([
oos_has_small_keys(state, player, 5, 5),
any([
oos_has_small_keys(state, player, 5, 5),
region_holds_small_key(state, player, "d5 basement", 5)
]),
state.has("_dropped_d5_magnet_ball", player),
oos_has_magnet_gloves(state, player),
any([
Expand Down Expand Up @@ -671,7 +694,8 @@ def make_d7_logic(player: int):
any([
oos_can_kill_armored_enemy(state, player),
oos_has_shield(state, player), # To push the darknut, the rod not really working
oos_option_medium_logic(state, player) # Pull the right darknut by just going and stalling in the hole
oos_option_medium_logic(state, player)
# Pull the right darknut by just going and stalling in the hole
])
]),
all([
Expand Down Expand Up @@ -716,7 +740,10 @@ def make_d7_logic(player: int):

# 5 keys
["d7 maze chest", "d7 stalfos chest", False, lambda state: all([
oos_has_small_keys(state, player, 7, 5),
any([
oos_has_small_keys(state, player, 7, 5),
region_holds_small_key(state, player, "d7 stalfos chest", 7)
]),
any([
oos_can_jump_5_wide_pit(state, player),
all([
Expand All @@ -729,7 +756,13 @@ def make_d7_logic(player: int):

["d7 stalfos chest", "shining blue owl", False, lambda state: oos_can_use_mystery_seeds(state, player)],

["enter d7", "d7 right of entrance", False, lambda state: oos_has_small_keys(state, player, 7, 5)],
["enter d7", "d7 right of entrance", False, lambda state: any([
oos_has_small_keys(state, player, 7, 5),
all([
oos_has_small_keys(state, player, 7, 1),
region_holds_small_key(state, player, "d7 right of entrance", 7)
])
])],

["d7 maze chest", "d7 boss", False, lambda state: all([
oos_has_boss_key(state, player, 7),
Expand Down
13 changes: 13 additions & 0 deletions worlds/tloz_oos/data/logic/LogicPredicates.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from BaseClasses import CollectionState
from Options import Accessibility
from worlds.tloz_oos.data.Constants import DUNGEON_NAMES, SEASON_ITEMS, ESSENCES, JEWELS


Expand Down Expand Up @@ -942,3 +943,15 @@ def oos_season_in_horon_village(state: CollectionState, player: int, season: str
if oos_get_default_season(state, player, "HORON_VILLAGE") == season:
return True
return oos_has_season(state, player, season)


def region_holds_small_key(state: CollectionState, player: int, region_name: str, dungeon: int):
if state.multiworld.worlds[player].options.accessibility == Accessibility.option_locations:
return False

region = state.multiworld.get_region(region_name, player)
items_in_region = [location.item for location in region.locations if location.item is not None]
for item in items_in_region:
if item.name == f"Small Key ({DUNGEON_NAMES[dungeon]})" and item.player == player:
return True
return False

0 comments on commit f857b9d

Please sign in to comment.