Expose gameplay generated-mode selector option coverage surface
This commit is contained in:
parent
a92cf4306f
commit
046ff8ee62
9 changed files with 426 additions and 2 deletions
|
|
@ -1,5 +1,7 @@
|
|||
#include "HyperTwistTraining/HyperTwistTrainingCoachLibrary.h"
|
||||
|
||||
#include "HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h"
|
||||
|
||||
namespace HyperTwistTrainingCoachLibraryInternal
|
||||
{
|
||||
EHyperTwistCoachPriority PriorityFromScore(const float Score);
|
||||
|
|
@ -7407,3 +7409,177 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurfa
|
|||
|
||||
return Surface;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
const FHyperTwistTrainingDeck& ActiveDeck,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface Surface;
|
||||
Surface.SelectorId = SelectorId;
|
||||
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
|
||||
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
|
||||
Surface.Headline = TEXT("Generated Mode Selector Option Coverage");
|
||||
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
|
||||
|
||||
if (!SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
|
||||
? FString::Printf(TEXT("Generated Mode Selector Option Coverage: %s"), *SelectorId)
|
||||
: FString::Printf(
|
||||
TEXT("Generated Mode Selector Option Coverage: %s"),
|
||||
*OptionMenuSurface.SelectorLabel
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectorId.IsEmpty())
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option coverage requires a selector id from the active selector roster.");
|
||||
Surface.CoverageLine = TEXT("Case coverage: n/a");
|
||||
Surface.SelectionCoverageLine = TEXT("Current selection: n/a");
|
||||
Surface.DetailLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Option detail: n/a")
|
||||
: FString::Printf(TEXT("Option detail: %s"), *OptionMenuSurface.StatusLine);
|
||||
return Surface;
|
||||
}
|
||||
|
||||
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface& OptionEntry :
|
||||
OptionMenuSurface.OptionEntries)
|
||||
{
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilityEntrySurface Entry;
|
||||
Entry.OptionId = OptionEntry.OptionId;
|
||||
Entry.Label = OptionEntry.Label;
|
||||
Entry.OptionValue = OptionEntry.OptionValue;
|
||||
Entry.bMatchesCurrentSelection = OptionEntry.bMatchesCurrentSelection;
|
||||
|
||||
FHyperTwistTrainingCase MatchingCase;
|
||||
Entry.bHasCoveredCase = UHyperTwistTrainingCatalogLibrary::TryGetImportedRuntimeSelectorOptionCaseFromDeck(
|
||||
ActiveDeck,
|
||||
SelectorId,
|
||||
OptionEntry.OptionId,
|
||||
MatchingCase
|
||||
);
|
||||
if (Entry.bHasCoveredCase)
|
||||
{
|
||||
Entry.MatchedCaseId = MatchingCase.CaseId;
|
||||
Entry.MatchedCaseLabel = MatchingCase.PromptLabel.IsEmpty()
|
||||
? MatchingCase.CaseId
|
||||
: MatchingCase.PromptLabel;
|
||||
Entry.ActionabilityLine = Entry.MatchedCaseLabel.IsEmpty()
|
||||
? TEXT("Case coverage: owned case available")
|
||||
: FString::Printf(TEXT("Case coverage: %s"), *Entry.MatchedCaseLabel);
|
||||
++Surface.CoveredOptionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.ActionabilityLine = TEXT("Case coverage: selection-only choice");
|
||||
++Surface.SelectionOnlyOptionCount;
|
||||
}
|
||||
|
||||
if (Entry.bMatchesCurrentSelection)
|
||||
{
|
||||
Surface.bCurrentSelectionHasCaseCoverage = Entry.bHasCoveredCase;
|
||||
Surface.bCurrentSelectionIsSelectionOnly = !Entry.bHasCoveredCase;
|
||||
}
|
||||
|
||||
Surface.OptionEntries.Add(MoveTemp(Entry));
|
||||
}
|
||||
|
||||
Surface.bHasCaseCoverage = Surface.CoveredOptionCount > 0;
|
||||
Surface.CoverageLine = Surface.OptionEntries.Num() > 0
|
||||
? FString::Printf(
|
||||
TEXT("Case coverage: %d covered | %d selection-only"),
|
||||
Surface.CoveredOptionCount,
|
||||
Surface.SelectionOnlyOptionCount
|
||||
)
|
||||
: TEXT("Case coverage: no imported options");
|
||||
|
||||
if (OptionMenuSurface.bSelectionMatchesAvailableOption && Surface.bCurrentSelectionHasCaseCoverage)
|
||||
{
|
||||
Surface.SelectionCoverageLine = FString::Printf(
|
||||
TEXT("Current selection maps to owned case: %s"),
|
||||
OptionMenuSurface.MatchedOptionLabel.IsEmpty()
|
||||
? (OptionMenuSurface.MatchedOptionId.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *OptionMenuSurface.MatchedOptionId)
|
||||
: *OptionMenuSurface.MatchedOptionLabel
|
||||
);
|
||||
}
|
||||
else if (OptionMenuSurface.bSelectionMatchesAvailableOption && Surface.bCurrentSelectionIsSelectionOnly)
|
||||
{
|
||||
Surface.SelectionCoverageLine =
|
||||
TEXT("Current selection remains available, but it is currently selection-only.");
|
||||
}
|
||||
else if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
|
||||
{
|
||||
Surface.SelectionCoverageLine = OptionMenuSurface.UnresolvedSelectionLine.IsEmpty()
|
||||
? TEXT("Current selection does not currently map to an imported option.")
|
||||
: OptionMenuSurface.UnresolvedSelectionLine;
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelection)
|
||||
{
|
||||
Surface.SelectionCoverageLine = TEXT("Current selection has no additional case-coverage detail.");
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector)
|
||||
{
|
||||
Surface.SelectionCoverageLine = TEXT("Current selection: unresolved");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.SelectionCoverageLine = TEXT("Current selection: n/a");
|
||||
}
|
||||
|
||||
if (OptionMenuSurface.bHasSelector && Surface.CoveredOptionCount > 0
|
||||
&& Surface.SelectionOnlyOptionCount == 0)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option coverage exposes %d covered option%s and no selection-only choices."),
|
||||
Surface.CoveredOptionCount,
|
||||
Surface.CoveredOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector && Surface.CoveredOptionCount > 0)
|
||||
{
|
||||
Surface.StatusLine = FString::Printf(
|
||||
TEXT("Generated-mode selector option coverage exposes %d covered option%s and %d selection-only choice%s."),
|
||||
Surface.CoveredOptionCount,
|
||||
Surface.CoveredOptionCount == 1 ? TEXT("") : TEXT("s"),
|
||||
Surface.SelectionOnlyOptionCount,
|
||||
Surface.SelectionOnlyOptionCount == 1 ? TEXT("") : TEXT("s")
|
||||
);
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector && OptionMenuSurface.bHasOptions)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option coverage is active, but none of the imported options currently map to owned cases.");
|
||||
}
|
||||
else if (OptionMenuSurface.bHasSelector)
|
||||
{
|
||||
Surface.StatusLine =
|
||||
TEXT("Generated-mode selector option coverage is active, but this selector does not expose imported options.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
|
||||
? TEXT("Generated-mode selector option coverage 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("Option detail: %s | Selector: %s | Binding: %s"),
|
||||
OptionMenuSurface.StatusLine.IsEmpty() ? TEXT("n/a") : *OptionMenuSurface.StatusLine,
|
||||
*SelectorSummary,
|
||||
*BindingSummary
|
||||
);
|
||||
|
||||
return Surface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -719,6 +719,20 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionMenu
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
CachedRunState.ActiveDeck,
|
||||
SelectorId,
|
||||
OptionMenuSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
|
||||
const int32 MaxCases,
|
||||
const FString& StartActionSourceLabel
|
||||
|
|
|
|||
|
|
@ -700,6 +700,20 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionMen
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
const FString& SelectorId
|
||||
) const
|
||||
{
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
|
||||
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
CachedRunState.ActiveDeck,
|
||||
SelectorId,
|
||||
OptionMenuSurface
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
|
||||
const int32 MaxCases
|
||||
)
|
||||
|
|
|
|||
|
|
@ -696,4 +696,12 @@ public:
|
|||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface& SelectorRosterSurface
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
DeriveCoachGeneratedModeSelectorOptionActionabilitySurface(
|
||||
const FHyperTwistTrainingDeck& ActiveDeck,
|
||||
const FString& SelectorId,
|
||||
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -260,6 +260,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedRun(
|
||||
int32 MaxCases,
|
||||
|
|
|
|||
|
|
@ -241,6 +241,10 @@ public:
|
|||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(const FString& SelectorId) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);
|
||||
|
||||
|
|
|
|||
|
|
@ -8676,3 +8676,103 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
|
|||
|| bHasSelection;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilityEntrySurface
|
||||
{
|
||||
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 ActionabilityLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MatchedCaseId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MatchedCaseLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMatchesCurrentSelection = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasCoveredCase = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !OptionId.IsEmpty()
|
||||
|| !Label.IsEmpty()
|
||||
|| !ActionabilityLine.IsEmpty()
|
||||
|| !MatchedCaseId.IsEmpty()
|
||||
|| bMatchesCurrentSelection
|
||||
|| bHasCoveredCase;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface
|
||||
{
|
||||
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 CoverageLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SelectionCoverageLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilityEntrySurface> OptionEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 CoveredOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SelectionOnlyOptionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasSelector = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasCaseCoverage = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionHasCaseCoverage = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCurrentSelectionIsSelectionOnly = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !StatusLine.IsEmpty()
|
||||
|| !SelectorId.IsEmpty()
|
||||
|| OptionEntries.Num() > 0
|
||||
|| bHasSelector
|
||||
|| bHasCaseCoverage;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 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 latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option actionability / case-coverage surface over the already-cached selector/runtime state, 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 next bounded packet is to expose one thin generated-mode selector option interaction / dispatch surface on those same two consumer surfaces 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 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 actionability / case-coverage 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 detail surface by exposing one parameterized option actionability / case-coverage surface for a chosen selector id.
|
||||
|
||||
The open tasks were:
|
||||
|
||||
- stop forcing gameplay / Blueprint callers to read raw option-case payloads or run their own option-to-case probes just to tell which imported options still map to owned cases
|
||||
- expose which imported options remain selection-only choices even though they still exist in the selector option menu
|
||||
- keep the surface derived from already-cached gameplay consumer selector/runtime state and the existing owned deck rather than widening into new backend behavior
|
||||
|
||||
It is not:
|
||||
|
||||
- a new selector-application behavior packet
|
||||
- a generated-mode execution packet
|
||||
- a selector option dispatch / interaction packet
|
||||
- a broader gameplay UI composition pass
|
||||
|
||||
## Scope
|
||||
|
||||
Bounded lane:
|
||||
|
||||
- add first-party generated-mode selector option actionability surfaces in shared training types
|
||||
- add a coach-library helper that derives case coverage from the active deck, requested selector id, and the already-landed selector option-menu surface
|
||||
- expose that parameterized actionability surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
|
||||
- keep the derivation presentation-only and rooted in already-owned deck cases plus cached gameplay consumer selector state
|
||||
|
||||
Out of scope:
|
||||
|
||||
- changing selector-choice persistence
|
||||
- changing generated-mode launch-config derivation rules
|
||||
- changing direct option-launch or generated-choice apply behavior
|
||||
- widening into per-option dispatch posture 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
|
||||
- those same consumers already exposed truthful chosen-selector option detail
|
||||
|
||||
But one thin consumer seam was still open:
|
||||
|
||||
- callers could render the option menu for a chosen selector
|
||||
- but they still had to inspect raw option-case behavior to explain which options still mapped to owned cases and which were now selection-only choices
|
||||
|
||||
That meant:
|
||||
|
||||
- option detail was thin and reusable
|
||||
- but case coverage still leaked low-level option matching work into callers
|
||||
|
||||
So the next bounded move was:
|
||||
|
||||
- keep the already-landed generated-mode context, roster, and option-detail surfaces unchanged
|
||||
- and expose one narrow option actionability / case-coverage surface above the owned deck case set
|
||||
|
||||
## What landed
|
||||
|
||||
Primary code changes:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilityEntrySurface`
|
||||
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionActionabilitySurface`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
|
||||
- added `DeriveCoachGeneratedModeSelectorOptionActionabilitySurface(...)`
|
||||
- case coverage is derived from the active owned deck, requested selector id, and the already-landed selector option-menu surface
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(const FString& SelectorId)`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
|
||||
- added `GetDisplayedCoachGeneratedModeSelectorOptionActionabilitySurface(const FString& SelectorId)`
|
||||
|
||||
Surface behavior:
|
||||
|
||||
- reports which imported options for the chosen selector still map to owned deck cases
|
||||
- reports which imported options remain selection-only choices
|
||||
- reports whether the current cached selection is case-covered, selection-only, or no longer present in the imported option list
|
||||
- keeps the surface presentation-only and compatible with the already-landed generated-mode option-detail surface
|
||||
|
||||
## 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 interaction / dispatch surface so 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.
|
||||
Loading…
Add table
Reference in a new issue