first commit
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using OTG.Lab06.Characters;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTG.Lab06.Abilities
|
||||
{
|
||||
public readonly struct AbilityCastContext
|
||||
{
|
||||
public AbilityCastContext(AbilityData ability, Transform caster, PlayerStats playerStats, Vector3 origin, Vector3 forward)
|
||||
{
|
||||
Ability = ability;
|
||||
Caster = caster;
|
||||
PlayerStats = playerStats;
|
||||
Origin = origin;
|
||||
Forward = forward.sqrMagnitude <= 0.0001f ? Vector3.forward : forward.normalized;
|
||||
}
|
||||
|
||||
public AbilityData Ability { get; }
|
||||
public Transform Caster { get; }
|
||||
public PlayerStats PlayerStats { get; }
|
||||
public Vector3 Origin { get; }
|
||||
public Vector3 Forward { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b1c5987e5ece80c8bb4717d9f7eadde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTG.Lab06.Abilities
|
||||
{
|
||||
[CreateAssetMenu(menuName = "OTG/Lab06/Ability Data", fileName = "AbilityData")]
|
||||
public sealed class AbilityData : ScriptableObject
|
||||
{
|
||||
[Header("Identity")]
|
||||
public string abilityId;
|
||||
public string abilityName;
|
||||
[TextArea(2, 5)] public string description;
|
||||
public Sprite icon;
|
||||
|
||||
[Header("Numbers")]
|
||||
public float manaCost;
|
||||
public float cooldown;
|
||||
public float damage;
|
||||
public float castRange;
|
||||
|
||||
[Header("Behaviour")]
|
||||
public GameObject effectPrefab;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 583149b3796dedd8f900ff0e616d02ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTG.Lab06.Abilities
|
||||
{
|
||||
public abstract class AbilityEffect : MonoBehaviour
|
||||
{
|
||||
public abstract void Activate(AbilityCastContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ceef3b14322f2aa3094f34228660f0ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,142 @@
|
||||
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<AbilityData> learnedAbilities = new List<AbilityData>();
|
||||
|
||||
private readonly List<AbilityRuntime> runtimes = new List<AbilityRuntime>();
|
||||
|
||||
public event Action<IReadOnlyList<AbilityRuntime>> AbilitiesChanged;
|
||||
public event Action ResourcesChanged;
|
||||
|
||||
public IReadOnlyList<AbilityRuntime> Abilities => runtimes;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (playerStats == null)
|
||||
{
|
||||
playerStats = GetComponent<PlayerStats>();
|
||||
}
|
||||
|
||||
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<AbilityData> 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<AbilityEffect>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd3c590200e1b8d059a875031b6fde6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTG.Lab06.Abilities
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class AbilityRuntime
|
||||
{
|
||||
public AbilityRuntime(AbilityData data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public AbilityData Data { get; }
|
||||
public float CooldownRemaining { get; private set; }
|
||||
public bool IsReady => CooldownRemaining <= 0f;
|
||||
public float CooldownNormalized => Data == null || Data.cooldown <= 0f ? 0f : Mathf.Clamp01(CooldownRemaining / Data.cooldown);
|
||||
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
if (CooldownRemaining <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CooldownRemaining = Mathf.Max(0f, CooldownRemaining - deltaTime);
|
||||
}
|
||||
|
||||
public void StartCooldown()
|
||||
{
|
||||
CooldownRemaining = Data == null ? 0f : Mathf.Max(0f, Data.cooldown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca078e83fa216cd60ae3941e64408c83
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user