CreateMod/src/main/java/com/simibubi/create/content/contraptions/pulley/PulleyBlockEntity.java
2023-05-24 07:59:45 -07:00

383 lines
12 KiB
Java

package com.simibubi.create.content.contraptions.pulley;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.AssemblyException;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.content.contraptions.ContraptionCollider;
import com.simibubi.create.content.contraptions.ControlledContraptionEntity;
import com.simibubi.create.content.contraptions.piston.LinearActuatorBlockEntity;
import com.simibubi.create.content.redstone.thresholdSwitch.ThresholdSwitchObservable;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.CenteredSideValueBoxTransform;
import com.simibubi.create.foundation.blockEntity.behaviour.ValueBoxTransform;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class PulleyBlockEntity extends LinearActuatorBlockEntity implements ThresholdSwitchObservable {
protected int initialOffset;
private float prevAnimatedOffset;
protected BlockPos mirrorParent;
protected List<BlockPos> mirrorChildren;
public WeakReference<AbstractContraptionEntity> sharedMirrorContraption;
public PulleyBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
@Override
protected AABB createRenderBoundingBox() {
double expandY = -offset;
if (sharedMirrorContraption != null) {
AbstractContraptionEntity ace = sharedMirrorContraption.get();
if (ace != null)
expandY = ace.getY() - worldPosition.getY();
}
return super.createRenderBoundingBox().expandTowards(0, expandY, 0);
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
registerAwardables(behaviours, AllAdvancements.PULLEY_MAXED);
}
@Override
public void tick() {
float prevOffset = offset;
super.tick();
if (level.isClientSide() && mirrorParent != null)
if (sharedMirrorContraption == null || sharedMirrorContraption.get() == null
|| !sharedMirrorContraption.get()
.isAlive()) {
sharedMirrorContraption = null;
if (level.getBlockEntity(mirrorParent)instanceof PulleyBlockEntity pte && pte.movedContraption != null)
sharedMirrorContraption = new WeakReference<>(pte.movedContraption);
}
if (isVirtual())
prevAnimatedOffset = offset;
invalidateRenderBoundingBox();
if (prevOffset < 200 && offset >= 200)
award(AllAdvancements.PULLEY_MAXED);
}
@Override
protected boolean isPassive() {
return mirrorParent != null;
}
@Nullable
public AbstractContraptionEntity getAttachedContraption() {
return mirrorParent != null && sharedMirrorContraption != null ? sharedMirrorContraption.get()
: movedContraption;
}
@Override
protected void assemble() throws AssemblyException {
if (!(level.getBlockState(worldPosition)
.getBlock() instanceof PulleyBlock))
return;
if (speed == 0 && mirrorParent == null)
return;
int maxLength = AllConfigs.server().kinetics.maxRopeLength.get();
int i = 1;
while (i <= maxLength) {
BlockPos ropePos = worldPosition.below(i);
BlockState ropeState = level.getBlockState(ropePos);
if (!AllBlocks.ROPE.has(ropeState) && !AllBlocks.PULLEY_MAGNET.has(ropeState)) {
break;
}
++i;
}
offset = i - 1;
if (offset >= getExtensionRange() && getSpeed() > 0)
return;
if (offset <= 0 && getSpeed() < 0)
return;
// Collect Construct
if (!level.isClientSide && mirrorParent == null) {
needsContraption = false;
BlockPos anchor = worldPosition.below(Mth.floor(offset + 1));
initialOffset = Mth.floor(offset);
PulleyContraption contraption = new PulleyContraption(initialOffset);
boolean canAssembleStructure = contraption.assemble(level, anchor);
if (canAssembleStructure) {
Direction movementDirection = getSpeed() > 0 ? Direction.DOWN : Direction.UP;
if (ContraptionCollider.isCollidingWithWorld(level, contraption, anchor.relative(movementDirection),
movementDirection))
canAssembleStructure = false;
}
if (!canAssembleStructure && getSpeed() > 0)
return;
removeRopes();
if (!contraption.getBlocks()
.isEmpty()) {
contraption.removeBlocksFromWorld(level, BlockPos.ZERO);
movedContraption = ControlledContraptionEntity.create(level, this, contraption);
movedContraption.setPos(anchor.getX(), anchor.getY(), anchor.getZ());
level.addFreshEntity(movedContraption);
forceMove = true;
needsContraption = true;
if (contraption.containsBlockBreakers())
award(AllAdvancements.CONTRAPTION_ACTORS);
for (BlockPos pos : contraption.createColliders(level, Direction.UP)) {
if (pos.getY() != 0)
continue;
pos = pos.offset(anchor);
if (level.getBlockEntity(
new BlockPos(pos.getX(), worldPosition.getY(), pos.getZ())) instanceof PulleyBlockEntity pbe)
pbe.startMirroringOther(worldPosition);
}
}
}
if (mirrorParent != null)
removeRopes();
clientOffsetDiff = 0;
running = true;
sendData();
}
private void removeRopes() {
for (int i = ((int) offset); i > 0; i--) {
BlockPos offset = worldPosition.below(i);
BlockState oldState = level.getBlockState(offset);
level.setBlock(offset, oldState.getFluidState()
.createLegacyBlock(), 66);
}
}
@Override
public void disassemble() {
if (!running && movedContraption == null && mirrorParent == null)
return;
offset = getGridOffset(offset);
if (movedContraption != null)
resetContraptionToOffset();
if (!level.isClientSide) {
if (shouldCreateRopes()) {
if (offset > 0) {
BlockPos magnetPos = worldPosition.below((int) offset);
FluidState ifluidstate = level.getFluidState(magnetPos);
level.destroyBlock(magnetPos, level.getBlockState(magnetPos)
.getCollisionShape(level, magnetPos)
.isEmpty());
level.setBlock(magnetPos, AllBlocks.PULLEY_MAGNET.getDefaultState()
.setValue(BlockStateProperties.WATERLOGGED,
Boolean.valueOf(ifluidstate.getType() == Fluids.WATER)),
66);
}
boolean[] waterlog = new boolean[(int) offset];
for (int i = 1; i <= ((int) offset) - 1; i++) {
BlockPos ropePos = worldPosition.below(i);
FluidState ifluidstate = level.getFluidState(ropePos);
waterlog[i] = ifluidstate.getType() == Fluids.WATER;
level.destroyBlock(ropePos, level.getBlockState(ropePos)
.getCollisionShape(level, ropePos)
.isEmpty());
}
for (int i = 1; i <= ((int) offset) - 1; i++)
level.setBlock(worldPosition.below(i), AllBlocks.ROPE.getDefaultState()
.setValue(BlockStateProperties.WATERLOGGED, waterlog[i]), 66);
}
if (movedContraption != null && mirrorParent == null)
movedContraption.disassemble();
notifyMirrorsOfDisassembly();
}
if (movedContraption != null)
movedContraption.discard();
movedContraption = null;
initialOffset = 0;
running = false;
sendData();
}
protected boolean shouldCreateRopes() {
return !remove;
}
@Override
protected Vec3 toPosition(float offset) {
if (movedContraption.getContraption() instanceof PulleyContraption) {
PulleyContraption contraption = (PulleyContraption) movedContraption.getContraption();
return Vec3.atLowerCornerOf(contraption.anchor)
.add(0, contraption.getInitialOffset() - offset, 0);
}
return Vec3.ZERO;
}
@Override
protected void visitNewPosition() {
super.visitNewPosition();
if (level.isClientSide)
return;
if (movedContraption != null)
return;
if (getSpeed() <= 0)
return;
BlockPos posBelow = worldPosition.below((int) (offset + getMovementSpeed()) + 1);
BlockState state = level.getBlockState(posBelow);
if (!BlockMovementChecks.isMovementNecessary(state, level, posBelow))
return;
if (BlockMovementChecks.isBrittle(state))
return;
disassemble();
assembleNextTick = true;
}
@Override
protected void read(CompoundTag compound, boolean clientPacket) {
initialOffset = compound.getInt("InitialOffset");
needsContraption = compound.getBoolean("NeedsContraption");
super.read(compound, clientPacket);
BlockPos prevMirrorParent = mirrorParent;
mirrorParent = null;
mirrorChildren = null;
if (compound.contains("MirrorParent")) {
mirrorParent = NbtUtils.readBlockPos(compound.getCompound("MirrorParent"));
offset = 0;
if (prevMirrorParent == null || !prevMirrorParent.equals(mirrorParent))
sharedMirrorContraption = null;
}
if (compound.contains("MirrorChildren"))
mirrorChildren = NBTHelper.readCompoundList(compound.getList("MirrorChildren", Tag.TAG_COMPOUND),
NbtUtils::readBlockPos);
if (mirrorParent == null)
sharedMirrorContraption = null;
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
compound.putInt("InitialOffset", initialOffset);
super.write(compound, clientPacket);
if (mirrorParent != null)
compound.put("MirrorParent", NbtUtils.writeBlockPos(mirrorParent));
if (mirrorChildren != null)
compound.put("MirrorChildren", NBTHelper.writeCompoundList(mirrorChildren, NbtUtils::writeBlockPos));
}
public void startMirroringOther(BlockPos parent) {
if (parent.equals(worldPosition))
return;
if (!(level.getBlockEntity(parent) instanceof PulleyBlockEntity pbe))
return;
if (pbe.getType() != getType())
return;
if (pbe.mirrorChildren == null)
pbe.mirrorChildren = new ArrayList<>();
pbe.mirrorChildren.add(worldPosition);
pbe.notifyUpdate();
mirrorParent = parent;
try {
assemble();
} catch (AssemblyException e) {
}
notifyUpdate();
}
public void notifyMirrorsOfDisassembly() {
if (mirrorChildren == null)
return;
for (BlockPos blockPos : mirrorChildren) {
if (!(level.getBlockEntity(blockPos) instanceof PulleyBlockEntity pbe))
continue;
pbe.offset = offset;
pbe.disassemble();
pbe.mirrorParent = null;
pbe.notifyUpdate();
}
mirrorChildren.clear();
notifyUpdate();
}
@Override
protected int getExtensionRange() {
return Math.max(0, Math.min(AllConfigs.server().kinetics.maxRopeLength.get(),
(worldPosition.getY() - 1) - level.getMinBuildHeight()));
}
@Override
protected int getInitialOffset() {
return initialOffset;
}
@Override
protected Vec3 toMotionVector(float speed) {
return new Vec3(0, -speed, 0);
}
@Override
protected ValueBoxTransform getMovementModeSlot() {
return new CenteredSideValueBoxTransform((state, d) -> d == Direction.UP);
}
@Override
public float getInterpolatedOffset(float partialTicks) {
if (isVirtual())
return Mth.lerp(partialTicks, prevAnimatedOffset, offset);
boolean moving = running && (movedContraption == null || !movedContraption.isStalled());
return super.getInterpolatedOffset(moving ? partialTicks : 0.5f);
}
public void animateOffset(float forcedOffset) {
offset = forcedOffset;
}
@Override
public float getPercent() {
int distance = worldPosition.getY() - level.getMinBuildHeight();
if (distance <= 0)
return 100;
return 100 * getInterpolatedOffset(.5f) / distance;
}
}