// Copyright Epic Games, Inc. All Rights Reserved. #include "CurveExpressionsDataAsset.h" #if WITH_EDITOR #include "ExpressionEvaluator.h" #endif #include "CurveExpressionCustomVersion.h" #include "String/ParseLines.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(CurveExpressionsDataAsset) TArray FCurveExpressionList::GetAssignments() const { // A helper string to mark an expression as returning an undefined value (for curve removal). static FString UndefExpr(TEXT("undef()")); TArray ExpressionAssignments; int32 LineIndex = 0; UE::String::ParseLines(AssignmentExpressions, [&ExpressionAssignments, &LineIndex](FStringView InLine) { InLine.TrimStartAndEndInline(); int32 AssignmentPos; if (InLine.FindChar('=', AssignmentPos)) { const FStringView AssignmentTarget = InLine.Left(AssignmentPos).TrimStartAndEnd(); const FStringView SourceExpression = InLine.Mid(AssignmentPos + 1).TrimStartAndEnd(); if (!AssignmentTarget.IsEmpty() && !SourceExpression.IsEmpty()) { ExpressionAssignments.Add({LineIndex, FName(AssignmentTarget), SourceExpression}); } } else if (InLine.StartsWith('-')) { const FStringView AssignmentTarget = InLine.Mid(1).TrimStartAndEnd(); ExpressionAssignments.Add({LineIndex, FName(AssignmentTarget), UndefExpr}); } LineIndex++; }); return ExpressionAssignments; } TArray FCurveExpressionList::GetParsedAssignments() const { using namespace CurveExpression::Evaluator; TArray ParsedAssignments; FEngine ExpressionEngine; for (const FCurveExpressionAssignment& Assignment: GetAssignments()) { TVariant Result = ExpressionEngine.Parse(Assignment.Expression); ParsedAssignments.Add({Assignment.LineIndex, Assignment.TargetName, MoveTemp(Result)}); } return ParsedAssignments; } void UCurveExpressionsDataAsset::Serialize(FArchive& Ar) { Ar.UsingCustomVersion(FCurveExpressionCustomVersion::GUID); UObject::Serialize(Ar); #if WITH_EDITOR if (Ar.IsSaving()) { // Make sure the expressions are compiled. CompileExpressions(); } #endif if (Ar.IsLoading() || !ExpressionData.IsValid()) { ExpressionData = MakeShared(); } if (Ar.IsLoading() && Ar.CustomVer(FCurveExpressionCustomVersion::GUID) < FCurveExpressionCustomVersion::ExpressionDataInSharedObject) { ExpressionData->NamedConstants = NamedConstants_DEPRECATED; } else { Ar << ExpressionData->NamedConstants; } // Serialize the compiled map so we can read it in cooked builds. Ar << ExpressionData->ExpressionMap; } #if WITH_EDITOR void UCurveExpressionsDataAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(FCurveExpressionList, AssignmentExpressions)) { CompileExpressions(); } } void UCurveExpressionsDataAsset::CompileExpressions() { using namespace CurveExpression::Evaluator; TSharedPtr NewExpressionData(new FExpressionData); TSet ConstantNames; for (const FCurveExpressionParsedAssignment& Assignment: Expressions.GetParsedAssignments()) { if (const FExpressionObject* Expression = Assignment.Result.TryGet()) { ConstantNames.Append(Expression->GetUsedConstants()); NewExpressionData->ExpressionMap.Add(Assignment.TargetName, *Expression); } } NewExpressionData->NamedConstants = ConstantNames.Array(); // Swap the new set of expressions in. Any client referring to the old expression data will still be // able to do so. ExpressionData = NewExpressionData; } #endif