Créer une table de craft compatible avec JEI et NEI



  • Sommaire

    Introduction

    ❗ EDIT : La mise en forme du tutoriel a été changée pour être compatible avec la V4 de MFF, et le code vérifié mais si vous trouvez une erreur bizarre, demandez :)

    Dans ce tutoriel je vais vous montrer comment créer une table de craft entièrement customisable, vous pourrez la faire garder les items (empêcher les items d'être droppés à la fermeture du gui), via une TileEntity, et lui donner la possibilité d'être utilisée à plusieurs en même temps. Et vous pourrez aussi changer la taille des recettes, nous allons utiliser une taille de 4*4 dans ce tutoriel mais vous pourrez la modifier très simplement. Cette table de crafting permettra aussi d'utiliser simplement l'ore dictionary de Forge qui permet de donner un nom à un item/block pour une compatibilité entre mods. Ce tutoriel permet également d'apprendre à faire des guis.

    Pré-requis

    Code

    La classe principale :

    Tout d'abord, vous devez ajouter votre bloc comme si vous en ajoutiez un basique (je vous laisse le faire puisque vous avez tous lu le tuto), donnez-lui un nom et enregistrez-le.
    Ensuite, il va falloir enregistrer le GuiHandler, ajoutez ceci dans votre fonction init :

        NetworkRegistry.INSTANCE.registerGuiHander(instance, new GuiHandler());
    

    ceci va enregistrer la classe qui va permettre d'enregistrer tous vos guis, vous aurez une erreur, c'est normal nous créerons la classe plus tard.
    Vous en avez maintenant fini avec la classe principale et nous allons donc nous occuper du bloc.

    La classe du bloc :

    Pour que la fenêtre de craft s'affiche lors du clic droit, vous allez devoir ajouter cette fonction dans la classe de votre bloc :

    • 1.9.x :
        @Override
        public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ)
        {
         if (!worldIn.isRemote)
         {
         playerIn.openGui(ModTutoriel.instance, GuiHandler.guiCraftingTableID, worldIn, pos.getX(), pos.getY(), pos.getZ());
         }
                return true;
        }
    
    • 1.8.x :
       @Override
       public boolean onBlockActivated (World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ)
       {
        if (!worldIn.isRemote)
        {
        playerIn.openGui(ModTutoriel.instance, GuiHandler.guiCraftingTableID, worldIn, pos.getX(), pos.getY(), pos.getZ());
        }
        return true;
       }
    

    Cette fonction va ouvrir le gui si on est sur le serveur, puis le serveur enverra automatiquement un packet au client pour afficher ce gui, je fais la remarque que cette fonction n'est pas appelée si le joueur sneak. Vous aurez une erreur sur "GuiHandler.guiCraftingTableID", nous allons tout de suite corriger ça.

    Le GuiHandler :

    C'est lui qui va afficher un gui en fonction de l'id donné, il y a deux fonctions à l'intérieur, getServerGuiElement qui doit renvoyer un Container, c'est lui qui assurera la synchronisation, (il n'est pas obligatoire si le gui n'a pas d'inventaire, mais ça c'est autre chose). La deuxième fonction est getClientGuiElement, qui elle doit retourner le gui à afficher.
    Voici la classe toute "nue" (créez-en une si ce n'est pas déjà fait) :

        public class GuiHandler implements IGuiHandler 
        {
        @Override
        public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
        {
        BlockPos pos = new BlockPos(x, y, z);
        return null;
        }
    
        @Override
        public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
        {
        BlockPos pos = new BlockPos(x, y, z);
        return null;
        }
        }
    

    Puis ajoutez cette variable au début de votre classe, ce sera l'id du gui de crafting, vous pouvez mettre n'importe quel chiffre, mais il doit être différent pour chaque gui.

        public static final int guiCraftingTableID = 0;
    

    Ensuite rajoutez ceci dans votre fonction getServerGuiElement, vous comprendrez le code :

        if(ID == guiCraftingTableID)
        {
        return new ContainerCraftingTable(player.inventory, world, pos); //On retourne le container du gui, vous aurez une erreur, c'est normal
        }
    

    Puis mettez ceci dans la fonction getClientGuiElement :

        if(ID == guiCraftingTableID)
        {
        return new GuiCraftingTable(player.inventory, world, pos); //On retourne le gui, vous aurez une erreur, c'est normal
        }
    

    Le container :

    C'est lui qui va gérer les slots et leur synchronisation, créez une nouvelle classe (je vous conseille de laisser Container dans son nom) extends Container puis collez-y le code que je vais vous donner. Certains vont dire que c'est du bête copier-coller mais j'ai trouvé plus logique de tout donner, j'ai bien sur commenté chaque partie utile, donc voici le code que nous allons avoir :

    ❗ EDIT DU 11/01/16, le contenu de la fonction transferStackInSlot a été amélioré via notepad++ et n'a pas été testé, si vous avez un problème rapportez le moi.

       /** Largeur du craft */
       public static final int craftWidth = 4;
       /** Hauteur du craft */
       public static final int craftHeigth = 4;
    
       /** Inventaire contenant le craft */
       private final InventoryCrafting craftMatrix = new InventoryCrafting(this, craftWidth, craftHeigth);
       /** Inventaire contenant le résultat du craft */
       private final IInventory craftResult = new InventoryCraftResult();
    
       private final World worldObj;
       private final BlockPos pos;
    
       public RemplacerParLeNomDeVotreClasse(InventoryPlayer invPlayer, World world, BlockPos pos) 
       {
       this.worldObj = world; 
       this.pos = pos;
    
       //Ajout du slot pour le résultat
       this.addSlotToContainer(new TutorielSlotCrafting(invPlayer.player, craftMatrix, craftResult, 0, 141, 43));
    
       int startX = 8; //Position x ou les slots de craft commencent à être dessinés
       int startY = 7; //Position y ou les slots de craft commencent à être dessinés
       //Ajout des slots de craft
       for (int y = 0; y < craftHeigth; ++y)
       {
       for(int x = 0; x < craftWidth; ++x) 
       {
       this.addSlotToContainer(new Slot(craftMatrix, x + y * craftWidth, startX + x * 18, startY + y * 18));
       }
       }
    
       startX = 8; //Position x ou les slots de l'inventaire commencent à être dessinés
       startY = 106; //Position y ou les slots de l'inventaire commencent à être dessinés
       //Ajout des slots de l'inventaire du joueur
       for (int y = 0; y < 3; ++y) 
       {
       for(int x = 0; x < 9; ++x)
       {
       this.addSlotToContainer(new Slot(invPlayer, x + y * 9 + 9, startX + x * 18, startY + y * 18));
       }
       }
       startY = 164; //Position y ou les slots de la hotbar commencent à être dessinés
       //Ajout des slots de la hotbar
       for (int x = 0; x < 9; ++x) 
       {
       this.addSlotToContainer(new Slot(invPlayer, x, startX + x * 18, startY));
       }
       }
    
       /**
       * Appelé quand la matrice (les slots de craft) change
       */
       @Override
       public void onCraftMatrixChanged(IInventory iiventory)
       {
       //On met le résultat du craft dans le slot de résultat
       craftResult.setInventorySlotContents(0, TutorielCraftingManager.getInstance().findMatchingRecipe(craftMatrix, worldObj));
       }
    
       /**
       * Retourne true si le joueur peut interagir avec ce gui, en général on teste la distance par rapport au joueur dans cette fonction
       */
       @Override
       public boolean canInteractWith(EntityPlayer player) 
       {
       return this.worldObj.getBlockState(this.pos).getBlock() != ModTutoriel.cratingTable ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
       }
    
       /**
       * Appelé quand le container est fermé
       */
       @Override
       public void onContainerClosed(EntityPlayer player) 
       {
              super.onContainerClosed(player);
              if (!this.worldObj.isRemote) //Si on est sur le serveur, on loot les items à la fermeture du gui
              {
                  for (int i = 0; i < 9; ++i)
                  {
                      ItemStack itemstack = this.craftMatrix.removeStackFromSlot(i);
                      if (itemstack != null)
                      {
                       player.dropItem(itemstack, false); //TODO A utiliser en 1.9
                          //player.dropPlayerItemWithRandomChoice(itemstack, false); //TODO A utiliser en 1.8
                      }
                  }
              }
          }
    
       /**
       * Cette fonction est appelée lors du shift+clic (je vous conseille de la laisser comme tel, elle s'adaptera en fonction de la taille de votre craft)
       * EDIT 11/01/16 Le contenu de cette fonction a été modifié via notepad++ et n'a pas été testé, si vous avez un problème rapportez le moi
       */
       @Override
       public ItemStack transferStackInSlot(EntityPlayer player, int slotId)
       {
              ItemStack itemstack = null;
              Slot slot = this.inventorySlots.get(slotId);
    
              if (slot != null && slot.getHasStack())
              {
                  ItemStack itemstack1 = slot.getStack();
                  itemstack = itemstack1.copy();
    
                  int invStart = craftWidth * craftHeigth + 1;
         int hotbarStart = invStart + 27;
                 if (slotId == 0)
                 {
                     if (!this.mergeItemStack(itemstack1, invStart, hotbarStart + 9, true))
                     {
                         return null;
                     }
                     slot.onSlotChange(itemstack1, itemstack);
                 }
                 else if (slotId >= invStart && slotId < invStart + 27)
                 {
                     if (!this.mergeItemStack(itemstack1, hotbarStart, hotbarStart + 9, false))
                     {
                         return null;
                     }
                 }
                 else if (slotId >= hotbarStart && slotId < hotbarStart + 9)
                 {
                     if (!this.mergeItemStack(itemstack1, invStart, invStart + 27, false))
                     {
                         return null;
                     }
                 }
                 else if (!this.mergeItemStack(itemstack1, invStart, hotbarStart + 9, false))
                 {
                     return null;
                 }
                  if (itemstack1.stackSize == 0)
                  {
                      slot.putStack((ItemStack)null);
                  }
                  else
                  {
                      slot.onSlotChanged();
                  }
                  if (itemstack1.stackSize == itemstack.stackSize)
                  {
                      return null;
                  }
    
                  slot.onPickupFromSlot(player, itemstack1);
              }
              return itemstack;
          }
    
       /**
       * Appelé quand on double clic sur un slot :
           * Called to determine if the current slot is valid for the stack merging (double-click) code. The stack passed in
           * is null for the initial slot that was double-clicked.
       */
          public boolean canMergeSlot(ItemStack stack, Slot slotIn)
          {
              return slotIn.inventory != this.craftResult && super.canMergeSlot(stack, slotIn);
          }
    

    Vous pouvez voir tout en haut les variables qui vont gérer la taille de votre craft, vous n'aurez que celles-ci à changer et tout sera géré par le Container, j'ai également rajouté des variables dans le constructeur afin de modifier facilement la position où les slots sont dessinés (les variables startX et startY).

    ❗ Pour ceux en 1.8 : dans la fonction onGuiClosed, vous devrez avoir une erreur, j'ai mis la ligne qui est correcte en commentaire juste en dessous de la ligne erronée.

    Le container est maitenant fait, il devrait vous rester une erreur sur "TutorielSlotCrafting", c'est le slot qui va contenir le résultat, il doit avoir son propre code car il va retirer les items des slots de craft quand on cliquera dessus, créez-le en veillant à mettre extends Slot, voici sa classe :

       /** The craft matrix inventory linked to this result slot. */
          private final InventoryCrafting craftMatrix;
          /** The player that is using the GUI where this slot resides. */
          private final EntityPlayer thePlayer;
          /** The number of items that have been crafted so far. Gets passed to ItemStack.onCrafting before being reset. */
          private int amountCrafted;
    
          public ARemplacerParLeNomDeVotreClasse(EntityPlayer player, InventoryCrafting craftingInventory, IInventory inventoryIn, int slotIndex, int xPosition, int yPosition)
          {
              super(inventoryIn, slotIndex, xPosition, yPosition);
              this.thePlayer = player;
              this.craftMatrix = craftingInventory;
          }
    
          /**
           * Check if the stack is a valid item for this slot. Always true beside for the armor slots.
           */
          @Override
          public boolean isItemValid(@Nullable ItemStack stack)
          {
              return false;
          }
    
          /**
           * Decrease the size of the stack in slot (first int arg) by the amount of the second int arg. Returns the new
           * stack.
           */
          @Override
          public ItemStack decrStackSize(int amount)
          {
              if (this.getHasStack())
              {
                  this.amountCrafted += Math.min(amount, this.getStack().stackSize);
              }
              return super.decrStackSize(amount);
          }
    
          /**
           * the itemStack passed in is the output - ie, iron ingots, and pickaxes, not ore and wood. Typically increases an
           * internal count then calls onCrafting(item).
           */
          @Override
          protected void onCrafting(ItemStack stack, int amount)
          {
              this.amountCrafted += amount;
              this.onCrafting(stack);
          }
    
          /**
           * Appelé quand on craft, permet de gérer les achievements
           */
          @Override
          protected void onCrafting(ItemStack stack)
          {
              if (this.amountCrafted > 0)
              {
                  stack.onCrafting(this.thePlayer.worldObj, this.thePlayer, this.amountCrafted);
              }
              this.amountCrafted = 0;
              if (stack.getItem() == Item.getItemFromBlock(Blocks.CRAFTING_TABLE))
              {
                  this.thePlayer.addStat(AchievementList.BUILD_WORK_BENCH);
              }
              if (stack.getItem() instanceof ItemPickaxe)
              {
                  this.thePlayer.addStat(AchievementList.BUILD_PICKAXE);
              }
              if (stack.getItem() == Item.getItemFromBlock(Blocks.FURNACE))
              {
                  this.thePlayer.addStat(AchievementList.BUILD_FURNACE);
              }
              if (stack.getItem() instanceof ItemHoe)
              {
                  this.thePlayer.addStat(AchievementList.BUILD_HOE);
              }
              if (stack.getItem() == Items.BREAD)
              {
                  this.thePlayer.addStat(AchievementList.MAKE_BREAD);
              }
              if (stack.getItem() == Items.CAKE)
              {
                  this.thePlayer.addStat(AchievementList.BAKE_CAKE);
              }
              if (stack.getItem() instanceof ItemPickaxe && ((ItemPickaxe)stack.getItem()).getToolMaterial() != Item.ToolMaterial.WOOD)
              {
                  this.thePlayer.addStat(AchievementList.BUILD_BETTER_PICKAXE);
              }
              if (stack.getItem() instanceof ItemSword)
              {
                  this.thePlayer.addStat(AchievementList.BUILD_SWORD);
              }
              if (stack.getItem() == Item.getItemFromBlock(Blocks.ENCHANTING_TABLE))
              {
                  this.thePlayer.addStat(AchievementList.ENCHANTMENTS);
              }
              if (stack.getItem() == Item.getItemFromBlock(Blocks.BOOKSHELF))
              {
                  this.thePlayer.addStat(AchievementList.BOOKCASE);
              }
          }
    
          /**
           * Appelée quand le joueur retire l'item du slot, permet de retirer le composants utilisés pour le craft
           */
          @Override
          public void onPickupFromSlot(EntityPlayer playerIn, ItemStack stack)
          {
              net.minecraftforge.fml.common.FMLCommonHandler.instance().firePlayerCraftingEvent(playerIn, stack, craftMatrix); //Distribue l'event de craft
              this.onCrafting(stack); //Gestion des achievements
              net.minecraftforge.common.ForgeHooks.setCraftingPlayer(playerIn); //Utilisé afin de retirer les items utiliser (pas sur de cela)
              ItemStack[] aitemstack = TutorielCraftingManager.getInstance().getRemainingItems(this.craftMatrix, playerIn.worldObj); //On récupère les items restants
              net.minecraftforge.common.ForgeHooks.setCraftingPlayer(null);
    
              for (int i = 0; i < aitemstack.length; ++i) //On actualise les slots de craft
              {
                  ItemStack itemstack = this.craftMatrix.getStackInSlot(i);
                  ItemStack itemstack1 = aitemstack*;
                  if (itemstack != null)
                  {
                      this.craftMatrix.decrStackSize(i, 1);
                      itemstack = this.craftMatrix.getStackInSlot(i);
                  }
                  if (itemstack1 != null)
                  {
                      if (itemstack == null)
                      {
                          this.craftMatrix.setInventorySlotContents(i, itemstack1);
                      }
                      else if (ItemStack.areItemsEqual(itemstack, itemstack1) && ItemStack.areItemStackTagsEqual(itemstack, itemstack1))
                      {
                          itemstack1.stackSize += itemstack.stackSize;
                          this.craftMatrix.setInventorySlotContents(i, itemstack1);
                      }
                      else if (!this.thePlayer.inventory.addItemStackToInventory(itemstack1))
                      {
                          this.thePlayer.dropItem(itemstack1, false);
                      }
                  }
              }
          }
    

    J'ai commenté les fonctions utiles, le code ne devrait pas être compliqué à comprendre  🙂

    Maintenant que le container est terminé, occupons-nous du gui.

    Le gui :

    C'est lui qui va afficher tous les éléments à l'écran, je vais encore vous donner la classe entière commentée, mais attention, vous aurez des erreurs en copiant ceci car j'ai retiré quelques éléments que je vous donnerai après  😉
    Créez une nouvelle classe extends GuiContainer et collez-y le code suivant :

         public ARemplacerParLeNomDeVotreClasse(InventoryPlayer invPlayer, World world, BlockPos pos) 
        {
        super(new ContainerCraftingTable(invPlayer, world,pos));
        this.xSize = 176; //La largeur du gui en pixels (supprimez-le pour laisser celle par défaut)
        this.ySize = 188; //La hauteur du gui en pixels (supprimez-le pour laisser celle par défaut)
        }
    
        /**
        * Fonction pour dessiner le premier plan
        */
        @Override
        protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) 
        { 
        fontRendererObj.drawString(I18n.format("container.crafting_table"), 100, 5, 0xFFFFFF); //On dessine le "titre" du gui, le I18n.format va traduire le texte donné, n'oubliez pas de l'ajouter dans votre fichier de langues !
        }
    
        /**
        * Fonction pour dessiner l'arrière plan
        */
        @Override
        protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
        {
        mc.getTextureManager().bindTexture(texture); //On bind la texture
        drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); //Et on la dessine
        }
    

    Le titre va être traduit puis dessiné donc pensez bien à l'ajouter dans votre fichier de langues.
    Maintenant, ajoutons la déclaration de la texture (au début de la classe) :

        private ResourceLocation texture = new ResourceLocation(ModTutoriel.MODID, "textures/gui/CraftingTable.png");
    

    ❗ Pensez à remplacer "CraftingTable" par le nom de votre texture.

    Si vous voulez, vous pouvez essayer de lancer le jeu, et si vous n'avez pas changé la position des slots que j'ai donné, vous vous direz "Il y a un problème, c'est quoi ce tuto de ***" avec une tête comme celle là  😮 (pour ceux qui ont la flemme de lancer, le gui est coupé). La raison est que la taille par défaut des guis de Minecraft est trop petite, mais heuresement il suffit de rajouter ceci :

         this.xSize = 176; //La largeur du gui en pixels (supprimez-le pour laisser celle par défaut)
        this.ySize = 188; //La hauteur du gui en pixels (supprimez-le pour laisser celle par défaut)
    

    dans votre constructeur.

    Le gui est maintenant terminé, nous allons maintenant nous occuper du plus compliqué (pour moi, vu que vous, vous n'aurez rien à changer 😛 ), le système des recettes.

    Le système de recettes : CraftingManager et classes de recettes :

    Le CraftingManager va contenir toutes les recettes de votre table de craft, nous allons aussi ajouter deux classes de recettes, une pour chaque type, qui s'adapteront à n'importe quelle taille de craft (22, 33, 44, 58...), je précise que Forge a commencé à implémenter ceci dans les classes ShapedOreRecipe et ShapelessOreRecipe, mais étant donné qu'ils ne l'ont jamais terminé, il faut faire des classes custom.
    Voici le TutorielCraftingManager (créez sa classe) :

        import java.util.List;
        import java.util.Map;
    
        import javax.annotation.Nullable;
    
        import net.minecraft.block.Block;
        import net.minecraft.inventory.InventoryCrafting;
        import net.minecraft.item.Item;
        import net.minecraft.item.ItemStack;
        import net.minecraft.item.crafting.IRecipe;
        import net.minecraft.world.World;
    
        import com.google.common.collect.Lists;
        import com.google.common.collect.Maps;
    
        public class TutorielCraftingManager 
        {
           private static final TutorielCraftingManager INSTANCE = new TutorielCraftingManager();
           public static TutorielCraftingManager getInstance()
           {
               return INSTANCE;
           }
    
           /** La liste des recettes */
           private final List <irecipe>recipes = Lists.<irecipe>newArrayList();
    
           private TutorielCraftingManager()
           {
            //Vous pouvez ajouter des recettes ici
           }
    
           /**
            * Adds a shaped recipe to the games recipe list.
            */
           public TutorielShapedRecipes addRecipe(ItemStack result, Object... recipeComponents)
           {
               String s = "";
               int i = 0;
               int j = 0;
               int k = 0;
               if (recipeComponents* instanceof String[])
               {
                   String[] astring = (String[])((String[])recipeComponents[i++]);
                   for (int l = 0; l < astring.length; ++l)
                   {
                       String s2 = astring[l];
                       ++k;
                       j = s2.length();
                       s = s + s2;
                   }
               }
               else
               {
                   while (recipeComponents* instanceof String)
                   {
                       String s1 = (String)recipeComponents[i++];
                       ++k;
                       j = s1.length();
                       s = s + s1;
                   }
               }
               Character character;
               Map <character, object="">components = Maps.<character, object="">newHashMap();
               Object in;
               for ( ; i < recipeComponents.length; i += 2)
               {
                in = recipeComponents[i];
                   Object component = null;
                   character = (Character)recipeComponents*;
                   if (in instanceof Item)
                   {
                       component = new ItemStack((Item)recipeComponents[i]);
                   }
                   else if (in instanceof Block)
                   {
                       component = new ItemStack((Block)recipeComponents[i], 1, 32767);
                   }
                   else if (in instanceof ItemStack)
                   {
                       component = (ItemStack)recipeComponents[i];
                   }
                   else if (in instanceof String)
                   {
                    component = (String)in;
                   }
                   else
                   {
                    throw new IllegalArgumentException("Invalid shaped recipe: unknown type " + in.getClass().getName() + "!");
                   }
                   components.put(character, component);
               }
               Object[] aitemstack = new Object[j * k];
               char key;
               Object component;
               for (int i1 = 0; i1 < j * k; ++i1)
               {
                   key = s.charAt(i1);
                   if (components.containsKey(Character.valueOf(key)))
                   {
                    component = components.get(Character.valueOf(key));
                       if(component instanceof ItemStack) 
                        aitemstack[i1] = ((ItemStack)component).copy();
                       else
                        aitemstack[i1] = component;
                   }
                   else
                       aitemstack[i1] = null;
               }
               TutorielShapedRecipes shapedrecipes = new TutorielShapedRecipes(j, k, aitemstack, result);
               this.recipes.add(shapedrecipes);
               return shapedrecipes;
           }
    
           /**
            * Adds a shapeless crafting recipe to the the game.
            */
           public void addShapelessRecipe(ItemStack result, Object... recipeComponents)
           {
               List list = Lists.newArrayList();
               for (Object component : recipeComponents) //Pour chaque composant de la recette
               {
                   if (component instanceof ItemStack)
                   {
                       list.add(((ItemStack)component).copy());
                   }
                   else if (component instanceof Item)
                   {
                       list.add(new ItemStack((Item)component));
                   }
                   else if(component instanceof Block)
                   {
                       list.add(new ItemStack((Block)component));
                   }
                   else if(component instanceof String) //Pour l'ore dictionnary
                   {
                    list.add(component);
                   }
                   else throw new IllegalArgumentException("Invalid shapeless recipe: unknown type " + component.getClass().getName() + "!");
               }
               this.recipes.add(new TutorielShapelessRecipe(result, list));
           }
    
           /**
            * Adds an IRecipe to the list of crafting recipes.
            */
           public void addRecipe(IRecipe recipe)
           {
               this.recipes.add(recipe);
           }
    
           /**
            * Retourne le résultat de la recette ou null si il n'y en a aucun
            */
           @Nullable
           public ItemStack findMatchingRecipe(InventoryCrafting craftMatrix, World worldIn)
           {
               for (IRecipe irecipe : this.recipes) //Pour chaque recette
               {
                   if (irecipe.matches(craftMatrix, worldIn)) //Si elle correspond à la matrice actuelle
                   {
                       return irecipe.getCraftingResult(craftMatrix); //On donne son résultat
                   }
               }
               return null;
           }
    
           /**
            * Retourne les items retants après un craft
            */
           public ItemStack[] getRemainingItems(InventoryCrafting craftMatrix, World worldIn)
           {
               for (IRecipe irecipe : this.recipes) //Pour chaque recette
               {
                   if (irecipe.matches(craftMatrix, worldIn)) //Si elle correspond à la matrice actuelle
                   {
                       return irecipe.getRemainingItems(craftMatrix); //On retourne les items restants
                   }
               }
               ItemStack[] aitemstack = new ItemStack[craftMatrix.getSizeInventory()];
               for (int i = 0; i < aitemstack.length; ++i)
               {
                   aitemstack[i] = craftMatrix.getStackInSlot(i);
               }
               return aitemstack; //Si ça ne correspond à aucune recette, on retourne tous les items qui sont présents dans la matrice
           }
    
           public List <irecipe>getRecipeList()
           {
               return this.recipes;
           }
        }
    

    Voilà pour le CraftingManager, j'ai commenté chaque endroit utile, sauf la fonction addShapedRecipe qui elle est plus compliquée à comprendre et que vous n'êtes donc pas obligés de comprendre.

    Vous devriez avoir deux erreurs sur TutorielShapedRecipes et TutorielShapelessRecipe, créez les classes en veillant bien à ce qu'elle simplements IRecipe puis je vais vous donner le code :

    • TutorielShapedRecipes :
         /** How many horizontal slots this recipe is wide. */
           public final int recipeWidth;
           /** How many vertical slots this recipe uses. */
           public final int recipeHeight;
           /** Is a array of ItemStack that composes the recipe. */
           public final Object[] recipeItems;
           /** Is the ItemStack that you get when craft the recipe. */
           private final ItemStack recipeOutput;
           private boolean copyIngredientNBT;
    
           public TutorielShapedRecipes(int width, int height, Object[] items, ItemStack output)
           {
               this.recipeWidth = width;
               this.recipeHeight = height;
               this.recipeItems = items;
               this.recipeOutput = output;
           }
    
           public ItemStack getRecipeOutput()
           {
               return this.recipeOutput;
           }
    
           public ItemStack[] getRemainingItems(InventoryCrafting inv)
           {
               ItemStack[] aitemstack = new ItemStack[inv.getSizeInventory()];
               for (int i = 0; i < aitemstack.length; ++i)
               {
                   ItemStack itemstack = inv.getStackInSlot(i);
                   aitemstack[i] = net.minecraftforge.common.ForgeHooks.getContainerItem(itemstack);
               }
               return aitemstack;
           }
    
           /**
            * Used to check if a recipe matches current crafting inventory
            * Retourne true si la recette correspond à la matrice donnée (le craft que le joueur a fait)
            */
           public boolean matches(InventoryCrafting inv, World worldIn)
           {
               for (int i = 0; i <= inv.getWidth() - this.recipeWidth; ++i)
               {
                   for (int j = 0; j <= inv.getHeight() - this.recipeHeight; ++j)
                   {
                       if (this.checkMatch(inv, i, j, true))
                       {
                           return true;
                       }
                       if (this.checkMatch(inv, i, j, false))
                       {
                           return true;
                       }
                   }
               }
               return false;
           }
    
           /**
            * Checks if the region of a crafting inventory is match for the recipe.
            * Compare deux parties du craft
            */
           private boolean checkMatch(InventoryCrafting inv, int regionX, int regionY, boolean mirror)
           {
               for (int x = 0 ; x < inv.getWidth() ; ++x)
               {
                   for (int y = 0 ; y < inv.getHeight() ; ++y)
                   {
                       int x1 = x - regionX;
                       int y1 = y - regionY;
                       Object patternStack = null;
                       if (x1 >= 0 && y1 >= 0 && x1 < this.recipeWidth && y1 < this.recipeHeight)
                       {
                        if (mirror) 
                        patternStack = this.recipeItems[this.recipeWidth - x1 - 1 + y1 * this.recipeWidth];
                        else
                        patternStack = this.recipeItems[x1 + y1 * this.recipeWidth];
                        if(patternStack instanceof String)
                        {
                        List <itemstack>stacks = OreDictionary.getOres((String) patternStack);
                        boolean matches = false;
                        for(ItemStack stack : stacks)
                        {
                        if(areItemStacksEquals(stack, inv.getStackInRowAndColumn(x, y))) //If the pattern's stack doesn't match with the stack in the inv crafting
                        {
                        matches = true;
                        }
                        }
                        if(!matches)
                        return false;
                        }
                        else if(!areItemStacksEquals((ItemStack) patternStack, inv.getStackInRowAndColumn(x, y)))
                        {
                        return false;
                        }
                       }
                   }
               }
               return true;
           }
    
           /**
            * Compare les deux stacks
            */
           public static boolean areItemStacksEquals(ItemStack stack1, ItemStack stack2)
           {
            if(stack1 == null || stack2 == null) return stack1 == stack2;
            return stack1.getItem() == stack2.getItem() && (stack1.getMetadata() == OreDictionary.WILDCARD_VALUE || stack1.getMetadata() == stack2.getMetadata());
           }
    
           /**
            * Returns an Item that is the result of this recipe
            */
           public ItemStack getCraftingResult(InventoryCrafting inv)
           {
               ItemStack itemstack = this.getRecipeOutput().copy();
               if (this.copyIngredientNBT)
               {
                   for (int i = 0; i < inv.getSizeInventory(); ++i)
                   {
                       ItemStack itemstack1 = inv.getStackInSlot(i);
                       if (itemstack1 != null && itemstack1.hasTagCompound())
                       {
                           itemstack.setTagCompound((NBTTagCompound)itemstack1.getTagCompound().copy());
                       }
                   }
               }
               return itemstack;
           }
    
           /**
            * Returns the size of the recipe area
            */
           public int getRecipeSize()
           {
               return this.recipeWidth * this.recipeHeight;
           }
    
           /**
            * Set this crafting recipe to copy the NBT tag compound of the last ItemStack that has one in the crafting table.
            */
           public TutorielShapedRecipes setCopyIngredientNBT()
           {
               this.copyIngredientNBT = true;
               return this;
           }
    

    Vous devriez comprendre toutes les fonctions, à part une qui est plus compliquée, qui sert à comparer une partie de la recette avec la matrice que le joueur à rempli, vous n'aurez pas à modifier cette fonction, donc ce n'est pas très gênant si  vous ne la comprenez pas  😉

    • TutorielShapelessRecipe :
         /** Is the ItemStack that you get when craft the recipe. */
           private final ItemStack recipeOutput;
           /** Is a List of ItemStack that composes the recipe. */
           public final List recipeItems;
    
           public TutorielShapelessRecipe(ItemStack output, List inputList)
           {
               this.recipeOutput = output;
               this.recipeItems = inputList;
           }
    
           public ItemStack getRecipeOutput()
           {
               return this.recipeOutput;
           }
    
           public ItemStack[] getRemainingItems(InventoryCrafting inv)
           {
               ItemStack[] aitemstack = new ItemStack[inv.getSizeInventory()];
               for (int i = 0; i < aitemstack.length; ++i)
               {
                   ItemStack itemstack = inv.getStackInSlot(i);
                   aitemstack[i] = net.minecraftforge.common.ForgeHooks.getContainerItem(itemstack);
               }
               return aitemstack;
           }
    
           /**
            * Used to check if a recipe matches current crafting inventory
            * Retourne true si la recette correspond à la matrice donnée (le craft que le joueur a fait)
            * 
            * La fonction prend une liste des items requis pour le craft, puis pour chaque item rencontré dans la matrice, le retire de la liste des items,
            * si la liste ne contient pas l'item, c'est qu'il y a un item en trop dans le craft et la fonction retourne false, à la fin, si la liste est vide, la
            * fonction retourne true.
            */
           public boolean matches(InventoryCrafting inv, World worldIn)
           {
               ArrayList arraylist = Lists.newArrayList(this.recipeItems); //Copie les items du craft dans une nouvelle liste
               for (int i = 0; i < inv.getHeight(); ++i)
               {
                   for (int j = 0; j < inv.getWidth(); ++j)
                   {
                       ItemStack itemstack = inv.getStackInRowAndColumn(j, i);
                       if (itemstack != null)
                       {
                           boolean flag = false;
                           for(Object component : arraylist)
                           {
                            if(component instanceof String) //Search in ore dictionary
                            {
                            List <itemstack>stacks = OreDictionary.getOres((String) component);
                            for(ItemStack itemstack1 : stacks)
                            {
                            if (TutorielShapedRecipes.areItemStacksEquals(itemstack1, itemstack))
                                   {
                                       flag = true;
                                       arraylist.remove(itemstack1);
                                       break;
                                   }
                            }
                            }
                            else
                            {
                               ItemStack itemstack1 = (ItemStack)component;
                               if (TutorielShapedRecipes.areItemStacksEquals(itemstack1, itemstack))
                               {
                                   flag = true;
                                   arraylist.remove(itemstack1);
                                   break;
                               }
                            }
                           }
                           if (!flag)
                            return false;
                       }
                   }
               }
               return arraylist.isEmpty();
           }
    
           /**
            * Returns an Item that is the result of this recipe
            */
           public ItemStack getCraftingResult(InventoryCrafting inv)
           {
               return this.recipeOutput.copy();
           }
    
           /**
            * Returns the size of the recipe area
            */
           public int getRecipeSize()
           {
               return this.recipeItems.size();
           }
    

    Il y a une fonction qui peut-être compliquée à comprendre, j'ai donc résumé son fonctionnement en commentaire, mais vous n'aurez de toute façon pas à la modifier.

    • Note : en 1.8.9, dans la classe TutorielShapedRecipe, vous devez changer les getMetadata() par des getItemDamage().
      J'ai adapté chacune de ces classes à partir de celles vanilla afin qu'elles acceptent n'importe quel taille de craft et qu'elles acceptent les items de l'ore dictionnary (au lieu de mettre des ItemStack quand vous ajoutez votre recette, vous pourrez utiliser des String correspondant à un minerai/bloc/item/lingot... de l'ore dictionnary (ici et ici pour plus d'infos (en Anglais)).

    Voilà c'est terminé, vous pouvez maintenant utiliser votre table de craft, si vous voulez un exemple pour ajouter les recettes, regardez le résultat.

    Faire une table de craft qui ne pert pas ses items :

    Dans Minecraft, lorsque l'on ferme la fenêtre de craft, les items qui sont à l'intérieur tombent au sol, dans cette partie, je vais vous expliquer comment éviter cela, il suffira d'utiliser un tile entity (entitée de bloc) qui sauvegardera vos items.

    Modifications du bloc :

    Nous allons tout d'abord modifier le code du bloc afin qu'il puisse "créer" le tile entity : changer le extends de votre block en BlockContainer puis ajoutez cette fonction :

        @Override
        public TileEntity createNewTileEntity(World worldIn, int meta) 
        {
        return new TileEntityCraftingTable();
        }
    
    • 1.9.x : pour une raison qui m'est inconnue, la classe BlockContainer redéfini la fonction getRenderType qui retourne le type de rendu et en retourne un invisible, il faut donc nous-mêmes redéfinir la fonction comme ceci :
        @Override
        public EnumBlockRenderType getRenderType(IBlockState state)
        {
            return EnumBlockRenderType.MODEL;
        }
    

    dans la classe de votre block afin d'avoir un rendu de block standard.Si vous souhaitez que votre de table craft puisse avoir un nom custom, ajoutez  également cette fonction, appelée quand le block est posé par une entité :

         @Override
        public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
        {
        super.onBlockPlacedBy(world, pos, state, placer, stack);
        if(stack.hasDisplayName()) //Si l'item de l'entité a un nom custom
        {
        TileEntity te = world.getTileEntity(pos);
        if(te instanceof TileEntityCraftingTable) //On vérifie que c'est la bonne TileEntity
        {
        ((TileEntityCraftingTable) te).setCustomName(stack.getDisplayName()); //On lui donne le nom
        }
        }
        }
    

    Vous aurez une erreur sur new TileEntityCraftingTable(), créez la classe en veillant bien à mettre extends TileEntity et implements IInventory.

    L'entité de bloc (TileEntity) :

    La classe est assez longue, donc je vais donner le code en entier, commenté par mes soins 🙂

        private String customName; //Le nom custom
        private ItemStack[] inventory = new ItemStack[ContainerCraftingTable.craftWidth * ContainerCraftingTable.craftHeigth]; //L'inventaire contenant les ingrédients
    
        @Override
        public String getName() 
        {
        return hasCustomName() ? customName : I18n.format("container.crafting_table");
        }
        @Override
        public boolean hasCustomName() 
        {
        return customName != null;
        }
        @Override
        public ITextComponent getDisplayName()
        {
        return new TextComponentString(getName());
        }
           public void setCustomName(String name)
           {
               this.customName = name;
           }
    
        @Override
        public int getSizeInventory()
        {
        return inventory.length;
        }
    
        /**
            * Returns the stack in the given slot.
            */
        @Override
           @Nullable
           public ItemStack getStackInSlot(int index)
           {
               return this.inventory[index];
           }
    
           /**
            * Removes up to a specified number of items from an inventory slot and returns them in a new stack.
            * Retire une quantité donnée d'items d'un slot
            */
        @Override
           @Nullable
           public ItemStack decrStackSize(int index, int count)
           {
               ItemStack itemstack;
               if (index >= 0 && index < inventory.length && inventory[index] != null && count > 0)
               {
                   itemstack = inventory[index].splitStack(count);
                   if (inventory[index].stackSize == 0)
                    inventory[index] = null;
               }
               else 
                itemstack =  null;
               if (itemstack != null) 
                this.markDirty();
               return itemstack;
           }
    
           /**
            * Removes a stack from the given slot and returns it.
            */
        @Override
           @Nullable
           public ItemStack removeStackFromSlot(int index)
           {
               if (index >= 0 && index < inventory.length)
               {
                   ItemStack itemstack = inventory[index];
                   inventory[index] = null;
                   return itemstack;
               }
               else
               {
                   return null;
               }
           }
    
           /**
            * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
            */
        @Override
           public void setInventorySlotContents(int index, @Nullable ItemStack stack)
           {
               this.inventory[index] = stack;
               if (stack != null && stack.stackSize > this.getInventoryStackLimit())
               {
                   stack.stackSize = this.getInventoryStackLimit();
               }
               this.markDirty();
           }
    
        @Override
           public void readFromNBT(NBTTagCompound compound)
           {
               super.readFromNBT(compound);
               if (compound.hasKey("CustomName", 8)) //Si le tag un à String (8) contenant le nom
               {
                   this.customName = compound.getString("CustomName"); //Lecture du nom custom
               }
               NBTTagList nbttaglist = compound.getTagList("Items", 10);
               for (int i = 0; i < nbttaglist.tagCount(); ++i) //Pour chaque tag de la liste des items
               {
                   NBTTagCompound nbttagcompound = nbttaglist.getCompoundTagAt(i);
                   int j = nbttagcompound.getInteger("Slot");
                   if (j >= 0 && j < this.inventory.length)
                   {
                       this.inventory[j] = ItemStack.loadItemStackFromNBT(nbttagcompound); //On place l'item lu dans l'inventaire
                   }
               }
           }
    
        @Override
           public NBTTagCompound writeToNBT(NBTTagCompound compound)
           {
               super.writeToNBT(compound);
               NBTTagList nbttaglist = new NBTTagList();
               //On place tous les items dans une liste sous forme de tags
               for (int i = 0; i < this.inventory.length; ++i)
               {
                   if (this.inventory* != null)
                   {
                       NBTTagCompound nbttagcompound = new NBTTagCompound();
                       nbttagcompound.setInteger("Slot", i);
                       this.inventory*.writeToNBT(nbttagcompound);
                       nbttaglist.appendTag(nbttagcompound);
                   }
               }
               compound.setTag("Items", nbttaglist);
               if (this.hasCustomName()) //Si il y a un nom custom
               {
                   compound.setString("CustomName", this.customName); //On le sauvegarde
               }
               return compound;
           }
    
           /**
            * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended.
            */
        @Override
           public int getInventoryStackLimit()
           {
               return 64;
           }
    
           /**
            * Do not make give this method the name canInteractWith because it clashes with Container
            */
        @Override
           public boolean isUseableByPlayer(EntityPlayer player)
           {
               return this.worldObj.getTileEntity(this.pos) != this ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
           }
    
        @Override
        public void openInventory(EntityPlayer player) {}
    
        @Override
        public void closeInventory(EntityPlayer player) {}
    
        @Override
        public boolean isItemValidForSlot(int index, ItemStack stack)
        {
        return index >= 0 && index < inventory.length;
        }
    
        @Override
        public int getField(int id) 
        {
        return 0;
        }
    
        @Override
        public void setField(int id, int value) {}
    
        @Override
        public int getFieldCount()
        {
        return 0;
        }
    
        @Override
        public void clear()
        {
        int size = inventory.length;
        inventory = new ItemStack;
        }
    

    C'est tout pour cette classe, maintenant, il faut enregistrer le tile entity pour que Minecraft le reconnaisse : dans la fonction init de votre classe principale, ajoutez ceci :

        GameRegistry.registerTileEntity(TileEntityCraftingTable.class, "CraftingTable");
    

    Maintenant, modifions le Container et le Gui pour qu'ils fonctionnent en symbiose avec le tile entity 🙂

    Modifications du container :

    Nous allons ajouter tout ce qu'il faut pour que votre container lise le contenu des slots du tileentity à son ouverture et update ce contenu lorsqu'il est modifié. Ajoutez

        private final TileEntityCraftingTable tileentity;
    

    à vos variables puis dans le constructeur, ajoutez "TileEntityCraftingTable tileentity" aux arguments et ajoutez ceci :

         //Pour chaque slot du tileentity, on update les slots de la matrice
        for(int slot = 0 ; slot < craftMatrix.getSizeInventory() ; slot++)
        {
        craftMatrix.setInventorySlotContents(slot, tileentity.getStackInSlot(slot));
        }
        this.tileentity = tileentity;
    

    ❗ Veillez à laisser "this.tileentity = tileentity;" après le for car la modification de la matrice va appeler la fonction onCraftMatrixChanged qui elle va changer le contenu du tile entity, ce que nous ne voulons pas étant donné que la matrice n'est pas encore complète (en gros c'est compliqué et ça m'a fait perdre 10 minutes de ma vie :'().

    Ensuite, modifions cette fameuse fonction onCraftMatrixChanged et ajoutons ceci :

         if(tileentity != null)
        {
        //Pour chaque slot de la matrice, on update les slots du tileentity
        for(int slot = 0 ; slot < tileentity.getSizeInventory() ; slot++)
        {
        tileentity.setInventorySlotContents(slot, craftMatrix.getStackInSlot(slot));
        }
        }
    

    ceci, si on a complété la matrice à l'ouverture du gui ("if(tileentity != null)"), va update les contenus du tile entity à chaque modification de la matrice.
    Ensuite, pour finir, retirez la fonction onContainerClosed qui elle droppait le contenu du container, ce que nous ne voulons plus.

    Modifications du gui:

    Il nous reste encore à modifier le gui afin qu'il puisse afficher le nom custom.
    Comme pour le Container, ajoutez

        private final TileEntityCraftingTable tileentity;
    

    à vos variables et dans le constructeur, ajoutez "TileEntityCraftingTable tileentity" aux arguments puis remplacez la première ligne du constructeur par ceci :

         super(new ContainerCraftingTable(invPlayer, world,pos, tileentity));
        this.tileentity = tileentity;
    

    (on ajoute "tileentity" au contructeur du Container et on défini la variable "tileentity").
    Et pour finir, modifiez la ligne où vous dessinez le titre par ceci :

        fontRendererObj.drawString(tileentity.getName(), 100, 5, 0xFFFFFF); //On dessine le "titre" du gui
    

    Si vous enregistrez maintenant, vous aurez des erreurs dans votre GuiHandler, corrigeons-les :

    Modifications du GuiHandler :

    Ajoutez ceci

        TileEntity tileentity = world.getTileEntity(pos);
    

    après la déclaration de votre variable "BlockPos pos" dans les deux fonctions du GuiHandler, puis pour le Container et pour le Gui, ajoutez ceci

        (TileEntityCraftingTable) tileentity
    

    à l'appel de leur constructeur pour correspondre à ce que nous avons mis dans les classes.

    Résultat

    Voici le résultat obtenu :

    Et le code permettant d'ajouter les recettes correspondant :

        this.addShapelessRecipe(new ItemStack(Blocks.ANVIL), Items.CARROT, Items.GOLDEN_APPLE);
        this.addRecipe(new ItemStack(cratingTable), " C ", "X X", " C ", 'C', "slabWood", 'X', Blocks.PLANKS); //"slabWood" est le nom des dalles de bois dans l'ore dictionnary, ceci est ajouté par Forge
        this.addRecipe(new ItemStack(Items.GOLDEN_APPLE), "A  A", "X  X", "X  X", "A  A", 'A', Items.CARROT, 'X', Blocks.PLANKS);
    
    

    à placer dans le constructeur du TutorielCraftingManager.

    (Commme vous pouvez le voir, j'ai insisté sur le côté rp des recettes ^^).

    Vous avez maintenant fini de lire cet assez long tuto, bravo à vous ! Je vais ajouter la possibilité de garder les items dans la table de craft très bientôt :), si vous voulez un bonus particulier ou si vous avez un problème quelque part (j'ai fait une erreur ou vous ne comprenez pas quelque chose), n'hésitez pas à demander  😉

    Bonus

    • Intégrer JEI (Just Enought Items, dérivé de NEI) à la table de craft : ici
    • Intégrer NEI (Not Enought Items) à la table de craft : ici
    • Proposez-moi ce que vous souhaitez avoir en bonus 🙂

    Crédits

    Rédaction :

    • AymericRed

    Correction/Améliorations :

    • MajorSquirrel pour une idée d'amélioration de la fonction transferStackInSlot du Container


    Ce tutoriel de AymericRed 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



  • Très bonne idée ce tutoriel ça peut permettre de créer un table de craft custom rien que pour les crafts d'un mod 😉

    Edit: pour le bonus je ne sais pas si cela rentre dans la catégorie mais peut-être comment changer la table de craft qui se trouve
    dans l'inventaire ce serait très pratique je trouve de pouvoir tout craft à porter de main ^^



  • Pour l'idée de bonus, ça peut être un truc cool, mais étant donné qu'il faudrait remplacer le gui de l'inventaire, je pense qu'il faudrait un tuto à part entière.



  • Ajout NEI ? en bonus bis



  • Ah oui ça, ça serait bien à mettre ici, je verrais…



  • Déjà gg AymericRed et merci beaucoup à toi pour ce tuto grâce à toi je vais réalisé ce que j'ai voulu faire il à longtemps 🙂
    après en bonus tu pourra rajouté 
    NEI,de décraft les objet qu'on on a craft du genre je craft une épée spécial avec 1 stick et 8 diamand et par rapport à sa durabilité par exemple si il est à 200 on récupéré 2 diamant

    Merci infiniment pour ce tuto 🙂

    Edit 
    comme je pourrais mettre ceci

    @Override
    public boolean canInteractWith(EntityPlayer player) 
    {
    return this.worldObj.getBlockState(this.pos).getBlock() != enderdeath.AnvilDragon ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
    }
    
    

    en 1.7.10?



  • De rien 🙂
    Donc du coup NEI c'est prévu, et pour ta proposition, il suffit d'ajouter les crafts mais en inversé.



  • @'AymericRed':

    De rien 🙂
    Donc du coup NEI c'est prévu, et pour ta proposition, il suffit d'ajouter les crafts mais en inversé.

    Merci grace à toi j'ai fait ma nouvelle table de craft dans mon mod en 1.9 et je m'attaque pour une nouvelle table de craft pour mon mod en 1.7.10 🙂
    (sa va je pensé que c'était plus dur de passé du codage 1.9 en 1.7.10 mais c'est bon ^^)
    et pour NEI Yes 🙂
    et pour les craft inversé je vais m'y mettre après la nouvelle table de craft en 1.7.10 🙂

    Merci beaucoup 🙂

    Ps ce tuto = 5 étoiles 🙂

    Edit comme je peux passé ceci

    @Override
    public boolean canInteractWith(EntityPlayer player) 
    {
    return this.worldObj.getBlockState(this.pos).getBlock() != enderdeath.AnvilDragon ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
    }
    
    

    en 1.7.10



  • getBlockState(..).getBlock() devient getBlock et ensuite il faut que tu fasses trois variables pour X, y, et z.

    Envoyé de mon RAINBOW LITE 4G en utilisant Tapatalk



  • J'ai un autre problème dans cette classe

    package ed.enderdeath.mod.AnvilDragon;
    
    import com.sun.istack.internal.Nullable;
    
    import cpw.mods.fml.common.FMLCommonHandler;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.init.Blocks;
    import net.minecraft.init.Items;
    import net.minecraft.inventory.IInventory;
    import net.minecraft.inventory.InventoryCrafting;
    import net.minecraft.inventory.Slot;
    import net.minecraft.item.Item;
    import net.minecraft.item.ItemHoe;
    import net.minecraft.item.ItemPickaxe;
    import net.minecraft.item.ItemStack;
    import net.minecraft.item.ItemSword;
    import net.minecraft.stats.AchievementList;
    import net.minecraftforge.common.ForgeHooks;
    
    public class DragonSlotCrafting extends Slot {
    /** The craft matrix inventory linked to this result slot. */
      private final InventoryCrafting craftMatrix;
      /** The player that is using the GUI where this slot resides. */
      private final EntityPlayer thePlayer;
      /** The number of items that have been crafted so far. Gets passed to ItemStack.onCrafting before being reset. */
      private int amountCrafted;
    
      public DragonSlotCrafting(EntityPlayer player, InventoryCrafting craftingInventory, IInventory inventoryIn, int slotIndex, int xPosition, int yPosition)
      {
          super(inventoryIn, slotIndex, xPosition, yPosition);
          this.thePlayer = player;
          this.craftMatrix = craftingInventory;
      }
    
      /**
       * Check if the stack is a valid item for this slot. Always true beside for the armor slots.
       */
      @Override
      public boolean isItemValid(@Nullable ItemStack stack)
      {
          return false;
      }
    
      /**
       * Decrease the size of the stack in slot (first int arg) by the amount of the second int arg. Returns the new
       * stack.
       */
      @Override
      public ItemStack decrStackSize(int amount)
      {
          if (this.getHasStack())
          {
              this.amountCrafted += Math.min(amount, this.getStack().stackSize);
          }
          return super.decrStackSize(amount);
      }
    
      /**
       * the itemStack passed in is the output - ie, iron ingots, and pickaxes, not ore and wood. Typically increases an
       * internal count then calls onCrafting(item).
       */
      @Override
      protected void onCrafting(ItemStack stack, int amount)
      {
          this.amountCrafted += amount;
          this.onCrafting(stack);
      }
    
      /**
       * Appelé quand on craft, permet de gérer les achievements
       */
      @Override
      protected void onCrafting(ItemStack stack)
      {
          if (this.amountCrafted > 0)
          {
              stack.onCrafting(this.thePlayer.worldObj, this.thePlayer, this.amountCrafted);
          }
          this.amountCrafted = 0;
    
          if (stack.getItem() == Item.getItemFromBlock(Blocks.bookshelf))
          {
              this.thePlayer.addStat(AchievementList.bookcase, 1);
          }
      }
    
      /**
       * Appelée quand le joueur retire l'item du slot, permet de retirer le composants utilisés pour le craft
       */
      @Override
      public void onPickupFromSlot(EntityPlayer playerIn, ItemStack stack)
      {
         FMLCommonHandler.instance().firePlayerCraftingEvent(playerIn, stack, craftMatrix); //Distribue l'event de craft
          this.onCrafting(stack); //Gestion des achievements
         ForgeHooks.setCraftingPlayer(playerIn); //Utilisé afin de retirer les items utiliser (pas sur de cela)
          ItemStack[] aitemstack = TutorielCraftingManager.getInstance().getRemainingItems(this.craftMatrix, playerIn.worldObj); //On récupère les items restants
          ForgeHooks.setCraftingPlayer(null);
    
          for (int i = 0; i < aitemstack.length; ++i) //On actualise les slots de craft
          {
              ItemStack itemstack = this.craftMatrix.getStackInSlot(i);
              ItemStack itemstack1 = aitemstack*;
              if (itemstack != null)
              {
                  this.craftMatrix.decrStackSize(i, 1);
                  itemstack = this.craftMatrix.getStackInSlot(i);
              }
              if (itemstack1 != null)
              {
                  if (itemstack == null)
                  {
                      this.craftMatrix.setInventorySlotContents(i, itemstack1);
                  }
                  else if (ItemStack.areItemStacksEqual(itemstack, itemstack1) && ItemStack.areItemStackTagsEqual(itemstack, itemstack1))
                  {
                      itemstack1.stackSize += itemstack.stackSize;
                      this.craftMatrix.setInventorySlotContents(i, itemstack1);
                  }
                  else if (!this.thePlayer.inventory.addItemStackToInventory(itemstack1))
                  {
                      this.thePlayer.dropOneItem(false);
                  }
              }
          }
      }
    }
    
    

    Le problème c'est que setCraftingPlayer n'existe pas donc j'ai éssayé de trouve une alternative mais j'ai pas trouvé



  • Pour le slot, je regarde tout à l'heure, pour le gui, ça devrait marcher mais retire xSize1 et ySize1, c'est totalement inutile.

    EDIT : PS : Tu peux regarder dans la classe SlotCrafting de mc qui est comparable à celle du tuto.

    Envoyé de mon RAINBOW LITE 4G en utilisant Tapatalk



  • Merci beaucoup 🙂

    Mais j'ai un autre problème

       return irecipe.getRemainingItems(craftMatrix); //On retourne les items restants
    
    getRemainingItems(craftMatrix);
    ``` n'existe pas j'ai essaye de trouvé un internative mais cela ne marche pas ):
    
    Merci de ta réponse :)


  • Essaye un icrecipe.func_quelquechose qui retourne la même chose (array de stack) et qui a un InventoryCrafting en paramètre.



  • c'est à dire il na pas de func dans la classe IRecipe et pour moi les seul truc que j'ai son :

    getCraftingResult

    matches

    getRecipeSize

    plus précisément

    boolean matches(InventoryCrafting p_77569_1_, World p_77569_2_);

    ItemStack getCraftingResult(InventoryCrafting p_77572_1_);

    int getRecipeSize();

    après c'est peut être ma version de forge   😛

    Merci de ta réponse rapide 🙂



  • Ah bon, je pensait qu'il y aurait ça en 1.7.10, ça doit fonctionner autrement, il faut regarder le SlotCrafting de Minecraft pour voir comment est-ce qu'il fait.



  • Quelques updates du tuto ont été réalisées :

    • Modification du code du Container, changement des variables craftWidth et craftHeigth de private à public et ajout de "final" aux différents variables.
    • Rédaction de la partie "Faire une table de craft qui ne pert pas ses items" terminée.

    Le bonus sur NEI arrivera quand NEI en 1.9.4 existera 🙂



  • @'AymericRed':

    Quelques updates du tuto ont été réalisées :

    • Modification du code du Container, changement des variables craftWidth et craftHeigth de private à public et ajout de "final" aux différents variables.
    • Rédaction de la partie "Faire une table de craft qui ne pert pas ses items" terminée.

    Le bonus sur NEI arrivera quand NEI en 1.9.4 existera 🙂

    Bravo sa complète ce tutoriel très bon 🙂

    Bonne chance pour NEI 🙂

    Pour revenir à mon problème après plusieurs recherche je n'ai pas trouvé peut être que je devrais cherché dans la classe qui gère les crafts

    Merci de complété ce tutoriel  qui est déjà très bon  et de ton aide 😄



  • Merci du compliment ^^ Je regarderais cet aprem dans les sources 1.7 que je devrais toujours avoir, pour ton problème 🙂

    Envoyé de mon RAINBOW LITE 4G en utilisant Tapatalk



  • Si c'est ce que je pense, avant la 1.8 c'était une autre méthode qui était utilisée : on configurait l'item qui restait depuis l'item et non depuis le craft, ça a du être modifié dans les dernières versions. Peut être qu'en retirant cette partie là ça fonctionnera (à vérifier)



  • Alors j'ai regardé rapidement le SlotCrafting de mc 1.7.10, il faut que tu retires toutes les fonctions getRemainingItems du crafting manager et des classes de recette, puis recopies la fonction onPickupFromSlot de la classe SlotCrafting dans la classe de ton Slot.