BITFALL/Assets/Artists/Scripts/Equip/MeleeControllerStates.cs

292 lines
7.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Animancer;
using BITFALL.Combat;
using BITFALL.Player.Movement;
using BITKit;
using BITKit.Entities;
using BITKit.StateMachine;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Interactions;
namespace BITFALL.Entities.Equipment.Melee
{
[Serializable]
public sealed class Empty:PlayerMeleeControllerState{}
[Serializable]
public sealed class Draw:PlayerMeleeControllerState
{
[SerializeField] private AnimationClip drawClip;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
var state = my.animancerComponent.Play(drawClip);
state.Events.OnEnd = () =>
{
state.Events.OnEnd = null;
my.TransitionState<Idle>();
};
}
}
[Serializable]
public sealed class Idle:PlayerMeleeControllerState
{
[SerializeField] private AnimationClip idleClip;
[SerializeField] private AnimationClip runClip;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
my.inputActionGroup.RegisterCallback(my.attackAction, OnAttack);
my.inputActionGroup.EnsureCreated(my.blockAction);
if (my.inputActionGroup.GetAction(my.attackAction).IsPressed())
{
my.TransitionState<Charging>();
return;
}
my.animancerComponent.Play(idleClip,0.2f);
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
my.inputActionGroup.UnRegisterCallback(my.attackAction, OnAttack);
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
switch (my.inputActionGroup.GetAction(my.blockAction))
{
case var x when x.IsPressed():
my.TransitionState<Blocking>();
return;
}
if (my._movement.CurrentState is IPlayerRunState)
{
my.animancerComponent.Play(runClip,0.2f);
}
else
{
my.animancerComponent.Play(idleClip,0.2f);
}
}
private void OnAttack(InputAction.CallbackContext obj)
{
if (BITAppForUnity.AllowCursor) return;
switch (obj)
{
case {interaction: PressInteraction,performed:true}:
my.TransitionState<Charging>();
return;
}
}
}
[Serializable]
public sealed class Attack:PlayerMeleeControllerState
{
[SerializeField] private AnimationClip[] attackClip;
private AnimancerState state;
private bool keepAttacking;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
AttackInternal();
my.inputActionGroup.RegisterCallback(my.attackAction, OnAttack);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this,true));
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
my.inputActionGroup.UnRegisterCallback(my.attackAction, OnAttack);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this,false));
keepAttacking = false;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if (state.NormalizedTime > 0.5f && keepAttacking)
AttackInternal();
}
private void OnEnd()
{
state.Events.OnEnd = null;
my.TransitionState<Idle>();
}
private void OnAttack(InputAction.CallbackContext obj)
{
if (BITAppForUnity.AllowCursor) return;
switch (obj)
{
case { interaction: HoldInteraction, started: true }:
if (state is not null)
state.Events.OnEnd = null;
my.TransitionState<Charging>();
return;
case { interaction: TapInteraction, performed: true } when state.NormalizedTime > 0.5f:
switch (state.NormalizedTime)
{
case > 0.5f:
AttackInternal();
return;
case > 0.16f:
keepAttacking = true;
return;
}
return;
}
}
private void AttackInternal()
{
keepAttacking = false;
if (state is not null)
{
state.Events.OnEnd = null;
state.Stop();
}
state?.Stop();
state = my.animancerComponent.Play(attackClip.Random());
state.Events.OnEnd = OnEnd;
state.Speed = my._playerMovement.Stamina > 0 ? 1 : 0.5f;
}
}
[Serializable]
public sealed class Charging : PlayerMeleeControllerState
{
[SerializeField] private AnimationClip chargingClip;
private AnimancerState state;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this, true));
state = my.animancerComponent.Play(chargingClip,0.2f);
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this, false));
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if (my.inputActionGroup.GetAction(my.attackAction).IsPressed()) return;
if (state.NormalizedTime >= 0.9f)
{
my.TransitionState<HeavyAttack>();
}
else
{
my.TransitionState<Attack>();
}
}
}
[Serializable]
public sealed class HeavyAttack:PlayerMeleeControllerState
{
[SerializeField] private AnimationClip heavyAttack;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
var state = my.animancerComponent.Play(heavyAttack);
state.Events.OnEnd = () =>
{
state.Events.OnEnd = null;
my.TransitionState<Idle>();
};
state.Speed = my._playerMovement.Stamina > 0 ? 1 : 0.5f;
}
}
[Serializable]
public sealed class Blocking : PlayerMeleeControllerState
{
[SerializeField] private AnimationClip blockClip;
[SerializeField] private AnimationClip blockedClip;
[SerializeField] private AnimationClip breakBlockClip;
public bool AllowBlockStun => _interval.AllowUpdateWithoutReset is false;
private readonly IntervalUpdate _interval = new(0.32f);
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
my.animancerComponent.Layers[1].IsAdditive = true;
_interval.Reset();
my.animancerComponent.Play(blockClip,0.2f);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this,true));
my._health.OnDamageFactory += OnDamageFactory;
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
my._movement.ExecuteCommand(new PlayerPauseRunCommand(this,false));
my._health.OnDamageFactory += OnDamageFactory;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if(my.inputActionGroup.GetAction(my.blockAction).IsPressed() is false)
my.TransitionState<Idle>();
}
private int OnDamageFactory(DamageMessage arg1, int arg2)
{
if (arg1.DamageType is not MeleeDamageMessage)
return arg2;
my.animancerComponent.Layers[1].Stop();
if (my._playerMovement.Stamina < my.melee.BlockStaminaCost)
{
my.animancerComponent.Layers[1].Play(breakBlockClip);
my._playerMovement.Stamina = 0;
return arg1.Damage/2;
}
my._playerMovement.Stamina -= my.melee.BlockStaminaCost;
my.animancerComponent.Layers[1].Play(breakBlockClip);
if (!AllowBlockStun) return 0;
if (arg1.Initiator.TryGetComponent<IMeleeCombat>(out var combat))
{
combat.HitStun();
}
return 0;
}
}
}