"""Resource definitions for the Village Simulation. Resource effects and decay rates are loaded dynamically from the global config. """ from dataclasses import dataclass from enum import Enum from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from backend.config import SimulationConfig class ResourceType(Enum): """Types of resources available in the simulation.""" MEAT = "meat" BERRIES = "berries" WATER = "water" WOOD = "wood" HIDE = "hide" CLOTHES = "clothes" @dataclass class ResourceEffect: """Effects applied when consuming a resource.""" hunger: int = 0 thirst: int = 0 heat: int = 0 energy: int = 0 def get_resource_effects() -> dict[ResourceType, ResourceEffect]: """Get resource effects from the global config.""" # Import here to avoid circular imports from backend.config import get_config config = get_config() resources = config.resources return { ResourceType.MEAT: ResourceEffect( hunger=resources.meat_hunger, energy=resources.meat_energy, ), ResourceType.BERRIES: ResourceEffect( hunger=resources.berries_hunger, thirst=resources.berries_thirst, ), ResourceType.WATER: ResourceEffect( thirst=resources.water_thirst, ), ResourceType.WOOD: ResourceEffect(), # Used as fuel, not consumed directly ResourceType.HIDE: ResourceEffect(), # Used for crafting ResourceType.CLOTHES: ResourceEffect(), # Passive heat reduction effect } def get_resource_decay_rates() -> dict[ResourceType, Optional[int]]: """Get resource decay rates from the global config.""" # Import here to avoid circular imports from backend.config import get_config config = get_config() resources = config.resources return { ResourceType.MEAT: resources.meat_decay if resources.meat_decay > 0 else None, ResourceType.BERRIES: resources.berries_decay if resources.berries_decay > 0 else None, ResourceType.WATER: None, # Infinite ResourceType.WOOD: None, # Infinite ResourceType.HIDE: None, # Infinite ResourceType.CLOTHES: resources.clothes_decay if resources.clothes_decay > 0 else None, } def get_fire_heat() -> int: """Get heat provided by building a fire.""" from backend.config import get_config return get_config().resources.fire_heat # Cached values for performance _resource_effects_cache: Optional[dict[ResourceType, ResourceEffect]] = None _resource_decay_cache: Optional[dict[ResourceType, Optional[int]]] = None def get_cached_resource_effects() -> dict[ResourceType, ResourceEffect]: """Get cached resource effects.""" global _resource_effects_cache if _resource_effects_cache is None: _resource_effects_cache = get_resource_effects() return _resource_effects_cache def get_cached_resource_decay_rates() -> dict[ResourceType, Optional[int]]: """Get cached resource decay rates.""" global _resource_decay_cache if _resource_decay_cache is None: _resource_decay_cache = get_resource_decay_rates() return _resource_decay_cache def reset_resource_cache() -> None: """Reset resource caches (call when config changes).""" global _resource_effects_cache, _resource_decay_cache _resource_effects_cache = None _resource_decay_cache = None # Accessor classes for backwards compatibility class _ResourceEffectsAccessor: """Accessor that provides dict-like access to resource effects.""" def __getitem__(self, resource_type: ResourceType) -> ResourceEffect: return get_cached_resource_effects()[resource_type] def get(self, resource_type: ResourceType, default=None) -> Optional[ResourceEffect]: return get_cached_resource_effects().get(resource_type, default) def __contains__(self, resource_type: ResourceType) -> bool: return resource_type in get_cached_resource_effects() class _ResourceDecayAccessor: """Accessor that provides dict-like access to resource decay rates.""" def __getitem__(self, resource_type: ResourceType) -> Optional[int]: return get_cached_resource_decay_rates()[resource_type] def get(self, resource_type: ResourceType, default=None) -> Optional[int]: return get_cached_resource_decay_rates().get(resource_type, default) def __contains__(self, resource_type: ResourceType) -> bool: return resource_type in get_cached_resource_decay_rates() # These provide the same interface as before but load from config RESOURCE_EFFECTS = _ResourceEffectsAccessor() RESOURCE_DECAY_RATES = _ResourceDecayAccessor() @dataclass class Resource: """A resource instance in the simulation.""" type: ResourceType quantity: int = 1 created_turn: int = 0 @property def decay_rate(self) -> Optional[int]: """Get the decay rate for this resource type.""" return get_cached_resource_decay_rates()[self.type] @property def effect(self) -> ResourceEffect: """Get the effect of consuming this resource.""" return get_cached_resource_effects()[self.type] def is_expired(self, current_turn: int) -> bool: """Check if the resource has decayed.""" if self.decay_rate is None: return False return (current_turn - self.created_turn) >= self.decay_rate def turns_until_decay(self, current_turn: int) -> Optional[int]: """Get remaining turns until decay, None if infinite.""" if self.decay_rate is None: return None remaining = self.decay_rate - (current_turn - self.created_turn) return max(0, remaining) def to_dict(self) -> dict: """Convert to dictionary for API serialization.""" return { "type": self.type.value, "quantity": self.quantity, "created_turn": self.created_turn, "decay_rate": self.decay_rate, }