241 lines
8.1 KiB
Python
241 lines
8.1 KiB
Python
"""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,
|
|
}
|