- Версия(и) Minecraft
- 1.12.2
Туториалов на тему создания хранилища для своей (или не своей) жидкости - не так уж много. Как на просторах русскоязычного моддинга, так и на англоязычных источниках. Этим туториалом я надеюсь исправить данную ситуацию.
Основное
Прежде всего, давайте создадим класс
А также класс
Дополнительно вы должны будете создать класс
Тайл-энтити
Создадим основной тайл-энтити для нашего резервуара
Разберём его:
А теперь пришло время создать блок нашего хранилища жидкости. Чтобы это сделать - создайте новый класс
Он расширяет родительский класс BlockTileEntity и содержит обобщение TileEntityFluidTank, так как тайл-энтити будет привязан к этому блоку.
Регистрация
Для регистрации блока-хранилища жидкости вместе с его тайл-энтити, необходимо добавить его в ваш
Затем зарегистрировать блок как и все остальные блоки, добавив (в моём случае) этот код в:
При клике пустой рукой по резервуару - отобразится жидкость в нём и её количество. Максимум можно заполнить 5 вёдер
Совместимость
Хранилище совместимо со всеми видами жидкостей из других модов или вашего мода.
Основное
Прежде всего, давайте создадим класс
BlockBase
, который будет наследовать BlockContainer.
BlockBase.java:
public class BlockBase extends BlockContainer
{
public BlockBase(String name, Material material)
{
super(material);
this.setRegistryName(name);
this.setUnlocalizedName(name);
}
@Override
public TileEntityFluidTank createNewTileEntity(World worldIn, int meta)
{
return new TileEntityFluidTank();
}
}
BlockTileEntity
, который упростит создание тайл-энтити для блока резервуара:
BlockTileEntity.java:
public abstract class BlockTileEntity<T extends TileEntity> extends BlockBase
{
public BlockTileEntity(String name, Material material)
{
super(name, material);
GameRegistry.registerTileEntity(this.getTileEntityClass(), this.getRegistryName().toString());
}
public abstract Class<T> getTileEntityClass();
public T getTileEntity(IBlockAccess world, BlockPos position)
{
return (T) world.getTileEntity(position);
}
@Override
public boolean hasTileEntity(IBlockState blockState)
{
return true;
}
@Nullable
@Override
public abstract T createTileEntity(World world, IBlockState blockState);
}
FluidHelper
. Он представляет собой некоторую функциональность библиотеки CodeChickenCore:
FluidHelper.java:
public class FluidHelper
{
@CapabilityInject (IFluidHandlerItem.class)
public static final Capability<IFluidHandler> FLUID_HANDLER_ITEM = null;
public static boolean isFluidHandler(ItemStack stack)
{
return !stack.isEmpty() && stack.hasCapability(FLUID_HANDLER_ITEM, null);
}
public static FluidStack getFluidStackFromHandler(ItemStack container)
{
if (isFluidHandler(container)) {
IFluidTankProperties[] tank = container.getCapability(FLUID_HANDLER_ITEM, null).getTankProperties();
return tank.length <= 0 ? null : tank[0].getContents();
}
return null;
}
public static FluidStack getFluidForFilledItem(ItemStack container)
{
if (container != null && isFluidHandler(container)) {
return getFluidStackFromHandler(container);
}
return null;
}
public static boolean isBucket(ItemStack stack)
{
return stack.getItem() == Items.BUCKET;
}
}
Тайл-энтити
Создадим основной тайл-энтити для нашего резервуара
TileEntityFluidTank
:
TileEntityFluidTank.java:
public class TileEntityFluidTank extends TileEntity implements IFluidHandler
{
public FluidTank tank;
private int capacity;
public TileEntityFluidTank()
{
// Устанавливаем вместимость цистерны. В данном случае вместимость в 5 вёдер
capacity = Fluid.BUCKET_VOLUME * 5;
tank = new FluidTank(capacity);
}
// Метод, которым можно будет уменьшить вместимость цистерны
public void decreaseTankCapacity(int tankCapacityModifier)
{
if (tank.getCapacity() > Fluid.BUCKET_VOLUME * tankCapacityModifier)
{
tank.setCapacity(tank.getCapacity() - (Fluid.BUCKET_VOLUME * tankCapacityModifier));
} else {
tank.setCapacity(0);
}
}
// Метод, которым можно будет увеличить вместимость цистерны
public void increaseTankCapacity(int tankCapacityModifier)
{
tank.setCapacity(tank.getCapacity() + (Fluid.BUCKET_VOLUME * tankCapacityModifier));
}
@Override
public void readFromNBT(NBTTagCompound nbt)
{
this.tank.setCapacity(nbt.getInteger("TankCapacity") * Fluid.BUCKET_VOLUME);
if (nbt.getBoolean("hasFluid"))
{
this.tank.setFluid(FluidRegistry.getFluidStack(nbt.getString("fluidName"), nbt.getInteger("fluidAmount")));
} else {
this.tank.setFluid(null);
}
super.readFromNBT(nbt);
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
{
nbt.setBoolean("hasFluid", tank.getFluid() != null);
if (tank.getFluid() != null)
{
nbt.setString("fluidName", tank.getFluid().getFluid().getName());
nbt.setInteger("fluidAmount", tank.getFluidAmount());
}
nbt.setInteger("TankCapacity", tank.getCapacity() / Fluid.BUCKET_VOLUME);
return super.writeToNBT(nbt);
}
@Override
public int fill(FluidStack resource, boolean doFill)
{
int amount = tank.fill(resource, doFill);
if (amount > 0 && doFill)
world.markBlockRangeForRenderUpdate(pos, pos);
return amount;
}
@Override
public FluidStack drain(FluidStack resource, boolean doDrain)
{
if (tank.getFluidAmount() > 0 && tank.getFluid().getFluid() != resource.getFluid())
{
return this.drain(resource.amount, doDrain);
}
return null;
}
@Override
public FluidStack drain(int maxDrain, boolean doDrain)
{
FluidStack fluidStack = tank.drain(maxDrain, doDrain);
if (fluidStack != null && doDrain)
world.markBlockRangeForRenderUpdate(pos, pos);
return fluidStack;
}
@Override
public IFluidTankProperties[] getTankProperties()
{
FluidStack fluidStack = tank.getFluid();
if (fluidStack != null)
{
return new IFluidTankProperties[]
{
new FluidTankProperties(fluidStack.copy(), tank.getCapacity())
};
}
return new IFluidTankProperties[] { null };
}
// Благодаря методам ниже - количество жидкости при перезаходе в мир или на сервер будет сохранено
@Override
@Nullable
public SPacketUpdateTileEntity getUpdatePacket()
{
return new SPacketUpdateTileEntity(this.pos, 3, this.getUpdateTag());
}
@Override
public NBTTagCompound getUpdateTag()
{
return this.writeToNBT(new NBTTagCompound());
}
@Override
public void onDataPacket(NetworkManager networkManager, SPacketUpdateTileEntity packet)
{
super.onDataPacket(networkManager, packet);
this.handleUpdateTag(packet.getNbtCompound());
}
}
- В конструкторе, который вызывается при создании тайла мы задаём значение вместимости резервуара.
- Чтобы изменить количество вместимой жидкости, измените число, на которое умножаеться переменная
BUCKET_VOLUME
(содержит число стандартного количества жидкости в одном ведре, равное 1,000 единицам). Я установил размер резервуара в 5,000 единиц (5 вёдер).
А теперь пришло время создать блок нашего хранилища жидкости. Чтобы это сделать - создайте новый класс
FluidTank
:
FluidTank.java:
public class FluidTank extends BlockTileEntity<TileEntityFluidTank>
{
public FluidTank(String name, Material material)
{
super(name, material);
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
// Если игрок приседает - возвращаем false (т.е. - жидкость разольётся в мир)
if (player.isSneaking()) return false;
// Проверка на сервер. Обязательно для Tile Entity
if(!world.isRemote)
{
TileEntityFluidTank tile = this.getTileEntity(world, pos);
if (tile != null)
{
ItemStack is = player.getHeldItemMainhand();
// Если в руке есть предмет
if (!is.isEmpty())
{
// Получаем стак с жидкостью в нашей руке
FluidStack liquid = FluidHelper.getFluidForFilledItem(is);
if (liquid != null)
{
// Вызываем метод заполнения с аргументом false, чтобы получить количество жидкости, которое будет заполнено
int amount = tile.fill(liquid, false);
// Проверяем что количество заполняемой жидкости равное количеству жидкости в нашей руке
if (amount == liquid.amount)
{
// Вызываем метод заполнения с аргументом true, который заполнит нашу цистерну жидкостью
tile.fill(liquid, true);
if (!player.capabilities.isCreativeMode)
{
// Если игрок не в креативе - заменяем предмет в руке на пустое ведро
player.inventory.setInventorySlotContents(player.inventory.currentItem, new ItemStack(Items.BUCKET));
}
// Возвращаем true как успешное взаимодействие с блоком (его заполнение жидкостью)
return true;
}
else
return true;
}
// Проверяем что у нас в руке пустое ведро
if (FluidHelper.isBucket(is))
{
// Получаем жидкость внутри нашей цистерны (переменная tile)
IFluidTankProperties[] tanks = tile.getTankProperties();
if (tanks[0] != null)
{
// Получаем стак жидкости из цистерны
FluidStack fillFluid = tanks[0].getContents();
// Получаем заполненное ведро с этой жидкостью
ItemStack fillStack = FluidUtil.getFilledBucket(fillFluid);
if (fillStack != null)
{
// Осушаем нашу цистерну на количество, которое мы забрали ведром
tile.drain(FluidUtil.getFluidContained(fillStack).amount, true);
if (!player.capabilities.isCreativeMode)
{
// Если ведро одно
if (is.getCount() == 1)
{
is.shrink(1);
player.inventory.setInventorySlotContents(player.inventory.currentItem, fillStack);
} else {
is.shrink(1);
player.inventory.setInventorySlotContents(player.inventory.currentItem, is);
// Выбрасываем наполненное ведро в мир если инвентарь заполнен
if (!player.inventory.addItemStackToInventory(fillStack))
player.dropItem(fillStack, false);
}
}
// Возвращаем true как успешное взаимодействие с блоком (его заполнение жидкостью)
return true;
}
else
return true;
}
}
} else {
// Если мы нажмём пустой рукой и в цистерне есть жидкость - отобразит её количество на экран
if (tile.tank.getInfo() != null && tile.tank.getInfo().fluid != null)
player.sendMessage(new TextComponentString("Жидкость / Количество: " + tile.tank.getInfo().fluid.getLocalizedName() + " / " + tile.tank.getInfo().fluid.amount));
}
return true;
}
}
return true;
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos)
{
// Устанавливаем коллизии нашей цистерне
return new AxisAlignedBB(2 / 16D, 0 / 16D, 2 / 16D, 14 / 16D, 16 / 16D, 14 / 16D);
}
@SideOnly(Side.CLIENT)
public BlockRenderLayer getBlockLayer()
{
return BlockRenderLayer.CUTOUT;
}
@Override
public boolean isFullCube(IBlockState state)
{
// Для того чтобы наша цистерна была прозрачной
return false;
}
@Override
public boolean hasComparatorInputOverride(IBlockState state)
{
return true;
}
@Override
public boolean isOpaqueCube(IBlockState state)
{
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state)
{
return EnumBlockRenderType.MODEL;
}
@Override
public int quantityDropped(Random rnd)
{
return 0;
}
@Override
public int getComparatorInputOverride(IBlockState blockState, World worldIn, BlockPos pos)
{
return 0;
}
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest)
{
return world.setBlockToAir(pos);
}
@Override
public void onBlockExploded(World world, BlockPos pos, Explosion explosion)
{
world.setBlockToAir(pos);
onBlockDestroyedByExplosion(world, pos, explosion);
}
@Override
public Class<TileEntityFluidTank> getTileEntityClass()
{
return TileEntityFluidTank.class;
}
@Override
public TileEntityFluidTank createTileEntity(World world, IBlockState blockState)
{
return new TileEntityFluidTank();
}
}
Регистрация
Для регистрации блока-хранилища жидкости вместе с его тайл-энтити, необходимо добавить его в ваш
BlocksRegister
(у вас класс может быть другим):
BlocksRegister.java:
public class BlocksRegister
{
// Добавляем блок в регистрации а также устанавливаем расположение в креативной вкладке "Остальное"
public static final Block TANK = new FluidTank("fluidtank", Material.GLASS).setCreativeTab(CreativeTabs.MISC);
public static void register()
{
setRegister(TANK);
}
@SideOnly(Side.CLIENT)
public static void registerRender()
{
setRender(TANK);
}
private static void setRegister(Block block)
{
ForgeRegistries.BLOCKS.register(block);
ForgeRegistries.ITEMS.register(new ItemBlock(block).setRegistryName(block.getRegistryName()));
}
@SideOnly(Side.CLIENT)
private static void setRender(Block block)
{
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(Item.getItemFromBlock(block), 0, new ModelResourceLocation(block.getRegistryName(), "inventory"));
}
}
Затем зарегистрировать блок как и все остальные блоки, добавив (в моём случае) этот код в:
ClientProxy
init
->BlocksRegister.registerRender();
CommonProxy
preInit
->BlocksRegister.register();
При клике пустой рукой по резервуару - отобразится жидкость в нём и её количество. Максимум можно заполнить 5 вёдер
Совместимость
Хранилище совместимо со всеми видами жидкостей из других модов или вашего мода.