"""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" # NEW: Oil industry resources OIL = "oil" # Raw crude oil - must be refined FUEL = "fuel" # Refined fuel - provides heat and energy @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.""" 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 # NEW: Oil resources ResourceType.OIL: ResourceEffect( energy=resources.oil_energy, # Raw oil has no direct use ), ResourceType.FUEL: ResourceEffect( energy=resources.fuel_energy, # Refined fuel provides energy heat=resources.fuel_heat, # And significant heat ), } def get_resource_decay_rates() -> dict[ResourceType, Optional[int]]: """Get resource decay rates from the global config.""" 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, # NEW: Oil resources don't decay ResourceType.OIL: resources.oil_decay if resources.oil_decay > 0 else None, ResourceType.FUEL: resources.fuel_decay if resources.fuel_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 def get_fuel_heat() -> int: """Get heat provided by burning fuel.""" from backend.config import get_config return get_config().resources.fuel_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() # Resource categories for AI and display FOOD_RESOURCES = {ResourceType.MEAT, ResourceType.BERRIES} DRINK_RESOURCES = {ResourceType.WATER} HEAT_RESOURCES = {ResourceType.WOOD, ResourceType.FUEL} CRAFTING_MATERIALS = {ResourceType.HIDE, ResourceType.OIL} VALUABLE_RESOURCES = {ResourceType.OIL, ResourceType.FUEL, ResourceType.CLOTHES} def get_resource_base_value(resource_type: ResourceType) -> int: """Get the base economic value of a resource.""" from backend.config import get_config config = get_config() # Oil and fuel have special pricing if resource_type == ResourceType.OIL: return config.economy.oil_base_price elif resource_type == ResourceType.FUEL: return config.economy.fuel_base_price # Other resources based on production cost base_values = { ResourceType.MEAT: 15, ResourceType.BERRIES: 5, ResourceType.WATER: 3, ResourceType.WOOD: 8, ResourceType.HIDE: 10, ResourceType.CLOTHES: 20, } return base_values.get(resource_type, 10) @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] @property def is_valuable(self) -> bool: """Check if this is a high-value resource.""" return self.type in VALUABLE_RESOURCES @property def base_value(self) -> int: """Get the base economic value.""" return get_resource_base_value(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, "base_value": self.base_value, }