Refactor: unified combat engine for PvE/PvP

- Create api/services/combat_engine.py with all shared combat logic
- Rewrite combat.py from 2820 to ~600 lines (thin orchestration)
- Fix buff consumption: fortify, berserker_rage, evade, foresight, iron_skin now actually work
- Fix stun: PvE skills now write stun to npc_status_effects
- Fix skill damage: now uses stats.attack_power consistently (includes perks)
- Fix PvPCombatActionRequest: add skill_id field for proper PvP skill support
- Remove dead code: PvP skill/item blocks copy-pasted into PvE endpoint
- Update game_logic.npc_attack to check buff modifiers (dodge, damage reduction, etc.)
This commit is contained in:
Joan
2026-02-25 12:10:45 +01:00
parent 540df02ae7
commit d5afd28eb9
4 changed files with 1693 additions and 2161 deletions

View File

@@ -810,17 +810,58 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
))
# Remove defending effect after use
await db.remove_effect(player_id, 'defending')
# ── Check buff-based damage reduction (fortify) ──
buff_dmg_reduction = 0.0
if player_stats:
buff_dmg_reduction = player_stats.get('buff_damage_reduction', 0.0)
if buff_dmg_reduction > 0:
npc_damage = max(1, int(npc_damage * (1 - buff_dmg_reduction)))
messages.append(create_combat_message(
"damage_reduced",
origin="player",
reduction=int(buff_dmg_reduction * 100)
))
# ── Check berserker rage increased damage taken ──
buff_dmg_taken_increase = 0.0
if player_stats:
buff_dmg_taken_increase = player_stats.get('buff_damage_taken_increase', 0.0)
if buff_dmg_taken_increase > 0:
npc_damage = int(npc_damage * (1 + buff_dmg_taken_increase))
# Check for dodge
# ── Check guaranteed dodge from Evade buff ──
dodged = False
if player_stats and 'dodge_chance' in player_stats:
if player_stats and player_stats.get('buff_guaranteed_dodge', False):
dodged = True
messages.append(create_combat_message(
"combat_dodge",
origin="player"
))
actual_damage = 0
new_player_hp = player['hp']
# Consume the evade buff
await db.remove_effect(player_id, 'evade')
# ── Check Foresight buff (enemy misses) ──
if not dodged and player_stats and player_stats.get('buff_enemy_miss', False):
dodged = True
messages.append(create_combat_message(
"combat_dodge",
origin="player"
))
actual_damage = 0
new_player_hp = player['hp']
# Foresight ticks down naturally via db.tick_player_effects
# Check for regular dodge (stat-based)
if not dodged and player_stats and 'dodge_chance' in player_stats:
if random.random() < player_stats['dodge_chance']:
dodged = True
messages.append(create_combat_message(
"combat_dodge",
origin="player"
))
# Prevent damage calculation
actual_damage = 0
new_player_hp = player['hp']
@@ -833,7 +874,6 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
"combat_block",
origin="player"
))
# Apply blocked effect (damage reduced significantly or nullified)
npc_damage = max(1, int(npc_damage * 0.2)) # Block mitigates 80% damage
if not dodged:
@@ -844,7 +884,6 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
if player_stats and player_stats.get('armor_reduction', 0) > 0:
pct_reduction = player_stats['armor_reduction']
actual_damage = max(1, int(npc_damage * (1 - pct_reduction)))
# Still show "armor_absorbed" conceptually for UI logs, though it's % based now
armor_absorbed_visual = npc_damage - actual_damage
else:
actual_damage = max(1, npc_damage - armor_absorbed)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -89,12 +89,13 @@ class PvPCombatInitiateRequest(BaseModel):
class PvPAcknowledgeRequest(BaseModel):
pass # No body needed
combat_id: int
class PvPCombatActionRequest(BaseModel):
action: str # 'attack', 'defend', 'flee', 'use_item'
action: str # 'attack', 'skill', 'flee', 'use_item'
item_id: Optional[str] = None # For use_item action
skill_id: Optional[str] = None # For skill action
# ============================================================================