32. Overlay Widget Controller

This commit is contained in:
Edward R. Gonzalez 2024-04-21 18:56:57 -04:00
parent adfb17b7df
commit 811dc33f4a
40 changed files with 326 additions and 157 deletions

Binary file not shown.

Binary file not shown.

View File

@ -131,3 +131,6 @@ ManualIPAddress=
+CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") +CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
[CoreRedirects]
+ClassRedirects=(OldName="/Script/Gasa.UI_HostWidget",NewName="/Script/Gasa.HUDHostWidget")

View File

@ -9,6 +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
[/Script/GameplayAbilities.AbilitySystemGlobals] [/Script/GameplayAbilities.AbilitySystemGlobals]
bUseDebugTargetFromHud=true bUseDebugTargetFromHud=true

BIN
Project/Content/Core/BP_HostWidgetController.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Project/Content/UI/UI_Host.uasset (Stored with Git LFS)

Binary file not shown.

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "AbilitySystemComponent.h" #include "AbilitySystemComponent.h"
#include "AbilitySystemInterface.h"
#include "GasaCommon.h" #include "GasaCommon.h"
@ -14,16 +13,3 @@ class GASA_API UGasaAbilitySystemComp : public UAbilitySystemComponent
GENERATED_BODY() GENERATED_BODY()
public: public:
}; };
namespace Gasa
{
inline
UGasaAbilitySystemComp* GetAbilitySystem(UObject* Object)
{
if (Object->Implements<UAbilitySystemInterface>())
{
return Cast<UGasaAbilitySystemComp>( Cast<IAbilitySystemInterface>(Object)->GetAbilitySystemComponent() );
}
return nullptr;
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "GasaAbilitySystemComponent.h"
#include "AbilitySystemInterface.h"
namespace Gasa
{
inline
UGasaAbilitySystemComp* GetAbilitySystem(UObject* Object)
{
if (Object->Implements<UAbilitySystemInterface>())
{
return Cast<UGasaAbilitySystemComp>( Cast<IAbilitySystemInterface>(Object)->GetAbilitySystemComponent() );
}
return nullptr;
}
}

View File

@ -1,5 +1,6 @@
// This was generated by GasaGen/GasaGen.cpp // Generated by GasaGen/GasaGen_UGasaAttributeSet.cpp
#include "GasaAttributeSet.h" #include "GasaAttributeSet.h"
#include "GasaAttributeSet_Inlines.h"
#include "AbilitySystemComponent.h" #include "AbilitySystemComponent.h"
#include "Net/UnrealNetwork.h" #include "Net/UnrealNetwork.h"

View File

@ -1,7 +1,7 @@
// This was generated by GasaGen/GasaGen.cpp // Generated by GasaGen/GasaGen_UGasaAttributeSet.cpp
#pragma once
#include "AttributeSet.h" #include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "GasaAttributeSet.generated.h" #include "GasaAttributeSet.generated.h"
UCLASS() UCLASS()
@ -53,6 +53,7 @@ public:
static FProperty* Prop = FindFieldChecked<FProperty>( UGasaAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, MaxMana ) ); static FProperty* Prop = FindFieldChecked<FProperty>( UGasaAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, MaxMana ) );
return Prop; return Prop;
} }
FORCEINLINE float GetHealth() const { return Health.GetCurrentValue(); } FORCEINLINE float GetHealth() const { return Health.GetCurrentValue(); }
FORCEINLINE float GetMaxHealth() const { return MaxHealth.GetCurrentValue(); } FORCEINLINE float GetMaxHealth() const { return MaxHealth.GetCurrentValue(); }
FORCEINLINE float GetMana() const { return Mana.GetCurrentValue(); } FORCEINLINE float GetMana() const { return Mana.GetCurrentValue(); }
@ -61,38 +62,11 @@ public:
#pragma region Setters #pragma region Setters
FORCEINLINE void FORCEINLINE void
SetHealth( float NewVal ) SetHealth( float NewVal );
{ FORCEINLINE void SetMaxHealth( float NewVal );
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); FORCEINLINE void SetMana( float NewVal );
if ( ensure( AbilityComp ) ) FORCEINLINE void SetMaxMana( float NewVal );
{
AbilityComp->SetNumericAttributeBase( GetHealthAttribute(), NewVal );
};
}
FORCEINLINE void SetMaxHealth( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetMaxHealthAttribute(), NewVal );
};
}
FORCEINLINE void SetMana( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetManaAttribute(), NewVal );
};
}
FORCEINLINE void SetMaxMana( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetMaxManaAttribute(), NewVal );
};
}
FORCEINLINE void InitHealth( float NewVal ) FORCEINLINE void InitHealth( float NewVal )
{ {
Health.SetBaseValue( NewVal ); Health.SetBaseValue( NewVal );
@ -121,11 +95,3 @@ public:
GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const override; GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const override;
#pragma endregion UObject #pragma endregion UObject
}; };
namespace Gasa
{
inline UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>( ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ) );
}
}

View File

@ -0,0 +1,51 @@
// Generated by GasaGen/GasaGen_UGasaAttributeSet.cpp
#pragma once
#include "GasaAttributeSet.h"
#include "AbilitySystemComponent.h"
FORCEINLINE
void UGasaAttributeSet::SetHealth( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetHealthAttribute(), NewVal );
};
}
FORCEINLINE
void UGasaAttributeSet::SetMaxHealth( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetMaxHealthAttribute(), NewVal );
};
}
FORCEINLINE
void UGasaAttributeSet::SetMana( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetManaAttribute(), NewVal );
};
}
FORCEINLINE
void UGasaAttributeSet::SetMaxMana( float NewVal )
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if ( ensure( AbilityComp ) )
{
AbilityComp->SetNumericAttributeBase( GetMaxManaAttribute(), NewVal );
};
}
namespace Gasa
{
inline UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>( ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ) );
}
}

View File

@ -1,8 +1,8 @@
#include "GasaEffectActor.h" #include "GasaEffectActor.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemInterface.h" #include "AbilitySystemInterface.h"
#include "GasaAttributeSet.h" #include "GasaAttributeSet.h"
#include "GasaAttributeSet_Inlines.h"
#include "Components/SphereComponent.h" #include "Components/SphereComponent.h"

View File

@ -1,6 +1,9 @@
#include "PlayerCharacter.h" #include "PlayerCharacter.h"
#include "AbilitySystemComponent.h" #include "AbilitySystemComponent.h"
#include "Game/GasaPlayerController.h"
#include "UI/GasaHUD.h"
#include "UI/WidgetController.h"
APlayerCharacter::APlayerCharacter() APlayerCharacter::APlayerCharacter()
{ {
@ -9,6 +12,7 @@ APlayerCharacter::APlayerCharacter()
bAutoAbilitySystem = false; bAutoAbilitySystem = false;
} }
// TODO(Ed): We need to setup Net Slime...
void APlayerCharacter::PossessedBy(AController* NewController) void APlayerCharacter::PossessedBy(AController* NewController)
{ {
Super::PossessedBy(NewController); Super::PossessedBy(NewController);
@ -20,8 +24,14 @@ void APlayerCharacter::PossessedBy(AController* NewController)
Attributes = PS->Attributes; Attributes = PS->Attributes;
AbilitySystem->InitAbilityActorInfo(PS, this); AbilitySystem->InitAbilityActorInfo(PS, this);
} }
AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitOverlay(& Data);
} }
// TODO(Ed): We need to setup Net Slime...
void APlayerCharacter::OnRep_PlayerState() void APlayerCharacter::OnRep_PlayerState()
{ {
Super::OnRep_PlayerState(); Super::OnRep_PlayerState();
@ -33,4 +43,12 @@ void APlayerCharacter::OnRep_PlayerState()
Attributes = PS->Attributes; Attributes = PS->Attributes;
AbilitySystem->InitAbilityActorInfo(PS, this); AbilitySystem->InitAbilityActorInfo(PS, this);
} }
if (IsLocallyControlled())
{
AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitOverlay(& Data);
}
} }

View File

@ -1,4 +1,5 @@
#include "GasaPlayerController.h" #include "GasaPlayerController.h"
#include "GasaPlayerController_Inlines.h"
#include "AbilitySystemComponent.h" #include "AbilitySystemComponent.h"
#include "Engine/LocalPlayer.h" #include "Engine/LocalPlayer.h"
@ -12,7 +13,6 @@
#include "Components/CapsuleComponent.h" #include "Components/CapsuleComponent.h"
#include "GameFramework/SpringArmComponent.h" #include "GameFramework/SpringArmComponent.h"
#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetSystemLibrary.h"
using namespace Gasa; using namespace Gasa;
AGasaPlayerController::AGasaPlayerController() AGasaPlayerController::AGasaPlayerController()
@ -24,6 +24,8 @@ AGasaPlayerController::AGasaPlayerController()
bReplicates = true; bReplicates = true;
} }
#pragma region Input #pragma region Input
void AGasaPlayerController::Move(FInputActionValue const& ActionValue) void AGasaPlayerController::Move(FInputActionValue const& ActionValue)
{ {
@ -31,8 +33,6 @@ void AGasaPlayerController::Move(FInputActionValue const& ActionValue)
if (pawn == nullptr ) if (pawn == nullptr )
return; return;
// Note(Ed): I did the follow optimization for practice, they are completely unnecessary for this context. // Note(Ed): I did the follow optimization for practice, they are completely unnecessary for this context.
#if 0 #if 0
FVector2D AxisV = ActionValue.Get<FVector2D>(); FVector2D AxisV = ActionValue.Get<FVector2D>();
@ -77,6 +77,7 @@ void AGasaPlayerController::SpawnDefaultHUD()
Super::SpawnDefaultHUD(); Super::SpawnDefaultHUD();
} }
// TODO(Ed): We need to setup Net Slime...
void AGasaPlayerController::OnPossess(APawn* InPawn) void AGasaPlayerController::OnPossess(APawn* InPawn)
{ {
Super::OnPossess(InPawn); Super::OnPossess(InPawn);

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "GasaCommon.h" #include "GasaCommon.h"
#include "GasaPlayerState.h"
#include "GameFramework/PlayerController.h" #include "GameFramework/PlayerController.h"
#include "GasaPlayerController.generated.h" #include "GasaPlayerController.generated.h"
@ -51,10 +50,7 @@ public:
AGasaPlayerController(); AGasaPlayerController();
AGasaPlayerState* GetPlayerState() inline AGasaPlayerState* GetPlayerState();
{
return Cast<AGasaPlayerState>( PlayerState );
}
#pragma region PlayerController #pragma region PlayerController
void SpawnDefaultHUD() override; void SpawnDefaultHUD() override;

View File

@ -0,0 +1,8 @@
#include "GasaPlayerController.h"
#include "GasaPlayerState.h"
inline
AGasaPlayerState* AGasaPlayerController::GetPlayerState()
{
return Cast<AGasaPlayerState>( PlayerState );
}

View File

@ -40,6 +40,7 @@ class AGasaGameInstance;
class AGasaGameState; class AGasaGameState;
class AGasaLevelScriptActor; class AGasaLevelScriptActor;
class AGasaPlayerController; class AGasaPlayerController;
class AGasaPlayerState;
class UGasaAbilitySystemComp; class UGasaAbilitySystemComp;
class UGasaAttributeSet; class UGasaAttributeSet;
@ -48,7 +49,11 @@ class UGasaImage;
class UGasaOverlay; class UGasaOverlay;
class UGasaProgressBar; class UGasaProgressBar;
class UGasaSizeBox; class UGasaSizeBox;
class UUI_HostWidget; class UHostWidgetController;
class UHUDHostWidget;
class UWidgetController;
struct FWidgetControllerData;
#pragma endregion Forwards #pragma endregion Forwards
#pragma region Logging #pragma region Logging

View File

@ -1,7 +1,8 @@
#include "GasaDevOptions.h" #include "GasaDevOptions.h"
#include "Actors/CameraMount.h" #include "Actors/CameraMount.h"
#include "UI/UI_HostWidget.h" #include "UI/HUDHostWidget.h"
#include "UI/HostWidgetController.h"
using namespace Gasa; using namespace Gasa;

View File

@ -19,7 +19,10 @@ public:
TSoftClassPtr<ACameraMount> Template_PlayerCamera; TSoftClassPtr<ACameraMount> Template_PlayerCamera;
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="UI") UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="UI")
TSoftClassPtr<UUI_HostWidget> Template_HUD_HostUI; TSoftClassPtr<UHUDHostWidget> Template_HUD_HostUI;
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="UI")
TSoftClassPtr<UHostWidgetController> Template_HostWidgetController;
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="Tags") UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="Tags")
FName Tag_GlobalPPV; FName Tag_GlobalPPV;

View File

@ -1,9 +1,10 @@
// This was generated by GasaGen/GasaGen.cpp // Generated by GasaGen/GasaGen_DevOptionsCache.cpp
#include "GasaDevOptionsCache.h" #include "GasaDevOptionsCache.h"
#include "GasaDevOptions.h" #include "GasaDevOptions.h"
#include "Actors/CameraMount.h" #include "Actors/CameraMount.h"
#include "UI/UI_HostWidget.h" #include "UI/HUDHostWidget.h"
#include "UI/HostWidgetController.h"
using namespace Gasa; using namespace Gasa;
void FGasaDevOptionsCache::CachedDevOptions() void FGasaDevOptionsCache::CachedDevOptions()
@ -11,7 +12,13 @@ void FGasaDevOptionsCache::CachedDevOptions()
UGasaDevOptions* DevOpts = GetMutDevOptions(); UGasaDevOptions* DevOpts = GetMutDevOptions();
Template_PlayerCamera = DevOpts->Template_PlayerCamera.LoadSynchronous(); Template_PlayerCamera = DevOpts->Template_PlayerCamera.LoadSynchronous();
Template_HUD_HostUI = DevOpts->Template_HUD_HostUI.LoadSynchronous(); ensureMsgf( Template_PlayerCamera != nullptr, TEXT( "Template_PlayerCamera is null, DO NOT RUN PIE or else you may get a crash if not handled in BP or C++" ) );
Template_HUD_HostUI = DevOpts->Template_HUD_HostUI.LoadSynchronous();
ensureMsgf( Template_HUD_HostUI != nullptr, TEXT( "Template_HUD_HostUI is null, DO NOT RUN PIE or else you may get a crash if not handled in BP or C++" ) );
Template_HostWidgetController = DevOpts->Template_HostWidgetController.LoadSynchronous();
ensureMsgf( Template_HostWidgetController != nullptr, TEXT( "Template_HostWidgetController is null, DO NOT RUN PIE or else you may get a crash if not handled in BP or C++" ) );
Tag_GlobalPPV = DevOpts->Tag_GlobalPPV; Tag_GlobalPPV = DevOpts->Tag_GlobalPPV;
} }

View File

@ -1,4 +1,4 @@
// This was generated by GasaGen/GasaGen.cpp // Generated by GasaGen/GasaGen_DevOptionsCache.cpp
#pragma once #pragma once
#include "GasaDevOptionsCache.generated.h" #include "GasaDevOptionsCache.generated.h"
@ -12,6 +12,8 @@ struct GASA_API FGasaDevOptionsCache
UClass* Template_PlayerCamera; UClass* Template_PlayerCamera;
UPROPERTY() UPROPERTY()
UClass* Template_HUD_HostUI; UClass* Template_HUD_HostUI;
UPROPERTY()
UClass* Template_HostWidgetController;
void CachedDevOptions(); void CachedDevOptions();
}; };

View File

@ -1,11 +1,21 @@
#include "GasaHUD.h" #include "GasaHUD.h"
#include "GasaHUD_Inlines.h"
#include "GasaDevOptions.h" #include "GasaDevOptions.h"
#include "UI_HostWidget.h" #include "HUDHostWidget.h"
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
using namespace Gasa; using namespace Gasa;
void AGasaHUD::InitOverlay(FWidgetControllerData const* WidgetControllerData)
{
HostWidget = CreateWidget<UHUDHostWidget>( GetWorld()
, GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() );
HostWidgetController = NewObject<UHostWidgetController>(this, GetDevOptions()->Template_HostWidgetController.Get());
HostWidget->SetWidgetController(HostWidgetController);
HostWidget->AddToViewport();
}
#pragma region HUD #pragma region HUD
void AGasaHUD::ShowHUD() void AGasaHUD::ShowHUD()
@ -15,17 +25,8 @@ void AGasaHUD::ShowHUD()
#pragma endregion HUD #pragma endregion HUD
#pragma region Actor #pragma region Actor
UE_DISABLE_OPTIMIZATION
void AGasaHUD::BeginPlay() void AGasaHUD::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
HostWidget = CreateWidget<UUI_HostWidget>( GetWorld()
, GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() );
HostWidget->AddToViewport();
bool bHostVis = HostWidget->IsVisible();
Log(FString::Printf(TEXT("HostVIs: %s"), *FString::FromInt(bHostVis)));
} }
UE_ENABLE_OPTIMIZATION
#pragma endregion Actor #pragma endregion Actor

View File

@ -12,7 +12,14 @@ class GASA_API AGasaHUD : public AHUD
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
TObjectPtr<UUI_HostWidget> HostWidget; TObjectPtr<UHUDHostWidget> HostWidget;
// This should only be accessed AFTER InitOverlay is called. Otherwise, it will be null
// See references to InitOverlay or docs for lifetime.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
TObjectPtr<UHostWidgetController> HostWidgetController;
void InitOverlay(FWidgetControllerData const* WidgetControllerData);
#pragma region HUD #pragma region HUD
void ShowHUD() override; void ShowHUD() override;
@ -20,6 +27,5 @@ public:
#pragma region Actor #pragma region Actor
void BeginPlay() override; void BeginPlay() override;
#pragma endregion Actor #pragma endregion Actor
}; };

View File

@ -0,0 +1,5 @@
#pragma once
#include "GasaHUD.h"
#include "GasaDevOptions.h"
#include "HostWidgetController.h"
#include "HUDHostWidget.h"

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "GasaCommon.h"
#include "GasaUserWidget.generated.h" #include "GasaUserWidget.generated.h"
UCLASS(Blueprintable) UCLASS(Blueprintable)
class GASA_API UGasaUserWidget : public UUserWidget class GASA_API UGasaUserWidget : public UUserWidget
{ {
@ -23,12 +25,15 @@ public:
TSubclassOf<UGasaUserWidget> LooseParent; TSubclassOf<UGasaUserWidget> LooseParent;
UPROPERTY(BlueprintReadOnly) UPROPERTY(BlueprintReadOnly)
TObjectPtr<UObject> WidgetController; TObjectPtr<UWidgetController> WidgetController;
UGasaUserWidget(FObjectInitializer const& ObjectInitializer); UGasaUserWidget(FObjectInitializer const& ObjectInitializer);
template<typename WidgetControllerType>
FORCEINLINE WidgetControllerType* GetWidgetController() { return Cast<WidgetControllerType>(WidgetController); }
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void SetWidgetController(UObject* Controller) void SetWidgetController(UWidgetController* Controller)
{ {
WidgetController = Controller; WidgetController = Controller;
OnWidgetControllerSet(); OnWidgetControllerSet();

View File

@ -0,0 +1 @@
#include "HUDHostWidget.h"

View File

@ -2,10 +2,10 @@
#include "GasaUserWidget.h" #include "GasaUserWidget.h"
#include "UI_HostWidget.generated.h" #include "HUDHostWidget.generated.h"
UCLASS() UCLASS()
class GASA_API UUI_HostWidget : public UGasaUserWidget class GASA_API UHUDHostWidget : public UGasaUserWidget
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:

View File

@ -0,0 +1 @@
#include "HostWidgetController.h"

View File

@ -0,0 +1,12 @@
#pragma once
#include "WidgetController.h"
#include "HostWidgetController.generated.h"
UCLASS()
class GASA_API UHostWidgetController : public UWidgetController
{
GENERATED_BODY()
public:
};

View File

@ -1 +0,0 @@
#include "UI_HostWidget.h"

View File

@ -3,17 +3,37 @@
#include "GasaCommon.h" #include "GasaCommon.h"
#include "WidgetController.generated.h" #include "WidgetController.generated.h"
UCLASS(BlueprintType) USTRUCT(BlueprintType)
class GASA_API UWidgetController : public UObject struct GASA_API FWidgetControllerData
{ {
GENERATED_BODY() GENERATED_BODY()
public:
FWidgetControllerData() = default;
FWidgetControllerData(AGasaPlayerController* Controller
, AGasaPlayerState* PlayerState
, UAbilitySystemComponent* AbilitySystem
, UAttributeSet* Attributes )
#if 1
: Controller(Controller)
, PlayerState(PlayerState)
, AbilitySystem(AbilitySystem)
, 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<APlayerController> Controller; TObjectPtr<AGasaPlayerController> Controller;
UPROPERTY(BlueprintReadOnly, Category="Player") UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<APlayerState> PlayerState; TObjectPtr<AGasaPlayerState> PlayerState;
UPROPERTY(BlueprintReadOnly, Category="Player") UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<UAbilitySystemComponent> AbilitySystem; TObjectPtr<UAbilitySystemComponent> AbilitySystem;
@ -21,3 +41,13 @@ public:
UPROPERTY(BlueprintReadOnly, Category="Player") UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<UAttributeSet> Attributes; TObjectPtr<UAttributeSet> Attributes;
}; };
UCLASS(Blueprintable)
class GASA_API UWidgetController : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category="Player")
FWidgetControllerData Data;
};

View File

@ -55,7 +55,7 @@ int gen_main()
PreprocessorDefines.append( get_cached_string(str_DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL)); PreprocessorDefines.append( get_cached_string(str_DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL));
PreprocessorDefines.append( get_cached_string(str_ENUM_CLASS_FLAGS)); PreprocessorDefines.append( get_cached_string(str_ENUM_CLASS_FLAGS));
PreprocessorDefines.append( get_cached_string(str_FORCEINLINE_DEBUGGABLE)); PreprocessorDefines.append( get_cached_string(str_FORCEINLINE_DEBUGGABLE));
PreprocessorDefines.append( get_cached_string(str_FORCEINLINE)); // PreprocessorDefines.append( get_cached_string(str_FORCEINLINE));
PreprocessorDefines.append( get_cached_string(str_GENERATED_BODY)); PreprocessorDefines.append( get_cached_string(str_GENERATED_BODY));
PreprocessorDefines.append( get_cached_string(str_GENERATED_UCLASS_BODY)); PreprocessorDefines.append( get_cached_string(str_GENERATED_UCLASS_BODY));
PreprocessorDefines.append( get_cached_string(str_GENERATED_USTRUCT_BODY)); PreprocessorDefines.append( get_cached_string(str_GENERATED_USTRUCT_BODY));

View File

@ -34,7 +34,7 @@ constexpr StrC str_DECLARE_MULTICAST_DELEGATE_TwoParams = txt("
constexpr StrC str_DEFINE_ACTORDESC_TYPE = txt("DEFINE_ACTORDESC_TYPE("); constexpr StrC str_DEFINE_ACTORDESC_TYPE = txt("DEFINE_ACTORDESC_TYPE(");
constexpr StrC str_DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL = txt("DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL("); constexpr StrC str_DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL = txt("DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(");
constexpr StrC str_ENUM_CLASS_FLAGS = txt("ENUM_CLASS_FLAGS("); constexpr StrC str_ENUM_CLASS_FLAGS = txt("ENUM_CLASS_FLAGS(");
constexpr StrC str_FORCEINLINE = txt("FORCEINLINE"); // constexpr StrC str_FORCEINLINE = txt("FORCEINLINE");
constexpr StrC str_FORCEINLINE_DEBUGGABLE = txt("FORCEINLINE_DEBUGGABLE"); constexpr StrC str_FORCEINLINE_DEBUGGABLE = txt("FORCEINLINE_DEBUGGABLE");
constexpr StrC str_GENERATED_BODY = txt("GENERATED_BODY("); constexpr StrC str_GENERATED_BODY = txt("GENERATED_BODY(");
constexpr StrC str_GENERATED_UCLASS_BODY = txt("GENERATED_UCLASS_BODY("); constexpr StrC str_GENERATED_UCLASS_BODY = txt("GENERATED_UCLASS_BODY(");

View File

@ -34,7 +34,7 @@ void gen_FGasaDevOptionsCache()
} }
} }
CodeComment generation_notice = def_comment(txt("This was generated by GasaGen/GasaGen.cpp")); CodeComment generation_notice = def_comment(txt("Generated by GasaGen/GasaGen_DevOptionsCache.cpp"));
Builder header = Builder::open( path_module_gasa "GasaDevOptionsCache.h" ); Builder header = Builder::open( path_module_gasa "GasaDevOptionsCache.h" );
{ {
@ -107,13 +107,17 @@ void gen_FGasaDevOptionsCache()
cached_property_assignments.append(fmt_newline); cached_property_assignments.append(fmt_newline);
for (CodeVar var : GasaDevOptions_UPROPERTIES) for (CodeVar var : GasaDevOptions_UPROPERTIES)
{ {
#pragma push_macro("TEXT")
#undef TEXT
Code assignment = code_fmt( "property", (StrC)var->Name, stringize( Code assignment = code_fmt( "property", (StrC)var->Name, stringize(
<property> = DevOpts-> <property>.LoadSynchronous(); <property> = DevOpts-> <property>.LoadSynchronous();
ensureMsgf(<property> != nullptr, TEXT("<property> is null, DO NOT RUN PIE or else you may get a crash if not handled in BP or C++"));
)); ));
#pragma pop_macro("TEXT")
cached_property_assignments.append(assignment); cached_property_assignments.append(assignment);
cached_property_assignments.append(fmt_newline);
cached_property_assignments.append(fmt_newline);
} }
cached_property_assignments.append(fmt_newline);
cached_property_assignments.append(fmt_newline);
} }
CodeFn CachedDevOptions = parse_function( token_fmt( CodeFn CachedDevOptions = parse_function( token_fmt(

View File

@ -7,18 +7,19 @@
#include "GasaGenCommon.cpp" #include "GasaGenCommon.cpp"
#endif #endif
void def_attribute_properties ( CodeBody body, Array<StringCached> properties ); void def_attribute_properties ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_on_reps ( CodeBody body, Array<StringCached> properties ); void def_attribute_field_on_reps ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties ); void def_attribute_field_property_getters ( CodeBody body, StrC class_name, Array<StringCached> properties );
void def_attribute_field_value_getters ( CodeBody body, Array<StringCached> properties ); void def_attribute_field_value_getters ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_value_setters ( CodeBody body, Array<StringCached> properties ); void def_attribute_field_value_setters ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties ); void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name, Array<StringCached> properties );
void impl_attribute_fields ( CodeBody body, StrC class_name, 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 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("This was generated by GasaGen/GasaGen.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 = Array<StringCached>::init( GlobalAllocator);
attribute_fields.append( get_cached_string(txt("Health"))); attribute_fields.append( get_cached_string(txt("Health")));
@ -31,10 +32,10 @@ void gen_UGasaAttributeSet()
Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h"); Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h");
{ {
header.print(generation_notice); header.print(generation_notice);
header.print(pragma_once);
header.print(fmt_newline); header.print(fmt_newline);
{ {
CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h")); CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h"));
CodeInclude Include_AbilitySystemComponent = def_include(txt("AbilitySystemComponent.h"));
CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h")); CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h"));
CodeAttributes api_attribute= def_attributes( UModule_GASA_API->Name); CodeAttributes api_attribute= def_attributes( UModule_GASA_API->Name);
@ -59,12 +60,15 @@ void gen_UGasaAttributeSet()
body.append( fmt_newline ); body.append( fmt_newline );
body.append( def_pragma(code( region Getters ))); body.append( def_pragma(code( region Getters )));
def_attribute_field_property_getters( body, class_name, attribute_fields ); def_attribute_field_property_getters( body, class_name, attribute_fields );
body.append( fmt_newline );
def_attribute_field_value_getters( body, attribute_fields ); def_attribute_field_value_getters( body, attribute_fields );
body.append( def_pragma(code( endregion Getters ))); body.append( def_pragma(code( endregion Getters )));
body.append( fmt_newline ); body.append( fmt_newline );
body.append( def_pragma(code( region Setters ))); body.append( def_pragma(code( region Setters )));
def_attribute_field_value_setters( body, attribute_fields ); def_attribute_field_value_setters( body, attribute_fields );
body.append( fmt_newline );
body.append( fmt_newline );
def_attribute_field_initers( body, attribute_fields ); def_attribute_field_initers( body, attribute_fields );
body.append( def_pragma(code( endregion Setters ))); body.append( def_pragma(code( endregion Setters )));
body.append( fmt_newline ); body.append( fmt_newline );
@ -82,34 +86,54 @@ void gen_UGasaAttributeSet()
); );
} }
CodeNS ns_gasa = parse_namespace( code(
namespace Gasa
{
inline
UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ));
}
}
));
header.print( Include_AttributeSet); header.print( Include_AttributeSet);
header.print( Include_AbilitySystemComponent);
header.print( Include_GasaAttributeSet_Generated); header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline); header.print( fmt_newline);
header.print( UHT_UCLASS ); header.print( UHT_UCLASS );
header.print(GasaAttributeSet); header.print(GasaAttributeSet);
header.print(ns_gasa);
} }
header.write(); header.write();
format_file(path_gasa_ability_system "GasaAttributeSet.h"); format_file(path_gasa_ability_system "GasaAttributeSet.h");
} }
Builder inlines = Builder::open( path_gasa_ability_system "GasaAttributeSet_Inlines.h");
{
inlines.print(generation_notice);
inlines.print(pragma_once);
inlines.print(fmt_newline);
inlines.print( def_include( txt("GasaAttributeSet.h")));
inlines.print( def_include(txt("AbilitySystemComponent.h")));
inlines.print(fmt_newline);
CodeBody body = def_body(CodeT::Global_Body);
{
def_attribute_field_property_setter_inlines( body, class_name, attribute_fields );
}
inlines.print(body);
inlines.print(fmt_newline);
CodeNS ns_gasa = parse_namespace( code(
namespace Gasa
{
inline
UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ));
}
}
));
inlines.print(ns_gasa);
inlines.write();
format_file(path_gasa_ability_system "GasaAttributeSet_Inlines.h");
}
Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" ); Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" );
{ {
source.print(generation_notice); source.print(generation_notice);
header.print(fmt_newline); header.print(fmt_newline);
source.print( def_include( txt("GasaAttributeSet.h"))); source.print( def_include( txt("GasaAttributeSet.h")));
source.print( def_include( txt("GasaAttributeSet_Inlines.h")));
source.print(fmt_newline); source.print(fmt_newline);
source.print( def_include( txt("AbilitySystemComponent.h"))); source.print( def_include( txt("AbilitySystemComponent.h")));
source.print( def_include( txt("Net/UnrealNetwork.h"))); source.print( def_include( txt("Net/UnrealNetwork.h")));
@ -231,18 +255,31 @@ void def_attribute_field_value_setters( CodeBody body, Array<StringCached> prope
{ {
body.append( code_fmt( "property", (StrC)property, body.append( code_fmt( "property", (StrC)property,
stringize( stringize(
FORCEINLINE void Set<property>(float NewVal) FORCEINLINE void Set<property>(float NewVal);
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if (ensure(AbilityComp))
{
AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal);
};
}
))); )));
} }
} }
void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name, Array<StringCached> properties )
{
for ( String property : properties )
{
CodeFn generated_get_attribute = parse_function(
token_fmt( "class_name", class_name, "property", (StrC)property,
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 );
}
}
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties ) void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties )
{ {
for ( String property : properties ) for ( String property : properties )

View File

@ -81,7 +81,7 @@ global CodeSpecifiers spec_constexpr;
global CodeSpecifiers spec_constinit; global CodeSpecifiers spec_constinit;
global CodeSpecifiers spec_extern_linkage; global CodeSpecifiers spec_extern_linkage;
global CodeSpecifiers spec_final; global CodeSpecifiers spec_final;
global CodeSpecifiers spec_forceinline; global CodeSpecifiers spec_FORCEINLINE;
global CodeSpecifiers spec_global; global CodeSpecifiers spec_global;
global CodeSpecifiers spec_inline; global CodeSpecifiers spec_inline;
global CodeSpecifiers spec_internal_linkage; global CodeSpecifiers spec_internal_linkage;
@ -2988,12 +2988,12 @@ internal void define_constants()
spec_##Type_ = def_specifiers( num_args( __VA_ARGS__ ), __VA_ARGS__ ); \ spec_##Type_ = def_specifiers( num_args( __VA_ARGS__ ), __VA_ARGS__ ); \
spec_##Type_.set_global(); spec_##Type_.set_global();
#pragma push_macro( "forceinline" ) #pragma push_macro( "FORCEINLINE" )
#pragma push_macro( "global" ) #pragma push_macro( "global" )
#pragma push_macro( "internal" ) #pragma push_macro( "internal" )
#pragma push_macro( "local_persist" ) #pragma push_macro( "local_persist" )
#pragma push_macro( "neverinline" ) #pragma push_macro( "neverinline" )
#undef forceinline #undef FORCEINLINE
#undef global #undef global
#undef internal #undef internal
#undef local_persist #undef local_persist
@ -3004,7 +3004,7 @@ internal void define_constants()
def_constant_spec( constinit, ESpecifier::Constinit ); def_constant_spec( constinit, ESpecifier::Constinit );
def_constant_spec( extern_linkage, ESpecifier::External_Linkage ); def_constant_spec( extern_linkage, ESpecifier::External_Linkage );
def_constant_spec( final, ESpecifier::Final ); def_constant_spec( final, ESpecifier::Final );
def_constant_spec( forceinline, ESpecifier::ForceInline ); def_constant_spec( FORCEINLINE, ESpecifier::ForceInline );
def_constant_spec( global, ESpecifier::Global ); def_constant_spec( global, ESpecifier::Global );
def_constant_spec( inline, ESpecifier::Inline ); def_constant_spec( inline, ESpecifier::Inline );
def_constant_spec( internal_linkage, ESpecifier::Internal_Linkage ); def_constant_spec( internal_linkage, ESpecifier::Internal_Linkage );
@ -3025,7 +3025,7 @@ internal void define_constants()
spec_local_persist = def_specifiers( 1, ESpecifier::Local_Persist ); spec_local_persist = def_specifiers( 1, ESpecifier::Local_Persist );
spec_local_persist.set_global(); spec_local_persist.set_global();
#pragma pop_macro( "forceinline" ) #pragma pop_macro( "FORCEINLINE" )
#pragma pop_macro( "global" ) #pragma pop_macro( "global" )
#pragma pop_macro( "internal" ) #pragma pop_macro( "internal" )
#pragma pop_macro( "local_persist" ) #pragma pop_macro( "local_persist" )
@ -5840,7 +5840,7 @@ namespace parser
{ sizeof( "explicit" ), "explicit" }, { sizeof( "explicit" ), "explicit" },
{ sizeof( "extern" ), "extern" }, { sizeof( "extern" ), "extern" },
{ sizeof( "final" ), "final" }, { sizeof( "final" ), "final" },
{ sizeof( "forceinline" ), "forceinline" }, { sizeof( "FORCEINLINE" ), "FORCEINLINE" },
{ sizeof( "global" ), "global" }, { sizeof( "global" ), "global" },
{ sizeof( "inline" ), "inline" }, { sizeof( "inline" ), "inline" },
{ sizeof( "internal" ), "internal" }, { sizeof( "internal" ), "internal" },
@ -6059,7 +6059,7 @@ namespace parser
} }
}; };
global Arena_128KB defines_map_arena; global Arena_256KB defines_map_arena;
global HashTable<StrC> defines; global HashTable<StrC> defines;
global Array<Token> Tokens; global Array<Token> Tokens;
@ -6110,7 +6110,7 @@ namespace parser
Lex_ReturnNull, Lex_ReturnNull,
}; };
forceinline s32 lex_preprocessor_directive( StrC& content, s32& left, char const*& scanner, s32& line, s32& column, HashTable<StrC>& defines, Token& token ) FORCEINLINE s32 lex_preprocessor_directive( StrC& content, s32& left, char const*& scanner, s32& line, s32& column, HashTable<StrC>& defines, Token& token )
{ {
char const* hash = scanner; char const* hash = scanner;
Tokens.append( { hash, 1, TokType::Preprocess_Hash, line, column, TF_Preprocess } ); Tokens.append( { hash, 1, TokType::Preprocess_Hash, line, column, TF_Preprocess } );
@ -6344,7 +6344,7 @@ namespace parser
return Lex_Continue; // Skip found token, its all handled here. return Lex_Continue; // Skip found token, its all handled here.
} }
forceinline void lex_found_token( StrC& content, s32& left, char const*& scanner, s32& line, s32& column, HashTable<StrC>& defines, Token& token ) FORCEINLINE void lex_found_token( StrC& content, s32& left, char const*& scanner, s32& line, s32& column, HashTable<StrC>& defines, Token& token )
{ {
if ( token.Type != TokType::Invalid ) if ( token.Type != TokType::Invalid )
{ {
@ -7255,7 +7255,7 @@ namespace parser
{ {
Tokens = Array<Token>::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / sizeof( Token ) ); Tokens = Array<Token>::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / sizeof( Token ) );
defines_map_arena = Arena_128KB::init(); defines_map_arena = Arena_256KB::init();
defines = HashTable<StrC>::init( defines_map_arena ); defines = HashTable<StrC>::init( defines_map_arena );
} }

View File

@ -145,21 +145,21 @@ GEN_NS_BEGIN
#define local_persist static // Local Persisting variables #define local_persist static // Local Persisting variables
#ifdef GEN_COMPILER_MSVC #ifdef GEN_COMPILER_MSVC
#define forceinline __forceinline #define FORCEINLINE __forceinline
#define neverinline __declspec( noinline ) #define neverinline __declspec( noinline )
#elif defined( GEN_COMPILER_GCC ) #elif defined( GEN_COMPILER_GCC )
#define forceinline inline __attribute__( ( __always_inline__ ) ) #define FORCEINLINE inline __attribute__( ( __always_inline__ ) )
#define neverinline __attribute__( ( __noinline__ ) ) #define neverinline __attribute__( ( __noinline__ ) )
#elif defined( GEN_COMPILER_CLANG ) #elif defined( GEN_COMPILER_CLANG )
#if __has_attribute( __always_inline__ ) #if __has_attribute( __always_inline__ )
#define forceinline inline __attribute__( ( __always_inline__ ) ) #define FORCEINLINE inline __attribute__( ( __always_inline__ ) )
#define neverinline __attribute__( ( __noinline__ ) ) #define neverinline __attribute__( ( __noinline__ ) )
#else #else
#define forceinline #define FORCEINLINE
#define neverinline #define neverinline
#endif #endif
#else #else
#define forceinline #define FORCEINLINE
#define neverinline #define neverinline
#endif #endif

View File

@ -451,7 +451,7 @@ namespace ESpecifier
{ sizeof( "constinit" ), "constinit" }, { sizeof( "constinit" ), "constinit" },
{ sizeof( "explicit" ), "explicit" }, { sizeof( "explicit" ), "explicit" },
{ sizeof( "extern" ), "extern" }, { sizeof( "extern" ), "extern" },
{ sizeof( "forceinline" ), "forceinline" }, { sizeof( "FORCEINLINE" ), "FORCEINLINE" },
{ sizeof( "global" ), "global" }, { sizeof( "global" ), "global" },
{ sizeof( "inline" ), "inline" }, { sizeof( "inline" ), "inline" },
{ sizeof( "internal" ), "internal" }, { sizeof( "internal" ), "internal" },
@ -671,7 +671,7 @@ struct Code
Using_Code( Code ); Using_Code( Code );
template<class Type> template<class Type>
forceinline Type cast() FORCEINLINE Type cast()
{ {
return *rcast( Type*, this ); return *rcast( Type*, this );
} }
@ -761,7 +761,7 @@ struct AST
neverinline void to_string( String& result ); neverinline void to_string( String& result );
template<class Type> template<class Type>
forceinline Type cast() FORCEINLINE Type cast()
{ {
return *this; return *this;
} }
@ -6397,7 +6397,7 @@ extern CodeSpecifiers spec_constexpr;
extern CodeSpecifiers spec_constinit; extern CodeSpecifiers spec_constinit;
extern CodeSpecifiers spec_extern_linkage; extern CodeSpecifiers spec_extern_linkage;
extern CodeSpecifiers spec_final; extern CodeSpecifiers spec_final;
extern CodeSpecifiers spec_forceinline; extern CodeSpecifiers spec_FORCEINLINE;
extern CodeSpecifiers spec_global; extern CodeSpecifiers spec_global;
extern CodeSpecifiers spec_inline; extern CodeSpecifiers spec_inline;
extern CodeSpecifiers spec_internal_linkage; extern CodeSpecifiers spec_internal_linkage;