464 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			464 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "GasaGen_AttributeSets.h"
 | |
| #include "GasaGen_Common.h"
 | |
| 
 | |
| #include "Gasa/AbilitySystem/GasaAbilitySystem.h"
 | |
| #include "Gasa/GasaDevOptions.h"
 | |
| 
 | |
| using namespace gen;
 | |
| 
 | |
| #pragma push_macro("UPROPERTY")
 | |
| #pragma push_macro("UFUNCTION")
 | |
| #pragma push_macro("FORCEINLINE")
 | |
| #pragma push_macro("ensure")
 | |
| #undef UPROPERTY
 | |
| #undef UFUNCTION
 | |
| #undef FORCEINLINE
 | |
| #undef ensure
 | |
| 
 | |
| PRAGMA_DISABLE_OPTIMIZATION
 | |
| void generate_AttributeSets()
 | |
| {
 | |
| 	// All attribute sets are tracked in Gasa's dev options for this project.
 | |
| 	TArray< TSoftObjectPtr<UDataTable>> AttributeSetTables = Gasa::GetDevOptions()->AttributeSets;
 | |
| 	check( AttributeSetTables.Num() > 0 );
 | |
| 	
 | |
| 	// TODO(Ed): Doing one for now
 | |
| 	FGraphEventRef   LoadTableTask;
 | |
| 	UDataTable*      AttributeSetTable = nullptr;
 | |
| 	FGraphEventArray Prerequisites;
 | |
| 	LoadTableTask = FFunctionGraphTask::CreateAndDispatchWhenReady(
 | |
| 		[ & AttributeSetTable,  & AttributeSetTables ]()
 | |
| 		{
 | |
| 			AttributeSetTable = AttributeSetTables[0].LoadSynchronous();
 | |
| 		}, 
 | |
| 		TStatId(), &Prerequisites, ENamedThreads::GameThread
 | |
| 	);
 | |
| 	FTaskGraphInterface::Get().WaitUntilTaskCompletes(LoadTableTask);
 | |
| 	
 | |
| 	TMap<FName, TArray<FAttributeSetField>> AttributesByCategory;
 | |
| 	{
 | |
| 		TMap< FName, uint8* > const& RowMap = AttributeSetTable->GetRowMap();
 | |
| 		for (const TPair<FName, uint8*>& Row : RowMap)
 | |
|         {
 | |
|             FAttributeSetField const* RowData = rcast( FAttributeSetField const* , Row.Value);
 | |
|             
 | |
|             // If category is empty, use a default category name
 | |
|             FName CategoryName = RowData->Category.IsNone() ? FName(TEXT("Default")) : RowData->Category;
 | |
|             
 | |
|             TArray<FAttributeSetField>& CategoryAttributes = AttributesByCategory.FindOrAdd(CategoryName);
 | |
|             CategoryAttributes.Add( * RowData);
 | |
|         }
 | |
| 	}
 | |
| 	
 | |
| 	FString AssetName = AttributeSetTables[0].GetAssetName();
 | |
| 	check( AssetName.StartsWith(TEXT("DT_") ))
 | |
| 	AssetName = AssetName.RightChop(3);
 | |
| 
 | |
| 	String str_AssetName = to_string(AssetName);
 | |
| 
 | |
| 	String class_name        = String::fmt_buf(GlobalAllocator, "U%S",          str_AssetName);
 | |
| 	String header_file_name  = String::fmt_buf(GlobalAllocator, "%S.h",         str_AssetName);
 | |
| 	String inlines_file_name = String::fmt_buf(GlobalAllocator, "%S_Inlines.h", str_AssetName);
 | |
| 	String path_header_file  = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S.h",         str_AssetName);
 | |
| 	String path_inlines_file = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S_Inlines.h", str_AssetName);
 | |
| 	String path_source_file  = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S.cpp",       str_AssetName);
 | |
| 	String uht_include_file  = String::fmt_buf(GlobalAllocator, "%S.generated.h", str_AssetName);
 | |
| 	
 | |
| 	CodeType    type_UAttributeSet = def_type( txt("UAttributeSet") );
 | |
| 	CodeComment generation_notice  = def_comment(txt("Generated by GasaEditor/GasaGen/GasaGen_AttributeSets.cpp"));
 | |
| 	
 | |
| 	CodeAttributes api_attribute = def_attributes( UModule_GASA_API->Name);
 | |
| 	
 | |
| 	Builder header = builder_open( path_header_file );
 | |
| 	{
 | |
| 		header.print(generation_notice);
 | |
| 		header.print(pragma_once);
 | |
| 		header.print(fmt_newline);	
 | |
| 		
 | |
| 		CodeInclude Include_AttributeSet               = def_include(txt("AttributeSet.h"));
 | |
| 		CodeInclude Include_GasaAttributeSet_Generated = def_include(uht_include_file);
 | |
| 		header.print( Include_AttributeSet);
 | |
| 		header.print( Include_GasaAttributeSet_Generated);
 | |
| 		header.print( fmt_newline);	
 | |
| 		
 | |
| 		gen::CodeClass attribute_set_class = {};
 | |
| 		{
 | |
| 			CodeBody body = def_body( CodeT::Class_Body );
 | |
| 			{	
 | |
| 				body.append( UHT_GENERATED_BODY);
 | |
| 				body.append( access_public );
 | |
| 
 | |
| 				body.append( def_constructor() );
 | |
| 				body.append(fmt_newline);	
 | |
| 				
 | |
| 				// Generate UPROPERTIES for each attribute field, organized by category
 | |
| 				for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 				{
 | |
| 					String      category_name = to_string(attributes.Key);
 | |
| 					CodeComment category_cmt  = def_comment(token_fmt("category_name", (StrC)category_name, "<category_name> Attributes"));
 | |
| 					body.append(category_cmt);
 | |
| 					body.append(fmt_newline);
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						Code field_uproperty = code_fmt(
 | |
| 							"category", (StrC)to_string(attribute.Category),
 | |
| 							"property", (StrC)to_string(attribute.Name), 
 | |
| 						stringize(
 | |
| 							UPROPERTY(ReplicatedUsing = Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category = "Attributes|<category>")
 | |
| 							FGameplayAttributeData <property>;
 | |
| 						));
 | |
| 						body.append(field_uproperty);
 | |
| 					}
 | |
| 					body.append(fmt_newline);
 | |
| 					body.append(fmt_newline);
 | |
| 				}
 | |
| 
 | |
| 				// Generate OnReps for each attribute field
 | |
| 				for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 				for ( FAttributeSetField attribute : attributes.Value )
 | |
| 				{
 | |
| 					Code umeta_UFUNCTION = code_str(UFUNCTION());
 | |
| 
 | |
| 					body.append(fmt_newline);
 | |
| 					body.append(umeta_UFUNCTION);
 | |
| 					body.append(fmt_newline);
 | |
| 					body.append(code_fmt("property", (StrC)to_string(attribute.Name), stringize(
 | |
| 						void Client_OnRep_<property>(FGameplayAttributeData& Prev<property>);
 | |
| 					)));
 | |
| 				}
 | |
| 				body.append(fmt_newline);
 | |
| 				body.append(fmt_newline);
 | |
| 
 | |
| 				body.append(def_pragma(txt("region Getters")));
 | |
| 				{
 | |
| 					// Generate property getters
 | |
| 					for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						CodeFn generated_get_attribute = parse_function(token_fmt(
 | |
| 							"class_name", (StrC)class_name, 
 | |
| 							"property",   (StrC)to_string(attribute.Name),
 | |
| 						stringize(
 | |
| 							static FGameplayAttribute Get<property>Attribute()
 | |
| 							{
 | |
| 								static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
 | |
| 								return Prop;
 | |
| 							}
 | |
| 						)));
 | |
| 						body.append(generated_get_attribute);
 | |
| 					}
 | |
| 					body.append(fmt_newline);
 | |
| 
 | |
| 					// Generate value getters
 | |
| 					for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						body.append(code_fmt("property", (StrC)to_string(attribute.Name),
 | |
| 						stringize(
 | |
| 							FORCEINLINE float Get<property>() const
 | |
| 							{
 | |
| 								return <property>.GetCurrentValue();
 | |
| 							}
 | |
| 						)));
 | |
| 					}
 | |
| 					body.append(fmt_newline);
 | |
| 				}
 | |
| 				body.append(def_pragma(txt("endregion Getters")));
 | |
| 				body.append(fmt_newline);
 | |
| 
 | |
| 				body.append(def_pragma(txt("region Setters")));
 | |
| 				{
 | |
| 					// Generate value setter forwards
 | |
| 					for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						body.append(code_fmt("property", (StrC)to_string(attribute.Name),
 | |
| 						stringize(
 | |
| 							FORCEINLINE void Set<property>(float NewVal);
 | |
| 						)));
 | |
| 					}
 | |
| 					body.append(fmt_newline);
 | |
| 					body.append(fmt_newline);
 | |
| 
 | |
| 					// Generate initers
 | |
| 					for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						body.append(code_fmt("property", (StrC)to_string(attribute.Name),
 | |
| 						stringize(
 | |
| 							FORCEINLINE void Init<property>(float NewVal)
 | |
| 							{
 | |
| 								<property>.SetBaseValue(NewVal);
 | |
| 								<property>.SetCurrentValue(NewVal);
 | |
| 							}
 | |
| 						)));
 | |
| 					}
 | |
| 				}
 | |
| 				body.append(def_pragma(txt("endregion Setters")));
 | |
| 				body.append(fmt_newline);
 | |
| 
 | |
| 				body.append(def_pragma(txt("region AttributeSet")));
 | |
| 				body.append(code_str(
 | |
| 					void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
 | |
| 					void PostGameplayEffectExecute(FGameplayEffectModCallbackData const& Data) override;
 | |
| 				));
 | |
| 				body.append(def_pragma(txt("endregion AttributeSet")));
 | |
| 				body.append(fmt_newline);
 | |
| 
 | |
| 				body.append(def_pragma(txt("region UObject")));
 | |
| 				CodeFn GetLifetimeOfReplicatedProps = parse_function(code(
 | |
| 					void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&OutLifetimeProps) const override;
 | |
| 				));
 | |
| 				body.append(GetLifetimeOfReplicatedProps);
 | |
| 				body.append(def_pragma(txt("endregion UObject")));
 | |
| 				body.append(fmt_newline);
 | |
| 			}
 | |
| 	
 | |
| 			attribute_set_class = def_class( class_name, body
 | |
| 				, type_UAttributeSet, AccessSpec::Public
 | |
| 				, api_attribute
 | |
| 			);
 | |
| 			header.print(UHT_UCLASS);
 | |
| 			header.print(attribute_set_class);
 | |
| 		}
 | |
| 		
 | |
| 		header.write();
 | |
| 		format_file(path_header_file);
 | |
| 	}
 | |
| 	
 | |
| 	Builder inlines = builder_open( path_inlines_file );
 | |
| 	{
 | |
| 		inlines.print(generation_notice);
 | |
| 		inlines.print(pragma_once);
 | |
| 		inlines.print(fmt_newline);
 | |
| 		inlines.print(def_include(header_file_name));
 | |
| 		inlines.print(def_include(txt("AbilitySystemComponent.h")));
 | |
| 		inlines.print(fmt_newline);
 | |
| 
 | |
| 		CodeBody body = def_body(ECode::Global_Body);
 | |
| 		{
 | |
| 			body.append(def_pragma(txt("region Attribute Setters")));
 | |
| 			{
 | |
| 				for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
 | |
| 				for (FAttributeSetField attribute : attributes.Value)
 | |
| 				{
 | |
| 					CodeFn generated_get_attribute = parse_function(token_fmt(
 | |
| 						"class_name", (StrC)class_name,
 | |
| 						"property",   (StrC)to_string(attribute.Name),
 | |
| 					stringize(
 | |
| 						FORCEINLINE void <class_name>::Set<property>(float NewVal)
 | |
| 						{
 | |
| 							UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
 | |
| 							if (ensure(AbilityComp))
 | |
| 							{
 | |
| 								AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal);
 | |
| 							};
 | |
| 						}
 | |
| 					)));
 | |
| 					body.append(generated_get_attribute);
 | |
| 				}
 | |
| 			}
 | |
| 			body.append(def_pragma(txt("endregion Attribute Setters")));
 | |
| 		}
 | |
| 		inlines.print(body);
 | |
| 		inlines.print(fmt_newline);
 | |
| 
 | |
| 		CodeNS ns_gasa = parse_namespace(token_fmt(
 | |
| 			"class_name", (StrC)class_name,
 | |
| 		stringize(
 | |
| 			namespace Gasa
 | |
| 			{
 | |
| 				inline 
 | |
| 				<class_name> const* GetAttributeSet(UAbilitySystemComponent* ASC)
 | |
| 				{
 | |
| 					return Cast< <class_name> >(ASC->GetAttributeSet(<class_name>::StaticClass()));
 | |
| 				}
 | |
| 			}
 | |
| 		)));
 | |
| 
 | |
| 		inlines.print(ns_gasa);
 | |
| 		inlines.write();
 | |
| 		format_file(path_inlines_file);
 | |
| 	}
 | |
| 	
 | |
| 	Builder source = builder_open( path_source_file );
 | |
| 	{
 | |
| 		source.print(generation_notice);
 | |
| 		header.print(fmt_newline);
 | |
| 		source.print(def_include(header_file_name));
 | |
| 		source.print(def_include(inlines_file_name));
 | |
| 		source.print(def_include(txt("EffectProperties.h")));
 | |
| 		source.print(fmt_newline);
 | |
| 		source.print(def_include(txt("AbilitySystemComponent.h")));
 | |
| 		source.print(def_include(txt("Net/UnrealNetwork.h")));
 | |
| 		source.print(def_include(txt("Networking/GasaNetLibrary.h")));
 | |
| 		source.print(def_include(txt("GameplayEffectExtension.h")));
 | |
| 		{
 | |
| 			CodeBody body = def_body(CodeT::Global_Body);
 | |
| 			body.append(fmt_newline);
 | |
| 
 | |
| 			CodeConstructor constructor_for_UGasaAttributeSet = parse_constructor(code(
 | |
| 				UGasaAttributeSet::UGasaAttributeSet()
 | |
| 			{}
 | |
| 			));
 | |
| 			body.append(constructor_for_UGasaAttributeSet);
 | |
| 
 | |
| 			// Generate Attribute fields implementation
 | |
| 			{
 | |
| 				body.append(fmt_newline);
 | |
| 				body.append(def_pragma(txt("region Rep Notifies")));
 | |
| 
 | |
| 				for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
 | |
| 				for (FAttributeSetField attribute : attributes.Value)
 | |
| 				{
 | |
| 					CodeFn field_impl = parse_function(token_fmt(
 | |
| 						"class_name",  (StrC)class_name,
 | |
| 						"property",    (StrC)to_string(attribute.Name),
 | |
| 						"from_notice", txt("\n// From GAMEPLAYATTRIBUTE_REPNOTIFY\n"),
 | |
| 					stringize(
 | |
| 						void <class_name>::Client_OnRep_<property>(FGameplayAttributeData & Prev<property>)
 | |
| 						{
 | |
| 							<from_notice>
 | |
| 							static FProperty* <class_name>Property = FindFieldChecked<FProperty>(StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
 | |
| 							GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(<class_name>Property), <property>, Prev<property>);
 | |
| 						}
 | |
| 					)));
 | |
| 					body.append(field_impl);
 | |
| 				}
 | |
| 				body.append(def_pragma(txt("endregion Rep Notifies")));
 | |
| 				body.append(fmt_newline);
 | |
| 			}
 | |
| 
 | |
| 			CodeFn PostGameplayEffectExecute;
 | |
| 			CodeFn PreAttributeChange;
 | |
| 			{
 | |
| 				CodeBody pre_attribute_clamps = def_body(CodeT::Function_Body);
 | |
| 				CodeBody post_attribute_clamps = def_body(CodeT::Function_Body);
 | |
| 
 | |
| 				// Generate field clamping ops for the pre & post functions
 | |
| 				{
 | |
| 					pre_attribute_clamps.append(fmt_newline);
 | |
| 					pre_attribute_clamps.append(fmt_newline);
 | |
| 					post_attribute_clamps.append(fmt_newline);
 | |
| 					post_attribute_clamps.append(fmt_newline);
 | |
| 
 | |
| 					for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
 | |
| 					for (FAttributeSetField attribute : attributes.Value)
 | |
| 					{
 | |
| 						String clamp_min;
 | |
| 						if (attribute.bUseMinAttribute)
 | |
| 							clamp_min = get_cached_string(token_fmt("MinName", (StrC)to_string(attribute.MinAttribute), "Get<MinName>()"));
 | |
| 						else
 | |
| 							clamp_min = String::fmt_buf(GlobalAllocator, "%f", attribute.MinValue);
 | |
| 
 | |
| 						String clamp_max;
 | |
| 						if (attribute.bUseMaxAttribute)
 | |
| 							clamp_max = get_cached_string(token_fmt("MaxName", (StrC)to_string(attribute.MaxAttribute), "Get<MaxName>()"));
 | |
| 						else
 | |
| 							clamp_max = String::fmt_buf(GlobalAllocator, "%f", attribute.MaxValue);
 | |
| 
 | |
| 						pre_attribute_clamps.append(code_fmt(
 | |
| 							"field",     (StrC)to_string(attribute.Name),
 | |
| 							"clamp_min", (StrC)clamp_min,
 | |
| 							"clamp_max", (StrC)clamp_max,
 | |
| 						stringize(
 | |
| 							if (Attribute == Get<field>Attribute())
 | |
| 							{
 | |
| 								NewValue = FMath::Clamp(NewValue, <clamp_min>, <clamp_max>);
 | |
| 							}
 | |
| 						)));
 | |
| 
 | |
| 						post_attribute_clamps.append(code_fmt(
 | |
| 							"field",     (StrC)to_string(attribute.Name),
 | |
| 							"clamp_min", (StrC)clamp_min,
 | |
| 							"clamp_max", (StrC)clamp_max,
 | |
| 						stringize(
 | |
| 							if (Data.EvaluatedData.Attribute == Get<field>Attribute())
 | |
| 							{
 | |
| 								Set<field>(FMath::Clamp(Get<field>(), <clamp_min>, <clamp_max>));
 | |
| 							}
 | |
| 						)));
 | |
| 					}
 | |
| 
 | |
| 					pre_attribute_clamps.append(fmt_newline);
 | |
| 					pre_attribute_clamps.append(fmt_newline);
 | |
| 					post_attribute_clamps.append(fmt_newline);
 | |
| 					post_attribute_clamps.append(fmt_newline);
 | |
| 				}
 | |
| 
 | |
| 				PreAttributeChange = parse_function(token_fmt(
 | |
| 					"class_name",       (StrC)class_name,
 | |
| 					"attribute_clamps", (StrC)pre_attribute_clamps.to_string(), 
 | |
| 				stringize(
 | |
| 					void <class_name>::PreAttributeChange(FGameplayAttribute const& Attribute, float& NewValue)
 | |
| 					{
 | |
| 						Super::PreAttributeChange(Attribute, NewValue);
 | |
| 
 | |
| 						<attribute_clamps>
 | |
| 					}
 | |
| 				)));
 | |
| 
 | |
| 				PostGameplayEffectExecute = parse_function(token_fmt(
 | |
| 					"class_name",       (StrC)class_name,
 | |
| 					"attribute_clamps", (StrC)post_attribute_clamps.to_string(),
 | |
| 				stringize(
 | |
| 					void <class_name>::PostGameplayEffectExecute(FGameplayEffectModCallbackData const& Data)
 | |
| 					{
 | |
| 						Super::PostGameplayEffectExecute(Data);
 | |
| 						FEffectProperties Props;
 | |
| 						Props.Populate(Data);
 | |
| 
 | |
| 						<attribute_clamps>
 | |
| 					}
 | |
| 				)));
 | |
| 
 | |
| 				body.append(PostGameplayEffectExecute);
 | |
| 				body.append(fmt_newline);
 | |
| 
 | |
| 				body.append(PreAttributeChange);
 | |
| 				body.append(fmt_newline);
 | |
| 			}
 | |
| 
 | |
| 			CodeFn GetLifetimeOfReplicatedProps;
 | |
| 			{
 | |
| 				CodeBody field_lifetimes = def_body(CodeT::Function_Body);
 | |
| 				field_lifetimes.append(fmt_newline);
 | |
| 				field_lifetimes.append(fmt_newline);
 | |
| 				for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
 | |
| 				for (FAttributeSetField attribute : attributes.Value)
 | |
| 				{
 | |
| 					field_lifetimes.append(code_fmt(
 | |
| 						"class_name", (StrC)class_name,
 | |
| 						"property",   (StrC)to_string(attribute.Name), 
 | |
| 					stringize(
 | |
| 						DOREPLIFETIME_DEFAULT_GAS( <class_name>, <property> );
 | |
| 					)));
 | |
| 				}
 | |
| 
 | |
| 				GetLifetimeOfReplicatedProps = parse_function(token_fmt(
 | |
| 					"class_name",         (StrC)class_name,
 | |
| 					"property_lifetimes", (StrC)(field_lifetimes.to_string()), 
 | |
| 				stringize(
 | |
| 					void <class_name>::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
 | |
| 					{
 | |
| 						Super::GetLifetimeReplicatedProps(OutLifetimeProps);
 | |
| 						<property_lifetimes>
 | |
| 					}
 | |
| 				)));
 | |
| 
 | |
| 				body.append(GetLifetimeOfReplicatedProps);
 | |
| 			}
 | |
| 			source.print(body);
 | |
| 		}
 | |
| 		source.write();
 | |
| 		format_file(path_source_file);
 | |
| 	}
 | |
| }
 | |
| PRAGMA_ENABLE_OPTIMIZATION
 | |
| 
 | |
| #pragma pop_macro("UPROPERTY")
 | |
| #pragma pop_macro("UFUNCTION")
 | |
| #pragma pop_macro("FORCEINLINE")
 | |
| #pragma pop_macro("ensure")
 | |
| 
 |