Compare commits
2 Commits
59a23e61db
...
98f0dbdce5
| Author | SHA1 | Date | |
|---|---|---|---|
| 98f0dbdce5 | |||
| 25abd59512 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -227,8 +227,12 @@ void UPS_AI_Behavior_BTTask_Attack::TickTask(
|
||||
{
|
||||
// No LOS and idle → find a firing position
|
||||
// Check if investigation (last known position) is active
|
||||
// ClearValue sets vectors to FAISystem::InvalidLocation, not zero — must check both
|
||||
const FVector LastKnownPos = BB->GetValueAsVector(PS_AI_Behavior_BB::LastKnownTargetPosition);
|
||||
if (!LastKnownPos.IsZero())
|
||||
const bool bHasLastKnown = !LastKnownPos.IsZero() &&
|
||||
!LastKnownPos.ContainsNaN() &&
|
||||
LastKnownPos.X < 1e30f; // Filter out FLT_MAX / InvalidLocation
|
||||
if (bHasLastKnown)
|
||||
{
|
||||
// Investigation mode: go to last known position
|
||||
AIC->MoveToLocation(
|
||||
@ -336,6 +340,7 @@ void UPS_AI_Behavior_BTTask_Attack::TickTask(
|
||||
*AIC->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EBTNodeResult::Type UPS_AI_Behavior_BTTask_Attack::AbortTask(
|
||||
@ -427,20 +432,9 @@ void UPS_AI_Behavior_BTTask_Attack::OnFiringPositionQueryFinished(
|
||||
|
||||
AActor* Target = WeakTarget.Get();
|
||||
|
||||
if (Result.IsValid() && Result->IsSuccessful())
|
||||
// Fallback lambda: advance directly toward target
|
||||
auto FallbackAdvance = [&](const TCHAR* Reason)
|
||||
{
|
||||
const FVector FiringPos = Result->GetItemAsLocation(0);
|
||||
AIC->MoveToLocation(
|
||||
FiringPos, 50.0f, /*bStopOnOverlap=*/true,
|
||||
/*bUsePathfinding=*/true, /*bProjectGoal=*/false, /*bCanStrafe=*/false);
|
||||
Memory->bSeekingFiringPos = true;
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS found firing position %s"),
|
||||
*AIC->GetName(), *FiringPos.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// EQS found nothing — fallback: advance toward target
|
||||
if (Target)
|
||||
{
|
||||
const float MidRange = (Memory->MinRange + Memory->MaxRange) * 0.5f;
|
||||
@ -449,9 +443,32 @@ void UPS_AI_Behavior_BTTask_Attack::OnFiringPositionQueryFinished(
|
||||
/*bUsePathfinding=*/true, /*bProjectGoal=*/true, /*bCanStrafe=*/false);
|
||||
Memory->bSeekingFiringPos = true;
|
||||
}
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: %s, fallback advance toward target"),
|
||||
*AIC->GetName(), Reason);
|
||||
};
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS no result, fallback advance"),
|
||||
*AIC->GetName());
|
||||
if (Result.IsValid() && Result->IsSuccessful())
|
||||
{
|
||||
const FVector FiringPos = Result->GetItemAsLocation(0);
|
||||
const EPathFollowingRequestResult::Type MoveResult = AIC->MoveToLocation(
|
||||
FiringPos, 50.0f, /*bStopOnOverlap=*/true,
|
||||
/*bUsePathfinding=*/true, /*bProjectGoal=*/true, /*bCanStrafe=*/false);
|
||||
|
||||
if (MoveResult == EPathFollowingRequestResult::RequestSuccessful)
|
||||
{
|
||||
Memory->bSeekingFiringPos = true;
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS moving to firing position %s"),
|
||||
*AIC->GetName(), *FiringPos.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// AlreadyAtGoal or Failed — EQS position is too close or unreachable
|
||||
FallbackAdvance(TEXT("EQS position unreachable or too close"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FallbackAdvance(TEXT("EQS no result"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -178,6 +178,17 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::AtCover));
|
||||
|
||||
// Crouch if the cover point requires it
|
||||
if (Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] CoverShootCycle: at cover, ducking for %.1fs"),
|
||||
*AIC->GetName(), Memory->PhaseDuration);
|
||||
}
|
||||
@ -240,13 +251,12 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
}
|
||||
else
|
||||
{
|
||||
// No better cover → stay, retry next cycle
|
||||
Memory->SubState = EPS_AI_Behavior_CombatSubState::AtCover;
|
||||
Memory->PhaseDuration = FMath::RandRange(Memory->EffCoverMin, Memory->EffCoverMax);
|
||||
Memory->Timer = Memory->PhaseDuration;
|
||||
Memory->CycleCount = 0;
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::AtCover));
|
||||
// No better cover found and no LOS → abandon cover, fall back to Attack task
|
||||
UE_LOG(LogPS_AI_Behavior, Log,
|
||||
TEXT("[%s] CoverShootCycle: no LOS and no advancing cover → abandoning cover for Attack fallback"),
|
||||
*AIC->GetName());
|
||||
FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -255,13 +265,15 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
Memory->SubState = EPS_AI_Behavior_CombatSubState::Peeking;
|
||||
Memory->PhaseDuration = FMath::RandRange(Memory->EffPeekMin, Memory->EffPeekMax);
|
||||
Memory->Timer = Memory->PhaseDuration;
|
||||
Memory->LOSCheckTimer = 0.3f;
|
||||
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::Peeking));
|
||||
|
||||
// Start attacking
|
||||
// Stand up to shoot
|
||||
if (Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, false);
|
||||
IPS_AI_Behavior_Interface::Execute_BehaviorStartAttack(Pawn, Target);
|
||||
}
|
||||
|
||||
@ -274,6 +286,40 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
// ─── PEEKING: Shooting at target ────────────────────────────────
|
||||
case EPS_AI_Behavior_CombatSubState::Peeking:
|
||||
{
|
||||
// Continuous LOS check while peeking — stop shooting if target hides
|
||||
Memory->LOSCheckTimer -= DeltaSeconds;
|
||||
if (Memory->LOSCheckTimer <= 0.0f)
|
||||
{
|
||||
Memory->LOSCheckTimer = 0.3f; // check every 0.3s
|
||||
const bool bStillHasLOS = UPS_AI_Behavior_Statics::HasLineOfSight(
|
||||
Pawn->GetWorld(), Pawn, Target, 150.0f);
|
||||
if (!bStillHasLOS)
|
||||
{
|
||||
// Target hid — stop shooting, crouch back to cover
|
||||
if (Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_BehaviorStopAttack(Pawn);
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Log,
|
||||
TEXT("[%s] CoverShootCycle: lost LOS during peek → back to cover"),
|
||||
*AIC->GetName());
|
||||
|
||||
Memory->SubState = EPS_AI_Behavior_CombatSubState::AtCover;
|
||||
Memory->PhaseDuration = FMath::RandRange(Memory->EffCoverMin, Memory->EffCoverMax);
|
||||
Memory->Timer = Memory->PhaseDuration;
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::AtCover));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Memory->Timer -= DeltaSeconds;
|
||||
if (Memory->Timer <= 0.0f)
|
||||
{
|
||||
@ -353,6 +399,17 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::AtCover));
|
||||
|
||||
// Re-crouch if cover requires it
|
||||
if (Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Verbose,
|
||||
TEXT("[%s] CoverShootCycle: ducking back (cycle %d/%d)"),
|
||||
*AIC->GetName(), Memory->CycleCount, Memory->EffMaxCycles);
|
||||
@ -375,6 +432,17 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::TickTask(
|
||||
BB->SetValueAsEnum(PS_AI_Behavior_BB::CombatSubState,
|
||||
static_cast<uint8>(EPS_AI_Behavior_CombatSubState::AtCover));
|
||||
|
||||
// Crouch if the new cover point requires it
|
||||
if (Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] CoverShootCycle: arrived at new cover, ducking"),
|
||||
*AIC->GetName());
|
||||
}
|
||||
@ -391,11 +459,12 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_CoverShootCycle::AbortTask(
|
||||
{
|
||||
AIC->StopMovement();
|
||||
|
||||
// Stop attacking if we were peeking
|
||||
// Stop attacking and stand up if we were peeking/crouching
|
||||
APawn* Pawn = AIC->GetPawn();
|
||||
if (Pawn && Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_BehaviorStopAttack(Pawn);
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,11 +477,12 @@ void UPS_AI_Behavior_BTTask_CoverShootCycle::OnTaskFinished(
|
||||
APS_AI_Behavior_AIController* AIC = Cast<APS_AI_Behavior_AIController>(OwnerComp.GetAIOwner());
|
||||
if (AIC)
|
||||
{
|
||||
// Stop attacking
|
||||
// Stop attacking and stand up
|
||||
APawn* Pawn = AIC->GetPawn();
|
||||
if (Pawn && Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_BehaviorStopAttack(Pawn);
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, false);
|
||||
}
|
||||
|
||||
// Release cover point
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "BT/PS_AI_Behavior_BTTask_FindCover.h"
|
||||
#include "PS_AI_Behavior_AIController.h"
|
||||
#include "PS_AI_Behavior_Interface.h"
|
||||
#include "PS_AI_Behavior_CoverPoint.h"
|
||||
#include "PS_AI_Behavior_PersonalityComponent.h"
|
||||
#include "PS_AI_Behavior_Definitions.h"
|
||||
@ -11,6 +12,7 @@
|
||||
#include "CollisionQueryParams.h"
|
||||
#include "Engine/World.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "EnvironmentQuery/EnvQueryManager.h"
|
||||
|
||||
UPS_AI_Behavior_BTTask_FindCover::UPS_AI_Behavior_BTTask_FindCover()
|
||||
{
|
||||
@ -120,7 +122,16 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
|
||||
|
||||
BB->SetValueAsVector(PS_AI_Behavior_BB::CoverLocation, BestCoverPos);
|
||||
|
||||
// Navigate to cover
|
||||
FCoverMemory* Memory = reinterpret_cast<FCoverMemory*>(NodeMemory);
|
||||
|
||||
// If we have a refinement EQS query and a manual CoverPoint, refine the position
|
||||
if (RefinementQuery && ChosenPoint)
|
||||
{
|
||||
RunRefinementQuery(OwnerComp, NodeMemory, AIC->GetPawn(), BestCoverPos);
|
||||
return EBTNodeResult::InProgress;
|
||||
}
|
||||
|
||||
// Navigate to cover directly (no refinement)
|
||||
const EPathFollowingRequestResult::Type Result = AIC->MoveToLocation(
|
||||
BestCoverPos, AcceptanceRadius, /*bStopOnOverlap=*/true,
|
||||
/*bUsePathfinding=*/true, /*bProjectDestinationToNavigation=*/true,
|
||||
@ -136,7 +147,6 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
|
||||
return EBTNodeResult::Succeeded;
|
||||
}
|
||||
|
||||
FCoverMemory* Memory = reinterpret_cast<FCoverMemory*>(NodeMemory);
|
||||
Memory->bMoveRequested = true;
|
||||
return EBTNodeResult::InProgress;
|
||||
}
|
||||
@ -145,6 +155,7 @@ void UPS_AI_Behavior_BTTask_FindCover::TickTask(
|
||||
UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
|
||||
{
|
||||
FCoverMemory* Memory = reinterpret_cast<FCoverMemory*>(NodeMemory);
|
||||
if (Memory->bEQSRunning) return; // Waiting for EQS callback
|
||||
if (!Memory->bMoveRequested) return;
|
||||
|
||||
APS_AI_Behavior_AIController* AIC = Cast<APS_AI_Behavior_AIController>(OwnerComp.GetAIOwner());
|
||||
@ -153,6 +164,23 @@ void UPS_AI_Behavior_BTTask_FindCover::TickTask(
|
||||
if (AIC->GetMoveStatus() == EPathFollowingStatus::Idle)
|
||||
{
|
||||
Memory->bMoveRequested = false;
|
||||
|
||||
// Crouch at cover if the point requires it
|
||||
APawn* Pawn = AIC->GetPawn();
|
||||
if (Pawn && Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
|
||||
if (BB)
|
||||
{
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
||||
}
|
||||
}
|
||||
@ -165,6 +193,13 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::AbortTask(
|
||||
{
|
||||
AIC->StopMovement();
|
||||
|
||||
// Stand up if crouching
|
||||
APawn* Pawn = AIC->GetPawn();
|
||||
if (Pawn && Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, false);
|
||||
}
|
||||
|
||||
// Release any claimed cover point
|
||||
UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
|
||||
if (BB)
|
||||
@ -173,7 +208,7 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::AbortTask(
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (Point)
|
||||
{
|
||||
Point->Release(AIC->GetPawn());
|
||||
if (Pawn) Point->Release(Pawn);
|
||||
BB->ClearValue(PS_AI_Behavior_BB::CoverPoint);
|
||||
}
|
||||
}
|
||||
@ -286,9 +321,108 @@ float UPS_AI_Behavior_BTTask_FindCover::EvaluateCoverQuality(
|
||||
|
||||
FString UPS_AI_Behavior_BTTask_FindCover::GetStaticDescription() const
|
||||
{
|
||||
return FString::Printf(TEXT("Find cover within %.0fcm\nManual %s + Procedural (%d candidates)\nBonus: +%.0f%%"),
|
||||
return FString::Printf(TEXT("Find cover within %.0fcm\nManual %s + Procedural (%d candidates)\nBonus: +%.0f%%\nRefinement: %s"),
|
||||
SearchRadius,
|
||||
CoverPointType == EPS_AI_Behavior_CoverPointType::Cover ? TEXT("Cover") : TEXT("Hiding"),
|
||||
NumCandidates,
|
||||
ManualPointBonus * 100.0f);
|
||||
ManualPointBonus * 100.0f,
|
||||
RefinementQuery ? *RefinementQuery->GetName() : TEXT("None"));
|
||||
}
|
||||
|
||||
// ─── EQS Refinement ────────────────────────────────────────────────────────
|
||||
|
||||
void UPS_AI_Behavior_BTTask_FindCover::RunRefinementQuery(
|
||||
UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
|
||||
APawn* Pawn, const FVector& CoverCenter)
|
||||
{
|
||||
FCoverMemory* Memory = reinterpret_cast<FCoverMemory*>(NodeMemory);
|
||||
|
||||
UWorld* World = Pawn->GetWorld();
|
||||
UEnvQueryManager* EQSManager = UEnvQueryManager::GetCurrent(World);
|
||||
if (!EQSManager)
|
||||
{
|
||||
// Fallback: move directly to the cover center
|
||||
APS_AI_Behavior_AIController* AIC = Cast<APS_AI_Behavior_AIController>(OwnerComp.GetAIOwner());
|
||||
if (AIC)
|
||||
{
|
||||
AIC->MoveToLocation(CoverCenter, AcceptanceRadius, true, true, true, false);
|
||||
Memory->bMoveRequested = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Memory->bEQSRunning = true;
|
||||
|
||||
FEnvQueryRequest Request(RefinementQuery, Pawn);
|
||||
Request.Execute(EEnvQueryRunMode::SingleResult,
|
||||
FQueryFinishedSignature::CreateUObject(this,
|
||||
&UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished,
|
||||
&OwnerComp, NodeMemory, CoverCenter));
|
||||
|
||||
UE_LOG(LogPS_AI_Behavior, Verbose, TEXT("[%s] FindCover: EQS refinement query launched around %s"),
|
||||
*Pawn->GetName(), *CoverCenter.ToString());
|
||||
}
|
||||
|
||||
void UPS_AI_Behavior_BTTask_FindCover::OnRefinementQueryFinished(
|
||||
TSharedPtr<FEnvQueryResult> Result,
|
||||
UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory,
|
||||
FVector OriginalCoverPos)
|
||||
{
|
||||
if (!OwnerComp || !NodeMemory) return;
|
||||
|
||||
FCoverMemory* Memory = reinterpret_cast<FCoverMemory*>(NodeMemory);
|
||||
Memory->bEQSRunning = false;
|
||||
|
||||
APS_AI_Behavior_AIController* AIC = Cast<APS_AI_Behavior_AIController>(OwnerComp->GetAIOwner());
|
||||
if (!AIC || !AIC->GetPawn()) return;
|
||||
|
||||
FVector FinalPos = OriginalCoverPos; // Fallback to original position
|
||||
|
||||
if (Result.IsValid() && Result->IsSuccessful())
|
||||
{
|
||||
FinalPos = Result->GetItemAsLocation(0);
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: EQS refined position %s (was %s)"),
|
||||
*AIC->GetName(), *FinalPos.ToString(), *OriginalCoverPos.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] FindCover: EQS refinement failed, using original cover position"),
|
||||
*AIC->GetName());
|
||||
}
|
||||
|
||||
// Update BB with refined position
|
||||
UBlackboardComponent* BB = OwnerComp->GetBlackboardComponent();
|
||||
if (BB)
|
||||
{
|
||||
BB->SetValueAsVector(PS_AI_Behavior_BB::CoverLocation, FinalPos);
|
||||
}
|
||||
|
||||
// Navigate to the refined (or original) position
|
||||
const EPathFollowingRequestResult::Type MoveResult = AIC->MoveToLocation(
|
||||
FinalPos, AcceptanceRadius, true, true, true, false);
|
||||
|
||||
if (MoveResult == EPathFollowingRequestResult::Failed)
|
||||
{
|
||||
FinishLatentTask(*OwnerComp, EBTNodeResult::Failed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MoveResult == EPathFollowingRequestResult::AlreadyAtGoal)
|
||||
{
|
||||
// Crouch at cover if needed
|
||||
APawn* Pawn = AIC->GetPawn();
|
||||
if (Pawn && Pawn->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
const APS_AI_Behavior_CoverPoint* CoverPt =
|
||||
Cast<APS_AI_Behavior_CoverPoint>(BB->GetValueAsObject(PS_AI_Behavior_BB::CoverPoint));
|
||||
if (CoverPt && CoverPt->bCrouch)
|
||||
{
|
||||
IPS_AI_Behavior_Interface::Execute_SetBehaviorCrouch(Pawn, true);
|
||||
}
|
||||
}
|
||||
FinishLatentTask(*OwnerComp, EBTNodeResult::Succeeded);
|
||||
return;
|
||||
}
|
||||
|
||||
Memory->bMoveRequested = true;
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ public:
|
||||
UPROPERTY(EditAnywhere, Category = "Attack|LOS", meta = (ClampMin = "0.1", ClampMax = "1.0"))
|
||||
float LOSCheckInterval = 0.2f;
|
||||
|
||||
|
||||
/**
|
||||
* EQS query asset for finding a firing position with clear LOS.
|
||||
* Compose in editor: OnCircle generator + LineOfSight filter + Distance tests.
|
||||
|
||||
@ -87,6 +87,7 @@ private:
|
||||
float PhaseDuration = 0.0f;
|
||||
int32 CycleCount = 0;
|
||||
bool bMoveRequested = false;
|
||||
float LOSCheckTimer = 0.0f; // cooldown for LOS checks during Peeking
|
||||
|
||||
// Effective durations (modulated by personality)
|
||||
float EffPeekMin = 2.0f;
|
||||
|
||||
@ -4,10 +4,12 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "EnvironmentQuery/EnvQueryTypes.h"
|
||||
#include "PS_AI_Behavior_Definitions.h"
|
||||
#include "PS_AI_Behavior_BTTask_FindCover.generated.h"
|
||||
|
||||
class APS_AI_Behavior_CoverPoint;
|
||||
class UEnvQuery;
|
||||
|
||||
/**
|
||||
* BT Task: Find a cover position and navigate to it.
|
||||
@ -70,6 +72,19 @@ public:
|
||||
UPROPERTY(EditAnywhere, Category = "Cover|Advancement", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float AdvancementBias = 0.0f;
|
||||
|
||||
/**
|
||||
* Optional EQS query to refine the exact cover position around a selected CoverPoint.
|
||||
* The query runs centered on the chosen CoverPoint and picks the best nearby spot.
|
||||
* Use OnCircle generator (small radius ~200cm) + CoverQuality test + Distance tests.
|
||||
* If null, the NPC goes directly to the CoverPoint's location (no refinement).
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "Cover|EQS Refinement")
|
||||
TObjectPtr<UEnvQuery> RefinementQuery;
|
||||
|
||||
/** Radius around the CoverPoint for EQS refinement search (cm). */
|
||||
UPROPERTY(EditAnywhere, Category = "Cover|EQS Refinement", meta = (ClampMin = "100.0", ClampMax = "500.0"))
|
||||
float RefinementRadius = 200.0f;
|
||||
|
||||
protected:
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
|
||||
virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
|
||||
@ -80,6 +95,7 @@ private:
|
||||
struct FCoverMemory
|
||||
{
|
||||
bool bMoveRequested = false;
|
||||
bool bEQSRunning = false;
|
||||
};
|
||||
|
||||
virtual uint16 GetInstanceMemorySize() const override { return sizeof(FCoverMemory); }
|
||||
@ -98,4 +114,13 @@ private:
|
||||
APS_AI_Behavior_CoverPoint* FindBestManualCoverPoint(
|
||||
const UWorld* World, const FVector& NpcLoc, const FVector& ThreatLoc,
|
||||
EPS_AI_Behavior_NPCType NPCType, float& OutScore) const;
|
||||
|
||||
/** Run EQS refinement query around the chosen CoverPoint. */
|
||||
void RunRefinementQuery(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
|
||||
APawn* Pawn, const FVector& CoverCenter);
|
||||
|
||||
/** Callback when the refinement EQS query completes. */
|
||||
void OnRefinementQueryFinished(TSharedPtr<FEnvQueryResult> Result,
|
||||
UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory,
|
||||
FVector OriginalCoverPos);
|
||||
};
|
||||
|
||||
@ -142,6 +142,18 @@ public:
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS AI Behavior")
|
||||
bool CanBehaviorAttack(AActor* Target) const;
|
||||
|
||||
// ─── Stance ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Order the Pawn to crouch or stand up.
|
||||
* Called by the cover system when entering/leaving a cover point with bCrouch set.
|
||||
* The Pawn implements this however it wants (CharacterMovement->Crouch, animation, etc.).
|
||||
*
|
||||
* @param bCrouch True = crouch, False = stand up.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS AI Behavior")
|
||||
void SetBehaviorCrouch(bool bCrouch);
|
||||
|
||||
// ─── Combat Style ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user