Adjusted sky update interval, added clothing assets

This commit is contained in:
2026-05-31 16:59:02 +03:00
parent bc98ea274b
commit a0c91c81fa
18 changed files with 72 additions and 43 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+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"