// Fill out your copyright notice in the Description page of Project Settings. #include "SPNpcAgent.h" #include "NavigationSystem.h" #include "NiagaraComponent.h" #include "Animations/SPNpcAgentAnimInstance.h" #include "Components/CapsuleComponent.h" #include "Components/WidgetComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "Kismet/KismetMathLibrary.h" #include "ProjectT/ProjectT.h" #include "ProjectT/Data/Gen/GenerateEnumDataTableKey.h" #include "ProjectT/Data/Gen/GenerateEnumEffectSubTypes.h" #include "ProjectT/Data/Gen/GenerateEnumSkillTypes.h" #include "ProjectT/Data/Gen/GenerateStructEffect.h" #include "ProjectT/System/Core/Characters/Player/CorePlayerCharacter.h" #include "ProjectT/System/Core/Characters/NPC/AI/Controllers/AIControllerBase.h" #include "ProjectT/System/Core/Characters/NPC/AI/Controllers/AIPerceptionController.h" #include "ProjectT/System/Core/Characters/NPC/AI/Controllers/AISightController.h" #include "ProjectT/System/Core/Characters/Skill/CoreSkill.h" #include "ProjectT/System/Core/Common/AssetUtilsLibrary.h" #include "ProjectT/System/Core/Common/GlobalUtilsLibrary.h" #include "ProjectT/System/Core/Components/ObstacleFilterComponent.h" #include "ProjectT/System/Core/Components/PathFollowComponent.h" #include "ProjectT/System/Core/Components/VisionConeComponent.h" #include "ProjectT/System/Core/GameModes/WorldGameMode.h" #include "ProjectT/System/Core/Managers/CoreCheatManager.h" #include "ProjectT/System/Core/Managers/PooledActorManager.h" #include "ProjectT/System/SinglePlay/Components/SPNpcStatusComponent.h" #include "ProjectT/System/SinglePlay/GameModes/SPGameModeBase.h" #include "ProjectT/System/SinglePlay/Interfaces/SPObjectProvider.h" #include "ProjectT/System/SinglePlay/Managers/SPEnemyVisionManager.h" ASPNpcAgent::ASPNpcAgent() : CurrentSpeed(ENpcSpeedType::Walk), SkillType(ESkillTypes::None), ChaseRangeProfileName(TEXT("ChaseRange")), DissolveParameterName(TEXT("Dissolve")), SkillCoolTime(0.f), DissolveElapsedTime(0.f), DissolveDuration(1.5f), bRotateWhileAttack(false), bSkillCooldownActive(false), bDissolving(false), CurrentUsingSkill(nullptr), AgentAnimInstance(nullptr), NpcActionMontage(nullptr) { PrimaryActorTick.bCanEverTick = true; AIControllerClass = AAISightController::StaticClass(); GetCapsuleComponent()->SetCollisionProfileName(TEXT("Enemy")); GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint); GetMesh()->SetAnimInstanceClass(USPNpcAgentAnimInstance::StaticClass()); GetMesh()->SetCustomDepthStencilValue(1); WeaponStaticComponent->SetCustomDepthStencilValue(1); WeaponStaticComponent->SetCustomDepthStencilValue(1); HpBarWidgetComponent = CreateDefaultSubobject(TEXT("HpBarWidgetComponent")); DetectIndicatorWidget = CreateDefaultSubobject(TEXT("DetectIndicatorWidget")); SphereChaseRangeComponent = CreateDefaultSubobject(TEXT("SphereChaseRangeComponent")); DissolveFXComponent = CreateDefaultSubobject(TEXT("DissolveFXComponent")); VisionLineMeshComponent = CreateDefaultSubobject(TEXT("VisionLineMeshComponent")); NMT_CHECKF(HpBarWidgetComponent && DetectIndicatorWidget && SphereChaseRangeComponent && DissolveFXComponent && VisionLineMeshComponent) HpBarWidgetComponent->SetupAttachment(WidgetRootComponent); HpBarWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen); DetectIndicatorWidget->SetupAttachment(WidgetRootComponent); DetectIndicatorWidget->SetWidgetSpace(EWidgetSpace::Screen); SphereChaseRangeComponent->SetupAttachment(SubRootComponent); SphereChaseRangeComponent->SetCollisionProfileName(ChaseRangeProfileName); SphereChaseRangeComponent->SetLineThickness(1.f); DissolveFXComponent->SetupAttachment(SubRootComponent); DissolveFXComponent->bAutoActivate = false; VisionLineMeshComponent->SetupAttachment(SubRootComponent); VisionLineMeshComponent->SetRelativeLocation(FVector(0, 0, -GetCapsuleComponent()->GetScaledCapsuleHalfHeight() - 1.f)); VisionLineMeshComponent->SetHiddenInGame(true); VisionLineMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); VisionLineMeshComponent->SetComponentTickEnabled(false); } void ASPNpcAgent::PostInitializeComponents() { Super::PostInitializeComponents(); } void ASPNpcAgent::BeginPlay() { Super::BeginPlay(); InitializeMaterials(); } void ASPNpcAgent::InitializeStatus(const FEnemyNpc InNpcStatus) { Super::InitializeStatus(InNpcStatus); GetCharacterMovement()->MaxWalkSpeed = InNpcStatus.fNpcMove; NMT_CHECKF(VisionConeComponent && NpcStatusComponent);; VisionConeComponent->SetupSightSettings(NpcStatusComponent->GetStatusNpcSightLength(), NpcStatusComponent->GetStatusNpcSightAngle()); } void ASPNpcAgent::Respawn() { Super::Respawn(); GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking); AgentAnimInstance->ChangeNpcNormalAnimState(ENpcNormalAnimState::Idle); } void ASPNpcAgent::ExecuteClickedEvent(UPrimitiveComponent* InTouchedComponent, FKey InButtonPressed) { if(InButtonPressed == EKeys::RightMouseButton) { UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); NMT_CHECKF(World) ASPGameModeBase* SPGM = UGlobalUtilsLibrary::GetGameModeChecked(World); NMT_CHECKF(SPGM) USPEnemyVisionManager* EM = ISPObjectProvider::Execute_GetEnemyVisionManager(SPGM); NMT_CHECKF(EM) EM->RegisterFocused(this); } } void ASPNpcAgent::ExecuteBeginCursorOverEvent(UPrimitiveComponent* InTouchedComponent) { GetMesh()->SetRenderCustomDepth(true); WeaponStaticComponent->SetRenderCustomDepth(true); WeaponDynamicComponent->SetRenderCustomDepth(true); } void ASPNpcAgent::ExecuteEndCursorOverEvent(UPrimitiveComponent* InTouchedComponent) { GetMesh()->SetRenderCustomDepth(false); WeaponStaticComponent->SetRenderCustomDepth(false); WeaponDynamicComponent->SetRenderCustomDepth(false); } void ASPNpcAgent::ResetStateAfterGameEnd() { Super::ResetStateAfterGameEnd(); StopSkillActionMontage(); FinishCurrentSkill(); FinishCheckInChaseRange(); } #if WITH_EDITOR void ASPNpcAgent::ApplyCharacterData(const FTableRowBase* RowData) { Super::ApplyCharacterData(RowData); } #endif void ASPNpcAgent::Tick(float DeltaTime) { Super::Tick(DeltaTime); if(bSkillCooldownActive) { UpdateSkillCooldown(DeltaTime); } if(bDissolving) { UpdateDissolve(DeltaTime); } if(CurrentVisionMode == EVisionMode::Line) { UpdateVisionLineLength(); } } void ASPNpcAgent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); StopSkillActionMontage(); UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); World->GetTimerManager().ClearTimer(CheckChaseRangeTimerHandle); World->GetTimerManager().ClearTimer(JumpBackTimerHandle); World->GetTimerManager().ClearTimer(EffectTimerHandle); } bool ASPNpcAgent::GetUseSight_Implementation() { return true; } bool ASPNpcAgent::GetUseHearing_Implementation() { return true; } FSightInfo ASPNpcAgent::GetSightInfo_Implementation() { NMT_CHECKF(NpcStatusComponent) bool bNpcDataValid = !NpcStatusComponent->GetCurrentNpcStatus().IsDefault(); if(!NMT_ENSURE(bNpcDataValid)) return FSightInfo(); FSightInfo Info; Info.SightRange = NpcStatusComponent->GetStatusNpcSightLength(); Info.SightAngle = NpcStatusComponent->GetStatusNpcSightAngle(); Info.LoseSightRange = Info.SightRange + LoseSightOffset; Info.SightMaxAge = PerceptionMaxAge; return Info; } class UBlackboardData* ASPNpcAgent::GetBlackboardData_Implementation() { return Blackboard; } class UBehaviorTree* ASPNpcAgent::GetBehaviorTree_Implementation() { return BehaviorTree; } void ASPNpcAgent::DetectTargetBySight_Implementation(AActor* InTarget, FAIStimulus InStimulus) { switch(CurrentState) { case ENpcState::Default: { ChangeDoubtState(EDoubtState::Increasing); ChangeNpcState(ENpcState::Doubt); break; } case ENpcState::Guard: case ENpcState::Returning: { ChangeNpcState(ENpcState::Chase); ApplyStateToQuest(ENpcState::Chase); break; } case ENpcState::Doubt: break; case ENpcState::Chase: break; case ENpcState::Attack: break; case ENpcState::Hit: break; case ENpcState::Dead: break; default : NMT_LOG("Not Valid Type") } } void ASPNpcAgent::ForgetTarget_Implementation(AActor* InTarget) { switch(CurrentState) { case ENpcState::Doubt: { GetAIControllerBase()->UpdateTargetInfo(nullptr); ChangeNpcState(ENpcState::Default); break; } case ENpcState::Default: break; case ENpcState::Guard: break; case ENpcState::Returning: break; case ENpcState::Chase: break; case ENpcState::Attack: break; case ENpcState::Hit: break; case ENpcState::Dead : break; default : NMT_LOG("Not Valid Type") } } FTransform ASPNpcAgent::GetViewTransform_Implementation() { FTransform Transform; Transform.SetLocation(SubRootComponent->GetComponentLocation()); Transform.SetRotation(SubRootComponent->GetComponentRotation().Quaternion()); Transform.SetScale3D(FVector(1, 1, 1)); return Transform; } void ASPNpcAgent::InitializeProperty() { Super::InitializeProperty(); AgentAnimInstance = Cast(GetMesh()->GetAnimInstance()); NMT_CHECKF(AgentAnimInstance); AgentAnimInstance->SetActionMontage(NpcActionMontage); NMT_CHECKF(SphereChaseRangeComponent && NpcStatusComponent); FObstacleFilterInfo FilterInfo; FilterInfo.TraceCollisionChannels.Add(ECC_GameTraceChannel4); // NOTE : Wall FilterInfo.TraceCollisionChannels.Add(ECC_GameTraceChannel6); // NOTE : Prop FilterInfo.SphereRadius = NpcStatusComponent->GetStatusNpcChaseLength(); FilterInfo.CheckOutOfRange = true; SphereChaseRangeComponent->InitializeProperty(FilterInfo); SphereChaseRangeComponent->DeactivateFiltering(); SetSkillClass(); } void ASPNpcAgent::ApplyDamageEvent_Implementation(AActor* InOccurActor, const FVector& InOriginLocation, const float InDamage, const TArray& InSkillEffects) { UCoreCheatManager* CCM = UGlobalUtilsLibrary::GetSharedCoreCheatManager(this); float LocFinalDamage = CCM->GetDebugStatus().bDebugPlayerDamage? InDamage + 1000.f : InDamage; if(NpcStatusComponent->ApplyDamage(LocFinalDamage)) { ChangeNpcState(ENpcState::Dead); } else { if(CCM->GetDebugStatus().bDebugEnemyInvincibleStatusEffect) return; if(InDamage > 0.f) { HpBarWidgetComponent->GetUserWidgetObject()->SetVisibility(ESlateVisibility::SelfHitTestInvisible); GetAIControllerBase()->UpdateTargetInfo(InOccurActor); } for(FEffect EffectType : InSkillEffects) { const EEffectSubTypes EffectSubType = UGlobalUtilsLibrary::GetStringToEnum(EffectType.sEffect); if(CheckEffectTime(EffectType)) { switch(EffectSubType) { case EEffectSubTypes::None: { ChangeNpcState(ENpcState::Chase); break; } case EEffectSubTypes::Hitback: { ChangeNpcState(ENpcState::Hit); if(!NpcStatusComponent->IsDead()) AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::HitBack); break; } case EEffectSubTypes::Jumpback: { ChangeNpcState(ENpcState::Hit); JumpBack(InOriginLocation); break; } case EEffectSubTypes::Slow: { if(InDamage > 0.f && CurrentState != ENpcState::Attack) { SetReturnLocationFromDefault(); ChangeNpcState(ENpcState::Chase); } StatusSlow(EffectType.fEffectValue); break; } case EEffectSubTypes::Fast: { if(InDamage > 0.f && CurrentState != ENpcState::Attack) { SetReturnLocationFromDefault(); ChangeNpcState(ENpcState::Chase); } StatusFast(EffectType.fEffectValue); break; } case EEffectSubTypes::Bind: { if(InDamage > 0.f && CurrentState != ENpcState::Attack) { SetReturnLocationFromDefault(); ChangeNpcState(ENpcState::Chase); } ChangeSpeed(ENpcSpeedType::Stop, true); break; } case EEffectSubTypes::Unrecog: { SetUnDetectTarget(true); break; } case EEffectSubTypes::CogImmune: NOT_IMPLEMENTED_MSG("EEffectSubTypes::CogImmune"); break; case EEffectSubTypes::Stun: NOT_IMPLEMENTED_MSG("EEffectSubTypes::Stun"); break; case EEffectSubTypes::Surge: NOT_IMPLEMENTED_MSG("EEffectSubTypes::Surge"); break; case EEffectSubTypes::HitbackImmune: NOT_IMPLEMENTED_MSG("EEffectSubTypes::HitbackImmune"); break; case EEffectSubTypes::Heal: NOT_IMPLEMENTED_MSG("EEffectSubTypes::Heal"); break; default: NMT_LOG("Not Valid"); } AddEffectTimeMap(EffectType); } } } } void ASPNpcAgent::ApplyRecoveryEvent_Implementation(const TArray& InReleaseEffects) { for(FEffect LocCurEffect : InReleaseEffects) { EEffectSubTypes LoCurReleaseEffect = UGlobalUtilsLibrary::GetStringToEnum(LocCurEffect.sEffect); EffectTimeMap.Remove(LoCurReleaseEffect); if(EffectTimeMap.IsEmpty()) { GetWorld()->GetTimerManager().ClearTimer(EffectTimerHandle); } switch(LoCurReleaseEffect) { case EEffectSubTypes::Unrecog: { SetUnDetectTarget(false); break; } case EEffectSubTypes::Surge: { if(CurrentState == ENpcState::Default) { ChangeNpcState(ENpcState::Returning); } break; } case EEffectSubTypes::Bind: { if(CurrentState == ENpcState::Returning || CurrentState == ENpcState::Chase || CurrentState == ENpcState::Attack) { ChangeSpeed(ENpcSpeedType::Run); } else { ChangeSpeed(ENpcSpeedType::Walk); } break; } case EEffectSubTypes::Fast: { StatusFast(LocCurEffect.fEffectValue, true); break; } case EEffectSubTypes::Slow: { StatusSlow(LocCurEffect.fEffectValue, true); break; } case EEffectSubTypes::Hitback: NOT_IMPLEMENTED_MSG("Release EEffectSubTypes::Hitback"); break; case EEffectSubTypes::Jumpback: NOT_IMPLEMENTED_MSG("Release EEffectSubTypes::Jumpback"); break; case EEffectSubTypes::CogImmune: NOT_IMPLEMENTED_MSG("Release EEffectSubTypes::CogImmune"); break; case EEffectSubTypes::None: break; case EEffectSubTypes::Stun: break; case EEffectSubTypes::HitbackImmune: break; case EEffectSubTypes::Heal: break; default: NMT_LOG("No Release Effect Type") } } } void ASPNpcAgent::StartDisableNpc() { Super::StartDisableNpc(); DissolveFXComponent->Activate(true); bDissolving = true; NotifyVisionDetection(false); ChangeSpeed(ENpcSpeedType::Stop, true); ClearSkillCoolTime(); FinishCurrentSkill(); FinishCheckInChaseRange(); } void ASPNpcAgent::CompleteDisableNpc() { Super::CompleteDisableNpc(); AgentAnimInstance->ResetAnimState(); for(UMeshComponent* MeshComponent : AllMeshComponents) { if(MeshComponent == nullptr) continue; MeshComponent->SetScalarParameterValueOnMaterials(DissolveParameterName, 1.f); } } class USPNpcAgentAnimInstance* ASPNpcAgent::GetAgentAnimInstance() const { NMT_CHECKF(AgentAnimInstance); return AgentAnimInstance; } void ASPNpcAgent::ChangeNpcState(const ENpcState InState) { PrevState = CurrentState; if(CurrentState != InState) { switch(InState) { case ENpcState::Default: { bCanRotateHeadToTarget = true; if(CurrentDoubtState == EDoubtState::Increasing) ChangeDoubtState(EDoubtState::Decreasing); break; } case ENpcState::Doubt: { NotifyVisionDetection(true); AgentAnimInstance->SetNormalState(true); AgentAnimInstance->ChangeNpcNormalAnimState(ENpcNormalAnimState::Idle); break; } case ENpcState::Chase: { if(GetAIControllerBase()->GetTargetActor()) StartCheckInChaseRange(); ChangeDoubtState(EDoubtState::None); NotifyVisionDetection(false); break; } case ENpcState::Attack: { if(GetAIControllerBase()->GetTargetActor()) StartCheckInChaseRange(); NotifyVisionDetection(false); break; } case ENpcState::Guard: { bCanTriggerAlert = true; ChangeSpeed(ENpcSpeedType::Stop); AgentAnimInstance->SetNormalState(true); AgentAnimInstance->ChangeNpcNormalAnimState(ENpcNormalAnimState::Idle); break; } case ENpcState::Hit: { SetReturnLocationFromDefault(); StopSkillActionMontage(); ClearSkillCoolTime(); GetAIControllerBase()->SetEnableAttack(true); FinishCurrentSkill(); ChangeSpeed(ENpcSpeedType::Stop, true); SetVisionMode(EVisionMode::None); break; } case ENpcState::Dead: { ChangeSpeed(ENpcSpeedType::Stop, true); AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::DeadStart); ApplyStateToQuest(ENpcState::Dead); HpBarWidgetComponent->GetUserWidgetObject()->SetVisibility(ESlateVisibility::Collapsed); break; } case ENpcState::Returning: { break; } default: NMT_LOG("Not Valid ENpcState") } } } void ASPNpcAgent::ActivateCharacter(const bool InbEnable) { Super::ActivateCharacter(InbEnable); DetectIndicatorWidget->SetVisibility(InbEnable); if(InbEnable) GetAIControllerBase()->SetCanRotateWhileAttack(bRotateWhileAttack); } void ASPNpcAgent::SetDetectTargetFromNoise(AActor* InTarget) { Super::SetDetectTargetFromNoise(InTarget); } void ASPNpcAgent::SetDetectLocationFromNoise(const FVector& InTargetLocation) { Super::SetDetectLocationFromNoise(InTargetLocation); } void ASPNpcAgent::ChangeSpeed(const ENpcSpeedType InSpeed, const bool InbStopImmediately) { float Speed = 0.f; switch(InSpeed) { case ENpcSpeedType::Walk: { Speed = NpcStatusComponent->GetStatusNpcMove(); break; } case ENpcSpeedType::Run: { Speed = NpcStatusComponent->GetStatusNpcRun(); break; } case ENpcSpeedType::Stop: { if(InbStopImmediately) GetCharacterMovement()->StopMovementImmediately(); if(UPathFollowComponent* PathFollowComponent = FindComponentByClass()) PathFollowComponent->StopMove(); break; } default: NMT_LOG("Not Valid Type") } CurrentSpeed = InSpeed; EffectTimeMap.Find(EEffectSubTypes::Bind) ? GetCharacterMovement()->MaxWalkSpeed = 0.f : GetCharacterMovement()->MaxWalkSpeed = Speed; } class ACoreSkill* ASPNpcAgent::GetCurrentSkill() const { return CurrentUsingSkill; } void ASPNpcAgent::GetSkillClass(TSubclassOf& OutSkillClass) const { OutSkillClass = NpcSkillClass; } void ASPNpcAgent::UpdateDissolve(const float InDeltaSeconds) { DissolveElapsedTime += InDeltaSeconds; float Alpha = FMath::Clamp(1.f - (DissolveElapsedTime / DissolveDuration), 0.f, 1.f); if(FMath::IsNearlyZero(Alpha)) { bDissolving = false; DissolveElapsedTime = 0.f; CompleteDisableNpc(); } else { for(UMeshComponent* MeshComponent : AllMeshComponents) { if(MeshComponent == nullptr) continue; MeshComponent->SetScalarParameterValueOnMaterials(DissolveParameterName, Alpha); } } } void ASPNpcAgent::NotifyVisionDetection(const bool InbDetect) { UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); NMT_CHECKF(World) ASPGameModeBase* SPGM = UGlobalUtilsLibrary::GetGameModeChecked(World); NMT_CHECKF(SPGM) USPEnemyVisionManager* EM = ISPObjectProvider::Execute_GetEnemyVisionManager(SPGM); if(EM) { InbDetect ? EM->RegisterDetection(this) : EM->UnregisterDetection(this); } } void ASPNpcAgent::ShowVisual_Implementation() { } void ASPNpcAgent::HideVisual_Implementation() { } void ASPNpcAgent::UpdateVisionLineLength() { AActor* Target = GetAIControllerBase()->GetTargetActor(); if(!Target) return; NMT_CHECKF(VisionLineMeshComponent) FVector Start = VisionLineMeshComponent->GetComponentLocation(); FVector End = Target->GetActorLocation(); float Distance = FVector::Dist2D(Start, End); float ScaleX = Distance / 100.f; FRotator LookRotator = UKismetMathLibrary::FindLookAtRotation(Start, End); VisionLineMeshComponent->SetRelativeScale3D(FVector(ScaleX, 1.f, 1.f)); VisionLineMeshComponent->SetWorldRotation(LookRotator); } void ASPNpcAgent::PlayActionMontage(FName InSectionName, FOnMontageBlendingOutStarted InEndedFunction) { NMT_CHECKF(AgentAnimInstance) if(!NMT_ENSURE(NpcActionMontage)) return; AgentAnimInstance->SetActionState(true); AgentAnimInstance->Montage_Play(NpcActionMontage, 1, EMontagePlayReturnType::MontageLength, 0, true); if(AgentAnimInstance->GetInstanceForMontage(NpcActionMontage)) { FAnimMontageInstance* MontageInstance = AgentAnimInstance->GetInstanceForMontage(NpcActionMontage); if(MontageInstance && MontageInstance->OnMontageBlendingOutStarted.IsBound()) { MontageInstance->OnMontageBlendingOutStarted.Unbind(); } } AgentAnimInstance->Montage_SetBlendingOutDelegate(InEndedFunction); AgentAnimInstance->Montage_JumpToSection(InSectionName); } void ASPNpcAgent::TryAttack() { if(!IsFinishSkillCoolDown()) { NMT_LOGF("Skill CoolDown.. : %f", SkillCoolTime) return; } UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); AWorldGameMode* LocGameMode = UGlobalUtilsLibrary::GetGameModeChecked(World); UPooledActorManager* LocPoolManager = IWorldObjectProvider::Execute_GetPooledActorManager(LocGameMode); if(!LocPoolManager) return; AgentAnimInstance->ChangeNpcNormalAnimState(ENpcNormalAnimState::Idle); ChangeSpeed(ENpcSpeedType::Stop, true); CurrentUsingSkill = Cast(LocPoolManager->Obtain(NpcSkillClass)); if(CurrentUsingSkill) { CurrentUsingSkill->SetSkillOwner(this); StartAttack(); GetAIControllerBase()->SetEnableAttack(false); SetHeadRotationEnable(false); return; } NMT_MSG_ENSURE(0, "Fail to obtain Npc Skill"); } void ASPNpcAgent::StartAttack() { FSkill SkillData = CurrentUsingSkill->GetSkillData(); ActionMontageSectionName = SkillData.sSkillName; switch(SkillType) { case ESkillTypes::Melee: { CurrentUsingSkill->StartSkill(); SetSkillCoolTime(SkillData.fCooldown); PlayActionMontage(FName(*ActionMontageSectionName), FOnMontageBlendingOutStarted::CreateWeakLambda(this, [this](UAnimMontage* Montage, bool Interrupted) { TryFinishAttack(); })); } break; case ESkillTypes::Direct: { if(PrevState == ENpcState::Attack) { ContinueAttack(); return; } CurrentUsingSkill->StartSkill(); ActionMontageSectionName.Append("Draw"); PlayActionMontage(FName(*ActionMontageSectionName), FOnMontageBlendingOutStarted::CreateWeakLambda(this, [this](UAnimMontage* Montage, bool Interrupted) { ContinueAttack(); })); } break; case ESkillTypes::None: NOT_IMPLEMENTED_MSG("StartAttack::None") break; case ESkillTypes::Buff: NOT_IMPLEMENTED_MSG("StartAttack::None") break; case ESkillTypes::Indirect: NOT_IMPLEMENTED_MSG("StartAttack::None") break; default : NMT_LOG("Not Valid Type") } } void ASPNpcAgent::ContinueAttack() { if(CurrentUsingSkill == nullptr) return; FSkill SkillData = CurrentUsingSkill->GetSkillData(); ActionMontageSectionName = SkillData.sSkillName; switch(SkillType) { case ESkillTypes::Direct: { CurrentUsingSkill->ActivateSkill(); SetSkillCoolTime(SkillData.fCooldown); ActionMontageSectionName.Append("Shoot"); PlayActionMontage(FName(*ActionMontageSectionName)); } break; case ESkillTypes::Melee: NOT_IMPLEMENTED_MSG("ContinueAttack::Melee") break; case ESkillTypes::None: NOT_IMPLEMENTED_MSG("ContinueAttack::None") break; case ESkillTypes::Buff: NOT_IMPLEMENTED_MSG("ContinueAttack::None") break; case ESkillTypes::Indirect: NOT_IMPLEMENTED_MSG("ContinueAttack::None") break; default : NMT_LOG("Not Valid Type") } } void ASPNpcAgent::TryFinishAttack() { NMT_LOG("TryFinishAttack") switch(SkillType) { case ESkillTypes::Melee: { GetAIControllerBase()->SetEnableAttack(true); FinishCurrentSkill(); ShouldContinueAttack() ? ChangeNpcState(ENpcState::Attack) : ChangeNpcState(ENpcState::Chase); } break; case ESkillTypes::Direct: { if(CurrentUsingSkill == nullptr) { GetAIControllerBase()->SetEnableAttack(true); return; } FSkill SkillData = CurrentUsingSkill->GetSkillData(); ActionMontageSectionName = SkillData.sSkillName; if(ShouldContinueAttack()) { NMT_LOG("Attack Again") GetAIControllerBase()->SetEnableAttack(true); FinishCurrentSkill(); ChangeNpcState(ENpcState::Attack); } else { ActionMontageSectionName.Append("Undraw"); PlayActionMontage(FName(*ActionMontageSectionName), FOnMontageBlendingOutStarted::CreateWeakLambda(this, [this](UAnimMontage* Montage, bool Interrupted) { FinishAttack(); })); } } break; case ESkillTypes::None: NOT_IMPLEMENTED_MSG("ESkillTypes::None") break; case ESkillTypes::Buff: NOT_IMPLEMENTED_MSG("ESkillTypes::None") break; case ESkillTypes::Indirect: NOT_IMPLEMENTED_MSG("ESkillTypes::None") break; default : NMT_LOG("Not Valid Type") } } void ASPNpcAgent::FinishAttack() { FinishCurrentSkill(); if(!GetController()) return; GetAIControllerBase()->SetEnableAttack(true); if(!ShouldContinueAttack()) ChangeNpcState(ENpcState::Chase); } void ASPNpcAgent::SetUnDetectTarget(const bool InbUnDetect) { Super::SetUnDetectTarget(InbUnDetect); GetAIPerceptionController()->SetUnDetect(bUnDetect); if(bUnDetect) { if(CurrentVisionMode == EVisionMode::Cone) { UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); NMT_CHECKF(World) ASPGameModeBase* SPGM = UGlobalUtilsLibrary::GetGameModeChecked(World); NMT_CHECKF(SPGM) USPEnemyVisionManager* EM = ISPObjectProvider::Execute_GetEnemyVisionManager(SPGM); NMT_CHECKF(EM) EM->IsFocusedNpc(this) ? EM->UnregisterFocused() : EM->UnregisterDetection(this); } } else { GetAIPerceptionController()->ForceUpdatePerception(); } } void ASPNpcAgent::SetVisionMode(EVisionMode InVisionMode) { CurrentVisionMode = InVisionMode; NMT_CHECKF(VisionConeComponent && VisionLineMeshComponent) switch(InVisionMode) { case EVisionMode::None: { VisionConeComponent->ToggleDraw(false); VisionLineMeshComponent->SetHiddenInGame(true); VisionLineMeshComponent->SetComponentTickEnabled(false); break; } case EVisionMode::Cone: { VisionConeComponent->ToggleDraw(true); VisionLineMeshComponent->SetHiddenInGame(true); VisionLineMeshComponent->SetComponentTickEnabled(false); break; } case EVisionMode::Line: { VisionConeComponent->ToggleDraw(false); VisionLineMeshComponent->SetComponentTickEnabled(true); VisionLineMeshComponent->SetHiddenInGame(false); break; } } } void ASPNpcAgent::ChangeDoubtState(const EDoubtState InDoubtState) { Super::ChangeDoubtState(InDoubtState); switch(InDoubtState) { case EDoubtState::None: { NotifyVisionDetection(false); break; } case EDoubtState::Increasing: case EDoubtState::Decreasing: break; default: NOT_IMPLEMENTED(); } } void ASPNpcAgent::InitializeMaterials() { TArray MeshComponents; GetComponents(MeshComponents); for(UMeshComponent* MeshComp : MeshComponents) { AllMeshComponents.Add(MeshComp); } } void ASPNpcAgent::StatusSlow(float InSlowValue, bool InbRecovery) { float LocBeforeValue = EffectTimeMap.Find(EEffectSubTypes::Slow)? LocBeforeValue = EffectTimeMap.Find(EEffectSubTypes::Slow)->EffectValue : LocBeforeValue = 0.f; if(LocBeforeValue == InSlowValue) return; if(!InbRecovery) InSlowValue = InSlowValue * -1.f; float LocSlowValue = InSlowValue - LocBeforeValue; NpcStatusComponent->AddStatusNpcMove(LocSlowValue); NpcStatusComponent->AddStatusNpcRun(LocSlowValue); if(CurrentSpeed == ENpcSpeedType::Walk) GetCharacterMovement()->MaxWalkSpeed = GetNpcStatusComponent()->GetCurrentNpcStatus().fNpcMove; else if(CurrentSpeed == ENpcSpeedType::Run) GetCharacterMovement()->MaxWalkSpeed = GetNpcStatusComponent()->GetCurrentNpcStatus().fNpcRun; } void ASPNpcAgent::StatusFast(float InFastValue, bool InbRecovery) { float LocBeforeValue = EffectTimeMap.Find(EEffectSubTypes::Fast)? LocBeforeValue = EffectTimeMap.Find(EEffectSubTypes::Fast)->EffectValue : LocBeforeValue = 0.f; if(LocBeforeValue == InFastValue) return; if(InbRecovery) InFastValue = InFastValue * -1.f; float LocFastValue = InFastValue - LocBeforeValue; NpcStatusComponent->AddStatusNpcMove(LocFastValue); NpcStatusComponent->AddStatusNpcRun(LocFastValue); if(CurrentSpeed == ENpcSpeedType::Walk) GetCharacterMovement()->MaxWalkSpeed = GetNpcStatusComponent()->GetCurrentNpcStatus().fNpcMove; else if(CurrentSpeed == ENpcSpeedType::Run) GetCharacterMovement()->MaxWalkSpeed = GetNpcStatusComponent()->GetCurrentNpcStatus().fNpcRun; } void ASPNpcAgent::JumpBack(const FVector& InTargetLocation) { FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), InTargetLocation); AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::JumpBack); float JumpPower = 100.f; FVector JumpDir = (GetActorLocation() - InTargetLocation).GetSafeNormal(); FVector JumpBackVector = JumpPower * JumpDir; FVector LaunchVelocity; SetActorRotation(LookAtRotation); UGlobalUtilsLibrary::SuggestProjectileVelocityAtLocation(GetWorld(), LaunchVelocity, GetActorLocation(), GetActorLocation() + JumpBackVector, 40.f, 0.f, 1.f); LaunchCharacter(LaunchVelocity, true, true); UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); if(!World->GetTimerManager().IsTimerActive(JumpBackTimerHandle)) { World->GetTimerManager().SetTimer(JumpBackTimerHandle, this, &ASPNpcAgent::CheckFallingFromJumpBack, World->GetDeltaSeconds(), true, 0.2f); } } void ASPNpcAgent::CheckFallingFromJumpBack() { if(!GetMovementComponent()->IsFalling() && GetWorld()->GetTimerManager().IsTimerActive(JumpBackTimerHandle)) { GetWorld()->GetTimerManager().ClearTimer(JumpBackTimerHandle); JumpBackTimerHandle.Invalidate(); AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::FallDown); // RelocateOnNavMesh(); } } bool ASPNpcAgent::ShouldContinueAttack() const { AActor* Target = GetAIControllerBase()->GetTargetActor(); if(Target && IsTargetInAttackRange(Target)) { if(SkillType == ESkillTypes::Direct) { NMT_CHECKF(NpcStatusComponent) FCollisionObjectQueryParams ObjectQueryParams; ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel4); // NOTE : Wall ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel6); // NOTE : Prop FHitResult HitResult; FVector EndTraceLocation = GetActorLocation() + GetActorForwardVector() * NpcStatusComponent->GetStatusNpcAttackLength(); GetWorld()->LineTraceSingleByObjectType(HitResult, GetActorLocation(), EndTraceLocation, ObjectQueryParams, FCollisionQueryParams()); return !HitResult.bBlockingHit; } return true; } return false; } void ASPNpcAgent::DetermineNextStateAfterAbnormal() { ENpcAbnormalAnimState AbnormalAnimState = AgentAnimInstance->GetNpcAbnormalAnimState(); switch(AbnormalAnimState) { case ENpcAbnormalAnimState::HitBack: { if(!NpcStatusComponent->IsDead()) { AgentAnimInstance->SetNormalState(true); ChangeNpcState(ENpcState::Chase); } break; } case ENpcAbnormalAnimState::FallDown: { if(!NpcStatusComponent->IsDead()) { AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::GetUp); } else { AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::DeadLoop); ChangeNpcState(ENpcState::Dead); } break; } case ENpcAbnormalAnimState::GetUp: { AgentAnimInstance->SetNormalState(true); AActor* Target = GetAIControllerBase()->GetTargetActor(); if(Target) { IsTargetInAttackRange(Target) ? ChangeNpcState(ENpcState::Attack) : ChangeNpcState(ENpcState::Chase); } else { ChangeNpcState(ENpcState::Chase); } break; } case ENpcAbnormalAnimState::DeadStart: { AgentAnimInstance->ChangeNpcAbnormalAnimState(ENpcAbnormalAnimState::DeadLoop); GetMesh()->bPauseAnims = true; break; } case ENpcAbnormalAnimState::DeadLoop: case ENpcAbnormalAnimState::JumpBack: case ENpcAbnormalAnimState::Stun: NOT_IMPLEMENTED() break; default: NMT_LOG("Not Valid") } } void ASPNpcAgent::SetCurrentUsingSkill(class ACoreSkill* InSkill) { CurrentUsingSkill = InSkill; } void ASPNpcAgent:: FinishCurrentSkill() { if(CurrentUsingSkill) { NMT_LOG("FinishCurrentSkill") CurrentUsingSkill->FinishSkill(); CurrentUsingSkill = nullptr; } } void ASPNpcAgent::SetSkillCoolTime(const float InCoolTime) { SkillCoolTime = InCoolTime; bSkillCooldownActive = true; } class AAIPerceptionController* ASPNpcAgent::GetAIPerceptionController() const { AAIPerceptionController* AIC = Cast(GetController()); NMT_CHECKF(AIC) return AIC; } bool ASPNpcAgent::IsFinishSkillCoolDown() const { return SkillCoolTime <= 0.f; } bool ASPNpcAgent::IsTargetInAttackRange(const AActor* InTarget) const { NMT_CHECKF(NpcStatusComponent) return GetHorizontalDistanceTo(InTarget) <= NpcStatusComponent->GetStatusNpcAttackLength(); } void ASPNpcAgent::StartCheckInChaseRange() { NMT_CHECKF(SphereChaseRangeComponent) if(!SphereChaseRangeComponent->OnExitedSphereRange.IsBound()) { SphereChaseRangeComponent->ActivateFiltering(); SphereChaseRangeComponent->OnEnterSphereRange.AddWeakLambda(this, [this](AActor* Target) { GetAIControllerBase()->UpdateTargetInfo(Target); }); SphereChaseRangeComponent->OnExitedSphereRange.AddWeakLambda(this, [this]() { GetAIControllerBase()->UpdateTargetInfo(nullptr); }); } UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); if(!World->GetTimerManager().IsTimerActive(CheckChaseRangeTimerHandle)) World->GetTimerManager().SetTimer(CheckChaseRangeTimerHandle, this, &ASPNpcAgent::CheckTargetInChaseRange, World->GetDeltaSeconds(), true); } void ASPNpcAgent::StopSkillActionMontage() { NMT_CHECKF(AgentAnimInstance) if(!NMT_ENSURE(NpcActionMontage)) return; AgentAnimInstance->SetActionState(false); if(AgentAnimInstance->GetInstanceForMontage(NpcActionMontage)) { FAnimMontageInstance* MontageInstance = AgentAnimInstance->GetInstanceForMontage(NpcActionMontage); if(MontageInstance && MontageInstance->OnMontageBlendingOutStarted.IsBound()) { MontageInstance->OnMontageBlendingOutStarted.Unbind(); } } AgentAnimInstance->Montage_Stop(0.f, NpcActionMontage); } void ASPNpcAgent::SetSkillClass() { UDataTable* SkillDataTable = UAssetUtilsLibrary::LoadDataTableByEnumKey(EDataTableKey::Skill); if(!NMT_ENSURE(SkillDataTable)) return; NMT_CHECKF(NpcStatusComponent) FString SkillRowName = FString::FromInt(NpcStatusComponent->GetStatusNpcAttackId()); FSkill* SkillDataRow = SkillDataTable->FindRow(*SkillRowName, TEXT("Not Found FSkill")); if(!NMT_ENSURE(SkillDataRow)) return; FString TempPath = SkillDataRow->sSkillBpPath; if(!CHECK_FREE(TempPath)) { FString ClassPath = UAssetUtilsLibrary::AssetReferencePathToClassPath(TempPath); UClass* LoadedClass = StaticLoadClass(UObject::StaticClass(), nullptr, *ClassPath); NMT_CHECKF(LoadedClass) NpcSkillClass = TSubclassOf(LoadedClass); SkillType = UGlobalUtilsLibrary::GetStringToEnum(*SkillDataRow->sSkillType); bRotateWhileAttack = SkillType == ESkillTypes::Melee; } } void ASPNpcAgent::ClearSkillCoolTime() { bSkillCooldownActive = false; SkillCoolTime = 0.f; } void ASPNpcAgent::UpdateSkillCooldown(const float InDeltaSeconds) { if(SkillCoolTime < 0.f) { ClearSkillCoolTime(); if(SkillType == ESkillTypes::Direct) TryFinishAttack(); } SkillCoolTime -= InDeltaSeconds; } void ASPNpcAgent::CheckTargetInChaseRange() { if(SphereChaseRangeComponent) { AActor* TargetActor = GetAIControllerBase()->GetTargetActor(); if(TargetActor == nullptr) return; TArray DetectedActor = SphereChaseRangeComponent->GetNonBlockedActors(TargetActor->GetActorLocation(), GetActorLocation()); ACorePlayerCharacter* DetectedPlayer = nullptr; for (AActor* Actor : DetectedActor) { if(ACorePlayerCharacter* Player = Cast(Actor)) DetectedPlayer = Player; } if(DetectedPlayer == nullptr) { NMT_LOG("DetectedPlayer is empty. Player is behind wall") FinishCheckInChaseRange(); } } } void ASPNpcAgent::FinishCheckInChaseRange() { if(GetWorld()->GetTimerManager().IsTimerActive(CheckChaseRangeTimerHandle) && SphereChaseRangeComponent) { NMT_LOG("FinishCheckInChaseRange") GetWorld()->GetTimerManager().ClearTimer(CheckChaseRangeTimerHandle); CheckChaseRangeTimerHandle.Invalidate(); GetAIControllerBase()->UpdateTargetInfo(nullptr); SphereChaseRangeComponent->DeactivateFiltering(); SphereChaseRangeComponent->OnExitedSphereRange.Clear(); if(CurrentState != ENpcState::Attack && CurrentState != ENpcState::Hit) { ChangeNpcState(ENpcState::Chase); } } } void ASPNpcAgent::RelocateOnNavMesh() { // FIXME : 현재 미사용. 테스트 필요 UWorld* World = UGlobalUtilsLibrary::GetValidWorld(this); NMT_CHECKF(World); UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(World); NMT_CHECKF(NavSys); float CapsuleRadius, CapsuleHalfHeight; GetCapsuleComponent()->GetScaledCapsuleSize(CapsuleRadius, CapsuleHalfHeight); FVector Extent(CapsuleRadius, CapsuleRadius, CapsuleHalfHeight); FNavLocation OutLocation; bool bOnNavMesh = NavSys->ProjectPointToNavigation(GetActorLocation(), OutLocation, Extent); if(bOnNavMesh) { NMT_LOG("On Nav Mesh") } else { NMT_LOG("NOT On Nav Mesh") Extent += FVector(50.0, 50.0, 0.0); if(NavSys->ProjectPointToNavigation(GetActorLocation(), OutLocation, Extent)) { SetActorLocation(OutLocation); } } }