Backup before cleanup

This commit is contained in:
Joan
2026-02-05 15:00:49 +01:00
parent e6747b1d05
commit 1b7ffd614d
60 changed files with 3013 additions and 460 deletions

View File

@@ -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: