Pre-menu-integration snapshot: combat, crafting, status effects, gamedata updates
This commit is contained in:
@@ -17,13 +17,14 @@ from ..services.constants import PVP_TURN_TIMEOUT
|
||||
|
||||
from ..core.security import get_current_user, security, verify_internal_key
|
||||
from ..services.models import *
|
||||
from ..services.helpers import calculate_distance, calculate_stamina_cost, calculate_player_capacity, get_locale_string, create_combat_message, get_game_message
|
||||
from ..services.helpers import calculate_distance, calculate_stamina_cost, calculate_player_capacity, get_locale_string, create_combat_message, get_game_message, get_resolved_player_effects
|
||||
from .. import database as db
|
||||
from ..items import ItemsManager
|
||||
from .. import game_logic
|
||||
from ..core.websockets import manager
|
||||
from .equipment import reduce_armor_durability
|
||||
from ..services import combat_engine
|
||||
from ..services.status_effects import status_effects_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -70,6 +71,27 @@ async def get_combat_status(current_user: dict = Depends(get_current_user)):
|
||||
time_elapsed = time.time() - turn_started_at
|
||||
turn_time_remaining = max(0, 300 - time_elapsed)
|
||||
|
||||
# Parse NPC status effects
|
||||
npc_effects_list = []
|
||||
npc_status_str = combat.get('npc_status_effects', '') or ''
|
||||
if npc_status_str:
|
||||
for part in npc_status_str.split('|'):
|
||||
tokens = part.split(':')
|
||||
effect_name = tokens[0] if len(tokens) > 0 else ''
|
||||
if not effect_name:
|
||||
continue
|
||||
ticks = int(tokens[2]) if len(tokens) > 2 else (int(tokens[1]) if len(tokens) > 1 else 0)
|
||||
info = status_effects_manager.get_effect_info(effect_name)
|
||||
npc_effects_list.append({
|
||||
'name': info['name'],
|
||||
'icon': info['icon'],
|
||||
'ticks_remaining': ticks,
|
||||
'description': info['description'],
|
||||
})
|
||||
|
||||
# Get player active buffs/debuffs (exclude cooldowns)
|
||||
player_effects = await get_resolved_player_effects(current_user['id'], in_combat=True)
|
||||
|
||||
return {
|
||||
"in_combat": True,
|
||||
"combat": {
|
||||
@@ -80,8 +102,11 @@ async def get_combat_status(current_user: dict = Depends(get_current_user)):
|
||||
"npc_image": f"{npc_def.image_path}" if npc_def else None,
|
||||
"turn": combat['turn'],
|
||||
"round": combat.get('round', 1),
|
||||
"turn_time_remaining": turn_time_remaining
|
||||
}
|
||||
"turn_time_remaining": turn_time_remaining,
|
||||
"npc_effects": npc_effects_list,
|
||||
"npc_intent": combat.get('npc_intent', 'attack')
|
||||
},
|
||||
"player_effects": player_effects
|
||||
}
|
||||
|
||||
|
||||
@@ -154,8 +179,10 @@ async def initiate_combat(
|
||||
"npc_max_hp": npc_hp,
|
||||
"npc_image": f"{npc_def.image_path}",
|
||||
"turn": "player",
|
||||
"round": 1
|
||||
}
|
||||
"round": 1,
|
||||
"npc_intent": "attack"
|
||||
},
|
||||
"player_effects": await get_resolved_player_effects(current_user['id'], in_combat=True)
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat()
|
||||
})
|
||||
@@ -185,8 +212,10 @@ async def initiate_combat(
|
||||
"npc_max_hp": npc_hp,
|
||||
"npc_image": f"{npc_def.image_path}",
|
||||
"turn": "player",
|
||||
"round": 1
|
||||
}
|
||||
"round": 1,
|
||||
"npc_intent": "attack"
|
||||
},
|
||||
"player_effects": await get_resolved_player_effects(current_user['id'], in_combat=True)
|
||||
}
|
||||
|
||||
|
||||
@@ -303,15 +332,20 @@ async def combat_action(
|
||||
exclude_player_id=player['id']
|
||||
)
|
||||
else:
|
||||
# Fetch fresh combat state to capture any player buffs applied
|
||||
fresh_combat = await db.get_active_combat(player['id'])
|
||||
st_effects = fresh_combat.get('npc_status_effects', '') if fresh_combat else combat.get('npc_status_effects', '')
|
||||
|
||||
# NPC turn
|
||||
npc_msgs, player_defeated = await combat_engine.execute_npc_turn(
|
||||
player['id'],
|
||||
{'npc_hp': new_npc_hp, 'npc_max_hp': combat['npc_max_hp'],
|
||||
'npc_intent': combat.get('npc_intent', 'attack'),
|
||||
'npc_status_effects': combat.get('npc_status_effects', '')},
|
||||
'npc_status_effects': st_effects},
|
||||
npc_def,
|
||||
reduce_armor_durability,
|
||||
redis_manager
|
||||
redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
messages.extend(npc_msgs)
|
||||
|
||||
@@ -336,6 +370,7 @@ async def combat_action(
|
||||
items_manager=ITEMS_MANAGER,
|
||||
reduce_armor_func=reduce_armor_durability,
|
||||
redis_manager=redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
|
||||
if result.get('error'):
|
||||
@@ -372,21 +407,28 @@ async def combat_action(
|
||||
exclude_player_id=player['id']
|
||||
)
|
||||
else:
|
||||
# Fetch fresh combat state to capture effects applied by the skill
|
||||
fresh_combat = await db.get_active_combat(player['id'])
|
||||
st_effects = fresh_combat.get('npc_status_effects', '') if fresh_combat else combat.get('npc_status_effects', '')
|
||||
|
||||
# NPC turn after skill
|
||||
npc_msgs, player_defeated = await combat_engine.execute_npc_turn(
|
||||
player['id'],
|
||||
{'npc_hp': new_npc_hp, 'npc_max_hp': combat['npc_max_hp'],
|
||||
'npc_intent': combat.get('npc_intent', 'attack'),
|
||||
'npc_status_effects': combat.get('npc_status_effects', '')},
|
||||
'npc_status_effects': st_effects},
|
||||
npc_def,
|
||||
reduce_armor_durability,
|
||||
redis_manager
|
||||
redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
messages.extend(npc_msgs)
|
||||
|
||||
if player_defeated:
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
combat_over = True
|
||||
else:
|
||||
await db.update_combat(player['id'], {'npc_hp': new_npc_hp})
|
||||
|
||||
# ── USE ITEM ──
|
||||
elif req.action == 'use_item':
|
||||
@@ -421,15 +463,20 @@ async def combat_action(
|
||||
messages.extend(victory['messages'])
|
||||
quest_updates = victory.get('quest_updates', [])
|
||||
elif not combat_over:
|
||||
# Fetch fresh combat state to capture effects applied by the item
|
||||
fresh_combat = await db.get_active_combat(player['id'])
|
||||
st_effects = fresh_combat.get('npc_status_effects', '') if fresh_combat else combat.get('npc_status_effects', '')
|
||||
|
||||
# NPC turn after item use
|
||||
npc_msgs, player_defeated = await combat_engine.execute_npc_turn(
|
||||
player['id'],
|
||||
{'npc_hp': result.get('target_hp', combat['npc_hp']), 'npc_max_hp': combat['npc_max_hp'],
|
||||
'npc_intent': combat.get('npc_intent', 'attack'),
|
||||
'npc_status_effects': combat.get('npc_status_effects', '')},
|
||||
'npc_status_effects': st_effects},
|
||||
npc_def,
|
||||
reduce_armor_durability,
|
||||
redis_manager
|
||||
redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
messages.extend(npc_msgs)
|
||||
|
||||
@@ -440,6 +487,38 @@ async def combat_action(
|
||||
# Update NPC HP from throwable damage
|
||||
if result.get('target_hp') is not None and result['target_hp'] != combat['npc_hp']:
|
||||
await db.update_combat(player['id'], {'npc_hp': result['target_hp']})
|
||||
|
||||
# ── DEFEND ──
|
||||
elif req.action == 'defend':
|
||||
result = await combat_engine.execute_defend(
|
||||
player_id=player['id'],
|
||||
player=player,
|
||||
player_stats=stats,
|
||||
is_pvp=False,
|
||||
locale=locale,
|
||||
)
|
||||
messages.extend(result['messages'])
|
||||
|
||||
# Fetch fresh combat state since defend could've updated stats (stamina)
|
||||
fresh_combat = await db.get_active_combat(player['id'])
|
||||
st_effects = fresh_combat.get('npc_status_effects', '') if fresh_combat else combat.get('npc_status_effects', '')
|
||||
|
||||
# NPC turn after defend
|
||||
npc_msgs, player_defeated = await combat_engine.execute_npc_turn(
|
||||
player['id'],
|
||||
{'npc_hp': combat['npc_hp'], 'npc_max_hp': combat['npc_max_hp'],
|
||||
'npc_intent': combat.get('npc_intent', 'attack'),
|
||||
'npc_status_effects': st_effects},
|
||||
npc_def,
|
||||
reduce_armor_durability,
|
||||
redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
messages.extend(npc_msgs)
|
||||
|
||||
if player_defeated:
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
combat_over = True
|
||||
|
||||
# ── FLEE ──
|
||||
elif req.action == 'flee':
|
||||
@@ -491,6 +570,7 @@ async def combat_action(
|
||||
|
||||
# ── Build response ──
|
||||
updated_combat = None
|
||||
npc_effects_list = []
|
||||
if not combat_over:
|
||||
raw_combat = await db.get_active_combat(current_user['id'])
|
||||
if raw_combat:
|
||||
@@ -499,6 +579,23 @@ async def combat_action(
|
||||
turn_started_at = raw_combat.get('turn_started_at', 0)
|
||||
turn_time_remaining = max(0, 300 - (time.time() - turn_started_at))
|
||||
|
||||
# Parse NPC status effects string into a list
|
||||
npc_status_str = raw_combat.get('npc_status_effects', '') or ''
|
||||
if npc_status_str:
|
||||
for part in npc_status_str.split('|'):
|
||||
tokens = part.split(':')
|
||||
effect_name = tokens[0] if len(tokens) > 0 else ''
|
||||
if not effect_name:
|
||||
continue
|
||||
ticks = int(tokens[2]) if len(tokens) > 2 else (int(tokens[1]) if len(tokens) > 1 else 0)
|
||||
info = status_effects_manager.get_effect_info(effect_name)
|
||||
npc_effects_list.append({
|
||||
'name': info['name'],
|
||||
'icon': info['icon'],
|
||||
'ticks_remaining': ticks,
|
||||
'description': info['description'],
|
||||
})
|
||||
|
||||
updated_combat = {
|
||||
"npc_id": raw_combat['npc_id'],
|
||||
"npc_name": npc_def.name,
|
||||
@@ -507,13 +604,75 @@ async def combat_action(
|
||||
"npc_image": f"{npc_def.image_path}",
|
||||
"turn": raw_combat['turn'],
|
||||
"round": raw_combat.get('round', 1),
|
||||
"turn_time_remaining": turn_time_remaining
|
||||
"turn_time_remaining": turn_time_remaining,
|
||||
"npc_effects": npc_effects_list,
|
||||
"npc_intent": raw_combat.get('npc_intent', 'attack')
|
||||
}
|
||||
|
||||
# Get player active buffs/debuffs (exclude cooldowns)
|
||||
player_effects = []
|
||||
if not combat_over:
|
||||
from ..services.skills import skills_manager
|
||||
all_effects = await db.get_player_effects(current_user['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
|
||||
)
|
||||
player_effects.append({
|
||||
'name': resolved['name'],
|
||||
'icon': resolved['icon'],
|
||||
'ticks_remaining': eff.get('ticks_remaining', 0),
|
||||
'type': eff.get('effect_type', 'buff'),
|
||||
'description': resolved['description'],
|
||||
})
|
||||
|
||||
updated_player = await db.get_player_by_id(current_user['id'])
|
||||
if not updated_player:
|
||||
updated_player = current_user
|
||||
|
||||
equipment_slots = await db.get_all_equipment(current_user['id'])
|
||||
equipment = {}
|
||||
for slot, item_data in equipment_slots.items():
|
||||
if item_data and item_data['item_id']:
|
||||
inv_item = await db.get_inventory_item_by_id(item_data['item_id'])
|
||||
if inv_item:
|
||||
item_def = ITEMS_MANAGER.get_item(inv_item['item_id'])
|
||||
if item_def:
|
||||
# Get unique item data if this is a unique item
|
||||
durability = None
|
||||
max_durability = None
|
||||
tier = None
|
||||
unique_stats = None
|
||||
if inv_item.get('unique_item_id'):
|
||||
unique_item = await db.get_unique_item(inv_item['unique_item_id'])
|
||||
if unique_item:
|
||||
durability = unique_item.get('durability')
|
||||
max_durability = unique_item.get('max_durability')
|
||||
tier = unique_item.get('tier')
|
||||
unique_stats = unique_item.get('unique_stats')
|
||||
|
||||
equipment[slot] = {
|
||||
"inventory_id": item_data['item_id'],
|
||||
"item_id": item_def.id,
|
||||
"name": item_def.name,
|
||||
"description": item_def.description,
|
||||
"emoji": item_def.emoji,
|
||||
"image_path": item_def.image_path,
|
||||
"durability": durability if durability is not None else None,
|
||||
"max_durability": max_durability if max_durability is not None else None,
|
||||
"tier": tier if tier is not None else None,
|
||||
"unique_stats": unique_stats,
|
||||
"stats": item_def.stats,
|
||||
"encumbrance": item_def.encumbrance,
|
||||
"weapon_effects": item_def.weapon_effects if hasattr(item_def, 'weapon_effects') else {}
|
||||
}
|
||||
if slot not in equipment:
|
||||
equipment[slot] = None
|
||||
return {
|
||||
"success": True,
|
||||
"messages": messages,
|
||||
@@ -526,6 +685,8 @@ async def combat_action(
|
||||
"xp": updated_player['xp'],
|
||||
"level": updated_player['level']
|
||||
},
|
||||
"player_effects": player_effects,
|
||||
"equipment": equipment,
|
||||
"quest_updates": quest_updates
|
||||
}
|
||||
|
||||
@@ -887,6 +1048,7 @@ async def pvp_combat_action(
|
||||
items_manager=ITEMS_MANAGER,
|
||||
reduce_armor_func=reduce_armor_durability,
|
||||
redis_manager=redis_manager,
|
||||
locale=locale
|
||||
)
|
||||
|
||||
if result.get('error'):
|
||||
@@ -978,6 +1140,25 @@ async def pvp_combat_action(
|
||||
'last_action': f"{last_action_text}|{time.time()}"
|
||||
})
|
||||
|
||||
# ── DEFEND ──
|
||||
elif req.action == 'defend':
|
||||
result = await combat_engine.execute_defend(
|
||||
player_id=current_player['id'],
|
||||
player=current_player,
|
||||
player_stats=current_player_stats,
|
||||
is_pvp=True,
|
||||
locale=locale,
|
||||
)
|
||||
messages.extend(result['messages'])
|
||||
last_action_text = f"{current_player['name']} took a defensive stance!"
|
||||
|
||||
# Switch turns
|
||||
await db.update_pvp_combat(pvp_combat['id'], {
|
||||
'turn': 'defender' if is_attacker else 'attacker',
|
||||
'turn_started_at': time.time(),
|
||||
'last_action': f"{last_action_text}|{time.time()}"
|
||||
})
|
||||
|
||||
# ── FLEE ──
|
||||
elif req.action == 'flee':
|
||||
result = await combat_engine.execute_flee_pvp(
|
||||
|
||||
@@ -25,13 +25,15 @@ logger = logging.getLogger(__name__)
|
||||
LOCATIONS = None
|
||||
ITEMS_MANAGER = None
|
||||
WORLD = None
|
||||
redis_manager = None
|
||||
|
||||
def init_router_dependencies(locations, items_manager, world):
|
||||
def init_router_dependencies(locations, items_manager, world, redis_mgr=None):
|
||||
"""Initialize router with game data dependencies"""
|
||||
global LOCATIONS, ITEMS_MANAGER, WORLD
|
||||
global LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager
|
||||
LOCATIONS = locations
|
||||
ITEMS_MANAGER = items_manager
|
||||
WORLD = world
|
||||
redis_manager = redis_mgr
|
||||
|
||||
router = APIRouter(tags=["crafting"])
|
||||
|
||||
@@ -509,9 +511,8 @@ async def uncraft_item(request: UncraftItemRequest, current_user: dict = Depends
|
||||
adjusted_quantity = int(round(base_quantity * durability_ratio))
|
||||
|
||||
mat_def = ITEMS_MANAGER.items.get(material['item_id'])
|
||||
mat_name = mat_def.name if mat_def else material['item_id']
|
||||
|
||||
loss_key = (material['item_id'], mat_name)
|
||||
loss_key = material['item_id']
|
||||
|
||||
# If durability is too low (< 10%), yield nothing for this material
|
||||
if durability_ratio < 0.1 or adjusted_quantity <= 0:
|
||||
@@ -535,7 +536,7 @@ async def uncraft_item(request: UncraftItemRequest, current_user: dict = Depends
|
||||
# But we need to check capacity.
|
||||
# Let's accumulate pending yield.
|
||||
|
||||
yield_key = (material['item_id'], mat_name, mat_def.emoji if mat_def else '📦', mat_def)
|
||||
yield_key = material['item_id']
|
||||
if yield_key not in materials_yielded_dict:
|
||||
materials_yielded_dict[yield_key] = 0
|
||||
materials_yielded_dict[yield_key] += adjusted_quantity
|
||||
@@ -546,18 +547,23 @@ async def uncraft_item(request: UncraftItemRequest, current_user: dict = Depends
|
||||
materials_dropped = []
|
||||
|
||||
# Convert lost dict to list
|
||||
for (item_id, name), qty in materials_lost_dict.items():
|
||||
for item_id, qty in materials_lost_dict.items():
|
||||
mat_def = ITEMS_MANAGER.items.get(item_id)
|
||||
materials_lost.append({
|
||||
'item_id': item_id,
|
||||
'name': name,
|
||||
'quantity': qty,
|
||||
'reason': 'lost_or_low_durability'
|
||||
'name': mat_def.name if mat_def else item_id,
|
||||
'emoji': mat_def.emoji if mat_def else '📦',
|
||||
'quantity': qty
|
||||
})
|
||||
|
||||
# Process yield
|
||||
for (item_id, name, emoji, mat_def), qty in materials_yielded_dict.items():
|
||||
mat_weight = getattr(mat_def, 'weight', 0) * qty
|
||||
mat_volume = getattr(mat_def, 'volume', 0) * qty
|
||||
for item_id, qty in materials_yielded_dict.items():
|
||||
mat_def = ITEMS_MANAGER.items.get(item_id)
|
||||
mat_name = mat_def.name if mat_def else item_id
|
||||
emoji = mat_def.emoji if mat_def else '📦'
|
||||
|
||||
mat_weight = getattr(mat_def, 'weight', 0) * qty if mat_def else 0
|
||||
mat_volume = getattr(mat_def, 'volume', 0) * qty if mat_def else 0
|
||||
|
||||
# Simple check against capacity (assuming current_weight was just updated from DB)
|
||||
# Note: we might fill up mid-loop. ideally we add one by one or check total.
|
||||
|
||||
@@ -50,6 +50,14 @@ async def equip_item(
|
||||
player_id = current_user['id']
|
||||
locale = request.headers.get('Accept-Language', 'en')
|
||||
|
||||
# Check if in combat
|
||||
in_combat = await db.get_active_combat(player_id)
|
||||
if in_combat:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=get_game_message('cannot_equip_combat', locale)
|
||||
)
|
||||
|
||||
# Get the inventory item
|
||||
inv_item = await db.get_inventory_item_by_id(equip_req.inventory_id)
|
||||
if not inv_item or inv_item['character_id'] != player_id:
|
||||
@@ -156,6 +164,14 @@ async def unequip_item(
|
||||
player_id = current_user['id']
|
||||
locale = request.headers.get('Accept-Language', 'en')
|
||||
|
||||
# Check if in combat
|
||||
in_combat = await db.get_active_combat(player_id)
|
||||
if in_combat:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=get_game_message('cannot_equip_combat', locale)
|
||||
)
|
||||
|
||||
# Check if slot is valid
|
||||
valid_slots = ['head', 'torso', 'legs', 'feet', 'weapon', 'offhand', 'backpack']
|
||||
if unequip_req.slot not in valid_slots:
|
||||
@@ -412,7 +428,7 @@ async def repair_item(
|
||||
|
||||
|
||||
|
||||
async def reduce_armor_durability(player_id: int, damage_taken: int) -> tuple:
|
||||
async def reduce_armor_durability(player_id: int, damage_taken: int, is_defending: bool = False) -> tuple:
|
||||
"""
|
||||
Reduce durability of equipped armor pieces when taking damage.
|
||||
Formula: durability_loss = max(1, (damage_taken / armor_value) * base_reduction_rate)
|
||||
@@ -452,7 +468,7 @@ async def reduce_armor_durability(player_id: int, damage_taken: int) -> tuple:
|
||||
|
||||
# Calculate durability loss for each armor piece
|
||||
# Balanced formula: armor should last many combats (10-20+ hits for low tier)
|
||||
base_reduction_rate = 0.1 # Reduced from 0.5 to make armor more durable
|
||||
base_reduction_rate = 0.2 if is_defending else 0.1 # Reduced from 0.5 to make armor more durable
|
||||
broken_armor = []
|
||||
|
||||
for armor in equipped_armor:
|
||||
|
||||
@@ -228,7 +228,8 @@ async def get_game_state(current_user: dict = Depends(get_current_user)):
|
||||
raise HTTPException(status_code=404, detail="Player not found")
|
||||
|
||||
# Get player status effects
|
||||
status_effects = await db.get_player_effects(player_id)
|
||||
from ..services.helpers import get_resolved_player_effects
|
||||
status_effects = await get_resolved_player_effects(player_id)
|
||||
player['status_effects'] = status_effects
|
||||
|
||||
# Get location
|
||||
@@ -375,13 +376,21 @@ async def get_game_state(current_user: dict = Depends(get_current_user)):
|
||||
"tags": getattr(location, 'tags', [])
|
||||
}
|
||||
|
||||
from ..services.stats import calculate_derived_stats
|
||||
derived_stats = await calculate_derived_stats(player_id, redis_manager)
|
||||
|
||||
# Add weight/volume to player data
|
||||
player_with_capacity = dict(player)
|
||||
player_with_capacity['current_weight'] = round(total_weight, 2)
|
||||
player_with_capacity['max_weight'] = round(max_weight, 2)
|
||||
player_with_capacity['current_volume'] = round(total_volume, 2)
|
||||
|
||||
player_with_capacity['max_weight'] = round(derived_stats.get('carry_weight', max_weight), 2)
|
||||
player_with_capacity['max_volume'] = round(max_volume, 2)
|
||||
|
||||
player_with_capacity['max_hp'] = derived_stats.get('max_hp', player['max_hp'])
|
||||
player_with_capacity['max_stamina'] = derived_stats.get('max_stamina', player['max_stamina'])
|
||||
player_with_capacity['derived_stats'] = derived_stats
|
||||
|
||||
# Calculate movement cooldown
|
||||
import time
|
||||
current_time = time.time()
|
||||
@@ -412,20 +421,29 @@ async def get_player_profile(current_user: dict = Depends(get_current_user)):
|
||||
raise HTTPException(status_code=404, detail="Player not found")
|
||||
|
||||
# Get player status effects
|
||||
status_effects = await db.get_player_effects(player_id)
|
||||
from ..services.helpers import get_resolved_player_effects
|
||||
status_effects = await get_resolved_player_effects(player_id)
|
||||
player['status_effects'] = status_effects
|
||||
|
||||
# Get capacity metrics (weight/volume) using the helper function
|
||||
# We don't need the inventory array itself, just the capacity calculations
|
||||
_, total_weight, total_volume, max_weight, max_volume = await _get_enriched_inventory(player_id)
|
||||
|
||||
from ..services.stats import calculate_derived_stats
|
||||
derived_stats = await calculate_derived_stats(player_id, redis_manager)
|
||||
|
||||
# Add weight/volume to player data
|
||||
player_with_capacity = dict(player)
|
||||
player_with_capacity['current_weight'] = round(total_weight, 2)
|
||||
player_with_capacity['max_weight'] = round(max_weight, 2)
|
||||
player_with_capacity['current_volume'] = round(total_volume, 2)
|
||||
|
||||
player_with_capacity['max_weight'] = round(derived_stats.get('carry_weight', max_weight), 2)
|
||||
player_with_capacity['max_volume'] = round(max_volume, 2)
|
||||
|
||||
player_with_capacity['max_hp'] = derived_stats.get('max_hp', player['max_hp'])
|
||||
player_with_capacity['max_stamina'] = derived_stats.get('max_stamina', player['max_stamina'])
|
||||
player_with_capacity['derived_stats'] = derived_stats
|
||||
|
||||
# Calculate movement cooldown
|
||||
import time
|
||||
current_time = time.time()
|
||||
@@ -962,6 +980,7 @@ async def move(
|
||||
await db.update_player_statistics(current_user['id'], combats_initiated=1, increment=True)
|
||||
|
||||
encounter_triggered = True
|
||||
from ..services.helpers import get_resolved_player_effects
|
||||
combat_data = {
|
||||
"npc_id": enemy_id,
|
||||
"npc_name": npc_def.name,
|
||||
@@ -972,6 +991,7 @@ async def move(
|
||||
"round": 1,
|
||||
"npc_intent": initial_intent['type']
|
||||
}
|
||||
player_effects = await get_resolved_player_effects(current_user['id'], in_combat=True)
|
||||
|
||||
response = {
|
||||
"success": True,
|
||||
@@ -986,7 +1006,8 @@ async def move(
|
||||
"triggered": True,
|
||||
"enemy_id": enemy_id,
|
||||
"message": get_game_message('enemy_ambush', locale),
|
||||
"combat": combat_data
|
||||
"combat": combat_data,
|
||||
"player_effects": player_effects
|
||||
}
|
||||
|
||||
# Broadcast movement to WebSocket clients
|
||||
@@ -1585,6 +1606,10 @@ async def get_character_sheet(current_user: dict = Depends(get_current_user)):
|
||||
# Get all perks with availability
|
||||
all_perks = perks_manager.get_available_perks(player, owned_perk_ids)
|
||||
|
||||
# Get active status effects
|
||||
from ..services.helpers import get_resolved_player_effects
|
||||
status_effects = await get_resolved_player_effects(character_id)
|
||||
|
||||
# Calculate perk points
|
||||
total_perk_points = get_total_perk_points(player['level'])
|
||||
used_perk_points = len(owned_perk_ids)
|
||||
@@ -1607,6 +1632,7 @@ async def get_character_sheet(current_user: dict = Depends(get_current_user)):
|
||||
"used_points": used_perk_points,
|
||||
"all_perks": all_perks,
|
||||
},
|
||||
"status_effects": status_effects,
|
||||
"character": {
|
||||
"name": player['name'],
|
||||
"level": player['level'],
|
||||
|
||||
@@ -25,13 +25,15 @@ logger = logging.getLogger(__name__)
|
||||
LOCATIONS = None
|
||||
ITEMS_MANAGER = None
|
||||
WORLD = None
|
||||
redis_manager = None
|
||||
|
||||
def init_router_dependencies(locations, items_manager, world):
|
||||
def init_router_dependencies(locations, items_manager, world, redis_mgr=None):
|
||||
"""Initialize router with game data dependencies"""
|
||||
global LOCATIONS, ITEMS_MANAGER, WORLD
|
||||
global LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager
|
||||
LOCATIONS = locations
|
||||
ITEMS_MANAGER = items_manager
|
||||
WORLD = world
|
||||
redis_manager = redis_mgr
|
||||
|
||||
router = APIRouter(tags=["loot"])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user