UnrealFightingFramework/Source/UnrealFightingFramework/State/FFState.cpp

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;
}