diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index bd23229..1ee1a9b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -99,6 +99,51 @@ namespace HyperTwistCoachDashboardWidgetInternal } return TextBox; } + + float ScoreDeferredEntryForPromotion( + const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry, + const FHyperTwistTrainingCoachPanelState& CoachPanelState) + { + float Score = FMath::Clamp(Entry.PriorityScore * 0.02f, 0.0f, 2.0f); + if (!CoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + && Entry.SourceLabel == CoachPanelState.PreferredGuidanceSourceLabel) + { + Score += 2.0f; + } + + switch (CoachPanelState.PreferredGuidanceLane) + { + case EHyperTwistTrainingCoachGuidanceLane::FollowUp: + if (Entry.bRequiresFollowUp || Entry.SourceLabel == TEXT("follow-up-brief")) + { + Score += 8.0f; + } + if (Entry.bRequiresArchiveRetentionTuning || Entry.SourceLabel == TEXT("archive-policy")) + { + Score -= 4.0f; + } + break; + case EHyperTwistTrainingCoachGuidanceLane::Recommended: + if (Entry.SourceLabel == TEXT("primary-brief")) + { + Score += 7.0f; + } + if (Entry.bRequiresArchiveRetentionTuning || Entry.SourceLabel == TEXT("archive-policy")) + { + Score -= 1.5f; + } + break; + default: + break; + } + + if (Entry.bPinned) + { + Score += 0.5f; + } + + return Score; + } } void UHyperTwistCoachDashboardWidget::NativeConstruct() @@ -4679,15 +4724,33 @@ FString UHyperTwistCoachDashboardWidget::BuildDefaultDeferredUntilUtc() const FString UHyperTwistCoachDashboardWidget::FindFirstDeferredQueueEntryId() const { + const FHyperTwistTrainingCoachSessionQueueStateEntry* BestDeferredEntry = nullptr; + float BestDeferredScore = TNumericLimits::Lowest(); for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : CachedCoachSessionQueueState.Entries) { - if (Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred) + if (Entry.EntryState != EHyperTwistTrainingCoachSessionQueueEntryState::Deferred) { - return Entry.EntryId; + continue; + } + + const float EntryScore = HyperTwistCoachDashboardWidgetInternal::ScoreDeferredEntryForPromotion( + Entry, + CachedCoachPanelState + ); + const bool bShouldReplaceBest = BestDeferredEntry == nullptr + || EntryScore > BestDeferredScore + || (FMath::IsNearlyEqual(EntryScore, BestDeferredScore) + && (Entry.SlotIndex < BestDeferredEntry->SlotIndex + || (Entry.SlotIndex == BestDeferredEntry->SlotIndex + && Entry.PriorityScore > BestDeferredEntry->PriorityScore))); + if (bShouldReplaceBest) + { + BestDeferredEntry = &Entry; + BestDeferredScore = EntryScore; } } - return FString(); + return BestDeferredEntry != nullptr ? BestDeferredEntry->EntryId : FString(); } bool UHyperTwistCoachDashboardWidget::TryParseAttemptTimeOverride(int32& OutTimeMs) const