Here are some Minecraft Forge tutorials. I thought it would be better to keep them separate from the ModLoader ones. I'll start off with the basics of Forge like Blocks and such. Ghosrec35 will be doing the advanced/important tutorials.
Main Registry
Your Forge registry class has almost nothing in common with your mod_ class from ModLoader except that it registers everything.
Firstly, just start with the package declaration as usual. You can use your own package but I will just use the default Minecraft source package for this example.
After you have the @Mod annotation we need to add the @NetworkMod annotation. We use this as well as the @Mod annotation because Forge mods are "universal"(one version for both Client and Server).
After the @NetworkMod annotation you can declare the class. Note that with Forge, this class doesn't require a mod_ prefix or extend BaseMod.
public class ModUndeadMainRegistry {
We then need to add three methods. They are each called at specific times in the loading of the game. The methods can be called anything as long as they have the Forge annotation and parameter that is required. The first is preInit.
@PreInit
public void preInit(FMLPreInitializationEvent event)
This method is called before the mods are loaded.
@Init
public void init(FMLInitializationEvent event)
This method has the same function as the load() method is ModLoader. In here is where we will register all of our blocks, recipes, entities, etc.
@PostInit
public void postInit(FMLPostInitializationEvent event)
This is the final method that is required in the Forge mod registry. Put stuff that you want to happen after the mods are loaded in here.
package ghosrec35.mods.example.common;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.IMCCallback;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PostInit;
import cpw.mods.fml.common.Mod.PreInit;
import cpw.mods.fml.common.Mod.ServerStarted;
import cpw.mods.fml.common.Mod.ServerStarting;
import cpw.mods.fml.common.Mod.ServerStopping;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartedEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.event.FMLServerStoppingEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
@Tutorial("Annotation Interfaces within Forge")
@Mod(modid = "Ghosrec35_ExampleMod", name = "ExampleMod", version = "1.0.0.0", dependencies = "after:UniversalElectricity")
@NetworkMod(clientSideRequired = true, serverSideRequired = false)
public class ExampleMod
{
@SidedProxy(clientSide = "ghosrec35.mods.examplemod.client.ClientProxy", serverSide = "ghosrec35.mods.examplemod.common.CommonProxy")
public static CommonProxy proxy;
@Instance("Ghosrec35_ExampleMod")
public ExampleMod instance;
@PreInit
public void loadPre(FMLPreInitializationEvent event)
{
}
@Init
public void load(FMLInitializationEvent event)
{
}
@PostInit
public void loadPost(FMLPostInitializationEvent event)
{
}
@ServerStarting
public void serverStarting(FMLServerStartingEvent event)
{
}
@ServerStarted
public void serverStarted(FMLServerStartedEvent event)
{
}
@ServerStopping
public void serverStopping(FMLServerStoppingEvent event)
{
}
@IMCCallback
public void interModComms(FMLInterModComms event)
{
}
}
Understanding the Code
Forge ModLoader and the MinecraftForge API include many different Annotation Interfaces that allow us to create methods within our main mod file that are able to be invoked from outside files without editing base classes. This is achieved by first Annotating the said mod file with the @Mod and @NetworkMod Annotation Interfaces which originally registers these certain files with Forge ModLoader.
The @Mod annotation requires three parameters, modid which must be equivalent to a String, name which must be equivalent to a String, and version which must be equivalent to a String. Now this main mod file is able to contain other Annotation Interfaces located within the Mod.java located in the cpw.mods.fml.common package.
The First of these methods introduced by the Mod.java is the @Instance annotation. This Annotation Interface Targets ElementTypes of the type Field, and so we annotate a public field of the type our mod file. This Annotation contains a value() of type String which is to be equivalent to your mod file's modid. This Annotation is then used to populate the field of which it annotates with the instance of your Mod File.
The rest of the Annotations derived from the Mod.java do not require such a level of complexity.
The first of these methods is the @PreInit Annotation which must Annotate a method with a parmeter of the type FMLPreInitializationEvent. This allows Forge ModLoader to perform two checks (The first being that this is the method designated for Pre-Initialization and the second being that the method again is designated for Pre-Initialization, but also that when invoked the method is able to retrieve information and perform tasks available from the instance of the PreInitEvent which is passed to the method upon its invocation.)
The next Annotation is the @Init annotation which must Annotate a method with a parameter of the type FMLInitializationEvent. This Annotation works in the same way as @PreInit, except the method is invoked after the Pre Initialization methods of all mods are invoked. This allows for such things as configuration files to be created and for settings to be loaded and saved before other processes begin within the Mod. (Of course, any fields created and initialized will actually be run before the PreInit method as this occurs upon the loading of the Class itself.) The @Init method is run at the normal time of Block Registration, Recipe Registration, etc, to occur within Minecraft.
The next Annotation is the @PostInit annotation interface, which must annotate a method with a parameter of FMLPostInitializationEvent. This method is invoked after both the @PreInit and @Init Annotated methods are invoked for each and every mod being loaded. These methods allow for easy manipulation of Minecraft and the many Blocks/Items/ and other things that have been introduced by your mod. This method is commonly called modsLoaded, as it occurs after all the mods have been loaded.
The next Annotation is the @ServerStarting Annotation. This Annotation yet again annotates a method, but this Annotation must annotate a method with a parameter of the type FMLServerStartingEvent. This method allows for the Manipulation of the server while the server is in the process of initializing all its files.
The next Annotation is the @ServerStarted Annotation, which annotates a method with a parameter of the type FMLServerStartedEvent. This method allows for easy manipulation of the Server after the server has been started, and is currently running.
The next Annotation interface us the @ServerStopping annotation which annotates must annotate a method with a parameter of the type FMLServerStoppingEvent. This Annotation allows for the invocation of this method to allow for manipulation of the server while the server is stopping.
The final Annotation I will be discussing is the @IMCCallback Annotation. This Annotation annotates a method with a parameter of the type FMLInterModComms. This allows your mod to send messages to other mods by the use of the Mods' modid and a key-value pairing. The next step is to move on to the NetworkMod annotation interface which continues on with much of the more complicated annotations.
The first thing to note is that there are only two attributes that are required with the @NetworkMod annotation.
@NetworkMod(clientSideRequired = true, serverSideRequired = false)
Both attributes must equal boolean values. These values determine whether a player is allowed to join a server or not. clientSideRequired should almost always be true, and serverSideRequired should almost always be false. clientSideRequired determines whether the client side of the mod must be installed for a player to join a server with the mod installed, while serverSideRequired determines whether the server side must be installed for the client to be able to join it.
We set clientSideRequired to true almost always because there are very few occassions in which nothing client side would be required for a mod.
We set serverSideRequired to false almost always because we will still want users to be able to join their favorite servers even if the server does not have the mod installed.
There are other attributes that are available for usage. These are channels, packetHandler, tinyPacketHandler, and connectionHandler. There are two more, but I will wait to discuss these until finished with the first three.
The channels attribute must be equivalent to an Array of Strings. This array will contain the Channels that will be automatically registered for your mod.
For Example:
The IConnectionHandler interface forces the ConnectionHandler class to override a few different methods that allow you to handle certain events with a player when they're connecting. Some more information on this in a future tutorial.
The tinyPacketHandler attribute must be equivalent to a class that implements ITinyPacketHandler. This works just like a regular PacketHandler, but for smaller packet sizes.
Before we get to the final two attributes, it's important to note again that you only NEED the clientSideRequired and serverSideRequired attributes to run your mod. The rest are only required for things like SMP or Connection Handling.
The final two attributes are a little trickier than the rest. They are clientPacketHandlerSpec and serverPacketHandlerSpec.
These attributes allow you to define separate packet handling classes and channels for both the Server and Client Side. You may use these rather than defining the channels and packetHandler attributes.
Each must be equivalent to an @SidedPacketHandler annotation. We are then able to use the attributes of the @SidedPacketHandler Annotation to further define each. The two attributes are channels, and packetHandler. These attributes function the same as they do above, but when used in an @SidedPacketHandler annotation only register the information with one side of Minecraft.
The next NetworkMod related annotation is the @SidedProxy annotation. @SidedProxy has a Target of the ElementType FIELD, meaning it must annotate a field. For our example, we are going to create a field of the type CommonProxy.
@SidedProxy(clientSide = "ghosrec35.mods.examplemod.client.ClientProxy", serverSide = "ghosrec35.mods.examplemod.common.CommonProxy")
public static CommonProxy proxy;
The two attributes of the @SidedProxy annotation are clientSide and serverSide. These two values are equivalent to String values and will contain the file path (including your package path) to your two separate proxies. More on this later.
You must create a file that will be your CommonProxy (Conventionally, this is usually called CommonProxy), and then you must create another file that will extend CommonProxy which will be your ClientProxy (Conventionally, this is usually called ClientProxy).
The @SidedProxy annotation requires the path to your ClientProxy and CommonProxy, as it automatically populates the proxy field of type CommonProxy with whichever proxy is designated for whichever side. This means that when invoking any methods, you must ensure that both the Client and Common Proxy contain the methods so that no exceptions occur during runtime on either the Server or Client. Proxys are used for such things as preloading textures, as you are able to register rendering information on only the client side by including the render methods in only the client proxy. The ClientProxy must extend the CommonProxy so that you are able to populate the field of the type CommonProxy with your ClientProxy as well.
The final @NetworkMod associated Annotation is the @SideOnly Annotation.
The @SideOnly annotation requires a value of the type Side, which is an enumeration containing the values CLIENT, SERVER, and BUKKIT. Annotating a field with @SideOnly ensures that the method is only run on the designated side. For instance, the vanilla Minecraft code has been edited throughout with
@SideOnly(Side.CLIENT)
over certain methods that are used for Client only details such as Rendering or Sound.
Thank-you
Thank you to the following people who have really assisted me well with helping people while I am absent. There are many others who have helped but these are the main ones who keep returning and giving great help to countless amounts of people.
ghosrec35- ghosrec35 has written the Forge tutorials.
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
On hold until problems are resolved.
This tutorial has been long awaited. It will utilise Forge and it alone. If you are following this tutorial it is expected that you have already set up your Forge workspace and have the main registry class of your mod set up. When I say "the main registry class", I am referring to the class with the @Mod annotation and the three methods with the @Init, @PreInit and @PostInit annotations.
At first, it will be a basic tutorial on how to get the dimension up and running. After that you can customise it however you like. I plan to have customisation tutorials up soon but I would prefer just to get this bit done well first.Before we actually get to making the dimension, I'll give a bit of an explanation as to how they actually work.
Dimensions use something called a WorldProvider. The WorldProvider registers almost everything to do with the dimension. Functions the WorldProvider has include registering the chunk manager and provider for the world(more about this below), the angle the sky is rotated at and setting the light levels, biomes and fog levels; just to name a few.
The biome/s of the world is/are set in the WorldChunkManager. You could have one biome, or many. It is all up to you when you get to customising it. The chunk manager is registered in the WorldProvider class.
The terrain itself is set in the ChunkProvider for the world. The ChunkProvider allows possibly the most customisation out of every other class that you will make. It is registered in the WorldProvider class aswell. In the chunk provider you can change terrain levels, water/lava pools, villages, strongholds, dungeons, etc. and several other variables that are fun to mess around with.
The first thing you'll need to do is register a provider for your dimension.
The registerProviderType method accepts three arguments: An int, a class and a boolean. The int is the id you want to assign to the world provider. The class is the actual class of the world provider. The boolean is whether or not to keep the world loaded. That is, keep the world prebuffered so it loads quicker(needs citation). For example, the Overworld and Nether have this boolean set to true but the End has it set to false. This is because the Overworld and Nether are the main two dimensions of the game.
Next you will need to register the dimension itself and assign the world provider.
The registerDimension method accepts two ints for its arguments. The first is the id of the dimension. You are best of calling the getNextFreeDimId method here so that there are less incompatibilities. The second int is the providerType id you assigned to the world provider above. In our example we used 25.
This section is under work. I will try not to let it die like the gui tutorial did. By the time this tutorial is fully done, it will probably be my most comprehensive tutorial by far.
Tutorials I Recommend
This is a list of tutorials I recommend. These tutorials are not made by me, nor will I make tutorials on things listed below. I can't guarantee that these tutorials will continue to be updated and work in the future.
Adding Sounds - By lockNload147 (Vanilla(All MC Versions), Forge 1.5)
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
Below are some tutorials updated for Minecraft 1.4.7 utilising ModLoader. There are also some more on the posts below utilising Minecraft Forge. None of them edit base classes so you should have no problems with compatibility. Hope you enjoy!
Getting Set-Up
Setting Up Your Workspace
Installing the Java Development Kit is the first thing you'll need to do. Get it here. The JDK comes pre-installed on Mac OSX so Mac users can move onto step 3. If you can't find the download button for the JDK, the red arrow in this picture is pointing to it:
Now you'll need to set up the Java Path. For Windows, go to My Computer, right click on 'Computer' in the left bar, and select 'Properties'. Once in properties, select 'Advanced System Settings'. Click 'Environment Variables...', then under 'System Variables', go to 'Path'. Without deleting any current path in there, add this to the end.
C:\Program Files\Java\jdk1.7.0_03\bin;
. Ensure that the version of the jdk that you add to Path is the same as the version you have installed. Just go to your C:/ drive and in program files look for Java, all your version nformation for this will be there.
Delete/backup your bin and resources folders in .minecraft. Start Minecraft and force an update when logging in.
Copy the newly downloaded bin and resources folders into the 'jars' folder into the extracted MCP folder. Open the minecraft.jar in the 'jars' folder with an application like WinRAR and add in the ModLoader classes. Deleting Meta-Inf is optional.
Double click 'decompile.bat' in the MCP directory or open the decompile.sh in Terminal if on a Mac. To open it in Terminal simply change the directory to your MCP folder and type
bash decompile.sh
The minecraft.jar will then be decompiled. Once it has finished, it should say something similar to '2 out of 2 hunks failed, ignoring'. Don't worry about it, it doesn't matter. All of the decompiled Minecraft files will be in mcp723/src/net/minecraft/src.
This is optional, but highly recommend. Download Eclipse. The correct version is the first on the list, 'Eclipse for Java Developers'. Just select your OS and download.
I currently use Eclipse Juno but any of the other Eclipse version will also work.
After it's downloaded, extract it to a folder and open eclipse.exe.
This is very easy to set up. When it asks you to select a workspace, select the 'eclipse' folder in your MCP directory. It will now set everything up automatically.
To access the decompiled class files of Minecraft in Eclipse go to 'Client' in the package explorer. Then go to 'src', then the 'net.minecraft.src' package. All classes of the source code of Minecraft will then be listed.
Basics of Modding
Just a few small notes to start with:
At least a basic knowledge of Java is recommended. It will greatly assist you with fixing errors and stops you from making them in the first place.
The main class of your mod where you instantiate and add names for things, etc. must be prefixed with
mod_
and extends BaseMod. Without these two things ModLoader will not recognise your mod.
All other class names are versatile, call them whatever you want. Note that you should follow one main rule though. This rule helps to keep everything organised. Say that you are creating a block and it requires a separate class because it has some special features. The name of that blocks class should begin with "Block" (without the quotes) followed by the name of that block. For example, if I am making a camera block, then the name of that class would be
BlockCamera
. This rule should also be followed for entities, items and other instances.
Read the tips section for more information.
Most of this will become easier to understand once you become a bit more fluent with modding.
The ModLoader Basics
package net.minecraft.src;
public class mod_*** extends BaseMod
{
public void load()
{
}
public String getVersion()
{
return "1.4.7";
}
}
This is the basic ModLoader 'template'. It is fairly simple and I will explain what each of the parts do.
package net.minecraft.src;
This line simply defines the package of the game that this file is placed in. There are several packages in Minecraft and different packages are called at different times.
public class mod_*** extends BaseMod
'public class' defines that this is a class. It is a part of the Java language which Minecraft is written on. Other coding languages use it too. 'mod_***' defines that this is a ModLoader mod. The mod_ file is the most important part of a ModLoader mod. Without it, the mod will not work. 'extends BaseMod' allows you to use all of the ModLoader methods in the mod_ file. It is just as important as the class name starting with mod_ because your mod will not work without it. You must use a lowercase
m
on
mod_
public void load()
This is the constructor of a mod_ file. This is ModLoader's constructor. Do not use or include the java constructor in this file. The ModLoader methods go in between the two squiggly brackets.
public String getVersion()
{
return "1.4.7";
}
This is used by ModLoader when it loads your mod. The version of Minecraft your mod is made for goes in here. A number/words in quotes should always be returned in this method. It should never return null.
ModLoader Tutorials
All tutorials updated for 1.4.7! Please let me know of any problems you encounter.
Block
mod_Block
package net.minecraft.src;
public class mod_Block extends BaseMod
{
public static final Block nameHere = new BlockNameHere(160, 0).setBlockName("anynamehere").setHardness(3F).setResistance(4F).setLightValue(1F).setCreativeTab(CreativeTabs.tabBlock).setStepSound(Block.soundGrassFootstep);
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNamehere
package net.minecraft.src;
import java.util.Random;
public class BlockNameHere extends Block
{
public BlockNameHere(int i, int j)
{
super(i, j, Material.wood);
}
public int idDropped(int i, Random random, int j)
{
return mod_Block.nameHere.blockID;
}
public int quantityDropped(Random random)
{
return 1;
}
}
Understanding the Code
mod_Block
public static final Block nameHere = new BlockNamehere(160, 0)
This declares the new block.
The first name you put in('Namehere' in this case), is what you use in the rest of the file for referencing to each particular block.
'= new BlockName' means that when the block is created, to get its data from the class called 'BlockNamehere'. It will be different for each block if it has special features(See below if it doesn't).
'(160, 0)' is the data value and the image coordinate in the terrain.png. Block ID's MUST be no higher then 255 and not being used already by Minecraft. As of 1.4.7 id's between 137-255 can be used.
If you want to use your own image for your block, leave the second number in the brackets as 0, and use the ModLoader method for your image.
'.setBlockName("name")' is used by the game to call the block while its running, instead of calling the other name you gave it, which is only used with-in the mod_ file.
'.setHardness(3F)' defines how hard the block is(obviously), and how long it takes to mine. Remember to put an F after the integer(number).
'.setResistance(4F)' is how resistant the block is to TNT.
'.setLightValue(1F)' is the amount of light the block emits. If the number is too high, the game will crash because it cant handle the brightness. Having your block emit light will give the same effect as torches and glowstone do on snow and ice. If you dont want your block to emit any light at all, just leave out this piece of code.
.setCreativeTab(CreativeTabs.tabBlock);
Read the "Adding Blocks to Creative Inventory" tutorial near the bottom of this post for information on this.
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/pathtoyourfile/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
The most important parts of this bit of code is the 'ModLoader.registerBlock' and 'ModLoader.addName'. 'ModLoader.registerBlock', quite simply, registers the block with the game so it knows that it is there. Without it, it wouldn't find it, and you might even get a crash.
'ModLoader.addName' adds the in-game name of the block, the name you see when the block is in your inventory/chest ect..
'Namehere.blockIndexInTexture' is not needed unless you want to use your own image for the block. Despite what it looks like, this doesn't actually override the terrain.png image. It only overrides the index to the terrain.png, which would normally be used for blocks. All images must be 16pixels by 16pixels and png in format. Have a look at the "Textures" section for more information.
'ModLoader.addRecipe......' just adds the recipe for the block. Have a look at the "Crafting and Smelting Recipes" section for more information.
BlockNamehere
Not much to explain here except for the material. There are several materials you can use for blocks, they all do different things. Ground, wood, rock and iron, are the main materials to use, but there are many more. If you want to see them all, have a look in Material.java.
You can change what your block drops by adding its name in the idDropped method. You can only put one block or item here. If you need it to drop more, use some ''if'' statements. You can find an example of this in BlockCrops.
The ''quantityDropped'' method simply allows you to change the amount dropped from the block when destroyed.
If your block has no special features, you can minimize the amount of files you have by doing this:
In your mod_ file, go to the line starting with 'public static final'. It should look something like this:
public static final Block nameHere = new BlockName(160, 0).setBlockName("anynamehere").setHardness(3F).setResistance(4F).setLightValue(1F);
Now change it to say ' = new Block(id, 0, Material.wood)'. So it would look like this:
public static final Block nameHere= new Block(160, 0, Material.wood).setBlockName("anynamehere").setHardness(3F).setResistance(4F).setLightValue(1F);
You can then delete the separate class you made for the block. You can change the matieral like explained above.
Item
mod_Item
package net.minecraft.src;
public class mod_Item extends BaseMod
{
public static final Item nameHere = new ItemNameHere(5000).setItemName("anyNameHere");
public void load()
{
nameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/image.png");
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
ItemNamehere
package net.minecraft.src;
public class ItemNameHere extends Item
{
public ItemNameHere(int i)
{
super(i);
maxStackSize = 64;
}
}
Understanding the Code
mod_Item
This is basically the same as the block except for a few differences.
The public static final line states that the new object is an Item. It says "public static final Item" instead of "public static final Block".
'.setBlockName' is also changed to '.setItemName' for items.
The other main difference is the icon indexing. For an item, it is
'itemName.iconIndex = ModLoader.addOverride("/gui/items.png", "/image.png");'. The differences to a block are in bold.
ItemNamehere
This is the same as a blocks, except it doesn't have 'int j' or the material included. Items do not have a material. They also don't have an idDropped or quantityDropped method
If your item has no special features, you can minimize the amount of files you have by doing this:
In your mod_ file, go to the line starting with 'public static final'. It should look something like this:
public static final Item nameHere= new ItemNamehere(960).setItemName("anyNameHere");
Now change it to say ' = new Item(id)'. So it would look like this:
public static final Item nameHere= new Item(960).setItemName("anyNameHere");
You can then delete the separate class you made for the item.
You will have one of these two for your block/item. Make sure you have a / in front of your image directory. If you are wondering what this means, or where you put your images, keep reading. Basically, this method just overrides the index of your item/blocks picture. The game would normally look in the terrain.png or items.png for an image, but because we're using ModLoader, and not editing any base classes or images, we add 'ModLoader.addOverride...'.
Your images can go in:
mcp/bin (If you have your textures here or the path below and are receiving crashes, put them in your jar)
mcp/bin/minecraft
mcp/jars/bin/minecraft.jar
If you're using Eclipse, they go here (Note: images can't go in the above directories when using eclipse):
mcp/eclipse/Client/bin (If you have your textures here and are receiving crashes, put them in your jar)
mcp/jars/bin/minecraft.jar
and was using eclipse,
my picture would go in here:
mcp/eclipse/Client/bin/mods/flower.png
Its basically the same thing, if you're not using Eclipse. Using the example above, but without eclipse, my picture would go in here:
mcp/bin/minecraft/mods/flower.png
All images should be 16pixelsX16pixels and must be .png in format.
When packaging your mod for final release, if your path is
'ModLoader.addRecipe' is just the ModLoader method for adding a recipe.
'(new ItemStack(itemtoreceive, quantity), new Object []' means to create a new inventory stack of the item in brackets, and add the number after the item name to the quantity of the stack.
'{"#@#", "%%%", "@#@", ' is the actual recipe of the item. The first set of quotes is the first row in the crafting table, the second set of quotes is the second row, and so on.
'Character.valueOf('#'), Block.stone... ' means to associate the name of the block/item, with the symbol in brackets, and then that symbol in the recipe. There must be an equal amount of spaces in each row. If you have a recipe like this,
then it needs to be like this in crafting.
"###", " $ ", " $ ", Character.valueOf('#')//and so on
Smelting
ModLoader.addSmelting(Block.dirt.blockID, new ItemStack(Item.ingotGold, 1), 1.0F);
Smelting recipes are fairly simple, 'ModLoader.addSmelting' is the ModLoader method for smelting recipes. '(Block.dirt.blockID, new ItemStack(Item.goldIngot, 1));' just says that putting a block of dirt into the furnace and smelting it, will result into a new stack being created of gold ingots. One ingot being added to the new stack every time a dirt block is smelted. The float at the end(1.0F) is the amount of XP received from smelting the item. As a reference, cobblestone is 0.1F and a gold ingot is 1.0F
Using Your Own Added Item
If you want to use your own item in a recipe, just use the name you made for it in the 'public static final' line. For example(also applys to blocks):
public static final Item Camera = new Item(2112).setItemName("Camera");
ModLoader.addSmelting(Camera.itemID, new ItemStack(BurntCamera, 1), 1.0F);
Using Dyes
ModLoader.addRecipe(new ItemStack(itemname, quantity), new Object[] {"#", Character.valueOf('#'), new ItemStack(Item.dyePowder, 1, 3)});
That recipe would make an item from cocoa beans being alone in the crafting grid. This is the part for adding the dye as an ingredient.
Character.valueOf('#'), new ItemStack(Item.dyePowder, 1, 3)
It is basically a call for metadata, which is what the dye is stored in(multiple items, 1 id. 15:1, 15:2, etc) 1 is the quantity, and 3 is the colour of the dye. Even as an ingredient, you must have the quantity there. The colour of 3 is cocoa beans. All of the id's for dyes can be found here.
Food
mod_Food
package net.minecraft.src;
public class mod_Food extends BaseMod
{
public static final Item nameHere = new ItemFood(5001, 4, 1F, false).setItemName("anyNameHere");
public void load()
{
nameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/image.png");
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
Understanding the Code
Food is just an item, that has a little difference. It heals the food bar.
How this is done, is by making the new item extend a class called ItemFood. ItemFood is set up with three integers and a boolean.
The first, is simply the id of the item.
The second is how many half it heals. Putting a 4 in this spot will heal on the food bar when the food is eaten.
The third is the saturation level. Food saturation is an invisible bar that stops you from getting hungry for a certain amount of time. When the saturation bar is empty, the food bar 'jitters'.
The boolean at the end is whether or not a wolf can eat it. Putting a 'true' here will allow it to be eaten by a wolf.
Potion Effected Food
Applying a potion effect to a food. This is very easy to do. You simply make a food(follow the tutorial above), and add this bit of code to it:
.setPotionEffect(Potion.hunger.id, 15, 0, 1F)
so it would fit in like this:
public static final Item nameHere = new ItemFood(5001, 4, 1F, false).setPotionEffect(Potion.hunger.id, 15, 0, 1F).setItemName("anyNameHere");
Note that .setPotionEffect MUST go before .setItemName otherwise you will get recompilation errors. I use the hunger potion in this example, which is what is used in rotten flesh. You can use any potion effect when doing this. They are all listed in Potion.java. The 15 is the time the effect lasts for. You can replace the 0 with another number. It should amplify the effect/time it lasts for. An int of 1 here lasts for a second, 30 lasts for 30 seconds, etc. The float(1F) is the chance of the potion being successful. Rotten flesh has 0.8F here, and it has an 80% chance of giving you food poisoning. Having 1F in this position should be a 100% success rate.
Multiple Potion Effects on Food
mod_Food
package net.minecraft.src;
public class mod_Food extends BaseMod
{
public static final Item nameHere = new ItemFoodNameHere(5001, 4, 1F, false).setItemName("anyNameHere");
public void load()
{
nameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/image.png");
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
ItemFoodNameHere
package net.minecraft.src;
public class ItemFoodNameHere extends ItemFood
{
public ItemFoodNameHere(int i, int j, boolean
{
super(i, j, B);
}
public ItemStack onFoodEaten(ItemStack itemStack, World world, EntityPlayer entityPlayer)
{
entityPlayer.getFoodStats().addStats(this);
world.playSoundAtEntity(entityPlayer, "random.burp", 0.5F, world.rand.nextFloat() * 0.1F + 0.9F);
itemStack.stackSize--;
entityPlayer.addPotionEffect(new PotionEffect(Potion.blindness.id, 10 * 20, 6));
entityPlayer.addPotionEffect(new PotionEffect(Potion.nightVision.id, 10 * 20, 6));
return itemStack;
}
}
Understanding the Code
mod_Food
You are basically making a new food(follow the tutorial above) but instead of calling ItemFood you are calling your own class. That class extends ItemFood though.
ItemFoodNameHere
The method onFoodEaten is automatically called when the food is fully eaten.
Its arguments are ItemStack, the World and the Player/EntityPlayer.
entityPlayer.getFoodStats().addStats(this);
This line gets the stats from the public static final line of the food, in the mod_ class, then applies them to the player.
This is the method we use to add potion effects to the player.
This part here is the main part we will be using:
(Potion.blindness.id, 10 * 20, 6)
The first argument of the method is the potion to apply to the player. These can all be found in Potion.java and are found fairly easily. Ensure you put .id after the potion's name to turn it into an int otherwise you will receive errors.
The next argument is the time length that the potion effect lasts for. A 1 here lasts for 1 tick so you multiply it by 20 to make it last one second. Leave the 20 as it is and just change the one to the duration in seconds you want the effect to last for.
The fourth argument is just an amplifier, you can leave it as zero if you want to.
You can add as many as these "add potion effect" lines as you wish to receive your desired outcome.
NPC Information
Specific Biome Spawning
ModLoader.addSpawn(EntityNameHere.class, 2, 1, 4, EnumCreatureType.monster, new BiomeGenBase[]
{
BiomeGenBase.biomesInHere
});
This is a part of ModLoader which allow specific biome spawning of mobs with the addSpawn method.
Additional biomes that are added are separated by a comma. The last biome in between the { } doesn't need a comma.
The names of all biomes can be found in BiomeGenBase.
Holding an Item
To make your mob hold something, simply add this code to its Entity*** class.
public ItemStack getHeldItem()
{
return defaultHeldItem;
}
static
{
defaultHeldItem = new ItemStack(Item.ingotIron, 1);
}
private static final ItemStack defaultHeldItem;
So that it fits in like this:
package net.minecraft.src;
import java.util.Random;
public class EntityNamehere extends EntityMob
{
public EntityNamehere(World world)
{
super(world);
texture = "/image.png";
moveSpeed = 0.5F;
isImmuneToFire = false;
attackStrength = 4; //This line can only be here if the class extends EntityMob above ^^^. If it doesn't, just delete this whole line
}
public int getMaxHealth()
{
return 20;
}
protected String getLivingSound()
{
return "mob.villager.default";
}
protected String getHurtSound()
{
return "mob.villager.defaulthurt";
}
protected String getDeathSound()
{
return "mob.villager.defaultdeath";
}
protected int getDropItemId()
{
return Item.ingotIron.itemID;
}
protected boolean canDespawn()
{
return false;
}
public ItemStack getHeldItem()
{
return defaultHeldItem;
}
static
{
defaultHeldItem = new ItemStack(Item.ingotIron, 1);
}
private static final ItemStack defaultHeldItem;
}
Understanding the Code
This is set up to work in the RenderBiped class. When this code is read, it calls to the render class, which gets the item we define and renders it icon in the mobs hand.
This is quite a large amount of code for this simple thing that we are doing. But, as you can see, it is all linked together.
getHeldItem calls defaultHeldItem
defaultHeldItem calls itself in the static context
In the static context, defaultHeldItem is equal to a new ItemStack of the type Item.ingotIron.
An iron ingot is then rendered in the mobs hand.
Advanced Blocks
Flower
Basic Version
mod_Flower
package net.minecraft.src;
import java.util.Random;
public class mod_Flower extends BaseMod
{
public static final Block nameHere= new BlockFlower(165, 0).setBlockName("anyNameHere");
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"##", "##", Character.valueOf('#'), Block.dirt});
}
public void generateSurface(World world, Random random, int i, int j)
{
for(int k = 0; k < 7; k++)
{
int randPosX = i + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = j + random.nextInt(16);
(new WorldGenFlowers(nameHere.blockID)).generate(world, random, randPosX, randPosY, randPosZ);
}
}
public String getVersion()
{
return "1.4.7";
}
}
Understanding the Code
This is a standard block with one little difference. It extends BlockFlower. BlockFlower has a few different methods in it. The most important ones are these:
public boolean isOpaqueCube()
{
return false;
}
public boolean renderAsNormalBlock()
{
return false;
}
public int getRenderType()
{
return 1;
}
isOpaqueCube allows us to use transparency/see through the block, without seeing down into the rest of the world.
renderAsNormalBlock, quite an obvious one, returning false means that it will not look like a normal block.
public int getRenderType()
{
return 1;
}
This is the most important one for making your block look like a flower. The render type of 1 duplicates your image, and then rotates them to like in an X shape(top view).
The generateSurface method allows for generation of the flower in the world. Look at the ore tutorial for more details.
More Complex Version
mod_Flower
package net.minecraft.src;
import java.util.Random;
public class mod_Flower extends BaseMod
{
public static final Block nameHere= new BlockNamehereFlower(165, 0).setBlockName("anyNameHere");
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"##", "##", Character.valueOf('#'), Block.dirt});
}
public void generateSurface(World world, Random random, int i, int j)
{
for(int k = 0; k < 7; k++)
{
int randPosX = i + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = j + random.nextInt(16);
(new WorldGenFlowers(nameHere.blockID)).generate(world, random, randPosX, randPosY, randPosZ);
}
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNamehereFlower
package net.minecraft.src;
import java.util.Random;
public class BlockNamehereFlower extends Block
{
protected BlockNamehereFlower(int i, int j)
{
super(i, Material.plants);
blockIndexInTexture = j;
setTickRandomly(true);
float f = 0.2F;
setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f * 3F, 0.5F + f);
}
public boolean canPlaceBlockAt(World world, int i, int j, int k)
{
return super.canPlaceBlockAt(world, i, j, k) && canThisPlantGrowOnThisBlockID(world.getBlockId(i, j - 1, k));
}
protected boolean canThisPlantGrowOnThisBlockID(int i)
{
return i == Block.grass.blockID || i == Block.dirt.blockID || i == Block.tilledField.blockID;
}
public void onNeighborBlockChange(World world, int i, int j, int k, int l)
{
super.onNeighborBlockChange(world, i, j, k, l);
checkFlowerChange(world, i, j, k);
}
public void updateTick(World world, int i, int j, int k, Random random)
{
checkFlowerChange(world, i, j, k);
}
protected final void checkFlowerChange(World world, int i, int j, int k)
{
if (!canBlockStay(world, i, j, k))
{
dropBlockAsItem(world, i, j, k, world.getBlockMetadata(i, j, k), 0);
world.setBlockWithNotify(i, j, k, 0);
}
}
public boolean canBlockStay(World world, int i, int j, int k)
{
return (world.getFullBlockLightValue(i, j, k) >= 8 || world.canBlockSeeTheSky(i, j, k)) && canThisPlantGrowOnThisBlockID(world.getBlockId(i, j - 1, k));
}
public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int i, int j, int k)
{
return null;
}
public boolean isOpaqueCube()
{
return false;
}
public boolean renderAsNormalBlock()
{
return false;
}
public int getRenderType()
{
return 1;
}
}
Understanding the Code
This will have the same appearance as the less complex version above, but gives you a few more options on what you can change. I won't be explaining everything in here, but I will try to explain the most important things you might want to change.
setBlockBounds is basically the area that the black line is drawn around when looking at a block. Each of those floats do something different, you'll just have to fiddle with them until you figure out what each one does.
canPlaceBlockAt and canThisPlantGrowOnThisBlockID just state which blocks you can put your new block on.
The isOpaqueCube, renderAsNormalBlock and getRenderType methods are all explained above.
Transparent Block
mod_Block
package net.minecraft.src;
public class mod_Block extends BaseMod
{
public static final Block nameHere= new BlockName(160, 0).setBlockName("anyNameHere").setHardness(3F).setResistance(4F).setLightValue(1F);
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNameHere
package net.minecraft.src;
public class BlockNameHere extends Block
{
public BlockNameHere(int i, int j)
{
super(i, j, Material.wood);
}
public boolean isOpaqueCube()
{
return false;
}
}
Understanding the Code
The mod_ class is exactly the same as normal. No changes whatsoever. The only difference here is that the 'isOpaqueCube' method is added to the block class. This is the method that makes your block transparent, without you being able to see down into the rest of the world. This needs to return false to be able to work. If it returns true, you'll still see be able to see through into the world.
Stairs
mod_Stairs
package net.minecraft.src;
public class mod_Stairs extends BaseMod
{
public static final Block nameHere= new BlockNameHereStairs(170, Block.leaves, 0).setBlockName("stairsNameHere").setHardness(3F).setResistance(4F);
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/imageblock.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"# ", "## ", "###", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNamehereStairs
package net.minecraft.src;
import java.util.List;
import java.util.Random;
public class BlockNameHereStairs extends Block
{
private static final int[][] field_72159_a = new int[][] {{2, 6}, {3, 7}, {2, 3}, {6, 7}, {0, 4}, {1, 5}, {0, 1}, {4, 5}};
private static final int[] field_82545_b = new int[] {1, -1, 0, 0};
private static final int[] field_82546_c = new int[] {0, 0, 1, -1};
/** The block that is used as model for the stair. */
private final Block modelBlock;
private final int field_72158_c;
private boolean field_72156_cr = false;
private int field_72160_cs = 0;
protected BlockNameHereStairs(int par1, Block par2Block, int par3)
{
super(par1, par2Block.blockIndexInTexture, par2Block.blockMaterial);
this.modelBlock = par2Block;
this.field_72158_c = par3;
this.setHardness(par2Block.blockHardness);
this.setResistance(par2Block.blockResistance / 3.0F);
this.setStepSound(par2Block.stepSound);
this.setCreativeTab(CreativeTabs.tabBlock);
}
/**
* Updates the blocks bounds based on its current state. Args: world, x, y, z
*/
public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
if (this.field_72156_cr)
{
this.setBlockBounds(0.5F * (float)(this.field_72160_cs % 2), 0.5F * (float)(this.field_72160_cs / 2 % 2), 0.5F * (float)(this.field_72160_cs / 4 % 2), 0.5F + 0.5F * (float)(this.field_72160_cs % 2), 0.5F + 0.5F * (float)(this.field_72160_cs / 2 % 2), 0.5F + 0.5F * (float)(this.field_72160_cs / 4 % 2));
}
else
{
this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
}
}
/**
* Is this block (a) opaque and (B) a full 1m cube? This determines whether or not to render the shared face of two
* adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
*/
public boolean isOpaqueCube()
{
return false;
}
/**
* If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
*/
public boolean renderAsNormalBlock()
{
return false;
}
/**
* The type of render function that is called for this block
*/
public int getRenderType()
{
return 10;
}
public void func_82541_d(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
int var5 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
if ((var5 & 4) != 0)
{
this.setBlockBounds(0.0F, 0.5F, 0.0F, 1.0F, 1.0F, 1.0F);
}
else
{
this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.5F, 1.0F);
}
}
public static boolean func_82543_e(int par0)
{
return par0 > 0 && Block.blocksList[par0] instanceof BlockNameHereStairs;
}
private boolean func_82540_f(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
{
int var6 = par1IBlockAccess.getBlockId(par2, par3, par4);
return func_82543_e(var6) && par1IBlockAccess.getBlockMetadata(par2, par3, par4) == par5;
}
public boolean func_82542_g(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
int var5 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
int var6 = var5 & 3;
float var7 = 0.5F;
float var8 = 1.0F;
if ((var5 & 4) != 0)
{
var7 = 0.0F;
var8 = 0.5F;
}
float var9 = 0.0F;
float var10 = 1.0F;
float var11 = 0.0F;
float var12 = 0.5F;
boolean var13 = true;
int var14;
int var15;
int var16;
if (var6 == 0)
{
var9 = 0.5F;
var12 = 1.0F;
var14 = par1IBlockAccess.getBlockId(par2 + 1, par3, par4);
var15 = par1IBlockAccess.getBlockMetadata(par2 + 1, par3, par4);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 3 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 + 1, var5))
{
var12 = 0.5F;
var13 = false;
}
else if (var16 == 2 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 - 1, var5))
{
var11 = 0.5F;
var13 = false;
}
}
}
else if (var6 == 1)
{
var10 = 0.5F;
var12 = 1.0F;
var14 = par1IBlockAccess.getBlockId(par2 - 1, par3, par4);
var15 = par1IBlockAccess.getBlockMetadata(par2 - 1, par3, par4);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 3 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 + 1, var5))
{
var12 = 0.5F;
var13 = false;
}
else if (var16 == 2 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 - 1, var5))
{
var11 = 0.5F;
var13 = false;
}
}
}
else if (var6 == 2)
{
var11 = 0.5F;
var12 = 1.0F;
var14 = par1IBlockAccess.getBlockId(par2, par3, par4 + 1);
var15 = par1IBlockAccess.getBlockMetadata(par2, par3, par4 + 1);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 1 && !this.func_82540_f(par1IBlockAccess, par2 + 1, par3, par4, var5))
{
var10 = 0.5F;
var13 = false;
}
else if (var16 == 0 && !this.func_82540_f(par1IBlockAccess, par2 - 1, par3, par4, var5))
{
var9 = 0.5F;
var13 = false;
}
}
}
else if (var6 == 3)
{
var14 = par1IBlockAccess.getBlockId(par2, par3, par4 - 1);
var15 = par1IBlockAccess.getBlockMetadata(par2, par3, par4 - 1);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 1 && !this.func_82540_f(par1IBlockAccess, par2 + 1, par3, par4, var5))
{
var10 = 0.5F;
var13 = false;
}
else if (var16 == 0 && !this.func_82540_f(par1IBlockAccess, par2 - 1, par3, par4, var5))
{
var9 = 0.5F;
var13 = false;
}
}
}
this.setBlockBounds(var9, var7, var11, var10, var8, var12);
return var13;
}
public boolean func_82544_h(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
int var5 = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
int var6 = var5 & 3;
float var7 = 0.5F;
float var8 = 1.0F;
if ((var5 & 4) != 0)
{
var7 = 0.0F;
var8 = 0.5F;
}
float var9 = 0.0F;
float var10 = 0.5F;
float var11 = 0.5F;
float var12 = 1.0F;
boolean var13 = false;
int var14;
int var15;
int var16;
if (var6 == 0)
{
var14 = par1IBlockAccess.getBlockId(par2 - 1, par3, par4);
var15 = par1IBlockAccess.getBlockMetadata(par2 - 1, par3, par4);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 3 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 - 1, var5))
{
var11 = 0.0F;
var12 = 0.5F;
var13 = true;
}
else if (var16 == 2 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 + 1, var5))
{
var11 = 0.5F;
var12 = 1.0F;
var13 = true;
}
}
}
else if (var6 == 1)
{
var14 = par1IBlockAccess.getBlockId(par2 + 1, par3, par4);
var15 = par1IBlockAccess.getBlockMetadata(par2 + 1, par3, par4);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var9 = 0.5F;
var10 = 1.0F;
var16 = var15 & 3;
if (var16 == 3 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 - 1, var5))
{
var11 = 0.0F;
var12 = 0.5F;
var13 = true;
}
else if (var16 == 2 && !this.func_82540_f(par1IBlockAccess, par2, par3, par4 + 1, var5))
{
var11 = 0.5F;
var12 = 1.0F;
var13 = true;
}
}
}
else if (var6 == 2)
{
var14 = par1IBlockAccess.getBlockId(par2, par3, par4 - 1);
var15 = par1IBlockAccess.getBlockMetadata(par2, par3, par4 - 1);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var11 = 0.0F;
var12 = 0.5F;
var16 = var15 & 3;
if (var16 == 1 && !this.func_82540_f(par1IBlockAccess, par2 - 1, par3, par4, var5))
{
var13 = true;
}
else if (var16 == 0 && !this.func_82540_f(par1IBlockAccess, par2 + 1, par3, par4, var5))
{
var9 = 0.5F;
var10 = 1.0F;
var13 = true;
}
}
}
else if (var6 == 3)
{
var14 = par1IBlockAccess.getBlockId(par2, par3, par4 + 1);
var15 = par1IBlockAccess.getBlockMetadata(par2, par3, par4 + 1);
if (func_82543_e(var14) && (var5 & 4) == (var15 & 4))
{
var16 = var15 & 3;
if (var16 == 1 && !this.func_82540_f(par1IBlockAccess, par2 - 1, par3, par4, var5))
{
var13 = true;
}
else if (var16 == 0 && !this.func_82540_f(par1IBlockAccess, par2 + 1, par3, par4, var5))
{
var9 = 0.5F;
var10 = 1.0F;
var13 = true;
}
}
}
if (var13)
{
this.setBlockBounds(var9, var7, var11, var10, var8, var12);
}
return var13;
}
/**
* if the specified block is in the given AABB, add its collision bounding box to the given list
*/
public void addCollidingBlockToList(World par1World, int par2, int par3, int par4, AxisAlignedBB par5AxisAlignedBB, List par6List, Entity par7Entity)
{
this.func_82541_d(par1World, par2, par3, par4);
super.addCollidingBlockToList(par1World, par2, par3, par4, par5AxisAlignedBB, par6List, par7Entity);
boolean var8 = this.func_82542_g(par1World, par2, par3, par4);
super.addCollidingBlockToList(par1World, par2, par3, par4, par5AxisAlignedBB, par6List, par7Entity);
if (var8 && this.func_82544_h(par1World, par2, par3, par4))
{
super.addCollidingBlockToList(par1World, par2, par3, par4, par5AxisAlignedBB, par6List, par7Entity);
}
this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
}
/**
* A randomly called display update to be able to add particles or other items for display
*/
public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
this.modelBlock.randomDisplayTick(par1World, par2, par3, par4, par5Random);
}
/**
* Called when the block is clicked by a player. Args: x, y, z, entityPlayer
*/
public void onBlockClicked(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer)
{
this.modelBlock.onBlockClicked(par1World, par2, par3, par4, par5EntityPlayer);
}
/**
* Called right before the block is destroyed by a player. Args: world, x, y, z, metaData
*/
public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5)
{
this.modelBlock.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5);
}
/**
* Goes straight to getLightBrightnessForSkyBlocks for Blocks, does some fancy computing for Fluids
*/
public int getMixedBrightnessForBlock(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
return this.modelBlock.getMixedBrightnessForBlock(par1IBlockAccess, par2, par3, par4);
}
/**
* How bright to render this block based on the light its receiving. Args: iBlockAccess, x, y, z
*/
public float getBlockBrightness(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
return this.modelBlock.getBlockBrightness(par1IBlockAccess, par2, par3, par4);
}
/**
* Returns how much this block can resist explosions from the passed in entity.
*/
public float getExplosionResistance(Entity par1Entity)
{
return this.modelBlock.getExplosionResistance(par1Entity);
}
/**
* Returns which pass should this block be rendered on. 0 for solids and 1 for alpha
*/
public int getRenderBlockPass()
{
return this.modelBlock.getRenderBlockPass();
}
/**
* From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
*/
public int getBlockTextureFromSideAndMetadata(int par1, int par2)
{
return this.modelBlock.getBlockTextureFromSideAndMetadata(par1, this.field_72158_c);
}
/**
* Returns the block texture based on the side being looked at. Args: side
*/
public int getBlockTextureFromSide(int par1)
{
return this.modelBlock.getBlockTextureFromSideAndMetadata(par1, this.field_72158_c);
}
/**
* How many world ticks before ticking
*/
public int tickRate()
{
return this.modelBlock.tickRate();
}
/**
* Returns the bounding box of the wired rectangular prism to render.
*/
public AxisAlignedBB getSelectedBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
{
return this.modelBlock.getSelectedBoundingBoxFromPool(par1World, par2, par3, par4);
}
/**
* Can add to the passed in vector for a movement vector to be applied to the entity. Args: x, y, z, entity, vec3d
*/
public void velocityToAddToEntity(World par1World, int par2, int par3, int par4, Entity par5Entity, Vec3 par6Vec3)
{
this.modelBlock.velocityToAddToEntity(par1World, par2, par3, par4, par5Entity, par6Vec3);
}
/**
* Returns if this block is collidable (only used by Fire). Args: x, y, z
*/
public boolean isCollidable()
{
return this.modelBlock.isCollidable();
}
/**
* Returns whether this block is collideable based on the arguments passed in Args: blockMetaData, unknownFlag
*/
public boolean canCollideCheck(int par1, boolean par2)
{
return this.modelBlock.canCollideCheck(par1, par2);
}
/**
* Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
*/
public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
{
return this.modelBlock.canPlaceBlockAt(par1World, par2, par3, par4);
}
/**
* Called whenever the block is added into the world. Args: world, x, y, z
*/
public void onBlockAdded(World par1World, int par2, int par3, int par4)
{
this.onNeighborBlockChange(par1World, par2, par3, par4, 0);
this.modelBlock.onBlockAdded(par1World, par2, par3, par4);
}
/**
* ejects contained items into the world, and notifies neighbours of an update, as appropriate
*/
public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6)
{
this.modelBlock.breakBlock(par1World, par2, par3, par4, par5, par6);
}
/**
* Called whenever an entity is walking on top of this block. Args: world, x, y, z, entity
*/
public void onEntityWalking(World par1World, int par2, int par3, int par4, Entity par5Entity)
{
this.modelBlock.onEntityWalking(par1World, par2, par3, par4, par5Entity);
}
/**
* Ticks the block if it's been scheduled
*/
public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
this.modelBlock.updateTick(par1World, par2, par3, par4, par5Random);
}
/**
* Called upon block activation (right click on the block.)
*/
public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
{
return this.modelBlock.onBlockActivated(par1World, par2, par3, par4, par5EntityPlayer, 0, 0.0F, 0.0F, 0.0F);
}
/**
* Called upon the block being destroyed by an explosion
*/
public void onBlockDestroyedByExplosion(World par1World, int par2, int par3, int par4)
{
this.modelBlock.onBlockDestroyedByExplosion(par1World, par2, par3, par4);
}
/**
* Called when the block is placed in the world.
*/
public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)
{
int var6 = MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
int var7 = par1World.getBlockMetadata(par2, par3, par4) & 4;
if (var6 == 0)
{
par1World.setBlockMetadataWithNotify(par2, par3, par4, 2 | var7);
}
if (var6 == 1)
{
par1World.setBlockMetadataWithNotify(par2, par3, par4, 1 | var7);
}
if (var6 == 2)
{
par1World.setBlockMetadataWithNotify(par2, par3, par4, 3 | var7);
}
if (var6 == 3)
{
par1World.setBlockMetadataWithNotify(par2, par3, par4, 0 | var7);
}
}
public int func_85104_a(World par1World, int par2, int par3, int par4, int par5, float par6, float par7, float par8, int par9)
{
return par5 != 0 && (par5 == 1 || (double)par7 <= 0.5D) ? par9 : par9 | 4;
}
/**
* Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. Args: world,
* x, y, z, startVec, endVec
*/
public MovingObjectPosition collisionRayTrace(World par1World, int par2, int par3, int par4, Vec3 par5Vec3, Vec3 par6Vec3)
{
MovingObjectPosition[] var7 = new MovingObjectPosition[8];
int var8 = par1World.getBlockMetadata(par2, par3, par4);
int var9 = var8 & 3;
boolean var10 = (var8 & 4) == 4;
int[] var11 = field_72159_a[var9 + (var10 ? 4 : 0)];
this.field_72156_cr = true;
int var14;
int var15;
int var16;
for (int var12 = 0; var12 < 8; ++var12)
{
this.field_72160_cs = var12;
int[] var13 = var11;
var14 = var11.length;
for (var15 = 0; var15 < var14; ++var15)
{
var16 = var13[var15];
if (var16 == var12)
{
;
}
}
var7[var12] = super.collisionRayTrace(par1World, par2, par3, par4, par5Vec3, par6Vec3);
}
int[] var21 = var11;
int var24 = var11.length;
for (var14 = 0; var14 < var24; ++var14)
{
var15 = var21[var14];
var7[var15] = null;
}
MovingObjectPosition var23 = null;
double var22 = 0.0D;
MovingObjectPosition[] var25 = var7;
var16 = var7.length;
for (int var17 = 0; var17 < var16; ++var17)
{
MovingObjectPosition var18 = var25[var17];
if (var18 != null)
{
double var19 = var18.hitVec.squareDistanceTo(par6Vec3);
if (var19 > var22)
{
var23 = var18;
var22 = var19;
}
}
}
return var23;
}
}
Understanding the Code
mod_Stairs
The only change here is that the second argument is a Block and the texture argument is now the third. Set the Block argument to what ever block you want the stairs to get their attributes(texture and material) from. Just leave the texture argument as 0.
BlockNamehereStairs
The addCollidingBlockToList method adds the collision box for the block. The 'else if' statements change the collision box depending on the direction that the block is facing.
All the 'if' statements in the onBlockPlacedBy method just make the block face the player's direction when placed.
The isOpaqueCube, getRenderType, renderAsNormalBlock and the other methods in the class are well explained in Javadoc comments in the code.
Half-Slab
mod_Slab
package net.minecraft.src;
public class mod_Slab extends BaseMod
{
public static final Block nameHereSingleSlab = new BlockNameHereSlab(250, false).setBlockName("anyNameHereSlabSingle").setHardness(3F).setResistance(4F);
public static final Block nameHereDoubleSlab = new BlockNameHereSlab(251, true).setBlockName("anyNameHereSlabDouble").setHardness(3F).setResistance(4F);
public void load()
{
nameHereSingleSlab.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/imageslab2.png");
ModLoader.registerBlock(nameHereSingleSlab);
ModLoader.addName(nameHereSingleSlab, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHereSingleSlab, 1), new Object [] {"#", Character.valueOf('#'), Block.cobblestone});
nameHereDoubleSlab.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/imageslab.png");
ModLoader.registerBlock(nameHereDoubleSlab);
ModLoader.addName(nameHereDoubleSlab, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHereDoubleSlab, 1), new Object [] {"#", Character.valueOf('#'), Block.cobblestone});
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNameHereSlab
package net.minecraft.src;
import java.util.List;
import java.util.Random;
public class BlockNameHereSlab extends BlockHalfSlab
{
/** The list of the types of step blocks. */
public BlockNameHereSlab(int par1, boolean par2)
{
super(par1, par2, Material.wood);
this.setCreativeTab(CreativeTabs.tabBlock);
setLightOpacity(0);
}
/**
* Returns the block texture based on the side being looked at. Args: side
*/
public int getBlockTextureFromSide(int par1)
{
return blockIndexInTexture;
}
/**
* Returns the ID of the items to drop on destruction.
*/
public int idDropped(int par1, Random par2Random, int par3)
{
return mod_Slab.nameHereSingleSlab.blockID;
}
public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)
{
if(par1World.getBlockId(par2, par3 - 1, par4) == mod_Slab.nameHereSingleSlab.blockID)
{
par1World.setBlockWithNotify(par2, par3, par4, 0);
par1World.setBlockWithNotify(par2, par3 - 1, par4, mod_Slab.nameHereDoubleSlab.blockID);
}
}
/**
* Returns an item stack containing a single instance of the current block type. 'par1' is the block's subtype/damage
* and is ignored for blocks which do not support subtypes. Blocks which cannot be harvested should return null.
*/
protected ItemStack createStackedBlock(int par1)
{
return new ItemStack(mod_Slab.nameHereSingleSlab.blockID, 2, par1 & 7);
}
public String getFullSlabName(int var1)
{
return null;
}
}
Understanding the Code
In this tutorial, we are actually adding two blocks. One is a single slab. One is the double version of that slab.
There are two parameters we need to have for the slab blocks. The first, as always, is the id of the block. The second is a boolean instead of an int. This boolean is whether or not the block is a double slab. Obviously we make it false here for only a single slab and true here for a double slab.
The block class extends BlockHalfSlab. This does most of the work for us in setting the block bounds and other similar things.
We set the light opacity to 0 to stop most lighting bugs.
Even though the getBlockTextureFromSide method is unused you need to leave it in the code otherwise you will get Stack Traces.
The idDropped method is exactly the same as is used in normal blocks.
The onBlockPlacedBy method is something I have added myself. It allows slabs to be stacked like Vanilla slabs. It is a bit of a shortcut but it works pretty much flawlessly.
The onBlockPlacedBy method has 5 parameters. They are the World; par2, par3 and par4 are all the X, Y, Z coordinates in the world, respectively. The final parameter is EntityLiving. It is the entity that places the block. We don't use it at all in what we're doing.
The if statement checks if the block below where we are placing is equal to a single slab. If it is true then it sets the block that was just placed to air and sets the single slab below to a double slab block. Take a look at the world generation tutorials for more details.
The createStackedBlock method is explained in the Javadoc comment in the code.
The getFullSlabName method is used in the Vanilla slabs to give them the correct name. We aren't using it in this as we don't have any metadata subtypes.
Achievement
mod_Achievement
package net.minecraft.src;
public class mod_Achievement extends BaseMod
{
public static final Achievement nameHere = new Achievement(4560, "nameHere", 5, 7, Item.diamond, null).registerAchievement();
public void load()
{
ModLoader.addAchievementDesc(nameHere, "Achievement Name", "Achievement Sub-Name");
}
public void takenFromCrafting(EntityPlayer entityplayer, ItemStack itemstack, IInventory iinventory)
{
if(itemstack.itemID == Block.fence.blockID)
{
entityplayer.addStat(nameHere, 1);
}
}
public String getVersion()
{
return "1.4.7";
}
}
Understanding the Code
mod_Achievement
public static final Achievement nameHere = new Achievement(4560, "nameHere", 5, 7, Item.diamond, null).registerAchievement();
This initiates the achievement. 'nameHere' is just the name of the achievement you will use in the rest of the class just like you would with an item.
4560 is a unique id for the achievement, no other achievement can have it.
The nameHere in quotes after 4560 should be the same as the first name you put before = new Achievement.
5 and 7 are the position of the achievement icon in the achievement window. You will just have to play with this until you find the right position.
Item.diamond defines what icon will be used for the achievement. If you want to use your own icon, you'll need to create an item with that icon.
null is where an achievement name is meant to be, but just leave it as null. See the Offspring section below for actually using this part.
.registerAchievement() is to register it so it is actually used by the game.
This is used if you want to acquire the achievement by picking something up off the crafting grid.
''if(itemstack.itemID == Block.fence.blockID)'' means that it will continue through the code if you pick up a fence from the results slot after crafting it from sticks
"entityplayer.addStat(nameHere, 1);" means to give you the achievement called ''nameHere''
- Simply put, all this statement means is to give you the achievement called "nameHere" when you craft a fence block.
I will be adding more ways to get the achievement as time goes on. Check back often for updates.
Making an Achievement Offspring of Another
This is just like where you need to have the "Acquire Hardware" achievement to get the "DIAMONDS!" achievement. To do this, you simply add this line of code:
static Achievement name = AchievementList.diamonds;
and change the "null" in the public static final line to the name you gave it before the "=" sign in the static line above. In this case it would be "name".
public static Achievement new Achievement = new Achievement(6130, "Diamond Hunter", -1, 3, Block.blockDiamond, name).registerAchievement();
You change the "AchievementList.diamonds" to any other achievement in AchievementList.java. For example, you might want your achievement to need to have the "Getting Wood" achievement, so your static line of code would look like this:
static Achievement name = AchievementList.mineWood;
You just use the name of the achievement from AchievementList.java in that space.
Below is an example of needing to have the "DIAMONDS!" achievement, to be able to get a new modded achievement, called "Diamond Hunter" which you receive from crafting a diamond block.
package net.minecraft.src;
public class mod_Achievement extends BaseMod
{
static Achievement diamonds = AchievementList.diamonds;
public static Achievement diamondBlockAchievement = new Achievement(6130, "Diamond Hunter", -1, 3, Block.blockDiamond, diamonds).registerAchievement();
public void load()
{
ModLoader.addAchievementDesc(diamondBlockAchievement , "Diamond Hunter", "Craft a Diamond Block");
}
public void takenFromCrafting(EntityPlayer entityplayer, ItemStack itemstack, IInventory iinventory)
{
if(itemstack.itemID == Block.blockDiamond.blockID)
{
entityplayer.addStat(diamondBlockAchievement , 1);
}
}
public String getVersion()
{
return "1.4.7";
}
}
Here is a picture of that in-game:
After achievements required are completed
Thanks to 61352151511 for letting me build off his base diamond block achievement code, so I could make a good example for how to use offspring.
Making an Achievement "Special"
This is what changes the achievement from looking like a standard square achievement in the achievement GUI, to looking like the "Overkill". This is very, very simple to do. All you do is add this code to the public static final line:
.setSpecial()
so that it fits in like this:
public static Achievement diamondBlockAchievement = new Achievement(6130, "Diamond Hunter", -1, 3, Block.blockDiamond, diamonds).setSpecial().registerAchievement();
Using the example from above, it will now look like this:
Multi-Textured Blocks
I'm using the block tutorial for this, and adding some code for the textures.
mod_Block
package net.minecraft.src;
public class mod_Block extends BaseMod
{
public static final Block nameHere= new BlockNameHere(160, 0).setBlockName("anyNameHere").setHardness(3F).setResistance(4F).setLightValue(1F);
public static int nameHereBottom = ModLoader.addOverride("/terrain.png", "/blockbottom.png");
public static int nameHereTop = ModLoader.addOverride("/terrain.png", "/blocktop.png");
public static int nameHereSides = ModLoader.addOverride("/terrain.png", "/blocksides.png");
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
BlockNameHere
package net.minecraft.src;
public class BlockNameHere extends Block
{
public BlockNameHere(int i, int j)
{
super(i, j, Material.wood);
}
public int getBlockTextureFromSideAndMetadata(int i, int j)
{
return getBlockTextureFromSide(i);
}
public int getBlockTextureFromSide(int i)
{
if (i == 0)
{
return mod_Block.nameHereBottom;
}
if (i == 1)
{
return mod_Block.nameHereTop;
}
else
{
return mod_Block.nameHereSides;
}
}
}
Understanding the Code
mod_Block
The only thing that is added to this class are the public static ints that contain our texture override information
public static int NamehereBottom = ModLoader.addOverride("/terrain.png", "/blockbottom.png");
public static int NamehereTop = ModLoader.addOverride("/terrain.png", "/blocktop.png");
public static int NamehereSides = ModLoader.addOverride("/terrain.png", "/blocksides.png");
Using an int like this allows us to use it in another class.
BlockNameHere
This is what we need to add to allow us to choose our textures for each side of the block:
public int getBlockTextureFromSideAndMetadata(int i, int j)
{
return getBlockTextureFromSide(i);
}
public int getBlockTextureFromSide(int i)
{
if (i == 0)
{
return mod_Block.NamehereBottom;
}
if (i == 1)
{
return mod_Block.NamehereTop;
}
else
{
return mod_Block.NamehereSides;
}
}
In the getBlockTextureFromSide method, there are if statements saying things like "if (i == 0)" and "if (i == 1)", etc. The i 's mean the different faces of the block. For example, side 0 is the bottom of the block, side 1 is the top of the block, and sides 2, 3, 4 and 5 are the sides of the block.
Where it has "return mod_Block.NamehereBottom;", this is the class that the texture int is in, and its name. For the bottom of the block, we made a public static int in mod_Block, and its name was NamehereBottom. So the game then gets the texture from that int, and applies it to that face of the block.
In this example, I have shown you how to use a different texture for the top, bottom of the block, and the same one for all the sides of it.
Fuels
This tutorial will build off the item tutorial.
mod_Fuels
package net.minecraft.src;
public class mod_Fuels extends BaseMod
{
public static final Item nameHere = new Item(5000).setItemName("anyNameHere");
public void load()
{
nameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/image.png");
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", '#', Block.dirt.blockID});
}
public int addFuel(int i, int j)
{
if(i == nameHere.itemID)
{
return 2750;
}
return 0;
}
public String getVersion()
{
return "1.4.7";
}
}
Understanding the Code
Adding a fuel is fairly simple and self-explanatory. The only extra code you need to use is the addFuel method.
addFuel is a ModLoader method inherited from BaseMod.
The name in the in the if statement is just the fuel you want to add.
The int returned after it is the amount of ticks that the item fuels the furnace for. To refer to it more simply, you could call it the "smelting level" of the item.
A return of 200 here will smelt one item. More simply, a full "smelting cycle".
For reference, sticks are 100 and coal is 1600.
Adding Blocks/Items to Creative Inventory
Adding blocks/items to the creative inventory in 1.4.7 is very simple. All you need to do is add in a piece of code:
.setCreativeTab(CreativeTabs.tabNameHere);
It goes on the end of the public static final line of your block or item.
public static final Block nameHere = new BlockNameHere(160, 0).setBlockName("anynamehere").setHardness(3F).setResistance(4F).setLightValue(1F).setCreativeTab(CreativeTabs.tabNameHere);
You can also put it in your block's class:
package net.minecraft.src;
import java.util.Random;
public class BlockNameHere extends Block
{
public BlockNameHere(int i, int j)
{
super(i, j, Material.wood);
setCreativeTab(CreativeTabs.tabNameHere); // Place it inside the constructor here, after the super call.
}
public int idDropped(int i, Random random, int j)
{
return mod_Block.nameHere.blockID;
}
public int quantityDropped(Random random)
{
return 1;
}
}
Or Item's class:
package net.minecraft.src;
import java.util.Random;
public class ItemNameHere extends Item
{
public BlockNameHere(int i)
{
super(i;
setCreativeTab(CreativeTabs.tabNameHere); // Place it inside the constructor here, after the super call.
}
}
You need to replace tabNameHere with the name of the tab you want the block to display on, not the name of your block.
The building block tab is:
CreativeTabs.tabBlock
The decorations tab is:
CreativeTabs.tabDecorations
The redstone tab is:
CreativeTabs.tabRedstone
The transport tab is:
CreativeTabs.tabTransport
These are just a few of the tabs. All of the rest of the tabs can be found in the CreativeTabs.java file.
1.2.5
Read the following before spamming something like: "There is an easier way to do this, just add your block to ContainerCreative":
That is stupid and lazy. When you're modding, you should always aim not to edit ANY base class. If every mod was to edit ContainerCreative, only the mod that was last added would have its blocks added because the file would just get overridden each time. If you want to be an idiot, go right ahead and edit ContainerCreative, but don't leave a reply here saying something like "I know an easier way, add to ContainerCreative!". Leaving a reply like that just shows everyone that you have nothing better to do. I have an idea for you, go learn how to do stuff properly!
If you are smart and would like to add blocks to the creative inventory the proper way, keep reading.
This will just be a snippet of code(with an explanation, of course). It won't include any parts from the block tutorial. It will have a very simple explanation.
All of the code below goes into your mod_ class.
As basically as I can put it, the code above checks if the creative inventory gui is open, then adds the blocks that you declare, into the inventory so you can get them.
list.add(new ItemStack(block1, 1, i));
The line above is what actually adds your block to the inventory after all the checks are done. You just replace block1, block2, block3, etc. with the name of your block which you declared in its public static final line.
Please ask if you need more of an explanation.
Multiple Things in One mod_ Class
package net.minecraft.src;
public class mod_Block extends BaseMod
{
public static final Block greenBlock = new BlockGreen(160, 0).setBlockName("green").setHardness(3F).setResistance(4F).setLightValue(1F);
public static final Block blueBlock = new BlockBlue(161, 0).setBlockName("blue").setHardness(3F).setResistance(4F).setLightValue(1F);
public void load()
{
greenBlock.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image.png");
ModLoader.registerBlock(greenBlock);
ModLoader.addName(greenBlock, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(greenBlock, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
blueBlock.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/image2.png");
ModLoader.registerBlock(blueBlock);
ModLoader.addName(blueBlock, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(blueBlock, 1), new Object [] {"##", Character.valueOf('#'), Block.dirt});
}
public String getVersion()
{
return "1.4.7";
}
}
A few people were having trouble with knowing how to add more than one item/block/etc. to a single mod_ class so I made this to show how to do it. This isn't so much of a tutorial, it is more made as a display on how to do what I wrote above.
Completing the Mod
This section will explain how to finish your mod and allow you to distribute it in a playable form.
After you have completed the coding of your mod, run recompile.bat/sh.
Once it has compiled, close the window and open reobfuscate.bat/sh. This will reobfuscate all the classes of the game you have modified in some way. It will also reobfuscate new classes you have created. All files being obfuscated will be printed to the console as it goes.
Once all files have been obfuscated into .class files, you can get them from mcpfolder/reobf/minecraft.
Get the classes and put them into a zip archive. Also put your images (if necessary) into it. Remember that if you use a path for your image like the one below that you put the image called "magic" in a subfolder called "blocks" in a folder called "mymod". The "mymod" folder goes into the zip.
"/mymod/blocks/magic.png"
You can then distribute the mod to the public. Just upload the zip onto a website like MediaFire or put it in the public folder of your Dropbox. Then make a thread on the MinecraftForums Mods section detailing all your mod. Don't forget to put the link to the download there Make sure to follow the rules of the section when posting.
More tutorials on the post below! All new ones will be there too.
Tips & Info
.blockID is only used for blocks.
.itemID is used for items only.
If the game crashes when starting the MCP client and says it can't find your image, check your images location, its name and where you have told minecraft to look for it, in/from your code.
Only use one mod_ class for your whole mod.
Don't use the Java contructor in your mod_ class. Use the load method instead.
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
[center][color=#0000ff][b][size=large]More Old ModLoader Tutorials[/size][/b][/color][/center]
[center][spoiler][/center]
[center][b][color=#ff0000]I no longer support these tutorials.[/color][/b][/center]
[center][b]Ore Generation[/b][/center]
[spoiler]
This tutorial will build off the block tutorial.
[center][i]mod_OreGeneration[/i][/center]
[code]package net.minecraft.src;
import java.util.Random;
public class mod_OreGeneration extends BaseMod
{
public static final Block nameHere = new Block(160, 0, Material.rock).setBlockName("anyNameHere").setHardness(3F).setResistance(4F);
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/imageblock.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
for(int i = 0; i < 7; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
}
public String getVersion()
{
return "1.4.2;
}
}[/code]
[center][font=Impact]Understanding the Code[/font][/center]
As you'll notice, the generateSurface method has been added. All the information about this is as follows:[list]
[*]The 7 in the for loop is the rarity of your block when generated.
[*]The following three ints, as it says in the code, are the random positions that the blocks may be generated.
[*]In the randPosX and randPosZ lines, chunkX and chunkZ signify the edge of the chunk on those axes. The random.nextInt(16) parts means that any number up to 16 can be chosen. The number is chosen for how far in from the side of a chunk the block can be generated. Because a chunk is 16x16, having 16 in the brackets allows for the block to be generated anywhere in the chunk.
[*]The randPosY line is pretty much the same thing, except it goes up the vertical axis. Therefore, the number in the brackets is the height the block can be generated up to.
[*]The last line uses all the information your provide it with, in the random position ints.
[*]You put the block you want to generate in the brackets after "WorldGenMinable"
[*]The 25 inside the brackets is the size of the vein.
[*]The generate method in WorldGenMinable requires 5 arguments. Those arguments are World(The world of the game), Random(A Java class, read the info about it at the link at the bottom of the OP), randPosX, randPosY and randPosZ. That is where the information from the ints is actually called and used.
[/list]
[b]Note: Everytime that you change anything about the generation of your block in the generateSurface method in mod_***, you need to create a new world to see it.[/b]
[/spoiler]
[code]package net.minecraft.src;
import java.util.Random;
public class mod_WorldGen extends BaseMod
{
public void load()
{
}
public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
for(int k = 0; k < 10; k++)
{
int RandPosX = chunkX + random.nextInt(5);
int RandPosY = random.nextInt(80);
int RandPosZ = chunkZ + random.nextInt(5);
(new WorldGenNameHere()).generate(world, random, RandPosX, RandPosY, RandPosZ);
}
}
public String getVersion()
{
return "1.4.2";
}
}[/code]
[center][i]WorldGenNamehere[/i][/center]
[code]package net.minecraft.src;
import java.util.Random;
public class WorldGenNameHere extends WorldGenerator
{
public WorldGenNameHere()
{
}
public boolean generate(World world, Random random, int i, int j, int k)
{
int stoneBrick = Block.stoneBrick.blockID;
world.setBlockWithNotify(i, j, k, stoneBrick);
world.setBlockAndMetadataWithNotify(i, j + 1, k, stoneBrick, 1);
return true;
}
}[/code]
[center][font=Impact]Understanding the Code[/font][/center]
[center][i]mod_WorldGen[/i][/center][list]
[*]This tutorial is fairly similar to the ore generation to the tutorial. The main changes are in the mod_ class.
[*]Just like the ore generation tutorial, Random is imported into the class.
[*]There is a slight change inside the generateSurface method.
[*]The line beginning with 'for' is a 'for loop'. The 10 in that line is the rarity of the structure.
[*]'RandPosX' and 'RandPosZ' mean to start the structure at any random position on that axis at least 5(or whatever the number is in the brackets) blocks away from the edge of the chunk. [u](Not 100% sure about this, I think it has more to do with the density of generation)[/u]
[/list][list]
[*]'RandPosY' means to place the structure anywhere on the y axis up to the layer in the brackets. (80 in this case)
[*][code](new WorldGenNamehere()).generate(world, random, RandPosX, RandPosY, RandPosZ);[/code] This line links it all together, and is where the structure in your WorldGenNamehere class is called.
[/list]
[center][i]WorldGenNameHere[/i][/center][list]
[*]Random is also imported in this class.
[*]This class extends WorldGenerator which is where the main methods are located. (The [i]generate[/i] method is mainly used)
[/list]
[code]public WorldGenNamehere() { }[list]
[*][/code] This is the constructor of the class. We don't need to add anything into it for what we're doing.
[*]The next method is the [i]generate[/i] method, and is where the designing of our structure takes place.
[*]Using an int like this one [code]int stoneBrick = Block.stoneBrick.blockID;[/code] makes it a lot quicker to type things in. This is because we don't need to type Block.stoneBrick.blockID every time we want to generate that block. Instead, we just write [code]stoneBrick[/code] like is shown in the example code.
[*][code]world.setBlockWithNotify(i, j, k, stoneBrick); [/code] That is the main method used to generate blocks. The i, j and k are positions on the i, j, k (x, y, z, respectively) axes of the block.
[*]As your structures get bigger, these do too. You simply make the distance change by using a + (plus) or - (minus/hyphen) after the letter, and then putting a number, which is the number of blocks. For example: [code]world.setBlockWithNotify(i + 3, j + 2, k - 7, stoneBrick);[/code] That would place a block 3 blocks further away from the starting point on the i/x axis, it would be 2 blocks higher than the starting point(It will still be at the same i/k position), and 7 blocks along the k/z axis away from the starting point.
[*][code]world.setBlockAndMetadataWithNotify(i, j, k, stoneBrick, 1);[/code] You don't need to use this unless trying to generate blocks with a damage value. In this example, I am generating a stone brick block with the damage value of 1. A stone brick block with the damage value of 1 is mossy stone brick, 2 is cracked stone brick.
[/list]This is a picture of what is generated when using the example:
[img]http://i1217.photobucket.com/albums/dd385/TechGuy543/Tutorials/generation.png[/img]
As you can see, the amount of generated sets of blocks in the background is quite high. This can be changed by changing the number in the brackets, in the 'RandPosX' and 'RandPosY' lines, in the GenerateSurface method, in your mod_ class. You can also change the 10 in the [i]for[/i] loop line.
[b]Note: Everytime that you change your design in your WorldGenNamehere class, or the density of it in the generateSurface method in mod_, you need to create a new world to see it.[/b]
[/spoiler]
[center][b]Other World Generation[/b][/center]
[spoiler]
[center][i]Specific Biome Block Generation[/i][/center]
[center][size=x-small][i](Generating a block in a specific biome only)[/i][/size][/center]
[spoiler]
This tutorial will build off the Ore Generation tutorial, but can be used the same way with Structure tutorial.
[center][i]mod_OreGeneration[/i][/center]
[code]package net.minecraft.src;
import java.util.Random;
public class mod_OreGeneration extends BaseMod
{
public static final Block nameHere = new BlockNamehere(160, 0).setBlockName("anyNameHere").setHardness(3F).setResistance(4F).setLightValue(1F);
public void load()
{
nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/pathtoyourfile/image.png");
ModLoader.registerBlock(nameHere);
ModLoader.addName(nameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt});
}
public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
BiomeGenBase biomegenbase = world.getWorldChunkManager().getBiomeGenAt(chunkX, chunkZ);
if(biomegenbase instanceof BiomeGen***)
{
for(int i = 0; i < 7; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
}
}
public String getVersion()
{
return "1.4.2";
}
}[/code]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
The only added code here is this:
[code]BiomeGenBase biomegenbase = world.getWorldChunkManager().getBiomeGenAt(chunkX, chunkZ);
if(biomegenbase instanceof BiomeGen***)
{
//... code from previous tutorial/s
}[/code]
Basically, it creates an instance called biomegenbase from the BiomeGenBase type, it then gets the current biome at the location from the world's chunk manager. The next line just gets the BiomeGen class so you can use it from a static context. The third line, the if statement, just uses the biomegenbase instance, and checks it is equivalent to the BiomeGen*** class, then it continues on with the generation.
Obviously, you change the asterisks in the BiomeGen*** parts to the actual name of a BiomeGen class.
I know this wasn't a very good explanation and I didn't really use the correct names for certain things, but I think the methods that are called basically explain what they actually do anyway. Self-explanatory!
[/spoiler]
[center][i]Ore Generation in Nether[/i][/center]
[spoiler]
This tutorial will build off of the ore generation tutorial.
[code]package net.minecraft.src;
import java.util.Random;
public class WorldGenNetherMinableNameHere extends WorldGenerator
{
/** The block ID of the ore to be placed using this generator. */
private int minableBlockId;
/** The number of blocks to generate. */
private int numberOfBlocks;
public WorldGenNetherMinableNameHere(int par1, int par2)
{
minableBlockId = par1;
numberOfBlocks = par2;
}
public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5)
{
float f = par2Random.nextFloat() * (float)Math.PI;
double d = (float)(par3 + 8) + (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
double d1 = (float)(par3 + 8) - (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
double d2 = (float)(par5 + 8) + (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
double d3 = (float)(par5 + 8) - (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
double d4 = (par4 + par2Random.nextInt(3)) - 2;
double d5 = (par4 + par2Random.nextInt(3)) - 2;
for (int i = 0; i <= numberOfBlocks; i++)
{
double d6 = d + ((d1 - d) * (double)i) / (double)numberOfBlocks;
double d7 = d4 + ((d5 - d4) * (double)i) / (double)numberOfBlocks;
double d8 = d2 + ((d3 - d2) * (double)i) / (double)numberOfBlocks;
double d9 = (par2Random.nextDouble() * (double)numberOfBlocks) / 16D;
double d10 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
double d11 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
int j = MathHelper.floor_double(d6 - d10 / 2D);
int k = MathHelper.floor_double(d7 - d11 / 2D);
int l = MathHelper.floor_double(d8 - d10 / 2D);
int i1 = MathHelper.floor_double(d6 + d10 / 2D);
int j1 = MathHelper.floor_double(d7 + d11 / 2D);
int k1 = MathHelper.floor_double(d8 + d10 / 2D);
for (int l1 = j; l1 <= i1; l1++)
{
double d12 = (((double)l1 + 0.5D) - d6) / (d10 / 2D);
if (d12 * d12 >= 1.0D)
{
continue;
}
for (int i2 = k; i2 <= j1; i2++)
{
double d13 = (((double)i2 + 0.5D) - d7) / (d11 / 2D);
if (d12 * d12 + d13 * d13 >= 1.0D)
{
continue;
}
for (int j2 = l; j2 <= k1; j2++)
{
double d14 = (((double)j2 + 0.5D) - d8) / (d10 / 2D);
if (d12 * d12 + d13 * d13 + d14 * d14 < 1.0D && par1World.getBlockId(l1, i2, j2) == Block.netherrack.blockID)
{
par1World.setBlock(l1, i2, j2, minableBlockId);
}
}
}
}
}
return true;
}
}[/code]
That is the only additional class we need. Now we just change the generateSurface method to generateNether and call this class instead of WorldGenMinable.
[code](new WorldGenNetherMinableNameHere(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);[/code]
So that it fits in like this:
[code] public void generateNether(World world, Random random, int chunkX, int chunkZ)
{
for(int i = 0; i < 7; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenNetherMinableNameHere(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
}[/code]
[center][size=large][font=Impact]Understanding the Code[/font][/size][/center]
[center]This is very similar to generating an ore normally but there is one difference.[/center][list]
[*]We have to create and call our own WorldGenMinable class because we have to change it to generate our blocks around Block.netherrack instead of Block.stone. Where you see Block.netherrack in the WorldGenNetherMinableNameHere class is where the change occurred.
[*]Ask if you need a better explanation.
[/list]
[/spoiler]
[center]Multiple Blocks/Structures in One Class[/center]
[spoiler]
I have been receiving several messages a day about how to do this so I decided to make a tutorial on it. It is quite simple Java work to be able to do this. I really recommend you learn at least some basic Java.
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
for(int i = 0; i < 7; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
for(int i = 0; i < 9; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere2.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
}[/code]
That is what the generateSurface method would look like with two blocks generating. It is simply adding another for loop for each block or structure. You can edit the generation rates as normal for each one.
With three blocks generating:
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
for(int i = 0; i < 7; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(35);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);
}
for(int i = 0; i < 4; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere2.blockID, 19)).generate(world, random, randPosX, randPosY, randPosZ);
}
for(int i = 0; i < 2; i++)
{
int randPosX = chunkX + random.nextInt(16);
int randPosY = random.nextInt(128);
int randPosZ = chunkZ + random.nextInt(16);
(new WorldGenMinable(nameHere3.blockID, 13)).generate(world, random, randPosX, randPosY, randPosZ);
}
}[/code]
The exact same thing is done for structures:
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ)
{
for(int k = 0; k < 10; k++)
{
int RandPosX = chunkX + random.nextInt(5);
int RandPosY = random.nextInt(80);
int RandPosZ = chunkZ + random.nextInt(5);
(new WorldGenNameHere()).generate(world, random, RandPosX, RandPosY, RandPosZ);
}
for(int k = 0; k < 10; k++)
{
int RandPosX = chunkX + random.nextInt(5);
int RandPosY = random.nextInt(80);
int RandPosZ = chunkZ + random.nextInt(5);
(new WorldGenNameHere2()).generate(world, random, RandPosX, RandPosY, RandPosZ);
}
}[/code]
I hope this clears it up a bit for everyone asking.
[/spoiler]
[/spoiler]
[center][b]Biome[/b][/center]
[spoiler]
[center]This is quite a basic tutorial on a biome. There are heaps of variables you can use to change your biome around and make it really different and creative. Let your imagination run wild![/center]
[center][i]mod_Biome[/i][/center]
[code]package net.minecraft.src;
public class mod_Biome extends BaseMod
{
public static final BiomeGenBase nameHere = (new BiomeGenNameHere(25)).setColor(0xfa9418).setBiomeName("Any Name Here");
public void load()
{
ModLoader.addBiome(nameHere);
}
public String getVersion()
{
return "1.4.2";
}
}[/code]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
[center][i]mod_Biome[/i][/center][list]
[*][code]public static final BiomeGenBase nameHere= (new BiomeGenNamehere(25)).setColor(0xfa9418).setBiomeName("Any Name Here");[/code]
[*]"BiomeGenBase" is put before the name of your instance just like items, blocks and achievements, etc.
[*]The number in brackets after the name of your new BiomeGen class is the ID of the new biome. This must be more than 23(that is the currently the highest uses biome ID) and less than 256.
[*]I think the ".setColor()" part of this line is optional. I believe it basically sets the ambience/colour around you when you're in it. You can find some colours [url="http://mindprod.com/jgloss/netscapehtml.html"]here[/url].
[*]".setBiomeName("")" is the name of the biome(obviously). It is mainly used in the F3 Debug screen where it tells you what biome you're in.
[/list][list]
[*][code]ModLoader.addBiome(nameHere);[/code]
[*]This is the ModLoader string for adding a biome to biomesList[] array and the other classes needed so that the biome actually generates with the world.
[/list]
[center][i]BiomeGenNamehere[/i][/center][list]
[*]This is a basic class that extends BiomeGenBase.
[/list][list]
[*][code]spawnableCreatureList.clear();[/code]
[*]That basically clears the list of creatures that can spawn in the biome.
[*]Have a look in the other BiomeGen*** classes on how you can make certain mobs spawn in your biome.
[/list][list]
[*][code]topBlock = (byte)Block.blockGold.blockID;
fillerBlock = (byte)Block.blockSteel.blockID;[/code]
[*]"topBlock" is the block in the biome that is the top 1-5 blocks.
[*]"fillerBlock" is the block that fills the rest of the space until stone.
[/list][list]
[*][code]biomeDecorator.treesPerChunk = 5;
biomeDecorator.flowersPerChunk = 4;
biomeDecorator.grassPerChunk = 10;[/code]
[*]I think these lines are fairly self-explanatory. Please ask if you need an explanation though.
[*]Some other biomeDecorator strings/methods you can use include:
[/list]
[code]biomeDecorator.deadBushPerChunk = 2;
biomeDecorator.reedsPerChunk = 50;
biomeDecorator.cactiPerChunk = 10;
biomeDecorator.mushroomsPerChunk = 8;
biomeDecorator.clayPerChunk = 1;
biomeDecorator.waterlilyPerChunk = 4;[/code]
[center][size=large][u][b][color=#FF0000]Keep Reading Below If You Receive A Crash When Generating a New World With Your Biome.[/color][/b][/u][/size][/center]
If you get a crash similar to the one below when generating a new world with your biome added into the generation, put the id of the block that you are generating below 127. The reason behind this is that a byte's limit in Java is -128 to 127. When you go over that 127, you'll most likely get a crash.[code]java.lang.ArrayIndexOutOfBoundsException: -96
at net.minecraft.src.ExtendedBlockStorage.setExtBlockID(ExtendedBlockStorage.java:83)
at net.minecraft.src.Chunk.<init>(Chunk.java:113)
at net.minecraft.src.ChunkProviderGenerate.provideChunk(ChunkProviderGenerate.java:290)
at net.minecraft.src.ChunkProvider.loadChunk(ChunkProvider.java:93)
at net.minecraft.src.ChunkProvider.provideChunk(ChunkProvider.java:119)
at net.minecraft.src.World.getChunkFromChunkCoords(World.java:654)
at net.minecraft.src.World.tickBlocksAndAmbiance(World.java:3104)
at net.minecraft.src.World.tick(World.java:2872)
at net.minecraft.client.Minecraft.runTick(Minecraft.java:1903)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:870)
at net.minecraft.client.Minecraft.run(Minecraft.java:801)
at java.lang.Thread.run(Thread.java:680)
--- END ERROR REPORT 75039747 ----------[/code]
[/spoiler]
[center][color=#000000][b]Tools[/b][/color][/center]
[spoiler]
Due to the amount of code required for this, I've put it in another spoiler.
[spoiler]
[center]
[i]mod_Tools[/i][/center]
[code]package net.minecraft.src;
public class mod_Tools extends BaseMod
{
public static final Item pickaxe = new ItemNameherePickaxe(2000, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("pickaxe");
public static final Item axe = new ItemNamehereAxe(2001, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("axe");
public static final Item shovel = new ItemNamehereShovel(2002, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("shovel");
public static final Item sword = new ItemNamehereSword(2003, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("sword");
public static final Item hoe = new ItemNamehereHoe(2004, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("hoe");
public void load()
{
pickaxe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/pickaxeimage.png");
axe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/axeimage.png");
shovel.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/shovelimage.png");
sword.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/swordimage.png");
hoe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/hoeimage.png");
ModLoader.addName(pickaxe, "Pickaxe");
ModLoader.addName(axe, "Axe");
ModLoader.addName(shovel, "Shovel");
ModLoader.addName(sword, "Sword");
ModLoader.addName(hoe, "Hoe");
ModLoader.addRecipe(new ItemStack(pickaxe, 1), new Object [] {"###", " % ", " % ", '#', Block.dirt, '%', Item.stick});
ModLoader.addRecipe(new ItemStack(axe, 1), new Object [] {"##", "#%", " %", '#', Block.dirt, '%', Item.stick});
ModLoader.addRecipe(new ItemStack(shovel, 1), new Object [] {"#", "%", "%", '#', Block.dirt, '%', Item.stick});
ModLoader.addRecipe(new ItemStack(sword, 1), new Object [] {"#", "#", "%", '#', Block.dirt, '%', Item.stick});
ModLoader.addRecipe(new ItemStack(hoe, 1), new Object [] {"##", " %", " %", '#', Block.dirt, '%', Item.stick});
}
public String getVersion()
{
return "1.4.2";
}
}[/code]
[center]
[i]ItemNamehereTool[/i][/center]
[code]package net.minecraft.src;
public class ItemNamehereTool extends Item
{
private Block blocksEffectiveAgainst[];
protected float efficiencyOnProperMaterial;
private int damageVsEntity;
protected EnumToolMaterialNamehere toolMaterial;
protected ItemNamehereTool(int i, int j, EnumToolMaterialNamehere par3EnumToolMaterialNamehere, Block par4ArrayOfBlock[])
{
super(i);
efficiencyOnProperMaterial = 4F;
toolMaterial = par3EnumToolMaterialNamehere;
blocksEffectiveAgainst = par4ArrayOfBlock;
maxStackSize = 1;
setMaxDamage(par3EnumToolMaterialNamehere.getMaxUses());
efficiencyOnProperMaterial = par3EnumToolMaterialNamehere.getEfficiencyOnProperMaterial();
damageVsEntity = j + par3EnumToolMaterialNamehere.getDamageVsEntity();
}
/**
* Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if
* sword
*/
public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block)
{
for (int i = 0; i < blocksEffectiveAgainst.length; i++)
{
if (blocksEffectiveAgainst[i] == par2Block)
{
return efficiencyOnProperMaterial;
}
}
return 1.0F;
}
/**
* Current implementations of this method in child classes do not use the entry argument beside ev. They just raise
* the damage on the stack.
*/
public boolean hitEntity(ItemStack par1ItemStack, EntityLiving par2EntityLiving, EntityLiving par3EntityLiving)
{
par1ItemStack.damageItem(2, par3EntityLiving);
return true;
}
//If someone decompiles a more updated MCP after the time of writing this tutorial and you get an error on this method, please let me know via either a PM or just a post on this topic. The method hasn't been named yet so it is highly likely in a forthcoming MCP update it will be.
public boolean onBlockDestroyed(ItemStack par1ItemStack, World par2World, int par3, int par4, int par5, int par6, EntityLiving par7EntityLiving)
{
if ((double)Block.blocksList[par3].getBlockHardness(par2World, par4, par5, par6) != 0.0D)
{
par1ItemStack.damageItem(1, par7EntityLiving);
}
return true;
}
return true;
}
/**
* Returns the damage against a given entity.
*/
public int getDamageVsEntity(Entity par1Entity)
{
return damageVsEntity;
}
/**
* Returns True is the item is renderer in full 3D when hold.
*/
public boolean isFull3D()
{
return true;
}
/**
* Return the enchantability factor of the item, most of the time is based on material.
*/
public int getItemEnchantability()
{
return toolMaterial.getEnchantability();
}
}[/code]
[center]
[i]EnumToolMaterialNamehere[/i][/center]
[code]package net.minecraft.src;
public enum EnumToolMaterialNamehere
{
MATERIALNAMEHERE(0, 59, 2.0F, 0, 15);
private final int harvestLevel;
private final int maxUses;
private final float efficiencyOnProperMaterial;
private final int damageVsEntity;
private final int enchantability;
private EnumToolMaterialNamehere(int par3, int par4, float par5, int par6, int par7)
{
harvestLevel = par3;
maxUses = par4;
efficiencyOnProperMaterial = par5;
damageVsEntity = par6;
enchantability = par7;
}
public int getMaxUses()
{
return maxUses;
}
public float getEfficiencyOnProperMaterial()
{
return efficiencyOnProperMaterial;
}
public int getDamageVsEntity()
{
return damageVsEntity;
}
public int getHarvestLevel()
{
return harvestLevel;
}
public int getEnchantability()
{
return enchantability;
}
}[/code]
[center]
[i]ItemNameherePickaxe[/i][/center]
[code]package net.minecraft.src;
public class ItemNameherePickaxe extends ItemNamehereTool
{
private static Block[] blocksEffectiveAgainst = new Block[] {Block.cobblestone, Block.stoneDoubleSlab, Block.stoneSingleSlab, Block.stone, Block.sandStone, Block.cobblestoneMossy, Block.oreIron, Block.blockSteel, Block.oreCoal, Block.blockGold, Block.oreGold, Block.oreDiamond, Block.blockDiamond, Block.ice, Block.netherrack, Block.oreLapis, Block.blockLapis, Block.oreRedstone, Block.oreRedstoneGlowing, Block.rail, Block.railDetector, Block.railPowered};
protected ItemNameherePickaxe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere)
{
super(par1, 2, par2EnumToolMaterialNamehere, blocksEffectiveAgainst);
}
/**
* Returns if the item (tool) can harvest results from the block type.
*/
public boolean canHarvestBlock(Block par1Block)
{
if (par1Block == Block.obsidian)
{
return toolMaterial.getHarvestLevel() == 3;
}
if (par1Block == Block.blockDiamond || par1Block == Block.oreDiamond)
{
return toolMaterial.getHarvestLevel() >= 2;
}
if (par1Block == Block.blockGold || par1Block == Block.oreGold)
{
return toolMaterial.getHarvestLevel() >= 2;
}
if (par1Block == Block.blockSteel || par1Block == Block.oreIron)
{
return toolMaterial.getHarvestLevel() >= 1;
}
if (par1Block == Block.blockLapis || par1Block == Block.oreLapis)
{
return toolMaterial.getHarvestLevel() >= 1;
}
if (par1Block == Block.oreRedstone || par1Block == Block.oreRedstoneGlowing)
{
return toolMaterial.getHarvestLevel() >= 2;
}
if (par1Block.blockMaterial == Material.rock)
{
return true;
}
return par1Block.blockMaterial == Material.iron;
}
/**
* Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if
* sword
*/
public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block)
{
if (par2Block != null && (par2Block.blockMaterial == Material.iron || par2Block.blockMaterial == Material.rock))
{
return efficiencyOnProperMaterial;
}
else
{
return super.getStrVsBlock(par1ItemStack, par2Block);
}
}
}[/code]
[center]
[i]ItemNamehereAxe[/i][/center]
[code]package net.minecraft.src;
public class ItemNamehereAxe extends ItemNamehereTool
{
private static Block[] blocksEffectiveAgainst = new Block[] {Block.planks, Block.bookShelf, Block.wood, Block.chest, Block.stoneDoubleSlab, Block.stoneSingleSlab, Block.pumpkin, Block.pumpkinLantern};
protected ItemNamehereAxe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere)
{
super(par1, 3, par2EnumToolMaterialNamehere, blocksEffectiveAgainst);
}
/**
* Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if
* sword
*/
public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block)
{
if (par2Block != null && par2Block.blockMaterial == Material.wood)
{
return efficiencyOnProperMaterial;
}
else
{
return super.getStrVsBlock(par1ItemStack, par2Block);
}
}
}[/code]
[center]
[i]ItemNamehereShovel[/i][/center]
[code]package net.minecraft.src;
public class ItemNamehereShovel extends ItemNamehereTool
{
private static Block blocksEffectiveAgainst[];
public ItemNamehereShovel(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere)
{
super(par1, 1, par2EnumToolMaterialNamehere, blocksEffectiveAgainst);
}
/**
* Returns if the item (tool) can harvest results from the block type.
*/
public boolean canHarvestBlock(Block par1Block)
{
if (par1Block == Block.snow)
{
return true;
}
return par1Block == Block.blockSnow;
}
static
{
blocksEffectiveAgainst = (new Block[]
{
Block.grass, Block.dirt, Block.sand, Block.gravel, Block.snow, Block.blockSnow, Block.blockClay, Block.tilledField, Block.slowSand, Block.mycelium
});
}
}[/code]
[center]
[i]ItemNamehereSword[/i][/center]
[code]package net.minecraft.src;
public class ItemNamehereSword extends Item
{
private int weaponDamage;
private final EnumToolMaterialNamehere toolMaterial;
public ItemNamehereSword(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere)
{
super(par1);
toolMaterial = par2EnumToolMaterialNamehere;
maxStackSize = 1;
setMaxDamage(par2EnumToolMaterialNamehere.getMaxUses());
weaponDamage = 4 + par2EnumToolMaterialNamehere.getDamageVsEntity();
}
/**
* Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if
* sword
*/
public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block)
{
return par2Block.blockID != Block.web.blockID ? 1.5F : 15F;
}
/**
* Current implementations of this method in child classes do not use the entry argument beside ev. They just raise
* the damage on the stack.
*/
public boolean hitEntity(ItemStack par1ItemStack, EntityLiving par2EntityLiving, EntityLiving par3EntityLiving)
{
par1ItemStack.damageItem(1, par3EntityLiving);
return true;
}
public boolean onBlockDestroyed(ItemStack par1ItemStack, int par2, int par3, int par4, int par5, EntityLiving par6EntityLiving)
{
par1ItemStack.damageItem(2, par6EntityLiving);
return true;
}
/**
* Returns the damage against a given entity.
*/
public int getDamageVsEntity(Entity par1Entity)
{
return weaponDamage;
}
/**
* Returns True is the item is renderer in full 3D when hold.
*/
public boolean isFull3D()
{
return true;
}
/**
* returns the action that specifies what animation to play when the items is being used
*/
public EnumAction getItemUseAction(ItemStack par1ItemStack)
{
return EnumAction.block;
}
/**
* How long it takes to use or consume an item
*/
public int getMaxItemUseDuration(ItemStack par1ItemStack)
{
return 0x11940;
}
/**
* Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer
*/
public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer)
{
par3EntityPlayer.setItemInUse(par1ItemStack, getMaxItemUseDuration(par1ItemStack));
return par1ItemStack;
}
/**
* Returns if the item (tool) can harvest results from the block type.
*/
public boolean canHarvestBlock(Block par1Block)
{
return par1Block.blockID == Block.web.blockID;
}
/**
* Return the enchantability factor of the item, most of the time is based on material.
*/
public int getItemEnchantability()
{
return toolMaterial.getEnchantability();
}
}[/code]
[center]
[i]ItemNamehereHoe[/i][/center]
[code]package net.minecraft.src;
public class ItemNamehereHoe extends Item
{
public ItemNamehereHoe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere)
{
super(par1);
maxStackSize = 1;
setMaxDamage(par2EnumToolMaterialNamehere.getMaxUses());
}
public boolean tryPlaceIntoWorld(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
{
if (!par2EntityPlayer.canPlayerEdit(par4, par5, par6))
{
return false;
}
int i = par3World.getBlockId(par4, par5, par6);
int j = par3World.getBlockId(par4, par5 + 1, par6);
if (par7 != 0 && j == 0 && i == Block.grass.blockID || i == Block.dirt.blockID)
{
Block block = Block.tilledField;
par3World.playSoundEffect((float)par4 + 0.5F, (float)par5 + 0.5F, (float)par6 + 0.5F, block.stepSound.getStepSound(), (block.stepSound.getVolume() + 1.0F) / 2.0F, block.stepSound.getPitch() * 0.8F);
if (par3World.isRemote)
{
return true;
}
else
{
par3World.setBlockWithNotify(par4, par5, par6, block.blockID);
par1ItemStack.damageItem(1, par2EntityPlayer);
return true;
}
}
else
{
return false;
}
}
public boolean isFull3D()
{
return true;
}
}[/code]
[/spoiler]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
[color=#008000][b]Notes:[/b][/color][list]
[*][color=#008000][b]A lot of the code is already explained in the comments above methods so I won't explain that part of code down here.[/b][/color]
[*][b]These tools aren't enchantable. Don't ask me how to make them enchantable.[/b]
[/list]
[center][i]mod_Tools[/i][/center][list]
[*]This is a fairly normal mod_ class. It is important to remember that tools are just items with special features.
[*]In the line that you declare the tool, there is an additional argument to a normal item. The new argument is the tools material which is stored in an Enum.
[*]Rest of code is same as items.
[/list]
I think most of the rest of the code is explained in comments. Just ask me if you need an explanation of it anyway. I suppose this isn't so much of a tutorial on my part but the Javadoc comments explain it just as well as I could, if not better.
[code]package net.minecraft.src;
public class mod_Crop extends BaseMod
{
//Just a standard block.
public static final Block cropNameHere = new BlockCropNameHere(165, 0).setBlockName("cropNameHere");
//This is a fairly standard item but it has two new arguments. The first is the block it plants, in this case is our new crop "cropNameHere". The second is the block that is required to be right clicked on to plant the seeds.
//We are using tilled field/dirt for this crop but you can change it to whatever you like. Remember to change it in the block class as well.
public static final Item seedsNameHere = new ItemSeedsNameHere(2500, cropNameHere.blockID, Block.tilledField.blockID).setItemName("seedsNameHere");
//These static ints are the textures of the crop in its various stages of growth.
public static int cropStageOne = ModLoader.addOverride("/terrain.png", "/stageOne.png");
public static int cropStageTwo = ModLoader.addOverride("/terrain.png", "/stageTwo.png");
public static int cropStageThree = ModLoader.addOverride("/terrain.png", "/stageThree.png");
public static int cropStageFour = ModLoader.addOverride("/terrain.png", "/stageFour.png");
public static int cropStageFive = ModLoader.addOverride("/terrain.png", "/stageFive.png");
public static int cropStageSix = ModLoader.addOverride("/terrain.png", "/stageSix.png");
public static int cropStageSeven = ModLoader.addOverride("/terrain.png", "/stageSeven.png");
public static int cropStageEight = ModLoader.addOverride("/terrain.png", "/stageEight.png");
public void load()
{
cropNameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/crop.png");
ModLoader.registerBlock(cropNameHere);
ModLoader.addName(cropNameHere, "In-Game Name Here");
seedsNameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/seeds.png");
ModLoader.addName(seedsNameHere, "In-Game Name Here");
ModLoader.addRecipe(new ItemStack(seedsNameHere), new Object [] {"#", '#', Block.dirt});
}
public String getVersion()
{
return "1.4.2";
}
}[/code]
[center][i]BlockCropNameHere[/i][/center]
[code]package net.minecraft.src;
import java.util.Random;
public class BlockCropNameHere extends BlockFlower
{
public BlockCropNameHere(int i, int j)
{
super(i, j);
blockIndexInTexture = j;
setTickRandomly(true);
float f = 0.5F;
setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f);
}
/**
* Gets passed in the blockID of the block below and supposed to return true if its allowed to grow on the type of
* blockID passed in. Args: blockID.
* This basically checks to see if the block below is a tilled field/tilled dirt. If it is true then the crop can grow.
*/
protected boolean canThisPlantGrowOnThisBlockID(int par1)
{
return par1 == Block.tilledField.blockID;
}
/**
* Ticks the block if it's been scheduled. This method gets scheduled to run because of the setTickRandomly part in the constructor of the class.
*/
public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
super.updateTick(par1World, par2, par3, par4, par5Random);
if (par1World.getBlockLightValue(par2, par3 + 1, par4) >= 9)
{
int i = par1World.getBlockMetadata(par2, par3, par4);
if (i < 8)
{
float f = getGrowthRate(par1World, par2, par3, par4);
if (par5Random.nextInt((int)(25F / f) + 1) == 0)
{
i++;
par1World.setBlockMetadataWithNotify(par2, par3, par4, i);
}
}
}
}
/**
* This method allows you to use bonemeal on your crop. Code explanation below:
ItemStack itemstack = entityplayer.inventory.getCurrentItem(); - This line makes "itemstack" equal to the item currently in the players hand.
if(itemstack != null && itemstack.itemID == Item.dyePowder.itemID) - This line checks if the item in players hand is equal to dye.
if(itemstack.getItemDamage() == 15) - This line checks if the damage value of that item is 15. Item.dyePowder's damage value of 15 is bonemeal.
world.setBlockMetadataWithNotify(i, j, k, 8); - This line sets the metadata value of the block to 8 which is the final growth stage.
itemstack.stackSize--; - This line makes the stack size go down by one.
world.notifyBlockChange(i, j, k, 0); - This line notifys adjacent blocks that this block has updated its state.
*/
public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
{
ItemStack itemstack = par5EntityPlayer.inventory.getCurrentItem();
if(itemstack != null && itemstack.itemID == Item.dyePowder.[size=medium]itemID[/size])
{
if(itemstack.getItemDamage() == 15)
{
par1World.setBlockMetadataWithNotify(par2, par3, par4, 8);
itemstack.stackSize--;
par1World.notifyBlockChange(par2, par3, par4, 0);
}
}
super.onBlockActivated(par1World, par2, par3, par4, par5EntityPlayer, par6, par7, par8, par9);
return true;
}
/**
* Gets the growth rate for the crop. Setup to encourage rows by halving growth rate if there is diagonals, crops on
* different sides that aren't opposing, and by adding growth for every crop next to this one (and for crop below
* this one). Args: x, y, z
*/
private float getGrowthRate(World par1World, int par2, int par3, int par4)
{
float f = 1.0F;
int i = par1World.getBlockId(par2, par3, par4 - 1);
int j = par1World.getBlockId(par2, par3, par4 + 1);
int k = par1World.getBlockId(par2 - 1, par3, par4);
int l = par1World.getBlockId(par2 + 1, par3, par4);
int i1 = par1World.getBlockId(par2 - 1, par3, par4 - 1);
int j1 = par1World.getBlockId(par2 + 1, par3, par4 - 1);
int k1 = par1World.getBlockId(par2 + 1, par3, par4 + 1);
int l1 = par1World.getBlockId(par2 - 1, par3, par4 + 1);
boolean flag = k == blockID || l == blockID;
boolean flag1 = i == blockID || j == blockID;
boolean flag2 = i1 == blockID || j1 == blockID || k1 == blockID || l1 == blockID;
for (int i2 = par2 - 1; i2 <= par2 + 1; i2++)
{
for (int j2 = par4 - 1; j2 <= par4 + 1; j2++)
{
int k2 = par1World.getBlockId(i2, par3 - 1, j2);
float f1 = 0.0F;
if (k2 == Block.tilledField.blockID)
{
f1 = 1.0F;
if (par1World.getBlockMetadata(i2, par3 - 1, j2) > 0)
{
f1 = 3F;
}
}
if (i2 != par2 || j2 != par4)
{
f1 /= 4F;
}
f += f1;
}
}
if (flag2 || flag && flag1)
{
f /= 2.0F;
}
return f;
}
/**
* The type of render function that is called for this block. The render type of 6 gets the texture and places it four times around the sides of the block and leaves nothing on the top or bottom.
*/
public int getRenderType()
{
return 6;
}
/**
* Drops the block items with a specified chance of dropping the specified items
*/
public void dropBlockAsItemWithChance(World par1World, int par2, int par3, int par4, int par5, float par6, int par7)
{
super.dropBlockAsItemWithChance(par1World, par2, par3, par4, par5, par6, 0);
if (par1World.isRemote)
{
return;
}
int i = 3 + par7;
for (int j = 0; j < i; j++)
{
if (par1World.rand.nextInt(15) <= par5)
{
float f = 0.7F;
float f1 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F;
float f2 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F;
float f3 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F;
EntityItem entityitem = new EntityItem(par1World, (float)par2 + f1, (float)par3 + f2, (float)par4 + f3, new ItemStack(mod_Crop.seedsNameHere));
entityitem.delayBeforeCanPickup = 10;
par1World.spawnEntityInWorld(entityitem);
}
}
}
/**
* Returns the ID of the items to drop on destruction. "i" is equal to the blocks metadata value(explained slightly more in the getBlockTextureFromSideAndMetadata method below). This means that it will check that that value is equal to 8(the final stage of growth) and if it is then it will drop wheat. It may be fairly obvious, but the 'else' statement means that if the growth state is not equal to 7 then drop nothing (-1 means nothing)
*/
public int idDropped(int i, Random random, int j)
{
if (i == 8)
{
return Item.wheat.itemID;
}
else
{
return -1;
}
}
/**
* Returns the quantity of items to drop on block destruction.
*/
public int quantityDropped(Random random)
{
return 1;
}
/**
* From the specified side and block metadata retrieves the blocks texture. Args: side, metadata.
* As you may have been able to tell from the line above, "j" is equal to the metadata value of the block. This checks if that value is equal to a certain number then sets the blocks texture to what you have defined.
* The things that are being returned are the ints in your mod_ class which you created and set to your texture for the specific stages of growth.
*/
public int getBlockTextureFromSideAndMetadata(int i, int j)
{
if(j == 0)
{
return blockIndexInTexture;
}
if(j == 1)
{
return mod_Crop.cropStageOne;
}
if(j == 2)
{
return mod_Crop.cropStageTwo;
}
if(j == 3)
{
return mod_Crop.cropStageThree;
}
if(j == 4)
{
return mod_Crop.cropStageFour;
}
if(j == 5)
{
return mod_Crop.cropStageFive;
}
if(j == 6)
{
return mod_Crop.cropStageSix;
}
if(j == 7)
{
return mod_Crop.cropStageSeven;
}
if(j == 8)
{
return mod_Crop.cropStageEight;
}
return j;
}
}[/code]
[center][i]ItemSeedsNameHere[/i][/center]
[code]package net.minecraft.src;
public class ItemSeedsNameHere extends Item
{
/** The type of block this seed turns into (wheat or pumpkin stems for instance)*/
private int blockType;
/** BlockID of the block the seeds can be planted on. */
private int soilBlockID;
public ItemSeedsNameHere(int i, int j, int k)
{
super(i);
blockType = j;
soilBlockID = k;
}
/**
* Callback for item usage. If the item does something special on right clicking, he will have one of those. Return
* True if something happen and false if it don't. This is for ITEMS, not BLOCKS !
*/
public boolean tryPlaceIntoWorld(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
{
if (par7 != 1)
{
return false;
}
if (!par2EntityPlayer.canPlayerEdit(par4, par5, par6) || !par2EntityPlayer.canPlayerEdit(par4, par5 + 1, par6))
{
return false;
}
int i = par3World.getBlockId(par4, par5, par6);
if (i == soilBlockID && par3World.isAirBlock(par4, par5 + 1, par6))
{
par3World.setBlockWithNotify(par4, par5 + 1, par6, blockType);
par1ItemStack.stackSize--;
return true;
}
else
{
return false;
}
}
}[/code]
This crop will have 8 stages. You can change the amount in the code in this method, from the block class:
[code]public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
super.updateTick(par1World, par2, par3, par4, par5Random);
if (par1World.getBlockLightValue(par2, par3 + 1, par4) >= 9)
{
int i = par1World.getBlockMetadata(par2, par3, par4);
if (i < 8) //THIS LINE HERE. CHANGE THE 8 TO YOUR FINAL GROWTH STAGE.
{
float f = getGrowthRate(par1World, par2, par3, par4);
if (par5Random.nextInt((int)(25F / f) + 1) == 0)
{
i++;
par1World.setBlockMetadataWithNotify(par2, par3, par4, i);
}
}
}
}[/code]
Just change that 8 at the line shown to your final growth stage. Also remember to change the 8 in the idDropped method to the final growth stage of your crop. Just have the relevant amount needed in the texture getting method.
You'll require a total of 10 textures if following this tutorial exactly. One for seeds, one for the block normally(can be the same as stage 1 but it needs to have a separate image file) and one each for every other stage.
I have explained all of the code in comments in this tutorial. If there was already a Javadoc comment for something I added some information to it in most instances. Let know what you all think, whether you like it like this or how I used to make these tutorials.
[/spoiler]
[center][b]GUI[/b][/center]
[spoiler]
A GUI is a Graphical User Interface. They are things like the inventory, furnace window and crafting window. Things like the menu that opens when you press "Escape" on your keyboard are also GUIs.
[center][b][i]The Basic GUI Class[/i][/b][/center]
[spoiler]
Here we have a very basic GUI class. It will do absolutely nothing when opened but all of this code is necessary.
[code]package net.minecraft.src;
import net.minecraft.client.Minecraft;
public class GuiNameHere extends GuiScreen
{
private Minecraft mc;
private World world;
private EntityPlayer entityPlayer;
public GuiNameHere(World world1, EntityPlayer entityPlayer1, Minecraft minecraft)
{
world = world1;
entityPlayer = entityPlayer1;
mc = minecraft;
}
public void initGui()
{
}
public void drawScreen(int i, int j, float f)
{
}
}[/code][list]
[*]This class extends GuiScreen. GuiScreen contains most of the methods we will use in creating our GUI but some are also further up in the "heirachy". GuiScreen extends Gui. Gui has several methods in it. The main ones we will use are drawString and drawCenteredString but we'll get onto them later.
[/list][list]
[*]Then we have three private variables:
[/list]
[code]private Minecraft mc;
private World world;
private EntityPlayer entityPlayer;[/code][list]
[*]We use these later when we get down to the constructor.
[/list][list]
[*]In the constructor, there are three parameters. World, EntityPlayer and Minecraft. Obviously, the World is the world that the player(EntityPlayer) is in. Minecraft is the game itself and its main classes.
[/list]
[code]public GuiNameHere(World world1, EntityPlayer entityPlayer1, Minecraft minecraft)[/code][list]
[*]You'll notice that the first to parameters(World and EntityPlayer) have a 1 after their variable(world1, entityPlayer1). We have to add this so that they are not the same as the private variables we previously created. You don't have to add a 1 after it, you could add anything. For example your constructor could say:
[/list]
[code]public GuiNameHere(World worldVariable, EntityPlayer entityPlayerVariable, Minecraft minecraft)[/code][list]
[*]It is just easier though if we keep it as adding a 1 after it.
[/list][list]
[*]Inside the constructor we have 3 more lines.
[/list]
[code] world = world1;
entityPlayer = entityPlayer1;
mc = minecraft;[/code][list]
[*]These lines make our private variables from above equal to our variables in the constructor's parameters.
[*]The reason we add those private variables is so that we can use the parameters from the constructor inside the entire class. Even though we can use ModLoader for it(ModLoader.getMinecraftInstance()), we are better off using the variables we have created. It is easier for us and doesn't make us type so much!
[/list][list]
[*]After the constructor we have our other methods. The first is initGui. This is called when the GUI is first opened and renders the buttons and other controls we may add.
[/list][list]
[*]The final method here so far is drawScreen. This method renders everything else on the screen. When we add in our pictures, text, etc, we will put it in this method. We will also need to add a little bit of code in here when adding buttons.
[/list]
[/spoiler]
[center][b][i]Rendering Text[/i][/b][/center]
[spoiler][list]
[*]There are two different methods we can use to render text on the screen. [i]drawString [/i]and [i]drawCenteredString[/i]. These two are a little bit different from each other.
[*]drawString will position the left side of the text at the specified position.
[*]drawCenteredString will position the center of the text at the specified position.
[/list]
[code]drawCenteredString(fontRenderer, "Text Here", width / 2, height / 2 - 75, 0xffffff);
//or
drawString(fontRenderer, "Text Here", width / 2, height / 2 - 75, 0xffffff);[/code][list]
[*]There we have our two different lines. There are 5 parameters of these methods. They both have the same parameters.
[*]The first parameter is:
[/list]
[code]fontRenderer[/code][list]
[*]This is the font renderer that is inherited from GuiScreen.
[*]The second parameter is the text we want to render on the screen, in the form of a String. Don't remove the quotation marks.
[/list]
[code]"Text Here"[/code][list]
[*]The third parameter is the horizontal position of the text on the screen. We use:
[/list]
[code]width / 2[/code][list]
[*]Which means [u]width divided by 2.[/u] This gives us the center of the screen. If you want to render something like a title and want it in the exact middle of the screen, you should use[i] drawCenteredString[/i] as the method and [u]width / 2[/u] as the position.
[*]The fourth parameter is the vertical position:
[/list]
[code]height / 2[/code][list]
[*]Using [u]height / 2[/u], gets the vertical center of the screen. Once you have the vertical center of the screen you'll probably want to move your text up and down the axis. If you use
[/list]
[code]height / 2 - 75[/code][list]
[*]Then your text will move 75 pixels towards the top of the screen.
[*]Obviously, using a + instead of a - will give the opposite effect.
[/list]
[code]height / 2 + 75[/code][list]
[*]With that, your text would move 75 pixels towards the bottom of the screen.You don't have to use [u]width / 2[/u] or [u]height / 2[/u] for the positions but I find it a lot easier to do so.
[/list][list]
[*]The fifth and final parameter is the colour of your text. This is in a hexadecimal colour code format. All you need to do is get the hex code of the colour you want and add "0x" to the front of it(without the quotation marks).
[*]You can use [url="http://html-color-codes.com/"]this[/url] colour chart. The numbers and letters on the colours is the hex code of that colour. You just get that and add the "0x" to it like explained above.
[/list][list]
[*]A few basic colours to use are:
[/list]
[code]Black = 0x000000
White = 0xffffff
Grey = 0xCCCCCC
Blue = 0x00CCFF
Red = 0xFF3300
Yellow = 0xFFFF00
Green = 0x339933[/code]
[/spoiler]
[center][b]This section is under construction![/b][/center]
[center][size=large][b][img]http://www.iowamotortruck.com/images/CSA%20Alert%20Symbol.jpg[/img][/b][/size][/center]
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
Hey do you or someone you know, or even a random tutorial How to make a boundry for my custom generated villages and make it so that stuff like trees, grass and other crap will not spawn there. If you know how to do this please help :sad.gif: .. as my mod basically needs this to work.
Hey do you or someone you know, or even a random tutorial How to make a boundry for my custom generated villages and make it so that stuff like trees, grass and other crap will not spawn there. If you know how to do this please help :sad.gif: .. as my mod basically needs this to work.
The only way I think you could do this, would be to edit the world generator class. As long as the ground is grass or dirt, you wouldn't need to worry about trees and grass. If you are doing your village in one world gen file(no components like the normal villages), then for pathways and things you would probably just need to set the blocks as air.
world.setBlockWithNotify(i, j, k, 0);
That is the code for setting a block to air if you need it.
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
I'll put up a section on recipes, and explain everything like that :smile.gif:
The only way I think you could do this, would be to edit the world generator class. As long as the ground is grass or dirt, you wouldn't need to worry about trees and grass. If you are doing your village in one world gen file(no components like the normal villages), then for pathways and things you would probably just need to set the blocks as air.
world.setBlockWithNotify(i, j, k, 0);
That is the code for setting a block to air if you need it.
Alright thanks bro ima try it out! Ill link you to my mod if you want, when im done it atleast. :wink.gif:
I just re-read my post. I meant that if the ground is grass or dirt, then trees and grass and things would generate, if it isnt, then you dont need to worry about it.
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
I'll try. I've got to figure out how to make one myself first
Added crafting and smelting recipe tutorials.
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
I have a massive problem. Whenever I decompile MC and Modloader in MCP, and then try to test run it in eclipse (without adding any of my code), Minecraft will crash upon having a log block in your inventory (or maybe hotbar). This has happened upon launch of creative worlds (which contain logs in the hotbar, I think), and whenever punch trees. Here is the error log if anyone is interested:
Mods loaded: 1 ModLoader 1.1 Minecraft has crashed! ---------------------- Minecraft has stopped running because it encountered a problem. If you wish to report this, please copy this entire text and email it to [email protected]. Please include a description of what you did when the error occured. --- BEGIN ERROR REPORT 9c4f49b2 -------- Generated 25/01/12 11:09 Minecraft: Minecraft 1.1 OS: Windows 7 (amd64) version 6.1 Java: 1.6.0_29, Sun Microsystems Inc. VM: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Sun Microsystems Inc. LWJGL: 2.4.2 OpenGL: Intel(R) HD Graphics Family version 3.1.0 - Build 8.15.10.2476, Intel java.lang.NullPointerException at net.minecraft.src.ItemMetadata.getIconFromDamage(ItemMetadata.java:17) at net.minecraft.src.Item.getIconIndex(Item.java:201) at net.minecraft.src.ItemStack.getIconIndex(ItemStack.java:81) at net.minecraft.src.RenderItem.renderItemIntoGUI(RenderItem.java:229) at net.minecraft.src.GuiIngame.renderInventorySlot(GuiIngame.java:574) at net.minecraft.src.GuiIngame.renderGameOverlay(GuiIngame.java:245) at net.minecraft.src.EntityRenderer.updateCameraAndRender(EntityRenderer.java:761) at net.minecraft.src.EntityRendererProxy.updateCameraAndRender(EntityRendererProxy.java:17) at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:737) at net.minecraft.client.Minecraft.run(Minecraft.java:634) at java.lang.Thread.run(Unknown Source) --- END ERROR REPORT 75caaa72 ----------
Try updating your jdk. I have 1.6.0_30 and it works fine. You have 1.6.0_29. There might be a bug fix in that release, I'm not sure.
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
How do you make it only set the light value if it's being powered by redstone?
Also, what do you do with this code? I put it into eclipse, but I don't know how to export it, or if I should name it a certain thing.
Sorry, I should have gone over the necessities first. You'll need to download MCP. I'm putting up how to set it up now.
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
This looks great! Well right now I'm busy with making android apps, so I haven't been doing much with minecraft, but I will definitely look at this :smile.gif:
Rollback Post to RevisionRollBack
Same ****, different day - Modification Development Section
This looks great! Well right now I'm busy with making android apps, so I haven't been doing much with minecraft, but I will definitely look at this :smile.gif:
Thanks :smile.gif:
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
Added a crappy banner. Taking requests for tutorials.
Rollback Post to RevisionRollBack
“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination."
Your Forge registry class has almost nothing in common with your mod_ class from ModLoader except that it registers everything.
Firstly, just start with the package declaration as usual. You can use your own package but I will just use the default Minecraft source package for this example.
Next we need to import a few Forge classes.
All annotations are well explained in ghosrec35's Mod Annotation Interfaces tutorial(below)
The first annotation we will use is the @Mod annotation. We will use it to declare some information about our mod.
As an example, I use this in Undead+:
After you have the @Mod annotation we need to add the @NetworkMod annotation. We use this as well as the @Mod annotation because Forge mods are "universal"(one version for both Client and Server).
NetworkMod is explained in ghosrec35's tutorial below. You also need to create a PacketHandler, one is below:
After the @NetworkMod annotation you can declare the class. Note that with Forge, this class doesn't require a mod_ prefix or extend BaseMod.
We then need to add three methods. They are each called at specific times in the loading of the game. The methods can be called anything as long as they have the Forge annotation and parameter that is required. The first is preInit.
This method is called before the mods are loaded.
This method has the same function as the load() method is ModLoader. In here is where we will register all of our blocks, recipes, entities, etc.
This is the final method that is required in the Forge mod registry. Put stuff that you want to happen after the mods are loaded in here.
Final Code:
Mod Annotation Interfaces
The first thing to note is that there are only two attributes that are required with the @NetworkMod annotation.
@NetworkMod(clientSideRequired = true, serverSideRequired = false)
Both attributes must equal boolean values. These values determine whether a player is allowed to join a server or not. clientSideRequired should almost always be true, and serverSideRequired should almost always be false. clientSideRequired determines whether the client side of the mod must be installed for a player to join a server with the mod installed, while serverSideRequired determines whether the server side must be installed for the client to be able to join it.
We set clientSideRequired to true almost always because there are very few occassions in which nothing client side would be required for a mod.
We set serverSideRequired to false almost always because we will still want users to be able to join their favorite servers even if the server does not have the mod installed.
There are other attributes that are available for usage. These are channels, packetHandler, tinyPacketHandler, and connectionHandler. There are two more, but I will wait to discuss these until finished with the first three.
The channels attribute must be equivalent to an Array of Strings. This array will contain the Channels that will be automatically registered for your mod.
For Example:
DON'T register channels so generic as the one used in this tutorial. Always try to use channel names that are unique and original.
The packetHandler attribute must be equivalent to a class that implement IPacketHandler.
For Example:
Any Packets that are sent using the channels you registered in the channels attribute will be sent to this PacketHandler.
The connectionHandler attribute must be equivalent to a class that implements IConnectionHandler.
For Example:
The IConnectionHandler interface forces the ConnectionHandler class to override a few different methods that allow you to handle certain events with a player when they're connecting. Some more information on this in a future tutorial.
The tinyPacketHandler attribute must be equivalent to a class that implements ITinyPacketHandler. This works just like a regular PacketHandler, but for smaller packet sizes.
Final NetworkMod annotation so far:
Before we get to the final two attributes, it's important to note again that you only NEED the clientSideRequired and serverSideRequired attributes to run your mod. The rest are only required for things like SMP or Connection Handling.
The final two attributes are a little trickier than the rest. They are clientPacketHandlerSpec and serverPacketHandlerSpec.
These attributes allow you to define separate packet handling classes and channels for both the Server and Client Side. You may use these rather than defining the channels and packetHandler attributes.
Each must be equivalent to an @SidedPacketHandler annotation. We are then able to use the attributes of the @SidedPacketHandler Annotation to further define each. The two attributes are channels, and packetHandler. These attributes function the same as they do above, but when used in an @SidedPacketHandler annotation only register the information with one side of Minecraft.
For Example:
The next NetworkMod related annotation is the @SidedProxy annotation. @SidedProxy has a Target of the ElementType FIELD, meaning it must annotate a field. For our example, we are going to create a field of the type CommonProxy.
The two attributes of the @SidedProxy annotation are clientSide and serverSide. These two values are equivalent to String values and will contain the file path (including your package path) to your two separate proxies. More on this later.
You must create a file that will be your CommonProxy (Conventionally, this is usually called CommonProxy), and then you must create another file that will extend CommonProxy which will be your ClientProxy (Conventionally, this is usually called ClientProxy).
The @SidedProxy annotation requires the path to your ClientProxy and CommonProxy, as it automatically populates the proxy field of type CommonProxy with whichever proxy is designated for whichever side. This means that when invoking any methods, you must ensure that both the Client and Common Proxy contain the methods so that no exceptions occur during runtime on either the Server or Client. Proxys are used for such things as preloading textures, as you are able to register rendering information on only the client side by including the render methods in only the client proxy. The ClientProxy must extend the CommonProxy so that you are able to populate the field of the type CommonProxy with your ClientProxy as well.
The final @NetworkMod associated Annotation is the @SideOnly Annotation.
The @SideOnly annotation requires a value of the type Side, which is an enumeration containing the values CLIENT, SERVER, and BUKKIT. Annotating a field with @SideOnly ensures that the method is only run on the designated side. For instance, the vanilla Minecraft code has been edited throughout with over certain methods that are used for Client only details such as Rendering or Sound.
together they are powerful beyond imagination."
On hold until problems are resolved.
This tutorial has been long awaited. It will utilise Forge and it alone. If you are following this tutorial it is expected that you have already set up your Forge workspace and have the main registry class of your mod set up. When I say "the main registry class", I am referring to the class with the @Mod annotation and the three methods with the @Init, @PreInit and @PostInit annotations.
At first, it will be a basic tutorial on how to get the dimension up and running. After that you can customise it however you like. I plan to have customisation tutorials up soon but I would prefer just to get this bit done well first.Before we actually get to making the dimension, I'll give a bit of an explanation as to how they actually work.
Dimensions use something called a WorldProvider. The WorldProvider registers almost everything to do with the dimension. Functions the WorldProvider has include registering the chunk manager and provider for the world(more about this below), the angle the sky is rotated at and setting the light levels, biomes and fog levels; just to name a few.
The biome/s of the world is/are set in the WorldChunkManager. You could have one biome, or many. It is all up to you when you get to customising it. The chunk manager is registered in the WorldProvider class.
The terrain itself is set in the ChunkProvider for the world. The ChunkProvider allows possibly the most customisation out of every other class that you will make. It is registered in the WorldProvider class aswell. In the chunk provider you can change terrain levels, water/lava pools, villages, strongholds, dungeons, etc. and several other variables that are fun to mess around with.
The first thing you'll need to do is register a provider for your dimension.
The registerProviderType method accepts three arguments: An int, a class and a boolean. The int is the id you want to assign to the world provider. The class is the actual class of the world provider. The boolean is whether or not to keep the world loaded. That is, keep the world prebuffered so it loads quicker(needs citation). For example, the Overworld and Nether have this boolean set to true but the End has it set to false. This is because the Overworld and Nether are the main two dimensions of the game.
Next you will need to register the dimension itself and assign the world provider.
The registerDimension method accepts two ints for its arguments. The first is the id of the dimension. You are best of calling the getNextFreeDimId method here so that there are less incompatibilities. The second int is the providerType id you assigned to the world provider above. In our example we used 25.
This section is under work. I will try not to let it die like the gui tutorial did. By the time this tutorial is fully done, it will probably be my most comprehensive tutorial by far.
together they are powerful beyond imagination."
Just a few small notes to start with:
This is the basic ModLoader 'template'. It is fairly simple and I will explain what each of the parts do.
This line simply defines the package of the game that this file is placed in. There are several packages in Minecraft and different packages are called at different times.
'public class' defines that this is a class. It is a part of the Java language which Minecraft is written on. Other coding languages use it too. 'mod_***' defines that this is a ModLoader mod. The mod_ file is the most important part of a ModLoader mod. Without it, the mod will not work. 'extends BaseMod' allows you to use all of the ModLoader methods in the mod_ file. It is just as important as the class name starting with mod_ because your mod will not work without it. You must use a lowercase on
This is the constructor of a mod_ file. This is ModLoader's constructor. Do not use or include the java constructor in this file. The ModLoader methods go in between the two squiggly brackets.
This is used by ModLoader when it loads your mod. The version of Minecraft your mod is made for goes in here. A number/words in quotes should always be returned in this method. It should never return null.
Now change it to say ' = new Block(id, 0, Material.wood)'. So it would look like this:
You can then delete the separate class you made for the block. You can change the matieral like explained above.
This is the same as a blocks, except it doesn't have 'int j' or the material included. Items do not have a material. They also don't have an idDropped or quantityDropped method
Now change it to say ' = new Item(id)'. So it would look like this:
You can then delete the separate class you made for the item.
You will have one of these two for your block/item. Make sure you have a / in front of your image directory. If you are wondering what this means, or where you put your images, keep reading. Basically, this method just overrides the index of your item/blocks picture. The game would normally look in the terrain.png or items.png for an image, but because we're using ModLoader, and not editing any base classes or images, we add 'ModLoader.addOverride...'.
Your images can go in:
mcp/bin (If you have your textures here or the path below and are receiving crashes, put them in your jar)
mcp/bin/minecraft
mcp/jars/bin/minecraft.jar
If you're using Eclipse, they go here (Note: images can't go in the above directories when using eclipse):
mcp/eclipse/Client/bin (If you have your textures here and are receiving crashes, put them in your jar)
mcp/jars/bin/minecraft.jar
If my line looked like this:
and was using eclipse,
my picture would go in here:
mcp/eclipse/Client/bin/mods/flower.png
Its basically the same thing, if you're not using Eclipse. Using the example above, but without eclipse, my picture would go in here:
mcp/bin/minecraft/mods/flower.png
All images should be 16pixelsX16pixels and must be .png in format.
When packaging your mod for final release, if your path is
then you would put your texture called "flower" in a folder called "mods" in the zip that has all the classes in it.
I hope this has cleared things up for a few people wondering about their custom images.
That recipe would look like this:
Smelting recipes are fairly simple, 'ModLoader.addSmelting' is the ModLoader method for smelting recipes. '(Block.dirt.blockID, new ItemStack(Item.goldIngot, 1));' just says that putting a block of dirt into the furnace and smelting it, will result into a new stack being created of gold ingots. One ingot being added to the new stack every time a dirt block is smelted. The float at the end(1.0F) is the amount of XP received from smelting the item. As a reference, cobblestone is 0.1F and a gold ingot is 1.0F
That recipe would make an item from cocoa beans being alone in the crafting grid. This is the part for adding the dye as an ingredient.
It is basically a call for metadata, which is what the dye is stored in(multiple items, 1 id. 15:1, 15:2, etc) 1 is the quantity, and 3 is the colour of the dye. Even as an ingredient, you must have the quantity there. The colour of 3 is cocoa beans. All of the id's for dyes can be found here.
Applying a potion effect to a food. This is very easy to do. You simply make a food(follow the tutorial above), and add this bit of code to it:
so it would fit in like this:
Note that .setPotionEffect MUST go before .setItemName otherwise you will get recompilation errors. I use the hunger potion in this example, which is what is used in rotten flesh. You can use any potion effect when doing this. They are all listed in Potion.java. The 15 is the time the effect lasts for. You can replace the 0 with another number. It should amplify the effect/time it lasts for. An int of 1 here lasts for a second, 30 lasts for 30 seconds, etc. The float(1F) is the chance of the potion being successful. Rotten flesh has 0.8F here, and it has an 80% chance of giving you food poisoning. Having 1F in this position should be a 100% success rate.
Understanding the Code
NPC Information
The names of all biomes can be found in BiomeGenBase.
To make your mob hold something, simply add this code to its Entity*** class.
So that it fits in like this:
mod_Slab
I will be adding more ways to get the achievement as time goes on. Check back often for updates.
and change the "null" in the public static final line to the name you gave it before the "=" sign in the static line above. In this case it would be "name".
You change the "AchievementList.diamonds" to any other achievement in AchievementList.java. For example, you might want your achievement to need to have the "Getting Wood" achievement, so your static line of code would look like this:
You just use the name of the achievement from AchievementList.java in that space.
Below is an example of needing to have the "DIAMONDS!" achievement, to be able to get a new modded achievement, called "Diamond Hunter" which you receive from crafting a diamond block.
Here is a picture of that in-game:
After achievements required are completed
Thanks to 61352151511 for letting me build off his base diamond block achievement code, so I could make a good example for how to use offspring.
so that it fits in like this:
Using the example from above, it will now look like this:
I'm using the block tutorial for this, and adding some code for the textures.
This tutorial will build off the item tutorial.
Adding a fuel is fairly simple and self-explanatory. The only extra code you need to use is the addFuel method.
Adding blocks/items to the creative inventory in 1.4.7 is very simple. All you need to do is add in a piece of code:
It goes on the end of the public static final line of your block or item.
You can also put it in your block's class:
Or Item's class:
You need to replace tabNameHere with the name of the tab you want the block to display on, not the name of your block.
The building block tab is:
The decorations tab is:
The redstone tab is:
The transport tab is:
These are just a few of the tabs. All of the rest of the tabs can be found in the CreativeTabs.java file.
1.2.5
That is stupid and lazy. When you're modding, you should always aim not to edit ANY base class. If every mod was to edit ContainerCreative, only the mod that was last added would have its blocks added because the file would just get overridden each time. If you want to be an idiot, go right ahead and edit ContainerCreative, but don't leave a reply here saying something like "I know an easier way, add to ContainerCreative!". Leaving a reply like that just shows everyone that you have nothing better to do. I have an idea for you, go learn how to do stuff properly!
If you are smart and would like to add blocks to the creative inventory the proper way, keep reading.
This will just be a snippet of code(with an explanation, of course). It won't include any parts from the block tutorial. It will have a very simple explanation.
All of the code below goes into your mod_ class.
Firstly, you need to import Minecraft and List.
Then this code:
A few people were having trouble with knowing how to add more than one item/block/etc. to a single mod_ class so I made this to show how to do it. This isn't so much of a tutorial, it is more made as a display on how to do what I wrote above.
Completing the Mod
This section will explain how to finish your mod and allow you to distribute it in a playable form.
You can then distribute the mod to the public. Just upload the zip onto a website like MediaFire or put it in the public folder of your Dropbox. Then make a thread on the MinecraftForums Mods section detailing all your mod. Don't forget to put the link to the download there Make sure to follow the rules of the section when posting.
More tutorials on the post below! All new ones will be there too.
together they are powerful beyond imagination."
[center][spoiler][/center]
[center][b][color=#ff0000]I no longer support these tutorials.[/color][/b][/center]
[center][b]Ore Generation[/b][/center]
[spoiler]
This tutorial will build off the block tutorial.
[center][i]mod_OreGeneration[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class mod_OreGeneration extends BaseMod { public static final Block nameHere = new Block(160, 0, Material.rock).setBlockName("anyNameHere").setHardness(3F).setResistance(4F); public void load() { nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/imageblock.png"); ModLoader.registerBlock(nameHere); ModLoader.addName(nameHere, "In-Game Name Here"); ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt}); } public void generateSurface(World world, Random random, int chunkX, int chunkZ) { for(int i = 0; i < 7; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } } public String getVersion() { return "1.4.2; } }[/code]
[center][font=Impact]Understanding the Code[/font][/center]
As you'll notice, the generateSurface method has been added. All the information about this is as follows:[list]
[*]The 7 in the for loop is the rarity of your block when generated.
[*]The following three ints, as it says in the code, are the random positions that the blocks may be generated.
[*]In the randPosX and randPosZ lines, chunkX and chunkZ signify the edge of the chunk on those axes. The random.nextInt(16) parts means that any number up to 16 can be chosen. The number is chosen for how far in from the side of a chunk the block can be generated. Because a chunk is 16x16, having 16 in the brackets allows for the block to be generated anywhere in the chunk.
[*]The randPosY line is pretty much the same thing, except it goes up the vertical axis. Therefore, the number in the brackets is the height the block can be generated up to.
[*]The last line uses all the information your provide it with, in the random position ints.
[*]You put the block you want to generate in the brackets after "WorldGenMinable"
[*]The 25 inside the brackets is the size of the vein.
[*]The generate method in WorldGenMinable requires 5 arguments. Those arguments are World(The world of the game), Random(A Java class, read the info about it at the link at the bottom of the OP), randPosX, randPosY and randPosZ. That is where the information from the ints is actually called and used.
[/list]
[b]Note: Everytime that you change anything about the generation of your block in the generateSurface method in mod_***, you need to create a new world to see it.[/b]
[/spoiler]
[center][b]Structure Generation[/b][/center]
[spoiler]
[center][i]mod_WorldGen[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class mod_WorldGen extends BaseMod { public void load() { } public void generateSurface(World world, Random random, int chunkX, int chunkZ) { for(int k = 0; k < 10; k++) { int RandPosX = chunkX + random.nextInt(5); int RandPosY = random.nextInt(80); int RandPosZ = chunkZ + random.nextInt(5); (new WorldGenNameHere()).generate(world, random, RandPosX, RandPosY, RandPosZ); } } public String getVersion() { return "1.4.2"; } }[/code]
[center][i]WorldGenNamehere[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class WorldGenNameHere extends WorldGenerator { public WorldGenNameHere() { } public boolean generate(World world, Random random, int i, int j, int k) { int stoneBrick = Block.stoneBrick.blockID; world.setBlockWithNotify(i, j, k, stoneBrick); world.setBlockAndMetadataWithNotify(i, j + 1, k, stoneBrick, 1); return true; } }[/code]
[center][font=Impact]Understanding the Code[/font][/center]
[center][i]mod_WorldGen[/i][/center][list]
[*]This tutorial is fairly similar to the ore generation to the tutorial. The main changes are in the mod_ class.
[*]Just like the ore generation tutorial, Random is imported into the class.
[*]There is a slight change inside the generateSurface method.
[*]The line beginning with 'for' is a 'for loop'. The 10 in that line is the rarity of the structure.
[*]'RandPosX' and 'RandPosZ' mean to start the structure at any random position on that axis at least 5(or whatever the number is in the brackets) blocks away from the edge of the chunk. [u](Not 100% sure about this, I think it has more to do with the density of generation)[/u]
[/list][list]
[*]'RandPosY' means to place the structure anywhere on the y axis up to the layer in the brackets. (80 in this case)
[*][code](new WorldGenNamehere()).generate(world, random, RandPosX, RandPosY, RandPosZ);[/code] This line links it all together, and is where the structure in your WorldGenNamehere class is called.
[/list]
[center][i]WorldGenNameHere[/i][/center][list]
[*]Random is also imported in this class.
[*]This class extends WorldGenerator which is where the main methods are located. (The [i]generate[/i] method is mainly used)
[/list]
[code]public WorldGenNamehere() { }[list] [*][/code] This is the constructor of the class. We don't need to add anything into it for what we're doing.
[*]The next method is the [i]generate[/i] method, and is where the designing of our structure takes place.
[*]Using an int like this one [code]int stoneBrick = Block.stoneBrick.blockID;[/code] makes it a lot quicker to type things in. This is because we don't need to type Block.stoneBrick.blockID every time we want to generate that block. Instead, we just write [code]stoneBrick[/code] like is shown in the example code.
[*][code]world.setBlockWithNotify(i, j, k, stoneBrick); [/code] That is the main method used to generate blocks. The i, j and k are positions on the i, j, k (x, y, z, respectively) axes of the block.
[*]As your structures get bigger, these do too. You simply make the distance change by using a + (plus) or - (minus/hyphen) after the letter, and then putting a number, which is the number of blocks. For example: [code]world.setBlockWithNotify(i + 3, j + 2, k - 7, stoneBrick);[/code] That would place a block 3 blocks further away from the starting point on the i/x axis, it would be 2 blocks higher than the starting point(It will still be at the same i/k position), and 7 blocks along the k/z axis away from the starting point.
[*][code]world.setBlockAndMetadataWithNotify(i, j, k, stoneBrick, 1);[/code] You don't need to use this unless trying to generate blocks with a damage value. In this example, I am generating a stone brick block with the damage value of 1. A stone brick block with the damage value of 1 is mossy stone brick, 2 is cracked stone brick.
[/list]This is a picture of what is generated when using the example:
[img]http://i1217.photobucket.com/albums/dd385/TechGuy543/Tutorials/generation.png[/img]
As you can see, the amount of generated sets of blocks in the background is quite high. This can be changed by changing the number in the brackets, in the 'RandPosX' and 'RandPosY' lines, in the GenerateSurface method, in your mod_ class. You can also change the 10 in the [i]for[/i] loop line.
[b]Note: Everytime that you change your design in your WorldGenNamehere class, or the density of it in the generateSurface method in mod_, you need to create a new world to see it.[/b]
[/spoiler]
[center][b]Other World Generation[/b][/center]
[spoiler]
[center][i]Specific Biome Block Generation[/i][/center]
[center][size=x-small][i](Generating a block in a specific biome only)[/i][/size][/center]
[spoiler]
This tutorial will build off the Ore Generation tutorial, but can be used the same way with Structure tutorial.
[center][i]mod_OreGeneration[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class mod_OreGeneration extends BaseMod { public static final Block nameHere = new BlockNamehere(160, 0).setBlockName("anyNameHere").setHardness(3F).setResistance(4F).setLightValue(1F); public void load() { nameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/pathtoyourfile/image.png"); ModLoader.registerBlock(nameHere); ModLoader.addName(nameHere, "In-Game Name Here"); ModLoader.addRecipe(new ItemStack(nameHere, 1), new Object [] {"#", Character.valueOf('#'), Block.dirt}); } public void generateSurface(World world, Random random, int chunkX, int chunkZ) { BiomeGenBase biomegenbase = world.getWorldChunkManager().getBiomeGenAt(chunkX, chunkZ); if(biomegenbase instanceof BiomeGen***) { for(int i = 0; i < 7; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } } } public String getVersion() { return "1.4.2"; } }[/code]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
The only added code here is this:
[code]BiomeGenBase biomegenbase = world.getWorldChunkManager().getBiomeGenAt(chunkX, chunkZ); if(biomegenbase instanceof BiomeGen***) { //... code from previous tutorial/s }[/code]
Basically, it creates an instance called biomegenbase from the BiomeGenBase type, it then gets the current biome at the location from the world's chunk manager. The next line just gets the BiomeGen class so you can use it from a static context. The third line, the if statement, just uses the biomegenbase instance, and checks it is equivalent to the BiomeGen*** class, then it continues on with the generation.
Obviously, you change the asterisks in the BiomeGen*** parts to the actual name of a BiomeGen class.
I know this wasn't a very good explanation and I didn't really use the correct names for certain things, but I think the methods that are called basically explain what they actually do anyway. Self-explanatory!
[/spoiler]
[center][i]Ore Generation in Nether[/i][/center]
[spoiler]
This tutorial will build off of the ore generation tutorial.
[center][i]WorldGenNetherMinableNameHere[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class WorldGenNetherMinableNameHere extends WorldGenerator { /** The block ID of the ore to be placed using this generator. */ private int minableBlockId; /** The number of blocks to generate. */ private int numberOfBlocks; public WorldGenNetherMinableNameHere(int par1, int par2) { minableBlockId = par1; numberOfBlocks = par2; } public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5) { float f = par2Random.nextFloat() * (float)Math.PI; double d = (float)(par3 + 8) + (MathHelper.sin(f) * (float)numberOfBlocks) / 8F; double d1 = (float)(par3 + 8) - (MathHelper.sin(f) * (float)numberOfBlocks) / 8F; double d2 = (float)(par5 + 8) + (MathHelper.cos(f) * (float)numberOfBlocks) / 8F; double d3 = (float)(par5 + 8) - (MathHelper.cos(f) * (float)numberOfBlocks) / 8F; double d4 = (par4 + par2Random.nextInt(3)) - 2; double d5 = (par4 + par2Random.nextInt(3)) - 2; for (int i = 0; i <= numberOfBlocks; i++) { double d6 = d + ((d1 - d) * (double)i) / (double)numberOfBlocks; double d7 = d4 + ((d5 - d4) * (double)i) / (double)numberOfBlocks; double d8 = d2 + ((d3 - d2) * (double)i) / (double)numberOfBlocks; double d9 = (par2Random.nextDouble() * (double)numberOfBlocks) / 16D; double d10 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D; double d11 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D; int j = MathHelper.floor_double(d6 - d10 / 2D); int k = MathHelper.floor_double(d7 - d11 / 2D); int l = MathHelper.floor_double(d8 - d10 / 2D); int i1 = MathHelper.floor_double(d6 + d10 / 2D); int j1 = MathHelper.floor_double(d7 + d11 / 2D); int k1 = MathHelper.floor_double(d8 + d10 / 2D); for (int l1 = j; l1 <= i1; l1++) { double d12 = (((double)l1 + 0.5D) - d6) / (d10 / 2D); if (d12 * d12 >= 1.0D) { continue; } for (int i2 = k; i2 <= j1; i2++) { double d13 = (((double)i2 + 0.5D) - d7) / (d11 / 2D); if (d12 * d12 + d13 * d13 >= 1.0D) { continue; } for (int j2 = l; j2 <= k1; j2++) { double d14 = (((double)j2 + 0.5D) - d8) / (d10 / 2D); if (d12 * d12 + d13 * d13 + d14 * d14 < 1.0D && par1World.getBlockId(l1, i2, j2) == Block.netherrack.blockID) { par1World.setBlock(l1, i2, j2, minableBlockId); } } } } } return true; } }[/code]
That is the only additional class we need. Now we just change the generateSurface method to generateNether and call this class instead of WorldGenMinable.
[code](new WorldGenNetherMinableNameHere(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ);[/code]
So that it fits in like this:
[code] public void generateNether(World world, Random random, int chunkX, int chunkZ) { for(int i = 0; i < 7; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenNetherMinableNameHere(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } }[/code]
[center][size=large][font=Impact]Understanding the Code[/font][/size][/center]
[center]This is very similar to generating an ore normally but there is one difference.[/center][list]
[*]We have to create and call our own WorldGenMinable class because we have to change it to generate our blocks around Block.netherrack instead of Block.stone. Where you see Block.netherrack in the WorldGenNetherMinableNameHere class is where the change occurred.
[*]Ask if you need a better explanation.
[/list]
[/spoiler]
[center]Multiple Blocks/Structures in One Class[/center]
[spoiler]
I have been receiving several messages a day about how to do this so I decided to make a tutorial on it. It is quite simple Java work to be able to do this. I really recommend you learn at least some basic Java.
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ) { for(int i = 0; i < 7; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } for(int i = 0; i < 9; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere2.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } }[/code]
That is what the generateSurface method would look like with two blocks generating. It is simply adding another for loop for each block or structure. You can edit the generation rates as normal for each one.
With three blocks generating:
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ) { for(int i = 0; i < 7; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(35); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere.blockID, 25)).generate(world, random, randPosX, randPosY, randPosZ); } for(int i = 0; i < 4; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere2.blockID, 19)).generate(world, random, randPosX, randPosY, randPosZ); } for(int i = 0; i < 2; i++) { int randPosX = chunkX + random.nextInt(16); int randPosY = random.nextInt(128); int randPosZ = chunkZ + random.nextInt(16); (new WorldGenMinable(nameHere3.blockID, 13)).generate(world, random, randPosX, randPosY, randPosZ); } }[/code]
The exact same thing is done for structures:
[code]public void generateSurface(World world, Random random, int chunkX, int chunkZ) { for(int k = 0; k < 10; k++) { int RandPosX = chunkX + random.nextInt(5); int RandPosY = random.nextInt(80); int RandPosZ = chunkZ + random.nextInt(5); (new WorldGenNameHere()).generate(world, random, RandPosX, RandPosY, RandPosZ); } for(int k = 0; k < 10; k++) { int RandPosX = chunkX + random.nextInt(5); int RandPosY = random.nextInt(80); int RandPosZ = chunkZ + random.nextInt(5); (new WorldGenNameHere2()).generate(world, random, RandPosX, RandPosY, RandPosZ); } }[/code]
I hope this clears it up a bit for everyone asking.
[/spoiler]
[/spoiler]
[center][b]Biome[/b][/center]
[spoiler]
[center]This is quite a basic tutorial on a biome. There are heaps of variables you can use to change your biome around and make it really different and creative. Let your imagination run wild![/center]
[center][i]mod_Biome[/i][/center]
[code]package net.minecraft.src; public class mod_Biome extends BaseMod { public static final BiomeGenBase nameHere = (new BiomeGenNameHere(25)).setColor(0xfa9418).setBiomeName("Any Name Here"); public void load() { ModLoader.addBiome(nameHere); } public String getVersion() { return "1.4.2"; } }[/code]
[center][i]BiomeGenNamehere[/i][/center]
[code]package net.minecraft.src; public class BiomeGenNameHere extends BiomeGenBase { public BiomeGenNameHere(int par1) { super(par1); spawnableCreatureList.clear(); topBlock = (byte)Block.blockGold.blockID; fillerBlock = (byte)Block.blockSteel.blockID; theBiomeDecorator.treesPerChunk = 5; theBiomeDecorator.flowersPerChunk = 4; theBiomeDecorator.grassPerChunk = 10; } }[/code]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
[center][i]mod_Biome[/i][/center][list]
[*][code]public static final BiomeGenBase nameHere= (new BiomeGenNamehere(25)).setColor(0xfa9418).setBiomeName("Any Name Here");[/code]
[*]"BiomeGenBase" is put before the name of your instance just like items, blocks and achievements, etc.
[*]The number in brackets after the name of your new BiomeGen class is the ID of the new biome. This must be more than 23(that is the currently the highest uses biome ID) and less than 256.
[*]I think the ".setColor()" part of this line is optional. I believe it basically sets the ambience/colour around you when you're in it. You can find some colours [url="http://mindprod.com/jgloss/netscapehtml.html"]here[/url].
[*]".setBiomeName("")" is the name of the biome(obviously). It is mainly used in the F3 Debug screen where it tells you what biome you're in.
[/list][list]
[*][code]ModLoader.addBiome(nameHere);[/code]
[*]This is the ModLoader string for adding a biome to biomesList[] array and the other classes needed so that the biome actually generates with the world.
[/list]
[center][i]BiomeGenNamehere[/i][/center][list]
[*]This is a basic class that extends BiomeGenBase.
[/list][list]
[*][code]spawnableCreatureList.clear();[/code]
[*]That basically clears the list of creatures that can spawn in the biome.
[*]Have a look in the other BiomeGen*** classes on how you can make certain mobs spawn in your biome.
[/list][list]
[*][code]topBlock = (byte)Block.blockGold.blockID; fillerBlock = (byte)Block.blockSteel.blockID;[/code]
[*]"topBlock" is the block in the biome that is the top 1-5 blocks.
[*]"fillerBlock" is the block that fills the rest of the space until stone.
[/list][list]
[*][code]biomeDecorator.treesPerChunk = 5; biomeDecorator.flowersPerChunk = 4; biomeDecorator.grassPerChunk = 10;[/code]
[*]I think these lines are fairly self-explanatory. Please ask if you need an explanation though.
[*]Some other biomeDecorator strings/methods you can use include:
[/list]
[code]biomeDecorator.deadBushPerChunk = 2; biomeDecorator.reedsPerChunk = 50; biomeDecorator.cactiPerChunk = 10; biomeDecorator.mushroomsPerChunk = 8; biomeDecorator.clayPerChunk = 1; biomeDecorator.waterlilyPerChunk = 4;[/code]
[center][size=large][u][b][color=#FF0000]Keep Reading Below If You Receive A Crash When Generating a New World With Your Biome.[/color][/b][/u][/size][/center]
If you get a crash similar to the one below when generating a new world with your biome added into the generation, put the id of the block that you are generating below 127. The reason behind this is that a byte's limit in Java is -128 to 127. When you go over that 127, you'll most likely get a crash.[code]java.lang.ArrayIndexOutOfBoundsException: -96 at net.minecraft.src.ExtendedBlockStorage.setExtBlockID(ExtendedBlockStorage.java:83) at net.minecraft.src.Chunk.<init>(Chunk.java:113) at net.minecraft.src.ChunkProviderGenerate.provideChunk(ChunkProviderGenerate.java:290) at net.minecraft.src.ChunkProvider.loadChunk(ChunkProvider.java:93) at net.minecraft.src.ChunkProvider.provideChunk(ChunkProvider.java:119) at net.minecraft.src.World.getChunkFromChunkCoords(World.java:654) at net.minecraft.src.World.tickBlocksAndAmbiance(World.java:3104) at net.minecraft.src.World.tick(World.java:2872) at net.minecraft.client.Minecraft.runTick(Minecraft.java:1903) at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:870) at net.minecraft.client.Minecraft.run(Minecraft.java:801) at java.lang.Thread.run(Thread.java:680) --- END ERROR REPORT 75039747 ----------[/code]
[/spoiler]
[center][color=#000000][b]Tools[/b][/color][/center]
[spoiler]
Due to the amount of code required for this, I've put it in another spoiler.
[spoiler]
[center]
[i]mod_Tools[/i][/center]
[code]package net.minecraft.src; public class mod_Tools extends BaseMod { public static final Item pickaxe = new ItemNameherePickaxe(2000, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("pickaxe"); public static final Item axe = new ItemNamehereAxe(2001, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("axe"); public static final Item shovel = new ItemNamehereShovel(2002, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("shovel"); public static final Item sword = new ItemNamehereSword(2003, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("sword"); public static final Item hoe = new ItemNamehereHoe(2004, EnumToolMaterialNamehere.MATERIALNAMEHERE).setItemName("hoe"); public void load() { pickaxe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/pickaxeimage.png"); axe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/axeimage.png"); shovel.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/shovelimage.png"); sword.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/swordimage.png"); hoe.iconIndex = ModLoader.addOverride("/gui/items.png", "/path/to/your/file/hoeimage.png"); ModLoader.addName(pickaxe, "Pickaxe"); ModLoader.addName(axe, "Axe"); ModLoader.addName(shovel, "Shovel"); ModLoader.addName(sword, "Sword"); ModLoader.addName(hoe, "Hoe"); ModLoader.addRecipe(new ItemStack(pickaxe, 1), new Object [] {"###", " % ", " % ", '#', Block.dirt, '%', Item.stick}); ModLoader.addRecipe(new ItemStack(axe, 1), new Object [] {"##", "#%", " %", '#', Block.dirt, '%', Item.stick}); ModLoader.addRecipe(new ItemStack(shovel, 1), new Object [] {"#", "%", "%", '#', Block.dirt, '%', Item.stick}); ModLoader.addRecipe(new ItemStack(sword, 1), new Object [] {"#", "#", "%", '#', Block.dirt, '%', Item.stick}); ModLoader.addRecipe(new ItemStack(hoe, 1), new Object [] {"##", " %", " %", '#', Block.dirt, '%', Item.stick}); } public String getVersion() { return "1.4.2"; } }[/code]
[center]
[i]ItemNamehereTool[/i][/center]
[code]package net.minecraft.src; public class ItemNamehereTool extends Item { private Block blocksEffectiveAgainst[]; protected float efficiencyOnProperMaterial; private int damageVsEntity; protected EnumToolMaterialNamehere toolMaterial; protected ItemNamehereTool(int i, int j, EnumToolMaterialNamehere par3EnumToolMaterialNamehere, Block par4ArrayOfBlock[]) { super(i); efficiencyOnProperMaterial = 4F; toolMaterial = par3EnumToolMaterialNamehere; blocksEffectiveAgainst = par4ArrayOfBlock; maxStackSize = 1; setMaxDamage(par3EnumToolMaterialNamehere.getMaxUses()); efficiencyOnProperMaterial = par3EnumToolMaterialNamehere.getEfficiencyOnProperMaterial(); damageVsEntity = j + par3EnumToolMaterialNamehere.getDamageVsEntity(); } /** * Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if * sword */ public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block) { for (int i = 0; i < blocksEffectiveAgainst.length; i++) { if (blocksEffectiveAgainst[i] == par2Block) { return efficiencyOnProperMaterial; } } return 1.0F; } /** * Current implementations of this method in child classes do not use the entry argument beside ev. They just raise * the damage on the stack. */ public boolean hitEntity(ItemStack par1ItemStack, EntityLiving par2EntityLiving, EntityLiving par3EntityLiving) { par1ItemStack.damageItem(2, par3EntityLiving); return true; } //If someone decompiles a more updated MCP after the time of writing this tutorial and you get an error on this method, please let me know via either a PM or just a post on this topic. The method hasn't been named yet so it is highly likely in a forthcoming MCP update it will be. public boolean onBlockDestroyed(ItemStack par1ItemStack, World par2World, int par3, int par4, int par5, int par6, EntityLiving par7EntityLiving) { if ((double)Block.blocksList[par3].getBlockHardness(par2World, par4, par5, par6) != 0.0D) { par1ItemStack.damageItem(1, par7EntityLiving); } return true; } return true; } /** * Returns the damage against a given entity. */ public int getDamageVsEntity(Entity par1Entity) { return damageVsEntity; } /** * Returns True is the item is renderer in full 3D when hold. */ public boolean isFull3D() { return true; } /** * Return the enchantability factor of the item, most of the time is based on material. */ public int getItemEnchantability() { return toolMaterial.getEnchantability(); } }[/code]
[center]
[i]EnumToolMaterialNamehere[/i][/center]
[code]package net.minecraft.src; public enum EnumToolMaterialNamehere { MATERIALNAMEHERE(0, 59, 2.0F, 0, 15); private final int harvestLevel; private final int maxUses; private final float efficiencyOnProperMaterial; private final int damageVsEntity; private final int enchantability; private EnumToolMaterialNamehere(int par3, int par4, float par5, int par6, int par7) { harvestLevel = par3; maxUses = par4; efficiencyOnProperMaterial = par5; damageVsEntity = par6; enchantability = par7; } public int getMaxUses() { return maxUses; } public float getEfficiencyOnProperMaterial() { return efficiencyOnProperMaterial; } public int getDamageVsEntity() { return damageVsEntity; } public int getHarvestLevel() { return harvestLevel; } public int getEnchantability() { return enchantability; } }[/code]
[center]
[i]ItemNameherePickaxe[/i][/center]
[code]package net.minecraft.src; public class ItemNameherePickaxe extends ItemNamehereTool { private static Block[] blocksEffectiveAgainst = new Block[] {Block.cobblestone, Block.stoneDoubleSlab, Block.stoneSingleSlab, Block.stone, Block.sandStone, Block.cobblestoneMossy, Block.oreIron, Block.blockSteel, Block.oreCoal, Block.blockGold, Block.oreGold, Block.oreDiamond, Block.blockDiamond, Block.ice, Block.netherrack, Block.oreLapis, Block.blockLapis, Block.oreRedstone, Block.oreRedstoneGlowing, Block.rail, Block.railDetector, Block.railPowered}; protected ItemNameherePickaxe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere) { super(par1, 2, par2EnumToolMaterialNamehere, blocksEffectiveAgainst); } /** * Returns if the item (tool) can harvest results from the block type. */ public boolean canHarvestBlock(Block par1Block) { if (par1Block == Block.obsidian) { return toolMaterial.getHarvestLevel() == 3; } if (par1Block == Block.blockDiamond || par1Block == Block.oreDiamond) { return toolMaterial.getHarvestLevel() >= 2; } if (par1Block == Block.blockGold || par1Block == Block.oreGold) { return toolMaterial.getHarvestLevel() >= 2; } if (par1Block == Block.blockSteel || par1Block == Block.oreIron) { return toolMaterial.getHarvestLevel() >= 1; } if (par1Block == Block.blockLapis || par1Block == Block.oreLapis) { return toolMaterial.getHarvestLevel() >= 1; } if (par1Block == Block.oreRedstone || par1Block == Block.oreRedstoneGlowing) { return toolMaterial.getHarvestLevel() >= 2; } if (par1Block.blockMaterial == Material.rock) { return true; } return par1Block.blockMaterial == Material.iron; } /** * Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if * sword */ public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block) { if (par2Block != null && (par2Block.blockMaterial == Material.iron || par2Block.blockMaterial == Material.rock)) { return efficiencyOnProperMaterial; } else { return super.getStrVsBlock(par1ItemStack, par2Block); } } }[/code]
[center]
[i]ItemNamehereAxe[/i][/center]
[code]package net.minecraft.src; public class ItemNamehereAxe extends ItemNamehereTool { private static Block[] blocksEffectiveAgainst = new Block[] {Block.planks, Block.bookShelf, Block.wood, Block.chest, Block.stoneDoubleSlab, Block.stoneSingleSlab, Block.pumpkin, Block.pumpkinLantern}; protected ItemNamehereAxe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere) { super(par1, 3, par2EnumToolMaterialNamehere, blocksEffectiveAgainst); } /** * Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if * sword */ public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block) { if (par2Block != null && par2Block.blockMaterial == Material.wood) { return efficiencyOnProperMaterial; } else { return super.getStrVsBlock(par1ItemStack, par2Block); } } }[/code]
[center]
[i]ItemNamehereShovel[/i][/center]
[code]package net.minecraft.src; public class ItemNamehereShovel extends ItemNamehereTool { private static Block blocksEffectiveAgainst[]; public ItemNamehereShovel(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere) { super(par1, 1, par2EnumToolMaterialNamehere, blocksEffectiveAgainst); } /** * Returns if the item (tool) can harvest results from the block type. */ public boolean canHarvestBlock(Block par1Block) { if (par1Block == Block.snow) { return true; } return par1Block == Block.blockSnow; } static { blocksEffectiveAgainst = (new Block[] { Block.grass, Block.dirt, Block.sand, Block.gravel, Block.snow, Block.blockSnow, Block.blockClay, Block.tilledField, Block.slowSand, Block.mycelium }); } }[/code]
[center]
[i]ItemNamehereSword[/i][/center]
[code]package net.minecraft.src; public class ItemNamehereSword extends Item { private int weaponDamage; private final EnumToolMaterialNamehere toolMaterial; public ItemNamehereSword(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere) { super(par1); toolMaterial = par2EnumToolMaterialNamehere; maxStackSize = 1; setMaxDamage(par2EnumToolMaterialNamehere.getMaxUses()); weaponDamage = 4 + par2EnumToolMaterialNamehere.getDamageVsEntity(); } /** * Returns the strength of the stack against a given block. 1.0F base, (Quality+1)*2 if correct blocktype, 1.5F if * sword */ public float getStrVsBlock(ItemStack par1ItemStack, Block par2Block) { return par2Block.blockID != Block.web.blockID ? 1.5F : 15F; } /** * Current implementations of this method in child classes do not use the entry argument beside ev. They just raise * the damage on the stack. */ public boolean hitEntity(ItemStack par1ItemStack, EntityLiving par2EntityLiving, EntityLiving par3EntityLiving) { par1ItemStack.damageItem(1, par3EntityLiving); return true; } public boolean onBlockDestroyed(ItemStack par1ItemStack, int par2, int par3, int par4, int par5, EntityLiving par6EntityLiving) { par1ItemStack.damageItem(2, par6EntityLiving); return true; } /** * Returns the damage against a given entity. */ public int getDamageVsEntity(Entity par1Entity) { return weaponDamage; } /** * Returns True is the item is renderer in full 3D when hold. */ public boolean isFull3D() { return true; } /** * returns the action that specifies what animation to play when the items is being used */ public EnumAction getItemUseAction(ItemStack par1ItemStack) { return EnumAction.block; } /** * How long it takes to use or consume an item */ public int getMaxItemUseDuration(ItemStack par1ItemStack) { return 0x11940; } /** * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer */ public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) { par3EntityPlayer.setItemInUse(par1ItemStack, getMaxItemUseDuration(par1ItemStack)); return par1ItemStack; } /** * Returns if the item (tool) can harvest results from the block type. */ public boolean canHarvestBlock(Block par1Block) { return par1Block.blockID == Block.web.blockID; } /** * Return the enchantability factor of the item, most of the time is based on material. */ public int getItemEnchantability() { return toolMaterial.getEnchantability(); } }[/code]
[center]
[i]ItemNamehereHoe[/i][/center]
[code]package net.minecraft.src; public class ItemNamehereHoe extends Item { public ItemNamehereHoe(int par1, EnumToolMaterialNamehere par2EnumToolMaterialNamehere) { super(par1); maxStackSize = 1; setMaxDamage(par2EnumToolMaterialNamehere.getMaxUses()); } public boolean tryPlaceIntoWorld(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10) { if (!par2EntityPlayer.canPlayerEdit(par4, par5, par6)) { return false; } int i = par3World.getBlockId(par4, par5, par6); int j = par3World.getBlockId(par4, par5 + 1, par6); if (par7 != 0 && j == 0 && i == Block.grass.blockID || i == Block.dirt.blockID) { Block block = Block.tilledField; par3World.playSoundEffect((float)par4 + 0.5F, (float)par5 + 0.5F, (float)par6 + 0.5F, block.stepSound.getStepSound(), (block.stepSound.getVolume() + 1.0F) / 2.0F, block.stepSound.getPitch() * 0.8F); if (par3World.isRemote) { return true; } else { par3World.setBlockWithNotify(par4, par5, par6, block.blockID); par1ItemStack.damageItem(1, par2EntityPlayer); return true; } } else { return false; } } public boolean isFull3D() { return true; } }[/code]
[/spoiler]
[center][font=Impact][size=large]Understanding the Code[/size][/font][/center]
[color=#008000][b]Notes:[/b][/color][list]
[*][color=#008000][b]A lot of the code is already explained in the comments above methods so I won't explain that part of code down here.[/b][/color]
[*][b]These tools aren't enchantable. Don't ask me how to make them enchantable.[/b]
[/list]
[center][i]mod_Tools[/i][/center][list]
[*]This is a fairly normal mod_ class. It is important to remember that tools are just items with special features.
[*]In the line that you declare the tool, there is an additional argument to a normal item. The new argument is the tools material which is stored in an Enum.
[*]Rest of code is same as items.
[/list]
I think most of the rest of the code is explained in comments. Just ask me if you need an explanation of it anyway. I suppose this isn't so much of a tutorial on my part but the Javadoc comments explain it just as well as I could, if not better.
[/spoiler]
[center][b]Crops [color=#FF0000](Broken)[/color][/b][/center]
[spoiler]
[center][i]mod_Crop[/i][/center]
[code]package net.minecraft.src; public class mod_Crop extends BaseMod { //Just a standard block. public static final Block cropNameHere = new BlockCropNameHere(165, 0).setBlockName("cropNameHere"); //This is a fairly standard item but it has two new arguments. The first is the block it plants, in this case is our new crop "cropNameHere". The second is the block that is required to be right clicked on to plant the seeds. //We are using tilled field/dirt for this crop but you can change it to whatever you like. Remember to change it in the block class as well. public static final Item seedsNameHere = new ItemSeedsNameHere(2500, cropNameHere.blockID, Block.tilledField.blockID).setItemName("seedsNameHere"); //These static ints are the textures of the crop in its various stages of growth. public static int cropStageOne = ModLoader.addOverride("/terrain.png", "/stageOne.png"); public static int cropStageTwo = ModLoader.addOverride("/terrain.png", "/stageTwo.png"); public static int cropStageThree = ModLoader.addOverride("/terrain.png", "/stageThree.png"); public static int cropStageFour = ModLoader.addOverride("/terrain.png", "/stageFour.png"); public static int cropStageFive = ModLoader.addOverride("/terrain.png", "/stageFive.png"); public static int cropStageSix = ModLoader.addOverride("/terrain.png", "/stageSix.png"); public static int cropStageSeven = ModLoader.addOverride("/terrain.png", "/stageSeven.png"); public static int cropStageEight = ModLoader.addOverride("/terrain.png", "/stageEight.png"); public void load() { cropNameHere.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/crop.png"); ModLoader.registerBlock(cropNameHere); ModLoader.addName(cropNameHere, "In-Game Name Here"); seedsNameHere.iconIndex = ModLoader.addOverride("/gui/items.png", "/seeds.png"); ModLoader.addName(seedsNameHere, "In-Game Name Here"); ModLoader.addRecipe(new ItemStack(seedsNameHere), new Object [] {"#", '#', Block.dirt}); } public String getVersion() { return "1.4.2"; } }[/code]
[center][i]BlockCropNameHere[/i][/center]
[code]package net.minecraft.src; import java.util.Random; public class BlockCropNameHere extends BlockFlower { public BlockCropNameHere(int i, int j) { super(i, j); blockIndexInTexture = j; setTickRandomly(true); float f = 0.5F; setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f); } /** * Gets passed in the blockID of the block below and supposed to return true if its allowed to grow on the type of * blockID passed in. Args: blockID. * This basically checks to see if the block below is a tilled field/tilled dirt. If it is true then the crop can grow. */ protected boolean canThisPlantGrowOnThisBlockID(int par1) { return par1 == Block.tilledField.blockID; } /** * Ticks the block if it's been scheduled. This method gets scheduled to run because of the setTickRandomly part in the constructor of the class. */ public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) { super.updateTick(par1World, par2, par3, par4, par5Random); if (par1World.getBlockLightValue(par2, par3 + 1, par4) >= 9) { int i = par1World.getBlockMetadata(par2, par3, par4); if (i < 8) { float f = getGrowthRate(par1World, par2, par3, par4); if (par5Random.nextInt((int)(25F / f) + 1) == 0) { i++; par1World.setBlockMetadataWithNotify(par2, par3, par4, i); } } } } /** * This method allows you to use bonemeal on your crop. Code explanation below: ItemStack itemstack = entityplayer.inventory.getCurrentItem(); - This line makes "itemstack" equal to the item currently in the players hand. if(itemstack != null && itemstack.itemID == Item.dyePowder.itemID) - This line checks if the item in players hand is equal to dye. if(itemstack.getItemDamage() == 15) - This line checks if the damage value of that item is 15. Item.dyePowder's damage value of 15 is bonemeal. world.setBlockMetadataWithNotify(i, j, k, 8); - This line sets the metadata value of the block to 8 which is the final growth stage. itemstack.stackSize--; - This line makes the stack size go down by one. world.notifyBlockChange(i, j, k, 0); - This line notifys adjacent blocks that this block has updated its state. */ public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) { ItemStack itemstack = par5EntityPlayer.inventory.getCurrentItem(); if(itemstack != null && itemstack.itemID == Item.dyePowder.[size=medium]itemID[/size]) { if(itemstack.getItemDamage() == 15) { par1World.setBlockMetadataWithNotify(par2, par3, par4, 8); itemstack.stackSize--; par1World.notifyBlockChange(par2, par3, par4, 0); } } super.onBlockActivated(par1World, par2, par3, par4, par5EntityPlayer, par6, par7, par8, par9); return true; } /** * Gets the growth rate for the crop. Setup to encourage rows by halving growth rate if there is diagonals, crops on * different sides that aren't opposing, and by adding growth for every crop next to this one (and for crop below * this one). Args: x, y, z */ private float getGrowthRate(World par1World, int par2, int par3, int par4) { float f = 1.0F; int i = par1World.getBlockId(par2, par3, par4 - 1); int j = par1World.getBlockId(par2, par3, par4 + 1); int k = par1World.getBlockId(par2 - 1, par3, par4); int l = par1World.getBlockId(par2 + 1, par3, par4); int i1 = par1World.getBlockId(par2 - 1, par3, par4 - 1); int j1 = par1World.getBlockId(par2 + 1, par3, par4 - 1); int k1 = par1World.getBlockId(par2 + 1, par3, par4 + 1); int l1 = par1World.getBlockId(par2 - 1, par3, par4 + 1); boolean flag = k == blockID || l == blockID; boolean flag1 = i == blockID || j == blockID; boolean flag2 = i1 == blockID || j1 == blockID || k1 == blockID || l1 == blockID; for (int i2 = par2 - 1; i2 <= par2 + 1; i2++) { for (int j2 = par4 - 1; j2 <= par4 + 1; j2++) { int k2 = par1World.getBlockId(i2, par3 - 1, j2); float f1 = 0.0F; if (k2 == Block.tilledField.blockID) { f1 = 1.0F; if (par1World.getBlockMetadata(i2, par3 - 1, j2) > 0) { f1 = 3F; } } if (i2 != par2 || j2 != par4) { f1 /= 4F; } f += f1; } } if (flag2 || flag && flag1) { f /= 2.0F; } return f; } /** * The type of render function that is called for this block. The render type of 6 gets the texture and places it four times around the sides of the block and leaves nothing on the top or bottom. */ public int getRenderType() { return 6; } /** * Drops the block items with a specified chance of dropping the specified items */ public void dropBlockAsItemWithChance(World par1World, int par2, int par3, int par4, int par5, float par6, int par7) { super.dropBlockAsItemWithChance(par1World, par2, par3, par4, par5, par6, 0); if (par1World.isRemote) { return; } int i = 3 + par7; for (int j = 0; j < i; j++) { if (par1World.rand.nextInt(15) <= par5) { float f = 0.7F; float f1 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F; float f2 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F; float f3 = par1World.rand.nextFloat() * f + (1.0F - f) * 0.5F; EntityItem entityitem = new EntityItem(par1World, (float)par2 + f1, (float)par3 + f2, (float)par4 + f3, new ItemStack(mod_Crop.seedsNameHere)); entityitem.delayBeforeCanPickup = 10; par1World.spawnEntityInWorld(entityitem); } } } /** * Returns the ID of the items to drop on destruction. "i" is equal to the blocks metadata value(explained slightly more in the getBlockTextureFromSideAndMetadata method below). This means that it will check that that value is equal to 8(the final stage of growth) and if it is then it will drop wheat. It may be fairly obvious, but the 'else' statement means that if the growth state is not equal to 7 then drop nothing (-1 means nothing) */ public int idDropped(int i, Random random, int j) { if (i == 8) { return Item.wheat.itemID; } else { return -1; } } /** * Returns the quantity of items to drop on block destruction. */ public int quantityDropped(Random random) { return 1; } /** * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata. * As you may have been able to tell from the line above, "j" is equal to the metadata value of the block. This checks if that value is equal to a certain number then sets the blocks texture to what you have defined. * The things that are being returned are the ints in your mod_ class which you created and set to your texture for the specific stages of growth. */ public int getBlockTextureFromSideAndMetadata(int i, int j) { if(j == 0) { return blockIndexInTexture; } if(j == 1) { return mod_Crop.cropStageOne; } if(j == 2) { return mod_Crop.cropStageTwo; } if(j == 3) { return mod_Crop.cropStageThree; } if(j == 4) { return mod_Crop.cropStageFour; } if(j == 5) { return mod_Crop.cropStageFive; } if(j == 6) { return mod_Crop.cropStageSix; } if(j == 7) { return mod_Crop.cropStageSeven; } if(j == 8) { return mod_Crop.cropStageEight; } return j; } }[/code]
[center][i]ItemSeedsNameHere[/i][/center]
[code]package net.minecraft.src; public class ItemSeedsNameHere extends Item { /** The type of block this seed turns into (wheat or pumpkin stems for instance)*/ private int blockType; /** BlockID of the block the seeds can be planted on. */ private int soilBlockID; public ItemSeedsNameHere(int i, int j, int k) { super(i); blockType = j; soilBlockID = k; } /** * Callback for item usage. If the item does something special on right clicking, he will have one of those. Return * True if something happen and false if it don't. This is for ITEMS, not BLOCKS ! */ public boolean tryPlaceIntoWorld(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10) { if (par7 != 1) { return false; } if (!par2EntityPlayer.canPlayerEdit(par4, par5, par6) || !par2EntityPlayer.canPlayerEdit(par4, par5 + 1, par6)) { return false; } int i = par3World.getBlockId(par4, par5, par6); if (i == soilBlockID && par3World.isAirBlock(par4, par5 + 1, par6)) { par3World.setBlockWithNotify(par4, par5 + 1, par6, blockType); par1ItemStack.stackSize--; return true; } else { return false; } } }[/code]
This crop will have 8 stages. You can change the amount in the code in this method, from the block class:
[code]public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) { super.updateTick(par1World, par2, par3, par4, par5Random); if (par1World.getBlockLightValue(par2, par3 + 1, par4) >= 9) { int i = par1World.getBlockMetadata(par2, par3, par4); if (i < 8) //THIS LINE HERE. CHANGE THE 8 TO YOUR FINAL GROWTH STAGE. { float f = getGrowthRate(par1World, par2, par3, par4); if (par5Random.nextInt((int)(25F / f) + 1) == 0) { i++; par1World.setBlockMetadataWithNotify(par2, par3, par4, i); } } } }[/code]
Just change that 8 at the line shown to your final growth stage. Also remember to change the 8 in the idDropped method to the final growth stage of your crop. Just have the relevant amount needed in the texture getting method.
You'll require a total of 10 textures if following this tutorial exactly. One for seeds, one for the block normally(can be the same as stage 1 but it needs to have a separate image file) and one each for every other stage.
I have explained all of the code in comments in this tutorial. If there was already a Javadoc comment for something I added some information to it in most instances. Let know what you all think, whether you like it like this or how I used to make these tutorials.
[/spoiler]
[center][b]GUI[/b][/center]
[spoiler]
A GUI is a Graphical User Interface. They are things like the inventory, furnace window and crafting window. Things like the menu that opens when you press "Escape" on your keyboard are also GUIs.
[center][b][i]The Basic GUI Class[/i][/b][/center]
[spoiler]
Here we have a very basic GUI class. It will do absolutely nothing when opened but all of this code is necessary.
[code]package net.minecraft.src; import net.minecraft.client.Minecraft; public class GuiNameHere extends GuiScreen { private Minecraft mc; private World world; private EntityPlayer entityPlayer; public GuiNameHere(World world1, EntityPlayer entityPlayer1, Minecraft minecraft) { world = world1; entityPlayer = entityPlayer1; mc = minecraft; } public void initGui() { } public void drawScreen(int i, int j, float f) { } }[/code][list]
[*]This class extends GuiScreen. GuiScreen contains most of the methods we will use in creating our GUI but some are also further up in the "heirachy". GuiScreen extends Gui. Gui has several methods in it. The main ones we will use are drawString and drawCenteredString but we'll get onto them later.
[/list][list]
[*]Then we have three private variables:
[/list]
[code]private Minecraft mc; private World world; private EntityPlayer entityPlayer;[/code][list]
[*]We use these later when we get down to the constructor.
[/list][list]
[*]In the constructor, there are three parameters. World, EntityPlayer and Minecraft. Obviously, the World is the world that the player(EntityPlayer) is in. Minecraft is the game itself and its main classes.
[/list]
[code]public GuiNameHere(World world1, EntityPlayer entityPlayer1, Minecraft minecraft)[/code][list]
[*]You'll notice that the first to parameters(World and EntityPlayer) have a 1 after their variable(world1, entityPlayer1). We have to add this so that they are not the same as the private variables we previously created. You don't have to add a 1 after it, you could add anything. For example your constructor could say:
[/list]
[code]public GuiNameHere(World worldVariable, EntityPlayer entityPlayerVariable, Minecraft minecraft)[/code][list]
[*]It is just easier though if we keep it as adding a 1 after it.
[/list][list]
[*]Inside the constructor we have 3 more lines.
[/list]
[code] world = world1; entityPlayer = entityPlayer1; mc = minecraft;[/code][list]
[*]These lines make our private variables from above equal to our variables in the constructor's parameters.
[*]The reason we add those private variables is so that we can use the parameters from the constructor inside the entire class. Even though we can use ModLoader for it(ModLoader.getMinecraftInstance()), we are better off using the variables we have created. It is easier for us and doesn't make us type so much!
[/list][list]
[*]After the constructor we have our other methods. The first is initGui. This is called when the GUI is first opened and renders the buttons and other controls we may add.
[/list][list]
[*]The final method here so far is drawScreen. This method renders everything else on the screen. When we add in our pictures, text, etc, we will put it in this method. We will also need to add a little bit of code in here when adding buttons.
[/list]
[/spoiler]
[center][b][i]Rendering Text[/i][/b][/center]
[spoiler][list]
[*]There are two different methods we can use to render text on the screen. [i]drawString [/i]and [i]drawCenteredString[/i]. These two are a little bit different from each other.
[*]drawString will position the left side of the text at the specified position.
[*]drawCenteredString will position the center of the text at the specified position.
[/list]
[code]drawCenteredString(fontRenderer, "Text Here", width / 2, height / 2 - 75, 0xffffff); //or drawString(fontRenderer, "Text Here", width / 2, height / 2 - 75, 0xffffff);[/code][list]
[*]There we have our two different lines. There are 5 parameters of these methods. They both have the same parameters.
[*]The first parameter is:
[/list]
[code]fontRenderer[/code][list]
[*]This is the font renderer that is inherited from GuiScreen.
[*]The second parameter is the text we want to render on the screen, in the form of a String. Don't remove the quotation marks.
[/list]
[code]"Text Here"[/code][list]
[*]The third parameter is the horizontal position of the text on the screen. We use:
[/list]
[code]width / 2[/code][list]
[*]Which means [u]width divided by 2.[/u] This gives us the center of the screen. If you want to render something like a title and want it in the exact middle of the screen, you should use[i] drawCenteredString[/i] as the method and [u]width / 2[/u] as the position.
[*]The fourth parameter is the vertical position:
[/list]
[code]height / 2[/code][list]
[*]Using [u]height / 2[/u], gets the vertical center of the screen. Once you have the vertical center of the screen you'll probably want to move your text up and down the axis. If you use
[/list]
[code]height / 2 - 75[/code][list]
[*]Then your text will move 75 pixels towards the top of the screen.
[*]Obviously, using a + instead of a - will give the opposite effect.
[/list]
[code]height / 2 + 75[/code][list]
[*]With that, your text would move 75 pixels towards the bottom of the screen.You don't have to use [u]width / 2[/u] or [u]height / 2[/u] for the positions but I find it a lot easier to do so.
[/list][list]
[*]The fifth and final parameter is the colour of your text. This is in a hexadecimal colour code format. All you need to do is get the hex code of the colour you want and add "0x" to the front of it(without the quotation marks).
[*]You can use [url="http://html-color-codes.com/"]this[/url] colour chart. The numbers and letters on the colours is the hex code of that colour. You just get that and add the "0x" to it like explained above.
[/list][list]
[*]A few basic colours to use are:
[/list]
[code]Black = 0x000000 White = 0xffffff Grey = 0xCCCCCC Blue = 0x00CCFF Red = 0xFF3300 Yellow = 0xFFFF00 Green = 0x339933[/code]
[/spoiler]
[center][b]This section is under construction![/b][/center]
[center][size=large][b][img]http://www.iowamotortruck.com/images/CSA%20Alert%20Symbol.jpg[/img][/b][/size][/center]
[/spoiler]
[/spoiler]
List tags are malformed.
together they are powerful beyond imagination."
I'll put up a section on recipes, and explain everything like that :smile.gif:
The only way I think you could do this, would be to edit the world generator class. As long as the ground is grass or dirt, you wouldn't need to worry about trees and grass. If you are doing your village in one world gen file(no components like the normal villages), then for pathways and things you would probably just need to set the blocks as air.
That is the code for setting a block to air if you need it.
together they are powerful beyond imagination."
Alright thanks bro ima try it out! Ill link you to my mod if you want, when im done it atleast. :wink.gif:
together they are powerful beyond imagination."
I'll try. I've got to figure out how to make one myself first
Added crafting and smelting recipe tutorials.
together they are powerful beyond imagination."
Try updating your jdk. I have 1.6.0_30 and it works fine. You have 1.6.0_29. There might be a bug fix in that release, I'm not sure.
together they are powerful beyond imagination."
Thanks for this! :biggrin.gif:
How do you make it only set the light value if it's being powered by redstone?
Also, what do you do with this code? I put it into eclipse, but I don't know how to export it, or if I should name it a certain thing.
I have one problem: When I compile it with the Free "Java-Editor" (http://www.javaeditor.org), then it says:
My rogram code (I suspect that it is not about him ):
Is it about the compiler?
What for a compiler do you use?
You should use MCP. I'm going to put up a tutorial about it. I also recommend Eclipse. It will help solve many errors you run into.
Thanks for the support. :smile.gif:
Yeh, I would try that. I use Eclipse Indigo.
Sorry, I should have gone over the necessities first. You'll need to download MCP. I'm putting up how to set it up now.
together they are powerful beyond imagination."
Thanks :smile.gif:
together they are powerful beyond imagination."
together they are powerful beyond imagination."