Initial commit: Item Interaction Ecosystem plugin (UE5.7)

Server-authoritative, data-driven item/inventory/interaction/carry/equipment/
world-slot/placement/crafting framework. 8 modules, compiles against UE 5.7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bonchellon
2026-06-22 20:49:56 +03:00
commit 7f7e043a88
98 changed files with 9328 additions and 0 deletions

View File

@ -0,0 +1,190 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "ItemRuntimeProperties.generated.h"
/**
* Tag->value pairs (section 4.4).
*
* The TЗ sketched these as TMaps, but UHT forbids TMap inside replicated structs,
* and runtime properties must replicate (they live in FItemInstanceData). So they
* are stored as small replicable arrays instead; the typed accessors below hide
* that and behave like a map. N is tiny per item, so linear lookup is fine.
*/
USTRUCT(BlueprintType)
struct FItemFloatProperty
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
FGameplayTag Tag;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
float Value = 0.f;
bool operator==(const FItemFloatProperty& O) const { return Tag == O.Tag && Value == O.Value; }
};
USTRUCT(BlueprintType)
struct FItemIntProperty
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
FGameplayTag Tag;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
int32 Value = 0;
bool operator==(const FItemIntProperty& O) const { return Tag == O.Tag && Value == O.Value; }
};
USTRUCT(BlueprintType)
struct FItemBoolProperty
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
FGameplayTag Tag;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
bool Value = false;
bool operator==(const FItemBoolProperty& O) const { return Tag == O.Tag && Value == O.Value; }
};
USTRUCT(BlueprintType)
struct FItemStringProperty
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
FGameplayTag Tag;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
FString Value;
bool operator==(const FItemStringProperty& O) const { return Tag == O.Tag && Value == O.Value; }
};
/**
* Dynamic, per-instance properties keyed by gameplay tag (section 4.4).
* Only data differing from the definition defaults should be stored, to keep
* replication/save payloads small. Typed accessors fall back when a key is absent.
*/
USTRUCT(BlueprintType)
struct ITEMCORE_API FItemRuntimeProperties
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
TArray<FItemFloatProperty> FloatProperties;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
TArray<FItemIntProperty> IntProperties;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
TArray<FItemBoolProperty> BoolProperties;
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Item|Runtime")
TArray<FItemStringProperty> StringProperties;
bool IsEmpty() const
{
return FloatProperties.Num() == 0 && IntProperties.Num() == 0
&& BoolProperties.Num() == 0 && StringProperties.Num() == 0;
}
// --- Float ---
float GetFloat(const FGameplayTag& Tag, float Fallback = 0.f) const
{
for (const FItemFloatProperty& P : FloatProperties) { if (P.Tag == Tag) { return P.Value; } }
return Fallback;
}
bool HasFloat(const FGameplayTag& Tag) const
{
for (const FItemFloatProperty& P : FloatProperties) { if (P.Tag == Tag) { return true; } }
return false;
}
void SetFloat(const FGameplayTag& Tag, float Value)
{
for (FItemFloatProperty& P : FloatProperties) { if (P.Tag == Tag) { P.Value = Value; return; } }
FloatProperties.Add({ Tag, Value });
}
void RemoveFloat(const FGameplayTag& Tag)
{
FloatProperties.RemoveAll([&Tag](const FItemFloatProperty& P) { return P.Tag == Tag; });
}
// --- Int ---
int32 GetInt(const FGameplayTag& Tag, int32 Fallback = 0) const
{
for (const FItemIntProperty& P : IntProperties) { if (P.Tag == Tag) { return P.Value; } }
return Fallback;
}
bool HasInt(const FGameplayTag& Tag) const
{
for (const FItemIntProperty& P : IntProperties) { if (P.Tag == Tag) { return true; } }
return false;
}
void SetInt(const FGameplayTag& Tag, int32 Value)
{
for (FItemIntProperty& P : IntProperties) { if (P.Tag == Tag) { P.Value = Value; return; } }
IntProperties.Add({ Tag, Value });
}
// --- Bool ---
bool GetBool(const FGameplayTag& Tag, bool Fallback = false) const
{
for (const FItemBoolProperty& P : BoolProperties) { if (P.Tag == Tag) { return P.Value; } }
return Fallback;
}
bool HasBool(const FGameplayTag& Tag) const
{
for (const FItemBoolProperty& P : BoolProperties) { if (P.Tag == Tag) { return true; } }
return false;
}
void SetBool(const FGameplayTag& Tag, bool Value)
{
for (FItemBoolProperty& P : BoolProperties) { if (P.Tag == Tag) { P.Value = Value; return; } }
BoolProperties.Add({ Tag, Value });
}
// --- String ---
FString GetString(const FGameplayTag& Tag, const FString& Fallback = FString()) const
{
for (const FItemStringProperty& P : StringProperties) { if (P.Tag == Tag) { return P.Value; } }
return Fallback;
}
bool HasString(const FGameplayTag& Tag) const
{
for (const FItemStringProperty& P : StringProperties) { if (P.Tag == Tag) { return true; } }
return false;
}
void SetString(const FGameplayTag& Tag, const FString& Value)
{
for (FItemStringProperty& P : StringProperties) { if (P.Tag == Tag) { P.Value = Value; return; } }
StringProperties.Add({ Tag, Value });
}
bool operator==(const FItemRuntimeProperties& Other) const
{
return ArrayEqual(FloatProperties, Other.FloatProperties)
&& ArrayEqual(IntProperties, Other.IntProperties)
&& ArrayEqual(BoolProperties, Other.BoolProperties)
&& ArrayEqual(StringProperties, Other.StringProperties);
}
bool operator!=(const FItemRuntimeProperties& Other) const { return !(*this == Other); }
private:
template<typename T>
static bool ArrayEqual(const TArray<T>& A, const TArray<T>& B)
{
if (A.Num() != B.Num()) { return false; }
for (const T& Elem : A) { if (!B.Contains(Elem)) { return false; } }
return true;
}
};