Expose dashboard last-attempt review and split-capture surfaces
This commit is contained in:
parent
b0d89dbca4
commit
4b53d20926
5 changed files with 438 additions and 56 deletions
|
|
@ -10997,6 +10997,201 @@ UHyperTwistCoachDashboardWidget::GetDisplayedCurrentCaseDeckStatusInspectSurface
|
|||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptReviewSummaryInspectSurface
|
||||
UHyperTwistCoachDashboardWidget::GetDisplayedLastAttemptReviewSummaryInspectSurface() const
|
||||
{
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptReviewSummaryInspectSurface Surface;
|
||||
Surface.Headline = TEXT("Last attempt review summary");
|
||||
|
||||
const FHyperTwistTrainingAttempt* ReviewedAttempt =
|
||||
SelectedAttemptIndex >= 0 && SelectedAttemptIndex < CachedRunState.Attempts.Num()
|
||||
? &CachedRunState.Attempts[SelectedAttemptIndex]
|
||||
: nullptr;
|
||||
const bool bHasSessionScopedLastStep = !LastStepResult.SessionSummary.TrainingSessionId.IsEmpty()
|
||||
&& LastStepResult.SessionSummary.TrainingSessionId == CachedRunState.Session.TrainingSessionId;
|
||||
const FString ReviewedAttemptOrdinal = ReviewedAttempt != nullptr
|
||||
? FString::Printf(TEXT("%d/%d"), SelectedAttemptIndex + 1, CachedRunState.Attempts.Num())
|
||||
: TEXT("none");
|
||||
|
||||
Surface.AttemptOrdinal = ReviewedAttemptOrdinal;
|
||||
Surface.AttemptId = ReviewedAttempt != nullptr && !ReviewedAttempt->AttemptId.IsEmpty()
|
||||
? ReviewedAttempt->AttemptId
|
||||
: TEXT("n/a");
|
||||
Surface.CaseId = ReviewedAttempt != nullptr && !ReviewedAttempt->CaseId.IsEmpty()
|
||||
? ReviewedAttempt->CaseId
|
||||
: TEXT("n/a");
|
||||
Surface.ResultLabel = ReviewedAttempt != nullptr
|
||||
? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(ReviewedAttempt->Result)
|
||||
: TEXT("n/a");
|
||||
Surface.CompletedAtUtc = ReviewedAttempt != nullptr && !ReviewedAttempt->CompletedAtUtc.IsEmpty()
|
||||
? ReviewedAttempt->CompletedAtUtc
|
||||
: TEXT("n/a");
|
||||
Surface.LatestSubmissionLabel = bHasSessionScopedLastStep ? TEXT("yes") : TEXT("no");
|
||||
Surface.SelectedAttemptIndex = SelectedAttemptIndex;
|
||||
Surface.TotalAttemptCount = CachedRunState.Attempts.Num();
|
||||
Surface.bHasReviewedAttempt = ReviewedAttempt != nullptr;
|
||||
Surface.bLatestSubmissionMatchesActiveSession = bHasSessionScopedLastStep;
|
||||
|
||||
Surface.StatusLine = !DisplayedLastAttemptReviewSummaryLine.IsEmpty()
|
||||
? DisplayedLastAttemptReviewSummaryLine
|
||||
: (ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Attempt review: %s | case %s | result %s | completed at %s | latest submission: %s"),
|
||||
*Surface.AttemptOrdinal,
|
||||
*Surface.CaseId,
|
||||
*Surface.ResultLabel,
|
||||
*Surface.CompletedAtUtc,
|
||||
*Surface.LatestSubmissionLabel)
|
||||
: TEXT("Attempt review: no attempts recorded in the active run yet."));
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptTimingDerivedPhaseReviewInspectSurface
|
||||
UHyperTwistCoachDashboardWidget::GetDisplayedLastAttemptTimingDerivedPhaseReviewInspectSurface() const
|
||||
{
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptTimingDerivedPhaseReviewInspectSurface Surface;
|
||||
Surface.Headline = TEXT("Last attempt timing / derived-phase review");
|
||||
|
||||
const FHyperTwistTrainingAttempt* ReviewedAttempt =
|
||||
SelectedAttemptIndex >= 0 && SelectedAttemptIndex < CachedRunState.Attempts.Num()
|
||||
? &CachedRunState.Attempts[SelectedAttemptIndex]
|
||||
: nullptr;
|
||||
FString ReviewedAttemptPhaseSummary = TEXT("none");
|
||||
if (ReviewedAttempt != nullptr && ReviewedAttempt->TimingBreakdown.DerivedPhaseTimings.Num() > 0)
|
||||
{
|
||||
TArray<FString> PhaseParts;
|
||||
PhaseParts.Reserve(ReviewedAttempt->TimingBreakdown.DerivedPhaseTimings.Num());
|
||||
for (const FHyperTwistPhaseTiming& PhaseTiming : ReviewedAttempt->TimingBreakdown.DerivedPhaseTimings)
|
||||
{
|
||||
PhaseParts.Add(FString::Printf(
|
||||
TEXT("%s %d-%d (%d ms)"),
|
||||
PhaseTiming.PhaseKind.IsEmpty() ? TEXT("phase") : *PhaseTiming.PhaseKind,
|
||||
PhaseTiming.StartedAtMs,
|
||||
PhaseTiming.EndedAtMs,
|
||||
PhaseTiming.DurationMs
|
||||
));
|
||||
}
|
||||
ReviewedAttemptPhaseSummary = FString::Join(PhaseParts, TEXT(" | "));
|
||||
}
|
||||
|
||||
Surface.AttemptId = ReviewedAttempt != nullptr && !ReviewedAttempt->AttemptId.IsEmpty()
|
||||
? ReviewedAttempt->AttemptId
|
||||
: TEXT("n/a");
|
||||
Surface.AppliedPenaltyLabel = ReviewedAttempt != nullptr
|
||||
? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
ReviewedAttempt->TimingBreakdown.AppliedPenalty)
|
||||
: TEXT("n/a");
|
||||
Surface.DerivedPhaseSummary = ReviewedAttemptPhaseSummary;
|
||||
Surface.InspectionElapsedMs =
|
||||
ReviewedAttempt != nullptr ? ReviewedAttempt->TimingBreakdown.InspectionElapsedMs : 0;
|
||||
Surface.RawSolveTimeMs =
|
||||
ReviewedAttempt != nullptr ? ReviewedAttempt->TimingBreakdown.RawSolveTimeMs : 0;
|
||||
Surface.FinalTimeMs =
|
||||
ReviewedAttempt != nullptr ? ReviewedAttempt->TimingBreakdown.FinalTimeMs : 0;
|
||||
Surface.SplitCaptureCount =
|
||||
ReviewedAttempt != nullptr ? ReviewedAttempt->TimingBreakdown.SplitCaptures.Num() : 0;
|
||||
Surface.DerivedPhaseCount =
|
||||
ReviewedAttempt != nullptr ? ReviewedAttempt->TimingBreakdown.DerivedPhaseTimings.Num() : 0;
|
||||
Surface.bHasReviewedAttempt = ReviewedAttempt != nullptr;
|
||||
Surface.bDidNotFinish =
|
||||
ReviewedAttempt != nullptr && ReviewedAttempt->TimingBreakdown.bDidNotFinish;
|
||||
|
||||
Surface.TimingLine = !DisplayedLastAttemptTimingLine.IsEmpty()
|
||||
? DisplayedLastAttemptTimingLine
|
||||
: (ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Reviewed timing: inspection %d ms | raw solve %d ms | final %d ms | penalty %s | split captures %d | did-not-finish: %s"),
|
||||
Surface.InspectionElapsedMs,
|
||||
Surface.RawSolveTimeMs,
|
||||
Surface.FinalTimeMs,
|
||||
*Surface.AppliedPenaltyLabel,
|
||||
Surface.SplitCaptureCount,
|
||||
Surface.bDidNotFinish ? TEXT("yes") : TEXT("no"))
|
||||
: TEXT("Reviewed timing: unavailable until the active run records an attempt."));
|
||||
Surface.DerivedPhaseLine = !DisplayedLastAttemptDerivedPhaseLine.IsEmpty()
|
||||
? DisplayedLastAttemptDerivedPhaseLine
|
||||
: (ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Reviewed derived phases: %s"),
|
||||
*Surface.DerivedPhaseSummary)
|
||||
: TEXT("Reviewed derived phases: none yet."));
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachDashboardLiveSplitCaptureStatusInspectSurface
|
||||
UHyperTwistCoachDashboardWidget::GetDisplayedLiveSplitCaptureStatusInspectSurface() const
|
||||
{
|
||||
FHyperTwistTrainingCoachDashboardLiveSplitCaptureStatusInspectSurface Surface;
|
||||
Surface.Headline = TEXT("Live split capture status");
|
||||
|
||||
const FHyperTwistTrainingTimingPolicy* TimingPolicy = FindActiveTimingPolicy();
|
||||
const int32 MaxManualSplitCaptureCount = GetMaxManualSplitCaptureCount();
|
||||
const FHyperTwistTrainingSplitPhaseDefinition* NextSplitPhaseDefinition = FindNextSplitPhaseToCapture();
|
||||
const FString NextSplitLabel = NextSplitPhaseDefinition == nullptr
|
||||
? TEXT("none")
|
||||
: (NextSplitPhaseDefinition->Label.IsEmpty()
|
||||
? NextSplitPhaseDefinition->PhaseId
|
||||
: NextSplitPhaseDefinition->Label);
|
||||
FString CapturedSplitSummary = TEXT("none");
|
||||
if (LiveAttemptSplitCaptures.Num() > 0)
|
||||
{
|
||||
TArray<FString> CapturedSplitParts;
|
||||
CapturedSplitParts.Reserve(LiveAttemptSplitCaptures.Num());
|
||||
for (const FHyperTwistTrainingSplitCapture& SplitCapture : LiveAttemptSplitCaptures)
|
||||
{
|
||||
const FString Label = SplitCapture.Label.IsEmpty() ? SplitCapture.PhaseId : SplitCapture.Label;
|
||||
CapturedSplitParts.Add(FString::Printf(TEXT("%s@%d"), *Label, SplitCapture.TimestampMs));
|
||||
}
|
||||
CapturedSplitSummary = FString::Join(CapturedSplitParts, TEXT(" | "));
|
||||
}
|
||||
const FString FinalSplitLabel =
|
||||
TimingPolicy != nullptr
|
||||
&& TimingPolicy->SplitPhases.IsValidIndex(MaxManualSplitCaptureCount)
|
||||
? (TimingPolicy->SplitPhases[MaxManualSplitCaptureCount].Label.IsEmpty()
|
||||
? TimingPolicy->SplitPhases[MaxManualSplitCaptureCount].PhaseId
|
||||
: TimingPolicy->SplitPhases[MaxManualSplitCaptureCount].Label)
|
||||
: TEXT("solve end");
|
||||
const bool bLiveInspectionPhase = IsLiveAttemptInspectionPhase();
|
||||
const bool bLiveSolvePhase = bHasLiveAttemptTimer
|
||||
&& LiveAttemptPhase == EHyperTwistCoachDashboardAttemptPhase::Solving;
|
||||
|
||||
Surface.NextSplitLabel = NextSplitLabel;
|
||||
Surface.FinalAutoCloseLabel = FinalSplitLabel;
|
||||
Surface.CapturedSummary = CapturedSplitSummary;
|
||||
Surface.CapturedSplitCount = LiveAttemptSplitCaptures.Num();
|
||||
Surface.MaxManualSplitCaptureCount = MaxManualSplitCaptureCount;
|
||||
Surface.bHasConfiguredManualSplitPhases = MaxManualSplitCaptureCount > 0;
|
||||
Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer;
|
||||
Surface.bLiveInspectionPhase = bLiveInspectionPhase;
|
||||
Surface.bLiveSolvePhase = bLiveSolvePhase;
|
||||
|
||||
if (!DisplayedSplitCaptureStatusLine.IsEmpty())
|
||||
{
|
||||
Surface.StatusLine = DisplayedSplitCaptureStatusLine;
|
||||
}
|
||||
else if (MaxManualSplitCaptureCount <= 0)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Split capture: no manual split phases are configured for this timing policy.");
|
||||
}
|
||||
else
|
||||
{
|
||||
const FString SplitStateLine = FString::Printf(
|
||||
TEXT("Manual splits %d/%d | Next: %s | Final auto-close: %s | Captured: %s"),
|
||||
Surface.CapturedSplitCount,
|
||||
Surface.MaxManualSplitCaptureCount,
|
||||
*Surface.NextSplitLabel,
|
||||
*Surface.FinalAutoCloseLabel,
|
||||
*Surface.CapturedSummary);
|
||||
Surface.StatusLine = bLiveSolvePhase
|
||||
? SplitStateLine + TEXT(" | capture phase ends during solve; final phase closes on attempt stop")
|
||||
: (bLiveInspectionPhase
|
||||
? SplitStateLine + TEXT(" | split capture unlocks after solve start")
|
||||
: SplitStateLine + TEXT(" | start a live attempt to record manual split boundaries"));
|
||||
}
|
||||
return Surface;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectPreviousMethodDrillFollowUpHistoryEntry()
|
||||
{
|
||||
if (MethodDrillFollowUpHistoryEntries.Num() <= 0)
|
||||
|
|
@ -22908,9 +23103,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
}
|
||||
if (LastAttemptTextBlock != nullptr)
|
||||
{
|
||||
if (ReviewedAttempt != nullptr)
|
||||
{
|
||||
LastAttemptTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
DisplayedLastAttemptReviewSummaryLine = ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Attempt review: %s | case %s | result %s | completed at %s | latest submission: %s"),
|
||||
*ReviewedAttemptOrdinal,
|
||||
ReviewedAttempt->CaseId.IsEmpty() ? TEXT("n/a") : *ReviewedAttempt->CaseId,
|
||||
|
|
@ -22919,20 +23113,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
? TEXT("n/a")
|
||||
: *ReviewedAttempt->CompletedAtUtc,
|
||||
bHasSessionScopedLastStep ? TEXT("yes") : TEXT("no")
|
||||
)));
|
||||
}
|
||||
else
|
||||
{
|
||||
LastAttemptTextBlock->SetText(FText::FromString(
|
||||
TEXT("Attempt review: no attempts recorded in the active run yet.")
|
||||
));
|
||||
}
|
||||
)
|
||||
: TEXT("Attempt review: no attempts recorded in the active run yet.");
|
||||
LastAttemptTextBlock->SetText(FText::FromString(DisplayedLastAttemptReviewSummaryLine));
|
||||
}
|
||||
if (LastAttemptTimingTextBlock != nullptr)
|
||||
{
|
||||
if (ReviewedAttempt != nullptr)
|
||||
{
|
||||
LastAttemptTimingTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
DisplayedLastAttemptTimingLine = ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Reviewed timing: inspection %d ms | raw solve %d ms | final %d ms | penalty %s | split captures %d | did-not-finish: %s"),
|
||||
ReviewedAttempt->TimingBreakdown.InspectionElapsedMs,
|
||||
ReviewedAttempt->TimingBreakdown.RawSolveTimeMs,
|
||||
|
|
@ -22940,30 +23128,19 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(ReviewedAttempt->TimingBreakdown.AppliedPenalty),
|
||||
ReviewedAttempt->TimingBreakdown.SplitCaptures.Num(),
|
||||
ReviewedAttempt->TimingBreakdown.bDidNotFinish ? TEXT("yes") : TEXT("no")
|
||||
)));
|
||||
}
|
||||
else
|
||||
{
|
||||
LastAttemptTimingTextBlock->SetText(FText::FromString(
|
||||
TEXT("Reviewed timing: unavailable until the active run records an attempt.")
|
||||
));
|
||||
}
|
||||
)
|
||||
: TEXT("Reviewed timing: unavailable until the active run records an attempt.");
|
||||
LastAttemptTimingTextBlock->SetText(FText::FromString(DisplayedLastAttemptTimingLine));
|
||||
}
|
||||
if (LastAttemptPhaseTextBlock != nullptr)
|
||||
{
|
||||
if (ReviewedAttempt != nullptr)
|
||||
{
|
||||
LastAttemptPhaseTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
DisplayedLastAttemptDerivedPhaseLine = ReviewedAttempt != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Reviewed derived phases: %s"),
|
||||
*ReviewedAttemptPhaseSummary
|
||||
)));
|
||||
}
|
||||
else
|
||||
{
|
||||
LastAttemptPhaseTextBlock->SetText(FText::FromString(
|
||||
TEXT("Reviewed derived phases: none yet.")
|
||||
));
|
||||
}
|
||||
)
|
||||
: TEXT("Reviewed derived phases: none yet.");
|
||||
LastAttemptPhaseTextBlock->SetText(FText::FromString(DisplayedLastAttemptDerivedPhaseLine));
|
||||
}
|
||||
if (CurrentCaseHeaderTextBlock != nullptr)
|
||||
{
|
||||
|
|
@ -23085,9 +23262,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
if (MaxManualSplitCaptureCount <= 0)
|
||||
{
|
||||
SplitStatusTextBlock->SetText(FText::FromString(
|
||||
TEXT("Split capture: no manual split phases are configured for this timing policy.")
|
||||
));
|
||||
DisplayedSplitCaptureStatusLine =
|
||||
TEXT("Split capture: no manual split phases are configured for this timing policy.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -23099,25 +23275,13 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
*FinalSplitLabel,
|
||||
*CapturedSplitSummary
|
||||
);
|
||||
if (bLiveSolvePhase)
|
||||
{
|
||||
SplitStatusTextBlock->SetText(FText::FromString(
|
||||
SplitStateLine + TEXT(" | capture phase ends during solve; final phase closes on attempt stop")
|
||||
));
|
||||
}
|
||||
else if (bLiveInspectionPhase)
|
||||
{
|
||||
SplitStatusTextBlock->SetText(FText::FromString(
|
||||
SplitStateLine + TEXT(" | split capture unlocks after solve start")
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
SplitStatusTextBlock->SetText(FText::FromString(
|
||||
SplitStateLine + TEXT(" | start a live attempt to record manual split boundaries")
|
||||
));
|
||||
}
|
||||
DisplayedSplitCaptureStatusLine = bLiveSolvePhase
|
||||
? SplitStateLine + TEXT(" | capture phase ends during solve; final phase closes on attempt stop")
|
||||
: (bLiveInspectionPhase
|
||||
? SplitStateLine + TEXT(" | split capture unlocks after solve start")
|
||||
: SplitStateLine + TEXT(" | start a live attempt to record manual split boundaries"));
|
||||
}
|
||||
SplitStatusTextBlock->SetText(FText::FromString(DisplayedSplitCaptureStatusLine));
|
||||
}
|
||||
if (AttemptTimeTextBox != nullptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -827,6 +827,18 @@ public:
|
|||
FHyperTwistTrainingCoachDashboardCurrentCaseDeckStatusInspectSurface
|
||||
GetDisplayedCurrentCaseDeckStatusInspectSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptReviewSummaryInspectSurface
|
||||
GetDisplayedLastAttemptReviewSummaryInspectSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
FHyperTwistTrainingCoachDashboardLastAttemptTimingDerivedPhaseReviewInspectSurface
|
||||
GetDisplayedLastAttemptTimingDerivedPhaseReviewInspectSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
FHyperTwistTrainingCoachDashboardLiveSplitCaptureStatusInspectSurface
|
||||
GetDisplayedLiveSplitCaptureStatusInspectSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectPreviousMethodDrillFollowUpHistoryEntry();
|
||||
|
||||
|
|
@ -1872,7 +1884,11 @@ private:
|
|||
FString DisplayedActiveRunSummaryLine;
|
||||
FString DisplayedCurrentCasePromptLine;
|
||||
FString DisplayedCurrentCaseNotationLine;
|
||||
FString DisplayedLastAttemptReviewSummaryLine;
|
||||
FString DisplayedLastAttemptTimingLine;
|
||||
FString DisplayedLastAttemptDerivedPhaseLine;
|
||||
FString DisplayedAttemptInputStatusLine;
|
||||
FString DisplayedSplitCaptureStatusLine;
|
||||
FString DisplayedTimerStatusLine;
|
||||
FString DisplayedRecommendedDeckPreviewLine;
|
||||
FString DisplayedFollowUpDeckPreviewLine;
|
||||
|
|
|
|||
|
|
@ -10821,6 +10821,152 @@ struct FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachDashboardLastAttemptReviewSummaryInspectSurface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Headline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AttemptOrdinal;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AttemptId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CaseId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ResultLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CompletedAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LatestSubmissionLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SelectedAttemptIndex = INDEX_NONE;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalAttemptCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasReviewedAttempt = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLatestSubmissionMatchesActiveSession = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| bHasReviewedAttempt;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachDashboardLastAttemptTimingDerivedPhaseReviewInspectSurface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Headline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString TimingLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DerivedPhaseLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AttemptId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AppliedPenaltyLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DerivedPhaseSummary;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 InspectionElapsedMs = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RawSolveTimeMs = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 FinalTimeMs = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SplitCaptureCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 DerivedPhaseCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasReviewedAttempt = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bDidNotFinish = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !TimingLine.IsEmpty()
|
||||
|| !DerivedPhaseLine.IsEmpty()
|
||||
|| bHasReviewedAttempt;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachDashboardLiveSplitCaptureStatusInspectSurface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Headline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString NextSplitLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FinalAutoCloseLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CapturedSummary;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 CapturedSplitCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 MaxManualSplitCaptureCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasConfiguredManualSplitPhases = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasLiveAttemptTimer = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLiveInspectionPhase = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLiveSolvePhase = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| bHasConfiguredManualSplitPhases
|
||||
|| bHasLiveAttemptTimer;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachDashboardActiveRunStatusInspectSurface
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ Current correction call from the source-exposed audit and current execution refr
|
|||
- the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete
|
||||
- the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle
|
||||
- the now-complete deliberate `UHyperTwistTrainingRuntimeLibrary` world-context consumer lane reaches through active generated-mode selector option owned-execution split-phase current-selection merged ordered roster phase-id lookup, normalized-order lookup, and evaluation-order lookup inspect surfaces, so gameplay/Blueprint callers can inspect one retained merged-roster row directly by phase id, normalized order, or evaluation order without scanning the merged roster array or re-running inspect derivation details directly
|
||||
- the latest landed bounded `Phase 4` packet stays on that same live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and adds displayed active-run status, displayed active-attempt/timer status, and displayed current-case / current-deck status inspect surfaces over already-derived live dashboard run-state, timer-state, and current-selection presentation state, so gameplay/Blueprint callers can inspect the current active-run line, run-summary line, attempt-input/timer posture, and current-case/current-deck presentation without scraping dashboard text blocks or reopening run/timer/current-case derivation
|
||||
- the next clean move is to stay on that same live dashboard guidance-consumer lane and land the displayed last-attempt review summary, displayed last-attempt timing / derived-phase review, and live split-capture status inspect surface group rather than drifting back into backend continuity or reopening the already-complete retained-history seam
|
||||
- the latest landed bounded `Phase 4` packet stays on that same live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and adds displayed last-attempt review summary, displayed last-attempt timing / derived-phase review, and live split-capture status inspect surfaces over already-derived live dashboard review/timing/split presentation state, so gameplay/Blueprint callers can inspect the active reviewed attempt line, reviewed timing and derived-phase lines, and live split-capture posture without scraping dashboard text blocks or reopening attempt-review or split-capture derivation
|
||||
- the next clean move is to stay on that same live dashboard guidance-consumer lane and land the attempt-time input hint, live timer / split control label, and attempt result-action enablement inspect surface group rather than drifting back into backend continuity or reopening the already-complete retained-history seam
|
||||
- the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it
|
||||
|
||||
## Current execution-reality references
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# HyperTwist Phase 4 dashboard live last-attempt review and split-capture status inspect surface packet
|
||||
|
||||
- bounded Phase `4` dashboard-consumer slice
|
||||
- date: `2026-05-09`
|
||||
|
||||
## Intent
|
||||
|
||||
Stay on the deliberate live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and expose the remaining live last-attempt review and split-capture presentation state directly to gameplay and Blueprint callers.
|
||||
|
||||
## Why this packet exists
|
||||
|
||||
The dashboard already derived:
|
||||
|
||||
- the displayed last-attempt review summary line
|
||||
- the displayed reviewed timing line
|
||||
- the displayed reviewed derived-phase line
|
||||
- the displayed split-capture status line
|
||||
|
||||
But that live state still only surfaced through dashboard text blocks. Callers still had to scrape widget presentation or reopen last-attempt review and split-capture derivation to inspect what the dashboard was currently showing.
|
||||
|
||||
## What changed
|
||||
|
||||
- added `FHyperTwistTrainingCoachDashboardLastAttemptReviewSummaryInspectSurface`
|
||||
- added `FHyperTwistTrainingCoachDashboardLastAttemptTimingDerivedPhaseReviewInspectSurface`
|
||||
- added `FHyperTwistTrainingCoachDashboardLiveSplitCaptureStatusInspectSurface`
|
||||
- added `GetDisplayedLastAttemptReviewSummaryInspectSurface()`
|
||||
- added `GetDisplayedLastAttemptTimingDerivedPhaseReviewInspectSurface()`
|
||||
- added `GetDisplayedLiveSplitCaptureStatusInspectSurface()`
|
||||
- retained the exact displayed last-attempt review line, reviewed timing line, reviewed derived-phase line, and split-capture status line inside the widget so the new inspect surfaces report the same live strings the dashboard is already presenting
|
||||
- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no dashboard redesign and no backend continuity changes
|
||||
|
||||
## Files
|
||||
|
||||
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistTrainingTypes.h`
|
||||
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistCoachDashboardWidget.h`
|
||||
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Private\HyperTwistTraining\HyperTwistCoachDashboardWidget.cpp`
|
||||
- `C:\HyperTwist\docs\HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md`
|
||||
|
||||
## Outcome
|
||||
|
||||
Gameplay and Blueprint callers can now inspect:
|
||||
|
||||
- the displayed reviewed-attempt summary line and selected attempt identity/posture
|
||||
- the displayed reviewed timing line and reviewed derived-phase line
|
||||
- the displayed live split-capture status line, including next split, final auto-close, and captured manual boundaries
|
||||
|
||||
without scraping dashboard text blocks or reopening attempt-review / split-capture derivation.
|
||||
|
||||
## Validation
|
||||
|
||||
- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln`
|
||||
- passed with `0 Warning(s), 0 Error(s)`
|
||||
|
||||
## Next clean slice
|
||||
|
||||
Stay on the same live dashboard guidance-consumer lane and land the attempt-time input hint, live timer / split control label, and attempt result-action enablement inspect surface group.
|
||||
Loading…
Add table
Reference in a new issue