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>
99 lines
2.8 KiB
C++
99 lines
2.8 KiB
C++
// Copyright ExByte Studios. Item Interaction Ecosystem.
|
|
|
|
#include "ItemCraftingLibrary.h"
|
|
#include "ItemInventoryLog.h"
|
|
#include "InventoryComponent.h"
|
|
#include "ItemDatabaseSubsystem.h"
|
|
#include "ItemContentRows.h"
|
|
|
|
bool UItemCraftingLibrary::CanCraft(const UObject* WorldContext, UInventoryComponent* Inventory, FName RecipeId, FGameplayTag AvailableStation)
|
|
{
|
|
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
|
|
if (!DB || !Inventory)
|
|
{
|
|
return false;
|
|
}
|
|
FCraftingRecipeRow Recipe;
|
|
if (!DB->FindRecipe(RecipeId, Recipe))
|
|
{
|
|
return false;
|
|
}
|
|
if (Recipe.StationTag.IsValid() && !AvailableStation.MatchesTag(Recipe.StationTag))
|
|
{
|
|
return false;
|
|
}
|
|
for (const FCraftingIngredient& In : Recipe.Inputs)
|
|
{
|
|
if (Inventory->GetItemCount(In.ItemId) < In.Quantity)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool UItemCraftingLibrary::Craft(const UObject* WorldContext, UInventoryComponent* Inventory, FName RecipeId, FGameplayTag AvailableStation)
|
|
{
|
|
if (!Inventory || !Inventory->GetOwner() || !Inventory->GetOwner()->HasAuthority())
|
|
{
|
|
return false;
|
|
}
|
|
if (!CanCraft(WorldContext, Inventory, RecipeId, AvailableStation))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
|
|
FCraftingRecipeRow Recipe;
|
|
DB->FindRecipe(RecipeId, Recipe);
|
|
|
|
// Consume inputs (already verified present).
|
|
for (const FCraftingIngredient& In : Recipe.Inputs)
|
|
{
|
|
if (!ConsumeItem(Inventory, In.ItemId, In.Quantity))
|
|
{
|
|
UE_LOG(LogItemInventory, Error, TEXT("Craft '%s' failed mid-consume for %s."), *RecipeId.ToString(), *In.ItemId.ToString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FItemInstanceData Output = DB->MakeItemInstance(Recipe.OutputItemId, FMath::Max(1, Recipe.OutputQuantity));
|
|
Inventory->AddItem(Output);
|
|
UE_LOG(LogItemInventory, Log, TEXT("[Crafting] crafted %s"), *Recipe.OutputItemId.ToString());
|
|
return true;
|
|
}
|
|
|
|
void UItemCraftingLibrary::RollLootIntoInventory(const UObject* WorldContext, UInventoryComponent* Inventory, FName TableId, int32 RollCount)
|
|
{
|
|
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
|
|
if (!DB || !Inventory || !Inventory->GetOwner() || !Inventory->GetOwner()->HasAuthority())
|
|
{
|
|
return;
|
|
}
|
|
TArray<FItemInstanceData> Loot;
|
|
DB->RollLoot(TableId, RollCount, Loot);
|
|
for (FItemInstanceData& Item : Loot)
|
|
{
|
|
Inventory->AddItem(Item);
|
|
}
|
|
}
|
|
|
|
bool UItemCraftingLibrary::ConsumeItem(UInventoryComponent* Inventory, FName ItemId, int32 Count)
|
|
{
|
|
int32 Remaining = Count;
|
|
TArray<FItemInstanceData> Items;
|
|
Inventory->GetAllItems(Items);
|
|
for (const FItemInstanceData& Item : Items)
|
|
{
|
|
if (Remaining <= 0) { break; }
|
|
if (Item.ItemId != ItemId || Item.Lock.bLocked) { continue; }
|
|
|
|
const int32 Take = FMath::Min(Remaining, Item.Quantity);
|
|
if (Inventory->RemoveQuantity(Item.InstanceId, Take))
|
|
{
|
|
Remaining -= Take;
|
|
}
|
|
}
|
|
return Remaining <= 0;
|
|
}
|