Slabs are pretty neat, right?
They're half-blocks, stack on top of each other, and often make a nice aesthetic touch to your base or home.
Wouldn't you like to make a custom slab?
I tried for about a day and a half to get a custom slab to work. After several hours of debugging, googling, repeat client launches, and some help from some very special people here, I managed to get a custom slab to work no problem (well, -almost-). Now, I want to share my knowledge and code.
Requirements, readme & thanks
Requirements Java Development Kit (depending on your OS and 64-bit/32-bit) Eclipse (depending on your OS, and 64-bit/32-bit) Forge (get the latest version)
A FUNCTIONING BRAIN
Readme!
Firstly, setup a config in your mod if you haven't already. I am using MrrGingerNinja's config, and the tutorial for it can be found below.
Backup your mod's .java files. BACKUP THEM. I am not responsible if my tutorial breaks your mod.
Read the commenting for each class carefully. Don't forgot to add and fix any necessary imports. If you're getting errors (either in Eclipse or during runtime), doublecheck the steps or compare your code to mine. Lastly, try solving the errors on your own by trial and error, googling solutions and searching the MC forums.
If you get a console error that says something like "@ <slab-ID> item slot already occupied", ignore it. It's not gamebreaking at all, but I can't figure out how to solve right now. ProgrammarHero doesn't know how to solve it either, though.
If nothing else, post what section and step you have a problem with, and use spoilers and screenshots, please.
Simple class examples (for main mod class and config setup)
Main mod class
package example;
import java.util.logging.Level;
import net.minecraftforge.common.MinecraftForge;
import example.blocks.Blocks;
import example.items.Items;
import example.lib.LogHelper;
import example.lib.ModInfo;
import example.lib.Recipes;
import example.lib.config.ConfigHandler;
import example.proxies.*;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
@Mod(modid = ModInfo.MODID, name = ModInfo.NAME, version = ModInfo.VERSION)
@NetworkMod( channels = {ModInfo.CHANNEL}, clientSideRequired = true, serverSideRequired = false)
public class ExampleMain
{
// The mod's ID, this basically tells Forge: What mod is this? It's required for a mod to run properly.
public static final String modid = ModInfo.MODID;
// The instance of your mod that Forge uses
@Instance(value = ModInfo.MODID)
public static ExampleMain instance;
// Says where the client and server proxy code is loaded, this is important for rendering sounds and model textures!
@SidedProxy( clientSide = ModInfo.PROXY_LOCATION + ".ClientProxy", serverSide = ModInfo.PROXY_LOCATION + ".CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void load(FMLPreInitializationEvent event)
{
//Loads the config to get block/item values and initializes the loghelper, blocks, items, etc
ConfigHandler.init(event.getSuggestedConfigurationFile());
LogHelper.init();
LogHelper.log(Level.INFO, "Loading LogHelper...");
//Proxy Renderers & sounds
LogHelper.log(Level.INFO, "Preparing renderers and sounds");
proxy.initRenderers();
proxy.initSounds();
LogHelper.log(Level.INFO, "Renderers and sounds loaded");
// Blocks
LogHelper.log(Level.INFO, "Preparing blocks");
Blocks.init();
Blocks.addNames();
LogHelper.log(Level.INFO, "Blocks loaded");
// Items
LogHelper.log(Level.INFO, "Preparing items");
Items.init();
Items.addNames();
LogHelper.log(Level.INFO, "Items loaded");
// Recipes (don't forget to add the class file and init to said class)
// If you do, add these to it:
// import net.minecraft.item.ItemStack;
// import cpw.mods.fml.common.registry.GameRegistry;
// But feel free to leave it blank until you actually want to add recipes!
LogHelper.log(Level.INFO, "Preparing recipes");
Recipes.init();
LogHelper.log(Level.INFO, "Recipes loaded");
}
@EventHandler
public void load(FMLInitializationEvent event)
{
}
@EventHandler
public void load(FMLPostInitializationEvent event)
{
}
}
Blocks class
package example.blocks;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import example.lib.config.IDs;
import example.lib.config.Names;
public class Blocks {
//Put your block public statics here
public static Block exampleBlock;
public static void init() {
//Put the ID, nameBlock = new NameBlock, GameRegistry and setBlcokHarvestLevel here
exampleBlock = new ExampleBlock(IDs.exampleBlock_actual);
GameRegistry.registerBlock(exampleBlock, Names.exampleBlock_name);
MinecraftForge.setBlockHarvestLevel(exampleBlock, "pickaxe", 1);
// See the line of code above? For the three values, you have to use your block's public static name, the tool name (must be in lowercase and in quotes, so you have "pickaxe", "axe", and "shovel" as valid values, and the last are tool tier. Zero is wood/gold, one is stone, two is iron, and three is diamond.
}
public static void addNames() {
//This adds the name of the block in game.
LanguageRegistry.addName(exampleBlock, Names.exampleBlock_name);
}
}
Items class
package example.items;
import net.minecraft.item.Item;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.registry.LanguageRegistry;
import example.lib.config.IDs;
import example.lib.config.Names;
public class Items {
//Section for public static names
public static Item exampleItem;
public static void init() {
//Section for adding the items
exampleItem = new ExampleItem(IDs.exampleItem_actual);
}
public static void addNames() {
//Section for registering item names
LanguageRegistry.addName(exampleItem, Names.exampleItem_name);
}
}
IDs class
package example.lib.config;
public class IDs {
// Adding your block and item IDs, IDs above 3000 for blocks and IDs above 5000 for items is recommended
// Blocks
public static int exampleBlock_actual;
public static final int exampleBlock_default = 3000;
// Items
public static int exampleItem_actual;
public static final int exampleItem_default = 5000;
}
Names class
package example.lib.config;
import cpw.mods.fml.common.registry.LanguageRegistry;
public class Names {
// Blocks
// I'm not sure what the unlocalized name does, but I think it has to do differentiating between different blocks and items. Could someone confirm or correct me on this?
public static final String exampleBlock_unlocalizedName = "exampleBlock";
public static final String exampleBlock_name = "Example Block";
// Items
public static final String exampleItem_unlocalizedName = "exampleItem";
public static final String exampleItem_name = "Example Item";
}
ConfigHandler class
package example.lib.config;
import java.io.File;
import net.minecraftforge.common.Configuration;
//The java.io.File is needed. Do not DELETE THE LINE!
// This inits the config, and adds a new .cfg file to the config folder in forge/mcp/jars
public class ConfigHandler {
public static void init(File configFile) {
Configuration config = new Configuration(configFile);
config.load();
//Where the config loads the block and item IDs, and it sets the ID by making IDs.exampleBlock_actual = IDs.exampleBlock_default, and also gets the name of the block
// Blocks
IDs.exampleBlock_actual = config.getBlock(Names.exampleBlock_name, IDs.exampleBlock_default).getInt();
// Items
// Vanilla Minecraft shifts the IDs of items by +256, I'm not sure why so the "- 256" is at the end to give us our imputed item IDs.
IDs.exampleItem_actual = config.getItem(Names.exampleItem_name, IDs.exampleItem_default).getInt() - 256;
config.save();
}
}
ModInfo class
package example.lib;
public class ModInfo {
// Strings are values where you can store text and/or numbers. Useful for keeping tidy.
public static final String NAME = "Example";
public static final String MODID = "username_example";
public static final String VERSION = "0.1";
public static final String CHANNEL = MODID;
public static final String PROXY_LOCATION = "example.proxies";
}
LogHelper class
package example.lib;
import java.util.logging.Level;
import java.util.logging.Logger;
import cpw.mods.fml.common.FMLLog;
public class LogHelper {
// What this does is that the Logger function is being stored in a value named logger and it will print out a statement in the console that says your mod's name when you run the client from Eclipse.
private static Logger logger = Logger.getLogger(ModInfo.NAME);
public static void init() {
// Setting parent is how we are able to print messages.
logger.setParent(FMLLog.getLogger());
}
// We are just calling a method to provide it with our conditions to print a log message.
public static void log(Level logLevel, String message) {
logger.log(logLevel, message);
}
}
ClientProxy class
package example.proxies;
import net.minecraftforge.client.MinecraftForgeClient;
public class ClientProxy extends CommonProxy {
@Override
public void initRenderers() {
// This is for rendering entities and so forth later on
}
@Override
public void initSounds() {
// This is for rendering sounds and so forth later on
}
}
CommonProxy class
package example.proxies;
public class CommonProxy
{
// Client stuff
public void initRenderers() {
// Nothing to put here as the server doesn't render graphics or entities.
}
public void initSounds() {
// Nothing to put here as the server doesn't render sounds.
}
}
ExampleBlock class
package example.blocks;
import example.ExampleMain;
import example.lib.config.Names;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
public class ExampleBlock extends Block
{
public ExampleBlock(int id)
{
// Initializes the ID.
//Don't forget the setUnlocalizedName part. It's vital.
// Sets creative tab and step.
// Sets hardness and resistance. Hardness is for how long it takes to mine a block, and resistance is the resistance to explosions, such as from creepers or tnt blocks.
super(id, Material.rock);
this.setUnlocalizedName(Names.exampleBlock_unlocalizedName);
this.setCreativeTab(CreativeTabs.tabBlock);
this.setStepSound(Block.soundStoneFootstep);
this.setHardness(2.5F);
this.setResistance(5.0F);
}
// Adds the texture location, so the game can find it and render it onto the block and item.
@SideOnly(Side.CLIENT)
public void registerIcons(IconRegister par1IconRegister)
{
this.blockIcon = par1IconRegister.registerIcon(example.lib.ModInfo.MODID + ":" + (this.getUnlocalizedName().substring(5)));
}
}
ExampleItem class
//The example item class is very similar to block classes, so very simple item classes don't usually have any unique properties.
package example.items;
import example.ExampleMain;
import example.lib.config.Names;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
public class ExampleItem extends Item {
public ExampleItem(int id)
{
super(id);
this.setUnlocalizedName(Names.exampleItem_unlocalizedName);
this.setCreativeTab(CreativeTabs.tabMaterials);
}
@SideOnly(Side.CLIENT)
public void registerIcons(IconRegister par1IconRegister)
{
this.itemIcon = par1IconRegister.registerIcon(example.lib.ModInfo.MODID + ":" + (this.getUnlocalizedName().substring(5)));
}
}
Slab code (read the commenting)
Main class
package tutorial;
// Comments that have a *** in any classes after the double-backslash are required for slab code.
import java.util.logging.Level;
import net.minecraftforge.common.MinecraftForge;
// ***
import net.minecraft.block.BlockHalfSlab;
import tutorial.blocks.Blocks;
import tutorial.blocks.TutorialSlabBlock;
import tutorial.items.Items;
import tutorial.lib.LogHelper;
import tutorial.lib.ModInfo;
import tutorial.lib.Recipes;
import tutorial.lib.config.ConfigHandler;
import tutorial.proxies.*;
// ***
import tutorial.items.TutorialSlabItem;
// ***
import net.minecraft.item.ItemSlab;
//***
import net.minecraft.block.BlockHalfSlab;
// ***
import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.registry.LanguageRegistry;
@Mod(modid = ModInfo.MODID, name = ModInfo.NAME, version = ModInfo.VERSION)
@NetworkMod( channels = {ModInfo.CHANNEL}, clientSideRequired = true, serverSideRequired = false)
public class TutorialMain
{
public static final String modid = ModInfo.MODID;
@Instance(value = ModInfo.MODID)
public static TutorialMain instance;
@SidedProxy( clientSide = ModInfo.PROXY_LOCATION + ".ClientProxy", serverSide = ModInfo.PROXY_LOCATION + ".CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void load(FMLPreInitializationEvent event)
{
ConfigHandler.init(event.getSuggestedConfigurationFile());
LogHelper.init();
LogHelper.log(Level.INFO, "Loading LogHelper...");
//Proxy Renderers & sounds
LogHelper.log(Level.INFO, "Preparing renderers and sounds");
proxy.initRenderers();
proxy.initSounds();
LogHelper.log(Level.INFO, "Renderers and sounds loaded");
// Blocks
LogHelper.log(Level.INFO, "Preparing blocks");
Blocks.init();
Blocks.addNames();
LogHelper.log(Level.INFO, "Blocks loaded");
// Items
LogHelper.log(Level.INFO, "Preparing items");
Items.init();
Items.addNames();
LogHelper.log(Level.INFO, "Items loaded");
// Recipes
LogHelper.log(Level.INFO, "Preparing recipes");
Recipes.init();
LogHelper.log(Level.INFO, "Recipes loaded");
}
@EventHandler
public void load(FMLInitializationEvent event)
{
// ***
// Kind of not sure what this does. Differencing between slab sub-types? Correction/confirmation is apprciated.
for (int i=0; i<TutorialSlabBlock.blockStepTypes.length; i++) {
ItemStack tutorialslab = new ItemStack(tutorial.blocks.Blocks.tutorialhalfslabBlock, 1, i);
LanguageRegistry.addName(tutorialslab, TutorialSlabBlock.blockStepTypes[i]);
}
// ***
// Adds the slab name, via string and config classes
LanguageRegistry.instance().addStringLocalization(((tutorial.blocks.TutorialSlabBlock)tutorial.blocks.Blocks.tutorialslabBlock).getFullSlabName(0)+".name", tutorial.lib.config.Names.tutorialslabBlock_name);
}
@EventHandler
public void load(FMLPostInitializationEvent event)
{
}
}
Blocks class
package tutorial.blocks;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import net.minecraft.block.Block;
// *** V
import net.minecraft.block.BlockHalfSlab;
import net.minecraft.block.material.Material;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import tutorial.lib.config.IDs;
import tutorial.lib.config.Names;
public class Blocks {
public static Block tutorialBlock;
// ***
public static BlockHalfSlab tutorialslabBlock;
public static BlockHalfSlab tutorialhalfslabBlock;
public static void init() {
tutorialBlock = new TutorialBlock(IDs.tutorialBlock_actual);
GameRegistry.registerBlock(tutorialBlock, Names.tutorialBlock_name);
MinecraftForge.setBlockHarvestLevel(tutorialBlock, "pickaxe", 1);
// ***
tutorialslabBlock = new TutorialSlabBlock(IDs.tutorialslabBlock_actual, true, null);
GameRegistry.registerBlock(tutorialslabBlock, Names.tutorialslabBlock_name);
MinecraftForge.setBlockHarvestLevel(tutorialslabBlock, "pickaxe", 1);
// ***
tutorialhalfslabBlock = new TutorialSlabBlock(IDs.tutorialhalfslabBlock_actual, false, null);
GameRegistry.registerBlock(tutorialhalfslabBlock, Names.tutorialhalfslabBlock_name);
MinecraftForge.setBlockHarvestLevel(tutorialhalfslabBlock, "pickaxe", 1);
}
public static void addNames() {
LanguageRegistry.addName(tutorialBlock, Names.tutorialBlock_name);
// ***
LanguageRegistry.addName(tutorialslabBlock, Names.tutorialslabBlock_name);
}
}
Items class
package tutorial.items;
import net.minecraft.item.Item;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.registry.LanguageRegistry;
import tutorial.lib.config.IDs;
import tutorial.lib.config.Names;
public class Items {
//Section for public static names
public static Item tutorialItem;
// ***
public static Item tutorialslabItem;
public static Item tutorialhalfslabItem;
public static void init() {
//Section for adding the items
tutorialItem = new TutorialItem(IDs.tutorialItem_actual);
// ***
// Sets the ID of the slab item to the ID of the slab block, but minus 256. Also gets the true/false for if slab is one slab or two stacked slabs.
tutorialhalfslabItem = new TutorialSlabItem(tutorial.blocks.Blocks.tutorialhalfslabBlock.blockID - 256, tutorial.blocks.Blocks.tutorialhalfslabBlock, tutorial.blocks.Blocks.tutorialslabBlock, false);
tutorialslabItem = new TutorialSlabItem(tutorial.blocks.Blocks.tutorialslabBlock.blockID - 256, tutorial.blocks.Blocks.tutorialhalfslabBlock, tutorial.blocks.Blocks.tutorialslabBlock, true);
}
public static void addNames() {
//Section for registering item names
LanguageRegistry.addName(tutorialItem, Names.tutorialItem_name);
// ***
LanguageRegistry.addName(tutorialslabItem, Names.tutorialslabItem_name);
}
}
Names class
package tutorial.lib.config;
// ***
import tutorial.blocks.TutorialSlabBlock;
import cpw.mods.fml.common.registry.LanguageRegistry;
public class Names {
// Blocks
public static final String tutorialBlock_unlocalizedName = "tutorialBlock";
public static final String tutorialBlock_name = "Tutorial Block";
// ***
public static final String tutorialslabBlock_unlocalizedName = "tutorialslabBlock";
public static final String tutorialslabBlock_name = "Tutorial Slab";
public static final String tutorialhalfslabBlock_unlocalizedName = "tutorialslabBlock";
public static final String tutorialhalfslabBlock_name = "Tutorial Half Slab";
// Items
public static final String tutorialItem_unlocalizedName = "tutorialItem";
public static final String tutorialItem_name = "Tutorial Item";
// ***
public static final String tutorialslabItem_unlocalizedName = "tutorialslabItem";
public static final String tutorialslabItem_name = "Tutorial Slab";
}
IDs class
package tutorial.lib.config;
// ***
import net.minecraft.block.BlockHalfSlab;
public class IDs {
// Blocks
public static int tutorialBlock_actual;
public static final int tutorialBlock_default = 3000;
// ***
public static int tutorialslabBlock_actual;
public static final int tutorialslabBlock_default = 3001;
public static int tutorialhalfslabBlock_actual;
public static final int tutorialhalfslabBlock_default = 3002;
// Items
public static int tutorialItem_actual;
public static final int tutorialItem_default = 5000;
// ***
public static int tutorialslabItem_actual;
public static final int tutorialslabItem_default = 5001;
}
package tutorial.items;
import net.minecraft.block.BlockHalfSlab;
import net.minecraft.item.ItemSlab;
import net.minecraft.util.Icon;
import tutorial.items.Items;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TutorialSlabItem extends ItemSlab {
// Sets the ID, and half/double state for the slab.
public TutorialSlabItem(int id, BlockHalfSlab halfSlab, BlockHalfSlab doubleSlab, boolean par4) {
super(id, halfSlab, doubleSlab, par4);
}
}
TutorialSlabBlock class
package tutorial.blocks;
import java.util.List;
import java.util.Random;
import net.minecraft.block.BlockHalfSlab;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Icon;
import net.minecraft.world.World;
import tutorial.TutorialMain;
import tutorial.lib.config.Names;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TutorialSlabBlock extends BlockHalfSlab {
// String for setting slab sub-types. Haven't figured this out, so this will come later.
public static String[] blockStepTypes = new String[]{ "sapphireSlab" };
@SideOnly(Side.CLIENT)
private Icon sideIcon[];
public TutorialSlabBlock(int id, boolean DoubleSlab, Material par3Material) {
// You can customize the Material, Hardness, Resistance, StepSound if you wish.
// I recommend:
// 5.0F(Hardness)/30.0F(Resistance) for metal and gemstone
// 3.0F(Hardness)/20.0F(Resistance) for stone
// 2.0F(Hardness)/15.0F(Resistance) for wood
// Also, for wood, don't forget to add:
// setBurnProperties(this.blockID, 5, 20);
super(id, DoubleSlab, Material.rock);
this.setCreativeTab(CreativeTabs.tabBlock);
setHardness(3.0F);
setResistance(20.0F);
setStepSound(soundStoneFootstep);
// The flag for if this block should use neighbor blocks as it's brightness value
useNeighborBrightness[this.blockID] = true;
}
// A bit for getting the full slab name, depending on sub-type
@Override
public String getFullSlabName(int meta) {
if (meta < 0 || meta >= blockStepTypes.length) {
meta = 0;
}
return getUnlocalizedName() + "." + blockStepTypes[meta];
}
// Registering the textures for the slab.
// This has support for side and multiple sub-type textures!
@SideOnly(Side.CLIENT)
public void registerIcons(IconRegister par1IconRegister) {
sideIcon = new Icon[2];
blockIcon = par1IconRegister.registerIcon("ladestitute_tutorial:tutorialslabBlock");
sideIcon[0] = par1IconRegister.registerIcon("ladestitute_tutorial:tutorialslabsideBlock");
}
/**
* From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
*/
@SideOnly(Side.CLIENT)
public Icon getBlockTextureFromSideAndMetadata(int side, int metadata) {
int k = metadata & 7;
if (k == 1) return sideIcon[1];
if (isDoubleSlab && (metadata & 8) != 0) {
side = 1;
}
return (side != 1 || side != 0) ? blockIcon : sideIcon[0];
}
/**
* From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
*/
@SideOnly(Side.CLIENT)
public Icon getIcon(int side, int meta) {
int k = meta & 7;
if (k == 0) {
if (side == 0 || side == 1) return blockIcon;
return sideIcon[0];
}
return sideIcon[k];
}
/**
* Takes a block ID, returns true if it's the same as the ID for a stone or wooden single slab.
*/
@SideOnly(Side.CLIENT)
private static boolean isBlockSingleSlab(int par0) {
return par0 == tutorial.blocks.Blocks.tutorialhalfslabBlock.blockID;
}
/**
* only called by clickMiddleMouseButton , and passed to inventory.setCurrentItem (along with isCreative)
*/
@SideOnly(Side.CLIENT)
public int idPicked(World par1World, int par2, int par3, int par4) {
return tutorial.blocks.Blocks.tutorialhalfslabBlock.blockID;
}
//Tells game what slab sub-type drops on destruction
public int idDropped(int par1, Random par2Random, int par3) {
return tutorial.blocks.Blocks.tutorialhalfslabBlock.blockID;
}
// The bit of code that creates a stacked slab block if two are stacked together
@Override
protected ItemStack createStackedBlock(int meta) {
return new ItemStack(tutorial.blocks.Blocks.tutorialhalfslabBlock.blockID, 2, meta & 7);
}
// Gets the sub-block type
@SuppressWarnings("unchecked")
@Override
public void getSubBlocks(int id, CreativeTabs par2CreativeTabs, List par3List) {
if (id != tutorial.blocks.Blocks.tutorialslabBlock.blockID) {
for (int i = 0; i < blockStepTypes.length; i++) {
par3List.add(new ItemStack(id, 1, i));
}
}
}
}
They're half-blocks, stack on top of each other, and often make a nice aesthetic touch to your base or home.
Wouldn't you like to make a custom slab?
I tried for about a day and a half to get a custom slab to work. After several hours of debugging, googling, repeat client launches, and some help from some very special people here, I managed to get a custom slab to work no problem (well, -almost-). Now, I want to share my knowledge and code.
Requirements, readme & thanks
Java Development Kit (depending on your OS and 64-bit/32-bit)
Eclipse (depending on your OS, and 64-bit/32-bit)
Forge (get the latest version)
A FUNCTIONING BRAIN
Readme!
Firstly, setup a config in your mod if you haven't already. I am using MrrGingerNinja's config, and the tutorial for it can be found below.
Backup your mod's .java files. BACKUP THEM. I am not responsible if my tutorial breaks your mod.
Read the commenting for each class carefully. Don't forgot to add and fix any necessary imports. If you're getting errors (either in Eclipse or during runtime), doublecheck the steps or compare your code to mine. Lastly, try solving the errors on your own by trial and error, googling solutions and searching the MC forums.
If you get a console error that says something like "@ <slab-ID> item slot already occupied", ignore it. It's not gamebreaking at all, but I can't figure out how to solve right now. ProgrammarHero doesn't know how to solve it either, though.
If nothing else, post what section and step you have a problem with, and use spoilers and screenshots, please.
Thanks
ProgrammerHero (for his slab source, which was a basis for this, and it can be found here)
MrrGingerNinja (for his config tutorial)
Pahimar (for his Eclipse development environment video, which can be found )
Simple class examples (for main mod class and config setup)
Blocks class
Items class
IDs class
Names class
ConfigHandler class
ModInfo class
LogHelper class
ClientProxy class
CommonProxy class
ExampleBlock class
ExampleItem class
Slab code (read the commenting)
Blocks class
Items class
Names class
IDs class
ConfigHandler class
TutorialSlabItem class
TutorialSlabBlock class
Github (source code)
Huzzah!
The following is true, the former is false.
Disclaimer: I'm a bit biased since I contributed too