Runtime ability system
This commit is contained in:
parent
226eae666e
commit
00b86fa97c
@ -78,7 +78,7 @@ Material:
|
|||||||
- _Mode: 0
|
- _Mode: 0
|
||||||
- _OcclusionStrength: 1
|
- _OcclusionStrength: 1
|
||||||
- _Parallax: 0.02
|
- _Parallax: 0.02
|
||||||
- _Rotation: 6.6876717
|
- _Rotation: 7.162766
|
||||||
- _SmoothnessTextureChannel: 0
|
- _SmoothnessTextureChannel: 0
|
||||||
- _SpecularHighlights: 1
|
- _SpecularHighlights: 1
|
||||||
- _SrcBlend: 1
|
- _SrcBlend: 1
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 5. EASY MIGRATION HELPER - Update Your Existing Components
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to easily convert your existing ability references
|
||||||
|
/// Just change "BaseAbility" to "RuntimeAbilityInstance" in your existing scripts
|
||||||
|
/// </summary>
|
||||||
|
public static class AbilityMigrationHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a BaseAbility to RuntimeAbilityInstance
|
||||||
|
/// </summary>
|
||||||
|
public static RuntimeAbilityInstance ToRuntime(this BaseAbility ability)
|
||||||
|
{
|
||||||
|
return new RuntimeAbilityInstance(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a modified version of an ability
|
||||||
|
/// </summary>
|
||||||
|
public static RuntimeAbilityInstance CreateModifiedVersion(BaseAbility baseAbility, params AbilityModifier[] modifiers)
|
||||||
|
{
|
||||||
|
var runtime = new RuntimeAbilityInstance(baseAbility);
|
||||||
|
|
||||||
|
foreach (var modifier in modifiers)
|
||||||
|
{
|
||||||
|
runtime.AddModifier(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create legendary version with special behaviors
|
||||||
|
/// </summary>
|
||||||
|
public static RuntimeAbilityInstance CreateLegendaryVersion(BaseAbility baseAbility, params RuntimeBehavior[] behaviors)
|
||||||
|
{
|
||||||
|
var runtime = new RuntimeAbilityInstance(baseAbility);
|
||||||
|
|
||||||
|
foreach (var behavior in behaviors)
|
||||||
|
{
|
||||||
|
runtime.AddBehavior(behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fddd81fa3189fcf43a2207ea59bdae95
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 2. ABILITY MODIFIER SYSTEM
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class AbilityModifier
|
||||||
|
{
|
||||||
|
[Header("Resource Cost Modifiers")]
|
||||||
|
public float manaCostMultiplier = 1f;
|
||||||
|
public float manaCostFlat = 0f;
|
||||||
|
public float healthCostMultiplier = 1f;
|
||||||
|
public float healthCostFlat = 0f;
|
||||||
|
public float classResourceCostMultiplier = 1f;
|
||||||
|
public float classResourceCostFlat = 0f;
|
||||||
|
|
||||||
|
[Header("Timing Modifiers")]
|
||||||
|
public float castTimeMultiplier = 1f;
|
||||||
|
public float cooldownMultiplier = 1f;
|
||||||
|
|
||||||
|
[Header("Effect Modifiers")]
|
||||||
|
public float damageMultiplier = 1f;
|
||||||
|
public float healingMultiplier = 1f;
|
||||||
|
public float durationMultiplier = 1f;
|
||||||
|
public float rangeMultiplier = 1f;
|
||||||
|
|
||||||
|
[Header("Meta Info")]
|
||||||
|
public string modifierName = "Unnamed Modifier";
|
||||||
|
public string description = "";
|
||||||
|
public float duration = -1f; // -1 = permanent
|
||||||
|
|
||||||
|
// Factory methods for common modifiers
|
||||||
|
public static AbilityModifier CreateDamageBoost(float multiplier, string name = "Damage Boost")
|
||||||
|
{
|
||||||
|
return new AbilityModifier
|
||||||
|
{
|
||||||
|
damageMultiplier = multiplier,
|
||||||
|
modifierName = name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AbilityModifier CreateCooldownReduction(float reductionPercent, string name = "Cooldown Reduction")
|
||||||
|
{
|
||||||
|
return new AbilityModifier
|
||||||
|
{
|
||||||
|
cooldownMultiplier = 1f - (reductionPercent / 100f),
|
||||||
|
modifierName = name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AbilityModifier CreateManaCostReduction(float reductionPercent, string name = "Mana Cost Reduction")
|
||||||
|
{
|
||||||
|
return new AbilityModifier
|
||||||
|
{
|
||||||
|
manaCostMultiplier = 1f - (reductionPercent / 100f),
|
||||||
|
modifierName = name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 759877a48198a3549b2a51623fe20a7c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,59 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 6. USAGE EXAMPLES - How to Use in Your Existing Code
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
public class AbilityUsageExamples : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Your Existing Abilities")]
|
||||||
|
public BaseAbility fireballAbility;
|
||||||
|
public BaseAbility healingAbility;
|
||||||
|
|
||||||
|
// These can now be RuntimeAbilityInstances!
|
||||||
|
private RuntimeAbilityInstance runtimeFireball;
|
||||||
|
private RuntimeAbilityInstance runtimeHealing;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
// Convert your existing abilities
|
||||||
|
runtimeFireball = fireballAbility.ToRuntime();
|
||||||
|
runtimeHealing = healingAbility.ToRuntime();
|
||||||
|
|
||||||
|
// Example: Add legendary item effect
|
||||||
|
AddLegendaryFireballEffect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddLegendaryFireballEffect()
|
||||||
|
{
|
||||||
|
// This is how you'd handle your legendary item!
|
||||||
|
var explosionBehavior = new ExplodeOnHitBehavior
|
||||||
|
{
|
||||||
|
explosionRadius = 5f,
|
||||||
|
explosionDamage = 50f
|
||||||
|
};
|
||||||
|
|
||||||
|
runtimeFireball.AddBehavior(explosionBehavior);
|
||||||
|
|
||||||
|
// Also add a damage boost
|
||||||
|
var damageBoost = AbilityModifier.CreateDamageBoost(1.5f, "Legendary Damage");
|
||||||
|
runtimeFireball.AddModifier(damageBoost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Your existing ability usage code works exactly the same!
|
||||||
|
public void CastFireball()
|
||||||
|
{
|
||||||
|
var user = GetComponent<Taggable>();
|
||||||
|
|
||||||
|
// This works exactly like your old BaseAbility.Execute()
|
||||||
|
runtimeFireball.Execute(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CastFireballAtTarget(Transform target)
|
||||||
|
{
|
||||||
|
var user = GetComponent<Taggable>();
|
||||||
|
runtimeFireball.Execute(user, target);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1d5a488d66722a468d8af337e46e1e1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 30b6d22e7928d8047a51992b3fda64b6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,74 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class ChainLightningBehavior : RuntimeBehavior
|
||||||
|
{
|
||||||
|
public int maxChains = 3;
|
||||||
|
public float chainRange = 8f;
|
||||||
|
public float damageMultiplier = 0.5f; // Each chain does 50% of original damage
|
||||||
|
|
||||||
|
public ChainLightningBehavior()
|
||||||
|
{
|
||||||
|
Trigger = BehaviorTrigger.OnHit;
|
||||||
|
BehaviorName = "Chain Lightning";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute(RuntimeAbilityInstance ability, Taggable user, Transform target, Vector3 point)
|
||||||
|
{
|
||||||
|
if (target == null) return;
|
||||||
|
|
||||||
|
var currentTarget = target.GetComponent<Taggable>();
|
||||||
|
var chainedTargets = new List<Taggable> { currentTarget };
|
||||||
|
|
||||||
|
for (int i = 0; i < maxChains; i++)
|
||||||
|
{
|
||||||
|
var nextTarget = FindNextChainTarget(currentTarget.transform.position, chainedTargets);
|
||||||
|
if (nextTarget == null) break;
|
||||||
|
|
||||||
|
chainedTargets.Add(nextTarget);
|
||||||
|
|
||||||
|
// Apply reduced damage
|
||||||
|
var health = nextTarget.GetComponent<Health>();
|
||||||
|
if (health != null)
|
||||||
|
{
|
||||||
|
float chainDamage = CalculateChainDamage(ability, i + 1);
|
||||||
|
health.ChangeValue(-chainDamage);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTarget = nextTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Taggable FindNextChainTarget(Vector3 position, List<Taggable> excludeTargets)
|
||||||
|
{
|
||||||
|
var colliders = Physics.OverlapSphere(position, chainRange);
|
||||||
|
|
||||||
|
foreach (var collider in colliders)
|
||||||
|
{
|
||||||
|
var target = collider.GetComponent<Taggable>();
|
||||||
|
if (target != null && !excludeTargets.Contains(target))
|
||||||
|
{
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float CalculateChainDamage(RuntimeAbilityInstance ability, int chainNumber)
|
||||||
|
{
|
||||||
|
// This would need to be adapted to your damage system
|
||||||
|
// For now, just return a reduced amount
|
||||||
|
return 50f * Mathf.Pow(damageMultiplier, chainNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RuntimeBehavior Clone()
|
||||||
|
{
|
||||||
|
return new ChainLightningBehavior
|
||||||
|
{
|
||||||
|
maxChains = this.maxChains,
|
||||||
|
chainRange = this.chainRange,
|
||||||
|
damageMultiplier = this.damageMultiplier
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3d7ec80971f1d2d4b93d7395e6dcf78c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,58 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 4. COMMON RUNTIME BEHAVIORS - Your Legendary Item Effects!
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class ExplodeOnHitBehavior : RuntimeBehavior
|
||||||
|
{
|
||||||
|
public GameObject explosionPrefab;
|
||||||
|
public float explosionRadius = 5f;
|
||||||
|
public float explosionDamage = 30f;
|
||||||
|
public LayerMask targetLayers = -1;
|
||||||
|
|
||||||
|
public ExplodeOnHitBehavior()
|
||||||
|
{
|
||||||
|
Trigger = BehaviorTrigger.OnHit;
|
||||||
|
BehaviorName = "Explode on Hit";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute(RuntimeAbilityInstance ability, Taggable user, Transform target, Vector3 point)
|
||||||
|
{
|
||||||
|
Vector3 explosionPoint = target != null ? target.position : point;
|
||||||
|
|
||||||
|
// Spawn explosion visual if prefab exists
|
||||||
|
if (explosionPrefab != null)
|
||||||
|
{
|
||||||
|
Object.Instantiate(explosionPrefab, explosionPoint, Quaternion.identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find targets in explosion radius
|
||||||
|
var colliders = Physics.OverlapSphere(explosionPoint, explosionRadius, targetLayers);
|
||||||
|
|
||||||
|
foreach (var collider in colliders)
|
||||||
|
{
|
||||||
|
var explosionTarget = collider.GetComponent<Taggable>();
|
||||||
|
if (explosionTarget != null && explosionTarget != user)
|
||||||
|
{
|
||||||
|
// Apply explosion damage
|
||||||
|
var health = explosionTarget.GetComponent<Health>();
|
||||||
|
if (health != null)
|
||||||
|
{
|
||||||
|
health.ChangeValue(-explosionDamage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RuntimeBehavior Clone()
|
||||||
|
{
|
||||||
|
return new ExplodeOnHitBehavior
|
||||||
|
{
|
||||||
|
explosionPrefab = this.explosionPrefab,
|
||||||
|
explosionRadius = this.explosionRadius,
|
||||||
|
explosionDamage = this.explosionDamage,
|
||||||
|
targetLayers = this.targetLayers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7b92d75ddf02f444fa969dcb95660e60
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,34 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class LifeStealBehavior : RuntimeBehavior
|
||||||
|
{
|
||||||
|
public float lifeStealPercent = 0.15f; // 15% life steal
|
||||||
|
|
||||||
|
public LifeStealBehavior()
|
||||||
|
{
|
||||||
|
Trigger = BehaviorTrigger.OnHit;
|
||||||
|
BehaviorName = "Life Steal";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute(RuntimeAbilityInstance ability, Taggable user, Transform target, Vector3 point)
|
||||||
|
{
|
||||||
|
// This would need to track the damage dealt to calculate life steal
|
||||||
|
// For now, just heal a fixed amount
|
||||||
|
var userHealth = user.GetComponent<Health>();
|
||||||
|
if (userHealth != null)
|
||||||
|
{
|
||||||
|
float healAmount = 20f * lifeStealPercent; // Placeholder calculation
|
||||||
|
userHealth.ChangeValue(healAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RuntimeBehavior Clone()
|
||||||
|
{
|
||||||
|
return new LifeStealBehavior
|
||||||
|
{
|
||||||
|
lifeStealPercent = this.lifeStealPercent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 091400d6a45eaac4782ee5636a0f0d1f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,308 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 63ca0a6b1e4e799418c4e8dd0cf4ace5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,19 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public enum BehaviorTrigger
|
||||||
|
{
|
||||||
|
PreCast, // Before ability executes
|
||||||
|
PostCast, // After ability executes
|
||||||
|
OnHit, // When ability hits target
|
||||||
|
OnKill, // When ability kills target
|
||||||
|
OnCrit // When ability crits
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class RuntimeBehavior
|
||||||
|
{
|
||||||
|
public BehaviorTrigger Trigger { get; set; }
|
||||||
|
public string BehaviorName { get; set; }
|
||||||
|
|
||||||
|
public abstract void Execute(RuntimeAbilityInstance ability, Taggable user, Transform target, Vector3 point);
|
||||||
|
public abstract RuntimeBehavior Clone();
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a8f3389f97c1f343ba02dc9b50a1848
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
x
Reference in New Issue
Block a user