Sliding into motion

- Contraptions now send block changes to other clients when doors are opened
- Train doors now have special behaviour on contraptions
- Train doors no longer require solid ground
This commit is contained in:
simibubi 2022-06-01 20:32:16 +02:00
parent 8ab66b8da6
commit b4e047c5e4
14 changed files with 339 additions and 44 deletions

View file

@ -137,6 +137,7 @@ import com.simibubi.create.content.curiosities.bell.HauntedBellMovementBehaviour
import com.simibubi.create.content.curiosities.bell.PeculiarBellBlock;
import com.simibubi.create.content.curiosities.deco.MetalLadderBlock;
import com.simibubi.create.content.curiosities.deco.PlacardBlock;
import com.simibubi.create.content.curiosities.deco.SlidingDoorMovementBehaviour;
import com.simibubi.create.content.curiosities.deco.TrainDoorBlock;
import com.simibubi.create.content.curiosities.deco.TrainTrapdoorBlock;
import com.simibubi.create.content.curiosities.girder.ConnectedGirderModel;
@ -1567,6 +1568,7 @@ public class AllBlocks {
.addLayer(() -> RenderType::cutoutMipped)
.transform(pickaxeOnly())
.onRegister(addInteractionBehaviour(new DoorMovingInteraction()))
.onRegister(addMovementBehaviour(new SlidingDoorMovementBehaviour()))
.tag(BlockTags.DOORS)
.tag(BlockTags.WOODEN_DOORS) // for villager AI
.loot((lr, block) -> lr.add(block, BlockLoot.createDoorTable(block)))

View file

@ -27,7 +27,9 @@ import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket;
import com.simibubi.create.content.curiosities.deco.TrainDoorBlock;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.entity.Train;
@ -62,6 +64,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.phys.AABB;
@ -69,6 +72,7 @@ import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.entity.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.network.PacketDistributor;
@ -340,6 +344,12 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
living.setYRot(angle);
}
public void setBlock(BlockPos localPos, StructureBlockInfo newInfo) {
contraption.blocks.put(localPos, newInfo);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
new ContraptionBlockChangedPacket(getId(), localPos, newInfo.state));
}
protected abstract void tickContraption();
public abstract Vec3 applyRotation(Vec3 localPos, float partialTicks);
@ -653,26 +663,37 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
@OnlyIn(Dist.CLIENT)
static void handleStallPacket(ContraptionStallPacket packet) {
Entity entity = Minecraft.getInstance().level.getEntity(packet.entityID);
if (!(entity instanceof AbstractContraptionEntity))
return;
AbstractContraptionEntity ce = (AbstractContraptionEntity) entity;
ce.handleStallInformation(packet.x, packet.y, packet.z, packet.angle);
if (Minecraft.getInstance().level.getEntity(packet.entityID) instanceof AbstractContraptionEntity ce)
ce.handleStallInformation(packet.x, packet.y, packet.z, packet.angle);
}
@OnlyIn(Dist.CLIENT)
static void handleBlockChangedPacket(ContraptionBlockChangedPacket packet) {
if (Minecraft.getInstance().level.getEntity(packet.entityID) instanceof AbstractContraptionEntity ce)
ce.handleBlockChange(packet.localPos, packet.newState);
}
@OnlyIn(Dist.CLIENT)
static void handleDisassemblyPacket(ContraptionDisassemblyPacket packet) {
Entity entity = Minecraft.getInstance().level.getEntity(packet.entityID);
if (!(entity instanceof AbstractContraptionEntity))
return;
AbstractContraptionEntity ce = (AbstractContraptionEntity) entity;
ce.moveCollidedEntitiesOnDisassembly(packet.transform);
if (Minecraft.getInstance().level.getEntity(packet.entityID) instanceof AbstractContraptionEntity ce)
ce.moveCollidedEntitiesOnDisassembly(packet.transform);
}
protected abstract float getStalledAngle();
protected abstract void handleStallInformation(float x, float y, float z, float angle);
@OnlyIn(Dist.CLIENT)
protected void handleBlockChange(BlockPos localPos, BlockState newState) {
if (contraption == null || !contraption.blocks.containsKey(localPos))
return;
StructureBlockInfo info = contraption.blocks.get(localPos);
contraption.blocks.put(localPos, new StructureBlockInfo(info.pos, newState, info.nbt));
if (info.state != newState && !(newState.getBlock() instanceof TrainDoorBlock))
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ContraptionRenderDispatcher.invalidate(contraption));
contraption.invalidateColliders();
}
@Override
public CompoundTag saveWithoutId(CompoundTag nbt) {
Vec3 vec = position();

View file

@ -29,6 +29,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.pis
import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyTileEntity;
import com.simibubi.create.content.contraptions.fluids.tank.FluidTankBlock;
import com.simibubi.create.content.curiosities.deco.TrainDoorBlock;
import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
@ -388,6 +389,8 @@ public class BlockMovementChecks {
.getAxis();
if (AllBlocks.STICKER.has(state) && !state.getValue(StickerBlock.EXTENDED))
return facing == state.getValue(StickerBlock.FACING);
if (state.getBlock() instanceof TrainDoorBlock)
return false;
return isBrittle(state);
}

View file

@ -56,6 +56,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankTileEntity;
import com.simibubi.create.content.contraptions.relays.advanced.GantryShaftBlock;
import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.content.curiosities.deco.TrainDoorBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateTileEntity;
import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity;
@ -105,6 +106,7 @@ import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
@ -590,6 +592,8 @@ public abstract class Contraption {
blockstate = BlockHelper.copyProperties(blockstate, AllBlocks.SHAFT.getDefaultState());
if (AllBlocks.CONTROLS.has(blockstate))
blockstate = blockstate.setValue(ControlsBlock.OPEN, true);
if (blockstate.hasProperty(TrainDoorBlock.VISIBLE))
blockstate = blockstate.setValue(TrainDoorBlock.VISIBLE, false);
if (blockstate.getBlock() instanceof ButtonBlock) {
blockstate = blockstate.setValue(ButtonBlock.POWERED, false);
world.scheduleTick(pos, blockstate.getBlock(), -1);
@ -673,6 +677,8 @@ public abstract class Contraption {
.forEach(c -> {
CompoundTag comp = (CompoundTag) c;
StructureBlockInfo info = this.blocks.get(NbtUtils.readBlockPos(comp.getCompound("Pos")));
if (info == null)
return;
MovementContext context = MovementContext.readNBT(world, info, comp, this);
getActors().add(MutablePair.of(info, context));
});
@ -695,7 +701,10 @@ public abstract class Contraption {
interactors.clear();
NBTHelper.iterateCompoundList(nbt.getList("Interactors", Tag.TAG_COMPOUND), c -> {
BlockPos pos = NbtUtils.readBlockPos(c.getCompound("Pos"));
MovingInteractionBehaviour behaviour = AllInteractionBehaviours.of(getBlocks().get(pos).state.getBlock());
StructureBlockInfo structureBlockInfo = getBlocks().get(pos);
if (structureBlockInfo == null)
return;
MovingInteractionBehaviour behaviour = AllInteractionBehaviours.of(structureBlockInfo.state.getBlock());
if (behaviour != null)
interactors.put(pos, behaviour);
});
@ -1013,6 +1022,9 @@ public abstract class Contraption {
if (AllBlocks.SHAFT.has(state))
state = ShaftBlock.pickCorrectShaftType(state, world, targetPos);
if (state.hasProperty(TrainDoorBlock.VISIBLE))
state = state.setValue(TrainDoorBlock.VISIBLE, !state.getValue(TrainDoorBlock.OPEN))
.setValue(TrainDoorBlock.POWERED, false);
world.setBlock(targetPos, state, Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_ALL);
@ -1024,6 +1036,7 @@ public abstract class Contraption {
}
BlockEntity tileEntity = world.getBlockEntity(targetPos);
CompoundTag tag = block.nbt;
if (tileEntity != null)
tag = NBTProcessors.process(tileEntity, tag, false);
@ -1112,6 +1125,8 @@ public abstract class Contraption {
if (PoiType.forState(info.state)
.isPresent())
return false;
if (info.state.getBlock() instanceof TrainDoorBlock)
return false;
return true;
}
@ -1199,7 +1214,7 @@ public abstract class Contraption {
for (Entry<BlockPos, StructureBlockInfo> entry : blocks.entrySet()) {
StructureBlockInfo info = entry.getValue();
BlockPos localPos = entry.getKey();
VoxelShape collisionShape = info.state.getCollisionShape(world, localPos);
VoxelShape collisionShape = info.state.getCollisionShape(world, localPos, CollisionContext.empty());
if (collisionShape.isEmpty())
continue;
combinedShape = Shapes.joinUnoptimized(combinedShape,

View file

@ -0,0 +1,49 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent.Context;
public class ContraptionBlockChangedPacket extends SimplePacketBase {
int entityID;
BlockPos localPos;
BlockState newState;
public ContraptionBlockChangedPacket(int id, BlockPos pos, BlockState state) {
entityID = id;
localPos = pos;
newState = state;
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(entityID);
buffer.writeBlockPos(localPos);
buffer.writeNbt(NbtUtils.writeBlockState(newState));
}
public ContraptionBlockChangedPacket(FriendlyByteBuf buffer) {
entityID = buffer.readInt();
localPos = buffer.readBlockPos();
newState = NbtUtils.readBlockState(buffer.readNbt());
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> AbstractContraptionEntity.handleBlockChangedPacket(this)));
context.get()
.setPacketHandled(true);
}
}

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEn
import com.simibubi.create.content.logistics.trains.entity.TrainRelocator;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.RaycastHelper;
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
import com.simibubi.create.foundation.utility.VecHelper;
@ -135,20 +136,25 @@ public class ContraptionHandlerClient {
MutableObject<BlockHitResult> mutableResult = new MutableObject<>();
PredicateTraceResult predicateResult = RaycastHelper.rayTraceUntil(localOrigin, localTarget, p -> {
StructureBlockInfo blockInfo = contraption.getBlocks()
.get(p);
if (blockInfo == null)
return false;
BlockState state = blockInfo.state;
VoxelShape raytraceShape = state.getShape(Minecraft.getInstance().level, BlockPos.ZERO.below());
if (raytraceShape.isEmpty())
return false;
if (contraption.isHiddenInPortal(p))
return false;
BlockHitResult rayTrace = raytraceShape.clip(localOrigin, localTarget, p);
if (rayTrace != null) {
mutableResult.setValue(rayTrace);
return true;
for (Direction d : Iterate.directions) {
if (d == Direction.UP)
continue;
BlockPos pos = d == Direction.DOWN ? p : p.relative(d);
StructureBlockInfo blockInfo = contraption.getBlocks()
.get(pos);
if (blockInfo == null)
continue;
BlockState state = blockInfo.state;
VoxelShape raytraceShape = state.getShape(contraption.getContraptionWorld(), BlockPos.ZERO.below());
if (raytraceShape.isEmpty())
continue;
if (contraption.isHiddenInPortal(pos))
continue;
BlockHitResult rayTrace = raytraceShape.clip(localOrigin, localTarget, pos);
if (rayTrace != null) {
mutableResult.setValue(rayTrace);
return true;
}
}
return false;
});

View file

@ -27,9 +27,9 @@ public abstract class MovingInteractionBehaviour {
protected void setContraptionBlockData(AbstractContraptionEntity contraptionEntity, BlockPos pos,
StructureBlockInfo info) {
contraptionEntity.contraption.blocks.put(pos, info);
if (contraptionEntity.level.isClientSide)
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(contraptionEntity.contraption));
if (contraptionEntity.level.isClientSide())
return;
contraptionEntity.setBlock(pos, info);
}
@OnlyIn(Dist.CLIENT)

View file

@ -1,13 +1,17 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.curiosities.deco.TrainDoorBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoorHingeSide;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
@ -15,19 +19,42 @@ public class DoorMovingInteraction extends SimpleBlockMovingInteraction {
@Override
protected BlockState handle(Player player, Contraption contraption, BlockPos pos, BlockState currentState) {
SoundEvent sound =
currentState.getValue(DoorBlock.OPEN) ? SoundEvents.WOODEN_DOOR_CLOSE : SoundEvents.WOODEN_DOOR_OPEN;
if (!(currentState.getBlock() instanceof DoorBlock))
return currentState;
boolean trainDoor = currentState.getBlock() instanceof TrainDoorBlock;
SoundEvent sound = currentState.getValue(DoorBlock.OPEN) ? trainDoor ? null : SoundEvents.WOODEN_DOOR_CLOSE
: trainDoor ? SoundEvents.IRON_DOOR_OPEN : SoundEvents.WOODEN_DOOR_OPEN;
BlockPos otherPos = currentState.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? pos.above() : pos.below();
StructureBlockInfo info = contraption.getBlocks()
.get(otherPos);
if (info.state.hasProperty(DoorBlock.OPEN))
setContraptionBlockData(contraption.entity, otherPos,
new StructureBlockInfo(info.pos, info.state.cycle(DoorBlock.OPEN), info.nbt));
if (info.state.hasProperty(DoorBlock.OPEN)) {
BlockState newState = info.state.cycle(DoorBlock.OPEN);
setContraptionBlockData(contraption.entity, otherPos, new StructureBlockInfo(info.pos, newState, info.nbt));
}
float pitch = player.level.random.nextFloat() * 0.1F + 0.9F;
playSound(player, sound, pitch);
return currentState.cycle(DoorBlock.OPEN);
currentState = currentState.cycle(DoorBlock.OPEN);
if (player != null) {
if (trainDoor) {
DoorHingeSide hinge = currentState.getValue(TrainDoorBlock.HINGE);
Direction facing = currentState.getValue(TrainDoorBlock.FACING);
BlockPos doublePos =
pos.relative(hinge == DoorHingeSide.LEFT ? facing.getClockWise() : facing.getCounterClockWise());
StructureBlockInfo doubleInfo = contraption.getBlocks()
.get(doublePos);
if (doubleInfo != null && TrainDoorBlock.isDoubleDoor(currentState, hinge, facing, doubleInfo.state))
handlePlayerInteraction(null, InteractionHand.MAIN_HAND, doublePos, contraption.entity);
}
float pitch = player.level.random.nextFloat() * 0.1F + 0.9F;
if (sound != null)
playSound(player, sound, pitch);
}
return currentState;
}
@Override

View file

@ -0,0 +1,107 @@
package com.simibubi.create.content.curiosities.deco;
import java.util.Map;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.entity.CarriageSyncData;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
public class SlidingDoorMovementBehaviour implements MovementBehaviour {
@Override
public boolean renderAsNormalTileEntity() {
return true;
}
@Override
public void tick(MovementContext context) {
StructureBlockInfo structureBlockInfo = context.contraption.getBlocks()
.get(context.localPos);
if (structureBlockInfo == null)
return;
boolean open = SlidingDoorTileEntity.isOpen(structureBlockInfo.state);
if (!context.world.isClientSide())
tickOpen(context, open);
Map<BlockPos, BlockEntity> tes = context.contraption.presentTileEntities;
if (!(tes.get(context.localPos) instanceof SlidingDoorTileEntity doorTE))
return;
boolean wasSettled = doorTE.animation.settled();
doorTE.animation.chase(open ? 1 : 0, .15f, Chaser.LINEAR);
doorTE.animation.tickChaser();
if (!wasSettled && doorTE.animation.settled() && !open)
context.world.playLocalSound(context.position.x, context.position.y, context.position.z,
SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, .5f, 1, false);
}
protected void tickOpen(MovementContext context, boolean currentlyOpen) {
boolean shouldOpen = shouldOpen(context);
if (!shouldUpdate(context, shouldOpen))
return;
if (currentlyOpen == shouldOpen)
return;
BlockPos pos = context.localPos;
Contraption contraption = context.contraption;
StructureBlockInfo info = contraption.getBlocks()
.get(pos);
if (info == null || !info.state.hasProperty(DoorBlock.OPEN))
return;
toggleDoor(pos, contraption, info);
if (shouldOpen)
context.world.playSound(null, new BlockPos(context.position), SoundEvents.IRON_DOOR_OPEN,
SoundSource.BLOCKS, .5f, 1);
}
private void toggleDoor(BlockPos pos, Contraption contraption, StructureBlockInfo info) {
BlockState newState = info.state.cycle(DoorBlock.OPEN);
contraption.entity.setBlock(pos, new StructureBlockInfo(info.pos, newState, info.nbt));
BlockPos otherPos = newState.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? pos.above() : pos.below();
info = contraption.getBlocks()
.get(otherPos);
if (info != null && info.state.hasProperty(DoorBlock.OPEN)) {
newState = info.state.cycle(DoorBlock.OPEN);
contraption.entity.setBlock(otherPos, new StructureBlockInfo(info.pos, newState, info.nbt));
}
}
protected boolean shouldUpdate(MovementContext context, boolean shouldOpen) {
if (context.firstMovement && shouldOpen)
return false;
if (!context.data.contains("Open")) {
context.data.putBoolean("Open", shouldOpen);
return true;
}
boolean wasOpen = context.data.getBoolean("Open");
context.data.putBoolean("Open", shouldOpen);
return wasOpen != shouldOpen;
}
protected boolean shouldOpen(MovementContext context) {
if (context.contraption.entity instanceof CarriageContraptionEntity cce) {
CarriageSyncData carriageData = cce.getCarriageData();
if (Math.abs(carriageData.distanceToDestination) > 1)
return false;
}
return context.motion.length() < 1 / 128f && !context.contraption.entity.isStalled();
}
}

View file

@ -45,7 +45,7 @@ public class SlidingDoorRenderer extends SafeTileEntityRenderer<SlidingDoorTileE
for (DoubleBlockHalf half : DoubleBlockHalf.values()) {
CachedBufferer.block(blockState.setValue(DoorBlock.OPEN, false)
.setValue(DoorBlock.HALF, half))
.translate(0, half == DoubleBlockHalf.UPPER ? 1 : 0, 0)
.translate(0, half == DoubleBlockHalf.UPPER ? 1 - 1 / 512f : 0, 0)
.translate(offset)
.light(light)
.renderInto(ms, vb);

View file

@ -10,6 +10,7 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
@ -18,6 +19,7 @@ public class SlidingDoorTileEntity extends SmartTileEntity {
LerpedFloat animation;
int bridgeTicks;
boolean deferUpdate;
public SlidingDoorTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
@ -27,6 +29,12 @@ public class SlidingDoorTileEntity extends SmartTileEntity {
@Override
public void tick() {
if (deferUpdate && !level.isClientSide()) {
deferUpdate = false;
BlockState blockState = getBlockState();
blockState.neighborChanged(level, worldPosition, Blocks.AIR, worldPosition, false);
}
super.tick();
boolean open = isOpen(getBlockState());
boolean wasSettled = animation.settled();

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.curiosities.deco;
import javax.annotation.Nullable;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionWorld;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ITE;
@ -16,6 +17,7 @@ import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DoorBlock;
@ -59,7 +61,7 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
if (!pState.getValue(OPEN) && pState.getValue(VISIBLE))
if (!pState.getValue(OPEN) && (pState.getValue(VISIBLE) || pLevel instanceof ContraptionWorld))
return super.getShape(pState, pLevel, pPos, pContext);
Direction direction = pState.getValue(FACING);
@ -73,6 +75,12 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
};
}
@Override
public boolean canSurvive(BlockState pState, LevelReader pLevel, BlockPos pPos) {
return pState.getValue(HALF) == DoubleBlockHalf.LOWER || pLevel.getBlockState(pPos.below())
.is(this);
}
@Override
public VoxelShape getInteractionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
return getShape(pState, pLevel, pPos, CollisionContext.empty());
@ -82,10 +90,17 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
BlockState stateForPlacement = super.getStateForPlacement(pContext);
if (stateForPlacement != null && stateForPlacement.getValue(OPEN))
return stateForPlacement.setValue(VISIBLE, false);
return stateForPlacement.setValue(OPEN, false)
.setValue(POWERED, false);
return stateForPlacement;
}
@Override
public void onPlace(BlockState pState, Level pLevel, BlockPos pPos, BlockState pOldState, boolean pIsMoving) {
if (!pOldState.is(this))
deferUpdate(pLevel, pPos);
}
@Override
public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel,
BlockPos pCurrentPos, BlockPos pFacingPos) {
@ -128,13 +143,17 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
@Override
public void neighborChanged(BlockState pState, Level pLevel, BlockPos pPos, Block pBlock, BlockPos pFromPos,
boolean pIsMoving) {
boolean isPowered = pLevel.hasNeighborSignal(pPos) || pLevel.hasNeighborSignal(
pPos.relative(pState.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
boolean lower = pState.getValue(HALF) == DoubleBlockHalf.LOWER;
boolean isPowered = isDoorPowered(pLevel, pPos, pState);
if (defaultBlockState().is(pBlock))
return;
if (isPowered == pState.getValue(POWERED))
return;
SlidingDoorTileEntity te = getTileEntity(pLevel, lower ? pPos : pPos.below());
if (te != null && te.deferUpdate)
return;
BlockState changedState = pState.setValue(POWERED, Boolean.valueOf(isPowered))
.setValue(OPEN, Boolean.valueOf(isPowered));
if (isPowered)
@ -149,6 +168,7 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
BlockPos otherPos =
pPos.relative(hinge == DoorHingeSide.LEFT ? facing.getClockWise() : facing.getCounterClockWise());
BlockState otherDoor = pLevel.getBlockState(otherPos);
if (isDoubleDoor(changedState, hinge, facing, otherDoor)) {
otherDoor = otherDoor.setValue(POWERED, Boolean.valueOf(isPowered))
.setValue(OPEN, Boolean.valueOf(isPowered));
@ -161,6 +181,22 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
pLevel.setBlock(pPos, changedState, 2);
}
public static boolean isDoorPowered(Level pLevel, BlockPos pPos, BlockState state) {
boolean lower = state.getValue(HALF) == DoubleBlockHalf.LOWER;
DoorHingeSide hinge = state.getValue(HINGE);
Direction facing = state.getValue(FACING);
BlockPos otherPos =
pPos.relative(hinge == DoorHingeSide.LEFT ? facing.getClockWise() : facing.getCounterClockWise());
BlockState otherDoor = pLevel.getBlockState(otherPos);
if (isDoubleDoor(state.cycle(OPEN), hinge, facing, otherDoor) && (pLevel.hasNeighborSignal(otherPos)
|| pLevel.hasNeighborSignal(otherPos.relative(lower ? Direction.UP : Direction.DOWN))))
return true;
return pLevel.hasNeighborSignal(pPos)
|| pLevel.hasNeighborSignal(pPos.relative(lower ? Direction.UP : Direction.DOWN));
}
@Override
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand,
BlockHitResult pHit) {
@ -184,7 +220,11 @@ public class TrainDoorBlock extends DoorBlock implements IWrenchable, ITE<Slidin
return InteractionResult.sidedSuccess(pLevel.isClientSide);
}
private boolean isDoubleDoor(BlockState pState, DoorHingeSide hinge, Direction facing, BlockState otherDoor) {
public void deferUpdate(LevelAccessor level, BlockPos pos) {
withTileEntityDo(level, pos, sdte -> sdte.deferUpdate = true);
}
public static boolean isDoubleDoor(BlockState pState, DoorHingeSide hinge, Direction facing, BlockState otherDoor) {
return otherDoor.getBlock() == pState.getBlock() && otherDoor.getValue(HINGE) != hinge
&& otherDoor.getValue(FACING) == facing && otherDoor.getValue(OPEN) != pState.getValue(OPEN)
&& otherDoor.getValue(HALF) == pState.getValue(HALF);

View file

@ -16,6 +16,7 @@ import com.simibubi.create.AllEntityDataSerializers;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
@ -26,6 +27,7 @@ import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalC
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Lang;
@ -52,6 +54,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.network.PacketDistributor;
public class CarriageContraptionEntity extends OrientedContraptionEntity {
@ -127,7 +130,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
}
}
private CarriageSyncData getCarriageData() {
public CarriageSyncData getCarriageData() {
return entityData.get(CARRIAGE_DATA);
}
@ -184,6 +187,18 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
}
}
@Override
public void setBlock(BlockPos localPos, StructureBlockInfo newInfo) {
if (carriage == null)
return;
carriage.forEachPresentEntity(cce -> {
cce.contraption.getBlocks()
.put(localPos, newInfo);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> cce),
new ContraptionBlockChangedPacket(cce.getId(), localPos, newInfo.state));
});
}
@Override
protected void tickContraption() {
if (nonDamageTicks > 0)

View file

@ -8,6 +8,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionDisassemblyPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.TrainCollisionPacket;
@ -142,6 +143,7 @@ public enum AllPackets {
S_CONFIGURE_CONFIG(SConfigureConfigPacket.class, SConfigureConfigPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_STALL(ContraptionStallPacket.class, ContraptionStallPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_DISASSEMBLE(ContraptionDisassemblyPacket.class, ContraptionDisassemblyPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_BLOCK_CHANGED(ContraptionBlockChangedPacket.class, ContraptionBlockChangedPacket::new, PLAY_TO_CLIENT),
GLUE_EFFECT(GlueEffectPacket.class, GlueEffectPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new, PLAY_TO_CLIENT),
LIMBSWING_UPDATE(LimbSwingUpdatePacket.class, LimbSwingUpdatePacket::new, PLAY_TO_CLIENT),