Jump to content

[1.7.2+] Modding question


Electrobob99

Recommended Posts

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

Link to comment
Share on other sites

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];


}

Link to comment
Share on other sites

  • 1 month later...

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

Link to comment
Share on other sites

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".

Link to comment
Share on other sites

  • 2 weeks later...

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 xD

Member of Aerotech Networks, a Multi-Gaming server.

Also Currently porting the "Rise of the Automatons" minecraft mod

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.