(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:
2026-08-01 22:51:24 +02:00
parent d50ef564ba
commit 41d6e6a7f3
50 changed files with 2947 additions and 167 deletions
@@ -143,6 +143,65 @@ public:
UFUNCTION(BlueprintCallable, Category = "Inventaire")
bool MoveItemQuantity(int32 FromIndex, int32 ToIndex, int32 Quantity);
// ------------------------------------------------------------------
// Transferts entre deux inventaires (coffres)
// ------------------------------------------------------------------
/**
* Deplace le contenu d'un slot vers un autre, dans le MEME inventaire ou
* dans un autre. C'est desormais l'unique implementation du deplacement :
* MoveItem() et MoveItemQuantity() ne sont que des appels avec From == To.
*
* Statique parce qu'elle n'appartient ni a la source ni a la destination --
* elle arbitre entre les deux, et doit diffuser OnInventoryChanged sur les
* DEUX. Oublier la seconde diffusion laisse la grille d'en face afficher un
* etat perime jusqu'au prochain evenement, ce qui se lit comme un objet
* qui disparait.
*
* @param Quantity nombre d'exemplaires ; MAX_int32 pour la pile entiere.
* @return true si quelque chose a change.
*/
UFUNCTION(BlueprintCallable, Category = "Inventaire")
static bool TransferSlot(UInventoryComponent* From, int32 FromIndex, UInventoryComponent* To, int32 ToIndex, int32 Quantity);
/**
* Envoie le contenu d'un slot vers un autre inventaire sans designer de
* case d'arrivee : c'est le clic droit de l'ecran de coffre.
*
* Passe par AddItem(), donc exactement comme un ramassage -- piles entamees
* d'abord, puis barre rapide, puis grille. Ce qui ne rentre pas reste sur
* place : un transfert rapide ne perd jamais rien.
*
* @return le nombre d'exemplaires reellement partis.
*/
UFUNCTION(BlueprintCallable, Category = "Inventaire")
static int32 QuickTransferSlot(UInventoryComponent* From, int32 FromIndex, UInventoryComponent* To);
/**
* Deverse tout ce qui peut entrer vers un autre inventaire : les boutons
* "Tout ranger" et "Tout prendre".
*
* @param FirstIndex premier slot considere. Le joueur passe
* GetBackpackStartIndex() pour que "Tout ranger" laisse sa barre
* rapide tranquille -- ranger ses outils dans le coffre a chaque
* retour d'expedition serait une punition, pas un confort.
* @return le nombre d'exemplaires reellement partis.
*/
UFUNCTION(BlueprintCallable, Category = "Inventaire")
int32 TransferAllTo(UInventoryComponent* To, int32 FirstIndex = 0);
/**
* Transforme ce composant en contenant du monde : NewSlotCount cases de
* grille et AUCUNE barre rapide, pour que GetBackpackStartIndex() vaille 0
* et que la grille affiche la totalite du coffre.
*
* A appeler avant que le composant contienne quoi que ce soit -- typiquement
* depuis PreInitializeComponents() de l'acteur porteur. Reduire la taille
* d'un conteneur deja rempli fait disparaitre ce qui depasse.
*/
UFUNCTION(BlueprintCallable, Category = "Inventaire")
void ConfigureAsContainer(int32 NewSlotCount);
/** Index du premier slot libre, ou INDEX_NONE si l'inventaire est plein. */
UFUNCTION(BlueprintPure, Category = "Inventaire")
int32 FindFirstEmptySlot() const;
@@ -226,5 +285,33 @@ private:
/** Regroupe les slots occupes au debut du tableau. */
void CompactSlots();
/**
* Regles de deplacement entre deux cases, sans savoir a quel inventaire
* chacune appartient. Ne diffuse rien : c'est l'appelant qui sait combien
* de composants ont bouge.
*/
static bool ApplySlotMove(FInventorySlot& From, FInventorySlot& To, int32 Quantity);
/**
* Verse un slot dans un autre inventaire via AddItem(). Ne diffuse que du
* cote destination (AddItem s'en charge) : la source reste a la charge de
* l'appelant, qui peut en enchainer vingt.
* @return exemplaires reellement partis.
*/
int32 PushSlotInto(int32 SlotIndex, UInventoryComponent& To);
/**
* Diffusion differee. Un "Tout ranger" deplace jusqu'a vingt piles : sans
* regroupement, l'UI se reconstruirait vingt fois pour un seul clic.
* Toutes les modifications passent par BroadcastChanged() plutot que par
* OnInventoryChanged.Broadcast() directement, sinon le lot fuit.
*/
void BroadcastChanged();
void BeginBatch();
void EndBatch();
int32 SelectedHotbarIndex = 0;
int32 BatchDepth = 0;
bool bBatchDirty = false;
};