Core: Add a function to allow worlds to easily allow self-locking items (#1383)

* implement function to allow self locking items for items accessibility

* swap some lttp locations to use new functionality

* lambda capture `item_name` and `location`

* don't lambda capture location

* Revert weird visual indent

* make location.always_allow additive

* fix always_allow rule for multiple items

* don't need to lambda capture item_names

* oop

* move player assignment to the beginning

* always_allow should only be for that player so prevent non_local_items

* messenger got merged so have it use this

* Core: fix doc string indentation for allow_self_locking_items

* Core: fix doc string indentation for allow_self_locking_items, number two

---------

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
alwaysintreble 2023-03-13 18:55:34 -05:00 committed by GitHub
parent 060ee926e7
commit 573a1a8402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 56 deletions

View File

@ -966,7 +966,7 @@ class Location:
self.parent_region = parent self.parent_region = parent
def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool: def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool:
return (self.always_allow(state, item) return ((self.always_allow(state, item) and item.name not in state.multiworld.non_local_items[item.player])
or ((self.progress_type != LocationProgressType.EXCLUDED or not (item.advancement or item.useful)) or ((self.progress_type != LocationProgressType.EXCLUDED or not (item.advancement or item.useful))
and self.item_rule(item) and self.item_rule(item)
and (not check_access or self.can_reach(state)))) and (not check_access or self.can_reach(state))))

View File

@ -4,7 +4,7 @@ from typing import Iterator, Set
from BaseClasses import Entrance, MultiWorld from BaseClasses import Entrance, MultiWorld
from worlds.generic.Rules import (add_item_rule, add_rule, forbid_item, from worlds.generic.Rules import (add_item_rule, add_rule, forbid_item,
item_in_locations, item_name, set_rule) item_in_locations, location_item_name, set_rule, allow_self_locking_items)
from . import OverworldGlitchRules from . import OverworldGlitchRules
from .Bosses import GanonDefeatRule from .Bosses import GanonDefeatRule
@ -268,7 +268,7 @@ def global_rules(world, player):
if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]): if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]):
add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.multiworld.get_region('Desert Palace Main (Outer)', player).can_reach(state)) add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.multiworld.get_region('Desert Palace Main (Outer)', player).can_reach(state))
set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or location_item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player))
set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player))
set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player))
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: has_fire_source(state, player)) set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: has_fire_source(state, player))
@ -278,27 +278,27 @@ def global_rules(world, player):
set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player)) set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player))
set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player)) set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player))
set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player))
set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player) or item_name(state, 'Swamp Palace - Big Chest', player) == ('Big Key (Swamp Palace)', player)) set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) allow_self_locking_items(world.get_location('Swamp Palace - Big Chest', player), 'Big Key (Swamp Palace)')
set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player))
if not world.smallkey_shuffle[player] and world.logic[player] not in ['hybridglitches', 'nologic']: if not world.smallkey_shuffle[player] and world.logic[player] not in ['hybridglitches', 'nologic']:
forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player) forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player)
set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player)) set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player))
set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player)) set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player))
set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player)) and state.has('Hammer', player))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) allow_self_locking_items(world.get_location('Thieves\' Town - Big Chest', player), 'Small Key (Thieves Town)')
set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player)) set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player))
set_rule(world.get_entrance('Skull Woods First Section South Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player)) set_rule(world.get_entrance('Skull Woods First Section South Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player))
set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player)) set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player))
set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section
set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 2)) set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 2))
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) allow_self_locking_items(world.get_location('Skull Woods - Big Chest', player), 'Big Key (Skull Woods)')
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and has_sword(state, player)) # sword required for curtain set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and has_sword(state, player)) # sword required for curtain
set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: can_melt_things(state, player)) set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: can_melt_things(state, player))
@ -318,9 +318,9 @@ def global_rules(world, player):
set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state._lttp_has_key('Small Key (Misery Mire)', player, 1) or state._lttp_has_key('Big Key (Misery Mire)', player)) set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state._lttp_has_key('Small Key (Misery Mire)', player, 1) or state._lttp_has_key('Big Key (Misery Mire)', player))
# we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet # we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet
set_rule(world.get_entrance('Misery Mire (West)', player), lambda state: state._lttp_has_key('Small Key (Misery Mire)', player, 2) if (( set_rule(world.get_entrance('Misery Mire (West)', player), lambda state: state._lttp_has_key('Small Key (Misery Mire)', player, 2) if ((
item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or location_item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or
( (
item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state._lttp_has_key('Small Key (Misery Mire)', player, 3)) location_item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state._lttp_has_key('Small Key (Misery Mire)', player, 3))
set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: has_fire_source(state, player)) set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: has_fire_source(state, player))
set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: has_fire_source(state, player)) set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: has_fire_source(state, player))
set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player))
@ -350,12 +350,12 @@ def global_rules(world, player):
set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or ( set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or (
item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3))) location_item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3)))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5)) set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5))
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or ( set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or (
item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 4))) location_item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 4)))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5)) set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5))
@ -369,7 +369,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player))) set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player)))
set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 4) or ( set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 4) or (
item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state._lttp_has_key('Small Key (Ganons Tower)', player, 3))) location_item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state._lttp_has_key('Small Key (Ganons Tower)', player, 3)))
if world.accessibility[player] != 'locations': if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state._lttp_has_key('Small Key (Ganons Tower)', player, 3) and state.can_reach('Ganons Tower (Hookshot Room)', 'region', player)) set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state._lttp_has_key('Small Key (Ganons Tower)', player, 3) and state.can_reach('Ganons Tower (Hookshot Room)', 'region', player))
@ -911,7 +911,7 @@ def set_trock_key_rules(world, player):
# Now we need to set rules based on which entrances we have access to. The most important point is whether we have back access. If we have back access, we # Now we need to set rules based on which entrances we have access to. The most important point is whether we have back access. If we have back access, we
# might open all the locked doors in any order so we need maximally restrictive rules. # might open all the locked doors in any order so we need maximally restrictive rules.
if can_reach_back: if can_reach_back:
set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state._lttp_has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state._lttp_has_key('Small Key (Turtle Rock)', player, 4) or location_item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player)))
set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 4)) set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 4))
# Only consider wasting the key on the Trinexx door for going from the front entrance to middle section. If other key doors are accessible, then these doors can be avoided # Only consider wasting the key on the Trinexx door for going from the front entrance to middle section. If other key doors are accessible, then these doors can be avoided
set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 3)) set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 3))
@ -931,7 +931,7 @@ def set_trock_key_rules(world, player):
def tr_big_key_chest_keys_needed(state): def tr_big_key_chest_keys_needed(state):
# This function handles the key requirements for the TR Big Chest in the situations it having the Big Key should logically require 2 keys, small key # This function handles the key requirements for the TR Big Chest in the situations it having the Big Key should logically require 2 keys, small key
# should logically require no keys, and anything else should logically require 4 keys. # should logically require no keys, and anything else should logically require 4 keys.
item = item_name(state, 'Turtle Rock - Big Key Chest', player) item = location_item_name(state, 'Turtle Rock - Big Key Chest', player)
if item in [('Small Key (Turtle Rock)', player)]: if item in [('Small Key (Turtle Rock)', player)]:
return 0 return 0
if item in [('Big Key (Turtle Rock)', player)]: if item in [('Big Key (Turtle Rock)', player)]:
@ -1514,4 +1514,4 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
continue continue
if location.name in bunny_accessible_locations: if location.name in bunny_accessible_locations:
continue continue
add_rule(location, get_rule_to_add(entrance.connected_region, location)) add_rule(location, get_rule_to_add(entrance.connected_region, location))

View File

@ -1,7 +1,7 @@
import collections import collections
import typing import typing
from BaseClasses import LocationProgressType, MultiWorld from BaseClasses import LocationProgressType, MultiWorld, Location, Region, Entrance
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
import BaseClasses import BaseClasses
@ -143,14 +143,41 @@ def add_item_rule(location: "BaseClasses.Location", rule: ItemRule, combine: str
def item_in_locations(state: "BaseClasses.CollectionState", item: str, player: int, def item_in_locations(state: "BaseClasses.CollectionState", item: str, player: int,
locations: typing.Sequence["BaseClasses.Location"]) -> bool: locations: typing.Sequence["BaseClasses.Location"]) -> bool:
for location in locations: for location in locations:
if item_name(state, location[0], location[1]) == (item, player): if location_item_name(state, location[0], location[1]) == (item, player):
return True return True
return False return False
def item_name(state: "BaseClasses.CollectionState", location: str, player: int) -> \ def location_item_name(state: "BaseClasses.CollectionState", location: str, player: int) -> \
typing.Optional[typing.Tuple[str, int]]: typing.Optional[typing.Tuple[str, int]]:
location = state.multiworld.get_location(location, player) location = state.multiworld.get_location(location, player)
if location.item is None: if location.item is None:
return None return None
return location.item.name, location.item.player return location.item.name, location.item.player
def allow_self_locking_items(spot: typing.Union[Location, Region], *item_names: str) -> None:
"""
This function sets rules on the supplied spot, such that the supplied item_name(s) can possibly be placed there.
spot: Location or Region that the item(s) are allowed to be placed in
item_names: item name or names that are allowed to be placed in the Location or Region
"""
player = spot.player
def add_allowed_rules(area: typing.Union[Location, Entrance], location: Location) -> None:
def set_always_allow(location: Location, rule: typing.Callable) -> None:
location.always_allow = rule
for item_name in item_names:
add_rule(area, lambda state, item_name=item_name:
location_item_name(state, location.name, player) == (item_name, player), "or")
set_always_allow(location, lambda state, item:
item.player == player and item.name in [item_name for item_name in item_names])
if isinstance(spot, Region):
for entrance in spot.entrances:
for location in spot.locations:
add_allowed_rules(entrance, location)
else:
add_allowed_rules(spot, spot)

View File

@ -1,9 +1,9 @@
from typing import Dict, Callable, Optional, Tuple, Union, TYPE_CHECKING, List, Iterable from typing import Dict, Callable, TYPE_CHECKING
from BaseClasses import CollectionState, MultiWorld, Location, Region, Entrance, Item from BaseClasses import CollectionState, MultiWorld
from worlds.generic.Rules import set_rule, allow_self_locking_items
from .Options import MessengerAccessibility, Goal from .Options import MessengerAccessibility, Goal
from .Constants import NOTES, PHOBEKINS from .Constants import NOTES, PHOBEKINS
from ..generic.Rules import add_rule, set_rule
if TYPE_CHECKING: if TYPE_CHECKING:
from . import MessengerWorld from . import MessengerWorld
@ -113,39 +113,6 @@ class MessengerRules:
set_self_locking_items(multiworld, self.player) set_self_locking_items(multiworld, self.player)
def location_item_name(state: CollectionState, location_name: str, player: int) -> Optional[Tuple[str, int]]:
location = state.multiworld.get_location(location_name, player)
if location.item is None:
return None
return location.item.name, location.item.player
def allow_self_locking_items(spot: Union[Location, Region], *item_names: str) -> None:
"""
Sets rules on the supplied spot, such that the supplied item_name(s) can possibly be placed there.
:param spot: Location or Region that the item(s) are allowed to be placed in
:param item_names: item name or names that are allowed to be placed in the Location or Region
"""
player = spot.player
def set_always_allow(location: Location, rule: Callable[[CollectionState, Item], bool]) -> None:
location.always_allow = rule
def add_allowed_rules(area: Union[Location, Entrance], location: Location) -> None:
for item_name in item_names:
add_rule(area, lambda state, item_name=item_name:
location_item_name(state, location.name, player) == (item_name, player), "or")
set_always_allow(location, lambda state, item:
item.player == player and item.name in [item_name for item_name in item_names])
if isinstance(spot, Region):
for entrance in spot.entrances:
for location in spot.locations:
add_allowed_rules(entrance, location)
else:
add_allowed_rules(spot, spot)
def set_self_locking_items(multiworld: MultiWorld, player: int) -> None: def set_self_locking_items(multiworld: MultiWorld, player: int) -> None:
# do the ones for seal shuffle on and off first # do the ones for seal shuffle on and off first
allow_self_locking_items(multiworld.get_location("Key of Strength", player), "Power Thistle") allow_self_locking_items(multiworld.get_location("Key of Strength", player), "Power Thistle")