diff options
Diffstat (limited to 'src/main/java/gregtech/common/covers/CoverFluidRegulator.java')
-rw-r--r-- | src/main/java/gregtech/common/covers/CoverFluidRegulator.java | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/covers/CoverFluidRegulator.java b/src/main/java/gregtech/common/covers/CoverFluidRegulator.java new file mode 100644 index 0000000000..a41ed08320 --- /dev/null +++ b/src/main/java/gregtech/common/covers/CoverFluidRegulator.java @@ -0,0 +1,520 @@ +package gregtech.common.covers; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidHandler; + +import com.google.common.io.ByteArrayDataInput; +import com.gtnewhorizons.modularui.api.NumberFormatMUI; +import com.gtnewhorizons.modularui.api.drawable.Text; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.api.gui.modularui.CoverUIBuildContext; +import gregtech.api.gui.modularui.GTUITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.util.CoverBehaviorBase; +import gregtech.api.util.GTUtility; +import gregtech.api.util.ISerializableObject; +import gregtech.common.gui.modularui.widget.CoverDataControllerWidget; +import gregtech.common.gui.modularui.widget.CoverDataFollowerNumericWidget; +import gregtech.common.gui.modularui.widget.CoverDataFollowerToggleButtonWidget; +import io.netty.buffer.ByteBuf; + +/** + * Cover variable + * + * <pre> + * 1111 1111 1111 1111 1111 1111 1111 1111 + * |- interval-| |- flow rate 2 compl. -| + * ^ export? + * </pre> + * + * Concat export and flow rate 2 compl. together to get actual flow rate. A positive actual flow rate is export, and + * vice versa. + * <p> + * Interval is an unsigned 11 bit integer minus 1, so the range is 1~2048. The stored bits will be flipped bitwise if + * speed is negative. This way, `0` means 1tick interval, while `-1` means 1 tick interval as well, preserving the + * legacy behavior. + */ +public class CoverFluidRegulator extends CoverBehaviorBase<CoverFluidRegulator.FluidRegulatorData> { + + private static final int SPEED_LENGTH = 20; + private static final int TICK_RATE_LENGTH = Integer.SIZE - SPEED_LENGTH - 1; + private static final int TICK_RATE_MIN = 1; + private static final int TICK_RATE_MAX = (-1 >>> (Integer.SIZE - TICK_RATE_LENGTH)) + TICK_RATE_MIN; + private static final int TICK_RATE_BITMASK = (TICK_RATE_MAX - TICK_RATE_MIN) << SPEED_LENGTH; + + public final int mTransferRate; + private boolean allowFluid = false; + + public CoverFluidRegulator(int aTransferRate, ITexture coverTexture) { + super(FluidRegulatorData.class, coverTexture); + if (aTransferRate > (-1 >>> (Integer.SIZE - SPEED_LENGTH))) + throw new IllegalArgumentException("aTransferRate too big: " + aTransferRate); + this.mTransferRate = aTransferRate; + } + + @Override + public FluidRegulatorData createDataObject(int aLegacyData) { + return new FluidRegulatorData(aLegacyData); + } + + @Override + public FluidRegulatorData createDataObject() { + return new FluidRegulatorData(); + } + + @Override + protected boolean isRedstoneSensitiveImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return aCoverVariable.condition.isRedstoneSensitive(); + } + + @Override + protected FluidRegulatorData doCoverThingsImpl(ForgeDirection side, byte aInputRedstone, int aCoverID, + FluidRegulatorData aCoverVariable, ICoverable aTileEntity, long aTimer) { + if (aCoverVariable.speed == 0 || !aCoverVariable.condition.isAllowedToWork(side, aCoverID, aTileEntity)) { + return aCoverVariable; + } + if ((aTileEntity instanceof IFluidHandler)) { + final IFluidHandler tTank1; + final IFluidHandler tTank2; + final ForgeDirection directionFrom; + final ForgeDirection directionTo; + if (aCoverVariable.speed > 0) { + tTank2 = aTileEntity.getITankContainerAtSide(side); + tTank1 = (IFluidHandler) aTileEntity; + directionFrom = side; + directionTo = side.getOpposite(); + } else { + tTank1 = aTileEntity.getITankContainerAtSide(side); + tTank2 = (IFluidHandler) aTileEntity; + directionFrom = side.getOpposite(); + directionTo = side; + } + if (tTank1 != null && tTank2 != null) { + allowFluid = true; + GTUtility + .moveFluid(tTank1, tTank2, directionFrom, Math.abs(aCoverVariable.speed), this::canTransferFluid); + allowFluid = false; + } + } + return aCoverVariable; + } + + private void adjustSpeed(EntityPlayer aPlayer, FluidRegulatorData aCoverVariable, int scale) { + int tSpeed = aCoverVariable.speed; + tSpeed += scale; + int tTickRate = aCoverVariable.tickRate; + if (Math.abs(tSpeed) > mTransferRate * tTickRate) { + tSpeed = mTransferRate * tTickRate * (tSpeed > 0 ? 1 : -1); + GTUtility.sendChatToPlayer(aPlayer, GTUtility.trans("316", "Pump speed limit reached!")); + } + if (tTickRate == 1) { + GTUtility.sendChatToPlayer( + aPlayer, + GTUtility.trans("048", "Pump speed: ") + tSpeed + + GTUtility.trans("049", "L/tick ") + + tSpeed * 20 + + GTUtility.trans("050", "L/sec")); + } else { + GTUtility.sendChatToPlayer( + aPlayer, + String.format( + GTUtility.trans("207", "Pump speed: %dL every %d ticks, %.2f L/sec on average"), + tSpeed, + tTickRate, + tSpeed * 20d / tTickRate)); + } + } + + @Override + public FluidRegulatorData onCoverScrewdriverClickImpl(ForgeDirection side, int aCoverID, + FluidRegulatorData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (GTUtility.getClickedFacingCoords(side, aX, aY, aZ)[0] >= 0.5F) { + adjustSpeed(aPlayer, aCoverVariable, aPlayer.isSneaking() ? 256 : 16); + } else { + adjustSpeed(aPlayer, aCoverVariable, aPlayer.isSneaking() ? -256 : -16); + } + return aCoverVariable; + } + + protected boolean canTransferFluid(FluidStack fluid) { + return true; + } + + @Override + public boolean letsRedstoneGoInImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsRedstoneGoOutImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsEnergyInImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsEnergyOutImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsItemsInImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsItemsOutImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsFluidInImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return allowFluid; + } + + @Override + public boolean letsFluidOutImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return allowFluid; + } + + @Override + protected boolean alwaysLookConnectedImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return true; + } + + @Override + protected int getTickRateImpl(ForgeDirection side, int aCoverID, FluidRegulatorData aCoverVariable, + ICoverable aTileEntity) { + return aCoverVariable.tickRate; + } + + // GUI stuff + + @Override + public boolean hasCoverGUI() { + return true; + } + + @Override + public ModularWindow createWindow(CoverUIBuildContext buildContext) { + return new FluidRegulatorUIFactory(buildContext).createWindow(); + } + + private class FluidRegulatorUIFactory extends UIFactory { + + private static final int startX = 10; + private static final int startY = 25; + private static final int spaceX = 18; + private static final int spaceY = 18; + + private static final NumberFormatMUI numberFormat; + static { + numberFormat = new NumberFormatMUI(); + numberFormat.setMaximumFractionDigits(2); + } + + public FluidRegulatorUIFactory(CoverUIBuildContext buildContext) { + super(buildContext); + } + + @SuppressWarnings("PointlessArithmeticExpression") + @Override + protected void addUIWidgets(ModularWindow.Builder builder) { + AtomicBoolean warn = new AtomicBoolean(false); + + builder.widget( + new CoverDataControllerWidget<>(this::getCoverData, this::setCoverData, CoverFluidRegulator.this) + .addFollower( + CoverDataFollowerToggleButtonWidget.ofDisableable(), + coverData -> coverData.speed >= 0, + (coverData, state) -> { + coverData.speed = Math.abs(coverData.speed); + return coverData; + }, + widget -> widget.setStaticTexture(GTUITextures.OVERLAY_BUTTON_EXPORT) + .addTooltip(GTUtility.trans("006", "Export")) + .setPos(spaceX * 0, spaceY * 0)) + .addFollower( + CoverDataFollowerToggleButtonWidget.ofDisableable(), + coverData -> coverData.speed <= 0, + (coverData, state) -> { + coverData.speed = -Math.abs(coverData.speed); + return coverData; + }, + widget -> widget.setStaticTexture(GTUITextures.OVERLAY_BUTTON_IMPORT) + .addTooltip(GTUtility.trans("007", "Import")) + .setPos(spaceX * 1, spaceY * 0)) + .addFollower( + CoverDataFollowerToggleButtonWidget.ofDisableable(), + coverData -> coverData.condition == Conditional.Always, + (coverData, state) -> { + coverData.condition = Conditional.Always; + return coverData; + }, + widget -> widget.setStaticTexture(GTUITextures.OVERLAY_BUTTON_CHECKMARK) + .addTooltip(GTUtility.trans("224", "Always On")) + .setPos(spaceX * 0, spaceY * 1)) + .addFollower( + CoverDataFollowerToggleButtonWidget.ofDisableable(), + coverData -> coverData.condition == Conditional.Conditional, + (coverData, state) -> { + coverData.condition = Conditional.Conditional; + return coverData; + }, + widget -> widget.setStaticTexture(GTUITextures.OVERLAY_BUTTON_USE_PROCESSING_STATE) + .addTooltip(GTUtility.trans("343", "Use Machine Processing State")) + .setPos(spaceX * 1, spaceY * 1)) + .addFollower( + CoverDataFollowerToggleButtonWidget.ofDisableable(), + coverData -> coverData.condition == Conditional.Inverted, + (coverData, state) -> { + coverData.condition = Conditional.Inverted; + return coverData; + }, + widget -> widget.setStaticTexture(GTUITextures.OVERLAY_BUTTON_USE_INVERTED_PROCESSING_STATE) + .addTooltip(GTUtility.trans("343.1", "Use Inverted Machine Processing State")) + .setPos(spaceX * 2, spaceY * 1)) + .addFollower( + new CoverDataFollowerNumericWidget<>(), + coverData -> (double) coverData.speed, + (coverData, state) -> { + coverData.speed = state.intValue(); + return coverData; + }, + widget -> widget.setBounds(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY) + .setValidator(val -> { + final int tickRate = getCoverData() != null ? getCoverData().tickRate : 0; + final long maxFlow = (long) mTransferRate + * GTUtility.clamp(tickRate, TICK_RATE_MIN, TICK_RATE_MAX); + warn.set(false); + if (val > maxFlow) { + val = maxFlow; + warn.set(true); + } else if (val < -maxFlow) { + val = -maxFlow; + warn.set(true); + } + return val; + }) + .setScrollValues(1, 144, 1000) + .setFocusOnGuiOpen(true) + .setPos(spaceX * 0, spaceY * 2 + 2) + .setSize(spaceX * 4 - 3, 12)) + .addFollower( + new CoverDataFollowerNumericWidget<>(), + coverData -> (double) coverData.tickRate, + (coverData, state) -> { + coverData.tickRate = state.intValue(); + return coverData; + }, + widget -> widget.setBounds(0, TICK_RATE_MAX) + .setValidator(val -> { + final int speed = getCoverData() != null ? getCoverData().speed : 0; + warn.set(false); + if (val > TICK_RATE_MAX) { + val = (long) TICK_RATE_MAX; + warn.set(true); + } else if (Math.abs(speed) > mTransferRate * val) { + val = (long) Math + .min(TICK_RATE_MAX, (Math.abs(speed) + mTransferRate - 1) / mTransferRate); + warn.set(true); + } else if (val < TICK_RATE_MIN) { + val = 1L; + } + return val; + }) + .setPos(spaceX * 5, spaceY * 2 + 2) + .setSize(spaceX * 2 - 3, 12)) + .setPos(startX, startY)) + .widget( + new TextWidget(GTUtility.trans("229", "Export/Import")).setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(3 + startX + spaceX * 4, 4 + startY + spaceY * 0)) + .widget( + new TextWidget(GTUtility.trans("230", "Conditional")).setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(3 + startX + spaceX * 4, 4 + startY + spaceY * 1)) + .widget( + new TextWidget(GTUtility.trans("208", " L")).setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(startX + spaceX * 4, 4 + startY + spaceY * 2)) + .widget( + new TextWidget(GTUtility.trans("209", " ticks")).setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(startX + spaceX * 7, 4 + startY + spaceY * 2)) + .widget(new TextWidget().setTextSupplier(() -> { + FluidRegulatorData coverVariable = getCoverData(); + if (coverVariable == null) return new Text(""); + return new Text( + GTUtility.trans("210.1", "Average:") + " " + + numberFormat.format( + coverVariable.tickRate == 0 ? 0 : coverVariable.speed * 20d / coverVariable.tickRate) + + " " + + GTUtility.trans("210.2", "L/sec")) + .color(warn.get() ? COLOR_TEXT_WARN.get() : COLOR_TEXT_GRAY.get()); + }) + .setPos(startX + spaceX * 0, 4 + startY + spaceY * 3)); + } + } + + @Override + public boolean allowsTickRateAddition() { + return false; + } + + public enum Conditional { + + Always(false) { + + @Override + boolean isAllowedToWork(ForgeDirection side, int aCoverID, ICoverable aTileEntity) { + return true; + } + }, + Conditional(true) { + + @Override + boolean isAllowedToWork(ForgeDirection side, int aCoverID, ICoverable aTileEntity) { + return !(aTileEntity instanceof IMachineProgress) || ((IMachineProgress) aTileEntity).isAllowedToWork(); + } + }, + Inverted(true) { + + @Override + boolean isAllowedToWork(ForgeDirection side, int aCoverID, ICoverable aTileEntity) { + return !(aTileEntity instanceof IMachineProgress) + || !((IMachineProgress) aTileEntity).isAllowedToWork(); + } + }; + + static final Conditional[] VALUES = values(); + private final boolean redstoneSensitive; + + Conditional(boolean redstoneSensitive) { + this.redstoneSensitive = redstoneSensitive; + } + + abstract boolean isAllowedToWork(ForgeDirection side, int aCoverID, ICoverable aTileEntity); + + boolean isRedstoneSensitive() { + return redstoneSensitive; + } + } + + public static class FluidRegulatorData implements ISerializableObject { + + private int tickRate; + private int speed; + private Conditional condition; + + private static int getSpeed(int aCoverVariable) { + // positive or 0 -> interval bits need to be set to zero + // negative -> interval bits need to be set to one + return aCoverVariable >= 0 ? aCoverVariable & ~TICK_RATE_BITMASK : aCoverVariable | TICK_RATE_BITMASK; + } + + private static int getTickRate(int aCoverVariable) { + // range: TICK_RATE_MIN ~ TICK_RATE_MAX + return ((Math.abs(aCoverVariable) & TICK_RATE_BITMASK) >>> SPEED_LENGTH) + TICK_RATE_MIN; + } + + public FluidRegulatorData() { + this(0); + } + + public FluidRegulatorData(int legacy) { + this(getTickRate(legacy), getSpeed(legacy), Conditional.Always); + } + + public FluidRegulatorData(int tickRate, int speed, Conditional condition) { + this.tickRate = tickRate; + this.speed = speed; + this.condition = condition; + } + + @Nonnull + @Override + public ISerializableObject copy() { + return new FluidRegulatorData(tickRate, speed, condition); + } + + @Nonnull + @Override + public NBTBase saveDataToNBT() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setInteger("mSpeed", speed); + tag.setInteger("mTickRate", tickRate); + tag.setByte("mCondition", (byte) condition.ordinal()); + return tag; + } + + @Override + public void writeToByteBuf(ByteBuf aBuf) { + aBuf.writeInt(tickRate) + .writeInt(speed) + .writeByte(condition.ordinal()); + } + + @Override + public void loadDataFromNBT(NBTBase aNBT) { + if (!(aNBT instanceof NBTTagCompound tag)) return; // not very good... + speed = tag.getInteger("mSpeed"); + tickRate = tag.getInteger("mTickRate"); + condition = Conditional.VALUES[tag.getByte("mCondition")]; + } + + @Nonnull + @Override + public ISerializableObject readFromPacket(ByteArrayDataInput aBuf, @Nullable EntityPlayerMP aPlayer) { + return new FluidRegulatorData(aBuf.readInt(), aBuf.readInt(), Conditional.VALUES[aBuf.readUnsignedByte()]); + } + + public int getTickRate() { + return tickRate; + } + + public void setTickRate(int tickRate) { + this.tickRate = tickRate; + } + + public int getSpeed() { + return speed; + } + + public void setSpeed(int speed) { + this.speed = speed; + } + + public Conditional getCondition() { + return condition; + } + + public void setCondition(Conditional condition) { + this.condition = condition; + } + } +} |