Backup before cleanup
This commit is contained in:
@@ -249,7 +249,59 @@ async def combat_action(
|
||||
|
||||
messages = []
|
||||
combat_over = False
|
||||
player_won = False
|
||||
|
||||
# Process status effects (bleeding, etc.) before action
|
||||
active_effects = await db.tick_player_effects(player['id'])
|
||||
|
||||
# Process status effects before action
|
||||
if active_effects:
|
||||
from ..game_logic import calculate_status_impact
|
||||
total_impact = calculate_status_impact(active_effects)
|
||||
|
||||
if total_impact > 0:
|
||||
# DAMAGE
|
||||
damage = total_impact
|
||||
new_hp = max(0, player['hp'] - damage)
|
||||
await db.update_player_hp(player['id'], new_hp)
|
||||
player['hp'] = new_hp # Update local reference
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"effect_damage",
|
||||
origin="player",
|
||||
damage=damage,
|
||||
effect_name="status effects"
|
||||
))
|
||||
|
||||
if new_hp <= 0:
|
||||
# Player died from effects
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
await db.end_combat(player['id'])
|
||||
|
||||
return {
|
||||
"player": player,
|
||||
"combat": None,
|
||||
"messages": messages + [create_combat_message("died", origin="player", message="You died from status effects!")],
|
||||
"active_effects": [],
|
||||
"round": combat['round']
|
||||
}
|
||||
elif total_impact < 0:
|
||||
# HEALING
|
||||
heal = abs(total_impact)
|
||||
new_hp = min(player['max_hp'], player['hp'] + heal)
|
||||
actual_heal = new_hp - player['hp']
|
||||
|
||||
if actual_heal > 0:
|
||||
await db.update_player_hp(player['id'], new_hp)
|
||||
player['hp'] = new_hp
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"effect_heal",
|
||||
origin="player",
|
||||
heal=actual_heal,
|
||||
effect_name="status effects"
|
||||
))
|
||||
|
||||
|
||||
|
||||
if req.action == 'attack':
|
||||
# Calculate player damage
|
||||
@@ -382,6 +434,9 @@ async def combat_action(
|
||||
loot_remaining=json.dumps(corpse_loot_dicts)
|
||||
)
|
||||
|
||||
|
||||
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
await db.end_combat(player['id'])
|
||||
|
||||
# Update Redis: Delete combat state cache
|
||||
@@ -456,6 +511,7 @@ async def combat_action(
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
await db.end_combat(player['id'])
|
||||
|
||||
# Broadcast to location that player fled from combat
|
||||
@@ -557,6 +613,7 @@ async def combat_action(
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
await db.end_combat(player['id'])
|
||||
|
||||
# Broadcast to location that player died (and corpse if created)
|
||||
@@ -584,6 +641,249 @@ async def combat_action(
|
||||
await db.update_player_statistics(player['id'], failed_flees=1, damage_taken=npc_damage, increment=True)
|
||||
await db.update_combat(player['id'], {'turn': 'player', 'turn_started_at': time.time()})
|
||||
|
||||
elif req.action == 'defend':
|
||||
# Apply "defending" status effect - reduces incoming damage by 50% for 1 turn
|
||||
await db.add_effect(
|
||||
player_id=player['id'],
|
||||
effect_name='defending',
|
||||
effect_icon='🛡️',
|
||||
effect_type='buff',
|
||||
value=50, # 50% damage reduction
|
||||
ticks_remaining=1,
|
||||
persist_after_combat=False,
|
||||
source='action:defend'
|
||||
)
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"defend",
|
||||
origin="player",
|
||||
message=get_game_message('defend_text', locale, name=player['name'])
|
||||
))
|
||||
|
||||
# NPC's turn after defend
|
||||
npc_attack_messages, player_defeated = await game_logic.npc_attack(
|
||||
player['id'],
|
||||
{'npc_hp': combat['npc_hp'], 'npc_max_hp': combat['npc_max_hp']},
|
||||
npc_def,
|
||||
reduce_armor_durability
|
||||
)
|
||||
messages.extend(npc_attack_messages)
|
||||
if player_defeated:
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
combat_over = True
|
||||
|
||||
elif req.action == 'use_item':
|
||||
combat_over = False
|
||||
# Validate item_id provided
|
||||
if not req.item_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="item_id required for use_item action"
|
||||
)
|
||||
|
||||
# Get the item from inventory
|
||||
player_inventory = await db.get_inventory(player['id'])
|
||||
inv_item = None
|
||||
for item in player_inventory:
|
||||
if item['item_id'] == req.item_id:
|
||||
inv_item = item
|
||||
break
|
||||
|
||||
if not inv_item:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Item not found in inventory"
|
||||
)
|
||||
|
||||
# Get item definition
|
||||
item_def = ITEMS_MANAGER.get_item(req.item_id)
|
||||
if not item_def:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown item"
|
||||
)
|
||||
|
||||
# Check if item is combat usable
|
||||
if not item_def.combat_usable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="This item cannot be used in combat"
|
||||
)
|
||||
|
||||
# Apply item effects
|
||||
item_name = get_locale_string(item_def.name, locale)
|
||||
effects_applied = []
|
||||
|
||||
# 1. Apply Status Effects (e.g. Regeneration from Bandage)
|
||||
if item_def.effects.get('status_effect'):
|
||||
status_data = item_def.effects['status_effect']
|
||||
await db.add_effect(
|
||||
player_id=player['id'],
|
||||
effect_name=status_data['name'],
|
||||
effect_icon=status_data.get('icon', '✨'),
|
||||
effect_type=status_data.get('type', 'buff'),
|
||||
damage_per_tick=status_data.get('damage_per_tick', 0),
|
||||
value=status_data.get('value', 0),
|
||||
ticks_remaining=status_data.get('ticks', 3),
|
||||
persist_after_combat=True, # Consumable effects usually persist
|
||||
source=f"item:{item_def.id}"
|
||||
)
|
||||
effects_applied.append(f"Applied {status_data['name']}")
|
||||
|
||||
# 2. Cure Status Effects
|
||||
if item_def.effects.get('cures'):
|
||||
cures = item_def.effects['cures']
|
||||
for cure_effect in cures:
|
||||
if await db.remove_effect(player['id'], cure_effect):
|
||||
effects_applied.append(f"Cured {cure_effect}")
|
||||
|
||||
# 3. Handle Direct healing (legacy/instant)
|
||||
if item_def.effects.get('hp_restore') and item_def.effects['hp_restore'] > 0:
|
||||
hp_restore = item_def.effects['hp_restore']
|
||||
old_hp = player['hp']
|
||||
new_hp = min(player.get('max_hp', 100), old_hp + hp_restore)
|
||||
actual_restored = new_hp - old_hp
|
||||
if actual_restored > 0:
|
||||
await db.update_player_hp(player['id'], new_hp)
|
||||
effects_applied.append(f"+{actual_restored} HP")
|
||||
|
||||
if item_def.effects.get('stamina_restore'):
|
||||
stamina_restore = item_def.effects['stamina_restore']
|
||||
old_stamina = player['stamina']
|
||||
new_stamina = min(player.get('max_stamina', 100), old_stamina + stamina_restore)
|
||||
actual_restored = new_stamina - old_stamina
|
||||
if actual_restored > 0:
|
||||
await db.update_player_stamina(player['id'], new_stamina)
|
||||
effects_applied.append(f"+{actual_restored} Stamina")
|
||||
|
||||
# Handle combat effects (throwables)
|
||||
combat_effects = item_def.combat_effects or {}
|
||||
|
||||
# Direct damage from throwable
|
||||
if combat_effects.get('damage_min') and combat_effects.get('damage_max'):
|
||||
damage = random.randint(combat_effects['damage_min'], combat_effects['damage_max'])
|
||||
new_npc_hp = max(0, combat['npc_hp'] - damage)
|
||||
effects_applied.append(f"{damage} damage")
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"item_damage",
|
||||
origin="player",
|
||||
damage=damage,
|
||||
item_name=item_name
|
||||
))
|
||||
|
||||
# Check if NPC is defeated
|
||||
if new_npc_hp <= 0:
|
||||
messages.append(create_combat_message(
|
||||
"victory",
|
||||
origin="neutral",
|
||||
npc_name=npc_def.name
|
||||
))
|
||||
combat_over = True
|
||||
player_won = True
|
||||
|
||||
# Award XP
|
||||
xp_gained = npc_def.xp_reward
|
||||
new_xp = player['xp'] + xp_gained
|
||||
messages.append(create_combat_message(
|
||||
"xp_gain",
|
||||
origin="player",
|
||||
amount=xp_gained
|
||||
))
|
||||
await db.update_player(player['id'], xp=new_xp)
|
||||
await db.update_player_statistics(player['id'], enemies_killed=1, damage_dealt=damage, increment=True)
|
||||
|
||||
# Check for level up
|
||||
level_up_result = await game_logic.check_and_apply_level_up(player['id'])
|
||||
if level_up_result['leveled_up']:
|
||||
messages.append(create_combat_message(
|
||||
"level_up",
|
||||
origin="player",
|
||||
level=level_up_result['new_level'],
|
||||
stat_points=level_up_result['levels_gained']
|
||||
))
|
||||
|
||||
# Create corpse with loot
|
||||
import json as json_module
|
||||
corpse_loot = npc_def.corpse_loot if hasattr(npc_def, 'corpse_loot') else []
|
||||
corpse_loot_dicts = []
|
||||
for loot in corpse_loot:
|
||||
if hasattr(loot, '__dict__'):
|
||||
corpse_loot_dicts.append({
|
||||
'item_id': loot.item_id,
|
||||
'quantity_min': loot.quantity_min,
|
||||
'quantity_max': loot.quantity_max,
|
||||
'required_tool': loot.required_tool
|
||||
})
|
||||
else:
|
||||
corpse_loot_dicts.append(loot)
|
||||
await db.create_npc_corpse(
|
||||
npc_id=combat['npc_id'],
|
||||
location_id=player['location_id'],
|
||||
loot_remaining=json_module.dumps(corpse_loot_dicts)
|
||||
)
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
await db.end_combat(player['id'])
|
||||
else:
|
||||
# Update NPC HP
|
||||
await db.update_combat(player['id'], {'npc_hp': new_npc_hp})
|
||||
|
||||
# Apply status effect from item (e.g., burning from molotov)
|
||||
status_effect = combat_effects.get('status')
|
||||
if status_effect and not combat_over:
|
||||
# Apply to NPC via combat status (simplified - NPC status stored in combat record)
|
||||
npc_status = f"{status_effect['name']}:{status_effect.get('damage_per_tick', 0)}:{status_effect.get('ticks', 1)}"
|
||||
await db.update_combat(player['id'], {'npc_status_effects': npc_status})
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"effect_applied",
|
||||
origin="player",
|
||||
effect_name=status_effect['name'],
|
||||
effect_icon=status_effect.get('icon', '🔥'),
|
||||
target="enemy"
|
||||
))
|
||||
|
||||
# Consume the item
|
||||
await db.remove_item_from_inventory(player['id'], req.item_id, 1)
|
||||
await db.update_player_statistics(player['id'], items_used=1, increment=True)
|
||||
|
||||
# Add item used message
|
||||
effects_str = f" ({', '.join(effects_applied)})" if effects_applied else ""
|
||||
|
||||
# Calculate total restored amounts for frontend floating text
|
||||
hp_restored_val = 0
|
||||
stamina_restored_val = 0
|
||||
|
||||
if item_def.effects.get('hp_restore'):
|
||||
hp_restored_val = min(player.get('max_hp', 100), old_hp + item_def.effects['hp_restore']) - old_hp
|
||||
|
||||
if item_def.effects.get('stamina_restore'):
|
||||
stamina_restored_val = min(player.get('max_stamina', 100), old_stamina + item_def.effects['stamina_restore']) - old_stamina
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"item_used",
|
||||
origin="player",
|
||||
item_name=item_name,
|
||||
effects=effects_str,
|
||||
hp_restore=hp_restored_val if hp_restored_val > 0 else None,
|
||||
stamina_restore=stamina_restored_val if stamina_restored_val > 0 else None
|
||||
))
|
||||
|
||||
# NPC's turn after using item (if combat not over)
|
||||
if not combat_over:
|
||||
npc_attack_messages, player_defeated = await game_logic.npc_attack(
|
||||
player['id'],
|
||||
{'npc_hp': combat['npc_hp'], 'npc_max_hp': combat['npc_max_hp']},
|
||||
npc_def,
|
||||
reduce_armor_durability
|
||||
)
|
||||
messages.extend(npc_attack_messages)
|
||||
|
||||
if player_defeated:
|
||||
await db.remove_non_persistent_effects(player['id'])
|
||||
combat_over = True
|
||||
|
||||
|
||||
# Get updated combat state if not over
|
||||
updated_combat = None
|
||||
if not combat_over:
|
||||
|
||||
Reference in New Issue
Block a user