308 lines
10 KiB
C#

// ============================================================================
// RUNTIME ABILITY WRAPPER - DROP-IN REPLACEMENT FOR BaseAbility
// ============================================================================
using System;
using System.Collections.Generic;
using UnityEngine;
// ============================================================================
// 1. RUNTIME ABILITY INSTANCE - Wraps Your Existing BaseAbility
// ============================================================================
/// <summary>
/// Runtime wrapper for BaseAbility that adds modifiers and behaviors
/// Can be used anywhere you currently use BaseAbility references
/// </summary>
public class RuntimeAbilityInstance
{
// ========================================================================
// CORE DATA - Wraps your existing ScriptableObject
// ========================================================================
[SerializeField] private BaseAbility sourceAbility;
private List<AbilityModifier> activeModifiers = new List<AbilityModifier>();
private List<RuntimeBehavior> runtimeBehaviors = new List<RuntimeBehavior>();
// Runtime state
private float lastUsedTime;
private int currentCharges;
// ========================================================================
// CONSTRUCTOR
// ========================================================================
public RuntimeAbilityInstance(BaseAbility source)
{
sourceAbility = source;
currentCharges = GetMaxCharges(); // In case you add charges later
RecalculateModifiedValues();
}
// ========================================================================
// PROPERTIES - Direct access to original + modified values
// ========================================================================
// Original values (unchanged)
public BaseAbility SourceAbility => sourceAbility;
public string displayName => sourceAbility.displayName;
public Sprite Icon => sourceAbility.Icon;
public List<TargetTag> targettingTags => sourceAbility.targettingTags;
public List<GameTag> tags => sourceAbility.tags;
public List<BaseEffect> abilityEffects => sourceAbility.abilityEffects;
public bool castableWhileMoving => sourceAbility.castableWhileMoving;
public AbilityAnimationType animationType => sourceAbility.animationType;
// Modified values (affected by modifiers)
public float manaCost { get; private set; }
public float healthCost { get; private set; }
public float classResourceCost { get; private set; }
public float spiritPowerReserveCost { get; private set; }
public float percentMaxManaCost { get; private set; }
public float percentMaxHealthCost { get; private set; }
public float castTime { get; private set; }
public float cooldown { get; private set; }
// Runtime state properties
public float LastUsedTime => lastUsedTime;
public bool IsOnCooldown => Time.time < lastUsedTime + cooldown;
public float CooldownRemaining => Mathf.Max(0f, (lastUsedTime + cooldown) - Time.time);
public List<AbilityModifier> ActiveModifiers => new List<AbilityModifier>(activeModifiers);
public List<RuntimeBehavior> RuntimeBehaviors => new List<RuntimeBehavior>(runtimeBehaviors);
// ========================================================================
// EXECUTION METHODS - Same signature as your BaseAbility
// ========================================================================
public virtual void Execute(Taggable user)
{
if (!CanExecute(user)) return;
// Execute pre-cast behaviors
ExecuteBehaviors(BehaviorTrigger.PreCast, user, null, Vector3.zero);
// Execute original ability
sourceAbility.Execute(user);
// Execute post-cast behaviors
ExecuteBehaviors(BehaviorTrigger.PostCast, user, null, Vector3.zero);
// Update runtime state
lastUsedTime = Time.time;
}
public virtual void Execute(Taggable user, Vector3 point)
{
if (!CanExecute(user)) return;
ExecuteBehaviors(BehaviorTrigger.PreCast, user, null, point);
sourceAbility.Execute(user, point);
ExecuteBehaviors(BehaviorTrigger.PostCast, user, null, point);
lastUsedTime = Time.time;
}
public virtual void Execute(Taggable user, Transform target)
{
if (!CanExecute(user)) return;
ExecuteBehaviors(BehaviorTrigger.PreCast, user, target, target.position);
sourceAbility.Execute(user, target);
ExecuteBehaviors(BehaviorTrigger.PostCast, user, target, target.position);
lastUsedTime = Time.time;
}
// ========================================================================
// ABILITY STATE CHECKS
// ========================================================================
public bool CanExecute(Taggable user)
{
// Check cooldown
if (IsOnCooldown) return false;
// Check resources using modified costs
return CanAffordResources(user);
}
private bool CanAffordResources(Taggable user)
{
// Use the modified costs, not original
var userMana = user.GetComponent<Mana>();
if (userMana != null)
{
float finalManaCost = manaCost + userMana.GetMaxValue() * percentMaxManaCost;
if (!userMana.EnoughMana(finalManaCost)) return false;
}
var userHealth = user.GetComponent<Health>();
if (userHealth != null)
{
float finalHealthCost = healthCost + userHealth.GetMaxValue() * percentMaxHealthCost;
if (userHealth.GetCurrentValue() <= finalHealthCost) return false;
}
var userClassResource = user.GetComponent<ClassResource>();
if (userClassResource != null && classResourceCost > 0)
{
if (userClassResource.GetCurrentValue() < classResourceCost) return false;
}
return true;
}
public float GetFinalManaCost(Mana userMana)
{
return manaCost + userMana.GetMaxValue() * percentMaxManaCost;
}
public float GetFinalHealthCost(Health userHealth)
{
return healthCost + userHealth.GetMaxValue() * percentMaxHealthCost;
}
// ========================================================================
// MODIFIER SYSTEM
// ========================================================================
public void AddModifier(AbilityModifier modifier)
{
activeModifiers.Add(modifier);
RecalculateModifiedValues();
}
public void RemoveModifier(AbilityModifier modifier)
{
activeModifiers.Remove(modifier);
RecalculateModifiedValues();
}
public void RemoveAllModifiers()
{
activeModifiers.Clear();
RecalculateModifiedValues();
}
private void RecalculateModifiedValues()
{
// Start with original values
manaCost = sourceAbility.manaCost;
healthCost = sourceAbility.healthCost;
classResourceCost = sourceAbility.classResourceCost;
spiritPowerReserveCost = sourceAbility.spiritPowerReserveCost;
percentMaxManaCost = sourceAbility.percentMaxManaCost;
percentMaxHealthCost = sourceAbility.percentMaxHealthCost;
castTime = sourceAbility.castTime;
cooldown = sourceAbility.cooldown;
// Apply all modifiers
foreach (var modifier in activeModifiers)
{
ApplyModifier(modifier);
}
}
private void ApplyModifier(AbilityModifier modifier)
{
// Resource cost modifiers
manaCost = manaCost * modifier.manaCostMultiplier + modifier.manaCostFlat;
healthCost = healthCost * modifier.healthCostMultiplier + modifier.healthCostFlat;
classResourceCost = classResourceCost * modifier.classResourceCostMultiplier + modifier.classResourceCostFlat;
// Timing modifiers
castTime *= modifier.castTimeMultiplier;
cooldown *= modifier.cooldownMultiplier;
// Ensure values don't go negative
manaCost = Mathf.Max(0f, manaCost);
healthCost = Mathf.Max(0f, healthCost);
classResourceCost = Mathf.Max(0f, classResourceCost);
castTime = Mathf.Max(0f, castTime);
cooldown = Mathf.Max(0f, cooldown);
}
// ========================================================================
// RUNTIME BEHAVIOR SYSTEM
// ========================================================================
public void AddBehavior(RuntimeBehavior behavior)
{
runtimeBehaviors.Add(behavior);
}
public void RemoveBehavior(RuntimeBehavior behavior)
{
runtimeBehaviors.Remove(behavior);
}
public T GetBehavior<T>() where T : RuntimeBehavior
{
return runtimeBehaviors.Find(b => b is T) as T;
}
public void RemoveBehavior<T>() where T : RuntimeBehavior
{
for (int i = runtimeBehaviors.Count - 1; i >= 0; i--)
{
if (runtimeBehaviors[i] is T)
{
runtimeBehaviors.RemoveAt(i);
}
}
}
private void ExecuteBehaviors(BehaviorTrigger trigger, Taggable user, Transform target, Vector3 point)
{
foreach (var behavior in runtimeBehaviors)
{
if (behavior.Trigger == trigger)
{
behavior.Execute(this, user, target, point);
}
}
}
// ========================================================================
// UTILITY METHODS
// ========================================================================
public RuntimeAbilityInstance Clone()
{
var clone = new RuntimeAbilityInstance(sourceAbility);
// Copy modifiers
foreach (var modifier in activeModifiers)
{
clone.AddModifier(modifier);
}
// Copy behaviors
foreach (var behavior in runtimeBehaviors)
{
clone.AddBehavior(behavior.Clone());
}
return clone;
}
private int GetMaxCharges()
{
// Future: add charge system
return 1;
}
// ========================================================================
// IMPLICIT CONVERSION - Makes it work seamlessly
// ========================================================================
public static implicit operator BaseAbility(RuntimeAbilityInstance instance)
{
return instance.sourceAbility;
}
public static implicit operator RuntimeAbilityInstance(BaseAbility ability)
{
return new RuntimeAbilityInstance(ability);
}
}