Files
Naked-Desire/Source/NakedDesire/NPC/NPCAIController.cpp
T
2026-06-01 19:12:00 +03:00

82 lines
2.5 KiB
C++

// © 2025 Naked People Team. All Rights Reserved.
#include "NPCAIController.h"
#include "NPC.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Perception/AISense_Sight.h"
#include "Perception/AISenseConfig_Sight.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/Stats/StatsManager.h"
#include "Perception/AIPerceptionComponent.h"
ANPCAIController::ANPCAIController()
{
UAIPerceptionComponent* Perception = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("PerceptionComponent"));
SetPerceptionComponent(*Perception);
UAISenseConfig_Sight* SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
Perception->ConfigureSense(*SightConfig);
Perception->SetDominantSense(SightConfig->GetSenseImplementation());
}
void ANPCAIController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
PerceptionComponent->OnTargetPerceptionUpdated.AddUniqueDynamic(this, &ANPCAIController::OnTargetPerceptionUpdate);
}
void ANPCAIController::OnUnPossess()
{
ClearObservation();
Super::OnUnPossess();
}
void ANPCAIController::ClearObservation()
{
if (bCurrentlyObserving && PlayerCharacter)
{
PlayerCharacter->StatsManager->SetObserved(false, GetPawn());
bCurrentlyObserving = false;
}
}
float ANPCAIController::GetObservationWeight() const
{
if (const ANPC* NPC = Cast<ANPC>(GetPawn()))
return NPC->GetObservationWeight();
return 1.0f;
}
void ANPCAIController::OnTargetPerceptionUpdate(AActor* Actor, FAIStimulus Stimulus)
{
if (Stimulus.Type != UAISense::GetSenseID<UAISense_Sight>())
return;
ANakedDesireCharacter* SensedPlayer = Cast<ANakedDesireCharacter>(Actor);
if (!SensedPlayer)
return;
PlayerCharacter = SensedPlayer;
const bool bSensed = Stimulus.WasSuccessfullySensed();
if (bSensed == bCurrentlyObserving)
return;
bCurrentlyObserving = bSensed;
PlayerCharacter->StatsManager->SetObserved(bSensed, GetPawn(), GetObservationWeight());
// Surface the sighting to the behavior tree: the react/wander branch keys off "Player", and a
// fresh sighting re-arms the one-shot notice reaction (see the BT handoff in PLAN.md).
if (UBlackboardComponent* BB = GetBlackboardComponent())
{
BB->SetValueAsObject(TEXT("Player"), bSensed ? PlayerCharacter : nullptr);
if (!bSensed)
BB->SetValueAsBool(TEXT("bHasReacted"), false);
}
}