Pre-menu-integration snapshot: combat, crafting, status effects, gamedata updates

This commit is contained in:
Joan
2026-03-11 12:43:23 +01:00
parent d5afd28eb9
commit a8dc8211d5
36 changed files with 1724 additions and 404 deletions

View File

@@ -3,11 +3,27 @@ Helper utilities for game calculations and common operations.
Contains distance calculations, stamina costs, capacity calculations, etc.
"""
import math
from typing import Tuple, List, Dict, Any, Union
import random
from typing import Tuple, List, Dict, Any, Union, Optional
from .. import database as db
from ..items import ItemsManager
def calculate_dynamic_status_damage(effects: dict, prefix: str, target: dict) -> Optional[int]:
"""Helper to calculate status damage based on percentage over max HP."""
if f'{prefix}_percent' in effects:
target_max_hp = target.get('max_hp') or target.get('npc_max_hp', 100)
pct = effects[f'{prefix}_percent']
base_dmg = target_max_hp * pct
# +/- 20% deviation
min_dmg = max(1, int(base_dmg * 0.8))
max_dmg = max(1, int(base_dmg * 1.2))
return random.randint(min_dmg, max_dmg)
elif f'{prefix}_damage' in effects:
return effects[f'{prefix}_damage']
return None
def get_locale_string(value: Union[str, Dict[str, str]], lang: str = 'en') -> str:
"""Helper to safely get string from i18n object or string."""
if isinstance(value, dict):
@@ -54,6 +70,8 @@ GAME_MESSAGES = {
'pvp_combat_started_broadcast': {'en': "⚔️ PvP combat started between {attacker} and {defender}!", 'es': "⚔️ ¡Combate PvP iniciado entre {attacker} y {defender}!"},
'flee_success_text': {'en': "{name} fled from combat!", 'es': "¡{name} huyó del combate!"},
'flee_fail_text': {'en': "{name} tried to flee but failed!", 'es': "¡{name} intentó huir pero falló!"},
'stunned_status': {'en': "💫 Stunned!", 'es': "💫 ¡Aturdido!"},
'npc_stunned_cannot_act': {'en': "💫 {npc_name} is stunned and cannot act!", 'es': "💫 ¡{npc_name} está aturdido y no puede actuar!"},
# Loot
'corpse_name_npc': {'en': "{name} Corpse", 'es': "Cadáver de {name}"},
@@ -101,12 +119,16 @@ GAME_MESSAGES = {
'item_used': {'en': "Used {name}", 'es': "Usado {name}"},
'no_item': {'en': "You don't have this item", 'es': "No tienes este objeto"},
'cannot_use': {'en': "This item cannot be used", 'es': "Este objeto no se puede usar"},
'cannot_equip_combat': {'en': "Cannot change equipment during combat", 'es': "No puedes cambiar de equipamiento durante el combate"},
'cured': {'en': "Cured", 'es': "Curado"},
# Status Effects
'statusDamage': {'en': "You took {damage} damage from status effects", 'es': "Has recibido {damage} de daño por efectos de estado"},
'statusHeal': {'en': "You recovered {heal} HP from status effects", 'es': "Has recuperado {heal} vida por efectos de estado"},
'diedFromStatus': {'en': "You died from status effects", 'es': "Has muerto por efectos de estado"},
# Combat Warnings
'enemy_charging': {'en': "⚠️ {enemy} is gathering strength for a massive attack!", 'es': "⚠️ ¡{enemy} está reuniendo fuerzas para un ataque masivo!"},
}
def get_game_message(key: str, lang: str = 'en', **kwargs) -> str:
@@ -140,6 +162,36 @@ def translate_travel_message(direction: str, location_name: str, lang: str = 'en
import json
from typing import List, Dict, Any, Tuple
from .. import database as db
async def get_resolved_player_effects(player_id: int, in_combat: bool = False) -> List[Dict]:
"""Helper to fetch and format active player effects for combat payloads."""
from ..services.skills import skills_manager
from ..services.status_effects import status_effects_manager
player_effects = []
all_effects = await db.get_player_effects(player_id)
for eff in all_effects:
if eff.get('effect_type') == 'cooldown':
continue
resolved = status_effects_manager.resolve_player_effect(
eff.get('effect_name', ''),
eff.get('effect_icon', ''),
eff.get('source', ''),
skills_manager,
in_combat=in_combat
)
player_effects.append({
'name': resolved['name'],
'effect_name': eff.get('effect_name', ''), # Needed for frontend state tracking
'icon': resolved['icon'],
'ticks_remaining': eff.get('ticks_remaining', 0),
'damage_per_tick': eff.get('damage_per_tick', 0), # Needed for logic
'type': eff.get('effect_type', 'buff'),
'description': resolved['description'],
})
return player_effects
def create_combat_message(message_type: str, origin: str = "neutral", **data) -> dict:
"""Create a structured combat message object.
@@ -274,7 +326,7 @@ async def calculate_player_capacity(inventory: List[Dict[str, Any]], items_manag
return current_weight, max_weight, current_volume, max_volume
async def reduce_armor_durability(player_id: int, damage_taken: int, items_manager: ItemsManager) -> Tuple[int, List[Dict[str, Any]]]:
async def reduce_armor_durability(player_id: int, damage_taken: int, items_manager: ItemsManager, is_defending: bool = False) -> Tuple[int, List[Dict[str, Any]]]:
"""
Reduce durability of equipped armor pieces when taking damage.
Returns: (armor_damage_absorbed, broken_armor_pieces)
@@ -311,7 +363,7 @@ async def reduce_armor_durability(player_id: int, damage_taken: int, items_manag
armor_absorbed = min(damage_taken // 2, total_armor)
# Calculate durability loss for each armor piece
base_reduction_rate = 0.1
base_reduction_rate = 0.2 if is_defending else 0.1
broken_armor = []
for armor in equipped_armor: