From 0ba5cd033a0cbdd51fe1f26bba517056ebc13c67 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Fri, 1 May 2026 23:10:04 +0200 Subject: [PATCH] Track imported generated selector choices in runs --- .../HyperTwistTrainingLibrary.cpp | 5 ++ .../HyperTwistTrainingRuntimeLibrary.cpp | 33 ++++++++ .../HyperTwistTrainingSubsystem.cpp | 84 +++++++++++++++++++ .../HyperTwistTrainingRuntimeLibrary.h | 14 ++++ .../HyperTwistTrainingSubsystem.h | 12 +++ .../HyperTwistTrainingTypes.h | 46 ++++++++++ 6 files changed, 194 insertions(+) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp index 98eff73..40ec521 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp @@ -1083,6 +1083,11 @@ FHyperTwistTrainingRunState UHyperTwistTrainingLibrary::StartTrainingRun( { return RunState; } + if (Deck.ImportedRuntimeSurface.IsStructurallyValid()) + { + RunState.ImportedRuntimeSelectionState.SurfaceId = Deck.ImportedRuntimeSurface.SurfaceId; + RunState.ImportedRuntimeSelectionState.SurfaceKind = Deck.ImportedRuntimeSurface.SurfaceKind; + } for (const FHyperTwistTrainingCase& TrainingCase : Deck.Cases) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.cpp index f831e1b..6a761a6 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.cpp @@ -102,6 +102,20 @@ bool UHyperTwistTrainingRuntimeLibrary::TryGetActiveImportedRuntimeSelectorOptio return false; } +bool UHyperTwistTrainingRuntimeLibrary::TryGetActiveImportedRuntimeSelectionState( + UObject* WorldContextObject, + FHyperTwistTrainingImportedRuntimeSelectionState& OutSelectionState +) +{ + if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject)) + { + return TrainingSubsystem->TryGetActiveImportedRuntimeSelectionState(OutSelectionState); + } + + OutSelectionState = FHyperTwistTrainingImportedRuntimeSelectionState(); + return false; +} + FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::ApplyActiveImportedRuntimeSelectorOption( UObject* WorldContextObject, const FString& SelectorId, @@ -116,6 +130,25 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::ApplyActiveImport return FHyperTwistTrainingRunState(); } +FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::ApplyActiveImportedGeneratedModeSelectorChoice( + UObject* WorldContextObject, + const FString& SelectorId, + const FString& SelectedValue, + const FString& DisplayValue +) +{ + if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject)) + { + return TrainingSubsystem->ApplyActiveImportedGeneratedModeSelectorChoice( + SelectorId, + SelectedValue, + DisplayValue + ); + } + + return FHyperTwistTrainingRunState(); +} + FHyperTwistTrainingMethodDrillRunState UHyperTwistTrainingRuntimeLibrary::GetActiveMethodDrillRunState( UObject* WorldContextObject ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index e54431c..06f859d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -745,6 +745,20 @@ bool UHyperTwistTrainingSubsystem::TryGetActiveImportedRuntimeSelectorOptionCase ); } +bool UHyperTwistTrainingSubsystem::TryGetActiveImportedRuntimeSelectionState( + FHyperTwistTrainingImportedRuntimeSelectionState& OutSelectionState +) const +{ + OutSelectionState = FHyperTwistTrainingImportedRuntimeSelectionState(); + if (!HasActiveRun() || !ActiveRunState.ImportedRuntimeSelectionState.IsStructurallyValid()) + { + return false; + } + + OutSelectionState = ActiveRunState.ImportedRuntimeSelectionState; + return OutSelectionState.IsStructurallyValid(); +} + FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::ApplyActiveImportedRuntimeSelectorOption( const FString& SelectorId, const FString& OptionId @@ -781,6 +795,76 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::ApplyActiveImportedRun ); } +FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::ApplyActiveImportedGeneratedModeSelectorChoice( + const FString& SelectorId, + const FString& SelectedValue, + const FString& DisplayValue +) +{ + if (!HasActiveRun() || SelectedValue.IsEmpty()) + { + return FHyperTwistTrainingRunState(); + } + + FHyperTwistTrainingImportedRuntimeSurface RuntimeSurface; + if (!UHyperTwistTrainingCatalogLibrary::TryGetImportedRuntimeSurfaceFromDeck( + ActiveRunState.ActiveDeck, + RuntimeSurface) + || !RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase)) + { + return FHyperTwistTrainingRunState(); + } + + FHyperTwistTrainingImportedRuntimeSelector Selector; + if (!UHyperTwistTrainingCatalogLibrary::TryGetImportedRuntimeSelectorFromDeck( + ActiveRunState.ActiveDeck, + SelectorId, + Selector)) + { + return FHyperTwistTrainingRunState(); + } + + if (!ActiveRunState.ImportedRuntimeSelectionState.IsStructurallyValid()) + { + ActiveRunState.ImportedRuntimeSelectionState.SurfaceId = RuntimeSurface.SurfaceId; + ActiveRunState.ImportedRuntimeSelectionState.SurfaceKind = RuntimeSurface.SurfaceKind; + } + + FHyperTwistTrainingImportedRuntimeSelectorChoice* ExistingChoice = + ActiveRunState.ImportedRuntimeSelectionState.SelectorChoices.FindByPredicate( + [&SelectorId](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate) + { + return Candidate.SelectorId == SelectorId; + } + ); + if (ExistingChoice != nullptr) + { + ExistingChoice->SelectorBinding = Selector.SelectorBinding; + ExistingChoice->SelectedValue = SelectedValue; + ExistingChoice->DisplayValue = DisplayValue.IsEmpty() ? SelectedValue : DisplayValue; + } + else + { + FHyperTwistTrainingImportedRuntimeSelectorChoice Choice; + Choice.SelectorId = SelectorId; + Choice.SelectorBinding = Selector.SelectorBinding; + Choice.SelectedValue = SelectedValue; + Choice.DisplayValue = DisplayValue.IsEmpty() ? SelectedValue : DisplayValue; + ActiveRunState.ImportedRuntimeSelectionState.SelectorChoices.Add(MoveTemp(Choice)); + } + + ActiveRunState.ActiveDeck.Tags.AddUnique(TEXT("runtime-surface:generated-choice-applied")); + ActiveRunState.ActiveDeck.Tags.AddUnique(FString::Printf(TEXT("selector:%s"), *SelectorId.ToLower())); + ActiveRunState.ActiveDeck.Tags.AddUnique(FString::Printf(TEXT("selector-choice:%s"), *SelectedValue.ToLower())); + RefreshSummary(); + TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::RecordRunState( + TrainingRepositoryState, + ActiveRunState + ); + RefreshRepositoryViews(); + return ActiveRunState; +} + FHyperTwistTrainingMethodDrillRunState UHyperTwistTrainingSubsystem::GetActiveMethodDrillRunState() const { return ActiveMethodDrillRunState; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h index d3e9d23..d42d29f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h @@ -48,6 +48,12 @@ public: TArray& OutCases ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject")) + static bool TryGetActiveImportedRuntimeSelectionState( + UObject* WorldContextObject, + FHyperTwistTrainingImportedRuntimeSelectionState& OutSelectionState + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject")) static FHyperTwistTrainingRunState ApplyActiveImportedRuntimeSelectorOption( UObject* WorldContextObject, @@ -55,6 +61,14 @@ public: const FString& OptionId ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject")) + static FHyperTwistTrainingRunState ApplyActiveImportedGeneratedModeSelectorChoice( + UObject* WorldContextObject, + const FString& SelectorId, + const FString& SelectedValue, + const FString& DisplayValue + ); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill", meta = (WorldContext = "WorldContextObject")) static FHyperTwistTrainingMethodDrillRunState GetActiveMethodDrillRunState(UObject* WorldContextObject); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSubsystem.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSubsystem.h index c6bbfba..013a52a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSubsystem.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSubsystem.h @@ -38,12 +38,24 @@ public: TArray& OutCases ) const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") + bool TryGetActiveImportedRuntimeSelectionState( + FHyperTwistTrainingImportedRuntimeSelectionState& OutSelectionState + ) const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") FHyperTwistTrainingRunState ApplyActiveImportedRuntimeSelectorOption( const FString& SelectorId, const FString& OptionId ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") + FHyperTwistTrainingRunState ApplyActiveImportedGeneratedModeSelectorChoice( + const FString& SelectorId, + const FString& SelectedValue, + const FString& DisplayValue + ); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill") FHyperTwistTrainingMethodDrillRunState GetActiveMethodDrillRunState() const; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index c543fa9..e967c0e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -450,6 +450,49 @@ struct FHyperTwistTrainingImportedRuntimeSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingImportedRuntimeSelectorChoice +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectorId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectorBinding; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedValue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayValue; + + bool IsStructurallyValid() const + { + return !SelectorId.IsEmpty() && !SelectedValue.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingImportedRuntimeSelectionState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SurfaceId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SurfaceKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray SelectorChoices; + + bool IsStructurallyValid() const + { + return !SurfaceId.IsEmpty() && !SurfaceKind.IsEmpty(); + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingDeck { @@ -925,6 +968,9 @@ struct FHyperTwistTrainingRunState UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistReplayEvent LastReplayEvent; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingImportedRuntimeSelectionState ImportedRuntimeSelectionState; + bool IsStructurallyValid() const { return ActiveDeck.IsStructurallyValid()