Skip to content

Commit 9837114

Browse files
committed
Built for Unreal Engine 5.7.4. Added experimental version.
1 parent 3da543d commit 9837114

23 files changed

Lines changed: 1193 additions & 0 deletions
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"FileVersion": 3,
3+
"Version": 1,
4+
"VersionName": "1.0",
5+
"FriendlyName": "Component Dynamic Attributes",
6+
"Description": "Unreal Engine 5 Actor component for dynamic attribute management in single-player games.Numeric attributes only — nothing extra!",
7+
"Category": "Experimental",
8+
"CreatedBy": "Pavel Gornostaev",
9+
"CreatedByURL": "https://github.com/Pavreally",
10+
"DocsURL": "https://github.com/Pavreally/ComponentDynamicAttributes",
11+
"MarketplaceURL": "",
12+
"SupportURL": "",
13+
"CanContainContent": true,
14+
"IsBetaVersion": true,
15+
"IsExperimentalVersion": true,
16+
"Installed": true,
17+
"Modules": [
18+
{
19+
"Name": "ComponentDynamicAttributes",
20+
"Type": "Runtime",
21+
"LoadingPhase": "Default",
22+
"PlatformAllowList": [
23+
"Win64",
24+
"Android"
25+
]
26+
}
27+
]
28+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[FilterPlugin]
2+
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
3+
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
4+
;
5+
; Examples:
6+
; /README.txt
7+
; /Extras/...
8+
; /Binaries/ThirdParty/*.dll
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[/Script/GameplayTags.GameplayTagsSettings]
2+
ImportTagsFromConfig=True
3+
WarnOnInvalidTags=True
4+
ClearInvalidTags=False
5+
AllowEditorTagUnloading=True
6+
AllowGameTagUnloading=True
7+
FastReplication=True
8+
9+
[/Script/GameplayTags.GameplayTagsList]
10+
GameplayTagList=(Tag="CDA.Attribute.Health",DevComment="")
11+
GameplayTagList=(Tag="CDA.Modifier.Buff",DevComment="")
12+
GameplayTagList=(Tag="CDA.Modifier.Debuff",DevComment="")
Binary file not shown.
14.1 KB
Loading
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright Epic Games, Inc. All Rights Reserved.
2+
3+
using UnrealBuildTool;
4+
5+
public class ComponentDynamicAttributes : ModuleRules
6+
{
7+
public ComponentDynamicAttributes(ReadOnlyTargetRules Target) : base(Target)
8+
{
9+
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
10+
11+
PublicIncludePaths.AddRange(
12+
new string[] {
13+
// ... add public include paths required here ...
14+
}
15+
);
16+
17+
18+
PrivateIncludePaths.AddRange(
19+
new string[] {
20+
// ... add other private include paths required here ...
21+
}
22+
);
23+
24+
25+
PublicDependencyModuleNames.AddRange(
26+
new string[]
27+
{
28+
"Core",
29+
"CoreUObject",
30+
"Engine",
31+
"GameplayTags",
32+
// ... add other public dependencies that you statically link with here ...
33+
}
34+
);
35+
36+
37+
PrivateDependencyModuleNames.AddRange(
38+
new string[]
39+
{
40+
"Slate",
41+
"SlateCore",
42+
// ... add private dependencies that you statically link with here ...
43+
}
44+
);
45+
46+
47+
DynamicallyLoadedModuleNames.AddRange(
48+
new string[]
49+
{
50+
// ... add any modules that your module loads dynamically here ...
51+
}
52+
);
53+
}
54+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright Epic Games, Inc. All Rights Reserved.
2+
3+
#include "ComponentDynamicAttributes.h"
4+
#include "GameplayTagsManager.h"
5+
#include "Misc/Paths.h"
6+
7+
#define LOCTEXT_NAMESPACE "FComponentDynamicAttributesModule"
8+
9+
// Define the log category
10+
DEFINE_LOG_CATEGORY(LogComponentDynamicAttributes);
11+
12+
void FComponentDynamicAttributesModule::StartupModule()
13+
{
14+
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
15+
16+
// Adding a path to plugin tags - fixed to use correct plugin directory
17+
UGameplayTagsManager::Get().AddTagIniSearchPath(
18+
FPaths::ProjectPluginsDir() / TEXT("ComponentDynamicAttributes/Config/Tags"));
19+
}
20+
21+
void FComponentDynamicAttributesModule::ShutdownModule()
22+
{
23+
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
24+
// we call this function before unloading the module.
25+
}
26+
27+
#undef LOCTEXT_NAMESPACE
28+
29+
IMPLEMENT_MODULE(FComponentDynamicAttributesModule, ComponentDynamicAttributes)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright Epic Games, Inc. All Rights Reserved.
2+
3+
#include "Components/ActorCDA.h"
4+
#include "ComponentDynamicAttributes.h"
5+
6+
UActorCDA::UActorCDA()
7+
{
8+
PrimaryComponentTick.bCanEverTick = false;
9+
bWantsInitializeComponent = true;
10+
}
11+
12+
void UActorCDA::InitializeComponent()
13+
{
14+
Super::InitializeComponent();
15+
16+
// Initialize attributes from data assets at the beginning of the game.
17+
InitializeAttributes();
18+
}
19+
20+
void UActorCDA::BeginPlay()
21+
{
22+
Super::BeginPlay();
23+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright Epic Games, Inc. All Rights Reserved.
2+
3+
#include "Components/ActorCDA.h"
4+
#include "ComponentDynamicAttributes.h"
5+
6+
void UActorCDA::InitializeAttributes()
7+
{
8+
AttributeIndex.Reset();
9+
Attributes.Reset();
10+
AttributeDefinitions.Reset();
11+
12+
if (AttributesData.IsEmpty())
13+
{
14+
return;
15+
}
16+
17+
int32 TotalDefinitions = 0;
18+
for (const TObjectPtr<UAttributesDataAssetCDA>& DataAsset : AttributesData)
19+
{
20+
if (IsValid(DataAsset))
21+
{
22+
TotalDefinitions += DataAsset->Attributes.Num();
23+
}
24+
}
25+
26+
Attributes.Reserve(TotalDefinitions);
27+
AttributeDefinitions.Reserve(TotalDefinitions);
28+
29+
for (const TObjectPtr<UAttributesDataAssetCDA>& DataAsset : AttributesData)
30+
{
31+
if (!IsValid(DataAsset))
32+
{
33+
UE_LOG(LogComponentDynamicAttributes, Warning, TEXT("ActorCDA::InitializeAttributes - Invalid AttributesDataAsset."));
34+
continue;
35+
}
36+
37+
if (DataAsset->Attributes.IsEmpty())
38+
{
39+
continue;
40+
}
41+
42+
for (const FAttributeDefinitionCDA& Definition : DataAsset->Attributes)
43+
{
44+
AddAttributeDefinition(Definition);
45+
}
46+
}
47+
}
48+
49+
void UActorCDA::AddAttributeDefinition(const FAttributeDefinitionCDA& Definition)
50+
{
51+
if (!Definition.AttributeTag.IsValid())
52+
{
53+
UE_LOG(LogComponentDynamicAttributes, Warning, TEXT("ActorCDA::AddAttributeDefinition - Invalid GameplayTag."));
54+
return;
55+
}
56+
57+
if (AttributeIndex.Contains(Definition.AttributeTag))
58+
{
59+
UE_LOG(LogComponentDynamicAttributes, Warning, TEXT("ActorCDA::AddAttributeDefinition - Duplicate GameplayTag found: %s. Skipping."), *Definition.AttributeTag.ToString());
60+
return;
61+
}
62+
63+
const int32 NewIndex = Attributes.AddDefaulted();
64+
AttributeIndex.Add(Definition.AttributeTag, NewIndex);
65+
AttributeDefinitions.Add(Definition);
66+
67+
FAttributeRuntimeDataCDA& RuntimeData = Attributes[NewIndex];
68+
RuntimeData.BaseValue = Definition.BaseValue;
69+
RuntimeData.MinValue = Definition.MinValue;
70+
RuntimeData.MaxValue = Definition.MaxValue;
71+
RuntimeData.CachedMaxValue = FMath::Clamp(Definition.BaseValue, Definition.MinValue, Definition.MaxValue);
72+
RuntimeData.CurrentValue = RuntimeData.CachedMaxValue;
73+
RuntimeData.Modifiers.Reset();
74+
RuntimeData.bNeedsRecalculation = false;
75+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright Epic Games, Inc. All Rights Reserved.
2+
3+
#include "Components/ActorCDA.h"
4+
#include "ComponentDynamicAttributes.h"
5+
6+
bool UActorCDA::TryGetAttributeIndex(FGameplayTag Tag, int32& OutIndex) const
7+
{
8+
if (!Tag.IsValid())
9+
{
10+
UE_LOG(LogComponentDynamicAttributes, VeryVerbose, TEXT("ActorCDA::TryGetAttributeIndex - Invalid GameplayTag: %s"), *Tag.ToString());
11+
return false;
12+
}
13+
14+
const int32* FoundIndex = AttributeIndex.Find(Tag);
15+
if (!FoundIndex)
16+
{
17+
UE_LOG(LogComponentDynamicAttributes, VeryVerbose, TEXT("ActorCDA::TryGetAttributeIndex - GameplayTag not found: %s"), *Tag.ToString());
18+
return false;
19+
}
20+
21+
if (!Attributes.IsValidIndex(*FoundIndex))
22+
{
23+
UE_LOG(LogComponentDynamicAttributes, VeryVerbose, TEXT("ActorCDA::TryGetAttributeIndex - Invalid index for tag: %s"), *Tag.ToString());
24+
return false;
25+
}
26+
27+
OutIndex = *FoundIndex;
28+
return true;
29+
}
30+
31+
const FAttributeDefinitionCDA* UActorCDA::GetAttributeDefinition(FGameplayTag Tag) const
32+
{
33+
const int32* FoundIndex = AttributeIndex.Find(Tag);
34+
if (!FoundIndex)
35+
{
36+
return nullptr;
37+
}
38+
39+
if (!AttributeDefinitions.IsValidIndex(*FoundIndex))
40+
{
41+
return nullptr;
42+
}
43+
44+
return &AttributeDefinitions[*FoundIndex];
45+
}
46+
47+
void UActorCDA::RecalculateAttributeIfNeeded(FGameplayTag Tag, int32 Index)
48+
{
49+
if (!Attributes.IsValidIndex(Index))
50+
{
51+
return;
52+
}
53+
54+
FAttributeRuntimeDataCDA& Data = Attributes[Index];
55+
if (!Data.bNeedsRecalculation)
56+
{
57+
return;
58+
}
59+
60+
const float OldValue = Data.CurrentValue;
61+
62+
float AddSum = 0.0f;
63+
float MultiplyProduct = 1.0f;
64+
bool bHasOverride = false;
65+
float OverrideValue = 0.0f;
66+
67+
for (const FAttributeModifierCDA& Modifier : Data.Modifiers)
68+
{
69+
switch (Modifier.Type)
70+
{
71+
case EAttributeModifierTypeCDA::Add:
72+
AddSum += Modifier.Value;
73+
break;
74+
case EAttributeModifierTypeCDA::Multiply:
75+
MultiplyProduct *= Modifier.Value;
76+
break;
77+
case EAttributeModifierTypeCDA::Override:
78+
bHasOverride = true;
79+
OverrideValue = Modifier.Value;
80+
break;
81+
default:
82+
break;
83+
}
84+
}
85+
86+
float FinalValue = (Data.BaseValue + AddSum) * MultiplyProduct;
87+
if (bHasOverride)
88+
{
89+
FinalValue = OverrideValue;
90+
}
91+
92+
const float NewMax = FMath::Clamp(FinalValue, Data.MinValue, Data.MaxValue);
93+
Data.CachedMaxValue = NewMax;
94+
Data.CurrentValue = FMath::Clamp(Data.CurrentValue, Data.MinValue, Data.CachedMaxValue);
95+
Data.bNeedsRecalculation = false;
96+
97+
BroadcastIfValueChanged(Tag, OldValue, Data.CurrentValue);
98+
}
99+
100+
void UActorCDA::ApplyOperation(float& TargetValue, EAttributeOperationCDA Operation, float Value) const
101+
{
102+
switch (Operation)
103+
{
104+
case EAttributeOperationCDA::Add:
105+
TargetValue += Value;
106+
break;
107+
case EAttributeOperationCDA::Subtract:
108+
TargetValue -= Value;
109+
break;
110+
case EAttributeOperationCDA::Multiply:
111+
TargetValue *= Value;
112+
break;
113+
case EAttributeOperationCDA::Divide:
114+
if (FMath::IsNearlyZero(Value))
115+
{
116+
UE_LOG(LogComponentDynamicAttributes, Warning, TEXT("ActorCDA::ApplyOperation - Divide by zero."));
117+
return;
118+
}
119+
TargetValue /= Value;
120+
break;
121+
case EAttributeOperationCDA::Set:
122+
TargetValue = Value;
123+
break;
124+
default:
125+
break;
126+
}
127+
}
128+
129+
void UActorCDA::BroadcastIfValueChanged(FGameplayTag Tag, float OldValue, float NewValue)
130+
{
131+
if (!FMath::IsNearlyEqual(OldValue, NewValue))
132+
{
133+
OnAttributeChangedCDA.Broadcast(Tag, OldValue, NewValue);
134+
}
135+
}

0 commit comments

Comments
 (0)