# Skills and Abilities System

## Overview

The game uses two distinct but interconnected systems for character progression and combat:

- **Skills**: Progression system that provides passive bonuses and unlocks abilities
- **Abilities**: Combat actions (attacks, spells, skills) that can be used in battle

**Key Distinction**: Skills are *unlocked through leveling* and provide permanent benefits. Abilities are *combat actions* that characters can execute during battles.

**⚠️ Terminology Note**: The word "skill" and "spell" have specific meanings in this system. See the [Terminology Clarification](#terminology-clarification) section below for important distinctions between Skills (progression nodes), Abilities (combat actions), Spells (a type of ability), and the confusing `AbilityType.SKILL` enum.

---

## Terminology Clarification

It's important to understand three distinct concepts that can be confusing due to overlapping names:

### 1. Skills (Skill Tree Nodes)

**What**: Nodes in the skill tree progression system that you unlock with skill points earned from leveling up.

**Where**: Defined in class YAML files at `/api/app/data/classes/*.yaml`

**Model**: `SkillNode` in [app/models/skills.py](../app/models/skills.py)

**Purpose**: Provide passive stat bonuses and/or unlock combat abilities

**Examples**:
- "Shield Bash" skill node (unlocks the Shield Bash ability)
- "Fortify" skill node (grants +5 defense bonus)
- "Fireball Mastery" skill node (unlocks the Fireball ability)

### 2. Abilities (Combat Actions)

**What**: Actions you can perform during combat - the umbrella term for all combat actions.

**Where**: Defined in separate YAML files at `/api/app/data/abilities/*.yaml`

**Model**: `Ability` in [app/models/abilities.py](../app/models/abilities.py)

**Purpose**: Define combat mechanics - damage, effects, costs, targeting, etc.

**Examples**:
- Fireball ability (deals fire damage)
- Shield Bash ability (stuns enemy)
- Heal ability (restores HP)
- Basic Attack ability (default physical attack)

### 3. Spells (A Type of Ability)

**What**: A **category** of ability that uses magic/arcane power (not a separate system).

**Type Field**: `ability_type: "spell"` in the Ability YAML

**Purpose**: Differentiate magical abilities from physical attacks or special skills

**Examples**:
- Fireball (ability_type: spell)
- Lightning Bolt (ability_type: spell)
- Heal (ability_type: spell)

### Ability Types (AbilityType Enum)

All abilities have an `ability_type` field that categorizes them. From [app/models/enums.py](../app/models/enums.py):

```python
class AbilityType(Enum):
    """Categories of abilities that can be used in combat or exploration."""

    ATTACK = "attack"           # Basic physical attack
    SPELL = "spell"             # Magical spell (arcane/divine)
    SKILL = "skill"             # Special class ability (⚠️ different from skill tree!)
    ITEM_USE = "item_use"       # Using a consumable item
    DEFEND = "defend"           # Defensive action
```

**⚠️ Important Confusion to Avoid**: `AbilityType.SKILL` refers to special class abilities used in combat (like a Rogue's "Sneak Attack"). This is **completely different** from "Skills" in the skill tree progression system (`SkillNode`).

### Complete Example: Fireball

To illustrate how all three concepts work together:

**1. Skill Tree Node** (what you unlock with a skill point):
```yaml
# In /api/app/data/classes/arcanist.yaml
- skill_id: fireball_mastery
  name: Fireball Mastery
  description: Learn to cast the devastating fireball spell
  tier: 1
  prerequisites: []
  effects:
    abilities:
      - fireball  # ← References the ability
```

**2. Ability Definition** (the combat action):
```yaml
# In /api/app/data/abilities/fireball.yaml
ability_id: "fireball"
name: "Fireball"
description: "Hurl a ball of fire at your enemies"
ability_type: "spell"  # ← This makes it a SPELL-type ability
base_power: 30
damage_type: "fire"
scaling_stat: "intelligence"
mana_cost: 15
```

**3. The Flow**:
- Player unlocks "Fireball Mastery" **skill** (spends 1 skill point)
- This grants access to the "Fireball" **ability**
- Fireball is categorized as a **spell** (ability_type: spell)
- Player can now use Fireball in combat

### Summary Table

| Term | What It Is | Where Defined | Purpose |
|------|------------|---------------|---------|
| **Skill** (SkillNode) | Progression node | `/api/app/data/classes/*.yaml` | Unlock abilities, grant bonuses |
| **Ability** | Combat action | `/api/app/data/abilities/*.yaml` | Define what you can do in battle |
| **Spell** | Type of ability | `ability_type: "spell"` | Magical/arcane combat actions |
| **AbilityType.SKILL** | Type of ability | `ability_type: "skill"` | Special class abilities (⚠️ not skill tree) |

---

## Architecture

### Skills

**Purpose**: Character progression and passive bonuses

**Data Model**: [app/models/skills.py](../app/models/skills.py)
- `SkillNode`: Individual skill in a tree
- `SkillTree`: Collection of related skills
- `PlayerClass`: Character class with multiple skill trees

**Data Location**: [app/data/classes/*.yaml](../app/data/classes/)
- Skills are embedded within class definitions
- Each class YAML contains 2+ skill trees
- Example: `vanguard.yaml`, `arcanist.yaml`

**Loader**: [app/services/class_loader.py](../app/services/class_loader.py)
- `ClassLoader` reads class YAML files
- Parses skill trees and nodes during class loading
- Caches PlayerClass instances

**Storage on Character**: [app/models/character.py](../app/models/character.py)
```python
unlocked_skills: List[str] = field(default_factory=list)  # Just skill IDs
```

### Abilities

**Purpose**: Combat actions and spells

**Data Model**: [app/models/abilities.py](../app/models/abilities.py)
- `Ability`: Complete definition of a combat action
- Includes damage, effects, costs, targeting, etc.

**Data Location**: [app/data/abilities/*.yaml](../app/data/abilities/)
- Each ability is a separate YAML file
- Example: `fireball.yaml`, `shield_bash.yaml`, `heal.yaml`

**Loader**: [app/models/abilities.py](../app/models/abilities.py)
- `AbilityLoader` class (lines 164-238)
- Loads abilities on-demand or in bulk
- Caches Ability instances

**Storage on Character**: Not stored directly
- Character has `player_class.starting_abilities`
- Abilities unlocked through skills are computed dynamically

---

## How Skills Unlock Abilities

### Skill Effects Dictionary

Skills can provide multiple types of benefits through their `effects` dictionary:

```yaml
# From vanguard.yaml
- skill_id: shield_bash
  name: Shield Bash
  description: Strike an enemy with your shield, dealing minor damage and stunning them
  tier: 1
  prerequisites: []
  effects:
    abilities:              # Unlocks abilities for combat use
      - shield_bash         # References /app/data/abilities/shield_bash.yaml
```

```yaml
# Skills can also provide stat bonuses
- skill_id: fortify
  name: Fortify
  description: Your defensive training grants you enhanced protection
  tier: 1
  prerequisites: []
  effects:
    stat_bonuses:
      defense: 5           # Passive stat increase
```

```yaml
# Or both at once
- skill_id: perfect_form
  name: Perfect Form
  description: Your combat technique reaches perfection
  tier: 5
  prerequisites:
    - weapon_mastery
  effects:
    stat_bonuses:
      strength: 20
      dexterity: 10
    combat_bonuses:
      crit_chance: 0.1
      crit_multiplier: 0.5
```

### Effect Types

Skills can define effects in several categories:

1. **`abilities`**: List of ability IDs to unlock
2. **`stat_bonuses`**: Direct stat increases (strength, defense, etc.)
3. **`combat_bonuses`**: Combat modifiers (crit_chance, crit_multiplier)
4. **`passive_effects`**: Special passive effects (stun_resistance, etc.)
5. **`ability_enhancements`**: Modify existing abilities

---

## Data Flow: Skill Unlock to Ability Usage

### 1. Character Levels Up

```python
# Character gains a level and skill point
character.level_up()
# character.level is now higher
# character.available_skill_points increases
```

### 2. Player Unlocks a Skill

```python
# Service validates and unlocks the skill
skill_id = "shield_bash"
character.unlocked_skills.append(skill_id)
# Character saves to database
```

### 3. Getting Available Abilities

```python
# Character.get_unlocked_abilities() - character.py:177-194
def get_unlocked_abilities(self) -> List[str]:
    # Start with class's built-in abilities
    abilities = list(self.player_class.starting_abilities)

    # Get all skill nodes from all trees
    all_skills = self.player_class.get_all_skills()

    # Collect abilities from unlocked skills
    for skill in all_skills:
        if skill.skill_id in self.unlocked_skills:
            abilities.extend(skill.get_unlocked_abilities())

    return abilities  # Returns list of ability IDs
```

### 4. Extracting Abilities from Skills

```python
# SkillNode.get_unlocked_abilities() - skills.py:73-87
def get_unlocked_abilities(self) -> List[str]:
    abilities = []
    if "abilities" in self.effects:
        ability = self.effects["abilities"]
        if isinstance(ability, list):
            abilities.extend(ability)
        else:
            abilities.append(ability)
    return abilities
```

### 5. Loading Ability Details for Combat

```python
# During combat initialization
ability_loader = AbilityLoader()
ability_ids = character.get_unlocked_abilities()

# Load full Ability objects
available_abilities = []
for ability_id in ability_ids:
    ability = ability_loader.load_ability(ability_id)
    if ability:
        available_abilities.append(ability)

# Now character can use these abilities in combat
```

### 6. Using an Ability in Combat

```python
# Player selects ability
ability = ability_loader.load_ability("fireball")

# Calculate power with character's stats
effective_stats = character.get_effective_stats()
damage = ability.calculate_power(effective_stats)

# Apply to target
target.current_hp -= damage

# Apply effects (DoT, buffs, debuffs)
effects = ability.get_effects_to_apply()
for effect in effects:
    target.active_effects.append(effect)
```

---

## Complete Example: Fireball (Step-by-Step Workflow)

This example shows the complete journey from skill definition through combat usage. For the YAML definitions, see the [Terminology Clarification](#terminology-clarification) section above.

### Step 1: Define the Skill (in class YAML)

See the Fireball Mastery skill node example in the Terminology section.

### Step 2: Define the Ability (separate YAML)

See the Fireball ability YAML example in the Terminology section.

**Key Point**: The skill's `effects.abilities` list contains `fireball`, which references `/api/app/data/abilities/fireball.yaml`.

### Step 3: Character Progression

```python
# Character is created as Arcanist
character = Character(
    name="Merlin",
    player_class=arcanist_class,
    level=1,
    unlocked_skills=[],  # No skills yet
)

# Starting abilities from class
character.get_unlocked_abilities()  # Returns: ["basic_attack"]

# Character reaches level 2, spends skill point
character.unlocked_skills.append("fireball_skill")

# Now has fireball ability
character.get_unlocked_abilities()  # Returns: ["basic_attack", "fireball"]
```

### Step 4: Combat Usage

```python
# Combat begins
ability_loader = AbilityLoader()
fireball = ability_loader.load_ability("fireball")

# Character's intelligence is 15
# Effective stats calculation includes all bonuses
effective_stats = character.get_effective_stats()  # intelligence = 15

# Calculate damage
damage = fireball.calculate_power(effective_stats)
# damage = 30 + (15 * 0.5) = 30 + 7.5 = 37

# Apply to enemy
enemy.current_hp -= 37

# Apply burning effect
burn_effect = fireball.get_effects_to_apply()[0]
enemy.active_effects.append(burn_effect)
# Enemy will take 5 fire damage per turn for 3 turns
```

---

## Skill Bonuses vs Abilities

### Passive Bonuses

Skills provide **permanent** stat increases calculated by `Character.get_effective_stats()`:

```python
# Character._get_skill_bonuses() - character.py:156-175
def _get_skill_bonuses(self) -> Dict[str, int]:
    bonuses: Dict[str, int] = {}

    # Get all skill nodes from all trees
    all_skills = self.player_class.get_all_skills()

    # Sum up bonuses from unlocked skills
    for skill in all_skills:
        if skill.skill_id in self.unlocked_skills:
            skill_bonuses = skill.get_stat_bonuses()
            for stat_name, bonus in skill_bonuses.items():
                bonuses[stat_name] = bonuses.get(stat_name, 0) + bonus

    return bonuses
```

### Extracting Stat Bonuses

```python
# SkillNode.get_stat_bonuses() - skills.py:57-71
def get_stat_bonuses(self) -> Dict[str, int]:
    bonuses = {}
    for key, value in self.effects.items():
        # Look for stat names in effects
        if key in ["strength", "dexterity", "constitution",
                   "intelligence", "wisdom", "charisma"]:
            bonuses[key] = value
        elif key == "defense" or key == "resistance" or key == "hit_points":
            bonuses[key] = value
    return bonuses
```

**Note**: The current implementation looks for direct stat keys in `effects`. The newer YAML format uses nested `stat_bonuses` dictionaries. This may need updating.

---

## Key Design Principles

1. **Separation of Concerns**
   - Skills = Progression system (what you unlock)
   - Abilities = Combat system (what you can do)

2. **Data-Driven Design**
   - Both are defined in YAML files
   - Game designers can modify without code changes
   - Easy to balance and iterate

3. **Loose Coupling**
   - Skills reference abilities by ID only
   - Abilities are loaded on-demand
   - Changes to abilities don't affect skill definitions

4. **Efficient Storage**
   - Character only stores skill IDs (not full objects)
   - Abilities computed dynamically when needed
   - Reduces database payload

5. **Flexible Effects System**
   - Skills can unlock multiple abilities
   - Skills can provide stat bonuses
   - Skills can do both simultaneously
   - Room for future effect types

---

## Related Documentation

- [DATA_MODELS.md](DATA_MODELS.md) - Character system and items
- [GAME_SYSTEMS.md](GAME_SYSTEMS.md) - Combat mechanics
- [API_REFERENCE.md](API_REFERENCE.md) - API endpoints for skills

---

## Future Enhancements

Potential improvements to the system:

1. **Skill Levels**: Allow skills to be upgraded multiple times
2. **Ability Enhancements**: Skills that modify existing abilities
3. **Conditional Unlocks**: Require items, quests, or achievements
4. **Skill Synergies**: Bonuses for unlocking related skills
5. **Respec System**: Allow skill point redistribution
6. **Skill Prerequisites Validation**: Runtime validation of skill trees
