200 lines
5.5 KiB
C++
200 lines
5.5 KiB
C++
// Unreal Fighting Framework by Kevin Poretti
|
|
|
|
#include "FFState.h"
|
|
|
|
// FF includes
|
|
#include "FFStateMachineComponent.h"
|
|
#include "IFFStateAvatarInterface.h"
|
|
#include "IFFStateOwnerInterface.h"
|
|
|
|
// UE includes
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
|
|
void UFFState::Init(const FFFStateContext& InStateContext)
|
|
{
|
|
OnInit(InStateContext);
|
|
}
|
|
|
|
bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
|
{
|
|
/**
|
|
* Check to see if avatar implements StateAvatarInterface
|
|
* if so then
|
|
* Check if the avatar is in the correct stance to perform this action
|
|
* Check if the state is enabled
|
|
*
|
|
* Check to see if the owner implements the StateOwnerInterface
|
|
* Check input conditions if there are any
|
|
*
|
|
* If at least one input condition is good then we can transition
|
|
* so return true otherwise return false
|
|
*/
|
|
IFFStateAvatarInterface* SAI = Cast<IFFStateAvatarInterface>(InStateContext.Avatar);
|
|
if(SAI)
|
|
{
|
|
if(!(SAI->CheckStance(StanceRequired) && SAI->CheckStateEnabled(StateType)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("CanTransition :: Avatar of FFFStateContext does not implement IFFStateAvatarInterface"));
|
|
return false;
|
|
}
|
|
|
|
IFFStateOwnerInterface* SOI = Cast<IFFStateOwnerInterface>(InStateContext.Owner);
|
|
if(SOI)
|
|
{
|
|
if(!SOI->CheckInputSequences(InputSequences))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("CanTransition :: Owner of FFFStateContext does not implement IFFStateOwnerInterface"));
|
|
return false;
|
|
}
|
|
|
|
return OnCanTransition(InStateContext);
|
|
}
|
|
|
|
|
|
void UFFState::Enter(const FFFStateContext& InStateContext)
|
|
{
|
|
if(InStateContext.Avatar)
|
|
{
|
|
USkeletalMeshComponent* SMC = InStateContext.Avatar->GetComponentByClass<USkeletalMeshComponent>();
|
|
if(SMC && MontageToPlay)
|
|
{
|
|
SMC->GetAnimInstance()->Montage_Play(MontageToPlay);
|
|
}
|
|
}
|
|
|
|
OnEnter(InStateContext);
|
|
}
|
|
|
|
|
|
void UFFState::Exit(const FFFStateContext& InStateContext)
|
|
{
|
|
if(InStateContext.Avatar)
|
|
{
|
|
USkeletalMeshComponent* SMC = InStateContext.Avatar->GetComponentByClass<USkeletalMeshComponent>();
|
|
if(SMC && MontageToPlay)
|
|
{
|
|
FAlphaBlendArgs BlendOutArgs = MontageToPlay->GetBlendOutArgs();
|
|
SMC->GetAnimInstance()->Montage_Stop(BlendOutArgs.BlendTime, MontageToPlay);
|
|
}
|
|
}
|
|
|
|
OnExit(InStateContext);
|
|
}
|
|
|
|
|
|
void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
|
{
|
|
IFFStateOwnerInterface* SOI = Cast<IFFStateOwnerInterface>(InStateContext.Owner);
|
|
// TODO: maybe a check/asset is better because I can't think of a reason that not having the Owner implement
|
|
// IFFStateOwnerInterface is valid. Although there could be objects that have state that don't necessarily directly
|
|
// respond to input and don't have an Owner/controller responsible for them. Or they do have an owner but are associated
|
|
// with another Avatar like a character who is owned by a player/player controller.
|
|
if(!SOI)
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Owner of FFFStateContext does not implement IFFStateOwnerInterface"));
|
|
return;
|
|
}
|
|
|
|
for(FFFInputEventHandler InputHandler : InputHandlers)
|
|
{
|
|
if(SOI->CheckInputSequence(InputHandler.RequiredSequence))
|
|
{
|
|
if(InputHandler.Delegate.ExecuteIfBound(InStateContext))
|
|
{
|
|
UE_LOG(LogTemp, Error,
|
|
TEXT("Trying to execute an input handler delegate on %s but it is not bound"),
|
|
*Name.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
OnUpdate(OneFrame, InStateContext);
|
|
|
|
if(bStateHasDuration && InStateContext.Parent->GetTicksInState() >= StateDuration)
|
|
{
|
|
Finish(InStateContext);
|
|
}
|
|
}
|
|
|
|
|
|
void UFFState::Landed(const FHitResult& Hit, const FFFStateContext& InStateContext)
|
|
{
|
|
OnLanded(Hit, InStateContext);
|
|
|
|
// TODO: might want to also finish the state here by default and have the subclasses overwrite
|
|
// that behavior in the few cases we don't want to finish the state when we land
|
|
}
|
|
|
|
|
|
void UFFState::MovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode,
|
|
EMovementMode NewMovementMode, uint8 NewCustomMode, const FFFStateContext& InStateContext)
|
|
{
|
|
OnMovementModeChanged(PrevMovementMode, PreviousCustomMode,
|
|
NewMovementMode, NewCustomMode, InStateContext);
|
|
|
|
if(NewMovementMode != ReqMovementMode || ((ReqMovementMode == MOVE_Custom) && NewCustomMode != RequiredCustomMode))
|
|
{
|
|
Finish(InStateContext);
|
|
}
|
|
}
|
|
|
|
|
|
void UFFState::Hit(const FFFStateContext& InStateContext)
|
|
{
|
|
OnHit(InStateContext);
|
|
}
|
|
|
|
|
|
void UFFState::Block(const FFFStateContext& InStateContext)
|
|
{
|
|
OnBlock(InStateContext);
|
|
}
|
|
|
|
|
|
void UFFState::Finish(const FFFStateContext& InStateContext)
|
|
{
|
|
InStateContext.Parent->GoToEntryState();
|
|
}
|
|
|
|
|
|
void UFFState::RegisterInputHandler(const FFFInputSequence& InRequiredSequence, FFFInputEventDelegate InDelegate)
|
|
{
|
|
FFFInputEventHandler TempHandler;
|
|
TempHandler.RequiredSequence = InRequiredSequence;
|
|
TempHandler.Delegate = InDelegate;
|
|
InputHandlers.Add(TempHandler);
|
|
}
|
|
|
|
|
|
void UFFState::OnInit_Implementation(const FFFStateContext& InStateContext)
|
|
{
|
|
}
|
|
|
|
|
|
bool UFFState::OnCanTransition_Implementation(const FFFStateContext& InStateContext)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
UWorld* UFFState::GetWorld() const
|
|
{
|
|
UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter());
|
|
if(SMC)
|
|
{
|
|
return SMC->GetWorld();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|