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")
|
|
|