Expose gameplay generated-mode selector option detail surface
This commit is contained in:
parent
99a82a9676
commit
a92cf4306f
9 changed files with 497 additions and 2 deletions
|
|
@ -7169,3 +7169,241 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorRosterSurface(
|
|||
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
const FHyperTwistTrainingDeck& ActiveDeck,
|
||||
const FHyperTwistTrainingImportedRuntimeSelectionState& ImportedRuntimeSelectionState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface& SelectorRosterSurface
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface Surface;
|
||||
Surface.SelectorId = SelectorId;
|
||||
Surface.Headline = TEXT("Generated Mode Selector Options");
|
||||
|
||||
const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = ActiveDeck.ImportedRuntimeSurface;
|
||||
const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid()
|
||||
&& RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase);
|
||||
|
||||
if (SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option detail requires a selector id from the active selector roster.");
|
||||
Surface.SelectionLine = TEXT("Selection: n/a");
|
||||
Surface.AvailabilityLine = TEXT("Options: n/a");
|
||||
Surface.DetailLine = SelectorRosterSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Roster: n/a")
|
||||
: FString::Printf(TEXT("Roster: %s"), *SelectorRosterSurface.StatusLine);
|
||||
return Surface;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterEntrySurface* MatchingRosterEntry =
|
||||
SelectorRosterSurface.SelectorEntries.FindByPredicate(
|
||||
[&SelectorId](const FHyperTwistTrainingCoachGeneratedModeSelectorRosterEntrySurface& Entry)
|
||||
{
|
||||
return Entry.SelectorId == SelectorId;
|
||||
}
|
||||
);
|
||||
const FHyperTwistTrainingImportedRuntimeSelectorChoice* MatchingChoice =
|
||||
ImportedRuntimeSelectionState.SelectorChoices.FindByPredicate(
|
||||
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
|
||||
{
|
||||
return Candidate.SelectorId == SelectorId && Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
const FHyperTwistTrainingImportedRuntimeSelector* MatchingSelector =
|
||||
RuntimeSurface.Selectors.FindByPredicate(
|
||||
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelector& Candidate)
|
||||
{
|
||||
return Candidate.SelectorId == SelectorId && Candidate.IsStructurallyValid();
|
||||
}
|
||||
);
|
||||
|
||||
Surface.bHasSelection = MatchingChoice != nullptr;
|
||||
if (MatchingChoice != nullptr)
|
||||
{
|
||||
Surface.SelectedValue = MatchingChoice->SelectedValue;
|
||||
Surface.SelectedDisplayValue = MatchingChoice->DisplayValue.IsEmpty()
|
||||
? MatchingChoice->SelectedValue
|
||||
: MatchingChoice->DisplayValue;
|
||||
}
|
||||
else if (MatchingRosterEntry != nullptr && MatchingRosterEntry->bHasSelection)
|
||||
{
|
||||
Surface.bHasSelection = true;
|
||||
Surface.SelectedValue = MatchingRosterEntry->SelectedValue;
|
||||
Surface.SelectedDisplayValue = MatchingRosterEntry->SelectedDisplayValue;
|
||||
}
|
||||
|
||||
if (MatchingSelector != nullptr)
|
||||
{
|
||||
Surface.bHasSelector = true;
|
||||
Surface.SelectorId = MatchingSelector->SelectorId;
|
||||
Surface.SelectorLabel = MatchingSelector->Label;
|
||||
Surface.BindingLabel = MatchingSelector->SelectorBinding;
|
||||
Surface.Headline = MatchingSelector->Label.IsEmpty()
|
||||
? FString::Printf(TEXT("Generated Mode Selector Options: %s"), *MatchingSelector->SelectorId)
|
||||
: FString::Printf(TEXT("Generated Mode Selector Options: %s"), *MatchingSelector->Label);
|
||||
|
||||
for (const FHyperTwistTrainingImportedRuntimeSelectorOption& Option : MatchingSelector->Options)
|
||||
{
|
||||
if (!Option.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface Entry;
|
||||
Entry.OptionId = Option.OptionId;
|
||||
Entry.Label = Option.Label;
|
||||
Entry.OptionValue = Option.OptionValue;
|
||||
Entry.DetailLine = Entry.OptionValue.IsEmpty() || Entry.OptionValue == Entry.OptionId
|
||||
? FString::Printf(TEXT("Option id: %s"), *Entry.OptionId)
|
||||
: FString::Printf(
|
||||
TEXT("Option id: %s | Value: %s"),
|
||||
*Entry.OptionId,
|
||||
*Entry.OptionValue
|
||||
);
|
||||
Entry.bMatchesCurrentSelection = Surface.bHasSelection
|
||||
&& (Surface.SelectedValue == Option.OptionId || Surface.SelectedValue == Option.OptionValue);
|
||||
if (Entry.bMatchesCurrentSelection && !Surface.bSelectionMatchesAvailableOption)
|
||||
{
|
||||
Surface.bSelectionMatchesAvailableOption = true;
|
||||
Surface.MatchedOptionId = Entry.OptionId;
|
||||
Surface.MatchedOptionLabel = Entry.Label;
|
||||
Surface.MatchedOptionValue = Entry.OptionValue;
|
||||
}
|
||||
|
||||
Surface.OptionEntries.Add(MoveTemp(Entry));
|
||||
}
|
||||
}
|
||||
|
||||
Surface.AvailableOptionCount = Surface.OptionEntries.Num();
|
||||
Surface.bHasOptions = Surface.AvailableOptionCount > 0;
|
||||
Surface.bHasUnresolvedSelectionDetail =
|
||||
Surface.bHasSelection && !Surface.bSelectionMatchesAvailableOption;
|
||||
|
||||
if (Surface.bHasSelection)
|
||||
{
|
||||
const FString SelectionSummary = Surface.SelectedDisplayValue.IsEmpty()
|
||||
? Surface.SelectedValue
|
||||
: Surface.SelectedDisplayValue;
|
||||
Surface.SelectionLine = Surface.bSelectionMatchesAvailableOption
|
||||
? FString::Printf(
|
||||
TEXT("Selection: %s [%s]"),
|
||||
SelectionSummary.IsEmpty() ? TEXT("n/a") : *SelectionSummary,
|
||||
Surface.MatchedOptionId.IsEmpty() ? TEXT("n/a") : *Surface.MatchedOptionId
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("Selection: %s (cached)"),
|
||||
SelectionSummary.IsEmpty() ? TEXT("n/a") : *SelectionSummary
|
||||
);
|
||||
}
|
||||
else if (Surface.bHasSelector)
|
||||
{
|
||||
Surface.SelectionLine = TEXT("Selection: unresolved");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.SelectionLine = TEXT("Selection: n/a");
|
||||
}
|
||||
|
||||
if (Surface.bHasUnresolvedSelectionDetail)
|
||||
{
|
||||
const FString SelectionDisplaySummary = Surface.SelectedDisplayValue.IsEmpty()
|
||||
? Surface.SelectedValue
|
||||
: Surface.SelectedDisplayValue;
|
||||
Surface.UnresolvedSelectionLine =
|
||||
Surface.SelectedDisplayValue.IsEmpty() || Surface.SelectedDisplayValue == Surface.SelectedValue
|
||||
? FString::Printf(
|
||||
TEXT("Cached selection '%s' is not present in the imported options."),
|
||||
SelectionDisplaySummary.IsEmpty() ? TEXT("n/a") : *SelectionDisplaySummary
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("Cached selection '%s' stored as '%s' is not present in the imported options."),
|
||||
SelectionDisplaySummary.IsEmpty() ? TEXT("n/a") : *SelectionDisplaySummary,
|
||||
Surface.SelectedValue.IsEmpty() ? TEXT("n/a") : *Surface.SelectedValue
|
||||
);
|
||||
}
|
||||
|
||||
if (Surface.bHasOptions)
|
||||
{
|
||||
Surface.AvailabilityLine = FString::Printf(
|
||||
TEXT("Options: %d imported"),
|
||||
Surface.AvailableOptionCount
|
||||
);
|
||||
}
|
||||
else if (Surface.bHasSelector)
|
||||
{
|
||||
Surface.AvailabilityLine = TEXT("Options: no imported options");
|
||||
}
|
||||
else if (bHasGeneratedModeSurface)
|
||||
{
|
||||
Surface.AvailabilityLine = TEXT("Options: selector not found in active generated-mode surface");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.AvailabilityLine = TEXT("Options: n/a");
|
||||
}
|
||||
|
||||
if (Surface.bHasSelector && Surface.bHasOptions && Surface.bSelectionMatchesAvailableOption)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option detail exposes %d imported option%s and the cached selection resolves cleanly."),
|
||||
Surface.AvailableOptionCount,
|
||||
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (Surface.bHasSelector && Surface.bHasOptions && Surface.bHasSelection)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option detail exposes %d imported option%s, but the cached selection no longer matches an imported option."),
|
||||
Surface.AvailableOptionCount,
|
||||
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (Surface.bHasSelector && Surface.bHasOptions)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option detail exposes %d imported option%s and the selector is currently unresolved."),
|
||||
Surface.AvailableOptionCount,
|
||||
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (Surface.bHasSelector)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option detail is active, but this selector does not expose imported options.");
|
||||
}
|
||||
else if (bHasGeneratedModeSurface)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option detail could not find the requested selector in the active generated-mode surface.");
|
||||
}
|
||||
else if (SelectorRosterSurface.bHasSelectorRoster || ImportedRuntimeSelectionState.IsStructurallyValid())
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option detail is not active, but cached selector state remains.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option detail is not currently available.");
|
||||
}
|
||||
|
||||
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("Roster: %s | Selector: %s | Binding: %s"),
|
||||
SelectorRosterSurface.StatusLine.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *SelectorRosterSurface.StatusLine,
|
||||
*SelectorSummary,
|
||||
*BindingSummary
|
||||
);
|
||||
|
||||
return Surface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -704,6 +704,21 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorRosterSurf
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface SelectorRosterSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorRosterSurface();
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
CachedRunState.ActiveDeck,
|
||||
CachedImportedRuntimeSelectionState,
|
||||
SelectorId,
|
||||
SelectorRosterSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
|
||||
const int32 MaxCases,
|
||||
const FString& StartActionSourceLabel
|
||||
|
|
|
|||
|
|
@ -685,6 +685,21 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorRosterSur
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface SelectorRosterSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorRosterSurface();
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
CachedRunState.ActiveDeck,
|
||||
CachedImportedRuntimeSelectionState,
|
||||
SelectorId,
|
||||
SelectorRosterSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
|
||||
const int32 MaxCases
|
||||
)
|
||||
|
|
|
|||
|
|
@ -687,4 +687,13 @@ public:
|
|||
const FHyperTwistTrainingCoachGeneratedModeSelectorApplicationContextSurface&
|
||||
SelectorApplicationContextSurface
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
DeriveCoachGeneratedModeSelectorOptionMenuSurface(
|
||||
const FHyperTwistTrainingDeck& ActiveDeck,
|
||||
const FHyperTwistTrainingImportedRuntimeSelectionState& ImportedRuntimeSelectionState,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface& SelectorRosterSurface
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -256,6 +256,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorRosterSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedRun(
|
||||
int32 MaxCases,
|
||||
|
|
|
|||
|
|
@ -237,6 +237,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorRosterSurface() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);
|
||||
|
||||
|
|
|
|||
|
|
@ -8569,3 +8569,110 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
|
|||
|| bHasUnresolvedSelectors;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface
|
||||
{
|
||||
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")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesCurrentSelection = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !OptionId.IsEmpty()
|
||||
|| !Label.IsEmpty()
|
||||
|| !OptionValue.IsEmpty()
|
||||
|| bMatchesCurrentSelection;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
{
|
||||
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 SelectionLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString UnresolvedSelectionLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AvailabilityLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface> OptionEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SelectedValue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SelectedDisplayValue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MatchedOptionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MatchedOptionLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MatchedOptionValue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 AvailableOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasSelector = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasSelection = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasOptions = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSelectionMatchesAvailableOption = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasUnresolvedSelectionDetail = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| !SelectorId.IsEmpty()
|
||||
|| OptionEntries.Num() > 0
|
||||
|| bHasSelector
|
||||
|| bHasSelection;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 roster / option-availability surface over the active imported runtime surface and cached selection state, so gameplay/Blueprint callers can render active selector labels, bindings, selected values, and unresolved option availability without reading raw imported selector arrays or option payloads directly
|
||||
- the current next bounded packet is to expose one thin generated-mode selector option-menu / detail surface on those same two consumer surfaces so gameplay/Blueprint callers can render normalized selector-option ids, labels, and unresolved-selection detail for a chosen selector without reading raw imported option payloads directly
|
||||
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin parameterized generated-mode selector option-menu / detail surface over the already-cached selector roster/runtime state, so gameplay/Blueprint callers can render normalized selector-option ids, labels, values, and unresolved-selection detail for a chosen selector without reading raw imported option payloads directly
|
||||
- the current next bounded packet is to expose one thin generated-mode selector option actionability / case-coverage surface on those same two consumer surfaces so gameplay/Blueprint callers can render which imported options still map cleanly to owned cases and which remain selection-only choices without reading raw option-case payloads or runtime probes 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,103 @@
|
|||
# HyperTwist Phase 4 gameplay generated-mode selector option-menu / detail 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 roster by exposing one parameterized selector option-menu / detail surface for a chosen selector id.
|
||||
|
||||
The open tasks were:
|
||||
|
||||
- stop forcing gameplay / Blueprint callers to read raw imported selector-option payloads just to render normalized option ids, labels, values, and chosen-selector option menus
|
||||
- expose when a cached generated-mode selector choice still matches an imported option and when it has drifted out of the imported option list
|
||||
- keep the surface derived from already-cached gameplay consumer selector/runtime state instead of widening into selector-application behavior or new runtime fetches
|
||||
|
||||
It is not:
|
||||
|
||||
- a new selector-application behavior packet
|
||||
- a per-option actionability or case-coverage packet
|
||||
- a generated-mode execution packet
|
||||
- a broader gameplay UI composition pass
|
||||
|
||||
## Scope
|
||||
|
||||
Bounded lane:
|
||||
|
||||
- add first-party generated-mode selector option-entry and option-menu surfaces in shared training types
|
||||
- add a coach-library helper that derives the chosen-selector option menu from the active deck imported runtime surface, cached imported selection state, requested selector id, and the already-landed selector roster surface
|
||||
- expose that parameterized option-menu surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
|
||||
- keep the derivation presentation-only and rooted in already-cached gameplay consumer state
|
||||
|
||||
Out of scope:
|
||||
|
||||
- changing selector-choice persistence
|
||||
- changing generated-mode launch-config derivation rules
|
||||
- changing selector-option application behavior
|
||||
- widening into per-option case coverage or actionability yet
|
||||
|
||||
## 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 selector / application context
|
||||
- those same consumers already exposed truthful selector roster / option-availability summary
|
||||
|
||||
But one thin consumer seam was still open:
|
||||
|
||||
- callers could list active selectors and see whether unresolved selectors still had imported options
|
||||
- but they still had to read raw option payloads to render the option menu for a chosen selector and to explain whether the cached selection still matched a live imported option
|
||||
|
||||
That meant:
|
||||
|
||||
- selector roster posture was thin and reusable
|
||||
- but chosen-selector option detail still leaked raw imported option payloads into callers
|
||||
|
||||
So the next bounded move was:
|
||||
|
||||
- keep the already-landed launch / execution, selector / application, and selector roster surfaces unchanged
|
||||
- and expose one narrow parameterized option-menu / detail surface above that cached selector state
|
||||
|
||||
## What landed
|
||||
|
||||
Primary code changes:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
|
||||
- added `DeriveCoachGeneratedModeSelectorOptionMenuSurface(...)`
|
||||
- option detail is derived from the active imported runtime surface, cached imported selection state, requested selector id, and the already-landed selector roster surface
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId)`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId)`
|
||||
|
||||
Surface behavior:
|
||||
|
||||
- reports normalized option ids, labels, and option values for the chosen selector
|
||||
- reports whether the cached selection still matches one of the current imported options
|
||||
- reports unresolved-selection detail when cached selection state remains but no longer maps to the imported option list
|
||||
- keeps the surface presentation-only and compatible with the already-landed generated-mode context and roster 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 actionability / case-coverage surface so callers can render which imported options still map to owned cases and which remain selection-only choices without reading raw option-case payloads or runtime probes directly.
|
||||
Loading…
Add table
Reference in a new issue