La fonction Container#transfertStackInSlot


  • Rédacteurs

    Sommaire

    Introduction

    Aujourd'hui nous allons nous intéresser à la fonction Container#transferStackInSlot(EntityPlayer, int). Cette fonction est appelée lorsque le joueur effectue un shift-clic sur un slot, et c'est dans cette fonction que l'on doit déplacer le contenu du slot vers le slot que l'on juge approprié. C'est grâce à cette fonction que vous pouvez facilement déplacer vos objets de votre inventaire vers un coffre par exemple. Cependant l'implémentation de base de cette fonction n'a aucun effet, le shift-click est "désactivé".

    Pré-requis

    • Connaitre le fonctionnement d'un ItemStack
    • Connaitre le fonctionnement d'un Container
    • Connaitre le fonctionnement d'un Slot

    Code

    La fonction mergeItemStack

    Avons de commencer nous allons voir le fonctionnement de la fonction Container#mergeItemStack que nous allons utiliser pour déplacer les ItemStack dans les Slot que l'on veut. Je vais seulement expliquer ce qu'elle fait (vous pouvez toujours regarder les sources si vous voulez), cette fonction essaie de déplacer un ItemStack dans les slots spécifiés (on les indiques grâce à l'ID avec lequel ils ont été ajouté) et nous retourne ensuite vrai si l'ItemStack a été complètement ou partiellement déplacé.
    Intéressons-nous à son prototype :

        boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection)
    

    L'ItemStack est celui que nous voulons déplacer, startIndex est l'Id du premier slot Slot, endIndex est l'Id du dernier Slot et reverseDirection permet d'inverser la direction, vous allez comprendre de suite ce tout cela veux dire. Imaginons que j'ai un ItemStack que je veuille déplacer dans un autre inventaire, et que cet inventaire a 3 Slots, si j'invoque :

        this.mergeItemStack(stack, 0, 3, false);
    

    Alors la fonction va tenté de mettre stack dans le slot 0, si il a réussi il renvoie vrai sinon il passe au slot 1, de la même façon si il a réussi il renvoie vrai sinon il passe au slot 2, si là il n'a toujours pas réussi alors il renvoie faux. Vous avez sûrement remarqué que lorsque que l'on indique startIndex et endIndex, startIndex est inclu tandis que endIndex est exclu. On a donc [ startIndex ; endIndex [. Si on met reverseDirection à vrai alors il tentera de mettre l'ItemStack dans le slot 2 puis 1 puis 0.
    Attention, si la fonction renvoie faux, cela veux dire que l'ItemStack n'a pas du tout été déplacé.
    Note : cette fonction va en priorité fusionner les ItemStacks.

    La fonction transferStackInSlot

    Comme vous l'aurez compris lorsque le joueur va shift-click sur un Slot, en fonction de son Id nous allons répartir le contenue du Slot entre tels ou tels Slots. Voyons le prototype de la fonction :

    ItemStack transferStackInSlot(EntityPlayer player, int index)
    

    On a donc le joueur et l'index du Slot, on peux récupéré l'instance du Slot grâce à son Id :

        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            Slot slot = this.inventorySlots.get(index);
        }
    

    Puis on va vérifier qu'il y a bien quelque chose dans le Slot sinon ça ne sert à rien de continuer (on vérifie aussi si il n'est pas null car l'index donné peut être mauvais) :

        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            Slot slot = this.inventorySlots.get(index);
            if(slot != null && slot.getHasStack()) {
    
            }
        }
    

    Avant de continuer je vais vous parler de la valeur de retour, vous devez retourner un ItemStack, si vous retournez un ItemStack vide cela veux dire que rien n'a changé, que l'ItemStack se trouvant dans le slot shift-cliqué n'a pas été modifié, sinon il faut retourner le contenu du slot tel qu'il était AVANT le shift-click.

    Nous allons donc préparer notre valeur de retour :

        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            ItemStack stackToReturn = ItemStack.EMPTY;
            Slot slot = this.inventorySlots.get(index);
    
            if (slot != null && slot.getHasStack()) {
                ItemStack stack = slot.getStack();
                stackToReturn = stack.copy();
            }
    
            return stackToReturn;
        }
    

    On utilise ItemStack#copy car lors de l'utilisation de Container#mergeItemStack notre ItemStack va être modifié et nous voulons pouvoir retourner l'ItemStack tel qu'il était avant modification. Pour la suite le même schéma va se répéter, suivant l'index donné on va essayer de placer l'ItemStack à un certain endroit, si Container#mergeItemStack renvoie faux, alors rien n'a changé et on retourne ItemStack#EMPTY. Imaginons un coffre qui a 10 slots et nous voulons faire passer l'ItemStack du l'inventaire du coffre à celui du joueur. Je vais condidérer que les slots du coffre ont les ids [[ 0 ; 9 ]] et ceux de l'inventaire du joueur ont les ids [[ 10 ; 45 ]]. Ainsi :
    Note : je rappelle que l'index de fin est exclu.

    
        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            ItemStack stackToReturn = ItemStack.EMPTY;
            Slot slot = this.inventorySlots.get(index);
    
            if (slot != null && slot.getHasStack()) {
                ItemStack stack = slot.getStack();
                stackToReturn = stack.copy();
    
                if (index < 10) {
                    // On inverse pour remplir la hotbar en premier
                    if (!this.mergeItemStack(stack, 10, 46, true)) {
                        return ItemStack.EMPTY;
                    }
                }
    
            }
    
            return stackToReturn;
        }
    

    On met bien de 10 à 46 car les ids des Slots de l'inventaire du joueur vont de 10 à 45, et on inverse pour que la fonction essai d'abord de remplir la hotbar. Mais le code n'est pas terminé, que ce passe-t-il actuellement si l'ItemStack a été déplacé ? On renvoie sa valeur de départ, donc c'est bon mais il faut quand même dire au slot que son contenu a changé et si l'ItemStack a complètement été déplacé il faut mettre ItemStack#EMPTY dans le slot. Ainsi :

    
        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            ItemStack stackToReturn = ItemStack.EMPTY;
            Slot slot = this.inventorySlots.get(index);
    
            if (slot != null && slot.getHasStack()) {
                ItemStack stack = slot.getStack();
                stackToReturn = stack.copy();
    
                if (index < 10) {
                    if (!this.mergeItemStack(stack, 10, 46, true)) {
                        return ItemStack.EMPTY;
                    }
                }
    
                // Si il a été complètement déplacé on met ItemStack#EMPTY dans le
                // slot
                if (stack.isEmpty()) {
                    slot.putStack(ItemStack.EMPTY);
                }
                // Sinon on signale que le contenu a changé.
                // Slot#putStack appelle Slot#onSlotChanged c'est pourquoi on ne le
                // fait pas ci-dessus
                else {
                    slot.onSlotChanged();
                }
    
            }
    
            return stackToReturn;
        }
    

    Et voilà, on va juste finir on rajoutant le chemin inverse, c'est à dire de l'inventaire du joueur vers le coffre :

    
        @Override
        public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
            ItemStack stackToReturn = ItemStack.EMPTY;
            Slot slot = this.inventorySlots.get(index);
    
            if (slot != null && slot.getHasStack()) {
                ItemStack stack = slot.getStack();
                stackToReturn = stack.copy();
    
                if (index < 10) {
                    if (!this.mergeItemStack(stack, 10, 46, true)) {
                        return ItemStack.EMPTY;
                    }
                } else if (!this.mergeItemStack(stack, 0, 10, false)) {
                    return ItemStack.EMPTY;
                }
    
                if (stack.isEmpty()) {
                    slot.putStack(ItemStack.EMPTY);
                } else {
                    slot.onSlotChanged();
                }
    
            }
            return stackToReturn;
        }
    

    Vous savez maintenant créer une fonction qui gère le shift-click et qui fonctionne. Il vous suffit de bien réfléchir quel comportement l'ItemStack doit avoir lors du shift-click et coder on conséquence.

    Crédits

    Rédaction :

    • BrokenSwing

    Ce tutoriel de BrokenSwing publié sur Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International

    Retour vers le sommaire des tutoriels