120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
import random
|
|
from typing import Tuple, Dict, Any
|
|
|
|
from data.items import ITEMS
|
|
from data.models import Action, Outcome
|
|
|
|
def calculate_inventory_load(player_inventory: list) -> Tuple[float, float]:
|
|
"""Calculates the total weight and volume of a player's inventory."""
|
|
total_weight = 0.0
|
|
total_volume = 0.0
|
|
for item in player_inventory:
|
|
item_def = ITEMS.get(item["item_id"])
|
|
if item_def:
|
|
total_weight += item_def["weight"] * item["quantity"]
|
|
total_volume += item_def["volume"] * item["quantity"]
|
|
return round(total_weight, 2), round(total_volume, 2)
|
|
|
|
def get_player_capacity(player_inventory: list, player_stats: dict) -> Tuple[float, float]:
|
|
"""Calculates the total carrying capacity of a player."""
|
|
base_weight_cap = player_stats['strength'] * 5 # Example formula
|
|
base_volume_cap = player_stats['strength'] * 2 # Example formula
|
|
|
|
for item in player_inventory:
|
|
if item["is_equipped"]:
|
|
item_def = ITEMS.get(item["item_id"])
|
|
if item_def and item_def.get("type") == "equipment":
|
|
effects = item_def.get("effects", {})
|
|
base_weight_cap += effects.get("capacity_weight", 0)
|
|
base_volume_cap += effects.get("capacity_volume", 0)
|
|
|
|
return base_weight_cap, base_volume_cap
|
|
|
|
def resolve_action(player_stats: dict, action_obj: Action) -> Outcome:
|
|
"""
|
|
Resolves a player action, like searching, based on stats and luck.
|
|
Returns the resulting Outcome object.
|
|
"""
|
|
# A simple success chance calculation
|
|
base_chance = 50 + (player_stats.get('intellect', 5) * 2)
|
|
roll = random.randint(1, 100)
|
|
|
|
outcome_key = "failure"
|
|
if roll <= 5 and "critical_failure" in action_obj.outcomes:
|
|
outcome_key = "critical_failure"
|
|
elif roll <= base_chance and "success" in action_obj.outcomes:
|
|
outcome_key = "success"
|
|
|
|
return action_obj.outcomes.get(outcome_key, action_obj.outcomes["failure"])
|
|
|
|
async def can_add_item_to_inventory(user_id: int, item_id: str, quantity: int) -> Tuple[bool, str]:
|
|
"""
|
|
Check if an item can be added to the player's inventory.
|
|
Returns (can_add, reason_if_not)
|
|
"""
|
|
from . import database
|
|
|
|
player = await database.get_player(user_id)
|
|
if not player:
|
|
return False, "Player not found."
|
|
|
|
inventory = await database.get_inventory(user_id)
|
|
item_def = ITEMS.get(item_id)
|
|
|
|
if not item_def:
|
|
return False, "Invalid item."
|
|
|
|
# Calculate current and projected weight/volume
|
|
current_weight, current_volume = calculate_inventory_load(inventory)
|
|
max_weight, max_volume = get_player_capacity(inventory, player)
|
|
|
|
item_weight = item_def["weight"] * quantity
|
|
item_volume = item_def["volume"] * quantity
|
|
|
|
new_weight = current_weight + item_weight
|
|
new_volume = current_volume + item_volume
|
|
|
|
if new_weight > max_weight:
|
|
return False, f"Too heavy! ({new_weight:.1f}/{max_weight:.1f} kg)"
|
|
|
|
if new_volume > max_volume:
|
|
return False, f"Not enough space! ({new_volume:.1f}/{max_volume:.1f} vol)"
|
|
|
|
return True, ""
|
|
|
|
def calculate_travel_stamina_cost(player: dict, inventory: list, from_location, to_location) -> int:
|
|
"""
|
|
Calculate stamina cost for traveling between locations.
|
|
Based on distance, endurance (reduces cost), and carried weight (increases cost).
|
|
|
|
Args:
|
|
player: Player stats dictionary
|
|
inventory: Player's inventory list
|
|
from_location: Location object being traveled from
|
|
to_location: Location object being traveled to
|
|
"""
|
|
from data.travel_helpers import calculate_base_stamina_cost
|
|
|
|
# Get base cost from shared helper (used by map and game)
|
|
distance_cost = calculate_base_stamina_cost(from_location, to_location)
|
|
|
|
# Endurance reduces cost (each point reduces by 0.5)
|
|
endurance_reduction = player['endurance'] * 0.5
|
|
|
|
# Calculate weight burden
|
|
current_weight, _ = calculate_inventory_load(inventory)
|
|
max_weight, _ = get_player_capacity(inventory, player)
|
|
|
|
# Weight penalty: if carrying more than 50% capacity, add extra cost
|
|
weight_ratio = current_weight / max_weight if max_weight > 0 else 0
|
|
weight_penalty = 0
|
|
|
|
if weight_ratio > 0.5:
|
|
# Each 10% over 50% adds 1 stamina
|
|
weight_penalty = int((weight_ratio - 0.5) * 10)
|
|
|
|
# Calculate final cost (minimum 3)
|
|
final_cost = max(3, int(distance_cost - endurance_reduction + weight_penalty))
|
|
|
|
return final_cost
|