ItemStack.addEnchantment takes two arguments, the first being the Enchantment Id, the second the level of Enchantment.
If you still need help, check out my other tutorial on crafting that goes into more detail on the basics.
You can find it here: http://www.minecraft...afting-recipes/
As a way to learn about how NBT Tag Compounds work, we're going to design a system that will automatically generate crafting (NOT enchanting) recipes for every single Enchanted Book in the game.
Prerequisites for this tutorial:
You will need to be comfortable with arrays. This also presumes the ability to navigate multiple levels of 'for' loops.
Arrays are not specific to Java, but there are many websites dedicated to teaching Java that can teach you about arrays. Or you can check out my other tutorial (in the spoiler above), in which I give some basic info on how arrays function along with some examples of them in use, conveniently also dealing with automatically generating recipes.
Another useful tip, for those of you who don't know, is System.out.println(""). This guy right here is going to solve innumerable problems, if used correctly. Whenever you don't know what a variable is, what a function returns, etc., just use this to print out the value. Here's what it would look like:
I like to put [YOUR MOD] in there just to make it stand out in the console log.
Be sure to identify the variable whose value you are printing, otherwise it will look like this:
Finally, variable can be anything. Here are some examples I've used while debugging my own code:
This runs through the Enchantment List and outputs valid array indices, as well as the max enchantment level of the enchantment stored at that index in the array.
for (int i = 0; i < Enchantment.enchantmentsList.length; ++i)
{
if (Enchantment.enchantmentsList[i] != null)
{
System.out.println("[MY MOD] Valid enchantment value: " + i);
System.out.println("[MY MOD] Enchantment max level: " + Enchantment.enchantmentsList[i].getMaxLevel());
}
}
Try it out for yourself and see how much more readable it is than the first example. Try putting such 'debug' lines in other places in your code. For example, if you're not sure how much damage your new weapon is inflicting, you can use this technique to print the damage in the log whenever that function is called. Very handy.
Random tip:
The ++ notation has two placements, before and after a variable. If it precedes the variable, the variable will be incremented by 1 BEFORE any calculations are done on it; conversely, variable++ will be incremented only AFTER said calculations are done. As a simple demonstration, try this:
int i = 0;
int j = 0;
System.out.println("i = " + (++i));
System.out.println("j = " + (j++));
As always, knowledge of Java or any other programming language will go a long way in helping you solve problems. I don't actually have any background in Java, but I programmed in C++ before, and there are many similarities.
Okay, on to the real tutorial. If you're reading this, I'll assume that you're beyond the basics.
An essential tool while using Eclipse is the Class Outline in the right-hand sidebar. There you will see all the variables and methods available for the class currently open, and clicking one brings you directly to its code, as well as highlighting all occurrences of it with little rectangles just to the right of the main window, which you can also click.
One last thing: if you're using Eclipse, if you hold ctrl and hover over any method or variable, you have the option to jump to its declaration, implementation or even super implementation. Ctrl-clicking (not hovering) will bring you immediately to the first option in the list. If you A VERY handy trick somebody mentioned in the forums here (sorry don't remember who).
PART I: Using the ItemStack.addEnchantment method to learn about NBT Tags
Here is the code for ItemStack.addEnchantment:
public void addEnchantment(Enchantment par1Enchantment, int par2)
{
if (this.stackTagCompound == null)
{
this.setTagCompound(new NBTTagCompound());
}
if (!this.stackTagCompound.hasKey("ench"))
{
this.stackTagCompound.setTag("ench", new NBTTagList("ench"));
}
NBTTagList nbttaglist = (NBTTagList)this.stackTagCompound.getTag("ench");
NBTTagCompound nbttagcompound = new NBTTagCompound();
nbttagcompound.setShort("id", (short)par1Enchantment.effectId);
nbttagcompound.setShort("lvl", (short)((byte)par2));
nbttaglist.appendTag(nbttagcompound);
}
}
There is much we can learn from this. We're going to be doing lots of ctrl-clicking here, so I'll annotate it by placing whatever needs to be ctrl-clicked in < >.
ctrl-click <NBTTagCompound> and you'll be brought to the NBTTagCompound class. Scroll through the Outline to see what's available.
Now back to our example. Looking at the first section, we see here that every ItemStack has an NBTTagCompound, and if not, the function creates a new one. If stackTagCompound doesn't already contain an NBTTagList with the name "ench", it creates one.
This is a bit misleading, because it doesn't really create a new NBTTagList, but instead a new NBTBase with a name. <NBTTagList> (remember? ctrl-click) and then <super> - you will see what I mean. This new NBTBase will store the beginning of our new list named "ench".
<setTag>, you'll see that we're storing a new NBTTagList named "ench" in a Map. Sorry, I'm not going to explain Maps here. <NBTBase>. In the NBTBase Outline (you remembered to ctrl-click, right?), click newTag (byte, String). Here's what we see:
/**
* Creates and returns a new tag of the specified type, or null if invalid.
*/
public static NBTBase newTag(byte par0, String par1Str)
{
switch (par0)
{
case 0:
return new NBTTagEnd();
case 1:
return new NBTTagByte(par1Str);
case 2:
return new NBTTagShort(par1Str);
case 3:
return new NBTTagInt(par1Str);
case 4:
return new NBTTagLong(par1Str);
case 5:
return new NBTTagFloat(par1Str);
case 6:
return new NBTTagDouble(par1Str);
case 7:
return new NBTTagByteArray(par1Str);
case 8:
return new NBTTagString(par1Str);
case 9:
return new NBTTagList(par1Str);
case 10:
return new NBTTagCompound(par1Str);
case 11:
return new NBTTagIntArray(par1Str);
default:
return null;
}
}
So newTag creates a new NBTBase, the byte par0 tells us what the stored variable type is, and String par1Str stores the value of that variable. A String is used because it can be recast into any of the other types. Notice that NBTBase can also store NBTTagCompounds and NBTTagLists. Interesting.
The first line stores a pointer to our newly created or old list of enchantments. <getTag> and you'll notice it returns type NBTBase, so we have to cast it as NBTTagList. In a slick move of coding slight of hand, NBTBase can also store NBTTagLists, which store NBTBases.
The second line makes our new NBTTagCompound, which is essentially NBTBase with more functionality (which you can see by comparing the two class Outlines). It is able to store "named" tags.
Then, we <setShort> twice, each time creating a new tag with the first argument as it's name, the second argument as the stored data value.
Finally, we append this list of tags onto our list of lists (of tags).
What does all of this mean?
1. NBTBase, aka tags, are being stored in a list, <NBTTagList>. Note that these tags are "nameless." Look at the Outline. Of particular note here are tagCount() and tagAt(int) - these will allow us to run through the list like an array, among other things. If you know any C++, this is roughly the equivalent of a dynamic linked list.
2. NBTTagLists are stored in another list, NBTTagCompound. A list of lists, but NBTTagCompound stores a list of "named" tags. So what setTag() did above is to create a new, empty list named "ench" and store it in the tagMap. We can access this list, and tags in it, by using:
3. If you're paying attention, you'll say, "Wait a second, aren't the NBTTagLists storing lists of NBTTagCompounds?" Well, yes. The neat thing about it is NBTTagLists, NBTTagCompounds and NBTBase can all store and be cast as each other. That makes it extremely confusing to work with, but also very versatile. It can be useful to think of TagLists as storing lists of TagCompounds...
4. BUT, every NBT file must start with an NBTTagCompound, so technically it's more correct to say that NBTTagCompounds store lists of NBTTagLists (and other Tag types).
5. These tags are a low-memory way of storing lots of information, as you only need to store one tag that points to the Map, and from there you can access any of the linked data. Pretty awesome.
For some clearer information on the role of each NBT type, see: http://wiki.vg/NBT
To illustrate some of the values that the NBT functions will return, try making adding an enchantment to an item and printing out different parameters to the screen. Since we're going to be dealing with Enchanted Books in this tutorial, here's what I did:
If you're going to copy, I urge you to manually type it in yourself; as you type, Eclipse will bring up menus that list the functions that are available to you. For instance, say you type:
bookList[bookListIndex-1].stackTagCompound
As soon as you press '.' a list of functions will come up. This is very useful when working with the NBT system, as all 3 are inter-convertible but their available functions are very different.
Once you've got your code ready, start up Minecraft, quit, and study your log carefully. You'll notice that most of these don't give us anything we can really work with, but there are two that stick out:
Notice that they are exactly the same, except for the final string argument in getShort. If your Item or Block has other lists, you can change the getTagList argument from "ench" to whatever you need.
<NBTTagCompound> and look again in the Outline to see the functions available. hasKey(String) is one that will be very useful so you don't throw null pointer exceptions. Add that to your list from earlier (remember when we looked at <NBTTagList> functions?) and you'll be well on your way to not crashing your game. If you're careful.
Okay, so now we know a little bit about how to use NBT Tags, and we also made some code that adds every (currently level 1) enchanted book to an array. What to do? Well, maybe you've always been bothered by the experience system, but you want to use enchantments; why not make Enchanted Books craftable instead? Or perhaps you feel crafting them with rare materials gives the game more 'atmosphere.' Or maybe you just want to learn a little more about programming.
Whatever the case, the next step in this tutorial is to build some code that automatically generates recipes for all of the Enchanted Books we just made.
PART II: Building an Automatic Enchanted Book Recipe Generator
Let me start by saying that there are easier ways to accomplish what we're about to do, but that wouldn't be any fun, would it? I'll put a comparison of 3 different methods at the end of this section.
At this point, unless you want your main mod class to get really messy, I'd encourage you to create a new class called RegisterCraftingRecipes or whatever, then call that from your main class where you normally add recipes. I also made a separate function to register the recipes in that class called addRecipes, so I call mine like this:
(new RegisterCraftingRecipes()).addRecipes();
At any rate, let's think about this for a minute.
What we want is for an Enchanted Book X, use the following items {list} for crafting. Take a look at vanilla RecipesTools and see if you can figure out what we'll need.
public class RecipesTools
{
private String[][] recipePatterns = new String[][] {{"XXX", " # ", " # "}, {"X", "#", "#"}, {"XX", "X#", " #"}, {"XX",
" #", " #"}};
private Object[][] recipeItems;
public RecipesTools()
{
this.recipeItems = new Object[][] {{Block.planks, Block.cobblestone, Item.ingotIron, Item.diamond,
Item.ingotGold}, {Item.pickaxeWood, Item.pickaxeStone, Item.pickaxeIron, Item.pickaxeDiamond, Item.pickaxeGold},
{Item.shovelWood, Item.shovelStone, Item.shovelIron, Item.shovelDiamond, Item.shovelGold}, {Item.axeWood,
Item.axeStone, Item.axeIron, Item.axeDiamond, Item.axeGold}, {Item.hoeWood, Item.hoeStone, Item.hoeIron,
Item.hoeDiamond, Item.hoeGold}};
}
/**
* Adds the tool recipes to the CraftingManager.
*/
public void addRecipes(CraftingManager par1CraftingManager)
{
for (int i = 0; i < this.recipeItems[0].length; ++i)
{
Object object = this.recipeItems[0][i];
for (int j = 0; j < this.recipeItems.length - 1; ++j)
{
Item item = (Item)this.recipeItems[j + 1][i];
par1CraftingManager.addRecipe(new ItemStack(item), new Object[] {this.recipePatterns[j], '#', Item.stick, 'X',
object});
}
}
par1CraftingManager.addRecipe(new ItemStack(Item.shears), new Object[] {" #", "# ", '#', Item.ingotIron});
}
}
If you said we need a double array of Objects[][], you're right. For this tutorial, we'll be using shapeless recipes, but if you want an added level of complexity, you can see how shaped recipes could be handled above. We already have an array of Level 1 Enchanted books (if you don't, see PART I). We'll leave it at that for now; later we'll turn it into a double array so that bookList[0] contains all Enchanted Books of type 0, bookList[0][0] would be type 0, level 1, [0][1] type 0 level 2 and so on.
So let's take a look at our RegisterCraftingRecipes class:
public class RegisterCraftingRecipes
{
private static Object[][] bookRecipeMatrix;
private static ItemStack[] bookList;
private static int bookListIndex = 0;
public RegisterCraftingRecipes()
{
this.bookList = new ItemStack[256];
for (int i = 0; i < Enchantment.enchantmentsList.length; ++i)
{
if (Enchantment.enchantmentsList[i] != null)
{
this.bookList[this.bookListIndex] = new ItemStack(Item.enchantedBook);
this.bookList[this.bookListIndex++].addEnchantment(Enchantment.enchantmentsList[i], 1);
}
}
this.bookRecipeMatrix = new Object[this.bookListIndex][];
for (int j = 0; j < this.bookListIndex; ++j)
{
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, new ItemStack(Item.dyePowder,1,j)};
}
}
Note that bookList[] is of type ItemStack; this is so we can use the addEnchantment method.
We use bookListIndex so we can keep track of where in the bookList array we are, no matter how many enchantments are added (or removed) at a later date.
After we've built our bookList, we can initialize bookRecipeMatrix. Create a new array of arrays of length
bookListIndex. Then we have to run through each element bookRecipeMatrix[j] and assign a new array of Objects to it.
This is where we put in whatever Objects we want Enchanted Books to be crafted from.
In the interest of keeping it simple, we're using a book, a feather, and dye of color 'j' to make the recipes unique. But there are only 16 types of dye, you say, and currently 21 level 1 Enchanted Books. Yep. Values higher than 15 all return 'white dye.' We'll deal with this problem later, but for now, it will suffice.
We've got all of our variables initialized, now it's time to add the recipes. Let's look at <addShapelessRecipe> syntax:
public static void addShapelessRecipe(ItemStack output, Object... params);
bookList[] is already an ItemStack, so that's ready to go. But what to do about Object bookRecipeMatrix[][]? It's the right type, but how do we iterate through the second array? Something really cool I learned while putting this together, is you DON'T HAVE TO. That's right. Look at this syntax:
for (int i = 0; i < this.bookListIndex; ++i)
{
GameRegistry.addShapelessRecipe(this.bookList[i], this.bookRecipeMatrix[i]);
}
Now isn't that slick? this.bookRecipeMatrix[i] IS { obj1, obj2, obj3...}, so you can just stick that in there as is. Wow. Doesn't get much easier than that, does it? Sure beats my initial implementation method with several functions, embedded if statements and switches.
There is still the problem of there only being 16 kinds of dye, and what if you want to use other ingredients in your recipe based on the enchantment type? All we need is a way to tell what enchantment is on the book when we're building the bookRecipeMatrix - oh wait, remember those NBT tags? Sweet. Let's look again at where we initialize our variables:
this.bookRecipeMatrix = new Object[this.bookListIndex][];
for (int j = 0; j < this.bookListIndex; ++j)
{
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, new ItemStack(Item.dyePowder,1,j)};
}
}
What we want to do here is separate each Recipe based on its index 'j.' Recall that bookList[j] will return the
EnchantedBook with a specific enchantment id. It's important to note that the enchantment id value is NOT the same as the value of 'j', but rather is stored in an NBT Tag connected to the ItemStack. While not necessary, for the sake of readability we're going to create a local int effectId and use it to temporarily store the NBT tag value. From there, we can either use a switch or if / else if statements to sort out our list. Here's what it looks like with only one special recipe:
for (int j = 0; j < this.bookListIndex; ++j)
{
int effectId = ((NBTTagCompound)this.bookList[j].getEnchantmentTagList().tagAt(0)).getShort("id");
if (effectId == Enchantment.flame.effectId) {
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, Item.magmaCream};
}
else {
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, new ItemStack(Item.dyePowder,1,j)};
}
}
From here, you can easily flesh it out to use whatever Items (or Blocks) you want in your recipes. There's no need to keep the recipes the same length, either, as we saw above in the addShapelessRecipe syntax.
There's only one last thing to do, if you're really dedicated to making ALL of the Enchanted Books available through crafting, and that's to add in every level of enchantment, not just level 1. See if you can figure it out with what you've learned. If you really need help, I'll put some hints here:
Did you really try? Ok here's a hint:
Try making bookList a double array. Need an explanation?
The bookListIndex will stay the same, but at each bookList[bookListIndex] will be an array of EnchantedBooks, one for each level of the enchantment.
Having trouble getting the max level of the enchantment?
You don't need to mess around with NBT tags for the max level: you can use "Enchantment.enchantmentsList[i].getMaxLevel();" instead.
Still need help? Here's some code:
/*
* ENCHANTED BOOK CRAFTING RECIPES
* Variable initialization
*/
this.bookList = new ItemStack[256][];
for (int i = 0; i < Enchantment.enchantmentsList.length; ++i)
{
if (Enchantment.enchantmentsList[i] != null)
{
int maxLevel = Enchantment.enchantmentsList[i].getMaxLevel();
this.bookList[this.bookListIndex] = new ItemStack[maxLevel];
for (int j = 0; j < maxLevel; ++j)
{
this.bookList[this.bookListIndex][j] = new ItemStack(Item.enchantedBook);
this.bookList[this.bookListIndex][j].addEnchantment(Enchantment.enchantmentsList[i], j
+ 1);
}
++this.bookListIndex;
}
}
this.bookRecipeMatrix = new Object[this.bookListIndex][];
for (int j = 0; j < this.bookListIndex; ++j)
{
int effectId = ((NBTTagCompound)this.bookList[j][0].getEnchantmentTagList().tagAt(0)).getShort("id");
if (effectId == Enchantment.flame.effectId)
{
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, Item.magmaCream};
}
else
{
this.bookRecipeMatrix[j] = new Object[] {Item.book, Item.feather, new ItemStack
(Item.dyePowder,1,j)};
}
}
/*
* Adding the recipes
*/
for (int i = 0; i < this.bookListIndex; ++i)
{
for (int j = 0; j < this.bookList[i].length; ++j)
{
GameRegistry.addShapelessRecipe(this.bookList[i][j], this.bookRecipeMatrix[i]);
}
}
If you've been following along up to this point, and if you're especially astute, you'll notice that we have the same recipe for every Enchanted Book that has the same effect id. That's not good. To fix that, you're going to need to make a TRIPLE array. Oh yes, now we're talking.
We're going to change "private static Object[][] bookRecipeMatrix;" to "private static Object[][][] bookRecipeMatrix;"
I'm assuming you can take it from here. Good luck!
PART III: Comparing 3 different methods of adding all Enchanted Book Recipes
Here we'll discuss pros and cons of 3 different methods;
Method I: Type all recipes by hand
// The BAD way to do it
ItemStack magicBook = new ItemStack(Item.enchantedBook);
magicBook.addEnchantment(0, 1);
GameRegistry.addShapelessRecipe(magicBook, Object... params);
magicBook = new ItemStack(Item.enchantedBook);
magicBook.addEnchantment(0, 2);
GameRegistry.addShapelessRecipe(magicBook, Object... params);
// The GOOD way to do it
ItemStack magicBook = new ItemStack(Item.enchantedBook);
magicBook.addEnchantment(Enchantment.protection, 1);
GameRegistry.addShapelessRecipe(magicBook, Object... params);
magicBook = new ItemStack(Item.enchantedBook);
magicBook.addEnchantment(Enchantment.protection, 2);
GameRegistry.addShapelessRecipe(magicBook, Object... params);
Note that the GOOD way only differs in that we use Enchantment.<enchanmentId> instead of writing a number such as '0.' The reason this is better is two-fold: it's more readable and if the id ever changes, our code still works. For example, next update Mojang makes protection id 5 and flame id 0. In the BAD method, our recipe now crafts books of flame, whereas the GOOD example is still correct.
PRO: Extremely simple, automatically stays up to date (if you used the GOOD method). It's also the ideal method if your recipes will not follow a standardized format.
CON: Tedious to write, 3 lines of code per recipe (currently 70 unique enchanted books, so 210 lines of code), not interesting to do.
Method II: Mimic the method RecipesTools uses, also using the method we developed to auto-generate a list of all the Enchanted Books
// Here is the code we developed to auto-generate all the books we want to craft
this.bookList = new ItemStack[256][];
for (int i = 0; i < Enchantment.enchantmentsList.length; ++i) {
if (Enchantment.enchantmentsList[i] != null) {
int maxLevel = Enchantment.enchantmentsList[i].getMaxLevel();
this.bookList[this.bookListIndex] = new ItemStack[maxLevel];
for (int j = 0; j < maxLevel; ++j) {
this.bookList[this.bookListIndex][j] = new ItemStack(Item.enchantedBook);
this.bookList[this.bookListIndex][j].addEnchantment(Enchantment.enchantmentsList[i], j + 1);
}
++this.bookListIndex;
}
}
// This is the method RecipesTools uses to define recipe ingredients, adapted to our needs:
private Object[][][] recipeItems;
public RecipesBooks()
{
this.recipeItems= new Object[][][]
{{{Item.book, Item.feather, Item.legsLeather}, {Item.book, Item.feather, Item.legsIron}, { }, ... etc. for all levels of Enchantment.protection},
{{Item.book, Item.feather, Item.magmaCream, Item.legsLeather}, {Item.book, Item.feather, Item.magmaCream, Item.legsIron}... etc. for all levels of Enchantment.fireProtection},
{{ etc. for all enchantments, in descending numerical order by id}};
}
PRO: Easy to code, easy to read (IF you format it like the example), automatically generates all of the Enchanted Books, easy to see how to alter recipes
CON: Needs to be manually updated if / when Mojang alters the existing enchantment layout in any fashion, such as by removing an enchantment, adding a new level to an enchantment, etc. Must be formatted EXACTLY in the order of enchantments, by effectId, listed in Enchantment.java, and each line must contain a number of recipes equal to the max level the enchantment can reach.
Method III: The Auto-Generator
private static Object[][][] bookRecipeMatrix;
private static ItemStack[][] bookList;
private static int bookListIndex = 0;
private static int BASE_INGREDIENTS = 2; // number of common ingredients (e.g. Item.book, Item.feather)
// Generate all of the Enchanted Books to craft
public RegisterBookRecipes() {
this.bookList = new ItemStack[256][];
for (int i = 0; i < Enchantment.enchantmentsList.length; ++i) {
if (Enchantment.enchantmentsList[i] != null) {
int maxLevel = Enchantment.enchantmentsList[i].getMaxLevel();
this.bookList[this.bookListIndex] = new ItemStack[maxLevel];
for (int j = 0; j < maxLevel; ++j) {
this.bookList[this.bookListIndex][j] = new ItemStack(Item.enchantedBook);
this.bookList[this.bookListIndex][j].addEnchantment(Enchantment.enchantmentsList[i], j + 1);
}
++this.bookListIndex;
}
}
// Generate all of the recipes
this.bookRecipeMatrix = new Object[this.bookListIndex][][];
for (int i = 0; i < this.bookListIndex; ++i) {
int effectId = ((NBTTagCompound)this.bookList[i][0].getEnchantmentTagList().tagAt(0)).getShort("id");
int maxLevel = Enchantment.enchantmentsList[effectId].getMaxLevel();
this.bookRecipeMatrix[i] = new Object[maxLevel][];
for (int j = 0; j < maxLevel; ++j) {
Object[] extraIngredients = new Object[j+1];
if (effectId == Enchantment.flame.effectId) {
for (int k = 0; k < extraIngredients.length; ++k) {
extraIngredients[k] = new ItemStack(Item.magmaCream);
}
this.bookRecipeMatrix[i][j] = extraIngredients;
}
// add an "else if (effectId == X)" for each recipe you want to specify
else {
this.bookRecipeMatrix[i][j] = new Object[extraIngredients.length + this.BASE_INGREDIENTS];
this.bookRecipeMatrix[i][j][0] = new ItemStack(Item.book);
this.bookRecipeMatrix[i][j][1] = new ItemStack(Item.feather);
for (int k = 0; k < extraIngredients.length; ++k) {
extraIngredients[k + this.BASE_INGREDIENTS] = new ItemStack(Item.dyePowder,1,i);
this.bookRecipeMatrix[i][j][k+ this.BASE_INGREDIENTS] = extraIngredients[k];
}
}
}
}
}
Note that within I've implemented 2 slightly different methods of setting the ingredients. Choose whichever suits your needs.
Also note that I've left in a bug which will throw a null pointer exception. These are nasty as they are only caught at run-time. See if you can isolate it with System.out.println rather than using the log. It will improve your understanding of arrays significantly if you do.
PRO: Fun to code, potentially takes fewer lines to code (my implementation is 130 lines), won't break if enchantment ids or levels change. Ideal for recipes that all follow a single crafting template that can be expressed mathematically, such as recipe = common items + (unique item * enchanting level).
CON: Complex, very easy to throw null pointer exceptions when changing things, still need to manually code the ingredients for each recipe, unless you use dye or some similar item as an ingredient. NOT suitable for recipes that don't share a common format - use Method 1 instead.
One solution to that problem would be to create an Object array SpecificIngredient that stores a single unique ingredient for each enchantment id (of which you would need one per level), then use that to auto-generate recipes with the common ingredients. This brings with it the problems of Method 2, though, and if you want to use different ingredients for different levels, it won't work without making a double array, which we already have.
Here's a better solution: create a function that returns the unique recipe item.
private Object uniqueIngredient(int effectId, int i) {
if (effectId == Enchantment.protection.effectId) {
return new ItemStack(Item.plateIron);
}
else if (effectId == Enchantment.flame.effectId) {
return new ItemStack(Item.magmaCream);
}
else {
return new ItemStack(Item.dyePowder,1,i);
}
}
We only need to pass 'int i' if we need to use an item such as dye that has metadata. Instead of passing int i, you can also make a static int in your class that gets iterated whenever it's used in the function. That way none of the dyes are wasted.
Now we can implement the recipe generator like so:
Object uniqueObject = new Object();
this.bookRecipeMatrix[i][j] = new Object[j + this.BASE_INGREDIENTS + 1];
this.bookRecipeMatrix[i][j][0] = new ItemStack(Item.book);
this.bookRecipeMatrix[i][j][1] = new ItemStack(Item.feather);
for (int k = 0; k < (this.bookRecipeMatrix[i][j].length - this.BASE_INGREDIENTS); ++k) {
uniqueObject = uniqueIngredient(effectId, i);
this.bookRecipeMatrix[i][j][k + this.BASE_INGREDIENTS] = uniqueObject;
}
Yeah, that looks better. The only limitation now is that you are restricted to only one kind of special ingredient per recipe, but that's just the kind of situation that is ideal for automatic generation.
Congratulations if you read all of that. I hope you can learn something from it. I wrote this simply as a way for myself to learn about NBT tags, so if you spot anything in error, please let me know and I'll update it. The Enchanted Book section was just for fun, and didn't end up illustrating as much of the NBT structure as I hoped. Oh well. I suppose it could be considered a tutorial for arrays
If you'd like the recipe generator code for your mod, just p.m. me. It's only 130 lines The way I have it set up is you can have up to 4 ingredients in common for all recipes, then a single unique item that you need one of per level of the desired enchantment. As it is, it uses only vanilla blocks and items, but it would be easy to customize with your own mod ingredients. I don't actually use this in my own game, so I haven't bothered to balance the recipes very well.
If you'd like the recipe generator code for your mod, just p.m. me. It's only 130 lines The way I have it set up is you can have up to 4 ingredients in common for all recipes, then a single unique item that you need one of per level of the desired enchantment. As it is, it uses only vanilla blocks and items, but it would be easy to customize with your own mod ingredients. I don't actually use this in my own game, so I haven't bothered to balance the recipes very well.
coolAlias, Could you please tell me how to use more than one object in the crafting grid? I'm trying to use, eg; a slime ball and a custom item that's called by EnchantableItems.enchantability_pearl
I've tried just adding it to the recipe, but it doesn't work.
here is what I tried:
@Criticaldiamonds - that looks like it should work just fine; are your crafting recipes not working at all?
To require more than one of an item, the simplest way is to add it multiple times (each ingredient needs to be placed in a separate crafting slot):
// requires 4 of someItem to craft whatever the outputStack is
GameRegistry.addShapelessRecipe(outputStack, someItem, someItem, someItem, someItem);
Thanks! I had the ordering messed up; I had magicBook at the end of the recipe instead of the beginning. I guess it would've worked if i used magicBook + enchantability_pearl to get a regular book;)
Anyway, thanks for the help! Ill credit you for 'helper' in the mod
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
Little bit late to the party, but is there some way to use an item with NBT attached in the actual recipe part? This only talks about using NBT in the final product of crafting, but for what I need, the recipe parts need to have NBT. (So basically like using 2 protection IV books to craft some item)
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
I'm sorry, I've been googling around, and I am not even sure where to start with this class. All I see is one confusing post by FlameFlare. I have no idea how I am supposed to implement the IRecipe and use it to check if the things in the crafting table have my NBT.
Is there some place that actually explains that or did you just figure out yourself?
I am trying to look through the fireworks class, but I am still having trouble figuring out how it works, since it codes for so many different recipes for the fireworks.
Ok I have no idea what I am doing, I want to make a recipe that needs 3 of the same item but each with a certain NBT to craft something, and once again I have no idea where to start.
[I can just start a new thread on my own if you would like]
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
You literally just make a new class that implements IRecipe:
public class MyRecipe implements IRecipe {
// everything in here will be auto-generated by your IDE and you just fill out what you need
}
There should be a method named #matches that has the crafting matrix inventory as the first parameter - iterate through that inventory and make sure the recipe matches what you want, both in shape and stack contents.
Then once you have your recipe class made, you just register an instance of it:
GameRegistry.addRecipe(new MyRecipe());
You can do fancier things as well, such as pass the various parameters to your recipe constructor so that it can have different inputs and outputs much like the vanilla tool recipes, or you can code one recipe at a time, but that's all just Java, not anything specific to Minecraft.
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
Ok well you confirmed what I was trying to do was in the right ballpark, and I just worked on it and thought I at least had a shapeless recipe working. But it is not doing anything.
Custom Crafting class is here
Custom crafting
public class CustomCrafting implements IRecipe{
@Override
public boolean matches(InventoryCrafting inv, World worldIn) {
ItemStack creeper50kill = new ItemStack(Itemsss.SkeletonSword);
creeper50kill.setTagCompound(new NBTTagCompound());
creeper50kill.getTagCompound().setString("mob", "creeper");
creeper50kill.getTagCompound().setInteger("kills", 50);
ItemStack skeleton50kill = new ItemStack(Itemsss.SkeletonSword);
skeleton50kill.setTagCompound(new NBTTagCompound());
skeleton50kill.getTagCompound().setString("mob", "skeleton");
skeleton50kill.getTagCompound().setInteger("kills", 50);
ItemStack zombie50kill = new ItemStack(Itemsss.SkeletonSword);
zombie50kill.setTagCompound(new NBTTagCompound());
zombie50kill.getTagCompound().setString("mob", "zombie");
zombie50kill.getTagCompound().setInteger("kills", 50);
int zombie = 0;
int skeleton = 0;
int creeper = 0;
for(int i=0; i < inv.getSizeInventory(); ++i)
{
ItemStack itemstack = inv.getStackInSlot(i);
if(itemstack != null)
{
if(itemstack.equals(zombie50kill))
{
++zombie;
}
else if(itemstack.equals(skeleton50kill))
{
++skeleton;
}
else if(itemstack.equals(creeper50kill))
{
++creeper;
}
}
}
if(zombie == 1) //&& skeleton == 1 && creeper == 1
{
return true;
}
else
{
return false;
}
}
@Override
public ItemStack getCraftingResult(InventoryCrafting inv) {
// TODO Auto-generated method stub
return new ItemStack(Itemsss.deathsSythe);
}
@Override
public int getRecipeSize() {
// TODO Auto-generated method stub
return 0;
}
@Override
public ItemStack getRecipeOutput() {
// TODO Auto-generated method stub
return null;
}
@Override
public ItemStack[] getRemainingItems(InventoryCrafting inv) {
// TODO Auto-generated method stub
return null;
}
//methods to implement using class variables
}
And my recipe is made just the way you said in the class that has the rest of my recipes.
It is only testing for the zombie one because I was testing just to see if I could get it to work.
I know the NBT tags of "mob" and "kills" are both the same as the ones that are put on my items in game.
Also, how would I see where it is in the crafting grid to even make a shaped recipe?
Rollback Post to RevisionRollBack
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
The slot index tells you which slot it is in, so in your for loop, i=0 is the top left (or bottom left, but I think it's the top) and each iteration moves you over 1 and then down (or up) to the next row, like a typewriter.
As for checking to see if your recipe works, the easiest way is to put breakpoints with the debugger or use System.out.println in your conditions to see if your logic is actually working as you expect, e.g.:
if (itemstack.equals(zombie50kill)) {
++zombie;
System.out.println("Matched Zombie");
} else if (itemstack.equals(skeleton50kill)) {
++skeleton;
System.out.println("Matched Skeleton");
} else if (itemstack.equals(creeper50kill)) {
++creeper;
System.out.println("Matched Creeper");
} else {
System.out.println("ERROR: No match");
}
You'll probably see tons of "ERROR: No match" printing to your console, reason being that #equals is very likely NOT want you want to use to compare ItemStacks. I don't have the code in front of me (coding computer died), but #equal, ItemStack#areItemStacksEqual, etc. all compare too literally for your use. What I mean by that is if your input item stack has ANY variation from the stack you are using to compare against, the condition will fail. Stack damage value, extra NBT tags such as custom name or enchantments, etc.
Instead, I would just check the stacks as I come to them without making a new ItemStack:
for loop {
stack = getStack(i);
if (stack != null && stack.getItem() == Itemsss.SkeletonSword && stack.getTagCompound() != null) {
NBTTagCompound tag = stack.getTagCompound();
if (tag.getInteger("kills") == 50) { // do you really want it to be EXACTLY 50 kills? or > 49?
switch (tag.getString("mob")) {
case "zombie": ++zombie; break;
case "creeper": ++creeper; break;
case "skeleton": ++skeleton; break;
}
}
}
Something like that. ItemStack comparisons are fraught with gotchas, so you're better off not doing them unless you carefully read and understand fully the methods you are using.
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
First off I would like to note the max kills on the item was 50 just in case there was not an easy way to use it with a higher number, but I can take away the max on that now.
Ok I do not want to butcher this code, so I put it back to where I started off decent and I'm going to explain now.
When I put the 3 in, it shows the death scythe to be crafted. When I put other items in though it does not take away showing the death scythe as a crafting result.
When you try and take it out of the bench, this crash happens
---- Minecraft Crash Report ----
// This doesn't make any sense!
java.lang.NullPointerException: Updating screen events
at net.minecraft.inventory.SlotCrafting.onPickupFromSlot(SlotCrafting.java:138)
at net.minecraft.inventory.Container.slotClick(Container.java:334)
at net.minecraft.client.multiplayer.PlayerControllerMP.windowClick(PlayerControllerMP.java:535)
at net.minecraft.client.gui.inventory.GuiContainer.handleMouseClick(GuiContainer.java:679)
at net.minecraft.client.gui.inventory.GuiContainer.mouseClicked(GuiContainer.java:421)
at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:620)
at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:586)
at net.minecraft.client.Minecraft.runTick(Minecraft.java:1761)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1080)
at net.minecraft.client.Minecraft.run(Minecraft.java:380)
at net.minecraft.client.main.Main.main(Main.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
at GradleStart.main(GradleStart.java:26)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Stacktrace:
at net.minecraft.inventory.SlotCrafting.onPickupFromSlot(SlotCrafting.java:138)
at net.minecraft.inventory.Container.slotClick(Container.java:334)
at net.minecraft.client.multiplayer.PlayerControllerMP.windowClick(PlayerControllerMP.java:535)
at net.minecraft.client.gui.inventory.GuiContainer.handleMouseClick(GuiContainer.java:679)
at net.minecraft.client.gui.inventory.GuiContainer.mouseClicked(GuiContainer.java:421)
at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:620)
at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:586)
-- Affected level --
Details:
Level name: MpServer
All players: 1 total; [EntityPlayerSP['Player299'/19, l='MpServer', x=1377.46, y=6.00, z=52.30]]
Chunk stats: MultiplayerChunkCache: 625, 625
Level seed: 0
Level generator: ID 01 - flat, ver 0. Features enabled: false
Level generator options:
Level spawn location: 1414.00,4.00,79.00 - World: (1414,4,79), Chunk: (at 6,0,15 in 88,4; contains blocks 1408,0,64 to 1423,255,79), Region: (2,0; contains chunks 64,0 to 95,31, blocks 1024,0,0 to 1535,255,511)
Level time: 423580 game time, 393674 day time
Level dimension: 0
Level storage version: 0x00000 - Unknown?
Level weather: Rain time: 0 (now: false), thunder time: 0 (now: false)
Level game mode: Game mode: survival (ID 0). Hardcore: false. Cheats: false
Forced entities: 4 total; [EntitySheep['Sheep'/0, l='MpServer', x=1306.03, y=4.00, z=83.94], EntitySheep['Sheep'/1, l='MpServer', x=1386.06, y=4.00, z=128.09], EntityChicken['Chicken'/3, l='MpServer', x=1426.63, y=4.00, z=-18.66], EntityPlayerSP['Player299'/19, l='MpServer', x=1377.46, y=6.00, z=52.30]]
Retry entities: 0 total; []
Server brand: fml,forge
Server type: Integrated singleplayer server
Stacktrace:
at net.minecraft.client.multiplayer.WorldClient.addWorldInfoToCrashReport(WorldClient.java:383)
at net.minecraft.client.Minecraft.addGraphicsAndWorldToCrashReport(Minecraft.java:2645)
at net.minecraft.client.Minecraft.run(Minecraft.java:401)
at net.minecraft.client.main.Main.main(Main.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
at GradleStart.main(GradleStart.java:26)
-- System Details --
Details:
Minecraft Version: 1.8.9
Operating System: Windows 10 (amd64) version 10.0
Java Version: 1.8.0_71, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 668958600 bytes (637 MB) / 1037959168 bytes (989 MB) up to 1037959168 bytes (989 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
FML: MCP 9.19 Powered by Forge 11.15.0.1718 4 mods loaded, 4 mods active
States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
UCHIJAAAA mcp{9.18} [Minecraft Coder Pack] (minecraft.jar)
UCHIJAAAA FML{8.0.99.99} [Forge Mod Loader] (forgeSrc-1.8.9-11.15.0.1718.jar)
UCHIJAAAA Forge{11.15.0.1718} [Minecraft Forge] (forgeSrc-1.8.9-11.15.0.1718.jar)
UCHIJAAAA kitchensink{0.01} [Kitchen Sink Mod] (bin)
Loaded coremods (and transformers):
GL info: ' Vendor: 'Intel' Version: '4.4.0 - Build 20.19.15.4331' Renderer: 'Intel(R) HD Graphics 5600'
Launched Version: 1.8.9
LWJGL: 2.9.4
OpenGL: Intel(R) HD Graphics 5600 GL version 4.4.0 - Build 20.19.15.4331, Intel
GL Caps: Using GL 1.3 multitexturing.
Using GL 1.3 texture combiners.
Using framebuffer objects because OpenGL 3.0 is supported and separate blending is supported.
Shaders are available because OpenGL 2.1 is supported.
VBOs are available because OpenGL 1.5 is supported.
Using VBOs: No
Is Modded: Definitely; Client brand changed to 'fml,forge'
Type: Client (map_client.txt)
Resource Packs:
Current Language: English (US)
Profiler Position: N/A (disabled)
CPU: 8x Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz
I tried adding if(!worldIn.isRemote) and it made it so when I stuck all 3 in I didn't see a result, but when I picked up what would have been there, put it in my inventory, and picked it up again, it showed a death scythe(Meaning it crafted it serverside and the client just needed to update). But even more of a kicker, it didn't consume any of the items when it crafted it. The system.print.ln would print out multible times that it had added to the zombie or whichever one I put in, but if I put a second one in it would list nothing, since 2 tools can craft together to make 1 item I assume, since that is what it showed.
I can't think of any more information to give, so here is the class code
public class CustomCrafting implements IRecipe{
public int mob = 0;
@Override
public boolean matches(InventoryCrafting inv, World worldIn) {
int zombie = 0;
int skeleton = 0;
int creeper = 0;
for(int i=0; i < inv.getSizeInventory(); ++i)
{
ItemStack itemstack = inv.getStackInSlot(i);
//itemstack = getStack(i);
if (itemstack != null && itemstack.getItem() == Itemsss.SkeletonSword && itemstack.getTagCompound() != null) {
NBTTagCompound tag = itemstack.getTagCompound();
if (tag.getInteger("kills") > 49) { // do you really want it to be EXACTLY 50 kills? or > 49?
if(tag.getString("mob").equals("zombie"))
{
this.mob = 1;
}
else if(tag.getString("mob").equals("creeper"))
{
this.mob = 2;
}
else if(tag.getString("mob").equals("skeleton"))
{
this.mob = 3;
}
switch (mob) {
case 1: ++zombie; System.out.println("ZOMBIE ADDED CLAY"); break;
case 2: ++creeper;System.out.println("CREEPER ADDED CLAY"); break;
case 3: ++skeleton;System.out.println("SKELETON ADDED CLAY"); break;
}
}
}
}
if(zombie == 1 && skeleton == 1 && creeper == 1) //&& skeleton == 1 && creeper == 1
{
return true;
}
else
{
System.out.println("DIDNT WORK CLAY");;
return false;
}
}
@Override
public ItemStack getCraftingResult(InventoryCrafting inv) {
// TODO Auto-generated method stub
return new ItemStack(Itemsss.deathsSythe);
}
@Override
public int getRecipeSize() {
// TODO Auto-generated method stub
return 0;
}
@Override
public ItemStack getRecipeOutput() {
// TODO Auto-generated method stub
return null;
}
@Override
public ItemStack[] getRemainingItems(InventoryCrafting inv) {
// TODO Auto-generated method stub
return null;
}
//methods to implement using class variables
}
Would my errors have something to do with the other methods I just let auto-generate?
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
Thank you, it might have taken me a few days, but with your code as an example I was able to figure it out!
Rollback Post to RevisionRollBack
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
Sorry to bother you again, but how would I find a block? The only methods I see coming off of the inventory to get an item is the one that returns the itemstack, but if it is even possible to convert from itemstack to a block, I have no idea how. Would you happen to know how to detect for blocks in the inventory?
Rollback Post to RevisionRollBack
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
All blocks have corresponding items, so you check if stack.getItem() is the itemblock you want. I believe some blocks have actual items listed in the Items class, but others you may have to get using Block.getItemFromBlock(Blocks.some_block) or something like that. I don't have access to the source right now, so the method names and such may be different.
The Meaning of Life, the Universe, and Everything.
Join Date:
5/28/2014
Posts:
285
Location:
USA
Minecraft:
TheUnderTaker11
Member Details
Thank you, I didn't realize the method worked that way.
Rollback Post to RevisionRollBack
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
ItemStack.addEnchantment takes two arguments, the first being the Enchantment Id, the second the level of Enchantment.
If you still need help, check out my other tutorial on crafting that goes into more detail on the basics.
You can find it here: http://www.minecraft...afting-recipes/
Prerequisites for this tutorial:
You will need to be comfortable with arrays. This also presumes the ability to navigate multiple levels of 'for' loops.
Arrays are not specific to Java, but there are many websites dedicated to teaching Java that can teach you about arrays. Or you can check out my other tutorial (in the spoiler above), in which I give some basic info on how arrays function along with some examples of them in use, conveniently also dealing with automatically generating recipes.
Another useful tip, for those of you who don't know, is System.out.println(""). This guy right here is going to solve innumerable problems, if used correctly. Whenever you don't know what a variable is, what a function returns, etc., just use this to print out the value. Here's what it would look like:
I like to put [YOUR MOD] in there just to make it stand out in the console log.
Be sure to identify the variable whose value you are printing, otherwise it will look like this:
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 1
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 2
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 3
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 4
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 5
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 6
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 7
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 16
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 17
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 18
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 19
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 20
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 21
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 32
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 33
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 34
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 35
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 48
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 49
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 50
2013-07-14 15:35:45 [INFO] [STDOUT] [YOUR MOD] 51
Finally, variable can be anything. Here are some examples I've used while debugging my own code:
This runs through the Enchantment List and outputs valid array indices, as well as the max enchantment level of the enchantment stored at that index in the array.
Try it out for yourself and see how much more readable it is than the first example. Try putting such 'debug' lines in other places in your code. For example, if you're not sure how much damage your new weapon is inflicting, you can use this technique to print the damage in the log whenever that function is called. Very handy.
Random tip:
An essential tool while using Eclipse is the Class Outline in the right-hand sidebar. There you will see all the variables and methods available for the class currently open, and clicking one brings you directly to its code, as well as highlighting all occurrences of it with little rectangles just to the right of the main window, which you can also click.
One last thing: if you're using Eclipse, if you hold ctrl and hover over any method or variable, you have the option to jump to its declaration, implementation or even super implementation. Ctrl-clicking (not hovering) will bring you immediately to the first option in the list. If you A VERY handy trick somebody mentioned in the forums here (sorry don't remember who).
PART I: Using the ItemStack.addEnchantment method to learn about NBT Tags
There is much we can learn from this. We're going to be doing lots of ctrl-clicking here, so I'll annotate it by placing whatever needs to be ctrl-clicked in < >.
ctrl-click <NBTTagCompound> and you'll be brought to the NBTTagCompound class. Scroll through the Outline to see what's available.
Now back to our example. Looking at the first section, we see here that every ItemStack has an NBTTagCompound, and if not, the function creates a new one. If stackTagCompound doesn't already contain an NBTTagList with the name "ench", it creates one.
This is a bit misleading, because it doesn't really create a new NBTTagList, but instead a new NBTBase with a name. <NBTTagList> (remember? ctrl-click) and then <super> - you will see what I mean. This new NBTBase will store the beginning of our new list named "ench".
<setTag>, you'll see that we're storing a new NBTTagList named "ench" in a Map. Sorry, I'm not going to explain Maps here. <NBTBase>. In the NBTBase Outline (you remembered to ctrl-click, right?), click newTag (byte, String). Here's what we see:
Now let's look at the next bit of code:
The first line stores a pointer to our newly created or old list of enchantments. <getTag> and you'll notice it returns type NBTBase, so we have to cast it as NBTTagList. In a slick move of coding slight of hand, NBTBase can also store NBTTagLists, which store NBTBases.
The second line makes our new NBTTagCompound, which is essentially NBTBase with more functionality (which you can see by comparing the two class Outlines). It is able to store "named" tags.
Then, we <setShort> twice, each time creating a new tag with the first argument as it's name, the second argument as the stored data value.
Finally, we append this list of tags onto our list of lists (of tags).
What does all of this mean?
1. NBTBase, aka tags, are being stored in a list, <NBTTagList>. Note that these tags are "nameless." Look at the Outline. Of particular note here are tagCount() and tagAt(int) - these will allow us to run through the list like an array, among other things. If you know any C++, this is roughly the equivalent of a dynamic linked list.
2. NBTTagLists are stored in another list, NBTTagCompound. A list of lists, but NBTTagCompound stores a list of "named" tags. So what setTag() did above is to create a new, empty list named "ench" and store it in the tagMap. We can access this list, and tags in it, by using:
3. If you're paying attention, you'll say, "Wait a second, aren't the NBTTagLists storing lists of NBTTagCompounds?" Well, yes. The neat thing about it is NBTTagLists, NBTTagCompounds and NBTBase can all store and be cast as each other. That makes it extremely confusing to work with, but also very versatile. It can be useful to think of TagLists as storing lists of TagCompounds...
4. BUT, every NBT file must start with an NBTTagCompound, so technically it's more correct to say that NBTTagCompounds store lists of NBTTagLists (and other Tag types).
5. These tags are a low-memory way of storing lots of information, as you only need to store one tag that points to the Map, and from there you can access any of the linked data. Pretty awesome.
For some clearer information on the role of each NBT type, see: http://wiki.vg/NBT
To illustrate some of the values that the NBT functions will return, try making adding an enchantment to an item and printing out different parameters to the screen. Since we're going to be dealing with Enchanted Books in this tutorial, here's what I did:
If you're going to copy, I urge you to manually type it in yourself; as you type, Eclipse will bring up menus that list the functions that are available to you. For instance, say you type:
As soon as you press '.' a list of functions will come up. This is very useful when working with the NBT system, as all 3 are inter-convertible but their available functions are very different.
Notice that they are exactly the same, except for the final string argument in getShort. If your Item or Block has other lists, you can change the getTagList argument from "ench" to whatever you need.
<NBTTagCompound> and look again in the Outline to see the functions available. hasKey(String) is one that will be very useful so you don't throw null pointer exceptions. Add that to your list from earlier (remember when we looked at <NBTTagList> functions?) and you'll be well on your way to not crashing your game. If you're careful.
Whatever the case, the next step in this tutorial is to build some code that automatically generates recipes for all of the Enchanted Books we just made.
PART II: Building an Automatic Enchanted Book Recipe Generator
At this point, unless you want your main mod class to get really messy, I'd encourage you to create a new class called RegisterCraftingRecipes or whatever, then call that from your main class where you normally add recipes. I also made a separate function to register the recipes in that class called addRecipes, so I call mine like this:
At any rate, let's think about this for a minute.
What we want is for an Enchanted Book X, use the following items {list} for crafting. Take a look at vanilla RecipesTools and see if you can figure out what we'll need.
So let's take a look at our RegisterCraftingRecipes class:
Note that bookList[] is of type ItemStack; this is so we can use the addEnchantment method.
We use bookListIndex so we can keep track of where in the bookList array we are, no matter how many enchantments are added (or removed) at a later date.
After we've built our bookList, we can initialize bookRecipeMatrix. Create a new array of arrays of length
bookListIndex. Then we have to run through each element bookRecipeMatrix[j] and assign a new array of Objects to it.
This is where we put in whatever Objects we want Enchanted Books to be crafted from.
In the interest of keeping it simple, we're using a book, a feather, and dye of color 'j' to make the recipes unique. But there are only 16 types of dye, you say, and currently 21 level 1 Enchanted Books. Yep. Values higher than 15 all return 'white dye.' We'll deal with this problem later, but for now, it will suffice.
bookList[] is already an ItemStack, so that's ready to go. But what to do about Object bookRecipeMatrix[][]? It's the right type, but how do we iterate through the second array? Something really cool I learned while putting this together, is you DON'T HAVE TO. That's right. Look at this syntax:
Now isn't that slick? this.bookRecipeMatrix[i] IS { obj1, obj2, obj3...}, so you can just stick that in there as is. Wow. Doesn't get much easier than that, does it? Sure beats my initial implementation method with several functions, embedded if statements and switches.
There is still the problem of there only being 16 kinds of dye, and what if you want to use other ingredients in your recipe based on the enchantment type? All we need is a way to tell what enchantment is on the book when we're building the bookRecipeMatrix - oh wait, remember those NBT tags? Sweet. Let's look again at where we initialize our variables:
What we want to do here is separate each Recipe based on its index 'j.' Recall that bookList[j] will return the
EnchantedBook with a specific enchantment id. It's important to note that the enchantment id value is NOT the same as the value of 'j', but rather is stored in an NBT Tag connected to the ItemStack. While not necessary, for the sake of readability we're going to create a local int effectId and use it to temporarily store the NBT tag value. From there, we can either use a switch or if / else if statements to sort out our list. Here's what it looks like with only one special recipe:
From here, you can easily flesh it out to use whatever Items (or Blocks) you want in your recipes. There's no need to keep the recipes the same length, either, as we saw above in the addShapelessRecipe syntax.
There's only one last thing to do, if you're really dedicated to making ALL of the Enchanted Books available through crafting, and that's to add in every level of enchantment, not just level 1. See if you can figure it out with what you've learned. If you really need help, I'll put some hints here:
We're going to change "private static Object[][] bookRecipeMatrix;" to "private static Object[][][] bookRecipeMatrix;"
I'm assuming you can take it from here. Good luck!
PART III: Comparing 3 different methods of adding all Enchanted Book Recipes
Method I: Type all recipes by hand
Note that the GOOD way only differs in that we use Enchantment.<enchanmentId> instead of writing a number such as '0.' The reason this is better is two-fold: it's more readable and if the id ever changes, our code still works. For example, next update Mojang makes protection id 5 and flame id 0. In the BAD method, our recipe now crafts books of flame, whereas the GOOD example is still correct.
CON: Tedious to write, 3 lines of code per recipe (currently 70 unique enchanted books, so 210 lines of code), not interesting to do.
Method II: Mimic the method RecipesTools uses, also using the method we developed to auto-generate a list of all the Enchanted Books
CON: Needs to be manually updated if / when Mojang alters the existing enchantment layout in any fashion, such as by removing an enchantment, adding a new level to an enchantment, etc. Must be formatted EXACTLY in the order of enchantments, by effectId, listed in Enchantment.java, and each line must contain a number of recipes equal to the max level the enchantment can reach.
Method III: The Auto-Generator
Note that within I've implemented 2 slightly different methods of setting the ingredients. Choose whichever suits your needs.
Also note that I've left in a bug which will throw a null pointer exception. These are nasty as they are only caught at run-time. See if you can isolate it with System.out.println rather than using the log. It will improve your understanding of arrays significantly if you do.
CON: Complex, very easy to throw null pointer exceptions when changing things, still need to manually code the ingredients for each recipe, unless you use dye or some similar item as an ingredient. NOT suitable for recipes that don't share a common format - use Method 1 instead.
One solution to that problem would be to create an Object array SpecificIngredient that stores a single unique ingredient for each enchantment id (of which you would need one per level), then use that to auto-generate recipes with the common ingredients. This brings with it the problems of Method 2, though, and if you want to use different ingredients for different levels, it won't work without making a double array, which we already have.
Here's a better solution: create a function that returns the unique recipe item.
We only need to pass 'int i' if we need to use an item such as dye that has metadata. Instead of passing int i, you can also make a static int in your class that gets iterated whenever it's used in the function. That way none of the dyes are wasted.
Congratulations if you read all of that. I hope you can learn something from it. I wrote this simply as a way for myself to learn about NBT tags, so if you spot anything in error, please let me know and I'll update it. The Enchanted Book section was just for fun, and didn't end up illustrating as much of the NBT structure as I hoped. Oh well. I suppose it could be considered a tutorial for arrays
For a tutorial on using NBT Tags in Blocks, check the wiki here:
http://www.minecraft...BT_Tag_Compound
Be sure to check out my other tutorials - they are all on github here or follow the forum links below:
- Making an EventHandler and Event type explanations: http://www.minecraft...e-explanations/
- Creating an Item that stores an Inventory: http://www.minecraft...s-an-inventory/
- How to properly override shift-clicking: http://www.minecraft...shift-clicking/
- Rendering Your Custom Item Texture: http://www.minecraft...m-item-texture/
- Using Potions in Crafting Recipes: http://www.minecraft...afting-recipes/
- See Mazetar's List for the best Minecraft tutorials from around the web!
Lastly, don't forget to up the green arrow on any posts that help you!
coolAlias, Could you please tell me how to use more than one object in the crafting grid? I'm trying to use, eg; a slime ball and a custom item that's called by EnchantableItems.enchantability_pearl
I've tried just adding it to the recipe, but it doesn't work.
here is what I tried:
How would I make this work?
Thanks
«~» My Youtube Channel
To require more than one of an item, the simplest way is to add it multiple times (each ingredient needs to be placed in a separate crafting slot):
Thanks! I had the ordering messed up; I had magicBook at the end of the recipe instead of the beginning. I guess it would've worked if i used magicBook + enchantability_pearl to get a regular book;)
Anyway, thanks for the help! Ill credit you for 'helper' in the mod
«~» My Youtube Channel
Update: the crafting works perfectly, thanks! However, I can't use it in an Anvil to apply it to armor and tools. Is there a reason for this?
Thanks, Criticaldiamonds
«~» My Youtube Channel
Yes.... Make it work.... Good.
Little bit late to the party, but is there some way to use an item with NBT attached in the actual recipe part? This only talks about using NBT in the final product of crafting, but for what I need, the recipe parts need to have NBT. (So basically like using 2 protection IV books to craft some item)
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
Create a class implementing IRecipe and you can require specific NBT tags as part of the recipe.
I'm sorry, I've been googling around, and I am not even sure where to start with this class. All I see is one confusing post by FlameFlare. I have no idea how I am supposed to implement the IRecipe and use it to check if the things in the crafting table have my NBT.
Is there some place that actually explains that or did you just figure out yourself?
I am trying to look through the fireworks class, but I am still having trouble figuring out how it works, since it codes for so many different recipes for the fireworks.
Ok I have no idea what I am doing, I want to make a recipe that needs 3 of the same item but each with a certain NBT to craft something, and once again I have no idea where to start.
[I can just start a new thread on my own if you would like]
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
Then once you have your recipe class made, you just register an instance of it: You can do fancier things as well, such as pass the various parameters to your recipe constructor so that it can have different inputs and outputs much like the vanilla tool recipes, or you can code one recipe at a time, but that's all just Java, not anything specific to Minecraft.
Ok well you confirmed what I was trying to do was in the right ballpark, and I just worked on it and thought I at least had a shapeless recipe working. But it is not doing anything.
Custom Crafting class is here
Custom crafting
And my recipe is made just the way you said in the class that has the rest of my recipes.
It is only testing for the zombie one because I was testing just to see if I could get it to work.
I know the NBT tags of "mob" and "kills" are both the same as the ones that are put on my items in game.
Also, how would I see where it is in the crafting grid to even make a shaped recipe?
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
As for checking to see if your recipe works, the easiest way is to put breakpoints with the debugger or use System.out.println in your conditions to see if your logic is actually working as you expect, e.g.: You'll probably see tons of "ERROR: No match" printing to your console, reason being that #equals is very likely NOT want you want to use to compare ItemStacks. I don't have the code in front of me (coding computer died), but #equal, ItemStack#areItemStacksEqual, etc. all compare too literally for your use. What I mean by that is if your input item stack has ANY variation from the stack you are using to compare against, the condition will fail. Stack damage value, extra NBT tags such as custom name or enchantments, etc.
Instead, I would just check the stacks as I come to them without making a new ItemStack: Something like that. ItemStack comparisons are fraught with gotchas, so you're better off not doing them unless you carefully read and understand fully the methods you are using.
First off I would like to note the max kills on the item was 50 just in case there was not an easy way to use it with a higher number, but I can take away the max on that now.
Ok I do not want to butcher this code, so I put it back to where I started off decent and I'm going to explain now.
When I put the 3 in, it shows the death scythe to be crafted. When I put other items in though it does not take away showing the death scythe as a crafting result.
When you try and take it out of the bench, this crash happens
---- Minecraft Crash Report ----
// This doesn't make any sense!
Time: 3/7/16 8:55 PM
Description: Updating screen events
java.lang.NullPointerException: Updating screen events
at net.minecraft.inventory.SlotCrafting.onPickupFromSlot(SlotCrafting.java:138)
at net.minecraft.inventory.Container.slotClick(Container.java:334)
at net.minecraft.client.multiplayer.PlayerControllerMP.windowClick(PlayerControllerMP.java:535)
at net.minecraft.client.gui.inventory.GuiContainer.handleMouseClick(GuiContainer.java:679)
at net.minecraft.client.gui.inventory.GuiContainer.mouseClicked(GuiContainer.java:421)
at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:620)
at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:586)
at net.minecraft.client.Minecraft.runTick(Minecraft.java:1761)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1080)
at net.minecraft.client.Minecraft.run(Minecraft.java:380)
at net.minecraft.client.main.Main.main(Main.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
at GradleStart.main(GradleStart.java:26)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Stacktrace:
at net.minecraft.inventory.SlotCrafting.onPickupFromSlot(SlotCrafting.java:138)
at net.minecraft.inventory.Container.slotClick(Container.java:334)
at net.minecraft.client.multiplayer.PlayerControllerMP.windowClick(PlayerControllerMP.java:535)
at net.minecraft.client.gui.inventory.GuiContainer.handleMouseClick(GuiContainer.java:679)
at net.minecraft.client.gui.inventory.GuiContainer.mouseClicked(GuiContainer.java:421)
at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:620)
at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:586)
-- Affected screen --
Details:
Screen name: net.minecraft.client.gui.inventory.GuiCrafting
-- Affected level --
Details:
Level name: MpServer
All players: 1 total; [EntityPlayerSP['Player299'/19, l='MpServer', x=1377.46, y=6.00, z=52.30]]
Chunk stats: MultiplayerChunkCache: 625, 625
Level seed: 0
Level generator: ID 01 - flat, ver 0. Features enabled: false
Level generator options:
Level spawn location: 1414.00,4.00,79.00 - World: (1414,4,79), Chunk: (at 6,0,15 in 88,4; contains blocks 1408,0,64 to 1423,255,79), Region: (2,0; contains chunks 64,0 to 95,31, blocks 1024,0,0 to 1535,255,511)
Level time: 423580 game time, 393674 day time
Level dimension: 0
Level storage version: 0x00000 - Unknown?
Level weather: Rain time: 0 (now: false), thunder time: 0 (now: false)
Level game mode: Game mode: survival (ID 0). Hardcore: false. Cheats: false
Forced entities: 4 total; [EntitySheep['Sheep'/0, l='MpServer', x=1306.03, y=4.00, z=83.94], EntitySheep['Sheep'/1, l='MpServer', x=1386.06, y=4.00, z=128.09], EntityChicken['Chicken'/3, l='MpServer', x=1426.63, y=4.00, z=-18.66], EntityPlayerSP['Player299'/19, l='MpServer', x=1377.46, y=6.00, z=52.30]]
Retry entities: 0 total; []
Server brand: fml,forge
Server type: Integrated singleplayer server
Stacktrace:
at net.minecraft.client.multiplayer.WorldClient.addWorldInfoToCrashReport(WorldClient.java:383)
at net.minecraft.client.Minecraft.addGraphicsAndWorldToCrashReport(Minecraft.java:2645)
at net.minecraft.client.Minecraft.run(Minecraft.java:401)
at net.minecraft.client.main.Main.main(Main.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
at GradleStart.main(GradleStart.java:26)
-- System Details --
Details:
Minecraft Version: 1.8.9
Operating System: Windows 10 (amd64) version 10.0
Java Version: 1.8.0_71, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 668958600 bytes (637 MB) / 1037959168 bytes (989 MB) up to 1037959168 bytes (989 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
FML: MCP 9.19 Powered by Forge 11.15.0.1718 4 mods loaded, 4 mods active
States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
UCHIJAAAA mcp{9.18} [Minecraft Coder Pack] (minecraft.jar)
UCHIJAAAA FML{8.0.99.99} [Forge Mod Loader] (forgeSrc-1.8.9-11.15.0.1718.jar)
UCHIJAAAA Forge{11.15.0.1718} [Minecraft Forge] (forgeSrc-1.8.9-11.15.0.1718.jar)
UCHIJAAAA kitchensink{0.01} [Kitchen Sink Mod] (bin)
Loaded coremods (and transformers):
GL info: ' Vendor: 'Intel' Version: '4.4.0 - Build 20.19.15.4331' Renderer: 'Intel(R) HD Graphics 5600'
Launched Version: 1.8.9
LWJGL: 2.9.4
OpenGL: Intel(R) HD Graphics 5600 GL version 4.4.0 - Build 20.19.15.4331, Intel
GL Caps: Using GL 1.3 multitexturing.
Using GL 1.3 texture combiners.
Using framebuffer objects because OpenGL 3.0 is supported and separate blending is supported.
Shaders are available because OpenGL 2.1 is supported.
VBOs are available because OpenGL 1.5 is supported.
Using VBOs: No
Is Modded: Definitely; Client brand changed to 'fml,forge'
Type: Client (map_client.txt)
Resource Packs:
Current Language: English (US)
Profiler Position: N/A (disabled)
CPU: 8x Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz
I tried adding if(!worldIn.isRemote) and it made it so when I stuck all 3 in I didn't see a result, but when I picked up what would have been there, put it in my inventory, and picked it up again, it showed a death scythe(Meaning it crafted it serverside and the client just needed to update). But even more of a kicker, it didn't consume any of the items when it crafted it. The system.print.ln would print out multible times that it had added to the zombie or whichever one I put in, but if I put a second one in it would list nothing, since 2 tools can craft together to make 1 item I assume, since that is what it showed.
I can't think of any more information to give, so here is the class code
Would my errors have something to do with the other methods I just let auto-generate?
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
Here is an example from my own mod.
Thank you, it might have taken me a few days, but with your code as an example I was able to figure it out!
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
Nice - that's always a good feeling
Sorry to bother you again, but how would I find a block? The only methods I see coming off of the inventory to get an item is the one that returns the itemstack, but if it is even possible to convert from itemstack to a block, I have no idea how. Would you happen to know how to detect for blocks in the inventory?
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!
All blocks have corresponding items, so you check if stack.getItem() is the itemblock you want. I believe some blocks have actual items listed in the Items class, but others you may have to get using Block.getItemFromBlock(Blocks.some_block) or something like that. I don't have access to the source right now, so the method names and such may be different.
Thank you, I didn't realize the method worked that way.
Want a mod with the best magnets available? The download Better Magnets!
And if you like Avaritia then you will love More Avaritia! Adds recipes for creative items from a huge list of mods, and even adds more custom items that fit the Avaritia theme!