(Feat) Add Storage Chests + Item Details Panel
Coffres : AStorageContainer, un UInventoryComponent configure en conteneur, affiche comme panneau de WBP_InventoryScreen plutot que dans un ecran a lui. Deplacement unifie par UInventoryComponent::TransferSlot / TransferAllTo. Panneau de detail : UItemDetailsWidget, pose DANS WBP_Inventory a droite de la grille. Icone, nom, separation, description, barre d'usure. Il recoit un couple (inventaire, index) et s'abonne a OnInventoryChanged, donc une charge consommee sous le curseur se voit. Une case ne connait plus sa grille : elle diffuse OnHoverChanged. La barre rapide s'y abonne aussi et relaie par le PlayerController, seul a posseder a la fois le HUD et l'ecran. Le panneau efface la case qu'on QUITTE et non lui-meme, sinon passer de la barre a la grille viderait ce qui vient d'etre affiche. L'ecran allume et eteint le panneau (SetItemDetailsEnabled) : WBP_Inventory etant instancie deux fois en mode coffre, une case a cocher par instance serait un bug qui attend qu'on oublie de la decocher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,15 @@
|
||||
#include "InventoryScreenWidget.h"
|
||||
|
||||
#include "Components/Button.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Components/WidgetSwitcher.h"
|
||||
#include "CraftingWidget.h"
|
||||
#include "FpsPlayer.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "InventoryDragDropOperation.h"
|
||||
#include "InventoryWidget.h"
|
||||
#include "StorageContainer.h"
|
||||
|
||||
void UInventoryScreenWidget::NativeConstruct()
|
||||
{
|
||||
@@ -25,6 +28,30 @@ void UInventoryScreenWidget::NativeConstruct()
|
||||
CraftingTabButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleCraftingTabClicked);
|
||||
}
|
||||
|
||||
if (StoreAllButton)
|
||||
{
|
||||
StoreAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleStoreAllClicked);
|
||||
}
|
||||
|
||||
if (TakeAllButton)
|
||||
{
|
||||
TakeAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleTakeAllClicked);
|
||||
}
|
||||
|
||||
// La grille de coffre n'affiche jamais de detail : c'est le meme
|
||||
// WBP_Inventory que le sac, elle en porte donc un exemplaire qu'on coupe une
|
||||
// fois pour toutes ici plutot que de compter sur une case a cocher.
|
||||
if (StorageGrid)
|
||||
{
|
||||
StorageGrid->SetItemDetailsEnabled(false);
|
||||
}
|
||||
|
||||
// Le panneau de coffre ne se montre qu'a l'ouverture d'un coffre. On le
|
||||
// masque en C++ plutot que de dependre de ce qui a ete regle dans la
|
||||
// Designer : un panneau laisse Visible par erreur afficherait une grille
|
||||
// vide en permanence a cote du sac.
|
||||
HideStorage();
|
||||
|
||||
ShowInventoryTab();
|
||||
}
|
||||
|
||||
@@ -56,6 +83,152 @@ int32 UInventoryScreenWidget::GetHoveredSlotIndex() const
|
||||
return InventoryPage->GetHoveredSlotIndex();
|
||||
}
|
||||
|
||||
bool UInventoryScreenWidget::GetHoveredSlot(UInventoryComponent*& OutInventory, int32& OutIndex) const
|
||||
{
|
||||
// Le coffre en premier : c'est le panneau le plus a droite, donc celui que
|
||||
// le curseur survole quand les deux sont affiches.
|
||||
if (bStorageVisible && StorageGrid && StorageGrid->GetHoveredSlot(OutInventory, OutIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (InventoryPage && IsInventoryTabActive() && InventoryPage->GetHoveredSlot(OutInventory, OutIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
OutInventory = nullptr;
|
||||
OutIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::NotifyExternalSlotHovered(UInventoryComponent* Inventory, int32 SlotIndex, bool bHovered)
|
||||
{
|
||||
// Rien a faire sous l'onglet Fabrication : le panneau est parti avec la
|
||||
// page, et la barre rapide y est de toute facon masquee.
|
||||
if (InventoryPage && IsInventoryTabActive())
|
||||
{
|
||||
InventoryPage->NotifyExternalSlotHovered(Inventory, SlotIndex, bHovered);
|
||||
}
|
||||
}
|
||||
|
||||
UInventoryComponent* UInventoryScreenWidget::GetPlayerInventory() const
|
||||
{
|
||||
const APlayerController* OwningController = GetOwningPlayer();
|
||||
AFpsPlayer* PlayerPawn = OwningController ? Cast<AFpsPlayer>(OwningController->GetPawn()) : nullptr;
|
||||
return PlayerPawn ? PlayerPawn->GetInventoryComponent() : nullptr;
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::ShowStorage(AStorageContainer* Container)
|
||||
{
|
||||
UInventoryComponent* Contents = Container ? Container->GetStorage() : nullptr;
|
||||
if (!Contents)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget::ShowStorage : coffre nul ou sans inventaire."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StoragePanel || !StorageGrid)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget : 'StoragePanel' ou 'StorageGrid' manque dans WBP_InventoryScreen, le coffre ne s'affichera pas."));
|
||||
return;
|
||||
}
|
||||
|
||||
BoundStorage = Contents;
|
||||
bStorageVisible = true;
|
||||
|
||||
StorageGrid->SetColumnCount(Container->GetColumnCount());
|
||||
StorageGrid->BindToInventory(Contents);
|
||||
|
||||
// Le clic droit envoie d'un cote a l'autre. Le pawn a pu changer depuis la
|
||||
// derniere ouverture (respawn), d'ou la relecture plutot qu'une valeur
|
||||
// gardee en cache.
|
||||
StorageGrid->SetQuickTransferTarget(GetPlayerInventory());
|
||||
if (InventoryPage)
|
||||
{
|
||||
InventoryPage->SetQuickTransferTarget(Contents);
|
||||
|
||||
// Le detail disparait en mode coffre : l'ecran passe deja a deux grilles
|
||||
// cote a cote, une troisieme colonne le rendrait illisible.
|
||||
InventoryPage->SetItemDetailsEnabled(false);
|
||||
}
|
||||
|
||||
if (StorageTitle)
|
||||
{
|
||||
StorageTitle->SetText(Container->GetDisplayName());
|
||||
}
|
||||
|
||||
StoragePanel->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
// Un coffre ouvert sous l'onglet Fabrication n'aurait aucun sens : on force
|
||||
// l'onglet Inventaire, et ShowPage grise le bouton Fabrication tant que
|
||||
// bStorageVisible est vrai.
|
||||
ShowInventoryTab();
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::HideStorage()
|
||||
{
|
||||
BoundStorage.Reset();
|
||||
bStorageVisible = false;
|
||||
|
||||
// On coupe les transferts rapides AVANT de debrancher : une case qui garde
|
||||
// un coffre en memoire y enverrait encore des objets au prochain clic droit,
|
||||
// panneau ferme.
|
||||
if (InventoryPage)
|
||||
{
|
||||
InventoryPage->SetQuickTransferTarget(nullptr);
|
||||
|
||||
// Le sac retrouve son panneau. Repose a chaque fermeture, y compris a la
|
||||
// construction : c'est l'etat par defaut de l'ecran, il ne doit dependre
|
||||
// ni de la Designer ni de ce qu'a laisse la session precedente.
|
||||
InventoryPage->SetItemDetailsEnabled(true);
|
||||
}
|
||||
|
||||
if (StorageGrid)
|
||||
{
|
||||
StorageGrid->SetQuickTransferTarget(nullptr);
|
||||
StorageGrid->BindToInventory(nullptr);
|
||||
}
|
||||
|
||||
if (StoragePanel)
|
||||
{
|
||||
StoragePanel->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
|
||||
// Rend la main a l'onglet Fabrication, que ShowPage avait grise.
|
||||
if (CraftingTabButton)
|
||||
{
|
||||
CraftingTabButton->SetIsEnabled(IsInventoryTabActive());
|
||||
}
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::HandleStoreAllClicked()
|
||||
{
|
||||
UInventoryComponent* PlayerInventory = GetPlayerInventory();
|
||||
if (!PlayerInventory || !BoundStorage.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// A partir de la grille seulement : la barre rapide porte les outils qu'on
|
||||
// vient d'utiliser, et les ranger a chaque retour d'expedition serait une
|
||||
// punition deguisee en confort.
|
||||
PlayerInventory->TransferAllTo(BoundStorage.Get(), PlayerInventory->GetBackpackStartIndex());
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::HandleTakeAllClicked()
|
||||
{
|
||||
UInventoryComponent* PlayerInventory = GetPlayerInventory();
|
||||
if (!PlayerInventory || !BoundStorage.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Depuis l'index 0 : un coffre n'a pas de barre rapide. Ce qui ne rentre
|
||||
// pas dans le sac reste dans le coffre.
|
||||
BoundStorage->TransferAllTo(PlayerInventory, 0);
|
||||
}
|
||||
|
||||
void UInventoryScreenWidget::ShowInventoryTab()
|
||||
{
|
||||
ShowPage(InventoryPage);
|
||||
@@ -96,7 +269,10 @@ void UInventoryScreenWidget::ShowPage(UWidget* Page)
|
||||
|
||||
if (CraftingTabButton)
|
||||
{
|
||||
CraftingTabButton->SetIsEnabled(bInventoryActive);
|
||||
// Grise aussi tant qu'un coffre est ouvert : ses ressources ne comptent
|
||||
// pas encore dans les recettes, et un clic dont le resultat serait
|
||||
// incomprehensible vaut mieux etre rendu impossible.
|
||||
CraftingTabButton->SetIsEnabled(bInventoryActive && !bStorageVisible);
|
||||
}
|
||||
|
||||
OnTabChanged.Broadcast(bInventoryActive);
|
||||
@@ -114,11 +290,13 @@ bool UInventoryScreenWidget::NativeOnDrop(const FGeometry& InGeometry, const FDr
|
||||
return true;
|
||||
}
|
||||
|
||||
// Depuis l'inventaire SOURCE et non celui du joueur : sortir une pile du
|
||||
// coffre en la lachant dans le vide doit la retirer du coffre.
|
||||
if (const APlayerController* OwningController = GetOwningPlayer())
|
||||
{
|
||||
if (AFpsPlayer* PlayerPawn = Cast<AFpsPlayer>(OwningController->GetPawn()))
|
||||
{
|
||||
PlayerPawn->DropSlot(Payload->SourceSlotIndex, Payload->Quantity);
|
||||
PlayerPawn->DropFromInventory(Payload->SourceInventory.Get(), Payload->SourceSlotIndex, Payload->Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user