119 lines
3.2 KiB
C++
119 lines
3.2 KiB
C++
// © 2025 Naked People Team. All Rights Reserved.
|
|
|
|
|
|
#include "NPC.h"
|
|
|
|
#include "BrainComponent.h"
|
|
#include "NPCTypeDefinition.h"
|
|
#include "NPCAIController.h"
|
|
#include "NPCTargetLocation.h"
|
|
#include "NPCVisualsConfig.h"
|
|
#include "BehaviorTree/BlackboardComponent.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
|
|
ANPC::ANPC()
|
|
{
|
|
PrimaryActorTick.bCanEverTick = false;
|
|
|
|
GetCharacterMovement()->MaxWalkSpeed = 200.0f;
|
|
GetCharacterMovement()->bUseControllerDesiredRotation = true;
|
|
GetCharacterMovement()->bOrientRotationToMovement = false;
|
|
bUseControllerRotationYaw = false;
|
|
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
|
|
|
|
// Crowd anim hygiene (mesh-agnostic): only evaluate the pose while the NPC is on screen and
|
|
// throttle the eval rate by distance. CharacterMovement still ticks off-screen so pooled-in
|
|
// NPCs can keep walking; only the visible pose freezes when unrendered.
|
|
if (USkeletalMeshComponent* MeshComp = GetMesh())
|
|
{
|
|
MeshComp->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
|
|
MeshComp->bEnableUpdateRateOptimizations = true;
|
|
}
|
|
}
|
|
|
|
ENPCType ANPC::GetNPCType() const
|
|
{
|
|
return TypeDefinition ? TypeDefinition->Type : ENPCType::Walker;
|
|
}
|
|
|
|
float ANPC::GetObservationWeight() const
|
|
{
|
|
return TypeDefinition ? TypeDefinition->ObservationWeight : 1.0f;
|
|
}
|
|
|
|
bool ANPC::ShouldStopToObserve() const
|
|
{
|
|
return TypeDefinition ? TypeDefinition->bStopsToObserve : false;
|
|
}
|
|
|
|
float ANPC::GetObserveDuration() const
|
|
{
|
|
return TypeDefinition ? TypeDefinition->ObserveDurationSeconds : 0.0f;
|
|
}
|
|
|
|
void ANPC::ActivateFromPool(const FVector& Location, const FRotator& Rotation)
|
|
{
|
|
SetActorLocationAndRotation(Location, Rotation, false, nullptr, ETeleportType::TeleportPhysics);
|
|
SetActorHiddenInGame(false);
|
|
SetActorEnableCollision(true);
|
|
|
|
if (UCharacterMovementComponent* Move = GetCharacterMovement())
|
|
{
|
|
Move->SetComponentTickEnabled(true);
|
|
Move->SetMovementMode(MOVE_Walking);
|
|
}
|
|
|
|
if (ANPCAIController* NPCController = Cast<ANPCAIController>(GetController()))
|
|
{
|
|
NPCController->RunCustomBehaviorTree();
|
|
NPCController->GetBlackboardComponent()->SetValueAsBool(TEXT("bHasReacted"), false);
|
|
}
|
|
}
|
|
|
|
void ANPC::DeactivateToPool()
|
|
{
|
|
// Drop any active observation so a pooled-out NPC stops contributing to the player's embarrassment.
|
|
if (ANPCAIController* NPCController = Cast<ANPCAIController>(GetController()))
|
|
{
|
|
NPCController->ClearObservation();
|
|
if (UBrainComponent* Brain = NPCController->GetBrainComponent())
|
|
{
|
|
Brain->StopLogic(TEXT("Moved to pool"));
|
|
}
|
|
NPCController->ClearFocus(EAIFocusPriority::Default);
|
|
}
|
|
|
|
if (UCharacterMovementComponent* Move = GetCharacterMovement())
|
|
{
|
|
Move->StopMovementImmediately();
|
|
Move->DisableMovement();
|
|
Move->SetComponentTickEnabled(false);
|
|
}
|
|
|
|
if (TargetLocationActor)
|
|
{
|
|
TargetLocationActor->Release(this);
|
|
}
|
|
|
|
SetActorHiddenInGame(true);
|
|
SetActorEnableCollision(false);
|
|
}
|
|
|
|
void ANPC::Init(ENPCType InType)
|
|
{
|
|
if (InType == ENPCType::None)
|
|
return;
|
|
|
|
Type = InType;
|
|
|
|
if (!NPCTypeDefinitions.Contains(Type))
|
|
return;
|
|
|
|
UNPCTypeDefinition* Definition = NPCTypeDefinitions[Type];
|
|
if (!Definition)
|
|
return;
|
|
|
|
TypeDefinition = Definition;
|
|
}
|