Expose gameplay selector option execution-impact surface
This commit is contained in:
parent
3521d4ca4d
commit
6c57e4f0ab
9 changed files with 838 additions and 2 deletions
|
|
@ -7815,3 +7815,512 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionDispatchS
|
|||
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
const FHyperTwistTrainingRunState& RunState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface& LaunchExecutionContextSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface Surface;
|
||||
Surface.SelectorId = SelectorId;
|
||||
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
|
||||
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
|
||||
Surface.Headline = TEXT("Generated Mode Selector Option Execution Impact");
|
||||
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
|
||||
|
||||
if (!SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
|
||||
? FString::Printf(
|
||||
TEXT("Generated Mode Selector Option Execution Impact: %s"),
|
||||
*SelectorId
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("Generated Mode Selector Option Execution Impact: %s"),
|
||||
*OptionMenuSurface.SelectorLabel
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option execution impact requires a selector id from the active selector roster.");
|
||||
Surface.ExecutionImpactSummaryLine = TEXT("Execution impact: n/a");
|
||||
Surface.CurrentSelectionExecutionImpactLine = TEXT("Current selection: n/a");
|
||||
Surface.DetailLine = OptionDispatchSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Dispatch: n/a")
|
||||
: FString::Printf(TEXT("Dispatch: %s"), *OptionDispatchSurface.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 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 BuildUnresolvedSelectorBlockedReason = [&DescribeSelector, &RuntimeSurface](
|
||||
const FHyperTwistTrainingImportedRuntimeSelectionState& SelectionState,
|
||||
int32& OutUnresolvedSelectorCount
|
||||
) -> FString
|
||||
{
|
||||
OutUnresolvedSelectorCount = 0;
|
||||
FString FirstUnresolvedSelectorLabel;
|
||||
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
|
||||
{
|
||||
if (!Selector.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* MatchingChoice =
|
||||
SelectionState.SelectorChoices.FindByPredicate(
|
||||
[&Selector](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
|
||||
{
|
||||
return Candidate.SelectorId == Selector.SelectorId
|
||||
&& Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
if (MatchingChoice != nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++OutUnresolvedSelectorCount;
|
||||
if (FirstUnresolvedSelectorLabel.IsEmpty())
|
||||
{
|
||||
FirstUnresolvedSelectorLabel = DescribeSelector(Selector);
|
||||
}
|
||||
}
|
||||
|
||||
return OutUnresolvedSelectorCount > 0
|
||||
? FString::Printf(
|
||||
TEXT("Generated-mode execution is blocked because selector %s is unresolved."),
|
||||
FirstUnresolvedSelectorLabel.IsEmpty() ? TEXT("n/a") : *FirstUnresolvedSelectorLabel
|
||||
)
|
||||
: FString();
|
||||
};
|
||||
|
||||
const auto DoSelectorChoiceSetsMatch = [&RuntimeSurface](
|
||||
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& LeftChoices,
|
||||
const TArray<FHyperTwistTrainingImportedRuntimeSelectorChoice>& RightChoices
|
||||
) -> bool
|
||||
{
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelector& Selector : RuntimeSurface.Selectors)
|
||||
{
|
||||
if (!Selector.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* LeftChoice =
|
||||
LeftChoices.FindByPredicate(
|
||||
[&Selector](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
|
||||
{
|
||||
return Candidate.SelectorId == Selector.SelectorId
|
||||
&& Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* RightChoice =
|
||||
RightChoices.FindByPredicate(
|
||||
[&Selector](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
|
||||
{
|
||||
return Candidate.SelectorId == Selector.SelectorId
|
||||
&& Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
if (LeftChoice == nullptr || RightChoice == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (LeftChoice->SelectedValue != RightChoice->SelectedValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto DoesLaunchRequestMatchLaunchConfig = [&DoSelectorChoiceSetsMatch](
|
||||
const FHyperTwistTrainingImportedGeneratedModeLaunchRequest& LaunchRequest,
|
||||
const FHyperTwistTrainingImportedGeneratedModeLaunchConfig& ExpectedLaunchConfig
|
||||
) -> bool
|
||||
{
|
||||
return LaunchRequest.IsStructurallyValid()
|
||||
&& LaunchRequest.LaunchConfig.DeckId == ExpectedLaunchConfig.DeckId
|
||||
&& LaunchRequest.LaunchConfig.SurfaceId == ExpectedLaunchConfig.SurfaceId
|
||||
&& LaunchRequest.LaunchConfig.RuntimeModeId.Equals(
|
||||
ExpectedLaunchConfig.RuntimeModeId,
|
||||
ESearchCase::IgnoreCase
|
||||
)
|
||||
&& DoSelectorChoiceSetsMatch(
|
||||
LaunchRequest.LaunchConfig.SelectorChoices,
|
||||
ExpectedLaunchConfig.SelectorChoices
|
||||
);
|
||||
};
|
||||
|
||||
const auto DescribeUnchangedExecutionImpact = [&LaunchExecutionContextSurface]() -> FString
|
||||
{
|
||||
if (LaunchExecutionContextSurface.bHasExecutionTarget
|
||||
&& LaunchExecutionContextSurface.bCanExecute)
|
||||
{
|
||||
return LaunchExecutionContextSurface.bWillRefreshLaunchRequestOnExecute
|
||||
? TEXT("Execution impact: execute posture is unchanged and still refreshes the stored request.")
|
||||
: TEXT("Execution impact: execute posture is unchanged and still uses the stored request.");
|
||||
}
|
||||
if (LaunchExecutionContextSurface.bHasExecutionTarget)
|
||||
{
|
||||
return TEXT("Execution impact: execute posture is unchanged and remains blocked.");
|
||||
}
|
||||
if (LaunchExecutionContextSurface.bHasLatestLaunchRequest)
|
||||
{
|
||||
return TEXT("Execution impact: execute posture is unchanged and no execution target is currently staged.");
|
||||
}
|
||||
return TEXT("Execution impact: execute posture is unchanged and no stored request or execution target is currently staged.");
|
||||
};
|
||||
|
||||
if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface)
|
||||
{
|
||||
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Generated-mode selector option execution impact is not currently available.")
|
||||
: OptionMenuSurface.StatusLine;
|
||||
Surface.ExecutionImpactSummaryLine = TEXT("Execution impact: n/a");
|
||||
Surface.CurrentSelectionExecutionImpactLine = OptionMenuSurface.bHasUnresolvedSelectionDetail
|
||||
? OptionMenuSurface.UnresolvedSelectionLine
|
||||
: (OptionMenuSurface.bHasSelection
|
||||
? TEXT("Current selection remains cached, but no active generated-mode execute context is available.")
|
||||
: TEXT("Current selection: unresolved"));
|
||||
Surface.DetailLine = FString::Printf(
|
||||
TEXT("Dispatch: %s | Execute: %s"),
|
||||
OptionDispatchSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *OptionDispatchSurface.StatusLine,
|
||||
LaunchExecutionContextSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *LaunchExecutionContextSurface.StatusLine
|
||||
);
|
||||
return Surface;
|
||||
}
|
||||
|
||||
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchEntrySurface& DispatchEntry :
|
||||
OptionDispatchSurface.OptionEntries)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactEntrySurface Entry;
|
||||
Entry.OptionId = DispatchEntry.OptionId;
|
||||
Entry.Label = DispatchEntry.Label;
|
||||
Entry.OptionValue = DispatchEntry.OptionValue;
|
||||
Entry.bMatchesCurrentSelection = DispatchEntry.bMatchesCurrentSelection;
|
||||
|
||||
if (DispatchEntry.DispatchKind
|
||||
== EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind::DirectCaseLaunch
|
||||
&& DispatchEntry.bCanDispatch)
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::ImmediateOwnedCaseStart;
|
||||
Entry.ImpactLine = TEXT("Execution impact: starts a direct owned case immediately.");
|
||||
Entry.DetailLine = DispatchEntry.DetailLine.IsEmpty()
|
||||
? TEXT("Direct option launch bypasses generated-mode execute for this interaction.")
|
||||
: DispatchEntry.DetailLine;
|
||||
Entry.bStartsOwnedCaseImmediately = true;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.ImmediateOwnedCaseStartOptionCount;
|
||||
}
|
||||
else if (DispatchEntry.DispatchKind
|
||||
== EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind::ApplyGeneratedChoice
|
||||
&& DispatchEntry.bCanDispatch && MatchingSelector != nullptr)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
int32 UnresolvedSelectorCount = 0;
|
||||
Entry.BlockedReasonLine = BuildUnresolvedSelectorBlockedReason(
|
||||
HypotheticalSelectionState,
|
||||
UnresolvedSelectorCount
|
||||
);
|
||||
|
||||
if (!Entry.BlockedReasonLine.IsEmpty())
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::ExecuteBlocked;
|
||||
Entry.ImpactLine =
|
||||
TEXT("Execution impact: applies the selector choice, but execute remains blocked.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("Choice apply: %s | %d selector%s still need resolution before generated-mode execute."),
|
||||
DispatchEntry.DetailLine.IsEmpty() ? TEXT("n/a") : *DispatchEntry.DetailLine,
|
||||
UnresolvedSelectorCount,
|
||||
UnresolvedSelectorCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
Entry.bLeavesExecuteBlocked = true;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.BlockedExecuteOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
FHyperTwistTrainingImportedGeneratedModeLaunchConfig HypotheticalLaunchConfig;
|
||||
if (!UHyperTwistTrainingCatalogLibrary::TryBuildImportedGeneratedModeLaunchConfig(
|
||||
RunState.ActiveDeck,
|
||||
HypotheticalSelectionState,
|
||||
RunState.Session,
|
||||
HypotheticalLaunchConfig))
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::ExecuteBlocked;
|
||||
Entry.ImpactLine =
|
||||
TEXT("Execution impact: applies the selector choice, but execute remains blocked.");
|
||||
Entry.BlockedReasonLine =
|
||||
TEXT("Generated-mode execution could not derive a structurally valid launch request from the resulting selector state.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("Choice apply: %s | The resulting selector state is not executable yet."),
|
||||
DispatchEntry.DetailLine.IsEmpty() ? TEXT("n/a") : *DispatchEntry.DetailLine
|
||||
);
|
||||
Entry.bLeavesExecuteBlocked = true;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.BlockedExecuteOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool bWouldUseStoredRequest = DoesLaunchRequestMatchLaunchConfig(
|
||||
RunState.ImportedGeneratedModeLaunchRequest,
|
||||
HypotheticalLaunchConfig
|
||||
);
|
||||
|
||||
if (DispatchEntry.bMatchesCurrentSelection)
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::ExecutePostureUnchanged;
|
||||
Entry.ImpactLine = DescribeUnchangedExecutionImpact();
|
||||
Entry.BlockedReasonLine = LaunchExecutionContextSurface.bHasBlockedExecution
|
||||
? LaunchExecutionContextSurface.BlockedReasonLine
|
||||
: FString();
|
||||
Entry.DetailLine = LaunchExecutionContextSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Current generated-mode execute posture remains unchanged.")
|
||||
: FString::Printf(
|
||||
TEXT("Current generated-mode execute posture: %s"),
|
||||
*LaunchExecutionContextSurface.StatusLine
|
||||
);
|
||||
Entry.bLeavesExecutePostureUnchanged = true;
|
||||
Entry.bExecutesFromStoredRequest =
|
||||
LaunchExecutionContextSurface.bHasExecutionTarget
|
||||
&& LaunchExecutionContextSurface.bCanExecute
|
||||
&& !LaunchExecutionContextSurface.bWillRefreshLaunchRequestOnExecute;
|
||||
Entry.bRequiresLaunchRequestRefreshBeforeExecute =
|
||||
LaunchExecutionContextSurface.bWillRefreshLaunchRequestOnExecute;
|
||||
Entry.bLeavesExecuteBlocked =
|
||||
LaunchExecutionContextSurface.bHasExecutionTarget
|
||||
&& !LaunchExecutionContextSurface.bCanExecute;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.UnchangedExecutePostureOptionCount;
|
||||
}
|
||||
else if (bWouldUseStoredRequest)
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::ExecuteFromStoredRequest;
|
||||
Entry.ImpactLine = TEXT("Execution impact: generated-mode execute can use the stored request after this choice.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("Choice apply: %s | The resulting selector state realigns with the current stored request."),
|
||||
DispatchEntry.DetailLine.IsEmpty() ? TEXT("n/a") : *DispatchEntry.DetailLine
|
||||
);
|
||||
Entry.bExecutesFromStoredRequest = true;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.ExecuteFromStoredRequestOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::LaunchRequestRefreshRequired;
|
||||
Entry.ImpactLine = TEXT("Execution impact: generated-mode execute will require launch-request refresh after this choice.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("Choice apply: %s | The resulting selector state no longer matches the current stored request."),
|
||||
DispatchEntry.DetailLine.IsEmpty() ? TEXT("n/a") : *DispatchEntry.DetailLine
|
||||
);
|
||||
Entry.bRequiresLaunchRequestRefreshBeforeExecute = true;
|
||||
Entry.bCanApplyImpact = true;
|
||||
++Surface.RefreshRequiredOptionCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::Unavailable;
|
||||
Entry.ImpactLine = TEXT("Execution impact: unavailable.");
|
||||
Entry.DetailLine = TEXT("Execution-impact preview is unavailable because this selector option cannot dispatch from the active generated-mode surface.");
|
||||
Entry.bCanApplyImpact = false;
|
||||
++Surface.UnavailableOptionCount;
|
||||
}
|
||||
|
||||
if (Entry.bMatchesCurrentSelection)
|
||||
{
|
||||
Surface.bCurrentSelectionStartsOwnedCaseImmediately = Entry.bStartsOwnedCaseImmediately;
|
||||
Surface.bCurrentSelectionLeavesExecutePostureUnchanged =
|
||||
Entry.bLeavesExecutePostureUnchanged;
|
||||
Surface.bCurrentSelectionRequiresLaunchRequestRefreshBeforeExecute =
|
||||
Entry.bRequiresLaunchRequestRefreshBeforeExecute;
|
||||
Surface.bCurrentSelectionLeavesExecuteBlocked = Entry.bLeavesExecuteBlocked;
|
||||
Surface.CurrentSelectionExecutionImpactLine = Entry.bLeavesExecuteBlocked
|
||||
&& !Entry.BlockedReasonLine.IsEmpty()
|
||||
? FString::Printf(TEXT("%s %s"), *Entry.ImpactLine, *Entry.BlockedReasonLine)
|
||||
: Entry.ImpactLine;
|
||||
}
|
||||
|
||||
Surface.OptionEntries.Add(MoveTemp(Entry));
|
||||
}
|
||||
|
||||
Surface.bHasExecutionImpactOptions = Surface.OptionEntries.Num() > 0;
|
||||
Surface.bHasImmediateOwnedCaseStartOptions =
|
||||
Surface.ImmediateOwnedCaseStartOptionCount > 0;
|
||||
Surface.bHasStoredRequestExecutionOptions =
|
||||
Surface.ExecuteFromStoredRequestOptionCount > 0;
|
||||
Surface.bHasRefreshRequiredOptions = Surface.RefreshRequiredOptionCount > 0;
|
||||
Surface.bHasBlockedExecuteOptions = Surface.BlockedExecuteOptionCount > 0;
|
||||
|
||||
Surface.ExecutionImpactSummaryLine = Surface.bHasExecutionImpactOptions
|
||||
? FString::Printf(
|
||||
TEXT("Execution impact: %d direct-start | %d stored-request | %d unchanged | %d refresh-required | %d blocked | %d unavailable"),
|
||||
Surface.ImmediateOwnedCaseStartOptionCount,
|
||||
Surface.ExecuteFromStoredRequestOptionCount,
|
||||
Surface.UnchangedExecutePostureOptionCount,
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.BlockedExecuteOptionCount,
|
||||
Surface.UnavailableOptionCount
|
||||
)
|
||||
: TEXT("Execution impact: no imported options");
|
||||
|
||||
if (Surface.CurrentSelectionExecutionImpactLine.IsEmpty())
|
||||
{
|
||||
if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
|
||||
{
|
||||
Surface.CurrentSelectionExecutionImpactLine = OptionMenuSurface.UnresolvedSelectionLine;
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelection)
|
||||
{
|
||||
Surface.CurrentSelectionExecutionImpactLine =
|
||||
TEXT("Current selection has no additional execution-impact detail.");
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector)
|
||||
{
|
||||
Surface.CurrentSelectionExecutionImpactLine = TEXT("Current selection: unresolved");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.CurrentSelectionExecutionImpactLine = TEXT("Current selection: n/a");
|
||||
}
|
||||
}
|
||||
|
||||
if (OptionMenuSurface.bHasSelector && Surface.bHasExecutionImpactOptions)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option execution impact exposes %d direct-start option%s, %d stored-request option%s, %d unchanged execute option%s, %d refresh-required option%s, %d blocked option%s, and %d unavailable option%s."),
|
||||
Surface.ImmediateOwnedCaseStartOptionCount,
|
||||
Surface.ImmediateOwnedCaseStartOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.ExecuteFromStoredRequestOptionCount,
|
||||
Surface.ExecuteFromStoredRequestOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.UnchangedExecutePostureOptionCount,
|
||||
Surface.UnchangedExecutePostureOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.RefreshRequiredOptionCount,
|
||||
Surface.RefreshRequiredOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.BlockedExecuteOptionCount,
|
||||
Surface.BlockedExecuteOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.UnavailableOptionCount,
|
||||
Surface.UnavailableOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option execution impact is active, but this selector does not expose imported options.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Generated-mode selector option execution impact 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 | Execute: %s | Selector: %s | Binding: %s"),
|
||||
OptionDispatchSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *OptionDispatchSurface.StatusLine,
|
||||
LaunchExecutionContextSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *LaunchExecutionContextSurface.StatusLine,
|
||||
*SelectorSummary,
|
||||
*BindingSummary
|
||||
);
|
||||
|
||||
return Surface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -750,6 +750,27 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionDisp
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface
|
||||
LaunchExecutionContextSurface =
|
||||
GetDisplayedCoachGeneratedModeLaunchExecutionContextSurface();
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
CachedRunState,
|
||||
SelectorId,
|
||||
LaunchExecutionContextSurface,
|
||||
OptionMenuSurface,
|
||||
OptionDispatchSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
|
||||
const int32 MaxCases,
|
||||
const FString& StartActionSourceLabel
|
||||
|
|
|
|||
|
|
@ -731,6 +731,27 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionDis
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface
|
||||
LaunchExecutionContextSurface =
|
||||
GetDisplayedCoachGeneratedModeLaunchExecutionContextSurface();
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface OptionDispatchSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
CachedRunState,
|
||||
SelectorId,
|
||||
LaunchExecutionContextSurface,
|
||||
OptionMenuSurface,
|
||||
OptionDispatchSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
|
||||
const int32 MaxCases
|
||||
)
|
||||
|
|
|
|||
|
|
@ -713,4 +713,15 @@ public:
|
|||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface&
|
||||
OptionActionabilitySurface
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
DeriveCoachGeneratedModeSelectorOptionExecutionImpactSurface(
|
||||
const FHyperTwistTrainingRunState& RunState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface&
|
||||
LaunchExecutionContextSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface& OptionDispatchSurface
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -268,6 +268,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedRun(
|
||||
int32 MaxCases,
|
||||
|
|
|
|||
|
|
@ -249,6 +249,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionDispatchSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,18 @@ enum class EHyperTwistTrainingCoachGeneratedModeOptionDispatchKind : uint8
|
|||
Unavailable UMETA(DisplayName = "Unavailable")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
ImmediateOwnedCaseStart UMETA(DisplayName = "Immediate Owned Case Start"),
|
||||
ExecuteFromStoredRequest UMETA(DisplayName = "Execute From Stored Request"),
|
||||
ExecutePostureUnchanged UMETA(DisplayName = "Execute Posture Unchanged"),
|
||||
LaunchRequestRefreshRequired UMETA(DisplayName = "Launch-Request Refresh Required"),
|
||||
ExecuteBlocked UMETA(DisplayName = "Execute Blocked"),
|
||||
Unavailable UMETA(DisplayName = "Unavailable")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingPromptKind : uint8
|
||||
{
|
||||
|
|
@ -8903,3 +8915,153 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionDispatchSurface
|
|||
|| bHasDispatchOptions;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactEntrySurface
|
||||
{
|
||||
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")
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind ExecutionImpactKind =
|
||||
EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ImpactLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString BlockedReasonLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesCurrentSelection = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bStartsOwnedCaseImmediately = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bExecutesFromStoredRequest = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLeavesExecutePostureUnchanged = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresLaunchRequestRefreshBeforeExecute = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLeavesExecuteBlocked = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCanApplyImpact = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !OptionId.IsEmpty()
|
||||
|| !Label.IsEmpty()
|
||||
|| !ImpactLine.IsEmpty()
|
||||
|| ExecutionImpactKind
|
||||
!= EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind::None
|
||||
|| bCanApplyImpact;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface
|
||||
{
|
||||
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 ExecutionImpactSummaryLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CurrentSelectionExecutionImpactLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactEntrySurface>
|
||||
OptionEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ImmediateOwnedCaseStartOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ExecuteFromStoredRequestOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 UnchangedExecutePostureOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RefreshRequiredOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 BlockedExecuteOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 UnavailableOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasSelector = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasExecutionImpactOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasImmediateOwnedCaseStartOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasStoredRequestExecutionOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasRefreshRequiredOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasBlockedExecuteOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionStartsOwnedCaseImmediately = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionLeavesExecutePostureUnchanged = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionRequiresLaunchRequestRefreshBeforeExecute = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionLeavesExecuteBlocked = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| !SelectorId.IsEmpty()
|
||||
|| OptionEntries.Num() > 0
|
||||
|| bHasSelector
|
||||
|| bHasExecutionImpactOptions;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 interaction / dispatch surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render whether choosing an option should launch a direct owned case, apply a generated-mode selector choice, or stay unavailable without reaching into raw runtime action-path rules directly
|
||||
- the current next bounded packet is to expose one thin generated-mode selector option execution-impact / launch-request-refresh surface on those same two consumer surfaces so gameplay/Blueprint callers can render whether a choice would start an owned case immediately, leave generated-mode execute posture unchanged, or require launch-request refresh before execute without reading raw launch-request freshness or blocked-reason rules directly
|
||||
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option execution-impact / launch-request-refresh surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render whether a choice would start a direct owned case immediately, leave generated-mode execute posture unchanged, realign with the stored request, require launch-request refresh before execute, or still leave execute blocked without reading raw launch-request freshness or blocked-reason rules directly
|
||||
- the current next bounded packet is to expose one thin generated-mode selector option request-alignment / execute-preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render hypothetical selector summary, stored-request alignment, and unresolved-selector preview for a chosen option without diffing cached selector choices or launch-request 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
# HyperTwist Phase 4 gameplay generated-mode selector option execution-impact / launch-request-refresh 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 interaction / dispatch surface by exposing one parameterized option execution-impact surface for a chosen selector id.
|
||||
|
||||
The open tasks were:
|
||||
|
||||
- stop forcing gameplay / Blueprint callers to reconstruct launch-request freshness and blocked-execute rules just to explain what choosing an imported option would do to generated-mode execute posture
|
||||
- expose whether an option would start a direct owned case immediately, keep generated-mode execute posture unchanged, realign with the stored request, require a fresh launch request before execute, or still leave execute blocked
|
||||
- keep the surface derived from already-cached gameplay consumer run, selector, and request state plus the already-landed launch-execution, option-detail, and option-dispatch surfaces instead of widening into backend logic
|
||||
|
||||
It is not:
|
||||
|
||||
- a new selector-dispatch packet
|
||||
- a new launch-request creation packet
|
||||
- a generated-mode backend behavior change
|
||||
- a broader gameplay UI composition pass
|
||||
|
||||
## Scope
|
||||
|
||||
Bounded lane:
|
||||
|
||||
- add first-party generated-mode selector option execution-impact surfaces in shared training types
|
||||
- add a coach-library helper that derives execution-impact posture from the cached run state plus the already-landed launch-execution, option-detail, and option-dispatch surfaces
|
||||
- expose that parameterized execution-impact surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
|
||||
- keep the derivation presentation-only and rooted in the already-owned selector-choice / launch-request / execute rules
|
||||
|
||||
Out of scope:
|
||||
|
||||
- changing selector-choice apply behavior
|
||||
- changing direct owned-case option-launch behavior
|
||||
- changing generated-mode execute or launch-request persistence 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 launch / execution context
|
||||
- those same consumers already exposed truthful chosen-selector option detail
|
||||
- those same consumers already exposed truthful chosen-selector option dispatch posture
|
||||
|
||||
But one thin consumer seam was still open:
|
||||
|
||||
- callers could tell how an option dispatches
|
||||
- but they still had to know raw launch-request freshness and blocked-execute rules to explain what that option would do to generated-mode execute posture
|
||||
|
||||
That meant:
|
||||
|
||||
- dispatch posture was thin and reusable
|
||||
- but execute-impact posture still leaked runtime freshness/blocking rules into callers
|
||||
|
||||
So the next bounded move was:
|
||||
|
||||
- keep the already-landed generated-mode execute context, option-detail, and option-dispatch surfaces unchanged
|
||||
- and expose one narrow option execution-impact / launch-request-refresh surface above those already-cached surfaces
|
||||
|
||||
## What landed
|
||||
|
||||
Primary code changes:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
|
||||
- added `EHyperTwistTrainingCoachGeneratedModeOptionExecutionImpactKind`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactEntrySurface`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionExecutionImpactSurface`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
|
||||
- added `DeriveCoachGeneratedModeSelectorOptionExecutionImpactSurface(...)`
|
||||
- execution-impact posture is derived from the cached run state plus the already-landed launch-execution, option-detail, and option-dispatch surfaces
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(const FString& SelectorId)`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionExecutionImpactSurface(const FString& SelectorId)`
|
||||
|
||||
Surface behavior:
|
||||
|
||||
- reports whether each imported option starts a direct owned case immediately, realigns with the stored request, leaves current execute posture unchanged, requires launch-request refresh before execute, or still leaves execute blocked
|
||||
- exposes blocked-reason preview for options that still leave generated-mode execute unresolved
|
||||
- reports how the current cached selection would affect generated-mode execute right now
|
||||
- keeps the surface presentation-only and compatible with the already-landed generated-mode launch / execution, option-detail, and option-dispatch 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 request-alignment / execute-preview surface so callers can render hypothetical selector summary, stored-request alignment, and unresolved-selector preview for a chosen option without diffing cached selector choices or launch-request payloads directly.
|
||||
Loading…
Add table
Reference in a new issue