Setup player preview for equipment panel

This commit is contained in:
2026-05-31 15:33:43 +03:00
parent 4218b36ac9
commit 56cc2fce98
42 changed files with 823 additions and 318 deletions
@@ -0,0 +1,143 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "TimeOfDaySubsystem.generated.h"
class UGlobalSaveGameData;
/**
* Day vs. night phase (GDD §10.1). Drives NPC density and embarrassment rate.
* Day is 08:0020:00; everything else is night.
*/
UENUM(BlueprintType)
enum class EDayPhase : uint8
{
Day,
Night
};
/**
* Why the 90-day campaign ended (GDD §3.3). Evicted is the rent-failure loss;
* CampaignComplete fires when the player survives to day 90. The ending screen /
* win-threshold logic (§21 open) lives in BP and reacts to OnCampaignEnded.
*/
UENUM(BlueprintType)
enum class ECampaignEndReason : uint8
{
Evicted,
CampaignComplete
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHourChangedSignature, int32, Hour);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDayChangedSignature, int32, NewDay);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPhaseChangedSignature, EDayPhase, NewPhase);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCampaignEndedSignature, ECampaignEndReason, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPushTimeToSkySignature, const FTimecode&, Timecode);
/**
* The single authoritative clock (GDD §2.4, §10.1). Owns time-of-day and the
* calendar in C++ and pushes the current time to the UltraDynamicSky actor each
* in-game minute via ANakedDesireGameMode::SetCurrentTime — inverting the old
* BP-drives-time flow. Persists to UGlobalSaveGameData (MinuteOfDay / DaysPassed).
*
* The calendar rolls at 04:00 (DAY_ROLL_HOUR); the day phase flips at 08:00 / 20:00.
* Weekly rent is charged every WEEK_LENGTH_DAYS-th roll; follower income deposits
* each roll. Sleep / time-skips funnel through AdvanceClock so boundaries always fire.
*/
UCLASS()
class NAKEDDESIRE_API UTimeOfDaySubsystem : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
// FTickableGameObject
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override;
virtual bool IsTickable() const override { return bBegunPlay && !IsTemplate(); }
// --- Queries ---
UFUNCTION(BlueprintPure, Category = "Time")
int32 GetDay() const;
UFUNCTION(BlueprintPure, Category = "Time")
float GetMinuteOfDay() const;
UFUNCTION(BlueprintPure, Category = "Time")
int32 GetHour() const;
UFUNCTION(BlueprintPure, Category = "Time")
int32 GetMinute() const;
UFUNCTION(BlueprintPure, Category = "Time")
EDayPhase GetPhase() const;
UFUNCTION(BlueprintPure, Category = "Time")
bool IsDay() const { return GetPhase() == EDayPhase::Day; }
// --- Time control ---
// Advance the clock by a number of in-game minutes, firing every boundary crossed.
UFUNCTION(BlueprintCallable, Category = "Time")
void SkipTime(float Minutes);
// Fast-forward to the next 08:00 (used by the §4.4 holding-cell cutscene).
UFUNCTION(BlueprintCallable, Category = "Time")
void SkipToNextMorning();
// §2.4 sleep: fast-forward 8 hours, restore energy, autosave. (The apartment bed
// also routes outside-clothing loss through USessionLossResolver — see §4.4.)
UFUNCTION(BlueprintCallable, Category = "Time")
void Sleep();
// --- Pause (reason-keyed; the clock runs only while the reason set is empty).
// Note §11.17: the holding-cell cutscene deliberately does NOT pause the clock. ---
UFUNCTION(BlueprintCallable, Category = "Time")
void PushPause(FName Reason);
UFUNCTION(BlueprintCallable, Category = "Time")
void PopPause(FName Reason);
UFUNCTION(BlueprintPure, Category = "Time")
bool IsPaused() const { return PauseReasons.Num() > 0; }
// --- Delegates ---
UPROPERTY(BlueprintAssignable, Category = "Time")
FOnHourChangedSignature OnHourChanged;
UPROPERTY(BlueprintAssignable, Category = "Time")
FOnDayChangedSignature OnDayChanged;
UPROPERTY(BlueprintAssignable, Category = "Time")
FOnPhaseChangedSignature OnPhaseChanged;
UPROPERTY(BlueprintAssignable, Category = "Time")
FOnCampaignEndedSignature OnCampaignEnded;
UPROPERTY(BlueprintAssignable, Category = "Time")
FOnPushTimeToSkySignature OnPushTimeToSky;
private:
void AdvanceClock(double DeltaMinutes);
void HandleHourBoundary(int32 HourOfDay); // 023
void SetPhase(EDayPhase NewPhase);
void AdvanceCalendarDay();
void ChargeWeeklyRent();
void DepositDailyFollowerIncome();
void PushTimeToSky();
static EDayPhase ComputePhase(float InMinuteOfDay);
UGlobalSaveGameData* GetSave() const;
void RestorePlayerEnergy() const;
void Autosave() const;
// Last whole in-game minute pushed to the sky, to throttle the push to ~1/min.
int32 LastPushedMinute = -1;
EDayPhase CurrentPhase = EDayPhase::Day;
TSet<FName> PauseReasons;
bool bBegunPlay = false;
};