Skip to content

Integrating food items with AppleCore

Ryan Liptak edited this page Dec 10, 2017 · 17 revisions

If you want your food to integrate with AppleCore, do not call FoodStats.addStats(int, float) when it is eaten. Use the ItemStack-aware FoodStats.func_151686_a(ItemFood, ItemStack) method instead.

ItemFood integration checklist

If your food extends ItemFood and uses the standard Minecraft ItemFood methods, your food will integrate with AppleCore without issue.

  • Have your item extend ItemFood.
  • Use the ItemStack-aware hunger and saturation getters (getHealAmount/func_150905_g for hunger value and getSaturationModifier/func_150906_h for the saturation modifier).
  • Use the default ItemFood.onEaten (1.7.10) / ItemFood.onItemUseFinish (1.8+) implementation or make sure that your overridden version still calls FoodStats.func_151686_a(ItemFood, ItemStack) (the ItemStack-aware version of FoodStats.addStats).

That's it. Your food will integrate properly with AppleCore if you're able to check off all of the above. If you want to use metadata-based food items, you can find an example AppleCore-compatible implementation in the example package.

Non-standard food integration

If your food does not extend ItemFood, then you can implement AppleCore's IEdible interface and use ItemFoodProxy to call FoodStats.func_151686_a(ItemFood, ItemStack) when the food is eaten.

Here is an example implementation with the irrelevant parts removed (full implementation):

@Optional.Interface(iface = "squeek.applecore.api.food.IEdible", modid = "AppleCore")
public class ItemNonStandardFood extends Item implements IEdible
{
	@Optional.Method(modid = "AppleCore")
	@Override
	public FoodValues getFoodValues(ItemStack itemStack)
	{
		return new FoodValues(1, 1f);
	}

	// This needs to be abstracted into an Optional method,
	// otherwise the ItemFoodProxy reference will cause problems
	@Optional.Method(modid = "AppleCore")
	public void onEatenCompatibility(ItemStack itemStack, EntityPlayer player)
	{
		// one possible compatible method
		player.getFoodStats().func_151686_a(new ItemFoodProxy(this), itemStack);

		// another possible compatible method:
		// new ItemFoodProxy(this).onEaten(itemStack, player);
	}

	@Override
	// this method was called onEaten in 1.7.10
	public ItemStack onItemUseFinish(ItemStack itemStack, World world, EntityPlayer player)
	{
		--itemStack.stackSize;

		if (Loader.isModLoaded("AppleCore"))
		{
			onEatenCompatibility(itemStack, player);
		}
		else
		{
			// this method is not compatible with AppleCore
			player.getFoodStats().addStats(1, 1f);
		}

		return itemStack;
	}
}

Note also that the above example degrades nicely: if AppleCore is not loaded, it will still function as expected.

Making a food item inedible

Foods are considered inedible, and therefore will return false when passed to AppleCoreAPI.accessor.isFood if the Item implementation does not return EnumAction.EAT or EnumAction.DRINK from Item.getItemUseAction.

public class ItemFoodEdibleSometimes extends ItemFood
{
	@Override
	public EnumAction getItemUseAction(ItemStack itemStack)
	{
		return someCondition(itemStack) ? EnumAction.EAT : EnumAction.NONE;
	}
}

Block-based foods (like cake)

To mark a block as edible, have its Block class implement IEdibleBlock. Here is an example implementation with the irrelevant parts removed (full implementation):

@Optional.Interface(iface = "squeek.applecore.api.food.IEdibleBlock", modid = "AppleCore")
public class BlockEdibleExample extends Block implements IEdibleBlock
{
	private boolean isEdibleAtMaxHunger = false;

	@Optional.Method(modid = "AppleCore")
	@Override
	public void setEdibleAtMaxHunger(boolean value)
	{
		isEdibleAtMaxHunger = value;
	}

	@Optional.Method(modid = "AppleCore")
	@Override
	public FoodValues getFoodValues(ItemStack itemStack)
	{
		return new FoodValues(2, 0.1f);
	}

	// This needs to be abstracted into an Optional method,
	// otherwise the ItemFoodProxy reference will cause problems
	@Optional.Method(modid = "AppleCore")
	public void onEatenCompatibility(ItemStack itemStack, EntityPlayer player)
	{
		// one possible compatible method
		player.getFoodStats().addStats(new ItemFoodProxy(this), itemStack);

		// another possible compatible method:
		// new ItemFoodProxy(this).onEaten(itemStack, player);
	}

	@Override
	public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ)
	{
		if (player.canEat(isEdibleAtMaxHunger))
		{
			if (Loader.isModLoaded("AppleCore"))
			{
				onEatenCompatibility(new ItemStack(this), player);
			}
			else
			{
				// this method is not compatible with AppleCore
				player.getFoodStats().addStats(2, 0.1f);
			}
		}
		return true;
	}
}

If your Block is registered with an ItemBlock counterpart, then that's all that is needed. However, if your Block and Item are not registered together (e.g. like Minecraft's cake, where Item.getItemFromBlock(Blocks.CAKE) returns null), then you need to register the Item <-> Block relationship with AppleCore like so (see IAppleCoreRegistry for more info):

AppleCoreAPI.registry.registerEdibleBlock(block, item);