Posted October 28, 201410 yr Ok first question I have today is this. Basically I have a Block with subblocks, you know, with metadata. And everything seems fine, except for some reason the unlocalized name of them don't seem to be registering, the textures seem to work fine aswell. I also find that the localized name is always tile.[Nameofblockgroup].name. I am using an enum file containing block names and certain variables to simplify for future plans btw. Now, my second question may be a bit of a long shot of a question.... Is it at all possible to create subblocks of crops, each with their own texture, drops, max growth, etc. Based on what I have done, it seems possible to do, but I am unsure. Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
October 28, 201410 yr For the unlocalized name: you need a custom ItemBlock. Have a look at this. For the crops, I think you can, but keep in mind that a block can only have 16 variants. And some of that might be easier in 1.8 with BlockStates. Check out my mod, Realms of Chaos, here. If I helped you, be sure to press the "Thank You" button!
October 29, 201410 yr You don't need a custom item block, there's an overloaded version of getUnlocalizedName that has an integer (metadata) Just override that method. BEFORE ASKING FOR HELP READ THE EAQ! I'll help if I can. Apologies if I do something obviously stupid. If you don't know basic Java yet, go and follow these tutorials.
October 29, 201410 yr It is for items but the method should be the same ^^ https://bitbucket.org/Dragonisser/cobaltmod/src/178c28c1f444b11466696e40bcbfb0f8978acf76/cobaltmod/items/ItemScroll.java?at=master @Override public String getUnlocalizedName(ItemStack is) { int i = MathHelper.clamp_int(is.getItemDamage(), 0, 1); return super.getUnlocalizedName() + "_" + itemMetaNames[i]; }
December 11, 201410 yr Author Ok, now that I have got this settled for now. I have a new question, is it possible to make an item that can look for item/s that are on top of a block it right clicks on, make those item/s instantly despawn, and replace the detected item/s with another item/s? For example, if I drop 3 redstone, and 1 stick on any block and right click it with my item it will "consume" the items on that block and create the item I set it to. I'd assume this is possible, but not completely sure. Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 11, 201410 yr Such a thing is perfectly possible! Here are your ingredients: Anywhere you are given a World as a parameter, you can call getEntitesWithinAABB(Class passedClass, AxisAlignedBB passedAABB), which will return an ArrayList of entities of the passed class type within the passed bounding box. Dropped items are objects of type EntityItem; and EntityItems have a method called getEntityItem which returns the ItemStack they "contain".
December 13, 201410 yr Author So, how would I utilize getEntitesWithinAABB within the onItemUse method for what I want it to do? Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 25, 201410 yr Author Ok, so I have tried to use the getEntitiesWithinAABB within the onItemUse method, and I can't seem to get it to work Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 25, 201410 yr Author To be specific, I can't seem to get my item to check above of the block it is used on for 4 redstone item, 2 glowstone dusts, and 1 stick. Aswell as remove only those "amount" items from the world and spawn a certain item in their place. Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 25, 201410 yr Hi Since I'm a newbie, forgive if I don't know everything correctly. What I'v learned so far, you need to do something like the following: 1. Get the EntityItem within a certain radius (MrCaracal gave you the ingredients) 2. Iterate through the result to check if a) the item's are correct b) the amount of each item is correct. I mostly learn forge through examples, so here is one: @Override public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ){ List<EntityItem> list = world.getEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getBoundingBox(x-1,y-1,z-1,x+2,y+2,z+2)); //BoundingBox: A box from x-1,y-1,z-1 to x+2,y+2,z+2 //EntityItem.class since you are checking for items. if(list != null){ //now we now that at least the list is not null Iterator it = list.iterator(); while(it.hasNext() ){ EntityItem ei = (EntityItem) it.next(); //check for your conditions } } } I hope that was helpful Sincerely -pick PS: It really IS helpful to post the 'not working' code... Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
December 26, 201410 yr Author Thanks to you both, I have made good progress, except when I put a stack of the item it searches for down it only drops the number of set item when there is only one.... Also I would like to make it do different things when it detect different numbers. For instance when atleast 5 redstone, 2 glowstone, and 1 stick are detected it will only remove those set numbers of items from the area, and spawn the correct itemstack. Here is my current onItemUse method: @Override public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7, float par8, float par9, float par10) { List<EntityItem> list = world.getEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getBoundingBox(x-1, y-1, z-1, x+2, y+2, z+2)); if(!world.isRemote) { if(list != null){ Iterator it = list.iterator(); while(it.hasNext()){ EntityItem ai = (EntityItem) it.next(); if(ai.getEntityItem().getItem() == Items.apple){ ItemStack itemStack = new ItemStack(Items.arrow,1,0); world.removeEntity(ai); world.spawnEntityInWorld(new EntityItem(world, x, y+1, z, itemStack)); } } } } return false; } Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 26, 201410 yr G'Day Thanks to you both, I have made good progress, except when I put a stack of the item it searches for down it only drops the number of set item when there is only one.... Also I would like to make it do different things when it detect different numbers. For instance when atleast 5 redstone, 2 glowstone, and 1 stick are detected it will only remove those set numbers of items from the area, and spawn the correct itemstack. Wow, that's a weird sentence. Let's check if I got you right: - You want to remove the 'consumed' amount from a ItemStack - You'd like to craft the result several times, if enough items are provided? If that is _not_ your target, I suggest to rewrite your question. Otherwise: Your code is a kind of strict and mercyless, isn't it? You check for the desired item and if it is, you just remove the complete EntityItem, regardless to stackSize or anything else. If I had to do something like your target, I'd create my own recipes class and code a manager for those recipes. This manager may receives an ArrayList of EntityItems and all the magic would happen there. It would be responsible for only taking the desired amount of items, the spawning of the new one etc The point you missed in your previous code example is, that you need to check the _stackSize_ of the ItemStack. Something like: //ei is an object of EntityItem if(ei.getEntityItem().getItem() == Items.diamond){ int neededAmount = 9; if(ei.getEntityItem().stackSize <= neededAmount){ //can craft once the result //craft the Item, spawn it //and then set the new stackSize: ei.getEntityItem().stackSize -= neededAmount; //since I looked into EntityItem, I assume it dies if stackSize <= 0. return;//we're done here } if(ei.getEntityItem().stackSize > neededAmount){ int toCraft = ei.getEntityItem().stackSize / neededAmount; int rest = ei.getEntityItem().stackSize - (toCraft * neededAmount); //toCraft = calculate how many times you can craft your item //rest = calculate the rest (=new size) of the itemstack //now spawn your result as usual ei.getEntityItem().stackSize = rest; //see above return; } if(ei.getEntityItem().stackSize < neededAmount){ //we cannot craft return; } } The above code would go into said manager or into the iteration through the list of EntityItems. I haven't covered yet that you also want to check several items. I would have for each amount / item tuple a boolean flag. Instead of crafting the result during the iteration, I'd then check _after_ the iteration if all the flags are true. I hope this reply is helpful (sorry, it's kinda long) Sincerely -pick Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
December 26, 201410 yr Author From what I can see, you got what I meant with my question. If this does want I need, I may try to get a recipe class made for this, which to be honest, I haven't done that before Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 26, 201410 yr Author Ok, so it does work Now few questions now. 1)How do I code a recipemanager for this? 2)How would I code it for multiple "ingredients"? Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 27, 201410 yr Before you initialize your EntityItem variable, you should probably check if the entity on top of the block IS an Item. If it's not your game will crash. As for the recipe manager, look into the CraftingManager class. Maybe you could use ArrayLists and a getter or something. I'm not sure exactly, but I'd have to look. Creator of the MyFit, MagiCraft, Tesseract gun, and Papa's Wingeria mod.
December 27, 201410 yr Author Before you initialize your EntityItem variable, you should probably check if the entity on top of the block IS an Item. If it's not your game will crash. Ok, I have tested and it doesn't seem to crash with any other entity, unless I'm missing one. And hasn't crashed yet.... Currently trying to build and implement the recipe manager for this type of function, hopefully with the possibilities for extra "ingredients" to search for. Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 27, 201410 yr Author Ok, so I honestly haven't been making any leeway on even starting a custom manager for this concept Can someone point me in the proper direction, aside from just copying and pasting the CraftingManager class and editing it? Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 27, 201410 yr Dear Electrobob99 I'd like to help and I am still a newbie in forge, but I do code in java for a longer time now. The said 'CraftingManager' that I mentioned previously is what you have to come up with. From my point of view, the vanilla craftingmanager doesn't fit to the requirements. More than this, I think this is the part where your creativity gets involved: Having an idea and convert it to actual code is not teachable. I'd suggest to write down (on good old paper) what you need and then construct your classes. May there is already something out there, that fits in what you are looking for. To still give some reply on your last question: Firstly you need an abstraction of such an recipe: Store the tuple ingredient/amount/result in it and may some other information. As a next step, I'd suggest to code some sort of CraftingManager that is capable of holding such recipes. It should also have some power, meaning that it's able to sort these recipes (Comperable recipes or a Comperator). Since this is not my project, I don't know wheter I covered all the points... May this post was helpful, but I'd really recommed in tinkering around or get another person on board. Sincerely -pick Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
December 29, 201410 yr Author I really don't understand what you are saying to be honest. From what I understand, I should write it in pseudo-code on good old paper and then code it. I've done that. But I don't know how to code it... I know my handler needs to be able to take in the entity items that are around the item's rightclicked area, take those items and compare them with needed items within stock recipes, then if they match check if all needed items for one creation are there, and if they are create said items.... And that's basically what I had written down for weeks.... But question is how do I code it? I don't understand Pseudo-code at all Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 29, 201410 yr Hello again Well, what I've tried to say is, that you need your creativity to answer this question. Or another developer. It's slightly difficult to explain this process of 'having an idea and get into code'. I personally absolutely don't like pseudo-code, I'd rather work with some sequenz diagrams or mostly better with a mix of UML class diagrams, 'ability list' and some sort of crazy sketches of the object's behaviour that no-one but me can read. I cannot tell you more about this than I've done yet. Learn more Java or break down your goal into smaller pieces and achieve them step by step. I don't support copy-paste replies on questions, but this time I've made an exception, I guess. Since I replied a lot in this thread I made some further thoughts on the given problem. In the next spoilers I will post some code that I made but please keep in mind: - This code is anything but near completion - It's made quickly and not tested into depth - Not able of the desired 'craft as many times as possible' - Very basic - Do whatever you want with it May it helps you. I haven't commented much, so feel free to ask if something is not 'self-explaining'. First the recipe class public class InWorldRecipe implements Comparable<InWorldRecipe>{ private final Vector<ItemStack> ings; private final ItemStack result; /** * The ingredients itemStacks contain the desired amount of the ingredient! * Example: * new InWorldRecipe(ItemStack(Blocks.diamond_block), new ItemStack(Items.diamond, 9) ); * would be the recipe to get 1 dia block out of 9 dias. */ public InWorldRecipe(ItemStack result, ItemStack...ingredients){ ings = new Vector<ItemStack>(5);//5 or whatever average of items used. this.result = result; for(ItemStack is : ingredients){ ings.add(is); } } /** * Tests whether the given {@link ItemStack} test is part of this recipe. * The test lies only on equality of the items and item-damage. * ESPECIALLY it IGNORES nbt data of the itemstacks!!! */ public boolean containsIngredient(ItemStack test){ Iterator<ItemStack> it = ings.iterator(); while(it.hasNext()){ ItemStack is = it.next(); if(compareItemStacksLight(is, test) ){ return true; } } return false; } public ItemStack getResult(){ return result; } /** * Returns the demanded amount for a given itemstack. * The given itemstack is a reference for the tuple item/itemDamage and ignores NBT * @return the demanded amount for the given ingredient item */ public int demandedAmount(ItemStack item){ //go through ingredients and see if item and damage are equal Iterator<ItemStack> it = ings.iterator(); while(it.hasNext()){ ItemStack is = it.next(); if(compareItemStacksLight(is, item)){ return is.stackSize; } } return 0; } private boolean compareItemStacksLight(ItemStack s1, ItemStack s2){ return s1.getItem().equals(s2.getItem()) && s1.getItemDamage() == s2.getItemDamage(); } public Vector<ItemStack> getIngredients(){ return ings; } public int getRecipeSize(){ return ings.size(); } @Override public int compareTo(InWorldRecipe o) { //may a better compareTo ? if( this.equals(o)){ return 0; }else if(this.getRecipeSize() < o.getRecipeSize() ){ //so return <0 if this recipe's size is less than the testing one return -1; }else{ //vice versa return 1; } } @Override public int hashCode() { //eclipse generated final int prime = 31; int result = 1; result = prime * result + ((ings == null) ? 0 : ings.hashCode()); result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); return result; } @Override public boolean equals(Object obj) { //eclipse generated if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; InWorldRecipe other = (InWorldRecipe) obj; if (ings == null) { if (other.ings != null) return false; } else if (!ings.equals(other.ings)) return false; if (result == null) { if (other.result != null) return false; } else if (!result.equals(other.result)) return false; return true; } } Secondly the manager class: public class InWorldCraftingManager { private Vector<InWorldRecipe> recipes; private InWorldCraftingManager(){ recipes = new Vector<InWorldRecipe>(10);//avarage of such recipes } public static InWorldCraftingManager object = new InWorldCraftingManager(); public void addRecipe(InWorldRecipe recipe){ recipes.add(recipe); Collections.sort(recipes); } /** * @return NULL if no recipe was found */ public InWorldRecipe findMatching(List<ItemStack> items){ for(InWorldRecipe recipe : recipes){ if(recipe.getRecipeSize() != items.size() ){ continue; //the sizes do not match: next recipe please! } int nbMatchingItems = 0; for(ItemStack is : items){ if(recipe.containsIngredient(is)){ //okay, is is part of the recipe nbMatchingItems++; } } if(nbMatchingItems == recipe.getRecipeSize() ){ //yeah found recipe (but did not check correct amount!) return recipe; }else{ //not enough matching items return null; } } return null; } public void craftInWorld(World world, int x, int y, int z, List<ItemStack> items){ } private void spawnItems(World world, int x, int y, int z, List<ItemStack> items){ if(world.isRemote){ return; } for(ItemStack is : items){ if(is == null){ System.out.println("the is was null"); continue;//avoid spawining nulls in world } EntityItem ei = new EntityItem(world, x, y+1, z, is); world.spawnEntityInWorld(ei); } } public List<ItemStack> craft(List<ItemStack> items){ InWorldRecipe recipe = findMatching(items); int nbResults = 0; if(recipe != null){ for(ItemStack is : items){ int demanded = recipe.demandedAmount(is); if(demanded < is.stackSize){ //does not have the correct amount return null; } int stackFactor = is.stackSize / demanded; //integer division always rounded to next smaller integer //following code looks that the smallest factor will be used later. if(nbResults == 0){ nbResults = stackFactor; }else{ nbResults = Math.min(nbResults, stackFactor); } } } if(nbResults == 0){ //recipe was null return null; }else{ //make as many results as possible List<ItemStack> out = new ArrayList<ItemStack>(nbResults); for(int i=0; i<nbResults; i++){ out.add(recipe.getResult()); } return out; } } } And lastly an example-testing item: public class InWorldCrafter extends Item { private InWorldCraftingManager manager = InWorldCraftingManager.object; public InWorldCrafter() { manager.addRecipe(new InWorldRecipe(new ItemStack(Blocks.diamond_block), new ItemStack(Items.diamond, 9))); setCreativeTab(CreativeTabs.tabTools); setMaxStackSize(1); setFull3D(); setNoRepair(); setUnlocalizedName("crafter"); setTextureName("stick"); } /** * Callback for item usage. If the item does something special on right * clicking, he will have one of those. Return True if something happen and * false if it don't. This is for ITEMS, not BLOCKS */ @Override public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) { if (!world.isRemote) { List<EntityItem> list = world.getEntitiesWithinAABB( EntityItem.class, AxisAlignedBB.getBoundingBox(x - 1, y - 1, z - 1, x + 2, y + 2, z + 2));// max must be // +1 of min if (list == null) { System.out.println("no items found"); return false; } List<ItemStack> stacks = new ArrayList<ItemStack>(list.size()); Iterator it = list.iterator(); while (it.hasNext()) { EntityItem ei = (EntityItem) it.next(); if(ei.getEntityItem() != null){ stacks.add(ei.getEntityItem()); } } //now stacks has itemStacks withing given bounds manager.craftInWorld(world, x, y, z, stacks); } return true; } } May you got me this time... Sincerely -pick Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
December 30, 201410 yr Author Ok this makes sense now... But one question remains. Do I have to have the crafting manager initialized somewhere? I ask this because no matter what I adjust it doesn't seem to do anything with rightclick, doesn't even get to System.out.println("no items found"); when no items are in the list. Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 30, 201410 yr Author Ok, found out a possible reason why it's not working... It seems that the method: craftInWorld is empty inside of the manager class. Guess I will just have to think and put the proper info inside that method to get the full file to do it's job Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 30, 201410 yr Author From what I can see the craftInWorld method needs to send the list of itemstacks it receives from the item used to the craft method, etc. Currently it works for a short time, but crashes when no items are around the item's right click. The following is what I currently have in the manager, where I used print statements for debugging: public class InWorldCraftingManager { private Vector<InWorldRecipe> recipes; private InWorldCraftingManager(){ recipes = new Vector<>(10); //average of such recipes } public static InWorldCraftingManager object = new InWorldCraftingManager(); public void addRecipe(InWorldRecipe recipe){ recipes.add(recipe); Collections.sort(recipes); } /** * Returns NULL if no recipe was found */ public InWorldRecipe findMatching(List<ItemStack> items){ for(InWorldRecipe recipe : recipes){ if(recipe.getRecipeSize() != items.size()){ System.out.println("SIZES DON'T MATCH!!"); continue; //the sizes don't match: next recipe please } int MatchingItems = 0; for(ItemStack is : items){ if(recipe.containsIngredient(is)){ System.out.println("ING CONFIRMED");//okay, is it part of the recipe MatchingItems++; } } if(MatchingItems == recipe.getRecipeSize()){ //Yeah found recipe, but didn't check correct amounts! return recipe; } else{ System.out.println("NOT ENOUGH INGRS!!");//not enough matching items return null; } } return null; } public void craftInWorld(World world, int x, int y, int z, List<ItemStack> items){ List<ItemStack> test = craft(items); spawnItems(world, x,y,z, test); } private void spawnItems(World world, int x, int y, int z, List<ItemStack> items){ if(world.isRemote){ return; } for(ItemStack is : items){ if(is == null){ System.out.println("the is was null!!"); continue; //avoid spawning nulls in world } EntityItem ei = new EntityItem(world, x, y+1, z, is); world.spawnEntityInWorld(ei); } } public List<ItemStack> craft(List<ItemStack> items){ InWorldRecipe recipe = findMatching(items); int Results = 0; if(recipe != null){ for(ItemStack is : items){ int demanded = recipe.demandedAmount(is); if(demanded < is.stackSize){ System.out.println("Not correct amount!!"); //doesn't have correct amount return null; } int stackFactor = is.stackSize / demanded; //integer division rounds to next smaller integer //following code looks that the smaller factor will be used later. if(Results == 0){ Results = stackFactor; } else{ Results = Math.min(Results, stackFactor); } } } if(Results == 0){ System.out.println("Recipe was null!!"); //recipe was null return null; } else{ //make as many results as possible List<ItemStack> out = new ArrayList<ItemStack>(Results); for(int i = 0; i < Results; i++){ out.add(recipe.getResult()); System.out.println("RESULTS!!"); } return out; } } } Can someone tell me what I may be missing? Member of Aerotech Networks, a Multi-Gaming server. Also Currently porting the "Rise of the Automatons" minecraft mod
December 31, 201410 yr Hi Well, the point is that you don't check if there ARE items or not. It's written so, that if no recipe was found (which is the case if no ingredients are available), the recipe is null. If you try to craft the result of null, it will throw a NullPointerException. Be patient, I think I can come up with another solution shortly. Sincerely -pick Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
December 31, 201410 yr Dear Electrobob99 I spend another few moments on your problem and since since other modders could be interested as well, I'd like to share my code with you. Although I versioned the files with version 1.0, I guess these mechanique could be improved alot. Read the javadoc breifly and you should understand what I've done. Please keep in mind, that I wrote this quickly and did only made the following tests: I used the _InWorldCrafter_ item to test the manager and recipes, in this item I also added the recipes - that's not how to use it in a real environment! The given recipes are processed correctly and beyond that I don't know (as often in programming: 'it should work...'). I'm absolutely not keen on fame but if any of you take these lines, please leave a notice from where this code comes from. The recipe class: package org.bitbucket.pickaxe_engineer.api.worldcrafting; import java.util.Iterator; import java.util.Vector; import net.minecraft.item.ItemStack; /** * A custom class to represent recipes. This class is capable of storing a * result (in form of a {@link ItemStack}) and a list of ingredients (in form of * a {@link Vector} of {@link ItemStack}s). * * It also has the ability to check if a given {@link ItemStack} is part of this * recipe and how much of given {@link ItemStack} is demanded by this recipe. * * <p> * <b>Warning</b> This class was written quickly and did not went through JUnit * tests. Its also not very completed, since NBTTags do matter and this class * just ignores them. The original idea comes from the minecraft forge forums: * http://www.minecraftforge.net/forum/index.php/topic,24744.0.html * </p> * * @author pickaxe_engineer * @version 1.0 */ public class InWorldRecipe implements Comparable<InWorldRecipe> { private final Vector<ItemStack> ings; private final ItemStack result; /** * The ingredients itemStacks contain the desired amount of the ingredient! * Example: new InWorldRecipe(ItemStack(Blocks.diamond_block), new * ItemStack(Items.diamond, 9) ); would be the recipe to get 1 dia block out * of 9 dias. * * @param result * The {@link ItemStack} that represents the result * @param ingredients * A vararg list of {@link ItemStack}s that are ingredients for * this recipe. */ public InWorldRecipe(ItemStack result, ItemStack... ingredients) { ings = new Vector<ItemStack>(5);// 5 or whatever average of items used. this.result = result; for (ItemStack is : ingredients) { ings.add(is); } } /** * Compares two given {@link ItemStack} light. Light indicates that its not * a full comparison: This method returns <code>true</code> for two * itemstacks that have the same item and do have the same itemDamage. * * @param s1 * The first {@link ItemStack} to test with s2 * @param s2 * The second {@link ItemStack} to test with s1 * @return <code>true</code> if both items of the {@link ItemStack}s are * equal and when the item damages are equal. */ private boolean compareItemStacksLight(ItemStack s1, ItemStack s2) { return s1.getItem().equals(s2.getItem()) && s1.getItemDamage() == s2.getItemDamage(); } /** * {@inheritDoc} */ @Override public int compareTo(InWorldRecipe o) { // may a better compareTo ? if (this.equals(o)) { return 0; } else if (this.getRecipeSize() < o.getRecipeSize()) { // so return <0 if this recipe's size is less than the testing one return -1; } else { // vice versa return 1; } } /** * Tests whether the given {@link ItemStack} test is part of this recipe. * <b>The test lies only on equality of the items and item-damage.</b> * ESPECIALLY it IGNORES nbt data of the itemstacks!!! * * @param test * The {@link ItemStack} to test whether is part of this recipe. * @return <code>true</code> if the given {@link ItemStack} is part of this * recipe - otherwise <code>false</code>. */ public boolean containsIngredient(ItemStack test) { Iterator<ItemStack> it = ings.iterator(); while (it.hasNext()) { ItemStack is = it.next(); if (compareItemStacksLight(is, test)) { return true; } } return false; } /** * Returns the demanded amount for a given itemstack. <b>The given itemstack * is a reference for the tuple item/itemDamage and ignores NBT</b> * * @param item * The {@link ItemStack} to get its demanded amount for. * @return the demanded amount for the given ingredient item */ public int demandedAmount(ItemStack item) { // go through ingredients and see if item and damage are equal Iterator<ItemStack> it = ings.iterator(); while (it.hasNext()) { ItemStack is = it.next(); if (compareItemStacksLight(is, item)) { return is.stackSize; } } return 0; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { // eclipse generated if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } InWorldRecipe other = (InWorldRecipe) obj; if (ings == null) { if (other.ings != null) { return false; } } else if (!ings.equals(other.ings)) { return false; } if (result == null) { if (other.result != null) { return false; } } else if (!result.equals(other.result)) { return false; } return true; } /** * Returns a {@link Vector} of ingredietns for this recipe. * * @return the ingredients of this recipe */ public Vector<ItemStack> getIngredients() { return ings; } /** * Returns the size of this recipe. Size means amount of ingredients in this * context. * * @return The amount of ingredients (the size) of this recipe */ public int getRecipeSize() { return ings.size(); } /** * Returns the result of this recipe * * @return The resulting {@link ItemStack} of this recipe */ public ItemStack getResult() { return result; } /** * {@inheritDoc} */ @Override public int hashCode() { // eclipse generated final int prime = 31; int result = 1; result = prime * result + (ings == null ? 0 : ings.hashCode()); result = prime * result + (this.result == null ? 0 : this.result.hashCode()); return result; } } The manager class: package org.bitbucket.pickaxe_engineer.api.worldcrafting; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Vector; import net.minecraft.entity.item.EntityItem; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import cpw.mods.fml.common.event.FMLInitializationEvent; /** * A crafting manager that is capable of managing and crafting * {@link InWorldRecipe}s. * * It is recommended to add recipes during the {@link FMLInitializationEvent}. * * This crafting manager can (currently only) add recipes of type * {@link InWorldRecipe}. It is also able to find matching recipes for a given * list of ingredients and processes the complete 'crafting process' for a given * world and bounding box. * * <p> * <b>Warning</b> This class was written quickly and did not went through JUnit * tests. Its also not very completed, since NBTTags do matter and this class * just ignores them. The original idea comes from the minecraft forge forums: * http://www.minecraftforge.net/forum/index.php/topic,24744.0.html * </p> * * @author pickaxe_engineer * @version 1.0 */ public class InWorldCraftingManager { private Vector<InWorldRecipe> recipes; /** * The instance of this {@link InWorldCraftingManager}. Its thought to exist * only once per server?(not sure actually) */ public static InWorldCraftingManager object = new InWorldCraftingManager(); /** * Creates a new {@link InWorldCraftingManager}. This is private since it * should only exist one craftingmanager per server? */ private InWorldCraftingManager() { recipes = new Vector<InWorldRecipe>(10);// avarage of such recipes } /** * Adds the given recipe to the list of recipes this manager knows. Like * registering a recipe to the GameRegistry. * * @param recipe * The recipe to add */ public void addRecipe(InWorldRecipe recipe) { recipes.add(recipe); Collections.sort(recipes); } /** * Don't use this method since it does not consume items. * * @deprecated */ @Deprecated public List<ItemStack> craft(List<ItemStack> items) { InWorldRecipe recipe = findMatching(items); int nbResults = 0; if (recipe != null) { for (ItemStack is : items) { int demanded = recipe.demandedAmount(is); if (demanded < is.stackSize) { // does not have the correct amount return null; } int stackFactor = is.stackSize / demanded; // integer division always rounded to next smaller integer // following code looks that the smallest factor will be used // later. if (nbResults == 0) { nbResults = stackFactor; } else { nbResults = Math.min(nbResults, stackFactor); } } } if (nbResults == 0) { // recipe was null return null; } else { // make as many results as possible List<ItemStack> out = new ArrayList<ItemStack>(nbResults); for (int i = 0; i < nbResults; i++) { out.add(recipe.getResult()); } return out; } } /** * Crafts whatever is possible within the given bounds. This method does a * lot at once. It firstly searches for {@link EntityItem}s within the given * {@link AxisAlignedBB}. Then it looks if a recipe matches (with * {@link InWorldCraftingManager#findMatching(List)}) and if found one - it * crafts as many times result as possible and consumes the used * ingredients. * * @param world * The world where the crafting should happen. This method tests * if its executed on the server and if not, it returns * immediatley. * @param bounds * The bounds in where to look for items ( {@link EntityItem}s ). * @param x * The x-coordinate on which the result-spawning-calculation will * start * @param y * The y-coordinate on which the result-spawning-calculation will * start * @param z * The z-coordinate on which the result-spawning-calculation will * start */ public void craftIfPossible(World world, AxisAlignedBB bounds, int x, int y, int z) { if (world.isRemote) { return;// don't craft on client } List<EntityItem> entities = world.getEntitiesWithinAABB( EntityItem.class, bounds); if (entities == null || entities.size() == 0) { return;// no items found or the list has no entries (not sure if // possible). } List<ItemStack> items = extractItemStacks(entities); InWorldRecipe recipe = findMatching(items); Vector<Integer> nbConsumed = craftRecipe(recipe, items, world, x, y, z); if (nbConsumed == null) { // nothing crafted System.out.println("Did not craft"); return; } else { // crafted items spawned already in world! } for (int i = 0; i < nbConsumed.size(); i++) { // going through nbConsumed: // if actual nbConsumed > 0 -> entities[i].getEntityItem().stackSize // -= nbConsumed // if entities[i].getEntityItem().stackSize <= 0: // entities[i].setDead if (nbConsumed.get(i) > 0) { entities.get(i).getEntityItem().stackSize -= nbConsumed.get(i); if (entities.get(i).getEntityItem().stackSize <= 0) { entities.get(i).setDead(); world.removeEntity(entities.get(i)); } } } } /** * DONT use. Does nothing */ @Deprecated public void craftInWorld(World world, int x, int y, int z, List<ItemStack> items) { } /** * Crafts a recipe and returns the amount of used items. The returning * list's entries do correspond to the argument {@link List} of * {@link ItemStack}s. Thus in the returning {@link Vector} at position i is * stored how much items of the ItemStack at position i are used. * <b>Warning: this method should only be invoked on server side, but does * not test it!</b> * * @param recipe * The recipe to craft * @param items * The ingredients * @param world * The world in where the crafting happens * @param x * The x-coordinate on which the result-spawning-calculation will * start * @param y * The y-coordinate on which the result-spawning-calculation will * start * @param z * The z-coordinate on which the result-spawning-calculation will * start * @return A {@link Vector} of {@link Integer}s which correspond to the * amount of items used during the crafting process. If no crafting * happend it returns NULL! */ private Vector<Integer> craftRecipe(InWorldRecipe recipe, List<ItemStack> items, World world, int x, int y, int z) { if (recipe != null && items != null && items.size() > 0) { Vector<Integer> nbConsumed = new Vector<Integer>(items.size()); int nbResults = 0; for (ItemStack is : items) { int demanded = recipe.demandedAmount(is); if (demanded > is.stackSize) { // does not have the correct amount return null; } int stackFactor = is.stackSize / demanded; // integer division always rounded to next smaller integer // following code looks that the smallest factor will be used // later. if (nbResults == 0) { nbResults = stackFactor; } else { nbResults = Math.min(nbResults, stackFactor); } nbConsumed.add(demanded * nbResults); }// end loop items if (nbResults == 0) { // recipe was null return null; } else { spawnItems(world, x, y, z, recipe.getResult(), nbResults); } return nbConsumed; } // null recipe or empty list return null; } /** * Extracts the {@link ItemStack}s out of a list of {@link EntityItem}s * * @param list * The {@link EntityItem} {@link List} from where to extract * @return A {@link List} of {@link ItemStack}s which correspond to the * {@link EntityItem} list. Or NULL if the list was null or empty. */ private List<ItemStack> extractItemStacks(List<EntityItem> list) { if (list == null || list.size() == 0) { return null; // emtpy list } List<ItemStack> out = new Vector<ItemStack>(list.size()); for (EntityItem ei : list) { out.add(ei.getEntityItem()); } return out; } /** * Returns the matching {@link InWorldRecipe} that has the given items as * ingredients. * * @param items * A {@link List} of {@link ItemStack}s with items to test. * @return Either the mathing {@link InWorldRecipe} where the given items * are ingredients or <code>null</code> if no recipe was found */ public InWorldRecipe findMatching(List<ItemStack> items) { for (InWorldRecipe recipe : recipes) { if (recipe.getRecipeSize() != items.size()) { continue; // the sizes do not match: next recipe please! } int nbMatchingItems = 0; for (ItemStack is : items) { if (recipe.containsIngredient(is)) { // okay, is is part of the recipe nbMatchingItems++; } } if (nbMatchingItems == recipe.getRecipeSize()) { // yeah found recipe (but did not check correct amount!) return recipe; } else { // not enough matching items return null; } } return null; } /** * Spawns a given amount of {@link EntityItem}s in the given world, near the * given position. This method respects the stacksize limits and thus spawns * several entities if the stacksize would overflow. * * @param world * The world in where to spawn the items. If this world is * remote, the method does nothing * @param x * The x-coordinate on which the result-spawning-calculation will * start * @param y * The y-coordinate on which the result-spawning-calculation will * start * @param z * The z-coordinate on which the result-spawning-calculation will * start * @param item * The item to spawn. If this is null, nothing happens * @param nbResults * The amount of times the item should spawn. Example: the item * is 9 redstone pieces and nbResults is 4, a stack of 36 * redstone pieces would spawn. */ private void spawnItems(World world, int x, int y, int z, ItemStack item, int nbResults) { if (item != null && nbResults > 0 && !world.isRemote) { int total = item.stackSize * nbResults; if (total <= item.getMaxStackSize()) { item.stackSize = total; EntityItem ei = new EntityItem(world, x + 0.5, y + 1.5, z + 0.5, item); world.spawnEntityInWorld(ei); } else { int nbStacks = total / item.getMaxStackSize(); int remaining = total % item.getMaxStackSize(); for (int i = 0; i < nbStacks; i++) { ItemStack is = new ItemStack(item.getItem(), item.getMaxStackSize(), item.getItemDamage()); EntityItem ei = new EntityItem(world, x + 0.5, y + 1.5, z + 0.5, is); world.spawnEntityInWorld(ei); } if (remaining > 0) { ItemStack is = new ItemStack(item.getItem(), remaining, item.getItemDamage()); EntityItem ei = new EntityItem(world, x + 0.5, y + 1.5, z + 0.5, is); world.spawnEntityInWorld(ei); } } } } /** * DONT use. Its not working! */ @Deprecated private void spawnItems(World world, int x, int y, int z, List<ItemStack> items) { for (ItemStack is : items) { if (is == null) { System.out.println("the is was null"); continue;// avoid spawining nulls in world } System.out.println("spawning: " + is.getUnlocalizedName());// DEBUG EntityItem ei = new EntityItem(world, x + 0.5, y + 1.5, z + .5, is); world.spawnEntityInWorld(ei); } } } [/spoiler] And lastly the Item that I used to test. Again, don't use the manager like I did in here /** * TEST CLASS * ONLY FOR DEBUG PURPOSE! */ public class InWorldCrafter extends Item { private InWorldCraftingManager manager = InWorldCraftingManager.object; public InWorldCrafter() { manager.addRecipe(new InWorldRecipe(new ItemStack(Blocks.diamond_block), new ItemStack(Items.diamond, 9))); manager.addRecipe(new InWorldRecipe(new ItemStack(Items.diamond_axe), new ItemStack(Items.diamond, 3), new ItemStack(Items.stick, 2))); manager.addRecipe(new InWorldRecipe(new ItemStack(Blocks.redstone_lamp), new ItemStack(Items.redstone, 3), new ItemStack(Items.glowstone_dust, 4), new ItemStack(Blocks.cobblestone))); setCreativeTab(CreativeTabs.tabTools); setMaxStackSize(1); setFull3D(); setNoRepair(); setUnlocalizedName("crafter"); setTextureName("stick"); } /** * Callback for item usage. If the item does something special on right * clicking, he will have one of those. Return True if something happen and * false if it don't. This is for ITEMS, not BLOCKS */ @Override public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) { if (!world.isRemote) { manager.craftIfPossible(world, AxisAlignedBB.getBoundingBox(x - 1, y - 1, z - 1, x + 2, y + 2, z + 2),x,y,z); } return true; } } Feel free to criticize the code and make useful additions. If any of the moderators feel that this post belongs not in here, please feel free to move it to the correct spot. Sincerely -pickaxe PS: For the future, dear Electrobob99, I'd suggest to only code what you really can. Since English is not my mother tongue, my sentences may are confusing. I'm coding java for a long time now - just MC and forge stop me sometimes.
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.