Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions src/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def checkRequireStringForArea(state: CollectionState, area: dict):
requires_list = area["requires"]

# Get the "real" item counts of item in the pool/placed/starting_items
items_counts = world.get_item_counts(player)
items_counts = world.get_item_counts(player, only_progression=True)

# Preparing some variables for exception messages
area_type = "region" if area.get("is_region",False) else "location"
Expand Down Expand Up @@ -280,8 +280,7 @@ def fullLocationOrRegionCheck(state: CollectionState, area: dict):
return checkRequireStringForArea(state, area)
else: # item access is in dict form
return checkRequireDictForArea(state, area)
# calling get_item_counts here make sure the item_counts cache is created correctly for UT
world.get_item_counts(player, True)

used_location_names = []
# Region access rules
for region in regionMap.keys():
Expand Down Expand Up @@ -404,15 +403,15 @@ def ItemValue(state: CollectionState, player: int, valueCount: str):


# Two useful functions to make require work if an item is disabled instead of making it inaccessible
def OptOne(world: World, item: str, items_counts: Optional[dict] = None):
def OptOne(world: "ManualWorld", item: str, items_counts: Optional[dict] = None):
"""Check if the passed item (with or without ||) is enabled, then this returns |item:count|
where count is clamped to the maximum number of said item in the itempool.\n
Eg. requires: "{OptOne(|DisabledItem|)} and |other items|" become "|DisabledItem:0| and |other items|" if the item is disabled.
"""
if item == "":
return "" #Skip this function if item is left blank
if not items_counts:
items_counts = world.get_item_counts()
items_counts = world.get_item_counts(only_progression=True)

require_type = 'item'

Expand Down Expand Up @@ -443,14 +442,14 @@ def OptOne(world: World, item: str, items_counts: Optional[dict] = None):
return f"|{item_name}:{item_count}|"

# OptAll check the passed require string and loop every item to check if they're enabled,
def OptAll(world: World, multiworld: MultiWorld, state: CollectionState, player: int, requires: str):
def OptAll(world: "ManualWorld", requires: str):
"""Check the passed require string and loop every item to check if they're enabled,
then returns the require string with items counts adjusted using OptOne\n
eg. requires: "{OptAll(|DisabledItem| and |@CategoryWithModifedCount:10|)} and |other items|"
become "|DisabledItem:0| and |@CategoryWithModifedCount:2| and |other items|" """
requires_list = requires

items_counts = world.get_item_counts()
items_counts = world.get_item_counts(only_progression=True)

functions = {}
if requires_list == "":
Expand Down
38 changes: 28 additions & 10 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import os
import json
from typing import Callable, Optional
from typing import Callable, Optional, Counter
import webbrowser

import Utils
Expand Down Expand Up @@ -58,7 +58,8 @@ class ManualWorld(World):

filler_item_name = filler_item_name

item_counts = {}
item_counts: dict[int, Counter[str]] = {}
item_counts_progression: dict[int, Counter[str]] = {}
start_inventory = {}

location_id_to_name = location_id_to_name
Expand Down Expand Up @@ -110,11 +111,11 @@ def create_regions(self):

def create_items(self):
# Generate item pool
pool = []
pool: list[Item] = []
traps = []
configured_item_names = self.item_id_to_name.copy()

items_config: dict[str, int|dict[str, int]] = {}
items_config: dict[str, int|dict[ItemClassification | str | int, int]] = {}
for name in configured_item_names.values():
if name == "__Victory__": continue
if name == filler_item_name: continue # intentionally using the Game.py filler_item_name here because it's a non-Items item
Expand Down Expand Up @@ -239,6 +240,10 @@ def create_items(self):
# then will remove specific item placements below from the overall pool
self.multiworld.itempool += pool

real_pool = pool + items_started
self.item_counts[self.player] = self.get_item_counts(pool=real_pool)
self.item_counts_progression[self.player] = self.get_item_counts(pool=real_pool, only_progression=True)

def create_item(self, name: str, class_override: Optional['ItemClassification']=None) -> Item:
name = before_create_item(name, self, self.multiworld, self.player)

Expand Down Expand Up @@ -472,15 +477,28 @@ def adjust_filler_items(self, item_pool, traps):

return item_pool

def get_item_counts(self, player: Optional[int] = None, reset: bool = False) -> dict[str, int]:
"""returns the player real item count"""
def get_item_counts(self, player: Optional[int] = None, pool: list[Item] | None | bool = None, only_progression: bool = False) -> Counter[str]:
"""Returns the player real item counts.\n
If you provide an item pool using the pool argument, then it's item counts will be returned.
Otherwise, this function will only work after create_items, before then an empty Counter is returned.\n
The only_progression argument let you filter the items to only get the count of progression items."""
if player is None:
player = self.player

if not self.item_counts.get(player, {}) or reset:
real_pool = get_items_for_player(self.multiworld, player, True)
self.item_counts[player] = {i.name: real_pool.count(i) for i in real_pool}
return self.item_counts.get(player)
if isinstance(pool, bool):
Utils.deprecate("the 'reset' argument of get_item_counts has been deprecated to increase the stability of item counts.\
\nIt should be removed. If you require a new up to date count you can get it using the 'pool' argument.\
\nThat result wont be saved to world unless you override the values of world.item_counts_progression or world.item_counts depending on if you counted only the items with progresion or not.")
pool = None

if pool is not None:
return Counter([i.name for i in pool if not only_progression or i.advancement])

if only_progression:
return self.item_counts_progression.get(player, Counter())
else:
return self.item_counts.get(player, Counter())


def client_data(self):
return {
Expand Down