Files
Code_of_Conquest/api/app/models/enemy.py
Phillip Tarrant 03ab783eeb Combat Backend & Data Models
- Implement Combat Service
- Implement Damage Calculator
- Implement Effect Processor
- Implement Combat Actions
- Created Combat API Endpoints
2025-11-26 15:43:20 -06:00

218 lines
6.8 KiB
Python

"""
Enemy data models for combat encounters.
This module defines the EnemyTemplate dataclass representing enemies/monsters
that can be encountered in combat. Enemy definitions are loaded from YAML files
for data-driven game design.
"""
from dataclasses import dataclass, field, asdict
from typing import Dict, Any, List, Optional, Tuple
from enum import Enum
from app.models.stats import Stats
class EnemyDifficulty(Enum):
"""Enemy difficulty levels for scaling and encounter building."""
EASY = "easy"
MEDIUM = "medium"
HARD = "hard"
BOSS = "boss"
@dataclass
class LootEntry:
"""
Single entry in an enemy's loot table.
Attributes:
item_id: Reference to item definition
drop_chance: Probability of dropping (0.0 to 1.0)
quantity_min: Minimum quantity if dropped
quantity_max: Maximum quantity if dropped
"""
item_id: str
drop_chance: float = 0.1
quantity_min: int = 1
quantity_max: int = 1
def to_dict(self) -> Dict[str, Any]:
"""Serialize loot entry to dictionary."""
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'LootEntry':
"""Deserialize loot entry from dictionary."""
return cls(
item_id=data["item_id"],
drop_chance=data.get("drop_chance", 0.1),
quantity_min=data.get("quantity_min", 1),
quantity_max=data.get("quantity_max", 1),
)
@dataclass
class EnemyTemplate:
"""
Template definition for an enemy type.
EnemyTemplates define the base characteristics of enemy types. When combat
starts, instances are created from templates with randomized variations.
Attributes:
enemy_id: Unique identifier (e.g., "goblin", "dire_wolf")
name: Display name (e.g., "Goblin Scout")
description: Flavor text about the enemy
base_stats: Base stat block for this enemy
abilities: List of ability_ids this enemy can use
loot_table: Potential drops on defeat
experience_reward: Base XP granted on defeat
gold_reward_min: Minimum gold dropped
gold_reward_max: Maximum gold dropped
difficulty: Difficulty classification for encounter building
tags: Classification tags (e.g., ["humanoid", "goblinoid"])
image_url: Optional image reference for UI
Combat-specific attributes:
base_damage: Base damage for basic attack (no weapon)
crit_chance: Critical hit chance (0.0 to 1.0)
flee_chance: Chance to successfully flee from this enemy
"""
enemy_id: str
name: str
description: str
base_stats: Stats
abilities: List[str] = field(default_factory=list)
loot_table: List[LootEntry] = field(default_factory=list)
experience_reward: int = 10
gold_reward_min: int = 1
gold_reward_max: int = 5
difficulty: EnemyDifficulty = EnemyDifficulty.EASY
tags: List[str] = field(default_factory=list)
image_url: Optional[str] = None
# Combat attributes
base_damage: int = 5
crit_chance: float = 0.05
flee_chance: float = 0.5
def get_gold_reward(self) -> int:
"""
Roll random gold reward within range.
Returns:
Random gold amount between min and max
"""
import random
return random.randint(self.gold_reward_min, self.gold_reward_max)
def roll_loot(self) -> List[Dict[str, Any]]:
"""
Roll for loot drops based on loot table.
Returns:
List of dropped items with quantities
"""
import random
drops = []
for entry in self.loot_table:
if random.random() < entry.drop_chance:
quantity = random.randint(entry.quantity_min, entry.quantity_max)
drops.append({
"item_id": entry.item_id,
"quantity": quantity,
})
return drops
def is_boss(self) -> bool:
"""Check if this enemy is a boss."""
return self.difficulty == EnemyDifficulty.BOSS
def has_tag(self, tag: str) -> bool:
"""Check if enemy has a specific tag."""
return tag.lower() in [t.lower() for t in self.tags]
def to_dict(self) -> Dict[str, Any]:
"""
Serialize enemy template to dictionary.
Returns:
Dictionary containing all enemy data
"""
return {
"enemy_id": self.enemy_id,
"name": self.name,
"description": self.description,
"base_stats": self.base_stats.to_dict(),
"abilities": self.abilities,
"loot_table": [entry.to_dict() for entry in self.loot_table],
"experience_reward": self.experience_reward,
"gold_reward_min": self.gold_reward_min,
"gold_reward_max": self.gold_reward_max,
"difficulty": self.difficulty.value,
"tags": self.tags,
"image_url": self.image_url,
"base_damage": self.base_damage,
"crit_chance": self.crit_chance,
"flee_chance": self.flee_chance,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'EnemyTemplate':
"""
Deserialize enemy template from dictionary.
Args:
data: Dictionary containing enemy data (from YAML or JSON)
Returns:
EnemyTemplate instance
"""
# Parse base stats
stats_data = data.get("base_stats", {})
base_stats = Stats.from_dict(stats_data)
# Parse loot table
loot_table = [
LootEntry.from_dict(entry)
for entry in data.get("loot_table", [])
]
# Parse difficulty
difficulty_value = data.get("difficulty", "easy")
if isinstance(difficulty_value, str):
difficulty = EnemyDifficulty(difficulty_value)
else:
difficulty = difficulty_value
return cls(
enemy_id=data["enemy_id"],
name=data["name"],
description=data.get("description", ""),
base_stats=base_stats,
abilities=data.get("abilities", []),
loot_table=loot_table,
experience_reward=data.get("experience_reward", 10),
gold_reward_min=data.get("gold_reward_min", 1),
gold_reward_max=data.get("gold_reward_max", 5),
difficulty=difficulty,
tags=data.get("tags", []),
image_url=data.get("image_url"),
base_damage=data.get("base_damage", 5),
crit_chance=data.get("crit_chance", 0.05),
flee_chance=data.get("flee_chance", 0.5),
)
def __repr__(self) -> str:
"""String representation of the enemy template."""
return (
f"EnemyTemplate({self.enemy_id}, {self.name}, "
f"difficulty={self.difficulty.value}, "
f"xp={self.experience_reward})"
)