Right-click to make a bush that can be harvested
--A class for registering added blocks and items and a proxy class are prepared. -(I did it according to Ayutaki-san's tutorial) -Pixel art of texture is prepared
Note: I didn't write anything about the new process, I just assigned it to a vanilla variable.
--1.12.2 - ItemFood.class - ItemSeed.class - BlockCrops.class - BlockCarrots.class - apple.json - acacia_sapling.json - carrots.json --1.14.4 - SweetBerryBushBlock.class
I would like to add an apricot tree (harvesting apricots from this mod) Remarks: Please have a registration class ready
The package name is a vanilla man
src/main/java --FFFMItems.java (class for registering items) - oguro.fffm.init --FFFMBlocks.java (block registration class) - oguro.fffm.init --BlockFruitTree.java (class that determines the characteristics of fruit trees) - oguro.fffm.block --BlockApricotTree.java (Class that determines the unique properties of Apricot Tree) - oguro.fffm.block --ItemFruit.java (class that determines the characteristics of fruits) - oguro.fffm.item --ItemApricot.java (class that determines apricot's unique properties) - oguro.fffm.item --ItemSapling.java (Class that determines the nature of fruit tree seedlings) - oguro.fffm.item --ItemApricotSapling.java (A class that determines the unique properties of apricot seedlings) - oguro.fffm.item
Remarks: I haven't prepared the json that looks like in the inventory of BlockApricotTree because it will not be displayed in the inventory unless I use the command, but it is annoying because an error appears in the log, so it may be better to prepare it (it looks good) It's okay because it just can't be read)
--apricot.json (Appearance of apricot) - assets.fffm.models.item --apricot_tree_sapling.json (Appearance of apricot seedlings) - assets.fffm.models.item --apricot_tree (Apricot tree blocks are multiple blocks with different metadata, so the json that describes each appearance is a file that specifies which one) - assets.fffm.blockstates
--apricot.png (Apricot texture) - assets.fffm.textures.items --apricot_tree_sapling.png (Apricot seedling texture) - assets.fffm.textures.items
FFFMItems.java
package oguro.fffm.init;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraft.item.ItemFood;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import oguro.fffm.FFFMRef;
import oguro.fffm.item.ItemApricot;
import oguro.fffm.item.ItemApricotTreeSapling;
import oguro.fffm.item.ItemSapling;
public class FFFMItems
{
//アイテムを宣言する
public static ItemSapling APRICOT_TREE_SAPLING;
public static ItemFood APRICOT;
//アイテムのインスタンスを生成する
public static void init()
{
APRICOT_TREE_SAPLING = (ItemSapling) new ItemApricotTreeSapling(FFFMBlocks.APRICOT_TREE).setRegistryName("apricot_tree_sapling")
.setUnlocalizedName("apricot_tree_sapling");
APRICOT = (ItemFood) new ItemApricot().setRegistryName("apricot")
.setUnlocalizedName("apricot");
}
//アイテムを登録する
public static void register()
{
registerItem(APRICOT_TREE_SAPLING);
registerItem(APRICOT);
}
public static void registerItem(Item item)
{
RegistrationHandler.ITEMS.add(item);
}
//アイテムの描画を登録する
public static void registerRenders()
{
registerRender(APRICOT_TREE_SAPLING);
registerRender(APRICOT);
}
private static void registerRender(Item item)
{
ModelLoader.setCustomModelResourceLocation(item, 0,
new ModelResourceLocation(item.getRegistryName(),"inventory"));
}
//内部クラスでアイテムを登録する
@Mod.EventBusSubscriber(modid = FFFMRef.MODID)
public static class RegistrationHandler
{
public static final List<Item> ITEMS = new LinkedList<>();
@SubscribeEvent
public static void registerItems(final RegistryEvent.Register<Item> event)
{
FFFMItems.init();
FFFMItems.register();
ITEMS.stream().forEach(item -> event.getRegistry().register(item));
}
}
}
FFFMBlocks.java
package oguro.fffm.init;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import oguro.fffm.FFFMRef;
import oguro.fffm.block.BlockApricotTree;
import oguro.fffm.block.BlockFruitTree;
public class FFFMBlocks
{
//登録するブロックを宣言
public static BlockFruitTree APRICOT_TREE;
//ブロックのインスタンスを生成
public static void init()
{
APRICOT_TREE = (BlockFruitTree) new BlockApricotTree().setRegistryName("apricot_tree")
.setUnlocalizedName("apricot_tree");
}
//ブロックを登録する
public static void register()
{
registerBlock(APRICOT_TREE);
}
//ブロックの登録
public static void registerBlock(Block block)
{
registerBlock(block, new ItemBlock(block));
}
//アイテムブロックの登録
public static void registerBlock(Block block, ItemBlock item)
{
RegistrationHandler.BLOCKS.add(block);
item.setRegistryName(block.getRegistryName());
FFFMItems.RegistrationHandler.ITEMS.add(item);
}
//アイテムブロックの描画を登録する
public static void registerRenders()
{
registerRender(APRICOT_TREE);
}
@SideOnly(Side.CLIENT)
private static void registerRender(Block block)
{
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(block), 0,
new ModelResourceLocation(block.getRegistryName(),"inventory"));
}
//内部クラスでブロックを登録する
@Mod.EventBusSubscriber(modid = FFFMRef.MODID)
public static class RegistrationHandler {
public static final List<Block> BLOCKS = new LinkedList<>();
@SubscribeEvent
public static void registerItems(final RegistryEvent.Register<Block> event) {
FFFMBlocks.init();
FFFMBlocks.register();
BLOCKS.stream().forEach(block -> event.getRegistry().register(block));
}
}
}
BlockFruitTree.java
package oguro.fffm.block;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockBush;
import net.minecraft.block.IGrowable;
import net.minecraft.block.SoundType;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import oguro.fffm.init.FFFMItems;
// While remodeling BlockCrops, I've been working on getting fruits from the SweetBerry Bush Block of 1.14.4.
//新しいフルーツを追加するときは、このクラスを継承してドロップするアイテムなどの変数を書き換えることにする。
public class BlockFruitTree
extends BlockBush
implements IGrowable
{
//作物の成長段階を作る。ここでは0から3の4段階。
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 3);
protected BlockFruitTree()
{
this.setDefaultState(this.blockState.getBaseState().withProperty(this.getAgeProperty(), Integer.valueOf(0)));
this.setTickRandomly(true);
this.setCreativeTab((CreativeTabs)null);
this.setHardness(0.0F);
this.setSoundType(SoundType.PLANT);
this.disableStats();
}
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
{
return FULL_BLOCK_AABB;
}
//成長に関する記述
protected boolean canSustainBush(IBlockState state)
{
//設置可能(作付け可能)なブロック
//BlockFruitTreeでは「耕地(FARMLAND)」、「草ブロック(GRASS)」、「土(DIRT)」の上に植えられるようにした。
return state.getBlock() == Blocks.FARMLAND ||
state.getBlock() == Blocks.GRASS ||
state.getBlock() == Blocks.DIRT;
}
protected PropertyInteger getAgeProperty()
{
return AGE;
}
public int getMaxAge()
{
//最大成長段階は今回は3
return 3;
}
protected int getAge(IBlockState state)
{
return ((Integer)state.getValue(this.getAgeProperty())).intValue();
}
public IBlockState withAge(int age)
{
return this.getDefaultState().withProperty(this.getAgeProperty(), Integer.valueOf(age));
}
public boolean isMaxAge(IBlockState state)
{
return ((Integer)state.getValue(this.getAgeProperty())).intValue() >= this.getMaxAge();
}
public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand)
{
super.updateTick(worldIn, pos, state, rand);
if (!worldIn.isAreaLoaded(pos, 1)) return; // Forge: prevent loading unloaded chunks when checking neighbor's light
if (worldIn.getLightFromNeighbors(pos.up()) >= 9)
{
int i = this.getAge(state);
if (i < this.getMaxAge())
{
float f = getGrowthChance(this, worldIn, pos);
if(net.minecraftforge.common.ForgeHooks.onCropsGrowPre(worldIn, pos, state, rand.nextInt((int)(25.0F / f) + 1) == 0))
{
worldIn.setBlockState(pos, this.withAge(i + 1), 2);
net.minecraftforge.common.ForgeHooks.onCropsGrowPost(worldIn, pos, state, worldIn.getBlockState(pos));
}
}
}
}
public void grow(World worldIn, BlockPos pos, IBlockState state)
{
int i = this.getAge(state) + this.getBonemealAgeIncrease(worldIn);
int j = this.getMaxAge();
if (i > j)
{
i = j;
}
worldIn.setBlockState(pos, this.withAge(i), 2);
}
protected int getBonemealAgeIncrease(World worldIn)
{
return MathHelper.getInt(worldIn.rand, 2, 5);
}
protected static float getGrowthChance(Block blockIn, World worldIn, BlockPos pos)
{
float f = 4.0F;
BlockPos blockpos = pos.down();
for (int i = -1; i <= 1; ++i)
{
for (int j = -1; j <= 1; ++j)
{
float f1 = 0.0F;
IBlockState iblockstate = worldIn.getBlockState(blockpos.add(i, 0, j));
if (iblockstate.getBlock().canSustainPlant(iblockstate, worldIn, blockpos.add(i, 0, j), net.minecraft.util.EnumFacing.UP, (net.minecraftforge.common.IPlantable)blockIn))
{
f1 = 4.0F;
if (iblockstate.getBlock().isFertile(worldIn, blockpos.add(i, 0, j)))
{
f1 = 5.2F;
}
}
if (i != 0 || j != 0)
{
f1 /= 6.0F;
}
f += f1;
}
}
BlockPos blockpos1 = pos.north();
BlockPos blockpos2 = pos.south();
BlockPos blockpos3 = pos.west();
BlockPos blockpos4 = pos.east();
boolean flag = blockIn == worldIn.getBlockState(blockpos3).getBlock() || blockIn == worldIn.getBlockState(blockpos4).getBlock();
boolean flag1 = blockIn == worldIn.getBlockState(blockpos1).getBlock() || blockIn == worldIn.getBlockState(blockpos2).getBlock();
if (flag && flag1)
{
f /= 2.0F;
}
else
{
boolean flag2 = blockIn == worldIn.getBlockState(blockpos3.north()).getBlock() || blockIn == worldIn.getBlockState(blockpos4.north()).getBlock() || blockIn == worldIn.getBlockState(blockpos4.south()).getBlock() || blockIn == worldIn.getBlockState(blockpos3.south()).getBlock();
if (flag2)
{
f /= 4.8F;
}
}
return f;
}
//破壊したときに落とす「苗アイテム」の設定
//このクラスはどの果樹からも直接呼び出されない予定なのでとりあえずあんずの苗にしておく。
protected Item getSapling()
{
return FFFMItems.APRICOT_TREE_SAPLING;
}
//破壊したときに落とす「果物」とその個数の設定
//このクラスはどの果樹からも直接呼び出されない予定なのでとりあえずあんずを2個にしておく。
protected Item getFruit()
{
return Items.APRICOT;
}
protected int getHowManyPickUp()
{
return 2;
}
//ドロップするようにする
public Item getItemDropped(IBlockState state, Random rand, int fortune)
{
return this.getSapling();
}
public ItemStack getItem(World worldIn, BlockPos pos, IBlockState state)
{
return new ItemStack(this.getSapling());
}
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
int i = this.getAge(state);
boolean flag = i == 3;
if (!flag && playerIn.getHeldItem(hand).getItem() == Items.DYE) {
return false;
}
//age=2(準最大成長)のときに収穫する処理
else if (i == 2)
{
//収穫できる個数
//getHowManyPickUpで設定した収穫個数だけ収穫できる設定にしてある。
int j = getHowManyPickUp();
spawnAsEntity(worldIn, pos, new ItemStack(getFruit(), j + (flag ? 1 : 0)));
//収穫時に鳴らす音
//新しい音を追加せずに「額縁からアイテムを剥ぎ取る音」にした。
worldIn.playSound((EntityPlayer)null, pos, SoundEvents.ENTITY_ITEMFRAME_REMOVE_ITEM, SoundCategory.BLOCKS, 1.0F, 0.8F + worldIn.rand.nextFloat() * 0.4F);
worldIn.setBlockState(pos, this.withAge(Integer.valueOf(0)), 2);
return true;
}
//age=3(最大成長)のときに収穫する処理
else if (i == 3)
{
//収穫できる個数
//getHowManyPickUpで設定した収穫個数の2倍を収穫できる設定にしてある。
int j = getHowManyPickUp() * 2;
spawnAsEntity(worldIn, pos, new ItemStack(getFruit(), j + (flag ? 1 : 0)));
//収穫時に鳴らす音
//新しい音を追加せずに「額縁からアイテムを剥ぎ取る音」にした。
worldIn.playSound((EntityPlayer)null, pos, SoundEvents.ENTITY_ITEMFRAME_REMOVE_ITEM, SoundCategory.BLOCKS, 1.0F, 0.8F + worldIn.rand.nextFloat() * 0.4F);
worldIn.setBlockState(pos, this.withAge(Integer.valueOf(0)), 2);
return true;
}
//age=2に満たないときは「収穫しない処理
else
{
return super.onBlockActivated(worldIn, pos, state, playerIn, hand, facing, hitX, hitY, hitZ);
}
}
//IGrowableに関する記述
@Override
public boolean canGrow(World worldIn, BlockPos pos, IBlockState state, boolean isClient)
{
return !this.isMaxAge(state);
}
@Override
public boolean canUseBonemeal(World worldIn, Random rand, BlockPos pos, IBlockState state)
{
return true;
}
@Override
public void grow(World worldIn, Random rand, BlockPos pos, IBlockState state)
{
this.grow(worldIn, pos, state);
}
//成長段階ごとのブロックをメタデータの異なるブロックとして管理
public IBlockState getStateFromMeta(int meta)
{
return this.withAge(meta);
}
public int getMetaFromState(IBlockState state)
{
return this.getAge(state);
}
protected BlockStateContainer createBlockState()
{
return new BlockStateContainer(this, new IProperty[] {AGE});
}
}
BlockApricotTree.java
package oguro.fffm.block;
import net.minecraft.item.Item;
import oguro.fffm.init.FFFMItems;
public class BlockApricotTree
extends BlockFruitTree
{
//苗アイテムを設定
protected Item getSapling()
{
return FFFMItems.APRICOT_TREE_SAPLING;
}
//果物アイテムを設定
protected Item getFruit()
{
return FFFMItems.APRICOT;
}
//収穫できる個数を設定
protected int getHowManyPickUp()
{
return 2;
}
}
ItemFruit.java
package oguro.fffm.item;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemFood;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import oguro.fffm.creativetab.FFFMCreativeTabs;
public class ItemFruit
extends ItemFood
{
//満腹度回復量
protected static int getHealAmount()
{
return 5;
}
//オオカミが食べるかどうか
protected static boolean canWolfEat()
{
return false;
}
//食べるのにかかる時間
protected int getEatTime()
{
return 32;
}
//食べるときのアニメーション
//デフォルトでEAT
public EnumAction getItemUseAction(ItemStack stack)
{
return EnumAction.EAT;
}
//満腹でも食べられるか
//デフォルトでいいえ
protected boolean canEatAlways()
{
return false;
}
public ItemFruit()
{
super(getHealAmount(), canWolfEat());
this.setCreativeTab(FFFMCreativeTabs.FFFM_FRUIT);
}
public int getMaxItemUseDuration(ItemStack stack)
{
return getEatTime();
}
public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn)
{
ItemStack itemstack = playerIn.getHeldItem(handIn);
if (playerIn.canEat(canEatAlways()))
{
playerIn.setActiveHand(handIn);
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, itemstack);
}
else
{
return new ActionResult<ItemStack>(EnumActionResult.FAIL, itemstack);
}
}
}
ItemApricot.java
package oguro.fffm.item;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemFood;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import oguro.fffm.creativetab.FFFMCreativeTabs;
public class ItemFruit
extends ItemFood
{
//満腹度回復量
protected static int getHealAmount()
{
return 5;
}
//オオカミが食べるかどうか
protected static boolean canWolfEat()
{
return false;
}
//食べるのにかかる時間
protected int getEatTime()
{
return 32;
}
//食べるときのアニメーション
//デフォルトでEAT
public EnumAction getItemUseAction(ItemStack stack)
{
return EnumAction.EAT;
}
//満腹でも食べられるか
//デフォルトでいいえ
protected boolean canEatAlways()
{
return false;
}
public ItemFruit()
{
super(getHealAmount(), canWolfEat());
this.setCreativeTab(FFFMCreativeTabs.FFFM_FRUIT);
}
public int getMaxItemUseDuration(ItemStack stack)
{
return getEatTime();
}
public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn)
{
ItemStack itemstack = playerIn.getHeldItem(handIn);
if (playerIn.canEat(canEatAlways()))
{
playerIn.setActiveHand(handIn);
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, itemstack);
}
else
{
return new ActionResult<ItemStack>(EnumActionResult.FAIL, itemstack);
}
}
}
ItemSapling.java
package oguro.fffm.item;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.IPlantable;
import oguro.fffm.block.BlockFruitTree;
import oguro.fffm.creativetab.FFFMCreativeTabs;
import oguro.fffm.init.FFFMBlocks;
//バニラのItemSeedを引っ張ってきたっぽい(覚えてない)。
//新しいフルーツを追加するときは、このクラスを継承して植える果樹などの変数を書き換えることにする。
public class ItemSapling
extends Item
implements IPlantable
{
private final BlockFruitTree crops;
protected Block getTree()
{
//植える果樹
//このクラスはどの果樹からも直接呼び出されない予定なのでとりあえずあんずの木にしておく。
return FFFMBlocks.APRICOT_TREE;
}
public ItemSapling(BlockFruitTree fruit_tree)
{
this.crops = (BlockFruitTree) this.getTree();
//クリエイティブタブ
//私は新しく「FFFM_SAPLING」というクリエイティブタブを作ってそこに入れた。
// If you want to use the vanilla creative tab as it is, this.setCreativeTab (CreativeTabs.FOOD);
this.setCreativeTab(FFFMCreativeTabs.FFFM_SAPLINGS);
}
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
ItemStack itemstack = player.getHeldItem(hand);
net.minecraft.block.state.IBlockState state = worldIn.getBlockState(pos);
if (facing == EnumFacing.UP && player.canPlayerEdit(pos.offset(facing), facing, itemstack) && state.getBlock().canSustainPlant(state, worldIn, pos, EnumFacing.UP, (IPlantable) this) && worldIn.isAirBlock(pos.up()))
{
worldIn.setBlockState(pos.up(), this.crops.getDefaultState());
if (player instanceof EntityPlayerMP)
{
CriteriaTriggers.PLACED_BLOCK.trigger((EntityPlayerMP)player, pos.up(), itemstack);
}
itemstack.shrink(1);
return EnumActionResult.SUCCESS;
}
else
{
return EnumActionResult.FAIL;
}
}
@Override
public net.minecraftforge.common.EnumPlantType getPlantType(net.minecraft.world.IBlockAccess world, BlockPos pos)
{
return net.minecraftforge.common.EnumPlantType.Plains;
}
@Override
public net.minecraft.block.state.IBlockState getPlant(net.minecraft.world.IBlockAccess world, BlockPos pos)
{
return this.crops.getDefaultState();
}
}
ItemApricotTreeSapling.java
package oguro.fffm.item;
import net.minecraft.block.Block;
import oguro.fffm.block.BlockFruitTree;
import oguro.fffm.init.FFFMBlocks;
public class ItemApricotTreeSapling extends ItemSapling {
public ItemApricotTreeSapling(BlockFruitTree fruit_tree) {
super(fruit_tree);
}
//植える果樹
@Override
protected Block getTree()
{
return FFFMBlocks.APRICOT_TREE;
}
}
If you play with json and class collision detection, you may be able to make it 2 squares in height
apricot.json
{
"parent": "item/generated",
"textures": {
"layer0": "fffm:items/apricot"
}
}
apricot_tree_sapling.json
{
"parent": "item/generated",
"textures": {
"layer0": "fffm:blocks/apricot_tree_sapling"
}
}
apricot_tree.json
{
"variants": {
"age=0": { "model": "fffm:apricot_tree_stage0" },
"age=1": { "model": "fffm:apricot_tree_stage1" },
"age=2": { "model": "fffm:apricot_tree_stage2" },
"age=3": { "model": "fffm:apricot_tree_stage3" }
}
}
apricot_tree_stage◯.json Play with the numbers in ◯
{
"parent": "block/cross",
"textures": {
"cross": "fffm:blocks/apricot_tree_stage◯"
}
}