// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "InventoryComponent.generated.h" class UItemDataAsset; /** * Contenu d'un emplacement. Item nul = slot vide. * Le tableau de slots a toujours exactement la taille de l'inventaire : * l'index dans le tableau EST la position dans la grille, ce qui rend * l'affichage et le futur drag & drop triviaux. */ USTRUCT(BlueprintType) struct FInventorySlot { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire") TObjectPtr Item = nullptr; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire") int32 Quantity = 0; /** * Charges restantes de l'exemplaire courant, pour les objets a usages * multiples. Etat par instance, d'ou sa place ici et pas sur le DataAsset : * une canette entamee ne doit pas entamer toutes les canettes du jeu. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire") int32 RemainingUses = 0; bool IsEmpty() const { return Item == nullptr || Quantity <= 0; } void Clear() { Item = nullptr; Quantity = 0; RemainingUses = 0; } }; /** Diffuse a chaque modification du contenu ou de la taille. L'UI s'y abonne. */ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnInventoryChanged); /** Diffuse quand le joueur change de slot actif dans la barre rapide. */ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSelectedHotbarSlotChanged, int32, NewIndex); /** * Inventaire en grille a slots, facon Raft / Valheim. * * Le nombre de slots est dynamique : BaseSlotCount fixe le minimum, et * BonusSlotCount permet a un sac equipe de l'agrandir. Retirer le sac * refuse si les objets ne rentrent plus, plutot que de les faire * disparaitre en silence. */ UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent)) class SURVIVAL_PROJET_API UInventoryComponent : public UActorComponent { GENERATED_BODY() public: UInventoryComponent(); virtual void InitializeComponent() override; /** * Ajoute des objets, en remplissant d'abord les piles entamees. * @param RemainingUses charges de l'exemplaire ajoute ; -1 signifie "plein". * Sert a rendre son etat a un objet ramasse au sol. * @return le nombre d'exemplaires qui N'ONT PAS pu rentrer (0 = tout est passe). */ UFUNCTION(BlueprintCallable, Category = "Inventaire") int32 AddItem(UItemDataAsset* Item, int32 Quantity = 1, int32 RemainingUses = -1); /** * Consomme une utilisation du slot. * Retire un exemplaire quand il ne reste plus de charge. * @return true si quelque chose a ete consomme. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") bool ConsumeUse(int32 SlotIndex); /** * Retire des objets, en vidant d'abord les piles les plus petites. * @return le nombre d'exemplaires reellement retires. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") int32 RemoveItem(UItemDataAsset* Item, int32 Quantity = 1); /** Combien d'exemplaires de cet objet, toutes piles confondues. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetItemCount(const UItemDataAsset* Item) const; /** Combien d'exemplaires pourraient encore rentrer, sans rien modifier. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetRoomFor(const UItemDataAsset* Item) const; /** * Un seul tableau, deux conteneurs distincts : * index 0 .. HotbarSlotCount-1 -> barre rapide * index HotbarSlotCount .. fin -> grille d'inventaire (+ bonus de sac) * * Un objet range dans la barre n'apparait PAS dans la grille, et * inversement. Ils partagent le meme tableau uniquement pour que le * glisser-deposer entre les deux ne demande aucune conversion d'index. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetTotalSlotCount() const { return FMath::Max(0, HotbarSlotCount + BackpackSlotCount + BonusSlotCount); } /** Premier index affiche par la grille, juste apres la barre rapide. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetBackpackStartIndex() const { return GetHotbarSlotCount(); } UFUNCTION(BlueprintPure, Category = "Inventaire") int32 GetUsedSlotCount() const; UFUNCTION(BlueprintPure, Category = "Inventaire") const TArray& GetSlots() const { return Slots; } /** * Deplace la pile entiere d'un slot vers un autre. * - cible vide -> la pile entiere part * - meme objet -> fusion jusqu'a MaxStackSize, le reste demeure a la source * - objet different -> echange des deux slots * @return true si quelque chose a change. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") bool MoveItem(int32 FromIndex, int32 ToIndex); /** * Deplace seulement une partie d'une pile. * * Un transfert partiel vers un slot occupe par un objet DIFFERENT est * refuse : il n'existe aucune facon sensee d'echanger la moitie d'une pile * contre autre chose. Seul un deplacement total peut provoquer un echange. * * @return true si quelque chose a change. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") bool MoveItemQuantity(int32 FromIndex, int32 ToIndex, int32 Quantity); /** Index du premier slot libre, ou INDEX_NONE si l'inventaire est plein. */ UFUNCTION(BlueprintPure, Category = "Inventaire") int32 FindFirstEmptySlot() const; /** * Retire jusqu'a Quantity exemplaires d'un slot precis et renvoie ce qui a * ete retire. Sert au drop au sol et, plus tard, au transfert vers un coffre. * * Different de RemoveItem() qui pioche par type sans se soucier du slot : * ici le joueur a designe une case, on ne touche qu'a celle-la. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") bool TakeFromSlot(int32 SlotIndex, int32 Quantity, FInventorySlot& OutTaken); /** * Change le bonus de slots apporte par l'equipement (sac). * @return false si reduire la taille laisserait des objets dehors ; * dans ce cas rien n'est modifie et le sac ne doit pas etre retire. */ UFUNCTION(BlueprintCallable, Category = "Inventaire") bool SetBonusSlotCount(int32 NewBonusSlots); UPROPERTY(BlueprintAssignable, Category = "Inventaire") FOnInventoryChanged OnInventoryChanged; // ------------------------------------------------------------------ // Barre rapide // ------------------------------------------------------------------ // // La barre rapide n'est PAS un jeu de slots separe : ce sont les // HotbarSlotCount premiers slots de l'inventaire, simplement affiches // deux fois. Consequence directe : le glisser-deposer entre la barre et // la grille fonctionne sans une ligne de code de plus, puisque ce sont // les memes index dans le meme tableau. /** Change le slot actif. Un index hors de la barre est ignore. */ UFUNCTION(BlueprintCallable, Category = "Inventaire|Barre rapide") void SelectHotbarSlot(int32 Index); /** Avance ou recule dans la barre, en boucle. */ UFUNCTION(BlueprintCallable, Category = "Inventaire|Barre rapide") void CycleHotbarSelection(int32 Delta); UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide") int32 GetSelectedHotbarIndex() const { return SelectedHotbarIndex; } UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide") int32 GetHotbarSlotCount() const { return FMath::Min(HotbarSlotCount, Slots.Num()); } /** Contenu du slot actif. C'est ce que le joueur "a en main". */ UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide") FInventorySlot GetSelectedSlot() const; UPROPERTY(BlueprintAssignable, Category = "Inventaire|Barre rapide") FOnSelectedHotbarSlotChanged OnSelectedHotbarSlotChanged; protected: /** Cases affichees dans la grille d'inventaire. N'inclut PAS la barre rapide. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire", meta = (ClampMin = "0")) int32 BackpackSlotCount = 20; /** Slots supplementaires apportes par l'equipement (sac). Passe par SetBonusSlotCount(). */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire") int32 BonusSlotCount = 0; /** * Cases de la barre rapide, independantes de celles de la grille. * Elles occupent les premiers index du tableau. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire|Barre rapide", meta = (ClampMin = "0")) int32 HotbarSlotCount = 10; /** Toujours dimensionne a GetTotalSlotCount(). */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire") TArray Slots; private: /** Redimensionne le tableau si la taille cible a change. */ void EnsureSlotCount(); /** Regroupe les slots occupes au debut du tableau. */ void CompactSlots(); int32 SelectedHotbarIndex = 0; };