diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.dll b/Project/Binaries/Win64/UnrealEditor-Gasa.dll index e0d6a05..6135399 100644 Binary files a/Project/Binaries/Win64/UnrealEditor-Gasa.dll and b/Project/Binaries/Win64/UnrealEditor-Gasa.dll differ diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.pdb b/Project/Binaries/Win64/UnrealEditor-Gasa.pdb index fb25b60..43a0b64 100644 Binary files a/Project/Binaries/Win64/UnrealEditor-Gasa.pdb and b/Project/Binaries/Win64/UnrealEditor-Gasa.pdb differ diff --git a/Project/Source/Gasa/Characters/GasaCharacter.cpp b/Project/Source/Gasa/Characters/GasaCharacter.cpp index b604efb..b97078d 100644 --- a/Project/Source/Gasa/Characters/GasaCharacter.cpp +++ b/Project/Source/Gasa/Characters/GasaCharacter.cpp @@ -1,6 +1,6 @@ #include "GasaCharacter.h" -#include "GasaLevelScriptActor.h" +#include "Game/GasaLevelScriptActor.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "GameFramework/CharacterMovementComponent.h" diff --git a/Project/Source/Gasa/GasaGameInstance.cpp b/Project/Source/Gasa/Game/GasaGameInstance.cpp similarity index 100% rename from Project/Source/Gasa/GasaGameInstance.cpp rename to Project/Source/Gasa/Game/GasaGameInstance.cpp diff --git a/Project/Source/Gasa/GasaGameInstance.h b/Project/Source/Gasa/Game/GasaGameInstance.h similarity index 53% rename from Project/Source/Gasa/GasaGameInstance.h rename to Project/Source/Gasa/Game/GasaGameInstance.h index e189314..4c50a68 100644 --- a/Project/Source/Gasa/GasaGameInstance.h +++ b/Project/Source/Gasa/Game/GasaGameInstance.h @@ -21,9 +21,15 @@ public: namespace Gasa { - FORCEINLINE - UGasaGameInstance* GetGameInstance(UObject* Context) { - // TODO(Ed): Do this with proper checks - return Context->GetWorld()->GetGameInstance(); + inline + UGasaGameInstance* GetGameInstance(UObject* Context) + { + UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); + if (World == nullptr) + { + Log("World is null... are you running in a proper context?", ELogV::Error); + return nullptr; + } + return Cast(World->GetGameInstance()); } -} \ No newline at end of file +} diff --git a/Project/Source/Gasa/GasaGameMode.cpp b/Project/Source/Gasa/Game/GasaGameMode.cpp similarity index 100% rename from Project/Source/Gasa/GasaGameMode.cpp rename to Project/Source/Gasa/Game/GasaGameMode.cpp diff --git a/Project/Source/Gasa/Game/GasaGameMode.h b/Project/Source/Gasa/Game/GasaGameMode.h new file mode 100644 index 0000000..cd0ba2e --- /dev/null +++ b/Project/Source/Gasa/Game/GasaGameMode.h @@ -0,0 +1,28 @@ +#pragma once +#include "GameFramework/GameMode.h" + +#include "GasaCommon.h" + +#include "GasaGameMode.generated.h" + +UCLASS(Blueprintable) +class GASA_API AGasaGameMode : public AGameMode +{ + GENERATED_BODY() +public: +}; + +namespace Gasa +{ + inline + AGasaGameMode* GetGameMode(UObject* Context) + { + UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); + if (World == nullptr) + { + Log("World is null... are you running in a proper context?", ELogV::Error); + return nullptr; + } + return Cast(World->GetAuthGameMode()); + } +} diff --git a/Project/Source/Gasa/GasaGameState.cpp b/Project/Source/Gasa/Game/GasaGameState.cpp similarity index 100% rename from Project/Source/Gasa/GasaGameState.cpp rename to Project/Source/Gasa/Game/GasaGameState.cpp diff --git a/Project/Source/Gasa/GasaGameState.h b/Project/Source/Gasa/Game/GasaGameState.h similarity index 64% rename from Project/Source/Gasa/GasaGameState.h rename to Project/Source/Gasa/Game/GasaGameState.h index ddd9691..b8a9e37 100644 --- a/Project/Source/Gasa/GasaGameState.h +++ b/Project/Source/Gasa/Game/GasaGameState.h @@ -1,6 +1,7 @@ #pragma once #include "GameFramework/GameState.h" + #include "GasaCommon.h" #include "GasaGameState.generated.h" @@ -31,8 +32,15 @@ public: namespace Gasa { - FORCEINLINE - AGasaGameState* GetGameState(UObject* Context) { - return Context->GetWorld()->GetGameState(); + inline + AGasaGameState* GetGameState(UObject* Context) + { + UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); + if (World == nullptr) + { + Log("World is null... are you running in a proper context?", ELogV::Error); + return nullptr; + } + return Cast(World->GetGameState()); } } diff --git a/Project/Source/Gasa/GasaGameplayTags.cpp b/Project/Source/Gasa/Game/GasaGameplayTags.cpp similarity index 100% rename from Project/Source/Gasa/GasaGameplayTags.cpp rename to Project/Source/Gasa/Game/GasaGameplayTags.cpp diff --git a/Project/Source/Gasa/GasaGameplayTags.h b/Project/Source/Gasa/Game/GasaGameplayTags.h similarity index 100% rename from Project/Source/Gasa/GasaGameplayTags.h rename to Project/Source/Gasa/Game/GasaGameplayTags.h diff --git a/Project/Source/Gasa/GasaLevelScriptActor.cpp b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp similarity index 70% rename from Project/Source/Gasa/GasaLevelScriptActor.cpp rename to Project/Source/Gasa/Game/GasaLevelScriptActor.cpp index 8a08cc9..ec21511 100644 --- a/Project/Source/Gasa/GasaLevelScriptActor.cpp +++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp @@ -1,22 +1,21 @@ #include "GasaLevelScriptActor.h" #include "GasaDevOptions.h" -#include "GasaGameplayTags.h" #include "Kismet/GameplayStatics.h" -#include "GasaLibrary.h" - void AGasaLevelScriptActor::BeginPlay() { Super::BeginPlay(); + using namespace Gasa; + TArray TaggedActors; - UGameplayStatics::GetAllActorsWithTag(GetWorld(), Gasa::GetDevOptions()->Tag_GlobalPPV,TaggedActors); + UGameplayStatics::GetAllActorsWithTag(GetWorld(), GetDevOptions()->Tag_GlobalPPV, TaggedActors); for (AActor* Actor : TaggedActors) { GlobalPPV = Cast(Actor); - APostProcessVolume* PPV = Gasa::GetLevelActor(this)->GlobalPPV; + APostProcessVolume* PPV = GetLevelActor(this)->GlobalPPV; UMaterialInstance* Blendable = Cast(PPV->Settings.WeightedBlendables.Array[0].Object); UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(Blendable, this); PPV->Settings.WeightedBlendables.Array[0].Object = MID; diff --git a/Project/Source/Gasa/GasaLevelScriptActor.h b/Project/Source/Gasa/Game/GasaLevelScriptActor.h similarity index 70% rename from Project/Source/Gasa/GasaLevelScriptActor.h rename to Project/Source/Gasa/Game/GasaLevelScriptActor.h index fcd023b..f5447d4 100644 --- a/Project/Source/Gasa/GasaLevelScriptActor.h +++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.h @@ -2,6 +2,8 @@ #include "Engine/LevelScriptActor.h" +#include "GasaCommon.h" + #include "GasaLevelScriptActor.generated.h" UCLASS(Blueprintable) @@ -21,7 +23,12 @@ namespace Gasa { inline AGasaLevelScriptActor* GetLevelActor(UObject* Context, ULevel* OwnerLevel = nullptr) { - UWorld* World = GEngine->GetWorldFromContextObjectChecked(Context); + UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); + if (World == nullptr) + { + Log("World is null... are you running in a proper context?", ELogV::Error); + return nullptr; + } return Cast(World->GetLevelScriptActor(OwnerLevel)); } } diff --git a/Project/Source/Gasa/GasaPlayerController.cpp b/Project/Source/Gasa/Game/GasaPlayerController.cpp similarity index 100% rename from Project/Source/Gasa/GasaPlayerController.cpp rename to Project/Source/Gasa/Game/GasaPlayerController.cpp diff --git a/Project/Source/Gasa/GasaPlayerController.h b/Project/Source/Gasa/Game/GasaPlayerController.h similarity index 82% rename from Project/Source/Gasa/GasaPlayerController.h rename to Project/Source/Gasa/Game/GasaPlayerController.h index b3dfd32..5628f30 100644 --- a/Project/Source/Gasa/GasaPlayerController.h +++ b/Project/Source/Gasa/Game/GasaPlayerController.h @@ -72,3 +72,18 @@ public: void Tick(float DeltaSeconds) override; #pragma endregion Actor }; + +namespace Gasa +{ + inline + AGasaPlayerController* GetPrimaryPlayerController(UObject* Context) + { + UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); + if (World == nullptr) + { + Log("World is null... are you running in a proper context?", ELogV::Error); + return nullptr; + } + return Cast(World->GetFirstPlayerController()); + } +} diff --git a/Project/Source/Gasa/GasaCommon.cpp b/Project/Source/Gasa/GasaCommon.cpp new file mode 100644 index 0000000..eca18c0 --- /dev/null +++ b/Project/Source/Gasa/GasaCommon.cpp @@ -0,0 +1,3 @@ +#include "GasaCommon.h" + +DEFINE_LOG_CATEGORY(LogGasa); \ No newline at end of file diff --git a/Project/Source/Gasa/GasaCommon.h b/Project/Source/Gasa/GasaCommon.h index c08a2e4..b29025b 100644 --- a/Project/Source/Gasa/GasaCommon.h +++ b/Project/Source/Gasa/GasaCommon.h @@ -26,7 +26,99 @@ class UCogWindowManager; #pragma region Forwards class ACameraMount; class AGasaCharacter; +class AGasaGameInstance; +class AGasaGameState; class AGasaLevelScriptActor; +class AGasaPlayerController; class UGasaDevOptions; #pragma endregion Forwards + +// Straight from the Engine +UENUM(BlueprintType) +enum class EGasaVerbosity : uint8 +{ + /** Not used */ + NoLogging = 0, + + /** Always prints a fatal error to console (and log file) and crashes (even if logging is disabled) */ + // Fatal, + // Just use GASA_Fatal... + + /** + * Prints an error to console (and log file). + * Commandlets and the editor collect and report errors. Error messages result in commandlet failure. + */ + Error = ELogVerbosity::Error, + + /** + * Prints a warning to console (and log file). + * Commandlets and the editor collect and report warnings. Warnings can be treated as an error. + */ + Warning, + + /** Prints a message to console (and log file) */ + Display, + + /** Prints a message to a log file (does not print to console) */ + Log, + + /** + * Prints a verbose message to a log file (if Verbose logging is enabled for the given category, + * usually used for detailed logging) + */ + Verbose, + + /** + * Prints a verbose message to a log file (if VeryVerbose logging is enabled, + * usually used for detailed logging that would otherwise spam output) + */ + VeryVerbose, +}; + +DECLARE_LOG_CATEGORY_EXTERN(LogGasa, Log, All); + +namespace Gasa +{ + using ELogV = EGasaVerbosity; + + //◞ ‸ ◟// + // Works for Unreal 5.4, Win64 MSVC (untested in other scenarios, for now) + inline + void Log( FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log + , FLogCategoryBase& Category = LogGasa + , bool DumpStack = false + , int32 Line = __builtin_LINE() + , const ANSICHAR* File = __builtin_FILE() + , const ANSICHAR* Func = __builtin_FUNCTION() ) + { + #if !UE_BUILD_SHIPPING || !NO_LOGGING + ELogVerbosity::Type EngineVerbosity = (ELogVerbosity::Type) Verbosity; + + static ::UE::Logging::Private::FStaticBasicLogDynamicData LOG_Dynamic; + static ::UE::Logging::Private::FStaticBasicLogRecord + LOG_Static(TEXT("%s -- %hs %hs(%d)"), File, Line, EngineVerbosity, LOG_Dynamic); + + if ((EngineVerbosity & ::ELogVerbosity::VerbosityMask) <= ::ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) + { + if ((EngineVerbosity & ::ELogVerbosity::VerbosityMask) <= Category.GetVerbosity()) + { + if ( ! Category.IsSuppressed(EngineVerbosity)) + { + if (DumpStack) + FDebug::DumpStackTraceToLog(EngineVerbosity); + ::UE::Logging::Private::BasicLog(Category, &LOG_Static, *Message, File, Func, Line); + } + } + } + #endif + } +} + +#define GASA_Fatal(Message) UE_LOG( Gasa, Fatal, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_FILE(), __func__, __builtin_LINE() ); +#define GASA_Error(Message) UE_LOG( Gasa, Error, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define GASA_Warning(Message) UE_LOG( Gasa, Warning, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define GASA_Display(Message) UE_LOG( Gasa, Display, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define GASA_Log(Message) UE_LOG( Gasa, Log, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define GASA_Verbose(Message) UE_LOG( Gasa, Verbose, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define GASA_VeryVerbose(Message) UE_LOG( Gasa, VeryVerbose, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); diff --git a/Project/Source/Gasa/GasaGameMode.h b/Project/Source/Gasa/GasaGameMode.h deleted file mode 100644 index a443e6e..0000000 --- a/Project/Source/Gasa/GasaGameMode.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "GameFramework/GameMode.h" - -#include "GasaGameMode.generated.h" - -UCLASS(Blueprintable) -class GASA_API AGasaGameMode : public AGameMode -{ - GENERATED_BODY() -public: -}; - -namespace Gasa -{ - FORCEINLINE - AGasaGameMode* GetGameMode(UObject* Context) { - return Context->GetWorld()->GetAuthGameMode(); - } -} \ No newline at end of file diff --git a/Project/Source/Gasa/GasaLibrary.cpp b/Project/Source/Gasa/GasaLibrary.cpp index f0513cb..3ac8cef 100644 --- a/Project/Source/Gasa/GasaLibrary.cpp +++ b/Project/Source/Gasa/GasaLibrary.cpp @@ -1,9 +1,69 @@ #include "GasaLibrary.h" #include "GasaDevOptions.h" -#include "GasaLevelScriptActor.h" +#include "Game/GasaLevelScriptActor.h" #include "Engine/LevelScriptActor.h" +#include "Game/GasaGameInstance.h" +#include "Game/GasaGameMode.h" +#include "Game/GasaGameState.h" +#include "Game/GasaPlayerController.h" +#include "Kismet/KismetSystemLibrary.h" -UGasaDevOptions* UGasaLib::GetDevOptions(UObject* Context) { +#pragma region Game +UGasaDevOptions* UGasaLib::GetGasaDevOptions(UObject* Context) { return Gasa::GetMutDevOptions(); } + +UGasaGameInstance* UGasaLib::GetGasaGameInstance(UObject* Context) { + return Gasa::GetGameInstance(Context); +} + +AGasaGameMode* UGasaLib::GetGasaGameMode(UObject* Context) { + return Gasa::GetGameMode(Context); +} + +AGasaGameState* UGasaLib::GetGasaGameState(UObject* Context) { + return Gasa::GetGameState(Context); +} + +AGasaLevelScriptActor* UGasaLib::GetGasaLevelActor(UObject* Context) { + return Gasa::GetLevelActor(Context); +} + +AGasaPlayerController* UGasaLib::GetPrimaryGasaPlayerController(UObject* Context) { + return Gasa::GetPrimaryPlayerController(Context); +} +#pragma endregion Game + +#pragma region Logging +void UGasaLib::Log(UObject* Context, FString Message, EGasaVerbosity Verbosity, bool bPrintToScreen) +{ + Gasa::Log(Message, Verbosity, LogGasa, false, 0, "", TCHAR_TO_ANSI( *Context->GetName() )); + if (bPrintToScreen) + { + if (GAreScreenMessagesEnabled) + { + if (GConfig && printToScreenDuration < 0) + { + GConfig->GetFloat(TEXT("Kismet"), TEXT("PrintStringDuration"), printToScreenDuration, GEngineIni); + } + GEngine->AddOnScreenDebugMessage((uint64)-1, printToScreenDuration, logRegularColor.ToFColor(true), Message); + } + else + { + UE_LOG(LogBlueprint, VeryVerbose, TEXT("Screen messages disabled (!GAreScreenMessagesEnabled). Cannot print to screen.")); + } + } +}; + +// Protected + +const FLinearColor UGasaLib::logRegularColor = FLinearColor(0.0, 0.66, 1.0); +float UGasaLib::printToScreenDuration = 10.0f; +#pragma endregion Logging + +#pragma region Timing +// Protected + +float UGasaLib::timingRate_Std = UGasaLib::_60Hz; +#pragma endregion Timing diff --git a/Project/Source/Gasa/GasaLibrary.h b/Project/Source/Gasa/GasaLibrary.h index 383c27b..204c5e6 100644 --- a/Project/Source/Gasa/GasaLibrary.h +++ b/Project/Source/Gasa/GasaLibrary.h @@ -15,9 +15,101 @@ class GASA_API UGasaLib : public UBlueprintFunctionLibrary public: #pragma region Game + UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) + static UGasaDevOptions* GetGasaDevOptions(UObject* Context); + + UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) + static UGasaGameInstance* GetGasaGameInstance(UObject* Context); UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) - static UGasaDevOptions* GetDevOptions(UObject* Context); -#pragma endregion Game -}; + static AGasaGameMode* GetGasaGameMode(UObject* Context); + + UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) + static AGasaGameState* GetGasaGameState(UObject* Context); + + UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) + static AGasaLevelScriptActor* GetGasaLevelActor(UObject* Context); + UFUNCTION(BlueprintCallable, Category="Gasa|Game", BlueprintPure, meta=(WorldContext="Context")) + static AGasaPlayerController* GetPrimaryGasaPlayerController(UObject* Context); +#pragma endregion Game + +#pragma region Logging + UFUNCTION(BlueprintCallable, Category="Gasa|Logging", meta=(WorldContext="Context", DisplayName="Log Gasa", AdvancedDisplay="bPrintToScreen", DevelopmentOnly)) + static void Log( UObject* Context, FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log, bool bPrintOnScreen = false); + + static const FLinearColor logRegularColor; + static float printToScreenDuration; +#pragma endregion Logging + +#pragma region Timing +public: + + static constexpr float _24Hz = .042f; + static constexpr float _30Hz = .033f; + static constexpr float _42Hz = .024f; + static constexpr float _45Hz = .022f; + static constexpr float _50Hz = .020f; + static constexpr float _60Hz = .016f; + static constexpr float _72Hz = .014f; + static constexpr float _80Hz = .013f; + static constexpr float _90Hz = .011f; + static constexpr float _100Hz = .010f; + static constexpr float _120Hz = .083f; + static constexpr float _240Hz = .004f; + static constexpr float _480Hz = .002f; + + // Constant Rates + + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "24 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_24Hz () { return _24Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "30 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_30Hz () { return _30Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "42 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_42Hz () { return _42Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "50 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_50Hz () { return _50Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "60 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_60Hz () { return _60Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "72 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_72Hz () { return _72Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "80 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_80Hz () { return _80Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "90 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_90Hz () { return _90Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "100 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_100Hz() { return _100Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "120 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_120Hz() { return _120Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "240 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BP_Clock_240Hz() { return _240Hz; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "480 Hz", CallableWithoutWorldContext)) + static FORCEINLINE float BPClock_480Hz() { return _480Hz; } + + // Standard Timing + + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "Time Rate", CallableWithoutWorldContext)) + static FORCEINLINE float GetStd_Timerate () { return timingRate_Std; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "Quarter Time Rate", CallableWithoutWorldContext)) + static FORCEINLINE float GetStd_Timerate_Quarter() { return timingRate_Std / 4.0f; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "Half Time Rate", CallableWithoutWorldContext)) + static FORCEINLINE float GetStd_Timerate_Half () { return timingRate_Std / 2.0f; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "2X Time Rate", CallableWithoutWorldContext)) + static FORCEINLINE float GetStd_Timerate_2X () { return timingRate_Std * 2.0f; } + UFUNCTION(BlueprintPure, Category = "Timing", Meta = (DisplayName = "4X Time Rate", CallableWithoutWorldContext)) + static FORCEINLINE float GetStd_Timerate_4X () { return timingRate_Std * 2.0f; } + + UFUNCTION(BlueprintCallable, Category = "Timing", Meta = (DisplayName = "Set Time Rate")) + static void SetStd_Timerate(float _rateDesired) + { + // Not the best check.. if inconsistency arises, use the a rigorous one. + if (_rateDesired >= _24Hz) + { + timingRate_Std = _rateDesired; + } + } + +protected: + static float timingRate_Std; +#pragma endregion Timing +};