308 lines
10 KiB
C#
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);
|
|
}
|
|
} |