diff --git a/Source/UnrealFightingFramework/State/FFState.cpp b/Source/UnrealFightingFramework/State/FFState.cpp index 3609899..3979892 100644 --- a/Source/UnrealFightingFramework/State/FFState.cpp +++ b/Source/UnrealFightingFramework/State/FFState.cpp @@ -57,7 +57,7 @@ bool UFFState::CanTransition(const FFFStateContext& InStateContext) return false; } - return OnCanTransition(InStateContext); + return OnCanTransition(InStateContext) && !bIsFollowupState; } @@ -163,26 +163,24 @@ void UFFState::Finish(const FFFStateContext& InStateContext, EFFStateFinishReaso // the appropriate flags are set. I think having this state finish reason is good but I may want // to rethink the way we handle logic for ending a state and which class is in charge of handling // what - if(FollowupState != NAME_None) + + if(GetFollowupState() != NAME_None) { - InStateContext.Parent->GoToState(FollowupState, StateFinishReason); + InStateContext.Parent->GoToState(GetFollowupState(), StateFinishReason); } else { InStateContext.Parent->GoToEntryState(StateFinishReason); } - } -void UFFState::RegisterInputHandler(const FFFInputSequence& InRequiredSequence, FFFInputEventDelegate InDelegate) +FName UFFState::GetFollowupState_Implementation() { - FFFInputEventHandler TempHandler; - TempHandler.RequiredSequence = InRequiredSequence; - TempHandler.Delegate = InDelegate; - InputHandlers.Add(TempHandler); + return FollowupState; } + void UFFState::PlayMontage(const FFFStateContext& InStateContext) { // TODO: think of a better way to handle optionally playing montages other than the one set as MontageToPlay diff --git a/Source/UnrealFightingFramework/State/FFState.h b/Source/UnrealFightingFramework/State/FFState.h index 62bc2e3..bacea5a 100644 --- a/Source/UnrealFightingFramework/State/FFState.h +++ b/Source/UnrealFightingFramework/State/FFState.h @@ -126,6 +126,17 @@ public: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties") bool bCanTransitionToSelf = false; + /** + * Specifies whether a state is a followup state or not. + * + * Followup states are states that should only be transitioned into from another specific state + * but otherwise have no other specific conditions that need to be met to transition. + * Setting this flag to true when there are no other conditions prevents this state from + * constantly being transitioned into by the state machine. + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties") + bool bIsFollowupState = false; + /** * State to transition to when this state is finished. If left blank then the entry state * of the state machine will be transitioned into. @@ -197,10 +208,12 @@ public: EMovementMode NewMovementMode, uint8 NewCustomMode, const FFFStateContext& InStateContext); // TODO: document + // TODO: call this callback when the avatar is hit // TODO: pass in hitdata struct as well virtual void Hit(const FFFStateContext& InStateContext); // TODO: document + // TODO: call this callback when the avatar blocks a hit // TODO: pass in hitdata struct as well virtual void Block(const FFFStateContext& InStateContext); @@ -219,12 +232,7 @@ public: */ UFUNCTION(BlueprintCallable) virtual void Finish(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason); - - // TODO: document - UFUNCTION(BlueprintCallable) - virtual void RegisterInputHandler( - const FFFInputSequence& InRequiredSequence, FFFInputEventDelegate InDelegate); - + // TODO: document UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") void OnInit(const FFFStateContext& InStateContext); @@ -255,16 +263,30 @@ public: UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events") void OnUpdate(float OneFrame, const FFFStateContext& InStateContext); + /** + * Returns the next state to transition into when this state finishes. + * + * This is called during the Finish function, either when the state duration is reached or the + * state is manually finished due to some other logic. + * + * By default this returns the name of the state specified by the FollowupState property or + * NAME_None if the FollowupState is not specified. + */ + UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") + FName GetFollowupState(); + + // TODO: Don't like this at all and needs to be refactored or redesigned UFUNCTION(BlueprintCallable, Category="UFF|State|Animations") void PlayMontage(const FFFStateContext& InStateContext); + // TODO: Don't like this at all and needs to be refactored or redesigned /** * Blueprint hook for overriding the logic for when a anim montage plays at the start of a state * @param InStateContext */ UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") void OnPlayMontage(const FFFStateContext& InStateContext); - + // TODO: document UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events") void OnLanded(const FHitResult& Hit, const FFFStateContext& InStateContext); diff --git a/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp b/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp index e0ddcef..6011e39 100644 --- a/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp +++ b/Source/UnrealFightingFramework/State/FFStateMachineComponent.cpp @@ -224,7 +224,7 @@ UFFState* UFFStateMachineComponent::FindStateWithName(FName StateName) } } - UE_LOG(LogTemp, Warning, + UE_LOG(LogTemp, Error, TEXT("Could not find state in state machine with name %s"), *StateName.ToString()); return nullptr; diff --git a/Source/UnrealFightingFramework/State/FFStateMachineComponent.h b/Source/UnrealFightingFramework/State/FFStateMachineComponent.h index f122ec7..e365498 100644 --- a/Source/UnrealFightingFramework/State/FFStateMachineComponent.h +++ b/Source/UnrealFightingFramework/State/FFStateMachineComponent.h @@ -184,7 +184,7 @@ protected: UPROPERTY() UFFState* CurrentState; - /** Current SubState label or NAME_None if there is no SubState label set for the current state*/ + /** Current SubState label or NAME_None if there is no SubState label set for the current state */ FName CurrentSubStateLabel; // TODO: should be a TMap