Here is a very simple container which allows you to have replicated Gameplay Tags as a Stack Count. Useful for say things like Weapon Ammo, Inventory item count, stat points, etc.
Here is the header file:
USTRUCT(BlueprintType)
struct FKaosGameplayTagStack : public FFastArraySerializerItem
{
GENERATED_BODY()
FKaosGameplayTagStack()
{}
FKaosGameplayTagStack(FGameplayTag InTag, int32 InCount)
: Tag(InTag)
{
Tag = InTag;
Count = InCount;
}
private:
friend FKaosGameplayTagStackContainer;
UPROPERTY()
FGameplayTag Tag;
UPROPERTY()
int32 Count = 0;
};
USTRUCT(BlueprintType)
struct FKaosGameplayTagStackContainer : public FFastArraySerializer
{
GENERATED_BODY()
FKaosGameplayTagStackContainer() { }
public:
void PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize);
void PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize);
void PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize);
bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms)
{
return FastArrayDeltaSerialize<FKaosGameplayTagStack, FKaosGameplayTagStackContainer>(Stacks, DeltaParms, *this);
}
/*
* Adds count to the tag's stack
*/
void AddStackCount(FGameplayTag Tag, int32 Count);
/*
* Removes count from the tag's stack
*/
void RemoveStackCount(FGameplayTag Tag, int32 Count);
/*
* Returns true if we have a one or more of the tag in the stack.
*/
bool HasTag(FGameplayTag Tag) const
{
return TagToCountMap.Contains(Tag);
}
/*
* Returns the amount of the stack we have for the supplied tag
*/
int32 GetStackCount(FGameplayTag Tag) const
{
return TagToCountMap.FindRef(Tag);
}
private:
// List of gameplay tag stacks. Use the accelerated TagCountMap for checking tag count, etc.
UPROPERTY()
TArray<FKaosGameplayTagStack> Stacks;
// Accelerated list for queries
TMap<FGameplayTag, int32> TagCountMap;
};
template<>
struct TStructOpsTypeTraits<FKaosGameplayTagStackContainer> : public TStructOpsTypeTraitsBase2<FKaosGameplayTagStackContainer>
{
enum
{
WithNetDeltaSerializer = true,
};
};
And the cpp file part:
void FKaosGameplayTagStackContainer::AddStackCount(FGameplayTag Tag, int32 Count)
{
if (Count > 0)
{
for (FKaosGameplayTagStack& Stack : Stacks)
{
if (Stack.Tag == Tag)
{
const int32 NewCount = Stack.Count + Count;
Stack.Count = NewCount;
TagCountMap[Tag] = NewCount;
MarkItemDirty(Stack);
return;
}
}
FKaosGameplayTagStack& NewStack = Stacks.Emplace_GetRef(Tag, Count);
MarkItemDirty(NewStack);
TagCountMap.Add(Tag, Count);
}
}
void FKaosGameplayTagStackContainer::RemoveStackCount(FGameplayTag Tag, int32 Count)
{
if (Count > 0)
{
for (auto It = Stacks.CreateIterator(); It; ++It)
{
FKaosGameplayTagStack& Stack = *It;
if (Stack.Tag == Tag)
{
if (Stack.Count <= Count)
{
It.RemoveCurrent();
TagCountMap.Remove(Tag);
MarkArrayDirty();
}
else
{
const int32 NewCount = Stack.Count - Count;
Stack.Count = NewCount;
TagCountMap[Tag] = NewCount;
MarkItemDirty(Stack);
}
return;
}
}
}
}
void FKaosGameplayTagStackContainer::PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize)
{
for (const int32 Index : RemovedIndices)
{
const FGameplayTag Tag = Stacks[Index].Tag;
TagCountMap.Remove(Tag);
}
}
void FKaosGameplayTagStackContainer::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
{
for (const int32 Index : AddedIndices)
{
const FKaosGameplayTagStack& Stack = Stacks[Index];
TagCountMap.Add(Stack.Tag, Stack.Count);
}
}
void FKaosGameplayTagStackContainer::PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize)
{
for (const int32 Index : ChangedIndices)
{
const FKaosGameplayTagStack& Stack = Stacks[Index];
TagCountMap[Stack.Tag] = Stack.Count;
}
}
To use it, simply add a replicated property somewhere, say for example your player
UPROPERTY(Replicated)
FKaosGameplayTagStackContainer GameplayStats;
And you can use it like so:
void AMyCharacter::AddStatTagCount(FGameplayTag Tag, int32 Count)
{
GameplayStats.AddStack(Tag, Count);
}
void AMyCharacter::RemoveStatTagCount(FGameplayTag Tag, int32 Count)
{
GameplayStats.RemoveStack(Tag, Count);
}
int32 AMyCharacter::GetStatTagStackCount(FGameplayTag Tag) const
{
return GameplayStats.GetStackCount(Tag);
}
bool AMyCharacter::HasStatTag(FGameplayTag Tag) const
{
return GameplayStats.ContainsTag(Tag);
}
Hopefully people find this helpful and useful.