[1.12] Créer un four
-
Sommaire
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 afficherQuand vous utilisez cette fonction, il faut associer la texture au textureManager de minecraft, il faut
donc mettre 1 fois au début de la fonctionthis.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 textePour 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 :
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
Crédits
Rédaction :
- BrokenSwing
Correction :
- BrokenSwing
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 -
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 ^^
-
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.
-
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 !
-
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().
-
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)
-
Merci à vous deux, je vais essayer !
EDIT = Tout marche à merveille -
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 !
-
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
- The method sendProgressBarUpdate(ContainerCustomFurnace, int, int) is undefined for the type
-
ContainerCustomFurnace hérite bien de Container ?
-
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; } }
-
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 ?
-
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à.
-
C’est bien pourquoi je précise que ce tutoriel est pour la 1.11.
-
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'
-
Dans le constructeur de ton container tu as oublié
this.tile = tile;
-
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)); -
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é.
-
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[],>
-
Il faut mettre les addRecipe dans un bloc static, regarde ici : https://www.minecraftforgefrance.fr/showthread.php?tid=4386#classerecette