diff --git a/Content/Input/Actions/IA_Inventory.uasset b/Content/Input/Actions/IA_Inventory.uasset new file mode 100644 index 0000000..0044fb9 Binary files /dev/null and b/Content/Input/Actions/IA_Inventory.uasset differ diff --git a/Content/Input/IMC_Default.uasset b/Content/Input/IMC_Default.uasset index 6430975..ac07702 100644 Binary files a/Content/Input/IMC_Default.uasset and b/Content/Input/IMC_Default.uasset differ diff --git a/Content/ThirdPerson/Blueprints/BP_ThirdPersonPlayerController.uasset b/Content/ThirdPerson/Blueprints/BP_ThirdPersonPlayerController.uasset index bb6f4e5..a96ba83 100644 Binary files a/Content/ThirdPerson/Blueprints/BP_ThirdPersonPlayerController.uasset and b/Content/ThirdPerson/Blueprints/BP_ThirdPersonPlayerController.uasset differ diff --git a/Content/UI/Inventory/WBP_Equipment.uasset b/Content/UI/Inventory/WBP_Equipment.uasset new file mode 100644 index 0000000..0e2b114 Binary files /dev/null and b/Content/UI/Inventory/WBP_Equipment.uasset differ diff --git a/Content/UI/Inventory/WBP_EquipmentInventoryTAB.uasset b/Content/UI/Inventory/WBP_EquipmentInventoryTAB.uasset new file mode 100644 index 0000000..609321d Binary files /dev/null and b/Content/UI/Inventory/WBP_EquipmentInventoryTAB.uasset differ diff --git a/Content/UI/Inventory/WBP_EquipmentSlot.uasset b/Content/UI/Inventory/WBP_EquipmentSlot.uasset new file mode 100644 index 0000000..d965150 Binary files /dev/null and b/Content/UI/Inventory/WBP_EquipmentSlot.uasset differ diff --git a/Content/UI/Inventory/WBP_InventoryContainerList.uasset b/Content/UI/Inventory/WBP_InventoryContainerList.uasset new file mode 100644 index 0000000..855f486 Binary files /dev/null and b/Content/UI/Inventory/WBP_InventoryContainerList.uasset differ diff --git a/Content/UI/Inventory/WBP_InventoryGrid.uasset b/Content/UI/Inventory/WBP_InventoryGrid.uasset new file mode 100644 index 0000000..b5356ed Binary files /dev/null and b/Content/UI/Inventory/WBP_InventoryGrid.uasset differ diff --git a/Content/UI/Inventory/WBP_Item.uasset b/Content/UI/Inventory/WBP_Item.uasset new file mode 100644 index 0000000..76085b9 Binary files /dev/null and b/Content/UI/Inventory/WBP_Item.uasset differ diff --git a/Content/UI/Inventory/WBP_ItemList.uasset b/Content/UI/Inventory/WBP_ItemList.uasset new file mode 100644 index 0000000..b5b9a31 Binary files /dev/null and b/Content/UI/Inventory/WBP_ItemList.uasset differ diff --git a/Content/UI/Inventory/WBP_QuickSlotList.uasset b/Content/UI/Inventory/WBP_QuickSlotList.uasset new file mode 100644 index 0000000..c299a59 Binary files /dev/null and b/Content/UI/Inventory/WBP_QuickSlotList.uasset differ diff --git a/Source/KingshearthLegacy/Inventory/InventoryTypes.h b/Source/KingshearthLegacy/Inventory/InventoryTypes.h new file mode 100644 index 0000000..97c922b --- /dev/null +++ b/Source/KingshearthLegacy/Inventory/InventoryTypes.h @@ -0,0 +1,104 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "InventoryTypes.generated.h" + +class UTexture2D; + +/** + * Where an equipped item is displayed. Purely a display label for WBP_EquipmentSlot - there is + * no attachment-point/socket hierarchy and no validation that an item is appropriate for the + * slot it's put in. Multiple slots can share the same type (e.g. two Back slots, two Belt + * slots) - disambiguate those via FEquipmentSlotData::SlotDisplayNameOverride. + */ +UENUM(BlueprintType) +enum class EEquipmentSlotType : uint8 +{ + Head, + Body, + Hands, + Legs, + Feet, + Back, + Belt +}; + +/** + * A single inventory/equipment/quick-slot item as far as the UI is concerned. Mass and Volume + * describe the item's physical footprint for weight/capacity tracking - they never affect how + * large the item appears on screen, since every item always renders at the same fixed 2x2 UI + * size (see UInventoryItemWidget). An ItemID of NAME_None means the slot is empty. + */ +USTRUCT(BlueprintType) +struct FItemData +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + FName ItemID; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + FText DisplayName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + TObjectPtr Icon = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + int32 Quantity = 1; + + /** Physical mass in kg, used for carry-weight totals - not UI size. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + float Mass = 0.0f; + + /** Physical volume in liters, used for carry-capacity totals - not UI size. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + float Volume = 0.0f; + + /** Optional basic state label shown alongside the item (e.g. "Broken", "New", "Equipped"). Empty means no state to show. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item") + FText StateText; + + bool IsEmpty() const { return ItemID.IsNone(); } +}; + +/** + * One equipment slot's contents. SlotType is a display label only - WBP_EquipmentSlot shows + * whatever it's given here and does not validate that EquippedItem actually belongs in SlotType. + */ +USTRUCT(BlueprintType) +struct FEquipmentSlotData +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment") + EEquipmentSlotType SlotType = EEquipmentSlotType::Head; + + /** Optional override shown instead of SlotType's enum name - use this to tell apart slots that share a type (e.g. "Back 1"/"Back 2", "Belt 1"/"Belt 2"). Empty falls back to the SlotType display name. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment") + FText SlotDisplayNameOverride; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Equipment") + FItemData EquippedItem; +}; + +/** + * One inventory container's contents (the player's own bag, a chest being looted, ...). Items + * are a flat, ordered list - there is no physical/spatial grid position per item, only UI-list + * ordering (no Tetris-style placement). + */ +USTRUCT(BlueprintType) +struct FInventoryContainerInfo +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") + FName ContainerID; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") + FText DisplayName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") + TArray Items; +}; diff --git a/Source/KingshearthLegacy/KingshearthLegacy.Build.cs b/Source/KingshearthLegacy/KingshearthLegacy.Build.cs index d53dece..f6f756b 100644 --- a/Source/KingshearthLegacy/KingshearthLegacy.Build.cs +++ b/Source/KingshearthLegacy/KingshearthLegacy.Build.cs @@ -32,7 +32,9 @@ public class KingshearthLegacy : ModuleRules "KingshearthLegacy/AttributeSets", "KingshearthLegacy/GameplayEffects", "KingshearthLegacy/Interaction", + "KingshearthLegacy/Inventory", "KingshearthLegacy/UI/Menu", + "KingshearthLegacy/UI/Inventory", "KingshearthLegacy/Variant_Platforming", "KingshearthLegacy/Variant_Platforming/Animation", "KingshearthLegacy/Variant_Combat", diff --git a/Source/KingshearthLegacy/KingshearthLegacyPlayerController.cpp b/Source/KingshearthLegacy/KingshearthLegacyPlayerController.cpp index 603d216..d128f50 100644 --- a/Source/KingshearthLegacy/KingshearthLegacyPlayerController.cpp +++ b/Source/KingshearthLegacy/KingshearthLegacyPlayerController.cpp @@ -13,6 +13,7 @@ #include "UI/PlayerHUDWidget.h" #include "UI/Menu/InGameMenuWidget.h" #include "UI/Menu/MenuManagerSubsystem.h" +#include "UI/Inventory/EquipmentInventoryTabWidget.h" #include "KingshearthLegacyCharacter.h" void AKingshearthLegacyPlayerController::BeginPlay() @@ -70,6 +71,11 @@ void AKingshearthLegacyPlayerController::SetupInputComponent() { EnhancedInputComponent->BindAction(MenuAction, ETriggerEvent::Started, this, &AKingshearthLegacyPlayerController::ToggleInGameMenu); } + + if (InventoryAction) + { + EnhancedInputComponent->BindAction(InventoryAction, ETriggerEvent::Started, this, &AKingshearthLegacyPlayerController::ToggleInventory); + } } } } @@ -137,3 +143,27 @@ void AKingshearthLegacyPlayerController::ToggleInGameMenu() MenuManager->PushScreen(InGameMenuClass); } } + +void AKingshearthLegacyPlayerController::ToggleInventory() +{ + if (!IsLocalPlayerController() || !InventoryScreenClass) + { + return; + } + + UGameInstance* GI = GetGameInstance(); + UMenuManagerSubsystem* MenuManager = GI ? GI->GetSubsystem() : nullptr; + if (!MenuManager) + { + return; + } + + if (MenuManager->IsAnyScreenOpen()) + { + MenuManager->PopScreen(); + } + else + { + MenuManager->PushScreen(InventoryScreenClass); + } +} diff --git a/Source/KingshearthLegacy/KingshearthLegacyPlayerController.h b/Source/KingshearthLegacy/KingshearthLegacyPlayerController.h index e35931c..a3695ed 100644 --- a/Source/KingshearthLegacy/KingshearthLegacyPlayerController.h +++ b/Source/KingshearthLegacy/KingshearthLegacyPlayerController.h @@ -11,6 +11,7 @@ class UInputAction; class UUserWidget; class UPlayerHUDWidget; class UInGameMenuWidget; +class UEquipmentInventoryTabWidget; /** * Basic PlayerController class for a third person game @@ -59,6 +60,14 @@ protected: UPROPERTY(EditAnywhere, Category = "Input|Input Mappings") TObjectPtr MenuAction; + /** Inventory/equipment screen, pushed/popped through the menu manager by ToggleInventory */ + UPROPERTY(EditAnywhere, Category = "UI") + TSubclassOf InventoryScreenClass; + + /** Input action that opens the inventory screen, or closes the top menu screen if one is already open */ + UPROPERTY(EditAnywhere, Category = "Input|Input Mappings") + TObjectPtr InventoryAction; + /** Gameplay initialization */ virtual void BeginPlay() override; @@ -75,4 +84,8 @@ protected: UFUNCTION(BlueprintCallable, Category = "UI") void ToggleInGameMenu(); + /** Opens InventoryScreenClass, or closes the top menu screen if one is already open */ + UFUNCTION(BlueprintCallable, Category = "UI") + void ToggleInventory(); + }; diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.cpp new file mode 100644 index 0000000..b2811d6 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.cpp @@ -0,0 +1,86 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "EquipmentInventoryTabWidget.h" +#include "UI/Inventory/EquipmentWidget.h" +#include "UI/Inventory/QuickSlotListWidget.h" +#include "UI/Inventory/InventoryItemListWidget.h" +#include "UI/Inventory/InventoryGridWidget.h" +#include "KingshearthLegacy.h" +#include "InputCoreTypes.h" + +UEquipmentInventoryTabWidget::UEquipmentInventoryTabWidget(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + // Needed so the menu manager's SetWidgetToFocus (see UMenuManagerSubsystem::RefreshInputMode) + // can actually give this screen keyboard focus - otherwise NativeOnPreviewKeyDown below never fires. + // Only takes effect set here, at construction - UUserWidget::SetIsFocusable asserts if called later. + SetIsFocusable(true); +} + +FReply UEquipmentInventoryTabWidget::NativeOnPreviewKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) +{ + if (InKeyEvent.GetKey() == EKeys::Tab) + { + Close(); + return FReply::Handled(); + } + + return Super::NativeOnPreviewKeyDown(InGeometry, InKeyEvent); +} + +void UEquipmentInventoryTabWidget::InitializeInventory(const TArray& InEquipmentSlots, const TArray& InBigQuickSlots, const TArray& InSmallQuickSlots, const TArray& InInventoryItems) +{ + UE_LOG(LogKingshearthLegacy, Log, TEXT("[Inventory] EquipmentInventoryTab InitializeInventory: WBP_Equipment bound: %s, WBP_QuickSlotList bound: %s, WBP_ItemList bound: %s, WBP_InventoryGrid bound: %s"), + WBP_Equipment ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UEquipmentWidget"), + WBP_QuickSlotList ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UQuickSlotListWidget"), + WBP_ItemList ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UInventoryItemListWidget"), + WBP_InventoryGrid ? TEXT("yes") : TEXT("NO - check its Name in the Designer tree matches the native property, and that it's reparented to UInventoryGridWidget")); + + if (WBP_Equipment) + { + WBP_Equipment->SetEquipmentSlots(InEquipmentSlots); + } + + if (WBP_QuickSlotList) + { + WBP_QuickSlotList->SetQuickSlots(InBigQuickSlots, InSmallQuickSlots); + } + + if (WBP_ItemList) + { + WBP_ItemList->SetItems(InInventoryItems); + } + + if (WBP_InventoryGrid) + { + WBP_InventoryGrid->SetItems(InInventoryItems); + } + + ShowInventoryGridView(); +} + +void UEquipmentInventoryTabWidget::ShowItemListView() +{ + if (WBP_ItemList) + { + WBP_ItemList->SetVisibility(ESlateVisibility::Visible); + } + + if (WBP_InventoryGrid) + { + WBP_InventoryGrid->SetVisibility(ESlateVisibility::Collapsed); + } +} + +void UEquipmentInventoryTabWidget::ShowInventoryGridView() +{ + if (WBP_ItemList) + { + WBP_ItemList->SetVisibility(ESlateVisibility::Collapsed); + } + + if (WBP_InventoryGrid) + { + WBP_InventoryGrid->SetVisibility(ESlateVisibility::Visible); + } +} diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.h b/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.h new file mode 100644 index 0000000..0f7c1b5 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentInventoryTabWidget.h @@ -0,0 +1,87 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UI/Menu/MenuScreenWidget.h" +#include "Inventory/InventoryTypes.h" +#include "EquipmentInventoryTabWidget.generated.h" + +class UEquipmentWidget; +class UQuickSlotListWidget; +class UInventoryItemListWidget; +class UInventoryGridWidget; + +/** + * Main inventory screen - a shallow wrapper coordinating its child widgets. Pushed/popped + * through the menu manager like any other UMenuScreenWidget (see + * AKingshearthLegacyPlayerController::ToggleInventory), so opening/closing it gets the usual UI + * input mode + cursor handling for free, and an optional "BackButton" (inherited from + * UMenuScreenWidget) closes it the same way any other menu screen closes: + * + * WBP_EquipmentInventoryTAB + * |-- WBP_Equipment + * | `-- WBP_EquipmentSlot + * |-- WBP_QuickSlotList + * `-- Inventory View + * |-- WBP_ItemList + * `-- WBP_InventoryGrid + * + * Owns no inventory logic itself - it only forwards data supplied by the inventory/equipment + * system to its children, and switches which of WBP_ItemList / WBP_InventoryGrid is visible for + * the "Inventory View" section (both are kept populated with the same items; only one is shown + * at a time). + * + * Nest instances named exactly "WBP_Equipment", "WBP_QuickSlotList", "WBP_ItemList" and + * "WBP_InventoryGrid" in the Designer tree, reparented to their respective C++ base classes. + */ +UCLASS(abstract) +class UEquipmentInventoryTabWidget : public UMenuScreenWidget +{ + GENERATED_BODY() + +public: + + UEquipmentInventoryTabWidget(const FObjectInitializer& ObjectInitializer); + + /** Populates every child widget. Call once the inventory/equipment system has data ready (e.g. right after this screen is pushed). */ + UFUNCTION(BlueprintCallable, Category = "Inventory") + void InitializeInventory(const TArray& InEquipmentSlots, const TArray& InBigQuickSlots, const TArray& InSmallQuickSlots, const TArray& InInventoryItems); + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void ShowItemListView(); + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void ShowInventoryGridView(); + +protected: + + /** + * While this screen is open, the menu manager puts input in UI-only mode - the PlayerController + * stops receiving Tab presses entirely, so re-pressing Tab can only be caught here, at the + * focused widget itself. Requires bIsFocusable (set true in the constructor) so the menu + * manager's SetWidgetToFocus call actually succeeds. + * + * Uses the Preview (tunneling) pass rather than NativeOnKeyDown (bubbling) - Tab is Slate's + * default "focus next widget" navigation key, and that default navigation runs as a fallback + * once the bubbling pass goes unhandled, which pre-empts NativeOnKeyDown ever getting a + * chance to close the screen. Intercepting in Preview, which runs before that fallback, avoids it. + */ + virtual FReply NativeOnPreviewKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override; + + /** Nested WBP_Equipment instance - named exactly "WBP_Equipment" in the Designer tree. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr WBP_Equipment; + + /** Nested WBP_QuickSlotList instance - named exactly "WBP_QuickSlotList" in the Designer tree. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr WBP_QuickSlotList; + + /** Nested WBP_ItemList instance - named exactly "WBP_ItemList" in the Designer tree, part of the Inventory View toggle. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr WBP_ItemList; + + /** Nested WBP_InventoryGrid instance - named exactly "WBP_InventoryGrid" in the Designer tree, part of the Inventory View toggle. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr WBP_InventoryGrid; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotListEntry.h b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotListEntry.h new file mode 100644 index 0000000..3964bf8 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotListEntry.h @@ -0,0 +1,24 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "Inventory/InventoryTypes.h" +#include "EquipmentSlotListEntry.generated.h" + +/** + * Plain UObject wrapper around one FEquipmentSlotData - UListView items must be UObjects, not + * structs. Used by WBP_Equipment before calling SetListItems, so each row's UEquipmentSlotWidget + * receives one of these via IUserObjectListEntry. + */ +UCLASS() +class UEquipmentSlotListEntry : public UObject +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintReadOnly, Category = "Equipment") + FEquipmentSlotData Slot; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.cpp new file mode 100644 index 0000000..abec3a1 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.cpp @@ -0,0 +1,58 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "EquipmentSlotWidget.h" +#include "UI/Inventory/EquipmentSlotListEntry.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" + +void UEquipmentSlotWidget::SetSlotData(const FEquipmentSlotData& InSlotData) +{ + SlotData = InSlotData; + ApplySlotVisuals(); +} + +void UEquipmentSlotWidget::NativeOnListItemObjectSet(UObject* ListItemObject) +{ + if (const UEquipmentSlotListEntry* ListItem = Cast(ListItemObject)) + { + SetSlotData(ListItem->Slot); + } +} + +void UEquipmentSlotWidget::ApplySlotVisuals() +{ + const bool bHasItem = !SlotData.EquippedItem.IsEmpty(); + + if (Icon) + { + if (bHasItem && SlotData.EquippedItem.Icon) + { + Icon->SetBrushFromTexture(SlotData.EquippedItem.Icon); + Icon->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + Icon->SetVisibility(ESlateVisibility::Collapsed); + } + } + + if (QuantityText) + { + if (bHasItem && SlotData.EquippedItem.Quantity > 1) + { + QuantityText->SetText(FText::AsNumber(SlotData.EquippedItem.Quantity)); + QuantityText->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + QuantityText->SetVisibility(ESlateVisibility::Collapsed); + } + } + + if (SlotLabel) + { + SlotLabel->SetText(!SlotData.SlotDisplayNameOverride.IsEmpty() + ? SlotData.SlotDisplayNameOverride + : UEnum::GetDisplayValueAsText(SlotData.SlotType)); + } +} diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.h b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.h new file mode 100644 index 0000000..5922b37 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentSlotWidget.h @@ -0,0 +1,57 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Blueprint/IUserObjectListEntry.h" +#include "Inventory/InventoryTypes.h" +#include "EquipmentSlotWidget.generated.h" + +class UImage; +class UTextBlock; + +/** + * A single equipment slot's visuals. This is a display slot only - it shows whatever + * FEquipmentSlotData it is given and does not validate that the item is appropriate for the + * slot, and does not represent an attachment point/socket on the character mesh. + * + * Name your widgets exactly "Icon" (Image), "QuantityText" (TextBlock) and "SlotLabel" + * (TextBlock) in the Designer and population is handled here in C++; no BP event graph logic + * needed. + */ +UCLASS(abstract) +class UEquipmentSlotWidget : public UUserWidget, public IUserObjectListEntry +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Equipment") + void SetSlotData(const FEquipmentSlotData& InSlotData); + + UFUNCTION(BlueprintPure, Category = "Equipment") + const FEquipmentSlotData& GetSlotData() const { return SlotData; } + +protected: + + /** IUserObjectListEntry - called by the owning ListView (WBP_Equipment) when this row is assigned an UEquipmentSlotListEntry. */ + virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override; + + void ApplySlotVisuals(); + + /** Equipped item's icon - bind an Image named exactly "Icon" in the Designer. Hidden automatically when the slot is empty. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr Icon; + + /** Equipped item's stack count - bind a TextBlock named exactly "QuantityText" in the Designer. Hidden automatically when Quantity <= 1. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr QuantityText; + + /** Slot type label (e.g. "Head", "Back 1") - bind a TextBlock named exactly "SlotLabel" in the Designer. Prefers SlotData.SlotDisplayNameOverride when set, falling back to the SlotType enum name. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr SlotLabel; + + UPROPERTY(BlueprintReadOnly, Category = "Equipment") + FEquipmentSlotData SlotData; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.cpp new file mode 100644 index 0000000..fa70084 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.cpp @@ -0,0 +1,29 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "EquipmentWidget.h" +#include "UI/Inventory/EquipmentSlotListEntry.h" +#include "Components/ListView.h" + +void UEquipmentWidget::SetEquipmentSlots(const TArray& InSlots) +{ + if (!SlotsList) + { + return; + } + + SlotsList->ClearListItems(); + ListItems.Reset(InSlots.Num()); + + TArray SlotObjects; + SlotObjects.Reserve(InSlots.Num()); + + for (const FEquipmentSlotData& SlotData : InSlots) + { + UEquipmentSlotListEntry* Entry = NewObject(this); + Entry->Slot = SlotData; + ListItems.Add(Entry); + SlotObjects.Add(Entry); + } + + SlotsList->SetListItems(SlotObjects); +} diff --git a/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.h b/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.h new file mode 100644 index 0000000..fc6f3ad --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/EquipmentWidget.h @@ -0,0 +1,38 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Inventory/InventoryTypes.h" +#include "EquipmentWidget.generated.h" + +class UEquipmentSlotListEntry; +class UListView; + +/** + * The character's full set of equipment slots. + * + * Name a ListView exactly "SlotsList" in the Designer, with its Entry Widget Class set to a WBP + * subclass of UEquipmentSlotWidget, and population is handled here in C++. + */ +UCLASS(abstract) +class UEquipmentWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Equipment") + void SetEquipmentSlots(const TArray& InSlots); + +protected: + + /** Named exactly "SlotsList" in the Designer, Entry Widget Class set to a WBP subclass of UEquipmentSlotWidget. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr SlotsList; + + /** UObject wrappers backing SlotsList - kept alive here so the ListView's UObjects don't get GC'd. */ + UPROPERTY() + TArray> ListItems; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.cpp new file mode 100644 index 0000000..411a59a --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.cpp @@ -0,0 +1,42 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "InventoryContainerListWidget.h" +#include "UI/Inventory/InventoryGridWidget.h" +#include "Components/TextBlock.h" + +void UInventoryContainerListWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + SetExpanded(bExpanded); +} + +void UInventoryContainerListWidget::SetContainerData(const FInventoryContainerInfo& InContainerData) +{ + ContainerData = InContainerData; + + if (ContainerLabel) + { + ContainerLabel->SetText(ContainerData.DisplayName); + } + + if (WBP_InventoryGrid) + { + WBP_InventoryGrid->SetItems(ContainerData.Items); + } +} + +void UInventoryContainerListWidget::SetExpanded(bool bInExpanded) +{ + bExpanded = bInExpanded; + + if (WBP_InventoryGrid) + { + WBP_InventoryGrid->SetVisibility(bExpanded ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + } +} + +void UInventoryContainerListWidget::ToggleExpanded() +{ + SetExpanded(!bExpanded); +} diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.h b/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.h new file mode 100644 index 0000000..9c49d23 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryContainerListWidget.h @@ -0,0 +1,67 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Inventory/InventoryTypes.h" +#include "InventoryContainerListWidget.generated.h" + +class UInventoryGridWidget; +class UTextBlock; + +/** + * A labeled, collapsible view of one inventory container (the player's own bag, a chest being + * looted, ...): + * + * WBP_InventoryContainerList + * |-- Container Label + * `-- Collapsible Inventory Grid + * `-- WBP_InventoryGrid + * `-- WBP_Item + * + * Name a TextBlock exactly "ContainerLabel" in the Designer, and nest a WBP subclass of + * UInventoryGridWidget named exactly "WBP_InventoryGrid". SetContainerData populates the label + * and forwards the container's items to the nested grid; SetExpanded/ToggleExpanded show or + * collapse it. Wire an expand/collapse button to ToggleExpanded in the Designer/event graph. + */ +UCLASS(abstract) +class UInventoryContainerListWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void SetContainerData(const FInventoryContainerInfo& InContainerData); + + UFUNCTION(BlueprintPure, Category = "Inventory") + const FInventoryContainerInfo& GetContainerData() const { return ContainerData; } + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void SetExpanded(bool bInExpanded); + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void ToggleExpanded(); + + UFUNCTION(BlueprintPure, Category = "Inventory") + bool IsExpanded() const { return bExpanded; } + +protected: + + virtual void NativeConstruct() override; + + /** Named exactly "ContainerLabel" in the Designer. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr ContainerLabel; + + /** Nested WBP_InventoryGrid instance - named exactly "WBP_InventoryGrid" in the Designer tree, reparented to a UInventoryGridWidget subclass. Collapsed/shown by SetExpanded. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr WBP_InventoryGrid; + + UPROPERTY(BlueprintReadOnly, Category = "Inventory") + FInventoryContainerInfo ContainerData; + + UPROPERTY(EditAnywhere, Category = "Inventory") + bool bExpanded = true; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.cpp new file mode 100644 index 0000000..0565717 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.cpp @@ -0,0 +1,35 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "InventoryGridWidget.h" +#include "UI/Inventory/InventoryItemListEntry.h" +#include "Components/TileView.h" + +void UInventoryGridWidget::SetItems(const TArray& InItems) +{ + Items = InItems; + PopulateGrid(); +} + +void UInventoryGridWidget::PopulateGrid() +{ + if (!ItemsGrid) + { + return; + } + + ItemsGrid->ClearListItems(); + ListItems.Reset(Items.Num()); + + TArray ItemObjects; + ItemObjects.Reserve(Items.Num()); + + for (const FItemData& Item : Items) + { + UInventoryItemListEntry* Entry = NewObject(this); + Entry->Item = Item; + ListItems.Add(Entry); + ItemObjects.Add(Entry); + } + + ItemsGrid->SetListItems(ItemObjects); +} diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.h b/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.h new file mode 100644 index 0000000..70d4825 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryGridWidget.h @@ -0,0 +1,49 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Inventory/InventoryTypes.h" +#include "InventoryGridWidget.generated.h" + +class UInventoryItemListEntry; +class UTileView; + +/** + * A UI grid of items. This is purely a tiled layout - every item occupies the same fixed-size + * 2x2 tile no matter its Mass/Volume, and items have no spatial position of their own (no + * physical/Tetris-style grid placement). Carries no container/label concept of its own - that's + * WBP_InventoryContainerList's job when a labeled, collapsible section is needed. + * + * Name a TileView exactly "ItemsGrid" in the Designer, with its Entry Widget Class set to a WBP + * subclass of UInventoryItemWidget, and population is handled here in C++. + */ +UCLASS(abstract) +class UInventoryGridWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Inventory") + void SetItems(const TArray& InItems); + + UFUNCTION(BlueprintPure, Category = "Inventory") + const TArray& GetItems() const { return Items; } + +protected: + + void PopulateGrid(); + + /** Named exactly "ItemsGrid" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr ItemsGrid; + + UPROPERTY(BlueprintReadOnly, Category = "Inventory") + TArray Items; + + /** UObject wrappers backing ItemsGrid - kept alive here so the TileView's UObjects don't get GC'd. */ + UPROPERTY() + TArray> ListItems; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryItemListEntry.h b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListEntry.h new file mode 100644 index 0000000..a3b26ae --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListEntry.h @@ -0,0 +1,25 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "Inventory/InventoryTypes.h" +#include "InventoryItemListEntry.generated.h" + +/** + * Plain UObject wrapper around one FItemData - UListView/UTileView items must be UObjects, not + * structs. Used by WBP_ItemList, WBP_InventoryGrid and WBP_QuickSlotList before calling + * SetListItems, so each row's UInventoryItemWidget receives one of these via + * IUserObjectListEntry. + */ +UCLASS() +class UInventoryItemListEntry : public UObject +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintReadOnly, Category = "Item") + FItemData Item; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.cpp new file mode 100644 index 0000000..b719e18 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.cpp @@ -0,0 +1,29 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "InventoryItemListWidget.h" +#include "UI/Inventory/InventoryItemListEntry.h" +#include "Components/ListView.h" + +void UInventoryItemListWidget::SetItems(const TArray& InItems) +{ + if (!ItemsList) + { + return; + } + + ItemsList->ClearListItems(); + ListItems.Reset(InItems.Num()); + + TArray ItemObjects; + ItemObjects.Reserve(InItems.Num()); + + for (const FItemData& Item : InItems) + { + UInventoryItemListEntry* Entry = NewObject(this); + Entry->Item = Item; + ListItems.Add(Entry); + ItemObjects.Add(Entry); + } + + ItemsList->SetListItems(ItemObjects); +} diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.h b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.h new file mode 100644 index 0000000..5574d76 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryItemListWidget.h @@ -0,0 +1,41 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Inventory/InventoryTypes.h" +#include "InventoryItemListWidget.generated.h" + +class UInventoryItemListEntry; +class UListView; + +/** + * A simple vertical list of items - not a physical/spatial grid, just an ordered list. Used as + * the list-view alternative to UInventoryGridWidget inside WBP_EquipmentInventoryTAB's Inventory + * View (see UEquipmentInventoryTabWidget::ShowItemListView), and reusable standalone anywhere + * else that only needs a plain item list (a crafting recipe's ingredients, a shop's stock, ...). + * + * Name a ListView exactly "ItemsList" in the Designer, with its Entry Widget Class set to a WBP + * subclass of UInventoryItemWidget, and population is handled here in C++. + */ +UCLASS(abstract) +class UInventoryItemListWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Item List") + void SetItems(const TArray& InItems); + +protected: + + /** Named exactly "ItemsList" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr ItemsList; + + /** UObject wrappers backing ItemsList - kept alive here so the ListView's UObjects don't get GC'd. */ + UPROPERTY() + TArray> ListItems; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.cpp new file mode 100644 index 0000000..7de6c05 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.cpp @@ -0,0 +1,75 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "InventoryItemWidget.h" +#include "UI/Inventory/InventoryItemListEntry.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" + +void UInventoryItemWidget::SetItemData(const FItemData& InItemData) +{ + ItemData = InItemData; + ApplyItemVisuals(); +} + +void UInventoryItemWidget::NativeOnListItemObjectSet(UObject* ListItemObject) +{ + if (const UInventoryItemListEntry* ListItem = Cast(ListItemObject)) + { + SetItemData(ListItem->Item); + } +} + +void UInventoryItemWidget::ApplyItemVisuals() +{ + if (Icon) + { + if (!ItemData.IsEmpty() && ItemData.Icon) + { + Icon->SetBrushFromTexture(ItemData.Icon); + Icon->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + Icon->SetVisibility(ESlateVisibility::Collapsed); + } + } + + if (ItemNameText) + { + if (!ItemData.IsEmpty()) + { + ItemNameText->SetText(ItemData.DisplayName); + ItemNameText->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + ItemNameText->SetVisibility(ESlateVisibility::Collapsed); + } + } + + if (QuantityText) + { + if (!ItemData.IsEmpty() && ItemData.Quantity > 1) + { + QuantityText->SetText(FText::AsNumber(ItemData.Quantity)); + QuantityText->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + QuantityText->SetVisibility(ESlateVisibility::Collapsed); + } + } + + if (StateText) + { + if (!ItemData.IsEmpty() && !ItemData.StateText.IsEmpty()) + { + StateText->SetText(ItemData.StateText); + StateText->SetVisibility(ESlateVisibility::HitTestInvisible); + } + else + { + StateText->SetVisibility(ESlateVisibility::Collapsed); + } + } +} diff --git a/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.h b/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.h new file mode 100644 index 0000000..0825546 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/InventoryItemWidget.h @@ -0,0 +1,63 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Blueprint/IUserObjectListEntry.h" +#include "Inventory/InventoryTypes.h" +#include "InventoryItemWidget.generated.h" + +class UImage; +class UTextBlock; + +/** + * A single inventory/equipment/quick-slot item. Always renders at the same fixed UI size + * regardless of the item's Mass/Volume - physical footprint is inventory-property data, not a + * UI dimension. Reused as the entry widget class for WBP_ItemList, WBP_InventoryGrid and + * WBP_QuickSlotList (via IUserObjectListEntry), and can also be placed directly and driven with + * SetItemData. + * + * Name your widgets exactly "Icon" (Image), "ItemNameText" (TextBlock), "QuantityText" + * (TextBlock) and "StateText" (TextBlock) in the Designer and population is handled here in + * C++; no BP event graph logic needed. + */ +UCLASS(abstract) +class UInventoryItemWidget : public UUserWidget, public IUserObjectListEntry +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Item") + void SetItemData(const FItemData& InItemData); + + UFUNCTION(BlueprintPure, Category = "Item") + const FItemData& GetItemData() const { return ItemData; } + +protected: + + /** IUserObjectListEntry - called by the owning ListView/TileView when this row is assigned an UInventoryItemListEntry. */ + virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override; + + void ApplyItemVisuals(); + + /** Item icon - bind an Image named exactly "Icon" in the Designer. Hidden automatically when the item slot is empty. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr Icon; + + /** Item name - bind a TextBlock named exactly "ItemNameText" in the Designer. Hidden automatically when the item slot is empty. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr ItemNameText; + + /** Stack count - bind a TextBlock named exactly "QuantityText" in the Designer. Hidden automatically when Quantity <= 1. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr QuantityText; + + /** Basic item state (e.g. "Broken", "New") - bind a TextBlock named exactly "StateText" in the Designer. Hidden automatically when the item has no StateText set. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr StateText; + + UPROPERTY(BlueprintReadOnly, Category = "Item") + FItemData ItemData; +}; diff --git a/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.cpp b/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.cpp new file mode 100644 index 0000000..97611a7 --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.cpp @@ -0,0 +1,35 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "QuickSlotListWidget.h" +#include "UI/Inventory/InventoryItemListEntry.h" +#include "Components/TileView.h" + +void UQuickSlotListWidget::SetQuickSlots(const TArray& InBigQuickSlots, const TArray& InSmallQuickSlots) +{ + PopulateSlots(BigQuickSlotsView, InBigQuickSlots, BigListItems); + PopulateSlots(SmallQuickSlotsView, InSmallQuickSlots, SmallListItems); +} + +void UQuickSlotListWidget::PopulateSlots(UTileView* TargetView, const TArray& InSlots, TArray>& OutListItems) +{ + if (!TargetView) + { + return; + } + + TargetView->ClearListItems(); + OutListItems.Reset(InSlots.Num()); + + TArray ItemObjects; + ItemObjects.Reserve(InSlots.Num()); + + for (const FItemData& Item : InSlots) + { + UInventoryItemListEntry* Entry = NewObject(this); + Entry->Item = Item; + OutListItems.Add(Entry); + ItemObjects.Add(Entry); + } + + TargetView->SetListItems(ItemObjects); +} diff --git a/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.h b/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.h new file mode 100644 index 0000000..ab76d0e --- /dev/null +++ b/Source/KingshearthLegacy/UI/Inventory/QuickSlotListWidget.h @@ -0,0 +1,55 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Inventory/InventoryTypes.h" +#include "QuickSlotListWidget.generated.h" + +class UInventoryItemListEntry; +class UTileView; + +/** + * The player's quick-access hotbar: a row of "big" slots (primary, keybound actions) and a row + * of "small" slots (secondary/utility). Both still render items at the standard fixed 2x2 size - + * "big"/"small" describes the slot's role and on-screen footprint, not item icon scaling. Pass + * an entry with an empty FItemData (see FItemData::IsEmpty) for slots that have nothing + * assigned. The slot arrays may include slots granted by equipped items (e.g. a backpack) - + * that's the inventory/equipment system's decision, this widget just displays whatever it's + * given. + * + * Name two TileViews exactly "BigQuickSlotsView" and "SmallQuickSlotsView" in the Designer, both + * with their Entry Widget Class set to a WBP subclass of UInventoryItemWidget (the same class + * used by WBP_ItemList/WBP_InventoryGrid), and population is handled here in C++. + */ +UCLASS(abstract) +class UQuickSlotListWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Quick Slots") + void SetQuickSlots(const TArray& InBigQuickSlots, const TArray& InSmallQuickSlots); + +protected: + + void PopulateSlots(UTileView* TargetView, const TArray& InSlots, TArray>& OutListItems); + + /** Named exactly "BigQuickSlotsView" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr BigQuickSlotsView; + + /** Named exactly "SmallQuickSlotsView" in the Designer, Entry Widget Class set to a WBP subclass of UInventoryItemWidget. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional = true)) + TObjectPtr SmallQuickSlotsView; + + /** UObject wrappers backing BigQuickSlotsView - kept alive here so the TileView's UObjects don't get GC'd. */ + UPROPERTY() + TArray> BigListItems; + + /** UObject wrappers backing SmallQuickSlotsView - kept alive here so the TileView's UObjects don't get GC'd. */ + UPROPERTY() + TArray> SmallListItems; +};