From f940ff00d33dc69dd82f1376ba7da36e7efd922e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:35:58 +0000 Subject: [PATCH 1/7] Initial plan From f655307b05bf1fe3d9c55de585f34b7e6cf089c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:45:05 +0000 Subject: [PATCH 2/7] Sync codebase with Item-System-Refactor branch (NaItemInventory, INaItemInventoryWidget, etc.) Co-authored-by: SodiumZH <46860829+SodiumZH@users.noreply.github.com> --- .../Private/BPLibraries/NaItemStatics.cpp | 31 -- .../Components/NaItemContainerComponent.cpp | 51 --- .../Components/NaItemInventoryComponent.cpp | 48 +++ .../NaItemSystem/Private/NaItemContainer.cpp | 383 ------------------ .../NaItemSystem/Private/NaItemEffect.cpp | 20 - .../NaItemSystem/Private/NaItemInventory.cpp | 381 +++++++++++++++++ .../NaItemSystem/Private/NaItemType.cpp | 10 +- .../Private/UI/NaItemInventoryWidget.cpp | 7 + .../Private/UI/UMG/NaItemSlotList.cpp | 5 +- .../Private/UI/Widgets/SNaItemSlot.cpp | 2 +- .../Private/UI/Widgets/SNaItemSlotList.cpp | 31 +- .../Public/Actors/NaDroppedItem.h | 1 + .../Public/BPLibraries/NaItemDataStatics.h | 1 - .../Public/BPLibraries/NaItemStatics.h | 76 ---- .../Components/NaItemContainerComponent.h | 54 --- .../Components/NaItemInventoryComponent.h | 43 ++ .../Public/Data/NaItemDisplayData.h | 30 -- .../Public/Data/NaItemEffectData.h | 34 -- .../NaItemSystem/Public/NaItemContainer.h | 208 ---------- .../Source/NaItemSystem/Public/NaItemEffect.h | 183 --------- .../NaItemSystem/Public/NaItemInventory.h | 143 +++++++ .../Source/NaItemSystem/Public/NaItemStack.h | 4 +- .../Source/NaItemSystem/Public/NaItemType.h | 3 +- .../Public/UI/NaItemInventoryWidget.h | 43 ++ .../Public/UI/UMG/NaItemSlotList.h | 6 +- .../Public/UI/Widgets/SNaItemSlotList.h | 14 +- .../ProjectAS/Private/Items/LItemEffect.cpp | 10 - .../Private/Items/LItemEffectBase.cpp | 0 Source/ProjectAS/Public/Items/LItemEffect.h | 21 - .../ProjectAS/Public/Items/LItemEffectBase.h | 21 - 30 files changed, 708 insertions(+), 1156 deletions(-) delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemContainerComponent.cpp create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemInventoryComponent.cpp delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemContainer.cpp delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemEffect.cpp create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemInventory.cpp create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemContainerComponent.h create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemInventoryComponent.h delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemDisplayData.h delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemEffectData.h delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemContainer.h delete mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemEffect.h create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemInventory.h create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h delete mode 100644 Source/ProjectAS/Private/Items/LItemEffect.cpp delete mode 100644 Source/ProjectAS/Private/Items/LItemEffectBase.cpp delete mode 100644 Source/ProjectAS/Public/Items/LItemEffect.h delete mode 100644 Source/ProjectAS/Public/Items/LItemEffectBase.h diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/BPLibraries/NaItemStatics.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/BPLibraries/NaItemStatics.cpp index ea8885e..324342a 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/BPLibraries/NaItemStatics.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/BPLibraries/NaItemStatics.cpp @@ -1,39 +1,8 @@ #pragma once #include "BPLibraries/NaItemStatics.h" -#include "NaItemContainer.h" #include "NaPublicDependencies/NaPublicDependencies.h" #include "Components/NaGameModeItemSystemComponent.h" -void UNaItemStatics::FindEntryFromIndex(const FNaItemContainer& Target, int Position, ENaItemContainerFindingResult& ReturnType, UNaItemStack*& Result) { - FNaItemContainerFindingReturn Return = Target.Find(Position); - ReturnType = Return.Result; - Result = (ReturnType == ENaItemContainerFindingResult::ICFR_Filled) ? Return.Stack : nullptr; -} - -bool UNaItemStatics::ResizeContainer(FNaItemContainer& Target, int NewSize, bool bForce) { - return Target.Resize(NewSize, bForce); -} - -bool UNaItemStatics::AddItemEntry(FNaItemContainer& Target, int Position, UNaItemStack* Stack, bool bForce) { - return Target.AddEntry(Position, Stack, bForce); -} - -int UNaItemStatics::AddOrStackItemEntry(FNaItemContainer& Target, int Position, UNaItemStack* Stack) { - return Target.AddOrStack(Position, Stack); -} - -void UNaItemStatics::RemoveItemEntry(FNaItemContainer& Target, int Position) { - Target.RemoveEntry(Position); -} - -bool UNaItemStatics::MoveItemEntry(FNaItemContainer& Target, int From, int To, bool bForce) { - return Target.MoveEntry(From, To, bForce); -} - -void UNaItemStatics::SwapItemEntry(FNaItemContainer& Target, int P1, int P2) { - Target.SwapEntry(P1, P2); -} - UNaGameModeItemSystemComponent* UNaItemStatics::GetGameModeItemSystemComponent(UObject* WorldContext) { return Cast(UNaPublicDependencyStatics::GetNaGameModeSubunit(WorldContext, UNaGameModeItemSystemComponent::StaticClass())); } \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemContainerComponent.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemContainerComponent.cpp deleted file mode 100644 index 0c72f60..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemContainerComponent.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// By Sodium - -#include "Components/NaItemContainerComponent.h" -#include "NaItemContainer.h" -#include "UI/UMG/NaItemSlotList.h" -#include "UI/Widgets/SNaItemSlotList.h" -#include "NaItemEffect.h" - -// Sets default values for this component's properties -UNaItemContainerComponent::UNaItemContainerComponent() -{ - // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features - // off to improve performance if you don't need them. - PrimaryComponentTick.bCanEverTick = false; - bWantsInitializeComponent = true; - // ... -} - -void UNaItemContainerComponent::InitializeComponent() { - Super::InitializeComponent(); - if (bInitFromInitializer) - Container = FNaItemContainer(InitSize, Initializer); - else - Container = FNaItemContainer(InitSize); -} - -// Called when the game starts -void UNaItemContainerComponent::BeginPlay() -{ - Super::BeginPlay(); - - - // ... - -} - - -// Called every frame -void UNaItemContainerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) -{ - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - // ... -} - -ENaItemContainerUsageResult UNaItemContainerComponent::UseItemFromSelf(int Position, AActor* Target) { - checkf(Container.IsInSize(Position), TEXT("NaItemContainerComponent use item error: invalid position. Position: %d"), Position); - if(IsValid(GetOwner())) - return Container.UseItem(this, Position, GetOwner(), Target); - else return ENaItemContainerUsageResult::ICUR_Failed; -} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemInventoryComponent.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemInventoryComponent.cpp new file mode 100644 index 0000000..f2c8c30 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/Components/NaItemInventoryComponent.cpp @@ -0,0 +1,48 @@ +#include "Components/NaItemInventoryComponent.h" +#include "NatriumItemSystem.h" + +UNaItemInventoryComponent::UNaItemInventoryComponent() +{ + bWantsInitializeComponent = true; + Inventory = nullptr; +} + +void UNaItemInventoryComponent::InitializeComponent() +{ + Super::InitializeComponent(); + Inventory = UNaItemInventory::CreateInventory(this, InitSize); +} + +UNaItemInventory* UNaItemInventoryComponent::GetInventory() const +{ + return Inventory; +} + +UNaItemStack* UNaItemInventoryComponent::GetSlot(int32 Slot) const +{ + if (!Inventory) + { + return nullptr; + } + return Inventory->GetSlot(Slot); +} + +int32 UNaItemInventoryComponent::GiveItem(UNaItemStack* Stack) +{ + if (!Inventory) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventoryComponent::GiveItem: Inventory is null")); + return Stack ? Stack->Count : 0; + } + return Inventory->GiveItem(Stack); +} + +int32 UNaItemInventoryComponent::ConsumeItemByType(UNaItemType* Type, int32 Amount) +{ + if (!Inventory) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventoryComponent::ConsumeItemByType: Inventory is null")); + return 0; + } + return Inventory->ConsumeItemByType(Type, Amount); +} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemContainer.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemContainer.cpp deleted file mode 100644 index 9a7113b..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemContainer.cpp +++ /dev/null @@ -1,383 +0,0 @@ - -#include "NaItemContainer.h" -#include "NatriumUtility.h" -#include "NaItemEffect.h" -#include "NatriumItemSystem.h" -#include "GameFramework/Actor.h" - - -// Found item successfully -FNaItemContainerFindingReturn::FNaItemContainerFindingReturn(UNaItemStack* InStack) { - Stack = InStack; - Result = ENaItemContainerFindingResult::ICFR_Filled; -} - -// Not found -FNaItemContainerFindingReturn::FNaItemContainerFindingReturn(NotFoundType Type) { - Stack = nullptr; - switch (Type) - { - case FNaItemContainerFindingReturn::NotFoundType::Empty: - Result = ENaItemContainerFindingResult::ICFR_Empty; - break; - case FNaItemContainerFindingReturn::NotFoundType::OOS: - Result = ENaItemContainerFindingResult::ICFR_OOS; - UE_LOG(LogNaItem, Log, TEXT("Item finding: out of container size.")); - break; - case FNaItemContainerFindingReturn::NotFoundType::InvID: - Result = ENaItemContainerFindingResult::ICFR_InvID; - UE_LOG(LogNaItem, Warning, TEXT("Item finding: wrong input index (negative or larger than ITEM_CONTAINER_MAX_SIZE).")); - break; - case FNaItemContainerFindingReturn::NotFoundType::InvVal: - Result = ENaItemContainerFindingResult::ICFR_InvVal; - UE_LOG(LogNaItem, Error, TEXT("Item finding: wrong item entry detected.")); - break; - case FNaItemContainerFindingReturn::NotFoundType::Error: - UE_LOG(LogNaItem, Error, TEXT("Item finding: unknown error detected.")); - Result = ENaItemContainerFindingResult::ICFR_Error; - break; - default: - checkNoEntry(); - } -} - - -/**** FNaItemContainer ****/ - -FNaItemContainer::FNaItemContainer() { - Size = 64; - for (int i = 0; i < 64; ++i) - Content.Emplace(nullptr); -} - -FNaItemContainer::FNaItemContainer(int InSize) { - Size = InSize; - for (int i = 0; i < Size; ++i) - Content.Emplace(nullptr); -} - -FNaItemContainer::FNaItemContainer(const FNaItemContainer& CopyFrom) { - Content.Empty(); - Size = CopyFrom.GetSize(); - for (int i = 0; i < Size; ++i) { - if (CopyFrom.Content[i] && !CopyFrom.Content[i]->IsEmpty()) - Content.Emplace(UNaItemStack::CopyItemStack(nullptr, CopyFrom.Content[i].Get())); - else - Content.Emplace(nullptr); - } -} - -FNaItemContainer::FNaItemContainer(int InSize, const TMap& InitialContent) : FNaItemContainer(InSize) { - for (const auto& Elem : InitialContent) { - if (IsInSize(Elem.Key)) { - Content[Elem.Key] = Elem.Value; - } - } -} - -bool FNaItemContainer::Resize(int NewSize, bool bForce) { - - check(Content.Num() == Size); - - if (NewSize >= Size) { - /* Expand: add empty entries */ - for (int i = Size; i < NewSize; ++i) { - Content.Emplace(nullptr); - } - Size = NewSize; - return true; - } - else { - /* Shrink until arrive new size */ - if (bForce) { - while (Content.Num() > NewSize) - Content.RemoveAt(Content.Num() - 1); - Size = NewSize; - return true; - } - else { - /* Check there isn't any valid items */ - for (int i = NewSize; i < Size; ++i) { - if (Content[i] != nullptr && !Content[i]->IsEmpty()) - return false; - } - while (Content.Num() > NewSize) - Content.RemoveAt(Content.Num() - 1); - } - Size = NewSize; - return true; - } -} - -FNaItemContainerFindingReturn FNaItemContainer::Find(int Index) const { - - /* Out of size check */ - if (Index < 0 || Index > ITEM_CONTAINER_MAX_SIZE) - return FNaItemContainerFindingReturn(FNaItemContainerFindingReturn::NotFoundType::InvID); - - if (Index >= Size) - return FNaItemContainerFindingReturn(FNaItemContainerFindingReturn::NotFoundType::OOS); - - if (!Content[Index] || Content[Index]->IsEmpty()) // Entry is empty/null - return FNaItemContainerFindingReturn(FNaItemContainerFindingReturn::NotFoundType::Empty); - if (!Content[Index]->IsValid()) // Entry is invalid - return FNaItemContainerFindingReturn(FNaItemContainerFindingReturn::NotFoundType::InvVal); - - // Succeeded - return FNaItemContainerFindingReturn(Content[Index].Get()); -} - - - -bool FNaItemContainer::FindByType(UNaItemType* Type, TArray& Positions) { - - Positions.Empty(); - CheckSize(); - - for (int i = 0; i < Size; ++i) { - if (Content[i] && Content[i]->ItemType == Type) - Positions.Add(i); - } - return Positions.Num() != 0; -} - -void FNaItemContainer::ClearInvalid() { - for (int i = 0; i < Size; ++i) { - if (Content[i] && Content[i]->IsEmpty()) { - UE_LOG(LogNaItem, Warning, TEXT("NaItemContainer ClearInvalid: Empty item stack detected. Automatically cleared.")); - Content[i] = nullptr; - } - } -} - - -bool FNaItemContainer::AddEntry(int Position, UNaItemStack* Stack, bool bForce) { - - CheckSize(); - - // Check OOS - if (!IsInSize(Position)) { - UE_LOG(LogNaItem, Warning, TEXT("Add entry: out of size.")); - return false; - } - - // Case if index is empty - if (!Content[Position] || Content[Position]->IsEmpty()) { - Content[Position] = Stack; - return true; - } - - // Case if occupied, but force add - if (bForce) { - UE_LOG(LogNaItem, Log, TEXT("Add entry: an entry is replaced due to bForce.")); - Content[Position] = Stack; - return true; - } - - // Occupied, don't add - return false; -} - -void FNaItemContainer::RemoveEntry(int Position) { - - CheckSize(); - - // Check OOS - if (!IsInSize(Position)) { - UE_LOG(LogNaItem, Warning, TEXT("Remove entry: out of size.")); - return; - } - - Content[Position] = nullptr; -} - -bool FNaItemContainer::MoveEntry(int From, int To, bool bForce) { - - CheckSize(); - - // Check OOS - if (!IsInSize(From) || !IsInSize(To)) { - UE_LOG(LogNaItem, Warning, TEXT("Move entry: out of size.")); - return false; - } - - // Case when from is empty - if (!Content[From] || Content[From]->IsEmpty()) { - UE_LOG(LogNaItem, Log, TEXT("Move entry: trying to move empty entry. Use RemoveEntry() to empty a position.")); - return false; - } - - // Case when to is occupied - if (Content[To] && !Content[To]->IsEmpty()) { - if (bForce) { - Content[To] = Content[From]; - Content[From] = nullptr; - UE_LOG(LogNaItem, Log, TEXT("Move entry: an entry is overwritten due to bForce.")); - return true; - } - else { - UE_LOG(LogNaItem, Log, TEXT("Move entry: failed: trying to move into an occupied position.")); - return false; - } - } - // Ideal case - else { - Content[To] = Content[From]; - Content[From] = nullptr; - return true; - } -} - -void FNaItemContainer::SwapEntry(int P1, int P2) { - - CheckSize(); - - // Check OOS - if (!IsInSize(P1) || !IsInSize(P2)) { - UE_LOG(LogNaItem, Warning, TEXT("Swap entry: out of size.")); - return; - } - - TObjectPtr Temp = Content[P2]; - Content[P2] = Content[P1]; - Content[P1] = Temp; -} - -/*- Data-dependent Operations Below -*/ - -int FNaItemContainer::AddOrStack(int Position, UNaItemStack* Stack) { - - CheckSize(); - - if (!Stack || Stack->IsEmpty()) - return 0; - - // OOS case - if (!IsInSize(Position)) { - UE_LOG(LogNaItem, Warning, TEXT("Add or Stack: index out of size.")); - return Stack->Count; - } - - // Empty, add - if (!Content[Position] || Content[Position]->IsEmpty()) { - Content[Position] = Stack; - return 0; - } - - // Containing the same item type, stack - if (Content[Position]->CanStackWith(Stack, false)) { - return Stack->Count - Content[Position]->MergeFrom(Stack, true); - } - - // Mismatch, do nothing and return the input - return Stack->Count; -} - -int FNaItemContainer::GiveItem(UNaItemStack* Stack) { - - ClearInvalid(); - - if (!Stack || Stack->IsEmpty()) - return 0; - - int AmountLeft = Stack->Count; - UNaItemType* Type = Stack->ItemType; - int MaxStacking = Stack->GetMaxStackSize(); - - // Try stacking onto existing stacks - for (int i = 0; i < Size && AmountLeft > 0; ++i) { - if (!Content[i] || Content[i]->IsEmpty()) - continue; - if (Content[i]->ItemType != Type) - continue; - // Fully occupied, skip - if (Content[i]->IsFullStack()) - continue; - - int CanAdd = Content[i]->GetRemainingCapacity(); - int ToAdd = FMath::Min(CanAdd, AmountLeft); - Content[i]->Grow(ToAdd); - AmountLeft -= ToAdd; - } - - // Try adding to empty slots - for (int i = 0; i < Size && AmountLeft > 0; ++i) { - if (Content[i] && !Content[i]->IsEmpty()) - continue; - - int ToAdd = FMath::Min(AmountLeft, MaxStacking); - UNaItemStack* NewStack = UNaItemStack::CreateItemStack(nullptr, Type, ToAdd); - Content[i] = NewStack; - AmountLeft -= ToAdd; - } - - return AmountLeft; -} - -bool FNaItemContainer::CanGiveItemComplete(const UNaItemStack* Stack) const { - FNaItemContainer ContainerCopy(*this); - UNaItemStack* StackCopy = UNaItemStack::CopyItemStack(nullptr, Stack); - int res = ContainerCopy.GiveItem(StackCopy); - return res == 0; -} - -bool FNaItemContainer::GiveItemComplete(UNaItemStack* Stack) { - if (CanGiveItemComplete(Stack)) { - GiveItem(Stack); - return true; - } - else return false; -} - -bool FNaItemContainer::CanGiveItemMultiComplete(const TArray& Stacks) { - FNaItemContainer ContainerCopy(*this); - for (UNaItemStack* Stack : Stacks) { - if (!Stack || Stack->IsEmpty()) - continue; - UNaItemStack* StackCopy = UNaItemStack::CopyItemStack(nullptr, Stack); - int temp = ContainerCopy.GiveItem(StackCopy); - if (temp > 0) { - return false; - } - } - return true; -} - -ENaItemContainerUsageResult FNaItemContainer::PreUsageProcess(UObject* WorldContext, int Position, AActor* Source, AActor* Target) { - - if (!IsInSize(Position) || !Content[Position] || Content[Position]->IsEmpty()) - return ENaItemContainerUsageResult::ICUR_Empty; - - return ENaItemContainerUsageResult::ICUR_Succeeded; -} - -ENaItemContainerUsageResult FNaItemContainer::ExecuteUseItem(UObject* WorldContext, int Position, class AActor* Source, AActor* Target, ENaItemContainerUsageResult PreUsageResult) { - if (PreUsageResult == ENaItemContainerUsageResult::ICUR_Succeeded) { - bool bUsed = Content[Position]->Use(Source, Target); - return bUsed ? ENaItemContainerUsageResult::ICUR_Succeeded : ENaItemContainerUsageResult::ICUR_NotUsable; - } - else return PreUsageResult; -} - -ENaItemContainerUsageResult FNaItemContainer::PostUsageProcess(UObject* WorldContext, int Position, class AActor* Source, AActor* Target, ENaItemContainerUsageResult UsageResult) { - if (UsageResult == ENaItemContainerUsageResult::ICUR_Succeeded) { - // Try to consume one item - if (Content[Position] && !Content[Position]->IsEmpty()) { - Content[Position]->Consume(Source); - } - // If stack is empty after consumption, clear it - if (!Content[Position] || Content[Position]->IsEmpty()) - Content[Position] = nullptr; - } - return UsageResult; -} - -ENaItemContainerUsageResult FNaItemContainer::UseItem(UObject* WorldContext, int Position, AActor* Source, AActor* Target) { - ENaItemContainerUsageResult PreRes = PreUsageProcess(WorldContext, Position, Source, Target); - ENaItemContainerUsageResult UseRes = ExecuteUseItem(WorldContext, Position, Source, Target, PreRes); - ENaItemContainerUsageResult PostRes = PostUsageProcess(WorldContext, Position, Source, Target, UseRes); - return PostRes; -} - - - diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemEffect.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemEffect.cpp deleted file mode 100644 index a111241..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemEffect.cpp +++ /dev/null @@ -1,20 +0,0 @@ - -#include "NaItemEffect.h" - -ENaItemContainerUsageResult UNaItemEffect::ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) { - return ENaItemContainerUsageResult::ICUR_Failed; -} - -ENaItemContainerUsageResult UNaItemEffect::UseItem(UObject* WorldContext, int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) { - - // Source actor is not allowed to be null - checkf(IsValid(SourceActor), TEXT("UseItem error: Invalid source actor. Using item ID: %d"), ItemID); - - // ID == 0, empty item - if (ItemID == 0) { - return ENaItemContainerUsageResult::ICUR_Empty; - } - - return ItemEffect(ItemID, SourceActor, TargetActor, ItemPosition); - -} \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemInventory.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemInventory.cpp new file mode 100644 index 0000000..12bff94 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemInventory.cpp @@ -0,0 +1,381 @@ +#include "NaItemInventory.h" +#include "NatriumItemSystem.h" + +UNaItemInventory::UNaItemInventory() +{ +} + +int32 UNaItemInventory::GetSize() const +{ + return Slots.Num(); +} + +bool UNaItemInventory::IsValidSlot(int32 Slot) const +{ + return Slot >= 0 && Slot < Slots.Num(); +} + +UNaItemStack* UNaItemInventory::GetSlot(int32 Slot) const +{ + if (!IsValidSlot(Slot)) + { + return nullptr; + } + return Slots[Slot]; +} + +bool UNaItemInventory::IsSlotEmpty(int32 Slot) const +{ + if (!IsValidSlot(Slot)) + { + return true; + } + UNaItemStack* Stack = Slots[Slot]; + return !Stack || Stack->IsEmpty(); +} + +int32 UNaItemInventory::FindFirstEmptySlot() const +{ + for (int32 i = 0; i < Slots.Num(); ++i) + { + if (IsSlotEmpty(i)) + { + return i; + } + } + return -1; +} + +int32 UNaItemInventory::FindItemByType(const UNaItemType* Type, int32 StartFrom) const +{ + if (!Type) + { + return -1; + } + for (int32 i = FMath::Max(0, StartFrom); i < Slots.Num(); ++i) + { + UNaItemStack* Stack = Slots[i]; + if (Stack && !Stack->IsEmpty() && Stack->ItemType == Type) + { + return i; + } + } + return -1; +} + +int32 UNaItemInventory::CountItemByType(const UNaItemType* Type) const +{ + if (!Type) + { + return 0; + } + int32 Total = 0; + for (const UNaItemStack* Stack : Slots) + { + if (Stack && !Stack->IsEmpty() && Stack->ItemType == Type) + { + Total += Stack->Count; + } + } + return Total; +} + +void UNaItemInventory::SetSlot(int32 Slot, UNaItemStack* Stack) +{ + if (!IsValidSlot(Slot)) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventory::SetSlot: Invalid slot %d"), Slot); + return; + } + if (Stack && Stack->IsEmpty()) + { + Slots[Slot] = nullptr; + } + else + { + Slots[Slot] = Stack; + } +} + +void UNaItemInventory::ClearSlot(int32 Slot) +{ + if (!IsValidSlot(Slot)) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventory::ClearSlot: Invalid slot %d"), Slot); + return; + } + Slots[Slot] = nullptr; +} + +void UNaItemInventory::SwapSlots(int32 SlotA, int32 SlotB) +{ + if (!IsValidSlot(SlotA) || !IsValidSlot(SlotB)) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventory::SwapSlots: Invalid slot(s) %d, %d"), SlotA, SlotB); + return; + } + Slots.SwapMemory(SlotA, SlotB); +} + +bool UNaItemInventory::MergeSlots(int32 SourceSlot, int32 DestSlot) +{ + if (!IsValidSlot(SourceSlot) || !IsValidSlot(DestSlot)) + { + return false; + } + UNaItemStack* Source = Slots[SourceSlot]; + UNaItemStack* Dest = Slots[DestSlot]; + + if (!Source || Source->IsEmpty()) + { + return false; + } + + if (!Dest || Dest->IsEmpty()) + { + // Move entire source to dest slot + Slots[DestSlot] = Source; + Slots[SourceSlot] = nullptr; + return true; + } + + const int32 Moved = Dest->MergeFrom(Source, true); + if (Moved > 0 && Source->IsEmpty()) + { + Slots[SourceSlot] = nullptr; + } + return Moved > 0; +} + +int32 UNaItemInventory::GiveItem(UNaItemStack* Stack) +{ + if (!Stack || Stack->IsEmpty()) + { + return 0; + } + + // First pass: merge into existing stacks of the same type + int32 StartSearch = 0; + while (Stack->Count > 0) + { + const int32 SlotIdx = FindItemByType(Stack->ItemType, StartSearch); + if (SlotIdx == -1) + { + break; + } + UNaItemStack* ExistingStack = Slots[SlotIdx]; + if (ExistingStack && !ExistingStack->IsFullStack() && ExistingStack->CanStackWith(Stack, true)) + { + ExistingStack->MergeFrom(Stack, true); + } + StartSearch = SlotIdx + 1; + } + + // Second pass: place remainder into empty slots + while (Stack->Count > 0) + { + const int32 EmptySlot = FindFirstEmptySlot(); + if (EmptySlot == -1) + { + break; + } + + const int32 MaxSize = Stack->GetMaxStackSize(); + if (Stack->Count <= MaxSize) + { + // All remaining items fit in one slot + Slots[EmptySlot] = UNaItemStack::CreateItemStack(this, Stack->ItemType, Stack->Count); + Stack->SetCount(0); + } + else + { + // Fill a full stack in this slot + Slots[EmptySlot] = UNaItemStack::CreateItemStack(this, Stack->ItemType, MaxSize); + Stack->Shrink(MaxSize); + } + } + + return Stack->Count; +} + +bool UNaItemInventory::CanGiveItemComplete(const UNaItemStack* Stack) const +{ + if (!Stack || Stack->IsEmpty()) + { + return true; + } + + int32 Remaining = Stack->Count; + + // Count space in existing stacks + for (const UNaItemStack* ExistingStack : Slots) + { + if (Remaining <= 0) + { + break; + } + if (ExistingStack && !ExistingStack->IsEmpty() && ExistingStack->CanStackWith(Stack, true)) + { + Remaining -= ExistingStack->GetRemainingCapacity(); + } + } + + if (Remaining <= 0) + { + return true; + } + + // Count available empty slots + const int32 MaxSize = Stack->GetMaxStackSize(); + for (const UNaItemStack* SlotEntry : Slots) + { + if (Remaining <= 0) + { + break; + } + if (!SlotEntry || SlotEntry->IsEmpty()) + { + Remaining -= MaxSize; + } + } + + return Remaining <= 0; +} + +bool UNaItemInventory::GiveItemComplete(UNaItemStack* Stack) +{ + if (!Stack || Stack->IsEmpty()) + { + return true; + } + + if (!CanGiveItemComplete(Stack)) + { + return false; + } + + GiveItem(Stack); + return true; +} + +int32 UNaItemInventory::ConsumeItemByType(UNaItemType* Type, int32 Amount) +{ + if (!Type || Amount <= 0) + { + return 0; + } + + int32 Remaining = Amount; + for (int32 i = 0; i < Slots.Num() && Remaining > 0; ++i) + { + UNaItemStack* Stack = Slots[i]; + if (!Stack || Stack->IsEmpty() || Stack->ItemType != Type) + { + continue; + } + const int32 ToRemove = FMath::Min(Remaining, Stack->Count); + Stack->Shrink(ToRemove); + Remaining -= ToRemove; + if (Stack->IsEmpty()) + { + Slots[i] = nullptr; + } + } + + return Amount - Remaining; +} + +bool UNaItemInventory::CanConsumeItemByType(const UNaItemType* Type, int32 Amount) const +{ + return CountItemByType(Type) >= Amount; +} + +UNaItemStack* UNaItemInventory::TakeFromSlot(int32 Slot, int32 Amount) +{ + if (!IsValidSlot(Slot) || Amount <= 0) + { + return nullptr; + } + + UNaItemStack* Stack = Slots[Slot]; + if (!Stack || Stack->IsEmpty()) + { + return nullptr; + } + + const int32 ActualAmount = FMath::Min(Amount, Stack->Count); + UNaItemStack* NewStack = UNaItemStack::CreateItemStack(this, Stack->ItemType, ActualAmount); + Stack->Shrink(ActualAmount); + if (Stack->IsEmpty()) + { + Slots[Slot] = nullptr; + } + return NewStack; +} + +bool UNaItemInventory::Resize(int32 NewSize, bool bForce) +{ + if (NewSize < 0) + { + UE_LOG(LogNaItem, Warning, TEXT("UNaItemInventory::Resize: NewSize %d is negative"), NewSize); + return false; + } + + const int32 OldSize = Slots.Num(); + if (NewSize == OldSize) + { + return true; + } + + if (NewSize < OldSize) + { + // Check for occupied slots in the range to be removed + if (!bForce) + { + for (int32 i = NewSize; i < OldSize; ++i) + { + if (!IsSlotEmpty(i)) + { + UE_LOG(LogNaItem, Warning, + TEXT("UNaItemInventory::Resize: Slot %d is occupied; use bForce to force shrink"), i); + return false; + } + } + } + Slots.SetNum(NewSize); + } + else + { + // Growing — add null slots + Slots.SetNum(NewSize); + } + + return true; +} + +void UNaItemInventory::ClearAll() +{ + for (int32 i = 0; i < Slots.Num(); ++i) + { + Slots[i] = nullptr; + } +} + +void UNaItemInventory::ClearEmptyStacks() +{ + for (int32 i = 0; i < Slots.Num(); ++i) + { + UNaItemStack* Stack = Slots[i]; + if (Stack && Stack->IsEmpty()) + { + Slots[i] = nullptr; + } + } +} + +UNaItemInventory* UNaItemInventory::CreateInventory(UObject* Outer, int32 Size) +{ + UNaItemInventory* Inv = NewObject(Outer ? Outer : GetTransientPackage()); + Inv->Slots.SetNum(FMath::Max(0, Size)); + return Inv; +} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemType.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemType.cpp index 83a0938..e71a8cd 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemType.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/NaItemType.cpp @@ -129,4 +129,12 @@ TOptional UNaItemType::GetOrUpdateName() } return TOptional(); -} \ No newline at end of file +} + +UNaItemStack* UNaItemType::CreateDefaultInstance() const +{ + UNaItemStack* Stack = NewObject(GetTransientPackage()); + Stack->ItemType = const_cast(this); + Stack->Count = 1; + return Stack; +} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp new file mode 100644 index 0000000..d09289e --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp @@ -0,0 +1,7 @@ +// By Sodium + + +#include "UI/NaItemInventoryWidget.h" + + +// Add default functionality here for any INaItemInventoryWidget functions that are not pure virtual. diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp index eaa231c..d0fedeb 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp @@ -1,7 +1,6 @@ #include "UI/UMG/NaItemSlotList.h" #include "UI/Widgets/SNaItemSlotList.h" -#include "Components/NaItemContainerComponent.h" -#include "NaItemContainer.h" +#include "Components/NaItemInventoryComponent.h" #include "NatriumItemSystem.h" #include "NaUtilityMinimal.h" @@ -36,7 +35,7 @@ TSharedRef UNaItemSlotList::RebuildWidget(){ } -void UNaItemSlotList::SetContainerComponent(UNaItemContainerComponent* NewComponent) { +void UNaItemSlotList::SetContainerComponent(UNaItemInventoryComponent* NewComponent) { ContainerComponent = NewComponent; if(List.IsValid()) List->ResetContainer(NewComponent); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp index 02ccf1c..5d6e906 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp @@ -106,7 +106,7 @@ void SNaItemSlot::SetDisabled(bool NewDisabledState) { void SNaItemSlot::BindItemSlotListEvents() { // If in item slot list, bind mouse events - if (ItemSlotList && !ItemSlotList->IsInvalid() && ItemSlotList->GetContainer()->Container.IsInSize(PositionInSlotList) && BoxSlot.IsValid()) { + if (ItemSlotList && !ItemSlotList->IsInvalid() && ItemSlotList->GetContainer()->Inventory->IsValidSlot(PositionInSlotList) && BoxSlot.IsValid()) { BoxSlot->OnPointed.BindSP(this, &SNaItemSlot::SlotPointedToList); BoxSlot->OnUnpointed.BindSP(this, &SNaItemSlot::SlotUnpointedToList); BoxSlot->OnSelected.BindSP(this, &SNaItemSlot::SlotSelectedToList); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp index 873ae6f..7405142 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp @@ -5,7 +5,7 @@ #include "BPLibraries/NaItemStatics.h" #include "BPLibraries/NaItemDataStatics.h" #include "Widgets/Layout/SWrapBox.h" -#include "Components/NaItemContainerComponent.h" +#include "Components/NaItemInventoryComponent.h" #include "UI/UMG/NaItemSlotList.h" #include "Components/NaGameModeItemSystemComponent.h" @@ -80,29 +80,32 @@ void SNaItemSlotList::Reconstruct() { // Start rebuild if (!bIsInvalid) { + UNaItemInventory* Inv = Container->GetInventory(); + const int32 ContainerSize = Inv->GetSize(); + /* Calculate actual length */ // When row count is applied - if (RowCount * RowLength > Container->Container.GetSize()) { + if (RowCount * RowLength > ContainerSize) { ActualLength = RowCount * RowLength; } // When additional disabled slots are not needed - else if(!bFillDisabledToCompleteRectangle || Container->Container.GetSize() % RowLength == 0) { - ActualLength = Container->Container.GetSize(); + else if(!bFillDisabledToCompleteRectangle || ContainerSize % RowLength == 0) { + ActualLength = ContainerSize; } // When disabled slots are needed and actual length needs to be automatically calculated (no rows with all disabled) else { - ActualLength = (int(Container->Container.GetSize() / RowLength) + 1) * RowLength; + ActualLength = (int(ContainerSize / RowLength) + 1) * RowLength; } // Re-add child slots int i = 0; Slots.Init(TSharedPtr(nullptr), ActualLength); // Add enabled slots first - for (i = 0; i < Container->Container.GetSize(); ++i) { + for (i = 0; i < ContainerSize; ++i) { WrapBox->AddSlot()[ SAssignNew(Slots[i], SNaItemSlot) .StylePtr(StylePtr) .WorldContext(GMComponent) - .Stack(Container->Container.Find(i).Stack) + .Stack(Inv->GetSlot(i)) ]; } // Then fill with disabled slots @@ -143,14 +146,14 @@ bool SNaItemSlotList::IsUpdated(bool bDisplay) { } // Check size - if (Slots.Num() != Container->Container.GetSize()) { + if (Slots.Num() != Container->GetInventory()->GetSize()) { UE_LOG(LogNaItem, Display, TEXT("SNaItemSlotList: incorrect size.")); return false; } // Check each slots for (i = 0; i < Slots.Num(); ++i) { - if (Slots[i]->GetStack() != Container->Container.Find(i).Stack) { + if (Slots[i]->GetStack() != Container->GetInventory()->GetSlot(i)) { if(bDisplay) UE_LOG(LogNaItem, Display, TEXT("SNaItemSlotList: position %d is not updated."), i); Res = false; @@ -167,31 +170,31 @@ void SNaItemSlotList::ResetSlot(int Position) { if (IsInvalid()) return; - if (Container->Container.IsInSize(Position)) { + if (!Container->GetInventory()->IsValidSlot(Position)) { UE_LOG(LogNaItem, Warning, TEXT("SNaItemSlotList::ResetSlot() failed: position out of range.")); return; } - Slots[Position]->ResetItemStack(Container->Container.Find(Position).Stack); + Slots[Position]->ResetItemStack(Container->GetInventory()->GetSlot(Position)); } void SNaItemSlotList::ResetAllSlots() { int i = 0; for (i = 0; i < Slots.Num(); ++i) { - Slots[i]->ResetItemStack(Container->Container.Find(i).Stack); + Slots[i]->ResetItemStack(Container->GetInventory()->GetSlot(i)); } } -void SNaItemSlotList::ResetContainer(UNaItemContainerComponent* NewContainer) { +void SNaItemSlotList::ResetContainer(UNaItemInventoryComponent* NewContainer) { Container = NewContainer; Reconstruct(); } void SNaItemSlotList::SelectSlot(int Position) { if (!IsValid(Container)) return; - check(Container->Container.IsInSize(Position)); + check(Container->GetInventory()->IsValidSlot(Position)); if (SelectedPosition >= 0) { Slots[SelectedPosition]->GetBoxSlot()->SetSelected(false); } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Actors/NaDroppedItem.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Actors/NaDroppedItem.h index d7674aa..fc70d41 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Actors/NaDroppedItem.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Actors/NaDroppedItem.h @@ -1,3 +1,4 @@ + // By Sodium #pragma once diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemDataStatics.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemDataStatics.h index 230f6f2..549146c 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemDataStatics.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemDataStatics.h @@ -2,7 +2,6 @@ #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" -#include "../Data/NaItemEffectdata.h" #include "../NaItemType.h" #include "NaItemDataStatics.generated.h" diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemStatics.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemStatics.h index f68badb..210db2d 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemStatics.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/BPLibraries/NaItemStatics.h @@ -2,7 +2,6 @@ #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" -#include "NaItemContainer.h" #include "NaItemStack.h" #include "NaItemStatics.generated.h" @@ -22,83 +21,8 @@ class NAITEMSYSTEM_API UNaItemStatics :public UBlueprintFunctionLibrary { public: - /*** NaItemContainer ***/ - - /*--- Get functions ---*/ - - // Get max supported item container size. To set this value, reset the macro ITEM_CONTAINER_MAX_SIZE in NaItemContainer.h file. - UFUNCTION(BlueprintPure, Category = "NaItemSystem|ItemContainer") - static int GetItemContainerSizeLimit() { return ITEM_CONTAINER_MAX_SIZE; }; - - // Get the current container size. - UFUNCTION(BlueprintPure, Category = "NaItemSystem|ItemContainer") - static int GetContainerSize(const FNaItemContainer & Target) { return Target.GetSize(); }; - - // Check if an index is in the container size. Note: container indeces start from 0 just like arrays. - UFUNCTION(BlueprintPure, Category = "NaItemSystem|ItemContainer") - static bool IsInContainerSize(const FNaItemContainer & Target, int Index) { return Target.IsInSize(Index); }; - - /** - * Find item stack from a given index. - * @Param ReturnType Type of finding result, e.g. found, empty, errors, etc. - * @Param Result Item stack found. If not found (empty or wrong index), it will be null. - */ - UFUNCTION(BlueprintPure, Category = "NaItemSystem|ItemContainer") - static void FindEntryFromIndex(const FNaItemContainer & Target, int Position, ENaItemContainerFindingResult & ReturnType, UNaItemStack*& Result); - - /*-- Operations --*/ - - /** - * Resize a container. - * @Param bForce If true, when shrinking, it will force shrinking, deleting the item entries in the shrinked positions. - * @ReturnValue Whether resizing succeeded. - */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static bool ResizeContainer(UPARAM(ref) FNaItemContainer & Target, int NewSize, bool bForce); - - /** - * Add item stack to container. - * @Param bForce If true, it will replace the existing entry if position to add is occupied. Or it will fail. - * @ReturnValue Whether adding succeeded. - */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static bool AddItemEntry(UPARAM(ref) FNaItemContainer & Target, int Position, UNaItemStack* Stack, bool bForce = false); - - /** - * Add item stack or stack onto the same entry. - * If the position is empty, add into it; if the position is occupied with the same item type, stack on it. - * Warning: If the target container is not valid, return -1. - * @ReturnValue Amount of items remained not added. - */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static int AddOrStackItemEntry(UPARAM(ref) FNaItemContainer & Target, int Position, UNaItemStack* Stack); - - /** - * Remove an entry of given position in a container. - */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static void RemoveItemEntry(UPARAM(ref) FNaItemContainer & Target, int Position); - - /** - * Move an item entry to another position. - * @Param bForce If true, the target position will be overwritten if occupied. - */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static bool MoveItemEntry(UPARAM(ref) FNaItemContainer & Target, int From, int To, bool bForce = false); - - /* Swap two entries. This action will not fail if no Out Of Size. */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - static void SwapItemEntry(UPARAM(ref) FNaItemContainer & Target, int P1, int P2); - /*- Item System game mode component related -*/ UFUNCTION(BlueprintCallable, Category = "NaItemSystem|GameMode") static UNaGameModeItemSystemComponent * GetGameModeItemSystemComponent(UObject* WorldContext); - - - /*-- Item usage --*/ -// UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Usage") -// static - - }; \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemContainerComponent.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemContainerComponent.h deleted file mode 100644 index 0235a38..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemContainerComponent.h +++ /dev/null @@ -1,54 +0,0 @@ -// By Sodium - -#pragma once - -#include "CoreMinimal.h" -#include "Components/ActorComponent.h" -#include "NaItemStack.h" -#include "NaItemContainer.h" -#include "NaItemContainerComponent.generated.h" - -/** NaItemContainerComponent is a component bringing an item container for attachment to actors. -*/ -UCLASS( ClassGroup=(NaItemSystem), meta=(BlueprintSpawnableComponent), Blueprintable ) -class NAITEMSYSTEM_API UNaItemContainerComponent : public UActorComponent -{ - GENERATED_BODY() - -public: - // Sets default values for this component's properties - UNaItemContainerComponent(); - - virtual void InitializeComponent() override; - -protected: - // Called when the game starts - virtual void BeginPlay() override; - -public: - // Called every frame - virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - - /* Initial size */ - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "ItemContainer|ContainerInitialization") - int InitSize = 64; - - /* If true, container will be initialized with InitializerContent */ - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "ItemContainer|ContainerInitialization") - bool bInitFromInitializer = true; - - /* Initializer if bInitFromInitializer is true. Key integer = index. Indexes out of size will be ignored. */ - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "ItemContainer|ContainerInitialization") - TMap Initializer; - - UPROPERTY(BlueprintReadWrite) - FNaItemContainer Container; - - /* Usage */ - - /* Use item once from owning actor */ - UFUNCTION(BlueprintCallable, Category = "NaItemSystem|ItemContainer") - ENaItemContainerUsageResult UseItemFromSelf(int Position, AActor* Target); - - -}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemInventoryComponent.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemInventoryComponent.h new file mode 100644 index 0000000..83f2b88 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Components/NaItemInventoryComponent.h @@ -0,0 +1,43 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "NaItemInventory.h" +#include "NaItemInventoryComponent.generated.h" + +/** + * UNaItemInventoryComponent - ActorComponent that owns and exposes a UNaItemInventory. + */ +UCLASS(ClassGroup=(NaItemSystem), meta=(BlueprintSpawnableComponent), Blueprintable, BlueprintType) +class NAITEMSYSTEM_API UNaItemInventoryComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UNaItemInventoryComponent(); + + virtual void InitializeComponent() override; + + /** Initial inventory size */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "NaItemSystem|Inventory") + int32 InitSize = 64; + + /** The owned inventory */ + UPROPERTY(BlueprintReadOnly, Category = "NaItemSystem|Inventory") + UNaItemInventory* Inventory; + + /** Get the owned inventory */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + UNaItemInventory* GetInventory() const; + + /***** Convenience delegates to inventory *****/ + + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + UNaItemStack* GetSlot(int32 Slot) const; + + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + int32 GiveItem(UNaItemStack* Stack); + + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + int32 ConsumeItemByType(UNaItemType* Type, int32 Amount); +}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemDisplayData.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemDisplayData.h deleted file mode 100644 index cdb77d3..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemDisplayData.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Engine/DataTable.h" -#include "Styling/SlateTypes.h" -#include "NaItemDisplayData.generated.h" - - - -/** -* This is the item data applied by UI. -* -*/ -USTRUCT(BlueprintType) -struct NAITEMSYSTEM_API FNaItemDisplayData : public FTableRowBase { - - GENERATED_BODY() - -public: - - /* Image applied to display an item in container UI. */ - UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (AllowedClasses = "/Script/Engine.Texture, /Script/Engine.MaterialInterface, /Script/Engine.SlateTextureAtlasInterface", DisallowedClasses = "/Script/MediaAssets.MediaTexture")) - UObject* BrushImage = nullptr; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString DefaultDescription = TEXT("This is an item."); - - - - -}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemEffectData.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemEffectData.h deleted file mode 100644 index 789f07f..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/Data/NaItemEffectData.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "Engine/DataTable.h" -#include "../NaItemEffect.h" -#include "NaItemEffectData.generated.h" - -// Data table row struct defining usage effect of item -USTRUCT(BlueprintType) -struct NAITEMSYSTEM_API FNaItemEffectData : public FTableRowBase { - - GENERATED_BODY() - -public: - - /* Usage effect function is called only when this value is true. When false, the usage effect function will be skipped. - * Set false for avoiding unexpected usage or errors. - */ - UPROPERTY(BlueprintReadWrite, EditAnywhere) - bool bCanUse = false; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - TSubclassOf EffectClass = UNaItemEffect::StaticClass(); - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - ENaItemUsageConsumptionType ConsumptionType = ENaItemUsageConsumptionType::IUCT_One; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - int IntParam = -1; - -public: - - -}; \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemContainer.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemContainer.h deleted file mode 100644 index d581857..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemContainer.h +++ /dev/null @@ -1,208 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "NaItemStack.h" -#include "NaItemEffect.h" -#include "NaItemContainer.generated.h" - -// This value is intended to avoid integer overflow. -#define ITEM_CONTAINER_MAX_SIZE 10000 - -class UNaGameModeItemSystemComponent; - - -/* Result types for browsing an item container */ -UENUM() -enum class ENaItemContainerFindingResult :uint8 { - ICFR_Filled UMETA(DisplayName = "Filled"), // Browsed successfully, and the socket is containing valid item(s). - ICFR_Empty UMETA(DisplayName = "Empty"), // Browsed successfully, and the socket is empty. - ICFR_OOS UMETA(DisplayName = "Out of Size"), // Trying to browse at the position out of the max size - /* The above values are correct results */ - /* The below values are errors that should not appear */ - ICFR_InvID UMETA(DisplayName = "Invalid Index"), // Trying to browse at invalid position (negative or out of ITEM_CONTAINER_MAX_SIZE defined in NaItemManager.h) - ICFR_InvVal UMETA(DisplayName = "Invalid Item Record"), // Found record correctly, but the item record itself is invalid (containing something wrong) - ICFR_Error UMETA(DisplayName = "Error") // Unexpected error -}; - - -/* Return values for Searching in a container */ -struct FNaItemContainerFindingReturn { - // Values - UNaItemStack* Stack; - ENaItemContainerFindingResult Result; - - // Initializer when not found - enum class NotFoundType :uint8 { - Empty, OOS, InvID, InvVal, Error - }; - - // Found item successfully - FNaItemContainerFindingReturn(UNaItemStack* InStack); - - // Not found - FNaItemContainerFindingReturn(NotFoundType); - - // Copy - FNaItemContainerFindingReturn(const FNaItemContainerFindingReturn& Copy) : - Stack(Copy.Stack), - Result(Copy.Result) - {}; -}; - -UENUM() -enum class ENaItemContainerAddingCheckResult { - ICACR_CanAdd, - ICACR_CanPartlyAdd, - ICACR_CannotAdd - - -}; - - -/* Structure that contains a series of items. -* It can be used to describe a bag, shop, etc. -*/ -USTRUCT(Blueprintable) -struct NAITEMSYSTEM_API FNaItemContainer { - - GENERATED_BODY() - -protected: - - /* Content as an array of item stacks. Null entries represent empty slots. */ - UPROPERTY() - TArray> Content; - - /* Max size */ - int Size = 64; - - - -// Check if Content.Num() == Size. CRASH when failed. -#define CheckSize() checkf(Content.Num() == Size, TEXT("NaItemContainer Error: Container size != Content array actual size.")) - - -public: - - /***** Constructors *****/ - - // Make an empty container. Default size is 64. - FNaItemContainer(); - - // Make empty container with given size. - FNaItemContainer(int InSize); - - // Copy from other. - FNaItemContainer(const FNaItemContainer& CopyFrom); - - // Make from initial content map and size - FNaItemContainer(int InSize, const TMap& InitialContent); - - /** Resize the container. - * @Param bForce If true, when shrinked area contains items, it will ignore them (causing the items lost!). Or it will fail if shrinked area contains items. - */ - bool Resize(int NewSize, bool bForce = false); - - /*----------------------------------------------------*/ - /** Get functions **/ - - // Get the size. - FORCEINLINE int GetSize() const { return Size; }; - - // Check if a position is in size. Please note that the valid values is [0,Size) just like an array. - FORCEINLINE bool IsInSize(int Position) const { return Position >= 0 && Position < Size; }; - - // Get entry from index - FNaItemContainerFindingReturn Find(int Index) const; - - /** Get all positions that contain items of a given type. - * Return whether there are any. - * @Param Type The item type to search for. - * @Param Positions Return positions containing the item of given type. - */ - bool FindByType(UNaItemType* Type, TArray& Positions); - - - /** Operations **/ - - /** Add item stack at position. - * If the container position is occupied, the values will not be added unless bForce == true. - * Return whether succeeded. - */ - bool AddEntry(int Position, UNaItemStack* Stack, bool bForce = false); - - - /* Remove an item from position. */ - void RemoveEntry(int Position); - - /* Move an item entry to another position. - * @Param bForce If true, the "from" entry will overwrite the "to" entry if the latter is occupied. - * @ReturnValue Whether moved successfully. Fail if moving from an empty position or to an occupied position without bForce == true. - */ - bool MoveEntry(int From, int To, bool bForce = false); - - /* Swap two entries. This action will not fail if no Out Of Size. */ - void SwapEntry(int P1, int P2); - - - /** Maintainance **/ - - // Find all positions with invalid/empty stacks and reset them to null - void ClearInvalid(); - - - - - /*----- Data-dependent Operations ------*/ - - /** Add or stack items to a position. - * If the position is empty, add stack. If the position contains the same item type, stack on it. Or fail. - * @ReturnValue Amount that cannot be added. If failed, return the total amount of input stack. - */ - int AddOrStack(int Position, UNaItemStack* Stack); - - - /* --- Operations not specifying positions --- */ - - /** Give a stack to the container, not specifying the position. - * @ ReturnValue Amount that cannot be added. If failed, return the total amount of input stack. - **/ - int GiveItem(UNaItemStack* Stack); - - /** GiveItem function that executes only when all items can be given. - * @ ReturnValue Whether succeeded. - **/ - bool GiveItemComplete(UNaItemStack* Stack); - - /** Check if a stack can be completely added to the container. - * This function doesn't really add items. - **/ - bool CanGiveItemComplete(const UNaItemStack* Stack) const; - - /** Check if multiple stacks can all be completely added. - * This function doesn't really add items. - **/ - bool CanGiveItemMultiComplete(const TArray& Stacks); - - - - /*--- Item Usage ---*/ - - // Check if the container is correct and item can be used before usage; actions before usage - virtual ENaItemContainerUsageResult PreUsageProcess(UObject* WorldContext, int Position, class AActor* Source, AActor* Target); - - // Action of usage of item itself - virtual ENaItemContainerUsageResult ExecuteUseItem(UObject* WorldContext, int Position, class AActor* Source, AActor* Target, ENaItemContainerUsageResult PreUsageResult); - - // Actions after usage - virtual ENaItemContainerUsageResult PostUsageProcess(UObject* WorldContext, int Position, class AActor* Source, AActor* Target, ENaItemContainerUsageResult UsageResult); - - // Function actually called for usage - virtual ENaItemContainerUsageResult UseItem(UObject* WorldContext, int Position, class AActor* Source, AActor* Target); - - - virtual ~FNaItemContainer() {}; -}; - - - diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemEffect.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemEffect.h deleted file mode 100644 index 4b7fa05..0000000 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemEffect.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/Actor.h" -#include "NaItemEffect.generated.h" - -// Types of consumption on item usage. -// Consumption needs additionally an int (IntParam) and a NaItemEntry array (EntryArray). -UENUM(BlueprintType) -enum class ENaItemUsageConsumptionType :uint8 { - - // Consume one item - IUCT_One UMETA(DisplayName = "Consuming One"), - - // Consume more than 1 items - // IntParam - Consuming amount - IUCT_Multi UMETA(DisplayName = "Consuming Multiple"), - - // No consumption - IUCT_None UMETA(DisplayName = "No Consumption"), - - // Consume the item and replace the emptied slot with another item. - // Applied on e.g. equipments, replacing the equipment with player's wearing one - // Only applied on items not stackable (i.e. MaxStackingAmount == 1). - // EntryArray[0] - Item entry to replace - IUCT_Replace_NOT_IMPLEMENTED UMETA(DisplayName = "Replacing NOT IMPLEMENTED"), - - // Consume not this but other items, e.g. recipes - // EntryArray - Other items to consume - IUCT_Other_NOT_IMPLEMENTED UMETA(DisplayName = "Consuming Other Items NOT IMPLEMENTED"), - - // Consume other items and this item - // EntryArray - Other items to consume - IUCT_OtherAndThis_NOT_IMPLEMENTED UMETA(DisplayName = "Consuming Other Items and This NOT IMPLEMENTED"), - - // Consume other items and multiple this items - // IntParam - Consuming amount - // EntryArray - Other items to consume - IUCT_OtherAndMultiThis_NOT_IMPLEMENTED UMETA(DisplayName = "Consuming Other Items and Multiple This NOT IMPLEMENTED"), - - // Consume one this and get other items - // EntryArray - Other items to get - IUCT_GetOther_NOT_IMPLEMENTED UMETA(DisplayName = "Consuming This and Get Other Items NOT IMPLEMENTED"), - - // Get other item without consuming this - // EntryArray - Other items to get - IUCT_GetOtherNoConsumption_NOT_IMPLEMENTED UMETA(DisplayName = "Getting Other Items without Consumption NOT IMPLEMENTED"), - - // Consume multiple this and get other items - // IntParam - Consuming amount - // EntryArray - Other items to get - IUCT_GetOtherConsumingMulti_NOT_IMPLEMENTED UMETA(DisplayName = "Consuming Multiple This and Get Other Items NOT IMPLEMENTED"), - - // Custom type. Should be defined in corresponding item effect - IUCT_Custom UMETA(DisplayName = "Custom") -}; - - -// When item is used from a container, the usage result -UENUM(BlueprintType) -enum class ENaItemContainerUsageResult :uint8 { - - /*------------- Errors -------------*/ - - // Unidentified failure - ICUR_Failed UMETA(Display = "UnidentifiedFailure"), - - // Internal error, usually from C++ code - ICUR_Error UMETA(Display = "InternalError"), - - // Using items undefined in data table - ICUR_Invalid UMETA(Display = "UsingItemWithInvalidIndex"), - - /*------------- Succeeded -------------*/ - - // Usage succeeded - ICUR_Succeeded UMETA(DisplayName = "Succeeded"), - - - /*------------- Normal failures from item properties -------------*/ - /*------------- index starts from 10 -------------*/ - - // Item to use is defined non-usable in Effect Data Table - ICUR_NotUsable = 10 UMETA(DisplayName = "NotUsable"), - - // Attempting to use an empty item - ICUR_Empty UMETA(DisplayName = "UsingEmptyItem"), - - - /*------------- Normal failures from container -------------*/ - /*------------- index start from 20 -------------*/ - // The item to use is not enough - ICUR_NoEnoughItem = 20 UMETA(DisplayName = "NoEnoughItem"), - - // The item to use is enough, but additional other items to consume are not enough - ICUR_NoEnoughAdditionalItems UMETA(DisplayName = "NoEnoughOtherItems"), - - // The item will give another items to the container, but the space of container is not enough - ICUR_NoEnoughSpace UMETA(DisplayName = "NoEnoughContainerSpace"), - - - /*------------- Custom failure types -------------*/ - /*------------- index start from 100 -------------*/ - - ICUR_CustomFailure0 = 100 UMETA(DisplayName = "Custom Failure Type 0"), - ICUR_CustomFailure1 UMETA(DisplayName = "Custom Failure Type 1"), - ICUR_CustomFailure2 UMETA(DisplayName = "Custom Failure Type 2"), - ICUR_CustomFailure3 UMETA(DisplayName = "Custom Failure Type 3"), - ICUR_CustomFailure4 UMETA(DisplayName = "Custom Failure Type 4"), - ICUR_CustomFailure5 UMETA(DisplayName = "Custom Failure Type 5"), - ICUR_CustomFailure6 UMETA(DisplayName = "Custom Failure Type 6"), - ICUR_CustomFailure7 UMETA(DisplayName = "Custom Failure Type 7"), - ICUR_CustomFailure8 UMETA(DisplayName = "Custom Failure Type 8"), - ICUR_CustomFailure9 UMETA(DisplayName = "Custom Failure Type 9") - -}; - - -/* Effect class when using items */ -// Server only -UCLASS(Blueprintable, BlueprintType) -class NAITEMSYSTEM_API UNaItemEffect :public UObject { - - GENERATED_BODY() - -public: - - /* Item effect applied. - * When the item type can be used, return Succeeded without considering the container status. - * Usage in the container will double check its usability, and is not defined here. - * @Param ItemID Item ID to be used. - * @Param SourceActor Actor which uses the item. - * @Param TargetActor Actor as target of the item. - * @Param ItemPosition If the item comes from an item container, its position in the container. -1 means disabled. - * @ReturnValue Usage result, including whether successful, how many consumed. - * For tutorial and warnings, see NaItemEffect.h - **/ - UFUNCTION(BlueprintNativeEvent, DisplayName = "ItemEffect", Category = "NaItemSystem|ItemEffect") - ENaItemContainerUsageResult ItemEffect(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition = -1); - virtual ENaItemContainerUsageResult ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition = -1); - -public: - - ENaItemContainerUsageResult UseItem(UObject* WorldContext, int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition); - -}; - -/* ItemEffect setting tutorial -* Item ID is the item expected to use. This means multiple items can be defined in a single effect class. -* SourceActor is the item used from (usually the player, must be valid). Invalid source actor triggers assert. Keep in mind that what exactly the source actor is, player controller or pawn. -* TargetActor is the item used toward. E.g. Other players, monsters, etc. Optional. (Caution for nullptr error!) -* Position: Only for when the item is used from a container. This input is applied to define position-related behaviors. Also, this value can serve as an additional parameter, not limited in the container position. -* return: Whether this item type is usable, and why it's not usable. DO NOT CONSIDER CONTAINER!! -*/ - -/** Example - If we want to implement an HP potion recovering 50 HP each use, as ID == 10 - - In ItemEffect override (for BP, override ItemEffect()): -class UMyEffect: public UNaItemEffect -{ - //.... - virtual ENaItemContainerUsageResult ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) override - { - switch (ItemID){ - //.... - case 10: - { - Cast SourceActor->HP += 50; // Usage behavior here - return ENaItemContainerUsageResult::Succeeded; // Return succeeded because this item is usable - } - //.... - }; - //.... -}; - - In Item Effect Data Table: - ID: 0000010, - EffectClass: UMyEffect, // Browsed in editor here - ConsumptionType: ConsumingOne, // Set here and consumption will be executed in container's using item - IntParam: -1, // ConsumingOne has no int param input, so it will not be applied - TypeArray: {} // ConsumingOne has no type array input, so it will not be applied -*/ \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemInventory.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemInventory.h new file mode 100644 index 0000000..fcf2b40 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemInventory.h @@ -0,0 +1,143 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "NaItemStack.h" +#include "NaItemInventory.generated.h" + +/** + * UNaItemInventory - UObject-based inventory storing an ordered list of item stacks. + * Null entries represent empty slots. + */ +UCLASS(BlueprintType, Blueprintable) +class NAITEMSYSTEM_API UNaItemInventory : public UObject +{ + GENERATED_BODY() + +public: + UNaItemInventory(); + + /***** Query *****/ + + /** Get the inventory size (number of slots) */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + int32 GetSize() const; + + /** Check if a slot index is in range */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + bool IsValidSlot(int32 Slot) const; + + /** Get the stack at a slot (returns nullptr for empty or out-of-range) */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + UNaItemStack* GetSlot(int32 Slot) const; + + /** Check if a slot is empty (null or empty stack) */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + bool IsSlotEmpty(int32 Slot) const; + + /** Find the first empty slot; returns -1 if full */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + int32 FindFirstEmptySlot() const; + + /** Find first slot with the given item type starting from StartFrom; returns -1 if not found */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + int32 FindItemByType(const UNaItemType* Type, int32 StartFrom = 0) const; + + /** Count total items of a given type across all slots */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + int32 CountItemByType(const UNaItemType* Type) const; + + /***** Direct Slot Operations *****/ + + /** Directly set a slot. Null or empty stack clears the slot. */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + void SetSlot(int32 Slot, UNaItemStack* Stack); + + /** Clear a slot (set to nullptr) */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + void ClearSlot(int32 Slot); + + /** Swap contents of two slots */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + void SwapSlots(int32 SlotA, int32 SlotB); + + /** + * Try to merge the source stack into the dest stack. + * Returns true if any items were moved. Clears source slot if it becomes empty. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + bool MergeSlots(int32 SourceSlot, int32 DestSlot); + + /***** Giving Items *****/ + + /** + * Give an item stack to the inventory. + * Merges into existing stacks first, then fills empty slots. + * Returns the number of items that could NOT be added (0 = fully added). + * The input Stack's count is reduced by the amount actually added. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + int32 GiveItem(UNaItemStack* Stack); + + /** + * Like GiveItem but only executes if ALL items fit. + * Returns true if succeeded. Does not modify the input stack on failure. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + bool GiveItemComplete(UNaItemStack* Stack); + + /** Check if the given stack can be fully added without modifying anything */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + bool CanGiveItemComplete(const UNaItemStack* Stack) const; + + /***** Consuming / Taking Items *****/ + + /** + * Remove up to Amount items of the given type from the inventory. + * Returns the number actually consumed. Clears slots that become empty. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + int32 ConsumeItemByType(UNaItemType* Type, int32 Amount); + + /** Check if at least Amount items of the given type exist in the inventory */ + UFUNCTION(BlueprintPure, Category = "NaItemSystem|Inventory") + bool CanConsumeItemByType(const UNaItemType* Type, int32 Amount) const; + + /** + * Take a given amount from a slot, returning a new stack with those items. + * Clears the slot if all items are taken. Returns nullptr on failure. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + UNaItemStack* TakeFromSlot(int32 Slot, int32 Amount); + + /***** Resize *****/ + + /** + * Resize the inventory. + * If shrinking and occupied slots would be removed, fails unless bForce is true. + * When force-shrinking, items in removed slots are lost. + */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + bool Resize(int32 NewSize, bool bForce = false); + + /***** Maintenance *****/ + + /** Clear the entire inventory (all slots to nullptr) */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + void ClearAll(); + + /** Find any stacks with Count==0 and set their slots to nullptr */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + void ClearEmptyStacks(); + + /***** Static Factory *****/ + + /** Create a new inventory with the given size. All slots initialized to nullptr. */ + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + static UNaItemInventory* CreateInventory(UObject* Outer, int32 Size = 64); + +protected: + /** Ordered list of item stacks. Null entries represent empty slots. */ + UPROPERTY() + TArray Slots; +}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemStack.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemStack.h index d25f86f..a8272ad 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemStack.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemStack.h @@ -28,10 +28,10 @@ class NAITEMSYSTEM_API UNaItemStack : public UObject UNaItemType* ItemType; /** Number of items in this stack */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item Stack", meta = (ClampMin = "0", ClampMax = "999")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item Stack", meta = (ClampMin = "0")) int32 Count; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item Stack", meta = (ClampMin = "0", ClampMax = "999")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item Stack") TOptional CustomName; /***** Stack Management Functions *****/ diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemType.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemType.h index 3260e20..9a2b671 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemType.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/NaItemType.h @@ -88,6 +88,7 @@ class NAITEMSYSTEM_API UNaItemType : public UObject UFUNCTION(BlueprintPure, Category = "Natrium|Item System") TOptional GetRegistryName() const; - + UFUNCTION(BlueprintPure, Category = "Natrium|Item System") + virtual UNaItemStack* CreateDefaultInstance() const; }; \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h new file mode 100644 index 0000000..3a9ae7e --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h @@ -0,0 +1,43 @@ +// By Sodium + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "NaItemInventoryWidget.generated.h" + +UINTERFACE() +class UNaItemInventoryWidget : public UInterface +{ + GENERATED_BODY() +}; + +/** + * + */ +class NAITEMSYSTEM_API INaItemInventoryWidget +{ + GENERATED_BODY() + +public: + /** + * Get the inventory this widget describes. + */ + UFUNCTION(BlueprintPure) + virtual UNaItemInventory* GetInventory(); + + /** + * Get overall slot amount, including disabled slots. + * This amount is not necessarily equal to the inventory size. If this amount is smaller, the odd inventory slots + * will be ignored. If this amount is larger, disabled slots will be added in the tail. + */ + UFUNCTION(BlueprintPure) + virtual int32 GetSlotAmount(); + + /** + * Function mapping slot indexes to XY positions. + */ + UFUNCTION(BlueprintPure) + virtual FVector2D GetSlotPosition(int32 index); + +}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h index d6dc0c7..2d29993 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h @@ -9,7 +9,7 @@ #include "NaItemSlotList.generated.h" class SNaItemSlotList; -class UNaItemContainerComponent; +class UNaItemInventoryComponent; /** * @@ -66,10 +66,10 @@ class NAITEMSYSTEM_API UNaItemSlotList : public UWidget public: UPROPERTY(BlueprintReadOnly) - UNaItemContainerComponent* ContainerComponent = nullptr; + UNaItemInventoryComponent* ContainerComponent = nullptr; UFUNCTION(BlueprintCallable, Category = "NaItemSystem|UI|ItemSlotList") - void SetContainerComponent(UNaItemContainerComponent* NewComponent); + void SetContainerComponent(UNaItemInventoryComponent* NewComponent); UFUNCTION(BlueprintCallable, meta = (DisplayName = "Refresh Item Slot List"), Category = "NaItemSystem|UI|ItemSlotList") void Refresh(); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h index a126473..311a874 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h @@ -4,13 +4,11 @@ #include "CoreMinimal.h" #include "Widgets/SCompoundWidget.h" -#include "NaItemContainer.h" -#include "Data/NaItemDisplayData.h" +#include "Components/NaItemInventoryComponent.h" #include "UI/Widgets/SNaItemSlot.h" -#include "../../Components/NaItemContainerComponent.h" class UNaGameModeItemSystemComponent; -class UNaItemContainerComponent; +class UNaItemInventoryComponent; class UNaItemSlotList; /** * @@ -37,7 +35,7 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget } SLATE_ATTRIBUTE(FNaItemSlotPublicStyle*, StylePtr) - SLATE_ATTRIBUTE(UNaItemContainerComponent*, Container) + SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) SLATE_ATTRIBUTE(bool, bFillDisabledToCompleteRectangle) /* If true, it will add disabled slots to the end to fill a complete rectangle. */ SLATE_ATTRIBUTE(int, RowLength) /* How many boxes in a row */ SLATE_ATTRIBUTE(int, RowCount) // Amount of rows. If this count is not enough for container, it will be ignored and the row count will be automatically calculated from container. @@ -58,7 +56,7 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget /* Input copies */ UNaGameModeItemSystemComponent* GMComponent; - UNaItemContainerComponent* Container; + UNaItemInventoryComponent* Container; FNaItemSlotPublicStyle* StylePtr = nullptr; bool bFillDisabledToCompleteRectangle; int RowLength; @@ -79,7 +77,7 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget public: /* Get functions */ - UNaItemContainerComponent* GetContainer(){ return Container; }; + UNaItemInventoryComponent* GetContainer(){ return Container; }; TSharedPtr GetWrapBox() { return WrapBox; }; @@ -122,7 +120,7 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget void ResetAllSlots(); /* Reset container reference, and reconstruct widget */ - void ResetContainer(UNaItemContainerComponent* NewContainer); + void ResetContainer(UNaItemInventoryComponent* NewContainer); public: diff --git a/Source/ProjectAS/Private/Items/LItemEffect.cpp b/Source/ProjectAS/Private/Items/LItemEffect.cpp deleted file mode 100644 index 3c45810..0000000 --- a/Source/ProjectAS/Private/Items/LItemEffect.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// By Sodium - - -#include "Items/LItemEffect.h" -#include "NaMob.h" - -//ENaItemContainerUsageResult UItemEffect::ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) { - - -//} \ No newline at end of file diff --git a/Source/ProjectAS/Private/Items/LItemEffectBase.cpp b/Source/ProjectAS/Private/Items/LItemEffectBase.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/Source/ProjectAS/Public/Items/LItemEffect.h b/Source/ProjectAS/Public/Items/LItemEffect.h deleted file mode 100644 index 84bc15d..0000000 --- a/Source/ProjectAS/Public/Items/LItemEffect.h +++ /dev/null @@ -1,21 +0,0 @@ -// By Sodium - -#pragma once - -#include "CoreMinimal.h" -#include "NaItemEffect.h" -#include "LItemEffect.generated.h" - -/** - * - */ -UCLASS() -class PROJECTAS_API UItemEffect : public UNaItemEffect -{ - GENERATED_BODY() - -public: - -// virtual ENaItemContainerUsageResult ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) override; - -}; diff --git a/Source/ProjectAS/Public/Items/LItemEffectBase.h b/Source/ProjectAS/Public/Items/LItemEffectBase.h deleted file mode 100644 index 1d17cf3..0000000 --- a/Source/ProjectAS/Public/Items/LItemEffectBase.h +++ /dev/null @@ -1,21 +0,0 @@ -// By Sodium - -#pragma once - -#include "CoreMinimal.h" -#include "NaItemEffect.h" -#include "LItemEffectBase.generated.h" - -/** - * Base of game item effects, providing MobSystem interface - */ -UCLASS() -class PROJECTAS_API UItemEffectBase : public UNaItemEffect -{ - GENERATED_BODY() - -public: - - // virtual ENaItemContainerUsageResult ItemEffect_Implementation(int ItemID, AActor* SourceActor, AActor* TargetActor, int ItemPosition) override; - -}; From e582f9ec79c96a2beafbac96526cfeed04ffcb35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:50:40 +0000 Subject: [PATCH 3/7] Add SNaInventoryCanvas Slate widget and UNaInventoryCanvas UMG widget Co-authored-by: SodiumZH <46860829+SodiumZH@users.noreply.github.com> --- .../Private/UI/UMG/NaInventoryCanvas.cpp | 69 ++++ .../Private/UI/Widgets/SNaInventoryCanvas.cpp | 308 ++++++++++++++++++ .../Public/UI/UMG/NaInventoryCanvas.h | 118 +++++++ .../Public/UI/Widgets/SNaInventoryCanvas.h | 171 ++++++++++ 4 files changed, 666 insertions(+) create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h create mode 100644 Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp new file mode 100644 index 0000000..a2d2c3c --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp @@ -0,0 +1,69 @@ +// By Sodium + +#include "UI/UMG/NaInventoryCanvas.h" +#include "UI/Widgets/SNaInventoryCanvas.h" +#include "Components/NaItemInventoryComponent.h" +#include "NatriumItemSystem.h" +#include "NaUtilityMinimal.h" + +#define LOCTEXT_NAMESPACE "NaItemSystem" + +const FText UNaInventoryCanvas::GetPaletteCategory() { + return LOCTEXT("NaItemSystem", "NaItemSystem"); +} + +TSharedRef UNaInventoryCanvas::RebuildWidget() { + + FNaItemSlotPublicStyle* PublicStylePtr = &PublicStyle; + if (!IsValid(ContainerComponent)) { + UE_LOG(LogNaItem, Display, TEXT("UNaInventoryCanvas: invalid container reference.")); + SAssignNew(CanvasWidget, SNaInventoryCanvas) + .FromUMG(this) + .StylePtr(PublicStylePtr) + .SlotAmount(SlotAmount); + return CanvasWidget.ToSharedRef(); + } + else { + SAssignNew(CanvasWidget, SNaInventoryCanvas) + .Container(ContainerComponent) + .FromUMG(this) + .StylePtr(PublicStylePtr) + .SlotAmount(SlotAmount); + } + + return CanvasWidget.ToSharedRef(); +} + +void UNaInventoryCanvas::SetContainerComponent(UNaItemInventoryComponent* NewComponent) { + ContainerComponent = NewComponent; + if (CanvasWidget.IsValid()) + CanvasWidget->ResetContainer(NewComponent); +} + +void UNaInventoryCanvas::Refresh() { + if (CanvasWidget.IsValid()) + CanvasWidget->ResetAllSlots(); +} + +void UNaInventoryCanvas::SelectSlot(int Position) { + if (CanvasWidget.IsValid()) + CanvasWidget->SelectSlot(Position); +} + +void UNaInventoryCanvas::UnselectAll() { + if (CanvasWidget.IsValid()) + CanvasWidget->UnselectAll(); +} + +int UNaInventoryCanvas::GetSelectedPosition() { + if (CanvasWidget.IsValid()) + return CanvasWidget->GetSelectedPosition(); + else return -1; +} + +void UNaInventoryCanvas::ReleaseSlateResources(bool bReleaseChildren) { + Super::ReleaseSlateResources(bReleaseChildren); + CanvasWidget.Reset(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp new file mode 100644 index 0000000..b3a21b2 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp @@ -0,0 +1,308 @@ +// By Sodium + +#include "UI/Widgets/SNaInventoryCanvas.h" + +#include "NatriumItemSystem.h" +#include "SlateOptMacros.h" +#include "BPLibraries/NaItemStatics.h" +#include "Widgets/SCanvas.h" +#include "Widgets/BoxSlots/SNaBoxSlot.h" +#include "Components/NaItemInventoryComponent.h" +#include "UI/UMG/NaInventoryCanvas.h" +#include "Components/NaGameModeItemSystemComponent.h" + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION +void SNaInventoryCanvas::Construct(const FArguments& InArgs) +{ + Container = InArgs._Container.Get(); + + // When container is invalid, this widget is invalid + if (!IsValid(Container)) { + UE_LOG(LogNaItem, Display, TEXT("SNaInventoryCanvas: invalid item container.")); + bIsInvalid = true; + } + + /* Get context */ + GMComponent = bIsInvalid ? nullptr : UNaItemStatics::GetGameModeItemSystemComponent(InArgs._Container.Get()); + + if (!IsValid(GMComponent)) { + if (!bIsInvalid) + UE_LOG(LogNaItem, Warning, TEXT("SNaInventoryCanvas: invalid game mode component.")); + bIsInvalid = true; + } + + /* Copy inputs */ + StylePtr = InArgs._StylePtr.Get(); + SlotAmount = InArgs._SlotAmount.Get(); + UMGRef = InArgs._FromUMG; + + /* Create canvas panel */ + ChildSlot + [ + SAssignNew(Canvas, SCanvas) + ]; + + /* Reconstruct can do the rest */ + Reconstruct(); + + BindEventsToUMG(); + + bIsConstructed = true; +} +END_SLATE_FUNCTION_BUILD_OPTIMIZATION + +FVector2D SNaInventoryCanvas::GetSlotPosition(int32 Index) const +{ + return FVector2D::ZeroVector; +} + +void SNaInventoryCanvas::Reconstruct() +{ + // Clear all old slots + Slots.Empty(); + Canvas->ClearChildren(); + + // Re-get game mode component from container + GMComponent = IsValid(Container) ? UNaItemStatics::GetGameModeItemSystemComponent(Container) : nullptr; + + // When container is invalid, this widget is invalid + if (!IsValid(Container)) { + UE_LOG(LogNaItem, Display, TEXT("SNaInventoryCanvas: invalid item container.")); + bIsInvalid = true; + } + else if (!IsValid(GMComponent)) { + UE_LOG(LogNaItem, Warning, TEXT("SNaInventoryCanvas: invalid game mode component.")); + bIsInvalid = true; + } + else bIsInvalid = false; + + if (!bIsInvalid) { + + UNaItemInventory* Inv = Container->GetInventory(); + const int32 InvSize = Inv->GetSize(); + + // Determine actual slot count + const int32 ActualSlotAmount = (SlotAmount > 0) ? SlotAmount : InvSize; + + const FVector2D SlotSize = StylePtr ? StylePtr->SlotSize : FVector2D(64.f, 64.f); + + Slots.Init(TSharedPtr(nullptr), ActualSlotAmount); + + for (int32 i = 0; i < ActualSlotAmount; ++i) { + + const FVector2D Pos = GetSlotPosition(i); + const bool bDisabled = (i >= InvSize); + + if (bDisabled) { + Canvas->AddSlot() + .Position(Pos) + .Size(SlotSize) + [ + SAssignNew(Slots[i], SNaItemSlot) + .StylePtr(StylePtr) + .WorldContext(GMComponent) + .bIsDisabled(true) + ]; + } + else { + Canvas->AddSlot() + .Position(Pos) + .Size(SlotSize) + [ + SAssignNew(Slots[i], SNaItemSlot) + .StylePtr(StylePtr) + .WorldContext(GMComponent) + .Stack(Inv->GetSlot(i)) + ]; + } + + BindSlotEvents(i); + } + } +} + +void SNaInventoryCanvas::BindSlotEvents(int32 Position) +{ + TSharedPtr Slot = Slots[Position]; + if (!Slot.IsValid()) return; + + TSharedPtr BoxSlotWidget = Slot->GetBoxSlot(); + if (!BoxSlotWidget.IsValid()) return; + + // Only bind events for valid inventory slots + if (!IsValid(Container) || !Container->GetInventory()->IsValidSlot(Position)) { + return; + } + + // Bind pointing and selection events with Position payload + BoxSlotWidget->OnPointed.BindSP(this, &SNaInventoryCanvas::HandleSlotPointed, Position); + BoxSlotWidget->OnUnpointed.BindSP(this, &SNaInventoryCanvas::HandleSlotUnpointed, Position); + BoxSlotWidget->OnSelected.BindSP(this, &SNaInventoryCanvas::HandleSlotSelected, Position); + BoxSlotWidget->OnUnselected.BindSP(this, &SNaInventoryCanvas::HandleSlotUnselected, Position); + + // Bind button events + BoxSlotWidget->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaInventoryCanvas::HandleSlotClicked, Position)); + BoxSlotWidget->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotHovered, Position)); + BoxSlotWidget->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotUnhovered, Position)); + + // Bind mouse events + BoxSlotWidget->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonDown, Position)); + BoxSlotWidget->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonUp, Position)); + BoxSlotWidget->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseMove, Position)); + BoxSlotWidget->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotDoubleClicked, Position)); +} + +/* Slot event handlers */ + +void SNaInventoryCanvas::HandleSlotPointed(int32 Position) { + OnSlotPointed.Broadcast(Position); +} +void SNaInventoryCanvas::HandleSlotUnpointed(int32 Position) { + OnSlotUnpointed.Broadcast(Position); +} +void SNaInventoryCanvas::HandleSlotSelected(int32 Position) { + OnSlotSelected.Broadcast(Position); +} +void SNaInventoryCanvas::HandleSlotUnselected(int32 Position) { + OnSlotUnselected.Broadcast(Position); +} +FReply SNaInventoryCanvas::HandleSlotClicked(int32 Position) { + OnSlotClicked.Broadcast(Position); + return FReply::Handled(); +} +void SNaInventoryCanvas::HandleSlotHovered(int32 Position) { + // Replicate the SetPointed(true) behavior from SNaItemSlot::SlotHoveredToList + Slots[Position]->GetBoxSlot()->SetPointed(true); + OnSlotHovered.Broadcast(Position); +} +void SNaInventoryCanvas::HandleSlotUnhovered(int32 Position) { + // Replicate the SetPointed(false) behavior from SNaItemSlot::SlotUnhoveredToList + Slots[Position]->GetBoxSlot()->SetPointed(false); + OnSlotUnhovered.Broadcast(Position); +} + +FReply SNaInventoryCanvas::HandleSlotMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position) { + OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); + return FReply::Handled(); +} +FReply SNaInventoryCanvas::HandleSlotMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position) { + OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); + return FReply::Handled(); +} +FReply SNaInventoryCanvas::HandleSlotMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position) { + OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); + return FReply::Handled(); +} +FReply SNaInventoryCanvas::HandleSlotDoubleClicked(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position) { + OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); + return FReply::Handled(); +} + +void SNaInventoryCanvas::ResetSlot(int Position) +{ + if (IsInvalid()) + return; + + if (!Container->GetInventory()->IsValidSlot(Position)) { + UE_LOG(LogNaItem, Warning, TEXT("SNaInventoryCanvas::ResetSlot() failed: position out of range.")); + return; + } + + Slots[Position]->ResetItemStack(Container->GetInventory()->GetSlot(Position)); +} + +void SNaInventoryCanvas::ResetAllSlots() +{ + if (IsInvalid()) + return; + + UNaItemInventory* Inv = Container->GetInventory(); + for (int32 i = 0; i < Slots.Num() && i < Inv->GetSize(); ++i) { + Slots[i]->ResetItemStack(Inv->GetSlot(i)); + } +} + +void SNaInventoryCanvas::ResetContainer(UNaItemInventoryComponent* NewContainer) +{ + Container = NewContainer; + Reconstruct(); +} + +void SNaInventoryCanvas::SelectSlot(int Position) +{ + if (!IsValid(Container)) return; + check(Container->GetInventory()->IsValidSlot(Position)); + if (SelectedPosition >= 0) { + Slots[SelectedPosition]->GetBoxSlot()->SetSelected(false); + } + SelectedPosition = Position; + Slots[SelectedPosition]->GetBoxSlot()->SetSelected(true); +} + +void SNaInventoryCanvas::UnselectAll() +{ + if (SelectedPosition >= 0) { + Slots[SelectedPosition]->GetBoxSlot()->SetSelected(false); + } + SelectedPosition = -1; +} + +int SNaInventoryCanvas::GetSelectedPosition() +{ + return SelectedPosition; +} + +/* UMG event callers */ + +void SNaInventoryCanvas::SlotPointedToUMG(int Position) { + UMGRef->OnSlotPointed.Broadcast(Position); +} +void SNaInventoryCanvas::SlotUnpointedToUMG(int Position) { + UMGRef->OnSlotUnpointed.Broadcast(Position); +} +void SNaInventoryCanvas::SlotSelectedToUMG(int Position) { + UMGRef->OnSlotSelected.Broadcast(Position); +} +void SNaInventoryCanvas::SlotUnselectedToUMG(int Position) { + UMGRef->OnSlotUnselected.Broadcast(Position); +} +void SNaInventoryCanvas::SlotClickedToUMG(int Position) { + UMGRef->OnSlotClicked.Broadcast(Position); +} +void SNaInventoryCanvas::SlotHoveredToUMG(int Position) { + UMGRef->OnSlotHovered.Broadcast(Position); +} +void SNaInventoryCanvas::SlotUnhoveredToUMG(int Position) { + UMGRef->OnSlotUnhovered.Broadcast(Position); +} + +void SNaInventoryCanvas::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); +} +void SNaInventoryCanvas::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); +} +void SNaInventoryCanvas::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); +} +void SNaInventoryCanvas::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); +} + +void SNaInventoryCanvas::BindEventsToUMG() +{ + if (IsValid(UMGRef)) { + OnSlotPointed.AddSP(this, &SNaInventoryCanvas::SlotPointedToUMG); + OnSlotUnpointed.AddSP(this, &SNaInventoryCanvas::SlotUnpointedToUMG); + OnSlotSelected.AddSP(this, &SNaInventoryCanvas::SlotSelectedToUMG); + OnSlotUnselected.AddSP(this, &SNaInventoryCanvas::SlotUnselectedToUMG); + OnSlotClicked.AddSP(this, &SNaInventoryCanvas::SlotClickedToUMG); + OnSlotHovered.AddSP(this, &SNaInventoryCanvas::SlotHoveredToUMG); + OnSlotUnhovered.AddSP(this, &SNaInventoryCanvas::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddSP(this, &SNaInventoryCanvas::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddSP(this, &SNaInventoryCanvas::SlotDoubleClickedToUMG); + } +} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h new file mode 100644 index 0000000..bde2355 --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h @@ -0,0 +1,118 @@ +// By Sodium + +#pragma once + +#include "CoreMinimal.h" +#include "Components/Widget.h" +#include "UI/Widgets/SNaInventoryCanvas.h" +#include "UI/Widgets/SNaItemSlot.h" +#include "NaInventoryCanvas.generated.h" + +class SNaInventoryCanvas; +class UNaItemInventoryComponent; + +/** + * UNaInventoryCanvas - UMG widget wrapping SNaInventoryCanvas. + * Displays inventory slots on a canvas panel, where slot positions are + * determined by GetSlotPosition(). Override GetSlotPosition in a Blueprint + * or C++ subclass to customize slot layout. + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNaInventoryCanvasSlotEvent, int, Position); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNaInventoryCanvasSlotPointerEvent, int, Position, const FGeometry&, MyGeometry, const FPointerEvent&, MouseEvent); + + +UCLASS() +class NAITEMSYSTEM_API UNaInventoryCanvas : public UWidget +{ + GENERATED_BODY() + +protected: + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + + virtual TSharedRef RebuildWidget() override; + +public: + + /* Style */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "InventoryCanvas") + FNaItemSlotPublicStyle PublicStyle = FNaItemSlotPublicStyle(); + + /** + * Overall slot amount including disabled slots. + * If <= 0, defaults to inventory size. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "InventoryCanvas") + int SlotAmount = -1; + +protected: + + TSharedPtr CanvasWidget; + +public: + + UPROPERTY(BlueprintReadOnly) + UNaItemInventoryComponent* ContainerComponent = nullptr; + + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|UI|InventoryCanvas") + void SetContainerComponent(UNaItemInventoryComponent* NewComponent); + + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Refresh Inventory Canvas"), Category = "NaItemSystem|UI|InventoryCanvas") + void Refresh(); + +public: + + /* Selection related */ + + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|UI|InventoryCanvas") + void SelectSlot(int Position); + + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|UI|InventoryCanvas") + void UnselectAll(); + + UFUNCTION(BlueprintPure, Category = "NaItemSystem|UI|InventoryCanvas") + int GetSelectedPosition(); + +public: + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotPointed; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotUnpointed; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotSelected; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotUnselected; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotClicked; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotHovered; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotEvent OnSlotUnhovered; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotPointerEvent OnSlotMouseButtonDown; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotPointerEvent OnSlotMouseButtonUp; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotPointerEvent OnSlotMouseMove; + + UPROPERTY(BlueprintAssignable, Category = "NaItemSystem|UI|InventoryCanvas") + FNaInventoryCanvasSlotPointerEvent OnSlotDoubleClicked; + +public: + + virtual void ReleaseSlateResources(bool bReleaseChildren) override; + +}; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h new file mode 100644 index 0000000..485d8cb --- /dev/null +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h @@ -0,0 +1,171 @@ +// By Sodium + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/SCompoundWidget.h" +#include "Components/NaItemInventoryComponent.h" +#include "UI/Widgets/SNaItemSlot.h" + +class UNaGameModeItemSystemComponent; +class UNaItemInventoryComponent; +class UNaInventoryCanvas; + +/* Delegates */ +DECLARE_MULTICAST_DELEGATE_OneParam(FSNaInventoryCanvasSlotEvent, int); +DECLARE_MULTICAST_DELEGATE_ThreeParams(FSNaInventoryCanvasSlotPointerEvent, int, const FGeometry&, const FPointerEvent&); + + +/** + * SNaInventoryCanvas - Slate widget that displays inventory slots on a canvas panel. + * Unlike SNaItemSlotList which uses SWrapBox for grid layout, this widget uses SCanvas + * and positions each slot via GetSlotPosition(), allowing arbitrary slot placement. + * Override GetSlotPosition() in a subclass to customize slot layout. + */ +class NAITEMSYSTEM_API SNaInventoryCanvas : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SNaInventoryCanvas) + { + _StylePtr = nullptr; + _Container = nullptr; + _SlotAmount = -1; + _FromUMG = nullptr; + } + + SLATE_ATTRIBUTE(FNaItemSlotPublicStyle*, StylePtr) + SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) + /** Overall slot amount including disabled slots. If <= 0, uses inventory size. */ + SLATE_ATTRIBUTE(int, SlotAmount) + SLATE_ARGUMENT(UNaInventoryCanvas*, FromUMG) + + SLATE_END_ARGS() + + /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); + + +protected: + + bool bIsConstructed = false; + + /* Input copies */ + UNaGameModeItemSystemComponent* GMComponent = nullptr; + UNaItemInventoryComponent* Container = nullptr; + FNaItemSlotPublicStyle* StylePtr = nullptr; + int SlotAmount; + + // If true, this widget will not contain anything. + bool bIsInvalid = false; + + /* Subwidgets */ + TArray> Slots; + TSharedPtr Canvas; + +public: + + /* Getters */ + UNaItemInventoryComponent* GetContainer() { return Container; } + TSharedPtr GetCanvas() { return Canvas; } + bool IsInvalid() { return bIsInvalid; } + +public: + + /* Widget actions */ + + /** + * Get the position for a slot at the given index. + * Override in subclasses for custom slot placement. + * The default implementation returns FVector2D::ZeroVector. + */ + virtual FVector2D GetSlotPosition(int32 Index) const; + + /** + * Reconstruct the widget: destroy all slots and re-add them from the container. + * Also works when this widget is invalid - it will recheck and try constructing. + */ + void Reconstruct(); + + /** Reset a single slot's displayed item from the container. */ + void ResetSlot(int Position); + + /** Reset all slots' displayed items from the container. */ + void ResetAllSlots(); + + /** Reset container reference and reconstruct the widget. */ + void ResetContainer(UNaItemInventoryComponent* NewContainer); + +public: + + /* Selection */ + + void SelectSlot(int Position); + void UnselectAll(); + int GetSelectedPosition(); + +protected: + + int SelectedPosition = -1; + +public: + + /* Delegates executed when slot events are detected */ + FSNaInventoryCanvasSlotEvent OnSlotPointed; + FSNaInventoryCanvasSlotEvent OnSlotUnpointed; + FSNaInventoryCanvasSlotEvent OnSlotSelected; + FSNaInventoryCanvasSlotEvent OnSlotUnselected; + FSNaInventoryCanvasSlotEvent OnSlotClicked; + FSNaInventoryCanvasSlotEvent OnSlotHovered; + FSNaInventoryCanvasSlotEvent OnSlotUnhovered; + + FSNaInventoryCanvasSlotPointerEvent OnSlotMouseButtonDown; + FSNaInventoryCanvasSlotPointerEvent OnSlotMouseButtonUp; + FSNaInventoryCanvasSlotPointerEvent OnSlotMouseMove; + FSNaInventoryCanvasSlotPointerEvent OnSlotDoubleClicked; + +protected: + + /** + * Bind a single slot's box-slot events (pointed, selected, clicked, mouse, etc.) + * directly to this canvas's delegates, using payload binding. + */ + void BindSlotEvents(int32 Position); + + /* Slot event handlers - bound via payload with the slot position */ + void HandleSlotPointed(int32 Position); + void HandleSlotUnpointed(int32 Position); + void HandleSlotSelected(int32 Position); + void HandleSlotUnselected(int32 Position); + FReply HandleSlotClicked(int32 Position); + void HandleSlotHovered(int32 Position); + void HandleSlotUnhovered(int32 Position); + + FReply HandleSlotMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position); + FReply HandleSlotMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position); + FReply HandleSlotMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position); + FReply HandleSlotDoubleClicked(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 Position); + +public: + + /* UMG interface */ + + void SlotPointedToUMG(int Position); + void SlotUnpointedToUMG(int Position); + void SlotSelectedToUMG(int Position); + void SlotUnselectedToUMG(int Position); + void SlotClickedToUMG(int Position); + void SlotHoveredToUMG(int Position); + void SlotUnhoveredToUMG(int Position); + + void SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); + void SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); + void SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); + void SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); + +protected: + + UNaInventoryCanvas* UMGRef = nullptr; + + void BindEventsToUMG(); + +}; From 42832a79a5efc2eaee06f1440fa7fe93eea22dbc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:52:01 +0000 Subject: [PATCH 4/7] Add bounds check in hover handlers for safety Co-authored-by: SodiumZH <46860829+SodiumZH@users.noreply.github.com> --- .../Private/UI/Widgets/SNaInventoryCanvas.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp index b3a21b2..103f5e5 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp @@ -172,12 +172,16 @@ FReply SNaInventoryCanvas::HandleSlotClicked(int32 Position) { } void SNaInventoryCanvas::HandleSlotHovered(int32 Position) { // Replicate the SetPointed(true) behavior from SNaItemSlot::SlotHoveredToList - Slots[Position]->GetBoxSlot()->SetPointed(true); + if (Slots.IsValidIndex(Position) && Slots[Position].IsValid()) { + Slots[Position]->GetBoxSlot()->SetPointed(true); + } OnSlotHovered.Broadcast(Position); } void SNaInventoryCanvas::HandleSlotUnhovered(int32 Position) { // Replicate the SetPointed(false) behavior from SNaItemSlot::SlotUnhoveredToList - Slots[Position]->GetBoxSlot()->SetPointed(false); + if (Slots.IsValidIndex(Position) && Slots[Position].IsValid()) { + Slots[Position]->GetBoxSlot()->SetPointed(false); + } OnSlotUnhovered.Broadcast(Position); } From 18fe38f55ba0442a21cb85c67e9e0009908d2cd4 Mon Sep 17 00:00:00 2001 From: SodiumZH <46860829+SodiumZH@users.noreply.github.com> Date: Sat, 28 Feb 2026 15:54:52 +0800 Subject: [PATCH 5/7] Item widgets --- .../Config/DefaultNaItemSystem.ini | 3 +- .../Private/UI/NaItemInventoryWidget.cpp | 18 +++-- .../Private/UI/UMG/NaInventoryCanvas.cpp | 2 +- .../Private/UI/UMG/NaItemSlotList.cpp | 8 +-- ...lotList.cpp => SNaInventoryWrappedBox.cpp} | 68 +++++++++---------- .../Private/UI/Widgets/SNaItemSlot.cpp | 5 +- .../Public/UI/NaItemInventoryWidget.h | 45 +++++++++--- .../Public/UI/UMG/NaInventoryCanvas.h | 2 +- .../Public/UI/UMG/NaItemSlotList.h | 8 +-- .../Public/UI/Widgets/SNaInventoryCanvas.h | 4 +- ...temSlotList.h => SNaInventoryWrappedBox.h} | 12 ++-- .../Public/UI/Widgets/SNaItemSlot.h | 36 +++++----- 12 files changed, 127 insertions(+), 84 deletions(-) rename Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/{SNaItemSlotList.cpp => SNaInventoryWrappedBox.cpp} (71%) rename Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/{SNaItemSlotList.h => SNaInventoryWrappedBox.h} (93%) diff --git a/Plugins/NaItemSystem/Config/DefaultNaItemSystem.ini b/Plugins/NaItemSystem/Config/DefaultNaItemSystem.ini index 0471129..0ccad52 100644 --- a/Plugins/NaItemSystem/Config/DefaultNaItemSystem.ini +++ b/Plugins/NaItemSystem/Config/DefaultNaItemSystem.ini @@ -1,3 +1,4 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/NaItemSystem.NaItemType",NewName="/Script/NaItemSystem.UNaItemType") -+ClassRedirects=(OldName="/Script/NaItemSystem.NaItemStack",NewName="/Script/NaItemSystem.NaItemStack") \ No newline at end of file ++ClassRedirects=(OldName="/Script/NaItemSystem.NaItemStack",NewName="/Script/NaItemSystem.NaItemStack") ++StructRedirects=(OldName="/Script/NaItemSystem.NaItemSlotPublicStyle",NewName="/Script/NaItemSystem.NaItemSlotStyle") \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp index d09289e..305c385 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/NaItemInventoryWidget.cpp @@ -1,7 +1,17 @@ -// By Sodium - - +#pragma once #include "UI/NaItemInventoryWidget.h" +UNaItemInventory* INaItemInventoryWidget::GetInventory() const +{ + return this->GetNativeInterface().Pin().Get()->GetInventory(); +} + +int32 INaItemInventoryWidget::GetSlotAmount() const +{ + return this->GetNativeInterface().Pin().Get()->GetSlotAmount(); +} -// Add default functionality here for any INaItemInventoryWidget functions that are not pure virtual. +FVector2D INaItemInventoryWidget::GetSlotPosition(int32 index) const +{ + return this->GetNativeInterface().Pin().Get()->GetSlotPosition(index); +} diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp index a2d2c3c..b5d3add 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaInventoryCanvas.cpp @@ -14,7 +14,7 @@ const FText UNaInventoryCanvas::GetPaletteCategory() { TSharedRef UNaInventoryCanvas::RebuildWidget() { - FNaItemSlotPublicStyle* PublicStylePtr = &PublicStyle; + FNaItemSlotStyle* PublicStylePtr = &PublicStyle; if (!IsValid(ContainerComponent)) { UE_LOG(LogNaItem, Display, TEXT("UNaInventoryCanvas: invalid container reference.")); SAssignNew(CanvasWidget, SNaInventoryCanvas) diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp index d0fedeb..55896fa 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotList.cpp @@ -1,5 +1,5 @@ #include "UI/UMG/NaItemSlotList.h" -#include "UI/Widgets/SNaItemSlotList.h" +#include "UI/Widgets/SNaInventoryWrappedBox.h" #include "Components/NaItemInventoryComponent.h" #include "NatriumItemSystem.h" #include "NaUtilityMinimal.h" @@ -12,10 +12,10 @@ const FText UNaItemSlotList::GetPaletteCategory() { TSharedRef UNaItemSlotList::RebuildWidget(){ - FNaItemSlotPublicStyle* PublicStylePtr = &PublicStyle; + FNaItemSlotStyle* PublicStylePtr = &PublicStyle; if (!IsValid(ContainerComponent)) { UE_LOG(LogNaItem, Display, TEXT("UNaItemSlotList: invalid container reference.")); - SAssignNew(List, SNaItemSlotList) + SAssignNew(List, SNaInventoryWrappedBox) .FromUMG(this) .StylePtr(PublicStylePtr) .RowLength(RowLength) @@ -23,7 +23,7 @@ TSharedRef UNaItemSlotList::RebuildWidget(){ return List.ToSharedRef(); } else { - SAssignNew(List, SNaItemSlotList) + SAssignNew(List, SNaInventoryWrappedBox) .Container(ContainerComponent) .FromUMG(this) .StylePtr(PublicStylePtr) diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp similarity index 71% rename from Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp rename to Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp index 7405142..85121d4 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlotList.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp @@ -1,4 +1,4 @@ -#include "UI/Widgets/SNaItemSlotList.h" +#include "UI/Widgets/SNaInventoryWrappedBox.h" #include "NatriumItemSystem.h" #include "SlateOptMacros.h" @@ -10,7 +10,7 @@ #include "Components/NaGameModeItemSystemComponent.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION -void SNaItemSlotList::Construct(const FArguments& InArgs) +void SNaInventoryWrappedBox::Construct(const FArguments& InArgs) { Container = InArgs._Container.Get(); @@ -55,7 +55,7 @@ void SNaItemSlotList::Construct(const FArguments& InArgs) bIsConstructed = true; return; } -void SNaItemSlotList::Reconstruct() { +void SNaInventoryWrappedBox::Reconstruct() { // Clear all old slots Slots.Empty(); @@ -128,7 +128,7 @@ void SNaItemSlotList::Reconstruct() { END_SLATE_FUNCTION_BUILD_OPTIMIZATION -bool SNaItemSlotList::IsUpdated(bool bDisplay) { +bool SNaInventoryWrappedBox::IsUpdated(bool bDisplay) { bool Res = true; int i = 0; @@ -165,7 +165,7 @@ bool SNaItemSlotList::IsUpdated(bool bDisplay) { -void SNaItemSlotList::ResetSlot(int Position) { +void SNaInventoryWrappedBox::ResetSlot(int Position) { if (IsInvalid()) return; @@ -180,19 +180,19 @@ void SNaItemSlotList::ResetSlot(int Position) { } -void SNaItemSlotList::ResetAllSlots() { +void SNaInventoryWrappedBox::ResetAllSlots() { int i = 0; for (i = 0; i < Slots.Num(); ++i) { Slots[i]->ResetItemStack(Container->GetInventory()->GetSlot(i)); } } -void SNaItemSlotList::ResetContainer(UNaItemInventoryComponent* NewContainer) { +void SNaInventoryWrappedBox::ResetContainer(UNaItemInventoryComponent* NewContainer) { Container = NewContainer; Reconstruct(); } -void SNaItemSlotList::SelectSlot(int Position) { +void SNaInventoryWrappedBox::SelectSlot(int Position) { if (!IsValid(Container)) return; check(Container->GetInventory()->IsValidSlot(Position)); if (SelectedPosition >= 0) { @@ -202,67 +202,67 @@ void SNaItemSlotList::SelectSlot(int Position) { Slots[SelectedPosition]->GetBoxSlot()->SetSelected(true); } -void SNaItemSlotList::UnselectAll() { +void SNaInventoryWrappedBox::UnselectAll() { if (SelectedPosition >= 0) { Slots[SelectedPosition]->GetBoxSlot()->SetSelected(false); } SelectedPosition = -1; } -int SNaItemSlotList::GetSelectedPosition() { +int SNaInventoryWrappedBox::GetSelectedPosition() { return SelectedPosition; }; /* UNaItemSlotList event callers */ -void SNaItemSlotList::SlotPointedToUMG(int Position) { +void SNaInventoryWrappedBox::SlotPointedToUMG(int Position) { UMGRef->OnSlotPointed.Broadcast(Position); } -void SNaItemSlotList::SlotUnpointedToUMG(int Position) { +void SNaInventoryWrappedBox::SlotUnpointedToUMG(int Position) { UMGRef->OnSlotUnpointed.Broadcast(Position); } -void SNaItemSlotList::SlotSelectedToUMG(int Position) { +void SNaInventoryWrappedBox::SlotSelectedToUMG(int Position) { UMGRef->OnSlotSelected.Broadcast(Position); } -void SNaItemSlotList::SlotUnselectedToUMG(int Position) { +void SNaInventoryWrappedBox::SlotUnselectedToUMG(int Position) { UMGRef->OnSlotUnselected.Broadcast(Position); } -void SNaItemSlotList::SlotClickedToUMG(int Position) { +void SNaInventoryWrappedBox::SlotClickedToUMG(int Position) { UMGRef->OnSlotClicked.Broadcast(Position); } -void SNaItemSlotList::SlotHoveredToUMG(int Position) { +void SNaInventoryWrappedBox::SlotHoveredToUMG(int Position) { UMGRef->OnSlotHovered.Broadcast(Position); } -void SNaItemSlotList::SlotUnhoveredToUMG(int Position) { +void SNaInventoryWrappedBox::SlotUnhoveredToUMG(int Position) { UMGRef->OnSlotUnhovered.Broadcast(Position); } -void SNaItemSlotList::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { +void SNaInventoryWrappedBox::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); } -void SNaItemSlotList::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { +void SNaInventoryWrappedBox::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); } -void SNaItemSlotList::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { +void SNaInventoryWrappedBox::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); } -void SNaItemSlotList::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { +void SNaInventoryWrappedBox::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); } -void SNaItemSlotList::BindEventsToUMG() { +void SNaInventoryWrappedBox::BindEventsToUMG() { if (IsValid(UMGRef)) { - OnSlotPointed.AddSP(this, &SNaItemSlotList::SlotPointedToUMG); - OnSlotUnpointed.AddSP(this, &SNaItemSlotList::SlotUnpointedToUMG); - OnSlotSelected.AddSP(this, &SNaItemSlotList::SlotSelectedToUMG); - OnSlotUnselected.AddSP(this, &SNaItemSlotList::SlotUnselectedToUMG); - OnSlotClicked.AddSP(this, &SNaItemSlotList::SlotClickedToUMG); - OnSlotHovered.AddSP(this, &SNaItemSlotList::SlotHoveredToUMG); - OnSlotUnhovered.AddSP(this, &SNaItemSlotList::SlotUnhoveredToUMG); - - OnSlotMouseButtonDown.AddSP(this, &SNaItemSlotList::SlotMouseButtonDownToUMG); - OnSlotMouseButtonUp.AddSP(this, &SNaItemSlotList::SlotMouseButtonUpToUMG); - OnSlotMouseMove.AddSP(this, &SNaItemSlotList::SlotMouseMoveToUMG); - OnSlotDoubleClicked.AddSP(this, &SNaItemSlotList::SlotDoubleClickedToUMG); + OnSlotPointed.AddSP(this, &SNaInventoryWrappedBox::SlotPointedToUMG); + OnSlotUnpointed.AddSP(this, &SNaInventoryWrappedBox::SlotUnpointedToUMG); + OnSlotSelected.AddSP(this, &SNaInventoryWrappedBox::SlotSelectedToUMG); + OnSlotUnselected.AddSP(this, &SNaInventoryWrappedBox::SlotUnselectedToUMG); + OnSlotClicked.AddSP(this, &SNaInventoryWrappedBox::SlotClickedToUMG); + OnSlotHovered.AddSP(this, &SNaInventoryWrappedBox::SlotHoveredToUMG); + OnSlotUnhovered.AddSP(this, &SNaInventoryWrappedBox::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddSP(this, &SNaInventoryWrappedBox::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddSP(this, &SNaInventoryWrappedBox::SlotDoubleClickedToUMG); } } \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp index 5d6e906..392e7ee 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp @@ -2,12 +2,13 @@ #include "SlateOptMacros.h" #include "BPLibraries/NaItemStatics.h" #include "Components/NaGameModeItemSystemComponent.h" -#include "UI/Widgets/SNaItemSlotList.h" +#include "UI/Widgets/SNaInventoryWrappedBox.h" #include "Widgets/BoxSlots/SNaBoxSlot.h" #include "NaUtilityMinimal.h" #include "UI/UMG/NaItemSlotList.h" #include "NatriumItemSystem.h" +const FNaItemSlotStyle FNaItemSlotStyle::DefaultStyle = FNaItemSlotStyle(); BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SNaItemSlot::Construct(const FArguments& InArgs) @@ -188,7 +189,7 @@ FReply SNaItemSlot::SlotDoubleClickedToList(const FGeometry& MyGeometry, const F /* Events end */ -void SNaItemSlot::SetItemSlotList(SNaItemSlotList* List, int Position) { +void SNaItemSlot::SetItemSlotList(SNaInventoryWrappedBox* List, int Position) { check(List); ItemSlotList = List; PositionInSlotList = Position; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h index 3a9ae7e..70701ca 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/NaItemInventoryWidget.h @@ -1,6 +1,4 @@ -// By Sodium - -#pragma once +#pragma once #include "CoreMinimal.h" #include "UObject/Interface.h" @@ -23,21 +21,50 @@ class NAITEMSYSTEM_API INaItemInventoryWidget /** * Get the inventory this widget describes. */ - UFUNCTION(BlueprintPure) - virtual UNaItemInventory* GetInventory(); + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + virtual UNaItemInventory* GetInventory() const; /** * Get overall slot amount, including disabled slots. * This amount is not necessarily equal to the inventory size. If this amount is smaller, the odd inventory slots * will be ignored. If this amount is larger, disabled slots will be added in the tail. */ - UFUNCTION(BlueprintPure) - virtual int32 GetSlotAmount(); + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + virtual int32 GetSlotAmount() const; /** * Function mapping slot indexes to XY positions. */ - UFUNCTION(BlueprintPure) - virtual FVector2D GetSlotPosition(int32 index); + UFUNCTION(BlueprintCallable, Category = "NaItemSystem|Inventory") + virtual FVector2D GetSlotPosition(int32 index) const; + + virtual TWeakPtr GetNativeInterface() const = 0; + +}; + +/** + * Base interface for inventory slate classes. + */ +class NaItemInventoryWidgetInterface +{ + +public: + /** + * Get the inventory this widget describes. + */ + virtual UNaItemInventory* GetInventory() = 0; + + /** + * Get overall slot amount, including disabled slots. + * This amount is not necessarily equal to the inventory size. If this amount is smaller, the odd inventory slots + * will be ignored. If this amount is larger, disabled slots will be added in the tail. + */ + virtual int32 GetSlotAmount() const = 0; + + /** + * Function mapping slot indexes to XY positions. + */ + virtual FVector2D GetSlotPosition(int32 index) = 0; }; + diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h index bde2355..825eead 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaInventoryCanvas.h @@ -39,7 +39,7 @@ class NAITEMSYSTEM_API UNaInventoryCanvas : public UWidget /* Style */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "InventoryCanvas") - FNaItemSlotPublicStyle PublicStyle = FNaItemSlotPublicStyle(); + FNaItemSlotStyle PublicStyle = FNaItemSlotStyle(); /** * Overall slot amount including disabled slots. diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h index 2d29993..db47477 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h @@ -4,11 +4,11 @@ #include "CoreMinimal.h" #include "Components/Widget.h" -#include "UI/Widgets/SNaItemSlotList.h" +#include "UI/Widgets/SNaInventoryWrappedBox.h" #include "UI/Widgets/SNaItemSlot.h" #include "NaItemSlotList.generated.h" -class SNaItemSlotList; +class SNaInventoryWrappedBox; class UNaItemInventoryComponent; /** @@ -41,7 +41,7 @@ class NAITEMSYSTEM_API UNaItemSlotList : public UWidget /* Style */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ItemSlotList") - FNaItemSlotPublicStyle PublicStyle = FNaItemSlotPublicStyle(); + FNaItemSlotStyle PublicStyle = FNaItemSlotStyle(); // Amount of slots each row UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ItemSlotList") @@ -61,7 +61,7 @@ class NAITEMSYSTEM_API UNaItemSlotList : public UWidget protected: - TSharedPtr List; + TSharedRef List; public: diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h index 485d8cb..4c57db5 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h @@ -33,7 +33,7 @@ class NAITEMSYSTEM_API SNaInventoryCanvas : public SCompoundWidget _FromUMG = nullptr; } - SLATE_ATTRIBUTE(FNaItemSlotPublicStyle*, StylePtr) + SLATE_ATTRIBUTE(FNaItemSlotStyle*, StylePtr) SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) /** Overall slot amount including disabled slots. If <= 0, uses inventory size. */ SLATE_ATTRIBUTE(int, SlotAmount) @@ -52,7 +52,7 @@ class NAITEMSYSTEM_API SNaInventoryCanvas : public SCompoundWidget /* Input copies */ UNaGameModeItemSystemComponent* GMComponent = nullptr; UNaItemInventoryComponent* Container = nullptr; - FNaItemSlotPublicStyle* StylePtr = nullptr; + FNaItemSlotStyle* StylePtr = nullptr; int SlotAmount; // If true, this widget will not contain anything. diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h similarity index 93% rename from Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h rename to Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h index 311a874..1735abc 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h @@ -22,10 +22,10 @@ DECLARE_MULTICAST_DELEGATE_OneParam(FSNaItemListSlotEvent, int); DECLARE_MULTICAST_DELEGATE_ThreeParams(FSNaItemListSlotPointerEvent, int, const FGeometry&, const FPointerEvent&); -class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget +class NAITEMSYSTEM_API SNaInventoryWrappedBox : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SNaItemSlotList) + SLATE_BEGIN_ARGS(SNaInventoryWrappedBox) { _StylePtr = nullptr; _Container = nullptr; @@ -34,12 +34,12 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget _FromUMG = nullptr; } - SLATE_ATTRIBUTE(FNaItemSlotPublicStyle*, StylePtr) - SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) + SLATE_ATTRIBUTE(TSharedRef, StylePtr) + SLATE_ATTRIBUTE(TWeakObjectPtr, Container) SLATE_ATTRIBUTE(bool, bFillDisabledToCompleteRectangle) /* If true, it will add disabled slots to the end to fill a complete rectangle. */ SLATE_ATTRIBUTE(int, RowLength) /* How many boxes in a row */ SLATE_ATTRIBUTE(int, RowCount) // Amount of rows. If this count is not enough for container, it will be ignored and the row count will be automatically calculated from container. - SLATE_ARGUMENT(UNaItemSlotList*, FromUMG) /* If generated from UNaItemSlotList, set this value */ + SLATE_ARGUMENT(TWeakObjectPtr, FromUMG) /* If generated from UNaItemSlotList, set this value */ SLATE_END_ARGS() @@ -57,7 +57,7 @@ class NAITEMSYSTEM_API SNaItemSlotList : public SCompoundWidget UNaGameModeItemSystemComponent* GMComponent; UNaItemInventoryComponent* Container; - FNaItemSlotPublicStyle* StylePtr = nullptr; + FNaItemSlotStyle* StylePtr = nullptr; bool bFillDisabledToCompleteRectangle; int RowLength; int RowCount; diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h index fb1b9fa..af19491 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h @@ -10,19 +10,22 @@ #include "SNaItemSlot.generated.h" class UNaGameModeItemSystemComponent; -class SNaItemSlotList; +class SNaInventoryWrappedBox; -/* Item slot styles that are often constant for a whole slot list. +/** + * Item slot styles that are often constant for a whole slot list. * It can also be used to define single slots, and is not limited in slot lists. -* This struct is utilized in SNaItemSlot input params which can easily defined in slot list BPs and transferred as a whole to each slot. +* This struct is used in SNaItemSlot input params which can easily defined in slot list BPs and transferred as a whole to each slot. */ USTRUCT(BlueprintType) -struct NAITEMSYSTEM_API FNaItemSlotPublicStyle { +struct NAITEMSYSTEM_API FNaItemSlotStyle { GENERATED_BODY() public: - + + static const FNaItemSlotStyle DefaultStyle; + /* Slot size (in pixel). */ UPROPERTY(BlueprintReadWrite, EditAnywhere) FVector2D SlotSize = FVector2D(64.f, 64.f); @@ -71,7 +74,8 @@ struct NAITEMSYSTEM_API FNaItemSlotPublicStyle { UPROPERTY(BlueprintReadWrite, EditAnywhere) bool bHideAmountWhenOne = true; - FNaItemSlotPublicStyle() {}; + FNaItemSlotStyle() {}; + }; /** @@ -82,15 +86,15 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget public: SLATE_BEGIN_ARGS(SNaItemSlot) { - _StylePtr = nullptr; + _Style = FNaItemSlotStyle::DefaultStyle; _WorldContext = nullptr; _Stack = nullptr; _bIsDisabled = false; } - SLATE_ATTRIBUTE(FNaItemSlotPublicStyle*, StylePtr) + SLATE_ATTRIBUTE(FNaItemSlotStyle, Style) SLATE_ATTRIBUTE(UObject*, WorldContext) // object as world context (indicating world) - SLATE_ATTRIBUTE(UNaItemStack*, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ + SLATE_ATTRIBUTE(TWeakObjectPtr, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ SLATE_ATTRIBUTE(bool, bIsDisabled) /* If true, this slot will be regarded as disabled, ignoring the value of ItemEntry and bIsEmpty() */ SLATE_END_ARGS() @@ -98,7 +102,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /** Constructs this widget with InArgs */ void Construct(const FArguments& InArgs); - /* Make slot params for box slot construction, from public style and item entry */ + /* Make slot params for box slot construction, from style and item stack */ void MakeParams(FNaBoxSlotParams& OutParams); protected: @@ -114,7 +118,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /* Style */ - FNaItemSlotPublicStyle* StylePtr = nullptr; + TSharedPtr StylePtr = nullptr; FSlateFontInfo Font = FTextBlockStyle::GetDefault().Font; @@ -153,12 +157,11 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget protected: - /**************************************************/ - /* Functions for interaction with SNaItemSlotList */ + /* Functions for interaction with inventory widget */ /* If this ptr is null, this slot is not in an SNaItemSlotList */ - SNaItemSlotList* ItemSlotList = nullptr; + TWeakPtr ItemSlotList = nullptr; int PositionInSlotList = -1; @@ -167,7 +170,8 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget public: - /* Functions for transfer events to ItemSlotList. + /* + * Functions for transfer events to ItemSlotList. * Warning: no validity check inside. Check validity before calling. */ // SNaItemSlot only transfers pointing/selecting/mouse events from SNaBoxSlot to SNaItemSlotList. @@ -192,7 +196,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget // Call this function ONLY when constructing slots in item slot list. - void SetItemSlotList(SNaItemSlotList* List, int Position); + void SetItemSlotList(SNaInventoryWrappedBox* List, int Position); /* SNaItemSlotList end */ /***********************/ From 5b15a297c6ed26d6339e55a08f703e809804ce3c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 08:10:14 +0000 Subject: [PATCH 6/7] Fix memory leaks: break cyclic TSharedPtr references in item UI widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SNaItemSlot: BindSP→BindRaw/CreateRaw in BoxSlot delegate bindings to break parent↔child cycle - SNaInventoryWrappedBox: AddSP→AddRaw in BindEventsToUMG to break self-referential cycle - SNaInventoryCanvas: BindSP/CreateSP→BindRaw/CreateRaw in both BindSlotEvents and BindEventsToUMG - NaItemSlotList UMG: TSharedRef→TSharedPtr for List member (TSharedRef has no default ctor/Reset) - Fix broken SLATE_ATTRIBUTE types in SNaInventoryWrappedBox and SNaItemSlot headers - Add UMGRef assignment in SNaInventoryWrappedBox::Construct (was never set) - Change UMGRef to TWeakObjectPtr in SNaInventoryWrappedBox and SNaInventoryCanvas - Add validity checks in all SlotXxxToUMG() functions Co-authored-by: SodiumZH <46860829+SodiumZH@users.noreply.github.com> --- .../Private/UI/UMG/NaItemSlotListPreset.cpp | 6 +- .../Private/UI/Widgets/SNaInventoryCanvas.cpp | 83 +++++++++++-------- .../UI/Widgets/SNaInventoryWrappedBox.cpp | 60 ++++++++------ .../Private/UI/Widgets/SNaItemSlot.cpp | 46 +++++----- .../Public/UI/UMG/NaItemSlotList.h | 2 +- .../Public/UI/Widgets/SNaInventoryCanvas.h | 2 +- .../UI/Widgets/SNaInventoryWrappedBox.h | 8 +- .../Public/UI/Widgets/SNaItemSlot.h | 10 +-- 8 files changed, 122 insertions(+), 95 deletions(-) diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp index 4271abc..cb0acb6 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp @@ -7,8 +7,10 @@ TSharedRef UNaItemSlotListPreset::RebuildWidget() { TSharedRef Widget = Super::RebuildWidget(); - List->OnSlotClicked.AddUObject(this, &UNaItemSlotListPreset::EventClickedPreset); - List->OnSlotDoubleClicked.AddUObject(this, &UNaItemSlotListPreset::EventDoubleClickedPreset); + if (List.IsValid()) { + List->OnSlotClicked.AddUObject(this, &UNaItemSlotListPreset::EventClickedPreset); + List->OnSlotDoubleClicked.AddUObject(this, &UNaItemSlotListPreset::EventDoubleClickedPreset); + } return Widget; } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp index 103f5e5..c50cad0 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp @@ -135,21 +135,23 @@ void SNaInventoryCanvas::BindSlotEvents(int32 Position) } // Bind pointing and selection events with Position payload - BoxSlotWidget->OnPointed.BindSP(this, &SNaInventoryCanvas::HandleSlotPointed, Position); - BoxSlotWidget->OnUnpointed.BindSP(this, &SNaInventoryCanvas::HandleSlotUnpointed, Position); - BoxSlotWidget->OnSelected.BindSP(this, &SNaInventoryCanvas::HandleSlotSelected, Position); - BoxSlotWidget->OnUnselected.BindSP(this, &SNaInventoryCanvas::HandleSlotUnselected, Position); + // Use BindRaw/CreateRaw instead of BindSP/CreateSP to avoid cyclic TSharedPtr references + // (Canvas → Slots → SNaItemSlot → BoxSlot → delegates → Canvas) + BoxSlotWidget->OnPointed.BindRaw(this, &SNaInventoryCanvas::HandleSlotPointed, Position); + BoxSlotWidget->OnUnpointed.BindRaw(this, &SNaInventoryCanvas::HandleSlotUnpointed, Position); + BoxSlotWidget->OnSelected.BindRaw(this, &SNaInventoryCanvas::HandleSlotSelected, Position); + BoxSlotWidget->OnUnselected.BindRaw(this, &SNaInventoryCanvas::HandleSlotUnselected, Position); // Bind button events - BoxSlotWidget->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaInventoryCanvas::HandleSlotClicked, Position)); - BoxSlotWidget->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotHovered, Position)); - BoxSlotWidget->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotUnhovered, Position)); + BoxSlotWidget->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaInventoryCanvas::HandleSlotClicked, Position)); + BoxSlotWidget->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaInventoryCanvas::HandleSlotHovered, Position)); + BoxSlotWidget->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaInventoryCanvas::HandleSlotUnhovered, Position)); // Bind mouse events - BoxSlotWidget->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonDown, Position)); - BoxSlotWidget->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonUp, Position)); - BoxSlotWidget->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseMove, Position)); - BoxSlotWidget->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotDoubleClicked, Position)); + BoxSlotWidget->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseButtonDown, Position)); + BoxSlotWidget->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseButtonUp, Position)); + BoxSlotWidget->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseMove, Position)); + BoxSlotWidget->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotDoubleClicked, Position)); } /* Slot event handlers */ @@ -259,54 +261,65 @@ int SNaInventoryCanvas::GetSelectedPosition() /* UMG event callers */ void SNaInventoryCanvas::SlotPointedToUMG(int Position) { - UMGRef->OnSlotPointed.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotPointed.Broadcast(Position); } void SNaInventoryCanvas::SlotUnpointedToUMG(int Position) { - UMGRef->OnSlotUnpointed.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnpointed.Broadcast(Position); } void SNaInventoryCanvas::SlotSelectedToUMG(int Position) { - UMGRef->OnSlotSelected.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotSelected.Broadcast(Position); } void SNaInventoryCanvas::SlotUnselectedToUMG(int Position) { - UMGRef->OnSlotUnselected.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnselected.Broadcast(Position); } void SNaInventoryCanvas::SlotClickedToUMG(int Position) { - UMGRef->OnSlotClicked.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotClicked.Broadcast(Position); } void SNaInventoryCanvas::SlotHoveredToUMG(int Position) { - UMGRef->OnSlotHovered.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotHovered.Broadcast(Position); } void SNaInventoryCanvas::SlotUnhoveredToUMG(int Position) { - UMGRef->OnSlotUnhovered.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnhovered.Broadcast(Position); } void SNaInventoryCanvas::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::BindEventsToUMG() { - if (IsValid(UMGRef)) { - OnSlotPointed.AddSP(this, &SNaInventoryCanvas::SlotPointedToUMG); - OnSlotUnpointed.AddSP(this, &SNaInventoryCanvas::SlotUnpointedToUMG); - OnSlotSelected.AddSP(this, &SNaInventoryCanvas::SlotSelectedToUMG); - OnSlotUnselected.AddSP(this, &SNaInventoryCanvas::SlotUnselectedToUMG); - OnSlotClicked.AddSP(this, &SNaInventoryCanvas::SlotClickedToUMG); - OnSlotHovered.AddSP(this, &SNaInventoryCanvas::SlotHoveredToUMG); - OnSlotUnhovered.AddSP(this, &SNaInventoryCanvas::SlotUnhoveredToUMG); - - OnSlotMouseButtonDown.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonDownToUMG); - OnSlotMouseButtonUp.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonUpToUMG); - OnSlotMouseMove.AddSP(this, &SNaInventoryCanvas::SlotMouseMoveToUMG); - OnSlotDoubleClicked.AddSP(this, &SNaInventoryCanvas::SlotDoubleClickedToUMG); + if (UMGRef.IsValid()) { + OnSlotPointed.AddRaw(this, &SNaInventoryCanvas::SlotPointedToUMG); + OnSlotUnpointed.AddRaw(this, &SNaInventoryCanvas::SlotUnpointedToUMG); + OnSlotSelected.AddRaw(this, &SNaInventoryCanvas::SlotSelectedToUMG); + OnSlotUnselected.AddRaw(this, &SNaInventoryCanvas::SlotUnselectedToUMG); + OnSlotClicked.AddRaw(this, &SNaInventoryCanvas::SlotClickedToUMG); + OnSlotHovered.AddRaw(this, &SNaInventoryCanvas::SlotHoveredToUMG); + OnSlotUnhovered.AddRaw(this, &SNaInventoryCanvas::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddRaw(this, &SNaInventoryCanvas::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddRaw(this, &SNaInventoryCanvas::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddRaw(this, &SNaInventoryCanvas::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddRaw(this, &SNaInventoryCanvas::SlotDoubleClickedToUMG); } } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp index 85121d4..2cdee92 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp @@ -39,6 +39,7 @@ void SNaInventoryWrappedBox::Construct(const FArguments& InArgs) bFillDisabledToCompleteRectangle = InArgs._bFillDisabledToCompleteRectangle.Get(); RowLength = InArgs._RowLength.Get(); RowCount = InArgs._RowCount.Get(); + UMGRef = InArgs._FromUMG; /* Add panel */ ChildSlot @@ -216,53 +217,64 @@ int SNaInventoryWrappedBox::GetSelectedPosition() { /* UNaItemSlotList event callers */ void SNaInventoryWrappedBox::SlotPointedToUMG(int Position) { - UMGRef->OnSlotPointed.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotPointed.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnpointedToUMG(int Position) { - UMGRef->OnSlotUnpointed.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnpointed.Broadcast(Position); } void SNaInventoryWrappedBox::SlotSelectedToUMG(int Position) { - UMGRef->OnSlotSelected.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotSelected.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnselectedToUMG(int Position) { - UMGRef->OnSlotUnselected.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnselected.Broadcast(Position); } void SNaInventoryWrappedBox::SlotClickedToUMG(int Position) { - UMGRef->OnSlotClicked.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotClicked.Broadcast(Position); } void SNaInventoryWrappedBox::SlotHoveredToUMG(int Position) { - UMGRef->OnSlotHovered.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotHovered.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnhoveredToUMG(int Position) { - UMGRef->OnSlotUnhovered.Broadcast(Position); + if (UMGRef.IsValid()) + UMGRef->OnSlotUnhovered.Broadcast(Position); } void SNaInventoryWrappedBox::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); + if (UMGRef.IsValid()) + UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::BindEventsToUMG() { - if (IsValid(UMGRef)) { - OnSlotPointed.AddSP(this, &SNaInventoryWrappedBox::SlotPointedToUMG); - OnSlotUnpointed.AddSP(this, &SNaInventoryWrappedBox::SlotUnpointedToUMG); - OnSlotSelected.AddSP(this, &SNaInventoryWrappedBox::SlotSelectedToUMG); - OnSlotUnselected.AddSP(this, &SNaInventoryWrappedBox::SlotUnselectedToUMG); - OnSlotClicked.AddSP(this, &SNaInventoryWrappedBox::SlotClickedToUMG); - OnSlotHovered.AddSP(this, &SNaInventoryWrappedBox::SlotHoveredToUMG); - OnSlotUnhovered.AddSP(this, &SNaInventoryWrappedBox::SlotUnhoveredToUMG); - - OnSlotMouseButtonDown.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonDownToUMG); - OnSlotMouseButtonUp.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonUpToUMG); - OnSlotMouseMove.AddSP(this, &SNaInventoryWrappedBox::SlotMouseMoveToUMG); - OnSlotDoubleClicked.AddSP(this, &SNaInventoryWrappedBox::SlotDoubleClickedToUMG); + if (UMGRef.IsValid()) { + OnSlotPointed.AddRaw(this, &SNaInventoryWrappedBox::SlotPointedToUMG); + OnSlotUnpointed.AddRaw(this, &SNaInventoryWrappedBox::SlotUnpointedToUMG); + OnSlotSelected.AddRaw(this, &SNaInventoryWrappedBox::SlotSelectedToUMG); + OnSlotUnselected.AddRaw(this, &SNaInventoryWrappedBox::SlotUnselectedToUMG); + OnSlotClicked.AddRaw(this, &SNaInventoryWrappedBox::SlotClickedToUMG); + OnSlotHovered.AddRaw(this, &SNaInventoryWrappedBox::SlotHoveredToUMG); + OnSlotUnhovered.AddRaw(this, &SNaInventoryWrappedBox::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddRaw(this, &SNaInventoryWrappedBox::SlotDoubleClickedToUMG); } } \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp index 392e7ee..8c4fdb1 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp @@ -108,36 +108,36 @@ void SNaItemSlot::BindItemSlotListEvents() { // If in item slot list, bind mouse events if (ItemSlotList && !ItemSlotList->IsInvalid() && ItemSlotList->GetContainer()->Inventory->IsValidSlot(PositionInSlotList) && BoxSlot.IsValid()) { - BoxSlot->OnPointed.BindSP(this, &SNaItemSlot::SlotPointedToList); - BoxSlot->OnUnpointed.BindSP(this, &SNaItemSlot::SlotUnpointedToList); - BoxSlot->OnSelected.BindSP(this, &SNaItemSlot::SlotSelectedToList); - BoxSlot->OnUnselected.BindSP(this, &SNaItemSlot::SlotUnselectedToList); - BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaItemSlot::SlotClickedToList)); - BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::SlotHoveredToList)); - BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::SlotUnhoveredToList)); + BoxSlot->OnPointed.BindRaw(this, &SNaItemSlot::SlotPointedToList); + BoxSlot->OnUnpointed.BindRaw(this, &SNaItemSlot::SlotUnpointedToList); + BoxSlot->OnSelected.BindRaw(this, &SNaItemSlot::SlotSelectedToList); + BoxSlot->OnUnselected.BindRaw(this, &SNaItemSlot::SlotUnselectedToList); + BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaItemSlot::SlotClickedToList)); + BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::SlotHoveredToList)); + BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::SlotUnhoveredToList)); - BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseButtonDownToList)); - BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseButtonUpToList)); - BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseMoveToList)); - BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotDoubleClickedToList)); + BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseButtonDownToList)); + BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseButtonUpToList)); + BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseMoveToList)); + BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotDoubleClickedToList)); } // Or bind to null functions (no behavior). else { - BoxSlot->OnPointed.BindSP(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnUnpointed.BindSP(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnSelected.BindSP(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnUnselected.BindSP(this, &SNaItemSlot::ExecNoList); - BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaItemSlot::ExecNoListClicked)); - BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::ExecNoList)); - BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::ExecNoList)); - - BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->OnPointed.BindRaw(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnUnpointed.BindRaw(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnSelected.BindRaw(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnUnselected.BindRaw(this, &SNaItemSlot::ExecNoList); + BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaItemSlot::ExecNoListClicked)); + BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::ExecNoList)); + BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::ExecNoList)); + + BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); } } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h index db47477..b3cb7a7 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h @@ -61,7 +61,7 @@ class NAITEMSYSTEM_API UNaItemSlotList : public UWidget protected: - TSharedRef List; + TSharedPtr List; public: diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h index 4c57db5..9cc6506 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h @@ -164,7 +164,7 @@ class NAITEMSYSTEM_API SNaInventoryCanvas : public SCompoundWidget protected: - UNaInventoryCanvas* UMGRef = nullptr; + TWeakObjectPtr UMGRef = nullptr; void BindEventsToUMG(); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h index 1735abc..670e023 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h @@ -34,12 +34,12 @@ class NAITEMSYSTEM_API SNaInventoryWrappedBox : public SCompoundWidget _FromUMG = nullptr; } - SLATE_ATTRIBUTE(TSharedRef, StylePtr) - SLATE_ATTRIBUTE(TWeakObjectPtr, Container) + SLATE_ATTRIBUTE(FNaItemSlotStyle*, StylePtr) + SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) SLATE_ATTRIBUTE(bool, bFillDisabledToCompleteRectangle) /* If true, it will add disabled slots to the end to fill a complete rectangle. */ SLATE_ATTRIBUTE(int, RowLength) /* How many boxes in a row */ SLATE_ATTRIBUTE(int, RowCount) // Amount of rows. If this count is not enough for container, it will be ignored and the row count will be automatically calculated from container. - SLATE_ARGUMENT(TWeakObjectPtr, FromUMG) /* If generated from UNaItemSlotList, set this value */ + SLATE_ARGUMENT(UNaItemSlotList*, FromUMG) /* If generated from UNaItemSlotList, set this value */ SLATE_END_ARGS() @@ -183,7 +183,7 @@ class NAITEMSYSTEM_API SNaInventoryWrappedBox : public SCompoundWidget protected: - UNaItemSlotList* UMGRef = nullptr; + TWeakObjectPtr UMGRef = nullptr; void BindEventsToUMG(); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h index af19491..3ee7190 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h @@ -86,15 +86,15 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget public: SLATE_BEGIN_ARGS(SNaItemSlot) { - _Style = FNaItemSlotStyle::DefaultStyle; + _StylePtr = nullptr; _WorldContext = nullptr; _Stack = nullptr; _bIsDisabled = false; } - SLATE_ATTRIBUTE(FNaItemSlotStyle, Style) + SLATE_ATTRIBUTE(FNaItemSlotStyle*, StylePtr) SLATE_ATTRIBUTE(UObject*, WorldContext) // object as world context (indicating world) - SLATE_ATTRIBUTE(TWeakObjectPtr, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ + SLATE_ATTRIBUTE(UNaItemStack*, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ SLATE_ATTRIBUTE(bool, bIsDisabled) /* If true, this slot will be regarded as disabled, ignoring the value of ItemEntry and bIsEmpty() */ SLATE_END_ARGS() @@ -118,7 +118,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /* Style */ - TSharedPtr StylePtr = nullptr; + FNaItemSlotStyle* StylePtr = nullptr; FSlateFontInfo Font = FTextBlockStyle::GetDefault().Font; @@ -161,7 +161,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /* If this ptr is null, this slot is not in an SNaItemSlotList */ - TWeakPtr ItemSlotList = nullptr; + SNaInventoryWrappedBox* ItemSlotList = nullptr; int PositionInSlotList = -1; From 474ecb8276776d67f163d5ffd64108010845dc6d Mon Sep 17 00:00:00 2001 From: SodiumZH <46860829+SodiumZH@users.noreply.github.com> Date: Sat, 28 Feb 2026 16:54:45 +0800 Subject: [PATCH 7/7] Revert "Fix memory leaks: break cyclic TSharedPtr references in item UI widgets" This reverts commit 5b15a297c6ed26d6339e55a08f703e809804ce3c. --- .../Private/UI/UMG/NaItemSlotListPreset.cpp | 6 +- .../Private/UI/Widgets/SNaInventoryCanvas.cpp | 83 ++++++++----------- .../UI/Widgets/SNaInventoryWrappedBox.cpp | 60 ++++++-------- .../Private/UI/Widgets/SNaItemSlot.cpp | 46 +++++----- .../Public/UI/UMG/NaItemSlotList.h | 2 +- .../Public/UI/Widgets/SNaInventoryCanvas.h | 2 +- .../UI/Widgets/SNaInventoryWrappedBox.h | 8 +- .../Public/UI/Widgets/SNaItemSlot.h | 10 +-- 8 files changed, 95 insertions(+), 122 deletions(-) diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp index cb0acb6..4271abc 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/UMG/NaItemSlotListPreset.cpp @@ -7,10 +7,8 @@ TSharedRef UNaItemSlotListPreset::RebuildWidget() { TSharedRef Widget = Super::RebuildWidget(); - if (List.IsValid()) { - List->OnSlotClicked.AddUObject(this, &UNaItemSlotListPreset::EventClickedPreset); - List->OnSlotDoubleClicked.AddUObject(this, &UNaItemSlotListPreset::EventDoubleClickedPreset); - } + List->OnSlotClicked.AddUObject(this, &UNaItemSlotListPreset::EventClickedPreset); + List->OnSlotDoubleClicked.AddUObject(this, &UNaItemSlotListPreset::EventDoubleClickedPreset); return Widget; } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp index c50cad0..103f5e5 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryCanvas.cpp @@ -135,23 +135,21 @@ void SNaInventoryCanvas::BindSlotEvents(int32 Position) } // Bind pointing and selection events with Position payload - // Use BindRaw/CreateRaw instead of BindSP/CreateSP to avoid cyclic TSharedPtr references - // (Canvas → Slots → SNaItemSlot → BoxSlot → delegates → Canvas) - BoxSlotWidget->OnPointed.BindRaw(this, &SNaInventoryCanvas::HandleSlotPointed, Position); - BoxSlotWidget->OnUnpointed.BindRaw(this, &SNaInventoryCanvas::HandleSlotUnpointed, Position); - BoxSlotWidget->OnSelected.BindRaw(this, &SNaInventoryCanvas::HandleSlotSelected, Position); - BoxSlotWidget->OnUnselected.BindRaw(this, &SNaInventoryCanvas::HandleSlotUnselected, Position); + BoxSlotWidget->OnPointed.BindSP(this, &SNaInventoryCanvas::HandleSlotPointed, Position); + BoxSlotWidget->OnUnpointed.BindSP(this, &SNaInventoryCanvas::HandleSlotUnpointed, Position); + BoxSlotWidget->OnSelected.BindSP(this, &SNaInventoryCanvas::HandleSlotSelected, Position); + BoxSlotWidget->OnUnselected.BindSP(this, &SNaInventoryCanvas::HandleSlotUnselected, Position); // Bind button events - BoxSlotWidget->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaInventoryCanvas::HandleSlotClicked, Position)); - BoxSlotWidget->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaInventoryCanvas::HandleSlotHovered, Position)); - BoxSlotWidget->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaInventoryCanvas::HandleSlotUnhovered, Position)); + BoxSlotWidget->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaInventoryCanvas::HandleSlotClicked, Position)); + BoxSlotWidget->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotHovered, Position)); + BoxSlotWidget->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaInventoryCanvas::HandleSlotUnhovered, Position)); // Bind mouse events - BoxSlotWidget->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseButtonDown, Position)); - BoxSlotWidget->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseButtonUp, Position)); - BoxSlotWidget->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotMouseMove, Position)); - BoxSlotWidget->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaInventoryCanvas::HandleSlotDoubleClicked, Position)); + BoxSlotWidget->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonDown, Position)); + BoxSlotWidget->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseButtonUp, Position)); + BoxSlotWidget->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotMouseMove, Position)); + BoxSlotWidget->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaInventoryCanvas::HandleSlotDoubleClicked, Position)); } /* Slot event handlers */ @@ -261,65 +259,54 @@ int SNaInventoryCanvas::GetSelectedPosition() /* UMG event callers */ void SNaInventoryCanvas::SlotPointedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotPointed.Broadcast(Position); + UMGRef->OnSlotPointed.Broadcast(Position); } void SNaInventoryCanvas::SlotUnpointedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnpointed.Broadcast(Position); + UMGRef->OnSlotUnpointed.Broadcast(Position); } void SNaInventoryCanvas::SlotSelectedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotSelected.Broadcast(Position); + UMGRef->OnSlotSelected.Broadcast(Position); } void SNaInventoryCanvas::SlotUnselectedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnselected.Broadcast(Position); + UMGRef->OnSlotUnselected.Broadcast(Position); } void SNaInventoryCanvas::SlotClickedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotClicked.Broadcast(Position); + UMGRef->OnSlotClicked.Broadcast(Position); } void SNaInventoryCanvas::SlotHoveredToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotHovered.Broadcast(Position); + UMGRef->OnSlotHovered.Broadcast(Position); } void SNaInventoryCanvas::SlotUnhoveredToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnhovered.Broadcast(Position); + UMGRef->OnSlotUnhovered.Broadcast(Position); } void SNaInventoryCanvas::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryCanvas::BindEventsToUMG() { - if (UMGRef.IsValid()) { - OnSlotPointed.AddRaw(this, &SNaInventoryCanvas::SlotPointedToUMG); - OnSlotUnpointed.AddRaw(this, &SNaInventoryCanvas::SlotUnpointedToUMG); - OnSlotSelected.AddRaw(this, &SNaInventoryCanvas::SlotSelectedToUMG); - OnSlotUnselected.AddRaw(this, &SNaInventoryCanvas::SlotUnselectedToUMG); - OnSlotClicked.AddRaw(this, &SNaInventoryCanvas::SlotClickedToUMG); - OnSlotHovered.AddRaw(this, &SNaInventoryCanvas::SlotHoveredToUMG); - OnSlotUnhovered.AddRaw(this, &SNaInventoryCanvas::SlotUnhoveredToUMG); - - OnSlotMouseButtonDown.AddRaw(this, &SNaInventoryCanvas::SlotMouseButtonDownToUMG); - OnSlotMouseButtonUp.AddRaw(this, &SNaInventoryCanvas::SlotMouseButtonUpToUMG); - OnSlotMouseMove.AddRaw(this, &SNaInventoryCanvas::SlotMouseMoveToUMG); - OnSlotDoubleClicked.AddRaw(this, &SNaInventoryCanvas::SlotDoubleClickedToUMG); + if (IsValid(UMGRef)) { + OnSlotPointed.AddSP(this, &SNaInventoryCanvas::SlotPointedToUMG); + OnSlotUnpointed.AddSP(this, &SNaInventoryCanvas::SlotUnpointedToUMG); + OnSlotSelected.AddSP(this, &SNaInventoryCanvas::SlotSelectedToUMG); + OnSlotUnselected.AddSP(this, &SNaInventoryCanvas::SlotUnselectedToUMG); + OnSlotClicked.AddSP(this, &SNaInventoryCanvas::SlotClickedToUMG); + OnSlotHovered.AddSP(this, &SNaInventoryCanvas::SlotHoveredToUMG); + OnSlotUnhovered.AddSP(this, &SNaInventoryCanvas::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddSP(this, &SNaInventoryCanvas::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddSP(this, &SNaInventoryCanvas::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddSP(this, &SNaInventoryCanvas::SlotDoubleClickedToUMG); } } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp index 2cdee92..85121d4 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaInventoryWrappedBox.cpp @@ -39,7 +39,6 @@ void SNaInventoryWrappedBox::Construct(const FArguments& InArgs) bFillDisabledToCompleteRectangle = InArgs._bFillDisabledToCompleteRectangle.Get(); RowLength = InArgs._RowLength.Get(); RowCount = InArgs._RowCount.Get(); - UMGRef = InArgs._FromUMG; /* Add panel */ ChildSlot @@ -217,64 +216,53 @@ int SNaInventoryWrappedBox::GetSelectedPosition() { /* UNaItemSlotList event callers */ void SNaInventoryWrappedBox::SlotPointedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotPointed.Broadcast(Position); + UMGRef->OnSlotPointed.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnpointedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnpointed.Broadcast(Position); + UMGRef->OnSlotUnpointed.Broadcast(Position); } void SNaInventoryWrappedBox::SlotSelectedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotSelected.Broadcast(Position); + UMGRef->OnSlotSelected.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnselectedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnselected.Broadcast(Position); + UMGRef->OnSlotUnselected.Broadcast(Position); } void SNaInventoryWrappedBox::SlotClickedToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotClicked.Broadcast(Position); + UMGRef->OnSlotClicked.Broadcast(Position); } void SNaInventoryWrappedBox::SlotHoveredToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotHovered.Broadcast(Position); + UMGRef->OnSlotHovered.Broadcast(Position); } void SNaInventoryWrappedBox::SlotUnhoveredToUMG(int Position) { - if (UMGRef.IsValid()) - UMGRef->OnSlotUnhovered.Broadcast(Position); + UMGRef->OnSlotUnhovered.Broadcast(Position); } void SNaInventoryWrappedBox::SlotMouseButtonDownToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseButtonDown.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotMouseButtonUpToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseButtonUp.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotMouseMoveToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotMouseMove.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::SlotDoubleClickedToUMG(int Position, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (UMGRef.IsValid()) - UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); + UMGRef->OnSlotDoubleClicked.Broadcast(Position, MyGeometry, MouseEvent); } void SNaInventoryWrappedBox::BindEventsToUMG() { - if (UMGRef.IsValid()) { - OnSlotPointed.AddRaw(this, &SNaInventoryWrappedBox::SlotPointedToUMG); - OnSlotUnpointed.AddRaw(this, &SNaInventoryWrappedBox::SlotUnpointedToUMG); - OnSlotSelected.AddRaw(this, &SNaInventoryWrappedBox::SlotSelectedToUMG); - OnSlotUnselected.AddRaw(this, &SNaInventoryWrappedBox::SlotUnselectedToUMG); - OnSlotClicked.AddRaw(this, &SNaInventoryWrappedBox::SlotClickedToUMG); - OnSlotHovered.AddRaw(this, &SNaInventoryWrappedBox::SlotHoveredToUMG); - OnSlotUnhovered.AddRaw(this, &SNaInventoryWrappedBox::SlotUnhoveredToUMG); - - OnSlotMouseButtonDown.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseButtonDownToUMG); - OnSlotMouseButtonUp.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseButtonUpToUMG); - OnSlotMouseMove.AddRaw(this, &SNaInventoryWrappedBox::SlotMouseMoveToUMG); - OnSlotDoubleClicked.AddRaw(this, &SNaInventoryWrappedBox::SlotDoubleClickedToUMG); + if (IsValid(UMGRef)) { + OnSlotPointed.AddSP(this, &SNaInventoryWrappedBox::SlotPointedToUMG); + OnSlotUnpointed.AddSP(this, &SNaInventoryWrappedBox::SlotUnpointedToUMG); + OnSlotSelected.AddSP(this, &SNaInventoryWrappedBox::SlotSelectedToUMG); + OnSlotUnselected.AddSP(this, &SNaInventoryWrappedBox::SlotUnselectedToUMG); + OnSlotClicked.AddSP(this, &SNaInventoryWrappedBox::SlotClickedToUMG); + OnSlotHovered.AddSP(this, &SNaInventoryWrappedBox::SlotHoveredToUMG); + OnSlotUnhovered.AddSP(this, &SNaInventoryWrappedBox::SlotUnhoveredToUMG); + + OnSlotMouseButtonDown.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonDownToUMG); + OnSlotMouseButtonUp.AddSP(this, &SNaInventoryWrappedBox::SlotMouseButtonUpToUMG); + OnSlotMouseMove.AddSP(this, &SNaInventoryWrappedBox::SlotMouseMoveToUMG); + OnSlotDoubleClicked.AddSP(this, &SNaInventoryWrappedBox::SlotDoubleClickedToUMG); } } \ No newline at end of file diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp index 8c4fdb1..392e7ee 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Private/UI/Widgets/SNaItemSlot.cpp @@ -108,36 +108,36 @@ void SNaItemSlot::BindItemSlotListEvents() { // If in item slot list, bind mouse events if (ItemSlotList && !ItemSlotList->IsInvalid() && ItemSlotList->GetContainer()->Inventory->IsValidSlot(PositionInSlotList) && BoxSlot.IsValid()) { - BoxSlot->OnPointed.BindRaw(this, &SNaItemSlot::SlotPointedToList); - BoxSlot->OnUnpointed.BindRaw(this, &SNaItemSlot::SlotUnpointedToList); - BoxSlot->OnSelected.BindRaw(this, &SNaItemSlot::SlotSelectedToList); - BoxSlot->OnUnselected.BindRaw(this, &SNaItemSlot::SlotUnselectedToList); - BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaItemSlot::SlotClickedToList)); - BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::SlotHoveredToList)); - BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::SlotUnhoveredToList)); + BoxSlot->OnPointed.BindSP(this, &SNaItemSlot::SlotPointedToList); + BoxSlot->OnUnpointed.BindSP(this, &SNaItemSlot::SlotUnpointedToList); + BoxSlot->OnSelected.BindSP(this, &SNaItemSlot::SlotSelectedToList); + BoxSlot->OnUnselected.BindSP(this, &SNaItemSlot::SlotUnselectedToList); + BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaItemSlot::SlotClickedToList)); + BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::SlotHoveredToList)); + BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::SlotUnhoveredToList)); - BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseButtonDownToList)); - BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseButtonUpToList)); - BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotMouseMoveToList)); - BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::SlotDoubleClickedToList)); + BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseButtonDownToList)); + BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseButtonUpToList)); + BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotMouseMoveToList)); + BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaItemSlot::SlotDoubleClickedToList)); } // Or bind to null functions (no behavior). else { - BoxSlot->OnPointed.BindRaw(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnUnpointed.BindRaw(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnSelected.BindRaw(this, &SNaItemSlot::ExecNoList); - BoxSlot->OnUnselected.BindRaw(this, &SNaItemSlot::ExecNoList); - BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateRaw(this, &SNaItemSlot::ExecNoListClicked)); - BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::ExecNoList)); - BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateRaw(this, &SNaItemSlot::ExecNoList)); - - BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); - BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateRaw(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->OnPointed.BindSP(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnUnpointed.BindSP(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnSelected.BindSP(this, &SNaItemSlot::ExecNoList); + BoxSlot->OnUnselected.BindSP(this, &SNaItemSlot::ExecNoList); + BoxSlot->GetButton()->SetOnClicked(FOnClicked::CreateSP(this, &SNaItemSlot::ExecNoListClicked)); + BoxSlot->GetButton()->SetOnHovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::ExecNoList)); + BoxSlot->GetButton()->SetOnUnhovered(FSimpleDelegate::CreateSP(this, &SNaItemSlot::ExecNoList)); + + BoxSlot->GetButton()->SetOnMouseButtonDown(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseButtonUp(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseMove(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); + BoxSlot->GetButton()->SetOnMouseDoubleClick(FPointerEventHandler::CreateSP(this, &SNaItemSlot::ExecNoListMouse)); } } diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h index b3cb7a7..db47477 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/UMG/NaItemSlotList.h @@ -61,7 +61,7 @@ class NAITEMSYSTEM_API UNaItemSlotList : public UWidget protected: - TSharedPtr List; + TSharedRef List; public: diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h index 9cc6506..4c57db5 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryCanvas.h @@ -164,7 +164,7 @@ class NAITEMSYSTEM_API SNaInventoryCanvas : public SCompoundWidget protected: - TWeakObjectPtr UMGRef = nullptr; + UNaInventoryCanvas* UMGRef = nullptr; void BindEventsToUMG(); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h index 670e023..1735abc 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaInventoryWrappedBox.h @@ -34,12 +34,12 @@ class NAITEMSYSTEM_API SNaInventoryWrappedBox : public SCompoundWidget _FromUMG = nullptr; } - SLATE_ATTRIBUTE(FNaItemSlotStyle*, StylePtr) - SLATE_ATTRIBUTE(UNaItemInventoryComponent*, Container) + SLATE_ATTRIBUTE(TSharedRef, StylePtr) + SLATE_ATTRIBUTE(TWeakObjectPtr, Container) SLATE_ATTRIBUTE(bool, bFillDisabledToCompleteRectangle) /* If true, it will add disabled slots to the end to fill a complete rectangle. */ SLATE_ATTRIBUTE(int, RowLength) /* How many boxes in a row */ SLATE_ATTRIBUTE(int, RowCount) // Amount of rows. If this count is not enough for container, it will be ignored and the row count will be automatically calculated from container. - SLATE_ARGUMENT(UNaItemSlotList*, FromUMG) /* If generated from UNaItemSlotList, set this value */ + SLATE_ARGUMENT(TWeakObjectPtr, FromUMG) /* If generated from UNaItemSlotList, set this value */ SLATE_END_ARGS() @@ -183,7 +183,7 @@ class NAITEMSYSTEM_API SNaInventoryWrappedBox : public SCompoundWidget protected: - TWeakObjectPtr UMGRef = nullptr; + UNaItemSlotList* UMGRef = nullptr; void BindEventsToUMG(); diff --git a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h index 3ee7190..af19491 100644 --- a/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h +++ b/Plugins/NaItemSystem/Source/NaItemSystem/Public/UI/Widgets/SNaItemSlot.h @@ -86,15 +86,15 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget public: SLATE_BEGIN_ARGS(SNaItemSlot) { - _StylePtr = nullptr; + _Style = FNaItemSlotStyle::DefaultStyle; _WorldContext = nullptr; _Stack = nullptr; _bIsDisabled = false; } - SLATE_ATTRIBUTE(FNaItemSlotStyle*, StylePtr) + SLATE_ATTRIBUTE(FNaItemSlotStyle, Style) SLATE_ATTRIBUTE(UObject*, WorldContext) // object as world context (indicating world) - SLATE_ATTRIBUTE(UNaItemStack*, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ + SLATE_ATTRIBUTE(TWeakObjectPtr, Stack) /* Ptr to corresponding item stack. Null ptr means empty */ SLATE_ATTRIBUTE(bool, bIsDisabled) /* If true, this slot will be regarded as disabled, ignoring the value of ItemEntry and bIsEmpty() */ SLATE_END_ARGS() @@ -118,7 +118,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /* Style */ - FNaItemSlotStyle* StylePtr = nullptr; + TSharedPtr StylePtr = nullptr; FSlateFontInfo Font = FTextBlockStyle::GetDefault().Font; @@ -161,7 +161,7 @@ class NAITEMSYSTEM_API SNaItemSlot : public SCompoundWidget /* If this ptr is null, this slot is not in an SNaItemSlotList */ - SNaInventoryWrappedBox* ItemSlotList = nullptr; + TWeakPtr ItemSlotList = nullptr; int PositionInSlotList = -1;