1069 lines
35 KiB
C++
1069 lines
35 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CombatPlayerCharacter.h"
|
|
|
|
#include "CombatPlayerController.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Components/CapsuleComponent.h"
|
|
#include "Components/InputComponent.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "GameFramework/Controller.h"
|
|
#include "GameFramework/SpringArmComponent.h"
|
|
#include "EnhancedInputComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "Interface/Interact.h"
|
|
#include "Actor/BaseWeapon.h"
|
|
#include "Components/CombatComponent.h"
|
|
#include "Components/StatsComponent.h"
|
|
#include "Definitions/CombatGameplayTags.h"
|
|
#include "Engine/DamageEvents.h"
|
|
#include "NiagaraFunctionLibrary.h"
|
|
#include "Actor/BaseConsumable.h"
|
|
#include "Components/EquipmentComponent.h"
|
|
#include "DamageType/AttackDamageType.h"
|
|
#include "Kismet/KismetSystemLibrary.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "UI/CombatHUD.h"
|
|
#include "UI/UI_MainHUD.h"
|
|
#include "UI/UI_PotionAmountText.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ACombatCharacter
|
|
|
|
ACombatPlayerCharacter::ACombatPlayerCharacter()
|
|
{
|
|
// Set size for collision capsule
|
|
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
|
|
|
|
// Don't rotate when the controller rotates. Let that just affect the camera.
|
|
bUseControllerRotationPitch = false;
|
|
bUseControllerRotationYaw = false;
|
|
bUseControllerRotationRoll = false;
|
|
|
|
// Configure character movement
|
|
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
|
|
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
|
|
|
|
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
|
|
// instead of recompiling to adjust them
|
|
GetCharacterMovement()->MaxAcceleration = 1500.f;
|
|
GetCharacterMovement()->BrakingFrictionFactor = 1.f;
|
|
GetCharacterMovement()->bUseSeparateBrakingFriction = true; //Sprint 시 애니메이션이 끊기는 거 방지
|
|
GetCharacterMovement()->JumpZVelocity = 700.f;
|
|
GetCharacterMovement()->AirControl = 0.35f;
|
|
GetCharacterMovement()->MaxWalkSpeed = 500.f;
|
|
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
|
|
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
|
|
GetCharacterMovement()->GravityScale = 1.75f;
|
|
|
|
GetMesh()->SetRelativeLocationAndRotation(FVector(0.f, 0.f, -100.f), FRotator(0.f, -90.f, 0.f));
|
|
|
|
// Create a camera boom (pulls in towards the player if there is a collision)
|
|
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
|
CameraBoom->SetupAttachment(RootComponent);
|
|
CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character
|
|
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
|
|
|
|
// Create a follow camera
|
|
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
|
|
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
|
|
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
|
|
|
|
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
|
|
// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
|
|
|
|
|
|
// Setting CombatComponent
|
|
CombatComponent = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
|
|
CombatComponent->OnCombatToggled.AddUObject(this, &ACombatPlayerCharacter::CharacterCombatToggled);
|
|
|
|
// Setting StateManagerComponent
|
|
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
|
|
StateManagerComponent->OnStateBegin.AddUObject(this, &ACombatPlayerCharacter::CharacterStateBegin);
|
|
StateManagerComponent->OnStateEnd.AddUObject(this, &ACombatPlayerCharacter::CharacterStateEnd);
|
|
|
|
// Setting StatsComponent
|
|
StatsComponent = CreateDefaultSubobject<UStatsComponent>(TEXT("StatsComponent"));
|
|
StatsComponent->OnCurrentStatValueUpdated.AddUObject(this, &ACombatPlayerCharacter::CharacterCurrentStatValueUpdated);
|
|
StatsComponent->SetBaseStatValue(EStats::Health, 100.f);
|
|
StatsComponent->SetMaxStatValue(EStats::Health, 100.f);
|
|
StatsComponent->SetBaseStatValue(EStats::Stamina, 100.f);
|
|
StatsComponent->SetMaxStatValue(EStats::Stamina, 100.f);
|
|
StatsComponent->SetBaseStatValue(EStats::Armor, 0.f);
|
|
StatsComponent->SetMaxStatValue(EStats::Armor, 100.f);
|
|
StatsComponent->InitializeStats();
|
|
|
|
//Setting TargetComponent
|
|
TargetingComponent = CreateDefaultSubobject<UTargetingComponent>(TEXT("TargetingComponent"));
|
|
|
|
//Setting EquipmentComponent
|
|
EquipmentComponent = CreateDefaultSubobject<UEquipmentComponent>(TEXT("EquipmentComponent"));
|
|
|
|
//Setting MovementSpeed
|
|
MovementSpeedMode = EMovementSpeedMode::Jogging;
|
|
WalkingSpeed = 200.f;
|
|
JoggingSpeed = 500.f;
|
|
SprintSpeed = 700.f;
|
|
|
|
//Settings OwnedGameplayTags
|
|
OwnedGameplayTags.AddTag(FCombatGameplayTags::Get().Character_Player);
|
|
|
|
ChargeAttackTime = 0.18f;
|
|
PelvisBoneName = TEXT("pelvis");
|
|
|
|
//Setting Timeline
|
|
RotateToTargetTimeLineComponent = CreateDefaultSubobject<UTimelineComponent>(TEXT("RotateToTargetTimeLineComponent"));
|
|
}
|
|
|
|
void ACombatPlayerCharacter::BeginPlay()
|
|
{
|
|
// Call the base class
|
|
Super::BeginPlay();
|
|
|
|
//Add Input Mapping Context
|
|
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
|
|
{
|
|
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
|
|
{
|
|
Subsystem->AddMappingContext(DefaultMappingContext, 0);
|
|
}
|
|
}
|
|
|
|
EquipmentComponent->InitializeEquipment();
|
|
|
|
//Setting Timeline - if you set on Constructor, Can not get Curve
|
|
FOnTimelineFloat TimelineFloatCallback;
|
|
TimelineFloatCallback.BindUFunction(this, FName("RotateToTargetUpdate"));
|
|
RotateToTargetTimeLineComponent->SetTimelineLength(5.0f);
|
|
RotateToTargetTimeLineComponent->SetLooping(false);
|
|
if(CurveFloatTimeline)
|
|
RotateToTargetTimeLineComponent->AddInterpFloat(CurveFloatTimeline, TimelineFloatCallback);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Tick(float DeltaSeconds)
|
|
{
|
|
Super::Tick(DeltaSeconds);
|
|
}
|
|
|
|
float ACombatPlayerCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
|
|
{
|
|
float fDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
|
|
|
|
const UAttackDamageType* damageTypeClass = Cast<UAttackDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
|
|
if(!IsValid(damageTypeClass))
|
|
return false;
|
|
|
|
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
|
|
{
|
|
const FPointDamageEvent* PointDamageEvent = static_cast<const FPointDamageEvent*>(&DamageEvent);
|
|
ReceiveDamage(fDamage, PointDamageEvent, damageTypeClass, EventInstigator);
|
|
|
|
}
|
|
else if(DamageEvent.IsOfType(FRadialDamageEvent::ClassID))
|
|
{
|
|
const FRadialDamageEvent* RadialDamageEvent = static_cast<const FRadialDamageEvent*>(&DamageEvent);
|
|
ReceiveDamage(fDamage, RadialDamageEvent, damageTypeClass, EventInstigator);
|
|
}
|
|
return fDamage;
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ContinueAttack_Implementation()
|
|
{
|
|
if (CombatComponent->GetIsAttackSaved())
|
|
{
|
|
CombatComponent->SetIsAttackSaved(false);
|
|
if (StateManagerComponent->GetCurrentState() == FCombatGameplayTags::Get().Character_State_Attacking)
|
|
StateManagerComponent->SetCurrentState(FGameplayTag::EmptyTag);
|
|
AttackEvent();
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ResetAttack_Implementation()
|
|
{
|
|
CombatComponent->ResetAttack();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ResetCombat_Implementation()
|
|
{
|
|
CombatComponent->ResetAttack();
|
|
StateManagerComponent->ResetState();
|
|
StateManagerComponent->SetCurrentAction(FGameplayTag::EmptyTag);
|
|
if(CanPerformBlock())
|
|
CombatComponent->SetBlockingState(true);
|
|
}
|
|
|
|
FRotator ACombatPlayerCharacter::GetDesiredRotation_Implementation()
|
|
{
|
|
FVector InputVector = GetCharacterMovement()->GetLastInputVector();
|
|
if (InputVector.Equals(FVector(0, 0, 0), 0.001f))
|
|
return GetActorRotation();
|
|
else
|
|
return UKismetMathLibrary::MakeRotFromX(GetLastMovementInputVector());
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanReceiveDamage_Implementation()
|
|
{
|
|
bool result = (StateManagerComponent->GetCurrentState() != FCombatGameplayTags::Get().Character_State_Dead);
|
|
result &= !bEnableIFrame;
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Input
|
|
|
|
void ACombatPlayerCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
|
|
{
|
|
// Set up action bindings
|
|
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
|
|
{
|
|
//Jumping
|
|
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACombatPlayerCharacter::Jumping);
|
|
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
|
|
|
|
//Moving
|
|
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ACombatPlayerCharacter::Move);
|
|
|
|
//Looking
|
|
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ACombatPlayerCharacter::Look);
|
|
|
|
//Interact
|
|
EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::Interact);
|
|
|
|
//ToggleCombat
|
|
EnhancedInputComponent->BindAction(ToggleCombatInputAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::ToggleCombatAction);
|
|
|
|
//LightAttack
|
|
EnhancedInputComponent->BindAction(LightAttackAction, ETriggerEvent::Triggered, this, &ACombatPlayerCharacter::LightChargeAttack);
|
|
EnhancedInputComponent->BindAction(LightAttackAction, ETriggerEvent::Completed, this, &ACombatPlayerCharacter::LightAttack);
|
|
|
|
//HeavyAttack
|
|
EnhancedInputComponent->BindAction(HeavyAttackAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::HeavyAttack);
|
|
|
|
//Dodge
|
|
EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::Dodge);
|
|
|
|
//ToggleWalk
|
|
EnhancedInputComponent->BindAction(ToggleWalkAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::ToggleWalk);
|
|
|
|
//Sprint
|
|
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::StartSprint);
|
|
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &ACombatPlayerCharacter::StopSprint);
|
|
|
|
//ToggleLockOn
|
|
EnhancedInputComponent->BindAction(ToggleLockOnAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::ToggleLockOn);
|
|
|
|
//Block
|
|
EnhancedInputComponent->BindAction(BlockAction, ETriggerEvent::Triggered, this, &ACombatPlayerCharacter::Blocking);
|
|
EnhancedInputComponent->BindAction(BlockAction, ETriggerEvent::Completed, this, &ACombatPlayerCharacter::StopBlocking);
|
|
|
|
//Use Item
|
|
EnhancedInputComponent->BindAction(UseItemAction, ETriggerEvent::Started, this, &ACombatPlayerCharacter::UseItem);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::SetCanMove_Implementation(bool inputCanMove)
|
|
{
|
|
bCanMove = inputCanMove;
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ActivateCollision_Implementation(ECollisionPart CollisionPart)
|
|
{
|
|
ABaseWeapon* weapon = CombatComponent->GetMainWeapon();
|
|
if(IsValid(weapon))
|
|
weapon->ActivateCollision(CollisionPart);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::DeactivateCollision_Implementation(ECollisionPart CollisionPart)
|
|
{
|
|
ABaseWeapon* weapon = CombatComponent->GetMainWeapon();
|
|
if(IsValid(weapon))
|
|
weapon->DeactivateCollision(CollisionPart);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::SetIFrame_Implementation(bool InputEnableIFrame)
|
|
{
|
|
bEnableIFrame = InputEnableIFrame;
|
|
}
|
|
|
|
float ACombatPlayerCharacter::PerformAction(FGameplayTag ActionTag, FGameplayTag StateTag, int32 MontageIndex, bool bRandomIndex)
|
|
{
|
|
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
|
if (!CurrentWeapon)
|
|
return false;
|
|
|
|
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(ActionTag);
|
|
int32 montageIdx = MontageIndex;
|
|
|
|
if(bRandomIndex)
|
|
montageIdx = FMath::RandRange(0, montages.Num() - 1);
|
|
else if (montages.Num() <= montageIdx)
|
|
montageIdx = 0;
|
|
|
|
if (!montages.IsValidIndex(montageIdx))
|
|
return false;
|
|
|
|
UAnimMontage* actionMontage = montages[montageIdx];
|
|
float actionDuration = 0.f;
|
|
if (IsValid(actionMontage))
|
|
{
|
|
StateManagerComponent->SetCurrentState(StateTag);
|
|
StateManagerComponent->SetCurrentAction(ActionTag);
|
|
if(!IsValid(GetMesh()->GetAnimInstance()))
|
|
return 0.f;
|
|
actionDuration = GetMesh()->GetAnimInstance()->Montage_Play(actionMontage);
|
|
}
|
|
else
|
|
{
|
|
FString str = FString::Printf(TEXT("Dodge Index %d is NOT VALID!!"), montageIdx);
|
|
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
|
|
}
|
|
return actionDuration;
|
|
}
|
|
|
|
float ACombatPlayerCharacter::PerformAttack(FGameplayTag AttackType, int32 AttackIndex, bool bRandomIndex)
|
|
{
|
|
if(!CombatComponent->GetCombatEnabled())
|
|
return 0.f;
|
|
|
|
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
|
if (!CurrentWeapon)
|
|
return 0.f;
|
|
|
|
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(AttackType);
|
|
int32 attackIdx = AttackIndex;
|
|
|
|
if(bRandomIndex)
|
|
attackIdx = FMath::RandRange(0, montages.Num() - 1);
|
|
else if (montages.Num() <= attackIdx)
|
|
attackIdx = 0;
|
|
|
|
if (!montages.IsValidIndex(attackIdx))
|
|
return 0.f;
|
|
|
|
UAnimMontage* attackMontage = montages[attackIdx];
|
|
float attackDuration = 0.f;
|
|
if (!IsValid(attackMontage))
|
|
{
|
|
FString debugStr = FString::Printf(TEXT("Index %d Is NOT VALID!!"), attackIdx);
|
|
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, debugStr);
|
|
}
|
|
else
|
|
{
|
|
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Attacking);
|
|
StateManagerComponent->SetCurrentAction(AttackType);
|
|
|
|
if(!IsValid(GetMesh()->GetAnimInstance()))
|
|
return 0.f;
|
|
attackDuration = GetMesh()->GetAnimInstance()->Montage_Play(attackMontage);
|
|
|
|
int idx = attackIdx + 1;
|
|
if (idx >= montages.Num())
|
|
idx = 0;
|
|
|
|
CombatComponent->SetAttackCount(idx);
|
|
}
|
|
return attackDuration;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::PerformCustomAction(FGameplayTag ActionTag, FGameplayTag StateTag, UAnimMontage* InMontage, float fMontagePlayRate, bool bAutoReset)
|
|
{
|
|
|
|
UAnimMontage* actionMontage = InMontage;
|
|
if (IsValid(actionMontage))
|
|
{
|
|
StateManagerComponent->SetCurrentState(StateTag);
|
|
StateManagerComponent->SetCurrentAction(ActionTag);
|
|
if(!IsValid(GetMesh()->GetAnimInstance()))
|
|
return false;
|
|
GetMesh()->GetAnimInstance()->Montage_Play(actionMontage, fMontagePlayRate);
|
|
}
|
|
else
|
|
{
|
|
FString str = FString::Printf(TEXT("PerformCustomAction is NOT VALID!!"));
|
|
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ACombatPlayerCharacter::SetMovementSpeedMode(EMovementSpeedMode NewSpeedMode)
|
|
{
|
|
if (NewSpeedMode == MovementSpeedMode)
|
|
return;
|
|
|
|
MovementSpeedMode = NewSpeedMode;
|
|
TargetingComponent->UpdateRotationMode();
|
|
|
|
switch (MovementSpeedMode)
|
|
{
|
|
case EMovementSpeedMode::Walking:
|
|
GetCharacterMovement()->MaxWalkSpeed = WalkingSpeed;
|
|
break;
|
|
case EMovementSpeedMode::Jogging:
|
|
GetCharacterMovement()->MaxWalkSpeed = JoggingSpeed;
|
|
break;
|
|
case EMovementSpeedMode::Sprinting:
|
|
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::SetPotionUI(UUserWidget* potionUI)
|
|
{
|
|
PotionUI = potionUI;
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Move(const FInputActionValue& Value)
|
|
{
|
|
if(!bCanMove) //Value changes SetCanMove Func Call to Animnotify
|
|
return;
|
|
|
|
// input is a Vector2D
|
|
FVector2D MovementVector = Value.Get<FVector2D>();
|
|
|
|
if (Controller != nullptr)
|
|
{
|
|
// find out which way is forward
|
|
const FRotator Rotation = Controller->GetControlRotation();
|
|
const FRotator YawRotation(0, Rotation.Yaw, 0);
|
|
|
|
// get forward vector
|
|
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
|
|
|
// get right vector
|
|
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
|
|
|
// add movement
|
|
AddMovementInput(ForwardDirection, MovementVector.Y);
|
|
AddMovementInput(RightDirection, MovementVector.X);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Look(const FInputActionValue& Value)
|
|
{
|
|
// input is a Vector2D
|
|
FVector2D LookAxisVector = Value.Get<FVector2D>();
|
|
|
|
if (Controller != nullptr)
|
|
{
|
|
// add yaw and pitch input to controller
|
|
if(!TargetingComponent->GetIsTargeting()) // Not Targeting
|
|
{
|
|
AddControllerYawInput(LookAxisVector.X);
|
|
AddControllerPitchInput(LookAxisVector.Y);
|
|
}
|
|
else // Targeting
|
|
AddControllerPitchInput(LookAxisVector.Y * 0.2f);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Jumping(const FInputActionValue& Value)
|
|
{
|
|
if (!CanJumping())
|
|
return;
|
|
|
|
StopAnimMontage();
|
|
StateManagerComponent->ResetState();
|
|
CombatComponent->ResetAttack();
|
|
Super::Jump();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Interact(const FInputActionValue& Value)
|
|
{
|
|
bool bInput = Value.Get<bool>();
|
|
|
|
TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypes;
|
|
TEnumAsByte<EObjectTypeQuery> InteractiveType = UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_GameTraceChannel1);
|
|
ObjectTypes.Add(InteractiveType);
|
|
|
|
TArray<AActor*> ActorsToIgnore;
|
|
FHitResult OutHit;
|
|
if (UKismetSystemLibrary::SphereTraceSingleForObjects(GetWorld(), GetActorLocation(), GetActorLocation(), 100.f, ObjectTypes, false, ActorsToIgnore, EDrawDebugTrace::ForDuration, OutHit, true))
|
|
{
|
|
IInteract* interactObject = Cast<IInteract>(OutHit.GetActor());
|
|
if (interactObject)
|
|
interactObject->Interact(this);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ToggleCombatAction(const FInputActionValue& Value)
|
|
{
|
|
bool bInput = Value.Get<bool>();
|
|
|
|
ToggleCombatEvent();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::LightAttack(const FInputActionValue& Value)
|
|
{
|
|
if (!ResetChargeAttack())
|
|
return;
|
|
IsHeavyAttack = false;
|
|
|
|
if (StateManagerComponent->GetCurrentState() == FCombatGameplayTags::Get().Character_State_Attacking)
|
|
CombatComponent->SetIsAttackSaved(true);
|
|
else
|
|
AttackEvent();
|
|
}
|
|
|
|
//누르고 있는 시간을 받기 위해서 FInputActionValue가 아니라 FInputActionInstance로 인자값을 받음
|
|
void ACombatPlayerCharacter::LightChargeAttack(const FInputActionInstance& Instance)
|
|
{
|
|
AttackHeldTime = Instance.GetElapsedTime();
|
|
bAttackCharged = (AttackHeldTime >= ChargeAttackTime);
|
|
if (bAttackCharged)
|
|
ChargeAttackEvent();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::HeavyAttack(const FInputActionValue& Value)
|
|
{
|
|
if (bAttackCharged)
|
|
return;
|
|
IsHeavyAttack = true;
|
|
if (StateManagerComponent->GetCurrentState() == FCombatGameplayTags::Get().Character_State_Attacking)
|
|
CombatComponent->SetIsAttackSaved(true);
|
|
else
|
|
AttackEvent();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Dodge(const FInputActionValue& Value)
|
|
{
|
|
if (CanPerformDodge())
|
|
{
|
|
PerformAction(FCombatGameplayTags::Get().Character_Action_Dodge, FCombatGameplayTags::Get().Character_State_Dodging, 0);
|
|
ABaseWeapon* pBaseWeapon = CombatComponent->GetMainWeapon();
|
|
if (IsValid(pBaseWeapon))
|
|
{
|
|
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -1.f * pBaseWeapon->GetStatCostForAction());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ToggleWalk(const FInputActionValue& Value)
|
|
{
|
|
if (GetCombatMovementSpeedMode() != EMovementSpeedMode::Walking)
|
|
SetMovementSpeedMode(EMovementSpeedMode::Walking);
|
|
else
|
|
SetMovementSpeedMode(EMovementSpeedMode::Jogging);
|
|
|
|
}
|
|
|
|
void ACombatPlayerCharacter::StartSprint(const FInputActionValue& Value)
|
|
{
|
|
if (CanPerformSprint())
|
|
{
|
|
SetMovementSpeedMode(EMovementSpeedMode::Sprinting);
|
|
UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
|
|
if (World)
|
|
World->GetTimerManager().SetTimer(StaminaTimerHandle, this, &ACombatPlayerCharacter::SprintStaminaCost, 0.1f, true);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::StopSprint(const FInputActionValue& Value)
|
|
{
|
|
DisableSprint();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ToggleLockOn(const FInputActionValue& Value)
|
|
{
|
|
TargetingComponent->ToggleLockOn();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::Blocking(const FInputActionValue& Value)
|
|
{
|
|
bIsBlockPressed = true;
|
|
if(CanPerformBlock())
|
|
{
|
|
CombatComponent->SetBlockingState(true);
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::StopBlocking(const FInputActionValue& Value)
|
|
{
|
|
bIsBlockPressed = false;
|
|
CombatComponent->SetBlockingState(false);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::UseItem(const FInputActionValue& Value)
|
|
{
|
|
if(CanUseItem())
|
|
EquipmentComponent->PerformActionFromItem(FCombatGameplayTags::Get().Item_Consumable);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::CharacterStateBegin(FGameplayTag CharState)
|
|
{
|
|
if (FGameplayTag::EmptyTag == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
|
|
{
|
|
RotateToTarget();
|
|
}
|
|
else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_Dead == CharState)
|
|
{
|
|
PerformDeath();
|
|
}
|
|
else if (FCombatGameplayTags::Get().Character_State_Disable == CharState)
|
|
{/*None*/}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::CharacterStateEnd(FGameplayTag CharState)
|
|
{
|
|
if (FGameplayTag::EmptyTag == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
|
|
{
|
|
StopRotateToTarget();
|
|
}
|
|
else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_Dead == CharState)
|
|
{/*None*/}
|
|
else if (FCombatGameplayTags::Get().Character_State_Disable == CharState)
|
|
{/*None*/}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::CharacterCurrentStatValueUpdated(EStats statType, float value)
|
|
{
|
|
if (!(statType == EStats::Health) || value > 0.f)
|
|
return;
|
|
|
|
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Dead);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::CharacterCombatToggled(bool IsCombatEnabled)
|
|
{
|
|
if(IsValid(TargetingComponent))
|
|
TargetingComponent->UpdateRotationMode();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ToggleCombatEvent()
|
|
{
|
|
ABaseWeapon* baseWeapon = CombatComponent->GetMainWeapon();
|
|
if (!baseWeapon)
|
|
return;
|
|
|
|
if (!CanPerformToggleCombat())
|
|
return;
|
|
|
|
if (!CombatComponent->GetCombatEnabled())
|
|
PerformAction(FCombatGameplayTags::Get().Character_Action_EnterCombat, FCombatGameplayTags::Get().Character_State_GeneralActionState, 0);
|
|
else
|
|
PerformAction(FCombatGameplayTags::Get().Character_Action_ExitCombat, FCombatGameplayTags::Get().Character_State_GeneralActionState, 0);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::AttackEvent()
|
|
{
|
|
if (!CanPerformAttack())
|
|
return;
|
|
|
|
if (CombatComponent->GetCombatEnabled())
|
|
{
|
|
PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount());
|
|
if (IsValid(CombatComponent->GetMainWeapon()))
|
|
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -1.f * CombatComponent->GetMainWeapon()->GetStatCostForAction());
|
|
}
|
|
else
|
|
ToggleCombatEvent();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ChargeAttackEvent()
|
|
{
|
|
if (!CanPerformAttack())
|
|
return;
|
|
if(CombatComponent->GetCombatEnabled())
|
|
{
|
|
PerformAttack(FCombatGameplayTags::Get().Character_Action_Attack_ChargedAttack, CombatComponent->GetAttackCount());
|
|
ABaseWeapon* pBaseWeapon = CombatComponent->GetMainWeapon();
|
|
if (IsValid(pBaseWeapon))
|
|
{
|
|
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -1.f * pBaseWeapon->GetStatCostForAction());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::ResetChargeAttack()
|
|
{
|
|
AttackHeldTime = 0.f;
|
|
if (bAttackCharged)
|
|
{
|
|
bAttackCharged = false;
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
|
|
{
|
|
if (!GetMesh())
|
|
return;
|
|
|
|
GetMesh()->SetPhysicsLinearVelocity(GetActorForwardVector() * -InitialSpeed, false, PelvisBoneName);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::EnableRagdoll()
|
|
{
|
|
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None, 0); //movement 더 이상 없게 바꿈
|
|
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore); //충돌무시
|
|
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore); //카메라 충돌무시
|
|
FAttachmentTransformRules rules(EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, true);
|
|
GetCameraBoom()->AttachToComponent(GetMesh(), rules, PelvisBoneName); //camera를 척추뼈에 붙임
|
|
GetCameraBoom()->bDoCollisionTest = false; //카메라 충돌 없게 함
|
|
GetMesh()->SetCollisionProfileName(TEXT("ragdoll"), true); //ragdoll 로 변경
|
|
GetMesh()->SetAllBodiesBelowSimulatePhysics(PelvisBoneName, true, true);
|
|
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::DisableSprint()
|
|
{
|
|
UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
|
|
if (World)
|
|
World->GetTimerManager().ClearTimer(StaminaTimerHandle);
|
|
|
|
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
|
|
SetMovementSpeedMode(EMovementSpeedMode::Jogging);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::SprintStaminaCost()
|
|
{
|
|
if (!CanPerformSprint())
|
|
{
|
|
DisableSprint();
|
|
return;
|
|
}
|
|
|
|
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -2.f);
|
|
if (StatsComponent->GetCurrentStatValue(EStats::Stamina) < 10.f)
|
|
{
|
|
DisableSprint();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ApplyHitReaction(EDamageType InDamageType)
|
|
{
|
|
if(WasHitBlocked())
|
|
{
|
|
PerformBlock();
|
|
}
|
|
else
|
|
{
|
|
switch (InDamageType)
|
|
{
|
|
case EDamageType::None:
|
|
PerformHitStun();
|
|
break;
|
|
case EDamageType::MeleeDamage:
|
|
PerformHitStun();
|
|
break;
|
|
case EDamageType::KnockdownDamage:
|
|
PerformKnockdown();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ApplyImpactEffect(EDamageType InDamageType)
|
|
{
|
|
//Play Sound
|
|
UGameplayStatics::PlaySoundAtLocation(this, HitSound, LastHitInfo.Location);
|
|
|
|
//Hit Effect
|
|
UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), HitEmitter, LastHitInfo.Location);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ReceiveDamage(float Damage, const FPointDamageEvent* pointDamageEvent, const UAttackDamageType* damageTypeClass, AController* eventInstigator)
|
|
{
|
|
//앞에서 맞았는지 뒤에서 맞았는지 판별
|
|
bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(eventInstigator->GetPawn()), -0.1f, 1.f);
|
|
LastHitInfo = pointDamageEvent->HitInfo;
|
|
|
|
//공격을 막았을 경우
|
|
if(WasHitBlocked())
|
|
Damage = 0.f; //막았을 때는 데미지 처리안함
|
|
else
|
|
ApplyImpactEffect(damageTypeClass->DamageType); //play sound, effect
|
|
|
|
//스텟 관련 처리
|
|
StatsComponent->TakeDamageOnStat(Damage);
|
|
|
|
if (CanReceiveHitReaction())
|
|
ApplyHitReaction(damageTypeClass->DamageType);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::ReceiveDamage(float Damage, const FRadialDamageEvent* radialDamageEvent, const UAttackDamageType* damageTypeClass, AController* eventInstigator)
|
|
{
|
|
//앞에서 맞았는지 뒤에서 맞았는지 판별
|
|
bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(eventInstigator->GetPawn()), -0.1f, 1.f);
|
|
LastHitInfo = radialDamageEvent->ComponentHits[0];
|
|
|
|
//공격을 막았을 경우
|
|
if(WasHitBlocked())
|
|
Damage = 0.f; //막았을 때는 데미지 처리안함
|
|
else
|
|
ApplyImpactEffect(damageTypeClass->DamageType); //play sound, effect
|
|
|
|
//스텟 관련 처리
|
|
StatsComponent->TakeDamageOnStat(Damage);
|
|
|
|
if (CanReceiveHitReaction())
|
|
ApplyHitReaction(damageTypeClass->DamageType);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::RotateToTarget()
|
|
{
|
|
RotateToTargetTimeLineComponent->PlayFromStart();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::StopRotateToTarget()
|
|
{
|
|
RotateToTargetTimeLineComponent->Stop();
|
|
}
|
|
|
|
void ACombatPlayerCharacter::RotateToTargetUpdate(float Value)
|
|
{
|
|
if(!IsValid(TargetingComponent->GetTargetActor()))
|
|
return;
|
|
|
|
const FRotator CurrentRotator = GetActorRotation();
|
|
FVector StartVector = GetActorLocation();
|
|
FVector TargetVector = TargetingComponent->GetTargetActor()->GetActorLocation();
|
|
FRotator TargetRotator = UKismetMathLibrary::FindLookAtRotation(StartVector, TargetVector);
|
|
|
|
UWorld* WorldPointer = GetWorld();
|
|
if(!WorldPointer)
|
|
return;
|
|
double deltaTime = UGameplayStatics::GetWorldDeltaSeconds(WorldPointer);
|
|
|
|
FRotator NewRotator = FMath::RInterpTo(CurrentRotator, TargetRotator, deltaTime, RotateToTargetInterpSpeed);
|
|
NewRotator.Roll = CurrentRotator.Roll;
|
|
NewRotator.Pitch = CurrentRotator.Pitch;
|
|
SetActorRotation(NewRotator);
|
|
}
|
|
|
|
void ACombatPlayerCharacter::PerformDeath()
|
|
{
|
|
if(!DeathAnimations.IsEmpty())
|
|
{
|
|
const int randomIdx = FMath::RandRange(0, DeathAnimations.Num() - 1);
|
|
PlayAnimMontage(DeathAnimations[randomIdx]);
|
|
}
|
|
else
|
|
EnableRagdoll();
|
|
|
|
if(bHitFront) //충돌을 좀 더 그럴싸하게 하기 위해서 피격방향으로 충격
|
|
ApplyHitReactionPhysicsVelocity(2000.f);
|
|
else
|
|
ApplyHitReactionPhysicsVelocity(-2000.f);
|
|
|
|
if (IsValid(CombatComponent->GetMainWeapon()))
|
|
CombatComponent->GetMainWeapon()->SimulateWeaponPhysics(); //무기의 충돌킴
|
|
|
|
FTimerHandle deathTimer;
|
|
GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([&]()
|
|
{
|
|
if (IsValid(CombatComponent->GetMainWeapon()))
|
|
CombatComponent->GetMainWeapon()->Destroy();
|
|
this->Destroy();
|
|
}), 4.f, false); // 4초 후에 object 삭제
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::PerformHitStun()
|
|
{
|
|
UAnimMontage* hitMontage = nullptr;
|
|
if(bHitFront)
|
|
hitMontage = HitStunFrontMontage;
|
|
else
|
|
hitMontage = HitStunBackMontage;
|
|
|
|
if(!IsValid(hitMontage))
|
|
return false;
|
|
|
|
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Disable);
|
|
PlayAnimMontage(hitMontage);
|
|
return true;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::PerformKnockdown()
|
|
{
|
|
UAnimMontage* hitMontage = nullptr;
|
|
if(bHitFront)
|
|
hitMontage = KnockdownFrontMontage;
|
|
else
|
|
hitMontage = KnockdownBackMontage;
|
|
|
|
if(!IsValid(hitMontage))
|
|
return false;
|
|
|
|
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Disable);
|
|
PlayAnimMontage(hitMontage);
|
|
return true;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::PerformBlock()
|
|
{
|
|
if(!IsValid(CombatComponent->GetShieldWeapon()))
|
|
return false;
|
|
TArray<UAnimMontage*> 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;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanPerformAttack()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= (StatsComponent->GetCurrentStatValue(EStats::Stamina) >= 10.f);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanPerformDodge()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
|
ReturnValue &= (StatsComponent->GetCurrentStatValue(EStats::Stamina) >= 10.f);
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanJumping()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanReceiveHitReaction()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanPerformSprint()
|
|
{
|
|
return (FMath::IsNearlyEqual(GetVelocity().Length(), 0.f)) == false;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanPerformBlock()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= CombatComponent->GetCombatEnabled();
|
|
ReturnValue &= IsValid(CombatComponent->GetShieldWeapon());
|
|
ReturnValue &= bIsBlockPressed; //Block Key를 눌렀는가?
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::CanUseItem()
|
|
{
|
|
bool ReturnValue = true;
|
|
|
|
FGameplayTagContainer inputContainer;
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
|
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
|
|
|
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
|
ReturnValue &= (StatsComponent->GetCurrentStatValue(EStats::Stamina) >= 10.f);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
FGameplayTag ACombatPlayerCharacter::GetDesiredAttackType()
|
|
{
|
|
if (GetCharacterMovement()->IsFalling())
|
|
return FCombatGameplayTags::Get().Character_Action_Attack_FallingAttack;
|
|
|
|
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
|
|
return FCombatGameplayTags::Get().Character_Action_Attack_SprintAttack;
|
|
|
|
if (IsHeavyAttack)
|
|
return FCombatGameplayTags::Get().Character_Action_Attack_HeavyAttack;
|
|
|
|
return FCombatGameplayTags::Get().Character_Action_Attack_LightAttack;
|
|
}
|
|
|
|
bool ACombatPlayerCharacter::WasHitBlocked()
|
|
{
|
|
if(IsValid(CombatComponent))
|
|
return (CombatComponent->GetIsBlocking() && bHitFront);
|
|
else
|
|
return false;
|
|
}
|
|
|