base mod created
This commit is contained in:
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.BlockStairs;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyBool;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.property.IExtendedBlockState;
|
||||
import net.minecraftforge.common.property.IUnlistedProperty;
|
||||
import net.minecraftforge.common.property.Properties;
|
||||
import net.minecraftforge.common.property.PropertyFloat;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* This is a base implementation for Fluid blocks.
|
||||
*
|
||||
* It is highly recommended that you extend this class or one of the Forge-provided child classes.
|
||||
*
|
||||
*/
|
||||
public abstract class BlockFluidBase extends Block implements IFluidBlock
|
||||
{
|
||||
protected final static Map<Block, Boolean> defaultDisplacements = Maps.newHashMap();
|
||||
|
||||
static
|
||||
{
|
||||
defaultDisplacements.put(Blocks.OAK_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.SPRUCE_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.BIRCH_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.JUNGLE_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.ACACIA_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.DARK_OAK_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.TRAPDOOR, false);
|
||||
defaultDisplacements.put(Blocks.IRON_TRAPDOOR, false);
|
||||
defaultDisplacements.put(Blocks.OAK_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.SPRUCE_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.BIRCH_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.JUNGLE_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.DARK_OAK_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.ACACIA_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.NETHER_BRICK_FENCE, false);
|
||||
defaultDisplacements.put(Blocks.OAK_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.SPRUCE_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.BIRCH_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.JUNGLE_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.DARK_OAK_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.ACACIA_FENCE_GATE, false);
|
||||
defaultDisplacements.put(Blocks.WOODEN_PRESSURE_PLATE, false);
|
||||
defaultDisplacements.put(Blocks.STONE_PRESSURE_PLATE, false);
|
||||
defaultDisplacements.put(Blocks.LIGHT_WEIGHTED_PRESSURE_PLATE, false);
|
||||
defaultDisplacements.put(Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE, false);
|
||||
defaultDisplacements.put(Blocks.LADDER, false);
|
||||
defaultDisplacements.put(Blocks.IRON_BARS, false);
|
||||
defaultDisplacements.put(Blocks.GLASS_PANE, false);
|
||||
defaultDisplacements.put(Blocks.STAINED_GLASS_PANE, false);
|
||||
defaultDisplacements.put(Blocks.PORTAL, false);
|
||||
defaultDisplacements.put(Blocks.END_PORTAL, false);
|
||||
defaultDisplacements.put(Blocks.COBBLESTONE_WALL, false);
|
||||
defaultDisplacements.put(Blocks.BARRIER, false);
|
||||
defaultDisplacements.put(Blocks.STANDING_BANNER, false);
|
||||
defaultDisplacements.put(Blocks.WALL_BANNER, false);
|
||||
defaultDisplacements.put(Blocks.CAKE, false);
|
||||
|
||||
defaultDisplacements.put(Blocks.IRON_DOOR, false);
|
||||
defaultDisplacements.put(Blocks.STANDING_SIGN, false);
|
||||
defaultDisplacements.put(Blocks.WALL_SIGN, false);
|
||||
defaultDisplacements.put(Blocks.REEDS, false);
|
||||
}
|
||||
protected Map<Block, Boolean> displacements = Maps.newHashMap();
|
||||
|
||||
private static final class UnlistedPropertyBool extends Properties.PropertyAdapter<Boolean>
|
||||
{
|
||||
public UnlistedPropertyBool(String name)
|
||||
{
|
||||
super(PropertyBool.create(name));
|
||||
}
|
||||
}
|
||||
|
||||
public static final PropertyInteger LEVEL = PropertyInteger.create("level", 0, 15);
|
||||
public static final PropertyFloat[] LEVEL_CORNERS = new PropertyFloat[4];
|
||||
public static final PropertyFloat FLOW_DIRECTION = new PropertyFloat("flow_direction", -1000f, 1000f);
|
||||
public static final UnlistedPropertyBool[] SIDE_OVERLAYS = new UnlistedPropertyBool[4];
|
||||
public static final ImmutableList<IUnlistedProperty<?>> FLUID_RENDER_PROPS;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableList.Builder<IUnlistedProperty<?>> builder = ImmutableList.builder();
|
||||
builder.add(FLOW_DIRECTION);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
LEVEL_CORNERS[i] = new PropertyFloat("level_corner_" + i, 0f, 1f);
|
||||
builder.add(LEVEL_CORNERS[i]);
|
||||
|
||||
SIDE_OVERLAYS[i] = new UnlistedPropertyBool("side_overlay_" + i);
|
||||
builder.add(SIDE_OVERLAYS[i]);
|
||||
}
|
||||
FLUID_RENDER_PROPS = builder.build();
|
||||
}
|
||||
|
||||
protected int quantaPerBlock = 8;
|
||||
protected float quantaPerBlockFloat = 8F;
|
||||
protected float quantaFraction = 8f / 9f;
|
||||
protected int density = 1;
|
||||
protected int densityDir = -1;
|
||||
protected int temperature = 295;
|
||||
|
||||
protected int tickRate = 20;
|
||||
protected BlockRenderLayer renderLayer = BlockRenderLayer.TRANSLUCENT;
|
||||
protected int maxScaledLight = 0;
|
||||
|
||||
protected final String fluidName;
|
||||
|
||||
/**
|
||||
* This is the fluid used in the constructor. Use this reference to configure things
|
||||
* like icons for your block. It might not be active in the registry, so do
|
||||
* NOT expose it.
|
||||
*/
|
||||
protected final Fluid definedFluid;
|
||||
|
||||
public BlockFluidBase(Fluid fluid, Material material)
|
||||
{
|
||||
super(material);
|
||||
this.setTickRandomly(true);
|
||||
this.disableStats();
|
||||
|
||||
this.fluidName = fluid.getName();
|
||||
this.density = fluid.density;
|
||||
this.temperature = fluid.temperature;
|
||||
this.maxScaledLight = fluid.luminosity;
|
||||
this.tickRate = fluid.viscosity / 200;
|
||||
this.densityDir = fluid.density > 0 ? -1 : 1;
|
||||
fluid.setBlock(this);
|
||||
|
||||
this.definedFluid = fluid;
|
||||
displacements.putAll(defaultDisplacements);
|
||||
this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, getMaxRenderHeightMeta()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
protected BlockStateContainer createBlockState()
|
||||
{
|
||||
return new BlockStateContainer.Builder(this)
|
||||
.add(LEVEL)
|
||||
.add(FLUID_RENDER_PROPS.toArray(new IUnlistedProperty<?>[0]))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the BlockState into the correct metadata value
|
||||
*/
|
||||
@Override
|
||||
public int getMetaFromState(@Nonnull IBlockState state)
|
||||
{
|
||||
return state.getValue(LEVEL);
|
||||
}
|
||||
/**
|
||||
* Convert the given metadata into a BlockState for this Block
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public IBlockState getStateFromMeta(int meta)
|
||||
{
|
||||
return this.getDefaultState().withProperty(LEVEL, meta);
|
||||
}
|
||||
|
||||
public BlockFluidBase setQuantaPerBlock(int quantaPerBlock)
|
||||
{
|
||||
if (quantaPerBlock > 16 || quantaPerBlock < 1) quantaPerBlock = 8;
|
||||
this.quantaPerBlock = quantaPerBlock;
|
||||
this.quantaPerBlockFloat = quantaPerBlock;
|
||||
this.quantaFraction = quantaPerBlock / (quantaPerBlock + 1f);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidBase setDensity(int density)
|
||||
{
|
||||
if (density == 0) density = 1;
|
||||
this.density = density;
|
||||
this.densityDir = density > 0 ? -1 : 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidBase setTemperature(int temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidBase setTickRate(int tickRate)
|
||||
{
|
||||
if (tickRate <= 0) tickRate = 20;
|
||||
this.tickRate = tickRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidBase setRenderLayer(BlockRenderLayer renderLayer)
|
||||
{
|
||||
this.renderLayer = renderLayer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidBase setMaxScaledLight(int maxScaledLight)
|
||||
{
|
||||
this.maxScaledLight = maxScaledLight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final int getDensity()
|
||||
{
|
||||
return density;
|
||||
}
|
||||
|
||||
public final int getTemperature()
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the block at (pos) is displaceable. Does not displace the block.
|
||||
*/
|
||||
public boolean canDisplace(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block.isAir(state, world, pos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (block == this)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (displacements.containsKey(block))
|
||||
{
|
||||
return displacements.get(block);
|
||||
}
|
||||
|
||||
Material material = state.getMaterial();
|
||||
if (material.blocksMovement() || material == Material.PORTAL || material == Material.STRUCTURE_VOID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int density = getDensity(world, pos);
|
||||
if (density == Integer.MAX_VALUE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.density > density;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to displace the block at (pos), return true if it was displaced.
|
||||
*/
|
||||
public boolean displaceIfPossible(World world, BlockPos pos)
|
||||
{
|
||||
boolean canDisplace = canDisplace(world, pos);
|
||||
if (canDisplace)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (!block.isAir(state, world, pos) && !isFluid(state))
|
||||
{
|
||||
// Forge: Vanilla has a 'bug' where snowballs don't drop like every other block. So special case because ewww...
|
||||
if (block != Blocks.SNOW_LAYER) block.dropBlockAsItem(world, pos, state, 0);
|
||||
}
|
||||
}
|
||||
return canDisplace;
|
||||
}
|
||||
|
||||
public abstract int getQuantaValue(IBlockAccess world, BlockPos pos);
|
||||
|
||||
@Override
|
||||
public abstract boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit);
|
||||
|
||||
public abstract int getMaxRenderHeightMeta();
|
||||
|
||||
/* BLOCK FUNCTIONS */
|
||||
/**
|
||||
* Called after the block is set in the Chunk data, but before the Tile Entity is set
|
||||
*/
|
||||
@Override
|
||||
public void onBlockAdded(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state)
|
||||
{
|
||||
world.scheduleUpdate(pos, this, tickRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a neighboring block was changed and marks that this state should perform any checks during a neighbor
|
||||
* change. Cases may include when redstone power is updated, cactus blocks popping off due to a neighboring solid
|
||||
* block, etc.
|
||||
*/
|
||||
@Override
|
||||
public void neighborChanged(@Nonnull IBlockState state, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Block neighborBlock, @Nonnull BlockPos neighbourPos)
|
||||
{
|
||||
world.scheduleUpdate(pos, this, tickRate);
|
||||
}
|
||||
|
||||
// Used to prevent updates on chunk generation
|
||||
@Override
|
||||
public boolean requiresUpdates()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an entity can path through this block
|
||||
*/
|
||||
@Override
|
||||
public boolean isPassable(@Nonnull IBlockAccess world, @Nonnull BlockPos pos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Item that this Block should drop when harvested.
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public Item getItemDropped(@Nonnull IBlockState state, @Nonnull Random rand, int fortune)
|
||||
{
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quantity of items to drop on block destruction.
|
||||
*/
|
||||
@Override
|
||||
public int quantityDropped(@Nonnull Random par1Random)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many world ticks before ticking
|
||||
*/
|
||||
@Override
|
||||
public int tickRate(@Nonnull World world)
|
||||
{
|
||||
return tickRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Vec3d modifyAcceleration(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Entity entity, @Nonnull Vec3d vec)
|
||||
{
|
||||
return densityDir > 0 ? vec : vec.add(getFlowVector(world, pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
|
||||
{
|
||||
if (maxScaledLight == 0)
|
||||
{
|
||||
return super.getLightValue(state, world, pos);
|
||||
}
|
||||
return (int) (getQuantaPercentage(world, pos) * maxScaledLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine ambient occlusion and culling when rebuilding chunks for render
|
||||
*/
|
||||
@Override
|
||||
public boolean isOpaqueCube(@Nonnull IBlockState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube(@Nonnull IBlockState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackedLightmapCoords(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
|
||||
{
|
||||
int lightThis = world.getCombinedLight(pos, 0);
|
||||
int lightUp = world.getCombinedLight(pos.up(), 0);
|
||||
int lightThisBase = lightThis & 255;
|
||||
int lightUpBase = lightUp & 255;
|
||||
int lightThisExt = lightThis >> 16 & 255;
|
||||
int lightUpExt = lightUp >> 16 & 255;
|
||||
return (lightThisBase > lightUpBase ? lightThisBase : lightUpBase) |
|
||||
((lightThisExt > lightUpExt ? lightThisExt : lightUpExt) << 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Nonnull
|
||||
public BlockRenderLayer getBlockLayer()
|
||||
{
|
||||
return this.renderLayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the geometry of the queried face at the given position and state. This is used to decide whether things like
|
||||
* buttons are allowed to be placed on the face, or how glass panes connect to the face, among other things.
|
||||
* <p>
|
||||
* Common values are {@code SOLID}, which is the default, and {@code UNDEFINED}, which represents something that
|
||||
* does not fit the other descriptions and will generally cause other things not to connect to the face.
|
||||
*
|
||||
* @return an approximation of the form of the given face
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public BlockFaceShape getBlockFaceShape(@Nonnull IBlockAccess worldIn, @Nonnull IBlockState state, @Nonnull BlockPos pos, @Nonnull EnumFacing face)
|
||||
{
|
||||
return BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSideBeRendered(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side)
|
||||
{
|
||||
IBlockState neighbor = world.getBlockState(pos.offset(side));
|
||||
if (neighbor.getMaterial() == state.getMaterial())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (side == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return super.shouldSideBeRendered(state, world, pos, side);
|
||||
}
|
||||
|
||||
private static boolean isFluid(@Nonnull IBlockState blockstate)
|
||||
{
|
||||
return blockstate.getMaterial().isLiquid() || blockstate.getBlock() instanceof IFluidBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public IBlockState getExtendedState(@Nonnull IBlockState oldState, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
|
||||
{
|
||||
IExtendedBlockState state = (IExtendedBlockState)oldState;
|
||||
state = state.withProperty(FLOW_DIRECTION, (float)getFlowDirection(world, pos));
|
||||
IBlockState[][] upBlockState = new IBlockState[3][3];
|
||||
float[][] height = new float[3][3];
|
||||
float[][] corner = new float[2][2];
|
||||
upBlockState[1][1] = world.getBlockState(pos.down(densityDir));
|
||||
height[1][1] = getFluidHeightForRender(world, pos, upBlockState[1][1]);
|
||||
if (height[1][1] == 1)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
corner[i][j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
if (i != 1 || j != 1)
|
||||
{
|
||||
upBlockState[i][j] = world.getBlockState(pos.add(i - 1, 0, j - 1).down(densityDir));
|
||||
height[i][j] = getFluidHeightForRender(world, pos.add(i - 1, 0, j - 1), upBlockState[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
corner[i][j] = getFluidHeightAverage(height[i][j], height[i][j + 1], height[i + 1][j], height[i + 1][j + 1]);
|
||||
}
|
||||
}
|
||||
//check for downflow above corners
|
||||
boolean n = isFluid(upBlockState[0][1]);
|
||||
boolean s = isFluid(upBlockState[2][1]);
|
||||
boolean w = isFluid(upBlockState[1][0]);
|
||||
boolean e = isFluid(upBlockState[1][2]);
|
||||
boolean nw = isFluid(upBlockState[0][0]);
|
||||
boolean ne = isFluid(upBlockState[0][2]);
|
||||
boolean sw = isFluid(upBlockState[2][0]);
|
||||
boolean se = isFluid(upBlockState[2][2]);
|
||||
if (nw || n || w)
|
||||
{
|
||||
corner[0][0] = 1;
|
||||
}
|
||||
if (ne || n || e)
|
||||
{
|
||||
corner[0][1] = 1;
|
||||
}
|
||||
if (sw || s || w)
|
||||
{
|
||||
corner[1][0] = 1;
|
||||
}
|
||||
if (se || s || e)
|
||||
{
|
||||
corner[1][1] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
EnumFacing side = EnumFacing.getHorizontal(i);
|
||||
BlockPos offset = pos.offset(side);
|
||||
boolean useOverlay = world.getBlockState(offset).getBlockFaceShape(world, offset, side.getOpposite()) == BlockFaceShape.SOLID;
|
||||
state = state.withProperty(SIDE_OVERLAYS[i], useOverlay);
|
||||
}
|
||||
|
||||
state = state.withProperty(LEVEL_CORNERS[0], corner[0][0]);
|
||||
state = state.withProperty(LEVEL_CORNERS[1], corner[0][1]);
|
||||
state = state.withProperty(LEVEL_CORNERS[2], corner[1][1]);
|
||||
state = state.withProperty(LEVEL_CORNERS[3], corner[1][0]);
|
||||
return state;
|
||||
}
|
||||
|
||||
/* FLUID FUNCTIONS */
|
||||
public static int getDensity(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block instanceof BlockFluidBase)
|
||||
{
|
||||
return ((BlockFluidBase)block).getDensity();
|
||||
}
|
||||
|
||||
Fluid fluid = getFluid(state);
|
||||
if (fluid != null)
|
||||
{
|
||||
return fluid.getDensity();
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public static int getTemperature(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block instanceof BlockFluidBase)
|
||||
{
|
||||
return ((BlockFluidBase)block).getTemperature();
|
||||
}
|
||||
|
||||
Fluid fluid = getFluid(state);
|
||||
if (fluid != null)
|
||||
{
|
||||
return fluid.getTemperature();
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Fluid getFluid(IBlockState state)
|
||||
{
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block instanceof IFluidBlock)
|
||||
{
|
||||
return ((IFluidBlock)block).getFluid();
|
||||
}
|
||||
if (block instanceof BlockLiquid)
|
||||
{
|
||||
if (state.getMaterial() == Material.WATER)
|
||||
{
|
||||
return FluidRegistry.WATER;
|
||||
}
|
||||
if (state.getMaterial() == Material.LAVA)
|
||||
{
|
||||
return FluidRegistry.LAVA;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static double getFlowDirection(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
if (!state.getMaterial().isLiquid())
|
||||
{
|
||||
return -1000.0;
|
||||
}
|
||||
Vec3d vec = ((BlockFluidBase)state.getBlock()).getFlowVector(world, pos);
|
||||
return vec.x == 0.0D && vec.z == 0.0D ? -1000.0D : MathHelper.atan2(vec.z, vec.x) - Math.PI / 2D;
|
||||
}
|
||||
|
||||
public final int getQuantaValueBelow(IBlockAccess world, BlockPos pos, int belowThis)
|
||||
{
|
||||
int quantaRemaining = getQuantaValue(world, pos);
|
||||
if (quantaRemaining >= belowThis)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return quantaRemaining;
|
||||
}
|
||||
|
||||
public final int getQuantaValueAbove(IBlockAccess world, BlockPos pos, int aboveThis)
|
||||
{
|
||||
int quantaRemaining = getQuantaValue(world, pos);
|
||||
if (quantaRemaining <= aboveThis)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return quantaRemaining;
|
||||
}
|
||||
|
||||
public final float getQuantaPercentage(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
int quantaRemaining = getQuantaValue(world, pos);
|
||||
return quantaRemaining / quantaPerBlockFloat;
|
||||
}
|
||||
|
||||
public float getFluidHeightAverage(float... flow)
|
||||
{
|
||||
float total = 0;
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < flow.length; i++)
|
||||
{
|
||||
if (flow[i] >= quantaFraction)
|
||||
{
|
||||
total += flow[i] * 10;
|
||||
count += 10;
|
||||
}
|
||||
|
||||
if (flow[i] >= 0)
|
||||
{
|
||||
total += flow[i];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return total / count;
|
||||
}
|
||||
|
||||
public float getFluidHeightForRender(IBlockAccess world, BlockPos pos, @Nonnull IBlockState up)
|
||||
{
|
||||
IBlockState here = world.getBlockState(pos);
|
||||
if (here.getBlock() == this)
|
||||
{
|
||||
if (isFluid(up))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getMetaFromState(here) == getMaxRenderHeightMeta())
|
||||
{
|
||||
return quantaFraction;
|
||||
}
|
||||
}
|
||||
if (here.getBlock() instanceof BlockLiquid)
|
||||
{
|
||||
return Math.min(1 - BlockLiquid.getLiquidHeightPercent(here.getValue(BlockLiquid.LEVEL)), quantaFraction);
|
||||
}
|
||||
return !here.getMaterial().isSolid() && up.getBlock() == this ? 1 : this.getQuantaPercentage(world, pos) * quantaFraction;
|
||||
}
|
||||
|
||||
public Vec3d getFlowVector(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
Vec3d vec = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||
int decay = getFlowDecay(world, pos);
|
||||
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
BlockPos offset = pos.offset(side);
|
||||
int otherDecay = getFlowDecay(world, offset);
|
||||
if (otherDecay >= quantaPerBlock)
|
||||
{
|
||||
if (!world.getBlockState(offset).getMaterial().blocksMovement())
|
||||
{
|
||||
otherDecay = getFlowDecay(world, offset.up(densityDir));
|
||||
if (otherDecay < quantaPerBlock)
|
||||
{
|
||||
int power = otherDecay - (decay - quantaPerBlock);
|
||||
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int power = otherDecay - decay;
|
||||
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasVerticalFlow(world, pos))
|
||||
{
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
BlockPos offset = pos.offset(side);
|
||||
if (causesDownwardCurrent(world, offset, side) || causesDownwardCurrent(world, offset.down(densityDir), side))
|
||||
{
|
||||
vec = vec.normalize().addVector(0.0, 6.0 * densityDir, 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vec.normalize();
|
||||
}
|
||||
|
||||
private int getFlowDecay(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
int quantaValue = getQuantaValue(world, pos);
|
||||
return quantaValue > 0 && hasVerticalFlow(world, pos) ? 0 : quantaPerBlock - quantaValue;
|
||||
}
|
||||
|
||||
private boolean hasVerticalFlow(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
return world.getBlockState(pos.down(densityDir)).getBlock() == this;
|
||||
}
|
||||
|
||||
protected boolean causesDownwardCurrent(IBlockAccess world, BlockPos pos, EnumFacing face)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block == this) return false;
|
||||
if (face == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN)) return true;
|
||||
if (state.getMaterial() == Material.ICE) return false;
|
||||
|
||||
boolean flag = isExceptBlockForAttachWithPiston(block) || block instanceof BlockStairs;
|
||||
return !flag && state.getBlockFaceShape(world, pos, face) == BlockFaceShape.SOLID;
|
||||
}
|
||||
|
||||
/* IFluidBlock */
|
||||
@Override
|
||||
public Fluid getFluid()
|
||||
{
|
||||
return FluidRegistry.getFluid(fluidName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFilledPercentage(World world, BlockPos pos)
|
||||
{
|
||||
return getFilledPercentage((IBlockAccess) world, pos);
|
||||
}
|
||||
|
||||
public float getFilledPercentage(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
int quantaRemaining = getQuantaValue(world, pos);
|
||||
float remaining = (quantaRemaining + 1f) / (quantaPerBlockFloat + 1f);
|
||||
return remaining * (density > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getCollisionBoundingBox(@Nonnull IBlockState blockState, @Nonnull IBlockAccess worldIn, @Nonnull BlockPos pos)
|
||||
{
|
||||
return NULL_AABB;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly (Side.CLIENT)
|
||||
public Vec3d getFogColor(World world, BlockPos pos, IBlockState state, Entity entity, Vec3d originalColor, float partialTicks)
|
||||
{
|
||||
if (!isWithinFluid(world, pos, ActiveRenderInfo.projectViewFromEntity(entity, partialTicks)))
|
||||
{
|
||||
BlockPos otherPos = pos.down(densityDir);
|
||||
IBlockState otherState = world.getBlockState(otherPos);
|
||||
return otherState.getBlock().getFogColor(world, otherPos, otherState, entity, originalColor, partialTicks);
|
||||
}
|
||||
|
||||
if (getFluid() != null)
|
||||
{
|
||||
int color = getFluid().getColor();
|
||||
float red = (color >> 16 & 0xFF) / 255.0F;
|
||||
float green = (color >> 8 & 0xFF) / 255.0F;
|
||||
float blue = (color & 0xFF) / 255.0F;
|
||||
return new Vec3d(red, green, blue);
|
||||
}
|
||||
|
||||
return super.getFogColor(world, pos, state, entity, originalColor, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateAtViewpoint(IBlockState state, IBlockAccess world, BlockPos pos, Vec3d viewpoint)
|
||||
{
|
||||
if (!isWithinFluid(world, pos, viewpoint))
|
||||
{
|
||||
return world.getBlockState(pos.down(densityDir));
|
||||
}
|
||||
|
||||
return super.getStateAtViewpoint(state, world, pos, viewpoint);
|
||||
}
|
||||
|
||||
private boolean isWithinFluid(IBlockAccess world, BlockPos pos, Vec3d vec)
|
||||
{
|
||||
float filled = getFilledPercentage(world, pos);
|
||||
return filled < 0 ? vec.y > pos.getY() + filled + 1
|
||||
: vec.y < pos.getY() + filled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This is a fluid block implementation which emulates vanilla Minecraft fluid behavior.
|
||||
*
|
||||
* It is highly recommended that you use/extend this class for "classic" fluid blocks.
|
||||
*
|
||||
*/
|
||||
public class BlockFluidClassic extends BlockFluidBase
|
||||
{
|
||||
protected static final List<EnumFacing> SIDES = Collections.unmodifiableList(Arrays.asList(
|
||||
EnumFacing.WEST, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.SOUTH));
|
||||
|
||||
protected boolean[] isOptimalFlowDirection = new boolean[4];
|
||||
protected int[] flowCost = new int[4];
|
||||
|
||||
protected boolean canCreateSources = false;
|
||||
|
||||
protected FluidStack stack;
|
||||
|
||||
public BlockFluidClassic(Fluid fluid, Material material)
|
||||
{
|
||||
super(fluid, material);
|
||||
stack = new FluidStack(fluid, Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
|
||||
public BlockFluidClassic setFluidStack(FluidStack stack)
|
||||
{
|
||||
this.stack = stack;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockFluidClassic setFluidStackAmount(int amount)
|
||||
{
|
||||
this.stack.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQuantaValue(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
if (state.getBlock().isAir(state, world, pos))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state.getBlock() != this)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return quantaPerBlock - state.getValue(LEVEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit)
|
||||
{
|
||||
return fullHit && state.getValue(LEVEL) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxRenderHeightMeta()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Random rand)
|
||||
{
|
||||
int quantaRemaining = quantaPerBlock - state.getValue(LEVEL);
|
||||
int expQuanta = -101;
|
||||
|
||||
// check adjacent block levels if non-source
|
||||
if (quantaRemaining < quantaPerBlock)
|
||||
{
|
||||
int adjacentSourceBlocks = 0;
|
||||
|
||||
if (ForgeEventFactory.canCreateFluidSource(world, pos, state, canCreateSources))
|
||||
{
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
if (isSourceBlock(world, pos.offset(side))) adjacentSourceBlocks++;
|
||||
}
|
||||
}
|
||||
|
||||
// new source block
|
||||
if (adjacentSourceBlocks >= 2 && (world.getBlockState(pos.up(densityDir)).getMaterial().isSolid() || isSourceBlock(world, pos.up(densityDir))))
|
||||
{
|
||||
expQuanta = quantaPerBlock;
|
||||
}
|
||||
// unobstructed flow from 'above'
|
||||
else if (world.getBlockState(pos.down(densityDir)).getBlock() == this
|
||||
|| hasDownhillFlow(world, pos, EnumFacing.EAST)
|
||||
|| hasDownhillFlow(world, pos, EnumFacing.WEST)
|
||||
|| hasDownhillFlow(world, pos, EnumFacing.NORTH)
|
||||
|| hasDownhillFlow(world, pos, EnumFacing.SOUTH))
|
||||
{
|
||||
expQuanta = quantaPerBlock - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int maxQuanta = -100;
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
maxQuanta = getLargerQuanta(world, pos.offset(side), maxQuanta);
|
||||
}
|
||||
expQuanta = maxQuanta - 1;
|
||||
}
|
||||
|
||||
// decay calculation
|
||||
if (expQuanta != quantaRemaining)
|
||||
{
|
||||
quantaRemaining = expQuanta;
|
||||
|
||||
if (expQuanta <= 0)
|
||||
{
|
||||
world.setBlockToAir(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
world.setBlockState(pos, state.withProperty(LEVEL, quantaPerBlock - expQuanta), 2);
|
||||
world.scheduleUpdate(pos, this, tickRate);
|
||||
world.notifyNeighborsOfStateChange(pos, this, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flow vertically if possible
|
||||
if (canDisplace(world, pos.up(densityDir)))
|
||||
{
|
||||
flowIntoBlock(world, pos.up(densityDir), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Flow outward if possible
|
||||
int flowMeta = quantaPerBlock - quantaRemaining + 1;
|
||||
if (flowMeta >= quantaPerBlock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSourceBlock(world, pos) || !isFlowingVertically(world, pos))
|
||||
{
|
||||
if (world.getBlockState(pos.down(densityDir)).getBlock() == this)
|
||||
{
|
||||
flowMeta = 1;
|
||||
}
|
||||
boolean flowTo[] = getOptimalFlowDirections(world, pos);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (flowTo[i]) flowIntoBlock(world, pos.offset(SIDES.get(i)), flowMeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final boolean hasDownhillFlow(IBlockAccess world, BlockPos pos, EnumFacing direction)
|
||||
{
|
||||
return world.getBlockState(pos.offset(direction).down(densityDir)).getBlock() == this
|
||||
&& (canFlowInto(world, pos.offset(direction))
|
||||
|| canFlowInto(world, pos.down(densityDir)));
|
||||
}
|
||||
|
||||
public boolean isFlowingVertically(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
return world.getBlockState(pos.up(densityDir)).getBlock() == this ||
|
||||
(world.getBlockState(pos).getBlock() == this && canFlowInto(world, pos.up(densityDir)));
|
||||
}
|
||||
|
||||
public boolean isSourceBlock(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
return state.getBlock() == this && state.getValue(LEVEL) == 0;
|
||||
}
|
||||
|
||||
protected boolean[] getOptimalFlowDirections(World world, BlockPos pos)
|
||||
{
|
||||
for (int side = 0; side < 4; side++)
|
||||
{
|
||||
flowCost[side] = 1000;
|
||||
|
||||
BlockPos pos2 = pos.offset(SIDES.get(side));
|
||||
|
||||
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canFlowInto(world, pos2.up(densityDir)))
|
||||
{
|
||||
flowCost[side] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flowCost[side] = calculateFlowCost(world, pos2, 1, side);
|
||||
}
|
||||
}
|
||||
|
||||
int min = Ints.min(flowCost);
|
||||
for (int side = 0; side < 4; side++)
|
||||
{
|
||||
isOptimalFlowDirection[side] = flowCost[side] == min;
|
||||
}
|
||||
return isOptimalFlowDirection;
|
||||
}
|
||||
|
||||
protected int calculateFlowCost(World world, BlockPos pos, int recurseDepth, int side)
|
||||
{
|
||||
int cost = 1000;
|
||||
for (int adjSide = 0; adjSide < 4; adjSide++)
|
||||
{
|
||||
if (SIDES.get(adjSide) == SIDES.get(side).getOpposite())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockPos pos2 = pos.offset(SIDES.get(adjSide));
|
||||
|
||||
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canFlowInto(world, pos2.up(densityDir)))
|
||||
{
|
||||
return recurseDepth;
|
||||
}
|
||||
|
||||
if (recurseDepth >= quantaPerBlock / 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cost = Math.min(cost, calculateFlowCost(world, pos2, recurseDepth + 1, adjSide));
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
protected void flowIntoBlock(World world, BlockPos pos, int meta)
|
||||
{
|
||||
if (meta < 0) return;
|
||||
if (displaceIfPossible(world, pos))
|
||||
{
|
||||
world.setBlockState(pos, this.getDefaultState().withProperty(LEVEL, meta), 3);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean canFlowInto(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
return world.getBlockState(pos).getBlock() == this || canDisplace(world, pos);
|
||||
}
|
||||
|
||||
protected int getLargerQuanta(IBlockAccess world, BlockPos pos, int compare)
|
||||
{
|
||||
int quantaRemaining = getQuantaValue(world, pos);
|
||||
if (quantaRemaining <= 0)
|
||||
{
|
||||
return compare;
|
||||
}
|
||||
return quantaRemaining >= compare ? quantaRemaining : compare;
|
||||
}
|
||||
|
||||
/* IFluidBlock */
|
||||
@Override
|
||||
public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace)
|
||||
{
|
||||
if (fluidStack.amount < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (doPlace)
|
||||
{
|
||||
FluidUtil.destroyBlockOnFluidPlacement(world, pos);
|
||||
world.setBlockState(pos, this.getDefaultState(), 11);
|
||||
}
|
||||
return Fluid.BUCKET_VOLUME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack drain(World world, BlockPos pos, boolean doDrain)
|
||||
{
|
||||
if (!isSourceBlock(world, pos))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (doDrain)
|
||||
{
|
||||
world.setBlockToAir(pos);
|
||||
}
|
||||
|
||||
return stack.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrain(World world, BlockPos pos)
|
||||
{
|
||||
return isSourceBlock(world, pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* This is a cellular-automata based finite fluid block implementation.
|
||||
*
|
||||
* It is highly recommended that you use/extend this class for finite fluid blocks.
|
||||
*
|
||||
*/
|
||||
public class BlockFluidFinite extends BlockFluidBase
|
||||
{
|
||||
public BlockFluidFinite(Fluid fluid, Material material)
|
||||
{
|
||||
super(fluid, material);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQuantaValue(IBlockAccess world, BlockPos pos)
|
||||
{
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
if (state.getBlock().isAir(state, world, pos))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state.getBlock() != this)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return state.getValue(LEVEL) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit)
|
||||
{
|
||||
return fullHit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxRenderHeightMeta()
|
||||
{
|
||||
return quantaPerBlock - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Random rand)
|
||||
{
|
||||
boolean changed = false;
|
||||
int quantaRemaining = state.getValue(LEVEL) + 1;
|
||||
|
||||
// Flow vertically if possible
|
||||
int prevRemaining = quantaRemaining;
|
||||
quantaRemaining = tryToFlowVerticallyInto(world, pos, quantaRemaining);
|
||||
|
||||
if (quantaRemaining < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (quantaRemaining != prevRemaining)
|
||||
{
|
||||
changed = true;
|
||||
if (quantaRemaining == 1)
|
||||
{
|
||||
world.setBlockState(pos, state.withProperty(LEVEL, quantaRemaining - 1), 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (quantaRemaining == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Flow out if possible
|
||||
int lowerThan = quantaRemaining - 1;
|
||||
int total = quantaRemaining;
|
||||
int count = 1;
|
||||
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
BlockPos off = pos.offset(side);
|
||||
if (displaceIfPossible(world, off))
|
||||
world.setBlockToAir(off);
|
||||
|
||||
int quanta = getQuantaValueBelow(world, off, lowerThan);
|
||||
if (quanta >= 0)
|
||||
{
|
||||
count++;
|
||||
total += quanta;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
if (changed)
|
||||
{
|
||||
world.setBlockState(pos, state.withProperty(LEVEL, quantaRemaining - 1), 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int each = total / count;
|
||||
int rem = total % count;
|
||||
|
||||
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
|
||||
{
|
||||
BlockPos off = pos.offset(side);
|
||||
int quanta = getQuantaValueBelow(world, off, lowerThan);
|
||||
if (quanta >= 0)
|
||||
{
|
||||
int newQuanta = each;
|
||||
if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0)
|
||||
{
|
||||
++newQuanta;
|
||||
--rem;
|
||||
}
|
||||
|
||||
if (newQuanta != quanta)
|
||||
{
|
||||
if (newQuanta == 0)
|
||||
{
|
||||
world.setBlockToAir(off);
|
||||
}
|
||||
else
|
||||
{
|
||||
world.setBlockState(off, getDefaultState().withProperty(LEVEL, newQuanta - 1), 2);
|
||||
}
|
||||
world.scheduleUpdate(off, this, tickRate);
|
||||
}
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
if (rem > 0)
|
||||
{
|
||||
++each;
|
||||
}
|
||||
world.setBlockState(pos, state.withProperty(LEVEL, each - 1), 2);
|
||||
}
|
||||
|
||||
public int tryToFlowVerticallyInto(World world, BlockPos pos, int amtToInput)
|
||||
{
|
||||
IBlockState myState = world.getBlockState(pos);
|
||||
BlockPos other = pos.add(0, densityDir, 0);
|
||||
if (other.getY() < 0 || other.getY() >= world.getHeight())
|
||||
{
|
||||
world.setBlockToAir(pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amt = getQuantaValueBelow(world, other, quantaPerBlock);
|
||||
if (amt >= 0)
|
||||
{
|
||||
amt += amtToInput;
|
||||
if (amt > quantaPerBlock)
|
||||
{
|
||||
world.setBlockState(other, myState.withProperty(LEVEL, quantaPerBlock - 1), 3);
|
||||
world.scheduleUpdate(other, this, tickRate);
|
||||
return amt - quantaPerBlock;
|
||||
}
|
||||
else if (amt > 0)
|
||||
{
|
||||
world.setBlockState(other, myState.withProperty(LEVEL, amt - 1), 3);
|
||||
world.scheduleUpdate(other, this, tickRate);
|
||||
world.setBlockToAir(pos);
|
||||
return 0;
|
||||
}
|
||||
return amtToInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
int density_other = getDensity(world, other);
|
||||
if (density_other == Integer.MAX_VALUE)
|
||||
{
|
||||
if (displaceIfPossible(world, other))
|
||||
{
|
||||
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
|
||||
world.scheduleUpdate(other, this, tickRate);
|
||||
world.setBlockToAir(pos);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return amtToInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (densityDir < 0)
|
||||
{
|
||||
if (density_other < density) // then swap
|
||||
{
|
||||
IBlockState state = world.getBlockState(other);
|
||||
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
|
||||
world.setBlockState(pos, state, 3);
|
||||
world.scheduleUpdate(other, this, tickRate);
|
||||
world.scheduleUpdate(pos, state.getBlock(), state.getBlock().tickRate(world));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (density_other > density)
|
||||
{
|
||||
IBlockState state = world.getBlockState(other);
|
||||
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
|
||||
world.setBlockState(pos, state, 3);
|
||||
world.scheduleUpdate(other, this, tickRate);
|
||||
world.scheduleUpdate(pos, state.getBlock(), state.getBlock().tickRate(world));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return amtToInput;
|
||||
}
|
||||
}
|
||||
|
||||
/* IFluidBlock */
|
||||
@Override
|
||||
public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace)
|
||||
{
|
||||
IBlockState existing = world.getBlockState(pos);
|
||||
float quantaAmount = Fluid.BUCKET_VOLUME / quantaPerBlockFloat;
|
||||
// If the stack contains more available fluid than the full source block,
|
||||
// set a source block
|
||||
int closest = Fluid.BUCKET_VOLUME;
|
||||
int quanta = quantaPerBlock;
|
||||
if (fluidStack.amount < closest)
|
||||
{
|
||||
// Figure out maximum level to match stack amount
|
||||
closest = MathHelper.floor(quantaAmount * MathHelper.floor(fluidStack.amount / quantaAmount));
|
||||
quanta = MathHelper.floor(closest / quantaAmount);
|
||||
}
|
||||
if (existing.getBlock() == this)
|
||||
{
|
||||
int existingQuanta = existing.getValue(LEVEL) + 1;
|
||||
int missingQuanta = quantaPerBlock - existingQuanta;
|
||||
closest = Math.min(closest, MathHelper.floor(missingQuanta * quantaAmount));
|
||||
quanta = Math.min(quanta + existingQuanta, quantaPerBlock);
|
||||
}
|
||||
|
||||
// If too little (or too much, technically impossible) fluid is to be placed, abort
|
||||
if (quanta < 1 || quanta > 16)
|
||||
return 0;
|
||||
|
||||
if (doPlace)
|
||||
{
|
||||
FluidUtil.destroyBlockOnFluidPlacement(world, pos);
|
||||
world.setBlockState(pos, getDefaultState().withProperty(LEVEL, quanta - 1), 11);
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(World world, BlockPos pos, boolean doDrain)
|
||||
{
|
||||
final FluidStack fluidStack = new FluidStack(getFluid(), MathHelper.floor(getQuantaPercentage(world, pos) * Fluid.BUCKET_VOLUME));
|
||||
|
||||
if (doDrain)
|
||||
{
|
||||
world.setBlockToAir(pos);
|
||||
}
|
||||
|
||||
return fluidStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrain(World world, BlockPos pos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.block.BlockDispenser;
|
||||
import net.minecraft.dispenser.BehaviorDefaultDispenseItem;
|
||||
import net.minecraft.dispenser.IBlockSource;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
|
||||
/**
|
||||
* Fills or drains a fluid container item using a Dispenser.
|
||||
*/
|
||||
public class DispenseFluidContainer extends BehaviorDefaultDispenseItem
|
||||
{
|
||||
private static final DispenseFluidContainer INSTANCE = new DispenseFluidContainer();
|
||||
|
||||
public static DispenseFluidContainer getInstance()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private DispenseFluidContainer() {}
|
||||
|
||||
private final BehaviorDefaultDispenseItem dispenseBehavior = new BehaviorDefaultDispenseItem();
|
||||
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack dispenseStack(@Nonnull IBlockSource source, @Nonnull ItemStack stack)
|
||||
{
|
||||
if (FluidUtil.getFluidContained(stack) != null)
|
||||
{
|
||||
return dumpContainer(source, stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fillContainer(source, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks up fluid in front of a Dispenser and fills a container with it.
|
||||
*/
|
||||
@Nonnull
|
||||
private ItemStack fillContainer(@Nonnull IBlockSource source, @Nonnull ItemStack stack)
|
||||
{
|
||||
World world = source.getWorld();
|
||||
EnumFacing dispenserFacing = source.getBlockState().getValue(BlockDispenser.FACING);
|
||||
BlockPos blockpos = source.getBlockPos().offset(dispenserFacing);
|
||||
|
||||
FluidActionResult actionResult = FluidUtil.tryPickUpFluid(stack, null, world, blockpos, dispenserFacing.getOpposite());
|
||||
ItemStack resultStack = actionResult.getResult();
|
||||
|
||||
if (!actionResult.isSuccess() || resultStack.isEmpty())
|
||||
{
|
||||
return super.dispenseStack(source, stack);
|
||||
}
|
||||
|
||||
if (stack.getCount() == 1)
|
||||
{
|
||||
return resultStack;
|
||||
}
|
||||
else if (((TileEntityDispenser)source.getBlockTileEntity()).addItemStack(resultStack) < 0)
|
||||
{
|
||||
this.dispenseBehavior.dispense(source, resultStack);
|
||||
}
|
||||
|
||||
ItemStack stackCopy = stack.copy();
|
||||
stackCopy.shrink(1);
|
||||
return stackCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drains a filled container and places the fluid in front of the Dispenser.
|
||||
*/
|
||||
@Nonnull
|
||||
private ItemStack dumpContainer(IBlockSource source, @Nonnull ItemStack stack)
|
||||
{
|
||||
ItemStack singleStack = stack.copy();
|
||||
singleStack.setCount(1);
|
||||
IFluidHandlerItem fluidHandler = FluidUtil.getFluidHandler(singleStack);
|
||||
if (fluidHandler == null)
|
||||
{
|
||||
return super.dispenseStack(source, stack);
|
||||
}
|
||||
|
||||
FluidStack fluidStack = fluidHandler.drain(Fluid.BUCKET_VOLUME, false);
|
||||
EnumFacing dispenserFacing = source.getBlockState().getValue(BlockDispenser.FACING);
|
||||
BlockPos blockpos = source.getBlockPos().offset(dispenserFacing);
|
||||
FluidActionResult result = fluidStack != null ? FluidUtil.tryPlaceFluid(null, source.getWorld(), blockpos, stack, fluidStack) : FluidActionResult.FAILURE;
|
||||
|
||||
if (result.isSuccess())
|
||||
{
|
||||
ItemStack drainedStack = result.getResult();
|
||||
|
||||
if (drainedStack.getCount() == 1)
|
||||
{
|
||||
return drainedStack;
|
||||
}
|
||||
else if (!drainedStack.isEmpty() && ((TileEntityDispenser)source.getBlockTileEntity()).addItemStack(drainedStack) < 0)
|
||||
{
|
||||
this.dispenseBehavior.dispense(source, drainedStack);
|
||||
}
|
||||
|
||||
ItemStack stackCopy = drainedStack.copy();
|
||||
stackCopy.shrink(1);
|
||||
return stackCopy;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.dispenseBehavior.dispense(source, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Locale;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.translation.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
|
||||
/**
|
||||
* Minecraft Forge Fluid Implementation
|
||||
*
|
||||
* This class is a fluid (liquid or gas) equivalent to "Item." It describes the nature of a fluid
|
||||
* and contains its general properties.
|
||||
*
|
||||
* These properties do not have inherent gameplay mechanics - they are provided so that mods may
|
||||
* choose to take advantage of them.
|
||||
*
|
||||
* Fluid implementations are not required to actively use these properties, nor are objects
|
||||
* interfacing with fluids required to make use of them, but it is encouraged.
|
||||
*
|
||||
* The default values can be used as a reference point for mods adding fluids such as oil or heavy
|
||||
* water.
|
||||
*
|
||||
*/
|
||||
public class Fluid
|
||||
{
|
||||
public static final int BUCKET_VOLUME = 1000;
|
||||
|
||||
/** The unique identification name for this fluid. */
|
||||
protected final String fluidName;
|
||||
|
||||
/** The unlocalized name of this fluid. */
|
||||
protected String unlocalizedName;
|
||||
|
||||
protected final ResourceLocation still;
|
||||
protected final ResourceLocation flowing;
|
||||
|
||||
@Nullable
|
||||
protected final ResourceLocation overlay;
|
||||
|
||||
private SoundEvent fillSound;
|
||||
private SoundEvent emptySound;
|
||||
|
||||
/**
|
||||
* The light level emitted by this fluid.
|
||||
*
|
||||
* Default value is 0, as most fluids do not actively emit light.
|
||||
*/
|
||||
protected int luminosity = 0;
|
||||
|
||||
/**
|
||||
* Density of the fluid - completely arbitrary; negative density indicates that the fluid is
|
||||
* lighter than air.
|
||||
*
|
||||
* Default value is approximately the real-life density of water in kg/m^3.
|
||||
*/
|
||||
protected int density = 1000;
|
||||
|
||||
/**
|
||||
* Temperature of the fluid - completely arbitrary; higher temperature indicates that the fluid is
|
||||
* hotter than air.
|
||||
*
|
||||
* Default value is approximately the real-life room temperature of water in degrees Kelvin.
|
||||
*/
|
||||
protected int temperature = 300;
|
||||
|
||||
/**
|
||||
* Viscosity ("thickness") of the fluid - completely arbitrary; negative values are not
|
||||
* permissible.
|
||||
*
|
||||
* Default value is approximately the real-life density of water in m/s^2 (x10^-3).
|
||||
*
|
||||
* Higher viscosity means that a fluid flows more slowly, like molasses.
|
||||
* Lower viscosity means that a fluid flows more quickly, like helium.
|
||||
*
|
||||
*/
|
||||
protected int viscosity = 1000;
|
||||
|
||||
/**
|
||||
* This indicates if the fluid is gaseous.
|
||||
*
|
||||
* Generally this is associated with negative density fluids.
|
||||
*/
|
||||
protected boolean isGaseous;
|
||||
|
||||
/**
|
||||
* The rarity of the fluid.
|
||||
*
|
||||
* Used primarily in tool tips.
|
||||
*/
|
||||
protected EnumRarity rarity = EnumRarity.COMMON;
|
||||
|
||||
/**
|
||||
* If there is a Block implementation of the Fluid, the Block is linked here.
|
||||
*
|
||||
* The default value of null should remain for any Fluid without a Block implementation.
|
||||
*/
|
||||
protected Block block = null;
|
||||
|
||||
/**
|
||||
* Color used by universal bucket and the ModelFluid baked model.
|
||||
* Note that this int includes the alpha so converting this to RGB with alpha would be
|
||||
* float r = ((color >> 16) & 0xFF) / 255f; // red
|
||||
* float g = ((color >> 8) & 0xFF) / 255f; // green
|
||||
* float b = ((color >> 0) & 0xFF) / 255f; // blue
|
||||
* float a = ((color >> 24) & 0xFF) / 255f; // alpha
|
||||
*/
|
||||
protected int color = 0xFFFFFFFF;
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, Color color)
|
||||
{
|
||||
this(fluidName, still, flowing, null, color);
|
||||
}
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay, Color color)
|
||||
{
|
||||
this(fluidName, still, flowing, overlay);
|
||||
this.setColor(color);
|
||||
}
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, int color)
|
||||
{
|
||||
this(fluidName, still, flowing, null, color);
|
||||
}
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay, int color)
|
||||
{
|
||||
this(fluidName, still, flowing, overlay);
|
||||
this.setColor(color);
|
||||
}
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing)
|
||||
{
|
||||
this(fluidName, still, flowing, (ResourceLocation) null);
|
||||
}
|
||||
|
||||
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay)
|
||||
{
|
||||
this.fluidName = fluidName.toLowerCase(Locale.ENGLISH);
|
||||
this.unlocalizedName = fluidName;
|
||||
this.still = still;
|
||||
this.flowing = flowing;
|
||||
this.overlay = overlay;
|
||||
}
|
||||
|
||||
public Fluid setUnlocalizedName(String unlocalizedName)
|
||||
{
|
||||
this.unlocalizedName = unlocalizedName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setBlock(Block block)
|
||||
{
|
||||
if (this.block == null || this.block == block)
|
||||
{
|
||||
this.block = block;
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.log.warn("A mod has attempted to assign Block {} to the Fluid '{}' but this Fluid has already been linked to the Block {}. "
|
||||
+ "You may have duplicate Fluid Blocks as a result. It *may* be possible to configure your mods to avoid this.", block, fluidName, this.block);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setLuminosity(int luminosity)
|
||||
{
|
||||
this.luminosity = luminosity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setDensity(int density)
|
||||
{
|
||||
this.density = density;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setTemperature(int temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setViscosity(int viscosity)
|
||||
{
|
||||
this.viscosity = viscosity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setGaseous(boolean isGaseous)
|
||||
{
|
||||
this.isGaseous = isGaseous;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setRarity(EnumRarity rarity)
|
||||
{
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setFillSound(SoundEvent fillSound)
|
||||
{
|
||||
this.fillSound = fillSound;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setEmptySound(SoundEvent emptySound)
|
||||
{
|
||||
this.emptySound = emptySound;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setColor(Color color)
|
||||
{
|
||||
this.color = color.getRGB();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fluid setColor(int color)
|
||||
{
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final String getName()
|
||||
{
|
||||
return this.fluidName;
|
||||
}
|
||||
|
||||
public final Block getBlock()
|
||||
{
|
||||
return block;
|
||||
}
|
||||
|
||||
public final boolean canBePlacedInWorld()
|
||||
{
|
||||
return block != null;
|
||||
}
|
||||
|
||||
public final boolean isLighterThanAir()
|
||||
{
|
||||
int density = this.density;
|
||||
if (block instanceof BlockFluidBase)
|
||||
{
|
||||
density = ((BlockFluidBase) block).getDensity();
|
||||
}
|
||||
return density <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this fluid should vaporize in dimensions where water vaporizes when placed.
|
||||
* To preserve the intentions of vanilla, fluids that can turn lava into obsidian should vaporize.
|
||||
* This prevents players from making the nether safe with a single bucket.
|
||||
* Based on {@link net.minecraft.item.ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)}
|
||||
*
|
||||
* @param fluidStack The fluidStack is trying to be placed.
|
||||
* @return true if this fluid should vaporize in dimensions where water vaporizes when placed.
|
||||
*/
|
||||
public boolean doesVaporize(FluidStack fluidStack)
|
||||
{
|
||||
if (block == null)
|
||||
return false;
|
||||
return block.getDefaultState().getMaterial() == Material.WATER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called instead of placing the fluid block if {@link WorldProvider#doesWaterVaporize()} and {@link #doesVaporize(FluidStack)} are true.
|
||||
* Override this to make your explosive liquid blow up instead of the default smoke, etc.
|
||||
* Based on {@link net.minecraft.item.ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)}
|
||||
*
|
||||
* @param player Player who tried to place the fluid. May be null for blocks like dispensers.
|
||||
* @param worldIn World to vaporize the fluid in.
|
||||
* @param pos The position in the world the fluid block was going to be placed.
|
||||
* @param fluidStack The fluidStack that was going to be placed.
|
||||
*/
|
||||
public void vaporize(@Nullable EntityPlayer player, World worldIn, BlockPos pos, FluidStack fluidStack)
|
||||
{
|
||||
worldIn.playSound(player, pos, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (worldIn.rand.nextFloat() - worldIn.rand.nextFloat()) * 0.8F);
|
||||
|
||||
for (int l = 0; l < 8; ++l)
|
||||
{
|
||||
worldIn.spawnParticle(EnumParticleTypes.SMOKE_LARGE, (double) pos.getX() + Math.random(), (double) pos.getY() + Math.random(), (double) pos.getZ() + Math.random(), 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized name of this fluid.
|
||||
*/
|
||||
public String getLocalizedName(FluidStack stack)
|
||||
{
|
||||
String s = this.getUnlocalizedName();
|
||||
return s == null ? "" : I18n.translateToLocal(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* A FluidStack sensitive version of getUnlocalizedName
|
||||
*/
|
||||
public String getUnlocalizedName(FluidStack stack)
|
||||
{
|
||||
return this.getUnlocalizedName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unlocalized name of this fluid.
|
||||
*/
|
||||
public String getUnlocalizedName()
|
||||
{
|
||||
return "fluid." + this.unlocalizedName;
|
||||
}
|
||||
|
||||
/* Default Accessors */
|
||||
public final int getLuminosity()
|
||||
{
|
||||
return this.luminosity;
|
||||
}
|
||||
|
||||
public final int getDensity()
|
||||
{
|
||||
return this.density;
|
||||
}
|
||||
|
||||
public final int getTemperature()
|
||||
{
|
||||
return this.temperature;
|
||||
}
|
||||
|
||||
public final int getViscosity()
|
||||
{
|
||||
return this.viscosity;
|
||||
}
|
||||
|
||||
public final boolean isGaseous()
|
||||
{
|
||||
return this.isGaseous;
|
||||
}
|
||||
|
||||
public EnumRarity getRarity()
|
||||
{
|
||||
return rarity;
|
||||
}
|
||||
|
||||
public int getColor()
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
public ResourceLocation getStill()
|
||||
{
|
||||
return still;
|
||||
}
|
||||
|
||||
public ResourceLocation getFlowing()
|
||||
{
|
||||
return flowing;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResourceLocation getOverlay()
|
||||
{
|
||||
return overlay;
|
||||
}
|
||||
|
||||
public SoundEvent getFillSound()
|
||||
{
|
||||
if(fillSound == null)
|
||||
{
|
||||
if(getBlock() != null && getBlock().getDefaultState().getMaterial() == Material.LAVA)
|
||||
{
|
||||
fillSound = SoundEvents.ITEM_BUCKET_FILL_LAVA;
|
||||
}
|
||||
else
|
||||
{
|
||||
fillSound = SoundEvents.ITEM_BUCKET_FILL;
|
||||
}
|
||||
}
|
||||
|
||||
return fillSound;
|
||||
}
|
||||
|
||||
public SoundEvent getEmptySound()
|
||||
{
|
||||
if(emptySound == null)
|
||||
{
|
||||
if(getBlock() != null && getBlock().getDefaultState().getMaterial() == Material.LAVA)
|
||||
{
|
||||
emptySound = SoundEvents.ITEM_BUCKET_EMPTY_LAVA;
|
||||
}
|
||||
else
|
||||
{
|
||||
emptySound = SoundEvents.ITEM_BUCKET_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return emptySound;
|
||||
}
|
||||
|
||||
/* Stack-based Accessors */
|
||||
public int getLuminosity(FluidStack stack){ return getLuminosity(); }
|
||||
public int getDensity(FluidStack stack){ return getDensity(); }
|
||||
public int getTemperature(FluidStack stack){ return getTemperature(); }
|
||||
public int getViscosity(FluidStack stack){ return getViscosity(); }
|
||||
public boolean isGaseous(FluidStack stack){ return isGaseous(); }
|
||||
public EnumRarity getRarity(FluidStack stack){ return getRarity(); }
|
||||
public int getColor(FluidStack stack){ return getColor(); }
|
||||
public ResourceLocation getStill(FluidStack stack) { return getStill(); }
|
||||
public ResourceLocation getFlowing(FluidStack stack) { return getFlowing(); }
|
||||
public SoundEvent getFillSound(FluidStack stack) { return getFillSound(); }
|
||||
public SoundEvent getEmptySound(FluidStack stack) { return getEmptySound(); }
|
||||
|
||||
/* World-based Accessors */
|
||||
public int getLuminosity(World world, BlockPos pos){ return getLuminosity(); }
|
||||
public int getDensity(World world, BlockPos pos){ return getDensity(); }
|
||||
public int getTemperature(World world, BlockPos pos){ return getTemperature(); }
|
||||
public int getViscosity(World world, BlockPos pos){ return getViscosity(); }
|
||||
public boolean isGaseous(World world, BlockPos pos){ return isGaseous(); }
|
||||
public EnumRarity getRarity(World world, BlockPos pos){ return getRarity(); }
|
||||
public int getColor(World world, BlockPos pos){ return getColor(); }
|
||||
public ResourceLocation getStill(World world, BlockPos pos) { return getStill(); }
|
||||
public ResourceLocation getFlowing(World world, BlockPos pos) { return getFlowing(); }
|
||||
public SoundEvent getFillSound(World world, BlockPos pos) { return getFillSound(); }
|
||||
public SoundEvent getEmptySound(World world, BlockPos pos) { return getEmptySound(); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Holds the result of a fluid action from {@link FluidUtil}.
|
||||
*
|
||||
* Failed actions will always have {@link #isSuccess()} == false and an empty ItemStack result. See {@link #FAILURE}.
|
||||
*
|
||||
* Successful actions will always have {@link #isSuccess()} == true.
|
||||
* Successful actions may have an empty ItemStack result in some cases,
|
||||
* for example the action succeeded and the resulting item was consumed.
|
||||
*/
|
||||
public class FluidActionResult
|
||||
{
|
||||
public static final FluidActionResult FAILURE = new FluidActionResult(false, ItemStack.EMPTY);
|
||||
|
||||
public final boolean success;
|
||||
@Nonnull
|
||||
public final ItemStack result;
|
||||
|
||||
public FluidActionResult(@Nonnull ItemStack result)
|
||||
{
|
||||
this(true, result);
|
||||
}
|
||||
|
||||
private FluidActionResult(boolean success, @Nonnull ItemStack result)
|
||||
{
|
||||
this.success = success;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public boolean isSuccess()
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ItemStack getResult()
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
|
||||
public class FluidEvent extends Event
|
||||
{
|
||||
private final FluidStack fluid;
|
||||
private final World world;
|
||||
private final BlockPos pos;
|
||||
|
||||
public FluidEvent(FluidStack fluid, World world, BlockPos pos)
|
||||
{
|
||||
this.fluid = fluid;
|
||||
this.world = world;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
return fluid;
|
||||
}
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
public BlockPos getPos()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mods should fire this event when they move fluids around.
|
||||
*
|
||||
*/
|
||||
public static class FluidMotionEvent extends FluidEvent
|
||||
{
|
||||
public FluidMotionEvent(FluidStack fluid, World world, BlockPos pos)
|
||||
{
|
||||
super(fluid, world, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mods should fire this event when a fluid is {@link IFluidTank#fill(FluidStack, boolean)}
|
||||
* their tank implementation. {@link FluidTank} does.
|
||||
*
|
||||
*/
|
||||
public static class FluidFillingEvent extends FluidEvent
|
||||
{
|
||||
private final IFluidTank tank;
|
||||
private final int amount;
|
||||
|
||||
public FluidFillingEvent(FluidStack fluid, World world, BlockPos pos, IFluidTank tank, int amount)
|
||||
{
|
||||
super(fluid, world, pos);
|
||||
this.tank = tank;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public IFluidTank getTank()
|
||||
{
|
||||
return tank;
|
||||
}
|
||||
|
||||
public int getAmount()
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mods should fire this event when a fluid is {@link IFluidTank#drain(int, boolean)} from their
|
||||
* tank.
|
||||
*
|
||||
*/
|
||||
public static class FluidDrainingEvent extends FluidEvent
|
||||
{
|
||||
private final IFluidTank tank;
|
||||
private final int amount;
|
||||
|
||||
public FluidDrainingEvent(FluidStack fluid, World world, BlockPos pos, IFluidTank tank, int amount)
|
||||
{
|
||||
super(fluid, world, pos);
|
||||
this.amount = amount;
|
||||
this.tank = tank;
|
||||
}
|
||||
|
||||
public IFluidTank getTank()
|
||||
{
|
||||
return tank;
|
||||
}
|
||||
|
||||
public int getAmount()
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mods should fire this event when a fluid "spills", for example, if a block containing fluid
|
||||
* is broken.
|
||||
*
|
||||
*/
|
||||
public static class FluidSpilledEvent extends FluidEvent
|
||||
{
|
||||
public FluidSpilledEvent(FluidStack fluid, World world, BlockPos pos)
|
||||
{
|
||||
super(fluid, world, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handy shortcut for firing the various fluid events.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
public static final void fireEvent(FluidEvent event)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraftforge.fml.common.LoaderState;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.translation.I18n;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.ModContainer;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Handles Fluid registrations. Fluids MUST be registered in order to function.
|
||||
*/
|
||||
public abstract class FluidRegistry
|
||||
{
|
||||
static int maxID = 0;
|
||||
|
||||
static BiMap<String, Fluid> fluids = HashBiMap.create();
|
||||
static BiMap<Fluid, Integer> fluidIDs = HashBiMap.create();
|
||||
static BiMap<Integer, String> fluidNames = HashBiMap.create(); //Caching this just makes some other calls faster
|
||||
static BiMap<Block, Fluid> fluidBlocks;
|
||||
|
||||
// the globally unique fluid map - only used to associate non-defaults during world/server loading
|
||||
static BiMap<String,Fluid> masterFluidReference = HashBiMap.create();
|
||||
static BiMap<String,String> defaultFluidName = HashBiMap.create();
|
||||
static Map<Fluid,FluidDelegate> delegates = Maps.newHashMap();
|
||||
|
||||
static boolean universalBucketEnabled = false;
|
||||
static Set<Fluid> bucketFluids = Sets.newHashSet();
|
||||
|
||||
public static final Fluid WATER = new Fluid("water", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay")) {
|
||||
@Override
|
||||
public String getLocalizedName(FluidStack fs) {
|
||||
return I18n.translateToLocal("tile.water.name");
|
||||
}
|
||||
}.setBlock(Blocks.WATER).setUnlocalizedName(Blocks.WATER.getUnlocalizedName());
|
||||
|
||||
public static final Fluid LAVA = new Fluid("lava", new ResourceLocation("blocks/lava_still"), new ResourceLocation("blocks/lava_flow")) {
|
||||
@Override
|
||||
public String getLocalizedName(FluidStack fs) {
|
||||
return I18n.translateToLocal("tile.lava.name");
|
||||
}
|
||||
}.setBlock(Blocks.LAVA).setLuminosity(15).setDensity(3000).setViscosity(6000).setTemperature(1300).setUnlocalizedName(Blocks.LAVA.getUnlocalizedName());
|
||||
|
||||
static
|
||||
{
|
||||
registerFluid(WATER);
|
||||
registerFluid(LAVA);
|
||||
}
|
||||
|
||||
private FluidRegistry(){}
|
||||
|
||||
/**
|
||||
* Called by Forge to prepare the ID map for server -> client sync.
|
||||
* Modders, DO NOT call this.
|
||||
*/
|
||||
public static void initFluidIDs(BiMap<Fluid, Integer> newfluidIDs, Set<String> defaultNames)
|
||||
{
|
||||
maxID = newfluidIDs.size();
|
||||
loadFluidDefaults(newfluidIDs, defaultNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by forge to load default fluid IDs from the world or from server -> client for syncing
|
||||
* DO NOT call this and expect useful behaviour.
|
||||
* @param localFluidIDs
|
||||
* @param defaultNames
|
||||
*/
|
||||
private static void loadFluidDefaults(BiMap<Fluid, Integer> localFluidIDs, Set<String> defaultNames)
|
||||
{
|
||||
// If there's an empty set of default names, use the defaults as defined locally
|
||||
if (defaultNames.isEmpty()) {
|
||||
defaultNames.addAll(defaultFluidName.values());
|
||||
}
|
||||
BiMap<String, Fluid> localFluids = HashBiMap.create(fluids);
|
||||
for (String defaultName : defaultNames)
|
||||
{
|
||||
Fluid fluid = masterFluidReference.get(defaultName);
|
||||
if (fluid == null) {
|
||||
String derivedName = defaultName.split(":",2)[1];
|
||||
String localDefault = defaultFluidName.get(derivedName);
|
||||
if (localDefault == null) {
|
||||
FMLLog.log.error("The fluid {} (specified as {}) is missing from this instance - it will be removed", derivedName, defaultName);
|
||||
continue;
|
||||
}
|
||||
fluid = masterFluidReference.get(localDefault);
|
||||
FMLLog.log.error("The fluid {} specified as default is not present - it will be reverted to default {}", defaultName, localDefault);
|
||||
}
|
||||
FMLLog.log.debug("The fluid {} has been selected as the default fluid for {}", defaultName, fluid.getName());
|
||||
Fluid oldFluid = localFluids.put(fluid.getName(), fluid);
|
||||
Integer id = localFluidIDs.remove(oldFluid);
|
||||
localFluidIDs.put(fluid, id);
|
||||
}
|
||||
BiMap<Integer, String> localFluidNames = HashBiMap.create();
|
||||
for (Entry<Fluid, Integer> e : localFluidIDs.entrySet()) {
|
||||
localFluidNames.put(e.getValue(), e.getKey().getName());
|
||||
}
|
||||
fluidIDs = localFluidIDs;
|
||||
fluids = localFluids;
|
||||
fluidNames = localFluidNames;
|
||||
fluidBlocks = null;
|
||||
for (FluidDelegate fd : delegates.values())
|
||||
{
|
||||
fd.rebind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new Fluid. If a fluid with the same name already exists, registration the alternative fluid is tracked
|
||||
* in case it is the default in another place
|
||||
*
|
||||
* @param fluid
|
||||
* The fluid to register.
|
||||
* @return True if the fluid was registered as the current default fluid, false if it was only registered as an alternative
|
||||
*/
|
||||
public static boolean registerFluid(Fluid fluid)
|
||||
{
|
||||
masterFluidReference.put(uniqueName(fluid), fluid);
|
||||
delegates.put(fluid, new FluidDelegate(fluid, fluid.getName()));
|
||||
if (fluids.containsKey(fluid.getName()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
fluids.put(fluid.getName(), fluid);
|
||||
maxID++;
|
||||
fluidIDs.put(fluid, maxID);
|
||||
fluidNames.put(maxID, fluid.getName());
|
||||
defaultFluidName.put(fluid.getName(), uniqueName(fluid));
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new FluidRegisterEvent(fluid.getName(), maxID));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String uniqueName(Fluid fluid)
|
||||
{
|
||||
ModContainer activeModContainer = Loader.instance().activeModContainer();
|
||||
String activeModContainerName = activeModContainer == null ? "minecraft" : activeModContainer.getModId();
|
||||
return activeModContainerName+":"+fluid.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the supplied fluid the current default fluid for it's name
|
||||
* @param fluid the fluid we're testing
|
||||
* @return if the fluid is default
|
||||
*/
|
||||
public static boolean isFluidDefault(Fluid fluid)
|
||||
{
|
||||
return fluids.containsValue(fluid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the supplied fluid have an entry for it's name (whether or not the fluid itself is default)
|
||||
* @param fluid the fluid we're testing
|
||||
* @return if the fluid's name has a registration entry
|
||||
*/
|
||||
public static boolean isFluidRegistered(Fluid fluid)
|
||||
{
|
||||
return fluid != null && fluids.containsKey(fluid.getName());
|
||||
}
|
||||
|
||||
public static boolean isFluidRegistered(String fluidName)
|
||||
{
|
||||
return fluids.containsKey(fluidName);
|
||||
}
|
||||
|
||||
public static Fluid getFluid(String fluidName)
|
||||
{
|
||||
return fluids.get(fluidName);
|
||||
}
|
||||
|
||||
public static String getFluidName(Fluid fluid)
|
||||
{
|
||||
return fluids.inverse().get(fluid);
|
||||
}
|
||||
|
||||
public static String getFluidName(FluidStack stack)
|
||||
{
|
||||
return getFluidName(stack.getFluid());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static FluidStack getFluidStack(String fluidName, int amount)
|
||||
{
|
||||
if (!fluids.containsKey(fluidName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new FluidStack(getFluid(fluidName), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a read-only map containing Fluid Names and their associated Fluids.
|
||||
*/
|
||||
public static Map<String, Fluid> getRegisteredFluids()
|
||||
{
|
||||
return ImmutableMap.copyOf(fluids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a read-only map containing Fluid Names and their associated IDs.
|
||||
* Modders should never actually use this, use the String names.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Fluid, Integer> getRegisteredFluidIDs()
|
||||
{
|
||||
return ImmutableMap.copyOf(fluidIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the universal bucket in forge.
|
||||
* Has to be called before pre-initialization.
|
||||
* Actually just call it statically in your mod class.
|
||||
*/
|
||||
public static void enableUniversalBucket()
|
||||
{
|
||||
if (Loader.instance().hasReachedState(LoaderState.PREINITIALIZATION))
|
||||
{
|
||||
ModContainer modContainer = Loader.instance().activeModContainer();
|
||||
String modContainerName = modContainer == null ? null : modContainer.getName();
|
||||
FMLLog.log.error("Trying to activate the universal filled bucket too late. Call it statically in your Mods class. Mod: {}", modContainerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
universalBucketEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUniversalBucketEnabled()
|
||||
{
|
||||
return universalBucketEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a fluid with the universal bucket.
|
||||
* This only has an effect if the universal bucket is enabled.
|
||||
* @param fluid The fluid that the bucket shall be able to hold
|
||||
* @return True if the fluid was added successfully, false if it already was registered or couldn't be registered with the bucket.
|
||||
*/
|
||||
public static boolean addBucketForFluid(Fluid fluid)
|
||||
{
|
||||
if(fluid == null) {
|
||||
return false;
|
||||
}
|
||||
// register unregistered fluids
|
||||
if (!isFluidRegistered(fluid))
|
||||
{
|
||||
registerFluid(fluid);
|
||||
}
|
||||
return bucketFluids.add(fluid);
|
||||
}
|
||||
|
||||
/**
|
||||
* All fluids registered with the universal bucket
|
||||
* @return An immutable set containing the fluids
|
||||
*/
|
||||
public static Set<Fluid> getBucketFluids()
|
||||
{
|
||||
return ImmutableSet.copyOf(bucketFluids);
|
||||
}
|
||||
|
||||
|
||||
public static Fluid lookupFluidForBlock(Block block)
|
||||
{
|
||||
if (fluidBlocks == null)
|
||||
{
|
||||
BiMap<Block, Fluid> tmp = HashBiMap.create();
|
||||
for (Fluid fluid : fluids.values())
|
||||
{
|
||||
if (fluid.canBePlacedInWorld() && fluid.getBlock() != null)
|
||||
{
|
||||
tmp.put(fluid.getBlock(), fluid);
|
||||
}
|
||||
}
|
||||
fluidBlocks = tmp;
|
||||
}
|
||||
if (block == Blocks.FLOWING_WATER)
|
||||
{
|
||||
block = Blocks.WATER;
|
||||
}
|
||||
else if (block == Blocks.FLOWING_LAVA)
|
||||
{
|
||||
block = Blocks.LAVA;
|
||||
}
|
||||
return fluidBlocks.get(block);
|
||||
}
|
||||
|
||||
public static class FluidRegisterEvent extends Event
|
||||
{
|
||||
private final String fluidName;
|
||||
private final int fluidID;
|
||||
|
||||
public FluidRegisterEvent(String fluidName, int fluidID)
|
||||
{
|
||||
this.fluidName = fluidName;
|
||||
this.fluidID = fluidID;
|
||||
}
|
||||
|
||||
public String getFluidName()
|
||||
{
|
||||
return fluidName;
|
||||
}
|
||||
|
||||
public int getFluidID()
|
||||
{
|
||||
return fluidID;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getMaxID()
|
||||
{
|
||||
return maxID;
|
||||
}
|
||||
|
||||
public static String getDefaultFluidName(Fluid key)
|
||||
{
|
||||
String name = masterFluidReference.inverse().get(key);
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
FMLLog.log.error("The fluid registry is corrupted. A fluid {} {} is not properly registered. The mod that registered this is broken", key.getClass().getName(), key.getName());
|
||||
throw new IllegalStateException("The fluid registry is corrupted");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getModId(@Nullable FluidStack fluidStack)
|
||||
{
|
||||
if (fluidStack != null)
|
||||
{
|
||||
String defaultFluidName = getDefaultFluidName(fluidStack.getFluid());
|
||||
if (defaultFluidName != null)
|
||||
{
|
||||
ResourceLocation fluidResourceName = new ResourceLocation(defaultFluidName);
|
||||
return fluidResourceName.getResourceDomain();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void loadFluidDefaults(NBTTagCompound tag)
|
||||
{
|
||||
Set<String> defaults = Sets.newHashSet();
|
||||
if (tag.hasKey("DefaultFluidList",9))
|
||||
{
|
||||
FMLLog.log.debug("Loading persistent fluid defaults from world");
|
||||
NBTTagList tl = tag.getTagList("DefaultFluidList", 8);
|
||||
for (int i = 0; i < tl.tagCount(); i++)
|
||||
{
|
||||
defaults.add(tl.getStringTagAt(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.log.debug("World is missing persistent fluid defaults - using local defaults");
|
||||
}
|
||||
loadFluidDefaults(HashBiMap.create(fluidIDs), defaults);
|
||||
}
|
||||
|
||||
public static void writeDefaultFluidList(NBTTagCompound forgeData)
|
||||
{
|
||||
NBTTagList tagList = new NBTTagList();
|
||||
|
||||
for (Entry<String, Fluid> def : fluids.entrySet())
|
||||
{
|
||||
tagList.appendTag(new NBTTagString(getDefaultFluidName(def.getValue())));
|
||||
}
|
||||
|
||||
forgeData.setTag("DefaultFluidList", tagList);
|
||||
}
|
||||
|
||||
public static void validateFluidRegistry()
|
||||
{
|
||||
Set<Fluid> illegalFluids = Sets.newHashSet();
|
||||
for (Fluid f : fluids.values())
|
||||
{
|
||||
if (!masterFluidReference.containsValue(f))
|
||||
{
|
||||
illegalFluids.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!illegalFluids.isEmpty())
|
||||
{
|
||||
FMLLog.log.fatal("The fluid registry is corrupted. Something has inserted a fluid without registering it");
|
||||
FMLLog.log.fatal("There is {} unregistered fluids", illegalFluids.size());
|
||||
for (Fluid f: illegalFluids)
|
||||
{
|
||||
FMLLog.log.fatal(" Fluid name : {}, type: {}", f.getName(), f.getClass().getName());
|
||||
}
|
||||
FMLLog.log.fatal("The mods that own these fluids need to register them properly");
|
||||
throw new IllegalStateException("The fluid map contains fluids unknown to the master fluid registry");
|
||||
}
|
||||
}
|
||||
|
||||
static IRegistryDelegate<Fluid> makeDelegate(Fluid fl)
|
||||
{
|
||||
return delegates.get(fl);
|
||||
}
|
||||
|
||||
|
||||
private static class FluidDelegate implements IRegistryDelegate<Fluid>
|
||||
{
|
||||
private String name;
|
||||
private Fluid fluid;
|
||||
|
||||
FluidDelegate(Fluid fluid, String name)
|
||||
{
|
||||
this.fluid = fluid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fluid get()
|
||||
{
|
||||
return fluid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation name() {
|
||||
return new ResourceLocation(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Fluid> type()
|
||||
{
|
||||
return Fluid.class;
|
||||
}
|
||||
|
||||
void rebind()
|
||||
{
|
||||
fluid = fluids.get(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ItemStack substitute for Fluids.
|
||||
*
|
||||
* NOTE: Equality is based on the Fluid, not the amount. Use
|
||||
* {@link #isFluidStackIdentical(FluidStack)} to determine if FluidID, Amount and NBT Tag are all
|
||||
* equal.
|
||||
*
|
||||
*/
|
||||
public class FluidStack
|
||||
{
|
||||
public int amount;
|
||||
public NBTTagCompound tag;
|
||||
private IRegistryDelegate<Fluid> fluidDelegate;
|
||||
|
||||
public FluidStack(Fluid fluid, int amount)
|
||||
{
|
||||
if (fluid == null)
|
||||
{
|
||||
FMLLog.bigWarning("Null fluid supplied to fluidstack. Did you try and create a stack for an unregistered fluid?");
|
||||
throw new IllegalArgumentException("Cannot create a fluidstack from a null fluid");
|
||||
}
|
||||
else if (!FluidRegistry.isFluidRegistered(fluid))
|
||||
{
|
||||
FMLLog.bigWarning("Failed attempt to create a FluidStack for an unregistered Fluid {} (type {})", fluid.getName(), fluid.getClass().getName());
|
||||
throw new IllegalArgumentException("Cannot create a fluidstack from an unregistered fluid");
|
||||
}
|
||||
this.fluidDelegate = FluidRegistry.makeDelegate(fluid);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public FluidStack(Fluid fluid, int amount, NBTTagCompound nbt)
|
||||
{
|
||||
this(fluid, amount);
|
||||
|
||||
if (nbt != null)
|
||||
{
|
||||
tag = (NBTTagCompound) nbt.copy();
|
||||
}
|
||||
}
|
||||
|
||||
public FluidStack(FluidStack stack, int amount)
|
||||
{
|
||||
this(stack.getFluid(), amount, stack.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* This provides a safe method for retrieving a FluidStack - if the Fluid is invalid, the stack
|
||||
* will return as null.
|
||||
*/
|
||||
@Nullable
|
||||
public static FluidStack loadFluidStackFromNBT(NBTTagCompound nbt)
|
||||
{
|
||||
if (nbt == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!nbt.hasKey("FluidName", Constants.NBT.TAG_STRING))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String fluidName = nbt.getString("FluidName");
|
||||
if (FluidRegistry.getFluid(fluidName) == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
FluidStack stack = new FluidStack(FluidRegistry.getFluid(fluidName), nbt.getInteger("Amount"));
|
||||
|
||||
if (nbt.hasKey("Tag"))
|
||||
{
|
||||
stack.tag = nbt.getCompoundTag("Tag");
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
|
||||
{
|
||||
nbt.setString("FluidName", FluidRegistry.getFluidName(getFluid()));
|
||||
nbt.setInteger("Amount", amount);
|
||||
|
||||
if (tag != null)
|
||||
{
|
||||
nbt.setTag("Tag", tag);
|
||||
}
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public final Fluid getFluid()
|
||||
{
|
||||
return fluidDelegate.get();
|
||||
}
|
||||
|
||||
public String getLocalizedName()
|
||||
{
|
||||
return this.getFluid().getLocalizedName(this);
|
||||
}
|
||||
|
||||
public String getUnlocalizedName()
|
||||
{
|
||||
return this.getFluid().getUnlocalizedName(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A copy of this FluidStack
|
||||
*/
|
||||
public FluidStack copy()
|
||||
{
|
||||
return new FluidStack(getFluid(), amount, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the FluidIDs and NBT Tags are equal. This does not check amounts.
|
||||
*
|
||||
* @param other
|
||||
* The FluidStack for comparison
|
||||
* @return true if the Fluids (IDs and NBT Tags) are the same
|
||||
*/
|
||||
public boolean isFluidEqual(@Nullable FluidStack other)
|
||||
{
|
||||
return other != null && getFluid() == other.getFluid() && isFluidStackTagEqual(other);
|
||||
}
|
||||
|
||||
private boolean isFluidStackTagEqual(FluidStack other)
|
||||
{
|
||||
return tag == null ? other.tag == null : other.tag == null ? false : tag.equals(other.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the NBT Tags are equal. Useful if the FluidIDs are known to be equal.
|
||||
*/
|
||||
public static boolean areFluidStackTagsEqual(@Nullable FluidStack stack1, @Nullable FluidStack stack2)
|
||||
{
|
||||
return stack1 == null && stack2 == null ? true : stack1 == null || stack2 == null ? false : stack1.isFluidStackTagEqual(stack2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the Fluids are equal and this stack is larger.
|
||||
*
|
||||
* @param other
|
||||
* @return true if this FluidStack contains the other FluidStack (same fluid and >= amount)
|
||||
*/
|
||||
public boolean containsFluid(@Nullable FluidStack other)
|
||||
{
|
||||
return isFluidEqual(other) && amount >= other.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the FluidIDs, Amounts, and NBT Tags are all equal.
|
||||
*
|
||||
* @param other
|
||||
* - the FluidStack for comparison
|
||||
* @return true if the two FluidStacks are exactly the same
|
||||
*/
|
||||
public boolean isFluidStackIdentical(FluidStack other)
|
||||
{
|
||||
return isFluidEqual(other) && amount == other.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the FluidIDs and NBT Tags are equal compared to a registered container
|
||||
* ItemStack. This does not check amounts.
|
||||
*
|
||||
* @param other
|
||||
* The ItemStack for comparison
|
||||
* @return true if the Fluids (IDs and NBT Tags) are the same
|
||||
*/
|
||||
public boolean isFluidEqual(ItemStack other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return isFluidEqual(FluidUtil.getFluidContained(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode()
|
||||
{
|
||||
int code = 1;
|
||||
code = 31*code + getFluid().hashCode();
|
||||
code = 31*code + amount;
|
||||
if (tag != null)
|
||||
code = 31*code + tag.hashCode();
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default equality comparison for a FluidStack. Same functionality as isFluidEqual().
|
||||
*
|
||||
* This is included for use in data structures.
|
||||
*/
|
||||
@Override
|
||||
public final boolean equals(Object o)
|
||||
{
|
||||
if (!(o instanceof FluidStack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return isFluidEqual((FluidStack) o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraftforge.fluids.capability.FluidTankPropertiesWrapper;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
|
||||
/**
|
||||
* Reference implementation of {@link IFluidTank}. Use/extend this or implement your own.
|
||||
*/
|
||||
public class FluidTank implements IFluidTank, IFluidHandler
|
||||
{
|
||||
@Nullable
|
||||
protected FluidStack fluid;
|
||||
protected int capacity;
|
||||
protected TileEntity tile;
|
||||
protected boolean canFill = true;
|
||||
protected boolean canDrain = true;
|
||||
protected IFluidTankProperties[] tankProperties;
|
||||
|
||||
public FluidTank(int capacity)
|
||||
{
|
||||
this(null, capacity);
|
||||
}
|
||||
|
||||
public FluidTank(@Nullable FluidStack fluidStack, int capacity)
|
||||
{
|
||||
this.fluid = fluidStack;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public FluidTank(Fluid fluid, int amount, int capacity)
|
||||
{
|
||||
this(new FluidStack(fluid, amount), capacity);
|
||||
}
|
||||
|
||||
public FluidTank readFromNBT(NBTTagCompound nbt)
|
||||
{
|
||||
if (!nbt.hasKey("Empty"))
|
||||
{
|
||||
FluidStack fluid = FluidStack.loadFluidStackFromNBT(nbt);
|
||||
setFluid(fluid);
|
||||
}
|
||||
else
|
||||
{
|
||||
setFluid(null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
|
||||
{
|
||||
if (fluid != null)
|
||||
{
|
||||
fluid.writeToNBT(nbt);
|
||||
}
|
||||
else
|
||||
{
|
||||
nbt.setString("Empty", "");
|
||||
}
|
||||
return nbt;
|
||||
}
|
||||
|
||||
/* IFluidTank */
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
return fluid;
|
||||
}
|
||||
|
||||
public void setFluid(@Nullable FluidStack fluid)
|
||||
{
|
||||
this.fluid = fluid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFluidAmount()
|
||||
{
|
||||
if (fluid == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return fluid.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity()
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public void setCapacity(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public void setTileEntity(TileEntity tile)
|
||||
{
|
||||
this.tile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidTankInfo getInfo()
|
||||
{
|
||||
return new FluidTankInfo(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
if (this.tankProperties == null)
|
||||
{
|
||||
this.tankProperties = new IFluidTankProperties[] { new FluidTankPropertiesWrapper(this) };
|
||||
}
|
||||
return this.tankProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (!canFillFluidType(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fillInternal(resource, doFill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to bypass the restrictions from {@link #canFillFluidType(FluidStack)}
|
||||
* Meant for use by the owner of the tank when they have {@link #canFill() set to false}.
|
||||
*/
|
||||
public int fillInternal(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (resource == null || resource.amount <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!doFill)
|
||||
{
|
||||
if (fluid == null)
|
||||
{
|
||||
return Math.min(capacity, resource.amount);
|
||||
}
|
||||
|
||||
if (!fluid.isFluidEqual(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.min(capacity - fluid.amount, resource.amount);
|
||||
}
|
||||
|
||||
if (fluid == null)
|
||||
{
|
||||
fluid = new FluidStack(resource, Math.min(capacity, resource.amount));
|
||||
|
||||
onContentsChanged();
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorld(), tile.getPos(), this, fluid.amount));
|
||||
}
|
||||
return fluid.amount;
|
||||
}
|
||||
|
||||
if (!fluid.isFluidEqual(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int filled = capacity - fluid.amount;
|
||||
|
||||
if (resource.amount < filled)
|
||||
{
|
||||
fluid.amount += resource.amount;
|
||||
filled = resource.amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
fluid.amount = capacity;
|
||||
}
|
||||
|
||||
onContentsChanged();
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(fluid, tile.getWorld(), tile.getPos(), this, filled));
|
||||
}
|
||||
return filled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (!canDrainFluidType(getFluid()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return drainInternal(resource, doDrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (!canDrainFluidType(fluid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return drainInternal(maxDrain, doDrain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to bypass the restrictions from {@link #canDrainFluidType(FluidStack)}
|
||||
* Meant for use by the owner of the tank when they have {@link #canDrain()} set to false}.
|
||||
*/
|
||||
@Nullable
|
||||
public FluidStack drainInternal(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (resource == null || !resource.isFluidEqual(getFluid()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return drainInternal(resource.amount, doDrain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to bypass the restrictions from {@link #canDrainFluidType(FluidStack)}
|
||||
* Meant for use by the owner of the tank when they have {@link #canDrain()} set to false}.
|
||||
*/
|
||||
@Nullable
|
||||
public FluidStack drainInternal(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (fluid == null || maxDrain <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int drained = maxDrain;
|
||||
if (fluid.amount < drained)
|
||||
{
|
||||
drained = fluid.amount;
|
||||
}
|
||||
|
||||
FluidStack stack = new FluidStack(fluid, drained);
|
||||
if (doDrain)
|
||||
{
|
||||
fluid.amount -= drained;
|
||||
if (fluid.amount <= 0)
|
||||
{
|
||||
fluid = null;
|
||||
}
|
||||
|
||||
onContentsChanged();
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(fluid, tile.getWorld(), tile.getPos(), this, drained));
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this tank can be filled with {@link IFluidHandler}
|
||||
*
|
||||
* @see IFluidTankProperties#canFill()
|
||||
*/
|
||||
public boolean canFill()
|
||||
{
|
||||
return canFill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this tank can be drained with {@link IFluidHandler}
|
||||
*
|
||||
* @see IFluidTankProperties#canDrain()
|
||||
*/
|
||||
public boolean canDrain()
|
||||
{
|
||||
return canDrain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this tank can be filled with {@link IFluidHandler}
|
||||
*
|
||||
* @see IFluidTankProperties#canFill()
|
||||
*/
|
||||
public void setCanFill(boolean canFill)
|
||||
{
|
||||
this.canFill = canFill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this tank can be drained with {@link IFluidHandler}
|
||||
*
|
||||
* @see IFluidTankProperties#canDrain()
|
||||
*/
|
||||
public void setCanDrain(boolean canDrain)
|
||||
{
|
||||
this.canDrain = canDrain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the tank can be filled with this type of fluid.
|
||||
* Used as a filter for fluid types.
|
||||
* Does not consider the current contents or capacity of the tank,
|
||||
* only whether it could ever fill with this type of fluid.
|
||||
*
|
||||
* @see IFluidTankProperties#canFillFluidType(FluidStack)
|
||||
*/
|
||||
public boolean canFillFluidType(FluidStack fluid)
|
||||
{
|
||||
return canFill();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the tank can drain out this type of fluid.
|
||||
* Used as a filter for fluid types.
|
||||
* Does not consider the current contents or capacity of the tank,
|
||||
* only whether it could ever drain out this type of fluid.
|
||||
*
|
||||
* @see IFluidTankProperties#canDrainFluidType(FluidStack)
|
||||
*/
|
||||
public boolean canDrainFluidType(@Nullable FluidStack fluid)
|
||||
{
|
||||
return fluid != null && canDrain();
|
||||
}
|
||||
|
||||
protected void onContentsChanged()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Wrapper class used to encapsulate information about an IFluidTank.
|
||||
*/
|
||||
public final class FluidTankInfo
|
||||
{
|
||||
@Nullable
|
||||
public final FluidStack fluid;
|
||||
public final int capacity;
|
||||
|
||||
public FluidTankInfo(@Nullable FluidStack fluid, int capacity)
|
||||
{
|
||||
this.fluid = fluid;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public FluidTankInfo(IFluidTank tank)
|
||||
{
|
||||
this.fluid = tank.getFluid();
|
||||
this.capacity = tank.getCapacity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,758 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemBucket;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.fluids.capability.wrappers.BlockLiquidWrapper;
|
||||
import net.minecraftforge.fluids.capability.wrappers.BlockWrapper;
|
||||
import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.ItemHandlerHelper;
|
||||
|
||||
public class FluidUtil
|
||||
{
|
||||
private FluidUtil()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to handle the common case of a player holding a fluid item and right-clicking on a fluid handler block.
|
||||
* First it tries to fill the item from the block,
|
||||
* if that action fails then it tries to drain the item into the block.
|
||||
* Automatically updates the item in the player's hand and stashes any extra items created.
|
||||
*
|
||||
* @param player The player doing the interaction between the item and fluid handler block.
|
||||
* @param hand The player's hand that is holding an item that should interact with the fluid handler block.
|
||||
* @param world The world that contains the fluid handler block.
|
||||
* @param pos The position of the fluid handler block in the world.
|
||||
* @param side The side of the block to interact with. May be null.
|
||||
* @return true if the interaction succeeded and updated the item held by the player, false otherwise.
|
||||
*/
|
||||
public static boolean interactWithFluidHandler(@Nonnull EntityPlayer player, @Nonnull EnumHand hand, @Nonnull World world, @Nonnull BlockPos pos, @Nullable EnumFacing side)
|
||||
{
|
||||
Preconditions.checkNotNull(world);
|
||||
Preconditions.checkNotNull(pos);
|
||||
|
||||
IFluidHandler blockFluidHandler = getFluidHandler(world, pos, side);
|
||||
return blockFluidHandler != null && interactWithFluidHandler(player, hand, blockFluidHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to handle the common case of a player holding a fluid item and right-clicking on a fluid handler.
|
||||
* First it tries to fill the item from the handler,
|
||||
* if that action fails then it tries to drain the item into the handler.
|
||||
* Automatically updates the item in the player's hand and stashes any extra items created.
|
||||
*
|
||||
* @param player The player doing the interaction between the item and fluid handler.
|
||||
* @param hand The player's hand that is holding an item that should interact with the fluid handler.
|
||||
* @param handler The fluid handler.
|
||||
* @return true if the interaction succeeded and updated the item held by the player, false otherwise.
|
||||
*/
|
||||
public static boolean interactWithFluidHandler(@Nonnull EntityPlayer player, @Nonnull EnumHand hand, @Nonnull IFluidHandler handler)
|
||||
{
|
||||
Preconditions.checkNotNull(player);
|
||||
Preconditions.checkNotNull(hand);
|
||||
Preconditions.checkNotNull(handler);
|
||||
|
||||
ItemStack heldItem = player.getHeldItem(hand);
|
||||
if (!heldItem.isEmpty())
|
||||
{
|
||||
IItemHandler playerInventory = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
|
||||
if (playerInventory != null)
|
||||
{
|
||||
FluidActionResult fluidActionResult = tryFillContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
|
||||
if (!fluidActionResult.isSuccess())
|
||||
{
|
||||
fluidActionResult = tryEmptyContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
|
||||
}
|
||||
|
||||
if (fluidActionResult.isSuccess())
|
||||
{
|
||||
player.setHeldItem(hand, fluidActionResult.getResult());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a container from the given fluidSource.
|
||||
*
|
||||
* @param container The container to be filled. Will not be modified.
|
||||
* Separate handling must be done to reduce the stack size, stow containers, etc, on success.
|
||||
* See {@link #tryFillContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer, boolean)}.
|
||||
* @param fluidSource The fluid handler to be drained.
|
||||
* @param maxAmount The largest amount of fluid that should be transferred.
|
||||
* @param player The player to make the filling noise. Pass null for no noise.
|
||||
* @param doFill true if the container should actually be filled, false if it should be simulated.
|
||||
* @return a {@link FluidActionResult} holding the filled container if successful.
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryFillContainer(@Nonnull ItemStack container, IFluidHandler fluidSource, int maxAmount, @Nullable EntityPlayer player, boolean doFill)
|
||||
{
|
||||
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
|
||||
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
|
||||
if (containerFluidHandler != null)
|
||||
{
|
||||
FluidStack simulatedTransfer = tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, false);
|
||||
if (simulatedTransfer != null)
|
||||
{
|
||||
if (doFill)
|
||||
{
|
||||
tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, true);
|
||||
if (player != null)
|
||||
{
|
||||
SoundEvent soundevent = simulatedTransfer.getFluid().getFillSound(simulatedTransfer);
|
||||
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
containerFluidHandler.fill(simulatedTransfer, true);
|
||||
}
|
||||
|
||||
ItemStack resultContainer = containerFluidHandler.getContainer();
|
||||
return new FluidActionResult(resultContainer);
|
||||
}
|
||||
}
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a filled container and tries to empty it into the given tank.
|
||||
*
|
||||
* @param container The filled container. Will not be modified.
|
||||
* Separate handling must be done to reduce the stack size, stow containers, etc, on success.
|
||||
* See {@link #tryEmptyContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer, boolean)}.
|
||||
* @param fluidDestination The fluid handler to be filled by the container.
|
||||
* @param maxAmount The largest amount of fluid that should be transferred.
|
||||
* @param player Player for making the bucket drained sound. Pass null for no noise.
|
||||
* @param doDrain true if the container should actually be drained, false if it should be simulated.
|
||||
* @return a {@link FluidActionResult} holding the empty container if the fluid handler was filled.
|
||||
* NOTE If the container is consumable, the empty container will be null on success.
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryEmptyContainer(@Nonnull ItemStack container, IFluidHandler fluidDestination, int maxAmount, @Nullable EntityPlayer player, boolean doDrain)
|
||||
{
|
||||
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
|
||||
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
|
||||
if (containerFluidHandler != null)
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
FluidStack transfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, true);
|
||||
if (transfer != null)
|
||||
{
|
||||
if (player != null)
|
||||
{
|
||||
SoundEvent soundevent = transfer.getFluid().getEmptySound(transfer);
|
||||
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
ItemStack resultContainer = containerFluidHandler.getContainer();
|
||||
return new FluidActionResult(resultContainer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FluidStack simulatedTransfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, false);
|
||||
if (simulatedTransfer != null)
|
||||
{
|
||||
containerFluidHandler.drain(simulatedTransfer, true);
|
||||
ItemStack resultContainer = containerFluidHandler.getContainer();
|
||||
return new FluidActionResult(resultContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an Fluid Container Item and tries to fill it from the given tank.
|
||||
* If the player is in creative mode, the container will not be modified on success, and no additional items created.
|
||||
* If the input itemstack has a stacksize > 1 it will stow the filled container in the given inventory.
|
||||
* If the inventory does not accept it, it will be given to the player or dropped at the players feet.
|
||||
* If player is null in this case, the action will be aborted.
|
||||
*
|
||||
* @param container The Fluid Container ItemStack to fill.
|
||||
* Will not be modified directly, if modifications are necessary a modified copy is returned in the result.
|
||||
* @param fluidSource The fluid source to fill from
|
||||
* @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put
|
||||
* @param maxAmount Maximum amount of fluid to take from the tank.
|
||||
* @param player The player that gets the items the inventory can't take.
|
||||
* Can be null, only used if the inventory cannot take the filled stack.
|
||||
* @return a {@link FluidActionResult} holding the result and the resulting container. The resulting container is empty on failure.
|
||||
* @deprecated use {@link #tryFillContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer, boolean)}
|
||||
*/
|
||||
@Deprecated // TODO remove in 1.13
|
||||
@Nonnull
|
||||
public static FluidActionResult tryFillContainerAndStow(@Nonnull ItemStack container, IFluidHandler fluidSource, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player)
|
||||
{
|
||||
return tryFillContainerAndStow(container, fluidSource, inventory, maxAmount, player, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an Fluid Container Item and tries to fill it from the given tank.
|
||||
* If the player is in creative mode, the container will not be modified on success, and no additional items created.
|
||||
* If the input itemstack has a stacksize > 1 it will stow the filled container in the given inventory.
|
||||
* If the inventory does not accept it, it will be given to the player or dropped at the players feet.
|
||||
* If player is null in this case, the action will be aborted.
|
||||
*
|
||||
* @param container The Fluid Container ItemStack to fill.
|
||||
* Will not be modified directly, if modifications are necessary a modified copy is returned in the result.
|
||||
* @param fluidSource The fluid source to fill from
|
||||
* @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put
|
||||
* @param maxAmount Maximum amount of fluid to take from the tank.
|
||||
* @param player The player that gets the items the inventory can't take.
|
||||
* Can be null, only used if the inventory cannot take the filled stack.
|
||||
* @param doFill true if the container should actually be filled, false if it should be simulated.
|
||||
* @return a {@link FluidActionResult} holding the result and the resulting container. The resulting container is empty on failure.
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryFillContainerAndStow(@Nonnull ItemStack container, IFluidHandler fluidSource, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player, boolean doFill)
|
||||
{
|
||||
if (container.isEmpty())
|
||||
{
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
if (player != null && player.capabilities.isCreativeMode)
|
||||
{
|
||||
FluidActionResult filledReal = tryFillContainer(container, fluidSource, maxAmount, player, doFill);
|
||||
if (filledReal.isSuccess())
|
||||
{
|
||||
return new FluidActionResult(container); // creative mode: item does not change
|
||||
}
|
||||
}
|
||||
else if (container.getCount() == 1) // don't need to stow anything, just fill the container stack
|
||||
{
|
||||
FluidActionResult filledReal = tryFillContainer(container, fluidSource, maxAmount, player, doFill);
|
||||
if (filledReal.isSuccess())
|
||||
{
|
||||
return filledReal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FluidActionResult filledSimulated = tryFillContainer(container, fluidSource, maxAmount, player, false);
|
||||
if (filledSimulated.isSuccess())
|
||||
{
|
||||
// check if we can give the itemStack to the inventory
|
||||
ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, filledSimulated.getResult(), true);
|
||||
if (remainder.isEmpty() || player != null)
|
||||
{
|
||||
FluidActionResult filledReal = tryFillContainer(container, fluidSource, maxAmount, player, doFill);
|
||||
remainder = ItemHandlerHelper.insertItemStacked(inventory, filledReal.getResult(), !doFill);
|
||||
|
||||
// give it to the player or drop it at their feet
|
||||
if (!remainder.isEmpty() && player != null && doFill)
|
||||
{
|
||||
ItemHandlerHelper.giveItemToPlayer(player, remainder);
|
||||
}
|
||||
|
||||
ItemStack containerCopy = container.copy();
|
||||
containerCopy.shrink(1);
|
||||
return new FluidActionResult(containerCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an Fluid Container Item, tries to empty it into the fluid handler, and stows it in the given inventory.
|
||||
* If the player is in creative mode, the container will not be modified on success, and no additional items created.
|
||||
* If the input itemstack has a stacksize > 1 it will stow the emptied container in the given inventory.
|
||||
* If the inventory does not accept the emptied container, it will be given to the player or dropped at the players feet.
|
||||
* If player is null in this case, the action will be aborted.
|
||||
*
|
||||
* @param container The filled Fluid Container Itemstack to empty.
|
||||
* Will not be modified directly, if modifications are necessary a modified copy is returned in the result.
|
||||
* @param fluidDestination The fluid destination to fill from the fluid container.
|
||||
* @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put
|
||||
* @param maxAmount Maximum amount of fluid to take from the tank.
|
||||
* @param player The player that gets the items the inventory can't take. Can be null, only used if the inventory cannot take the filled stack.
|
||||
* @return a {@link FluidActionResult} holding the result and the resulting container. The resulting container is empty on failure.
|
||||
* @deprecated use {@link #tryEmptyContainerAndStow(ItemStack, IFluidHandler, IItemHandler, int, EntityPlayer, boolean)}
|
||||
*/
|
||||
@Deprecated // TODO: remove in 1.13
|
||||
@Nonnull
|
||||
public static FluidActionResult tryEmptyContainerAndStow(@Nonnull ItemStack container, IFluidHandler fluidDestination, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player)
|
||||
{
|
||||
return tryEmptyContainerAndStow(container, fluidDestination, inventory, maxAmount, player, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an Fluid Container Item, tries to empty it into the fluid handler, and stows it in the given inventory.
|
||||
* If the player is in creative mode, the container will not be modified on success, and no additional items created.
|
||||
* If the input itemstack has a stacksize > 1 it will stow the emptied container in the given inventory.
|
||||
* If the inventory does not accept the emptied container, it will be given to the player or dropped at the players feet.
|
||||
* If player is null in this case, the action will be aborted.
|
||||
*
|
||||
* @param container The filled Fluid Container Itemstack to empty.
|
||||
* Will not be modified directly, if modifications are necessary a modified copy is returned in the result.
|
||||
* @param fluidDestination The fluid destination to fill from the fluid container.
|
||||
* @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put
|
||||
* @param maxAmount Maximum amount of fluid to take from the tank.
|
||||
* @param player The player that gets the items the inventory can't take. Can be null, only used if the inventory cannot take the filled stack.
|
||||
* @param doDrain true if the container should actually be drained, false if it should be simulated.
|
||||
* @return a {@link FluidActionResult} holding the result and the resulting container. The resulting container is empty on failure.
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryEmptyContainerAndStow(@Nonnull ItemStack container, IFluidHandler fluidDestination, IItemHandler inventory, int maxAmount, @Nullable EntityPlayer player, boolean doDrain)
|
||||
{
|
||||
if (container.isEmpty())
|
||||
{
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
if (player != null && player.capabilities.isCreativeMode)
|
||||
{
|
||||
FluidActionResult emptiedReal = tryEmptyContainer(container, fluidDestination, maxAmount, player, doDrain);
|
||||
if (emptiedReal.isSuccess())
|
||||
{
|
||||
return new FluidActionResult(container); // creative mode: item does not change
|
||||
}
|
||||
}
|
||||
else if (container.getCount() == 1) // don't need to stow anything, just fill and edit the container stack
|
||||
{
|
||||
FluidActionResult emptiedReal = tryEmptyContainer(container, fluidDestination, maxAmount, player, doDrain);
|
||||
if (emptiedReal.isSuccess())
|
||||
{
|
||||
return emptiedReal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FluidActionResult emptiedSimulated = tryEmptyContainer(container, fluidDestination, maxAmount, player, false);
|
||||
if (emptiedSimulated.isSuccess())
|
||||
{
|
||||
// check if we can give the itemStack to the inventory
|
||||
ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, emptiedSimulated.getResult(), true);
|
||||
if (remainder.isEmpty() || player != null)
|
||||
{
|
||||
FluidActionResult emptiedReal = tryEmptyContainer(container, fluidDestination, maxAmount, player, doDrain);
|
||||
remainder = ItemHandlerHelper.insertItemStacked(inventory, emptiedReal.getResult(), !doDrain);
|
||||
|
||||
// give it to the player or drop it at their feet
|
||||
if (!remainder.isEmpty() && player != null && doDrain)
|
||||
{
|
||||
ItemHandlerHelper.giveItemToPlayer(player, remainder);
|
||||
}
|
||||
|
||||
ItemStack containerCopy = container.copy();
|
||||
containerCopy.shrink(1);
|
||||
return new FluidActionResult(containerCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a destination fluid handler from a source fluid handler with a max amount.
|
||||
* To specify a fluid to transfer instead of max amount, use {@link #tryFluidTransfer(IFluidHandler, IFluidHandler, FluidStack, boolean)}
|
||||
* To transfer as much as possible, use {@link Integer#MAX_VALUE} for maxAmount.
|
||||
*
|
||||
* @param fluidDestination The fluid handler to be filled.
|
||||
* @param fluidSource The fluid handler to be drained.
|
||||
* @param maxAmount The largest amount of fluid that should be transferred.
|
||||
* @param doTransfer True if the transfer should actually be done, false if it should be simulated.
|
||||
* @return the fluidStack that was transferred from the source to the destination. null on failure.
|
||||
*/
|
||||
@Nullable
|
||||
public static FluidStack tryFluidTransfer(IFluidHandler fluidDestination, IFluidHandler fluidSource, int maxAmount, boolean doTransfer)
|
||||
{
|
||||
FluidStack drainable = fluidSource.drain(maxAmount, false);
|
||||
if (drainable != null && drainable.amount > 0)
|
||||
{
|
||||
return tryFluidTransfer_Internal(fluidDestination, fluidSource, drainable, doTransfer);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a destination fluid handler from a source fluid handler using a specific fluid.
|
||||
* To specify a max amount to transfer instead of specific fluid, use {@link #tryFluidTransfer(IFluidHandler, IFluidHandler, int, boolean)}
|
||||
* To transfer as much as possible, use {@link Integer#MAX_VALUE} for resource.amount.
|
||||
*
|
||||
* @param fluidDestination The fluid handler to be filled.
|
||||
* @param fluidSource The fluid handler to be drained.
|
||||
* @param resource The fluid that should be transferred. Amount represents the maximum amount to transfer.
|
||||
* @param doTransfer True if the transfer should actually be done, false if it should be simulated.
|
||||
* @return the fluidStack that was transferred from the source to the destination. null on failure.
|
||||
*/
|
||||
@Nullable
|
||||
public static FluidStack tryFluidTransfer(IFluidHandler fluidDestination, IFluidHandler fluidSource, FluidStack resource, boolean doTransfer)
|
||||
{
|
||||
FluidStack drainable = fluidSource.drain(resource, false);
|
||||
if (drainable != null && drainable.amount > 0 && resource.isFluidEqual(drainable))
|
||||
{
|
||||
return tryFluidTransfer_Internal(fluidDestination, fluidSource, drainable, doTransfer);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for filling a destination fluid handler from a source fluid handler using a specific fluid.
|
||||
* Assumes that "drainable" can be drained from "fluidSource".
|
||||
*
|
||||
* Modders: Instead of this method, use {@link #tryFluidTransfer(IFluidHandler, IFluidHandler, FluidStack, boolean)}
|
||||
* or {@link #tryFluidTransfer(IFluidHandler, IFluidHandler, int, boolean)}.
|
||||
*/
|
||||
@Nullable
|
||||
private static FluidStack tryFluidTransfer_Internal(IFluidHandler fluidDestination, IFluidHandler fluidSource, FluidStack drainable, boolean doTransfer)
|
||||
{
|
||||
int fillableAmount = fluidDestination.fill(drainable, false);
|
||||
if (fillableAmount > 0)
|
||||
{
|
||||
if (doTransfer)
|
||||
{
|
||||
FluidStack drained = fluidSource.drain(fillableAmount, true);
|
||||
if (drained != null)
|
||||
{
|
||||
drained.amount = fluidDestination.fill(drained, true);
|
||||
return drained;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
drainable.amount = fillableAmount;
|
||||
return drainable;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get an {@link IFluidHandlerItem} for an itemStack.
|
||||
*
|
||||
* The itemStack passed in here WILL be modified, the {@link IFluidHandlerItem} acts on it directly.
|
||||
* Some {@link IFluidHandlerItem} will change the item entirely, always use {@link IFluidHandlerItem#getContainer()}
|
||||
* after using the fluid handler to get the resulting item back.
|
||||
*
|
||||
* Note that the itemStack MUST have a stackSize of 1 if you want to fill or drain it.
|
||||
* You can't fill or drain multiple items at once, if you do then liquid is multiplied or destroyed.
|
||||
*
|
||||
* Vanilla buckets will be converted to universal buckets if they are enabled.
|
||||
*
|
||||
* Returns null if the itemStack passed in does not have a fluid handler.
|
||||
*/
|
||||
@Nullable
|
||||
public static IFluidHandlerItem getFluidHandler(@Nonnull ItemStack itemStack)
|
||||
{
|
||||
if (itemStack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null))
|
||||
{
|
||||
return itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the fluid contained in an itemStack
|
||||
*/
|
||||
@Nullable
|
||||
public static FluidStack getFluidContained(@Nonnull ItemStack container)
|
||||
{
|
||||
if (!container.isEmpty())
|
||||
{
|
||||
container = ItemHandlerHelper.copyStackWithSize(container, 1);
|
||||
IFluidHandlerItem fluidHandler = getFluidHandler(container);
|
||||
if (fluidHandler != null)
|
||||
{
|
||||
return fluidHandler.drain(Integer.MAX_VALUE, false);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get an IFluidHandler for at a block position.
|
||||
*
|
||||
* Returns null if there is no valid fluid handler.
|
||||
*/
|
||||
@Nullable
|
||||
public static IFluidHandler getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side)
|
||||
{
|
||||
IBlockState state = world.getBlockState(blockPos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block.hasTileEntity(state))
|
||||
{
|
||||
TileEntity tileEntity = world.getTileEntity(blockPos);
|
||||
if (tileEntity != null && tileEntity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side))
|
||||
{
|
||||
return tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side);
|
||||
}
|
||||
}
|
||||
if (block instanceof IFluidBlock)
|
||||
{
|
||||
return new FluidBlockWrapper((IFluidBlock) block, world, blockPos);
|
||||
}
|
||||
else if (block instanceof BlockLiquid)
|
||||
{
|
||||
return new BlockLiquidWrapper((BlockLiquid) block, world, blockPos);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to pick up a fluid in the world and put it in an empty container item.
|
||||
*
|
||||
* @param emptyContainer The empty container to fill.
|
||||
* Will not be modified directly, if modifications are necessary a modified copy is returned in the result.
|
||||
* @param playerIn The player filling the container. Optional.
|
||||
* @param worldIn The world the fluid is in.
|
||||
* @param pos The position of the fluid in the world.
|
||||
* @param side The side of the fluid that is being drained.
|
||||
* @return a {@link FluidActionResult} holding the result and the resulting container.
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryPickUpFluid(@Nonnull ItemStack emptyContainer, @Nullable EntityPlayer playerIn, World worldIn, BlockPos pos, EnumFacing side)
|
||||
{
|
||||
if (emptyContainer.isEmpty() || worldIn == null || pos == null)
|
||||
{
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
IBlockState state = worldIn.getBlockState(pos);
|
||||
Block block = state.getBlock();
|
||||
|
||||
if (block instanceof IFluidBlock || block instanceof BlockLiquid)
|
||||
{
|
||||
IFluidHandler targetFluidHandler = getFluidHandler(worldIn, pos, side);
|
||||
if (targetFluidHandler != null)
|
||||
{
|
||||
return tryFillContainer(emptyContainer, targetFluidHandler, Integer.MAX_VALUE, playerIn, true);
|
||||
}
|
||||
}
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ItemStack version of {@link #tryPlaceFluid(EntityPlayer, World, BlockPos, IFluidHandler, FluidStack)}.
|
||||
* Use the returned {@link FluidActionResult} to update the container ItemStack.
|
||||
*
|
||||
* @param player Player who places the fluid. May be null for blocks like dispensers.
|
||||
* @param world World to place the fluid in
|
||||
* @param pos The position in the world to place the fluid block
|
||||
* @param container The fluid container holding the fluidStack to place
|
||||
* @param resource The fluidStack to place
|
||||
* @return the container's ItemStack with the remaining amount of fluid if the placement was successful, null otherwise
|
||||
*/
|
||||
@Nonnull
|
||||
public static FluidActionResult tryPlaceFluid(@Nullable EntityPlayer player, World world, BlockPos pos, @Nonnull ItemStack container, FluidStack resource)
|
||||
{
|
||||
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
|
||||
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
|
||||
if (containerFluidHandler != null && tryPlaceFluid(player, world, pos, containerFluidHandler, resource))
|
||||
{
|
||||
return new FluidActionResult(containerFluidHandler.getContainer());
|
||||
}
|
||||
return FluidActionResult.FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to place a fluid resource into the world as a block and drains the fluidSource.
|
||||
* Makes a fluid emptying or vaporization sound when successful.
|
||||
* Honors the amount of fluid contained by the used container.
|
||||
* Checks if water-like fluids should vaporize like in the nether.
|
||||
*
|
||||
* Modeled after {@link ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)}
|
||||
*
|
||||
* @param player Player who places the fluid. May be null for blocks like dispensers.
|
||||
* @param world World to place the fluid in
|
||||
* @param pos The position in the world to place the fluid block
|
||||
* @param fluidSource The fluid source holding the fluidStack to place
|
||||
* @param resource The fluidStack to place.
|
||||
* @return true if the placement was successful, false otherwise
|
||||
*/
|
||||
public static boolean tryPlaceFluid(@Nullable EntityPlayer player, World world, BlockPos pos, IFluidHandler fluidSource, FluidStack resource)
|
||||
{
|
||||
if (world == null || resource == null || pos == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Fluid fluid = resource.getFluid();
|
||||
if (fluid == null || !fluid.canBePlacedInWorld())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fluidSource.drain(resource, false) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that we can place the fluid at the destination
|
||||
IBlockState destBlockState = world.getBlockState(pos);
|
||||
Material destMaterial = destBlockState.getMaterial();
|
||||
boolean isDestNonSolid = !destMaterial.isSolid();
|
||||
boolean isDestReplaceable = destBlockState.getBlock().isReplaceable(world, pos);
|
||||
if (!world.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable)
|
||||
{
|
||||
return false; // Non-air, solid, unreplacable block. We can't put fluid here.
|
||||
}
|
||||
|
||||
if (world.provider.doesWaterVaporize() && fluid.doesVaporize(resource))
|
||||
{
|
||||
FluidStack result = fluidSource.drain(resource, true);
|
||||
if (result != null)
|
||||
{
|
||||
result.getFluid().vaporize(player, world, pos, result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This fluid handler places the fluid block when filled
|
||||
IFluidHandler handler = getFluidBlockHandler(fluid, world, pos);
|
||||
FluidStack result = tryFluidTransfer(handler, fluidSource, resource, true);
|
||||
if (result != null)
|
||||
{
|
||||
SoundEvent soundevent = resource.getFluid().getEmptySound(resource);
|
||||
world.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for getting a fluid block handler for placing a fluid.
|
||||
*
|
||||
* Modders: Instead of this method, use {@link #tryPlaceFluid(EntityPlayer, World, BlockPos, ItemStack, FluidStack)}
|
||||
* or {@link #tryPlaceFluid(EntityPlayer, World, BlockPos, IFluidHandler, FluidStack)}
|
||||
*/
|
||||
private static IFluidHandler getFluidBlockHandler(Fluid fluid, World world, BlockPos pos)
|
||||
{
|
||||
Block block = fluid.getBlock();
|
||||
if (block instanceof IFluidBlock)
|
||||
{
|
||||
return new FluidBlockWrapper((IFluidBlock) block, world, pos);
|
||||
}
|
||||
else if (block instanceof BlockLiquid)
|
||||
{
|
||||
return new BlockLiquidWrapper((BlockLiquid) block, world, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BlockWrapper(block, world, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a block when a fluid is placed in the same position.
|
||||
* Modeled after {@link ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)}
|
||||
*
|
||||
* This is a helper method for implementing {@link IFluidBlock#place(World, BlockPos, FluidStack, boolean)}.
|
||||
*
|
||||
* @param world the world that the fluid will be placed in
|
||||
* @param pos the location that the fluid will be placed
|
||||
*/
|
||||
public static void destroyBlockOnFluidPlacement(World world, BlockPos pos)
|
||||
{
|
||||
if (!world.isRemote)
|
||||
{
|
||||
IBlockState destBlockState = world.getBlockState(pos);
|
||||
Material destMaterial = destBlockState.getMaterial();
|
||||
boolean isDestNonSolid = !destMaterial.isSolid();
|
||||
boolean isDestReplaceable = destBlockState.getBlock().isReplaceable(world, pos);
|
||||
if ((isDestNonSolid || isDestReplaceable) && !destMaterial.isLiquid())
|
||||
{
|
||||
world.destroyBlock(pos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fluidStack contents used to fill the bucket.
|
||||
* FluidStack is used instead of Fluid to preserve fluid NBT, the amount is ignored.
|
||||
* @return a filled vanilla bucket or filled universal bucket.
|
||||
* Returns empty itemStack if none of the enabled buckets can hold the fluid.
|
||||
*/
|
||||
@Nonnull
|
||||
public static ItemStack getFilledBucket(@Nonnull FluidStack fluidStack)
|
||||
{
|
||||
Fluid fluid = fluidStack.getFluid();
|
||||
|
||||
if (fluidStack.tag == null || fluidStack.tag.hasNoTags())
|
||||
{
|
||||
if (fluid == FluidRegistry.WATER)
|
||||
{
|
||||
return new ItemStack(Items.WATER_BUCKET);
|
||||
}
|
||||
else if (fluid == FluidRegistry.LAVA)
|
||||
{
|
||||
return new ItemStack(Items.LAVA_BUCKET);
|
||||
}
|
||||
else if (fluid.getName().equals("milk"))
|
||||
{
|
||||
return new ItemStack(Items.MILK_BUCKET);
|
||||
}
|
||||
}
|
||||
|
||||
if (FluidRegistry.isUniversalBucketEnabled() && FluidRegistry.getBucketFluids().contains(fluid))
|
||||
{
|
||||
UniversalBucket bucket = ForgeModContainer.getInstance().universalBucket;
|
||||
ItemStack filledBucket = new ItemStack(bucket);
|
||||
FluidStack fluidContents = new FluidStack(fluidStack, bucket.getCapacity());
|
||||
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
fluidContents.writeToNBT(tag);
|
||||
filledBucket.setTagCompound(tag);
|
||||
|
||||
return filledBucket;
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Implement this interface on Block classes which represent world-placeable Fluids.
|
||||
*
|
||||
* NOTE: Using/extending the reference implementations {@link BlockFluidBase} is encouraged.
|
||||
*
|
||||
*/
|
||||
public interface IFluidBlock
|
||||
{
|
||||
/**
|
||||
* Returns the Fluid associated with this Block.
|
||||
*/
|
||||
Fluid getFluid();
|
||||
|
||||
/**
|
||||
* Attempts to place the block at a given position. The placed block's level will correspond
|
||||
* to the provided fluid amount.
|
||||
* This method should be called by fluid containers such as buckets, but it is recommended
|
||||
* to use {@link FluidUtil}.
|
||||
*
|
||||
* @param world the world to place the block in
|
||||
* @param pos the position to place the block at
|
||||
* @param fluidStack the fluid stack to get the required data from
|
||||
* @param doPlace if false, the placement will only be simulated
|
||||
* @return the amount of fluid extracted from the provided stack to achieve some fluid level
|
||||
*/
|
||||
int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace);
|
||||
|
||||
/**
|
||||
* Attempt to drain the block. This method should be called by devices such as pumps.
|
||||
*
|
||||
* NOTE: The block is intended to handle its own state changes.
|
||||
*
|
||||
* @param doDrain
|
||||
* If false, the drain will only be simulated.
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack drain(World world, BlockPos pos, boolean doDrain);
|
||||
|
||||
/**
|
||||
* Check to see if a block can be drained. This method should be called by devices such as
|
||||
* pumps.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean canDrain(World world, BlockPos pos);
|
||||
|
||||
/**
|
||||
* Returns the amount of a single block is filled. Value between 0 and 1.
|
||||
* 1 meaning the entire 1x1x1 cube is full, 0 meaning completely empty.
|
||||
*
|
||||
* If the return value is negative. It will be treated as filling the block
|
||||
* from the top down instead of bottom up.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
float getFilledPercentage(World world, BlockPos pos);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A tank is the unit of interaction with Fluid inventories.
|
||||
*
|
||||
* A reference implementation can be found at {@link FluidTank}.
|
||||
*/
|
||||
public interface IFluidTank
|
||||
{
|
||||
/**
|
||||
* @return FluidStack representing the fluid in the tank, null if the tank is empty.
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack getFluid();
|
||||
|
||||
/**
|
||||
* @return Current amount of fluid in the tank.
|
||||
*/
|
||||
int getFluidAmount();
|
||||
|
||||
/**
|
||||
* @return Capacity of this fluid tank.
|
||||
*/
|
||||
int getCapacity();
|
||||
|
||||
/**
|
||||
* Returns a wrapper object {@link FluidTankInfo } containing the capacity of the tank and the
|
||||
* FluidStack it holds.
|
||||
*
|
||||
* Should prevent manipulation of the IFluidTank. See {@link FluidTank}.
|
||||
*
|
||||
* @return State information for the IFluidTank.
|
||||
*/
|
||||
FluidTankInfo getInfo();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource
|
||||
* FluidStack attempting to fill the tank.
|
||||
* @param doFill
|
||||
* If false, the fill will only be simulated.
|
||||
* @return Amount of fluid that was accepted by the tank.
|
||||
*/
|
||||
int fill(FluidStack resource, boolean doFill);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param maxDrain
|
||||
* Maximum amount of fluid to be removed from the container.
|
||||
* @param doDrain
|
||||
* If false, the drain will only be simulated.
|
||||
* @return Amount of fluid that was removed from the tank.
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack drain(int maxDrain, boolean doDrain);
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids;
|
||||
|
||||
import net.minecraft.block.BlockDispenser;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.stats.StatList;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.text.translation.I18n;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.event.entity.player.FillBucketEvent;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.items.ItemHandlerHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A universal bucket that can hold any liquid
|
||||
*/
|
||||
public class UniversalBucket extends Item
|
||||
{
|
||||
|
||||
private final int capacity; // how much the bucket holds
|
||||
@Nonnull
|
||||
private final ItemStack empty; // empty item to return and recognize when filling
|
||||
private final boolean nbtSensitive;
|
||||
|
||||
public UniversalBucket()
|
||||
{
|
||||
this(Fluid.BUCKET_VOLUME, new ItemStack(Items.BUCKET), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param capacity Capacity of the container
|
||||
* @param empty Item used for filling with the bucket event and returned when emptied
|
||||
* @param nbtSensitive Whether the empty item is NBT sensitive (usually true if empty and full are the same items)
|
||||
*/
|
||||
public UniversalBucket(int capacity, @Nonnull ItemStack empty, boolean nbtSensitive)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.empty = empty;
|
||||
this.nbtSensitive = nbtSensitive;
|
||||
|
||||
this.setMaxStackSize(1);
|
||||
|
||||
this.setCreativeTab(CreativeTabs.MISC);
|
||||
|
||||
BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject(this, DispenseFluidContainer.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainerItem(@Nonnull ItemStack stack)
|
||||
{
|
||||
return !getEmpty().isEmpty();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getContainerItem(@Nonnull ItemStack itemStack)
|
||||
{
|
||||
if (!getEmpty().isEmpty())
|
||||
{
|
||||
// Create a copy such that the game can't mess with it
|
||||
return getEmpty().copy();
|
||||
}
|
||||
return super.getContainerItem(itemStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of items with the same ID, but different meta (eg: dye returns 16 items)
|
||||
*/
|
||||
@Override
|
||||
public void getSubItems(@Nullable CreativeTabs tab, @Nonnull NonNullList<ItemStack> subItems)
|
||||
{
|
||||
if (!this.isInCreativeTab(tab))
|
||||
return;
|
||||
for (Fluid fluid : FluidRegistry.getRegisteredFluids().values())
|
||||
{
|
||||
if (fluid != FluidRegistry.WATER && fluid != FluidRegistry.LAVA && !fluid.getName().equals("milk"))
|
||||
{
|
||||
// add all fluids that the bucket can be filled with
|
||||
FluidStack fs = new FluidStack(fluid, getCapacity());
|
||||
ItemStack stack = new ItemStack(this);
|
||||
IFluidHandlerItem fluidHandler = new FluidBucketWrapper(stack);
|
||||
if (fluidHandler.fill(fs, true) == fs.amount)
|
||||
{
|
||||
ItemStack filled = fluidHandler.getContainer();
|
||||
subItems.add(filled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getItemStackDisplayName(@Nonnull ItemStack stack)
|
||||
{
|
||||
FluidStack fluidStack = getFluid(stack);
|
||||
if (fluidStack == null)
|
||||
{
|
||||
if(!getEmpty().isEmpty())
|
||||
{
|
||||
return getEmpty().getDisplayName();
|
||||
}
|
||||
return super.getItemStackDisplayName(stack);
|
||||
}
|
||||
|
||||
String unloc = this.getUnlocalizedNameInefficiently(stack);
|
||||
|
||||
if (I18n.canTranslate(unloc + "." + fluidStack.getFluid().getName()))
|
||||
{
|
||||
return I18n.translateToLocal(unloc + "." + fluidStack.getFluid().getName());
|
||||
}
|
||||
|
||||
return I18n.translateToLocalFormatted(unloc + ".name", fluidStack.getLocalizedName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the equipped item is right clicked.
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public ActionResult<ItemStack> onItemRightClick(@Nonnull World world, @Nonnull EntityPlayer player, @Nonnull EnumHand hand)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItem(hand);
|
||||
FluidStack fluidStack = getFluid(itemstack);
|
||||
// empty bucket shouldn't exist, do nothing since it should be handled by the bucket event
|
||||
if (fluidStack == null)
|
||||
{
|
||||
return ActionResult.newResult(EnumActionResult.PASS, itemstack);
|
||||
}
|
||||
|
||||
// clicked on a block?
|
||||
RayTraceResult mop = this.rayTrace(world, player, false);
|
||||
|
||||
ActionResult<ItemStack> ret = ForgeEventFactory.onBucketUse(player, world, itemstack, mop);
|
||||
if (ret != null) return ret;
|
||||
|
||||
if(mop == null || mop.typeOfHit != RayTraceResult.Type.BLOCK)
|
||||
{
|
||||
return ActionResult.newResult(EnumActionResult.PASS, itemstack);
|
||||
}
|
||||
|
||||
BlockPos clickPos = mop.getBlockPos();
|
||||
// can we place liquid there?
|
||||
if (world.isBlockModifiable(player, clickPos))
|
||||
{
|
||||
// the block adjacent to the side we clicked on
|
||||
BlockPos targetPos = clickPos.offset(mop.sideHit);
|
||||
|
||||
// can the player place there?
|
||||
if (player.canPlayerEdit(targetPos, mop.sideHit, itemstack))
|
||||
{
|
||||
// try placing liquid
|
||||
FluidActionResult result = FluidUtil.tryPlaceFluid(player, world, targetPos, itemstack, fluidStack);
|
||||
if (result.isSuccess() && !player.capabilities.isCreativeMode)
|
||||
{
|
||||
// success!
|
||||
player.addStat(StatList.getObjectUseStats(this));
|
||||
|
||||
itemstack.shrink(1);
|
||||
ItemStack drained = result.getResult();
|
||||
ItemStack emptyStack = !drained.isEmpty() ? drained.copy() : new ItemStack(this);
|
||||
|
||||
// check whether we replace the item or add the empty one to the inventory
|
||||
if (itemstack.isEmpty())
|
||||
{
|
||||
return ActionResult.newResult(EnumActionResult.SUCCESS, emptyStack);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add empty bucket to player inventory
|
||||
ItemHandlerHelper.giveItemToPlayer(player, emptyStack);
|
||||
return ActionResult.newResult(EnumActionResult.SUCCESS, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// couldn't place liquid there2
|
||||
return ActionResult.newResult(EnumActionResult.FAIL, itemstack);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // low priority so other mods can handle their stuff first
|
||||
public void onFillBucket(FillBucketEvent event)
|
||||
{
|
||||
if (event.getResult() != Event.Result.DEFAULT)
|
||||
{
|
||||
// event was already handled
|
||||
return;
|
||||
}
|
||||
|
||||
// not for us to handle
|
||||
ItemStack emptyBucket = event.getEmptyBucket();
|
||||
if (emptyBucket.isEmpty() ||
|
||||
!emptyBucket.isItemEqual(getEmpty()) ||
|
||||
(isNbtSensitive() && ItemStack.areItemStackTagsEqual(emptyBucket, getEmpty())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// needs to target a block
|
||||
RayTraceResult target = event.getTarget();
|
||||
if (target == null || target.typeOfHit != RayTraceResult.Type.BLOCK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
World world = event.getWorld();
|
||||
BlockPos pos = target.getBlockPos();
|
||||
|
||||
ItemStack singleBucket = emptyBucket.copy();
|
||||
singleBucket.setCount(1);
|
||||
|
||||
FluidActionResult filledResult = FluidUtil.tryPickUpFluid(singleBucket, event.getEntityPlayer(), world, pos, target.sideHit);
|
||||
if (filledResult.isSuccess())
|
||||
{
|
||||
event.setResult(Event.Result.ALLOW);
|
||||
event.setFilledBucket(filledResult.getResult());
|
||||
}
|
||||
else
|
||||
{
|
||||
// cancel event, otherwise the vanilla minecraft ItemBucket would
|
||||
// convert it into a water/lava bucket depending on the blocks material
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the NBT-sensitive version {@link FluidUtil#getFilledBucket(FluidStack)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public static ItemStack getFilledBucket(@Nonnull UniversalBucket item, Fluid fluid)
|
||||
{
|
||||
return FluidUtil.getFilledBucket(new FluidStack(fluid, Fluid.BUCKET_VOLUME));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FluidStack getFluid(@Nonnull ItemStack container)
|
||||
{
|
||||
return FluidStack.loadFluidStackFromNBT(container.getTagCompound());
|
||||
}
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ItemStack getEmpty()
|
||||
{
|
||||
return empty;
|
||||
}
|
||||
|
||||
public boolean isNbtSensitive()
|
||||
{
|
||||
return nbtSensitive;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getCreatorModId(@Nonnull ItemStack itemStack)
|
||||
{
|
||||
FluidStack fluidStack = getFluid(itemStack);
|
||||
String modId = FluidRegistry.getModId(fluidStack);
|
||||
return modId != null ? modId : super.getCreatorModId(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, NBTTagCompound nbt)
|
||||
{
|
||||
return new FluidBucketWrapper(stack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidTank;
|
||||
import net.minecraftforge.fluids.IFluidTank;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack;
|
||||
|
||||
public class CapabilityFluidHandler
|
||||
{
|
||||
@CapabilityInject(IFluidHandler.class)
|
||||
public static Capability<IFluidHandler> FLUID_HANDLER_CAPABILITY = null;
|
||||
@CapabilityInject(IFluidHandlerItem.class)
|
||||
public static Capability<IFluidHandlerItem> FLUID_HANDLER_ITEM_CAPABILITY = null;
|
||||
|
||||
public static void register()
|
||||
{
|
||||
CapabilityManager.INSTANCE.register(IFluidHandler.class, new DefaultFluidHandlerStorage<>(), () -> new FluidTank(Fluid.BUCKET_VOLUME));
|
||||
|
||||
CapabilityManager.INSTANCE.register(IFluidHandlerItem.class, new DefaultFluidHandlerStorage<>(), () -> new FluidHandlerItemStack(new ItemStack(Items.BUCKET), Fluid.BUCKET_VOLUME));
|
||||
}
|
||||
|
||||
private static class DefaultFluidHandlerStorage<T extends IFluidHandler> implements Capability.IStorage<T> {
|
||||
@Override
|
||||
public NBTBase writeNBT(Capability<T> capability, T instance, EnumFacing side)
|
||||
{
|
||||
if (!(instance instanceof IFluidTank))
|
||||
throw new RuntimeException("IFluidHandler instance does not implement IFluidTank");
|
||||
NBTTagCompound nbt = new NBTTagCompound();
|
||||
IFluidTank tank = (IFluidTank) instance;
|
||||
FluidStack fluid = tank.getFluid();
|
||||
if (fluid != null)
|
||||
{
|
||||
fluid.writeToNBT(nbt);
|
||||
}
|
||||
else
|
||||
{
|
||||
nbt.setString("Empty", "");
|
||||
}
|
||||
nbt.setInteger("Capacity", tank.getCapacity());
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(Capability<T> capability, T instance, EnumFacing side, NBTBase nbt)
|
||||
{
|
||||
if (!(instance instanceof FluidTank))
|
||||
throw new RuntimeException("IFluidHandler instance is not instance of FluidTank");
|
||||
NBTTagCompound tags = (NBTTagCompound) nbt;
|
||||
FluidTank tank = (FluidTank) instance;
|
||||
tank.setCapacity(tags.getInteger("Capacity"));
|
||||
tank.readFromNBT(tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidTankInfo;
|
||||
|
||||
/**
|
||||
* Basic implementation of {@link IFluidTankProperties}.
|
||||
*/
|
||||
public class FluidTankProperties implements IFluidTankProperties
|
||||
{
|
||||
public static FluidTankProperties[] convert(FluidTankInfo[] fluidTankInfos)
|
||||
{
|
||||
FluidTankProperties[] properties = new FluidTankProperties[fluidTankInfos.length];
|
||||
for (int i = 0; i < fluidTankInfos.length; i++)
|
||||
{
|
||||
FluidTankInfo info = fluidTankInfos[i];
|
||||
properties[i] = new FluidTankProperties(info.fluid, info.capacity);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final FluidStack contents;
|
||||
private final int capacity;
|
||||
private final boolean canFill;
|
||||
private final boolean canDrain;
|
||||
|
||||
public FluidTankProperties(@Nullable FluidStack contents, int capacity)
|
||||
{
|
||||
this(contents, capacity, true, true);
|
||||
}
|
||||
|
||||
public FluidTankProperties(@Nullable FluidStack contents, int capacity, boolean canFill, boolean canDrain)
|
||||
{
|
||||
this.contents = contents;
|
||||
this.capacity = capacity;
|
||||
this.canFill = canFill;
|
||||
this.canDrain = canDrain;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack getContents()
|
||||
{
|
||||
return contents == null ? null : contents.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity()
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFill()
|
||||
{
|
||||
return canFill;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrain()
|
||||
{
|
||||
return canDrain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFillFluidType(FluidStack fluidStack)
|
||||
{
|
||||
return canFill;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrainFluidType(FluidStack fluidStack)
|
||||
{
|
||||
return canDrain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidTank;
|
||||
|
||||
/**
|
||||
* Basic {@link IFluidTankProperties} wrapper for {@link FluidTank}.
|
||||
*/
|
||||
public class FluidTankPropertiesWrapper implements IFluidTankProperties
|
||||
{
|
||||
protected final FluidTank tank;
|
||||
|
||||
public FluidTankPropertiesWrapper(FluidTank tank)
|
||||
{
|
||||
this.tank = tank;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack getContents()
|
||||
{
|
||||
FluidStack contents = tank.getFluid();
|
||||
return contents == null ? null : contents.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity()
|
||||
{
|
||||
return tank.getCapacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFill()
|
||||
{
|
||||
return tank.canFill();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrain()
|
||||
{
|
||||
return tank.canDrain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFillFluidType(FluidStack fluidStack)
|
||||
{
|
||||
return tank.canFillFluidType(fluidStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrainFluidType(FluidStack fluidStack)
|
||||
{
|
||||
return tank.canDrainFluidType(fluidStack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fluids.*;
|
||||
|
||||
/**
|
||||
* Implement this interface as a capability which should handle fluids, generally storing them in
|
||||
* one or more internal {@link IFluidTank} objects.
|
||||
* <p/>
|
||||
* A reference implementation is provided {@link TileFluidHandler}.
|
||||
*/
|
||||
public interface IFluidHandler
|
||||
{
|
||||
/**
|
||||
* Returns an array of objects which represent the internal tanks.
|
||||
* These objects cannot be used to manipulate the internal tanks.
|
||||
*
|
||||
* @return Properties for the relevant internal tanks.
|
||||
*/
|
||||
IFluidTankProperties[] getTankProperties();
|
||||
|
||||
/**
|
||||
* Fills fluid into internal tanks, distribution is left entirely to the IFluidHandler.
|
||||
*
|
||||
* @param resource FluidStack representing the Fluid and maximum amount of fluid to be filled.
|
||||
* @param doFill If false, fill will only be simulated.
|
||||
* @return Amount of resource that was (or would have been, if simulated) filled.
|
||||
*/
|
||||
int fill(FluidStack resource, boolean doFill);
|
||||
|
||||
/**
|
||||
* Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler.
|
||||
*
|
||||
* @param resource FluidStack representing the Fluid and maximum amount of fluid to be drained.
|
||||
* @param doDrain If false, drain will only be simulated.
|
||||
* @return FluidStack representing the Fluid and amount that was (or would have been, if
|
||||
* simulated) drained.
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack drain(FluidStack resource, boolean doDrain);
|
||||
|
||||
/**
|
||||
* Drains fluid out of internal tanks, distribution is left entirely to the IFluidHandler.
|
||||
* <p/>
|
||||
* This method is not Fluid-sensitive.
|
||||
*
|
||||
* @param maxDrain Maximum amount of fluid to drain.
|
||||
* @param doDrain If false, drain will only be simulated.
|
||||
* @return FluidStack representing the Fluid and amount that was (or would have been, if
|
||||
* simulated) drained.
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack drain(int maxDrain, boolean doDrain);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* ItemStacks handled by an {@link IFluidHandler} may change, so this class allows
|
||||
* users of the fluid handler to get the container after it has been used.
|
||||
*/
|
||||
public interface IFluidHandlerItem extends IFluidHandler
|
||||
{
|
||||
/**
|
||||
* Get the container currently acted on by this fluid handler.
|
||||
* The ItemStack may be different from its initial state, in the case of fluid containers that have different items
|
||||
* for their filled and empty states.
|
||||
* May be an empty item if the container was drained and is consumable.
|
||||
*/
|
||||
@Nonnull
|
||||
ItemStack getContainer();
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
/**
|
||||
* Simplified Read-only Information about the internals of an {@link IFluidHandler}.
|
||||
* This is useful for displaying information, and as hints for interacting with it.
|
||||
* These properties are constant and do not depend on the fluid contents (except the contents themselves, of course).
|
||||
*
|
||||
* The information here may not tell the full story of how the tank actually works,
|
||||
* for real fluid transactions you must use {@link IFluidHandler} to simulate, check, and then interact.
|
||||
* None of the information in these properties is required to successfully interact using a {@link IFluidHandler}.
|
||||
*/
|
||||
public interface IFluidTankProperties
|
||||
{
|
||||
/**
|
||||
* @return A copy of the fluid contents of this tank. May be null.
|
||||
* To modify the contents, use {@link IFluidHandler}.
|
||||
*/
|
||||
@Nullable
|
||||
FluidStack getContents();
|
||||
|
||||
/**
|
||||
* @return The maximum amount of fluid this tank can hold, in millibuckets.
|
||||
*/
|
||||
int getCapacity();
|
||||
|
||||
/**
|
||||
* Returns true if the tank can be filled at any time (even if it is currently full).
|
||||
* It does not consider the contents or capacity of the tank.
|
||||
*
|
||||
* This value is constant. If the tank behavior is more complicated, returns true.
|
||||
*/
|
||||
boolean canFill();
|
||||
|
||||
/**
|
||||
* Returns true if the tank can be drained at any time (even if it is currently empty).
|
||||
* It does not consider the contents or capacity of the tank.
|
||||
*
|
||||
* This value is constant. If the tank behavior is more complicated, returns true.
|
||||
*/
|
||||
boolean canDrain();
|
||||
|
||||
/**
|
||||
* Returns true if the tank can be filled with a specific type of fluid.
|
||||
* Used as a filter for fluid types.
|
||||
*
|
||||
* Does not consider the current contents or capacity of the tank,
|
||||
* only whether it could ever fill with this type of fluid.
|
||||
* {@link FluidStack} is used here because fluid properties can depend on NBT, the amount is ignored.
|
||||
*/
|
||||
boolean canFillFluidType(FluidStack fluidStack);
|
||||
|
||||
/**
|
||||
* Returns true if the tank can drain out this a specific of fluid.
|
||||
* Used as a filter for fluid types.
|
||||
*
|
||||
* Does not consider the current contents or capacity of the tank,
|
||||
* only whether it could ever drain out this type of fluid.
|
||||
* {@link FluidStack} is used here because fluid properties can depend on NBT, the amount is ignored.
|
||||
*/
|
||||
boolean canDrainFluidType(FluidStack fluidStack);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A simple fluid container, to replace the functionality of the old FluidContainerRegistry and IFluidContainerItem.
|
||||
* This fluid container may be set so that is can only completely filled or empty. (binary)
|
||||
* It may also be set so that it gets consumed when it is drained. (consumable)
|
||||
*/
|
||||
public class ItemFluidContainer extends Item
|
||||
{
|
||||
protected final int capacity;
|
||||
|
||||
/**
|
||||
* @param capacity The maximum capacity of this fluid container.
|
||||
*/
|
||||
public ItemFluidContainer(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, @Nullable NBTTagCompound nbt)
|
||||
{
|
||||
return new FluidHandlerItemStack(stack, capacity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidTank;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TileFluidHandler extends TileEntity
|
||||
{
|
||||
protected FluidTank tank = new FluidTank(Fluid.BUCKET_VOLUME);
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound tag)
|
||||
{
|
||||
super.readFromNBT(tag);
|
||||
tank.readFromNBT(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound tag)
|
||||
{
|
||||
tag = super.writeToNBT(tag);
|
||||
tank.writeToNBT(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || super.hasCapability(capability, facing);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)
|
||||
return (T) tank;
|
||||
return super.getCapability(capability, facing);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidTankInfo;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.IFluidTank;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
public class EmptyFluidHandler implements IFluidHandler, IFluidTank
|
||||
{
|
||||
public static final EmptyFluidHandler INSTANCE = new EmptyFluidHandler();
|
||||
public static final FluidTankInfo EMPTY_TANK_INFO = new FluidTankInfo(null, 0);
|
||||
public static final IFluidTankProperties EMPTY_TANK_PROPERTIES = new FluidTankProperties(null, 0, false, false);
|
||||
public static final IFluidTankProperties[] EMPTY_TANK_PROPERTIES_ARRAY = new IFluidTankProperties[] { EMPTY_TANK_PROPERTIES };
|
||||
|
||||
protected EmptyFluidHandler() {}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
return EMPTY_TANK_PROPERTIES_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFluidAmount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidTankInfo getInfo()
|
||||
{
|
||||
return EMPTY_TANK_INFO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* FluidHandlerConcatenate is a template class for concatenating multiple handlers into one.
|
||||
* If each tank is restricted to exactly one type of fluid, then use {@link FluidHandlerFluidMap} as it is more efficient.
|
||||
*/
|
||||
public class FluidHandlerConcatenate implements IFluidHandler
|
||||
{
|
||||
protected final IFluidHandler[] subHandlers;
|
||||
|
||||
public FluidHandlerConcatenate(IFluidHandler... subHandlers)
|
||||
{
|
||||
this.subHandlers = subHandlers;
|
||||
}
|
||||
|
||||
public FluidHandlerConcatenate(Collection<IFluidHandler> subHandlers)
|
||||
{
|
||||
this.subHandlers = subHandlers.toArray(new IFluidHandler[subHandlers.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
List<IFluidTankProperties> tanks = Lists.newArrayList();
|
||||
for (IFluidHandler handler : subHandlers)
|
||||
{
|
||||
Collections.addAll(tanks, handler.getTankProperties());
|
||||
}
|
||||
return tanks.toArray(new IFluidTankProperties[tanks.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (resource == null || resource.amount <= 0)
|
||||
return 0;
|
||||
|
||||
resource = resource.copy();
|
||||
|
||||
int totalFillAmount = 0;
|
||||
for (IFluidHandler handler : subHandlers)
|
||||
{
|
||||
int fillAmount = handler.fill(resource, doFill);
|
||||
totalFillAmount += fillAmount;
|
||||
resource.amount -= fillAmount;
|
||||
if (resource.amount <= 0)
|
||||
break;
|
||||
}
|
||||
return totalFillAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (resource == null || resource.amount <= 0)
|
||||
return null;
|
||||
|
||||
resource = resource.copy();
|
||||
|
||||
FluidStack totalDrained = null;
|
||||
for (IFluidHandler handler : subHandlers)
|
||||
{
|
||||
FluidStack drain = handler.drain(resource, doDrain);
|
||||
if (drain != null)
|
||||
{
|
||||
if (totalDrained == null)
|
||||
totalDrained = drain;
|
||||
else
|
||||
totalDrained.amount += drain.amount;
|
||||
|
||||
resource.amount -= drain.amount;
|
||||
if (resource.amount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return totalDrained;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (maxDrain == 0)
|
||||
return null;
|
||||
FluidStack totalDrained = null;
|
||||
for (IFluidHandler handler : subHandlers)
|
||||
{
|
||||
if (totalDrained == null)
|
||||
{
|
||||
totalDrained = handler.drain(maxDrain, doDrain);
|
||||
if (totalDrained != null)
|
||||
{
|
||||
maxDrain -= totalDrained.amount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FluidStack copy = totalDrained.copy();
|
||||
copy.amount = maxDrain;
|
||||
FluidStack drain = handler.drain(copy, doDrain);
|
||||
if (drain != null)
|
||||
{
|
||||
totalDrained.amount += drain.amount;
|
||||
maxDrain -= drain.amount;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDrain <= 0)
|
||||
break;
|
||||
}
|
||||
return totalDrained;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* FluidHandlerFluidMap is a template class for concatenating multiple handlers into one,
|
||||
* where each handler is associated with a different fluid.
|
||||
*/
|
||||
public class FluidHandlerFluidMap implements IFluidHandler
|
||||
{
|
||||
protected final Map<Fluid, IFluidHandler> handlers;
|
||||
|
||||
public FluidHandlerFluidMap()
|
||||
{
|
||||
// LinkedHashMap to ensure iteration order is consistent.
|
||||
this(new LinkedHashMap<Fluid, IFluidHandler>());
|
||||
}
|
||||
|
||||
public FluidHandlerFluidMap(Map<Fluid, IFluidHandler> handlers)
|
||||
{
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public FluidHandlerFluidMap addHandler(Fluid fluid, IFluidHandler handler)
|
||||
{
|
||||
handlers.put(fluid, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
List<IFluidTankProperties> tanks = Lists.newArrayList();
|
||||
for (IFluidHandler iFluidHandler : handlers.values())
|
||||
{
|
||||
Collections.addAll(tanks, iFluidHandler.getTankProperties());
|
||||
}
|
||||
return tanks.toArray(new IFluidTankProperties[tanks.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (resource == null)
|
||||
return 0;
|
||||
IFluidHandler handler = handlers.get(resource.getFluid());
|
||||
if (handler == null)
|
||||
return 0;
|
||||
return handler.fill(resource, doFill);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (resource == null)
|
||||
return null;
|
||||
IFluidHandler handler = handlers.get(resource.getFluid());
|
||||
if (handler == null)
|
||||
return null;
|
||||
return handler.drain(resource, doDrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
for (IFluidHandler handler : handlers.values())
|
||||
{
|
||||
FluidStack drain = handler.drain(maxDrain, doDrain);
|
||||
if (drain != null)
|
||||
return drain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.fluids.*;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
|
||||
/**
|
||||
* FluidHandlerItemStack is a template capability provider for ItemStacks.
|
||||
* Data is stored directly in the vanilla NBT, in the same way as the old ItemFluidContainer.
|
||||
*
|
||||
* This class allows an itemStack to contain any partial level of fluid up to its capacity, unlike {@link FluidHandlerItemStackSimple}
|
||||
*
|
||||
* Additional examples are provided to enable consumable fluid containers (see {@link Consumable}),
|
||||
* fluid containers with different empty and full items (see {@link SwapEmpty},
|
||||
*/
|
||||
public class FluidHandlerItemStack implements IFluidHandlerItem, ICapabilityProvider
|
||||
{
|
||||
public static final String FLUID_NBT_KEY = "Fluid";
|
||||
|
||||
@Nonnull
|
||||
protected ItemStack container;
|
||||
protected int capacity;
|
||||
|
||||
/**
|
||||
* @param container The container itemStack, data is stored on it directly as NBT.
|
||||
* @param capacity The maximum capacity of this fluid tank.
|
||||
*/
|
||||
public FluidHandlerItemStack(@Nonnull ItemStack container, int capacity)
|
||||
{
|
||||
this.container = container;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
NBTTagCompound tagCompound = container.getTagCompound();
|
||||
if (tagCompound == null || !tagCompound.hasKey(FLUID_NBT_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return FluidStack.loadFluidStackFromNBT(tagCompound.getCompoundTag(FLUID_NBT_KEY));
|
||||
}
|
||||
|
||||
protected void setFluid(FluidStack fluid)
|
||||
{
|
||||
if (!container.hasTagCompound())
|
||||
{
|
||||
container.setTagCompound(new NBTTagCompound());
|
||||
}
|
||||
|
||||
NBTTagCompound fluidTag = new NBTTagCompound();
|
||||
fluid.writeToNBT(fluidTag);
|
||||
container.getTagCompound().setTag(FLUID_NBT_KEY, fluidTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
return new FluidTankProperties[] { new FluidTankProperties(getFluid(), capacity) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount <= 0 || !canFillFluidType(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
FluidStack contained = getFluid();
|
||||
if (contained == null)
|
||||
{
|
||||
int fillAmount = Math.min(capacity, resource.amount);
|
||||
|
||||
if (doFill)
|
||||
{
|
||||
FluidStack filled = resource.copy();
|
||||
filled.amount = fillAmount;
|
||||
setFluid(filled);
|
||||
}
|
||||
|
||||
return fillAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (contained.isFluidEqual(resource))
|
||||
{
|
||||
int fillAmount = Math.min(capacity - contained.amount, resource.amount);
|
||||
|
||||
if (doFill && fillAmount > 0) {
|
||||
contained.amount += fillAmount;
|
||||
setFluid(contained);
|
||||
}
|
||||
|
||||
return fillAmount;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount <= 0 || !resource.isFluidEqual(getFluid()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return drain(resource.amount, doDrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || maxDrain <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack contained = getFluid();
|
||||
if (contained == null || contained.amount <= 0 || !canDrainFluidType(contained))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final int drainAmount = Math.min(contained.amount, maxDrain);
|
||||
|
||||
FluidStack drained = contained.copy();
|
||||
drained.amount = drainAmount;
|
||||
|
||||
if (doDrain)
|
||||
{
|
||||
contained.amount -= drainAmount;
|
||||
if (contained.amount == 0)
|
||||
{
|
||||
setContainerToEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
setFluid(contained);
|
||||
}
|
||||
}
|
||||
|
||||
return drained;
|
||||
}
|
||||
|
||||
public boolean canFillFluidType(FluidStack fluid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canDrainFluidType(FluidStack fluid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method for special handling.
|
||||
* Can be used to swap out or destroy the container.
|
||||
*/
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
container.getTagCompound().removeTag(FLUID_NBT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY ? (T) this : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the container item when it's emptied.
|
||||
*/
|
||||
public static class Consumable extends FluidHandlerItemStack
|
||||
{
|
||||
public Consumable(ItemStack container, int capacity)
|
||||
{
|
||||
super(container, capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
super.setContainerToEmpty();
|
||||
container.shrink(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the container item for a different one when it's emptied.
|
||||
*/
|
||||
public static class SwapEmpty extends FluidHandlerItemStack
|
||||
{
|
||||
protected final ItemStack emptyContainer;
|
||||
|
||||
public SwapEmpty(ItemStack container, ItemStack emptyContainer, int capacity)
|
||||
{
|
||||
super(container, capacity);
|
||||
this.emptyContainer = emptyContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
super.setContainerToEmpty();
|
||||
container = emptyContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
|
||||
/**
|
||||
* FluidHandlerItemStackSimple is a template capability provider for ItemStacks.
|
||||
* Data is stored directly in the vanilla NBT, in the same way as the old ItemFluidContainer.
|
||||
*
|
||||
* This implementation only allows item containers to be fully filled or emptied, similar to vanilla buckets.
|
||||
*/
|
||||
public class FluidHandlerItemStackSimple implements IFluidHandlerItem, ICapabilityProvider
|
||||
{
|
||||
public static final String FLUID_NBT_KEY = "Fluid";
|
||||
|
||||
@Nonnull
|
||||
protected ItemStack container;
|
||||
protected int capacity;
|
||||
|
||||
/**
|
||||
* @param container The container itemStack, data is stored on it directly as NBT.
|
||||
* @param capacity The maximum capacity of this fluid tank.
|
||||
*/
|
||||
public FluidHandlerItemStackSimple(@Nonnull ItemStack container, int capacity)
|
||||
{
|
||||
this.container = container;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
NBTTagCompound tagCompound = container.getTagCompound();
|
||||
if (tagCompound == null || !tagCompound.hasKey(FLUID_NBT_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return FluidStack.loadFluidStackFromNBT(tagCompound.getCompoundTag(FLUID_NBT_KEY));
|
||||
}
|
||||
|
||||
protected void setFluid(FluidStack fluid)
|
||||
{
|
||||
if (!container.hasTagCompound())
|
||||
{
|
||||
container.setTagCompound(new NBTTagCompound());
|
||||
}
|
||||
|
||||
NBTTagCompound fluidTag = new NBTTagCompound();
|
||||
fluid.writeToNBT(fluidTag);
|
||||
container.getTagCompound().setTag(FLUID_NBT_KEY, fluidTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
return new IFluidTankProperties[] { new FluidTankProperties(getFluid(), capacity) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount <= 0 || !canFillFluidType(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
FluidStack contained = getFluid();
|
||||
if (contained == null)
|
||||
{
|
||||
int fillAmount = Math.min(capacity, resource.amount);
|
||||
if (fillAmount == capacity) {
|
||||
if (doFill) {
|
||||
FluidStack filled = resource.copy();
|
||||
filled.amount = fillAmount;
|
||||
setFluid(filled);
|
||||
}
|
||||
|
||||
return fillAmount;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount <= 0 || !resource.isFluidEqual(getFluid()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return drain(resource.amount, doDrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || maxDrain <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack contained = getFluid();
|
||||
if (contained == null || contained.amount <= 0 || !canDrainFluidType(contained))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final int drainAmount = Math.min(contained.amount, maxDrain);
|
||||
if (drainAmount == capacity) {
|
||||
FluidStack drained = contained.copy();
|
||||
|
||||
if (doDrain) {
|
||||
setContainerToEmpty();
|
||||
}
|
||||
|
||||
return drained;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean canFillFluidType(FluidStack fluid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canDrainFluidType(FluidStack fluid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method for special handling.
|
||||
* Can be used to swap out the container's item for a different one with "container.setItem".
|
||||
* Can be used to destroy the container with "container.stackSize--"
|
||||
*/
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
container.getTagCompound().removeTag(FLUID_NBT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY ? (T) this : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the container item when it's emptied.
|
||||
*/
|
||||
public static class Consumable extends FluidHandlerItemStackSimple
|
||||
{
|
||||
public Consumable(ItemStack container, int capacity)
|
||||
{
|
||||
super(container, capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
super.setContainerToEmpty();
|
||||
container.shrink(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the container item for a different one when it's emptied.
|
||||
*/
|
||||
public static class SwapEmpty extends FluidHandlerItemStackSimple
|
||||
{
|
||||
protected final ItemStack emptyContainer;
|
||||
|
||||
public SwapEmpty(ItemStack container, ItemStack emptyContainer, int capacity)
|
||||
{
|
||||
super(container, capacity);
|
||||
this.emptyContainer = emptyContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setContainerToEmpty()
|
||||
{
|
||||
super.setContainerToEmpty();
|
||||
container = emptyContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.templates;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidTankInfo;
|
||||
import net.minecraftforge.fluids.IFluidTank;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static net.minecraftforge.fluids.capability.templates.EmptyFluidHandler.EMPTY_TANK_INFO;
|
||||
import static net.minecraftforge.fluids.capability.templates.EmptyFluidHandler.EMPTY_TANK_PROPERTIES_ARRAY;
|
||||
|
||||
/**
|
||||
* VoidFluidHandler is a template fluid handler that can be filled indefinitely without ever getting full.
|
||||
* It does not store fluid that gets filled into it, but "destroys" it upon receiving it.
|
||||
*/
|
||||
public class VoidFluidHandler implements IFluidHandler, IFluidTank
|
||||
{
|
||||
public static final EmptyFluidHandler INSTANCE = new EmptyFluidHandler();
|
||||
|
||||
public VoidFluidHandler() {}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
return EMPTY_TANK_PROPERTIES_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFluidAmount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity()
|
||||
{
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidTankInfo getInfo()
|
||||
{
|
||||
return EMPTY_TANK_INFO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
return resource.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.wrappers;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemBucket;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
/**
|
||||
* Wrapper to handle vanilla Water or Lava as an IFluidHandler.
|
||||
* Methods are modeled after {@link ItemBucket#onItemRightClick(World, EntityPlayer, EnumHand)}
|
||||
*/
|
||||
public class BlockLiquidWrapper implements IFluidHandler
|
||||
{
|
||||
protected final BlockLiquid blockLiquid;
|
||||
protected final World world;
|
||||
protected final BlockPos blockPos;
|
||||
|
||||
public BlockLiquidWrapper(BlockLiquid blockLiquid, World world, BlockPos blockPos)
|
||||
{
|
||||
this.blockLiquid = blockLiquid;
|
||||
this.world = world;
|
||||
this.blockPos = blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
FluidStack containedStack = null;
|
||||
IBlockState blockState = world.getBlockState(blockPos);
|
||||
if (blockState.getBlock() == blockLiquid)
|
||||
{
|
||||
containedStack = getStack(blockState);
|
||||
}
|
||||
return new FluidTankProperties[]{new FluidTankProperties(containedStack, Fluid.BUCKET_VOLUME, false, true)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
// NOTE: "Filling" means placement in this context!
|
||||
if (resource.amount < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (doFill)
|
||||
{
|
||||
Material material = blockLiquid.getDefaultState().getMaterial();
|
||||
BlockLiquid block = BlockLiquid.getFlowingBlock(material);
|
||||
world.setBlockState(blockPos, block.getDefaultState().withProperty(BlockLiquid.LEVEL, 0), 11);
|
||||
}
|
||||
|
||||
return Fluid.BUCKET_VOLUME;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (resource == null || resource.amount < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IBlockState blockState = world.getBlockState(blockPos);
|
||||
if (blockState.getBlock() == blockLiquid && blockState.getValue(BlockLiquid.LEVEL) == 0)
|
||||
{
|
||||
FluidStack containedStack = getStack(blockState);
|
||||
if (containedStack != null && resource.containsFluid(containedStack))
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 11);
|
||||
}
|
||||
return containedStack;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (maxDrain < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IBlockState blockState = world.getBlockState(blockPos);
|
||||
if (blockState.getBlock() == blockLiquid)
|
||||
{
|
||||
FluidStack containedStack = getStack(blockState);
|
||||
if (containedStack != null && containedStack.amount <= maxDrain)
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 11);
|
||||
}
|
||||
return containedStack;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FluidStack getStack(IBlockState blockState)
|
||||
{
|
||||
Material material = blockState.getMaterial();
|
||||
if (material == Material.WATER && blockState.getValue(BlockLiquid.LEVEL) == 0)
|
||||
{
|
||||
return new FluidStack(FluidRegistry.WATER, Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
else if (material == Material.LAVA && blockState.getValue(BlockLiquid.LEVEL) == 0)
|
||||
{
|
||||
return new FluidStack(FluidRegistry.LAVA, Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.wrappers;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidUtil;
|
||||
import net.minecraftforge.fluids.capability.templates.VoidFluidHandler;
|
||||
|
||||
/**
|
||||
* Wrapper around any block, only accounts for fluid placement, otherwise the block acts a void.
|
||||
* If the block in question inherits from the default Vanilla or Forge implementations,
|
||||
* consider using {@link BlockLiquidWrapper} or {@link FluidBlockWrapper} respectively.
|
||||
*/
|
||||
public class BlockWrapper extends VoidFluidHandler
|
||||
{
|
||||
protected final Block block;
|
||||
protected final World world;
|
||||
protected final BlockPos blockPos;
|
||||
|
||||
public BlockWrapper(Block block, World world, BlockPos blockPos)
|
||||
{
|
||||
this.block = block;
|
||||
this.world = world;
|
||||
this.blockPos = blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
// NOTE: "Filling" means placement in this context!
|
||||
if (resource.amount < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (doFill)
|
||||
{
|
||||
FluidUtil.destroyBlockOnFluidPlacement(world, blockPos);
|
||||
world.setBlockState(blockPos, block.getDefaultState(), 11);
|
||||
}
|
||||
return Fluid.BUCKET_VOLUME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.wrappers;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.IFluidBlock;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
/**
|
||||
* Wrapper to handle {@link IFluidBlock} as an IFluidHandler
|
||||
*/
|
||||
public class FluidBlockWrapper implements IFluidHandler
|
||||
{
|
||||
protected final IFluidBlock fluidBlock;
|
||||
protected final World world;
|
||||
protected final BlockPos blockPos;
|
||||
|
||||
public FluidBlockWrapper(IFluidBlock fluidBlock, World world, BlockPos blockPos)
|
||||
{
|
||||
this.fluidBlock = fluidBlock;
|
||||
this.world = world;
|
||||
this.blockPos = blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
float percentFilled = fluidBlock.getFilledPercentage(world, blockPos);
|
||||
if (percentFilled < 0)
|
||||
{
|
||||
percentFilled *= -1;
|
||||
}
|
||||
int amountFilled = Math.round(Fluid.BUCKET_VOLUME * percentFilled);
|
||||
FluidStack fluid = amountFilled > 0 ? new FluidStack(fluidBlock.getFluid(), amountFilled) : null;
|
||||
return new FluidTankProperties[]{ new FluidTankProperties(fluid, Fluid.BUCKET_VOLUME, false, true)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
// NOTE: "Filling" means placement in this context!
|
||||
if (resource == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return fluidBlock.place(world, blockPos, resource, doFill);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (resource == null || !fluidBlock.canDrain(world, blockPos))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack simulatedDrain = fluidBlock.drain(world, blockPos, false);
|
||||
if (resource.containsFluid(simulatedDrain))
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
return fluidBlock.drain(world, blockPos, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return simulatedDrain;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (maxDrain <= 0 || !fluidBlock.canDrain(world, blockPos))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack simulatedDrain = fluidBlock.drain(world, blockPos, false);
|
||||
if (simulatedDrain != null && simulatedDrain.amount <= maxDrain)
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
return fluidBlock.drain(world, blockPos, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return simulatedDrain;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fluids.capability.wrappers;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBucketMilk;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidUtil;
|
||||
import net.minecraftforge.fluids.capability.FluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
|
||||
/**
|
||||
* Wrapper for vanilla and forge buckets.
|
||||
* Swaps between empty bucket and filled bucket of the correct type.
|
||||
*/
|
||||
public class FluidBucketWrapper implements IFluidHandlerItem, ICapabilityProvider
|
||||
{
|
||||
@Nonnull
|
||||
protected ItemStack container;
|
||||
|
||||
public FluidBucketWrapper(@Nonnull ItemStack container)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
public boolean canFillFluidType(FluidStack fluid)
|
||||
{
|
||||
if (fluid.getFluid() == FluidRegistry.WATER || fluid.getFluid() == FluidRegistry.LAVA || fluid.getFluid().getName().equals("milk"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return FluidRegistry.isUniversalBucketEnabled() && FluidRegistry.getBucketFluids().contains(fluid.getFluid());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FluidStack getFluid()
|
||||
{
|
||||
Item item = container.getItem();
|
||||
if (item == Items.WATER_BUCKET)
|
||||
{
|
||||
return new FluidStack(FluidRegistry.WATER, Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
else if (item == Items.LAVA_BUCKET)
|
||||
{
|
||||
return new FluidStack(FluidRegistry.LAVA, Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
else if (item == Items.MILK_BUCKET)
|
||||
{
|
||||
return FluidRegistry.getFluidStack("milk", Fluid.BUCKET_VOLUME);
|
||||
}
|
||||
else if (item == ForgeModContainer.getInstance().universalBucket)
|
||||
{
|
||||
return ForgeModContainer.getInstance().universalBucket.getFluid(container);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the NBT-sensitive version {@link #setFluid(FluidStack)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected void setFluid(@Nullable Fluid fluid) {
|
||||
setFluid(new FluidStack(fluid, Fluid.BUCKET_VOLUME));
|
||||
}
|
||||
|
||||
protected void setFluid(@Nullable FluidStack fluidStack)
|
||||
{
|
||||
if (fluidStack == null)
|
||||
container = new ItemStack(Items.BUCKET);
|
||||
else
|
||||
container = FluidUtil.getFilledBucket(fluidStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{
|
||||
return new FluidTankProperties[] { new FluidTankProperties(getFluid(), Fluid.BUCKET_VOLUME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount < Fluid.BUCKET_VOLUME || container.getItem() instanceof ItemBucketMilk || getFluid() != null || !canFillFluidType(resource))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (doFill)
|
||||
{
|
||||
setFluid(resource);
|
||||
}
|
||||
|
||||
return Fluid.BUCKET_VOLUME;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || resource == null || resource.amount < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack fluidStack = getFluid();
|
||||
if (fluidStack != null && fluidStack.isFluidEqual(resource))
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
setFluid((FluidStack) null);
|
||||
}
|
||||
return fluidStack;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if (container.getCount() != 1 || maxDrain < Fluid.BUCKET_VOLUME)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FluidStack fluidStack = getFluid();
|
||||
if (fluidStack != null)
|
||||
{
|
||||
if (doDrain)
|
||||
{
|
||||
setFluid((FluidStack) null);
|
||||
}
|
||||
return fluidStack;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
if (capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY)
|
||||
{
|
||||
return CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY.cast(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user