Files
ThordekkCore/CLASSAI_DUPLICATION_ANALYSIS.md
T
2026-01-20 21:25:30 -03:00

11 KiB
Raw Blame History

ClassAI Code Duplication Analysis

Executive Summary

Critical Code Duplication Detected: 1,740 occurrences of common methods across 200 files.

Impact

  • Maintenance Burden: Bug fixes require changes in 13+ places
  • Linker Warnings: Multiple definition warnings for identical methods
  • Memory Waste: Duplicated code increases binary size unnecessarily
  • Inconsistency Risk: Same bugs exist in multiple classes

Duplicate Code Patterns Identified

Pattern 1: Cooldown Management (EVERY SPECIALIZATION)

Duplicated in: 50+ files

void UpdateCooldowns(uint32 diff)
{
    for (auto& cooldown : _cooldowns)
        if (cooldown.second > diff)
            cooldown.second -= diff;
        else
            cooldown.second = 0;
}

Pattern 2: Ability Validation (EVERY SPECIALIZATION)

Duplicated in: 50+ files

bool CanUseAbility(uint32 spellId)
{
    auto it = _cooldowns.find(spellId);
    if (it != _cooldowns.end() && it->second > 0)
        return false;
    return HasEnoughResource(spellId);
}

Pattern 3: Combat State Management (EVERY SPECIALIZATION)

Duplicated in: 50+ files

void OnCombatStart(::Unit* target)
{
    Player* bot = GetBot();
    if (!bot)
        return;
    // Identical logic across all specs
}

void OnCombatEnd()
{
    // Identical cleanup logic
}

Pattern 4: Range Calculation (EVERY SPECIALIZATION)

Duplicated in: 50+ files

float GetOptimalRange(::Unit* target)
{
    // Melee: return 5.0f
    // Ranged DPS: return 25.0f
    // Healers: return 30.0f
    // Only difference is the return value
}

Pattern 5: Resource Management (MANY SPECIALIZATIONS)

Duplicated in: 30+ files

bool HasEnoughResource(uint32 spellId)
{
    // Check mana/rage/energy/etc
    // Logic varies slightly by resource type
}

void ConsumeResource(uint32 spellId)
{
    // Deduct resource cost
    // Logic varies slightly by resource type
}

Files with Duplicate Definitions (Linker Warnings)

FrostSpecialization (Death Knight)

warning LNK4006: "GetOptimalRange" bereits definiert
warning LNK4006: "GetOptimalPosition" bereits definiert
warning LNK4006: "ConsumeResource" bereits definiert
warning LNK4006: "HasEnoughResource" bereits definiert
warning LNK4006: "OnCombatEnd" bereits definiert
warning LNK4006: "OnCombatStart" bereits definiert
warning LNK4006: "CanUseAbility" bereits definiert
warning LNK4006: "UpdateCooldowns" bereits definiert
warning LNK4006: "UpdateBuffs" bereits definiert
warning LNK4006: "UpdateRotation" bereits definiert

Current Class Hierarchy

ClassAI (base)
├── DeathKnightAI
│   ├── BloodSpecialization (862 lines)
│   ├── FrostSpecialization
│   └── UnholySpecialization
├── PaladinAI
│   ├── HolySpecialization
│   ├── ProtectionSpecialization
│   └── RetributionSpecialization (433 lines)
├── WarriorAI
│   ├── ArmsSpecialization (620 lines)
│   ├── FurySpecialization
│   └── ProtectionSpecialization
├── MageAI
│   ├── ArcaneSpecialization
│   ├── FireSpecialization (622 lines)
│   └── FrostSpecialization
├── (9 more classes with 3+ specs each)

Total Specs: ~40 specializations Estimated Duplicated Code: 60-70% of each spec is duplicated boilerplate

Proposed Refactoring Strategy

Phase 1: Create Base Template System

New Base Class: CombatSpecializationBase

template<typename ResourceType>
class CombatSpecializationBase : public ClassAI
{
protected:
    // ===== COMMON COOLDOWN MANAGEMENT =====
    std::unordered_map<uint32, uint32> _cooldowns;

    virtual void UpdateCooldowns(uint32 diff) final
    {
        for (auto& cooldown : _cooldowns)
            if (cooldown.second > diff)
                cooldown.second -= diff;
            else
                cooldown.second = 0;
    }

    bool IsCooldownReady(uint32 spellId) const
    {
        auto it = _cooldowns.find(spellId);
        return it == _cooldowns.end() || it->second == 0;
    }

    void StartCooldown(uint32 spellId, uint32 duration)
    {
        _cooldowns[spellId] = duration;
    }

    // ===== COMMON RESOURCE MANAGEMENT =====
    ResourceType _currentResource;
    ResourceType _maxResource;

    virtual bool HasEnoughResource(uint32 spellId)
    {
        ResourceType cost = GetResourceCost(spellId);
        return _currentResource >= cost;
    }

    virtual void ConsumeResource(uint32 spellId)
    {
        ResourceType cost = GetResourceCost(spellId);
        _currentResource -= cost;
    }

    // ===== COMMON ABILITY VALIDATION =====
    virtual bool CanUseAbility(uint32 spellId) final
    {
        return IsCooldownReady(spellId) &&
               HasEnoughResource(spellId) &&
               IsSpellUsable(spellId);
    }

    // ===== COMMON COMBAT STATE =====
    virtual void OnCombatStart(::Unit* target) override
    {
        _inCombat = true;
        _combatTarget = target;
        OnSpecializationCombatStart(target);
    }

    virtual void OnCombatEnd() override
    {
        _inCombat = false;
        _combatTarget = nullptr;
        OnSpecializationCombatEnd();
    }

    // ===== HOOKS FOR SPECIALIZATION-SPECIFIC LOGIC =====
    virtual void OnSpecializationCombatStart(::Unit* target) {}
    virtual void OnSpecializationCombatEnd() {}
    virtual ResourceType GetResourceCost(uint32 spellId) = 0;
    virtual void UpdateRotation(::Unit* target) = 0;
};

Specialized Resource Types

// For Mana-based classes (Mage, Priest, Paladin, etc)
using ManaSpecialization = CombatSpecializationBase<uint32>;

// For Rage-based classes (Warrior)
using RageSpecialization = CombatSpecializationBase<uint32>;

// For Energy-based classes (Rogue, Monk)
using EnergySpecialization = CombatSpecializationBase<uint32>;

// For Rune-based classes (Death Knight)
struct RuneResource { /* complex rune management */ };
using RuneSpecialization = CombatSpecializationBase<RuneResource>;

Role-Based Templates

// Melee DPS template
template<typename ResourceType>
class MeleeDpsSpecialization : public CombatSpecializationBase<ResourceType>
{
public:
    float GetOptimalRange(::Unit* target) override { return 5.0f; }

protected:
    bool IsInMeleeRange(::Unit* target) const
    {
        return GetBot()->GetDistance(target) <= 5.0f;
    }
};

// Ranged DPS template
template<typename ResourceType>
class RangedDpsSpecialization : public CombatSpecializationBase<ResourceType>
{
public:
    float GetOptimalRange(::Unit* target) override { return 25.0f; }

protected:
    bool ShouldMaintainDistance(::Unit* target) const
    {
        return GetBot()->GetDistance(target) < 15.0f;
    }
};

// Healer template
template<typename ResourceType>
class HealerSpecialization : public CombatSpecializationBase<ResourceType>
{
public:
    float GetOptimalRange(::Unit* target) override { return 30.0f; }

protected:
    ::Unit* GetBestHealTarget()
    {
        // Common heal target selection logic
    }
};

// Tank template
template<typename ResourceType>
class TankSpecialization : public CombatSpecializationBase<ResourceType>
{
public:
    float GetOptimalRange(::Unit* target) override { return 5.0f; }

protected:
    void ManageThreat(::Unit* target)
    {
        // Common threat management
    }
};

Phase 2: Refactor Each Specialization

Before (433 lines - RetributionSpecialization)

class RetributionSpecialization : public PaladinSpecialization
{
public:
    void UpdateCooldowns(uint32 diff) { /* 10 lines */ }
    bool CanUseAbility(uint32 spellId) { /* 8 lines */ }
    void OnCombatStart(::Unit* target) { /* 12 lines */ }
    void OnCombatEnd() { /* 8 lines */ }
    float GetOptimalRange(::Unit* target) { return 5.0f; }
    // ... 390 more lines of actual rotation logic
};

After (~250 lines - 42% reduction)

class RetributionSpecialization : public MeleeDpsSpecialization<uint32>
{
public:
    RetributionSpecialization(Player* bot)
        : MeleeDpsSpecialization(bot)
    {
        // Specialization-specific initialization
    }

    void UpdateRotation(::Unit* target) override
    {
        // ONLY the unique Retribution logic
        if (ShouldCastTemplarsVerdict(target))
            CastTemplarsVerdict(target);
        else if (ShouldCastCrusaderStrike(target))
            CastCrusaderStrike(target);
        // ... only rotation logic, no boilerplate
    }

protected:
    uint32 GetResourceCost(uint32 spellId) override
    {
        // Mana cost calculation
    }

private:
    // ONLY Retribution-specific state
    uint8 _holyPower;
    bool _hasArtOfWar;

    // ONLY Retribution-specific methods
    bool ShouldCastTemplarsVerdict(::Unit* target);
    void CastTemplarsVerdict(::Unit* target);
    bool ShouldCastCrusaderStrike(::Unit* target);
    void CastCrusaderStrike(::Unit* target);
};

Expected Benefits

Code Reduction

  • Before: ~40 specs × 400 lines avg = 16,000 lines
  • After: 40 specs × 200 lines avg = 8,000 lines
  • Reduction: 50% less code to maintain

Eliminated Duplication

  • Remove 1,740 duplicate method implementations
  • Eliminate all linker warnings
  • Single source of truth for common logic

Maintenance Improvements

  • Fix bugs in ONE place instead of 40+
  • Add features to base class, all specs benefit
  • Consistent behavior across all classes

Performance Improvements

  • Smaller binary size
  • Better CPU cache utilization
  • Reduced memory footprint

Implementation Plan

Week 1: Foundation

  • Day 1-2: Create CombatSpecializationBase template
  • Day 3: Create role-based templates (Melee, Ranged, Healer, Tank)
  • Day 4: Create resource-type specializations
  • Day 5: Test compilation, ensure no regressions

Week 2: First Wave (Simpler Classes)

  • Day 1: Refactor Warriors (3 specs)
  • Day 2: Refactor Paladins (3 specs)
  • Day 3: Refactor Hunters (3 specs)
  • Day 4: Refactor Rogues (3 specs)
  • Day 5: Test all 12 specs in combat

Week 3: Second Wave (Complex Classes)

  • Day 1: Refactor Death Knights (3 specs)
  • Day 2: Refactor Mages (3 specs)
  • Day 3: Refactor Priests (3 specs)
  • Day 4: Refactor Druids (4 specs)
  • Day 5: Test all 13 specs in combat

Week 4: Final Wave + Testing

  • Day 1: Refactor Warlocks (3 specs)
  • Day 2: Refactor Shamans (3 specs)
  • Day 3: Refactor Monks (3 specs)
  • Day 4: Refactor Demon Hunters + Evokers
  • Day 5: Full regression testing with all 40 specs

Risk Mitigation

Backup Strategy

  • Keep original files as .cpp.backup
  • Branch: refactor/classai-templates
  • Commit after each class refactored

Testing Strategy

  • Test EACH class after refactoring
  • Verify combat works for all 3 specs
  • Check memory usage hasn't increased
  • Confirm no new crashes

Rollback Plan

If refactoring causes issues:

  1. Revert to backup files
  2. Cherry-pick working classes
  3. Leave problematic classes unreffactored temporarily

Success Criteria

✅ All linker warnings eliminated ✅ 50%+ code reduction ✅ All 40 specs compile cleanly ✅ All specs function in combat ✅ No performance regression ✅ No memory increase

Conclusion

This refactoring will:

  • Cut maintenance burden in HALF
  • Eliminate technical debt
  • Make future features easier to add
  • Improve code quality significantly
  • Pay dividends for ALL future development

Recommendation: Proceed with refactoring immediately before adding more features.