using System; using System.Linq; using Schema; using UnityEngine; using VOID.Generic.Dict; using VOID.Generic.Objects.Unit; using VOID.Generic.Objects.Unit.Actions; using VOID.Generic.Objects.Unit.Events; using VOID.ScriptableObjects.Abilities; using Action = Schema.Action; namespace VOID.Generic.AI.Schema.Actions { [Description("Order this unit to use selected ability")] [Category(SchemaCategory.ThisUnit)] public class UnitUseAbility : Action { private class UnitUseAbilityMemory { public SchemaUnitEventWrapper onCastAnimationEnd; public bool isRunning; public bool isFinished; } private enum TargetType { Position, Unit } [SerializeField] private BlackboardEntrySelector _ability; [SerializeField, Space(15)] private TargetType _targetType; [SerializeField] private BlackboardEntrySelector _position; [SerializeField] private BlackboardEntrySelector _unit; public override NodeStatus Tick(object nodeMemory, SchemaAgent agent) { var memory = (UnitUseAbilityMemory)nodeMemory; var unitAgent = (UnitObjectSchemaAgent)agent; var thisUnit = unitAgent.unit; // Ability not selected if (!_ability.value) return NodeStatus.Failure; // Whenever is during casting or already done that if (memory.isFinished) return NodeStatus.Success; if (memory.isRunning) return NodeStatus.Running; // Targets not selected if (_targetType == TargetType.Position && !_position.value) return NodeStatus.Failure; if (_targetType == TargetType.Unit && !_unit.value) return NodeStatus.Failure; AbilityAction abilityAction; if (_targetType is TargetType.Position) { // TODO: for now if ability needs multiple targets all will be identical var targets = Enumerable.Repeat(_position.value.position, _ability.value.castUsages).ToArray(); abilityAction = new AbilityAction(thisUnit, _ability.value, targets); } else if (_targetType is TargetType.Unit) { // TODO: for now if ability needs multiple targets all will be identical var targets = Enumerable.Repeat(_unit.value, _ability.value.castUsages).ToArray(); abilityAction = new AbilityAction(thisUnit, _ability.value, targets); } else { throw new ArgumentOutOfRangeException(); } // Check if that action can be done if (!abilityAction.CanDoItBoolean()) return NodeStatus.Failure; // Listen to end of abilisty casting animation memory.onCastAnimationEnd?.Off(); memory.onCastAnimationEnd = new SchemaUnitEventWrapper( thisUnit.unitEvents.onAnimation[AnimationType.Ability].onEnd, OnAbilityCastEnd, unitAgent, memory); memory.onCastAnimationEnd.On(); // Everything seems ok - order unit to do that action thisUnit.OrderStop(); thisUnit.Order(abilityAction); memory.isRunning = true; return NodeStatus.Running; } public override void OnNodeExit(object nodeMemory, SchemaAgent agent) { var memory = (UnitUseAbilityMemory)nodeMemory; memory.onCastAnimationEnd?.Off(); memory.isFinished = false; memory.isRunning = false; } public override void OnNodeAbort(object nodeMemory, SchemaAgent agent) { var memory = (UnitUseAbilityMemory)nodeMemory; memory.onCastAnimationEnd?.Off(); memory.isFinished = false; memory.isRunning = false; } private void OnAbilityCastEnd(AnimationGenericEvent abilityCastEvent, UnitUseAbilityMemory memory, UnitObjectSchemaAgent agent) { memory.isFinished = true; } } }