using System; using System.Collections.Generic; using OTG.Lab06.Characters; using UnityEngine; namespace OTG.Lab06.Abilities { public sealed class AbilityManager : MonoBehaviour { [SerializeField] private PlayerStats playerStats; [SerializeField] private Transform castOrigin; [SerializeField] private List learnedAbilities = new List(); private readonly List runtimes = new List(); public event Action> AbilitiesChanged; public event Action ResourcesChanged; public IReadOnlyList Abilities => runtimes; private void Awake() { if (playerStats == null) { playerStats = GetComponent(); } if (castOrigin == null) { castOrigin = transform; } RebuildRuntime(); } private void Update() { for (int i = 0; i < runtimes.Count; i++) { runtimes[i].Tick(Time.deltaTime); } HandleHotkeys(); AbilitiesChanged?.Invoke(runtimes); } public void SetLearnedAbilities(IEnumerable abilities) { learnedAbilities.Clear(); learnedAbilities.AddRange(abilities); RebuildRuntime(); } public bool TryCast(int abilityIndex) { if (abilityIndex < 0 || abilityIndex >= runtimes.Count) { return false; } AbilityRuntime runtime = runtimes[abilityIndex]; AbilityData ability = runtime.Data; if (ability == null || !runtime.IsReady || playerStats == null || !playerStats.SpendMana(ability.manaCost)) { return false; } if (ability.effectPrefab == null) { Debug.LogWarning($"Ability '{ability.abilityName}' has no effect prefab.", ability); playerStats.RestoreMana(ability.manaCost); return false; } Quaternion rotation = Quaternion.LookRotation(GetCastForward(), Vector3.up); GameObject effectObject = Instantiate(ability.effectPrefab, GetCastOrigin(), rotation); AbilityEffect effect = effectObject.GetComponent(); if (effect == null) { Debug.LogWarning($"Ability prefab '{ability.effectPrefab.name}' has no AbilityEffect component.", ability.effectPrefab); Destroy(effectObject); playerStats.RestoreMana(ability.manaCost); return false; } var context = new AbilityCastContext(ability, transform, playerStats, GetCastOrigin(), GetCastForward()); effect.Activate(context); runtime.StartCooldown(); ResourcesChanged?.Invoke(); AbilitiesChanged?.Invoke(runtimes); return true; } private void RebuildRuntime() { runtimes.Clear(); foreach (AbilityData ability in learnedAbilities) { if (ability != null) { runtimes.Add(new AbilityRuntime(ability)); } } AbilitiesChanged?.Invoke(runtimes); } private void HandleHotkeys() { for (int i = 0; i < runtimes.Count && i < 9; i++) { if (Input.GetKeyDown((KeyCode)((int)KeyCode.Alpha1 + i))) { TryCast(i); } } } private Vector3 GetCastOrigin() { return castOrigin == null ? transform.position + Vector3.up : castOrigin.position; } private Vector3 GetCastForward() { if (Camera.main != null) { Vector3 cameraForward = Camera.main.transform.forward; cameraForward.y = 0f; if (cameraForward.sqrMagnitude > 0.01f) { return cameraForward.normalized; } } Vector3 forward = transform.forward; forward.y = 0f; return forward.sqrMagnitude <= 0.01f ? Vector3.forward : forward.normalized; } } }