diff --git a/Content/BPML_StateMacros.uasset b/Content/BPML_StateMacros.uasset new file mode 100644 index 0000000..be47fa1 Binary files /dev/null and b/Content/BPML_StateMacros.uasset differ diff --git a/Source/UnrealFightingFramework/Input/FFInputBufferComponent.cpp b/Source/UnrealFightingFramework/Input/FFInputBufferComponent.cpp index 5bf3e0b..375bd0e 100644 --- a/Source/UnrealFightingFramework/Input/FFInputBufferComponent.cpp +++ b/Source/UnrealFightingFramework/Input/FFInputBufferComponent.cpp @@ -28,6 +28,7 @@ bool UFFInputBufferComponent::CheckInputSequence(const FFFInputSequence& InputSe int32 CurrDisable = InputBuffer[InpIdx].DisabledButtons; switch (RequiredButtonState) { + // TODO: should it be (PrevInput & RequiredButtons) == RequiredButtons or what we have now? case EFFButtonState::BTNS_Pressed: if(!(PrevInput & RequiredButtons | PrevDisable) && CurrInput & RequiredButtons & ~CurrDisable) diff --git a/Source/UnrealFightingFramework/State/FFState.cpp b/Source/UnrealFightingFramework/State/FFState.cpp index 96a3a32..b1fbb4d 100644 --- a/Source/UnrealFightingFramework/State/FFState.cpp +++ b/Source/UnrealFightingFramework/State/FFState.cpp @@ -10,6 +10,43 @@ // UE includes #include "Components/SkeletalMeshComponent.h" +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 that all state entry conditions are met if there are any + * + * Check to see if the owner implements the StateOwnerInterface + * Check input conditions if there are any + * + * If all state entry conditions are good and at least one input condition is good then we can transition + * so return true otherwise return false + */ + IFFStateAvatarInterface* SAI = Cast(InStateContext.Avatar); + if(SAI) + { + if(!(SAI->CheckStance(StanceRequired) && SAI->CheckStateEnabled(StateType) && SAI->CheckStateEntryConditions(EntryConditions))) + { + return false; + } + } + + IFFStateOwnerInterface* SOI = Cast(InStateContext.Owner); + if(SOI) + { + if(!SOI->CheckInputSequences(InputSequences)) + { + return false; + } + } + + return OnCanTransition(InStateContext); +} + + void UFFState::Enter(const FFFStateContext& InStateContext) { if(InStateContext.Avatar) @@ -54,43 +91,12 @@ void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext) void UFFState::Finish(const FFFStateContext& InStateContext) { - InStateContext.Parent->SwitchToEntryState(); + InStateContext.Parent->GoToEntryState(); } -bool UFFState::CanTransition_Implementation(const FFFStateContext& InStateContext) +bool UFFState::OnCanTransition_Implementation(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 that all state entry conditions are met if there are any - * - * Check to see if the owner implements the StateOwnerInterface - * Check input conditions if there are any - * - * If all state entry conditions are good and at least one input condition is good then we can transition - * so return true otherwise return false - */ - IFFStateAvatarInterface* SAI = Cast(InStateContext.Avatar); - if(SAI) - { - if(!(SAI->CheckStance(StanceRequired) && SAI->CheckStateEnabled(StateType) && SAI->CheckStateEntryConditions(EntryConditions))) - { - return false; - } - } - - IFFStateOwnerInterface* SOI = Cast(InStateContext.Owner); - if(SOI) - { - if(!SOI->CheckInputSequences(InputSequences)) - { - return false; - } - } - return true; } diff --git a/Source/UnrealFightingFramework/State/FFState.h b/Source/UnrealFightingFramework/State/FFState.h index 53edfea..bd73626 100644 --- a/Source/UnrealFightingFramework/State/FFState.h +++ b/Source/UnrealFightingFramework/State/FFState.h @@ -109,8 +109,7 @@ public: * all state entry conditions were met AND * at least one input sequence is present in the Owner's input buffer */ - UFUNCTION(BlueprintNativeEvent, Category="UFF|State") - bool CanTransition(const FFFStateContext& InStateContext); + virtual bool CanTransition(const FFFStateContext& InStateContext); /** * Called whenever this state is transitioned into. @@ -151,6 +150,12 @@ public: UFUNCTION(BlueprintCallable) virtual void Finish(const FFFStateContext& InStateContext); + /** + * Blueprint hook for overriding the CanTransition logic + */ + UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") + bool OnCanTransition(const FFFStateContext& InStateContext); + /** * Blueprint hook for whenever this state is transitioned into */ diff --git a/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp b/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp index ab225f8..4af0eb3 100644 --- a/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp +++ b/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp @@ -85,7 +85,16 @@ void UFFStateMachineComponent::RemoveState(FName StateToRemove) UE_LOG(LogTemp, Error, TEXT("UFFStateMachineComponent::RemoveState is not yet implemented")); } -void UFFStateMachineComponent::SwitchStates(UFFState* NewState) +void UFFStateMachineComponent::GoToState(FName NewStateName) +{ + UFFState* NewState = FindStateWithName(NewStateName); + if(NewState) + { + GoToState(NewState); + } +} + +void UFFStateMachineComponent::GoToState(UFFState* NewState) { check(CurrentState); check(NewState); @@ -97,12 +106,12 @@ void UFFStateMachineComponent::SwitchStates(UFFState* NewState) CurrentSubStateLabel = NAME_None; } -void UFFStateMachineComponent::SwitchToEntryState() +void UFFStateMachineComponent::GoToEntryState() { // can't have an entry state if there are no states check(States.Num() > 0); - SwitchStates(States[0]); + GoToState(States[0]); } @@ -170,7 +179,7 @@ void UFFStateMachineComponent::FixedTick(float OneFrame) if(StateToTransitionTo && (CurrentState->Name != StateToTransitionTo->Name || StateToTransitionTo->bCanTransitionToSelf)) { - SwitchStates(StateToTransitionTo); + GoToState(StateToTransitionTo); } else { diff --git a/Source/UnrealFightingFramework/State/FFStateMachineComponent.h b/Source/UnrealFightingFramework/State/FFStateMachineComponent.h index 0c8f6fa..82066d9 100644 --- a/Source/UnrealFightingFramework/State/FFStateMachineComponent.h +++ b/Source/UnrealFightingFramework/State/FFStateMachineComponent.h @@ -62,19 +62,26 @@ public: */ void RemoveState(FName StateToRemove); + /** + * Transitions from CurrentState to the new state with the name passed to this function + * + * Triggers the Exit callback on the CurrentState and the Enter callback on the new state + */ + void GoToState(FName NewStateName); + /** * Transitions from CurrentState to the new state passed to this function * * Triggers the Exit callback on the CurrentState and the Enter callback on the new state */ - void SwitchStates(UFFState* NewState); + void GoToState(UFFState* NewState); /** * Transitions from CurrentState to the default entry state */ - void SwitchToEntryState(); + void GoToEntryState(); - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure) FORCEINLINE int64 GetTicksInState() const; /**