// © 2025 Naked People Team. All Rights Reserved. #include "RunNakedDistanceObjective.h" #include "TimerManager.h" #include "NakedDesire/Global/Gait.h" #include "NakedDesire/Player/NakedDesireCharacter.h" #define LOCTEXT_NAMESPACE "Commissions.Objectives.RunNakedDistance" namespace { constexpr float SampleIntervalSeconds = 0.2f; constexpr float MaxStepCm = 1000.0f; // ignore single-sample jumps larger than this (teleports / streaming) } void URunNakedDistanceObjective::OnActivate() { Super::OnActivate(); AccumulatedCm = 0.0f; if (Player) { LastLocation = Player->GetActorLocation(); Player->GetWorldTimerManager().SetTimer(SampleTimer, this, &URunNakedDistanceObjective::SampleDistance, SampleIntervalSeconds, true); } } void URunNakedDistanceObjective::OnDeactivate() { if (Player) Player->GetWorldTimerManager().ClearTimer(SampleTimer); SampleTimer.Invalidate(); Super::OnDeactivate(); } void URunNakedDistanceObjective::SampleDistance() { if (bSatisfied || !Player) return; const FVector Now = Player->GetActorLocation(); const float Delta = FVector::Dist(Now, LastLocation); LastLocation = Now; // Only running, naked, and within any constraints counts toward the distance. if (IsFullyNaked() && Player->GetGait() == EGait::Run && AreConstraintsMet()) { AccumulatedCm += FMath::Min(Delta, MaxStepCm); if (AccumulatedCm >= RequiredMeters * 100.0f) MarkSatisfied(); } } float URunNakedDistanceObjective::GetProgress() const { if (bSatisfied) return 1.0f; const float TargetCm = RequiredMeters * 100.0f; return TargetCm > 0.0f ? FMath::Clamp(AccumulatedCm / TargetCm, 0.0f, 1.0f) : 0.0f; } FText URunNakedDistanceObjective::GetDescription() const { return FText::Format(LOCTEXT("Run", "Run {0} m naked"), FText::AsNumber(FMath::RoundToInt(RequiredMeters))); } #undef LOCTEXT_NAMESPACE