24 août 2013, 21:47

Dans ce tutoriel nous allons voir comment créer des dalles avec 8 métadatas maximum. Pourquoi 8 métadatas et non 16 ? Car les demi dalles utilisent deux metadata, un pour le bloc placé vers le bas, et un pour le bloc placé vers le haut.

Dans la classe principale

Comme d’habitude, on commence par déclarer les blocs :

public static Block DoubleSlabTuto, SingleSlabTuto;

Ensuite on complète dans le preinit :

    DoubleSlabTuto = new BlockSlabTutorial(2003, true).setHardness(2.0F).setResistance(10.0F).setStepSound(Block.soundStoneFootstep).setUnlocalizedName("TutorialSlab");
    SingleSlabTuto = new BlockSlabTutorial(2004, false).setHardness(2.0F).setResistance(10.0F).setStepSound(Block.soundStoneFootstep).setUnlocalizedName("TutorialSlab");

Vous devrez avoir l’habitude de ce code, la seul différence est le constructeur, en effet il y a une boolean en plus, true pour la dalle double et false pour la dalle simple.

Ensuite nous allons enregistrer les blocs :

    GameRegistry.registerBlock(DoubleSlabTuto, ItemBlockTutorialSlab.class, "DoubleSlabTuto");
    GameRegistry.registerBlock(SingleSlabTuto, ItemBlockTutorialSlab.class, "SingleSlabTuto");

Même si vous ne voulez pas de metadata, il faut enregistrer un ItemBlock différent, il est nécessaire pour compléter une demi dalle.

Vous avez normalement 4 erreurs, deux sur BlockSlabTutorial et deux sur ItemBlockTutorialSlab, nous allons commencez par créer la classe du bloc.

La classe du bloc

Faite un extends BlockStep et ajoutez le constructeur :

    public static final String[] StepTypes = new String[] {"tuto", "tutometa1", "diamond", "gold", "iron"};

    public BlockSlabTutorial(int id, boolean isdouble)
    {
        super(id, isdouble);
        if(!this.isDoubleSlab)
        {
            this.setLightOpacity(0);
        }
    }

Le tableau de String StepTypes est tout les types de dalle, mettez un seul si vous le voulez pas de metadata, sinon vous pouvez monter jusqu’à 8.
La condition sert à mettre l’opacité de la demi dalle sur 0 (pour éviter des bug d’ombre).

Ensuite quelques méthodes :

    @SideOnly(Side.CLIENT)
    private static boolean isBlockSingleSlab(int id)
    {
        return id == ModTutoriel.SingleSlabTuto.blockID;
    }

Mettez ici votreclasseprincipale.ladallesimple.blockID.

    @SideOnly(Side.CLIENT)
    public int idPicked(World world, int x, int y, int z)
    {
        return isBlockSingleSlab(this.blockID) ? this.blockID : ModTutoriel.DoubleSlabTuto.blockID;
    }

Détermine l’id du pickupblock.

    public int idDropped(int metadata, Random rand, int fortune)
    {
        return ModTutoriel.SingleSlabTuto.blockID;
    }

Détermine l’id dropé. Pour la quantité dropée une méthode est déjà définie dans la classe BlockHalfSlab, et comme notre classe hérite celle-ci, nous avons pas besoin de remettre la méthode pour la quantité du drop.

    protected ItemStack createStackedBlock(int metadata)
    {
        return new ItemStack(ModTutoriel.SingleSlabTuto.blockID, 2, metadata & 7);
    }

Une méthode pour l’itemstack.

    public String getFullSlabName(int metadata)
    {
        if(metadata < 0 || metadata >= StepTypes.length)
        {
            metadata = 0;
        }

        return super.getUnlocalizedName() + "." + StepTypes[metadata];
    }

Une méthode pour le nom non localisé, il utilisera automatiquement les noms mis dans le tableau de String StepType. C’est la même que dans le tutoriel sur les metadatas.

    @SideOnly(Side.CLIENT)
    public void getSubBlocks(int id, CreativeTabs creativeTabs, List list)
    {
        if(id != ModTutoriel.DoubleSlabTuto.blockID)
        {
            for(int i = 0; i < StepTypes.length; i++)
            {
                list.add(new ItemStack(id, 1, i));
            }
        }
    }

Pour afficher dans l’inventaire créatif les différentes dalles en fonction du metadata.

Et pour finir, le getIcon :

    @SideOnly(Side.CLIENT)
    public Icon getIcon(int side, int metadata)
    {
        int k = metadata & 7;
        return k == 0 ? ModTutoriel.BlockTutorial.getBlockTextureFromSide(side) : k == 1 ? ModTutoriel.TutorialMetadata.getIcon(side, 0) : k == 2 ? Block.blockDiamond.getBlockTextureFromSide(side) : k == 3 ? Block.blockGold.getBlockTextureFromSide(side) : Block.blockIron.getBlockTextureFromSide(side);
    }

Il vous suffit à chaque fois de faire Block.nomduBloc.getBlockTextureFromSide(side) pour les blocs vanilla et ClassePrincipale.nomduBloc.getBlockTextureFromSide(side) pour les blocs de votre mod. Pour les blocs avec métadata, utilisez :
getIcon(side, <le metadata voulu>)

La classe de l’ItemBlock

Cette classe est plutôt longue, je vais donc vous donner le rendu final et expliquer après :

package tutoriel.common;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Icon;
import net.minecraft.world.World;

public class ItemBlockTutorialSlab extends ItemBlock
{
    private final boolean isFullBlock;
    private final Block theHalfSlab;
    private final Block doubleSlab;

    public ItemBlockTutorialSlab(int id)
    {
        super(id);
        this.theHalfSlab = ModTutoriel.SingleSlabTuto;
        this.doubleSlab = ModTutoriel.DoubleSlabTuto;
        if(id - 256 == ModTutoriel.DoubleSlabTuto.blockID)
        {
            this.isFullBlock = true;
        }
        else
        {
            this.isFullBlock = false;
        }
        this.setMaxDamage(0);
        this.setHasSubtypes(true);
    }

    @SideOnly(Side.CLIENT)
    public Icon getIconFromDamage(int metadata)
    {
        return Block.blocksList[this.itemID].getIcon(2, metadata);
    }

    public int getMetadata(int metadata)
    {
        return metadata;
    }

    public String getUnlocalizedName(ItemStack stack)
    {
        return ((BlockSlabTutorial)theHalfSlab).getFullSlabName(stack.getItemDamage());
    }

    public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float par8, float par9, float par10)
    {
        if(this.isFullBlock)
        {
            return super.onItemUse(stack, player, world, x, y, z, side, par8, par9, par10);
        }
        else if(stack.stackSize == 0)
        {
            return false;
        }
        else if(!player.canPlayerEdit(x, y, z, side, stack))
        {
            return false;
        }
        else
        {
            int i1 = world.getBlockId(x, y, z);
            int j1 = world.getBlockMetadata(x, y, z);
            int k1 = j1 & 7;
            boolean flag = (j1 & 8) != 0;

            if((side == 1 && !flag || side == 0 && flag) && i1 == this.theHalfSlab.blockID && k1 == stack.getItemDamage())
            {
                if(world.checkNoEntityCollision(this.doubleSlab.getCollisionBoundingBoxFromPool(world, x, y, z)) && world.setBlock(x, y, z, this.doubleSlab.blockID, k1, 3))
                {
                    world.playSoundEffect((double)((float)x + 0.5F), (double)((float)y + 0.5F), (double)((float)z + 0.5F), this.doubleSlab.stepSound.getPlaceSound(), (this.doubleSlab.stepSound.getVolume() + 1.0F) / 2.0F, this.doubleSlab.stepSound.getPitch() * 0.8F);
                    –stack.stackSize;
                }
                return true;
            }
            else
            {
                return this.placeDoubleSlabFromTop(stack, player, world, x, y, z, side) ? true : super.onItemUse(stack, player, world, x, y, z, side, par8, par9, par10);
            }
        }
    }

    @SideOnly(Side.CLIENT)
    public boolean canPlaceItemBlockOnSide(World world, int x, int y, int z, int side, EntityPlayer player, ItemStack stack)
    {
        int i1 = x;
        int j1 = y;
        int k1 = z;
        int id = world.getBlockId(x, y, z);
        int meta = world.getBlockMetadata(x, y, z);
        int j2 = meta & 7;
        boolean flag = (meta & 8) != 0;

        if((side == 1 && !flag || side == 0 && flag) && id == this.theHalfSlab.blockID && j2 == stack.getItemDamage())
        {
            return true;
        }
        else
        {
            if(side == 0)
            {
                --y;
            }

            if(side == 1)
            {
                ++y;
            }

            if(side == 2)
            {
                --z;
            }

            if(side == 3)
            {
                ++z;
            }

            if(side == 4)
            {
                --x;
            }

            if(side == 5)
            {
                ++x;
            }

            id = world.getBlockId(x, y, z);
            meta = world.getBlockMetadata(x, y, z);
            j2 = meta & 7;
            flag = (meta & 8) != 0;
            return id == this.theHalfSlab.blockID && j2 == stack.getItemDamage() ? true : super.canPlaceItemBlockOnSide(world, i1, j1, k1, side, player, stack);
        }
    }

    private boolean placeDoubleSlabFromTop(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side)
    {
        if(side == 0)
        {
            --y;
        }

        if(side == 1)
        {
            ++y;
        }

        if(side == 2)
        {
            --z;
        }

        if(side == 3)
        {
            ++z;
        }

        if(side == 4)
        {
            --x;
        }

        if(side == 5)
        {
            ++x;
        }

        int i1 = world.getBlockId(x, y, z);
        int j1 = world.getBlockMetadata(x, y, z);
        int k1 = j1 & 7;

        if(i1 == this.theHalfSlab.blockID && k1 == stack.getItemDamage())
        {
            if(world.checkNoEntityCollision(this.doubleSlab.getCollisionBoundingBoxFromPool(world, x, y, z)) && world.setBlock(x, y, z, this.doubleSlab.blockID, k1, 3))
            {
                world.playSoundEffect((double)((float)x + 0.5F), (double)((float)y + 0.5F), (double)((float)z + 0.5F), this.doubleSlab.stepSound.getPlaceSound(), (this.doubleSlab.stepSound.getVolume() + 1.0F) / 2.0F, this.doubleSlab.stepSound.getPitch() * 0.8F);
                --stack.stackSize;
            }

            return true;
        }
        else
        {
            return false;
        }
    }
}

this.theHalfSlab = ModTutoriel.SingleSlabTuto;
this.doubleSlab = ModTutoriel.DoubleSlabTuto;
Initialise les variables, remplacez ModTutoriel par votre classe principale.
if(id - 256 == ModTutoriel.DoubleSlabTuto.blockID) […]
Défini si le bloc est double ou simple. le - 256 est important car les id d’item sont augmentés de 256 automatiquement par minecraft (cf : le constructeur de Item.java)

Pour les deux autres méthodes, vous devez les connaître, si ce n’est pas le cas retournez voir le tutoriel sur les metadatas de blocs (lien en dessous).
getIconFromDamage est pour la texture de l’ItemBlock.
getMetadata vous devrez aussi la connaître, si c’est pas le cas retournez ici
getUnlocalizedName utilise la fonction getFullSlabName, que nous avons mit dans la classe du bloc dalle, n’oubliez pas de changer le cast dans le return par le nom de votre classe.
La fonction onItemUse sert à placer le bloc, si une dalle existe déjà, le bloc sera complété et deviendra une double dalle.
La fonction placeDoubleSlabFromTop sert à compléter une dalle déjà existante placée en haut.
Et la fonction canPlaceItemBlockOnSide sert à définir si le joueur peut poser le bloc. (À nouveau vérification avec la dalle si elle est simple ou double).

En se qui concerne les & 7 ce sont eux qui gèrent si la dalle est en bas ou en haut, ils ne sont pas là pour rien, et les & 8 servent pour vérifier si le metadata de l’item n’est pas trop haut, je rappelle que vous pouvez faire que jusqu’à 8 dalles en metadata :

:::
metadata 0 : exemple 1, posé en bas
metadata 1 : exemple 2, posé en bas
metadata 2 : exemple 3, posé en bas
metadata 3 : exemple 4, posé en bas
metadata 4 : exemple 5, posé en bas
metadata 5 : exemple 6, posé en bas
metadata 6 : exemple 7, posé en bas
metadata 7 : exemple 8, posé en bas
metadata 8 : exemple 1, posé en haut
metadata 9 : exemple 2, posé en haut
metadata 10 : exemple 3, posé en haut
metadata 11 : exemple 4, posé en haut
metadata 12 : exemple 5, posé en haut
metadata 13 : exemple 6, posé en haut
metadata 14 : exemple 7, posé en haut
metadata 15 : exemple 8, posé en haut
:::

Finissions + rendu final

N’oubliez pas de mettre à jour les fichiers de lang pour le nom en jeu. Le principale est le même que pour les blocs avec metadata, le tableau de String est utilisé pour le nom non localisé :
Par exemple pour moi :

tile.TutorialSlab.tuto.name=Tutorial Slab
tile.TutorialSlab.tutometa1.name=Metadata Tutorial 1 Slab
tile.TutorialSlab.diamond.name=Diamond Slab
tile.TutorialSlab.gold.name=Gold Slab
tile.TutorialSlab.iron.name=Iron Slab

Rendu final sur Github

Minecraft dalle moddé