From 0ed3baabd42fb4014abaf2cce533870b7a7b9a30 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 31 Oct 2022 00:47:23 +0100 Subject: [PATCH] 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. --- BaseClasses.py | 10 ++++++---- docs/world api.md | 6 +++++- worlds/generic/Rules.py | 1 - worlds/pokemon_rb/regions.py | 1 - worlds/soe/__init__.py | 1 - 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index b8955500..8d641b0b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1115,13 +1115,15 @@ class Location: self.parent_region = parent 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: # 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): - return True - return False + assert self.parent_region, "Can't reach location without region" + return self.access_rule(state) and self.parent_region.can_reach(state) def place_locked_item(self, item: Item): if self.item: diff --git a/docs/world api.md b/docs/world api.md index a43d61e5..d95627dd 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -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. 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 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. @@ -110,6 +110,10 @@ World-specific IDs are 1 to 253-1, IDs ≤ 0 are global and reserved. 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 are all things that can "drop" for your game. This may be RPG items like diff --git a/worlds/generic/Rules.py b/worlds/generic/Rules.py index 865dc395..98fb560a 100644 --- a/worlds/generic/Rules.py +++ b/worlds/generic/Rules.py @@ -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: raise Exception(f"Unable to exclude location {loc_name} in player {player}'s world.") from e else: - add_item_rule(location, lambda i: not (i.advancement or i.useful)) location.progress_type = LocationProgressType.EXCLUDED diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py index 5141f06c..9640e0a8 100644 --- a/worlds/pokemon_rb/regions.py +++ b/worlds/pokemon_rb/regions.py @@ -14,7 +14,6 @@ def create_region(world: MultiWorld, player: int, name: str, locations_per_regio ret.locations.append(location) if world.randomize_hidden_items[player].value == 2 and "Hidden" in location.name: location.progress_type = LocationProgressType.EXCLUDED - add_item_rule(location, lambda i: not (i.advancement or i.useful)) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) diff --git a/worlds/soe/__init__.py b/worlds/soe/__init__.py index 864d2daa..f45508bf 100644 --- a/worlds/soe/__init__.py +++ b/worlds/soe/__init__.py @@ -242,7 +242,6 @@ class SoEWorld(World): for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count): assert location.name != "Energy Core #285", "Error in sphere generation" location.progress_type = LocationProgressType.EXCLUDED - # TODO: do we need to set an item rule? def sphere1_blocked_items_rule(item): if isinstance(item, SoEItem):