Source code for simulation_framework.src.actions.gathering

from __future__ import annotations

import random
from typing import TYPE_CHECKING, Optional

from ..items.item import Item
from .base import Action, ActionResult, Event, ResourceCost

if TYPE_CHECKING:
    from ..core.world import World
    from ..entities.base import Entity


[docs] class GatherAction(Action): def __init__( self, actor_id: int, resource_type: str, required_tool: Optional[str] = None, required_terrain: Optional[str] = None, skill_name: str = "general", ): super().__init__(actor_id) self.resource_type = resource_type self.required_tool = required_tool self.required_terrain = required_terrain self.skill_name = skill_name
[docs] def can_execute(self, actor: Entity, world: World) -> bool: tile = world.get_tile(*actor.position) if not tile: return False if self.required_terrain: if tile.terrain_type.value != self.required_terrain: return False # Pass current_tick to check if resource is actually harvestable if not tile.can_gather(self.resource_type, world.current_tick): return False if self.required_tool: tool = actor.inventory.get_equipped_tool(self.required_tool) if not tool: return False if tool.is_broken(): return False return self.get_cost().can_afford(actor)
[docs] def execute(self, actor: Entity, world: World) -> ActionResult: if not self.can_execute(actor, world): return ActionResult.failure(f"Cannot gather {self.resource_type}") cost = self.get_cost() if not cost.consume(actor): return ActionResult.failure("Not enough resources to gather") tile = world.get_tile(*actor.position) resource_deposit = None for deposit in tile.resources: if deposit.resource_type == self.resource_type: resource_deposit = deposit break if not resource_deposit: return ActionResult.failure(f"No {self.resource_type} found") if not resource_deposit.can_harvest(world.current_tick): return ActionResult.failure(f"{self.resource_type} not ready for harvest") base_yield = self._calculate_yield(actor) tool = None if self.required_tool: tool = actor.inventory.get_equipped_tool(self.required_tool) if tool: base_yield = int(base_yield * tool.get_efficiency()) tool.use(1) actual_yield = resource_deposit.harvest(base_yield, world.current_tick) if actual_yield > 0: item = self._create_resource_item(self.resource_type, actual_yield) remaining = actor.inventory.add_item(item, actual_yield) if remaining > 0: return ActionResult.failure( f"Inventory full, lost {remaining} {self.resource_type}" ) self._gain_experience(actor) event = Event( event_type="gather", actor_id=actor.id, data={ "resource_type": self.resource_type, "yield": actual_yield, "position": actor.position, }, ) return ActionResult.success( f"Gathered {actual_yield} {self.resource_type}", [event] ) else: return ActionResult.failure(f"Failed to gather {self.resource_type}")
def _calculate_yield(self, actor: Entity) -> int: base_yield = 2 skill_level = getattr(actor, "skills", {}).get(self.skill_name, 1) skill_bonus = skill_level * 0.1 random_factor = random.uniform(0.8, 1.2) total_yield = base_yield * (1 + skill_bonus) * random_factor rare_chance = 0.1 + (skill_level * 0.01) if random.random() < rare_chance: total_yield *= 2 return max(1, int(total_yield)) def _create_resource_item(self, resource_type: str, quantity: int) -> Item: item_data = { "wood": { "id": 100, "name": "Wood", "value": 2, "description": "Raw wood material", }, "stone": { "id": 101, "name": "Stone", "value": 1, "description": "Common stone", }, "iron_ore": { "id": 102, "name": "Iron Ore", "value": 5, "description": "Iron ore for smelting", }, "gold_ore": { "id": 103, "name": "Gold Ore", "value": 20, "description": "Precious gold ore", }, "fish": { "id": 104, "name": "Fish", "value": 3, "description": "Fresh fish", }, "berries": { "id": 105, "name": "Berries", "value": 2, "description": "Wild berries", }, "herbs": { "id": 106, "name": "Herbs", "value": 4, "description": "Medicinal herbs", }, } data = item_data.get( resource_type, { "id": 999, "name": resource_type.title(), "value": 1, "description": f"Raw {resource_type}", }, ) return Item( id=data["id"], name=data["name"], item_type="material", properties={"resource_type": resource_type}, value=data["value"], description=data["description"], weight=1.0, max_stack_size=99, ) def _gain_experience(self, actor: Entity) -> None: if hasattr(actor, "skills"): current_xp = actor.skills.get(self.skill_name, 0) actor.skills[self.skill_name] = current_xp + 1
[docs] def get_duration(self) -> int: return 3
[docs] def get_cost(self) -> ResourceCost: return ResourceCost(stamina=5)
def __repr__(self) -> str: return f"GatherAction({self.resource_type})"
[docs] class FishAction(GatherAction): def __init__(self, actor_id: int): super().__init__( actor_id=actor_id, resource_type="fish", required_tool="fishing_rod", skill_name="fishing", )
[docs] def can_execute(self, actor: Entity, world: World) -> bool: x, y = actor.position water_adjacent = False for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: neighbor = world.get_tile(x + dx, y + dy) if neighbor and neighbor.terrain_type.value == "water": water_adjacent = True break if not water_adjacent: return False return super().can_execute(actor, world)
[docs] class MineAction(GatherAction): def __init__(self, actor_id: int, resource_type: str = "stone"): super().__init__( actor_id=actor_id, resource_type=resource_type, required_tool="pickaxe", # Restored tool requirement required_terrain="mountain", skill_name="mining", )
[docs] class ForageAction(GatherAction): def __init__(self, actor_id: int, resource_type: str = "berries"): super().__init__( actor_id=actor_id, resource_type=resource_type, skill_name="foraging" )
[docs] def can_execute(self, actor: Entity, world: World) -> bool: tile = world.get_tile(*actor.position) if not tile: return False valid_terrain = tile.terrain_type.value in ["forest", "grass"] if not valid_terrain: return False return tile.can_gather( self.resource_type, world.current_tick ) and self.get_cost().can_afford(actor)
[docs] class WoodcutAction(GatherAction): def __init__(self, actor_id: int): super().__init__( actor_id=actor_id, resource_type="wood", required_tool="axe", # Restored tool requirement required_terrain="forest", skill_name="woodcutting", )