(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:
@@ -310,7 +310,6 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Cooking")
|
||||
bool bShowRemainingTimeInPrompt = false;
|
||||
|
||||
|
||||
/** Active le Custom Depth quand le poste est vise, comme les ramassables. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Cooking")
|
||||
bool bHighlightOnFocus = true;
|
||||
|
||||
@@ -121,6 +121,19 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool DropSlot(int32 SlotIndex, int32 Quantity);
|
||||
|
||||
/**
|
||||
* Meme chose, depuis un inventaire quelconque : celui d'un coffre ouvert,
|
||||
* typiquement.
|
||||
*
|
||||
* C'est le PAWN qui pose au sol et pas le composant, pour la meme raison
|
||||
* que le surplus de fabrication : un UInventoryComponent ne sait pas
|
||||
* engendrer un acteur, et un coffre qui deposerait lui-meme laisserait le
|
||||
* butin dans sa propre caisse. L'objet part donc toujours devant les yeux
|
||||
* du joueur, quel que soit le contenant d'ou il sort.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool DropFromInventory(UInventoryComponent* Inventory, int32 SlotIndex, int32 Quantity);
|
||||
|
||||
/** Vide l'inventaire entier en un tas autour du personnage. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void DropAllItems();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "FpsPlayerController.generated.h"
|
||||
|
||||
class AStorageContainer;
|
||||
class UConfirmDialogWidget;
|
||||
class UCraftingComponent;
|
||||
class UHotbarWidget;
|
||||
@@ -66,6 +67,27 @@ public:
|
||||
UFUNCTION(BlueprintPure, Category = "UI")
|
||||
bool IsInventoryOpen() const { return bInventoryOpen; }
|
||||
|
||||
/**
|
||||
* Ouvre l'ecran d'inventaire avec le panneau du coffre a cote : le sac a
|
||||
* gauche, son contenu a droite.
|
||||
*
|
||||
* Ce n'est PAS un ecran a part. Le sac du joueur reste exactement au meme
|
||||
* endroit qu'à l'ouverture par Tab -- c'est tout l'interet, un ecran
|
||||
* distinct le ferait sauter a chaque fois qu'on ouvre un coffre.
|
||||
*
|
||||
* Comme pour l'etabli, c'est le controller qui pilote et non le coffre : lui
|
||||
* seul sait quand l'ecran se referme, et donc quand refermer le couvercle.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UI|Storage")
|
||||
void OpenStorage(AStorageContainer* Container);
|
||||
|
||||
/** Referme l'ecran de coffre. Sans effet s'il n'y en a pas d'ouvert. */
|
||||
UFUNCTION(BlueprintCallable, Category = "UI|Storage")
|
||||
void CloseStorage();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UI|Storage")
|
||||
bool IsStorageOpen() const { return bStorageOpen; }
|
||||
|
||||
/** Ferme le menu de pause et relance le monde. */
|
||||
UFUNCTION(BlueprintCallable, Category = "UI|Pause")
|
||||
void ResumeGame();
|
||||
@@ -189,6 +211,15 @@ protected:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UInventoryScreenWidget> InventoryScreenClass;
|
||||
|
||||
/**
|
||||
* Distance au-dela de laquelle l'ecran de coffre se referme tout seul.
|
||||
*
|
||||
* Sans elle, on viderait un coffre depuis l'autre bout de la carte : rien
|
||||
* n'oblige le joueur a rester devant une fois l'ecran ouvert.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Storage", meta = (ClampMin = "50", Units = "cm"))
|
||||
float MaxStorageDistance = 400.f;
|
||||
|
||||
/** Ton WBP_Hotbar, affiche en permanence. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UHotbarWidget> HotbarClass;
|
||||
@@ -297,6 +328,29 @@ private:
|
||||
/** Rend le poste accorde par le dernier OpenCraftingAtStation. Sans effet sinon. */
|
||||
void ReleaseActiveStation();
|
||||
|
||||
/**
|
||||
* Rend le coffre ouvert : couvercle referme, panneau masque, surveillance
|
||||
* arretee. Sans effet s'il n'y en a pas.
|
||||
*
|
||||
* Separee de CloseStorage() pour la meme raison que ReleaseActiveStation :
|
||||
* c'est la fermeture de l'ECRAN qui l'appelle, quelle qu'en soit la cause --
|
||||
* Tab, Echap, la mort, un autre coffre. Un seul point de sortie, donc aucun
|
||||
* chemin qui laisse un couvercle leve derriere soi.
|
||||
*/
|
||||
void ReleaseActiveStorage();
|
||||
|
||||
/**
|
||||
* Mode d'input et curseur pour un ecran plein cadre.
|
||||
*
|
||||
* Partage par l'inventaire et le coffre : deux copies de ces huit lignes
|
||||
* finiraient par diverger, et le symptome -- une camera qui ne repond plus,
|
||||
* un sprint bloque -- se cherche tres longtemps.
|
||||
*/
|
||||
void SetUiInputMode(bool bUiOpen);
|
||||
|
||||
/** Le joueur s'est-il trop eloigne du coffre ouvert. Appele par un timer. */
|
||||
void CheckStorageDistance();
|
||||
|
||||
/** Raccourci vers le composant de fabrication du pawn possede. */
|
||||
UCraftingComponent* GetPawnCrafting() const;
|
||||
|
||||
@@ -311,6 +365,17 @@ private:
|
||||
*/
|
||||
void UpdateHotbarVisibility();
|
||||
|
||||
/**
|
||||
* Relaie le survol d'une case de la barre rapide vers le panneau de detail
|
||||
* de l'ecran d'inventaire.
|
||||
*
|
||||
* Ignore ecran ferme : sans curseur il n'y a pas de survol, mais un widget
|
||||
* masque sous la souris peut encore emettre sa sortie -- et decrire un objet
|
||||
* dans un panneau invisible n'aurait aucun sens.
|
||||
*/
|
||||
UFUNCTION()
|
||||
void HandleHotbarSlotHovered(UInventoryComponent* Inventory, int32 SlotIndex, bool bHovered);
|
||||
|
||||
/** Jette la pile survolee dans la grille, si l'inventaire est ouvert. */
|
||||
void DropHoveredItem();
|
||||
|
||||
@@ -329,6 +394,14 @@ private:
|
||||
|
||||
FTimerHandle RespawnTimerHandle;
|
||||
|
||||
/** Surveillance de l'eloignement, actif seulement pendant qu'un coffre est ouvert. */
|
||||
FTimerHandle StorageDistanceTimerHandle;
|
||||
|
||||
/** Le coffre actuellement ouvert, pour lui refermer son couvercle. */
|
||||
TWeakObjectPtr<AStorageContainer> ActiveStorage;
|
||||
|
||||
bool bStorageOpen = false;
|
||||
|
||||
bool bInventoryOpen = false;
|
||||
|
||||
/** Onglet Fabrication affiche. N'a de sens que si bInventoryOpen est vrai. */
|
||||
|
||||
@@ -10,6 +10,15 @@ class UHorizontalBox;
|
||||
class UInventoryComponent;
|
||||
class UInventorySlotWidget;
|
||||
|
||||
/**
|
||||
* Le curseur entre ou sort d'une case de la barre rapide.
|
||||
*
|
||||
* La barre n'affiche rien elle-meme : le panneau de detail vit dans l'ecran
|
||||
* d'inventaire, qu'elle ne connait pas. C'est le PlayerController qui tient les
|
||||
* deux widgets et qui fait le pont -- il est deja le proprietaire du HUD.
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnHotbarSlotHovered, UInventoryComponent*, Inventory, int32, SlotIndex, bool, bHovered);
|
||||
|
||||
/**
|
||||
* Barre rapide en bas de l'ecran, affichee en permanence.
|
||||
*
|
||||
@@ -34,6 +43,19 @@ public:
|
||||
/** (Re)branche sur l'inventaire du pawn possede. Voir UInventoryWidget. */
|
||||
void BindToOwningPawn();
|
||||
|
||||
/**
|
||||
* Destination du clic droit pour les cases de la barre.
|
||||
*
|
||||
* La barre reste affichee et cliquable par-dessus l'ecran de coffre : sans
|
||||
* ca, le clic droit marcherait dans la grille et pas dans la barre, ce qui
|
||||
* passerait pour un bug. Le controller lui pousse le coffre a l'ouverture
|
||||
* et nullptr a la fermeture.
|
||||
*/
|
||||
void SetQuickTransferTarget(UInventoryComponent* InTarget);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Inventory")
|
||||
FOnHotbarSlotHovered OnSlotHovered;
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "SlotContainer" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
@@ -54,12 +76,19 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleSelectionChanged(int32 NewIndex);
|
||||
|
||||
/** Abonne aux cases : relaie le survol vers l'exterieur. */
|
||||
UFUNCTION()
|
||||
void HandleSlotHovered(int32 SlotIndex, bool bHovered);
|
||||
|
||||
void RebuildSlots(int32 DesiredCount);
|
||||
|
||||
UInventoryComponent* ResolveInventory() const;
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundInventory;
|
||||
|
||||
/** Destination du clic droit, recopiee sur chaque case creee. */
|
||||
TWeakObjectPtr<UInventoryComponent> QuickTransferTarget;
|
||||
|
||||
/** UPROPERTY obligatoire : sans ca le GC collecterait les cases. */
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UInventorySlotWidget>> SlotWidgets;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InventoryScreenWidget.generated.h"
|
||||
|
||||
class AStorageContainer;
|
||||
class UButton;
|
||||
class UCraftingWidget;
|
||||
class UInventoryComponent;
|
||||
class UInventoryWidget;
|
||||
class UTextBlock;
|
||||
class UWidgetSwitcher;
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,19 @@ public:
|
||||
*/
|
||||
int32 GetHoveredSlotIndex() const;
|
||||
|
||||
/**
|
||||
* Meme chose, avec l'inventaire auquel la case appartient. Depuis les
|
||||
* coffres, l'index seul ne designe plus rien de facon unique.
|
||||
*/
|
||||
bool GetHoveredSlot(UInventoryComponent*& OutInventory, int32& OutIndex) const;
|
||||
|
||||
/**
|
||||
* Survol d'une case exterieure a l'ecran -- la barre rapide, qui reste
|
||||
* affichee par-dessus lui. Alimente le panneau de detail de la page
|
||||
* d'inventaire, seul panneau existant.
|
||||
*/
|
||||
void NotifyExternalSlotHovered(UInventoryComponent* Inventory, int32 SlotIndex, bool bHovered);
|
||||
|
||||
/** Revient sur l'onglet Inventaire. Appele a chaque ouverture par Tab. */
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
void ShowInventoryTab();
|
||||
@@ -62,6 +78,29 @@ public:
|
||||
UPROPERTY(BlueprintAssignable, Category = "UI")
|
||||
FOnInventoryScreenTabChanged OnTabChanged;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Panneau de coffre
|
||||
//
|
||||
// Le contenu d'un coffre s'affiche A COTE de la grille du joueur, dans ce
|
||||
// meme ecran, et non dans un ecran a part. La raison est du ressenti pur :
|
||||
// le sac ne bouge pas d'un pixel entre "j'ouvre mon sac" et "j'ouvre un
|
||||
// coffre". Deux ecrans distincts le feraient sauter a chaque fois.
|
||||
//
|
||||
// Techniquement c'est une deuxieme instance du MEME WBP_Inventory que
|
||||
// InventoryPage. Elle ne peut pas vivre a l'interieur de WBP_Inventory :
|
||||
// un Widget Blueprint qui se contient lui-meme est une reference
|
||||
// circulaire, et UMG refuse de le compiler.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Branche et affiche le panneau de droite. Force l'onglet Inventaire. */
|
||||
void ShowStorage(AStorageContainer* Container);
|
||||
|
||||
/** Masque et debranche le panneau. Sans effet s'il est deja cache. */
|
||||
void HideStorage();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UI|Storage")
|
||||
bool IsStorageVisible() const { return bStorageVisible; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Absorbe ou traite les depots qui ne visent aucune page.
|
||||
@@ -91,7 +130,45 @@ protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCraftingWidget> CraftingPage;
|
||||
|
||||
/**
|
||||
* Tout le bloc de droite -- cadre, titre, grille, boutons -- qu'on montre et
|
||||
* qu'on cache d'un bloc.
|
||||
*
|
||||
* BindWidgetOptional et non BindWidget, comme tous les elements de coffre
|
||||
* qui suivent : l'ecran doit rester valide dans un projet ou personne n'a
|
||||
* encore construit ce panneau. Sans ca, ajouter les coffres casserait la
|
||||
* compilation de WBP_InventoryScreen tant que la mise en page n'est pas
|
||||
* faite -- et donc l'inventaire entier.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UWidget> StoragePanel;
|
||||
|
||||
/** Un deuxieme WBP_Inventory, renomme "StorageGrid". DECOCHE-lui Auto Bind To Pawn. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UInventoryWidget> StorageGrid;
|
||||
|
||||
/** Nom du coffre ouvert. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UTextBlock> StorageTitle;
|
||||
|
||||
/** Deverse la grille du sac dans le coffre. La barre rapide n'y passe pas. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> StoreAllButton;
|
||||
|
||||
/** Vide le coffre dans le sac, dans la limite de la place disponible. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> TakeAllButton;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleStoreAllClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleTakeAllClicked();
|
||||
|
||||
/** Inventaire du pawn possede, ou nullptr. */
|
||||
UInventoryComponent* GetPlayerInventory() const;
|
||||
|
||||
UFUNCTION()
|
||||
void HandleInventoryTabClicked();
|
||||
|
||||
@@ -108,4 +185,16 @@ private:
|
||||
void ShowPage(UWidget* Page);
|
||||
|
||||
bool IsInventoryTabActive() const;
|
||||
|
||||
/**
|
||||
* Le panneau de coffre est affiche.
|
||||
*
|
||||
* Tant qu'il l'est, l'onglet Fabrication est grise : fabriquer avec un
|
||||
* coffre ouvert a cote n'a pas de sens tant que ses ressources ne comptent
|
||||
* pas dans les recettes, et laisser le bouton actif inviterait a un clic
|
||||
* dont le resultat serait incomprehensible.
|
||||
*/
|
||||
bool bStorageVisible = false;
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundStorage;
|
||||
};
|
||||
|
||||
@@ -8,10 +8,18 @@
|
||||
#include "InventorySlotWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UInventoryWidget;
|
||||
class UProgressBar;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Le curseur est entre sur cette case, ou en est sorti.
|
||||
*
|
||||
* Un delegue et non un pointeur vers la grille : la barre rapide pose les
|
||||
* memes cases sans etre un UInventoryWidget, et elle a autant besoin du survol
|
||||
* depuis que l'inventaire affiche un panneau de detail.
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInventorySlotHoverChanged, int32, SlotIndex, bool, bHovered);
|
||||
|
||||
/**
|
||||
* Une case de la grille d'inventaire : une icone et une quantite.
|
||||
*
|
||||
@@ -29,7 +37,11 @@ public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
/** Appele une fois a la creation : dit a la case qui elle represente. */
|
||||
void InitSlot(UInventoryComponent* InInventory, int32 InSlotIndex, UInventoryWidget* InOwningGrid);
|
||||
void InitSlot(UInventoryComponent* InInventory, int32 InSlotIndex);
|
||||
|
||||
/** Le conteneur qui a cree cette case s'y abonne pour suivre le survol. */
|
||||
UPROPERTY()
|
||||
FOnInventorySlotHoverChanged OnHoverChanged;
|
||||
|
||||
/** Affiche le contenu d'un slot, ou se vide si le slot est vide. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
@@ -45,6 +57,15 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void SetSelected(bool bSelected);
|
||||
|
||||
/**
|
||||
* Inventaire vers lequel le CLIC DROIT envoie le contenu de cette case.
|
||||
*
|
||||
* Nul en temps normal : hors de l'ecran de coffre il n'existe aucun "autre
|
||||
* cote" evident, et deviner une destination reviendrait a faire disparaitre
|
||||
* des objets sur un clic malheureux. C'est l'ecran qui sait, et qui pousse.
|
||||
*/
|
||||
void SetQuickTransferTarget(UInventoryComponent* InTarget);
|
||||
|
||||
protected:
|
||||
// --- Glisser-deposer ---
|
||||
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
||||
@@ -101,8 +122,8 @@ private:
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> OwningInventory;
|
||||
|
||||
/** La grille parente, pour lui signaler le survol. */
|
||||
TWeakObjectPtr<UInventoryWidget> OwningGrid;
|
||||
/** Destination du clic droit. Voir SetQuickTransferTarget(). */
|
||||
TWeakObjectPtr<UInventoryComponent> QuickTransferTarget;
|
||||
|
||||
int32 SlotIndex = INDEX_NONE;
|
||||
|
||||
|
||||
@@ -8,13 +8,19 @@
|
||||
|
||||
class UInventoryComponent;
|
||||
class UInventorySlotWidget;
|
||||
class UItemDetailsWidget;
|
||||
class UUniformGridPanel;
|
||||
|
||||
/**
|
||||
* Grille d'inventaire. Cree ses cases dynamiquement a partir du nombre de
|
||||
* slots du composant, et se remet a jour uniquement quand l'inventaire change.
|
||||
* Grille de cases. Cree ses cases dynamiquement a partir du nombre de slots du
|
||||
* composant, et se remet a jour uniquement quand l'inventaire change.
|
||||
*
|
||||
* Le nombre de cases n'est PAS fige : quand un sac ajoutera des slots, la
|
||||
* Elle ne suppose PLUS qu'elle affiche le sac du joueur : le meme widget sert
|
||||
* de grille de coffre en lui donnant simplement un autre UInventoryComponent.
|
||||
* C'est ce qui permet d'avoir un seul WBP_Inventory pose des deux cotes de
|
||||
* l'ecran de coffre -- et donc la meme apparence, sans duplication.
|
||||
*
|
||||
* Le nombre de cases n'est pas fige : quand un sac ajoutera des slots, la
|
||||
* grille s'agrandira toute seule au prochain rafraichissement.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
@@ -34,9 +40,41 @@ public:
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetHoveredSlotIndex() const { return HoveredSlotIndex; }
|
||||
|
||||
/** Appele par les cases quand le curseur entre ou sort. */
|
||||
/**
|
||||
* Case survolee ET inventaire auquel elle appartient.
|
||||
*
|
||||
* L'index seul ne suffit plus : la touche "jeter" doit savoir de quel
|
||||
* conteneur sortir l'objet, et la case 3 existe aussi bien dans le sac que
|
||||
* dans le coffre.
|
||||
*/
|
||||
bool GetHoveredSlot(UInventoryComponent*& OutInventory, int32& OutIndex) const;
|
||||
|
||||
/** Abonne aux cases : le curseur entre ou sort de l'une d'elles. */
|
||||
UFUNCTION()
|
||||
void NotifySlotHovered(int32 Index, bool bHovered);
|
||||
|
||||
/**
|
||||
* Survol d'une case qui n'appartient PAS a cette grille -- la barre rapide.
|
||||
*
|
||||
* Ne touche pas a HoveredSlotIndex : celui-ci sert a la touche "jeter", qui
|
||||
* doit continuer de viser la grille et elle seule. Seul le panneau de detail
|
||||
* repond, parce qu'il n'y en a qu'un a l'ecran et qu'il appartient a la
|
||||
* grille du sac.
|
||||
*/
|
||||
void NotifyExternalSlotHovered(UInventoryComponent* InInventory, int32 InSlotIndex, bool bHovered);
|
||||
|
||||
/**
|
||||
* Autorise ou coupe le panneau de detail de cette grille.
|
||||
*
|
||||
* C'est l'ECRAN qui decide, jamais une case a cocher dans la Designer : le
|
||||
* WBP_Inventory est instancie deux fois sur l'ecran de coffre, et un reglage
|
||||
* qu'on peut oublier de decocher sur l'une des deux instances est un bug qui
|
||||
* attend. Coupe : la grille de coffre n'en a jamais, et le sac perd le sien
|
||||
* tant qu'un coffre est ouvert -- l'ecran est deja assez large a trois
|
||||
* colonnes.
|
||||
*/
|
||||
void SetItemDetailsEnabled(bool bEnabled);
|
||||
|
||||
/**
|
||||
* (Re)branche le widget sur l'inventaire du pawn actuellement possede.
|
||||
* Indispensable apres un respawn : le nouveau pawn a un nouveau composant,
|
||||
@@ -44,6 +82,29 @@ public:
|
||||
*/
|
||||
void BindToOwningPawn();
|
||||
|
||||
/**
|
||||
* (Re)branche le widget sur un inventaire quelconque -- celui d'un coffre,
|
||||
* typiquement. nullptr vide la grille : une grille de coffre referme ne doit
|
||||
* pas continuer d'afficher un contenu qu'on ne pilote plus.
|
||||
*/
|
||||
void BindToInventory(UInventoryComponent* InInventory);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
UInventoryComponent* GetBoundInventory() const { return BoundInventory.Get(); }
|
||||
|
||||
/** Destination du clic droit pour toutes les cases de cette grille. */
|
||||
void SetQuickTransferTarget(UInventoryComponent* InTarget);
|
||||
|
||||
/**
|
||||
* Change le nombre de colonnes et redispose les cases.
|
||||
*
|
||||
* Sert a la grille de coffre : c'est le coffre pose dans le niveau qui
|
||||
* decide de sa forme, pas le Widget Blueprint -- un coffre de 12 cases et
|
||||
* un de 30 ne se lisent pas bien avec le meme nombre de colonnes.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void SetColumnCount(int32 NewColumnCount);
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "SlotGrid" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
@@ -57,6 +118,29 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire", meta = (ClampMin = "1"))
|
||||
int32 ColumnCount = 6;
|
||||
|
||||
/**
|
||||
* La grille va chercher toute seule l'inventaire du pawn a sa construction.
|
||||
*
|
||||
* DECOCHE-LE sur la grille de coffre. Sans ca elle s'accrocherait au pawn
|
||||
* dans son NativeConstruct -- qui s'execute avant que l'ecran ait eu le
|
||||
* temps de lui donner le conteneur -- et afficherait le sac du joueur des
|
||||
* deux cotes de l'ecran.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
bool bAutoBindToPawn = true;
|
||||
|
||||
/**
|
||||
* Le panneau de detail pose a cote de la grille, dans le meme Widget
|
||||
* Blueprint. A nommer "ItemDetailsPanel".
|
||||
*
|
||||
* Optionnel : la grille doit rester valide dans un WBP_Inventory ou ce
|
||||
* panneau n'existe pas encore, sinon ajouter cette fonctionnalite casserait
|
||||
* la compilation de l'inventaire entier tant que la mise en page n'est pas
|
||||
* faite.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UItemDetailsWidget> ItemDetailsPanel;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Absorbe les depots qui ne visent aucune case. Sans ca, lacher un objet
|
||||
@@ -72,13 +156,31 @@ private:
|
||||
/** Cree ou supprime des cases pour coller au nombre de slots. */
|
||||
void RebuildGrid(int32 DesiredCount);
|
||||
|
||||
/** Detruit toutes les cases : leur inventaire a change, elles sont perimees. */
|
||||
void ClearSlotWidgets();
|
||||
|
||||
/** Retrouve l'inventaire du pawn possede. */
|
||||
UInventoryComponent* ResolveInventory() const;
|
||||
|
||||
/**
|
||||
* Point de passage UNIQUE du survol : met a jour l'index et le panneau de
|
||||
* detail. Ne fait rien si l'index ne change pas.
|
||||
*/
|
||||
void SetHoveredSlot(int32 NewIndex);
|
||||
|
||||
/** Pousse la case survolee dans le panneau, ou l'efface. */
|
||||
void UpdateItemDetails();
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundInventory;
|
||||
|
||||
/** Destination du clic droit, recopiee sur chaque case creee. */
|
||||
TWeakObjectPtr<UInventoryComponent> QuickTransferTarget;
|
||||
|
||||
int32 HoveredSlotIndex = INDEX_NONE;
|
||||
|
||||
/** Voir SetItemDetailsEnabled(). */
|
||||
bool bItemDetailsEnabled = true;
|
||||
|
||||
/** UPROPERTY obligatoire : sans ca le GC collecterait les cases. */
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UInventorySlotWidget>> SlotWidgets;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "ItemDetailsWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UInventoryComponent;
|
||||
class UProgressBar;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Le panneau qui decrit l'objet survole : icone, nom, description, usure.
|
||||
* Rien d'autre -- volontairement.
|
||||
*
|
||||
* Il ne connait ni la grille ni l'ecran. On lui pousse un couple
|
||||
* (inventaire, index) et il lit -- et NON une copie du FInventorySlot :
|
||||
* le contenu peut changer pendant le survol (une charge consommee, un craft
|
||||
* qui vide la pile), donc le panneau s'abonne a OnInventoryChanged de
|
||||
* l'inventaire affiche et se relit tout seul. Jamais de Tick.
|
||||
*
|
||||
* Aucun texte en dur ici en dehors du format numerique de l'usure : la mise en
|
||||
* page et les libelles appartiennent au Widget Blueprint.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UItemDetailsWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** Affiche la case InSlotIndex de InInventory. Index invalide = etat vide. */
|
||||
void ShowSlot(UInventoryComponent* InInventory, int32 InSlotIndex);
|
||||
|
||||
/** Repasse en etat vide et coupe l'abonnement. */
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* Efface, mais seulement si c'est bien CETTE case qui est affichee.
|
||||
*
|
||||
* Le panneau est unique et trois conteneurs le nourrissent -- grille, barre
|
||||
* rapide, et la grille elle-meme quand elle se rebranche. Passer de l'un a
|
||||
* l'autre emet "entre dans B" PUIS "sort de A" : un effacement
|
||||
* inconditionnel viderait l'affichage qui vient d'arriver.
|
||||
*/
|
||||
void ClearSlot(const UInventoryComponent* InInventory, int32 InSlotIndex);
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "ItemIcon" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UImage> ItemIcon;
|
||||
|
||||
/** A nommer exactement "ItemNameText". */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> ItemNameText;
|
||||
|
||||
/** Pense a cocher "Auto Wrap Text" dessus, sinon le panneau s'elargit. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UTextBlock> ItemDescriptionText;
|
||||
|
||||
/**
|
||||
* Bloc d'usure entier -- barre et texte ensemble -- replie sur tout objet
|
||||
* qui n'a pas de charges multiples. Sans lui, la barre et son texte
|
||||
* devraient etre replies separement et finiraient un jour desynchronises.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UWidget> UsesPanel;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UProgressBar> UsesBar;
|
||||
|
||||
/** "3 / 5", pose par-dessus la barre. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UTextBlock> UsesText;
|
||||
|
||||
/**
|
||||
* Le conteneur qui empile tout le panneau -- a renommer "ContentRoot", ce
|
||||
* n'est pas un widget de plus.
|
||||
*
|
||||
* Il est masque en bloc quand rien n'est survole. Sans lui, la barre de
|
||||
* separation resterait seule a l'ecran : c'est une decoration du Blueprint,
|
||||
* le C++ n'a aucune raison de la connaitre individuellement.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UWidget> ContentRoot;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleInventoryChanged();
|
||||
|
||||
/** Relit la case et repeint tout le panneau. */
|
||||
void Refresh();
|
||||
|
||||
/**
|
||||
* Etat vide : plus rien a l'ecran.
|
||||
*
|
||||
* Le panneau n'a pas de fond, donc vide il est simplement invisible -- et sa
|
||||
* largeur est tenue par le Size Box du Blueprint, pas par son contenu. C'est
|
||||
* ce qui empeche la grille de sauter lateralement quand le curseur quitte
|
||||
* une case.
|
||||
*/
|
||||
void ClearDisplay();
|
||||
|
||||
/** (Re)branche OnInventoryChanged. Un seul abonnement a la fois. */
|
||||
void BindInventory(UInventoryComponent* InInventory);
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundInventory;
|
||||
|
||||
int32 SlotIndex = INDEX_NONE;
|
||||
};
|
||||
@@ -0,0 +1,239 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Interactable.h"
|
||||
#include "StorageContainer.generated.h"
|
||||
|
||||
class UBoxComponent;
|
||||
class UInventoryComponent;
|
||||
class UItemDataAsset;
|
||||
class USoundBase;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
/**
|
||||
* Contenu initial d'un coffre pose dans le niveau.
|
||||
*
|
||||
* De la donnee de level design, pas du gameplay : un coffre de depart, une
|
||||
* cache a trouver. Verse une seule fois au BeginPlay, jamais relu ensuite.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FStorageStartingItem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<UItemDataAsset> Item = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1"))
|
||||
int32 Quantity = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Coffre pose dans le monde.
|
||||
*
|
||||
* Il ne contient AUCUN modele de donnees a lui : son contenu est un
|
||||
* UInventoryComponent, exactement le meme que celui du joueur, simplement
|
||||
* configure sans barre rapide. C'est ce qui rend le glisser-deposer entre le
|
||||
* sac et le coffre gratuit -- ce sont les memes cases, le meme FInventorySlot,
|
||||
* et donc la durabilite d'une hache traverse le coffre sans une ligne de code.
|
||||
*
|
||||
* Toute la collision tient dans UNE boite, et les deux meshes n'en portent
|
||||
* aucune. C'est la difference avec ACraftingStation, qui se contente de son
|
||||
* mesh : un coffre est en deux morceaux dont l'un PIVOTE. Se reposer sur leurs
|
||||
* collisions imposerait que le pack en fournisse deux correctes, et laisserait
|
||||
* surtout un trou dans la surface visable des que le couvercle se leve --
|
||||
* viser le haut du coffre ouvert ne toucherait plus rien.
|
||||
*
|
||||
* La boite est fille de la caisse et ne bouge donc jamais : le coffre se vise
|
||||
* exactement pareil ouvert ou ferme. Elle bloque aussi le joueur physiquement,
|
||||
* ce qui evite d'avoir a se demander si le mesh du pack sait le faire.
|
||||
*
|
||||
* Aucun Tick au repos : il ne s'active que le temps d'animer le couvercle, et
|
||||
* se rendort des que l'angle est atteint.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AStorageContainer : public AActor, public IInteractable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AStorageContainer();
|
||||
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// --- IInteractable ---
|
||||
virtual FText GetInteractionPrompt_Implementation(AActor* Interactor) const override;
|
||||
virtual void Interact_Implementation(AActor* Interactor) override;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Storage")
|
||||
UInventoryComponent* GetStorage() const { return Storage; }
|
||||
|
||||
/** Nom affiche en titre de l'ecran de coffre. */
|
||||
UFUNCTION(BlueprintPure, Category = "Storage")
|
||||
FText GetDisplayName() const { return DisplayName; }
|
||||
|
||||
/** Cases par ligne souhaitees pour la grille de l'ecran. */
|
||||
UFUNCTION(BlueprintPure, Category = "Storage")
|
||||
int32 GetColumnCount() const { return ColumnCount; }
|
||||
|
||||
/**
|
||||
* Ouvre ou ferme le couvercle.
|
||||
*
|
||||
* Appele par le controller, pas par le coffre lui-meme : c'est lui qui sait
|
||||
* quand l'ecran se referme, que ce soit par Echap, par la mort du joueur ou
|
||||
* parce qu'il s'est trop eloigne. Un coffre qui deciderait seul finirait
|
||||
* ouvert dans la moitie de ces cas.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Storage")
|
||||
void SetOpen(bool bInOpen);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Storage")
|
||||
bool IsOpen() const { return bOpen; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Dimensionne le composant AVANT que son InitializeComponent() fige la
|
||||
* taille par defaut. PostInitializeComponents serait deja trop tard, et le
|
||||
* constructeur trop tot -- il ne connait pas les valeurs reglees sur
|
||||
* l'instance posee dans le niveau.
|
||||
*/
|
||||
virtual void PreInitializeComponents() override;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Redimensionne la boite quand un mesh change dans le Blueprint. */
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Composants
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** La caisse : SM_Chest_Bottom. Racine, purement visuelle. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<UStaticMeshComponent> BaseMesh;
|
||||
|
||||
/**
|
||||
* L'unique collision du coffre : ce que le trace d'interaction vient
|
||||
* toucher, et ce qui empeche le joueur de traverser.
|
||||
*
|
||||
* Fille de la caisse et non du pivot : elle ne suit surtout pas le
|
||||
* couvercle. Un coffre ouvert doit se viser au meme endroit qu'un coffre
|
||||
* ferme, sinon le prompt disparait au moment precis ou l'on s'en sert.
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<UBoxComponent> InteractionBox;
|
||||
|
||||
/**
|
||||
* La charniere. Place-la a la souris sur l'arete arriere du coffre.
|
||||
*
|
||||
* Ce composant vide n'est pas un luxe : le pivot de SM_Chest_Top est celui
|
||||
* que le pack lui a donne, presque jamais la charniere. Faire tourner le
|
||||
* mesh directement le ferait pivoter autour de son centre et traverser la
|
||||
* caisse. Ici on tourne le pivot, et le mesh suit avec son decalage.
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<USceneComponent> LidPivot;
|
||||
|
||||
/** Le couvercle : SM_Chest_Top, enfant du pivot. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<UStaticMeshComponent> LidMesh;
|
||||
|
||||
/** Le contenu. Un inventaire ordinaire, sans barre rapide. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TObjectPtr<UInventoryComponent> Storage;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Reglages
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Nombre de cases. Reglable PAR INSTANCE : le meme Blueprint donne un petit
|
||||
* coffre de 12 et un grand de 30 selon celui qu'on pose.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1", ClampMax = "60"))
|
||||
int32 SlotCount = 20;
|
||||
|
||||
/** Cases par ligne dans l'ecran. Purement visuel. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1"))
|
||||
int32 ColumnCount = 6;
|
||||
|
||||
/** Nom affiche en titre de l'ecran. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
FText DisplayName;
|
||||
|
||||
/** Texte affiche quand le joueur vise le coffre. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
FText InteractionPrompt;
|
||||
|
||||
/** Verse dans le coffre au demarrage. Laisse vide pour un coffre a remplir. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
||||
TArray<FStorageStartingItem> StartingItems;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Collision
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* La boite epouse toute seule les deux meshes, couvercle ferme.
|
||||
*
|
||||
* Coche par defaut : changer SM_Chest_v1 pour le v3 doit suffire, sans
|
||||
* avoir a retailler une boite a la main derriere. Decoche-le seulement si
|
||||
* tu veux une zone volontairement differente du volume du coffre.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Collision")
|
||||
bool bAutoFitInteractionBox = true;
|
||||
|
||||
/**
|
||||
* Marge ajoutee autour des meshes par l'ajustement automatique.
|
||||
*
|
||||
* Garde-la petite. Le trace d'interaction est deja un balayage spherique
|
||||
* tolerant : gonfler la boite ne rendrait pas le coffre plus facile a viser,
|
||||
* ca l'empecherait juste de se coller a un mur.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Collision", meta = (ClampMin = "0", Units = "cm"))
|
||||
float InteractionBoxPadding = 0.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Couvercle
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rotation RELATIVE du pivot couvercle ouvert. Le repos vaut toujours zero.
|
||||
*
|
||||
* Un FRotator complet et pas un simple angle : selon l'orientation que tu
|
||||
* donnes au pivot, la charniere peut tomber sur le pitch, le yaw ou le roll.
|
||||
* Regle les trois a l'oeil dans le viewport plutot que d'aller comprendre
|
||||
* quel axe local du mesh pointe ou.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Lid")
|
||||
FRotator OpenRotation = FRotator(-100.f, 0.f, 0.f);
|
||||
|
||||
/** Duree de l'ouverture, en secondes. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Lid", meta = (ClampMin = "0.05", Units = "s"))
|
||||
float LidDuration = 0.35f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Sons. Facultatifs : un champ vide ne joue rien.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Sound")
|
||||
TObjectPtr<USoundBase> OpenSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Sound")
|
||||
TObjectPtr<USoundBase> CloseSound;
|
||||
|
||||
private:
|
||||
/** Recopie LidAlpha dans la rotation du pivot. */
|
||||
void ApplyLidRotation();
|
||||
|
||||
/** Cale la boite sur le volume combine des deux meshes, couvercle ferme. */
|
||||
void FitInteractionBox();
|
||||
|
||||
bool bOpen = false;
|
||||
|
||||
/** 0 = ferme, 1 = ouvert. Interpole, jamais pose directement. */
|
||||
float LidAlpha = 0.f;
|
||||
};
|
||||
Reference in New Issue
Block a user