Expose gameplay selector launch-request preview surface
This commit is contained in:
parent
71d29c331c
commit
8fff384597
9 changed files with 1292 additions and 2 deletions
|
|
@ -9591,3 +9591,875 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionStoredReq
|
|||
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
const FHyperTwistTrainingRunState& RunState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
|
||||
OptionRequestAlignmentSurface
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface Surface;
|
||||
Surface.SelectorId = SelectorId;
|
||||
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
|
||||
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
|
||||
Surface.Headline = TEXT("Generated Mode Selector Option Launch-Request Preview");
|
||||
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
|
||||
|
||||
if (!SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
|
||||
? FString::Printf(
|
||||
TEXT("Generated Mode Selector Option Launch-Request Preview: %s"),
|
||||
*SelectorId
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("Generated Mode Selector Option Launch-Request Preview: %s"),
|
||||
*OptionMenuSurface.SelectorLabel
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option launch-request preview requires a selector id from the active selector roster.");
|
||||
Surface.PayloadSummaryLine = TEXT("Payload preview: n/a");
|
||||
Surface.StoredRequestReuseSummaryLine = TEXT("Stored request reuse: n/a");
|
||||
Surface.RefreshPreviewSummaryLine = TEXT("Refresh preview: n/a");
|
||||
Surface.CurrentSelectionPayloadLine = 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 FHyperTwistTrainingCase* SeedCase = RunState.ActiveDeck.Cases.FindByPredicate(
|
||||
[](const FHyperTwistTrainingCase& Candidate)
|
||||
{
|
||||
return Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
const FString FallbackPuzzleId = SeedCase != nullptr ? SeedCase->PuzzleId : FString();
|
||||
|
||||
const auto DescribeDeliveryMode = [](
|
||||
const EHyperTwistTrainingDeliveryMode DeliveryMode
|
||||
) -> FString
|
||||
{
|
||||
switch (DeliveryMode)
|
||||
{
|
||||
case EHyperTwistTrainingDeliveryMode::Timer:
|
||||
return TEXT("Timer");
|
||||
case EHyperTwistTrainingDeliveryMode::VirtualCube:
|
||||
return TEXT("Virtual Cube");
|
||||
case EHyperTwistTrainingDeliveryMode::SmartDevice:
|
||||
return TEXT("Smart Device");
|
||||
case EHyperTwistTrainingDeliveryMode::RecognitionAssisted:
|
||||
return TEXT("Recognition Assisted");
|
||||
case EHyperTwistTrainingDeliveryMode::CoachReviewed:
|
||||
return TEXT("Coach Reviewed");
|
||||
default:
|
||||
return TEXT("Unknown");
|
||||
}
|
||||
};
|
||||
|
||||
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 DoSelectorChoiceSetsMatch = [&RuntimeSurface, &FindSelectorChoice](
|
||||
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& LeftChoices,
|
||||
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& RightChoices
|
||||
) -> bool
|
||||
{
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
|
||||
{
|
||||
if (!Selector.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* LeftChoice =
|
||||
FindSelectorChoice(LeftChoices, Selector.SelectorId);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* RightChoice =
|
||||
FindSelectorChoice(RightChoices, Selector.SelectorId);
|
||||
if (LeftChoice == nullptr || RightChoice == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (LeftChoice->SelectedValue != RightChoice->SelectedValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto DoesLaunchRequestMatchLaunchConfig = [&DoSelectorChoiceSetsMatch](
|
||||
const FHyperTwistTrainingImportedGeneratedModeLaunchRequest& InLaunchRequest,
|
||||
const FHyperTwistTrainingImportedGeneratedModeLaunchConfig& ExpectedLaunchConfig
|
||||
) -> bool
|
||||
{
|
||||
return InLaunchRequest.IsStructurallyValid()
|
||||
&& InLaunchRequest.LaunchConfig.DeckId == ExpectedLaunchConfig.DeckId
|
||||
&& InLaunchRequest.LaunchConfig.SurfaceId == ExpectedLaunchConfig.SurfaceId
|
||||
&& InLaunchRequest.LaunchConfig.RuntimeModeId.Equals(
|
||||
ExpectedLaunchConfig.RuntimeModeId,
|
||||
ESearchCase::IgnoreCase
|
||||
)
|
||||
&& DoSelectorChoiceSetsMatch(
|
||||
InLaunchRequest.LaunchConfig.SelectorChoices,
|
||||
ExpectedLaunchConfig.SelectorChoices
|
||||
);
|
||||
};
|
||||
|
||||
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 BuildPayloadIdentityLine = [&DescribeDeliveryMode](
|
||||
const FString& DeckId,
|
||||
const FString& TrainingSessionId,
|
||||
const EHyperTwistTrainingDeliveryMode DeliveryMode,
|
||||
const FString& SurfaceId,
|
||||
const FString& SurfaceLabel,
|
||||
const FString& RuntimeModeId,
|
||||
const FString& GeneratorType,
|
||||
const FString& PuzzleId
|
||||
) -> FString
|
||||
{
|
||||
const FString DeckSummary = DeckId.IsEmpty() ? TEXT("n/a") : DeckId;
|
||||
const FString SessionSummary = TrainingSessionId.IsEmpty() ? TEXT("n/a") : TrainingSessionId;
|
||||
const FString SurfaceSummary = SurfaceLabel.IsEmpty()
|
||||
? (SurfaceId.IsEmpty() ? FString(TEXT("n/a")) : SurfaceId)
|
||||
: FString::Printf(
|
||||
TEXT("%s [%s]"),
|
||||
*SurfaceLabel,
|
||||
SurfaceId.IsEmpty() ? TEXT("n/a") : *SurfaceId
|
||||
);
|
||||
const FString RuntimeModeSummary = RuntimeModeId.IsEmpty() ? TEXT("n/a") : RuntimeModeId;
|
||||
const FString GeneratorSummary = GeneratorType.IsEmpty() ? TEXT("n/a") : GeneratorType;
|
||||
const FString PuzzleSummary = PuzzleId.IsEmpty() ? TEXT("n/a") : PuzzleId;
|
||||
return FString::Printf(
|
||||
TEXT("Payload: deck=%s | session=%s | delivery=%s | surface=%s | mode=%s | generator=%s | puzzle=%s"),
|
||||
*DeckSummary,
|
||||
*SessionSummary,
|
||||
*DescribeDeliveryMode(DeliveryMode),
|
||||
*SurfaceSummary,
|
||||
*RuntimeModeSummary,
|
||||
*GeneratorSummary,
|
||||
*PuzzleSummary
|
||||
);
|
||||
};
|
||||
|
||||
const auto BuildSelectorChoiceEntriesAndSummary =
|
||||
[&RuntimeSurface,
|
||||
&SelectorId,
|
||||
&RunState,
|
||||
&LaunchRequest,
|
||||
&bHasComparableStoredRequest,
|
||||
&DescribeSelector,
|
||||
&FindSelectorChoice,
|
||||
&DoChoicesMatch,
|
||||
&DescribeChoiceValue](
|
||||
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& PreviewChoices,
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface>&
|
||||
OutEntries,
|
||||
int32& OutResolvedChoiceCount
|
||||
) -> FString
|
||||
{
|
||||
OutEntries.Reset();
|
||||
OutResolvedChoiceCount = 0;
|
||||
|
||||
TSet<FString> AddedSelectorIds;
|
||||
TArray<FString> SelectorParts;
|
||||
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
|
||||
{
|
||||
if (!Selector.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* PreviewChoice =
|
||||
FindSelectorChoice(PreviewChoices, Selector.SelectorId);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* CurrentChoice =
|
||||
FindSelectorChoice(
|
||||
RunState.ImportedRuntimeSelectionState.SelectorChoices,
|
||||
Selector.SelectorId
|
||||
);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* StoredRequestChoice =
|
||||
bHasComparableStoredRequest
|
||||
? FindSelectorChoice(LaunchRequest.LaunchConfig.SelectorChoices, Selector.SelectorId)
|
||||
: nullptr;
|
||||
const FString ChoiceValueSummary = DescribeChoiceValue(PreviewChoice, TEXT("unresolved"));
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface
|
||||
ChoiceEntry;
|
||||
ChoiceEntry.SelectorId = Selector.SelectorId;
|
||||
ChoiceEntry.SelectorLabel = DescribeSelector(Selector);
|
||||
ChoiceEntry.BindingLabel = Selector.SelectorBinding;
|
||||
ChoiceEntry.SelectedValue = PreviewChoice == nullptr ? FString() : PreviewChoice->SelectedValue;
|
||||
ChoiceEntry.DisplayValue = ChoiceValueSummary;
|
||||
ChoiceEntry.PayloadLine = FString::Printf(
|
||||
TEXT("Payload choice: %s=%s"),
|
||||
*ChoiceEntry.SelectorLabel,
|
||||
*ChoiceValueSummary
|
||||
);
|
||||
ChoiceEntry.bIsTargetSelector = Selector.SelectorId == SelectorId;
|
||||
ChoiceEntry.bHasChoice = PreviewChoice != nullptr;
|
||||
ChoiceEntry.bMatchesCurrentSelectionChoice = DoChoicesMatch(CurrentChoice, PreviewChoice);
|
||||
ChoiceEntry.bMatchesStoredRequestChoice =
|
||||
bHasComparableStoredRequest && DoChoicesMatch(StoredRequestChoice, PreviewChoice);
|
||||
|
||||
if (PreviewChoice != nullptr)
|
||||
{
|
||||
++OutResolvedChoiceCount;
|
||||
}
|
||||
|
||||
SelectorParts.Add(FString::Printf(
|
||||
TEXT("%s=%s"),
|
||||
*ChoiceEntry.SelectorLabel,
|
||||
*ChoiceValueSummary
|
||||
));
|
||||
AddedSelectorIds.Add(Selector.SelectorId);
|
||||
OutEntries.Add(MoveTemp(ChoiceEntry));
|
||||
}
|
||||
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelectorChoice& PreviewChoice : PreviewChoices)
|
||||
{
|
||||
if (!PreviewChoice.IsStructurallyValid()
|
||||
|| AddedSelectorIds.Contains(PreviewChoice.SelectorId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* CurrentChoice =
|
||||
FindSelectorChoice(
|
||||
RunState.ImportedRuntimeSelectionState.SelectorChoices,
|
||||
PreviewChoice.SelectorId
|
||||
);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* StoredRequestChoice =
|
||||
bHasComparableStoredRequest
|
||||
? FindSelectorChoice(
|
||||
LaunchRequest.LaunchConfig.SelectorChoices,
|
||||
PreviewChoice.SelectorId
|
||||
)
|
||||
: nullptr;
|
||||
const FString ChoiceValueSummary = DescribeChoiceValue(&PreviewChoice, TEXT("unresolved"));
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface
|
||||
ChoiceEntry;
|
||||
ChoiceEntry.SelectorId = PreviewChoice.SelectorId;
|
||||
ChoiceEntry.SelectorLabel = PreviewChoice.SelectorBinding.IsEmpty()
|
||||
? PreviewChoice.SelectorId
|
||||
: PreviewChoice.SelectorBinding;
|
||||
ChoiceEntry.BindingLabel = PreviewChoice.SelectorBinding;
|
||||
ChoiceEntry.SelectedValue = PreviewChoice.SelectedValue;
|
||||
ChoiceEntry.DisplayValue = ChoiceValueSummary;
|
||||
ChoiceEntry.PayloadLine = FString::Printf(
|
||||
TEXT("Payload choice: %s=%s"),
|
||||
*ChoiceEntry.SelectorLabel,
|
||||
*ChoiceValueSummary
|
||||
);
|
||||
ChoiceEntry.bIsTargetSelector = PreviewChoice.SelectorId == SelectorId;
|
||||
ChoiceEntry.bHasChoice = true;
|
||||
ChoiceEntry.bMatchesCurrentSelectionChoice = DoChoicesMatch(CurrentChoice, &PreviewChoice);
|
||||
ChoiceEntry.bMatchesStoredRequestChoice =
|
||||
bHasComparableStoredRequest && DoChoicesMatch(StoredRequestChoice, &PreviewChoice);
|
||||
|
||||
++OutResolvedChoiceCount;
|
||||
SelectorParts.Add(FString::Printf(
|
||||
TEXT("%s=%s"),
|
||||
*ChoiceEntry.SelectorLabel,
|
||||
*ChoiceValueSummary
|
||||
));
|
||||
OutEntries.Add(MoveTemp(ChoiceEntry));
|
||||
}
|
||||
|
||||
return SelectorParts.Num() > 0
|
||||
? FString::Printf(
|
||||
TEXT("Selector payload: %s"),
|
||||
*FString::Join(SelectorParts, TEXT(" | "))
|
||||
)
|
||||
: TEXT("Selector payload: none");
|
||||
};
|
||||
|
||||
const auto BuildFallbackUnresolvedSelectorLine = [&RuntimeSurface, &DescribeSelector, &FindSelectorChoice](
|
||||
const FHyperTwistTrainingImportedRuntimeSelectionState& SelectionState,
|
||||
int32& OutUnresolvedSelectorCount
|
||||
) -> FString
|
||||
{
|
||||
OutUnresolvedSelectorCount = 0;
|
||||
TArray<FString> UnresolvedLabels;
|
||||
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
|
||||
{
|
||||
if (!Selector.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FindSelectorChoice(SelectionState.SelectorChoices, Selector.SelectorId) != nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++OutUnresolvedSelectorCount;
|
||||
UnresolvedLabels.Add(DescribeSelector(Selector));
|
||||
}
|
||||
|
||||
if (OutUnresolvedSelectorCount == 0)
|
||||
{
|
||||
return TEXT("Unresolved after choice: none");
|
||||
}
|
||||
|
||||
const int32 PreviewCount = FMath::Min(3, UnresolvedLabels.Num());
|
||||
TArray<FString> PreviewLabels;
|
||||
for (int32 Index = 0; Index < PreviewCount; ++Index)
|
||||
{
|
||||
PreviewLabels.Add(UnresolvedLabels[Index]);
|
||||
}
|
||||
|
||||
return FString::Printf(
|
||||
TEXT("Unresolved after choice: %s%s"),
|
||||
*FString::Join(PreviewLabels, TEXT(", ")),
|
||||
UnresolvedLabels.Num() > PreviewCount
|
||||
? *FString::Printf(TEXT(" (+%d more)"), UnresolvedLabels.Num() - PreviewCount)
|
||||
: TEXT("")
|
||||
);
|
||||
};
|
||||
|
||||
if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface)
|
||||
{
|
||||
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Generated-mode selector option launch-request preview is not currently available.")
|
||||
: OptionMenuSurface.StatusLine;
|
||||
Surface.PayloadSummaryLine = TEXT("Payload preview: n/a");
|
||||
Surface.StoredRequestReuseSummaryLine = TEXT("Stored request reuse: n/a");
|
||||
Surface.RefreshPreviewSummaryLine = TEXT("Refresh preview: n/a");
|
||||
Surface.CurrentSelectionPayloadLine = OptionMenuSurface.bHasUnresolvedSelectionDetail
|
||||
? OptionMenuSurface.UnresolvedSelectionLine
|
||||
: (OptionMenuSurface.bHasSelection
|
||||
? TEXT("Current selection remains cached, but no active launch-request 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;
|
||||
}
|
||||
|
||||
const FString FallbackPayloadIdentityLine = BuildPayloadIdentityLine(
|
||||
RunState.ActiveDeck.DeckId,
|
||||
RunState.Session.TrainingSessionId,
|
||||
RunState.Session.Mode,
|
||||
RuntimeSurface.SurfaceId,
|
||||
RuntimeSurface.SurfaceLabel,
|
||||
RuntimeSurface.RuntimeModeId,
|
||||
RuntimeSurface.GeneratorType,
|
||||
FallbackPuzzleId
|
||||
);
|
||||
|
||||
int32 PreviewableGeneratedOptionCount = 0;
|
||||
int32 PreviewablePayloadOptionCount = 0;
|
||||
|
||||
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchEntrySurface& DispatchEntry :
|
||||
OptionDispatchSurface.OptionEntries)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface Entry;
|
||||
Entry.OptionId = DispatchEntry.OptionId;
|
||||
Entry.Label = DispatchEntry.Label;
|
||||
Entry.OptionValue = DispatchEntry.OptionValue;
|
||||
Entry.bMatchesCurrentSelection = DispatchEntry.bMatchesCurrentSelection;
|
||||
Entry.HypotheticalDeckId = RunState.ActiveDeck.DeckId;
|
||||
Entry.HypotheticalTrainingSessionId = RunState.Session.TrainingSessionId;
|
||||
Entry.HypotheticalSurfaceId = RuntimeSurface.SurfaceId;
|
||||
Entry.HypotheticalSurfaceLabel = RuntimeSurface.SurfaceLabel;
|
||||
Entry.HypotheticalRuntimeModeId = RuntimeSurface.RuntimeModeId;
|
||||
Entry.HypotheticalGeneratorType = RuntimeSurface.GeneratorType;
|
||||
Entry.HypotheticalPuzzleId = FallbackPuzzleId;
|
||||
Entry.HypotheticalDeliveryMode = RunState.Session.Mode;
|
||||
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentEntrySurface*
|
||||
MatchingRequestAlignmentEntry =
|
||||
OptionRequestAlignmentSurface.OptionEntries.FindByPredicate(
|
||||
[&DispatchEntry](
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentEntrySurface&
|
||||
Candidate
|
||||
)
|
||||
{
|
||||
return Candidate.OptionId == DispatchEntry.OptionId;
|
||||
}
|
||||
);
|
||||
const FString RequestAlignmentDetail =
|
||||
MatchingRequestAlignmentEntry == nullptr || MatchingRequestAlignmentEntry->DetailLine.IsEmpty()
|
||||
? TEXT("Request alignment detail: n/a")
|
||||
: MatchingRequestAlignmentEntry->DetailLine;
|
||||
|
||||
if (DispatchEntry.DispatchKind
|
||||
== EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind::DirectCaseLaunch
|
||||
&& DispatchEntry.bCanDispatch)
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
|
||||
DirectOwnedCaseStart;
|
||||
Entry.PayloadIdentityLine =
|
||||
TEXT("Payload: direct owned-case launch bypasses generated-mode launch-request staging.");
|
||||
Entry.SelectorPayloadLine =
|
||||
TEXT("Selector payload: direct owned-case launch bypasses generated-mode selector payload staging.");
|
||||
Entry.StoredRequestReuseLine =
|
||||
TEXT("Stored request reuse: direct owned-case launch bypasses stored launch-request reuse.");
|
||||
Entry.RefreshPreviewLine =
|
||||
TEXT("Refresh preview: direct owned-case launch bypasses generated-mode request refresh.");
|
||||
Entry.ExecutePreviewLine =
|
||||
MatchingRequestAlignmentEntry == nullptr
|
||||
|| MatchingRequestAlignmentEntry->ExecutePreviewLine.IsEmpty()
|
||||
? TEXT("Execute preview: direct owned-case launch starts immediately.")
|
||||
: MatchingRequestAlignmentEntry->ExecutePreviewLine;
|
||||
Entry.DetailLine = MatchingRequestAlignmentEntry == nullptr
|
||||
? DispatchEntry.DetailLine
|
||||
: FString::Printf(TEXT("%s | %s"), *Entry.PayloadIdentityLine, *RequestAlignmentDetail);
|
||||
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.ExecutePreviewLine =
|
||||
MatchingRequestAlignmentEntry == nullptr
|
||||
|| MatchingRequestAlignmentEntry->ExecutePreviewLine.IsEmpty()
|
||||
? TEXT("Execute preview: unavailable.")
|
||||
: MatchingRequestAlignmentEntry->ExecutePreviewLine;
|
||||
if (MatchingRequestAlignmentEntry != nullptr)
|
||||
{
|
||||
Entry.bWouldLeaveSelectorsUnresolved =
|
||||
MatchingRequestAlignmentEntry->bWouldLeaveSelectorsUnresolved;
|
||||
}
|
||||
|
||||
int32 UnresolvedSelectorCount = 0;
|
||||
FString UnresolvedSelectorsLine = MatchingRequestAlignmentEntry == nullptr
|
||||
? BuildFallbackUnresolvedSelectorLine(
|
||||
HypotheticalSelectionState,
|
||||
UnresolvedSelectorCount
|
||||
)
|
||||
: MatchingRequestAlignmentEntry->UnresolvedSelectorsLine;
|
||||
if (MatchingRequestAlignmentEntry != nullptr)
|
||||
{
|
||||
UnresolvedSelectorCount = MatchingRequestAlignmentEntry->UnresolvedSelectorCount;
|
||||
}
|
||||
|
||||
Entry.PayloadIdentityLine = FallbackPayloadIdentityLine;
|
||||
Entry.SelectorPayloadLine = BuildSelectorChoiceEntriesAndSummary(
|
||||
HypotheticalSelectionState.SelectorChoices,
|
||||
Entry.SelectorChoiceEntries,
|
||||
Entry.SelectorChoiceCount
|
||||
);
|
||||
|
||||
if (Entry.bWouldLeaveSelectorsUnresolved)
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
|
||||
UnresolvedAfterChoice;
|
||||
Entry.StoredRequestReuseLine =
|
||||
TEXT("Stored request reuse: no structurally valid launch payload is available while selector coverage remains incomplete.");
|
||||
Entry.RefreshPreviewLine =
|
||||
TEXT("Refresh preview: execute cannot stage a launch request until every required selector is resolved.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("%s | %s | %s"),
|
||||
*Entry.SelectorPayloadLine,
|
||||
*UnresolvedSelectorsLine,
|
||||
*RequestAlignmentDetail
|
||||
);
|
||||
Entry.bCanPreview = true;
|
||||
++Surface.UnresolvedAfterChoiceOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
FHyperTwistTrainingImportedGeneratedModeLaunchConfig HypotheticalLaunchConfig;
|
||||
if (!UHyperTwistTrainingCatalogLibrary::TryBuildImportedGeneratedModeLaunchConfig(
|
||||
RunState.ActiveDeck,
|
||||
HypotheticalSelectionState,
|
||||
RunState.Session,
|
||||
HypotheticalLaunchConfig))
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
|
||||
PayloadUnavailable;
|
||||
Entry.StoredRequestReuseLine =
|
||||
TEXT("Stored request reuse: no structurally valid launch payload could be derived from the resulting selector state.");
|
||||
Entry.RefreshPreviewLine =
|
||||
TEXT("Refresh preview: execute cannot preview request refresh because the resulting selector state is not structurally valid.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("%s | %s"),
|
||||
*Entry.SelectorPayloadLine,
|
||||
*RequestAlignmentDetail
|
||||
);
|
||||
Entry.bCanPreview = true;
|
||||
++Surface.PayloadUnavailableOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool bWouldReuseStoredRequest =
|
||||
MatchingRequestAlignmentEntry != nullptr
|
||||
? MatchingRequestAlignmentEntry->bWouldUseStoredRequestOnExecute
|
||||
: DoesLaunchRequestMatchLaunchConfig(
|
||||
LaunchRequest,
|
||||
HypotheticalLaunchConfig
|
||||
);
|
||||
const bool bWouldRequireLaunchRequestRefresh =
|
||||
MatchingRequestAlignmentEntry != nullptr
|
||||
? MatchingRequestAlignmentEntry->bWouldRequireLaunchRequestRefreshBeforeExecute
|
||||
: !bWouldReuseStoredRequest;
|
||||
|
||||
Entry.HypotheticalDeckId = HypotheticalLaunchConfig.DeckId;
|
||||
Entry.HypotheticalTrainingSessionId =
|
||||
HypotheticalLaunchConfig.TrainingSessionId;
|
||||
Entry.HypotheticalSurfaceId = HypotheticalLaunchConfig.SurfaceId;
|
||||
Entry.HypotheticalSurfaceLabel = HypotheticalLaunchConfig.SurfaceLabel;
|
||||
Entry.HypotheticalRuntimeModeId = HypotheticalLaunchConfig.RuntimeModeId;
|
||||
Entry.HypotheticalGeneratorType = HypotheticalLaunchConfig.GeneratorType;
|
||||
Entry.HypotheticalPuzzleId = HypotheticalLaunchConfig.PuzzleId;
|
||||
Entry.HypotheticalDeliveryMode = HypotheticalLaunchConfig.DeliveryMode;
|
||||
Entry.PayloadIdentityLine = BuildPayloadIdentityLine(
|
||||
HypotheticalLaunchConfig.DeckId,
|
||||
HypotheticalLaunchConfig.TrainingSessionId,
|
||||
HypotheticalLaunchConfig.DeliveryMode,
|
||||
HypotheticalLaunchConfig.SurfaceId,
|
||||
HypotheticalLaunchConfig.SurfaceLabel,
|
||||
HypotheticalLaunchConfig.RuntimeModeId,
|
||||
HypotheticalLaunchConfig.GeneratorType,
|
||||
HypotheticalLaunchConfig.PuzzleId
|
||||
);
|
||||
Entry.SelectorPayloadLine = BuildSelectorChoiceEntriesAndSummary(
|
||||
HypotheticalLaunchConfig.SelectorChoices,
|
||||
Entry.SelectorChoiceEntries,
|
||||
Entry.SelectorChoiceCount
|
||||
);
|
||||
Entry.bWouldReuseStoredRequest = bWouldReuseStoredRequest;
|
||||
Entry.bWouldRequireLaunchRequestRefresh =
|
||||
bWouldRequireLaunchRequestRefresh;
|
||||
Entry.bHasStructurallyValidLaunchPayload = true;
|
||||
Entry.bCanPreview = true;
|
||||
|
||||
if (bWouldReuseStoredRequest)
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
|
||||
StoredRequestReusable;
|
||||
Entry.StoredRequestReuseLine =
|
||||
TEXT("Stored request reuse: the current stored request already matches this hypothetical launch payload.");
|
||||
Entry.RefreshPreviewLine =
|
||||
TEXT("Refresh preview: execute can use the stored request without rebuilding it.");
|
||||
++Surface.StoredRequestReusableOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
|
||||
RefreshRequired;
|
||||
Entry.StoredRequestReuseLine =
|
||||
!LaunchRequest.IsStructurallyValid()
|
||||
? TEXT("Stored request reuse: no stored request is currently persisted for this hypothetical launch payload.")
|
||||
: (bHasComparableStoredRequest
|
||||
? TEXT("Stored request reuse: this hypothetical launch payload diverges from the current stored request.")
|
||||
: TEXT("Stored request reuse: a stored request exists, but it does not match this generated-mode surface or runtime mode."));
|
||||
Entry.RefreshPreviewLine =
|
||||
!LaunchRequest.IsStructurallyValid()
|
||||
? TEXT("Refresh preview: execute would persist a first stored request from this hypothetical launch payload.")
|
||||
: (bHasComparableStoredRequest
|
||||
? TEXT("Refresh preview: execute would refresh the stored request from this hypothetical launch payload.")
|
||||
: TEXT("Refresh preview: execute would replace the currently persisted stored request with this hypothetical launch payload."));
|
||||
++Surface.RefreshRequiredOptionCount;
|
||||
}
|
||||
|
||||
++PreviewablePayloadOptionCount;
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("%s | %s"),
|
||||
*Entry.PayloadIdentityLine,
|
||||
*RequestAlignmentDetail
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable;
|
||||
Entry.PayloadIdentityLine = TEXT("Payload: unavailable");
|
||||
Entry.SelectorPayloadLine = TEXT("Selector payload: unavailable");
|
||||
Entry.StoredRequestReuseLine = TEXT("Stored request reuse: unavailable");
|
||||
Entry.RefreshPreviewLine = TEXT("Refresh preview: unavailable");
|
||||
Entry.ExecutePreviewLine = TEXT("Execute preview: unavailable.");
|
||||
Entry.DetailLine =
|
||||
TEXT("Launch-request preview is unavailable because this selector option cannot dispatch from the active generated-mode surface.");
|
||||
Entry.bCanPreview = false;
|
||||
++Surface.UnavailableOptionCount;
|
||||
}
|
||||
|
||||
if (Entry.bMatchesCurrentSelection)
|
||||
{
|
||||
Surface.bCurrentSelectionWouldReuseStoredRequest = Entry.bWouldReuseStoredRequest;
|
||||
Surface.bCurrentSelectionWouldRequireLaunchRequestRefresh =
|
||||
Entry.bWouldRequireLaunchRequestRefresh;
|
||||
Surface.bCurrentSelectionWouldLeaveSelectorsUnresolved =
|
||||
Entry.bWouldLeaveSelectorsUnresolved;
|
||||
Surface.bCurrentSelectionHasStructurallyValidLaunchPayload =
|
||||
Entry.bHasStructurallyValidLaunchPayload;
|
||||
Surface.CurrentSelectionPayloadLine = Entry.bStartsOwnedCaseImmediately
|
||||
? FString::Printf(
|
||||
TEXT("%s | %s"),
|
||||
*Entry.PayloadIdentityLine,
|
||||
*Entry.ExecutePreviewLine
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("%s | %s | %s"),
|
||||
*Entry.PayloadIdentityLine,
|
||||
*Entry.StoredRequestReuseLine,
|
||||
*Entry.RefreshPreviewLine
|
||||
);
|
||||
}
|
||||
|
||||
Surface.OptionEntries.Add(MoveTemp(Entry));
|
||||
}
|
||||
|
||||
Surface.bHasLaunchRequestPreviewOptions = Surface.OptionEntries.Num() > 0;
|
||||
Surface.bHasStoredRequestReusableOptions = Surface.StoredRequestReusableOptionCount > 0;
|
||||
Surface.bHasRefreshRequiredOptions = Surface.RefreshRequiredOptionCount > 0;
|
||||
Surface.bHasUnresolvedAfterChoiceOptions = Surface.UnresolvedAfterChoiceOptionCount > 0;
|
||||
Surface.bHasPayloadUnavailableOptions = Surface.PayloadUnavailableOptionCount > 0;
|
||||
|
||||
Surface.PayloadSummaryLine = Surface.OptionEntries.Num() > 0
|
||||
? FString::Printf(
|
||||
TEXT("Payload preview: %d reusable | %d refresh-required | %d unresolved | %d payload-unavailable | %d direct-launch | %d unavailable"),
|
||||
Surface.StoredRequestReusableOptionCount,
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.UnresolvedAfterChoiceOptionCount,
|
||||
Surface.PayloadUnavailableOptionCount,
|
||||
Surface.DirectOwnedCaseStartOptionCount,
|
||||
Surface.UnavailableOptionCount
|
||||
)
|
||||
: TEXT("Payload preview: no imported options");
|
||||
Surface.StoredRequestReuseSummaryLine = PreviewableGeneratedOptionCount > 0
|
||||
? FString::Printf(
|
||||
TEXT("Stored request reuse: %d option%s can reuse the current stored request; %d would need refresh; %d still have no valid launch payload."),
|
||||
Surface.StoredRequestReusableOptionCount,
|
||||
Surface.StoredRequestReusableOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.UnresolvedAfterChoiceOptionCount + Surface.PayloadUnavailableOptionCount
|
||||
)
|
||||
: TEXT("Stored request reuse: no previewable generated-choice options");
|
||||
Surface.RefreshPreviewSummaryLine = PreviewableGeneratedOptionCount > 0
|
||||
? FString::Printf(
|
||||
TEXT("Refresh preview: %d option%s would refresh the stored request; %d stay reusable without refresh; %d still cannot stage a complete payload."),
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.RefreshRequiredOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.StoredRequestReusableOptionCount,
|
||||
Surface.UnresolvedAfterChoiceOptionCount + Surface.PayloadUnavailableOptionCount
|
||||
)
|
||||
: TEXT("Refresh preview: no previewable generated-choice options");
|
||||
|
||||
if (Surface.CurrentSelectionPayloadLine.IsEmpty())
|
||||
{
|
||||
if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
|
||||
{
|
||||
Surface.CurrentSelectionPayloadLine = OptionMenuSurface.UnresolvedSelectionLine;
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelection)
|
||||
{
|
||||
Surface.CurrentSelectionPayloadLine =
|
||||
TEXT("Current selection has no additional launch-request payload preview.");
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector)
|
||||
{
|
||||
Surface.CurrentSelectionPayloadLine = TEXT("Current selection: unresolved");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.CurrentSelectionPayloadLine = TEXT("Current selection: n/a");
|
||||
}
|
||||
}
|
||||
|
||||
if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option launch-request preview exposes %d stored-request-reusable option%s, %d refresh-required option%s, %d unresolved option%s, %d payload-unavailable option%s, %d direct-launch option%s, and %d unavailable option%s."),
|
||||
Surface.StoredRequestReusableOptionCount,
|
||||
Surface.StoredRequestReusableOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.RefreshRequiredOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.UnresolvedAfterChoiceOptionCount,
|
||||
Surface.UnresolvedAfterChoiceOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.PayloadUnavailableOptionCount,
|
||||
Surface.PayloadUnavailableOptionCount == 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 launch-request preview is active, but this selector does not expose imported options.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Generated-mode selector option launch-request preview 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 | Payload-ready: %d | Selector: %s | Binding: %s"),
|
||||
OptionDispatchSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *OptionDispatchSurface.StatusLine,
|
||||
OptionRequestAlignmentSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *OptionRequestAlignmentSurface.StatusLine,
|
||||
PreviewablePayloadOptionCount,
|
||||
*SelectorSummary,
|
||||
*BindingSummary
|
||||
);
|
||||
|
||||
return Surface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -813,6 +813,27 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionStor
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
|
||||
OptionRequestAlignmentSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
CachedRunState,
|
||||
SelectorId,
|
||||
OptionMenuSurface,
|
||||
OptionDispatchSurface,
|
||||
OptionRequestAlignmentSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
|
||||
const int32 MaxCases,
|
||||
const FString& StartActionSourceLabel
|
||||
|
|
|
|||
|
|
@ -794,6 +794,27 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionSto
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface
|
||||
OptionRequestAlignmentSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionRequestAlignmentSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
CachedRunState,
|
||||
SelectorId,
|
||||
OptionMenuSurface,
|
||||
OptionDispatchSurface,
|
||||
OptionRequestAlignmentSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
|
||||
const int32 MaxCases
|
||||
)
|
||||
|
|
|
|||
|
|
@ -746,4 +746,15 @@ public:
|
|||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
|
||||
OptionRequestAlignmentSurface
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
DeriveCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(
|
||||
const FHyperTwistTrainingRunState& RunState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
|
||||
OptionRequestAlignmentSurface
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -280,6 +280,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedRun(
|
||||
int32 MaxCases,
|
||||
|
|
|
|||
|
|
@ -261,6 +261,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionStoredRequestDeltaSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,18 @@ enum class EHyperTwistTrainingCoachGeneratedModeOptionStoredRequestDeltaKind : u
|
|||
Unavailable UMETA(DisplayName = "Unavailable")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
DirectOwnedCaseStart UMETA(DisplayName = "Direct Owned Case Start"),
|
||||
StoredRequestReusable UMETA(DisplayName = "Stored Request Reusable"),
|
||||
RefreshRequired UMETA(DisplayName = "Refresh Required"),
|
||||
UnresolvedAfterChoice UMETA(DisplayName = "Unresolved After Choice"),
|
||||
PayloadUnavailable UMETA(DisplayName = "Payload Unavailable"),
|
||||
Unavailable UMETA(DisplayName = "Unavailable")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingPromptKind : uint8
|
||||
{
|
||||
|
|
@ -9496,3 +9508,243 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionStoredRequestDeltaSurf
|
|||
|| bHasStoredRequestDeltaOptions;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface
|
||||
{
|
||||
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 SelectedValue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayValue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PayloadLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bIsTargetSelector = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasChoice = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesCurrentSelectionChoice = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesStoredRequestChoice = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !SelectorId.IsEmpty()
|
||||
|| !SelectorLabel.IsEmpty()
|
||||
|| !PayloadLine.IsEmpty()
|
||||
|| bHasChoice;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface
|
||||
{
|
||||
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")
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind LaunchRequestPreviewKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PayloadIdentityLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SelectorPayloadLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StoredRequestReuseLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RefreshPreviewLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ExecutePreviewLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface>
|
||||
SelectorChoiceEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalDeckId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalTrainingSessionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalSurfaceId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalSurfaceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalRuntimeModeId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalGeneratorType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HypotheticalPuzzleId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingDeliveryMode HypotheticalDeliveryMode = EHyperTwistTrainingDeliveryMode::Timer;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SelectorChoiceCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesCurrentSelection = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bStartsOwnedCaseImmediately = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bWouldReuseStoredRequest = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bWouldRequireLaunchRequestRefresh = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bWouldLeaveSelectorsUnresolved = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasStructurallyValidLaunchPayload = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCanPreview = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !OptionId.IsEmpty()
|
||||
|| !Label.IsEmpty()
|
||||
|| !PayloadIdentityLine.IsEmpty()
|
||||
|| LaunchRequestPreviewKind
|
||||
!= EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None
|
||||
|| bCanPreview;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
|
||||
{
|
||||
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 PayloadSummaryLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StoredRequestReuseSummaryLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RefreshPreviewSummaryLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CurrentSelectionPayloadLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface>
|
||||
OptionEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 StoredRequestReusableOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RefreshRequiredOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 UnresolvedAfterChoiceOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 PayloadUnavailableOptionCount = 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 bHasLaunchRequestPreviewOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasStoredRequestReusableOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasRefreshRequiredOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasUnresolvedAfterChoiceOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasPayloadUnavailableOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionWouldReuseStoredRequest = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionWouldRequireLaunchRequestRefresh = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionWouldLeaveSelectorsUnresolved = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionHasStructurallyValidLaunchPayload = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| !SelectorId.IsEmpty()
|
||||
|| OptionEntries.Num() > 0
|
||||
|| bHasSelector
|
||||
|| bHasLaunchRequestPreviewOptions;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 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 latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option hypothetical launch-request payload / refresh-preview surface over the already-cached selector/runtime state, 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 next bounded packet is to expose one thin generated-mode selector option hypothetical launch-request metadata / origin-notes preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render imported origin-path and notes provenance over that hypothetical payload without reading raw launch-config metadata 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
# HyperTwist Phase 4 gameplay generated-mode selector option launch-request payload / refresh-preview 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 stored-request delta / selector-diff surface by exposing one parameterized option launch-request payload / refresh-preview surface for a chosen selector id.
|
||||
|
||||
The open tasks were:
|
||||
|
||||
- stop forcing gameplay / Blueprint callers to rebuild hypothetical generated-mode launch-config payloads just to explain what a chosen imported option would stage
|
||||
- expose the concrete selector-choice payload, stored-request reuse posture, and refresh-on-execute preview for each imported option on the chosen selector
|
||||
- keep the surface derived from already-cached gameplay consumer run, selector, and request state plus the already-landed option-menu, dispatch, and request-alignment surfaces instead of widening into backend logic
|
||||
|
||||
It is not:
|
||||
|
||||
- a selector-dispatch behavior change
|
||||
- a stored launch-request persistence change
|
||||
- a generated-mode execution-policy change
|
||||
- a broader gameplay UI composition pass
|
||||
|
||||
## Scope
|
||||
|
||||
Bounded lane:
|
||||
|
||||
- add first-party generated-mode selector option launch-request payload / refresh-preview surfaces in shared training types
|
||||
- add a coach-library helper that derives hypothetical launch payload, selector payload, stored-request reuse posture, and refresh preview from cached run state plus the already-landed option-menu, dispatch, and request-alignment surfaces
|
||||
- expose that parameterized launch-request preview 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 stored launch-request creation, refresh, or execute behavior
|
||||
- changing direct owned-case launch 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 stored-request delta / selector-diff posture
|
||||
- those same consumers already exposed truthful generated-mode option request-alignment / execute-preview posture
|
||||
|
||||
But one thin consumer seam was still open:
|
||||
|
||||
- callers could tell whether a chosen option stayed aligned with the stored request or required refresh
|
||||
- but they still had to rebuild the hypothetical launch payload themselves to show the actual selector payload and request-refresh consequences behind that posture
|
||||
|
||||
That meant:
|
||||
|
||||
- alignment and delta posture were thin and reusable
|
||||
- but concrete launch-request preview still leaked launch-config reconstruction work into callers
|
||||
|
||||
So the next bounded move was:
|
||||
|
||||
- keep the already-landed option-menu, dispatch, request-alignment, and stored-request-delta surfaces unchanged
|
||||
- and expose one narrow launch-request payload / refresh-preview surface above those already-cached surfaces
|
||||
|
||||
## What landed
|
||||
|
||||
Primary code changes:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
|
||||
- added `EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
|
||||
- added `DeriveCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(...)`
|
||||
- launch-request preview is derived from cached run state plus the already-landed option-menu, dispatch, and request-alignment surfaces
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId)`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId)`
|
||||
|
||||
Surface behavior:
|
||||
|
||||
- reports the concrete hypothetical payload identity for each imported option on the chosen selector, including deck, session, delivery, surface, runtime mode, generator, and puzzle context
|
||||
- exposes the hypothetical selector payload row set for each option, including which selector choices match current cached selection and the stored request
|
||||
- classifies each option as stored-request-reusable, refresh-required, unresolved-after-choice, payload-unavailable, direct-launch, or unavailable
|
||||
- reports a compact stored-request reuse and refresh preview for the current cached selection on the chosen selector
|
||||
- keeps the surface presentation-only and compatible with the already-landed generated-mode option-menu, dispatch, request-alignment, and stored-request-delta 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 metadata / origin-notes preview surface so callers can render imported origin-path and notes provenance over the hypothetical payload without reading raw launch-config metadata directly.
|
||||
Loading…
Add table
Reference in a new issue