Adjusted sky update interval, added clothing assets

This commit is contained in:
2026-05-31 16:59:02 +03:00
parent 7dd75915ed
commit 49310a992b
18 changed files with 72 additions and 43 deletions
+5 -1
View File
@@ -9,7 +9,7 @@ inline const FString DefaultSaveSlotName = TEXT("Slot1");
// --- Time of day / calendar (GDD §2.4, §10.1; tuning §21) ---
// 1440 in-game minutes elapse over the ~90 real-minute day/night cycle.
inline constexpr float INGAME_MINUTES_PER_REAL_SECOND = 16.0f;
inline constexpr float INGAME_MINUTES_PER_REAL_SECOND = 1.0f;
inline constexpr int32 MINUTES_PER_HOUR = 60;
inline constexpr int32 MINUTES_PER_DAY = 1440;
inline constexpr float DAY_START_HOUR = 8.0f; // 08:00 — day phase begins (§10.1)
@@ -20,3 +20,7 @@ inline constexpr int32 CAMPAIGN_LENGTH_DAYS = 90; // §3.3 survive 90 days
inline constexpr int32 WEEK_LENGTH_DAYS = 7; // §2.4 rent due each week
inline constexpr float WEEKLY_RENT = 20000.0f; // §15.3 early-tier placeholder (§21 tuning)
// How often the current time is pushed to the sky actor. Decoupled from the in-game
// minute so the sun moves smoothly regardless of how fast the clock runs.
inline constexpr double SKY_PUSH_INTERVAL_SECONDS = 1.0 / 30.0; // 30fps
@@ -4,6 +4,7 @@
#include "TimeOfDaySubsystem.h"
#include "Constants.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/App.h"
#include "Misc/Timecode.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
@@ -20,7 +21,12 @@ void UTimeOfDaySubsystem::OnWorldBeginPlay(UWorld& InWorld)
}
bBegunPlay = true;
PushTimeToSky(); // sync the sky to the loaded time immediately
PushTimeToSky(/*bForce=*/true); // sync the sky to the loaded time immediately
// Sanity-log the clock speed: how long a full 24h in-game day takes in real time.
const double RealMinutesPerDay = MINUTES_PER_DAY / (INGAME_MINUTES_PER_REAL_SECOND * 60.0);
UE_LOG(LogTemp, Warning, TEXT("UTimeOfDaySubsystem: a full in-game day takes %.2f real minutes (%.1f in-game minutes/real second)."),
RealMinutesPerDay, INGAME_MINUTES_PER_REAL_SECOND);
}
void UTimeOfDaySubsystem::Tick(float DeltaTime)
@@ -67,7 +73,8 @@ void UTimeOfDaySubsystem::SkipTime(float Minutes)
{
if (Minutes > 0.0f)
{
AdvanceClock(static_cast<double>(Minutes));
// Discrete jump — snap the sky to the new time, don't wait for the throttle.
AdvanceClock(static_cast<double>(Minutes), /*bForceSkyPush=*/true);
}
}
@@ -79,7 +86,7 @@ void UTimeOfDaySubsystem::SkipToNextMorning()
{
Delta += MINUTES_PER_DAY;
}
AdvanceClock(Delta);
AdvanceClock(Delta, /*bForceSkyPush=*/true);
}
void UTimeOfDaySubsystem::Sleep()
@@ -101,7 +108,7 @@ void UTimeOfDaySubsystem::PopPause(FName Reason)
PauseReasons.Remove(Reason);
}
void UTimeOfDaySubsystem::AdvanceClock(double DeltaMinutes)
void UTimeOfDaySubsystem::AdvanceClock(double DeltaMinutes, bool bForceSkyPush)
{
UGlobalSaveGameData* Save = GetSave();
if (!Save || DeltaMinutes <= 0.0)
@@ -125,7 +132,7 @@ void UTimeOfDaySubsystem::AdvanceClock(double DeltaMinutes)
}
Save->MinuteOfDay = static_cast<float>(Next);
PushTimeToSky();
PushTimeToSky(bForceSkyPush);
}
void UTimeOfDaySubsystem::HandleHourBoundary(int32 HourOfDay)
@@ -207,20 +214,31 @@ void UTimeOfDaySubsystem::DepositDailyFollowerIncome()
// follower attribute yet, so this is intentionally a no-op (payout reads 0).
}
void UTimeOfDaySubsystem::PushTimeToSky()
void UTimeOfDaySubsystem::PushTimeToSky(bool bForce)
{
const UGlobalSaveGameData* Save = GetSave();
if (!Save)
return;
const int32 CurMinute = FMath::FloorToInt(Save->MinuteOfDay);
if (CurMinute == LastPushedMinute)
// Throttle continuous updates to 30fps of real time. Discrete jumps (load / skips)
// force a push so the sky snaps to the new time without waiting for the next frame.
const double Now = FApp::GetCurrentTime();
if (!bForce && LastSkyPushRealTime >= 0.0 && (Now - LastSkyPushRealTime) < SKY_PUSH_INTERVAL_SECONDS)
return;
LastPushedMinute = CurMinute;
LastSkyPushRealTime = Now;
const int32 Hours = CurMinute / MINUTES_PER_HOUR;
const int32 Minutes = CurMinute % MINUTES_PER_HOUR;
OnPushTimeToSky.Broadcast(FTimecode(Hours, Minutes, 0, 0, false));
// Carry sub-minute precision so the sun moves smoothly between minute marks. The
// fractional minute becomes seconds + frames (at the same 30fps cadence).
const double MinuteOfDay = Save->MinuteOfDay;
const int32 Hours = FMath::FloorToInt(MinuteOfDay / MINUTES_PER_HOUR);
const int32 Minutes = FMath::FloorToInt(MinuteOfDay) % MINUTES_PER_HOUR;
const double FracMinute = MinuteOfDay - FMath::FloorToDouble(MinuteOfDay);
const double FracSeconds = FracMinute * 60.0;
const int32 Seconds = FMath::FloorToInt(FracSeconds);
const int32 Frames = FMath::FloorToInt((FracSeconds - Seconds) * 30.0);
OnPushTimeToSky.Broadcast(FTimecode(Hours, Minutes, Seconds, Frames, false));
}
EDayPhase UTimeOfDaySubsystem::ComputePhase(float InMinuteOfDay)
@@ -256,7 +274,7 @@ void UTimeOfDaySubsystem::Autosave() const
{
if (const UGameInstance* GameInstance = GetWorld()->GetGameInstance())
{
if (USaveSubsystem* SaveSubsystem = GameInstance->GetSubsystem<USaveSubsystem>())
if (const USaveSubsystem* SaveSubsystem = GameInstance->GetSubsystem<USaveSubsystem>())
{
SaveSubsystem->SaveGame();
}
@@ -39,9 +39,9 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPushTimeToSkySignature, const FTi
/**
* 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).
* calendar in C++ and pushes the current time to the UltraDynamicSky actor at 30fps
* (SKY_PUSH_INTERVAL_SECONDS) 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
@@ -121,13 +121,15 @@ public:
FOnPushTimeToSkySignature OnPushTimeToSky;
private:
void AdvanceClock(double DeltaMinutes);
// bForceSkyPush bypasses the 30fps throttle for discrete jumps (skips / load),
// so the sky snaps to the new time immediately instead of waiting a frame.
void AdvanceClock(double DeltaMinutes, bool bForceSkyPush = false);
void HandleHourBoundary(int32 HourOfDay); // 023
void SetPhase(EDayPhase NewPhase);
void AdvanceCalendarDay();
void ChargeWeeklyRent();
void DepositDailyFollowerIncome();
void PushTimeToSky();
void PushTimeToSky(bool bForce = false);
static EDayPhase ComputePhase(float InMinuteOfDay);
@@ -135,8 +137,8 @@ private:
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;
// Real-time stamp (seconds, FApp clock) of the last sky push, to throttle to 30fps.
double LastSkyPushRealTime = -1.0;
EDayPhase CurrentPhase = EDayPhase::Day;
TSet<FName> PauseReasons;
bool bBegunPlay = false;
@@ -8,7 +8,6 @@
#include "InputMappingContext.h"
#include "NakedDesire/Clothing/BodyPart.h"
#include "NakedDesire/Global/Gait.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
#include "NakedDesire/Global/Stance.h"
#include "Perception/AISightTargetInterface.h"
#include "NakedDesireCharacter.generated.h"