From 885791f878a7693056342aab0b00b65c1a8a14cc Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Wed, 29 Apr 2020 23:32:17 +0200 Subject: [PATCH] Cart Rotation Lock - Minecart assemblers can now be configured to 3 different rotation modes --- .../com/simibubi/create/AllTileEntities.java | 2 + .../com/simibubi/create/ScreenResources.java | 3 + .../foundation/utility/AngleHelper.java | 8 +-- .../contraptions/ContraptionEntity.java | 45 ++++++++---- .../mounted/CartAssemblerBlock.java | 30 ++++++-- .../mounted/CartAssemblerTileEntity.java | 65 ++++++++++++++++++ .../mounted/MountedContraption.java | 22 +++++- .../resources/assets/create/lang/en_us.json | 4 ++ .../assets/create/textures/gui/icons.png | Bin 3955 -> 4163 bytes 9 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerTileEntity.java diff --git a/src/main/java/com/simibubi/create/AllTileEntities.java b/src/main/java/com/simibubi/create/AllTileEntities.java index 2ffc992a1..53519a6c0 100644 --- a/src/main/java/com/simibubi/create/AllTileEntities.java +++ b/src/main/java/com/simibubi/create/AllTileEntities.java @@ -15,6 +15,7 @@ import com.simibubi.create.modules.contraptions.components.contraptions.bearing. import com.simibubi.create.modules.contraptions.components.contraptions.bearing.ClockworkBearingTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.bearing.MechanicalBearingTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.chassis.ChassisTileEntity; +import com.simibubi.create.modules.contraptions.components.contraptions.mounted.CartAssemblerTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.piston.MechanicalPistonTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.piston.MechanicalPistonTileEntityRenderer; import com.simibubi.create.modules.contraptions.components.contraptions.pulley.PulleyRenderer; @@ -146,6 +147,7 @@ public enum AllTileEntities { SPEED_GAUGE(SpeedGaugeTileEntity::new, AllBlocks.SPEED_GAUGE), STRESS_GAUGE(StressGaugeTileEntity::new, AllBlocks.STRESS_GAUGE), ANALOG_LEVER(AnalogLeverTileEntity::new, AllBlocks.ANALOG_LEVER), + CART_ASSEMBLER(CartAssemblerTileEntity::new, AllBlocks.CART_ASSEMBLER), // Logistics REDSTONE_BRIDGE(RedstoneLinkTileEntity::new, AllBlocks.REDSTONE_BRIDGE), diff --git a/src/main/java/com/simibubi/create/ScreenResources.java b/src/main/java/com/simibubi/create/ScreenResources.java index 3270590ab..19f7f780a 100644 --- a/src/main/java/com/simibubi/create/ScreenResources.java +++ b/src/main/java/com/simibubi/create/ScreenResources.java @@ -133,6 +133,9 @@ public enum ScreenResources { I_MOVE_PLACE(9, 1), I_MOVE_PLACE_RETURNED(10, 1), I_MOVE_NEVER_PLACE(11, 1), + I_CART_ROTATE(12, 1), + I_CART_ROTATE_PAUSED(13, 1), + I_CART_ROTATE_LOCKED(14, 1), I_DONT_REPLACE(0, 2), I_REPLACE_SOLID(1, 2), diff --git a/src/main/java/com/simibubi/create/foundation/utility/AngleHelper.java b/src/main/java/com/simibubi/create/foundation/utility/AngleHelper.java index 058c23c1e..580c4d3cb 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/AngleHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/AngleHelper.java @@ -19,16 +19,16 @@ public class AngleHelper { public static float rad(float angle) { return (float) (angle / 180 * Math.PI); } - + public static float deg(float angle) { return (float) (angle * 180 / Math.PI); } - - public static float angleLerp(float pct, float current, float target) { + + public static float angleLerp(float pct, float current, float target) { return current + getShortestAngleDiff(current, target) * pct; } - public static float getShortestAngleDiff(double current, double target) { + public static float getShortestAngleDiff(double current, double target) { current = current % 360; target = target % 360; return (float) (((((target - current) % 360) + 540) % 360) - 180); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java index 6e4f01a95..2a0afc2a0 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java @@ -12,8 +12,11 @@ import org.apache.commons.lang3.tuple.MutablePair; import com.google.common.collect.ImmutableSet; import com.simibubi.create.AllEntities; import com.simibubi.create.AllPackets; +import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.components.contraptions.bearing.BearingContraption; +import com.simibubi.create.modules.contraptions.components.contraptions.mounted.CartAssemblerTileEntity.CartMovementMode; +import com.simibubi.create.modules.contraptions.components.contraptions.mounted.MountedContraption; import com.simibubi.create.modules.contraptions.components.contraptions.piston.LinearActuatorTileEntity; import net.minecraft.block.material.PushReaction; @@ -156,6 +159,14 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } public void tickAsPassenger(Entity e) { + boolean rotationLock = false; + boolean pauseWhileRotating = false; + + if (contraption instanceof MountedContraption) { + rotationLock = ((MountedContraption) contraption).rotationMode == CartMovementMode.ROTATION_LOCKED; + pauseWhileRotating = ((MountedContraption) contraption).rotationMode == CartMovementMode.ROTATE_PAUSED; + } + Entity riding = e; while (riding.getRidingEntity() != null) riding = riding.getRidingEntity(); @@ -163,20 +174,28 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD if (riding instanceof BoatEntity) movementVector = new Vec3d(posX - prevPosX, posY - prevPosY, posZ - prevPosZ); Vec3d motion = movementVector.normalize(); + boolean rotating = false; - if (motion.length() > 0) { - targetYaw = yawFromVector(motion); - if (targetYaw < 0) - targetYaw += 360; - if (yaw < 0) - yaw += 360; + if (!rotationLock) { + if (motion.length() > 0) { + targetYaw = yawFromVector(motion); + if (targetYaw < 0) + targetYaw += 360; + if (yaw < 0) + yaw += 360; + } + + prevYaw = yaw; + yaw = angleLerp(0.4f, yaw, targetYaw); + if (Math.abs(AngleHelper.getShortestAngleDiff(yaw, targetYaw)) < 1f) + yaw = targetYaw; + else + rotating = true; } - prevYaw = yaw; - yaw = angleLerp(0.4f, yaw, targetYaw); - boolean wasStalled = isStalled(); - tickActors(movementVector); + if (!rotating || !pauseWhileRotating) + tickActors(movementVector); if (isStalled()) { if (!wasStalled) motionBeforeStall = riding.getMotion(); @@ -434,8 +453,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } @Override - protected void doWaterSplashEffect() { - } + protected void doWaterSplashEffect() {} public void preventMovedEntitiesFromGettingStuck() { Vec3d stuckTest = new Vec3d(0, -2, 0); @@ -524,8 +542,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD @Override // Make sure nothing can move contraptions out of the way - public void setMotion(Vec3d motionIn) { - } + public void setMotion(Vec3d motionIn) {} @Override public PushReaction getPushReaction() { diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java index d33f49619..a789eef59 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java @@ -1,10 +1,11 @@ package com.simibubi.create.modules.contraptions.components.contraptions.mounted; import com.simibubi.create.AllBlocks; +import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.RenderUtilityBlock; import com.simibubi.create.foundation.utility.AllShapes; -import com.simibubi.create.modules.contraptions.components.contraptions.Contraption; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; +import com.simibubi.create.modules.contraptions.components.contraptions.mounted.CartAssemblerTileEntity.CartMovementMode; import net.minecraft.block.AbstractRailBlock; import net.minecraft.block.Block; @@ -19,6 +20,7 @@ import net.minecraft.state.IProperty; import net.minecraft.state.StateContainer.Builder; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.RailShape; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.math.BlockPos; @@ -28,7 +30,7 @@ import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.IBlockReader; import net.minecraft.world.World; -public class CartAssemblerBlock extends AbstractRailBlock { +public class CartAssemblerBlock extends AbstractRailBlock implements ITE { public static IProperty RAIL_SHAPE = EnumProperty.create("shape", RailShape.class, RailShape.EAST_WEST, RailShape.NORTH_SOUTH); @@ -45,6 +47,16 @@ public class CartAssemblerBlock extends AbstractRailBlock { super.fillStateContainer(builder); } + @Override + public boolean hasTileEntity(BlockState state) { + return true; + } + + @Override + public TileEntity createTileEntity(BlockState state, IBlockReader world) { + return new CartAssemblerTileEntity(); + } + @Override public BlockState getStateForPlacement(BlockItemUseContext context) { boolean alongX = context.getPlacementHorizontalFacing().getAxis() == Axis.X; @@ -72,12 +84,15 @@ public class CartAssemblerBlock extends AbstractRailBlock { if (!cart.getPassengers().isEmpty()) return; - Contraption contraption = MountedContraption.assembleMinecart(world, pos); + MountedContraption contraption = MountedContraption.assembleMinecart(world, pos); if (contraption == null) return; if (contraption.blocks.size() == 1) return; + float initialAngle = ContraptionEntity.yawFromVector(cart.getMotion()); + + withTileEntityDo(world, pos, te -> contraption.rotationMode = CartMovementMode.values()[te.movementMode.value]); ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialAngle); entity.setPosition(pos.getX(), pos.getY(), pos.getZ()); world.addEntity(entity); @@ -129,7 +144,7 @@ public class CartAssemblerBlock extends AbstractRailBlock { public PushReaction getPushReaction(BlockState state) { return PushReaction.BLOCK; } - + @Override public boolean isNormalCube(BlockState state, IBlockReader worldIn, BlockPos pos) { return false; @@ -142,7 +157,7 @@ public class CartAssemblerBlock extends AbstractRailBlock { builder.add(BlockStateProperties.HORIZONTAL_AXIS); super.fillStateContainer(builder); } - + @Override public boolean isSolid(BlockState state) { return false; @@ -155,4 +170,9 @@ public class CartAssemblerBlock extends AbstractRailBlock { return AllBlocks.MINECART_ANCHOR.get().getDefaultState().with(BlockStateProperties.HORIZONTAL_AXIS, axis); } + @Override + public Class getTileEntityClass() { + return CartAssemblerTileEntity.class; + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerTileEntity.java new file mode 100644 index 000000000..89ae09aa9 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerTileEntity.java @@ -0,0 +1,65 @@ +package com.simibubi.create.modules.contraptions.components.contraptions.mounted; + +import java.util.List; + +import com.simibubi.create.AllTileEntities; +import com.simibubi.create.ScreenResources; +import com.simibubi.create.foundation.behaviour.CenteredSideValueBoxTransform; +import com.simibubi.create.foundation.behaviour.ValueBoxTransform; +import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; +import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.foundation.behaviour.scrollvalue.INamedIconOptions; +import com.simibubi.create.foundation.behaviour.scrollvalue.ScrollOptionBehaviour; +import com.simibubi.create.foundation.utility.Lang; + +import net.minecraft.util.Direction; + +public class CartAssemblerTileEntity extends SmartTileEntity { + + protected ScrollOptionBehaviour movementMode; + + public CartAssemblerTileEntity() { + super(AllTileEntities.CART_ASSEMBLER.type); + } + + @Override + public void addBehaviours(List behaviours) { + movementMode = new ScrollOptionBehaviour<>(CartMovementMode.class, + Lang.translate("contraptions.cart_movement_mode"), this, getMovementModeSlot()); + movementMode.requiresWrench(); + behaviours.add(movementMode); + } + + protected ValueBoxTransform getMovementModeSlot() { + return new CenteredSideValueBoxTransform((state, d) -> d == Direction.UP); + } + + public static enum CartMovementMode implements INamedIconOptions { + + ROTATE(ScreenResources.I_CART_ROTATE), + ROTATE_PAUSED(ScreenResources.I_CART_ROTATE_PAUSED), + ROTATION_LOCKED(ScreenResources.I_CART_ROTATE_LOCKED), + + ; + + private String translationKey; + private ScreenResources icon; + + private CartMovementMode(ScreenResources icon) { + this.icon = icon; + translationKey = "contraptions.cart_movement_mode." + Lang.asId(name()); + } + + @Override + public ScreenResources getIcon() { + return icon; + } + + @Override + public String getTranslationKey() { + return translationKey; + } + + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/MountedContraption.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/MountedContraption.java index fd6697d7d..0c4b56ca0 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/MountedContraption.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/MountedContraption.java @@ -7,11 +7,14 @@ import java.util.List; import org.apache.commons.lang3.tuple.Pair; import com.simibubi.create.AllBlocks; +import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.modules.contraptions.components.contraptions.AllContraptionTypes; import com.simibubi.create.modules.contraptions.components.contraptions.BlockMovementTraits; import com.simibubi.create.modules.contraptions.components.contraptions.Contraption; +import com.simibubi.create.modules.contraptions.components.contraptions.mounted.CartAssemblerTileEntity.CartMovementMode; import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.RailShape; import net.minecraft.tileentity.TileEntity; @@ -26,12 +29,14 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo; public class MountedContraption extends Contraption { + public CartMovementMode rotationMode; + @Override protected AllContraptionTypes getType() { return AllContraptionTypes.MOUNTED; } - public static Contraption assembleMinecart(World world, BlockPos pos) { + public static MountedContraption assembleMinecart(World world, BlockPos pos) { if (isFrozen()) return null; @@ -39,7 +44,7 @@ public class MountedContraption extends Contraption { if (!state.has(RAIL_SHAPE)) return null; - Contraption contraption = new MountedContraption(); + MountedContraption contraption = new MountedContraption(); if (!contraption.searchMovedStructure(world, pos, null)) return null; @@ -83,6 +88,19 @@ public class MountedContraption extends Contraption { return pair; } + @Override + public CompoundNBT writeNBT() { + CompoundNBT writeNBT = super.writeNBT(); + writeNBT.putString("RotationMode", NBTHelper.writeEnum(rotationMode)); + return writeNBT; + } + + @Override + public void readNBT(World world, CompoundNBT nbt) { + rotationMode = NBTHelper.readEnum(nbt.getString("RotationMode"), CartMovementMode.class); + super.readNBT(world, nbt); + } + @Override public void removeBlocksFromWorld(IWorld world, BlockPos offset) { super.removeBlocksFromWorld(world, offset, (pos, state) -> pos.equals(anchor)); diff --git a/src/main/resources/assets/create/lang/en_us.json b/src/main/resources/assets/create/lang/en_us.json index 6bb894769..8eab4367d 100644 --- a/src/main/resources/assets/create/lang/en_us.json +++ b/src/main/resources/assets/create/lang/en_us.json @@ -391,6 +391,10 @@ "create.contraptions.movement_mode.rotate_place": "Always Place when Stopped", "create.contraptions.movement_mode.rotate_place_returned": "Only Place near Initial Angle", "create.contraptions.movement_mode.rotate_never_place": "Only Place when Anchor Destroyed", + "create.contraptions.cart_movement_mode": "Cart Movement Mode", + "create.contraptions.cart_movement_mode.rotate": "Always face toward motion", + "create.contraptions.cart_movement_mode.rotate_paused": "Pause actors while rotating", + "create.contraptions.cart_movement_mode.rotation_locked": "Lock rotation", "create.logistics.filter": "Filter", "create.logistics.firstFrequency": "Freq. #1", diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png index 0b0183a2e7a6a8b1351d069813bcf3ed735cce33..06aa1ce240b58cd034610eb2b430b161c20fe611 100644 GIT binary patch delta 3941 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 l*z=N