mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-03 06:14:41 +01:00
Fundamentals of Fluid Transfer
- Fixed some inconsistencies with a tanks' fluidhandler invalidation when resized - Patched crashes in present fluid handling of the basin - Tanks now slightly shade horizontal faces of the contained liquid - Tanks no longer resend data every tick when filled gradually - Introduced a new lerped value type with better design decisions - Refactored Smart tileentity serialization to better support custom overrides in contained behaviours - Pumps propagate flows in the pipe networks in front and behind itself. - Pumps collect all possible in and outputs across the reachable pipe graph as endpoints - Flows move across multiple branches of a pipe network when both are equally viable - Open-ended pipes are treated as endpoints and leak fluid into and out of a block space - Open endpoints serialize stateful information about fluid units gathered and held at the interface - Open endpoints turn a fluid block into 1000 fluid units and back - Open endpoints undo their transaction when their flow changes from pull to push - Open endpoints cannot pull fluids back when a full liquid block was not placed yet - Open endpoints waterlog blocks when the provided fluid is water - A collision response is triggered when different types of fluids meet at open endpoints - Fluids are transferred instantly by the throughput of a completed flow per tick - Pumps cut flows when vital pipes are removed - Pumps do not lose progress of finished flows when an unrelated part of the pipe network changes - Pumps do not lose progress of finished flows when reversed - Pumps distribute their throughput across available input flows evenly - Pumps distribute gathered input fluid across outputs evenly - Pumps expose furthest reachable pipefaces to other pumps for chained transfer - Chained pumps with fully overlapping flow sections provide their endpoints at the entrance of the other pump - Chained pumps with overlapping flow sections participate in two shared endpoints, one for each pump dominating the contested region - Chained pumps with overlapping flow only transfer via the optimal of the two possible endpoints based on their speeds - Chained pumps of equal speed pick one of the two available endpoints deterministically - Pumps transfer without flows when no pipe is between the pump and the endpoint - Pumps serialize and recover stateful information about held fluid units at open endpoints - Chained pumps do not actively transfer when both are partaking with push flows (or both pulling) - A pull flow originating from an inter-pump endpoint only commences when the corresponding push flow is completed - Chained pumps re-determine the optimal flow when the speed of one is changed at runtime - Throughput of chained pumps is determined by their weakest link in terms of speed - Endpoints created for chained pumps is treated equally to other available endpoints when fluid is distributed - Pipes do not contain a physical amount of fluid. - Pipes never hold serialized vital stateful information about fluid transfer. - Pipes synchronize local flow progress and fluid type to clients - Flows in a pipe progress with the speed of the network flow - A networks flow speed depends on the speed of the aggregated pump - Pipe flows of different flow graphs of different pumps interact with each other - A collision response is triggered when two different types of fluid meet within a pipe - Pipes spawn particles to illustrate contained flows/liquids of flows - The fluid transfer role is exposed through a TE behaviour with some callbacks and properties - Open endpoints show particles when interacting with in-world fluids
This commit is contained in:
parent
8e349380a5
commit
2040d66c3e
94 changed files with 3282 additions and 937 deletions
|
@ -11,6 +11,7 @@ public enum AllSpecialTextures {
|
|||
BLANK("blank.png"),
|
||||
CHECKERED("checkerboard.png"),
|
||||
THIN_CHECKERED("thin_checkerboard.png"),
|
||||
CUTOUT_CHECKERED("cutout_checkerboard.png"),
|
||||
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
|
||||
SELECTION("selection.png"),
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.pul
|
|||
import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyTileEntity;
|
||||
import com.simibubi.create.content.contraptions.components.turntable.TurntableTileEntity;
|
||||
import com.simibubi.create.content.contraptions.components.waterwheel.WaterWheelTileEntity;
|
||||
import com.simibubi.create.content.contraptions.fluids.FluidPipeTileEntity;
|
||||
import com.simibubi.create.content.contraptions.fluids.FluidTankRenderer;
|
||||
import com.simibubi.create.content.contraptions.fluids.FluidTankTileEntity;
|
||||
import com.simibubi.create.content.contraptions.fluids.PumpRenderer;
|
||||
|
@ -57,7 +58,11 @@ import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Sequen
|
|||
import com.simibubi.create.content.contraptions.relays.belt.BeltRenderer;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.elementary.SimpleKineticTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.*;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.AdjustablePulleyTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.ClutchTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.EncasedShaftRenderer;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.EncasedShaftTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.encased.SplitShaftRenderer;
|
||||
import com.simibubi.create.content.contraptions.relays.gauge.GaugeRenderer;
|
||||
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeTileEntity;
|
||||
|
@ -86,7 +91,12 @@ import com.simibubi.create.content.logistics.block.mechanicalArm.ArmRenderer;
|
|||
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.packager.PackagerRenderer;
|
||||
import com.simibubi.create.content.logistics.block.packager.PackagerTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.redstone.*;
|
||||
import com.simibubi.create.content.logistics.block.redstone.AnalogLeverRenderer;
|
||||
import com.simibubi.create.content.logistics.block.redstone.AnalogLeverTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.redstone.NixieTubeRenderer;
|
||||
import com.simibubi.create.content.logistics.block.redstone.NixieTubeTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.redstone.StockpileSwitchTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.transposer.LinkedTransposerTileEntity;
|
||||
import com.simibubi.create.content.logistics.block.transposer.TransposerTileEntity;
|
||||
import com.simibubi.create.content.schematics.block.SchematicTableTileEntity;
|
||||
|
@ -95,6 +105,7 @@ import com.simibubi.create.content.schematics.block.SchematicannonTileEntity;
|
|||
import com.simibubi.create.foundation.tileEntity.renderer.SmartTileEntityRenderer;
|
||||
import com.tterrag.registrate.util.entry.TileEntityEntry;
|
||||
import com.tterrag.registrate.util.nullness.NonNullFunction;
|
||||
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
|
||||
public class AllTileEntities {
|
||||
|
@ -191,6 +202,11 @@ public class AllTileEntities {
|
|||
.renderer(() -> PumpRenderer::new)
|
||||
.register();
|
||||
|
||||
public static final TileEntityEntry<FluidPipeTileEntity> FLUID_PIPE = Create.registrate()
|
||||
.tileEntity("fluid_pipe", (NonNullFunction<TileEntityType<FluidPipeTileEntity>, ? extends FluidPipeTileEntity>) FluidPipeTileEntity::new)
|
||||
.validBlocks(AllBlocks.FLUID_PIPE)
|
||||
.register();
|
||||
|
||||
public static final TileEntityEntry<FluidTankTileEntity> FLUID_TANK = Create.registrate()
|
||||
.tileEntity("fluid_tank", (NonNullFunction<TileEntityType<FluidTankTileEntity>, ? extends FluidTankTileEntity>) FluidTankTileEntity::new)
|
||||
.validBlocks(AllBlocks.FLUID_TANK)
|
||||
|
|
|
@ -43,7 +43,7 @@ public class KineticDebugger {
|
|||
VoxelShape shape = world.getBlockState(toOutline)
|
||||
.getRenderShape(world, toOutline);
|
||||
|
||||
if (te.getTheoreticalSpeed() != 0)
|
||||
if (te.getTheoreticalSpeed() != 0 && !shape.isEmpty())
|
||||
CreateClient.outliner.chaseAABB("kineticSource", shape.getBoundingBox()
|
||||
.offset(toOutline))
|
||||
.lineWidth(1 / 16f)
|
||||
|
|
|
@ -178,7 +178,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
protected void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putFloat("Speed", speed);
|
||||
|
||||
if (needsSpeedUpdate())
|
||||
|
@ -202,7 +202,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
compound.put("Network", networkTag);
|
||||
}
|
||||
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
public boolean needsSpeedUpdate() {
|
||||
|
@ -210,12 +210,13 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
boolean overStressedBefore = overStressed;
|
||||
clearKineticInformation();
|
||||
|
||||
// DO NOT READ kinetic information when placed after movement
|
||||
if (wasMoved) {
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -235,14 +236,9 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
overStressed = capacity < stress && StressImpact.isEnabled();
|
||||
}
|
||||
|
||||
super.read(compound);
|
||||
}
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
boolean overStressedBefore = overStressed;
|
||||
super.readClientUpdate(tag);
|
||||
if (overStressedBefore != overStressed && speed != 0)
|
||||
if (clientPacket && overStressedBefore != overStressed && speed != 0)
|
||||
effects.triggerOverStressedEffect();
|
||||
}
|
||||
|
||||
|
@ -450,7 +446,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
public int getFlickerScore() {
|
||||
return flickerTally;
|
||||
}
|
||||
|
||||
|
||||
public static float convertToDirection(float axisSpeed, Direction d) {
|
||||
return d.getAxisDirection() == AxisDirection.POSITIVE ? axisSpeed : -axisSpeed;
|
||||
}
|
||||
|
|
|
@ -58,21 +58,21 @@ public abstract class BlockBreakingKineticTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("Progress", destroyProgress);
|
||||
compound.putInt("NextTick", ticksUntilNextProgress);
|
||||
if (breakingPos != null)
|
||||
compound.put("Breaking", NBTUtil.writeBlockPos(breakingPos));
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
destroyProgress = compound.getInt("Progress");
|
||||
ticksUntilNextProgress = compound.getInt("NextTick");
|
||||
if (compound.contains("Breaking"))
|
||||
breakingPos = NBTUtil.readBlockPos(compound.getCompound("Breaking"));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.simibubi.create.content.contraptions.components.actors;
|
|||
import java.util.UUID;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
|
||||
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
|
|
@ -39,23 +39,23 @@ public class CuckooClockTileEntity extends KineticTileEntity {
|
|||
super(type);
|
||||
animationType = Animation.NONE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (sendAnimationUpdate)
|
||||
NBTHelper.writeEnum(compound, "Animation", animationType);
|
||||
sendAnimationUpdate = false;
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
if (tag.contains("Animation")) {
|
||||
animationType = NBTHelper.readEnum(tag, "Animation", Animation.class);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
if (clientPacket && compound.contains("Animation")) {
|
||||
animationType = NBTHelper.readEnum(compound, "Animation", Animation.class);
|
||||
animationProgress.lastValue = 0;
|
||||
animationProgress.value = 0;
|
||||
}
|
||||
super.readClientUpdate(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (clientPacket && sendAnimationUpdate)
|
||||
NBTHelper.writeEnum(compound, "Animation", animationType);
|
||||
sendAnimationUpdate = false;
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -123,7 +123,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
|
||||
CompoundNBT inputNBT = new CompoundNBT();
|
||||
|
@ -138,43 +138,19 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
|
|||
compound.putInt("CountDown", countDown);
|
||||
compound.putBoolean("Cover", covered);
|
||||
|
||||
return super.write(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT tag) {
|
||||
if (reRender) {
|
||||
tag.putBoolean("Redraw", true);
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
if (clientPacket && reRender) {
|
||||
compound.putBoolean("Redraw", true);
|
||||
reRender = false;
|
||||
}
|
||||
return super.writeToClient(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
if (tag.contains("Redraw"))
|
||||
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 16);
|
||||
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
Phase phaseBefore = phase;
|
||||
GroupedItems before = this.groupedItems;
|
||||
|
||||
super.readClientUpdate(tag);
|
||||
|
||||
if (phaseBefore != phase && phase == Phase.CRAFTING)
|
||||
groupedItemsBeforeCraft = before;
|
||||
if (phaseBefore == Phase.EXPORTING && phase == Phase.WAITING) {
|
||||
Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING);
|
||||
Vec3d vec = new Vec3d(facing.getDirectionVec()).scale(.75)
|
||||
.add(VecHelper.getCenterOf(pos));
|
||||
Direction targetDirection = MechanicalCrafterBlock.getTargetDirection(getBlockState());
|
||||
vec = vec.add(new Vec3d(targetDirection.getDirectionVec()).scale(1));
|
||||
world.addParticle(ParticleTypes.CRIT, vec.x, vec.y, vec.z, 0, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
|
||||
inventory.deserializeNBT(compound.getCompound("Inventory"));
|
||||
input.read(compound.getCompound("ConnectedInput"));
|
||||
groupedItems = GroupedItems.read(compound.getCompound("GroupedItems"));
|
||||
|
@ -186,7 +162,22 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
|
|||
this.phase = phase;
|
||||
countDown = compound.getInt("CountDown");
|
||||
covered = compound.getBoolean("Cover");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (compound.contains("Redraw"))
|
||||
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 16);
|
||||
if (phaseBefore != phase && phase == Phase.CRAFTING)
|
||||
groupedItemsBeforeCraft = before;
|
||||
if (phaseBefore == Phase.EXPORTING && phase == Phase.WAITING) {
|
||||
Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING);
|
||||
Vec3d vec = new Vec3d(facing.getDirectionVec()).scale(.75)
|
||||
.add(VecHelper.getCenterOf(pos));
|
||||
Direction targetDirection = MechanicalCrafterBlock.getTargetDirection(getBlockState());
|
||||
vec = vec.add(new Vec3d(targetDirection.getDirectionVec()).scale(1));
|
||||
world.addParticle(ParticleTypes.CRIT, vec.x, vec.y, vec.z, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -293,7 +284,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
|
|||
Vec3d vec = facingVec.scale(.65)
|
||||
.add(VecHelper.getCenterOf(pos));
|
||||
Vec3d offset = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f)
|
||||
.mul(VecHelper.planeByNormal(facingVec))
|
||||
.mul(VecHelper.axisAlingedPlaneOf(facingVec))
|
||||
.normalize()
|
||||
.scale(progress * .5f)
|
||||
.add(vec);
|
||||
|
@ -307,7 +298,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
|
|||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Vec3d randVec = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f)
|
||||
.mul(VecHelper.planeByNormal(facingVec))
|
||||
.mul(VecHelper.axisAlingedPlaneOf(facingVec))
|
||||
.normalize()
|
||||
.scale(.25f);
|
||||
Vec3d offset2 = randVec.add(vec);
|
||||
|
|
|
@ -38,15 +38,15 @@ public class HandCrankTileEntity extends GeneratingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("InUse", inUse);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
inUse = compound.getInt("InUse");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -215,19 +215,17 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (hasEntity())
|
||||
compound.put("Entity", NBTUtil.writeUniqueId(entityUUID));
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
compound.putFloat("Speed", crushingspeed);
|
||||
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
if (compound.contains("Entity") && !isFrozen() && !isOccupied()) {
|
||||
entityUUID = NBTUtil.readUniqueId(compound.getCompound("Entity"));
|
||||
this.searchForEntity = true;
|
||||
|
|
|
@ -51,11 +51,10 @@ import net.minecraftforge.items.ItemHandlerHelper;
|
|||
|
||||
public class DeployerTileEntity extends KineticTileEntity {
|
||||
|
||||
private static final List<Pair<BlockPos, Direction>> EXTRACTING_LOCATIONS = Arrays
|
||||
.asList(Direction.values())
|
||||
.stream()
|
||||
.map(d -> Pair.of(BlockPos.ZERO.offset(d), d.getOpposite()))
|
||||
.collect(Collectors.toList());
|
||||
private static final List<Pair<BlockPos, Direction>> EXTRACTING_LOCATIONS = Arrays.asList(Direction.values())
|
||||
.stream()
|
||||
.map(d -> Pair.of(BlockPos.ZERO.offset(d), d.getOpposite()))
|
||||
.collect(Collectors.toList());
|
||||
private FilteringBehaviour filtering;
|
||||
private ExtractingBehaviour extracting;
|
||||
|
||||
|
@ -167,7 +166,8 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
return;
|
||||
}
|
||||
|
||||
if (filtering.getFilter().isEmpty() && stack.isEmpty())
|
||||
if (filtering.getFilter()
|
||||
.isEmpty() && stack.isEmpty())
|
||||
extracting.extract(1);
|
||||
|
||||
Direction facing = getBlockState().get(FACING);
|
||||
|
@ -182,12 +182,16 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
|
||||
state = State.EXPANDING;
|
||||
Vec3d movementVector = getMovementVector();
|
||||
Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f));
|
||||
Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f));
|
||||
Vec3d rayOrigin = VecHelper.getCenterOf(pos)
|
||||
.add(movementVector.scale(3 / 2f));
|
||||
Vec3d rayTarget = VecHelper.getCenterOf(pos)
|
||||
.add(movementVector.scale(5 / 2f));
|
||||
RayTraceContext rayTraceContext =
|
||||
new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, FluidMode.NONE, player);
|
||||
BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext);
|
||||
reach = (float) (.5f + Math.min(result.getHitVec().subtract(rayOrigin).length(), .75f));
|
||||
reach = (float) (.5f + Math.min(result.getHitVec()
|
||||
.subtract(rayOrigin)
|
||||
.length(), .75f));
|
||||
|
||||
timer = 1000;
|
||||
sendData();
|
||||
|
@ -226,7 +230,9 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
if (!(otherTile instanceof DeployerTileEntity))
|
||||
return false;
|
||||
DeployerTileEntity deployerTile = (DeployerTileEntity) otherTile;
|
||||
if (world.getBlockState(otherDeployer).get(FACING).getOpposite() != facing || deployerTile.mode != Mode.PUNCH)
|
||||
if (world.getBlockState(otherDeployer)
|
||||
.get(FACING)
|
||||
.getOpposite() != facing || deployerTile.mode != Mode.PUNCH)
|
||||
return false;
|
||||
|
||||
boop = true;
|
||||
|
@ -295,13 +301,15 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
protected void tryDisposeOfItems() {
|
||||
boolean noInv = extracting.getInventories().isEmpty();
|
||||
boolean noInv = extracting.getInventories()
|
||||
.isEmpty();
|
||||
for (Iterator<ItemStack> iterator = overflowItems.iterator(); iterator.hasNext();) {
|
||||
ItemStack itemStack = iterator.next();
|
||||
|
||||
if (noInv) {
|
||||
Vec3d offset = getMovementVector();
|
||||
Vec3d outPos = VecHelper.getCenterOf(pos).add(offset.scale(-.65f));
|
||||
Vec3d outPos = VecHelper.getCenterOf(pos)
|
||||
.add(offset.scale(-.65f));
|
||||
Vec3d motion = offset.scale(-.25f);
|
||||
ItemEntity e = new ItemEntity(world, outPos.x, outPos.y, outPos.z, itemStack.copy());
|
||||
e.setMotion(motion);
|
||||
|
@ -328,11 +336,12 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
protected Vec3d getMovementVector() {
|
||||
if (!AllBlocks.DEPLOYER.has(getBlockState()))
|
||||
return Vec3d.ZERO;
|
||||
return new Vec3d(getBlockState().get(FACING).getDirectionVec());
|
||||
return new Vec3d(getBlockState().get(FACING)
|
||||
.getDirectionVec());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
state = NBTHelper.readEnum(compound, "State", State.class);
|
||||
mode = NBTHelper.readEnum(compound, "Mode", Mode.class);
|
||||
timer = compound.getInt("Timer");
|
||||
|
@ -340,48 +349,45 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
overflowItems = NBTHelper.readItemList(compound.getList("Overflow", NBT.TAG_COMPOUND));
|
||||
if (compound.contains("HeldItem"))
|
||||
heldItem = ItemStack.read(compound.getCompound("HeldItem"));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
reach = compound.getFloat("Reach");
|
||||
if (compound.contains("Particle")) {
|
||||
ItemStack particleStack = ItemStack.read(compound.getCompound("Particle"));
|
||||
SandPaperItem.spawnParticles(VecHelper.getCenterOf(pos)
|
||||
.add(getMovementVector().scale(2f)), particleStack, this.world);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
NBTHelper.writeEnum(compound, "Mode", mode);
|
||||
NBTHelper.writeEnum(compound, "State", state);
|
||||
compound.putInt("Timer", timer);
|
||||
if (player != null) {
|
||||
compound.put("HeldItem", player.getHeldItemMainhand().serializeNBT());
|
||||
compound.put("HeldItem", player.getHeldItemMainhand()
|
||||
.serializeNBT());
|
||||
ListNBT invNBT = new ListNBT();
|
||||
player.inventory.write(invNBT);
|
||||
compound.put("Inventory", invNBT);
|
||||
compound.put("Overflow", NBTHelper.writeItemList(overflowItems));
|
||||
}
|
||||
return super.write(compound);
|
||||
}
|
||||
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (!clientPacket)
|
||||
return;
|
||||
compound.putFloat("Reach", reach);
|
||||
if (player != null) {
|
||||
compound.put("HeldItem", player.getHeldItemMainhand().serializeNBT());
|
||||
if (player.spawnedItemEffects != null) {
|
||||
compound.put("Particle", player.spawnedItemEffects.serializeNBT());
|
||||
player.spawnedItemEffects = null;
|
||||
}
|
||||
if (player == null)
|
||||
return;
|
||||
compound.put("HeldItem", player.getHeldItemMainhand()
|
||||
.serializeNBT());
|
||||
if (player.spawnedItemEffects != null) {
|
||||
compound.put("Particle", player.spawnedItemEffects.serializeNBT());
|
||||
player.spawnedItemEffects = null;
|
||||
}
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
reach = tag.getFloat("Reach");
|
||||
if (tag.contains("Particle")) {
|
||||
ItemStack particleStack = ItemStack.read(tag.getCompound("Particle"));
|
||||
SandPaperItem
|
||||
.spawnParticles(VecHelper.getCenterOf(pos).add(getMovementVector().scale(2f)), particleStack,
|
||||
this.world);
|
||||
}
|
||||
|
||||
super.readClientUpdate(tag);
|
||||
}
|
||||
|
||||
private IItemHandlerModifiable createHandler() {
|
||||
|
@ -395,7 +401,7 @@ public class DeployerTileEntity extends KineticTileEntity {
|
|||
|
||||
public AllBlockPartials getHandPose() {
|
||||
return mode == Mode.PUNCH ? AllBlockPartials.DEPLOYER_HAND_PUNCHING
|
||||
: heldItem.isEmpty() ? AllBlockPartials.DEPLOYER_HAND_POINTING : AllBlockPartials.DEPLOYER_HAND_HOLDING;
|
||||
: heldItem.isEmpty() ? AllBlockPartials.DEPLOYER_HAND_POINTING : AllBlockPartials.DEPLOYER_HAND_HOLDING;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -165,7 +165,7 @@ public class AirCurrent {
|
|||
.get(BlockStateProperties.FACING);
|
||||
pushing = source.getAirFlowDirection() == direction;
|
||||
Vec3d directionVec = new Vec3d(direction.getDirectionVec());
|
||||
Vec3d planeVec = VecHelper.planeByNormal(directionVec);
|
||||
Vec3d planeVec = VecHelper.axisAlingedPlaneOf(directionVec);
|
||||
|
||||
// 4 Rays test for holes in the shapes blocking the flow
|
||||
float offsetDistance = .25f;
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlo
|
|||
import com.simibubi.create.content.logistics.block.chute.ChuteTileEntity;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.config.CKinetics;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
|
@ -30,21 +31,17 @@ public class EncasedFanTileEntity extends GeneratingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
super.readClientUpdate(tag);
|
||||
airCurrent.rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
isGenerator = compound.getBoolean("Generating");
|
||||
if (clientPacket)
|
||||
airCurrent.rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Generating", isGenerator);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,10 +74,12 @@ public class EncasedFanTileEntity extends GeneratingKineticTileEntity {
|
|||
return false;
|
||||
BlockState checkState = world.getBlockState(pos.down());
|
||||
|
||||
if (!checkState.getBlock().isIn(AllBlockTags.FAN_HEATERS.tag))
|
||||
if (!checkState.getBlock()
|
||||
.isIn(AllBlockTags.FAN_HEATERS.tag))
|
||||
return false;
|
||||
|
||||
if (checkState.has(BlazeBurnerBlock.HEAT_LEVEL) && !checkState.get(BlazeBurnerBlock.HEAT_LEVEL).isAtLeast(BlazeBurnerBlock.HeatLevel.FADING))
|
||||
if (checkState.has(BlazeBurnerBlock.HEAT_LEVEL) && !checkState.get(BlazeBurnerBlock.HEAT_LEVEL)
|
||||
.isAtLeast(BlazeBurnerBlock.HeatLevel.FADING))
|
||||
return false;
|
||||
|
||||
if (checkState.has(BlockStateProperties.LIT) && !checkState.get(BlockStateProperties.LIT))
|
||||
|
|
|
@ -38,26 +38,30 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
|
||||
}
|
||||
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
protected void write(CompoundNBT compound, boolean clientPacket) {
|
||||
super.write(compound, clientPacket);
|
||||
if (!clientPacket)
|
||||
return;
|
||||
compound.putFloat("Range", range);
|
||||
compound.putBoolean("Pushing", pushing);
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
range = tag.getFloat("Range");
|
||||
pushing = tag.getBoolean("Pushing");
|
||||
super.readClientUpdate(tag);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
if (!clientPacket)
|
||||
return;
|
||||
range = compound.getFloat("Range");
|
||||
pushing = compound.getBoolean("Pushing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
fanPos = pos.offset(getBlockState().get(NozzleBlock.FACING).getOpposite());
|
||||
fanPos = pos.offset(getBlockState().get(NozzleBlock.FACING)
|
||||
.getOpposite());
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
|
@ -72,24 +76,26 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
Vec3d center = VecHelper.getCenterOf(pos);
|
||||
if (world.isRemote && range != 0) {
|
||||
if (world.rand.nextInt(
|
||||
MathHelper.clamp((AllConfigs.SERVER.kinetics.fanPushDistance.get() - (int) range), 1, 10)) == 0) {
|
||||
MathHelper.clamp((AllConfigs.SERVER.kinetics.fanPushDistance.get() - (int) range), 1, 10)) == 0) {
|
||||
Vec3d start = VecHelper.offsetRandomly(center, world.rand, pushing ? 1 : range / 2);
|
||||
Vec3d motion = center.subtract(start).normalize()
|
||||
.scale(MathHelper.clamp(range * (pushing ? .025f : 1f), 0, .5f) * (pushing ? -1 : 1));
|
||||
Vec3d motion = center.subtract(start)
|
||||
.normalize()
|
||||
.scale(MathHelper.clamp(range * (pushing ? .025f : 1f), 0, .5f) * (pushing ? -1 : 1));
|
||||
world.addParticle(ParticleTypes.POOF, start.x, start.y, start.z, motion.x, motion.y, motion.z);
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<Entity> iterator = pushingEntities.iterator(); iterator.hasNext();) {
|
||||
Entity entity = iterator.next();
|
||||
Vec3d diff = entity.getPositionVec().subtract(center);
|
||||
Vec3d diff = entity.getPositionVec()
|
||||
.subtract(center);
|
||||
|
||||
if (!(entity instanceof PlayerEntity) && world.isRemote)
|
||||
continue;
|
||||
|
||||
double distance = diff.length();
|
||||
if (distance > range || entity.isSneaking()
|
||||
|| (entity instanceof PlayerEntity && ((PlayerEntity) entity).isCreative())) {
|
||||
|| (entity instanceof PlayerEntity && ((PlayerEntity) entity).isCreative())) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
@ -98,8 +104,10 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
continue;
|
||||
|
||||
float factor = (entity instanceof ItemEntity) ? 1 / 128f : 1 / 32f;
|
||||
Vec3d pushVec = diff.normalize().scale((range - distance) * (pushing ? 1 : -1));
|
||||
entity.setMotion(entity.getMotion().add(pushVec.scale(factor)));
|
||||
Vec3d pushVec = diff.normalize()
|
||||
.scale((range - distance) * (pushing ? 1 : -1));
|
||||
entity.setMotion(entity.getMotion()
|
||||
.add(pushVec.scale(factor)));
|
||||
entity.fallDistance = 0;
|
||||
entity.velocityChanged = true;
|
||||
}
|
||||
|
@ -125,7 +133,8 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
return 0;
|
||||
if (fan.getSpeed() == 0)
|
||||
return 0;
|
||||
pushing = fan.getAirFlowDirection() == fan.getBlockState().get(EncasedFanBlock.FACING);
|
||||
pushing = fan.getAirFlowDirection() == fan.getBlockState()
|
||||
.get(EncasedFanBlock.FACING);
|
||||
return fan.getMaxDistance();
|
||||
}
|
||||
|
||||
|
@ -140,11 +149,12 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
AxisAlignedBB bb = new AxisAlignedBB(center, center).grow(range / 2f);
|
||||
|
||||
for (Entity entity : world.getEntitiesWithinAABB(Entity.class, bb)) {
|
||||
Vec3d diff = entity.getPositionVec().subtract(center);
|
||||
Vec3d diff = entity.getPositionVec()
|
||||
.subtract(center);
|
||||
|
||||
double distance = diff.length();
|
||||
if (distance > range || entity.isSneaking()
|
||||
|| (entity instanceof PlayerEntity && ((PlayerEntity) entity).isCreative())) {
|
||||
|| (entity instanceof PlayerEntity && ((PlayerEntity) entity).isCreative())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -164,7 +174,7 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
continue;
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
|
||||
if (!pushing && pushingEntities.size() > 256 && !world.isRemote) {
|
||||
world.createExplosion(null, center.x, center.y, center.z, 2, Mode.NONE);
|
||||
for (Iterator<Entity> iterator = pushingEntities.iterator(); iterator.hasNext();) {
|
||||
|
@ -178,8 +188,9 @@ public class NozzleTileEntity extends SmartTileEntity {
|
|||
|
||||
private boolean canSee(Entity entity) {
|
||||
RayTraceContext context = new RayTraceContext(entity.getPositionVec(), VecHelper.getCenterOf(pos),
|
||||
BlockMode.COLLIDER, FluidMode.NONE, entity);
|
||||
return pos.equals(world.rayTraceBlocks(context).getPos());
|
||||
BlockMode.COLLIDER, FluidMode.NONE, entity);
|
||||
return pos.equals(world.rayTraceBlocks(context)
|
||||
.getPos());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,30 +53,21 @@ public class FlywheelTileEntity extends GeneratingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
super.readClientUpdate(tag);
|
||||
visualSpeed.withSpeed(1 / 32f).target(getGeneratedSpeed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putFloat("GeneratedSpeed", generatedSpeed);
|
||||
compound.putFloat("GeneratedCapacity", generatedCapacity);
|
||||
compound.putInt("Cooldown", stoppingCooldown);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
generatedSpeed = compound.getFloat("GeneratedSpeed");
|
||||
generatedCapacity = compound.getFloat("GeneratedCapacity");
|
||||
stoppingCooldown = compound.getInt("Cooldown");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
if (clientPacket)
|
||||
visualSpeed.withSpeed(1 / 32f).target(getGeneratedSpeed());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -116,19 +116,19 @@ public class MillstoneTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("Timer", timer);
|
||||
compound.put("InputInventory", inputInv.serializeNBT());
|
||||
compound.put("OutputInventory", outputInv.serializeNBT());
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
timer = compound.getInt("Timer");
|
||||
inputInv.deserializeNBT(compound.getCompound("InputInventory"));
|
||||
outputInv.deserializeNBT(compound.getCompound("OutputInventory"));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
public int getProcessingSpeed() {
|
||||
|
|
|
@ -109,17 +109,17 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
running = compound.getBoolean("Running");
|
||||
runningTicks = compound.getInt("Ticks");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Running", running);
|
||||
compound.putInt("Ticks", runningTicks);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.simibubi.create.foundation.advancement.AllTriggers;
|
|||
import com.simibubi.create.foundation.item.ItemHelper;
|
||||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour;
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
@ -25,7 +26,6 @@ import net.minecraft.item.crafting.ICraftingRecipe;
|
|||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.particles.ItemParticleData;
|
||||
import net.minecraft.particles.ParticleTypes;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
|
@ -85,37 +85,30 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
running = compound.getBoolean("Running");
|
||||
mode = Mode.values()[compound.getInt("Mode")];
|
||||
finished = compound.getBoolean("Finished");
|
||||
runningTicks = compound.getInt("Ticks");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (clientPacket) {
|
||||
NBTHelper.iterateCompoundList(compound.getList("ParticleItems", NBT.TAG_COMPOUND),
|
||||
c -> pressedItems.add(ItemStack.read(c)));
|
||||
spawnParticles();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Running", running);
|
||||
compound.putInt("Mode", mode.ordinal());
|
||||
compound.putBoolean("Finished", finished);
|
||||
compound.putInt("Ticks", runningTicks);
|
||||
return super.write(compound);
|
||||
}
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT tag) {
|
||||
ListNBT particleItems = new ListNBT();
|
||||
pressedItems.forEach(stack -> particleItems.add(stack.serializeNBT()));
|
||||
tag.put("ParticleItems", particleItems);
|
||||
return super.writeToClient(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
super.readClientUpdate(tag);
|
||||
ListNBT particleItems = tag.getList("ParticleItems", NBT.TAG_COMPOUND);
|
||||
particleItems.forEach(nbt -> pressedItems.add(ItemStack.read((CompoundNBT) nbt)));
|
||||
spawnParticles();
|
||||
if (clientPacket)
|
||||
compound.put("ParticleItems", NBTHelper.writeCompoundList(pressedItems, ItemStack::serializeNBT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -301,7 +294,8 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
|
|||
|
||||
CombinedItemFluidList remaining = new CombinedItemFluidList();
|
||||
inputs.forEachItemStack(stack -> remaining.add(stack.copy()));
|
||||
basinFluidInv.ifPresent(fluidInv -> ((CombinedFluidHandler) fluidInv).forEachTank(fluidStack -> remaining.add(fluidStack.copy())));
|
||||
basinFluidInv.ifPresent(
|
||||
fluidInv -> ((CombinedFluidHandler) fluidInv).forEachTank(fluidStack -> remaining.add(fluidStack.copy())));
|
||||
|
||||
Ingredients: for (Ingredient ingredient : ingredients) {
|
||||
for (ItemStack stack : remaining.getItemStacks()) {
|
||||
|
|
|
@ -94,15 +94,15 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
compound.putInt("RecipeIndex", recipeIndex);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
inventory.deserializeNBT(compound.getCompound("Inventory"));
|
||||
recipeIndex = compound.getInt("RecipeIndex");
|
||||
}
|
||||
|
|
|
@ -797,7 +797,7 @@ public abstract class Contraption {
|
|||
|
||||
Vec3d vec = new Vec3d(Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis)
|
||||
.getDirectionVec());
|
||||
Vec3d planeByNormal = VecHelper.planeByNormal(vec);
|
||||
Vec3d planeByNormal = VecHelper.axisAlingedPlaneOf(vec);
|
||||
Vec3d min = vec.mul(bb.minX, bb.minY, bb.minZ)
|
||||
.add(planeByNormal.scale(-maxDiff));
|
||||
Vec3d max = vec.mul(bb.maxX, bb.maxY, bb.maxZ)
|
||||
|
|
|
@ -476,7 +476,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
BearingContraption bc = (BearingContraption) getContraption();
|
||||
Direction facing = bc.getFacing();
|
||||
Vec3d activeAreaOffset = actor.getActiveAreaOffset(context);
|
||||
if (activeAreaOffset.mul(VecHelper.planeByNormal(new Vec3d(facing.getDirectionVec())))
|
||||
if (activeAreaOffset.mul(VecHelper.axisAlingedPlaneOf(new Vec3d(facing.getDirectionVec())))
|
||||
.equals(Vec3d.ZERO)) {
|
||||
if (VecHelper.onSameAxis(blockInfo.pos, BlockPos.ZERO, facing.getAxis())) {
|
||||
context.motion = new Vec3d(facing.getDirectionVec()).scale(facing.getAxis()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||
import org.apache.commons.lang3.mutable.MutableObject;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket;
|
||||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
import com.simibubi.create.foundation.utility.RaycastHelper;
|
|
@ -227,26 +227,26 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag.putBoolean("Running", running);
|
||||
tag.putFloat("HourAngle", hourAngle);
|
||||
tag.putFloat("MinuteAngle", minuteAngle);
|
||||
return super.write(tag);
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Running", running);
|
||||
compound.putFloat("HourAngle", hourAngle);
|
||||
compound.putFloat("MinuteAngle", minuteAngle);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
running = tag.getBoolean("Running");
|
||||
hourAngle = tag.getFloat("HourAngle");
|
||||
minuteAngle = tag.getFloat("MinuteAngle");
|
||||
super.read(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
float hourAngleBefore = hourAngle;
|
||||
float minuteAngleBefore = minuteAngle;
|
||||
super.readClientUpdate(tag);
|
||||
|
||||
running = compound.getBoolean("Running");
|
||||
hourAngle = compound.getFloat("HourAngle");
|
||||
minuteAngle = compound.getFloat("MinuteAngle");
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
|
||||
if (running) {
|
||||
clientHourAngleDiff = AngleHelper.getShortestAngleDiff(hourAngleBefore, hourAngle);
|
||||
clientMinuteAngleDiff = AngleHelper.getShortestAngleDiff(minuteAngleBefore, minuteAngle);
|
||||
|
|
|
@ -102,27 +102,25 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag.putBoolean("Running", running);
|
||||
tag.putBoolean("Windmill", isWindmill);
|
||||
tag.putFloat("Angle", angle);
|
||||
tag.putFloat("LastGenerated", lastGeneratedSpeed);
|
||||
return super.write(tag);
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Running", running);
|
||||
compound.putBoolean("Windmill", isWindmill);
|
||||
compound.putFloat("Angle", angle);
|
||||
compound.putFloat("LastGenerated", lastGeneratedSpeed);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
running = tag.getBoolean("Running");
|
||||
isWindmill = tag.getBoolean("Windmill");
|
||||
angle = tag.getFloat("Angle");
|
||||
lastGeneratedSpeed = tag.getFloat("LastGenerated");
|
||||
super.read(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
float angleBefore = angle;
|
||||
super.readClientUpdate(tag);
|
||||
running = compound.getBoolean("Running");
|
||||
isWindmill = compound.getBoolean("Windmill");
|
||||
angle = compound.getFloat("Angle");
|
||||
lastGeneratedSpeed = compound.getFloat("LastGenerated");
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (running) {
|
||||
clientAngleDiff = AngleHelper.getShortestAngleDiff(angleBefore, angle);
|
||||
angle = angleBefore;
|
||||
|
|
|
@ -81,7 +81,7 @@ public class SuperGlueItem extends Item {
|
|||
@OnlyIn(Dist.CLIENT)
|
||||
public static void spawnParticles(World world, BlockPos pos, Direction direction, boolean fullBlock) {
|
||||
Vec3d vec = new Vec3d(direction.getDirectionVec());
|
||||
Vec3d plane = VecHelper.planeByNormal(vec);
|
||||
Vec3d plane = VecHelper.axisAlingedPlaneOf(vec);
|
||||
Vec3d facePos = VecHelper.getCenterOf(pos)
|
||||
.add(vec.scale(.5f));
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.mojang.blaze3d.vertex.IVertexBuilder;
|
|||
import com.simibubi.create.AllItems;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||
import com.simibubi.create.foundation.utility.MatrixStacker;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -63,7 +64,9 @@ public class SuperGlueRenderer extends EntityRenderer<SuperGlueEntity> {
|
|||
Direction face = entity.getFacingDirection();
|
||||
|
||||
ms.push();
|
||||
AngleHelper.applyRotation(face, ms);
|
||||
MatrixStacker.of(ms)
|
||||
.rotateY(AngleHelper.horizontalAngle(face))
|
||||
.rotateX(AngleHelper.verticalAngle(face));
|
||||
Entry peek = ms.peek();
|
||||
|
||||
Vec3d[][] quads = { quad1, quad2 };
|
||||
|
@ -87,7 +90,7 @@ public class SuperGlueRenderer extends EntityRenderer<SuperGlueEntity> {
|
|||
Vec3d diff = new Vec3d(Direction.SOUTH.getDirectionVec());
|
||||
Vec3d extension = diff.normalize()
|
||||
.scale(1 / 32f - 1 / 128f);
|
||||
Vec3d plane = VecHelper.planeByNormal(diff);
|
||||
Vec3d plane = VecHelper.axisAlingedPlaneOf(diff);
|
||||
Axis axis = Direction.getFacingFromVector(diff.x, diff.y, diff.z)
|
||||
.getAxis();
|
||||
|
||||
|
|
|
@ -145,50 +145,38 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag.putBoolean("Running", running);
|
||||
tag.putBoolean("Waiting", waitingForSpeedChange);
|
||||
tag.putFloat("Offset", offset);
|
||||
return super.write(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (forceMove) {
|
||||
protected void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Running", running);
|
||||
compound.putBoolean("Waiting", waitingForSpeedChange);
|
||||
compound.putFloat("Offset", offset);
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
if (clientPacket && forceMove) {
|
||||
compound.putBoolean("ForceMovement", forceMove);
|
||||
forceMove = false;
|
||||
}
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
running = tag.getBoolean("Running");
|
||||
waitingForSpeedChange = tag.getBoolean("Waiting");
|
||||
offset = tag.getFloat("Offset");
|
||||
super.read(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
boolean forceMovement = tag.contains("ForceMovement");
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
boolean forceMovement = compound.contains("ForceMovement");
|
||||
float offsetBefore = offset;
|
||||
super.readClientUpdate(tag);
|
||||
|
||||
if (forceMovement) {
|
||||
if (movedContraption != null) {
|
||||
applyContraptionPosition();
|
||||
}
|
||||
} else {
|
||||
if (running) {
|
||||
clientOffsetDiff = offset - offsetBefore;
|
||||
offset = offsetBefore;
|
||||
}
|
||||
running = compound.getBoolean("Running");
|
||||
waitingForSpeedChange = compound.getBoolean("Waiting");
|
||||
offset = compound.getFloat("Offset");
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (forceMovement)
|
||||
applyContraptionPosition();
|
||||
else if (running) {
|
||||
clientOffsetDiff = offset - offsetBefore;
|
||||
offset = offsetBefore;
|
||||
}
|
||||
|
||||
if (!running)
|
||||
movedContraption = null;
|
||||
|
||||
}
|
||||
|
||||
public abstract void disassemble();
|
||||
|
|
|
@ -29,15 +29,15 @@ public class MechanicalPistonTileEntity extends LinearActuatorTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
extensionLength = tag.getInt("ExtensionLength");
|
||||
super.read(tag);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
extensionLength = compound.getInt("ExtensionLength");
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
protected void write(CompoundNBT tag, boolean clientPacket) {
|
||||
tag.putInt("ExtensionLength", extensionLength);
|
||||
return super.write(tag);
|
||||
super.write(tag, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.pis
|
|||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.CenteredSideValueBoxTransform;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.IWaterLoggable;
|
||||
|
@ -168,15 +169,15 @@ public class PulleyTileEntity extends LinearActuatorTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
initialOffset = tag.getInt("InitialOffset");
|
||||
super.read(tag);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
initialOffset = compound.getInt("InitialOffset");
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag.putInt("InitialOffset", initialOffset);
|
||||
return super.write(tag);
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("InitialOffset", initialOffset);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,8 +24,8 @@ public class WaterWheelTileEntity extends GeneratingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
if (compound.contains("Flows")) {
|
||||
for (Direction d : Direction.values())
|
||||
setFlow(d, compound.getCompound("Flows")
|
||||
|
@ -39,13 +39,13 @@ public class WaterWheelTileEntity extends GeneratingKineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
CompoundNBT flows = new CompoundNBT();
|
||||
for (Direction d : Direction.values())
|
||||
flows.putFloat(d.getName(), this.flows.get(d));
|
||||
compound.put("Flows", flows);
|
||||
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
public void setFlow(Direction direction, float speed) {
|
||||
|
|
|
@ -87,17 +87,20 @@ public class CombinedFluidHandler implements IFluidHandler {
|
|||
@Nonnull
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, FluidAction action) {
|
||||
|
||||
FluidStack stack = new FluidStack(tanks[0].getFluid(), 0);
|
||||
|
||||
for (int i = 0; i < tanks.length; i++) {
|
||||
if (tanks[i].isFluidEqual(stack)) {
|
||||
if (stack.isEmpty() || tanks[i].isFluidEqual(stack)) {
|
||||
int newDrainAmount = MathHelper.clamp(stack.getAmount() + tanks[i].getAmount(), 0, maxDrain);
|
||||
if (action == FluidAction.EXECUTE) {
|
||||
tanks[i].shrink(newDrainAmount - stack.getAmount());
|
||||
if (tanks[i].isEmpty())
|
||||
tanks[i] = FluidStack.EMPTY;
|
||||
}
|
||||
if (stack.isEmpty())
|
||||
stack = tanks[i].copy();
|
||||
if (stack.isEmpty())
|
||||
continue;
|
||||
stack.setAmount(newDrainAmount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
public class FluidNetwork {
|
||||
|
||||
BlockFace pumpLocation;
|
||||
Map<BlockPos, Pair<Integer, Map<Direction, Boolean>>> pipeGraph;
|
||||
List<FluidNetworkFlow> flows;
|
||||
Set<FluidNetworkEndpoint> targets;
|
||||
Set<BlockFace> rangeEndpoints;
|
||||
Map<BlockFace, FluidStack> previousFlow;
|
||||
|
||||
boolean connectToPumps;
|
||||
int waitForUnloadedNetwork;
|
||||
|
||||
public FluidNetwork() {
|
||||
pipeGraph = new HashMap<>();
|
||||
flows = new ArrayList<>();
|
||||
targets = new HashSet<>();
|
||||
rangeEndpoints = new HashSet<>();
|
||||
previousFlow = new HashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasEndpoints() {
|
||||
for (FluidNetworkFlow pipeFlow : flows)
|
||||
if (pipeFlow.hasValidTargets())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<FluidNetworkEndpoint> getEndpoints(boolean pulling) {
|
||||
if (!pulling) {
|
||||
for (FluidNetworkFlow pipeFlow : flows)
|
||||
return pipeFlow.outputEndpoints;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
List<FluidNetworkEndpoint> list = new ArrayList<>();
|
||||
for (FluidNetworkFlow pipeFlow : flows) {
|
||||
if (!pipeFlow.hasValidTargets())
|
||||
continue;
|
||||
list.add(pipeFlow.source);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void tick(IWorld world, PumpTileEntity pumpTE) {
|
||||
if (connectToPumps) {
|
||||
connectToOtherFNs(world, pumpTE);
|
||||
connectToPumps = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void tickFlows(IWorld world, PumpTileEntity pumpTE, boolean pulling, float speed) {
|
||||
if (connectToPumps)
|
||||
return;
|
||||
initFlows(pumpTE, pulling);
|
||||
previousFlow.clear();
|
||||
flows.forEach(ep -> ep.tick(world, speed));
|
||||
}
|
||||
|
||||
private void initFlows(PumpTileEntity pumpTE, boolean pulling) {
|
||||
if (targets.isEmpty())
|
||||
return;
|
||||
if (!flows.isEmpty())
|
||||
return;
|
||||
World world = pumpTE.getWorld();
|
||||
if (pulling) {
|
||||
targets.forEach(ne -> flows.add(new FluidNetworkFlow(this, ne, world, pulling)));
|
||||
} else {
|
||||
PumpEndpoint pumpEndpoint = new PumpEndpoint(pumpLocation.getOpposite(), pumpTE);
|
||||
flows.add(new FluidNetworkFlow(this, pumpEndpoint, world, pulling));
|
||||
}
|
||||
}
|
||||
|
||||
public void connectToOtherFNs(IWorld world, PumpTileEntity pump) {
|
||||
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
|
||||
Set<BlockPos> visited = new HashSet<>();
|
||||
int maxDistance = FluidPropagator.getPumpRange() * 2;
|
||||
frontier.add(Pair.of(-1, pumpLocation.getPos()));
|
||||
|
||||
while (!frontier.isEmpty()) {
|
||||
Pair<Integer, BlockPos> entry = frontier.remove(0);
|
||||
int distance = entry.getFirst();
|
||||
BlockPos currentPos = entry.getSecond();
|
||||
|
||||
if (!world.isAreaLoaded(currentPos, 0))
|
||||
continue;
|
||||
if (visited.contains(currentPos))
|
||||
continue;
|
||||
visited.add(currentPos);
|
||||
|
||||
List<Direction> connections;
|
||||
if (currentPos.equals(pumpLocation.getPos())) {
|
||||
connections = ImmutableList.of(pumpLocation.getFace());
|
||||
} else {
|
||||
BlockState currentState = world.getBlockState(currentPos);
|
||||
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos);
|
||||
if (pipe == null)
|
||||
continue;
|
||||
connections = FluidPropagator.getPipeConnections(currentState, pipe);
|
||||
}
|
||||
|
||||
for (Direction face : connections) {
|
||||
BlockFace blockFace = new BlockFace(currentPos, face);
|
||||
BlockPos connectedPos = blockFace.getConnectedPos();
|
||||
BlockState connectedState = world.getBlockState(connectedPos);
|
||||
|
||||
if (connectedPos.equals(pumpLocation.getPos()))
|
||||
continue;
|
||||
if (!world.isAreaLoaded(connectedPos, 0))
|
||||
continue;
|
||||
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING)
|
||||
.getAxis() == face.getAxis()) {
|
||||
TileEntity tileEntity = world.getTileEntity(connectedPos);
|
||||
if (tileEntity instanceof PumpTileEntity) {
|
||||
PumpTileEntity otherPump = (PumpTileEntity) tileEntity;
|
||||
if (otherPump.networks == null)
|
||||
continue;
|
||||
|
||||
otherPump.networks.forEach(fn -> {
|
||||
int nearest = Integer.MAX_VALUE;
|
||||
BlockFace argNearest = null;
|
||||
for (BlockFace pumpEndpoint : fn.rangeEndpoints) {
|
||||
if (pumpEndpoint.isEquivalent(pumpLocation)) {
|
||||
argNearest = pumpEndpoint;
|
||||
break;
|
||||
}
|
||||
Pair<Integer, Map<Direction, Boolean>> pair =
|
||||
pipeGraph.get(pumpEndpoint.getConnectedPos());
|
||||
if (pair == null)
|
||||
continue;
|
||||
Integer distanceFromPump = pair.getFirst();
|
||||
Map<Direction, Boolean> pipeConnections = pair.getSecond();
|
||||
|
||||
if (!pipeConnections.containsKey(pumpEndpoint.getOppositeFace()))
|
||||
continue;
|
||||
if (nearest <= distanceFromPump)
|
||||
continue;
|
||||
nearest = distanceFromPump;
|
||||
argNearest = pumpEndpoint;
|
||||
|
||||
}
|
||||
if (argNearest != null) {
|
||||
InterPumpEndpoint endpoint = new InterPumpEndpoint(world, argNearest.getOpposite(),
|
||||
pump, otherPump, pumpLocation, fn.pumpLocation);
|
||||
targets.add(endpoint);
|
||||
fn.targets.add(endpoint.opposite(world));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (visited.contains(connectedPos))
|
||||
continue;
|
||||
if (distance > maxDistance)
|
||||
continue;
|
||||
FluidPipeBehaviour targetPipe = FluidPropagator.getPipe(world, connectedPos);
|
||||
if (targetPipe == null)
|
||||
continue;
|
||||
if (targetPipe.isConnectedTo(connectedState, face.getOpposite()))
|
||||
frontier.add(Pair.of(distance + 1, connectedPos));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void assemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) {
|
||||
Map<BlockFace, OpenEndedPipe> openEnds = pumpTE.getOpenEnds(pumpLocation.getFace());
|
||||
openEnds.values()
|
||||
.forEach(OpenEndedPipe::markStale);
|
||||
|
||||
this.pumpLocation = pumpLocation;
|
||||
if (!collectEndpoint(world, pumpLocation, openEnds, 0)) {
|
||||
|
||||
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
|
||||
Set<BlockPos> visited = new HashSet<>();
|
||||
int maxDistance = FluidPropagator.getPumpRange();
|
||||
frontier.add(Pair.of(0, pumpLocation.getConnectedPos()));
|
||||
|
||||
while (!frontier.isEmpty()) {
|
||||
Pair<Integer, BlockPos> entry = frontier.remove(0);
|
||||
int distance = entry.getFirst();
|
||||
BlockPos currentPos = entry.getSecond();
|
||||
|
||||
if (!world.isAreaLoaded(currentPos, 0))
|
||||
continue;
|
||||
if (visited.contains(currentPos))
|
||||
continue;
|
||||
visited.add(currentPos);
|
||||
BlockState currentState = world.getBlockState(currentPos);
|
||||
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos);
|
||||
if (pipe == null)
|
||||
continue;
|
||||
|
||||
for (Direction face : FluidPropagator.getPipeConnections(currentState, pipe)) {
|
||||
BlockFace blockFace = new BlockFace(currentPos, face);
|
||||
BlockPos connectedPos = blockFace.getConnectedPos();
|
||||
|
||||
if (connectedPos.equals(pumpLocation.getPos())) {
|
||||
addEntry(blockFace.getPos(), blockFace.getFace(), true, distance);
|
||||
continue;
|
||||
}
|
||||
if (!world.isAreaLoaded(connectedPos, 0))
|
||||
continue;
|
||||
if (collectEndpoint(world, blockFace, openEnds, distance))
|
||||
continue;
|
||||
if (FluidPropagator.getPipe(world, connectedPos) == null)
|
||||
continue;
|
||||
if (visited.contains(connectedPos))
|
||||
continue;
|
||||
if (distance + 1 >= maxDistance) {
|
||||
rangeEndpoints.add(blockFace);
|
||||
addEntry(currentPos, face, false, distance);
|
||||
FluidPropagator.showBlockFace(blockFace)
|
||||
.lineWidth(1 / 8f)
|
||||
.colored(0xff0000);
|
||||
continue;
|
||||
}
|
||||
|
||||
addConnection(connectedPos, currentPos, face.getOpposite(), distance);
|
||||
frontier.add(Pair.of(distance + 1, connectedPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<BlockFace> staleEnds = new HashSet<>();
|
||||
openEnds.entrySet()
|
||||
.forEach(e -> {
|
||||
if (e.getValue()
|
||||
.isStale())
|
||||
staleEnds.add(e.getKey());
|
||||
});
|
||||
staleEnds.forEach(openEnds::remove);
|
||||
|
||||
connectToPumps = true;
|
||||
}
|
||||
|
||||
private FluidNetworkEndpoint reuseOrCreateOpenEnd(IWorld world, Map<BlockFace, OpenEndedPipe> openEnds,
|
||||
BlockFace toCreate) {
|
||||
OpenEndedPipe openEndedPipe = null;
|
||||
if (openEnds.containsKey(toCreate)) {
|
||||
openEndedPipe = openEnds.get(toCreate);
|
||||
openEndedPipe.unmarkStale();
|
||||
} else {
|
||||
openEndedPipe = new OpenEndedPipe(toCreate);
|
||||
openEnds.put(toCreate, openEndedPipe);
|
||||
}
|
||||
return new FluidNetworkEndpoint(world, toCreate, openEndedPipe.getCapability());
|
||||
|
||||
}
|
||||
|
||||
private boolean collectEndpoint(IWorld world, BlockFace blockFace, Map<BlockFace, OpenEndedPipe> openEnds,
|
||||
int distance) {
|
||||
BlockPos connectedPos = blockFace.getConnectedPos();
|
||||
BlockState connectedState = world.getBlockState(connectedPos);
|
||||
|
||||
// other pipe, no endpoint
|
||||
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, connectedPos);
|
||||
if (pipe != null && pipe.isConnectedTo(connectedState, blockFace.getOppositeFace()))
|
||||
return false;
|
||||
TileEntity tileEntity = world.getTileEntity(connectedPos);
|
||||
|
||||
// fluid handler endpoint
|
||||
Direction face = blockFace.getFace();
|
||||
if (tileEntity != null) {
|
||||
LazyOptional<IFluidHandler> capability =
|
||||
tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face.getOpposite());
|
||||
if (capability.isPresent()) {
|
||||
targets.add(new FluidNetworkEndpoint(world, blockFace, capability));
|
||||
addEntry(blockFace.getPos(), face, false, distance);
|
||||
FluidPropagator.showBlockFace(blockFace)
|
||||
.colored(0x00b7c2)
|
||||
.lineWidth(1 / 8f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// open endpoint
|
||||
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING)
|
||||
.getAxis() == face.getAxis()) {
|
||||
rangeEndpoints.add(blockFace);
|
||||
addEntry(blockFace.getPos(), face, false, distance);
|
||||
return true;
|
||||
}
|
||||
if (!FluidPropagator.isOpenEnd(world, blockFace.getPos(), face))
|
||||
return false;
|
||||
|
||||
targets.add(reuseOrCreateOpenEnd(world, openEnds, blockFace));
|
||||
addEntry(blockFace.getPos(), face, false, distance);
|
||||
FluidPropagator.showBlockFace(blockFace)
|
||||
.colored(0xb700c2)
|
||||
.lineWidth(1 / 8f);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addConnection(BlockPos from, BlockPos to, Direction direction, int distance) {
|
||||
addEntry(from, direction, true, distance);
|
||||
addEntry(to, direction.getOpposite(), false, distance + 1);
|
||||
}
|
||||
|
||||
private void addEntry(BlockPos pos, Direction direction, boolean outbound, int distance) {
|
||||
if (!pipeGraph.containsKey(pos))
|
||||
pipeGraph.put(pos, Pair.of(distance, new HashMap<>()));
|
||||
pipeGraph.get(pos)
|
||||
.getSecond()
|
||||
.put(direction, outbound);
|
||||
}
|
||||
|
||||
public void reAssemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) {
|
||||
rangeEndpoints.clear();
|
||||
targets.clear();
|
||||
pipeGraph.clear();
|
||||
assemble(world, pumpTE, pumpLocation);
|
||||
}
|
||||
|
||||
public void remove(IWorld world) {
|
||||
clearFlows(world, false);
|
||||
}
|
||||
|
||||
public void clearFlows(IWorld world, boolean saveState) {
|
||||
for (FluidNetworkFlow networkFlow : flows) {
|
||||
if (!networkFlow.getFluidStack()
|
||||
.isEmpty())
|
||||
networkFlow.addToSkippedConnections(world);
|
||||
networkFlow.resetFlow(world);
|
||||
}
|
||||
flows.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Couple;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||
|
||||
class FluidNetworkEndpoint {
|
||||
BlockFace location;
|
||||
protected LazyOptional<IFluidHandler> handler;
|
||||
|
||||
public FluidNetworkEndpoint(IWorld world, BlockFace location, LazyOptional<IFluidHandler> handler) {
|
||||
this.location = location;
|
||||
this.handler = handler;
|
||||
this.handler.addListener($ -> onHandlerInvalidated(world));
|
||||
}
|
||||
|
||||
protected void onHandlerInvalidated(IWorld world) {
|
||||
IFluidHandler tank = handler.orElse(null);
|
||||
if (tank != null)
|
||||
return;
|
||||
TileEntity tileEntity = world.getTileEntity(location.getConnectedPos());
|
||||
if (tileEntity == null)
|
||||
return;
|
||||
LazyOptional<IFluidHandler> capability =
|
||||
tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, location.getOppositeFace());
|
||||
if (capability.isPresent()) {
|
||||
handler = capability;
|
||||
handler.addListener($ -> onHandlerInvalidated(world));
|
||||
}
|
||||
}
|
||||
|
||||
public FluidStack provideFluid() {
|
||||
IFluidHandler tank = provideHandler().orElse(null);
|
||||
if (tank == null)
|
||||
return FluidStack.EMPTY;
|
||||
return tank.drain(1, FluidAction.SIMULATE);
|
||||
}
|
||||
|
||||
public LazyOptional<IFluidHandler> provideHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PumpEndpoint extends FluidNetworkEndpoint {
|
||||
|
||||
PumpTileEntity pumpTE;
|
||||
|
||||
public PumpEndpoint(BlockFace location, PumpTileEntity pumpTE) {
|
||||
super(pumpTE.getWorld(), location, LazyOptional.empty());
|
||||
this.pumpTE = pumpTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandlerInvalidated(IWorld world) {}
|
||||
|
||||
@Override
|
||||
public FluidStack provideFluid() {
|
||||
return pumpTE.providedFluid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InterPumpEndpoint extends FluidNetworkEndpoint {
|
||||
|
||||
Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> pumps;
|
||||
|
||||
private InterPumpEndpoint(IWorld world, BlockFace location, LazyOptional<IFluidHandler> handler) {
|
||||
super(world, location, handler);
|
||||
}
|
||||
|
||||
public InterPumpEndpoint(IWorld world, BlockFace location, PumpTileEntity source, PumpTileEntity interfaced,
|
||||
BlockFace sourcePos, BlockFace interfacedPos) {
|
||||
this(world, location, LazyOptional.empty());
|
||||
handler = LazyOptional.of(() -> new InterPumpFluidHandler(this));
|
||||
pumps = Couple.create(Pair.of(sourcePos, new WeakReference<>(source)),
|
||||
Pair.of(interfacedPos, new WeakReference<>(interfaced)));
|
||||
}
|
||||
|
||||
public InterPumpEndpoint opposite(IWorld world) {
|
||||
InterPumpEndpoint interPumpEndpoint = new InterPumpEndpoint(world, this.location.getOpposite(), handler);
|
||||
interPumpEndpoint.pumps = pumps.copy();
|
||||
return interPumpEndpoint;
|
||||
}
|
||||
|
||||
public Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> getPumps() {
|
||||
return pumps;
|
||||
}
|
||||
|
||||
public boolean isPulling(boolean first) {
|
||||
Pair<BlockFace, WeakReference<PumpTileEntity>> pair = getPumps().get(first);
|
||||
PumpTileEntity pumpTileEntity = pair.getSecond()
|
||||
.get();
|
||||
if (pumpTileEntity == null || pumpTileEntity.isRemoved())
|
||||
return false;
|
||||
return pumpTileEntity.isPullingOnSide(pumpTileEntity.isFront(pair.getFirst()
|
||||
.getFace()));
|
||||
}
|
||||
|
||||
public int getTransferSpeed(boolean first) {
|
||||
PumpTileEntity pumpTileEntity = getPumps().get(first)
|
||||
.getSecond()
|
||||
.get();
|
||||
if (pumpTileEntity == null || pumpTileEntity.isRemoved())
|
||||
return 0;
|
||||
return pumpTileEntity.getFluidTransferSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyOptional<IFluidHandler> provideHandler() {
|
||||
if (isPulling(true) == isPulling(false))
|
||||
return LazyOptional.empty();
|
||||
if (getTransferSpeed(true) > getTransferSpeed(false))
|
||||
return LazyOptional.empty();
|
||||
return super.provideHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack provideFluid() {
|
||||
if (!provideHandler().isPresent())
|
||||
return FluidStack.EMPTY;
|
||||
|
||||
Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> pumps = getPumps();
|
||||
for (boolean current : Iterate.trueAndFalse) {
|
||||
if (isPulling(current))
|
||||
continue;
|
||||
|
||||
Pair<BlockFace, WeakReference<PumpTileEntity>> pair = pumps.get(current);
|
||||
BlockFace blockFace = pair.getFirst();
|
||||
PumpTileEntity pumpTileEntity = pair.getSecond()
|
||||
.get();
|
||||
if (pumpTileEntity == null)
|
||||
continue;
|
||||
if (pumpTileEntity.networks == null)
|
||||
continue;
|
||||
FluidNetwork fluidNetwork = pumpTileEntity.networks.get(pumpTileEntity.isFront(blockFace.getFace()));
|
||||
for (FluidNetworkFlow fluidNetworkFlow : fluidNetwork.flows) {
|
||||
for (FluidNetworkEndpoint fne : fluidNetworkFlow.outputEndpoints) {
|
||||
if (!(fne instanceof InterPumpEndpoint))
|
||||
continue;
|
||||
InterPumpEndpoint ipe = (InterPumpEndpoint) fne;
|
||||
if (!ipe.location.isEquivalent(location))
|
||||
continue;
|
||||
|
||||
FluidStack heldFluid = fluidNetworkFlow.fluidStack;
|
||||
if (heldFluid.isEmpty())
|
||||
return heldFluid;
|
||||
FluidStack copy = heldFluid.copy();
|
||||
copy.setAmount(1);
|
||||
return heldFluid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FluidStack.EMPTY;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
class FluidNetworkFlow {
|
||||
|
||||
@FunctionalInterface
|
||||
static interface PipeFlowConsumer {
|
||||
void accept(FluidPipeBehaviour pipe, Direction face, boolean inbound);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private final FluidNetwork activePipeNetwork;
|
||||
FluidNetworkEndpoint source;
|
||||
FluidStack fluidStack;
|
||||
Set<BlockFace> flowPointers;
|
||||
|
||||
Set<FluidNetworkEndpoint> outputEndpoints;
|
||||
boolean pumpReached;
|
||||
|
||||
boolean pulling;
|
||||
float speed;
|
||||
|
||||
public FluidNetworkFlow(FluidNetwork activePipeNetwork, FluidNetworkEndpoint source, IWorld world,
|
||||
boolean pulling) {
|
||||
this.activePipeNetwork = activePipeNetwork;
|
||||
this.source = source;
|
||||
this.pulling = pulling;
|
||||
flowPointers = new HashSet<>();
|
||||
outputEndpoints = new HashSet<>();
|
||||
fluidStack = FluidStack.EMPTY;
|
||||
tick(world, 0);
|
||||
}
|
||||
|
||||
void resetFlow(IWorld world) {
|
||||
fluidStack = FluidStack.EMPTY;
|
||||
flowPointers.clear();
|
||||
outputEndpoints.clear();
|
||||
pumpReached = false;
|
||||
forEachPipeFlow(world, (pipe, face, inbound) -> pipe.removeFlow(this, face, inbound));
|
||||
}
|
||||
|
||||
void addToSkippedConnections(IWorld world) {
|
||||
forEachPipeFlow(world, (pipe, face, inbound) -> {
|
||||
if (!pipe.fluid.isFluidEqual(fluidStack))
|
||||
return;
|
||||
BlockFace blockFace = new BlockFace(pipe.getPos(), face);
|
||||
this.activePipeNetwork.previousFlow.put(blockFace, pipe.fluid);
|
||||
});
|
||||
}
|
||||
|
||||
void forEachPipeFlow(IWorld world, FluidNetworkFlow.PipeFlowConsumer consumer) {
|
||||
Set<BlockFace> flowPointers = new HashSet<>();
|
||||
flowPointers.add(getSource());
|
||||
|
||||
// Update all branches of this flow, and create new ones if necessary
|
||||
while (!flowPointers.isEmpty()) {
|
||||
List<BlockFace> toAdd = new ArrayList<>();
|
||||
for (Iterator<BlockFace> iterator = flowPointers.iterator(); iterator.hasNext();) {
|
||||
BlockFace flowPointer = iterator.next();
|
||||
BlockPos currentPos = flowPointer.getPos();
|
||||
FluidPipeBehaviour pipe = getPipeInTree(world, currentPos);
|
||||
if (pipe == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
Map<Direction, Boolean> directions = this.activePipeNetwork.pipeGraph.get(currentPos)
|
||||
.getSecond();
|
||||
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
|
||||
boolean inbound = entry.getValue() != pulling;
|
||||
Direction face = entry.getKey();
|
||||
if (inbound && face != flowPointer.getFace())
|
||||
continue;
|
||||
consumer.accept(pipe, face, inbound);
|
||||
if (inbound)
|
||||
continue;
|
||||
toAdd.add(new BlockFace(currentPos.offset(face), face.getOpposite()));
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
flowPointers.addAll(toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
void tick(IWorld world, float speed) {
|
||||
boolean skipping = speed == 0;
|
||||
Map<BlockFace, FluidStack> previousFlow = this.activePipeNetwork.previousFlow;
|
||||
if (skipping && previousFlow.isEmpty())
|
||||
return;
|
||||
|
||||
this.speed = speed;
|
||||
FluidStack provideFluid = source.provideFluid();
|
||||
if (!fluidStack.isEmpty() && !fluidStack.isFluidEqual(provideFluid)) {
|
||||
resetFlow(world);
|
||||
return;
|
||||
}
|
||||
|
||||
fluidStack = provideFluid;
|
||||
|
||||
// There is currently no unfinished flow being followed
|
||||
if (flowPointers.isEmpty()) {
|
||||
|
||||
// The fluid source has run out -> reset
|
||||
if (fluidStack.isEmpty()) {
|
||||
if (hasValidTargets())
|
||||
resetFlow(world);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the flows if all is well
|
||||
if (hasValidTargets())
|
||||
return;
|
||||
|
||||
// Start a new flow from or towards the pump
|
||||
BlockFace source = getSource();
|
||||
if (tryConnectTo(world, source.getOpposite()))
|
||||
return;
|
||||
flowPointers.add(source);
|
||||
}
|
||||
|
||||
boolean skipped = false;
|
||||
Set<BlockFace> pausedPointers = new HashSet<>();
|
||||
|
||||
do {
|
||||
skipped = false;
|
||||
List<BlockFace> toAdd = null;
|
||||
|
||||
// Update all branches of this flow, and create new ones if necessary
|
||||
for (Iterator<BlockFace> iterator = flowPointers.iterator(); iterator.hasNext();) {
|
||||
BlockFace flowPointer = iterator.next();
|
||||
BlockPos currentPos = flowPointer.getPos();
|
||||
|
||||
if (pausedPointers.contains(flowPointer))
|
||||
continue;
|
||||
|
||||
FluidPipeBehaviour pipe = getPipeInTree(world, currentPos);
|
||||
if (pipe == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<Direction, Boolean> directions = this.activePipeNetwork.pipeGraph.get(currentPos)
|
||||
.getSecond();
|
||||
boolean inboundComplete = false;
|
||||
boolean allFlowsComplete = true;
|
||||
BlockState state = world.getBlockState(currentPos);
|
||||
|
||||
// First loop only inbound flows of a pipe to see if they have reached the
|
||||
// center
|
||||
for (boolean inboundPass : Iterate.trueAndFalse) {
|
||||
if (!inboundPass && !inboundComplete)
|
||||
break;
|
||||
|
||||
// For all connections of the pipe tree of the pump
|
||||
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
|
||||
Boolean awayFromPump = entry.getValue();
|
||||
Direction direction = entry.getKey();
|
||||
boolean inbound = awayFromPump != pulling;
|
||||
|
||||
if (inboundPass && direction != flowPointer.getFace())
|
||||
continue;
|
||||
if (!inboundPass && inbound)
|
||||
continue;
|
||||
if (!pipe.canTransferToward(fluidStack, state, direction, inbound))
|
||||
continue;
|
||||
|
||||
BlockFace blockface = new BlockFace(currentPos, direction);
|
||||
|
||||
if (!pipe.hasStartedFlow(this, direction, inbound))
|
||||
pipe.addFlow(this, direction, inbound);
|
||||
if (skipping && canSkip(previousFlow, blockface)) {
|
||||
pipe.skipFlow(direction, inbound);
|
||||
FluidPropagator.showBlockFace(blockface)
|
||||
.colored(0x0)
|
||||
.lineWidth(1 / 8f);
|
||||
skipped = true;
|
||||
}
|
||||
|
||||
if (!pipe.hasCompletedFlow(direction, inbound)) {
|
||||
allFlowsComplete = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inboundPass) {
|
||||
inboundComplete = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Outward pass, check if any target was reached
|
||||
tryConnectTo(world, blockface);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allFlowsComplete && !skipping)
|
||||
continue;
|
||||
|
||||
// Create a new flow branch at each outward pipe connection
|
||||
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
|
||||
if (entry.getValue() != pulling)
|
||||
continue;
|
||||
Direction face = entry.getKey();
|
||||
BlockFace addedBlockFace = new BlockFace(currentPos.offset(face), face.getOpposite());
|
||||
if (skipping && !canSkip(previousFlow, addedBlockFace)) {
|
||||
allFlowsComplete = false;
|
||||
continue;
|
||||
}
|
||||
if (toAdd == null)
|
||||
toAdd = new ArrayList<>();
|
||||
toAdd.add(addedBlockFace);
|
||||
}
|
||||
|
||||
if (!allFlowsComplete && skipping) {
|
||||
pausedPointers.add(flowPointer);
|
||||
continue;
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
|
||||
} // End of branch loop
|
||||
|
||||
if (toAdd != null)
|
||||
flowPointers.addAll(toAdd);
|
||||
|
||||
} while (skipping && skipped);
|
||||
}
|
||||
|
||||
private boolean canSkip(Map<BlockFace, FluidStack> previousFlow, BlockFace blockface) {
|
||||
return previousFlow.containsKey(blockface) && previousFlow.get(blockface)
|
||||
.isFluidEqual(fluidStack);
|
||||
}
|
||||
|
||||
private boolean tryConnectTo(IWorld world, BlockFace blockface) {
|
||||
// Pulling flow, target is the pump
|
||||
if (pulling) {
|
||||
if (!this.activePipeNetwork.pumpLocation.getOpposite()
|
||||
.equals(blockface))
|
||||
return false;
|
||||
pumpReached = true;
|
||||
TileEntity targetTE = world.getTileEntity(this.activePipeNetwork.pumpLocation.getPos());
|
||||
if (targetTE instanceof PumpTileEntity)
|
||||
((PumpTileEntity) targetTE).setProvidedFluid(fluidStack);
|
||||
FluidPropagator.showBlockFace(this.activePipeNetwork.pumpLocation)
|
||||
.colored(0x799351)
|
||||
.lineWidth(1 / 8f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pushing flow, targets are the endpoints
|
||||
for (FluidNetworkEndpoint networkEndpoint : this.activePipeNetwork.targets) {
|
||||
if (!networkEndpoint.location.isEquivalent(blockface))
|
||||
continue;
|
||||
outputEndpoints.add(networkEndpoint);
|
||||
FluidPropagator.showBlockFace(blockface)
|
||||
.colored(0x799351)
|
||||
.lineWidth(1 / 8f);
|
||||
return !(networkEndpoint instanceof InterPumpEndpoint);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private BlockFace getSource() {
|
||||
return pulling ? source.location : this.activePipeNetwork.pumpLocation.getOpposite();
|
||||
}
|
||||
|
||||
private FluidPipeBehaviour getPipeInTree(IWorld world, BlockPos currentPos) {
|
||||
if (!world.isAreaLoaded(currentPos, 0))
|
||||
return null;
|
||||
if (!this.activePipeNetwork.pipeGraph.containsKey(currentPos))
|
||||
return null;
|
||||
return TileEntityBehaviour.get(world, currentPos, FluidPipeBehaviour.TYPE);
|
||||
}
|
||||
|
||||
boolean hasValidTargets() {
|
||||
return pumpReached || !outputEndpoints.isEmpty();
|
||||
}
|
||||
|
||||
public float getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public FluidStack getFluidStack() {
|
||||
return fluidStack;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,478 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.AllSpecialTextures;
|
||||
import com.simibubi.create.CreateClient;
|
||||
import com.simibubi.create.content.contraptions.KineticDebugger;
|
||||
import com.simibubi.create.foundation.fluid.FluidHelper;
|
||||
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
|
||||
import com.simibubi.create.foundation.utility.Couple;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.LerpedFloat;
|
||||
import com.simibubi.create.foundation.utility.LerpedFloat.Chaser;
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.particles.BlockParticleData;
|
||||
import net.minecraft.particles.IParticleData;
|
||||
import net.minecraft.particles.ParticleTypes;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
|
||||
public abstract class FluidPipeBehaviour extends TileEntityBehaviour {
|
||||
|
||||
public static BehaviourType<FluidPipeBehaviour> TYPE = new BehaviourType<>();
|
||||
|
||||
// Direction -> (inboundflows{}, outwardflows{})
|
||||
Map<Direction, Couple<PipeFlows>> allFlows;
|
||||
FluidStack fluid;
|
||||
Couple<FluidStack> collision;
|
||||
|
||||
public FluidPipeBehaviour(SmartTileEntity te) {
|
||||
super(te);
|
||||
allFlows = new IdentityHashMap<>();
|
||||
fluid = FluidStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BehaviourType<?> getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public void notifyNetwork() {
|
||||
FluidPropagator.propagateChangedPipe(this.getWorld(), tileEntity.getPos(), tileEntity.getBlockState());
|
||||
}
|
||||
|
||||
public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) {
|
||||
return isConnectedTo(state, direction);
|
||||
}
|
||||
|
||||
public abstract boolean isConnectedTo(BlockState state, Direction direction);
|
||||
|
||||
public float getRimRadius(BlockState state, Direction direction) {
|
||||
return 1 / 4f + 1 / 64f;
|
||||
}
|
||||
|
||||
public boolean hasStartedFlow(FluidNetworkFlow flow, Direction face, boolean inbound) {
|
||||
return allFlows.containsKey(face) && allFlows.get(face)
|
||||
.get(inbound)
|
||||
.hasFlow(flow);
|
||||
}
|
||||
|
||||
public boolean hasCompletedFlow(Direction face, boolean inbound) {
|
||||
return allFlows.containsKey(face) && allFlows.get(face)
|
||||
.get(inbound)
|
||||
.isCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(CompoundNBT compound, boolean client) {
|
||||
compound.put("Fluid", fluid.writeToNBT(new CompoundNBT()));
|
||||
ListNBT flows = new ListNBT();
|
||||
for (Direction face : Iterate.directions)
|
||||
for (boolean inbound : Iterate.trueAndFalse) {
|
||||
LerpedFloat flowProgress = getFlowProgress(face, inbound);
|
||||
if (flowProgress == null)
|
||||
continue;
|
||||
CompoundNBT nbt = new CompoundNBT();
|
||||
NBTHelper.writeEnum(nbt, "Face", face);
|
||||
nbt.putBoolean("In", inbound);
|
||||
nbt.put("Progress", flowProgress.writeNBT());
|
||||
flows.add(nbt);
|
||||
}
|
||||
compound.put("Flows", flows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound, boolean client) {
|
||||
fluid = FluidStack.loadFluidStackFromNBT(compound.getCompound("Fluid"));
|
||||
|
||||
if (client) {
|
||||
for (Direction face : Iterate.directions)
|
||||
if (allFlows.containsKey(face))
|
||||
allFlows.get(face)
|
||||
.forEach(pf -> pf.progress = null);
|
||||
}
|
||||
|
||||
NBTHelper.iterateCompoundList(compound.getList("Flows", NBT.TAG_COMPOUND), nbt -> {
|
||||
Direction face = NBTHelper.readEnum(nbt, "Face", Direction.class);
|
||||
boolean inbound = nbt.getBoolean("In");
|
||||
LerpedFloat progress = createFlowProgress(0);
|
||||
progress.readNBT(nbt.getCompound("Progress"), false);
|
||||
addFlow(null, face, inbound);
|
||||
setFlowProgress(face, inbound, progress);
|
||||
});
|
||||
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
for (Direction face : Iterate.directions) {
|
||||
if (!allFlows.containsKey(face))
|
||||
return;
|
||||
Couple<PipeFlows> couple = allFlows.get(face);
|
||||
if (couple.get(true).progress == null && couple.get(false).progress == null)
|
||||
allFlows.remove(face);
|
||||
if (allFlows.isEmpty())
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void addFlow(@Nullable FluidNetworkFlow flow, Direction face, boolean inbound) {
|
||||
if (flow != null) {
|
||||
FluidStack fluid = flow.getFluidStack();
|
||||
if (!this.fluid.isEmpty() && !fluid.isFluidEqual(this.fluid)) {
|
||||
collision = Couple.create(this.fluid, fluid);
|
||||
return;
|
||||
}
|
||||
this.fluid = fluid;
|
||||
}
|
||||
|
||||
if (!allFlows.containsKey(face)) {
|
||||
allFlows.put(face, Couple.create(PipeFlows::new));
|
||||
if (inbound)
|
||||
spawnSplashOnRim(face);
|
||||
}
|
||||
|
||||
if (flow != null) {
|
||||
PipeFlows flows = allFlows.get(face)
|
||||
.get(inbound);
|
||||
flows.addFlow(flow);
|
||||
contentsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFlow(FluidNetworkFlow flow, Direction face, boolean inbound) {
|
||||
if (!allFlows.containsKey(face))
|
||||
return;
|
||||
Couple<PipeFlows> couple = allFlows.get(face);
|
||||
couple.get(inbound)
|
||||
.removeFlow(flow);
|
||||
if (!couple.get(true)
|
||||
.isActive()
|
||||
&& !couple.get(false)
|
||||
.isActive())
|
||||
allFlows.remove(face);
|
||||
if (allFlows.isEmpty())
|
||||
clear();
|
||||
}
|
||||
|
||||
public void setFlowProgress(Direction face, boolean inbound, LerpedFloat progress) {
|
||||
if (!allFlows.containsKey(face))
|
||||
return;
|
||||
allFlows.get(face)
|
||||
.get(inbound).progress = progress;
|
||||
}
|
||||
|
||||
public LerpedFloat getFlowProgress(Direction face, boolean inbound) {
|
||||
if (!allFlows.containsKey(face))
|
||||
return null;
|
||||
return allFlows.get(face)
|
||||
.get(inbound).progress;
|
||||
}
|
||||
|
||||
public void skipFlow(Direction face, boolean inbound) {
|
||||
if (!allFlows.containsKey(face))
|
||||
return;
|
||||
Couple<PipeFlows> couple = allFlows.get(face);
|
||||
couple.get(inbound)
|
||||
.skip();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
allFlows.clear();
|
||||
fluid = FluidStack.EMPTY;
|
||||
contentsChanged();
|
||||
}
|
||||
|
||||
public void spawnParticles() {
|
||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> this::spawnParticlesInner);
|
||||
}
|
||||
|
||||
public void spawnSplashOnRim(Direction face) {
|
||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnSplashOnRimInner(face));
|
||||
}
|
||||
|
||||
public static final int MAX_PARTICLE_RENDER_DISTANCE = 20;
|
||||
public static final int SPLASH_PARTICLE_AMOUNT = 10;
|
||||
public static final float IDLE_PARTICLE_SPAWN_CHANCE = 1 / 100f;
|
||||
public static final Random r = new Random();
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void spawnParticlesInner() {
|
||||
if (!isRenderEntityWithinDistance())
|
||||
return;
|
||||
if (fluid.isEmpty())
|
||||
return;
|
||||
|
||||
World world = Minecraft.getInstance().world;
|
||||
BlockPos pos = tileEntity.getPos();
|
||||
BlockState state = world.getBlockState(pos);
|
||||
|
||||
for (Direction face : Iterate.directions) {
|
||||
boolean open = FluidPropagator.isOpenEnd(world, pos, face);
|
||||
if (isConnectedTo(state, face)) {
|
||||
if (open) {
|
||||
spawnPouringLiquid(world, state, fluid, face, 1);
|
||||
continue;
|
||||
}
|
||||
if (r.nextFloat() < IDLE_PARTICLE_SPAWN_CHANCE)
|
||||
spawnRimParticles(world, state, fluid, face, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void spawnSplashOnRimInner(Direction face) {
|
||||
if (!isRenderEntityWithinDistance())
|
||||
return;
|
||||
if (fluid.isEmpty())
|
||||
return;
|
||||
World world = Minecraft.getInstance().world;
|
||||
BlockPos pos = tileEntity.getPos();
|
||||
BlockState state = world.getBlockState(pos);
|
||||
spawnRimParticles(world, state, fluid, face, 20);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void spawnRimParticles(World world, BlockState state, FluidStack fluid, Direction side, int amount) {
|
||||
BlockPos pos = tileEntity.getPos();
|
||||
if (FluidPropagator.isOpenEnd(world, pos, side)) {
|
||||
spawnPouringLiquid(world, state, fluid, side, amount);
|
||||
return;
|
||||
}
|
||||
|
||||
IParticleData particle = null;
|
||||
if (FluidHelper.isWater(fluid.getFluid()))
|
||||
particle = ParticleTypes.DRIPPING_WATER;
|
||||
if (FluidHelper.isLava(fluid.getFluid()))
|
||||
particle = ParticleTypes.DRIPPING_LAVA;
|
||||
// TODO: Generic drip particle type for forge fluids
|
||||
|
||||
if (particle == null)
|
||||
return;
|
||||
|
||||
float rimRadius = getRimRadius(state, side);
|
||||
Vec3d directionVec = new Vec3d(side.getDirectionVec());
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Vec3d vec = VecHelper.offsetRandomly(Vec3d.ZERO, r, 1)
|
||||
.normalize();
|
||||
vec = VecHelper.clampComponentWise(vec, rimRadius)
|
||||
.mul(VecHelper.axisAlingedPlaneOf(directionVec))
|
||||
.add(directionVec.scale(.45 + r.nextFloat() / 16f));
|
||||
Vec3d m = vec;
|
||||
vec = vec.add(VecHelper.getCenterOf(pos));
|
||||
|
||||
world.addOptionalParticle(particle, vec.x, vec.y - 1 / 16f, vec.z, m.x, m.y, m.z);
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void spawnPouringLiquid(World world, BlockState state, FluidStack fluid, Direction side, int amount) {
|
||||
IParticleData particle = new BlockParticleData(ParticleTypes.BLOCK, fluid.getFluid()
|
||||
.getDefaultState()
|
||||
.getBlockState());
|
||||
float rimRadius = getRimRadius(state, side);
|
||||
Vec3d directionVec = new Vec3d(side.getDirectionVec());
|
||||
|
||||
Couple<PipeFlows> couple = allFlows.get(side);
|
||||
if (couple == null)
|
||||
return;
|
||||
couple.forEachWithContext((flow, inbound) -> {
|
||||
if (flow.progress == null)
|
||||
return;
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Vec3d vec = VecHelper.offsetRandomly(Vec3d.ZERO, r, rimRadius);
|
||||
vec = vec.mul(VecHelper.axisAlingedPlaneOf(directionVec))
|
||||
.add(directionVec.scale(.5 + r.nextFloat() / 4f));
|
||||
Vec3d m = vec;
|
||||
Vec3d centerOf = VecHelper.getCenterOf(tileEntity.getPos());
|
||||
vec = vec.add(centerOf);
|
||||
if (inbound) {
|
||||
vec = vec.add(m);
|
||||
m = centerOf.add(directionVec.scale(.5)).subtract(vec).scale(3);
|
||||
}
|
||||
world.addOptionalParticle(particle, vec.x, vec.y - 1 / 16f, vec.z, m.x, m.y, m.z);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private boolean isRenderEntityWithinDistance() {
|
||||
Entity renderViewEntity = Minecraft.getInstance()
|
||||
.getRenderViewEntity();
|
||||
if (renderViewEntity == null)
|
||||
return false;
|
||||
Vec3d center = VecHelper.getCenterOf(tileEntity.getPos());
|
||||
if (renderViewEntity.getPositionVec()
|
||||
.distanceTo(center) > MAX_PARTICLE_RENDER_DISTANCE)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25);
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
boolean isRemote = getWorld().isRemote;
|
||||
|
||||
allFlows.values()
|
||||
.forEach(c -> c.forEach(pf -> pf.tick(isRemote)));
|
||||
|
||||
if (isRemote) {
|
||||
clientTick();
|
||||
return;
|
||||
}
|
||||
|
||||
if (collision != null) {
|
||||
FluidReactions.handlePipeFlowCollision(getWorld(), tileEntity.getPos(), collision.getFirst(),
|
||||
collision.getSecond());
|
||||
collision = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void clientTick() {
|
||||
spawnParticles();
|
||||
|
||||
if (!KineticDebugger.isActive())
|
||||
return;
|
||||
if (fluid.isEmpty())
|
||||
return;
|
||||
for (Entry<Direction, Couple<PipeFlows>> entry : allFlows.entrySet()) {
|
||||
Direction face = entry.getKey();
|
||||
Vec3d directionVec = new Vec3d(face.getDirectionVec());
|
||||
float size = 1 / 4f;
|
||||
boolean extended = !isConnectedTo(tileEntity.getBlockState(), face.getOpposite());
|
||||
float length = extended ? .75f : .5f;
|
||||
|
||||
entry.getValue()
|
||||
.forEachWithContext((flow, inbound) -> {
|
||||
if (flow.progress == null)
|
||||
return;
|
||||
float value = flow.progress.getValue();
|
||||
Vec3d start = directionVec.scale(inbound ? .5 : .5f - length);
|
||||
Vec3d offset = directionVec.scale(length * (inbound ? -1 : 1))
|
||||
.scale(value);
|
||||
|
||||
Vec3d scale = new Vec3d(1, 1, 1).subtract(directionVec.scale(face.getAxisDirection()
|
||||
.getOffset()))
|
||||
.scale(size);
|
||||
AxisAlignedBB bb =
|
||||
new AxisAlignedBB(start, start.add(offset)).offset(VecHelper.getCenterOf(tileEntity.getPos()))
|
||||
.grow(scale.x, scale.y, scale.z);
|
||||
|
||||
int color = 0x7fdbda;
|
||||
if (!fluid.isEmpty()) {
|
||||
Fluid fluid2 = fluid.getFluid();
|
||||
if (fluid2 == Fluids.WATER)
|
||||
color = 0x1D4D9B;
|
||||
if (fluid2 == Fluids.LAVA)
|
||||
color = 0xFF773D;
|
||||
}
|
||||
|
||||
CreateClient.outliner.chaseAABB(Pair.of(this, face), bb)
|
||||
.withFaceTexture(AllSpecialTextures.CUTOUT_CHECKERED)
|
||||
.colored(color)
|
||||
.lineWidth(1 / 16f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void contentsChanged() {
|
||||
tileEntity.markDirty();
|
||||
tileEntity.sendData();
|
||||
}
|
||||
|
||||
private LerpedFloat createFlowProgress(double speed) {
|
||||
return LerpedFloat.linear()
|
||||
.startWithValue(0)
|
||||
.chase(1, speed, Chaser.LINEAR);
|
||||
}
|
||||
|
||||
class PipeFlows {
|
||||
LerpedFloat progress;
|
||||
Set<FluidNetworkFlow> participants;
|
||||
float bestFlowStrength;
|
||||
|
||||
void addFlow(FluidNetworkFlow flow) {
|
||||
if (participants == null)
|
||||
participants = new HashSet<>();
|
||||
participants.add(flow);
|
||||
|
||||
if (progress == null) {
|
||||
progress = createFlowProgress(flow.getSpeed());
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasFlow(FluidNetworkFlow flow) {
|
||||
return participants != null && participants.contains(flow);
|
||||
}
|
||||
|
||||
void tick(boolean onClient) {
|
||||
if (progress == null)
|
||||
return;
|
||||
if (!onClient) {
|
||||
if (participants == null)
|
||||
return;
|
||||
bestFlowStrength = 0;
|
||||
for (FluidNetworkFlow networkFlow : participants)
|
||||
bestFlowStrength = Math.max(bestFlowStrength, networkFlow.getSpeed());
|
||||
if (isCompleted())
|
||||
return;
|
||||
if (progress.updateChaseSpeed(bestFlowStrength))
|
||||
contentsChanged();
|
||||
}
|
||||
progress.tickChaser();
|
||||
}
|
||||
|
||||
void skip() {
|
||||
progress = LerpedFloat.linear()
|
||||
.startWithValue(1);
|
||||
}
|
||||
|
||||
void removeFlow(FluidNetworkFlow flow) {
|
||||
if (participants == null)
|
||||
return;
|
||||
participants.remove(flow);
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
return participants != null && !participants.isEmpty();
|
||||
}
|
||||
|
||||
boolean isCompleted() {
|
||||
return progress != null && progress.getValue() == 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +1,24 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.AllTileEntities;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FlowingFluidBlock;
|
||||
import net.minecraft.block.IWaterLoggable;
|
||||
import net.minecraft.block.SixWayBlock;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.network.DebugPacketSender;
|
||||
import net.minecraft.state.StateContainer.Builder;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraft.util.Direction.AxisDirection;
|
||||
|
@ -17,141 +26,210 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.TickPriority;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable {
|
||||
|
||||
public FluidPipeBlock(Properties properties) {
|
||||
super(4 / 16f, properties);
|
||||
this.setDefaultState(super.getDefaultState().with(BlockStateProperties.WATERLOGGED, false));
|
||||
}
|
||||
public FluidPipeBlock(Properties properties) {
|
||||
super(4 / 16f, properties);
|
||||
this.setDefaultState(super.getDefaultState().with(BlockStateProperties.WATERLOGGED, false));
|
||||
}
|
||||
|
||||
public static boolean isPipe(BlockState state) {
|
||||
return state.getBlock() instanceof FluidPipeBlock;
|
||||
}
|
||||
@Override
|
||||
public boolean hasTileEntity(BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isTank(BlockState state, IBlockReader world, BlockPos pos, Direction blockFace) {
|
||||
return state.hasTileEntity() && world.getTileEntity(pos).getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, blockFace.getOpposite()).isPresent();
|
||||
}
|
||||
@Override
|
||||
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
|
||||
return AllTileEntities.FLUID_PIPE.create();
|
||||
}
|
||||
|
||||
// TODO: more generic pipe connection handling. Ideally without marker interface
|
||||
public static boolean canConnectTo(ILightReader world, BlockPos pos, BlockState neighbour, Direction blockFace) {
|
||||
if (isPipe(neighbour) || isTank(neighbour, world, pos, blockFace))
|
||||
return true;
|
||||
return neighbour.getBlock() instanceof PumpBlock && blockFace.getAxis() == neighbour.get(PumpBlock.FACING)
|
||||
.getAxis();
|
||||
}
|
||||
@Override
|
||||
public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
boolean blockTypeChanged = state.getBlock() != newState.getBlock();
|
||||
if (blockTypeChanged && !world.isRemote)
|
||||
FluidPropagator.propagateChangedPipe(world, pos, state);
|
||||
if (state.hasTileEntity() && (blockTypeChanged || !newState.hasTileEntity()))
|
||||
world.removeTileEntity(pos);
|
||||
}
|
||||
|
||||
public static boolean shouldDrawRim(ILightReader world, BlockPos pos, BlockState state, Direction direction) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
if (!state.get(FACING_TO_PROPERTY_MAP.get(direction)))
|
||||
return false;
|
||||
BlockPos offsetPos = pos.offset(direction);
|
||||
BlockState facingState = world.getBlockState(offsetPos);
|
||||
if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING)
|
||||
.getAxis() == direction.getAxis())
|
||||
return false;
|
||||
if (!isPipe(facingState))
|
||||
return true;
|
||||
if (!isCornerOrEndPipe(world, pos, state))
|
||||
return false;
|
||||
if (isStraightPipe(world, offsetPos, facingState))
|
||||
return true;
|
||||
if (!shouldDrawCasing(world, pos, state) && shouldDrawCasing(world, offsetPos, facingState))
|
||||
return true;
|
||||
if (isCornerOrEndPipe(world, offsetPos, facingState))
|
||||
return direction.getAxisDirection() == AxisDirection.POSITIVE;
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean isMoving) {
|
||||
if (world.isRemote)
|
||||
return;
|
||||
if (state != oldState)
|
||||
world.getPendingBlockTicks()
|
||||
.scheduleTick(pos, this, 1, TickPriority.HIGH);
|
||||
}
|
||||
|
||||
public static boolean isCornerOrEndPipe(ILightReader world, BlockPos pos, BlockState state) {
|
||||
return isPipe(state) && !isStraightPipe(world, pos, state) && !shouldDrawCasing(world, pos, state);
|
||||
}
|
||||
@Override
|
||||
public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos,
|
||||
boolean isMoving) {
|
||||
DebugPacketSender.func_218806_a(world, pos);
|
||||
if (world.isRemote)
|
||||
return;
|
||||
if (otherBlock instanceof FluidPipeBlock)
|
||||
return;
|
||||
if (otherBlock instanceof PumpBlock)
|
||||
return;
|
||||
if (otherBlock instanceof FlowingFluidBlock)
|
||||
return;
|
||||
if (!isStraightPipe(state))
|
||||
return;
|
||||
for (Direction d : Iterate.directions) {
|
||||
if (!pos.offset(d)
|
||||
.equals(neighborPos))
|
||||
continue;
|
||||
if (!isOpenAt(state, d))
|
||||
return;
|
||||
world.getPendingBlockTicks()
|
||||
.scheduleTick(pos, this, 1, TickPriority.HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isStraightPipe(ILightReader world, BlockPos pos, BlockState state) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
boolean axisFound = false;
|
||||
for (Axis axis : Iterate.axes) {
|
||||
Direction d1 = Direction.getFacingFromAxis(AxisDirection.NEGATIVE, axis);
|
||||
Direction d2 = Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis);
|
||||
if (state.get(FACING_TO_PROPERTY_MAP.get(d1)) && state.get(FACING_TO_PROPERTY_MAP.get(d2)))
|
||||
if (axisFound)
|
||||
return false;
|
||||
else
|
||||
axisFound = true;
|
||||
}
|
||||
return axisFound;
|
||||
}
|
||||
@Override
|
||||
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) {
|
||||
FluidPropagator.propagateChangedPipe(world, pos, state);
|
||||
}
|
||||
|
||||
public static boolean shouldDrawCasing(ILightReader world, BlockPos pos, BlockState state) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
for (Axis axis : Iterate.axes) {
|
||||
int connections = 0;
|
||||
for (Direction direction : Iterate.directions)
|
||||
if (direction.getAxis() != axis && state.get(FACING_TO_PROPERTY_MAP.get(direction)))
|
||||
connections++;
|
||||
if (connections > 2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static boolean isPipe(BlockState state) {
|
||||
return state.getBlock() instanceof FluidPipeBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillStateContainer(Builder<Block, BlockState> builder) {
|
||||
builder.add(NORTH, EAST, SOUTH, WEST, UP, DOWN, BlockStateProperties.WATERLOGGED);
|
||||
super.fillStateContainer(builder);
|
||||
}
|
||||
public static boolean hasFluidCapability(BlockState state, IBlockReader world, BlockPos pos, Direction blockFace) {
|
||||
return state.hasTileEntity() && world.getTileEntity(pos)
|
||||
.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, blockFace.getOpposite())
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockItemUseContext context) {
|
||||
IFluidState ifluidstate = context.getWorld().getFluidState(context.getPos());
|
||||
return updateBlockState(getDefaultState(), context.getNearestLookingDirection(), null, context.getWorld(),
|
||||
context.getPos()).with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
}
|
||||
public static boolean canConnectTo(ILightReader world, BlockPos pos, BlockState neighbour, Direction blockFace) {
|
||||
if (isPipe(neighbour) || hasFluidCapability(neighbour, world, pos, blockFace))
|
||||
return true;
|
||||
// TODO: more generic pipe connection handling.
|
||||
return neighbour.getBlock() instanceof PumpBlock && blockFace.getAxis() == neighbour.get(PumpBlock.FACING)
|
||||
.getAxis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState updatePostPlacement(BlockState state, Direction direction, BlockState neighbourState,
|
||||
IWorld world, BlockPos pos, BlockPos neighbourPos) {
|
||||
if (state.get(BlockStateProperties.WATERLOGGED)) {
|
||||
world.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
}
|
||||
return updateBlockState(state, direction, direction.getOpposite(), world, pos);
|
||||
}
|
||||
public static boolean shouldDrawRim(ILightReader world, BlockPos pos, BlockState state, Direction direction) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
if (!isOpenAt(state, direction))
|
||||
return false;
|
||||
BlockPos offsetPos = pos.offset(direction);
|
||||
BlockState facingState = world.getBlockState(offsetPos);
|
||||
if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING)
|
||||
.getAxis() == direction.getAxis())
|
||||
return false;
|
||||
if (!isPipe(facingState))
|
||||
return true;
|
||||
if (!isCornerOrEndPipe(world, pos, state))
|
||||
return false;
|
||||
if (isStraightPipe(facingState))
|
||||
return true;
|
||||
if (!shouldDrawCasing(world, pos, state) && shouldDrawCasing(world, offsetPos, facingState))
|
||||
return true;
|
||||
if (isCornerOrEndPipe(world, offsetPos, facingState))
|
||||
return direction.getAxisDirection() == AxisDirection.POSITIVE;
|
||||
return false;
|
||||
}
|
||||
|
||||
public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore,
|
||||
ILightReader world, BlockPos pos) {
|
||||
// Update sides that are not ignored
|
||||
for (Direction d : Iterate.directions)
|
||||
if (d != ignore)
|
||||
state = state.with(FACING_TO_PROPERTY_MAP.get(d),
|
||||
canConnectTo(world, pos.offset(d), world.getBlockState(pos.offset(d)), d.getOpposite()));
|
||||
private static boolean isOpenAt(BlockState state, Direction direction) {
|
||||
return state.get(FACING_TO_PROPERTY_MAP.get(direction));
|
||||
}
|
||||
|
||||
// See if it has enough connections
|
||||
Direction connectedDirection = null;
|
||||
for (Direction d : Iterate.directions) {
|
||||
if (state.get(FACING_TO_PROPERTY_MAP.get(d))) {
|
||||
if (connectedDirection != null)
|
||||
return state;
|
||||
connectedDirection = d;
|
||||
}
|
||||
}
|
||||
public static boolean isCornerOrEndPipe(ILightReader world, BlockPos pos, BlockState state) {
|
||||
return isPipe(state) && !isStraightPipe(state) && !shouldDrawCasing(world, pos, state);
|
||||
}
|
||||
|
||||
// Add opposite end if only one connection
|
||||
if (connectedDirection != null)
|
||||
return state.with(FACING_TO_PROPERTY_MAP.get(connectedDirection.getOpposite()), true);
|
||||
public static boolean isStraightPipe(BlockState state) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
boolean axisFound = false;
|
||||
for (Axis axis : Iterate.axes) {
|
||||
Direction d1 = Direction.getFacingFromAxis(AxisDirection.NEGATIVE, axis);
|
||||
Direction d2 = Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis);
|
||||
if (isOpenAt(state, d1) && isOpenAt(state, d2))
|
||||
if (axisFound)
|
||||
return false;
|
||||
else
|
||||
axisFound = true;
|
||||
}
|
||||
return axisFound;
|
||||
}
|
||||
|
||||
// Use preferred
|
||||
return state.with(FACING_TO_PROPERTY_MAP.get(preferredDirection), true)
|
||||
.with(FACING_TO_PROPERTY_MAP.get(preferredDirection.getOpposite()), true);
|
||||
}
|
||||
public static boolean shouldDrawCasing(ILightReader world, BlockPos pos, BlockState state) {
|
||||
if (!isPipe(state))
|
||||
return false;
|
||||
for (Axis axis : Iterate.axes) {
|
||||
int connections = 0;
|
||||
for (Direction direction : Iterate.directions)
|
||||
if (direction.getAxis() != axis && isOpenAt(state, direction))
|
||||
connections++;
|
||||
if (connections > 2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidState getFluidState(BlockState state) {
|
||||
return state.get(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getStillFluidState(false) : Fluids.EMPTY.getDefaultState();
|
||||
}
|
||||
@Override
|
||||
protected void fillStateContainer(Builder<Block, BlockState> builder) {
|
||||
builder.add(NORTH, EAST, SOUTH, WEST, UP, DOWN, BlockStateProperties.WATERLOGGED);
|
||||
super.fillStateContainer(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockItemUseContext context) {
|
||||
IFluidState ifluidstate = context.getWorld()
|
||||
.getFluidState(context.getPos());
|
||||
return updateBlockState(getDefaultState(), context.getNearestLookingDirection(), null, context.getWorld(),
|
||||
context.getPos()).with(BlockStateProperties.WATERLOGGED,
|
||||
Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState updatePostPlacement(BlockState state, Direction direction, BlockState neighbourState,
|
||||
IWorld world, BlockPos pos, BlockPos neighbourPos) {
|
||||
if (state.get(BlockStateProperties.WATERLOGGED)) {
|
||||
world.getPendingFluidTicks()
|
||||
.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
}
|
||||
return updateBlockState(state, direction, direction.getOpposite(), world, pos);
|
||||
}
|
||||
|
||||
public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore,
|
||||
ILightReader world, BlockPos pos) {
|
||||
// Update sides that are not ignored
|
||||
for (Direction d : Iterate.directions)
|
||||
if (d != ignore)
|
||||
state = state.with(FACING_TO_PROPERTY_MAP.get(d),
|
||||
canConnectTo(world, pos.offset(d), world.getBlockState(pos.offset(d)), d.getOpposite()));
|
||||
|
||||
// See if it has enough connections
|
||||
Direction connectedDirection = null;
|
||||
for (Direction d : Iterate.directions) {
|
||||
if (isOpenAt(state, d)) {
|
||||
if (connectedDirection != null)
|
||||
return state;
|
||||
connectedDirection = d;
|
||||
}
|
||||
}
|
||||
|
||||
// Add opposite end if only one connection
|
||||
if (connectedDirection != null)
|
||||
return state.with(FACING_TO_PROPERTY_MAP.get(connectedDirection.getOpposite()), true);
|
||||
|
||||
// Use preferred
|
||||
return state.with(FACING_TO_PROPERTY_MAP.get(preferredDirection), true)
|
||||
.with(FACING_TO_PROPERTY_MAP.get(preferredDirection.getOpposite()), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidState getFluidState(BlockState state) {
|
||||
return state.get(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getStillFluidState(false)
|
||||
: Fluids.EMPTY.getDefaultState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.Direction;
|
||||
|
||||
public class FluidPipeTileEntity extends SmartTileEntity {
|
||||
|
||||
FluidPipeBehaviour behaviour;
|
||||
|
||||
public FluidPipeTileEntity(TileEntityType<?> tileEntityTypeIn) {
|
||||
super(tileEntityTypeIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
|
||||
behaviour = new StandardPipeBehaviour(this);
|
||||
behaviours.add(behaviour);
|
||||
}
|
||||
|
||||
class StandardPipeBehaviour extends FluidPipeBehaviour {
|
||||
|
||||
public StandardPipeBehaviour(SmartTileEntity te) {
|
||||
super(te);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnectedTo(BlockState state, Direction direction) {
|
||||
return FluidPipeBlock.isPipe(state) && state.get(FluidPipeBlock.FACING_TO_PROPERTY_MAP.get(direction));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableObject;
|
||||
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.CreateClient;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.outliner.Outline.OutlineParams;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
|
||||
public class FluidPropagator {
|
||||
|
||||
public static FluidPipeBehaviour getPipe(IBlockReader reader, BlockPos pos) {
|
||||
return TileEntityBehaviour.get(reader, pos, FluidPipeBehaviour.TYPE);
|
||||
}
|
||||
|
||||
public static boolean isOpenEnd(IBlockReader reader, BlockPos pos, Direction side) {
|
||||
BlockPos connectedPos = pos.offset(side);
|
||||
BlockState connectedState = reader.getBlockState(connectedPos);
|
||||
FluidPipeBehaviour pipe = FluidPropagator.getPipe(reader, connectedPos);
|
||||
if (pipe != null && pipe.isConnectedTo(connectedState, side.getOpposite()))
|
||||
return false;
|
||||
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING)
|
||||
.getAxis() == side.getAxis())
|
||||
return false;
|
||||
if (Block.hasSolidSide(connectedState, reader, connectedPos, side.getOpposite()))
|
||||
return false;
|
||||
if (!(connectedState.getMaterial()
|
||||
.isReplaceable() && connectedState.getBlockHardness(reader, connectedPos) != -1)
|
||||
&& !connectedState.has(BlockStateProperties.WATERLOGGED))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void propagateChangedPipe(IWorld world, BlockPos pipePos, BlockState pipeState) {
|
||||
List<BlockPos> frontier = new ArrayList<>();
|
||||
Set<BlockPos> visited = new HashSet<>();
|
||||
|
||||
frontier.add(pipePos);
|
||||
|
||||
// Visit all connected pumps to update their network
|
||||
while (!frontier.isEmpty()) {
|
||||
BlockPos currentPos = frontier.remove(0);
|
||||
if (visited.contains(currentPos))
|
||||
continue;
|
||||
visited.add(currentPos);
|
||||
BlockState currentState = currentPos.equals(pipePos) ? pipeState : world.getBlockState(currentPos);
|
||||
FluidPipeBehaviour pipe = getPipe(world, currentPos);
|
||||
if (pipe == null)
|
||||
continue;
|
||||
for (Direction direction : getPipeConnections(currentState, pipe)) {
|
||||
BlockPos target = currentPos.offset(direction);
|
||||
if (!world.isAreaLoaded(target, 0))
|
||||
continue;
|
||||
|
||||
TileEntity tileEntity = world.getTileEntity(target);
|
||||
BlockState targetState = world.getBlockState(target);
|
||||
if (tileEntity instanceof PumpTileEntity) {
|
||||
if (!AllBlocks.MECHANICAL_PUMP.has(targetState) || targetState.get(PumpBlock.FACING)
|
||||
.getAxis() != direction.getAxis())
|
||||
continue;
|
||||
PumpTileEntity pump = (PumpTileEntity) tileEntity;
|
||||
pump.updatePipesOnSide(direction.getOpposite());
|
||||
continue;
|
||||
}
|
||||
if (visited.contains(target))
|
||||
continue;
|
||||
FluidPipeBehaviour targetPipe = getPipe(world, target);
|
||||
if (targetPipe == null)
|
||||
continue;
|
||||
if (targetPipe.isConnectedTo(targetState, direction.getOpposite()))
|
||||
frontier.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Direction> getPipeConnections(BlockState state, FluidPipeBehaviour pipe) {
|
||||
List<Direction> list = new ArrayList<>();
|
||||
for (Direction d : Iterate.directions)
|
||||
if (pipe.isConnectedTo(state, d))
|
||||
list.add(d);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int getPumpRange() {
|
||||
return AllConfigs.SERVER.fluids.mechanicalPumpRange.get();
|
||||
}
|
||||
|
||||
public static OutlineParams showBlockFace(BlockFace face) {
|
||||
MutableObject<OutlineParams> params = new MutableObject<>(new OutlineParams());
|
||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
|
||||
Vec3d directionVec = new Vec3d(face.getFace()
|
||||
.getDirectionVec());
|
||||
Vec3d scaleVec = directionVec.scale(-.25f * face.getFace()
|
||||
.getAxisDirection()
|
||||
.getOffset());
|
||||
directionVec = directionVec.scale(.5f);
|
||||
params.setValue(CreateClient.outliner.showAABB(face,
|
||||
FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(face.getPos())))
|
||||
.grow(scaleVec.x, scaleVec.y, scaleVec.z)
|
||||
.grow(1 / 16f)));
|
||||
});
|
||||
return params.getValue();
|
||||
}
|
||||
|
||||
static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25);
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import com.simibubi.create.foundation.fluid.FluidHelper;
|
||||
import com.simibubi.create.foundation.utility.BlockHelper;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
public class FluidReactions {
|
||||
|
||||
public static void handlePipeFlowCollision(World world, BlockPos pos, FluidStack fluid, FluidStack fluid2) {
|
||||
Fluid f1 = fluid.getFluid();
|
||||
Fluid f2 = fluid2.getFluid();
|
||||
BlockHelper.destroyBlock(world, pos, 1);
|
||||
if (f1 == Fluids.WATER && f2 == Fluids.LAVA || f2 == Fluids.WATER && f1 == Fluids.LAVA)
|
||||
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
|
||||
}
|
||||
|
||||
public static void handlePipeSpillCollision(World world, BlockPos pos, Fluid pipeFluid, IFluidState worldFluid) {
|
||||
Fluid pf = FluidHelper.convertToStill(pipeFluid);
|
||||
Fluid wf = worldFluid.getFluid();
|
||||
if (pf == Fluids.WATER && wf == Fluids.LAVA)
|
||||
world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState());
|
||||
if (pf == Fluids.WATER && wf == Fluids.FLOWING_LAVA)
|
||||
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
|
||||
else if (pf == Fluids.LAVA && wf == Fluids.WATER)
|
||||
world.setBlockState(pos, Blocks.STONE.getDefaultState());
|
||||
else if (pf == Fluids.LAVA && wf == Fluids.FLOWING_WATER)
|
||||
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
|
||||
}
|
||||
|
||||
}
|
|
@ -182,7 +182,7 @@ public class FluidTankBlock extends Block implements IWrenchable, ITE<FluidTankT
|
|||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
|
||||
controllerTE.sendData();
|
||||
controllerTE.sendDataImmediately();
|
||||
controllerTE.markDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,6 +276,7 @@ public class FluidTankConnectivityHandler {
|
|||
}
|
||||
}
|
||||
|
||||
te.fluidCapability.invalidate();
|
||||
if (tryReconnect)
|
||||
formTanks(world, cache == null ? new TankSearchCache() : cache, frontier);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,10 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
protected int width;
|
||||
protected int height;
|
||||
|
||||
private static final int SYNC_RATE = 8;
|
||||
protected int syncCooldown;
|
||||
protected boolean queuedSync;
|
||||
|
||||
// For rendering purposes only
|
||||
InterpolatedChasingValue fluidLevel;
|
||||
|
||||
|
@ -60,6 +64,7 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
window = true;
|
||||
height = 1;
|
||||
width = 1;
|
||||
refreshCapability();
|
||||
}
|
||||
|
||||
protected void updateConnectivity() {
|
||||
|
@ -74,6 +79,11 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (syncCooldown > 0) {
|
||||
syncCooldown--;
|
||||
if (syncCooldown == 0 && queuedSync)
|
||||
sendData();
|
||||
}
|
||||
if (updateConnectivity)
|
||||
updateConnectivity();
|
||||
if (fluidLevel != null)
|
||||
|
@ -96,7 +106,7 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
|
||||
FluidAttributes attributes = newFluidStack.getFluid()
|
||||
.getAttributes();
|
||||
int luminosity = attributes.getLuminosity(newFluidStack) / 2;
|
||||
int luminosity = (int) (attributes.getLuminosity(newFluidStack) / 1.2f);
|
||||
boolean reversed = attributes.isLighterThanAir();
|
||||
int maxY = (int) ((getFillState() * height) + 1);
|
||||
|
||||
|
@ -116,6 +126,11 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!world.isRemote) {
|
||||
markDirty();
|
||||
sendData();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setLuminosity(int luminosity) {
|
||||
|
@ -162,6 +177,7 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
getWorld().setBlockState(pos, state, 22);
|
||||
}
|
||||
|
||||
refreshCapability();
|
||||
markDirty();
|
||||
sendData();
|
||||
}
|
||||
|
@ -173,6 +189,23 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
te.setWindows(!te.window);
|
||||
}
|
||||
|
||||
public void sendDataImmediately() {
|
||||
syncCooldown = 0;
|
||||
queuedSync = false;
|
||||
sendData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData() {
|
||||
if (syncCooldown > 0) {
|
||||
queuedSync = true;
|
||||
return;
|
||||
}
|
||||
super.sendData();
|
||||
queuedSync = false;
|
||||
syncCooldown = SYNC_RATE;
|
||||
}
|
||||
|
||||
public void setWindows(boolean window) {
|
||||
this.window = window;
|
||||
for (int yOffset = 0; yOffset < height; yOffset++) {
|
||||
|
@ -213,10 +246,18 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
if (controller.equals(this.controller))
|
||||
return;
|
||||
this.controller = controller;
|
||||
refreshCapability();
|
||||
markDirty();
|
||||
sendData();
|
||||
}
|
||||
|
||||
private void refreshCapability() {
|
||||
LazyOptional<IFluidHandler> oldCap = fluidCapability;
|
||||
fluidCapability = LazyOptional.of(() -> isController() ? tankInventory
|
||||
: getControllerTE() != null ? getControllerTE().tankInventory : new FluidTank(0));
|
||||
oldCap.invalidate();
|
||||
}
|
||||
|
||||
public BlockPos getController() {
|
||||
return isController() ? pos : controller;
|
||||
}
|
||||
|
@ -236,39 +277,38 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT tag) {
|
||||
super.read(tag);
|
||||
updateConnectivity = tag.contains("Uninitialized");
|
||||
luminosity = tag.getInt("Luminosity");
|
||||
controller = null;
|
||||
|
||||
if (tag.contains("Controller"))
|
||||
controller = NBTUtil.readBlockPos(tag.getCompound("Controller"));
|
||||
|
||||
if (isController()) {
|
||||
window = tag.getBoolean("Window");
|
||||
width = tag.getInt("Size");
|
||||
height = tag.getInt("Height");
|
||||
tankInventory.setCapacity(getTotalTankSize() * getCapacityMultiplier());
|
||||
tankInventory.readFromNBT(tag.getCompound("TankContent"));
|
||||
if (tankInventory.getSpace() < 0)
|
||||
tankInventory.drain(-tankInventory.getSpace(), FluidAction.EXECUTE);
|
||||
}
|
||||
|
||||
if (tag.contains("ForceFluidLevel") || fluidLevel == null)
|
||||
fluidLevel = new InterpolatedChasingValue().start(getFillState())
|
||||
.withSpeed(1 / 2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
BlockPos controllerBefore = controller;
|
||||
int prevSize = width;
|
||||
int prevHeight = height;
|
||||
int prevLum = luminosity;
|
||||
|
||||
updateConnectivity = compound.contains("Uninitialized");
|
||||
luminosity = compound.getInt("Luminosity");
|
||||
controller = null;
|
||||
|
||||
super.readClientUpdate(tag);
|
||||
if (compound.contains("Controller"))
|
||||
controller = NBTUtil.readBlockPos(compound.getCompound("Controller"));
|
||||
|
||||
if (isController()) {
|
||||
window = compound.getBoolean("Window");
|
||||
width = compound.getInt("Size");
|
||||
height = compound.getInt("Height");
|
||||
tankInventory.setCapacity(getTotalTankSize() * getCapacityMultiplier());
|
||||
tankInventory.readFromNBT(compound.getCompound("TankContent"));
|
||||
if (tankInventory.getSpace() < 0)
|
||||
tankInventory.drain(-tankInventory.getSpace(), FluidAction.EXECUTE);
|
||||
}
|
||||
|
||||
if (compound.contains("ForceFluidLevel") || fluidLevel == null)
|
||||
fluidLevel = new InterpolatedChasingValue().start(getFillState())
|
||||
.withSpeed(1 / 2f);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
|
||||
boolean changeOfController =
|
||||
controllerBefore == null ? controller != null : !controllerBefore.equals(controller);
|
||||
if (changeOfController || prevSize != width || prevHeight != height) {
|
||||
|
@ -279,15 +319,17 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
}
|
||||
if (isController()) {
|
||||
float fillState = getFillState();
|
||||
if (tag.contains("ForceFluidLevel") || fluidLevel == null)
|
||||
fluidLevel = new InterpolatedChasingValue().start(fillState)
|
||||
.withSpeed(1 / 2f);
|
||||
if (compound.contains("ForceFluidLevel") || fluidLevel == null)
|
||||
fluidLevel = new InterpolatedChasingValue().start(fillState);
|
||||
fluidLevel.target(fillState);
|
||||
}
|
||||
if (luminosity != prevLum && hasWorld())
|
||||
world.getChunkProvider()
|
||||
.getLightManager()
|
||||
.checkBlock(pos);
|
||||
.getLightManager()
|
||||
.checkBlock(pos);
|
||||
|
||||
if (compound.contains("LazySync"))
|
||||
fluidLevel.withSpeed(compound.contains("LazySync") ? 1 / 8f : 1 / 2f);
|
||||
}
|
||||
|
||||
protected float getFillState() {
|
||||
|
@ -295,44 +337,42 @@ public class FluidTankTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (updateConnectivity)
|
||||
tag.putBoolean("Uninitialized", true);
|
||||
compound.putBoolean("Uninitialized", true);
|
||||
if (!isController())
|
||||
tag.put("Controller", NBTUtil.writeBlockPos(controller));
|
||||
compound.put("Controller", NBTUtil.writeBlockPos(controller));
|
||||
if (isController()) {
|
||||
tag.putBoolean("Window", window);
|
||||
tag.put("TankContent", tankInventory.writeToNBT(new CompoundNBT()));
|
||||
tag.putInt("Size", width);
|
||||
tag.putInt("Height", height);
|
||||
compound.putBoolean("Window", window);
|
||||
compound.put("TankContent", tankInventory.writeToNBT(new CompoundNBT()));
|
||||
compound.putInt("Size", width);
|
||||
compound.putInt("Height", height);
|
||||
}
|
||||
tag.putInt("Luminosity", luminosity);
|
||||
return super.write(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
compound.putInt("Luminosity", luminosity);
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (forceFluidLevelUpdate)
|
||||
compound.putBoolean("ForceFluidLevel", true);
|
||||
if (queuedSync)
|
||||
compound.putBoolean("LazySync", true);
|
||||
forceFluidLevelUpdate = false;
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
|
||||
if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
|
||||
FluidTankTileEntity controller = getControllerTE();
|
||||
if (controller != null)
|
||||
return controller.fluidCapability.cast();
|
||||
}
|
||||
if (!fluidCapability.isPresent())
|
||||
refreshCapability();
|
||||
if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)
|
||||
return fluidCapability.cast();
|
||||
return super.getCapability(cap, side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
fluidCapability.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidTank;
|
||||
|
||||
public class InterPumpFluidHandler extends FluidTank {
|
||||
|
||||
InterPumpEndpoint endpoint;
|
||||
|
||||
public InterPumpFluidHandler(InterPumpEndpoint endpoint) {
|
||||
super(Integer.MAX_VALUE);
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, FluidAction action) {
|
||||
if (resource.isEmpty())
|
||||
return 0;
|
||||
int maxInput = Math.min(resource.getAmount(), Math.max(getTransferCapacity() - getFluidAmount(), 0));
|
||||
FluidStack toInsert = resource.copy();
|
||||
toInsert.setAmount(maxInput);
|
||||
FluidPropagator.showBlockFace(endpoint.location).colored(0x77d196).lineWidth(1/4f);
|
||||
return super.fill(toInsert, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack drain(int maxDrain, FluidAction action) {
|
||||
return super.drain(maxDrain, action);
|
||||
}
|
||||
|
||||
public FluidStack provide() {
|
||||
FluidStack heldFluid = getFluid();
|
||||
if (heldFluid.isEmpty())
|
||||
return heldFluid;
|
||||
FluidStack copy = heldFluid.copy();
|
||||
copy.setAmount(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private int getTransferCapacity() {
|
||||
return Math.min(endpoint.getTransferSpeed(true), endpoint.getTransferSpeed(false));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FlowingFluidBlock;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidTank;
|
||||
|
||||
public class OpenEndedPipe {
|
||||
|
||||
World world;
|
||||
|
||||
private OpenEndFluidHandler fluidHandler;
|
||||
private BlockPos outputPos;
|
||||
private boolean wasPulling;
|
||||
private boolean stale;
|
||||
|
||||
public OpenEndedPipe(BlockFace face) {
|
||||
fluidHandler = new OpenEndFluidHandler();
|
||||
outputPos = face.getConnectedPos();
|
||||
}
|
||||
|
||||
public void tick(World world, boolean pulling) {
|
||||
this.world = world;
|
||||
if (!world.isAreaLoaded(outputPos, 0))
|
||||
return;
|
||||
if (pulling != wasPulling) {
|
||||
if (pulling)
|
||||
fluidHandler.clear();
|
||||
wasPulling = pulling;
|
||||
}
|
||||
|
||||
BlockState state = world.getBlockState(outputPos);
|
||||
IFluidState fluidState = state.getFluidState();
|
||||
boolean waterlog = state.has(BlockStateProperties.WATERLOGGED);
|
||||
|
||||
if (!waterlog && !state.getMaterial()
|
||||
.isReplaceable())
|
||||
return;
|
||||
|
||||
// TODO different pipe end types
|
||||
if (pulling) {
|
||||
if (fluidState.isEmpty() || !fluidState.isSource())
|
||||
return;
|
||||
if (!fluidHandler.tryCollectFluid(fluidState.getFluid()))
|
||||
return;
|
||||
if (waterlog) {
|
||||
world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, false), 3);
|
||||
return;
|
||||
}
|
||||
world.setBlockState(outputPos, fluidState.getBlockState()
|
||||
.with(FlowingFluidBlock.LEVEL, 14), 3);
|
||||
return;
|
||||
}
|
||||
|
||||
Fluid providedFluid = fluidHandler.tryProvidingFluid();
|
||||
if (providedFluid == null)
|
||||
return;
|
||||
if (!fluidState.isEmpty() && fluidState.getFluid() != providedFluid) {
|
||||
FluidReactions.handlePipeSpillCollision(world, outputPos, providedFluid, fluidState);
|
||||
return;
|
||||
}
|
||||
if (fluidState.isSource())
|
||||
return;
|
||||
if (waterlog) {
|
||||
if (providedFluid.getFluid() != Fluids.WATER)
|
||||
return;
|
||||
world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, true), 3);
|
||||
return;
|
||||
}
|
||||
world.setBlockState(outputPos, providedFluid.getDefaultState()
|
||||
.getBlockState(), 3);
|
||||
}
|
||||
|
||||
public LazyOptional<IFluidHandler> getCapability() {
|
||||
return LazyOptional.of(() -> fluidHandler);
|
||||
}
|
||||
|
||||
public CompoundNBT writeToNBT(CompoundNBT compound) {
|
||||
fluidHandler.writeToNBT(compound);
|
||||
compound.putBoolean("Pulling", wasPulling);
|
||||
return compound;
|
||||
}
|
||||
|
||||
public void readNBT(CompoundNBT compound) {
|
||||
fluidHandler.readFromNBT(compound);
|
||||
wasPulling = compound.getBoolean("Pulling");
|
||||
}
|
||||
|
||||
public void markStale() {
|
||||
stale = true;
|
||||
}
|
||||
|
||||
public void unmarkStale() {
|
||||
stale = false;
|
||||
}
|
||||
|
||||
public boolean isStale() {
|
||||
return stale;
|
||||
}
|
||||
|
||||
private class OpenEndFluidHandler extends FluidTank {
|
||||
|
||||
public OpenEndFluidHandler() {
|
||||
super(1500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, FluidAction action) {
|
||||
// Never allow being filled when a source is attached
|
||||
if (world == null)
|
||||
return 0;
|
||||
if (!world.isAreaLoaded(outputPos, 0))
|
||||
return 0;
|
||||
BlockState state = world.getBlockState(outputPos);
|
||||
IFluidState fluidState = state.getFluidState();
|
||||
if (!fluidState.isEmpty() && fluidState.getFluid() != resource.getFluid()) {
|
||||
FluidReactions.handlePipeSpillCollision(world, outputPos, resource.getFluid(), fluidState);
|
||||
return 0;
|
||||
}
|
||||
if (fluidState.isSource())
|
||||
return 0;
|
||||
if (!(state.has(BlockStateProperties.WATERLOGGED) && resource.getFluid() == Fluids.WATER)
|
||||
&& !state.getMaterial()
|
||||
.isReplaceable())
|
||||
return 0;
|
||||
|
||||
// Never allow being filled above 1000
|
||||
FluidStack insertable = resource.copy();
|
||||
insertable.setAmount(Math.min(insertable.getAmount(), Math.max(1000 - getFluidAmount(), 0)));
|
||||
return super.fill(insertable, action);
|
||||
}
|
||||
|
||||
public boolean tryCollectFluid(Fluid fluid) {
|
||||
for (boolean simulate : Iterate.trueAndFalse)
|
||||
if (super.fill(new FluidStack(fluid, 1000),
|
||||
simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE) != 1000)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Fluid tryProvidingFluid() {
|
||||
Fluid fluid = getFluid().getFluid();
|
||||
for (boolean simulate : Iterate.trueAndFalse)
|
||||
if (drain(1000, simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE).getAmount() != 1000)
|
||||
return null;
|
||||
return fluid;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
setFluid(FluidStack.EMPTY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,23 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.simibubi.create.AllShapes;
|
||||
import com.simibubi.create.AllTileEntities;
|
||||
import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock;
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.IWaterLoggable;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.item.ItemUseContext;
|
||||
import net.minecraft.network.DebugPacketSender;
|
||||
import net.minecraft.state.StateContainer.Builder;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
@ -20,71 +29,137 @@ import net.minecraft.util.math.shapes.VoxelShape;
|
|||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.IWorldReader;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable {
|
||||
|
||||
public PumpBlock(Properties p_i48415_1_) {
|
||||
super(p_i48415_1_);
|
||||
setDefaultState(super.getDefaultState().with(BlockStateProperties.WATERLOGGED, false));
|
||||
}
|
||||
public PumpBlock(Properties p_i48415_1_) {
|
||||
super(p_i48415_1_);
|
||||
setDefaultState(super.getDefaultState().with(BlockStateProperties.WATERLOGGED, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(BlockState state) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean hasTileEntity(BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
|
||||
return AllTileEntities.MECHANICAL_PUMP.create();
|
||||
}
|
||||
@Override
|
||||
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
|
||||
return AllTileEntities.MECHANICAL_PUMP.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getRotatedBlockState(BlockState originalState, Direction targetedFace) {
|
||||
return originalState.with(FACING, originalState.get(FACING)
|
||||
.getOpposite());
|
||||
}
|
||||
@Override
|
||||
public BlockState getRotatedBlockState(BlockState originalState, Direction targetedFace) {
|
||||
return originalState.with(FACING, originalState.get(FACING)
|
||||
.getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Axis getRotationAxis(BlockState state) {
|
||||
return state.get(FACING)
|
||||
.getAxis();
|
||||
}
|
||||
@Override
|
||||
public BlockState updateAfterWrenched(BlockState newState, ItemUseContext context) {
|
||||
BlockState state = super.updateAfterWrenched(newState, context);
|
||||
World world = context.getWorld();
|
||||
BlockPos pos = context.getPos();
|
||||
if (world.isRemote)
|
||||
return state;
|
||||
TileEntity tileEntity = world.getTileEntity(pos);
|
||||
if (!(tileEntity instanceof PumpTileEntity))
|
||||
return state;
|
||||
PumpTileEntity pump = (PumpTileEntity) tileEntity;
|
||||
if (pump.networks == null)
|
||||
return state;
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_,
|
||||
ISelectionContext p_220053_4_) {
|
||||
return AllShapes.PUMP.get(state.get(FACING));
|
||||
}
|
||||
FluidNetwork apn1 = pump.networks.get(true);
|
||||
FluidNetwork apn2 = pump.networks.get(false);
|
||||
|
||||
@Override
|
||||
public boolean hasIntegratedCogwheel(IWorldReader world, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
// Collect pipes that can be skipped
|
||||
apn1.clearFlows(world, true);
|
||||
apn2.clearFlows(world, true);
|
||||
|
||||
@Override
|
||||
public IFluidState getFluidState(BlockState state) {
|
||||
return state.get(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getStillFluidState(false) : Fluids.EMPTY.getDefaultState();
|
||||
}
|
||||
// Swap skipsets as the networks change sides
|
||||
Map<BlockFace, FluidStack> skippedConnections = apn1.previousFlow;
|
||||
apn1.previousFlow = apn2.previousFlow;
|
||||
apn2.previousFlow = skippedConnections;
|
||||
|
||||
@Override
|
||||
protected void fillStateContainer(Builder<Block, BlockState> builder) {
|
||||
builder.add(BlockStateProperties.WATERLOGGED);
|
||||
super.fillStateContainer(builder);
|
||||
}
|
||||
// Init networks next tick
|
||||
pump.networksToUpdate.forEach(MutableBoolean::setTrue);
|
||||
pump.networks.swap();
|
||||
pump.reversed = !pump.reversed;
|
||||
|
||||
@Override
|
||||
public BlockState updatePostPlacement(BlockState state, Direction direction, BlockState neighbourState,
|
||||
IWorld world, BlockPos pos, BlockPos neighbourPos) {
|
||||
if (state.get(BlockStateProperties.WATERLOGGED)) {
|
||||
world.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
}
|
||||
return state;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockItemUseContext context) {
|
||||
IFluidState ifluidstate = context.getWorld().getFluidState(context.getPos());
|
||||
return super.getStateForPlacement(context).with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
}
|
||||
@Override
|
||||
public Axis getRotationAxis(BlockState state) {
|
||||
return state.get(FACING)
|
||||
.getAxis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_,
|
||||
ISelectionContext p_220053_4_) {
|
||||
return AllShapes.PUMP.get(state.get(FACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIntegratedCogwheel(IWorldReader world, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos,
|
||||
boolean isMoving) {
|
||||
DebugPacketSender.func_218806_a(world, pos);
|
||||
if (world.isRemote)
|
||||
return;
|
||||
if (otherBlock instanceof FluidPipeBlock)
|
||||
return;
|
||||
TileEntity tileEntity = world.getTileEntity(pos);
|
||||
if (!(tileEntity instanceof PumpTileEntity))
|
||||
return;
|
||||
PumpTileEntity pump = (PumpTileEntity) tileEntity;
|
||||
Direction facing = state.get(FACING);
|
||||
for (boolean front : Iterate.trueAndFalse) {
|
||||
Direction side = front ? facing : facing.getOpposite();
|
||||
if (!pos.offset(side)
|
||||
.equals(neighborPos))
|
||||
continue;
|
||||
pump.updatePipesOnSide(side);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFluidState getFluidState(BlockState state) {
|
||||
return state.get(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getStillFluidState(false)
|
||||
: Fluids.EMPTY.getDefaultState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillStateContainer(Builder<Block, BlockState> builder) {
|
||||
builder.add(BlockStateProperties.WATERLOGGED);
|
||||
super.fillStateContainer(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState updatePostPlacement(BlockState state, Direction direction, BlockState neighbourState,
|
||||
IWorld world, BlockPos pos, BlockPos neighbourPos) {
|
||||
if (state.get(BlockStateProperties.WATERLOGGED)) {
|
||||
world.getPendingFluidTicks()
|
||||
.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockItemUseContext context) {
|
||||
IFluidState ifluidstate = context.getWorld()
|
||||
.getFluidState(context.getPos());
|
||||
return super.getStateForPlacement(context).with(BlockStateProperties.WATERLOGGED,
|
||||
Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
}
|
||||
|
||||
public static boolean isPump(BlockState state) {
|
||||
return state.getBlock() instanceof PumpBlock;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class PumpRenderer extends KineticTileEntityRenderer {
|
|||
PumpTileEntity pump = (PumpTileEntity) te;
|
||||
Vec3d rotationOffset = new Vec3d(.5, 14 / 16f, .5);
|
||||
BlockState blockState = te.getBlockState();
|
||||
float angle = MathHelper.lerp(pump.arrowDirection.get(partialTicks), 0, 90) - 90;
|
||||
float angle = MathHelper.lerp(pump.arrowDirection.getValue(partialTicks), 0, 90) - 90;
|
||||
for (float yRot : new float[] { 0, 90 }) {
|
||||
ms.push();
|
||||
SuperByteBuffer arrow = AllBlockPartials.MECHANICAL_PUMP_ARROW.renderOn(blockState);
|
||||
|
|
|
@ -1,29 +1,349 @@
|
|||
package com.simibubi.create.content.contraptions.fluids;
|
||||
|
||||
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.foundation.gui.widgets.InterpolatedChasingValue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.foundation.utility.BlockFace;
|
||||
import com.simibubi.create.foundation.utility.Couple;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.LerpedFloat;
|
||||
import com.simibubi.create.foundation.utility.LerpedFloat.Chaser;
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||
|
||||
public class PumpTileEntity extends KineticTileEntity {
|
||||
|
||||
InterpolatedChasingValue arrowDirection;
|
||||
LerpedFloat arrowDirection;
|
||||
Couple<FluidNetwork> networks;
|
||||
Couple<Map<BlockFace, OpenEndedPipe>> openEnds;
|
||||
Couple<MutableBoolean> networksToUpdate;
|
||||
|
||||
boolean reversed;
|
||||
FluidStack providedFluid;
|
||||
|
||||
public PumpTileEntity(TileEntityType<?> typeIn) {
|
||||
super(typeIn);
|
||||
arrowDirection = new InterpolatedChasingValue();
|
||||
arrowDirection.start(1);
|
||||
arrowDirection = LerpedFloat.linear()
|
||||
.startWithValue(1);
|
||||
networksToUpdate = Couple.create(MutableBoolean::new);
|
||||
openEnds = Couple.create(HashMap::new);
|
||||
setProvidedFluid(FluidStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
reversed = getSpeed() < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
float speed = getSpeed();
|
||||
|
||||
if (world.isRemote) {
|
||||
float speed = getSpeed();
|
||||
if (speed != 0)
|
||||
arrowDirection.target(Math.signum(speed));
|
||||
arrowDirection.tick();
|
||||
if (speed == 0)
|
||||
return;
|
||||
arrowDirection.chase(speed >= 0 ? 1 : -1, .5f, Chaser.EXP);
|
||||
arrowDirection.tickChaser();
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState blockState = getBlockState();
|
||||
if (!(blockState.getBlock() instanceof PumpBlock))
|
||||
return;
|
||||
Direction face = blockState.get(PumpBlock.FACING);
|
||||
MutableBoolean networkUpdated = new MutableBoolean(false);
|
||||
|
||||
if (networks == null) {
|
||||
networks = Couple.create(new FluidNetwork(), new FluidNetwork());
|
||||
networks.forEachWithContext((fn, front) -> {
|
||||
BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite());
|
||||
fn.assemble(world, this, blockFace);
|
||||
FluidPropagator.showBlockFace(blockFace)
|
||||
.lineWidth(1 / 8f);
|
||||
});
|
||||
networkUpdated.setTrue();
|
||||
}
|
||||
|
||||
networksToUpdate.forEachWithContext((update, front) -> {
|
||||
if (update.isFalse())
|
||||
return;
|
||||
FluidNetwork activePipeNetwork = networks.get(front);
|
||||
if (activePipeNetwork == null)
|
||||
return;
|
||||
BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite());
|
||||
activePipeNetwork.reAssemble(world, this, blockFace);
|
||||
FluidPropagator.showBlockFace(blockFace)
|
||||
.lineWidth(1 / 8f);
|
||||
update.setFalse();
|
||||
networkUpdated.setTrue();
|
||||
});
|
||||
|
||||
if (networkUpdated.isTrue())
|
||||
return;
|
||||
|
||||
networks.forEach(fn -> fn.tick(world, this));
|
||||
|
||||
if (speed == 0)
|
||||
return;
|
||||
if (speed < 0 != reversed) {
|
||||
networks.forEachWithContext((fn, current) -> fn.clearFlows(world, true));
|
||||
reversed = speed < 0;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean pullingSide = isPullingOnSide(true);
|
||||
float flowSpeed = Math.abs(speed) / 256f;
|
||||
|
||||
networks.forEachWithContext((fn, front) -> {
|
||||
boolean pulling = isPullingOnSide(front);
|
||||
fn.tickFlows(world, this, pulling, flowSpeed);
|
||||
openEnds.get(front)
|
||||
.values()
|
||||
.forEach(oep -> oep.tick(world, pulling));
|
||||
});
|
||||
|
||||
if (!networks.get(pullingSide)
|
||||
.hasEndpoints()) {
|
||||
setProvidedFluid(FluidStack.EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (networks.getFirst()
|
||||
.hasEndpoints()
|
||||
&& networks.getSecond()
|
||||
.hasEndpoints()) {
|
||||
performTransfer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
if (networks != null)
|
||||
networks.forEachWithContext((fn, current) -> fn.clearFlows(world, false));
|
||||
}
|
||||
|
||||
private void performTransfer() {
|
||||
boolean input = isPullingOnSide(true);
|
||||
Collection<FluidNetworkEndpoint> inputs = networks.get(input)
|
||||
.getEndpoints(true);
|
||||
Collection<FluidNetworkEndpoint> outputs = networks.get(!input)
|
||||
.getEndpoints(false);
|
||||
|
||||
int flowSpeed = getFluidTransferSpeed();
|
||||
FluidStack transfer = FluidStack.EMPTY;
|
||||
for (boolean simulate : Iterate.trueAndFalse) {
|
||||
FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE;
|
||||
|
||||
List<FluidNetworkEndpoint> availableInputs = new ArrayList<>(inputs);
|
||||
while (!availableInputs.isEmpty() && transfer.getAmount() < flowSpeed) {
|
||||
int diff = flowSpeed - transfer.getAmount();
|
||||
int dividedTransfer = diff / availableInputs.size();
|
||||
int remainder = diff % availableInputs.size();
|
||||
|
||||
for (Iterator<FluidNetworkEndpoint> iterator = availableInputs.iterator(); iterator.hasNext();) {
|
||||
int toTransfer = dividedTransfer;
|
||||
if (remainder > 0) {
|
||||
toTransfer++;
|
||||
remainder--;
|
||||
}
|
||||
|
||||
FluidNetworkEndpoint ne = iterator.next();
|
||||
IFluidHandler handler = ne.provideHandler()
|
||||
.orElse(null);
|
||||
if (handler == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
FluidStack drained = handler.drain(toTransfer, action);
|
||||
if (drained.isEmpty()) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (transfer.isFluidEqual(drained) || transfer.isEmpty()) {
|
||||
if (drained.getAmount() < toTransfer)
|
||||
iterator.remove();
|
||||
FluidStack copy = drained.copy();
|
||||
copy.setAmount(drained.getAmount() + transfer.getAmount());
|
||||
transfer = copy;
|
||||
continue;
|
||||
}
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<FluidNetworkEndpoint> availableOutputs = new ArrayList<>(outputs);
|
||||
while (!availableOutputs.isEmpty() && transfer.getAmount() > 0) {
|
||||
int dividedTransfer = transfer.getAmount() / availableOutputs.size();
|
||||
int remainder = transfer.getAmount() % availableOutputs.size();
|
||||
|
||||
for (Iterator<FluidNetworkEndpoint> iterator = availableOutputs.iterator(); iterator.hasNext();) {
|
||||
FluidNetworkEndpoint ne = iterator.next();
|
||||
int toTransfer = dividedTransfer;
|
||||
if (remainder > 0) {
|
||||
toTransfer++;
|
||||
remainder--;
|
||||
}
|
||||
|
||||
if (transfer.isEmpty())
|
||||
break;
|
||||
IFluidHandler handler = ne.provideHandler()
|
||||
.orElse(null);
|
||||
if (handler == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
FluidStack divided = transfer.copy();
|
||||
divided.setAmount(toTransfer);
|
||||
int fill = handler.fill(divided, action);
|
||||
transfer.setAmount(transfer.getAmount() - fill);
|
||||
if (fill < toTransfer)
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flowSpeed -= transfer.getAmount();
|
||||
transfer = FluidStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public int getFluidTransferSpeed() {
|
||||
float rotationSpeed = Math.abs(getSpeed());
|
||||
int flowSpeed = (int) (rotationSpeed / 2f);
|
||||
if (rotationSpeed != 0 && flowSpeed == 0)
|
||||
flowSpeed = 1;
|
||||
return flowSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Reversed", reversed);
|
||||
serializeOpenEnds(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
reversed = compound.getBoolean("Reversed");
|
||||
deserializeOpenEnds(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
public void updatePipesOnSide(Direction side) {
|
||||
if (!isSideAccessible(side))
|
||||
return;
|
||||
updatePipeNetwork(isFront(side));
|
||||
}
|
||||
|
||||
protected boolean isFront(Direction side) {
|
||||
if (networks == null)
|
||||
return false;
|
||||
BlockState blockState = getBlockState();
|
||||
if (!(blockState.getBlock() instanceof PumpBlock))
|
||||
return false;
|
||||
Direction front = blockState.get(PumpBlock.FACING);
|
||||
boolean isFront = side == front;
|
||||
return isFront;
|
||||
}
|
||||
|
||||
protected void updatePipeNetwork(boolean front) {
|
||||
if (networks != null)
|
||||
networks.get(front)
|
||||
.clearFlows(world, true);
|
||||
networksToUpdate.get(front)
|
||||
.setTrue();
|
||||
if (getSpeed() == 0 || (isPullingOnSide(front)) && networks != null)
|
||||
setProvidedFluid(FluidStack.EMPTY);
|
||||
}
|
||||
|
||||
public boolean isSideAccessible(Direction side) {
|
||||
BlockState blockState = getBlockState();
|
||||
if (!(blockState.getBlock() instanceof PumpBlock))
|
||||
return false;
|
||||
return blockState.get(PumpBlock.FACING)
|
||||
.getAxis() == side.getAxis();
|
||||
}
|
||||
|
||||
public boolean isPullingOnSide(boolean front) {
|
||||
return front == reversed;
|
||||
}
|
||||
|
||||
public Map<BlockFace, OpenEndedPipe> getOpenEnds(Direction side) {
|
||||
return openEnds.get(isFront(side));
|
||||
}
|
||||
|
||||
private void serializeOpenEnds(CompoundNBT compound) {
|
||||
compound.put("OpenEnds", openEnds.serializeEach(m -> {
|
||||
CompoundNBT compoundNBT = new CompoundNBT();
|
||||
ListNBT entries = new ListNBT();
|
||||
m.entrySet()
|
||||
.forEach(e -> {
|
||||
CompoundNBT innerCompound = new CompoundNBT();
|
||||
innerCompound.put("Pos", e.getKey()
|
||||
.serializeNBT());
|
||||
e.getValue()
|
||||
.writeToNBT(innerCompound);
|
||||
entries.add(innerCompound);
|
||||
});
|
||||
compoundNBT.put("Entries", entries);
|
||||
return compoundNBT;
|
||||
}));
|
||||
}
|
||||
|
||||
private void deserializeOpenEnds(CompoundNBT compound) {
|
||||
openEnds = Couple.deserializeEach(compound.getList("OpenEnds", NBT.TAG_COMPOUND), c -> {
|
||||
Map<BlockFace, OpenEndedPipe> map = new HashMap<>();
|
||||
NBTHelper.iterateCompoundList(c.getList("Entries", NBT.TAG_COMPOUND), innerCompound -> {
|
||||
BlockFace key = BlockFace.fromNBT(innerCompound.getCompound("Pos"));
|
||||
OpenEndedPipe value = new OpenEndedPipe(key);
|
||||
value.readNBT(innerCompound);
|
||||
map.put(key, value);
|
||||
});
|
||||
return map;
|
||||
});
|
||||
|
||||
compound.put("OpenEnds", openEnds.serializeEach(m -> {
|
||||
CompoundNBT compoundNBT = new CompoundNBT();
|
||||
ListNBT entries = new ListNBT();
|
||||
m.entrySet()
|
||||
.forEach(e -> {
|
||||
CompoundNBT innerCompound = new CompoundNBT();
|
||||
innerCompound.put("Pos", e.getKey()
|
||||
.serializeNBT());
|
||||
e.getValue()
|
||||
.writeToNBT(innerCompound);
|
||||
entries.add(innerCompound);
|
||||
});
|
||||
compoundNBT.put("Entries", entries);
|
||||
return compoundNBT;
|
||||
}));
|
||||
}
|
||||
|
||||
public void setProvidedFluid(FluidStack providedFluid) {
|
||||
this.providedFluid = providedFluid;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -118,8 +118,8 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
inputItemInventory.deserializeNBT(compound.getCompound("InputItems"));
|
||||
outputItemInventory.deserializeNBT(compound.getCompound("OutputItems"));
|
||||
if (compound.contains("fluids"))
|
||||
|
@ -128,15 +128,14 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
super.write(compound);
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
super.write(compound, clientPacket);
|
||||
compound.put("InputItems", inputItemInventory.serializeNBT());
|
||||
compound.put("OutputItems", outputItemInventory.serializeNBT());
|
||||
fluidInventory.ifPresent(combinedFuidHandler -> {
|
||||
ListNBT nbt = combinedFuidHandler.getListNBT();
|
||||
compound.put("fluids", nbt);
|
||||
});
|
||||
return compound;
|
||||
}
|
||||
|
||||
public void onEmptied() {
|
||||
|
|
|
@ -155,17 +155,17 @@ public class BlazeBurnerTileEntity extends SmartTileEntity {
|
|||
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("fuelLevel", activeFuel.ordinal());
|
||||
compound.putInt("burnTimeRemaining", remainingBurnTime);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
activeFuel = FuelType.values()[compound.getInt("fuelLevel")];
|
||||
remainingBurnTime = compound.getInt("burnTimeRemaining");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -103,21 +103,21 @@ public class SequencedGearshiftTileEntity extends SplitShaftTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("InstructionIndex", currentInstruction);
|
||||
compound.putInt("InstructionDuration", currentInstructionDuration);
|
||||
compound.putInt("Timer", timer);
|
||||
compound.put("Instructions", Instruction.serializeAll(instructions));
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
currentInstruction = compound.getInt("InstructionIndex");
|
||||
currentInstructionDuration = compound.getInt("InstructionDuration");
|
||||
timer = compound.getInt("Timer");
|
||||
instructions = Instruction.deserializeAll(compound.getList("Instructions", NBT.TAG_COMPOUND));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -79,8 +79,7 @@ public class BeltTileEntity extends KineticTileEntity {
|
|||
@Override
|
||||
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
|
||||
super.addBehaviours(behaviours);
|
||||
behaviours.add(new DirectBeltInputBehaviour(this)
|
||||
.setInsertionHandler(this::tryInsertingFromSide));
|
||||
behaviours.add(new DirectBeltInputBehaviour(this).setInsertionHandler(this::tryInsertingFromSide));
|
||||
behaviours.add(new TransportedItemStackHandlerBehaviour(this, this::applyToAllItems)
|
||||
.withStackPlacement(this::getWorldPositionOf));
|
||||
}
|
||||
|
@ -175,7 +174,7 @@ public class BeltTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (controller != null)
|
||||
compound.put("Controller", NBTUtil.writeBlockPos(controller));
|
||||
compound.putBoolean("IsController", isController());
|
||||
|
@ -186,23 +185,12 @@ public class BeltTileEntity extends KineticTileEntity {
|
|||
|
||||
if (isController())
|
||||
compound.put("Inventory", getInventory().write());
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
CasingType casingBefore = casing;
|
||||
super.readClientUpdate(tag);
|
||||
if (casingBefore != casing) {
|
||||
requestModelDataUpdate();
|
||||
if (hasWorld())
|
||||
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 16);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (compound.getBoolean("IsController"))
|
||||
controller = pos;
|
||||
|
@ -219,7 +207,16 @@ public class BeltTileEntity extends KineticTileEntity {
|
|||
if (isController())
|
||||
getInventory().read(compound.getCompound("Inventory"));
|
||||
|
||||
CasingType casingBefore = casing;
|
||||
casing = NBTHelper.readEnum(compound, "Casing", CasingType.class);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (casingBefore == casing)
|
||||
return;
|
||||
requestModelDataUpdate();
|
||||
if (hasWorld())
|
||||
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -397,16 +394,17 @@ public class BeltTileEntity extends KineticTileEntity {
|
|||
BeltTileEntity nextBeltController = getControllerTE();
|
||||
ItemStack inserted = transportedStack.stack;
|
||||
ItemStack empty = ItemStack.EMPTY;
|
||||
|
||||
|
||||
if (nextBeltController == null)
|
||||
return inserted;
|
||||
BeltInventory nextInventory = nextBeltController.getInventory();
|
||||
|
||||
|
||||
TileEntity teAbove = world.getTileEntity(pos.up());
|
||||
if (teAbove instanceof BrassTunnelTileEntity) {
|
||||
BrassTunnelTileEntity tunnelTE = (BrassTunnelTileEntity) teAbove;
|
||||
if (tunnelTE.hasDistributionBehaviour()) {
|
||||
if (!tunnelTE.getStackToDistribute().isEmpty())
|
||||
if (!tunnelTE.getStackToDistribute()
|
||||
.isEmpty())
|
||||
return inserted;
|
||||
if (!tunnelTE.testFlapFilter(side.getOpposite(), inserted))
|
||||
return inserted;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.simibubi.create.content.contraptions.relays.encased;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
|
@ -11,8 +14,6 @@ import net.minecraft.util.Direction;
|
|||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IWorldReader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@MethodsReturnNonnullByDefault
|
||||
public abstract class AbstractEncasedShaftBlock extends RotatedPillarKineticBlock {
|
||||
public AbstractEncasedShaftBlock(Properties properties) {
|
||||
|
@ -30,7 +31,6 @@ public abstract class AbstractEncasedShaftBlock extends RotatedPillarKineticBloc
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public PushReaction getPushReaction(@Nullable BlockState state) {
|
||||
return PushReaction.PUSH_ONLY;
|
||||
}
|
||||
|
|
|
@ -17,15 +17,15 @@ public class AdjustablePulleyTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("Signal", signal);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
signal = compound.getInt("Signal");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
public float getModifier() {
|
||||
|
|
|
@ -15,10 +15,8 @@ import net.minecraft.state.BooleanProperty;
|
|||
import net.minecraft.state.StateContainer.Builder;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.IWorldReader;
|
||||
import net.minecraft.world.TickPriority;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
|
|
@ -21,17 +21,17 @@ public class GaugeTileEntity extends KineticTileEntity implements IHaveGoggleInf
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putFloat("Value", dialTarget);
|
||||
compound.putInt("Color", color);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
dialTarget = compound.getFloat("Value");
|
||||
color = compound.getInt("Color");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -73,15 +73,15 @@ public class BeltObserverTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("TurnOff", turnOffTicks);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
turnOffTicks = compound.getInt("TurnOff");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,38 +52,18 @@ public class BeltTunnelTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
ListNBT flapsNBT = new ListNBT();
|
||||
for (Direction direction : flaps.keySet())
|
||||
flapsNBT.add(IntNBT.of(direction.getIndex()));
|
||||
compound.put("Flaps", flapsNBT);
|
||||
return super.write(compound);
|
||||
}
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
Set<Direction> newFlaps = new HashSet<>(6);
|
||||
ListNBT flapsNBT = compound.getList("Flaps", NBT.TAG_INT);
|
||||
for (INBT inbt : flapsNBT)
|
||||
if (inbt instanceof IntNBT)
|
||||
newFlaps.add(Direction.byIndex(((IntNBT) inbt).getInt()));
|
||||
|
||||
for (Direction d : Iterate.directions)
|
||||
if (!newFlaps.contains(d))
|
||||
flaps.remove(d);
|
||||
else if (!flaps.containsKey(d))
|
||||
flaps.put(d, new InterpolatedChasingValue().start(.25f)
|
||||
.target(0)
|
||||
.withSpeed(.05f));
|
||||
if (!clientPacket)
|
||||
return;
|
||||
|
||||
super.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT tag) {
|
||||
CompoundNBT writeToClient = super.writeToClient(tag);
|
||||
flapsNBT = new ListNBT();
|
||||
if (!flapsToSend.isEmpty()) {
|
||||
ListNBT flapsNBT = new ListNBT();
|
||||
for (Pair<Direction, Boolean> pair : flapsToSend) {
|
||||
CompoundNBT flap = new CompoundNBT();
|
||||
flap.putInt("Flap", pair.getKey()
|
||||
|
@ -91,22 +71,38 @@ public class BeltTunnelTileEntity extends SmartTileEntity {
|
|||
flap.putBoolean("FlapInward", pair.getValue());
|
||||
flapsNBT.add(flap);
|
||||
}
|
||||
writeToClient.put("TriggerFlaps", flapsNBT);
|
||||
compound.put("TriggerFlaps", flapsNBT);
|
||||
flapsToSend.clear();
|
||||
}
|
||||
return writeToClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
super.readClientUpdate(tag);
|
||||
if (tag.contains("TriggerFlaps")) {
|
||||
ListNBT flapsNBT = tag.getList("TriggerFlaps", NBT.TAG_COMPOUND);
|
||||
for (INBT inbt : flapsNBT) {
|
||||
CompoundNBT flap = (CompoundNBT) inbt;
|
||||
Direction side = Direction.byIndex(flap.getInt("Flap"));
|
||||
flap(side, flap.getBoolean("FlapInward"));
|
||||
}
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
Set<Direction> newFlaps = new HashSet<>(6);
|
||||
ListNBT flapsNBT = compound.getList("Flaps", NBT.TAG_INT);
|
||||
for (INBT inbt : flapsNBT)
|
||||
if (inbt instanceof IntNBT)
|
||||
newFlaps.add(Direction.byIndex(((IntNBT) inbt).getInt()));
|
||||
|
||||
for (Direction d : Iterate.directions)
|
||||
if (!newFlaps.contains(d))
|
||||
flaps.remove(d);
|
||||
else if (!flaps.containsKey(d))
|
||||
flaps.put(d, new InterpolatedChasingValue().start(.25f)
|
||||
.target(0)
|
||||
.withSpeed(.05f));
|
||||
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (!compound.contains("TriggerFlaps"))
|
||||
return;
|
||||
flapsNBT = compound.getList("TriggerFlaps", NBT.TAG_COMPOUND);
|
||||
for (INBT inbt : flapsNBT) {
|
||||
CompoundNBT flap = (CompoundNBT) inbt;
|
||||
Direction side = Direction.byIndex(flap.getInt("Flap"));
|
||||
flap(side, flap.getBoolean("FlapInward"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,12 +127,12 @@ public class BeltTunnelTileEntity extends SmartTileEntity {
|
|||
if (!positive && shape == Shape.T_RIGHT)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
BlockState funnelState = world.getBlockState(getPos().offset(direction));
|
||||
if (funnelState.getBlock() instanceof BeltFunnelBlock)
|
||||
if (funnelState.getBlock() instanceof BeltFunnelBlock)
|
||||
if (funnelState.get(BeltFunnelBlock.HORIZONTAL_FACING) == direction.getOpposite())
|
||||
continue;
|
||||
|
||||
|
||||
flaps.put(direction, new InterpolatedChasingValue().start(.25f)
|
||||
.target(0)
|
||||
.withSpeed(.05f));
|
||||
|
|
|
@ -328,7 +328,7 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("ConnectedLeft", connectedLeft);
|
||||
compound.putBoolean("ConnectedRight", connectedRight);
|
||||
|
||||
|
@ -344,14 +344,16 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity {
|
|||
return nbt;
|
||||
}));
|
||||
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
boolean wasConnectedLeft = connectedLeft;
|
||||
boolean wasConnectedRight = connectedRight;
|
||||
|
||||
connectedLeft = compound.getBoolean("ConnectedLeft");
|
||||
connectedRight = compound.getBoolean("ConnectedRight");
|
||||
|
||||
stackToDistribute = ItemStack.read(compound.getCompound("StackToDistribute"));
|
||||
distributionProgress = compound.getFloat("DistributionProgress");
|
||||
distributionDistanceLeft = compound.getInt("DistanceLeft");
|
||||
|
@ -362,14 +364,10 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity {
|
|||
return Pair.of(pos, face);
|
||||
});
|
||||
|
||||
super.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
boolean wasConnectedLeft = connectedLeft;
|
||||
boolean wasConnectedRight = connectedRight;
|
||||
super.readClientUpdate(tag);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
if (wasConnectedLeft != connectedLeft || wasConnectedRight != connectedRight) {
|
||||
requestModelDataUpdate();
|
||||
if (hasWorld())
|
||||
|
|
|
@ -277,22 +277,22 @@ public class ChuteTileEntity extends SmartTileEntity implements IHaveGoggleInfor
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.put("Item", item.serializeNBT());
|
||||
compound.putFloat("ItemPosition", itemPosition.value);
|
||||
compound.putFloat("Pull", pull);
|
||||
compound.putFloat("Push", push);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
ItemStack previousItem = item;
|
||||
item = ItemStack.read(compound.getCompound("Item"));
|
||||
itemPosition.lastValue = itemPosition.value = compound.getFloat("ItemPosition");
|
||||
pull = compound.getFloat("Pull");
|
||||
push = compound.getFloat("Push");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
if (hasWorld() && world.isRemote && !previousItem.equals(item, false) && !item.isEmpty()) {
|
||||
if (world.rand.nextInt(3) != 0)
|
||||
|
|
|
@ -93,20 +93,20 @@ public class DepotTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (heldItem != null)
|
||||
compound.put("HeldItem", heldItem.serializeNBT());
|
||||
compound.put("OutputBuffer", processingOutputBuffer.serializeNBT());
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
heldItem = null;
|
||||
if (compound.contains("HeldItem"))
|
||||
heldItem = TransportedItemStack.read(compound.getCompound("HeldItem"));
|
||||
processingOutputBuffer.deserializeNBT(compound.getCompound("OutputBuffer"));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,17 +43,17 @@ public class AdjustableRepeaterTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
state = compound.getInt("State");
|
||||
charging = compound.getBoolean("Charging");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("State", state);
|
||||
compound.putBoolean("Charging", charging);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
private int step(StepContext context) {
|
||||
|
|
|
@ -218,23 +218,23 @@ public class FunnelTileEntity extends SmartTileEntity {
|
|||
return getBlockState().getBlock() instanceof BeltFunnelBlock
|
||||
&& getBlockState().get(BeltFunnelBlock.SHAPE) == Shape.RETRACTED;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (sendFlap != 0) {
|
||||
protected void write(CompoundNBT compound, boolean clientPacket) {
|
||||
super.write(compound, clientPacket);
|
||||
if (clientPacket && sendFlap != 0) {
|
||||
compound.putInt("Flap", sendFlap);
|
||||
sendFlap = 0;
|
||||
}
|
||||
return super.writeToClient(compound);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
if (tag.contains("Flap")) {
|
||||
int direction = tag.getInt("Flap");
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
super.read(compound, clientPacket);
|
||||
if (clientPacket && compound.contains("Flap")) {
|
||||
int direction = compound.getInt("Flap");
|
||||
flap.set(direction);
|
||||
}
|
||||
super.readClientUpdate(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -142,18 +142,18 @@ public class AdjustableCrateTileEntity extends CrateTileEntity implements INamed
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Main", true);
|
||||
compound.putInt("AllowedAmount", allowedAmount);
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
allowedAmount = compound.getInt("AllowedAmount");
|
||||
inventory.deserializeNBT(compound.getCompound("Inventory"));
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -272,8 +272,8 @@ public class ArmTileEntity extends KineticTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
super.write(compound);
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
super.write(compound, clientPacket);
|
||||
|
||||
ListNBT pointsNBT = new ListNBT();
|
||||
inputs.stream()
|
||||
|
@ -288,32 +288,24 @@ public class ArmTileEntity extends KineticTileEntity {
|
|||
compound.put("HeldItem", heldItem.serializeNBT());
|
||||
compound.putInt("TargetPointIndex", chasedPointIndex);
|
||||
compound.putFloat("MovementProgress", chasedPointProgress);
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
super.writeToClient(compound);
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
super.read(compound);
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
int previousIndex = chasedPointIndex;
|
||||
Phase previousPhase = phase;
|
||||
ListNBT interactionPointTagBefore = interactionPointTag;
|
||||
|
||||
super.read(compound, clientPacket);
|
||||
heldItem = ItemStack.read(compound.getCompound("HeldItem"));
|
||||
phase = NBTHelper.readEnum(compound, "Phase", Phase.class);
|
||||
chasedPointIndex = compound.getInt("TargetPointIndex");
|
||||
chasedPointProgress = compound.getFloat("MovementProgress");
|
||||
interactionPointTag = compound.getList("InteractionPoints", NBT.TAG_COMPOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT tag) {
|
||||
int previousIndex = chasedPointIndex;
|
||||
Phase previousPhase = phase;
|
||||
ListNBT interactionPointTagBefore = interactionPointTag;
|
||||
super.readClientUpdate(tag);
|
||||
|
||||
|
||||
if (!clientPacket)
|
||||
return;
|
||||
|
||||
boolean ceiling = isOnCeiling();
|
||||
if (interactionPointTagBefore == null || interactionPointTagBefore.size() != interactionPointTag.size())
|
||||
updateInteractionPoints = true;
|
||||
|
|
|
@ -23,18 +23,18 @@ public class AnalogLeverTileEntity extends SmartTileEntity implements IHaveGoggl
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putInt("State", state);
|
||||
compound.putInt("ChangeTimer", lastChange);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
state = compound.getInt("State");
|
||||
lastChange = compound.getInt("ChangeTimer");
|
||||
clientState.target(state);
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -64,18 +64,18 @@ public class RedstoneLinkTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putBoolean("Transmitter", transmitter);
|
||||
compound.putInt("Receive", getReceivedSignal());
|
||||
compound.putBoolean("ReceivedChanged", receivedSignalChanged);
|
||||
compound.putInt("Transmit", transmittedSignal);
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
transmitter = compound.getBoolean("Transmitter");
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
|
||||
receivedSignal = compound.getInt("Receive");
|
||||
receivedSignalChanged = compound.getBoolean("ReceivedChanged");
|
||||
|
|
|
@ -36,25 +36,23 @@ public class StockpileSwitchTileEntity extends SmartTileEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
onWhenAbove = compound.getFloat("OnAbove");
|
||||
offWhenBelow = compound.getFloat("OffBelow");
|
||||
currentLevel = compound.getFloat("Current");
|
||||
powered = compound.getBoolean("Powered");
|
||||
|
||||
super.read(compound);
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
compound.putFloat("OnAbove", onWhenAbove);
|
||||
compound.putFloat("OffBelow", offWhenBelow);
|
||||
compound.putFloat("Current", currentLevel);
|
||||
compound.putBoolean("Powered", powered);
|
||||
|
||||
return super.write(compound);
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
public float getLevel() {
|
||||
|
|
|
@ -6,7 +6,6 @@ import static net.minecraft.util.text.TextFormatting.GRAY;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.simibubi.create.content.logistics.item.filter.FilterScreenPacket.Option;
|
||||
import com.simibubi.create.foundation.gui.AbstractSimiContainerScreen;
|
||||
import com.simibubi.create.foundation.gui.AllGuiTextures;
|
||||
|
@ -20,7 +19,6 @@ import com.simibubi.create.foundation.item.TooltipHelper;
|
|||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
|
||||
import net.minecraft.client.gui.widget.Widget;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
|
|
@ -7,9 +7,9 @@ import com.simibubi.create.AllBlocks;
|
|||
import com.simibubi.create.AllItems;
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltPart;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltSlope;
|
||||
import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity;
|
||||
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
|
||||
import com.simibubi.create.content.schematics.ItemRequirement;
|
||||
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
|
||||
|
@ -162,19 +162,13 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
inventory.deserializeNBT(compound.getCompound("Inventory"));
|
||||
|
||||
if (compound.contains("CurrentPos"))
|
||||
currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos"));
|
||||
|
||||
readClientUpdate(compound);
|
||||
super.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readClientUpdate(CompoundNBT compound) {
|
||||
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
if (!clientPacket) {
|
||||
inventory.deserializeNBT(compound.getCompound("Inventory"));
|
||||
if (compound.contains("CurrentPos"))
|
||||
currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos"));
|
||||
}
|
||||
|
||||
// Gui information
|
||||
statusMsg = compound.getString("Status");
|
||||
schematicProgress = compound.getFloat("Progress");
|
||||
|
@ -184,23 +178,24 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
|
|||
blocksPlaced = compound.getInt("AmountPlaced");
|
||||
blocksToPlace = compound.getInt("AmountToPlace");
|
||||
printingEntityIndex = compound.getInt("EntityProgress");
|
||||
|
||||
|
||||
missingItem = null;
|
||||
if (compound.contains("MissingItem"))
|
||||
missingItem = ItemStack.read(compound.getCompound("MissingItem"));
|
||||
|
||||
|
||||
// Settings
|
||||
CompoundNBT options = compound.getCompound("Options");
|
||||
replaceMode = options.getInt("ReplaceMode");
|
||||
skipMissing = options.getBoolean("SkipMissing");
|
||||
replaceTileEntities = options.getBoolean("ReplaceTileEntities");
|
||||
|
||||
|
||||
// Printer & Flying Blocks
|
||||
if (compound.contains("Target"))
|
||||
target = NBTUtil.readBlockPos(compound.getCompound("Target"));
|
||||
if (compound.contains("FlyingBlocks"))
|
||||
readFlyingBlocks(compound);
|
||||
|
||||
super.read(compound, clientPacket);
|
||||
}
|
||||
|
||||
protected void readFlyingBlocks(CompoundNBT compound) {
|
||||
|
@ -239,22 +234,16 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
|
||||
if (state == State.RUNNING) {
|
||||
compound.putBoolean("Running", true);
|
||||
if (currentPos != null)
|
||||
compound.put("CurrentPos", NBTUtil.writeBlockPos(currentPos));
|
||||
public void write(CompoundNBT compound, boolean clientPacket) {
|
||||
if (!clientPacket) {
|
||||
compound.put("Inventory", inventory.serializeNBT());
|
||||
if (state == State.RUNNING) {
|
||||
compound.putBoolean("Running", true);
|
||||
if (currentPos != null)
|
||||
compound.put("CurrentPos", NBTUtil.writeBlockPos(currentPos));
|
||||
}
|
||||
}
|
||||
|
||||
writeToClient(compound);
|
||||
return super.write(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
|
||||
|
||||
// Gui information
|
||||
compound.putFloat("Progress", schematicProgress);
|
||||
compound.putFloat("PaperProgress", bookPrintingProgress);
|
||||
|
@ -264,17 +253,17 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
|
|||
compound.putInt("AmountPlaced", blocksPlaced);
|
||||
compound.putInt("AmountToPlace", blocksToPlace);
|
||||
compound.putInt("EntityProgress", printingEntityIndex);
|
||||
|
||||
|
||||
if (missingItem != null)
|
||||
compound.put("MissingItem", missingItem.serializeNBT());
|
||||
|
||||
|
||||
// Settings
|
||||
CompoundNBT options = new CompoundNBT();
|
||||
options.putInt("ReplaceMode", replaceMode);
|
||||
options.putBoolean("SkipMissing", skipMissing);
|
||||
options.putBoolean("ReplaceTileEntities", replaceTileEntities);
|
||||
compound.put("Options", options);
|
||||
|
||||
|
||||
// Printer & Flying Blocks
|
||||
if (target != null)
|
||||
compound.put("Target", NBTUtil.writeBlockPos(target));
|
||||
|
@ -283,7 +272,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
|
|||
tagBlocks.add(b.serializeNBT());
|
||||
compound.put("FlyingBlocks", tagBlocks);
|
||||
|
||||
return compound;
|
||||
super.write(compound, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@ public class CFluids extends ConfigBase {
|
|||
|
||||
public ConfigInt fluidTankCapacity = i(8, 1, "fluidTankCapacity", Comments.buckets, Comments.fluidTankCapacity);
|
||||
public ConfigInt fluidTankMaxHeight = i(32, 1, "fluidTankMaxHeight", Comments.blocks, Comments.fluidTankMaxHeight);
|
||||
public ConfigInt mechanicalPumpRange = i(16, 1, "mechanicalPumpRange", Comments.blocks, Comments.mechanicalPumpRange);
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -15,6 +16,7 @@ public class CFluids extends ConfigBase {
|
|||
static String buckets = "[in Buckets]";
|
||||
static String fluidTankCapacity = "The amount of liquid a tank can hold per block.";
|
||||
static String fluidTankMaxHeight = "The maximum height a fluid tank can reach.";
|
||||
static String mechanicalPumpRange = "The maximum distance a mechanical pump can push or pull liquids on either side.";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package com.simibubi.create.foundation.fluid;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.ForgeFlowingFluid;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
|
@ -13,6 +16,34 @@ public class FluidHelper {
|
|||
ITEM_TO_TANK, TANK_TO_ITEM;
|
||||
}
|
||||
|
||||
public static boolean isWater(Fluid fluid) {
|
||||
return convertToStill(fluid) == Fluids.WATER;
|
||||
}
|
||||
|
||||
public static boolean isLava(Fluid fluid) {
|
||||
return convertToStill(fluid) == Fluids.LAVA;
|
||||
}
|
||||
|
||||
public static Fluid convertToFlowing(Fluid fluid) {
|
||||
if (fluid == Fluids.WATER)
|
||||
return Fluids.FLOWING_WATER;
|
||||
if (fluid == Fluids.LAVA)
|
||||
return Fluids.FLOWING_LAVA;
|
||||
if (fluid instanceof ForgeFlowingFluid)
|
||||
return ((ForgeFlowingFluid) fluid).getFlowingFluid();
|
||||
return fluid;
|
||||
}
|
||||
|
||||
public static Fluid convertToStill(Fluid fluid) {
|
||||
if (fluid == Fluids.FLOWING_WATER)
|
||||
return Fluids.WATER;
|
||||
if (fluid == Fluids.FLOWING_LAVA)
|
||||
return Fluids.LAVA;
|
||||
if (fluid instanceof ForgeFlowingFluid)
|
||||
return ((ForgeFlowingFluid) fluid).getStillFluid();
|
||||
return fluid;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static FluidExchange exchange(IFluidHandler fluidTank, IFluidHandlerItem fluidItem, FluidExchange preferred,
|
||||
int maxAmount) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.simibubi.create.foundation.fluid;
|
|||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.matrix.MatrixStack.Entry;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import com.simibubi.create.foundation.utility.ColorHelper;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.MatrixStacker;
|
||||
|
||||
|
@ -62,8 +63,9 @@ public class FluidRenderer {
|
|||
.translateBack(center);
|
||||
|
||||
boolean X = side.getAxis() == Axis.X;
|
||||
int darkColor = ColorHelper.mixColors(color, 0xff000011, 1/4f);
|
||||
renderTiledHorizontalFace(X ? xMax : zMax, side, X ? zMin : xMin, yMin, X ? zMax : xMax, yMax, builder,
|
||||
ms, light, color, fluidTexture);
|
||||
ms, light, darkColor, fluidTexture);
|
||||
|
||||
ms.pop();
|
||||
continue;
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package com.simibubi.create.foundation.tileEntity;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public abstract class PosBoundSmartTileEntity extends SmartTileEntity {
|
||||
|
||||
private boolean newPositionVisited;
|
||||
|
||||
public PosBoundSmartTileEntity(TileEntityType<?> tileEntityTypeIn) {
|
||||
super(tileEntityTypeIn);
|
||||
newPositionVisited = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (!world.isRemote && newPositionVisited) {
|
||||
newPositionVisited = false;
|
||||
initInNewPosition();
|
||||
}
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
long positionInTag = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z")).toLong();
|
||||
long positionKey = compound.getLong("BoundPosition");
|
||||
|
||||
newPositionVisited = false;
|
||||
if (!hasWorld() || !world.isRemote) {
|
||||
if (positionInTag != positionKey) {
|
||||
removePositionDependentData(compound);
|
||||
newPositionVisited = true;
|
||||
}
|
||||
}
|
||||
|
||||
super.read(compound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-only. When this TE realizes, that it's reading its tag in a different
|
||||
* position, it should remove all position dependent information here.
|
||||
*
|
||||
* @param nbt
|
||||
*/
|
||||
protected void removePositionDependentData(CompoundNBT nbt) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-only. When a TE has been created or moved, it will call this before the
|
||||
* regular init.
|
||||
*/
|
||||
protected void initInNewPosition() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -38,8 +38,7 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka
|
|||
* Gets called just before reading tile data for behaviours. Register anything
|
||||
* here that depends on your custom te data.
|
||||
*/
|
||||
public void addBehavioursDeferred(List<TileEntityBehaviour> behaviours) {
|
||||
}
|
||||
public void addBehavioursDeferred(List<TileEntityBehaviour> behaviours) {}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
|
@ -53,44 +52,61 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka
|
|||
lazyTick();
|
||||
}
|
||||
|
||||
behaviours.values().forEach(TileEntityBehaviour::tick);
|
||||
behaviours.values()
|
||||
.forEach(TileEntityBehaviour::tick);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
behaviours.values().forEach(TileEntityBehaviour::initialize);
|
||||
behaviours.values()
|
||||
.forEach(TileEntityBehaviour::initialize);
|
||||
lazyTick();
|
||||
}
|
||||
|
||||
public void updateClient(CompoundNBT compound) {
|
||||
behaviours.values().forEach(tb -> tb.updateClient(compound));
|
||||
@Override
|
||||
public final CompoundNBT write(CompoundNBT compound) {
|
||||
write(compound, false);
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
behaviours.values().forEach(tb -> tb.writeNBT(compound));
|
||||
return super.write(compound);
|
||||
public final CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
write(compound, true);
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void readClientUpdate(CompoundNBT tag) {
|
||||
read(tag, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
behaviours.values().forEach(tb -> tb.writeToClient(compound));
|
||||
return super.writeToClient(compound);
|
||||
public final void read(CompoundNBT tag) {
|
||||
read(tag, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
/**
|
||||
* Hook only these in future subclasses of STE
|
||||
*/
|
||||
protected void read(CompoundNBT compound, boolean clientPacket) {
|
||||
if (firstNbtRead) {
|
||||
firstNbtRead = false;
|
||||
ArrayList<TileEntityBehaviour> list = new ArrayList<>();
|
||||
addBehavioursDeferred(list);
|
||||
list.forEach(b -> behaviours.put(b.getType(), b));
|
||||
}
|
||||
|
||||
super.read(compound);
|
||||
forEachBehaviour(tb -> tb.readNBT(compound));
|
||||
|
||||
if (world != null && world.isRemote)
|
||||
updateClient(compound);
|
||||
behaviours.values()
|
||||
.forEach(tb -> tb.read(compound, clientPacket));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook only these in future subclasses of STE
|
||||
*/
|
||||
protected void write(CompoundNBT compound, boolean clientPacket) {
|
||||
super.write(compound);
|
||||
behaviours.values()
|
||||
.forEach(tb -> tb.write(compound, clientPacket));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,12 +123,13 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka
|
|||
public void lazyTick() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void forEachBehaviour(Consumer<TileEntityBehaviour> action) {
|
||||
behaviours.values().forEach(tb -> {
|
||||
if (!tb.isPaused())
|
||||
action.accept(tb);
|
||||
});
|
||||
behaviours.values()
|
||||
.forEach(tb -> {
|
||||
if (!tb.isPaused())
|
||||
action.accept(tb);
|
||||
});
|
||||
}
|
||||
|
||||
protected void putBehaviour(TileEntityBehaviour behaviour) {
|
||||
|
|
|
@ -37,15 +37,11 @@ public abstract class TileEntityBehaviour {
|
|||
|
||||
}
|
||||
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
|
||||
}
|
||||
|
||||
public void updateClient(CompoundNBT nbt) {
|
||||
|
||||
}
|
||||
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -111,8 +107,4 @@ public abstract class TileEntityBehaviour {
|
|||
return ste.getBehaviour(type);
|
||||
}
|
||||
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
return compound;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,30 +52,26 @@ public class FilteringBehaviour extends TileEntityBehaviour {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
nbt.put("Filter", getFilter().serializeNBT());
|
||||
nbt.putInt("FilterAmount", count);
|
||||
super.writeNBT(nbt);
|
||||
|
||||
if (clientPacket && forceClientState) {
|
||||
nbt.putBoolean("ForceScrollable", true);
|
||||
forceClientState = false;
|
||||
}
|
||||
super.write(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
filter = ItemStack.read(nbt.getCompound("Filter"));
|
||||
count = nbt.getInt("FilterAmount");
|
||||
if (nbt.contains("ForceScrollable")) {
|
||||
scrollableValue = count;
|
||||
ticksUntilScrollPacket = -1;
|
||||
}
|
||||
super.readNBT(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (forceClientState) {
|
||||
compound.putBoolean("ForceScrollable", true);
|
||||
forceClientState = false;
|
||||
}
|
||||
return super.writeToClient(compound);
|
||||
super.read(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,12 +35,12 @@ public class SidedFilteringBehaviour extends FilteringBehaviour {
|
|||
sidedFilters = new IdentityHashMap<>();
|
||||
updateFilterPresence();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
|
||||
public FilteringBehaviour get(Direction side) {
|
||||
return sidedFilters.get(side);
|
||||
}
|
||||
|
@ -59,40 +59,27 @@ public class SidedFilteringBehaviour extends FilteringBehaviour {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
nbt.put("Filters", NBTHelper.writeCompoundList(sidedFilters.entrySet(), entry -> {
|
||||
CompoundNBT compound = new CompoundNBT();
|
||||
compound.putInt("Side", entry.getKey()
|
||||
.getIndex());
|
||||
entry.getValue()
|
||||
.writeNBT(compound);
|
||||
.write(compound, clientPacket);
|
||||
return compound;
|
||||
}));
|
||||
super.writeNBT(nbt);
|
||||
super.write(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
NBTHelper.iterateCompoundList(nbt.getList("Filters", NBT.TAG_COMPOUND), compound -> {
|
||||
Direction face = Direction.byIndex(compound.getInt("Side"));
|
||||
if (sidedFilters.containsKey(face))
|
||||
sidedFilters.get(face)
|
||||
.readNBT(compound);
|
||||
.read(compound, clientPacket);
|
||||
});
|
||||
super.readNBT(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT nbt) {
|
||||
nbt.put("Filters", NBTHelper.writeCompoundList(sidedFilters.entrySet(), entry -> {
|
||||
CompoundNBT compound = new CompoundNBT();
|
||||
compound.putInt("Side", entry.getKey()
|
||||
.getIndex());
|
||||
entry.getValue()
|
||||
.writeToClient(compound);
|
||||
return compound;
|
||||
}));
|
||||
return super.writeToClient(nbt);
|
||||
super.read(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,15 +33,15 @@ public class SingleTargetAutoExtractingBehaviour extends AutoExtractingBehaviour
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
nbt.putBoolean("Advantage", advantageOnNextSync);
|
||||
super.writeNBT(nbt);
|
||||
super.write(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
advantageOnNextSync = nbt.getBoolean("Advantage");
|
||||
super.readNBT(nbt);
|
||||
super.read(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -116,26 +116,26 @@ public class LinkBehaviour extends TileEntityBehaviour {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT compound) {
|
||||
super.writeNBT(compound);
|
||||
compound.put("FrequencyFirst", frequencyFirst.getStack()
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
super.write(nbt, clientPacket);
|
||||
nbt.put("FrequencyFirst", frequencyFirst.getStack()
|
||||
.write(new CompoundNBT()));
|
||||
compound.put("FrequencyLast", frequencyLast.getStack()
|
||||
nbt.put("FrequencyLast", frequencyLast.getStack()
|
||||
.write(new CompoundNBT()));
|
||||
compound.putLong("LastKnownPosition", tileEntity.getPos()
|
||||
nbt.putLong("LastKnownPosition", tileEntity.getPos()
|
||||
.toLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT compound) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
long positionInTag = tileEntity.getPos()
|
||||
.toLong();
|
||||
long positionKey = compound.getLong("LastKnownPosition");
|
||||
long positionKey = nbt.getLong("LastKnownPosition");
|
||||
newPosition = positionInTag != positionKey;
|
||||
|
||||
super.readNBT(compound);
|
||||
frequencyFirst = new Frequency(ItemStack.read(compound.getCompound("FrequencyFirst")));
|
||||
frequencyLast = new Frequency(ItemStack.read(compound.getCompound("FrequencyLast")));
|
||||
super.read(nbt, clientPacket);
|
||||
frequencyFirst = new Frequency(ItemStack.read(nbt.getCompound("FrequencyFirst")));
|
||||
frequencyLast = new Frequency(ItemStack.read(nbt.getCompound("FrequencyLast")));
|
||||
}
|
||||
|
||||
public void setFrequency(boolean first, ItemStack stack) {
|
||||
|
|
|
@ -51,28 +51,23 @@ public class ScrollValueBehaviour extends TileEntityBehaviour {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
nbt.putInt("ScrollValue", value);
|
||||
super.writeNBT(nbt);
|
||||
if (clientPacket && forceClientState) {
|
||||
nbt.putBoolean("ForceScrollable", true);
|
||||
forceClientState = false;
|
||||
}
|
||||
super.write(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
value = nbt.getInt("ScrollValue");
|
||||
if (nbt.contains("ForceScrollable")) {
|
||||
ticksUntilScrollPacket = -1;
|
||||
scrollableValue = value;
|
||||
}
|
||||
super.readNBT(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT writeToClient(CompoundNBT compound) {
|
||||
if (forceClientState) {
|
||||
compound.putBoolean("ForceScrollable", true);
|
||||
forceClientState = false;
|
||||
}
|
||||
return super.writeToClient(compound);
|
||||
super.read(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,15 +21,15 @@ public class DeferralBehaviour extends TileEntityBehaviour {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeNBT(CompoundNBT nbt) {
|
||||
public void write(CompoundNBT nbt, boolean clientPacket) {
|
||||
nbt.putBoolean("NeedsUpdate", needsUpdate);
|
||||
super.writeNBT(nbt);
|
||||
super.write(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(CompoundNBT nbt) {
|
||||
public void read(CompoundNBT nbt, boolean clientPacket) {
|
||||
needsUpdate = nbt.getBoolean("NeedsUpdate");
|
||||
super.readNBT(nbt);
|
||||
super.read(nbt, clientPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
package com.simibubi.create.foundation.utility;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
public class AngleHelper {
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void applyRotation(Direction direction, MatrixStack ms) {
|
||||
ms.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(AngleHelper.horizontalAngle(direction)));
|
||||
ms.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(AngleHelper.verticalAngle(direction)));
|
||||
}
|
||||
|
||||
public static float horizontalAngle(Direction facing) {
|
||||
float angle = facing.getHorizontalAngle();
|
||||
|
@ -39,8 +28,8 @@ public class AngleHelper {
|
|||
return (float) (angle * 180 / Math.PI);
|
||||
}
|
||||
|
||||
public static float angleLerp(float pct, float current, float target) {
|
||||
return current + getShortestAngleDiff(current, target) * pct;
|
||||
public static float angleLerp(double pct, double current, double target) {
|
||||
return (float) (current + getShortestAngleDiff(current, target) * pct);
|
||||
}
|
||||
|
||||
public static float getShortestAngleDiff(double current, double target) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package com.simibubi.create.foundation.utility;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class BlockFace extends Pair<BlockPos, Direction> {
|
||||
|
||||
public BlockFace(BlockPos first, Direction second) {
|
||||
super(first, second);
|
||||
}
|
||||
|
||||
public boolean isEquivalent(BlockFace other) {
|
||||
if (equals(other))
|
||||
return true;
|
||||
return getConnectedPos().equals(other.getPos()) && getPos().equals(other.getConnectedPos());
|
||||
}
|
||||
|
||||
public BlockPos getPos() {
|
||||
return getFirst();
|
||||
}
|
||||
|
||||
public Direction getFace() {
|
||||
return getSecond();
|
||||
}
|
||||
|
||||
public Direction getOppositeFace() {
|
||||
return getSecond().getOpposite();
|
||||
}
|
||||
|
||||
public BlockFace getOpposite() {
|
||||
return new BlockFace(getConnectedPos(), getOppositeFace());
|
||||
}
|
||||
|
||||
public BlockPos getConnectedPos() {
|
||||
return getPos().offset(getFace());
|
||||
}
|
||||
|
||||
public CompoundNBT serializeNBT() {
|
||||
CompoundNBT compoundNBT = new CompoundNBT();
|
||||
compoundNBT.put("Pos", NBTUtil.writeBlockPos(getPos()));
|
||||
NBTHelper.writeEnum(compoundNBT, "Face", getFace());
|
||||
return compoundNBT;
|
||||
}
|
||||
|
||||
public static BlockFace fromNBT(CompoundNBT compound) {
|
||||
return new BlockFace(NBTUtil.readBlockPos(compound.getCompound("Pos")),
|
||||
NBTHelper.readEnum(compound, "Face", Direction.class));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +1,49 @@
|
|||
package com.simibubi.create.foundation.utility;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
|
||||
public class Couple<T> extends Pair<T, T> {
|
||||
|
||||
private static Couple<Boolean> TRUE_AND_FALSE = Couple.create(true, false);
|
||||
|
||||
|
||||
protected Couple(T first, T second) {
|
||||
super(first, second);
|
||||
}
|
||||
|
||||
|
||||
public static <T> Couple<T> create(T first, T second) {
|
||||
return new Couple<>(first, second);
|
||||
}
|
||||
|
||||
|
||||
public static <T> Couple<T> create(Supplier<T> factory) {
|
||||
return new Couple<>(factory.get(), factory.get());
|
||||
}
|
||||
|
||||
public T get(boolean first) {
|
||||
return first ? getFirst() : getSecond();
|
||||
}
|
||||
|
||||
|
||||
public void set(boolean first, T value) {
|
||||
if (first)
|
||||
setFirst(value);
|
||||
else
|
||||
setSecond(value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Couple<T> copy() {
|
||||
return create(first, second);
|
||||
}
|
||||
|
||||
|
||||
public <S> Couple<S> map(Function<T, S> function) {
|
||||
return Couple.create(function.apply(first), function.apply(second));
|
||||
}
|
||||
|
@ -41,11 +52,11 @@ public class Couple<T> extends Pair<T, T> {
|
|||
setFirst(function.apply(getFirst()));
|
||||
setSecond(function.apply(getSecond()));
|
||||
}
|
||||
|
||||
|
||||
public void replaceWithContext(BiFunction<T, Boolean, T> function) {
|
||||
replaceWithParams(function, TRUE_AND_FALSE);
|
||||
}
|
||||
|
||||
|
||||
public <S> void replaceWithParams(BiFunction<T, S, T> function, Couple<S> values) {
|
||||
setFirst(function.apply(getFirst(), values.getFirst()));
|
||||
setSecond(function.apply(getSecond(), values.getSecond()));
|
||||
|
@ -55,18 +66,27 @@ public class Couple<T> extends Pair<T, T> {
|
|||
consumer.accept(getFirst());
|
||||
consumer.accept(getSecond());
|
||||
}
|
||||
|
||||
|
||||
public void forEachWithContext(BiConsumer<T, Boolean> consumer) {
|
||||
forEachWithParams(consumer, TRUE_AND_FALSE);
|
||||
}
|
||||
|
||||
|
||||
public <S> void forEachWithParams(BiConsumer<T, S> function, Couple<S> values) {
|
||||
function.accept(getFirst(), values.getFirst());
|
||||
function.accept(getSecond(), values.getSecond());
|
||||
}
|
||||
|
||||
|
||||
public Couple<T> swap() {
|
||||
return Couple.create(second, first);
|
||||
}
|
||||
|
||||
|
||||
public ListNBT serializeEach(Function<T, CompoundNBT> serializer) {
|
||||
return NBTHelper.writeCompoundList(ImmutableList.of(first, second), serializer);
|
||||
}
|
||||
|
||||
public static <S> Couple<S> deserializeEach(ListNBT list, Function<CompoundNBT, S> deserializer) {
|
||||
List<S> readCompoundList = NBTHelper.readCompoundList(list, deserializer);
|
||||
return new Couple<>(readCompoundList.get(0), readCompoundList.get(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
package com.simibubi.create.foundation.utility;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
// Can replace all Interpolated value classes
|
||||
// InterpolatedChasingValue, InterpolatedValue, InterpolatedChasingAngle, InterpolatedAngle
|
||||
public class LerpedFloat {
|
||||
|
||||
Interpolater interpolater;
|
||||
float previousValue;
|
||||
float value;
|
||||
|
||||
Chaser chaseFunction;
|
||||
float chaseTarget;
|
||||
float chaseSpeed;
|
||||
|
||||
boolean forcedSync;
|
||||
|
||||
public LerpedFloat(Interpolater interpolater) {
|
||||
this.interpolater = interpolater;
|
||||
startWithValue(0);
|
||||
forcedSync = true;
|
||||
}
|
||||
|
||||
public static LerpedFloat linear() {
|
||||
return new LerpedFloat((p, c, t) -> (float) MathHelper.lerp(p, c, t));
|
||||
}
|
||||
|
||||
public static LerpedFloat angular() {
|
||||
return new LerpedFloat(AngleHelper::angleLerp);
|
||||
}
|
||||
|
||||
public LerpedFloat startWithValue(double value) {
|
||||
float f = (float) value;
|
||||
this.previousValue = f;
|
||||
this.chaseTarget = f;
|
||||
this.value = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LerpedFloat chase(double value, double speed, Chaser chaseFunction) {
|
||||
this.chaseTarget = (float) value;
|
||||
this.chaseSpeed = (float) speed;
|
||||
this.chaseFunction = chaseFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean updateChaseSpeed(double speed) {
|
||||
float prevSpeed = this.chaseSpeed;
|
||||
this.chaseSpeed = (float) speed;
|
||||
return !MathHelper.epsilonEquals(prevSpeed, speed);
|
||||
}
|
||||
|
||||
public void tickChaser() {
|
||||
previousValue = value;
|
||||
if (chaseFunction == null)
|
||||
return;
|
||||
if (MathHelper.epsilonEquals((double) value, chaseTarget)) {
|
||||
value = chaseTarget;
|
||||
return;
|
||||
}
|
||||
value = chaseFunction.chase(value, chaseSpeed, chaseTarget);
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.previousValue = this.value;
|
||||
this.value = (float) value;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return getValue(1);
|
||||
}
|
||||
|
||||
public float getValue(float partialTicks) {
|
||||
return MathHelper.lerp(partialTicks, previousValue, value);
|
||||
}
|
||||
|
||||
public float getChaseTarget() {
|
||||
return chaseTarget;
|
||||
}
|
||||
|
||||
public void forceNextSync() {
|
||||
forcedSync = true;
|
||||
}
|
||||
|
||||
public CompoundNBT writeNBT() {
|
||||
CompoundNBT compoundNBT = new CompoundNBT();
|
||||
compoundNBT.putFloat("Speed", chaseSpeed);
|
||||
compoundNBT.putFloat("Target", chaseTarget);
|
||||
compoundNBT.putFloat("Value", value);
|
||||
if (forcedSync)
|
||||
compoundNBT.putBoolean("Force", true);
|
||||
forcedSync = false;
|
||||
return compoundNBT;
|
||||
}
|
||||
|
||||
public void readNBT(CompoundNBT compoundNBT, boolean clientPacket) {
|
||||
if (!clientPacket || compoundNBT.contains("Force"))
|
||||
startWithValue(compoundNBT.getFloat("Value"));
|
||||
readChaser(compoundNBT);
|
||||
}
|
||||
|
||||
private void readChaser(CompoundNBT compoundNBT) {
|
||||
chaseSpeed = compoundNBT.getFloat("Speed");
|
||||
chaseTarget = compoundNBT.getFloat("Target");
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Interpolater {
|
||||
float interpolate(double progress, double current, double target);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Chaser {
|
||||
|
||||
public static final Chaser IDLE = (c, s, t) -> (float) c;
|
||||
public static final Chaser EXP = exp(Double.MAX_VALUE);
|
||||
public static final Chaser LINEAR = (c, s, t) -> (float) (c + MathHelper.clamp(t - c, -s, s));
|
||||
|
||||
public static Chaser exp(double maxEffectiveSpeed) {
|
||||
return (c, s, t) -> (float) (c + MathHelper.clamp((t - c) * s, -maxEffectiveSpeed, maxEffectiveSpeed));
|
||||
}
|
||||
|
||||
float chase(double current, double speed, double target);
|
||||
}
|
||||
|
||||
}
|
|
@ -68,10 +68,14 @@ public class VecHelper {
|
|||
vec.z + (r.nextFloat() - .5f) * 2 * radius);
|
||||
}
|
||||
|
||||
public static Vec3d planeByNormal(Vec3d vec) {
|
||||
public static Vec3d axisAlingedPlaneOf(Vec3d vec) {
|
||||
vec = vec.normalize();
|
||||
return new Vec3d(1, 1, 1).subtract(Math.abs(vec.x), Math.abs(vec.y), Math.abs(vec.z));
|
||||
}
|
||||
|
||||
public static Vec3d axisAlingedPlaneOf(Direction face) {
|
||||
return axisAlingedPlaneOf(new Vec3d(face.getDirectionVec()));
|
||||
}
|
||||
|
||||
public static ListNBT writeNBT(Vec3d vec) {
|
||||
ListNBT listnbt = new ListNBT();
|
||||
|
@ -114,12 +118,17 @@ public class VecHelper {
|
|||
.scale(maxLength) : vec;
|
||||
}
|
||||
|
||||
public static Vec3d clampComponentWise(Vec3d vec, float maxLength) {
|
||||
return new Vec3d(MathHelper.clamp(vec.x, -maxLength, maxLength), MathHelper.clamp(vec.y, -maxLength, maxLength),
|
||||
MathHelper.clamp(vec.z, -maxLength, maxLength));
|
||||
}
|
||||
|
||||
public static Vec3d project(Vec3d vec, Vec3d ontoVec) {
|
||||
if (ontoVec.equals(Vec3d.ZERO))
|
||||
return Vec3d.ZERO;
|
||||
return ontoVec.scale(vec.dotProduct(ontoVec) / ontoVec.lengthSquared());
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static Vec3d intersectSphere(Vec3d origin, Vec3d lineDirection, Vec3d sphereCenter, double radius) {
|
||||
if (lineDirection.equals(Vec3d.ZERO))
|
||||
|
|
|
@ -58,7 +58,7 @@ public class BlockClusterOutline extends Outline {
|
|||
|
||||
Vec3d center = VecHelper.getCenterOf(pos);
|
||||
Vec3d offset = new Vec3d(face.getDirectionVec());
|
||||
Vec3d plane = VecHelper.planeByNormal(offset);
|
||||
Vec3d plane = VecHelper.axisAlingedPlaneOf(offset);
|
||||
Axis axis = face.getAxis();
|
||||
|
||||
offset = offset.scale(1 / 2f + 1 / 64d);
|
||||
|
|
|
@ -61,7 +61,7 @@ public abstract class Outline {
|
|||
float lineWidth = params.getLineWidth();
|
||||
Vec3d extension = diff.normalize()
|
||||
.scale(lineWidth / 2);
|
||||
Vec3d plane = VecHelper.planeByNormal(diff);
|
||||
Vec3d plane = VecHelper.axisAlingedPlaneOf(diff);
|
||||
Direction face = Direction.getFacingFromVector(diff.x, diff.y, diff.z);
|
||||
Axis axis = face.getAxis();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.simibubi.create.foundation.utility.outliner;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -110,7 +111,7 @@ public class Outliner {
|
|||
// Maintenance
|
||||
|
||||
public Outliner() {
|
||||
outlines = new HashMap<>();
|
||||
outlines = Collections.synchronizedMap(new HashMap<>());
|
||||
}
|
||||
|
||||
public void tickOutlines() {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
"advancement.create.electron_tube": "Beep boop",
|
||||
"advancement.create.electron_tube.desc": "Make some Electron Tubes, useful in crafting less primitive machinery.",
|
||||
"advancement.create.mechanical_saw": "Stationary Chopping",
|
||||
"advancement.create.mechanical_saw.desc": "Place and power a Mechanical mechanical_saw",
|
||||
"advancement.create.mechanical_saw.desc": "Place and power a Mechanical Saw",
|
||||
"advancement.create.basin": "Basin Operation",
|
||||
"advancement.create.basin.desc": "Place a basin and try throwing items into it.",
|
||||
"advancement.create.mixer": "Mixin' it Up",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"death.attack.create.fan_fire": "%1$s was burned to death by hot air",
|
||||
"death.attack.create.fan_lava": "%1$s was burned to death by lava fan",
|
||||
"death.attack.create.mechanical_drill": "%1$s was impaled by Mechanical mechanical_drill",
|
||||
"death.attack.create.mechanical_saw": "%1$s got cut in half by Mechanical mechanical_saw",
|
||||
"death.attack.create.mechanical_saw": "%1$s got cut in half by Mechanical Saw",
|
||||
"death.attack.create.cuckoo_clock_explosion": "%1$s was blown up by tampered cuckoo clock",
|
||||
"create.block.deployer.damage_source_name": "a rogue Deployer",
|
||||
"create.block.cart_assembler.invalid": "Place your Cart Assembler on a rail block",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"create.recipe.pressing": "Pressing",
|
||||
"create.recipe.mixing": "Mixing",
|
||||
"create.recipe.packing": "Compacting",
|
||||
"create.recipe.mechanical_sawing": "mechanical_sawing",
|
||||
"create.recipe.mechanical_sawing": "Sawing",
|
||||
"create.recipe.mechanical_crafting": "Mechanical Crafting",
|
||||
"create.recipe.block_cutting": "Block Cutting",
|
||||
"create.recipe.blockzapper_upgrade": "Handheld Blockzapper",
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
"to": [12, 12, 12],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 8, 4, 12], "texture": "#3"},
|
||||
"east": {"uv": [0, 8, 4, 12], "texture": "#3"},
|
||||
"south": {"uv": [0, 8, 4, 12], "texture": "#3"},
|
||||
"west": {"uv": [0, 8, 4, 12], "texture": "#3"}
|
||||
"north": {"uv": [0, 6, 4, 10], "texture": "#3"},
|
||||
"east": {"uv": [0, 6, 4, 10], "texture": "#3"},
|
||||
"south": {"uv": [0, 6, 4, 10], "texture": "#3"},
|
||||
"west": {"uv": [0, 6, 4, 10], "texture": "#3"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -76,20 +76,6 @@
|
|||
"up": {"uv": [0, 6, 6, 8.5], "rotation": 180, "texture": "#5"},
|
||||
"down": {"uv": [0, 6, 6, 8.5], "texture": "#5"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "GearCaseOuter",
|
||||
"from": [4, 4, 4.5],
|
||||
"to": [12, 12, 11.5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 6.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 1, 5, 5], "rotation": 180, "texture": "#5"},
|
||||
"east": {"uv": [6, 4.5, 10, 8], "rotation": 270, "texture": "#5"},
|
||||
"south": {"uv": [1, 1, 5, 5], "texture": "#5"},
|
||||
"west": {"uv": [6, 4.5, 10, 8], "rotation": 90, "texture": "#5"},
|
||||
"up": {"uv": [6, 4.5, 10, 8], "rotation": 180, "texture": "#5"},
|
||||
"down": {"uv": [6, 4.5, 10, 8], "texture": "#5"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
|
@ -130,7 +116,7 @@
|
|||
{
|
||||
"name": "cogwheel",
|
||||
"origin": [8, 8, 8],
|
||||
"children": [0, 1, 2, 3, 4, 5]
|
||||
"children": [0, 1, 2, 3, 4]
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 148 B |
Loading…
Add table
Reference in a new issue