using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class NetworkedChanneling : MonoBehaviour { [Header("Visuals")] [SerializeField] protected GameObject visuals; [Header("Physics:")] [SerializeField] protected LayerMask abilityHitLayer; [Header("Set by Code")] public Taggable ownerTag; public Health ownerHealth; public Mana ownerMana; public ChanneledAbility ability; public float duration; public bool canHitSelf; public bool followUser; public bool allowAiming; public float radius; public float healthCostPerTick; public float percentHealthCostPerTick; public float manaCostPerTick; public float percentManaCostPerTick; private float finalHealthCostPerTick; private float finalManaCostPerTick; public bool channeling = false; private float endTime; protected Taggable target; protected List targets = new List(); protected Collider[] hits; public BoxCollider OptionalBoxOverlap; ProjectileSpawnLocationController aimController; private CastingStateController castingStateController; public UnityEvent> onTickHappened = new UnityEvent>(); protected void OnTickPerformed() { onTickHappened.Invoke(ownerTag, targets); } protected virtual void Awake() { } public virtual void Init(ref Coroutine channelingCoroutine) { channeling = true; aimController = ownerTag.GetComponentInChildren(); castingStateController = ownerTag.GetComponentInChildren(); channelingCoroutine = StartCoroutine(ExecuteChanneling()); if (followUser) StartCoroutine(FollowUser()); StartCoroutine(SelfDestruct()); } IEnumerator FollowUser() { while (Time.time < endTime) { this.transform.position = ownerTag.transform.position; if (allowAiming) { this.transform.forward = aimController.GetLookat(); } yield return new WaitForEndOfFrame(); } visuals.SetActive(false); } IEnumerator ExecuteChanneling() { visuals.SetActive(true); endTime = Time.time + duration; while (Time.time < endTime) { yield return new WaitForSeconds(0.5f); if (!HasEnoughResourcesForNextTick()) { castingStateController.ResetChannelingCast(); CastBarHandler.Instance.CancelChannelingOnButtonReleased(); channeling = false; DisableVisuals(); break; } HandleCostsForNextTick(); CheckSurroundings(); } } private bool HasEnoughResourcesForNextTick() { finalManaCostPerTick = manaCostPerTick + ownerMana.GetMaxValue() * percentManaCostPerTick; finalHealthCostPerTick = healthCostPerTick + ownerHealth.GetMaxValue() * percentHealthCostPerTick; return ownerMana.EnoughMana(finalManaCostPerTick) && ownerHealth.EnoughHealth(finalHealthCostPerTick); } private void HandleCostsForNextTick() { ownerMana.ChangeValue(-finalManaCostPerTick); ownerHealth.ChangeValue(-finalHealthCostPerTick); } public void DisableVisuals() { visuals.SetActive(false); } protected virtual void CheckSurroundings() { if (!channeling) return; if (OptionalBoxOverlap != null) { hits = Physics.OverlapBox(OptionalBoxOverlap.transform.position, OptionalBoxOverlap.size / 2f, OptionalBoxOverlap.transform.rotation, abilityHitLayer); } else { hits = Physics.OverlapSphere(this.transform.position, radius, abilityHitLayer); } targets.Clear(); foreach (Collider collider in hits) { Debug.Log("hit collider " + collider.name); target = collider.GetComponentInParent(); if (target == null) continue; if (target == ownerTag && !canHitSelf) continue; //Debug.Log("hit collider, targetTag: " + target.targetTag.name); foreach (TargetTag tag in ability.targettingTags) { Debug.Log("hit collider, ability.Tags: " + tag); } if (!target.IsValidTarget(ability.targettingTags)) continue; Debug.Log("hit collider, added target: " + target.name); targets.Add(target); } foreach (BaseEffect effect in ability.abilityEffects) { effect.ApplyEffect(ownerTag, targets); } OnTickPerformed(); } protected IEnumerator SelfDestruct() { yield return new WaitForSeconds(duration + 1f); Destroy(this.gameObject); } protected void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawWireSphere(this.transform.position, radius); } }