/** * Transforms CurveTable data into format more effecient to read at runtime. * UCurveTable requires string parsing to map to GroupName/AttributeSet/Attribute * Each curve in the table represents a *single attribute's values for all levels*. * At runtime, we want *all attribute values at given level*. */ void FAttributeSetInitter::PreloadAttributeSetData(UCurveTable* CurveData) { if(!ensure(CurveData)) { return; } /** * Get list of AttributeSet classes loaded */ TArray<TSubclassOf<UAttributeSet> > ClassList; for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* TestClass = *ClassIt; if (TestClass->IsChildOf(UAttributeSet::StaticClass())) { ClassList.Add(TestClass); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // This can only work right now on POD attribute sets. If we ever support FStrings or TArrays in AttributeSets // we will need to update this code to not use memcpy etc. for (TFieldIterator<UProperty> PropIt(TestClass, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt) { if (!PropIt->HasAllPropertyFlags(CPF_IsPlainOldData)) { ABILITY_LOG(Error, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to Handle AttributeClass %s because it has a non POD property: %s"), *TestClass->GetName(), *PropIt->GetName()); return; } } #endif } } /** * Loop through CurveData table and build sets of Defaults that keyed off of Name + Level */ for (auto It = CurveData->RowMap.CreateConstIterator(); It; ++It) { FString RowName = It.Key().ToString(); FString ClassName; FString SetName; FString AttributeName; FString Temp; RowName.Split(TEXT("."), &ClassName, &Temp); Temp.Split(TEXT("."), &SetName, &AttributeName); if (!ensure(!ClassName.IsEmpty() && !SetName.IsEmpty() && !AttributeName.IsEmpty())) { ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to parse row %s in %s"), *RowName, *CurveData->GetName()); continue; } // Find the AttributeSet TSubclassOf<UAttributeSet> Set = FindBestAttributeClass(ClassList, SetName); if (!Set) { // This is ok, we may have rows in here that don't correspond directly to attributes ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to match AttributeSet from %s (row: %s)"), *SetName, *RowName); continue; } // Find the UProperty UNumericProperty* Property = FindField<UNumericProperty>(*Set, *AttributeName); if (!Property) { ABILITY_LOG(Verbose, TEXT("FAttributeSetInitter::PreloadAttributeSetData Unable to match Attribute from %s (row: %s)"), *AttributeName, *RowName); continue; } FRichCurve* Curve = It.Value(); FName ClassFName = FName(*ClassName); FAttributeSetDefaulsCollection& DefaultCollection = Defaults.FindOrAdd(ClassFName); int32 LastLevel = Curve->GetLastKey().Time; DefaultCollection.LevelData.SetNum(FMath::Max(LastLevel, DefaultCollection.LevelData.Num())); //At this point we know the Name of this "class"/"group", the AttributeSet, and the Property Name. Now loop through the values on the curve to get the attribute default value at each level. for (auto KeyIter = Curve->GetKeyIterator(); KeyIter; ++KeyIter) { const FRichCurveKey& CurveKey = *KeyIter; int32 Level = CurveKey.Time; float Value = CurveKey.Value; FAttributeSetDefaults& SetDefaults = DefaultCollection.LevelData[Level-1]; FAttributeDefaultValueList* DefaultDataList = SetDefaults.DataMap.Find(Set); if (DefaultDataList == nullptr) { ABILITY_LOG(Verbose, TEXT("Initializing new default set for %s[%d]. PropertySize: %d.. DefaultSize: %d"), *Set->GetName(), Level, Set->GetPropertiesSize(), UAttributeSet::StaticClass()->GetPropertiesSize()); DefaultDataList = &SetDefaults.DataMap.Add(Set); } // Import curve value into default data check(DefaultDataList); DefaultDataList->AddPair(Property, Value); } } }