diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.dll b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.dll index 7ebe381..489c3a7 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.dll and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.dll differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.exe index e75a932..5573ec5 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.exe and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.exe differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.pdb index eaf7b47..24a141a 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_0.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.exe index 5f247b6..4c31aa9 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.exe and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.exe differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.pdb index ecec114..f274404 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_1.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.exe index 1106244..cdbfe69 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.exe and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.exe differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.pdb index d4b5305..76f1128 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_2.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.exe index 9d54a07..19e2ea6 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.exe and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.exe differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.pdb index d417377..65c8273 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_3.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.exe index 875b841..5a6245c 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.exe and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.exe differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.pdb index 8465ae0..2e009ba 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.patch_4.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.pdb index 295f370..aec1eac 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_Behavior.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.dll b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.dll index 41a226c..43ffd49 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.dll and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.dll differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exe b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exe deleted file mode 100644 index 826dec3..0000000 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exe and /dev/null differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exp index f361d1b..2f2da9a 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exp and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.exp differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.pdb index 4fa3ebb..1d3fe88 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.patch_0.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.pdb b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.pdb index 6b7ace9..73b106e 100644 Binary files a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.pdb and b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Binaries/Win64/UnrealEditor-PS_AI_BehaviorEditor.pdb differ diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTService_UpdateThreat.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTService_UpdateThreat.cpp index cb27698..ba2cb1d 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTService_UpdateThreat.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTService_UpdateThreat.cpp @@ -76,17 +76,31 @@ void UPS_AI_Behavior_BTService_UpdateThreat::TickNode( if (ThreatActor) { - // Target switched by perception (higher score) → reset LOS tracking + // Target switched by perception (higher score) if (ThreatActor != CurrentBBTarget) { - Memory->TimeSinceLOS = 0.0f; - Memory->bHadLOS = true; - Memory->bInvestigating = false; - Memory->LastVisiblePosition = ThreatActor->GetActorLocation(); - BB->ClearValue(PS_AI_Behavior_BB::LastKnownTargetPosition); + const EPS_AI_Behavior_State CurrentState = AIC->GetBehaviorState(); - UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: target switched to '%s', LOS tracking reset"), - *AIC->GetName(), *ThreatActor->GetName()); + // During TakingCover: don't switch target — cover is positioned against current threat. + // The flanking check will handle cover invalidation if needed. + if (CurrentBBTarget && CurrentState == EPS_AI_Behavior_State::TakingCover) + { + UE_LOG(LogPS_AI_Behavior, Log, + TEXT("[%s] UpdateThreat: suppressed target switch to '%s' — in TakingCover (keeping '%s')"), + *AIC->GetName(), *ThreatActor->GetName(), *CurrentBBTarget->GetName()); + ThreatActor = CurrentBBTarget; // Keep current target + } + else + { + Memory->TimeSinceLOS = 0.0f; + Memory->bHadLOS = true; + Memory->bInvestigating = false; + Memory->LastVisiblePosition = ThreatActor->GetActorLocation(); + BB->ClearValue(PS_AI_Behavior_BB::LastKnownTargetPosition); + + UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: target switched to '%s', LOS tracking reset"), + *AIC->GetName(), *ThreatActor->GetName()); + } } BB->SetValueAsObject(PS_AI_Behavior_BB::ThreatActor, ThreatActor); diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_CoverShootCycle.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_CoverShootCycle.cpp index f41e159..6fb8e19 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_CoverShootCycle.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_CoverShootCycle.cpp @@ -220,8 +220,15 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask( if (Memory->LOSCheckTimer <= 0.0f) { Memory->LOSCheckTimer = 0.5f; - const bool bThreatHasLOS = UPS_AI_Behavior_Statics::HasLineOfSight( - Pawn->GetWorld(), Target, Pawn, 60.0f); + // Check if threat can see NPC at chest height (not feet) + const FVector ThreatEye = Target->GetActorLocation() + FVector(0, 0, 60.0f); + const FVector NpcChest = Pawn->GetActorLocation() + FVector(0, 0, 60.0f); + FHitResult FlankHit; + FCollisionQueryParams FlankParams(SCENE_QUERY_STAT(FlankLOS), true); + FlankParams.AddIgnoredActor(Target); + FlankParams.AddIgnoredActor(Pawn); + const bool bThreatHasLOS = !Pawn->GetWorld()->LineTraceSingleByChannel( + FlankHit, ThreatEye, NpcChest, ECC_Visibility, FlankParams); if (bThreatHasLOS) { UE_LOG(LogPS_AI_Behavior, Log, @@ -469,6 +476,14 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask( DrawDebugString(World, Memory->FiringPosition + FVector(0, 0, 50.0f), TEXT("FIRE"), nullptr, FColor::Red, 0.0f, true); } + else if (Memory->SubState == EPS_AI_Behavior_CombatSubState::Peeking) + { + // No firing position — shooting in place + DrawDebugSphere(World, Pawn->GetActorLocation() + FVector(0, 0, 30.0f), + 15.0f, 6, FColor::Orange, false, 0.0f); + DrawDebugString(World, Pawn->GetActorLocation() + FVector(0, 0, 50.0f), + TEXT("IN PLACE"), nullptr, FColor::Orange, 0.0f, true); + } } #endif } @@ -687,8 +702,23 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::OnFiringPositionQueryFinished( // EQS failed → fallback: peek in place if LOS exists Memory->bHasFiringPosition = false; - UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] CoverShootCycle: firing position EQS failed, peeking in place"), - *AIC->GetName()); + const int32 NumItems = Result.IsValid() ? Result->Items.Num() : 0; + const FVector CoverLoc = BB->GetValueAsVector(PS_AI_Behavior_BB::CoverLocation); + UE_LOG(LogPS_AI_Behavior, Log, + TEXT("[%s] CoverShootCycle: firing position EQS FAILED at cover %s — items=%d"), + *AIC->GetName(), *CoverLoc.ToString(), NumItems); + +#if ENABLE_DRAW_DEBUG + if (bDebugDraw) + { + UWorld* World = Pawn->GetWorld(); + const FVector PawnLoc = Pawn->GetActorLocation(); + DrawDebugSphere(World, PawnLoc + FVector(0, 0, 30.0f), + 20.0f, 8, FColor::Orange, false, 5.0f); + DrawDebugString(World, PawnLoc + FVector(0, 0, 55.0f), + TEXT("NO FIRE POS"), nullptr, FColor::Orange, 5.0f, true); + } +#endif if (Target) { diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_FindCover.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_FindCover.cpp index ba01eca..d41b801 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_FindCover.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/BT/PS_AI_Behavior_BTTask_FindCover.cpp @@ -440,18 +440,8 @@ void UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished( // Log all items with their scores for debugging const int32 NumSurvived = Result->Items.Num(); - UE_LOG(LogPS_AI_Behavior, Warning, TEXT("[%s] FindCover: EQS refinement — %d items survived filters:"), - *AIC->GetName(), NumSurvived); - for (int32 i = 0; i < NumSurvived; ++i) - { - if (!Result->Items[i].IsValid()) continue; - const FVector Loc = Result->GetItemAsLocation(i); - const float ItemScore = Result->GetItemScore(i); - UE_LOG(LogPS_AI_Behavior, Warning, TEXT(" [%d] %s score=%.3f %s"), - i, *Loc.ToString(), ItemScore, (i == 0) ? TEXT("← CHOSEN") : TEXT("")); - } - UE_LOG(LogPS_AI_Behavior, Warning, TEXT("[%s] FindCover: refined %s → %s"), - *AIC->GetName(), *OriginalCoverPos.ToString(), *FinalPos.ToString()); + UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: EQS refinement — %d items survived, refined %s → %s"), + *AIC->GetName(), NumSurvived, *OriginalCoverPos.ToString(), *FinalPos.ToString()); #if ENABLE_DRAW_DEBUG if (bDebugDraw) @@ -487,8 +477,26 @@ void UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished( } else { - UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: EQS refinement failed, using original cover position"), - *AIC->GetName()); + const int32 NumItems = Result.IsValid() ? Result->Items.Num() : 0; + UE_LOG(LogPS_AI_Behavior, Log, + TEXT("[%s] FindCover: EQS refinement FAILED — items=%d, valid=%s"), + *AIC->GetName(), NumItems, + Result.IsValid() ? TEXT("yes") : TEXT("no")); + +#if ENABLE_DRAW_DEBUG + if (bDebugDraw) + { + UWorld* World = AIC->GetWorld(); + if (World) + { + // Show that refinement failed — orange box at original position + DrawDebugBox(World, OriginalCoverPos + FVector(0, 0, 50.0f), + FVector(20.0f), FColor::Orange, false, 5.0f, 0, 2.5f); + DrawDebugString(World, OriginalCoverPos + FVector(0, 0, 75.0f), + TEXT("NO REFINE"), nullptr, FColor::Orange, 5.0f, true); + } + } +#endif } // Update BB with refined position diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSContext_CoverLocation.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSContext_CoverLocation.cpp index ff338f8..6c2c97e 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSContext_CoverLocation.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSContext_CoverLocation.cpp @@ -6,6 +6,7 @@ #include "EnvironmentQuery/EnvQueryTypes.h" #include "EnvironmentQuery/Items/EnvQueryItemType_Point.h" #include "BehaviorTree/BlackboardComponent.h" +#include "NavigationSystem.h" void UPS_AI_Behavior_EQSContext_CoverLocation::ProvideContext( FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const @@ -22,19 +23,39 @@ void UPS_AI_Behavior_EQSContext_CoverLocation::ProvideContext( UBlackboardComponent* BB = AIC->GetBlackboardComponent(); if (!BB) return; - // Prefer the original CoverPoint actor location (center of cover geometry) - // over the refined CoverLocation (which may be offset behind cover) - const AActor* CoverPoint = Cast(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint)); - if (CoverPoint) + // Use CoverPoint actor location (center of cover geometry) for circle generation. + // Fallback to CoverLocation vector if no actor. + FVector RawLoc = FVector::ZeroVector; + const AActor* CoverPointActor = Cast(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint)); + if (CoverPointActor) { - UEnvQueryItemType_Point::SetContextHelper(ContextData, CoverPoint->GetActorLocation()); - return; + RawLoc = CoverPointActor->GetActorLocation(); + } + else + { + RawLoc = BB->GetValueAsVector(PS_AI_Behavior_BB::CoverLocation); } - // Fallback to CoverLocation vector (procedural cover, no actor) - const FVector CoverLoc = BB->GetValueAsVector(PS_AI_Behavior_BB::CoverLocation); - if (!CoverLoc.IsZero()) + if (RawLoc.IsZero()) return; + + // Project to navmesh so the EQS generator works correctly + // (cover points inside geometry or above navmesh need projection) + FVector FinalLoc = RawLoc; + UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(QuerierPawn->GetWorld()); + if (NavSys) { - UEnvQueryItemType_Point::SetContextHelper(ContextData, CoverLoc); + FNavLocation NavLoc; + if (NavSys->ProjectPointToNavigation(RawLoc, NavLoc, FVector(200.0f, 200.0f, 200.0f))) + { + FinalLoc = NavLoc.Location; + } } + + UE_LOG(LogPS_AI_Behavior, Verbose, + TEXT("[EQSContext_CoverLocation] %s '%s' raw=%s → nav=%s"), + CoverPointActor ? TEXT("CoverPoint") : TEXT("CoverLocation"), + CoverPointActor ? *CoverPointActor->GetName() : TEXT("(vector)"), + *RawLoc.ToString(), *FinalLoc.ToString()); + + UEnvQueryItemType_Point::SetContextHelper(ContextData, FinalLoc); } diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_CoverQuality.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_CoverQuality.cpp index 49a5025..5f40b9e 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_CoverQuality.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_CoverQuality.cpp @@ -7,6 +7,7 @@ #include "EnvironmentQuery/Items/EnvQueryItemType_VectorBase.h" #include "CollisionQueryParams.h" #include "Engine/World.h" +#include "DrawDebugHelpers.h" UPS_AI_Behavior_EQSTest_CoverQuality::UPS_AI_Behavior_EQSTest_CoverQuality() { @@ -38,6 +39,20 @@ void UPS_AI_Behavior_EQSTest_CoverQuality::RunTest(FEnvQueryInstance& QueryInsta FCollisionQueryParams TraceParams(SCENE_QUERY_STAT(CoverQualityEQS), true); + // Ignore the threat actor itself (e.g. AimTargetActor sphere blocks its own traces) + TArray ThreatActors; + if (QueryInstance.PrepareContext(UPS_AI_Behavior_EQSContext_Threat::StaticClass(), ThreatActors)) + { + for (AActor* A : ThreatActors) + { + TraceParams.AddIgnoredActor(A); + for (AActor* Parent = A->GetAttachParentActor(); Parent; Parent = Parent->GetAttachParentActor()) + { + TraceParams.AddIgnoredActor(Parent); + } + } + } + // Compute height steps TArray TraceHeights; if (NumTraceHeights == 1) @@ -86,7 +101,7 @@ void UPS_AI_Behavior_EQSTest_CoverQuality::RunTest(FEnvQueryInstance& QueryInsta { const float HitDist = FVector::Dist(CandidatePos, Hit.ImpactPoint); - UE_LOG(LogPS_AI_Behavior, Log, + UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("CoverQuality[%d] h=%.0f lat=%.0f: HIT '%s' dist=%.0fcm"), It.GetIndex(), Height, Lateral, Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("null"), @@ -100,7 +115,7 @@ void UPS_AI_Behavior_EQSTest_CoverQuality::RunTest(FEnvQueryInstance& QueryInsta } else { - UE_LOG(LogPS_AI_Behavior, Log, + UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("CoverQuality[%d] h=%.0f lat=%.0f: NO HIT"), It.GetIndex(), Height, Lateral); } @@ -109,12 +124,26 @@ void UPS_AI_Behavior_EQSTest_CoverQuality::RunTest(FEnvQueryInstance& QueryInsta const float Score = BlockedCount / static_cast(TotalTraces); - UE_LOG(LogPS_AI_Behavior, Log, + UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("CoverQuality[%d] at %s → score=%.2f (blocked=%d/%d)"), It.GetIndex(), *CandidatePos.ToString(), Score, (int32)BlockedCount, TotalTraces); - It.SetScore(TestPurpose, FilterType, Score, 0.0f, 1.0f); +#if ENABLE_DRAW_DEBUG + if (bDrawDebug) + { + // Color: red(0) → yellow(0.5) → green(1) + const uint8 R = static_cast(FMath::Lerp(255.0f, 0.0f, FMath::Clamp(Score, 0.0f, 1.0f))); + const uint8 G = static_cast(FMath::Lerp(0.0f, 255.0f, FMath::Clamp(Score, 0.0f, 1.0f))); + DrawDebugBox(const_cast(World), CandidatePos + FVector(0, 0, 20.0f), + FVector(8.0f), FColor(R, G, 0), false, 5.0f); + DrawDebugString(const_cast(World), CandidatePos + FVector(0, 0, 35.0f), + FString::Printf(TEXT("%.0f%%"), Score * 100.0f), nullptr, + FColor(R, G, 0), 5.0f, true); + } +#endif + + It.SetScore(TestPurpose, FilterType, Score, FloatValueMin.GetValue(), FloatValueMax.GetValue()); } } diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_LineOfSight.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_LineOfSight.cpp index 662351e..f267a53 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_LineOfSight.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/EQS/PS_AI_Behavior_EQSTest_LineOfSight.cpp @@ -2,10 +2,12 @@ #include "EQS/PS_AI_Behavior_EQSTest_LineOfSight.h" #include "EQS/PS_AI_Behavior_EQSContext_Threat.h" +#include "PS_AI_Behavior_Definitions.h" #include "EnvironmentQuery/EnvQueryTypes.h" #include "EnvironmentQuery/Items/EnvQueryItemType_VectorBase.h" #include "CollisionQueryParams.h" #include "Engine/World.h" +#include "DrawDebugHelpers.h" UPS_AI_Behavior_EQSTest_LineOfSight::UPS_AI_Behavior_EQSTest_LineOfSight() { @@ -37,20 +39,91 @@ void UPS_AI_Behavior_EQSTest_LineOfSight::RunTest(FEnvQueryInstance& QueryInstan FCollisionQueryParams TraceParams(SCENE_QUERY_STAT(LOSTestEQS), true); - const FVector TraceEnd = ThreatLoc + FVector(0, 0, TargetHeightOffset); + // Ignore the threat actor itself (e.g. AimTargetActor sphere blocks its own traces) + TArray ThreatActors; + if (QueryInstance.PrepareContext(UPS_AI_Behavior_EQSContext_Threat::StaticClass(), ThreatActors)) + { + for (AActor* A : ThreatActors) + { + TraceParams.AddIgnoredActor(A); + // Also ignore parent chain (AimTarget → Character capsule) + for (AActor* Parent = A->GetAttachParentActor(); Parent; Parent = Parent->GetAttachParentActor()) + { + TraceParams.AddIgnoredActor(Parent); + } + } + } + + // Lateral offsets: center + optional left/right perpendicular to threat direction + TArray LateralOffsets; + LateralOffsets.Add(0.0f); + if (LateralSpread > 0.0f) + { + LateralOffsets.Add(-LateralSpread); + LateralOffsets.Add(LateralSpread); + } + const int32 TotalTraces = LateralOffsets.Num(); for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector CandidatePos = GetItemLocation(QueryInstance, It.GetIndex()); - const FVector TraceStart = CandidatePos + FVector(0, 0, TraceHeight); - FHitResult Hit; - const bool bBlocked = World->LineTraceSingleByChannel( - Hit, TraceStart, TraceEnd, ECC_Visibility, TraceParams); + // Direction from candidate to threat (2D) and its perpendicular + const FVector DirToThreat = (ThreatLoc - CandidatePos).GetSafeNormal2D(); + const FVector LateralDir = FVector::CrossProduct(FVector::UpVector, DirToThreat); - // Score: 1.0 = clear LOS (not blocked), 0.0 = blocked - const float Score = bBlocked ? 0.0f : 1.0f; - It.SetScore(TestPurpose, FilterType, Score, 0.0f, 1.0f); + int32 ClearCount = 0; + for (float Lateral : LateralOffsets) + { + const FVector TraceStart = CandidatePos + FVector(0, 0, TraceHeight) + LateralDir * Lateral; + const FVector TraceEnd = ThreatLoc + FVector(0, 0, TargetHeightOffset) + LateralDir * Lateral; + + FHitResult Hit; + const bool bBlocked = World->LineTraceSingleByChannel( + Hit, TraceStart, TraceEnd, ECC_Visibility, TraceParams); + + if (!bBlocked) + { + ClearCount++; + } + else + { + UE_LOG(LogPS_AI_Behavior, Verbose, + TEXT("LOS[%d] lat=%.0f: BLOCKED by '%s' at %s (dist=%.0fcm)"), + It.GetIndex(), Lateral, + Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("null"), + *Hit.ImpactPoint.ToString(), + FVector::Dist(CandidatePos, Hit.ImpactPoint)); + } + +#if ENABLE_DRAW_DEBUG + if (bDrawDebug) + { + const FColor Color = bBlocked ? FColor::Red : FColor::Green; + DrawDebugLine(const_cast(World), TraceStart, TraceEnd, + Color, false, 5.0f, 0, 0.5f); + } +#endif + } + + // Score: ratio of clear traces (1.0 = all clear, 0.0 = all blocked) + const float Score = static_cast(ClearCount) / static_cast(TotalTraces); + + UE_LOG(LogPS_AI_Behavior, Verbose, + TEXT("LOS[%d] at %s → clear=%d/%d score=%.2f"), + It.GetIndex(), *CandidatePos.ToString(), + ClearCount, TotalTraces, Score); + +#if ENABLE_DRAW_DEBUG + if (bDrawDebug) + { + const uint8 G = static_cast(Score * 255.0f); + DrawDebugSphere(const_cast(World), CandidatePos + FVector(0, 0, 20.0f), + 10.0f, 6, FColor(255 - G, G, 0), false, 5.0f); + } +#endif + + It.SetScore(TestPurpose, FilterType, Score, FloatValueMin.GetValue(), FloatValueMax.GetValue()); } } diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/PS_AI_Behavior_PerceptionComponent.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/PS_AI_Behavior_PerceptionComponent.cpp index 3ff1e08..1b02ce4 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/PS_AI_Behavior_PerceptionComponent.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Private/PS_AI_Behavior_PerceptionComponent.cpp @@ -16,6 +16,7 @@ #include "GameFramework/SpectatorPawn.h" #include "AIController.h" #include "BehaviorTree/BlackboardComponent.h" +#include "EngineUtils.h" UPS_AI_Behavior_PerceptionComponent::UPS_AI_Behavior_PerceptionComponent() { @@ -270,6 +271,53 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor( TArray PerceivedActors; GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses at once + // ─── Omniscient awareness for high-priority target types ──────── + // Protectors (police, player) are always known if within sight radius, + // even outside the perception cone. This ensures enemies prioritize + // dangerous threats they're aware of (e.g. a cop standing next to them). + if (TargetPriority.Num() > 0) + { + const AAIController* OwnerAIC = Cast(GetOwner()); + const APawn* OwnerPawn = OwnerAIC ? OwnerAIC->GetPawn() : nullptr; + + if (OwnerPawn) + { + const EPS_AI_Behavior_TargetType TopPriority = TargetPriority[0]; + const UPS_AI_Behavior_Settings* Settings = GetDefault(); + const float AwarenessRadius = Settings->DefaultSightRadius; + const FVector OwnerLoc = OwnerPawn->GetActorLocation(); + + for (TActorIterator It(GetWorld()); It; ++It) + { + APawn* CandidatePawn = *It; + if (!CandidatePawn || CandidatePawn == OwnerPawn) continue; + + // Already perceived → skip + if (PerceivedActors.Contains(CandidatePawn)) continue; + + // Check distance + const float Dist = FVector::Dist(OwnerLoc, CandidatePawn->GetActorLocation()); + if (Dist > AwarenessRadius) continue; + + // Check if this actor matches the top priority type + const EPS_AI_Behavior_TargetType CandidateType = ClassifyActor(CandidatePawn); + const EPS_AI_Behavior_TargetType MappedType = + (CandidateType == EPS_AI_Behavior_TargetType::Player) + ? EPS_AI_Behavior_TargetType::Protector + : CandidateType; + + if (MappedType == TopPriority) + { + PerceivedActors.Add(CandidatePawn); + UE_LOG(LogPS_AI_Behavior, Log, + TEXT("[%s] Omniscient awareness: added '%s' (type=%d, dist=%.0f) — top priority target"), + *OwnerPawn->GetName(), *CandidatePawn->GetName(), + static_cast(CandidateType), Dist); + } + } + } + } + if (PerceivedActors.Num() == 0) { return nullptr; diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_CoverQuality.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_CoverQuality.h index 21a49cb..eb3bd1e 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_CoverQuality.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_CoverQuality.h @@ -25,11 +25,11 @@ public: UPROPERTY(EditAnywhere, Category = "Cover", meta = (ClampMin = "1", ClampMax = "5")) int32 NumTraceHeights = 3; - /** Minimum height for the lowest trace (cm relative to item — items are at capsule center, ~90cm above ground). */ + /** Minimum height for the lowest trace (cm relative to item — items are at navmesh level). */ UPROPERTY(EditAnywhere, Category = "Cover") float MinTraceHeight = 0.0f; - /** Maximum height for the highest trace (cm relative to item — 70 ≈ top of head from capsule center). */ + /** Maximum height for the highest trace (cm relative to item — 70 ≈ knee-to-waist from navmesh). */ UPROPERTY(EditAnywhere, Category = "Cover") float MaxTraceHeight = 70.0f; @@ -48,6 +48,9 @@ public: UPROPERTY(EditAnywhere, Category = "Cover", meta = (ClampMin = "0.0", ClampMax = "100.0")) float LateralSpread = 30.0f; + /** Draw debug boxes for each candidate with color-coded score. */ + UPROPERTY(EditAnywhere, Category = "Debug") + bool bDrawDebug = false; protected: virtual void RunTest(FEnvQueryInstance& QueryInstance) const override; diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_LineOfSight.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_LineOfSight.h index fe4174e..27b95d8 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_LineOfSight.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/Source/PS_AI_Behavior/Public/EQS/PS_AI_Behavior_EQSTest_LineOfSight.h @@ -31,6 +31,18 @@ public: UPROPERTY(EditDefaultsOnly, Category = "LOS", meta = (ClampMin = "0.0")) float TargetHeightOffset = 100.0f; + /** + * Lateral offset (cm) for side traces perpendicular to threat direction. + * 0 = center trace only, 30 = adds traces at ±30cm. + * Requires wider clear LOS, not just a narrow slit past cover edge. + */ + UPROPERTY(EditAnywhere, Category = "LOS", meta = (ClampMin = "0.0", ClampMax = "100.0")) + float LateralSpread = 0.0f; + + /** Draw debug spheres and LOS lines for each candidate. */ + UPROPERTY(EditAnywhere, Category = "Debug") + bool bDrawDebug = false; + protected: virtual void RunTest(FEnvQueryInstance& QueryInstance) const override; virtual FText GetDescriptionTitle() const override;