// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "InventoryWidget.generated.h" class UInventoryComponent; class UInventorySlotWidget; class UItemDetailsWidget; class UUniformGridPanel; /** * 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. * * 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) class SURVIVAL_PROJET_API UInventoryWidget : public UUserWidget { GENERATED_BODY() public: virtual void NativeConstruct() override; virtual void NativeDestruct() override; /** Reconstruit si necessaire, puis pousse le contenu dans chaque case. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") void Refresh(); /** Index de la case sous le curseur, ou INDEX_NONE. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetHoveredSlotIndex() const { return HoveredSlotIndex; } /** * 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, * et l'ancien abonnement pointerait vers un objet detruit. */ 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)) TObjectPtr SlotGrid; /** Le widget de case a instancier : ton WBP_InventorySlot. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire") TSubclassOf SlotWidgetClass; /** Nombre de cases par ligne. */ 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 ItemDetailsPanel; protected: /** * Absorbe les depots qui ne visent aucune case. Sans ca, lacher un objet * sur le fond du panneau declencherait OnDragCancelled -- et si un jour on * y branche le drop au sol, le joueur jetterait son butin par accident. */ virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override; private: UFUNCTION() void HandleInventoryChanged(); /** 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 BoundInventory; /** Destination du clic droit, recopiee sur chaque case creee. */ TWeakObjectPtr QuickTransferTarget; int32 HoveredSlotIndex = INDEX_NONE; /** Voir SetItemDetailsEnabled(). */ bool bItemDetailsEnabled = true; /** UPROPERTY obligatoire : sans ca le GC collecterait les cases. */ UPROPERTY(Transient) TArray> SlotWidgets; };