Java tutorial
/* * Minecraft Forge * Copyright (c) 2016-2019. * * 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.block.BlockState; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.IFluidState; import net.minecraft.item.ItemStack; import net.minecraft.particles.ParticleTypes; import net.minecraft.util.*; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.ILightReader; import java.util.function.BiFunction; import java.util.stream.Stream; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.item.Rarity; import net.minecraft.world.biome.BiomeColors; /** * 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 FluidAttributes { public static final int BUCKET_VOLUME = 1000; private String translationKey; private final ResourceLocation stillTexture; private final ResourceLocation flowingTexture; @Nullable private final ResourceLocation overlayTexture; private final SoundEvent fillSound; private final SoundEvent emptySound; /** * The light level emitted by this fluid. * * Default value is 0, as most fluids do not actively emit light. */ private final int luminosity; /** * 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. */ private final int density; /** * 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. */ private final int temperature; /** * 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. * */ private final int viscosity; /** * This indicates if the fluid is gaseous. * * Generally this is associated with negative density fluids. */ private final boolean isGaseous; /** * The rarity of the fluid. * * Used primarily in tool tips. */ private final Rarity rarity; /** * 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 */ private final int color; protected FluidAttributes(Builder builder, Fluid fluid) { this.translationKey = builder.translationKey != null ? builder.translationKey : Util.makeTranslationKey("fluid", fluid.getRegistryName()); this.stillTexture = builder.stillTexture; this.flowingTexture = builder.flowingTexture; this.overlayTexture = builder.overlayTexture; this.color = builder.color; this.fillSound = builder.fillSound; this.emptySound = builder.emptySound; this.luminosity = builder.luminosity; this.temperature = builder.temperature; this.viscosity = builder.viscosity; this.density = builder.density; this.isGaseous = builder.isGaseous; this.rarity = builder.rarity; } public ItemStack getBucket(FluidStack stack) { return new ItemStack(stack.getFluid().getFilledBucket()); } public BlockState getBlock(ILightReader reader, BlockPos pos, IFluidState state) { return state.getBlockState(); } public IFluidState getStateForPlacement(ILightReader reader, BlockPos pos, FluidStack state) { return state.getFluid().getDefaultState(); } public final boolean canBePlacedInWorld(ILightReader reader, BlockPos pos, IFluidState state) { return !getBlock(reader, pos, state).isAir(reader, pos); } public final boolean canBePlacedInWorld(ILightReader reader, BlockPos pos, FluidStack state) { return !getBlock(reader, pos, getStateForPlacement(reader, pos, state)).isAir(reader, pos); } public final boolean isLighterThanAir() { return this.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.BucketItem#tryPlaceContainedLiquid(PlayerEntity, 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(ILightReader reader, BlockPos pos, FluidStack fluidStack) { BlockState blockstate = getBlock(reader, pos, getStateForPlacement(reader, pos, fluidStack)); if (blockstate == null) return false; return blockstate.getMaterial() == net.minecraft.block.material.Material.WATER; } /** * Called instead of placing the fluid block if {@link net.minecraft.world.dimension.Dimension#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.BucketItem#tryPlaceContainedLiquid(PlayerEntity, 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 PlayerEntity 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.addOptionalParticle(ParticleTypes.LARGE_SMOKE, (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 ITextComponent getDisplayName(FluidStack stack) { return new TranslationTextComponent(getTranslationKey()); } /** * A FluidStack sensitive version of getTranslationKey */ public String getTranslationKey(FluidStack stack) { return this.getTranslationKey(); } /** * Returns the translation key of this fluid. */ public String getTranslationKey() { return this.translationKey; } /* 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 Rarity getRarity() { return rarity; } public int getColor() { return color; } public ResourceLocation getStillTexture() { return stillTexture; } public ResourceLocation getFlowingTexture() { return flowingTexture; } @Nullable public ResourceLocation getOverlayTexture() { return overlayTexture; } public SoundEvent getFillSound() { return fillSound; } public SoundEvent getEmptySound() { 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 Rarity getRarity(FluidStack stack) { return getRarity(); } public int getColor(FluidStack stack) { return getColor(); } public ResourceLocation getStillTexture(FluidStack stack) { return getStillTexture(); } public ResourceLocation getFlowingTexture(FluidStack stack) { return getFlowingTexture(); } public SoundEvent getFillSound(FluidStack stack) { return getFillSound(); } public SoundEvent getEmptySound(FluidStack stack) { return getEmptySound(); } /* World-based Accessors */ public int getLuminosity(ILightReader world, BlockPos pos) { return getLuminosity(); } public int getDensity(ILightReader world, BlockPos pos) { return getDensity(); } public int getTemperature(ILightReader world, BlockPos pos) { return getTemperature(); } public int getViscosity(ILightReader world, BlockPos pos) { return getViscosity(); } public boolean isGaseous(ILightReader world, BlockPos pos) { return isGaseous(); } public Rarity getRarity(ILightReader world, BlockPos pos) { return getRarity(); } public int getColor(ILightReader world, BlockPos pos) { return getColor(); } public ResourceLocation getStillTexture(ILightReader world, BlockPos pos) { return getStillTexture(); } public ResourceLocation getFlowingTexture(ILightReader world, BlockPos pos) { return getFlowingTexture(); } public SoundEvent getFillSound(ILightReader world, BlockPos pos) { return getFillSound(); } public SoundEvent getEmptySound(ILightReader world, BlockPos pos) { return getEmptySound(); } public static Builder builder(ResourceLocation stillTexture, ResourceLocation flowingTexture) { return new Builder(stillTexture, flowingTexture, FluidAttributes::new); } public Stream<ResourceLocation> getTextures() { if (overlayTexture != null) return Stream.of(stillTexture, flowingTexture, overlayTexture); return Stream.of(stillTexture, flowingTexture); } public static class Builder { private final ResourceLocation stillTexture; private final ResourceLocation flowingTexture; private ResourceLocation overlayTexture; private int color = 0xFFFFFFFF; private String translationKey; private SoundEvent fillSound; private SoundEvent emptySound; private int luminosity = 0; private int density = 1000; private int temperature = 300; private int viscosity = 1000; private boolean isGaseous; private Rarity rarity = Rarity.COMMON; private BiFunction<Builder, Fluid, FluidAttributes> factory; protected Builder(ResourceLocation stillTexture, ResourceLocation flowingTexture, BiFunction<Builder, Fluid, FluidAttributes> factory) { this.factory = factory; this.stillTexture = stillTexture; this.flowingTexture = flowingTexture; } public final Builder translationKey(String translationKey) { this.translationKey = translationKey; return this; } public final Builder color(int color) { this.color = color; return this; } public final Builder overlay(ResourceLocation texture) { overlayTexture = texture; return this; } public final Builder luminosity(int luminosity) { this.luminosity = luminosity; return this; } public final Builder density(int density) { this.density = density; return this; } public final Builder temperature(int temperature) { this.temperature = temperature; return this; } public final Builder viscosity(int viscosity) { this.viscosity = viscosity; return this; } public final Builder gaseous() { isGaseous = true; return this; } public final Builder rarity(Rarity rarity) { this.rarity = rarity; return this; } public final Builder sound(SoundEvent sound) { this.fillSound = this.emptySound = sound; return this; } public final Builder sound(SoundEvent fillSound, SoundEvent emptySound) { this.fillSound = fillSound; this.emptySound = emptySound; return this; } public FluidAttributes build(Fluid fluid) { return factory.apply(this, fluid); } } public static class Water extends FluidAttributes { protected Water(Builder builder, Fluid fluid) { super(builder, fluid); } @Override public int getColor(ILightReader world, BlockPos pos) { return BiomeColors.func_228363_c_(world, pos) | 0xFF000000; } public static Builder builder(ResourceLocation stillTexture, ResourceLocation flowingTexture) { return new Builder(stillTexture, flowingTexture, Water::new); } } }