Core: add generic handling of excluded locations

Currently there can be locations that are marked as excluded,
but don't have rules to enforce it, while fill has special handling
for excluded locations already.

This change removes special rules, and adds a generic rule instead.
This commit is contained in:
black-sliver 2022-10-31 00:47:23 +01:00
parent 2db55ac50b
commit 0ed3baabd4
5 changed files with 11 additions and 8 deletions

View File

@ -1115,13 +1115,15 @@ 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) or (self.item_rule(item) and (not check_access or self.can_reach(state))) return (self.always_allow(state, item)
or ((self.progress_type != LocationProgressType.EXCLUDED or not (item.advancement or item.useful))
and self.item_rule(item)
and (not check_access or self.can_reach(state))))
def can_reach(self, state: CollectionState) -> bool: def can_reach(self, state: CollectionState) -> bool:
# self.access_rule computes faster on average, so placing it first for faster abort # self.access_rule computes faster on average, so placing it first for faster abort
if self.access_rule(state) and self.parent_region.can_reach(state): assert self.parent_region, "Can't reach location without region"
return True return self.access_rule(state) and self.parent_region.can_reach(state)
return False
def place_locked_item(self, item: Item): def place_locked_item(self, item: Item):
if self.item: if self.item:

View File

@ -102,7 +102,7 @@ Locations are places where items can be located in your game. This may be chests
or boss drops for RPG-like games but could also be progress in a research tree. or boss drops for RPG-like games but could also be progress in a research tree.
Each location has a `name` and an `id` (a.k.a. "code" or "address"), is placed Each location has a `name` and an `id` (a.k.a. "code" or "address"), is placed
in a Region and has access rules. in a Region, has access rules and a classification.
The name needs to be unique in each game and must not be numeric (has to The name needs to be unique in each game and must not be numeric (has to
contain least 1 letter or symbol). The ID needs to be unique across all games contain least 1 letter or symbol). The ID needs to be unique across all games
and is best in the same range as the item IDs. and is best in the same range as the item IDs.
@ -110,6 +110,10 @@ World-specific IDs are 1 to 2<sup>53</sup>-1, IDs ≤ 0 are global and reserved.
Special locations with ID `None` can hold events. Special locations with ID `None` can hold events.
Classification is one of `LocationProgressType.DEFAULT`, `PRIORITY` or `EXCLUDED`.
The Fill algorithm will fill priority first, giving higher chance of it being
required, and not place progression or useful items in excluded locations.
### Items ### Items
Items are all things that can "drop" for your game. This may be RPG items like Items are all things that can "drop" for your game. This may be RPG items like

View File

@ -89,7 +89,6 @@ def exclusion_rules(world: MultiWorld, player: int, exclude_locations: typing.Se
if loc_name not in world.worlds[player].location_name_to_id: if loc_name not in world.worlds[player].location_name_to_id:
raise Exception(f"Unable to exclude location {loc_name} in player {player}'s world.") from e raise Exception(f"Unable to exclude location {loc_name} in player {player}'s world.") from e
else: else:
add_item_rule(location, lambda i: not (i.advancement or i.useful))
location.progress_type = LocationProgressType.EXCLUDED location.progress_type = LocationProgressType.EXCLUDED

View File

@ -14,7 +14,6 @@ def create_region(world: MultiWorld, player: int, name: str, locations_per_regio
ret.locations.append(location) ret.locations.append(location)
if world.randomize_hidden_items[player].value == 2 and "Hidden" in location.name: if world.randomize_hidden_items[player].value == 2 and "Hidden" in location.name:
location.progress_type = LocationProgressType.EXCLUDED location.progress_type = LocationProgressType.EXCLUDED
add_item_rule(location, lambda i: not (i.advancement or i.useful))
if exits: if exits:
for exit in exits: for exit in exits:
ret.exits.append(Entrance(player, exit, ret)) ret.exits.append(Entrance(player, exit, ret))

View File

@ -242,7 +242,6 @@ class SoEWorld(World):
for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count): for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count):
assert location.name != "Energy Core #285", "Error in sphere generation" assert location.name != "Energy Core #285", "Error in sphere generation"
location.progress_type = LocationProgressType.EXCLUDED location.progress_type = LocationProgressType.EXCLUDED
# TODO: do we need to set an item rule?
def sphere1_blocked_items_rule(item): def sphere1_blocked_items_rule(item):
if isinstance(item, SoEItem): if isinstance(item, SoEItem):