Combat foundation complete
This commit is contained in:
@@ -20,7 +20,7 @@ from app.models.stats import Stats
|
||||
from app.models.abilities import Ability, AbilityLoader
|
||||
from app.models.effects import Effect
|
||||
from app.models.items import Item
|
||||
from app.models.enums import CombatStatus, AbilityType, DamageType, EffectType
|
||||
from app.models.enums import CombatStatus, AbilityType, DamageType, EffectType, StatType
|
||||
from app.services.damage_calculator import DamageCalculator, DamageResult
|
||||
from app.services.enemy_loader import EnemyLoader, get_enemy_loader
|
||||
from app.services.session_service import get_session_service
|
||||
@@ -94,6 +94,7 @@ class ActionResult:
|
||||
combat_status: Final combat status if ended
|
||||
next_combatant_id: ID of combatant whose turn is next
|
||||
turn_effects: Effects that triggered at turn start/end
|
||||
rewards: Combat rewards if victory (XP, gold, items)
|
||||
"""
|
||||
|
||||
success: bool
|
||||
@@ -105,6 +106,7 @@ class ActionResult:
|
||||
next_combatant_id: Optional[str] = None
|
||||
next_is_player: bool = True # True if next turn is player's
|
||||
turn_effects: List[Dict[str, Any]] = field(default_factory=list)
|
||||
rewards: Optional[Dict[str, Any]] = None # Populated on victory
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary for API response."""
|
||||
@@ -127,6 +129,7 @@ class ActionResult:
|
||||
"next_combatant_id": self.next_combatant_id,
|
||||
"next_is_player": self.next_is_player,
|
||||
"turn_effects": self.turn_effects,
|
||||
"rewards": self.rewards,
|
||||
}
|
||||
|
||||
|
||||
@@ -451,6 +454,113 @@ class CombatService:
|
||||
|
||||
return rewards
|
||||
|
||||
def check_existing_combat(
|
||||
self,
|
||||
session_id: str,
|
||||
user_id: str
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Check if a session has an existing active combat encounter.
|
||||
|
||||
Returns combat summary if exists, None otherwise.
|
||||
|
||||
Args:
|
||||
session_id: Game session ID
|
||||
user_id: User ID for authorization
|
||||
|
||||
Returns:
|
||||
Dictionary with combat summary if in combat, None otherwise
|
||||
"""
|
||||
logger.info("Checking for existing combat",
|
||||
session_id=session_id)
|
||||
|
||||
session = self.session_service.get_session(session_id, user_id)
|
||||
|
||||
if not session.is_in_combat():
|
||||
return None
|
||||
|
||||
# Get encounter details
|
||||
encounter = self.get_combat_state(session_id, user_id)
|
||||
if not encounter:
|
||||
return None
|
||||
|
||||
# Build summary of combatants
|
||||
players = []
|
||||
enemies = []
|
||||
for combatant in encounter.combatants:
|
||||
combatant_info = {
|
||||
"name": combatant.name,
|
||||
"current_hp": combatant.current_hp,
|
||||
"max_hp": combatant.max_hp,
|
||||
"is_alive": combatant.is_alive(),
|
||||
}
|
||||
if combatant.is_player:
|
||||
players.append(combatant_info)
|
||||
else:
|
||||
enemies.append(combatant_info)
|
||||
|
||||
return {
|
||||
"has_active_combat": True,
|
||||
"encounter_id": encounter.encounter_id,
|
||||
"round_number": encounter.round_number,
|
||||
"status": encounter.status.value,
|
||||
"players": players,
|
||||
"enemies": enemies,
|
||||
}
|
||||
|
||||
def abandon_combat(
|
||||
self,
|
||||
session_id: str,
|
||||
user_id: str
|
||||
) -> bool:
|
||||
"""
|
||||
Abandon an existing combat encounter without completing it.
|
||||
|
||||
Deletes the encounter from the database and clears the session
|
||||
reference. No rewards are distributed.
|
||||
|
||||
Args:
|
||||
session_id: Game session ID
|
||||
user_id: User ID for authorization
|
||||
|
||||
Returns:
|
||||
True if combat was abandoned, False if no combat existed
|
||||
"""
|
||||
logger.info("Abandoning combat",
|
||||
session_id=session_id)
|
||||
|
||||
session = self.session_service.get_session(session_id, user_id)
|
||||
|
||||
if not session.is_in_combat():
|
||||
logger.info("No combat to abandon",
|
||||
session_id=session_id)
|
||||
return False
|
||||
|
||||
encounter_id = session.active_combat_encounter_id
|
||||
|
||||
# Delete encounter from repository
|
||||
if encounter_id:
|
||||
try:
|
||||
self.combat_repository.delete_encounter(encounter_id)
|
||||
logger.info("Deleted encounter from repository",
|
||||
encounter_id=encounter_id)
|
||||
except Exception as e:
|
||||
logger.warning("Failed to delete encounter from repository",
|
||||
encounter_id=encounter_id,
|
||||
error=str(e))
|
||||
|
||||
# Clear session combat references
|
||||
session.active_combat_encounter_id = None
|
||||
session.combat_encounter = None # Clear legacy field too
|
||||
session.update_activity()
|
||||
self.session_service.update_session(session)
|
||||
|
||||
logger.info("Combat abandoned",
|
||||
session_id=session_id,
|
||||
encounter_id=encounter_id)
|
||||
|
||||
return True
|
||||
|
||||
# =========================================================================
|
||||
# Action Execution
|
||||
# =========================================================================
|
||||
@@ -549,6 +659,7 @@ class CombatService:
|
||||
if status == CombatStatus.VICTORY:
|
||||
rewards = self._calculate_rewards(encounter, session, user_id)
|
||||
result.message += f" Victory! Earned {rewards.experience} XP and {rewards.gold} gold."
|
||||
result.rewards = rewards.to_dict()
|
||||
|
||||
# End encounter in repository
|
||||
if session.active_combat_encounter_id:
|
||||
@@ -699,6 +810,11 @@ class CombatService:
|
||||
result.combat_ended = True
|
||||
result.combat_status = status
|
||||
|
||||
# Calculate and distribute rewards on victory
|
||||
if status == CombatStatus.VICTORY:
|
||||
rewards = self._calculate_rewards(encounter, session, user_id)
|
||||
result.rewards = rewards.to_dict()
|
||||
|
||||
# End encounter in repository
|
||||
if session.active_combat_encounter_id:
|
||||
self.combat_repository.end_encounter(
|
||||
@@ -946,7 +1062,7 @@ class CombatService:
|
||||
effect_type=EffectType.BUFF,
|
||||
duration=1,
|
||||
power=5, # +5 defense
|
||||
stat_affected="constitution",
|
||||
stat_affected=StatType.CONSTITUTION,
|
||||
source="defend_action",
|
||||
)
|
||||
combatant.add_effect(defense_buff)
|
||||
|
||||
Reference in New Issue
Block a user