Compare commits

..

No commits in common. "a31ac1d78299ac239eff199ae23c8113e423c359" and "c32aba9902158d4cd342b93638fe8410f9c2ef7a" have entirely different histories.

29 changed files with 70 additions and 164 deletions

View File

@ -40,22 +40,19 @@ void UPS_AI_Behavior_BTService_UpdateThreat::TickNode(
// Debug: check what perception sees // Debug: check what perception sees
TArray<AActor*> PerceivedActors; TArray<AActor*> PerceivedActors;
Perception->GetCurrentlyPerceivedActors(nullptr, PerceivedActors); Perception->GetCurrentlyPerceivedActors(nullptr, PerceivedActors);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] UpdateThreat: Perceived %d actors"),
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: Perceived %d actors"),
*AIC->GetName(), PerceivedActors.Num()); *AIC->GetName(), PerceivedActors.Num());
for (AActor* A : PerceivedActors) for (AActor* A : PerceivedActors)
{ {
UE_LOG(LogPS_AI_Behavior, Log, TEXT(" - %s"), *A->GetName()); UE_LOG(LogPS_AI_Behavior, Verbose, TEXT(" - %s"), *A->GetName());
}
} }
// Also check known actors (perceived in the past but maybe lost sight) // Also check known actors (perceived in the past but maybe lost sight)
TArray<AActor*> KnownActors; TArray<AActor*> KnownActors;
Perception->GetKnownPerceivedActors(nullptr, KnownActors); Perception->GetKnownPerceivedActors(nullptr, KnownActors);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()) && KnownActors.Num() != PerceivedActors.Num()) if (KnownActors.Num() != PerceivedActors.Num())
{ {
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: Known (past) %d actors"), UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] UpdateThreat: Known (past) %d actors"),
*AIC->GetName(), KnownActors.Num()); *AIC->GetName(), KnownActors.Num());
} }
@ -77,11 +74,8 @@ void UPS_AI_Behavior_BTService_UpdateThreat::TickNode(
BB->SetValueAsFloat(PS_AI_Behavior_BB::ThreatLevel, FinalThreat); BB->SetValueAsFloat(PS_AI_Behavior_BB::ThreatLevel, FinalThreat);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: raw=%.2f, stored=%.2f, final=%.2f"), UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] UpdateThreat: raw=%.2f, stored=%.2f, final=%.2f"),
*AIC->GetName(), RawThreat, StoredThreat, FinalThreat); *AIC->GetName(), RawThreat, StoredThreat, FinalThreat);
}
// ─── Update threat actor and location with LOS tracking ───────── // ─── Update threat actor and location with LOS tracking ─────────
FUpdateThreatMemory* Memory = reinterpret_cast<FUpdateThreatMemory*>(NodeMemory); FUpdateThreatMemory* Memory = reinterpret_cast<FUpdateThreatMemory*>(NodeMemory);

View File

@ -6,7 +6,6 @@
#include "PS_AI_Behavior_SplineNetwork.h" #include "PS_AI_Behavior_SplineNetwork.h"
#include "PS_AI_Behavior_SplinePath.h" #include "PS_AI_Behavior_SplinePath.h"
#include "PS_AI_Behavior_PersonalityComponent.h" #include "PS_AI_Behavior_PersonalityComponent.h"
#include "PS_AI_Behavior_Statics.h"
#include "PS_AI_Behavior_Definitions.h" #include "PS_AI_Behavior_Definitions.h"
#include "Navigation/PathFollowingComponent.h" #include "Navigation/PathFollowingComponent.h"
@ -33,13 +32,10 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
} }
// Debug: log state on entry // Debug: log state on entry
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindAndFollowSpline: bIsFollowing=%d CurrentSpline=%s"), UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindAndFollowSpline: bIsFollowing=%d CurrentSpline=%s"),
*AIC->GetName(), *AIC->GetName(),
(int32)Follower->bIsFollowing, (int32)Follower->bIsFollowing,
Follower->CurrentSpline ? *Follower->CurrentSpline->GetName() : TEXT("null")); Follower->CurrentSpline ? *Follower->CurrentSpline->GetName() : TEXT("null"));
}
// If already following a spline, don't re-search — just succeed immediately // If already following a spline, don't re-search — just succeed immediately
// The Follow Spline task will continue the movement // The Follow Spline task will continue the movement
@ -63,13 +59,10 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
const FVector SplineDir = Follower->CurrentSpline->GetWorldDirectionAtDistance(ClosestDist); const FVector SplineDir = Follower->CurrentSpline->GetWorldDirectionAtDistance(ClosestDist);
const bool bForward = FVector::DotProduct(PawnFwd, SplineDir) >= 0.0f; const bool bForward = FVector::DotProduct(PawnFwd, SplineDir) >= 0.0f;
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindAndFollowSpline: resuming spline '%s' at closest point (gap=%.0fcm, dist=%.0f, bWalkToSpline=%d, AcceptanceRadius=%.0f)"), TEXT("[%s] FindAndFollowSpline: resuming spline '%s' at closest point (gap=%.0fcm, dist=%.0f, bWalkToSpline=%d, AcceptanceRadius=%.0f)"),
*AIC->GetName(), *Follower->CurrentSpline->GetName(), GapToSpline, ClosestDist, *AIC->GetName(), *Follower->CurrentSpline->GetName(), GapToSpline, ClosestDist,
(int32)bWalkToSpline, AcceptanceRadius); (int32)bWalkToSpline, AcceptanceRadius);
}
if (bWalkToSpline && GapToSpline > AcceptanceRadius) if (bWalkToSpline && GapToSpline > AcceptanceRadius)
{ {
@ -80,12 +73,9 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
/*bUsePathfinding=*/true, /*bProjectDestinationToNavigation=*/true, /*bUsePathfinding=*/true, /*bProjectDestinationToNavigation=*/true,
/*bCanStrafe=*/false); /*bCanStrafe=*/false);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindAndFollowSpline: MoveToLocation result=%d (0=Failed, 1=AlreadyAtGoal, 2=RequestSuccessful)"), TEXT("[%s] FindAndFollowSpline: MoveToLocation result=%d (0=Failed, 1=AlreadyAtGoal, 2=RequestSuccessful)"),
*AIC->GetName(), (int32)Result); *AIC->GetName(), (int32)Result);
}
if (Result == EPathFollowingRequestResult::AlreadyAtGoal) if (Result == EPathFollowingRequestResult::AlreadyAtGoal)
{ {
@ -108,12 +98,9 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
{ {
// Close enough — clear any residual movement request and start following // Close enough — clear any residual movement request and start following
AIC->StopMovement(); AIC->StopMovement();
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindAndFollowSpline: close enough, StartFollowingAtDistance(dist=%.0f, fwd=%d)"), TEXT("[%s] FindAndFollowSpline: close enough, StartFollowingAtDistance(dist=%.0f, fwd=%d)"),
*AIC->GetName(), ClosestDist, (int32)bForward); *AIC->GetName(), ClosestDist, (int32)bForward);
}
Follower->StartFollowingAtDistance(Follower->CurrentSpline, ClosestDist, bForward); Follower->StartFollowingAtDistance(Follower->CurrentSpline, ClosestDist, bForward);
return EBTNodeResult::Succeeded; return EBTNodeResult::Succeeded;
} }
@ -144,12 +131,9 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
AIC->GetPawn()->GetActorLocation(), NPCType, MaxSearchDistance, AIC->GetPawn()->GetActorLocation(), NPCType, MaxSearchDistance,
ClosestSpline, DistAlongSpline)) ClosestSpline, DistAlongSpline))
{ {
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose,
{
UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindAndFollowSpline: no accessible spline within %.0fcm."), TEXT("[%s] FindAndFollowSpline: no accessible spline within %.0fcm."),
*AIC->GetName(), MaxSearchDistance); *AIC->GetName(), MaxSearchDistance);
}
return EBTNodeResult::Failed; return EBTNodeResult::Failed;
} }
@ -157,13 +141,10 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindAndFollowSpline::ExecuteTask(
const FVector SplinePoint = ClosestSpline->GetWorldLocationAtDistance(DistAlongSpline); const FVector SplinePoint = ClosestSpline->GetWorldLocationAtDistance(DistAlongSpline);
const float GapToSpline = FVector::Dist(AIC->GetPawn()->GetActorLocation(), SplinePoint); const float GapToSpline = FVector::Dist(AIC->GetPawn()->GetActorLocation(), SplinePoint);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose,
{
UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindAndFollowSpline: found spline '%s' at dist=%.0f/%.0f, gap=%.0fcm"), TEXT("[%s] FindAndFollowSpline: found spline '%s' at dist=%.0f/%.0f, gap=%.0fcm"),
*AIC->GetName(), *ClosestSpline->GetName(), DistAlongSpline, *AIC->GetName(), *ClosestSpline->GetName(), DistAlongSpline,
ClosestSpline->GetSplineLength(), GapToSpline); ClosestSpline->GetSplineLength(), GapToSpline);
}
if (bWalkToSpline && GapToSpline > AcceptanceRadius) if (bWalkToSpline && GapToSpline > AcceptanceRadius)
{ {

View File

@ -5,7 +5,6 @@
#include "PS_AI_Behavior_Interface.h" #include "PS_AI_Behavior_Interface.h"
#include "PS_AI_Behavior_CoverPoint.h" #include "PS_AI_Behavior_CoverPoint.h"
#include "PS_AI_Behavior_PersonalityComponent.h" #include "PS_AI_Behavior_PersonalityComponent.h"
#include "PS_AI_Behavior_Statics.h"
#include "PS_AI_Behavior_Definitions.h" #include "PS_AI_Behavior_Definitions.h"
#include "BehaviorTree/BlackboardComponent.h" #include "BehaviorTree/BlackboardComponent.h"
#include "NavigationSystem.h" #include "NavigationSystem.h"
@ -123,10 +122,7 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
if (BestScore < 0.1f) if (BestScore < 0.1f)
{ {
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] FindCover: no suitable cover found."), *AIC->GetName());
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: no suitable cover found."), *AIC->GetName());
}
return EBTNodeResult::Failed; return EBTNodeResult::Failed;
} }
@ -136,24 +132,18 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
ChosenPoint->Claim(AIC->GetPawn()); ChosenPoint->Claim(AIC->GetPawn());
BB->SetValueAsObject(PS_AI_Behavior_BB::CoverPoint, ChosenPoint); BB->SetValueAsObject(PS_AI_Behavior_BB::CoverPoint, ChosenPoint);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] FindCover: using manual %s '%s' (score %.2f)"),
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: using manual %s '%s' (score %.2f)"),
*AIC->GetName(), *AIC->GetName(),
ChosenPoint->PointType == EPS_AI_Behavior_CoverPointType::Cover ? TEXT("Cover") : TEXT("HidingSpot"), ChosenPoint->PointType == EPS_AI_Behavior_CoverPointType::Cover ? TEXT("Cover") : TEXT("HidingSpot"),
*ChosenPoint->GetName(), BestScore); *ChosenPoint->GetName(), BestScore);
} }
}
else else
{ {
BB->ClearValue(PS_AI_Behavior_BB::CoverPoint); BB->ClearValue(PS_AI_Behavior_BB::CoverPoint);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn())) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] FindCover: using procedural cover (score %.2f)"),
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: using procedural cover (score %.2f)"),
*AIC->GetName(), BestScore); *AIC->GetName(), BestScore);
} }
}
BB->SetValueAsVector(PS_AI_Behavior_BB::CoverLocation, BestCoverPos); BB->SetValueAsVector(PS_AI_Behavior_BB::CoverLocation, BestCoverPos);
@ -225,13 +215,10 @@ void UPS_AI_Behavior_BTTask_FindCover::TickTask(
const FVector CoverLoc = BB->GetValueAsVector(PS_AI_Behavior_BB::CoverLocation); const FVector CoverLoc = BB->GetValueAsVector(PS_AI_Behavior_BB::CoverLocation);
const float DistToCover = FVector::Dist2D(Pawn->GetActorLocation(), CoverLoc); const float DistToCover = FVector::Dist2D(Pawn->GetActorLocation(), CoverLoc);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(AIC->GetPawn()))
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] FindCover: MoveStatus=Idle, dist2D=%.0fcm, acceptance=%.0fcm, coverLoc=%s, pawnLoc=%s"), TEXT("[%s] FindCover: MoveStatus=Idle, dist2D=%.0fcm, acceptance=%.0fcm, coverLoc=%s, pawnLoc=%s"),
*AIC->GetName(), DistToCover, AcceptanceRadius, *AIC->GetName(), DistToCover, AcceptanceRadius,
*CoverLoc.ToString(), *Pawn->GetActorLocation().ToString()); *CoverLoc.ToString(), *Pawn->GetActorLocation().ToString());
}
if (DistToCover > AcceptanceRadius * 2.0f) if (DistToCover > AcceptanceRadius * 2.0f)
{ {
@ -458,11 +445,8 @@ void UPS_AI_Behavior_BTTask_FindCover::RunRefinementQuery(
&UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished, &UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished,
&OwnerComp, NodeMemory, CoverCenter)); &OwnerComp, NodeMemory, CoverCenter));
if (UPS_AI_Behavior_Statics::IsDebugEnabled(Pawn)) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] FindCover: EQS refinement query launched around %s"),
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: EQS refinement query launched around %s"),
*Pawn->GetName(), *CoverCenter.ToString()); *Pawn->GetName(), *CoverCenter.ToString());
}
} }
void UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished( void UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished(

View File

@ -5,7 +5,6 @@
#include "PS_AI_Behavior_PersonalityComponent.h" #include "PS_AI_Behavior_PersonalityComponent.h"
#include "PS_AI_Behavior_PersonalityProfile.h" #include "PS_AI_Behavior_PersonalityProfile.h"
#include "PS_AI_Behavior_TeamComponent.h" #include "PS_AI_Behavior_TeamComponent.h"
#include "PS_AI_Behavior_Statics.h"
#include "PS_AI_Behavior_Settings.h" #include "PS_AI_Behavior_Settings.h"
#include "Perception/AISenseConfig_Sight.h" #include "Perception/AISenseConfig_Sight.h"
#include "Perception/AISenseConfig_Hearing.h" #include "Perception/AISenseConfig_Hearing.h"
@ -287,11 +286,6 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
TArray<AActor*> PerceivedActors; TArray<AActor*> PerceivedActors;
GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses at once GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses at once
// Resolve debug flag once for all logs in this function
const AAIController* DebugAIC = Cast<AAIController>(GetOwner());
const APawn* DebugPawn = DebugAIC ? DebugAIC->GetPawn() : nullptr;
const bool bDebugLog = UPS_AI_Behavior_Statics::IsDebugEnabled(DebugPawn);
// ─── Omniscient awareness for high-priority target types ──────── // ─── Omniscient awareness for high-priority target types ────────
// Protectors (police, player) are always known if within sight radius, // Protectors (police, player) are always known if within sight radius,
// even outside the perception cone. This ensures enemies prioritize // even outside the perception cone. This ensures enemies prioritize
@ -331,8 +325,6 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
if (MappedType == TopPriority) if (MappedType == TopPriority)
{ {
PerceivedActors.Add(CandidatePawn); PerceivedActors.Add(CandidatePawn);
if (bDebugLog)
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] Omniscient awareness: added '%s' (type=%d, dist=%.0f) — top priority target"), TEXT("[%s] Omniscient awareness: added '%s' (type=%d, dist=%.0f) — top priority target"),
*OwnerPawn->GetName(), *CandidatePawn->GetName(), *OwnerPawn->GetName(), *CandidatePawn->GetName(),
@ -341,7 +333,6 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
} }
} }
} }
}
if (PerceivedActors.Num() == 0) if (PerceivedActors.Num() == 0)
{ {
@ -415,12 +406,9 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
// Same exact team (same NPCType + same Faction) → always skip // Same exact team (same NPCType + same Faction) → always skip
// Allied teams (Civilian ↔ Protector) → allow gunfire through // Allied teams (Civilian ↔ Protector) → allow gunfire through
if (AIC->GetGenericTeamId().GetId() == GetActorTeamId(OwningPawn)) if (AIC->GetGenericTeamId().GetId() == GetActorTeamId(OwningPawn))
{
if (bDebugLog)
{ {
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s': SKIPPED (same team 0x%02X)"), UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s': SKIPPED (same team 0x%02X)"),
*Owner->GetName(), *OwningPawn->GetName(), GetActorTeamId(OwningPawn)); *Owner->GetName(), *OwningPawn->GetName(), GetActorTeamId(OwningPawn));
}
continue; continue;
} }
@ -441,13 +429,10 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
} }
if (!bActorHasGunshot) if (!bActorHasGunshot)
{
if (bDebugLog)
{ {
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s': SKIPPED (attitude=%d, not hostile, no gunshot, theirTeam=0x%02X, myTeam=0x%02X)"), UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s': SKIPPED (attitude=%d, not hostile, no gunshot, theirTeam=0x%02X, myTeam=0x%02X)"),
*Owner->GetName(), *OwningPawn->GetName(), static_cast<int32>(Attitude), *Owner->GetName(), *OwningPawn->GetName(), static_cast<int32>(Attitude),
GetActorTeamId(OwningPawn), AIC->GetGenericTeamId().GetId()); GetActorTeamId(OwningPawn), AIC->GetGenericTeamId().GetId());
}
continue; // Not hostile, no gunshot — skip continue; // Not hostile, no gunshot — skip
} }
} }
@ -537,12 +522,9 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
{ {
const FActorScore& Entry = Pair.Value; const FActorScore& Entry = Pair.Value;
if (bDebugLog)
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s' (pawn='%s'): type=%d, hostile=%d, score=%.0f"), UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Target '%s' (pawn='%s'): type=%d, hostile=%d, score=%.0f"),
*Owner->GetName(), *Entry.Actor->GetName(), *Pair.Key->GetName(), *Owner->GetName(), *Entry.Actor->GetName(), *Pair.Key->GetName(),
static_cast<int32>(Entry.ActorType), Entry.bIsHostile ? 1 : 0, Entry.Score); static_cast<int32>(Entry.ActorType), Entry.bIsHostile ? 1 : 0, Entry.Score);
}
if (Entry.Score > BestScore) if (Entry.Score > BestScore)
{ {

View File

@ -3,7 +3,6 @@
#include "PS_AI_Behavior_PersonalityComponent.h" #include "PS_AI_Behavior_PersonalityComponent.h"
#include "PS_AI_Behavior_Interface.h" #include "PS_AI_Behavior_Interface.h"
#include "PS_AI_Behavior_PersonalityProfile.h" #include "PS_AI_Behavior_PersonalityProfile.h"
#include "PS_AI_Behavior_Statics.h"
#include "PS_AI_Behavior_AIController.h" #include "PS_AI_Behavior_AIController.h"
#include "PS_AI_Behavior_SplineFollowerComponent.h" #include "PS_AI_Behavior_SplineFollowerComponent.h"
#include "PS_AI_Behavior_SplinePath.h" #include "PS_AI_Behavior_SplinePath.h"
@ -88,14 +87,11 @@ EPS_AI_Behavior_State UPS_AI_Behavior_PersonalityComponent::EvaluateReaction() c
const float EffectiveFleeThresh = FleeThresh * (0.5f + Courage * 0.5f) * (1.5f - Caution * 0.5f); const float EffectiveFleeThresh = FleeThresh * (0.5f + Courage * 0.5f) * (1.5f - Caution * 0.5f);
const float EffectiveAttackThresh = AttackThresh * (1.5f - Aggressivity * 0.5f); const float EffectiveAttackThresh = AttackThresh * (1.5f - Aggressivity * 0.5f);
if (UPS_AI_Behavior_Statics::IsDebugEnabled(Cast<APawn>(GetOwner())))
{
UE_LOG(LogPS_AI_Behavior, Log, UE_LOG(LogPS_AI_Behavior, Log,
TEXT("[%s] EvaluateReaction: threat=%.2f, aggr=%.2f, courage=%.2f, caution=%.2f | attackThresh=%.2f, fleeThresh=%.2f, alertThresh=%.2f"), TEXT("[%s] EvaluateReaction: threat=%.2f, aggr=%.2f, courage=%.2f, caution=%.2f | attackThresh=%.2f, fleeThresh=%.2f, alertThresh=%.2f"),
GetOwner() ? *GetOwner()->GetName() : TEXT("?"), GetOwner() ? *GetOwner()->GetName() : TEXT("?"),
PerceivedThreatLevel, Aggressivity, Courage, Caution, PerceivedThreatLevel, Aggressivity, Courage, Caution,
EffectiveAttackThresh, EffectiveFleeThresh, AlertThresh); EffectiveAttackThresh, EffectiveFleeThresh, AlertThresh);
}
// Decision cascade — with hysteresis to prevent state flickering. // Decision cascade — with hysteresis to prevent state flickering.
// Once in a state, require a larger threshold drop to leave it. // Once in a state, require a larger threshold drop to leave it.
@ -235,14 +231,11 @@ EPS_AI_Behavior_State UPS_AI_Behavior_PersonalityComponent::ApplyReaction()
const EPS_AI_Behavior_State OldState = CurrentState; const EPS_AI_Behavior_State OldState = CurrentState;
CurrentState = NewState; // Replicated → OnRep fires on clients CurrentState = NewState; // Replicated → OnRep fires on clients
if (UPS_AI_Behavior_Statics::IsDebugEnabled(Cast<APawn>(GetOwner()))) UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] State: %s -> %s (Threat: %.2f)"),
{
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] State: %s -> %s (Threat: %.2f)"),
*GetOwner()->GetName(), *GetOwner()->GetName(),
*UEnum::GetValueAsString(OldState), *UEnum::GetValueAsString(OldState),
*UEnum::GetValueAsString(NewState), *UEnum::GetValueAsString(NewState),
PerceivedThreatLevel); PerceivedThreatLevel);
}
HandleStateChanged(OldState, NewState); HandleStateChanged(OldState, NewState);
} }

View File

@ -1,17 +1,10 @@
// Copyright Asterion. All Rights Reserved. // Copyright Asterion. All Rights Reserved.
#include "PS_AI_Behavior_Statics.h" #include "PS_AI_Behavior_Statics.h"
#include "PS_AI_Behavior_PersonalityComponent.h"
#include "Perception/AISense_Hearing.h" #include "Perception/AISense_Hearing.h"
#include "CollisionQueryParams.h" #include "CollisionQueryParams.h"
#include "Engine/World.h" #include "Engine/World.h"
static TAutoConsoleVariable<int32> CVarBehaviorDebug(
TEXT("ps.ai.Behavior.Debug"),
-1,
TEXT("Global debug override for PS_AI_Behavior. -1=use per-NPC bDebug property, 0=force off, 1=force on."),
ECVF_Default);
void UPS_AI_Behavior_Statics::ReportGunfire(UObject* WorldContext, FVector Location, void UPS_AI_Behavior_Statics::ReportGunfire(UObject* WorldContext, FVector Location,
AActor* Shooter, bool bIsEnemyFire, float Loudness, float MaxRange) AActor* Shooter, bool bIsEnemyFire, float Loudness, float MaxRange)
{ {
@ -65,16 +58,3 @@ bool UPS_AI_Behavior_Statics::HasLineOfSight(const UWorld* World, const AActor*
return !World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params); return !World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params);
} }
bool UPS_AI_Behavior_Statics::IsDebugEnabled(const APawn* Pawn)
{
// CVar override: 0=off, 1=on, -1=use property
const int32 CVar = CVarBehaviorDebug.GetValueOnGameThread();
if (CVar == 0) return false;
if (CVar == 1) return true;
// Fall through to per-NPC property
if (!Pawn) return false;
const auto* Personality = Pawn->FindComponentByClass<UPS_AI_Behavior_PersonalityComponent>();
return Personality && Personality->bDebug;
}

View File

@ -53,7 +53,4 @@ public:
*/ */
static bool HasLineOfSight(const UWorld* World, const AActor* Source, const AActor* Target, static bool HasLineOfSight(const UWorld* World, const AActor* Source, const AActor* Target,
float EyeHeightOffset = 150.0f); float EyeHeightOffset = 150.0f);
/** Check if debug is enabled for a given Pawn (checks CVar override first, then PersonalityComponent.bDebug). */
static bool IsDebugEnabled(const APawn* Pawn);
}; };

View File

@ -232,17 +232,18 @@ void UPS_AI_ConvAgent_BodyExpressionComponent::PickAndSwitchAnim()
void UPS_AI_ConvAgent_BodyExpressionComponent::OnConversationConnected( void UPS_AI_ConvAgent_BodyExpressionComponent::OnConversationConnected(
const FPS_AI_ConvAgent_ConversationInfo_ElevenLabs& Info) const FPS_AI_ConvAgent_ConversationInfo_ElevenLabs& Info)
{ {
// Don't activate yet — wait for the agent to actually speak (OnSpeakingStarted). bActive = true;
// This prevents body expressions from playing while the NPC is still walking
// and the user hasn't said anything yet.
bIsSpeaking = false; bIsSpeaking = false;
LastEventName = TEXT("Connected"); LastEventName = TEXT("Connected");
LastEventWorldTime = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; LastEventWorldTime = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f;
// Start with an idle anim
PickAndSwitchAnim();
if (bDebug) if (bDebug)
{ {
UE_LOG(LogPS_AI_ConvAgent_BodyExpr, Log, UE_LOG(LogPS_AI_ConvAgent_BodyExpr, Log,
TEXT("Conversation connected — waiting for agent speech to activate body expressions.")); TEXT("Conversation connected — body expression activating (idle)."));
} }
} }
@ -267,12 +268,6 @@ void UPS_AI_ConvAgent_BodyExpressionComponent::OnConversationDisconnected(
void UPS_AI_ConvAgent_BodyExpressionComponent::OnSpeakingStarted() void UPS_AI_ConvAgent_BodyExpressionComponent::OnSpeakingStarted()
{ {
// Activate on first speech if not already active (deferred from OnConversationConnected)
if (!bActive)
{
bActive = true;
}
bIsSpeaking = true; bIsSpeaking = true;
LastEventName = TEXT("SpeakStart"); LastEventName = TEXT("SpeakStart");
LastEventWorldTime = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; LastEventWorldTime = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f;
@ -283,7 +278,7 @@ void UPS_AI_ConvAgent_BodyExpressionComponent::OnSpeakingStarted()
if (bDebug) if (bDebug)
{ {
UE_LOG(LogPS_AI_ConvAgent_BodyExpr, Log, UE_LOG(LogPS_AI_ConvAgent_BodyExpr, Log,
TEXT("Agent started speaking — body expression activating + speaking anim.")); TEXT("Agent started speaking — switching to speaking body anim."));
} }
} }