Хранилище жидкости

Хранилище жидкости

Нет прав для скачивания
Версия(и) Minecraft
1.12.2
Туториалов на тему создания хранилища для своей (или не своей) жидкости - не так уж много. Как на просторах русскоязычного моддинга, так и на англоязычных источниках. Этим туториалом я надеюсь исправить данную ситуацию.

Основное
Прежде всего, давайте создадим класс 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();
    }
}
Он расширяет родительский класс BlockTileEntity и содержит обобщение 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"));
    }
}

Затем зарегистрировать блок как и все остальные блоки, добавив (в моём случае) этот код в:
  1. ClientProxy init -> BlocksRegister.registerRender();
  2. CommonProxy preInit -> BlocksRegister.register();
Можете запускать игру и проверять!
2021-07-21_18.02.29.png

При клике пустой рукой по резервуару - отобразится жидкость в нём и её количество. Максимум можно заполнить 5 вёдер

Совместимость
Хранилище совместимо со всеми видами жидкостей из других модов или вашего мода.

2021-07-21_18.35.41.png
Автор
sk9zist :l
Скачивания
1
Просмотры
1,395
Первый выпуск
Обновление
Оценка
5.00 звёзд 1 оценок

Другие ресурсы пользователя sk9zist :l

Сверху