diff --git a/Source/NakedDesire/NPC/NPCAIController.cpp b/Source/NakedDesire/NPC/NPCAIController.cpp index 30ba085b..b5e6bc20 100644 --- a/Source/NakedDesire/NPC/NPCAIController.cpp +++ b/Source/NakedDesire/NPC/NPCAIController.cpp @@ -8,8 +8,10 @@ #include "AI/NavigationSystemBase.h" #include "BehaviorTree/BlackboardComponent.h" #include "Kismet/GameplayStatics.h" +#include "Perception/AISense_Sight.h" #include "NakedDesire/Global/NakedDesireGameMode.h" #include "NakedDesire/Player/NakedDesireCharacter.h" +#include "NakedDesire/Stats/StatsManager.h" void ANPCAIController::SetShouldReactToPlayer(const bool Value) { @@ -19,7 +21,7 @@ void ANPCAIController::SetShouldReactToPlayer(const bool Value) void ANPCAIController::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); - + NavigationSystem = FNavigationSystem::GetCurrent(GetWorld()); GameMode = Cast(UGameplayStatics::GetGameMode(GetWorld())); @@ -29,7 +31,7 @@ void ANPCAIController::OnPossess(APawn* InPawn) TArray TargetActors; UGameplayStatics::GetAllActorsOfClass(GetWorld(), ANPCTargetLocation::StaticClass(), TargetActors); - + const int RandomIndex = FMath::RandRange(0, TargetActors.Num() - 1); Blackboard->SetValueAsVector("TargetLocation", TargetActors[RandomIndex]->GetActorLocation()); Blackboard->SetValueAsVector("SpawnLocation", SpawnLocation); @@ -37,3 +39,30 @@ void ANPCAIController::OnPossess(APawn* InPawn) PlayerCharacter = Cast(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)); Blackboard->SetValueAsObject("Player", PlayerCharacter); } + +void ANPCAIController::OnUnpossess() +{ + if (bCurrentlyObserving && PlayerCharacter) + { + PlayerCharacter->StatsManager->SetObserved(false); + bCurrentlyObserving = false; + } + Super::OnUnpossess(); +} + +void ANPCAIController::OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) +{ + Super::OnTargetPerceptionUpdated(Actor, Stimulus); + + if (Actor != PlayerCharacter) + return; + if (Stimulus.Type != UAISense::GetSenseID()) + return; + + const bool bSensed = Stimulus.WasSuccessfullySensed(); + if (bSensed == bCurrentlyObserving) + return; + + bCurrentlyObserving = bSensed; + PlayerCharacter->StatsManager->SetObserved(bSensed); +} diff --git a/Source/NakedDesire/NPC/NPCAIController.h b/Source/NakedDesire/NPC/NPCAIController.h index bbd96ef7..a39754fb 100644 --- a/Source/NakedDesire/NPC/NPCAIController.h +++ b/Source/NakedDesire/NPC/NPCAIController.h @@ -4,14 +4,13 @@ #include "CoreMinimal.h" #include "DetourCrowdAIController.h" +#include "Perception/AIPerceptionTypes.h" #include "NPCAIController.generated.h" class ANakedDesireGameMode; class UNavigationSystemV1; class ANakedDesireCharacter; -/** - * - */ + UCLASS() class NAKEDDESIRE_API ANPCAIController : public ADetourCrowdAIController { @@ -19,7 +18,7 @@ class NAKEDDESIRE_API ANPCAIController : public ADetourCrowdAIController UPROPERTY() ANakedDesireCharacter* PlayerCharacter = nullptr; - + UPROPERTY() const UNavigationSystemV1* NavigationSystem = nullptr; @@ -29,10 +28,14 @@ class NAKEDDESIRE_API ANPCAIController : public ADetourCrowdAIController UPROPERTY(EditDefaultsOnly) UBehaviorTree* BehaviorTreeAsset = nullptr; + bool bCurrentlyObserving = false; + public: UFUNCTION(BlueprintCallable) void SetShouldReactToPlayer(bool Value); protected: virtual void OnPossess(APawn* InPawn) override; + virtual void OnUnpossess() override; + virtual void OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) override; }; diff --git a/Source/NakedDesire/Stats/StatsManager.cpp b/Source/NakedDesire/Stats/StatsManager.cpp index 42684e39..86bdd29d 100644 --- a/Source/NakedDesire/Stats/StatsManager.cpp +++ b/Source/NakedDesire/Stats/StatsManager.cpp @@ -24,7 +24,22 @@ void UStatsManager::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { DecreaseEnergy(0.9f); - DecreaseEmbarrassment(1.0f); + + if (ObserverCount > 0) + { + // TODO (#05): replace 0.0f with ClothingManager->GetEffectiveCoverage() when that lands. + constexpr float CoverageWeight = 0.0f; + IncreaseEmbarrassment(EmbarrassmentGainRate * (1.0f - CoverageWeight) * DeltaTime); + } + else + { + DecreaseEmbarrassment(EmbarrassmentDecayRate * DeltaTime); + } +} + +void UStatsManager::SetObserved(const bool bObserved, const float CoverageWeight) +{ + ObserverCount = FMath::Max(0, bObserved ? ObserverCount + 1 : ObserverCount - 1); } void UStatsManager::IncreaseEmbarrassment(const float Amount) diff --git a/Source/NakedDesire/Stats/StatsManager.h b/Source/NakedDesire/Stats/StatsManager.h index ce0c229c..6437a9c4 100644 --- a/Source/NakedDesire/Stats/StatsManager.h +++ b/Source/NakedDesire/Stats/StatsManager.h @@ -19,9 +19,17 @@ class NAKEDDESIRE_API UStatsManager : public UActorComponent float Energy = 1000.0f; float MaxEnergy = 1000.0f; + int32 ObserverCount = 0; + public: UStatsManager(); + UPROPERTY(EditDefaultsOnly, Category = "Embarrassment") + float EmbarrassmentGainRate = 10.0f; + + UPROPERTY(EditDefaultsOnly, Category = "Embarrassment") + float EmbarrassmentDecayRate = 1.0f; + protected: virtual void BeginPlay() override; virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; @@ -30,6 +38,10 @@ public: float Stamina = 100.0f; float MaxStamina = 100.0f; + // Called by NPCAIController when sight is gained or lost. + // CoverageWeight: fraction of body covered [0..1]; pass 0.0f until #05 (GetEffectiveCoverage) lands. + void SetObserved(bool bObserved, float CoverageWeight = 0.0f); + UFUNCTION(BlueprintCallable) void IncreaseEmbarrassment(float Amount); void DecreaseEmbarrassment(float Amount);