• S'inscrire
    • Se connecter
    • Recherche
    • Récent
    • Mots-clés
    • Populaire
    • Utilisateurs
    • Groupes

    [1.12] Créer un four

    Les interfaces (GUI) et les container
    1.11.x
    13
    58
    13638
    Charger plus de messages
    • Du plus ancien au plus récent
    • Du plus récent au plus ancien
    • Les plus votés
    Répondre
    • Répondre à l'aide d'un nouveau sujet
    Se connecter pour répondre
    Ce sujet a été supprimé. Seuls les utilisateurs avec les droits d'administration peuvent le voir.
    • BrokenSwing
      BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par robin4002

      Sommaire

      • Introduction
      • Pré-requis
      • Code
        • La classe principale
        • La classe du bloc
        • La classe du TileEntity
        • La classe des recettes
        • La classe du container
        • La classe du GUI
      • Résultat
      • Crédits

      Introduction

      Nous allons créer un bloc qui prend des items en entrée et qui sort d’autres items en sortie comme à la façon d’un four par exemple.
      Pour cet exemple j’ai choisi un bloc qui prendra 2 objets en entrée, 2 objets pour l’alimenter et qui sortira 1 objet.
      Je vous montrerez cependant comment personnaliser le code pour obtenir ce que vous voulez mais pour cela il va falloir
      bien comprendre le code donc soyez attentif.

      Pré-requis

      • Créer un bloc basique
      • Créer un gui et un container sur un bloc OUTDATED
      • Ajouter un TileEntity au bloc OUTDATED

      Code

      La classe principale

      On va commencer par déclarer notre bloc, dans ce tutoriel je l’appellerai custom_furnace

      public static final Block CUSTOM_FURNACE = new BlockCustomFurnace().setRegistryName("modid:custom_furnace");
      

      Pensez à respecter la convention Java, cette variable est static et final, utilisez donc le SNAKE_CASE.

      Créez la classe BlockCustomFurnace.

      Enregistrez alors votre bloc :

      @SubscribeEvent
      public static void registerBlocks(RegistryEvent.Register<Block> event) {
          event.getRegistry().register(ModBlocks.CUSTOM_FURNACE);
      }
      

      Dans la méthode preInit de votre classe principale, enregistrez votre TileEntity :

      GameRegistry.registerTileEntity(TileCustomFurnace.class, "modid:tile_custom_furnace");
      

      Créez la classe TileCustomFurnace.

      Il faut aussi enregistrer le GUI Handler, mais ça devrait déjà être fait grâce aux pré-requis :

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

      Voilà pour la classe principale.

      La classe du bloc

      Allez maintenant dans la classe du bloc et faite-la hériter à BlockContainer :

      public class BlockCustomFurnace extends BlockContainer
      

      Ajoutez le constructeur :

      public BlockCustomFurnace() {
          super(Material.rock); // Choisissez ce que que vous voulez
          // Autres paramètres
      }
      

      On va maintenant déclarer que le bloc possède un TileEntity et dire quel TileEntity créer :

      @Override
      public boolean hasTileEntity() {
          return true;
      }
      
      @Override
      public TileEntity createNewTileEntity(World world, int metadata)  {
          return new TileCustomFurnace();
      }
      

      Maintenant occupons-nous de la méthode qui va permettre de drop les items quand on casse le bloc :

      @Override
      public void breakBlock(World worldIn, BlockPos pos, IBlockState state) {
          TileEntity tileentity = worldIn.getTileEntity(pos);
      
          if (tileentity instanceof TileCustomFurnace) {
              InventoryHelper.dropInventoryItems(worldIn, pos,
                      (TileCustomFurnace) tileentity);
          }
      
          super.breakBlock(worldIn, pos, state);
      }
      

      Et maintenant la méthode pour ouvrir le gui lorsqu’on fait clique droit sur le bloc :

      @Override
      public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
          if (world.isRemote) {
              return true;
          } else {
              TileEntity tileentity = world.getTileEntity(pos);
      
              if (tileentity instanceof TileCustomFurnace) {
                  player.openGui(ModTuto.instance, 0, world, pos.getX(),
                          pos.getY(), pos.getZ());
              }
      
              return true;
          }
      }
      

      Pour que votre bloc soit rendu comme un bloc normal il va falloir Override la fonction getRenderType :

      @Override
      public EnumBlockRenderType getRenderType(IBlockState state) {
          return EnumBlockRenderType.MODEL;
      }
      

      Et ceci pour mettre un nom personnalisé au bloc une fois posé :

      @Override
      public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
          if (stack.hasDisplayName()) {
              TileEntity tileentity = worldIn.getTileEntity(pos);
      
              if (tileentity instanceof TileCustomFurnace) {
                  ((TileCustomFurnace) tileentity).setCustomName(stack
                          .getDisplayName());
              }
          }
      }
      

      Vous avez sûrement des errreurs, ignorez-les, nous n’avons pas encore créé les différentes méthodes.

      Voilà pour la classe du bloc.

      La classe du TileEntity

      Allez dans la classe du TileCustomFurnace.

      On va déclarer plusieurs variables

      private NonNullList<ItemStack> stacks = NonNullList.withSize(5, ItemStack.EMPTY);
      private String customName;
      private int	timePassed = 0;
      private int	burningTimeLeft	= 0;
      

      Quelques explications :

      • customName contient le nom personnalisé du bloc si il en a un
      • stacks contient les ItemStack de votre bloc autrement dit tout les slots, c’est ici que sont stockés les items
      • timePassed contient l’avancement de la recette, il représente le temps passé
      • burningTimeLeft contient le temps restant avant avant qu’il n’y est plus de feux
        Maintenant faisont hériter la classe à TileEntityLockable et implémentons ITickable
      public class TileCustomFurnace extends TileEntityLockable implements ITickable
      

      N’implémentez pas les méthodes, on va le faire à la main.

      Tout d’abord les méthodes pour lire et écrire dans les NBT, dedans on lit, on écrit, rien de sorcier quand on sait utiliser les NBT :

      @Override
      public void readFromNBT(NBTTagCompound compound) {
          super.readFromNBT(compound);
          this.stacks = NonNullList.<ItemStack>withSize(this.getSizeInventory(), ItemStack.EMPTY);
          ItemStackHelper.loadAllItems(compound, this.stacks);
      
          if (compound.hasKey("CustomName", 8)) {
              this.customName = compound.getString("CustomName");
          }
          this.burningTimeLeft = compound.getInteger("burningTimeLeft");
          this.timePassed = compound.getInteger("timePassed");
      }
      
      @Override
      public NBTTagCompound writeToNBT(NBTTagCompound compound) {
          super.writeToNBT(compound);
          ItemStackHelper.saveAllItems(compound, this.stacks);
      
          if (this.hasCustomName()) {
              compound.setString("CustomName", this.customName);
          }
      
          compound.setInteger("burningTimeLeft", this.burningTimeLeft);
          compound.setInteger("timePassed", this.timePassed);
      
          return compound;
      }
      

      La fonction hasCustomName n’existe pas, nous allons la créer et tout ce qui va avec :

      @Override
      public boolean hasCustomName() {
          return this.customName != null && !this.customName.isEmpty();
      }
      
      @Override
      public String getName() {
          return hasCustomName() ? this.customName : "tile.custom_furnace";
      }
      
      public void setCustomName(String name) {
          this.customName = name;
      }
      

      Nous allons à présent créer les fonctions qui vont permettre d’accéder aux variables burningTimeLeft et timePassed :

      @Override
      public int getField(int id) {
          switch (id) {
              case 0:
                  return this.burningTimeLeft;
              case 1:
                  return this.timePassed;
          }
          return 0;
      }
      
      @Override
      public void setField(int id, int value) {
          switch (id) {
              case 0:
                  this.burningTimeLeft = value;
                  break;
              case 1:
                  this.timePassed = value;
          }
      }
      
      @Override
      public int getFieldCount() {
          return 2;
      }
      

      Ce sont juste des getters et setters.

      Maintenant, créons les fonctions qui permettrons de manipuler les ItemStack de nos slots :

      @Override
      public int getSizeInventory() {
          return this.stacks.size();
      }
      
      @Override
      public ItemStack getStackInSlot(int index) {
          return this.stacks.get(index);
      }
      
      @Override
      public ItemStack decrStackSize(int index, int count) {
          return ItemStackHelper.getAndSplit(this.stacks, index, count);
      }
      
      @Override
      public ItemStack removeStackFromSlot(int index) {
          return ItemStackHelper.getAndRemove(stacks, index);
      }
      
      @Override
      public void setInventorySlotContents(int index, ItemStack stack) {
          this.stacks.set(index, stack);
      
          if (stack.getCount() > this.getInventoryStackLimit()) {
              stack.setCount(this.getInventoryStackLimit());
          }
      }
      
      @Override
      public int getInventoryStackLimit() {
          return 64;
      }
      
      @Override
      public boolean isEmpty() {
          for(ItemStack stack : this.stacks) {
              if (!stack.isEmpty()) {
                  return false;
              }
          }
          return true;
      }
      
      @Override
      public void clear() {
          for(int i = 0; i < this.stacks.size(); i++) {
              this.stacks.set(i, ItemStack.EMPTY);
          }
      }
      

      Toutes ces fonctions sont très simples, je ne vais pas les détaillés, l’interface IInventory indique de toute façon leur utilité.
      Rajoutez aussi ces deux fonctions qui seront appelées lors de l’ouverture et de la fermeture de l’inventaire :

      @Override
      public void openInventory(EntityPlayer player) {}
      
      @Override
      public void closeInventory(EntityPlayer player) {}
      

      Rajoutez aussi ceux deux autres fonctions qui ne sont utiles qu’à Minecraft :

      @Override
      public Container createContainer(InventoryPlayer playerInventory, EntityPlayer playerIn) {
          return null;
      }
      
      @Override
      public String getGuiID() {
          return null;
      }
      

      Maintenant nous allons définir ce que peut contenir chaque slot (cette fonction ne sert que pour l’automatisation, pas pour le GUI) :

      @Override
      public boolean isItemValidForSlot(int index, ItemStack stack) {
          // Le slot 3 je n'autorise que les planches de bois
          if (index == 2)
              return OreDictionary.getOres("plankWood").contains(
                      new ItemStack(stack.getItem(), 1,
                              OreDictionary.WILDCARD_VALUE));
          // Le slot 4 je n'autorise que le blé
          if (index == 3)
              return stack.getItem() == Items.WHEAT;
          // Le slot 5 (celui du résultat) je n'autorise rien
          if (index == 4)
              return false;
          // Sinon pour les slots 1 et 2 on met ce qu'on veut
          return true;
      }
      

      Il nous faut aussi une fonction qui sera appelée depuis le Container pour savoir si le joueur peut utiliser l’inventaire :

      /** Vérifie la distance entre le joueur et le bloc et que le bloc soit toujours présent */
      public boolean isUsableByPlayer(EntityPlayer player) {
          return this.world.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;
      }
      

      Maintenant nous allons nous occuper du processus de cuisson, afin de nous aider nous allons créer quelques fonctions :

      public boolean hasFuelEmpty() {
          return this.getStackInSlot(2).isEmpty()
                  || this.getStackInSlot(3).isEmpty();
      }
      

      Permet de savoir si une slot de carburant est vide.

      public ItemStack getRecipeResult() {
          return RecipesCustomFurnace.getRecipeResult(new ItemStack[] {
                  this.getStackInSlot(0), this.getStackInSlot(1) });
      }
      

      La classe RecipesCustomFurnace n’existe toujours pas, c’est la partie d’après. Cette fonction sert à récupérer la recette associée aux ingrédients.

      public boolean canSmelt() {
          // On récupère le résultat de la recette
          ItemStack result = this.getRecipeResult();
      
          // Le résultat est null si il n'y a pas de recette associée, donc on retourne faux
          if (result != null) {
      
              // On récupère le contenu du slot de résultat
              ItemStack slot4 = this.getStackInSlot(4);
      
              // Si il est vide on renvoie vrai
              if (slot4.isEmpty())
                  return true;
      
              // Sinon on vérifie que ce soit le même objet, les même métadata et que la taille finale ne sera pas trop grande
              if (slot4.getItem() == result.getItem() && slot4.getItemDamage() == result.getItemDamage()) {
                  int newStackSize = slot4.getCount() + result.getCount();
                  if (newStackSize <= this.getInventoryStackLimit() && newStackSize <= slot4.getMaxStackSize()) {
                      return true;
                  }
              }
          }
          return false;
      }
      

      Cette fonction renvoie vrai si on peut faire cuire les ingrédients, c’est à dire que les ingrédients sont bons et que le résultat de la recette
      peut être mis dans le slot du résultat.

      Nous allons à présent rajouter la fonction qui fait cuire les ingrédients (qui transforme les ingrédient en résultat de la recette) :

      public void smelt() {
          // Cette fonction n'est appelée que si result != null, c'est pourquoi on ne fait pas de null check
          ItemStack result = this.getRecipeResult();
          // On enlève un item de chaque ingrédient
          this.decrStackSize(0, 1);
          this.decrStackSize(1, 1);
          // On récupère le slot de résultat
          ItemStack stack4 = this.getStackInSlot(4);
          // Si il est vide
          if (stack4.isEmpty()) {
              // On y insère une copie du résultat
              this.setInventorySlotContents(4, result.copy());
          } else {
              // Sinon on augmente le nombre d'objets de l'ItemStack
              stack4.setCount(stack4.getCount() + result.getCount());
          }
      }
      

      Et trois dernière fonctions auxiliaires pour nous aider :

      /** Temps de cuisson de la recette */
      public int getFullRecipeTime() {
          return 200;
      }
      
      /** Temps que dure 1 unité de carburant (ici : 1 planche + 1 blé) */
      public int getFullBurnTime() {
          return 300;
      }
      
      /** Renvoie vrai si le feu est allumé */
      public boolean isBurning() {
          return burningTimeLeft > 0;
      }
      

      Nous allons implémenter notre toute dernière fonction, celle de ITickable :

      @Override
      public void update() {
      
      }
      

      Elle est appelée à chaque tick. Tout ce qui se trouve dans la fonction devra être exécuté côté serveur donc :

      @Override
      public void update() {
          if (!this.world.isRemote) {
      
          }
      }
      

      Tout d’abord si le four est allumé on va diminuer le temps restant du feu :

      @Override
      public void update() {
          if (!this.world.isRemote) {
      
              /* Si le carburant brûle, on réduit réduit le temps restant */
              if (this.isBurning()) {
                  this.burningTimeLeft--;
              }
          }
      }
      

      Si le four n’est pas allumé, que le la recette est bonne et qu’il y a du carburant, alors on allume le four :

      @Override
      public void update() {
          if (!this.world.isRemote) {
      
              /* Si le carburant brûle, on réduit réduit le temps restant */
              if (this.isBurning()) {
                  this.burningTimeLeft--;
              }
      
              /*
                  * Si la on peut faire cuire la recette et que le four ne cuit pas
                  * alors qu'il peut, alors on le met en route
                  */
              if (!this.isBurning() && this.canSmelt() && !this.hasFuelEmpty()) {
                  this.burningTimeLeft = this.getFullBurnTime();
                  this.decrStackSize(2, 1);
                  this.decrStackSize(3, 1);
              }
          }
      }
      

      Et maintenant si le four est allumé et que la recette est bonne, alors on augmente l’avancement de la recette. Si l’avancement de la recette est au maximum
      alors on cuit les ingrédients.

      @Override
      public void update() {
          if (!this.world.isRemote) {
      
              /* Si le carburant brûle, on réduit réduit le temps restant */
              if (this.isBurning()) {
                  this.burningTimeLeft--;
              }
      
              /*
                  * Si la on peut faire cuire la recette et que le four ne cuit pas
                  * alors qu'il peut, alors on le met en route
                  */
              if (!this.isBurning() && this.canSmelt() && !this.hasFuelEmpty()) {
                  this.burningTimeLeft = this.getFullBurnTime();
                  this.decrStackSize(2, 1);
                  this.decrStackSize(3, 1);
              }
      
              /* Si on peut faire cuire la recette et que le feu cuit */
              if (this.isBurning() && this.canSmelt()) {
                  this.timePassed++;
                  if (timePassed >= this.getFullRecipeTime()) {
                      timePassed = 0;
                      this.smelt();
                  }
              } else {
                  timePassed = 0;
              }
              this.markDirty();
          }
      }
      

      Voilà, à présent nous allons passer à la classe des recettes.

      La classe des recettes

      Créez la classe RecipesCustomFurnace si ce n’est pas fait. Déclarez la HashMap suivante :

      private static final HashMap <ItemStack[], ItemStack>recipes = new HashMap<ItemStack[], ItemStack>();
      static {
          addRecipe(Items.APPLE, Items.ARROW, Items.BAKED_POTATO);
      }
      

      Elle permettra de faire le lien entre les ingrédients et le résultat des recettes.
      Le scope static permet de rajouter une recette.

      Déclarons maintenant les fonctions pour ajouter les recettes :

      private static void addRecipe(Item ingredient1, Item ingredient2, Item resultat1) {
          addRecipe(new ItemStack(ingredient1), new ItemStack(ingredient2), new ItemStack(resultat1));
      }
      
      private static void addRecipe(ItemStack ingredient1, ItemStack ingredient2, ItemStack resultat1) {
          recipes.put(new ItemStack[]{ingredient1, ingredient2}, resultat1);
      }
      

      Créons aussi cette fonction pour comparer les ItemStack :

      private static boolean areKeysEqual(ItemStack[] key1, ItemStack[] key2) {
          if(key1.length != key2.length) return false;
      
          for(int i = 0; i < key1.length; i++) {
              ItemStack s1 = key1[i];
              ItemStack s2 = key2[i];
              if(s1.isEmpty() && !s2.isEmpty()) return false;
              if(!s1.isEmpty() && s2.isEmpty()) return false;
              if(s1.getItem() != s2.getItem()) return false;
              if(s1.getItemDamage() != s2.getItemDamage()) return false;
          }
          return true;
      }
      

      Et la fonction qui permet de trouver la recette :

      public static ItemStack getRecipeResult(ItemStack[] ingredients) {
          Iterator<Entry<ItemStack[], ItemStack>> it = recipes.entrySet().iterator();
          while(it.hasNext()) {
              Entry <ItemStack[], ItemStack>entry = it.next();
              if(areKeysEqual(entry.getKey(), ingredients)) {
                  return entry.getValue();
              }
          }
          return null;
      }
      

      La classe du container

      Créez la classe ContainerCustomFurnace, faites-la hériter de Container.
      Déclarer 3 variables :

      private TileCustomFurnace tile;
      private int	timePassed = 0;
      private int	burnTimeLeft = 0;
      

      On ajoute les slots au container dans le constructeur :

      public ContainerCustomFurnace(TileCustomFurnace tile, InventoryPlayer playerInventory) {
          this.tile = tile;
      
          int i;
          for(i = 0; i < 2; i++) {
              this.addSlotToContainer(new Slot(tile, i, 33 + i * 18, 7));
          }
          for(i = 0; i < 2; i++) {
              this.addSlotToContainer(new SlotSingleItem(tile, i + 2, 42, 40 + i * 18, i == 0 ? Item.getItemFromBlock(Blocks.PLANKS) : Items.WHEAT));
          }
          this.addSlotToContainer(new SlotOutput(tile, 4, 116, 17));
      
          for(i = 0; i < 3; ++i) {
              for(int j = 0; j < 9; ++j) {
                  this.addSlotToContainer(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
              }
          }
      
          for(i = 0; i < 9; ++i) {
              this.addSlotToContainer(new Slot(playerInventory, i, 8 + i * 18, 142));
          }
      }
      

      Les slots SlotSingleItem et SlotOutput sont des slots créés par moi-même, on verra ça après.
      La focntion pour savoir si le joueur peut utiliser le containe :

      @Override
      public boolean canInteractWith(EntityPlayer player) {
          return tile.isUsableByPlayer(player);
      }
      

      Et les fonctions pour mettre à jour les valeurs du TileEntity pour l’affichage sur le client :

      @Override
      public void addListener(IContainerListener listener) {
          super.addListener(listener);
          listener.sendAllWindowProperties(this, this.tile);
      }
      
      @Override
      public void detectAndSendChanges() {
          super.detectAndSendChanges();
      
          for(int i = 0; i < this.listeners.size(); ++i) {
              IContainerListener icontainerlistener = (IContainerListener) this.listeners
                      .get(i);
      
              if (this.burnTimeLeft != this.tile.getField(0)) {
                  icontainerlistener.sendProgressBarUpdate(this, 0,
                          this.tile.getField(0));
              }
      
              if (this.timePassed != this.tile.getField(1)) {
                  icontainerlistener.sendProgressBarUpdate(this, 1,
                          this.tile.getField(1));
              }
          }
      
          this.burnTimeLeft = this.tile.getField(0);
          this.timePassed = this.tile.getField(1);
      }
      
      @Override
      @SideOnly(Side.CLIENT)
      public void updateProgressBar(int id, int data) {
          this.tile.setField(id, data);
      }
      

      Si vous êtes en 1.12+, la fonction sendProgressBarUpdate est devenue sendWindowProperty.

      Et la fonction pour gérer le shift+clic, étant donné que cette fonction est relative au nombre de slot choisis, etc … Je désactive ici le SHIFT + CLIC :

      @Override
      public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
          return ItemStack.EMPTY;
      }
      

      On va créer nos deux classe de Slot :

      public class SlotOutput extends Slot {
      
          public SlotOutput(IInventory inventoryIn, int index, int xPosition, int yPosition) {
              super(inventoryIn, index, xPosition, yPosition);
          }
      
          @Override
          public boolean isItemValid(ItemStack stack) {
              return false;
          }
      }
      

      Et :

      public class SlotSingleItem extends Slot {
      
          private Item item;
      
          public SlotSingleItem(IInventory inventoryIn, int index, int xPosition, int yPosition, Item item) {
              super(inventoryIn, index, xPosition, yPosition);
              this.item = item;
          }
      
          @Override
          public boolean isItemValid(ItemStack stack) {
              return stack.isEmpty() || stack.getItem() == item;
          }
      }
      

      La classe du GUI

      Dernière étape, le GUI. Créez la classe GuiCustomFurnace et mettez-la dans le package client. Il faut étendre la classe à GuiContainer.

      public class GuiCustomFurnace extends GuiContainer
      

      Il faut ensuite déclarer deux variables :

      private static final ResourceLocation background = new ResourceLocation("modid","textures/gui/container/custom_furnace.png");
      private TileCustomFurnace tile;
      

      Explications :

      • texture : lien vers le fond du GUI
      • tile : TileEntity associé à ce GUI, sera le même que celui du container
        A présent mettez le constructeur suivant :
      public GuiCustomFurnace(TileCustomFurnace tile, InventoryPlayer playerInv) {
              super(new ContainerCustomFurnace(tile, playerInv));
              this.tile = tile;
      }
      

      Les deux fonctions suivantes permettent de dessiner le Gui :

      • drawGuiContainerBackgroundLayer permet de dessiner l’arrière plan
      • drawGuiContainerForegroundLayer permet de dessiner le permier plan

      Vous pouvez dessiner à l’aide des fonctions :

      this.drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height)
      

      x correspond à la coordonnée x de l’endroit où vous voulez afficher votre texture
      y correspond à la coordonnée y de l’endroit où vous voulez afficher votre texture
      textureX correspond à la coordonnée x du morceau de texture que vous voulez afficher
      textureY correspond à la coordonnée y du morceau de texture que vous voulez afficher
      width correspond à largeur du morceau de texture que vous voulez afficher
      height correspond à la hauteur du morceau de texture que vous voulez afficher

      Quand vous utilisez cette fonction, il faut associer la texture au textureManager de minecraft, il faut
      donc mettre 1 fois au début de la fonction

      this.mc.getTextureManager().bindTexture(background);
      

      On peut écrire à l’aide de cette fonction

      this.fontRendererObj.drawString(String texte, int x, int, y, int color)
      

      texte est le texte que vous voulez afficher
      x est la coordonnée x de l’endroit où vous voulez l’afficher
      y est la coordonnée y de l’endroit où vous voulez l’afficher
      color est la couleur du texte

      Pour notre custom_furnace j’ai codé ceci :

      
      @Override
      protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
          int i = (this.width - this.xSize) / 2;
          int j = (this.height - this.ySize) / 2;
          this.drawDefaultBackground();
          this.mc.getTextureManager().bindTexture(background);
          this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
      
          int timePassed = this.tile.getField(1);
          int textureWidth = (int) (23f / 200f * timePassed);
          this.drawTexturedModalRect(i + 81, j + 24, 177, 18, textureWidth, 7);
      
          if (this.tile.isBurning()) {
              int burningTime = this.tile.getField(0);
              int textureHeight = (int) (12f / this.tile.getFullBurnTime() * burningTime);
              this.drawTexturedModalRect(i + 37, j + 26 + 12 - textureHeight,
                      177, 12 - textureHeight, 27, textureHeight);
          }
      
          this.fontRenderer.drawString(this.tile.getName(), i + 80, j + 45, 0xFFFFFF);
      }
      

      Avec cette texture :
      0_1529702355595_custom_furnace.png

      Les valeurs ont été ajustées en debug, le petit scarabé à côté du bonton play, en debug mode vous pouvez changer les
      valeurs dans les fonctions et ce sera automatiquement mis à jour en jeu. (Pour le container il fait ré-ouvrir le GUI)

      Il vous reste plus qu’à mettre le GuiHandler comme il le faut, aller je vous le donne :

      public class GuiHandler implements IGuiHandler {
      
          @Override
          public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
              TileEntity te = world.getTileEntity(new BlockPos(x, y, z));
              if(te instanceof TileCustomFurnace) {
                  return new ContainerCustomFurnace((TileCustomFurnace)te, player.inventory);
              }
              return null;
          }
      
          @Override
          public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
              TileEntity te = world.getTileEntity(new BlockPos(x, y, z));
              if(te instanceof TileCustomFurnace) {
                  return new GuiCustomFurnace((TileCustomFurnace)te, player.inventory);
              }
              return null;
          }
      }
      

      Résultat

      0_1529702367263_custom_furnace_final.png

      Crédits

      Rédaction :

      • BrokenSwing

      Correction :

      • BrokenSwing

      Creative Commons
      Ce tutoriel de 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

      retourRetour vers le sommaire des tutoriels

      1 réponse Dernière réponse Répondre Citer 2
      • Tituya
        Tituya dernière édition par

        Ouahhhh, TRES bon travail, juste une question, depuis quand tu écris ce sujet, depuis 3mois 😄 je ne vais pas tester, c’est trop long pour moi mais je te dis bravo quand même ^^

        Il y a deux choses qui m'énerve dans la vie : les babouches et les personnes jouant en 1.7.10 !

        1 réponse Dernière réponse Répondre Citer 1
        • BrokenSwing
          BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

          Je l’ai écrit ce matin et en début d’après-midi, c’est vrai que c’est long mais en organisant correctement tes classes tu peux être capable de créer plusieurs machines sans tout refaire à chaque fois. En tout cas merci.

          1 réponse Dernière réponse Répondre Citer 0
          • Deleted
            Deleted dernière édition par

            Bonjour,
            J’ai crée mon réacteur nucléaire avec ma classe ReactorRecipes qui enregistre les différentes recette sous cette forme:

            addItemRecipe(Items.COAL, new ItemStack(ItemHandler.radioactiveCoal), 0.35F);
            public void addItemRecipe(Item itemCombustible, ItemStack result, float experience)

            Mais ma question est de savoir comment rajouter un 4ème argument déterminant le temps de la cuisson ? J’ai vaguement vu que tu en parlais dans la classe Container, mais je voudrai le mettre dans ma classe Recipes. Une piste, un indice ? Merci d’avance !

            1 réponse Dernière réponse Répondre Citer 0
            • LeBossMax2
              LeBossMax2 dernière édition par

              Je pense qu’il suffit de changer la méthode getFullRecipeTime() pour donner la valeur en fonction de la recette en cours. Ensuite, dans le Gui, pour que la barre de chargement fonctionne bien, il faut modifier dans la méthode drawGuiBackgroundLayer(), le calcul de textureWidth en changent le 200f en this.tile.getFullRecipeTime().

              1 réponse Dernière réponse Répondre Citer 0
              • BrokenSwing
                BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                Il faut que tu renvoie une valeur différente suivant les “ingrédients” que tu as dans ta recette ici :

                
                /** Temps de cuisson de la recette */
                public int getFullRecipeTime() {
                return 200;
                }
                
                

                Avec une fonction du type : public static int ReactorRecipes.getTimeNeededFor(ItemStack[] ingredients)

                1 réponse Dernière réponse Répondre Citer 0
                • Deleted
                  Deleted dernière édition par

                  Merci à vous deux, je vais essayer !
                  EDIT = Tout marche à merveille 😃

                  1 réponse Dernière réponse Répondre Citer 0
                  • Tituya
                    Tituya dernière édition par

                    Ahah, je vais peut etre essayé finalement, j’ai l’idée de quelques chose qui va pas etre piqué des hannetons 😄 je vous demanderais de l’aide si je n’arrive pas !

                    Il y a deux choses qui m'énerve dans la vie : les babouches et les personnes jouant en 1.7.10 !

                    1 réponse Dernière réponse Répondre Citer 0
                    • A
                      aypristyle dernière édition par

                      Bonjour j’ai suivi le tutoriel mais j’ai une petite erreur dans ma classe Container dans la méthode pour mettre à jour ma TileEntity voici l’erreur en question :
                      sur la ligne ```java
                      if (this.burnTimeLeft != this.tile.getField(0)) {
                      icontainerlistener.sendProgressBarUpdate(this, 0,
                      this.tile.getField(0));
                      }

                      voici 
                      
                      • The method sendProgressBarUpdate(ContainerCustomFurnace, int, int) is undefined for the type
                        IContainerListener

                      **Je suis un membre apprécié et joueur, j'ai déjà obtenu 1[ point de réputation./…

                      1 réponse Dernière réponse Répondre Citer 0
                      • BrokenSwing
                        BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                        ContainerCustomFurnace hérite bien de Container ?

                        1 réponse Dernière réponse Répondre Citer 0
                        • A
                          aypristyle dernière édition par

                          oui l’héritage est bon, je met ma classe Container ci-dessous

                          package fr.priya.simpleores.gui;
                          
                          import net.minecraft.entity.player.EntityPlayer;
                          import net.minecraft.entity.player.InventoryPlayer;
                          import net.minecraft.init.Blocks;
                          import net.minecraft.inventory.InventoryCrafting;
                          import net.minecraft.init.Items;
                          import net.minecraft.inventory.Container;
                          import net.minecraft.inventory.IContainerListener;
                          import net.minecraft.inventory.Slot;
                          import net.minecraft.item.Item;
                          import net.minecraft.item.ItemStack;
                          import net.minecraftforge.fml.relauncher.Side;
                          import net.minecraftforge.fml.relauncher.SideOnly;
                          
                          public class ContainerCustomFurnace extends Container{
                          private TileEntityCustomFurnace tile;
                          private int timePassed = 0;
                          private int burnTimeLeft = 0;
                          
                          public ContainerCustomFurnace(TileEntityCustomFurnace tile, InventoryPlayer playerInventory)
                          {
                          int i;
                          for(i = 0; i < 2; i++)
                          {
                          this.addSlotToContainer(new Slot(tile, i, 33 + i*18, 7));
                          }
                          for(i = 0; i < 2; i++)
                          {
                          this.addSlotToContainer(new SlotSingleItem(tile, i + 2, 42, 40 + i* 18, i == 0 ? Item.getItemFromBlock(Blocks.PLANKS) : Items.WHEAT));
                          }
                          this.addSlotToContainer(new SlotOutput(tile, 4, 116, 17));
                          
                          for(i = 0; i < 3; ++i) {
                          for(int j = 0; j < 9; ++j) {
                          this.addSlotToContainer(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
                          }
                          }
                          
                          for(i = 0; i < 9; ++i) {
                          this.addSlotToContainer(new Slot(playerInventory, i, 8 + i * 18, 142));
                          }
                          }
                          
                          public boolean canInteractWith(EntityPlayer player)
                          {
                          return tile.isUsableByPlayer(player);
                          }
                          
                          public void addListener(IContainerListener listener) {
                          super.addListener(listener);
                          listener.sendAllWindowProperties(this, this.tile);
                          }
                          
                          @Override
                          public void detectAndSendChanges() {
                          super.detectAndSendChanges();
                          
                          for(int i = 0; i < this.listeners.size(); ++i) {
                          IContainerListener icontainerlistener = (IContainerListener) this.listeners.get(i);
                          
                          if (this.burnTimeLeft != this.tile.getField(0)) {
                          icontainerlistener.sendProgressBarUpdate(this, 0,
                          this.tile.getField(0));
                          }
                          
                          if (this.timePassed != this.tile.getField(1)) {
                          icontainerlistener.sendProgressBarUpdate(this, 1,
                          this.tile.getField(1));
                          }
                          }
                          
                          this.burnTimeLeft = this.tile.getField(0);
                          this.timePassed = this.tile.getField(1);
                          }
                          
                          @SideOnly(Side.CLIENT)
                          public void updateProgressBar(int id, int data) {
                          this.tile.setField(id, data);
                          }
                          
                          public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
                          return ItemStack.EMPTY;
                          }
                          
                          }
                          
                          

                          **Je suis un membre apprécié et joueur, j'ai déjà obtenu 1[ point de réputation./…

                          1 réponse Dernière réponse Répondre Citer 0
                          • BrokenSwing
                            BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                            C’est bizarre parce que la définition est la suivante : IContainerListener#sendProgressBarUpdate(Container containerIn, int varToUpdate, int newValue)
                            Donc il n’y a pas de raison pour que cela ne fonctionne pas.


                            Ce tutoriel concerne la 1.11, es-tu bien en 1.11 ?

                            1 réponse Dernière réponse Répondre Citer 0
                            • AymericRed
                              AymericRed dernière édition par

                              Non elle a changé de nom récemment (1.12 je crois), regardes dans le code l’interface IContainerListener il y aura une fonction avec les mêmes arguments c’est celle là.

                              Si je vous ai aidé, n'oubliez pas d’être heureux, j'aiderai encore +

                              AymericRed, moddeur expérimenté qui aide sur ce forum et qui peut accepter de faire un mod Forge rémunéré de temps en temps.

                              Mes tutos : Table de craft, plugin NEI, plugin JEI, modifier l'overlay
                              Je suis un membre apprécié et joueur, j'ai déjà obtenu 6 points de réputation.

                              1 réponse Dernière réponse Répondre Citer 1
                              • BrokenSwing
                                BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                                C’est bien pourquoi je précise que ce tutoriel est pour la 1.11.

                                1 réponse Dernière réponse Répondre Citer 1
                                • A
                                  aypristyle dernière édition par

                                  Je suis bien en 1.11 je vais essayer d’update mes mappings pour voir
                                  EDIT: même avec les derniers mappings ça ne fonctionne pas je l’ai remplacer par la fonction sendWindowProperty et maitenant mon jeu crash lorsque je fait clique droit sur mon block
                                  Voici les logs

                                  –-- Minecraft Crash Report ----
                                  // Surprise! Haha. Well, this is awkward.
                                  
                                  Time: 16/07/17 11:56
                                  Description: Ticking player
                                  
                                  java.lang.NullPointerException: Ticking player
                                  at fr.priya.simpleores.gui.ContainerCustomFurnace.detectAndSendChanges(ContainerCustomFurnace.java:62)
                                  at net.minecraft.entity.player.EntityPlayerMP.onUpdate(EntityPlayerMP.java:293)
                                  at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2152)
                                  at net.minecraft.world.WorldServer.updateEntityWithOptionalForce(WorldServer.java:879)
                                  at net.minecraft.world.World.updateEntity(World.java:2119)
                                  at net.minecraft.world.WorldServer.tickPlayers(WorldServer.java:680)
                                  at net.minecraft.world.World.updateEntities(World.java:1908)
                                  at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:651)
                                  at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:795)
                                  at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:699)
                                  at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156)
                                  at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:548)
                                  at java.lang.Thread.run(Unknown Source)
                                  
                                  A detailed walkthrough of the error, its code path and all known details is as follows:
                                  ---------------------------------------------------------------------------------------
                                  
                                  -- Head --
                                  Thread: Server thread
                                  Stacktrace:
                                  at fr.priya.simpleores.gui.ContainerCustomFurnace.detectAndSendChanges(ContainerCustomFurnace.java:62)
                                  at net.minecraft.entity.player.EntityPlayerMP.onUpdate(EntityPlayerMP.java:293)
                                  at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2152)
                                  at net.minecraft.world.WorldServer.updateEntityWithOptionalForce(WorldServer.java:879)
                                  at net.minecraft.world.World.updateEntity(World.java:2119)
                                  
                                  -- Player being ticked --
                                  Details:
                                  Entity Type: null (net.minecraft.entity.player.EntityPlayerMP)
                                  Entity ID: 281
                                  Entity Name: Player177
                                  Entity's Exact location: 257,83, 64,00, 224,36
                                  Entity's Block location: World: (257,64,224), Chunk: (at 1,4,0 in 16,14; contains blocks 256,0,224 to 271,255,239), Region: (0,0; contains chunks 0,0 to 31,31, blocks 0,0,0 to 511,255,511)
                                  Entity's Momentum: 0,00, -0,08, 0,00
                                  Entity's Passengers: []
                                  Entity's Vehicle: ~~ERROR~~ NullPointerException: null
                                  Stacktrace:
                                  at net.minecraft.world.WorldServer.tickPlayers(WorldServer.java:680)
                                  at net.minecraft.world.World.updateEntities(World.java:1908)
                                  at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:651)
                                  
                                  -- Affected level --
                                  Details:
                                  Level name: New World
                                  All players: 1 total; [EntityPlayerMP['Player177'/281, l='New World', x=257,83, y=64,00, z=224,36]]
                                  Chunk stats: ServerChunkCache: 626 Drop: 0
                                  Level seed: 2481567272931423200
                                  Level generator: ID 00 - default, ver 1\. Features enabled: true
                                  Level generator options:
                                  Level spawn location: World: (252,64,236), Chunk: (at 12,4,12 in 15,14; contains blocks 240,0,224 to 255,255,239), Region: (0,0; contains chunks 0,0 to 31,31, blocks 0,0,0 to 511,255,511)
                                  Level time: 184333 game time, 824 day time
                                  Level dimension: 0
                                  Level storage version: 0x04ABD - Anvil
                                  Level weather: Rain time: 1 (now: false), thunder time: 1 (now: false)
                                  Level game mode: Game mode: creative (ID 1). Hardcore: false. Cheats: true
                                  Stacktrace:
                                  at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:795)
                                  at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:699)
                                  at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156)
                                  at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:548)
                                  at java.lang.Thread.run(Unknown Source)
                                  
                                  – System Details --
                                  Details:
                                  Minecraft Version: 1.11
                                  Operating System: Windows 10 (amd64) version 10.0
                                  Java Version: 1.8.0_131, Oracle Corporation
                                  Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
                                  Memory: 657540448 bytes (627 MB) / 1056309248 bytes (1007 MB) up to 1056309248 bytes (1007 MB)
                                  JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
                                  IntCache: cache: 1, tcache: 1, allocated: 12, tallocated: 94
                                  FML: MCP 9.35 Powered by Forge 13.19.1.2189 4 mods loaded, 4 mods active
                                  States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
                                  UCHIJAAAA mcp{9.19} [Minecraft Coder Pack] (minecraft.jar)
                                  UCHIJAAAA FML{8.0.99.99} [Forge Mod Loader] (forgeSrc-1.11-13.19.1.2189.jar)
                                  UCHIJAAAA forge{13.19.1.2189} [Minecraft Forge] (forgeSrc-1.11-13.19.1.2189.jar)
                                  UCHIJAAAA simpleores{0.1} [Simple ores] (bin)
                                  Loaded coremods (and transformers):
                                  GL info: ~~ERROR~~ RuntimeException: No OpenGL context found in the current thread.
                                  Profiler Position: N/A (disabled)
                                  Player Count: 1 / 8; [EntityPlayerMP['Player177'/281, l='New World', x=257,83, y=64,00, z=224,36]]
                                  Type: Integrated Server (map_client.txt)
                                  Is Modded: Definitely; Client brand changed to 'fml,forge'
                                  

                                  **Je suis un membre apprécié et joueur, j'ai déjà obtenu 1[ point de réputation./…

                                  1 réponse Dernière réponse Répondre Citer 0
                                  • BrokenSwing
                                    BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par robin4002

                                    Dans le constructeur de ton container tu as oublié

                                    this.tile = tile;
                                    
                                    1 réponse Dernière réponse Répondre Citer 1
                                    • A
                                      aypristyle dernière édition par

                                      Merci beaucoup tout fonctionne, juste une dernière petite question comment fait-on pour ajouter des recettes pour notre four ?
                                      J’ai essayé ceci que j’ai mis dans ma classe recettes sans succès ```java
                                      addRecipe(new Item(SimpleoresItems.CORN), new ItemStack(Items.SUGAR), new ItemStack(SimpleoresItems.CORN_SEED));

                                      **Je suis un membre apprécié et joueur, j'ai déjà obtenu 1[ point de réputation./…

                                      1 réponse Dernière réponse Répondre Citer 0
                                      • BrokenSwing
                                        BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                                        addRecipe(new ItemStack(SimpleoresItems.CORN), new ItemStack(Items.SUGAR), new ItemStack(SimpleoresItems.CORN_SEED));
                                        

                                        Tutoriel testé et corrigé. Il fonctionne et peut être déplacé.

                                        1 réponse Dernière réponse Répondre Citer 0
                                        • A
                                          aypristyle dernière édition par

                                          Bonjour je n’arrive pas a ajouter mes recettes j’ai essayer de les mettres sous les addRecipes dans ma classe Recettes mais cela ne fonctionne pas

                                          package fr.priya.simpleores.gui;
                                          
                                          import java.util.HashMap;
                                          import java.util.Iterator;
                                          import java.util.Map.Entry;
                                          
                                          import fr.priya.simpleores.items.SimpleoresItems;
                                          import net.minecraft.init.Items;
                                          import net.minecraft.item.Item;
                                          import net.minecraft.item.ItemStack;
                                          
                                          public class RecipesCustomFurnace {
                                          private static HashMap <itemstack[], itemstack="">recipes = new  HashMap<itemstack[], itemstack="">();
                                          //ajouter les recettes
                                          private static void addRecipe(Item ingredient1, Item ingredient2, Item resultat1) {
                                          addRecipe(new ItemStack(ingredient1), new ItemStack(ingredient2), new ItemStack(resultat1));
                                          //j'ai essayer de le mettre ici
                                          }
                                          
                                          private static void addRecipe(ItemStack ingredient1, ItemStack ingredient2, ItemStack resultat1) {
                                          recipes.put(new ItemStack[]{ingredient1, ingredient2}, resultat1);
                                          //et ici
                                          }
                                          
                                          //comparer les items stacks
                                          private static boolean areKeysEqual(ItemStack[] key1, ItemStack[] key2) {
                                          if(key1.length != key2.length) return false;
                                          
                                          for(int i = 0; i < key1.length; i++) {
                                          ItemStack s1 = key1*;
                                          ItemStack s2 = key2*;
                                          if(s1.isEmpty() && !s2.isEmpty()) return false;
                                          if(!s1.isEmpty() && s2.isEmpty()) return false;
                                          if(s1.getItem() != s2.getItem()) return false;
                                          if(s1.getItemDamage() != s2.getItemDamage()) return false;
                                          }
                                          return true;
                                          }
                                          //fonction pour trouver la recette
                                          public static ItemStack getRecipesResult(ItemStack[] ingredients)
                                          {
                                          Iterator<entry<itemstack[], itemstack="">> it = recipes.entrySet().iterator();
                                          while(it.hasNext()) {
                                          Entry <itemstack[], itemstack="">entry = it.next();
                                          if (areKeysEqual(entry.getKey(), ingredients))
                                          {
                                          return entry.getValue();
                                          }
                                          }
                                          return null;
                                          }
                                          
                                          }
                                          
                                          ```</itemstack[],></entry<itemstack[],></itemstack[],></itemstack[],>

                                          **Je suis un membre apprécié et joueur, j'ai déjà obtenu 1[ point de réputation./…

                                          1 réponse Dernière réponse Répondre Citer 0
                                          • BrokenSwing
                                            BrokenSwing Moddeurs confirmés Rédacteurs dernière édition par

                                            Il faut mettre les addRecipe dans un bloc static, regarde ici : https://www.minecraftforgefrance.fr/showthread.php?tid=4386#classerecette

                                            1 réponse Dernière réponse Répondre Citer 0
                                            • 1
                                            • 2
                                            • 3
                                            • 1 / 3
                                            • Premier message
                                              Dernier message
                                            Design by Woryk
                                            Contact / Mentions Légales

                                            MINECRAFT FORGE FRANCE © 2018

                                            Powered by NodeBB