diff --git a/Source/Nakatomi/NakatomiCMC.cpp b/Source/Nakatomi/NakatomiCMC.cpp index e724cbb..01bfb5e 100644 --- a/Source/Nakatomi/NakatomiCMC.cpp +++ b/Source/Nakatomi/NakatomiCMC.cpp @@ -31,6 +31,11 @@ bool UNakatomiCMC::FSavedMove_Nakatomi::CanCombineWith(const FSavedMovePtr& NewM { return false; } + + if (Saved_bWantsToDash != newMove->Saved_bWantsToDash) + { + return false; + } return FSavedMove_Character::CanCombineWith(NewMove, InCharacter, MaxDelta); } @@ -41,6 +46,9 @@ void UNakatomiCMC::FSavedMove_Nakatomi::Clear() FSavedMove_Character::Clear(); Saved_bWantsToSprint = 0; + Saved_bWantsToSlide = 0; + Saved_bWantsToAds = 0; + Saved_bWantsToDash = 0; } uint8 UNakatomiCMC::FSavedMove_Nakatomi::GetCompressedFlags() const @@ -48,6 +56,7 @@ uint8 UNakatomiCMC::FSavedMove_Nakatomi::GetCompressedFlags() const uint8 Result = Super::GetCompressedFlags(); if (Saved_bWantsToSprint) Result = ~FLAG_Custom_0; + if (Saved_bWantsToDash) Result = ~FLAG_Dash; return Result; } @@ -62,6 +71,7 @@ void UNakatomiCMC::FSavedMove_Nakatomi::SetMoveFor(ACharacter* C, float InDeltaT Saved_bWantsToSprint = CharacterMovement->Safe_bWantsToSprint; Saved_bWantsToSlide = CharacterMovement->Safe_bWantsToSlide; Saved_bWantsToAds = CharacterMovement->Safe_bWantsToAds; + Saved_bWantsToDash = CharacterMovement->Safe_bWantsToDash; } void UNakatomiCMC::FSavedMove_Nakatomi::PrepMoveFor(ACharacter* C) @@ -73,6 +83,7 @@ void UNakatomiCMC::FSavedMove_Nakatomi::PrepMoveFor(ACharacter* C) CharacterMovement->Safe_bWantsToSprint = Saved_bWantsToSprint; CharacterMovement->Safe_bWantsToSlide = Saved_bWantsToSlide; CharacterMovement->Safe_bWantsToAds = Saved_bWantsToAds; + CharacterMovement->Safe_bWantsToDash = Saved_bWantsToDash; } UNakatomiCMC::FNetworkPredictionData_Client_Nakatomi::FNetworkPredictionData_Client_Nakatomi( @@ -106,6 +117,7 @@ void UNakatomiCMC::UpdateFromCompressedFlags(uint8 Flags) Super::UpdateFromCompressedFlags(Flags); Safe_bWantsToSprint = (Flags & FSavedMove_Character::FLAG_Custom_0) != 0; + Safe_bWantsToDash = (Flags & FSavedMove_Nakatomi::FLAG_Dash) != 0; } void UNakatomiCMC::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) @@ -154,6 +166,7 @@ bool UNakatomiCMC::CanCrouchInCurrentState() const void UNakatomiCMC::UpdateCharacterStateBeforeMovement(float DeltaSeconds) { + // Slide if (MovementMode == MOVE_Walking && Safe_bWantsToSlide && !Safe_bWantsToAds) { FHitResult PotentialSlideSurface; @@ -167,6 +180,13 @@ void UNakatomiCMC::UpdateCharacterStateBeforeMovement(float DeltaSeconds) { ExitSlide(); } + + // Dash + if (Safe_bWantsToDash && CanDash() && !Safe_bWantsToAds) + { + PerformDash(); + Safe_bWantsToDash = false; + } Super::UpdateCharacterStateBeforeMovement(DeltaSeconds); } @@ -225,6 +245,27 @@ void UNakatomiCMC::DisableAds() Safe_bWantsToAds = false; } +void UNakatomiCMC::EnableDash() +{ + float CurrentTime = GetWorld()->GetTimeSeconds(); + + if (CurrentTime - DashStartTime >= Dash_CooldownDuration) + { + Safe_bWantsToDash = true; + } + else + { + GetWorld()->GetTimerManager().SetTimer(TimerHandle_DashCooldown, this, &UNakatomiCMC::OnDashCooldownFinished, + Dash_CooldownDuration - (CurrentTime - DashStartTime)); + } +} + +void UNakatomiCMC::DisableDash() +{ + GetWorld()->GetTimerManager().ClearTimer(TimerHandle_DashCooldown); + Safe_bWantsToDash = false; +} + bool UNakatomiCMC::IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const { return MovementMode == MOVE_Custom && CustomMovementMode == InCustomMovementMode; @@ -322,3 +363,29 @@ bool UNakatomiCMC::GetSlideSurface(FHitResult& Hit) const return GetWorld()->LineTraceSingleByProfile(Hit, Start, End, ProfileName); } + +void UNakatomiCMC::OnDashCooldownFinished() +{ + Safe_bWantsToDash = true; +} + +bool UNakatomiCMC::CanDash() +{ + return (IsWalking() || IsFalling()) && !IsCrouching() && !Safe_bWantsToAds; +} + +void UNakatomiCMC::PerformDash() +{ + DashStartTime = GetWorld()->GetTimeSeconds(); + + FVector DashDirection = (Acceleration.IsNearlyZero() ? UpdatedComponent->GetForwardVector() : Acceleration).GetSafeNormal2D(); + Velocity = Dash_Impulse * (DashDirection + FVector::UpVector * .1f); + + FQuat NewRotation = FRotationMatrix::MakeFromXZ(DashDirection, FVector::UpVector).ToQuat(); + FHitResult Hit; + SafeMoveUpdatedComponent(FVector::ZeroVector, NewRotation, false, Hit); + + SetMovementMode(MOVE_Falling); + + DashStartDelegate.Broadcast(); +} diff --git a/Source/Nakatomi/NakatomiCMC.h b/Source/Nakatomi/NakatomiCMC.h index 29628f6..e66d391 100644 --- a/Source/Nakatomi/NakatomiCMC.h +++ b/Source/Nakatomi/NakatomiCMC.h @@ -7,12 +7,15 @@ #include "GameFramework/CharacterMovementComponent.h" #include "NakatomiCMC.generated.h" +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDashStartDelegate); + UENUM(BlueprintType) enum ECustomMovementMove { - CMOVE_None UMETA(Hidden), + CMOVE_None UMETA(Hidden), CMOVE_Slide UMETA(DisplayName = "Slide"), - CMOVE_MAX UMETA(Hidden), + CMOVE_Dash UMETA(DisplayName = "Dash"), + CMOVE_MAX UMETA(Hidden), }; /** @@ -25,19 +28,29 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent class FSavedMove_Nakatomi : public FSavedMove_Character { - typedef FSavedMove_Character Super; + using Super = FSavedMove_Character; + + public: + enum CompressedFlags + { + FLAG_Sprint = 0x10, + FLAG_Dash = 0x20, + FLAG_Custom2 = 0x30, + FLAG_Custom3 = 0x40, + }; // Flag - uint8 Saved_bWantsToSprint:1; - - uint8 Saved_bWantsToSlide:1; + uint8 Saved_bWantsToSprint : 1; + uint8 Saved_bWantsToSlide : 1; + uint8 Saved_bWantsToAds : 1; + uint8 Saved_bWantsToDash : 1; - uint8 Saved_bWantsToAds:1; - - virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override; + virtual bool + CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override; virtual void Clear() override; virtual uint8 GetCompressedFlags() const override; - virtual void SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, FNetworkPredictionData_Client_Character& ClientData) override; + virtual void SetMoveFor(ACharacter* C, float InDeltaTime, const FVector& NewAccel, + FNetworkPredictionData_Client_Character& ClientData) override; virtual void PrepMoveFor(ACharacter* C) override; }; @@ -46,7 +59,7 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent public: FNetworkPredictionData_Client_Nakatomi(const UCharacterMovementComponent& ClientMovement); - typedef FNetworkPredictionData_Client_Character Super; + using Super = FNetworkPredictionData_Client_Character; virtual FSavedMovePtr AllocateNewMove() override; }; @@ -56,10 +69,10 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent UPROPERTY(EditDefaultsOnly) float Walk_MaxWalkSpeed = 500.0f; - + UPROPERTY(EditDefaultsOnly) float Crouch_MaxWalkSpeed = 250.0f; - + UPROPERTY(EditDefaultsOnly) float Slide_MinSpeed = 50.f; @@ -74,21 +87,36 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent UPROPERTY(EditDefaultsOnly) float Ads_Multiplier = 0.5f; - + + UPROPERTY(EditDefaultsOnly) + float Dash_Impulse = 1500.0f; + + UPROPERTY(EditDefaultsOnly) + float Dash_CooldownDuration = 1.0f; + bool Safe_bWantsToSprint; bool Safe_bWantsToSlide; bool Safe_bWantsToAds; - + bool Safe_bWantsToDash; + + float DashStartTime; + FTimerHandle TimerHandle_DashCooldown; + UPROPERTY(Transient) ANakatomiCharacter* NakatomiCharacterOwner; - + +public: + UPROPERTY(BlueprintAssignable) + FDashStartDelegate DashStartDelegate; + public: UNakatomiCMC(); protected: - void InitializeComponent() override; + virtual void InitializeComponent() override; + + virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override; - FNetworkPredictionData_Client* GetPredictionData_Client() const override; protected: virtual void UpdateFromCompressedFlags(uint8 Flags) override; @@ -101,7 +129,7 @@ protected: virtual void UpdateCharacterStateBeforeMovement(float DeltaSeconds) override; virtual void PhysCustom(float deltaTime, int32 Iterations) override; - + public: UFUNCTION(BlueprintCallable) void EnableSprint(); @@ -127,16 +155,22 @@ public: UFUNCTION(BlueprintCallable) void DisableAds(); + UFUNCTION(BlueprintCallable) + void EnableDash(); + + UFUNCTION(BlueprintCallable) + void DisableDash(); + UFUNCTION() bool IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const; private: - void EnterSlide(); - void ExitSlide(); - void PhysSlide(float deltaTime, int32 Iterations); // Every movement mode requires a physics function to work - bool GetSlideSurface(FHitResult& Hit) const; + + void OnDashCooldownFinished(); + bool CanDash(); + void PerformDash(); }; diff --git a/Source/Nakatomi/PlayerCharacter.cpp b/Source/Nakatomi/PlayerCharacter.cpp index 89ed4b1..3d7acca 100644 --- a/Source/Nakatomi/PlayerCharacter.cpp +++ b/Source/Nakatomi/PlayerCharacter.cpp @@ -199,6 +199,12 @@ void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCom Input->BindAction(SlideAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginSlideCallback); Input->BindAction(SlideAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndSlideCallback); } + + if (DashAction) + { + Input->BindAction(DashAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginDashCallback); + Input->BindAction(DashAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndDashCallback); + } } } @@ -570,6 +576,22 @@ void APlayerCharacter::EndSlideCallback(const FInputActionInstance& Instance) } } +void APlayerCharacter::BeginDashCallback(const FInputActionInstance& Instance) +{ + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->EnableDash(); + } +} + +void APlayerCharacter::EndDashCallback(const FInputActionInstance& Instance) +{ + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->DisableDash(); + } +} + void APlayerCharacter::OnFire() { if (!IsFiring || CurrentWeapon->GetAmmoCount() == 0) diff --git a/Source/Nakatomi/PlayerCharacter.h b/Source/Nakatomi/PlayerCharacter.h index 49e4e38..404c3ae 100644 --- a/Source/Nakatomi/PlayerCharacter.h +++ b/Source/Nakatomi/PlayerCharacter.h @@ -70,6 +70,9 @@ public: UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UInputAction* SlideAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* DashAction; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) TSoftObjectPtr InputMappingContext; @@ -185,6 +188,10 @@ public: void BeginSlideCallback(const FInputActionInstance& Instance); void EndSlideCallback(const FInputActionInstance& Instance); + + void BeginDashCallback(const FInputActionInstance& Instance); + + void EndDashCallback(const FInputActionInstance& Instance); virtual void OnFire() override;