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,129 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "GameplayTagContainer.h"
#include "ItemEnums.h"
#include "ItemAuxiliaryRows.generated.h"
/**
* DT_ItemProperties row (section 6.2): default runtime property for an item.
* Multiple rows share one ItemId, so the row name is arbitrary - the database
* indexes these by ItemId.
*/
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemPropertyRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
FName ItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
FGameplayTag PropertyTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
float DefaultValue = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
float Min = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
float Max = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
EItemPropertyReplicationPolicy ReplicationPolicy = EItemPropertyReplicationPolicy::OwnerOnly;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Property")
bool bSave = true;
};
/**
* DT_ItemFeatures row (section 6.3): one tunable parameter of a feature.
* e.g. raw_meat | Rotting | DecayRatePerHour | 0.08
* Value is stored as text so it can carry floats, ints or item ids uniformly.
*/
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemFeatureRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Feature")
FName ItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Feature")
FGameplayTag Feature;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Feature")
FName Param;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Feature")
FString Value;
float GetFloat(float Fallback = 0.f) const
{
return Value.IsNumeric() ? FCString::Atof(*Value) : Fallback;
}
int32 GetInt(int32 Fallback = 0) const
{
return Value.IsNumeric() ? FCString::Atoi(*Value) : Fallback;
}
};
/**
* DT_ItemInteractions row (section 6.4): a data-driven interaction option.
* The interaction resolver turns matching rows into FInteractionOptions.
*/
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemInteractionRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FName ItemId;
/** Context filter, e.g. "World", "Inventory", "Held+Vehicle", "Held+Generator". */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FName Context;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FGameplayTag ActionTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
EItemInteractionMode InteractionMode = EItemInteractionMode::None;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
int32 Priority = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
bool bRequiresHold = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
float HoldDuration = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
float ActionDuration = 0.f;
/** Free-form requirement expression resolved by the requirement system (section 18). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FString Requirement;
/** Result verb, e.g. AddToInventory / HoldWorldItem / InstallIntoSlot / TransferFuel. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FName Result;
};
/** DT_ItemRedirects row (section 26.1): maps a retired ItemId to its replacement. */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemRedirectRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Redirect")
FName OldItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Redirect")
FName NewItemId;
};

View File

@ -0,0 +1,121 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "GameplayTagContainer.h"
#include "ItemEnums.h"
#include "ItemContentRows.generated.h"
/** DT_Containers row (section 6): a reusable container template. */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FContainerDefRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
FGameplayTag ContainerTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
EInventoryContainerType Type = EInventoryContainerType::SlotList;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
int32 Width = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
int32 Height = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
int32 MaxSlots = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
float MaxWeight = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
FGameplayTagContainer AcceptedItemTags;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Container")
FGameplayTagContainer BlockedItemTags;
};
/** DT_WorldSlots row (section 6/14): documents a world slot's contract. */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FWorldSlotDefRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "World Slot")
FGameplayTag SlotTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "World Slot")
FGameplayTagContainer AcceptedItemTags;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "World Slot")
FName AttachSocketName;
};
/** DT_LootTables row (section 6): one weighted drop in a named loot table. */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FLootEntryRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
FName TableId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
FName ItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
float Weight = 1.f;
/** 0..1 independent chance that this entry rolls at all. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
float Chance = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
int32 MinQuantity = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loot")
int32 MaxQuantity = 1;
};
/** One required ingredient of a recipe. */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FCraftingIngredient
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
FName ItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
int32 Quantity = 1;
};
/** DT_CraftingRecipes row (section 6). */
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FCraftingRecipeRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
FName RecipeId;
/** Optional station requirement, e.g. Station.Workbench. Empty = craft anywhere. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
FGameplayTag StationTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
TArray<FCraftingIngredient> Inputs;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
FName OutputItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
int32 OutputQuantity = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Crafting")
float CraftTime = 0.f;
};

View File

@ -0,0 +1,8 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Logging/LogMacros.h"
ITEMDATABASE_API DECLARE_LOG_CATEGORY_EXTERN(LogItemDatabase, Log, All);

View File

@ -0,0 +1,169 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "ItemDefinitionRow.h"
#include "ItemAuxiliaryRows.h"
#include "ItemContentRows.h"
#include "ItemInstanceData.h"
#include "ItemDatabaseSubsystem.generated.h"
/** One problem found by ValidateData (section 28). */
USTRUCT(BlueprintType)
struct FItemValidationIssue
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly, Category = "Validation")
FName ItemId;
UPROPERTY(BlueprintReadOnly, Category = "Validation")
FString Message;
UPROPERTY(BlueprintReadOnly, Category = "Validation")
bool bIsError = true;
};
/**
* Authoritative, read-only access point for all static item data (section 35 stage 1).
*
* Loads the configured data tables once, flattens them into fast lookup maps and
* exposes definition/property/feature/interaction queries plus redirect resolution
* and a data validator. Lives on the GameInstance so it is available on both
* server and clients; the tables themselves are never replicated (section 32).
*/
UCLASS()
class ITEMDATABASE_API UItemDatabaseSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
//~ USubsystem
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
/** Convenience accessor from any world context. */
static UItemDatabaseSubsystem* Get(const UObject* WorldContext);
/** (Re)load every configured table and rebuild the lookup caches. */
void RebuildCache();
// --- Definition lookup ---
/** Returns the definition for an item (after redirects), or null if unknown. C++ fast path. */
const FItemDefinitionRow* GetItemDefinition(FName ItemId) const;
UFUNCTION(BlueprintCallable, Category = "Item|Database")
bool FindItemDefinition(FName ItemId, FItemDefinitionRow& OutDefinition) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
bool HasItem(FName ItemId) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
void GetAllItemIds(TArray<FName>& OutItemIds) const;
/** Applies the redirect chain; returns the input unchanged if no redirect exists. */
UFUNCTION(BlueprintPure, Category = "Item|Database")
FName ResolveRedirect(FName ItemId) const;
// --- Instances ---
/**
* Builds a fresh instance of ItemId with a new GUID and its default runtime
* properties applied. The single correct way to mint an item (server-side).
*/
UFUNCTION(BlueprintCallable, Category = "Item|Database")
FItemInstanceData MakeItemInstance(FName ItemId, int32 Quantity = 1) const;
/** Writes the item's default runtime properties into Out (does not clear first). */
void ApplyDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const;
/** Adds only default properties that are missing (save migration, section 26.2). */
void ApplyMissingDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const;
/** Current schema version stamped onto fresh/migrated instances. */
static constexpr int32 CurrentItemDataVersion = 1;
/** Applies redirects + version migration to a loaded instance (section 26). */
UFUNCTION(BlueprintCallable, Category = "Item|Database")
void MigrateLoadedItem(UPARAM(ref) FItemInstanceData& Item) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
int32 GetMaxStack(FName ItemId) const;
// --- Auxiliary data ---
void GetPropertyRows(FName ItemId, TArray<FItemPropertyRow>& Out) const;
void GetFeatureRows(FName ItemId, TArray<FItemFeatureRow>& Out) const;
/** Feature rows for one specific feature tag (e.g. all Rotting params of raw_meat). */
void GetFeatureRows(FName ItemId, const FGameplayTag& Feature, TArray<FItemFeatureRow>& Out) const;
void GetInteractionRows(FName ItemId, TArray<FItemInteractionRow>& Out) const;
/** Per-property replication policy default (section 22), or None if unspecified. */
EItemPropertyReplicationPolicy GetPropertyPolicy(FName ItemId, const FGameplayTag& PropertyTag) const;
/** Property tags on an item that are flagged ServerOnly (never sent to clients). */
void GetServerOnlyProperties(FName ItemId, TArray<FGameplayTag>& Out) const;
// --- Containers / world slots / loot / recipes (section 6) ---
UFUNCTION(BlueprintPure, Category = "Item|Database")
bool FindContainerDef(FGameplayTag ContainerTag, FContainerDefRow& OutDef) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
bool FindWorldSlotDef(FGameplayTag SlotTag, FWorldSlotDefRow& OutDef) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
bool FindRecipe(FName RecipeId, FCraftingRecipeRow& OutRecipe) const;
UFUNCTION(BlueprintPure, Category = "Item|Database")
void GetAllRecipes(TArray<FCraftingRecipeRow>& OutRecipes) const;
void GetLootEntries(FName TableId, TArray<FLootEntryRow>& Out) const;
/** Rolls a loot table into a list of fresh item instances (server). */
UFUNCTION(BlueprintCallable, Category = "Item|Database")
void RollLoot(FName TableId, int32 RollCount, TArray<FItemInstanceData>& OutItems) const;
// --- Validation (section 28) ---
UFUNCTION(BlueprintCallable, Category = "Item|Database")
void ValidateData(TArray<FItemValidationIssue>& OutIssues) const;
private:
void LoadDefinitions(UDataTable* Table);
void LoadProperties(UDataTable* Table);
void LoadFeatures(UDataTable* Table);
void LoadInteractions(UDataTable* Table);
void LoadRedirects(UDataTable* Table);
void LoadContainers(UDataTable* Table);
void LoadWorldSlots(UDataTable* Table);
void LoadLoot(UDataTable* Table);
void LoadRecipes(UDataTable* Table);
/** ItemId -> definition. */
TMap<FName, FItemDefinitionRow> Definitions;
/** ItemId -> default property rows. */
TMap<FName, TArray<FItemPropertyRow>> PropertiesByItem;
/** ItemId -> feature parameter rows. */
TMap<FName, TArray<FItemFeatureRow>> FeaturesByItem;
/** ItemId -> interaction rows. */
TMap<FName, TArray<FItemInteractionRow>> InteractionsByItem;
/** Old -> new id. */
TMap<FName, FName> Redirects;
TMap<FGameplayTag, FContainerDefRow> ContainerDefs;
TMap<FGameplayTag, FWorldSlotDefRow> WorldSlotDefs;
TMap<FName, TArray<FLootEntryRow>> LootByTable;
TMap<FName, FCraftingRecipeRow> RecipesById;
bool bCacheBuilt = false;
};

View File

@ -0,0 +1,112 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "GameplayTagContainer.h"
#include "ItemPlacementRules.h"
#include "ItemDefinitionRow.generated.h"
class UTexture2D;
class UStaticMesh;
/**
* Static, design-time data for an item type (section 4.2 / 6.1 DT_Items).
*
* One row per ItemId. Assets are soft references so the database never force-loads
* meshes/icons just to answer a flag query (section 33). Never replicated; never
* saved - only the ItemId travels in FItemInstanceData.
*/
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemDefinitionRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity")
FName ItemId;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity")
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity", meta = (MultiLine = true))
FText Description;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity")
FGameplayTag ItemType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Identity")
FGameplayTagContainer ItemTags;
// --- Visuals (soft refs, section 33) ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visual")
TSoftObjectPtr<UTexture2D> Icon;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visual")
TSoftObjectPtr<UStaticMesh> WorldMesh;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visual")
TSoftClassPtr<AActor> PickupActorClass;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visual")
TSoftClassPtr<AActor> EquippedActorClass;
// --- Stacking / weight / grid ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stack", meta = (ClampMin = "1"))
int32 MaxStack = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stack", meta = (ClampMin = "0"))
float Weight = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stack")
FIntPoint GridSize = FIntPoint(1, 1);
// --- Capability flags (section 4.2) ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanStoreInInventory = true;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanInstantPickup = true;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanWorldCarry = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanEquip = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanPlace = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanInstallIntoWorldSlot = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bCanDrop = true;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capabilities")
bool bIsQuestCritical = false;
// --- Interaction defaults ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FGameplayTag DefaultInteraction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
FGameplayTag AlternativeInteraction;
// --- Slot compatibility ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Slots")
FGameplayTagContainer AllowedEquipmentSlots;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Slots")
FGameplayTagContainer CompatibleWorldSlots;
// --- Features ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Features")
FGameplayTagContainer ItemFeatures;
// --- Placement (section 13.3); only meaningful when bCanPlace ---
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement", meta = (EditCondition = "bCanPlace"))
FItemPlacementRules PlacementRules;
bool HasFeature(const FGameplayTag& Feature) const { return ItemFeatures.HasTag(Feature); }
};

View File

@ -0,0 +1,63 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DeveloperSettings.h"
#include "ItemEcosystemSettings.generated.h"
class UDataTable;
/**
* Project Settings -> Game -> Item Ecosystem.
* Designers assign the data tables here; the database subsystem reads them on init.
* Keeping the wiring in settings means no code change to repoint tables (section 3).
*/
UCLASS(Config = Game, DefaultConfig, meta = (DisplayName = "Item Ecosystem"))
class ITEMDATABASE_API UItemEcosystemSettings : public UDeveloperSettings
{
GENERATED_BODY()
public:
UItemEcosystemSettings();
virtual FName GetCategoryName() const override { return FName("Game"); }
/** DT_Items - row struct FItemDefinitionRow. The one required table. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> ItemsTable;
/** DT_ItemProperties - row struct FItemPropertyRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> PropertiesTable;
/** DT_ItemFeatures - row struct FItemFeatureRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> FeaturesTable;
/** DT_ItemInteractions - row struct FItemInteractionRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> InteractionsTable;
/** DT_ItemRedirects - row struct FItemRedirectRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> RedirectsTable;
/** DT_Containers - row struct FContainerDefRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> ContainersTable;
/** DT_WorldSlots - row struct FWorldSlotDefRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> WorldSlotsTable;
/** DT_LootTables - row struct FLootEntryRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> LootTable;
/** DT_CraftingRecipes - row struct FCraftingRecipeRow. */
UPROPERTY(Config, EditAnywhere, Category = "Data Tables", meta = (AllowedClasses = "/Script/Engine.DataTable"))
TSoftObjectPtr<UDataTable> RecipesTable;
static const UItemEcosystemSettings* Get();
};

View File

@ -0,0 +1,69 @@
// Copyright ExByte Studios. Item Interaction Ecosystem.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "ItemPlacementRules.generated.h"
/** How a placeable item snaps when previewed/placed (section 13.3). */
UENUM(BlueprintType)
enum class EItemPlacementMode : uint8
{
FreeSurface,
SnapToGrid,
SnapToSocket
};
/**
* Static placement constraints for a placeable item (section 13.3).
* Evaluated locally for the ghost preview and re-validated on the server.
*/
USTRUCT(BlueprintType)
struct ITEMDATABASE_API FItemPlacementRules
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
EItemPlacementMode PlacementMode = EItemPlacementMode::FreeSurface;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
float MaxDistance = 400.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
bool bRequiresFlatSurface = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement", meta = (ClampMin = "0", ClampMax = "90"))
float MaxSlopeAngle = 35.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
FName CollisionProfile = FName("BlockAll");
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
FGameplayTagContainer AllowedSurfaceTags;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
FGameplayTagContainer BlockedSurfaceTags;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
float SnapToGrid = 0.f;
/** When PlacementMode == SnapToSocket, only sockets carrying this tag accept the item. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
FGameplayTag SnapToSocketTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
float MinDistanceFromOtherSameItems = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
bool bCanPlaceInWater = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
bool bCanPlaceOnVehicle = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
bool bCanPlaceOnWall = false;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Placement")
bool bCanPlaceOnGround = true;
};