using System; using System.Collections.Generic; using Animancer; using BITFALL.Combat; using BITFALL.Player.Movement; using BITKit; using BITKit.AI.States; using BITKit.Entities; using BITKit.Entities.Melee; using BITKit.StateMachine; using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine; using Random = UnityEngine.Random; namespace BITFALL.AI.Zombie.States { [Serializable] public abstract class AIZombieState : IState { [Inject] protected AIZombie self; protected AnimancerComponent animancerComponent => self.animancerComponent; public virtual bool Enabled { get; set; } public virtual void Initialize() { } public virtual void OnStateEntry(IState old) { } public virtual void OnStateUpdate(float deltaTime) { } public virtual void OnStateExit(IState old, IState newState) { } } [Serializable] public abstract class AIZombieActionState : AIZombieState { [SerializeField] private AnimationClip[] clips; public override async void OnStateEntry(IState old) { base.OnStateEntry(old); var state = animancerComponent.Layers[1].Play(clips.Random()); self.entityOverride.AddOverride(this); await state; if (Enabled is false || state.IsValid is false) return; state.Stop(); self.TransitionState(); } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); self.entityOverride.RemoveOverride(this); } } [Serializable] public sealed class Idle : AIZombieState { [SerializeField] private float attackDistance; public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); switch (self.service.CurrentState) { case AI_Combat when self.service.CombatTarget is not null: var distance = Vector3.Distance(self.service.CombatTarget.As().transform.position, self.transform.position); if (distance <= attackDistance) { self.service.CombatTarget.TryGetComponent(out var movement); switch (Random.Range(0, 4), movement) { case (_, { Size: { y: < 1f } }): case (_, { CurrentState: IPlayerKnockdownState }): self.TransitionState(); return; default: self.TransitionState(); return; } } break; } } } [Serializable] public sealed class Attack : AIZombieActionState { [Inject] private IMeleeCombat _combat; public override void OnStateEntry(IState old) { base.OnStateEntry(old); var position = self.transform.position+Vector3.up; if (self.service.CombatTarget is MonoBehaviour monoBehaviour && monoBehaviour.TryGetComponent(out var collider)) { _combat.SetConfig(new MeleeConfig { Position = position, Rotation = Quaternion.LookRotation(collider.bounds.center - position) }); } } } [Serializable] public sealed class Catch : AIZombieActionState { private IEntityMovement _targetMovement; public override async void OnStateEntry(IState old) { switch (self.service.CombatTarget) { case { } x when x.TryGetComponent(out _targetMovement): _targetMovement.ExecuteCommand(new PlayerDisableMovementCommand() { Disable = true, Duration = 5, LockFile = this }); break; } base.OnStateEntry(old); } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); _targetMovement.ExecuteCommand(new PlayerDisableMovementCommand() { Disable = false, LockFile = this }); } } [Serializable] public sealed class Damaged : AIZombieActionState { public override void Initialize() { base.Initialize(); self.health.OnDamageRelease += OnDamageRelease; } private void OnDamageRelease(DamageMessage obj) { switch (self.health.IsAlive) { case true when self.CurrentState is not Damaged: self.TransitionState(); break; case false when self.CurrentState is not Death: self.TransitionState(); break; } } } [Serializable] public sealed class Death : AIZombieActionState { public override void OnStateEntry(IState old) { base.OnStateEntry(old); self.health.OnDamageVoid += OnDamageVoid; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); self.health.OnDamageVoid -= OnDamageVoid; } private async void OnDamageVoid(DamageMessage obj) { var velocity = self.animancerComponent.Animator.velocity; animancerComponent.Stop(); await UniTask.Delay(50); if (self.destroyCancellationToken.IsCancellationRequested) return; foreach (var x in self.GetComponentsInChildren()) { if(x.isKinematic) { //BIT4Log.Warning($"{x.gameObject.name} is kinematic"); } else { //BIT4Log.Log($"Setting {x.gameObject.name} velocity to {velocity}"); x.velocity = velocity; } } } } }