- EB547
- Curse Premium
-
Member for 13 years and 29 days
Last active Sat, Oct, 31 2015 22:36:13
- 1 Follower
- 842 Total Posts
- 3 Thanks
-
1
x_clucky posted a message on Curse Client - World Management Tools!Sweet, my hard drive is getting pretty full with only 7gb to sparePosted in: Minecraft News -
2
CowedOffACliff posted a message on Modding Test Client Hogs CPUPosted in: Modification DevelopmentQuote from EB547
All i have for code right now is some basic ore gen code and one block. Its small, and shouldn't consume much. I use eclipse to start my modded minecraft, and it still hogs CPU.
1. Show me the ore generation code
2. Tell me your operating system
3. Tell me what your CPU is and at what clockspeed
4. Tell me what your graphics card is
5. How much RAM (memory) the computer has
6. How old the computer is
7. If you have any backround programs open besides something like a file browser -
3
NinjaKAdv posted a message on Ban the user above you!Banned for not having your post +Posted in: Forum Games -
56
Jotamota posted a message on [Modding][ModLoader][Forge API] Jotamota's Tutorials!!!Intro!!!Posted in: Tutorials
So, I'm not a experienced modder, I must say, BUT I have some experience with Java and mostly codding Minecraft. I've been a coder on Minecraft for 2 months, and I've learned SO MUCH. Since I'm not the MASTER OF ALL CODES I'll only teach what I REALLY know. Meaning I'll try only to teach what I can back up.
I try also to explain things so people won't just copy and paste, but really understand what I do and why did I do it.
So, lets get on shall we?
Table of Contents
- 1. Intro
- 2. The Tutorials
- ModLoader
How to make metadata-Items!
Tutorial II:
How to generate structures!
(using loops and not line-by-line generation)
- Forge
Tutorial I:
How to infinite index textures
And how to smelt metadata items.
Tutorial II:
How to multi-texture a block
with Forge's infinite indexing of sprites!
Tutorial III:
How to create a block using metadata and Forge's texture indexing!
ModLoader Tutorials!!!
Tutorial I:How to make metadata-Items! (like ItemDye!!!)
Difficulty: Begginer
So, what is the point of making a metadata item? The thing is: Minecraft has a maximum number of ids that can be indexed to blocks and items. It is LIMITED so far, so metadata is a very easy (if you know how to use it) and practical solution the game has come up with to index a maximum of 16 (not confirmed) items to one ID, as done with ItemDye (16 items under one id: dyePowder.shiftedIndex).
I'm assuming you know only a bit of how to create mods so I'm starting with basics.
Notice that I will NOT explain how to setup MCP (there are plenty tutorials out there about it!!!)
First of all, you must create a new file for your mod, named as you want. Its a modding convention to name all mod files "mod_" something, as in "mod_MoreCreeps", "mod_Cooking", and else. I'm naming our new mod file mod_ItemMetadata.
After you created this file, you must extend it as a BaseMod (meaning it can do all a BaseMod can do) and create two functions: getVersion and public void load(). The function getVersion returns the version when asked when Minecraft runs. Public void load() is a void function that runs when the program is executed:
package net.minecraft.src; import java.util.Random; import net.minecraft.client.Minecraft; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE @Override public void load() { } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
Now, lets create our item shall we? First of all, we must declare the item! Note the spaces I've marked for declaration lines. You can do it anywhere, as long as it's inside the mod_ public class and outside any function.
To create a item, we use this line, that declares a static(non-changeble), public(useable by other scripts) and final (cannot be extended). Then we define what the varible is, it's class. The same way we do...
String stringVariable = "Hello, World";
...we call the Item class through the variable "variableForYourItem":
public static final Item variableForYourItem;
Lets call it noUse, shall we? We must now atribute a value for that variable. Since we want to customize, edit the script and we DONT want to mess with the base game files (because that cause mod incompatibilities if other mod also modifies it...) we create a new ItemClass, the ItemNoUse. To make a new Item with the caracteristics of a ItemNoUse and a Item, we use the constructor (a function that recieves input) ItemNoUse to send a id to Item. I'll explain later :smile.gif:. For now, we have this:
public static Item noUse; noUse = new ItemNoUse;
And then we access the ItemNoUse class that EXTENDS Item and we use a Item's function: setItemName. Since setItemName needs a String as input, we also give it a input. That's all:
public static Item noUse = new ItemNoUse(idNumberGoesHere).setItemName("noUse");
Modify the idNumberGoesHere with an Integer (an number) from about 200 to 3000. I would advice to choose around 2000 or 1000 since I'm not totally sure about the maximum of item ids.
Your file mod_ItemMetadata should look like this now:
Now, we must create an ItemMetadata, or on our case, an ItemNoUse. This is an example of items that I use on my mod and that have no use besides crafting. This way, we save ids from using.
package net.minecraft.src; import java.util.Random; import net.minecraft.client.Minecraft; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE public static Item noUse = new ItemNoUse(1694).setItemName("noUse"); @Override public void load() { //MOD COMMANDS GOES HERE } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
So, create a file name ItemNoUse.java and make the constructor ItemNoUse(int id). Since this is a metadata item, we must also declare the maxDamage to 0 (that means that there won't be any damage display on the item and call that this IS a itemMetadata with the setHasSubtypes function, like this:
package net.minecraft.src; /** * * @author Jotamota */ public class ItemNoUse extends Item { public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } }
Note that besides the public class that IS the ItemNoUse.java, we have a function with a constructor: ItemNoUse(int id). This means it recieves as input a Integer. And then it runs super(id);
super is a method to upper bump the code to the item that its the script's parent. Kinda hard to understand, huh? Since ItemNoUse extends Item, we can say its a "child" of Item. It can do what ever its "parent" can do, and by the function super(id) it sends the input as output to the constructor Item(int i) wich can be found on Item.java.
Now, we must create a a "multiple string" (on a way) by creating a String called "names". Add this on the declaration space (inside the main public class file function and outside any other function:
private String[] names = new String[]{};
We call it private so only this script can access it. This prevents conflicts between two scripts that call a variable named the same and it prevents other scripts to access it.
Note that this replaces the spot for "public". Instead, its "private".
So we can easily track the item indexed to each metadata, lets make a annotation here that mimics the line above and shows the damage to each item, like this:
private String[] names = new String[]{"item1", "item2"}; //private String[] names = new String[]{" 0 ", " 1 "};
This makes a String with 2 strings. The first one is "item1" and the second one is "item2". Since the item damage is initially 0, we say that this is the itemDamage(0).
Now we create the Item function that assigns each "names" to its real name:
@Override public String getItemNameIS(ItemStack itemstack) { return names[itemstack.getItemDamage()]; }
This is a String function. Being a String function, it returns a String. A int function returns a int, a boolean function returns a boolean (true or false), and among many others, a void function just runs.
It recieves as input a ItemStack called "itemstack", and we Override it to force the code to make sure that is the same function as it's parent (in this case, Item.java).
Your ItemNoUse should look like this so far:
Another thing we want for our items is that each one can be able to have a different icon. Item.java already defines that function for us. It's the getIconFromDamage, that selects a icon depending on the item metadata:
package net.minecraft.src; /** * * @author Jotamota */ public class ItemNoUse extends Item { public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } private String[] names = new String[]{"item1", "item2"}; //private String[] names = new String[]{" 0 ", " 1 "}; @Override public String getItemNameIS(ItemStack itemstack) { return names[itemstack.getItemDamage()]; } }
@Override public int getIconFromDamage(int i) { if(i==0){ setMaxStackSize(1); return mod_ItemMetadata.itemIcon1;} if(i==1) { //setContainerItem(Item.bucketEmpty); return mod_ItemMetadata.itemIcon2; } else{ setMaxStackSize(64); return mod_ItemMetadata.itemIcon3;} }
The int "i" that is called from this function is the item metadata. Meaning that we can index item commands for each specific item, as setting a container Item or setting MaxStackSize for each different item.
I've used a few commands. Those are located at Item.java. There are many many more commands to be used. I used setMaxStackSize(64), that sets the maxStack for this item to 64. setContainerItem(Item.bucketEmpty) that would set a container item (in this case, a empty bucket) if it wasnt after // (those two slashes make the line after that comment. It means that the code doesn't read it. It's for reading or disabling the line).
Note that this is a int function. As I said before, a int function returns a int. Look that we check the if condition to see if the metadata of the item ("i") is equal to 1,2 or else. Meaning that if its 1, it will run between 1 if condition. Else is for the non defined if conditions (if a item is beyond 1 or 2, it will run else instead of crash).
Since this is a ModLoader tutorial, we must create a itemIcon index for each item. Let's go to our mod_ItemMetadata now and add each itemIcon with the addOverride command.
Go to our declaration lines and add this line:
public static int itemIcon1 = ModLoader.addOverride("/gui/items.png", "/YourFileFolderHere/nameOfYourIcon.png");
This overrides the items.png file to include one more, the file that you want your icon to have. Your "YourFileFolderHere" folder must be in the /MCP/bin/minecraft/ directory, and your nameOfYourIcon image file should be there. You can always change the path if you want. So, we need 3 icons, lets copy and paste 2 times!
public static int itemIcon1 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon1.png"); public static int itemIcon2 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon2.png"); public static int itemIcon3 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon3.png");
Your icon.png files should be 16x16 and be saved as a PNG file.
Your mod_ItemMetadata should look like this:
Now, we must add names to each of our items. This must be done on mod_ItemMetadata, inside public void load() or inside a function called by public void load():
package net.minecraft.src; import java.util.Random; import net.minecraft.client.Minecraft; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE public static Item noUse = new ItemNoUse(1694).setItemName("noUse"); public static int itemIcon1 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon1.png"); public static int itemIcon2 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon2.png"); public static int itemIcon3 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon3.png"); @Override public void load() { //MOD COMMANDS GOES HERE } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
@Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); }
This calls the function "AddName" inside ModLoader.java (hence the ModLoader.FunctionName...) and it offers as input a ItemStack( will be explained later ) a String: the item's name. For a short, the new ItemStack creates a new function that recieves as input (in this case) a item (variable "noUse" located inside mod_ItemMetadata.java), a quantity (1) and the item damage (0). This makes each noUse item that has a different damage to have different names.
And now, we need to have some way of getting those items in-game. Lets make recipes!
ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks });
Time for a little explanation. The command AddRecipe calls a itemstack, wich is a item function that embraces quantity, damage, item, respectually, and many other item properties. What we must do is create a new ItemStack that is a noUse type of item, has quantity ONE (first number) and is damage 0 (second number), and then we create the shape of the recipe and its character.
Basically, everytime you need a itemstack of a metadata item, you must call THAT particular itemstack (item, quantity, damage). Change damage according to the item you want on your ItemNoUse names String, and also change quantity to your like.
The Object of the recipe is pretty simple. We first make how our recipe will look like ( if it was a sword, it would be ("x","x","y") and then we say the value of the character 'X'. You can change the characters used as long as you change both in recipe and valueOf function.
We can also create a shapeless recipe:
ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)});
This is an example recipe where we use one ItemNoUse to create other ItemNoUse. Note that we called the new ItemStack instead of calling Item.blahblahblah or Block.blahblahblah.
If you want to use this as a furnace PRODUCT, you should use this code:
ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0));
Now, if you want to use it as a INGREDIENT, that's where we face a problem. Minecraft default furnace NOR ModLoader AddSmelting methods tolerate using a metadata item as ingredient. However, Forge API accepts it. If you ever want it, you must download Forge API and install it, and meet me here for this same Tutorial on Forge's Version.
We have met the finale of this tutorial. Here are the full codes for your examination and comparison:
mod_ItemMetadata:
package net.minecraft.src; import java.util.Random; import net.minecraft.client.Minecraft; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE public static Item noUse = new ItemNoUse(1694).setItemName("noUse"); public static int itemIcon1 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon1.png"); public static int itemIcon2 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon2.png"); public static int itemIcon3 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon3.png"); @Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks }); ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)}); ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0)); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
ItemNoUse.java:
If you liked this tutorial, leave a +1 down there at the end of any of my posts;
package net.minecraft.src; /** * * @author Jotamota */ public class ItemNoUse extends Item { public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } private String[] names = new String[]{"item1", "item2"}; //private String[] names = new String[]{" 0 ", " 1 "}; @Override public String getItemNameIS(ItemStack itemstack) { return names[itemstack.getItemDamage()]; } @Override public int getIconFromDamage(int i) { if(i==0){ setMaxStackSize(1); return mod_ItemMetadata.itemIcon1;} if(i==1) { //setContainerItem(Item.bucketEmpty); return mod_ItemMetadata.itemIcon2; } else{ setMaxStackSize(64); return mod_ItemMetadata.itemIcon3;} } }
If you have any error while doing/copying this tutorial post 'em here that I'll try to help you as far as I can.
Hope you all liked it!
EDIT: thanks for callofcrafters for pointing me the weakness of this tutorial power of explanation. I hope this is more elaborated and understandable now. :smile.gif:
If you have doubts about any matter on modding post them here and I'll see if I can help you. If I CAN help, I'll make a new Tutorial just for you :smile.gif:
Tutorial II:How to generate structures(using loops and not line-by-line generation)!
Difficulty: Intermediate
Hey guys! It's Jota again, with one more Tutorial. This time, we will cover up a very expected tutorial: Generating Structures. Normally people do this line by line, like this:
You don't need to watch that video. Just by the thumbnail you can see the lots of lines with setBlock. I'm teaching you how to do this, the pro-way (:smile.gif:). And that is: using LOOPS.
Generate Structures
But before we get into generating, we must make a new block that when clicked generates our Structure. Let's get into it!
From our basic layout for blocks we can start working. This is the basic layout:
package net.minecraft.src; import java.util.Random; public class BlockGenStructure extends Block { protected BlockGenStructure(int i, int j) { super(i, j, Material.rock); } public int idDropped(int i, Random random) { return mod_Structures.genStructure.blockID; } }
I've named it BlockGenStructure. You can name it what ever you want. Well, this is the basic from basic. Now let's register our block on mod_Structures:
public static final Block genStructure = (new BlockGenStructure(1615, 28)).setHardness(3F).setResistance(5F).setBlockName("genStructure");
public void load() { genStructure.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/Blocks/genStructure.png"); ModLoader.RegisterBlock(genStructure); ModLoader.AddName(genStructure, "House-In-A-Block"); }
That's it. I'm not covering this again since I've done it already on ModLoader Tutorial I and if I'm not mistaken on Forge's Tutorial II. Now we add a very special function to our block: blockActivated.
The thing is: Block.java embraces a LOT of block's functions, including onBlockClicked, blockActivated, blockDestroyed, and many others. We are using blockActivated since this function allows us to check for a item used by the player. This way, we can do a "key" for this block. If we go to Block.java and look for blockActivated this is what we get:
public boolean blockActivated(World world, int i, int j, int k, EntityPlayer entityplayer) { return false; }
This is the "default" version of blockActivated, that EVERY block have. That is, unless we override this function on the new Block, wich is exactly what we are going to do.
As covered before, this is a perfect example of a function:
public CLASS functionNAME(InputClass input1, InputClass input2, InputClass input3,...) { }
We have a public function of type BOOLEAN (it has only 2 values: true or false) with the name blockActivated, and it has several inputs:
World world//A variable called "world" of type "World" ENtityPlayer entityplayer// A variable "entityplayer" of type "EntityPlayer" int j// A integer called "j"
Again, a integer is a number that is "complete" (e.g: 1,2,4,5671). "2.0" is NOT a int. "2/3" is also not a integer. "2/2" and "2*52" are integers.
Below a personal message (skip it if desired!):
Back to our tutorial!
Again, guys, this tutorial is not about giving codes. I like to explain what I use so YOU guys can understand and use it at your own advantage. If you guys find it boring and useless, you can skip this chat and go to the final files. YOu won't learn anything if you do so, but I won't stop you.
The function blockActivated offers for us to use a World, a EntityPlayer and coordinates:(i,j,k) meaning (x,y,z) axis (Vector3). This is why when wanting to check for inventory, coordinates and do things in the World we use the blockActivated. Now, how to use it, you ask? Simple thing my good reader!
public boolean blockActivated(World world, int i, int j, int k, EntityPlayer entityplayer) { if(world.multiplayerWorld) { return true; } ItemStack itemstack = entityplayer.inventory.getCurrentItem(); if(itemstack == null) { return true; } }
Now, we added some conditions to the blockActivated.
The first one is to prevent crashes on Multiplayer, so we can always activate a block on Multiplayer (again, this is a SinglePlayer mod, I never tried to mod SMP).
And then we define a new variable called "itemstack". It's the current Item from the entityplayer (the player that activated the block). Now we can use the itemstack and change things around it.
The last condition is to prevent crashes: when the itemstack DOES NOT EXIST ("null") the function returns true. So if you have no item you will click the block ("punch animation" but nothing will happen.
Now we do the checking for the item. We can do so checking the itemstack itemID or it's damage (for a metadata item):
if(itemstack.itemID == Item.stick.shiftedIndex) { //our code goes here itemstack.stackSize--; return true; } if(itemstack.itemID == mod_Metadata.metadataItem.shiftedIndex&&itemstack.getItemDamage() == 2) { //our code goes here itemstack.stackSize--; return true; }
The first condition is for normal Items. We check if the player is holding a stick. If he is, we take off one of the stack and we run our code.
The second is for metadata Items. We check for the itemID AND ("&&" operator) the itemDamage "2". Meaning we will only use the item if he is metadataItem "2".
Now we run the generation part. This is really tricky, so I'm explaining step by step.
As I said, most people use line-by-line codes. We can short it up and make the code run faster by using loops (a piece of code that runs several times). In java we have several loops ("while", "for"). This time we are using the "for" loop:
for(int clock = 0; clock<20; clock++) { System.out.println(clock); }
The for loop declares a variable, imposes a condition, and sets a "add" system.
This loops declare a int variable of value 0. Then it sets a condition: it will only run if the clock variable "value" is lower than 20. Then it increases the value of clock. (clock++ is the same as clock=clock+1 or clock+=1). Then it prints the clock value. The output of this code would be 0,1,2,3,4,5,6,7,8,...18,19. Remember that "<" means lower, so the loop will stop one number before 20. If you want it to stop ON 20, we use "<=" (lower or equal).
Now that we understand the basic for loop, lets apply it to generating:
for(int a = -3; a<4; a++) { world.setBlockWithNotify(i+a,j-1,k, Block.planks.blockID); }
Now THIS loop is gold! It sets a as -3 and it runs until a = 3.
Then it sets a block of type "Block.planks" with notify. The thing about using setBlock or setBlockWithNotify is that when you generate a world, you can use setBlock. But you need notify to tell the world that a new block appeared. If you don't you will only see the blockBounds but you won't be able to see the block. Since we want to generate on normal gameplay we use notify. Then we say the coordinates for the world to generate the block. We want it to generate one block below the actual block (j-1 is the same as y-1). And then it generates a block in the x coordinate i+a. On the first run of this loop it will generate a block on coordinates (i-3, j-1, k). On the second run it will generate on (i-2, j-1, k) and so on until it reaches (i+3, j-1, k). Basicly this 3 lines of code are the same as writing this code below:
world.setBlockWithNotify(i-3,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i-2,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i-1,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i+1,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i+2,j-1,k, Block.planks.blockID); world.setBlockWithNotify(i+3,j-1,k, Block.planks.blockID);
See what I meant about line-by-line code?
And that was just 7 blocks! Imagine how much lines we would have to write to generate a 7x7x7 cube! (:ohmy.gif:)
Meaning this will make a line of planks from i+(-3) to i+3 x axis.
Now we must add another coordinate! For this we can add a for loop INSIDE a for loop. Sounds trippy!
for(int a = -3; a<4; a++) { for(int c = -3; c<4; c++) { world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); } }
Amazing! 5 lines to generate 49 blocks! This 2 loops will generate from -3 to 3 on X line and -3 to 3 on Z line. You can mess around with the coordinates, because they work FINE. :smile.gif:
This generates a 7x7 square on the ground (-3 to 0 and 0 to 3 = 7 blocks on each axis) one block below the genStructure block.
But we want a house, don't we?
Now we will get into my code (the code I've used to create a house from one block). Things are gonna get REALLY intense now.
First of all we need pillars. Let's do one 4 tall pillar of wood on each house corner, shall we? For that we must set a condition: "this will only run if it is on the certain coordinates!":
for(int a = -3; a<4; a++) { for(int c = -3; b<4; c++) { if(a==-3&&c==-3) { world.setBlockWithNotify(i+a,j,k+c, Block.wood.blockID); } world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); } }
This says that it will only run the "new generation code" if it is on a coordinate -3 and c coordinate -3. This will create a block of wood on the coordinates i-3, j, and k-3. But we need 4 pillars right?
for(int a = -3; a<4; a++) { for(int c = -3; b<4; c++) { if((a==-3&&c==-3)||(a==-3&&c==+3)||(a==+3&&c==-3)||(a==+3&&c==+3)) { world.setBlockWithNotify(i+a,j,k+c, Block.wood.blockID); } world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); } }
This seems weird, but it's really simple. It sets a condition with 4 conditions blended with the OR operator ("||"). Meaning it will only run if it's on one of the 4 coordinates. This will create 4 blocks of wood on the corners. Now we want them to branch up! For that we need a new loop. This time it will go on the "y" axis, called "j" on our code. We can do it from 0 to 4 since we don't want it to grow "down":
for(int a = -3; a<4; a++) { for(int b = -3; b<4; b++) { if((a==-3&&c==-3)||(a==-3&&c==+3)||(a==+3&&c==-3)||(a==+3&&c==+3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.wood.blockID); } world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); } }
Yep. Done. This creates a 3 blocks tall of normal wood from the coordinates -3,-3, -3,3, 3,-3, and 3,3 on x and z axis. We used a new loop to create the pillars. Now we need some walls on our house, right? For that, (you guessed right!) we use NEW loops (but with new conditions). This time, we will generate 2 walls in each axis (x and z, in here "i" and "k"):
if((c==-3||c==3)&&(a>-3&&a<3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.planks.blockID); } }
This creates a wall in c "-3" and c "3". I said AND, even though the code says "OR". That's because when I say -3 or 3 on the code the loop will generate the wall in -3 OR 3, meaning, it will generate on both coordinates -3 and 3. Then we set a AND condition: when a is between -2 and 2 we will make a 3 tall pillar of planks. This pillar will be made on each "a" coordinate, meaning it will make 5 pillars of planks, also know as a wall! Now that we got 2 walls done, we make the other one!
if((a==-3||a==3)&&(c>-3&&c<3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.planks.blockID); } }
It looks like a lot, but it isnt the same. This time, it generates 5 pillars in (-3 a, -2 :cool.gif:, (-3 a, -1 :cool.gif:, (-3 a, 0 :cool.gif: and so on until it reaches (-3 a, 2 :cool.gif:. It also does the same on (3 a, from -2 b to 2 :cool.gif:. Now bleding this code with the rest, we get this:
for(int a = -3; a<4; a++) { for(int b = -3; b<4; b++) { if((c==-3||c==3)&&(a>-3&&a<3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.planks.blockID); } } if((a==-3||a==3)&&(c>-3&&c<3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.planks.blockID); } } if((a==-3&&c==-3)||(a==-3&&c==+3)||(a==+3&&c==-3)||(a==+3&&c==+3)) { for(int b=0; b<4; b++) { world.setBlockWithNotify(i+a,j+b,k+c, Block.wood.blockID); } world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); } }
Wow this code is becoming a monster! It seems like a not, but this house has no roof, or doors! The roof is easy peasy. We only need to generate a new 7x7 square. Only this time it will be made of wood and it will be above. Lets put this code right below the last line of our code, still inside the loop, out of the conditions:
world.setBlockWithNotify(i+a,j-1,k+c, Block.planks.blockID); world.setBlockWithNotify(i+a,j+4,k+c, Block.wood.blockID);
There. Roof done. This generates the same square as the line above, only it does on "x" axis +4 and it's made out of wood instead of -1 planks.
Last but not least, we need doors! Let's do 2 doors to 2 walls:
if((a==-3||a==3)&&(c>-3&&c<3)) { for(int b=0; b<4; b++) { if((a==-3||a==3)&&c==0&&b==1) { world.setBlockAndMetadataWithNotify(i+a,j+b,k+c, Block.doorWood.blockID, 8); } if((a==-3||a==3)&&c==0&&b==0) { world.setBlockAndMetadataWithNotify(i+a,j,k+c, Block.doorWood.blockID, 0); } else if(!((a==-3||a==3)&&c==0&&(b==1||b==0))) { world.setBlockWithNotify(i+a,j+b,k+c, Block.planks.blockID); } } }
Now THAT's freaky. But have no fear, young (or... "experienced") modders! We only applied our checking coordinates techniques to 2 sides.
Let's break the code down:
if((a==-3||a==3)&&c==0&&b==1) { world.setBlockAndMetadataWithNotify(i+a,j+b,k+c, Block.doorWood.blockID, 8); }
This runs when a is -3 and a is 3 in c 0 and b 1. Why so much specification? It's because a door is not one block. It's 2 blocks. The bootom block is door metadata 0, and the upper block is door metadata 8. So we set the generation of a upper door on b1, and...
if((a==-3||a==3)&&c==0&&b==0) { world.setBlockAndMetadataWithNotify(i+a,j,k+c, Block.doorWood.blockID, 0); }
... and we set the generation of the bottom door on b0. Note that this will run on both sides of a (-3 and 3).
Then we add a else if condition.
else if(!((a==-3||a==3)&&c==0&&(b==1||b==0)))
It will only run when it's not one of the if conditions above AND when the a is -3 or 3, when the c is 0 and when the b is 1 or 0. This condition prevents the other conditions of overlapping them selves and bugging.
So we can end this cake with icing on top, lets add a glowstone on top!
world.setBlockWithNotify(i+a,j+4,k+c, Block.wood.blockID); if(a==0&&c==0) { world.setBlockWithNotify(i+a,j+4,k+c, Block.glowStone.blockID); }
Meaning it will create a glowstone on a 0 and c 0 and b 4.
This is the complete BlockGenStructure file:
I've added a line to delete the block genStructures when clicked
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) braces deadcode package net.minecraft.src; import java.util.Random; public class BlockGenStructure extends Block { protected BlockGenStructure(int i, int j) { super(i, Material.wood); blockIndexInTexture = j; } @Override public boolean blockActivated(World world, int i, int j, int k, EntityPlayer entityplayer) { world.setBlockWithNotify(i,j,k, 0); for(int i1=-3; i1<4; i1++) { //System.out.println(i1); for(int k1=-3;k1<4; k1++) { //System.out.println(k1); world.setBlockWithNotify(i+i1,j-1,k+k1, Block.planks.blockID); if((i1==-3&&k1==-3)||(i1==-3&&k1==+3)||(i1==+3&&k1==-3)||(i1==+3&&k1==+3)) { for(int j1=0; j1<4; j1++) { world.setBlockWithNotify(i+i1,j+j1,k+k1, Block.wood.blockID); } } if((i1==-3||i1==3)&&(k1>-3&&k1<3)) { for(int j1=0; j1<4; j1++) { if((i1==-3||i1==3)&&k1==0&&j1==1) { world.setBlockAndMetadataWithNotify(i+i1,j+j1,k+k1, Block.doorWood.blockID, 8); } if((i1==-3||i1==3)&&k1==0&&j1==0) { world.setBlockAndMetadataWithNotify(i+i1,j,k+k1, Block.doorWood.blockID, 0); } else if(!((i1==-3||i1==3)&&k1==0&&(j1==1||j1==0))) { world.setBlockWithNotify(i+i1,j+j1,k+k1, Block.planks.blockID); } } } if((k1==-3||k1==3)&&(i1>-3&&i1<3)) { for(int j1=0; j1<4; j1++) { world.setBlockWithNotify(i+i1,j+j1,k+k1, Block.planks.blockID); } } world.setBlockWithNotify(i+i1,j+4,k+k1, Block.wood.blockID); if(i1==0&&k1==0) { world.setBlockWithNotify(i+i1,j+4,k+k1, Block.glowStone.blockID); } } } return true; } @Override public int idDropped(int i, Random random, int j) { return mod_Structure.genStructure.blockID; } }
world.setBlockWithNotify(i,j,k, 0);
And here is our silly mod_Structures file:
It's all done! You get the block by mixing dirt with sand (for testing purposes) and when you right click it, BAM. A house is spawned.
package net.minecraft.src; /** * * @author Jotamota */ public class mod_Structures extends BaseMod { //DECLARATION LINES HERE public static final Block genStructure = (new BlockGenStructure(1615, 28)).setHardness(3F).setResistance(5F).setBlockName("genStructure"); @Override public void load() { //MOD COMMANDS GOES HERE genStructure.blockIndexInTexture = ModLoader.addOverride("/terrain.png", "/Blocks/genStructure.png"); ModLoader.RegisterBlock(genStructure); ModLoader.AddName(genStructure, "House-In-A-Block"); ModLoader.AddShapelessRecipe(new ItemStack(genStructure, 1), new Object[] {Block.dirt, Block.sand}); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
Hope you liked this tutorial! As always, if you get any errors, post them on this thread and I'll reply as soon as possible. If you liked this tutorial hit the +1 Button for support! If you want more tutorials post below what kind of tutorial you want. See you later!
More tutorials on their way!!!
Forge Tutorials!!!
Tutorial I:How to index infinite texture and how to smelt metadata items! (like ItemDye!!!)
Difficulty: Begginer
There is not much to add with Forge. This is a COMPLEMENTORY tutorial, meaning that this only adds things. You should start looking at ModLoader's tutorial I, and then come back here :smile.gif:.
Already back, are we? Lets get into it!
Among other things, what makes Forge a supreme modding tool is it's "Hooks" for metadata and "infinite texture indexing". A hook is when a mod modifys a base file (a original game code file) so that people don't have to. This way, you can call for the hook, instead of scrambling the original files. Much like a middle man that stops both from getting to know eachother.
Anyways, Forge adds several hooks for metadata. Without Forge, you cannot make a furnace use cocoa beans. This shall be our first lesson.
Again, you should start reading the ModLoader's TUTORIAL I and THEN come back here. We are using the finished code files once used before.
Here's the mod_ItemMetadata:
... and here's ItemNoUse:
package net.minecraft.src; import java.util.Random; import net.minecraft.client.Minecraft; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE public static Item noUse = new ItemNoUse(1694).setItemName("noUse"); public static int itemIcon1 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon1.png"); public static int itemIcon2 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon2.png"); public static int itemIcon3 = ModLoader.addOverride("/gui/items.png", "/MetadataIcons/icon3.png"); @Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks }); ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)}); ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0)); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
Now, first of all, lets make infinite indexing of texture. Instead of overriding the original textures with ModLoader, we can preload a new texture and index items and blocks to that new texture. Much like creating a new terrain.png with 256 new icons to go.
package net.minecraft.src; /** * * @author Jotamota */ public class ItemNoUse extends Item { public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } private String[] names = new String[]{"item1", "item2"}; //private String[] names = new String[]{" 0 ", " 1 "}; @Override public String getItemNameIS(ItemStack itemstack) { return names[itemstack.getItemDamage()]; } @Override public int getIconFromDamage(int i) { if(i==0){ setMaxStackSize(1); return mod_ItemMetadata.itemIcon1;} if(i==1) { //setContainerItem(Item.bucketEmpty); return mod_ItemMetadata.itemIcon2; } else{ setMaxStackSize(64); return mod_ItemMetadata.itemIcon3;} } }
To use Forge's methods, we need to import the whole segment of Forge's code. Java created this line to import sources. It's a line that goes BEFORE the public class and it drags all the code related to so our code can handle it. One example of it it's the:
import Java.util.Random;
If we want to create Random variables on our code, we must import the java code that MAKES random. In our case, we will import forge's code. It's a folder called "forge" inside the folder SRC, that is inside the folder MINECRAFT, and that is inside the folder NET.
This is why our code must be like this:
import net.minecraft.src.forge.*;
This imports all the code inside the forge folder (hence the "*"). If you wanted only import ONE file, you could do import net.minecraft.src.forge.IBonemealHandler.
Anyways, we are getting a little diverted, lets get back on track! This is the mod_ItemMetadata after we import forge's code.
package net.minecraft.src; import net.minecraft.src.forge.*; /** * @author Jotamota */ public class mod_ItemMetadata extends BaseMod
Now before we can use the new texture files, we need to import it, so that forge can LOAD it before we can USE it. We can do it by using the method preloadTexture from MinecraftForgeClient. Main methods that Forge uses are located either on MinecraftForge.java or in MinecraftForgeClient.java. If you want to play around with new methods, you should look into these. Forge has no javadoc ("documentation" that explains the java methods) but it is all written in the code as commenteries.
To use the preloadTexture we must call the main file (MinecraftForgeClient) and access it by putting a dot in it.
MinecraftForgeClient.preloadTexture();
If you run this line, you will get a error, because preloadTexture requires a String to run. It's the string that tells the path so that the code can find the texture file. Just like the path from addOverride, we add the path to our texture file here.
@Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks }); ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)}); ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0)); MinecraftForgeClient.preloadTexture("/MetadataIcons/items.png"); }
Now we can use this items.png. The name of the file does NOT matter, as long as you name it the same as it's in code. I'm putting the items.png file inside our new folder (already created) MetadataIcons.
Now, let's delete the override functions on the declaration space. The deal about Forge is that it does NOT override the texture. This means that it doesn't "invades" Minecraft code and "imposes" a new texture to be accepted by the default texture file. It presents a "brother" that lives independently. We have our texture file ready, now let's go to ItemNoUse.java!
package net.minecraft.src; /** * * @author Jotamota */ public class ItemNoUse extends Item {
Since we are using forge's code, we need to import it again. We must import forge's code to each file that uses forge. Lets add the same line into the import space (after package, before public class):
package net.minecraft.src; import net.minecraft.src.forge.*; /** * @author Jotamota */
We must implement the class. Implementing a new class means it inherits all of the implemented class functions, or none. When we extend a class, we can call it a "son". When we implement, we call it a "brother". The difference is: the "son" class can only have one father. The "brother" can have many many brothers. We can only extend one class to be a descendent but we can implement many classes to be alike.
Being sad, we must implement the forge's code ITextureProvider. It's a file code that makes the code use the new texture file instead of the old one. We implement classes after extending them or after declaring them as public classes, like this:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class ItemNoUse extends Item implements ITextureProvider
Since we implemented it, if we need to add the function that ITextureProvider uses, or the game will crash. It's the function getTextureFile that is required, needed, priority, for the code to run when implementing ITextureProvider.
Lets add the function shall we?
public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } @Override public String getTextureFile() { return "/MetadataIcons/items.png"; }
Again, we want it public (so other scripts can access it, like the Forge code that will replace the original items.png by this one) and it is a String function. Meaning we need to return a String. The path to find the texture. It must be the same path that we declared to preload the Texture. We override the function so it denies it's implemented function. It "replaces" the function's "getTextureFile" from ITextureProvider insides with this one.
Now that we preloaded the Texture and we implemented it, we can use it. Delete the whole function getIconFromDamage. We will rewrite it!
@Override public int getIconFromDamage(int i) { if(i==0){ setMaxStackSize(1); return ; if(i==1) { return ; } else{ return ;} }
Now, instead of returning the mod_ItemMetadata icons, we will just add a number between 0 and 255.
We can do it to return 0 for the first damage item, 1 for the second and 2 for the rest.
@Override public int getIconFromDamage(int i) { if(i==0) { return 0; } if(i==1) { return 1; } else { return 2; } }
Now, we need to create a 256x256 png file called items.png and located inside MetadataIcons. I've made this file as an example:
The first item will return the value 0, wich represents the first icon from items.png (remembering it starts in 0). The second returns the second icon (space 1) and all the other items will return the icon 2.
This concludes the texture indexing with Forge.
Now let's temper with metadata furnaces, shall we?
Back to mod_ItemMetadata, let's replace the AddSmelting line with another one:
@Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks }); ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)}); ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0)); MinecraftForgeClient.preloadTexture("/MetadataIcons/items.png"); }
ModLoader uses the method AddSmelting from the file ModLoader.java, but Forge uses the method smelting from the game's file FurnaceRecipes. This methods (you can look it up on FurnaceRecipes.java) returns a new recipeBase that creates a new FurnaceRecipes. We now use the method inside that new FurnaceRecipe called addSmelting:
FurnaceRecipes.smelting().addSmelting());
If you go into FurnaceRecipes, you will find these two functions:
public void addSmelting(int i, ItemStack itemstack) { smeltingList.put(Integer.valueOf(i), itemstack); } /* FORGE: Add a metadata-sensitive furnace recipe. */ public void addSmelting(int i, int meta, ItemStack itemstack) { metaSmeltingList.put(Arrays.asList(i,meta), itemstack); }
They have the same name, but that is no problem, since each one recieves different inputs. One uses 2 inputs, and the other one, created by Forge, uses 3 inputs. That means that if you apply 2 inputs to that method, you will call the addSmelting(int i, ItemStack itemstack). If you use 3, it'll call the other one. We can use both, but for now let's use the metadata-sensitive version. It recieves a int i, a int meta, and a ItemStack. The first int is the item/block's id. The second is the item/block's metadata. The third one is the RESULT from the smelting.
FurnaceRecipes.smelting().addSmelting(mod_ItemMetadata.noUse.shiftedIndex, 0, new ItemStack(mod_ItemMetadata.noUse.shiftedIndex, 1, 1));
Now when we smelt the itemMetadata 0 we will make the itemMetadata 1.
And that concludes the first Tutorial for Forge. Here are full files for comparison and learning. Remember, copying this code won't take out far. But if you understand it, your mind is the limit.
mod_ItemMetadata:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class mod_ItemMetadata extends BaseMod { //DECLARATION LINES HERE public static Item noUse = new ItemNoUse(1694).setItemName("noUse"); @Override public void load() { //MOD COMMANDS GOES HERE ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 0), "Item 1 Name"); ModLoader.AddName(new ItemStack(mod_ItemMetadata.noUse, 1, 1), "Item 2 Name"); ModLoader.AddRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 0), new Object[] { "XXX","XYX","XXX", Character.valueOf('X'), Item.stick, Character.valueOf('Y'), Block.planks }); ModLoader.AddShapelessRecipe(new ItemStack(mod_ItemMetadata.noUse, 1, 1), new Object[] {Block.dirt, new ItemStack(mod_ItemMetadata.noUse, 1, 0)}); ModLoader.AddSmelting(Block.dirt.blockID, new ItemStack(mod_ItemMetadata.noUse, 1, 0)); MinecraftForgeClient.preloadTexture("/MetadataIcons/items.png"); FurnaceRecipes.smelting().addSmelting(mod_ItemMetadata.noUse.shiftedIndex, 0, new ItemStack(mod_ItemMetadata.noUse.shiftedIndex, 1, 1)); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
ItemNoUse:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class ItemNoUse extends Item implements ITextureProvider { public ItemNoUse(int id) { super(id); setHasSubtypes(true); setMaxDamage(0); } @Override public String getTextureFile() { return "/MetadataIcons/items.png"; } private String[] names = new String[]{"item1", "item2"}; //private String[] names = new String[]{" 0 ", " 1 "}; @Override public String getItemNameIS(ItemStack itemstack) { return names[itemstack.getItemDamage()]; } @Override public int getIconFromDamage(int i) { if(i==0) { return 0; } if(i==1) { return 1; } else { return 2; } } }
I hope you enjoyed. If you have any error, post them here and I'll try to help. If you want a new tutorial about something you don't understand well, post your idea here too! If you liked this tutorial hit the +1 greenie over there, since it bumps my reputation. Stay tuned for more!
Tutorial II:How to multi-texture a block with Forge's infinite indexing of sprites!
Difficulty: Begginer
Hello guys again! This is Jotamota, your friendly Modder, for one more Tutorial. This time, we will cover up how to multi texture a block (like a workbench, with one texture for each side). Note that this isnt a multi-rendering tutorial (custom shaping of blocks), we will only multi-texture it.
First of all, read Forge's Tutorial I. That tutorial explains how to use the infinite indexing feature of Forge. I will use it in this tutorial as well, but I won't explain it as clear as I did on the last Tutorial.
So, we need a fresh mod_XXX file. Here's the ModLoader 1.0.0 + Forge's API mod_XXX layout:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class mod_NewBlock extends BaseMod { //DECLARATION LINES HERE @Override public void load() { } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
We have the public void load(), the getVersion function, and our class mod_NewBlock. As in any block, we must declare it, register it, and build it. To declare a block, we apply the declaring line, similar to declaring a Item, on our Declaration Space:
public static final Block multiTexture = new BlockTexture(IdNumberGoesHere, BlockIndexInTextureGoesHere).setBlockName("nameGoesHere");
Lets break it down, shall we?
public static final Block multiTexture declares a new variable (type of Block) that is public (can be called by other scripts), final(cannot be extended), and static (cannot be changed). Then we apply a new value to this variable:
new BlockTexture(IdNumberGoesHere, BlockIndexInTextureGoesHere).setBlockName("nameGoesHere");
This piece creates a new BlockTexture Block, with the constructor BlockTexture(int i, int j), wich we will create in a few minutes. As I mentioned before, a constructor is a function that recieves inputs and create a class. This constructor will create a new Block, add it a NumberID and a BlockIndexInTexture. The numberID is what defines the block. We will use it many many times while on this tutorial. The BlockIndexInTexture is the number that says to the code where is the texture that we are going to use for our Block.
The last part of the code, ".setBlockName("nameGoesHere")", simply tells the game it's name to make sure it isnt the same block. If you put the same name that other block/item your new block will have the old block's icon and name. This is a common bug among modders.
Let's put the idNumber as 230 (it has to be over around 130 and under 256) and the indexTexture 0 for now:
public static final Block multiTexture = new BlockTexture(230, 0).setBlockName("nameGoesHere");
Good. Now we need to declare and set the block's name on our public void load() function:
@Override public void load() { ModLoader.RegisterBlock(multiTexture); ModLoader.AddName(multiTexture, "Multi-Textured Block"); }
To do so, we use ModLoader features. ModLoader uses the function RegisterBlock(Block block) to add our block to the game's original block's list. This way, our block is added to the game.
ModLoader has one function called AddName that adds a string name to a variable that is shown in-game. That is what we use to add names, as we did on Tutorial I for Metadata-Items.
Now, last but not least, we need to preload the Texture that we are going to use for our block. We now use a Forge's feature, the preloadTexture:
@Override public void load() { ModLoader.RegisterBlock(multiTexture); ModLoader.AddName(multiTexture, "Multi-Textured Block"); MinecraftForgeClient.preloadTexture("/NewTextures/terrain.png"); }
This loads the texture before the game run's so when the game calls the new texture it won't crash due to missing texture. Create our path inside MCP/bin/minecraft/, meaning that "/terrain.png" leads to MCP/bin/minecraft/terrain.png. Our new terrain file should be on /MCP/bin/minecraft/NewTextures/ with the name of terrain.png
Now, we must create our new BlockTexture.java. For a block file. THis is the simpliest of simple layouts you will ever find:
package net.minecraft.src; import java.util.Random; public class BlockXXX extends Block { protected BlockXXX(int i, int j) { super(i, j, Material.rock); } public int idDropped(int i, Random random) { return mod_NewBlock.xxx.blockID; } }
Now, let's see what do we do here:
We first import the package of the code, so the code can load ALL the files inside net.minecraft.src.
Then we import java's Random utilitie so we can use the Random generator on our code.
And then we declare the class and the constructor. Our constructor recieves 2 inputs, but we can always modify it! What is important is the super function.
A super function (on our case) bumps the code to a constructor on the extended class. On our case, it uses the int i and j from our constructor and it applies to Block(int i,int j, Material material) constructor. This means it creates a new Block.java.
The idDropped just tells what is the ID that the Block will drop when broken. Note that the "i" is the block's metadata.
Now, let's assemble our OWN block file:
1)We want it to run on a constructor of 2 integers, and we want it to drop the same block.
package net.minecraft.src; import java.util.Random; public class BlockTexture extends Block { protected BlockTexture(int i, int j) { super(i, j, Material.rock); } public int idDropped(int i, Random random) { return mod_NewBlock.multiTexture.blockID; } }
Easy peasy no?
Now, we will get into the multiTexturing. First, all blocks have the function getBlockTextureFromSide and getBlockTextureFromSideAndMetadata. These two function, when not defined, are defaultly defined by Block.java, rendering the block on one texture independently from it's metadata. Since we are not messing with metadata, we will use the getBlockTextureFromSide:
@Override public int getBlockTextureFromSide(int i) { }
This is our function. The int "i" represents the side, being 0 the bottom of the block, and 1 the top of it. 2,3,4 and 5 are it's sides.
Before we construct the getBlockTextureFromSide, we must implement ITextureProvider from Forge that possibilitate us from using our OWN textures to render the block!
I already taught this on Forge's Tutorial I, so I'll just do it here. If you wanna fully understand why do I do it, go uppost and check that out!
We implement the ITextureProvider after extending:
public class BlockTexture extends Block implements ITextureProvider
We import Forge's code on import lines:
package net.minecraft.src; import java.util.Random; import net.minecraft.src.forge.*;
And we set the path so the code can find the texture:
public String getTextureFile() { return "/NewTextures/terrain.png" }
Now back to getTextureFromSide:
package net.minecraft.src; import java.util.Random; import net.minecraft.src.forge.*; public class BlockTexture extends Block { protected BlockTexture(int i, int j) { super(i, j, Material.wood); } public int idDropped(int i, Random random) { return mod_NewBlock.multiTexture.blockID; } @Override public int getBlockTextureFromSide(int i) { } public String getTextureFile() { return "/NewTextures/terrain.png" } }
We need it to return a different texture from each side. Instead of doing conditions to each side and putting else conditions to the other sides, we can use Java's switch command. It is much like a if, only code-economic and runs smoother and faster than a lot of cogged-up if conditions. This is how switch works:
switch(metadata) { case 1: System.out.println("metadata is equal to 1."); break; case 0: System.out.println("metadata is equal to 0."); break; default: System.out.println("metadata is different than 1 and 0."); break; }
This makes the code switches between the values of the variable "metadata". In the case it's 1, it will print that metadata equals 1. Then the code breaks so it resets the switch.
Default case runs if metadata's value isnt covered by any case lines.
Now, in our code, we will run it on a "int" function. Int functions return a number. This way, we don't need the "break" line since the code will already stop when returned a value. This is how we use it:
@Override public int getBlockTextureFromSide(int i) { switch(i) { case 0: return blockIndexInTexture; case 1: return 4; default: return 15; } }
This switches around the side of the block, and it returns a number. What number is that, you ask? It's the blockIndexInTexture. The number that tells wich 16x16 sprite to use on terrain.png. Now we get our own terrain.png:
All the new terrain.png or items.png files MUST be 256x256 png files. I've made this one with random localization of the sprites. The first one we see is the sprite number 0 (we count it from left to right and from up to down), the second is the sprite number 4, and the third one is ONE SPRITE BEFORE THE SECOND LINE!!!(since there are 16 sprites each line, the first sprite will be sprite 0, the first sprite on the second line 16, on third line 32, and on and on)This way, it is one sprite before sprite 16, therefore, sprite 15.
You can order it where you want, as long as you tell the code where is it!
Last but not least, we must define the blockIndexInTexture. That variable is defined on our super(int i, int j, Material.wood). We declared it on mod_NewBlock:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class mod_NewBlock extends BaseMod { //DECLARATION LINES HERE public static final Block multiTexture = new BlockTexture(130, 0).setBlockName("nameGoesHere"); @Override public void load() { ModLoader.RegisterBlock(multiTexture); ModLoader.AddName(multiTexture, "Multi-Textured Block"); MinecraftForgeClient.preloadTexture("/NewTextures/terrain.png"); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
Last but not least, we need to find a way to get that block in-game!
Let's use ModLoader.AddShapelessRecipe!
ModLoader.AddShapelessRecipe(new ItemStack(mod_NewBlock.multiTexture, 64), new Object []{ Block.dirt, Block.dirt, Block.dirt, Block.dirt });
The code is done. Your block should be rendered differently on each sides, and all code should run smoothly. Here are ready files:
mod_NewBlock:
BlockTexture:
package net.minecraft.src; import net.minecraft.src.forge.*; /** * * @author Jotamota */ public class mod_NewBlock extends BaseMod { //DECLARATION LINES HERE public static final Block multiTexture = new BlockTexture(130, 0).setBlockName("nameGoesHere"); @Override public void load() { ModLoader.RegisterBlock(multiTexture); ModLoader.AddName(multiTexture, "Multi-Textured Block"); MinecraftForgeClient.preloadTexture("/NewTextures/terrain.png"); ModLoader.AddShapelessRecipe(new ItemStack(mod_NewBlock.multiTexture, 64), new Object []{ Block.dirt, Block.dirt, Block.dirt, Block.dirt }); } @Override public String getVersion() { return "0.1"; } //OR HERE :smile.gif: }
package net.minecraft.src; import java.util.Random; import net.minecraft.src.forge.*; public class BlockTexture extends Block { protected BlockTexture(int i, int j) { super(i, j, Material.wood); } public int idDropped(int i, Random random) { return mod_NewBlock.multiTexture.blockID; } @Override public int getBlockTextureFromSide(int i) { switch(i) { case 0: return blockIndexInTexture; case 1: return 4; default: return 15; } } public String getTextureFile() { return "/NewTextures/terrain.png" } }
And this concludes this tutorial. Hope you all liked and learned a lot by it, although it's still a simple tutorial. As always, don't hesitate posting any recompiling errors that you find on this thread, as well as new tutorial requests, that I'll be happy to make if I'm capable.
If you liked this tutorial, hit the +1 greenie over there!
See you in more tutorials!
More Tutorials to come!!!
-
47
Methuselah96 posted a message on [Creating Mods] Minecraft Forge [28/1/12]THESE ARE OUT OF DATE AND WILL BE UPDATED ASAP.Posted in: Tutorials
Please visit my main modding topic to vote for what tutorial I should make next.
Please Note: The titles that have smaller font are the ones that have been updated recently.
Table of contents:
How to install Minecraft Forge
How to use infinite terrain and sprite indexes
How to make an advanced configuration files
How to make a custom armor set
How to make custom filled buckets
How to make things grow with bonemeal
How to make a new redstone wire
How to make a custom item renderer - Coming Soon!
How to make a custom tool set
How to set the harvesting level for a block
How to remove tool effectiveness from a block
How to add your blocks and damage value items to creative mode
How to make a new ladder - Coming Soon!
How to make your block replaceable - Coming Soon!
How to use the ore dictionary - Coming Soon!
How to use version detect
How to add items to dungeon chests - Coming Soon!
...AND MORE
How to install Minecraft Forge
By: Methuselah96
Moved to http://minecraftforg...tallation/Short.
How to use infinite terrain and sprite indexes
How to make an advanced configuration files
How to make custom filled buckets
By: DiEvAl
In this tutorial we'll be making a mod that allows players to put dirt into buckets (pretty useless as a real mod, but good for a tutorial).
In a real mod, you'll probably use this with custom liquids. This tutorial explains how to add custom liquids: http://www.minecraft...111-generation/ (by Strengthowns)
We start out with a basic mod_*.java:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_ForgeTutBuckets extends BaseMod { public static String itemsPng = "/ForgeTutorials/Buckets/items.png"; public static String terrainPng = "/ForgeTutorials/Buckets/terrain.png"; public mod_ForgeTutBuckets() { } @Override public String getVersion() { return "1.0"; } @Override public void load() { MinecraftForgeClient.preloadTexture(itemsPng); MinecraftForgeClient.preloadTexture(terrainPng); } }
For details about how to use preloadTexture see How to use infinite terrain and sprite indexes.
Now you need to create your ItemDirtBucket class:
package net.minecraft.src; import net.minecraft.src.forge.*; public class ItemDirtBucket extends ItemBucket implements ITextureProvider { public ItemDirtBucket (int id, int blockId, int icon) { super(id, blockId); setIconIndex(icon); } public String getTextureFile() { return mod_ForgeTutBuckets.itemsPng; } }
ITextureProvider and getTextureFile() are also described in How to use infinite terrain and sprite indexes.
ItemBucket's constructor has 2 parameters: item ID, and ID of a block that will be placed when you right click with a full bucket (e.g. water source blocks). ItemDirtBucket's constructor also has 3rd parameter: icon index.
Next you need to implement IBucketHandler. Change third line to this:
public class ItemDirtBucket extends ItemBucket implements IBucketHandler, ITextureProvider
...and add this method:
@Override public ItemStack fillCustomBucket(World w, int i, int j, int k) { return null; }
It will be called every time player clicks on any block with empty bucket. If it returns null nothing happens. Otherwise, bucket in player's hand will be replaced with ItemStack that this method returned.
Your ItemDirtBucket looks like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class ItemDirtBucket extends ItemBucket implements IBucketHandler, ITextureProvider { public ItemDirtBucket (int id, int blockId, int icon) { super(id, blockId); setIconIndex(icon); } @Override public ItemStack fillCustomBucket(World w, int i, int j, int k) { return null; } public String getTextureFile() { return mod_ForgeTutBuckets.itemsPng; } }
Now go back to mod_*. Add a new bucket like you do with any other items and add this:
MinecraftForge.registerCustomBucketHandler(customBucket);
Now your mod_* should look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_ForgeTutBuckets extends BaseMod { public DirtBucket dirtBucket; public static String itemsPng = "/ForgeTutorials/Buckets/items.png"; public static String terrainPng = "/ForgeTutorials/Buckets/terrain.png"; public mod_ForgeTutBuckets() { } @Override public String getVersion() { return "1.0"; } @Override public void load() { MinecraftForgeClient.preloadTexture(itemsPng); MinecraftForgeClient.preloadTexture(terrainPng); dirtBucket = (DirtBucket) new DirtBucket(1337, Block.dirt.blockID, 0).setItemName("dirtBucket"); ModLoader.AddName(dirtBucket, "Dirt Bucket"); MinecraftForge.registerCustomBucketHandler(dirtBucket); } }
Custom bucket has ID 1337, will place dirt when used, it's texture is at (0,0) in the spritesheet, and it's name is "Dirt Bucket".
You don't want bucket that you can't get without inventory editor, right? So edit fillCustomBucket to something like this:
@Override public ItemStack fillCustomBucket(World w, int i, int j, int k) { if (w.getBlockId(i, j, k) == Block.grass.blockID || w.getBlockId(i, j, k) == Block.dirt.blockID) { w.setBlockWithNotify(i, j, k, 0); return new ItemStack(this); } return null; }
If a player clicked on dirt or grass, empty bucket in player's hand will be replaced with dirt bucket.
If you want to use your own blocks instead of dirt/grass (and you probably will), use this:
w.getBlockId(i, j, k) == mod_ForgeTutBuckets.customblock.blockID
Done!
Screenshots
Sources & Textures
mod_ForgeTutBuckets: http://pastebin.com/P1QfCFhR
DirtBucket: http://pastebin.com/nwuxbTdF
/ForgeTutorials/Buckets/items.png: http://imgur.com/pZlss
How to make a custom armor set
By: Methuselah96
In this tutorial we'll be making a mod that adds a whole new set of armor.
We start out with a basic mod_*.java with no items or blocks because we will be making our own:
package net.minecraft.src; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
The first thing you need to do is import Forge by adding this at the top of the file right under package net.minecraft.src;:
import net.minecraft.src.forge.*;
Now your mod_*.java will look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
Next we have to add the four armors like we would with any other armor by adding this where we would normally put this:
public static final Item helmetTitanium = (new CamelOreItemArmor(127, armorTITANIUM, 5, 0)).setIconCoord(0, 6).setItemName("helmetTitanium"); public static final Item plateTitanium = (new CamelOreItemArmor(128, armorTITANIUM, 5, 1)).setIconCoord(0, 7).setItemName("chestplateTitanium"); public static final Item legsTitanium = (new CamelOreItemArmor(129, armorTITANIUM, 5, 2)).setIconCoord(0, 8).setItemName("leggingsTitanium"); public static final Item bootsTitanium = (new CamelOreItemArmor(130, armorTITANIUM, 5, 3)).setIconCoord(0, 9).setItemName("bootsTitanium");
The first number (127) is the item id number like usual.
The second thing we have is armorTITANIUM which you will notice causes an error. I will explain that later.
The third number (5) is the material id it is. You have to start with 5 because diamond is 4, and if you added another set of armors they would all be 6.
The last number (0) shows what kind of armor it is (0 for helmet, 1 for chestplate, 2 for leggings, and 3 for boots).
This would make our code look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Item helmetTitanium = (new CamelOreItemArmor(127, armorTITANIUM, 5, 0)).setIconCoord(0, 6).setItemName("helmetTitanium"); public static final Item plateTitanium = (new CamelOreItemArmor(128, armorTITANIUM, 5, 1)).setIconCoord(0, 7).setItemName("chestplateTitanium"); public static final Item legsTitanium = (new CamelOreItemArmor(129, armorTITANIUM, 5, 2)).setIconCoord(0, 8).setItemName("leggingsTitanium"); public static final Item bootsTitanium = (new CamelOreItemArmor(130, armorTITANIUM, 5, 3)).setIconCoord(0, 9).setItemName("bootsTitanium"); public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
We also have to have the textures for the armors preloaded, which you can learn from How to use infinite terrain and sprite indexes making the code look like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Item helmetTitanium = (new CamelOreItemArmor(127, armorTITANIUM, 5, 0)).setIconCoord(0, 6).setItemName("helmetTitanium"); public static final Item plateTitanium = (new CamelOreItemArmor(128, armorTITANIUM, 5, 1)).setIconCoord(0, 7).setItemName("chestplateTitanium"); public static final Item legsTitanium = (new CamelOreItemArmor(129, armorTITANIUM, 5, 2)).setIconCoord(0, 8).setItemName("leggingsTitanium"); public static final Item bootsTitanium = (new CamelOreItemArmor(130, armorTITANIUM, 5, 3)).setIconCoord(0, 9).setItemName("bootsTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/gui/items.png"); } public String getVersion() { return "1.0.0"; } }
now we get back to the armorTITANIUM
This is an enum (you don't have to know what that means) that has certain characteristics about that specific armor.
It defines:
How much damage the armor can take before it is completely degraded
How much the armor reduces the amount of damage that would be dealt to the player
and How enchantable it is.
How we make this enum is right above where we defined the armors we put this:
static EnumArmorMaterial armorTITANIUM = EnumHelper.addArmorMaterial("TITANIUM", 29, new int[] {2, 7, 5, 3}, 9);
if you add this to your code it should get rid of that specific error you were getting
as we can see this adds a new armor material that is titanium
the first part ("TITANIUM") is just the name of material
the second number (29) is how much damage the armor can take before it is completely degraded
the next 4 numbers (2,7,5,3) are how much the damage the armor takes for the person (2 is for the helmet, 7 is for the chestplate, 5 is for the leggings, and 3 is for the boots)
and the last number (9) is how enchantible it is (i am not entirely sure on the details of this since it was just recently added)
your code should now look like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { static EnumArmorMaterial armorTITANIUM = EnumHelper.addArmorMaterial("TITANIUM", 29, new int[] {2, 7, 5, 3}, 9) public static final Item helmetTitanium = (new CamelOreItemArmor(127, armorTITANIUM, 5, 0)).setIconCoord(0, 6).setItemName("helmetTitanium"); public static final Item plateTitanium = (new CamelOreItemArmor(128, armorTITANIUM, 5, 1)).setIconCoord(0, 7).setItemName("chestplateTitanium"); public static final Item legsTitanium = (new CamelOreItemArmor(129, armorTITANIUM, 5, 2)).setIconCoord(0, 8).setItemName("leggingsTitanium"); public static final Item bootsTitanium = (new CamelOreItemArmor(130, armorTITANIUM, 5, 3)).setIconCoord(0, 9).setItemName("bootsTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/gui/items.png"); } public String getVersion() { return "1.0.0"; } }
Now that we have this you still see that we have some more errors.
We haven't defind CamelOreItemArmor yet!
Make a new class called CamelOreItemArmor and it should start out like a basic item file with a texture (How to use infinite terrain and sprite indexes).
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreItem extends Item implements ITextureProvider { public CamelOreItem(int i) { super(i); } public String getTextureFile() { return "/CamelMod/CamelOre/gui/items.png"; } }
We want this to be called CamelOreItemArmor, call the right parameters and extend ItemArmor so we make it like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreItemArmor extends ItemArmor implements ITextureProvider { public CamelOreItemArmor(int i, EnumArmorMaterial enumarmormaterial, int j, int k) { super(i, enumarmormaterial, j, k); } public String getTextureFile() { return "/CamelMod/CamelOre/gui/items.png"; } }
The last thing we have to do to make this complete is add the textures for what it looks like when the texture is actually on you.
The first thing you have to do is implement another interface by adding this right after implements ITextureProvider:
, IArmorTextureProvider
so now it looks like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreItemArmor extends ItemArmor implements ITextureProvider, IArmorTextureProvider { public CamelOreItemArmor(int i, EnumArmorMaterial enumarmormaterial, int j, int k) { super(i, enumarmormaterial, j, k); } public String getTextureFile() { return "/CamelMod/CamelOre/gui/items.png"; } }
Now all we have to do is add one more method to tell it what image files to use so put this at the end:
public String getArmorTextureFile(ItemStack itemstack) { if(itemstack.itemID == mod_CamelOre.helmetTitanium.shiftedIndex || itemstack.itemID == mod_CamelOre.plateTitanium.shiftedIndex || itemstack.itemID == mod_CamelOre.bootsTitanium.shiftedIndex) { return "/CamelMod/CamelOre/armor/titanium_1.png"; } if(itemstack.itemID == mod_CamelOre.legsTitanium.shiftedIndex) { return "/CamelMod/CamelOre/armor/titanium_2.png"; } return "/CamelMod/CamelOre/armor/titanium_1.png"; }
This says if the player is wearing a helmet, chestplate, or boots to load titanium_1.png and if the player is wearing leggings to load titanium_2.png.
You can find the template for these images in the armor folder in minecraft.jar.
I think that's it for now. Bye.
How to make things grow with bonemeal
By: Methuselah96
In this tutorial we'll be making a mod that allows players to use bonemeal to automatically grow plants.
In a real mod, you'll probably use this with a custom crop. This tutorial explains how to add custom crops and we will be using this code to start: http://www.minecraft...111-generation/ (by Strengthowns)
This is really rather simple.
In your mod_*.java add this in the load function:
MinecraftForge.registerBonemealHandler(new CamelOreBonemealHandler());
That's all you have to do in your mod_*.java.
Now all you have to do is make CamelOreBonemealHandler.
Here is an example of mine with a description below.
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreBonemealHandler implements IBonemealHandler { public boolean onUseBonemeal(World world, int bid, int i, int j, int k) { if(!world.multiplayerWorld) { ((BlockNameherecrop)mod_Namehere.BlockNameherecrop).fertilize(world, i, j, k); } return true; } }
make sure you import forge and implement IBonemealHandler
then all you have to do is put the name of your crop java file where it has the first BlockNameherecrop
then mod_Namehere.BlockNameherecrop is just the name of your new crop
I haven't fully tested this to see if it works, but it would be nice if someone would check to see if it uses up a bonemeal when it's done.
How to make a custom tool set
By: Methuselah96
In this tutorial we'll be making a mod that adds a whole new set of tools.
We start out with a basic mod_*.java with no items or blocks because we will be making our own:
package net.minecraft.src; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
The first thing you need to do is import Forge by adding this at the top of the file right under package net.minecraft.src;:
import net.minecraft.src.forge.*;
Now your mod_*.java will look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
Next we have to add the five tools/weapon like we would with any other tool/weapon by adding this where we would normally put this:
public static final Item shovelTitanium = (new CamelOreItemSpade(127, toolTITANIUM)).setIconCoord(0, 2).setItemName("shovelTitanium"); public static final Item pickaxeTitanium = (new CamelOreItemPickaxe(128, toolTITANIUM)).setIconCoord(0, 3).setItemName("pickaxeTitanium"); public static final Item axeTitanium = (new CamelOreItemAxe(129, toolTITANIUM)).setIconCoord(0, 4).setItemName("hatchetTitanium"); public static final Item swordTitanium = (new CamelOreItemSword(130, toolTITANIUM)).setIconCoord(0, 1).setItemName("swordTitanium"); public static final Item hoeTitanium = (new CamelOreItemHoe(131, toolTITANIUM)).setIconCoord(0, 5).setItemName("hoeTitanium");
The first number (127) is the item id number like usual.
The second thing we have is toolTITANIUM which you will notice causes an error. I will explain that later.
This would make our code look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Item shovelTitanium = (new CamelOreItemSpade(127, toolTITANIUM)).setIconCoord(0, 2).setItemName("shovelTitanium"); public static final Item pickaxeTitanium = (new CamelOreItemPickaxe(128, toolTITANIUM)).setIconCoord(0, 3).setItemName("pickaxeTitanium"); public static final Item axeTitanium = (new CamelOreItemAxe(129, toolTITANIUM)).setIconCoord(0, 4).setItemName("hatchetTitanium"); public static final Item swordTitanium = (new CamelOreItemSword(130, toolTITANIUM)).setIconCoord(0, 1).setItemName("swordTitanium"); public static final Item hoeTitanium = (new CamelOreItemHoe(131, toolTITANIUM)).setIconCoord(0, 5).setItemName("hoeTitanium"); public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
We also have to have the textures for the tools preloaded, which you can learn from How to use infinite terrain and sprite indexes making the code look like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Item shovelTitanium = (new CamelOreItemSpade(127, toolTITANIUM)).setIconCoord(0, 2).setItemName("shovelTitanium"); public static final Item pickaxeTitanium = (new CamelOreItemPickaxe(128, toolTITANIUM)).setIconCoord(0, 3).setItemName("pickaxeTitanium"); public static final Item axeTitanium = (new CamelOreItemAxe(129, toolTITANIUM)).setIconCoord(0, 4).setItemName("hatchetTitanium"); public static final Item swordTitanium = (new CamelOreItemSword(130, toolTITANIUM)).setIconCoord(0, 1).setItemName("swordTitanium"); public static final Item hoeTitanium = (new CamelOreItemHoe(131, toolTITANIUM)).setIconCoord(0, 5).setItemName("hoeTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/gui/items.png"); } public String getVersion() { return "1.0.0"; } }
now we get back to the toolTITANIUM
This is an enum (you don't have to know what that means) that has certain characteristics about that specific tool.
It defines:
What blocks it can harvest successfully.
How many uses it has before it becomes completely degraded
How much faster it breaks the block if it's a certain block
How much damage it does to other entities
and How enchantable it is.
How we make this enum is right above where we defined the armors we put this:
static EnumToolMaterial toolTITANIUM = EnumHelper.addToolMaterial("TITANIUM", 2, 500, 7F, 3, 9);
if you add this to your code it should get rid of that specific error you were getting
as we can see this adds a new tool material that is titanium
the first part ("TITANIUM") is just the name of material
the second number (2) defines what blocks it can harvest successfully (diamond = 3, iron = 2, stone = 1, gold and wood = 0)
the next number (500) are how many uses it has before it becomes completely degraded
the fourth number (7F) is how much faster it breaks the block if it's a certain block
the fifth number (3) is how much damage it does to other entities
and the last number (9) is how enchantible it is (i am not entirely sure on the details of this since it was just recently added)
your code should now look like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { static EnumToolMaterial toolTITANIUM = EnumHelper.addToolMaterial("TITANIUM", 2, 500, 7F, 3, 9); public static final Item shovelTitanium = (new CamelOreItemSpade(127, toolTITANIUM)).setIconCoord(0, 2).setItemName("shovelTitanium"); public static final Item pickaxeTitanium = (new CamelOreItemPickaxe(128, toolTITANIUM)).setIconCoord(0, 3).setItemName("pickaxeTitanium"); public static final Item axeTitanium = (new CamelOreItemAxe(129, toolTITANIUM)).setIconCoord(0, 4).setItemName("hatchetTitanium"); public static final Item swordTitanium = (new CamelOreItemSword(130, toolTITANIUM)).setIconCoord(0, 1).setItemName("swordTitanium"); public static final Item hoeTitanium = (new CamelOreItemHoe(131, toolTITANIUM)).setIconCoord(0, 5).setItemName("hoeTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/gui/items.png"); } public String getVersion() { return "1.0.0"; } }
Now that we have this you still see that we have some more errors.
We haven't defind CamelOreItemSpade, CamelOreItemPickaxe, CamelOreItemAxe, CamelOreItemSword, or CamelOreItemHoe yet!
Make five new class called CamelOreItemSpade, CamelOreItemPickaxe, CamelOreItemAxe, CamelOreItemSword, and CamelOreItemHoe and they should all start out like a basic item file with a texture (How to use infinite terrain and sprite indexes).
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreItem extends Item implements ITextureProvider { public CamelOreItem(int i) { super(i); } public String getTextureFile() { return "/CamelMod/CamelOre/gui/items.png"; } }
I'm just going to show you one of them because they will all be basically the same.
The first thing you have to do is have it extend what it really is (i.e. CamelOreItemSpade extends ItemSpade and CamelOreItemPickaxe extends Item Pickaxe)
the only other thing you have to is make sure you have the right parameters in your super tag
All of them are going to look like this:
super(i, enumtoolmaterial);
here is an example CamelOreItemPickaxe:
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreItemPickaxe extends ItemPickaxe implements ITextureProvider { protected CamelOreItemPickaxe(int i, EnumToolMaterial enumtoolmaterial) { super(i, enumtoolmaterial); } public String getTextureFile() { return "/CamelMod/CamelOre/gui/items.png"; } }
The last thing we have to do is tell Minecraft that we have these knew tools.
So we have to register them in your mod_CamelOre.java.
You only need to register the pickaxe, shovel, and axe because those are the only ones that make breaking blocks faster.
So we are going to add this:
MinecraftForge.setToolClass(pickaxeTitanium, "pickaxe", 2); MinecraftForge.setToolClass(shovelTitanium, "shovel", 2); MinecraftForge.setToolClass(axeTitanium, "axe", 2);
the first thing is just the name of the item
the second thing is just pickaxe, shovel, or axe
and the last thing is the harvest level that we defined in our enum thing above(What blocks it can harvest successfully.)
when we put this into our mod_CamelOre.java it looks like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { static EnumToolMaterial toolTITANIUM = EnumHelper.addToolMaterial("TITANIUM", 2, 500, 7F, 3, 9); public static final Item shovelTitanium = (new CamelOreItemSpade(127, toolTITANIUM)).setIconCoord(0, 2).setItemName("shovelTitanium"); public static final Item pickaxeTitanium = (new CamelOreItemPickaxe(128, toolTITANIUM)).setIconCoord(0, 3).setItemName("pickaxeTitanium"); public static final Item axeTitanium = (new CamelOreItemAxe(129, toolTITANIUM)).setIconCoord(0, 4).setItemName("hatchetTitanium"); public static final Item swordTitanium = (new CamelOreItemSword(130, toolTITANIUM)).setIconCoord(0, 1).setItemName("swordTitanium"); public static final Item hoeTitanium = (new CamelOreItemHoe(131, toolTITANIUM)).setIconCoord(0, 5).setItemName("hoeTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/gui/items.png"); MinecraftForge.setToolClass(pickaxeTitanium, "pickaxe", 2); MinecraftForge.setToolClass(shovelTitanium, "shovel", 2); MinecraftForge.setToolClass(axeTitanium, "axe", 2); } public String getVersion() { return "1.0.0"; } }
That's it I think.
How to set the harvesting level for a block
By: Methuselah96
In this tutorial I'll be showing you how to set the harvesting level (or what type of pickaxe you need) for a block. This is extremely easy.
If you haven't read the custom texture tutorial, please do: How to use infinite terrain and sprite indexes.
Ok, we are going to start with a basic block that has a texture already made for it.
Here is our mod_CamelOre.java:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Block oreTitanium = new CamelOreBlockOre(123, 0).setHardness(3F).setResistance(5F).setStepSound(Block.soundStoneFootstep).setBlockName("oreTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/terrain.png"); ModLoader.RegisterBlock(oreTitanium); } public String getVersion() { return "1.0.0"; } }
There is only one thing we have to do and that is add this line:
MinecraftForge.setBlockHarvestLevel(oreTitanium, "pickaxe", 2);
oreTitanium is just the name of the block.
"pickaxe" is what you're setting the harvest level too
I think it can be either pickaxe, shovel, or axe
You can also register the same block with different tools
2 is the harvest level (diamond = 3, iron = 2, stone = 1, gold and wood = 0)
now your mod_CamelOre looks like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Block oreTitanium = new CamelOreBlockOre(123, 0).setHardness(3F).setResistance(5F).setStepSound(Block.soundStoneFootstep).setBlockName("oreTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/terrain.png"); ModLoader.RegisterBlock(oreTitanium); MinecraftForge.setBlockHarvestLevel(oreTitanium, "pickaxe", 2); } public String getVersion() { return "1.0.0"; } }
That's it(I hope).
How to remove tool effectiveness from a block
By: Methuselah96
In this tutorial I'll be showing you how to remove the thing that makes diamond pickaxes mine faster for a block. This is also extremely easy.
If you haven't read the harvest level tutorial, please do: How to set the harvesting level for a block.
Ok, we are going to start with a basic block that has a texture already made for it and a harvest level.
Here is our mod_CamelOre.java:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Block oreTitanium = new CamelOreBlockOre(123, 0).setHardness(3F).setResistance(5F).setStepSound(Block.soundStoneFootstep).setBlockName("oreTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/terrain.png"); ModLoader.RegisterBlock(oreTitanium); MinecraftForge.setBlockHarvestLevel(oreTitanium, "pickaxe", 2); } public String getVersion() { return "1.0.0"; } }
There is only one thing we have to do and that is add this line:
MinecraftForge.removeBlockEffectiveness(oreTitanium,"pickaxe");
oreTitanium is just the name of the block.
"pickaxe" is what tool you're removing the effectiveness from
That's it...
now your mod_CamelOre looks like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Block oreTitanium = new CamelOreBlockOre(123, 0).setHardness(3F).setResistance(5F).setStepSound(Block.soundStoneFootstep).setBlockName("oreTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/terrain.png"); ModLoader.RegisterBlock(oreTitanium); MinecraftForge.setBlockHarvestLevel(oreTitanium, "pickaxe", 2); MinecraftForge.removeBlockEffectiveness(oreTitanium,"pickaxe"); } public String getVersion() { return "1.0.0"; } }
Again, that's it(I hope).
How to add your blocks and damage value items to creative mode
Prerequisites
Creating A New Block
Crating A Block With Damage Values - Coming Soon!
Crating An Item With Damage Values - Coming Soon!
Minecraft Version
1.1
Tutorial
In this tutorial I am going to show you how to add blocks, blocks with damage values, and items with damage values to the creative mode inventory. I assume you know how to create a new block and item and install Forge. If you don?t you can read tutorials here: Creating A New Block and How to install Minecraft Forge.
We?re going to start out with a normal block file, CamelOreBlockOre:
package net.minecraft.src; public class CamelOreBlockOre extends BlockOre { public CamelOreBlockOre(int i, int j) { super(i, j); } }
The first thing we need to do is import something so that we can use it:
import java.util.*;
This makes our CamelOreBlockOre look like this:
package net.minecraft.src; import java.util.*; public class CamelOreBlockOre extends BlockOre { public CamelOreBlockOre(int i, int j) { super(i, j, Material.rock); } }
Then this is the basic function that we add to each block file if we want it to show up in creative mode:
public void addCreativeItems(ArrayList itemList) { itemList.add(new ItemStack(this)); }
This will make it so that every block that uses that block file will show up in the inventory making our CamelOreBlockOre look like this:
package net.minecraft.src; import java.util.*; public class CamelOreBlockOre extends BlockOre { public CamelOreBlockOre(int i, int j) { super(i, j, Material.rock); } public void addCreativeItems(ArrayList itemList) { itemList.add(new ItemStack(this)); } }
But, what if we don?t want all of them in creative mode, what if we just want some of the ones that use this block file to be in the inventory. Then we would make an if statement that would look something like this:
public void addCreativeItems(ArrayList itemList) { if(blockID == mod_CamelOre.oreTitanium.blockID) { itemList.add(new ItemStack(this)); } }
This makes it so the only block that gets added to the inventory in this file is titanium ore.
That?s how to add a normal block to the inventory.
Now, what if this block has damage values? What are we going to do then? This is what we have to do:
public void addCreativeItems(ArrayList itemList) { for(int l1 = 0; l1 < 2; l1++) { itemList.add(new ItemStack(this, 1, l1)); } }
If you?re block has 2 damage values that is what you would add. If your block had 3 damage values you would change the 2 to a 3, and so on and so forth.
But again what if only some blocks have damage values and the others are normal in the same block file? Then you would have to make an if statement like before:
public void addCreativeItems(ArrayList itemList) { if(blockID == mod_CamelOre.oreTitanium.blockID) { for(int l1 = 0; l1 < 2; l1++) { itemList.add(new ItemStack(this, 1, l1)); } } else { itemList.add(new ItemStack(this)); } }[code] This would make it so only titanium ore would have multiple damage values and the rest of them would only have one. Then comes the ultimate example. What if some blocks have damage values, some don?t, and some you don?t even want to show in the inventory in the same class? Here it is: [code] public void addCreativeItems(ArrayList itemList) { if(blockID == mod_CamelOre.oreTitanium.blockID) { for(int l1 = 0; l1 < 2; l1++) { itemList.add(new ItemStack(this, 1, l1)); } } else if(blockID == mod_CamelOre.oreAluminum.blockID) { itemList.add(new ItemStack(this)); } }
This basically says that if the block is titanium ore then it will put 2 damage values of it in the inventory. If the block is aluminum ore it will put it in without any damage values. And if the block is anything else it will not be in the inventory. Finally, we?re done the blocks.
Items automatically show up in your inventory so you don?t have to worry about them unless they have damage values. You would add it EXACTLY the same way so here you go:
package net.minecraft.src; import java.util.*; public class ItemDye extends Item { public ItemDye(int i) { super(i); } public void addCreativeItems(ArrayList itemList) { for (int x = 0; x < 16; x++) { itemList.add(new ItemStack(this, 1, x)); } } }
Final Code
CamelOreBlockOre.java
package net.minecraft.src; import java.util.*; public class CamelOreBlockOre extends BlockOre { public CamelOreBlockOre(int i, int j) { super(i, j, Material.rock); } public void addCreativeItems(ArrayList itemList) { itemList.add(new ItemStack(this)); } }
Author
Methuselah96
How to make a new redstone wire
By: Methuselah96
In this tutorial I'll be showing you how to make a block that can connect to redstone wire.
If you haven't read the custom texture tutorial, please do: How to use infinite terrain and sprite indexes.
Ok, we are going to start with a basic block that has a texture already made for it.
Here is our mod_CamelOre.java:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public static final Block oreTitanium = new CamelOreBlockCopperWire(123, 0).setHardness(3F).setResistance(5F).setStepSound(Block.soundStoneFootstep).setBlockName("oreTitanium"); public mod_CamelOre() { } public void load() { MinecraftForgeClient.preloadTexture("/CamelMod/CamelOre/terrain.png"); ModLoader.RegisterBlock(oreTitanium); } public String getVersion() { return "1.0.0"; } }
We are not going to do anything to the mod_CamelOre.java, but actually do something to the CamelOreBlockCopperWire.java.
Here it is:
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreBlockCopperWire extends BlockRedstoneWire implements ITextureProvider { public CamelOreBlockCopperWire(int i, int j) { super(i, j); } public int idDropped(int i, Random random, int j) { return mod_CamelOre.copperWire.blockID; } public String getTextureFile() { return "/CamelMod/CamelOre/terrain.png"; } }
This is what we have to add:
public boolean canConnectRedstone(IBlockAccess iba, int i, int j, int k, int dir) { return true; }
Now it can connect to redstone.
package net.minecraft.src; import net.minecraft.src.forge.*; public class CamelOreBlockCopperWire extends BlockRedstoneWire implements ITextureProvider { public CamelOreBlockCopperWire(int i, int j) { super(i, j); } public int idDropped(int i, Random random, int j) { return mod_CamelOre.copperWire.blockID; } public boolean canConnectRedstone(IBlockAccess iba, int i, int j, int k, int dir) { return true; } public String getTextureFile() { return "/CamelMod/CamelOre/terrain.png"; } }
How to use version detect
Prerequisites
How to install Minecraft Forge
Minecraft Version
1.1
Tutorial
In this tutorial I am going to teach you how to make Forge give an error if a user isn?t using an up-to-date Forge so that they know why your mod isn?t working. I assume you have installed Forge. If you haven?t you can see my tutorial here: How to install Minecraft Forge.
We are going to start with a basic mod_CamelOre that doesn?t have anything:
package net.minecraft.src; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
The first thing we need to do is import forge so that we can use it like this:
import net.minecraft.src.forge.*;
This makes our mod_CamelOre look like this:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { } public String getVersion() { return "1.0.0"; } }
There are two different kinds of version detecting you can do: regular and strict. I?ll go over regular first.
If you are doing regular version detecting you would add this in your load function:
MinecraftForge.versionDetect("CamelOre", 1, 2, 2);
What this basically says is that the mod CamelOre was developed with Forge 1.2.2. You need to know some terms so I can explain you to this easily. The 1 is the major version number. The first 2 is the minor version number. The second 2 is the revision verison number. Ok, now I can explain it easily. If the major version is different it will crash Minecraft. If the minor version of the user is lower than the minor version that the mod was developed in, it will crash. If it is a newer minor version number it will give a warning. If the revision number is too old it will crash. If the revision number is newer it will do nothing. Here?s a table:
Major Version # Minor Version # Revision Version #
User?s is older than developer?s Crash Crash Crash
User?s is newer than developer?s Crash Warning Nothing
I hope you understood that (it?s hard to explain). This is what our new mod_CamelOre would look like:
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { MinecraftForge.versionDetect("CamelOre", 1, 2, 2); } public String getVersion() { return "1.0.0"; } }
Now for strict version detecting. This is what you would add:
MinecraftForge.versionDetectStrict("CamelOre", 1, 2, 2);
The only difference between regular and strict is if the major version number is newer it will crash. Here?s the new table:
Major Version # Minor Version # Revision Version #
User?s is older than developer?s Crash Crash Crash
User?s is newer than developer?s Crash Crash Nothing
That?s it!
Final Code
mod_CamelOre.java
package net.minecraft.src; import net.minecraft.src.forge.*; public class mod_CamelOre extends BaseMod { public mod_CamelOre() { } public void load() { MinecraftForge.versionDetect("CamelOre", 1, 2, 2); } public String getVersion() { return "1.0.0"; } }
Author
Methuselah96 - To post a comment, please login.
6
3
1
Edit-ninja'd' very clever.
2