494 lines
14 KiB
C++
494 lines
14 KiB
C++
//
|
||
|
||
|
||
#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();
|
||
}
|
||
}
|
||
}
|