D1/Source/D1/AI/MasterAI.cpp

709 lines
22 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#include "AI/MasterAI.h"
#include "AIController.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "Components/CombatComponent.h"
#include "Components/StatsComponent.h"
#include "Components/StateManagerComponent.h"
#include "Definitions/CombatGameplayTags.h"
#include "Engine/DamageEvents.h"
#include "NiagaraFunctionLibrary.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Components/CollisionComponent.h"
#include "Components/TimelineComponent.h"
#include "Components/WidgetComponent.h"
#include "DamageType/AttackDamageType.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Perception/AISense_Damage.h"
AMasterAI::AMasterAI()
{
// 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));
//Setting Widget class is Editor to Blueprint
TargetingWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("TargetingWidget"));
static ConstructorHelpers::FClassFinder<UUserWidget> WidgetRef(TEXT("/Game/CombatSystem/UI/WBP_LockOn.WBP_LockOn_C"));
if(WidgetRef.Class)
{
TargetingWidgetComponent->SetWidgetClass(WidgetRef.Class);
TargetingWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);
TargetingWidgetComponent->SetDrawSize({20.f, 20.f});
TargetingWidgetComponent->SetupAttachment(GetMesh());
TargetingWidgetComponent->SetRelativeLocation(FVector(0.f, 0.f, 40.f));
TargetingWidgetComponent->SetVisibility(false);
}
// Setting CombatComponent
CombatComponent = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
// Setting StateManagerComponent
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
StateManagerComponent->OnStateBegin.AddUObject(this, &AMasterAI::CharacterStateBegin);
StateManagerComponent->OnStateEnd.AddUObject(this, &AMasterAI::CharacterStateEnd);
// Setting StatsComponent
StatsComponent = CreateDefaultSubobject<UStatsComponent>(TEXT("StatsComponent"));
StatsComponent->OnCurrentStatValueUpdated.AddUObject(this, &AMasterAI::CharacterCurrentStatValueUpdated);
StatsComponent->SetBaseStatValue(EStats::Health, 100.f);
StatsComponent->SetMaxStatValue(EStats::Health, 100.f);
StatsComponent->SetBaseStatValue(EStats::Armor, 0.f);
StatsComponent->SetMaxStatValue(EStats::Armor, 100.f);
StatsComponent->SetBaseStatValue(EStats::Damage, 25.f);
StatsComponent->SetMaxStatValue(EStats::Damage, 300.f);
StatsComponent->InitializeStats();
// Setting WeaponCollisionComponent
// Setting OnHit Func is Child Class
MainCollisionComponent = CreateDefaultSubobject<UCollisionComponent>(TEXT("MainCollisionComponent"));
MainCollisionComponent->SetTraceRaius(20.f);
MainCollisionComponent->SetStartSocketName(TEXT("WeaponStart"));
MainCollisionComponent->SetEndSocketName(TEXT("WeaponEnd"));
TArray<TEnumAsByte<EObjectTypeQuery>> InputCollisionObjectTypes;
InputCollisionObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_Pawn));
MainCollisionComponent->SetCollisionObjectTypes(InputCollisionObjectTypes);
MainCollisionComponent->SetDrawDebugType(EDrawDebugTrace::None);
FGameplayTagContainer GameplayTags(FCombatGameplayTags::Get().Character_Enemy);
MainCollisionComponent->SetGameplayTagsIgnore(GameplayTags);
SubCollisionComponent = CreateDefaultSubobject<UCollisionComponent>(TEXT("SubCollisionComponent"));
SubCollisionComponent->SetTraceRaius(20.f);
SubCollisionComponent->SetStartSocketName(TEXT("WeaponStart"));
SubCollisionComponent->SetEndSocketName(TEXT("WeaponEnd"));
TArray<TEnumAsByte<EObjectTypeQuery>> LeftInputCollisionObjectTypes;
LeftInputCollisionObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_Pawn));
SubCollisionComponent->SetCollisionObjectTypes(LeftInputCollisionObjectTypes);
SubCollisionComponent->SetDrawDebugType(EDrawDebugTrace::None);
FGameplayTagContainer LeftGameplayTags(FCombatGameplayTags::Get().Character_Enemy);
SubCollisionComponent->SetGameplayTagsIgnore(LeftGameplayTags);
//Settings OwnedGameplayTags
OwnedGameplayTags.AddTag(FCombatGameplayTags::Get().Character_Enemy);
//Setting MovementSpeed
MovementSpeedMode = EMovementSpeedMode::Jogging;
WalkingSpeed = 200.f;
JoggingSpeed = 500.f;
SprintSpeed = 700.f;
PelvisBoneName = TEXT("pelvis");
//Setting Timeline
RotateToTargetTimeLineComponent = CreateDefaultSubobject<UTimelineComponent>(TEXT("RotateToTargetTimeLineComponent"));
static ConstructorHelpers::FObjectFinder<UCurveFloat> UCurveObj(TEXT("/Game/CombatSystem/Blueprints/Timeline/RotateToTargetCurve.RotateToTargetCurve"));
if(UCurveObj.Succeeded())
CurveFloatTimeline = UCurveObj.Object;
}
void AMasterAI::BeginPlay()
{
Super::BeginPlay();
// BP의 BeginPlay가 먼저 호출됨 (ReceiveBeginPlay)
// 따라서 생성자로 올림
//StatsComponent->InitializeStats();
//Setting Timeline
FOnTimelineFloat TimelineFloatCallback;
TimelineFloatCallback.BindUFunction(this, FName("RotateToTargetUpdate"));
RotateToTargetTimeLineComponent->SetTimelineLength(0.25f);
RotateToTargetTimeLineComponent->SetLooping(false);
if(CurveFloatTimeline)
RotateToTargetTimeLineComponent->AddInterpFloat(CurveFloatTimeline, TimelineFloatCallback);
}
float AMasterAI::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float fDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
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);
APawn* Attacker = EventInstigator->GetPawn();
//스텟 관련 처리
StatsComponent->TakeDamageOnStat(Damage);
//데미지 관련 AI 처리
UAISense_Damage::ReportDamageEvent(GetWorld(), this, Attacker, Damage, PointDamageEvent->HitInfo.Location, PointDamageEvent->HitInfo.Location);
//앞에서 맞았는지 뒤에서 맞았는지 판별
bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(Attacker), -0.1f, 1.f);
LastHitInfo = PointDamageEvent->HitInfo;
//play sound, effect
ApplyImpactEffect(damageTypeClass->DamageType);
if (CanReceiveHitReaction())
ApplyHitReaction(damageTypeClass->DamageType);
}
return fDamage;
}
void AMasterAI::ContinueAttack_Implementation()
{
if (CombatComponent->GetIsAttackSaved())
{
CombatComponent->SetIsAttackSaved(false);
if (StateManagerComponent->GetCurrentState() == FCombatGameplayTags::Get().Character_State_Attacking)
StateManagerComponent->SetCurrentState(FGameplayTag::EmptyTag);
AttackEvent();
}
}
void AMasterAI::ResetAttack_Implementation()
{
CombatComponent->ResetAttack();
}
void AMasterAI::ResetCombat_Implementation()
{
CombatComponent->ResetAttack();
StateManagerComponent->ResetState();
StateManagerComponent->SetCurrentAction(FGameplayTag::EmptyTag);
}
FRotator AMasterAI::GetDesiredRotation_Implementation()
{
FVector InputVector = GetCharacterMovement()->GetLastInputVector();
if (InputVector.Equals(FVector(0, 0, 0), 0.001f))
return GetActorRotation();
else
return UKismetMathLibrary::MakeRotFromX(GetLastMovementInputVector());
}
bool AMasterAI::CanReceiveDamage_Implementation()
{
bool result = (StateManagerComponent->GetCurrentState() != FCombatGameplayTags::Get().Character_State_Dead);
result &= !bEnableIFrame;
return result;
}
void AMasterAI::SetCanMove_Implementation(bool inputCanMove)
{
bCanMove = inputCanMove;
}
void AMasterAI::ActivateCollision_Implementation(ECollisionPart CollisionPart)
{
switch (CollisionPart)
{
case ECollisionPart::MainWeapon:
MainCollisionComponent->ActivateCollision();
break;
case ECollisionPart::OffhandWeapon:
SubCollisionComponent->ActivateCollision();
break;
case ECollisionPart::RightFoot:
break;
default:
break;
}
}
void AMasterAI::DeactivateCollision_Implementation(ECollisionPart CollisionPart)
{
switch (CollisionPart)
{
case ECollisionPart::MainWeapon:
MainCollisionComponent->DeactivateCollision();
break;
case ECollisionPart::OffhandWeapon:
SubCollisionComponent->DeactivateCollision();
break;
case ECollisionPart::RightFoot:
break;
default:
break;
}
}
void AMasterAI::SetIFrame_Implementation(bool InputEnableIFrame)
{
bEnableIFrame = InputEnableIFrame;
}
void AMasterAI::RotateToTarget_Implementation()
{
RotateToTargetTL();
}
float AMasterAI::PerformAction(FGameplayTag ActionTag, FGameplayTag StateTag, int32 MontageIndex, bool bRandomIndex)
{
TArray<UAnimMontage*> montages = 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 %n is NOT VALID!!"), montageIdx);
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
}
return actionDuration;
}
float AMasterAI::PerformAttack(FGameplayTag AttackType, int32 AttackIndex, bool bRandomIndex)
{
TArray<UAnimMontage*> montages = 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 AMasterAI::CanBeTargeted()
{
return (StateManagerComponent->GetCurrentState() != FCombatGameplayTags::Get().Character_State_Dead);
}
void AMasterAI::OnTargeted(bool bIsTargeted)
{
TargetingWidgetComponent->SetVisibility(bIsTargeted);
}
void AMasterAI::OnTargetSet(AActor* NewTarget)
{
TargetActor = NewTarget;
}
UPrimitiveComponent* AMasterAI::GetWeaponMesh()
{
return nullptr;
}
void AMasterAI::SetMovementSpeedMode(EMovementSpeedMode NewSpeedMode)
{
if (NewSpeedMode == MovementSpeedMode)
return;
MovementSpeedMode = NewSpeedMode;
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 AMasterAI::CharacterStateBegin(FGameplayTag CharState)
{
if (FGameplayTag::EmptyTag == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
{/*None*/}
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 AMasterAI::CharacterStateEnd(FGameplayTag CharState)
{
if (FGameplayTag::EmptyTag == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
{
StopRotateToTargetTL();
}
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 AMasterAI::CharacterCurrentStatValueUpdated(EStats statType, float value)
{
if (!(statType == EStats::Health) || value > 0.f)
return;
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Dead);
}
void AMasterAI::AttackEvent()
{
if (!CanPerformAttack())
return;
if (CombatComponent->GetCombatEnabled())
PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount(), false);
}
void AMasterAI::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
{
if (!GetMesh())
return;
GetMesh()->SetPhysicsLinearVelocity(GetActorForwardVector() * -InitialSpeed, false, PelvisBoneName);
}
void AMasterAI::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);
GetMesh()->SetCollisionProfileName(TEXT("ragdoll"), true); //ragdoll 로 변경
GetMesh()->SetAllBodiesBelowSimulatePhysics(PelvisBoneName, true, true);
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
}
void AMasterAI::DisableSprint()
{
UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
if (World)
World->GetTimerManager().ClearTimer(StaminaTimerHandle);
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
SetMovementSpeedMode(EMovementSpeedMode::Jogging);
}
void AMasterAI::SprintStaminaCost()
{
if (!CanPerformSprint())
{
DisableSprint();
return;
}
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -2.f);
if (StatsComponent->GetCurrentStatValue(EStats::Stamina) < 10.f)
{
DisableSprint();
return;
}
}
void AMasterAI::ApplyHitReaction(EDamageType InDamageType)
{
switch (InDamageType)
{
case EDamageType::None:
PerformHitStun();
break;
case EDamageType::MeleeDamage:
PerformHitStun();
break;
case EDamageType::KnockdownDamage:
PerformKnockdown();
break;
default:
break;
}
}
void AMasterAI::ApplyImpactEffect(EDamageType InDamageType)
{
//Play Sound
UGameplayStatics::PlaySoundAtLocation(this, HitSound, LastHitInfo.Location);
//Hit Effect
UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), HitEmitter, LastHitInfo.Location);
}
void AMasterAI::RotateToTargetTL()
{
RotateToTargetTimeLineComponent->PlayFromStart();
}
void AMasterAI::StopRotateToTargetTL()
{
RotateToTargetTimeLineComponent->Stop();
}
void AMasterAI::RotateToTargetUpdate(float Value)
{
if(!IsValid(TargetActor))
return;
const FRotator CurrentRotator = GetActorRotation();
FVector StartVector = GetActorLocation();
FVector TargetVector = TargetActor->GetActorLocation();
FRotator TargetRotator = UKismetMathLibrary::FindLookAtRotation(StartVector, TargetVector);
UWorld* WorldPointer = GetWorld();
if(!WorldPointer)
return;
FRotator NewRotator = FMath::Lerp<FRotator, float>(CurrentRotator, TargetRotator, Value);
NewRotator.Roll = CurrentRotator.Roll;
NewRotator.Pitch = CurrentRotator.Pitch;
SetActorRotation(NewRotator);
}
void AMasterAI::PerformDeath()
{
if(IsDead())
return;
SetDead(true);
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);
GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
FTimerHandle deathTimer;
GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([&]()
{
this->Destroy();
}), 4.f, false); // 4초 후에 object 삭제
}
bool AMasterAI::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 AMasterAI::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 AMasterAI::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 AMasterAI::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 AMasterAI::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 AMasterAI::CanReceiveHitReaction()
{
bool ReturnValue = true;
FGameplayTagContainer inputContainer;
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
ReturnValue &= !IsDead();
return ReturnValue;
}
bool AMasterAI::CanPerformSprint()
{
return (FMath::IsNearlyEqual(GetVelocity().Length(), 0.f)) == false;
}
FGameplayTag AMasterAI::GetDesiredAttackType()
{
if (GetCharacterMovement()->IsFalling())
return FCombatGameplayTags::Get().Character_Action_Attack_FallingAttack;
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
return FCombatGameplayTags::Get().Character_Action_Attack_SprintAttack;
return FCombatGameplayTags::Get().Character_Action_Attack_LightAttack;
}
TArray<UAnimMontage*> AMasterAI::GetActionMontage(FGameplayTag characterAction)
{
TArray<UAnimMontage*> outputArr;
if (FCombatGameplayTags::Get().Character_Action_Attack_CloseRange == characterAction)
outputArr = CloseRangeAttackMontage;
else if (FCombatGameplayTags::Get().Character_Action_Attack_MediumRange == characterAction)
outputArr = MediumRangeAttackMontage;
else if (FCombatGameplayTags::Get().Character_Action_Attack_RareAttack == characterAction)
outputArr = RareAttackMontage;
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);
else if (FCombatGameplayTags::Get().Character_Action_Intro == characterAction)
outputArr.Add(Intro);
return outputArr;
}
void AMasterAI::SetDead(bool Condition)
{
if(bIsDead == Condition)
return;
bIsDead = Condition;
AAIController* aiController = Cast<AAIController>(GetController());
if(aiController)
{
UBlackboardComponent* BlackboardComponent = aiController->GetBlackboardComponent();
if(BlackboardComponent)
BlackboardComponent->SetValueAsBool(TEXT("bIsDead"), bIsDead);
}
}
bool AMasterAI::IsDead()
{
return bIsDead;
}