From f3deb8ba85e60b8362fefeccedad5181b7abe428 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sat, 10 Oct 2020 21:12:28 +0200 Subject: [PATCH] Tilted Trains - The collision response now (semi)-supports yaw-pitch combined rotations of contraptions - Attempted collision and rendering of contraption couplings moving up and downhill - Fixed sychronization issues of a mounted contraptions' initial orientation - Contraption couplings no longer render the virtual coupling connection - Entities can no longer mount the cart connected by another carts' contraption - Contraption coupligs no longer rotate backwards when opposite couplings are added onto it - Minecarts no longer deadlock each other when one of them had stalled due to an unloaded coupling end - Cart assemblers only disassemble coupling contraptions if both carts are within an inactive cart assembler - Fixed interactions between coupling contraptions and furnace/chest minecart invs --- README.md | 4 +- .../actors/BellMovementBehaviour.java | 3 +- .../PortableStorageInterfaceMovement.java | 2 +- .../dispenser/DispenserMovementBehaviour.java | 3 +- .../MovedDefaultDispenseItemBehaviour.java | 3 +- .../deployer/DeployerMovementBehaviour.java | 3 +- .../structureMovement/Contraption.java | 14 +- .../ContraptionCollider.java | 32 +- .../structureMovement/ContraptionEntity.java | 387 +++++++++++------- .../ContraptionEntityRenderer.java | 16 +- .../ContraptionHandlerClient.java | 4 +- .../structureMovement/MovementContext.java | 8 +- .../mounted/CartAssemblerBlock.java | 178 +++++--- .../ItemHandlerModifiableFromIInventory.java | 140 ------- .../mounted/MinecartContraptionItem.java | 28 +- .../mounted/MountedContraption.java | 64 +-- .../train/CouplingHandler.java | 54 ++- .../train/CouplingRenderer.java | 6 +- .../train/MinecartSim2020.java | 26 +- .../CapabilityMinecartController.java | 2 + .../train/capability/MinecartController.java | 62 ++- .../processing/BasinMovementBehaviour.java | 14 +- .../redstone/ContactMovementBehaviour.java | 3 +- .../foundation/utility/MatrixStacker.java | 7 - 24 files changed, 591 insertions(+), 472 deletions(-) delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/ItemHandlerModifiableFromIInventory.java diff --git a/README.md b/README.md index 8e864c398..3d8590c60 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Logo

Create
- Patreon + Patreon Supported Versions License Discord @@ -19,7 +19,7 @@ Check out the wiki and in-game Tool-tips for further info on how to use these fe [](https://www.patreon.com/simibubi "Support Us") - Support for Minecraft 1.12: Not planned -- Support for Minecraft 1.16: Porting efforts will begin soon. +- Support for Minecraft 1.16: Porting efforts are making good progress. - Support for Fabric: Not planned

Find out more about Create on our Project Page

diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java index 6b93996d4..e5b89a27d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java @@ -18,8 +18,7 @@ public class BellMovementBehaviour extends MovementBehaviour { public void onSpeedChanged(MovementContext context, Vec3d oldMotion, Vec3d motion) { double dotProduct = oldMotion.dotProduct(motion); - if (dotProduct <= 0 && (context.relativeMotion.length() != 0 || context.rotation.length() == 0) - || context.firstMovement) + if (dotProduct <= 0 && (context.relativeMotion.length() != 0) || context.firstMovement) context.world.playSound(null, new BlockPos(context.position), SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS, 2.0F, 1.0F); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java index f91b4dca7..a3ff3fa8b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java @@ -138,7 +138,7 @@ public class PortableStorageInterfaceMovement extends MovementBehaviour { private Optional getCurrentFacingIfValid(MovementContext context) { Vec3d directionVec = new Vec3d(context.state.get(PortableStorageInterfaceBlock.FACING) .getDirectionVec()); - directionVec = VecHelper.rotate(directionVec, context.rotation.x, context.rotation.y, context.rotation.z); + directionVec = context.rotation.apply(directionVec); Direction facingFromVector = Direction.getFacingFromVector(directionVec.x, directionVec.y, directionVec.z); if (directionVec.distanceTo(new Vec3d(facingFromVector.getDirectionVec())) > 1 / 8f) return Optional.empty(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java index ac78f32f6..5a608e69d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java @@ -5,7 +5,6 @@ import java.util.HashMap; import javax.annotation.ParametersAreNonnullByDefault; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; -import com.simibubi.create.foundation.utility.VecHelper; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.Block; @@ -63,7 +62,7 @@ public class DispenserMovementBehaviour extends DropperMovementBehaviour { } Vec3d facingVec = new Vec3d(context.state.get(DispenserBlock.FACING).getDirectionVec()); - facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z); + facingVec = context.rotation.apply(facingVec); facingVec.normalize(); Direction clostestFacing = Direction.getFacingFromVector(facingVec.x, facingVec.y, facingVec.z); ContraptionBlockSource blockSource = new ContraptionBlockSource(context, pos, clostestFacing); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java index bcd4e4b59..b66d3bb75 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java @@ -1,7 +1,6 @@ package com.simibubi.create.content.contraptions.components.actors.dispenser; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; -import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.DispenserBlock; import net.minecraft.entity.item.ItemEntity; @@ -37,7 +36,7 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha @Override public ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos) { Vec3d facingVec = new Vec3d(context.state.get(DispenserBlock.FACING).getDirectionVec()); - facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z); + facingVec = context.rotation.apply(facingVec); facingVec.normalize(); Direction closestToFacing = getClosestFacingDirection(facingVec); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java index 11a175391..9dd309231 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java @@ -13,7 +13,6 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Mov import com.simibubi.create.content.logistics.item.filter.FilterItem; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.utility.NBTHelper; -import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.entity.player.PlayerInventory; @@ -52,7 +51,7 @@ public class DeployerMovementBehaviour extends MovementBehaviour { public void activate(MovementContext context, BlockPos pos, DeployerFakePlayer player, Mode mode) { Vec3d facingVec = new Vec3d(context.state.get(DeployerBlock.FACING) .getDirectionVec()); - facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z); + facingVec = context.rotation.apply(facingVec); Vec3d vec = context.position.subtract(facingVec.scale(2)); player.rotationYaw = ContraptionEntity.yawFromVector(facingVec); player.rotationPitch = ContraptionEntity.pitchFromVector(facingVec) - 90; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index 76ea8832a..fadd0ef75 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -332,9 +332,14 @@ public abstract class Contraption { frontier.add(otherPartPos); } + // Cart assemblers attach themselves + BlockState stateBelow = world.getBlockState(pos.down()); + if (!visited.contains(pos.down()) && AllBlocks.CART_ASSEMBLER.has(stateBelow)) + frontier.add(pos.down()); + Map superglue = SuperGlueHandler.gatherGlue(world, pos); - // Slime blocks drag adjacent blocks if possible + // Slime blocks and super glue drag adjacent blocks if possible boolean isSlimeBlock = state.getBlock() instanceof SlimeBlock; for (Direction offset : Direction.values()) { BlockPos offsetPos = pos.offset(offset); @@ -348,16 +353,13 @@ public abstract class Contraption { } boolean wasVisited = visited.contains(offsetPos); - boolean isMinecartAssembler = AllBlocks.CART_ASSEMBLER.has(blockState) && offset == Direction.DOWN; boolean faceHasGlue = superglue.containsKey(offset); boolean blockAttachedTowardsFace = BlockMovementTraits.isBlockAttachedTowards(blockState, offset.getOpposite()); boolean brittle = BlockMovementTraits.isBrittle(blockState); - if (!wasVisited - && ((isSlimeBlock && !brittle) || blockAttachedTowardsFace || faceHasGlue || isMinecartAssembler)) + if (!wasVisited && ((isSlimeBlock && !brittle) || blockAttachedTowardsFace || faceHasGlue)) frontier.add(offsetPos); - if (faceHasGlue) addGlue(superglue.get(offset)); } @@ -763,7 +765,7 @@ public abstract class Contraption { ctx.position = null; ctx.motion = Vec3d.ZERO; ctx.relativeMotion = Vec3d.ZERO; - ctx.rotation = Vec3d.ZERO; + ctx.rotation = v -> v; }); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index d273554b9..ff77620b8 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -32,6 +32,7 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.ReuseableStream; import net.minecraft.util.math.AxisAlignedBB; @@ -71,8 +72,18 @@ public class ContraptionCollider { Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO); double conRotX = contraptionRotation.x; - double conRotY = contraptionRotation.y; + double conRotY = contraptionRotation.y + contraptionEntity.getInitialYaw(); double conRotZ = contraptionRotation.z; + + double reverseYaw = 0; + + // Collision algorithm does not support rotation around two axes -> rotate + // entities manually + if (conRotZ != 0 && contraptionRotation.y != 0) { + reverseYaw = contraptionRotation.y; + conRotY = contraptionEntity.getInitialYaw(); + } + Vec3d contraptionCentreOffset = contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0); boolean axisAlignedCollision = contraptionRotation.equals(Vec3d.ZERO); Matrix3d rotation = null; @@ -100,13 +111,15 @@ public class ContraptionCollider { Vec3d centerY = new Vec3d(0, entityBounds.getYSize() / 2, 0); Vec3d motion = entity.getMotion(); - Vec3d position = entityPosition.subtract(contraptionCentreOffset) - .add(centerY); + Vec3d position = entityPosition; + position = position.subtract(contraptionCentreOffset); + position = position.add(centerY); position = position.subtract(contraptionPosition); + position = VecHelper.rotate(position, -reverseYaw, Axis.Y); position = rotation.transform(position); - position = position.add(centerOfBlock) - .subtract(centerY) - .subtract(entityPosition); + position = position.add(centerOfBlock); + position = position.subtract(centerY); + position = position.subtract(entityPosition); // Find all potential block shapes to collide with AxisAlignedBB localBB = entityBounds.offset(position) @@ -186,11 +199,13 @@ public class ContraptionCollider { Vec3d totalResponse = collisionResponse.getValue(); Vec3d motionResponse = allowedMotion.getValue(); boolean hardCollision = !totalResponse.equals(Vec3d.ZERO); + rotation.transpose(); motionResponse = rotation.transform(motionResponse) .add(contraptionMotion); totalResponse = rotation.transform(totalResponse); + totalResponse = VecHelper.rotate(totalResponse, reverseYaw, Axis.Y); rotation.transpose(); if (futureCollision.isTrue() && playerType != PlayerType.SERVER) { @@ -206,7 +221,7 @@ public class ContraptionCollider { entity.fallDistance = 0; entity.onGround = true; contraptionEntity.collidingEntities.add(entity); - if (playerType != PlayerType.SERVER) + if (playerType != PlayerType.SERVER) contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition); } @@ -235,6 +250,7 @@ public class ContraptionCollider { continue; } + totalResponse = totalResponse.add(contactPointMotion); Vec3d allowedMovement = getAllowedMovement(totalResponse, entity); contraptionEntity.collidingEntities.add(entity); @@ -245,7 +261,7 @@ public class ContraptionCollider { if (playerType != PlayerType.CLIENT) continue; - + double d0 = entity.getX() - entity.prevPosX - contactPointMotion.x; double d1 = entity.getZ() - entity.prevPosZ - contactPointMotion.z; float limbSwing = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java index ed74360c3..ea4a3e165 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java @@ -25,6 +25,8 @@ import com.simibubi.create.content.contraptions.components.structureMovement.tra import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.utility.AngleHelper; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; @@ -33,7 +35,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.IProjectile; -import net.minecraft.entity.item.BoatEntity; import net.minecraft.entity.item.HangingEntity; import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.entity.item.minecart.FurnaceMinecartEntity; @@ -49,10 +50,12 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.network.datasync.IDataSerializer; import net.minecraft.tags.BlockTags; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.DamageSource; import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; @@ -69,26 +72,48 @@ import net.minecraftforge.fml.network.PacketDistributor; public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnData { + public static final IDataSerializer> OPTIONAL_DIRECTION = + new IDataSerializer>() { + + public void write(PacketBuffer buffer, Optional opt) { + buffer.writeVarInt(opt.map(Direction::ordinal) + .orElse(-1) + 1); + } + + public Optional read(PacketBuffer buffer) { + int i = buffer.readVarInt(); + return i == 0 ? Optional.empty() : Optional.of(Direction.values()[i - 1]); + } + + public Optional copyValue(Optional opt) { + return Optional.ofNullable(opt.orElse(null)); + } + }; + + static { + DataSerializers.registerSerializer(OPTIONAL_DIRECTION); + } + + final List collidingEntities = new ArrayList<>(); + protected Contraption contraption; - protected float initialAngle; - protected float forcedAngle; protected BlockPos controllerPos; protected Vec3d motionBeforeStall; + protected boolean forceAngle; protected boolean stationary; protected boolean initialized; - final List collidingEntities = new ArrayList<>(); private boolean isSerializingFurnaceCart; private boolean attachedExtraInventories; private boolean prevPosInvalid; private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL); + private static final DataParameter STALLED = EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.BOOLEAN); - private static final DataParameter> COUPLING = EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.OPTIONAL_UNIQUE_ID); - private static final DataParameter> COUPLED_CART = - EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.OPTIONAL_UNIQUE_ID); + private static final DataParameter> INITIAL_ORIENTATION = + EntityDataManager.createKey(ContraptionEntity.class, ContraptionEntity.OPTIONAL_DIRECTION); public float prevYaw; public float prevPitch; @@ -108,23 +133,15 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD stationary = entityTypeIn == AllEntityTypes.STATIONARY_CONTRAPTION.get(); isSerializingFurnaceCart = false; attachedExtraInventories = false; - forcedAngle = -1; prevPosInvalid = true; } - public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) { + public static ContraptionEntity createMounted(World world, Contraption contraption, + Optional initialOrientation) { ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.CONTRAPTION.get(), world); entity.contraptionCreated(contraption); - entity.initialAngle = initialAngle; - entity.forceYaw(initialAngle); - return entity; - } - - public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle, - Direction facing) { - ContraptionEntity entity = createMounted(world, contraption, initialAngle); - entity.forcedAngle = facing.getHorizontalAngle(); - entity.forceYaw(entity.forcedAngle); + initialOrientation.ifPresent(entity::setInitialOrientation); + entity.startAtInitialYaw(); return entity; } @@ -134,6 +151,10 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD return entity; } + public void reOrientate(Direction newInitialAngle) { + setInitialOrientation(newInitialAngle); + } + protected void contraptionCreated(Contraption contraption) { this.contraption = contraption; if (contraption == null) @@ -220,7 +241,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD BlockPos seat = contraption.getSeat(passenger.getUniqueID()); if (seat == null) return null; - Vec3d transformedVector = toGlobalVector(new Vec3d(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5)) + Vec3d transformedVector = toGlobalVector(new Vec3d(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5), 1) .add(VecHelper.getCenterOf(BlockPos.ZERO)) .subtract(0.5, ySize, 0.5); return transformedVector; @@ -268,20 +289,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD return true; } - public Vec3d toGlobalVector(Vec3d localVec) { + public Vec3d toGlobalVector(Vec3d localVec, float partialTicks) { Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO); localVec = localVec.subtract(rotationOffset); - localVec = VecHelper.rotate(localVec, getRotationVec()); + localVec = applyRotation(localVec, partialTicks); localVec = localVec.add(rotationOffset) .add(getAnchorVec()); return localVec; } - public Vec3d toLocalVector(Vec3d globalVec) { + public Vec3d toLocalVector(Vec3d globalVec, float partialTicks) { Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO); globalVec = globalVec.subtract(getAnchorVec()) .subtract(rotationOffset); - globalVec = VecHelper.rotate(globalVec, getRotationVec().scale(-1)); + globalVec = reverseRotation(globalVec, partialTicks); globalVec = globalVec.add(rotationOffset); return globalVec; } @@ -311,7 +332,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD if (getMotion().length() < 1 / 4098f) setMotion(Vec3d.ZERO); - move(getMotion().x, getMotion().y, getMotion().z); if (ContraptionCollider.collideBlocks(this)) getController().collided(); @@ -323,6 +343,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD prevRoll = roll; super.tick(); + } public void tickAsPassenger(Entity e) { @@ -330,51 +351,68 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD boolean pauseWhileRotating = false; boolean rotating = false; boolean wasStalled = isStalled(); - - Entity riding = e; - while (riding.getRidingEntity() != null) - riding = riding.getRidingEntity(); - if (!attachedExtraInventories) { - contraption.addExtraInventories(riding); - attachedExtraInventories = true; - } - if (contraption instanceof MountedContraption) { MountedContraption mountedContraption = (MountedContraption) contraption; rotationLock = mountedContraption.rotationMode == CartMovementMode.ROTATION_LOCKED; pauseWhileRotating = mountedContraption.rotationMode == CartMovementMode.ROTATE_PAUSED; } + Entity riding = e; + while (riding.getRidingEntity() != null) + riding = riding.getRidingEntity(); + boolean isOnCoupling = false; UUID couplingId = getCouplingId(); isOnCoupling = couplingId != null && riding instanceof AbstractMinecartEntity; + if (!attachedExtraInventories) { + attachInventoriesFromRidingCarts(riding, isOnCoupling, couplingId); + attachedExtraInventories = true; + } + if (isOnCoupling) { -// MinecartCoupling coupling = MinecartCouplingHandler.getCoupling(world, couplingId); -// if (coupling != null && coupling.areBothEndsPresent()) { -// boolean notOnMainCart = !coupling.getId() -// .equals(riding.getUniqueID()); -// Vec3d positionVec = coupling.asCouple() -// .get(notOnMainCart) -// .getPositionVec(); -// prevYaw = yaw; -// prevPitch = pitch; -// double diffZ = positionVec.z - riding.getZ(); -// double diffX = positionVec.x - riding.getX(); -// yaw = (float) (MathHelper.atan2(diffZ, diffX) * 180 / Math.PI); -// pitch = (float) (Math.atan2(positionVec.y - getY(), Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 -// / Math.PI); -// -// if (notOnMainCart) { -// yaw += 180; -// } -// } + Couple coupledCarts = getCoupledCartsIfPresent(); + if (coupledCarts != null) { + + Vec3d positionVec = coupledCarts.getFirst() + .cart() + .getPositionVec(); + Vec3d coupledVec = coupledCarts.getSecond() + .cart() + .getPositionVec(); + + double diffX = positionVec.x - coupledVec.x; + double diffY = positionVec.y - coupledVec.y; + double diffZ = positionVec.z - coupledVec.z; + + prevYaw = yaw; + prevPitch = pitch; + yaw = (float) (MathHelper.atan2(diffZ, diffX) * 180 / Math.PI); + pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI); + + if (couplingId.equals(riding.getUniqueID())) { + pitch *= -1; + yaw += 180; + } + + } + } else if (!wasStalled) { Vec3d movementVector = riding.getMotion(); - if (riding instanceof BoatEntity) + if (!(riding instanceof AbstractMinecartEntity)) movementVector = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ); Vec3d motion = movementVector.normalize(); + if (!dataManager.get(INITIAL_ORIENTATION) + .isPresent() && !world.isRemote) { + if (motion.length() > 0) { + Direction facingFromVector = Direction.getFacingFromVector(motion.x, motion.y, motion.z); + if (facingFromVector.getAxis() + .isHorizontal()) + setInitialOrientation(facingFromVector); + } + } + if (!rotationLock) { if (motion.length() > 0) { targetYaw = yawFromVector(motion); @@ -415,52 +453,109 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } } - if (!isStalled() && (riding instanceof FurnaceMinecartEntity)) { - FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) riding; + if (world.isRemote) + return; - // Notify to not trigger serialization side-effects - isSerializingFurnaceCart = true; - CompoundNBT nbt = furnaceCart.serializeNBT(); - isSerializingFurnaceCart = false; - - int fuel = nbt.getInt("Fuel"); - int fuelBefore = fuel; - double pushX = nbt.getDouble("PushX"); - double pushZ = nbt.getDouble("PushZ"); - - int i = MathHelper.floor(furnaceCart.getX()); - int j = MathHelper.floor(furnaceCart.getY()); - int k = MathHelper.floor(furnaceCart.getZ()); - if (furnaceCart.world.getBlockState(new BlockPos(i, j - 1, k)) - .isIn(BlockTags.RAILS)) - --j; - - BlockPos blockpos = new BlockPos(i, j, k); - BlockState blockstate = this.world.getBlockState(blockpos); - if (furnaceCart.canUseRail() && blockstate.isIn(BlockTags.RAILS)) - if (fuel > 1) - riding.setMotion(riding.getMotion() - .normalize() - .scale(1)); - if (fuel < 5 && contraption != null) { - ItemStack coal = ItemHelper.extract(contraption.inventory, FUEL_ITEMS, 1, false); - if (!coal.isEmpty()) - fuel += 3600; - } - - if (fuel != fuelBefore || pushX != 0 || pushZ != 0) { - nbt.putInt("Fuel", fuel); - nbt.putDouble("PushX", 0); - nbt.putDouble("PushZ", 0); - furnaceCart.deserializeNBT(nbt); + if (!isStalled()) { + if (isOnCoupling) { + Couple coupledCarts = getCoupledCartsIfPresent(); + if (coupledCarts == null) + return; + coupledCarts.map(MinecartController::cart) + .forEach(this::powerFurnaceCartWithFuelFromStorage); + return; } + powerFurnaceCartWithFuelFromStorage(riding); } } + protected void powerFurnaceCartWithFuelFromStorage(Entity riding) { + if (!(riding instanceof FurnaceMinecartEntity)) + return; + FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) riding; + + // Notify to not trigger serialization side-effects + isSerializingFurnaceCart = true; + CompoundNBT nbt = furnaceCart.serializeNBT(); + isSerializingFurnaceCart = false; + + int fuel = nbt.getInt("Fuel"); + int fuelBefore = fuel; + double pushX = nbt.getDouble("PushX"); + double pushZ = nbt.getDouble("PushZ"); + + int i = MathHelper.floor(furnaceCart.getX()); + int j = MathHelper.floor(furnaceCart.getY()); + int k = MathHelper.floor(furnaceCart.getZ()); + if (furnaceCart.world.getBlockState(new BlockPos(i, j - 1, k)) + .isIn(BlockTags.RAILS)) + --j; + + BlockPos blockpos = new BlockPos(i, j, k); + BlockState blockstate = this.world.getBlockState(blockpos); + if (furnaceCart.canUseRail() && blockstate.isIn(BlockTags.RAILS)) + if (fuel > 1) + riding.setMotion(riding.getMotion() + .normalize() + .scale(1)); + if (fuel < 5 && contraption != null) { + ItemStack coal = ItemHelper.extract(contraption.inventory, FUEL_ITEMS, 1, false); + if (!coal.isEmpty()) + fuel += 3600; + } + + if (fuel != fuelBefore || pushX != 0 || pushZ != 0) { + nbt.putInt("Fuel", fuel); + nbt.putDouble("PushX", 0); + nbt.putDouble("PushZ", 0); + furnaceCart.deserializeNBT(nbt); + } + } + + @Nullable + public Couple getCoupledCartsIfPresent() { + UUID couplingId = getCouplingId(); + if (couplingId == null) + return null; + MinecartController controller = CapabilityMinecartController.getIfPresent(world, couplingId); + if (controller == null || !controller.isPresent()) + return null; + UUID coupledCart = controller.getCoupledCart(true); + MinecartController coupledController = CapabilityMinecartController.getIfPresent(world, coupledCart); + if (coupledController == null || !coupledController.isPresent()) + return null; + return Couple.create(controller, coupledController); + } + + protected void attachInventoriesFromRidingCarts(Entity riding, boolean isOnCoupling, UUID couplingId) { + if (isOnCoupling) { + Couple coupledCarts = getCoupledCartsIfPresent(); + if (coupledCarts == null) + return; + coupledCarts.map(MinecartController::cart) + .forEach(contraption::addExtraInventories); + return; + } + contraption.addExtraInventories(riding); + } + + public Vec3d applyRotation(Vec3d localPos, float partialTicks) { + localPos = VecHelper.rotate(localPos, getRoll(partialTicks), Axis.X); + localPos = VecHelper.rotate(localPos, getInitialYaw(), Axis.Y); + localPos = VecHelper.rotate(localPos, getPitch(partialTicks), Axis.Z); + localPos = VecHelper.rotate(localPos, getYaw(partialTicks), Axis.Y); + return localPos; + } + + public Vec3d reverseRotation(Vec3d localPos, float partialTicks) { + localPos = VecHelper.rotate(localPos, -getYaw(partialTicks), Axis.Y); + localPos = VecHelper.rotate(localPos, -getPitch(partialTicks), Axis.Z); + localPos = VecHelper.rotate(localPos, -getInitialYaw(), Axis.Y); + localPos = VecHelper.rotate(localPos, -getRoll(partialTicks), Axis.X); + return localPos; + } + public void tickActors() { - Vec3d rotationVec = getRotationVec(); - Vec3d reversedRotationVec = rotationVec.scale(-1); - Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO); boolean stalledPreviously = contraption.stalled; if (!world.isRemote) @@ -471,12 +566,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD BlockInfo blockInfo = pair.left; MovementBehaviour actor = Contraption.getMovement(blockInfo.state); - Vec3d actorPosition = new Vec3d(blockInfo.pos); - actorPosition = actorPosition.add(actor.getActiveAreaOffset(context)); - actorPosition = VecHelper.rotate(actorPosition, rotationVec); - actorPosition = actorPosition.add(rotationOffset) - .add(getAnchorVec()); - + Vec3d actorPosition = toGlobalVector(VecHelper.getCenterOf(blockInfo.pos) + .add(actor.getActiveAreaOffset(context)), 1); boolean newPosVisited = false; BlockPos gridPosition = new BlockPos(actorPosition); Vec3d oldMotion = context.motion; @@ -486,7 +577,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD if (previousPosition != null) { context.motion = actorPosition.subtract(previousPosition); Vec3d relativeMotion = context.motion; - relativeMotion = VecHelper.rotate(relativeMotion, reversedRotationVec); + relativeMotion = reverseRotation(relativeMotion, 1); context.relativeMotion = relativeMotion; newPosVisited = !new BlockPos(previousPosition).equals(gridPosition) || context.relativeMotion.length() > 0 && context.firstMovement; @@ -514,7 +605,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } } - context.rotation = rotationVec; + context.rotation = v -> applyRotation(v, 1); context.position = actorPosition; if (actor.isActive(context)) { @@ -561,6 +652,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD @Override public void notifyDataManagerChange(DataParameter key) { super.notifyDataManagerChange(key); + if (key == INITIAL_ORIENTATION) + startAtInitialYaw(); } public void rotate(double roll, double yaw, double pitch) { @@ -599,7 +692,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD public float getYaw(float partialTicks) { return (getRidingEntity() == null ? 1 : -1) - * (partialTicks == 1.0F ? yaw : angleLerp(partialTicks, prevYaw, yaw)) + initialAngle; + * (partialTicks == 1.0F ? yaw : angleLerp(partialTicks, prevYaw, yaw)); } public float getPitch(float partialTicks) { @@ -620,16 +713,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD protected void registerData() { this.dataManager.register(STALLED, false); this.dataManager.register(COUPLING, Optional.empty()); - this.dataManager.register(COUPLED_CART, Optional.empty()); + this.dataManager.register(INITIAL_ORIENTATION, Optional.empty()); } @Override protected void readAdditional(CompoundNBT compound) { initialized = compound.getBoolean("Initialized"); contraption = Contraption.fromNBT(world, compound.getCompound("Contraption")); - initialAngle = compound.getFloat("InitialAngle"); - forceYaw(compound.contains("ForcedYaw") ? compound.getFloat("ForcedYaw") : initialAngle); dataManager.set(STALLED, compound.getBoolean("Stalled")); + + if (compound.contains("InitialOrientation")) + setInitialOrientation(NBTHelper.readEnum(compound, "InitialOrientation", Direction.class)); + if (compound.contains("ForceYaw")) + startAtYaw(compound.getFloat("ForceYaw")); + ListNBT vecNBT = compound.getList("CachedMotion", 6); if (!vecNBT.isEmpty()) { motionBeforeStall = new Vec3d(vecNBT.getDouble(0), vecNBT.getDouble(1), vecNBT.getDouble(2)); @@ -637,20 +734,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD targetYaw = prevYaw = yaw += yawFromVector(motionBeforeStall); setMotion(Vec3d.ZERO); } + if (compound.contains("Controller")) controllerPos = NBTUtil.readBlockPos(compound.getCompound("Controller")); - - if (compound.contains("OnCoupling")) { - setCouplingId(NBTUtil.readUniqueId(compound.getCompound("OnCoupling"))); - setCoupledCart(NBTUtil.readUniqueId(compound.getCompound("CoupledCart"))); - } else { - setCouplingId(null); - setCoupledCart(null); - } + setCouplingId( + compound.contains("OnCoupling") ? NBTUtil.readUniqueId(compound.getCompound("OnCoupling")) : null); } - public void forceYaw(float forcedYaw) { - targetYaw = yaw = prevYaw = forcedYaw; + public void startAtInitialYaw() { + startAtYaw(getInitialYaw()); + } + + public void startAtYaw(float yaw) { + targetYaw = this.yaw = prevYaw = yaw; + forceAngle = true; } public void checkController() { @@ -679,17 +776,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD newDoubleNBTList(motionBeforeStall.x, motionBeforeStall.y, motionBeforeStall.z)); if (controllerPos != null) compound.put("Controller", NBTUtil.writeBlockPos(controllerPos)); - if (forcedAngle != -1) - compound.putFloat("ForcedYaw", forcedAngle); - compound.putFloat("InitialAngle", initialAngle); + Optional optional = dataManager.get(INITIAL_ORIENTATION); + if (optional.isPresent()) + NBTHelper.writeEnum(compound, "InitialOrientation", optional.get()); + if (forceAngle) { + compound.putFloat("ForceYaw", yaw); + forceAngle = false; + } + compound.putBoolean("Stalled", isStalled()); compound.putBoolean("Initialized", initialized); - if (getCouplingId() != null) { + if (getCouplingId() != null) compound.put("OnCoupling", NBTUtil.writeUniqueId(getCouplingId())); - compound.put("CoupledCart", NBTUtil.writeUniqueId(getCoupledCart())); - } } @Override @@ -715,7 +815,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD if (getContraption() != null) { remove(); BlockPos offset = new BlockPos(getAnchorVec().add(.5, .5, .5)); - Vec3d rotation = getRotationVec(); + Vec3d rotation = getRotationVec().add(0, getInitialYaw(), 0); StructureTransform transform = new StructureTransform(offset, rotation); contraption.addBlocksToWorld(world, transform); contraption.addPassengersToWorld(world, transform, getPassengers()); @@ -725,7 +825,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD Vec3d positionVec = getPositionVec(); Vec3d localVec = entity.getPositionVec() .subtract(positionVec); - localVec = VecHelper.rotate(localVec, getRotationVec().scale(-1)); + localVec = VecHelper.rotate(localVec, rotation.scale(-1)); Vec3d transformed = transform.apply(localVec); entity.setPositionAndUpdate(transformed.x, transformed.y, transformed.z); } @@ -844,8 +944,18 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD return false; } - public float getInitialAngle() { - return initialAngle; + public void setInitialOrientation(Direction direction) { + dataManager.set(INITIAL_ORIENTATION, Optional.of(direction)); + } + + public Optional getInitialOrientation() { + return dataManager.get(INITIAL_ORIENTATION); + } + + public float getInitialYaw() { + return dataManager.get(INITIAL_ORIENTATION) + .orElse(Direction.SOUTH) + .getHorizontalAngle(); } public Vec3d getRotationVec() { @@ -863,18 +973,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD public Vec3d getContactPointMotion(Vec3d globalContactPoint) { if (prevPosInvalid) return Vec3d.ZERO; - - Vec3d positionVec = getPositionVec(); - Vec3d conMotion = positionVec.subtract(getPrevPositionVec()); - Vec3d conAngularMotion = getRotationVec().subtract(getPrevRotationVec()); - Vec3d contraptionCentreOffset = stationary ? VecHelper.getCenterOf(BlockPos.ZERO) : Vec3d.ZERO.add(0, 0.5, 0); - Vec3d contactPoint = globalContactPoint.subtract(contraptionCentreOffset) - .subtract(positionVec); - contactPoint = VecHelper.rotate(contactPoint, conAngularMotion.x, conAngularMotion.y, conAngularMotion.z); - contactPoint = contactPoint.add(positionVec) - .add(contraptionCentreOffset) - .add(conMotion); - return contactPoint.subtract(globalContactPoint); + Vec3d contactPoint = toGlobalVector(toLocalVector(globalContactPoint, 0), 1); + return contactPoint.subtract(globalContactPoint) + .add(getPositionVec().subtract(getPrevPositionVec())); } public boolean canCollideWith(Entity e) { @@ -915,16 +1016,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD dataManager.set(COUPLING, Optional.ofNullable(id)); } - @Nullable - public UUID getCoupledCart() { - Optional uuid = dataManager.get(COUPLED_CART); - return uuid.isPresent() ? uuid.get() : null; - } - - public void setCoupledCart(UUID id) { - dataManager.set(COUPLED_CART, Optional.ofNullable(id)); - } - @Override public boolean isOnePlayerRiding() { return false; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java index af7aff573..7d7cbeba9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java @@ -39,13 +39,10 @@ public class ContraptionEntityRenderer extends EntityRenderer MatrixStack msLocal = getLocalTransform(entity); MatrixStack[] matrixStacks = new MatrixStack[] { ms, msLocal }; - float degYaw = entity.getYaw(partialTicks); - float degPitch = entity.getPitch(partialTicks); - float degRoll = entity.getRoll(partialTicks); - - float angleYaw = (float) (degYaw / 180 * Math.PI); - float anglePitch = (float) (degPitch / 180 * Math.PI); - float angleRoll = (float) (degRoll / 180 * Math.PI); + float angleInitialYaw = entity.getInitialYaw(); + float angleYaw = entity.getYaw(partialTicks); + float anglePitch = entity.getPitch(partialTicks); + float angleRoll = entity.getRoll(partialTicks); ms.push(); Entity ridingEntity = entity.getRidingEntity(); @@ -80,7 +77,10 @@ public class ContraptionEntityRenderer extends EntityRenderer MatrixStacker.of(stack) .nudge(entity.getEntityId()) .centre() - .rotateRadians(angleRoll, angleYaw, anglePitch) + .rotateY(angleYaw) + .rotateZ(anglePitch) + .rotateY(angleInitialYaw) + .rotateX(angleRoll) .unCentre(); ContraptionRenderer.render(entity.world, entity.getContraption(), ms, msLocal, buffers); ms.pop(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java index 191f19bc3..82fa74d47 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java @@ -78,8 +78,8 @@ public class ContraptionHandlerClient { for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class, new AxisAlignedBB(origin, target))) { - Vec3d localOrigin = contraptionEntity.toLocalVector(origin); - Vec3d localTarget = contraptionEntity.toLocalVector(target); + Vec3d localOrigin = contraptionEntity.toLocalVector(origin, 1); + Vec3d localTarget = contraptionEntity.toLocalVector(target, 1); Contraption contraption = contraptionEntity.getContraption(); MutableObject mutableResult = new MutableObject<>(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java index c3228b4c5..def5748e9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java @@ -1,5 +1,7 @@ package com.simibubi.create.content.contraptions.components.structureMovement; +import java.util.function.UnaryOperator; + import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; @@ -15,7 +17,7 @@ public class MovementContext { public Vec3d position; public Vec3d motion; public Vec3d relativeMotion; - public Vec3d rotation; + public UnaryOperator rotation; public World world; public BlockState state; public BlockPos localPos; @@ -36,7 +38,7 @@ public class MovementContext { firstMovement = true; motion = Vec3d.ZERO; relativeMotion = Vec3d.ZERO; - rotation = Vec3d.ZERO; + rotation = v -> v; position = null; data = new CompoundNBT(); stall = false; @@ -56,7 +58,6 @@ public class MovementContext { MovementContext context = new MovementContext(world, info); context.motion = VecHelper.readNBT(nbt.getList("Motion", NBT.TAG_DOUBLE)); context.relativeMotion = VecHelper.readNBT(nbt.getList("RelativeMotion", NBT.TAG_DOUBLE)); - context.rotation = VecHelper.readNBT(nbt.getList("Rotation", NBT.TAG_DOUBLE)); if (nbt.contains("Position")) context.position = VecHelper.readNBT(nbt.getList("Position", NBT.TAG_DOUBLE)); context.stall = nbt.getBoolean("Stall"); @@ -68,7 +69,6 @@ public class MovementContext { public CompoundNBT writeToNBT(CompoundNBT nbt) { nbt.put("Motion", VecHelper.writeNBT(motion)); nbt.put("RelativeMotion", VecHelper.writeNBT(relativeMotion)); - nbt.put("Rotation", VecHelper.writeNBT(rotation)); if (position != null) nbt.put("Position", VecHelper.writeNBT(position)); nbt.putBoolean("Stall", stall); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java index 36fb6b837..249e4e84f 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java @@ -2,6 +2,8 @@ package com.simibubi.create.content.contraptions.components.structureMovement.mo import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -12,11 +14,15 @@ import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandler; +import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController; +import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController; import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.foundation.block.ITE; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.AbstractRailBlock; @@ -58,6 +64,7 @@ import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.loot.LootContext; import net.minecraft.world.storage.loot.LootParameters; +import net.minecraftforge.common.util.LazyOptional; public class CartAssemblerBlock extends AbstractRailBlock implements ITE, IWrenchable, ISpecialBlockItemRequirement { @@ -116,56 +123,64 @@ public class CartAssemblerBlock extends AbstractRailBlock AbstractMinecartEntity cart) { if (!canAssembleTo(cart)) return; + if (world.isRemote) + return; withTileEntityDo(world, pos, te -> { - if (te.isMinecartUpdateValid()) { - switch (state.get(RAIL_TYPE)) { - case POWERED_RAIL: - if (state.get(POWERED)) { - assemble(world, pos, cart); - Direction facing = cart.getAdjustedHorizontalFacing(); - float speed = getRailMaxSpeed(state, world, pos, cart); - cart.setMotion(facing.getXOffset() * speed, facing.getYOffset() * speed, - facing.getZOffset() * speed); - } else { - disassemble(world, pos, cart); - Vec3d diff = VecHelper.getCenterOf(pos) - .subtract(cart.getPositionVec()); - cart.setMotion(diff.x / 16f, 0, diff.z / 16f); - } - break; - case REGULAR: - if (state.get(POWERED)) { - assemble(world, pos, cart); - } else { - disassemble(world, pos, cart); - } - break; - case ACTIVATOR_RAIL: - if (state.get(POWERED)) { - disassemble(world, pos, cart); - } - break; - case DETECTOR_RAIL: - if (cart.getPassengers() - .isEmpty()) { - assemble(world, pos, cart); - Direction facing = cart.getAdjustedHorizontalFacing(); - float speed = getRailMaxSpeed(state, world, pos, cart); - cart.setMotion(facing.getXOffset() * speed, facing.getYOffset() * speed, - facing.getZOffset() * speed); - } else { - disassemble(world, pos, cart); - } - break; - default: - break; - } - te.resetTicksSinceMinecartUpdate(); + if (!te.isMinecartUpdateValid()) + return; + + CartAssemblerAction action = getActionForCart(state, cart); + if (action.shouldAssemble()) + assemble(world, pos, cart); + if (action.shouldDisassemble()) + disassemble(world, pos, cart); + if (action == CartAssemblerAction.ASSEMBLE_ACCELERATE) { + Direction facing = cart.getAdjustedHorizontalFacing(); + float speed = getRailMaxSpeed(state, world, pos, cart); + cart.setMotion(facing.getXOffset() * speed, facing.getYOffset() * speed, facing.getZOffset() * speed); } + if (action == CartAssemblerAction.DISASSEMBLE_BRAKE) { + Vec3d diff = VecHelper.getCenterOf(pos) + .subtract(cart.getPositionVec()); + cart.setMotion(diff.x / 16f, 0, diff.z / 16f); + } + }); } + public enum CartAssemblerAction { + ASSEMBLE, DISASSEMBLE, ASSEMBLE_ACCELERATE, DISASSEMBLE_BRAKE, PASS; + + public boolean shouldAssemble() { + return this == ASSEMBLE || this == ASSEMBLE_ACCELERATE; + } + + public boolean shouldDisassemble() { + return this == DISASSEMBLE || this == DISASSEMBLE_BRAKE; + } + } + + public static CartAssemblerAction getActionForCart(BlockState state, AbstractMinecartEntity cart) { + CartAssembleRailType type = state.get(RAIL_TYPE); + boolean powered = state.get(POWERED); + + if (type == CartAssembleRailType.REGULAR) + return powered ? CartAssemblerAction.ASSEMBLE : CartAssemblerAction.DISASSEMBLE; + + if (type == CartAssembleRailType.ACTIVATOR_RAIL) + return powered ? CartAssemblerAction.DISASSEMBLE : CartAssemblerAction.PASS; + + if (type == CartAssembleRailType.POWERED_RAIL) + return powered ? CartAssemblerAction.ASSEMBLE_ACCELERATE : CartAssemblerAction.DISASSEMBLE_BRAKE; + + if (type == CartAssembleRailType.DETECTOR_RAIL) + return cart.getPassengers() + .isEmpty() ? CartAssemblerAction.ASSEMBLE_ACCELERATE : CartAssemblerAction.DISASSEMBLE; + + return CartAssemblerAction.PASS; + } + public static boolean canAssembleTo(AbstractMinecartEntity cart) { return cart.canBeRidden() || cart instanceof FurnaceMinecartEntity || cart instanceof ChestMinecartEntity; } @@ -204,34 +219,44 @@ public class CartAssemblerBlock extends AbstractRailBlock .isEmpty()) return; + LazyOptional optional = + cart.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY); + if (optional.isPresent() && optional.orElse(null) + .isCoupledThroughContraption()) + return; + MountedContraption contraption = MountedContraption.assembleMinecart(world, pos); if (contraption == null) return; if (contraption.blocks.size() == 1) return; - Direction facing = cart.getAdjustedHorizontalFacing(); - float initialAngle = facing.getHorizontalAngle(); - withTileEntityDo(world, pos, te -> contraption.rotationMode = CartMovementMode.values()[te.movementMode.value]); boolean couplingFound = contraption.connectedCart != null; + Optional initialOrientation = cart.getMotion() + .length() < 1 / 512f ? Optional.empty() : Optional.of(cart.getAdjustedHorizontalFacing()); + + if (couplingFound) { + cart.setPosition(pos.getX() + .5f, pos.getY(), pos.getZ() + .5f); + if (!CouplingHandler.tryToCoupleCarts(null, world, cart.getEntityId(), + contraption.connectedCart.getEntityId())) + return; + } + + contraption.removeBlocksFromWorld(world, BlockPos.ZERO); + contraption.initActors(world); + contraption.expandBoundsAroundAxis(Axis.Y); + if (couplingFound) { - CouplingHandler.tryToCoupleCarts(null, world, cart.getEntityId(), - contraption.connectedCart.getEntityId()); Vec3d diff = contraption.connectedCart.getPositionVec() .subtract(cart.getPositionVec()); - initialAngle = Direction.fromAngle(MathHelper.atan2(diff.z, diff.x) * 180 / Math.PI) - .getHorizontalAngle(); + initialOrientation = Optional.of(Direction.fromAngle(MathHelper.atan2(diff.z, diff.x) * 180 / Math.PI)); } - ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialAngle, facing); - - if (couplingFound) { + ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialOrientation); + if (couplingFound) entity.setCouplingId(cart.getUniqueID()); - entity.setCoupledCart(contraption.connectedCart.getUniqueID()); - } - entity.setPosition(pos.getX(), pos.getY(), pos.getZ()); world.addEntity(entity); entity.startRiding(cart); @@ -248,11 +273,44 @@ public class CartAssemblerBlock extends AbstractRailBlock if (cart.getPassengers() .isEmpty()) return; - if (!(cart.getPassengers() - .get(0) instanceof ContraptionEntity)) + Entity entity = cart.getPassengers() + .get(0); + if (!(entity instanceof ContraptionEntity)) return; - cart.removePassengers(); + ContraptionEntity contraption = (ContraptionEntity) entity; + UUID couplingId = contraption.getCouplingId(); + if (couplingId == null) { + disassembleCart(cart); + return; + } + + Couple coupledCarts = contraption.getCoupledCartsIfPresent(); + if (coupledCarts == null) + return; + + // Make sure connected cart is present and being disassembled + for (boolean current : Iterate.trueAndFalse) { + MinecartController minecartController = coupledCarts.get(current); + if (minecartController.cart() == cart) + continue; + BlockPos otherPos = minecartController.cart() + .getPosition(); + BlockState blockState = world.getBlockState(otherPos); + if (!AllBlocks.CART_ASSEMBLER.has(blockState)) + return; + if (!getActionForCart(blockState, minecartController.cart()).shouldDisassemble()) + return; + } + + for (boolean current : Iterate.trueAndFalse) + coupledCarts.get(current) + .removeConnection(current); + disassembleCart(cart); + } + + protected void disassembleCart(AbstractMinecartEntity cart) { + cart.removePassengers(); if (cart instanceof FurnaceMinecartEntity) { CompoundNBT nbt = cart.serializeNBT(); nbt.putDouble("PushZ", cart.getMotion().x); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/ItemHandlerModifiableFromIInventory.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/ItemHandlerModifiableFromIInventory.java deleted file mode 100644 index 3dd313411..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/ItemHandlerModifiableFromIInventory.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.simibubi.create.content.contraptions.components.structureMovement.mounted; - -import javax.annotation.Nonnull; -import javax.annotation.ParametersAreNonnullByDefault; - -import mcp.MethodsReturnNonnullByDefault; -import net.minecraft.inventory.IInventory; -import net.minecraft.item.ItemStack; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemHandlerHelper; - - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class ItemHandlerModifiableFromIInventory implements IItemHandlerModifiable { - private final IInventory inventory; - - public ItemHandlerModifiableFromIInventory(IInventory inventory) { - this.inventory = inventory; - } - - @Override - public void setStackInSlot(int slot, ItemStack stack) { - inventory.setInventorySlotContents(slot, stack); - } - - @Override - public int getSlots() { - return inventory.getSizeInventory(); - } - - @Override - public ItemStack getStackInSlot(int slot) { - return inventory.getStackInSlot(slot); - } - - @Override - @Nonnull - public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) - { - if (stack.isEmpty()) - return ItemStack.EMPTY; - - if (!isItemValid(slot, stack)) - return stack; - - validateSlotIndex(slot); - - ItemStack existing = getStackInSlot(slot); - - int limit = getStackLimit(slot, stack); - - if (!existing.isEmpty()) - { - if (!ItemHandlerHelper.canItemStacksStack(stack, existing)) - return stack; - - limit -= existing.getCount(); - } - - if (limit <= 0) - return stack; - - boolean reachedLimit = stack.getCount() > limit; - - if (!simulate) - { - if (existing.isEmpty()) - { - setStackInSlot(slot, reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack); - } - else - { - existing.grow(reachedLimit ? limit : stack.getCount()); - } - } - - return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount()- limit) : ItemStack.EMPTY; - } - - @Override - @Nonnull - public ItemStack extractItem(int slot, int amount, boolean simulate) - { - if (amount == 0) - return ItemStack.EMPTY; - - validateSlotIndex(slot); - - ItemStack existing = getStackInSlot(slot); - - if (existing.isEmpty()) - return ItemStack.EMPTY; - - int toExtract = Math.min(amount, existing.getMaxStackSize()); - - if (existing.getCount() <= toExtract) - { - if (!simulate) - { - setStackInSlot(slot, ItemStack.EMPTY); - return existing; - } - else - { - return existing.copy(); - } - } - else - { - if (!simulate) - { - setStackInSlot(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract)); - } - - return ItemHandlerHelper.copyStackWithSize(existing, toExtract); - } - } - - @Override - public int getSlotLimit(int slot) { - return inventory.getInventoryStackLimit(); - } - - @Override - public boolean isItemValid(int slot, ItemStack stack) { - return inventory.isItemValidForSlot(slot, stack); - } - - private void validateSlotIndex(int slot) - { - if (slot < 0 || slot >= getSlots()) - throw new RuntimeException("Slot " + slot + " not in valid range - [0," + getSlots() + ")"); - } - - private int getStackLimit(int slot, ItemStack stack) - { - return Math.min(getSlotLimit(slot), stack.getMaxStackSize()); - } -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java index a0d3c23c2..6ed2b83c3 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java @@ -1,12 +1,14 @@ package com.simibubi.create.content.contraptions.components.structureMovement.mounted; import java.util.List; +import java.util.Optional; import javax.annotation.Nullable; import com.simibubi.create.AllItems; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; +import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.block.AbstractRailBlock; import net.minecraft.block.BlockState; @@ -27,6 +29,7 @@ import net.minecraft.state.properties.RailShape; import net.minecraft.tags.BlockTags; import net.minecraft.util.ActionResultType; import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -154,18 +157,21 @@ public class MinecartContraptionItem extends Item { CompoundNBT tag = itemstack.getOrCreateTag(); if (tag.contains("Contraption")) { CompoundNBT contraptionTag = tag.getCompound("Contraption"); - float initialAngle = contraptionTag.getFloat("InitialAngle"); + + Direction initialOrientation = Direction.SOUTH; + if (contraptionTag.contains("InitialOrientation")) + initialOrientation = NBTHelper.readEnum(contraptionTag, "InitialOrientation", Direction.class); + Contraption mountedContraption = Contraption.fromNBT(world, contraptionTag); - ContraptionEntity contraption; + ContraptionEntity contraptionEntity = + ContraptionEntity.createMounted(world, mountedContraption, Optional.of(initialOrientation)); if (newFacing != null) - contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle, newFacing); - else - contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle); + contraptionEntity.reOrientate(newFacing.getAxis() == Axis.X ? newFacing : newFacing.getOpposite()); - contraption.startRiding(cart); - contraption.setPosition(cart.getX(), cart.getY(), cart.getZ()); - world.addEntity(contraption); + contraptionEntity.startRiding(cart); + contraptionEntity.setPosition(cart.getX(), cart.getY(), cart.getZ()); + world.addEntity(contraptionEntity); } } @@ -218,7 +224,11 @@ public class MinecartContraptionItem extends Item { tag.remove("UUID"); tag.remove("Pos"); tag.remove("Motion"); - tag.putFloat("InitialAngle", entity.getInitialAngle()); + + Optional initialOrientation = entity.getInitialOrientation(); + if (initialOrientation.isPresent()) + NBTHelper.writeEnum(tag, "InitialOrientation", initialOrientation.orElse(null)); + stack.getOrCreateTag() .put("Contraption", tag); return stack; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java index 91cb2ed84..82b11e8a8 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java @@ -29,7 +29,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.Template.BlockInfo; +import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.wrapper.CombinedInvWrapper; +import net.minecraftforge.items.wrapper.InvWrapper; public class MountedContraption extends Contraption { @@ -57,10 +59,6 @@ public class MountedContraption extends Contraption { Axis axis = state.get(RAIL_SHAPE) == RailShape.EAST_WEST ? Axis.X : Axis.Z; contraption.add(pos, Pair.of(new BlockInfo(pos, AllBlocks.MINECART_ANCHOR.getDefaultState() .with(BlockStateProperties.HORIZONTAL_AXIS, axis), null), null)); - contraption.removeBlocksFromWorld(world, BlockPos.ZERO); - contraption.initActors(world); - contraption.expandBoundsAroundAxis(Axis.Y); - return contraption; } @@ -75,35 +73,51 @@ public class MountedContraption extends Contraption { protected Pair capture(World world, BlockPos pos) { Pair pair = super.capture(world, pos); BlockInfo capture = pair.getKey(); - if (AllBlocks.CART_ASSEMBLER.has(capture.state)) { - if (!pos.equals(anchor)) { - for (Axis axis : Iterate.axes) { - if (axis.isVertical()) - continue; - if (VecHelper.onSameAxis(anchor, pos, axis) && connectedCart == null) { - for (AbstractMinecartEntity abstractMinecartEntity : world - .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) { - if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity)) - break; - connectedCart = abstractMinecartEntity; - addExtraInventories(abstractMinecartEntity); - } - } - } + if (!AllBlocks.CART_ASSEMBLER.has(capture.state)) + return pair; + + Pair anchorSwap = + Pair.of(new BlockInfo(pos, CartAssemblerBlock.createAnchor(capture.state), null), pair.getValue()); + if (pos.equals(anchor) || connectedCart != null) + return anchorSwap; + + for (Axis axis : Iterate.axes) { + if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis)) + continue; + for (AbstractMinecartEntity abstractMinecartEntity : world + .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) { + if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity)) + break; + connectedCart = abstractMinecartEntity; + connectedCart.setPosition(pos.getX() + .5, pos.getY(), pos.getZ() + .5f); } - return Pair.of(new BlockInfo(pos, CartAssemblerBlock.createAnchor(capture.state), null), pair.getValue()); } - return pair; + + return anchorSwap; } @Override protected boolean movementAllowed(World world, BlockPos pos) { BlockState blockState = world.getBlockState(pos); if (!pos.equals(anchor) && AllBlocks.CART_ASSEMBLER.has(blockState)) - return true; + return testSecondaryCartAssembler(world, blockState, pos); return super.movementAllowed(world, pos); } + protected boolean testSecondaryCartAssembler(World world, BlockState state, BlockPos pos) { + for (Axis axis : Iterate.axes) { + if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis)) + continue; + for (AbstractMinecartEntity abstractMinecartEntity : world + .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) { + if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity)) + break; + return true; + } + } + return false; + } + @Override public CompoundNBT writeNBT() { CompoundNBT writeNBT = super.writeNBT(); @@ -129,7 +143,9 @@ public class MountedContraption extends Contraption { @Override public void addExtraInventories(Entity cart) { - if (cart instanceof IInventory) - inventory = new CombinedInvWrapper(new ItemHandlerModifiableFromIInventory((IInventory) cart), inventory); + if (!(cart instanceof IInventory)) + return; + IItemHandlerModifiable handlerFromInv = new InvWrapper((IInventory) cart); + inventory = new CombinedInvWrapper(handlerFromInv, inventory); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java index 6aa5a599b..2cdbb52d6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java @@ -8,6 +8,7 @@ import javax.annotation.Nullable; import com.simibubi.create.AllItems; import com.simibubi.create.Create; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController; import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController; import com.simibubi.create.foundation.config.AllConfigs; @@ -22,9 +23,30 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.World; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.event.entity.EntityMountEvent; +import net.minecraftforge.eventbus.api.Event.Result; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +@EventBusSubscriber public class CouplingHandler { + @SubscribeEvent + public static void preventEntitiesFromMoutingOccupiedCart(EntityMountEvent event) { + Entity e = event.getEntityBeingMounted(); + LazyOptional optional = e.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY); + if (!optional.isPresent()) + return; + if (event.getEntityMounting() instanceof ContraptionEntity) + return; + MinecartController controller = optional.orElse(null); + if (controller.isCoupledThroughContraption()) { + event.setCanceled(true); + event.setResult(Result.DENY); + } + } + public static void forEachLoadedCoupling(World world, Consumer> consumer) { if (world == null) return; @@ -45,14 +67,14 @@ public class CouplingHandler { }); } - public static void tryToCoupleCarts(@Nullable PlayerEntity player, World world, int cartId1, int cartId2) { + public static boolean tryToCoupleCarts(@Nullable PlayerEntity player, World world, int cartId1, int cartId2) { Entity entity1 = world.getEntityByID(cartId1); Entity entity2 = world.getEntityByID(cartId2); if (!(entity1 instanceof AbstractMinecartEntity)) - return; + return false; if (!(entity2 instanceof AbstractMinecartEntity)) - return; + return false; String tooMany = "two_couplings_max"; String unloaded = "unloaded"; @@ -61,16 +83,17 @@ public class CouplingHandler { int distanceTo = (int) entity1.getPositionVec() .distanceTo(entity2.getPositionVec()); + boolean contraptionCoupling = player == null; if (distanceTo < 2) { - if (player == null) - return; // dont allow train contraptions with <2 distance + if (contraptionCoupling) + return false; // dont allow train contraptions with <2 distance distanceTo = 2; } if (distanceTo > AllConfigs.SERVER.kinetics.maxCartCouplingLength.get()) { status(player, tooFar); - return; + return false; } AbstractMinecartEntity cart1 = (AbstractMinecartEntity) entity1; @@ -82,18 +105,18 @@ public class CouplingHandler { if (mainController == null || connectedController == null) { status(player, unloaded); - return; + return false; } if (mainController.isFullyCoupled() || connectedController.isFullyCoupled()) { status(player, tooMany); - return; + return false; } if (mainController.isLeadingCoupling() && mainController.getCoupledCart(true) .equals(connectedID) || connectedController.isLeadingCoupling() && connectedController.getCoupledCart(true) .equals(mainID)) - return; + return false; for (boolean main : Iterate.trueAndFalse) { MinecartController current = main ? mainController : connectedController; @@ -103,24 +126,24 @@ public class CouplingHandler { while (true) { if (safetyCount-- <= 0) { Create.logger.warn("Infinite loop in coupling iteration"); - return; + return false; } current = getNextInCouplingChain(world, current, forward); if (current == null) { status(player, unloaded); - return; + return false; } if (current == connectedController) { status(player, noLoops); - return; + return false; } if (current == MinecartController.EMPTY) break; } } - if (player != null) { + if (!contraptionCoupling) { for (Hand hand : Hand.values()) { if (player.isCreative()) break; @@ -135,8 +158,9 @@ public class CouplingHandler { mainController.prepareForCoupling(true); connectedController.prepareForCoupling(false); - mainController.coupleWith(true, connectedID, distanceTo); - connectedController.coupleWith(false, mainID, distanceTo); + mainController.coupleWith(true, connectedID, distanceTo, contraptionCoupling); + connectedController.coupleWith(false, mainID, distanceTo, contraptionCoupling); + return true; } @Nullable diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java index 78e87fb35..9f9a7e926 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java @@ -32,7 +32,11 @@ public class CouplingRenderer { public static void renderAll(MatrixStack ms, IRenderTypeBuffer buffer) { CouplingHandler.forEachLoadedCoupling(Minecraft.getInstance().world, - c -> CouplingRenderer.renderCoupling(ms, buffer, c.map(MinecartController::cart))); + c -> { + if (c.getFirst().hasContraptionCoupling(true)) + return; + CouplingRenderer.renderCoupling(ms, buffer, c.map(MinecartController::cart)); + }); } public static void tickDebugModeRenders() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java index 98f89cdc1..a01d3e6ff 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java @@ -2,16 +2,15 @@ package com.simibubi.create.content.contraptions.components.structureMovement.tr import static net.minecraft.entity.Entity.horizontalMag; -import java.util.List; import java.util.Map; import com.google.common.collect.Maps; import com.mojang.datafixers.util.Pair; -import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; +import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController; +import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController; import net.minecraft.block.AbstractRailBlock; import net.minecraft.block.BlockState; -import net.minecraft.entity.Entity; import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.entity.item.minecart.FurnaceMinecartEntity; import net.minecraft.state.properties.RailShape; @@ -21,6 +20,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; +import net.minecraftforge.common.util.LazyOptional; /** * Useful methods for dealing with Minecarts @@ -47,10 +47,10 @@ public class MinecartSim2020 { }); public static Vec3d predictMotionOf(AbstractMinecartEntity cart) { - if (cart instanceof FurnaceMinecartEntity) { - return cart.getPositionVec() - .subtract(cart.lastTickPosX, cart.lastTickPosY, cart.lastTickPosZ); - } +// if (cart instanceof FurnaceMinecartEntity) { +// return cart.getPositionVec() +// .subtract(cart.lastTickPosX, cart.lastTickPosY, cart.lastTickPosZ); +// } return cart.getMotion().scale(1f); // if (cart instanceof ContainerMinecartEntity) { // ContainerMinecartEntity containerCart = (ContainerMinecartEntity) cart; @@ -71,15 +71,9 @@ public class MinecartSim2020 { if (c instanceof FurnaceMinecartEntity) return MathHelper.epsilonEquals(((FurnaceMinecartEntity) c).pushX, 0) && MathHelper.epsilonEquals(((FurnaceMinecartEntity) c).pushZ, 0); - List passengers = c.getPassengers(); - if (passengers.isEmpty()) - return true; - for (Entity entity : passengers) { - if (entity instanceof ContraptionEntity) { - ContraptionEntity contraptionEntity = (ContraptionEntity) entity; - return !contraptionEntity.isStalled(); - } - } + LazyOptional capability = c.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY); + if (capability.isPresent() && capability.orElse(null).isStalled()) + return false; return true; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/capability/CapabilityMinecartController.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/capability/CapabilityMinecartController.java index 31eef4d3d..3120a64c6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/capability/CapabilityMinecartController.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/capability/CapabilityMinecartController.java @@ -130,6 +130,8 @@ public class CapabilityMinecartController implements ICapabilitySerializable { UUID idOfOther = cd.idOfCart(!main); MinecartController otherCart = CapabilityMinecartController.getIfPresent(world, idOfOther); internalStall.setValue( - internalStall.booleanValue() || otherCart == null || !otherCart.isPresent() || otherCart.isStalled()); + internalStall.booleanValue() || otherCart == null || !otherCart.isPresent() || otherCart.isStalled(false)); })); if (!world.isRemote) @@ -93,6 +96,18 @@ public class MinecartController implements INBTSerializable { .isPresent(); } + public boolean isCoupledThroughContraption() { + for (boolean current : Iterate.trueAndFalse) + if (hasContraptionCoupling(current)) + return true; + return false; + } + + public boolean hasContraptionCoupling(boolean current) { + Optional optional = couplings.get(current); + return optional.isPresent() && optional.get().contraption; + } + public float getCouplingLength(boolean leading) { Optional optional = couplings.get(leading); if (optional.isPresent()) @@ -113,6 +128,15 @@ public class MinecartController implements INBTSerializable { } public void removeConnection(boolean main) { + if (hasContraptionCoupling(main) && !getWorld().isRemote) { + List passengers = cart().getPassengers(); + if (!passengers.isEmpty()) { + Entity entity = passengers.get(0); + if (entity instanceof ContraptionEntity) + ((ContraptionEntity) entity).disassemble(); + } + } + couplings.set(main, Optional.empty()); needsEntryRefresh |= main; sendData(); @@ -140,7 +164,28 @@ public class MinecartController implements INBTSerializable { for (MinecartController minecartController : cartsToFlip) { MinecartController mc = minecartController; - mc.couplings.forEach(opt -> opt.ifPresent(CouplingData::flip)); + mc.couplings.forEachWithContext((opt, leading) -> opt.ifPresent(cd -> { + cd.flip(); + if (!cd.contraption) + return; + List passengers = mc.cart() + .getPassengers(); + if (passengers.isEmpty()) + return; + Entity entity = passengers.get(0); + if (!(entity instanceof ContraptionEntity)) + return; + ContraptionEntity contraption = (ContraptionEntity) entity; + UUID couplingId = contraption.getCouplingId(); + if (couplingId == cd.mainCartID) { + contraption.setCouplingId(cd.connectedCartID); + return; + } + if (couplingId == cd.connectedCartID) { + contraption.setCouplingId(cd.mainCartID); + return; + } + })); mc.couplings = mc.couplings.swap(); if (mc == this) continue; @@ -150,10 +195,10 @@ public class MinecartController implements INBTSerializable { } } - public void coupleWith(boolean isLeading, UUID coupled, float length) { + public void coupleWith(boolean isLeading, UUID coupled, float length, boolean contraption) { UUID mainID = isLeading ? cart().getUniqueID() : coupled; UUID connectedID = isLeading ? coupled : cart().getUniqueID(); - couplings.set(isLeading, Optional.of(new CouplingData(mainID, connectedID, length))); + couplings.set(isLeading, Optional.of(new CouplingData(mainID, connectedID, length, contraption))); needsEntryRefresh |= isLeading; sendData(); } @@ -261,11 +306,13 @@ public class MinecartController implements INBTSerializable { private UUID mainCartID; private UUID connectedCartID; private float length; + private boolean contraption; - public CouplingData(UUID mainCartID, UUID connectedCartID, float length) { + public CouplingData(UUID mainCartID, UUID connectedCartID, float length, boolean contraption) { this.mainCartID = mainCartID; this.connectedCartID = connectedCartID; this.length = length; + this.contraption = contraption; } void flip() { @@ -279,13 +326,16 @@ public class MinecartController implements INBTSerializable { nbt.put("Main", NBTUtil.writeUniqueId(mainCartID)); nbt.put("Connected", NBTUtil.writeUniqueId(connectedCartID)); nbt.putFloat("Length", length); + nbt.putBoolean("Contraption", contraption); return nbt; } static CouplingData read(CompoundNBT nbt) { UUID mainCartID = NBTUtil.readUniqueId(nbt.getCompound("Main")); UUID connectedCartID = NBTUtil.readUniqueId(nbt.getCompound("Connected")); - return new CouplingData(mainCartID, connectedCartID, nbt.getFloat("Length")); + float length = nbt.getFloat("Length"); + boolean contraption = nbt.getBoolean("Contraption"); + return new CouplingData(mainCartID, connectedCartID, length, contraption); } public UUID idOfCart(boolean main) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinMovementBehaviour.java index 2bb4a4b60..cc76a74f1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinMovementBehaviour.java @@ -5,7 +5,6 @@ import java.util.Map; import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; -import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.ItemStack; @@ -31,7 +30,7 @@ public class BasinMovementBehaviour extends MovementBehaviour { public void tick(MovementContext context) { super.tick(context); if (context.temporaryData == null || (boolean) context.temporaryData) { - Vec3d facingVec = VecHelper.rotate(new Vec3d(Direction.UP.getDirectionVec()), context.rotation.x, context.rotation.y, context.rotation.z); + Vec3d facingVec = context.rotation.apply(new Vec3d(Direction.UP.getDirectionVec())); facingVec.normalize(); if (Direction.getFacingFromVector(facingVec.x, facingVec.y, facingVec.z) == Direction.DOWN) dump(context, facingVec); @@ -41,16 +40,21 @@ public class BasinMovementBehaviour extends MovementBehaviour { private void dump(MovementContext context, Vec3d facingVec) { getOrReadInventory(context).forEach((key, itemStackHandler) -> { for (int i = 0; i < itemStackHandler.getSlots(); i++) { - if (itemStackHandler.getStackInSlot(i).isEmpty()) + if (itemStackHandler.getStackInSlot(i) + .isEmpty()) continue; - ItemEntity itemEntity = new ItemEntity(context.world, context.position.x, context.position.y, context.position.z, itemStackHandler.getStackInSlot(i)); + ItemEntity itemEntity = new ItemEntity(context.world, context.position.x, context.position.y, + context.position.z, itemStackHandler.getStackInSlot(i)); itemEntity.setMotion(facingVec.scale(.05)); context.world.addEntity(itemEntity); itemStackHandler.setStackInSlot(i, ItemStack.EMPTY); } context.tileData.put(key, itemStackHandler.serializeNBT()); }); - context.contraption.customRenderTEs.stream().filter(te -> te.getPos().equals(context.localPos) && te instanceof BasinTileEntity).forEach(te -> ((BasinTileEntity) te).readOnlyItems(context.tileData)); + context.contraption.customRenderTEs.stream() + .filter(te -> te.getPos() + .equals(context.localPos) && te instanceof BasinTileEntity) + .forEach(te -> ((BasinTileEntity) te).readOnlyItems(context.tileData)); context.temporaryData = false; // did already dump, so can't any more } } diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/ContactMovementBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/ContactMovementBehaviour.java index 368758376..6f05a3486 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/ContactMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/ContactMovementBehaviour.java @@ -3,7 +3,6 @@ package com.simibubi.create.content.logistics.block.redstone; import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; -import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; import net.minecraft.nbt.NBTUtil; @@ -36,7 +35,7 @@ public class ContactMovementBehaviour extends MovementBehaviour { return; Vec3d contact = new Vec3d(block.get(RedstoneContactBlock.FACING).getDirectionVec()); - contact = VecHelper.rotate(contact, context.rotation.x, context.rotation.y, context.rotation.z); + contact = context.rotation.apply(contact); Direction direction = Direction.getFacingFromVector(contact.x, contact.y, contact.z); if (!RedstoneContactBlock.hasValidContact(world, pos.offset(direction.getOpposite()), direction)) diff --git a/src/main/java/com/simibubi/create/foundation/utility/MatrixStacker.java b/src/main/java/com/simibubi/create/foundation/utility/MatrixStacker.java index 545d41a50..5e7e973ea 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/MatrixStacker.java +++ b/src/main/java/com/simibubi/create/foundation/utility/MatrixStacker.java @@ -33,13 +33,6 @@ public class MatrixStacker { return multiply(Vector3f.POSITIVE_Z, angle); } - public MatrixStacker rotateRadians(double angleRoll, double angleYaw, double anglePitch) { - rotateX(AngleHelper.deg(angleRoll)); - rotateY(AngleHelper.deg(angleYaw)); - rotateZ(AngleHelper.deg(anglePitch)); - return this; - } - public MatrixStacker centre() { return translate(center); }