632 lines
18 KiB
C++
632 lines
18 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
||
#include "CombatCharacter.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 "Engine/DamageEvents.h"
|
||
#include "Kismet/KismetSystemLibrary.h"
|
||
#include "Kismet/KismetMathLibrary.h"
|
||
#include "Kismet/GameplayStatics.h"
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
// ACombatCharacter
|
||
|
||
ACombatCharacter::ACombatCharacter()
|
||
{
|
||
// 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()->JumpZVelocity = 700.f;
|
||
GetCharacterMovement()->AirControl = 0.35f;
|
||
GetCharacterMovement()->MaxWalkSpeed = 500.f;
|
||
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
|
||
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
|
||
GetCharacterMovement()->GravityScale = 1.75f;
|
||
|
||
// 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"));
|
||
|
||
// Setting StateManagerComponent
|
||
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
|
||
StateManagerComponent->OnStateBegin.AddUObject(this, &ACombatCharacter::CharacterStateBegin);
|
||
StateManagerComponent->OnStateEnd.AddUObject(this, &ACombatCharacter::CharacterStateEnd);
|
||
StateManagerComponent->OnActionBegin.AddUObject(this, &ACombatCharacter::CharacterActionBegin);
|
||
StateManagerComponent->OnActionEnd.AddUObject(this, &ACombatCharacter::CharacterActionEnd);
|
||
|
||
Health = 100.f;
|
||
PelvisBoneName = TEXT("pelvis");
|
||
}
|
||
|
||
void ACombatCharacter::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);
|
||
}
|
||
}
|
||
|
||
//Player<65><72> <20><><EFBFBD>⸦ ó<><C3B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ְ<EFBFBD><D6B0><EFBFBD>. <20>Ƹ<EFBFBD><C6B8><EFBFBD> <20>ӽ<EFBFBD><D3BD>ڵ<EFBFBD>
|
||
FActorSpawnParameters spawnParam;
|
||
spawnParam.Owner = this;
|
||
spawnParam.Instigator = this;
|
||
ABaseEquippable* SpawnItem = Cast<ABaseEquippable>(GetWorld()->SpawnActor(Weapon, &GetActorTransform(), spawnParam));
|
||
if (SpawnItem)
|
||
SpawnItem->OnEquipped();
|
||
}
|
||
|
||
float ACombatCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
|
||
{
|
||
float fDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
|
||
|
||
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
|
||
{
|
||
const FPointDamageEvent* PointDamageEvent = static_cast<const FPointDamageEvent*>(&DamageEvent);
|
||
|
||
CharacterTakeDamage(fDamage);
|
||
|
||
//Play Sound
|
||
UGameplayStatics::PlaySoundAtLocation(this, HitSound, PointDamageEvent->HitInfo.Location);
|
||
|
||
//Hit Effect
|
||
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitEmitter, PointDamageEvent->HitInfo.Location);
|
||
|
||
if (CanReceiveHitReaction())
|
||
{
|
||
StateManagerComponent->SetCurrentState(ECharacterState::Disable);
|
||
|
||
//Play Animation
|
||
PlayAnimMontage(HitMontage);
|
||
}
|
||
}
|
||
return fDamage;
|
||
}
|
||
|
||
void ACombatCharacter::ContinueAttack_Implementation()
|
||
{
|
||
if (CombatComponent->GetIsAttackSaved())
|
||
{
|
||
CombatComponent->SetIsAttackSaved(false);
|
||
if (StateManagerComponent->GetCurrentState() == ECharacterState::Attacking)
|
||
StateManagerComponent->SetCurrentState(ECharacterState::Nothing);
|
||
AttackEvent();
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::ResetAttack_Implementation()
|
||
{
|
||
CombatComponent->ResetAttack();
|
||
}
|
||
|
||
void ACombatCharacter::ResetCombat_Implementation()
|
||
{
|
||
CombatComponent->ResetAttack();
|
||
StateManagerComponent->ResetState();
|
||
StateManagerComponent->SetCurrentAction(ECharacterAction::Nothing);
|
||
}
|
||
|
||
FRotator ACombatCharacter::GetDesiredRotation_Implementation()
|
||
{
|
||
FVector InputVector = GetCharacterMovement()->GetLastInputVector();
|
||
if (InputVector.Equals(FVector(0, 0, 0), 0.001f))
|
||
return GetActorRotation();
|
||
else
|
||
return UKismetMathLibrary::MakeRotFromX(GetLastMovementInputVector());
|
||
}
|
||
|
||
bool ACombatCharacter::CanReceiveDamage_Implementation()
|
||
{
|
||
bool result;
|
||
result = (StateManagerComponent->GetCurrentState() != ECharacterState::Dead);
|
||
return result;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
// Input
|
||
|
||
void ACombatCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
|
||
{
|
||
// Set up action bindings
|
||
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
|
||
{
|
||
|
||
//Jumping
|
||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Jumping);
|
||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
|
||
|
||
//Moving
|
||
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Move);
|
||
|
||
//Looking
|
||
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ACombatCharacter::Look);
|
||
|
||
//Interact
|
||
EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &ACombatCharacter::Interact);
|
||
|
||
//ToggleCombat
|
||
EnhancedInputComponent->BindAction(ToggleCombatAction, ETriggerEvent::Started, this, &ACombatCharacter::ToggleCombat);
|
||
|
||
//LightAttack
|
||
EnhancedInputComponent->BindAction(LightAttackAction, ETriggerEvent::Started, this, &ACombatCharacter::LightAttack);
|
||
|
||
//Dodge
|
||
EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Started, this, &ACombatCharacter::Dodge);
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::Move(const FInputActionValue& Value)
|
||
{
|
||
// 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 ACombatCharacter::Look(const FInputActionValue& Value)
|
||
{
|
||
// input is a Vector2D
|
||
FVector2D LookAxisVector = Value.Get<FVector2D>();
|
||
|
||
if (Controller != nullptr)
|
||
{
|
||
// add yaw and pitch input to controller
|
||
AddControllerYawInput(LookAxisVector.X);
|
||
AddControllerPitchInput(LookAxisVector.Y);
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::Jumping(const FInputActionValue& Value)
|
||
{
|
||
if (!CanJumping())
|
||
return;
|
||
|
||
StopAnimMontage();
|
||
StateManagerComponent->ResetState();
|
||
CombatComponent->ResetAttack();
|
||
Super::Jump();
|
||
}
|
||
|
||
void ACombatCharacter::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 ACombatCharacter::ToggleCombat(const FInputActionValue& Value)
|
||
{
|
||
bool bInput = Value.Get<bool>();
|
||
|
||
ToggleCombatEvent();
|
||
}
|
||
|
||
void ACombatCharacter::LightAttack(const FInputActionValue& Value)
|
||
{
|
||
if (StateManagerComponent->GetCurrentState() == ECharacterState::Attacking)
|
||
CombatComponent->SetIsAttackSaved(true);
|
||
else
|
||
AttackEvent();
|
||
}
|
||
|
||
void ACombatCharacter::Dodge(const FInputActionValue& Value)
|
||
{
|
||
if (CanPerformDodge())
|
||
PerformDodge();
|
||
}
|
||
|
||
void ACombatCharacter::CharacterStateBegin(ECharacterState CharState)
|
||
{
|
||
switch (CharState)
|
||
{
|
||
case ECharacterState::Nothing:
|
||
break;
|
||
case ECharacterState::Attacking:
|
||
break;
|
||
case ECharacterState::Dodging:
|
||
break;
|
||
case ECharacterState::GeneralActionState:
|
||
break;
|
||
case ECharacterState::Dead:
|
||
PerformDeath();
|
||
break;
|
||
case ECharacterState::Disable:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::CharacterStateEnd(ECharacterState CharState)
|
||
{
|
||
switch (CharState)
|
||
{
|
||
case ECharacterState::Nothing:
|
||
break;
|
||
case ECharacterState::Attacking:
|
||
break;
|
||
case ECharacterState::Dodging:
|
||
break;
|
||
case ECharacterState::GeneralActionState:
|
||
break;
|
||
case ECharacterState::Dead:
|
||
break;
|
||
case ECharacterState::Disable:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::CharacterActionBegin(ECharacterAction CharAction)
|
||
{
|
||
switch (CharAction)
|
||
{
|
||
case ECharacterAction::Nothing:
|
||
break;
|
||
case ECharacterAction::GeneralAction:
|
||
break;
|
||
case ECharacterAction::LightAttack:
|
||
break;
|
||
case ECharacterAction::HeavyAttack:
|
||
break;
|
||
case ECharacterAction::ChargedAttack:
|
||
break;
|
||
case ECharacterAction::FallingAttack:
|
||
break;
|
||
case ECharacterAction::SprintAttack:
|
||
break;
|
||
case ECharacterAction::Dodge:
|
||
break;
|
||
case ECharacterAction::EnterCombat:
|
||
break;
|
||
case ECharacterAction::ExitCombat:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::CharacterActionEnd(ECharacterAction CharAction)
|
||
{
|
||
switch (CharAction)
|
||
{
|
||
case ECharacterAction::Nothing:
|
||
break;
|
||
case ECharacterAction::GeneralAction:
|
||
break;
|
||
case ECharacterAction::LightAttack:
|
||
break;
|
||
case ECharacterAction::HeavyAttack:
|
||
break;
|
||
case ECharacterAction::ChargedAttack:
|
||
break;
|
||
case ECharacterAction::FallingAttack:
|
||
break;
|
||
case ECharacterAction::SprintAttack:
|
||
break;
|
||
case ECharacterAction::Dodge:
|
||
break;
|
||
case ECharacterAction::EnterCombat:
|
||
break;
|
||
case ECharacterAction::ExitCombat:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::ToggleCombatEvent()
|
||
{
|
||
ABaseWeapon* baseWeapon = CombatComponent->GetMainWeapon();
|
||
if (!baseWeapon)
|
||
return;
|
||
|
||
if (!CanPerformToggleCombat())
|
||
return;
|
||
|
||
if (!CombatComponent->GetCombatEnabled())
|
||
PerformAction(ECharacterState::GeneralActionState, ECharacterAction::EnterCombat, 0);
|
||
else
|
||
PerformAction(ECharacterState::GeneralActionState, ECharacterAction::ExitCombat, 0);
|
||
}
|
||
|
||
void ACombatCharacter::AttackEvent()
|
||
{
|
||
if (!CanPerformAttack())
|
||
return;
|
||
|
||
if (CombatComponent->GetCombatEnabled())
|
||
PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount());
|
||
else
|
||
ToggleCombatEvent();
|
||
}
|
||
|
||
void ACombatCharacter::CharacterTakeDamage(float InDamage)
|
||
{
|
||
float tmp = Health - InDamage;
|
||
Health = UKismetMathLibrary::Clamp(tmp, 0, Health);
|
||
|
||
if (Health <= 0)
|
||
StateManagerComponent->SetCurrentState(ECharacterState::Dead);
|
||
}
|
||
|
||
void ACombatCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
|
||
{
|
||
if (!GetMesh())
|
||
return;
|
||
|
||
GetMesh()->SetPhysicsLinearVelocity(GetActorForwardVector() * -InitialSpeed, false, PelvisBoneName);
|
||
}
|
||
|
||
void ACombatCharacter::EnableRagdoll()
|
||
{
|
||
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None, 0); //movement <20><> <20>̻<EFBFBD> <20><><EFBFBD><EFBFBD> <20>ٲ<EFBFBD>
|
||
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore); //<2F>浹<EFBFBD><E6B5B9><EFBFBD><EFBFBD>
|
||
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore); //ī<><EFBFBD> <20>浹<EFBFBD><E6B5B9><EFBFBD><EFBFBD>
|
||
FAttachmentTransformRules rules(EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, true);
|
||
GetCameraBoom()->AttachToComponent(GetMesh(), rules, PelvisBoneName); //camera<72><61> ô<><EFBFBD><DFBB><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
GetCameraBoom()->bDoCollisionTest = false; //ī<><EFBFBD> <20>浹 <20><><EFBFBD><EFBFBD> <20><>
|
||
GetMesh()->SetCollisionProfileName(TEXT("ragdoll"), true); //ragdoll <20><> <20><><EFBFBD><EFBFBD>
|
||
GetMesh()->SetAllBodiesBelowSimulatePhysics(PelvisBoneName, true, true);
|
||
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
|
||
}
|
||
|
||
void ACombatCharacter::PerformAttack(ECharacterAction attackType, int32 attackIndex)
|
||
{
|
||
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
||
if (!CurrentWeapon)
|
||
return;
|
||
|
||
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(attackType);
|
||
int32 attackIdx = attackIndex;
|
||
|
||
if (montages.Num() <= attackIdx)
|
||
attackIdx = 0;
|
||
if (!montages.IsValidIndex(attackIdx))
|
||
return;
|
||
|
||
UAnimMontage* attackMontage = montages[attackIdx];
|
||
if (!IsValid(attackMontage))
|
||
{
|
||
FString debugStr = FString::Printf(TEXT("Index %d Is NOT VALID!!"), attackIdx);
|
||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, debugStr);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
StateManagerComponent->SetCurrentState(ECharacterState::Attacking);
|
||
StateManagerComponent->SetCurrentAction(attackType);
|
||
|
||
PlayAnimMontage(attackMontage);
|
||
|
||
int idx = attackIdx + 1;
|
||
if (idx > montages.Num())
|
||
idx = 0;
|
||
|
||
CombatComponent->SetAttackCount(idx);
|
||
}
|
||
}
|
||
|
||
//<2F><><EFBFBD>ڰ<EFBFBD><DAB0><EFBFBD> <20><><EFBFBD>߿<EFBFBD> <20>ʿ<EFBFBD><CABF>ϸ<EFBFBD> <20>߰<EFBFBD> PerformDodge(int32 dodgeIndex)
|
||
void ACombatCharacter::PerformDodge()
|
||
{
|
||
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
||
if (!CurrentWeapon)
|
||
return;
|
||
int32 montageIndex = 0;
|
||
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(ECharacterAction::Dodge);
|
||
if (montages.Num() <= montageIndex)
|
||
montageIndex = 0;
|
||
if (!montages.IsValidIndex(montageIndex))
|
||
return;
|
||
|
||
UAnimMontage* dodgeMontage = montages[montageIndex];
|
||
if (IsValid(dodgeMontage))
|
||
{
|
||
StateManagerComponent->SetCurrentState(ECharacterState::Dodging);
|
||
StateManagerComponent->SetCurrentAction(ECharacterAction::Dodge);
|
||
PlayAnimMontage(dodgeMontage);
|
||
}
|
||
else
|
||
{
|
||
FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIndex);
|
||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
|
||
}
|
||
}
|
||
|
||
bool ACombatCharacter::PerformAction(ECharacterState characterState, ECharacterAction characterAction, int32 montageIndex)
|
||
{
|
||
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
||
if (!CurrentWeapon)
|
||
return false;
|
||
|
||
TArray<UAnimMontage*> montages = CurrentWeapon->GetActionMontage(characterAction);
|
||
int32 montageIdx = montageIndex;
|
||
|
||
if (montages.Num() <= montageIdx)
|
||
montageIdx = 0;
|
||
if (!montages.IsValidIndex(montageIdx))
|
||
return false;
|
||
|
||
UAnimMontage* actionMontage = montages[montageIdx];
|
||
if (IsValid(actionMontage))
|
||
{
|
||
StateManagerComponent->SetCurrentState(characterState);
|
||
StateManagerComponent->SetCurrentAction(characterAction);
|
||
PlayAnimMontage(actionMontage);
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIdx);
|
||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void ACombatCharacter::PerformDeath()
|
||
{
|
||
EnableRagdoll();
|
||
ApplyHitReactionPhysicsVelocity(2000.f); //<2F>浹<EFBFBD><E6B5B9> <20><> <20><> <20><EFBFBD><D7B7><EFBFBD><EFBFBD>ϰ<EFBFBD> <20>ϱ<EFBFBD> <20><><EFBFBD>ؼ<EFBFBD> <20><EFBFBD><DEB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
|
||
if (IsValid(CombatComponent->GetMainWeapon()))
|
||
CombatComponent->GetMainWeapon()->SimulateWeaponPhysics(); //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>浹Ŵ
|
||
|
||
FTimerHandle deathTimer;
|
||
GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([&]()
|
||
{
|
||
if (IsValid(CombatComponent->GetMainWeapon()))
|
||
CombatComponent->GetMainWeapon()->Destroy();
|
||
this->Destroy();
|
||
}), 4.f, false); // 4<><34> <20>Ŀ<EFBFBD> object <20><><EFBFBD><EFBFBD>
|
||
}
|
||
|
||
bool ACombatCharacter::CanPerformToggleCombat()
|
||
{
|
||
bool ReturnValue = true;
|
||
|
||
TArray<ECharacterState> inputArr = {
|
||
ECharacterState::Attacking,
|
||
ECharacterState::Dodging,
|
||
ECharacterState::Dead,
|
||
ECharacterState::Disable,
|
||
ECharacterState::GeneralActionState
|
||
};
|
||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputArr);
|
||
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
||
return ReturnValue;
|
||
}
|
||
|
||
bool ACombatCharacter::CanPerformAttack()
|
||
{
|
||
bool ReturnValue = true;
|
||
TArray<ECharacterState> inputArr = {
|
||
ECharacterState::Attacking,
|
||
ECharacterState::Dodging,
|
||
ECharacterState::Dead,
|
||
ECharacterState::Disable,
|
||
ECharacterState::GeneralActionState
|
||
};
|
||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputArr);
|
||
return ReturnValue;
|
||
}
|
||
|
||
bool ACombatCharacter::CanPerformDodge()
|
||
{
|
||
bool ReturnValue = true;
|
||
TArray<ECharacterState> inputArr = {
|
||
ECharacterState::Dodging,
|
||
ECharacterState::Dead,
|
||
ECharacterState::Disable,
|
||
ECharacterState::GeneralActionState
|
||
};
|
||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputArr);
|
||
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
||
return ReturnValue;
|
||
}
|
||
|
||
bool ACombatCharacter::CanJumping()
|
||
{
|
||
bool ReturnValue = true;
|
||
TArray<ECharacterState> inputArr = {
|
||
ECharacterState::Dodging,
|
||
ECharacterState::Dead,
|
||
ECharacterState::Disable,
|
||
ECharacterState::GeneralActionState
|
||
};
|
||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputArr);
|
||
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
||
return ReturnValue;
|
||
}
|
||
|
||
bool ACombatCharacter::CanReceiveHitReaction()
|
||
{
|
||
bool ReturnValue = true;
|
||
TArray<ECharacterState> inputArr = {
|
||
ECharacterState::Dead,
|
||
};
|
||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputArr);
|
||
return ReturnValue;
|
||
}
|
||
|
||
ECharacterAction ACombatCharacter::GetDesiredAttackType()
|
||
{
|
||
if (GetCharacterMovement()->IsFalling())
|
||
return ECharacterAction::FallingAttack;
|
||
|
||
//TODO : Movement Speed Mode <20>߰<EFBFBD>
|
||
|
||
//TODO : Heavy Attack <20>߰<EFBFBD>
|
||
|
||
return ECharacterAction::LightAttack;
|
||
}
|
||
|
||
|
||
|