SEPARATION_OF_CONCERNS_GUIDE.md 5.1 KB

Improved Enemy System: Separation of Concerns

Overview

The enemy character system has been refactored to provide better separation of concerns between individual enemy data and spawning/encounter logic.

What Changed

Before: Mixed Responsibilities ❌

// EnemyCharacterData.cs (BAD)
public class EnemyCharacterData : ScriptableObject
{
    // Individual enemy stats
    public int maxHealth = 10;
    public int attack = 8;
    
    // Spawning logic (WRONG PLACE!)
    public int minGroupSize = 1;
    public int maxGroupSize = 3;
    public BiomeType[] preferredBiomes;
}

After: Clean Separation ✅

// EnemyCharacterData.cs (GOOD)
public class EnemyCharacterData : ScriptableObject
{
    // ONLY individual enemy data
    public int maxHealth = 10;
    public int attack = 8;
    public WeaponType preferredWeaponType;
    public bool regeneratesHealth;
    // etc...
}

// CombatTravelEvent.cs (GOOD)
public class CombatTravelEvent : TravelEvent
{
    // ONLY spawning/encounter logic
    public int minEnemies = 1;
    public int maxEnemies = 3;
    public float forestChance = 0.8f;
    public EnemyCharacterData[] possibleEnemies;
}

Benefits

🎯 Single Responsibility Principle

  • EnemyCharacterData: Individual enemy stats, abilities, and rewards
  • Travel Events: When, where, and how many enemies spawn

🔄 Reusability

  • Same enemy can be used in different encounters with different group sizes
  • Forest Troll can appear solo in "Boss Encounter" or in groups in "Troll Pack"

🛠️ Maintainability

  • Change spawn behavior without touching enemy stats
  • Modify enemy stats without affecting encounter design

🎮 Game Design Flexibility

// Same Skeleton Warrior in different contexts:

// Solo boss fight
var bossFight = new CombatTravelEvent {
    minEnemies = 1,
    maxEnemies = 1,
    possibleEnemies = { skeletonWarrior }
};

// Small patrol
var patrol = new CombatTravelEvent {
    minEnemies = 2,
    maxEnemies = 4,
    possibleEnemies = { skeletonWarrior, goblinScout }
};

// Large army encounter
var army = new CombatTravelEvent {
    minEnemies = 5,
    maxEnemies = 8,
    possibleEnemies = { skeletonWarrior, goblinScout, forestTroll }
};

File Structure

Enemy Data (Individual Stats)

Assets/Scripts/Characters/Enemies/
├── EnemyCharacterData.cs        (Base class for enemy stats)
├── SkeletonWarrior.asset        (Individual skeleton data)
├── GoblinScout.asset           (Individual goblin data)
└── ForestTroll.asset           (Individual troll data)

Travel Events (Encounter Logic)

Assets/Scripts/Events/
├── TravelEventTypes.cs         (CombatTravelEvent class)
├── SpecificTravelEvents.cs     (Specific encounter types)
└── TravelEventSystem.cs        (Main event manager)

Assets/Resources/TravelEvents/
├── SkeletonAmbush.asset        (1-3 skeletons in forests)
├── GoblinRaid.asset            (2-5 goblins on roads)
└── TrollEncounter.asset        (1 troll in deep forests)

Migration Guide

For Existing Enemy Assets

  • Automatically cleaned: Spawn settings removed from all enemy assets
  • No code changes needed: Generated enemy scripts still work
  • Backward compatible: Existing functionality preserved

For New Travel Events

// Create travel events that reference enemy data:
[CreateAssetMenu(fileName = "Skeleton Ambush", menuName = "RPG/Travel Events/Specific/Skeleton Ambush")]
public class SkeletonAmbushEvent : CombatTravelEvent
{
    void OnEnable()
    {
        eventName = "Skeleton Ambush";
        
        // Encounter-specific settings
        minEnemies = 1;
        maxEnemies = 3;
        forestChance = 0.8f;
        roadChance = 0.2f;
        
        // Reference to enemy data (drag from Project window)
        possibleEnemies = new EnemyCharacterData[] { 
            /* Drag SkeletonWarrior.asset here */ 
        };
    }
}

Best Practices

✅ Do This:

  • Put individual stats in EnemyCharacterData
  • Put encounter rules in Travel Events
  • Reference enemy data assets in travel events
  • Use multiple travel events for the same enemy in different contexts

❌ Don't Do This:

  • Put spawn logic in enemy data
  • Put individual stats in travel events
  • Hardcode encounter parameters in enemy scripts
  • Mix individual and group behaviors

Example Scenarios

Scenario 1: Bandit Encounters

// BanditLeader.asset (Individual)
maxHealth = 25, attack = 10, goldReward = 50

// BanditSoldier.asset (Individual)  
maxHealth = 15, attack = 8, goldReward = 20

// BanditAmbush.asset (Encounter)
minEnemies = 3, maxEnemies = 5
possibleEnemies = [BanditSoldier, BanditLeader]
forestChance = 0.9f, roadChance = 0.3f

Scenario 2: Dynamic Difficulty

// Same enemies, different encounter intensity:

// Easy Encounter
minEnemies = 1, maxEnemies = 2

// Normal Encounter  
minEnemies = 2, maxEnemies = 4

// Hard Encounter
minEnemies = 3, maxEnemies = 6

This separation makes the system much more flexible and maintainable! 🎯