diff --git a/Content/CombatSystem/Blueprints/Actor/BP_ToughShield.uasset b/Content/CombatSystem/Blueprints/Actor/BP_ToughShield.uasset index 426af099..d53242b4 100644 Binary files a/Content/CombatSystem/Blueprints/Actor/BP_ToughShield.uasset and b/Content/CombatSystem/Blueprints/Actor/BP_ToughShield.uasset differ diff --git a/Content/CombatSystem/CourseFiles/SoundFX/Block/BlockImpact_cue.uasset b/Content/CombatSystem/CourseFiles/SoundFX/Block/BlockImpact_cue.uasset new file mode 100644 index 00000000..b26de2e0 Binary files /dev/null and b/Content/CombatSystem/CourseFiles/SoundFX/Block/BlockImpact_cue.uasset differ diff --git a/Content/ParagonKwang/Animations/Defense/Deflect_InPlace_Anim1_Montage.uasset b/Content/ParagonKwang/Animations/Defense/Deflect_InPlace_Anim1_Montage.uasset new file mode 100644 index 00000000..7122602a Binary files /dev/null and b/Content/ParagonKwang/Animations/Defense/Deflect_InPlace_Anim1_Montage.uasset differ diff --git a/Content/ParagonKwang/Characters/Heroes/Kwang/Meshes/Kwang_Skeleton.uasset b/Content/ParagonKwang/Characters/Heroes/Kwang/Meshes/Kwang_Skeleton.uasset index d3da2f5e..a9f234fc 100644 Binary files a/Content/ParagonKwang/Characters/Heroes/Kwang/Meshes/Kwang_Skeleton.uasset and b/Content/ParagonKwang/Characters/Heroes/Kwang/Meshes/Kwang_Skeleton.uasset differ diff --git a/Source/D1/Actor/BaseShield.cpp b/Source/D1/Actor/BaseShield.cpp index 4bd49543..3326f242 100644 --- a/Source/D1/Actor/BaseShield.cpp +++ b/Source/D1/Actor/BaseShield.cpp @@ -34,3 +34,29 @@ void ABaseShield::OnUnequipped() Super::OnUnequipped(); CombatComponent->SetShieldWeapon(nullptr); } + +TArray ABaseShield::GetActionMontage(FGameplayTag characterAction) +{ + TArray outputArr; + + if (FCombatGameplayTags::Get().Character_Action_Attack_Blocking == characterAction) + outputArr = BlockAnimations; + else if (FCombatGameplayTags::Get().Character_Action_Attack_LightAttack == characterAction) + outputArr = LightAttackMontage; + else if (FCombatGameplayTags::Get().Character_Action_Attack_HeavyAttack == characterAction) + outputArr = HeavyAttackMontage; + else if (FCombatGameplayTags::Get().Character_Action_Attack_ChargedAttack == characterAction) + outputArr = ChargedAttackMontage; + else if (FCombatGameplayTags::Get().Character_Action_Attack_FallingAttack == characterAction) + outputArr = FallingAttackMontage; + else if (FCombatGameplayTags::Get().Character_Action_Attack_SprintAttack == characterAction) + outputArr = SprintAttackMontage; + else if (FCombatGameplayTags::Get().Character_Action_Dodge == characterAction) + outputArr = DodgeMontage; + else if (FCombatGameplayTags::Get().Character_Action_EnterCombat == characterAction) + outputArr.Add(EnterCombat); + else if (FCombatGameplayTags::Get().Character_Action_ExitCombat == characterAction) + outputArr.Add(ExitCombat); + + return outputArr; +} diff --git a/Source/D1/Actor/BaseShield.h b/Source/D1/Actor/BaseShield.h index f3e1a1e5..ca15c61a 100644 --- a/Source/D1/Actor/BaseShield.h +++ b/Source/D1/Actor/BaseShield.h @@ -17,4 +17,9 @@ class D1_API ABaseShield : public ABaseWeapon public: virtual void OnEquipped() override; virtual void OnUnequipped() override; + virtual TArray GetActionMontage(FGameplayTag characterAction) override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Montages") + TArray> BlockAnimations; }; diff --git a/Source/D1/Actor/BaseWeapon.h b/Source/D1/Actor/BaseWeapon.h index fe06cc17..e924ebdd 100644 --- a/Source/D1/Actor/BaseWeapon.h +++ b/Source/D1/Actor/BaseWeapon.h @@ -44,7 +44,7 @@ public: virtual void ToggleWeaponCombat(bool EnableCombat); public: - TArray GetActionMontage(FGameplayTag characterAction); + virtual TArray GetActionMontage(FGameplayTag characterAction); protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization") diff --git a/Source/D1/CombatPlayerCharacter.cpp b/Source/D1/CombatPlayerCharacter.cpp index 3fe498dc..73c53924 100644 --- a/Source/D1/CombatPlayerCharacter.cpp +++ b/Source/D1/CombatPlayerCharacter.cpp @@ -188,6 +188,8 @@ void ACombatPlayerCharacter::ResetCombat_Implementation() CombatComponent->ResetAttack(); StateManagerComponent->ResetState(); StateManagerComponent->SetCurrentAction(FGameplayTag::EmptyTag); + if(CanPerformBlock()) + CombatComponent->SetBlockingState(true); } FRotator ACombatPlayerCharacter::GetDesiredRotation_Implementation() @@ -542,6 +544,7 @@ void ACombatPlayerCharacter::ToggleLockOn(const FInputActionValue& Value) void ACombatPlayerCharacter::Blocking(const FInputActionValue& Value) { + bIsBlockPressed = true; if(CanPerformBlock()) { CombatComponent->SetBlockingState(true); @@ -550,6 +553,7 @@ void ACombatPlayerCharacter::Blocking(const FInputActionValue& Value) void ACombatPlayerCharacter::StopBlocking(const FInputActionValue& Value) { + bIsBlockPressed = false; CombatComponent->SetBlockingState(false); } @@ -711,19 +715,26 @@ void ACombatPlayerCharacter::SprintStaminaCost() void ACombatPlayerCharacter::ApplyHitReaction(EDamageType InDamageType) { - switch (InDamageType) + if(WasHitBlocked()) { - case EDamageType::None: - PerformHitStun(); - break; - case EDamageType::MeleeDamage: - PerformHitStun(); - break; - case EDamageType::KnockdownDamage: - PerformKnockdown(); - break; - default: - break; + PerformBlock(); + } + else + { + switch (InDamageType) + { + case EDamageType::None: + PerformHitStun(); + break; + case EDamageType::MeleeDamage: + PerformHitStun(); + break; + case EDamageType::KnockdownDamage: + PerformKnockdown(); + break; + default: + break; + } } } @@ -738,15 +749,18 @@ void ACombatPlayerCharacter::ApplyImpactEffect(EDamageType InDamageType) void ACombatPlayerCharacter::ReceiveDamage(float Damage, const FPointDamageEvent* pointDamageEvent, const UAttackDamageType* damageTypeClass, AController* eventInstigator) { - //스텟 관련 처리 - StatsComponent->TakeDamageOnStat(Damage); - //앞에서 맞았는지 뒤에서 맞았는지 판별 bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(eventInstigator->GetPawn()), -0.1f, 1.f); LastHitInfo = pointDamageEvent->HitInfo; - //play sound, effect - ApplyImpactEffect(damageTypeClass->DamageType); + //공격을 막았을 경우 + if(WasHitBlocked()) + Damage = 0.f; //막았을 때는 데미지 처리안함 + else + ApplyImpactEffect(damageTypeClass->DamageType); //play sound, effect + + //스텟 관련 처리 + StatsComponent->TakeDamageOnStat(Damage); if (CanReceiveHitReaction()) ApplyHitReaction(damageTypeClass->DamageType); @@ -754,15 +768,18 @@ void ACombatPlayerCharacter::ReceiveDamage(float Damage, const FPointDamageEvent void ACombatPlayerCharacter::ReceiveDamage(float Damage, const FRadialDamageEvent* radialDamageEvent, const UAttackDamageType* damageTypeClass, AController* eventInstigator) { - //스텟 관련 처리 - StatsComponent->TakeDamageOnStat(Damage); - //앞에서 맞았는지 뒤에서 맞았는지 판별 bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(eventInstigator->GetPawn()), -0.1f, 1.f); LastHitInfo = radialDamageEvent->ComponentHits[0]; - //play sound, effect - ApplyImpactEffect(damageTypeClass->DamageType); + //공격을 막았을 경우 + if(WasHitBlocked()) + Damage = 0.f; //막았을 때는 데미지 처리안함 + else + ApplyImpactEffect(damageTypeClass->DamageType); //play sound, effect + + //스텟 관련 처리 + StatsComponent->TakeDamageOnStat(Damage); if (CanReceiveHitReaction()) ApplyHitReaction(damageTypeClass->DamageType); @@ -858,6 +875,23 @@ bool ACombatPlayerCharacter::PerformKnockdown() return true; } +bool ACombatPlayerCharacter::PerformBlock() +{ + if(!IsValid(CombatComponent->GetShieldWeapon())) + return false; + TArray hitMontage = CombatComponent->GetShieldWeapon()->GetActionMontage(FCombatGameplayTags::Get().Character_Action_Attack_Blocking); + if(hitMontage.Num() <= 0) + return false; + + int32 indexNum = FMath::RandRange(0, hitMontage.Num() - 1); + if(!hitMontage.IsValidIndex(indexNum)) + return false; + + StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Blocking); + PlayAnimMontage(hitMontage[indexNum]); + return true; +} + bool ACombatPlayerCharacter::CanPerformToggleCombat() { bool ReturnValue = true; @@ -952,6 +986,7 @@ bool ACombatPlayerCharacter::CanPerformBlock() ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer); ReturnValue &= CombatComponent->GetCombatEnabled(); ReturnValue &= IsValid(CombatComponent->GetShieldWeapon()); + ReturnValue &= bIsBlockPressed; //Block Key를 눌렀는가? return ReturnValue; } @@ -970,4 +1005,12 @@ FGameplayTag ACombatPlayerCharacter::GetDesiredAttackType() return FCombatGameplayTags::Get().Character_Action_Attack_LightAttack; } +bool ACombatPlayerCharacter::WasHitBlocked() +{ + if(IsValid(CombatComponent)) + return (CombatComponent->GetIsBlocking() && bHitFront); + else + return false; +} + diff --git a/Source/D1/CombatPlayerCharacter.h b/Source/D1/CombatPlayerCharacter.h index 6aba6ee8..b7ce4f15 100644 --- a/Source/D1/CombatPlayerCharacter.h +++ b/Source/D1/CombatPlayerCharacter.h @@ -184,6 +184,7 @@ private: void PerformDeath(); bool PerformHitStun(); bool PerformKnockdown(); + bool PerformBlock(); protected: //Check Func bool CanPerformToggleCombat(); @@ -194,6 +195,7 @@ protected: //Check Func bool CanPerformSprint(); bool CanPerformBlock(); FGameplayTag GetDesiredAttackType(); + bool WasHitBlocked(); public: UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true")) @@ -235,6 +237,7 @@ public: TObjectPtr KnockdownBackMontage; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|Death", meta = (AllowPrivateAccess = "true")) TArray> DeathAnimations; + private: //Timeline UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Timeline", meta = (AllowPrivateAccess = "true")) @@ -253,6 +256,7 @@ private: bool bCanMove = true; bool bHitFront; bool bEnableIFrame; + bool bIsBlockPressed; FHitResult LastHitInfo; FGameplayTagContainer OwnedGameplayTags; }; diff --git a/Source/D1/Components/CombatComponent.h b/Source/D1/Components/CombatComponent.h index b17de27c..a379e873 100644 --- a/Source/D1/Components/CombatComponent.h +++ b/Source/D1/Components/CombatComponent.h @@ -38,6 +38,7 @@ public: FORCEINLINE int32 GetAttackCount() { return AttackCount; } void SetBlockingState(bool enableBlocking); + FORCEINLINE bool GetIsBlocking() {return bIsBlocking; } FORCEINLINE void SetShieldWeapon(ABaseWeapon* inShield) { EquippedShield = inShield; } FORCEINLINE ABaseWeapon* GetShieldWeapon() { return EquippedShield; } diff --git a/Source/D1/Definitions/CombatGameplayTags.cpp b/Source/D1/Definitions/CombatGameplayTags.cpp index 158d66e7..65ed7175 100644 --- a/Source/D1/Definitions/CombatGameplayTags.cpp +++ b/Source/D1/Definitions/CombatGameplayTags.cpp @@ -32,6 +32,11 @@ void FCombatGameplayTags::InitializeNativeGameplayTags() FString("State Attacking") ); + GameplayTags.Character_State_Blocking = UGameplayTagsManager::Get().AddNativeGameplayTag( + FName("Character.State.Blocking"), + FString("State Blocking") + ); + GameplayTags.Character_State_Dead = UGameplayTagsManager::Get().AddNativeGameplayTag( FName("Character.State.Dead"), FString("State Dead") @@ -119,4 +124,9 @@ void FCombatGameplayTags::InitializeNativeGameplayTags() FName("Character.Action.Attack.RareAttack"), FString("Action Attack RareAttack") ); + + GameplayTags.Character_Action_Attack_Blocking = UGameplayTagsManager::Get().AddNativeGameplayTag( + FName("Character.Action.Attack.Blocking"), + FString("Action Attack Blocking") + ); } diff --git a/Source/D1/Definitions/CombatGameplayTags.h b/Source/D1/Definitions/CombatGameplayTags.h index 82a3da82..ecdbe86d 100644 --- a/Source/D1/Definitions/CombatGameplayTags.h +++ b/Source/D1/Definitions/CombatGameplayTags.h @@ -18,6 +18,7 @@ public: //State FGameplayTag Character_State_Attacking; + FGameplayTag Character_State_Blocking; FGameplayTag Character_State_Dead; FGameplayTag Character_State_Disable; FGameplayTag Character_State_Dodging; @@ -37,6 +38,7 @@ public: FGameplayTag Character_Action_Attack_CloseRange; FGameplayTag Character_Action_Attack_MediumRange; FGameplayTag Character_Action_Attack_RareAttack; + FGameplayTag Character_Action_Attack_Blocking; private: static FCombatGameplayTags GameplayTags;