Transforming the bugs away

- Fix arm interaction point client-side deserialization and
initialization issues
- Fix sequenced gearshift structure rotation
- Fix encased cogwheel rotation, mirroring, and structure transformation
- Add ITransformableBlock to allow custom handling of
StructureTransforms
- Remove DirectionHelper
- Add jarJarRelease Gradle task for building a jarJar without a
classifier
This commit is contained in:
PepperCode1 2022-08-01 01:03:03 -07:00
parent 82be76d893
commit d9c6418fbf
15 changed files with 368 additions and 259 deletions

View file

@ -209,6 +209,15 @@ jar {
} }
} }
task jarJarRelease {
doLast {
tasks.jarJar {
classifier = ''
}
}
finalizedBy tasks.jarJar
}
java { java {
withSourcesJar() withSourcesJar()
withJavadocJar() withJavadocJar()

View file

@ -1,6 +1,7 @@
package com.simibubi.create.api.behaviour; package com.simibubi.create.api.behaviour;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.simibubi.create.Create; import com.simibubi.create.Create;
@ -14,7 +15,7 @@ import net.minecraftforge.fluids.FluidStack;
public abstract class BlockSpoutingBehaviour { public abstract class BlockSpoutingBehaviour {
private static final HashMap<ResourceLocation, BlockSpoutingBehaviour> BLOCK_SPOUTING_BEHAVIOURS = new HashMap<>(); private static final Map<ResourceLocation, BlockSpoutingBehaviour> BLOCK_SPOUTING_BEHAVIOURS = new HashMap<>();
public static void addCustomSpoutInteraction(ResourceLocation resourceLocation, public static void addCustomSpoutInteraction(ResourceLocation resourceLocation,
BlockSpoutingBehaviour movementBehaviour) { BlockSpoutingBehaviour movementBehaviour) {

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.base; package com.simibubi.create.content.contraptions.base;
import com.simibubi.create.foundation.utility.DirectionHelper; import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder; import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.BooleanProperty;
public abstract class DirectionalAxisKineticBlock extends DirectionalKineticBlock { public abstract class DirectionalAxisKineticBlock extends DirectionalKineticBlock implements ITransformableBlock {
public static final BooleanProperty AXIS_ALONG_FIRST_COORDINATE = BooleanProperty.create("axis_along_first"); public static final BooleanProperty AXIS_ALONG_FIRST_COORDINATE = BooleanProperty.create("axis_along_first");
@ -53,7 +54,7 @@ public abstract class DirectionalAxisKineticBlock extends DirectionalKineticBloc
if (faceAxis.isHorizontal()) { if (faceAxis.isHorizontal()) {
alongFirst = faceAxis == Axis.Z; alongFirst = faceAxis == Axis.Z;
Direction positivePerpendicular = DirectionHelper.getPositivePerpendicular(faceAxis); Direction positivePerpendicular = faceAxis == Axis.X ? Direction.SOUTH : Direction.EAST;
boolean shaftAbove = prefersConnectionTo(world, pos, Direction.UP, true); boolean shaftAbove = prefersConnectionTo(world, pos, Direction.UP, true);
boolean shaftBelow = prefersConnectionTo(world, pos, Direction.DOWN, true); boolean shaftBelow = prefersConnectionTo(world, pos, Direction.DOWN, true);
@ -121,6 +122,23 @@ public abstract class DirectionalAxisKineticBlock extends DirectionalKineticBloc
return super.rotate(state, rot); return super.rotate(state, rot);
} }
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null) {
state = mirror(state, transform.mirror);
}
if (transform.rotationAxis == Direction.Axis.Y) {
return rotate(state, transform.rotation);
}
Direction newFacing = transform.rotateFacing(state.getValue(FACING));
if (transform.rotationAxis == newFacing.getAxis() && transform.rotation.ordinal() % 2 == 1) {
state = state.cycle(AXIS_ALONG_FIRST_COORDINATE);
}
return state.setValue(FACING, newFacing);
}
@Override @Override
public boolean hasShaftTowards(LevelReader world, BlockPos pos, BlockState state, Direction face) { public boolean hasShaftTowards(LevelReader world, BlockPos pos, BlockState state, Direction face) {
return face.getAxis() == getRotationAxis(state); return face.getAxis() == getRotationAxis(state);

View file

@ -0,0 +1,7 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import net.minecraft.world.level.block.state.BlockState;
public interface ITransformableBlock {
BlockState transform(BlockState state, StructureTransform transform);
}

View file

@ -4,13 +4,6 @@ import static net.minecraft.world.level.block.state.properties.BlockStatePropert
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.FACING; import static net.minecraft.world.level.block.state.properties.BlockStateProperties.FACING;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING; import static net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.DirectionalAxisKineticBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.AbstractChassisBlock;
import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.content.contraptions.relays.belt.BeltSlope;
import com.simibubi.create.foundation.utility.DirectionHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -30,7 +23,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.level.block.state.properties.AttachFace;
import net.minecraft.world.level.block.state.properties.BellAttachType; import net.minecraft.world.level.block.state.properties.BellAttachType;
import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.Half;
@ -55,7 +47,7 @@ public class StructureTransform {
} }
public StructureTransform(BlockPos offset, Axis axis, Rotation rotation, Mirror mirror) { public StructureTransform(BlockPos offset, Axis axis, Rotation rotation, Mirror mirror) {
this(offset, rotation == Rotation.NONE ? 0 : (4 - rotation.ordinal())*90, axis, rotation, mirror); this(offset, rotation == Rotation.NONE ? 0 : (4 - rotation.ordinal()) * 90, axis, rotation, mirror);
} }
public StructureTransform(BlockPos offset, float xRotation, float yRotation, float zRotation) { public StructureTransform(BlockPos offset, float xRotation, float yRotation, float zRotation) {
@ -96,7 +88,7 @@ public class StructureTransform {
vec = VecHelper.rotate(vec, angle, rotationAxis); vec = VecHelper.rotate(vec, angle, rotationAxis);
return vec; return vec;
} }
public Vec3 applyWithoutOffset(Vec3 localVec) { public Vec3 applyWithoutOffset(Vec3 localVec) {
Vec3 vec = localVec; Vec3 vec = localVec;
if (mirror != null) if (mirror != null)
@ -124,16 +116,18 @@ public class StructureTransform {
} }
/** /**
* Minecraft does not support blockstate rotation around axes other than y. Add * Vanilla does not support block state rotation around axes other than Y. Add
* specific cases here for blockstates, that should react to rotations around * specific cases here for vanilla block states so that they can react to rotations
* horizontal axes * around horizontal axes. For Create blocks, implement ITransformableBlock.
*/ */
public BlockState apply(BlockState state) { public BlockState apply(BlockState state) {
Block block = state.getBlock();
if (block instanceof ITransformableBlock transformable)
return transformable.transform(state, this);
if (mirror != null) if (mirror != null)
state = state.mirror(mirror); state = state.mirror(mirror);
Block block = state.getBlock();
if (rotationAxis == Axis.Y) { if (rotationAxis == Axis.Y) {
if (block instanceof BellBlock) { if (block instanceof BellBlock) {
if (state.getValue(BlockStateProperties.BELL_ATTACHMENT) == BellAttachType.DOUBLE_WALL) if (state.getValue(BlockStateProperties.BELL_ATTACHMENT) == BellAttachType.DOUBLE_WALL)
@ -141,12 +135,10 @@ public class StructureTransform {
return state.setValue(BellBlock.FACING, return state.setValue(BellBlock.FACING,
rotation.rotate(state.getValue(BellBlock.FACING))); rotation.rotate(state.getValue(BellBlock.FACING)));
} }
return state.rotate(rotation); return state.rotate(rotation);
} }
if (block instanceof AbstractChassisBlock)
return rotateChassis(state);
if (block instanceof FaceAttachedHorizontalDirectionalBlock) { if (block instanceof FaceAttachedHorizontalDirectionalBlock) {
DirectionProperty facingProperty = FaceAttachedHorizontalDirectionalBlock.FACING; DirectionProperty facingProperty = FaceAttachedHorizontalDirectionalBlock.FACING;
EnumProperty<AttachFace> faceProperty = FaceAttachedHorizontalDirectionalBlock.FACE; EnumProperty<AttachFace> faceProperty = FaceAttachedHorizontalDirectionalBlock.FACE;
@ -185,30 +177,11 @@ public class StructureTransform {
return state; return state;
} }
if (AllBlocks.BELT.has(state)) {
state = transformBelt(state, halfTurn);
return state;
}
if (state.hasProperty(FACING)) { if (state.hasProperty(FACING)) {
Direction newFacing = transformFacing(state.getValue(FACING)); state = state.setValue(FACING, rotateFacing(state.getValue(FACING)));
if (state.hasProperty(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE)) {
if (rotationAxis == newFacing.getAxis() && rotation.ordinal() % 2 == 1)
state = state.cycle(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE);
}
state = state.setValue(FACING, newFacing);
} else if (state.hasProperty(AXIS)) { } else if (state.hasProperty(AXIS)) {
state = state.setValue(AXIS, transformAxis(state.getValue(AXIS))); state = state.setValue(AXIS, rotateAxis(state.getValue(AXIS)));
} else if (halfTurn) { } else if (halfTurn) {
if (state.hasProperty(FACING)) {
Direction stateFacing = state.getValue(FACING);
if (stateFacing.getAxis() == rotationAxis)
return state;
}
if (state.hasProperty(HORIZONTAL_FACING)) { if (state.hasProperty(HORIZONTAL_FACING)) {
Direction stateFacing = state.getValue(HORIZONTAL_FACING); Direction stateFacing = state.getValue(HORIZONTAL_FACING);
if (stateFacing.getAxis() == rotationAxis) if (stateFacing.getAxis() == rotationAxis)
@ -216,6 +189,7 @@ public class StructureTransform {
} }
state = state.rotate(rotation); state = state.rotate(rotation);
if (state.hasProperty(SlabBlock.TYPE) && state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE) if (state.hasProperty(SlabBlock.TYPE) && state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE)
state = state.setValue(SlabBlock.TYPE, state = state.setValue(SlabBlock.TYPE,
state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM ? SlabType.TOP : SlabType.BOTTOM); state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM ? SlabType.TOP : SlabType.BOTTOM);
@ -244,125 +218,21 @@ public class StructureTransform {
return state; return state;
} }
protected BlockState transformBelt(BlockState state, boolean halfTurn) { public Direction mirrorFacing(Direction facing) {
Direction initialDirection = state.getValue(BeltBlock.HORIZONTAL_FACING);
boolean diagonal =
state.getValue(BeltBlock.SLOPE) == BeltSlope.DOWNWARD || state.getValue(BeltBlock.SLOPE) == BeltSlope.UPWARD;
if (!diagonal) {
for (int i = 0; i < rotation.ordinal(); i++) {
Direction direction = state.getValue(BeltBlock.HORIZONTAL_FACING);
BeltSlope slope = state.getValue(BeltBlock.SLOPE);
boolean vertical = slope == BeltSlope.VERTICAL;
boolean horizontal = slope == BeltSlope.HORIZONTAL;
boolean sideways = slope == BeltSlope.SIDEWAYS;
Direction newDirection = direction.getOpposite();
BeltSlope newSlope = BeltSlope.VERTICAL;
if (vertical) {
if (direction.getAxis() == rotationAxis) {
newDirection = direction.getCounterClockWise();
newSlope = BeltSlope.SIDEWAYS;
} else {
newSlope = BeltSlope.HORIZONTAL;
newDirection = direction;
if (direction.getAxis() == Axis.Z)
newDirection = direction.getOpposite();
}
}
if (sideways) {
newDirection = direction;
if (direction.getAxis() == rotationAxis)
newSlope = BeltSlope.HORIZONTAL;
else
newDirection = direction.getCounterClockWise();
}
if (horizontal) {
newDirection = direction;
if (direction.getAxis() == rotationAxis)
newSlope = BeltSlope.SIDEWAYS;
else if (direction.getAxis() != Axis.Z)
newDirection = direction.getOpposite();
}
state = state.setValue(BeltBlock.HORIZONTAL_FACING, newDirection);
state = state.setValue(BeltBlock.SLOPE, newSlope);
}
} else if (initialDirection.getAxis() != rotationAxis) {
for (int i = 0; i < rotation.ordinal(); i++) {
Direction direction = state.getValue(BeltBlock.HORIZONTAL_FACING);
Direction newDirection = direction.getOpposite();
BeltSlope slope = state.getValue(BeltBlock.SLOPE);
boolean upward = slope == BeltSlope.UPWARD;
boolean downward = slope == BeltSlope.DOWNWARD;
// Rotate diagonal
if (direction.getAxisDirection() == AxisDirection.POSITIVE ^ downward ^ direction.getAxis() == Axis.Z) {
state = state.setValue(BeltBlock.SLOPE, upward ? BeltSlope.DOWNWARD : BeltSlope.UPWARD);
} else {
state = state.setValue(BeltBlock.HORIZONTAL_FACING, newDirection);
}
}
} else if (halfTurn) {
Direction direction = state.getValue(BeltBlock.HORIZONTAL_FACING);
Direction newDirection = direction.getOpposite();
BeltSlope slope = state.getValue(BeltBlock.SLOPE);
boolean vertical = slope == BeltSlope.VERTICAL;
if (diagonal) {
state = state.setValue(BeltBlock.SLOPE, slope == BeltSlope.UPWARD ? BeltSlope.DOWNWARD
: slope == BeltSlope.DOWNWARD ? BeltSlope.UPWARD : slope);
} else if (vertical) {
state = state.setValue(BeltBlock.HORIZONTAL_FACING, newDirection);
}
}
return state;
}
public Axis transformAxis(Axis axisIn) {
Direction facing = Direction.get(AxisDirection.POSITIVE, axisIn);
facing = transformFacing(facing);
Axis axis = facing.getAxis();
return axis;
}
public Direction transformFacing(Direction facing) {
if (mirror != null) if (mirror != null)
facing = mirror.mirror(facing); return mirror.mirror(facing);
for (int i = 0; i < rotation.ordinal(); i++)
facing = DirectionHelper.rotateAround(facing, rotationAxis);
return facing; return facing;
} }
private BlockState rotateChassis(BlockState state) { public Axis rotateAxis(Axis axis) {
if (rotation == Rotation.NONE) Direction facing = Direction.get(AxisDirection.POSITIVE, axis);
return state; return rotateFacing(facing).getAxis();
}
BlockState rotated = state.setValue(AXIS, transformAxis(state.getValue(AXIS))); public Direction rotateFacing(Direction facing) {
AbstractChassisBlock block = (AbstractChassisBlock) state.getBlock(); for (int i = 0; i < rotation.ordinal(); i++)
facing = facing.getClockWise(rotationAxis);
for (Direction face : Iterate.directions) { return facing;
BooleanProperty glueableSide = block.getGlueableSide(rotated, face);
if (glueableSide != null)
rotated = rotated.setValue(glueableSide, false);
}
for (Direction face : Iterate.directions) {
BooleanProperty glueableSide = block.getGlueableSide(state, face);
if (glueableSide == null || !state.getValue(glueableSide))
continue;
Direction rotatedFacing = transformFacing(face);
BooleanProperty rotatedGlueableSide = block.getGlueableSide(rotated, rotatedFacing);
if (rotatedGlueableSide != null)
rotated = rotated.setValue(rotatedGlueableSide, true);
}
return rotated;
} }
public static StructureTransform fromBuffer(FriendlyByteBuf buffer) { public static StructureTransform fromBuffer(FriendlyByteBuf buffer) {

View file

@ -3,6 +3,8 @@ package com.simibubi.create.content.contraptions.components.structureMovement.ch
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllSoundEvents; import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -26,7 +28,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.Tags; import net.minecraftforge.common.Tags;
public abstract class AbstractChassisBlock extends RotatedPillarBlock implements IWrenchable, ITE<ChassisTileEntity> { public abstract class AbstractChassisBlock extends RotatedPillarBlock implements IWrenchable, ITE<ChassisTileEntity>, ITransformableBlock {
public AbstractChassisBlock(Properties properties) { public AbstractChassisBlock(Properties properties) {
super(properties); super(properties);
@ -132,6 +134,44 @@ public abstract class AbstractChassisBlock extends RotatedPillarBlock implements
return mirrored; return mirrored;
} }
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null) {
state = mirror(state, transform.mirror);
}
if (transform.rotationAxis == Direction.Axis.Y) {
return rotate(state, transform.rotation);
}
return transformInner(state, transform);
}
protected BlockState transformInner(BlockState state, StructureTransform transform) {
if (transform.rotation == Rotation.NONE)
return state;
BlockState rotated = state.setValue(AXIS, transform.rotateAxis(state.getValue(AXIS)));
AbstractChassisBlock block = (AbstractChassisBlock) state.getBlock();
for (Direction face : Iterate.directions) {
BooleanProperty glueableSide = block.getGlueableSide(rotated, face);
if (glueableSide != null)
rotated = rotated.setValue(glueableSide, false);
}
for (Direction face : Iterate.directions) {
BooleanProperty glueableSide = block.getGlueableSide(state, face);
if (glueableSide == null || !state.getValue(glueableSide))
continue;
Direction rotatedFacing = transform.rotateFacing(face);
BooleanProperty rotatedGlueableSide = block.getGlueableSide(rotated, rotatedFacing);
if (rotatedGlueableSide != null)
rotated = rotated.setValue(rotatedGlueableSide, true);
}
return rotated;
}
public abstract BooleanProperty getGlueableSide(BlockState state, Direction face); public abstract BooleanProperty getGlueableSide(BlockState state, Direction face);
protected boolean glueAllowedOnSide(BlockGetter world, BlockPos pos, BlockState state, Direction side) { protected boolean glueAllowedOnSide(BlockGetter world, BlockPos pos, BlockState state, Direction side) {

View file

@ -7,6 +7,8 @@ import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock; import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock;
import com.simibubi.create.content.contraptions.base.KineticBlock; import com.simibubi.create.content.contraptions.base.KineticBlock;
import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock; import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.gui.ScreenOpener;
@ -35,7 +37,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
public class SequencedGearshiftBlock extends HorizontalAxisKineticBlock implements ITE<SequencedGearshiftTileEntity> { public class SequencedGearshiftBlock extends HorizontalAxisKineticBlock implements ITE<SequencedGearshiftTileEntity>, ITransformableBlock {
public static final BooleanProperty VERTICAL = BooleanProperty.create("vertical"); public static final BooleanProperty VERTICAL = BooleanProperty.create("vertical");
public static final IntegerProperty STATE = IntegerProperty.create("state", 0, 5); public static final IntegerProperty STATE = IntegerProperty.create("state", 0, 5);
@ -166,4 +168,24 @@ public class SequencedGearshiftBlock extends HorizontalAxisKineticBlock implemen
.intValue(); .intValue();
} }
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null) {
state = mirror(state, transform.mirror);
}
if (transform.rotationAxis == Direction.Axis.Y) {
return rotate(state, transform.rotation);
}
if (transform.rotation.ordinal() % 2 == 1) {
if (transform.rotationAxis != state.getValue(HORIZONTAL_AXIS)) {
return state.cycle(VERTICAL);
} else if (state.getValue(VERTICAL)) {
return state.cycle(VERTICAL).cycle(HORIZONTAL_AXIS);
}
}
return state;
}
} }

View file

@ -13,6 +13,8 @@ import com.simibubi.create.AllTileEntities;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.processing.EmptyingByBasin; import com.simibubi.create.content.contraptions.processing.EmptyingByBasin;
import com.simibubi.create.content.contraptions.relays.belt.BeltSlicer.Feedback; import com.simibubi.create.content.contraptions.relays.belt.BeltSlicer.Feedback;
import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity.CasingType; import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity.CasingType;
@ -82,7 +84,7 @@ import net.minecraftforge.common.Tags;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
public class BeltBlock extends HorizontalKineticBlock implements ITE<BeltTileEntity>, ISpecialBlockItemRequirement { public class BeltBlock extends HorizontalKineticBlock implements ITE<BeltTileEntity>, ISpecialBlockItemRequirement, ITransformableBlock {
public static final Property<BeltSlope> SLOPE = EnumProperty.create("slope", BeltSlope.class); public static final Property<BeltSlope> SLOPE = EnumProperty.create("slope", BeltSlope.class);
public static final Property<BeltPart> PART = EnumProperty.create("part", BeltPart.class); public static final Property<BeltPart> PART = EnumProperty.create("part", BeltPart.class);
@ -594,6 +596,100 @@ public class BeltBlock extends HorizontalKineticBlock implements ITE<BeltTileEnt
return rotate; return rotate;
} }
public BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null) {
state = mirror(state, transform.mirror);
}
if (transform.rotationAxis == Direction.Axis.Y) {
return rotate(state, transform.rotation);
}
return transformInner(state, transform);
}
protected BlockState transformInner(BlockState state, StructureTransform transform) {
boolean halfTurn = transform.rotation == Rotation.CLOCKWISE_180;
Direction initialDirection = state.getValue(HORIZONTAL_FACING);
boolean diagonal =
state.getValue(SLOPE) == BeltSlope.DOWNWARD || state.getValue(SLOPE) == BeltSlope.UPWARD;
if (!diagonal) {
for (int i = 0; i < transform.rotation.ordinal(); i++) {
Direction direction = state.getValue(HORIZONTAL_FACING);
BeltSlope slope = state.getValue(SLOPE);
boolean vertical = slope == BeltSlope.VERTICAL;
boolean horizontal = slope == BeltSlope.HORIZONTAL;
boolean sideways = slope == BeltSlope.SIDEWAYS;
Direction newDirection = direction.getOpposite();
BeltSlope newSlope = BeltSlope.VERTICAL;
if (vertical) {
if (direction.getAxis() == transform.rotationAxis) {
newDirection = direction.getCounterClockWise();
newSlope = BeltSlope.SIDEWAYS;
} else {
newSlope = BeltSlope.HORIZONTAL;
newDirection = direction;
if (direction.getAxis() == Axis.Z)
newDirection = direction.getOpposite();
}
}
if (sideways) {
newDirection = direction;
if (direction.getAxis() == transform.rotationAxis)
newSlope = BeltSlope.HORIZONTAL;
else
newDirection = direction.getCounterClockWise();
}
if (horizontal) {
newDirection = direction;
if (direction.getAxis() == transform.rotationAxis)
newSlope = BeltSlope.SIDEWAYS;
else if (direction.getAxis() != Axis.Z)
newDirection = direction.getOpposite();
}
state = state.setValue(HORIZONTAL_FACING, newDirection);
state = state.setValue(SLOPE, newSlope);
}
} else if (initialDirection.getAxis() != transform.rotationAxis) {
for (int i = 0; i < transform.rotation.ordinal(); i++) {
Direction direction = state.getValue(HORIZONTAL_FACING);
Direction newDirection = direction.getOpposite();
BeltSlope slope = state.getValue(SLOPE);
boolean upward = slope == BeltSlope.UPWARD;
boolean downward = slope == BeltSlope.DOWNWARD;
// Rotate diagonal
if (direction.getAxisDirection() == AxisDirection.POSITIVE ^ downward ^ direction.getAxis() == Axis.Z) {
state = state.setValue(SLOPE, upward ? BeltSlope.DOWNWARD : BeltSlope.UPWARD);
} else {
state = state.setValue(HORIZONTAL_FACING, newDirection);
}
}
} else if (halfTurn) {
Direction direction = state.getValue(HORIZONTAL_FACING);
Direction newDirection = direction.getOpposite();
BeltSlope slope = state.getValue(SLOPE);
boolean vertical = slope == BeltSlope.VERTICAL;
if (diagonal) {
state = state.setValue(SLOPE, slope == BeltSlope.UPWARD ? BeltSlope.DOWNWARD
: slope == BeltSlope.DOWNWARD ? BeltSlope.UPWARD : slope);
} else if (vertical) {
state = state.setValue(HORIZONTAL_FACING, newDirection);
}
}
return state;
}
@Override @Override
public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) { public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) {
return false; return false;

View file

@ -6,12 +6,15 @@ import com.simibubi.create.content.contraptions.base.CasingBlock;
import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.IRotate;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock; import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock;
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel; import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
import com.simibubi.create.content.contraptions.relays.elementary.SimpleKineticTileEntity; import com.simibubi.create.content.contraptions.relays.elementary.SimpleKineticTileEntity;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement; import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.utility.VoxelShaper;
import com.tterrag.registrate.util.entry.BlockEntry; import com.tterrag.registrate.util.entry.BlockEntry;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -29,6 +32,8 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -38,7 +43,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
public class EncasedCogwheelBlock extends RotatedPillarKineticBlock public class EncasedCogwheelBlock extends RotatedPillarKineticBlock
implements ICogWheel, ITE<SimpleKineticTileEntity>, ISpecialBlockItemRequirement { implements ICogWheel, ITE<SimpleKineticTileEntity>, ISpecialBlockItemRequirement, ITransformableBlock {
public static final BooleanProperty TOP_SHAFT = BooleanProperty.create("top_shaft"); public static final BooleanProperty TOP_SHAFT = BooleanProperty.create("top_shaft");
public static final BooleanProperty BOTTOM_SHAFT = BooleanProperty.create("bottom_shaft"); public static final BooleanProperty BOTTOM_SHAFT = BooleanProperty.create("bottom_shaft");
@ -120,6 +125,16 @@ public class EncasedCogwheelBlock extends RotatedPillarKineticBlock
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
} }
@Override
public BlockState getRotatedBlockState(BlockState originalState, Direction targetedFace) {
originalState = swapShaftsForRotation(originalState, Rotation.CLOCKWISE_90, targetedFace.getAxis());
return originalState.setValue(RotatedPillarKineticBlock.AXIS,
VoxelShaper
.axisAsFace(originalState.getValue(RotatedPillarKineticBlock.AXIS))
.getClockWise(targetedFace.getAxis())
.getAxis());
}
@Override @Override
public InteractionResult onSneakWrenched(BlockState state, UseOnContext context) { public InteractionResult onSneakWrenched(BlockState state, UseOnContext context) {
if (context.getLevel().isClientSide) if (context.getLevel().isClientSide)
@ -170,6 +185,81 @@ public class EncasedCogwheelBlock extends RotatedPillarKineticBlock
return state.getValue(AXIS); return state.getValue(AXIS);
} }
public BlockState swapShafts(BlockState state) {
boolean bottom = state.getValue(BOTTOM_SHAFT);
boolean top = state.getValue(TOP_SHAFT);
state = state.setValue(BOTTOM_SHAFT, top);
state = state.setValue(TOP_SHAFT, bottom);
return state;
}
public BlockState swapShaftsForRotation(BlockState state, Rotation rotation, Direction.Axis rotationAxis) {
if (rotation == Rotation.NONE) {
return state;
}
Direction.Axis axis = state.getValue(AXIS);
if (axis == rotationAxis) {
return state;
}
if (rotation == Rotation.CLOCKWISE_180) {
return swapShafts(state);
}
boolean clockwise = rotation == Rotation.CLOCKWISE_90;
if (rotationAxis == Direction.Axis.X) {
if ( axis == Direction.Axis.Z && !clockwise
|| axis == Direction.Axis.Y && clockwise) {
return swapShafts(state);
}
} else if (rotationAxis == Direction.Axis.Y) {
if ( axis == Direction.Axis.X && !clockwise
|| axis == Direction.Axis.Z && clockwise) {
return swapShafts(state);
}
} else if (rotationAxis == Direction.Axis.Z) {
if ( axis == Direction.Axis.Y && !clockwise
|| axis == Direction.Axis.X && clockwise) {
return swapShafts(state);
}
}
return state;
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
Direction.Axis axis = state.getValue(AXIS);
if (axis == Direction.Axis.X && mirror == Mirror.FRONT_BACK
|| axis == Direction.Axis.Z && mirror == Mirror.LEFT_RIGHT) {
return swapShafts(state);
}
return state;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
state = swapShaftsForRotation(state, rotation, Direction.Axis.Y);
return super.rotate(state, rotation);
}
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null) {
state = mirror(state, transform.mirror);
}
if (transform.rotationAxis == Direction.Axis.Y) {
return rotate(state, transform.rotation);
}
state = swapShaftsForRotation(state, transform.rotation, transform.rotationAxis);
state = state.setValue(AXIS, transform.rotateAxis(state.getValue(AXIS)));
return state;
}
@Override @Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) { public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) {
return ItemRequirement return ItemRequirement

View file

@ -9,7 +9,6 @@ import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock;
import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock; import com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock;
import com.simibubi.create.foundation.utility.DirectionHelper;
import com.simibubi.create.foundation.utility.VoxelShaper; import com.simibubi.create.foundation.utility.VoxelShaper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -81,20 +80,20 @@ public interface IWrenchable {
if (targetedFace.getAxis() == Direction.Axis.Y) { if (targetedFace.getAxis() == Direction.Axis.Y) {
if (originalState.hasProperty(HorizontalAxisKineticBlock.HORIZONTAL_AXIS)) if (originalState.hasProperty(HorizontalAxisKineticBlock.HORIZONTAL_AXIS))
return originalState.setValue(HorizontalAxisKineticBlock.HORIZONTAL_AXIS, DirectionHelper return originalState.setValue(HorizontalAxisKineticBlock.HORIZONTAL_AXIS, VoxelShaper
.rotateAround(VoxelShaper.axisAsFace(originalState.getValue(HorizontalAxisKineticBlock.HORIZONTAL_AXIS)), .axisAsFace(originalState.getValue(HorizontalAxisKineticBlock.HORIZONTAL_AXIS))
targetedFace.getAxis()) .getClockWise(targetedFace.getAxis())
.getAxis()); .getAxis());
if (originalState.hasProperty(HorizontalKineticBlock.HORIZONTAL_FACING)) if (originalState.hasProperty(HorizontalKineticBlock.HORIZONTAL_FACING))
return originalState.setValue(HorizontalKineticBlock.HORIZONTAL_FACING, DirectionHelper return originalState.setValue(HorizontalKineticBlock.HORIZONTAL_FACING, originalState
.rotateAround(originalState.getValue(HorizontalKineticBlock.HORIZONTAL_FACING), targetedFace.getAxis())); .getValue(HorizontalKineticBlock.HORIZONTAL_FACING).getClockWise(targetedFace.getAxis()));
} }
if (originalState.hasProperty(RotatedPillarKineticBlock.AXIS)) if (originalState.hasProperty(RotatedPillarKineticBlock.AXIS))
return originalState.setValue(RotatedPillarKineticBlock.AXIS, return originalState.setValue(RotatedPillarKineticBlock.AXIS,
DirectionHelper VoxelShaper
.rotateAround(VoxelShaper.axisAsFace(originalState.getValue(RotatedPillarKineticBlock.AXIS)), .axisAsFace(originalState.getValue(RotatedPillarKineticBlock.AXIS))
targetedFace.getAxis()) .getClockWise(targetedFace.getAxis())
.getAxis()); .getAxis());
if (!originalState.hasProperty(DirectionalKineticBlock.FACING)) if (!originalState.hasProperty(DirectionalKineticBlock.FACING))
@ -111,7 +110,7 @@ public interface IWrenchable {
} else { } else {
do { do {
newState = newState.setValue(DirectionalKineticBlock.FACING, newState = newState.setValue(DirectionalKineticBlock.FACING,
DirectionHelper.rotateAround(newState.getValue(DirectionalKineticBlock.FACING), targetedFace.getAxis())); newState.getValue(DirectionalKineticBlock.FACING).getClockWise(targetedFace.getAxis()));
if (targetedFace.getAxis() == Direction.Axis.Y if (targetedFace.getAxis() == Direction.Axis.Y
&& newState.hasProperty(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE)) && newState.hasProperty(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE))
newState = newState.cycle(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE); newState = newState.cycle(DirectionalAxisKineticBlock.AXIS_ALONG_FIRST_COORDINATE);

View file

@ -151,7 +151,10 @@ public class ArmInteractionPoint {
if (type == null) if (type == null)
return null; return null;
BlockPos pos = NbtUtils.readBlockPos(nbt.getCompound("Pos")).offset(anchor); BlockPos pos = NbtUtils.readBlockPos(nbt.getCompound("Pos")).offset(anchor);
ArmInteractionPoint point = type.createPoint(level, pos, level.getBlockState(pos)); BlockState state = level.getBlockState(pos);
if (!type.canCreatePoint(level, pos, state))
return null;
ArmInteractionPoint point = type.createPoint(level, pos, state);
if (point == null) if (point == null)
return null; return null;
point.deserialize(nbt, anchor); point.deserialize(nbt, anchor);

View file

@ -27,6 +27,7 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
@ -39,6 +40,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.JukeboxBlock; import net.minecraft.world.level.block.JukeboxBlock;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -410,10 +412,45 @@ public class ArmTileEntity extends KineticTileEntity implements ITransformableTE
notifyUpdate(); notifyUpdate();
} }
// ClientLevel#hasChunk (and consequently #isAreaLoaded) always returns true,
// so manually check the ChunkSource to avoid weird behavior on the client side
protected boolean isAreaActuallyLoaded(BlockPos center, int range) {
if (!level.isAreaLoaded(center, range)) {
return false;
}
if (level.isClientSide) {
int minY = center.getY() - range;
int maxY = center.getY() + range;
if (maxY < level.getMinBuildHeight() || minY >= level.getMaxBuildHeight()) {
return false;
}
int minX = center.getX() - range;
int minZ = center.getZ() - range;
int maxX = center.getX() + range;
int maxZ = center.getZ() + range;
int minChunkX = SectionPos.blockToSectionCoord(minX);
int maxChunkX = SectionPos.blockToSectionCoord(maxX);
int minChunkZ = SectionPos.blockToSectionCoord(minZ);
int maxChunkZ = SectionPos.blockToSectionCoord(maxZ);
ChunkSource chunkSource = level.getChunkSource();
for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
if (!chunkSource.hasChunk(chunkX, chunkZ)) {
return false;
}
}
}
}
return true;
}
protected void initInteractionPoints() { protected void initInteractionPoints() {
if (!updateInteractionPoints || interactionPointTag == null) if (!updateInteractionPoints || interactionPointTag == null)
return; return;
if (!level.isAreaLoaded(worldPosition, getRange() + 1)) if (!isAreaActuallyLoaded(worldPosition, getRange() + 1))
return; return;
inputs.clear(); inputs.clear();
outputs.clear(); outputs.clear();
@ -506,7 +543,7 @@ public class ArmTileEntity extends KineticTileEntity implements ITransformableTE
previousTarget = previousPoint == null ? ArmAngleTarget.NO_TARGET previousTarget = previousPoint == null ? ArmAngleTarget.NO_TARGET
: previousPoint.getTargetAngles(worldPosition, ceiling); : previousPoint.getTargetAngles(worldPosition, ceiling);
if (previousPoint != null) if (previousPoint != null)
previousBaseAngle = previousPoint.getTargetAngles(worldPosition, ceiling).baseAngle; previousBaseAngle = previousTarget.baseAngle;
ArmInteractionPoint targetedPoint = getTargetedInteractionPoint(); ArmInteractionPoint targetedPoint = getTargetedInteractionPoint();
if (targetedPoint != null) if (targetedPoint != null)

View file

@ -1,7 +1,6 @@
package com.simibubi.create.foundation.block; package com.simibubi.create.foundation.block;
import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.utility.DirectionHelper;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
@ -31,7 +30,7 @@ public class WrenchableDirectionalBlock extends DirectionalBlock implements IWre
if (facing.getAxis() == targetedFace.getAxis()) if (facing.getAxis() == targetedFace.getAxis())
return originalState; return originalState;
Direction newFacing = DirectionHelper.rotateAround(facing, targetedFace.getAxis()); Direction newFacing = facing.getClockWise(targetedFace.getAxis());
return originalState.setValue(FACING, newFacing); return originalState.setValue(FACING, newFacing);
} }

View file

@ -1,82 +0,0 @@
package com.simibubi.create.foundation.utility;
import static net.minecraft.core.Direction.DOWN;
import static net.minecraft.core.Direction.EAST;
import static net.minecraft.core.Direction.NORTH;
import static net.minecraft.core.Direction.SOUTH;
import static net.minecraft.core.Direction.UP;
import static net.minecraft.core.Direction.WEST;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
/**
* A bunch of methods that got stripped out of Direction in 1.15
*
* @author Mojang
*/
public class DirectionHelper {
public static Direction rotateAround(Direction dir, Direction.Axis axis) {
switch (axis) {
case X:
if (dir != WEST && dir != EAST) {
return rotateX(dir);
}
return dir;
case Y:
if (dir != UP && dir != DOWN) {
return dir.getClockWise();
}
return dir;
case Z:
if (dir != NORTH && dir != SOUTH) {
return rotateZ(dir);
}
return dir;
default:
throw new IllegalStateException("Unable to get CW facing for axis " + axis);
}
}
public static Direction rotateX(Direction dir) {
switch (dir) {
case NORTH:
return DOWN;
case EAST:
case WEST:
default:
throw new IllegalStateException("Unable to get X-rotated facing of " + dir);
case SOUTH:
return UP;
case UP:
return NORTH;
case DOWN:
return SOUTH;
}
}
public static Direction rotateZ(Direction dir) {
switch (dir) {
case EAST:
return DOWN;
case SOUTH:
default:
throw new IllegalStateException("Unable to get Z-rotated facing of " + dir);
case WEST:
return UP;
case UP:
return EAST;
case DOWN:
return WEST;
}
}
public static Direction getPositivePerpendicular(Axis horizontalAxis) {
return horizontalAxis == Axis.X ? SOUTH : EAST;
}
}

View file

@ -28,7 +28,7 @@ public enum Pointing implements StringRepresentable {
Direction top = axis == Axis.Y ? Direction.SOUTH : Direction.UP; Direction top = axis == Axis.Y ? Direction.SOUTH : Direction.UP;
int rotations = direction.getAxisDirection() == AxisDirection.NEGATIVE ? 4 - ordinal() : ordinal(); int rotations = direction.getAxisDirection() == AxisDirection.NEGATIVE ? 4 - ordinal() : ordinal();
for (int i = 0; i < rotations; i++) for (int i = 0; i < rotations; i++)
top = DirectionHelper.rotateAround(top, axis); top = top.getClockWise(axis);
return top; return top;
} }