34. Listening for Attribute Changes

This commit is contained in:
Edward R. Gonzalez 2024-04-22 00:30:29 -04:00
parent 7db411586e
commit a6dc269630
24 changed files with 305 additions and 44 deletions

Binary file not shown.

Binary file not shown.

View File

@ -9,7 +9,7 @@ Tag_PPV=Global_PPV
Tag_GlobalPPV=Global_PPV Tag_GlobalPPV=Global_PPV
Template_PlayerCamera=/Game/Actors/BP_CameraMount.BP_CameraMount_C Template_PlayerCamera=/Game/Actors/BP_CameraMount.BP_CameraMount_C
Template_HUD_HostUI=/Game/UI/UI_Host.UI_Host_C Template_HUD_HostUI=/Game/UI/UI_Host.UI_Host_C
Template_HostWidgetController=/Game/Core/BP_HostWidgetController.BP_HostWidgetController_C Template_HostWidgetController=/Game/UI/BP_HostWidgetController.BP_HostWidgetController_C
[/Script/GameplayAbilities.AbilitySystemGlobals] [/Script/GameplayAbilities.AbilitySystemGlobals]
bUseDebugTargetFromHud=true bUseDebugTargetFromHud=true

View File

@ -8,9 +8,9 @@
UGasaAttributeSet::UGasaAttributeSet() UGasaAttributeSet::UGasaAttributeSet()
{ {
InitHealth( 100.f ); InitHealth( 80.f );
InitMaxHealth( 100.f ); InitMaxHealth( 100.f );
InitMana( 50.f ); InitMana( 20.f );
InitMaxMana( 50.f ); InitMaxMana( 50.f );
} }
@ -33,7 +33,6 @@ void UGasaAttributeSet::Client_OnRep_MaxMana( FGameplayAttributeData& PrevMaxMan
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, MaxMana, PrevMaxMana ) GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, MaxMana, PrevMaxMana )
} }
void UGasaAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const void UGasaAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const
{ {
Super::GetLifetimeReplicatedProps( OutLifetimeProps ); Super::GetLifetimeReplicatedProps( OutLifetimeProps );

View File

@ -90,7 +90,6 @@ public:
#pragma endregion Setters #pragma endregion Setters
#pragma region UObject #pragma region UObject
void void
GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const override; GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const override;
#pragma endregion UObject #pragma endregion UObject

View File

@ -28,7 +28,7 @@ void APlayerCharacter::PossessedBy(AController* NewController)
AGasaPlayerController* PC = GetController<AGasaPlayerController>(); AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>(); AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes }; FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitOverlay(& Data); HUD->InitHostWidget(& Data);
} }
// TODO(Ed): We need to setup Net Slime... // TODO(Ed): We need to setup Net Slime...
@ -49,6 +49,6 @@ void APlayerCharacter::OnRep_PlayerState()
AGasaPlayerController* PC = GetController<AGasaPlayerController>(); AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>(); AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes }; FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitOverlay(& Data); HUD->InitHostWidget(& Data);
} }
} }

View File

@ -6,14 +6,16 @@
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
using namespace Gasa; using namespace Gasa;
void AGasaHUD::InitOverlay(FWidgetControllerData const* WidgetControllerData) void AGasaHUD::InitHostWidget(FWidgetControllerData const* WidgetControllerData)
{ {
HostWidget = CreateWidget<UHUDHostWidget>( GetWorld() HostWidget = CreateWidget<UHUDHostWidget>( GetWorld()
, GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() ); , GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() );
HostWidgetController = NewObject<UHostWidgetController>(this, GetDevOptions()->Template_HostWidgetController.Get()); HostWidgetController = NewObject<UHostWidgetController>(this, GetDevOptions()->Template_HostWidgetController.Get());
HostWidgetController->Data = (* WidgetControllerData);
HostWidget->SetWidgetController(HostWidgetController); HostWidget->SetWidgetController(HostWidgetController);
HostWidgetController->BroadcastInitialValues();
HostWidget->AddToViewport(); HostWidget->AddToViewport();
} }

View File

@ -19,7 +19,7 @@ public:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
TObjectPtr<UHostWidgetController> HostWidgetController; TObjectPtr<UHostWidgetController> HostWidgetController;
void InitOverlay(FWidgetControllerData const* WidgetControllerData); void InitHostWidget(FWidgetControllerData const* WidgetControllerData);
#pragma region HUD #pragma region HUD
void ShowHUD() override; void ShowHUD() override;

View File

@ -89,6 +89,11 @@ void UGlobeProgressBar::SetGlassStyle(FSlateBrush brush)
Glass->SetBrush(brush); Glass->SetBrush(brush);
} }
void UGlobeProgressBar::SetPercentage(float CurrentValue, float MaxValue)
{
Bar->SetPercent( MaxValue > 0.f ? CurrentValue / MaxValue : 0.f );
}
void UGlobeProgressBar::SetSize(float width, float height) void UGlobeProgressBar::SetSize(float width, float height)
{ {
Root_SB->SetWidthOverride( width ); Root_SB->SetWidthOverride( width );

View File

@ -46,6 +46,9 @@ public:
UFUNCTION(BlueprintCallable, Category="Globe") UFUNCTION(BlueprintCallable, Category="Globe")
void SetGlassStyle(FSlateBrush brush); void SetGlassStyle(FSlateBrush brush);
UFUNCTION(BlueprintCallable, Category="Globe")
void SetPercentage(float CurrentValue, float MaxValue);
UFUNCTION(BlueprintCallable, Category="Globe") UFUNCTION(BlueprintCallable, Category="Globe")
void SetSize(float width, float height); void SetSize(float width, float height);

View File

@ -1 +1,14 @@
#include "HostWidgetController.h" #include "HostWidgetController.h"
#include "AbilitySystem/GasaAttributeSet.h"
void UHostWidgetController::BroadcastInitialValues()
{
Super::BroadcastInitialValues();
UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>( Data.Attributes );
if ( GasaAttribs )
{
Event_OnHealthChanged.Broadcast( GasaAttribs->GetHealth() );
Event_OnMaxHealthChanged.Broadcast( GasaAttribs->GetMaxHealth() );
Event_OnManaChanged.Broadcast( GasaAttribs->GetMana() );
Event_OnMaxManaChanged.Broadcast( GasaAttribs->GetMaxMana() );
}
}

View File

@ -1,12 +1,31 @@
#pragma once #pragma once
#include "WidgetController.h" #include "WidgetController.h"
#include "HostWidgetController.generated.h" #include "HostWidgetController.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FAttributeFloatChangedSig, float, NewValue );
UCLASS() UCLASS( Blueprintable, BlueprintType )
class GASA_API UHostWidgetController : public UWidgetController class GASA_API UHostWidgetController : public UWidgetController
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
#pragma region Attribute Events
// Attribute Events are generated by GasaGen/GasaGen_HostWidgetController.cpp
UPROPERTY( BlueprintAssignable, Category = "Attributes" )
FAttributeFloatChangedSig Event_OnHealthChanged;
UPROPERTY( BlueprintAssignable, Category = "Attributes" )
FAttributeFloatChangedSig Event_OnMaxHealthChanged;
UPROPERTY( BlueprintAssignable, Category = "Attributes" )
FAttributeFloatChangedSig Event_OnManaChanged;
UPROPERTY( BlueprintAssignable, Category = "Attributes" )
FAttributeFloatChangedSig Event_OnMaxManaChanged;
#pragma endregion Attribute Events
#pragma region WidgetController
void BroadcastInitialValues() override;
#pragma endregion WidgetController
}; };

View File

@ -7,27 +7,19 @@ USTRUCT(BlueprintType)
struct GASA_API FWidgetControllerData struct GASA_API FWidgetControllerData
{ {
GENERATED_BODY() GENERATED_BODY()
public:
FWidgetControllerData() = default; FWidgetControllerData() = default;
FWidgetControllerData(AGasaPlayerController* Controller FWidgetControllerData
, AGasaPlayerState* PlayerState ( AGasaPlayerController* Controller
, UAbilitySystemComponent* AbilitySystem , AGasaPlayerState* PlayerState
, UAttributeSet* Attributes ) , UAbilitySystemComponent* AbilitySystem
#if 1 , UAttributeSet* Attributes )
: Controller(Controller) : Controller(Controller)
, PlayerState(PlayerState) , PlayerState(PlayerState)
, AbilitySystem(AbilitySystem) , AbilitySystem(AbilitySystem)
, Attributes(Attributes) , Attributes(Attributes)
#endif {}
{
#if 0
this->Controller = Controller;
this->PlayerState = PlayerState;
this->AbilitySystem = AbilitySystem;
this->Attributes = Attributes;
#endif
}
UPROPERTY(BlueprintReadOnly, Category="Player") UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<AGasaPlayerController> Controller; TObjectPtr<AGasaPlayerController> Controller;
@ -50,4 +42,7 @@ public:
UPROPERTY(BlueprintReadOnly, Category="Player") UPROPERTY(BlueprintReadOnly, Category="Player")
FWidgetControllerData Data; FWidgetControllerData Data;
UFUNCTION()
virtual void BroadcastInitialValues() {};
}; };

View File

@ -17,6 +17,197 @@ using namespace gen;
#include "GasaGen_UGasaAttributeSet.cpp" #include "GasaGen_UGasaAttributeSet.cpp"
#include "GasaGen_ChangeBPActionMenu.cpp" #include "GasaGen_ChangeBPActionMenu.cpp"
#include "GasaGen_DevOptionsCache.cpp" #include "GasaGen_DevOptionsCache.cpp"
#include "GasaGen_HostWidgetController.cpp"
void gen_UHostWidgetController()
{
Array<StringCached> attribute_fields = get_gasa_attribute_fields();
CodeBody ori_HostWidgetController_header = parse_file(path_gasa_ui "HostWidgetController.h");
{
CodeBody header_body = def_body(ECode::Global_Body);
StrC str_UHostWidgetController = txt("UHostWidgetController");
CodeClass ori_UHostWidgetController = NoCode;
Code file_code = ori_HostWidgetController_header.begin();
for ( ; file_code != ori_HostWidgetController_header.end(); ++ file_code )
{
if (s32 never_enter = 0; never_enter)
found: break;
switch (file_code->Type)
{
default:
header_body.append(file_code);
continue;
case ECode::Class:
if ( ! file_code->Name.starts_with(str_UHostWidgetController))
continue;
ori_UHostWidgetController = file_code.cast<CodeClass>();
++ file_code;
goto found;
case ECode::Preprocess_Include:
header_body.append(file_code);
if ( file_code->Content.starts_with(txt("HostWidgetController.generated.h")))
{
header_body.append(fmt_newline);
header_body.append(fmt_newline);
}
continue;
case ECode::Untyped:
header_body.append(file_code);
if (file_code->Content.starts_with( txt("DECLARE_"))
|| file_code->Content.starts_with( txt("UCLASS"))
)
header_body.append(fmt_newline);
continue;
}
}
CodeBody attribute_events = def_body(ECode::Class_Body);
{
attribute_events.append( def_comment( txt("Attribute Events are generated by GasaGen/GasaGen_HostWidgetController.cpp")));
attribute_events.append(fmt_newline);
for ( s32 id = 0; id < attribute_fields.num(); )
{
StringCached attribute_field = attribute_fields[id];
attribute_events.append( code_str(
UPROPERTY(BlueprintAssignable, Category = "Attributes")
));
attribute_events.append(fmt_newline);
attribute_events.append( parse_variable(
token_fmt( "field", (StrC) attribute_field, stringize( FAttributeFloatChangedSig Event_On<field>Changed; ))
));
++ id;
if ( id < attribute_fields.num() )
{
attribute_events.append(fmt_newline);
}
}
}
CodeClass new_UHostWidgetController = ori_UHostWidgetController.duplicate().cast<CodeClass>();
CodeBody new_body = def_body(ECode::Class_Body);
for (Code code = ori_UHostWidgetController->Body.begin();
code != ori_UHostWidgetController->Body.end();
++ code )
{
switch (code->Type)
{
default:
new_body.append(code);
continue;
case ECode::Preprocess_Pragma:
{
local_persist bool found = false;
if (found)
{
new_body.append(code);
continue;
}
CodePragma pragma = code.cast<CodePragma>();
if ( pragma->Content.starts_with(txt("region Attribute Events")) )
{
new_body.append(pragma);
++ code;
new_body.append(attribute_events);
while (code->Type != ECode::Preprocess_Pragma
|| ! code->Content.starts_with(txt("endregion Attribute Events")))
++ code;
new_body.append( code );
found = true;
}
}
break;
case ECode::Untyped:
new_body.append(code);
if (code->Content.starts_with( txt("GENERATED_BODY")))
new_body.append(fmt_newline);
}
}
new_body.append(fmt_newline);
new_UHostWidgetController->Body = new_body;
header_body.append(new_UHostWidgetController);
for (; file_code != ori_HostWidgetController_header.end(); ++ file_code)
{
header_body.append(file_code);
}
Builder header = Builder::open(path_gasa_ui "HostWidgetController.h");
header.print(header_body);
header.write();
format_file(path_gasa_ui "HostWidgetController.h");
}
CodeBody ori_HostWidgetController_source = parse_file(path_gasa_ui "HostWidgetController.cpp");
{
CodeBody source_body = def_body(ECode::Global_Body);
CodeBody broadcast_calls = def_body(ECode::Function_Body);
for (StringCached field : attribute_fields)
{
broadcast_calls.append( code_fmt( "field", (StrC)field,
stringize( Event_On<field>Changed.Broadcast( GasaAttribs->Get<field>() ); )
));
}
CodeFn BroadcastInitialValues = parse_function( token_fmt( "broadcast_calls", (StrC)broadcast_calls.to_string(),
stringize(
void UHostWidgetController::BroadcastInitialValues()
{
Super::BroadcastInitialValues();
// Thiis function is managed by: GasaGen/GasaGen_HostWidgetController.cpp
UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>(Data.Attributes);
if (GasaAttribs)
{
<broadcast_calls>
}
})
));
for ( Code code : ori_HostWidgetController_source)
{
switch (code->Type)
{
case ECode::Function:
CodeFn function_def = code.cast<CodeFn>();
if ( String::are_equal(function_def->Name, BroadcastInitialValues->Name)
&& function_def->Params.is_equal(BroadcastInitialValues->Params))
{
code = BroadcastInitialValues;
log_fmt("Swapped: %S", BroadcastInitialValues->Name);
}
break;
}
source_body.append(code);
}
Builder source = Builder::open(path_gasa_ui "HostWidgetController.cpp");
source.print(source_body);
source.write();
format_file(path_gasa_ui "HostWidgetController.cpp");
}
}
int gen_main() int gen_main()
{ {
@ -38,6 +229,7 @@ int gen_main()
PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_RetVal_OneParam)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_RetVal_OneParam));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_RetVal_ThreeParams)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_RetVal_ThreeParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_SixParams)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_SixParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams)); PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams));
@ -77,6 +269,7 @@ int gen_main()
gen_UGasaAttributeSet(); gen_UGasaAttributeSet();
gen_FGasaDevOptionsCache(); gen_FGasaDevOptionsCache();
gen_UHostWidgetController();
// One offs // One offs
if (0) if (0)

View File

@ -13,11 +13,13 @@ using namespace gen;
#define path_config path_source "Config/" #define path_config path_source "Config/"
#define path_module_gasa path_source "Gasa/" #define path_module_gasa path_source "Gasa/"
#define path_gasa_ability_system path_module_gasa "AbilitySystem/" #define path_gasa_ability_system path_module_gasa "AbilitySystem/"
#define path_gasa_ui path_module_gasa "UI/"
constexpr StrC str_DECLARE_CLASS = txt("DECLARE_CLASS("); constexpr StrC str_DECLARE_CLASS = txt("DECLARE_CLASS(");
constexpr StrC str_DECLARE_DELEGATE_RetVal_OneParam = txt("DECLARE_DELEGATE_RetVal_OneParam("); constexpr StrC str_DECLARE_DELEGATE_RetVal_OneParam = txt("DECLARE_DELEGATE_RetVal_OneParam(");
constexpr StrC str_DECLARE_DELEGATE_RetVal_ThreeParams = txt("DECLARE_DELEGATE_RetVal_ThreeParams("); constexpr StrC str_DECLARE_DELEGATE_RetVal_ThreeParams = txt("DECLARE_DELEGATE_RetVal_ThreeParams(");
constexpr StrC str_DECLARE_DELEGATE_SixParams = txt("DECLARE_DELEGATE_SixParams("); constexpr StrC str_DECLARE_DELEGATE_SixParams = txt("DECLARE_DELEGATE_SixParams(");
constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam = txt("DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(");
constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams("); constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams(");
constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams("); constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams(");
constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams("); constexpr StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams(");

View File

@ -16,16 +16,26 @@ void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties ); void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties );
void impl_attribute_fields ( CodeBody body, StrC class_name, Array<StringCached> properties ); void impl_attribute_fields ( CodeBody body, StrC class_name, Array<StringCached> properties );
Array<StringCached> get_gasa_attribute_fields()
{
local_persist
Array<StringCached> attribute_fields = Array<StringCached>::init_reserve(GlobalAllocator, 64);
for (local_persist s32 do_once = 0; do_once == 0; ++ do_once) {
attribute_fields.append( get_cached_string(txt("Health")));
attribute_fields.append( get_cached_string(txt("MaxHealth")));
attribute_fields.append( get_cached_string(txt("Mana")));
attribute_fields.append( get_cached_string(txt("MaxMana")));
}
return attribute_fields;
}
void gen_UGasaAttributeSet() void gen_UGasaAttributeSet()
{ {
CodeType type_UAttributeSet = def_type( txt("UAttributeSet") ); CodeType type_UAttributeSet = def_type( txt("UAttributeSet") );
CodeComment generation_notice = def_comment(txt("Generated by GasaGen/GasaGen_UGasaAttributeSet.cpp")); CodeComment generation_notice = def_comment(txt("Generated by GasaGen/GasaGen_UGasaAttributeSet.cpp"));
Array<StringCached> attribute_fields = Array<StringCached>::init( GlobalAllocator); Array<StringCached> attribute_fields = get_gasa_attribute_fields();
attribute_fields.append( get_cached_string(txt("Health")));
attribute_fields.append( get_cached_string(txt("MaxHealth")));
attribute_fields.append( get_cached_string(txt("Mana")));
attribute_fields.append( get_cached_string(txt("MaxMana")));
StrC class_name = txt("UGasaAttributeSet"); StrC class_name = txt("UGasaAttributeSet");

View File

@ -1950,6 +1950,7 @@ void CodeFn::to_string_def( String& result )
if ( ast->Attributes ) if ( ast->Attributes )
result.append_fmt( " %S ", ast->Attributes.to_string() ); result.append_fmt( " %S ", ast->Attributes.to_string() );
b32 prefix_specs = false;
if ( ast->Specs ) if ( ast->Specs )
{ {
for ( SpecifierT spec : ast->Specs ) for ( SpecifierT spec : ast->Specs )
@ -1958,11 +1959,13 @@ void CodeFn::to_string_def( String& result )
{ {
StrC spec_str = ESpecifier::to_str( spec ); StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
prefix_specs = true;
} }
} }
} }
if ( ast->Attributes || ast->Specs ) if ( ast->Attributes || prefix_specs )
result.append( "\n" ); result.append( "\n" );
if ( ast->ReturnType ) if ( ast->ReturnType )
@ -2000,19 +2003,22 @@ void CodeFn::to_string_fwd( String& result )
if ( ast->Attributes ) if ( ast->Attributes )
result.append_fmt( "%S ", ast->Attributes.to_string() ); result.append_fmt( "%S ", ast->Attributes.to_string() );
bool prefix_specs = false;
if ( ast->Specs ) if ( ast->Specs )
{ {
for ( SpecifierT spec : ast->Specs ) for ( SpecifierT spec : ast->Specs )
{ {
if ( ESpecifier::is_trailing( spec ) && ! ( spec != ESpecifier::Pure ) ) if ( ! ESpecifier::is_trailing( spec ) || ! ( spec != ESpecifier::Pure ) )
{ {
StrC spec_str = ESpecifier::to_str( spec ); StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
prefix_specs = true;
} }
} }
} }
if ( ast->Attributes || ast->Specs ) if ( ast->Attributes || prefix_specs )
{ {
result.append( "\n" ); result.append( "\n" );
} }
@ -2894,17 +2900,17 @@ internal void define_constants()
access_private = make_code(); access_private = make_code();
access_private->Type = ECode::Access_Private; access_private->Type = ECode::Access_Private;
access_private->Name = get_cached_string( txt( "private:" ) ); access_private->Name = get_cached_string( txt( "private:\n" ) );
access_private.set_global(); access_private.set_global();
access_protected = make_code(); access_protected = make_code();
access_protected->Type = ECode::Access_Protected; access_protected->Type = ECode::Access_Protected;
access_protected->Name = get_cached_string( txt( "protected:" ) ); access_protected->Name = get_cached_string( txt( "protected:\n" ) );
access_protected.set_global(); access_protected.set_global();
access_public = make_code(); access_public = make_code();
access_public->Type = ECode::Access_Public; access_public->Type = ECode::Access_Public;
access_public->Name = get_cached_string( txt( "public:" ) ); access_public->Name = get_cached_string( txt( "public:\n" ) );
access_public.set_global(); access_public.set_global();
attrib_api_export = def_attributes( code( GEN_API_Export_Code ) ); attrib_api_export = def_attributes( code( GEN_API_Export_Code ) );

View File

@ -5146,6 +5146,9 @@ Code CodeParam::duplicate()
bool CodeParam::is_equal( Code other ) bool CodeParam::is_equal( Code other )
{ {
if ( ast == nullptr && other.ast == nullptr)
return true;
if ( ast == nullptr || other.ast == nullptr ) if ( ast == nullptr || other.ast == nullptr )
{ {
log_failure( "Code::is_equal: Cannot compare code, AST is null!" ); log_failure( "Code::is_equal: Cannot compare code, AST is null!" );

View File

@ -166,7 +166,16 @@ SpacesInSquareBrackets: false
Standard: c++20 Standard: c++20
StatementMacros: ['UPROPERTY', 'UFUNCTION', 'UCLASS', 'USTRUCT', 'UENUM', 'UINTERFACE', 'GENERATED_BODY'] StatementMacros: [
'UPROPERTY',
'UFUNCTION',
'UCLASS',
'USTRUCT',
'UENUM',
'UINTERFACE',
'GENERATED_BODY',
'DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam'
]
TabWidth: 4 TabWidth: 4

View File

@ -79,6 +79,7 @@ function build-gengasa
$compiler_args = @() $compiler_args = @()
$compiler_args += ($flag_define + 'GEN_TIME') $compiler_args += ($flag_define + 'GEN_TIME')
$compiler_args += ($flag_cpp_version + 'c++17')
$linker_args = @() $linker_args = @()
$linker_args += $flag_link_win_subsystem_console $linker_args += $flag_link_win_subsystem_console

View File

@ -94,6 +94,7 @@ if ( $vendor -match "clang" )
# https://clang.llvm.org/docs/ClangCommandLineReference.html # https://clang.llvm.org/docs/ClangCommandLineReference.html
$flag_all_c = '-x c' $flag_all_c = '-x c'
$flag_all_cpp = '-x c++' $flag_all_cpp = '-x c++'
$flag_cpp_version = '-std='
$flag_compile = '-c' $flag_compile = '-c'
$flag_color_diagnostics = '-fcolor-diagnostics' $flag_color_diagnostics = '-fcolor-diagnostics'
$flag_no_color_diagnostics = '-fno-color-diagnostics' $flag_no_color_diagnostics = '-fno-color-diagnostics'
@ -318,6 +319,7 @@ if ( $vendor -match "msvc" )
# https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170 # https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
$flag_all_c = '/TC' $flag_all_c = '/TC'
$flag_all_cpp = '/TP' $flag_all_cpp = '/TP'
$flag_cpp_version = '/std:'
$flag_compile = '/c' $flag_compile = '/c'
$flag_debug = '/Zi' $flag_debug = '/Zi'
$flag_define = '/D' $flag_define = '/D'