292 lines
7.1 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|