// © 2025 Naked People Team. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Subsystems/WorldSubsystem.h" #include "SessionManagerSubsystem.generated.h" class UStatsManager; /** * Why a session ended (GDD §4.4). SafeReturn is a non-loss end (player walked * home); the other three are the session-loss conditions from §3.3. The actual * "what gets lost" handling lives in USessionLossResolver (DEV-100), which also * applies police-chase precedence via bPoliceChaseActive. */ UENUM(BlueprintType) enum class ESessionLossCause : uint8 { SafeReturn, EmbarrassmentMax, EnergyZero, PoliceCapture }; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSessionStartSignature); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionEndSignature, ESessionLossCause, Cause); /** * Owns the "going outside" session state (GDD §4). A session starts when the * player leaves the apartment and ends when they return safely or hit a loss * condition (embarrassment max / energy zero / police capture). The apartment * ALocationTrigger drives start / end; embarrassment-max and energy-zero are * detected by subscribing to UStatsManager. This replaces the EndGameEmbarrassed * Blueprint event on ANakedDesireGameMode. */ UCLASS() class NAKEDDESIRE_API USessionManagerSubsystem : public UWorldSubsystem { GENERATED_BODY() public: virtual void OnWorldBeginPlay(UWorld& InWorld) override; // Called by the apartment ALocationTrigger as the player crosses the threshold. void NotifyEnteredApartment(); void NotifyLeftApartment(); UFUNCTION(BlueprintPure, Category = "Session") bool IsSessionActive() const { return bSessionActive; } // Set by the police AI pipeline (Phase 6). Read by USessionLossResolver for // the §4.4 loss precedence rule: a loss fired mid-chase resolves as capture. UFUNCTION(BlueprintCallable, Category = "Session") void SetPoliceChaseActive(bool bActive) { bPoliceChaseActive = bActive; } UFUNCTION(BlueprintPure, Category = "Session") bool IsPoliceChaseActive() const { return bPoliceChaseActive; } UPROPERTY(BlueprintAssignable, Category = "Session") FOnSessionStartSignature OnSessionStart; UPROPERTY(BlueprintAssignable, Category = "Session") FOnSessionEndSignature OnSessionEnd; private: void StartSession(); void EndSession(ESessionLossCause Cause); void BindToPlayerStats(); UFUNCTION() void HandleEmbarrassmentUpdate(float CurrentValue, float MaxValue); UFUNCTION() void HandleEnergyUpdate(float CurrentValue, float MaxValue); bool bSessionActive = false; bool bPoliceChaseActive = false; };