(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:
@@ -123,7 +123,7 @@ int32 UInventoryComponent::AddItem(UItemDataAsset* Item, int32 Quantity, int32 R
|
||||
|
||||
if (Remaining < Quantity)
|
||||
{
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
}
|
||||
|
||||
return Remaining;
|
||||
@@ -172,7 +172,7 @@ int32 UInventoryComponent::RemoveItem(UItemDataAsset* Item, int32 Quantity)
|
||||
const int32 Removed = Quantity - Remaining;
|
||||
if (Removed > 0)
|
||||
{
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
}
|
||||
|
||||
return Removed;
|
||||
@@ -202,7 +202,7 @@ bool UInventoryComponent::ConsumeUse(int32 SlotIndex)
|
||||
Slot.Clear();
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ bool UInventoryComponent::ConsumeUse(int32 SlotIndex)
|
||||
}
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -327,86 +327,50 @@ bool UInventoryComponent::TakeFromSlot(int32 SlotIndex, int32 Quantity, FInvento
|
||||
Slot.Clear();
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::MoveItemQuantity(int32 FromIndex, int32 ToIndex, int32 Quantity)
|
||||
{
|
||||
if (Quantity <= 0 || FromIndex == ToIndex || !Slots.IsValidIndex(FromIndex) || !Slots.IsValidIndex(ToIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FInventorySlot& From = Slots[FromIndex];
|
||||
if (From.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deplacement total : on retombe sur le comportement complet, echange compris.
|
||||
if (Quantity >= From.Quantity)
|
||||
{
|
||||
return MoveItem(FromIndex, ToIndex);
|
||||
}
|
||||
|
||||
FInventorySlot& To = Slots[ToIndex];
|
||||
const int32 MaxStack = From.Item->GetEffectiveMaxStack();
|
||||
|
||||
if (To.IsEmpty())
|
||||
{
|
||||
const int32 Transferred = FMath::Min(Quantity, MaxStack);
|
||||
To.Item = From.Item;
|
||||
To.Quantity = Transferred;
|
||||
To.RemainingUses = From.RemainingUses;
|
||||
From.Quantity -= Transferred;
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (To.Item == From.Item)
|
||||
{
|
||||
const int32 Transferable = FMath::Min(Quantity, MaxStack - To.Quantity);
|
||||
if (Transferable <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
To.Quantity += Transferable;
|
||||
From.Quantity -= Transferable;
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Objet different et transfert partiel : aucune interpretation raisonnable.
|
||||
// On refuse plutot que d'inventer un comportement que le joueur ne
|
||||
// comprendrait pas.
|
||||
return false;
|
||||
return TransferSlot(this, FromIndex, this, ToIndex, Quantity);
|
||||
}
|
||||
|
||||
bool UInventoryComponent::MoveItem(int32 FromIndex, int32 ToIndex)
|
||||
{
|
||||
if (FromIndex == ToIndex || !Slots.IsValidIndex(FromIndex) || !Slots.IsValidIndex(ToIndex))
|
||||
// MAX_int32 depasse forcement la pile : TransferSlot bascule alors sur le
|
||||
// deplacement total, echange compris.
|
||||
return TransferSlot(this, FromIndex, this, ToIndex, MAX_int32);
|
||||
}
|
||||
|
||||
bool UInventoryComponent::ApplySlotMove(FInventorySlot& From, FInventorySlot& To, int32 Quantity)
|
||||
{
|
||||
if (Quantity <= 0 || From.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FInventorySlot& From = Slots[FromIndex];
|
||||
if (From.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const int32 MaxStack = From.Item->GetEffectiveMaxStack();
|
||||
const bool bTotal = (Quantity >= From.Quantity);
|
||||
|
||||
FInventorySlot& To = Slots[ToIndex];
|
||||
|
||||
// Cible vide : la pile entiere part, quelle que soit sa taille.
|
||||
if (To.IsEmpty())
|
||||
{
|
||||
To = From;
|
||||
From.Clear();
|
||||
OnInventoryChanged.Broadcast();
|
||||
// Deplacement total : la pile entiere part, quelle que soit sa taille --
|
||||
// on ne la tronque pas a MaxStack, elle etait deja legale a la source.
|
||||
if (bTotal)
|
||||
{
|
||||
To = From;
|
||||
From.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const int32 Transferred = FMath::Min(Quantity, MaxStack);
|
||||
To.Item = From.Item;
|
||||
To.Quantity = Transferred;
|
||||
// L'usure suit l'objet : diviser une pile de canettes entamees donne
|
||||
// deux piles au meme niveau, jamais une neuve.
|
||||
To.RemainingUses = From.RemainingUses;
|
||||
From.Quantity -= Transferred;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -415,15 +379,21 @@ bool UInventoryComponent::MoveItem(int32 FromIndex, int32 ToIndex)
|
||||
// l'echange -- deux canettes entamees ne fusionnent jamais.
|
||||
if (To.Item == From.Item)
|
||||
{
|
||||
const int32 MaxStack = From.Item->GetEffectiveMaxStack();
|
||||
const int32 Transferable = FMath::Min(From.Quantity, MaxStack - To.Quantity);
|
||||
const int32 Wanted = bTotal ? From.Quantity : Quantity;
|
||||
const int32 Transferable = FMath::Min(Wanted, MaxStack - To.Quantity);
|
||||
|
||||
// Cible deja pleine : on echange plutot que de ne rien faire, sinon le
|
||||
// geste du joueur resterait sans effet et passerait pour un bug.
|
||||
if (Transferable <= 0)
|
||||
{
|
||||
// Cible pleine. Sur un deplacement total on echange plutot que de
|
||||
// ne rien faire, sinon le geste du joueur passerait pour un bug ;
|
||||
// sur un deplacement partiel on refuse, echanger la moitie d'une
|
||||
// pile contre autre chose n'ayant aucun sens.
|
||||
if (!bTotal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Swap(From, To);
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -434,16 +404,168 @@ bool UInventoryComponent::MoveItem(int32 FromIndex, int32 ToIndex)
|
||||
From.Clear();
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Objets differents : simple echange.
|
||||
// Objets differents : echange, et seulement sur un deplacement total. Il
|
||||
// n'existe aucune facon sensee d'echanger la moitie d'une pile contre autre
|
||||
// chose -- on refuse plutot que d'inventer un comportement incomprehensible.
|
||||
if (!bTotal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Swap(From, To);
|
||||
OnInventoryChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UInventoryComponent::TransferSlot(UInventoryComponent* From, int32 FromIndex, UInventoryComponent* To, int32 ToIndex, int32 Quantity)
|
||||
{
|
||||
if (!From || !To || Quantity <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Meme inventaire et meme case : rien a faire. Le test doit porter sur le
|
||||
// couple et non sur l'index seul, deux inventaires differents ayant tous
|
||||
// les deux une case 3.
|
||||
if (From == To && FromIndex == ToIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!From->Slots.IsValidIndex(FromIndex) || !To->Slots.IsValidIndex(ToIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deux references dans le meme tableau quand From == To : legal ici parce
|
||||
// qu'aucun redimensionnement n'a lieu entre les deux acces.
|
||||
if (!ApplySlotMove(From->Slots[FromIndex], To->Slots[ToIndex], Quantity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
From->BroadcastChanged();
|
||||
if (To != From)
|
||||
{
|
||||
To->BroadcastChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 UInventoryComponent::PushSlotInto(int32 SlotIndex, UInventoryComponent& To)
|
||||
{
|
||||
if (!Slots.IsValidIndex(SlotIndex))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
FInventorySlot& Slot = Slots[SlotIndex];
|
||||
if (Slot.IsEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32 Left = To.AddItem(Slot.Item, Slot.Quantity, Slot.RemainingUses);
|
||||
const int32 Moved = Slot.Quantity - Left;
|
||||
if (Moved <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Slot.Quantity = Left;
|
||||
if (Slot.Quantity <= 0)
|
||||
{
|
||||
Slot.Clear();
|
||||
}
|
||||
|
||||
return Moved;
|
||||
}
|
||||
|
||||
int32 UInventoryComponent::QuickTransferSlot(UInventoryComponent* From, int32 FromIndex, UInventoryComponent* To)
|
||||
{
|
||||
if (!From || !To || From == To)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32 Moved = From->PushSlotInto(FromIndex, *To);
|
||||
if (Moved > 0)
|
||||
{
|
||||
From->BroadcastChanged();
|
||||
}
|
||||
|
||||
return Moved;
|
||||
}
|
||||
|
||||
int32 UInventoryComponent::TransferAllTo(UInventoryComponent* To, int32 FirstIndex)
|
||||
{
|
||||
if (!To || To == this)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
To->BeginBatch();
|
||||
|
||||
int32 Moved = 0;
|
||||
for (int32 Index = FMath::Max(0, FirstIndex); Index < Slots.Num(); ++Index)
|
||||
{
|
||||
Moved += PushSlotInto(Index, *To);
|
||||
}
|
||||
|
||||
To->EndBatch();
|
||||
|
||||
if (Moved > 0)
|
||||
{
|
||||
BroadcastChanged();
|
||||
}
|
||||
|
||||
return Moved;
|
||||
}
|
||||
|
||||
void UInventoryComponent::ConfigureAsContainer(int32 NewSlotCount)
|
||||
{
|
||||
// Aucune barre rapide : GetBackpackStartIndex() vaut alors 0, et la grille
|
||||
// affiche la totalite du conteneur au lieu d'en masquer les dix premieres
|
||||
// cases.
|
||||
HotbarSlotCount = 0;
|
||||
BonusSlotCount = 0;
|
||||
BackpackSlotCount = FMath::Max(0, NewSlotCount);
|
||||
SelectedHotbarIndex = 0;
|
||||
|
||||
EnsureSlotCount();
|
||||
}
|
||||
|
||||
void UInventoryComponent::BroadcastChanged()
|
||||
{
|
||||
if (BatchDepth > 0)
|
||||
{
|
||||
bBatchDirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventoryComponent::BeginBatch()
|
||||
{
|
||||
++BatchDepth;
|
||||
}
|
||||
|
||||
void UInventoryComponent::EndBatch()
|
||||
{
|
||||
BatchDepth = FMath::Max(0, BatchDepth - 1);
|
||||
if (BatchDepth > 0 || !bBatchDirty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bBatchDirty = false;
|
||||
OnInventoryChanged.Broadcast();
|
||||
}
|
||||
|
||||
bool UInventoryComponent::SetBonusSlotCount(int32 NewBonusSlots)
|
||||
{
|
||||
const int32 NewTotal = FMath::Max(0, HotbarSlotCount + BackpackSlotCount + FMath::Max(0, NewBonusSlots));
|
||||
@@ -463,7 +585,7 @@ bool UInventoryComponent::SetBonusSlotCount(int32 NewBonusSlots)
|
||||
CompactSlots();
|
||||
Slots.SetNum(NewTotal);
|
||||
|
||||
OnInventoryChanged.Broadcast();
|
||||
BroadcastChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user