Skip to content
2 changes: 2 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseBlocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public static void initialize() {
PylonBlock.register(BaseKeys.FLUID_VALVE, Material.STRUCTURE_VOID, FluidValve.class);
PylonBlock.register(BaseKeys.WATER_PUMP, Material.BLUE_TERRACOTTA, WaterPump.class);
PylonBlock.register(BaseKeys.FLUID_FILTER, Material.STRUCTURE_VOID, FluidFilter.class);
PylonBlock.register(BaseKeys.FLUID_LIMITER, Material.STRUCTURE_VOID, FluidLimiter.class);
PylonBlock.register(BaseKeys.FLUID_ACCUMULATOR, Material.STRUCTURE_VOID, FluidAccumulator.class);
PylonBlock.register(BaseKeys.FLUID_METER, Material.STRUCTURE_VOID, FluidMeter.class);
PylonBlock.register(BaseKeys.WATER_PLACER, Material.DISPENSER, FluidPlacer.class);
PylonBlock.register(BaseKeys.LAVA_PLACER, Material.DISPENSER, FluidPlacer.class);
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,22 @@ private BaseItems() {
BasePages.FLUID_MACHINES.addItem(FLUID_FILTER);
}

public static final ItemStack FLUID_LIMITER = ItemStackBuilder.pylon(Material.STRUCTURE_VOID, BaseKeys.FLUID_LIMITER)
.set(DataComponentTypes.ITEM_MODEL, Material.WHITE_CONCRETE.getKey())
.build();
static {
PylonItem.register(PylonItem.class, FLUID_LIMITER, BaseKeys.FLUID_LIMITER);
BasePages.FLUID_MACHINES.addItem(FLUID_LIMITER);
}

public static final ItemStack FLUID_ACCUMULATOR = ItemStackBuilder.pylon(Material.STRUCTURE_VOID, BaseKeys.FLUID_ACCUMULATOR)
.set(DataComponentTypes.ITEM_MODEL, Material.WHITE_CONCRETE.getKey())
.build();
static {
PylonItem.register(PylonItem.class, FLUID_ACCUMULATOR, BaseKeys.FLUID_ACCUMULATOR);
BasePages.FLUID_MACHINES.addItem(FLUID_ACCUMULATOR);
}

public static final ItemStack FLUID_METER = ItemStackBuilder.pylon(Material.STRUCTURE_VOID, BaseKeys.FLUID_METER)
.set(DataComponentTypes.ITEM_MODEL, Material.WHITE_CONCRETE.getKey())
.build();
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ public class BaseKeys {
public static final NamespacedKey FLUID_VALVE = baseKey("fluid_valve");
public static final NamespacedKey FLUID_METER = baseKey("fluid_meter");
public static final NamespacedKey FLUID_FILTER = baseKey("fluid_filter");
public static final NamespacedKey FLUID_LIMITER = baseKey("fluid_limiter");
public static final NamespacedKey FLUID_ACCUMULATOR = baseKey("fluid_accumulator");

public static final NamespacedKey WATER_PLACER = baseKey("water_placer");
public static final NamespacedKey LAVA_PLACER = baseKey("lava_placer");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package io.github.pylonmc.pylon.base.content.machines.fluid;

import io.github.pylonmc.pylon.base.BaseItems;
import io.github.pylonmc.pylon.base.content.machines.fluid.gui.IntRangeInventory;
import io.github.pylonmc.pylon.core.block.PylonBlock;
import io.github.pylonmc.pylon.core.block.base.PylonDirectionalBlock;
import io.github.pylonmc.pylon.core.block.base.PylonFluidTank;
import io.github.pylonmc.pylon.core.block.base.PylonInteractBlock;
import io.github.pylonmc.pylon.core.block.context.BlockCreateContext;
import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter;
import io.github.pylonmc.pylon.core.datatypes.PylonSerializers;
import io.github.pylonmc.pylon.core.entity.display.ItemDisplayBuilder;
import io.github.pylonmc.pylon.core.entity.display.transform.TransformBuilder;
import io.github.pylonmc.pylon.core.fluid.FluidPointType;
import io.github.pylonmc.pylon.core.fluid.PylonFluid;
import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.component.AdventureComponentWrapper;
import xyz.xenondevs.invui.window.Window;

import java.util.Map;

import static io.github.pylonmc.pylon.base.util.BaseUtils.baseKey;

public class FluidAccumulator extends PylonBlock implements PylonDirectionalBlock, PylonFluidTank, PylonInteractBlock {

public final ItemStackBuilder mainStack = ItemStackBuilder.of(Material.WHITE_CONCRETE)
.addCustomModelDataString(getKey() + ":main");
public final ItemStackBuilder noFluidStack = ItemStackBuilder.of(Material.RED_CONCRETE)
.addCustomModelDataString(getKey() + ":fluid:none");

public final int buffer = getSettings().getOrThrow("buffer", ConfigAdapter.INT);
public final IntRangeInventory regulator;

// when true, starts the output and stops the input, opposite when false
private boolean outputReady;

public static final NamespacedKey AMOUNT_KEY = baseKey("amount");
public static final NamespacedKey OUTPUT_READY_KEY = baseKey("output_ready");

@SuppressWarnings("unused")
public FluidAccumulator(@NotNull Block block, @NotNull BlockCreateContext context) {
super(block);
this.regulator = new IntRangeInventory(
BaseItems.FLUID_ACCUMULATOR,
() -> buffer,
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.add"),
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.dec"),
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.amount")
);
this.outputReady = false;
setCapacity(buffer);

Player player = ((BlockCreateContext.PlayerPlace) context).getPlayer();

// fatty filter for now todo: add a proper unique display
addEntity("main", new ItemDisplayBuilder()
.itemStack(mainStack)
.transformation(new TransformBuilder()
.lookAlong(getFacing())
.scale(0.5, 0.25, 0.5)
)
.build(block.getLocation().toCenterLocation())
);
addEntity("fluid", new ItemDisplayBuilder()
.itemStack(noFluidStack)
.transformation(new TransformBuilder()
.lookAlong(getFacing())
.scale(0.3, 0.3, 0.3)
)
.build(block.getLocation().toCenterLocation())
);
createFluidPoint(FluidPointType.INPUT, BlockFace.EAST, context, false, 0.3F);
createFluidPoint(FluidPointType.OUTPUT, BlockFace.WEST, context, false, 0.3F);
setDisableBlockTextureEntity(true);
}

@SuppressWarnings("unused")
public FluidAccumulator(@NotNull Block block, @NotNull PersistentDataContainer pdc) {
super(block);
this.regulator = new IntRangeInventory(
BaseItems.FLUID_ACCUMULATOR,
() -> buffer,
pdc.get(AMOUNT_KEY, PylonSerializers.INTEGER),
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.add"),
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.dec"),
Component.translatable("pylon.pylonbase.item.fluid_accumulator.gui.amount")
);
this.outputReady = pdc.get(OUTPUT_READY_KEY, PylonSerializers.BOOLEAN);
setCapacity(regulator.getAmount());
setDisableBlockTextureEntity(true);
}

@Override
public void write(@NotNull PersistentDataContainer pdc) {
pdc.set(AMOUNT_KEY, PylonSerializers.INTEGER, regulator.getAmount());
pdc.set(OUTPUT_READY_KEY, PylonSerializers.BOOLEAN, outputReady);
}

@Override
public boolean isAllowedFluid(@NotNull PylonFluid fluid) {
return true;
}

@Override
public void onInteract(PlayerInteractEvent event) {
if (!event.getAction().isRightClick()
|| event.getPlayer().isSneaking()
|| event.getHand() != EquipmentSlot.HAND
|| event.useInteractedBlock() == Event.Result.DENY) {
return;
}

event.setUseInteractedBlock(Event.Result.DENY);
event.setUseItemInHand(Event.Result.DENY);

Window.single()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use PylonGuiBlock here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no because it doesn't allow me to add close/open handlers

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't need them after swapping to FluidThresholdButton as I mentioned in my next comment

.setGui(regulator.makeGui())
.setTitle(new AdventureComponentWrapper(getNameTranslationKey()))
.setViewer(event.getPlayer())
.addCloseHandler(() -> setCapacity(regulator.getAmount()))
.build()
.open();
}

@Override
public double fluidAmountRequested(@NotNull PylonFluid fluid) {
if (getBlock().isBlockIndirectlyPowered()) return 0.0;

if (getFluidAmount() == 0.0) {
outputReady = false;
return PylonFluidTank.super.fluidAmountRequested(fluid);
}

if (outputReady) return 0.0;

return PylonFluidTank.super.fluidAmountRequested(fluid);
}

@Override
public @NotNull Map<@NotNull PylonFluid, @NotNull Double> getSuppliedFluids() {
if (getBlock().isBlockIndirectlyPowered()) return PylonFluidTank.super.getSuppliedFluids();

if (getFluidSpaceRemaining() == 0.0) {
outputReady = true;
return PylonFluidTank.super.getSuppliedFluids();
}

if (outputReady) return PylonFluidTank.super.getSuppliedFluids();

return Map.of();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package io.github.pylonmc.pylon.base.content.machines.fluid;

import com.google.common.base.Preconditions;
import io.github.pylonmc.pylon.base.BaseItems;
import io.github.pylonmc.pylon.base.content.machines.fluid.gui.IntRangeInventory;
import io.github.pylonmc.pylon.core.block.PylonBlock;
import io.github.pylonmc.pylon.core.block.base.PylonDirectionalBlock;
import io.github.pylonmc.pylon.core.block.base.PylonFluidTank;
import io.github.pylonmc.pylon.core.block.base.PylonInteractBlock;
import io.github.pylonmc.pylon.core.block.context.BlockCreateContext;
import io.github.pylonmc.pylon.core.datatypes.PylonSerializers;
import io.github.pylonmc.pylon.core.entity.display.ItemDisplayBuilder;
import io.github.pylonmc.pylon.core.entity.display.transform.TransformBuilder;
import io.github.pylonmc.pylon.core.fluid.FluidManager;
import io.github.pylonmc.pylon.core.fluid.FluidPointType;
import io.github.pylonmc.pylon.core.fluid.PylonFluid;
import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder;
import io.github.pylonmc.pylon.core.util.PylonUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.component.AdventureComponentWrapper;
import xyz.xenondevs.invui.window.Window;

import static io.github.pylonmc.pylon.base.util.BaseUtils.baseKey;

public class FluidLimiter extends PylonBlock implements PylonDirectionalBlock, PylonFluidTank, PylonInteractBlock {

public final ItemStackBuilder mainStack = ItemStackBuilder.of(Material.WHITE_CONCRETE)
.addCustomModelDataString(getKey() + ":main");
public final ItemStackBuilder noFluidStack = ItemStackBuilder.of(Material.RED_CONCRETE)
.addCustomModelDataString(getKey() + ":fluid:none");

public final IntRangeInventory regulator;

private static final NamespacedKey AMOUNT_KEY = baseKey("amount");
public static final NamespacedKey REAL_MAX_CAPACITY = baseKey("real_max_capacity");

@SuppressWarnings("unused")
public FluidLimiter(@NotNull Block block, @NotNull BlockCreateContext context) {
super(block);
this.regulator = new IntRangeInventory(
BaseItems.FLUID_LIMITER,
this::getPipeMax,
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.add"),
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.dec"),
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.amount")
);

// a bit of a hack - treat capacity as effectively infinite and override
// fluidAmountRequested to control how much fluid comes in
setCapacity(1000);

Preconditions.checkState(context instanceof BlockCreateContext.PlayerPlace, "Fluid filter can only be placed by a player");
Player player = ((BlockCreateContext.PlayerPlace) context).getPlayer();

// fatty filter for now todo: add a proper unique display
addEntity("main", new ItemDisplayBuilder()
.itemStack(mainStack)
.transformation(new TransformBuilder()
.lookAlong(getFacing())
.scale(0.4, 0.25, 0.5)
)
.build(block.getLocation().toCenterLocation())
);
addEntity("fluid", new ItemDisplayBuilder()
.itemStack(noFluidStack)
.transformation(new TransformBuilder()
.lookAlong(getFacing())
.scale(0.2, 0.3, 0.45)
)
.build(block.getLocation().toCenterLocation())
);
createFluidPoint(FluidPointType.INPUT, BlockFace.EAST, context, false, 0.25F);
createFluidPoint(FluidPointType.OUTPUT, BlockFace.WEST, context, false, 0.25F);
setDisableBlockTextureEntity(true);
}

@SuppressWarnings("unused")
public FluidLimiter(@NotNull Block block, @NotNull PersistentDataContainer pdc) {
super(block);
this.regulator = new IntRangeInventory(
BaseItems.FLUID_LIMITER,
this::getPipeMax,
pdc.get(AMOUNT_KEY, PylonSerializers.INTEGER),
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.add"),
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.dec"),
Component.translatable("pylon.pylonbase.item.fluid_limiter.gui.amount")
);
setDisableBlockTextureEntity(true);
}

@Override
public void write(@NotNull PersistentDataContainer pdc) {
pdc.set(AMOUNT_KEY, PylonSerializers.INTEGER, regulator.getAmount());
}

@Override
public boolean isAllowedFluid(@NotNull PylonFluid fluid) {
return true;
}

@Override
public void onInteract(PlayerInteractEvent event) {
if (!event.getAction().isRightClick()
|| event.getPlayer().isSneaking()
|| event.getHand() != EquipmentSlot.HAND
|| event.useInteractedBlock() == Event.Result.DENY) {
return;
}

event.setUseInteractedBlock(Event.Result.DENY);
event.setUseItemInHand(Event.Result.DENY);

Window.single()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use PylonGuiBlock

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same reason as above

.setGui(regulator.makeGui())
.setTitle(new AdventureComponentWrapper(getNameTranslationKey()))
.setViewer(event.getPlayer())
.addCloseHandler(this::updateFluidPerSecond)
.build()
.open();
}

private void updateFluidPerSecond() {
var output = getFluidPointDisplayOrThrow(FluidPointType.OUTPUT);
var segment = output.getPoint().getSegment();
FluidManager.setFluidPerSecond(segment, regulator.getAmount());
}

private int getPipeMax() {
return Double.valueOf(getPipeMaxDouble()).intValue();
}

private double getPipeMaxDouble() {
var output = getFluidPointDisplayOrThrow(FluidPointType.OUTPUT);

PersistentDataContainer pdc = output.getEntity().getPersistentDataContainer();
Double realCapacity = pdc.get(REAL_MAX_CAPACITY, PylonSerializers.DOUBLE);
if (realCapacity == null) {
double original = FluidManager.getFluidPerSecond(output.getPoint().getSegment());
pdc.set(REAL_MAX_CAPACITY, PersistentDataType.DOUBLE, original);
return original;
} else {
return realCapacity;
}
}
}
Loading