Pre-menu-integration snapshot: combat, crafting, status effects, gamedata updates
This commit is contained in:
@@ -599,29 +599,85 @@ def generate_npc_intent(npc_def, combat_state: dict) -> dict:
|
||||
Generate the NEXT intent for an NPC.
|
||||
Returns a dict with intent type and details.
|
||||
"""
|
||||
# Default intent is attack
|
||||
intent = {"type": "attack", "value": 0}
|
||||
import random
|
||||
from api.services.skills import skills_manager
|
||||
|
||||
# Logic could be more complex based on NPC type, HP, etc.
|
||||
roll = random.random()
|
||||
npc_hp_pct = combat_state['npc_hp'] / combat_state['npc_max_hp'] if combat_state['npc_max_hp'] > 0 else 0
|
||||
skills = getattr(npc_def, 'skills', [])
|
||||
|
||||
# 20% chance to defend if HP < 50%
|
||||
if (combat_state['npc_hp'] / combat_state['npc_max_hp'] < 0.5) and roll < 0.2:
|
||||
intent = {"type": "defend", "value": 0}
|
||||
# 15% chance for special attack (if defined, otherwise strong attack)
|
||||
elif roll < 0.35:
|
||||
intent = {"type": "special", "value": 0}
|
||||
else:
|
||||
intent = {"type": "attack", "value": 0}
|
||||
active_effects = combat_state.get('npc_status_effects', '')
|
||||
|
||||
cooldowns = {}
|
||||
if active_effects:
|
||||
for eff in active_effects.split('|'):
|
||||
if eff.startswith('cd_'):
|
||||
parts = eff.split(':')
|
||||
if len(parts) >= 2:
|
||||
cooldowns[parts[0][3:]] = int(parts[1])
|
||||
|
||||
available_skills = []
|
||||
has_heal = None
|
||||
has_buff = None
|
||||
damage_skills = []
|
||||
|
||||
for skill_id in skills:
|
||||
if cooldowns.get(skill_id, 0) > 0:
|
||||
continue
|
||||
skill = skills_manager.get_skill(skill_id)
|
||||
if not skill: continue
|
||||
available_skills.append(skill)
|
||||
|
||||
return intent
|
||||
if 'heal_percent' in skill.effects:
|
||||
has_heal = skill
|
||||
elif 'buff' in skill.effects:
|
||||
has_buff = skill
|
||||
else:
|
||||
damage_skills.append(skill)
|
||||
|
||||
# 1. Survival First
|
||||
if has_heal and npc_hp_pct < 0.3:
|
||||
if random.random() < 0.8:
|
||||
return {"type": "skill", "value": has_heal.id}
|
||||
|
||||
# 2. Buffs
|
||||
if has_buff:
|
||||
buff_name = has_buff.effects['buff']
|
||||
is_buff_active = False
|
||||
if active_effects:
|
||||
for eff in active_effects.split('|'):
|
||||
if eff.startswith(buff_name + ':'):
|
||||
is_buff_active = True
|
||||
break
|
||||
if not is_buff_active and random.random() < 0.6:
|
||||
return {"type": "skill", "value": has_buff.id}
|
||||
|
||||
# 3. Telegraphed Attack Check (15% chance if health > 30%)
|
||||
if npc_hp_pct > 0.3 and random.random() < 0.15:
|
||||
return {"type": "charge", "value": "charging_attack"}
|
||||
|
||||
# 4. Damage Skills
|
||||
if damage_skills and random.random() < 0.4:
|
||||
chosen = random.choice(damage_skills)
|
||||
return {"type": "skill", "value": chosen.id}
|
||||
|
||||
# Default to attack or defend (legacy logic)
|
||||
roll = random.random()
|
||||
if npc_hp_pct < 0.5 and roll < 0.1:
|
||||
return {"type": "defend", "value": 0}
|
||||
|
||||
return {"type": "attack", "value": 0}
|
||||
|
||||
|
||||
async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, player_stats: dict = None) -> Tuple[List[dict], bool]:
|
||||
async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, player_stats: dict = None, locale: str = 'en') -> Tuple[List[dict], bool]:
|
||||
"""
|
||||
Execute NPC turn based on PREVIOUS intent, then generate NEXT intent.
|
||||
Returns: (messages_list, player_defeated)
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
from api import database as db
|
||||
from api.services.helpers import create_combat_message, get_game_message, get_locale_string
|
||||
from api.services.skills import skills_manager
|
||||
player = await db.get_player_by_id(player_id)
|
||||
if not player:
|
||||
return [], True
|
||||
@@ -635,13 +691,10 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
|
||||
is_stunned = False
|
||||
|
||||
if npc_status_str:
|
||||
# Parse status: "bleeding:5:3" (name:dmg:ticks) or "stun:1"
|
||||
# Handling multiple effects separated by |
|
||||
effects_list = npc_status_str.split('|')
|
||||
active_effects = []
|
||||
npc_damage_taken = 0
|
||||
npc_healing_received = 0
|
||||
is_stunned = False
|
||||
|
||||
for effect_str in effects_list:
|
||||
if not effect_str: continue
|
||||
@@ -656,18 +709,24 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
|
||||
messages.append(create_combat_message(
|
||||
"skill_effect",
|
||||
origin="enemy",
|
||||
message=f"💫 {npc_def.name} is stunned and cannot act!"
|
||||
message=get_game_message('npc_stunned_cannot_act', locale, npc_name=get_locale_string(npc_def.name, locale))
|
||||
))
|
||||
ticks -= 1
|
||||
if ticks > 0:
|
||||
active_effects.append(f"stun:{ticks}")
|
||||
continue
|
||||
|
||||
if name.startswith('cd_') and len(parts) >= 3:
|
||||
ticks = int(parts[2])
|
||||
ticks -= 1
|
||||
if ticks > 0:
|
||||
active_effects.append(f"{name}:{parts[1]}:{ticks}")
|
||||
continue
|
||||
|
||||
if len(parts) >= 3:
|
||||
dmg = int(parts[1])
|
||||
ticks = int(parts[2])
|
||||
|
||||
# Apply effect
|
||||
if ticks > 0:
|
||||
if dmg > 0:
|
||||
npc_damage_taken += dmg
|
||||
@@ -682,272 +741,210 @@ async def npc_attack(player_id: int, combat: dict, npc_def, reduce_armor_func, p
|
||||
heal = abs(dmg)
|
||||
npc_healing_received += heal
|
||||
messages.append(create_combat_message(
|
||||
"effect_heal", # Check if this message type exists or fallback
|
||||
"effect_heal",
|
||||
origin="enemy",
|
||||
heal=heal,
|
||||
effect_name=name,
|
||||
npc_name=npc_def.name
|
||||
))
|
||||
elif name in ["berserker_rage", "fortify", "analyzed"]:
|
||||
pass
|
||||
|
||||
# Decrement tick
|
||||
ticks -= 1
|
||||
if ticks > 0:
|
||||
active_effects.append(f"{name}:{dmg}:{ticks}")
|
||||
except Exception as e:
|
||||
print(f"Error parsing NPC status: {e}")
|
||||
|
||||
# Update NPC active effects
|
||||
new_status_str = "|".join(active_effects)
|
||||
if new_status_str != npc_status_str:
|
||||
await db.update_combat(player_id, {'npc_status_effects': new_status_str})
|
||||
|
||||
# Apply Total Damage/Healing
|
||||
if npc_damage_taken > 0:
|
||||
npc_hp = max(0, npc_hp - npc_damage_taken)
|
||||
|
||||
if npc_healing_received > 0:
|
||||
npc_hp = min(npc_max_hp, npc_hp + npc_healing_received)
|
||||
|
||||
# Update NPC HP in DB
|
||||
await db.update_combat(player_id, {'npc_hp': npc_hp})
|
||||
|
||||
# Check if NPC died from effects
|
||||
if npc_hp <= 0:
|
||||
messages.append(create_combat_message(
|
||||
"victory",
|
||||
origin="neutral",
|
||||
npc_name=npc_def.name
|
||||
))
|
||||
# Award XP/Loot logic handled in combat route mostly, but we need to signal it.
|
||||
# Returning true for player_defeated is definitely WRONG here if NPC died.
|
||||
# The router usually handles "victory" check after action.
|
||||
# But here this is triggered during NPC turn (which happens after Player turn).
|
||||
# If NPC dies on its OWN turn, we need to handle it.
|
||||
# However, typically NPC dies on Player turn.
|
||||
# If NPC dies from bleeding on its turn, the player wins.
|
||||
# We need to signal this back to router.
|
||||
# But the current return signature is (messages, player_defeated).
|
||||
# We might need to handle the win logic here or update signature.
|
||||
# For now, let's update HP and let the flow continue.
|
||||
# Wait, if NPC is dead, it shouldn't attack!
|
||||
# returning here prevents NPC from attacking if it died from status effects
|
||||
messages.append(create_combat_message("victory", origin="neutral", npc_name=npc_def.name))
|
||||
return messages, False
|
||||
|
||||
# Parse current intent (stored in DB as string or JSON, assuming simple string for now or we parse it)
|
||||
current_intent_str = combat.get('npc_intent', 'attack')
|
||||
# Handle legacy/null
|
||||
if not current_intent_str:
|
||||
current_intent_str = 'attack'
|
||||
|
||||
intent_type = current_intent_str
|
||||
intent_parts = current_intent_str.split(':')
|
||||
intent_type = intent_parts[0]
|
||||
intent_value = intent_parts[1] if len(intent_parts) > 1 else None
|
||||
|
||||
actual_damage = 0
|
||||
|
||||
# EXECUTE INTENT
|
||||
if npc_hp > 0 and not is_stunned: # Only attack if alive and not stunned
|
||||
new_player_hp = player['hp']
|
||||
|
||||
if npc_hp > 0 and not is_stunned:
|
||||
if intent_type == 'defend':
|
||||
# NPC defends - heals 5% HP
|
||||
heal_amount = int(combat['npc_max_hp'] * 0.05)
|
||||
new_npc_hp = min(combat['npc_max_hp'], combat['npc_hp'] + heal_amount)
|
||||
await db.update_combat(player_id, {'npc_hp': new_npc_hp})
|
||||
messages.append(create_combat_message("enemy_defend", origin="enemy", npc_name=npc_def.name, heal=heal_amount))
|
||||
|
||||
elif intent_type == 'charge':
|
||||
messages.append(create_combat_message(
|
||||
"enemy_defend",
|
||||
origin="enemy",
|
||||
npc_name=npc_def.name,
|
||||
heal=heal_amount
|
||||
"skill_effect", origin="enemy", message=get_game_message('enemy_charging', locale, enemy=get_locale_string(npc_def.name, locale))
|
||||
))
|
||||
|
||||
elif intent_type == 'special':
|
||||
# Strong attack (1.5x damage)
|
||||
npc_damage = int(random.randint(npc_def.damage_min, npc_def.damage_max) * 1.5)
|
||||
armor_absorbed, broken_armor = await reduce_armor_func(player_id, npc_damage)
|
||||
actual_damage = max(1, npc_damage - armor_absorbed)
|
||||
new_player_hp = max(0, player['hp'] - actual_damage)
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"enemy_special",
|
||||
origin="enemy",
|
||||
npc_name=npc_def.name,
|
||||
damage=npc_damage,
|
||||
armor_absorbed=armor_absorbed
|
||||
))
|
||||
|
||||
if broken_armor:
|
||||
for armor in broken_armor:
|
||||
messages.append(create_combat_message(
|
||||
"item_broken",
|
||||
origin="player",
|
||||
item_name=armor['name'],
|
||||
emoji=armor['emoji']
|
||||
))
|
||||
|
||||
await db.update_player(player_id, hp=new_player_hp)
|
||||
|
||||
else: # Default 'attack'
|
||||
elif intent_type in ('charging_attack', 'special', 'attack', 'skill'):
|
||||
npc_damage = random.randint(npc_def.damage_min, npc_def.damage_max)
|
||||
skill = None
|
||||
is_charging = intent_type == 'charging_attack'
|
||||
|
||||
# Enrage bonus if NPC is below 30% HP
|
||||
is_enraged = combat['npc_hp'] / combat['npc_max_hp'] < 0.3
|
||||
if is_enraged:
|
||||
if intent_type == 'charging_attack':
|
||||
npc_damage = int(npc_damage * 2.5)
|
||||
elif intent_type == 'special':
|
||||
npc_damage = int(npc_damage * 1.5)
|
||||
messages.append(create_combat_message(
|
||||
"enemy_enraged",
|
||||
origin="enemy",
|
||||
npc_name=npc_def.name
|
||||
))
|
||||
|
||||
# Check if player is defending (reduces damage by value%)
|
||||
player_effects = await db.get_player_effects(player_id)
|
||||
defending_effect = next((e for e in player_effects if e['effect_name'] == 'defending'), None)
|
||||
if defending_effect:
|
||||
reduction = defending_effect.get('value', 50) / 100 # Default 50% reduction
|
||||
npc_damage = int(npc_damage * (1 - reduction))
|
||||
messages.append(create_combat_message(
|
||||
"damage_reduced",
|
||||
origin="player",
|
||||
reduction=int(reduction * 100)
|
||||
))
|
||||
# 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 guaranteed dodge from Evade buff ──
|
||||
dodged = False
|
||||
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
|
||||
elif intent_type == 'skill' and intent_value:
|
||||
skill = skills_manager.get_skill(intent_value)
|
||||
if skill:
|
||||
if skill.cooldown > 0:
|
||||
cd_str = f"cd_{skill.id}:0:{skill.cooldown}"
|
||||
curr_combat = await db.get_active_combat(player_id)
|
||||
curr_status = curr_combat.get('npc_status_effects', '') if curr_combat else ''
|
||||
new_status = curr_status + f"|{cd_str}" if curr_status else cd_str
|
||||
await db.update_combat(player_id, {'npc_status_effects': new_status})
|
||||
|
||||
effects = skill.effects
|
||||
if 'heal_percent' in effects:
|
||||
heal_amount = int(combat['npc_max_hp'] * effects['heal_percent'])
|
||||
new_npc_hp = min(combat['npc_max_hp'], npc_hp + heal_amount)
|
||||
await db.update_combat(player_id, {'npc_hp': new_npc_hp})
|
||||
messages.append(create_combat_message("skill_heal", origin="enemy", heal=heal_amount, skill_icon=skill.icon, skill_name=get_locale_string(skill.name, locale), npc_name=npc_def.name))
|
||||
npc_damage = 0
|
||||
|
||||
if 'buff' in effects:
|
||||
buff_str = f"{effects['buff']}:0:{effects['buff_duration']}"
|
||||
curr_combat = await db.get_active_combat(player_id)
|
||||
curr_status = curr_combat.get('npc_status_effects', '') if curr_combat else ''
|
||||
new_status = curr_status + f"|{buff_str}" if curr_status else buff_str
|
||||
await db.update_combat(player_id, {'npc_status_effects': new_status})
|
||||
messages.append(create_combat_message("skill_buff", origin="enemy", skill_name=get_locale_string(skill.name, locale), skill_icon=skill.icon, duration=effects['buff_duration'], npc_name=npc_def.name))
|
||||
if 'damage_multiplier' not in effects and 'poison_damage' not in effects:
|
||||
npc_damage = 0
|
||||
|
||||
if 'damage_multiplier' in effects:
|
||||
npc_damage = max(1, int(npc_damage * effects['damage_multiplier']))
|
||||
|
||||
from api.services.helpers import calculate_dynamic_status_damage
|
||||
poison_dmg = calculate_dynamic_status_damage(effects, 'poison', player)
|
||||
if poison_dmg is not None:
|
||||
await db.add_effect(player_id=player_id, effect_name="Poison", effect_icon="🧪", effect_type="damage", damage_per_tick=poison_dmg, ticks_remaining=effects.get('poison_duration', 3), persist_after_combat=True, source=f"enemy_skill:{skill.id}")
|
||||
|
||||
# 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']:
|
||||
burn_dmg = calculate_dynamic_status_damage(effects, 'burn', player)
|
||||
if burn_dmg is not None:
|
||||
await db.add_effect(player_id=player_id, effect_name="Burning", effect_icon="🔥", effect_type="damage", damage_per_tick=burn_dmg, ticks_remaining=effects.get('burn_duration', 3), persist_after_combat=True, source=f"enemy_skill:{skill.id}")
|
||||
|
||||
is_enraged = combat['npc_hp'] / combat['npc_max_hp'] < 0.3
|
||||
if is_enraged and npc_damage > 0:
|
||||
npc_damage = int(npc_damage * 1.5)
|
||||
messages.append(create_combat_message("enemy_enraged", origin="enemy", npc_name=npc_def.name))
|
||||
|
||||
curr_combat = await db.get_active_combat(player_id)
|
||||
curr_status = curr_combat.get('npc_status_effects', '') if curr_combat else ''
|
||||
if 'berserker_rage' in curr_status and npc_damage > 0:
|
||||
npc_damage = int(npc_damage * 1.5)
|
||||
|
||||
if npc_damage > 0:
|
||||
dodged = False
|
||||
|
||||
is_defending = False
|
||||
player_effects = await db.get_player_effects(player_id)
|
||||
defending_effect = next((e for e in player_effects if e['effect_name'] == 'defending'), None)
|
||||
if defending_effect:
|
||||
is_defending = True
|
||||
reduction = defending_effect.get('value', 50) / 100
|
||||
npc_damage = max(1, int(npc_damage * (1 - reduction)))
|
||||
messages.append(create_combat_message("damage_reduced", origin="player", reduction=int(reduction * 100)))
|
||||
await db.remove_effect(player_id, 'defending')
|
||||
|
||||
buff_dmg_reduction = player_stats.get('buff_damage_reduction', 0.0) if player_stats else 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)))
|
||||
|
||||
buff_dmg_taken_increase = player_stats.get('buff_damage_taken_increase', 0.0) if player_stats else 0.0
|
||||
if buff_dmg_taken_increase > 0:
|
||||
npc_damage = int(npc_damage * (1 + buff_dmg_taken_increase))
|
||||
|
||||
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']
|
||||
|
||||
# Check for block (if shield is equipped)
|
||||
blocked = False
|
||||
if not dodged and player_stats and player_stats.get('has_shield', False):
|
||||
if random.random() < player_stats.get('block_chance', 0):
|
||||
blocked = True
|
||||
messages.append(create_combat_message(
|
||||
"combat_block",
|
||||
origin="player"
|
||||
))
|
||||
npc_damage = max(1, int(npc_damage * 0.2)) # Block mitigates 80% damage
|
||||
messages.append(create_combat_message("combat_dodge", origin="player"))
|
||||
await db.remove_effect(player_id, 'evade')
|
||||
elif player_stats and player_stats.get('buff_enemy_miss', False):
|
||||
dodged = True
|
||||
messages.append(create_combat_message("combat_dodge", origin="player"))
|
||||
elif player_stats and 'dodge_chance' in player_stats and random.random() < player_stats['dodge_chance']:
|
||||
dodged = True
|
||||
messages.append(create_combat_message("combat_dodge", origin="player"))
|
||||
|
||||
if not dodged and player_stats and player_stats.get('has_shield', False) and random.random() < player_stats.get('block_chance', 0):
|
||||
messages.append(create_combat_message("combat_block", origin="player"))
|
||||
npc_damage = max(1, int(npc_damage * 0.2))
|
||||
|
||||
if not dodged:
|
||||
armor_absorbed, broken_armor = await reduce_armor_func(player_id, npc_damage, is_defending)
|
||||
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)))
|
||||
armor_absorbed_visual = npc_damage - actual_damage
|
||||
else:
|
||||
actual_damage = max(1, npc_damage - armor_absorbed)
|
||||
armor_absorbed_visual = armor_absorbed
|
||||
|
||||
if not dodged:
|
||||
# Calculate armor durability loss based on PRE-reduction damage
|
||||
armor_absorbed, broken_armor = await reduce_armor_func(player_id, npc_damage)
|
||||
|
||||
# If player_stats provides a percentage reduction, apply it instead of raw absorption
|
||||
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)))
|
||||
armor_absorbed_visual = npc_damage - actual_damage
|
||||
else:
|
||||
actual_damage = max(1, npc_damage - armor_absorbed)
|
||||
armor_absorbed_visual = armor_absorbed
|
||||
|
||||
new_player_hp = max(0, player['hp'] - actual_damage)
|
||||
|
||||
messages.append(create_combat_message(
|
||||
"enemy_attack",
|
||||
origin="enemy",
|
||||
npc_name=npc_def.name,
|
||||
damage=actual_damage,
|
||||
armor_absorbed=armor_absorbed_visual
|
||||
))
|
||||
|
||||
if broken_armor and not dodged:
|
||||
for armor in broken_armor:
|
||||
messages.append(create_combat_message(
|
||||
"item_broken",
|
||||
origin="player",
|
||||
item_name=armor['name'],
|
||||
emoji=armor['emoji']
|
||||
))
|
||||
new_player_hp = max(0, player['hp'] - actual_damage)
|
||||
|
||||
await db.update_player(player_id, hp=new_player_hp)
|
||||
if skill and 'damage_multiplier' in skill.effects:
|
||||
messages.append(create_combat_message("skill_attack", origin="enemy", damage=actual_damage, skill_name=get_locale_string(skill.name, locale), skill_icon=skill.icon, hits=1))
|
||||
elif is_charging:
|
||||
messages.append(create_combat_message("enemy_special", origin="enemy", npc_name=npc_def.name, damage=actual_damage, armor_absorbed=armor_absorbed_visual))
|
||||
else:
|
||||
messages.append(create_combat_message("enemy_attack", origin="enemy", npc_name=npc_def.name, damage=actual_damage, armor_absorbed=armor_absorbed_visual))
|
||||
|
||||
if broken_armor:
|
||||
for armor in broken_armor:
|
||||
messages.append(create_combat_message("item_broken", origin="player", item_name=armor['name'], emoji=armor['emoji']))
|
||||
|
||||
await db.update_player(player_id, hp=new_player_hp)
|
||||
|
||||
# GENERATE NEXT INTENT
|
||||
|
||||
# Check if player defeated
|
||||
player_defeated = False
|
||||
if player['hp'] - actual_damage <= 0 and intent_type != 'defend': # Check HP after damage
|
||||
# Re-fetch to be sure or just trust calculation
|
||||
if new_player_hp <= 0:
|
||||
messages.append(create_combat_message(
|
||||
"player_defeated",
|
||||
origin="neutral",
|
||||
npc_name=npc_def.name
|
||||
))
|
||||
player_defeated = True
|
||||
await db.update_player(player_id, hp=0, is_dead=True)
|
||||
await db.update_player_statistics(player_id, deaths=1, damage_taken=actual_damage, increment=True)
|
||||
await db.end_combat(player_id)
|
||||
return messages, player_defeated
|
||||
if new_player_hp <= 0 and intent_type != 'defend' and intent_type != 'charge':
|
||||
messages.append(create_combat_message("player_defeated", origin="neutral", npc_name=npc_def.name))
|
||||
player_defeated = True
|
||||
await db.update_player(player_id, hp=0, is_dead=True)
|
||||
await db.update_player_statistics(player_id, deaths=1, damage_taken=actual_damage, increment=True)
|
||||
await db.end_combat(player_id)
|
||||
return messages, player_defeated
|
||||
|
||||
if not player_defeated:
|
||||
if actual_damage > 0:
|
||||
await db.update_player_statistics(player_id, damage_taken=actual_damage, increment=True)
|
||||
|
||||
# Generate NEXT intent
|
||||
# We need the updated NPC HP for the logic
|
||||
current_npc_hp = combat['npc_hp']
|
||||
if intent_type == 'defend':
|
||||
current_npc_hp = min(combat['npc_max_hp'], combat['npc_hp'] + int(combat['npc_max_hp'] * 0.05))
|
||||
|
||||
temp_combat_state = combat.copy()
|
||||
temp_combat_state['npc_hp'] = current_npc_hp
|
||||
|
||||
if actual_damage > 0:
|
||||
await db.update_player_statistics(player_id, damage_taken=actual_damage, increment=True)
|
||||
|
||||
current_npc_hp = combat['npc_hp']
|
||||
if intent_type == 'defend':
|
||||
current_npc_hp = min(combat['npc_max_hp'], combat['npc_hp'] + int(combat['npc_max_hp'] * 0.05))
|
||||
|
||||
temp_combat_state = combat.copy()
|
||||
temp_combat_state['npc_hp'] = current_npc_hp
|
||||
|
||||
if intent_type == 'charge':
|
||||
next_intent_str = 'charging_attack'
|
||||
else:
|
||||
next_intent = generate_npc_intent(npc_def, temp_combat_state)
|
||||
|
||||
# Update combat with new intent and turn
|
||||
await db.update_combat(player_id, {
|
||||
'turn': 'player',
|
||||
'turn_started_at': time.time(),
|
||||
'npc_intent': next_intent['type']
|
||||
})
|
||||
next_intent_str = f"{next_intent['type']}:{next_intent['value']}" if next_intent['type'] == 'skill' else next_intent['type']
|
||||
|
||||
await db.update_combat(player_id, {
|
||||
'turn': 'player',
|
||||
'turn_started_at': time.time(),
|
||||
'npc_intent': next_intent_str
|
||||
})
|
||||
|
||||
return messages, player_defeated
|
||||
|
||||
Reference in New Issue
Block a user