Files
PHY/Source/PHYInventory/Private/UI/ItemStacks/ItemStackContainer.cpp
2026-03-03 01:23:02 +08:00

494 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
#include "UI/ItemStacks/ItemStackContainer.h"
#include "CommonTabListWidgetBase.h"
#include "GIS_InventoryFunctionLibrary.h"
#include "GIS_InventorySystemComponent.h"
#include "GIS_ItemCollection.h"
#include "GIS_ItemSlotCollection.h"
#include "ItemFilterInterface.h"
#include "Async/GIS_AsyncAction_WaitInventorySystem.h"
#include "Components/ListView.h"
void UItemStackContainer::SetOwningActor_Implementation(AActor* NewOwningActor)
{
OwningActor = NewOwningActor;
}
void UItemStackContainer::OnDeactivated_Implementation()
{
IGUIS_UserWidgetInterface::OnDeactivated_Implementation();
}
void UItemStackContainer::HandleTabSelected(const FName TabId)
{
ApplyFilter(TabId);
}
void UItemStackContainer::HandleInventorySystemInitialized()
{
WaitInventorySystem = nullptr;
if (OwningActor.IsValid())
{
InventorySystemComponent = UGIS_InventorySystemComponent::GetInventorySystemComponent(OwningActor.Get());
if (InventorySystemComponent.IsValid())
{
// 订阅库存系统的消息
InventorySystemComponent.Get()->OnInventoryStackUpdate.AddDynamic(this,&UItemStackContainer::HandleInventoryStackChanged);
}
if (FilterTabsReference)
{
FilterTabsReference.Get()->OnTabSelected.AddDynamic(this,&UItemStackContainer::HandleTabSelected);
}
// 创建默认的item data槽位
CreateDefaultItemDataSlots();
// 同步数据
SyncAll();
}
}
void UItemStackContainer::CreateDefaultItemDataSlots()
{
if (!InventorySystemComponent.IsValid()) return;
UGIS_ItemCollection* OutCollection;
// 从库存系统中获取集合并添加到item data中
if (InventorySystemComponent->FindTypedCollectionByTag(CollectionToTrackTag,UGIS_ItemSlotCollection::StaticClass(),OutCollection))
{
if (UGIS_ItemSlotCollection* SlotCollection = Cast<UGIS_ItemSlotCollection>(OutCollection))
{
const TArray<FGIS_ItemSlotDefinition>& Definitions = SlotCollection->GetMyDefinition()->GetSlotDefinitions();
for (int i = 0; i < Definitions.Num(); ++i)
{
SetOrAddItemInfoToItemDataArray(FGIS_ItemInfo(),i);
}
for (int i = 0; i < ItemDataArray.Num(); ++i)
{
ItemDataArray[i]->SetItemSlotDefinition(Definitions[i]);
}
return;
}
}
// 如果没有找到slot collection则创建默认数量的item data槽位
if (bCreateDefaultItemDataSlots)
{
for (int i = 0; i < DefaultItemDataSlotCount; ++i)
{
SetOrAddItemInfoToItemDataArray(FGIS_ItemInfo(),i);
}
}
}
void UItemStackContainer::OnActivated_Implementation()
{
// 注册list view events
RegisterListViewEvents();
OwningActor = Execute_GetOwningActor(this);
if (OwningActor.IsValid())
{
WaitInventorySystem = UGIS_AsyncAction_WaitInventorySystemInitialized::WaitInventorySystemInitialized(this,OwningActor.Get());
if (WaitInventorySystem)
{
WaitInventorySystem->OnCompleted.AddDynamic(this,&UItemStackContainer::HandleInventorySystemInitialized);
}
}
}
AActor* UItemStackContainer::GetOwningActor_Implementation()
{
if (!OwningActor.IsValid())
{
if (GetOwningPlayerPawn())
{
OwningActor = GetOwningPlayerPawn();
}
else
{
OwningActor = GetOwningPlayer();
}
}
return OwningActor.Get();
}
void UItemStackContainer::AddFilterObject(UObject* InFilterObject)
{
if (InFilterObject && InFilterObject->Implements<UItemFilterInterface>())
{
ItemFilterObjects.AddUnique(InFilterObject);
}
}
void UItemStackContainer::RemoveFilterObject(UObject* InFilterObject)
{
if (InFilterObject && InFilterObject->Implements<UItemFilterInterface>())
{
ItemFilterObjects.Remove(InFilterObject);
}
}
void UItemStackContainer::SetSelectedIndexForNewItem(const int32 NewSelectedIndex)
{
SelectedIndexForNewItem = NewSelectedIndex;
}
bool UItemStackContainer::SwapItemDataSlots(const int32 IndexA, const int32 IndexB)
{
if (!CanMoveItem(IndexA,this,IndexB)) return false;
if (ItemDataArray.IsValidIndex(IndexA) && ItemDataArray.IsValidIndex(IndexB))
{
// 缓存A的数据
const UItemData* TempItemData = ItemDataArray[IndexA];
const UItemData* TargetItemData = ItemDataArray[IndexB];
// 缓存其中的数据
TOptional<FGIS_ItemInfo> TempItemInfo = TempItemData ? TOptional<FGIS_ItemInfo>(TempItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>();
// 交换数据
if (TOptional<FGIS_ItemInfo> TempTargetItemInfo = TargetItemData ? TOptional<FGIS_ItemInfo>(TargetItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>(); TempTargetItemInfo.IsSet())
{
AssignItemInfoToItemData(TempTargetItemInfo.GetValue(), IndexA);
}
if (TempItemInfo.IsSet())
{
AssignItemInfoToItemData(TempItemInfo.GetValue(), IndexB);
}
LastDropIndex = IndexB;
// 同步list view
SyncItemDataToListView();
return true;
}
return false;
}
bool UItemStackContainer::SwapItemDataToOtherContainer(const int32 SourceIndex, UItemStackContainer* TargetContainer,
const int32 TargetIndex)
{
if (!TargetContainer) return false;
if (!CanMoveItem(SourceIndex,TargetContainer,TargetIndex)) return false;
if (ItemDataArray.IsValidIndex(SourceIndex) && TargetContainer->GetItemDataArray().IsValidIndex(TargetIndex))
{
// 缓存A的数据
const UItemData* TempItemData = ItemDataArray[SourceIndex];
const UItemData* TargetItemData = TargetContainer->GetItemDataArray()[TargetIndex];
// 缓存其中的数据
TOptional<FGIS_ItemInfo> TempItemInfo = TempItemData ? TOptional<FGIS_ItemInfo>(TempItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>();
if (TOptional<FGIS_ItemInfo> TempTargetItemInfo = TargetItemData ? TOptional<FGIS_ItemInfo>(TargetItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>(); TempTargetItemInfo.IsSet())
{
AssignItemInfoToItemData(TempTargetItemInfo.GetValue(), SourceIndex);
}
if (TempItemInfo.IsSet())
{
TargetContainer->AssignItemInfoToItemData(TempItemInfo.GetValue(), TargetIndex);
}
TargetContainer->LastDropIndex = TargetIndex;
// 同步list view
SyncItemDataToListView();
TargetContainer->SyncItemDataToListView();
return true;
}
return false;
}
bool UItemStackContainer::CanMoveItem(const int32 IndexA,UItemStackContainer* TargetContainer,const int32 IndexB)
{
return true;
}
int32 UItemStackContainer::FindItemSlotIndex(const UItemData* InItemData) const
{
if (!InItemData) return INDEX_NONE;
return ItemDataArray.IndexOfByKey(InItemData);
}
void UItemStackContainer::AssignItemInfoToItemData(const FGIS_ItemInfo& InInfo, const int32 SlotIndex)
{
if (ItemDataArray.IsValidIndex(SlotIndex))
{
if (UItemData* ItemData = ItemDataArray[SlotIndex])
{
ItemData->SetItemInfo(InInfo);
}
}
}
void UItemStackContainer::UnassignItemInfoToItemData(const FGIS_ItemInfo& InInfo)
{
if (const int32 Index = FindItemIndexFromDataByStackID(InInfo.StackId); Index != INDEX_NONE)
{
const FGIS_ItemInfo EmptyInfo;
AssignItemInfoToItemData(EmptyInfo, Index);
}
}
int32 UItemStackContainer::FindItemIndexFromDataByStackID(const FGuid& StackID) const
{
for (int32 Index = 0; Index < ItemDataArray.Num(); ++Index)
{
if (const UItemData* ItemData = ItemDataArray[Index])
{
if (ItemData->GetItemInfo().StackId == StackID)
{
return Index;
}
}
}
return INDEX_NONE;
}
TArray<FGIS_ItemInfo> UItemStackContainer::GetItemInfoArrayFromInventorySystem()
{
TArray<FGIS_ItemInfo> LocalItemInfos;
if (InventorySystemComponent.IsValid())
{
if (CollectionToTrackTag.IsValid())
{
if (const UGIS_ItemCollection* ItemCollection = InventorySystemComponent.Get()->GetCollectionByTag(CollectionToTrackTag))
{
LocalItemInfos = ItemCollection->GetAllItemInfos();
}
}
}
if (LocalItemInfos.IsEmpty()) return LocalItemInfos;
// 通过配置的name和query 过滤
if (ItemFilterMap.Num() > 0 && ItemFilterName.IsValid())
{
if (const FGameplayTagQuery* Query = ItemFilterMap.Find(ItemFilterName))
{
LocalItemInfos = UGIS_InventoryFunctionLibrary::FilterItemInfosByTagQuery(LocalItemInfos,*Query);
}
}
// 通过Object过滤
if (ItemFilterObjects.Num() > 0)
{
for (UObject* FilterObject : ItemFilterObjects)
{
if (FilterObject && FilterObject->Implements<UItemFilterInterface>())
{
LocalItemInfos = IItemFilterInterface::Execute_FilterItemInfo(FilterObject,LocalItemInfos);
}
}
}
return LocalItemInfos;
}
void UItemStackContainer::ResetItemDataArray()
{
for (UItemData* ItemData : ItemDataArray)
{
ItemData->Reset();
}
}
void UItemStackContainer::SetOrAddItemInfoToItemDataArray(const FGIS_ItemInfo& InInfo, const int32 SlotIndex)
{
if (SlotIndex < 0)
{
return;
}
// 确保数组大小足够
if (ItemDataArray.Num() <= SlotIndex)
{
ItemDataArray.SetNum(SlotIndex + 1);
}
// 确保该槽位有对象
UItemData* ItemDataRef = ItemDataArray[SlotIndex];
if (!ItemDataRef)
{
ItemDataRef = NewObject<UItemData>(this);
}
ItemDataRef->SetItemInfo(InInfo);
}
void UItemStackContainer::SyncItemInfoToItemDataArray(const TArray<FGIS_ItemInfo>& InItemInfos)
{
if (InventorySystemComponent.IsValid() && CollectionToTrackTag.IsValid())
{
// 获取集合,如果是slotted collection则根据slot来赋值
if (const UGIS_ItemSlotCollection* SlotCollection = Cast<UGIS_ItemSlotCollection>(InventorySystemComponent->GetCollectionByTag(CollectionToTrackTag)))
{
for (int i = 0; i < InItemInfos.Num(); ++i)
{
const FGIS_ItemInfo& Info = InItemInfos[i];
const int32 SlotIndex = SlotCollection->GetItemSlotIndex(Info.Item);
SetOrAddItemInfoToItemDataArray(Info, SlotIndex == INDEX_NONE ? i : SlotIndex);
}
}
else
{
// 如果是普通的集合,则直接按顺序赋值
for (int i = 0; i < InItemInfos.Num(); ++i)
{
SetOrAddItemInfoToItemDataArray(InItemInfos[i], i);
}
}
}
}
void UItemStackContainer::SyncItemDataToListView()
{
if (ItemListView == nullptr) return;
ItemListView->ClearSelection();
ItemListView->ClearListItems();
ItemListView->SetListItems(ItemDataArray);
ItemListView->RegenerateAllEntries();
// 恢复上次拖拽的选中状态
if (ItemDataArray.IsValidIndex(LastDropIndex) && LastDropIndex != INDEX_NONE)
{
ItemListView->SetSelectedItem(ItemDataArray[LastDropIndex]);
LastDropIndex = INDEX_NONE;
} else
{
// 默认选中第一个
if (ItemDataArray.IsValidIndex(0) && ItemDataArray[0]->IsValidItem())
{
ItemListView->SetSelectedItem(ItemDataArray[0]);
}
}
}
int32 UItemStackContainer::FindSuitableItemDataSlotForNewItem()
{
if (SelectedIndexForNewItem == INDEX_NONE)
{
for (int i = 0; i < ItemDataArray.Num(); ++i)
{
if (const UItemData* ItemData = ItemDataArray[i])
{
if (!ItemData->IsValidItem())
{
return i;
}
}
}
return ItemDataArray.Num();
}
const int32 ReturnIndex = SelectedIndexForNewItem;
SelectedIndexForNewItem = INDEX_NONE;
return ReturnIndex;
}
void UItemStackContainer::SyncAll()
{
ResetItemDataArray();
SyncItemInfoToItemDataArray(GetItemInfoArrayFromInventorySystem());
SyncItemDataToListView();
}
void UItemStackContainer::ApplyFilter(const FName& InFilterName)
{
if (InFilterName.IsNone()) return;
if (ItemFilterMap.Contains(InFilterName))
{
ItemFilterName = InFilterName;
SyncAll();
}
}
void UItemStackContainer::HandleItemSelectionChanged(UObject* Object) const
{
if (UItemData* SelectedItemData = Cast<UItemData>(Object))
{
OnItemSelectionChanged.Broadcast(SelectedItemData,true);
return;
}
OnItemSelectionChanged.Broadcast(nullptr,false);
}
void UItemStackContainer::HandleItemHoveredChanged(UObject* Object, bool bIsHovered) const
{
if (UItemData* HoveredItemData = Cast<UItemData>(Object))
{
OnItemHoveredChanged.Broadcast(HoveredItemData,bIsHovered);
}
}
void UItemStackContainer::HandleItemClicked(UObject* Object) const
{
if (UItemData* ClickedItemData = Cast<UItemData>(Object))
{
OnItemClicked.Broadcast(ClickedItemData);
}
}
void UItemStackContainer::HandleListViewEntryWidgetGenerated(UUserWidget& UserWidget) const
{
OnItemEntryGenerated.Broadcast(&UserWidget);
}
void UItemStackContainer::HandleItemDoubleClicked(UObject* Object) const
{
if (UItemData* ClickedItemData = Cast<UItemData>(Object))
{
OnItemDoubleClicked.Broadcast(ClickedItemData);
}
}
void UItemStackContainer::RegisterListViewEvents()
{
if (ItemListView)
{
// 注册事件
ItemSelectionChangedHandle = ItemListView->OnItemSelectionChanged().AddUObject(this, &UItemStackContainer::HandleItemSelectionChanged);
ItemHoveredChangedHandle = ItemListView->OnItemIsHoveredChanged().AddUObject(this, &UItemStackContainer::HandleItemHoveredChanged);
ItemClickedHandle = ItemListView->OnItemClicked().AddUObject(this, &UItemStackContainer::HandleItemClicked);
ListViewEntryWidgetGeneratedHandle = ItemListView->OnEntryWidgetGenerated().AddUObject(this, &UItemStackContainer::HandleListViewEntryWidgetGenerated);
ItemDoubleClickedHandle = ItemListView->OnItemDoubleClicked().AddUObject(this, &UItemStackContainer::HandleItemDoubleClicked);
}
}
void UItemStackContainer::UnregisterListViewEvents() const
{
if (ItemListView)
{
// 注销事件
ItemListView->OnItemSelectionChanged().Remove(ItemSelectionChangedHandle);
ItemListView->OnItemIsHoveredChanged().Remove(ItemHoveredChangedHandle);
ItemListView->OnItemClicked().Remove(ItemClickedHandle);
ItemListView->OnEntryWidgetGenerated().Remove(ListViewEntryWidgetGeneratedHandle);
ItemListView->OnItemDoubleClicked().Remove(ItemDoubleClickedHandle);
}
}
void UItemStackContainer::HandleInventoryStackChanged(const FGIS_InventoryStackUpdateMessage& Message)
{
if (const UGIS_InventorySystemComponent* Inventory = Message.Inventory)
{
// 仅处理关联的库存系统组件的消息
if (!InventorySystemComponent.IsValid() && Inventory != InventorySystemComponent.Get()) return;
// 获取集合
if (UGIS_ItemCollection* ItemCollection = Inventory->GetCollectionById(Message.CollectionId))
{
// 仅处理配置的集合tag的消息
if (ItemCollection->GetCollectionTag() != CollectionToTrackTag) return;
FGIS_ItemInfo ChangedItemInfo = FGIS_ItemInfo(Message.Instance,Message.NewCount,ItemCollection,Message.StackId);
switch (Message.ChangeType) {
case EGIS_ItemStackChangeType::WasAdded:
// 添加
{
const int32 SlotIndex = FindSuitableItemDataSlotForNewItem();
SetOrAddItemInfoToItemDataArray(ChangedItemInfo, SlotIndex);
}
break;
case EGIS_ItemStackChangeType::WasRemoved:
// 移除
{
ChangedItemInfo.Amount = Message.NewCount - Message.Delta;
UnassignItemInfoToItemData(ChangedItemInfo);
}
break;
case EGIS_ItemStackChangeType::Changed:
// 修改
{
if (const int32 ChangedIndex = FindItemIndexFromDataByStackID(Message.StackId); ChangedIndex != INDEX_NONE)
{
SetOrAddItemInfoToItemDataArray(ChangedItemInfo, ChangedIndex);
}
}
break;
}
SyncItemDataToListView();
}
}
}