Early Items option (#1086)
* Early Items option * Early Items description update * Change Early Items to dict * Rewrite early items as extra fill steps * Move if statement up * Document early_items * Move early_items handling before fill_hook * Apply suggestions from code review Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> * Subnautica pre-fill moved to early_items * Subnautica early items fix * Remove unit test bug workaround * beauxq's pr Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
This commit is contained in:
parent
700fe8b75e
commit
4b18920819
|
@ -48,6 +48,7 @@ class MultiWorld():
|
|||
state: CollectionState
|
||||
|
||||
accessibility: Dict[int, Options.Accessibility]
|
||||
early_items: Dict[int, Options.EarlyItems]
|
||||
local_items: Dict[int, Options.LocalItems]
|
||||
non_local_items: Dict[int, Options.NonLocalItems]
|
||||
progression_balancing: Dict[int, Options.ProgressionBalancing]
|
||||
|
|
39
Fill.py
39
Fill.py
|
@ -258,6 +258,45 @@ def distribute_items_restrictive(world: MultiWorld) -> None:
|
|||
usefulitempool: typing.List[Item] = []
|
||||
filleritempool: typing.List[Item] = []
|
||||
|
||||
early_items_count: typing.Dict[typing.Tuple[str, int], int] = {}
|
||||
for player in world.player_ids:
|
||||
for item, count in world.early_items[player].value.items():
|
||||
early_items_count[(item, player)] = count
|
||||
if early_items_count:
|
||||
early_locations: typing.List[Location] = []
|
||||
early_priority_locations: typing.List[Location] = []
|
||||
for loc in reversed(fill_locations):
|
||||
if loc.can_reach(world.state):
|
||||
if loc.progress_type == LocationProgressType.PRIORITY:
|
||||
early_priority_locations.append(loc)
|
||||
else:
|
||||
early_locations.append(loc)
|
||||
fill_locations.remove(loc)
|
||||
|
||||
early_prog_items: typing.List[Item] = []
|
||||
early_rest_items: typing.List[Item] = []
|
||||
for item in reversed(itempool):
|
||||
if (item.name, item.player) in early_items_count:
|
||||
if item.advancement:
|
||||
early_prog_items.append(item)
|
||||
else:
|
||||
early_rest_items.append(item)
|
||||
itempool.remove(item)
|
||||
early_items_count[(item.name, item.player)] -= 1
|
||||
if early_items_count[(item.name, item.player)] == 0:
|
||||
del early_items_count[(item.name, item.player)]
|
||||
fill_restrictive(world, world.state, early_locations, early_rest_items, lock=True)
|
||||
early_locations += early_priority_locations
|
||||
fill_restrictive(world, world.state, early_locations, early_prog_items, lock=True)
|
||||
unplaced_early_items = early_rest_items + early_prog_items
|
||||
if unplaced_early_items:
|
||||
logging.warning(f"Ran out of early locations for early items. Failed to place \
|
||||
{len(unplaced_early_items)} items early.")
|
||||
itempool += unplaced_early_items
|
||||
|
||||
fill_locations += early_locations + early_priority_locations
|
||||
world.random.shuffle(fill_locations)
|
||||
|
||||
for item in itempool:
|
||||
if item.advancement:
|
||||
progitempool.append(item)
|
||||
|
|
|
@ -883,6 +883,11 @@ class NonLocalItems(ItemSet):
|
|||
display_name = "Not Local Items"
|
||||
|
||||
|
||||
class EarlyItems(ItemDict):
|
||||
"""Force the specified items to be in locations that are reachable from the start."""
|
||||
display_name = "Early Items"
|
||||
|
||||
|
||||
class StartInventory(ItemDict):
|
||||
"""Start with these items."""
|
||||
verify_item_name = True
|
||||
|
@ -981,6 +986,7 @@ per_game_common_options = {
|
|||
**common_options, # can be overwritten per-game
|
||||
"local_items": LocalItems,
|
||||
"non_local_items": NonLocalItems,
|
||||
"early_items": EarlyItems,
|
||||
"start_inventory": StartInventory,
|
||||
"start_hints": StartHints,
|
||||
"start_location_hints": StartLocationHints,
|
||||
|
|
|
@ -106,7 +106,7 @@ settings. If a game can be rolled it **must** have a settings section even if it
|
|||
|
||||
Some options in Archipelago can be used by every game but must still be placed within the relevant game's section.
|
||||
|
||||
Currently, these options are `start_inventory`, `start_hints`, `local_items`, `non_local_items`, `start_location_hints`
|
||||
Currently, these options are `start_inventory`, `early_items`, `start_hints`, `local_items`, `non_local_items`, `start_location_hints`
|
||||
, `exclude_locations`, and various plando options.
|
||||
|
||||
See the plando guide for more info on plando options. Plando
|
||||
|
@ -115,6 +115,8 @@ guide: [Archipelago Plando Guide](/tutorial/Archipelago/plando/en)
|
|||
* `start_inventory` will give any items defined here to you at the beginning of your game. The format for this must be
|
||||
the name as it appears in the game files and the amount you would like to start with. For example `Rupees(5): 6` which
|
||||
will give you 30 rupees.
|
||||
* `early_items` is formatted in the same way as `start_inventory` and will force the number of each item specified to be
|
||||
forced into locations that are reachable from the start, before obtaining any items.
|
||||
* `start_hints` gives you free server hints for the defined item/s at the beginning of the game allowing you to hint for
|
||||
the location without using any hint points.
|
||||
* `local_items` will force any items you want to be in your world instead of being in another world.
|
||||
|
@ -172,6 +174,8 @@ A Link to the Past:
|
|||
- Quake
|
||||
non_local_items:
|
||||
- Moon Pearl
|
||||
early_items:
|
||||
Flute: 1
|
||||
start_location_hints:
|
||||
- Spike Cave
|
||||
priority_locations:
|
||||
|
@ -235,6 +239,9 @@ Timespinner:
|
|||
* `local_items` forces the `Bombos`, `Ether`, and `Quake` medallions to all be placed within our own world, meaning we
|
||||
have to find it ourselves.
|
||||
* `non_local_items` forces the `Moon Pearl` to be placed in someone else's world, meaning we won't be able to find it.
|
||||
* `early_items` forces the `Flute` to be placed in a location that is available from the beginning of the game ("Sphere
|
||||
1"). Since it is not specified in `local_items` or `non_local_items`, it can be placed one of these locations in any
|
||||
world.
|
||||
* `start_location_hints` gives us a starting hint for the `Spike Cave` location available at the beginning of the
|
||||
multiworld that can be used for no cost.
|
||||
* `priority_locations` forces a progression item to be placed on the `Link's House` location.
|
||||
|
|
|
@ -175,7 +175,7 @@ item_table: Dict[int, ItemDict] = {
|
|||
'name': 'Thermal Plant Fragment',
|
||||
'tech_type': 'ThermalPlantFragment'},
|
||||
35041: {'classification': ItemClassification.progression,
|
||||
'count': 2,
|
||||
'count': 4,
|
||||
'name': 'Seaglide Fragment',
|
||||
'tech_type': 'SeaglideFragment'},
|
||||
35042: {'classification': ItemClassification.progression,
|
||||
|
|
|
@ -44,14 +44,12 @@ class SubnauticaWorld(World):
|
|||
data_version = 7
|
||||
required_client_version = (0, 3, 5)
|
||||
|
||||
prefill_items: List[Item]
|
||||
creatures_to_scan: List[str]
|
||||
|
||||
def generate_early(self) -> None:
|
||||
self.prefill_items = [
|
||||
self.create_item("Seaglide Fragment"),
|
||||
self.create_item("Seaglide Fragment")
|
||||
]
|
||||
if "Seaglide Fragment" not in self.world.early_items[self.player]:
|
||||
self.world.early_items[self.player].value["Seaglide Fragment"] = 2
|
||||
|
||||
scan_option: Options.AggressiveScanLogic = self.world.creature_scan_logic[self.player]
|
||||
creature_pool = scan_option.get_pool()
|
||||
|
||||
|
@ -149,17 +147,6 @@ class SubnauticaWorld(World):
|
|||
ret.exits.append(Entrance(self.player, region_exit, ret))
|
||||
return ret
|
||||
|
||||
def get_pre_fill_items(self) -> List[Item]:
|
||||
return self.prefill_items
|
||||
|
||||
def pre_fill(self) -> None:
|
||||
reachable = [location for location in self.world.get_reachable_locations(player=self.player)
|
||||
if not location.item]
|
||||
self.world.random.shuffle(reachable)
|
||||
items = self.prefill_items.copy()
|
||||
for item in items:
|
||||
reachable.pop().place_locked_item(item)
|
||||
|
||||
|
||||
class SubnauticaLocation(Location):
|
||||
game: str = "Subnautica"
|
||||
|
|
Loading…
Reference in New Issue