Faire un nouveau four
-
Sommaire
- Le BlockFour ( Un bloc basique )
- Le TileEntityFour ( Les entités de bloc (TileEntity) )
- Le ContainerFour ( Pas de tutoriel sous la main )
- Le GuiFour ( Pas de tutoriel sous la main )
- Le GuiHandler ( Pas de tutoriel sous la main )
- Le SlotFour ( Pas de tutoriel sous la main )
- Le RecipesFour ( Pas de tutoriel sous la main )
- Les modifications sur la classe principale / les proxys
Le BlockFour
Notre BlockFour sera la base du block : Ce que vous posez, ce qui apparaît, et aussi, ce sur quoi vous ferez click droit pour l’ouvrir.
D’abord, on créer un block de base, mais qui extend de BlockContainer.
public class BlockFour extends BlockContainer { }
Passons au constructeur, qui est un peu différent ce que on trouve habituellement.
private final boolean isActive; // On défini une boolean isActive qui servira pour le rendu du block ( Allumé ou non ) public BlockFour(int par1, boolean par2) { super(par1, Material.rock); this.isActive = par2; }
Pourquoi un par2 ? Car on doit définir deux blocks avec des ID différents, mais de la même classe ( BlockFour ). Un actif, l’autre non.
Jusqu’ici, tout est simple !
Maintenant, on ajoute quelque chose de tout bête, mais pratique : Le block droppé !
Il faut dropper un block de four éteint et non allumé !
Le code est simple :public int idDropped(int par1, Random par2Random, int par3) { return MonMod.monFourEteint.blockID; }
Encore quelque chose de basique, vous l’aurez compris.
Maintenant, on va commencer un peu avec la texture selon l’orientation !
On doit définir 3 icones : Les côtés, la face avant, le dessus ( Qui est le même que le dessous )
On les définis ( Au dessus du constructeur ) :
@SideOnly(Side.CLIENT) private Icon furnaceIconTop; @SideOnly(Side.CLIENT) private Icon furnaceIconFront;
Note : La troisième est celle de base du block : On ne la définie pas ici.
Maintenant, on défini l’apparence selon les côtés !
@SideOnly(Side.CLIENT) public Icon getIcon(int side, int metadata) { return side == 1 ? this.furnaceIconTop : (side == 0 ? this.furnaceIconTop : (metadata == 2 && side == 2 ? this.furnaceIconFront : (metadata == 3 && side == 5 ? this.furnaceIconFront : (metadata == 0 && side == 3 ? this.furnaceIconFront : (metadata == 1 && side == 4 ? this.furnaceIconFront : this.blockIcon))))); }
Code assez long et complexe, mais vous le trouverez sur le tuto des blocks orientables
Ensuite, on enregistre les icones :
@SideOnly(Side.CLIENT) public void registerIcons(IconRegister par1IconRegister) { this.blockIcon = par1IconRegister.registerIcon("furnace_side"); this.furnaceIconFront = par1IconRegister.registerIcon(this.isActive ? "furnace_front_on" : "furnace_front_off"); this.furnaceIconTop = par1IconRegister.registerIcon("furnace_top"); }
furnace_side = le nom de votre texture de côté
furnace_front_on = Four allumé
furnace_front_off = Four éteint
furnace_top = Dessus / DessousMaintenant, on passe au choses sérieuse : L’interaction du block !
C’est ce qui fait que click droit sur le block fait ouvrir la GUIpublic boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) { if (par1World.isRemote) { eturn true; } else { TileEntityFour tileentityfour = (TileEntityFour)par1World.getBlockTileEntity(par2, par3, par4); if (tileentityfurnace != null) { par5EntityPlayer.openGui(MonMod.instance, 1, par1World, par2, par3, par4); } return true; } }
( Vous allez avoir des erreurs car la classe n’est pas encore crée )
Ensuite, on va s’occuper de l’état du four : Allumé, eteint
public static void updateFurnaceBlockState(boolean par0, World par1World, int par2, int par3, int par4) { int l = par1World.getBlockMetadata(par2, par3, par4); TileEntity tileentity = par1World.getBlockTileEntity(par2, par3, par4); keepFurnaceInventory = true; if (par0) { par1World.setBlock(par2, par3, par4, BlockCentralization.fourOn.blockID); } else { par1World.setBlock(par2, par3, par4, BlockCentralization.fourOff.blockID); } keepFurnaceInventory = false; par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 2); if (tileentity != null) { tileentity.validate(); par1World.setBlockTileEntity(par2, par3, par4, tileentity); } }
N’oubliez pas d’ajouter cette variable avant votre constructeur :
private static boolean keepFurnaceInventory = false;
Plusieurs méthodes d’un coup qui parlent d’elles mêmes :
@SideOnly(Side.CLIENT) /** * Tick au hasard qui fait spawn des flammes et de la fumée */ public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random) { if (this.isActive) { int l = par1World.getBlockMetadata(par2, par3, par4); float f = (float)par2 + 0.5F; float f1 = (float)par3 + 0.0F + par5Random.nextFloat() * 6.0F / 16.0F; float f2 = (float)par4 + 0.5F; float f3 = 0.52F; float f4 = par5Random.nextFloat() * 0.6F - 0.3F; if (l == 4) { par1World.spawnParticle("smoke", (double)(f - f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D); par1World.spawnParticle("flame", (double)(f - f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D); } else if (l == 5) { par1World.spawnParticle("smoke", (double)(f + f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D); par1World.spawnParticle("flame", (double)(f + f3), (double)f1, (double)(f2 + f4), 0.0D, 0.0D, 0.0D); } else if (l == 2) { par1World.spawnParticle("smoke", (double)(f + f4), (double)f1, (double)(f2 - f3), 0.0D, 0.0D, 0.0D); par1World.spawnParticle("flame", (double)(f + f4), (double)f1, (double)(f2 - f3), 0.0D, 0.0D, 0.0D); } else if (l == 3) { par1World.spawnParticle("smoke", (double)(f + f4), (double)f1, (double)(f2 + f3), 0.0D, 0.0D, 0.0D); par1World.spawnParticle("flame", (double)(f + f4), (double)f1, (double)(f2 + f3), 0.0D, 0.0D, 0.0D); } } } /** * Crée une entité Four quand le block est placé */ public TileEntity createNewTileEntity(World par1World) { return new TileEntityFour(); } /** * Appelé quand le block est placé */ public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase living, ItemStack stack) { int direction = MathHelper.floor_double((double)(living.rotationYaw * 4.0F / 360.0F) + 2.5D) & 3; world.setBlockMetadataWithNotify(x, y, z, direction, 2); }
Maintenant, quand on casse le block, pour récupérer le contenu.
public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6) { if (!keepFurnaceInventory) { TileEntityFour tileentityfurnace = (TileEntityFour)par1World.getBlockTileEntity(par2, par3, par4); if (tileentityfurnace != null) { for (int j1 = 0; j1 < tileentityfurnace.getSizeInventory(); ++j1) { ItemStack itemstack = tileentityfurnace.getStackInSlot(j1); if (itemstack != null) { float f = this.furnaceRand.nextFloat() * 0.8F + 0.1F; float f1 = this.furnaceRand.nextFloat() * 0.8F + 0.1F; float f2 = this.furnaceRand.nextFloat() * 0.8F + 0.1F; while (itemstack.stackSize > 0) { int k1 = this.furnaceRand.nextInt(21) + 10; if (k1 > itemstack.stackSize) { k1 = itemstack.stackSize; } itemstack.stackSize -= k1; EntityItem entityitem = new EntityItem(par1World, (double)((float)par2 + f), (double)((float)par3 + f1), (double)((float)par4 + f2), new ItemStack(itemstack.itemID, k1, itemstack.getItemDamage())); if (itemstack.hasTagCompound()) { entityitem.getEntityItem().setTagCompound((NBTTagCompound)itemstack.getTagCompound().copy()); } float f3 = 0.05F; entityitem.motionX = (double)((float)this.furnaceRand.nextGaussian() * f3); entityitem.motionY = (double)((float)this.furnaceRand.nextGaussian() * f3 + 0.2F); entityitem.motionZ = (double)((float)this.furnaceRand.nextGaussian() * f3); par1World.spawnEntityInWorld(entityitem); } } } par1World.func_96440_m(par2, par3, par4, par5); } } super.breakBlock(par1World, par2, par3, par4, par5, par6); }
Ajoutez ceci avant votre constructeur ( C’est pour quand les items sont éjectés, que leur trajectoire soit au hasard )
private final Random furnaceRand = new Random();
Les dernières méthodes sont par rapport au comparateur et au Pick Block ( Click milieu de souris ) :
public boolean hasComparatorInputOverride() { return true; } public int getComparatorInputOverride(World par1World, int par2, int par3, int par4, int par5) { return Container.calcRedstoneFromInventory((IInventory)par1World.getBlockTileEntity(par2, par3, par4)); } @SideOnly(Side.CLIENT) public int idPicked(World par1World, int par2, int par3, int par4) { return MonMod.blockFourEteint.blockID; }
Le block est normalement terminé !
Passons a la TileEntity !
Le TileEntityFour
Le TileEntityFour va être ce qui va relier toutes les parties entres elles plus où moins.
On commence par créer la classe, et a la faire extend de TileEntity.
Aussi, il faut implementer la classe ISidedInventoryOn a la base !
public class TileEntityFour extends TileEntity implements ISidedInventory { }
Note : La TileEntity ne dispose pas de constructeur ! Étrange n’est-ce pas ?
Donc, on ajoute la méthode pour obtenir la taille de l’inventaire du four !
Ajoutez la variable, et la méthode suivante :
private ItemStack[] furnaceItemStacks = new ItemStack[3]; public int getSizeInventory() { return this.furnaceItemStacks.length; }
WOAW ! DES CROCHETS SUR UN ITEMSTACK ? MAIS ! MAIS ! POURQUOI ?
Tout simplement car notre ItemStack est un tableau d’ItemStack.
En réalité, il sagit de 3 itemstacks :Le carburant
Le stack a faire cuire
Le résultatSi a la place de [3] je mettais [4], je pourrais faire un four a double-entrée, double-sortie ou double-carburant ! ( Note : J’ai déjà fait un four 2 entrée grace a ceci )
Bon, on se calme, et on reprend
Maintenant, nous ajoutons une méthode pour obtenir l’ItemStack dans le slot voulu :
public ItemStack getStackInSlot(int par1) { return this.furnaceItemStacks[par1]; }
Cette méthode est utilisée par les recettes et le système de carburant.
Exemple : Si l’ItemStack du slot 1 ( Item a faire cuire ) est un steak cru, le cuire
Où : Si l’ItemStack du slot 0 (Carburant ) est du charbon, le temps de carburant sera egal a xPoursuivons.
La méthode suivante sert a retirer l’item du joueur et le mettre dans le slot visé du four
public ItemStack decrStackSize(int par1, int par2) { if (this.furnaceItemStacks[par1] != null) { ItemStack itemstack; if (this.furnaceItemStacks[par1].stackSize <= par2) { itemstack = this.furnaceItemStacks[par1]; this.furnaceItemStacks[par1] = null; return itemstack; } else { itemstack = this.furnaceItemStacks[par1].splitStack(par2); if (this.furnaceItemStacks[par1].stackSize == 0) { this.furnaceItemStacks[par1] = null; } return itemstack; } } else { return null; } }
Maintenant, une méthode intéressante :
Celle qui fait en sorte que lorsqu’on ferme le four, les items restent dedans, et ne sont pas éjectés comme quand on ferme la table de craft !public ItemStack getStackInSlotOnClosing(int par1) { if (this.furnaceItemStacks[par1] != null) { ItemStack itemstack = this.furnaceItemStacks[par1]; this.furnaceItemStacks[par1] = null; return itemstack; } else { return null; } }
Cette méthode mets l’item stack donné dans le slot voulu :
public void setInventorySlotContents(int par1, ItemStack par2ItemStack) { this.furnaceItemStacks[par1] = par2ItemStack; if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit()) { par2ItemStack.stackSize = this.getInventoryStackLimit(); } }
Ensuite, des méthode qui vont faire en sorte que si je renomme mon four “SuperFourDeLaMort”, le nom sera affiché en haut!
private String field_94130_e; //Variable a mettre au debut de votre classe public String getInvName() { return this.isInvNameLocalized() ? this.field_94130_e : "container.furnace"; //Ici, c'est en rapport avec le fichier lang :o } public boolean isInvNameLocalized() { return this.field_94130_e != null && this.field_94130_e.length() > 0; } public void setGuiDisplayName(String par1Str) { this.field_94130_e = par1Str; }
Ensuite, les méthodes qui vont sauvegarder notre coffre et son contenu, ainsi que nom joli petit nom :
public void readFromNBT(NBTTagCompound par1NBTTagCompound) { super.readFromNBT(par1NBTTagCompound); NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items"); this.furnaceItemStacks = new ItemStack[this.getSizeInventory()]; for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i); byte b0 = nbttagcompound1.getByte("Slot"); if (b0 >= 0 && b0 < this.furnaceItemStacks.length) { this.furnaceItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } this.furnaceBurnTime = par1NBTTagCompound.getShort("BurnTime"); this.furnaceCookTime = par1NBTTagCompound.getShort("CookTime"); this.currentItemBurnTime = getItemBurnTime(this.furnaceItemStacks[1]); if (par1NBTTagCompound.hasKey("CustomName")) { this.field_94130_e = par1NBTTagCompound.getString("CustomName"); } } public void writeToNBT(NBTTagCompound par1NBTTagCompound) { super.writeToNBT(par1NBTTagCompound); par1NBTTagCompound.setShort("BurnTime", (short)this.furnaceBurnTime); par1NBTTagCompound.setShort("CookTime", (short)this.furnaceCookTime); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < this.furnaceItemStacks.length; ++i) { if (this.furnaceItemStacks* != null) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Slot", (byte)i); this.furnaceItemStacks*.writeToNBT(nbttagcompound1); nbttaglist.appendTag(nbttagcompound1); } } par1NBTTagCompound.setTag("Items", nbttaglist); if (this.isInvNameLocalized()) { par1NBTTagCompound.setString("CustomName", this.field_94130_e); } }
Vous allez avoir des erreures.
Ajoutez ces variables:private static final int[] slots_top = new int[] {0}; private static final int[] slots_bottom = new int[] {2, 1}; private static final int[] slots_sides = new int[] {1}; public int furnaceBurnTime; public int furnaceCookTime; public int currentItemBurnTime;
Maintenant, une méthode a ajouter qui parle d’elle même :
public int getInventoryStackLimit() { return 64; }
La, on ajoute les méthodes qui vont faire le calcul pour le carburant, et le temps de cuisson afin que notre GUI puisse afficher la bonne progression :
@SideOnly(Side.CLIENT) public int getCookProgressScaled(int par1) { return this.furnaceCookTime * par1 / 200; } @SideOnly(Side.CLIENT) public int getBurnTimeRemainingScaled(int par1) { if (this.currentItemBurnTime == 0) { this.currentItemBurnTime = 200; } return this.furnaceBurnTime * par1 / this.currentItemBurnTime; }
Ensuite, la méthode qui va retourner si le four est actif ou non :
public boolean isBurning() { return this.furnaceBurnTime > 0; }
Une boolean qui retourne autre chose que false ou true ?!
Et oui, cette boolean retourneras true si la “condition” est completée :
furnaceBurnTime supérieur à 0C’est tout de suite plus logique !
Maintenant, la grosse méthode qui update l’entitée.
Celle ci étant assez longue, je vous laisse prendre le temps de bien l’examiner et de comprendre son fonctionnement.public void updateEntity() { boolean flag = this.furnaceBurnTime > 0; boolean flag1 = false; if (this.furnaceBurnTime > 0) { –this.furnaceBurnTime; } if (!this.worldObj.isRemote) { if (this.furnaceBurnTime == 0 && this.canSmelt()) { this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]); if (this.furnaceBurnTime > 0) { flag1 = true; if (this.furnaceItemStacks[1] != null) { --this.furnaceItemStacks[1].stackSize; if (this.furnaceItemStacks[1].stackSize == 0) { this.furnaceItemStacks[1] = this.furnaceItemStacks[1].getItem().getContainerItemStack(furnaceItemStacks[1]); } } } } if (this.isBurning() && this.canSmelt()) { ++this.furnaceCookTime; if (this.furnaceCookTime == 200) { this.furnaceCookTime = 0; this.smeltItem(); flag1 = true; } } else { this.furnaceCookTime = 0; } if (flag != this.furnaceBurnTime > 0) { flag1 = true; BlockFour.updateFurnaceBlockState(this.furnaceBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord); } } if (flag1) { this.onInventoryChanged(); } }
Au tour de la méthode canSmelt() appelée par la précédente :
private boolean canSmelt() { if (this.furnaceItemStacks[0] == null) { return false; } else { ItemStack itemstack = RecipesFour.smelting().getSmeltingResult(this.furnaceItemStacks[0]); if (itemstack == null) return false; if (this.furnaceItemStacks[2] == null) return true; if (!this.furnaceItemStacks[2].isItemEqual(itemstack)) return false; int result = furnaceItemStacks[2].stackSize + itemstack.stackSize; return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize()); } }
La classe RecipesFour est demandée : Elle sert a savoir quelles sont les recettes valables pour cuire des items.
Ensuite, la méthode qui va cuire les items :
public void smeltItem() { if (this.canSmelt()) { ItemStack itemstack = RecipesFour.smelting().getSmeltingResult(this.furnaceItemStacks[0]); if (this.furnaceItemStacks[2] == null) { this.furnaceItemStacks[2] = itemstack.copy(); } else if (this.furnaceItemStacks[2].isItemEqual(itemstack)) { furnaceItemStacks[2].stackSize += itemstack.stackSize; } –this.furnaceItemStacks[0].stackSize; if (this.furnaceItemStacks[0].stackSize <= 0) { this.furnaceItemStacks[0] = null; } } }
On progresse !
Ensuite, le plus important : Le carburant !
Cette méthode la sert a obtenir le temps de cuisson par carburant ( Modifier la a votre guise )public static int getItemBurnTime(ItemStack par0ItemStack) { if (par0ItemStack == null) { return 0; } else { int i = par0ItemStack.getItem().itemID; Item item = par0ItemStack.getItem(); if (par0ItemStack.getItem() instanceof ItemBlock && Block.blocksList* != null) { Block block = Block.blocksList*; if (block == Block.woodSingleSlab) { return 150; } if (block.blockMaterial == Material.wood) { return 300; } if (block == Block.field_111034_cE) { return 16000; } } if (item instanceof ItemTool && ((ItemTool) item).getToolMaterialName().equals("WOOD")) return 200; if (item instanceof ItemSword && ((ItemSword) item).getToolMaterialName().equals("WOOD")) return 200; if (item instanceof ItemHoe && ((ItemHoe) item).getMaterialName().equals("WOOD")) return 200; if (i == Item.stick.itemID) return 100; if (i == Item.coal.itemID) return 1600; if (i == Item.bucketLava.itemID) return 20000; if (i == Block.sapling.blockID) return 100; if (i == Item.blazeRod.itemID) return 2400; return GameRegistry.getFuelValue(par0ItemStack); } }
Cette méthode sert simplement a savoir si l’item dans le slot 0 est un carburant ou non.
public static boolean isItemFuel(ItemStack par0ItemStack) { return getItemBurnTime(par0ItemStack) > 0; }
La méthode pour savoir si le joueur peut utiliser le four :
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer) { return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D; }
Maintenant, les dernières méthodes de la classe !
Je vous laisse voir ce qu’elles font par vous mêmes, car elle ne sont pas compliquées :public void openChest() {} public void closeChest() {} public boolean isItemValidForSlot(int par1, ItemStack par2ItemStack) { return par1 == 2 ? false : (par1 == 1 ? isItemFuel(par2ItemStack) : true); } public int[] getAccessibleSlotsFromSide(int par1) { return par1 == 0 ? slots_bottom : (par1 == 1 ? slots_top : slots_sides); } public boolean canInsertItem(int par1, ItemStack par2ItemStack, int par3) { return this.isItemValidForSlot(par1, par2ItemStack); } public boolean canExtractItem(int par1, ItemStack par2ItemStack, int par3) { return par3 != 0 || par1 != 1 || par2ItemStack.itemID == Item.bucketEmpty.itemID; }
La TileEntity est finie !
Passons au Container, qui va être moins long, mais tout aussi important !
Le ContainerFour
Le ContainerFour est la classe qui va contenir les items, et qui va gérer les slots.
Commençons :
D’abord, création de la classe, et extends de Container.
public class ContainerFour extends Container { }
Ensuite, le constructeur, qui est simple.
Paramètres :
Inventaire du player, TileEntity du fourpublic ContainerFour(InventoryPlayer par1InventoryPlayer, TileEntityFour par2TileEntityFour) { this.furnace = par2TileEntityFour; this.addSlotToContainer(new Slot(par2TileEntityFour, 0, 56, 17)); this.addSlotToContainer(new Slot(par2TileEntityFour, 1, 56, 53)); this.addSlotToContainer(new SlotFour(par1InventoryPlayer.player, par2TileEntityFour, 2, 116, 35)); int i; for (i = 0; i < 3; ++i) { for (int j = 0; j < 9; ++j) { this.addSlotToContainer(new Slot(par1InventoryPlayer, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); } } for (i = 0; i < 9; ++i) { this.addSlotToContainer(new Slot(par1InventoryPlayer, i, 8 + i * 18, 142)); } }
Ajoutez aussi ces variables :
private TileEntityFour furnace; private int lastCookTime; private int lastBurnTime; private int lastItemBurnTime;
Il reste une erreur : Il faut le SlotFour !
Ne vous inquiétez pas, il arrive !
Mais finissons d’abord le reste !Maintenant, passons a la méthode avec le nom le plus sympa du monde :
public void addCraftingToCrafters(ICrafting par1ICrafting) { super.addCraftingToCrafters(par1ICrafting); par1ICrafting.sendProgressBarUpdate(this, 0, this.furnace.furnaceCookTime); par1ICrafting.sendProgressBarUpdate(this, 1, this.furnace.furnaceBurnTime); par1ICrafting.sendProgressBarUpdate(this, 2, this.furnace.currentItemBurnTime); }
Celle ci modifie l’état des temps de cuissons, et la méthode suivante les enregistres et les envoie :
public void detectAndSendChanges() { super.detectAndSendChanges(); for (int i = 0; i < this.crafters.size(); ++i) { ICrafting icrafting = (ICrafting)this.crafters.get(i); if (this.lastCookTime != this.furnace.furnaceCookTime) { icrafting.sendProgressBarUpdate(this, 0, this.furnace.furnaceCookTime); } if (this.lastBurnTime != this.furnace.furnaceBurnTime) { icrafting.sendProgressBarUpdate(this, 1, this.furnace.furnaceBurnTime); } if (this.lastItemBurnTime != this.furnace.currentItemBurnTime) { icrafting.sendProgressBarUpdate(this, 2, this.furnace.currentItemBurnTime); } } this.lastCookTime = this.furnace.furnaceCookTime; this.lastBurnTime = this.furnace.furnaceBurnTime; this.lastItemBurnTime = this.furnace.currentItemBurnTime; }
La méthode qui suit est simple : Elle mets a jour les temps de progression du côté client uniquement :
@SideOnly(Side.CLIENT) public void updateProgressBar(int par1, int par2) { if (par1 == 0) { this.furnace.furnaceCookTime = par2; } if (par1 == 1) { this.furnace.furnaceBurnTime = par2; } if (par1 == 2) { t his.furnace.currentItemBurnTime = par2; } }
Cette méthode vérifie que le joueur peut intéragir avec le container :
public boolean canInteractWith(EntityPlayer par1EntityPlayer) { return this.furnace.isUseableByPlayer(par1EntityPlayer); }
La méthode suivante permet d’avoir un shift+click fonctionel, et de récuperer nos items d’un coup :
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int par2) { ItemStack itemstack = null; Slot slot = (Slot)this.inventorySlots.get(par2); if (slot != null && slot.getHasStack()) { ItemStack itemstack1 = slot.getStack(); itemstack = itemstack1.copy(); if (par2 == 2) { if (!this.mergeItemStack(itemstack1, 3, 39, true)) { return null; } slot.onSlotChange(itemstack1, itemstack); } else if (par2 != 1 && par2 != 0) { if (RecipesFour.smelting().getSmeltingResult(itemstack1) != null) { if (!this.mergeItemStack(itemstack1, 0, 1, false)) { return null; } } else if (TileEntityFour.isItemFuel(itemstack1)) { if (!this.mergeItemStack(itemstack1, 1, 2, false)) { return null; } } else if (par2 >= 3 && par2 < 30) { if (!this.mergeItemStack(itemstack1, 30, 39, false)) { return null; } } else if (par2 >= 30 && par2 < 39 && !this.mergeItemStack(itemstack1, 3, 30, false)) { return null; } } else if (!this.mergeItemStack(itemstack1, 3, 39, false)) { return null; } if (itemstack1.stackSize == 0) { slot.putStack((ItemStack)null); } else { slot.onSlotChanged(); } if (itemstack1.stackSize == itemstack.stackSize) { return null; } slot.onPickupFromSlot(par1EntityPlayer, itemstack1); } return itemstack; }
Et c’est tout pour notre cher container !
Le reste va aller très vite, vous n’allez pas le voir passer !
Donc c’est parti pour le GuiFour !
Le GuiFour
La classe du GUI est très courte, et très simple !
Créez la classe, et faites la extends de GuiContainer.
Ajoutez aussi un SideOnly(Side.CLIENT)
@SideOnly(Side.CLIENT) public class GuiFour extends GuiContainer { }
Maintenant, on défini deux variables :
La texture, et l’inventaire du four.private static final ResourceLocation field_110410_t = new ResourceLocation("MOD_ID:textures/gui/container/guiFour.png"); private TileEntityFour furnaceInventory;
Maintenant, on fait le constructeur qui a pour paramètres l’inventaire du joueur, et le TileEntityFour :
public GuiFour(InventoryPlayer par1InventoryPlayer, TileEntityFour par2TileEntityFour) { super(new ContainerFurnace(par1InventoryPlayer, par2TileEntityFour)); this.furnaceInventory = par2TileEntityFour; }
Ensuite, on dessine la gui.
Je donne les méthodes comme ça car elles n’ont rien de particulier :
protected void drawGuiContainerForegroundLayer(int par1, int par2) { String s = this.furnaceInventory.isInvNameLocalized() ? this.furnaceInventory.getInvName() : I18n.getString(this.furnaceInventory.getInvName()); this.fontRenderer.drawString(s, this.xSize / 2 - this.fontRenderer.getStringWidth(s) / 2, 6, 4210752); this.fontRenderer.drawString(I18n.getString("container.inventory"), 8, this.ySize - 96 + 2, 4210752); } protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3) { GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(field_110410_t); int k = (this.width - this.xSize) / 2; int l = (this.height - this.ySize) / 2; this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize); int i1; if (this.furnaceInventory.isBurning()) { i1 = this.furnaceInventory.getBurnTimeRemainingScaled(12); this.drawTexturedModalRect(k + 56, l + 36 + 12 - i1, 176, 12 - i1, 14, i1 + 2); } i1 = this.furnaceInventory.getCookProgressScaled(24); this.drawTexturedModalRect(k + 79, l + 34, 176, 14, i1 + 1, 16); }
Et la GUI est finie ! Et, oui, c’est rapide !
Passons au GuiHandler, qui va gérer notre gui !
Le GuiHandler
Le GuiHandler va attribuer notre gui a notre TileEntity et note Container.
Je donne la classe entière, car faire une description serait inutile :
public class GuiHandler implements IGuiHandler{ @Override public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { TileEntity tile_entity = world.getBlockTileEntity(x, y, z); if(tile_entity instanceof TileEntityFour){ return new ContainerFour(player.inventory, (TileEntityFour) tile_entity); } return null; } @Override public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { TileEntity tile_entity = world.getBlockTileEntity(x, y, z); if(tile_entity instanceof TileEntityFour){ return new GuiFour(player.inventory, (TileEntityFour) tile_entity); } return null; } }
Maintenant, on arrive sur la fin !
Le Slot, les Recipes et l’enregistrement du block !
Le SlotFour
Le SlotFour est la classe de résultat de votre four.
Celle ci bloque tout item voulant y entrer par l’intermédiaire de la souris.Elle attribue aussi les achievements lié au four au joueurs.
Tout d’abord, faites la classe, et extendez la de Slot.
public class SlotFurnace extends Slot { }
Ensuite, deux variables :
private EntityPlayer thePlayer; private int field_75228_b;
Une fois celles-ci crée, faites votre constructeur :
public SlotFour(EntityPlayer par1EntityPlayer, IInventory par2IInventory, int par3, int par4, int par5) { super(par2IInventory, par3, par4, par5); this.thePlayer = par1EntityPlayer; }
Maintenant, nous allons interdire l’arrivée d’item dans ce slot :
public boolean isItemValid(ItemStack par1ItemStack) { return false; }
Vous avez la base.
Ajoutons plus de fonctions.
public ItemStack decrStackSize(int par1) { if (this.getHasStack()) { this.field_75228_b += Math.min(par1, this.getStack().stackSize); } return super.decrStackSize(par1); }
Voir plus haut pour cette méthode.
Ces méthodes déclenchent l’evennement onCrafting de l’item cuit :
public void onPickupFromSlot(EntityPlayer par1EntityPlayer, ItemStack par2ItemStack) { this.onCrafting(par2ItemStack); super.onPickupFromSlot(par1EntityPlayer, par2ItemStack); } protected void onCrafting(ItemStack par1ItemStack, int par2) { this.field_75228_b += par2; this.onCrafting(par1ItemStack); } protected void onCrafting(ItemStack par1ItemStack) { par1ItemStack.onCrafting(this.thePlayer.worldObj, this.thePlayer, this.field_75228_b); if (!this.thePlayer.worldObj.isRemote) { int i = this.field_75228_b; float f = FurnaceRecipes.smelting().getExperience(par1ItemStack); int j; if (f == 0.0F) { i = 0; } else if (f < 1.0F) { j = MathHelper.floor_float((float)i * f); if (j < MathHelper.ceiling_float_int((float)i * f) && (float)Math.random() < (float)i * f - (float)j) { ++j; } i = j; } while (i > 0) { j = EntityXPOrb.getXPSplit(i); i -= j; this.thePlayer.worldObj.spawnEntityInWorld(new EntityXPOrb(this.thePlayer.worldObj, this.thePlayer.posX, this.thePlayer.posY + 0.5D, this.thePlayer.posZ + 0.5D, j)); } } this.field_75228_b = 0; GameRegistry.onItemSmelted(thePlayer, par1ItemStack); if (par1ItemStack.itemID == Item.ingotIron.itemID) { this.thePlayer.addStat(AchievementList.acquireIron, 1); } if (par1ItemStack.itemID == Item.fishCooked.itemID) { t his.thePlayer.addStat(AchievementList.cookFish, 1); } }
Et c’est tout pour le slot !
On a presque fini !
Plus que les recettes, et on enregistre tout ça !
Le RecipesFour
Cette classe est la plus simple : Elle enregistre les recettes du four.
Je vais donner celle du four de base de Minecraft, a vous d’y modifier avec ce que vous voulez
public class RecipesFour { private static final RecipesFour smeltingBase = new RecipesFour(); /** The list of smelting results. */ private Map smeltingList = new HashMap(); private Map experienceList = new HashMap(); private HashMap<List<Integer>, ItemStack> metaSmeltingList = new HashMap<List<Integer>, ItemStack>(); private HashMap<List<Integer>, Float> metaExperience = new HashMap<List<Integer>, Float>(); /** * Used to call methods addSmelting and getSmeltingResult. */ public static final RecipesFour smelting() { return smeltingBase; } private RecipesFour() { this.addSmelting(Block.oreIron.blockID, new ItemStack(Item.ingotIron), 0.7F); this.addSmelting(Block.oreGold.blockID, new ItemStack(Item.ingotGold), 1.0F); this.addSmelting(Block.oreDiamond.blockID, new ItemStack(Item.diamond), 1.0F); this.addSmelting(Block.sand.blockID, new ItemStack(Block.glass), 0.1F); this.addSmelting(Item.porkRaw.itemID, new ItemStack(Item.porkCooked), 0.35F); this.addSmelting(Item.beefRaw.itemID, new ItemStack(Item.beefCooked), 0.35F); this.addSmelting(Item.chickenRaw.itemID, new ItemStack(Item.chickenCooked), 0.35F); this.addSmelting(Item.fishRaw.itemID, new ItemStack(Item.fishCooked), 0.35F); this.addSmelting(Block.cobblestone.blockID, new ItemStack(Block.stone), 0.1F); this.addSmelting(Item.clay.itemID, new ItemStack(Item.brick), 0.3F); this.addSmelting(Block.blockClay.blockID, new ItemStack(Block.field_111032_cD), 0.35F); this.addSmelting(Block.cactus.blockID, new ItemStack(Item.dyePowder, 1, 2), 0.2F); this.addSmelting(Block.wood.blockID, new ItemStack(Item.coal, 1, 1), 0.15F); this.addSmelting(Block.oreEmerald.blockID, new ItemStack(Item.emerald), 1.0F); this.addSmelting(Item.potato.itemID, new ItemStack(Item.bakedPotato), 0.35F); this.addSmelting(Block.netherrack.blockID, new ItemStack(Item.netherrackBrick), 0.1F); this.addSmelting(Block.oreCoal.blockID, new ItemStack(Item.coal), 0.1F); this.addSmelting(Block.oreRedstone.blockID, new ItemStack(Item.redstone), 0.7F); this.addSmelting(Block.oreLapis.blockID, new ItemStack(Item.dyePowder, 1, 4), 0.2F); this.addSmelting(Block.oreNetherQuartz.blockID, new ItemStack(Item.netherQuartz), 0.2F); } /** * Adds a smelting recipe. */ public void addSmelting(int par1, ItemStack par2ItemStack, float par3) { this.smeltingList.put(Integer.valueOf(par1), par2ItemStack); this.experienceList.put(Integer.valueOf(par2ItemStack.itemID), Float.valueOf(par3)); } /** * Returns the smelting result of an item. Deprecated in favor of a metadata * sensitive version */ @Deprecated public ItemStack getSmeltingResult(int par1) { return (ItemStack) this.smeltingList.get(Integer.valueOf(par1)); } public Map getSmeltingList() { return this.smeltingList; } @Deprecated // In favor of ItemStack sensitive version public float getExperience(int par1) { return this.experienceList.containsKey(Integer.valueOf(par1)) ? ((Float) this.experienceList.get(Integer.valueOf(par1))).floatValue() : 0.0F; } /** * A metadata sensitive version of adding a furnace recipe. */ public void addSmelting(int itemID, int metadata, ItemStack itemstack, float experience) { metaSmeltingList.put(Arrays.asList(itemID, metadata), itemstack); metaExperience.put(Arrays.asList(itemstack.itemID, itemstack.getItemDamage()), experience); } /** * Used to get the resulting ItemStack form a source ItemStack * * @param item The Source ItemStack * @return The result ItemStack */ public ItemStack getSmeltingResult(ItemStack item) { if (item == null) { return null; } ItemStack ret = (ItemStack) metaSmeltingList.get(Arrays.asList(item.itemID, item.getItemDamage())); if (ret != null) { return ret; } return (ItemStack) smeltingList.get(Integer.valueOf(item.itemID)); } /** * Grabs the amount of base experience for this item to give when pulled from * the furnace slot. */ public float getExperience(ItemStack item) { if (item == null || item.getItem() == null) { return 0; } float ret = item.getItem().getSmeltingExperience(item); if (ret < 0 && metaExperience.containsKey(Arrays.asList(item.itemID, item.getItemDamage()))) { ret = metaExperience.get(Arrays.asList(item.itemID, item.getItemDamage())); } if (ret < 0 && experienceList.containsKey(item.itemID)) { ret = ((Float) experienceList.get(item.itemID)).floatValue(); } return (ret < 0 ? 0 : ret); } public Map<list<integer>, ItemStack> getMetaSmeltingList() { return metaSmeltingList; } }
Maintenant, on fini tout ça en enregistrant le block, la TileEntity et le GuiHandler !
Les modifications sur la classe principale / les proxys
C’est parti !
D’abord : le Block !
public static Block fourOn, fourOff; fourOn = new BlockFour(MonID, true).func_111022_d("monmod:fouron").setUnlocalizedName("Mon four"); fourOff = new BlockFour(MonID2, false).func_111022_d("modmod:fouroff").setUnlocalizedName("Mon four"); GameRegistry.registerBlock(fourOn, "fourOn"); GameRegistry.registerBlock(fourOff, "fourOff");
Il faut un ID différent pour fourOn et fourOff /!
On donne deux blocks : Le four allumé, et le four éteint. Principalement pour l’apparence en réalité.
Ensuite, on va définir notre TileEntity :
GameRegistry.registerTileEntity(TileEntityFour.class, "TileEntityFour");
Maintenant, on enregistre le GuiHandler, et c’est fini !
Dans les variables :
protected static final GuiHandler guihandler = new GuiHandler();
Dans la méthode init :
NetworkRegistry.instance().registerGuiHandler(this, guihandler);
C’est fini !
Rendez vous pour un prochain tutoriel sur les items, et une fonction très recherchée
-
Bon tuto Gugu et bonne continuation pour la suite
-
Est ce que c’est pareil en 1.5.2 ou s’est différent?
-
Merci gugu42 :), mais comme dit superloup10, va t-il être fonctionnelle en 1.5.2 ? x), car c’est en 1.5.2 que je vais en avoir besoin x)
-
Je ne sais pas dutout.
J’ai juste copié les classes du four et les ai adaptée a ce que je voulais ( Le four dont j’avais besoin était un four 2 entrée une sortie et non pas 1 entrée 1 sortie
-
Ok XD, je me contenterais de sa alors :(.
Autan pour moi j’ai mal lue ton le message x).
-
Tutoriel terminé.
-
Putain
-
YES ! Merci Gugu42 :), Il est vachement long ^^, mais je vais le suivre a la lettre ^^.
-
tuto génial et util
-
Derien ^^
Et oui, il est LONG !
Mais bon, une fois que c’est fait, c’est fait ( OBVIOUS ).
Mon prochain tutoriel sera bien moins long, mais sera unique mondialement
-
Par contre il serai sympa de donnais qu’elle import prendre quand il en propose plusieurs … s’est l’une des seule chose que je reproche .
-
Tu bloque sur quel importation ? (les deux choix possible)
-
Si c’est a propos des list faut prendre celui de java.util
-
bain, je dit sa, c’est surtout pour se qui connaisse 0, car des fois j’ai des doute aussi ^^', mais le plus souvent je prend celui qui me semble le plus logique.
Sinon je demande ^^. -
Si tu bloque, donne les choix, je te dirais
-
Ok :).
Ou au pire, donne les import a mètre dans chaque class XD.
Bon je veux pas être méchant mais il manque pas mal de truc dans tes explication :(, car j’aie plain d’erreur, et je ne sais pas du tout comment les corrigé :(.
Comme par exemple, je le mais ou EXACTEMENT le " GameRegistry.registerTileEntity(TileEntityFour.class, “TileEntityFour”);" ?
Ou sinon des “field” qui son souligné en rouge et je sais pas quoi en faire :(;
Ou même a certain endroit sa te propose de créer des class pour corrigé des erreur :(.
Pareille pour le “SlotFour”, j’en fais quoi ? T_T
Apparemment le “TileEntitySmelter” n’existe pas, insi que le “EntityLivingBase”.
-
Alors, les trucs avec Smelter c’est de ma faute ( J’ai édité le tuto ), mais le reste, je vois pas pourquoi tu as des erreurs.
-
Pour 1.5.2, ça devrait pouvoir fonctionner, suffit de changer les trucs qui faut pour les textures
-
ok merci x)