Expose gameplay selector stored-request delta surface

This commit is contained in:
axiomlogicnexus 2026-05-08 04:21:06 +02:00
parent 92487e3692
commit 71d29c331c
9 changed files with 1158 additions and 2 deletions

View file

@ -8868,3 +8868,726 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionRequestAl
return Surface;
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
OptionRequestAlignmentSurface
)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface Surface;
Surface.SelectorId = SelectorId;
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
Surface.Headline = TEXT("Generated Mode Selector Option Stored-Request Delta");
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
if (!SelectorId.IsEmpty())
{
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
? FString::Printf(
TEXT("Generated Mode Selector Option Stored-Request Delta: %s"),
*SelectorId
)
: FString::Printf(
TEXT("Generated Mode Selector Option Stored-Request Delta: %s"),
*OptionMenuSurface.SelectorLabel
);
}
if (SelectorId.IsEmpty())
{
Surface.StatusLine =
TEXT("Generated-mode selector option stored-request delta requires a selector id from the active selector roster.");
Surface.SelectorDiffSummaryLine = TEXT("Selector diff: n/a");
Surface.CurrentToHypotheticalSummaryLine = TEXT("Current -> hypothetical: n/a");
Surface.StoredRequestToHypotheticalSummaryLine = TEXT("Stored request -> hypothetical: n/a");
Surface.CurrentToStoredRequestSummaryLine = TEXT("Current -> stored request: n/a");
Surface.CurrentSelectionDeltaLine = TEXT("Current selection: n/a");
Surface.DetailLine = OptionRequestAlignmentSurface.StatusLine.IsEmpty()
? TEXT("Request alignment: n/a")
: FString::Printf(
TEXT("Request alignment: %s"),
*OptionRequestAlignmentSurface.StatusLine
);
return Surface;
}
const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = RunState.ActiveDeck.ImportedRuntimeSurface;
const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid()
&& RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase);
const FHyperTwistTrainingImportedRuntimeSelector* MatchingSelector =
RuntimeSurface.Selectors.FindByPredicate(
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelector& Candidate)
{
return Candidate.SelectorId == SelectorId && Candidate.IsStructurallyValid();
}
);
const FString SelectorBinding = MatchingSelector != nullptr
? MatchingSelector->SelectorBinding
: OptionMenuSurface.BindingLabel;
const FHyperTwistTrainingImportedGeneratedModeLaunchRequest& LaunchRequest =
RunState.ImportedGeneratedModeLaunchRequest;
const bool bHasComparableStoredRequest = LaunchRequest.IsStructurallyValid()
&& LaunchRequest.LaunchConfig.DeckId == RunState.ActiveDeck.DeckId
&& LaunchRequest.LaunchConfig.SurfaceId == RuntimeSurface.SurfaceId
&& LaunchRequest.LaunchConfig.RuntimeModeId.Equals(
RuntimeSurface.RuntimeModeId,
ESearchCase::IgnoreCase
);
const auto DescribeSelector = [](
const FHyperTwistTrainingImportedRuntimeSelector& Selector
) -> FString
{
if (!Selector.Label.IsEmpty())
{
return Selector.Label;
}
if (!Selector.SelectorBinding.IsEmpty())
{
return Selector.SelectorBinding;
}
return Selector.SelectorId;
};
const auto FindSelectorChoice = [](
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& Choices,
const FString& InSelectorId
) -> const FHyperTwistTrainingImportedRuntimeSelectorChoice*
{
return Choices.FindByPredicate(
[&InSelectorId](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
{
return Candidate.SelectorId == InSelectorId && Candidate.IsStructurallyValid();
}
);
};
const auto DoChoicesMatch = [](
const FHyperTwistTrainingImportedRuntimeSelectorChoice* LeftChoice,
const FHyperTwistTrainingImportedRuntimeSelectorChoice* RightChoice
) -> bool
{
if (LeftChoice == nullptr || RightChoice == nullptr)
{
return LeftChoice == nullptr && RightChoice == nullptr;
}
return LeftChoice->SelectedValue == RightChoice->SelectedValue;
};
const auto DescribeChoiceValue = [](
const FHyperTwistTrainingImportedRuntimeSelectorChoice* Choice,
const TCHAR* MissingText
) -> FString
{
if (Choice == nullptr)
{
return MissingText;
}
if (Choice->DisplayValue.IsEmpty() || Choice->DisplayValue == Choice->SelectedValue)
{
return Choice->SelectedValue.IsEmpty() ? FString(MissingText) : Choice->SelectedValue;
}
return FString::Printf(
TEXT("%s [%s]"),
*Choice->DisplayValue,
Choice->SelectedValue.IsEmpty() ? TEXT("n/a") : *Choice->SelectedValue
);
};
const auto BuildPairDeltaLine = [](
const TCHAR* Label,
const FString& LeftValue,
const FString& RightValue,
const bool bMatches
) -> FString
{
return bMatches
? FString::Printf(TEXT("%s: no change (%s)"), Label, *RightValue)
: FString::Printf(TEXT("%s: %s -> %s"), Label, *LeftValue, *RightValue);
};
const auto BuildAggregateDeltaLine = [](
const TCHAR* Label,
const TArray<FString>& ChangedSelectorLabels
) -> FString
{
if (ChangedSelectorLabels.Num() == 0)
{
return FString::Printf(TEXT("%s: no selector change"), Label);
}
const int32 PreviewCount = FMath::Min(3, ChangedSelectorLabels.Num());
TArray<FString> PreviewLabels;
for (int32 Index = 0; Index < PreviewCount; ++Index)
{
PreviewLabels.Add(ChangedSelectorLabels[Index]);
}
return FString::Printf(
TEXT("%s: %d selector change%s (%s%s)"),
Label,
ChangedSelectorLabels.Num(),
ChangedSelectorLabels.Num() == 1 ? TEXT("") : TEXT("s"),
*FString::Join(PreviewLabels, TEXT(", ")),
ChangedSelectorLabels.Num() > PreviewCount
? *FString::Printf(TEXT(" (+%d more)"), ChangedSelectorLabels.Num() - PreviewCount)
: TEXT("")
);
};
const auto BuildFallbackUnresolvedSelectorLine = [&DescribeSelector, &FindSelectorChoice, &RuntimeSurface](
const FHyperTwistTrainingImportedRuntimeSelectionState& SelectionState,
int32& OutUnresolvedSelectorCount
) -> FString
{
OutUnresolvedSelectorCount = 0;
TArray<FString> UnresolvedSelectorLabels;
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
{
if (!Selector.IsStructurallyValid())
{
continue;
}
if (FindSelectorChoice(SelectionState.SelectorChoices, Selector.SelectorId) != nullptr)
{
continue;
}
++OutUnresolvedSelectorCount;
UnresolvedSelectorLabels.Add(DescribeSelector(Selector));
}
if (OutUnresolvedSelectorCount == 0)
{
return TEXT("Unresolved after choice: none");
}
const int32 PreviewCount = FMath::Min(3, UnresolvedSelectorLabels.Num());
TArray<FString> PreviewLabels;
for (int32 Index = 0; Index < PreviewCount; ++Index)
{
PreviewLabels.Add(UnresolvedSelectorLabels[Index]);
}
return FString::Printf(
TEXT("Unresolved after choice: %s%s"),
*FString::Join(PreviewLabels, TEXT(", ")),
UnresolvedSelectorLabels.Num() > PreviewCount
? *FString::Printf(TEXT(" (+%d more)"), UnresolvedSelectorLabels.Num() - PreviewCount)
: TEXT("")
);
};
if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface)
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option stored-request delta is not currently available.")
: OptionMenuSurface.StatusLine;
Surface.SelectorDiffSummaryLine = TEXT("Selector diff: n/a");
Surface.CurrentToHypotheticalSummaryLine = TEXT("Current -> hypothetical: n/a");
Surface.StoredRequestToHypotheticalSummaryLine = TEXT("Stored request -> hypothetical: n/a");
Surface.CurrentToStoredRequestSummaryLine = TEXT("Current -> stored request: n/a");
Surface.CurrentSelectionDeltaLine = OptionMenuSurface.bHasUnresolvedSelectionDetail
? OptionMenuSurface.UnresolvedSelectionLine
: (OptionMenuSurface.bHasSelection
? TEXT("Current selection remains cached, but no active stored-request delta preview is available.")
: TEXT("Current selection: unresolved"));
Surface.DetailLine = FString::Printf(
TEXT("Dispatch: %s | Request alignment: %s"),
OptionDispatchSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionDispatchSurface.StatusLine,
OptionRequestAlignmentSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionRequestAlignmentSurface.StatusLine
);
return Surface;
}
int32 PreviewableGeneratedOptionCount = 0;
int32 CurrentToHypotheticalDeltaOptionCount = 0;
int32 StoredRequestToHypotheticalDeltaOptionCount = 0;
int32 CurrentToStoredRequestDeltaOptionCount = 0;
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchEntrySurface& DispatchEntry :
OptionDispatchSurface.OptionEntries)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaEntrySurface Entry;
Entry.OptionId = DispatchEntry.OptionId;
Entry.Label = DispatchEntry.Label;
Entry.OptionValue = DispatchEntry.OptionValue;
Entry.bMatchesCurrentSelection = DispatchEntry.bMatchesCurrentSelection;
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentEntrySurface*
MatchingRequestAlignmentEntry =
OptionRequestAlignmentSurface.OptionEntries.FindByPredicate(
[&DispatchEntry](
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentEntrySurface&
Candidate
)
{
return Candidate.OptionId == DispatchEntry.OptionId;
}
);
if (DispatchEntry.DispatchKind
== EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind::DirectCaseLaunch
&& DispatchEntry.bCanDispatch)
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::DirectOwnedCaseStart;
Entry.HypotheticalSelectorSummaryLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->HypotheticalSelectorSummaryLine.IsEmpty()
? TEXT("Hypothetical selectors: direct owned-case launch bypasses generated-mode selector-choice apply.")
: MatchingRequestAlignmentEntry->HypotheticalSelectorSummaryLine;
Entry.CurrentToHypotheticalDeltaLine =
TEXT("Current -> hypothetical: direct owned-case launch bypasses selector diff preview.");
Entry.StoredRequestToHypotheticalDeltaLine =
TEXT("Stored request -> hypothetical: direct owned-case launch bypasses selector diff preview.");
Entry.CurrentToStoredRequestDeltaLine =
TEXT("Current -> stored request: direct owned-case launch bypasses selector diff preview.");
Entry.UnresolvedSelectorsLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->UnresolvedSelectorsLine.IsEmpty()
? TEXT("Unresolved after choice: n/a")
: MatchingRequestAlignmentEntry->UnresolvedSelectorsLine;
Entry.ExecutePreviewLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->ExecutePreviewLine.IsEmpty()
? TEXT("Execute preview: direct owned-case launch starts immediately.")
: MatchingRequestAlignmentEntry->ExecutePreviewLine;
Entry.DetailLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->DetailLine.IsEmpty()
? TEXT("Direct option launch bypasses generated-mode selector diff preview for this interaction.")
: MatchingRequestAlignmentEntry->DetailLine;
Entry.bStartsOwnedCaseImmediately = true;
Entry.bCanPreview = true;
++Surface.DirectOwnedCaseStartOptionCount;
}
else if (DispatchEntry.DispatchKind
== EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind::ApplyGeneratedChoice
&& DispatchEntry.bCanDispatch && MatchingSelector != nullptr)
{
++PreviewableGeneratedOptionCount;
FHyperTwistTrainingImportedRuntimeSelectionState HypotheticalSelectionState =
RunState.ImportedRuntimeSelectionState;
if (!HypotheticalSelectionState.IsStructurallyValid())
{
HypotheticalSelectionState.SurfaceId = RuntimeSurface.SurfaceId;
HypotheticalSelectionState.SurfaceKind = RuntimeSurface.SurfaceKind;
}
const FString SelectedValue = DispatchEntry.GeneratedChoiceSelectedValue.IsEmpty()
? (DispatchEntry.OptionValue.IsEmpty()
? DispatchEntry.OptionId
: DispatchEntry.OptionValue)
: DispatchEntry.GeneratedChoiceSelectedValue;
const FString DisplayValue = DispatchEntry.GeneratedChoiceDisplayValue.IsEmpty()
? (DispatchEntry.Label.IsEmpty() ? SelectedValue : DispatchEntry.Label)
: DispatchEntry.GeneratedChoiceDisplayValue;
FHyperTwistTrainingImportedRuntimeSelectorChoice* ExistingChoice =
HypotheticalSelectionState.SelectorChoices.FindByPredicate(
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
{
return Candidate.SelectorId == SelectorId;
}
);
if (ExistingChoice != nullptr)
{
ExistingChoice->SelectorBinding = SelectorBinding;
ExistingChoice->SelectedValue = SelectedValue;
ExistingChoice->DisplayValue = DisplayValue.IsEmpty() ? SelectedValue : DisplayValue;
}
else
{
FHyperTwistTrainingImportedRuntimeSelectorChoice Choice;
Choice.SelectorId = SelectorId;
Choice.SelectorBinding = SelectorBinding;
Choice.SelectedValue = SelectedValue;
Choice.DisplayValue = DisplayValue.IsEmpty() ? SelectedValue : DisplayValue;
HypotheticalSelectionState.SelectorChoices.Add(MoveTemp(Choice));
}
Entry.HypotheticalSelectorSummaryLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->HypotheticalSelectorSummaryLine.IsEmpty()
? OptionRequestAlignmentSurface.CurrentSelectionPreviewLine
: MatchingRequestAlignmentEntry->HypotheticalSelectorSummaryLine;
Entry.ExecutePreviewLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->ExecutePreviewLine.IsEmpty()
? TEXT("Execute preview: unavailable.")
: MatchingRequestAlignmentEntry->ExecutePreviewLine;
if (MatchingRequestAlignmentEntry != nullptr)
{
Entry.UnresolvedSelectorsLine = MatchingRequestAlignmentEntry->UnresolvedSelectorsLine;
Entry.UnresolvedSelectorCount = MatchingRequestAlignmentEntry->UnresolvedSelectorCount;
Entry.bWouldLeaveSelectorsUnresolved =
MatchingRequestAlignmentEntry->bWouldLeaveSelectorsUnresolved;
}
else
{
Entry.UnresolvedSelectorsLine = BuildFallbackUnresolvedSelectorLine(
HypotheticalSelectionState,
Entry.UnresolvedSelectorCount
);
Entry.bWouldLeaveSelectorsUnresolved = Entry.UnresolvedSelectorCount > 0;
}
TArray<FString> CurrentToHypotheticalChangedSelectorLabels;
TArray<FString> StoredRequestToHypotheticalChangedSelectorLabels;
TArray<FString> CurrentToStoredRequestChangedSelectorLabels;
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
{
if (!Selector.IsStructurallyValid())
{
continue;
}
const FString SelectorLabel = DescribeSelector(Selector);
const FHyperTwistTrainingImportedRuntimeSelectorChoice* CurrentChoice =
FindSelectorChoice(
RunState.ImportedRuntimeSelectionState.SelectorChoices,
Selector.SelectorId
);
const FHyperTwistTrainingImportedRuntimeSelectorChoice* HypotheticalChoice =
FindSelectorChoice(
HypotheticalSelectionState.SelectorChoices,
Selector.SelectorId
);
const FHyperTwistTrainingImportedRuntimeSelectorChoice* StoredRequestChoice =
bHasComparableStoredRequest
? FindSelectorChoice(
LaunchRequest.LaunchConfig.SelectorChoices,
Selector.SelectorId
)
: nullptr;
FHyperTwistTrainingCoachGeneratedModeSelectorOptionSelectorDiffEntrySurface
SelectorDiffEntry;
SelectorDiffEntry.SelectorId = Selector.SelectorId;
SelectorDiffEntry.SelectorLabel = SelectorLabel;
SelectorDiffEntry.BindingLabel = Selector.SelectorBinding;
SelectorDiffEntry.CurrentSelectionValue =
DescribeChoiceValue(CurrentChoice, TEXT("unresolved"));
SelectorDiffEntry.HypotheticalSelectionValue =
DescribeChoiceValue(HypotheticalChoice, TEXT("unresolved"));
SelectorDiffEntry.StoredRequestSelectionValue =
DescribeChoiceValue(StoredRequestChoice, TEXT("not stored"));
SelectorDiffEntry.bIsTargetSelector = Selector.SelectorId == SelectorId;
SelectorDiffEntry.bHasCurrentSelection = CurrentChoice != nullptr;
SelectorDiffEntry.bHasHypotheticalSelection = HypotheticalChoice != nullptr;
SelectorDiffEntry.bHasStoredRequestSelection = StoredRequestChoice != nullptr;
SelectorDiffEntry.bCurrentMatchesHypothetical =
DoChoicesMatch(CurrentChoice, HypotheticalChoice);
SelectorDiffEntry.bStoredRequestMatchesHypothetical =
DoChoicesMatch(StoredRequestChoice, HypotheticalChoice);
SelectorDiffEntry.bCurrentMatchesStoredRequest =
DoChoicesMatch(CurrentChoice, StoredRequestChoice);
SelectorDiffEntry.CurrentToHypotheticalDeltaLine = BuildPairDeltaLine(
TEXT("Current -> hypothetical"),
SelectorDiffEntry.CurrentSelectionValue,
SelectorDiffEntry.HypotheticalSelectionValue,
SelectorDiffEntry.bCurrentMatchesHypothetical
);
SelectorDiffEntry.StoredRequestToHypotheticalDeltaLine = BuildPairDeltaLine(
TEXT("Stored request -> hypothetical"),
SelectorDiffEntry.StoredRequestSelectionValue,
SelectorDiffEntry.HypotheticalSelectionValue,
SelectorDiffEntry.bStoredRequestMatchesHypothetical
);
SelectorDiffEntry.CurrentToStoredRequestDeltaLine = BuildPairDeltaLine(
TEXT("Current -> stored request"),
SelectorDiffEntry.CurrentSelectionValue,
SelectorDiffEntry.StoredRequestSelectionValue,
SelectorDiffEntry.bCurrentMatchesStoredRequest
);
if (!SelectorDiffEntry.bCurrentMatchesHypothetical)
{
++Entry.CurrentToHypotheticalChangedSelectorCount;
CurrentToHypotheticalChangedSelectorLabels.Add(SelectorLabel);
}
if (!SelectorDiffEntry.bStoredRequestMatchesHypothetical)
{
++Entry.StoredRequestToHypotheticalChangedSelectorCount;
StoredRequestToHypotheticalChangedSelectorLabels.Add(SelectorLabel);
}
if (!SelectorDiffEntry.bCurrentMatchesStoredRequest)
{
++Entry.CurrentToStoredRequestChangedSelectorCount;
CurrentToStoredRequestChangedSelectorLabels.Add(SelectorLabel);
}
Entry.SelectorDiffEntries.Add(MoveTemp(SelectorDiffEntry));
}
Entry.bHasCurrentToHypotheticalDelta =
Entry.CurrentToHypotheticalChangedSelectorCount > 0;
Entry.bHasStoredRequestToHypotheticalDelta =
Entry.StoredRequestToHypotheticalChangedSelectorCount > 0;
Entry.bHasCurrentToStoredRequestDelta =
Entry.CurrentToStoredRequestChangedSelectorCount > 0;
Entry.CurrentToHypotheticalDeltaLine = BuildAggregateDeltaLine(
TEXT("Current -> hypothetical"),
CurrentToHypotheticalChangedSelectorLabels
);
Entry.StoredRequestToHypotheticalDeltaLine = BuildAggregateDeltaLine(
TEXT("Stored request -> hypothetical"),
StoredRequestToHypotheticalChangedSelectorLabels
);
Entry.CurrentToStoredRequestDeltaLine = BuildAggregateDeltaLine(
TEXT("Current -> stored request"),
CurrentToStoredRequestChangedSelectorLabels
);
if (Entry.bWouldLeaveSelectorsUnresolved)
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::
UnresolvedAfterChoice;
++Surface.UnresolvedAfterChoiceOptionCount;
}
else if (!Entry.bHasCurrentToHypotheticalDelta
&& !Entry.bHasStoredRequestToHypotheticalDelta)
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::NoSelectorDelta;
++Surface.NoSelectorDeltaOptionCount;
}
else if (Entry.bHasCurrentToHypotheticalDelta
&& !Entry.bHasStoredRequestToHypotheticalDelta)
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::
CurrentDeltaOnly;
++Surface.CurrentDeltaOnlyOptionCount;
}
else if (!Entry.bHasCurrentToHypotheticalDelta
&& Entry.bHasStoredRequestToHypotheticalDelta)
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::
StoredRequestDeltaOnly;
++Surface.StoredRequestDeltaOnlyOptionCount;
}
else
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::
CurrentAndStoredDelta;
++Surface.CurrentAndStoredDeltaOptionCount;
}
const FString RequestAlignmentDetail =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->DetailLine.IsEmpty()
? TEXT("Request alignment detail: n/a")
: MatchingRequestAlignmentEntry->DetailLine;
const FString StoredRequestAlignmentLine =
MatchingRequestAlignmentEntry == nullptr
|| MatchingRequestAlignmentEntry->StoredRequestAlignmentLine.IsEmpty()
? TEXT("Stored request alignment: n/a")
: MatchingRequestAlignmentEntry->StoredRequestAlignmentLine;
Entry.DetailLine = FString::Printf(
TEXT("%s | %s | %s"),
*Entry.CurrentToStoredRequestDeltaLine,
*StoredRequestAlignmentLine,
*RequestAlignmentDetail
);
Entry.bCanPreview = true;
if (Entry.bHasCurrentToHypotheticalDelta)
{
++CurrentToHypotheticalDeltaOptionCount;
}
if (Entry.bHasStoredRequestToHypotheticalDelta)
{
++StoredRequestToHypotheticalDeltaOptionCount;
}
if (Entry.bHasCurrentToStoredRequestDelta)
{
++CurrentToStoredRequestDeltaOptionCount;
}
}
else
{
Entry.StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::Unavailable;
Entry.HypotheticalSelectorSummaryLine = TEXT("Hypothetical selectors: unavailable");
Entry.CurrentToHypotheticalDeltaLine = TEXT("Current -> hypothetical: unavailable");
Entry.StoredRequestToHypotheticalDeltaLine =
TEXT("Stored request -> hypothetical: unavailable");
Entry.CurrentToStoredRequestDeltaLine = TEXT("Current -> stored request: unavailable");
Entry.UnresolvedSelectorsLine = TEXT("Unresolved after choice: unavailable");
Entry.ExecutePreviewLine = TEXT("Execute preview: unavailable.");
Entry.DetailLine =
TEXT("Stored-request delta preview is unavailable because this selector option cannot dispatch from the active generated-mode surface.");
Entry.bCanPreview = false;
++Surface.UnavailableOptionCount;
}
if (Entry.bMatchesCurrentSelection)
{
Surface.bCurrentSelectionHasCurrentToHypotheticalDelta =
Entry.bHasCurrentToHypotheticalDelta;
Surface.bCurrentSelectionHasStoredRequestToHypotheticalDelta =
Entry.bHasStoredRequestToHypotheticalDelta;
Surface.bCurrentSelectionHasCurrentToStoredRequestDelta =
Entry.bHasCurrentToStoredRequestDelta;
Surface.bCurrentSelectionWouldLeaveSelectorsUnresolved =
Entry.bWouldLeaveSelectorsUnresolved;
Surface.CurrentSelectionDeltaLine = Entry.bStartsOwnedCaseImmediately
? FString::Printf(
TEXT("%s | %s"),
*Entry.CurrentToHypotheticalDeltaLine,
*Entry.ExecutePreviewLine
)
: FString::Printf(
TEXT("%s | %s | %s | %s"),
*Entry.CurrentToHypotheticalDeltaLine,
*Entry.StoredRequestToHypotheticalDeltaLine,
*Entry.CurrentToStoredRequestDeltaLine,
Entry.bWouldLeaveSelectorsUnresolved
? *Entry.UnresolvedSelectorsLine
: *Entry.ExecutePreviewLine
);
}
Surface.OptionEntries.Add(MoveTemp(Entry));
}
Surface.bHasStoredRequestDeltaOptions = PreviewableGeneratedOptionCount > 0;
Surface.bHasCurrentToHypotheticalDeltaOptions = CurrentToHypotheticalDeltaOptionCount > 0;
Surface.bHasStoredRequestToHypotheticalDeltaOptions =
StoredRequestToHypotheticalDeltaOptionCount > 0;
Surface.bHasCurrentToStoredRequestDeltaOptions =
CurrentToStoredRequestDeltaOptionCount > 0;
Surface.bHasUnresolvedAfterChoiceOptions = Surface.UnresolvedAfterChoiceOptionCount > 0;
Surface.SelectorDiffSummaryLine = Surface.OptionEntries.Num() > 0
? FString::Printf(
TEXT("Selector diff: %d no-delta | %d current-only | %d stored-only | %d current+stored | %d unresolved | %d direct-launch | %d unavailable"),
Surface.NoSelectorDeltaOptionCount,
Surface.CurrentDeltaOnlyOptionCount,
Surface.StoredRequestDeltaOnlyOptionCount,
Surface.CurrentAndStoredDeltaOptionCount,
Surface.UnresolvedAfterChoiceOptionCount,
Surface.DirectOwnedCaseStartOptionCount,
Surface.UnavailableOptionCount
)
: TEXT("Selector diff: no imported options");
Surface.CurrentToHypotheticalSummaryLine = PreviewableGeneratedOptionCount > 0
? FString::Printf(
TEXT("Current -> hypothetical: %d option%s change at least one selector; %d keep selector state unchanged."),
CurrentToHypotheticalDeltaOptionCount,
CurrentToHypotheticalDeltaOptionCount == 1 ? TEXT("") : TEXT("s"),
PreviewableGeneratedOptionCount - CurrentToHypotheticalDeltaOptionCount
)
: TEXT("Current -> hypothetical: no previewable generated-choice options");
Surface.StoredRequestToHypotheticalSummaryLine = PreviewableGeneratedOptionCount > 0
? FString::Printf(
TEXT("Stored request -> hypothetical: %d option%s diverge from the stored request baseline; %d stay aligned."),
StoredRequestToHypotheticalDeltaOptionCount,
StoredRequestToHypotheticalDeltaOptionCount == 1 ? TEXT("") : TEXT("s"),
PreviewableGeneratedOptionCount - StoredRequestToHypotheticalDeltaOptionCount
)
: TEXT("Stored request -> hypothetical: no previewable generated-choice options");
Surface.CurrentToStoredRequestSummaryLine = PreviewableGeneratedOptionCount > 0
? FString::Printf(
TEXT("Current -> stored request: %d option%s retain at least one selector delta from the stored request baseline."),
CurrentToStoredRequestDeltaOptionCount,
CurrentToStoredRequestDeltaOptionCount == 1 ? TEXT("") : TEXT("s")
)
: TEXT("Current -> stored request: no previewable generated-choice options");
if (Surface.CurrentSelectionDeltaLine.IsEmpty())
{
if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
{
Surface.CurrentSelectionDeltaLine = OptionMenuSurface.UnresolvedSelectionLine;
}
else if (OptionMenuSurface.bHasSelection)
{
Surface.CurrentSelectionDeltaLine =
TEXT("Current selection has no additional stored-request delta preview.");
}
else if (OptionMenuSurface.bHasSelector)
{
Surface.CurrentSelectionDeltaLine = TEXT("Current selection: unresolved");
}
else
{
Surface.CurrentSelectionDeltaLine = TEXT("Current selection: n/a");
}
}
if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option stored-request delta exposes %d no-delta option%s, %d current-delta-only option%s, %d stored-request-delta-only option%s, %d current-and-stored-delta option%s, %d unresolved option%s, %d direct-launch option%s, and %d unavailable option%s."),
Surface.NoSelectorDeltaOptionCount,
Surface.NoSelectorDeltaOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.CurrentDeltaOnlyOptionCount,
Surface.CurrentDeltaOnlyOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.StoredRequestDeltaOnlyOptionCount,
Surface.StoredRequestDeltaOnlyOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.CurrentAndStoredDeltaOptionCount,
Surface.CurrentAndStoredDeltaOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.UnresolvedAfterChoiceOptionCount,
Surface.UnresolvedAfterChoiceOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.DirectOwnedCaseStartOptionCount,
Surface.DirectOwnedCaseStartOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.UnavailableOptionCount,
Surface.UnavailableOptionCount == 1 ? TEXT("") : TEXT("s")
);
}
else if (OptionMenuSurface.bHasSelector)
{
Surface.StatusLine =
TEXT("Generated-mode selector option stored-request delta is active, but this selector does not expose imported options.");
}
else
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option stored-request delta is not currently available.")
: OptionMenuSurface.StatusLine;
}
const FString SelectorSummary = Surface.SelectorLabel.IsEmpty()
? (Surface.SelectorId.IsEmpty() ? FString(TEXT("n/a")) : Surface.SelectorId)
: Surface.SelectorLabel;
const FString BindingSummary = Surface.BindingLabel.IsEmpty()
? FString(TEXT("n/a"))
: Surface.BindingLabel;
Surface.DetailLine = FString::Printf(
TEXT("Dispatch: %s | Request alignment: %s | Selector: %s | Binding: %s"),
OptionDispatchSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionDispatchSurface.StatusLine,
OptionRequestAlignmentSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionRequestAlignmentSurface.StatusLine,
*SelectorSummary,
*BindingSummary
);
return Surface;
}

View file

@ -792,6 +792,27 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionRequ
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
OptionRequestAlignmentSurface =
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionDispatchSurface,
OptionRequestAlignmentSurface
);
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -773,6 +773,27 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionReq
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
OptionRequestAlignmentSurface =
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionDispatchSurface,
OptionRequestAlignmentSurface
);
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
const int32 MaxCases
)

View file

@ -735,4 +735,15 @@ public:
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface&
OptionExecutionImpactSurface
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
DeriveCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
OptionRequestAlignmentSurface
);
};

View file

@ -276,6 +276,10 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,

View file

@ -257,6 +257,10 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);

View file

@ -103,6 +103,19 @@ enum class EHyperTwistTrainingCoachGeneratedModeOptionRequestAlignmentKind : uin
Unavailable UMETA(DisplayName = "Unavailable")
};
UENUM(BlueprintType)
enum class EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind : uint8
{
None UMETA(DisplayName = "None"),
DirectOwnedCaseStart UMETA(DisplayName = "Direct Owned Case Start"),
NoSelectorDelta UMETA(DisplayName = "No Selector Delta"),
CurrentDeltaOnly UMETA(DisplayName = "Current Delta Only"),
StoredRequestDeltaOnly UMETA(DisplayName = "Stored-Request Delta Only"),
CurrentAndStoredDelta UMETA(DisplayName = "Current And Stored Delta"),
UnresolvedAfterChoice UMETA(DisplayName = "Unresolved After Choice"),
Unavailable UMETA(DisplayName = "Unavailable")
};
UENUM(BlueprintType)
enum class EHyperTwistTrainingPromptKind : uint8
{
@ -9229,3 +9242,257 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurfac
|| bHasRequestAlignmentOptions;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionSelectorDiffEntrySurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectorId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectorLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BindingLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentSelectionValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalSelectionValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StoredRequestSelectionValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToHypotheticalDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StoredRequestToHypotheticalDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToStoredRequestDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bIsTargetSelector = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCurrentSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasHypotheticalSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasStoredRequestSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentMatchesHypothetical = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bStoredRequestMatchesHypothetical = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentMatchesStoredRequest = false;
bool IsStructurallyValid() const
{
return !SelectorId.IsEmpty()
|| !SelectorLabel.IsEmpty()
|| !CurrentToHypotheticalDeltaLine.IsEmpty()
|| bHasCurrentSelection
|| bHasHypotheticalSelection
|| bHasStoredRequestSelection;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaEntrySurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OptionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Label;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OptionValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind StoredRequestDeltaKind =
EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalSelectorSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToHypotheticalDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StoredRequestToHypotheticalDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToStoredRequestDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString UnresolvedSelectorsLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ExecutePreviewLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionSelectorDiffEntrySurface>
SelectorDiffEntries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CurrentToHypotheticalChangedSelectorCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StoredRequestToHypotheticalChangedSelectorCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CurrentToStoredRequestChangedSelectorCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 UnresolvedSelectorCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bMatchesCurrentSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bStartsOwnedCaseImmediately = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCurrentToHypotheticalDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasStoredRequestToHypotheticalDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCurrentToStoredRequestDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bWouldLeaveSelectorsUnresolved = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCanPreview = false;
bool IsStructurallyValid() const
{
return !OptionId.IsEmpty()
|| !Label.IsEmpty()
|| !HypotheticalSelectorSummaryLine.IsEmpty()
|| StoredRequestDeltaKind
!= EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind::None
|| bCanPreview;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectorId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectorLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BindingLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Headline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectorDiffSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToHypotheticalSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StoredRequestToHypotheticalSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentToStoredRequestSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentSelectionDeltaLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaEntrySurface>
OptionEntries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 NoSelectorDeltaOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CurrentDeltaOnlyOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StoredRequestDeltaOnlyOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CurrentAndStoredDeltaOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 UnresolvedAfterChoiceOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DirectOwnedCaseStartOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 UnavailableOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelector = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasStoredRequestDeltaOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCurrentToHypotheticalDeltaOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasStoredRequestToHypotheticalDeltaOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCurrentToStoredRequestDeltaOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasUnresolvedAfterChoiceOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasCurrentToHypotheticalDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasStoredRequestToHypotheticalDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasCurrentToStoredRequestDelta = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionWouldLeaveSelectorsUnresolved = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !SelectorId.IsEmpty()
|| OptionEntries.Num() > 0
|| bHasSelector
|| bHasStoredRequestDeltaOptions;
}
};

View file

@ -68,8 +68,8 @@ Current correction call from the source-exposed audit and current execution refr
- the imported generated-mode gap is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code
- 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 latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option request-alignment / execute-preview surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render hypothetical selector summary, stored-request alignment, unresolved-selector preview, and execute preview for a chosen option without diffing cached selector choices or launch-request payloads directly
- the current next bounded packet is to expose one thin generated-mode selector option stored-request delta / selector-diff surface on those same two consumer surfaces so gameplay/Blueprint callers can render the exact current-vs-hypothetical-vs-stored selector change set for a chosen option without diffing cached selector choices or launch-request payloads directly
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option stored-request delta / selector-diff surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render the exact current-vs-hypothetical-vs-stored selector change set for a chosen option without diffing cached selector choices or launch-request payloads directly
- the current next bounded packet is to expose one thin generated-mode selector option hypothetical launch-request payload / refresh-preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render the concrete selector-choice payload, stored-request reuse posture, and request-refresh preview for a chosen option without rebuilding launch-config payloads directly
- 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

View file

@ -0,0 +1,105 @@
# HyperTwist Phase 4 gameplay generated-mode selector option stored-request delta / selector-diff surface packet
Created on `2026-05-08`
Status:
- first-party HyperTwist packet
- bounded Phase `4` gameplay-facing consumer slice
## Purpose
This packet closes the next thin gameplay-facing consumer seam above the panel-widget and session-actor generated-mode selector option request-alignment surface by exposing one parameterized option stored-request delta / selector-diff surface for a chosen selector id.
The open tasks were:
- stop forcing gameplay / Blueprint callers to diff cached selector choices and stored launch-request payloads just to render the exact selector change set behind a chosen imported option
- expose the exact current-vs-hypothetical-vs-stored selector delta posture for each imported option, plus the current selections own delta posture
- keep the surface derived from already-cached gameplay consumer run, selector, and request state plus the already-landed option-detail, dispatch, and request-alignment surfaces instead of widening into backend logic
It is not:
- a selector-dispatch behavior change
- a launch-request persistence change
- a generated-mode backend execution change
- a broader gameplay UI composition pass
## Scope
Bounded lane:
- add first-party generated-mode selector option stored-request delta / selector-diff surfaces in shared training types
- add a coach-library helper that derives current-vs-hypothetical-vs-stored selector diffs from cached run state plus the already-landed option-detail, dispatch, and request-alignment surfaces
- expose that parameterized stored-request delta surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
- keep the derivation presentation-only and rooted in the already-owned selector-choice / launch-request rules
Out of scope:
- changing selector-choice apply behavior
- changing direct owned-case launch behavior
- changing stored launch-request creation, refresh, or execute behavior
- widening into higher-level UI composition or dashboard-only work
## Why this was the right next packet
Before this slice:
- gameplay-facing panel/session consumers already exposed truthful generated-mode option request-alignment / execute-preview posture
- those same consumers already exposed truthful generated-mode option dispatch posture
But one thin consumer seam was still open:
- callers could tell whether a chosen option stayed aligned with the stored request, required refresh, or left selectors unresolved
- but they still had to diff the concrete selector choices themselves to explain exactly what changed between current, hypothetical, and stored baselines
That meant:
- request-alignment posture was thin and reusable
- but exact selector-diff reasoning still leaked cached launch-request and selector-choice comparison work into callers
So the next bounded move was:
- keep the already-landed option-detail, dispatch, and request-alignment surfaces unchanged
- and expose one narrow stored-request delta / selector-diff surface above those already-cached surfaces
## What landed
Primary code changes:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- added `EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionSelectorDiffEntrySurface`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaEntrySurface`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
- added `DeriveCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(...)`
- stored-request delta preview is derived from cached run state plus the already-landed option-detail, dispatch, and request-alignment surfaces
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId)`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId)`
Surface behavior:
- reports the exact current-vs-hypothetical-vs-stored selector change set for each imported option on the chosen selector
- exposes per-selector diff rows with current, hypothetical, and stored request values plus pairwise delta lines
- classifies each option as no-delta, current-delta-only, stored-request-delta-only, current-and-stored delta, unresolved-after-choice, direct-launch, or unavailable
- reports a compact stored-request delta preview for the current cached selection on the chosen selector
- keeps the surface presentation-only and compatible with the already-landed generated-mode option-detail, dispatch, and request-alignment surfaces
## Validation
Validation target:
- full `Development Editor|Win64` build of `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln`
Result:
- passed with `0` warnings and `0` errors
## Follow-on
The next bounded packet on this same gameplay-facing consumer lane should stay thin and expose one generated-mode selector option hypothetical launch-request payload / refresh-preview surface so callers can render the concrete selector-choice payload, stored-request reuse posture, and request-refresh preview for a chosen option without rebuilding launch-config payloads directly.