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

@@ -208,8 +208,18 @@ async def execute_attack(
broken_armor = []
if is_pvp:
is_defending = False
target_effects = await db.get_player_effects(target['id'])
defending_effect = next((e for e in target_effects if e['effect_name'] == 'defending'), None)
if defending_effect:
is_defending = True
reduction = defending_effect.get('value', 50) / 100
damage = max(1, int(damage * (1 - reduction)))
messages.append(create_combat_message("damage_reduced", origin="enemy", reduction=int(reduction * 100)))
await db.remove_effect(target['id'], 'defending')
# PvP: use equipment-based armor reduction + durability
armor_absorbed, broken_armor = await reduce_armor_func(target['id'], damage)
armor_absorbed, broken_armor = await reduce_armor_func(target['id'], damage, is_defending)
actual_damage = max(1, damage - armor_absorbed)
else:
# PvE: use NPC's flat defense value
@@ -279,6 +289,54 @@ async def execute_attack(
}
async def execute_defend(
player_id: int,
player: dict,
player_stats: dict,
is_pvp: bool,
locale: str = 'en'
) -> Dict[str, Any]:
"""
Execute a defend action.
Reduces incoming damage by 50% for the next turn, but increases durability loss.
Returns: {
messages: list
}
"""
from .. import database as db
from .helpers import create_combat_message, get_game_message
messages = []
# 5% Stamina restore
stamina_restore = max(5, int(player_stats.get('max_stamina', 100) * 0.05))
new_stamina = min(player_stats.get('max_stamina', 100), player.get('stamina', 100) + stamina_restore)
await db.update_player(player_id, stamina=new_stamina)
# Add defending effect
await db.add_effect(
player_id=player_id,
effect_name="defending",
effect_icon="🛡️",
effect_type="buff",
ticks_remaining=1,
persist_after_combat=False,
source="combat_defend",
value=50 # 50% reduction
)
messages.append(create_combat_message(
"player_defend",
origin="player"
))
return {
'messages': messages,
'stamina_restored': stamina_restore,
}
# ============================================================================
# SKILL ACTION
# ============================================================================
@@ -294,6 +352,7 @@ async def execute_skill(
items_manager: ItemsManager,
reduce_armor_func,
redis_manager=None,
locale: str = 'en'
) -> Dict[str, Any]:
"""
Execute a skill action. Validates requirements, deducts stamina, applies effects.
@@ -402,6 +461,17 @@ async def execute_skill(
if effects.get('guaranteed_crit'):
damage = int(damage * player_stats.get('crit_damage', 1.5))
is_defending = False
if is_pvp:
target_effects = await db.get_player_effects(target['id'])
defending_effect = next((e for e in target_effects if e['effect_name'] == 'defending'), None)
if defending_effect:
is_defending = True
reduction = defending_effect.get('value', 50) / 100
damage = max(1, int(damage * (1 - reduction)))
messages.append(create_combat_message("damage_reduced", origin="enemy", reduction=int(reduction * 100)))
await db.remove_effect(target['id'], 'defending')
# Multi-hit
num_hits = effects.get('hits', 1)
total_damage = 0
@@ -412,7 +482,7 @@ async def execute_skill(
if is_pvp:
# PvP: armor from equipment
absorbed, broken_armor = await reduce_armor_func(target['id'], hit_dmg)
absorbed, broken_armor = await reduce_armor_func(target['id'], hit_dmg, is_defending)
total_armor_absorbed += absorbed
for broken in broken_armor:
messages.append(create_combat_message(
@@ -450,8 +520,11 @@ async def execute_skill(
"skill_heal", origin="player", heal=heal_amount, skill_icon="🩸"
))
from .helpers import calculate_dynamic_status_damage
# Poison DoT
if 'poison_damage' in effects:
poison_dmg = calculate_dynamic_status_damage(effects, 'poison', target)
if poison_dmg is not None:
poison_dur = effects.get('poison_duration', 3)
if is_pvp:
# PvP: add as player effect
await db.add_effect(
@@ -459,14 +532,14 @@ async def execute_skill(
effect_name="Poison",
effect_icon="🧪",
effect_type="damage",
damage_per_tick=effects['poison_damage'],
ticks_remaining=effects['poison_duration'],
damage_per_tick=poison_dmg,
ticks_remaining=poison_dur,
persist_after_combat=True,
source=f"skill_poison:{skill.id}"
)
else:
# PvE: add to npc_status_effects string
poison_str = f"poison:{effects['poison_damage']}:{effects['poison_duration']}"
poison_str = f"poison:{poison_dmg}:{poison_dur}"
existing = combat_state.get('npc_status_effects', '') or ''
if existing:
existing += '|' + poison_str
@@ -476,7 +549,38 @@ async def execute_skill(
messages.append(create_combat_message(
"skill_effect", origin="player",
message=f"🧪 Poisoned! ({effects['poison_damage']} dmg/turn)"
message=f"🧪 Poisoned! ({poison_dmg} dmg/turn)"
))
# Burn DoT
burn_dmg = calculate_dynamic_status_damage(effects, 'burn', target)
if burn_dmg is not None:
burn_dur = effects.get('burn_duration', 3)
if is_pvp:
# PvP: add as player effect
await db.add_effect(
player_id=target['id'],
effect_name="Burning",
effect_icon="🔥",
effect_type="damage",
damage_per_tick=burn_dmg,
ticks_remaining=burn_dur,
persist_after_combat=True,
source=f"skill_burn:{skill.id}"
)
else:
# PvE: add to npc_status_effects string
burn_str = f"burning:{burn_dmg}:{burn_dur}"
existing = combat_state.get('npc_status_effects', '') or ''
if existing:
existing += '|' + burn_str
else:
existing = burn_str
await db.update_combat(player_id, {'npc_status_effects': existing})
messages.append(create_combat_message(
"skill_effect", origin="player",
message=f"🔥 Burning! ({burn_dmg} dmg/turn)"
))
# Stun chance
@@ -506,7 +610,7 @@ async def execute_skill(
await db.update_combat(player_id, {'npc_status_effects': existing})
messages.append(create_combat_message(
"skill_effect", origin="player", message="💫 Stunned!"
"skill_effect", origin="player", message=get_game_message('stunned_status', locale)
))
# Weapon durability
@@ -721,7 +825,13 @@ async def execute_use_item(
# 6. Status effect on target (burn from molotov etc.) — PvE only
status_effect = combat_effects.get('status') if not is_pvp else None
if status_effect and not target_defeated:
npc_status = f"{status_effect['name']}:{status_effect.get('damage_per_tick', 0)}:{status_effect.get('ticks', 1)}"
dmg = status_effect.get('damage_per_tick', 0)
if 'damage_percent' in status_effect:
max_hp = target.get('npc_max_hp', target.get('max_hp', 100))
base_dmg = max_hp * status_effect['damage_percent']
dmg = random.randint(max(1, int(base_dmg * 0.8)), max(1, int(base_dmg * 1.2)))
npc_status = f"{status_effect['name']}:{dmg}:{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",
@@ -1123,6 +1233,7 @@ async def execute_npc_turn(
npc_def,
reduce_armor_func,
redis_manager=None,
locale: str = 'en'
) -> Tuple[List[dict], bool]:
"""
Execute the NPC's turn with buff-aware damage reduction.
@@ -1145,7 +1256,7 @@ async def execute_npc_turn(
from ..game_logic import npc_attack
messages, player_defeated = await npc_attack(
player_id, combat, npc_def, reduce_armor_func, player_stats=stats
player_id, combat, npc_def, reduce_armor_func, player_stats=stats, locale=locale
)
return messages, player_defeated