BITFALL/Assets/Artists/Scripts/Entities/AI/AIZombieStates.cs

201 lines
4.9 KiB
C#

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<Idle>();
}
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<MonoBehaviour>().transform.position,
self.transform.position);
if (distance <= attackDistance)
{
self.service.CombatTarget.TryGetComponent<IEntityMovement>(out var movement);
switch (Random.Range(0, 4), movement)
{
case (_, { Size: { y: < 1f } }):
case (_, { CurrentState: IPlayerKnockdownState }):
self.TransitionState<Attack>();
return;
default:
self.TransitionState<Catch>();
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<CapsuleCollider>(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<IEntityMovement>(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<Damaged>();
break;
case false when self.CurrentState is not Death:
self.TransitionState<Death>();
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<Rigidbody>())
{
if(x.isKinematic)
{
//BIT4Log.Warning<AIZombieState>($"{x.gameObject.name} is kinematic");
}
else
{
//BIT4Log.Log<AIZombieState>($"Setting {x.gameObject.name} velocity to {velocity}");
x.velocity = velocity;
}
}
}
}
}