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>
174 lines
4.4 KiB
C++
174 lines
4.4 KiB
C++
// Copyright ExByte Studios. Item Interaction Ecosystem.
|
|
|
|
#include "ItemTransaction.h"
|
|
#include "ItemInventoryLog.h"
|
|
#include "InventoryComponent.h"
|
|
|
|
FItemTransaction& FItemTransaction::Add(UInventoryComponent* Inventory, const FItemInstanceData& Item)
|
|
{
|
|
FItemTransactionStep Step;
|
|
Step.Type = EItemTransactionStepType::AddItem;
|
|
Step.Inventory = Inventory;
|
|
Step.Item = Item;
|
|
Step.Item.EnsureInstanceId();
|
|
Step.InstanceId = Step.Item.InstanceId;
|
|
Steps.Add(Step);
|
|
return *this;
|
|
}
|
|
|
|
FItemTransaction& FItemTransaction::Remove(UInventoryComponent* Inventory, const FGuid& InstanceId)
|
|
{
|
|
FItemTransactionStep Step;
|
|
Step.Type = EItemTransactionStepType::RemoveItem;
|
|
Step.Inventory = Inventory;
|
|
Step.InstanceId = InstanceId;
|
|
Steps.Add(Step);
|
|
return *this;
|
|
}
|
|
|
|
FItemTransaction& FItemTransaction::Update(UInventoryComponent* Inventory, const FItemInstanceData& Item)
|
|
{
|
|
FItemTransactionStep Step;
|
|
Step.Type = EItemTransactionStepType::UpdateItem;
|
|
Step.Inventory = Inventory;
|
|
Step.Item = Item;
|
|
Step.InstanceId = Item.InstanceId;
|
|
Steps.Add(Step);
|
|
return *this;
|
|
}
|
|
|
|
FItemTransaction& FItemTransaction::Move(UInventoryComponent* Inventory, const FGuid& InstanceId, const FGuid& ToContainerId, int32 ToSlotIndex)
|
|
{
|
|
FItemTransactionStep Step;
|
|
Step.Type = EItemTransactionStepType::MoveItem;
|
|
Step.Inventory = Inventory;
|
|
Step.InstanceId = InstanceId;
|
|
Step.ToContainer = ToContainerId;
|
|
Step.ToSlot = ToSlotIndex;
|
|
Steps.Add(Step);
|
|
return *this;
|
|
}
|
|
|
|
bool FItemTransaction::Validate() const
|
|
{
|
|
for (const FItemTransactionStep& Step : Steps)
|
|
{
|
|
UInventoryComponent* Inv = Step.Inventory.Get();
|
|
if (!Inv)
|
|
{
|
|
return false;
|
|
}
|
|
switch (Step.Type)
|
|
{
|
|
case EItemTransactionStepType::AddItem:
|
|
{
|
|
// At least one container must accept it.
|
|
TArray<FInventoryContainer> Containers;
|
|
Inv->GetContainers(Containers);
|
|
bool bAccepts = false;
|
|
for (const FInventoryContainer& C : Containers)
|
|
{
|
|
if (Inv->CanAcceptItem(Step.Item, C.ContainerId)) { bAccepts = true; break; }
|
|
}
|
|
if (!bAccepts) { return false; }
|
|
break;
|
|
}
|
|
case EItemTransactionStepType::RemoveItem:
|
|
case EItemTransactionStepType::UpdateItem:
|
|
case EItemTransactionStepType::MoveItem:
|
|
{
|
|
FItemInstanceData Found;
|
|
if (!Inv->FindItem(Step.InstanceId, Found) || Found.Lock.bLocked) { return false; }
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FItemTransaction::Apply()
|
|
{
|
|
if (!Validate())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (FItemTransactionStep& Step : Steps)
|
|
{
|
|
UInventoryComponent* Inv = Step.Inventory.Get();
|
|
bool bOk = false;
|
|
switch (Step.Type)
|
|
{
|
|
case EItemTransactionStepType::AddItem:
|
|
{
|
|
FItemInstanceData ToAdd = Step.Item;
|
|
bOk = Inv->AddItem(ToAdd);
|
|
break;
|
|
}
|
|
case EItemTransactionStepType::RemoveItem:
|
|
{
|
|
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot); // capture for rollback
|
|
bOk = Inv->RemoveItem(Step.InstanceId);
|
|
break;
|
|
}
|
|
case EItemTransactionStepType::UpdateItem:
|
|
{
|
|
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot);
|
|
bOk = Inv->UpdateItem(Step.Item);
|
|
break;
|
|
}
|
|
case EItemTransactionStepType::MoveItem:
|
|
{
|
|
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot);
|
|
Step.UndoFromContainer = Step.UndoSnapshot.CurrentContainerId;
|
|
Step.UndoFromSlot = Step.UndoSnapshot.SlotIndex;
|
|
bOk = Inv->MoveItem(Step.InstanceId, Step.UndoFromContainer, Step.ToContainer, Step.ToSlot);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bOk)
|
|
{
|
|
UE_LOG(LogItemInventory, Warning, TEXT("[Transaction %s] step failed; rolling back."), *TransactionId.ToString());
|
|
Rollback();
|
|
return false;
|
|
}
|
|
Step.bApplied = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FItemTransaction::Rollback()
|
|
{
|
|
// Undo applied steps in reverse order.
|
|
for (int32 i = Steps.Num() - 1; i >= 0; --i)
|
|
{
|
|
FItemTransactionStep& Step = Steps[i];
|
|
if (!Step.bApplied)
|
|
{
|
|
continue;
|
|
}
|
|
UInventoryComponent* Inv = Step.Inventory.Get();
|
|
if (!Inv)
|
|
{
|
|
continue;
|
|
}
|
|
switch (Step.Type)
|
|
{
|
|
case EItemTransactionStepType::AddItem:
|
|
Inv->RemoveItem(Step.InstanceId);
|
|
break;
|
|
case EItemTransactionStepType::RemoveItem:
|
|
if (Step.UndoSnapshot.IsValid()) { FItemInstanceData Restore = Step.UndoSnapshot; Inv->AddItem(Restore); }
|
|
break;
|
|
case EItemTransactionStepType::UpdateItem:
|
|
if (Step.UndoSnapshot.IsValid()) { Inv->UpdateItem(Step.UndoSnapshot); }
|
|
break;
|
|
case EItemTransactionStepType::MoveItem:
|
|
Inv->MoveItem(Step.InstanceId, Step.ToContainer, Step.UndoFromContainer, Step.UndoFromSlot);
|
|
break;
|
|
}
|
|
Step.bApplied = false;
|
|
}
|
|
}
|