using Kryz.CharacterStats.Examples; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; public class CastingStateController : MonoBehaviour { public bool isCasting; public PlayerMovement playerMovement; public LayerMask movementMask; private MovementSpeedModifierEffectInstance movementSpeedModifier; private Camera cam; private Ray ray; private RaycastHit hit; private ProjectileSpawnLocationController projectileSpawnLocationController; private CharacterStats stats; public UnityEvent OnAbilityQueued = new UnityEvent(); private void Awake() { cam = Camera.main; movementSpeedModifier = playerMovement.GetComponent(); stats = playerMovement.GetComponent(); projectileSpawnLocationController = transform.root.GetComponentInChildren(); } /// /// Calculate current attack interval based on base speed and modifiers /// private float GetCurrentAttackInterval() { float totalAttackSpeed = GameConstants.CharacterStatsBalancing.BaseAttacksPerSecond * (1f + (MathHelpers.NormalizePercentage(stats.GetStat("attackspeed").Value) / 100f)); float interval = 1f / totalAttackSpeed; return Mathf.Max(interval, GameConstants.CharacterStatsBalancing.FastestAttacksPerSecond); } /// /// Get current attacks per second /// public float GetCurrentAttacksPerSecond() { return 1f / GetCurrentAttackInterval(); } public void RequestAbilityCast(BaseAbility ability, Action abilityExecution) { if (isCasting) return; if (!ability.castableWhileMoving) { movementSpeedModifier.ToggleCastPenalty(true); } OnAbilityQueued.Invoke(ability); StartCoroutine(Casting(ability, abilityExecution)); } public void RequestAbilityChannel(BaseAbility channeledAbility, Action abilityChannelExecution) { if (isCasting) return; if (!channeledAbility.castableWhileMoving) { movementSpeedModifier.ToggleCastPenalty(true); } OnAbilityQueued.Invoke(channeledAbility); StartCoroutine(Channeling(channeledAbility, abilityChannelExecution)); } private IEnumerator Casting(BaseAbility ability, Action abilityExecution) { isCasting = true; // Use player attack speed instead of ability cast time float castTime = GetCurrentAttackInterval(); CastBarHandler.Instance.ShowCastBar(ability, abilityExecution, castTime); playerMovement.InstantFaceCast(projectileSpawnLocationController.GetLookat()); // Wait for the calculated attack interval yield return new WaitForSeconds(castTime); if (!GameConstants.Animation.IsAnimationEventBasedAbility(ability.animationType)) { abilityExecution.Invoke(); } isCasting = false; if (!ability.castableWhileMoving) { movementSpeedModifier.ToggleCastPenalty(false); } } private IEnumerator Channeling(BaseAbility channeledAbility, Action abilityChannelExecution) { isCasting = true; // For channeled abilities, you might want different behavior // This maintains the original channeling logic CastBarHandler.Instance.ShowChannelingBar(channeledAbility, abilityChannelExecution); playerMovement.InstantFaceCast(projectileSpawnLocationController.GetLookat()); abilityChannelExecution.Invoke(); if (!channeledAbility.castableWhileMoving) { movementSpeedModifier.ToggleCastPenalty(true); } yield return null; } public void ResetChannelingCast() { isCasting = false; movementSpeedModifier.ToggleCastPenalty(false); } public void ResetCastingOnMeleeAnimationEvent() { isCasting = false; movementSpeedModifier.ToggleCastPenalty(false); StopAllCoroutines(); } }