Close Roux Bound2 drill recovery packet

This commit is contained in:
axiomlogicnexus 2026-04-28 23:38:03 +02:00
parent 5ac8ff9169
commit 7a715358a0
11 changed files with 684 additions and 1 deletions

View file

@ -496,6 +496,48 @@ FHyperTwistTrainingMethodDrillBatch UHyperTwistContractLibrary::MakeSampleMethod
);
}
FHyperTwistTrainingMethodDrillDefinition UHyperTwistContractLibrary::MakeSampleFavoriteFirstMethodDrillDefinition()
{
const FHyperTwistTrainingMethodDrillDefinition BaseDefinition = MakeSampleMethodDrillDefinition();
if (!BaseDefinition.IsStructurallyValid())
{
return FHyperTwistTrainingMethodDrillDefinition();
}
TArray<FHyperTwistTrainingMethodDrillFavoriteEntry> Favorites;
FHyperTwistTrainingMethodDrillFavoriteEntry FavoriteEntry = MakeSampleMethodDrillFavoriteEntry();
FavoriteEntry.CaseId = TEXT("roux-block-case-02");
Favorites.Add(FavoriteEntry);
return UHyperTwistTrainingRepositoryLibrary::BuildDrillDefinitionFavoriteFirst(
BaseDefinition,
Favorites,
1
);
}
FHyperTwistTrainingMethodDrillBatchReadiness UHyperTwistContractLibrary::MakeSampleMethodDrillBatchReadiness()
{
return UHyperTwistTrainingRepositoryLibrary::DeriveDrillBatchReadiness(
MakeSampleMethodDrillBatch(),
MakeSampleMethodDrillDefinition()
);
}
FHyperTwistTrainingDrillCaseModeCompatibility UHyperTwistContractLibrary::MakeSampleMethodDrillCaseModeCompatibility()
{
const FHyperTwistTrainingMethodDrillDefinition DrillDefinition = MakeSampleMethodDrillDefinition();
if (!DrillDefinition.IsStructurallyValid() || DrillDefinition.SourceDeck.Cases.Num() == 0)
{
return FHyperTwistTrainingDrillCaseModeCompatibility();
}
return UHyperTwistTrainingLibrary::DeriveDrillCaseModeCompatibility(
DrillDefinition.SourceDeck.Cases[0],
DrillDefinition.DrillMode,
DrillDefinition.VisibilityPolicy
);
}
FHyperTwistTrainingRunStepResult UHyperTwistContractLibrary::MakeSampleTrainingRunStepResult()
{
const FHyperTwistTrainingRunState RunState = MakeSampleTrainingRunState();

View file

@ -1638,6 +1638,15 @@ FHyperTwistTrainingMethodDrillDefinition UHyperTwistTrainingLibrary::BuildMethod
DrillDefinition.SourceDeckId = Deck.DeckId;
DrillDefinition.SourceDeck = Deck;
DrillDefinition.SourceDeck.Cases = FilterDrillCasesByModeCompatibility(
Deck.Cases,
DrillMode,
VisibilityPolicy
);
if (DrillDefinition.SourceDeck.Cases.Num() == 0)
{
return FHyperTwistTrainingMethodDrillDefinition();
}
DrillDefinition.FocusCaseIds = HyperTwistTrainingLibraryInternal::BuildDrillCaseIds(DrillDefinition);
DrillDefinition.DrillMode = DrillMode;
DrillDefinition.VisibilityPolicy = VisibilityPolicy;
@ -1963,3 +1972,105 @@ FHyperTwistTrainingMethodDrillBatch UHyperTwistTrainingLibrary::BuildMethodDrill
DrillBatch.bPinnedFavoritesOnly = bPinnedFavoritesOnly;
return DrillBatch;
}
FHyperTwistTrainingDrillCaseModeCompatibility UHyperTwistTrainingLibrary::DeriveDrillCaseModeCompatibility(
const FHyperTwistTrainingCase& TrainingCase,
EHyperTwistTrainingDrillModeKind DrillMode,
EHyperTwistTrainingDrillVisibilityPolicy VisibilityPolicy
)
{
FHyperTwistTrainingDrillCaseModeCompatibility Result;
Result.CaseId = TrainingCase.CaseId;
Result.DrillMode = DrillMode;
Result.VisibilityPolicy = VisibilityPolicy;
if (!TrainingCase.IsStructurallyValid())
{
return Result;
}
const bool bHasNotation = !TrainingCase.CanonicalNotation.IsEmpty();
const bool bHasStateRef = !TrainingCase.CanonicalStateRef.IsEmpty();
const bool bHasScramble = !TrainingCase.ScrambleNotation.IsEmpty();
const bool bHasPromptLabel = !TrainingCase.PromptLabel.IsEmpty();
const EHyperTwistTrainingPromptKind PromptKind = TrainingCase.PromptKind;
switch (DrillMode)
{
case EHyperTwistTrainingDrillModeKind::Recognition:
Result.bCompatibleWithMode =
bHasStateRef
|| bHasScramble
|| PromptKind == EHyperTwistTrainingPromptKind::Recognition
|| PromptKind == EHyperTwistTrainingPromptKind::State;
break;
case EHyperTwistTrainingDrillModeKind::Recall:
Result.bCompatibleWithMode =
bHasNotation
|| PromptKind == EHyperTwistTrainingPromptKind::Algorithm
|| PromptKind == EHyperTwistTrainingPromptKind::Sequence
|| PromptKind == EHyperTwistTrainingPromptKind::Memo;
break;
case EHyperTwistTrainingDrillModeKind::Execution:
Result.bCompatibleWithMode =
bHasNotation
|| PromptKind == EHyperTwistTrainingPromptKind::Algorithm
|| PromptKind == EHyperTwistTrainingPromptKind::Sequence;
break;
case EHyperTwistTrainingDrillModeKind::Reconstruction:
Result.bCompatibleWithMode =
bHasStateRef
|| PromptKind == EHyperTwistTrainingPromptKind::State
|| PromptKind == EHyperTwistTrainingPromptKind::Recognition;
break;
default:
Result.bCompatibleWithMode = false;
break;
}
switch (VisibilityPolicy)
{
case EHyperTwistTrainingDrillVisibilityPolicy::FullInformation:
Result.bCompatibleWithVisibility = true;
break;
case EHyperTwistTrainingDrillVisibilityPolicy::PromptOnly:
Result.bCompatibleWithVisibility = bHasPromptLabel || bHasNotation;
break;
case EHyperTwistTrainingDrillVisibilityPolicy::MaskedState:
case EHyperTwistTrainingDrillVisibilityPolicy::PartialState:
Result.bCompatibleWithVisibility = bHasStateRef;
break;
default:
Result.bCompatibleWithVisibility = false;
break;
}
return Result;
}
TArray<FHyperTwistTrainingCase> UHyperTwistTrainingLibrary::FilterDrillCasesByModeCompatibility(
const TArray<FHyperTwistTrainingCase>& Cases,
EHyperTwistTrainingDrillModeKind DrillMode,
EHyperTwistTrainingDrillVisibilityPolicy VisibilityPolicy
)
{
TArray<FHyperTwistTrainingCase> Filtered;
for (const FHyperTwistTrainingCase& TrainingCase : Cases)
{
const FHyperTwistTrainingDrillCaseModeCompatibility Compat =
DeriveDrillCaseModeCompatibility(TrainingCase, DrillMode, VisibilityPolicy);
if (Compat.bCompatibleWithMode && Compat.bCompatibleWithVisibility)
{
Filtered.Add(TrainingCase);
}
}
return Filtered;
}

View file

@ -2953,6 +2953,211 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::RemoveM
return UpdatedRepositoryState;
}
FHyperTwistTrainingMethodDrillBatchReadiness UHyperTwistTrainingRepositoryLibrary::DeriveDrillBatchReadiness(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& CurrentDefinition
)
{
FHyperTwistTrainingMethodDrillBatchReadiness Readiness;
Readiness.BatchId = DrillBatch.BatchId;
Readiness.LastUpdatedAtUtc = DrillBatch.LastUpdatedAtUtc;
if (!DrillBatch.IsStructurallyValid())
{
return Readiness;
}
TSet<FString> AllowedCaseIds;
for (const FHyperTwistTrainingCase& TrainingCase : CurrentDefinition.SourceDeck.Cases)
{
if (!TrainingCase.CaseId.IsEmpty())
{
AllowedCaseIds.Add(TrainingCase.CaseId);
}
}
if (CurrentDefinition.FocusCaseIds.Num() > 0)
{
for (const FString& FocusId : CurrentDefinition.FocusCaseIds)
{
AllowedCaseIds.Add(FocusId);
}
}
bool bFoundStaleCase = false;
for (const FString& PendingId : DrillBatch.PendingCaseIds)
{
if (!AllowedCaseIds.Contains(PendingId))
{
bFoundStaleCase = true;
}
}
if (!DrillBatch.CurrentCaseId.IsEmpty() && !AllowedCaseIds.Contains(DrillBatch.CurrentCaseId))
{
bFoundStaleCase = true;
}
for (const FString& CompletedId : DrillBatch.CompletedCaseIds)
{
if (!AllowedCaseIds.Contains(CompletedId))
{
bFoundStaleCase = true;
}
}
Readiness.bIsStale = bFoundStaleCase;
const int32 PendingCount = DrillBatch.PendingCaseIds.Num();
const bool bHasActiveCurrent = !DrillBatch.CurrentCaseId.IsEmpty()
&& !DrillBatch.CompletedCaseIds.Contains(DrillBatch.CurrentCaseId);
Readiness.RemainingCaseCount = PendingCount + (bHasActiveCurrent ? 1 : 0);
Readiness.CompletedCaseCount = DrillBatch.CompletedCaseIds.Num();
Readiness.bIsResumable = Readiness.RemainingCaseCount > 0;
const int32 Total = Readiness.RemainingCaseCount + Readiness.CompletedCaseCount;
Readiness.CompletionFraction = Total > 0
? static_cast<float>(Readiness.CompletedCaseCount) / static_cast<float>(Total)
: 0.0f;
return Readiness;
}
TArray<FHyperTwistTrainingMethodDrillBatch> UHyperTwistTrainingRepositoryLibrary::ListResumableDrillBatches(
const FHyperTwistTrainingRepositoryState& RepositoryState,
const FString& UserId,
const FString& MethodSegmentId
)
{
TArray<FHyperTwistTrainingMethodDrillBatch> AllBatches =
ListMethodDrillBatches(RepositoryState, UserId, MethodSegmentId);
TArray<FHyperTwistTrainingMethodDrillBatch> Resumable;
for (const FHyperTwistTrainingMethodDrillBatch& Batch : AllBatches)
{
const bool bHasPending = Batch.PendingCaseIds.Num() > 0;
const bool bHasActiveCurrent = !Batch.CurrentCaseId.IsEmpty()
&& !Batch.CompletedCaseIds.Contains(Batch.CurrentCaseId);
if (bHasPending || bHasActiveCurrent)
{
Resumable.Add(Batch);
}
}
return Resumable;
}
FHyperTwistTrainingMethodDrillBatch UHyperTwistTrainingRepositoryLibrary::RebuildDrillBatchForResume(
const FHyperTwistTrainingMethodDrillBatch& SavedBatch,
const FHyperTwistTrainingMethodDrillDefinition& CurrentDefinition
)
{
FHyperTwistTrainingMethodDrillBatch Rebuilt = SavedBatch;
if (!SavedBatch.IsStructurallyValid())
{
return Rebuilt;
}
TSet<FString> AllowedCaseIds;
for (const FHyperTwistTrainingCase& TrainingCase : CurrentDefinition.SourceDeck.Cases)
{
if (!TrainingCase.CaseId.IsEmpty())
{
AllowedCaseIds.Add(TrainingCase.CaseId);
}
}
if (CurrentDefinition.FocusCaseIds.Num() > 0)
{
for (const FString& FocusId : CurrentDefinition.FocusCaseIds)
{
AllowedCaseIds.Add(FocusId);
}
}
Rebuilt.PendingCaseIds.RemoveAll([&AllowedCaseIds](const FString& CaseId)
{
return !AllowedCaseIds.Contains(CaseId);
});
Rebuilt.CompletedCaseIds.RemoveAll([&AllowedCaseIds](const FString& CaseId)
{
return !AllowedCaseIds.Contains(CaseId);
});
Rebuilt.Responses.RemoveAll([&AllowedCaseIds](const FHyperTwistTrainingMethodDrillResponseRecord& Record)
{
return !AllowedCaseIds.Contains(Record.CaseId);
});
const bool bCurrentStillValid = !Rebuilt.CurrentCaseId.IsEmpty()
&& AllowedCaseIds.Contains(Rebuilt.CurrentCaseId);
if (!bCurrentStillValid)
{
if (Rebuilt.PendingCaseIds.Num() > 0)
{
Rebuilt.CurrentCaseId = Rebuilt.PendingCaseIds[0];
Rebuilt.PendingCaseIds.RemoveAt(0);
}
else
{
Rebuilt.CurrentCaseId = FString();
}
}
Rebuilt.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
return Rebuilt;
}
FHyperTwistTrainingMethodDrillDefinition UHyperTwistTrainingRepositoryLibrary::BuildDrillDefinitionFavoriteFirst(
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const TArray<FHyperTwistTrainingMethodDrillFavoriteEntry>& Favorites,
int32 MaxFavoriteCases
)
{
FHyperTwistTrainingMethodDrillDefinition Result = BaseDefinition;
if (!BaseDefinition.IsStructurallyValid())
{
return Result;
}
TSet<FString> DeckCaseIds;
for (const FHyperTwistTrainingCase& TrainingCase : BaseDefinition.SourceDeck.Cases)
{
if (!TrainingCase.CaseId.IsEmpty())
{
DeckCaseIds.Add(TrainingCase.CaseId);
}
}
TArray<FString> FavoriteCaseIds;
for (const FHyperTwistTrainingMethodDrillFavoriteEntry& FavoriteEntry : Favorites)
{
if (!FavoriteEntry.CaseId.IsEmpty() && DeckCaseIds.Contains(FavoriteEntry.CaseId))
{
FavoriteCaseIds.AddUnique(FavoriteEntry.CaseId);
if (MaxFavoriteCases > 0 && FavoriteCaseIds.Num() >= MaxFavoriteCases)
{
break;
}
}
}
TArray<FString> OrderedCaseIds = FavoriteCaseIds;
for (const FHyperTwistTrainingCase& TrainingCase : BaseDefinition.SourceDeck.Cases)
{
if (!TrainingCase.CaseId.IsEmpty() && !OrderedCaseIds.Contains(TrainingCase.CaseId))
{
OrderedCaseIds.Add(TrainingCase.CaseId);
}
}
Result.FocusCaseIds = OrderedCaseIds;
if (FavoriteCaseIds.Num() > 0)
{
Result.EntrySource = EHyperTwistTrainingDrillEntrySource::Favorites;
}
return Result;
}
FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertRunRecord(
const FHyperTwistTrainingRepositoryState& RepositoryState,
const FHyperTwistTrainingRunRecord& RunRecord

View file

@ -514,6 +514,67 @@ TArray<FHyperTwistTrainingMethodDrillBatch> UHyperTwistTrainingRuntimeLibrary::G
return TArray<FHyperTwistTrainingMethodDrillBatch>();
}
TArray<FHyperTwistTrainingMethodDrillBatch> UHyperTwistTrainingRuntimeLibrary::GetResumableMethodDrillBatchesForUser(
UObject* WorldContextObject,
const FString& UserId,
const FString& MethodSegmentId
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->GetResumableMethodDrillBatchesForUser(UserId, MethodSegmentId);
}
return TArray<FHyperTwistTrainingMethodDrillBatch>();
}
FHyperTwistTrainingMethodDrillBatchReadiness UHyperTwistTrainingRuntimeLibrary::DeriveMethodDrillBatchReadiness(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->DeriveMethodDrillBatchReadiness(DrillBatch, DrillDefinition);
}
return FHyperTwistTrainingMethodDrillBatchReadiness();
}
FHyperTwistTrainingMethodDrillBatch UHyperTwistTrainingRuntimeLibrary::RebuildMethodDrillBatchForResume(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->RebuildMethodDrillBatchForResume(DrillBatch, DrillDefinition);
}
return FHyperTwistTrainingMethodDrillBatch();
}
FHyperTwistTrainingMethodDrillDefinition UHyperTwistTrainingRuntimeLibrary::BuildFavoriteFirstMethodDrillDefinition(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const FString& UserId,
int32 MaxFavoriteCases
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->BuildFavoriteFirstMethodDrillDefinition(
BaseDefinition,
UserId,
MaxFavoriteCases
);
}
return FHyperTwistTrainingMethodDrillDefinition();
}
TArray<FHyperTwistTrainingCaseRecommendation> UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCaseRecommendations(UObject* WorldContextObject)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))

View file

@ -863,6 +863,70 @@ TArray<FHyperTwistTrainingMethodDrillBatch> UHyperTwistTrainingSubsystem::GetMet
);
}
TArray<FHyperTwistTrainingMethodDrillBatch> UHyperTwistTrainingSubsystem::GetResumableMethodDrillBatchesForUser(
const FString& UserId,
const FString& MethodSegmentId
) const
{
return UHyperTwistTrainingRepositoryLibrary::ListResumableDrillBatches(
TrainingRepositoryState,
UserId,
MethodSegmentId
);
}
FHyperTwistTrainingMethodDrillBatchReadiness UHyperTwistTrainingSubsystem::DeriveMethodDrillBatchReadiness(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
) const
{
return UHyperTwistTrainingRepositoryLibrary::DeriveDrillBatchReadiness(
DrillBatch,
DrillDefinition
);
}
FHyperTwistTrainingMethodDrillBatch UHyperTwistTrainingSubsystem::RebuildMethodDrillBatchForResume(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
) const
{
return UHyperTwistTrainingRepositoryLibrary::RebuildDrillBatchForResume(
DrillBatch,
DrillDefinition
);
}
FHyperTwistTrainingMethodDrillDefinition UHyperTwistTrainingSubsystem::BuildFavoriteFirstMethodDrillDefinition(
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const FString& UserId,
int32 MaxFavoriteCases
) const
{
if (!BaseDefinition.IsStructurallyValid())
{
return BaseDefinition;
}
const FString EffectiveUserId = !UserId.IsEmpty() ? UserId : ActiveMethodDrillRunState.UserId;
if (EffectiveUserId.IsEmpty())
{
return BaseDefinition;
}
const TArray<FHyperTwistTrainingMethodDrillFavoriteEntry> Favorites =
UHyperTwistTrainingRepositoryLibrary::ListMethodDrillFavorites(
TrainingRepositoryState,
EffectiveUserId,
BaseDefinition.MethodDescriptor.SegmentId
);
return UHyperTwistTrainingRepositoryLibrary::BuildDrillDefinitionFavoriteFirst(
BaseDefinition,
Favorites,
MaxFavoriteCases
);
}
TArray<FHyperTwistTrainingCaseRecommendation> UHyperTwistTrainingSubsystem::GetActiveCaseRecommendations() const
{
return ActiveCaseRecommendations;
@ -2062,11 +2126,49 @@ FHyperTwistTrainingMethodDrillRunState UHyperTwistTrainingSubsystem::ResumeMetho
const FString& RunId
)
{
const FHyperTwistTrainingMethodDrillBatch RebuiltBatch =
UHyperTwistTrainingRepositoryLibrary::RebuildDrillBatchForResume(DrillBatch, DrillDefinition);
if (!RebuiltBatch.IsStructurallyValid())
{
ActiveMethodDrillRunState = FHyperTwistTrainingMethodDrillRunState();
bHasActiveMethodDrillRun = false;
RefreshMethodDrillSummary();
return ActiveMethodDrillRunState;
}
const FHyperTwistTrainingMethodDrillBatchReadiness Readiness =
UHyperTwistTrainingRepositoryLibrary::DeriveDrillBatchReadiness(RebuiltBatch, DrillDefinition);
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertMethodDrillBatch(
TrainingRepositoryState,
RebuiltBatch
);
RefreshRepositoryViews();
ActiveMethodDrillRunState = UHyperTwistTrainingLibrary::ResumeMethodDrillRunFromBatch(
DrillDefinition,
DrillBatch,
RebuiltBatch,
RunId
);
if (ActiveMethodDrillRunState.IsStructurallyValid()
&& !Readiness.bIsResumable
&& ActiveMethodDrillRunState.LifecycleState != EHyperTwistTrainingDrillLifecycleState::Completed
&& ActiveMethodDrillRunState.LifecycleState != EHyperTwistTrainingDrillLifecycleState::Aborted)
{
ActiveMethodDrillRunState = UHyperTwistTrainingLibrary::FinishMethodDrillRun(
ActiveMethodDrillRunState,
false
);
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertMethodDrillBatch(
TrainingRepositoryState,
UHyperTwistTrainingLibrary::BuildMethodDrillBatch(
ActiveMethodDrillRunState,
RebuiltBatch.BatchId,
RebuiltBatch.Title,
RebuiltBatch.bPinnedFavoritesOnly
)
);
RefreshRepositoryViews();
}
bHasActiveMethodDrillRun = ActiveMethodDrillRunState.IsStructurallyValid();
RefreshMethodDrillSummary();
return ActiveMethodDrillRunState;

View file

@ -83,6 +83,15 @@ public:
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static FHyperTwistTrainingMethodDrillBatch MakeSampleMethodDrillBatch();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static FHyperTwistTrainingMethodDrillDefinition MakeSampleFavoriteFirstMethodDrillDefinition();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static FHyperTwistTrainingMethodDrillBatchReadiness MakeSampleMethodDrillBatchReadiness();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static FHyperTwistTrainingDrillCaseModeCompatibility MakeSampleMethodDrillCaseModeCompatibility();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
static FHyperTwistTrainingRunStepResult MakeSampleTrainingRunStepResult();

View file

@ -208,4 +208,18 @@ public:
const FString& Title,
bool bPinnedFavoritesOnly
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static FHyperTwistTrainingDrillCaseModeCompatibility DeriveDrillCaseModeCompatibility(
const FHyperTwistTrainingCase& TrainingCase,
EHyperTwistTrainingDrillModeKind DrillMode,
EHyperTwistTrainingDrillVisibilityPolicy VisibilityPolicy
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
static TArray<FHyperTwistTrainingCase> FilterDrillCasesByModeCompatibility(
const TArray<FHyperTwistTrainingCase>& Cases,
EHyperTwistTrainingDrillModeKind DrillMode,
EHyperTwistTrainingDrillVisibilityPolicy VisibilityPolicy
);
};

View file

@ -98,6 +98,32 @@ public:
const FString& BatchId
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Drill")
static FHyperTwistTrainingMethodDrillBatchReadiness DeriveDrillBatchReadiness(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& CurrentDefinition
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Drill")
static TArray<FHyperTwistTrainingMethodDrillBatch> ListResumableDrillBatches(
const FHyperTwistTrainingRepositoryState& RepositoryState,
const FString& UserId,
const FString& MethodSegmentId
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Drill")
static FHyperTwistTrainingMethodDrillBatch RebuildDrillBatchForResume(
const FHyperTwistTrainingMethodDrillBatch& SavedBatch,
const FHyperTwistTrainingMethodDrillDefinition& CurrentDefinition
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Drill")
static FHyperTwistTrainingMethodDrillDefinition BuildDrillDefinitionFavoriteFirst(
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const TArray<FHyperTwistTrainingMethodDrillFavoriteEntry>& Favorites,
int32 MaxFavoriteCases
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository")
static FHyperTwistTrainingRepositoryState UpsertRunRecord(
const FHyperTwistTrainingRepositoryState& RepositoryState,

View file

@ -178,6 +178,35 @@ public:
const FString& MethodSegmentId
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill", meta = (WorldContext = "WorldContextObject"))
static TArray<FHyperTwistTrainingMethodDrillBatch> GetResumableMethodDrillBatchesForUser(
UObject* WorldContextObject,
const FString& UserId,
const FString& MethodSegmentId
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingMethodDrillBatchReadiness DeriveMethodDrillBatchReadiness(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingMethodDrillBatch RebuildMethodDrillBatchForResume(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingMethodDrillDefinition BuildFavoriteFirstMethodDrillDefinition(
UObject* WorldContextObject,
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const FString& UserId,
int32 MaxFavoriteCases
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
static TArray<FHyperTwistTrainingCaseRecommendation> GetActiveTrainingCaseRecommendations(UObject* WorldContextObject);

View file

@ -152,6 +152,31 @@ public:
const FString& MethodSegmentId
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
TArray<FHyperTwistTrainingMethodDrillBatch> GetResumableMethodDrillBatchesForUser(
const FString& UserId,
const FString& MethodSegmentId
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
FHyperTwistTrainingMethodDrillBatchReadiness DeriveMethodDrillBatchReadiness(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
FHyperTwistTrainingMethodDrillBatch RebuildMethodDrillBatchForResume(
const FHyperTwistTrainingMethodDrillBatch& DrillBatch,
const FHyperTwistTrainingMethodDrillDefinition& DrillDefinition
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Drill")
FHyperTwistTrainingMethodDrillDefinition BuildFavoriteFirstMethodDrillDefinition(
const FHyperTwistTrainingMethodDrillDefinition& BaseDefinition,
const FString& UserId,
int32 MaxFavoriteCases
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
TArray<FHyperTwistTrainingCaseRecommendation> GetActiveCaseRecommendations() const;

View file

@ -2401,6 +2401,65 @@ struct FHyperTwistTrainingMethodDrillBatch
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingMethodDrillBatchReadiness
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BatchId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bIsResumable = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bIsStale = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RemainingCaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CompletedCaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
float CompletionFraction = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString LastUpdatedAtUtc;
bool IsStructurallyValid() const
{
return !BatchId.IsEmpty();
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingDrillCaseModeCompatibility
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CaseId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCompatibleWithMode = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCompatibleWithVisibility = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingDrillModeKind DrillMode = EHyperTwistTrainingDrillModeKind::Recognition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingDrillVisibilityPolicy VisibilityPolicy =
EHyperTwistTrainingDrillVisibilityPolicy::FullInformation;
bool IsStructurallyValid() const
{
return !CaseId.IsEmpty();
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingLearnerPreset
{