There are many ways of doing this but i do it in a simlar way.
I have a ABaseItem:: it contains the most basic stuff User Firendly Name, is it a consumable, useable, placeable.
Then for any item that a player can interact with i have a second class derived from ABaseItem::.
Called AInteractableItem:: this is class has several virtual methods to be overriden in other classes.
So AInteractableItem:: has methods like virtual void OnPlayerUse();
So now you can override any behavior you need for other items when a player press use/ interacts with the item.
If picking up a weapon should do someting diferent from a e.g: Health Kit you can make two new classes.
AWeaponPrimary:: and ADoor:: for weapon it will try to add it self to inventory while pressing use on the door item will do something completly diferent, depending on the situation.
Then in the base class i have a structure that can define any item in my game like so.
USTRUCT()
struct FItemInventoryStruct
{
GENERATED_USTRUCT_BODY()
public:
// A integer representing one type of item.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item, Inventory")
int32 ItemID;
// A user friendly name for this item.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item, Inventory")
FString ItemName;
// The items UMG/ SLate brush icon all items have one.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item, Inventory")
USlateBrushAsset* ItemImageBrush;
// Used for spawning a new item when dropped.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item, Inventory")
FStringAssetReference ItemStringReference;
// The number the player have in this stack in the inventory UI.
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item, Inventory")
int32 NumberOfItemsInStack;
FItemInventoryStruct()
{
NumberOfItemsInStack = 1;
}
};
So when you want to spawn this item you know that it is ABaseItem:: and you can spawn it one as well.
And stile get any Blueprint Generated with the use of FStringAssetReference ,
Hope this helps.