diff --git a/Content/Characters/Common/Montages/Rifle/rifle_reload_crouch_Montage.uasset b/Content/Characters/Common/Montages/Rifle/rifle_reload_crouch_Montage.uasset new file mode 100644 index 000000000..f701d3e82 Binary files /dev/null and b/Content/Characters/Common/Montages/Rifle/rifle_reload_crouch_Montage.uasset differ diff --git a/Content/Characters/Common/Montages/Shotgun/shotgun_reload_start_Montage.uasset b/Content/Characters/Common/Montages/Shotgun/shotgun_reload_start_Montage.uasset index c5a71ecc3..a364c7961 100644 Binary files a/Content/Characters/Common/Montages/Shotgun/shotgun_reload_start_Montage.uasset and b/Content/Characters/Common/Montages/Shotgun/shotgun_reload_start_Montage.uasset differ diff --git a/Content/Characters/Common/Sequences/Grenade/grenade_near_fire.uasset b/Content/Characters/Common/Sequences/Grenade/grenade_near_fire.uasset deleted file mode 100644 index e7ec191ad..000000000 Binary files a/Content/Characters/Common/Sequences/Grenade/grenade_near_fire.uasset and /dev/null differ diff --git a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_end_crouch.uasset b/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_end_crouch.uasset deleted file mode 100644 index c3b9ee102..000000000 Binary files a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_end_crouch.uasset and /dev/null differ diff --git a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_loop_crouch.uasset b/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_loop_crouch.uasset deleted file mode 100644 index 024300800..000000000 Binary files a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_loop_crouch.uasset and /dev/null differ diff --git a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_start_crouch.uasset b/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_start_crouch.uasset deleted file mode 100644 index aaea886b7..000000000 Binary files a/Content/Characters/Common/Sequences/Shotgun/shotgun_reload_start_crouch.uasset and /dev/null differ diff --git a/Content/Characters/Common/Sequences/Smg/smg_reload_crouch.uasset b/Content/Characters/Common/Sequences/Smg/smg_reload_crouch.uasset deleted file mode 100644 index 4b9526fde..000000000 Binary files a/Content/Characters/Common/Sequences/Smg/smg_reload_crouch.uasset and /dev/null differ diff --git a/Content/Characters/Common/Sequences/Sniper/sniper_reload_crouch.uasset b/Content/Characters/Common/Sequences/Sniper/sniper_reload_crouch.uasset deleted file mode 100644 index 0b2dbc486..000000000 Binary files a/Content/Characters/Common/Sequences/Sniper/sniper_reload_crouch.uasset and /dev/null differ diff --git a/Content/Weapons/WeaponTableDefinitions.uasset b/Content/Weapons/WeaponTableDefinitions.uasset index a0292b067..26dbec70c 100644 Binary files a/Content/Weapons/WeaponTableDefinitions.uasset and b/Content/Weapons/WeaponTableDefinitions.uasset differ diff --git a/Source/Cloud9/Character/Components/Cloud9AnimationComponent.cpp b/Source/Cloud9/Character/Components/Cloud9AnimationComponent.cpp index 436210ee8..8e3508dcb 100644 --- a/Source/Cloud9/Character/Components/Cloud9AnimationComponent.cpp +++ b/Source/Cloud9/Character/Components/Cloud9AnimationComponent.cpp @@ -20,27 +20,96 @@ UAnimInstance* UCloud9AnimationComponent::GetAnimInstance() const return Mesh->GetAnimInstance(); } -bool UCloud9AnimationComponent::PlayMontage(UAnimMontage* Montage, float StartTime, float Rate) const +bool UCloud9AnimationComponent::PlayMontage( + UAnimMontage* NewBasePoseMontage, + UAnimMontage* NewOtherPoseMontage, + float StartTime, + float Rate) { - OBJECT_RETURN_IF_FAIL(IsValid(Montage), false, Error, "Montage is invalid"); + OBJECT_RETURN_IF_FAIL(IsValid(NewBasePoseMontage), false, Error, "Montage is invalid"); let AnimInstance = GetAnimInstance(); OBJECT_RETURN_IF_FAIL( IsValid(AnimInstance), false, Error, "AnimInstance is invalid for montage '%s'", - *Montage->GetName()); + *NewBasePoseMontage->GetName()); OBJECT_RETURN_IF_FAIL( - AnimInstance->Montage_Play(Montage, Rate, EMontagePlayReturnType::MontageLength, StartTime), false, - Error, "Can't play montage '%s'", *Montage->GetName() + AnimInstance->Montage_Play(NewBasePoseMontage, Rate, EMontagePlayReturnType::MontageLength, StartTime), false, + Error, "Can't play montage '%s'", *NewBasePoseMontage->GetName() ); + let MontageInstance = AnimInstance->GetActiveInstanceForMontage(NewBasePoseMontage); + let InstanceID = MontageInstance->GetInstanceID(); + MontageInstance->OnMontageEnded.BindLambda( + [this, InstanceID](UAnimMontage* Montage, bool IsInterrupted) + { + OBJECT_VERBOSE( + "Montage stopped=%s InstanceID=%d Interrupted=%d", + *Montage->GetName(), InstanceID, IsInterrupted); + Montages.Remove(InstanceID); + } + ); + Montages.Add(InstanceID, NewOtherPoseMontage); + OBJECT_VERBOSE("Montage started=%s InstanceID=%d", *NewBasePoseMontage->GetName(), InstanceID); + return true; } +void UCloud9AnimationComponent::StopAllMontages(float BlendOut) const +{ + let AnimInstance = GetAnimInstance(); + OBJECT_VOID_IF_FAIL(IsValid(AnimInstance), Error, "AnimInstance is invalid"); + AnimInstance->StopAllMontages(BlendOut); +} + bool UCloud9AnimationComponent::IsAnyMontagePlaying() const { let AnimInstance = GetAnimInstance(); OBJECT_RETURN_IF_FAIL(IsValid(AnimInstance), false, Error, "AnimInstance is invalid"); return AnimInstance->IsAnyMontagePlaying(); } + +void UCloud9AnimationComponent::StopMontage(const UAnimMontage* Montage) const +{ + let AnimInstance = GetAnimInstance(); + OBJECT_VOID_IF_FAIL(IsValid(AnimInstance), Error, "AnimInstance is invalid"); + + if (let MontageInstance = AnimInstance->GetActiveInstanceForMontage(Montage)) + { + MontageInstance->Stop(Montage->BlendOut); + } +} + +UAnimMontage* UCloud9AnimationComponent::StopMontage(int32 InstanceID) const +{ + let AnimInstance = GetAnimInstance(); + OBJECT_RETURN_IF_FAIL(IsValid(AnimInstance), nullptr, Error, "AnimInstance is invalid"); + + if (let MontageInstance = AnimInstance->GetMontageInstanceForID(InstanceID)) + { + if (let Montage = MontageInstance->Montage; IsValid(Montage)) + { + MontageInstance->Stop(Montage->BlendOut); + return Montage; + } + } + + return nullptr; +} + +void UCloud9AnimationComponent::PoseChanged() +{ + let AnimInstance = GetAnimInstance(); + OBJECT_VOID_IF_FAIL(IsValid(AnimInstance), Error, "AnimInstance is invalid"); + + // Prevent modification during iteration + for (let Copy = Montages; let [InstanceID, OtherMontage] : Copy) + { + let MontageInstance = AnimInstance->GetMontageInstanceForID(InstanceID); + let Position = MontageInstance->GetPosition(); + let BaseMontage = StopMontage(InstanceID); + OBJECT_VERBOSE("Pose changed ActiveMontage=%s InstanceID=%d", *BaseMontage->GetName(), InstanceID); + PlayMontage(OtherMontage, BaseMontage, Position); + } +} diff --git a/Source/Cloud9/Character/Components/Cloud9AnimationComponent.h b/Source/Cloud9/Character/Components/Cloud9AnimationComponent.h index 1eb448f64..f658c25ba 100644 --- a/Source/Cloud9/Character/Components/Cloud9AnimationComponent.h +++ b/Source/Cloud9/Character/Components/Cloud9AnimationComponent.h @@ -18,7 +18,28 @@ class CLOUD9_API UCloud9AnimationComponent : public UActorComponent UAnimInstance* GetAnimInstance() const; - bool PlayMontage(UAnimMontage* Montage, float StartTime = 0.0f, float Rate = 1.0f) const; + bool PlayMontage( + UAnimMontage* NewBasePoseMontage, + UAnimMontage* NewOtherPoseMontage, + float StartTime = 0.0f, + float Rate = 1.0f); bool IsAnyMontagePlaying() const; + void StopMontage(const UAnimMontage* Montage) const; + UAnimMontage* StopMontage(int32 InstanceID) const; + void StopAllMontages(float BlendOut) const; + + // TODO: Change with delegate and to untethered PlayerController with AnimationComponent + void PoseChanged(); + +protected: + // This was made in an account that several montages can be played in same time, + // but easier way is just stop all other montages for weapon deploy and reload + // when the character pose changed. + // + // Currently left as is in order future modification. + + /** Montage instance ID to another pose montage */ + UPROPERTY() + TMap Montages; }; diff --git a/Source/Cloud9/Contollers/Cloud9KeyboardController.cpp b/Source/Cloud9/Contollers/Cloud9KeyboardController.cpp index 61e446ac3..65a0e99a6 100644 --- a/Source/Cloud9/Contollers/Cloud9KeyboardController.cpp +++ b/Source/Cloud9/Contollers/Cloud9KeyboardController.cpp @@ -25,6 +25,7 @@ #include "Cloud9KeyboardController.h" +#include "Cloud9/Character/Components/Cloud9AnimationComponent.h" #include "GameFramework/SpringArmComponent.h" #include "Cloud9/Game/Cloud9DeveloperSettings.h" @@ -147,6 +148,9 @@ void UCloud9KeyboardController::OnCrouchPressed() if (let Pawn = GetCloud9Pawn(); IsValid(Pawn)) { Pawn->Crouch(false); + let AnimationComponent = Pawn->GetAnimationComponent(); + OBJECT_VOID_IF_FAIL(IsValid(AnimationComponent), Error, "AnimationComponent is invalid"); + AnimationComponent->PoseChanged(); } } @@ -155,6 +159,9 @@ void UCloud9KeyboardController::OnCrouchReleased() if (let Pawn = GetCloud9Pawn(); IsValid(Pawn)) { Pawn->UnCrouch(false); + let AnimationComponent = Pawn->GetAnimationComponent(); + OBJECT_VOID_IF_FAIL(IsValid(AnimationComponent), Error, "AnimationComponent is invalid"); + AnimationComponent->PoseChanged(); } } diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp index 523a85fd1..cb0d2b548 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp @@ -222,7 +222,7 @@ bool ACloud9WeaponBase::UpdateWeaponAttachment(EWeaponSlot NewSlot, EWeaponBond CharacterMesh->GetSocketByName(SocketName), false, Error, "Socket not found in character mesh for '%s'", SLOT_NAME); - OBJECT_DISPLAY( + OBJECT_VERBOSE( "Update attachment to character '%s' into socket '%s'", *Character->GetName(), *SocketName.ToString()); diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp index ea60164e2..0bf92629d 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp @@ -208,11 +208,12 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) OBJECT_VOID_IF_FAIL(IsValid(AnimComponent), Error, "AnimComponent isn't valid"); let WeaponInfo = WeaponDefinition.GetWeaponInfo(); - let PoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let BasePoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let OtherPoseMontages = WeaponDefinition.GetPoseMontages(not Character->bIsCrouched); let CommonData = WeaponDefinition.GetCommonData(); - let HasSecondaryAction = PoseMontages->bHasSecondaryAction; - let HasLoopedReload = PoseMontages->bHasReloadLoop; + let HasSecondaryAction = BasePoseMontages->bHasSecondaryAction; + let HasLoopedReload = BasePoseMontages->bHasReloadLoop; if (WeaponState.IsActionActive(EWeaponAction::ReloadStart)) { @@ -221,8 +222,14 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) WeaponInfo->ReloadTime, [&] { + // Stop current montage action to if change pose + AnimComponent->StopAllMontages(0.0f); + return UpdateReloadAmmo(WeaponInfo->Type == EWeaponType::Shotgun) - and AnimComponent->PlayMontage(PoseMontages->ReloadMontage); + and AnimComponent->PlayMontage( + BasePoseMontages->ReloadMontage, + OtherPoseMontages->ReloadMontage + ); }, [this, HasLoopedReload] { @@ -253,7 +260,10 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) [&] { return UpdateReloadAmmo(true) and - AnimComponent->PlayMontage(PoseMontages->ReloadLoopMontage); + AnimComponent->PlayMontage( + BasePoseMontages->ReloadLoopMontage, + OtherPoseMontages->ReloadLoopMontage + ); } ); } @@ -263,7 +273,13 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::ReloadLoop, WeaponInfo->ReloadEndTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->ReloadEndMontage); } + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->ReloadEndMontage, + OtherPoseMontages->ReloadEndMontage + ); + } ); WeaponState.ClearAction(EWeaponAction::ReloadEnd); @@ -274,7 +290,15 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::Deploy, WeaponInfo->DeployTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->DeployMontage); }, + [&] + { + // Stop current montage action to if change pose + AnimComponent->StopAllMontages(0.0f); + + return AnimComponent->PlayMontage( + BasePoseMontages->DeployMontage, + OtherPoseMontages->DeployMontage); + }, [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } @@ -301,7 +325,10 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) Error, "Weapon fire failure status=%d", Status); OBJECT_RETURN_IF_FAIL( - AnimComponent->PlayMontage(PoseMontages->PrimaryActionMontage), false, + AnimComponent->PlayMontage( + BasePoseMontages->PrimaryActionMontage, + OtherPoseMontages->PrimaryActionMontage + ), false, Error, "No montage for primary action specified" ); diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp index 79f5f28a8..3452743b2 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp @@ -145,14 +145,21 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) let AnimComponent = Character->GetAnimationComponent(); OBJECT_VOID_IF_FAIL(IsValid(AnimComponent), Error, "AnimComponent isn't valid"); - let PoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let BasePoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let OtherPoseMontages = WeaponDefinition.GetPoseMontages(not Character->bIsCrouched); if (WeaponState.IsActionActive(EWeaponAction::Deploy)) { ExecuteAction( EWeaponAction::Deploy, GetWeaponInfo()->DeployTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->DeployMontage); }, + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->DeployMontage, + OtherPoseMontages->DeployMontage + ); + }, [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } @@ -161,7 +168,13 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::PrimaryStart, GetWeaponInfo()->PinpullTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->PinpullPrimaryActionMontage); }, + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->PinpullPrimaryActionMontage, + OtherPoseMontages->PinpullPrimaryActionMontage + ); + }, [this] { WeaponState.ClearAction(EWeaponAction::PrimaryStart); @@ -173,8 +186,9 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) { // Play hold frame of montage AnimComponent->PlayMontage( - PoseMontages->PinpullPrimaryActionMontage, - PoseMontages->PinpullPrimaryActionHoldTiming); + BasePoseMontages->PinpullPrimaryActionMontage, + OtherPoseMontages->PinpullPrimaryActionMontage, + BasePoseMontages->PinpullPrimaryActionHoldTiming); if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) { @@ -194,7 +208,13 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::PrimaryEnd, GetWeaponInfo()->ThrowTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->PrimaryActionMontage); }, + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->PrimaryActionMontage, + OtherPoseMontages->PrimaryActionMontage + ); + }, [this] { WeaponState.ClearAction(EWeaponAction::PrimaryEnd); diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp index 48e0fcb55..20788f428 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp @@ -78,16 +78,23 @@ void ACloud9WeaponMelee::Tick(float DeltaSeconds) OBJECT_VOID_IF_FAIL(IsValid(AnimComponent), Error, "AnimComponent isn't valid"); let WeaponInfo = WeaponDefinition.GetWeaponInfo(); - let PoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let BasePoseMontages = WeaponDefinition.GetPoseMontages(Character->bIsCrouched); + let OtherPoseMontages = WeaponDefinition.GetPoseMontages(not Character->bIsCrouched); - let HasSecondaryAction = PoseMontages->bHasSecondaryAction; + let HasSecondaryAction = BasePoseMontages->bHasSecondaryAction; if (WeaponState.IsActionActive(EWeaponAction::Deploy)) { ExecuteAction( EWeaponAction::Deploy, WeaponInfo->DeployTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->DeployMontage); }, + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->DeployMontage, + OtherPoseMontages->DeployMontage + ); + }, [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } @@ -100,7 +107,13 @@ void ACloud9WeaponMelee::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::PrimaryLoop, WeaponInfo->SlashCycleTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->PrimaryActionMontage); } + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->PrimaryActionMontage, + OtherPoseMontages->PrimaryActionMontage + ); + } ); } else if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) @@ -112,7 +125,13 @@ void ACloud9WeaponMelee::Tick(float DeltaSeconds) ExecuteAction( EWeaponAction::Secondary, WeaponInfo->StabCycleTime, - [&] { return AnimComponent->PlayMontage(PoseMontages->SecondaryActionMontage); } + [&] + { + return AnimComponent->PlayMontage( + BasePoseMontages->SecondaryActionMontage, + OtherPoseMontages->SecondaryActionMontage + ); + } ); // no auto stab